pi-crew 0.9.16 → 0.9.17

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 (432) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21790 -0
  4. package/dist/index.mjs +83070 -0
  5. package/dist/index.mjs.map +7 -0
  6. package/docs/A +358 -0
  7. package/docs/M-A +357 -0
  8. package/docs/REVIEW-FINDINGS-2026-06 +357 -0
  9. package/docs/Y +357 -0
  10. package/docs/a +357 -0
  11. package/docs/aA +358 -0
  12. package/docs/archive/README.md +91 -0
  13. package/docs/patterns/command-agent-skill.md +1 -1
  14. package/docs/skills/REFERENCE.md +0 -17
  15. package/docs//303/242mmaAAA/303/242 +357 -0
  16. package/index.bundle.ts +25 -0
  17. package/index.ts +95 -4
  18. package/package.json +15 -4
  19. package/skills/iterative-audit/SKILL.md +0 -1
  20. package/skills/widget-rendering/SKILL.md +17 -0
  21. package/src/adapters/claude-adapter.ts +1 -3
  22. package/src/adapters/export-util.ts +16 -16
  23. package/src/adapters/index.ts +5 -5
  24. package/src/agents/agent-config.ts +20 -14
  25. package/src/agents/agent-serializer.ts +1 -1
  26. package/src/agents/discover-agents.ts +83 -39
  27. package/src/benchmark/benchmark-runner.ts +51 -51
  28. package/src/benchmark/feedback-loop.ts +1 -1
  29. package/src/config/config.ts +166 -590
  30. package/src/config/defaults.ts +28 -2
  31. package/src/config/drift-detector.ts +11 -14
  32. package/src/config/markers.ts +201 -203
  33. package/src/config/resilient-parser.ts +14 -6
  34. package/src/config/role-tools.ts +3 -3
  35. package/src/config/suggestions.ts +5 -12
  36. package/src/config/types.ts +9 -25
  37. package/src/errors.ts +151 -157
  38. package/src/extension/action-suggestions.ts +54 -9
  39. package/src/extension/async-notifier.ts +43 -13
  40. package/src/extension/autonomous-policy.ts +77 -37
  41. package/src/extension/command-completions.ts +9 -8
  42. package/src/extension/context-status-injection.ts +14 -12
  43. package/src/extension/crew-autocomplete.ts +8 -16
  44. package/src/extension/crew-cleanup.ts +2 -1
  45. package/src/extension/crew-shortcuts.ts +9 -6
  46. package/src/extension/cross-extension-rpc.ts +136 -42
  47. package/src/extension/help.ts +1 -1
  48. package/src/extension/import-index.ts +10 -5
  49. package/src/extension/knowledge-injection.ts +88 -15
  50. package/src/extension/management.ts +89 -349
  51. package/src/extension/message-renderers.ts +3 -4
  52. package/src/extension/notification-router.ts +22 -5
  53. package/src/extension/notification-sink.ts +6 -3
  54. package/src/extension/pi-api.ts +17 -8
  55. package/src/extension/plan-orchestrate.ts +8 -27
  56. package/src/extension/project-init.ts +21 -6
  57. package/src/extension/register.ts +329 -975
  58. package/src/extension/registration/artifact-cleanup.ts +9 -4
  59. package/src/extension/registration/command-utils.ts +5 -1
  60. package/src/extension/registration/commands.ts +806 -314
  61. package/src/extension/registration/compaction-guard.ts +15 -15
  62. package/src/extension/registration/lifecycle.ts +259 -0
  63. package/src/extension/registration/observability.ts +293 -0
  64. package/src/extension/registration/subagent-helpers.ts +29 -7
  65. package/src/extension/registration/subagent-tools.ts +253 -44
  66. package/src/extension/registration/team-tool.ts +19 -84
  67. package/src/extension/registration/ui.ts +168 -0
  68. package/src/extension/registration/viewers.ts +64 -24
  69. package/src/extension/result-watcher.ts +18 -7
  70. package/src/extension/run-bundle-schema.ts +21 -6
  71. package/src/extension/run-export.ts +15 -6
  72. package/src/extension/run-import.ts +64 -29
  73. package/src/extension/run-index.ts +28 -9
  74. package/src/extension/run-maintenance.ts +32 -12
  75. package/src/extension/session-summary.ts +1 -1
  76. package/src/extension/team-manager-command.ts +64 -7
  77. package/src/extension/team-onboard.ts +151 -150
  78. package/src/extension/team-recommendation.ts +121 -21
  79. package/src/extension/team-tool/anchor.ts +31 -64
  80. package/src/extension/team-tool/api.ts +929 -141
  81. package/src/extension/team-tool/auto-summarize.ts +14 -26
  82. package/src/extension/team-tool/cache-control.ts +1 -5
  83. package/src/extension/team-tool/cancel.ts +138 -38
  84. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  85. package/src/extension/team-tool/chain-executor.ts +29 -51
  86. package/src/extension/team-tool/config-patch.ts +1 -1
  87. package/src/extension/team-tool/context.ts +40 -20
  88. package/src/extension/team-tool/destructive-gate.ts +10 -5
  89. package/src/extension/team-tool/doctor.ts +170 -51
  90. package/src/extension/team-tool/explain.ts +228 -214
  91. package/src/extension/team-tool/failure-patterns.ts +3 -9
  92. package/src/extension/team-tool/goal-wrap.ts +71 -29
  93. package/src/extension/team-tool/goal.ts +185 -35
  94. package/src/extension/team-tool/handle-schedule.ts +34 -15
  95. package/src/extension/team-tool/handle-settings.ts +94 -56
  96. package/src/extension/team-tool/health-monitor.ts +27 -108
  97. package/src/extension/team-tool/inspect.ts +42 -9
  98. package/src/extension/team-tool/intent-policy.ts +4 -1
  99. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  100. package/src/extension/team-tool/orchestrate.ts +7 -18
  101. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  102. package/src/extension/team-tool/plan.ts +29 -5
  103. package/src/extension/team-tool/respond.ts +49 -16
  104. package/src/extension/team-tool/run-not-found.ts +3 -8
  105. package/src/extension/team-tool/run.ts +80 -317
  106. package/src/extension/team-tool/status.ts +135 -43
  107. package/src/extension/team-tool/workflow-manage.ts +92 -25
  108. package/src/extension/team-tool-types.ts +8 -1
  109. package/src/extension/team-tool.ts +142 -551
  110. package/src/extension/validate-resources.ts +39 -8
  111. package/src/hooks/registry.ts +66 -17
  112. package/src/hooks/types.ts +1 -1
  113. package/src/i18n.ts +25 -11
  114. package/src/observability/correlation.ts +17 -3
  115. package/src/observability/event-bus.ts +51 -56
  116. package/src/observability/event-to-metric.ts +117 -15
  117. package/src/observability/exporters/otlp-exporter.ts +56 -17
  118. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  119. package/src/observability/metric-registry.ts +16 -4
  120. package/src/observability/metric-retention.ts +8 -2
  121. package/src/observability/metric-sink.ts +11 -3
  122. package/src/observability/metrics-primitives.ts +42 -9
  123. package/src/plugins/plugin-define.ts +2 -2
  124. package/src/plugins/plugin-registry.ts +25 -25
  125. package/src/plugins/plugins/index.ts +1 -1
  126. package/src/plugins/plugins/nextjs.ts +16 -16
  127. package/src/plugins/plugins/vite.ts +7 -11
  128. package/src/plugins/plugins/vitest.ts +6 -11
  129. package/src/runtime/adaptive-plan.ts +225 -40
  130. package/src/runtime/agent-control.ts +58 -15
  131. package/src/runtime/agent-memory.ts +15 -9
  132. package/src/runtime/agent-observability.ts +10 -2
  133. package/src/runtime/anchor-manager.ts +27 -25
  134. package/src/runtime/async-marker.ts +7 -1
  135. package/src/runtime/async-runner.ts +27 -23
  136. package/src/runtime/auto-summarize.ts +7 -13
  137. package/src/runtime/background-runner.ts +189 -251
  138. package/src/runtime/batch-barrier.ts +18 -16
  139. package/src/runtime/cancellation-token.ts +16 -6
  140. package/src/runtime/cancellation.ts +46 -8
  141. package/src/runtime/capability-inventory.ts +6 -4
  142. package/src/runtime/chain-parser.ts +44 -11
  143. package/src/runtime/chain-runner.ts +63 -69
  144. package/src/runtime/checkpoint.ts +12 -56
  145. package/src/runtime/child-pi.ts +343 -78
  146. package/src/runtime/coalesce-tasks.ts +268 -0
  147. package/src/runtime/code-summary.ts +71 -26
  148. package/src/runtime/compact-stages/index.ts +15 -5
  149. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  150. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  151. package/src/runtime/compaction-summary.ts +17 -10
  152. package/src/runtime/completion-guard.ts +33 -16
  153. package/src/runtime/crash-classification.ts +28 -7
  154. package/src/runtime/crash-recovery.ts +177 -37
  155. package/src/runtime/crew-agent-records.ts +145 -47
  156. package/src/runtime/crew-hooks.ts +9 -24
  157. package/src/runtime/cross-extension-rpc.ts +25 -23
  158. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  159. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  160. package/src/runtime/delivery-coordinator.ts +34 -7
  161. package/src/runtime/delta-conflict.ts +9 -21
  162. package/src/runtime/deterministic-ast.ts +1 -1
  163. package/src/runtime/diagnostic-export.ts +53 -20
  164. package/src/runtime/direct-run.ts +12 -2
  165. package/src/runtime/dwf-state-store.ts +1 -1
  166. package/src/runtime/dynamic-workflow-context.ts +186 -58
  167. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  168. package/src/runtime/effectiveness.ts +16 -8
  169. package/src/runtime/errors/crew-errors.ts +7 -11
  170. package/src/runtime/event-stream-bridge.ts +9 -3
  171. package/src/runtime/foreground-control.ts +54 -14
  172. package/src/runtime/foreground-watchdog.ts +9 -6
  173. package/src/runtime/goal-achievement.ts +27 -10
  174. package/src/runtime/goal-evaluator.ts +54 -19
  175. package/src/runtime/goal-loop-runner.ts +280 -99
  176. package/src/runtime/goal-state-store.ts +27 -17
  177. package/src/runtime/green-contract.ts +11 -2
  178. package/src/runtime/group-join.ts +64 -31
  179. package/src/runtime/handoff-manager.ts +37 -42
  180. package/src/runtime/heartbeat-gradient.ts +10 -2
  181. package/src/runtime/heartbeat-watcher.ts +32 -6
  182. package/src/runtime/hidden-handoff.ts +12 -30
  183. package/src/runtime/important-line-classifier.ts +12 -2
  184. package/src/runtime/iteration-hooks.ts +25 -15
  185. package/src/runtime/live-agent-control.ts +55 -7
  186. package/src/runtime/live-agent-manager.ts +130 -27
  187. package/src/runtime/live-control-realtime.ts +23 -3
  188. package/src/runtime/live-irc.ts +4 -1
  189. package/src/runtime/live-session-health.ts +12 -4
  190. package/src/runtime/live-session-runtime.ts +379 -117
  191. package/src/runtime/manifest-cache.ts +28 -12
  192. package/src/runtime/mcp-proxy.ts +8 -16
  193. package/src/runtime/metric-parser.ts +1 -5
  194. package/src/runtime/model-fallback.ts +55 -18
  195. package/src/runtime/model-resolver.ts +2 -6
  196. package/src/runtime/model-scope.ts +19 -3
  197. package/src/runtime/notebook-helpers.ts +60 -62
  198. package/src/runtime/orphan-worker-registry.ts +37 -26
  199. package/src/runtime/output-validator.ts +18 -3
  200. package/src/runtime/overflow-recovery.ts +5 -4
  201. package/src/runtime/parallel-research.ts +29 -5
  202. package/src/runtime/parallel-utils.ts +9 -11
  203. package/src/runtime/path-overlap.ts +150 -0
  204. package/src/runtime/peer-dep.ts +12 -17
  205. package/src/runtime/per-write-validator.ts +1 -3
  206. package/src/runtime/phase-tracker.ts +342 -330
  207. package/src/runtime/pi-args.ts +26 -8
  208. package/src/runtime/pi-json-output.ts +1 -1
  209. package/src/runtime/pi-spawn.ts +30 -19
  210. package/src/runtime/pipeline-runner.ts +56 -55
  211. package/src/runtime/plan-templates.ts +5 -6
  212. package/src/runtime/policy-engine.ts +43 -7
  213. package/src/runtime/post-checks.ts +12 -4
  214. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  215. package/src/runtime/process-lifecycle.ts +20 -10
  216. package/src/runtime/process-status.ts +16 -5
  217. package/src/runtime/progress-event-coalescer.ts +2 -1
  218. package/src/runtime/progress-tracker.ts +103 -103
  219. package/src/runtime/prose-compressor.ts +9 -11
  220. package/src/runtime/recovery-recipes.ts +118 -24
  221. package/src/runtime/replace.ts +25 -10
  222. package/src/runtime/resilient-edit.ts +10 -25
  223. package/src/runtime/result-extractor.ts +1 -2
  224. package/src/runtime/retry-executor.ts +20 -4
  225. package/src/runtime/retry-runner.ts +26 -50
  226. package/src/runtime/role-permission.ts +6 -1
  227. package/src/runtime/run-coalesced-task-group.ts +256 -0
  228. package/src/runtime/run-drift.ts +14 -15
  229. package/src/runtime/run-tracker.ts +6 -28
  230. package/src/runtime/runtime-policy.ts +1 -6
  231. package/src/runtime/runtime-resolver.ts +80 -15
  232. package/src/runtime/scheduler.ts +47 -18
  233. package/src/runtime/semaphore.ts +5 -7
  234. package/src/runtime/sensitive-paths.ts +2 -1
  235. package/src/runtime/session-usage.ts +1 -1
  236. package/src/runtime/settings-store.ts +16 -12
  237. package/src/runtime/sidechain-output.ts +6 -2
  238. package/src/runtime/single-agent-compose.ts +1 -4
  239. package/src/runtime/skill-effectiveness.ts +29 -97
  240. package/src/runtime/skill-instructions.ts +40 -83
  241. package/src/runtime/stale-reconciler.ts +73 -197
  242. package/src/runtime/stream-preview.ts +1 -1
  243. package/src/runtime/streaming-output.ts +1 -1
  244. package/src/runtime/subagent-manager.ts +44 -169
  245. package/src/runtime/subprocess-tool-registry.ts +4 -1
  246. package/src/runtime/supervisor-contact.ts +10 -5
  247. package/src/runtime/task-display.ts +19 -5
  248. package/src/runtime/task-graph-scheduler.ts +95 -21
  249. package/src/runtime/task-graph.ts +5 -11
  250. package/src/runtime/task-health.ts +54 -47
  251. package/src/runtime/task-id.ts +11 -18
  252. package/src/runtime/task-output-context.ts +265 -81
  253. package/src/runtime/task-packet.ts +12 -20
  254. package/src/runtime/task-quality.ts +6 -14
  255. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  256. package/src/runtime/task-runner/live-executor.ts +114 -36
  257. package/src/runtime/task-runner/output-splitter.ts +152 -0
  258. package/src/runtime/task-runner/progress.ts +50 -12
  259. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  260. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  261. package/src/runtime/task-runner/result-utils.ts +3 -1
  262. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  263. package/src/runtime/task-runner/run-projection.ts +27 -8
  264. package/src/runtime/task-runner/state-helpers.ts +29 -8
  265. package/src/runtime/task-runner/tail-read.ts +2 -6
  266. package/src/runtime/task-runner.ts +180 -326
  267. package/src/runtime/team-runner-artifacts.ts +13 -0
  268. package/src/runtime/team-runner.ts +456 -974
  269. package/src/runtime/tool-output-pruner.ts +6 -9
  270. package/src/runtime/tool-progress.ts +11 -21
  271. package/src/runtime/verification-gates.ts +33 -24
  272. package/src/runtime/verification-integrity.ts +1 -4
  273. package/src/runtime/verification-worktree.ts +62 -12
  274. package/src/runtime/worker-heartbeat.ts +5 -1
  275. package/src/runtime/worker-startup.ts +29 -5
  276. package/src/runtime/workflow-state.ts +17 -9
  277. package/src/runtime/workspace-lock.ts +30 -35
  278. package/src/runtime/workspace-tree.ts +20 -32
  279. package/src/runtime/yield-handler.ts +43 -9
  280. package/src/runtime/zombie-scanner.ts +5 -4
  281. package/src/schema/config-schema.ts +266 -172
  282. package/src/schema/team-tool-schema.ts +29 -74
  283. package/src/schema/validation-types.ts +25 -17
  284. package/src/skills/discover-skills.ts +35 -10
  285. package/src/skills/skill-templates.ts +109 -27
  286. package/src/skills/validate.ts +26 -8
  287. package/src/state/active-run-registry.ts +87 -28
  288. package/src/state/artifact-store.ts +9 -5
  289. package/src/state/atomic-write-v2.ts +85 -63
  290. package/src/state/atomic-write.ts +105 -26
  291. package/src/state/blob-store.ts +47 -20
  292. package/src/state/contracts.ts +20 -5
  293. package/src/state/crew-init.ts +5 -22
  294. package/src/state/decision-ledger.ts +19 -73
  295. package/src/state/event-log-rotation.ts +41 -15
  296. package/src/state/event-log.ts +168 -53
  297. package/src/state/event-reconstructor.ts +11 -1
  298. package/src/state/gitignore-manager.ts +2 -8
  299. package/src/state/health-store.ts +57 -57
  300. package/src/state/hook-instinct-bridge.ts +1 -1
  301. package/src/state/hook-integrations.ts +1 -1
  302. package/src/state/instinct-store.ts +17 -5
  303. package/src/state/jsonl-writer.ts +1 -1
  304. package/src/state/locks.ts +23 -9
  305. package/src/state/mailbox.ts +151 -28
  306. package/src/state/observation-store.ts +20 -14
  307. package/src/state/run-cache.ts +142 -135
  308. package/src/state/run-graph.ts +6 -15
  309. package/src/state/run-metrics.ts +5 -18
  310. package/src/state/schedule.ts +15 -9
  311. package/src/state/state-store.ts +129 -45
  312. package/src/state/task-claims.ts +12 -2
  313. package/src/state/tiered-eval.ts +52 -43
  314. package/src/state/types-eval.ts +2 -2
  315. package/src/state/types.ts +24 -20
  316. package/src/state/usage.ts +20 -4
  317. package/src/state/worker-atomic-writer.ts +6 -3
  318. package/src/subagents/index.ts +2 -2
  319. package/src/teams/discover-teams.ts +21 -6
  320. package/src/tools/safe-bash-extension.ts +5 -6
  321. package/src/tools/safe-bash.ts +10 -11
  322. package/src/types/new-api-types.ts +6 -10
  323. package/src/ui/agent-management-overlay.ts +52 -40
  324. package/src/ui/card-colors.ts +13 -4
  325. package/src/ui/crew-footer.ts +8 -7
  326. package/src/ui/crew-select-list.ts +1 -1
  327. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  328. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  329. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  330. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  331. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  332. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  333. package/src/ui/heartbeat-aggregator.ts +15 -4
  334. package/src/ui/keybinding-map.ts +40 -7
  335. package/src/ui/live-conversation-overlay.ts +36 -20
  336. package/src/ui/live-duration.ts +1 -4
  337. package/src/ui/live-run-sidebar.ts +88 -25
  338. package/src/ui/loaders.ts +2 -8
  339. package/src/ui/mascot.ts +20 -36
  340. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  341. package/src/ui/overlays/confirm-overlay.ts +4 -2
  342. package/src/ui/overlays/help-overlay.ts +25 -14
  343. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  344. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  345. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  346. package/src/ui/pi-ui-compat.ts +7 -7
  347. package/src/ui/powerbar-publisher.ts +120 -42
  348. package/src/ui/render-diff.ts +10 -3
  349. package/src/ui/render-scheduler.ts +12 -4
  350. package/src/ui/run-action-dispatcher.ts +81 -18
  351. package/src/ui/run-dashboard.ts +172 -76
  352. package/src/ui/run-event-bus.ts +59 -37
  353. package/src/ui/run-snapshot-cache.ts +276 -86
  354. package/src/ui/settings-overlay.ts +361 -74
  355. package/src/ui/status-colors.ts +12 -1
  356. package/src/ui/syntax-highlight.ts +1 -1
  357. package/src/ui/theme-adapter.ts +16 -14
  358. package/src/ui/theme-discovery.ts +13 -2
  359. package/src/ui/tool-progress-formatter.ts +7 -7
  360. package/src/ui/tool-render.ts +128 -56
  361. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  362. package/src/ui/tool-renderers/index.ts +109 -43
  363. package/src/ui/transcript-cache.ts +32 -6
  364. package/src/ui/transcript-entries.ts +25 -23
  365. package/src/ui/transcript-viewer.ts +78 -29
  366. package/src/ui/widget/index.ts +99 -40
  367. package/src/ui/widget/widget-formatters.ts +13 -6
  368. package/src/ui/widget/widget-model.ts +19 -8
  369. package/src/ui/widget/widget-renderer.ts +23 -13
  370. package/src/ui/widget/widget-types.ts +1 -1
  371. package/src/utils/bm25-search.ts +199 -199
  372. package/src/utils/conflict-detect.ts +22 -21
  373. package/src/utils/env-filter.ts +14 -7
  374. package/src/utils/file-coalescer.ts +5 -1
  375. package/src/utils/fingerprint.ts +3 -6
  376. package/src/utils/frontmatter.ts +3 -1
  377. package/src/utils/fs-watch.ts +2 -6
  378. package/src/utils/gh-protocol.ts +119 -44
  379. package/src/utils/git.ts +13 -15
  380. package/src/utils/guards.ts +2 -5
  381. package/src/utils/ids.ts +9 -2
  382. package/src/utils/incremental-reader.ts +14 -3
  383. package/src/utils/internal-error.ts +2 -1
  384. package/src/utils/names.ts +12 -3
  385. package/src/utils/paths.ts +49 -5
  386. package/src/utils/project-detector.ts +2 -2
  387. package/src/utils/redaction.ts +29 -19
  388. package/src/utils/resolve-shell.ts +9 -7
  389. package/src/utils/run-watcher-registry.ts +19 -31
  390. package/src/utils/safe-paths.ts +45 -24
  391. package/src/utils/scan-cache.ts +9 -2
  392. package/src/utils/session-utils.ts +2 -4
  393. package/src/utils/sleep.ts +12 -5
  394. package/src/utils/sse-parser.ts +5 -17
  395. package/src/utils/visual.ts +52 -32
  396. package/src/workflows/discover-workflows.ts +41 -109
  397. package/src/workflows/intermediate-store.ts +5 -21
  398. package/src/workflows/preflight-validator.ts +6 -33
  399. package/src/workflows/topology-analyzer.ts +5 -21
  400. package/src/workflows/workflow-config.ts +7 -6
  401. package/src/worktree/branch-freshness.ts +66 -8
  402. package/src/worktree/cleanup.ts +130 -20
  403. package/src/worktree/worktree-manager.ts +212 -69
  404. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  405. package/skills/detection-pipeline-design/SKILL.md +0 -286
  406. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  407. package/skills/incident-playbook-construction/SKILL.md +0 -384
  408. package/skills/security-review/SKILL.md +0 -561
  409. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  410. package/skills/ui-render-performance/SKILL.md +0 -58
  411. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  412. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  413. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  414. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  415. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  416. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  417. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  418. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  419. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  420. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  421. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  422. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  430. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  431. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  432. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -6,13 +6,12 @@
6
6
  * Uses visibleWidth() for ANSI-aware padding so borders align correctly.
7
7
  */
8
8
 
9
- import { Container, Text } from "@earendil-works/pi-tui";
10
- import { visibleWidth } from "@earendil-works/pi-tui";
11
- import type { CrewTheme } from "../theme-adapter.ts";
12
- import { truncLine, formatTokens, formatDuration } from "../tool-render.ts";
9
+ import { Container, Text, visibleWidth } from "@earendil-works/pi-tui";
13
10
  import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
14
- import { isBrief, briefToolResult } from "./brief-mode.ts";
15
11
  import { truncateToWidth } from "../../utils/visual.ts";
12
+ import type { CrewTheme } from "../theme-adapter.ts";
13
+ import { formatDuration, formatTokens, truncLine } from "../tool-render.ts";
14
+ import { briefToolResult, isBrief } from "./brief-mode.ts";
16
15
 
17
16
  // ── Types ──────────────────────────────────────────────────────────────
18
17
 
@@ -55,22 +54,30 @@ function truncVisual(str: string, maxWidth: number): string {
55
54
  /** Status icon with color */
56
55
  function statusBadge(status: string, theme: CrewTheme): string {
57
56
  switch (status) {
58
- case "completed": return theme.fg("success", "●");
57
+ case "completed":
58
+ return theme.fg("success", "●");
59
59
  case "failed":
60
- case "cancelled": return theme.fg("error", "✖");
61
- case "running": return theme.fg("warning", "");
62
- default: return theme.fg("dim", "○");
60
+ case "cancelled":
61
+ return theme.fg("error", "");
62
+ case "running":
63
+ return theme.fg("warning", "◉");
64
+ default:
65
+ return theme.fg("dim", "○");
63
66
  }
64
67
  }
65
68
 
66
69
  /** Status icon — compact */
67
70
  export function statusIcon(status: string, theme: CrewTheme): string {
68
71
  switch (status) {
69
- case "completed": return theme.fg("success", "✓");
72
+ case "completed":
73
+ return theme.fg("success", "✓");
70
74
  case "failed":
71
- case "cancelled": return theme.fg("error", "✗");
72
- case "running": return theme.fg("warning", "");
73
- default: return theme.fg("dim", "○");
75
+ case "cancelled":
76
+ return theme.fg("error", "");
77
+ case "running":
78
+ return theme.fg("warning", "⟳");
79
+ default:
80
+ return theme.fg("dim", "○");
74
81
  }
75
82
  }
76
83
 
@@ -82,10 +89,13 @@ function shortId(id: string | undefined): string {
82
89
  /** Border color based on status */
83
90
  function borderColorForStatus(status: string): "success" | "error" | "border" {
84
91
  switch (status) {
85
- case "completed": return "success";
92
+ case "completed":
93
+ return "success";
86
94
  case "failed":
87
- case "cancelled": return "error";
88
- default: return "border";
95
+ case "cancelled":
96
+ return "error";
97
+ default:
98
+ return "border";
89
99
  }
90
100
  }
91
101
 
@@ -113,9 +123,15 @@ import { deriveCardBackground, padWithBackground } from "../card-colors.ts";
113
123
  * When bgSlot is provided, the interior is filled with a subtle status-tinted
114
124
  * background derived from the theme (mixBg at 8% intensity).
115
125
  */
116
- function buildFrame(contentLines: string[], totalWidth: number, theme: CrewTheme, borderSlot: "success" | "error" | "border" | "borderAccent" = "border", bgSlot?: "success" | "error" | "border" | "borderAccent"): string {
126
+ function buildFrame(
127
+ contentLines: string[],
128
+ totalWidth: number,
129
+ theme: CrewTheme,
130
+ borderSlot: "success" | "error" | "border" | "borderAccent" = "border",
131
+ bgSlot?: "success" | "error" | "border" | "borderAccent",
132
+ ): string {
117
133
  const frameW = totalWidth - 2; // available after Box(1,1) padding
118
- const innerW = frameW - 2; // │ chars
134
+ const innerW = frameW - 2; // │ chars
119
135
  const top = theme.fg(borderSlot, `╭${"─".repeat(innerW)}╮`);
120
136
  const bottom = theme.fg(borderSlot, `╰${"─".repeat(innerW)}╯`);
121
137
  const v = theme.fg(borderSlot, "│");
@@ -132,7 +148,13 @@ function buildFrame(contentLines: string[], totalWidth: number, theme: CrewTheme
132
148
 
133
149
  /** Build frame TOP: top border + content lines, NO bottom border.
134
150
  * Pairs with buildFrameBottom() so renderCall + renderResult merge into ONE frame. */
135
- function buildFrameTop(contentLines: string[], totalWidth: number, theme: CrewTheme, borderSlot: "success" | "error" | "border" | "borderAccent" = "border", bgSlot?: "success" | "error" | "border" | "borderAccent"): string {
151
+ function buildFrameTop(
152
+ contentLines: string[],
153
+ totalWidth: number,
154
+ theme: CrewTheme,
155
+ borderSlot: "success" | "error" | "border" | "borderAccent" = "border",
156
+ bgSlot?: "success" | "error" | "border" | "borderAccent",
157
+ ): string {
136
158
  const frameW = totalWidth - 2;
137
159
  const innerW = frameW - 2;
138
160
  const top = theme.fg(borderSlot, `╭${"─".repeat(innerW)}╮`);
@@ -148,7 +170,13 @@ function buildFrameTop(contentLines: string[], totalWidth: number, theme: CrewTh
148
170
 
149
171
  /** Build frame BOTTOM: content lines + bottom border, NO top border.
150
172
  * Pairs with buildFrameTop() so renderCall + renderResult merge into ONE frame. */
151
- function buildFrameBottom(contentLines: string[], totalWidth: number, theme: CrewTheme, borderSlot: "success" | "error" | "border" | "borderAccent" = "border", bgSlot?: "success" | "error" | "border" | "borderAccent"): string {
173
+ function buildFrameBottom(
174
+ contentLines: string[],
175
+ totalWidth: number,
176
+ theme: CrewTheme,
177
+ borderSlot: "success" | "error" | "border" | "borderAccent" = "border",
178
+ bgSlot?: "success" | "error" | "border" | "borderAccent",
179
+ ): string {
152
180
  const frameW = totalWidth - 2;
153
181
  const innerW = frameW - 2;
154
182
  const v = theme.fg(borderSlot, "│");
@@ -174,10 +202,10 @@ function borderFromContext(ctx: ToolRenderContext): "success" | "error" | "borde
174
202
 
175
203
  export const teamToolRenderer: ToolRenderer = {
176
204
  renderCall(args, theme, ctx) {
177
- const action = args.action as string ?? "";
178
- const goal = args.goal as string ?? "";
205
+ const action = (args.action as string) ?? "";
206
+ const goal = (args.goal as string) ?? "";
179
207
  const team = args.team as string | undefined;
180
- const w = (ctx.width || process.stdout.columns || 116);
208
+ const w = ctx.width || process.stdout.columns || 116;
181
209
  const innerW = w - 4;
182
210
 
183
211
  const contentLines: string[] = [];
@@ -215,7 +243,7 @@ function renderTeamResult(result: Record<string, unknown>, options: unknown, the
215
243
  const action = typeof d.action === "string" ? d.action : "";
216
244
  const status = typeof d.status === "string" ? d.status : "";
217
245
  const runId = typeof d.runId === "string" ? d.runId : "";
218
- const w = (ctx.width || process.stdout.columns || 116);
246
+ const w = ctx.width || process.stdout.columns || 116;
219
247
  const innerW = w - 4;
220
248
  const bColor = borderColorForStatus(status);
221
249
  const contentLines: string[] = [];
@@ -238,7 +266,9 @@ function renderTeamResult(result: Record<string, unknown>, options: unknown, the
238
266
  const barW = Math.min(innerW - 22, 30);
239
267
  const bar = progressBar(ratio, barW, theme);
240
268
  const count = theme.fg("muted", ` ${parsed.completed}/${parsed.total}`);
241
- contentLines.push(padVisual(` ${spinner} ${theme.fg("toolTitle", theme.bold("crew run"))} ${theme.fg("dim", elapsed)}`, innerW));
269
+ contentLines.push(
270
+ padVisual(` ${spinner} ${theme.fg("toolTitle", theme.bold("crew run"))} ${theme.fg("dim", elapsed)}`, innerW),
271
+ );
242
272
  contentLines.push(padVisual(` ${bar}${count}`, innerW));
243
273
  if (parsed.activeAgent) contentLines.push(padVisual(` ${theme.fg("dim", parsed.activeAgent)}`, innerW));
244
274
  } else {
@@ -300,9 +330,9 @@ function renderTeamResult(result: Record<string, unknown>, options: unknown, the
300
330
 
301
331
  export const agentToolRenderer: ToolRenderer = {
302
332
  renderCall(args, theme, ctx) {
303
- const agentName = args.agent as string ?? args.subagent_type as string ?? "";
333
+ const agentName = (args.agent as string) ?? (args.subagent_type as string) ?? "";
304
334
  const prompt = (args.prompt ?? args.task ?? "") as string;
305
- const w = (ctx.width || process.stdout.columns || 116);
335
+ const w = ctx.width || process.stdout.columns || 116;
306
336
  const innerW = w - 4;
307
337
 
308
338
  const contentLines: string[] = [];
@@ -333,7 +363,7 @@ export const agentToolRenderer: ToolRenderer = {
333
363
  function renderAgentResult(result: Record<string, unknown>, options: unknown, theme: CrewTheme, ctx: ToolRenderContext): string {
334
364
  const d = (result.details ?? result) as Record<string, unknown>;
335
365
  const results = d.results as Array<Record<string, unknown>> | undefined;
336
- const w = (ctx.width || process.stdout.columns || 116);
366
+ const w = ctx.width || process.stdout.columns || 116;
337
367
  const innerW = w - 4;
338
368
  const status = ((d.status ?? (results?.[0] as Record<string, unknown>)?.status ?? "") as string) || "completed";
339
369
  const bColor: "success" | "error" | "border" = status === "completed" ? "success" : status === "failed" ? "error" : "border";
@@ -359,7 +389,7 @@ function renderAgentResult(result: Record<string, unknown>, options: unknown, th
359
389
  if (!ctx.expanded) {
360
390
  // Collapsed: compact card
361
391
  const badge = statusBadge(status, theme);
362
- const agentId = (d.agentId as string) ?? (results?.[0] as Record<string, unknown>)?.agentId as string ?? "agent";
392
+ const agentId = (d.agentId as string) ?? ((results?.[0] as Record<string, unknown>)?.agentId as string) ?? "agent";
363
393
  const nameTag = theme.fg("toolTitle", theme.bold(agentId));
364
394
  contentLines.push(padVisual(` ${badge} ${nameTag}`, innerW));
365
395
 
@@ -378,7 +408,7 @@ function renderAgentResult(result: Record<string, unknown>, options: unknown, th
378
408
  if (results?.length) {
379
409
  for (let i = 0; i < results.length; i++) {
380
410
  const item = results[i]!;
381
- const icon = statusIcon(item.status as string ?? "", theme);
411
+ const icon = statusIcon((item.status as string) ?? "", theme);
382
412
  const label = theme.fg("toolTitle", theme.bold((item.agentId as string) ?? "agent"));
383
413
  contentLines.push(padVisual(` ${icon} ${label}`, innerW));
384
414
 
@@ -396,7 +426,7 @@ function renderAgentResult(result: Record<string, unknown>, options: unknown, th
396
426
  }
397
427
  }
398
428
  } else if (d.agentId) {
399
- const icon = statusIcon(d.status as string ?? "", theme);
429
+ const icon = statusIcon((d.status as string) ?? "", theme);
400
430
  contentLines.push(padVisual(` ${icon} ${theme.fg("toolTitle", theme.bold(d.agentId as string))}`, innerW));
401
431
  if (d.error) {
402
432
  contentLines.push(padVisual(` ${theme.fg("error", truncLine(String(d.error), innerW - 6))}`, innerW));
@@ -473,7 +503,15 @@ function appendSimpleCard(lines: string[], status: string, runId: string, theme:
473
503
  lines.push(padVisual(` ${badge} ${theme.fg("text", parts.join(" · ") || "done")}`, innerW));
474
504
  }
475
505
 
476
- function appendExpandedRun(lines: string[], records: CrewAgentRecord[], status: string, runId: string, theme: CrewTheme, w: number, innerW: number): void {
506
+ function appendExpandedRun(
507
+ lines: string[],
508
+ records: CrewAgentRecord[],
509
+ status: string,
510
+ runId: string,
511
+ theme: CrewTheme,
512
+ w: number,
513
+ innerW: number,
514
+ ): void {
477
515
  const completed = records.filter((r) => r.status === "completed").length;
478
516
  const total = records.length;
479
517
  const ratio = total > 0 ? completed / total : 0;
@@ -491,10 +529,9 @@ function appendExpandedRun(lines: string[], records: CrewAgentRecord[], status:
491
529
  lines.push(padVisual(` ${theme.fg("muted", "Progress")} ${bar}${count}`, innerW));
492
530
 
493
531
  // Metrics
494
- const metricLine = [
495
- theme.fg("muted", formatDuration(duration)),
496
- theme.fg("muted", `${formatTokens(tokens)} tok`),
497
- ].join(theme.fg("dim", " · "));
532
+ const metricLine = [theme.fg("muted", formatDuration(duration)), theme.fg("muted", `${formatTokens(tokens)} tok`)].join(
533
+ theme.fg("dim", " · "),
534
+ );
498
535
  lines.push(padVisual(` ${metricLine}`, innerW));
499
536
  lines.push(padVisual(theme.fg("borderMuted", "─".repeat(innerW - 2)), innerW));
500
537
 
@@ -517,7 +554,15 @@ function appendExpandedRun(lines: string[], records: CrewAgentRecord[], status:
517
554
  }
518
555
  }
519
556
 
520
- function appendExpandedMetrics(lines: string[], m: Metrics | undefined, status: string, runId: string, theme: CrewTheme, w: number, innerW: number): void {
557
+ function appendExpandedMetrics(
558
+ lines: string[],
559
+ m: Metrics | undefined,
560
+ status: string,
561
+ runId: string,
562
+ theme: CrewTheme,
563
+ w: number,
564
+ innerW: number,
565
+ ): void {
521
566
  lines.push(padVisual(` ${theme.fg("toolTitle", theme.bold("CREW RUN RESULT"))} ${theme.fg("dim", shortId(runId))}`, innerW));
522
567
  lines.push(padVisual(theme.fg("borderMuted", "─".repeat(innerW - 2)), innerW));
523
568
  if (m) {
@@ -538,8 +583,9 @@ function appendExpandedMetrics(lines: string[], m: Metrics | undefined, status:
538
583
  function extractContentText(content: unknown): string {
539
584
  if (!Array.isArray(content)) return typeof content === "string" ? content : "";
540
585
  // onUpdate appends text blocks — only use the LAST one to avoid stacking
541
- const texts = content
542
- .filter((c): c is Record<string, unknown> => typeof c === "object" && c !== null && (c as Record<string, unknown>).type === "text");
586
+ const texts = content.filter(
587
+ (c): c is Record<string, unknown> => typeof c === "object" && c !== null && (c as Record<string, unknown>).type === "text",
588
+ );
543
589
  if (texts.length === 0) return "";
544
590
  return String((texts[texts.length - 1]! as Record<string, unknown>).text ?? "");
545
591
  }
@@ -573,17 +619,35 @@ function parseStreamingProgress(text: string): StreamingProgress | null {
573
619
  const toolInfo = toolMatch ? ` · ${toolMatch[1]}` : "";
574
620
 
575
621
  if (doneMatch) {
576
- return { elapsedMs, completed: parseInt(doneMatch[1]!, 10), total: parseInt(doneMatch[2]!, 10), status: null, activeAgent: (activeAgent ?? "") + toolInfo };
622
+ return {
623
+ elapsedMs,
624
+ completed: parseInt(doneMatch[1]!, 10),
625
+ total: parseInt(doneMatch[2]!, 10),
626
+ status: null,
627
+ activeAgent: (activeAgent ?? "") + toolInfo,
628
+ };
577
629
  }
578
630
  if (taskMatch) {
579
631
  const done = parseInt(taskMatch[1]!, 10);
580
632
  const running = parseInt(taskMatch[2]!, 10);
581
633
  const total = done + running;
582
- return { elapsedMs, completed: done, total, status: null, activeAgent: (activeAgent ?? "") + toolInfo };
634
+ return {
635
+ elapsedMs,
636
+ completed: done,
637
+ total,
638
+ status: null,
639
+ activeAgent: (activeAgent ?? "") + toolInfo,
640
+ };
583
641
  }
584
642
  if (elapsedMs > 0) {
585
643
  const statusMatch = text.match(/status=(\w+)/);
586
- return { elapsedMs, completed: null, total: null, status: statusMatch?.[1] ?? null, activeAgent };
644
+ return {
645
+ elapsedMs,
646
+ completed: null,
647
+ total: null,
648
+ status: statusMatch?.[1] ?? null,
649
+ activeAgent,
650
+ };
587
651
  }
588
652
 
589
653
  return null;
@@ -617,7 +681,9 @@ function computeRecordDuration(r: CrewAgentRecord): number {
617
681
 
618
682
  function computeTotalCost(records: CrewAgentRecord[]): number {
619
683
  let total = 0;
620
- for (const r of records) { if (r.usage?.cost) total += r.usage.cost; }
684
+ for (const r of records) {
685
+ if (r.usage?.cost) total += r.usage.cost;
686
+ }
621
687
  return total;
622
688
  }
623
689
 
@@ -22,7 +22,12 @@ const DEFAULT_TAIL_BYTES = 256 * 1024;
22
22
  const MAX_CACHE_SIZE = 100;
23
23
  const transcriptCache = new Map<string, TranscriptCacheEntry>();
24
24
 
25
- function cacheKey(path: string, options: Required<Pick<TranscriptReadOptions, "full">> & { maxTailBytes: number }): string {
25
+ function cacheKey(
26
+ path: string,
27
+ options: Required<Pick<TranscriptReadOptions, "full">> & {
28
+ maxTailBytes: number;
29
+ },
30
+ ): string {
26
31
  return `${path}:${options.full ? "full" : `tail:${options.maxTailBytes}`}`;
27
32
  }
28
33
 
@@ -35,13 +40,26 @@ export function clearTranscriptCache(path?: string): void {
35
40
  }
36
41
 
37
42
  export function getTranscriptCacheEntry(path: string, options: TranscriptReadOptions = {}): TranscriptCacheEntry | undefined {
38
- const normalized = { full: options.full === true, maxTailBytes: options.maxTailBytes ?? DEFAULT_TAIL_BYTES };
43
+ const normalized = {
44
+ full: options.full === true,
45
+ maxTailBytes: options.maxTailBytes ?? DEFAULT_TAIL_BYTES,
46
+ };
39
47
  return transcriptCache.get(cacheKey(path, normalized)) ?? transcriptCache.get(path);
40
48
  }
41
49
 
42
- function readTranscriptText(path: string, stat: fs.Stats, options: Required<Pick<TranscriptReadOptions, "full">> & { maxTailBytes: number }): { text: string; bytesRead: number; truncated: boolean } {
50
+ function readTranscriptText(
51
+ path: string,
52
+ stat: fs.Stats,
53
+ options: Required<Pick<TranscriptReadOptions, "full">> & {
54
+ maxTailBytes: number;
55
+ },
56
+ ): { text: string; bytesRead: number; truncated: boolean } {
43
57
  if (options.full || stat.size <= options.maxTailBytes) {
44
- return { text: fs.readFileSync(path, "utf-8"), bytesRead: stat.size, truncated: false };
58
+ return {
59
+ text: fs.readFileSync(path, "utf-8"),
60
+ bytesRead: stat.size,
61
+ truncated: false,
62
+ };
45
63
  }
46
64
  const bytesToRead = Math.min(stat.size, options.maxTailBytes);
47
65
  const fd = fs.openSync(path, "r");
@@ -57,8 +75,16 @@ function readTranscriptText(path: string, stat: fs.Stats, options: Required<Pick
57
75
  }
58
76
  }
59
77
 
60
- export function readTranscriptLinesCached(path: string, parse: (text: string) => string[], now = Date.now(), options: TranscriptReadOptions = {}): string[] {
61
- const normalized = { full: options.full === true, maxTailBytes: Math.max(1024, options.maxTailBytes ?? DEFAULT_TAIL_BYTES) };
78
+ export function readTranscriptLinesCached(
79
+ path: string,
80
+ parse: (text: string) => string[],
81
+ now = Date.now(),
82
+ options: TranscriptReadOptions = {},
83
+ ): string[] {
84
+ const normalized = {
85
+ full: options.full === true,
86
+ maxTailBytes: Math.max(1024, options.maxTailBytes ?? DEFAULT_TAIL_BYTES),
87
+ };
62
88
  const key = cacheKey(path, normalized);
63
89
  const previous = transcriptCache.get(key);
64
90
  let stat: fs.Stats;
@@ -30,9 +30,7 @@ function textFromContent(content: unknown): string {
30
30
 
31
31
  /** Safely cast unknown to a record for property access. */
32
32
  function asRecord(value: unknown): Record<string, unknown> | undefined {
33
- return value && typeof value === "object" && !Array.isArray(value)
34
- ? (value as Record<string, unknown>)
35
- : undefined;
33
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
36
34
  }
37
35
 
38
36
  /** Detect if an event type is a tool invocation (call/use/start). */
@@ -48,10 +46,13 @@ function isToolResultType(type: string): boolean {
48
46
  /** Extract tool name from various event shapes. */
49
47
  function extractToolName(obj: Record<string, unknown>): string | undefined {
50
48
  const name =
51
- typeof obj.toolName === "string" ? obj.toolName :
52
- typeof obj.name === "string" ? obj.name :
53
- typeof obj.tool === "string" ? obj.tool :
54
- undefined;
49
+ typeof obj.toolName === "string"
50
+ ? obj.toolName
51
+ : typeof obj.name === "string"
52
+ ? obj.name
53
+ : typeof obj.tool === "string"
54
+ ? obj.tool
55
+ : undefined;
55
56
  return name;
56
57
  }
57
58
 
@@ -105,9 +106,7 @@ export function parseTranscriptEntries(lines: string[]): TranscriptEntry[] {
105
106
  if (isToolCallType(type)) {
106
107
  // Look ahead for matching tool_result
107
108
  const toolName = extractToolName(obj) ?? "unknown";
108
- const inputText =
109
- typeof obj.input === "string" ? obj.input :
110
- obj.input !== undefined ? JSON.stringify(obj.input) : "";
109
+ const inputText = typeof obj.input === "string" ? obj.input : obj.input !== undefined ? JSON.stringify(obj.input) : "";
111
110
  let resultText = "";
112
111
  let isError = false;
113
112
  let resultTimestamp = timestamp;
@@ -120,9 +119,13 @@ export function parseTranscriptEntries(lines: string[]): TranscriptEntry[] {
120
119
  if (isToolResultType(nextType) || /tool/i.test(nextType)) {
121
120
  const robj = parsed[j]!.obj;
122
121
  const rt =
123
- typeof robj.text === "string" ? robj.text :
124
- typeof robj.result === "string" ? robj.result :
125
- robj.result !== undefined ? JSON.stringify(robj.result) : "";
122
+ typeof robj.text === "string"
123
+ ? robj.text
124
+ : typeof robj.result === "string"
125
+ ? robj.result
126
+ : robj.result !== undefined
127
+ ? JSON.stringify(robj.result)
128
+ : "";
126
129
  resultText = rt;
127
130
  isError = robj.isError === true;
128
131
  resultTimestamp = typeof robj.timestamp === "number" ? robj.timestamp : resultTimestamp;
@@ -159,15 +162,16 @@ export function parseTranscriptEntries(lines: string[]): TranscriptEntry[] {
159
162
  const toolName = extractToolName(obj) ?? "unknown";
160
163
  const isError = obj.isError === true;
161
164
  const text =
162
- typeof obj.text === "string" ? obj.text :
163
- typeof obj.result === "string" ? obj.result :
164
- obj.result !== undefined ? JSON.stringify(obj.result) : "";
165
+ typeof obj.text === "string"
166
+ ? obj.text
167
+ : typeof obj.result === "string"
168
+ ? obj.result
169
+ : obj.result !== undefined
170
+ ? JSON.stringify(obj.result)
171
+ : "";
165
172
 
166
173
  const summary = summarize(`${isError ? "✗" : "✓"} ${toolName}${text ? ": " + summarize(text, 60) : ""}`, SUMMARY_MAX);
167
- const content = [
168
- `[Tool Result: ${toolName}${isError ? " (error)" : ""}]`,
169
- text || "(no output)",
170
- ].join("\n");
174
+ const content = [`[Tool Result: ${toolName}${isError ? " (error)" : ""}]`, text || "(no output)"].join("\n");
171
175
 
172
176
  entries.push({
173
177
  id: id++,
@@ -225,9 +229,7 @@ export function parseTranscriptEntries(lines: string[]): TranscriptEntry[] {
225
229
 
226
230
  /** Toggle expand/collapse for an entry by index. Returns a new array. */
227
231
  export function toggleEntry(entries: TranscriptEntry[], index: number): TranscriptEntry[] {
228
- return entries.map((entry, i) =>
229
- i === index ? { ...entry, expanded: !entry.expanded } : entry,
230
- );
232
+ return entries.map((entry, i) => (i === index ? { ...entry, expanded: !entry.expanded } : entry));
231
233
  }
232
234
 
233
235
  /** Render entries into display lines, respecting expand/collapse.
@@ -1,16 +1,20 @@
1
1
  import * as fs from "node:fs";
2
- import type { TeamRunManifest } from "../state/types.ts";
3
2
  import { agentOutputPath, readCrewAgents } from "../runtime/crew-agent-records.ts";
4
- import type { CrewTheme } from "./theme-adapter.ts";
5
- import { asCrewTheme, subscribeThemeChange } from "./theme-adapter.ts";
6
- import { renderDiff } from "./render-diff.ts";
7
- import { highlightCode, highlightJson } from "./syntax-highlight.ts";
3
+ import type { TeamRunManifest } from "../state/types.ts";
4
+ import { resolveRealContainedPath } from "../utils/safe-paths.ts";
8
5
  import { pad, truncate, truncateToVisualLines } from "../utils/visual.ts";
6
+ import { renderDiff } from "./render-diff.ts";
9
7
  import { colorForStatus, iconForStatus, type RunStatus } from "./status-colors.ts";
8
+ import { highlightCode, highlightJson } from "./syntax-highlight.ts";
9
+ import type { CrewTheme } from "./theme-adapter.ts";
10
+ import { asCrewTheme, subscribeThemeChange } from "./theme-adapter.ts";
10
11
  import { DEFAULT_TRANSCRIPT_TAIL_BYTES, getTranscriptCacheEntry, readTranscriptLinesCached } from "./transcript-cache.ts";
11
- import { resolveRealContainedPath } from "../utils/safe-paths.ts";
12
12
 
13
- type Component = { invalidate(): void; render(width: number): string[]; handleInput(data: string): void };
13
+ type Component = {
14
+ invalidate(): void;
15
+ render(width: number): string[];
16
+ handleInput(data: string): void;
17
+ };
14
18
 
15
19
  type TranscriptTheme = CrewTheme;
16
20
 
@@ -71,7 +75,10 @@ export function formatTranscriptEvent(event: unknown, themeLike: unknown = undef
71
75
  const isError = obj.isError === true || result?.isError === true;
72
76
  const isPartial = obj.isPartial === true;
73
77
  const status: RunStatus = isError ? "failed" : isPartial ? "running" : "completed";
74
- const header = theme.fg(colorForStatus(status), `${iconForStatus(status, { runningGlyph: "⋯" })} [Tool${toolName ? `: ${toolName}` : ""}] ${type}`);
78
+ const header = theme.fg(
79
+ colorForStatus(status),
80
+ `${iconForStatus(status, { runningGlyph: "⋯" })} [Tool${toolName ? `: ${toolName}` : ""}] ${type}`,
81
+ );
75
82
  const text = (content || (typeof obj.text === "string" ? obj.text : typeof obj.result === "string" ? obj.result : "")).trim();
76
83
  if (!text) return [header, "(no output)"];
77
84
  if (isLikelyDiff(text)) {
@@ -83,7 +90,13 @@ export function formatTranscriptEvent(event: unknown, themeLike: unknown = undef
83
90
  if (text.includes("```") && text.includes("```")) {
84
91
  return [header, ...highlightCodeBlocks(text, theme)];
85
92
  }
86
- return [header, ...text.split(/\r?\n/).filter(Boolean).map((line) => theme.fg("muted", line))];
93
+ return [
94
+ header,
95
+ ...text
96
+ .split(/\r?\n/)
97
+ .filter(Boolean)
98
+ .map((line) => theme.fg("muted", line)),
99
+ ];
87
100
  }
88
101
  const message = asRecord(obj.message);
89
102
  if (message) {
@@ -97,9 +110,7 @@ export function formatTranscriptEvent(event: unknown, themeLike: unknown = undef
97
110
  return [theme.fg("accent", header), ...highlightCodeBlocks(text, theme)];
98
111
  }
99
112
  if (lines.length > 1) {
100
- const block = lines
101
- .map((line) => (role === "assistant" ? theme.bold(line) : line))
102
- .join("\n");
113
+ const block = lines.map((line) => (role === "assistant" ? theme.bold(line) : line)).join("\n");
103
114
  return [theme.fg("accent", header), ...block.split(/\r?\n/).filter(Boolean)];
104
115
  }
105
116
  return [theme.fg("accent", header), ...lines.filter(Boolean)];
@@ -125,9 +136,22 @@ export function formatTranscriptText(text: string, themeLike: unknown = undefine
125
136
  return lines.length ? lines : ["(no transcript content)"];
126
137
  }
127
138
 
128
- export function readRunTranscript(manifest: TeamRunManifest, taskId?: string, options: { full?: boolean; maxTailBytes?: number } = {}): { title: string; path: string; lines: string[]; bytesRead: number; size: number; truncated: boolean } {
139
+ export function readRunTranscript(
140
+ manifest: TeamRunManifest,
141
+ taskId?: string,
142
+ options: { full?: boolean; maxTailBytes?: number } = {},
143
+ ): {
144
+ title: string;
145
+ path: string;
146
+ lines: string[];
147
+ bytesRead: number;
148
+ size: number;
149
+ truncated: boolean;
150
+ } {
129
151
  const agents = readCrewAgents(manifest);
130
- const agent = taskId ? agents.find((item) => item.taskId === taskId || item.id === taskId) : agents.find((item) => item.transcriptPath) ?? agents[0];
152
+ const agent = taskId
153
+ ? agents.find((item) => item.taskId === taskId || item.id === taskId)
154
+ : (agents.find((item) => item.transcriptPath) ?? agents[0]);
131
155
  const selectedTaskId = agent?.taskId ?? taskId ?? "unknown";
132
156
  let transcriptPath = "";
133
157
  try {
@@ -148,10 +172,20 @@ export function readRunTranscript(manifest: TeamRunManifest, taskId?: string, op
148
172
  // Ignore untrusted transcript paths from mutable agent state and fall back to durable agent output.
149
173
  }
150
174
  }
151
- const readOptions = { full: options.full === true, maxTailBytes: options.maxTailBytes ?? DEFAULT_TRANSCRIPT_TAIL_BYTES };
175
+ const readOptions = {
176
+ full: options.full === true,
177
+ maxTailBytes: options.maxTailBytes ?? DEFAULT_TRANSCRIPT_TAIL_BYTES,
178
+ };
152
179
  const lines = readTranscriptLinesCached(transcriptPath, (text) => formatTranscriptText(text), Date.now(), readOptions);
153
180
  const entry = getTranscriptCacheEntry(transcriptPath, readOptions);
154
- return { title: `${manifest.runId}:${selectedTaskId}`, path: transcriptPath, lines: lines.length ? lines : ["(no transcript content)"], bytesRead: entry?.bytesRead ?? 0, size: entry?.size ?? 0, truncated: entry?.truncated ?? false };
181
+ return {
182
+ title: `${manifest.runId}:${selectedTaskId}`,
183
+ path: transcriptPath,
184
+ lines: lines.length ? lines : ["(no transcript content)"],
185
+ bytesRead: entry?.bytesRead ?? 0,
186
+ size: entry?.size ?? 0,
187
+ truncated: entry?.truncated ?? false,
188
+ };
155
189
  }
156
190
 
157
191
  interface ViewerState {
@@ -161,13 +195,7 @@ interface ViewerState {
161
195
  scroll: number;
162
196
  }
163
197
 
164
- function renderViewerBase(
165
- state: ViewerState,
166
- width: number,
167
- lines: string[],
168
- title: string,
169
- subtitle: string,
170
- ): string[] {
198
+ function renderViewerBase(state: ViewerState, width: number, lines: string[], title: string, subtitle: string): string[] {
171
199
  const inner = Math.max(20, width - 4);
172
200
  const bodyText = lines.join("\n");
173
201
  const { visualLines, skippedCount } = truncateToVisualLines(bodyText, state.lastHeight, inner);
@@ -194,7 +222,6 @@ function renderViewerBase(
194
222
  return linesOut.map((line) => truncate(line, width));
195
223
  }
196
224
 
197
-
198
225
  export class DurableTextViewer implements Component {
199
226
  private scroll = 0;
200
227
  private lastHeight = 16;
@@ -252,7 +279,12 @@ export class DurableTextViewer implements Component {
252
279
 
253
280
  render(width: number): string[] {
254
281
  return renderViewerBase(
255
- { theme: this.theme, autoScroll: this.autoScroll, lastHeight: this.lastHeight, scroll: this.scroll },
282
+ {
283
+ theme: this.theme,
284
+ autoScroll: this.autoScroll,
285
+ lastHeight: this.lastHeight,
286
+ scroll: this.scroll,
287
+ },
256
288
  width,
257
289
  this.lines,
258
290
  this.title,
@@ -273,7 +305,13 @@ export class DurableTranscriptViewer implements Component {
273
305
  private maxTailBytes: number;
274
306
  private readonly unsubscribeTheme: () => void;
275
307
 
276
- constructor(manifest: TeamRunManifest, theme: unknown, done: (result: undefined) => void, taskId?: string, options: { maxTailBytes?: number } = {}) {
308
+ constructor(
309
+ manifest: TeamRunManifest,
310
+ theme: unknown,
311
+ done: (result: undefined) => void,
312
+ taskId?: string,
313
+ options: { maxTailBytes?: number } = {},
314
+ ) {
277
315
  this.manifest = manifest;
278
316
  this.theme = asCrewTheme(theme);
279
317
  this.done = done;
@@ -293,7 +331,10 @@ export class DurableTranscriptViewer implements Component {
293
331
  this.done(undefined);
294
332
  return;
295
333
  }
296
- const content = readRunTranscript(this.manifest, this.taskId, { full: this.fullTranscript, maxTailBytes: this.maxTailBytes }).lines;
334
+ const content = readRunTranscript(this.manifest, this.taskId, {
335
+ full: this.fullTranscript,
336
+ maxTailBytes: this.maxTailBytes,
337
+ }).lines;
297
338
  const maxScroll = Math.max(0, content.length - this.lastHeight);
298
339
  if (data === "k" || data === "\u001b[A") {
299
340
  this.scroll = Math.max(0, this.scroll - 1);
@@ -323,9 +364,17 @@ export class DurableTranscriptViewer implements Component {
323
364
  }
324
365
 
325
366
  render(width: number): string[] {
326
- const data = readRunTranscript(this.manifest, this.taskId, { full: this.fullTranscript, maxTailBytes: this.maxTailBytes });
367
+ const data = readRunTranscript(this.manifest, this.taskId, {
368
+ full: this.fullTranscript,
369
+ maxTailBytes: this.maxTailBytes,
370
+ });
327
371
  return renderViewerBase(
328
- { theme: this.theme, autoScroll: this.autoScroll, lastHeight: this.lastHeight, scroll: this.scroll },
372
+ {
373
+ theme: this.theme,
374
+ autoScroll: this.autoScroll,
375
+ lastHeight: this.lastHeight,
376
+ scroll: this.scroll,
377
+ },
329
378
  width,
330
379
  data.lines,
331
380
  "pi-crew transcript",