# pen.dev CLI (formerly Pencil CLI)

Command-line interface for [pen.dev](https://pen.dev) — create and edit `.pen` design files from the terminal. Run the AI agent with a prompt, call MCP tools directly in an interactive shell, batch-process multiple designs, or export to PNG/JPEG/WEBP/PDF. Built on the same editor engine as the desktop app and IDE extension, with full headless rendering, AI image generation, and stock photo support.

## Installation

```bash
npm install -g @pen.dev/cli
# or
pnpm add -g @pen.dev/cli
# or
yarn global add @pen.dev/cli
```

## Authentication

The CLI requires authentication before running agent operations. There are two methods:

### Sign Up

```bash
pen signup --email you@example.com --username johndoe --name "John Doe"
```

Creates a new account. You'll receive a verification email — click the link, then log in.

### Interactive Login

```bash
pen login
```

This starts an interactive session where you choose your login method (email + password or email + OTP code). On success the session token is stored in `~/.pencil/session-cli.json`.

### Non-Interactive Login

```bash
# Step 1: Request an OTP code
pen login --email you@example.com

# Step 2: Log in with the code from your email
pen login --email you@example.com --code 123456
```

When flags are provided the interactive prompts are skipped, useful for scripting and CI.

### CLI Key (for CI/CD)

Set the `PEN_CLI_KEY` environment variable. CLI keys are scoped to an organization and can be created/revoked in the **Developer Keys** section of your organization settings on the pen.dev web app.

```bash
PEN_CLI_KEY=pencil_cli_... pen --out design.pen --prompt "Create a form" --agent claude
```

The CLI key always takes precedence over a stored session token.

### Checking Status

```bash
pen status
```

Displays the current authentication method, verifies the session with the backend, and shows account details (email, name, organization for CLI keys).

## Quick Start

```bash
# Log in first
pen login

# Create a new design from scratch
pen --out design.pen --prompt "Create a login page with email and password fields" --agent claude

# Modify an existing design
pen --in existing.pen --out modified.pen --prompt "Add a blue submit button" --agent claude

# Start an interactive shell (in headless mode)
pen interactive -o design.pen

# Start an interactive shell (connect to a running pen.dev app)
pen interactive -a desktop -i design.pen

# List available models
pen --list-models --agent claude
```

## Usage

```
pen [command] [options]

Commands:
  signup                 Create a new account (flags required, see below)
  login                  Log in interactively (email + password or OTP)
  status                 Check authentication status
  version                Show CLI version
  interactive            Start an interactive tool shell (see below)

Options:
  --in, -i <path>        Input .pen file (optional, starts with empty canvas if omitted)
  --out, -o <path>       Output .pen file path (required unless --export is used)
  --prompt, -p <text>    Prompt for the AI agent (required)
  --prompt-file, -f <path>  Attach a file to send with the prompt (repeatable). Images (png, jpeg, gif, webp) or text files; paths are not the prompt text itself.
  --agent <type>         Agent to use when --model is omitted: claude, codex, gemini (default: claude)
  --model, -m <id>       Model to use; agent is inferred from the model id
  --custom, -c           Use custom Claude model config (e.g. AWS Bedrock, Vertex AI)
  --list-models          List available models and exit
  --tasks, -t <path>     JSON tasks file for batch operations
  --workspace, -w <path> Workspace folder path to run the agent in
  --export, -e <path>    Export an image of the final result
  --export-scale <n>     Export scale factor (default: 1)
  --export-type <type>   Export format: png, jpeg, webp, pdf (default: png)
  --verbose-mcp          Log full MCP tool error details to the console
  --help, -h             Show help message
```

## Interactive Mode

The interactive shell lets you call MCP tools directly on `.pen` files — useful for scripting, debugging, and agentic workflows that need fine-grained control over design operations.

```
pen interactive [options]

Options:
  --app, -a <name>   Connect to a running pen.dev app (e.g. desktop, vscode)
  --in, -i <path>    Input .pen file (optional, empty canvas if omitted)
  --out, -o <path>   Output .pen file (required in headless mode)
  --help, -h         Show detailed tool reference. Important for agentic workflows, so agents can learn the tool.
```

### Modes

**App mode** — connects to a running pen.dev desktop or extension. Changes are applied live.

```bash
pen interactive -a desktop -i my-design.pen
```

**Headless mode** — spins up a local editor without a GUI. Use `save()` to write to `--out`.

```bash
# New empty canvas saved to the output file
pen interactive -o output.pen

# Edit an existing file
pen interactive -i input.pen -o output.pen
```

### Shell commands

```
tool_name({ key: value })   Call an MCP tool
save()                      Save the document to disk (headless) or app
exit()                      Exit the shell
```

### Getting started

Begin with `read_skill` to load the .pen schema and execute instructions, then `get_app_state` to understand the document:

```
pen > read_skill()
pen > read_skill({ path: "pen-schema.md" })
pen > read_skill({ path: "execute.md" })
pen > get_app_state()
pen > execute({ input: 'Get((n,c)=>{c.skipChildren();Print(n.id,n.name)})' }) # list top-level nodes
pen > execute({ input: 'Get(n=>n.reusable&&Print(n.id,n.name))' })            # find components
```

### Example

```
pen > read_skill()
pen > read_skill({ path: "pen-schema.md" })
pen > read_skill({ path: "execute.md" })
pen > get_app_state()
pen > get_style()
pen > get_style({ name: "Aerial Gravitas" })
pen > execute({ input: 'rect=Insert(document,{type:"rectangle",name:"Foo",x:10,y:10,width:300,height:200,fill:"#E5484D"})' })
pen > execute({ input: 'TakeScreenshot([rect])' })
pen > save()
pen > exit()
```

Run `pen interactive --help` for the full tool reference with parameter types and descriptions.

## Available Models

```bash
pen --list-models --agent claude|gemini|codex
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| `PEN_CLI_KEY` | CLI API key for CI/CD (takes precedence over stored session) |
| `PEN_AGENT_API_KEY` | API key for the selected agent. |
| `ANTHROPIC_API_KEY` | Anthropic API key for Claude agents. Ignored for Codex and Gemini. |
| `PEN_API_BASE` | Backend API base URL (default: `https://api.pen.dev`) |
| `DEBUG` | Enable debug logging |

Further environment variables and agent config can be set in `settings.json`
as well.

## Supported Operations

The CLI supports the following MCP tools with full feature parity to the desktop app:

### Design Operations

| Tool | Description |
|------|-------------|
| `execute` | Insert, Update, Delete, Move, Copy, Replace, SetVariables, GetVariables, Get, Print, Generate, FindEmptySpace, TakeScreenshot, Export |
| `get_app_state` | Get document metadata and structure |

### Visual Operations (headless rendering via CanvasKit)

Screenshots are taken with the `execute` `TakeScreenshot()` operation. Files
are exported with the `execute` `Export()` operation, which supports
PNG/JPEG/WEBP/PDF images and HTML output.

### Image Generation

The `execute` `Generate()` operation supports both AI-generated and stock images. Generated images are saved to an `images/` directory alongside the output `.pen` file.

| Type | Description |
|------|-------------|
| `Generate(nodeId, "ai", prompt)` | AI-generated image from a text prompt |
| `Generate(nodeId, "stock", keywords)` | Stock photo from Unsplash |

### Guidelines

| Tool | Description |
|------|-------------|
| `read_skill` | Read the pen-dev skill (SKILL.md, pen-schema.md, execute.md, guides) |
| `get_style` | Load styles for working with .pen files |

## Examples

### Create a Login Page

```bash
pen --out login.pen --agent claude --prompt "Create a modern login page with:
- Email input field
- Password input field
- 'Sign In' button
- 'Forgot password?' link
- Social login options (Google, GitHub)"
```

### Add Components to Existing Design

```bash
pen --in dashboard.pen --out dashboard-v2.pen --agent gemini --prompt "Add a sidebar navigation with:
- Dashboard link (active)
- Users link
- Settings link
- Logout button at bottom"
```

### Create a Component Library

```bash
pen --out components.pen --agent codex --prompt "Create a component library with:
- Primary, secondary, and ghost button variants
- Text input with label and error state
- Card component with header and content
- Badge component in success, warning, error colors"
```

### Using a Specific Model

Use `--model` to select a different Claude model:

```bash
# Use Claude Opus for complex tasks requiring highest capability
pen --out complex-app.pen \
  --model claude-opus-4-6 \
  --prompt "Create a complete e-commerce product page with image gallery,
reviews section, related products, and add-to-cart functionality"

# Use Claude Haiku for simple, fast tasks
pen --out simple.pen \
  --model claude-haiku-4-5 \
  --prompt "Create a simple 404 error page"
```

### CI/CD Usage

```bash
# Authenticate with a CLI key
export PEN_CLI_KEY=pencil_cli_...
export PEN_AGENT_API_KEY=sk-...

# Generate designs in a pipeline
pen --out onboarding.pen --prompt "Create a 3-step onboarding flow" --agent claude
```

### Verbose MCP Error Logging

Use `--verbose-mcp` to show full MCP tool error details (including stack traces where available) in the CLI output when a tool fails:

```bash
pen --out debug.pen \
  --agent claude \
  --prompt "Create a simple layout" \
  --verbose-mcp
```

### Batch Processing with Tasks File

Use `--tasks` to process multiple designs from a JSON tasks file:

```bash
pen --tasks batch-tasks.json --agent claude
```

Example `batch-tasks.json`:

```json
{
  "tasks": [
    {
      "out": "landing-page.pen",
      "prompt": "Create a SaaS landing page with hero, features, and pricing sections"
    },
    {
      "in": "existing-app.pen",
      "out": "existing-app-v2.pen",
      "prompt": "Add a dark mode toggle to the header"
    },
    {
      "out": "mobile-menu.pen",
      "model": "claude-haiku-4-5",
      "prompt": "Create a mobile hamburger menu component"
    },
    {
      "out": "from-reference.pen",
      "prompt": "Match the layout and palette of the attached reference",
      "promptFiles": ["./assets/reference.png"]
    }
  ]
}
```

Each task in the array supports the same options as CLI arguments:
- `in` - Input file (optional)
- `out` - Output file (required)
- `prompt` - AI prompt (required)
- `model` - Model override (optional)
- `promptFiles` - Array of attachment paths (optional). Relative paths are resolved from the directory that contains the tasks JSON file.

## Limitations

The CLI is designed for headless operation and has some limitations compared to the desktop app:

- **No real-time preview** - Changes are saved to file, not displayed interactively
- **No interactive UI features** - No selection, zoom, or pan controls
- **No library browsing** - Cannot browse or import from `.pen` libraries

## Token Storage

| File | Purpose |
|------|---------|
| `~/.pencil/session-cli.json` | Stored session token from `pen login` |

The CLI uses a separate session file from the desktop app (`~/.pencil/session-desktop.json`) so the backend can distinguish which client is in use.

## License

Proprietary — see LICENSE file. © High Agency, Inc.
