@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,105 @@
1
+ /**
2
+ * @plannotator/ai — AI provider layer for Plannotator.
3
+ *
4
+ * This package provides the backbone for AI-powered features (inline chat,
5
+ * plan Q&A, code review assistance) across all Plannotator surfaces.
6
+ *
7
+ * Architecture:
8
+ *
9
+ * ┌─────────────────┐ ┌──────────────┐
10
+ * │ Plan Review UI │────▶│ │
11
+ * ├─────────────────┤ │ AI Endpoints │──▶ SSE stream
12
+ * │ Code Review UI │────▶│ (HTTP) │
13
+ * ├─────────────────┤ │ │
14
+ * │ Annotate UI │────▶└──────┬───────┘
15
+ * └─────────────────┘ │
16
+ * ▼
17
+ * ┌────────────────┐
18
+ * │ Session Manager │
19
+ * └────────┬───────┘
20
+ * │
21
+ * ┌────────▼───────┐
22
+ * │ AIProvider │ (abstract)
23
+ * └────────┬───────┘
24
+ * │
25
+ * ┌─────────────┼──────────────┐
26
+ * ▼ ▼ ▼
27
+ * ┌──────────────┐ ┌──────────┐ ┌───────────┐
28
+ * │ Claude Agent │ │ OpenCode │ │ Future │
29
+ * │ SDK Provider │ │ Provider │ │ Providers │
30
+ * └──────────────┘ └──────────┘ └───────────┘
31
+ *
32
+ * Quick start:
33
+ *
34
+ * ```ts
35
+ * import "@plannotator/ai/providers/claude-agent-sdk";
36
+ * import { ProviderRegistry, createProvider, createAIEndpoints, SessionManager } from "@plannotator/ai";
37
+ *
38
+ * // 1. Create a registry and provider
39
+ * const registry = new ProviderRegistry();
40
+ * const provider = await createProvider({ type: "claude-agent-sdk", cwd: process.cwd() });
41
+ * registry.register(provider);
42
+ *
43
+ * // 2. Create endpoints and session manager
44
+ * const sessionManager = new SessionManager();
45
+ * const aiEndpoints = createAIEndpoints({ registry, sessionManager });
46
+ *
47
+ * // 3. Mount endpoints in your Bun server
48
+ * // aiEndpoints["/api/ai/query"](request) → SSE Response
49
+ * ```
50
+ */
51
+
52
+ // Types
53
+ export type {
54
+ AIProvider,
55
+ AIProviderCapabilities,
56
+ AIProviderConfig,
57
+ AISession,
58
+ AIMessage,
59
+ AITextMessage,
60
+ AITextDeltaMessage,
61
+ AIToolUseMessage,
62
+ AIToolResultMessage,
63
+ AIErrorMessage,
64
+ AIResultMessage,
65
+ AIPermissionRequestMessage,
66
+ AIUnknownMessage,
67
+ AIContext,
68
+ AIContextMode,
69
+ PlanContext,
70
+ CodeReviewContext,
71
+ AnnotateContext,
72
+ ParentSession,
73
+ CreateSessionOptions,
74
+ ClaudeAgentSDKConfig,
75
+ CodexSDKConfig,
76
+ PiSDKConfig,
77
+ OpenCodeConfig,
78
+ } from "./types.ts";
79
+
80
+ // Provider registry
81
+ export {
82
+ ProviderRegistry,
83
+ registerProviderFactory,
84
+ createProvider,
85
+ } from "./provider.ts";
86
+
87
+ // Context builders
88
+ export { buildSystemPrompt, buildForkPreamble, buildEffectivePrompt } from "./context.ts";
89
+
90
+ // Base session
91
+ export { BaseSession } from "./base-session.ts";
92
+
93
+ // Session manager
94
+ export { SessionManager } from "./session-manager.ts";
95
+ export type { SessionEntry, SessionManagerOptions } from "./session-manager.ts";
96
+
97
+ // HTTP endpoints
98
+ export { createAIEndpoints } from "./endpoints.ts";
99
+ export type {
100
+ AIEndpoints,
101
+ AIEndpointDeps,
102
+ CreateSessionRequest,
103
+ QueryRequest,
104
+ AbortRequest,
105
+ } from "./endpoints.ts";
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@plannotator/ai",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "description": "AI provider layer for Plannotator — pluggable agent runtime backbone",
6
+ "exports": {
7
+ ".": "./index.ts",
8
+ "./types": "./types.ts",
9
+ "./context": "./context.ts",
10
+ "./provider": "./provider.ts",
11
+ "./session-manager": "./session-manager.ts",
12
+ "./endpoints": "./endpoints.ts",
13
+ "./providers/claude-agent-sdk": "./providers/claude-agent-sdk.ts",
14
+ "./providers/codex-sdk": "./providers/codex-sdk.ts",
15
+ "./providers/command-path": "./providers/command-path.ts",
16
+ "./providers/pi-sdk": "./providers/pi-sdk.ts",
17
+ "./providers/opencode-sdk": "./providers/opencode-sdk.ts",
18
+ "./providers/pi-sdk-node": "./providers/pi-sdk-node.ts"
19
+ },
20
+ "dependencies": {}
21
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Provider registry — manages AI provider instances.
3
+ *
4
+ * Supports multiple instances of the same provider type (e.g., two Claude
5
+ * Agent SDK providers with different configs) keyed by instance ID.
6
+ *
7
+ * Each server (plan review, code review, annotate) should create its own
8
+ * ProviderRegistry or share one — no module-level global state.
9
+ */
10
+
11
+ import type { AIProvider, AIProviderConfig } from "./types.ts";
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Factory registry (global — factories are stateless type→constructor maps)
15
+ // ---------------------------------------------------------------------------
16
+
17
+ type ProviderFactory = (config: AIProviderConfig) => Promise<AIProvider>;
18
+ const factories = new Map<string, ProviderFactory>();
19
+
20
+ /** Register a factory function for a provider type. */
21
+ export function registerProviderFactory(
22
+ type: string,
23
+ factory: ProviderFactory
24
+ ): void {
25
+ factories.set(type, factory);
26
+ }
27
+
28
+ /** Create a provider from config using a registered factory. Does NOT auto-register. */
29
+ export async function createProvider(
30
+ config: AIProviderConfig
31
+ ): Promise<AIProvider> {
32
+ const factory = factories.get(config.type);
33
+ if (!factory) {
34
+ throw new Error(
35
+ `No AI provider factory registered for type "${config.type}". ` +
36
+ `Available: ${[...factories.keys()].join(", ") || "(none)"}`
37
+ );
38
+ }
39
+ return factory(config);
40
+ }
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Registry
44
+ // ---------------------------------------------------------------------------
45
+
46
+ export class ProviderRegistry {
47
+ private instances = new Map<string, AIProvider>();
48
+
49
+ /**
50
+ * Register a provider instance under an ID.
51
+ * If no instanceId is provided, uses `provider.name`.
52
+ * Returns the instanceId used.
53
+ */
54
+ register(provider: AIProvider, instanceId?: string): string {
55
+ const id = instanceId ?? provider.name;
56
+ this.instances.set(id, provider);
57
+ return id;
58
+ }
59
+
60
+ /** Get a provider by instance ID. */
61
+ get(instanceId: string): AIProvider | undefined {
62
+ return this.instances.get(instanceId);
63
+ }
64
+
65
+ /** Get the first registered provider (convenience for single-provider setups). */
66
+ getDefault(): { id: string; provider: AIProvider } | undefined {
67
+ const first = this.instances.entries().next();
68
+ if (first.done) return undefined;
69
+ return { id: first.value[0], provider: first.value[1] };
70
+ }
71
+
72
+ /** Get all instances of a given provider type (by provider.name). */
73
+ getByType(typeName: string): AIProvider[] {
74
+ return [...this.instances.values()].filter((p) => p.name === typeName);
75
+ }
76
+
77
+ /** List all instance IDs. */
78
+ list(): string[] {
79
+ return [...this.instances.keys()];
80
+ }
81
+
82
+ /** Dispose and remove a single instance. No-op if not found. */
83
+ dispose(instanceId: string): void {
84
+ const provider = this.instances.get(instanceId);
85
+ if (provider) {
86
+ provider.dispose();
87
+ this.instances.delete(instanceId);
88
+ }
89
+ }
90
+
91
+ /** Dispose all providers and clear the registry. */
92
+ disposeAll(): void {
93
+ for (const provider of this.instances.values()) {
94
+ provider.dispose();
95
+ }
96
+ this.instances.clear();
97
+ }
98
+
99
+ /** Number of registered instances. */
100
+ get size(): number {
101
+ return this.instances.size;
102
+ }
103
+ }
@@ -0,0 +1,447 @@
1
+ /**
2
+ * Claude Agent SDK provider — the first concrete AIProvider implementation.
3
+ *
4
+ * Uses @anthropic-ai/claude-agent-sdk to create sessions that can:
5
+ * - Start fresh with Plannotator context as the system prompt
6
+ * - Fork from a parent Claude Code session (preserving full history)
7
+ * - Resume a previous Plannotator inline chat session
8
+ * - Stream text deltas back to the UI in real time
9
+ *
10
+ * Sessions are read-only by default (tools limited to Read, Glob, Grep)
11
+ * to keep inline chat safe and cost-bounded.
12
+ */
13
+
14
+ import { buildSystemPrompt, buildForkPreamble, buildEffectivePrompt } from "../context.ts";
15
+ import { BaseSession } from "../base-session.ts";
16
+ import type {
17
+ AIProvider,
18
+ AIProviderCapabilities,
19
+ AISession,
20
+ AIMessage,
21
+ CreateSessionOptions,
22
+ ClaudeAgentSDKConfig,
23
+ } from "../types.ts";
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Constants
27
+ // ---------------------------------------------------------------------------
28
+
29
+ const PROVIDER_NAME = "claude-agent-sdk";
30
+
31
+ /** Default read-only tools for inline chat. */
32
+ const DEFAULT_ALLOWED_TOOLS = ["Read", "Glob", "Grep", "WebSearch"];
33
+
34
+ const DEFAULT_MAX_TURNS = 99;
35
+ const DEFAULT_MODEL = "claude-sonnet-4-6";
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // SDK query options — typed to catch typos at compile time
39
+ // ---------------------------------------------------------------------------
40
+
41
+ interface ClaudeSDKQueryOptions {
42
+ model: string;
43
+ maxTurns: number;
44
+ allowedTools: string[];
45
+ cwd: string;
46
+ abortController: AbortController;
47
+ includePartialMessages: boolean;
48
+ persistSession: boolean;
49
+ maxBudgetUsd?: number;
50
+ systemPrompt?: string | { type: "preset"; preset: string; append?: string };
51
+ resume?: string;
52
+ forkSession?: boolean;
53
+ permissionMode?: ClaudeAgentSDKConfig['permissionMode'];
54
+ allowDangerouslySkipPermissions?: boolean;
55
+ pathToClaudeCodeExecutable?: string;
56
+ settingSources?: string[];
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Provider
61
+ // ---------------------------------------------------------------------------
62
+
63
+ export class ClaudeAgentSDKProvider implements AIProvider {
64
+ readonly name = PROVIDER_NAME;
65
+ readonly capabilities: AIProviderCapabilities = {
66
+ fork: true,
67
+ resume: true,
68
+ streaming: true,
69
+ tools: true,
70
+ };
71
+ readonly models = [
72
+ { id: 'claude-fable-5', label: 'Fable 5' },
73
+ { id: 'claude-opus-4-8', label: 'Opus 4.8' },
74
+ { id: 'claude-opus-4-8[1m]', label: 'Opus 4.8 (1M)' },
75
+ { id: 'claude-sonnet-4-6', label: 'Sonnet 4.6', default: true },
76
+ { id: 'claude-sonnet-4-6[1m]', label: 'Sonnet 4.6 (1M)' },
77
+ { id: 'claude-opus-4-7', label: 'Opus 4.7' },
78
+ { id: 'claude-opus-4-7[1m]', label: 'Opus 4.7 (1M)' },
79
+ { id: 'claude-opus-4-6', label: 'Opus 4.6' },
80
+ { id: 'claude-opus-4-6[1m]', label: 'Opus 4.6 (1M)' },
81
+ { id: 'claude-haiku-4-5', label: 'Haiku 4.5' },
82
+ ] as const;
83
+
84
+ private config: ClaudeAgentSDKConfig;
85
+
86
+ constructor(config: ClaudeAgentSDKConfig) {
87
+ this.config = config;
88
+ }
89
+
90
+ async createSession(options: CreateSessionOptions): Promise<AISession> {
91
+ return new ClaudeAgentSDKSession({
92
+ ...this.baseConfig(options),
93
+ systemPrompt: buildSystemPrompt(options.context),
94
+ cwd: options.cwd ?? this.config.cwd ?? process.cwd(),
95
+ parentSessionId: null,
96
+ forkFromSession: null,
97
+ });
98
+ }
99
+
100
+ async forkSession(options: CreateSessionOptions): Promise<AISession> {
101
+ const parent = options.context.parent;
102
+ if (!parent) {
103
+ throw new Error(
104
+ "Cannot fork: no parent session provided in context. " +
105
+ "Use createSession() for standalone sessions."
106
+ );
107
+ }
108
+
109
+ return new ClaudeAgentSDKSession({
110
+ ...this.baseConfig(options),
111
+ systemPrompt: null,
112
+ forkPreamble: buildForkPreamble(options.context),
113
+ cwd: parent.cwd,
114
+ parentSessionId: parent.sessionId,
115
+ forkFromSession: parent.sessionId,
116
+ });
117
+ }
118
+
119
+ async resumeSession(sessionId: string): Promise<AISession> {
120
+ return new ClaudeAgentSDKSession({
121
+ ...this.baseConfig(),
122
+ systemPrompt: null,
123
+ cwd: this.config.cwd ?? process.cwd(),
124
+ parentSessionId: null,
125
+ forkFromSession: null,
126
+ resumeSessionId: sessionId,
127
+ });
128
+ }
129
+
130
+ dispose(): void {
131
+ // No persistent resources to clean up
132
+ }
133
+
134
+ private baseConfig(options?: CreateSessionOptions) {
135
+ return {
136
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
137
+ maxTurns: options?.maxTurns ?? DEFAULT_MAX_TURNS,
138
+ maxBudgetUsd: options?.maxBudgetUsd,
139
+ allowedTools: this.config.allowedTools ?? DEFAULT_ALLOWED_TOOLS,
140
+ permissionMode: this.config.permissionMode ?? "default",
141
+ claudeExecutablePath: this.config.claudeExecutablePath,
142
+ settingSources: this.config.settingSources ?? ['user', 'project'],
143
+ };
144
+ }
145
+ }
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // SDK import cache — resolve once, reuse across all queries
149
+ // ---------------------------------------------------------------------------
150
+
151
+ // biome-ignore lint/suspicious/noExplicitAny: SDK types resolved at runtime via dynamic import
152
+ let sdkQueryFn: ((...args: any[]) => any) | null = null;
153
+
154
+ async function getSDKQuery() {
155
+ if (!sdkQueryFn) {
156
+ const sdk = await import("@anthropic-ai/claude-agent-sdk");
157
+ sdkQueryFn = sdk.query;
158
+ }
159
+ return sdkQueryFn!;
160
+ }
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // Session
164
+ // ---------------------------------------------------------------------------
165
+
166
+ interface SessionConfig {
167
+ systemPrompt: string | null;
168
+ forkPreamble?: string;
169
+ model: string;
170
+ maxTurns: number;
171
+ maxBudgetUsd?: number;
172
+ allowedTools: string[];
173
+ permissionMode: ClaudeAgentSDKConfig['permissionMode'];
174
+ cwd: string;
175
+ parentSessionId: string | null;
176
+ forkFromSession: string | null;
177
+ resumeSessionId?: string;
178
+ claudeExecutablePath?: string;
179
+ settingSources?: string[];
180
+ }
181
+
182
+ class ClaudeAgentSDKSession extends BaseSession {
183
+ private config: SessionConfig;
184
+ /** Active Query object — needed to send control responses (permission decisions) */
185
+ private _activeQuery: { streamInput: (iter: AsyncIterable<unknown>) => Promise<void> } | null = null;
186
+
187
+ constructor(config: SessionConfig) {
188
+ super({
189
+ parentSessionId: config.parentSessionId,
190
+ initialId: config.resumeSessionId,
191
+ });
192
+ this.config = config;
193
+ }
194
+
195
+ async *query(prompt: string): AsyncIterable<AIMessage> {
196
+ const started = this.startQuery();
197
+ if (!started) { yield BaseSession.BUSY_ERROR; return; }
198
+ const { gen } = started;
199
+
200
+ try {
201
+ const queryFn = await getSDKQuery();
202
+
203
+ const queryPrompt = buildEffectivePrompt(
204
+ prompt,
205
+ this.config.forkPreamble ?? null,
206
+ this._firstQuerySent,
207
+ );
208
+ const options = this.buildQueryOptions();
209
+
210
+ const stream = queryFn({ prompt: queryPrompt, options }) as
211
+ AsyncIterable<Record<string, unknown>> & { streamInput: (iter: AsyncIterable<unknown>) => Promise<void> };
212
+ this._activeQuery = stream;
213
+
214
+ this._firstQuerySent = true;
215
+
216
+ for await (const message of stream) {
217
+ const mapped = mapSDKMessage(message);
218
+
219
+ // Capture the real session ID from the init message
220
+ if (
221
+ !this._resolvedId &&
222
+ "session_id" in message &&
223
+ typeof message.session_id === "string" &&
224
+ message.session_id
225
+ ) {
226
+ this.resolveId(message.session_id);
227
+ }
228
+
229
+ for (const msg of mapped) {
230
+ yield msg;
231
+ }
232
+ }
233
+ } catch (err) {
234
+ yield {
235
+ type: "error",
236
+ error: err instanceof Error ? err.message : String(err),
237
+ code: "provider_error",
238
+ };
239
+ } finally {
240
+ this.endQuery(gen);
241
+ this._activeQuery = null;
242
+ }
243
+ }
244
+
245
+ abort(): void {
246
+ this._activeQuery = null;
247
+ super.abort();
248
+ }
249
+
250
+ respondToPermission(requestId: string, allow: boolean, message?: string): void {
251
+ if (!this._activeQuery || !this._activeQuery.streamInput) return;
252
+
253
+ const response = allow
254
+ ? { type: 'control_response', response: { subtype: 'success', request_id: requestId, response: { behavior: 'allow' } } }
255
+ : { type: 'control_response', response: { subtype: 'success', request_id: requestId, response: { behavior: 'deny', message: message ?? 'User denied this action' } } };
256
+
257
+ this._activeQuery.streamInput(
258
+ (async function* () { yield response; })()
259
+ ).catch(() => {});
260
+ }
261
+
262
+ // -------------------------------------------------------------------------
263
+ // Internal
264
+ // -------------------------------------------------------------------------
265
+
266
+ private buildQueryOptions(): ClaudeSDKQueryOptions {
267
+ const opts: ClaudeSDKQueryOptions = {
268
+ model: this.config.model,
269
+ maxTurns: this.config.maxTurns,
270
+ allowedTools: this.config.allowedTools,
271
+ cwd: this.config.cwd,
272
+ abortController: this._currentAbort!,
273
+ includePartialMessages: true,
274
+ persistSession: true,
275
+ ...(this.config.claudeExecutablePath && {
276
+ pathToClaudeCodeExecutable: this.config.claudeExecutablePath,
277
+ }),
278
+ ...(this.config.settingSources && {
279
+ settingSources: this.config.settingSources,
280
+ }),
281
+ };
282
+
283
+ if (this.config.maxBudgetUsd) {
284
+ opts.maxBudgetUsd = this.config.maxBudgetUsd;
285
+ }
286
+
287
+ // After the first query resolves a real session ID, all subsequent
288
+ // queries must resume that session to continue the conversation.
289
+ if (this._resolvedId) {
290
+ opts.resume = this._resolvedId;
291
+ return this.applyPermissionMode(opts);
292
+ }
293
+
294
+ // First query: use Claude Code's built-in prompt with our context appended
295
+ if (this.config.systemPrompt) {
296
+ opts.systemPrompt = {
297
+ type: "preset",
298
+ preset: "claude_code",
299
+ append: this.config.systemPrompt,
300
+ };
301
+ }
302
+
303
+ if (this.config.forkFromSession) {
304
+ opts.resume = this.config.forkFromSession;
305
+ opts.forkSession = true;
306
+ }
307
+
308
+ if (this.config.resumeSessionId) {
309
+ opts.resume = this.config.resumeSessionId;
310
+ }
311
+
312
+ return this.applyPermissionMode(opts);
313
+ }
314
+
315
+ private applyPermissionMode(opts: ClaudeSDKQueryOptions): ClaudeSDKQueryOptions {
316
+ if (this.config.permissionMode === "bypassPermissions") {
317
+ opts.permissionMode = "bypassPermissions";
318
+ opts.allowDangerouslySkipPermissions = true;
319
+ } else if (this.config.permissionMode === "plan") {
320
+ opts.permissionMode = "plan";
321
+ }
322
+ return opts;
323
+ }
324
+ }
325
+
326
+ // ---------------------------------------------------------------------------
327
+ // Message mapping
328
+ // ---------------------------------------------------------------------------
329
+
330
+ /**
331
+ * Map an SDK message to one or more AIMessages.
332
+ *
333
+ * An SDK assistant message can contain both text and tool_use content blocks
334
+ * in a single response. We emit each block as a separate AIMessage so no
335
+ * content is dropped.
336
+ */
337
+ function mapSDKMessage(msg: Record<string, unknown>): AIMessage[] {
338
+ const type = msg.type as string;
339
+
340
+ switch (type) {
341
+ case "assistant": {
342
+ const message = msg.message as Record<string, unknown> | undefined;
343
+ if (!message) return [{ type: "unknown", raw: msg }];
344
+ const content = message.content as Array<Record<string, unknown>>;
345
+ if (!content) return [{ type: "unknown", raw: msg }];
346
+
347
+ const messages: AIMessage[] = [];
348
+ const textParts: string[] = [];
349
+
350
+ for (const block of content) {
351
+ if (block.type === "text" && typeof block.text === "string") {
352
+ textParts.push(block.text);
353
+ } else if (block.type === "tool_use") {
354
+ // Flush accumulated text before the tool_use block
355
+ if (textParts.length > 0) {
356
+ messages.push({ type: "text", text: textParts.join("") });
357
+ textParts.length = 0;
358
+ }
359
+ messages.push({
360
+ type: "tool_use",
361
+ toolName: block.name as string,
362
+ toolInput: block.input as Record<string, unknown>,
363
+ toolUseId: block.id as string,
364
+ });
365
+ }
366
+ }
367
+
368
+ // Flush any remaining text after the last block
369
+ if (textParts.length > 0) {
370
+ messages.push({ type: "text", text: textParts.join("") });
371
+ }
372
+
373
+ return messages.length > 0 ? messages : [{ type: "unknown", raw: msg }];
374
+ }
375
+
376
+ case "stream_event": {
377
+ const event = msg.event as Record<string, unknown> | undefined;
378
+ if (!event) return [{ type: "unknown", raw: msg }];
379
+ const eventType = event.type as string;
380
+
381
+ if (eventType === "content_block_delta") {
382
+ const delta = event.delta as Record<string, unknown>;
383
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
384
+ return [{ type: "text_delta", delta: delta.text }];
385
+ }
386
+ }
387
+ return [{ type: "unknown", raw: msg }];
388
+ }
389
+
390
+ case "user": {
391
+ // SDK wraps tool results in SDKUserMessage (type: "user")
392
+ if (msg.tool_use_result != null) {
393
+ return [{
394
+ type: "tool_result",
395
+ result: typeof msg.tool_use_result === "string"
396
+ ? msg.tool_use_result
397
+ : JSON.stringify(msg.tool_use_result),
398
+ }];
399
+ }
400
+ return [{ type: "unknown", raw: msg }];
401
+ }
402
+
403
+ case "control_request": {
404
+ const request = msg.request as Record<string, unknown> | undefined;
405
+ if (request?.subtype === "can_use_tool") {
406
+ return [{
407
+ type: "permission_request",
408
+ requestId: msg.request_id as string,
409
+ toolName: request.tool_name as string,
410
+ toolInput: (request.input as Record<string, unknown>) ?? {},
411
+ title: request.title as string | undefined,
412
+ displayName: request.display_name as string | undefined,
413
+ description: request.description as string | undefined,
414
+ toolUseId: request.tool_use_id as string,
415
+ }];
416
+ }
417
+ return [{ type: "unknown", raw: msg }];
418
+ }
419
+
420
+ case "result": {
421
+ const sessionId = (msg.session_id as string) ?? "";
422
+ const subtype = msg.subtype as string;
423
+ return [{
424
+ type: "result",
425
+ sessionId,
426
+ success: subtype === "success",
427
+ result: (msg.result as string) ?? undefined,
428
+ costUsd: msg.total_cost_usd as number | undefined,
429
+ turns: msg.num_turns as number | undefined,
430
+ }];
431
+ }
432
+
433
+ default:
434
+ return [{ type: "unknown", raw: msg }];
435
+ }
436
+ }
437
+
438
+ // ---------------------------------------------------------------------------
439
+ // Factory registration
440
+ // ---------------------------------------------------------------------------
441
+
442
+ import { registerProviderFactory } from "../provider.ts";
443
+
444
+ registerProviderFactory(
445
+ PROVIDER_NAME,
446
+ async (config) => new ClaudeAgentSDKProvider(config as ClaudeAgentSDKConfig)
447
+ );