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,269 @@
1
+ /**
2
+ * Terminal tab title + Ghostty native progress bar.
3
+ *
4
+ * Distilled from pi-status (Thinkscape — `source/pi-status/src/index.ts`):
5
+ * two UI channels pi-crew didn't use before, both surviving subprocess and
6
+ * visible even when the TUI isn't in focus:
7
+ *
8
+ * 1. **Tab title** via `ctx.ui.setTitle()` — shows a one-line crew run summary
9
+ * (e.g. "π-crew · 2 active · explorer, executor"). Cheap, works in any
10
+ * terminal. pi-status restores the title on idle with an exponential
11
+ * back-off re-assert loop (pi itself re-sets the title on some events).
12
+ * 2. **Ghostty OSC 9;4** native progress bar — written to `/dev/tty` (a
13
+ * separate channel from pi's TUI, so it works even when pi runs in a
14
+ * subprocess). state 3 = indeterminate pulsing while runs are active;
15
+ * state 1 value 100 = green completion flash; state 0 = clear. Compatible
16
+ * with all libghostty-based terminals (Ghostty, cmux, muxy).
17
+ *
18
+ * All writes are BEST-EFFORT: `/dev/tty` may be absent (non-interactive /
19
+ * subagent / CI context) and `setTitle` may be unavailable — failures are
20
+ * swallowed and never surface to the user. This module is purely additive:
21
+ * it cannot regress existing behavior because nothing depends on it.
22
+ *
23
+ * Lifecycle (wired from register.ts via runEventBus + turn events):
24
+ * - run active (running/queued/planning) → title "π-crew · N active · <roles>",
25
+ * Ghostty state 3 (indeterminate).
26
+ * - run just completed → Ghostty state 1/100 (green flash), schedule clear.
27
+ * - idle (no active runs) → restore pi's natural title, Ghostty state 0.
28
+ *
29
+ * @module terminal-status
30
+ */
31
+
32
+ import { writeFileSync } from "node:fs";
33
+ import { listLiveAgents } from "../runtime/live-agent-manager.ts";
34
+
35
+ /**
36
+ * Minimal UI surface this module needs. Deliberately narrower than
37
+ * `ExtensionContext["ui"]` so it's trivial to mock in tests (dependency
38
+ * inversion: depend only on what you use).
39
+ */
40
+ export interface TerminalStatusUi {
41
+ hasUI: boolean;
42
+ ui: { setTitle(title: string): void };
43
+ }
44
+
45
+ /** Ghostty OSC 9;4 progress states. */
46
+ export const GHOSTTY_PROGRESS = {
47
+ CLEAR: 0, // remove the progress indicator
48
+ SET: 1, // set explicit progress (0-100)
49
+ ERROR: 2, // error state
50
+ INDETERMINATE: 3, // pulsing/working animation
51
+ } as const;
52
+
53
+ /** Title prefix so the source is identifiable in the tab. */
54
+ const TITLE_PREFIX = "π-crew";
55
+ /** Separator between summary segments. */
56
+ const SEP = " · ";
57
+ /** Max roles listed in the title (keep it short for narrow tabs). */
58
+ const MAX_TITLE_ROLES = 3;
59
+
60
+ /** Idle-reassert backoff bounds (mirrors pi-status, clamped). */
61
+ const IDLE_REASSERT_START_MS = 200;
62
+ const IDLE_REASSERT_MAX_MS = 5000;
63
+ /** How long the green completion flash stays before clearing. */
64
+ const COMPLETE_FLASH_MS = 1500;
65
+
66
+ /** Injected for tests (defaults write to the real /dev/tty). */
67
+ export type GhosttyWriter = (seq: string) => void;
68
+
69
+ let ghosttyWriter: GhosttyWriter | undefined;
70
+
71
+ /**
72
+ * Set the raw-sequence writer (test seam). Pass `undefined` to restore the
73
+ * default `/dev/tty` writer. Tests inject a buffer-capturing fn.
74
+ */
75
+ export function setGhosttyWriterForTest(writer: GhosttyWriter | undefined): void {
76
+ ghosttyWriter = writer;
77
+ }
78
+
79
+ function defaultGhosttyWriter(seq: string): void {
80
+ try {
81
+ writeFileSync("/dev/tty", seq);
82
+ } catch {
83
+ // /dev/tty unavailable (non-interactive, subagent, CI, or Windows) — no-op.
84
+ // This is expected in most automated contexts; never surface it.
85
+ }
86
+ }
87
+
88
+ function writeGhostty(seq: string): void {
89
+ (ghosttyWriter ?? defaultGhosttyWriter)(seq);
90
+ }
91
+
92
+ /**
93
+ * Emit a Ghostty OSC 9;4 progress sequence.
94
+ * Format: `\x1b]9;4;<state>[;<value>]\x07`
95
+ */
96
+ export function setGhosttyProgress(state: number, value?: number): void {
97
+ const args = value !== undefined ? `${state};${value}` : `${state}`;
98
+ writeGhostty(`\x1b]9;4;${args}\x07`);
99
+ }
100
+
101
+ /** Indeterminate pulsing — use while crew runs are active. */
102
+ export function ghosttyWorking(): void {
103
+ setGhosttyProgress(GHOSTTY_PROGRESS.INDETERMINATE);
104
+ }
105
+
106
+ /** Green completion flash at 100% — use when a run finishes successfully. */
107
+ export function ghosttyComplete(): void {
108
+ setGhosttyProgress(GHOSTTY_PROGRESS.SET, 100);
109
+ }
110
+
111
+ /** Clear the progress indicator. */
112
+ export function ghosttyClear(): void {
113
+ setGhosttyProgress(GHOSTTY_PROGRESS.CLEAR);
114
+ }
115
+
116
+ /**
117
+ * Build the crew run-summary title segment from live agents.
118
+ * Returns "" when no agents are live (so the title can be restored).
119
+ */
120
+ export function buildCrewTitleSegment(cwd?: string): string {
121
+ const live = listLiveAgents();
122
+ if (live.length === 0) return "";
123
+ // Distinct roles across active agents, in stable order.
124
+ const roles: string[] = [];
125
+ const seen = new Set<string>();
126
+ for (const handle of live) {
127
+ const role = handle.role || handle.agent || "agent";
128
+ if (!seen.has(role)) {
129
+ seen.add(role);
130
+ roles.push(role);
131
+ }
132
+ }
133
+ const shown = roles.slice(0, MAX_TITLE_ROLES);
134
+ const more = roles.length > shown.length ? ` +${roles.length - shown.length}` : "";
135
+ return `${TITLE_PREFIX}${SEP}${live.length} active${SEP}${shown.join(", ")}${more}`;
136
+ }
137
+
138
+ /** Idle title — what we restore when no runs are active. */
139
+ export function buildIdleTitle(): string {
140
+ return TITLE_PREFIX;
141
+ }
142
+
143
+ /**
144
+ * Set the terminal tab title if the UI supports it. Best-effort.
145
+ */
146
+ export function setTerminalTitle(ctx: TerminalStatusUi, title: string): void {
147
+ if (!ctx.hasUI) return;
148
+ try {
149
+ ctx.ui.setTitle(title);
150
+ } catch {
151
+ // setTitle unavailable or failed — swallow (purely cosmetic).
152
+ }
153
+ }
154
+
155
+ interface TerminalStatusState {
156
+ /** Idle re-assert timer (mirrors pi-status backoff). */
157
+ idleTimer: ReturnType<typeof setTimeout> | undefined;
158
+ /** Whether we currently show an "active" title (so we know to restore). */
159
+ showingActive: boolean;
160
+ /** Completion-flash clear timer. */
161
+ flashTimer: ReturnType<typeof setTimeout> | undefined;
162
+ /** True after dispose() — stop all timers/writes. */
163
+ destroyed: boolean;
164
+ }
165
+
166
+ /**
167
+ * Terminal status controller. Owns the title + Ghostty progress lifecycle.
168
+ *
169
+ * The controller is a small stateful object (no class hierarchy) so it can be
170
+ * constructed per-session in register.ts and disposed cleanly. It exposes
171
+ * `onRunsActive()`, `onRunCompleted()`, `onIdle()` entry points that the
172
+ * runEventBus + turn handlers call.
173
+ */
174
+ export interface TerminalStatusController {
175
+ onRunsActive(): void;
176
+ onRunCompleted(): void;
177
+ onIdle(): void;
178
+ dispose(): void;
179
+ }
180
+
181
+ export function createTerminalStatusController(ctx: TerminalStatusUi): TerminalStatusController {
182
+ const state: TerminalStatusState = {
183
+ idleTimer: undefined,
184
+ showingActive: false,
185
+ flashTimer: undefined,
186
+ destroyed: false,
187
+ };
188
+
189
+ const clearIdleTimer = (): void => {
190
+ if (state.idleTimer) {
191
+ clearTimeout(state.idleTimer);
192
+ state.idleTimer = undefined;
193
+ }
194
+ };
195
+ const clearFlashTimer = (): void => {
196
+ if (state.flashTimer) {
197
+ clearTimeout(state.flashTimer);
198
+ state.flashTimer = undefined;
199
+ }
200
+ };
201
+
202
+ /**
203
+ * Idle re-assert loop: pi itself re-sets the title on some events
204
+ * (session_info_changed, model_select, rebinds), so a single setTitle
205
+ * is not durable. Re-assert on an exponential backoff until the next
206
+ * activity. (Pattern from pi-status scheduleIdleReassert.)
207
+ */
208
+ const scheduleIdleReassert = (delay: number): void => {
209
+ if (state.destroyed || state.showingActive) return;
210
+ clearIdleTimer();
211
+ state.idleTimer = setTimeout(() => {
212
+ if (state.destroyed || state.showingActive) return;
213
+ setTerminalTitle(ctx, buildIdleTitle());
214
+ scheduleIdleReassert(Math.min(delay * 2, IDLE_REASSERT_MAX_MS));
215
+ }, delay);
216
+ // Unref so this recursive backoff timer never keeps the event loop alive
217
+ // (critical on SIGTERM where dispose() is not reached → process hang).
218
+ state.idleTimer.unref?.();
219
+ };
220
+
221
+ return {
222
+ onRunsActive(): void {
223
+ if (state.destroyed) return;
224
+ clearIdleTimer();
225
+ clearFlashTimer();
226
+ state.showingActive = true;
227
+ const segment = buildCrewTitleSegment();
228
+ if (segment) setTerminalTitle(ctx, segment);
229
+ ghosttyWorking();
230
+ },
231
+ onRunCompleted(): void {
232
+ if (state.destroyed) return;
233
+ clearFlashTimer();
234
+ ghosttyComplete();
235
+ // Clear the green flash after a short beat, then re-evaluate.
236
+ state.flashTimer = setTimeout(() => {
237
+ if (state.destroyed) return;
238
+ state.flashTimer = undefined;
239
+ // If still active, resume indeterminate; else go idle.
240
+ if (listLiveAgents().length > 0) {
241
+ ghosttyWorking();
242
+ } else {
243
+ ghosttyClear();
244
+ }
245
+ }, COMPLETE_FLASH_MS);
246
+ // Unref so the one-shot flash clear never blocks process exit.
247
+ state.flashTimer.unref?.();
248
+ },
249
+ onIdle(): void {
250
+ if (state.destroyed) return;
251
+ state.showingActive = false;
252
+ clearFlashTimer();
253
+ ghosttyClear();
254
+ setTerminalTitle(ctx, buildIdleTitle());
255
+ scheduleIdleReassert(IDLE_REASSERT_START_MS);
256
+ },
257
+ dispose(): void {
258
+ state.destroyed = true;
259
+ clearIdleTimer();
260
+ clearFlashTimer();
261
+ // Best-effort: clear the Ghostty progress so we don't leave a stale bar.
262
+ try {
263
+ ghosttyClear();
264
+ } catch {
265
+ // swallow
266
+ }
267
+ },
268
+ };
269
+ }
@@ -0,0 +1,271 @@
1
+ export type CrewThemeColor =
2
+ | "accent"
3
+ | "border"
4
+ | "borderAccent"
5
+ | "borderMuted"
6
+ | "success"
7
+ | "error"
8
+ | "warning"
9
+ | "muted"
10
+ | "dim"
11
+ | "text"
12
+ | "thinkingText"
13
+ // Tool rendering
14
+ | "toolTitle"
15
+ | "toolOutput"
16
+ | "toolDiffAdded"
17
+ | "toolDiffRemoved"
18
+ | "toolDiffContext"
19
+ // Markdown
20
+ | "mdHeading"
21
+ | "mdLink"
22
+ | "mdCode"
23
+ | "mdCodeBlock"
24
+ | "mdQuote"
25
+ | "mdHr"
26
+ | "mdListBullet"
27
+ // Syntax highlighting
28
+ | "syntaxKeyword"
29
+ | "syntaxString"
30
+ | "syntaxNumber"
31
+ | "syntaxComment"
32
+ | "syntaxFunction"
33
+ | "syntaxVariable"
34
+ | "syntaxType"
35
+ | "syntaxOperator"
36
+ | "syntaxPunctuation"
37
+ // Message display
38
+ | "userMessageText"
39
+ | "customMessageLabel"
40
+ // Thinking gradient (6 levels, low→high intensity)
41
+ | "thinkingOff"
42
+ | "thinkingMinimal"
43
+ | "thinkingLow"
44
+ | "thinkingMedium"
45
+ | "thinkingHigh"
46
+ | "thinkingXhigh"
47
+ // Special
48
+ | "bashMode";
49
+
50
+ export type CrewThemeBg = "selectedBg" | "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
51
+
52
+ /** ANSI fallback values for theme color slots when the active theme doesn't define them. */
53
+ export const THEME_COLOR_FALLBACKS: Record<CrewThemeColor, string> = {
54
+ accent: "\x1b[36m",
55
+ border: "\x1b[38;5;240m",
56
+ borderAccent: "\x1b[35m",
57
+ borderMuted: "\x1b[38;5;236m",
58
+ success: "\x1b[32m",
59
+ error: "\x1b[31m",
60
+ warning: "\x1b[33m",
61
+ muted: "\x1b[38;5;245m",
62
+ dim: "\x1b[38;5;240m",
63
+ text: "\x1b[39m",
64
+ thinkingText: "\x1b[38;5;245m",
65
+ toolTitle: "\x1b[36m",
66
+ toolOutput: "\x1b[38;5;245m",
67
+ toolDiffAdded: "\x1b[32m",
68
+ toolDiffRemoved: "\x1b[31m",
69
+ toolDiffContext: "\x1b[38;5;245m",
70
+ mdHeading: "\x1b[33m",
71
+ mdLink: "\x1b[35m",
72
+ mdCode: "\x1b[32m",
73
+ mdCodeBlock: "\x1b[39m",
74
+ mdQuote: "\x1b[38;5;245m",
75
+ mdHr: "\x1b[38;5;240m",
76
+ mdListBullet: "\x1b[36m",
77
+ syntaxKeyword: "\x1b[35m",
78
+ syntaxString: "\x1b[32m",
79
+ syntaxNumber: "\x1b[33m",
80
+ syntaxComment: "\x1b[38;5;245m",
81
+ syntaxFunction: "\x1b[36m",
82
+ syntaxVariable: "\x1b[39m",
83
+ syntaxType: "\x1b[35m",
84
+ syntaxOperator: "\x1b[35m",
85
+ syntaxPunctuation: "\x1b[35m",
86
+ userMessageText: "\x1b[39m",
87
+ customMessageLabel: "\x1b[35m",
88
+ thinkingOff: "\x1b[38;5;236m",
89
+ thinkingMinimal: "\x1b[38;5;245m",
90
+ thinkingLow: "\x1b[35m",
91
+ thinkingMedium: "\x1b[35m",
92
+ thinkingHigh: "\x1b[36m",
93
+ thinkingXhigh: "\x1b[35m",
94
+ bashMode: "\x1b[32m",
95
+ };
96
+
97
+ /** Map a thinking intensity level (0–5) to a theme color slot. */
98
+ export function thinkingColorForLevel(level: number): CrewThemeColor {
99
+ const slots: CrewThemeColor[] = ["thinkingOff", "thinkingMinimal", "thinkingLow", "thinkingMedium", "thinkingHigh", "thinkingXhigh"];
100
+ return slots[Math.min(Math.max(level, 0), 5)] ?? "thinkingOff";
101
+ }
102
+
103
+ export interface CrewTheme {
104
+ fg(color: CrewThemeColor, text: string): string;
105
+ bg?(color: CrewThemeBg, text: string): string;
106
+ bold(text: string): string;
107
+ italic?(text: string): string;
108
+ underline?(text: string): string;
109
+ inverse?(text: string): string;
110
+ }
111
+
112
+ function inverseAnsi(text: string): string {
113
+ return `\u001b[7m${text}\u001b[27m`;
114
+ }
115
+
116
+ function safeNoopTheme(): CrewTheme {
117
+ return {
118
+ fg: (_color, text) => text,
119
+ bold: (text) => text,
120
+ inverse: inverseAnsi,
121
+ };
122
+ }
123
+
124
+ function asStringFn(value: unknown, owner?: object): ((color: CrewThemeColor | CrewThemeBg, text: string) => string) | undefined {
125
+ if (typeof value !== "function") return undefined;
126
+ return (color: CrewThemeColor | CrewThemeBg, text: string) => {
127
+ try {
128
+ const fn = value as (this: object | undefined, color: CrewThemeColor | CrewThemeBg, text: string) => unknown;
129
+ const result = fn.call(owner, color, text);
130
+ return typeof result === "string" ? result : text;
131
+ } catch {
132
+ return text;
133
+ }
134
+ };
135
+ }
136
+
137
+ function asUnaryFn(value: unknown, owner?: object): ((text: string) => string) | undefined {
138
+ if (typeof value !== "function") return undefined;
139
+ return (text: string) => {
140
+ try {
141
+ const fn = value as (this: object | undefined, text: string) => unknown;
142
+ const result = fn.call(owner, text);
143
+ return typeof result === "string" ? result : text;
144
+ } catch {
145
+ return text;
146
+ }
147
+ };
148
+ }
149
+
150
+ function asInverse(value: unknown, owner?: object): (text: string) => string {
151
+ return asUnaryFn(value, owner) ?? inverseAnsi;
152
+ }
153
+
154
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
155
+ return value && typeof value === "object" ? (value as Record<string, unknown>) : undefined;
156
+ }
157
+
158
+ function callMaybeString(fn: unknown): string | undefined {
159
+ if (typeof fn !== "function") return undefined;
160
+ try {
161
+ const result = (fn as () => unknown)();
162
+ return typeof result === "string" || typeof result === "number" || typeof result === "boolean" ? String(result) : undefined;
163
+ } catch {
164
+ return undefined;
165
+ }
166
+ }
167
+
168
+ function themeSignature(theme: object): string {
169
+ const record = theme as Record<string, unknown>;
170
+ const primitiveEntries = Object.entries(record)
171
+ .filter(
172
+ ([_key, value]) =>
173
+ value === undefined ||
174
+ value === null ||
175
+ typeof value === "string" ||
176
+ typeof value === "number" ||
177
+ typeof value === "boolean",
178
+ )
179
+ .map(([key, value]) => `${key}:${String(value)}`)
180
+ .sort();
181
+ const colorMode = callMaybeString(record.getColorMode);
182
+ return [colorMode ? `mode:${colorMode}` : undefined, ...primitiveEntries].filter((item): item is string => Boolean(item)).join("|");
183
+ }
184
+
185
+ type Unsubscribe = () => void;
186
+
187
+ interface ThemeSourceSubscription {
188
+ callbacks: Set<() => void>;
189
+ unsubscribeSource?: Unsubscribe;
190
+ pollTimer?: ReturnType<typeof setInterval>;
191
+ lastSignature: string;
192
+ }
193
+
194
+ const themeSubscriptions = new WeakMap<object, ThemeSourceSubscription>();
195
+
196
+ function asUnsubscribe(value: unknown): Unsubscribe | undefined {
197
+ if (typeof value === "function") return value as Unsubscribe;
198
+ const record = asRecord(value);
199
+ if (!record) return undefined;
200
+ if (typeof record.unsubscribe === "function") return () => (record.unsubscribe as () => void)();
201
+ if (typeof record.dispose === "function") return () => (record.dispose as () => void)();
202
+ return undefined;
203
+ }
204
+
205
+ function startThemeSourceSubscription(theme: object, subscription: ThemeSourceSubscription): void {
206
+ const record = theme as Record<string, unknown>;
207
+ const emit = () => {
208
+ for (const callback of [...subscription.callbacks]) callback();
209
+ };
210
+ if (typeof record.onThemeChange === "function") {
211
+ const result = (record.onThemeChange as (callback: () => void) => unknown)(emit);
212
+ subscription.unsubscribeSource = asUnsubscribe(result);
213
+ return;
214
+ }
215
+ if (typeof record.addEventListener === "function") {
216
+ (record.addEventListener as (type: string, callback: () => void) => void)("change", emit);
217
+ if (typeof record.removeEventListener === "function") {
218
+ subscription.unsubscribeSource = () =>
219
+ (record.removeEventListener as (type: string, callback: () => void) => void)("change", emit);
220
+ }
221
+ return;
222
+ }
223
+ subscription.pollTimer = setInterval(() => {
224
+ const nextSignature = themeSignature(theme);
225
+ if (nextSignature === subscription.lastSignature) return;
226
+ subscription.lastSignature = nextSignature;
227
+ emit();
228
+ }, 1000);
229
+ subscription.pollTimer.unref();
230
+ }
231
+
232
+ export function subscribeThemeChange(theme: unknown, callback: () => void): () => void {
233
+ if (!theme || typeof theme !== "object") return () => {};
234
+ const key = theme;
235
+ let subscription = themeSubscriptions.get(key);
236
+ if (!subscription) {
237
+ subscription = {
238
+ callbacks: new Set(),
239
+ lastSignature: themeSignature(key),
240
+ };
241
+ themeSubscriptions.set(key, subscription);
242
+ startThemeSourceSubscription(key, subscription);
243
+ }
244
+ subscription.callbacks.add(callback);
245
+ return () => {
246
+ const current = themeSubscriptions.get(key);
247
+ if (!current) return;
248
+ current.callbacks.delete(callback);
249
+ if (current.callbacks.size > 0) return;
250
+ if (current.pollTimer) clearInterval(current.pollTimer);
251
+ current.unsubscribeSource?.();
252
+ themeSubscriptions.delete(key);
253
+ };
254
+ }
255
+
256
+ export function asCrewTheme(raw: unknown): CrewTheme {
257
+ const fallback = safeNoopTheme();
258
+ if (!raw || typeof raw !== "object") return fallback;
259
+ const record = raw as Record<string, unknown>;
260
+ const fg = asStringFn(record.fg, raw);
261
+ const bold = asUnaryFn(record.bold, raw);
262
+ if (!fg || !bold) return fallback;
263
+ return {
264
+ fg,
265
+ bg: asStringFn(record.bg, raw),
266
+ bold,
267
+ italic: asUnaryFn(record.italic, raw),
268
+ underline: asUnaryFn(record.underline, raw),
269
+ inverse: asInverse(record.inverse, raw),
270
+ };
271
+ }