@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,247 @@
1
+ import { loadConfig, type PlannotatorConfig, type PromptRuntime } from "./config";
2
+
3
+ // ─── Template engine ─────────────────────────────────────────────────────────
4
+
5
+ export function resolveTemplate(
6
+ template: string,
7
+ vars: Record<string, string | undefined>,
8
+ ): string {
9
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
10
+ const val = vars[key];
11
+ return val !== undefined ? val : match;
12
+ });
13
+ }
14
+
15
+ // ─── Tool name map ───────────────────────────────────────────────────────────
16
+
17
+ export const PLAN_TOOL_NAMES: Record<PromptRuntime, string> = {
18
+ "claude-code": "ExitPlanMode",
19
+ amp: "ExitPlanMode",
20
+ droid: "ExitPlanMode",
21
+ "kiro-cli": "ExitPlanMode",
22
+ opencode: "submit_plan",
23
+ "copilot-cli": "exit_plan_mode",
24
+ pi: "plannotator_submit_plan",
25
+ codex: "ExitPlanMode",
26
+ "gemini-cli": "exit_plan_mode",
27
+ };
28
+
29
+ export function getPlanToolName(runtime?: PromptRuntime | null): string {
30
+ return (runtime && PLAN_TOOL_NAMES[runtime]) || "ExitPlanMode";
31
+ }
32
+
33
+ export function buildPlanFileRule(toolName: string, planFilePath?: string): string {
34
+ if (!planFilePath) return "";
35
+ return `- Your plan is saved at: ${planFilePath}\n You can edit this file to make targeted changes, then pass its path to ${toolName}.\n`;
36
+ }
37
+
38
+ // ─── Default constants ───────────────────────────────────────────────────────
39
+
40
+ export const DEFAULT_REVIEW_APPROVED_PROMPT = "# Code Review\n\nCode review completed — no changes requested.";
41
+
42
+ export const DEFAULT_REVIEW_DENIED_SUFFIX = "\nThe reviewer has identified issues above. You must address all of them.";
43
+
44
+ export const DEFAULT_PLAN_DENIED_PROMPT =
45
+ "YOUR PLAN WAS NOT APPROVED.\n\nYou MUST revise the plan to address ALL of the feedback below before calling {{toolName}} again.\n\nRules:\n{{planFileRule}}- Do not resubmit the same plan unchanged.\n- Do NOT change the plan title (first # heading) unless the user explicitly asks you to.\n\n{{feedback}}";
46
+
47
+ export const DEFAULT_PLAN_APPROVED_PROMPT =
48
+ "Plan approved. You now have full tool access (read, bash, edit, write). Execute the plan in {{planFilePath}}. {{doneMsg}}";
49
+
50
+ export const DEFAULT_PLAN_APPROVED_WITH_NOTES_PROMPT =
51
+ "Plan approved with notes! You now have full tool access (read, bash, edit, write). Execute the plan in {{planFilePath}}. {{doneMsg}}\n\n## Implementation Notes\n\nThe user approved your plan but added the following notes to consider during implementation:\n\n{{feedback}}\n\nProceed with implementation, incorporating these notes where applicable.";
52
+
53
+ export const DEFAULT_PLAN_AUTO_APPROVED_PROMPT =
54
+ "Plan auto-approved (non-interactive mode). Execute the plan now.";
55
+
56
+ export const DEFAULT_ANNOTATE_FILE_FEEDBACK_PROMPT =
57
+ "# Markdown Annotations\n\n{{fileHeader}}: {{filePath}}\n\n{{feedback}}\n\nPlease address the annotation feedback above.";
58
+
59
+ export const DEFAULT_ANNOTATE_MESSAGE_FEEDBACK_PROMPT =
60
+ "# Message Annotations\n\n{{feedback}}\n\nPlease address the annotation feedback above.";
61
+
62
+ export const DEFAULT_ANNOTATE_APPROVED_PROMPT = "The user approved.";
63
+
64
+ // ─── Core resolver ───────────────────────────────────────────────────────────
65
+
66
+ type PromptSection = "review" | "plan" | "annotate";
67
+ type PromptKey = "approved" | "approvedWithNotes" | "autoApproved" | "denied"
68
+ | "fileFeedback" | "messageFeedback";
69
+
70
+ interface PromptLookupOptions {
71
+ section: PromptSection;
72
+ key: PromptKey;
73
+ runtime?: PromptRuntime | null;
74
+ config?: PlannotatorConfig;
75
+ fallback: string;
76
+ runtimeFallbacks?: Partial<Record<PromptRuntime, string>>;
77
+ }
78
+
79
+ function normalizePrompt(prompt: unknown): string | undefined {
80
+ if (typeof prompt !== "string") return undefined;
81
+ return prompt.trim() ? prompt : undefined;
82
+ }
83
+
84
+ export function getConfiguredPrompt(options: PromptLookupOptions): string {
85
+ const resolvedConfig = options.config ?? loadConfig();
86
+ const section = resolvedConfig.prompts?.[options.section];
87
+ const runtimePrompt = options.runtime
88
+ ? normalizePrompt(section?.runtimes?.[options.runtime]?.[options.key])
89
+ : undefined;
90
+ const genericPrompt = normalizePrompt(section?.[options.key]);
91
+ const runtimeFallback = options.runtime
92
+ ? options.runtimeFallbacks?.[options.runtime]
93
+ : undefined;
94
+
95
+ return runtimePrompt ?? genericPrompt ?? runtimeFallback ?? options.fallback;
96
+ }
97
+
98
+ type FeedbackVars = Record<string, string | undefined>;
99
+
100
+ // ─── Review wrappers ─────────────────────────────────────────────────────────
101
+
102
+ export function getReviewApprovedPrompt(
103
+ runtime?: PromptRuntime | null,
104
+ config?: PlannotatorConfig,
105
+ ): string {
106
+ return getConfiguredPrompt({
107
+ section: "review",
108
+ key: "approved",
109
+ runtime,
110
+ config,
111
+ fallback: DEFAULT_REVIEW_APPROVED_PROMPT,
112
+ });
113
+ }
114
+
115
+ const REVIEW_DENIED_RUNTIME_DEFAULTS: Partial<Record<PromptRuntime, string>> = {
116
+ opencode: "\n\nPlease address this feedback.",
117
+ pi: "\n\nPlease address this feedback.",
118
+ };
119
+
120
+ export function getReviewDeniedSuffix(
121
+ runtime?: PromptRuntime | null,
122
+ config?: PlannotatorConfig,
123
+ ): string {
124
+ return getConfiguredPrompt({
125
+ section: "review",
126
+ key: "denied",
127
+ runtime,
128
+ config,
129
+ fallback: DEFAULT_REVIEW_DENIED_SUFFIX,
130
+ runtimeFallbacks: REVIEW_DENIED_RUNTIME_DEFAULTS,
131
+ });
132
+ }
133
+
134
+ // ─── Plan wrappers ───────────────────────────────────────────────────────────
135
+
136
+ export function getPlanDeniedPrompt(
137
+ runtime?: PromptRuntime | null,
138
+ config?: PlannotatorConfig,
139
+ vars?: FeedbackVars,
140
+ ): string {
141
+ const template = getConfiguredPrompt({
142
+ section: "plan",
143
+ key: "denied",
144
+ runtime,
145
+ config,
146
+ fallback: DEFAULT_PLAN_DENIED_PROMPT,
147
+ });
148
+ return resolveTemplate(template, vars ?? {});
149
+ }
150
+
151
+ const PLAN_APPROVED_RUNTIME_DEFAULTS: Partial<Record<PromptRuntime, string>> = {
152
+ opencode: "Plan approved!{{doneMsg}}",
153
+ };
154
+
155
+ export function getPlanApprovedPrompt(
156
+ runtime?: PromptRuntime | null,
157
+ config?: PlannotatorConfig,
158
+ vars?: FeedbackVars,
159
+ ): string {
160
+ const template = getConfiguredPrompt({
161
+ section: "plan",
162
+ key: "approved",
163
+ runtime,
164
+ config,
165
+ fallback: DEFAULT_PLAN_APPROVED_PROMPT,
166
+ runtimeFallbacks: PLAN_APPROVED_RUNTIME_DEFAULTS,
167
+ });
168
+ return resolveTemplate(template, vars ?? {});
169
+ }
170
+
171
+ const PLAN_APPROVED_WITH_NOTES_RUNTIME_DEFAULTS: Partial<Record<PromptRuntime, string>> = {
172
+ opencode: "Plan approved with notes!\n{{doneMsg}}\n\n## Implementation Notes\n\nThe user approved your plan but added the following notes to consider during implementation:\n\n{{feedback}}{{proceedSuffix}}",
173
+ };
174
+
175
+ export function getPlanApprovedWithNotesPrompt(
176
+ runtime?: PromptRuntime | null,
177
+ config?: PlannotatorConfig,
178
+ vars?: FeedbackVars,
179
+ ): string {
180
+ const template = getConfiguredPrompt({
181
+ section: "plan",
182
+ key: "approvedWithNotes",
183
+ runtime,
184
+ config,
185
+ fallback: DEFAULT_PLAN_APPROVED_WITH_NOTES_PROMPT,
186
+ runtimeFallbacks: PLAN_APPROVED_WITH_NOTES_RUNTIME_DEFAULTS,
187
+ });
188
+ return resolveTemplate(template, { proceedSuffix: "", ...vars });
189
+ }
190
+
191
+ export function getPlanAutoApprovedPrompt(
192
+ runtime?: PromptRuntime | null,
193
+ config?: PlannotatorConfig,
194
+ ): string {
195
+ return getConfiguredPrompt({
196
+ section: "plan",
197
+ key: "autoApproved",
198
+ runtime,
199
+ config,
200
+ fallback: DEFAULT_PLAN_AUTO_APPROVED_PROMPT,
201
+ });
202
+ }
203
+
204
+ // ─── Annotate wrappers ──────────────────────────────────────────────────────
205
+
206
+ export function getAnnotateFileFeedbackPrompt(
207
+ runtime?: PromptRuntime | null,
208
+ config?: PlannotatorConfig,
209
+ vars?: FeedbackVars,
210
+ ): string {
211
+ const template = getConfiguredPrompt({
212
+ section: "annotate",
213
+ key: "fileFeedback",
214
+ runtime,
215
+ config,
216
+ fallback: DEFAULT_ANNOTATE_FILE_FEEDBACK_PROMPT,
217
+ });
218
+ return resolveTemplate(template, vars ?? {});
219
+ }
220
+
221
+ export function getAnnotateMessageFeedbackPrompt(
222
+ runtime?: PromptRuntime | null,
223
+ config?: PlannotatorConfig,
224
+ vars?: FeedbackVars,
225
+ ): string {
226
+ const template = getConfiguredPrompt({
227
+ section: "annotate",
228
+ key: "messageFeedback",
229
+ runtime,
230
+ config,
231
+ fallback: DEFAULT_ANNOTATE_MESSAGE_FEEDBACK_PROMPT,
232
+ });
233
+ return resolveTemplate(template, vars ?? {});
234
+ }
235
+
236
+ export function getAnnotateApprovedPrompt(
237
+ runtime?: PromptRuntime | null,
238
+ config?: PlannotatorConfig,
239
+ ): string {
240
+ return getConfiguredPrompt({
241
+ section: "annotate",
242
+ key: "approved",
243
+ runtime,
244
+ config,
245
+ fallback: DEFAULT_ANNOTATE_APPROVED_PROMPT,
246
+ });
247
+ }
@@ -0,0 +1,87 @@
1
+ // --- Vault file tree helpers ---
2
+
3
+ export const FILE_BROWSER_EXCLUDED = [
4
+ "node_modules/",
5
+ ".git/",
6
+ "dist/",
7
+ "build/",
8
+ ".next/",
9
+ "__pycache__/",
10
+ ".obsidian/",
11
+ ".trash/",
12
+ ".venv/",
13
+ "vendor/",
14
+ "target/",
15
+ ".cache/",
16
+ "coverage/",
17
+ ".turbo/",
18
+ ".svelte-kit/",
19
+ ".nuxt/",
20
+ ".output/",
21
+ ".parcel-cache/",
22
+ ".webpack/",
23
+ ".expo/",
24
+ "_site/",
25
+ "public/",
26
+ ".jekyll-cache/",
27
+ "out/",
28
+ ".docusaurus/",
29
+ "storybook-static/",
30
+ ];
31
+
32
+ export interface VaultNode {
33
+ name: string;
34
+ path: string; // relative path within vault
35
+ type: "file" | "folder";
36
+ children?: VaultNode[];
37
+ }
38
+
39
+ /**
40
+ * Build a nested file tree from a sorted list of relative paths.
41
+ * Folders are sorted before files at each level.
42
+ */
43
+ export function buildFileTree(relativePaths: string[]): VaultNode[] {
44
+ const root: VaultNode[] = [];
45
+
46
+ for (const filePath of relativePaths) {
47
+ const parts = filePath.split("/");
48
+ let current = root;
49
+ let pathSoFar = "";
50
+
51
+ for (let i = 0; i < parts.length; i++) {
52
+ const part = parts[i];
53
+ pathSoFar = pathSoFar ? `${pathSoFar}/${part}` : part;
54
+ const isFile = i === parts.length - 1;
55
+
56
+ let node = current.find(
57
+ (n) => n.name === part && n.type === (isFile ? "file" : "folder"),
58
+ );
59
+ if (!node) {
60
+ node = {
61
+ name: part,
62
+ path: pathSoFar,
63
+ type: isFile ? "file" : "folder",
64
+ };
65
+ if (!isFile) node.children = [];
66
+ current.push(node);
67
+ }
68
+ if (!isFile) {
69
+ current = node.children!;
70
+ }
71
+ }
72
+ }
73
+
74
+ // Sort: folders first (alphabetical), then files (alphabetical)
75
+ const sortNodes = (nodes: VaultNode[]) => {
76
+ nodes.sort((a, b) => {
77
+ if (a.type !== b.type) return a.type === "folder" ? -1 : 1;
78
+ return a.name.localeCompare(b.name);
79
+ });
80
+ for (const node of nodes) {
81
+ if (node.children) sortNodes(node.children);
82
+ }
83
+ };
84
+ sortNodes(root);
85
+
86
+ return root;
87
+ }
@@ -0,0 +1,71 @@
1
+ export interface RepoInfo {
2
+ /** Display string (e.g., "backnotprop/plannotator" or "my-project") */
3
+ display: string;
4
+ /** Current git branch (if in a git repo) */
5
+ branch?: string;
6
+ /** Host of the git remote (e.g., "github.com", "gitlab.com"). Populated */
7
+ /** only when the remote URL is parseable; absent for directory-only fallbacks. */
8
+ host?: string;
9
+ }
10
+
11
+ /**
12
+ * Parse org/repo from a git remote URL
13
+ *
14
+ * Handles:
15
+ * - SSH: git@github.com:org/repo.git
16
+ * - HTTPS: https://github.com/org/repo.git
17
+ * - SSH with port: ssh://git@github.com:22/org/repo.git
18
+ * - GitLab subgroups: git@gitlab.com:group/subgroup/project.git
19
+ */
20
+ export function parseRemoteUrl(url: string): string | null {
21
+ if (!url) return null;
22
+
23
+ // SSH with port: ssh://git@host:22/path.git — strip scheme+host+port
24
+ const sshPortMatch = url.match(/^ssh:\/\/[^/]+(?::\d+)?\/(.+?)(?:\.git)?$/);
25
+ if (sshPortMatch) return sshPortMatch[1];
26
+
27
+ // SSH format: git@host:path.git — capture full path after ':'
28
+ // Reject URLs with :// scheme (HTTPS with non-standard ports like :8443)
29
+ if (!url.includes("://")) {
30
+ const sshMatch = url.match(/:([^/][^:]*?)(?:\.git)?$/);
31
+ if (sshMatch) return sshMatch[1];
32
+ }
33
+
34
+ // HTTPS format: https://host/path.git — capture full path after host
35
+ const httpsMatch = url.match(/^https?:\/\/[^/]+\/(.+?)(?:\.git)?$/);
36
+ if (httpsMatch) return httpsMatch[1];
37
+
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * Parse the host from a git remote URL. Returns null when the shape
43
+ * doesn't match a known remote form. Used to identify the forge
44
+ * (github.com, gitlab.com, self-hosted) so inline mention / issue
45
+ * refs can link to the correct destination instead of assuming GitHub.
46
+ */
47
+ export function parseRemoteHost(url: string): string | null {
48
+ if (!url) return null;
49
+ // ssh://git@host:port/path
50
+ const sshPort = url.match(/^ssh:\/\/(?:[^@]+@)?([^:/]+)/i);
51
+ if (sshPort) return sshPort[1];
52
+ // git@host:path
53
+ if (!url.includes('://')) {
54
+ const ssh = url.match(/^[^@\s]+@([^:\s]+):/);
55
+ if (ssh) return ssh[1];
56
+ }
57
+ // https://host/path or http://host/path
58
+ const https = url.match(/^https?:\/\/([^/:]+)/i);
59
+ if (https) return https[1];
60
+ return null;
61
+ }
62
+
63
+ /**
64
+ * Get directory name from path
65
+ */
66
+ export function getDirName(path: string): string | null {
67
+ if (!path) return null;
68
+ const trimmed = path.trim().replace(/\/+$/, "");
69
+ const parts = trimmed.split("/");
70
+ return parts[parts.length - 1] || null;
71
+ }
@@ -0,0 +1,113 @@
1
+ import { describe, test, expect, beforeAll, afterAll } from "bun:test";
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
5
+ import { resolveCodeFile } from "./resolve-file";
6
+
7
+ let root: string;
8
+
9
+ beforeAll(() => {
10
+ root = mkdtempSync(join(tmpdir(), "plannotator-resolve-"));
11
+ mkdirSync(join(root, "packages/editor"), { recursive: true });
12
+ mkdirSync(join(root, "packages/review-editor"), { recursive: true });
13
+ mkdirSync(join(root, "packages/ui/components"), { recursive: true });
14
+ mkdirSync(join(root, "node_modules/junk"), { recursive: true });
15
+ writeFileSync(join(root, "packages/editor/App.tsx"), "// editor");
16
+ writeFileSync(join(root, "packages/review-editor/App.tsx"), "// review");
17
+ writeFileSync(join(root, "packages/ui/components/Button.tsx"), "// btn");
18
+ writeFileSync(join(root, "packages/ui/index.ts"), "// idx");
19
+ writeFileSync(join(root, "node_modules/junk/App.tsx"), "// junk");
20
+ });
21
+
22
+ afterAll(() => {
23
+ rmSync(root, { recursive: true, force: true });
24
+ });
25
+
26
+ describe("resolveCodeFile", () => {
27
+ test("resolves an exact relative path", async () => {
28
+ const r = await resolveCodeFile("packages/editor/App.tsx", root);
29
+ expect(r.kind).toBe("found");
30
+ if (r.kind === "found") {
31
+ expect(r.path).toBe(join(root, "packages/editor/App.tsx"));
32
+ }
33
+ });
34
+
35
+ test("resolves an abbreviated path via suffix match", async () => {
36
+ const r = await resolveCodeFile("editor/App.tsx", root);
37
+ expect(r.kind).toBe("found");
38
+ if (r.kind === "found") {
39
+ expect(r.path).toBe(join(root, "packages/editor/App.tsx"));
40
+ }
41
+ });
42
+
43
+ test("returns ambiguous when basename matches multiple files", async () => {
44
+ const r = await resolveCodeFile("App.tsx", root);
45
+ expect(r.kind).toBe("ambiguous");
46
+ if (r.kind === "ambiguous") {
47
+ expect(r.matches).toHaveLength(2);
48
+ }
49
+ });
50
+
51
+ test("returns not_found for a non-existent path", async () => {
52
+ const r = await resolveCodeFile("packages/ui/shortcuts/core.ts", root);
53
+ expect(r.kind).toBe("not_found");
54
+ });
55
+
56
+ test("ignores node_modules", async () => {
57
+ const r = await resolveCodeFile("junk/App.tsx", root);
58
+ expect(r.kind).toBe("not_found");
59
+ });
60
+
61
+ test("does not match similarly-named directories", async () => {
62
+ // myeditor/App.tsx must NOT match packages/editor/App.tsx — segment boundary required.
63
+ const r = await resolveCodeFile("myeditor/App.tsx", root);
64
+ expect(r.kind).toBe("not_found");
65
+ });
66
+
67
+ test("returns found for a single-segment input that uniquely exists", async () => {
68
+ // `index.ts` is bare basename; only one in tree.
69
+ const r = await resolveCodeFile("index.ts", root);
70
+ expect(r.kind).toBe("found");
71
+ if (r.kind === "found") {
72
+ expect(r.path).toBe(join(root, "packages/ui/index.ts"));
73
+ }
74
+ });
75
+
76
+ test("strips leading ./ before suffix matching", async () => {
77
+ // Earlier this fell through to step 3 with target='./editor/app.tsx'
78
+ // and never matched any real file. The cleanup makes it work.
79
+ const r = await resolveCodeFile("./editor/App.tsx", root);
80
+ expect(r.kind).toBe("found");
81
+ if (r.kind === "found") {
82
+ expect(r.path).toBe(join(root, "packages/editor/App.tsx"));
83
+ }
84
+ });
85
+
86
+ test("does NOT strip leading ../ — without baseDir, refuses to fabricate", async () => {
87
+ // `../foo.tsx` is meaningful (escape parent). With no baseDir context,
88
+ // we can't honor it, so we must fail rather than silently returning
89
+ // an unrelated file with the same basename from inside cwd.
90
+ const r = await resolveCodeFile("../editor/App.tsx", root);
91
+ expect(r.kind).toBe("not_found");
92
+ });
93
+
94
+ test("resolves via baseDir when input is relative to active doc", async () => {
95
+ // Linked doc at `<root>/packages/review-editor/...` references `../editor/App.tsx`
96
+ const baseDir = join(root, "packages/review-editor");
97
+ const r = await resolveCodeFile("../editor/App.tsx", root, baseDir);
98
+ expect(r.kind).toBe("found");
99
+ if (r.kind === "found") {
100
+ expect(r.path).toBe(join(root, "packages/editor/App.tsx"));
101
+ }
102
+ });
103
+
104
+ test("baseDir miss falls through to suffix walk", async () => {
105
+ // baseDir doesn't have the file, but cwd tree does — walk catches it.
106
+ const baseDir = join(root, "packages/review-editor");
107
+ const r = await resolveCodeFile("ui/components/Button.tsx", root, baseDir);
108
+ expect(r.kind).toBe("found");
109
+ if (r.kind === "found") {
110
+ expect(r.path).toBe(join(root, "packages/ui/components/Button.tsx"));
111
+ }
112
+ });
113
+ });