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,185 @@
1
+ /**
2
+ * context-status-injection.ts — Ambient crew-status injection (GAP-2).
3
+ *
4
+ * Registers a `context` event handler that keeps the parent agent continuously
5
+ * aware of in-flight crew runs. Without this, the agent "forgets" about active
6
+ * runs between turns unless it explicitly calls the `team` tool.
7
+ *
8
+ * ## How it works
9
+ *
10
+ * Pi's `context` event fires before EVERY LLM call (see Pi source
11
+ * `extensions/runner.ts:emitContext`). The handler receives the full messages
12
+ * array and may return a modified copy. Critically, the returned messages are
13
+ * used ONLY for that single LLM call (`agent-loop.ts:283-289` feeds the result
14
+ * straight into `convertToLlm` for the request) — they do NOT mutate the
15
+ * agent's persistent `state.messages`. So injection is transient per-call:
16
+ * - No accumulation across turns (the note never enters history).
17
+ * - No need to dedup against prior injections.
18
+ * - No risk of corrupting the conversation transcript.
19
+ *
20
+ * The injected note is a compact 1–4 line ambient status, inserted BEFORE the
21
+ * last message so the last message remains the active turn driver (preserves
22
+ * the user/assistant/tool alternation the LLMs expect).
23
+ *
24
+ * ## Safety
25
+ *
26
+ * - No-op when zero runs are in-flight (returns undefined → Pi uses original
27
+ * messages unchanged). Normal single-agent operation is completely unaffected.
28
+ * - `emitContext` already wraps handlers in try/catch and emits errors instead
29
+ * of crashing the loop (Pi `runner.ts:933`), so a throw here can't break the
30
+ * agent — but we also guard defensively.
31
+ * - Opt-out: `runtime.reliability.ambientStatusInjection: false` in config.
32
+ */
33
+
34
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
35
+ import type { Message } from "@earendil-works/pi-ai";
36
+ import type { ContextEvent, ExtensionAPI } from "@earendil-works/pi-coding-agent";
37
+ import type { TeamRunManifest } from "../state/types.ts";
38
+ import { extractSessionId } from "../utils/session-utils.ts";
39
+ import { collectInFlightRuns } from "./registration/compaction-guard.ts";
40
+
41
+ /** Sentinel that marks an injected ambient-status user message. */
42
+ export const AMBIENT_STATUS_SENTINEL = "[pi-crew ambient status";
43
+
44
+ /** Cap the number of runs listed inline to keep the note compact. */
45
+ const MAX_INLINE_RUNS = 3;
46
+ /** Truncate long goals so one run can't dominate the context window. */
47
+ const MAX_GOAL_LEN = 80;
48
+
49
+ /**
50
+ * Cheap human-readable run age from manifest timestamps (no extra I/O).
51
+ * Returns "running 12m" / "updated 3m ago" style, or "" if timestamps are
52
+ * missing/invalid. Keeps the ambient note informative without reading
53
+ * tasks.json on every LLM call.
54
+ */
55
+ function runAge(createdAt?: string, updatedAt?: string): string {
56
+ try {
57
+ const updated = updatedAt ? Date.parse(updatedAt) : NaN;
58
+ const created = createdAt ? Date.parse(createdAt) : NaN;
59
+ if (Number.isFinite(updated)) {
60
+ const sinceUpdate = Date.now() - updated;
61
+ if (sinceUpdate < 60_000) return `, updated just now`;
62
+ return `, updated ${humanizeMs(sinceUpdate)} ago`;
63
+ }
64
+ if (Number.isFinite(created)) {
65
+ return `, running ${humanizeMs(Date.now() - created)}`;
66
+ }
67
+ } catch {
68
+ /* ignore malformed timestamps */
69
+ }
70
+ return "";
71
+ }
72
+
73
+ function humanizeMs(ms: number): string {
74
+ if (ms < 60_000) return `${Math.round(ms / 1000)}s`;
75
+ const m = Math.floor(ms / 60_000);
76
+ if (m < 60) return `${m}m`;
77
+ const h = Math.floor(m / 60);
78
+ return h < 24 ? `${h}h${m % 60}m` : `${Math.floor(h / 24)}d`;
79
+ }
80
+
81
+ /**
82
+ * Build a compact, human+LLM-readable ambient status string for the given
83
+ * in-flight runs. Returns "" for an empty list (caller treats as no-op).
84
+ *
85
+ * Exported for unit testing.
86
+ */
87
+ export function formatAmbientStatus(runs: TeamRunManifest[]): string {
88
+ if (runs.length === 0) return "";
89
+ const truncate = (s: string, n: number): string => (s.length > n ? `${s.slice(0, n - 1)}…` : s);
90
+ const lines: string[] = [
91
+ `${AMBIENT_STATUS_SENTINEL} — environmental context, not a user request]`,
92
+ `${runs.length} pi-crew run${runs.length === 1 ? "" : "s"} in flight:`,
93
+ ];
94
+ const shown = runs.slice(0, MAX_INLINE_RUNS);
95
+ for (const run of shown) {
96
+ const wf = run.workflow ? `, ${run.workflow}` : "";
97
+ const age = runAge(run.createdAt, run.updatedAt);
98
+ lines.push(`• ${run.runId} (${run.status}, ${run.team}${wf})${age}: ${truncate(run.goal ?? "(no goal)", MAX_GOAL_LEN)}`);
99
+ }
100
+ if (runs.length > MAX_INLINE_RUNS) {
101
+ lines.push(`• …and ${runs.length - MAX_INLINE_RUNS} more`);
102
+ }
103
+ lines.push(
104
+ 'Inspect/join via the `team` tool: action="status" (list), action="wait" (join running), action="summary"/action="get" (results).',
105
+ );
106
+ return lines.join("\n");
107
+ }
108
+
109
+ /**
110
+ * Construct a user-role AgentMessage carrying the ambient status. Uses the
111
+ * `user` role (the Message union has no `system` role — the system prompt is a
112
+ * separate field). The sentinel prefix signals to the model that this is
113
+ * environmental information, not a typed user instruction.
114
+ *
115
+ * Exported for unit testing.
116
+ */
117
+ export function buildStatusMessage(runs: TeamRunManifest[]): Message {
118
+ return {
119
+ role: "user",
120
+ content: [{ type: "text", text: formatAmbientStatus(runs) }],
121
+ timestamp: Date.now(),
122
+ };
123
+ }
124
+
125
+ /** Result type for the `context` event handler (mirrors Pi's ContextEventResult,
126
+ * which isn't re-exported from the coding-agent package entry). */
127
+ export interface AmbientContextResult {
128
+ messages?: AgentMessage[];
129
+ }
130
+
131
+ /**
132
+ * Core handler logic, separated from the Pi registration so it is trivially
133
+ * unit-testable without a live ExtensionAPI.
134
+ *
135
+ * Returns `{messages}` with the ambient status inserted before the last
136
+ * message, or `undefined` to leave the context untouched (no in-flight runs).
137
+ *
138
+ * Exported for unit testing.
139
+ */
140
+ export function handleContextEvent(event: ContextEvent, cwd: string, sessionId?: string): AmbientContextResult | undefined {
141
+ let runs: TeamRunManifest[] = [];
142
+ try {
143
+ runs = collectInFlightRuns(cwd, sessionId);
144
+ } catch {
145
+ // State read failure → don't inject, don't crash. Pi catches handler
146
+ // errors anyway, but we avoid noisy error emission for a best-effort
147
+ // awareness feature.
148
+ return undefined;
149
+ }
150
+ if (runs.length === 0) return undefined;
151
+
152
+ const messages = [...event.messages];
153
+ const statusMsg = buildStatusMessage(runs);
154
+ // Insert BEFORE the last message so the genuine last message (the current
155
+ // turn driver — user prompt or tool result) stays last. When there are 0–1
156
+ // messages, appending is the only sensible option.
157
+ const insertAt = messages.length > 1 ? messages.length - 1 : messages.length;
158
+ messages.splice(insertAt, 0, statusMsg as unknown as AgentMessage);
159
+ return { messages };
160
+ }
161
+
162
+ /**
163
+ * Register the ambient-status `context` event handler. Reads the project cwd
164
+ * from the session context on each call (crew state is per-project).
165
+ *
166
+ * Pass `enabled: false` (from `runtime.reliability.ambientStatusInjection`) to
167
+ * disable the feature without unwiring the handler.
168
+ */
169
+ export function registerContextStatusInjection(pi: ExtensionAPI, opts: { enabled?: boolean } = {}): void {
170
+ if (opts.enabled === false) return;
171
+ pi.on("context", (event: ContextEvent, ctx: unknown): AmbientContextResult | undefined => {
172
+ // crew state is per-project; use the session ctx cwd when available,
173
+ // falling back to process.cwd(). Thread the session id so ambient
174
+ // status only reflects runs owned by THIS session (the state store is
175
+ // per-project, shared across sessions).
176
+ const cwd =
177
+ typeof ctx === "object" && ctx !== null && typeof (ctx as { cwd?: unknown }).cwd === "string"
178
+ ? (ctx as { cwd: string }).cwd
179
+ : typeof process.cwd === "function"
180
+ ? process.cwd()
181
+ : ".";
182
+ const sessionId = extractSessionId(ctx);
183
+ return handleContextEvent(event, cwd, sessionId);
184
+ });
185
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Crew editor autocomplete provider (Round 13 UX).
3
+ *
4
+ * Wraps Pi's built-in autocomplete provider and adds natural-language crew
5
+ * phrase completion: when the user types `crew <prefix>` or `team <prefix>`
6
+ * at the start of the input line, we suggest the matching phrases (e.g.
7
+ * "crew status → /team-status"). This teaches users the natural-language
8
+ * phrases that the input router (crew-input-router.ts) will rewrite on
9
+ * submit — so they discover the feature without reading docs.
10
+ *
11
+ * For any non-crew input we delegate to the wrapped (`current`) provider, so
12
+ * slash-command, file (`@`), and command-argument completion all keep working
13
+ * unchanged.
14
+ */
15
+ import type { AutocompleteItem, AutocompleteProvider, AutocompleteSuggestions } from "@earendil-works/pi-tui";
16
+ import { CREW_PHRASES } from "./crew-input-router.ts";
17
+
18
+ /** Max phrases to suggest. */
19
+ const MAX_PHRASES = 12;
20
+
21
+ /**
22
+ * If the text before the cursor is a crew-natural-language trigger, return the
23
+ * query word (the partial keyword after `crew `/`team `), or `undefined` when
24
+ * it is not a crew trigger.
25
+ *
26
+ * Triggers look like `crew ` or `team ` optionally followed by a partial word
27
+ * made of word characters, anchored at the start of the line.
28
+ */
29
+ function extractCrewQuery(textBeforeCursor: string): string | undefined {
30
+ // Anchor at start of line; require the `crew|team` keyword + whitespace,
31
+ // then an optional partial word. We do NOT trigger mid-word on the keyword
32
+ // itself (e.g. "cre" alone is not a trigger) — the keyword must be complete.
33
+ const match = textBeforeCursor.match(/^(?:crew|team)\s+([\w-]*)$/i);
34
+ return match?.[1];
35
+ }
36
+
37
+ /** Filter the shared phrase list by a partial keyword prefix. */
38
+ export function suggestCrewPhrases(query: string): AutocompleteItem[] {
39
+ const q = query.toLowerCase();
40
+ // Phrases are keyed by their keyword after "crew "/"team " (or the bare
41
+ // word for "teams"). Build a lookup keyword per phrase.
42
+ const seen = new Set<string>();
43
+ const items: AutocompleteItem[] = [];
44
+ for (const entry of CREW_PHRASES) {
45
+ // Derive the autocomplete keyword: for "crew status" → "status";
46
+ // for "teams" → "teams".
47
+ const parts = entry.phrase.split(/\s+/);
48
+ const keyword = parts.length > 1 ? parts.slice(1).join(" ") : entry.phrase;
49
+ if (seen.has(entry.phrase)) continue;
50
+ if (q && !keyword.toLowerCase().startsWith(q)) continue;
51
+ seen.add(entry.phrase);
52
+ items.push({
53
+ value: entry.phrase,
54
+ label: entry.phrase,
55
+ description: `→ ${entry.command}`,
56
+ });
57
+ if (items.length >= MAX_PHRASES) break;
58
+ }
59
+ return items;
60
+ }
61
+
62
+ /**
63
+ * Create a crew autocomplete provider that wraps `current`. When the input is
64
+ * a crew natural-language trigger, returns phrase suggestions; otherwise
65
+ * delegates to `current`.
66
+ */
67
+ export function createCrewAutocompleteProvider(current: AutocompleteProvider): AutocompleteProvider {
68
+ return {
69
+ async getSuggestions(
70
+ lines: string[],
71
+ cursorLine: number,
72
+ cursorCol: number,
73
+ options: { signal: AbortSignal; force?: boolean },
74
+ ): Promise<AutocompleteSuggestions | null> {
75
+ // Only trigger on the first line (Pi's main input is single-line;
76
+ // multiline editors are out of scope and would surprise the user).
77
+ if (cursorLine === 0) {
78
+ const currentLine = lines[cursorLine] ?? "";
79
+ const before = currentLine.slice(0, cursorCol);
80
+ const query = extractCrewQuery(before);
81
+ if (query !== undefined) {
82
+ const items = suggestCrewPhrases(query);
83
+ if (items.length > 0) {
84
+ // prefix = the full text to replace (e.g. "crew st").
85
+ // The default applyCompletion replaces the trailing
86
+ // `prefix`-length chars with item.value.
87
+ return { items, prefix: before };
88
+ }
89
+ // Triggered but no matches — return empty rather than
90
+ // falling through to file/command completion, so the user
91
+ // doesn't get a confusing file list while typing a phrase.
92
+ return { items: [], prefix: before };
93
+ }
94
+ }
95
+ return current.getSuggestions(lines, cursorLine, cursorCol, options);
96
+ },
97
+
98
+ applyCompletion(
99
+ lines: string[],
100
+ cursorLine: number,
101
+ cursorCol: number,
102
+ item: AutocompleteItem,
103
+ prefix: string,
104
+ ): { lines: string[]; cursorLine: number; cursorCol: number } {
105
+ // Delegate to the wrapped provider. For a non-slash, non-@ prefix
106
+ // the default applyCompletion replaces the trailing `prefix`-length
107
+ // chars with item.value — which is exactly the full phrase. This
108
+ // matches our prefix contract (prefix = full text to cursor).
109
+ return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
110
+ },
111
+
112
+ shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean {
113
+ // Suppress file-completion trigger inside a crew phrase so the
114
+ // editor doesn't pop a file list over our phrase suggestions.
115
+ if (cursorLine === 0) {
116
+ const before = (lines[cursorLine] ?? "").slice(0, cursorCol);
117
+ if (extractCrewQuery(before) !== undefined) return false;
118
+ }
119
+ return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
120
+ },
121
+ };
122
+ }
123
+
124
+ /** Register the crew autocomplete provider on a Pi UI context. Safe to call once. */
125
+ export function registerCrewAutocomplete(ctx: {
126
+ ui?: {
127
+ addAutocompleteProvider?: (factory: (current: AutocompleteProvider) => AutocompleteProvider) => void;
128
+ };
129
+ }): void {
130
+ ctx.ui?.addAutocompleteProvider?.((current) => createCrewAutocompleteProvider(current));
131
+ }
@@ -0,0 +1,168 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { cleanupAllTrackedTempDirs } from "../runtime/pi-args.ts";
3
+ import { logInternalError } from "../utils/internal-error.ts";
4
+
5
+ // NOTE: globalProgressTracker import kept for documentation but not directly used
6
+ // since we don't have agent IDs to untrack. Actual progress clearing should be
7
+ // handled by the progress tracker itself on shutdown.
8
+ // import { globalProgressTracker } from "../runtime/progress-tracker.ts";
9
+
10
+ /**
11
+ * Registers cleanup handlers for graceful shutdown.
12
+ * Handles session_shutdown and SIGTERM/SIGHUP signals.
13
+ */
14
+
15
+ // Module-level flag to ensure signal handlers are registered only once,
16
+ // even if registerCleanupHandler is called multiple times (e.g., on extension
17
+ // reload or during dev hot-reload). Without this, listeners stack up and
18
+ // cleanupChildProcesses fires N times on shutdown.
19
+ let signalHandlersRegistered = false;
20
+
21
+ interface ChildProcessInfo {
22
+ pid: number;
23
+ runId: string;
24
+ agentId: string;
25
+ startedAt: number;
26
+ }
27
+
28
+ class ChildProcessRegistry {
29
+ private processes = new Map<number, ChildProcessInfo>();
30
+
31
+ register(pid: number, runId: string, agentId: string): void {
32
+ this.processes.set(pid, { pid, runId, agentId, startedAt: Date.now() });
33
+ }
34
+
35
+ unregister(pid: number): void {
36
+ this.processes.delete(pid);
37
+ }
38
+
39
+ getAllPids(): number[] {
40
+ return Array.from(this.processes.keys());
41
+ }
42
+
43
+ getInfo(pid: number): ChildProcessInfo | undefined {
44
+ return this.processes.get(pid);
45
+ }
46
+
47
+ clear(): void {
48
+ this.processes.clear();
49
+ }
50
+ }
51
+
52
+ export const childProcessRegistry = new ChildProcessRegistry();
53
+
54
+ /**
55
+ * Optional UI timer disposal hook. Set by register.ts so the SIGTERM/SIGHUP
56
+ * signal handler can dispose terminal-status timers (idle re-assert loop +
57
+ * flash clear) that would otherwise keep the event loop alive.
58
+ */
59
+ let terminalStatusDispose: (() => void) | undefined;
60
+
61
+ export function registerCleanupHandler(pi: ExtensionAPI, opts?: { disposeTerminalStatus?: () => void }): void {
62
+ // Store the latest dispose fn at module level so the signal handler
63
+ // (registered once) always uses the current one even across re-registrations.
64
+ terminalStatusDispose = opts?.disposeTerminalStatus;
65
+ // Handle session_shutdown event
66
+ pi.on("session_shutdown", async () => {
67
+ console.log("[pi-crew] Session shutdown - cleaning up resources");
68
+
69
+ try {
70
+ // Kill all child-pi processes
71
+ await cleanupChildProcesses();
72
+
73
+ // Cleanup temp directories
74
+ await cleanupTempDirectories();
75
+
76
+ console.log("[pi-crew] Cleanup complete");
77
+ } catch (error) {
78
+ logInternalError("crew-cleanup.shutdown", error);
79
+ }
80
+ });
81
+
82
+ // Register signal handlers exactly once, even if registerCleanupHandler
83
+ // is called multiple times. This prevents listener stacking on extension
84
+ // reload and avoids double-cleanup on shutdown.
85
+ if (!signalHandlersRegistered) {
86
+ signalHandlersRegistered = true;
87
+ const handleSignal = async (signal: string): Promise<void> => {
88
+ console.log(`[pi-crew] Received ${signal} - starting cleanup`);
89
+ // Dispose UI timers so the idle re-assert loop / flash clear don't
90
+ // keep the event loop alive after child processes are killed.
91
+ try {
92
+ terminalStatusDispose?.();
93
+ } catch {
94
+ // Best-effort — must not block child cleanup on signal.
95
+ }
96
+ await cleanupChildProcesses();
97
+ };
98
+ process.on("SIGTERM", () => {
99
+ handleSignal("SIGTERM").catch((error) => {
100
+ logInternalError("crew-cleanup.SIGTERM", error);
101
+ });
102
+ });
103
+ process.on("SIGHUP", () => {
104
+ handleSignal("SIGHUP").catch((error) => {
105
+ logInternalError("crew-cleanup.SIGHUP", error);
106
+ });
107
+ });
108
+ }
109
+ }
110
+
111
+ async function cleanupChildProcesses(): Promise<void> {
112
+ const pids = childProcessRegistry.getAllPids();
113
+ if (pids.length === 0) return;
114
+ // Lazy-import to avoid eagerly pulling the heavy runtime/child-pi chain into the
115
+ // extension load path (check-lazy-imports) AND to avoid a static import cycle
116
+ // (child-pi.ts already imports registerChildProcess from this module).
117
+ // LAZY: defer child-pi.ts load (heavy runtime chain) + avoid static import cycle.
118
+ const { killProcessPid } = await import("../runtime/child-pi.ts");
119
+ for (const pid of pids) {
120
+ // killProcessPid signals the whole process GROUP (-pid on POSIX, taskkill /T on
121
+ // Windows) and escalates to SIGKILL after HARD_KILL_MS. A bare
122
+ // process.kill(pid, "SIGTERM") only reaches the direct child — grandchildren
123
+ // (bash/MCP the worker spawned) in the child's group survive as orphans, and
124
+ // a child that lags SIGTERM would linger forever with no escalation.
125
+ // NOTE: on graceful session_shutdown (reason=quit/reload) register.ts ALSO runs
126
+ // terminateActiveChildPiProcesses(); both are idempotent for a dying pid. This
127
+ // signal handler (SIGTERM/SIGHUP) is the SOLE teardown path when the Pi process
128
+ // is killed by signal, so it must be robust on its own.
129
+ try {
130
+ killProcessPid(pid);
131
+ console.log(`[pi-crew] Sent group SIGTERM (+SIGKILL escalation) to child process ${pid}`);
132
+ } catch (error: unknown) {
133
+ // Process may already be dead or not exist
134
+ const err = error as NodeJS.ErrnoException;
135
+ if (err.code !== "ESRCH" && err.code !== "ENOENT") {
136
+ logInternalError("crew-cleanup.kill", error, `pid=${pid}`);
137
+ }
138
+ }
139
+ childProcessRegistry.unregister(pid);
140
+ }
141
+
142
+ // Clear progress tracker
143
+ // Note: Can't call untrack on all because we don't track agent IDs here
144
+ // The progress tracker should clear itself on shutdown via session_dispose
145
+ }
146
+
147
+ async function cleanupTempDirectories(): Promise<void> {
148
+ // Clean up every temp dir created in this process. Previously this was
149
+ // a stub that just logged; it caused /tmp/pi-crew-* dirs to accumulate
150
+ // from killed test runs and child-pi invocations. See issue #<n>.
151
+ try {
152
+ const result = cleanupAllTrackedTempDirs();
153
+ if (result.cleaned > 0) {
154
+ console.log(`[pi-crew] Cleaned ${result.cleaned} tracked temp dirs (${result.failed} failed)`);
155
+ }
156
+ } catch (error) {
157
+ logInternalError("crew-cleanup.temp", error);
158
+ }
159
+ }
160
+
161
+ // Export for child-pi.ts to register processes
162
+ export function registerChildProcess(pid: number, runId: string, agentId: string): void {
163
+ childProcessRegistry.register(pid, runId, agentId);
164
+ }
165
+
166
+ export function unregisterChildProcess(pid: number): void {
167
+ childProcessRegistry.unregister(pid);
168
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Natural-language crew input routing (Round 13 UX).
3
+ *
4
+ * Pi fires the `input` event before skill/template expansion and before
5
+ * before_agent_start. A handler can transform the text (e.g. rewrite
6
+ * "crew status" → "/team-status"), or fully handle it.
7
+ *
8
+ * This module matches a small set of natural-language crew phrases and
9
+ * rewrites them to the equivalent slash command, so users do not need to
10
+ * memorize command names. Slash-command input (text starting with "/") is
11
+ * always passed through unchanged — we never shadow explicit commands.
12
+ */
13
+ import type { InputEvent, InputEventResult } from "@earendil-works/pi-coding-agent";
14
+
15
+ /**
16
+ * Natural-language crew phrases → slash-command mapping.
17
+ *
18
+ * Single source of truth shared by:
19
+ * - the `input`-event router (rewrites submitted text), and
20
+ * - the editor autocomplete provider (suggests phrases as you type).
21
+ *
22
+ * Each entry maps a phrase (what the user types) to a slash command.
23
+ * The router matches when submitted text STARTS WITH a phrase (word boundary);
24
+ * the autocomplete matches when the line starts with `crew `/`team ` and the
25
+ * partial word is a prefix of a phrase's keyword.
26
+ */
27
+ export const CREW_PHRASES: ReadonlyArray<{ phrase: string; command: string }> = [
28
+ { phrase: "crew status", command: "/team-status" },
29
+ { phrase: "crew list", command: "/team-status" },
30
+ { phrase: "crew dashboard", command: "/team-dashboard" },
31
+ { phrase: "crew board", command: "/team-dashboard" },
32
+ { phrase: "crew panel", command: "/team-dashboard" },
33
+ { phrase: "crew help", command: "/team-help" },
34
+ { phrase: "crew commands", command: "/team-help" },
35
+ { phrase: "crew doctor", command: "/team-doctor" },
36
+ { phrase: "crew diagnose", command: "/team-doctor" },
37
+ { phrase: "teams", command: "/teams" },
38
+ ];
39
+
40
+ /**
41
+ * Build a case-insensitive anchored regex from a phrase. The leading `crew `
42
+ * keyword is treated as interchangeable with `team ` (so "crew status" matches
43
+ * both "crew status" and "team status"). Bare phrases like "teams" match
44
+ * verbatim.
45
+ */
46
+ function phraseToRegex(phrase: string): RegExp {
47
+ const kw = phrase.match(/^(crew|team)\s+(.*)$/i);
48
+ if (kw) {
49
+ const rest = kw[2].replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
+ return new RegExp(`^(?:crew|team)\\s+${rest}\\b`, "i");
51
+ }
52
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
53
+ return new RegExp(`^${escaped}\\b`, "i");
54
+ }
55
+
56
+ /**
57
+ * Try to rewrite a natural-language crew phrase into a slash command.
58
+ * Returns the rewritten command string, or `null` if no rule matches.
59
+ *
60
+ * Rules intentionally only match at the START of the input and require a
61
+ * word boundary, so ordinary sentences mentioning "crew" are untouched.
62
+ */
63
+ export function rewriteCrewInput(text: string): string | null {
64
+ const trimmed = text.trim();
65
+ // Never transform explicit slash commands or inputs that don't start with
66
+ // a crew/team keyword phrase.
67
+ if (trimmed.startsWith("/")) return null;
68
+ for (const entry of CREW_PHRASES) {
69
+ const match = trimmed.match(phraseToRegex(entry.phrase));
70
+ if (!match) continue;
71
+ // Carry any remaining args after the matched phrase forward.
72
+ const rest = trimmed.slice(match[0].length).trim();
73
+ return rest ? `${entry.command} ${rest}` : entry.command;
74
+ }
75
+ return null;
76
+ }
77
+
78
+ /**
79
+ * Pi `input` event handler. Transforms matching crew phrases; passes
80
+ * everything else through unchanged.
81
+ */
82
+ export function handleCrewInput(event: InputEvent): InputEventResult {
83
+ // Only transform interactive user input — never programmatic/scripted input.
84
+ if (event.source !== "interactive") return { action: "continue" };
85
+ const rewritten = rewriteCrewInput(event.text);
86
+ if (!rewritten) return { action: "continue" };
87
+ return { action: "transform", text: rewritten, images: event.images };
88
+ }
89
+
90
+ /** Register the crew input router on a Pi instance. Safe to call once. */
91
+ export function registerCrewInputRouter(pi: { on?: (event: "input", handler: (e: InputEvent) => InputEventResult) => void }): void {
92
+ pi.on?.("input", handleCrewInput);
93
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Crew keyboard shortcuts (Round 13 UX).
3
+ *
4
+ * Registers a small set of keyboard shortcuts for fast access to the most
5
+ * useful pi-crew overlays. Keys are chosen to avoid collisions with Pi's
6
+ * built-in keymap (see analysis of pi-tui core/keybindings defaults):
7
+ *
8
+ * alt+s → open the pi-crew settings overlay (config + theme picker)
9
+ * alt+c → open the pi-crew run dashboard overlay (mnemonic: **C**rew)
10
+ *
11
+ * OCCUPIED alt+ keys in the built-in keymap (must NOT reuse — verified against
12
+ * pi-tui TUI_KEYBINDINGS + pi core keybindings.js):
13
+ * alt+b (cursor word left) alt+f (cursor word right)
14
+ * alt+d (delete word forward) alt+y (yank pop)
15
+ * alt+v (paste) alt+s (crew settings — this module)
16
+ * alt+enter / alt+up/down/left/right / alt+backspace / alt+delete
17
+ * Free alt+<letter> keys include: a, c, e, g, h, i, j, k, l, m, n, o, p, q,
18
+ * r, t, u, w, x, z.
19
+ *
20
+ * NOTE: an earlier revision used `alt+d` for the dashboard; that collided
21
+ * with `tui.editor.deleteWordForward` and Pi's conflict detector stripped the
22
+ * editor binding. `alt+c` is free AND mnemonic.
23
+ *
24
+ * NOTE: alt+m (mailbox) and alt+t (status) were considered but are NOT wired
25
+ * — the mailbox overlay is run-scoped (requires a runId; reached via the
26
+ * dashboard) and there is no standalone status overlay (status is a text
27
+ * command). See the K-2 note accompanying openTeamDashboard in commands.ts.
28
+ *
29
+ * Shortcuts are guarded by `hasUI` so they never fire in print/RPC mode, and
30
+ * by the optional `registerShortcut` API so older Pi versions degrade
31
+ * gracefully (no-op).
32
+ */
33
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
34
+ import type { KeyId } from "@earendil-works/pi-tui";
35
+
36
+ type ShortcutHandler = (ctx: ExtensionContext) => Promise<void> | void;
37
+
38
+ interface ShortcutRegistration {
39
+ /** Pi KeyId, e.g. "alt+s". */
40
+ key: KeyId;
41
+ description: string;
42
+ handler: ShortcutHandler;
43
+ }
44
+
45
+ const CREW_SHORTCUTS: ReadonlyArray<ShortcutRegistration> = [
46
+ {
47
+ key: "alt+s",
48
+ description: "pi-crew: open settings (config + theme picker)",
49
+ // Lazy-import the overlay so this module stays lightweight at load time
50
+ // (avoids pulling the full commands.ts dependency tree into every
51
+ // process that imports this module, e.g. the unit test).
52
+ handler: async (ctx) => {
53
+ // LAZY: defer dynamic import of ./registration/commands.ts to its call site.
54
+ const { openTeamSettingsOverlay } = await import("./registration/commands.ts");
55
+ await openTeamSettingsOverlay(ctx);
56
+ },
57
+ },
58
+ {
59
+ key: "alt+c",
60
+ description: "pi-crew: open run dashboard (Crew)",
61
+ // Lazy-import so the heavy UI module chain (RunDashboard etc.) is only
62
+ // loaded on first use, not at extension load.
63
+ handler: async (ctx) => {
64
+ // LAZY: defer dynamic import of ./registration/commands.ts to its call site.
65
+ const { openTeamDashboard } = await import("./registration/commands.ts");
66
+ await openTeamDashboard(ctx);
67
+ },
68
+ },
69
+ ];
70
+
71
+ /**
72
+ * Register all crew keyboard shortcuts on a Pi instance. Safe to call once at
73
+ * extension load. No-ops when `registerShortcut` is unavailable (older Pi).
74
+ */
75
+ export function registerCrewShortcuts(pi: {
76
+ registerShortcut?: (shortcut: KeyId, options: { description?: string; handler: ShortcutHandler }) => void;
77
+ }): void {
78
+ for (const sc of CREW_SHORTCUTS) {
79
+ pi.registerShortcut?.(sc.key, {
80
+ description: sc.description,
81
+ handler: sc.handler,
82
+ });
83
+ }
84
+ }
85
+
86
+ /** Exported for tests / introspection. */
87
+ export const CREW_SHORTCUT_KEYS: readonly KeyId[] = CREW_SHORTCUTS.map((s) => s.key);
@@ -0,0 +1,18 @@
1
+ /**
2
+ * ANSI art cat animation frames extracted from runner-spritesheet.png.
3
+ * Used when PUA font glyphs are not available (web terminals like gotty).
4
+ * Each frame is an array of strings (one per line).
5
+ */
6
+ export const CAT_FRAMES: readonly string[][] = [
7
+ // Frame 0 — standing
8
+ ["▄▄ ███▄ ", "▀▀█████▄", " ██▀██ ", " ▀▀ ▀▀"],
9
+ // Frame 1 — step 1
10
+ [" ████ ", " █████▄", "▄██████▀", " ██ ▀█▄"],
11
+ // Frame 2 — step 2
12
+ [" ▄██████", " █████▀ ", "███████ ", "▀██████ "],
13
+ // Frame 3 — step 3
14
+ [" ████ ", "▄█████▄ ", "▀██████▄", "▄█▀ ██ "],
15
+ ] as const;
16
+
17
+ /** Number of animation frames. */
18
+ export const CAT_FRAME_COUNT = CAT_FRAMES.length;