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,418 @@
1
+ /**
2
+ * Crew widget — public API and component.
3
+ *
4
+ * Re-exports from widget submodules. The main component class and
5
+ * update/stop functions live here.
6
+ */
7
+
8
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
9
+ import type { CrewUiConfig } from "../../config/config.ts";
10
+ import { DEFAULT_UI } from "../../config/defaults.ts";
11
+ import type { ManifestCache } from "../../runtime/manifest-cache.ts";
12
+ import type { TeamRunManifest } from "../../state/types.ts";
13
+ import { truncate } from "../../utils/visual.ts";
14
+ import { requestRender, requestRenderTarget, setExtensionWidget } from "../pi-ui-compat.ts";
15
+ import type { OverlaySchedulerHandle } from "../shared-overlay-scheduler.ts";
16
+ import { registerOverlayScheduler } from "../shared-overlay-scheduler.ts";
17
+ import type { RunSnapshotCache } from "../snapshot-types.ts";
18
+ import { spinnerBucket, spinnerFrame } from "../spinner.ts";
19
+ import type { CrewTheme } from "../theme-adapter.ts";
20
+ import { asCrewTheme, subscribeThemeChange } from "../theme-adapter.ts";
21
+ import { activeWidgetRuns, statusSummary } from "./widget-model.ts";
22
+ import { buildWidgetLines, colorWidgetLine, DEFAULT_WIDGET_WIDTH, renderLines } from "./widget-renderer.ts";
23
+ import type { CrewWidgetModel, CrewWidgetState, WidgetRun } from "./widget-types.ts";
24
+
25
+ export { activeWidgetRuns, statusSummary } from "./widget-model.ts";
26
+ export {
27
+ buildWidgetLines as buildCrewWidgetLines,
28
+ DEFAULT_WIDGET_WIDTH,
29
+ TASK_DESC_MAX,
30
+ widgetHeader,
31
+ } from "./widget-renderer.ts";
32
+ // Re-export types and helpers for backward compatibility
33
+ export type {
34
+ CrewWidgetModel,
35
+ CrewWidgetState,
36
+ WidgetRun,
37
+ } from "./widget-types.ts";
38
+
39
+ /**
40
+ * Resolve the real render width for widget lines, in priority order:
41
+ * 1. explicit `width` argument (e.g. from caller that already knows terminal width)
42
+ * 2. `process.stdout.columns` (works in Node when stdout is a TTY)
43
+ * 3. `DEFAULT_WIDGET_WIDTH` (100) — last-resort fallback so we never paint
44
+ * a line wider than the smallest expected TUI.
45
+ *
46
+ * Callers SHOULD pass the width they already hold (e.g. `WidgetRender.render(width)`
47
+ * in this file already receives one). This helper exists for paths that don't.
48
+ */
49
+ export function getRenderWidth(width?: number): number {
50
+ if (Number.isFinite(width) && width! > 0) return Math.floor(width!);
51
+ const stdoutCols = (globalThis as { process?: { stdout?: { columns?: number } } }).process?.stdout?.columns;
52
+ if (Number.isFinite(stdoutCols) && stdoutCols! > 0) return Math.floor(stdoutCols!);
53
+ return DEFAULT_WIDGET_WIDTH;
54
+ }
55
+ export {
56
+ NOTIFICATION_BADGE_CAP,
57
+ notificationBadge,
58
+ } from "./widget-formatters.ts";
59
+
60
+ // ── Constants ─────────────────────────────────────────────────────────
61
+
62
+ const MAX_LINES_DEFAULT = DEFAULT_UI.widgetMaxLines;
63
+ const LEGACY_WIDGET_KEY = "pi-crew";
64
+ const WIDGET_KEY = "pi-crew-active";
65
+ const STATUS_KEY = "pi-crew";
66
+
67
+ /**
68
+ * C4 — short-TTL safety net for the buildSignature() result cache. Combined
69
+ * with invalidate-on-write (onInvalidate clears the cache on every real
70
+ * event), this prevents redundant O(runs×agents) signature recomputation
71
+ * during a burst of host-driven renders within one state snapshot while
72
+ * ensuring the signature is always recomputed after genuine data changes.
73
+ */
74
+ const SIGNATURE_CACHE_TTL_MS = 100;
75
+
76
+ // ── Terminal resize handling (T-2) ────────────────────────────────────
77
+ // On a terminal resize the widget's cached render width goes stale until the
78
+ // next invalidate; a mid-run resize could briefly paint a frame at the old
79
+ // width. We register ONE debounced process-level listener (guarded so it never
80
+ // accumulates across widget reinstalls) that busts the active widget's cache
81
+ // and pokes Pi to repaint at the new width. The listener references a
82
+ // module-level `activeResizeTarget` (the most-recently-mounted widget) rather
83
+ // than a specific instance, so replaced widgets do not leak listeners.
84
+ let resizeListenerInstalled = false;
85
+ let activeResizeTarget: { invalidate(): void; requestRepaint(): void } | undefined;
86
+ let resizeTimer: ReturnType<typeof setTimeout> | undefined;
87
+
88
+ /**
89
+ * F-1: the resize callback is stored at module level so
90
+ * `uninstallResizeListener()` can remove the exact same reference via
91
+ * `process.off` (requires the identical function reference).
92
+ */
93
+ const onResize = (): void => {
94
+ if (resizeTimer) clearTimeout(resizeTimer);
95
+ // Debounce (~120ms) so a drag-resize doesn't thrash renders.
96
+ resizeTimer = setTimeout(() => {
97
+ resizeTimer = undefined;
98
+ activeResizeTarget?.invalidate();
99
+ activeResizeTarget?.requestRepaint();
100
+ }, 120);
101
+ };
102
+
103
+ function installResizeListener(): void {
104
+ if (resizeListenerInstalled) return;
105
+ resizeListenerInstalled = true;
106
+ process.on("SIGWINCH", onResize);
107
+ // Windows has no SIGWINCH; Node emits "resize" on stdout instead.
108
+ if (typeof process.stdout?.on === "function") process.stdout.on("resize", onResize);
109
+ }
110
+
111
+ /**
112
+ * F-1: remove the process-level resize listeners so they don't outlive the
113
+ * extension session. Safe to call when no listener is installed (no-op).
114
+ * Re-installs automatically when a new widget mounts via `installResizeListener()`.
115
+ */
116
+ export function uninstallResizeListener(): void {
117
+ if (!resizeListenerInstalled) return;
118
+ resizeListenerInstalled = false;
119
+ if (resizeTimer) {
120
+ clearTimeout(resizeTimer);
121
+ resizeTimer = undefined;
122
+ }
123
+ activeResizeTarget = undefined;
124
+ process.off("SIGWINCH", onResize);
125
+ // Windows has no SIGWINCH; Node emits "resize" on stdout instead.
126
+ if (typeof process.stdout?.off === "function") process.stdout.off("resize", onResize);
127
+ }
128
+
129
+ // ── Widget Component ──────────────────────────────────────────────────
130
+
131
+ interface WidgetComponent {
132
+ render(width: number): string[];
133
+ invalidate(): void;
134
+ }
135
+
136
+ class CrewWidgetComponent implements WidgetComponent {
137
+ private readonly model: CrewWidgetModel;
138
+ private theme: CrewTheme;
139
+ private cacheSignature = "";
140
+ /** C4 — invalidate-on-write cache for the buildSignature() result. */
141
+ private cachedBuildSignature = "";
142
+ private cachedBuildSignatureAt = 0;
143
+ private cachedWidth = 0;
144
+ private cachedLines: string[] = [];
145
+ private cachedBaseLines: string[] = [];
146
+ private cachedTheme: CrewTheme;
147
+ private readonly tui: unknown;
148
+ private readonly unsubscribeTheme: () => void;
149
+ private readonly schedulerHandle: OverlaySchedulerHandle;
150
+
151
+ constructor(model: CrewWidgetModel, themeLike: unknown, tui?: unknown) {
152
+ this.model = model;
153
+ this.theme = asCrewTheme(themeLike);
154
+ this.cachedTheme = this.theme;
155
+ this.tui = tui;
156
+ // Register as the active resize target and ensure the single, guarded
157
+ // terminal-resize listener is installed. On a resize the cached width
158
+ // goes stale; busting the cache + requesting a repaint refreshes the
159
+ // widget at the new width without waiting for the next event tick (T-2).
160
+ activeResizeTarget = this;
161
+ installResizeListener();
162
+ this.unsubscribeTheme = subscribeThemeChange(themeLike, () => this.invalidate());
163
+ // 1.10 (UI-P1-1): route run:state / worker:lifecycle / ui:invalidate
164
+ // through a RenderScheduler (debounce + fallback) instead of three
165
+ // direct runEventBus.onChannel subscriptions. With 3 overlays
166
+ // subscribing independently a single event triggered up to 9 callbacks
167
+ // and ~150 invalidates/sec under load. The scheduler collapses bursts
168
+ // into one debounced invalidate.
169
+ this.schedulerHandle = registerOverlayScheduler(
170
+ () => this.invalidate(),
171
+ () => {
172
+ // C4 invalidate-on-write: drop the cached buildSignature()
173
+ // result immediately when a real event arrives (run:state /
174
+ // worker:lifecycle / ui:invalidate). The prior blind-TTL
175
+ // attempt (reverted in 619a0cd) held a stale signature
176
+ // within the window and skipped re-render on genuine state
177
+ // changes. Clearing here + in invalidate() guarantees the
178
+ // next render tick always recomputes from fresh data.
179
+ this.cachedBuildSignature = "";
180
+ this.cachedBuildSignatureAt = 0;
181
+ },
182
+ );
183
+ }
184
+
185
+ private buildSignature(runs: WidgetRun[]): string {
186
+ const liveSig = [...listLiveAgents()]
187
+ .map(
188
+ (h) =>
189
+ `${h.agentId}:${h.status}:${h.activity.turnCount}:${h.activity.toolUses}:${[...h.activity.activeTools.values()].join(",")}:${h.activity.responseText.slice(-30)}`,
190
+ )
191
+ .join("|");
192
+
193
+ const hasRunning =
194
+ runs.some((entry) => entry.agents.some((a) => a.status === "running")) ||
195
+ [...listLiveAgents()].some((h) => h.status === "running");
196
+ const animation = hasRunning ? `:spin=${spinnerBucket()}` : "";
197
+
198
+ const sig =
199
+ runs
200
+ .map(
201
+ (entry) =>
202
+ entry.snapshot?.signature ??
203
+ `${entry.run.runId}:${entry.run.status}:${entry.run.updatedAt}:` +
204
+ entry.agents
205
+ .map((a) => {
206
+ const recentOutput = a.progress?.recentOutput.at(-1) ?? "";
207
+ const progress = [
208
+ a.progress?.currentTool ?? "",
209
+ a.progress?.toolCount ?? 0,
210
+ a.progress?.tokens ?? 0,
211
+ a.progress?.turns ?? 0,
212
+ a.progress?.lastActivityAt ?? "",
213
+ recentOutput,
214
+ ].join(":");
215
+ return `${a.status}:${a.startedAt}:${a.completedAt ?? ""}:${a.toolUses ?? 0}:${progress}`;
216
+ })
217
+ .join(","),
218
+ )
219
+ .join("|") + `|live:${liveSig}${animation}`;
220
+ return sig;
221
+ }
222
+
223
+ private colorize(lines: string[], width: number): string[] {
224
+ return renderLines(
225
+ lines.map((line, index) => colorWidgetLine(line, index, this.theme)),
226
+ width,
227
+ );
228
+ }
229
+
230
+ invalidate(): void {
231
+ this.cacheSignature = "";
232
+ this.cachedBaseLines = [];
233
+ this.cachedLines = [];
234
+ this.cachedBuildSignature = "";
235
+ this.cachedBuildSignatureAt = 0;
236
+ }
237
+
238
+ /** Poke the host TUI to repaint immediately (defensive: no-op if unavailable). */
239
+ requestRepaint(): void {
240
+ requestRenderTarget(this.tui);
241
+ }
242
+
243
+ dispose(): void {
244
+ this.unsubscribeTheme();
245
+ this.schedulerHandle.dispose();
246
+ if (activeResizeTarget === this) activeResizeTarget = undefined;
247
+ }
248
+
249
+ render(width: number): string[] {
250
+ const runs = activeWidgetRuns(this.model.cwd, this.model.manifestCache, this.model.snapshotCache, this.model.preloadManifests);
251
+ // C4: invalidate-on-write signature cache. buildSignature() is
252
+ // O(runs×agents) string work called on every ~160ms host-driven render
253
+ // tick. Within one state snapshot (no event arrived → cache not cleared
254
+ // by onInvalidate) the result is identical, so we reuse the cached value
255
+ // to skip redundant recomputation. A short TTL acts as a safety net for
256
+ // the unlikely case where a real event is missed.
257
+ const now = Date.now();
258
+ let sigBase: string;
259
+ if (this.cachedBuildSignature !== "" && now - this.cachedBuildSignatureAt < SIGNATURE_CACHE_TTL_MS) {
260
+ sigBase = this.cachedBuildSignature;
261
+ } else {
262
+ sigBase = this.buildSignature(runs);
263
+ this.cachedBuildSignature = sigBase;
264
+ this.cachedBuildSignatureAt = now;
265
+ }
266
+ const signature = `${sigBase}:${this.model.notificationCount ?? 0}`;
267
+ const runningGlyph = spinnerFrame("widget-header");
268
+
269
+ if (this.cacheSignature !== signature || width !== this.cachedWidth || this.cachedTheme !== this.theme) {
270
+ this.cachedBaseLines = buildWidgetLines(
271
+ this.model.cwd,
272
+ 0,
273
+ this.model.maxLines,
274
+ runs,
275
+ this.model.notificationCount ?? 0,
276
+ width,
277
+ ).map((line, index) => {
278
+ if (index === 0 && line.length > 0) return `${runningGlyph}${line.slice(1)}`;
279
+ return line;
280
+ });
281
+ this.cachedLines = this.colorize(this.cachedBaseLines, width);
282
+ this.cachedWidth = width;
283
+ this.cachedTheme = this.theme;
284
+ this.cacheSignature = signature;
285
+ }
286
+
287
+ if (runs.length === 0) {
288
+ this.invalidate();
289
+ // P0-6: render from snapshots only — never read disk on every render tick.
290
+ // When the snapshot cache is provided but hasn't populated yet, paint a
291
+ // single "(loading…)" line so the pre-load frame is well-formed instead
292
+ // of an empty panel. Without a cache (legacy/tests) keep the empty result.
293
+ if (this.model.snapshotCache) return ["(loading…)"];
294
+ return [];
295
+ }
296
+
297
+ const updatedHeader = `${runningGlyph}${this.cachedBaseLines[0]?.slice(1) ?? ""}`;
298
+ this.cachedLines[0] = truncate(colorWidgetLine(updatedHeader, 0, this.theme), width);
299
+ return this.cachedLines.map((line) => truncate(line, width));
300
+ }
301
+ }
302
+
303
+ // ── Re-export listLiveAgents for buildSignature ───────────────────────
304
+
305
+ import { listLiveAgents } from "../../runtime/live-agent-manager.ts";
306
+
307
+ // ── Public API ────────────────────────────────────────────────────────
308
+
309
+ export function updateCrewWidget(
310
+ ctx: Pick<ExtensionContext, "cwd" | "hasUI" | "ui" | "sessionManager">,
311
+ state: CrewWidgetState,
312
+ config?: CrewUiConfig,
313
+ manifestCache?: ManifestCache,
314
+ snapshotCache?: RunSnapshotCache,
315
+ preloadedManifests?: TeamRunManifest[],
316
+ ): void {
317
+ if (!ctx.hasUI) return;
318
+ state.frame += 1;
319
+ const maxLines = config?.widgetMaxLines ?? MAX_LINES_DEFAULT;
320
+
321
+ let workspaceId = ctx.sessionManager?.getSessionId?.();
322
+ if (!workspaceId && manifestCache) {
323
+ const runs = manifestCache.list(20);
324
+ const active = runs.find((r) => r.status === "running" || r.status === "queued");
325
+ if (active?.ownerSessionId) workspaceId = active.ownerSessionId;
326
+ }
327
+
328
+ const runs = activeWidgetRuns(ctx.cwd, manifestCache, snapshotCache, preloadedManifests, workspaceId);
329
+ const lines = buildWidgetLines(ctx.cwd, state.frame, maxLines, runs, state.notificationCount ?? 0, getRenderWidth());
330
+ const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
331
+
332
+ ctx.ui.setStatus(STATUS_KEY, lines.length ? statusSummary(runs) : undefined);
333
+
334
+ const shouldClearLegacy = state.legacyCleared !== true || state.lastPlacement !== placement;
335
+ if (shouldClearLegacy) {
336
+ setExtensionWidget(ctx, LEGACY_WIDGET_KEY, undefined, { placement });
337
+ state.legacyCleared = true;
338
+ }
339
+
340
+ if (!lines.length) {
341
+ if (state.lastVisibility !== "hidden" || state.lastPlacement !== placement) {
342
+ setExtensionWidget(ctx, WIDGET_KEY, undefined, { placement });
343
+ state.lastVisibility = "hidden";
344
+ state.lastPlacement = placement;
345
+ state.lastKey = WIDGET_KEY;
346
+ state.lastMaxLines = maxLines;
347
+ state.lastCwd = ctx.cwd;
348
+ state.model = undefined;
349
+ }
350
+ requestRender(ctx);
351
+ return;
352
+ }
353
+
354
+ const needsWidgetInstall =
355
+ state.lastVisibility !== "visible" ||
356
+ state.lastPlacement !== placement ||
357
+ state.lastKey !== WIDGET_KEY ||
358
+ state.lastMaxLines !== maxLines ||
359
+ state.lastCwd !== ctx.cwd ||
360
+ !state.model;
361
+
362
+ if (!state.model)
363
+ state.model = {
364
+ cwd: ctx.cwd,
365
+ frame: state.frame,
366
+ maxLines,
367
+ notificationCount: state.notificationCount ?? 0,
368
+ manifestCache,
369
+ snapshotCache,
370
+ preloadManifests: preloadedManifests,
371
+ };
372
+ else {
373
+ state.model.cwd = ctx.cwd;
374
+ state.model.frame = state.frame;
375
+ state.model.maxLines = maxLines;
376
+ state.model.notificationCount = state.notificationCount ?? 0;
377
+ state.model.manifestCache = manifestCache;
378
+ state.model.snapshotCache = snapshotCache;
379
+ state.model.preloadManifests = preloadedManifests;
380
+ }
381
+
382
+ if (needsWidgetInstall) {
383
+ const model = state.model;
384
+ setExtensionWidget(ctx, WIDGET_KEY, ((_tui: unknown, theme: unknown) => new CrewWidgetComponent(model, theme, _tui)) as never, {
385
+ placement,
386
+ persist: true,
387
+ });
388
+ state.lastVisibility = "visible";
389
+ state.lastPlacement = placement;
390
+ state.lastKey = WIDGET_KEY;
391
+ state.lastMaxLines = maxLines;
392
+ state.lastCwd = ctx.cwd;
393
+ }
394
+
395
+ requestRender(ctx);
396
+ }
397
+
398
+ export function stopCrewWidget(
399
+ ctx: Pick<ExtensionContext, "hasUI" | "ui"> | undefined,
400
+ state: CrewWidgetState,
401
+ config?: CrewUiConfig,
402
+ ): void {
403
+ // F-1: remove the process-level SIGWINCH/resize listener when the widget is
404
+ // stopped. The listener is guarded so re-mounting a widget re-installs it.
405
+ uninstallResizeListener();
406
+ if (ctx?.hasUI) {
407
+ const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
408
+ ctx.ui.setStatus(STATUS_KEY, undefined);
409
+ setExtensionWidget(ctx, LEGACY_WIDGET_KEY, undefined, { placement });
410
+ setExtensionWidget(ctx, WIDGET_KEY, undefined, { placement });
411
+ state.lastVisibility = "hidden";
412
+ state.lastPlacement = placement;
413
+ state.lastKey = WIDGET_KEY;
414
+ state.model = undefined;
415
+ state.legacyCleared = true;
416
+ requestRender(ctx);
417
+ }
418
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Widget formatting utilities.
3
+ *
4
+ * Extracted from crew-widget.ts for reuse and testability.
5
+ */
6
+
7
+ import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
8
+ import type { LiveAgentHandle } from "../../runtime/live-agent-manager.ts";
9
+ import { getTaskUsage } from "../../runtime/usage-tracker.ts";
10
+ import { visibleWidth } from "../../utils/visual.ts";
11
+ import { computeLiveDurationMs } from "../live-duration.ts";
12
+
13
+ // ── Token formatting ──────────────────────────────────────────────────
14
+
15
+ // V-1: fixed visible widths for per-agent numeric metrics so columns don't
16
+ // jitter every tick as values change width (e.g. 9.9s→10.0s, 950→1.0k).
17
+ // alignMetric right-aligns to the width; values wider than the width overflow
18
+ // verbatim (no truncation/crash) — these are rare one-off states, not jitter.
19
+ const TOOLS_METRIC_WIDTH = 8; // "127 tools"
20
+ const TOKENS_METRIC_WIDTH = 10; // "1.2k tok", "12.3M tok"
21
+ const TPS_METRIC_WIDTH = 9; // "411 tok/s"
22
+ const CTX_METRIC_WIDTH = 7; // "100% ctx"
23
+ const DURATION_METRIC_WIDTH = 6; // "120.0s"
24
+
25
+ function alignMetric(value: string, width: number): string {
26
+ const pad = Math.max(0, width - visibleWidth(value));
27
+ return " ".repeat(pad) + value;
28
+ }
29
+
30
+ export function formatTokensCompact(count: number): string {
31
+ if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M tok`;
32
+ if (count >= 1_000) return `${(count / 1_000).toFixed(1)}k tok`;
33
+ return `${count} tok`;
34
+ }
35
+
36
+ // ── Elapsed time ──────────────────────────────────────────────────────
37
+
38
+ export function elapsed(iso: string | undefined, now = Date.now()): string | undefined {
39
+ if (!iso) return undefined;
40
+ const ms = Math.max(0, now - new Date(iso).getTime());
41
+ if (!Number.isFinite(ms)) return undefined;
42
+ if (ms < 1000) return "now";
43
+ if (ms < 60_000) return `${Math.floor(ms / 1000)}s`;
44
+ if (ms < 3_600_000) return `${Math.floor(ms / 60_000)}m`;
45
+ return `${Math.floor(ms / 3_600_000)}h`;
46
+ }
47
+
48
+ // ── Agent activity description ────────────────────────────────────────
49
+
50
+ const TOOL_LABELS: Record<string, string> = {
51
+ read: "reading",
52
+ bash: "running command",
53
+ edit: "editing",
54
+ write: "writing",
55
+ grep: "searching",
56
+ find: "finding files",
57
+ ls: "listing",
58
+ };
59
+
60
+ const TOOL_ICONS: Record<string, string> = {
61
+ read: "📖",
62
+ bash: ">",
63
+ edit: "✏",
64
+ write: "📝",
65
+ grep: "🔍",
66
+ find: "📁",
67
+ ls: "📋",
68
+ agent: "🤖",
69
+ };
70
+
71
+ export function describeLiveActivity(handle: LiveAgentHandle): string {
72
+ const act = handle.activity;
73
+ if (act.activeTools.size > 0) {
74
+ const groups = new Map<string, number>();
75
+ for (const toolName of act.activeTools.values()) {
76
+ groups.set(toolName, (groups.get(toolName) ?? 0) + 1);
77
+ }
78
+ const parts: string[] = [];
79
+ for (const [toolName, count] of groups) {
80
+ const icon = TOOL_ICONS[toolName] ?? "?";
81
+ const label = TOOL_LABELS[toolName] ?? toolName;
82
+ if (count > 1) {
83
+ parts.push(`${icon}${count} ${label}s`);
84
+ } else {
85
+ parts.push(`${icon} ${label}`);
86
+ }
87
+ }
88
+ return parts.join(", ") + "…";
89
+ }
90
+ if (act.responseText?.trim()) {
91
+ const line =
92
+ act.responseText
93
+ .split("\n")
94
+ .find((l) => l.trim())
95
+ ?.trim() ?? "";
96
+ return line.length > 60 ? line.slice(0, 60) + "…" : line;
97
+ }
98
+ return "thinking…";
99
+ }
100
+
101
+ export function agentActivity(agent: CrewAgentRecord, liveHandle?: LiveAgentHandle): string {
102
+ if (liveHandle && liveHandle.status === "running") {
103
+ const live = describeLiveActivity(liveHandle);
104
+ if (live === "thinking…" && agent.progress?.currentTool)
105
+ return `${TOOL_LABELS[agent.progress.currentTool] ?? agent.progress.currentTool}…`;
106
+ return live;
107
+ }
108
+ if (agent.progress?.currentTool) return `${TOOL_LABELS[agent.progress.currentTool] ?? agent.progress.currentTool}…`;
109
+ const recent = agent.progress?.recentOutput?.at(-1);
110
+ if (recent) {
111
+ const cleaned = recent.replace(/\s+/g, " ").trim();
112
+ return cleaned.length > 60 ? cleaned.slice(0, 60) + "…" : cleaned;
113
+ }
114
+ if (agent.progress?.activityState === "needs_attention") return "needs attention";
115
+ if (agent.status === "queued") return "queued";
116
+ if (agent.status === "running") {
117
+ const age = agent.startedAt ? Date.now() - new Date(agent.startedAt).getTime() : Infinity;
118
+ if (age < 5000 && !agent.progress?.currentTool) return "spawning…";
119
+ return "thinking…";
120
+ }
121
+ if (agent.status === "failed") return agent.error ?? "failed";
122
+ return "done";
123
+ }
124
+
125
+ // ── Agent stats line ──────────────────────────────────────────────────
126
+
127
+ export function agentStats(agent: CrewAgentRecord, liveHandle?: LiveAgentHandle): string {
128
+ const parts: string[] = [];
129
+ if (liveHandle) {
130
+ const act = liveHandle.activity;
131
+ if (act.toolUses > 0) parts.push(alignMetric(`${act.toolUses} tools`, TOOLS_METRIC_WIDTH));
132
+ const usage = getTaskUsage(liveHandle.taskId);
133
+ const total = (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheWrite ?? 0);
134
+ if (total > 0) parts.push(alignMetric(formatTokensCompact(total), TOKENS_METRIC_WIDTH));
135
+ try {
136
+ const stats = liveHandle.session.getSessionStats?.();
137
+ const ctxPct = stats?.contextUsage?.percent;
138
+ if (ctxPct != null) parts.push(alignMetric(`${Math.round(ctxPct)}% ctx`, CTX_METRIC_WIDTH));
139
+ } catch {
140
+ /* ignore */
141
+ }
142
+ const ms = computeLiveDurationMs(act);
143
+ if (total > 0 && ms > 1000) {
144
+ const tps = Math.round(total / (ms / 1000));
145
+ if (tps > 0) parts.push(alignMetric(`${formatTokensCompact(tps)}/s`, TPS_METRIC_WIDTH));
146
+ }
147
+ parts.push(alignMetric(`${(ms / 1000).toFixed(1)}s`, DURATION_METRIC_WIDTH));
148
+ } else {
149
+ if (agent.toolUses) parts.push(alignMetric(`${agent.toolUses} tools`, TOOLS_METRIC_WIDTH));
150
+ if (agent.progress?.tokens) parts.push(alignMetric(formatTokensCompact(agent.progress.tokens), TOKENS_METRIC_WIDTH));
151
+ const ageMs = agent.startedAt ? Math.max(0, Date.now() - new Date(agent.startedAt).getTime()) : 0;
152
+ if (agent.progress?.tokens && ageMs > 1000) {
153
+ const tps = Math.round(agent.progress.tokens / (ageMs / 1000));
154
+ if (tps > 0) parts.push(alignMetric(`${formatTokensCompact(tps)}/s`, TPS_METRIC_WIDTH));
155
+ }
156
+ const age = elapsed(agent.completedAt ?? agent.startedAt);
157
+ if (age) parts.push(alignMetric(age, DURATION_METRIC_WIDTH));
158
+ }
159
+ return parts.join(" · ");
160
+ }
161
+
162
+ // ── Notification badge ────────────────────────────────────────────────
163
+
164
+ // Bug 021: the bell glyph 🔔 was misread as "queued messages" — users saw
165
+ // `🔔227` and concluded there were 227 pending items, when the value is a
166
+ // CUMULATIVE warning/error/critical count with zero actual queue behind it.
167
+ // Fix: relabel to an explicit "alerts" segment (no bell) and cap the display
168
+ // at 99+ (standard badge practice). The cumulative count stays accurate
169
+ // internally (widgetState.notificationCount) and remains fully logged in
170
+ // .crew/state/notifications/YYYY-MM-DD.jsonl — this bounds presentation only.
171
+ // Deeper fixes (decay window, owner-scope, auto-reset on all-runs-terminal,
172
+ // full deprecation) are product decisions documented in
173
+ // docs/bugs/bug-021-notification-badge-counter-misleading.md.
174
+ export const NOTIFICATION_BADGE_CAP = 99;
175
+
176
+ export function notificationBadge(count: number | undefined, env: NodeJS.ProcessEnv = process.env): string {
177
+ if (!count || count <= 0) return "";
178
+ const term = `${env.TERM ?? ""} ${env.WT_SESSION ?? ""} ${env.TERM_PROGRAM ?? ""}`.toLowerCase();
179
+ const supportsEmoji = !term.includes("dumb") && env.NO_COLOR !== "1";
180
+ const label = count > NOTIFICATION_BADGE_CAP ? `${NOTIFICATION_BADGE_CAP}+ alerts` : `${count} alerts`;
181
+ return supportsEmoji ? ` · ${label}` : ` [${label}]`;
182
+ }