pi-crew 0.9.15 → 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 +145 -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 +193 -40
  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 +86 -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
@@ -134,11 +134,7 @@ export function resultDigest(toolName: string, content: string, isError?: boolea
134
134
  const files = text.match(/(\d+)\s+files?/i) ?? text.match(/filesWithMatches["']?:\s*(\d+)/i);
135
135
  const error = firstErrorLine(text);
136
136
  return (
137
- [
138
- match ? `matches=${match[1]}` : undefined,
139
- files ? `files=${files[1]}` : undefined,
140
- error ? `error=${error}` : undefined,
141
- ]
137
+ [match ? `matches=${match[1]}` : undefined, files ? `files=${files[1]}` : undefined, error ? `error=${error}` : undefined]
142
138
  .filter((part): part is string => part !== undefined)
143
139
  .join("; ") || "search digest unavailable"
144
140
  );
@@ -289,9 +285,7 @@ export function pruneToolOutputs(results: ToolResultEntry[], config: PruneConfig
289
285
  // Staleness waives protected-tool immunity for overridable tools
290
286
  // (e.g. a superseded `read`); the most recent result per target is
291
287
  // never stale, so the latest read of each file stays protected.
292
- const isProtected =
293
- config.protectedTools.includes(entry.toolName) &&
294
- !(isStale && staleOverridable.has(entry.toolName));
288
+ const isProtected = config.protectedTools.includes(entry.toolName) && !(isStale && staleOverridable.has(entry.toolName));
295
289
 
296
290
  // Stale results are prunable even inside the recency protect window —
297
291
  // they are superseded, so recency no longer implies relevance. They
@@ -325,7 +319,10 @@ export function pruneToolOutputs(results: ToolResultEntry[], config: PruneConfig
325
319
 
326
320
  const prunedResults = [...results];
327
321
  for (const candidate of candidates) {
328
- prunedResults[candidate.index] = { ...candidate.entry, content: candidate.notice };
322
+ prunedResults[candidate.index] = {
323
+ ...candidate.entry,
324
+ content: candidate.notice,
325
+ };
329
326
  prunedIds.push(candidate.entry.id);
330
327
  prunedCount++;
331
328
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Tool Progress Event System
3
- *
3
+ *
4
4
  * Provides real-time visibility into tool execution within child Pi workers.
5
- *
5
+ *
6
6
  * Event flow:
7
7
  * 1. Child Pi emits JSON events on stdout (tool_execution_start, tool_execution_end, etc.)
8
8
  * 2. child-pi.ts parses these events and passes them to onJsonEvent callback
@@ -165,9 +165,7 @@ export function formatToolProgress(progress: CrewAgentProgress, maxContextTokens
165
165
  }));
166
166
 
167
167
  // If there's a currentTool but no endedAt, it's still running
168
- const currentRunning = progress.recentTools.find(
169
- (t) => !t.endedAt && t.tool === progress.currentTool,
170
- );
168
+ const currentRunning = progress.recentTools.find((t) => !t.endedAt && t.tool === progress.currentTool);
171
169
  if (currentRunning && progress.currentTool) {
172
170
  recentTools.push({
173
171
  tool: progress.currentTool,
@@ -199,13 +197,13 @@ export function formatToolProgress(progress: CrewAgentProgress, maxContextTokens
199
197
  */
200
198
  export function formatCurrentToolLine(progress: CrewAgentProgress): string {
201
199
  if (!progress.currentTool) return "";
202
-
203
- const args = progress.currentToolArgs
200
+
201
+ const args = progress.currentToolArgs
204
202
  ? ` ${progress.currentToolArgs.slice(0, 50)}${progress.currentToolArgs.length > 50 ? "..." : ""}`
205
203
  : "";
206
-
204
+
207
205
  const toolCount = progress.toolCount > 0 ? ` (${progress.toolCount})` : "";
208
-
206
+
209
207
  return `${progress.currentTool}${args}${toolCount}`;
210
208
  }
211
209
 
@@ -229,10 +227,7 @@ export interface ProgressBarOptions {
229
227
  /**
230
228
  * Render a progress bar for tool execution
231
229
  */
232
- export function renderProgressBar(
233
- progress: CrewAgentProgress,
234
- options: ProgressBarOptions = {},
235
- ): string {
230
+ export function renderProgressBar(progress: CrewAgentProgress, options: ProgressBarOptions = {}): string {
236
231
  const width = options.width ?? 20;
237
232
  const showPercent = options.showPercent ?? true;
238
233
  const showCount = options.showCount ?? true;
@@ -244,9 +239,7 @@ export function renderProgressBar(
244
239
 
245
240
  const bar = "█".repeat(filled) + "░".repeat(empty);
246
241
  const percent = showPercent ? ` ${progress.toolCount} tools` : "";
247
- const tokens = progress.tokens
248
- ? ` | ${(progress.tokens / 1000).toFixed(1)}k tokens`
249
- : "";
242
+ const tokens = progress.tokens ? ` | ${(progress.tokens / 1000).toFixed(1)}k tokens` : "";
250
243
 
251
244
  return `[${bar}]${percent}${tokens}`;
252
245
  }
@@ -270,10 +263,7 @@ export function filterToolEvents(events: ToolProgressEvent[]): ToolProgressEvent
270
263
  /**
271
264
  * Get events for a specific tool
272
265
  */
273
- export function getEventsForTool(
274
- events: ToolProgressEvent[],
275
- toolName: string,
276
- ): ToolProgressEvent[] {
266
+ export function getEventsForTool(events: ToolProgressEvent[], toolName: string): ToolProgressEvent[] {
277
267
  return events.filter((e) => {
278
268
  if ("toolName" in e) return e.toolName === toolName;
279
269
  return false;
@@ -285,4 +275,4 @@ export function getEventsForTool(
285
275
  */
286
276
  export function hasError(events: ToolProgressEvent[]): boolean {
287
277
  return events.some(isToolError);
288
- }
278
+ }
@@ -1,22 +1,22 @@
1
1
  /**
2
2
  * Verification Gates — ECC VERIFICATION_LOOP Pattern Implementation
3
- *
3
+ *
4
4
  * Implements RED/GREEN phase gates for task verification.
5
5
  * Sequential execution: cannot skip to Phase N+1 without Phase N passing.
6
- *
6
+ *
7
7
  * Based on: docs/distillation/ECC-10-skills.md §2 (verification-loop)
8
- *
8
+ *
9
9
  * @module verification-gates
10
10
  */
11
11
 
12
12
  import { spawn } from "node:child_process";
13
13
  import * as fs from "node:fs";
14
14
  import * as path from "node:path";
15
- import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
16
15
  import { writeArtifact } from "../state/artifact-store.ts";
17
- import { redactSecretString } from "../utils/redaction.ts";
16
+ import type { ArtifactDescriptor, GreenLevel, VerificationCommandResult, VerificationContract } from "../state/types.ts";
17
+ import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
18
18
  import { sanitizeEnvSecrets } from "../utils/env-filter.ts";
19
- import type { VerificationContract, VerificationCommandResult, GreenLevel, ArtifactDescriptor } from "../state/types.ts";
19
+ import { redactSecretString } from "../utils/redaction.ts";
20
20
 
21
21
  /**
22
22
  * Phase 1.5 #1 (RFC 13 §6 info-disclosure mitigation): sanitize the env passed
@@ -78,9 +78,15 @@ function buildVerificationEnv(): Record<string, string> {
78
78
  return { ...process.env, FORCE_COLOR: "0" };
79
79
  }
80
80
  const preserveRaw = process.env.PI_CREW_VERIFICATION_PRESERVE_ENV ?? process.env.PI_TEAMS_VERIFICATION_PRESERVE_ENV ?? "";
81
- const preserve = preserveRaw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
81
+ const preserve = preserveRaw
82
+ .split(",")
83
+ .map((s) => s.trim())
84
+ .filter((s) => s.length > 0);
82
85
  const allowList = [...VERIFICATION_ENV_ALLOWLIST, ...preserve];
83
- return { ...sanitizeEnvSecrets(process.env, { allowList }), FORCE_COLOR: "0" };
86
+ return {
87
+ ...sanitizeEnvSecrets(process.env, { allowList }),
88
+ FORCE_COLOR: "0",
89
+ };
84
90
  }
85
91
 
86
92
  export interface PhaseGateResult {
@@ -105,7 +111,11 @@ export interface PhaseGateBundle {
105
111
  * Standard phase gate definitions for npm/TypeScript projects.
106
112
  * Sequential enforcement: each phase must pass before proceeding.
107
113
  */
108
- export const NPM_TYPESCRIPT_GATES: Array<{ name: string; command: string; critical: boolean }> = [
114
+ export const NPM_TYPESCRIPT_GATES: Array<{
115
+ name: string;
116
+ command: string;
117
+ critical: boolean;
118
+ }> = [
109
119
  { name: "build", command: "npm run build 2>&1", critical: true },
110
120
  { name: "typecheck", command: "npx tsc --noEmit 2>&1", critical: true },
111
121
  { name: "lint", command: "npm run lint 2>&1", critical: false },
@@ -115,7 +125,11 @@ export const NPM_TYPESCRIPT_GATES: Array<{ name: string; command: string; critic
115
125
  /**
116
126
  * Cargo/Rust project phase gates.
117
127
  */
118
- export const CARGO_RUST_GATES: Array<{ name: string; command: string; critical: boolean }> = [
128
+ export const CARGO_RUST_GATES: Array<{
129
+ name: string;
130
+ command: string;
131
+ critical: boolean;
132
+ }> = [
119
133
  { name: "check", command: "cargo check 2>&1", critical: true },
120
134
  { name: "test", command: "cargo test 2>&1", critical: true },
121
135
  { name: "clippy", command: "cargo clippy 2>&1", critical: false },
@@ -160,15 +174,13 @@ function validateGateCommand(command: string): void {
160
174
  );
161
175
  }
162
176
  const normalized = command
163
- .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '') // ANSI escape sequences
164
- .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, '') // control chars
165
- .replace(/\\\n/g, ' ') // escaped newlines
166
- .replace(/\s+/g, ' ') // collapse whitespace
177
+ .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "") // ANSI escape sequences
178
+ .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "") // control chars
179
+ .replace(/\\\n/g, " ") // escaped newlines
180
+ .replace(/\s+/g, " ") // collapse whitespace
167
181
  .trim();
168
182
  if (DANGEROUS_SHELL_PATTERNS.test(normalized)) {
169
- throw new Error(
170
- `Security: verification gate command rejected (dangerous shell pattern): ${command}`,
171
- );
183
+ throw new Error(`Security: verification gate command rejected (dangerous shell pattern): ${command}`);
172
184
  }
173
185
  }
174
186
 
@@ -237,7 +249,7 @@ async function executeCommand(
237
249
 
238
250
  /**
239
251
  * Run phase gates sequentially, stopping on first critical failure.
240
- *
252
+ *
241
253
  * @param gates - Array of phase gate definitions
242
254
  * @param cwd - Working directory to execute commands in
243
255
  * @param signal - Optional abort signal
@@ -312,7 +324,7 @@ export async function runPhaseGates(
312
324
  /**
313
325
  * Execute verification commands from a task's verification contract.
314
326
  * Maps the contract commands to phase gates and runs them sequentially.
315
- *
327
+ *
316
328
  * @param contract - Verification contract with commands to execute
317
329
  * @param cwd - Working directory
318
330
  * @param runId - Run ID for artifact naming
@@ -413,15 +425,12 @@ export async function executeVerificationCommands(
413
425
  /**
414
426
  * Compute observed green level from verification results.
415
427
  * Maps verification outcomes to green levels per ECC pattern.
416
- *
428
+ *
417
429
  * @param commands - Array of verification command results
418
430
  * @param requiredLevel - Required green level from contract
419
431
  * @returns Observed green level
420
432
  */
421
- export function computeGreenLevelFromResults(
422
- commands: VerificationCommandResult[],
423
- requiredLevel: GreenLevel,
424
- ): GreenLevel {
433
+ export function computeGreenLevelFromResults(commands: VerificationCommandResult[], requiredLevel: GreenLevel): GreenLevel {
425
434
  if (commands.length === 0) {
426
435
  return "none";
427
436
  }
@@ -88,10 +88,7 @@ export function snapshotManifests(cwd: string): Record<string, string> {
88
88
  * @returns Sorted array of relative manifest paths that drifted. Identical
89
89
  * snapshots yield `[]`.
90
90
  */
91
- export function compareSnapshot(
92
- a: Record<string, string>,
93
- b: Record<string, string>,
94
- ): string[] {
91
+ export function compareSnapshot(a: Record<string, string>, b: Record<string, string>): string[] {
95
92
  const drifted = new Set<string>();
96
93
  for (const [key, hash] of Object.entries(a)) {
97
94
  const other = b[key];
@@ -53,27 +53,57 @@ export function isWorktreeSandboxEnabled(): boolean {
53
53
  */
54
54
  export function checkWorktreeSandboxAvailable(cwd: string): { available: true; commitSha: string } | { available: false; reason: string } {
55
55
  if (!isWorktreeSandboxEnabled()) {
56
- return { available: false, reason: "PI_CREW_VERIFICATION_WORKTREE not set (opt-in)" };
56
+ return {
57
+ available: false,
58
+ reason: "PI_CREW_VERIFICATION_WORKTREE not set (opt-in)",
59
+ };
57
60
  }
58
61
  try {
59
62
  // Is cwd inside a git repo? `git rev-parse --show-toplevel` errors out
60
63
  // (non-zero exit) when not in a repo. execFileSync throws on non-zero.
61
- const toplevel = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
62
- if (!toplevel) return { available: false, reason: "git rev-parse returned empty toplevel" };
64
+ const toplevel = execFileSync("git", ["rev-parse", "--show-toplevel"], {
65
+ cwd,
66
+ stdio: ["ignore", "pipe", "pipe"],
67
+ encoding: "utf-8",
68
+ }).trim();
69
+ if (!toplevel)
70
+ return {
71
+ available: false,
72
+ reason: "git rev-parse returned empty toplevel",
73
+ };
63
74
  // Current commit SHA (this is what T_snap will pin to).
64
- const commitSha = execFileSync("git", ["rev-parse", "HEAD"], { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
65
- if (!commitSha) return { available: false, reason: "git rev-parse HEAD returned empty SHA" };
75
+ const commitSha = execFileSync("git", ["rev-parse", "HEAD"], {
76
+ cwd,
77
+ stdio: ["ignore", "pipe", "pipe"],
78
+ encoding: "utf-8",
79
+ }).trim();
80
+ if (!commitSha)
81
+ return {
82
+ available: false,
83
+ reason: "git rev-parse HEAD returned empty SHA",
84
+ };
66
85
  // Dirty index? `git status --porcelain` outputs non-empty if there are
67
86
  // uncommitted changes. We refuse to sandbox a dirty workspace because
68
87
  // the worktree would NOT contain the in-progress edits (T_snap would
69
88
  // pin to a stale commit). Better to fall back + warn than silently
70
89
  // verify against the wrong code.
71
- const status = execFileSync("git", ["status", "--porcelain"], { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
72
- if (status.length > 0) return { available: false, reason: `dirty git index (${status.split("\n").length} changed files); refusing to sandbox — worktree would pin to stale commit` };
90
+ const status = execFileSync("git", ["status", "--porcelain"], {
91
+ cwd,
92
+ stdio: ["ignore", "pipe", "pipe"],
93
+ encoding: "utf-8",
94
+ }).trim();
95
+ if (status.length > 0)
96
+ return {
97
+ available: false,
98
+ reason: `dirty git index (${status.split("\n").length} changed files); refusing to sandbox — worktree would pin to stale commit`,
99
+ };
73
100
  return { available: true, commitSha };
74
101
  } catch (error) {
75
102
  const msg = error instanceof Error ? error.message : String(error);
76
- return { available: false, reason: `git precondition check failed: ${msg.slice(0, 200)}` };
103
+ return {
104
+ available: false,
105
+ reason: `git precondition check failed: ${msg.slice(0, 200)}`,
106
+ };
77
107
  }
78
108
  }
79
109
 
@@ -102,13 +132,29 @@ export function prepareVerificationWorktree(cwd: string, commitSha: string): Ver
102
132
  execFileSync("git", ["worktree", "remove", "--force", worktreePath], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 5000 });
103
133
  } catch {
104
134
  // Fall back to `git worktree prune` if remove fails (already gone).
105
- try { execFileSync("git", ["worktree", "prune"], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 5000 }); } catch { /* best-effort */ }
135
+ try {
136
+ execFileSync("git", ["worktree", "prune"], {
137
+ cwd,
138
+ stdio: ["ignore", "pipe", "pipe"],
139
+ timeout: 5000,
140
+ });
141
+ } catch {
142
+ /* best-effort */
143
+ }
106
144
  }
107
145
  // Remove the temp parent dir.
108
- try { fs.rmSync(tmpRoot, { recursive: true, force: true }); } catch { /* best-effort */ }
146
+ try {
147
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
148
+ } catch {
149
+ /* best-effort */
150
+ }
109
151
  };
110
152
  try {
111
- execFileSync("git", ["worktree", "add", "--detach", worktreePath, commitSha], { cwd, stdio: ["ignore", "pipe", "pipe"], timeout: 30_000 });
153
+ execFileSync("git", ["worktree", "add", "--detach", worktreePath, commitSha], {
154
+ cwd,
155
+ stdio: ["ignore", "pipe", "pipe"],
156
+ timeout: 30_000,
157
+ });
112
158
  return { worktreePath, commitSha, cleanup };
113
159
  } catch (error) {
114
160
  cleanup();
@@ -126,7 +172,11 @@ export function prepareVerificationWorktree(cwd: string, commitSha: string): Ver
126
172
  * If preparation fails, the function rethrows WITHOUT calling fn — caller
127
173
  * must handle the prep failure (typically by falling back to non-sandboxed).
128
174
  */
129
- export async function withVerificationWorktree<T>(cwd: string, commitSha: string, fn: (worktree: VerificationWorktree) => Promise<T> | T): Promise<T> {
175
+ export async function withVerificationWorktree<T>(
176
+ cwd: string,
177
+ commitSha: string,
178
+ fn: (worktree: VerificationWorktree) => Promise<T> | T,
179
+ ): Promise<T> {
130
180
  const worktree = prepareVerificationWorktree(cwd, commitSha);
131
181
  try {
132
182
  return await fn(worktree);
@@ -12,7 +12,11 @@ export function createWorkerHeartbeat(workerId: string, pid?: number, now = new
12
12
  return { workerId, pid, lastSeenAt: now.toISOString(), alive: true };
13
13
  }
14
14
 
15
- export function touchWorkerHeartbeat(heartbeat: WorkerHeartbeatState, updates: Partial<Omit<WorkerHeartbeatState, "workerId">> = {}, now = new Date()): WorkerHeartbeatState {
15
+ export function touchWorkerHeartbeat(
16
+ heartbeat: WorkerHeartbeatState,
17
+ updates: Partial<Omit<WorkerHeartbeatState, "workerId">> = {},
18
+ now = new Date(),
19
+ ): WorkerHeartbeatState {
16
20
  return { ...heartbeat, ...updates, lastSeenAt: now.toISOString() };
17
21
  }
18
22
 
@@ -1,5 +1,13 @@
1
1
  export type WorkerLifecycleState = "spawning" | "trust_required" | "ready_for_prompt" | "running" | "finished" | "failed";
2
- export type StartupFailureClassification = "trust_required" | "prompt_misdelivery" | "prompt_acceptance_timeout" | "transport_dead" | "worker_crashed" | "rate_limited" | "provider_error" | "unknown";
2
+ export type StartupFailureClassification =
3
+ | "trust_required"
4
+ | "prompt_misdelivery"
5
+ | "prompt_acceptance_timeout"
6
+ | "transport_dead"
7
+ | "worker_crashed"
8
+ | "rate_limited"
9
+ | "provider_error"
10
+ | "unknown";
3
11
 
4
12
  export interface WorkerStartupEvidence {
5
13
  lastLifecycleState: WorkerLifecycleState;
@@ -16,17 +24,28 @@ export interface WorkerStartupEvidence {
16
24
 
17
25
  export function detectTrustPrompt(text: string): boolean {
18
26
  const lowered = text.toLowerCase();
19
- return lowered.includes("do you trust") || lowered.includes("trust this") || lowered.includes("untrusted") || lowered.includes("workspace trust") || lowered.includes("allow this folder");
27
+ return (
28
+ lowered.includes("do you trust") ||
29
+ lowered.includes("trust this") ||
30
+ lowered.includes("untrusted") ||
31
+ lowered.includes("workspace trust") ||
32
+ lowered.includes("allow this folder")
33
+ );
20
34
  }
21
35
 
22
36
  export function classifyStartupFailure(evidence: Omit<WorkerStartupEvidence, "classification">): StartupFailureClassification {
23
37
  if (evidence.stderrPreview && /429|rate.?limit/i.test(evidence.stderrPreview)) return "rate_limited";
24
- if (evidence.stderrPreview && /5\d{2}|server.?error|internal.?error|provider.?error/i.test(evidence.stderrPreview)) return "provider_error";
38
+ if (evidence.stderrPreview && /5\d{2}|server.?error|internal.?error|provider.?error/i.test(evidence.stderrPreview))
39
+ return "provider_error";
25
40
  if (!evidence.transportHealthy) return "transport_dead";
26
41
  if (evidence.trustPromptDetected || evidence.lastLifecycleState === "trust_required") return "trust_required";
27
42
  if (evidence.promptSentAt && !evidence.promptAccepted && evidence.childProcessAlive) return "prompt_acceptance_timeout";
28
43
  if (evidence.promptSentAt && !evidence.promptAccepted && !evidence.childProcessAlive) return "worker_crashed";
29
- if (evidence.stderrPreview?.toLowerCase().includes("command not found") || evidence.stderrPreview?.toLowerCase().includes("not recognized")) return "prompt_misdelivery";
44
+ if (
45
+ evidence.stderrPreview?.toLowerCase().includes("command not found") ||
46
+ evidence.stderrPreview?.toLowerCase().includes("not recognized")
47
+ )
48
+ return "prompt_misdelivery";
30
49
  if (!evidence.childProcessAlive && evidence.lastLifecycleState !== "finished") return "worker_crashed";
31
50
  return "unknown";
32
51
  }
@@ -45,7 +64,12 @@ export function createStartupEvidence(input: {
45
64
  const trustPromptDetected = detectTrustPrompt(stderrPreview ?? "");
46
65
  const childProcessAlive = input.exitCode === undefined || input.exitCode === null ? !input.finishedAt : false;
47
66
  const base: Omit<WorkerStartupEvidence, "classification"> = {
48
- lastLifecycleState: input.error || (input.exitCode !== undefined && input.exitCode !== null && input.exitCode !== 0) ? "failed" : input.finishedAt ? "finished" : "running",
67
+ lastLifecycleState:
68
+ input.error || (input.exitCode !== undefined && input.exitCode !== null && input.exitCode !== 0)
69
+ ? "failed"
70
+ : input.finishedAt
71
+ ? "finished"
72
+ : "running",
49
73
  command: input.command,
50
74
  promptSentAt: input.promptSentAt?.toISOString(),
51
75
  promptAccepted: input.promptAccepted ?? !input.error,
@@ -84,20 +84,22 @@ export function getPhaseInputs(machine: WorkflowStateMachine, phaseIndex: number
84
84
  * 3. All declared `inputs` for the target phase must appear in `completedArtifacts`.
85
85
  * 4. If a custom `guard` is supplied on a transition, it must also approve.
86
86
  */
87
- export function canTransitionToPhase(
88
- machine: WorkflowStateMachine,
89
- phaseIndex: number,
90
- context: PhaseGuardContext,
91
- ): GuardResult {
87
+ export function canTransitionToPhase(machine: WorkflowStateMachine, phaseIndex: number, context: PhaseGuardContext): GuardResult {
92
88
  if (phaseIndex < 0 || phaseIndex >= machine.phases.length) {
93
- return { allowed: false, reason: `Phase index ${phaseIndex} is out of bounds (0..${machine.phases.length - 1}).` };
89
+ return {
90
+ allowed: false,
91
+ reason: `Phase index ${phaseIndex} is out of bounds (0..${machine.phases.length - 1}).`,
92
+ };
94
93
  }
95
94
 
96
95
  // Previous phase must have completed (or been skipped).
97
96
  if (phaseIndex > 0) {
98
97
  const prevStatus = context.previousPhaseStatus;
99
98
  if (prevStatus !== "completed" && prevStatus !== "skipped") {
100
- return { allowed: false, reason: `Previous phase status is '${prevStatus}'; expected 'completed' or 'skipped'.` };
99
+ return {
100
+ allowed: false,
101
+ reason: `Previous phase status is '${prevStatus}'; expected 'completed' or 'skipped'.`,
102
+ };
101
103
  }
102
104
  }
103
105
 
@@ -107,7 +109,10 @@ export function canTransitionToPhase(
107
109
  const artifactSet = new Set(context.completedArtifacts);
108
110
  const missing = phase.inputs.filter((input) => !artifactSet.has(input));
109
111
  if (missing.length > 0) {
110
- return { allowed: false, reason: `Missing required artifacts: ${missing.join(", ")}.` };
112
+ return {
113
+ allowed: false,
114
+ reason: `Missing required artifacts: ${missing.join(", ")}.`,
115
+ };
111
116
  }
112
117
  }
113
118
 
@@ -124,7 +129,10 @@ export function validatePhasePreconditions(
124
129
  ): { ready: boolean; blocking: string[] } {
125
130
  const phase = machine.phases[machine.currentPhaseIndex];
126
131
  if (!phase) {
127
- return { ready: false, blocking: [`No phase at index ${machine.currentPhaseIndex}.`] };
132
+ return {
133
+ ready: false,
134
+ blocking: [`No phase at index ${machine.currentPhaseIndex}.`],
135
+ };
128
136
  }
129
137
 
130
138
  const artifactSet = new Set(context.completedArtifacts);
@@ -31,17 +31,7 @@
31
31
  */
32
32
 
33
33
  import { createHash } from "node:crypto";
34
- import {
35
- existsSync,
36
- mkdirSync,
37
- readFileSync,
38
- readdirSync,
39
- unlinkSync,
40
- openSync,
41
- closeSync,
42
- statSync,
43
- writeFileSync,
44
- } from "node:fs";
34
+ import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
45
35
  import * as path from "node:path";
46
36
  import { atomicWriteJson } from "../state/atomic-write.ts";
47
37
  import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
@@ -69,7 +59,10 @@ export const defaultStartTimeResolver: StartTimeResolver = (pid: number): number
69
59
  const stat = readFileSync(`/proc/${pid}/stat`, "utf-8");
70
60
  const lastParen = stat.lastIndexOf(")");
71
61
  if (lastParen === -1) return undefined;
72
- const fieldsAfterComm = stat.slice(lastParen + 1).trim().split(/\s+/);
62
+ const fieldsAfterComm = stat
63
+ .slice(lastParen + 1)
64
+ .trim()
65
+ .split(/\s+/);
73
66
  // starttime is at index 19 (the 20th field after comm) of /proc/<pid>/stat.
74
67
  const startTimeClockTicks = Number(fieldsAfterComm[19]);
75
68
  if (!Number.isFinite(startTimeClockTicks)) return undefined;
@@ -202,7 +195,11 @@ function claimLock(lockPath: string, contents: WorkspaceLockContents, staleRecla
202
195
  // Caller verified the existing lock is logically stale; remove it and claim. A concurrent
203
196
  // reclaimer might re-create between our unlink and our open — that's fine, we lose the race
204
197
  // and return false, falling through to the queue path.
205
- try { unlinkSync(lockPath); } catch { /* best-effort */ }
198
+ try {
199
+ unlinkSync(lockPath);
200
+ } catch {
201
+ /* best-effort */
202
+ }
206
203
  return tryCreate();
207
204
  }
208
205
  if (tryCreate()) return true;
@@ -210,10 +207,16 @@ function claimLock(lockPath: string, contents: WorkspaceLockContents, staleRecla
210
207
  try {
211
208
  const stat = statSync(lockPath);
212
209
  if (Date.now() - stat.mtimeMs > staleReclaimMs) {
213
- try { unlinkSync(lockPath); } catch { /* fall through */ }
210
+ try {
211
+ unlinkSync(lockPath);
212
+ } catch {
213
+ /* fall through */
214
+ }
214
215
  return tryCreate();
215
216
  }
216
- } catch { /* fall through to false */ }
217
+ } catch {
218
+ /* fall through to false */
219
+ }
217
220
  return false;
218
221
  }
219
222
 
@@ -281,9 +284,7 @@ export async function acquireWorkspaceLock(
281
284
  while (true) {
282
285
  // Poll-loop: re-check the lock each tick until free/stale or aborted.
283
286
  if (opts.signal?.aborted) {
284
- throw new Error(
285
- `workspace lock acquisition aborted for goal ${goalId} (cwd=${cwd})`,
286
- );
287
+ throw new Error(`workspace lock acquisition aborted for goal ${goalId} (cwd=${cwd})`);
287
288
  }
288
289
  const existing = readLock(lockPath);
289
290
  // Classify the existing lock: "absent" / "stale" (PID recycled or heartbeat dead) / "live".
@@ -293,7 +294,9 @@ export async function acquireWorkspaceLock(
293
294
  // acquireWorkspaceLock poll loop would re-queue forever = hang.
294
295
  const existingKind: "absent" | "stale" | "live" = !existing
295
296
  ? "absent"
296
- : (isLockStale(existing, resolveStartTime, heartbeatStaleMs, now()).stale ? "stale" : "live");
297
+ : isLockStale(existing, resolveStartTime, heartbeatStaleMs, now()).stale
298
+ ? "stale"
299
+ : "live";
297
300
  if (existingKind !== "live") {
298
301
  // Claim the lock (covers both no-lock and stale-lock cases).
299
302
  // Cold-review #3 NIT #N1 fix: claim via O_EXCL (claimLock), NOT temp+rename — two
@@ -324,9 +327,7 @@ export async function acquireWorkspaceLock(
324
327
  }
325
328
  // Lock is held and live.
326
329
  if (opts.failOnWorkspaceBusy) {
327
- throw new Error(
328
- `workspace busy: cwd=${cwd} held by goalId=${existing!.goalId} (pid=${existing!.pid})`,
329
- );
330
+ throw new Error(`workspace busy: cwd=${cwd} held by goalId=${existing!.goalId} (pid=${existing!.pid})`);
330
331
  }
331
332
  // Queue: wait for the next poll interval, then re-check.
332
333
  await sleepOrAbort(pollMs, opts.signal);
@@ -338,20 +339,10 @@ export async function acquireWorkspaceLock(
338
339
  * (goalId, pid, startTime). A stale handle (whose lock was reclaimed and
339
340
  * re-acquired by another goal) must NOT delete the new owner's lock.
340
341
  */
341
- function safeRelease(
342
- lockPath: string,
343
- goalId: string,
344
- pid: number,
345
- writtenStartTime: number | undefined,
346
- ): void {
342
+ function safeRelease(lockPath: string, goalId: string, pid: number, writtenStartTime: number | undefined): void {
347
343
  try {
348
344
  const current = readLock(lockPath);
349
- if (
350
- current &&
351
- current.goalId === goalId &&
352
- current.pid === pid &&
353
- current.startTime === writtenStartTime
354
- ) {
345
+ if (current && current.goalId === goalId && current.pid === pid && current.startTime === writtenStartTime) {
355
346
  unlinkSync(lockPath);
356
347
  }
357
348
  } catch {
@@ -390,7 +381,11 @@ function sleepOrAbort(ms: number, signal?: AbortSignal): Promise<void> {
390
381
  */
391
382
  export function isWorkspaceBusy(
392
383
  cwd: string,
393
- opts: { startTimeResolver?: StartTimeResolver; heartbeatStaleMs?: number; now?: () => number } = {},
384
+ opts: {
385
+ startTimeResolver?: StartTimeResolver;
386
+ heartbeatStaleMs?: number;
387
+ now?: () => number;
388
+ } = {},
394
389
  ): string | undefined {
395
390
  const lockPath = workspaceLockPath(cwd);
396
391
  const resolveStartTime = opts.startTimeResolver ?? defaultStartTimeResolver;