@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,538 @@
1
+ import { existsSync, copyFileSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from "fs";
2
+ import { join, basename, relative } from "path";
3
+ import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
4
+ import { loadConfig, type PiPiConfig, type VariantConfig } from "./config.js";
5
+ import {
6
+ createTask,
7
+ loadTask,
8
+ saveTask,
9
+ lockTask,
10
+ type TaskType,
11
+ type TaskState,
12
+ type Phase,
13
+ } from "./state.js";
14
+ import { phasePipeline } from "./phases/machine.js";
15
+ import { loadContextFiles, getPhaseArtifacts, getLatestSynthesizedPlan } from "./context.js";
16
+ import { brainstormSystemPrompt } from "./phases/brainstorm.js";
17
+ import { planningSystemPrompt, spawnPlanners } from "./phases/planning.js";
18
+ import { implementationSystemPrompt } from "./phases/implementation.js";
19
+ import { reviewSystemPrompt } from "./phases/review.js";
20
+ import { registerAgentDefinitions, unregisterAgentDefinitions } from "./agents/registry.js";
21
+ import { createExploreAgent } from "./agents/explore.js";
22
+ import { createLibrarianAgent } from "./agents/librarian.js";
23
+ import { createTaskAgent } from "./agents/task.js";
24
+
25
+ const BUNDLED_TOOLS = new Set([
26
+ "Agent", "get_subagent_result", "steer_subagent",
27
+ "TaskCreate", "TaskList", "TaskGet", "TaskUpdate", "TaskOutput", "TaskStop", "TaskExecute",
28
+ "ask_user",
29
+ ]);
30
+
31
+ export interface ActiveTask {
32
+ dir: string;
33
+ type: TaskType;
34
+ state: TaskState;
35
+ release: (() => Promise<void>) | null;
36
+ taskId: string;
37
+ modifiedFiles: Set<string>;
38
+ reviewPass: number;
39
+ description: string;
40
+ }
41
+
42
+ export class Orchestrator {
43
+ active: ActiveTask | null = null;
44
+ config!: PiPiConfig;
45
+ cwd = "";
46
+ spawnedAgentIds = new Set<string>();
47
+ agentDescriptions = new Map<string, string>();
48
+ agentSpawnTimes = new Map<string, number>();
49
+ agentLifecycle = new Map<string, {
50
+ createdAt?: number;
51
+ startedAt?: number;
52
+ firstToolAt?: number;
53
+ firstTurnAt?: number;
54
+ lastEventAt?: number;
55
+ type?: string;
56
+ description?: string;
57
+ phase?: string;
58
+ step?: string;
59
+ }>();
60
+ staleAgentTimer: ReturnType<typeof setInterval> | null = null;
61
+ phaseCompactionPending = false;
62
+ phaseCompactionResolve: (() => void) | null = null;
63
+ taskDoneCompactionPending = false;
64
+ taskDoneCompactionSummary = "";
65
+ nudgeTimestamps: number[] = [];
66
+ cooldownHits: number[] = [];
67
+ nudgeHalted = false;
68
+ pendingSubagentSpawns = 0;
69
+ errorRetryCount = 0;
70
+ commitReminderSent = false;
71
+ phaseStartTime = 0;
72
+ awaitPollTimer: ReturnType<typeof setInterval> | null = null;
73
+ pendingRetryTimer: ReturnType<typeof setTimeout> | null = null;
74
+ activeTaskToken = 0;
75
+ userGatePending = false;
76
+ reviewTransitionToken = -1;
77
+ lastCtx: any = null;
78
+ failedPlannerVariants: string[] = [];
79
+ failedReviewerVariants: string[] = [];
80
+ plannerFailureDialogPending = false;
81
+ reviewerFailureDialogPending = false;
82
+ plannotatorReject: ((reason: Error) => void) | null = null;
83
+ plannotatorUnsub: (() => void) | null = null;
84
+ transitionToNextPhase: (ctx: any) => Promise<{ ok: boolean; error?: string }> = async () => ({ ok: false, error: "not initialized" });
85
+
86
+ constructor(readonly pi: ExtensionAPI) {}
87
+
88
+ truncateResult(result: string): string {
89
+ const trimmed = result.trim();
90
+ if (!trimmed) return "";
91
+ const lines = trimmed.split("\n");
92
+ if (lines.length <= 20 && trimmed.length <= 2000) return trimmed;
93
+ const truncated = lines.slice(0, 20).join("\n").slice(0, 2000);
94
+ return truncated + "\n…(truncated)";
95
+ }
96
+
97
+ async switchModel(ctx: ExtensionContext, modelSpec: string, thinking: string): Promise<boolean> {
98
+ const registry = ctx.modelRegistry;
99
+ const allModels = registry.getAvailable();
100
+
101
+ const slashIdx = modelSpec.indexOf("/");
102
+ let resolved;
103
+ if (slashIdx !== -1) {
104
+ const provider = modelSpec.substring(0, slashIdx).trim().toLowerCase();
105
+ const modelId = modelSpec.substring(slashIdx + 1).trim().toLowerCase();
106
+ resolved = allModels.find(
107
+ (m) => m.provider.toLowerCase() === provider && m.id.toLowerCase() === modelId,
108
+ );
109
+ }
110
+ if (!resolved) {
111
+ const pattern = modelSpec.toLowerCase();
112
+ const matches = allModels.filter(
113
+ (m) => m.id.toLowerCase() === pattern || m.id.toLowerCase().includes(pattern),
114
+ );
115
+ if (matches.length === 1) resolved = matches[0];
116
+ }
117
+
118
+ if (!resolved) return false;
119
+
120
+ const ok = await this.pi.setModel(resolved);
121
+ if (!ok) return false;
122
+
123
+ const VALID_THINKING = new Set(["off", "low", "medium", "high"]);
124
+ const thinkingLevel = (VALID_THINKING.has(thinking) ? thinking : "high") as "off" | "low" | "medium" | "high";
125
+ this.pi.setThinkingLevel(thinkingLevel);
126
+ return true;
127
+ }
128
+
129
+ updateStatus(ctx: ExtensionContext): void {
130
+ if (!this.active || this.active.state.phase === "done") {
131
+ ctx.ui.setStatus("pp-phase", undefined);
132
+ return;
133
+ }
134
+
135
+ const type = this.active.type;
136
+ const phase = this.active.state.phase;
137
+ const step = this.active.state.step;
138
+ const reviewCycle = this.active.state.reviewCycle;
139
+
140
+ if (type === "debug" || type === "brainstorm") {
141
+ const elapsed = this.phaseStartTime > 0 ? this.formatElapsed(this.phaseStartTime) : "";
142
+ const suffix = elapsed ? ` (${elapsed})` : "";
143
+ ctx.ui.setStatus("pp-phase", `pp: ${type}${suffix}`);
144
+ return;
145
+ }
146
+
147
+ const pipeline = phasePipeline(type).filter((p) => p !== "done");
148
+ const currentIdx = pipeline.indexOf(phase as (typeof pipeline)[number]);
149
+
150
+ const parts: string[] = [];
151
+ for (let i = 0; i < pipeline.length; i++) {
152
+ const p = pipeline[i];
153
+ if (i < currentIdx) {
154
+ parts.push(`✔ ${p}`);
155
+ } else if (p === phase) {
156
+ let detail = "";
157
+ if (step === "await_planners") detail = "planners";
158
+ else if (step === "await_reviewers") detail = "reviewers";
159
+ else if (step === "synthesize") detail = "synthesize";
160
+ else if (step === "apply_feedback") detail = "feedback";
161
+ else if (step === "user_gate") detail = "review";
162
+
163
+ if (reviewCycle) {
164
+ const kind = reviewCycle.kind === "plannotator" ? "plannotator" : reviewCycle.kind === "auto-deep" ? "deep review" : "review";
165
+ detail = `${kind} #${reviewCycle.pass}`;
166
+ }
167
+
168
+ const elapsed = this.phaseStartTime > 0 ? this.formatElapsed(this.phaseStartTime) : "";
169
+ const sub = [detail, elapsed].filter(Boolean).join(", ");
170
+ parts.push(sub ? `${p} (${sub})` : p);
171
+ } else {
172
+ parts.push(p);
173
+ }
174
+ }
175
+
176
+ ctx.ui.setStatus("pp-phase", `pp: ${parts.join(" → ")}`);
177
+ }
178
+
179
+ private formatElapsed(startTime: number): string {
180
+ const sec = Math.floor((Date.now() - startTime) / 1000);
181
+ if (sec < 60) return `${sec}s`;
182
+ const min = Math.floor(sec / 60);
183
+ const remSec = sec % 60;
184
+ if (min < 60) return remSec > 0 ? `${min}m ${remSec}s` : `${min}m`;
185
+ const hr = Math.floor(min / 60);
186
+ const remMin = min % 60;
187
+ return remMin > 0 ? `${hr}h ${remMin}m` : `${hr}h`;
188
+ }
189
+
190
+ getPlanStartState(taskDir: string): { step: string; shouldSpawnPlanners: boolean } {
191
+ const plansDir = join(taskDir, "plans");
192
+ const enabledPlannerCount = Object.values(this.config.planners).filter((v) => v.enabled).length;
193
+ const plannerOutputs = existsSync(plansDir)
194
+ ? readdirSync(plansDir).filter((f) => f.endsWith(".md") && !f.includes("synthesized") && !f.includes("review_"))
195
+ : [];
196
+
197
+ if (enabledPlannerCount === 0 || plannerOutputs.length >= enabledPlannerCount || getLatestSynthesizedPlan(taskDir)) {
198
+ return { step: "synthesize", shouldSpawnPlanners: false };
199
+ }
200
+
201
+ return { step: "await_planners", shouldSpawnPlanners: true };
202
+ }
203
+
204
+ getPhasePrompt(_ctx: ExtensionContext): string {
205
+ if (!this.active) return "";
206
+
207
+ if (this.active.state.reviewCycle?.step === "apply_feedback") {
208
+ const pass = this.active.state.reviewCycle.pass;
209
+ const manualReview = this.active.state.reviewCycle.kind === "manual";
210
+ return reviewSystemPrompt(this.active.dir, pass, manualReview, this.active.state.phase);
211
+ }
212
+
213
+ switch (this.active.state.phase) {
214
+ case "brainstorm":
215
+ return brainstormSystemPrompt(this.active.type, this.active.description, this.active.dir);
216
+ case "debug":
217
+ return brainstormSystemPrompt(this.active.type, this.active.description, this.active.dir);
218
+ case "plan":
219
+ return planningSystemPrompt(this.active.dir);
220
+ case "implement":
221
+ return implementationSystemPrompt(this.active.dir);
222
+ default:
223
+ return "";
224
+ }
225
+ }
226
+
227
+ taskIdFromDir(dir: string): string {
228
+ const name = basename(dir);
229
+ return name.split("_")[0];
230
+ }
231
+
232
+ persistReviewPass(): void {
233
+ if (!this.active) return;
234
+ this.active.state.reviewPass = this.active.reviewPass;
235
+ saveTask(this.active.dir, this.active.state);
236
+ }
237
+
238
+ async startTask(
239
+ ctx: ExtensionCommandContext,
240
+ type: TaskType,
241
+ description: string,
242
+ fromTaskDir?: string,
243
+ skipBrainstorm?: boolean,
244
+ ): Promise<void> {
245
+ if (this.active) {
246
+ ctx.ui.notify(
247
+ `Finishing previous task "${this.active.description}" (phase: ${this.active.state.phase})…`,
248
+ "info",
249
+ );
250
+ this.abortAllSubagents();
251
+ this.active.state.phase = "done";
252
+ saveTask(this.active.dir, this.active.state);
253
+ unregisterAgentDefinitions(this.pi);
254
+ await this.cleanupActive();
255
+ }
256
+
257
+ try {
258
+ this.config = loadConfig(this.cwd);
259
+ } catch (err: any) {
260
+ ctx.ui.notify(`Config error: ${err.message}`, "error");
261
+ return;
262
+ }
263
+
264
+ ensureGitignore(this.cwd);
265
+
266
+ const dir = createTask(this.cwd, type, description);
267
+ const state = loadTask(dir);
268
+
269
+ if (fromTaskDir) {
270
+ const srcUr = join(fromTaskDir, "USER_REQUEST.md");
271
+ const srcRes = join(fromTaskDir, "RESEARCH.md");
272
+ const srcArtifacts = join(fromTaskDir, "artifacts");
273
+ if (existsSync(srcUr)) copyFileSync(srcUr, join(dir, "USER_REQUEST.md"));
274
+ if (existsSync(srcRes)) copyFileSync(srcRes, join(dir, "RESEARCH.md"));
275
+ if (existsSync(srcArtifacts)) {
276
+ const destArtifacts = join(dir, "artifacts");
277
+ mkdirSync(destArtifacts, { recursive: true });
278
+ for (const f of readdirSync(srcArtifacts).filter((f) => f.endsWith(".md"))) {
279
+ copyFileSync(join(srcArtifacts, f), join(destArtifacts, f));
280
+ }
281
+ }
282
+ state.from = relative(join(this.cwd, ".pp", "state"), fromTaskDir);
283
+ if (skipBrainstorm && type === "implement") {
284
+ state.phase = "plan";
285
+ state.step = this.getPlanStartState(dir).step;
286
+ }
287
+ saveTask(dir, state);
288
+ }
289
+
290
+ let release: (() => Promise<void>) | null = null;
291
+ try {
292
+ release = await lockTask(dir, this.config.timeouts);
293
+ } catch (err: any) {
294
+ try {
295
+ rmSync(dir, { recursive: true, force: true });
296
+ } catch {
297
+ console.error(`[pi-pi] Failed to clean up orphaned task dir: ${dir}`);
298
+ }
299
+ ctx.ui.notify(`Failed to lock task: ${err.message}`, "error");
300
+ return;
301
+ }
302
+
303
+ this.resetTaskScopedState();
304
+ this.activeTaskToken++;
305
+
306
+ this.active = {
307
+ dir,
308
+ type,
309
+ state,
310
+ release,
311
+ taskId: this.taskIdFromDir(dir),
312
+ modifiedFiles: new Set(),
313
+ reviewPass: state.reviewPass,
314
+ description: state.description,
315
+ };
316
+
317
+ const modelConfig = this.config.mainModel[type === "debug" ? "debug" : type === "brainstorm" ? "brainstorm" : "implement"];
318
+ const modelOk = await this.switchModel(ctx, modelConfig.model, modelConfig.thinking);
319
+ if (!modelOk) {
320
+ ctx.ui.notify(`Model "${modelConfig.model}" not found — using current model`, "warning");
321
+ }
322
+
323
+ this.registerAgents();
324
+ this.pi.setSessionName(this.active.description.slice(0, 50));
325
+ this.lastCtx = ctx;
326
+ this.updateStatus(ctx);
327
+
328
+ this.injectContextAndArtifacts(this.active.dir, this.active.state.phase);
329
+
330
+ this.phaseStartTime = Date.now();
331
+ const isGenericDescription = ["implement", "debug", "brainstorm"].includes(this.active.description);
332
+ const hasInheritedTaskContext = Boolean(fromTaskDir && type === "implement");
333
+ const isWaitingForPlanners = this.active.state.phase === "plan" && this.active.state.step === "await_planners";
334
+ if (isGenericDescription && !hasInheritedTaskContext) {
335
+ ctx.ui.notify("Task created. Describe what you'd like to do.", "info");
336
+ } else if (isWaitingForPlanners) {
337
+ ctx.ui.notify("Entered plan phase. Waiting for planners to complete before synthesis.", "info");
338
+ } else {
339
+ this.pi.sendUserMessage(`[PI-PI] Entered ${this.active.state.phase} phase. Begin working.`);
340
+ }
341
+
342
+ if (this.active.state.phase === "plan" && this.active.state.step === "await_planners") {
343
+ this.pendingSubagentSpawns = Object.values(this.config.planners).filter((v) => v.enabled).length;
344
+ this.failedPlannerVariants = [];
345
+ spawnPlanners(this.pi, this.cwd, this.active.dir, this.active.taskId, this.config).then((result) => {
346
+ this.failedPlannerVariants = result.failedVariants;
347
+ if (result.spawned === 0) this.pendingSubagentSpawns = 0;
348
+ for (const id of result.agentIds ?? []) {
349
+ this.spawnedAgentIds.delete(id);
350
+ }
351
+ this.pendingSubagentSpawns = 0;
352
+ }).catch((err) => {
353
+ this.pendingSubagentSpawns = 0;
354
+ console.error(`[pi-pi] spawnPlanners failed: ${err.message}`);
355
+ });
356
+ }
357
+ }
358
+
359
+ abortAllSubagents(): void {
360
+ for (const agentId of this.spawnedAgentIds) {
361
+ this.pi.events.emit("subagents:rpc:stop", {
362
+ requestId: crypto.randomUUID(),
363
+ agentId,
364
+ });
365
+ }
366
+ this.spawnedAgentIds.clear();
367
+ this.pendingSubagentSpawns = 0;
368
+ }
369
+
370
+ resetTaskScopedState(): void {
371
+ this.spawnedAgentIds.clear();
372
+ this.agentDescriptions.clear();
373
+ this.agentSpawnTimes.clear();
374
+ this.agentLifecycle.clear();
375
+ this.pendingSubagentSpawns = 0;
376
+ this.errorRetryCount = 0;
377
+ this.commitReminderSent = false;
378
+ this.nudgeTimestamps = [];
379
+ this.cooldownHits = [];
380
+ this.nudgeHalted = false;
381
+ this.phaseCompactionPending = false;
382
+ this.phaseCompactionResolve = null;
383
+ this.phaseStartTime = 0;
384
+ this.userGatePending = false;
385
+ this.reviewTransitionToken = -1;
386
+ this.failedPlannerVariants = [];
387
+ this.failedReviewerVariants = [];
388
+ this.plannerFailureDialogPending = false;
389
+ this.reviewerFailureDialogPending = false;
390
+ if (this.awaitPollTimer) {
391
+ clearInterval(this.awaitPollTimer);
392
+ this.awaitPollTimer = null;
393
+ }
394
+ if (this.pendingRetryTimer) {
395
+ clearTimeout(this.pendingRetryTimer);
396
+ this.pendingRetryTimer = null;
397
+ }
398
+ if (this.staleAgentTimer) {
399
+ clearInterval(this.staleAgentTimer);
400
+ this.staleAgentTimer = null;
401
+ }
402
+ }
403
+
404
+ async cleanupActive(): Promise<void> {
405
+ if (!this.active) return;
406
+ this.resetTaskScopedState();
407
+ if (this.active.release) {
408
+ try {
409
+ await this.active.release();
410
+ } catch (err: any) {
411
+ console.error(`[pi-pi] Failed to release lock for ${this.active.dir}: ${err.message}`);
412
+ }
413
+ }
414
+ this.active = null;
415
+ }
416
+
417
+ registerAgents(): void {
418
+ const explore = createExploreAgent(this.config);
419
+ const librarian = createLibrarianAgent(this.config);
420
+ const taskAgent = createTaskAgent(this.config, "{{subtask}}", { userRequest: "", synthesizedPlan: "" });
421
+
422
+ const appendContext = (agentType: string, prompt: string): string => {
423
+ const contextFiles = loadContextFiles(this.cwd, agentType as any, "system");
424
+ if (contextFiles.length === 0) return prompt;
425
+ const contextBlock = contextFiles.map((f) => f.content).join("\n\n");
426
+ return prompt + "\n\n# Project Context\n\n" + contextBlock;
427
+ };
428
+
429
+ registerAgentDefinitions(this.pi, [
430
+ { type: "explore", variant: null, ...explore, prompt: appendContext("explore", explore.prompt) },
431
+ { type: "librarian", variant: null, ...librarian, prompt: appendContext("librarian", librarian.prompt) },
432
+ { type: "task", variant: null, ...taskAgent, prompt: appendContext("task", taskAgent.prompt) },
433
+ ]);
434
+ }
435
+
436
+ injectContextAndArtifacts(taskDir: string, phase: Phase): void {
437
+ const contextFiles = loadContextFiles(this.cwd, "main", "context");
438
+ for (const cf of contextFiles) {
439
+ this.pi.sendMessage(
440
+ { customType: "pp-context", content: cf.content, display: false },
441
+ { deliverAs: "steer" },
442
+ );
443
+ }
444
+ const artifacts = getPhaseArtifacts(taskDir, phase);
445
+ for (const artifact of artifacts) {
446
+ this.pi.sendMessage(
447
+ { customType: "pp-artifact", content: `=== ${artifact.name} ===\n${artifact.content}`, display: false },
448
+ { deliverAs: "steer" },
449
+ );
450
+ }
451
+ }
452
+
453
+ compactAndTransition(ctx: ExtensionContext, taskDir: string, phase: Phase): void {
454
+ this.phaseCompactionPending = true;
455
+ ctx.compact({
456
+ customInstructions: "Phase transition — discard all prior conversation. Produce a one-line summary: 'Previous phase completed.'",
457
+ onComplete: () => {
458
+ this.phaseCompactionPending = false;
459
+ if (this.phaseCompactionResolve) {
460
+ this.phaseCompactionResolve();
461
+ this.phaseCompactionResolve = null;
462
+ }
463
+ this.phaseStartTime = Date.now();
464
+ this.injectContextAndArtifacts(taskDir, phase);
465
+ if (this.active?.state.phase === "plan" && this.active.state.step === "await_planners") {
466
+ ctx.ui.notify("Entered plan phase. Waiting for planners to complete before synthesis.", "info");
467
+ } else {
468
+ this.pi.sendUserMessage(`[PI-PI] Entered ${phase} phase. Begin working.`);
469
+ }
470
+ },
471
+ onError: (err) => {
472
+ console.error(`[pi-pi] Phase compaction failed: ${err.message}`);
473
+ this.phaseCompactionPending = false;
474
+ if (this.phaseCompactionResolve) {
475
+ this.phaseCompactionResolve();
476
+ this.phaseCompactionResolve = null;
477
+ }
478
+ this.phaseStartTime = Date.now();
479
+ this.injectContextAndArtifacts(taskDir, phase);
480
+ if (this.active?.state.phase === "plan" && this.active.state.step === "await_planners") {
481
+ ctx.ui.notify("Entered plan phase. Waiting for planners to complete before synthesis.", "info");
482
+ } else {
483
+ this.pi.sendUserMessage(`[PI-PI] Entered ${phase} phase. Begin working.`);
484
+ }
485
+ },
486
+ });
487
+ }
488
+
489
+ checkForConflictingExtensions(): string[] {
490
+ const allTools = this.pi.getAllTools();
491
+ const seen = new Map<string, number>();
492
+ for (const tool of allTools) {
493
+ if (BUNDLED_TOOLS.has(tool.name)) {
494
+ seen.set(tool.name, (seen.get(tool.name) ?? 0) + 1);
495
+ }
496
+ }
497
+ return [...seen.entries()].filter(([, count]) => count > 1).map(([name]) => name);
498
+ }
499
+ }
500
+
501
+ export function deepReviewConfig(config: PiPiConfig): PiPiConfig {
502
+ const THINKING_UPGRADE: Record<string, string> = { low: "medium", medium: "high", high: "xhigh" };
503
+ const upgrade = (reviewers: Record<string, VariantConfig>) => {
504
+ const upgraded: Record<string, VariantConfig> = {};
505
+ for (const [name, variant] of Object.entries(reviewers)) {
506
+ upgraded[name] = { ...variant, thinking: THINKING_UPGRADE[variant.thinking] ?? "high" };
507
+ }
508
+ return upgraded;
509
+ };
510
+ return {
511
+ ...config,
512
+ codeReviewers: upgrade(config.codeReviewers),
513
+ planReviewers: upgrade(config.planReviewers),
514
+ brainstormReviewers: upgrade(config.brainstormReviewers),
515
+ };
516
+ }
517
+
518
+ export function ensureGitignore(cwd: string): void {
519
+ const ppDir = join(cwd, ".pp");
520
+ if (!existsSync(ppDir)) {
521
+ mkdirSync(ppDir, { recursive: true });
522
+ }
523
+
524
+ const gitignorePath = join(ppDir, ".gitignore");
525
+ const requiredEntries = ["state/", "config.json"];
526
+
527
+ if (!existsSync(gitignorePath)) {
528
+ writeFileSync(gitignorePath, requiredEntries.join("\n") + "\n", "utf-8");
529
+ } else {
530
+ let content = readFileSync(gitignorePath, "utf-8");
531
+ for (const entry of requiredEntries) {
532
+ if (!content.includes(entry)) {
533
+ content = content.trimEnd() + "\n" + entry + "\n";
534
+ }
535
+ }
536
+ writeFileSync(gitignorePath, content, "utf-8");
537
+ }
538
+ }
@@ -0,0 +1,15 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { brainstormSystemPrompt } from "./brainstorm.js";
3
+
4
+ describe("brainstormSystemPrompt", () => {
5
+ it("debug prompt uses pp_phase_complete", () => {
6
+ const prompt = brainstormSystemPrompt("debug", "fix a bug", "/tmp/task");
7
+ expect(prompt).toContain("pp_phase_complete");
8
+ expect(prompt).not.toContain("/pp:next");
9
+ });
10
+
11
+ it("brainstorm prompt uses pp_phase_complete", () => {
12
+ const prompt = brainstormSystemPrompt("brainstorm", "explore ideas", "/tmp/task");
13
+ expect(prompt).toContain("pp_phase_complete");
14
+ });
15
+ });