pi-crew 0.9.53 → 0.9.55

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 (493) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/package.json +6 -3
  3. package/scripts/clean-strip-types.mjs +60 -0
  4. package/src/adapters/claude-adapter.ts +23 -0
  5. package/src/adapters/codex-adapter.ts +21 -0
  6. package/src/adapters/cursor-adapter.ts +17 -0
  7. package/src/adapters/export-util.ts +143 -0
  8. package/src/adapters/index.ts +15 -0
  9. package/src/adapters/registry.ts +18 -0
  10. package/src/adapters/types.ts +23 -0
  11. package/src/agents/agent-config.ts +194 -0
  12. package/src/agents/agent-search.ts +98 -0
  13. package/src/agents/agent-serializer.ts +38 -0
  14. package/src/agents/discover-agents.ts +623 -0
  15. package/src/benchmark/benchmark-runner.ts +313 -0
  16. package/src/benchmark/feedback-loop.ts +73 -0
  17. package/src/config/config.ts +1270 -0
  18. package/src/config/defaults.ts +193 -0
  19. package/src/config/drift-detector.ts +290 -0
  20. package/src/config/markers.ts +330 -0
  21. package/src/config/resilient-parser.ts +117 -0
  22. package/src/config/role-tools.ts +118 -0
  23. package/src/config/suggestions.ts +75 -0
  24. package/src/config/types.ts +280 -0
  25. package/src/errors.ts +194 -0
  26. package/src/extension/action-suggestions.ts +117 -0
  27. package/src/extension/async-notifier.ts +179 -0
  28. package/src/extension/autonomous-policy.ts +209 -0
  29. package/src/extension/command-completions.ts +120 -0
  30. package/src/extension/context-status-injection.ts +185 -0
  31. package/src/extension/crew-autocomplete.ts +131 -0
  32. package/src/extension/crew-cleanup.ts +168 -0
  33. package/src/extension/crew-input-router.ts +93 -0
  34. package/src/extension/crew-shortcuts.ts +87 -0
  35. package/src/extension/crew-vibes/cat-frames.ts +18 -0
  36. package/src/extension/crew-vibes/config.ts +195 -0
  37. package/src/extension/crew-vibes/figures.ts +101 -0
  38. package/src/extension/crew-vibes/font-detect.ts +71 -0
  39. package/src/extension/crew-vibes/footer.ts +292 -0
  40. package/src/extension/crew-vibes/index.ts +432 -0
  41. package/src/extension/crew-vibes/provider-usage.ts +396 -0
  42. package/src/extension/crew-vibes/render.ts +206 -0
  43. package/src/extension/crew-vibes/speed.ts +286 -0
  44. package/src/extension/cross-extension-rpc.ts +350 -0
  45. package/src/extension/help.ts +62 -0
  46. package/src/extension/import-index.ts +74 -0
  47. package/src/extension/knowledge-injection.ts +402 -0
  48. package/src/extension/management.ts +571 -0
  49. package/src/extension/message-renderers.ts +108 -0
  50. package/src/extension/notification-router.ts +152 -0
  51. package/src/extension/notification-sink.ts +54 -0
  52. package/src/extension/pi-api.ts +56 -0
  53. package/src/extension/plan-orchestrate.ts +302 -0
  54. package/src/extension/project-init.ts +170 -0
  55. package/src/extension/register.ts +113 -0
  56. package/src/extension/registration/artifact-cleanup.ts +20 -0
  57. package/src/extension/registration/command-registration.ts +58 -0
  58. package/src/extension/registration/command-utils.ts +58 -0
  59. package/src/extension/registration/commands.ts +1228 -0
  60. package/src/extension/registration/compaction-guard.ts +333 -0
  61. package/src/extension/registration/context-builder.ts +135 -0
  62. package/src/extension/registration/crash-recovery-cache.ts +54 -0
  63. package/src/extension/registration/foreground-run-controller.ts +238 -0
  64. package/src/extension/registration/hook-registration.ts +119 -0
  65. package/src/extension/registration/lazy-configurers.ts +124 -0
  66. package/src/extension/registration/lifecycle-handlers.ts +923 -0
  67. package/src/extension/registration/lifecycle.ts +259 -0
  68. package/src/extension/registration/observability.ts +322 -0
  69. package/src/extension/registration/registration-types.ts +158 -0
  70. package/src/extension/registration/runtime-cleanup.ts +230 -0
  71. package/src/extension/registration/subagent-helpers.ts +125 -0
  72. package/src/extension/registration/subagent-manager-setup.ts +330 -0
  73. package/src/extension/registration/subagent-tools.ts +456 -0
  74. package/src/extension/registration/team-tool.ts +225 -0
  75. package/src/extension/registration/tool-registration.ts +50 -0
  76. package/src/extension/registration/ui.ts +172 -0
  77. package/src/extension/registration/viewers.ts +121 -0
  78. package/src/extension/registration/wire-cross-extension.ts +37 -0
  79. package/src/extension/result-watcher.ts +139 -0
  80. package/src/extension/rpc-hmac.ts +256 -0
  81. package/src/extension/run-bundle-schema.ts +104 -0
  82. package/src/extension/run-export.ts +97 -0
  83. package/src/extension/run-import.ts +168 -0
  84. package/src/extension/run-index.ts +131 -0
  85. package/src/extension/run-maintenance.ts +190 -0
  86. package/src/extension/session-summary.ts +18 -0
  87. package/src/extension/team-manager-command.ts +153 -0
  88. package/src/extension/team-onboard.ts +180 -0
  89. package/src/extension/team-recommendation.ts +288 -0
  90. package/src/extension/team-tool/anchor.ts +172 -0
  91. package/src/extension/team-tool/api.ts +1244 -0
  92. package/src/extension/team-tool/auto-summarize.ts +142 -0
  93. package/src/extension/team-tool/cache-control.ts +19 -0
  94. package/src/extension/team-tool/cancel.ts +337 -0
  95. package/src/extension/team-tool/chain-dispatch.ts +95 -0
  96. package/src/extension/team-tool/chain-executor.ts +379 -0
  97. package/src/extension/team-tool/config-patch.ts +50 -0
  98. package/src/extension/team-tool/context.ts +149 -0
  99. package/src/extension/team-tool/destructive-gate.ts +52 -0
  100. package/src/extension/team-tool/dispatch/automate.ts +80 -0
  101. package/src/extension/team-tool/dispatch/control.ts +42 -0
  102. package/src/extension/team-tool/dispatch/index.ts +95 -0
  103. package/src/extension/team-tool/dispatch/manage.ts +161 -0
  104. package/src/extension/team-tool/dispatch/run.ts +53 -0
  105. package/src/extension/team-tool/dispatch/status.ts +187 -0
  106. package/src/extension/team-tool/doctor.ts +431 -0
  107. package/src/extension/team-tool/explain.ts +282 -0
  108. package/src/extension/team-tool/failure-patterns.ts +118 -0
  109. package/src/extension/team-tool/goal-wrap.ts +330 -0
  110. package/src/extension/team-tool/goal.ts +556 -0
  111. package/src/extension/team-tool/handle-schedule.ts +332 -0
  112. package/src/extension/team-tool/handle-settings.ts +520 -0
  113. package/src/extension/team-tool/health-monitor.ts +481 -0
  114. package/src/extension/team-tool/inspect.ts +88 -0
  115. package/src/extension/team-tool/intent-policy.ts +45 -0
  116. package/src/extension/team-tool/lifecycle-actions.ts +615 -0
  117. package/src/extension/team-tool/orchestrate.ts +98 -0
  118. package/src/extension/team-tool/parallel-dispatch.ts +183 -0
  119. package/src/extension/team-tool/plan.ts +50 -0
  120. package/src/extension/team-tool/respond.ts +150 -0
  121. package/src/extension/team-tool/run-deadline.ts +66 -0
  122. package/src/extension/team-tool/run-not-found.ts +49 -0
  123. package/src/extension/team-tool/run.ts +1051 -0
  124. package/src/extension/team-tool/status.ts +260 -0
  125. package/src/extension/team-tool/workflow-manage.ts +261 -0
  126. package/src/extension/team-tool-types.ts +26 -0
  127. package/src/extension/team-tool.ts +750 -0
  128. package/src/extension/tool-result.ts +16 -0
  129. package/src/extension/validate-resources.ts +108 -0
  130. package/src/hooks/registry.ts +200 -0
  131. package/src/hooks/types.ts +69 -0
  132. package/src/i18n.ts +212 -0
  133. package/src/observability/correlation.ts +50 -0
  134. package/src/observability/event-bus.ts +86 -0
  135. package/src/observability/event-to-metric.ts +170 -0
  136. package/src/observability/exporters/adapter.ts +27 -0
  137. package/src/observability/exporters/otlp-exporter.ts +356 -0
  138. package/src/observability/exporters/prometheus-exporter.ts +54 -0
  139. package/src/observability/metric-registry.ts +100 -0
  140. package/src/observability/metric-retention.ts +64 -0
  141. package/src/observability/metric-sink.ts +99 -0
  142. package/src/observability/metrics-primitives.ts +219 -0
  143. package/src/plugins/plugin-define.ts +6 -0
  144. package/src/plugins/plugin-registry.ts +32 -0
  145. package/src/plugins/plugins/index.ts +3 -0
  146. package/src/plugins/plugins/nextjs.ts +19 -0
  147. package/src/plugins/plugins/vite.ts +10 -0
  148. package/src/plugins/plugins/vitest.ts +9 -0
  149. package/src/prompt/prompt-runtime.ts +380 -0
  150. package/src/runtime/adaptive-plan.ts +563 -0
  151. package/src/runtime/agent-control.ts +215 -0
  152. package/src/runtime/agent-memory.ts +81 -0
  153. package/src/runtime/agent-observability.ts +122 -0
  154. package/src/runtime/anchor-manager.ts +475 -0
  155. package/src/runtime/async-marker.ts +32 -0
  156. package/src/runtime/async-runner.ts +381 -0
  157. package/src/runtime/attention-events.ts +28 -0
  158. package/src/runtime/auto-summarize.ts +344 -0
  159. package/src/runtime/background-runner.ts +840 -0
  160. package/src/runtime/batch-barrier.ts +147 -0
  161. package/src/runtime/broker-issuer.ts +39 -0
  162. package/src/runtime/cancellation-token.ts +99 -0
  163. package/src/runtime/cancellation.ts +99 -0
  164. package/src/runtime/capability-inventory.ts +137 -0
  165. package/src/runtime/chain-parser.ts +237 -0
  166. package/src/runtime/chain-runner.ts +606 -0
  167. package/src/runtime/checkpoint.ts +291 -0
  168. package/src/runtime/child-pi-constants.ts +42 -0
  169. package/src/runtime/child-pi-kill.ts +180 -0
  170. package/src/runtime/child-pi-pool.ts +68 -0
  171. package/src/runtime/child-pi-spawn.ts +287 -0
  172. package/src/runtime/child-pi-steering.ts +128 -0
  173. package/src/runtime/child-pi-streams.ts +296 -0
  174. package/src/runtime/child-pi-transcript.ts +169 -0
  175. package/src/runtime/child-pi.ts +1105 -0
  176. package/src/runtime/coalesce-tasks.ts +268 -0
  177. package/src/runtime/code-summary.ts +292 -0
  178. package/src/runtime/command-trace.ts +105 -0
  179. package/src/runtime/compact-pipeline.ts +56 -0
  180. package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
  181. package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
  182. package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
  183. package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
  184. package/src/runtime/compact-stages/index.ts +23 -0
  185. package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
  186. package/src/runtime/compact-stages/truncation-stage.ts +74 -0
  187. package/src/runtime/compaction-summary.ts +278 -0
  188. package/src/runtime/completion-guard.ts +207 -0
  189. package/src/runtime/concurrency.ts +58 -0
  190. package/src/runtime/crash-classification.ts +229 -0
  191. package/src/runtime/crash-recovery.ts +594 -0
  192. package/src/runtime/crew-agent-records.ts +595 -0
  193. package/src/runtime/crew-agent-runtime.ts +62 -0
  194. package/src/runtime/crew-broker-child.ts +88 -0
  195. package/src/runtime/crew-broker-client.ts +673 -0
  196. package/src/runtime/crew-broker-tokens.ts +159 -0
  197. package/src/runtime/crew-broker.ts +1304 -0
  198. package/src/runtime/crew-hooks.ts +227 -0
  199. package/src/runtime/cross-extension-rpc.ts +143 -0
  200. package/src/runtime/custom-tools/irc-tool.ts +282 -0
  201. package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
  202. package/src/runtime/deadletter.ts +47 -0
  203. package/src/runtime/delivery-coordinator.ts +211 -0
  204. package/src/runtime/delta-conflict.ts +348 -0
  205. package/src/runtime/deterministic-ast.ts +161 -0
  206. package/src/runtime/diagnostic-export.ts +171 -0
  207. package/src/runtime/direct-run.ts +45 -0
  208. package/src/runtime/dwf-state-store.ts +102 -0
  209. package/src/runtime/dynamic-workflow-context.ts +1013 -0
  210. package/src/runtime/dynamic-workflow-runner.ts +325 -0
  211. package/src/runtime/effectiveness.ts +90 -0
  212. package/src/runtime/errors/crew-errors.ts +162 -0
  213. package/src/runtime/event-stream-bridge.ts +98 -0
  214. package/src/runtime/foreground-control.ts +193 -0
  215. package/src/runtime/foreground-watchdog.ts +132 -0
  216. package/src/runtime/global-worker-cap.ts +96 -0
  217. package/src/runtime/goal-achievement.ts +148 -0
  218. package/src/runtime/goal-evaluator.ts +365 -0
  219. package/src/runtime/goal-loop-runner.ts +825 -0
  220. package/src/runtime/goal-state-store.ts +219 -0
  221. package/src/runtime/green-contract.ts +55 -0
  222. package/src/runtime/group-join.ts +267 -0
  223. package/src/runtime/handoff-manager.ts +598 -0
  224. package/src/runtime/heartbeat-gradient.ts +36 -0
  225. package/src/runtime/heartbeat-watcher.ts +239 -0
  226. package/src/runtime/hidden-handoff.ts +407 -0
  227. package/src/runtime/important-line-classifier.ts +140 -0
  228. package/src/runtime/intercom-bridge.ts +187 -0
  229. package/src/runtime/iteration-hooks.ts +305 -0
  230. package/src/runtime/live-agent-control.ts +136 -0
  231. package/src/runtime/live-agent-manager.ts +704 -0
  232. package/src/runtime/live-control-realtime.ts +56 -0
  233. package/src/runtime/live-extension-bridge.ts +148 -0
  234. package/src/runtime/live-irc.ts +97 -0
  235. package/src/runtime/live-session-health.ts +108 -0
  236. package/src/runtime/live-session-runtime.ts +1146 -0
  237. package/src/runtime/loop-gates.ts +128 -0
  238. package/src/runtime/manifest-cache.ts +375 -0
  239. package/src/runtime/mcp-proxy.ts +105 -0
  240. package/src/runtime/metric-parser.ts +36 -0
  241. package/src/runtime/model-fallback.ts +409 -0
  242. package/src/runtime/model-resolver.ts +126 -0
  243. package/src/runtime/model-scope.ts +158 -0
  244. package/src/runtime/orphan-worker-registry.ts +465 -0
  245. package/src/runtime/output-validator.ts +202 -0
  246. package/src/runtime/overflow-recovery.ts +206 -0
  247. package/src/runtime/parallel-research.ts +68 -0
  248. package/src/runtime/parallel-utils.ts +160 -0
  249. package/src/runtime/parent-guard.ts +134 -0
  250. package/src/runtime/path-overlap.ts +150 -0
  251. package/src/runtime/peer-dep.ts +292 -0
  252. package/src/runtime/per-write-validator.ts +181 -0
  253. package/src/runtime/phase-progress.ts +217 -0
  254. package/src/runtime/phase-tracker.ts +385 -0
  255. package/src/runtime/pi-args.ts +655 -0
  256. package/src/runtime/pi-json-output.ts +170 -0
  257. package/src/runtime/pi-spawn.ts +273 -0
  258. package/src/runtime/pipeline-runner.ts +523 -0
  259. package/src/runtime/plan-templates.ts +201 -0
  260. package/src/runtime/policy-engine.ts +115 -0
  261. package/src/runtime/post-checks.ts +142 -0
  262. package/src/runtime/post-exit-stdio-guard.ts +109 -0
  263. package/src/runtime/process-lifecycle.ts +491 -0
  264. package/src/runtime/process-status.ts +154 -0
  265. package/src/runtime/progress-event-coalescer.ts +44 -0
  266. package/src/runtime/progress-tracker.ts +124 -0
  267. package/src/runtime/prose-compressor.ts +162 -0
  268. package/src/runtime/recovery-recipes.ts +203 -0
  269. package/src/runtime/replace.ts +570 -0
  270. package/src/runtime/resilient-edit.ts +153 -0
  271. package/src/runtime/result-extractor.ts +217 -0
  272. package/src/runtime/retry-executor.ts +109 -0
  273. package/src/runtime/retry-runner.ts +336 -0
  274. package/src/runtime/role-permission.ts +59 -0
  275. package/src/runtime/run-coalesced-task-group.ts +308 -0
  276. package/src/runtime/run-drift.ts +219 -0
  277. package/src/runtime/run-tracker.ts +116 -0
  278. package/src/runtime/run-worker.ts +78 -0
  279. package/src/runtime/runtime-policy.ts +29 -0
  280. package/src/runtime/runtime-resolver.ts +172 -0
  281. package/src/runtime/runtime-warmup.ts +160 -0
  282. package/src/runtime/scheduler.ts +353 -0
  283. package/src/runtime/semaphore.ts +141 -0
  284. package/src/runtime/sensitive-paths.ts +93 -0
  285. package/src/runtime/session-resources.ts +25 -0
  286. package/src/runtime/session-snapshot.ts +59 -0
  287. package/src/runtime/session-usage.ts +79 -0
  288. package/src/runtime/settings-store.ts +155 -0
  289. package/src/runtime/sidechain-output.ts +35 -0
  290. package/src/runtime/single-agent-compose.ts +84 -0
  291. package/src/runtime/skill-effectiveness.ts +524 -0
  292. package/src/runtime/skill-instructions.ts +394 -0
  293. package/src/runtime/stale-reconciler.ts +680 -0
  294. package/src/runtime/stream-preview.ts +184 -0
  295. package/src/runtime/streaming-output.ts +50 -0
  296. package/src/runtime/subagent-manager.ts +561 -0
  297. package/src/runtime/subprocess-tool-registry.ts +70 -0
  298. package/src/runtime/supervisor-contact.ts +64 -0
  299. package/src/runtime/task-display.ts +52 -0
  300. package/src/runtime/task-graph-scheduler.ts +227 -0
  301. package/src/runtime/task-graph.ts +290 -0
  302. package/src/runtime/task-health.ts +83 -0
  303. package/src/runtime/task-id.ts +161 -0
  304. package/src/runtime/task-output-context.ts +602 -0
  305. package/src/runtime/task-packet.ts +268 -0
  306. package/src/runtime/task-quality.ts +199 -0
  307. package/src/runtime/task-runner/capabilities.ts +78 -0
  308. package/src/runtime/task-runner/child-executor.ts +790 -0
  309. package/src/runtime/task-runner/context-retrieval.ts +163 -0
  310. package/src/runtime/task-runner/live-executor.ts +227 -0
  311. package/src/runtime/task-runner/output-splitter.ts +152 -0
  312. package/src/runtime/task-runner/post-execution.ts +480 -0
  313. package/src/runtime/task-runner/pre-execution.ts +365 -0
  314. package/src/runtime/task-runner/progress.ts +157 -0
  315. package/src/runtime/task-runner/prompt-builder.ts +278 -0
  316. package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
  317. package/src/runtime/task-runner/result-utils.ts +16 -0
  318. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  319. package/src/runtime/task-runner/run-projection.ts +125 -0
  320. package/src/runtime/task-runner/scaffold-executor.ts +25 -0
  321. package/src/runtime/task-runner/state-helpers.ts +176 -0
  322. package/src/runtime/task-runner/tail-read.ts +34 -0
  323. package/src/runtime/task-runner.ts +216 -0
  324. package/src/runtime/team-runner-artifacts.ts +13 -0
  325. package/src/runtime/team-runner.ts +2396 -0
  326. package/src/runtime/tool-output-pruner.ts +333 -0
  327. package/src/runtime/tool-progress.ts +278 -0
  328. package/src/runtime/usage-tracker.ts +73 -0
  329. package/src/runtime/verification-gates.ts +579 -0
  330. package/src/runtime/verification-integrity.ts +108 -0
  331. package/src/runtime/verification-worktree.ts +186 -0
  332. package/src/runtime/worker-heartbeat.ts +25 -0
  333. package/src/runtime/worker-startup.ts +83 -0
  334. package/src/runtime/workflow-state.ts +195 -0
  335. package/src/runtime/workspace-lock.ts +443 -0
  336. package/src/runtime/workspace-tree.ts +312 -0
  337. package/src/runtime/yield-handler.ts +224 -0
  338. package/src/runtime/zombie-scanner.ts +298 -0
  339. package/src/schema/config-schema.ts +327 -0
  340. package/src/schema/team-tool-schema.ts +492 -0
  341. package/src/schema/validation-types.ts +159 -0
  342. package/src/skills/discover-skills.ts +203 -0
  343. package/src/skills/skill-templates.ts +456 -0
  344. package/src/skills/validate.ts +285 -0
  345. package/src/state/active-run-registry.ts +439 -0
  346. package/src/state/artifact-store.ts +176 -0
  347. package/src/state/atomic-write.ts +970 -0
  348. package/src/state/blob-store.ts +308 -0
  349. package/src/state/contracts.ts +160 -0
  350. package/src/state/crew-init.ts +269 -0
  351. package/src/state/decision-ledger.ts +372 -0
  352. package/src/state/event-log-rotation.ts +399 -0
  353. package/src/state/event-log.ts +1465 -0
  354. package/src/state/event-reconstructor.ts +229 -0
  355. package/src/state/gitignore-manager.ts +46 -0
  356. package/src/state/health-store.ts +79 -0
  357. package/src/state/hook-instinct-bridge.ts +94 -0
  358. package/src/state/hook-integrations.ts +51 -0
  359. package/src/state/instinct-store.ts +275 -0
  360. package/src/state/jsonl-writer.ts +107 -0
  361. package/src/state/locks.ts +562 -0
  362. package/src/state/mailbox.ts +916 -0
  363. package/src/state/observation-store.ts +176 -0
  364. package/src/state/run-cache.ts +193 -0
  365. package/src/state/run-graph.ts +183 -0
  366. package/src/state/run-metrics.ts +165 -0
  367. package/src/state/schedule.ts +166 -0
  368. package/src/state/session-state-map.ts +51 -0
  369. package/src/state/state-store.ts +973 -0
  370. package/src/state/task-claims.ts +61 -0
  371. package/src/state/tiered-eval.ts +480 -0
  372. package/src/state/types-eval.ts +58 -0
  373. package/src/state/types.ts +447 -0
  374. package/src/state/usage.ts +138 -0
  375. package/src/state/worker-atomic-writer.ts +198 -0
  376. package/src/subagents/async-entry.ts +1 -0
  377. package/src/subagents/index.ts +3 -0
  378. package/src/subagents/live/control.ts +1 -0
  379. package/src/subagents/live/manager.ts +1 -0
  380. package/src/subagents/live/realtime.ts +1 -0
  381. package/src/subagents/live/session-runtime.ts +1 -0
  382. package/src/subagents/manager.ts +1 -0
  383. package/src/subagents/spawn.ts +1 -0
  384. package/src/teams/discover-teams.ts +187 -0
  385. package/src/teams/team-config.ts +27 -0
  386. package/src/teams/team-serializer.ts +38 -0
  387. package/src/tools/safe-bash-extension.ts +54 -0
  388. package/src/tools/safe-bash.ts +505 -0
  389. package/src/types/diff.d.ts +18 -0
  390. package/src/types/new-api-types.ts +35 -0
  391. package/src/ui/agent-management-overlay.ts +160 -0
  392. package/src/ui/card-colors.ts +139 -0
  393. package/src/ui/crew-footer.ts +102 -0
  394. package/src/ui/crew-select-list.ts +112 -0
  395. package/src/ui/dashboard-panes/agents-pane.ts +164 -0
  396. package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
  397. package/src/ui/dashboard-panes/capability-pane.ts +77 -0
  398. package/src/ui/dashboard-panes/health-pane.ts +31 -0
  399. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
  400. package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
  401. package/src/ui/dashboard-panes/progress-pane.ts +38 -0
  402. package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
  403. package/src/ui/deploy-bundled-themes.ts +71 -0
  404. package/src/ui/dwf-phase-display.ts +151 -0
  405. package/src/ui/dynamic-border.ts +35 -0
  406. package/src/ui/format-helpers.ts +31 -0
  407. package/src/ui/heartbeat-aggregator.ts +74 -0
  408. package/src/ui/key-utils.ts +42 -0
  409. package/src/ui/keybinding-map.ts +248 -0
  410. package/src/ui/layout-primitives.ts +106 -0
  411. package/src/ui/live-conversation-overlay.ts +183 -0
  412. package/src/ui/live-duration.ts +55 -0
  413. package/src/ui/live-run-sidebar.ts +293 -0
  414. package/src/ui/loaders.ts +6 -0
  415. package/src/ui/mascot.ts +449 -0
  416. package/src/ui/overlays/agent-picker-overlay.ts +66 -0
  417. package/src/ui/overlays/confirm-overlay.ts +60 -0
  418. package/src/ui/overlays/help-overlay.ts +177 -0
  419. package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
  420. package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
  421. package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
  422. package/src/ui/pi-ui-compat.ts +68 -0
  423. package/src/ui/powerbar-publisher.ts +459 -0
  424. package/src/ui/render-coalescer.ts +101 -0
  425. package/src/ui/render-diff.ts +160 -0
  426. package/src/ui/render-scheduler.ts +278 -0
  427. package/src/ui/run-action-dispatcher.ts +182 -0
  428. package/src/ui/run-dashboard.ts +910 -0
  429. package/src/ui/run-event-bus.ts +381 -0
  430. package/src/ui/run-snapshot-cache.ts +1057 -0
  431. package/src/ui/settings-overlay.ts +1067 -0
  432. package/src/ui/shared-overlay-scheduler.ts +96 -0
  433. package/src/ui/snapshot-types.ts +90 -0
  434. package/src/ui/spinner.ts +17 -0
  435. package/src/ui/status-colors.ts +143 -0
  436. package/src/ui/syntax-highlight.ts +136 -0
  437. package/src/ui/terminal-status.ts +269 -0
  438. package/src/ui/theme-adapter.ts +271 -0
  439. package/src/ui/theme-discovery.ts +199 -0
  440. package/src/ui/tool-progress-formatter.ts +93 -0
  441. package/src/ui/tool-renderers/brief-mode.ts +225 -0
  442. package/src/ui/tool-renderers/index.ts +726 -0
  443. package/src/ui/transcript-cache.ts +128 -0
  444. package/src/ui/transcript-entries.ts +256 -0
  445. package/src/ui/transcript-viewer.ts +424 -0
  446. package/src/ui/widget/index.ts +418 -0
  447. package/src/ui/widget/widget-formatters.ts +182 -0
  448. package/src/ui/widget/widget-model.ts +110 -0
  449. package/src/ui/widget/widget-renderer.ts +182 -0
  450. package/src/ui/widget/widget-types.ts +36 -0
  451. package/src/utils/bm25-search.ts +235 -0
  452. package/src/utils/completion-dedupe.ts +63 -0
  453. package/src/utils/conflict-detect.ts +668 -0
  454. package/src/utils/env-allowlist.ts +30 -0
  455. package/src/utils/env-filter.ts +155 -0
  456. package/src/utils/file-coalescer.ts +98 -0
  457. package/src/utils/fingerprint.ts +180 -0
  458. package/src/utils/frontmatter.ts +70 -0
  459. package/src/utils/fs-watch.ts +37 -0
  460. package/src/utils/gh-protocol.ts +556 -0
  461. package/src/utils/git.ts +260 -0
  462. package/src/utils/guards.ts +107 -0
  463. package/src/utils/ids.ts +24 -0
  464. package/src/utils/incremental-reader.ts +220 -0
  465. package/src/utils/internal-error.ts +10 -0
  466. package/src/utils/names.ts +36 -0
  467. package/src/utils/ndjson.ts +115 -0
  468. package/src/utils/paths.ts +227 -0
  469. package/src/utils/project-detector.ts +160 -0
  470. package/src/utils/redaction.ts +370 -0
  471. package/src/utils/resolve-shell.ts +36 -0
  472. package/src/utils/run-watcher-registry.ts +152 -0
  473. package/src/utils/safe-paths.ts +393 -0
  474. package/src/utils/scan-cache.ts +144 -0
  475. package/src/utils/session-utils.ts +110 -0
  476. package/src/utils/sleep.ts +54 -0
  477. package/src/utils/socket-path.ts +164 -0
  478. package/src/utils/sse-parser.ts +131 -0
  479. package/src/utils/task-name-generator.ts +337 -0
  480. package/src/utils/timings.ts +33 -0
  481. package/src/utils/token-counter.ts +200 -0
  482. package/src/utils/visual.ts +207 -0
  483. package/src/workflows/cost-estimator.ts +34 -0
  484. package/src/workflows/discover-workflows.ts +308 -0
  485. package/src/workflows/intermediate-store.ts +166 -0
  486. package/src/workflows/preflight-validator.ts +168 -0
  487. package/src/workflows/topology-analyzer.ts +203 -0
  488. package/src/workflows/validate-workflow.ts +50 -0
  489. package/src/workflows/workflow-config.ts +75 -0
  490. package/src/workflows/workflow-serializer.ts +32 -0
  491. package/src/worktree/branch-freshness.ts +112 -0
  492. package/src/worktree/cleanup.ts +341 -0
  493. package/src/worktree/worktree-manager.ts +1204 -0
@@ -0,0 +1,155 @@
1
+ import { isSecretKey } from "./redaction.ts";
2
+
3
+ // Well-known LLM provider API keys that are intentionally allowlisted in
4
+ // child-pi.ts and async-runner.ts for pass-through to worker processes.
5
+ // These are "secret" by pattern (contain KEY/API) but are safe to allowlist
6
+ // because they are standard provider credentials, not arbitrary secrets.
7
+ const KNOWN_PROVIDER_KEYS = new Set([
8
+ "MINIMAX_API_KEY",
9
+ "MINIMAX_GROUP_ID",
10
+ "OPENAI_API_KEY",
11
+ "OPENAI_ORG_ID",
12
+ "ANTHROPIC_API_KEY",
13
+ "GOOGLE_API_KEY",
14
+ "GOOGLE_GENERATIVE_LANGUAGE_API_KEY",
15
+ "AZURE_OPENAI_API_KEY",
16
+ "AZURE_OPENAI_ENDPOINT",
17
+ "AWS_ACCESS_KEY_ID",
18
+ "AWS_SECRET_ACCESS_KEY",
19
+ "AWS_REGION",
20
+ "ZEU_API_KEY",
21
+ "ZERODEV_API_KEY",
22
+ ]);
23
+
24
+ // Map from provider prefix (lowercase) to env var names.
25
+ // Used by providerEnvKeys() to scope API keys per task.
26
+ const PROVIDER_ENV_KEY_MAP: Record<string, string[]> = {
27
+ minimax: ["MINIMAX_API_KEY", "MINIMAX_GROUP_ID"],
28
+ openai: ["OPENAI_API_KEY", "OPENAI_ORG_ID"],
29
+ anthropic: ["ANTHROPIC_API_KEY"],
30
+ google: ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],
31
+ gemini: ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],
32
+ azure: ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
33
+ "azure-openai": ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
34
+ aws: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"],
35
+ bedrock: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"],
36
+ zai: ["ZEU_API_KEY"],
37
+ zerodev: ["ZERODEV_API_KEY"],
38
+ };
39
+
40
+ function isKnownProviderKey(key: string): boolean {
41
+ return KNOWN_PROVIDER_KEYS.has(key);
42
+ }
43
+
44
+ /**
45
+ * Extract provider prefix from a model ID string and return the corresponding
46
+ * env var names. Returns empty array for unknown/custom providers or invalid input.
47
+ *
48
+ * @param modelId - Model in "provider/modelId" format (e.g. "openai/gpt-4o")
49
+ * @returns Array of env var names needed for this provider
50
+ */
51
+ export function providerEnvKeys(modelId: string | undefined): string[] {
52
+ if (!modelId) return [];
53
+ const separatorIndex = modelId.indexOf("/");
54
+ if (separatorIndex <= 0) return [];
55
+ const provider = modelId.substring(0, separatorIndex).toLowerCase();
56
+ return PROVIDER_ENV_KEY_MAP[provider] ?? [];
57
+ }
58
+
59
+ /**
60
+ * Build a scoped allowlist that includes system vars + only the provider keys
61
+ * relevant to the assigned model (and its fallback chain).
62
+ *
63
+ * @param baseAllowList - Non-provider system vars to always include
64
+ * @param models - Model IDs to include provider keys for (primary + fallbacks)
65
+ * @returns Combined allowlist with scoped provider keys
66
+ */
67
+ export function buildScopedAllowList(baseAllowList: string[], models: (string | undefined)[]): string[] {
68
+ const providerKeys = new Set<string>();
69
+ for (const model of models) {
70
+ for (const key of providerEnvKeys(model)) {
71
+ providerKeys.add(key);
72
+ }
73
+ }
74
+ return [...baseAllowList, ...providerKeys];
75
+ }
76
+
77
+ export interface SanitizeEnvOptions {
78
+ /** Allow-list of env var names to preserve. Supports trailing glob, e.g. `"PI_*"`. */
79
+ allowList?: string[];
80
+ }
81
+
82
+ // Keywords that indicate a secret env var
83
+ const SECRET_SUFFIXES = ["token", "api", "key", "password", "passwd", "secret", "credential", "authorization", "private"];
84
+
85
+ /**
86
+ * Check if a glob pattern could match secret env vars.
87
+ * A pattern like "PI_*" is dangerous because it could match PI_TOKEN, PI_API_KEY, etc.
88
+ *
89
+ * Exception: `PI_CREW_*` is a controlled namespace — the pi-crew codebase owns
90
+ * every PI_CREW_* env var (PI_CREW_PARENT_PID, PI_CREW_ADAPTIVE_REPAIR, etc.)
91
+ * and none of them are secrets. Allowing the glob here lets child Pi processes
92
+ * inherit our config without needing a per-var allowlist.
93
+ */
94
+ function isDangerousGlob(pattern: string): boolean {
95
+ if (!pattern.endsWith("*")) return false;
96
+ const prefix = pattern.slice(0, -1); // Remove trailing *
97
+ if (prefix === "") return true; // Single "*" matches everything
98
+ // PI_CREW_* is the pi-crew controlled namespace — no secrets live here.
99
+ // This covers PI_CREW_*, PI_CREW_TEAMS_*, PI_CREW_AGENT_*, etc.
100
+ if (prefix.startsWith("PI_CREW_") || prefix === "PI_CREW") return false;
101
+ // Check if combining the prefix with any secret suffix would create a secret key
102
+ for (const suffix of SECRET_SUFFIXES) {
103
+ if (isSecretKey(prefix + suffix)) {
104
+ return true;
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Strip env vars whose keys look like secrets before passing to child processes.
112
+ *
113
+ * Default mode (no allowList): deny-list using isSecretKey.
114
+ * When allowList is provided, only keys matching the allow-list are preserved.
115
+ */
116
+ export function sanitizeEnvSecrets(env: NodeJS.ProcessEnv, options?: SanitizeEnvOptions): Record<string, string> {
117
+ const filtered: Record<string, string> = {};
118
+ if (options?.allowList && options.allowList.length > 0) {
119
+ // Validate allowlist patterns don't match secrets
120
+ for (const pattern of options.allowList) {
121
+ if (isDangerousGlob(pattern)) {
122
+ throw new Error(`Allowlist pattern "${pattern}" could match secret env vars. Use a more specific pattern.`);
123
+ }
124
+ // Validate non-glob entries don't look like secret keys.
125
+ // Exception 1: if the key exists in env, it was intentionally set and
126
+ // should be allowed through (user knows what they're doing).
127
+ // Exception 2: known provider keys (MINIMAX_API_KEY, etc.) are always
128
+ // allowed because they are standard provider credentials explicitly
129
+ // listed in async-runner.ts / child-pi.ts allowlists.
130
+ if (!pattern.endsWith("*") && isSecretKey(pattern) && !(pattern in env) && !isKnownProviderKey(pattern)) {
131
+ throw new Error(`Allowlist entry "${pattern}" looks like a secret key. Use a more specific pattern.`);
132
+ }
133
+ }
134
+ const matchers = options.allowList.map((p) => {
135
+ if (p.endsWith("*")) {
136
+ // Glob pattern: matches keys that start with the prefix AND have
137
+ // at least one additional character (distinguishes "PI_CREW_*" from "PI_CREW_").
138
+ // For example, "PI_CREW_*" matches "PI_CREW_DEPTH" but not "PI_CREW_".
139
+ // This ensures trailing glob patterns require extra chars, not exact-prefix-only matches.
140
+ const prefix = p.slice(0, -1);
141
+ return (k: string) => k.startsWith(prefix) && k.length > prefix.length;
142
+ }
143
+ // Exact match is case-sensitive; Unix env vars are uppercase by convention.
144
+ return (k: string) => k === p;
145
+ });
146
+ for (const [key, value] of Object.entries(env)) {
147
+ if (value !== undefined && matchers.some((fn) => fn(key))) filtered[key] = value;
148
+ }
149
+ return filtered;
150
+ }
151
+ for (const [key, value] of Object.entries(env)) {
152
+ if (value !== undefined && !isSecretKey(key)) filtered[key] = value;
153
+ }
154
+ return filtered;
155
+ }
@@ -0,0 +1,98 @@
1
+ import * as fs from "node:fs";
2
+
3
+ interface TimerApi {
4
+ setTimeout(handler: () => void, delayMs: number): unknown;
5
+ clearTimeout(handle: unknown): void;
6
+ }
7
+
8
+ const defaultTimerApi: TimerApi = {
9
+ setTimeout: (handler, delayMs) => {
10
+ const t = setTimeout(handler, delayMs);
11
+ // Defense in depth: never let a coalescer timer block process exit.
12
+ // The timer may be cleared before it fires; .unref() is idempotent.
13
+ if (typeof t === "object" && t && "unref" in t && typeof t.unref === "function") {
14
+ t.unref();
15
+ }
16
+ return t;
17
+ },
18
+ clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),
19
+ };
20
+
21
+ export interface FileCoalescer {
22
+ schedule(file: string, delayMs?: number): boolean;
23
+ clear(): void;
24
+ }
25
+
26
+ export function createFileCoalescer(
27
+ handler: (file: string) => void,
28
+ defaultDelayMs: number,
29
+ timerApi: TimerApi = defaultTimerApi,
30
+ ): FileCoalescer {
31
+ const pending = new Map<string, unknown>();
32
+ return {
33
+ schedule(file, delayMs = defaultDelayMs) {
34
+ if (pending.has(file)) return false;
35
+ const timer = timerApi.setTimeout(() => {
36
+ pending.delete(file);
37
+ handler(file);
38
+ }, delayMs);
39
+ pending.set(file, timer);
40
+ return true;
41
+ },
42
+ clear() {
43
+ for (const timer of pending.values()) timerApi.clearTimeout(timer);
44
+ pending.clear();
45
+ },
46
+ };
47
+ }
48
+
49
+ interface ReadCacheEntry<T> {
50
+ value: T;
51
+ mtimeMs: number;
52
+ size: number;
53
+ expiresAt: number;
54
+ }
55
+
56
+ const readCache = new Map<string, ReadCacheEntry<unknown>>();
57
+ const readCacheSizeLimit = 128;
58
+
59
+ function evictOldestCacheEntry(): void {
60
+ if (readCache.size < readCacheSizeLimit) return;
61
+ // Map iteration order is insertion order; first key is LRU.
62
+ const oldestKey = readCache.keys().next().value;
63
+ if (oldestKey !== undefined) readCache.delete(oldestKey);
64
+ }
65
+
66
+ export function clearReadCache(): void {
67
+ readCache.clear();
68
+ }
69
+
70
+ export function readJsonFileCoalesced<T>(filePath: string, ttlMs: number, read: () => T): T {
71
+ const now = Date.now();
72
+ const stat = (() => {
73
+ try {
74
+ const fileStat = fs.statSync(filePath);
75
+ return { mtimeMs: fileStat.mtimeMs, size: fileStat.size };
76
+ } catch {
77
+ return undefined;
78
+ }
79
+ })();
80
+ const cached = readCache.get(filePath);
81
+ if (cached && stat && cached.expiresAt > now && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
82
+ // Re-insert to implement LRU: move to end of Map.
83
+ readCache.delete(filePath);
84
+ readCache.set(filePath, cached);
85
+ return cached.value as T;
86
+ }
87
+ const value = read();
88
+ if (stat !== undefined) {
89
+ readCache.set(filePath, {
90
+ value,
91
+ mtimeMs: stat.mtimeMs,
92
+ size: stat.size,
93
+ expiresAt: now + ttlMs,
94
+ });
95
+ evictOldestCacheEntry();
96
+ }
97
+ return value;
98
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Incremental fingerprinting — detect file changes to skip unchanged work.
3
+ *
4
+ * Pattern origin: Understand-Anything/packages/core/src/fingerprint.ts
5
+ * Content hash + structural signature per file. Change classifier:
6
+ * NONE (unchanged), COSMETIC (whitespace/comments only), STRUCTURAL (code changed).
7
+ * Only STRUCTURAL changes trigger re-analysis.
8
+ */
9
+
10
+ import { createHash } from "node:crypto";
11
+ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
12
+ import { logInternalError } from "../utils/internal-error.ts";
13
+
14
+ // ── Types ────────────────────────────────────────────────────────────────
15
+
16
+ export type ChangeClass = "NONE" | "COSMETIC" | "STRUCTURAL";
17
+
18
+ export interface FileFingerprint {
19
+ path: string;
20
+ contentHash: string;
21
+ structuralSignature: string;
22
+ lastModified: number; // mtime ms
23
+ changeClass: ChangeClass;
24
+ }
25
+
26
+ export interface FingerprintDelta {
27
+ added: string[];
28
+ removed: string[];
29
+ modified: FileFingerprint[]; // only STRUCTURAL changes
30
+ unchanged: number;
31
+ }
32
+
33
+ // ── Fingerprinting ───────────────────────────────────────────────────────
34
+
35
+ /**
36
+ * Compute SHA-256 content hash of a file.
37
+ */
38
+ export function computeContentHash(filePath: string): string {
39
+ try {
40
+ const content = readFileSync(filePath);
41
+ return createHash("sha256").update(content).digest("hex");
42
+ } catch {
43
+ return "";
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Extract a structural signature from source code.
49
+ *
50
+ * Captures function signatures, class methods, and import specifiers.
51
+ * Ignores whitespace, comments, and string content.
52
+ * Returns a hash of the structural elements.
53
+ */
54
+ export function computeStructuralSignature(content: string, filePath: string): string {
55
+ const lines = content.split("\n");
56
+ const structural: string[] = [];
57
+
58
+ for (const line of lines) {
59
+ const trimmed = line.trim();
60
+
61
+ // Skip empty lines and comments
62
+ if (trimmed.length === 0) continue;
63
+ if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) continue;
64
+
65
+ // Capture structural lines
66
+ if (
67
+ // Function/method declarations
68
+ /^(export\s+)?(async\s+)?function\s/.test(trimmed) ||
69
+ (/^\w+\s*\(/.test(trimmed) && !/^(if|for|while|switch|return|throw|await)/.test(trimmed)) ||
70
+ // Class declarations
71
+ /^(export\s+)?(abstract\s+)?class\s/.test(trimmed) ||
72
+ /^(public|private|protected|static|readonly|abstract)\s/.test(trimmed) ||
73
+ // Interface/type declarations
74
+ /^(export\s+)?(interface|type)\s/.test(trimmed) ||
75
+ // Import declarations
76
+ /^import\s/.test(trimmed) ||
77
+ // Export declarations
78
+ /^export\s+(default\s+)?(const|let|var|function|class|interface|type|enum)\s/.test(trimmed)
79
+ ) {
80
+ structural.push(trimmed);
81
+ }
82
+ }
83
+
84
+ return createHash("sha256").update(structural.join("\n")).digest("hex");
85
+ }
86
+
87
+ /**
88
+ * Classify the change between two fingerprints.
89
+ */
90
+ export function classifyChange(previous: FileFingerprint | undefined, current: FileFingerprint): ChangeClass {
91
+ if (!previous) return "STRUCTURAL"; // New file
92
+
93
+ if (previous.contentHash === current.contentHash) return "NONE";
94
+ if (previous.structuralSignature === current.structuralSignature) return "COSMETIC";
95
+ return "STRUCTURAL";
96
+ }
97
+
98
+ /**
99
+ * Compute fingerprint for a single file.
100
+ */
101
+ export function fingerprintFile(filePath: string): FileFingerprint {
102
+ const content = readFileSync(filePath, "utf-8");
103
+ const stat = statSync(filePath);
104
+
105
+ return {
106
+ path: filePath,
107
+ contentHash: computeContentHash(filePath),
108
+ structuralSignature: computeStructuralSignature(content, filePath),
109
+ lastModified: stat.mtimeMs,
110
+ changeClass: "NONE", // will be classified during delta computation
111
+ };
112
+ }
113
+
114
+ // ── Fingerprint Store ────────────────────────────────────────────────────
115
+
116
+ const MAX_FINGERPRINTS = 10000;
117
+
118
+ /**
119
+ * Load fingerprint baseline from a JSON file.
120
+ */
121
+ export function loadFingerprintBaseline(storePath: string): Map<string, FileFingerprint> {
122
+ const map = new Map<string, FileFingerprint>();
123
+ if (!existsSync(storePath)) return map;
124
+
125
+ try {
126
+ const data = JSON.parse(readFileSync(storePath, "utf-8")) as FileFingerprint[];
127
+ for (const fp of data) {
128
+ if (map.size >= MAX_FINGERPRINTS) break;
129
+ map.set(fp.path, fp);
130
+ }
131
+ } catch (error) {
132
+ logInternalError("fingerprint.load", error, `storePath=${storePath}`);
133
+ }
134
+ return map;
135
+ }
136
+
137
+ /**
138
+ * Save fingerprint baseline to a JSON file.
139
+ */
140
+ export function saveFingerprintBaseline(storePath: string, fingerprints: Map<string, FileFingerprint>): void {
141
+ const entries = [...fingerprints.entries()].slice(0, MAX_FINGERPRINTS).map(([, fp]) => fp);
142
+ writeFileSync(storePath, JSON.stringify(entries, null, 2), "utf-8");
143
+ }
144
+
145
+ /**
146
+ * Compute delta between baseline and current fingerprints.
147
+ *
148
+ * Only returns STRUCTURAL changes in the `modified` field.
149
+ */
150
+ export function computeFingerprintDelta(baseline: Map<string, FileFingerprint>, current: Map<string, FileFingerprint>): FingerprintDelta {
151
+ const added: string[] = [];
152
+ const removed: string[] = [];
153
+ const modified: FileFingerprint[] = [];
154
+ let unchanged = 0;
155
+
156
+ // Find added and modified
157
+ for (const [path, currentFp] of current) {
158
+ const prev = baseline.get(path);
159
+ const changeClass = classifyChange(prev, currentFp);
160
+
161
+ if (!prev) {
162
+ added.push(path);
163
+ } else if (changeClass === "STRUCTURAL") {
164
+ modified.push({ ...currentFp, changeClass: "STRUCTURAL" });
165
+ } else if (changeClass === "COSMETIC") {
166
+ unchanged++; // cosmetic = treated as unchanged for re-analysis
167
+ } else {
168
+ unchanged++;
169
+ }
170
+ }
171
+
172
+ // Find removed
173
+ for (const path of baseline.keys()) {
174
+ if (!current.has(path)) {
175
+ removed.push(path);
176
+ }
177
+ }
178
+
179
+ return { added, removed, modified, unchanged };
180
+ }
@@ -0,0 +1,70 @@
1
+ export interface ParsedFrontmatter {
2
+ frontmatter: Record<string, string>;
3
+ body: string;
4
+ }
5
+
6
+ export function parseFrontmatter(content: string): ParsedFrontmatter {
7
+ if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
8
+ return { frontmatter: {}, body: content };
9
+ }
10
+
11
+ const normalized = content.replaceAll("\r\n", "\n");
12
+ const end = normalized.indexOf("\n---\n", 4);
13
+ if (end === -1) {
14
+ // Support frontmatter that ends at EOF without trailing newline after ---.
15
+ const altEnd = normalized.indexOf("\n---", 4);
16
+ if (altEnd !== -1 && altEnd + 4 === normalized.length) {
17
+ const raw = normalized.slice(4, altEnd);
18
+ const frontmatter = parseLines(raw);
19
+ return { frontmatter, body: "" };
20
+ }
21
+ return { frontmatter: {}, body: content };
22
+ }
23
+
24
+ const raw = normalized.slice(4, end);
25
+ const body = normalized.slice(end + "\n---\n".length);
26
+ const frontmatter = parseLines(raw);
27
+ return { frontmatter, body };
28
+ }
29
+
30
+ function parseLines(raw: string): Record<string, string> {
31
+ const frontmatter: Record<string, string> = {};
32
+ for (const line of raw.split("\n")) {
33
+ const trimmed = line.trim();
34
+ if (!trimmed || trimmed.startsWith("#")) continue;
35
+ const separator = trimmed.indexOf(":");
36
+ if (separator === -1) continue;
37
+ const key = trimmed.slice(0, separator).trim();
38
+ const value = trimmed.slice(separator + 1).trim();
39
+ if (key) frontmatter[key] = value;
40
+ }
41
+ return frontmatter;
42
+ }
43
+
44
+ export function parseCsv(value: string | undefined): string[] | undefined {
45
+ if (value === undefined) return undefined;
46
+ // Handle quoted values with commas inside.
47
+ const values = splitCsv(value)
48
+ .map((item) => item.trim())
49
+ .filter(Boolean);
50
+ return values.length > 0 ? [...new Set(values)] : undefined;
51
+ }
52
+
53
+ function splitCsv(input: string): string[] {
54
+ const result: string[] = [];
55
+ let current = "";
56
+ let inQuotes = false;
57
+ for (let i = 0; i < input.length; i++) {
58
+ const char = input[i];
59
+ if (char === '"') {
60
+ inQuotes = !inQuotes;
61
+ } else if (char === "," && !inQuotes) {
62
+ result.push(current);
63
+ current = "";
64
+ } else {
65
+ current += char;
66
+ }
67
+ }
68
+ result.push(current);
69
+ return result;
70
+ }
@@ -0,0 +1,37 @@
1
+ import type { FSWatcher, WatchListener } from "node:fs";
2
+ import * as fs from "node:fs";
3
+
4
+ /**
5
+ * Filesystem watcher helpers (slimmed down — pts/2 hang fix 2026-06-16).
6
+ *
7
+ * The recursive-watcher helpers (createRecursiveWatcher / watchCrewState /
8
+ * runIdFromStateRelativePath) were REMOVED: a recursive fs.watch on the run
9
+ * state tree exploded to O(total run history) inotify watches on Linux and
10
+ * caused a permanent interactive-session busy-loop. The bounded
11
+ * {@link RunWatcherRegistry} (one non-recursive watcher per ACTIVE run) now
12
+ * replaces them. Only the two primitives below survive — they are still used by
13
+ * manifest-cache, result-watcher, and run-watcher-registry.
14
+ */
15
+
16
+ export function closeWatcher(watcher: FSWatcher | null | undefined): void {
17
+ if (!watcher) {
18
+ return;
19
+ }
20
+
21
+ try {
22
+ watcher.close();
23
+ } catch {
24
+ // Ignore watcher close errors
25
+ }
26
+ }
27
+
28
+ export function watchWithErrorHandler(path: string, listener: WatchListener<string>, onError: (error?: unknown) => void): FSWatcher | null {
29
+ try {
30
+ const watcher = fs.watch(path, listener);
31
+ watcher.on("error", onError);
32
+ return watcher;
33
+ } catch (error) {
34
+ onError(error);
35
+ return null;
36
+ }
37
+ }