@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,80 @@
1
+ import {
2
+ type DiffType,
3
+ type GitDiffOptions,
4
+ type VcsProvider,
5
+ createGitProvider,
6
+ createJjProvider,
7
+ createVcsApi,
8
+ resolveInitialDiffType,
9
+ } from "@plannotator/shared/vcs-core";
10
+ import {
11
+ detectP4Workspace,
12
+ getP4Context,
13
+ getP4FileContentsForDiff,
14
+ runP4Diff,
15
+ } from "./p4";
16
+ import { runtime as gitRuntime } from "./git";
17
+ import { runtime as jjRuntime } from "./jj";
18
+
19
+ const p4Provider: VcsProvider = {
20
+ id: "p4",
21
+
22
+ async detect(cwd?: string): Promise<boolean> {
23
+ return (await detectP4Workspace(cwd)) !== null;
24
+ },
25
+
26
+ ownsDiffType(diffType: string): boolean {
27
+ return diffType === "p4-default" || diffType.startsWith("p4-changelist:");
28
+ },
29
+
30
+ getContext: getP4Context,
31
+
32
+ runDiff(diffType: DiffType, _defaultBranch: string, cwd?: string, _options?: GitDiffOptions) {
33
+ return runP4Diff(diffType, cwd);
34
+ },
35
+
36
+ getFileContents(diffType, _defaultBranch, filePath, _oldPath?, cwd?) {
37
+ return getP4FileContentsForDiff(diffType, filePath, cwd);
38
+ },
39
+ };
40
+
41
+ const api = createVcsApi([
42
+ createJjProvider(jjRuntime),
43
+ createGitProvider(gitRuntime),
44
+ p4Provider,
45
+ ]);
46
+
47
+ export const {
48
+ detectVcs,
49
+ detectManagedVcs,
50
+ getVcsContext,
51
+ detectRemoteDefaultCompareTarget,
52
+ prepareLocalReviewDiff,
53
+ runVcsDiff,
54
+ getVcsFileContentsForDiff,
55
+ canStageFiles,
56
+ stageFile,
57
+ unstageFile,
58
+ resolveVcsCwd,
59
+ } = api;
60
+
61
+ export { resolveInitialDiffType, gitRuntime };
62
+
63
+ export type {
64
+ DiffOption,
65
+ DiffType,
66
+ GitContext,
67
+ GitDiffOptions,
68
+ VcsProvider,
69
+ VcsSelection,
70
+ WorktreeInfo,
71
+ } from "@plannotator/shared/vcs-core";
72
+
73
+ export {
74
+ JJ_TRUNK_REVSET,
75
+ jjCompareTargetRevset,
76
+ jjLineBaseRevset,
77
+ parseRemoteBookmark,
78
+ parseWorktreeDiffType,
79
+ validateFilePath,
80
+ } from "@plannotator/shared/vcs-core";
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Agent Jobs — shared types, state machine, and SSE helpers.
3
+ *
4
+ * Runtime-agnostic: no node:fs, no node:http, no Bun APIs.
5
+ * Both the Bun server handler and (future) Node handler import
6
+ * this module and wrap it with their respective HTTP transport layers.
7
+ *
8
+ * Mirrors packages/shared/external-annotation.ts in structure.
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Types
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export type AgentJobStatus = "starting" | "running" | "done" | "failed" | "killed";
16
+
17
+ /**
18
+ * Snapshot of the diff the reviewer was looking at when this job was launched.
19
+ * Carried on the job so downstream UIs (agent-result panel "Copy All") export
20
+ * the same `**Diff:** ...` header the job was actually run against — if the
21
+ * reviewer switches the UI to a different diff afterwards, the job's snapshot
22
+ * still reflects truth. Structurally compatible with the UI-side
23
+ * `FeedbackDiffContext` in `packages/review-editor/utils/exportFeedback.ts`.
24
+ */
25
+ export interface AgentJobDiffContext {
26
+ mode: string;
27
+ base?: string;
28
+ worktreePath?: string | null;
29
+ }
30
+
31
+ export interface AgentJobInfo {
32
+ /** Unique job identifier (UUID). */
33
+ id: string;
34
+ /** Source identifier for external annotations — "agent-{id prefix}". */
35
+ source: string;
36
+ /** Provider that spawned this job — "claude", "codex", "tour", "shell", etc. */
37
+ provider: string;
38
+ /** Underlying engine used (e.g., "claude" or "codex"). Set when provider is "tour". */
39
+ engine?: string;
40
+ /** Model used (e.g., "sonnet", "opus"). Set when provider is "tour" with Claude engine. */
41
+ model?: string;
42
+ /** Claude --effort level (e.g., "low", "medium", "high", "xhigh", "max"). */
43
+ effort?: string;
44
+ /** Codex reasoning effort level (e.g., "high", "medium"). */
45
+ reasoningEffort?: string;
46
+ /** Whether Codex fast mode (service_tier=fast) was enabled. */
47
+ fastMode?: boolean;
48
+ /** Human-readable label for the job. */
49
+ label: string;
50
+ /** Current lifecycle status. */
51
+ status: AgentJobStatus;
52
+ /** Timestamp when the job was created. */
53
+ startedAt: number;
54
+ /** Timestamp when the job reached a terminal state. */
55
+ endedAt?: number;
56
+ /** Process exit code (set on done/failed). */
57
+ exitCode?: number;
58
+ /** Last ~500 chars of stderr on failure. */
59
+ error?: string;
60
+ /** The actual command that was spawned (for display/debug). */
61
+ command: string[];
62
+ /** Working directory where the process was spawned. */
63
+ cwd?: string;
64
+ /** The review prompt text (system + user message). Stored separately from command for providers that use stdin. */
65
+ prompt?: string;
66
+ /** Review summary set by the agent on completion. */
67
+ summary?: {
68
+ correctness: string;
69
+ explanation: string;
70
+ confidence: number;
71
+ };
72
+ /** PR URL at launch time — used to attribute findings to the correct PR. */
73
+ prUrl?: string;
74
+ /** PR diff scope at launch time — "layer" or "full-stack". */
75
+ diffScope?: string;
76
+ /** Diff context at launch time (see AgentJobDiffContext). */
77
+ diffContext?: AgentJobDiffContext;
78
+ }
79
+
80
+ export interface AgentCapability {
81
+ id: string;
82
+ name: string;
83
+ available: boolean;
84
+ }
85
+
86
+ export interface AgentCapabilities {
87
+ mode: "plan" | "review" | "annotate";
88
+ providers: AgentCapability[];
89
+ /** True if at least one provider is available. */
90
+ available: boolean;
91
+ }
92
+
93
+ // ---------------------------------------------------------------------------
94
+ // SSE event types
95
+ // ---------------------------------------------------------------------------
96
+
97
+ export type AgentJobEvent =
98
+ | { type: "snapshot"; jobs: AgentJobInfo[] }
99
+ | { type: "job:started"; job: AgentJobInfo }
100
+ | { type: "job:updated"; job: AgentJobInfo }
101
+ | { type: "job:completed"; job: AgentJobInfo }
102
+ | { type: "job:log"; jobId: string; delta: string }
103
+ | { type: "jobs:cleared" };
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // SSE helpers
107
+ // ---------------------------------------------------------------------------
108
+
109
+ /** Heartbeat comment to keep SSE connections alive (sent every 30s). */
110
+ export const AGENT_HEARTBEAT_COMMENT = ":\n\n";
111
+
112
+ /** Interval in ms between heartbeat comments. */
113
+ export const AGENT_HEARTBEAT_INTERVAL_MS = 30_000;
114
+
115
+ /** Encode an event as an SSE `data:` line. */
116
+ export function serializeAgentSSEEvent(event: AgentJobEvent): string {
117
+ return `data: ${JSON.stringify(event)}\n\n`;
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Helpers
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /** Check if a status is terminal (no further transitions). */
125
+ export function isTerminalStatus(status: AgentJobStatus): boolean {
126
+ return status === "done" || status === "failed" || status === "killed";
127
+ }
128
+
129
+ /** Generate the source identifier for a job from its ID. */
130
+ export function jobSource(id: string): string {
131
+ return "agent-" + id.slice(0, 8);
132
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Centralized agent configuration — single source of truth for all supported agents.
3
+ *
4
+ * To add a new agent:
5
+ * 1. Add an entry to AGENT_CONFIG below (origin key, display name, badge CSS classes,
6
+ * optional AI provider types)
7
+ * 2. If detection is via environment variable, add it to the detection chain
8
+ * in apps/hook/server/index.ts (detectedOrigin constant)
9
+ * 3. That's it — all UI components read from this config automatically
10
+ */
11
+
12
+ type AgentConfigEntry = {
13
+ name: string;
14
+ badge: string;
15
+ /** AI provider type(s) that naturally match this origin, in preference order. */
16
+ aiProviderTypes?: readonly string[];
17
+ };
18
+
19
+ export const AGENT_CONFIG = {
20
+ 'claude-code': { name: 'Claude Code', badge: 'bg-orange-500/15 text-orange-400', aiProviderTypes: ['claude-agent-sdk'] },
21
+ 'amp': { name: 'Amp', badge: 'bg-lime-500/15 text-lime-400' },
22
+ 'droid': { name: 'Droid', badge: 'bg-cyan-500/15 text-cyan-400' },
23
+ 'kiro-cli': { name: 'Kiro CLI', badge: 'bg-amber-500/15 text-amber-400' },
24
+ 'opencode': { name: 'OpenCode', badge: 'bg-emerald-500/15 text-emerald-400', aiProviderTypes: ['opencode-sdk'] },
25
+ 'copilot-cli': { name: 'GitHub Copilot', badge: 'bg-blue-500/15 text-blue-400' },
26
+ 'pi': { name: 'Pi', badge: 'bg-violet-500/15 text-violet-400', aiProviderTypes: ['pi-sdk'] },
27
+ 'codex': { name: 'Codex', badge: 'bg-purple-500/15 text-purple-400', aiProviderTypes: ['codex-sdk'] },
28
+ 'gemini-cli': { name: 'Gemini CLI', badge: 'bg-sky-500/15 text-sky-400' },
29
+ } as const satisfies Record<string, AgentConfigEntry>;
30
+
31
+ /** All recognized origin values. */
32
+ export type Origin = keyof typeof AGENT_CONFIG;
33
+
34
+ /** Resolve an origin to a human-readable agent name. */
35
+ export function getAgentName(origin: Origin | null | undefined): string {
36
+ if (origin && origin in AGENT_CONFIG) return AGENT_CONFIG[origin as Origin].name;
37
+ return 'Coding Agent';
38
+ }
39
+
40
+ /** Resolve an origin to Tailwind badge classes. */
41
+ export function getAgentBadge(origin: Origin | null | undefined): string {
42
+ if (origin && origin in AGENT_CONFIG) return AGENT_CONFIG[origin as Origin].badge;
43
+ return 'bg-zinc-500/20 text-zinc-400';
44
+ }
45
+
46
+ /** Resolve an origin to matching AI provider types, in preference order. */
47
+ export function getAgentAIProviderTypes(origin: Origin | null | undefined): readonly string[] {
48
+ if (origin && origin in AGENT_CONFIG) {
49
+ const config = AGENT_CONFIG[origin as Origin];
50
+ return 'aiProviderTypes' in config ? config.aiProviderTypes : [];
51
+ }
52
+ return [];
53
+ }
@@ -0,0 +1,386 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { parseAnnotateArgs } from "./annotate-args";
3
+
4
+ describe("parseAnnotateArgs", () => {
5
+ test("path only", () => {
6
+ expect(parseAnnotateArgs("spec.md")).toEqual({
7
+ filePath: "spec.md",
8
+ rawFilePath: "spec.md",
9
+ gate: false,
10
+ json: false,
11
+ hook: false,
12
+ renderHtml: false,
13
+ noJina: false,
14
+ });
15
+ });
16
+
17
+ test("path with --gate at end", () => {
18
+ expect(parseAnnotateArgs("spec.md --gate")).toEqual({
19
+ filePath: "spec.md",
20
+ rawFilePath: "spec.md",
21
+ gate: true,
22
+ json: false,
23
+ hook: false,
24
+ renderHtml: false,
25
+ noJina: false,
26
+ });
27
+ });
28
+
29
+ test("--gate before path", () => {
30
+ expect(parseAnnotateArgs("--gate spec.md")).toEqual({
31
+ filePath: "spec.md",
32
+ rawFilePath: "spec.md",
33
+ gate: true,
34
+ json: false,
35
+ hook: false,
36
+ renderHtml: false,
37
+ noJina: false,
38
+ });
39
+ });
40
+
41
+ test("path with both flags", () => {
42
+ expect(parseAnnotateArgs("spec.md --gate --json")).toEqual({
43
+ filePath: "spec.md",
44
+ rawFilePath: "spec.md",
45
+ gate: true,
46
+ json: true,
47
+ hook: false,
48
+ renderHtml: false,
49
+ noJina: false,
50
+ });
51
+ });
52
+
53
+ test("flags only, no path", () => {
54
+ expect(parseAnnotateArgs("--gate --json")).toEqual({
55
+ filePath: "",
56
+ rawFilePath: "",
57
+ gate: true,
58
+ json: true,
59
+ hook: false,
60
+ renderHtml: false,
61
+ noJina: false,
62
+ });
63
+ });
64
+
65
+ test("path with spaces rejoins with single space", () => {
66
+ expect(parseAnnotateArgs("my file.md --gate")).toEqual({
67
+ filePath: "my file.md",
68
+ rawFilePath: "my file.md",
69
+ gate: true,
70
+ json: false,
71
+ hook: false,
72
+ renderHtml: false,
73
+ noJina: false,
74
+ });
75
+ });
76
+
77
+ // `@` is the reference-mode marker (Claude Code / OpenCode / Pi convention),
78
+ // not part of the filename. The parser strips it on `filePath` as the primary
79
+ // behavior — that's the common case. `rawFilePath` preserves the original
80
+ // for callers that want to try the literal form as a fallback (scoped-package-
81
+ // style names). See at-reference.ts for the combined helper.
82
+
83
+ test("leading @ is stripped (reference-mode primary) and rawFilePath preserves it", () => {
84
+ expect(parseAnnotateArgs("@spec.md --gate")).toEqual({
85
+ filePath: "spec.md",
86
+ rawFilePath: "@spec.md",
87
+ gate: true,
88
+ json: false,
89
+ hook: false,
90
+ renderHtml: false,
91
+ noJina: false,
92
+ });
93
+ });
94
+
95
+ test("scoped-package-style path: filePath stripped, rawFilePath literal", () => {
96
+ expect(parseAnnotateArgs("@plannotator/ui/README.md")).toEqual({
97
+ filePath: "plannotator/ui/README.md",
98
+ rawFilePath: "@plannotator/ui/README.md",
99
+ gate: false,
100
+ json: false,
101
+ hook: false,
102
+ renderHtml: false,
103
+ noJina: false,
104
+ });
105
+ });
106
+
107
+ test("@ stripped on filePath when combined with --gate --json, raw preserved", () => {
108
+ expect(parseAnnotateArgs("@docs/spec.md --gate --json")).toEqual({
109
+ filePath: "docs/spec.md",
110
+ rawFilePath: "@docs/spec.md",
111
+ gate: true,
112
+ json: true,
113
+ hook: false,
114
+ renderHtml: false,
115
+ noJina: false,
116
+ });
117
+ });
118
+
119
+ test("URL passes through", () => {
120
+ expect(parseAnnotateArgs("https://example.com/docs --gate")).toEqual({
121
+ filePath: "https://example.com/docs",
122
+ rawFilePath: "https://example.com/docs",
123
+ gate: true,
124
+ json: false,
125
+ hook: false,
126
+ renderHtml: false,
127
+ noJina: false,
128
+ });
129
+ });
130
+
131
+ test("--no-jina is stripped from URL args", () => {
132
+ expect(parseAnnotateArgs("https://example.com/docs --no-jina --gate")).toEqual({
133
+ filePath: "https://example.com/docs",
134
+ rawFilePath: "https://example.com/docs",
135
+ gate: true,
136
+ json: false,
137
+ hook: false,
138
+ renderHtml: false,
139
+ noJina: true,
140
+ });
141
+ });
142
+
143
+ test("--no-jina before path is stripped", () => {
144
+ expect(parseAnnotateArgs("--no-jina https://example.com/docs")).toEqual({
145
+ filePath: "https://example.com/docs",
146
+ rawFilePath: "https://example.com/docs",
147
+ gate: false,
148
+ json: false,
149
+ hook: false,
150
+ renderHtml: false,
151
+ noJina: true,
152
+ });
153
+ });
154
+
155
+ test("extra whitespace is collapsed", () => {
156
+ expect(parseAnnotateArgs(" spec.md --gate ")).toEqual({
157
+ filePath: "spec.md",
158
+ rawFilePath: "spec.md",
159
+ gate: true,
160
+ json: false,
161
+ hook: false,
162
+ renderHtml: false,
163
+ noJina: false,
164
+ });
165
+ });
166
+
167
+ test("empty string produces empty result", () => {
168
+ expect(parseAnnotateArgs("")).toEqual({
169
+ filePath: "",
170
+ rawFilePath: "",
171
+ gate: false,
172
+ json: false,
173
+ hook: false,
174
+ renderHtml: false,
175
+ noJina: false,
176
+ });
177
+ });
178
+
179
+ test("nullish input is tolerated", () => {
180
+ expect(parseAnnotateArgs(undefined as unknown as string)).toEqual({
181
+ filePath: "",
182
+ rawFilePath: "",
183
+ gate: false,
184
+ json: false,
185
+ hook: false,
186
+ renderHtml: false,
187
+ noJina: false,
188
+ });
189
+ });
190
+
191
+ test("folder path with trailing slash", () => {
192
+ expect(parseAnnotateArgs("./specs/ --gate --json")).toEqual({
193
+ filePath: "./specs/",
194
+ rawFilePath: "./specs/",
195
+ gate: true,
196
+ json: true,
197
+ hook: false,
198
+ renderHtml: false,
199
+ noJina: false,
200
+ });
201
+ });
202
+
203
+ // Regressions from the initial parser: the tokenize-and-rejoin approach
204
+ // collapsed consecutive whitespace in file paths. Before this branch,
205
+ // OpenCode and Pi passed the raw args string straight through, so files
206
+ // with double-spaces or tabs in their names worked fine. These tests pin
207
+ // that behavior so we don't regress it again.
208
+
209
+ test("double-space inside a file path is preserved (flag at end)", () => {
210
+ expect(parseAnnotateArgs("My Notes.md --gate")).toEqual({
211
+ filePath: "My Notes.md",
212
+ rawFilePath: "My Notes.md",
213
+ gate: true,
214
+ json: false,
215
+ hook: false,
216
+ renderHtml: false,
217
+ noJina: false,
218
+ });
219
+ });
220
+
221
+ test("double-space inside a file path is preserved (flag at start)", () => {
222
+ expect(parseAnnotateArgs("--gate My Notes.md")).toEqual({
223
+ filePath: "My Notes.md",
224
+ rawFilePath: "My Notes.md",
225
+ gate: true,
226
+ json: false,
227
+ hook: false,
228
+ renderHtml: false,
229
+ noJina: false,
230
+ });
231
+ });
232
+
233
+ test("tab inside a file path is preserved", () => {
234
+ expect(parseAnnotateArgs("My\tNotes.md --gate")).toEqual({
235
+ filePath: "My\tNotes.md",
236
+ rawFilePath: "My\tNotes.md",
237
+ gate: true,
238
+ json: false,
239
+ hook: false,
240
+ renderHtml: false,
241
+ noJina: false,
242
+ });
243
+ });
244
+
245
+ test("multi-whitespace path with no flags passes through untouched", () => {
246
+ expect(parseAnnotateArgs("/tmp/My Notes.md")).toEqual({
247
+ filePath: "/tmp/My Notes.md",
248
+ rawFilePath: "/tmp/My Notes.md",
249
+ gate: false,
250
+ json: false,
251
+ hook: false,
252
+ renderHtml: false,
253
+ noJina: false,
254
+ });
255
+ });
256
+
257
+ // OpenCode and Pi don't go through a shell, so users who quote paths
258
+ // (shell muscle memory, copy-paste from docs) have literal quote
259
+ // characters reach the parser. Strip them at the tokenization layer
260
+ // so downstream callers don't have to reason about quoting.
261
+
262
+ test("wrapping double quotes are stripped from both filePath and rawFilePath", () => {
263
+ expect(parseAnnotateArgs(`"@foo.md" --gate`)).toEqual({
264
+ filePath: "foo.md",
265
+ rawFilePath: "@foo.md",
266
+ gate: true,
267
+ json: false,
268
+ hook: false,
269
+ renderHtml: false,
270
+ noJina: false,
271
+ });
272
+ });
273
+
274
+ test("wrapping single quotes are stripped", () => {
275
+ expect(parseAnnotateArgs(`'@foo.md' --gate`)).toEqual({
276
+ filePath: "foo.md",
277
+ rawFilePath: "@foo.md",
278
+ gate: true,
279
+ json: false,
280
+ hook: false,
281
+ renderHtml: false,
282
+ noJina: false,
283
+ });
284
+ });
285
+
286
+ test("wrapping quotes around a path with spaces", () => {
287
+ expect(parseAnnotateArgs(`"@My Notes.md" --gate`)).toEqual({
288
+ filePath: "My Notes.md",
289
+ rawFilePath: "@My Notes.md",
290
+ gate: true,
291
+ json: false,
292
+ hook: false,
293
+ renderHtml: false,
294
+ noJina: false,
295
+ });
296
+ });
297
+
298
+ test("wrapping quotes without @ still get stripped", () => {
299
+ expect(parseAnnotateArgs(`"My Notes.md"`)).toEqual({
300
+ filePath: "My Notes.md",
301
+ rawFilePath: "My Notes.md",
302
+ gate: false,
303
+ json: false,
304
+ hook: false,
305
+ renderHtml: false,
306
+ noJina: false,
307
+ });
308
+ });
309
+
310
+ // --hook emits hook-native JSON ({"decision":"block","reason":"..."}) for
311
+ // annotations and empty stdout for approve/close. It implies --gate in the
312
+ // binary, but the parser is a pure tokenizer — it reports which flags were
313
+ // present without applying implication logic.
314
+
315
+ test("--hook alongside --gate", () => {
316
+ expect(parseAnnotateArgs("spec.md --gate --hook")).toEqual({
317
+ filePath: "spec.md",
318
+ rawFilePath: "spec.md",
319
+ gate: true,
320
+ json: false,
321
+ hook: true,
322
+ renderHtml: false,
323
+ noJina: false,
324
+ });
325
+ });
326
+
327
+ test("--hook with all three flags", () => {
328
+ expect(parseAnnotateArgs("spec.md --gate --json --hook")).toEqual({
329
+ filePath: "spec.md",
330
+ rawFilePath: "spec.md",
331
+ gate: true,
332
+ json: true,
333
+ hook: true,
334
+ renderHtml: false,
335
+ noJina: false,
336
+ });
337
+ });
338
+
339
+ test("--hook alone implies gate", () => {
340
+ expect(parseAnnotateArgs("spec.md --hook")).toEqual({
341
+ filePath: "spec.md",
342
+ rawFilePath: "spec.md",
343
+ gate: true,
344
+ json: false,
345
+ hook: true,
346
+ renderHtml: false,
347
+ noJina: false,
348
+ });
349
+ });
350
+
351
+ test("--hook before path", () => {
352
+ expect(parseAnnotateArgs("--hook --gate spec.md")).toEqual({
353
+ filePath: "spec.md",
354
+ rawFilePath: "spec.md",
355
+ gate: true,
356
+ json: false,
357
+ hook: true,
358
+ renderHtml: false,
359
+ noJina: false,
360
+ });
361
+ });
362
+
363
+ test("--render-html with HTML file and --gate", () => {
364
+ expect(parseAnnotateArgs("plan.html --render-html --gate")).toEqual({
365
+ filePath: "plan.html",
366
+ rawFilePath: "plan.html",
367
+ gate: true,
368
+ json: false,
369
+ hook: false,
370
+ renderHtml: true,
371
+ noJina: false,
372
+ });
373
+ });
374
+
375
+ test("--render-html alone", () => {
376
+ expect(parseAnnotateArgs("plan.html --render-html")).toEqual({
377
+ filePath: "plan.html",
378
+ rawFilePath: "plan.html",
379
+ gate: false,
380
+ json: false,
381
+ hook: false,
382
+ renderHtml: true,
383
+ noJina: false,
384
+ });
385
+ });
386
+ });