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,160 @@
1
+ /**
2
+ * Agent Management Overlay — displays discovered agents with their configuration.
3
+ * Read-only view of agent definitions from builtin/user/project sources.
4
+ * Future: enable/disable toggle, model override editing.
5
+ */
6
+ import type { AgentConfig, ResourceSource } from "../agents/agent-config.ts";
7
+ import { truncate } from "../utils/visual.ts";
8
+
9
+ export interface AgentEntry {
10
+ name: string;
11
+ description: string;
12
+ source: ResourceSource;
13
+ model?: string;
14
+ thinking?: string;
15
+ loadMode?: string;
16
+ contextMode?: string;
17
+ disabled?: boolean;
18
+ filePath: string;
19
+ }
20
+
21
+ export function agentToEntry(agent: AgentConfig): AgentEntry {
22
+ return {
23
+ name: agent.name,
24
+ description: agent.description,
25
+ source: agent.source,
26
+ model: agent.model,
27
+ thinking: agent.thinking,
28
+ loadMode: agent.loadMode,
29
+ contextMode: agent.contextMode,
30
+ disabled: agent.disabled,
31
+ filePath: agent.filePath,
32
+ };
33
+ }
34
+
35
+ function sourceIcon(source: ResourceSource): string {
36
+ switch (source) {
37
+ case "builtin":
38
+ return "📦";
39
+ case "user":
40
+ return "👤";
41
+ case "project":
42
+ return "📂";
43
+ case "git":
44
+ return "🔗";
45
+ case "dynamic":
46
+ return "⚡";
47
+ default:
48
+ return "❓";
49
+ }
50
+ }
51
+
52
+ function sourceLabel(source: ResourceSource): string {
53
+ switch (source) {
54
+ case "builtin":
55
+ return "builtin";
56
+ case "user":
57
+ return "user";
58
+ case "project":
59
+ return "project";
60
+ case "git":
61
+ return "git";
62
+ case "dynamic":
63
+ return "dynamic";
64
+ default:
65
+ return "unknown";
66
+ }
67
+ }
68
+
69
+ export interface AgentOverlayState {
70
+ entries: AgentEntry[];
71
+ selectedIndex: number;
72
+ scrollOffset: number;
73
+ expanded: Set<number>;
74
+ maxVisible: number;
75
+ }
76
+
77
+ export function createAgentOverlayState(entries: AgentEntry[], maxVisible = 20): AgentOverlayState {
78
+ return {
79
+ entries: entries.sort((a, b) => {
80
+ const order: Record<ResourceSource, number> = {
81
+ project: 0,
82
+ "project-pi": 1,
83
+ user: 2,
84
+ git: 3,
85
+ builtin: 4,
86
+ dynamic: 5,
87
+ };
88
+ const diff = (order[a.source] ?? 4) - (order[b.source] ?? 4);
89
+ return diff !== 0 ? diff : a.name.localeCompare(b.name);
90
+ }),
91
+ selectedIndex: 0,
92
+ scrollOffset: 0,
93
+ expanded: new Set(),
94
+ maxVisible,
95
+ };
96
+ }
97
+
98
+ export function moveSelection(state: AgentOverlayState, direction: -1 | 1): AgentOverlayState {
99
+ const next = Math.max(0, Math.min(state.entries.length - 1, state.selectedIndex + direction));
100
+ const visibleStart = state.scrollOffset;
101
+ const visibleEnd = state.scrollOffset + state.maxVisible;
102
+ const newScroll = next < visibleStart ? next : next >= visibleEnd ? Math.max(0, next - state.maxVisible + 1) : state.scrollOffset;
103
+ return { ...state, selectedIndex: next, scrollOffset: newScroll };
104
+ }
105
+
106
+ export function toggleExpand(state: AgentOverlayState): AgentOverlayState {
107
+ const expanded = new Set(state.expanded);
108
+ if (expanded.has(state.selectedIndex)) {
109
+ expanded.delete(state.selectedIndex);
110
+ } else {
111
+ expanded.add(state.selectedIndex);
112
+ }
113
+ return { ...state, expanded };
114
+ }
115
+
116
+ export function renderAgentOverlay(state: AgentOverlayState, width: number): string[] {
117
+ const lines: string[] = [];
118
+ const header = ` Agent Configuration (${state.entries.length} agents)`;
119
+ lines.push(truncate(header, width));
120
+ lines.push(truncate("─".repeat(Math.min(width, 60)), width));
121
+
122
+ if (state.entries.length === 0) {
123
+ lines.push(truncate(" No agents discovered.", width));
124
+ return lines;
125
+ }
126
+
127
+ const visible = state.entries.slice(state.scrollOffset, state.scrollOffset + state.maxVisible);
128
+
129
+ for (const [i, entry] of visible.entries()) {
130
+ const globalIndex = state.scrollOffset + i;
131
+ const isSelected = globalIndex === state.selectedIndex;
132
+ const isExpanded = state.expanded.has(globalIndex);
133
+ const cursor = isSelected ? "▸" : " ";
134
+ const disabled = entry.disabled ? " [disabled]" : "";
135
+ const model = entry.model ? ` (${entry.model})` : "";
136
+
137
+ const summary = `${cursor} ${sourceIcon(entry.source)} ${entry.name}${model}${disabled}`;
138
+ lines.push(truncate(summary, width));
139
+
140
+ if (isExpanded) {
141
+ const desc = ` ${entry.description}`;
142
+ lines.push(truncate(desc, width));
143
+ const meta: string[] = [` source: ${sourceLabel(entry.source)}`];
144
+ if (entry.model) meta.push(`model: ${entry.model}`);
145
+ if (entry.thinking) meta.push(`thinking: ${entry.thinking}`);
146
+ if (entry.loadMode) meta.push(`loadMode: ${entry.loadMode}`);
147
+ if (entry.contextMode) meta.push(`context: ${entry.contextMode}`);
148
+ meta.push(`file: ${entry.filePath}`);
149
+ lines.push(truncate(meta.join(" · "), width));
150
+ lines.push(truncate("─".repeat(Math.min(width - 4, 50)), width));
151
+ }
152
+ }
153
+
154
+ if (state.scrollOffset + state.maxVisible < state.entries.length) {
155
+ const remaining = state.entries.length - state.scrollOffset - state.maxVisible;
156
+ lines.push(truncate(` … +${remaining} more`, width));
157
+ }
158
+
159
+ return lines;
160
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Color utilities for pi-crew UI — background tinting and ANSI parsing.
3
+ *
4
+ * Ported from pi-diff's mixBg() / autoDeriveBgFromTheme() technique:
5
+ * derive subtle status-tinted backgrounds from the theme's foreground colors
6
+ * so crew cards stay readable across light/dark themes without hardcoding RGB.
7
+ *
8
+ * mixBg(base, accent, intensity) does a LINEAR interpolation:
9
+ * result = base + (accent - base) * intensity
10
+ *
11
+ * Intensity tiers (per pi-diff research):
12
+ * 10-12% — subtle gutter/hint
13
+ * 15-18% — line background (visible but not loud)
14
+ * 30-35% — word-level emphasis (prominent)
15
+ */
16
+
17
+ import { visibleWidth as visualWidth } from "../utils/visual.ts";
18
+ import type { CrewTheme } from "./theme-adapter.ts";
19
+
20
+ // ── ANSI parsing ────────────────────────────────────────────────────────
21
+
22
+ interface Rgb {
23
+ r: number;
24
+ g: number;
25
+ b: number;
26
+ }
27
+
28
+ const ANSI_RGB_FG_RE = /^\x1b?\[?38;2;(\d+);(\d+);(\d+)m?$/;
29
+ const ANSI_RGB_BG_RE = /^\x1b?\[?48;2;(\d+);(\d+);(\d+)m?$/;
30
+
31
+ /** Parse a truecolor ANSI SGR sequence (e.g. "\x1b[38;2;255;100;50m") to RGB. */
32
+ export function parseAnsiRgb(ansi: string | undefined | null): Rgb | null {
33
+ if (!ansi) return null;
34
+ const stripped = ansi.replace(/^\x1b?\[?/, "").replace(/m?$/, "");
35
+ const fgMatch = stripped.match(/^38;2;(\d+);(\d+);(\d+)$/);
36
+ const bgMatch = stripped.match(/^48;2;(\d+);(\d+);(\d+)$/);
37
+ const m = fgMatch ?? bgMatch;
38
+ if (!m) return null;
39
+ const r = Number(m[1]);
40
+ const g = Number(m[2]);
41
+ const b = Number(m[3]);
42
+ if (![r, g, b].every(Number.isFinite)) return null;
43
+ return { r, g, b };
44
+ }
45
+
46
+ // ── Color mixing ────────────────────────────────────────────────────────
47
+
48
+ /** Linear-interpolate two RGB colors and emit a 48;2 (truecolor bg) SGR code. */
49
+ export function mixBg(base: Rgb, accent: Rgb, intensity: number): string {
50
+ const r = Math.round(base.r + (accent.r - base.r) * intensity);
51
+ const g = Math.round(base.g + (accent.g - base.g) * intensity);
52
+ const b = Math.round(base.b + (accent.b - base.b) * intensity);
53
+ return `\x1b[48;2;${r};${g};${b}m`;
54
+ }
55
+
56
+ const BLACK: Rgb = { r: 0, g: 0, b: 0 };
57
+
58
+ // ── Theme → background derivation ───────────────────────────────────────
59
+
60
+ /**
61
+ * Resolve a subtle status-tinted background for a crew card interior.
62
+ *
63
+ * Reads the theme's fg color for the given status slot (success/error/border),
64
+ * mixes it into a dark base at low intensity (default 8%), and returns a bg SGR
65
+ * sequence. Returns "" if the theme exposes no fg ANSI (graceful — caller
66
+ * renders without background).
67
+ *
68
+ * @param theme pi-crew theme adapter
69
+ * @param statusSlot which status color to tint with ("success" | "error" | "borderAccent" | "border")
70
+ * @param intensity 0-1, how strong the tint is (default 0.08 = very subtle)
71
+ */
72
+ export function deriveCardBackground(
73
+ theme: CrewTheme,
74
+ statusSlot: "success" | "error" | "borderAccent" | "border",
75
+ intensity = 0.08,
76
+ ): string {
77
+ // Prefer the theme's bg color as base (matches the card's surroundings).
78
+ let base = BLACK;
79
+ const themeAny = theme as unknown as {
80
+ getBgAnsi?: (slot: string) => string | undefined;
81
+ getFgAnsi?: (slot: string) => string | undefined;
82
+ };
83
+ if (themeAny.getBgAnsi) {
84
+ try {
85
+ const bg = themeAny.getBgAnsi("background");
86
+ const parsed = parseAnsiRgb(bg);
87
+ if (parsed) base = parsed;
88
+ } catch {
89
+ /* fall back to black */
90
+ }
91
+ }
92
+
93
+ // Get the accent from the theme's fg for the requested slot.
94
+ let accentRgb: Rgb | null = null;
95
+ if (themeAny.getFgAnsi) {
96
+ try {
97
+ accentRgb = parseAnsiRgb(themeAny.getFgAnsi(statusSlot));
98
+ } catch {
99
+ /* accent stays null */
100
+ }
101
+ }
102
+ if (!accentRgb) return ""; // can't derive — render without bg tint
103
+
104
+ return mixBg(base, accentRgb, intensity);
105
+ }
106
+
107
+ // ── Helpers for padding lines with a background ─────────────────────────
108
+
109
+ const RESET = "\x1b[0m";
110
+
111
+ /** Strip ANSI SGR codes then compute the VISUAL width (Unicode-aware).
112
+ * Round 23 (BUG 2): previously this used `.length` (UTF-16 code units), which
113
+ * under-counts CJK/emoji → wrong padding → broken frame borders in crew cards.
114
+ * Delegate to the canonical Unicode-aware visualWidth from utils/visual.ts
115
+ * used by every other renderer. */
116
+ export function visibleWidth(text: string): number {
117
+ return visualWidth(text);
118
+ }
119
+
120
+ /**
121
+ * Pad a line to `targetWidth` and apply `bgAnsi` as the background for the
122
+ * full visible width (including any trailing padding spaces).
123
+ *
124
+ * `bgAnsi` should be a 48;2 (truecolor bg) SGR sequence from mixBg().
125
+ * The function inserts the bg code after any existing leading SGR, fills the
126
+ * content, pads to width, then resets. This ensures the entire row's background
127
+ * is tinted — not just the characters.
128
+ */
129
+ export function padWithBackground(line: string, targetWidth: number, bgAnsi: string): string {
130
+ if (!bgAnsi) {
131
+ // No background — just space-pad (preserving any existing styling).
132
+ const width = visibleWidth(line);
133
+ return width >= targetWidth ? line : line + " ".repeat(targetWidth - width);
134
+ }
135
+ const width = visibleWidth(line);
136
+ const pad = width >= targetWidth ? "" : " ".repeat(targetWidth - width);
137
+ // Re-apply bg after a leading reset if present, otherwise prepend.
138
+ return `${bgAnsi}${line}${pad}${RESET}`;
139
+ }
@@ -0,0 +1,102 @@
1
+ import type { UsageState } from "../state/types.ts";
2
+ import { pad, truncate } from "../utils/visual.ts";
3
+ import type { RunStatus } from "./status-colors.ts";
4
+ import type { CrewTheme } from "./theme-adapter.ts";
5
+
6
+ export interface CrewFooterData {
7
+ pwd: string;
8
+ branch?: string;
9
+ runId?: string;
10
+ status?: RunStatus;
11
+ usage?: UsageState;
12
+ contextWindow?: number;
13
+ contextPercent?: number;
14
+ badges?: string[];
15
+ }
16
+
17
+ function formatCount(value: number | undefined): string {
18
+ if (value === undefined || !Number.isFinite(value)) return "?";
19
+ if (Math.abs(value) >= 1000) return `${(value / 1000).toFixed(Math.abs(value) >= 10_000 ? 0 : 1)}k`;
20
+ return `${value}`;
21
+ }
22
+
23
+ function formatCost(value: number | undefined): string {
24
+ return value === undefined || !Number.isFinite(value) ? "$0.0000" : `$${value.toFixed(4)}`;
25
+ }
26
+
27
+ function displayPwd(pwd: string): string {
28
+ const home = process.env.HOME || process.env.USERPROFILE;
29
+ if (home && pwd.startsWith(home)) return `~${pwd.slice(home.length) || "/"}`;
30
+ return pwd || ".";
31
+ }
32
+
33
+ function contextText(data: CrewFooterData): string {
34
+ const windowText = data.contextWindow && Number.isFinite(data.contextWindow) ? formatCount(data.contextWindow) : "window";
35
+ const percent = data.contextPercent;
36
+ if (percent === undefined || !Number.isFinite(percent)) return `?/${windowText}`;
37
+ return `${percent.toFixed(1)}%/${windowText}`;
38
+ }
39
+
40
+ export class CrewFooter {
41
+ private data: CrewFooterData;
42
+ private readonly theme: CrewTheme;
43
+ private cacheKey = "";
44
+ private cacheWidth = 0;
45
+ private cacheLines: string[] = [];
46
+
47
+ constructor(data: CrewFooterData, theme: CrewTheme) {
48
+ this.data = data;
49
+ this.theme = theme;
50
+ }
51
+
52
+ setData(data: CrewFooterData): void {
53
+ this.data = data;
54
+ this.invalidate();
55
+ }
56
+
57
+ invalidate(): void {
58
+ this.cacheKey = "";
59
+ this.cacheLines = [];
60
+ }
61
+
62
+ render(width: number): string[] {
63
+ const key = JSON.stringify(this.data);
64
+ if (this.cacheKey === key && this.cacheWidth === width && this.cacheLines.length) return this.cacheLines;
65
+ const lineWidth = Math.max(1, width);
66
+ const firstParts = [
67
+ displayPwd(this.data.pwd),
68
+ this.data.branch ? `(${this.data.branch})` : undefined,
69
+ this.data.runId,
70
+ this.data.status,
71
+ ].filter((part): part is string => Boolean(part));
72
+ const usage = this.data.usage;
73
+ const context = contextText(this.data);
74
+ const contextPercent = this.data.contextPercent;
75
+ const contextColor =
76
+ contextPercent !== undefined && Number.isFinite(contextPercent)
77
+ ? contextPercent > 90
78
+ ? "error"
79
+ : contextPercent > 70
80
+ ? "warning"
81
+ : undefined
82
+ : undefined;
83
+ const contextRendered = contextColor ? this.theme.fg(contextColor, context) : context;
84
+ const usageLine = [
85
+ `↑${formatCount(usage?.input)}`,
86
+ `↓${formatCount(usage?.output)}`,
87
+ `R ${formatCount(usage?.cacheRead)} cache`,
88
+ `W ${formatCount(usage?.cacheWrite)} cache`,
89
+ formatCost(usage?.cost),
90
+ contextRendered,
91
+ ].join(" • ");
92
+ const badges = this.data.badges?.length ? this.data.badges.map((badge) => `[${badge}]`).join(" ") : "";
93
+ this.cacheLines = [
94
+ this.theme.fg("dim", pad(truncate(firstParts.join(" • "), lineWidth), lineWidth)),
95
+ this.theme.fg("dim", pad(truncate(usageLine, lineWidth), lineWidth)),
96
+ this.theme.fg("dim", pad(truncate(badges, lineWidth), lineWidth)),
97
+ ];
98
+ this.cacheKey = key;
99
+ this.cacheWidth = width;
100
+ return this.cacheLines;
101
+ }
102
+ }
@@ -0,0 +1,112 @@
1
+ import { pad, truncate } from "../utils/visual.ts";
2
+ import { keyOf } from "./key-utils.ts";
3
+ import type { CrewTheme } from "./theme-adapter.ts";
4
+
5
+ export interface CrewSelectItem<T = string> {
6
+ value: T;
7
+ label: string;
8
+ description?: string;
9
+ }
10
+
11
+ export interface CrewSelectListOptions<T = string> {
12
+ onSelect: (item: CrewSelectItem<T>) => void;
13
+ onCancel: () => void;
14
+ onPreview?: (item: CrewSelectItem<T>) => void;
15
+ maxHeight?: number;
16
+ }
17
+
18
+ export class CrewSelectList<T = string> {
19
+ private readonly items: CrewSelectItem<T>[];
20
+ private readonly theme: CrewTheme;
21
+ private readonly options: CrewSelectListOptions<T>;
22
+ private selectedIndex = 0;
23
+ private scrollOffset = 0;
24
+
25
+ constructor(items: CrewSelectItem<T>[], theme: CrewTheme, options: CrewSelectListOptions<T>) {
26
+ this.items = [...items];
27
+ this.theme = theme;
28
+ this.options = options;
29
+ this.selectedIndex = this.items.length ? 0 : -1;
30
+ }
31
+
32
+ invalidate(): void {}
33
+
34
+ getSelected(): CrewSelectItem<T> | undefined {
35
+ return this.selectedIndex >= 0 ? this.items[this.selectedIndex] : undefined;
36
+ }
37
+
38
+ setSelectedIndex(index: number): void {
39
+ if (!this.items.length) {
40
+ this.selectedIndex = -1;
41
+ this.scrollOffset = 0;
42
+ return;
43
+ }
44
+ const next = Math.min(this.items.length - 1, Math.max(0, index));
45
+ const changed = next !== this.selectedIndex;
46
+ this.selectedIndex = next;
47
+ this.ensureVisible();
48
+ if (changed) {
49
+ const selected = this.getSelected();
50
+ if (selected) this.options.onPreview?.(selected);
51
+ }
52
+ }
53
+
54
+ handleInput(data: string): void {
55
+ if (data === "q" || data === "\u001b") {
56
+ this.options.onCancel();
57
+ return;
58
+ }
59
+ if (data === "j" || keyOf(data) === "down") {
60
+ this.setSelectedIndex(this.selectedIndex + 1);
61
+ return;
62
+ }
63
+ if (data === "k" || keyOf(data) === "up") {
64
+ this.setSelectedIndex(this.selectedIndex - 1);
65
+ return;
66
+ }
67
+ if (data === "\r" || data === "\n") {
68
+ const selected = this.getSelected();
69
+ if (selected) this.options.onSelect(selected);
70
+ }
71
+ }
72
+
73
+ render(width: number): string[] {
74
+ if (!this.items.length) return [this.theme.fg("muted", "(no items)")];
75
+ const maxHeight = Math.max(1, Math.floor(this.options.maxHeight ?? this.items.length));
76
+ this.ensureVisible();
77
+ const hasTop = this.scrollOffset > 0;
78
+ const availableWithoutBottom = Math.max(1, maxHeight - (hasTop ? 1 : 0));
79
+ const hasBottom = this.scrollOffset + availableWithoutBottom < this.items.length;
80
+ const slots = this.visibleItemSlots(maxHeight, hasTop, hasBottom);
81
+ const visibleItems = this.items.slice(this.scrollOffset, this.scrollOffset + slots);
82
+ const lines: string[] = [];
83
+ if (hasTop) lines.push(this.theme.fg("muted", `↑ ${this.scrollOffset} more`));
84
+ for (const [offset, item] of visibleItems.entries()) {
85
+ const index = this.scrollOffset + offset;
86
+ const prefix = index === this.selectedIndex ? " → " : " ";
87
+ const suffix = item.description ? this.theme.fg("dim", ` — ${item.description}`) : "";
88
+ const raw = `${prefix}${item.label}${suffix}`;
89
+ const line = index === this.selectedIndex ? (this.theme.inverse?.(raw) ?? raw) : raw;
90
+ lines.push(pad(truncate(line, width), Math.max(1, width)));
91
+ }
92
+ if (hasBottom) lines.push(this.theme.fg("muted", `↓ ${this.items.length - (this.scrollOffset + slots)} more`));
93
+ return lines.slice(0, maxHeight);
94
+ }
95
+
96
+ private visibleItemSlots(maxHeight: number, hasTop: boolean, hasBottom: boolean): number {
97
+ return Math.max(1, maxHeight - (hasTop ? 1 : 0) - (hasBottom ? 1 : 0));
98
+ }
99
+
100
+ private ensureVisible(): void {
101
+ if (this.selectedIndex < 0) return;
102
+ const maxHeight = Math.max(1, Math.floor(this.options.maxHeight ?? this.items.length));
103
+ const reservedTop = this.scrollOffset > 0 ? 1 : 0;
104
+ const visibleSlots = Math.max(1, maxHeight - reservedTop - 1);
105
+ if (this.selectedIndex < this.scrollOffset) {
106
+ this.scrollOffset = this.selectedIndex;
107
+ } else if (this.selectedIndex >= this.scrollOffset + visibleSlots) {
108
+ this.scrollOffset = Math.max(0, this.selectedIndex - visibleSlots + 1);
109
+ }
110
+ this.scrollOffset = Math.min(this.scrollOffset, Math.max(0, this.items.length - 1));
111
+ }
112
+ }
@@ -0,0 +1,164 @@
1
+ import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
2
+ import { type LiveAgentHandle, listLiveAgents, listLiveAgentsByWorkspace } from "../../runtime/live-agent-manager.ts";
3
+ import { formatCost } from "../../state/usage.ts";
4
+ import { visibleWidth } from "../../utils/visual.ts";
5
+ import { computeLiveDurationMs } from "../live-duration.ts";
6
+ import type { RunDashboardOptions } from "../run-dashboard.ts";
7
+ import type { RunUiSnapshot } from "../snapshot-types.ts";
8
+ import { spinnerFrame } from "../spinner.ts";
9
+ import { iconForStatus } from "../status-colors.ts";
10
+
11
+ /**
12
+ * Fixed visible widths for the per-agent numeric metrics (finding V-1).
13
+ * Right-aligning each field to a constant width stops the right edge of the
14
+ * stats block from jittering every tick as values change width
15
+ * (e.g. `9.9s`→`10.0s`, `950`→`1.0k`, `$0.9`→`$1.0`).
16
+ */
17
+ const TOKENS_METRIC_WIDTH = 5; // `1.2k`, `12.3k`, `123`
18
+ const COST_METRIC_WIDTH = 7; // `$0.0500`, `$1.50`
19
+ const DURATION_METRIC_WIDTH = 6; // `3.0s`, `59.9s`, `120.0s`
20
+
21
+ /**
22
+ * Width-aware right-align (padStart) for a numeric metric so its column stays
23
+ * stable across ticks. Uses `visibleWidth` so wide/CJK glyphs never misalign
24
+ * the padding. Values wider than `width` are returned verbatim (no truncation)
25
+ * so outliers overflow gracefully instead of crashing the render.
26
+ */
27
+ function alignMetric(value: string, width: number): string {
28
+ const gap = width - visibleWidth(value);
29
+ return gap > 0 ? " ".repeat(gap) + value : value;
30
+ }
31
+
32
+ /**
33
+ * Returns true if this agent did real work (LLM call, tool use, or non-trivial duration).
34
+ * Scaffold-only agents (no tokens, no tools, no turns) are skipped in the agents pane —
35
+ * they represent pipeline infrastructure steps, not actual agent execution.
36
+ */
37
+ function isRealAgent(agent: CrewAgentRecord, liveHandle?: LiveAgentHandle): boolean {
38
+ if (agent.runtime === "live-session" || agent.runtime === "child-process") return true;
39
+ // Scaffold agents with real work done are still worth showing
40
+ const tokens = (agent.usage?.input ?? 0) + (agent.usage?.output ?? 0) + (agent.usage?.cacheRead ?? 0) + (agent.usage?.cacheWrite ?? 0);
41
+ if (tokens > 0) return true;
42
+ const turns = (agent.usage as { turns?: number } | undefined)?.turns;
43
+ if (turns != null && turns > 0) return true;
44
+ if ((agent.progress?.toolCount ?? 0) > 0) return true;
45
+ // If it's still running and has been alive for > 30s, it might be real
46
+ if (liveHandle) {
47
+ const ms = Date.now() - liveHandle.activity.startedAtMs;
48
+ if (ms > 30_000) return true;
49
+ }
50
+ return false;
51
+ }
52
+
53
+ const TOOL_LABELS: Record<string, string> = {
54
+ read: "reading",
55
+ bash: "cmd",
56
+ edit: "editing",
57
+ write: "writing",
58
+ grep: "searching",
59
+ find: "finding",
60
+ ls: "listing",
61
+ };
62
+
63
+ function describeActivity(handle: LiveAgentHandle): string {
64
+ const act = handle.activity;
65
+ if (act.activeTools.size > 0) {
66
+ const tools = [...new Set([...act.activeTools.values()])].map((t) => TOOL_LABELS[t] ?? t);
67
+ return tools.join(", ") + "…";
68
+ }
69
+ if (act.responseText?.trim()) {
70
+ const line =
71
+ act.responseText
72
+ .split("\n")
73
+ .find((l) => l.trim())
74
+ ?.trim() ?? "";
75
+ return line.length > 40 ? line.slice(0, 40) + "…" : line;
76
+ }
77
+ return "thinking…";
78
+ }
79
+
80
+ export function renderAgentsPane(snapshot: RunUiSnapshot | undefined, options: RunDashboardOptions = {}): string[] {
81
+ if (!snapshot) return ["(snapshot unavailable)"];
82
+ if (!snapshot.agents.length) return ["(no agents)"];
83
+ // Filter live agents by workspaceId for session isolation
84
+ const allLive = options.workspaceId ? listLiveAgentsByWorkspace(options.workspaceId) : listLiveAgents();
85
+ const liveForRun = allLive.filter((h) => h.runId === snapshot.runId);
86
+ const { completed, total } = snapshot.progress;
87
+
88
+ const lines: string[] = [];
89
+
90
+ const realAgents = snapshot.agents.filter((a) =>
91
+ isRealAgent(
92
+ a,
93
+ liveForRun.find((h) => h.taskId === a.taskId),
94
+ ),
95
+ );
96
+ const lineCount = Math.min(realAgents.length, 12);
97
+ const label =
98
+ realAgents.length !== snapshot.agents.length
99
+ ? `${realAgents.length} real agents (${snapshot.agents.length} total)`
100
+ : `${realAgents.length} agents`;
101
+
102
+ lines.push(`${completed}/${total} tasks · ${label}`);
103
+
104
+ for (const agent of realAgents.slice(0, 12)) {
105
+ const liveHandle = liveForRun.find((h) => h.taskId === agent.taskId);
106
+ const icon = iconForStatus(agent.status, {
107
+ runningGlyph: spinnerFrame(agent.taskId),
108
+ });
109
+ const role = `${agent.role}`;
110
+
111
+ // Compact activity line
112
+ const activity = liveHandle
113
+ ? describeActivity(liveHandle)
114
+ : agent.progress?.currentTool
115
+ ? `${TOOL_LABELS[agent.progress.currentTool] ?? agent.progress.currentTool}…`
116
+ : agent.status === "running"
117
+ ? "thinking…"
118
+ : agent.status === "queued"
119
+ ? "queued"
120
+ : agent.status === "failed"
121
+ ? (agent.error ?? "failed")
122
+ : "done";
123
+
124
+ // Stats: tokens + cost + duration
125
+ const stats: string[] = [];
126
+ const tokenTotal =
127
+ (agent.usage?.input ?? 0) + (agent.usage?.output ?? 0) + (agent.usage?.cacheRead ?? 0) + (agent.usage?.cacheWrite ?? 0);
128
+ if (tokenTotal > 0) {
129
+ const tok = tokenTotal >= 1000 ? `${(tokenTotal / 1000).toFixed(1)}k` : `${tokenTotal}`;
130
+ stats.push(alignMetric(tok, TOKENS_METRIC_WIDTH));
131
+ }
132
+ // Per-agent cost (Round 17 BS-1): the data is already on task.usage.cost;
133
+ // surface it live so the user sees $ burn per agent during a run.
134
+ if (agent.usage?.cost && agent.usage.cost > 0) {
135
+ stats.push(alignMetric(formatCost(agent.usage.cost), COST_METRIC_WIDTH));
136
+ }
137
+ if (liveHandle) {
138
+ // Round 23 (BUG 1): the duration math here was naive —
139
+ // (completedAtMs ?? Date.now()) - startedAtMs
140
+ // which produced a giant NEGATIVE duration whenever startedAtMs was
141
+ // 0/undefined/bad, or a race set completedAtMs < startedAtMs. This
142
+ // fired for EVERY running live agent in the dashboard. Use the shared,
143
+ // validated computeLiveDurationMs (mirrors widget-formatters.ts).
144
+ const ms = computeLiveDurationMs(liveHandle.activity);
145
+ stats.push(alignMetric(`${(ms / 1000).toFixed(1)}s`, DURATION_METRIC_WIDTH));
146
+ if (options.showModel !== false && liveHandle.modelName && liveHandle.modelName !== "default") {
147
+ stats.push(liveHandle.modelName);
148
+ }
149
+ } else if (agent.startedAt) {
150
+ const ms = Date.now() - new Date(agent.startedAt).getTime();
151
+ if (Number.isFinite(ms)) stats.push(alignMetric(`${(ms / 1000).toFixed(1)}s`, DURATION_METRIC_WIDTH));
152
+ }
153
+
154
+ const statsStr = stats.length ? ` · ${stats.join(" ")}` : "";
155
+ lines.push(` ${icon} ${agent.taskId} ${role}${statsStr}`);
156
+ lines.push(` ${activity}`);
157
+ }
158
+
159
+ if (snapshot.agents.length > 12) {
160
+ lines.push(` … +${snapshot.agents.length - 12} more`);
161
+ }
162
+
163
+ return lines;
164
+ }