11# Plannotator
22
3- A plan review UI for Claude Code that intercepts ` ExitPlanMode ` via hooks, letting users approve or request changes with annotated feedback.
3+ A plan review UI for Claude Code that intercepts ` ExitPlanMode ` via hooks, letting users approve or request changes with annotated feedback. Also provides code review for git diffs.
44
55## Project Structure
66
@@ -9,15 +9,23 @@ plannotator/
99├── apps/
1010│ ├── hook/ # Claude Code plugin
1111│ │ ├── .claude-plugin/plugin.json
12+ │ │ ├── commands/ # Slash commands (plannotator-review.md)
1213│ │ ├── hooks/hooks.json # PermissionRequest hook config
13- │ │ ├── server/index.ts # Entry point (reads stdin, outputs decision)
14- │ │ └── dist/index.html # Built single-file app
15- │ └── opencode-plugin/ # OpenCode plugin
16- │ ├── index.ts # Plugin entry with submit_plan tool
17- │ └── plannotator.html # Built single-file app (copied from hook)
14+ │ │ ├── server/index.ts # Entry point (plan + review subcommand)
15+ │ │ └── dist/ # Built single-file apps (index.html, review.html)
16+ │ ├── opencode-plugin/ # OpenCode plugin
17+ │ │ ├── commands/ # Slash commands (plannotator-review.md)
18+ │ │ ├── index.ts # Plugin entry with submit_plan tool + review event handler
19+ │ │ ├── plannotator.html # Built plan review app
20+ │ │ └── review-editor.html # Built code review app
21+ │ └── review/ # Standalone review server (for development)
22+ │ ├── index.html
23+ │ ├── index.tsx
24+ │ └── vite.config.ts
1825├── packages/
1926│ ├── server/ # Shared server implementation
2027│ │ ├── index.ts # startPlannotatorServer(), handleServerReady()
28+ │ │ ├── review.ts # startReviewServer(), handleReviewServerReady()
2129│ │ ├── storage.ts # Plan saving to disk (getPlanDir, savePlan, etc.)
2230│ │ ├── remote.ts # isRemoteSession(), getServerPort()
2331│ │ ├── browser.ts # openBrowser()
@@ -28,7 +36,12 @@ plannotator/
2836│ │ ├── utils/ # parser.ts, sharing.ts, storage.ts, planSave.ts, agentSwitch.ts
2937│ │ ├── hooks/ # useSharing.ts
3038│ │ └── types.ts
31- │ └── editor/ # Main App.tsx
39+ │ ├── editor/ # Plan review App.tsx
40+ │ └── review-editor/ # Code review UI
41+ │ ├── App.tsx # Main review app
42+ │ ├── components/ # DiffViewer, FileTree, ReviewPanel
43+ │ ├── demoData.ts # Demo diff for standalone mode
44+ │ └── index.css # Review-specific styles
3245├── .claude-plugin/marketplace.json # For marketplace install
3346└── legacy/ # Old pre-monorepo code (reference only)
3447```
@@ -63,7 +76,7 @@ export PLANNOTATOR_REMOTE=1
6376export PLANNOTATOR_PORT=9999
6477```
6578
66- ## Hook Flow
79+ ## Plan Review Flow
6780
6881```
6982Claude calls ExitPlanMode
@@ -80,8 +93,28 @@ Approve → stdout: {"hookSpecificOutput":{"decision":{"behavior":"allow"}}}
8093Deny → stdout: {"hookSpecificOutput":{"decision":{"behavior":"deny","message":"..."}}}
8194```
8295
96+ ## Code Review Flow
97+
98+ ```
99+ User runs /plannotator-review command
100+ ↓
101+ Claude Code: plannotator review subcommand runs
102+ OpenCode: event handler intercepts command
103+ ↓
104+ git diff captures unstaged changes
105+ ↓
106+ Review server starts, opens browser with diff viewer
107+ ↓
108+ User annotates code, provides feedback
109+ ↓
110+ Send Feedback → feedback sent to agent session
111+ Approve → "LGTM" sent to agent session
112+ ```
113+
83114## Server API
84115
116+ ### Plan Server (` packages/server/index.ts ` )
117+
85118| Endpoint | Method | Purpose |
86119| --------------------- | ------ | ------------------------------------------ |
87120| ` /api/plan ` | GET | Returns ` { plan, origin } ` |
@@ -91,9 +124,16 @@ Deny → stdout: {"hookSpecificOutput":{"decision":{"behavior":"deny","messag
91124| ` /api/upload ` | POST | Upload image, returns temp path |
92125| ` /api/obsidian/vaults ` | GET | Detect available Obsidian vaults |
93126
94- ** Location:** ` packages/server/index.ts `
127+ ### Review Server (` packages/server/review.ts ` )
128+
129+ | Endpoint | Method | Purpose |
130+ | --------------------- | ------ | ------------------------------------------ |
131+ | ` /api/diff ` | GET | Returns ` { rawPatch, gitRef, origin } ` |
132+ | ` /api/feedback ` | POST | Submit review (body: feedback, annotations, agentSwitch) |
133+ | ` /api/image ` | GET | Serve image by path query param |
134+ | ` /api/upload ` | POST | Upload image, returns temp path |
95135
96- Both plugins use ` startPlannotatorServer() ` from ` packages/server ` . Port is random locally or fixed (` 19432 ` ) in remote mode.
136+ Both servers use random ports locally or fixed port (` 19432 ` ) in remote mode.
97137
98138## Data Types
99139
@@ -213,7 +253,8 @@ Code blocks use bundled `highlight.js`. Language is extracted from fence (```rus
213253bun install
214254
215255# Run any app
216- bun run dev:hook # Hook server
256+ bun run dev:hook # Hook server (plan review)
257+ bun run dev:review # Review editor (code review)
217258bun run dev:portal # Portal editor
218259bun run dev:marketing # Marketing site
219260```
@@ -222,8 +263,11 @@ bun run dev:marketing # Marketing site
222263
223264``` bash
224265bun run build:hook # Single-file HTML for hook server
266+ bun run build:review # Code review editor
267+ bun run build:opencode # OpenCode plugin (copies HTML from hook + review)
225268bun run build:portal # Static build for share.plannotator.ai
226269bun run build:marketing # Static build for plannotator.ai
270+ bun run build # Build hook + opencode (main targets)
227271```
228272
229273## Test plugin locally
0 commit comments