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,431 @@
1
+ import { execFileSync, spawnSync } from "node:child_process";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { allAgents, discoverAgents } from "../../agents/discover-agents.ts";
5
+ import { loadConfig } from "../../config/config.ts";
6
+ import { DEFAULT_PATHS } from "../../config/defaults.ts";
7
+ import { type DriftReport, detectDrift, formatDriftReport } from "../../config/drift-detector.ts";
8
+ import { getPiSpawnCommand } from "../../runtime/pi-spawn.ts";
9
+ import { getRuntimeWarmupStatus } from "../../runtime/runtime-warmup.ts";
10
+ import { formatZombieReport, scanZombieSubagents } from "../../runtime/zombie-scanner.ts";
11
+ import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
12
+ import { TeamToolParams } from "../../schema/team-tool-schema.ts";
13
+ import { atomicWriteFile } from "../../state/atomic-write.ts";
14
+ import { allTeams, discoverTeams } from "../../teams/discover-teams.ts";
15
+ import { projectCrewRoot, userCrewRoot } from "../../utils/paths.ts";
16
+ import { allWorkflows, discoverWorkflows } from "../../workflows/discover-workflows.ts";
17
+ import type { PiTeamsToolResult } from "../tool-result.ts";
18
+ import { validateResources } from "../validate-resources.ts";
19
+ import { configRecord, result, type TeamContext } from "./context.ts";
20
+
21
+ interface DoctorCheck {
22
+ label: string;
23
+ ok: boolean;
24
+ detail: string;
25
+ }
26
+
27
+ function firstOutputLine(stdout: string | null | undefined, stderr: string | null | undefined): string {
28
+ const output = `${stdout ?? ""}\n${stderr ?? ""}`.trim();
29
+ return (
30
+ output
31
+ .split(/\r?\n/)
32
+ .find((line) => line.trim().length > 0)
33
+ ?.trim() ?? "available"
34
+ );
35
+ }
36
+
37
+ // Round 29 optimization: memoize spawnSync probe results at module level.
38
+ // The probes (git --version, pi --version) are stable for the process
39
+ // lifetime, and spawnSync on a node script can cost 1-2s. Without the
40
+ // cache, each buildTeamDoctorReport() call would pay that cost, and a
41
+ // file with 12 tests would take 20s+ even with empty cwd. The cache is
42
+ // safe: a doctor check is informational, and a stale ok=true would
43
+ // self-correct on the next process restart.
44
+ const commandExistsCache = new Map<string, { ok: boolean; detail: string }>();
45
+ function commandExists(command: string, args: string[]): { ok: boolean; detail: string } {
46
+ const cacheKey = `${command} ${args.join(" ")}`;
47
+ const cached = commandExistsCache.get(cacheKey);
48
+ if (cached) return cached;
49
+ let result: { ok: boolean; detail: string };
50
+ try {
51
+ const output = spawnSync(command, args, {
52
+ encoding: "utf-8",
53
+ stdio: ["ignore", "pipe", "pipe"],
54
+ });
55
+ if (output.error) {
56
+ result = { ok: false, detail: output.error.message };
57
+ } else if (output.status !== 0) {
58
+ result = {
59
+ ok: false,
60
+ detail: firstOutputLine(output.stdout, output.stderr) || `status ${output.status}`,
61
+ };
62
+ } else {
63
+ result = {
64
+ ok: true,
65
+ detail: firstOutputLine(output.stdout, output.stderr),
66
+ };
67
+ }
68
+ } catch (error) {
69
+ result = {
70
+ ok: false,
71
+ detail: error instanceof Error ? error.message : String(error),
72
+ };
73
+ }
74
+ commandExistsCache.set(cacheKey, result);
75
+ return result;
76
+ }
77
+
78
+ let piCommandExistsCache: { ok: boolean; detail: string } | undefined;
79
+ function piCommandExists(): { ok: boolean; detail: string } {
80
+ if (piCommandExistsCache) return piCommandExistsCache;
81
+ const spec = getPiSpawnCommand(["--version"]);
82
+ const output = commandExists(spec.command, spec.args);
83
+ if (!output.ok) {
84
+ piCommandExistsCache = output;
85
+ return piCommandExistsCache;
86
+ }
87
+ const executable = spec.command === "pi" ? "pi" : `${spec.command} ${spec.args[0] ?? ""}`.trim();
88
+ piCommandExistsCache = {
89
+ ok: true,
90
+ detail: `${output.detail} (${executable})`,
91
+ };
92
+ return piCommandExistsCache;
93
+ }
94
+
95
+ function checkWritableDir(dir: string): { ok: boolean; detail: string } {
96
+ try {
97
+ if (!fs.existsSync(dir)) return { ok: false, detail: `${dir}: missing` };
98
+ if (!fs.statSync(dir).isDirectory()) return { ok: false, detail: `${dir}: not a directory` };
99
+ // fs.accessSync(W_OK) is unreliable on Windows; verify by writing a temp file.
100
+ const probePath = `${dir}/.pi-crew-write-test`;
101
+ try {
102
+ atomicWriteFile(probePath, "ok");
103
+ fs.rmSync(probePath, { force: true });
104
+ } catch {
105
+ return {
106
+ ok: false,
107
+ detail: `${dir}: not writable (write test failed)`,
108
+ };
109
+ }
110
+ return { ok: true, detail: dir };
111
+ } catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ return { ok: false, detail: `${dir}: ${message}` };
114
+ }
115
+ }
116
+
117
+ function auditJsonSchema(schema: unknown): string[] {
118
+ const issues: string[] = [];
119
+ const walk = (node: unknown): void => {
120
+ if (!node || typeof node !== "object" || Array.isArray(node)) return;
121
+ const record = node as Record<string, unknown>;
122
+ if (Array.isArray(record.type)) issues.push("schema node uses array-valued type");
123
+ if (record.description && !record.type && !record.anyOf && !record.oneOf && !record.allOf && !record.properties)
124
+ issues.push(`description-only schema node: ${record.description}`);
125
+ if (record.type === "array" && !record.items) issues.push("array schema missing items");
126
+ if (record.type && (record.anyOf || record.oneOf)) issues.push("schema node combines type with union keyword");
127
+ for (const value of Object.values(record)) {
128
+ if (Array.isArray(value)) for (const item of value) walk(item);
129
+ else walk(value);
130
+ }
131
+ };
132
+ walk(schema);
133
+ return issues;
134
+ }
135
+
136
+ function makeLine(check: DoctorCheck): string {
137
+ return `- ${check.ok ? "OK" : "FAIL"} ${check.label}: ${check.detail}`;
138
+ }
139
+
140
+ function section(title: string, checks: () => DoctorCheck[]): string[] {
141
+ try {
142
+ return [title, ...checks().map(makeLine)];
143
+ } catch (error) {
144
+ const detail = error instanceof Error ? error.message : String(error);
145
+ return [title, `- FAIL ${title}: ${detail}`];
146
+ }
147
+ }
148
+
149
+ export interface TeamDoctorReportInput {
150
+ cwd: string;
151
+ configPath: string;
152
+ configErrors: string[];
153
+ configWarnings: string[];
154
+ model?: { provider: string; id: string };
155
+ validationErrors: number;
156
+ validationWarnings: number;
157
+ smokeChildPi?: { ok: boolean; detail: string };
158
+ }
159
+
160
+ export interface TeamDoctorReport {
161
+ text: string;
162
+ hasErrors: boolean;
163
+ drift?: DriftReport;
164
+ }
165
+
166
+ export function buildTeamDoctorReport(input: TeamDoctorReportInput): TeamDoctorReport {
167
+ // Discover once — used in both Drift and Discovery sections. Walking the
168
+ // filesystem 3x (agents/teams/workflows) is the dominant cost of this
169
+ // function; calling it twice doubles the cost. Round 29 optimization.
170
+ const discoveredAgentsAll = allAgents(discoverAgents(input.cwd));
171
+ const discoveredTeamsAll = allTeams(discoverTeams(input.cwd));
172
+ const discoveredWorkflowsAll = allWorkflows(discoverWorkflows(input.cwd));
173
+ // Compute drift once — reused in both Drift section and return value
174
+ const driftResult = detectDrift(
175
+ {
176
+ agents: discoveredAgentsAll.map((a) => a.name),
177
+ teams: discoveredTeamsAll.map((t) => t.name),
178
+ workflows: discoveredWorkflowsAll.map((w) => w.name),
179
+ },
180
+ loadConfig(input.cwd).config,
181
+ );
182
+ const sections = [
183
+ section("Runtime", () => {
184
+ const git = commandExists("git", ["--version"]);
185
+ const pi = piCommandExists();
186
+ return [
187
+ { label: "cwd", ok: true, detail: input.cwd },
188
+ {
189
+ label: "platform",
190
+ ok: true,
191
+ detail: `${process.platform}/${process.arch} node=${process.version}`,
192
+ },
193
+ { label: "pi command", ok: pi.ok, detail: pi.detail },
194
+ { label: "git command", ok: git.ok, detail: git.detail },
195
+ {
196
+ label: "config",
197
+ ok: input.configErrors.length === 0,
198
+ detail: `${input.configPath} (${input.configErrors.length} errors)`,
199
+ },
200
+ {
201
+ label: "model",
202
+ ok: true,
203
+ detail: input.model ? `${input.model.provider}/${input.model.id}` : "not available in this context",
204
+ },
205
+ {
206
+ label: "config warnings",
207
+ ok: true,
208
+ detail: `${input.configWarnings.length} warnings`,
209
+ },
210
+ ];
211
+ }),
212
+ section("Filesystem", () => {
213
+ const userWritable = checkWritableDir(userCrewRoot());
214
+ const projectWritable = checkWritableDir(projectCrewRoot(input.cwd));
215
+ return [
216
+ {
217
+ label: "user state",
218
+ ok: userWritable.ok || userWritable.detail.endsWith(": missing"),
219
+ detail: userWritable.detail,
220
+ },
221
+ {
222
+ label: "project state",
223
+ ok: projectWritable.ok || projectWritable.detail.endsWith(": missing"),
224
+ detail: projectWritable.detail,
225
+ },
226
+ {
227
+ label: "project state root",
228
+ ok: true,
229
+ detail: path.join(projectCrewRoot(input.cwd), DEFAULT_PATHS.state.runsSubdir),
230
+ },
231
+ {
232
+ label: "artifacts root",
233
+ ok: true,
234
+ detail: path.join(projectCrewRoot(input.cwd), DEFAULT_PATHS.state.artifactsSubdir),
235
+ },
236
+ ];
237
+ }),
238
+ section("Discovery", () => {
239
+ const agentModelHints = discoveredAgentsAll.filter((agent) => agent.model || agent.fallbackModels?.length).length;
240
+ return [
241
+ {
242
+ label: "agents",
243
+ ok: true,
244
+ detail: `${discoveredAgentsAll.length} discovered`,
245
+ },
246
+ {
247
+ label: "teams",
248
+ ok: true,
249
+ detail: `${discoveredTeamsAll.length} discovered`,
250
+ },
251
+ {
252
+ label: "workflows",
253
+ ok: true,
254
+ detail: `${discoveredWorkflowsAll.length} discovered`,
255
+ },
256
+ {
257
+ label: "resource model hints",
258
+ ok: true,
259
+ detail: `${agentModelHints} agents declare model/fallback preferences`,
260
+ },
261
+ ];
262
+ }),
263
+ section("Resource validation", () => [
264
+ {
265
+ label: "resource validation",
266
+ ok: input.validationErrors === 0,
267
+ detail: `${input.validationErrors} errors, ${input.validationWarnings} warnings`,
268
+ },
269
+ ]),
270
+ section("Drift", () => {
271
+ const driftErrors = driftResult.items.filter((item) => item.severity === "error").length;
272
+ const driftWarnings = driftResult.items.filter((item) => item.severity === "warning").length;
273
+ return [
274
+ {
275
+ label: "config drift",
276
+ ok: !driftResult.hasDrift || driftErrors === 0,
277
+ detail: driftResult.hasDrift ? `${driftErrors} errors, ${driftWarnings} warnings` : "no drift detected",
278
+ },
279
+ ];
280
+ }),
281
+ section("Schema", () => {
282
+ const schemaIssues = auditJsonSchema(TeamToolParams);
283
+ return [
284
+ {
285
+ label: "strict-provider schema",
286
+ ok: schemaIssues.length === 0,
287
+ detail: schemaIssues.length ? schemaIssues.slice(0, 3).join("; ") : "team tool schema compatible",
288
+ },
289
+ ];
290
+ }),
291
+ section("Async/result delivery", () => [
292
+ {
293
+ label: "result watcher",
294
+ ok: true,
295
+ detail: "fs.watch with polling fallback for EMFILE/ENOSPC/EPERM",
296
+ },
297
+ {
298
+ label: "async notifier",
299
+ ok: true,
300
+ detail: "session-stale guarded completion notifications enabled",
301
+ },
302
+ ]),
303
+ section("Worktrees", () => [
304
+ { label: "leader repository", ok: true, detail: input.cwd },
305
+ {
306
+ label: "cleanup policy",
307
+ ok: true,
308
+ detail: "dirty worktrees preserved unless force is set",
309
+ },
310
+ ]),
311
+ section("Runtime warmup (cold-start fix v0.8.6)", () => {
312
+ // Surface whether the general cold-start-race fix is active + how long
313
+ // the graph warmup took, so a session can confirm the fix loaded
314
+ // (post-restart) and isn't pathologically slow. An UNWARMED graph is
315
+ // the documented cause of `Cannot read properties of undefined
316
+ // (reading '<binding>')` under concurrent subagent spawn.
317
+ //
318
+ // "Not started" is NOT a doctor error: it is the normal state in unit
319
+ // tests and in any caller that invokes buildTeamDoctorReport directly
320
+ // without going through registerPiTeams. Only a STARTED-but-FAILED
321
+ // warmup is an error (something genuinely went wrong during pre-warm).
322
+ const status = getRuntimeWarmupStatus();
323
+ const checks: DoctorCheck[] = [
324
+ {
325
+ label: "warmup started",
326
+ ok: true, // informational — "not started" is not a failure
327
+ detail: status.started
328
+ ? "module graph pre-warmed at registration"
329
+ : "not started in this process (normal for direct unit-test calls; in a live Pi session, started at extension load)",
330
+ },
331
+ ];
332
+ if (status.started) {
333
+ checks.push({
334
+ label: "warmup completed",
335
+ ok: status.completed,
336
+ detail: status.completed
337
+ ? status.durationMs !== undefined
338
+ ? `graph warm in ${status.durationMs}ms`
339
+ : "completed"
340
+ : "in progress",
341
+ });
342
+ if (status.error) {
343
+ checks.push({
344
+ label: "warmup error",
345
+ ok: false,
346
+ detail: status.error,
347
+ });
348
+ }
349
+ }
350
+ return checks;
351
+ }),
352
+ ];
353
+ if (input.smokeChildPi) {
354
+ sections.push([`Child check`, `- ${input.smokeChildPi.ok ? "OK" : "FAIL"} child Pi smoke: ${input.smokeChildPi.detail}`]);
355
+ }
356
+ const lines = ["pi-crew doctor report"];
357
+ for (const block of sections) {
358
+ if (block.length > 0) {
359
+ lines.push(...block);
360
+ lines.push("");
361
+ }
362
+ }
363
+ if (lines.at(-1) === "") lines.pop();
364
+ const text = lines.join("\n");
365
+ return {
366
+ text,
367
+ hasErrors: sections.some((sectionLines) => sectionLines.some((line) => line.includes("FAIL"))),
368
+ drift: driftResult.hasDrift ? driftResult : undefined,
369
+ };
370
+ }
371
+
372
+ export function handleDoctor(ctx: TeamContext, params: TeamToolParamsValue = {}): PiTeamsToolResult {
373
+ // Sub-focus: zombie sub-agent scan. READ-ONLY — never kills. Returns a table of
374
+ // orphaned pi-crew sub-agents identified by the authoritative PI_CREW_KIND=subagent
375
+ // marker. The user's main session never carries that marker, so it can never appear.
376
+ if (params.focus === "zombies") {
377
+ const scan = scanZombieSubagents();
378
+ const text = formatZombieReport(scan);
379
+ return result(
380
+ text,
381
+ {
382
+ action: "doctor",
383
+ status: "ok",
384
+ data: {
385
+ zombies: scan.zombies.length,
386
+ live: scan.live.length,
387
+ errors: scan.errors.length,
388
+ },
389
+ },
390
+ false,
391
+ );
392
+ }
393
+
394
+ const loadedConfig = loadConfig(ctx.cwd);
395
+ let smokeChildPi: { ok: boolean; detail: string } | undefined;
396
+ if (configRecord(params.config).smokeChildPi === true) {
397
+ try {
398
+ const spec = getPiSpawnCommand(["--mode", "json", "-p", "Reply with exactly PI-TEAMS-SMOKE-OK"]);
399
+ const output = execFileSync(spec.command, spec.args, {
400
+ cwd: ctx.cwd,
401
+ encoding: "utf-8",
402
+ stdio: ["ignore", "pipe", "pipe"],
403
+ timeout: 15_000,
404
+ }).trim();
405
+ smokeChildPi = {
406
+ ok: output.includes("PI-TEAMS-SMOKE-OK"),
407
+ detail: output.split("\n").slice(-1)[0] ?? "completed",
408
+ };
409
+ } catch (error) {
410
+ const message = error instanceof Error ? error.message : String(error);
411
+ smokeChildPi = { ok: false, detail: message };
412
+ }
413
+ }
414
+ const validation = validateResources(ctx.cwd);
415
+ const { text, hasErrors, drift } = buildTeamDoctorReport({
416
+ cwd: ctx.cwd,
417
+ configPath: loadedConfig.path,
418
+ configErrors: loadedConfig.error ? [loadedConfig.error] : [],
419
+ configWarnings: loadedConfig.warnings ?? [],
420
+ model: ctx.model,
421
+ validationErrors: validation.issues.filter((issue) => issue.level === "error").length,
422
+ validationWarnings: validation.issues.filter((issue) => issue.level === "warning").length,
423
+ smokeChildPi,
424
+ });
425
+ // Append detailed drift section if any drift was detected
426
+ let finalText = text;
427
+ if (drift?.hasDrift) {
428
+ finalText = `${text}\n\nDrift details:\n${formatDriftReport(drift)}`;
429
+ }
430
+ return result(finalText, { action: "doctor", status: hasErrors ? "error" : "ok" }, hasErrors);
431
+ }
@@ -0,0 +1,282 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { loadRunManifestById } from "../../state/state-store.ts";
4
+ import type { TeamRunManifest, TeamTaskState } from "../../state/types.ts";
5
+ import { RUN_NOT_FOUND_HINT } from "./run-not-found.ts";
6
+
7
+ /**
8
+ * Local wrapper matching the planned `result` API used by handleExplain.
9
+ */
10
+ function result(text: string, details: Record<string, unknown>, isError: boolean): { isError: boolean; text: string } {
11
+ return { isError, text };
12
+ }
13
+
14
+ export interface TaskExplainContext {
15
+ taskId: string;
16
+ role: string;
17
+ status: string;
18
+ phase?: string;
19
+ why: string;
20
+ what: string;
21
+ filesTouched: string[];
22
+ connectedTasks: Array<{ taskId: string; status: string; relation: string }>;
23
+ layer: string;
24
+ complexity: "simple" | "moderate" | "complex";
25
+ usage?: { inputTokens?: number; outputTokens?: number };
26
+ duration?: number;
27
+ }
28
+
29
+ /**
30
+ * Build explain context for a specific task in a run.
31
+ */
32
+ export function buildTaskExplainContext(manifest: TeamRunManifest, tasks: TeamTaskState[], taskId: string): TaskExplainContext {
33
+ const task = tasks.find((t) => t.id === taskId);
34
+ if (!task) {
35
+ throw new Error(`Task ${taskId} not found in run ${manifest.runId}`);
36
+ }
37
+
38
+ const dependsOn = task.dependsOn ?? [];
39
+ const dependents = tasks.filter((t) => (t.dependsOn ?? []).includes(taskId));
40
+
41
+ // Layer from phase
42
+ const layerMap: Record<string, string> = {
43
+ explore: "exploration",
44
+ plan: "planning",
45
+ assess: "assessment",
46
+ execute: "execution",
47
+ verify: "verification",
48
+ analyze: "analysis",
49
+ write: "documentation",
50
+ "": "unknown",
51
+ };
52
+ const layer = layerMap[task.adaptive?.phase ?? ""] ?? "unknown";
53
+
54
+ // Why it exists
55
+ let why = `Part of ${manifest.team ?? "unknown"} team workflow.`;
56
+ if (dependsOn.length > 0) {
57
+ const depTasks = dependsOn
58
+ .map((d) => {
59
+ const dep = tasks.find((t) => t.id === d);
60
+ return dep ? `\`${d}\` (${dep.status})` : `\`${d}\``;
61
+ })
62
+ .join(", ");
63
+ why += ` Depends on ${depTasks}.`;
64
+ }
65
+ if (dependents.length > 0) {
66
+ why += ` ${dependents.length} task(s) depend on this.`;
67
+ }
68
+
69
+ // What it did
70
+ let what = `Ran agent: ${task.role}`;
71
+ if (task.model) {
72
+ what += ` (${task.model})`;
73
+ }
74
+ if (task.usage) {
75
+ const inputTokens = task.usage.input ?? 0;
76
+ const outputTokens = task.usage.output ?? 0;
77
+ what += `. Usage: input=${inputTokens}, output=${outputTokens}`;
78
+ }
79
+ if (task.status === "failed") {
80
+ what += `. Status: FAILED`;
81
+ if (task.error) {
82
+ what += ` — ${task.error}`;
83
+ }
84
+ }
85
+
86
+ // Files from artifacts
87
+ const artifactsPath = manifest.artifactsRoot;
88
+ const filesTouched: string[] = [];
89
+ if (fs.existsSync(artifactsPath)) {
90
+ try {
91
+ const entries = fs.readdirSync(artifactsPath);
92
+ for (const entry of entries) {
93
+ const fullPath = path.join(artifactsPath, entry);
94
+ try {
95
+ if (fs.statSync(fullPath).isFile()) {
96
+ filesTouched.push(entry);
97
+ }
98
+ } catch {
99
+ /* ignore */
100
+ }
101
+ }
102
+ } catch {
103
+ /* ignore */
104
+ }
105
+ }
106
+
107
+ // Duration
108
+ let duration: number | undefined;
109
+ if (task.startedAt && task.finishedAt) {
110
+ duration = (new Date(task.finishedAt).getTime() - new Date(task.startedAt).getTime()) / 1000;
111
+ }
112
+
113
+ // Complexity
114
+ const complexity = tasks.length <= 3 ? "simple" : tasks.length <= 8 ? "moderate" : "complex";
115
+
116
+ return {
117
+ taskId,
118
+ role: task.role,
119
+ status: task.status,
120
+ phase: task.adaptive?.phase,
121
+ why,
122
+ what,
123
+ filesTouched,
124
+ connectedTasks: [
125
+ ...dependsOn.map((d) => {
126
+ const dep = tasks.find((t) => t.id === d);
127
+ return {
128
+ taskId: d,
129
+ status: dep?.status ?? "unknown",
130
+ relation: "depends on",
131
+ };
132
+ }),
133
+ ...dependents.map((d) => ({
134
+ taskId: d.id,
135
+ status: d.status,
136
+ relation: "depended on by",
137
+ })),
138
+ ],
139
+ layer,
140
+ complexity,
141
+ usage: task.usage ? { inputTokens: task.usage.input, outputTokens: task.usage.output } : undefined,
142
+ duration,
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Format task explain context as markdown.
148
+ */
149
+ export function formatTaskExplain(ctx: TaskExplainContext): string {
150
+ const lines: string[] = [];
151
+
152
+ lines.push(`# Task: ${ctx.taskId} (${ctx.role})`);
153
+ lines.push("");
154
+ lines.push(`| | |`);
155
+ lines.push(`|---|---|`);
156
+ lines.push(`| **Status** | ${ctx.status} |`);
157
+ if (ctx.phase) lines.push(`| **Phase** | ${ctx.phase} |`);
158
+ lines.push(`| **Layer** | ${ctx.layer} |`);
159
+ lines.push(`| **Complexity** | ${ctx.complexity} |`);
160
+
161
+ if (ctx.duration) {
162
+ const minutes = Math.round(ctx.duration / 60);
163
+ lines.push(`| **Duration** | ${minutes} min |`);
164
+ }
165
+
166
+ if (ctx.usage) {
167
+ const input = ctx.usage.inputTokens ?? 0;
168
+ const output = ctx.usage.outputTokens ?? 0;
169
+ lines.push(`| **Usage** | ↑${input} ↓${output} tokens |`);
170
+ }
171
+
172
+ lines.push("");
173
+ lines.push(`## Why it exists`);
174
+ lines.push("");
175
+ lines.push(ctx.why);
176
+ lines.push("");
177
+ lines.push(`## What it did`);
178
+ lines.push("");
179
+ lines.push(ctx.what);
180
+ lines.push("");
181
+
182
+ if (ctx.filesTouched.length > 0) {
183
+ lines.push("## Files produced");
184
+ lines.push("");
185
+ for (const file of ctx.filesTouched) {
186
+ lines.push(`- \`${file}\``);
187
+ }
188
+ lines.push("");
189
+ }
190
+
191
+ if (ctx.connectedTasks.length > 0) {
192
+ lines.push("## Connected tasks");
193
+ lines.push("");
194
+ for (const conn of ctx.connectedTasks) {
195
+ lines.push(`- ${conn.relation} \`${conn.taskId}\` (${conn.status})`);
196
+ }
197
+ lines.push("");
198
+ }
199
+
200
+ return lines.join("\n");
201
+ }
202
+
203
+ /**
204
+ * Handle team action='explain'.
205
+ */
206
+ export function handleExplain(
207
+ params: {
208
+ runId?: string;
209
+ taskId?: string;
210
+ cwd?: string;
211
+ },
212
+ cwd: string,
213
+ ): { isError: boolean; text: string } {
214
+ if (!params.runId) {
215
+ return result("explain requires runId", { action: "explain", status: "error" }, true);
216
+ }
217
+
218
+ const loaded = loadRunManifestById(cwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
219
+ if (!loaded) {
220
+ return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "explain", status: "error" }, true);
221
+ }
222
+
223
+ const { manifest, tasks } = loaded;
224
+
225
+ if (params.taskId) {
226
+ try {
227
+ const ctx = buildTaskExplainContext(manifest, tasks, params.taskId);
228
+ const output = formatTaskExplain(ctx);
229
+ return result(output, { action: "explain", runId: params.runId }, false);
230
+ } catch (err) {
231
+ return result(
232
+ `Task '${params.taskId}' not found: ${err instanceof Error ? err.message : String(err)}`,
233
+ { action: "explain", status: "error" },
234
+ true,
235
+ );
236
+ }
237
+ }
238
+
239
+ // Explain entire run
240
+ const lines: string[] = [];
241
+ lines.push(`# Run: ${params.runId}`);
242
+ lines.push("");
243
+ lines.push(`| | |`);
244
+ lines.push(`|---|---|`);
245
+
246
+ const start = new Date(manifest.createdAt).getTime();
247
+ const end = manifest.updatedAt ? new Date(manifest.updatedAt).getTime() : Date.now();
248
+ const duration = Math.round((end - start) / 1000 / 60);
249
+
250
+ lines.push(`| **Team** | ${manifest.team} |`);
251
+ lines.push(`| **Workflow** | ${manifest.workflow ?? "default"} |`);
252
+ lines.push(`| **Status** | ${manifest.status} |`);
253
+ lines.push(`| **Duration** | ${duration} min |`);
254
+ lines.push(`| **Tasks** | ${tasks.length} |`);
255
+ lines.push("");
256
+
257
+ lines.push("## Tasks");
258
+ lines.push("");
259
+ lines.push("| Task | Role | Status | Layer |");
260
+ lines.push("|------|------|--------|-------|");
261
+
262
+ for (const task of tasks) {
263
+ const layerMap: Record<string, string> = {
264
+ explore: "exploration",
265
+ plan: "planning",
266
+ assess: "assessment",
267
+ execute: "execution",
268
+ verify: "verification",
269
+ analyze: "analysis",
270
+ write: "documentation",
271
+ };
272
+ const layer = layerMap[task.adaptive?.phase ?? ""] ?? "unknown";
273
+ const statusIcon = task.status === "completed" ? "✅" : task.status === "failed" ? "❌" : "⏳";
274
+ lines.push(`| \`${task.id}\` | ${task.role} | ${statusIcon} ${task.status} | ${layer} |`);
275
+ }
276
+ lines.push("");
277
+
278
+ lines.push("---");
279
+ lines.push(`*Use \`team action='explain' runId=${params.runId} taskId=<taskId>\` for task detail.*`);
280
+
281
+ return result(lines.join("\n"), { action: "explain", runId: params.runId }, false);
282
+ }