@ilya-lesikov/pi-pi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/3p/pi-ask-user/index.ts +1835 -0
  2. package/3p/pi-ask-user/package.json +50 -0
  3. package/3p/pi-ask-user/single-select-layout.ts +203 -0
  4. package/3p/pi-lsp/extensions/lsp/client.ts +542 -0
  5. package/3p/pi-lsp/extensions/lsp/config.ts +135 -0
  6. package/3p/pi-lsp/extensions/lsp/effects/command.ts +42 -0
  7. package/3p/pi-lsp/extensions/lsp/effects/filesystem.ts +50 -0
  8. package/3p/pi-lsp/extensions/lsp/effects/runtime.ts +21 -0
  9. package/3p/pi-lsp/extensions/lsp/errors.ts +113 -0
  10. package/3p/pi-lsp/extensions/lsp/formatting.ts +315 -0
  11. package/3p/pi-lsp/extensions/lsp/index.ts +231 -0
  12. package/3p/pi-lsp/extensions/lsp/protocol.ts +209 -0
  13. package/3p/pi-lsp/extensions/lsp/retry.ts +39 -0
  14. package/3p/pi-lsp/extensions/lsp/tools/programs.ts +293 -0
  15. package/3p/pi-lsp/extensions/lsp/tools.ts +89 -0
  16. package/3p/pi-lsp/extensions/lsp/types.ts +243 -0
  17. package/3p/pi-lsp/package.json +54 -0
  18. package/3p/pi-plannotator/AGENTS.md +583 -0
  19. package/3p/pi-plannotator/apps/pi-extension/README.md +228 -0
  20. package/3p/pi-plannotator/apps/pi-extension/assistant-message.ts +128 -0
  21. package/3p/pi-plannotator/apps/pi-extension/config.test.ts +166 -0
  22. package/3p/pi-plannotator/apps/pi-extension/config.ts +318 -0
  23. package/3p/pi-plannotator/apps/pi-extension/current-pi-session.ts +147 -0
  24. package/3p/pi-plannotator/apps/pi-extension/index.ts +1279 -0
  25. package/3p/pi-plannotator/apps/pi-extension/package.json +56 -0
  26. package/3p/pi-plannotator/apps/pi-extension/plannotator-browser.test.ts +13 -0
  27. package/3p/pi-plannotator/apps/pi-extension/plannotator-browser.ts +616 -0
  28. package/3p/pi-plannotator/apps/pi-extension/plannotator-events.ts +342 -0
  29. package/3p/pi-plannotator/apps/pi-extension/plannotator.json +12 -0
  30. package/3p/pi-plannotator/apps/pi-extension/server/agent-jobs.ts +515 -0
  31. package/3p/pi-plannotator/apps/pi-extension/server/ai-runtime.ts +169 -0
  32. package/3p/pi-plannotator/apps/pi-extension/server/annotations.ts +85 -0
  33. package/3p/pi-plannotator/apps/pi-extension/server/external-annotations.ts +189 -0
  34. package/3p/pi-plannotator/apps/pi-extension/server/handlers.ts +210 -0
  35. package/3p/pi-plannotator/apps/pi-extension/server/helpers.ts +78 -0
  36. package/3p/pi-plannotator/apps/pi-extension/server/ide.ts +46 -0
  37. package/3p/pi-plannotator/apps/pi-extension/server/integrations.ts +195 -0
  38. package/3p/pi-plannotator/apps/pi-extension/server/network.test.ts +158 -0
  39. package/3p/pi-plannotator/apps/pi-extension/server/network.ts +268 -0
  40. package/3p/pi-plannotator/apps/pi-extension/server/pr.ts +126 -0
  41. package/3p/pi-plannotator/apps/pi-extension/server/project.ts +64 -0
  42. package/3p/pi-plannotator/apps/pi-extension/server/reference.ts +362 -0
  43. package/3p/pi-plannotator/apps/pi-extension/server/serverAnnotate.ts +199 -0
  44. package/3p/pi-plannotator/apps/pi-extension/server/serverPlan.ts +505 -0
  45. package/3p/pi-plannotator/apps/pi-extension/server/serverReview.ts +1229 -0
  46. package/3p/pi-plannotator/apps/pi-extension/server/vcs.ts +121 -0
  47. package/3p/pi-plannotator/apps/pi-extension/server.test.ts +966 -0
  48. package/3p/pi-plannotator/apps/pi-extension/server.ts +44 -0
  49. package/3p/pi-plannotator/apps/pi-extension/tool-scope.test.ts +88 -0
  50. package/3p/pi-plannotator/apps/pi-extension/tool-scope.ts +39 -0
  51. package/3p/pi-plannotator/apps/pi-extension/tsconfig.json +16 -0
  52. package/3p/pi-plannotator/apps/pi-extension/vendor.sh +48 -0
  53. package/3p/pi-plannotator/package.json +53 -0
  54. package/3p/pi-plannotator/packages/ai/ai.test.ts +1407 -0
  55. package/3p/pi-plannotator/packages/ai/base-session.ts +94 -0
  56. package/3p/pi-plannotator/packages/ai/context.ts +250 -0
  57. package/3p/pi-plannotator/packages/ai/endpoints.ts +326 -0
  58. package/3p/pi-plannotator/packages/ai/index.ts +105 -0
  59. package/3p/pi-plannotator/packages/ai/package.json +21 -0
  60. package/3p/pi-plannotator/packages/ai/provider.ts +103 -0
  61. package/3p/pi-plannotator/packages/ai/providers/claude-agent-sdk.ts +447 -0
  62. package/3p/pi-plannotator/packages/ai/providers/codex-sdk.ts +430 -0
  63. package/3p/pi-plannotator/packages/ai/providers/command-path.ts +115 -0
  64. package/3p/pi-plannotator/packages/ai/providers/opencode-sdk.ts +546 -0
  65. package/3p/pi-plannotator/packages/ai/providers/pi-events.ts +110 -0
  66. package/3p/pi-plannotator/packages/ai/providers/pi-sdk-node.ts +425 -0
  67. package/3p/pi-plannotator/packages/ai/providers/pi-sdk.ts +469 -0
  68. package/3p/pi-plannotator/packages/ai/session-manager.ts +195 -0
  69. package/3p/pi-plannotator/packages/ai/tsconfig.json +15 -0
  70. package/3p/pi-plannotator/packages/ai/types.ts +379 -0
  71. package/3p/pi-plannotator/packages/server/agent-jobs.ts +538 -0
  72. package/3p/pi-plannotator/packages/server/agent-review-message.test.ts +135 -0
  73. package/3p/pi-plannotator/packages/server/agent-review-message.ts +243 -0
  74. package/3p/pi-plannotator/packages/server/ai-runtime.ts +108 -0
  75. package/3p/pi-plannotator/packages/server/annotate.ts +401 -0
  76. package/3p/pi-plannotator/packages/server/browser.test.ts +87 -0
  77. package/3p/pi-plannotator/packages/server/browser.ts +245 -0
  78. package/3p/pi-plannotator/packages/server/claude-review.ts +352 -0
  79. package/3p/pi-plannotator/packages/server/code-nav.ts +73 -0
  80. package/3p/pi-plannotator/packages/server/codex-review-schema.json +41 -0
  81. package/3p/pi-plannotator/packages/server/codex-review.ts +330 -0
  82. package/3p/pi-plannotator/packages/server/config.ts +8 -0
  83. package/3p/pi-plannotator/packages/server/draft.ts +1 -0
  84. package/3p/pi-plannotator/packages/server/editor-annotations.ts +76 -0
  85. package/3p/pi-plannotator/packages/server/external-annotations.test.ts +18 -0
  86. package/3p/pi-plannotator/packages/server/external-annotations.ts +207 -0
  87. package/3p/pi-plannotator/packages/server/git.ts +141 -0
  88. package/3p/pi-plannotator/packages/server/goal-setup.test.ts +55 -0
  89. package/3p/pi-plannotator/packages/server/goal-setup.ts +248 -0
  90. package/3p/pi-plannotator/packages/server/ide.ts +43 -0
  91. package/3p/pi-plannotator/packages/server/image.test.ts +63 -0
  92. package/3p/pi-plannotator/packages/server/image.ts +66 -0
  93. package/3p/pi-plannotator/packages/server/index.ts +648 -0
  94. package/3p/pi-plannotator/packages/server/integrations.test.ts +172 -0
  95. package/3p/pi-plannotator/packages/server/integrations.ts +214 -0
  96. package/3p/pi-plannotator/packages/server/jj.test.ts +69 -0
  97. package/3p/pi-plannotator/packages/server/jj.ts +86 -0
  98. package/3p/pi-plannotator/packages/server/p4.ts +417 -0
  99. package/3p/pi-plannotator/packages/server/package.json +40 -0
  100. package/3p/pi-plannotator/packages/server/path-utils.ts +18 -0
  101. package/3p/pi-plannotator/packages/server/pr.ts +146 -0
  102. package/3p/pi-plannotator/packages/server/project.test.ts +115 -0
  103. package/3p/pi-plannotator/packages/server/project.ts +45 -0
  104. package/3p/pi-plannotator/packages/server/reference-handlers.ts +404 -0
  105. package/3p/pi-plannotator/packages/server/remote.test.ts +150 -0
  106. package/3p/pi-plannotator/packages/server/remote.ts +74 -0
  107. package/3p/pi-plannotator/packages/server/repo.ts +80 -0
  108. package/3p/pi-plannotator/packages/server/resolve-file.test.ts +310 -0
  109. package/3p/pi-plannotator/packages/server/review-workspace.test.ts +1032 -0
  110. package/3p/pi-plannotator/packages/server/review-workspace.ts +48 -0
  111. package/3p/pi-plannotator/packages/server/review.ts +1318 -0
  112. package/3p/pi-plannotator/packages/server/sessions.ts +111 -0
  113. package/3p/pi-plannotator/packages/server/share-url.ts +53 -0
  114. package/3p/pi-plannotator/packages/server/shared-handlers.test.ts +43 -0
  115. package/3p/pi-plannotator/packages/server/shared-handlers.ts +177 -0
  116. package/3p/pi-plannotator/packages/server/storage.test.ts +176 -0
  117. package/3p/pi-plannotator/packages/server/storage.ts +17 -0
  118. package/3p/pi-plannotator/packages/server/tour/tour-review.test.ts +146 -0
  119. package/3p/pi-plannotator/packages/server/tour/tour-review.ts +604 -0
  120. package/3p/pi-plannotator/packages/server/tsconfig.json +15 -0
  121. package/3p/pi-plannotator/packages/server/vcs.test.ts +48 -0
  122. package/3p/pi-plannotator/packages/server/vcs.ts +80 -0
  123. package/3p/pi-plannotator/packages/shared/agent-jobs.ts +132 -0
  124. package/3p/pi-plannotator/packages/shared/agents.ts +53 -0
  125. package/3p/pi-plannotator/packages/shared/annotate-args.test.ts +386 -0
  126. package/3p/pi-plannotator/packages/shared/annotate-args.ts +107 -0
  127. package/3p/pi-plannotator/packages/shared/at-reference.test.ts +99 -0
  128. package/3p/pi-plannotator/packages/shared/at-reference.ts +52 -0
  129. package/3p/pi-plannotator/packages/shared/checklist.ts +52 -0
  130. package/3p/pi-plannotator/packages/shared/code-file.test.ts +112 -0
  131. package/3p/pi-plannotator/packages/shared/code-file.ts +41 -0
  132. package/3p/pi-plannotator/packages/shared/code-nav.test.ts +515 -0
  133. package/3p/pi-plannotator/packages/shared/code-nav.ts +436 -0
  134. package/3p/pi-plannotator/packages/shared/compress.ts +51 -0
  135. package/3p/pi-plannotator/packages/shared/config.ts +262 -0
  136. package/3p/pi-plannotator/packages/shared/crypto.test.ts +172 -0
  137. package/3p/pi-plannotator/packages/shared/crypto.ts +97 -0
  138. package/3p/pi-plannotator/packages/shared/data-dir.ts +42 -0
  139. package/3p/pi-plannotator/packages/shared/diff-paths.test.ts +30 -0
  140. package/3p/pi-plannotator/packages/shared/diff-paths.ts +137 -0
  141. package/3p/pi-plannotator/packages/shared/draft.ts +64 -0
  142. package/3p/pi-plannotator/packages/shared/external-annotation.ts +397 -0
  143. package/3p/pi-plannotator/packages/shared/extract-code-paths.test.ts +59 -0
  144. package/3p/pi-plannotator/packages/shared/extract-code-paths.ts +66 -0
  145. package/3p/pi-plannotator/packages/shared/favicon.ts +5 -0
  146. package/3p/pi-plannotator/packages/shared/feedback-templates.test.ts +65 -0
  147. package/3p/pi-plannotator/packages/shared/feedback-templates.ts +29 -0
  148. package/3p/pi-plannotator/packages/shared/goal-setup.test.ts +231 -0
  149. package/3p/pi-plannotator/packages/shared/goal-setup.ts +336 -0
  150. package/3p/pi-plannotator/packages/shared/html-to-markdown.test.ts +62 -0
  151. package/3p/pi-plannotator/packages/shared/html-to-markdown.ts +32 -0
  152. package/3p/pi-plannotator/packages/shared/improvement-hooks.test.ts +135 -0
  153. package/3p/pi-plannotator/packages/shared/improvement-hooks.ts +115 -0
  154. package/3p/pi-plannotator/packages/shared/integrations-common.ts +243 -0
  155. package/3p/pi-plannotator/packages/shared/jj-core.test.ts +236 -0
  156. package/3p/pi-plannotator/packages/shared/jj-core.ts +433 -0
  157. package/3p/pi-plannotator/packages/shared/package.json +55 -0
  158. package/3p/pi-plannotator/packages/shared/pfm-reminder.test.ts +88 -0
  159. package/3p/pi-plannotator/packages/shared/pfm-reminder.ts +80 -0
  160. package/3p/pi-plannotator/packages/shared/pr-github.ts +661 -0
  161. package/3p/pi-plannotator/packages/shared/pr-gitlab.test.ts +202 -0
  162. package/3p/pi-plannotator/packages/shared/pr-gitlab.ts +620 -0
  163. package/3p/pi-plannotator/packages/shared/pr-provider.test.ts +266 -0
  164. package/3p/pi-plannotator/packages/shared/pr-provider.ts +123 -0
  165. package/3p/pi-plannotator/packages/shared/pr-stack.test.ts +104 -0
  166. package/3p/pi-plannotator/packages/shared/pr-stack.ts +194 -0
  167. package/3p/pi-plannotator/packages/shared/pr-types.ts +326 -0
  168. package/3p/pi-plannotator/packages/shared/project.ts +71 -0
  169. package/3p/pi-plannotator/packages/shared/prompts-integration.test.ts +421 -0
  170. package/3p/pi-plannotator/packages/shared/prompts.test.ts +504 -0
  171. package/3p/pi-plannotator/packages/shared/prompts.ts +247 -0
  172. package/3p/pi-plannotator/packages/shared/reference-common.ts +87 -0
  173. package/3p/pi-plannotator/packages/shared/repo.ts +71 -0
  174. package/3p/pi-plannotator/packages/shared/resolve-file.test.ts +113 -0
  175. package/3p/pi-plannotator/packages/shared/resolve-file.ts +509 -0
  176. package/3p/pi-plannotator/packages/shared/review-args.test.ts +64 -0
  177. package/3p/pi-plannotator/packages/shared/review-args.ts +85 -0
  178. package/3p/pi-plannotator/packages/shared/review-core.test.ts +286 -0
  179. package/3p/pi-plannotator/packages/shared/review-core.ts +895 -0
  180. package/3p/pi-plannotator/packages/shared/review-workspace-node.ts +230 -0
  181. package/3p/pi-plannotator/packages/shared/review-workspace.ts +436 -0
  182. package/3p/pi-plannotator/packages/shared/semantic-diff-types.ts +76 -0
  183. package/3p/pi-plannotator/packages/shared/semantic-diff.test.ts +322 -0
  184. package/3p/pi-plannotator/packages/shared/semantic-diff.ts +520 -0
  185. package/3p/pi-plannotator/packages/shared/storage.ts +378 -0
  186. package/3p/pi-plannotator/packages/shared/tour.ts +61 -0
  187. package/3p/pi-plannotator/packages/shared/tsconfig.json +15 -0
  188. package/3p/pi-plannotator/packages/shared/types.ts +29 -0
  189. package/3p/pi-plannotator/packages/shared/url-to-markdown.test.ts +177 -0
  190. package/3p/pi-plannotator/packages/shared/url-to-markdown.ts +351 -0
  191. package/3p/pi-plannotator/packages/shared/vcs-core.test.ts +332 -0
  192. package/3p/pi-plannotator/packages/shared/vcs-core.ts +482 -0
  193. package/3p/pi-plannotator/packages/shared/worktree-pool.test.ts +162 -0
  194. package/3p/pi-plannotator/packages/shared/worktree-pool.ts +103 -0
  195. package/3p/pi-plannotator/packages/shared/worktree.ts +119 -0
  196. package/3p/pi-subagents/package.json +50 -0
  197. package/3p/pi-subagents/src/agent-manager.ts +413 -0
  198. package/3p/pi-subagents/src/agent-runner.ts +502 -0
  199. package/3p/pi-subagents/src/agent-types.ts +248 -0
  200. package/3p/pi-subagents/src/context.ts +58 -0
  201. package/3p/pi-subagents/src/cross-extension-rpc.ts +135 -0
  202. package/3p/pi-subagents/src/custom-agents.ts +137 -0
  203. package/3p/pi-subagents/src/default-agents.ts +144 -0
  204. package/3p/pi-subagents/src/env.ts +33 -0
  205. package/3p/pi-subagents/src/group-join.ts +141 -0
  206. package/3p/pi-subagents/src/index.ts +1811 -0
  207. package/3p/pi-subagents/src/invocation-config.ts +40 -0
  208. package/3p/pi-subagents/src/memory.ts +165 -0
  209. package/3p/pi-subagents/src/model-resolver.ts +81 -0
  210. package/3p/pi-subagents/src/output-file.ts +77 -0
  211. package/3p/pi-subagents/src/prompts.ts +85 -0
  212. package/3p/pi-subagents/src/skill-loader.ts +79 -0
  213. package/3p/pi-subagents/src/types.ts +111 -0
  214. package/3p/pi-subagents/src/ui/agent-widget.ts +496 -0
  215. package/3p/pi-subagents/src/ui/conversation-viewer.ts +250 -0
  216. package/3p/pi-subagents/src/worktree.ts +162 -0
  217. package/3p/pi-tasks/package.json +52 -0
  218. package/3p/pi-tasks/src/auto-clear.ts +91 -0
  219. package/3p/pi-tasks/src/index.ts +1150 -0
  220. package/3p/pi-tasks/src/process-tracker.ts +140 -0
  221. package/3p/pi-tasks/src/reminder-cadence.ts +90 -0
  222. package/3p/pi-tasks/src/task-store.ts +324 -0
  223. package/3p/pi-tasks/src/tasks-config.ts +27 -0
  224. package/3p/pi-tasks/src/types.ts +40 -0
  225. package/3p/pi-tasks/src/ui/settings-menu.ts +152 -0
  226. package/3p/pi-tasks/src/ui/task-widget.ts +296 -0
  227. package/AGENTS.md +28 -0
  228. package/LICENSE +201 -0
  229. package/extensions/orchestrator/agents/brainstorm-reviewer.ts +77 -0
  230. package/extensions/orchestrator/agents/code-reviewer.ts +89 -0
  231. package/extensions/orchestrator/agents/explore.ts +33 -0
  232. package/extensions/orchestrator/agents/librarian.ts +43 -0
  233. package/extensions/orchestrator/agents/plan-reviewer.ts +75 -0
  234. package/extensions/orchestrator/agents/planner.ts +61 -0
  235. package/extensions/orchestrator/agents/registry.ts +156 -0
  236. package/extensions/orchestrator/agents/task.ts +50 -0
  237. package/extensions/orchestrator/agents/tool-routing.ts +84 -0
  238. package/extensions/orchestrator/ast-search.ts +85 -0
  239. package/extensions/orchestrator/cbm.ts +330 -0
  240. package/extensions/orchestrator/command-handlers.test.ts +163 -0
  241. package/extensions/orchestrator/command-handlers.ts +116 -0
  242. package/extensions/orchestrator/commands.test.ts +81 -0
  243. package/extensions/orchestrator/commands.ts +75 -0
  244. package/extensions/orchestrator/config.test.ts +221 -0
  245. package/extensions/orchestrator/config.ts +230 -0
  246. package/extensions/orchestrator/context.test.ts +293 -0
  247. package/extensions/orchestrator/context.ts +206 -0
  248. package/extensions/orchestrator/event-handlers.test.ts +190 -0
  249. package/extensions/orchestrator/event-handlers.ts +1416 -0
  250. package/extensions/orchestrator/exa.ts +104 -0
  251. package/extensions/orchestrator/flant-infra.ts +486 -0
  252. package/extensions/orchestrator/index.ts +80 -0
  253. package/extensions/orchestrator/integration.test.ts +1214 -0
  254. package/extensions/orchestrator/orchestrator.test.ts +252 -0
  255. package/extensions/orchestrator/orchestrator.ts +538 -0
  256. package/extensions/orchestrator/phases/brainstorm.test.ts +15 -0
  257. package/extensions/orchestrator/phases/brainstorm.ts +273 -0
  258. package/extensions/orchestrator/phases/implementation.ts +40 -0
  259. package/extensions/orchestrator/phases/machine.test.ts +293 -0
  260. package/extensions/orchestrator/phases/machine.ts +209 -0
  261. package/extensions/orchestrator/phases/planning.ts +255 -0
  262. package/extensions/orchestrator/phases/review.ts +193 -0
  263. package/extensions/orchestrator/plannotator.ts +56 -0
  264. package/extensions/orchestrator/pp-menu.ts +866 -0
  265. package/extensions/orchestrator/state.test.ts +343 -0
  266. package/extensions/orchestrator/state.ts +237 -0
  267. package/extensions/orchestrator/validate-artifacts.test.ts +88 -0
  268. package/extensions/orchestrator/validate-artifacts.ts +272 -0
  269. package/extensions/orchestrator/vendor.d.ts +26 -0
  270. package/package.json +73 -0
  271. package/scripts/postinstall.sh +18 -0
@@ -0,0 +1,583 @@
1
+ # Plannotator
2
+
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 and annotation of arbitrary markdown files.
4
+
5
+ ## Project Structure
6
+
7
+ ```
8
+ plannotator/
9
+ ├── apps/
10
+ │ ├── hook/ # Claude Code plugin (no commands/ — core skills installed to ~/.claude/skills act as slash commands)
11
+ │ │ ├── .claude-plugin/plugin.json
12
+ │ │ ├── hooks/hooks.json # PermissionRequest hook config
13
+ │ │ ├── server/index.ts # Entry point (plan + review + annotate + archive subcommands)
14
+ │ │ └── dist/ # Built single-file apps (index.html, review.html)
15
+ │ ├── opencode-plugin/ # OpenCode plugin
16
+ │ │ ├── commands/ # Slash command stubs (review, annotate, last — plugin intercepts execution)
17
+ │ │ ├── index.ts # Plugin entry with submit_plan tool + review/annotate event handlers
18
+ │ │ ├── plannotator.html # Built plan review app
19
+ │ │ └── review-editor.html # Built code review app
20
+ │ ├── amp-plugin/ # Amp plugin
21
+ │ │ ├── plannotator.ts # Native Amp command-palette integration
22
+ │ │ └── README.md # Install and local development notes
23
+ │ ├── droid-plugin/ # Droid plugin
24
+ │ │ ├── .factory-plugin/plugin.json
25
+ │ │ ├── commands/ # Slash command entrypoints
26
+ │ │ └── lib/ # Shared command wrapper helpers
27
+ │ ├── marketing/ # Marketing site, docs, and blog (plannotator.ai)
28
+ │ │ └── astro.config.mjs # Astro 5 static site with content collections
29
+ │ ├── kiro-cli/ # Kiro CLI integration source (consumed by scripts/install.sh; auto-detected via ~/.kiro)
30
+ │ │ ├── agents/plannotator.json # Example Kiro custom agent
31
+ │ │ └── skills/ # Kiro-specific skill packages (review, annotate); setup-goal + visual-explainer install from apps/skills/extra
32
+ │ ├── paste-service/ # Paste service for short URL sharing
33
+ │ │ ├── core/ # Platform-agnostic logic (handler, storage interface, cors)
34
+ │ │ ├── stores/ # Storage backends (fs, kv, s3)
35
+ │ │ └── targets/ # Deployment entries (bun.ts, cloudflare.ts)
36
+ │ ├── review/ # Standalone review server (for development)
37
+ │ │ ├── index.html
38
+ │ │ ├── index.tsx
39
+ │ │ └── vite.config.ts
40
+ │ ├── vscode-extension/ # VS Code extension — opens plans in editor tabs
41
+ │ │ ├── bin/ # Router scripts (open-in-vscode, xdg-open)
42
+ │ │ ├── src/ # extension.ts, cookie-proxy.ts, ipc-server.ts, panel-manager.ts, editor-annotations.ts, vscode-theme.ts
43
+ │ │ └── package.json # Extension manifest (publisher: backnotprop)
44
+ │ └── skills/ # Agent skills (agentskills.io format)
45
+ │ ├── core/ # CORE skills (single-sourced) — installed to ~/.claude/skills and ~/.agents/skills (Codex)
46
+ │ │ ├── plannotator-review/ # Lightweight: opens review UI
47
+ │ │ ├── plannotator-annotate/ # Lightweight: opens annotate UI
48
+ │ │ └── plannotator-last/ # Lightweight: annotates last message
49
+ │ └── extra/ # EXTRA skills — NOT default-installed (except Kiro); add via `npx skills add backnotprop/plannotator/apps/skills/extra`
50
+ │ ├── plannotator-compound/ # Research analysis agent (map-reduce over denied plans)
51
+ │ ├── plannotator-setup-goal/ # Goal package scaffolder for /goal workflows
52
+ │ └── plannotator-visual-explainer/ # Visual HTML generator (plans, diagrams, PR explainers) with Plannotator theming
53
+ ├── packages/
54
+ │ ├── server/ # Shared server implementation
55
+ │ │ ├── index.ts # startPlannotatorServer(), handleServerReady()
56
+ │ │ ├── review.ts # startReviewServer(), handleReviewServerReady()
57
+ │ │ ├── annotate.ts # startAnnotateServer(), handleAnnotateServerReady()
58
+ │ │ ├── storage.ts # Re-exports from @plannotator/shared/storage
59
+ │ │ ├── share-url.ts # Server-side share URL generation for remote sessions
60
+ │ │ ├── remote.ts # isRemoteSession(), getServerPort()
61
+ │ │ ├── browser.ts # openBrowser()
62
+ │ │ ├── draft.ts # Re-exports from @plannotator/shared/draft
63
+ │ │ ├── integrations.ts # Obsidian, Bear integrations
64
+ │ │ ├── ide.ts # VS Code diff integration (openEditorDiff)
65
+ │ │ ├── editor-annotations.ts # VS Code editor annotation endpoints
66
+ │ │ └── project.ts # Project name detection for tags
67
+ │ ├── ui/ # Shared React components + theme
68
+ │ │ ├── theme.css # Single source of truth for color tokens + Tailwind bridge
69
+ │ │ ├── components/ # Viewer, Toolbar, Settings, etc.
70
+ │ │ │ ├── icons/ # Shared SVG icon components (themeIcons, etc.)
71
+ │ │ │ ├── plan-diff/ # PlanDiffBadge, PlanDiffViewer, clean/raw diff views
72
+ │ │ │ └── sidebar/ # SidebarContainer, SidebarTabs, VersionBrowser, ArchiveBrowser
73
+ │ │ ├── shortcuts/ # Keyboard shortcut registry (see Keyboard Shortcuts section below)
74
+ │ │ │ ├── core.ts # Engine: parser, formatter, dispatcher, validator
75
+ │ │ │ ├── runtime.ts # Engine: useShortcutScope, useDoubleTapShortcuts hooks
76
+ │ │ │ ├── index.ts # Barrel — re-exports engine + scopes from both subfolders
77
+ │ │ │ ├── plan-review/ # Scopes for plan-editor surfaces (annotationToolbar, annotationPanel, commentPopover, imageAnnotator, inputMethod, viewer)
78
+ │ │ │ └── code-review/ # Scopes for review-editor surfaces (ai, allFilesDiff, annotationToolbar, fileTree, prComments, suggestionModal, tourDialog)
79
+ │ │ ├── shortcuts.test.ts # Registry unit tests (parser, dispatcher, validator)
80
+ │ │ ├── utils/ # parser.ts, sharing.ts, storage.ts, planSave.ts, agentSwitch.ts, planDiffEngine.ts, planAgentInstructions.ts
81
+ │ │ ├── hooks/ # useAnnotationHighlighter.ts, useSharing.ts, usePlanDiff.ts, useSidebar.ts, useLinkedDoc.ts, useAnnotationDraft.ts, useCodeAnnotationDraft.ts, useArchive.ts
82
+ │ │ └── types.ts
83
+ │ ├── ai/ # Provider-agnostic AI backbone (providers, sessions, endpoints)
84
+ │ ├── shared/ # Shared types, utilities, and cross-runtime logic
85
+ │ │ ├── storage.ts # Plan saving, version history, archive listing (node:fs only)
86
+ │ │ ├── draft.ts # Annotation draft persistence (node:fs only)
87
+ │ │ └── project.ts # Pure string helpers (sanitizeTag, extractRepoName, extractDirName)
88
+ │ ├── editor/ # Plan review app
89
+ │ │ ├── App.tsx # Main plan review app
90
+ │ │ └── shortcuts.ts # planReviewSurface + annotateSurface — composes plan-review scopes into per-surface registries
91
+ │ └── review-editor/ # Code review UI
92
+ │ ├── App.tsx # Main review app
93
+ │ ├── shortcuts.ts # codeReviewSurface — composes code-review scopes into the review registry
94
+ │ ├── components/ # DiffViewer, FileTree, ReviewSidebar
95
+ │ ├── dock/ # Dockview center panel infrastructure
96
+ │ ├── demoData.ts # Demo diff for standalone mode
97
+ │ └── index.css # Review-specific styles
98
+ ├── .claude-plugin/marketplace.json # For marketplace install
99
+ └── legacy/ # Old pre-monorepo code (reference only)
100
+ ```
101
+
102
+ ## Server Runtimes
103
+
104
+ There are two separate server implementations with the same API surface:
105
+
106
+ - **Bun server** (`packages/server/`) — used by both Claude Code (`apps/hook/`) and OpenCode (`apps/opencode-plugin/`). These plugins import directly from `@plannotator/server`.
107
+ - **Pi server** (`apps/pi-extension/server/`) — a standalone Node.js server for the Pi extension. It mirrors the Bun server's API but uses `node:http` primitives instead of Bun's `Request`/`Response` APIs.
108
+
109
+ When adding or modifying server endpoints, both implementations must be updated. Runtime-agnostic logic (store, validation, types) lives in `packages/shared/` and is imported by both.
110
+
111
+ ## Installation
112
+
113
+ **Via plugin marketplace** (when repo is public):
114
+
115
+ ```
116
+ /plugin marketplace add backnotprop/plannotator
117
+ ```
118
+
119
+ **Local testing:**
120
+
121
+ ```bash
122
+ claude --plugin-dir ./apps/hook
123
+ ```
124
+
125
+ ## Environment Variables
126
+
127
+ | Variable | Description |
128
+ |----------|-------------|
129
+ | `PLANNOTATOR_REMOTE` | Set to `1` / `true` for remote mode, `0` / `false` for local mode, or leave unset for SSH auto-detection. Uses a fixed port in remote mode; browser-opening behavior depends on the environment. |
130
+ | `PLANNOTATOR_PORT` | Fixed port to use. Default: random locally, `19432` for remote sessions. |
131
+ | `PLANNOTATOR_BROWSER` | Custom browser to open plans in. macOS: app name or path. Linux/Windows: executable path. |
132
+ | `PLANNOTATOR_SHARE` | Set to `disabled` to turn off URL sharing entirely. Default: enabled. |
133
+ | `PLANNOTATOR_SHARE_URL` | Custom base URL for share links (self-hosted portal). Default: `https://share.plannotator.ai`. |
134
+ | `PLANNOTATOR_PASTE_URL` | Base URL of the paste service API for short URL sharing. Default: `https://plannotator-paste.plannotator.workers.dev`. |
135
+ | `PLANNOTATOR_ORIGIN` | Explicit agent-origin override at the top of the detection chain. Valid values: `claude-code`, `amp`, `droid`, `opencode`, `codex`, `copilot-cli`, `gemini-cli`, `kiro-cli`, `pi`. Invalid values silently fall through to env-based detection. Unset by default. |
136
+ | `PLANNOTATOR_JINA` | Set to `0` / `false` to disable Jina Reader for URL annotation, or `1` / `true` to enable. Default: enabled. Can also be set via `~/.plannotator/config.json` (`{ "jina": false }`) or per-invocation via `--no-jina`. |
137
+ | `JINA_API_KEY` | Optional Jina Reader API key for higher rate limits (500 RPM vs 20 RPM unauthenticated). Free keys include 10M tokens. |
138
+ | `PLANNOTATOR_DATA_DIR` | Override the base data directory. Supports `~` expansion. Default: `~/.plannotator`. All data (plans, history, drafts, config, hooks, sessions, debug logs, IPC registry) is stored under this directory. |
139
+ | `PLANNOTATOR_GLIMPSE` | Set to `0` / `false` to disable the Glimpse native window even when `glimpseui` is installed. Default: enabled. Can also be set via `~/.plannotator/config.json` (`{ "glimpse": false }`). |
140
+ | `PLANNOTATOR_GLIMPSE_WIDTH` | Width in pixels for the Glimpse native window. Default: `1280`. |
141
+ | `PLANNOTATOR_GLIMPSE_HEIGHT` | Height in pixels for the Glimpse native window. Default: `900`. |
142
+ | `PLANNOTATOR_VERIFY_ATTESTATION` | **Read by the install scripts only**, not by the runtime binary. Set to `1` / `true` to have `scripts/install.sh` / `install.ps1` / `install.cmd` run `gh attestation verify` on every install. Off by default. Can also be set persistently via `~/.plannotator/config.json` (`{ "verifyAttestation": true }`) or per-invocation via `--verify-attestation`. Requires `gh` installed and authenticated. |
143
+
144
+ **Config-only settings (`~/.plannotator/config.json`)**: Some settings have no env-var equivalent and are toggled by editing the config file directly:
145
+
146
+ - `pfmReminder` (`true` / `false`, default `false`) — when enabled, a Plannotator Flavored Markdown reminder is injected at plan-time describing the renderer's extensions (code-file links, callouts, tables, diagrams, task lists, hex swatches, wiki-links). Lets the planning agent enrich plans with PFM features without having to discover them. Composes cleanly with the compound-skill improvement hook. Supported across all three runtimes: Claude Code (`improve-context` PreToolUse hook in `apps/hook/server/index.ts`), OpenCode (`experimental.chat.system.transform` in `apps/opencode-plugin/index.ts`), and Pi (`before_agent_start` in `apps/pi-extension/index.ts`).
147
+
148
+ **Legacy:** `SSH_TTY` and `SSH_CONNECTION` are still detected when `PLANNOTATOR_REMOTE` is unset. Set `PLANNOTATOR_REMOTE=1` / `true` to force remote mode or `0` / `false` to force local mode.
149
+
150
+ **Devcontainer/SSH usage:**
151
+ ```bash
152
+ export PLANNOTATOR_REMOTE=1
153
+ export PLANNOTATOR_PORT=9999
154
+ ```
155
+
156
+ ## Plan Review Flow
157
+
158
+ ```
159
+ Claude calls ExitPlanMode
160
+
161
+ PermissionRequest hook fires
162
+
163
+ Bun server reads plan from stdin JSON (tool_input.plan)
164
+
165
+ Server starts on random port, opens browser
166
+
167
+ User reviews plan, optionally adds annotations
168
+
169
+ Approve → stdout: {"hookSpecificOutput":{"decision":{"behavior":"allow"}}}
170
+ Deny → stdout: {"hookSpecificOutput":{"decision":{"behavior":"deny","message":"..."}}}
171
+ ```
172
+
173
+ ## Code Review Flow
174
+
175
+ ```
176
+ User runs /plannotator-review command
177
+
178
+ Claude Code: plannotator review subcommand runs
179
+ OpenCode: event handler intercepts command
180
+
181
+ VCS diff captures local changes (git diff or jj diff). When review runs from a
182
+ non-VCS parent that contains nested Git repos, child diffs are combined with
183
+ folder-prefixed paths.
184
+
185
+ Review server starts, opens browser with diff viewer
186
+
187
+ User annotates code, provides feedback
188
+
189
+ Send Feedback → feedback sent to agent session
190
+ Approve → "LGTM" sent to agent session
191
+ ```
192
+
193
+ ## Ask AI Provider Defaults
194
+
195
+ Ask AI providers are detected independently from installed/authenticated local CLIs, then the UI picks a default from the detected Plannotator origin. The mapping lives in `packages/shared/agents.ts` and is applied by `packages/ui/utils/aiProvider.ts`:
196
+
197
+ | Origin | Preferred Ask AI provider |
198
+ |--------|---------------------------|
199
+ | `claude-code` | `claude-agent-sdk` |
200
+ | `amp` | no dedicated provider; fallback to saved/server default |
201
+ | `droid` | no dedicated provider; fallback to saved/server default |
202
+ | `codex` | `codex-sdk` |
203
+ | `opencode` | `opencode-sdk` |
204
+ | `pi` | `pi-sdk` |
205
+ | `copilot-cli` | no dedicated provider; fallback to saved/server default |
206
+ | `gemini-cli` | no dedicated provider; fallback to saved/server default |
207
+
208
+ Per-origin choices are persisted in cookies, so a user can override the automatic match for one agent without changing the default for another.
209
+
210
+ ## Annotate Flow
211
+
212
+ ```
213
+ User runs /plannotator-annotate <file.md | file.html | https://... | folder/>
214
+
215
+ Claude Code: plannotator annotate subcommand runs
216
+ OpenCode/Pi: event handler intercepts command
217
+
218
+ Input type detected:
219
+ .md/.mdx → file read from disk
220
+ .html/.htm → file read, converted to markdown via Turndown (or rendered as-is with --render-html)
221
+ https:// → fetched via Jina Reader (default) or fetch+Turndown (--no-jina)
222
+ folder/ → file browser opened, files converted on demand
223
+
224
+ Annotate server starts (reuses plan editor HTML with mode:"annotate")
225
+
226
+ User annotates content, provides feedback
227
+
228
+ Send Annotations → feedback sent to agent session
229
+ ```
230
+
231
+ ## Archive Flow
232
+
233
+ ```
234
+ User runs plannotator archive (CLI)
235
+
236
+ Server starts in mode:"archive", reads ~/.plannotator/plans/
237
+
238
+ Browser opens read-only archive viewer (sharing disabled)
239
+
240
+ User browses saved plan decisions with approved/denied badges
241
+
242
+ Done → POST /api/done closes the browser
243
+ ```
244
+
245
+ During normal plan review, an Archive sidebar tab provides the same browsing via linked doc overlay without leaving the current session.
246
+
247
+ ## Server API
248
+
249
+ ### Plan Server (`packages/server/index.ts`)
250
+
251
+ | Endpoint | Method | Purpose |
252
+ | --------------------- | ------ | ------------------------------------------ |
253
+ | `/api/plan` | GET | Returns `{ plan, origin, previousPlan, versionInfo }` (plan mode) or `{ plan, origin, mode: "archive", archivePlans }` (archive mode) |
254
+ | `/api/plan/version` | GET | Fetch specific version (`?v=N`) |
255
+ | `/api/plan/versions` | GET | List all versions of current plan |
256
+ | `/api/archive/plans` | GET | List archived plan decisions (`?customPath=`) |
257
+ | `/api/archive/plan` | GET | Fetch archived plan content (`?filename=&customPath=`) |
258
+ | `/api/done` | POST | Close archive browser (archive mode only) |
259
+ | `/api/approve` | POST | Approve plan (body: planSave, agentSwitch, obsidian, bear, feedback) |
260
+ | `/api/deny` | POST | Deny plan (body: feedback, planSave) |
261
+ | `/api/image` | GET | Serve image by path query param |
262
+ | `/api/upload` | POST | Upload image, returns `{ path, originalName }` |
263
+ | `/api/obsidian/vaults`| GET | Detect available Obsidian vaults |
264
+ | `/api/reference/obsidian/files` | GET | List vault markdown files as nested tree (`?vaultPath=<path>`) |
265
+ | `/api/reference/obsidian/doc` | GET | Read a vault markdown file (`?vaultPath=<path>&path=<file>`) |
266
+ | `/api/plan/vscode-diff` | POST | Open diff in VS Code (body: baseVersion) |
267
+ | `/api/doc` | GET | Serve linked .md/.mdx file (`?path=<path>`) |
268
+ | `/api/doc/exists` | POST | Batch-validate code-file paths (body: `{ paths: string[], base?: string }`) returns `{ results: { [path]: { status: "found"\|"ambiguous"\|"missing"\|"unavailable", … } } }` |
269
+ | `/api/draft` | GET/POST/DELETE | Auto-save annotation drafts to survive server crashes |
270
+ | `/api/editor-annotations` | GET | List editor annotations (VS Code only) |
271
+ | `/api/editor-annotation` | POST/DELETE | Add or remove an editor annotation (VS Code only) |
272
+ | `/api/ai/capabilities` | GET | Check if AI features are available |
273
+ | `/api/ai/session` | POST | Create or fork an AI session |
274
+ | `/api/ai/query` | POST | Send a message and stream the response (SSE) |
275
+ | `/api/ai/abort` | POST | Abort the current query |
276
+ | `/api/ai/permission` | POST | Respond to a permission request |
277
+ | `/api/ai/sessions` | GET | List active sessions |
278
+ | `/api/external-annotations/stream` | GET | SSE stream for real-time external annotations |
279
+ | `/api/external-annotations` | GET | Snapshot of external annotations (polling fallback, `?since=N` for version gating) |
280
+ | `/api/external-annotations` | POST | Add external annotations (single or batch `{ annotations: [...] }`) |
281
+ | `/api/external-annotations` | PATCH | Update fields on a single annotation (`?id=`) |
282
+ | `/api/external-annotations` | DELETE | Remove by `?id=`, `?source=`, or clear all |
283
+
284
+ ### Review Server (`packages/server/review.ts`)
285
+
286
+ | Endpoint | Method | Purpose |
287
+ | --------------------- | ------ | ------------------------------------------ |
288
+ | `/api/diff` | GET | Returns `{ rawPatch, gitRef, origin, mode?, diffType, base, hideWhitespace, gitContext, agentCwd?, semanticDiff? }`. Workspace mode returns `mode: "workspace"` with folder-prefixed paths and no `gitContext`. |
289
+ | `/api/diff/switch` | POST | Switch diff type, base branch, or whitespace mode (body: `{ diffType, base?, hideWhitespace? }`). Response includes `semanticDiff?`. |
290
+ | `/api/semantic-diff` | GET | Runs semantic diff for the active patch and returns parsed sem output or an unavailable/error response (`?fileExt=` / `?fileExts=` optional). |
291
+ | `/api/file-content` | GET | Returns `{ oldContent, newContent }` for expandable diff context (`?path=&oldPath=&base=`) |
292
+ | `/api/git-add` | POST | Stage/unstage a file (body: `{ filePath, undo? }`) |
293
+ | `/api/feedback` | POST | Submit review (body: feedback, annotations, agentSwitch) |
294
+ | `/api/image` | GET | Serve image by path query param |
295
+ | `/api/upload` | POST | Upload image, returns `{ path, originalName }` |
296
+ | `/api/draft` | GET/POST/DELETE | Auto-save annotation drafts to survive server crashes |
297
+ | `/api/editor-annotations` | GET | List editor annotations (VS Code only) |
298
+ | `/api/editor-annotation` | POST/DELETE | Add or remove an editor annotation (VS Code only) |
299
+ | `/api/ai/capabilities` | GET | Check if AI features are available |
300
+ | `/api/ai/session` | POST | Create or fork an AI session |
301
+ | `/api/ai/query` | POST | Send a message and stream the response (SSE) |
302
+ | `/api/ai/abort` | POST | Abort the current query |
303
+ | `/api/ai/permission` | POST | Respond to a permission request |
304
+ | `/api/ai/sessions` | GET | List active sessions |
305
+ | `/api/external-annotations/stream` | GET | SSE stream for real-time external annotations |
306
+ | `/api/external-annotations` | GET | Snapshot of external annotations (polling fallback, `?since=N` for version gating) |
307
+ | `/api/external-annotations` | POST | Add external annotations (single or batch `{ annotations: [...] }`) |
308
+ | `/api/external-annotations` | PATCH | Update fields on a single annotation (`?id=`) |
309
+ | `/api/external-annotations` | DELETE | Remove by `?id=`, `?source=`, or clear all |
310
+ | `/api/agents/capabilities` | GET | Check available agent providers (claude, codex, tour) |
311
+ | `/api/agents/jobs/stream` | GET | SSE stream for real-time agent job status updates |
312
+ | `/api/agents/jobs` | GET | Snapshot of agent jobs (polling fallback, `?since=N` for version gating) |
313
+ | `/api/agents/jobs` | POST | Launch an agent job (body: `{ provider, command, label }`) |
314
+ | `/api/agents/jobs` | DELETE | Kill all running agent jobs |
315
+ | `/api/agents/jobs/:id` | DELETE | Kill a specific agent job |
316
+ | `/api/pr-diff-scope` | POST | Switch between layer and full-stack diff scope. Response includes `semanticDiff?`. |
317
+ | `/api/pr-list` | GET | List PRs for the current repo (cached 30s) |
318
+ | `/api/pr-switch` | POST | Switch to a different PR in-place (body: `{ url }`). Response includes `semanticDiff?`. |
319
+ | `/api/tour/:jobId` | GET | Fetch Code Tour result (greeting, stops, checklist) for a completed tour job |
320
+ | `/api/tour/:jobId/checklist` | PUT | Persist checklist item state for a Code Tour |
321
+ | `/api/code-nav/resolve` | POST | Search for symbol definitions and references via ripgrep (body: `{ symbol, filePath, line, charStart, side, language? }`) |
322
+ | `/api/code-nav/file` | GET | Read file from working tree for code-nav preview (`?path=`) |
323
+
324
+ ### Annotate Server (`packages/server/annotate.ts`)
325
+
326
+ | Endpoint | Method | Purpose |
327
+ | --------------------- | ------ | ------------------------------------------ |
328
+ | `/api/plan` | GET | Returns `{ plan, origin, mode: "annotate", filePath, sourceInfo?, gate, renderAs?, rawHtml? }` |
329
+ | `/api/feedback` | POST | Submit annotations (body: feedback, annotations) |
330
+ | `/api/approve` | POST | Approve without feedback (review-gate UX, `--gate`) |
331
+ | `/api/exit` | POST | Close session without feedback |
332
+ | `/api/image` | GET | Serve image by path query param |
333
+ | `/api/upload` | POST | Upload image, returns `{ path, originalName }` |
334
+ | `/api/doc` | GET | Serve linked .md/.mdx/.html file or code file (`?path=<path>&base=<dir>`) |
335
+ | `/api/doc/exists` | POST | Batch-validate code-file paths (body: `{ paths: string[], base?: string }`) |
336
+ | `/api/draft` | GET/POST/DELETE | Auto-save annotation drafts to survive server crashes |
337
+ | `/api/ai/capabilities` | GET | Check if AI features are available |
338
+ | `/api/ai/session` | POST | Create or fork an AI session |
339
+ | `/api/ai/query` | POST | Send a message and stream the response (SSE) |
340
+ | `/api/ai/abort` | POST | Abort the current query |
341
+ | `/api/ai/permission` | POST | Respond to a permission request |
342
+ | `/api/ai/sessions` | GET | List active sessions |
343
+ | `/api/external-annotations/stream` | GET | SSE stream for real-time external annotations |
344
+ | `/api/external-annotations` | GET | Snapshot of external annotations (polling fallback, `?since=N` for version gating) |
345
+ | `/api/external-annotations` | POST | Add external annotations (single or batch `{ annotations: [...] }`) |
346
+ | `/api/external-annotations` | PATCH | Update fields on a single annotation (`?id=`) |
347
+ | `/api/external-annotations` | DELETE | Remove by `?id=`, `?source=`, or clear all |
348
+
349
+ All servers use random ports locally or fixed port (`19432`) in remote mode.
350
+
351
+ ### Paste Service (`apps/paste-service/`)
352
+
353
+ | Endpoint | Method | Purpose |
354
+ | --------------------- | ------ | ------------------------------------------ |
355
+ | `/api/paste` | POST | Store compressed plan data, returns `{ id }` |
356
+ | `/api/paste/:id` | GET | Retrieve stored compressed data |
357
+
358
+ Runs as a separate service on port `19433` (self-hosted) or as a Cloudflare Worker (hosted).
359
+
360
+ ## Plan Version History
361
+
362
+ Every plan is automatically saved to `~/.plannotator/history/{project}/{slug}/` on arrival, before the user sees the UI. Versions are numbered sequentially (`001.md`, `002.md`, etc.). The slug is derived from the plan's first `# Heading` + today's date via `generateSlug()`, scoped by project name (git repo or cwd). Same heading on the same day = same slug = same plan being iterated on. Identical resubmissions are deduplicated (no new file if content matches the latest version).
363
+
364
+ This powers the version history API (`/api/plan/version`, `/api/plan/versions`) and the plan diff system.
365
+
366
+ History saves independently of the `planSave` user setting (which controls decision snapshots in `~/.plannotator/plans/`). Storage functions live in `packages/shared/storage.ts` (runtime-agnostic, re-exported by `packages/server/storage.ts`). Pi copies the shared files at build time. Slug format: `{sanitized-heading}-YYYY-MM-DD` (heading first for readability).
367
+
368
+ ## Plan Diff
369
+
370
+ When a user denies a plan and Claude resubmits, the UI shows what changed between versions. A `+N/-M` badge appears below the document card; clicking it toggles between normal view and diff view.
371
+
372
+ **Diff engine** (`packages/ui/utils/planDiffEngine.ts`): Uses the `diff` npm package (`diffLines()`) to compute line-level diffs. Groups consecutive remove+add into "modified" blocks. Returns `PlanDiffBlock[]` and `PlanDiffStats`.
373
+
374
+ **Two view modes** (toggle via `PlanDiffModeSwitcher`):
375
+ - **Rendered** (`PlanCleanDiffView`): Color-coded left borders — green (added), red (removed/strikethrough), yellow (modified)
376
+ - **Raw** (`PlanRawDiffView`): Monospace `+/-` lines, git-style
377
+
378
+ **State** (`packages/ui/hooks/usePlanDiff.ts`): Manages base version selection, diff computation, and version fetching. The server sends `previousPlan` with the initial `/api/plan` response; the hook auto-diffs against it. Users can select any prior version from the sidebar Version Browser.
379
+
380
+ **Diff annotations:** The clean diff view supports block-level annotation — hover over added/removed/modified sections to annotate entire blocks. Annotations carry a `diffContext` field (`added`/`removed`/`modified`). Exported feedback includes `[In diff content]` labels.
381
+
382
+ **Annotation hook** (`packages/ui/hooks/useAnnotationHighlighter.ts`): Annotation infrastructure used by `Viewer.tsx`. Manages web-highlighter lifecycle, toolbar/popover state, annotation creation, text-based restoration, and scroll-to-selected. The diff view uses its own block-level hover system instead.
383
+
384
+ **Sidebar** (`packages/ui/hooks/useSidebar.ts`): Shared left sidebar with three tabs — Table of Contents, Version Browser, and Archive. The "Auto-open Sidebar" setting controls whether it opens on load (TOC tab only). In archive mode, the sidebar opens to the Archive tab automatically.
385
+
386
+ ## Data Types
387
+
388
+ **Location:** `packages/ui/types.ts`
389
+
390
+ ```typescript
391
+ enum AnnotationType {
392
+ DELETION = "DELETION",
393
+ COMMENT = "COMMENT",
394
+ GLOBAL_COMMENT = "GLOBAL_COMMENT",
395
+ }
396
+
397
+ interface ImageAttachment {
398
+ path: string; // temp file path
399
+ name: string; // human-readable label (e.g., "login-mockup")
400
+ }
401
+
402
+ interface Annotation {
403
+ id: string;
404
+ blockId: string;
405
+ startOffset: number;
406
+ endOffset: number;
407
+ type: AnnotationType;
408
+ text?: string; // For comment
409
+ originalText: string; // The selected text
410
+ createdA: number; // Timestamp
411
+ author?: string; // Tater identity
412
+ images?: ImageAttachment[]; // Attached images with names
413
+ source?: string; // External tool identifier (e.g., "eslint") — set when annotation comes from external API
414
+ diffContext?: 'added' | 'removed' | 'modified'; // Set when annotation created in plan diff view
415
+ startMeta?: { parentTagName; parentIndex; textOffset };
416
+ endMeta?: { parentTagName; parentIndex; textOffset };
417
+ }
418
+
419
+ interface Block {
420
+ id: string;
421
+ type: "paragraph" | "heading" | "blockquote" | "list-item" | "code" | "hr" | "table" | "html" | "directive";
422
+ content: string;
423
+ level?: number; // For headings (1-6)
424
+ language?: string; // For code blocks
425
+ alertKind?: "note" | "tip" | "warning" | "caution" | "important"; // GitHub alerts (blockquote subtype)
426
+ order: number;
427
+ startLine: number;
428
+ }
429
+ ```
430
+
431
+ ## Markdown Parser
432
+
433
+ **Location:** `packages/ui/utils/parser.ts`
434
+
435
+ `parseMarkdownToBlocks(markdown)` splits markdown into Block objects. Handles:
436
+
437
+ - Headings (`#`, `##`, etc.) with slug-derived anchor ids
438
+ - Code blocks (``` with language extraction)
439
+ - List items (`-`, `*`, `1.`)
440
+ - Blockquotes (`>`) — including GitHub alerts (`> [!NOTE|TIP|WARNING|CAUTION|IMPORTANT]`) which set `alertKind`
441
+ - Horizontal rules (`---`)
442
+ - Tables (pipe-delimited) — rendered via `TableBlock` with a `TableToolbar` (copy as markdown/CSV) and `TablePopout` overlay
443
+ - Raw HTML blocks (`<details>`, `<summary>`, etc.) — rendered via `HtmlBlock` through `marked` + DOMPurify
444
+ - Directive containers (`:::kind ... :::`) — rendered via `Callout`
445
+ - Paragraphs (default) with inline extras: bare URL autolinks, `@mentions` / `#issue-refs`, emoji shortcodes, smart punctuation
446
+
447
+ `exportAnnotations(blocks, annotations, globalAttachments)` generates human-readable feedback for Claude. Images are referenced by name: `[image-name] /tmp/path...`. Annotations with `diffContext` include `[In diff content]` labels.
448
+
449
+ ## Annotation System
450
+
451
+ **Selection mode:** User selects text → toolbar appears → choose annotation type
452
+ **Redline mode:** User selects text → auto-creates DELETION annotation
453
+
454
+ Text highlighting uses `web-highlighter` library. Code blocks use manual `<mark>` wrapping (web-highlighter can't select inside `<pre>`).
455
+
456
+ ## Keyboard Shortcuts
457
+
458
+ **Location:** `packages/ui/shortcuts/` (engine + scope data), `packages/editor/shortcuts.ts` and `packages/review-editor/shortcuts.ts` (per-app surfaces).
459
+
460
+ The shortcut system has three layers:
461
+
462
+ 1. **Engine** (`packages/ui/shortcuts/{core,runtime}.ts`) — parser for declarative bindings (`Mod+Enter`, `Alt Alt` double-tap, `Alt hold`), dispatcher, platform-aware formatter (mac glyphs vs. `Ctrl`), validator, and the `useShortcutScope` / `useDoubleTapShortcuts` React hooks. Truly shared — both apps use it as-is.
463
+ 2. **Scopes** — `defineShortcutScope({ id, title, shortcuts: { actionId: { bindings, description, section, ... } } })`. One scope per UI surface (annotation toolbar, comment popover, file tree, etc.). Lives in `packages/ui/shortcuts/{plan-review,code-review}/` — **the subfolder names which app's UI the scope serves**. Components/Apps wire handlers to a scope via `useShortcutScope({ scope, handlers: { actionId: () => ... } })`.
464
+ 3. **Surfaces** (`packages/editor/shortcuts.ts`, `packages/review-editor/shortcuts.ts`) — each app composes its scopes into a `ShortcutSurface` (`planReviewSurface`, `annotateSurface`, `codeReviewSurface`). Surfaces feed both the in-app help modal and the marketing site's auto-generated docs page.
465
+
466
+ **Convention for adding new shortcuts:** define the action in the relevant scope file under the right subfolder (`plan-review/` or `code-review/`), declare the binding(s) and description, then wire a handler at the call site with `useShortcutScope`. The marketing docs page picks it up automatically at next build. Unit tests in `packages/ui/shortcuts.test.ts` enforce normalized binding tokens (`Mod`, `Shift`, `Alt`, `A-Z`, `1-0`, named keys, `F1`–`F12`) and unique scope ids.
467
+
468
+ **Marketing docs auto-generation:** `apps/marketing/src/lib/shortcutReference.ts` reads the three surfaces and `apps/marketing/src/components/ShortcutReference.astro` renders them as tables. The `/docs/reference/keyboard-shortcuts` page is special-cased in `apps/marketing/src/pages/docs/[...slug].astro` to render the component instead of the markdown body.
469
+
470
+ ## URL Sharing
471
+
472
+ **Location:** `packages/ui/utils/sharing.ts`, `packages/ui/hooks/useSharing.ts`
473
+
474
+ Shares full plan + annotations via URL hash using deflate compression. For large plans, short URLs are created via the paste service (user must explicitly confirm).
475
+
476
+ **Payload format:**
477
+
478
+ ```typescript
479
+ // Image in shareable format: plain string (old) or [path, name] tuple (new)
480
+ type ShareableImage = string | [string, string];
481
+
482
+ interface SharePayload {
483
+ p: string; // Plan markdown
484
+ a: ShareableAnnotation[]; // Compact annotations
485
+ g?: ShareableImage[]; // Global attachments
486
+ d?: (string | null)[]; // diffContext per annotation, parallel to `a`
487
+ s?: (string | undefined)[]; // source per annotation (external tool identifier), parallel to `a`
488
+ h?: string; // Raw HTML content (--render-html mode)
489
+ r?: 'html'; // Render mode flag (omitted = markdown)
490
+ }
491
+
492
+ type ShareableAnnotation =
493
+ | ["D", string, string | null, ShareableImage[]?] // [type, original, author, images?]
494
+ | ["C", string, string, string | null, ShareableImage[]?] // [type, original, comment, author, images?]
495
+ | ["G", string, string | null, ShareableImage[]?]; // [type, comment, author, images?]
496
+ ```
497
+
498
+ **Compression pipeline:**
499
+
500
+ 1. `JSON.stringify(payload)`
501
+ 2. `CompressionStream('deflate-raw')`
502
+ 3. Base64 encode
503
+ 4. URL-safe: replace `+/=` with `-_`
504
+
505
+ **On load from shared URL:**
506
+
507
+ 1. Parse hash, decompress, restore annotations
508
+ 2. Find text positions in rendered DOM via text search
509
+ 3. Apply `<mark>` highlights
510
+ 4. Clear hash from URL (prevents re-parse on refresh)
511
+
512
+ ## Settings Persistence
513
+
514
+ **Location:** `packages/ui/utils/storage.ts`, `planSave.ts`, `agentSwitch.ts`
515
+
516
+ Uses cookies (not localStorage) because each hook invocation runs on a random port. Settings include identity, plan saving (enabled/custom path), and agent switching (OpenCode only).
517
+
518
+ ## Syntax Highlighting
519
+
520
+ Code blocks use bundled `highlight.js`. Language is extracted from fence (```rust) and applied as `language-{lang}`class. Each block highlighted individually via`hljs.highlightElement()`.
521
+
522
+ ## Requirements
523
+
524
+ - Bun runtime
525
+ - Claude Code with plugin/hooks support, or OpenCode
526
+ - Cross-platform: macOS (`open`), Linux (`xdg-open`), Windows (`start`)
527
+
528
+ ## Development
529
+
530
+ ```bash
531
+ bun install
532
+
533
+ # Run any app
534
+ bun run dev:hook # Hook server (plan review)
535
+ bun run dev:review # Review editor (code review)
536
+ bun run dev:portal # Portal editor
537
+ bun run dev:marketing # Marketing site
538
+ bun run dev:vscode # VS Code extension (watch mode)
539
+ ```
540
+
541
+ ## Build
542
+
543
+ ```bash
544
+ bun run build:hook # Single-file HTML for hook server
545
+ bun run build:review # Code review editor
546
+ bun run build:opencode # OpenCode plugin (copies HTML from hook + review)
547
+ bun run build:portal # Static build for share.plannotator.ai
548
+ bun run build:marketing # Static build for plannotator.ai
549
+ bun run build:vscode # VS Code extension bundle
550
+ bun run package:vscode # Package .vsix for marketplace
551
+ bun run build # Build hook + opencode (main targets)
552
+ ```
553
+
554
+ **Important: Tailwind `@source` paths.** When creating new directories that contain `.tsx` files with Tailwind classes, add a matching `@source` entry to the app's `index.css`. Tailwind only generates CSS for classes it finds in scanned files — missing paths means classes appear in the DOM but have no effect.
555
+
556
+ **Important: Build order matters.** The hook build (`build:hook`) copies pre-built HTML from `apps/review/dist/`. If you change UI code in `packages/ui/`, `packages/editor/`, or `packages/review-editor/`, you **must** rebuild the review app first, then the hook:
557
+
558
+ ```bash
559
+ bun run --cwd apps/review build && bun run build:hook # For review UI changes
560
+ bun run build:hook # For plan UI changes only
561
+ bun run build:hook && bun run build:opencode # For OpenCode plugin
562
+ ```
563
+
564
+ Running only `build:hook` after review-editor changes will copy stale HTML files. When testing locally with a compiled binary, the full sequence is:
565
+
566
+ ```bash
567
+ bun run --cwd apps/review build && bun run build:hook && \
568
+ bun build apps/hook/server/index.ts --compile --outfile ~/.local/bin/plannotator
569
+ ```
570
+
571
+ Running only `build:opencode` will copy stale HTML files.
572
+
573
+ ## Marketing Site
574
+
575
+ `apps/marketing/` is the plannotator.ai website — landing page, documentation, and blog. Built with Astro 5 (static output, zero client JS except a theme toggle island). Docs are markdown files in `src/content/docs/`, blog posts in `src/content/blog/`, both using Astro content collections. Tailwind CSS v4 via `@tailwindcss/vite`. Deploys to S3/CloudFront via GitHub Actions on push to main.
576
+
577
+ The `/docs/reference/keyboard-shortcuts` page is auto-generated from the shortcut registry at build time — see the Keyboard Shortcuts section above. Editing the markdown body has no effect; update the scope files instead.
578
+
579
+ ## Test plugin locally
580
+
581
+ ```
582
+ claude --plugin-dir ./apps/hook
583
+ ```