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,131 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { DEFAULT_PATHS } from "../config/defaults.ts";
5
+ import { createCancellationToken } from "../runtime/cancellation-token.ts";
6
+ import { activeRunEntries } from "../state/active-run-registry.ts";
7
+ import type { TeamRunManifest } from "../state/types.ts";
8
+ import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
9
+ import { isSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
10
+ import { sharedScanCache } from "../utils/scan-cache.ts";
11
+
12
+ function readManifest(filePath: string): TeamRunManifest | undefined {
13
+ const cached = sharedScanCache.readAndCache("manifests", filePath, filePath);
14
+ if (cached) return cached.raw as TeamRunManifest;
15
+ try {
16
+ return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TeamRunManifest;
17
+ } catch {
18
+ return undefined;
19
+ }
20
+ }
21
+
22
+ function collectRuns(root: string, maxEntries?: number, signal?: AbortSignal): TeamRunManifest[] {
23
+ const runsRoot = path.join(root, DEFAULT_PATHS.state.runsSubdir);
24
+ if (!fs.existsSync(runsRoot)) return [];
25
+ if (signal?.aborted) return [];
26
+ // 2.19 — skip temp directories to avoid phantom runs from test/isolated workspaces.
27
+ // Runs in /tmp/ are typically from tests or isolated worktrees and should not
28
+ // appear in production dashboards unless the process is actually alive.
29
+ const tempDirs = [os.tmpdir(), "/var/tmp", "/tmp"];
30
+ const isTempRoot = tempDirs.some((t) => root.startsWith(t + path.sep));
31
+ const token = createCancellationToken({ signal });
32
+ const entries = fs
33
+ .readdirSync(runsRoot, { withFileTypes: true })
34
+ .filter((entry) => entry.isDirectory() && isSafePathId(entry.name))
35
+ .map((entry) => entry.name)
36
+ .sort((a, b) => (b ?? "").localeCompare(a ?? ""));
37
+ const selected = maxEntries !== undefined ? entries.slice(0, Math.max(0, maxEntries)) : entries;
38
+ const results: TeamRunManifest[] = [];
39
+ for (let i = 0; i < selected.length; i++) {
40
+ if (i % 10 === 0) token.heartbeat(`collectRuns:${i}/${selected.length}`);
41
+ try {
42
+ const manifest = readManifest(path.join(resolveRealContainedPath(runsRoot, selected[i]), DEFAULT_PATHS.state.manifestFile));
43
+ if (!manifest) continue;
44
+ // Filter out ghost runs: active status but CWD no longer exists.
45
+ // These are deadletter/replay/temp runs whose temp dirs were cleaned up.
46
+ if (
47
+ (manifest.status === "queued" || manifest.status === "running" || manifest.status === "planning") &&
48
+ manifest.cwd &&
49
+ !fs.existsSync(manifest.cwd)
50
+ )
51
+ continue;
52
+ // 2.19 — for runs in temp directories, verify the background process is alive.
53
+ // Without this, runs that completed/crashed but left stale manifests will still show.
54
+ if (isTempRoot && (manifest.status === "running" || manifest.status === "queued" || manifest.status === "planning")) {
55
+ const asyncPidPath = path.join(path.dirname(manifest.stateRoot), "async.pid");
56
+ try {
57
+ const pidData = JSON.parse(fs.readFileSync(asyncPidPath, "utf-8"));
58
+ const pid = pidData?.pid;
59
+ if (pid && typeof pid === "number") {
60
+ try {
61
+ process.kill(pid, 0);
62
+ } catch {
63
+ continue;
64
+ } // PID dead, skip this run
65
+ }
66
+ } catch {
67
+ /* no async.pid or parse error — keep the run */
68
+ }
69
+ }
70
+ results.push(manifest);
71
+ } catch {
72
+ /* skip unreadable manifests */
73
+ }
74
+ }
75
+ return results;
76
+ }
77
+
78
+ function mergeRuns(runSets: TeamRunManifest[][], max?: number): TeamRunManifest[] {
79
+ const byId = new Map<string, TeamRunManifest>();
80
+ for (const runs of runSets) for (const run of runs) byId.set(run.runId, run);
81
+ const sorted = [...byId.values()].sort((a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? ""));
82
+ return max !== undefined ? sorted.slice(0, Math.max(0, max)) : sorted;
83
+ }
84
+
85
+ function scopedRunRoots(cwd: string): string[] {
86
+ const roots = new Set<string>();
87
+ roots.add(userCrewRoot());
88
+ const projectRoot = findRepoRoot(cwd);
89
+ if (projectRoot) roots.add(projectCrewRoot(cwd));
90
+ return [...roots];
91
+ }
92
+
93
+ function collectActiveRuns(cwd?: string): TeamRunManifest[] {
94
+ return activeRunEntries()
95
+ .map((entry) => readManifest(entry.manifestPath))
96
+ .filter((manifest): manifest is TeamRunManifest => manifest !== undefined);
97
+ }
98
+
99
+ export function listRuns(cwd: string, signal?: AbortSignal): TeamRunManifest[] {
100
+ const roots = scopedRunRoots(cwd);
101
+ return mergeRuns([...roots.map((root) => collectRuns(root, undefined, signal)), collectActiveRuns()]);
102
+ }
103
+
104
+ export function listRecentRuns(cwd: string, max = 20, signal?: AbortSignal): TeamRunManifest[] {
105
+ const roots = scopedRunRoots(cwd);
106
+ return mergeRuns([...roots.map((root) => collectRuns(root, max, signal)), collectActiveRuns()], max);
107
+ }
108
+
109
+ /**
110
+ * List runs filtered to a specific scope.
111
+ * - "project": only runs in the project crew root
112
+ * - "user": only runs in the user crew root
113
+ * - "all" (default): merge both scopes (current behavior)
114
+ */
115
+ export function listRunsByScope(
116
+ cwd: string,
117
+ scope: "project" | "user" | "all" = "all",
118
+ max?: number,
119
+ signal?: AbortSignal,
120
+ ): TeamRunManifest[] {
121
+ const projectRoot = findRepoRoot(cwd);
122
+ switch (scope) {
123
+ case "project":
124
+ return projectRoot ? collectRuns(projectCrewRoot(cwd), max, signal) : [];
125
+ case "user":
126
+ return collectRuns(userCrewRoot(), max, signal);
127
+ case "all":
128
+ default:
129
+ return max !== undefined ? listRecentRuns(cwd, max, signal) : listRuns(cwd, signal);
130
+ }
131
+ }
@@ -0,0 +1,190 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { DEFAULT_PATHS } from "../config/defaults.ts";
4
+ import { createCancellationToken } from "../runtime/cancellation-token.ts";
5
+ import type { TeamRunManifest } from "../state/types.ts";
6
+ import { logInternalError } from "../utils/internal-error.ts";
7
+ import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
8
+ import { redactSecrets } from "../utils/redaction.ts";
9
+ import { isSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
10
+ import { cleanupRunWorktrees } from "../worktree/cleanup.ts";
11
+ import { listRuns } from "./run-index.ts";
12
+
13
+ export interface PruneRunsResult {
14
+ kept: string[];
15
+ removed: string[];
16
+ auditPath?: string;
17
+ }
18
+
19
+ export interface PruneRunsOptions {
20
+ intent?: string;
21
+ signal?: AbortSignal;
22
+ }
23
+
24
+ function isFinished(run: TeamRunManifest): boolean {
25
+ // P3: "blocked" is NOT a terminal status — the run is waiting on something
26
+ // (plan approval, mailbox reply, scheduler stall) and can transition to
27
+ // running/cancelled/failed (per TEAM_RUN_STATUS_TRANSITIONS). Pruning a
28
+ // blocked run destroys recoverable state (e.g. a plan the user needs to
29
+ // approve, or a mailbox wait the user needs to answer). Truly stuck
30
+ // blocked runs are handled by the stale-reconciler/crash-recovery, not
31
+ // by prune.
32
+ return run.status === "completed" || run.status === "failed" || run.status === "cancelled";
33
+ }
34
+
35
+ function isSafeToPrune(cwd: string, run: TeamRunManifest): boolean {
36
+ try {
37
+ const crewRoot = run.stateRoot.startsWith(userCrewRoot() + path.sep) ? userCrewRoot() : projectCrewRoot(cwd);
38
+ resolveRealContainedPath(crewRoot, run.stateRoot);
39
+ resolveRealContainedPath(crewRoot, run.artifactsRoot);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
46
+ function appendPruneAudit(cwd: string, payload: Record<string, unknown>): string | undefined {
47
+ try {
48
+ const filePath = path.join(projectCrewRoot(cwd), "audit", "prune.jsonl");
49
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
50
+ fs.appendFileSync(filePath, `${JSON.stringify(redactSecrets({ ...payload, auditedAt: new Date().toISOString() }))}\n`, "utf-8");
51
+ return filePath;
52
+ } catch (error) {
53
+ logInternalError("prune.audit-write", error, `cwd=${cwd}`);
54
+ return undefined;
55
+ }
56
+ }
57
+
58
+ export function pruneFinishedRuns(cwd: string, keep: number, options: PruneRunsOptions = {}): PruneRunsResult {
59
+ const token = createCancellationToken({ signal: options.signal });
60
+ const finished = listRuns(cwd, options.signal)
61
+ .filter((run) => run.cwd === cwd && isFinished(run))
62
+ .sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
63
+ const kept = finished.slice(0, keep).map((run) => run.runId);
64
+ const removed: string[] = [];
65
+ const toRemove = finished.slice(keep);
66
+ for (let i = 0; i < toRemove.length; i++) {
67
+ if (i % 5 === 0) token.heartbeat(`prune:${i}/${toRemove.length}`);
68
+ const run = toRemove[i];
69
+ if (!isSafeToPrune(cwd, run)) {
70
+ logInternalError(
71
+ "prune.path-unsafe",
72
+ new Error(`Skipping unsafe prune: stateRoot=${run.stateRoot}, artifactsRoot=${run.artifactsRoot}`),
73
+ `runId=${run.runId}`,
74
+ );
75
+ continue;
76
+ }
77
+ // P2: clean up git worktrees BEFORE deleting state. Worktrees live at
78
+ // <crewRoot>/state/worktrees/<runId>/ — a separate path from stateRoot
79
+ // (<crewRoot>/state/runs/<runId>/) and artifactsRoot, so fs.rmSync below
80
+ // does NOT touch them. Without this, every pruned worktree-using run
81
+ // leaks its worktree dir + git branch. cleanupRunWorktrees (no force)
82
+ // preserves dirty worktrees by writing a diff artifact and skipping
83
+ // the removal — in that case we skip the whole prune of this run so
84
+ // the user can recover (no silent data loss). Compare handleForget
85
+ // which calls cleanupRunWorktrees.
86
+ const worktreeCleanup = cleanupRunWorktrees(run, { signal: options.signal });
87
+ if (worktreeCleanup.preserved.length > 0) {
88
+ logInternalError(
89
+ "prune.worktree-preserved",
90
+ new Error(
91
+ `Skipping prune: ${worktreeCleanup.preserved.length} dirty worktree(s) preserved for recovery: ${worktreeCleanup.preserved.map((p) => p.path).join(", ")}`,
92
+ ),
93
+ `runId=${run.runId}`,
94
+ );
95
+ continue;
96
+ }
97
+ fs.rmSync(run.stateRoot, { recursive: true, force: true });
98
+ fs.rmSync(run.artifactsRoot, { recursive: true, force: true });
99
+ removed.push(run.runId);
100
+ }
101
+ const auditPath = appendPruneAudit(cwd, {
102
+ action: "prune",
103
+ keep,
104
+ intent: options.intent,
105
+ kept,
106
+ removed,
107
+ });
108
+ return { kept, removed, auditPath };
109
+ }
110
+
111
+ /**
112
+ * Prune finished run directories at the user level (~/.pi/agent/extensions/pi-crew/state/runs/).
113
+ *
114
+ * This handles runs created without a project root (e.g. `team action='run'` from home directory)
115
+ * that would otherwise accumulate forever.
116
+ *
117
+ * @param keep Number of most recent finished runs to retain
118
+ * @returns kept and removed run IDs
119
+ */
120
+ export function pruneUserLevelRuns(keep: number): PruneRunsResult {
121
+ const crewRoot = userCrewRoot();
122
+ const runsRoot = path.join(crewRoot, DEFAULT_PATHS.state.runsSubdir);
123
+ if (!fs.existsSync(runsRoot)) return { kept: [], removed: [] };
124
+
125
+ // Read all run directories, parse manifests, filter to finished
126
+ const MAX_DIRS = 500;
127
+ const finished: Array<{
128
+ runId: string;
129
+ updatedAt: string;
130
+ stateRoot: string;
131
+ artifactsRoot: string;
132
+ }> = [];
133
+ const ghostRemoved: string[] = [];
134
+ const dirs = fs
135
+ .readdirSync(runsRoot, { withFileTypes: true })
136
+ .filter((entry) => entry.isDirectory() && isSafePathId(entry.name))
137
+ .slice(0, MAX_DIRS)
138
+ .map((entry) => entry.name);
139
+
140
+ for (const dir of dirs) {
141
+ const manifestPath = path.join(runsRoot, dir, DEFAULT_PATHS.state.manifestFile);
142
+ let manifest: TeamRunManifest | undefined;
143
+ try {
144
+ manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as TeamRunManifest;
145
+ } catch {
146
+ continue;
147
+ }
148
+
149
+ // Ghost run cleanup: active status but CWD no longer exists.
150
+ // These are deadletter/replay/temp runs from dead Pi sessions.
151
+ const isActive = manifest.status === "queued" || manifest.status === "running" || manifest.status === "planning";
152
+ if (isActive && manifest.cwd && !fs.existsSync(manifest.cwd)) {
153
+ fs.rmSync(path.join(runsRoot, dir), {
154
+ recursive: true,
155
+ force: true,
156
+ });
157
+ ghostRemoved.push(manifest.runId);
158
+ continue;
159
+ }
160
+
161
+ if (!isFinished(manifest)) continue;
162
+
163
+ // Safety check: ensure stateRoot and artifactsRoot are contained within user crew root
164
+ try {
165
+ resolveRealContainedPath(crewRoot, manifest.stateRoot);
166
+ resolveRealContainedPath(crewRoot, manifest.artifactsRoot);
167
+ } catch {
168
+ continue;
169
+ }
170
+
171
+ finished.push({
172
+ runId: manifest.runId,
173
+ updatedAt: manifest.updatedAt,
174
+ stateRoot: manifest.stateRoot,
175
+ artifactsRoot: manifest.artifactsRoot,
176
+ });
177
+ }
178
+
179
+ // Sort newest first, keep top N, remove the rest
180
+ finished.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
181
+ const kept = finished.slice(0, keep).map((r) => r.runId);
182
+ const removed: string[] = [];
183
+ for (const run of finished.slice(keep)) {
184
+ fs.rmSync(run.stateRoot, { recursive: true, force: true });
185
+ fs.rmSync(run.artifactsRoot, { recursive: true, force: true });
186
+ removed.push(run.runId);
187
+ }
188
+
189
+ return { kept, removed: [...removed, ...ghostRemoved] };
190
+ }
@@ -0,0 +1,18 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { readCrewAgents } from "../runtime/crew-agent-records.ts";
3
+ import { isDisplayActiveRun } from "../runtime/process-status.ts";
4
+ import { listRuns } from "./run-index.ts";
5
+
6
+ export function notifyActiveRuns(ctx: ExtensionContext): void {
7
+ const active = listRuns(ctx.cwd)
8
+ .filter((run) => {
9
+ if (run.status !== "queued" && run.status !== "planning" && run.status !== "running") return false;
10
+ // Use the same display filter as the widget/powerbar — runs without
11
+ // real agent evidence (e.g. integration test fixtures) must not appear.
12
+ const agents = readCrewAgents(run);
13
+ return isDisplayActiveRun(run, agents);
14
+ })
15
+ .slice(0, 5);
16
+ if (active.length === 0) return;
17
+ ctx.ui.notify(`pi-crew active runs: ${active.map((run) => `${run.runId} [${run.status}]`).join(", ")}`, "info");
18
+ }
@@ -0,0 +1,153 @@
1
+ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { listRuns } from "./run-index.ts";
3
+ // Lazy-loaded: team-tool.ts pulls in entire runtime chain.
4
+ import type { handleTeamTool as HandleTeamToolFn } from "./team-tool.ts";
5
+
6
+ let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
7
+ async function handleTeamTool(
8
+ params: Parameters<typeof HandleTeamToolFn>[0],
9
+ ctx: Parameters<typeof HandleTeamToolFn>[1],
10
+ ): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
11
+ if (!_cachedHandleTeamTool) {
12
+ // LAZY: team-tool.ts pulls in the entire runtime chain.
13
+ const mod = await import("./team-tool.ts");
14
+ _cachedHandleTeamTool = mod.handleTeamTool;
15
+ }
16
+ return _cachedHandleTeamTool(params, ctx);
17
+ }
18
+
19
+ import { isToolError, textFromToolResult } from "./tool-result.ts";
20
+
21
+ async function notifyResult(ctx: ExtensionCommandContext, result: Awaited<ReturnType<typeof handleTeamTool>>): Promise<void> {
22
+ const text = textFromToolResult(result);
23
+ ctx.ui.notify(text.length > 1000 ? `${text.slice(0, 997)}...` : text, isToolError(result) ? "error" : "info");
24
+ }
25
+
26
+ export async function handleTeamManagerCommand(_args: string, ctx: ExtensionCommandContext): Promise<void> {
27
+ const action = await ctx.ui.select("pi-crew", [
28
+ "List teams/workflows/agents/runs",
29
+ "Run team",
30
+ "Show run status",
31
+ "Cleanup run worktrees",
32
+ "Create routed resource",
33
+ "Update routed resource",
34
+ "Doctor",
35
+ ]);
36
+ if (!action) return;
37
+
38
+ if (action.startsWith("List")) {
39
+ await notifyResult(ctx, await handleTeamTool({ action: "list" }, ctx));
40
+ return;
41
+ }
42
+
43
+ if (action === "Doctor") {
44
+ await notifyResult(ctx, await handleTeamTool({ action: "doctor" }, ctx));
45
+ return;
46
+ }
47
+
48
+ if (action === "Create routed resource" || action === "Update routed resource") {
49
+ const isUpdate = action === "Update routed resource";
50
+ const resource = await ctx.ui.select("Resource type", ["agent", "team"]);
51
+ if (resource !== "agent" && resource !== "team") return;
52
+ const name = await ctx.ui.input("Name", resource === "agent" ? "custom-agent" : "custom-team");
53
+ if (!name) return;
54
+ const description = await ctx.ui.input("Description", "When to use this resource");
55
+ if (!description) return;
56
+ const triggers = await ctx.ui.input("Triggers (comma-separated)", "");
57
+ const useWhen = await ctx.ui.input("Use when (comma-separated)", "");
58
+ const avoidWhen = await ctx.ui.input("Avoid when (comma-separated)", "");
59
+ const cost = await ctx.ui.select("Cost", ["cheap", "free", "expensive"]);
60
+ const category = await ctx.ui.input("Category", "custom");
61
+ const baseConfig = {
62
+ name,
63
+ description,
64
+ scope: "project",
65
+ triggers,
66
+ useWhen,
67
+ avoidWhen,
68
+ cost,
69
+ category,
70
+ };
71
+ if (resource === "agent") {
72
+ const systemPrompt = isUpdate ? undefined : `You are ${name}.`;
73
+ await notifyResult(
74
+ ctx,
75
+ await handleTeamTool(
76
+ {
77
+ action: isUpdate ? "update" : "create",
78
+ resource,
79
+ agent: name,
80
+ config: { ...baseConfig, systemPrompt },
81
+ },
82
+ ctx,
83
+ ),
84
+ );
85
+ return;
86
+ }
87
+ const agent = await ctx.ui.input("Role agent", "executor");
88
+ await notifyResult(
89
+ ctx,
90
+ await handleTeamTool(
91
+ {
92
+ action: isUpdate ? "update" : "create",
93
+ resource,
94
+ team: name,
95
+ config: {
96
+ ...baseConfig,
97
+ roles: [{ name: "executor", agent: agent || "executor" }],
98
+ },
99
+ },
100
+ ctx,
101
+ ),
102
+ );
103
+ return;
104
+ }
105
+
106
+ if (action === "Run team") {
107
+ const team = await ctx.ui.input("Team name", "default");
108
+ if (team === undefined) return;
109
+ const goal = await ctx.ui.input("Goal", "Describe the team objective");
110
+ if (!goal) return;
111
+ const asyncRun = await ctx.ui.confirm("Async run?", "Run in detached background mode?");
112
+ const worktree = await ctx.ui.confirm("Worktree mode?", "Use git worktrees for task workspaces? Requires a clean repo by default.");
113
+ await notifyResult(
114
+ ctx,
115
+ await handleTeamTool(
116
+ {
117
+ action: "run",
118
+ team: team || "default",
119
+ goal,
120
+ async: asyncRun,
121
+ workspaceMode: worktree ? "worktree" : "single",
122
+ },
123
+ ctx,
124
+ ),
125
+ );
126
+ return;
127
+ }
128
+
129
+ const runs = listRuns(ctx.cwd).slice(0, 20);
130
+ if (runs.length === 0) {
131
+ ctx.ui.notify("No pi-crew runs found.", "info");
132
+ return;
133
+ }
134
+ const selected = await ctx.ui.select(
135
+ "Select run",
136
+ runs.map((run) => `${run.runId} [${run.status}] ${run.team}/${run.workflow ?? "none"}`),
137
+ );
138
+ if (!selected) return;
139
+ const runId = selected.split(" ")[0];
140
+ if (!runId) return;
141
+
142
+ if (action === "Show run status") {
143
+ await notifyResult(ctx, await handleTeamTool({ action: "status", runId }, ctx));
144
+ return;
145
+ }
146
+ if (action === "Cleanup run worktrees") {
147
+ const force = await ctx.ui.confirm(
148
+ "Force cleanup?",
149
+ "Force may remove dirty worktrees. Choose false to preserve dirty worktrees and capture cleanup diffs.",
150
+ );
151
+ await notifyResult(ctx, await handleTeamTool({ action: "cleanup", runId, force }, ctx));
152
+ }
153
+ }
@@ -0,0 +1,180 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { TeamRunManifest } from "../state/types.ts";
4
+ import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
5
+ import { logInternalError } from "../utils/internal-error.ts";
6
+ import { projectCrewRoot } from "../utils/paths.ts";
7
+
8
+ export interface OnboardingOptions {
9
+ team?: string;
10
+ limit?: number;
11
+ cwd?: string;
12
+ }
13
+
14
+ interface RunSummary {
15
+ runId: string;
16
+ status: string;
17
+ goal: string;
18
+ team: string;
19
+ createdAt: string;
20
+ completedAt?: string;
21
+ taskCount: number;
22
+ }
23
+
24
+ /**
25
+ * Load run summaries from the state directory.
26
+ */
27
+ function loadRunSummaries(cwd: string, options: OnboardingOptions = {}): RunSummary[] {
28
+ const crewRoot = projectCrewRoot(cwd);
29
+ const runsRoot = path.join(crewRoot, "state", "runs");
30
+
31
+ if (!fs.existsSync(runsRoot)) return [];
32
+
33
+ const limit = options.limit ?? 5;
34
+ const teamFilter = options.team;
35
+
36
+ const entries = fs
37
+ .readdirSync(runsRoot)
38
+ .filter((e) => {
39
+ try {
40
+ return fs.statSync(path.join(runsRoot, e)).isDirectory();
41
+ } catch {
42
+ return false;
43
+ }
44
+ })
45
+ .sort()
46
+ .reverse()
47
+ .slice(0, 100);
48
+
49
+ const summaries: RunSummary[] = [];
50
+
51
+ for (const runId of entries) {
52
+ if (summaries.length >= limit) break;
53
+
54
+ const manifestPath = path.join(runsRoot, runId, "manifest.json");
55
+ if (!fs.existsSync(manifestPath)) continue;
56
+
57
+ try {
58
+ const raw = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as TeamRunManifest & { completedAt?: string };
59
+
60
+ if (teamFilter && raw.team !== teamFilter) continue;
61
+
62
+ summaries.push({
63
+ runId,
64
+ status: raw.status,
65
+ goal: raw.goal ?? "",
66
+ team: raw.team,
67
+ createdAt: raw.createdAt,
68
+ completedAt: raw.completedAt ?? raw.updatedAt,
69
+ taskCount: 0, // tasks stored separately, not in manifest
70
+ });
71
+ } catch (error) {
72
+ logInternalError("team-onboard.load-manifest", error, undefined, "debug");
73
+ }
74
+ }
75
+
76
+ return summaries;
77
+ }
78
+
79
+ /**
80
+ * Format duration in minutes. Defensive against missing/invalid timestamps
81
+ * (e.g. legacy test runs) — returns "?" instead of "NaNm".
82
+ */
83
+ export function formatDuration(createdAt: string, completedAt?: string): string {
84
+ const start = new Date(createdAt).getTime();
85
+ const end = completedAt ? new Date(completedAt).getTime() : Date.now();
86
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return "?";
87
+ const minutes = Math.round((end - start) / 1000 / 60);
88
+ if (minutes < 1) return "<1m";
89
+ if (minutes >= 60) return `${Math.round(minutes / 60)}h`;
90
+ return `${minutes}m`;
91
+ }
92
+
93
+ /**
94
+ * Build markdown onboarding guide for a team.
95
+ */
96
+ export function buildTeamOnboarding(team: string, cwd: string, options: OnboardingOptions = {}): string {
97
+ const runs = loadRunSummaries(cwd, { ...options, team });
98
+
99
+ const lines: string[] = [];
100
+
101
+ // Header
102
+ lines.push(`# Team: ${team}`);
103
+ lines.push("");
104
+ lines.push(`> Multi-agent ${team} team for pi-crew.`);
105
+ lines.push("");
106
+
107
+ // Overview stats
108
+ const completed = runs.filter((r) => r.status === "completed");
109
+ const failed = runs.filter((r) => r.status === "failed" || r.status === "cancelled");
110
+
111
+ let avgDuration = 0;
112
+ if (completed.length > 0) {
113
+ const totalMs = completed.reduce((sum, r) => {
114
+ const start = new Date(r.createdAt).getTime();
115
+ const end = r.completedAt ? new Date(r.completedAt).getTime() : Date.now();
116
+ return sum + (end - start);
117
+ }, 0);
118
+ avgDuration = totalMs / completed.length / 1000 / 60;
119
+ }
120
+
121
+ lines.push("## Overview");
122
+ lines.push("");
123
+ lines.push(`| Metric | Value |`);
124
+ lines.push(`|--------|-------|`);
125
+ lines.push(`| Total runs | ${runs.length} |`);
126
+ lines.push(`| Completed | ${completed.length} |`);
127
+ lines.push(`| Failed/Cancelled | ${failed.length} |`);
128
+ if (avgDuration > 0) {
129
+ lines.push(`| Avg duration | ${avgDuration.toFixed(1)} min |`);
130
+ }
131
+ lines.push("");
132
+
133
+ // Past runs table
134
+ if (runs.length > 0) {
135
+ lines.push("## Past Runs");
136
+ lines.push("");
137
+ lines.push("| Run | Goal | Duration | Status |");
138
+ lines.push("|-----|------|----------|--------|");
139
+
140
+ for (const run of runs) {
141
+ const duration = formatDuration(run.createdAt, run.completedAt);
142
+ const goalPreview = run.goal ? run.goal.slice(0, 40) : "N/A";
143
+ const goalSuffix = run.goal && run.goal.length > 40 ? "..." : "";
144
+ const status = run.status ?? "unknown";
145
+ const statusIcon = status === "completed" ? "✅" : status === "failed" ? "❌" : status === "cancelled" ? "⏹️" : "⚠️";
146
+ lines.push(`| \`${run.runId.slice(-8)}\` | ${goalPreview}${goalSuffix} | ${duration} | ${statusIcon} ${status} |`);
147
+ }
148
+ lines.push("");
149
+ }
150
+
151
+ // How to run
152
+ lines.push("## How to Run");
153
+ lines.push("");
154
+ lines.push("```bash");
155
+ lines.push(`team action='run' team='${team}' goal="<your goal>"`);
156
+ lines.push("```");
157
+ lines.push("");
158
+ lines.push("See `team action='help'` for more options.");
159
+ lines.push("");
160
+
161
+ // Teams
162
+ lines.push("## Available Teams");
163
+ lines.push("");
164
+ try {
165
+ const discovery = discoverTeams(cwd);
166
+ const teams = allTeams(discovery);
167
+ for (const t of teams) {
168
+ lines.push(`- \`${t.name}\` — ${t.description ?? "No description"}`);
169
+ }
170
+ } catch {
171
+ lines.push("- *(team discovery unavailable)*");
172
+ }
173
+ lines.push("");
174
+
175
+ // Footer
176
+ lines.push("---");
177
+ lines.push("*Generated by pi-crew. For up-to-date info, check recent run history.*");
178
+
179
+ return lines.join("\n");
180
+ }