pi-crew 0.9.16 → 0.9.17

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 (432) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21790 -0
  4. package/dist/index.mjs +83070 -0
  5. package/dist/index.mjs.map +7 -0
  6. package/docs/A +358 -0
  7. package/docs/M-A +357 -0
  8. package/docs/REVIEW-FINDINGS-2026-06 +357 -0
  9. package/docs/Y +357 -0
  10. package/docs/a +357 -0
  11. package/docs/aA +358 -0
  12. package/docs/archive/README.md +91 -0
  13. package/docs/patterns/command-agent-skill.md +1 -1
  14. package/docs/skills/REFERENCE.md +0 -17
  15. package/docs//303/242mmaAAA/303/242 +357 -0
  16. package/index.bundle.ts +25 -0
  17. package/index.ts +95 -4
  18. package/package.json +15 -4
  19. package/skills/iterative-audit/SKILL.md +0 -1
  20. package/skills/widget-rendering/SKILL.md +17 -0
  21. package/src/adapters/claude-adapter.ts +1 -3
  22. package/src/adapters/export-util.ts +16 -16
  23. package/src/adapters/index.ts +5 -5
  24. package/src/agents/agent-config.ts +20 -14
  25. package/src/agents/agent-serializer.ts +1 -1
  26. package/src/agents/discover-agents.ts +83 -39
  27. package/src/benchmark/benchmark-runner.ts +51 -51
  28. package/src/benchmark/feedback-loop.ts +1 -1
  29. package/src/config/config.ts +166 -590
  30. package/src/config/defaults.ts +28 -2
  31. package/src/config/drift-detector.ts +11 -14
  32. package/src/config/markers.ts +201 -203
  33. package/src/config/resilient-parser.ts +14 -6
  34. package/src/config/role-tools.ts +3 -3
  35. package/src/config/suggestions.ts +5 -12
  36. package/src/config/types.ts +9 -25
  37. package/src/errors.ts +151 -157
  38. package/src/extension/action-suggestions.ts +54 -9
  39. package/src/extension/async-notifier.ts +43 -13
  40. package/src/extension/autonomous-policy.ts +77 -37
  41. package/src/extension/command-completions.ts +9 -8
  42. package/src/extension/context-status-injection.ts +14 -12
  43. package/src/extension/crew-autocomplete.ts +8 -16
  44. package/src/extension/crew-cleanup.ts +2 -1
  45. package/src/extension/crew-shortcuts.ts +9 -6
  46. package/src/extension/cross-extension-rpc.ts +136 -42
  47. package/src/extension/help.ts +1 -1
  48. package/src/extension/import-index.ts +10 -5
  49. package/src/extension/knowledge-injection.ts +88 -15
  50. package/src/extension/management.ts +89 -349
  51. package/src/extension/message-renderers.ts +3 -4
  52. package/src/extension/notification-router.ts +22 -5
  53. package/src/extension/notification-sink.ts +6 -3
  54. package/src/extension/pi-api.ts +17 -8
  55. package/src/extension/plan-orchestrate.ts +8 -27
  56. package/src/extension/project-init.ts +21 -6
  57. package/src/extension/register.ts +329 -975
  58. package/src/extension/registration/artifact-cleanup.ts +9 -4
  59. package/src/extension/registration/command-utils.ts +5 -1
  60. package/src/extension/registration/commands.ts +806 -314
  61. package/src/extension/registration/compaction-guard.ts +15 -15
  62. package/src/extension/registration/lifecycle.ts +259 -0
  63. package/src/extension/registration/observability.ts +293 -0
  64. package/src/extension/registration/subagent-helpers.ts +29 -7
  65. package/src/extension/registration/subagent-tools.ts +253 -44
  66. package/src/extension/registration/team-tool.ts +19 -84
  67. package/src/extension/registration/ui.ts +168 -0
  68. package/src/extension/registration/viewers.ts +64 -24
  69. package/src/extension/result-watcher.ts +18 -7
  70. package/src/extension/run-bundle-schema.ts +21 -6
  71. package/src/extension/run-export.ts +15 -6
  72. package/src/extension/run-import.ts +64 -29
  73. package/src/extension/run-index.ts +28 -9
  74. package/src/extension/run-maintenance.ts +32 -12
  75. package/src/extension/session-summary.ts +1 -1
  76. package/src/extension/team-manager-command.ts +64 -7
  77. package/src/extension/team-onboard.ts +151 -150
  78. package/src/extension/team-recommendation.ts +121 -21
  79. package/src/extension/team-tool/anchor.ts +31 -64
  80. package/src/extension/team-tool/api.ts +929 -141
  81. package/src/extension/team-tool/auto-summarize.ts +14 -26
  82. package/src/extension/team-tool/cache-control.ts +1 -5
  83. package/src/extension/team-tool/cancel.ts +138 -38
  84. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  85. package/src/extension/team-tool/chain-executor.ts +29 -51
  86. package/src/extension/team-tool/config-patch.ts +1 -1
  87. package/src/extension/team-tool/context.ts +40 -20
  88. package/src/extension/team-tool/destructive-gate.ts +10 -5
  89. package/src/extension/team-tool/doctor.ts +170 -51
  90. package/src/extension/team-tool/explain.ts +228 -214
  91. package/src/extension/team-tool/failure-patterns.ts +3 -9
  92. package/src/extension/team-tool/goal-wrap.ts +71 -29
  93. package/src/extension/team-tool/goal.ts +185 -35
  94. package/src/extension/team-tool/handle-schedule.ts +34 -15
  95. package/src/extension/team-tool/handle-settings.ts +94 -56
  96. package/src/extension/team-tool/health-monitor.ts +27 -108
  97. package/src/extension/team-tool/inspect.ts +42 -9
  98. package/src/extension/team-tool/intent-policy.ts +4 -1
  99. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  100. package/src/extension/team-tool/orchestrate.ts +7 -18
  101. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  102. package/src/extension/team-tool/plan.ts +29 -5
  103. package/src/extension/team-tool/respond.ts +49 -16
  104. package/src/extension/team-tool/run-not-found.ts +3 -8
  105. package/src/extension/team-tool/run.ts +80 -317
  106. package/src/extension/team-tool/status.ts +135 -43
  107. package/src/extension/team-tool/workflow-manage.ts +92 -25
  108. package/src/extension/team-tool-types.ts +8 -1
  109. package/src/extension/team-tool.ts +142 -551
  110. package/src/extension/validate-resources.ts +39 -8
  111. package/src/hooks/registry.ts +66 -17
  112. package/src/hooks/types.ts +1 -1
  113. package/src/i18n.ts +25 -11
  114. package/src/observability/correlation.ts +17 -3
  115. package/src/observability/event-bus.ts +51 -56
  116. package/src/observability/event-to-metric.ts +117 -15
  117. package/src/observability/exporters/otlp-exporter.ts +56 -17
  118. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  119. package/src/observability/metric-registry.ts +16 -4
  120. package/src/observability/metric-retention.ts +8 -2
  121. package/src/observability/metric-sink.ts +11 -3
  122. package/src/observability/metrics-primitives.ts +42 -9
  123. package/src/plugins/plugin-define.ts +2 -2
  124. package/src/plugins/plugin-registry.ts +25 -25
  125. package/src/plugins/plugins/index.ts +1 -1
  126. package/src/plugins/plugins/nextjs.ts +16 -16
  127. package/src/plugins/plugins/vite.ts +7 -11
  128. package/src/plugins/plugins/vitest.ts +6 -11
  129. package/src/runtime/adaptive-plan.ts +225 -40
  130. package/src/runtime/agent-control.ts +58 -15
  131. package/src/runtime/agent-memory.ts +15 -9
  132. package/src/runtime/agent-observability.ts +10 -2
  133. package/src/runtime/anchor-manager.ts +27 -25
  134. package/src/runtime/async-marker.ts +7 -1
  135. package/src/runtime/async-runner.ts +27 -23
  136. package/src/runtime/auto-summarize.ts +7 -13
  137. package/src/runtime/background-runner.ts +189 -251
  138. package/src/runtime/batch-barrier.ts +18 -16
  139. package/src/runtime/cancellation-token.ts +16 -6
  140. package/src/runtime/cancellation.ts +46 -8
  141. package/src/runtime/capability-inventory.ts +6 -4
  142. package/src/runtime/chain-parser.ts +44 -11
  143. package/src/runtime/chain-runner.ts +63 -69
  144. package/src/runtime/checkpoint.ts +12 -56
  145. package/src/runtime/child-pi.ts +343 -78
  146. package/src/runtime/coalesce-tasks.ts +268 -0
  147. package/src/runtime/code-summary.ts +71 -26
  148. package/src/runtime/compact-stages/index.ts +15 -5
  149. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  150. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  151. package/src/runtime/compaction-summary.ts +17 -10
  152. package/src/runtime/completion-guard.ts +33 -16
  153. package/src/runtime/crash-classification.ts +28 -7
  154. package/src/runtime/crash-recovery.ts +177 -37
  155. package/src/runtime/crew-agent-records.ts +145 -47
  156. package/src/runtime/crew-hooks.ts +9 -24
  157. package/src/runtime/cross-extension-rpc.ts +25 -23
  158. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  159. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  160. package/src/runtime/delivery-coordinator.ts +34 -7
  161. package/src/runtime/delta-conflict.ts +9 -21
  162. package/src/runtime/deterministic-ast.ts +1 -1
  163. package/src/runtime/diagnostic-export.ts +53 -20
  164. package/src/runtime/direct-run.ts +12 -2
  165. package/src/runtime/dwf-state-store.ts +1 -1
  166. package/src/runtime/dynamic-workflow-context.ts +186 -58
  167. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  168. package/src/runtime/effectiveness.ts +16 -8
  169. package/src/runtime/errors/crew-errors.ts +7 -11
  170. package/src/runtime/event-stream-bridge.ts +9 -3
  171. package/src/runtime/foreground-control.ts +54 -14
  172. package/src/runtime/foreground-watchdog.ts +9 -6
  173. package/src/runtime/goal-achievement.ts +27 -10
  174. package/src/runtime/goal-evaluator.ts +54 -19
  175. package/src/runtime/goal-loop-runner.ts +280 -99
  176. package/src/runtime/goal-state-store.ts +27 -17
  177. package/src/runtime/green-contract.ts +11 -2
  178. package/src/runtime/group-join.ts +64 -31
  179. package/src/runtime/handoff-manager.ts +37 -42
  180. package/src/runtime/heartbeat-gradient.ts +10 -2
  181. package/src/runtime/heartbeat-watcher.ts +32 -6
  182. package/src/runtime/hidden-handoff.ts +12 -30
  183. package/src/runtime/important-line-classifier.ts +12 -2
  184. package/src/runtime/iteration-hooks.ts +25 -15
  185. package/src/runtime/live-agent-control.ts +55 -7
  186. package/src/runtime/live-agent-manager.ts +130 -27
  187. package/src/runtime/live-control-realtime.ts +23 -3
  188. package/src/runtime/live-irc.ts +4 -1
  189. package/src/runtime/live-session-health.ts +12 -4
  190. package/src/runtime/live-session-runtime.ts +379 -117
  191. package/src/runtime/manifest-cache.ts +28 -12
  192. package/src/runtime/mcp-proxy.ts +8 -16
  193. package/src/runtime/metric-parser.ts +1 -5
  194. package/src/runtime/model-fallback.ts +55 -18
  195. package/src/runtime/model-resolver.ts +2 -6
  196. package/src/runtime/model-scope.ts +19 -3
  197. package/src/runtime/notebook-helpers.ts +60 -62
  198. package/src/runtime/orphan-worker-registry.ts +37 -26
  199. package/src/runtime/output-validator.ts +18 -3
  200. package/src/runtime/overflow-recovery.ts +5 -4
  201. package/src/runtime/parallel-research.ts +29 -5
  202. package/src/runtime/parallel-utils.ts +9 -11
  203. package/src/runtime/path-overlap.ts +150 -0
  204. package/src/runtime/peer-dep.ts +12 -17
  205. package/src/runtime/per-write-validator.ts +1 -3
  206. package/src/runtime/phase-tracker.ts +342 -330
  207. package/src/runtime/pi-args.ts +26 -8
  208. package/src/runtime/pi-json-output.ts +1 -1
  209. package/src/runtime/pi-spawn.ts +30 -19
  210. package/src/runtime/pipeline-runner.ts +56 -55
  211. package/src/runtime/plan-templates.ts +5 -6
  212. package/src/runtime/policy-engine.ts +43 -7
  213. package/src/runtime/post-checks.ts +12 -4
  214. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  215. package/src/runtime/process-lifecycle.ts +20 -10
  216. package/src/runtime/process-status.ts +16 -5
  217. package/src/runtime/progress-event-coalescer.ts +2 -1
  218. package/src/runtime/progress-tracker.ts +103 -103
  219. package/src/runtime/prose-compressor.ts +9 -11
  220. package/src/runtime/recovery-recipes.ts +118 -24
  221. package/src/runtime/replace.ts +25 -10
  222. package/src/runtime/resilient-edit.ts +10 -25
  223. package/src/runtime/result-extractor.ts +1 -2
  224. package/src/runtime/retry-executor.ts +20 -4
  225. package/src/runtime/retry-runner.ts +26 -50
  226. package/src/runtime/role-permission.ts +6 -1
  227. package/src/runtime/run-coalesced-task-group.ts +256 -0
  228. package/src/runtime/run-drift.ts +14 -15
  229. package/src/runtime/run-tracker.ts +6 -28
  230. package/src/runtime/runtime-policy.ts +1 -6
  231. package/src/runtime/runtime-resolver.ts +80 -15
  232. package/src/runtime/scheduler.ts +47 -18
  233. package/src/runtime/semaphore.ts +5 -7
  234. package/src/runtime/sensitive-paths.ts +2 -1
  235. package/src/runtime/session-usage.ts +1 -1
  236. package/src/runtime/settings-store.ts +16 -12
  237. package/src/runtime/sidechain-output.ts +6 -2
  238. package/src/runtime/single-agent-compose.ts +1 -4
  239. package/src/runtime/skill-effectiveness.ts +29 -97
  240. package/src/runtime/skill-instructions.ts +40 -83
  241. package/src/runtime/stale-reconciler.ts +73 -197
  242. package/src/runtime/stream-preview.ts +1 -1
  243. package/src/runtime/streaming-output.ts +1 -1
  244. package/src/runtime/subagent-manager.ts +44 -169
  245. package/src/runtime/subprocess-tool-registry.ts +4 -1
  246. package/src/runtime/supervisor-contact.ts +10 -5
  247. package/src/runtime/task-display.ts +19 -5
  248. package/src/runtime/task-graph-scheduler.ts +95 -21
  249. package/src/runtime/task-graph.ts +5 -11
  250. package/src/runtime/task-health.ts +54 -47
  251. package/src/runtime/task-id.ts +11 -18
  252. package/src/runtime/task-output-context.ts +265 -81
  253. package/src/runtime/task-packet.ts +12 -20
  254. package/src/runtime/task-quality.ts +6 -14
  255. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  256. package/src/runtime/task-runner/live-executor.ts +114 -36
  257. package/src/runtime/task-runner/output-splitter.ts +152 -0
  258. package/src/runtime/task-runner/progress.ts +50 -12
  259. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  260. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  261. package/src/runtime/task-runner/result-utils.ts +3 -1
  262. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  263. package/src/runtime/task-runner/run-projection.ts +27 -8
  264. package/src/runtime/task-runner/state-helpers.ts +29 -8
  265. package/src/runtime/task-runner/tail-read.ts +2 -6
  266. package/src/runtime/task-runner.ts +180 -326
  267. package/src/runtime/team-runner-artifacts.ts +13 -0
  268. package/src/runtime/team-runner.ts +456 -974
  269. package/src/runtime/tool-output-pruner.ts +6 -9
  270. package/src/runtime/tool-progress.ts +11 -21
  271. package/src/runtime/verification-gates.ts +33 -24
  272. package/src/runtime/verification-integrity.ts +1 -4
  273. package/src/runtime/verification-worktree.ts +62 -12
  274. package/src/runtime/worker-heartbeat.ts +5 -1
  275. package/src/runtime/worker-startup.ts +29 -5
  276. package/src/runtime/workflow-state.ts +17 -9
  277. package/src/runtime/workspace-lock.ts +30 -35
  278. package/src/runtime/workspace-tree.ts +20 -32
  279. package/src/runtime/yield-handler.ts +43 -9
  280. package/src/runtime/zombie-scanner.ts +5 -4
  281. package/src/schema/config-schema.ts +266 -172
  282. package/src/schema/team-tool-schema.ts +29 -74
  283. package/src/schema/validation-types.ts +25 -17
  284. package/src/skills/discover-skills.ts +35 -10
  285. package/src/skills/skill-templates.ts +109 -27
  286. package/src/skills/validate.ts +26 -8
  287. package/src/state/active-run-registry.ts +87 -28
  288. package/src/state/artifact-store.ts +9 -5
  289. package/src/state/atomic-write-v2.ts +85 -63
  290. package/src/state/atomic-write.ts +105 -26
  291. package/src/state/blob-store.ts +47 -20
  292. package/src/state/contracts.ts +20 -5
  293. package/src/state/crew-init.ts +5 -22
  294. package/src/state/decision-ledger.ts +19 -73
  295. package/src/state/event-log-rotation.ts +41 -15
  296. package/src/state/event-log.ts +168 -53
  297. package/src/state/event-reconstructor.ts +11 -1
  298. package/src/state/gitignore-manager.ts +2 -8
  299. package/src/state/health-store.ts +57 -57
  300. package/src/state/hook-instinct-bridge.ts +1 -1
  301. package/src/state/hook-integrations.ts +1 -1
  302. package/src/state/instinct-store.ts +17 -5
  303. package/src/state/jsonl-writer.ts +1 -1
  304. package/src/state/locks.ts +23 -9
  305. package/src/state/mailbox.ts +151 -28
  306. package/src/state/observation-store.ts +20 -14
  307. package/src/state/run-cache.ts +142 -135
  308. package/src/state/run-graph.ts +6 -15
  309. package/src/state/run-metrics.ts +5 -18
  310. package/src/state/schedule.ts +15 -9
  311. package/src/state/state-store.ts +129 -45
  312. package/src/state/task-claims.ts +12 -2
  313. package/src/state/tiered-eval.ts +52 -43
  314. package/src/state/types-eval.ts +2 -2
  315. package/src/state/types.ts +24 -20
  316. package/src/state/usage.ts +20 -4
  317. package/src/state/worker-atomic-writer.ts +6 -3
  318. package/src/subagents/index.ts +2 -2
  319. package/src/teams/discover-teams.ts +21 -6
  320. package/src/tools/safe-bash-extension.ts +5 -6
  321. package/src/tools/safe-bash.ts +10 -11
  322. package/src/types/new-api-types.ts +6 -10
  323. package/src/ui/agent-management-overlay.ts +52 -40
  324. package/src/ui/card-colors.ts +13 -4
  325. package/src/ui/crew-footer.ts +8 -7
  326. package/src/ui/crew-select-list.ts +1 -1
  327. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  328. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  329. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  330. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  331. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  332. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  333. package/src/ui/heartbeat-aggregator.ts +15 -4
  334. package/src/ui/keybinding-map.ts +40 -7
  335. package/src/ui/live-conversation-overlay.ts +36 -20
  336. package/src/ui/live-duration.ts +1 -4
  337. package/src/ui/live-run-sidebar.ts +88 -25
  338. package/src/ui/loaders.ts +2 -8
  339. package/src/ui/mascot.ts +20 -36
  340. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  341. package/src/ui/overlays/confirm-overlay.ts +4 -2
  342. package/src/ui/overlays/help-overlay.ts +25 -14
  343. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  344. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  345. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  346. package/src/ui/pi-ui-compat.ts +7 -7
  347. package/src/ui/powerbar-publisher.ts +120 -42
  348. package/src/ui/render-diff.ts +10 -3
  349. package/src/ui/render-scheduler.ts +12 -4
  350. package/src/ui/run-action-dispatcher.ts +81 -18
  351. package/src/ui/run-dashboard.ts +172 -76
  352. package/src/ui/run-event-bus.ts +59 -37
  353. package/src/ui/run-snapshot-cache.ts +276 -86
  354. package/src/ui/settings-overlay.ts +361 -74
  355. package/src/ui/status-colors.ts +12 -1
  356. package/src/ui/syntax-highlight.ts +1 -1
  357. package/src/ui/theme-adapter.ts +16 -14
  358. package/src/ui/theme-discovery.ts +13 -2
  359. package/src/ui/tool-progress-formatter.ts +7 -7
  360. package/src/ui/tool-render.ts +128 -56
  361. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  362. package/src/ui/tool-renderers/index.ts +109 -43
  363. package/src/ui/transcript-cache.ts +32 -6
  364. package/src/ui/transcript-entries.ts +25 -23
  365. package/src/ui/transcript-viewer.ts +78 -29
  366. package/src/ui/widget/index.ts +99 -40
  367. package/src/ui/widget/widget-formatters.ts +13 -6
  368. package/src/ui/widget/widget-model.ts +19 -8
  369. package/src/ui/widget/widget-renderer.ts +23 -13
  370. package/src/ui/widget/widget-types.ts +1 -1
  371. package/src/utils/bm25-search.ts +199 -199
  372. package/src/utils/conflict-detect.ts +22 -21
  373. package/src/utils/env-filter.ts +14 -7
  374. package/src/utils/file-coalescer.ts +5 -1
  375. package/src/utils/fingerprint.ts +3 -6
  376. package/src/utils/frontmatter.ts +3 -1
  377. package/src/utils/fs-watch.ts +2 -6
  378. package/src/utils/gh-protocol.ts +119 -44
  379. package/src/utils/git.ts +13 -15
  380. package/src/utils/guards.ts +2 -5
  381. package/src/utils/ids.ts +9 -2
  382. package/src/utils/incremental-reader.ts +14 -3
  383. package/src/utils/internal-error.ts +2 -1
  384. package/src/utils/names.ts +12 -3
  385. package/src/utils/paths.ts +49 -5
  386. package/src/utils/project-detector.ts +2 -2
  387. package/src/utils/redaction.ts +29 -19
  388. package/src/utils/resolve-shell.ts +9 -7
  389. package/src/utils/run-watcher-registry.ts +19 -31
  390. package/src/utils/safe-paths.ts +45 -24
  391. package/src/utils/scan-cache.ts +9 -2
  392. package/src/utils/session-utils.ts +2 -4
  393. package/src/utils/sleep.ts +12 -5
  394. package/src/utils/sse-parser.ts +5 -17
  395. package/src/utils/visual.ts +52 -32
  396. package/src/workflows/discover-workflows.ts +41 -109
  397. package/src/workflows/intermediate-store.ts +5 -21
  398. package/src/workflows/preflight-validator.ts +6 -33
  399. package/src/workflows/topology-analyzer.ts +5 -21
  400. package/src/workflows/workflow-config.ts +7 -6
  401. package/src/worktree/branch-freshness.ts +66 -8
  402. package/src/worktree/cleanup.ts +130 -20
  403. package/src/worktree/worktree-manager.ts +212 -69
  404. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  405. package/skills/detection-pipeline-design/SKILL.md +0 -286
  406. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  407. package/skills/incident-playbook-construction/SKILL.md +0 -384
  408. package/skills/security-review/SKILL.md +0 -561
  409. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  410. package/skills/ui-render-performance/SKILL.md +0 -58
  411. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  412. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  413. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  414. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  415. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  416. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  417. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  418. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  419. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  420. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  421. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  422. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  430. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  431. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  432. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -21,7 +21,12 @@ export interface RetryHooks {
21
21
  signal?: AbortSignal;
22
22
  }
23
23
 
24
- export const DEFAULT_RETRY_POLICY: RetryPolicy = { maxAttempts: 3, backoffMs: 1000, jitterRatio: 0.3, exponentialFactor: 2 };
24
+ export const DEFAULT_RETRY_POLICY: RetryPolicy = {
25
+ maxAttempts: 3,
26
+ backoffMs: 1000,
27
+ jitterRatio: 0.3,
28
+ exponentialFactor: 2,
29
+ };
25
30
 
26
31
  function asError(error: unknown): Error {
27
32
  return error instanceof Error ? error : new Error(String(error));
@@ -45,11 +50,22 @@ export function calculateRetryDelay(attempt: number, policy: RetryPolicy = DEFAU
45
50
  }
46
51
 
47
52
  function retryAttemptInfo(attempt: number, hooks: RetryHooks): RetryAttemptInfo {
48
- return { attempt, attemptId: hooks.attemptId?.(attempt) ?? `retry_attempt_${attempt}` };
53
+ return {
54
+ attempt,
55
+ attemptId: hooks.attemptId?.(attempt) ?? `retry_attempt_${attempt}`,
56
+ };
49
57
  }
50
58
 
51
- export async function executeWithRetry<T>(fn: (attempt: number, info: RetryAttemptInfo) => Promise<T>, policy: RetryPolicy = DEFAULT_RETRY_POLICY, hooks: RetryHooks = {}): Promise<T> {
52
- const normalized: RetryPolicy = { ...DEFAULT_RETRY_POLICY, ...policy, maxAttempts: Math.max(1, policy.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts) };
59
+ export async function executeWithRetry<T>(
60
+ fn: (attempt: number, info: RetryAttemptInfo) => Promise<T>,
61
+ policy: RetryPolicy = DEFAULT_RETRY_POLICY,
62
+ hooks: RetryHooks = {},
63
+ ): Promise<T> {
64
+ const normalized: RetryPolicy = {
65
+ ...DEFAULT_RETRY_POLICY,
66
+ ...policy,
67
+ maxAttempts: Math.max(1, policy.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts),
68
+ };
53
69
  let lastError: Error | undefined;
54
70
  for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
55
71
  throwIfCancelled(hooks.signal);
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * RetryRunner - Execute tasks with retry support and summary accumulation.
3
- *
3
+ *
4
4
  * Based on pi-boomerang's --rethrow pattern:
5
5
  * - Retries failed tasks up to maxAttempts
6
6
  * - Generates handoffs between attempts
7
7
  * - Accumulates context from previous attempts
8
8
  * - Supports exponential backoff
9
9
  * - Limits handoff accumulation to prevent memory leaks
10
- *
10
+ *
11
11
  * @see docs/pi-boomerang-integration-plan.md
12
12
  */
13
13
 
14
- import type { HandoffSummary, HandoffManager, TaskPacket, TaskResult } from "./handoff-manager.ts";
14
+ import type { HandoffManager, HandoffSummary, TaskPacket, TaskResult } from "./handoff-manager.ts";
15
15
 
16
16
  /**
17
17
  * Retry configuration.
@@ -75,10 +75,7 @@ export class RetryRunner {
75
75
  private taskRunner: TaskRunnerLike;
76
76
  private handoffManager: HandoffManager;
77
77
 
78
- constructor(
79
- taskRunner: TaskRunnerLike,
80
- handoffManager: HandoffManager,
81
- ) {
78
+ constructor(taskRunner: TaskRunnerLike, handoffManager: HandoffManager) {
82
79
  this.taskRunner = taskRunner;
83
80
  this.handoffManager = handoffManager;
84
81
  }
@@ -127,15 +124,12 @@ export class RetryRunner {
127
124
  /**
128
125
  * Execute task with retry support.
129
126
  * Summaries accumulate between attempts for better context.
130
- *
127
+ *
131
128
  * @param packet - Task packet to execute
132
129
  * @param config - Retry configuration (stopOnSuccess defaults to true)
133
130
  * @returns Final retry result with all attempts
134
131
  */
135
- async runWithRetry(
136
- packet: TaskPacket,
137
- config: RetryConfig
138
- ): Promise<RetryResult> {
132
+ async runWithRetry(packet: TaskPacket, config: RetryConfig): Promise<RetryResult> {
139
133
  if (this._disposed) {
140
134
  throw new Error("RetryRunner has been disposed");
141
135
  }
@@ -148,7 +142,6 @@ export class RetryRunner {
148
142
  // Clear previous handoffs at start of each retry run
149
143
  this._handoffs = [];
150
144
 
151
-
152
145
  for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
153
146
  if (this._disposed) {
154
147
  throw new Error("RetryRunner was disposed during retry");
@@ -157,11 +150,7 @@ export class RetryRunner {
157
150
 
158
151
  try {
159
152
  // Inject accumulated handoffs into context
160
- const enrichedPacket = this.enrichPacketWithHandoffs(
161
- packet,
162
- handoffs,
163
- attempt
164
- );
153
+ const enrichedPacket = this.enrichPacketWithHandoffs(packet, handoffs, attempt);
165
154
 
166
155
  // Execute task
167
156
  const result = await this.taskRunner.runTask(enrichedPacket);
@@ -175,10 +164,7 @@ export class RetryRunner {
175
164
 
176
165
  // Generate summary between attempts
177
166
  if (config.summaryBetweenAttempts !== false) {
178
- const summary = await this.handoffManager.generateSummary(
179
- packet,
180
- result
181
- );
167
+ const summary = await this.handoffManager.generateSummary(packet, result);
182
168
  attemptResult.summary = summary;
183
169
  handoffs.push(summary);
184
170
 
@@ -213,13 +199,12 @@ export class RetryRunner {
213
199
  config.backoffMs ?? 1000,
214
200
  attempt,
215
201
  config.backoffMultiplier ?? 1,
216
- config.maxBackoffMs
202
+ config.maxBackoffMs,
217
203
  );
218
204
  if (backoffDelay > 0) {
219
205
  await this.sleep(backoffDelay);
220
206
  }
221
207
  }
222
-
223
208
  } catch (error) {
224
209
  attempts.push({
225
210
  attempt,
@@ -238,7 +223,7 @@ export class RetryRunner {
238
223
  config.backoffMs,
239
224
  attempt,
240
225
  config.backoffMultiplier ?? 1,
241
- config.maxBackoffMs
226
+ config.maxBackoffMs,
242
227
  );
243
228
  if (backoffDelay > 0) {
244
229
  await this.sleep(backoffDelay);
@@ -260,24 +245,23 @@ export class RetryRunner {
260
245
  /**
261
246
  * Enrich packet with accumulated handoffs from previous attempts.
262
247
  */
263
- private enrichPacketWithHandoffs(
264
- packet: TaskPacket,
265
- handoffs: HandoffSummary[],
266
- attempt: number
267
- ): TaskPacket {
248
+ private enrichPacketWithHandoffs(packet: TaskPacket, handoffs: HandoffSummary[], attempt: number): TaskPacket {
268
249
  if (handoffs.length === 0) {
269
250
  return packet;
270
251
  }
271
252
 
272
253
  // Build accumulated context from previous attempts
273
- const accumulatedContext = handoffs.map((h, index) =>
274
- `## Attempt ${index + 1}: ${h.task}\n` +
275
- `Outcome: ${h.outcome}\n` +
276
- `Files: created=${h.filesCreated.join(", ")}, modified=${h.filesModified.join(", ")}\n` +
277
- `Decisions: ${h.decisions.map(d => d.rationale).join("; ")}\n` +
278
- `Blockers: ${h.blockers.join(", ")}\n` +
279
- `Next Steps: ${h.nextSteps.join(", ")}\n`
280
- ).join("\n---\n");
254
+ const accumulatedContext = handoffs
255
+ .map(
256
+ (h, index) =>
257
+ `## Attempt ${index + 1}: ${h.task}\n` +
258
+ `Outcome: ${h.outcome}\n` +
259
+ `Files: created=${h.filesCreated.join(", ")}, modified=${h.filesModified.join(", ")}\n` +
260
+ `Decisions: ${h.decisions.map((d) => d.rationale).join("; ")}\n` +
261
+ `Blockers: ${h.blockers.join(", ")}\n` +
262
+ `Next Steps: ${h.nextSteps.join(", ")}\n`,
263
+ )
264
+ .join("\n---\n");
281
265
 
282
266
  return {
283
267
  ...packet,
@@ -293,12 +277,7 @@ export class RetryRunner {
293
277
  /**
294
278
  * Calculate backoff delay with optional capping.
295
279
  */
296
- private calculateBackoff(
297
- baseMs: number,
298
- attempt: number,
299
- multiplier: number,
300
- maxBackoffMs?: number
301
- ): number {
280
+ private calculateBackoff(baseMs: number, attempt: number, multiplier: number, maxBackoffMs?: number): number {
302
281
  let delay = baseMs * Math.pow(multiplier, attempt - 1);
303
282
  if (maxBackoffMs !== undefined) {
304
283
  delay = Math.min(delay, maxBackoffMs);
@@ -310,17 +289,14 @@ export class RetryRunner {
310
289
  * Sleep helper.
311
290
  */
312
291
  private sleep(ms: number): Promise<void> {
313
- return new Promise(resolve => setTimeout(resolve, ms));
292
+ return new Promise((resolve) => setTimeout(resolve, ms));
314
293
  }
315
294
  }
316
295
 
317
296
  /**
318
297
  * Create a RetryRunner with default dependencies.
319
298
  */
320
- export function createRetryRunner(
321
- taskRunner: TaskRunnerLike,
322
- handoffManager: HandoffManager
323
- ): RetryRunner {
299
+ export function createRetryRunner(taskRunner: TaskRunnerLike, handoffManager: HandoffManager): RetryRunner {
324
300
  return new RetryRunner(taskRunner, handoffManager);
325
301
  }
326
302
 
@@ -357,4 +333,4 @@ export const PERSISTENT_FAILURE_RETRY_CONFIG: RetryConfig = {
357
333
  backoffMs: 2000,
358
334
  backoffMultiplier: 2,
359
335
  maxBackoffMs: 60000,
360
- };
336
+ };
@@ -24,6 +24,11 @@ export function currentCrewRole(env: NodeJS.ProcessEnv = process.env): string |
24
24
  export function checkSubagentSpawnPermission(role: string | undefined): PermissionCheckResult {
25
25
  if (!role) return { allowed: true, mode: "workspace_write" };
26
26
  const mode = permissionForRole(role);
27
- if (mode === "read_only") return { allowed: false, mode, reason: `Role '${role}' is read-only and cannot spawn additional subagents.` };
27
+ if (mode === "read_only")
28
+ return {
29
+ allowed: false,
30
+ mode,
31
+ reason: `Role '${role}' is read-only and cannot spawn additional subagents.`,
32
+ };
28
33
  return { allowed: true, mode };
29
34
  }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * run-coalesced-task-group.ts — M6 real-dispatch MVP worker spawn.
3
+ *
4
+ * Spawns ONE child Pi process for an entire coalesced group of N tasks
5
+ * (sharing role + cwd per `planCoalescedGroups`). The combined prompt
6
+ * instructs the worker to wrap each task's result in `<<<TASK_RESULT:id>>>`
7
+ * ... `<<<END_TASK_RESULT>>>` delimiters. Output is split back into N
8
+ * per-task results via `splitCoalescedOutput`, each written to its own
9
+ * result artifact + state update.
10
+ */
11
+
12
+ import { writeFile } from "node:fs/promises";
13
+ import path from "node:path";
14
+ import { permissionForRole } from "./role-permission.ts";
15
+ import { runChildPi } from "./child-pi.ts";
16
+ import { splitCoalescedOutput } from "./task-runner/output-splitter.ts";
17
+ import { buildWorkspaceTree } from "./workspace-tree.ts";
18
+ import { saveRunTasks, updateRunStatus } from "../state/state-store.ts";
19
+ import { writeArtifact } from "../state/artifact-store.ts";
20
+ import { appendEventAsync } from "../state/event-log.ts";
21
+ import { createWorkerHeartbeat, touchWorkerHeartbeat } from "./worker-heartbeat.ts";
22
+ import type { AgentConfig } from "../agents/agent-config.ts";
23
+ import type { CrewRuntimeMode } from "./runtime-resolver.ts";
24
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
25
+ import type { WorkflowStep } from "../workflows/workflow-config.ts";
26
+ import { mergeArtifacts } from "./team-runner-artifacts.ts";
27
+
28
+ export interface CoalescedTaskGroupInput {
29
+ manifest: TeamRunManifest;
30
+ tasks: TeamTaskState[];
31
+ groupTasks: TeamTaskState[];
32
+ step: WorkflowStep;
33
+ agent: AgentConfig;
34
+ signal?: AbortSignal;
35
+ executeWorkers: boolean;
36
+ runtimeKind?: CrewRuntimeMode;
37
+ workspaceId: string;
38
+ onJsonEvent?: (taskId: string, runId: string, event: unknown) => void;
39
+ teamRole?: unknown;
40
+ perTaskRuntime?: CrewRuntimeMode;
41
+ }
42
+
43
+ export interface CoalescedTaskGroupResult {
44
+ manifest: TeamRunManifest;
45
+ tasks: TeamTaskState[];
46
+ taskIds: string[];
47
+ rawOutput: string;
48
+ success: boolean;
49
+ }
50
+
51
+ export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Promise<CoalescedTaskGroupResult> {
52
+ const { manifest, groupTasks, step, agent, signal, executeWorkers } = input;
53
+ const groupId = groupTasks.map((t) => t.id).join("+");
54
+ const firstTask = groupTasks[0]!;
55
+ const taskIds = groupTasks.map((t) => t.id);
56
+
57
+ // Set ALL N tasks to "running" before spawn so the dashboard reflects
58
+ // the in-flight state.
59
+ let updatedTasks: TeamTaskState[] = input.tasks.map((t) => {
60
+ if (taskIds.includes(t.id) && t.status !== "running") {
61
+ return { ...t, status: "running" as const, startedAt: new Date().toISOString() };
62
+ }
63
+ return t;
64
+ });
65
+ saveRunTasks(manifest, updatedTasks);
66
+ await appendEventAsync(manifest.eventsPath, {
67
+ type: "task.coalesced_dispatch_start",
68
+ runId: manifest.runId,
69
+ message: `Dispatching ${groupTasks.length} coalesced tasks in 1 worker (role=${firstTask.role}, cwd=${firstTask.cwd})`,
70
+ data: { groupId, role: firstTask.role, cwd: firstTask.cwd, taskIds },
71
+ });
72
+
73
+ const combinedPrompt = await buildCoalescedPrompt(manifest, step, groupTasks, agent);
74
+
75
+ // FIX (M6): write heartbeats for ALL N tasks in the coalesced group so the
76
+ // background watcher doesn't fire heartbeat_dead against the (single)
77
+ // child pi worker. Previously runCoalescedTaskGroup never wrote heartbeats,
78
+ // so the watcher saw `heartbeat.lastSeenAt: undefined` within 2 seconds of
79
+ // spawn and emitted heartbeat_dead even though the worker continued to
80
+ // completion (false-positive stuck-worker alarms). The singleton path
81
+ // (runTeamTask) writes heartbeats via persistHeartbeat — M6 needs the
82
+ // equivalent to avoid the false-positive.
83
+ updatedTasks = updatedTasks.map((t) => {
84
+ if (!taskIds.includes(t.id)) return t;
85
+ return {
86
+ ...t,
87
+ heartbeat: t.heartbeat ?? createWorkerHeartbeat(t.id),
88
+ };
89
+ });
90
+ saveRunTasks(manifest, updatedTasks);
91
+
92
+ let rawOutput = "";
93
+ let success = false;
94
+ if (!executeWorkers) {
95
+ rawOutput = buildScaffoldOutput(groupTasks);
96
+ success = true;
97
+ } else {
98
+ // Heartbeat refresher: touch every task's heartbeat every 15s while the
99
+ // worker is alive. Set `alive: true` explicitly so post-completion
100
+ // staleness checks immediately recognize liveness.
101
+ const heartbeatTimer = setInterval(() => {
102
+ const now = new Date().toISOString();
103
+ updatedTasks = updatedTasks.map((t) => {
104
+ if (!taskIds.includes(t.id)) return t;
105
+ return {
106
+ ...t,
107
+ heartbeat: touchWorkerHeartbeat(t.heartbeat ?? createWorkerHeartbeat(t.id), { alive: true }),
108
+ };
109
+ });
110
+ try {
111
+ saveRunTasks(manifest, updatedTasks);
112
+ } catch {
113
+ // Run may have been pruned mid-dispatch — best-effort only.
114
+ }
115
+ }, 15_000);
116
+ try {
117
+ const result = await runChildPi({
118
+ cwd: firstTask.cwd,
119
+ task: combinedPrompt,
120
+ agent,
121
+ signal,
122
+ excludeContextBash: true,
123
+ maxTurns: 5,
124
+ onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e),
125
+ });
126
+ rawOutput = result.rawFinalText ?? result.stdout ?? "";
127
+ success = result.exitStatus?.exitCode === 0;
128
+ } catch (err) {
129
+ rawOutput = `Worker dispatch failed: ${err instanceof Error ? err.message : String(err)}`;
130
+ success = false;
131
+ } finally {
132
+ clearInterval(heartbeatTimer);
133
+ }
134
+ }
135
+
136
+ const split = splitCoalescedOutput(rawOutput, taskIds);
137
+
138
+ const finishedAt = new Date().toISOString();
139
+ const newArtifacts: TeamRunManifest["artifacts"] = [];
140
+ updatedTasks = updatedTasks.map((t) => {
141
+ if (!taskIds.includes(t.id)) return t;
142
+ const entry = split.find((s) => s.taskId === t.id);
143
+ const ok = success && Boolean(entry?.text);
144
+ const text = entry?.text ?? rawOutput;
145
+ // BUGFIX (M6 real dispatch): write to artifactsRoot via writeArtifact
146
+ // so task.resultArtifact is set and aggregateTaskOutputs can read
147
+ // the per-task text. Previously the coalesced path used a raw
148
+ // writeFile to stateRoot/results/<id>.txt which aggregated task
149
+ // outputs could not locate — they only consult task.resultArtifact.
150
+ // Result: tasks reported "EMPTY OUTPUT" in the batch summary even
151
+ // though on-disk results were correct.
152
+ const resultArtifact = writeArtifact(manifest.artifactsRoot, {
153
+ kind: "result",
154
+ relativePath: `results/${t.id}.txt`,
155
+ content: text,
156
+ producer: t.id,
157
+ });
158
+ newArtifacts.push(resultArtifact);
159
+ return {
160
+ ...t,
161
+ status: ok ? ("completed" as const) : ("failed" as const),
162
+ finishedAt,
163
+ result: {
164
+ text,
165
+ producer: groupId,
166
+ strategy: entry?.strategy ?? "broadcast",
167
+ },
168
+ resultArtifact,
169
+ };
170
+ });
171
+ saveRunTasks(manifest, updatedTasks);
172
+ let updatedManifest: TeamRunManifest = {
173
+ ...manifest,
174
+ artifacts: mergeArtifacts([...manifest.artifacts, ...newArtifacts]),
175
+ };
176
+
177
+ if (success) {
178
+ updatedManifest = updateRunStatus(updatedManifest, "running");
179
+ }
180
+ await appendEventAsync(updatedManifest.eventsPath, {
181
+ type: "task.coalesced_dispatch_end",
182
+ runId: manifest.runId,
183
+ message: `Coalesced dispatch ${success ? "completed" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
184
+ data: { groupId, taskIds, success, strategy: split[0]?.strategy },
185
+ });
186
+
187
+ return { manifest: updatedManifest, tasks: updatedTasks, taskIds, rawOutput, success };
188
+ }
189
+
190
+ async function buildCoalescedPrompt(
191
+ manifest: TeamRunManifest,
192
+ step: WorkflowStep,
193
+ groupTasks: TeamTaskState[],
194
+ agent: AgentConfig,
195
+ ): Promise<string> {
196
+ const tree = await buildWorkspaceTree(groupTasks[0]!.cwd);
197
+ const treeBlock = tree.rendered ? `# Workspace Structure\n${tree.rendered}` : "";
198
+ const roleInstructions =
199
+ permissionForRole(groupTasks[0]!.role) === "read_only"
200
+ ? `You are running in READ-ONLY mode. Do not create, modify, delete, or move files. Emit your findings as TEXT in your final output.`
201
+ : "";
202
+
203
+ const taskBlocks = groupTasks
204
+ .map((task, idx) => {
205
+ return [
206
+ `### Task ${idx + 1} of ${groupTasks.length} (id: ${task.id})`,
207
+ `Step: ${step.id}`,
208
+ `Role: ${step.role}`,
209
+ `Task: ${step.task.replaceAll("{goal}", manifest.goal)}`,
210
+ ].join("\n");
211
+ })
212
+ .join("\n\n---\n\n");
213
+
214
+ const outputInstructions = groupTasks.map((task) => `<<<TASK_RESULT:${task.id}>>>`).join(" ... ");
215
+
216
+ return [
217
+ "# pi-crew Coalesced Worker Prompt",
218
+ `Run ID: ${manifest.runId}`,
219
+ `Team: ${manifest.team}`,
220
+ `Workflow: ${manifest.workflow ?? "(none)"}`,
221
+ `Goal: ${manifest.goal}`,
222
+ `Tasks in this batch: ${groupTasks.length}`,
223
+ ``,
224
+ roleInstructions,
225
+ ``,
226
+ treeBlock,
227
+ ``,
228
+ `# Your Tasks`,
229
+ `Complete ALL ${groupTasks.length} tasks below. For each, structure your final output using the delimiters shown.`,
230
+ ``,
231
+ taskBlocks,
232
+ ``,
233
+ `# Output Format (CRITICAL)`,
234
+ `After completing all tasks, structure your final output using these delimiters:`,
235
+ ``,
236
+ outputInstructions,
237
+ ``,
238
+ `Wrap each task's result between the start and end delimiters:`,
239
+ `<<<TASK_RESULT:{taskId}>>>`,
240
+ `...your result for this task...`,
241
+ `<<<END_TASK_RESULT>>>`,
242
+ ``,
243
+ `If delimiters don't fit your workflow, use \`### Task N of M\` headings and we'll parse those instead.`,
244
+ ]
245
+ .filter(Boolean)
246
+ .join("\n");
247
+ }
248
+
249
+ function buildScaffoldOutput(groupTasks: TeamTaskState[]): string {
250
+ return groupTasks
251
+ .map(
252
+ (task, idx) =>
253
+ `<<<TASK_RESULT:${task.id}>>>\nScaffold result for task ${idx + 1} of ${groupTasks.length}: ${task.id}\n<<<END_TASK_RESULT>>>`,
254
+ )
255
+ .join("\n\n");
256
+ }
@@ -6,19 +6,19 @@
6
6
  * Repair handlers are idempotent — safe to run multiple times.
7
7
  */
8
8
 
9
- import { existsSync, statSync, readdirSync, readFileSync } from "node:fs";
9
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
10
10
  import path from "node:path";
11
11
  import { logInternalError } from "../utils/internal-error.ts";
12
12
 
13
13
  // ── Types ────────────────────────────────────────────────────────────────
14
14
 
15
15
  export type DriftKind =
16
- | "stale-process" // Heartbeat timeout (existing)
17
- | "orphaned-claim" // Task claim without task
18
- | "orphaned-worktree" // Worktree dir without active run
19
- | "missing-timestamps" // State files without timestamps
20
- | "status-divergence" // Manifest status ≠ status file
21
- | "unregistered-run"; // State dir but no manifest
16
+ | "stale-process" // Heartbeat timeout (existing)
17
+ | "orphaned-claim" // Task claim without task
18
+ | "orphaned-worktree" // Worktree dir without active run
19
+ | "missing-timestamps" // State files without timestamps
20
+ | "status-divergence" // Manifest status ≠ status file
21
+ | "unregistered-run"; // State dir but no manifest
22
22
 
23
23
  export interface DriftReport {
24
24
  kind: DriftKind;
@@ -34,7 +34,12 @@ export interface DriftContext {
34
34
  /** Active run IDs (from registry) */
35
35
  activeRunIds: Set<string>;
36
36
  /** Manifest content if available */
37
- manifest?: { runId: string; status: string; cwd: string; [k: string]: unknown };
37
+ manifest?: {
38
+ runId: string;
39
+ status: string;
40
+ cwd: string;
41
+ [k: string]: unknown;
42
+ };
38
43
  }
39
44
 
40
45
  // ── Detectors ────────────────────────────────────────────────────────────
@@ -180,13 +185,7 @@ export function detectUnregisteredRun(ctx: DriftContext): DriftReport | null {
180
185
 
181
186
  // ── Reconciliation Loop ─────────────────────────────────────────────────
182
187
 
183
- const ALL_DETECTORS = [
184
- detectOrphanedClaim,
185
- detectOrphanedWorktree,
186
- detectMissingTimestamps,
187
- detectStatusDivergence,
188
- detectUnregisteredRun,
189
- ];
188
+ const ALL_DETECTORS = [detectOrphanedClaim, detectOrphanedWorktree, detectMissingTimestamps, detectStatusDivergence, detectUnregisteredRun];
190
189
 
191
190
  /**
192
191
  * Run all drift detectors and collect reports.
@@ -7,20 +7,14 @@ import { isFinishedRunStatus } from "./process-status.ts";
7
7
 
8
8
  export interface ActiveRunPromise {
9
9
  promise: Promise<{ manifest: TeamRunManifest; tasks: TeamTaskState[] }>;
10
- resolve: (value: {
11
- manifest: TeamRunManifest;
12
- tasks: TeamTaskState[];
13
- }) => void;
10
+ resolve: (value: { manifest: TeamRunManifest; tasks: TeamTaskState[] }) => void;
14
11
  reject: (reason: unknown) => void;
15
12
  }
16
13
 
17
14
  const activeRunPromises = new Map<string, ActiveRunPromise>();
18
15
 
19
16
  export function registerRunPromise(runId: string): ActiveRunPromise {
20
- let resolve!: (value: {
21
- manifest: TeamRunManifest;
22
- tasks: TeamTaskState[];
23
- }) => void;
17
+ let resolve!: (value: { manifest: TeamRunManifest; tasks: TeamTaskState[] }) => void;
24
18
  let reject!: (reason: unknown) => void;
25
19
  const promise = new Promise<{
26
20
  manifest: TeamRunManifest;
@@ -34,10 +28,7 @@ export function registerRunPromise(runId: string): ActiveRunPromise {
34
28
  return entry;
35
29
  }
36
30
 
37
- export function resolveRunPromise(
38
- runId: string,
39
- result: { manifest: TeamRunManifest; tasks: TeamTaskState[] },
40
- ): void {
31
+ export function resolveRunPromise(runId: string, result: { manifest: TeamRunManifest; tasks: TeamTaskState[] }): void {
41
32
  const entry = activeRunPromises.get(runId);
42
33
  if (entry) {
43
34
  entry.resolve(result);
@@ -78,13 +69,7 @@ export async function waitForRun(
78
69
  if (entry) {
79
70
  let timer: ReturnType<typeof setTimeout> | undefined;
80
71
  const timeoutPromise = new Promise<never>((_, reject) => {
81
- timer = setTimeout(
82
- () =>
83
- reject(
84
- new Error(`waitForRun timed out after ${timeoutMs}ms`),
85
- ),
86
- timeoutMs,
87
- );
72
+ timer = setTimeout(() => reject(new Error(`waitForRun timed out after ${timeoutMs}ms`)), timeoutMs);
88
73
  });
89
74
  try {
90
75
  return await Promise.race([entry.promise, timeoutPromise]);
@@ -102,16 +87,9 @@ export async function waitForRun(
102
87
  // projects (see issue #29). Without this, the hardcoded `.crew/state/runs/`
103
88
  // path never resolves in projects that use the `.pi/` layout, the throw
104
89
  // escapes via subagent-manager.ts:281, and pi crashes with uncaughtException.
105
- const runDir = path.join(
106
- projectCrewRoot(cwd),
107
- "state",
108
- "runs",
109
- runId,
110
- );
90
+ const runDir = path.join(projectCrewRoot(cwd), "state", "runs", runId);
111
91
  if (!fs.existsSync(runDir)) {
112
- throw new Error(
113
- `Run ${runId} not found. No run directory at ${runDir}`,
114
- );
92
+ throw new Error(`Run ${runId} not found. No run directory at ${runDir}`);
115
93
  }
116
94
  }
117
95
  const fresh = loadRunManifestById(cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
@@ -22,12 +22,7 @@ export function resolveTaskRuntimeKind(
22
22
  // Exception: when PI_CREW_MOCK_LIVE_SESSION is set, we're in a test harness
23
23
  // that mocks the live-session path — forcing child-process would spawn a real
24
24
  // pi process and hang the test.
25
- if (
26
- globalKind === "live-session" &&
27
- currentCrewDepth(env) > 0 &&
28
- env.PI_CREW_MOCK_LIVE_SESSION !== "success"
29
- )
30
- return "child-process";
25
+ if (globalKind === "live-session" && currentCrewDepth(env) > 0 && env.PI_CREW_MOCK_LIVE_SESSION !== "success") return "child-process";
31
26
  const isolatedRoles = isolationPolicy?.isolatedRoles ?? [];
32
27
  if (isolatedRoles.includes(role)) return "child-process";
33
28
  return isolationPolicy?.defaultRuntime ?? globalKind;