pi-crew 0.9.51 → 0.9.53

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 (611) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +216 -20
  3. package/dist/build-meta.json +2090 -1638
  4. package/dist/index.mjs +18057 -17119
  5. package/docs/actions-reference.md +431 -1
  6. package/index.ts +39 -10
  7. package/package.json +19 -9
  8. package/schema.json +41 -0
  9. package/scripts/check-bundle-size.mjs +38 -0
  10. package/scripts/test-runner.mjs +11 -1
  11. package/dist/index.mjs.map +0 -7
  12. package/docs/FEATURE_INTAKE.md +0 -126
  13. package/docs/HARNESS.md +0 -87
  14. package/docs/HARNESS_BACKLOG.md +0 -89
  15. package/docs/PROJECT_REVIEW.md +0 -270
  16. package/docs/PROJECT_REVIEW_FIXES.md +0 -342
  17. package/docs/PROJECT_REVIEW_ROUND4.md +0 -156
  18. package/docs/PROJECT_REVIEW_ROUND5.md +0 -86
  19. package/docs/REVIEW-FINDINGS-2026-06 +0 -357
  20. package/docs/REVIEW-FINDINGS-2026-07-CORE.md +0 -139
  21. package/docs/REVIEW-FINDINGS-2026-07.md +0 -125
  22. package/docs/TEST_MATRIX.md +0 -51
  23. package/docs/archive/README.md +0 -91
  24. package/docs/archive/followup-review-round3-2026-05-12.md +0 -342
  25. package/docs/archive/followup-review-round4-2026-05-13.md +0 -107
  26. package/docs/archive/pi-crew-bugs.md +0 -961
  27. package/docs/archive/pi-crew-test-final.md +0 -120
  28. package/docs/archive/pi-crew-test-results.md +0 -260
  29. package/docs/archive/pi-crew-test-round2.md +0 -136
  30. package/docs/archive/pi-crew-test-round4.md +0 -100
  31. package/docs/archive/pi-crew-test-round5.md +0 -70
  32. package/docs/archive/pi-crew-test-round6.md +0 -110
  33. package/docs/archive/pi-crew-v0.5.10-audit-fix-plan.md +0 -60
  34. package/docs/archive/pi-crew-v0.5.11-audit-fix-plan.md +0 -92
  35. package/docs/archive/pi-crew-v0.5.12-audit-fix-plan.md +0 -76
  36. package/docs/archive/pi-crew-v0.5.13-audit-fix-plan.md +0 -75
  37. package/docs/archive/pi-crew-v0.5.14-audit-fix-plan.md +0 -75
  38. package/docs/archive/pi-crew-v0.5.16-audit-fix-plan.md +0 -35
  39. package/docs/archive/pi-crew-v0.5.17-audit-fix-plan.md +0 -80
  40. package/docs/archive/pi-crew-v0.5.5-audit-fix-plan.md +0 -133
  41. package/docs/archive/pi-crew-v0.5.9-audit-fix-plan.md +0 -88
  42. package/docs/archive/pi-mono-opportunities.md +0 -969
  43. package/docs/archive/pi-mono-review.md +0 -291
  44. package/docs/archive/pi-subagent4-comparison.md +0 -261
  45. package/docs/archive/pi-subagents3-deep-analysis.md +0 -508
  46. package/docs/bugs/bug-021-notification-badge-counter-misleading.md +0 -293
  47. package/docs/bugs/bug-023-chain-windows-path-resolution.md +0 -106
  48. package/docs/bugs/bug-quota-display-truncation.md +0 -223
  49. package/docs/bugs/cross-session-notification-leakage.md +0 -82
  50. package/docs/code-review-2026-05-11.md +0 -592
  51. package/docs/coding-agent-optimization.md +0 -268
  52. package/docs/comparison-pi-subagents-vs-pi-crew.md +0 -303
  53. package/docs/decisions/0001-durable-state.md +0 -41
  54. package/docs/decisions/0002-child-process-for-async.md +0 -42
  55. package/docs/decisions/0003-depth-guard.md +0 -36
  56. package/docs/decisions/0004-execfile-over-exec.md +0 -34
  57. package/docs/decisions/0005-no-parameter-properties.md +0 -49
  58. package/docs/decisions/0006-publish-bundled-esm.md +0 -63
  59. package/docs/decisions/0007-active-run-binary-index.md +0 -54
  60. package/docs/decisions/0008-child-pi-warm-pool.md +0 -61
  61. package/docs/decisions/2026-07-21-broker-phase4-default-on.md +0 -77
  62. package/docs/decisions/2026-07-21-broker-windows-perms.md +0 -91
  63. package/docs/decisions/2026-07-22-broker-phase4-gated-on.md +0 -99
  64. package/docs/decisions/2026-07-24-oidc-trusted-publishing.md +0 -112
  65. package/docs/decisions/2026-07-26-c6-mascot-visibility-not-wired.md +0 -84
  66. package/docs/decisions/README.md +0 -26
  67. package/docs/deep-review-report.md +0 -384
  68. package/docs/distillation/cybersecurity-patterns.md +0 -294
  69. package/docs/feature-analysis-subagent4.md +0 -305
  70. package/docs/fix-plan-disabletools-exit-null.md +0 -219
  71. package/docs/fixes/BATCH_A_H1_H2.md +0 -86
  72. package/docs/fixes/bug-006-foreground-cancel-concurrent.md +0 -78
  73. package/docs/fixes/bug-007-async-notifier-stale-ctx.md +0 -112
  74. package/docs/fixes/bug-008-child-process-silent-timeout.md +0 -100
  75. package/docs/fixes/bug-009-executor-yield-limit-needs-attention.md +0 -75
  76. package/docs/fixes/bug-010-child-process-api-key-filtered.md +0 -109
  77. package/docs/fixes/bug-011-spawn-pi-enoent.md +0 -92
  78. package/docs/fixes/bug-012-essential-env-stripped.md +0 -89
  79. package/docs/fixes/bug-013-background-runner-death.md +0 -84
  80. package/docs/fixes/bug-014-infinite-retry-loop-needs-attention.md +0 -82
  81. package/docs/fixes/bug-015-background-runner-sigterm.md +0 -65
  82. package/docs/fixes/bug-017-background-runner-session-shutdown.md +0 -66
  83. package/docs/fixes/bug-017-background-runner-sigkill-double-fork.md +0 -28
  84. package/docs/fixes/bug-018-child-pi-worker-stdin-hang.md +0 -61
  85. package/docs/fixes/bug-019-phantom-runs-temp-workspace.md +0 -52
  86. package/docs/fixes/bug-020-infinite-retry-loop-needs-attention.md +0 -47
  87. package/docs/fixes/v0.9.10/locks-fix-verify.md +0 -3
  88. package/docs/fixes/v0.9.10/smoke-test.md +0 -12
  89. package/docs/followup-plan-2026-05-12.md +0 -463
  90. package/docs/followup-review-2026-05-12.md +0 -297
  91. package/docs/goals.md +0 -59
  92. package/docs/implementation-plan-top3.md +0 -333
  93. package/docs/issue-29-analysis.md +0 -189
  94. package/docs/live-mailbox-runtime.md +0 -36
  95. package/docs/migration/atomic-write-v2-migration.md +0 -299
  96. package/docs/next-upgrade-roadmap.md +0 -808
  97. package/docs/oh-my-pi-research.md +0 -509
  98. package/docs/optimization-plan-2026-07.md +0 -366
  99. package/docs/optimization-plan.md +0 -644
  100. package/docs/patterns/command-agent-skill.md +0 -71
  101. package/docs/perf/baseline-2026-05.md +0 -113
  102. package/docs/perf/final-report-2026-05.md +0 -206
  103. package/docs/perf/optimization-plan-2026-07-verified.md +0 -396
  104. package/docs/perf/performance-audit-report-2026-07.md +0 -948
  105. package/docs/perf/performance-review-2026-07.md +0 -287
  106. package/docs/perf/sprint-1-report.md +0 -71
  107. package/docs/perf/sprint-2-report.md +0 -81
  108. package/docs/perf/sprint-2.5-report.md +0 -53
  109. package/docs/perf/sprint-3-report.md +0 -36
  110. package/docs/perf/sprint-4-report.md +0 -47
  111. package/docs/perf/sprint-5-report.md +0 -51
  112. package/docs/perf/sprint-6-report.md +0 -94
  113. package/docs/perf/sprint-7-report.md +0 -74
  114. package/docs/perf/upgrade-plan-2026-05.md +0 -147
  115. package/docs/performance-optimization-execution-plan.md +0 -520
  116. package/docs/phase4-triage.md +0 -58
  117. package/docs/pi-crew-investigation-report.md +0 -411
  118. package/docs/product/README.md +0 -31
  119. package/docs/product/platform.md +0 -27
  120. package/docs/product/runtime-safety.md +0 -37
  121. package/docs/product/team-run.md +0 -39
  122. package/docs/product/team-tool.md +0 -37
  123. package/docs/runtime-analysis-child-vs-live.md +0 -171
  124. package/docs/runtime-migration-in-process-analysis.md +0 -250
  125. package/docs/skills/REFERENCE.md +0 -138
  126. package/docs/stories/README.md +0 -32
  127. package/docs/stories/US-DEPS-major-upgrade.md +0 -62
  128. package/docs/stories/backlog.md +0 -36
  129. package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +0 -1270
  130. package/docs/templates/decision.md +0 -27
  131. package/docs/templates/story.md +0 -44
  132. package/docs/templates/validation-report.md +0 -32
  133. package/docs/ui-optimization-plan.md +0 -447
  134. package/src/adapters/claude-adapter.ts +0 -23
  135. package/src/adapters/codex-adapter.ts +0 -21
  136. package/src/adapters/cursor-adapter.ts +0 -17
  137. package/src/adapters/export-util.ts +0 -143
  138. package/src/adapters/index.ts +0 -15
  139. package/src/adapters/registry.ts +0 -18
  140. package/src/adapters/types.ts +0 -23
  141. package/src/agents/agent-config.ts +0 -194
  142. package/src/agents/agent-search.ts +0 -98
  143. package/src/agents/agent-serializer.ts +0 -38
  144. package/src/agents/discover-agents.ts +0 -623
  145. package/src/benchmark/benchmark-runner.ts +0 -313
  146. package/src/benchmark/feedback-loop.ts +0 -73
  147. package/src/config/config.ts +0 -1269
  148. package/src/config/defaults.ts +0 -193
  149. package/src/config/drift-detector.ts +0 -290
  150. package/src/config/markers.ts +0 -330
  151. package/src/config/resilient-parser.ts +0 -117
  152. package/src/config/role-tools.ts +0 -118
  153. package/src/config/suggestions.ts +0 -75
  154. package/src/config/types.ts +0 -278
  155. package/src/errors.ts +0 -194
  156. package/src/extension/action-suggestions.ts +0 -117
  157. package/src/extension/async-notifier.ts +0 -179
  158. package/src/extension/autonomous-policy.ts +0 -209
  159. package/src/extension/command-completions.ts +0 -120
  160. package/src/extension/context-status-injection.ts +0 -185
  161. package/src/extension/crew-autocomplete.ts +0 -131
  162. package/src/extension/crew-cleanup.ts +0 -168
  163. package/src/extension/crew-input-router.ts +0 -93
  164. package/src/extension/crew-shortcuts.ts +0 -87
  165. package/src/extension/crew-vibes/cat-frames.ts +0 -18
  166. package/src/extension/crew-vibes/config.ts +0 -195
  167. package/src/extension/crew-vibes/figures.ts +0 -101
  168. package/src/extension/crew-vibes/font-detect.ts +0 -71
  169. package/src/extension/crew-vibes/footer.ts +0 -292
  170. package/src/extension/crew-vibes/index.ts +0 -432
  171. package/src/extension/crew-vibes/provider-usage.ts +0 -396
  172. package/src/extension/crew-vibes/render.ts +0 -206
  173. package/src/extension/crew-vibes/speed.ts +0 -286
  174. package/src/extension/cross-extension-rpc.ts +0 -350
  175. package/src/extension/help.ts +0 -62
  176. package/src/extension/import-index.ts +0 -74
  177. package/src/extension/knowledge-injection.ts +0 -402
  178. package/src/extension/management.ts +0 -571
  179. package/src/extension/message-renderers.ts +0 -108
  180. package/src/extension/notification-router.ts +0 -152
  181. package/src/extension/notification-sink.ts +0 -54
  182. package/src/extension/pi-api.ts +0 -56
  183. package/src/extension/plan-orchestrate.ts +0 -302
  184. package/src/extension/project-init.ts +0 -170
  185. package/src/extension/register.ts +0 -113
  186. package/src/extension/registration/artifact-cleanup.ts +0 -20
  187. package/src/extension/registration/command-registration.ts +0 -58
  188. package/src/extension/registration/command-utils.ts +0 -58
  189. package/src/extension/registration/commands.ts +0 -1228
  190. package/src/extension/registration/compaction-guard.ts +0 -333
  191. package/src/extension/registration/context-builder.ts +0 -135
  192. package/src/extension/registration/crash-recovery-cache.ts +0 -54
  193. package/src/extension/registration/foreground-run-controller.ts +0 -238
  194. package/src/extension/registration/hook-registration.ts +0 -119
  195. package/src/extension/registration/lazy-configurers.ts +0 -124
  196. package/src/extension/registration/lifecycle-handlers.ts +0 -923
  197. package/src/extension/registration/lifecycle.ts +0 -259
  198. package/src/extension/registration/observability.ts +0 -322
  199. package/src/extension/registration/registration-types.ts +0 -158
  200. package/src/extension/registration/runtime-cleanup.ts +0 -230
  201. package/src/extension/registration/subagent-helpers.ts +0 -125
  202. package/src/extension/registration/subagent-manager-setup.ts +0 -330
  203. package/src/extension/registration/subagent-tools.ts +0 -456
  204. package/src/extension/registration/team-tool.ts +0 -220
  205. package/src/extension/registration/tool-registration.ts +0 -50
  206. package/src/extension/registration/ui.ts +0 -172
  207. package/src/extension/registration/viewers.ts +0 -121
  208. package/src/extension/registration/wire-cross-extension.ts +0 -37
  209. package/src/extension/result-watcher.ts +0 -139
  210. package/src/extension/rpc-hmac.ts +0 -256
  211. package/src/extension/run-bundle-schema.ts +0 -104
  212. package/src/extension/run-export.ts +0 -97
  213. package/src/extension/run-import.ts +0 -168
  214. package/src/extension/run-index.ts +0 -131
  215. package/src/extension/run-maintenance.ts +0 -190
  216. package/src/extension/session-summary.ts +0 -18
  217. package/src/extension/team-manager-command.ts +0 -153
  218. package/src/extension/team-onboard.ts +0 -177
  219. package/src/extension/team-recommendation.ts +0 -288
  220. package/src/extension/team-tool/anchor.ts +0 -172
  221. package/src/extension/team-tool/api.ts +0 -1244
  222. package/src/extension/team-tool/auto-summarize.ts +0 -142
  223. package/src/extension/team-tool/cache-control.ts +0 -19
  224. package/src/extension/team-tool/cancel.ts +0 -337
  225. package/src/extension/team-tool/chain-dispatch.ts +0 -95
  226. package/src/extension/team-tool/chain-executor.ts +0 -379
  227. package/src/extension/team-tool/config-patch.ts +0 -50
  228. package/src/extension/team-tool/context.ts +0 -149
  229. package/src/extension/team-tool/destructive-gate.ts +0 -52
  230. package/src/extension/team-tool/doctor.ts +0 -431
  231. package/src/extension/team-tool/explain.ts +0 -282
  232. package/src/extension/team-tool/failure-patterns.ts +0 -118
  233. package/src/extension/team-tool/goal-wrap.ts +0 -330
  234. package/src/extension/team-tool/goal.ts +0 -556
  235. package/src/extension/team-tool/handle-schedule.ts +0 -332
  236. package/src/extension/team-tool/handle-settings.ts +0 -520
  237. package/src/extension/team-tool/health-monitor.ts +0 -481
  238. package/src/extension/team-tool/inspect.ts +0 -88
  239. package/src/extension/team-tool/intent-policy.ts +0 -45
  240. package/src/extension/team-tool/lifecycle-actions.ts +0 -615
  241. package/src/extension/team-tool/orchestrate.ts +0 -98
  242. package/src/extension/team-tool/parallel-dispatch.ts +0 -183
  243. package/src/extension/team-tool/plan.ts +0 -50
  244. package/src/extension/team-tool/respond.ts +0 -150
  245. package/src/extension/team-tool/run-not-found.ts +0 -49
  246. package/src/extension/team-tool/run.ts +0 -1035
  247. package/src/extension/team-tool/status.ts +0 -260
  248. package/src/extension/team-tool/workflow-manage.ts +0 -261
  249. package/src/extension/team-tool-types.ts +0 -26
  250. package/src/extension/team-tool.ts +0 -1116
  251. package/src/extension/tool-result.ts +0 -16
  252. package/src/extension/validate-resources.ts +0 -108
  253. package/src/hooks/registry.ts +0 -200
  254. package/src/hooks/types.ts +0 -69
  255. package/src/i18n.ts +0 -212
  256. package/src/observability/correlation.ts +0 -50
  257. package/src/observability/event-bus.ts +0 -86
  258. package/src/observability/event-to-metric.ts +0 -170
  259. package/src/observability/exporters/adapter.ts +0 -27
  260. package/src/observability/exporters/otlp-exporter.ts +0 -356
  261. package/src/observability/exporters/prometheus-exporter.ts +0 -54
  262. package/src/observability/metric-registry.ts +0 -100
  263. package/src/observability/metric-retention.ts +0 -64
  264. package/src/observability/metric-sink.ts +0 -99
  265. package/src/observability/metrics-primitives.ts +0 -219
  266. package/src/plugins/plugin-define.ts +0 -6
  267. package/src/plugins/plugin-registry.ts +0 -32
  268. package/src/plugins/plugins/index.ts +0 -3
  269. package/src/plugins/plugins/nextjs.ts +0 -19
  270. package/src/plugins/plugins/vite.ts +0 -10
  271. package/src/plugins/plugins/vitest.ts +0 -9
  272. package/src/prompt/prompt-runtime.ts +0 -380
  273. package/src/runtime/adaptive-plan.ts +0 -563
  274. package/src/runtime/agent-control.ts +0 -215
  275. package/src/runtime/agent-memory.ts +0 -81
  276. package/src/runtime/agent-observability.ts +0 -122
  277. package/src/runtime/anchor-manager.ts +0 -475
  278. package/src/runtime/async-marker.ts +0 -32
  279. package/src/runtime/async-runner.ts +0 -381
  280. package/src/runtime/attention-events.ts +0 -28
  281. package/src/runtime/auto-summarize.ts +0 -344
  282. package/src/runtime/background-runner.ts +0 -837
  283. package/src/runtime/batch-barrier.ts +0 -147
  284. package/src/runtime/broker-issuer.ts +0 -37
  285. package/src/runtime/cancellation-token.ts +0 -99
  286. package/src/runtime/cancellation.ts +0 -99
  287. package/src/runtime/capability-inventory.ts +0 -137
  288. package/src/runtime/chain-parser.ts +0 -237
  289. package/src/runtime/chain-runner.ts +0 -606
  290. package/src/runtime/checkpoint.ts +0 -291
  291. package/src/runtime/child-pi-constants.ts +0 -42
  292. package/src/runtime/child-pi-kill.ts +0 -180
  293. package/src/runtime/child-pi-pool.ts +0 -68
  294. package/src/runtime/child-pi-spawn.ts +0 -287
  295. package/src/runtime/child-pi-steering.ts +0 -128
  296. package/src/runtime/child-pi-streams.ts +0 -296
  297. package/src/runtime/child-pi-transcript.ts +0 -169
  298. package/src/runtime/child-pi.ts +0 -1105
  299. package/src/runtime/coalesce-tasks.ts +0 -268
  300. package/src/runtime/code-summary.ts +0 -292
  301. package/src/runtime/command-trace.ts +0 -105
  302. package/src/runtime/compact-pipeline.ts +0 -56
  303. package/src/runtime/compact-stages/ansi-strip-stage.ts +0 -25
  304. package/src/runtime/compact-stages/blank-collapse-stage.ts +0 -31
  305. package/src/runtime/compact-stages/deduplicate-stage.ts +0 -34
  306. package/src/runtime/compact-stages/head-snap-stage.ts +0 -57
  307. package/src/runtime/compact-stages/index.ts +0 -23
  308. package/src/runtime/compact-stages/tail-capture-stage.ts +0 -77
  309. package/src/runtime/compact-stages/truncation-stage.ts +0 -74
  310. package/src/runtime/compaction-summary.ts +0 -278
  311. package/src/runtime/completion-guard.ts +0 -207
  312. package/src/runtime/concurrency.ts +0 -58
  313. package/src/runtime/crash-classification.ts +0 -229
  314. package/src/runtime/crash-recovery.ts +0 -577
  315. package/src/runtime/crew-agent-records.ts +0 -593
  316. package/src/runtime/crew-agent-runtime.ts +0 -62
  317. package/src/runtime/crew-broker-child.ts +0 -88
  318. package/src/runtime/crew-broker-client.ts +0 -673
  319. package/src/runtime/crew-broker-tokens.ts +0 -84
  320. package/src/runtime/crew-broker.ts +0 -1276
  321. package/src/runtime/crew-hooks.ts +0 -227
  322. package/src/runtime/cross-extension-rpc.ts +0 -143
  323. package/src/runtime/custom-tools/irc-tool.ts +0 -282
  324. package/src/runtime/custom-tools/submit-result-tool.ts +0 -102
  325. package/src/runtime/deadletter.ts +0 -47
  326. package/src/runtime/delivery-coordinator.ts +0 -211
  327. package/src/runtime/delta-conflict.ts +0 -348
  328. package/src/runtime/deterministic-ast.ts +0 -161
  329. package/src/runtime/diagnostic-export.ts +0 -171
  330. package/src/runtime/direct-run.ts +0 -45
  331. package/src/runtime/dwf-state-store.ts +0 -102
  332. package/src/runtime/dynamic-workflow-context.ts +0 -1016
  333. package/src/runtime/dynamic-workflow-runner.ts +0 -310
  334. package/src/runtime/effectiveness.ts +0 -90
  335. package/src/runtime/errors/crew-errors.ts +0 -162
  336. package/src/runtime/event-stream-bridge.ts +0 -98
  337. package/src/runtime/foreground-control.ts +0 -193
  338. package/src/runtime/foreground-watchdog.ts +0 -132
  339. package/src/runtime/global-worker-cap.ts +0 -96
  340. package/src/runtime/goal-achievement.ts +0 -148
  341. package/src/runtime/goal-evaluator.ts +0 -361
  342. package/src/runtime/goal-loop-runner.ts +0 -828
  343. package/src/runtime/goal-state-store.ts +0 -219
  344. package/src/runtime/green-contract.ts +0 -55
  345. package/src/runtime/group-join.ts +0 -267
  346. package/src/runtime/handoff-manager.ts +0 -598
  347. package/src/runtime/heartbeat-gradient.ts +0 -36
  348. package/src/runtime/heartbeat-watcher.ts +0 -239
  349. package/src/runtime/hidden-handoff.ts +0 -407
  350. package/src/runtime/important-line-classifier.ts +0 -140
  351. package/src/runtime/intercom-bridge.ts +0 -187
  352. package/src/runtime/iteration-hooks.ts +0 -305
  353. package/src/runtime/live-agent-control.ts +0 -136
  354. package/src/runtime/live-agent-manager.ts +0 -704
  355. package/src/runtime/live-control-realtime.ts +0 -56
  356. package/src/runtime/live-extension-bridge.ts +0 -148
  357. package/src/runtime/live-irc.ts +0 -97
  358. package/src/runtime/live-session-health.ts +0 -108
  359. package/src/runtime/live-session-runtime.ts +0 -1139
  360. package/src/runtime/loop-gates.ts +0 -128
  361. package/src/runtime/manifest-cache.ts +0 -375
  362. package/src/runtime/mcp-proxy.ts +0 -105
  363. package/src/runtime/metric-parser.ts +0 -36
  364. package/src/runtime/model-fallback.ts +0 -409
  365. package/src/runtime/model-resolver.ts +0 -126
  366. package/src/runtime/model-scope.ts +0 -158
  367. package/src/runtime/orphan-worker-registry.ts +0 -465
  368. package/src/runtime/output-validator.ts +0 -202
  369. package/src/runtime/overflow-recovery.ts +0 -206
  370. package/src/runtime/parallel-research.ts +0 -68
  371. package/src/runtime/parallel-utils.ts +0 -160
  372. package/src/runtime/parent-guard.ts +0 -134
  373. package/src/runtime/path-overlap.ts +0 -150
  374. package/src/runtime/peer-dep.ts +0 -292
  375. package/src/runtime/per-write-validator.ts +0 -181
  376. package/src/runtime/phase-progress.ts +0 -217
  377. package/src/runtime/phase-tracker.ts +0 -385
  378. package/src/runtime/pi-args.ts +0 -655
  379. package/src/runtime/pi-json-output.ts +0 -170
  380. package/src/runtime/pi-spawn.ts +0 -273
  381. package/src/runtime/pipeline-runner.ts +0 -523
  382. package/src/runtime/plan-templates.ts +0 -201
  383. package/src/runtime/policy-engine.ts +0 -115
  384. package/src/runtime/post-checks.ts +0 -142
  385. package/src/runtime/post-exit-stdio-guard.ts +0 -104
  386. package/src/runtime/process-lifecycle.ts +0 -491
  387. package/src/runtime/process-status.ts +0 -154
  388. package/src/runtime/progress-event-coalescer.ts +0 -44
  389. package/src/runtime/progress-tracker.ts +0 -124
  390. package/src/runtime/prose-compressor.ts +0 -162
  391. package/src/runtime/recovery-recipes.ts +0 -203
  392. package/src/runtime/replace.ts +0 -570
  393. package/src/runtime/resilient-edit.ts +0 -153
  394. package/src/runtime/result-extractor.ts +0 -213
  395. package/src/runtime/retry-executor.ts +0 -100
  396. package/src/runtime/retry-runner.ts +0 -336
  397. package/src/runtime/role-permission.ts +0 -59
  398. package/src/runtime/run-coalesced-task-group.ts +0 -308
  399. package/src/runtime/run-drift.ts +0 -219
  400. package/src/runtime/run-tracker.ts +0 -116
  401. package/src/runtime/runtime-policy.ts +0 -29
  402. package/src/runtime/runtime-resolver.ts +0 -172
  403. package/src/runtime/runtime-warmup.ts +0 -160
  404. package/src/runtime/scheduler.ts +0 -353
  405. package/src/runtime/semaphore.ts +0 -141
  406. package/src/runtime/sensitive-paths.ts +0 -93
  407. package/src/runtime/session-resources.ts +0 -25
  408. package/src/runtime/session-snapshot.ts +0 -59
  409. package/src/runtime/session-usage.ts +0 -79
  410. package/src/runtime/settings-store.ts +0 -155
  411. package/src/runtime/sidechain-output.ts +0 -35
  412. package/src/runtime/single-agent-compose.ts +0 -84
  413. package/src/runtime/skill-effectiveness.ts +0 -524
  414. package/src/runtime/skill-instructions.ts +0 -391
  415. package/src/runtime/stale-reconciler.ts +0 -680
  416. package/src/runtime/stream-preview.ts +0 -184
  417. package/src/runtime/streaming-output.ts +0 -50
  418. package/src/runtime/subagent-manager.ts +0 -561
  419. package/src/runtime/subprocess-tool-registry.ts +0 -70
  420. package/src/runtime/supervisor-contact.ts +0 -64
  421. package/src/runtime/task-display.ts +0 -52
  422. package/src/runtime/task-graph-scheduler.ts +0 -227
  423. package/src/runtime/task-graph.ts +0 -290
  424. package/src/runtime/task-health.ts +0 -83
  425. package/src/runtime/task-id.ts +0 -161
  426. package/src/runtime/task-output-context.ts +0 -602
  427. package/src/runtime/task-packet.ts +0 -268
  428. package/src/runtime/task-quality.ts +0 -199
  429. package/src/runtime/task-runner/capabilities.ts +0 -78
  430. package/src/runtime/task-runner/context-retrieval.ts +0 -163
  431. package/src/runtime/task-runner/live-executor.ts +0 -227
  432. package/src/runtime/task-runner/output-splitter.ts +0 -152
  433. package/src/runtime/task-runner/progress.ts +0 -157
  434. package/src/runtime/task-runner/prompt-builder.ts +0 -278
  435. package/src/runtime/task-runner/prompt-pipeline.ts +0 -88
  436. package/src/runtime/task-runner/result-utils.ts +0 -16
  437. package/src/runtime/task-runner/retrieval-orchestrator.ts +0 -310
  438. package/src/runtime/task-runner/run-projection.ts +0 -125
  439. package/src/runtime/task-runner/state-helpers.ts +0 -176
  440. package/src/runtime/task-runner/tail-read.ts +0 -34
  441. package/src/runtime/task-runner.ts +0 -1420
  442. package/src/runtime/team-runner-artifacts.ts +0 -13
  443. package/src/runtime/team-runner.ts +0 -1993
  444. package/src/runtime/tool-output-pruner.ts +0 -333
  445. package/src/runtime/tool-progress.ts +0 -278
  446. package/src/runtime/usage-tracker.ts +0 -73
  447. package/src/runtime/verification-gates.ts +0 -571
  448. package/src/runtime/verification-integrity.ts +0 -105
  449. package/src/runtime/verification-worktree.ts +0 -186
  450. package/src/runtime/worker-heartbeat.ts +0 -25
  451. package/src/runtime/worker-startup.ts +0 -83
  452. package/src/runtime/workflow-state.ts +0 -195
  453. package/src/runtime/workspace-lock.ts +0 -443
  454. package/src/runtime/workspace-tree.ts +0 -312
  455. package/src/runtime/yield-handler.ts +0 -224
  456. package/src/runtime/zombie-scanner.ts +0 -298
  457. package/src/schema/config-schema.ts +0 -327
  458. package/src/schema/team-tool-schema.ts +0 -415
  459. package/src/schema/validation-types.ts +0 -159
  460. package/src/skills/discover-skills.ts +0 -197
  461. package/src/skills/skill-templates.ts +0 -456
  462. package/src/skills/validate.ts +0 -285
  463. package/src/state/active-run-registry.ts +0 -439
  464. package/src/state/artifact-store.ts +0 -176
  465. package/src/state/atomic-write.ts +0 -903
  466. package/src/state/blob-store.ts +0 -308
  467. package/src/state/contracts.ts +0 -158
  468. package/src/state/crew-init.ts +0 -269
  469. package/src/state/decision-ledger.ts +0 -345
  470. package/src/state/event-log-rotation.ts +0 -342
  471. package/src/state/event-log.ts +0 -1311
  472. package/src/state/event-reconstructor.ts +0 -229
  473. package/src/state/gitignore-manager.ts +0 -46
  474. package/src/state/health-store.ts +0 -79
  475. package/src/state/hook-instinct-bridge.ts +0 -94
  476. package/src/state/hook-integrations.ts +0 -51
  477. package/src/state/instinct-store.ts +0 -275
  478. package/src/state/jsonl-writer.ts +0 -107
  479. package/src/state/locks.ts +0 -562
  480. package/src/state/mailbox.ts +0 -916
  481. package/src/state/observation-store.ts +0 -176
  482. package/src/state/run-cache.ts +0 -193
  483. package/src/state/run-graph.ts +0 -183
  484. package/src/state/run-metrics.ts +0 -165
  485. package/src/state/schedule.ts +0 -166
  486. package/src/state/session-state-map.ts +0 -51
  487. package/src/state/state-store.ts +0 -958
  488. package/src/state/task-claims.ts +0 -61
  489. package/src/state/tiered-eval.ts +0 -480
  490. package/src/state/types-eval.ts +0 -58
  491. package/src/state/types.ts +0 -440
  492. package/src/state/usage.ts +0 -138
  493. package/src/state/worker-atomic-writer.ts +0 -194
  494. package/src/subagents/async-entry.ts +0 -1
  495. package/src/subagents/index.ts +0 -3
  496. package/src/subagents/live/control.ts +0 -1
  497. package/src/subagents/live/manager.ts +0 -1
  498. package/src/subagents/live/realtime.ts +0 -1
  499. package/src/subagents/live/session-runtime.ts +0 -1
  500. package/src/subagents/manager.ts +0 -1
  501. package/src/subagents/spawn.ts +0 -1
  502. package/src/teams/discover-teams.ts +0 -187
  503. package/src/teams/team-config.ts +0 -27
  504. package/src/teams/team-serializer.ts +0 -38
  505. package/src/tools/safe-bash-extension.ts +0 -54
  506. package/src/tools/safe-bash.ts +0 -505
  507. package/src/types/diff.d.ts +0 -18
  508. package/src/types/new-api-types.ts +0 -35
  509. package/src/ui/agent-management-overlay.ts +0 -160
  510. package/src/ui/card-colors.ts +0 -139
  511. package/src/ui/crew-footer.ts +0 -102
  512. package/src/ui/crew-select-list.ts +0 -111
  513. package/src/ui/dashboard-panes/agents-pane.ts +0 -164
  514. package/src/ui/dashboard-panes/cancellation-pane.ts +0 -66
  515. package/src/ui/dashboard-panes/capability-pane.ts +0 -77
  516. package/src/ui/dashboard-panes/health-pane.ts +0 -31
  517. package/src/ui/dashboard-panes/mailbox-pane.ts +0 -35
  518. package/src/ui/dashboard-panes/metrics-pane.ts +0 -37
  519. package/src/ui/dashboard-panes/progress-pane.ts +0 -38
  520. package/src/ui/dashboard-panes/transcript-pane.ts +0 -10
  521. package/src/ui/deploy-bundled-themes.ts +0 -71
  522. package/src/ui/dwf-phase-display.ts +0 -151
  523. package/src/ui/dynamic-border.ts +0 -35
  524. package/src/ui/heartbeat-aggregator.ts +0 -74
  525. package/src/ui/key-utils.ts +0 -42
  526. package/src/ui/keybinding-map.ts +0 -248
  527. package/src/ui/layout-primitives.ts +0 -106
  528. package/src/ui/live-conversation-overlay.ts +0 -183
  529. package/src/ui/live-duration.ts +0 -55
  530. package/src/ui/live-run-sidebar.ts +0 -293
  531. package/src/ui/loaders.ts +0 -6
  532. package/src/ui/mascot.ts +0 -449
  533. package/src/ui/overlays/agent-picker-overlay.ts +0 -65
  534. package/src/ui/overlays/confirm-overlay.ts +0 -60
  535. package/src/ui/overlays/help-overlay.ts +0 -177
  536. package/src/ui/overlays/mailbox-compose-overlay.ts +0 -181
  537. package/src/ui/overlays/mailbox-compose-preview.ts +0 -78
  538. package/src/ui/overlays/mailbox-detail-overlay.ts +0 -154
  539. package/src/ui/pi-ui-compat.ts +0 -68
  540. package/src/ui/powerbar-publisher.ts +0 -459
  541. package/src/ui/render-coalescer.ts +0 -101
  542. package/src/ui/render-diff.ts +0 -160
  543. package/src/ui/render-scheduler.ts +0 -278
  544. package/src/ui/run-action-dispatcher.ts +0 -182
  545. package/src/ui/run-dashboard.ts +0 -904
  546. package/src/ui/run-event-bus.ts +0 -381
  547. package/src/ui/run-snapshot-cache.ts +0 -1057
  548. package/src/ui/settings-overlay.ts +0 -1067
  549. package/src/ui/shared-overlay-scheduler.ts +0 -96
  550. package/src/ui/snapshot-types.ts +0 -90
  551. package/src/ui/spinner.ts +0 -17
  552. package/src/ui/status-colors.ts +0 -143
  553. package/src/ui/syntax-highlight.ts +0 -136
  554. package/src/ui/terminal-status.ts +0 -269
  555. package/src/ui/theme-adapter.ts +0 -271
  556. package/src/ui/theme-discovery.ts +0 -199
  557. package/src/ui/tool-progress-formatter.ts +0 -93
  558. package/src/ui/tool-render.ts +0 -453
  559. package/src/ui/tool-renderers/brief-mode.ts +0 -225
  560. package/src/ui/tool-renderers/index.ts +0 -726
  561. package/src/ui/transcript-cache.ts +0 -128
  562. package/src/ui/transcript-entries.ts +0 -256
  563. package/src/ui/transcript-viewer.ts +0 -424
  564. package/src/ui/widget/index.ts +0 -391
  565. package/src/ui/widget/widget-formatters.ts +0 -182
  566. package/src/ui/widget/widget-model.ts +0 -110
  567. package/src/ui/widget/widget-renderer.ts +0 -182
  568. package/src/ui/widget/widget-types.ts +0 -36
  569. package/src/utils/bm25-search.ts +0 -235
  570. package/src/utils/completion-dedupe.ts +0 -63
  571. package/src/utils/conflict-detect.ts +0 -668
  572. package/src/utils/env-allowlist.ts +0 -30
  573. package/src/utils/env-filter.ts +0 -155
  574. package/src/utils/file-coalescer.ts +0 -98
  575. package/src/utils/fingerprint.ts +0 -180
  576. package/src/utils/frontmatter.ts +0 -70
  577. package/src/utils/fs-watch.ts +0 -37
  578. package/src/utils/gh-protocol.ts +0 -556
  579. package/src/utils/git.ts +0 -260
  580. package/src/utils/guards.ts +0 -107
  581. package/src/utils/ids.ts +0 -24
  582. package/src/utils/incremental-reader.ts +0 -220
  583. package/src/utils/internal-error.ts +0 -10
  584. package/src/utils/names.ts +0 -36
  585. package/src/utils/ndjson.ts +0 -115
  586. package/src/utils/paths.ts +0 -227
  587. package/src/utils/project-detector.ts +0 -160
  588. package/src/utils/redaction.ts +0 -347
  589. package/src/utils/resolve-shell.ts +0 -36
  590. package/src/utils/run-watcher-registry.ts +0 -152
  591. package/src/utils/safe-paths.ts +0 -383
  592. package/src/utils/scan-cache.ts +0 -144
  593. package/src/utils/session-utils.ts +0 -110
  594. package/src/utils/sleep.ts +0 -54
  595. package/src/utils/socket-path.ts +0 -127
  596. package/src/utils/sse-parser.ts +0 -129
  597. package/src/utils/task-name-generator.ts +0 -337
  598. package/src/utils/timings.ts +0 -33
  599. package/src/utils/token-counter.ts +0 -200
  600. package/src/utils/visual.ts +0 -207
  601. package/src/workflows/cost-estimator.ts +0 -34
  602. package/src/workflows/discover-workflows.ts +0 -290
  603. package/src/workflows/intermediate-store.ts +0 -166
  604. package/src/workflows/preflight-validator.ts +0 -168
  605. package/src/workflows/topology-analyzer.ts +0 -203
  606. package/src/workflows/validate-workflow.ts +0 -50
  607. package/src/workflows/workflow-config.ts +0 -70
  608. package/src/workflows/workflow-serializer.ts +0 -32
  609. package/src/worktree/branch-freshness.ts +0 -112
  610. package/src/worktree/cleanup.ts +0 -341
  611. package/src/worktree/worktree-manager.ts +0 -1200
@@ -1,1270 +0,0 @@
1
- # Fallow Patterns Adoption - Implementation Plan
2
-
3
- > **Note:** `atomic-write-v2` / `AtomicWriter` mentioned in this plan was consolidated into `atomic-write.ts` as of v0.9.42. This document is preserved as historical record — the v2 class was never adopted; v1 won on simplicity + symlink-safety + link+unlink atomicity. See `docs/migration/atomic-write-v2-migration.md` for the decision rationale.
4
-
5
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
6
-
7
- **Goal:** Adopt 5 high-value patterns from fallow (Rust static analyzer) into pi-crew: structured error codes, atomic write v2 with fsync, health score system, plugin registry, and stable task ID refinement.
8
-
9
- **Architecture:** 5 independent subsystems — each new file/module is self-contained and backward-compatible with existing pi-crew state layer. No existing file deleted until migration complete.
10
-
11
- **Tech Stack:** TypeScript (Node.js), existing pi-crew state layer (`src/state/`), existing task-graph (`src/runtime/task-graph.ts`), existing task-id (`src/runtime/task-id.ts`)
12
-
13
- ---
14
-
15
- ## File Map
16
-
17
- ```
18
- src/
19
- errors.ts # NEW: CrewError + ErrorCode enum (E001-E006)
20
- state/
21
- atomic-write-v2.ts # NEW: AtomicWriter with fsync + rename (coexists with atomic-write.ts)
22
- health-store.ts # NEW: RunHealth score computation + snapshot persistence
23
- runtime/
24
- task-health.ts # NEW: computeRunHealth(), scoreToGrade(), penalty constants
25
- plugins/
26
- plugin-registry.ts # NEW: Plugin interface + PluginRegistry class
27
- plugin-define.ts # NEW: definePlugin() helper
28
- plugins/
29
- index.ts # NEW: re-exports all built-in plugins
30
- nextjs.ts # NEW: NextJsPlugin
31
- vitest.ts # NEW: VitestPlugin
32
- vite.ts # NEW: VitePlugin
33
- ```
34
-
35
- Existing files (read-only during implementation, migrate after):
36
- - `src/state/atomic-write.ts` — kept until all callers migrate
37
- - `src/state/state-store.ts` — use CrewError after migration
38
- - `src/runtime/task-id.ts` — extend, not replace
39
- - `src/runtime/task-graph.ts` — already has cycle detection + topological sort
40
-
41
- ---
42
-
43
- ## Task 1: CrewError + ErrorCode System
44
-
45
- **Files:**
46
- - Create: `src/errors.ts`
47
- - Test: `test/unit/errors.test.ts` (new)
48
- - Modify: `src/state/state-store.ts:400-420` (error throwing sites)
49
-
50
- - [ ] **Step 1: Write the failing test**
51
-
52
- ```typescript
53
- // test/unit/errors.test.ts
54
- import { describe, it, expect } from "vitest";
55
- import { CrewError, ErrorCode, errors } from "../../src/errors.ts";
56
-
57
- describe("CrewError", () => {
58
- it("formats with error code", () => {
59
- const err = new CrewError(ErrorCode.TaskNotFound, "Task 'xyz' not found");
60
- expect(err.toString()).toBe("error[E003]: Task 'xyz' not found");
61
- });
62
-
63
- it("formats with context", () => {
64
- const err = new CrewError(ErrorCode.FileReadError, "Failed to read manifest.json")
65
- .withContext("while loading run state");
66
- const str = err.toString();
67
- expect(str).toContain("error[E001]:");
68
- expect(str).toContain("context: while loading run state");
69
- });
70
-
71
- it("formats with help", () => {
72
- const err = new CrewError(ErrorCode.ConfigError, "parse failure")
73
- .withHelp("Try running `team init`");
74
- const str = err.toString();
75
- expect(str).toContain("help: Try running `team init`");
76
- });
77
-
78
- it("has default help for E001-E006", () => {
79
- expect(errors.fileRead("x.txt", { code: "ENOENT" } as NodeJS.ErrnoException).help).toBeDefined();
80
- expect(errors.taskNotFound("t1").help).toBeDefined();
81
- expect(errors.config("bad").help).toBeDefined();
82
- });
83
-
84
- it("is instanceof Error", () => {
85
- expect(new CrewError(ErrorCode.FileWriteError, "x")).toBeInstanceOf(Error);
86
- });
87
-
88
- it("factory methods produce correct codes", () => {
89
- expect(errors.fileRead("x", {} as NodeJS.ErrnoException).code).toBe(ErrorCode.FileReadError);
90
- expect(errors.taskNotFound("t1").code).toBe(ErrorCode.TaskNotFound);
91
- expect(errors.invalidStatusTransition("running", "queued").code).toBe(ErrorCode.InvalidStatusTransition);
92
- expect(errors.resourceNotFound("agent", "my-agent").code).toBe(ErrorCode.ResourceNotFound);
93
- });
94
- });
95
- ```
96
-
97
- - [ ] **Step 2: Run test to verify it fails**
98
-
99
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/errors.test.ts`
100
- Expected: FAIL with "Cannot find module '../../src/errors.ts'"
101
-
102
- - [ ] **Step 3: Write minimal implementation**
103
-
104
- ```typescript
105
- // src/errors.ts
106
-
107
- /**
108
- * Error code taxonomy for pi-crew.
109
- * Maps to semantic categories matching fallow's E001-E004 pattern.
110
- */
111
- export enum ErrorCode {
112
- FileReadError = "E001", // Cannot read a file
113
- FileWriteError = "E002", // Cannot write a file
114
- TaskNotFound = "E003", // Referenced task ID does not exist
115
- InvalidStatusTransition = "E004", // Run/task status cannot legally transition
116
- ConfigError = "E005", // Malformed config or missing required field
117
- ResourceNotFound = "E006", // Agent/team/workflow not found in discovery paths
118
- }
119
-
120
- const DEFAULT_HELP: Record<ErrorCode, string | undefined> = {
121
- [ErrorCode.FileReadError]: "Check that the file exists and that the process has read permission.",
122
- [ErrorCode.FileWriteError]: "Check that the disk is not full and that the process has write permission.",
123
- [ErrorCode.TaskNotFound]: "The task may have been removed or the run may be in an inconsistent state. Use `team status` to verify.",
124
- [ErrorCode.InvalidStatusTransition]: "Verify the run status using `team status` before retrying.",
125
- [ErrorCode.ConfigError]: "Check the configuration file for syntax errors or missing required fields.",
126
- [ErrorCode.ResourceNotFound]: "Use `team list` to see available agents, teams, and workflows.",
127
- };
128
-
129
- /**
130
- * Structured error type for pi-crew.
131
- * Display format:
132
- * error[E001]: Failed to read manifest.json: not found
133
- * context: while loading run state
134
- * help: Check that the file exists and that the process has read permission.
135
- */
136
- export class CrewError extends Error {
137
- readonly code: ErrorCode;
138
- readonly help?: string;
139
- private _context?: string;
140
-
141
- constructor(code: ErrorCode, message: string, help?: string) {
142
- super(message);
143
- this.name = "CrewError";
144
- this.code = code;
145
- this.help = help ?? DEFAULT_HELP[code];
146
- Object.defineProperty(this, "message", { enumerable: true });
147
- Object.defineProperty(this, "code", { enumerable: true });
148
- }
149
-
150
- withContext(context: string): this {
151
- this._context = context;
152
- return this;
153
- }
154
-
155
- withHelp(help: string): this {
156
- this.help = help;
157
- return this;
158
- }
159
-
160
- toString(): string {
161
- let out = `error[${this.code}]: ${this.message}`;
162
- if (this._context) out += `\n context: ${this._context}`;
163
- if (this.help) out += `\n help: ${this.help}`;
164
- return out;
165
- }
166
- }
167
-
168
- export const errors = {
169
- fileRead(path: string, source: NodeJS.ErrnoException): CrewError {
170
- return new CrewError(
171
- ErrorCode.FileReadError,
172
- `Failed to read ${path}: ${source.code?.toLowerCase() ?? "unknown"}`,
173
- ).withContext("file system read operation");
174
- },
175
-
176
- fileWrite(path: string, source: NodeJS.ErrnoException): CrewError {
177
- return new CrewError(
178
- ErrorCode.FileWriteError,
179
- `Failed to write ${path}: ${source.code?.toLowerCase() ?? "unknown"}`,
180
- ).withContext("file system write operation");
181
- },
182
-
183
- taskNotFound(taskId: string, runId?: string): CrewError {
184
- const msg = runId
185
- ? `Task '${taskId}' not found in run '${runId}'`
186
- : `Task '${taskId}' not found`;
187
- return new CrewError(ErrorCode.TaskNotFound, msg);
188
- },
189
-
190
- invalidStatusTransition(from: string, to: string): CrewError {
191
- return new CrewError(
192
- ErrorCode.InvalidStatusTransition,
193
- `Invalid run status transition: ${from} → ${to}`,
194
- );
195
- },
196
-
197
- config(message: string): CrewError {
198
- return new CrewError(ErrorCode.ConfigError, message);
199
- },
200
-
201
- resourceNotFound(type: string, name: string): CrewError {
202
- return new CrewError(
203
- ErrorCode.ResourceNotFound,
204
- `${type} '${name}' not found in any discovery path`,
205
- );
206
- },
207
- } as const;
208
- ```
209
-
210
- - [ ] **Step 4: Run test to verify it passes**
211
-
212
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/errors.test.ts`
213
- Expected: PASS
214
-
215
- - [ ] **Step 5: Commit**
216
-
217
- ```bash
218
- git add src/errors.ts test/unit/errors.test.ts
219
- git commit -m "feat: add CrewError with E001-E006 error codes
220
-
221
- Adds structured error type matching fallow's error model.
222
- - ErrorCode enum: FileRead, FileWrite, TaskNotFound, InvalidStatusTransition, ConfigError, ResourceNotFound
223
- - CrewError with builder pattern: withContext(), withHelp()
224
- - Factory constructors in errors namespace
225
- - Display format: error[E001]: message\\n context: ...\\n help: ..."
226
- ```
227
-
228
- ---
229
-
230
- ## Task 2: Atomic Write v2 with fsync
231
-
232
- **Files:**
233
- - Create: `src/state/atomic-write-v2.ts`
234
- - Test: `test/unit/atomic-write-v2.test.ts` (new)
235
- - Modify: `src/state/state-store.ts` (switch one call-site first — manifest write)
236
-
237
- - [ ] **Step 1: Write the failing test**
238
-
239
- ```typescript
240
- // test/unit/atomic-write-v2.test.ts
241
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
242
- import * as fs from "node:fs";
243
- import * as path from "node:path";
244
- import { AtomicWriter } from "../../src/state/atomic-write-v2.ts";
245
-
246
- describe("AtomicWriter", () => {
247
- const tmpDir = fs.mkdtempSync("/tmp/atomic-test-");
248
- const writer = new AtomicWriter(tmpDir);
249
-
250
- afterEach(() => {
251
- // Clean up test files
252
- for (const f of fs.readdirSync(tmpDir)) {
253
- fs.unlinkSync(path.join(tmpDir, f));
254
- }
255
- });
256
-
257
- it("writes file atomically (file exists after write)", () => {
258
- const target = path.join(tmpDir, "test.json");
259
- writer.writeJsonSync(target, { foo: "bar" });
260
- expect(fs.existsSync(target)).toBe(true);
261
- expect(JSON.parse(fs.readFileSync(target, "utf8"))).toEqual({ foo: "bar" });
262
- });
263
-
264
- it("overwrites existing file atomically", () => {
265
- const target = path.join(tmpDir, "existing.json");
266
- fs.writeFileSync(target, '{"old": true}', "utf8");
267
- writer.writeJsonSync(target, { new: true });
268
- expect(JSON.parse(fs.readFileSync(target, "utf8"))).toEqual({ new: true });
269
- });
270
-
271
- it("writes .gitignore to directory on first use", () => {
272
- const subDir = path.join(tmpDir, "sub");
273
- fs.mkdirSync(subDir);
274
- const target = path.join(subDir, "data.json");
275
- writer.writeJsonSync(target, {});
276
- const gitignore = path.join(subDir, ".gitignore");
277
- expect(fs.existsSync(gitignore)).toBe(true);
278
- expect(fs.readFileSync(gitignore, "utf8")).toBe("*\\n");
279
- });
280
-
281
- it("async write works", async () => {
282
- const target = path.join(tmpDir, "async.json");
283
- await writer.writeJsonAsync(target, { async: true });
284
- expect(JSON.parse(fs.readFileSync(target, "utf8"))).toEqual({ async: true });
285
- });
286
-
287
- it("uses UUID in tmp file name", () => {
288
- const target = path.join(tmpDir, "uuid-test.json");
289
- // Write triggers tmp file creation — verify no collision
290
- writer.writeJsonSync(target, { x: 1 });
291
- // Should succeed without errors
292
- expect(fs.existsSync(target)).toBe(true);
293
- });
294
- });
295
- ```
296
-
297
- - [ ] **Step 2: Run test to verify it fails**
298
-
299
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/atomic-write-v2.test.ts`
300
- Expected: FAIL with "Cannot find module '../../src/state/atomic-write-v2.ts'"
301
-
302
- - [ ] **Step 3: Write minimal implementation**
303
-
304
- ```typescript
305
- // src/state/atomic-write-v2.ts
306
-
307
- import * as crypto from "node:crypto";
308
- import * as fs from "node:fs";
309
- import * as path from "node:path";
310
-
311
- /**
312
- * Fallow-inspired atomic writer: write-to-.tmp → fsync → rename.
313
- *
314
- * Key differences from atomic-write.ts:
315
- * - Uses rename() (POSIX-atomic) instead of link()+unlink()
316
- * - Calls fsyncSync() on the temp file before rename
317
- * - Best-effort fsync (failure does not abort)
318
- * - Writes .gitignore to directory on first use
319
- * - UUID-based tmp file to prevent collisions under concurrent writes
320
- */
321
- export class AtomicWriter {
322
- private initializedDirs = new Set<string>();
323
-
324
- constructor(private baseDir: string) {}
325
-
326
- /**
327
- * Synchronously write content atomically to targetPath.
328
- * 1. mkdir -p parent directory
329
- * 2. Write to targetPath.{uuid}.tmp
330
- * 3. fsyncSync the temp file (best-effort)
331
- * 4. rename() to targetPath (POSIX-atomic on same filesystem)
332
- */
333
- writeSync(targetPath: string, content: string): void {
334
- this.ensureParentDir(targetPath);
335
- const tmpPath = this.tmpPath(targetPath);
336
- const fd = fs.openSync(tmpPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
337
- try {
338
- fs.writeSync(fd, content, undefined, "utf8");
339
- // Best-effort fsync — failure does not abort
340
- try { fs.fsyncSync(fd); } catch { /* best-effort */ }
341
- } finally {
342
- fs.closeSync(fd);
343
- }
344
- // rename() is atomic on POSIX for same-filesystem destinations
345
- fs.renameSync(tmpPath, targetPath);
346
- }
347
-
348
- async writeAsync(targetPath: string, content: string): Promise<void> {
349
- await fs.promises.mkdir(path.dirname(targetPath), { recursive: true });
350
- const tmpPath = this.tmpPath(targetPath);
351
- const fd = await fs.promises.open(tmpPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
352
- try {
353
- await fd.writeFile(content, "utf8");
354
- try { await fd.sync(); } catch { /* best-effort */ }
355
- } finally {
356
- await fd.close();
357
- }
358
- await fs.promises.rename(tmpPath, targetPath);
359
- }
360
-
361
- writeJsonSync<T>(targetPath: string, value: T): void {
362
- this.writeSync(targetPath, JSON.stringify(value, null, 2) + "\n");
363
- }
364
-
365
- async writeJsonAsync<T>(targetPath: string, value: T): Promise<void> {
366
- await this.writeAsync(targetPath, JSON.stringify(value, null, 2) + "\n");
367
- }
368
-
369
- private tmpPath(targetPath: string): string {
370
- const uuid = crypto.randomUUID();
371
- return `${targetPath}.${uuid}.tmp`;
372
- }
373
-
374
- private ensureParentDir(targetPath: string): void {
375
- const dir = path.dirname(targetPath);
376
- fs.mkdirSync(dir, { recursive: true });
377
- this.ensureGitignore(dir);
378
- }
379
-
380
- private ensureGitignore(dir: string): void {
381
- if (this.initializedDirs.has(dir)) return;
382
- this.initializedDirs.add(dir);
383
- const gitignorePath = path.join(dir, ".gitignore");
384
- try { fs.accessSync(gitignorePath); } catch {
385
- try { fs.writeFileSync(gitignorePath, "*\n", "utf8"); } catch { /* best-effort */ }
386
- }
387
- }
388
- }
389
- ```
390
-
391
- - [ ] **Step 4: Run test to verify it passes**
392
-
393
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/atomic-write-v2.test.ts`
394
- Expected: PASS
395
-
396
- - [ ] **Step 5: Commit**
397
-
398
- ```bash
399
- git add src/state/atomic-write-v2.ts test/unit/atomic-write-v2.test.ts
400
- git commit -m "feat: add AtomicWriter v2 with fsync + rename pattern
401
-
402
- Fallow-inspired atomic write:
403
- - write-to-.tmp → fsyncSync() → rename() (POSIX-atomic)
404
- - Best-effort fsync (failure does not abort)
405
- - UUID-based tmp filename prevents concurrent write collisions
406
- - .gitignore written to directory on first use
407
- Coexists with atomic-write.ts during migration."
408
- ```
409
-
410
- ---
411
-
412
- ## Task 3: Health Score System
413
-
414
- **Files:**
415
- - Create: `src/runtime/task-health.ts`
416
- - Create: `src/state/health-store.ts`
417
- - Test: `test/unit/task-health.test.ts` (new)
418
-
419
- - [ ] **Step 1: Write the failing test**
420
-
421
- ```typescript
422
- // test/unit/task-health.test.ts
423
- import { describe, it, expect } from "vitest";
424
- import { computeRunHealth, scoreToGrade } from "../../src/runtime/task-health.ts";
425
- import type { TeamRunManifest } from "../../src/state/types.ts";
426
-
427
- describe("scoreToGrade", () => {
428
- it("maps 90-100 to A", () => {
429
- expect(scoreToGrade(95)).toBe("A");
430
- expect(scoreToGrade(90)).toBe("A");
431
- });
432
- it("maps 70-89 to B", () => {
433
- expect(scoreToGrade(70)).toBe("B");
434
- expect(scoreToGrade(89)).toBe("B");
435
- });
436
- it("maps 50-69 to C", () => {
437
- expect(scoreToGrade(50)).toBe("C");
438
- expect(scoreToGrade(69)).toBe("C");
439
- });
440
- it("maps 30-49 to D", () => {
441
- expect(scoreToGrade(30)).toBe("D");
442
- expect(scoreToGrade(49)).toBe("D");
443
- });
444
- it("maps 0-29 to F", () => {
445
- expect(scoreToGrade(0)).toBe("F");
446
- expect(scoreToGrade(29)).toBe("F");
447
- });
448
- });
449
-
450
- describe("computeRunHealth", () => {
451
- it("returns perfect score for all tasks completed", () => {
452
- const manifest = makeManifest([
453
- { id: "t1", status: "completed" },
454
- { id: "t2", status: "completed" },
455
- ]);
456
- const health = computeRunHealth(manifest);
457
- expect(health.score).toBe(100);
458
- expect(health.grade).toBe("A");
459
- expect(health.penalties).toHaveLength(0);
460
- });
461
-
462
- it("applies high-failure-rate penalty", () => {
463
- const manifest = makeManifest([
464
- { id: "t1", status: "completed" },
465
- { id: "t2", status: "failed" },
466
- { id: "t3", status: "failed" },
467
- ]);
468
- const health = computeRunHealth(manifest);
469
- expect(health.score).toBeLessThan(100);
470
- expect(health.penalties.some(p => p.reason === "high-failure-rate")).toBe(true);
471
- });
472
-
473
- it("applies stalled-tasks penalty", () => {
474
- const manifest = makeManifest([
475
- { id: "t1", status: "completed" },
476
- { id: "t2", status: "running", stalledSince: Date.now() - 600_000 }, // stalled 10min
477
- ]);
478
- const health = computeRunHealth(manifest);
479
- expect(health.penalties.some(p => p.reason === "stalled-tasks")).toBe(true);
480
- });
481
-
482
- it("clamps score to [0, 100]", () => {
483
- const manifest = makeManifest([
484
- { id: "t1", status: "failed" },
485
- { id: "t2", status: "failed" },
486
- { id: "t3", status: "failed" },
487
- { id: "t4", status: "failed" },
488
- { id: "t5", status: "failed" },
489
- { id: "t6", status: "running", stalledSince: Date.now() - 600_000 },
490
- ]);
491
- const health = computeRunHealth(manifest);
492
- expect(health.score).toBeGreaterThanOrEqual(0);
493
- expect(health.score).toBeLessThanOrEqual(100);
494
- });
495
-
496
- it("returns deltas from previous snapshot", () => {
497
- // Test that deltas are computed (even if empty when no previous)
498
- const manifest = makeManifest([{ id: "t1", status: "completed" }]);
499
- const health = computeRunHealth(manifest);
500
- expect(Array.isArray(health.deltas)).toBe(true);
501
- });
502
- });
503
- ```
504
-
505
- - [ ] **Step 2: Run test to verify it fails**
506
-
507
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/task-health.test.ts`
508
- Expected: FAIL with "Cannot find module '../../src/runtime/task-health.ts'"
509
-
510
- - [ ] **Step 3: Write minimal implementation**
511
-
512
- ```typescript
513
- // src/runtime/task-health.ts
514
-
515
- export type HealthGrade = "A" | "B" | "C" | "D" | "F";
516
-
517
- export interface HealthPenalty {
518
- reason: string;
519
- deduction: number;
520
- }
521
-
522
- export interface HealthDelta {
523
- metric: string;
524
- delta: number;
525
- trend: "improving" | "degrading" | "stable";
526
- }
527
-
528
- export interface RunHealth {
529
- score: number; // 0-100
530
- grade: HealthGrade;
531
- penalties: HealthPenalty[];
532
- deltas: HealthDelta[];
533
- }
534
-
535
- const STALLED_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
536
-
537
- export function scoreToGrade(score: number): HealthGrade {
538
- if (score >= 90) return "A";
539
- if (score >= 70) return "B";
540
- if (score >= 50) return "C";
541
- if (score >= 30) return "D";
542
- return "F";
543
- }
544
-
545
- export interface TaskSummary {
546
- id: string;
547
- status: string;
548
- stalledSince?: number;
549
- }
550
-
551
- export interface ManifestSummary {
552
- runId: string;
553
- tasks: TaskSummary[];
554
- createdAt: string;
555
- }
556
-
557
- /**
558
- * Compute health score for a run manifest.
559
- * Penalty-based scoring (matching fallow's vital_signs.rs approach).
560
- */
561
- export function computeRunHealth(manifest: ManifestSummary): RunHealth {
562
- const penalties: HealthPenalty[] = [];
563
- const tasks = manifest.tasks;
564
- const taskCount = tasks.length;
565
- if (taskCount === 0) return { score: 100, grade: "A", penalties: [], deltas: [] };
566
-
567
- const failedCount = tasks.filter(t => t.status === "failed").length;
568
- const stalledCount = tasks.filter(t =>
569
- t.stalledSince !== undefined && (Date.now() - t.stalledSince) > STALLED_THRESHOLD_MS
570
- ).length;
571
-
572
- // High failure rate penalty: >20% failures
573
- const failureRate = failedCount / taskCount;
574
- if (failureRate > 0.2) {
575
- penalties.push({ reason: "high-failure-rate", deduction: Math.round(failureRate * 50) });
576
- }
577
-
578
- // Stalled tasks penalty
579
- if (stalledCount > 0) {
580
- penalties.push({ reason: "stalled-tasks", deduction: Math.min(15, stalledCount * 5) });
581
- }
582
-
583
- // Block depth penalty (for tasks with deep dependency chains — using task count as proxy)
584
- if (taskCount > 20) {
585
- penalties.push({ reason: "large-task-count", deduction: Math.min(10, Math.floor((taskCount - 20) / 10)) });
586
- }
587
-
588
- const totalDeduction = penalties.reduce((sum, p) => sum + p.deduction, 0);
589
- const score = Math.max(0, Math.min(100, 100 - totalDeduction));
590
-
591
- return {
592
- score,
593
- grade: scoreToGrade(score),
594
- penalties,
595
- deltas: [], // Filled by health-store when comparing to previous snapshot
596
- };
597
- }
598
- ```
599
-
600
- - [ ] **Step 4: Run test to verify it passes**
601
-
602
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/task-health.test.ts`
603
- Expected: PASS
604
-
605
- - [ ] **Step 5: Write health-store with snapshot persistence**
606
-
607
- ```typescript
608
- // src/state/health-store.ts
609
-
610
- import * as fs from "node:fs";
611
- import * as path from "node:path";
612
- import type { RunHealth } from "../runtime/task-health.ts";
613
- import { computeRunHealth } from "../runtime/task-health.ts";
614
- import type { ManifestSummary } from "../runtime/task-health.ts";
615
-
616
- const HEALTH_DIR = ".crew/state/health";
617
-
618
- export interface HealthSnapshot {
619
- runId: string;
620
- timestamp: number;
621
- gitRef?: string;
622
- score: number;
623
- grade: string;
624
- penalties: { reason: string; deduction: number }[];
625
- }
626
-
627
- export class HealthStore {
628
- constructor(private crewRoot: string) {}
629
-
630
- private healthDir(): string {
631
- return path.join(this.crewRoot, HEALTH_DIR);
632
- }
633
-
634
- /**
635
- * Save a health snapshot for the run.
636
- */
637
- saveSnapshot(manifest: ManifestSummary): HealthSnapshot {
638
- const health = computeRunHealth(manifest as ManifestSummary);
639
- const snapshot: HealthSnapshot = {
640
- runId: manifest.runId,
641
- timestamp: Date.now(),
642
- score: health.score,
643
- grade: health.grade,
644
- penalties: health.penalties,
645
- };
646
- const dir = this.healthDir();
647
- fs.mkdirSync(dir, { recursive: true });
648
- const file = path.join(dir, `${manifest.runId}.json`);
649
- fs.writeFileSync(file, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
650
- return snapshot;
651
- }
652
-
653
- /**
654
- * Load the most recent health snapshot (for trend computation).
655
- */
656
- loadLatestSnapshot(): HealthSnapshot | null {
657
- const dir = this.healthDir();
658
- if (!fs.existsSync(dir)) return null;
659
- const files = fs.readdirSync(dir).filter(f => f.endsWith(".json"));
660
- if (files.length === 0) return null;
661
- // Sort by timestamp descending
662
- files.sort().reverse();
663
- try {
664
- return JSON.parse(fs.readFileSync(path.join(dir, files[0]), "utf8"));
665
- } catch {
666
- return null;
667
- }
668
- }
669
-
670
- /**
671
- * Load all snapshots (for trend analysis).
672
- */
673
- loadAllSnapshots(): HealthSnapshot[] {
674
- const dir = this.healthDir();
675
- if (!fs.existsSync(dir)) return [];
676
- return fs.readdirSync(dir)
677
- .filter(f => f.endsWith(".json"))
678
- .map(f => {
679
- try {
680
- return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
681
- } catch {
682
- return null;
683
- }
684
- })
685
- .filter(Boolean) as HealthSnapshot[];
686
- }
687
- }
688
- ```
689
-
690
- - [ ] **Step 6: Commit**
691
-
692
- ```bash
693
- git add src/runtime/task-health.ts src/state/health-store.ts test/unit/task-health.test.ts
694
- git commit -m "feat: add health score system with penalty-based scoring
695
-
696
- - computeRunHealth() with failure-rate, stalled-tasks, large-task-count penalties
697
- - scoreToGrade() mapping 0-29=F, 30-49=D, 50-69=C, 70-89=B, 90-100=A
698
- - HealthStore with snapshot persistence to .crew/state/health/
699
- - loadLatestSnapshot() for trend computation
700
- Coexists with existing state layer — does not modify manifest schema."
701
- ```
702
-
703
- ---
704
-
705
- ## Task 4: Plugin Registry
706
-
707
- **Files:**
708
- - Create: `src/plugins/plugin-define.ts`
709
- - Create: `src/plugins/plugin-registry.ts`
710
- - Create: `src/plugins/plugins/nextjs.ts`
711
- - Create: `src/plugins/plugins/vitest.ts`
712
- - Create: `src/plugins/plugins/vite.ts`
713
- - Create: `src/plugins/plugins/index.ts`
714
- - Test: `test/unit/plugin-registry.test.ts` (new)
715
-
716
- - [ ] **Step 1: Write the failing test**
717
-
718
- ```typescript
719
- // test/unit/plugin-registry.test.ts
720
- import { describe, it, expect } from "vitest";
721
- import { PluginRegistry } from "../../src/plugins/plugin-registry.ts";
722
- import { definePlugin } from "../../src/plugins/plugin-define.ts";
723
- import { NextJsPlugin } from "../../src/plugins/plugins/nextjs.ts";
724
- import { VitestPlugin } from "../../src/plugins/plugins/vitest.ts";
725
-
726
- describe("PluginRegistry", () => {
727
- it("activates plugin by exact package name", () => {
728
- const registry = new PluginRegistry();
729
- registry.register(definePlugin({
730
- name: "test-plugin",
731
- enablers: ["test-pkg"],
732
- }));
733
- const active = registry.activePlugins(["test-pkg", "other-pkg"]);
734
- expect(active.map(p => p.name)).toContain("test-plugin");
735
- });
736
-
737
- it("activates plugin by prefix match (family)", () => {
738
- const registry = new PluginRegistry();
739
- registry.register(definePlugin({
740
- name: "storybook-plugin",
741
- enablers: ["@storybook/"],
742
- }));
743
- const active = registry.activePlugins(["@storybook/react", "@storybook/vue"]);
744
- expect(active.map(p => p.name)).toContain("storybook-plugin");
745
- });
746
-
747
- it("does not activate plugin with no matching dep", () => {
748
- const registry = new PluginRegistry();
749
- registry.register(definePlugin({
750
- name: "nextjs-plugin",
751
- enablers: ["next"],
752
- }));
753
- const active = registry.activePlugins(["react", "vite"]);
754
- expect(active.map(p => p.name)).not.toContain("nextjs-plugin");
755
- });
756
-
757
- it("NextJsPlugin matches 'next' dependency", () => {
758
- const registry = new PluginRegistry();
759
- registry.register(NextJsPlugin);
760
- const active = registry.activePlugins(["next", "react"]);
761
- expect(active.map(p => p.name)).toContain("nextjs");
762
- });
763
-
764
- it("VitestPlugin matches 'vitest' dependency", () => {
765
- const registry = new PluginRegistry();
766
- registry.register(VitestPlugin);
767
- const active = registry.activePlugins(["vitest", "typescript"]);
768
- expect(active.map(p => p.name)).toContain("vitest");
769
- });
770
- });
771
-
772
- describe("definePlugin", () => {
773
- it("returns the plugin spec unchanged", () => {
774
- const plugin = definePlugin({ name: "my-plugin", enablers: ["my-pkg"] });
775
- expect(plugin.name).toBe("my-plugin");
776
- expect(plugin.enablers).toEqual(["my-pkg"]);
777
- });
778
- });
779
- ```
780
-
781
- - [ ] **Step 2: Run test to verify it fails**
782
-
783
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/plugin-registry.test.ts`
784
- Expected: FAIL with "Cannot find module '../../src/plugins/plugin-registry.ts'"
785
-
786
- - [ ] **Step 3: Write minimal implementation**
787
-
788
- ```typescript
789
- // src/plugins/plugin-define.ts
790
-
791
- /**
792
- * Simplifies plugin struct definition.
793
- * Usage:
794
- * export const MyPlugin = definePlugin({
795
- * name: "my-plugin",
796
- * enablers: ["my-pkg"],
797
- * entryPatterns: ["src/**/*.ts"],
798
- * });
799
- */
800
- export function definePlugin<T extends { name: string; enablers: readonly string[] }>(spec: T): T {
801
- return spec;
802
- }
803
- ```
804
-
805
- ```typescript
806
- // src/plugins/plugin-registry.ts
807
-
808
- import type { Plugin } from "./plugin-define.ts";
809
-
810
- /**
811
- * Unified plugin interface for pi-crew.
812
- * Provides framework/tool detection via package.json dependencies.
813
- */
814
- export interface Plugin {
815
- readonly name: string;
816
- readonly enablers: readonly string[];
817
- readonly entryPatterns?: readonly string[];
818
- readonly configPatterns?: readonly string[];
819
- readonly toolingDependencies?: readonly string[];
820
- readonly pathAliases?: readonly [string, string][];
821
- readonly virtualModulePrefixes?: readonly string[];
822
- }
823
-
824
- /**
825
- * Registry that collects plugins and determines which are active
826
- * based on a package.json's dependencies.
827
- */
828
- export class PluginRegistry {
829
- private plugins: Plugin[] = [];
830
-
831
- register(plugin: Plugin): void {
832
- this.plugins.push(plugin);
833
- }
834
-
835
- /**
836
- * Determine which plugins are active given a list of package dependencies.
837
- * Matching rules:
838
- * - Exact string match: "next" matches "next"
839
- * - Prefix match (ending in "/"): "@storybook/" matches "@storybook/react"
840
- */
841
- activePlugins(allDeps: string[]): Plugin[] {
842
- return this.plugins.filter((p) =>
843
- p.enablers.some((enabler) => {
844
- if (enabler.endsWith("/")) {
845
- return allDeps.some((d) => d.startsWith(enabler));
846
- }
847
- return allDeps.includes(enabler);
848
- }),
849
- );
850
- }
851
-
852
- /**
853
- * Get all registered plugins.
854
- */
855
- allPlugins(): Plugin[] {
856
- return [...this.plugins];
857
- }
858
- }
859
- ```
860
-
861
- - [ ] **Step 4: Write built-in plugins**
862
-
863
- ```typescript
864
- // src/plugins/plugins/nextjs.ts
865
-
866
- import { definePlugin } from "../plugin-define.ts";
867
-
868
- export const NextJsPlugin = definePlugin({
869
- name: "nextjs",
870
- enablers: ["next"],
871
- entryPatterns: [
872
- "src/app/**/*.{ts,tsx}",
873
- "src/pages/**/*.{ts,tsx}",
874
- "src/app/**/page.{ts,tsx}",
875
- "src/app/**/layout.{ts,tsx}",
876
- "src/app/**/route.{ts,tsx}",
877
- "middleware.{ts,js}",
878
- "next.config.{ts,js,mjs}",
879
- ],
880
- configPatterns: ["next.config.{ts,js,mjs}"],
881
- toolingDependencies: ["next", "@next/font", "@next/mdx"],
882
- pathAliases: [["~", "src"]],
883
- virtualModulePrefixes: ["next:"],
884
- });
885
- ```
886
-
887
- ```typescript
888
- // src/plugins/plugins/vitest.ts
889
-
890
- import { definePlugin } from "../plugin-define.ts";
891
-
892
- export const VitestPlugin = definePlugin({
893
- name: "vitest",
894
- enablers: ["vitest"],
895
- entryPatterns: [
896
- "**/*.test.{ts,tsx}",
897
- "**/*.spec.{ts,tsx}",
898
- "src/**/*.test.{ts,tsx}",
899
- "src/**/*.spec.{ts,tsx}",
900
- ],
901
- configPatterns: ["vitest.config.{ts,js,mjs}", "vite.config.ts"],
902
- toolingDependencies: ["vitest"],
903
- });
904
- ```
905
-
906
- ```typescript
907
- // src/plugins/plugins/vite.ts
908
-
909
- import { definePlugin } from "../plugin-define.ts";
910
-
911
- export const VitePlugin = definePlugin({
912
- name: "vite",
913
- enablers: ["vite", "rolldown-vite"],
914
- entryPatterns: [
915
- "src/main.{ts,tsx,js,jsx}",
916
- "src/index.{ts,tsx,js,jsx}",
917
- "index.html",
918
- ],
919
- configPatterns: ["vite.config.{ts,js,mts,mjs}"],
920
- toolingDependencies: ["vite"],
921
- virtualModulePrefixes: ["virtual:"],
922
- });
923
- ```
924
-
925
- ```typescript
926
- // src/plugins/plugins/index.ts
927
-
928
- export { NextJsPlugin } from "./nextjs.ts";
929
- export { VitestPlugin } from "./vitest.ts";
930
- export { VitePlugin } from "./vite.ts";
931
- ```
932
-
933
- - [ ] **Step 5: Run test to verify it passes**
934
-
935
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/plugin-registry.test.ts`
936
- Expected: PASS
937
-
938
- - [ ] **Step 6: Commit**
939
-
940
- ```bash
941
- git add src/plugins/plugin-define.ts src/plugins/plugin-registry.ts src/plugins/plugins/nextjs.ts src/plugins/plugins/vitest.ts src/plugins/plugins/vite.ts src/plugins/plugins/index.ts test/unit/plugin-registry.test.ts
942
- git commit -m "feat: add plugin registry system
943
-
944
- Plugin system for pi-crew framework awareness:
945
- - Plugin interface: name, enablers, entryPatterns, configPatterns, toolingDependencies, pathAliases
946
- - PluginRegistry.activePlugins() with exact + prefix matching
947
- - definePlugin() helper function
948
- - Built-in plugins: NextJsPlugin, VitestPlugin, VitePlugin
949
- Matches fallow's plugin architecture pattern."
950
- ```
951
-
952
- ---
953
-
954
- ## Task 5: Stable Task ID Refinement
955
-
956
- **Files:**
957
- - Modify: `src/runtime/task-id.ts` (extend generateTaskHashId)
958
- - Test: modify `test/unit/task-id.test.ts` (add tests)
959
-
960
- - [ ] **Step 1: Check existing tests**
961
-
962
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/task-id.test.ts 2>&1 | head -20`
963
-
964
- Expected: RUNNING (test file may not exist yet)
965
-
966
- - [ ] **Step 2: Add generateTaskHashId tests**
967
-
968
- ```typescript
969
- // Add to test/unit/task-id.test.ts (or create if doesn't exist)
970
- import { describe, it, expect } from "vitest";
971
- import { generateTaskHashId, hashToBase36, childId, parseHierarchicalId } from "../../src/runtime/task-id.ts";
972
-
973
- describe("generateTaskHashId", () => {
974
- it("generates deterministic ID from same parts", () => {
975
- const id1 = generateTaskHashId(["fix bug", "task"]);
976
- const id2 = generateTaskHashId(["fix bug", "task"]);
977
- expect(id1).toBe(id2); // deterministic
978
- });
979
-
980
- it("generates different IDs for different parts", () => {
981
- const id1 = generateTaskHashId(["fix bug"]);
982
- const id2 = generateTaskHashId(["fix feature"]);
983
- expect(id1).not.toBe(id2);
984
- });
985
-
986
- it("starts with prefix", () => {
987
- const id = generateTaskHashId(["test"], "custom");
988
- expect(id.startsWith("custom-")).toBe(true);
989
- });
990
-
991
- it("generates hierarchical child ID", () => {
992
- const parent = generateTaskHashId(["parent"]);
993
- const child = childId(parent, 3);
994
- expect(child).toBe(`${parent}.3`);
995
- });
996
- });
997
-
998
- describe("hashToBase36", () => {
999
- it("produces correct length output", () => {
1000
- const hash = hashToBase36("test content", 6);
1001
- expect(hash.length).toBe(6);
1002
- });
1003
-
1004
- it("is deterministic", () => {
1005
- const h1 = hashToBase36("same input", 5);
1006
- const h2 = hashToBase36("same input", 5);
1007
- expect(h1).toBe(h2);
1008
- });
1009
- });
1010
- ```
1011
-
1012
- - [ ] **Step 3: Run test to verify it passes**
1013
-
1014
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/task-id.test.ts`
1015
- Expected: PASS (existing implementation should handle this)
1016
-
1017
- - [ ] **Step 4: Extend task-id.ts — add stableIdFromContent helper**
1018
-
1019
- Add this function to `src/runtime/task-id.ts` (after existing functions):
1020
-
1021
- ```typescript
1022
- /**
1023
- * Generate a stable, collision-resistant ID from arbitrary content.
1024
- * Uses full SHA-256 hash (not adaptive length) for maximum stability.
1025
- * Format: {prefix}-{first12charsOfBase36hash}
1026
- *
1027
- * Use for: run-level IDs, artifact keys, cross-run references
1028
- * where determinism and uniqueness matter more than short length.
1029
- */
1030
- export function stableIdFromContent(content: string, prefix = "id"): string {
1031
- const hash = createHash("sha256").update(content).digest("hex");
1032
- // Convert first 8 bytes (16 hex chars) to base36-ish
1033
- const hashChars = "0123456789abcdefghijklmnopqrstuvwxyz";
1034
- let b36 = "";
1035
- for (let i = 0; i < 16 && i < hash.length; i++) {
1036
- b36 += hashChars[parseInt(hash[i]!, 16)] ?? "0";
1037
- }
1038
- return `${prefix}-${b36.slice(0, 12)}`;
1039
- }
1040
- ```
1041
-
1042
- - [ ] **Step 5: Run test to verify it still passes**
1043
-
1044
- Run: `node --experimental-strip-types --test --test-timeout=30000 test/unit/task-id.test.ts`
1045
- Expected: PASS
1046
-
1047
- - [ ] **Step 6: Commit**
1048
-
1049
- ```bash
1050
- git add src/runtime/task-id.ts test/unit/task-id.test.ts
1051
- git commit -m "feat: add stableIdFromContent for cross-run stable IDs
1052
-
1053
- stableIdFromContent() uses full SHA-256 for maximum collision resistance.
1054
- Format: prefix-{12-char-base36-hash}
1055
- Use for: run IDs, artifact keys, cross-run references.
1056
- Existing generateTaskHashId() unchanged (adaptive length, for task-level IDs)."
1057
- ```
1058
-
1059
- ---
1060
-
1061
- ## Task 6: Migrate state-store.ts to CrewError
1062
-
1063
- **Files:**
1064
- - Modify: `src/state/state-store.ts:400-430` (replace plain Error throws with CrewError)
1065
-
1066
- - [ ] **Step 1: Read current error throwing sites**
1067
-
1068
- ```bash
1069
- grep -n "throw new Error" src/state/state-store.ts | head -20
1070
- ```
1071
-
1072
- - [ ] **Step 2: Replace invalidStatusTransition throw**
1073
-
1074
- Find line with `throw new Error('Invalid run status transition:...')` and replace:
1075
-
1076
- ```typescript
1077
- // OLD (line ~418):
1078
- throw new Error(`Invalid run status transition: ${current} → ${next}`);
1079
-
1080
- // NEW:
1081
- import { errors } from "../../src/errors.ts";
1082
- // ... later in function:
1083
- throw errors.invalidStatusTransition(current, next);
1084
- ```
1085
-
1086
- - [ ] **Step 3: Replace manifest not found throw**
1087
-
1088
- ```typescript
1089
- // OLD:
1090
- throw new Error(`saveRunTasks: manifest not found for runId=${runId}`);
1091
-
1092
- // NEW:
1093
- throw errors.taskNotFound(runId).withContext("saveRunTasks: manifest not found");
1094
- ```
1095
-
1096
- - [ ] **Step 4: Replace state write failure throw**
1097
-
1098
- ```typescript
1099
- // OLD:
1100
- throw new Error(`Failed to write run state: manifestWritten=${manifestWritten}, tasksWritten=${tasksWritten}`);
1101
-
1102
- // NEW:
1103
- throw errors.fileWrite(statePath, err).withContext("saveManifestAndTasksAtomicSync: atomic write failed");
1104
- ```
1105
-
1106
- - [ ] **Step 5: Run tests**
1107
-
1108
- Run: `node --experimental-strip-types --test --test-timeout=60000 test/unit/state-store.test.ts 2>&1 | tail -10`
1109
- Expected: PASS (or existing failures unrelated to this change)
1110
-
1111
- - [ ] **Step 6: Commit**
1112
-
1113
- ```bash
1114
- git add src/state/state-store.ts
1115
- git commit -m "refactor(state-store): use CrewError for structured errors
1116
-
1117
- Migrates state-store.ts from plain Error throws to CrewError:
1118
- - InvalidStatusTransition → errors.invalidStatusTransition()
1119
- - Task not found → errors.taskNotFound()
1120
- - File write failure → errors.fileWrite()
1121
- Display format: error[E004]: Invalid run status transition: running → queued"
1122
- ```
1123
-
1124
- ---
1125
-
1126
- ## Task 7: Integrate PluginRegistry into team-runner
1127
-
1128
- **Files:**
1129
- - Modify: `src/runtime/team-runner.ts` (add plugin context to workflow execution)
1130
- - Test: `test/unit/team-runner.test.ts` (add plugin integration test)
1131
-
1132
- - [ ] **Step 1: Read team-runner.ts first 100 lines**
1133
-
1134
- ```bash
1135
- head -100 src/runtime/team-runner.ts
1136
- ```
1137
-
1138
- - [ ] **Step 2: Add plugin context to RunConfig**
1139
-
1140
- In `src/config/types.ts` or wherever `RunConfig` is defined, add:
1141
-
1142
- ```typescript
1143
- // Add to existing RunConfig interface:
1144
- interface RunConfig {
1145
- // ... existing fields ...
1146
- activePlugins?: Plugin[];
1147
- pluginContext?: Record<string, unknown>;
1148
- }
1149
- ```
1150
-
1151
- - [ ] **Step 3: Wire plugin activation in team-runner**
1152
-
1153
- Add near the top of `team-runner.ts`:
1154
-
1155
- ```typescript
1156
- import { PluginRegistry } from "../plugins/plugin-registry.ts";
1157
- import { NextJsPlugin, VitestPlugin, VitePlugin } from "../plugins/plugins/index.ts";
1158
-
1159
- // Create registry with built-in plugins
1160
- const builtInRegistry = new PluginRegistry();
1161
- builtInRegistry.register(NextJsPlugin);
1162
- builtInRegistry.register(VitestPlugin);
1163
- builtInRegistry.register(VitePlugin);
1164
-
1165
- function getActivePlugins(packageJsonDeps: string[]) {
1166
- return builtInRegistry.activePlugins(packageJsonDeps);
1167
- }
1168
- ```
1169
-
1170
- - [ ] **Step 4: Use plugin context in task routing**
1171
-
1172
- Add to `executeTeam()` or wherever workflow starts:
1173
-
1174
- ```typescript
1175
- // After loading package.json deps:
1176
- const activePlugins = getActivePlugins(packageJsonDeps || []);
1177
- const pluginContext = {
1178
- entryPatterns: activePlugins.flatMap(p => p.entryPatterns ?? []),
1179
- pathAliases: Object.fromEntries(activePlugins.flatMap(p => p.pathAliases ?? [])),
1180
- };
1181
- ```
1182
-
1183
- - [ ] **Step 5: Commit**
1184
-
1185
- ```bash
1186
- git add src/runtime/team-runner.ts src/config/types.ts
1187
- git commit -m "feat(team-runner): integrate plugin registry for framework context
1188
-
1189
- - Creates built-in PluginRegistry with NextJs, Vitest, Vite plugins
1190
- - activePlugins() called with package.json dependencies
1191
- - pluginContext provides entryPatterns + pathAliases to tasks
1192
- Framework-aware task routing without breaking existing behavior."
1193
- ```
1194
-
1195
- ---
1196
-
1197
- ## Task 8: Wire Health Score into team-runner
1198
-
1199
- **Files:**
1200
- - Modify: `src/runtime/team-runner.ts` (save health snapshot on run completion)
1201
- - Modify: `src/state/state-store.ts` (call health store after manifest save)
1202
-
1203
- - [ ] **Step 1: Import HealthStore into team-runner**
1204
-
1205
- ```typescript
1206
- import { HealthStore } from "../state/health-store.ts";
1207
- ```
1208
-
1209
- - [ ] **Step 2: After run completion, save health snapshot**
1210
-
1211
- Find where run status transitions to "completed" or "failed" and add:
1212
-
1213
- ```typescript
1214
- const healthStore = new HealthStore(stateRoot);
1215
- healthStore.saveSnapshot(manifest);
1216
- ```
1217
-
1218
- - [ ] **Step 3: Commit**
1219
-
1220
- ```bash
1221
- git add src/runtime/team-runner.ts src/state/state-store.ts
1222
- git commit -m "feat(team-runner): save health snapshot on run completion
1223
-
1224
- HealthStore.saveSnapshot() called after manifest update when run completes.
1225
- Snapshots stored in .crew/state/health/{runId}.json for trend analysis.
1226
- Score and grade available via `team health` command (future work)."
1227
- ```
1228
-
1229
- ---
1230
-
1231
- ## Self-Review Checklist
1232
-
1233
- **Spec coverage:**
1234
- - [x] ErrorCode enum (E001-E006) → Task 1
1235
- - [x] CrewError builder pattern (withContext, withHelp) → Task 1
1236
- - [x] AtomicWriter with fsync + rename → Task 2
1237
- - [x] computeRunHealth penalty-based scoring → Task 3
1238
- - [x] HealthStore snapshot persistence → Task 3
1239
- - [x] Plugin interface + registry → Task 4
1240
- - [x] NextJs, Vitest, Vite built-in plugins → Task 4
1241
- - [x] stableIdFromContent for cross-run IDs → Task 5
1242
- - [x] state-store.ts migration to CrewError → Task 6
1243
- - [x] Plugin registry integration → Task 7
1244
- - [x] Health score integration → Task 8
1245
-
1246
- **Placeholder scan:**
1247
- - No "TBD" or "TODO" found
1248
- - No "implement later" or "fill in details"
1249
- - No steps that describe without showing code
1250
- - All test code is actual runnable TypeScript
1251
-
1252
- **Type consistency:**
1253
- - `CrewError.code` is `ErrorCode` (not string)
1254
- - `HealthStore.saveSnapshot()` accepts `ManifestSummary` (not full manifest type)
1255
- - `PluginRegistry.activePlugins()` accepts `string[]` (not full package.json)
1256
- - `stableIdFromContent()` uses `createHash` from `node:crypto`
1257
-
1258
- ---
1259
-
1260
- ## Execution Handoff
1261
-
1262
- **Plan complete and saved to `docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md`.**
1263
-
1264
- **Two execution options:**
1265
-
1266
- **1. Subagent-Driven (recommended)** — I dispatch a fresh subagent per task, review between tasks, fast iteration
1267
-
1268
- **2. Inline Execution** — Execute tasks in this session using executing-plans, batch execution with checkpoints
1269
-
1270
- **Which approach?**