---
slug: "pi-plan-html"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/saadzniber/pi-plan-html@main/README.md"
repo: "https://github.com/saadzniber/pi-plan-html"
source_file: "README.md"
branch: "main"
---
# plan-html

> Turn AI agent plans into commentable, beautifully designed HTML pages.
> Works with **pi** and **Claude Code**.

[![npm: pi-plan-html](https://img.shields.io/npm/v/pi-plan-html?label=pi-plan-html)](https://www.npmjs.com/package/pi-plan-html)
[![npm: plan-html-cli](https://img.shields.io/npm/v/plan-html-cli?label=plan-html-cli)](https://www.npmjs.com/package/plan-html-cli)
[![license: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)

---

## What it does

Instead of having your AI agent dump a markdown TODO list, it produces a structured `plan-json` block that gets rendered as a **polished, commentable HTML page** with:

- A sticky table of contents and progress widget
- **Realistic UI mockups** (real HTML in window/browser/phone chrome — not ASCII art)
- **Mermaid diagrams** for data flow and sequence
- Syntax-highlighted code snippets
- Callouts (note / warn / risk / ok)
- File-touch lists per step
- Risks + open questions panels
- **Inline comment threads** anchored per step

Three slash commands drive the workflow:

| Command | What it does |
|---|---|
| `/plan <prompt>` | Read-only research → emits `plan-json` → renders HTML → opens browser |
| `/plan-iterate` (Claude) / `/iterate` (pi) | Reads your inline comments, produces v2 |
| `/plan-approve` (Claude) / `/approve` (pi) | Restores full tools, executes the plan, ticks off `[DONE]` markers |

---

## Install — pi

Add to `~/.pi/agent/settings.json`:

```jsonc
{
  "packages": ["npm:pi-plan-html"]
}
```

Restart pi. The slash commands `/plan`, `/iterate`, `/approve`, `/plan-off`, `/plan-open` become available in **every** repo.

**Verify**:

```
$ pi
> /plan add a hello endpoint to the server
```

A browser tab should open showing the rendered plan. Plan files land in `<cwd>/.pi/plans/`.

**Uninstall**: remove `"npm:pi-plan-html"` from `packages` and restart pi.

---

## Install — Claude Code

Two steps (the skill files **and** the renderer CLI):

```sh
# One-liner: installs SKILL.md, the slash commands, AND plan-html-cli globally
curl -fsSL https://raw.githubusercontent.com/saadzniber/pi-plan-html/main/claude-skill/install.sh | bash
```

Or manually:

```sh
# 1. Install the renderer
npm install -g plan-html-cli

# 2. Install the skill + commands
git clone https://github.com/saadzniber/pi-plan-html.git
mkdir -p ~/.claude/skills/plan-html ~/.claude/commands
cp pi-plan-html/claude-skill/SKILL.md          ~/.claude/skills/plan-html/SKILL.md
cp pi-plan-html/claude-skill/commands/*.md     ~/.claude/commands/
```

Restart Claude Code. The slash commands `/plan`, `/plan-iterate`, `/plan-approve` become available.

**Verify**:

```
$ claude
> /plan add a hello endpoint to the server
```

Same browser tab, same HTML, same `.pi/plans/` directory — pi users and Claude Code users can hand plans back and forth.

**Uninstall**:

```sh
rm -rf ~/.claude/skills/plan-html
rm -f  ~/.claude/commands/{plan,plan-iterate,plan-approve}.md
npm uninstall -g plan-html-cli
```

---

## Workflow

1. **Type `/plan <prompt>`** — the agent stays read-only, explores the repo, emits `plan-json`.
2. **HTML opens in your browser** — scroll the TOC, expand mockups, read mermaid diagrams.
3. **Comment inline** — hover a section, click 💬 comment, type feedback. Save → exports `feedback.md` to `.pi/plans/`.
4. **`/iterate`** (pi) or **`/plan-iterate`** (Claude) — agent reads feedback, produces v2. Comments anchored by `step.key` persist across revisions.
5. **`/approve`** (pi) or **`/plan-approve`** (Claude) — full tools restored, agent executes, `[DONE:<key>]` markers tick a progress bar in the page footer.

---

## plan-json schema

<details>
<summary><strong>Click to expand the full schema</strong></summary>

Agents emit one fenced block — and nothing else after it — in this exact form:

````
```plan-json
{
  "title": "Short, specific name for the plan",
  "context": "1–3 sentences. Why this work, what it changes, what success looks like.",
  "header_chips": [
    { "label": "complexity: medium", "tone": "info" },
    { "label": "blast radius: contained", "tone": "ok" }
  ],
  "steps": [
    {
      "key": "bootstrap",
      "title": "Bootstrap the extension",
      "body": "Markdown description of the step. Lists, tables, inline `code`, **emphasis**.",
      "files": ["src/extension.ts", "package.json"],
      "callouts": [
        { "tone": "warn", "title": "Heads up", "body": "Don't forget to check existing patterns." }
      ],
      "code": [
        { "lang": "ts", "label": "src/extension.ts", "source": "export default function () { /* ... */ }" }
      ],
      "mockup": {
        "frame": "window",
        "title": "Recording — Product Sync",
        "html": "<header><strong>● Recording</strong><span class=\"spacer\"></span><button data-variant=\"primary\">Stop &amp; Save</button></header><main><section><label>What do you want to remember?</label><input placeholder=\"Ask about pricing roadmap…\"></section></main>"
      },
      "diagram": "flowchart LR\n  A[User] --> B[Extension] --> C[Browser]",
      "verification": "How to confirm this step works."
    }
  ],
  "risks": [ "Concrete risk 1", "Concrete risk 2" ],
  "open_questions": [ "Question we still need a human to answer" ]
}
```
````

**Rules**:

- **One fenced block.** Nothing after the closing fence.
- **Strict JSON.** No comments, no trailing commas.
- Every field outside `title`, `context`, and `steps[].title` is **optional**.
- `steps[].key` is a short, stable kebab-case slug. Reusing keys between v1 and v2 preserves the comment thread on the right section.

### Components

| Field | Purpose |
|---|---|
| `header_chips[]` | Top-of-page status pills. `tone`: `info \| ok \| warn \| bad \| accent`. Max 4. |
| `steps[].body` | Markdown prose. |
| `steps[].files[]` | Files that will be created/edited/deleted (not just read). |
| `steps[].callouts[]` | Annotated cards. `tone`: `note \| warn \| risk \| ok`. |
| `steps[].code[]` | Syntax-highlighted snippets, 5–20 lines. `lang` must be Prism-supported. |
| `steps[].mockup` | Realistic HTML UI mockup in a sandboxed iframe with chrome. See below. |
| `steps[].diagram` | Mermaid syntax (`flowchart`, `sequenceDiagram`, `classDiagram`, `stateDiagram`, `erDiagram`, `gantt`). |
| `steps[].verification` | 1–2 sentences on how to confirm the step works. |
| `risks[]` | Concrete > generic. |
| `open_questions[]` | Things only a human can answer. |

### Mockup frames

| Frame | Use for |
|---|---|
| `window` *(default)* | Desktop app UIs — macOS-style window with traffic lights + title |
| `browser` | Web pages — traffic lights + URL bar (uses `title` as URL) |
| `phone` | Mobile UIs — phone bezel with notch + home indicator |
| `none` | Fragments / partial UIs / chrome-less surfaces |

The iframe ships with a preset stylesheet — plain semantic markup already looks like a real product. Utility classes: `.row .col .spacer .muted .pill .card .primary .danger .success`. Button variants: `data-variant="primary|ghost"`. **No `<script>` tags** (stripped at render time).

</details>

---

## CLI usage (any agent)

The renderer is a standalone CLI — you don't need pi or Claude Code to use it. Any agent (Cursor, Cline, Aider, raw API) that can produce `plan-json` works:

```sh
plan-html render plan.json --open                  # render + open
plan-html extract transcript.md --out plan.json    # pull a plan-json fence out of an LLM transcript
plan-html --help
```

---

## How it works

```
agent → plan-json block → renderer (CLI or pi extension) → HTML in .pi/plans/
  ↑                                                              ↓
  └──── feedback.md ←── browser comments ←── (pi: local server; Claude: download)
```

Both the pi extension and the CLI render through the same design system (`packages/pi-plan-html/design-system/` and `packages/plan-html-cli/design-system/` — kept in sync). Output is byte-identical for the same input.

---

## Theming

Override design tokens in `:root`:

```css
:root {
  --pds-accent: #2e7d32;
  --pds-font-serif: "Lora", serif;
}
```

Dark mode is automatic via `prefers-color-scheme`; force with `data-pds-theme="dark"` on `<html>`.

---

## Development

```sh
git clone https://github.com/saadzniber/pi-plan-html.git
cd pi-plan-html
npm install                              # installs workspaces
npm run pack-all                         # produces tarballs in each package dir
```

**Test the pi extension locally** — pack and install into pi's npm store:

```sh
npm pack --workspace pi-plan-html
mv pi-plan-html-0.1.0.tgz ~/.pi/agent/npm/
cd ~/.pi/agent/npm && npm install ./pi-plan-html-0.1.0.tgz
# add "npm:pi-plan-html" to ~/.pi/agent/settings.json packages
```

**Test the Claude skill locally**:

```sh
./claude-skill/install.sh    # detects local checkout, copies from disk
```

**Test the CLI directly**:

```sh
node packages/plan-html-cli/bin/plan-html.js render examples/plan.json --open
```

---

## FAQ

**Does this work with Cursor / Cline / Aider?**
Yes, via `plan-html-cli`. Tell your agent to write `plan-json` to a file and run `plan-html render <file> --open`.

**Does this require a specific model?**
No. Any model that follows the `plan-json` schema (Claude, GPT, Gemini, local Llamas) works.

**Why does the package include 3 MB of vendored Mermaid?**
So plan viewing works offline. The alternative (CDN at render time) breaks on planes and in sandboxes.

**Plans saved by Claude Code can be iterated in pi and vice-versa?**
Yes. Both write `.pi/plans/<base>-vN.{json,html}` with the same schema.

**The Claude Code skill can't "lock tools" like pi does — what stops Claude from editing files during `/plan`?**
The slash command's prompt explicitly tells Claude not to. The model follows it, but it's prompt-level, not enforced by the harness. If you need strict read-only enforcement, use pi.

---

## Packages

| Package | What it is | Where it goes |
|---|---|---|
| [`pi-plan-html`](https://github.com/saadzniber/pi-plan-html/tree/HEAD/packages/pi-plan-html) | The pi extension | `npm:pi-plan-html` in `~/.pi/agent/settings.json` |
| [`plan-html-cli`](https://github.com/saadzniber/pi-plan-html/tree/HEAD/packages/plan-html-cli) | The standalone renderer CLI | `npm install -g plan-html-cli` |
| [`claude-skill`](https://github.com/saadzniber/pi-plan-html/tree/HEAD/claude-skill) | Claude Code skill + slash commands | `~/.claude/skills/plan-html/` + `~/.claude/commands/` |

---

## License

[MIT](https://github.com/saadzniber/pi-plan-html/tree/HEAD/LICENSE). Built on top of [pi](https://github.com/earendil-works/pi). Package shape inspired by [pi-show-diffs](https://github.com/xRyul/pi-show-diffs).
