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
@@ -4,10 +4,18 @@ import * as path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import type { AgentConfig } from "../agents/agent-config.ts";
6
6
  import { resolveToolPolicy } from "../agents/agent-config.ts";
7
- import { userPiRoot } from "../utils/paths.ts";
7
+ import { packageRoot, userPiRoot } from "../utils/paths.ts";
8
8
 
9
9
  const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
10
- const PROMPT_RUNTIME_EXTENSION_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "prompt", "prompt-runtime.ts");
10
+ // FIX (2026-07-02): use packageRoot() instead of import.meta.url-relative path.
11
+ // The previous path.resolve(path.dirname(fileURLToPath(import.meta.url)),
12
+ // "..", "prompt", "prompt-runtime.ts") was correct in source but BROKEN
13
+ // in the bundle: esbuild's __esm helper doesn't preserve per-module
14
+ // import.meta.url, so the resolve lands at <pi-crew>/prompt/... (missing
15
+ // `src/`) instead of <pi-crew>/src/prompt/... packageRoot() walks up to
16
+ // find pi-crew's package.json and works correctly from both src/ and
17
+ // dist/ entry points.
18
+ const PROMPT_RUNTIME_EXTENSION_PATH = path.join(packageRoot(), "src", "prompt", "prompt-runtime.ts");
11
19
  const TASK_ARG_LIMIT = 8000;
12
20
  const DEFAULT_MAX_CREW_DEPTH = 2;
13
21
 
@@ -80,7 +88,10 @@ export function resolveCrewMaxDepth(inputMaxDepth?: number, env: NodeJS.ProcessE
80
88
  return DEFAULT_MAX_CREW_DEPTH;
81
89
  }
82
90
 
83
- export function checkCrewDepth(inputMaxDepth?: number, env: NodeJS.ProcessEnv = process.env): { blocked: boolean; depth: number; maxDepth: number } {
91
+ export function checkCrewDepth(
92
+ inputMaxDepth?: number,
93
+ env: NodeJS.ProcessEnv = process.env,
94
+ ): { blocked: boolean; depth: number; maxDepth: number } {
84
95
  const depth = currentCrewDepth(env);
85
96
  const maxDepth = resolveCrewMaxDepth(inputMaxDepth, env);
86
97
  return { depth, maxDepth, blocked: depth >= maxDepth };
@@ -218,7 +229,8 @@ export function createSafeTempDir(base: string, prefix: string): string {
218
229
  resolvedAccumulated = path.join(resolvedAccumulated, resolvedParts[i]);
219
230
  try {
220
231
  const stat = fs.lstatSync(resolvedAccumulated);
221
- if (stat.isSymbolicLink()) throw new Error("Refusing to create temp dir: resolved path contains symlink ancestor: " + resolvedAccumulated);
232
+ if (stat.isSymbolicLink())
233
+ throw new Error("Refusing to create temp dir: resolved path contains symlink ancestor: " + resolvedAccumulated);
222
234
  } catch (e) {
223
235
  if (e instanceof Error && e.message.includes("symlink")) throw e;
224
236
  // Component doesn't exist — OK
@@ -334,9 +346,9 @@ export function buildPiWorkerArgs(input: BuildPiWorkerArgsInput): BuildPiWorkerA
334
346
  args,
335
347
  env: {
336
348
  // PI_CREW_KIND is the authoritative machine-readable sub-agent marker. It is always
337
- // present on a child-pi process and NEVER present on a user's interactive main session.
349
+ // present on a child-pi process and NEVER present on a user's interactive main session.
338
350
  // doctor --zombies uses it to safely list orphaned sub-agents without ever matching a
339
- // main session (the lesson from an accidental `kill` of a live main session).
351
+ // main session (the lesson from an accidental `kill` of a live main session).
340
352
  PI_CREW_KIND: "subagent",
341
353
  PI_CREW_INHERIT_PROJECT_CONTEXT: input.agent.inheritProjectContext ? "1" : "0",
342
354
  PI_CREW_INHERIT_SKILLS: input.agent.inheritSkills ? "1" : "0",
@@ -385,7 +397,10 @@ export function cleanupTempDir(tempDir: string | undefined): void {
385
397
  * /tmp/pi-crew-* dirs when individual cleanupTempDir calls are missed
386
398
  * (e.g. parent process killed before child-pi.ts settles).
387
399
  */
388
- export function cleanupAllTrackedTempDirs(): { cleaned: number; failed: number } {
400
+ export function cleanupAllTrackedTempDirs(): {
401
+ cleaned: number;
402
+ failed: number;
403
+ } {
389
404
  let cleaned = 0;
390
405
  let failed = 0;
391
406
  // Snapshot to avoid mutation during iteration
@@ -441,7 +456,10 @@ export function __test_getTrackedTempDirs(): readonly string[] {
441
456
  * Called at startup as a belt-and-suspenders measure alongside the
442
457
  * periodic cleanupOrphanTempDirs.
443
458
  */
444
- export function purgeStaleTrackedTempDirs(): { removed: number; remaining: number } {
459
+ export function purgeStaleTrackedTempDirs(): {
460
+ removed: number;
461
+ remaining: number;
462
+ } {
445
463
  let removed = 0;
446
464
  for (const dir of [...createdTempDirs]) {
447
465
  if (!fs.existsSync(dir)) {
@@ -21,7 +21,7 @@ export interface ParsedPiJsonOutput {
21
21
  }
22
22
 
23
23
  function asRecord(value: unknown): Record<string, unknown> | undefined {
24
- return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
24
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
25
25
  }
26
26
 
27
27
  function numberField(obj: Record<string, unknown>, keys: string[]): number | undefined {
@@ -1,18 +1,15 @@
1
+ import { execSync } from "node:child_process";
1
2
  import * as fs from "node:fs";
2
3
  import * as os from "node:os";
3
- import { execSync } from "node:child_process";
4
- import { fileURLToPath } from "node:url";
5
4
  import * as path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
6
 
7
7
  export interface PiSpawnCommand {
8
8
  command: string;
9
9
  args: string[];
10
10
  }
11
11
 
12
- const PI_PACKAGE_NAMES = [
13
- "@earendil-works/pi-coding-agent",
14
- "@mariozechner/pi-coding-agent",
15
- ];
12
+ const PI_PACKAGE_NAMES = ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"];
16
13
 
17
14
  function isRunnableNodeScript(filePath: string): boolean {
18
15
  return fs.existsSync(filePath) && /\.(?:mjs|cjs|js)$/i.test(filePath);
@@ -33,7 +30,9 @@ function isWithinAllowedPrefixes(resolvedPath: string): boolean {
33
30
  const execDir = path.dirname(fs.realpathSync.native(process.execPath));
34
31
  allowedPrefixes.push(execDir.toLowerCase());
35
32
  allowedPrefixes.push(path.join(path.dirname(execDir), "lib", "node_modules").toLowerCase());
36
- } catch { /* ignore */ }
33
+ } catch {
34
+ /* ignore */
35
+ }
37
36
 
38
37
  // npm global bin via APPDATA
39
38
  if (process.env.APPDATA) {
@@ -50,19 +49,25 @@ function isWithinAllowedPrefixes(resolvedPath: string): boolean {
50
49
  try {
51
50
  const projectBin = path.resolve("node_modules", ".bin");
52
51
  allowedPrefixes.push(projectBin.toLowerCase());
53
- } catch { /* ignore */ }
52
+ } catch {
53
+ /* ignore */
54
+ }
54
55
 
55
56
  // User home npm-global
56
57
  try {
57
58
  const homeNpm = path.join(os.homedir(), ".npm-global", "bin");
58
59
  allowedPrefixes.push(homeNpm.toLowerCase());
59
- } catch { /* ignore */ }
60
+ } catch {
61
+ /* ignore */
62
+ }
60
63
 
61
64
  // User home .local/bin
62
65
  try {
63
66
  const homeLocal = path.join(os.homedir(), ".local", "bin");
64
67
  allowedPrefixes.push(homeLocal.toLowerCase());
65
- } catch { /* ignore */ }
68
+ } catch {
69
+ /* ignore */
70
+ }
66
71
 
67
72
  return allowedPrefixes.some((prefix) => normalized.startsWith(prefix));
68
73
  }
@@ -89,8 +94,10 @@ function resolvePiPackageRoot(): string | undefined {
89
94
 
90
95
  function packageBinScript(packageJsonPath: string): string | undefined {
91
96
  try {
92
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as { bin?: string | Record<string, string> };
93
- const binPath = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.pi ?? Object.values(pkg.bin ?? {})[0];
97
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
98
+ bin?: string | Record<string, string>;
99
+ };
100
+ const binPath = typeof pkg.bin === "string" ? pkg.bin : (pkg.bin?.pi ?? Object.values(pkg.bin ?? {})[0]);
94
101
  if (!binPath) return undefined;
95
102
  const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
96
103
  return isRunnableNodeScript(candidate) ? candidate : undefined;
@@ -104,7 +111,9 @@ function findPiPackageJsonFrom(startDir: string): string | undefined {
104
111
  while (dir !== path.dirname(dir)) {
105
112
  const direct = path.join(dir, "package.json");
106
113
  try {
107
- const pkg = JSON.parse(fs.readFileSync(direct, "utf-8")) as { name?: string };
114
+ const pkg = JSON.parse(fs.readFileSync(direct, "utf-8")) as {
115
+ name?: string;
116
+ };
108
117
  if (pkg.name && PI_PACKAGE_NAMES.includes(pkg.name)) return direct;
109
118
  } catch {
110
119
  // Continue searching upward and in node_modules.
@@ -200,7 +209,7 @@ function resolvePiCliScript(): string | undefined {
200
209
  ].filter((entry): entry is string => Boolean(entry));
201
210
 
202
211
  for (const root of roots) {
203
- const packageJsonPath = root.endsWith("package.json") ? root : findPiPackageJsonFrom(root) ?? path.join(root, "package.json");
212
+ const packageJsonPath = root.endsWith("package.json") ? root : (findPiPackageJsonFrom(root) ?? path.join(root, "package.json"));
204
213
  const script = packageBinScript(packageJsonPath);
205
214
  if (script) return script;
206
215
  }
@@ -214,16 +223,14 @@ function validateExplicitBin(explicit: string): string | undefined {
214
223
  if (!isWithinAllowedPrefixes(resolved)) {
215
224
  throw new Error(
216
225
  `PI_TEAMS_PI_BIN path '${resolved}' is outside allowed prefixes. ` +
217
- `Allowed: npm global bin, project node_modules/.bin, APPDATA/npm, or process execPath directory.`,
226
+ `Allowed: npm global bin, project node_modules/.bin, APPDATA/npm, or process execPath directory.`,
218
227
  );
219
228
  }
220
229
  // Reject if symlink points outside expected directories
221
230
  try {
222
231
  const real = fs.realpathSync(resolved);
223
232
  if (!isWithinAllowedPrefixes(real)) {
224
- throw new Error(
225
- `PI_TEAMS_PI_BIN symlink target '${real}' is outside allowed prefixes.`,
226
- );
233
+ throw new Error(`PI_TEAMS_PI_BIN symlink target '${real}' is outside allowed prefixes.`);
227
234
  }
228
235
  } catch (e) {
229
236
  if (e instanceof Error && e.message.includes("allowed prefixes")) throw e;
@@ -238,7 +245,11 @@ export function getPiSpawnCommand(args: string[]): PiSpawnCommand {
238
245
  if (explicit) {
239
246
  const validated = validateExplicitBin(explicit);
240
247
  if (validated) {
241
- if (isRunnableNodeScript(validated)) return { command: process.execPath, args: [validated, ...args] };
248
+ if (isRunnableNodeScript(validated))
249
+ return {
250
+ command: process.execPath,
251
+ args: [validated, ...args],
252
+ };
242
253
  return { command: validated, args };
243
254
  }
244
255
  }
@@ -1,9 +1,9 @@
1
- import type { TeamTaskState } from "../state/types.ts";
2
- import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
3
- import type { TeamConfig } from "../teams/team-config.ts";
4
1
  import type { AgentConfig } from "../agents/agent-config.ts";
5
- import { appendEventAsync } from "../state/event-log.ts";
6
2
  import { errors } from "../errors.ts";
3
+ import { appendEventAsync } from "../state/event-log.ts";
4
+ import type { TeamTaskState } from "../state/types.ts";
5
+ import type { TeamConfig } from "../teams/team-config.ts";
6
+ import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
7
7
  import { mapConcurrent } from "./parallel-utils.ts";
8
8
 
9
9
  /**
@@ -80,7 +80,10 @@ export class PipelineRunner {
80
80
  private stopOnError: boolean;
81
81
  private defaultMaxConcurrency: number;
82
82
 
83
- constructor(options?: { stopOnError?: boolean; defaultMaxConcurrency?: number }) {
83
+ constructor(options?: {
84
+ stopOnError?: boolean;
85
+ defaultMaxConcurrency?: number;
86
+ }) {
84
87
  this.stopOnError = options?.stopOnError ?? true;
85
88
  this.defaultMaxConcurrency = options?.defaultMaxConcurrency ?? 5;
86
89
  }
@@ -139,12 +142,7 @@ export class PipelineRunner {
139
142
  const inputs = this.resolveInputs(stage.inputs, previousResults, context);
140
143
 
141
144
  // Execute stage (handle fan-out if enabled)
142
- const results = await this.executeStageInternal(
143
- stage,
144
- inputs,
145
- stageContext,
146
- executeStage,
147
- );
145
+ const results = await this.executeStageInternal(stage, inputs, stageContext, executeStage);
148
146
 
149
147
  const duration = Date.now() - stageStartTime;
150
148
  stages.push({
@@ -161,7 +159,12 @@ export class PipelineRunner {
161
159
  type: "pipeline:stage_completed",
162
160
  runId,
163
161
  message: `Stage '${stage.name}' completed`,
164
- data: { stageIndex: i, stageName: stage.name, duration, resultCount: results.length },
162
+ data: {
163
+ stageIndex: i,
164
+ stageName: stage.name,
165
+ duration,
166
+ resultCount: results.length,
167
+ },
165
168
  });
166
169
  } catch (error) {
167
170
  const duration = Date.now() - stageStartTime;
@@ -180,7 +183,12 @@ export class PipelineRunner {
180
183
  type: "pipeline:stage_failed",
181
184
  runId,
182
185
  message: `Stage '${stage.name}' failed: ${errorMessage}`,
183
- data: { stageIndex: i, stageName: stage.name, duration, error: errorMessage },
186
+ data: {
187
+ stageIndex: i,
188
+ stageName: stage.name,
189
+ duration,
190
+ error: errorMessage,
191
+ },
184
192
  });
185
193
 
186
194
  await appendEventAsync(eventsPath, {
@@ -209,7 +217,12 @@ export class PipelineRunner {
209
217
  type: "pipeline:stage_skipped",
210
218
  runId,
211
219
  message: `Stage '${stage.name}' skipped due to error`,
212
- data: { stageIndex: i, stageName: stage.name, duration, error: errorMessage },
220
+ data: {
221
+ stageIndex: i,
222
+ stageName: stage.name,
223
+ duration,
224
+ error: errorMessage,
225
+ },
213
226
  });
214
227
  }
215
228
  }
@@ -219,7 +232,9 @@ export class PipelineRunner {
219
232
  type: "pipeline:completed",
220
233
  runId,
221
234
  message: `Pipeline '${workflow.name}' completed`,
222
- data: { stages: stages.map((s) => ({ name: s.name, status: s.status })) },
235
+ data: {
236
+ stages: stages.map((s) => ({ name: s.name, status: s.status })),
237
+ },
223
238
  });
224
239
 
225
240
  return {
@@ -266,18 +281,20 @@ export class PipelineRunner {
266
281
  }));
267
282
 
268
283
  // Execute with concurrency limit - pass each item to callback
269
- const results = await mapConcurrent(
270
- tasks,
271
- maxConcurrency,
272
- async (task) => {
273
- // Call the user-provided callback with each item
274
- const result = await this.executeStageInternal(stage, task.item, {
284
+ const results = await mapConcurrent(tasks, maxConcurrency, async (task) => {
285
+ // Call the user-provided callback with each item
286
+ const result = await this.executeStageInternal(
287
+ stage,
288
+ task.item,
289
+ {
275
290
  ...stageContext,
276
291
  stageName: task.name,
277
- }, callback, nextDepth);
278
- return result;
279
- },
280
- );
292
+ },
293
+ callback,
294
+ nextDepth,
295
+ );
296
+ return result;
297
+ });
281
298
 
282
299
  return results;
283
300
  }
@@ -294,14 +311,10 @@ export class PipelineRunner {
294
311
  * - ${previous[0]} -> previousResults[0]
295
312
  * - ${context.key} -> context.key
296
313
  * - ${args.x} -> context.args.x
297
- *
314
+ *
298
315
  * C5: Validates template inputs to prevent injection.
299
316
  */
300
- private resolveInputs(
301
- inputs: unknown,
302
- previousResults: unknown[],
303
- context: Record<string, unknown>,
304
- ): unknown {
317
+ private resolveInputs(inputs: unknown, previousResults: unknown[], context: Record<string, unknown>): unknown {
305
318
  // If inputs is an array, resolve each element
306
319
  if (Array.isArray(inputs)) {
307
320
  // H4: Type safety - limit array size to prevent memory issues
@@ -337,13 +350,13 @@ export class PipelineRunner {
337
350
  private isValidObjectKey(key: string): boolean {
338
351
  // Reject dangerous keys
339
352
  const dangerousKeys = [
340
- '__proto__',
341
- 'constructor',
342
- 'prototype',
343
- '__defineGetter__',
344
- '__defineSetter__',
345
- '__lookupGetter__',
346
- '__lookupSetter__',
353
+ "__proto__",
354
+ "constructor",
355
+ "prototype",
356
+ "__defineGetter__",
357
+ "__defineSetter__",
358
+ "__lookupGetter__",
359
+ "__lookupSetter__",
347
360
  ];
348
361
  if (dangerousKeys.includes(key)) {
349
362
  return false;
@@ -373,11 +386,11 @@ export class PipelineRunner {
373
386
  return false;
374
387
  }
375
388
  // Reject paths with empty segments
376
- if (path.includes('..')) {
389
+ if (path.includes("..")) {
377
390
  return false;
378
391
  }
379
392
  // Validate each path segment
380
- const parts = path.split('.');
393
+ const parts = path.split(".");
381
394
  for (const part of parts) {
382
395
  if (!this.isValidObjectKey(part)) {
383
396
  return false;
@@ -390,11 +403,7 @@ export class PipelineRunner {
390
403
  * Resolve a single template string.
391
404
  * C5: Validates template inputs to prevent injection.
392
405
  */
393
- private resolveTemplate(
394
- template: string,
395
- previousResults: unknown[],
396
- context: Record<string, unknown>,
397
- ): unknown {
406
+ private resolveTemplate(template: string, previousResults: unknown[], context: Record<string, unknown>): unknown {
398
407
  // C5: Validate template length to prevent DoS
399
408
  if (template.length > 10000) {
400
409
  return template;
@@ -473,10 +482,7 @@ export class PipelineRunner {
473
482
  * Parse a pipeline workflow from a workflow configuration.
474
483
  * Converts standard WorkflowConfig to PipelineWorkflow.
475
484
  */
476
- static fromWorkflowConfig(
477
- workflow: WorkflowConfig,
478
- goal: string,
479
- ): PipelineWorkflow {
485
+ static fromWorkflowConfig(workflow: WorkflowConfig, goal: string): PipelineWorkflow {
480
486
  const stages: PipelineStage[] = workflow.steps.map((step) => ({
481
487
  name: step.id,
482
488
  team: step.role, // Using role as team identifier
@@ -498,12 +504,7 @@ export class PipelineRunner {
498
504
  /**
499
505
  * Create a pipeline workflow from a goal and stage definitions.
500
506
  */
501
- export function createPipelineWorkflow(
502
- name: string,
503
- description: string,
504
- goal: string,
505
- stages: PipelineStage[],
506
- ): PipelineWorkflow {
507
+ export function createPipelineWorkflow(name: string, description: string, goal: string, stages: PipelineStage[]): PipelineWorkflow {
507
508
  return {
508
509
  name,
509
510
  description,
@@ -89,10 +89,7 @@ export function listPlanTemplates(): string[] {
89
89
  * @param variables - Key-value pairs for substitution
90
90
  * @returns Rendered plan, or undefined if template not found
91
91
  */
92
- export function renderPlanTemplate(
93
- templateName: string,
94
- variables: Record<string, string>,
95
- ): RenderedPlan | undefined {
92
+ export function renderPlanTemplate(templateName: string, variables: Record<string, string>): RenderedPlan | undefined {
96
93
  const template = templates.get(templateName);
97
94
  if (!template) {
98
95
  logInternalError("plan-templates", new Error(`Template not found: ${templateName}`));
@@ -135,7 +132,8 @@ registerPlanTemplate({
135
132
  {
136
133
  name: "review",
137
134
  role: "reviewer",
138
- taskTemplate: "Review the code identified in the explore phase for: {{goal}}. Check correctness, maintainability, and security.",
135
+ taskTemplate:
136
+ "Review the code identified in the explore phase for: {{goal}}. Check correctness, maintainability, and security.",
139
137
  maxTasks: 1,
140
138
  dependsOn: ["explore"],
141
139
  },
@@ -160,7 +158,8 @@ registerPlanTemplate({
160
158
  {
161
159
  name: "explore",
162
160
  role: "explorer",
163
- taskTemplate: "Explore the codebase to understand the current state relevant to: {{goal}}. Identify affected files and patterns.",
161
+ taskTemplate:
162
+ "Explore the codebase to understand the current state relevant to: {{goal}}. Identify affected files and patterns.",
164
163
  maxTasks: 1,
165
164
  dependsOn: [],
166
165
  },
@@ -36,34 +36,70 @@ export function evaluateCrewPolicy(input: PolicyEngineInput): PolicyDecision[] {
36
36
  const decisions: PolicyDecision[] = [];
37
37
  const maxTasksPerRun = Number.isFinite(input.limits?.maxTasksPerRun) ? input.limits!.maxTasksPerRun : undefined;
38
38
  if (maxTasksPerRun !== undefined && input.tasks.length > maxTasksPerRun) {
39
- decisions.push(decision("block", "limit_exceeded", `Run has ${input.tasks.length} tasks, exceeding maxTasksPerRun=${maxTasksPerRun}.`));
39
+ decisions.push(
40
+ decision("block", "limit_exceeded", `Run has ${input.tasks.length} tasks, exceeding maxTasksPerRun=${maxTasksPerRun}.`),
41
+ );
40
42
  }
41
43
  const runningCount = input.tasks.filter((task) => task.status === "running").length;
42
44
  const maxConcurrentWorkers = Number.isFinite(input.limits?.maxConcurrentWorkers) ? input.limits!.maxConcurrentWorkers : undefined;
43
45
  if (maxConcurrentWorkers !== undefined && runningCount > maxConcurrentWorkers) {
44
- decisions.push(decision("block", "limit_exceeded", `Run has ${runningCount} running workers, exceeding maxConcurrentWorkers=${maxConcurrentWorkers}.`));
46
+ decisions.push(
47
+ decision(
48
+ "block",
49
+ "limit_exceeded",
50
+ `Run has ${runningCount} running workers, exceeding maxConcurrentWorkers=${maxConcurrentWorkers}.`,
51
+ ),
52
+ );
45
53
  }
46
54
  const tasksById = new Map(input.tasks.map((task) => [task.id, task]));
47
55
 
48
56
  for (const task of input.tasks) {
49
57
  if (input.limits?.maxChildrenPerTask !== undefined && (task.graph?.children.length ?? 0) > input.limits.maxChildrenPerTask) {
50
- decisions.push(decision("block", "limit_exceeded", `Task has ${task.graph?.children.length ?? 0} children, exceeding maxChildrenPerTask=${input.limits.maxChildrenPerTask}.`, task.id));
58
+ decisions.push(
59
+ decision(
60
+ "block",
61
+ "limit_exceeded",
62
+ `Task has ${task.graph?.children.length ?? 0} children, exceeding maxChildrenPerTask=${input.limits.maxChildrenPerTask}.`,
63
+ task.id,
64
+ ),
65
+ );
51
66
  }
52
67
  if (input.limits?.maxTaskDepth !== undefined && taskDepth(task, tasksById) > input.limits.maxTaskDepth) {
53
- decisions.push(decision("block", "limit_exceeded", `Task graph depth exceeds maxTaskDepth=${input.limits.maxTaskDepth}.`, task.id));
68
+ decisions.push(
69
+ decision("block", "limit_exceeded", `Task graph depth exceeds maxTaskDepth=${input.limits.maxTaskDepth}.`, task.id),
70
+ );
54
71
  }
55
72
  if (task.status === "failed") {
56
73
  const retryCount = task.policy?.retryCount ?? 0;
57
74
  const maxRetries = input.limits?.maxRetriesPerTask ?? 0;
58
- decisions.push(decision(retryCount < maxRetries ? "retry" : "escalate", "task_failed", task.error ? `Task failed: ${task.error}` : "Task failed.", task.id));
75
+ decisions.push(
76
+ decision(
77
+ retryCount < maxRetries ? "retry" : "escalate",
78
+ "task_failed",
79
+ task.error ? `Task failed: ${task.error}` : "Task failed.",
80
+ task.id,
81
+ ),
82
+ );
59
83
  }
60
- if ((task.status === "running" || task.status === "queued") && task.heartbeat && task.heartbeat.alive !== false && isWorkerHeartbeatStale(task.heartbeat, input.limits?.heartbeatStaleMs ?? 60_000, input.now)) {
84
+ if (
85
+ (task.status === "running" || task.status === "queued") &&
86
+ task.heartbeat &&
87
+ task.heartbeat.alive !== false &&
88
+ isWorkerHeartbeatStale(task.heartbeat, input.limits?.heartbeatStaleMs ?? 60_000, input.now)
89
+ ) {
61
90
  decisions.push(decision("escalate", "worker_stale", "Worker heartbeat is stale.", task.id));
62
91
  }
63
92
  if (task.taskPacket?.verification) {
64
93
  const outcome = evaluateGreenContract(task.taskPacket.verification, task.verification);
65
94
  if (!outcome.satisfied && task.status === "completed") {
66
- decisions.push(decision("block", "green_unsatisfied", `Green contract unsatisfied: required=${outcome.requiredGreenLevel}, observed=${outcome.observedGreenLevel}.`, task.id));
95
+ decisions.push(
96
+ decision(
97
+ "block",
98
+ "green_unsatisfied",
99
+ `Green contract unsatisfied: required=${outcome.requiredGreenLevel}, observed=${outcome.observedGreenLevel}.`,
100
+ task.id,
101
+ ),
102
+ );
67
103
  }
68
104
  }
69
105
  }
@@ -7,8 +7,8 @@
7
7
  import { execFileSync } from "node:child_process";
8
8
  import * as path from "node:path";
9
9
  import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
10
- import { resolveShellForScript } from "../utils/resolve-shell.ts";
11
10
  import { sanitizeEnvSecrets } from "../utils/env-filter.ts";
11
+ import { resolveShellForScript } from "../utils/resolve-shell.ts";
12
12
  import { resolveRealContainedPath } from "../utils/safe-paths.ts";
13
13
 
14
14
  /** Default timeout for post-check scripts (5 minutes). */
@@ -92,12 +92,17 @@ export async function runPostCheck(config: PostCheckConfig, cwd: string): Promis
92
92
  return new Promise<PostCheckResult>((resolve) => {
93
93
  try {
94
94
  const { command, args } = resolveShellForScript(scriptPath);
95
- const output = execFileSync(command, args, {
95
+ const output = execFileSync(command, args, {
96
96
  cwd,
97
97
  timeout: timeoutMs,
98
98
  encoding: "utf-8",
99
99
  maxBuffer: 10 * 1024 * 1024, // 10 MB
100
- env: { ...sanitizeEnvSecrets(process.env, { allowList: ["PATH", "HOME", "USER", ...WINDOWS_ESSENTIAL_ENV_VARS, "TMPDIR", "LANG", "LC_ALL", "PI_CREW_*"] }), PI_CREW_POST_CHECK: "1" },
100
+ env: {
101
+ ...sanitizeEnvSecrets(process.env, {
102
+ allowList: ["PATH", "HOME", "USER", ...WINDOWS_ESSENTIAL_ENV_VARS, "TMPDIR", "LANG", "LC_ALL", "PI_CREW_*"],
103
+ }),
104
+ PI_CREW_POST_CHECK: "1",
105
+ },
101
106
  });
102
107
 
103
108
  const durationMs = Date.now() - startTime;
@@ -118,7 +123,10 @@ export async function runPostCheck(config: PostCheckConfig, cwd: string): Promis
118
123
 
119
124
  let output = "";
120
125
  if (error instanceof Error) {
121
- const execError = error as NodeJS.ErrnoException & { stdout?: string; stderr?: string };
126
+ const execError = error as NodeJS.ErrnoException & {
127
+ stdout?: string;
128
+ stderr?: string;
129
+ };
122
130
  output = [execError.stdout ?? "", execError.stderr ?? "", execError.message ?? ""].join("\n").trim();
123
131
  } else {
124
132
  output = String(error);
@@ -35,12 +35,12 @@ export function attachPostExitStdioGuard(child: ChildWithPipedStdio, options: Po
35
35
  if (!stdoutEnded) {
36
36
  try {
37
37
  child.stdout?.destroy();
38
- } catch {}
38
+ } catch {}
39
39
  }
40
40
  if (!stderrEnded) {
41
41
  try {
42
42
  child.stderr?.destroy();
43
- } catch {}
43
+ } catch {}
44
44
  }
45
45
  };
46
46
 
@@ -29,7 +29,7 @@
29
29
  * `taskkill /F /T /PID` escalation directly (force-kill the whole tree).
30
30
  * See `.crew/knowledge.md` gotchas: BSD/Windows signal handling differs.
31
31
  */
32
- import { spawn, type ChildProcess, type SpawnOptions } from "node:child_process";
32
+ import { type ChildProcess, type SpawnOptions, spawn } from "node:child_process";
33
33
  import { logInternalError } from "../utils/internal-error.ts";
34
34
 
35
35
  // ── tunables ──────────────────────────────────────────────────────────────────
@@ -125,7 +125,10 @@ export class OwnedProcess {
125
125
  private deregistered = false;
126
126
  /** Terminal once teardown/reconciliation has confirmed the group is gone. */
127
127
  private terminated = false;
128
- private exitPromise: Promise<{ code: number | null; signal: NodeJS.Signals | null }>;
128
+ private exitPromise: Promise<{
129
+ code: number | null;
130
+ signal: NodeJS.Signals | null;
131
+ }>;
129
132
  private exitCallbacks = new Set<OwnedExitCallback>();
130
133
  private onAbort: (() => void) | undefined;
131
134
  private readonly abortSignal: AbortSignal | undefined;
@@ -187,7 +190,9 @@ export class OwnedProcess {
187
190
  void this.dispose();
188
191
  } else {
189
192
  this.onAbort = () => void this.dispose();
190
- this.abortSignal.addEventListener("abort", this.onAbort, { once: true });
193
+ this.abortSignal.addEventListener("abort", this.onAbort, {
194
+ once: true,
195
+ });
191
196
  }
192
197
  }
193
198
  }
@@ -230,14 +235,14 @@ export class OwnedProcess {
230
235
  * With no timeout it resolves only when the child exits. Never rejects.
231
236
  */
232
237
  async awaitExit(opts?: { timeoutMs?: number }): Promise<AwaitExitResult> {
233
- const exitResult = this.exitPromise.then((info) => ({ exited: true as const, code: info.code }));
238
+ const exitResult = this.exitPromise.then((info) => ({
239
+ exited: true as const,
240
+ code: info.code,
241
+ }));
234
242
  if (opts?.timeoutMs === undefined) return exitResult;
235
243
  let timer: ReturnType<typeof setTimeout> | undefined;
236
244
  const timeout = new Promise<AwaitExitResult>((resolve) => {
237
- timer = setTimeout(
238
- () => resolve({ exited: false, code: this.child.exitCode }),
239
- Math.max(0, opts.timeoutMs!),
240
- );
245
+ timer = setTimeout(() => resolve({ exited: false, code: this.child.exitCode }), Math.max(0, opts.timeoutMs!));
241
246
  });
242
247
  try {
243
248
  return await Promise.race([exitResult, timeout]);
@@ -311,7 +316,9 @@ export class OwnedProcess {
311
316
  if (await pollUntil(() => !groupAlive(this.pgid!), this.gracefulMs)) return;
312
317
  this.signalTree("SIGKILL");
313
318
  if (!(await pollUntil(() => !groupAlive(this.pgid!), SIGKILL_REAP_CAP_MS))) {
314
- console.warn(`[pi-crew] owned process group still alive after SIGKILL (name=${this.name ?? "?"}, pgid=${this.pgid})`);
319
+ console.warn(
320
+ `[pi-crew] owned process group still alive after SIGKILL (name=${this.name ?? "?"}, pgid=${this.pgid})`,
321
+ );
315
322
  }
316
323
  return;
317
324
  }
@@ -341,7 +348,10 @@ export class OwnedProcess {
341
348
  const tryTaskkill = (force: boolean): Promise<void> =>
342
349
  new Promise((resolve) => {
343
350
  const args = ["/T", "/PID", String(pid), ...(force ? ["/F"] : [])];
344
- const tk = spawn("taskkill", args, { stdio: "ignore", windowsHide: true });
351
+ const tk = spawn("taskkill", args, {
352
+ stdio: "ignore",
353
+ windowsHide: true,
354
+ });
345
355
  tk.on("error", () => resolve());
346
356
  tk.on("exit", () => resolve());
347
357
  });