pi-crew 0.9.50 → 0.9.52

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 (609) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +216 -20
  3. package/dist/build-meta.json +2222 -1761
  4. package/dist/index.mjs +18636 -17603
  5. package/docs/actions-reference.md +431 -1
  6. package/package.json +19 -9
  7. package/schema.json +41 -0
  8. package/scripts/check-bundle-size.mjs +38 -0
  9. package/scripts/test-runner.mjs +11 -1
  10. package/skills/distill-software/SKILL.md +24 -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/README.md +0 -26
  66. package/docs/deep-review-report.md +0 -384
  67. package/docs/distillation/cybersecurity-patterns.md +0 -294
  68. package/docs/feature-analysis-subagent4.md +0 -305
  69. package/docs/fix-plan-disabletools-exit-null.md +0 -219
  70. package/docs/fixes/BATCH_A_H1_H2.md +0 -86
  71. package/docs/fixes/bug-006-foreground-cancel-concurrent.md +0 -78
  72. package/docs/fixes/bug-007-async-notifier-stale-ctx.md +0 -112
  73. package/docs/fixes/bug-008-child-process-silent-timeout.md +0 -100
  74. package/docs/fixes/bug-009-executor-yield-limit-needs-attention.md +0 -75
  75. package/docs/fixes/bug-010-child-process-api-key-filtered.md +0 -109
  76. package/docs/fixes/bug-011-spawn-pi-enoent.md +0 -92
  77. package/docs/fixes/bug-012-essential-env-stripped.md +0 -89
  78. package/docs/fixes/bug-013-background-runner-death.md +0 -84
  79. package/docs/fixes/bug-014-infinite-retry-loop-needs-attention.md +0 -82
  80. package/docs/fixes/bug-015-background-runner-sigterm.md +0 -65
  81. package/docs/fixes/bug-017-background-runner-session-shutdown.md +0 -66
  82. package/docs/fixes/bug-017-background-runner-sigkill-double-fork.md +0 -28
  83. package/docs/fixes/bug-018-child-pi-worker-stdin-hang.md +0 -61
  84. package/docs/fixes/bug-019-phantom-runs-temp-workspace.md +0 -52
  85. package/docs/fixes/bug-020-infinite-retry-loop-needs-attention.md +0 -47
  86. package/docs/fixes/v0.9.10/locks-fix-verify.md +0 -3
  87. package/docs/fixes/v0.9.10/smoke-test.md +0 -12
  88. package/docs/followup-plan-2026-05-12.md +0 -463
  89. package/docs/followup-review-2026-05-12.md +0 -297
  90. package/docs/goals.md +0 -59
  91. package/docs/implementation-plan-top3.md +0 -333
  92. package/docs/issue-29-analysis.md +0 -189
  93. package/docs/live-mailbox-runtime.md +0 -36
  94. package/docs/migration/atomic-write-v2-migration.md +0 -299
  95. package/docs/next-upgrade-roadmap.md +0 -808
  96. package/docs/oh-my-pi-research.md +0 -509
  97. package/docs/optimization-plan-2026-07.md +0 -366
  98. package/docs/optimization-plan.md +0 -644
  99. package/docs/patterns/command-agent-skill.md +0 -71
  100. package/docs/perf/baseline-2026-05.md +0 -113
  101. package/docs/perf/final-report-2026-05.md +0 -206
  102. package/docs/perf/optimization-plan-2026-07-verified.md +0 -396
  103. package/docs/perf/performance-audit-report-2026-07.md +0 -948
  104. package/docs/perf/performance-review-2026-07.md +0 -287
  105. package/docs/perf/sprint-1-report.md +0 -71
  106. package/docs/perf/sprint-2-report.md +0 -81
  107. package/docs/perf/sprint-2.5-report.md +0 -53
  108. package/docs/perf/sprint-3-report.md +0 -36
  109. package/docs/perf/sprint-4-report.md +0 -47
  110. package/docs/perf/sprint-5-report.md +0 -51
  111. package/docs/perf/sprint-6-report.md +0 -94
  112. package/docs/perf/sprint-7-report.md +0 -74
  113. package/docs/perf/upgrade-plan-2026-05.md +0 -147
  114. package/docs/performance-optimization-execution-plan.md +0 -520
  115. package/docs/phase4-triage.md +0 -58
  116. package/docs/pi-crew-investigation-report.md +0 -411
  117. package/docs/product/README.md +0 -31
  118. package/docs/product/platform.md +0 -27
  119. package/docs/product/runtime-safety.md +0 -37
  120. package/docs/product/team-run.md +0 -39
  121. package/docs/product/team-tool.md +0 -37
  122. package/docs/runtime-analysis-child-vs-live.md +0 -171
  123. package/docs/runtime-migration-in-process-analysis.md +0 -250
  124. package/docs/skills/REFERENCE.md +0 -138
  125. package/docs/stories/README.md +0 -32
  126. package/docs/stories/US-DEPS-major-upgrade.md +0 -62
  127. package/docs/stories/backlog.md +0 -36
  128. package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +0 -1270
  129. package/docs/templates/decision.md +0 -27
  130. package/docs/templates/story.md +0 -44
  131. package/docs/templates/validation-report.md +0 -32
  132. package/docs/ui-optimization-plan.md +0 -447
  133. package/src/adapters/claude-adapter.ts +0 -23
  134. package/src/adapters/codex-adapter.ts +0 -21
  135. package/src/adapters/cursor-adapter.ts +0 -17
  136. package/src/adapters/export-util.ts +0 -143
  137. package/src/adapters/index.ts +0 -15
  138. package/src/adapters/registry.ts +0 -18
  139. package/src/adapters/types.ts +0 -23
  140. package/src/agents/agent-config.ts +0 -194
  141. package/src/agents/agent-search.ts +0 -98
  142. package/src/agents/agent-serializer.ts +0 -38
  143. package/src/agents/discover-agents.ts +0 -623
  144. package/src/benchmark/benchmark-runner.ts +0 -313
  145. package/src/benchmark/feedback-loop.ts +0 -73
  146. package/src/config/config.ts +0 -1269
  147. package/src/config/defaults.ts +0 -193
  148. package/src/config/drift-detector.ts +0 -290
  149. package/src/config/markers.ts +0 -330
  150. package/src/config/resilient-parser.ts +0 -117
  151. package/src/config/role-tools.ts +0 -118
  152. package/src/config/suggestions.ts +0 -75
  153. package/src/config/types.ts +0 -278
  154. package/src/errors.ts +0 -194
  155. package/src/extension/action-suggestions.ts +0 -117
  156. package/src/extension/async-notifier.ts +0 -179
  157. package/src/extension/autonomous-policy.ts +0 -209
  158. package/src/extension/command-completions.ts +0 -120
  159. package/src/extension/context-status-injection.ts +0 -185
  160. package/src/extension/crew-autocomplete.ts +0 -131
  161. package/src/extension/crew-cleanup.ts +0 -168
  162. package/src/extension/crew-input-router.ts +0 -93
  163. package/src/extension/crew-shortcuts.ts +0 -87
  164. package/src/extension/crew-vibes/cat-frames.ts +0 -18
  165. package/src/extension/crew-vibes/config.ts +0 -195
  166. package/src/extension/crew-vibes/figures.ts +0 -101
  167. package/src/extension/crew-vibes/font-detect.ts +0 -71
  168. package/src/extension/crew-vibes/footer.ts +0 -292
  169. package/src/extension/crew-vibes/index.ts +0 -432
  170. package/src/extension/crew-vibes/provider-usage.ts +0 -396
  171. package/src/extension/crew-vibes/render.ts +0 -206
  172. package/src/extension/crew-vibes/speed.ts +0 -286
  173. package/src/extension/cross-extension-rpc.ts +0 -350
  174. package/src/extension/help.ts +0 -62
  175. package/src/extension/import-index.ts +0 -74
  176. package/src/extension/knowledge-injection.ts +0 -402
  177. package/src/extension/management.ts +0 -571
  178. package/src/extension/message-renderers.ts +0 -108
  179. package/src/extension/notification-router.ts +0 -152
  180. package/src/extension/notification-sink.ts +0 -54
  181. package/src/extension/pi-api.ts +0 -56
  182. package/src/extension/plan-orchestrate.ts +0 -302
  183. package/src/extension/project-init.ts +0 -170
  184. package/src/extension/register.ts +0 -113
  185. package/src/extension/registration/artifact-cleanup.ts +0 -20
  186. package/src/extension/registration/command-registration.ts +0 -58
  187. package/src/extension/registration/command-utils.ts +0 -58
  188. package/src/extension/registration/commands.ts +0 -1228
  189. package/src/extension/registration/compaction-guard.ts +0 -333
  190. package/src/extension/registration/context-builder.ts +0 -135
  191. package/src/extension/registration/crash-recovery-cache.ts +0 -54
  192. package/src/extension/registration/foreground-run-controller.ts +0 -238
  193. package/src/extension/registration/hook-registration.ts +0 -119
  194. package/src/extension/registration/lazy-configurers.ts +0 -124
  195. package/src/extension/registration/lifecycle-handlers.ts +0 -923
  196. package/src/extension/registration/lifecycle.ts +0 -259
  197. package/src/extension/registration/observability.ts +0 -322
  198. package/src/extension/registration/registration-types.ts +0 -158
  199. package/src/extension/registration/runtime-cleanup.ts +0 -230
  200. package/src/extension/registration/subagent-helpers.ts +0 -125
  201. package/src/extension/registration/subagent-manager-setup.ts +0 -330
  202. package/src/extension/registration/subagent-tools.ts +0 -456
  203. package/src/extension/registration/team-tool.ts +0 -220
  204. package/src/extension/registration/tool-registration.ts +0 -50
  205. package/src/extension/registration/ui.ts +0 -172
  206. package/src/extension/registration/viewers.ts +0 -121
  207. package/src/extension/registration/wire-cross-extension.ts +0 -37
  208. package/src/extension/result-watcher.ts +0 -139
  209. package/src/extension/rpc-hmac.ts +0 -256
  210. package/src/extension/run-bundle-schema.ts +0 -104
  211. package/src/extension/run-export.ts +0 -97
  212. package/src/extension/run-import.ts +0 -168
  213. package/src/extension/run-index.ts +0 -131
  214. package/src/extension/run-maintenance.ts +0 -190
  215. package/src/extension/session-summary.ts +0 -18
  216. package/src/extension/team-manager-command.ts +0 -153
  217. package/src/extension/team-onboard.ts +0 -177
  218. package/src/extension/team-recommendation.ts +0 -288
  219. package/src/extension/team-tool/anchor.ts +0 -172
  220. package/src/extension/team-tool/api.ts +0 -1244
  221. package/src/extension/team-tool/auto-summarize.ts +0 -142
  222. package/src/extension/team-tool/cache-control.ts +0 -19
  223. package/src/extension/team-tool/cancel.ts +0 -337
  224. package/src/extension/team-tool/chain-dispatch.ts +0 -95
  225. package/src/extension/team-tool/chain-executor.ts +0 -379
  226. package/src/extension/team-tool/config-patch.ts +0 -50
  227. package/src/extension/team-tool/context.ts +0 -149
  228. package/src/extension/team-tool/destructive-gate.ts +0 -52
  229. package/src/extension/team-tool/doctor.ts +0 -431
  230. package/src/extension/team-tool/explain.ts +0 -282
  231. package/src/extension/team-tool/failure-patterns.ts +0 -118
  232. package/src/extension/team-tool/goal-wrap.ts +0 -330
  233. package/src/extension/team-tool/goal.ts +0 -556
  234. package/src/extension/team-tool/handle-schedule.ts +0 -332
  235. package/src/extension/team-tool/handle-settings.ts +0 -520
  236. package/src/extension/team-tool/health-monitor.ts +0 -481
  237. package/src/extension/team-tool/inspect.ts +0 -88
  238. package/src/extension/team-tool/intent-policy.ts +0 -45
  239. package/src/extension/team-tool/lifecycle-actions.ts +0 -615
  240. package/src/extension/team-tool/orchestrate.ts +0 -98
  241. package/src/extension/team-tool/parallel-dispatch.ts +0 -183
  242. package/src/extension/team-tool/plan.ts +0 -50
  243. package/src/extension/team-tool/respond.ts +0 -150
  244. package/src/extension/team-tool/run-not-found.ts +0 -49
  245. package/src/extension/team-tool/run.ts +0 -1035
  246. package/src/extension/team-tool/status.ts +0 -260
  247. package/src/extension/team-tool/workflow-manage.ts +0 -261
  248. package/src/extension/team-tool-types.ts +0 -26
  249. package/src/extension/team-tool.ts +0 -1116
  250. package/src/extension/tool-result.ts +0 -16
  251. package/src/extension/validate-resources.ts +0 -108
  252. package/src/hooks/registry.ts +0 -200
  253. package/src/hooks/types.ts +0 -69
  254. package/src/i18n.ts +0 -212
  255. package/src/observability/correlation.ts +0 -50
  256. package/src/observability/event-bus.ts +0 -86
  257. package/src/observability/event-to-metric.ts +0 -170
  258. package/src/observability/exporters/adapter.ts +0 -27
  259. package/src/observability/exporters/otlp-exporter.ts +0 -356
  260. package/src/observability/exporters/prometheus-exporter.ts +0 -54
  261. package/src/observability/metric-registry.ts +0 -100
  262. package/src/observability/metric-retention.ts +0 -64
  263. package/src/observability/metric-sink.ts +0 -99
  264. package/src/observability/metrics-primitives.ts +0 -219
  265. package/src/plugins/plugin-define.ts +0 -6
  266. package/src/plugins/plugin-registry.ts +0 -32
  267. package/src/plugins/plugins/index.ts +0 -3
  268. package/src/plugins/plugins/nextjs.ts +0 -19
  269. package/src/plugins/plugins/vite.ts +0 -10
  270. package/src/plugins/plugins/vitest.ts +0 -9
  271. package/src/prompt/prompt-runtime.ts +0 -380
  272. package/src/runtime/adaptive-plan.ts +0 -563
  273. package/src/runtime/agent-control.ts +0 -215
  274. package/src/runtime/agent-memory.ts +0 -81
  275. package/src/runtime/agent-observability.ts +0 -122
  276. package/src/runtime/anchor-manager.ts +0 -475
  277. package/src/runtime/async-marker.ts +0 -32
  278. package/src/runtime/async-runner.ts +0 -381
  279. package/src/runtime/attention-events.ts +0 -28
  280. package/src/runtime/auto-summarize.ts +0 -344
  281. package/src/runtime/background-runner.ts +0 -837
  282. package/src/runtime/batch-barrier.ts +0 -147
  283. package/src/runtime/broker-issuer.ts +0 -37
  284. package/src/runtime/cancellation-token.ts +0 -99
  285. package/src/runtime/cancellation.ts +0 -99
  286. package/src/runtime/capability-inventory.ts +0 -137
  287. package/src/runtime/chain-parser.ts +0 -237
  288. package/src/runtime/chain-runner.ts +0 -606
  289. package/src/runtime/checkpoint.ts +0 -291
  290. package/src/runtime/child-pi-constants.ts +0 -42
  291. package/src/runtime/child-pi-kill.ts +0 -180
  292. package/src/runtime/child-pi-pool.ts +0 -68
  293. package/src/runtime/child-pi-spawn.ts +0 -287
  294. package/src/runtime/child-pi-steering.ts +0 -128
  295. package/src/runtime/child-pi-streams.ts +0 -296
  296. package/src/runtime/child-pi-transcript.ts +0 -169
  297. package/src/runtime/child-pi.ts +0 -1105
  298. package/src/runtime/coalesce-tasks.ts +0 -268
  299. package/src/runtime/code-summary.ts +0 -292
  300. package/src/runtime/command-trace.ts +0 -105
  301. package/src/runtime/compact-pipeline.ts +0 -56
  302. package/src/runtime/compact-stages/ansi-strip-stage.ts +0 -25
  303. package/src/runtime/compact-stages/blank-collapse-stage.ts +0 -31
  304. package/src/runtime/compact-stages/deduplicate-stage.ts +0 -34
  305. package/src/runtime/compact-stages/head-snap-stage.ts +0 -57
  306. package/src/runtime/compact-stages/index.ts +0 -23
  307. package/src/runtime/compact-stages/tail-capture-stage.ts +0 -77
  308. package/src/runtime/compact-stages/truncation-stage.ts +0 -74
  309. package/src/runtime/compaction-summary.ts +0 -278
  310. package/src/runtime/completion-guard.ts +0 -207
  311. package/src/runtime/concurrency.ts +0 -58
  312. package/src/runtime/crash-classification.ts +0 -229
  313. package/src/runtime/crash-recovery.ts +0 -577
  314. package/src/runtime/crew-agent-records.ts +0 -593
  315. package/src/runtime/crew-agent-runtime.ts +0 -62
  316. package/src/runtime/crew-broker-child.ts +0 -88
  317. package/src/runtime/crew-broker-client.ts +0 -673
  318. package/src/runtime/crew-broker-tokens.ts +0 -84
  319. package/src/runtime/crew-broker.ts +0 -1276
  320. package/src/runtime/crew-hooks.ts +0 -227
  321. package/src/runtime/cross-extension-rpc.ts +0 -143
  322. package/src/runtime/custom-tools/irc-tool.ts +0 -282
  323. package/src/runtime/custom-tools/submit-result-tool.ts +0 -102
  324. package/src/runtime/deadletter.ts +0 -47
  325. package/src/runtime/delivery-coordinator.ts +0 -211
  326. package/src/runtime/delta-conflict.ts +0 -348
  327. package/src/runtime/deterministic-ast.ts +0 -161
  328. package/src/runtime/diagnostic-export.ts +0 -171
  329. package/src/runtime/direct-run.ts +0 -45
  330. package/src/runtime/dwf-state-store.ts +0 -102
  331. package/src/runtime/dynamic-workflow-context.ts +0 -1016
  332. package/src/runtime/dynamic-workflow-runner.ts +0 -310
  333. package/src/runtime/effectiveness.ts +0 -90
  334. package/src/runtime/errors/crew-errors.ts +0 -162
  335. package/src/runtime/event-stream-bridge.ts +0 -98
  336. package/src/runtime/foreground-control.ts +0 -193
  337. package/src/runtime/foreground-watchdog.ts +0 -132
  338. package/src/runtime/global-worker-cap.ts +0 -96
  339. package/src/runtime/goal-achievement.ts +0 -148
  340. package/src/runtime/goal-evaluator.ts +0 -361
  341. package/src/runtime/goal-loop-runner.ts +0 -828
  342. package/src/runtime/goal-state-store.ts +0 -219
  343. package/src/runtime/green-contract.ts +0 -55
  344. package/src/runtime/group-join.ts +0 -267
  345. package/src/runtime/handoff-manager.ts +0 -598
  346. package/src/runtime/heartbeat-gradient.ts +0 -36
  347. package/src/runtime/heartbeat-watcher.ts +0 -239
  348. package/src/runtime/hidden-handoff.ts +0 -407
  349. package/src/runtime/important-line-classifier.ts +0 -140
  350. package/src/runtime/intercom-bridge.ts +0 -187
  351. package/src/runtime/iteration-hooks.ts +0 -305
  352. package/src/runtime/live-agent-control.ts +0 -136
  353. package/src/runtime/live-agent-manager.ts +0 -704
  354. package/src/runtime/live-control-realtime.ts +0 -56
  355. package/src/runtime/live-extension-bridge.ts +0 -148
  356. package/src/runtime/live-irc.ts +0 -97
  357. package/src/runtime/live-session-health.ts +0 -108
  358. package/src/runtime/live-session-runtime.ts +0 -1139
  359. package/src/runtime/loop-gates.ts +0 -128
  360. package/src/runtime/manifest-cache.ts +0 -375
  361. package/src/runtime/mcp-proxy.ts +0 -105
  362. package/src/runtime/metric-parser.ts +0 -36
  363. package/src/runtime/model-fallback.ts +0 -409
  364. package/src/runtime/model-resolver.ts +0 -126
  365. package/src/runtime/model-scope.ts +0 -158
  366. package/src/runtime/orphan-worker-registry.ts +0 -465
  367. package/src/runtime/output-validator.ts +0 -202
  368. package/src/runtime/overflow-recovery.ts +0 -206
  369. package/src/runtime/parallel-research.ts +0 -68
  370. package/src/runtime/parallel-utils.ts +0 -160
  371. package/src/runtime/parent-guard.ts +0 -134
  372. package/src/runtime/path-overlap.ts +0 -150
  373. package/src/runtime/peer-dep.ts +0 -292
  374. package/src/runtime/per-write-validator.ts +0 -181
  375. package/src/runtime/phase-progress.ts +0 -217
  376. package/src/runtime/phase-tracker.ts +0 -385
  377. package/src/runtime/pi-args.ts +0 -655
  378. package/src/runtime/pi-json-output.ts +0 -170
  379. package/src/runtime/pi-spawn.ts +0 -273
  380. package/src/runtime/pipeline-runner.ts +0 -523
  381. package/src/runtime/plan-templates.ts +0 -201
  382. package/src/runtime/policy-engine.ts +0 -115
  383. package/src/runtime/post-checks.ts +0 -142
  384. package/src/runtime/post-exit-stdio-guard.ts +0 -104
  385. package/src/runtime/process-lifecycle.ts +0 -491
  386. package/src/runtime/process-status.ts +0 -154
  387. package/src/runtime/progress-event-coalescer.ts +0 -44
  388. package/src/runtime/progress-tracker.ts +0 -124
  389. package/src/runtime/prose-compressor.ts +0 -162
  390. package/src/runtime/recovery-recipes.ts +0 -203
  391. package/src/runtime/replace.ts +0 -570
  392. package/src/runtime/resilient-edit.ts +0 -153
  393. package/src/runtime/result-extractor.ts +0 -213
  394. package/src/runtime/retry-executor.ts +0 -100
  395. package/src/runtime/retry-runner.ts +0 -336
  396. package/src/runtime/role-permission.ts +0 -59
  397. package/src/runtime/run-coalesced-task-group.ts +0 -308
  398. package/src/runtime/run-drift.ts +0 -219
  399. package/src/runtime/run-tracker.ts +0 -116
  400. package/src/runtime/runtime-policy.ts +0 -29
  401. package/src/runtime/runtime-resolver.ts +0 -172
  402. package/src/runtime/runtime-warmup.ts +0 -160
  403. package/src/runtime/scheduler.ts +0 -353
  404. package/src/runtime/semaphore.ts +0 -141
  405. package/src/runtime/sensitive-paths.ts +0 -93
  406. package/src/runtime/session-resources.ts +0 -25
  407. package/src/runtime/session-snapshot.ts +0 -59
  408. package/src/runtime/session-usage.ts +0 -79
  409. package/src/runtime/settings-store.ts +0 -155
  410. package/src/runtime/sidechain-output.ts +0 -35
  411. package/src/runtime/single-agent-compose.ts +0 -84
  412. package/src/runtime/skill-effectiveness.ts +0 -524
  413. package/src/runtime/skill-instructions.ts +0 -391
  414. package/src/runtime/stale-reconciler.ts +0 -680
  415. package/src/runtime/stream-preview.ts +0 -184
  416. package/src/runtime/streaming-output.ts +0 -50
  417. package/src/runtime/subagent-manager.ts +0 -561
  418. package/src/runtime/subprocess-tool-registry.ts +0 -70
  419. package/src/runtime/supervisor-contact.ts +0 -64
  420. package/src/runtime/task-display.ts +0 -52
  421. package/src/runtime/task-graph-scheduler.ts +0 -227
  422. package/src/runtime/task-graph.ts +0 -290
  423. package/src/runtime/task-health.ts +0 -83
  424. package/src/runtime/task-id.ts +0 -161
  425. package/src/runtime/task-output-context.ts +0 -602
  426. package/src/runtime/task-packet.ts +0 -268
  427. package/src/runtime/task-quality.ts +0 -199
  428. package/src/runtime/task-runner/capabilities.ts +0 -78
  429. package/src/runtime/task-runner/context-retrieval.ts +0 -163
  430. package/src/runtime/task-runner/live-executor.ts +0 -227
  431. package/src/runtime/task-runner/output-splitter.ts +0 -152
  432. package/src/runtime/task-runner/progress.ts +0 -157
  433. package/src/runtime/task-runner/prompt-builder.ts +0 -278
  434. package/src/runtime/task-runner/prompt-pipeline.ts +0 -88
  435. package/src/runtime/task-runner/result-utils.ts +0 -16
  436. package/src/runtime/task-runner/retrieval-orchestrator.ts +0 -310
  437. package/src/runtime/task-runner/run-projection.ts +0 -125
  438. package/src/runtime/task-runner/state-helpers.ts +0 -176
  439. package/src/runtime/task-runner/tail-read.ts +0 -34
  440. package/src/runtime/task-runner.ts +0 -1420
  441. package/src/runtime/team-runner-artifacts.ts +0 -13
  442. package/src/runtime/team-runner.ts +0 -1993
  443. package/src/runtime/tool-output-pruner.ts +0 -333
  444. package/src/runtime/tool-progress.ts +0 -278
  445. package/src/runtime/usage-tracker.ts +0 -73
  446. package/src/runtime/verification-gates.ts +0 -571
  447. package/src/runtime/verification-integrity.ts +0 -105
  448. package/src/runtime/verification-worktree.ts +0 -186
  449. package/src/runtime/worker-heartbeat.ts +0 -25
  450. package/src/runtime/worker-startup.ts +0 -83
  451. package/src/runtime/workflow-state.ts +0 -195
  452. package/src/runtime/workspace-lock.ts +0 -443
  453. package/src/runtime/workspace-tree.ts +0 -312
  454. package/src/runtime/yield-handler.ts +0 -224
  455. package/src/runtime/zombie-scanner.ts +0 -298
  456. package/src/schema/config-schema.ts +0 -327
  457. package/src/schema/team-tool-schema.ts +0 -415
  458. package/src/schema/validation-types.ts +0 -159
  459. package/src/skills/discover-skills.ts +0 -197
  460. package/src/skills/skill-templates.ts +0 -456
  461. package/src/skills/validate.ts +0 -285
  462. package/src/state/active-run-registry.ts +0 -439
  463. package/src/state/artifact-store.ts +0 -176
  464. package/src/state/atomic-write.ts +0 -903
  465. package/src/state/blob-store.ts +0 -308
  466. package/src/state/contracts.ts +0 -158
  467. package/src/state/crew-init.ts +0 -269
  468. package/src/state/decision-ledger.ts +0 -345
  469. package/src/state/event-log-rotation.ts +0 -342
  470. package/src/state/event-log.ts +0 -1311
  471. package/src/state/event-reconstructor.ts +0 -229
  472. package/src/state/gitignore-manager.ts +0 -46
  473. package/src/state/health-store.ts +0 -79
  474. package/src/state/hook-instinct-bridge.ts +0 -94
  475. package/src/state/hook-integrations.ts +0 -51
  476. package/src/state/instinct-store.ts +0 -275
  477. package/src/state/jsonl-writer.ts +0 -107
  478. package/src/state/locks.ts +0 -562
  479. package/src/state/mailbox.ts +0 -916
  480. package/src/state/observation-store.ts +0 -176
  481. package/src/state/run-cache.ts +0 -193
  482. package/src/state/run-graph.ts +0 -183
  483. package/src/state/run-metrics.ts +0 -165
  484. package/src/state/schedule.ts +0 -166
  485. package/src/state/session-state-map.ts +0 -51
  486. package/src/state/state-store.ts +0 -958
  487. package/src/state/task-claims.ts +0 -61
  488. package/src/state/tiered-eval.ts +0 -480
  489. package/src/state/types-eval.ts +0 -58
  490. package/src/state/types.ts +0 -440
  491. package/src/state/usage.ts +0 -138
  492. package/src/state/worker-atomic-writer.ts +0 -194
  493. package/src/subagents/async-entry.ts +0 -1
  494. package/src/subagents/index.ts +0 -3
  495. package/src/subagents/live/control.ts +0 -1
  496. package/src/subagents/live/manager.ts +0 -1
  497. package/src/subagents/live/realtime.ts +0 -1
  498. package/src/subagents/live/session-runtime.ts +0 -1
  499. package/src/subagents/manager.ts +0 -1
  500. package/src/subagents/spawn.ts +0 -1
  501. package/src/teams/discover-teams.ts +0 -187
  502. package/src/teams/team-config.ts +0 -27
  503. package/src/teams/team-serializer.ts +0 -38
  504. package/src/tools/safe-bash-extension.ts +0 -54
  505. package/src/tools/safe-bash.ts +0 -505
  506. package/src/types/diff.d.ts +0 -18
  507. package/src/types/new-api-types.ts +0 -35
  508. package/src/ui/agent-management-overlay.ts +0 -160
  509. package/src/ui/card-colors.ts +0 -139
  510. package/src/ui/crew-footer.ts +0 -102
  511. package/src/ui/crew-select-list.ts +0 -111
  512. package/src/ui/dashboard-panes/agents-pane.ts +0 -164
  513. package/src/ui/dashboard-panes/cancellation-pane.ts +0 -66
  514. package/src/ui/dashboard-panes/capability-pane.ts +0 -77
  515. package/src/ui/dashboard-panes/health-pane.ts +0 -31
  516. package/src/ui/dashboard-panes/mailbox-pane.ts +0 -35
  517. package/src/ui/dashboard-panes/metrics-pane.ts +0 -37
  518. package/src/ui/dashboard-panes/progress-pane.ts +0 -38
  519. package/src/ui/dashboard-panes/transcript-pane.ts +0 -10
  520. package/src/ui/deploy-bundled-themes.ts +0 -71
  521. package/src/ui/dwf-phase-display.ts +0 -151
  522. package/src/ui/dynamic-border.ts +0 -35
  523. package/src/ui/heartbeat-aggregator.ts +0 -74
  524. package/src/ui/key-utils.ts +0 -42
  525. package/src/ui/keybinding-map.ts +0 -248
  526. package/src/ui/layout-primitives.ts +0 -106
  527. package/src/ui/live-conversation-overlay.ts +0 -183
  528. package/src/ui/live-duration.ts +0 -55
  529. package/src/ui/live-run-sidebar.ts +0 -301
  530. package/src/ui/loaders.ts +0 -176
  531. package/src/ui/mascot.ts +0 -436
  532. package/src/ui/overlays/agent-picker-overlay.ts +0 -65
  533. package/src/ui/overlays/confirm-overlay.ts +0 -60
  534. package/src/ui/overlays/help-overlay.ts +0 -177
  535. package/src/ui/overlays/mailbox-compose-overlay.ts +0 -181
  536. package/src/ui/overlays/mailbox-compose-preview.ts +0 -78
  537. package/src/ui/overlays/mailbox-detail-overlay.ts +0 -154
  538. package/src/ui/pi-ui-compat.ts +0 -68
  539. package/src/ui/powerbar-publisher.ts +0 -459
  540. package/src/ui/render-coalescer.ts +0 -101
  541. package/src/ui/render-diff.ts +0 -160
  542. package/src/ui/render-scheduler.ts +0 -224
  543. package/src/ui/run-action-dispatcher.ts +0 -182
  544. package/src/ui/run-dashboard.ts +0 -913
  545. package/src/ui/run-event-bus.ts +0 -381
  546. package/src/ui/run-snapshot-cache.ts +0 -1057
  547. package/src/ui/settings-overlay.ts +0 -1067
  548. package/src/ui/snapshot-types.ts +0 -90
  549. package/src/ui/spinner.ts +0 -17
  550. package/src/ui/status-colors.ts +0 -143
  551. package/src/ui/syntax-highlight.ts +0 -136
  552. package/src/ui/terminal-status.ts +0 -269
  553. package/src/ui/theme-adapter.ts +0 -271
  554. package/src/ui/theme-discovery.ts +0 -199
  555. package/src/ui/tool-progress-formatter.ts +0 -93
  556. package/src/ui/tool-render.ts +0 -453
  557. package/src/ui/tool-renderers/brief-mode.ts +0 -225
  558. package/src/ui/tool-renderers/index.ts +0 -726
  559. package/src/ui/transcript-cache.ts +0 -128
  560. package/src/ui/transcript-entries.ts +0 -256
  561. package/src/ui/transcript-viewer.ts +0 -424
  562. package/src/ui/widget/index.ts +0 -357
  563. package/src/ui/widget/widget-formatters.ts +0 -182
  564. package/src/ui/widget/widget-model.ts +0 -110
  565. package/src/ui/widget/widget-renderer.ts +0 -182
  566. package/src/ui/widget/widget-types.ts +0 -36
  567. package/src/utils/bm25-search.ts +0 -235
  568. package/src/utils/completion-dedupe.ts +0 -63
  569. package/src/utils/conflict-detect.ts +0 -668
  570. package/src/utils/env-allowlist.ts +0 -30
  571. package/src/utils/env-filter.ts +0 -155
  572. package/src/utils/file-coalescer.ts +0 -98
  573. package/src/utils/fingerprint.ts +0 -180
  574. package/src/utils/frontmatter.ts +0 -70
  575. package/src/utils/fs-watch.ts +0 -37
  576. package/src/utils/gh-protocol.ts +0 -556
  577. package/src/utils/git.ts +0 -260
  578. package/src/utils/guards.ts +0 -107
  579. package/src/utils/ids.ts +0 -24
  580. package/src/utils/incremental-reader.ts +0 -220
  581. package/src/utils/internal-error.ts +0 -10
  582. package/src/utils/names.ts +0 -36
  583. package/src/utils/ndjson.ts +0 -115
  584. package/src/utils/paths.ts +0 -227
  585. package/src/utils/project-detector.ts +0 -160
  586. package/src/utils/redaction.ts +0 -347
  587. package/src/utils/resolve-shell.ts +0 -36
  588. package/src/utils/run-watcher-registry.ts +0 -152
  589. package/src/utils/safe-paths.ts +0 -383
  590. package/src/utils/scan-cache.ts +0 -144
  591. package/src/utils/session-utils.ts +0 -110
  592. package/src/utils/sleep.ts +0 -54
  593. package/src/utils/socket-path.ts +0 -127
  594. package/src/utils/sse-parser.ts +0 -129
  595. package/src/utils/task-name-generator.ts +0 -337
  596. package/src/utils/timings.ts +0 -33
  597. package/src/utils/token-counter.ts +0 -200
  598. package/src/utils/visual.ts +0 -207
  599. package/src/workflows/cost-estimator.ts +0 -34
  600. package/src/workflows/discover-workflows.ts +0 -290
  601. package/src/workflows/intermediate-store.ts +0 -166
  602. package/src/workflows/preflight-validator.ts +0 -168
  603. package/src/workflows/topology-analyzer.ts +0 -203
  604. package/src/workflows/validate-workflow.ts +0 -50
  605. package/src/workflows/workflow-config.ts +0 -70
  606. package/src/workflows/workflow-serializer.ts +0 -32
  607. package/src/worktree/branch-freshness.ts +0 -112
  608. package/src/worktree/cleanup.ts +0 -341
  609. 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?**