@fitchmultz/pi-subagents 0.44.6 → 0.45.0

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 (947) hide show
  1. package/CHANGELOG.md +265 -7
  2. package/LICENSE +1 -0
  3. package/README.md +58 -1483
  4. package/agents/context-builder.md +19 -1
  5. package/agents/debugger.md +10 -3
  6. package/agents/delegate.md +1 -0
  7. package/agents/fixer.md +4 -1
  8. package/agents/oracle.md +13 -2
  9. package/agents/planner.md +14 -3
  10. package/agents/researcher.md +11 -2
  11. package/agents/reviewer-claude.md +11 -2
  12. package/agents/reviewer-gpt.md +12 -2
  13. package/agents/reviewer-ponytail.md +11 -1
  14. package/agents/reviewer-security.md +13 -2
  15. package/agents/reviewer.md +12 -3
  16. package/agents/scout.md +12 -1
  17. package/agents/ui-designer.md +10 -3
  18. package/agents/watcher.md +1 -0
  19. package/agents/worker.md +8 -2
  20. package/agents/writer.md +6 -3
  21. package/dist/agents/agent-defaults.js +9 -0
  22. package/dist/agents/agent-definition.js +196 -0
  23. package/dist/agents/agent-management.js +64 -905
  24. package/dist/agents/agent-scope.js +4 -2
  25. package/dist/agents/agent-selection.js +10 -5
  26. package/dist/agents/agent-serializer.js +55 -67
  27. package/dist/agents/agents.js +75 -716
  28. package/dist/agents/builtin-overrides.js +218 -0
  29. package/dist/agents/chain-serializer.js +3 -367
  30. package/dist/agents/config-values.js +52 -0
  31. package/dist/agents/discovery-paths.js +91 -0
  32. package/dist/agents/frontmatter.js +15 -5
  33. package/dist/agents/identity.js +25 -8
  34. package/dist/agents/json-chain.js +254 -0
  35. package/dist/agents/management-config.js +332 -0
  36. package/dist/agents/management-format.js +231 -0
  37. package/dist/agents/management-persistence.js +287 -0
  38. package/dist/agents/management-targets.js +136 -0
  39. package/dist/agents/management-warnings.js +81 -0
  40. package/dist/agents/markdown-chain.js +210 -0
  41. package/dist/agents/package-agents.js +154 -0
  42. package/dist/agents/skill-files.js +157 -0
  43. package/dist/agents/skill-search-paths.js +246 -0
  44. package/dist/agents/skill-types.js +1 -0
  45. package/dist/agents/skills.js +54 -499
  46. package/dist/extension/acceptance-schema.js +55 -0
  47. package/dist/extension/build-info.js +1 -1
  48. package/dist/extension/chain-schema.js +196 -0
  49. package/dist/extension/child-runtime.js +237 -0
  50. package/dist/extension/compact-tools.js +122 -51
  51. package/dist/extension/config-schema.js +29 -0
  52. package/dist/extension/config.js +27 -11
  53. package/dist/extension/control-notice-schema.js +37 -0
  54. package/dist/extension/control-notices.js +13 -6
  55. package/dist/extension/delegation-bridges.js +43 -0
  56. package/dist/extension/doctor.js +48 -25
  57. package/dist/extension/everyday-schemas.js +207 -0
  58. package/dist/extension/fanout-child.js +18 -319
  59. package/dist/extension/index.js +8 -563
  60. package/dist/extension/message-renderers.js +225 -0
  61. package/dist/extension/nested-control-inbox.js +154 -0
  62. package/dist/extension/parent-runtime.js +339 -0
  63. package/dist/extension/primary-tool.js +69 -0
  64. package/dist/extension/result-snapshot.js +22 -0
  65. package/dist/extension/runtime-reload.js +62 -0
  66. package/dist/extension/runtime-storage.js +43 -0
  67. package/dist/extension/schema-overrides.js +75 -0
  68. package/dist/extension/schemas.js +265 -336
  69. package/dist/extension/tool-input.js +27 -12
  70. package/dist/extension/tool-result.js +19 -4
  71. package/dist/history/answer-texts.js +57 -0
  72. package/dist/history/canonical-result.js +76 -43
  73. package/dist/history/canonical-selection.js +40 -0
  74. package/dist/history/database-generation.js +82 -0
  75. package/dist/history/database.js +57 -33
  76. package/dist/history/entry-ingestion.js +61 -0
  77. package/dist/history/index.js +242 -97
  78. package/dist/history/ingest.js +168 -331
  79. package/dist/history/output-digests.js +40 -0
  80. package/dist/history/preview.js +210 -0
  81. package/dist/history/prune.js +33 -0
  82. package/dist/history/queries.js +60 -349
  83. package/dist/history/query-scope.js +190 -0
  84. package/dist/history/rows.js +77 -0
  85. package/dist/history/run-projection.js +137 -0
  86. package/dist/history/run-queries.js +153 -0
  87. package/dist/history/schema.js +31 -0
  88. package/dist/history/search-queries.js +163 -0
  89. package/dist/history/selected-record.js +108 -0
  90. package/dist/history/source-file.js +45 -0
  91. package/dist/history/source-watches.js +141 -0
  92. package/dist/history/store.js +164 -153
  93. package/dist/history/text-chunks.js +169 -0
  94. package/dist/history/text.js +35 -0
  95. package/dist/history/transcript-queries.js +177 -0
  96. package/dist/history/types.js +5 -1
  97. package/dist/history/values.js +36 -0
  98. package/dist/history/wire-inputs.js +122 -0
  99. package/dist/history/wire-models.js +38 -0
  100. package/dist/history/worker-runtime.js +441 -0
  101. package/dist/history/worker.js +66 -462
  102. package/dist/intercom/intercom-bridge.js +48 -20
  103. package/dist/intercom/live-intercom.js +80 -23
  104. package/dist/intercom/nested-result-tree.js +163 -0
  105. package/dist/intercom/result-delivery.js +40 -0
  106. package/dist/intercom/result-intercom.js +178 -261
  107. package/dist/pi-intercom/agent-dir.js +7 -3
  108. package/dist/pi-intercom/async.js +27 -0
  109. package/dist/pi-intercom/bridge-ask.js +57 -0
  110. package/dist/pi-intercom/bridge-config.js +79 -0
  111. package/dist/pi-intercom/bridge-http.js +86 -0
  112. package/dist/pi-intercom/bridge-protocol.js +106 -0
  113. package/dist/pi-intercom/bridge-routes.js +64 -0
  114. package/dist/pi-intercom/bridge-session.js +416 -0
  115. package/dist/pi-intercom/bridge.js +182 -603
  116. package/dist/pi-intercom/broker/broker.js +236 -244
  117. package/dist/pi-intercom/broker/client.js +189 -304
  118. package/dist/pi-intercom/broker/connection.js +165 -0
  119. package/dist/pi-intercom/broker/framing.js +5 -3
  120. package/dist/pi-intercom/broker/paths.js +4 -1
  121. package/dist/pi-intercom/broker/pid.js +53 -25
  122. package/dist/pi-intercom/broker/snapshot-reader.js +78 -0
  123. package/dist/pi-intercom/broker/snapshots.js +188 -0
  124. package/dist/pi-intercom/broker/spawn.js +36 -25
  125. package/dist/pi-intercom/config.js +85 -71
  126. package/dist/pi-intercom/connection.js +297 -0
  127. package/dist/pi-intercom/identity.js +58 -0
  128. package/dist/pi-intercom/inbound-delivery.js +356 -0
  129. package/dist/pi-intercom/inbound-journal.js +303 -0
  130. package/dist/pi-intercom/inbound-record.js +113 -0
  131. package/dist/pi-intercom/inbound.js +179 -0
  132. package/dist/pi-intercom/index.js +79 -2933
  133. package/dist/pi-intercom/inspection.js +62 -0
  134. package/dist/pi-intercom/interview.js +236 -0
  135. package/dist/pi-intercom/lifecycle.js +168 -0
  136. package/dist/pi-intercom/message-format.js +69 -0
  137. package/dist/pi-intercom/outbound.js +304 -0
  138. package/dist/pi-intercom/peer-awareness.js +66 -0
  139. package/dist/pi-intercom/reply-tracker.js +62 -43
  140. package/dist/pi-intercom/reply-wait.js +201 -0
  141. package/dist/pi-intercom/runtime-types.js +3 -0
  142. package/dist/pi-intercom/session-events.js +180 -0
  143. package/dist/pi-intercom/session-targets.js +42 -34
  144. package/dist/pi-intercom/subagent-events.js +238 -0
  145. package/dist/pi-intercom/subagent-live-events.js +129 -86
  146. package/dist/pi-intercom/supervisor.js +282 -0
  147. package/dist/pi-intercom/tool-arguments.js +74 -0
  148. package/dist/pi-intercom/tool-registration.js +95 -0
  149. package/dist/pi-intercom/tool-results.js +38 -0
  150. package/dist/pi-intercom/tool-schemas.js +82 -0
  151. package/dist/pi-intercom/tools.js +51 -0
  152. package/dist/pi-intercom/topic-actions.js +177 -0
  153. package/dist/pi-intercom/topics.js +177 -92
  154. package/dist/pi-intercom/transport.js +1 -0
  155. package/dist/pi-intercom/types.js +151 -114
  156. package/dist/pi-intercom/ui/compose-paste.js +93 -0
  157. package/dist/pi-intercom/ui/compose.js +121 -143
  158. package/dist/pi-intercom/ui/inline-message.js +59 -59
  159. package/dist/pi-intercom/ui/message-renderers.js +147 -0
  160. package/dist/pi-intercom/ui/overlay.js +181 -0
  161. package/dist/pi-intercom/ui/session-format.js +124 -0
  162. package/dist/pi-intercom/ui/session-list.js +76 -43
  163. package/dist/pi-intercom/ui/tool-renderers.js +110 -0
  164. package/dist/pi-intercom/ui/topic-view.js +88 -0
  165. package/dist/pi-intercom/validation.js +59 -0
  166. package/dist/runs/background/async-chain-plan.js +265 -0
  167. package/dist/runs/background/async-control.js +134 -58
  168. package/dist/runs/background/async-execution.js +136 -633
  169. package/dist/runs/background/async-job-admission.js +40 -0
  170. package/dist/runs/background/async-job-projection.js +169 -0
  171. package/dist/runs/background/async-job-tracker.js +297 -327
  172. package/dist/runs/background/async-launch.js +216 -0
  173. package/dist/runs/background/async-plan.js +116 -0
  174. package/dist/runs/background/async-result-file.js +47 -23
  175. package/dist/runs/background/async-resume.js +195 -253
  176. package/dist/runs/background/async-run-discovery.js +213 -0
  177. package/dist/runs/background/async-run-location.js +135 -0
  178. package/dist/runs/background/async-run-record.js +1 -0
  179. package/dist/runs/background/async-run-repair.js +223 -0
  180. package/dist/runs/background/async-run-summary.js +1 -0
  181. package/dist/runs/background/async-single-plan.js +105 -0
  182. package/dist/runs/background/async-status-projection.js +155 -0
  183. package/dist/runs/background/async-status-render.js +117 -0
  184. package/dist/runs/background/async-status-validation.js +181 -0
  185. package/dist/runs/background/async-status.js +3 -442
  186. package/dist/runs/background/async-value.js +21 -0
  187. package/dist/runs/background/completion-dedupe.js +36 -12
  188. package/dist/runs/background/completion-delivery.js +211 -205
  189. package/dist/runs/background/completion-receipts.js +237 -0
  190. package/dist/runs/background/legacy-status-acceptance.js +34 -0
  191. package/dist/runs/background/notify.js +96 -48
  192. package/dist/runs/background/parallel-groups.js +36 -19
  193. package/dist/runs/background/result-payload.js +178 -0
  194. package/dist/runs/background/result-watcher.js +348 -339
  195. package/dist/runs/background/run-id-resolver.js +53 -33
  196. package/dist/runs/background/run-schemas.js +189 -0
  197. package/dist/runs/background/run-status-contracts.js +14 -0
  198. package/dist/runs/background/run-status-render.js +270 -0
  199. package/dist/runs/background/run-status.js +231 -339
  200. package/dist/runs/background/runner-attempt-output.js +155 -0
  201. package/dist/runs/background/runner-attempt.js +263 -0
  202. package/dist/runs/background/runner-child-executor.js +166 -0
  203. package/dist/runs/background/runner-child-observer.js +192 -0
  204. package/dist/runs/background/runner-completion-summary.js +71 -0
  205. package/dist/runs/background/runner-completion.js +211 -0
  206. package/dist/runs/background/runner-config.js +1 -0
  207. package/dist/runs/background/runner-contract.js +1 -0
  208. package/dist/runs/background/runner-dynamic.js +121 -0
  209. package/dist/runs/background/runner-finalization.js +149 -0
  210. package/dist/runs/background/runner-graph.js +68 -0
  211. package/dist/runs/background/runner-initial-status.js +60 -0
  212. package/dist/runs/background/runner-lifecycle.js +142 -0
  213. package/dist/runs/background/runner-monitor.js +309 -0
  214. package/dist/runs/background/runner-parallel.js +204 -0
  215. package/dist/runs/background/runner-prepare-step.js +86 -0
  216. package/dist/runs/background/runner-reporting.js +153 -0
  217. package/dist/runs/background/runner-status.js +148 -0
  218. package/dist/runs/background/runner-step-output.js +92 -0
  219. package/dist/runs/background/runner-step-record.js +45 -0
  220. package/dist/runs/background/runner-step.js +112 -0
  221. package/dist/runs/background/runner-streaming.js +173 -0
  222. package/dist/runs/background/runner-workflow.js +213 -0
  223. package/dist/runs/background/stale-run-reconciler.js +155 -292
  224. package/dist/runs/background/subagent-runner-launcher.js +33 -13
  225. package/dist/runs/background/subagent-runner.js +51 -1842
  226. package/dist/runs/background/top-level-async.js +2 -1
  227. package/dist/runs/foreground/chain-clarify.js +293 -1017
  228. package/dist/runs/foreground/clarification-draft.js +87 -0
  229. package/dist/runs/foreground/clarify-contracts.js +1 -0
  230. package/dist/runs/foreground/clarify-invocation.js +129 -38
  231. package/dist/runs/foreground/clarify-text-editor.js +210 -0
  232. package/dist/runs/foreground/clarify-view.js +167 -0
  233. package/dist/runs/foreground/execution-input.js +226 -353
  234. package/dist/runs/foreground/execution-routing.js +33 -0
  235. package/dist/runs/foreground/execution-timeout.js +81 -0
  236. package/dist/runs/foreground/foreground-control.js +222 -763
  237. package/dist/runs/foreground/foreground-memory.js +99 -0
  238. package/dist/runs/foreground/invocation-context.js +55 -0
  239. package/dist/runs/foreground/invocation-execution.js +331 -0
  240. package/dist/runs/foreground/invocation-expansion.js +48 -0
  241. package/dist/runs/foreground/management-actions.js +295 -0
  242. package/dist/runs/foreground/nested-control.js +179 -0
  243. package/dist/runs/foreground/question-continuation.js +78 -0
  244. package/dist/runs/foreground/question-control.js +155 -50
  245. package/dist/runs/foreground/resume-target.js +221 -0
  246. package/dist/runs/foreground/revival-launch-policy.js +58 -0
  247. package/dist/runs/foreground/run-async-path.js +190 -178
  248. package/dist/runs/foreground/run-interrupt.js +164 -0
  249. package/dist/runs/foreground/run-nudge.js +278 -0
  250. package/dist/runs/foreground/saved-revival.js +251 -0
  251. package/dist/runs/foreground/subagent-executor.js +135 -424
  252. package/dist/runs/foreground/subagent-params.js +67 -30
  253. package/dist/runs/foreground/wait-observation.js +104 -0
  254. package/dist/runs/foreground/wait-registration.js +17 -0
  255. package/dist/runs/foreground/wait-run.js +283 -123
  256. package/dist/runs/shared/acceptance-contract.js +193 -99
  257. package/dist/runs/shared/acceptance-defaults.js +1 -0
  258. package/dist/runs/shared/acceptance-evaluation.js +201 -181
  259. package/dist/runs/shared/acceptance-finalization.js +256 -180
  260. package/dist/runs/shared/acceptance-outcome.js +53 -0
  261. package/dist/runs/shared/acceptance-reports.js +137 -72
  262. package/dist/runs/shared/acceptance-submission.js +167 -0
  263. package/dist/runs/shared/acceptance-verification.js +100 -0
  264. package/dist/runs/shared/chain-outputs.js +80 -52
  265. package/dist/runs/shared/child-attempt-types.js +1 -0
  266. package/dist/runs/shared/child-attempt.js +314 -667
  267. package/dist/runs/shared/child-boundaries.js +173 -0
  268. package/dist/runs/shared/child-context-filter.js +44 -0
  269. package/dist/runs/shared/child-event-observer.js +390 -0
  270. package/dist/runs/shared/child-execution-cwd.js +73 -43
  271. package/dist/runs/shared/child-execution-outcome.js +72 -0
  272. package/dist/runs/shared/child-invocation.js +54 -0
  273. package/dist/runs/shared/child-message-validation.js +215 -0
  274. package/dist/runs/shared/child-native-accounting.js +106 -0
  275. package/dist/runs/shared/child-native-observation.js +69 -0
  276. package/dist/runs/shared/child-observation-spool.js +164 -0
  277. package/dist/runs/shared/child-observations.js +157 -76
  278. package/dist/runs/shared/child-presence.js +3 -0
  279. package/dist/runs/shared/child-route-env.js +119 -0
  280. package/dist/runs/shared/child-stream-observer.js +221 -0
  281. package/dist/runs/shared/child-stream-projection.js +137 -0
  282. package/dist/runs/shared/child-structured-tool.js +27 -0
  283. package/dist/runs/shared/claude-code.js +121 -158
  284. package/dist/runs/shared/claude-model.js +42 -0
  285. package/dist/runs/shared/claude-session.js +83 -0
  286. package/dist/runs/shared/claude-types.js +1 -0
  287. package/dist/runs/shared/completion-guard.js +18 -18
  288. package/dist/runs/shared/control-event-policy.js +8 -0
  289. package/dist/runs/shared/control-notification-owner.js +13 -0
  290. package/dist/runs/shared/dynamic-fanout.js +188 -173
  291. package/dist/runs/shared/dynamic-item.js +102 -0
  292. package/dist/runs/shared/history-index.js +62 -36
  293. package/dist/runs/shared/legacy-async-recovery.js +114 -0
  294. package/dist/runs/shared/legacy-output-recovery.js +148 -0
  295. package/dist/runs/shared/mcp-config-schema.js +36 -0
  296. package/dist/runs/shared/mcp-direct-tool-allowlist.js +147 -116
  297. package/dist/runs/shared/model-fallback.js +97 -38
  298. package/dist/runs/shared/mutating-tool-guard.js +128 -88
  299. package/dist/runs/shared/native-finalization-types.js +1 -0
  300. package/dist/runs/shared/native-finalization.js +155 -76
  301. package/dist/runs/shared/native-usage.js +238 -98
  302. package/dist/runs/shared/nested-control-records.js +128 -0
  303. package/dist/runs/shared/nested-events.js +8 -700
  304. package/dist/runs/shared/nested-path.js +32 -28
  305. package/dist/runs/shared/nested-projection.js +155 -0
  306. package/dist/runs/shared/nested-protocol.js +18 -0
  307. package/dist/runs/shared/nested-registry.js +191 -0
  308. package/dist/runs/shared/nested-render.js +106 -58
  309. package/dist/runs/shared/nested-route.js +220 -0
  310. package/dist/runs/shared/nested-validation.js +216 -0
  311. package/dist/runs/shared/owned-child-evidence.js +125 -0
  312. package/dist/runs/shared/owned-run-list.js +146 -0
  313. package/dist/runs/shared/owned-run-observation.js +267 -0
  314. package/dist/runs/shared/owned-run-progress.js +109 -0
  315. package/dist/runs/shared/owned-run-read-state.js +1 -0
  316. package/dist/runs/shared/owned-run-results.js +202 -0
  317. package/dist/runs/shared/owned-run-status.js +180 -0
  318. package/dist/runs/shared/owned-run-view.js +51 -0
  319. package/dist/runs/shared/owned-workflow-details.js +17 -0
  320. package/dist/runs/shared/owner-output.js +43 -0
  321. package/dist/runs/shared/parallel-utils.js +36 -28
  322. package/dist/runs/shared/parent-receipts.js +90 -32
  323. package/dist/runs/shared/parent-usage.js +169 -57
  324. package/dist/runs/shared/pi-args.js +108 -201
  325. package/dist/runs/shared/pi-launch-input.js +1 -0
  326. package/dist/runs/shared/pi-session-launch.js +86 -0
  327. package/dist/runs/shared/pi-spawn.js +35 -21
  328. package/dist/runs/shared/run-history-queries.js +126 -35
  329. package/dist/runs/shared/run-history.js +101 -35
  330. package/dist/runs/shared/run-metadata-paths.js +14 -0
  331. package/dist/runs/shared/run-metadata.js +158 -0
  332. package/dist/runs/shared/run-persistence.js +131 -0
  333. package/dist/runs/shared/run-records.js +9 -679
  334. package/dist/runs/shared/run-restoration.js +338 -0
  335. package/dist/runs/shared/run-state-owner.js +26 -0
  336. package/dist/runs/shared/session-cwd-preload.js +27 -18
  337. package/dist/runs/shared/single-output.js +148 -70
  338. package/dist/runs/shared/stable-value.js +15 -0
  339. package/dist/runs/shared/streaming-text.js +23 -12
  340. package/dist/runs/shared/structured-output.js +25 -13
  341. package/dist/runs/shared/subagent-control.js +167 -100
  342. package/dist/runs/shared/subagent-prompt-runtime.js +27 -130
  343. package/dist/runs/shared/subagent-tool-loop-guard.js +81 -52
  344. package/dist/runs/shared/supervisor-question-state.js +145 -0
  345. package/dist/runs/shared/supervisor-questions.js +185 -254
  346. package/dist/runs/shared/workflow-graph.js +259 -167
  347. package/dist/runs/shared/workflow-policy.js +120 -48
  348. package/dist/runs/shared/worktree-contract.js +1 -0
  349. package/dist/runs/shared/worktree-hook.js +138 -0
  350. package/dist/runs/shared/worktree-location.js +47 -0
  351. package/dist/runs/shared/worktree-preservation.js +8 -0
  352. package/dist/runs/shared/worktree-process.js +107 -0
  353. package/dist/runs/shared/worktree.js +183 -322
  354. package/dist/shared/agent-context-policy.js +34 -44
  355. package/dist/shared/agent-dir.js +14 -0
  356. package/dist/shared/artifacts.js +30 -12
  357. package/dist/shared/atomic-json.js +4 -1
  358. package/dist/shared/chain-files.js +72 -0
  359. package/dist/shared/concurrency.js +18 -0
  360. package/dist/shared/content-preview.js +115 -0
  361. package/dist/shared/cooperative.js +5 -2
  362. package/dist/shared/execution-cwd.js +13 -5
  363. package/dist/shared/file-coalescer.js +2 -1
  364. package/dist/shared/fork-context.js +10 -12
  365. package/dist/shared/formatters.js +45 -19
  366. package/dist/shared/journal-frames.js +164 -0
  367. package/dist/shared/journal-projections.js +121 -0
  368. package/dist/shared/journal-reader.js +6 -407
  369. package/dist/shared/json-projection.js +191 -0
  370. package/dist/shared/lazy-tools.js +19 -8
  371. package/dist/shared/message-output.js +116 -0
  372. package/dist/shared/model-info.js +31 -14
  373. package/dist/shared/native-import.js +20 -0
  374. package/dist/shared/native-journal.js +140 -0
  375. package/dist/shared/native-session.js +5 -2
  376. package/dist/shared/native-tui.js +7 -2
  377. package/dist/shared/native-typebox.js +15 -3
  378. package/dist/shared/output-page.js +63 -0
  379. package/dist/shared/post-exit-stdio-guard.js +128 -88
  380. package/dist/shared/prompt-sections.js +2 -0
  381. package/dist/shared/session-entries.js +70 -35
  382. package/dist/shared/session-identity.js +5 -3
  383. package/dist/shared/settings.js +62 -192
  384. package/dist/shared/status-format.js +97 -37
  385. package/dist/shared/task-file-policy.js +24 -0
  386. package/dist/shared/temp-root.js +44 -15
  387. package/dist/shared/types/acceptance.js +1 -0
  388. package/dist/shared/types/async.js +1 -0
  389. package/dist/shared/types/config.js +1 -0
  390. package/dist/shared/types/details.js +1 -0
  391. package/dist/shared/types/history.js +1 -0
  392. package/dist/shared/types/inputs.js +1 -0
  393. package/dist/shared/types/intercom.js +1 -0
  394. package/dist/shared/types/launch.js +1 -0
  395. package/dist/shared/types/messages.js +1 -0
  396. package/dist/shared/types/nested.js +1 -0
  397. package/dist/shared/types/owned-runs.js +1 -0
  398. package/dist/shared/types/progress.js +1 -0
  399. package/dist/shared/types/questions.js +1 -0
  400. package/dist/shared/types/results.js +1 -0
  401. package/dist/shared/types/state.js +1 -0
  402. package/dist/shared/types/usage.js +1 -0
  403. package/dist/shared/types/workflow.js +1 -0
  404. package/dist/shared/types.js +75 -30
  405. package/dist/shared/unknown.js +52 -0
  406. package/dist/shared/utils.js +54 -260
  407. package/dist/slash/command-discovery.js +82 -0
  408. package/dist/slash/command-input.js +190 -0
  409. package/dist/slash/prompt-template-bridge.js +94 -270
  410. package/dist/slash/prompt-template-contract.js +77 -0
  411. package/dist/slash/prompt-template-results.js +125 -0
  412. package/dist/slash/request-protocol.js +68 -0
  413. package/dist/slash/result-delivery.js +237 -0
  414. package/dist/slash/saved-chain.js +78 -0
  415. package/dist/slash/slash-bridge.js +123 -89
  416. package/dist/slash/slash-commands.js +101 -572
  417. package/dist/slash/slash-live-state.js +68 -47
  418. package/dist/tui/action-hints.js +50 -22
  419. package/dist/tui/activity-lines.js +32 -0
  420. package/dist/tui/agent-browser.js +290 -0
  421. package/dist/tui/agent-controls.js +289 -0
  422. package/dist/tui/agent-conversation.js +444 -0
  423. package/dist/tui/agent-dock.js +171 -0
  424. package/dist/tui/agent-history.js +50 -183
  425. package/dist/tui/agent-model.js +62 -0
  426. package/dist/tui/agent-picker.js +262 -0
  427. package/dist/tui/agent-view.js +232 -1537
  428. package/dist/tui/compact-results.js +193 -0
  429. package/dist/tui/conversation-details.js +190 -0
  430. package/dist/tui/conversation-frame.js +135 -0
  431. package/dist/tui/conversation-history.js +201 -0
  432. package/dist/tui/conversation-menu.js +78 -0
  433. package/dist/tui/conversation-notices.js +79 -0
  434. package/dist/tui/conversation-selection.js +58 -0
  435. package/dist/tui/conversation-viewport.js +129 -0
  436. package/dist/tui/direction-message.js +27 -0
  437. package/dist/tui/display.js +282 -0
  438. package/dist/tui/editor-viewport.js +18 -0
  439. package/dist/tui/expanded-content.js +87 -0
  440. package/dist/tui/expanded-multi.js +170 -0
  441. package/dist/tui/expanded-single.js +65 -0
  442. package/dist/tui/history-cards.js +216 -0
  443. package/dist/tui/history-display.js +238 -0
  444. package/dist/tui/history-json.js +16 -0
  445. package/dist/tui/history-metadata.js +152 -0
  446. package/dist/tui/history-record.js +251 -0
  447. package/dist/tui/history-text.js +34 -0
  448. package/dist/tui/indexed-history.js +116 -0
  449. package/dist/tui/nested-widget.js +120 -0
  450. package/dist/tui/picker-page.js +59 -0
  451. package/dist/tui/render.js +58 -1501
  452. package/dist/tui/result-facts.js +64 -0
  453. package/dist/tui/task-observation.js +59 -0
  454. package/dist/tui/task-store.js +366 -0
  455. package/dist/tui/text-values.js +16 -0
  456. package/dist/tui/view-connections.js +47 -0
  457. package/dist/tui/view-model.js +94 -0
  458. package/dist/tui/view-persistence.js +51 -0
  459. package/dist/tui/view-ports.js +1 -0
  460. package/dist/tui/view-session.js +37 -0
  461. package/dist/tui/widget-details.js +144 -0
  462. package/dist/tui/widget-status.js +245 -0
  463. package/dist/tui/widget.js +145 -0
  464. package/dist/tui/workflow-labels.js +223 -0
  465. package/dist/tui/workflow-outcomes.js +124 -0
  466. package/docs/code-quality.md +38 -0
  467. package/docs/development.md +99 -0
  468. package/docs/intercom-bridge.md +23 -18
  469. package/docs/intercom.md +132 -106
  470. package/docs/performance.md +19 -17
  471. package/docs/reference.md +1472 -0
  472. package/generated/RunValidators.d.ts +28 -0
  473. package/generated/RunValidators.js +2 -0
  474. package/package.json +27 -6
  475. package/prompts/parallel-cleanup.md +3 -0
  476. package/prompts/parallel-context-build.md +3 -0
  477. package/prompts/parallel-handoff-plan.md +6 -0
  478. package/prompts/parallel-research.md +4 -0
  479. package/prompts/parallel-review.md +2 -0
  480. package/prompts/review-loop.md +2 -0
  481. package/scripts/build.mjs +129 -85
  482. package/scripts/check-compat.mjs +48 -11
  483. package/scripts/compat-host.mjs +35 -7
  484. package/scripts/generate-run-schemas.mjs +210 -0
  485. package/scripts/intercom-bridge-credentials.mjs +155 -28
  486. package/scripts/intercom-remote.mjs +175 -50
  487. package/scripts/local-install-smoke.mjs +47 -37
  488. package/scripts/native-package-smoke.mjs +135 -25
  489. package/scripts/package-smoke.mjs +360 -140
  490. package/scripts/prepare.mjs +54 -34
  491. package/scripts/real-pi-processes.mjs +261 -0
  492. package/scripts/real-pi-smoke.mjs +432 -351
  493. package/scripts/run-tests.mjs +29 -15
  494. package/skills/pi-intercom/SKILL.md +11 -11
  495. package/skills/pi-intercom/references/peer-sessions.md +7 -2
  496. package/skills/pi-subagents/SKILL.md +1 -1
  497. package/skills/pi-subagents/references/full-orchestration-guide.md +254 -112
  498. package/src/agents/agent-defaults.ts +13 -0
  499. package/src/agents/agent-definition.ts +253 -0
  500. package/src/agents/agent-management.ts +111 -808
  501. package/src/agents/agent-scope.ts +7 -3
  502. package/src/agents/agent-selection.ts +26 -16
  503. package/src/agents/agent-serializer.ts +84 -93
  504. package/src/agents/agents.ts +199 -906
  505. package/src/agents/builtin-overrides.ts +297 -0
  506. package/src/agents/chain-serializer.ts +3 -329
  507. package/src/agents/config-values.ts +57 -0
  508. package/src/agents/discovery-paths.ts +134 -0
  509. package/src/agents/frontmatter.ts +35 -17
  510. package/src/agents/identity.ts +49 -18
  511. package/src/agents/json-chain.ts +305 -0
  512. package/src/agents/management-config.ts +342 -0
  513. package/src/agents/management-format.ts +243 -0
  514. package/src/agents/management-persistence.ts +408 -0
  515. package/src/agents/management-targets.ts +260 -0
  516. package/src/agents/management-warnings.ts +115 -0
  517. package/src/agents/markdown-chain.ts +237 -0
  518. package/src/agents/package-agents.ts +231 -0
  519. package/src/agents/skill-files.ts +182 -0
  520. package/src/agents/skill-search-paths.ts +289 -0
  521. package/src/agents/skill-types.ts +31 -0
  522. package/src/agents/skills.ts +152 -610
  523. package/src/extension/acceptance-schema.ts +88 -0
  524. package/src/extension/chain-schema.ts +276 -0
  525. package/src/extension/child-runtime.ts +302 -0
  526. package/src/extension/compact-tools.ts +259 -102
  527. package/src/extension/config-schema.ts +59 -0
  528. package/src/extension/config.ts +39 -17
  529. package/src/extension/control-notice-schema.ts +55 -0
  530. package/src/extension/control-notices.ts +57 -33
  531. package/src/extension/delegation-bridges.ts +59 -0
  532. package/src/extension/doctor.ts +171 -135
  533. package/src/extension/everyday-schemas.ts +252 -0
  534. package/src/extension/fanout-child.ts +29 -308
  535. package/src/extension/index.ts +17 -642
  536. package/src/extension/message-renderers.ts +334 -0
  537. package/src/extension/nested-control-inbox.ts +196 -0
  538. package/src/extension/parent-runtime.ts +405 -0
  539. package/src/extension/primary-tool.ts +130 -0
  540. package/src/extension/result-snapshot.ts +35 -0
  541. package/src/extension/runtime-reload.ts +72 -0
  542. package/src/extension/runtime-storage.ts +49 -0
  543. package/src/extension/schema-overrides.ts +114 -0
  544. package/src/extension/schemas.ts +451 -418
  545. package/src/extension/tool-input.ts +40 -13
  546. package/src/extension/tool-result.ts +45 -14
  547. package/src/history/answer-texts.ts +74 -0
  548. package/src/history/canonical-result.ts +127 -51
  549. package/src/history/canonical-selection.ts +54 -0
  550. package/src/history/database-generation.ts +86 -0
  551. package/src/history/database.ts +86 -48
  552. package/src/history/entry-ingestion.ts +129 -0
  553. package/src/history/index.ts +426 -148
  554. package/src/history/ingest.ts +372 -342
  555. package/src/history/output-digests.ts +41 -0
  556. package/src/history/preview.ts +235 -0
  557. package/src/history/prune.ts +48 -0
  558. package/src/history/queries.ts +122 -256
  559. package/src/history/query-scope.ts +278 -0
  560. package/src/history/rows.ts +118 -0
  561. package/src/history/run-projection.ts +164 -0
  562. package/src/history/run-queries.ts +199 -0
  563. package/src/history/schema.ts +34 -0
  564. package/src/history/search-queries.ts +216 -0
  565. package/src/history/selected-record.ts +140 -0
  566. package/src/history/source-file.ts +51 -0
  567. package/src/history/source-watches.ts +156 -0
  568. package/src/history/store.ts +310 -197
  569. package/src/history/text-chunks.ts +181 -0
  570. package/src/history/text.ts +43 -0
  571. package/src/history/transcript-queries.ts +251 -0
  572. package/src/history/types.ts +27 -164
  573. package/src/history/values.ts +36 -0
  574. package/src/history/wire-inputs.ts +141 -0
  575. package/src/history/wire-models.ts +51 -0
  576. package/src/history/worker-runtime.ts +502 -0
  577. package/src/history/worker.ts +94 -336
  578. package/src/intercom/intercom-bridge.ts +136 -69
  579. package/src/intercom/live-intercom.ts +197 -92
  580. package/src/intercom/nested-result-tree.ts +213 -0
  581. package/src/intercom/result-delivery.ts +56 -0
  582. package/src/intercom/result-intercom.ts +364 -382
  583. package/src/pi-intercom/agent-dir.ts +9 -3
  584. package/src/pi-intercom/async.ts +33 -0
  585. package/src/pi-intercom/bridge-ask.ts +64 -0
  586. package/src/pi-intercom/bridge-config.ts +101 -0
  587. package/src/pi-intercom/bridge-http.ts +97 -0
  588. package/src/pi-intercom/bridge-protocol.ts +123 -0
  589. package/src/pi-intercom/bridge-routes.ts +90 -0
  590. package/src/pi-intercom/bridge-session.ts +506 -0
  591. package/src/pi-intercom/bridge.ts +258 -547
  592. package/src/pi-intercom/broker/broker.ts +326 -263
  593. package/src/pi-intercom/broker/client.ts +273 -354
  594. package/src/pi-intercom/broker/connection.ts +192 -0
  595. package/src/pi-intercom/broker/framing.ts +11 -6
  596. package/src/pi-intercom/broker/paths.ts +28 -25
  597. package/src/pi-intercom/broker/pid.ts +68 -24
  598. package/src/pi-intercom/broker/snapshot-reader.ts +91 -0
  599. package/src/pi-intercom/broker/snapshots.ts +241 -0
  600. package/src/pi-intercom/broker/spawn.ts +58 -34
  601. package/src/pi-intercom/config.ts +113 -108
  602. package/src/pi-intercom/connection.ts +346 -0
  603. package/src/pi-intercom/identity.ts +71 -0
  604. package/src/pi-intercom/inbound-delivery.ts +450 -0
  605. package/src/pi-intercom/inbound-journal.ts +350 -0
  606. package/src/pi-intercom/inbound-record.ts +141 -0
  607. package/src/pi-intercom/inbound.ts +237 -0
  608. package/src/pi-intercom/index.ts +86 -2923
  609. package/src/pi-intercom/inspection.ts +82 -0
  610. package/src/pi-intercom/interview.ts +295 -0
  611. package/src/pi-intercom/lifecycle.ts +183 -0
  612. package/src/pi-intercom/message-format.ts +77 -0
  613. package/src/pi-intercom/outbound.ts +418 -0
  614. package/src/pi-intercom/peer-awareness.ts +84 -0
  615. package/src/pi-intercom/reply-tracker.ts +121 -59
  616. package/src/pi-intercom/reply-wait.ts +247 -0
  617. package/src/pi-intercom/runtime-types.ts +97 -0
  618. package/src/pi-intercom/session-events.ts +247 -0
  619. package/src/pi-intercom/session-targets.ts +124 -72
  620. package/src/pi-intercom/subagent-events.ts +299 -0
  621. package/src/pi-intercom/subagent-live-events.ts +212 -109
  622. package/src/pi-intercom/supervisor.ts +362 -0
  623. package/src/pi-intercom/tool-arguments.ts +120 -0
  624. package/src/pi-intercom/tool-registration.ts +121 -0
  625. package/src/pi-intercom/tool-results.ts +54 -0
  626. package/src/pi-intercom/tool-schemas.ts +134 -0
  627. package/src/pi-intercom/tools.ts +80 -0
  628. package/src/pi-intercom/topic-actions.ts +243 -0
  629. package/src/pi-intercom/topics.ts +267 -121
  630. package/src/pi-intercom/transport.ts +14 -0
  631. package/src/pi-intercom/types.ts +288 -190
  632. package/src/pi-intercom/ui/compose-paste.ts +109 -0
  633. package/src/pi-intercom/ui/compose.ts +173 -157
  634. package/src/pi-intercom/ui/inline-message.ts +80 -62
  635. package/src/pi-intercom/ui/message-renderers.ts +182 -0
  636. package/src/pi-intercom/ui/overlay.ts +238 -0
  637. package/src/pi-intercom/ui/session-format.ts +146 -0
  638. package/src/pi-intercom/ui/session-list.ts +145 -62
  639. package/src/pi-intercom/ui/tool-renderers.ts +148 -0
  640. package/src/pi-intercom/ui/topic-view.ts +107 -0
  641. package/src/pi-intercom/validation.ts +69 -0
  642. package/src/runs/background/async-chain-plan.ts +451 -0
  643. package/src/runs/background/async-control.ts +201 -71
  644. package/src/runs/background/async-execution.ts +206 -829
  645. package/src/runs/background/async-job-admission.ts +54 -0
  646. package/src/runs/background/async-job-projection.ts +225 -0
  647. package/src/runs/background/async-job-tracker.ts +416 -360
  648. package/src/runs/background/async-launch.ts +319 -0
  649. package/src/runs/background/async-plan.ts +285 -0
  650. package/src/runs/background/async-result-file.ts +79 -37
  651. package/src/runs/background/async-resume.ts +338 -285
  652. package/src/runs/background/async-run-discovery.ts +270 -0
  653. package/src/runs/background/async-run-location.ts +194 -0
  654. package/src/runs/background/async-run-record.ts +14 -0
  655. package/src/runs/background/async-run-repair.ts +307 -0
  656. package/src/runs/background/async-run-summary.ts +71 -0
  657. package/src/runs/background/async-single-plan.ts +168 -0
  658. package/src/runs/background/async-status-projection.ts +188 -0
  659. package/src/runs/background/async-status-render.ts +164 -0
  660. package/src/runs/background/async-status-validation.ts +238 -0
  661. package/src/runs/background/async-status.ts +9 -481
  662. package/src/runs/background/async-value.ts +29 -0
  663. package/src/runs/background/completion-dedupe.ts +103 -54
  664. package/src/runs/background/completion-delivery.ts +313 -187
  665. package/src/runs/background/completion-receipts.ts +309 -0
  666. package/src/runs/background/legacy-status-acceptance.ts +38 -0
  667. package/src/runs/background/notify.ts +167 -106
  668. package/src/runs/background/parallel-groups.ts +95 -46
  669. package/src/runs/background/result-payload.ts +268 -0
  670. package/src/runs/background/result-watcher.ts +484 -387
  671. package/src/runs/background/run-id-resolver.ts +126 -57
  672. package/src/runs/background/run-schemas.ts +240 -0
  673. package/src/runs/background/run-status-contracts.ts +46 -0
  674. package/src/runs/background/run-status-render.ts +364 -0
  675. package/src/runs/background/run-status.ts +338 -356
  676. package/src/runs/background/runner-attempt-output.ts +281 -0
  677. package/src/runs/background/runner-attempt.ts +354 -0
  678. package/src/runs/background/runner-child-executor.ts +232 -0
  679. package/src/runs/background/runner-child-observer.ts +270 -0
  680. package/src/runs/background/runner-completion-summary.ts +106 -0
  681. package/src/runs/background/runner-completion.ts +274 -0
  682. package/src/runs/background/runner-config.ts +52 -0
  683. package/src/runs/background/runner-contract.ts +51 -0
  684. package/src/runs/background/runner-dynamic.ts +185 -0
  685. package/src/runs/background/runner-finalization.ts +203 -0
  686. package/src/runs/background/runner-graph.ts +91 -0
  687. package/src/runs/background/runner-initial-status.ts +72 -0
  688. package/src/runs/background/runner-lifecycle.ts +159 -0
  689. package/src/runs/background/runner-monitor.ts +399 -0
  690. package/src/runs/background/runner-parallel.ts +280 -0
  691. package/src/runs/background/runner-prepare-step.ts +128 -0
  692. package/src/runs/background/runner-reporting.ts +192 -0
  693. package/src/runs/background/runner-status.ts +220 -0
  694. package/src/runs/background/runner-step-output.ts +158 -0
  695. package/src/runs/background/runner-step-record.ts +76 -0
  696. package/src/runs/background/runner-step.ts +150 -0
  697. package/src/runs/background/runner-streaming.ts +220 -0
  698. package/src/runs/background/runner-workflow.ts +284 -0
  699. package/src/runs/background/stale-run-reconciler.ts +279 -336
  700. package/src/runs/background/subagent-runner-launcher.ts +67 -35
  701. package/src/runs/background/subagent-runner.ts +80 -2048
  702. package/src/runs/background/top-level-async.ts +9 -7
  703. package/src/runs/foreground/chain-clarify.ts +499 -1334
  704. package/src/runs/foreground/clarification-draft.ts +92 -0
  705. package/src/runs/foreground/clarify-contracts.ts +33 -0
  706. package/src/runs/foreground/clarify-invocation.ts +192 -65
  707. package/src/runs/foreground/clarify-text-editor.ts +229 -0
  708. package/src/runs/foreground/clarify-view.ts +222 -0
  709. package/src/runs/foreground/execution-input.ts +358 -424
  710. package/src/runs/foreground/execution-routing.ts +41 -0
  711. package/src/runs/foreground/execution-timeout.ts +92 -0
  712. package/src/runs/foreground/foreground-control.ts +333 -812
  713. package/src/runs/foreground/foreground-memory.ts +132 -0
  714. package/src/runs/foreground/invocation-context.ts +88 -0
  715. package/src/runs/foreground/invocation-execution.ts +465 -0
  716. package/src/runs/foreground/invocation-expansion.ts +58 -0
  717. package/src/runs/foreground/management-actions.ts +360 -0
  718. package/src/runs/foreground/nested-control.ts +248 -0
  719. package/src/runs/foreground/question-continuation.ts +112 -0
  720. package/src/runs/foreground/question-control.ts +297 -70
  721. package/src/runs/foreground/resume-target.ts +330 -0
  722. package/src/runs/foreground/revival-launch-policy.ts +112 -0
  723. package/src/runs/foreground/run-async-path.ts +307 -223
  724. package/src/runs/foreground/run-interrupt.ts +202 -0
  725. package/src/runs/foreground/run-nudge.ts +355 -0
  726. package/src/runs/foreground/saved-revival.ts +356 -0
  727. package/src/runs/foreground/subagent-executor.ts +195 -493
  728. package/src/runs/foreground/subagent-params.ts +314 -242
  729. package/src/runs/foreground/wait-observation.ts +160 -0
  730. package/src/runs/foreground/wait-registration.ts +18 -0
  731. package/src/runs/foreground/wait-run.ts +361 -119
  732. package/src/runs/shared/acceptance-contract.ts +410 -209
  733. package/src/runs/shared/acceptance-defaults.ts +1 -0
  734. package/src/runs/shared/acceptance-evaluation.ts +314 -240
  735. package/src/runs/shared/acceptance-finalization.ts +447 -305
  736. package/src/runs/shared/acceptance-outcome.ts +69 -0
  737. package/src/runs/shared/acceptance-reports.ts +220 -126
  738. package/src/runs/shared/acceptance-submission.ts +251 -0
  739. package/src/runs/shared/acceptance-verification.ts +118 -0
  740. package/src/runs/shared/acceptance.ts +25 -25
  741. package/src/runs/shared/chain-outputs.ts +190 -98
  742. package/src/runs/shared/child-attempt-types.ts +118 -0
  743. package/src/runs/shared/child-attempt.ts +372 -608
  744. package/src/runs/shared/child-boundaries.ts +217 -0
  745. package/src/runs/shared/child-context-filter.ts +53 -0
  746. package/src/runs/shared/child-event-observer.ts +496 -0
  747. package/src/runs/shared/child-execution-cwd.ts +139 -68
  748. package/src/runs/shared/child-execution-outcome.ts +103 -0
  749. package/src/runs/shared/child-invocation.ts +74 -0
  750. package/src/runs/shared/child-message-validation.ts +233 -0
  751. package/src/runs/shared/child-native-accounting.ts +154 -0
  752. package/src/runs/shared/child-native-observation.ts +79 -0
  753. package/src/runs/shared/child-observation-spool.ts +188 -0
  754. package/src/runs/shared/child-observations.ts +219 -89
  755. package/src/runs/shared/child-presence.ts +6 -0
  756. package/src/runs/shared/child-route-env.ts +142 -0
  757. package/src/runs/shared/child-stream-observer.ts +302 -0
  758. package/src/runs/shared/child-stream-projection.ts +160 -0
  759. package/src/runs/shared/child-structured-tool.ts +38 -0
  760. package/src/runs/shared/claude-code.ts +201 -228
  761. package/src/runs/shared/claude-model.ts +67 -0
  762. package/src/runs/shared/claude-session.ts +124 -0
  763. package/src/runs/shared/claude-types.ts +31 -0
  764. package/src/runs/shared/completion-guard.ts +35 -26
  765. package/src/runs/shared/control-event-policy.ts +15 -0
  766. package/src/runs/shared/control-notification-owner.ts +20 -0
  767. package/src/runs/shared/dynamic-fanout.ts +402 -238
  768. package/src/runs/shared/dynamic-item.ts +114 -0
  769. package/src/runs/shared/history-index.ts +112 -51
  770. package/src/runs/shared/legacy-async-recovery.ts +153 -0
  771. package/src/runs/shared/legacy-output-recovery.ts +214 -0
  772. package/src/runs/shared/mcp-config-schema.ts +46 -0
  773. package/src/runs/shared/mcp-direct-tool-allowlist.ts +396 -307
  774. package/src/runs/shared/model-fallback.ts +302 -180
  775. package/src/runs/shared/mutating-tool-guard.ts +259 -158
  776. package/src/runs/shared/native-finalization-types.ts +29 -0
  777. package/src/runs/shared/native-finalization.ts +233 -109
  778. package/src/runs/shared/native-usage.ts +379 -96
  779. package/src/runs/shared/nested-control-records.ts +178 -0
  780. package/src/runs/shared/nested-events.ts +45 -752
  781. package/src/runs/shared/nested-path.ts +47 -34
  782. package/src/runs/shared/nested-projection.ts +230 -0
  783. package/src/runs/shared/nested-protocol.ts +67 -0
  784. package/src/runs/shared/nested-registry.ts +273 -0
  785. package/src/runs/shared/nested-render.ts +198 -100
  786. package/src/runs/shared/nested-route.ts +292 -0
  787. package/src/runs/shared/nested-validation.ts +291 -0
  788. package/src/runs/shared/owned-child-evidence.ts +184 -0
  789. package/src/runs/shared/owned-run-list.ts +187 -0
  790. package/src/runs/shared/owned-run-observation.ts +368 -0
  791. package/src/runs/shared/owned-run-progress.ts +172 -0
  792. package/src/runs/shared/owned-run-read-state.ts +7 -0
  793. package/src/runs/shared/owned-run-results.ts +289 -0
  794. package/src/runs/shared/owned-run-status.ts +239 -0
  795. package/src/runs/shared/owned-run-view.ts +65 -0
  796. package/src/runs/shared/owned-workflow-details.ts +24 -0
  797. package/src/runs/shared/owner-output.ts +73 -0
  798. package/src/runs/shared/parallel-utils.ts +93 -129
  799. package/src/runs/shared/parent-receipts.ts +177 -50
  800. package/src/runs/shared/parent-usage.ts +314 -91
  801. package/src/runs/shared/pi-args.ts +241 -311
  802. package/src/runs/shared/pi-launch-input.ts +48 -0
  803. package/src/runs/shared/pi-session-launch.ts +94 -0
  804. package/src/runs/shared/pi-spawn.ts +57 -33
  805. package/src/runs/shared/run-history-queries.ts +189 -38
  806. package/src/runs/shared/run-history.ts +197 -76
  807. package/src/runs/shared/run-id-ambiguity.ts +7 -3
  808. package/src/runs/shared/run-metadata-paths.ts +17 -0
  809. package/src/runs/shared/run-metadata.ts +240 -0
  810. package/src/runs/shared/run-persistence.ts +181 -0
  811. package/src/runs/shared/run-records.ts +19 -607
  812. package/src/runs/shared/run-restoration.ts +455 -0
  813. package/src/runs/shared/run-state-owner.ts +31 -0
  814. package/src/runs/shared/session-cwd-preload.ts +39 -19
  815. package/src/runs/shared/single-output.ts +358 -205
  816. package/src/runs/shared/stable-value.ts +15 -0
  817. package/src/runs/shared/streaming-text.ts +39 -12
  818. package/src/runs/shared/structured-output.ts +76 -46
  819. package/src/runs/shared/subagent-control.ts +330 -197
  820. package/src/runs/shared/subagent-prompt-runtime.ts +92 -174
  821. package/src/runs/shared/subagent-tool-loop-guard.ts +118 -68
  822. package/src/runs/shared/supervisor-question-state.ts +221 -0
  823. package/src/runs/shared/supervisor-questions.ts +396 -401
  824. package/src/runs/shared/workflow-graph.ts +400 -198
  825. package/src/runs/shared/workflow-policy.ts +245 -110
  826. package/src/runs/shared/worktree-contract.ts +29 -0
  827. package/src/runs/shared/worktree-hook.ts +181 -0
  828. package/src/runs/shared/worktree-location.ts +61 -0
  829. package/src/runs/shared/worktree-preservation.ts +10 -0
  830. package/src/runs/shared/worktree-process.ts +132 -0
  831. package/src/runs/shared/worktree.ts +459 -587
  832. package/src/shared/agent-context-policy.ts +176 -145
  833. package/src/shared/agent-dir.ts +15 -0
  834. package/src/shared/artifacts.ts +99 -68
  835. package/src/shared/atomic-json.ts +14 -11
  836. package/src/shared/chain-files.ts +72 -0
  837. package/src/shared/concurrency.ts +27 -0
  838. package/src/shared/content-preview.ts +117 -0
  839. package/src/shared/cooperative.ts +17 -13
  840. package/src/shared/execution-cwd.ts +34 -9
  841. package/src/shared/file-coalescer.ts +24 -22
  842. package/src/shared/fork-context.ts +52 -48
  843. package/src/shared/formatters.ts +88 -47
  844. package/src/shared/journal-frames.ts +197 -0
  845. package/src/shared/journal-projections.ts +133 -0
  846. package/src/shared/journal-reader.ts +18 -333
  847. package/src/shared/json-projection.ts +224 -0
  848. package/src/shared/lazy-tools.ts +76 -27
  849. package/src/shared/message-output.ts +146 -0
  850. package/src/shared/model-info.ts +94 -58
  851. package/src/shared/native-import.ts +25 -0
  852. package/src/shared/native-journal.ts +172 -0
  853. package/src/shared/native-session.ts +8 -3
  854. package/src/shared/native-tui.ts +13 -3
  855. package/src/shared/native-typebox.ts +18 -3
  856. package/src/shared/output-page.ts +76 -0
  857. package/src/shared/post-exit-stdio-guard.ts +232 -172
  858. package/src/shared/prompt-sections.ts +13 -7
  859. package/src/shared/session-entries.ts +103 -44
  860. package/src/shared/session-identity.ts +17 -10
  861. package/src/shared/settings.ts +182 -367
  862. package/src/shared/status-format.ts +207 -98
  863. package/src/shared/stream-json.d.ts +10 -0
  864. package/src/shared/task-file-policy.ts +35 -0
  865. package/src/shared/temp-root.ts +122 -71
  866. package/src/shared/types/acceptance.ts +149 -0
  867. package/src/shared/types/async.ts +247 -0
  868. package/src/shared/types/config.ts +139 -0
  869. package/src/shared/types/details.ts +104 -0
  870. package/src/shared/types/history.ts +217 -0
  871. package/src/shared/types/inputs.ts +4 -0
  872. package/src/shared/types/intercom.ts +57 -0
  873. package/src/shared/types/launch.ts +39 -0
  874. package/src/shared/types/messages.ts +104 -0
  875. package/src/shared/types/nested.ts +153 -0
  876. package/src/shared/types/owned-runs.ts +148 -0
  877. package/src/shared/types/progress.ts +106 -0
  878. package/src/shared/types/questions.ts +103 -0
  879. package/src/shared/types/results.ts +119 -0
  880. package/src/shared/types/state.ts +56 -0
  881. package/src/shared/types/usage.ts +36 -0
  882. package/src/shared/types/workflow.ts +187 -0
  883. package/src/shared/types.ts +222 -1161
  884. package/src/shared/ui-mode.ts +1 -1
  885. package/src/shared/unknown.ts +59 -0
  886. package/src/shared/utils.ts +111 -305
  887. package/src/slash/command-discovery.ts +109 -0
  888. package/src/slash/command-input.ts +232 -0
  889. package/src/slash/prompt-template-bridge.ts +153 -400
  890. package/src/slash/prompt-template-contract.ts +152 -0
  891. package/src/slash/prompt-template-results.ts +189 -0
  892. package/src/slash/request-protocol.ts +87 -0
  893. package/src/slash/result-delivery.ts +312 -0
  894. package/src/slash/saved-chain.ts +115 -0
  895. package/src/slash/slash-bridge.ts +202 -156
  896. package/src/slash/slash-commands.ts +176 -598
  897. package/src/slash/slash-live-state.ts +287 -229
  898. package/src/tui/action-hints.ts +147 -61
  899. package/src/tui/activity-lines.ts +40 -0
  900. package/src/tui/agent-browser.ts +354 -0
  901. package/src/tui/agent-controls.ts +355 -0
  902. package/src/tui/agent-conversation.ts +514 -0
  903. package/src/tui/agent-dock.ts +250 -0
  904. package/src/tui/agent-history.ts +78 -203
  905. package/src/tui/agent-model.ts +78 -0
  906. package/src/tui/agent-picker.ts +325 -0
  907. package/src/tui/agent-view.ts +391 -1310
  908. package/src/tui/compact-results.ts +342 -0
  909. package/src/tui/conversation-details.ts +206 -0
  910. package/src/tui/conversation-frame.ts +184 -0
  911. package/src/tui/conversation-history.ts +246 -0
  912. package/src/tui/conversation-menu.ts +119 -0
  913. package/src/tui/conversation-notices.ts +116 -0
  914. package/src/tui/conversation-selection.ts +66 -0
  915. package/src/tui/conversation-viewport.ts +169 -0
  916. package/src/tui/direction-message.ts +35 -0
  917. package/src/tui/display.ts +409 -0
  918. package/src/tui/editor-viewport.ts +19 -0
  919. package/src/tui/expanded-content.ts +134 -0
  920. package/src/tui/expanded-multi.ts +264 -0
  921. package/src/tui/expanded-single.ts +92 -0
  922. package/src/tui/history-cards.ts +316 -0
  923. package/src/tui/history-display.ts +334 -0
  924. package/src/tui/history-json.ts +17 -0
  925. package/src/tui/history-metadata.ts +177 -0
  926. package/src/tui/history-record.ts +321 -0
  927. package/src/tui/history-text.ts +34 -0
  928. package/src/tui/indexed-history.ts +149 -0
  929. package/src/tui/nested-widget.ts +192 -0
  930. package/src/tui/picker-page.ts +80 -0
  931. package/src/tui/render.ts +100 -1546
  932. package/src/tui/result-facts.ts +99 -0
  933. package/src/tui/task-observation.ts +96 -0
  934. package/src/tui/task-store.ts +432 -0
  935. package/src/tui/text-values.ts +20 -0
  936. package/src/tui/view-connections.ts +64 -0
  937. package/src/tui/view-model.ts +181 -0
  938. package/src/tui/view-persistence.ts +57 -0
  939. package/src/tui/view-ports.ts +55 -0
  940. package/src/tui/view-session.ts +42 -0
  941. package/src/tui/widget-details.ts +220 -0
  942. package/src/tui/widget-status.ts +331 -0
  943. package/src/tui/widget.ts +208 -0
  944. package/src/tui/workflow-labels.ts +340 -0
  945. package/src/tui/workflow-outcomes.ts +150 -0
  946. package/tsconfig.build.json +11 -10
  947. package/tsconfig.json +30 -17
@@ -1,380 +1,71 @@
1
- /**
2
- * Chain Clarification TUI Component
3
- *
4
- * Shows templates and resolved behaviors for each step in a chain.
5
- * Supports runtime editing of templates, output paths, reads lists, and progress toggle.
6
- */
7
- import { matchesKey, visibleWidth, truncateToWidth } from "@earendil-works/pi-tui";
1
+ import { matchesKey, truncateToWidth } from "@earendil-works/pi-tui";
2
+ import { ClarifyView } from "./clarify-view.js";
3
+ import { findModelInfo, getSupportedThinkingLevels, } from "../../shared/model-info.js";
8
4
  import { resolveModelCandidate, splitThinkingSuffix } from "../shared/model-fallback.js";
9
- import { findModelInfo, getSupportedThinkingLevels } from "../../shared/model-info.js";
10
- function createEditorState(initial = "") {
11
- return { buffer: initial, cursor: 0, viewportOffset: 0 };
12
- }
13
- function wrapText(text, width) {
14
- if (width <= 0)
15
- return { lines: [text], starts: [0] };
16
- if (text.length === 0)
17
- return { lines: [""], starts: [0] };
18
- const lines = [];
19
- const starts = [];
20
- let offset = 0;
21
- const segments = text.split("\n");
22
- for (const [index, segment] of segments.entries()) {
23
- if (segment.length === 0) {
24
- starts.push(offset);
25
- lines.push("");
26
- }
27
- else {
28
- let lineStart = 0;
29
- let pos = 0;
30
- let lineWidth = 0;
31
- while (pos < segment.length) {
32
- const char = String.fromCodePoint(segment.codePointAt(pos));
33
- const charWidth = visibleWidth(char);
34
- if (lineWidth > 0 && lineWidth + charWidth > width) {
35
- starts.push(offset + lineStart);
36
- lines.push(segment.slice(lineStart, pos));
37
- lineStart = pos;
38
- lineWidth = 0;
39
- continue;
40
- }
41
- pos += char.length;
42
- lineWidth += charWidth;
43
- }
44
- starts.push(offset + lineStart);
45
- lines.push(segment.slice(lineStart));
46
- }
47
- offset += segment.length + (index < segments.length - 1 ? 1 : 0);
48
- }
49
- if (!text.endsWith("\n") && text.length > 0 && visibleWidth(lines[lines.length - 1] ?? "") === width) {
50
- starts.push(text.length);
51
- lines.push("");
52
- }
53
- return { lines, starts };
54
- }
55
- function getCursorDisplayPos(cursor, starts) {
56
- for (let i = starts.length - 1; i >= 0; i--) {
57
- if (cursor >= starts[i])
58
- return { line: i, col: cursor - starts[i] };
59
- }
60
- return { line: 0, col: 0 };
61
- }
62
- function ensureCursorVisible(cursorLine, viewportHeight, currentOffset) {
63
- if (cursorLine < currentOffset)
64
- return Math.max(0, cursorLine);
65
- if (cursorLine >= currentOffset + viewportHeight)
66
- return Math.max(0, cursorLine - viewportHeight + 1);
67
- return Math.max(0, currentOffset);
68
- }
69
- function isWordChar(ch) {
70
- const code = ch.charCodeAt(0);
71
- return (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === 95;
72
- }
73
- function wordBackward(buffer, cursor) {
74
- let pos = cursor;
75
- while (pos > 0 && !isWordChar(buffer[pos - 1]))
76
- pos--;
77
- while (pos > 0 && isWordChar(buffer[pos - 1]))
78
- pos--;
79
- return pos;
80
- }
81
- function wordForward(buffer, cursor) {
82
- let pos = cursor;
83
- while (pos < buffer.length && isWordChar(buffer[pos]))
84
- pos++;
85
- while (pos < buffer.length && !isWordChar(buffer[pos]))
86
- pos++;
87
- return pos;
88
- }
89
- function normalizeInsertText(data) {
90
- let text = data.split("\x1b[200~").join("").split("\x1b[201~").join("");
91
- text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
92
- const newline = text.indexOf("\n");
93
- if (newline !== -1)
94
- text = text.slice(0, newline);
95
- text = text.replace(/\t/g, " ");
96
- if (text.length === 0)
97
- return null;
98
- for (let i = 0; i < text.length; i++) {
99
- if (text.charCodeAt(i) < 32)
100
- return null;
101
- }
102
- return text;
103
- }
104
- function handleEditorInput(state, data, textWidth) {
105
- if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "return"))
106
- return null;
107
- const { lines: wrapped, starts } = wrapText(state.buffer, textWidth);
108
- const cursorPos = getCursorDisplayPos(state.cursor, starts);
109
- if (matchesKey(data, "alt+left") || matchesKey(data, "ctrl+left"))
110
- return { ...state, cursor: wordBackward(state.buffer, state.cursor) };
111
- if (matchesKey(data, "alt+right") || matchesKey(data, "ctrl+right"))
112
- return { ...state, cursor: wordForward(state.buffer, state.cursor) };
113
- if (matchesKey(data, "left"))
114
- return state.cursor > 0 ? { ...state, cursor: state.cursor - 1 } : state;
115
- if (matchesKey(data, "right"))
116
- return state.cursor < state.buffer.length ? { ...state, cursor: state.cursor + 1 } : state;
117
- if (matchesKey(data, "up") && cursorPos.line > 0) {
118
- const targetLine = cursorPos.line - 1;
119
- return { ...state, cursor: starts[targetLine] + Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0) };
120
- }
121
- if (matchesKey(data, "down") && cursorPos.line < wrapped.length - 1) {
122
- const targetLine = cursorPos.line + 1;
123
- return { ...state, cursor: starts[targetLine] + Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0) };
124
- }
125
- if (matchesKey(data, "home"))
126
- return { ...state, cursor: starts[cursorPos.line] };
127
- if (matchesKey(data, "end"))
128
- return { ...state, cursor: starts[cursorPos.line] + (wrapped[cursorPos.line]?.length ?? 0) };
129
- if (matchesKey(data, "ctrl+home"))
130
- return { ...state, cursor: 0 };
131
- if (matchesKey(data, "ctrl+end"))
132
- return { ...state, cursor: state.buffer.length };
133
- if (matchesKey(data, "alt+backspace")) {
134
- const target = wordBackward(state.buffer, state.cursor);
135
- return target === state.cursor ? state : { ...state, buffer: state.buffer.slice(0, target) + state.buffer.slice(state.cursor), cursor: target };
136
- }
137
- if (matchesKey(data, "backspace")) {
138
- return state.cursor > 0
139
- ? { ...state, buffer: state.buffer.slice(0, state.cursor - 1) + state.buffer.slice(state.cursor), cursor: state.cursor - 1 }
140
- : state;
141
- }
142
- if (matchesKey(data, "delete")) {
143
- return state.cursor < state.buffer.length
144
- ? { ...state, buffer: state.buffer.slice(0, state.cursor) + state.buffer.slice(state.cursor + 1) }
145
- : state;
146
- }
147
- const insert = normalizeInsertText(data);
148
- return insert
149
- ? { ...state, buffer: state.buffer.slice(0, state.cursor) + insert + state.buffer.slice(state.cursor), cursor: state.cursor + insert.length }
150
- : null;
151
- }
152
- function renderWithCursor(text, cursorPos) {
153
- const before = text.slice(0, cursorPos);
154
- const cursorChar = text[cursorPos] ?? " ";
155
- const after = text.slice(cursorPos + 1);
156
- return `${before}\x1b[7m${cursorChar}\x1b[27m${after}`;
157
- }
158
- function renderEditor(state, width, viewportHeight) {
159
- const { lines: wrapped, starts } = wrapText(state.buffer, width);
160
- const cursorPos = getCursorDisplayPos(state.cursor, starts);
161
- const lines = [];
162
- for (let i = 0; i < viewportHeight; i++) {
163
- const lineIdx = state.viewportOffset + i;
164
- let content = lineIdx < wrapped.length ? wrapped[lineIdx] ?? "" : "";
165
- if (lineIdx === cursorPos.line)
166
- content = renderWithCursor(content, cursorPos.col);
167
- lines.push(content);
168
- }
169
- return lines;
170
- }
171
- /**
172
- * TUI component for chain clarification.
173
- * Factory signature matches ctx.ui.custom: (tui, theme, kb, done) => Component
174
- */
5
+ import { ClarifyTextEditor } from "./clarify-text-editor.js";
6
+ import { ClarificationDraft } from "./clarification-draft.js";
7
+ /** Owns one preview interaction. Edits replace local values; launch inputs are never mutated. */
175
8
  export class ChainClarifyComponent {
176
9
  renderWidth = 84;
177
- get width() { return this.renderWidth; }
178
10
  selectedStep = 0;
179
11
  editingStep = null;
180
12
  editMode = "template";
181
- editState = createEditorState();
182
- EDIT_VIEWPORT_HEIGHT = 12;
183
- behaviorOverrides = new Map();
13
+ editor = new ClarifyTextEditor();
14
+ draft;
15
+ tui;
16
+ theme;
17
+ options;
18
+ done;
184
19
  modelSearchQuery = "";
185
20
  modelSelectedIndex = 0;
186
- filteredModels = [];
187
- MODEL_SELECTOR_HEIGHT = 10;
21
+ filteredModels;
188
22
  thinkingSelectedIndex = 0;
189
23
  skillSearchQuery = "";
190
24
  skillSelectedNames = new Set();
191
25
  skillCursorIndex = 0;
192
- filteredSkills = [];
193
- noticeMessage = null;
194
- noticeMessageTimer = null;
195
- /** Run in background (async) mode */
26
+ filteredSkills;
27
+ noticeMessage;
28
+ noticeMessageTimer;
196
29
  runInBackground = false;
197
- tui;
198
- theme;
199
- agentConfigs;
200
- templates;
201
- originalTask;
202
- chainDir;
203
- resolvedBehaviors;
204
- availableModels;
205
- preferredProvider;
206
- availableSkills;
207
- done;
208
30
  mode;
209
- constructor(tui, theme, agentConfigs, templates, originalTask, chainDir, resolvedBehaviors, availableModels, preferredProvider, availableSkills, done, mode = 'chain') {
31
+ constructor(tui, theme, options, done) {
210
32
  this.tui = tui;
211
33
  this.theme = theme;
212
- this.agentConfigs = agentConfigs;
213
- this.templates = templates;
214
- this.originalTask = originalTask;
215
- this.chainDir = chainDir;
216
- this.resolvedBehaviors = resolvedBehaviors;
217
- this.availableModels = availableModels;
218
- this.preferredProvider = preferredProvider;
219
- this.availableSkills = availableSkills;
34
+ this.options = options;
220
35
  this.done = done;
221
- this.mode = mode;
222
- this.filteredModels = [...availableModels];
223
- this.filteredSkills = [...availableSkills];
224
- }
225
- // ─────────────────────────────────────────────────────────────────────────────
226
- // Helper methods for rendering
227
- // ─────────────────────────────────────────────────────────────────────────────
228
- /** Pad string to specified visible width */
229
- pad(s, len) {
230
- const vis = visibleWidth(s);
231
- return s + " ".repeat(Math.max(0, len - vis));
36
+ this.draft = new ClarificationDraft(options.templates, options.resolvedBehaviors);
37
+ this.filteredModels = options.availableModels;
38
+ this.filteredSkills = options.availableSkills;
39
+ this.mode = options.mode ?? "chain";
232
40
  }
233
- /** Create a row with border characters */
234
- row(content) {
235
- const innerW = this.width - 2;
236
- const clipped = truncateToWidth(content, innerW, "", true);
237
- return this.theme.fg("border", "│") + this.pad(clipped, innerW) + this.theme.fg("border", "│");
238
- }
239
- /** Render centered header line with border */
240
- renderHeader(text) {
241
- const innerW = this.width - 2;
242
- const clipped = truncateToWidth(text, innerW, "", true);
243
- const padLen = Math.max(0, innerW - visibleWidth(clipped));
244
- const padLeft = Math.floor(padLen / 2);
245
- const padRight = padLen - padLeft;
246
- return (this.theme.fg("border", "╭" + "─".repeat(padLeft)) +
247
- this.theme.fg("accent", clipped) +
248
- this.theme.fg("border", "─".repeat(padRight) + "╮"));
249
- }
250
- /** Render centered footer line with border */
251
- renderFooter(text) {
252
- const innerW = this.width - 2;
253
- const clipped = truncateToWidth(text, innerW, "", true);
254
- const padLen = Math.max(0, innerW - visibleWidth(clipped));
255
- const padLeft = Math.floor(padLen / 2);
256
- const padRight = padLen - padLeft;
257
- return (this.theme.fg("border", "╰" + "─".repeat(padLeft)) +
258
- this.theme.fg("dim", clipped) +
259
- this.theme.fg("border", "─".repeat(padRight) + "╯"));
41
+ getEffectiveModel(index) {
42
+ const model = this.draft.behavior(index).model;
43
+ if (model === undefined || model.length === 0) {
44
+ return "default";
45
+ }
46
+ return (resolveModelCandidate(model, this.options.availableModels, this.options.preferredProvider) ??
47
+ model);
260
48
  }
261
- /** Exit edit mode and reset state */
262
49
  exitEditMode() {
263
50
  this.editingStep = null;
264
- this.editState = createEditorState();
51
+ this.editor.setText("");
265
52
  this.tui.requestRender();
266
53
  }
267
- // ─────────────────────────────────────────────────────────────────────────────
268
- // Full edit mode methods
269
- // ─────────────────────────────────────────────────────────────────────────────
270
- /** Render the full-edit takeover view */
271
- renderFullEditMode() {
272
- const innerW = this.width - 2;
273
- const textWidth = innerW - 2; // 1 char padding on each side
274
- const lines = [];
275
- const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
276
- const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
277
- this.editState = {
278
- ...this.editState,
279
- viewportOffset: ensureCursorVisible(cursorPos.line, this.EDIT_VIEWPORT_HEIGHT, this.editState.viewportOffset),
280
- };
281
- // Header (truncate agent name to prevent overflow)
282
- const fieldName = this.editMode === "template" ? "task" : this.editMode;
283
- const rawAgentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
284
- const maxAgentLen = innerW - 30; // Reserve space for " Editing X (Step/Task N: ) "
285
- const agentName = rawAgentName.length > maxAgentLen
286
- ? rawAgentName.slice(0, maxAgentLen - 1) + "…"
287
- : rawAgentName;
288
- // Use mode-appropriate terminology
289
- const stepLabel = this.mode === 'single'
290
- ? agentName
291
- : this.mode === 'parallel'
292
- ? `Task ${this.editingStep + 1}: ${agentName}`
293
- : `Step ${this.editingStep + 1}: ${agentName}`;
294
- const headerText = ` Editing ${fieldName} (${stepLabel}) `;
295
- lines.push(this.renderHeader(headerText));
296
- lines.push(this.row(""));
297
- const editorLines = renderEditor(this.editState, textWidth, this.EDIT_VIEWPORT_HEIGHT);
298
- for (const line of editorLines) {
299
- lines.push(this.row(` ${line}`));
300
- }
301
- const linesBelow = wrapped.length - this.editState.viewportOffset - this.EDIT_VIEWPORT_HEIGHT;
302
- const hasMore = linesBelow > 0;
303
- const hasLess = this.editState.viewportOffset > 0;
304
- let scrollInfo = "";
305
- if (hasLess)
306
- scrollInfo += "↑";
307
- if (hasMore)
308
- scrollInfo += `↓ ${linesBelow}+`;
309
- lines.push(this.row(""));
310
- const footerText = scrollInfo
311
- ? ` [Esc] Done • [Ctrl+C] Discard • ${scrollInfo} `
312
- : " [Esc] Done • [Ctrl+C] Discard ";
313
- lines.push(this.renderFooter(footerText));
314
- return lines;
315
- }
316
- // ─────────────────────────────────────────────────────────────────────────────
317
- // Behavior helpers
318
- // ─────────────────────────────────────────────────────────────────────────────
319
- /** Get effective behavior for a step (with user overrides applied) */
320
- getEffectiveBehavior(stepIndex) {
321
- const base = this.resolvedBehaviors[stepIndex];
322
- const override = this.behaviorOverrides.get(stepIndex);
323
- if (!override)
324
- return base;
325
- return {
326
- output: override.output !== undefined ? override.output : base.output,
327
- outputMode: base.outputMode,
328
- reads: override.reads !== undefined ? override.reads : base.reads,
329
- progress: override.progress !== undefined ? override.progress : base.progress,
330
- skills: override.skills !== undefined ? override.skills : base.skills,
331
- model: override.model !== undefined ? override.model : base.model,
332
- };
333
- }
334
- /** Get the effective model for a step (override or agent default) */
335
- getEffectiveModel(stepIndex) {
336
- const override = this.behaviorOverrides.get(stepIndex);
337
- if (override?.model)
338
- return this.resolveModelFullId(override.model);
339
- const baseModel = this.resolvedBehaviors[stepIndex]?.model;
340
- if (baseModel)
341
- return this.resolveModelFullId(baseModel);
342
- return "default";
343
- }
344
- /** Resolve a model name to its full provider/model format */
345
- resolveModelFullId(modelName) {
346
- return resolveModelCandidate(modelName, this.availableModels, this.preferredProvider) ?? modelName;
347
- }
348
- /** Update a behavior override for a step */
349
- updateBehavior(stepIndex, field, value) {
350
- const existing = this.behaviorOverrides.get(stepIndex) ?? {};
351
- this.behaviorOverrides.set(stepIndex, { ...existing, [field]: value });
352
- }
353
- showNotice(text, type) {
354
- this.noticeMessage = { text, type };
355
- if (this.noticeMessageTimer)
54
+ showNotice(text) {
55
+ this.noticeMessage = text;
56
+ if (this.noticeMessageTimer !== undefined) {
356
57
  clearTimeout(this.noticeMessageTimer);
58
+ }
357
59
  this.noticeMessageTimer = setTimeout(() => {
358
- this.noticeMessage = null;
359
- this.noticeMessageTimer = null;
60
+ this.noticeMessage = undefined;
61
+ this.noticeMessageTimer = undefined;
360
62
  this.tui.requestRender();
361
63
  }, 2000);
362
64
  this.tui.requestRender();
363
65
  }
364
66
  handleInput(data) {
365
67
  if (this.editingStep !== null) {
366
- if (this.editMode === "model") {
367
- this.handleModelSelectorInput(data);
368
- }
369
- else if (this.editMode === "thinking") {
370
- this.handleThinkingSelectorInput(data);
371
- }
372
- else if (this.editMode === "skills") {
373
- this.handleSkillSelectorInput(data);
374
- }
375
- else {
376
- this.handleEditInput(data);
377
- }
68
+ this.handleEditingInput(data);
378
69
  return;
379
70
  }
380
71
  if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
@@ -382,771 +73,356 @@ export class ChainClarifyComponent {
382
73
  return;
383
74
  }
384
75
  if (matchesKey(data, "return")) {
385
- const overrides = [];
386
- for (let i = 0; i < this.agentConfigs.length; i++) {
387
- overrides.push(this.behaviorOverrides.get(i));
388
- }
389
- this.done({ confirmed: true, templates: this.templates, behaviorOverrides: overrides, runInBackground: this.runInBackground });
390
- return;
391
- }
392
- if (matchesKey(data, "up")) {
393
- this.selectedStep = Math.max(0, this.selectedStep - 1);
394
- this.tui.requestRender();
395
- return;
396
- }
397
- if (matchesKey(data, "down")) {
398
- const maxStep = Math.max(0, this.agentConfigs.length - 1);
399
- this.selectedStep = Math.min(maxStep, this.selectedStep + 1);
400
- this.tui.requestRender();
76
+ this.done({
77
+ confirmed: true,
78
+ templates: [...this.draft.templates],
79
+ behaviorOverrides: this.options.agentConfigs.map((_, index) => this.draft.override(index)),
80
+ runInBackground: this.runInBackground,
81
+ });
401
82
  return;
402
83
  }
403
- if (data === "e") {
404
- this.enterEditMode("template");
405
- return;
84
+ if (matchesKey(data, "up") || matchesKey(data, "down")) {
85
+ const delta = matchesKey(data, "up") ? -1 : 1;
86
+ this.selectedStep = Math.max(0, Math.min(this.options.agentConfigs.length - 1, this.selectedStep + delta));
406
87
  }
407
- if (data === "m") {
408
- this.enterModelSelector();
409
- return;
410
- }
411
- if (data === "t") {
412
- this.enterThinkingSelector();
413
- return;
414
- }
415
- if (data === "s") {
416
- this.editingStep = this.selectedStep;
417
- this.editMode = "skills";
418
- this.skillSearchQuery = "";
419
- this.skillCursorIndex = 0;
420
- this.filteredSkills = [...this.availableSkills];
421
- const current = this.getEffectiveBehavior(this.selectedStep).skills;
422
- this.skillSelectedNames.clear();
423
- if (current !== false && current.length > 0) {
424
- current.forEach((skillName) => this.skillSelectedNames.add(skillName));
425
- }
426
- this.tui.requestRender();
427
- return;
428
- }
429
- if (data === "w" && this.mode !== 'parallel') {
430
- this.enterEditMode("output");
431
- return;
432
- }
433
- if (data === "r" && this.mode === 'chain') {
434
- this.enterEditMode("reads");
435
- return;
436
- }
437
- if (data === "p" && this.mode === 'chain') {
438
- const anyEnabled = this.agentConfigs.some((_, i) => this.getEffectiveBehavior(i).progress);
439
- const newState = !anyEnabled;
440
- for (let i = 0; i < this.agentConfigs.length; i++) {
441
- this.updateBehavior(i, "progress", newState);
442
- }
443
- this.tui.requestRender();
444
- return;
88
+ else {
89
+ this.handleShortcut(data);
445
90
  }
446
- if (data === "b") {
447
- this.runInBackground = !this.runInBackground;
448
- this.tui.requestRender();
449
- return;
91
+ this.tui.requestRender();
92
+ }
93
+ handleShortcut(data) {
94
+ switch (data) {
95
+ case "e":
96
+ this.enterEditMode("template");
97
+ break;
98
+ case "m":
99
+ this.enterModelSelector();
100
+ break;
101
+ case "t":
102
+ this.enterThinkingSelector();
103
+ break;
104
+ case "s":
105
+ this.enterSkillSelector();
106
+ break;
107
+ case "w":
108
+ if (this.mode !== "parallel") {
109
+ this.enterEditMode("output");
110
+ }
111
+ break;
112
+ case "r":
113
+ if (this.mode === "chain") {
114
+ this.enterEditMode("reads");
115
+ }
116
+ break;
117
+ case "p":
118
+ if (this.mode === "chain") {
119
+ this.draft.toggleProgress();
120
+ }
121
+ break;
122
+ case "b":
123
+ this.runInBackground = !this.runInBackground;
124
+ break;
450
125
  }
451
126
  }
452
127
  enterEditMode(mode) {
453
128
  this.editingStep = this.selectedStep;
454
129
  this.editMode = mode;
455
- let buffer = "";
456
- if (mode === "template") {
457
- const template = this.templates[this.selectedStep] ?? "";
458
- buffer = template.split("\n")[0] ?? "";
459
- }
460
- else if (mode === "output") {
461
- const behavior = this.getEffectiveBehavior(this.selectedStep);
462
- buffer = behavior.output === false ? "" : (behavior.output || "");
130
+ const behavior = this.draft.behavior(this.selectedStep);
131
+ let buffer = (this.draft.templates[this.selectedStep] ?? "").split("\n")[0] ?? "";
132
+ if (mode === "output") {
133
+ buffer = behavior.output === false ? "" : behavior.output;
463
134
  }
464
- else if (mode === "reads") {
465
- const behavior = this.getEffectiveBehavior(this.selectedStep);
466
- buffer = behavior.reads === false ? "" : (behavior.reads?.join(", ") || "");
135
+ if (mode === "reads") {
136
+ buffer = behavior.reads === false ? "" : behavior.reads.join(", ");
467
137
  }
468
- this.editState = createEditorState(buffer);
469
- this.tui.requestRender();
138
+ this.editor.setText(buffer);
470
139
  }
471
- /** Enter model selector mode */
472
140
  enterModelSelector() {
473
141
  this.editingStep = this.selectedStep;
474
142
  this.editMode = "model";
475
143
  this.modelSearchQuery = "";
476
- this.modelSelectedIndex = 0;
477
- this.filteredModels = [...this.availableModels];
478
- const currentModel = splitThinkingSuffix(this.getEffectiveModel(this.selectedStep)).baseModel;
479
- const currentIndex = this.filteredModels.findIndex((m) => m.fullId === currentModel || m.id === currentModel);
480
- if (currentIndex >= 0) {
481
- this.modelSelectedIndex = currentIndex;
482
- }
144
+ this.filteredModels = this.options.availableModels;
145
+ const current = splitThinkingSuffix(this.getEffectiveModel(this.selectedStep)).baseModel;
146
+ this.modelSelectedIndex = Math.max(0, this.filteredModels.findIndex((model) => model.fullId === current || model.id === current));
483
147
  this.tui.requestRender();
484
148
  }
485
- /** Filter models based on search query */
486
- filterModels() {
487
- const query = this.modelSearchQuery.toLowerCase();
488
- if (!query) {
489
- this.filteredModels = [...this.availableModels];
490
- }
491
- else {
492
- this.filteredModels = this.availableModels.filter((m) => m.fullId.toLowerCase().includes(query) ||
493
- m.id.toLowerCase().includes(query) ||
494
- m.provider.toLowerCase().includes(query));
149
+ getAvailableThinkingLevels(index) {
150
+ return getSupportedThinkingLevels(findModelInfo(this.getEffectiveModel(index), this.options.availableModels, this.options.preferredProvider));
151
+ }
152
+ enterThinkingSelector() {
153
+ const model = this.draft.behavior(this.selectedStep).model;
154
+ if (model === undefined || model.length === 0) {
155
+ this.showNotice("Select a model first");
156
+ return;
495
157
  }
496
- this.modelSelectedIndex = Math.min(this.modelSelectedIndex, Math.max(0, this.filteredModels.length - 1));
158
+ this.editingStep = this.selectedStep;
159
+ this.editMode = "thinking";
160
+ const levels = this.getAvailableThinkingLevels(this.selectedStep);
161
+ const suffix = splitThinkingSuffix(this.getEffectiveModel(this.selectedStep)).thinkingSuffix.slice(1);
162
+ const index = levels.findIndex((level) => level === suffix);
163
+ this.thinkingSelectedIndex = index >= 0 ? index : Math.max(0, levels.indexOf("off"));
164
+ this.tui.requestRender();
497
165
  }
498
- handleModelSelectorInput(data) {
499
- if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
500
- this.exitEditMode();
166
+ applyThinkingLevel(level) {
167
+ if (this.editingStep === null) {
501
168
  return;
502
169
  }
503
- if (matchesKey(data, "return")) {
504
- const selected = this.filteredModels[this.modelSelectedIndex];
505
- if (selected) {
506
- const { thinkingSuffix } = splitThinkingSuffix(this.getEffectiveModel(this.editingStep));
507
- const requestedLevel = thinkingSuffix.slice(1);
508
- const selectedModel = findModelInfo(selected.fullId, this.availableModels, this.preferredProvider);
509
- const suffix = getSupportedThinkingLevels(selectedModel).some((level) => level === requestedLevel) ? thinkingSuffix : "";
510
- this.updateBehavior(this.editingStep, "model", `${selected.fullId}${suffix}`);
511
- }
512
- this.exitEditMode();
170
+ const current = this.draft.behavior(this.editingStep).model;
171
+ if (current === undefined || current.length === 0) {
513
172
  return;
514
173
  }
515
- if (matchesKey(data, "up")) {
516
- if (this.filteredModels.length > 0) {
517
- this.modelSelectedIndex = this.modelSelectedIndex === 0
518
- ? this.filteredModels.length - 1
519
- : this.modelSelectedIndex - 1;
520
- }
521
- this.tui.requestRender();
174
+ const { baseModel } = splitThinkingSuffix(current);
175
+ this.draft.update(this.editingStep, {
176
+ model: level === "off" ? baseModel : `${baseModel}:${level}`,
177
+ });
178
+ }
179
+ enterSkillSelector() {
180
+ this.editingStep = this.selectedStep;
181
+ this.editMode = "skills";
182
+ this.skillSearchQuery = "";
183
+ this.skillCursorIndex = 0;
184
+ this.filteredSkills = this.options.availableSkills;
185
+ this.skillSelectedNames.clear();
186
+ const skills = this.draft.behavior(this.selectedStep).skills;
187
+ if (skills !== false) {
188
+ skills.forEach((skill) => {
189
+ this.skillSelectedNames.add(skill);
190
+ });
191
+ }
192
+ }
193
+ handleEditingInput(data) {
194
+ if (this.editMode === "model") {
195
+ this.handleModelSelectorInput(data);
522
196
  return;
523
197
  }
524
- if (matchesKey(data, "down")) {
525
- if (this.filteredModels.length > 0) {
526
- this.modelSelectedIndex = this.modelSelectedIndex === this.filteredModels.length - 1
527
- ? 0
528
- : this.modelSelectedIndex + 1;
529
- }
530
- this.tui.requestRender();
198
+ if (this.editMode === "thinking") {
199
+ this.handleThinkingSelectorInput(data);
531
200
  return;
532
201
  }
533
- if (matchesKey(data, "backspace")) {
534
- if (this.modelSearchQuery.length > 0) {
535
- this.modelSearchQuery = this.modelSearchQuery.slice(0, -1);
536
- this.filterModels();
537
- }
538
- this.tui.requestRender();
202
+ if (this.editMode === "skills") {
203
+ this.handleSkillSelectorInput(data);
539
204
  return;
540
205
  }
541
- if (data.length === 1 && data.charCodeAt(0) >= 32) {
542
- this.modelSearchQuery += data;
543
- this.filterModels();
544
- this.tui.requestRender();
206
+ if (matchesKey(data, "escape")) {
207
+ this.saveEdit();
208
+ this.exitEditMode();
545
209
  return;
546
210
  }
547
- }
548
- getAvailableThinkingLevels(stepIndex) {
549
- return getSupportedThinkingLevels(findModelInfo(this.getEffectiveModel(stepIndex), this.availableModels, this.preferredProvider));
550
- }
551
- /** Enter thinking level selector mode */
552
- enterThinkingSelector() {
553
- if (!this.getEffectiveBehavior(this.selectedStep).model) {
554
- this.showNotice("Select a model first", "error");
211
+ if (matchesKey(data, "ctrl+c")) {
212
+ this.exitEditMode();
555
213
  return;
556
214
  }
557
- this.editingStep = this.selectedStep;
558
- this.editMode = "thinking";
559
- const levels = this.getAvailableThinkingLevels(this.selectedStep);
560
- const { thinkingSuffix } = splitThinkingSuffix(this.getEffectiveModel(this.selectedStep));
561
- const suffix = thinkingSuffix.slice(1);
562
- const levelIdx = levels.findIndex((level) => level === suffix);
563
- this.thinkingSelectedIndex = levelIdx >= 0 ? levelIdx : Math.max(0, levels.indexOf("off"));
215
+ this.editor.handleInput(data, this.renderWidth - 4);
564
216
  this.tui.requestRender();
565
217
  }
566
- handleThinkingSelectorInput(data) {
218
+ handleModelSelectorInput(data) {
567
219
  if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
568
220
  this.exitEditMode();
569
221
  return;
570
222
  }
571
- const levels = this.getAvailableThinkingLevels(this.editingStep);
572
- if (levels.length === 0)
573
- return;
574
223
  if (matchesKey(data, "return")) {
575
- const selectedLevel = levels[this.thinkingSelectedIndex] ?? "off";
576
- this.applyThinkingLevel(selectedLevel);
224
+ this.selectModel();
577
225
  this.exitEditMode();
578
226
  return;
579
227
  }
580
- if (matchesKey(data, "up")) {
581
- this.thinkingSelectedIndex = this.thinkingSelectedIndex === 0
582
- ? levels.length - 1
583
- : this.thinkingSelectedIndex - 1;
584
- this.tui.requestRender();
585
- return;
228
+ const direction = navigationDirection(data);
229
+ if (direction !== 0) {
230
+ this.modelSelectedIndex = moveSelection(this.modelSelectedIndex, direction, this.filteredModels.length);
586
231
  }
587
- if (matchesKey(data, "down")) {
588
- this.thinkingSelectedIndex = this.thinkingSelectedIndex === levels.length - 1
589
- ? 0
590
- : this.thinkingSelectedIndex + 1;
591
- this.tui.requestRender();
592
- return;
232
+ else {
233
+ this.modelSearchQuery = searchInput(this.modelSearchQuery, data);
234
+ const query = this.modelSearchQuery.toLowerCase();
235
+ this.filteredModels = this.options.availableModels.filter((model) => `${model.fullId} ${model.id} ${model.provider}`.toLowerCase().includes(query));
236
+ this.modelSelectedIndex = Math.min(this.modelSelectedIndex, Math.max(0, this.filteredModels.length - 1));
593
237
  }
238
+ this.tui.requestRender();
594
239
  }
595
- /** Apply thinking level to the current step's model */
596
- applyThinkingLevel(level) {
597
- const stepIndex = this.editingStep;
598
- const currentModel = this.getEffectiveBehavior(stepIndex).model;
599
- if (!currentModel)
240
+ selectModel() {
241
+ const selected = this.filteredModels.at(this.modelSelectedIndex);
242
+ if (!selected || this.editingStep === null) {
600
243
  return;
601
- const { baseModel } = splitThinkingSuffix(currentModel);
602
- const newModel = level === "off" ? baseModel : `${baseModel}:${level}`;
603
- this.updateBehavior(stepIndex, "model", newModel);
604
- }
605
- filterSkills() {
606
- const query = this.skillSearchQuery.toLowerCase();
607
- if (!query) {
608
- this.filteredSkills = [...this.availableSkills];
609
- }
610
- else {
611
- this.filteredSkills = this.availableSkills.filter((s) => s.name.toLowerCase().includes(query) ||
612
- (s.description?.toLowerCase().includes(query) ?? false));
613
244
  }
614
- this.skillCursorIndex = Math.min(this.skillCursorIndex, Math.max(0, this.filteredSkills.length - 1));
245
+ const { thinkingSuffix } = splitThinkingSuffix(this.getEffectiveModel(this.editingStep));
246
+ const info = findModelInfo(selected.fullId, this.options.availableModels, this.options.preferredProvider);
247
+ const suffix = getSupportedThinkingLevels(info).some((level) => level === thinkingSuffix.slice(1))
248
+ ? thinkingSuffix
249
+ : "";
250
+ this.draft.update(this.editingStep, { model: `${selected.fullId}${suffix}` });
615
251
  }
616
- handleSkillSelectorInput(data) {
252
+ handleThinkingSelectorInput(data) {
617
253
  if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
618
254
  this.exitEditMode();
619
255
  return;
620
256
  }
621
- if (matchesKey(data, "return")) {
622
- const selected = [...this.skillSelectedNames];
623
- this.updateBehavior(this.editingStep, "skills", selected);
624
- this.exitEditMode();
257
+ if (this.editingStep === null) {
625
258
  return;
626
259
  }
627
- if (data === " ") {
628
- if (this.filteredSkills.length > 0) {
629
- const skill = this.filteredSkills[this.skillCursorIndex];
630
- if (skill) {
631
- if (this.skillSelectedNames.has(skill.name)) {
632
- this.skillSelectedNames.delete(skill.name);
633
- }
634
- else {
635
- this.skillSelectedNames.add(skill.name);
636
- }
637
- }
638
- }
639
- this.tui.requestRender();
260
+ const levels = this.getAvailableThinkingLevels(this.editingStep);
261
+ if (levels.length === 0) {
640
262
  return;
641
263
  }
642
- if (matchesKey(data, "up")) {
643
- if (this.filteredSkills.length > 0) {
644
- this.skillCursorIndex = this.skillCursorIndex === 0
645
- ? this.filteredSkills.length - 1
646
- : this.skillCursorIndex - 1;
647
- }
648
- this.tui.requestRender();
264
+ if (matchesKey(data, "return")) {
265
+ this.applyThinkingLevel(levels[this.thinkingSelectedIndex] ?? "off");
266
+ this.exitEditMode();
649
267
  return;
650
268
  }
651
- if (matchesKey(data, "down")) {
652
- if (this.filteredSkills.length > 0) {
653
- this.skillCursorIndex = this.skillCursorIndex === this.filteredSkills.length - 1
654
- ? 0
655
- : this.skillCursorIndex + 1;
656
- }
657
- this.tui.requestRender();
269
+ this.thinkingSelectedIndex = moveSelection(this.thinkingSelectedIndex, navigationDirection(data), levels.length);
270
+ this.tui.requestRender();
271
+ }
272
+ handleSkillSelectorInput(data) {
273
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
274
+ this.exitEditMode();
658
275
  return;
659
276
  }
660
- if (matchesKey(data, "backspace")) {
661
- if (this.skillSearchQuery.length > 0) {
662
- this.skillSearchQuery = this.skillSearchQuery.slice(0, -1);
663
- this.filterSkills();
664
- }
665
- this.tui.requestRender();
277
+ if (matchesKey(data, "return") && this.editingStep !== null) {
278
+ this.draft.update(this.editingStep, { skills: [...this.skillSelectedNames] });
279
+ this.exitEditMode();
666
280
  return;
667
281
  }
668
- if (data.length === 1 && data.charCodeAt(0) >= 32) {
669
- this.skillSearchQuery += data;
670
- this.filterSkills();
671
- this.tui.requestRender();
672
- return;
282
+ if (data === " ") {
283
+ this.toggleSkill();
673
284
  }
674
- }
675
- handleEditInput(data) {
676
- const textWidth = this.width - 4; // Must match render: innerW - 2 = (width - 2) - 2
677
- if (matchesKey(data, "shift+up") || matchesKey(data, "pageUp")) {
678
- const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
679
- const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
680
- const targetLine = Math.max(0, cursorPos.line - this.EDIT_VIEWPORT_HEIGHT);
681
- const targetCol = Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0);
682
- this.editState = { ...this.editState, cursor: starts[targetLine] + targetCol };
683
- this.tui.requestRender();
684
- return;
285
+ else {
286
+ this.searchSkills(data);
685
287
  }
686
- if (matchesKey(data, "shift+down") || matchesKey(data, "pageDown")) {
687
- const { lines: wrapped, starts } = wrapText(this.editState.buffer, textWidth);
688
- const cursorPos = getCursorDisplayPos(this.editState.cursor, starts);
689
- const targetLine = Math.min(wrapped.length - 1, cursorPos.line + this.EDIT_VIEWPORT_HEIGHT);
690
- const targetCol = Math.min(cursorPos.col, wrapped[targetLine]?.length ?? 0);
691
- this.editState = { ...this.editState, cursor: starts[targetLine] + targetCol };
692
- this.tui.requestRender();
288
+ this.tui.requestRender();
289
+ }
290
+ toggleSkill() {
291
+ const skill = this.filteredSkills.at(this.skillCursorIndex);
292
+ if (!skill) {
693
293
  return;
694
294
  }
695
- if (matchesKey(data, "tab"))
696
- return;
697
- const nextState = handleEditorInput(this.editState, data, textWidth);
698
- if (nextState) {
699
- this.editState = nextState;
700
- this.tui.requestRender();
701
- return;
295
+ if (this.skillSelectedNames.has(skill.name)) {
296
+ this.skillSelectedNames.delete(skill.name);
702
297
  }
703
- if (matchesKey(data, "escape")) {
704
- this.saveEdit();
705
- this.exitEditMode();
706
- return;
298
+ else {
299
+ this.skillSelectedNames.add(skill.name);
707
300
  }
708
- if (matchesKey(data, "ctrl+c")) {
709
- this.exitEditMode();
301
+ }
302
+ searchSkills(data) {
303
+ const direction = navigationDirection(data);
304
+ if (direction !== 0) {
305
+ this.skillCursorIndex = moveSelection(this.skillCursorIndex, direction, this.filteredSkills.length);
710
306
  return;
711
307
  }
308
+ this.skillSearchQuery = searchInput(this.skillSearchQuery, data);
309
+ const query = this.skillSearchQuery.toLowerCase();
310
+ this.filteredSkills = this.options.availableSkills.filter((skill) => `${skill.name} ${skill.description ?? ""}`.toLowerCase().includes(query));
311
+ this.skillCursorIndex = Math.min(this.skillCursorIndex, Math.max(0, this.filteredSkills.length - 1));
712
312
  }
713
313
  saveEdit() {
714
- const stepIndex = this.editingStep;
715
- if (this.editMode === "template") {
716
- // For template, preserve other lines if they existed
717
- const original = this.templates[stepIndex] ?? "";
718
- const originalLines = original.split("\n");
719
- originalLines[0] = this.editState.buffer;
720
- this.templates[stepIndex] = originalLines.join("\n");
721
- }
722
- else if (this.editMode === "output") {
723
- // Capture OLD output before updating (for downstream propagation)
724
- const oldBehavior = this.getEffectiveBehavior(stepIndex);
725
- const oldOutput = typeof oldBehavior.output === "string" ? oldBehavior.output : null;
726
- // Empty string or whitespace means disable output
727
- const trimmed = this.editState.buffer.trim();
728
- const newOutput = trimmed === "" ? false : trimmed;
729
- this.updateBehavior(stepIndex, "output", newOutput);
730
- // Propagate output filename change to downstream steps' reads
731
- if (oldOutput && typeof newOutput === "string" && oldOutput !== newOutput) {
732
- this.propagateOutputChange(stepIndex, oldOutput, newOutput);
733
- }
734
- }
735
- else if (this.editMode === "reads") {
736
- // Parse comma-separated list, empty means disable reads
737
- const trimmed = this.editState.buffer.trim();
738
- if (trimmed === "") {
739
- this.updateBehavior(stepIndex, "reads", false);
740
- }
741
- else {
742
- const files = trimmed.split(",").map(f => f.trim()).filter(f => f !== "");
743
- this.updateBehavior(stepIndex, "reads", files.length > 0 ? files : false);
744
- }
314
+ if (this.editingStep !== null) {
315
+ this.draft.save(this.editingStep, this.editMode, this.editor.getText());
745
316
  }
746
317
  }
747
- /**
748
- * When a step's output filename changes, update downstream steps that read from it.
749
- * This maintains the chain dependency automatically.
750
- */
751
- propagateOutputChange(changedStepIndex, oldOutput, newOutput) {
752
- // Check all downstream steps (steps that come after the changed step)
753
- for (let i = changedStepIndex + 1; i < this.agentConfigs.length; i++) {
754
- const behavior = this.getEffectiveBehavior(i);
755
- // Skip if reads is disabled or empty
756
- if (behavior.reads === false || !behavior.reads || behavior.reads.length === 0) {
757
- continue;
758
- }
759
- // Check if this step reads the old output file
760
- const readsArray = behavior.reads;
761
- const oldIndex = readsArray.indexOf(oldOutput);
762
- if (oldIndex !== -1) {
763
- // Replace old filename with new filename in reads
764
- const newReads = [...readsArray];
765
- newReads[oldIndex] = newOutput;
766
- this.updateBehavior(i, "reads", newReads);
767
- }
318
+ stepLabel(index) {
319
+ const agent = this.options.agentConfigs[index]?.name ?? "unknown";
320
+ if (this.mode === "single") {
321
+ return agent;
768
322
  }
323
+ return `${this.mode === "parallel" ? "Task" : "Step"} ${index + 1}: ${agent}`;
769
324
  }
770
325
  render(width) {
771
- if (width < 40)
326
+ if (width < 40) {
772
327
  return [truncateToWidth("Subagent clarify: widen terminal", width)];
328
+ }
773
329
  this.renderWidth = Math.min(84, width);
774
330
  if (this.editingStep !== null) {
775
- if (this.editMode === "model") {
776
- return this.renderModelSelector();
777
- }
778
- if (this.editMode === "thinking") {
779
- return this.renderThinkingSelector();
780
- }
781
- if (this.editMode === "skills") {
782
- return this.renderSkillSelector();
783
- }
784
- return this.renderFullEditMode();
785
- }
786
- // Mode-based navigation rendering
787
- switch (this.mode) {
788
- case 'single': return this.renderSingleMode();
789
- case 'parallel': return this.renderParallelMode();
790
- case 'chain': return this.renderChainMode();
331
+ return this.renderEditing();
791
332
  }
333
+ return this.renderOverview();
792
334
  }
793
- /** Render the model selector view */
794
- renderModelSelector() {
795
- const th = this.theme;
796
- const lines = [];
797
- // Header (mode-aware terminology)
798
- const agentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
799
- const stepLabel = this.mode === 'single'
800
- ? agentName
801
- : this.mode === 'parallel'
802
- ? `Task ${this.editingStep + 1}: ${agentName}`
803
- : `Step ${this.editingStep + 1}: ${agentName}`;
804
- const headerText = ` Select Model (${stepLabel}) `;
805
- lines.push(this.renderHeader(headerText));
806
- lines.push(this.row(""));
807
- const searchPrefix = th.fg("dim", "Search: ");
808
- const cursor = "\x1b[7m \x1b[27m"; // Reverse video space for cursor
809
- const searchDisplay = this.modelSearchQuery + cursor;
810
- lines.push(this.row(` ${searchPrefix}${searchDisplay}`));
811
- lines.push(this.row(""));
812
- const currentModel = this.getEffectiveModel(this.editingStep);
813
- const currentModelBase = splitThinkingSuffix(currentModel).baseModel;
814
- const currentLabel = th.fg("dim", "Current: ");
815
- lines.push(this.row(` ${currentLabel}${th.fg("warning", currentModel)}`));
816
- lines.push(this.row(""));
817
- if (this.filteredModels.length === 0) {
818
- lines.push(this.row(` ${th.fg("dim", "No matching models")}`));
335
+ renderEditing() {
336
+ if (this.editMode === "model") {
337
+ return this.renderModelSelector();
819
338
  }
820
- else {
821
- const maxVisible = this.MODEL_SELECTOR_HEIGHT;
822
- let startIdx = 0;
823
- if (this.filteredModels.length > maxVisible) {
824
- startIdx = Math.max(0, this.modelSelectedIndex - Math.floor(maxVisible / 2));
825
- startIdx = Math.min(startIdx, this.filteredModels.length - maxVisible);
826
- }
827
- const endIdx = Math.min(startIdx + maxVisible, this.filteredModels.length);
828
- if (startIdx > 0) {
829
- lines.push(this.row(` ${th.fg("dim", ` ↑ ${startIdx} more`)}`));
830
- }
831
- for (let i = startIdx; i < endIdx; i++) {
832
- const model = this.filteredModels[i];
833
- const isSelected = i === this.modelSelectedIndex;
834
- const isCurrent = model.fullId === currentModelBase || model.id === currentModelBase;
835
- const prefix = isSelected ? th.fg("accent", "→ ") : " ";
836
- const modelText = isSelected ? th.fg("accent", model.id) : model.id;
837
- const providerBadge = th.fg("dim", ` [${model.provider}]`);
838
- const currentBadge = isCurrent ? th.fg("success", " current") : "";
839
- lines.push(this.row(` ${prefix}${modelText}${providerBadge}${currentBadge}`));
840
- }
841
- const remaining = this.filteredModels.length - endIdx;
842
- if (remaining > 0) {
843
- lines.push(this.row(` ${th.fg("dim", ` ↓ ${remaining} more`)}`));
844
- }
339
+ if (this.editMode === "thinking") {
340
+ return this.renderThinkingSelector();
845
341
  }
846
- const contentLines = lines.length;
847
- const targetHeight = 18;
848
- for (let i = contentLines; i < targetHeight; i++) {
849
- lines.push(this.row(""));
342
+ if (this.editMode === "skills") {
343
+ return this.renderSkillSelector();
850
344
  }
851
- const footerText = " [Enter] Select • [Esc] Cancel • Type to search ";
852
- lines.push(this.renderFooter(footerText));
853
- return lines;
345
+ const name = this.editMode === "template" ? "task" : this.editMode;
346
+ const view = new ClarifyView(this.theme, this.renderWidth);
347
+ return view.editor(`Editing ${name} (${this.stepLabel(this.editingStep ?? 0)})`, this.editor.render(this.renderWidth - 4, 12));
348
+ }
349
+ renderModelSelector() {
350
+ const current = this.getEffectiveModel(this.editingStep ?? 0);
351
+ const base = splitThinkingSuffix(current).baseModel;
352
+ const choices = this.filteredModels.map((model) => `${model.id} [${model.provider}]${model.fullId === base || model.id === base ? " current" : ""}`);
353
+ return new ClarifyView(this.theme, this.renderWidth).selector({
354
+ title: `Select Model (${this.stepLabel(this.editingStep ?? 0)})`,
355
+ subtitles: [`Search: ${this.modelSearchQuery}\x1b[7m \x1b[27m`, `Current: ${current}`],
356
+ choices,
357
+ selected: this.modelSelectedIndex,
358
+ footer: "[Enter] Select • [Esc] Cancel • Type to search",
359
+ });
854
360
  }
855
- /** Render the thinking level selector view */
856
361
  renderThinkingSelector() {
857
- const th = this.theme;
858
- const lines = [];
859
- const agentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
860
- const stepLabel = this.mode === 'single'
861
- ? agentName
862
- : this.mode === 'parallel'
863
- ? `Task ${this.editingStep + 1}: ${agentName}`
864
- : `Step ${this.editingStep + 1}: ${agentName}`;
865
- const headerText = ` Thinking Level (${stepLabel}) `;
866
- lines.push(this.renderHeader(headerText));
867
- lines.push(this.row(""));
868
- const currentModel = this.getEffectiveModel(this.editingStep);
869
- const currentLabel = th.fg("dim", "Model: ");
870
- lines.push(this.row(` ${currentLabel}${th.fg("accent", currentModel)}`));
871
- lines.push(this.row(""));
872
- lines.push(this.row(` ${th.fg("dim", "Select thinking level (extended thinking budget):")}`));
873
- lines.push(this.row(""));
874
- const levelDescriptions = {
875
- "off": "No extended thinking",
876
- "minimal": "Brief reasoning",
877
- "low": "Light reasoning",
878
- "medium": "Moderate reasoning",
879
- "high": "Deep reasoning",
880
- "xhigh": "Extra-high reasoning (ultrathink)",
881
- "max": "Maximum reasoning",
882
- };
883
- const levels = this.getAvailableThinkingLevels(this.editingStep);
884
- if (levels.length === 0) {
885
- lines.push(this.row(` ${th.fg("dim", "No supported thinking levels")}`));
886
- }
887
- else {
888
- for (let i = 0; i < levels.length; i++) {
889
- const level = levels[i];
890
- const isSelected = i === this.thinkingSelectedIndex;
891
- const prefix = isSelected ? th.fg("accent", "→ ") : " ";
892
- const levelText = isSelected ? th.fg("accent", level) : level;
893
- const desc = th.fg("dim", ` - ${levelDescriptions[level]}`);
894
- lines.push(this.row(` ${prefix}${levelText}${desc}`));
895
- }
896
- }
897
- const contentLines = lines.length;
898
- const targetHeight = 16;
899
- for (let i = contentLines; i < targetHeight; i++) {
900
- lines.push(this.row(""));
901
- }
902
- const footerText = levels.length === 0
903
- ? " [Esc] Cancel "
904
- : " [Enter] Select • [Esc] Cancel • ↑↓ Navigate ";
905
- lines.push(this.renderFooter(footerText));
906
- return lines;
362
+ return new ClarifyView(this.theme, this.renderWidth).thinking({
363
+ step: this.stepLabel(this.editingStep ?? 0),
364
+ model: this.getEffectiveModel(this.editingStep ?? 0),
365
+ levels: this.getAvailableThinkingLevels(this.editingStep ?? 0),
366
+ selected: this.thinkingSelectedIndex,
367
+ });
907
368
  }
908
369
  renderSkillSelector() {
909
- const innerW = this.width - 2;
910
- const th = this.theme;
911
- const lines = [];
912
- const agentName = this.agentConfigs[this.editingStep]?.name ?? "unknown";
913
- const stepLabel = this.mode === 'single'
914
- ? agentName
915
- : this.mode === 'parallel'
916
- ? `Task ${this.editingStep + 1}: ${agentName}`
917
- : `Step ${this.editingStep + 1}: ${agentName}`;
918
- lines.push(this.renderHeader(` Select Skills (${stepLabel}) `));
919
- lines.push(this.row(""));
920
- const cursor = "\x1b[7m \x1b[27m";
921
- lines.push(this.row(` ${th.fg("dim", "Search: ")}${this.skillSearchQuery}${cursor}`));
922
- lines.push(this.row(""));
923
- const selected = [...this.skillSelectedNames].join(", ") || th.fg("dim", "(none)");
924
- lines.push(this.row(` ${th.fg("dim", "Selected: ")}${truncateToWidth(selected, innerW - 12)}`));
925
- lines.push(this.row(""));
926
- const selectorHeight = 10;
927
- if (this.filteredSkills.length === 0) {
928
- lines.push(this.row(` ${th.fg("dim", "No matching skills")}`));
929
- }
930
- else {
931
- let startIdx = 0;
932
- if (this.filteredSkills.length > selectorHeight) {
933
- startIdx = Math.max(0, this.skillCursorIndex - Math.floor(selectorHeight / 2));
934
- startIdx = Math.min(startIdx, this.filteredSkills.length - selectorHeight);
935
- }
936
- const endIdx = Math.min(startIdx + selectorHeight, this.filteredSkills.length);
937
- if (startIdx > 0) {
938
- lines.push(this.row(` ${th.fg("dim", ` ↑ ${startIdx} more`)}`));
939
- }
940
- for (let i = startIdx; i < endIdx; i++) {
941
- const skill = this.filteredSkills[i];
942
- const isCursor = i === this.skillCursorIndex;
943
- const isSelected = this.skillSelectedNames.has(skill.name);
944
- const prefix = isCursor ? th.fg("accent", "→ ") : " ";
945
- const checkbox = isSelected ? th.fg("success", "[x]") : "[ ]";
946
- const nameText = isCursor ? th.fg("accent", skill.name) : skill.name;
947
- const sourceBadge = th.fg("dim", ` [${skill.source}]`);
948
- const desc = skill.description
949
- ? th.fg("dim", ` - ${truncateToWidth(skill.description, 25)}`)
950
- : "";
951
- lines.push(this.row(` ${prefix}${checkbox} ${nameText}${sourceBadge}${desc}`));
952
- }
953
- const remaining = this.filteredSkills.length - endIdx;
954
- if (remaining > 0) {
955
- lines.push(this.row(` ${th.fg("dim", ` ↓ ${remaining} more`)}`));
956
- }
957
- }
958
- const targetHeight = 18;
959
- for (let i = lines.length; i < targetHeight; i++) {
960
- lines.push(this.row(""));
961
- }
962
- lines.push(this.renderFooter(" [Enter] Confirm • [Space] Toggle • [Esc] Cancel "));
963
- return lines;
370
+ const selected = [...this.skillSelectedNames].join(", ");
371
+ const choices = this.filteredSkills.map((skill) => `${this.skillSelectedNames.has(skill.name) ? "[x]" : "[ ]"} ${skill.name} [${skill.source}] - ${truncateToWidth(skill.description ?? "", 25)}`);
372
+ return new ClarifyView(this.theme, this.renderWidth).selector({
373
+ title: `Select Skills (${this.stepLabel(this.editingStep ?? 0)})`,
374
+ subtitles: [
375
+ `Search: ${this.skillSearchQuery}\x1b[7m \x1b[27m`,
376
+ `Selected: ${selected.length > 0 ? selected : "(none)"}`,
377
+ ],
378
+ choices,
379
+ selected: this.skillCursorIndex,
380
+ footer: "[Enter] Confirm • [Space] Toggle • [Esc] Cancel",
381
+ });
382
+ }
383
+ renderOverview() {
384
+ const progress = this.options.agentConfigs.some((_, index) => this.draft.behavior(index).progress);
385
+ return new ClarifyView(this.theme, this.renderWidth).overview({
386
+ mode: this.mode,
387
+ selected: this.selectedStep,
388
+ background: this.runInBackground,
389
+ originalTask: this.options.originalTask,
390
+ chainDir: this.options.chainDir,
391
+ progress,
392
+ notice: this.noticeMessage,
393
+ steps: this.options.agentConfigs.map((agent, index) => ({
394
+ name: agent.name,
395
+ label: this.stepLabel(index),
396
+ template: (this.draft.templates[index] ?? "").split("\n")[0] ?? "",
397
+ model: this.getEffectiveModel(index),
398
+ modelOverridden: this.draft.override(index)?.model !== undefined,
399
+ behavior: this.draft.behavior(index),
400
+ nextUsesPrevious: (this.draft.templates[index + 1] ?? "").includes("{previous}"),
401
+ })),
402
+ });
403
+ }
404
+ invalidate() {
405
+ /* Rendering reads the current theme and owns no cache. */
964
406
  }
965
- getFooterText() {
966
- const bgLabel = this.runInBackground ? '[b]g:ON' : '[b]g';
967
- switch (this.mode) {
968
- case 'single':
969
- return ` [Enter] Run • [Esc] Cancel • e m t w s ${bgLabel} `;
970
- case 'parallel':
971
- return ` [Enter] Run • [Esc] Cancel • e m t s ${bgLabel} • ↑↓ Nav `;
972
- case 'chain':
973
- return ` [Enter] Run • [Esc] Cancel • e m t w r p s ${bgLabel} • ↑↓ Nav `;
974
- }
975
- }
976
- appendNotice(lines) {
977
- if (!this.noticeMessage)
978
- return;
979
- const color = this.noticeMessage.type === "error" ? "error" : "success";
980
- lines.push(this.row(` ${this.theme.fg(color, this.noticeMessage.text)}`));
981
- }
982
- renderSingleMode() {
983
- const innerW = this.width - 2;
984
- const th = this.theme;
985
- const lines = [];
986
- const agentName = this.agentConfigs[0]?.name ?? "unknown";
987
- const maxHeaderLen = innerW - 4;
988
- const headerText = ` Agent: ${truncateToWidth(agentName, maxHeaderLen - 9)} `;
989
- lines.push(this.renderHeader(headerText));
990
- lines.push(this.row(""));
991
- const config = this.agentConfigs[0];
992
- const behavior = this.getEffectiveBehavior(0);
993
- const stepLabel = config.name;
994
- lines.push(this.row(` ${th.fg("accent", "▶ " + stepLabel)}`));
995
- const template = (this.templates[0] ?? "").split("\n")[0] ?? "";
996
- const taskLabel = th.fg("dim", "task: ");
997
- lines.push(this.row(` ${taskLabel}${truncateToWidth(template, innerW - 12)}`));
998
- const effectiveModel = this.getEffectiveModel(0);
999
- const override = this.behaviorOverrides.get(0);
1000
- const isOverridden = override?.model !== undefined;
1001
- const modelValue = isOverridden
1002
- ? th.fg("warning", effectiveModel) + th.fg("dim", " ✎")
1003
- : effectiveModel;
1004
- const modelLabel = th.fg("dim", "model: ");
1005
- lines.push(this.row(` ${modelLabel}${truncateToWidth(modelValue, innerW - 13)}`));
1006
- const writesValue = behavior.output === false
1007
- ? th.fg("dim", "(disabled)")
1008
- : (behavior.output || th.fg("dim", "(none)"));
1009
- const writesLabel = th.fg("dim", "writes: ");
1010
- lines.push(this.row(` ${writesLabel}${truncateToWidth(writesValue, innerW - 14)}`));
1011
- const skillsValue = behavior.skills === false
1012
- ? th.fg("dim", "(disabled)")
1013
- : (behavior.skills?.length ? behavior.skills.join(", ") : th.fg("dim", "(none)"));
1014
- const skillsLabel = th.fg("dim", "skills: ");
1015
- lines.push(this.row(` ${skillsLabel}${truncateToWidth(skillsValue, innerW - 14)}`));
1016
- lines.push(this.row(""));
1017
- this.appendNotice(lines);
1018
- lines.push(this.renderFooter(this.getFooterText()));
1019
- return lines;
1020
- }
1021
- renderParallelMode() {
1022
- const innerW = this.width - 2;
1023
- const th = this.theme;
1024
- const lines = [];
1025
- const headerText = ` Parallel Tasks (${this.agentConfigs.length}) `;
1026
- lines.push(this.renderHeader(headerText));
1027
- lines.push(this.row(""));
1028
- for (let i = 0; i < this.agentConfigs.length; i++) {
1029
- const config = this.agentConfigs[i];
1030
- const isSelected = i === this.selectedStep;
1031
- const color = isSelected ? "accent" : "dim";
1032
- const prefix = isSelected ? "▶ " : " ";
1033
- const taskPrefix = `Task ${i + 1}: `;
1034
- const maxNameLen = innerW - 4 - prefix.length - taskPrefix.length;
1035
- const agentName = config.name.length > maxNameLen
1036
- ? config.name.slice(0, maxNameLen - 1) + "…"
1037
- : config.name;
1038
- const taskLabel = `${taskPrefix}${agentName}`;
1039
- lines.push(this.row(` ${th.fg(color, prefix + taskLabel)}`));
1040
- const template = (this.templates[i] ?? "").split("\n")[0] ?? "";
1041
- const taskTextLabel = th.fg("dim", "task: ");
1042
- lines.push(this.row(` ${taskTextLabel}${truncateToWidth(template, innerW - 12)}`));
1043
- const effectiveModel = this.getEffectiveModel(i);
1044
- const override = this.behaviorOverrides.get(i);
1045
- const isOverridden = override?.model !== undefined;
1046
- const modelValue = isOverridden
1047
- ? th.fg("warning", effectiveModel) + th.fg("dim", " ✎")
1048
- : effectiveModel;
1049
- const modelLabel = th.fg("dim", "model: ");
1050
- lines.push(this.row(` ${modelLabel}${truncateToWidth(modelValue, innerW - 13)}`));
1051
- const behavior = this.getEffectiveBehavior(i);
1052
- const skillsValue = behavior.skills === false
1053
- ? th.fg("dim", "(disabled)")
1054
- : (behavior.skills?.length ? behavior.skills.join(", ") : th.fg("dim", "(none)"));
1055
- const skillsLabel = th.fg("dim", "skills: ");
1056
- lines.push(this.row(` ${skillsLabel}${truncateToWidth(skillsValue, innerW - 14)}`));
1057
- lines.push(this.row(""));
407
+ dispose() {
408
+ if (this.noticeMessageTimer !== undefined) {
409
+ clearTimeout(this.noticeMessageTimer);
1058
410
  }
1059
- this.appendNotice(lines);
1060
- lines.push(this.renderFooter(this.getFooterText()));
1061
- return lines;
411
+ this.noticeMessageTimer = undefined;
1062
412
  }
1063
- renderChainMode() {
1064
- const innerW = this.width - 2;
1065
- const th = this.theme;
1066
- const lines = [];
1067
- const chainLabel = this.agentConfigs.map((c) => c.name).join(" → ");
1068
- const maxHeaderLen = innerW - 4;
1069
- const headerText = ` Chain: ${truncateToWidth(chainLabel, maxHeaderLen - 9)} `;
1070
- lines.push(this.renderHeader(headerText));
1071
- lines.push(this.row(""));
1072
- const taskPreview = truncateToWidth(this.originalTask, innerW - 16);
1073
- lines.push(this.row(` Original Task: ${taskPreview}`));
1074
- const chainDirPreview = truncateToWidth(this.chainDir ?? "", innerW - 12);
1075
- lines.push(this.row(` Chain Dir: ${th.fg("dim", chainDirPreview)}`));
1076
- const progressEnabled = this.agentConfigs.some((_, i) => this.getEffectiveBehavior(i).progress);
1077
- const progressValue = progressEnabled ? th.fg("success", "enabled") : th.fg("dim", "disabled");
1078
- lines.push(this.row(` Progress: ${progressValue} ${th.fg("dim", "(press [p] to toggle)")}`));
1079
- lines.push(this.row(""));
1080
- for (let i = 0; i < this.agentConfigs.length; i++) {
1081
- const config = this.agentConfigs[i];
1082
- const isSelected = i === this.selectedStep;
1083
- const behavior = this.getEffectiveBehavior(i);
1084
- const color = isSelected ? "accent" : "dim";
1085
- const prefix = isSelected ? "▶ " : " ";
1086
- const stepPrefix = `Step ${i + 1}: `;
1087
- const maxNameLen = innerW - 4 - prefix.length - stepPrefix.length;
1088
- const agentName = config.name.length > maxNameLen
1089
- ? config.name.slice(0, maxNameLen - 1) + "…"
1090
- : config.name;
1091
- const stepLabel = `${stepPrefix}${agentName}`;
1092
- lines.push(this.row(` ${th.fg(color, prefix + stepLabel)}`));
1093
- const template = (this.templates[i] ?? "").split("\n")[0] ?? "";
1094
- const highlighted = template
1095
- .replace(/\{task\}/g, th.fg("success", "{task}"))
1096
- .replace(/\{previous\}/g, th.fg("warning", "{previous}"))
1097
- .replace(/\{chain_dir\}/g, th.fg("accent", "{chain_dir}"));
1098
- const templateLabel = th.fg("dim", "task: ");
1099
- lines.push(this.row(` ${templateLabel}${truncateToWidth(highlighted, innerW - 12)}`));
1100
- const effectiveModel = this.getEffectiveModel(i);
1101
- const override = this.behaviorOverrides.get(i);
1102
- const isOverridden = override?.model !== undefined;
1103
- const modelValue = isOverridden
1104
- ? th.fg("warning", effectiveModel) + th.fg("dim", " ✎")
1105
- : effectiveModel;
1106
- const modelLabel = th.fg("dim", "model: ");
1107
- lines.push(this.row(` ${modelLabel}${truncateToWidth(modelValue, innerW - 13)}`));
1108
- const writesValue = behavior.output === false
1109
- ? th.fg("dim", "(disabled)")
1110
- : (behavior.output || th.fg("dim", "(none)"));
1111
- const writesLabel = th.fg("dim", "writes: ");
1112
- lines.push(this.row(` ${writesLabel}${truncateToWidth(writesValue, innerW - 14)}`));
1113
- const readsValue = behavior.reads === false
1114
- ? th.fg("dim", "(disabled)")
1115
- : (behavior.reads && behavior.reads.length > 0
1116
- ? behavior.reads.join(", ")
1117
- : th.fg("dim", "(none)"));
1118
- const readsLabel = th.fg("dim", "reads: ");
1119
- lines.push(this.row(` ${readsLabel}${truncateToWidth(readsValue, innerW - 13)}`));
1120
- const skillsValue = behavior.skills === false
1121
- ? th.fg("dim", "(disabled)")
1122
- : (behavior.skills?.length ? behavior.skills.join(", ") : th.fg("dim", "(none)"));
1123
- const skillsLabel = th.fg("dim", "skills: ");
1124
- lines.push(this.row(` ${skillsLabel}${truncateToWidth(skillsValue, innerW - 14)}`));
1125
- if (progressEnabled) {
1126
- const isFirstStep = i === 0;
1127
- const progressAction = isFirstStep
1128
- ? th.fg("success", "writes progress.md")
1129
- : th.fg("accent", "reads progress.md");
1130
- const progressLabel = th.fg("dim", "progress: ");
1131
- lines.push(this.row(` ${progressLabel}${progressAction}`));
1132
- }
1133
- if (i < this.agentConfigs.length - 1) {
1134
- const nextStepUsePrevious = (this.templates[i + 1] ?? "").includes("{previous}");
1135
- if (nextStepUsePrevious) {
1136
- const indicator = th.fg("dim", " ↳ response → ") + th.fg("warning", "{previous}");
1137
- lines.push(this.row(indicator));
1138
- }
1139
- }
1140
- lines.push(this.row(""));
1141
- }
1142
- this.appendNotice(lines);
1143
- lines.push(this.renderFooter(this.getFooterText()));
1144
- return lines;
413
+ }
414
+ function navigationDirection(data) {
415
+ if (matchesKey(data, "up")) {
416
+ return -1;
1145
417
  }
1146
- invalidate() { }
1147
- dispose() {
1148
- if (this.noticeMessageTimer)
1149
- clearTimeout(this.noticeMessageTimer);
1150
- this.noticeMessageTimer = null;
418
+ return matchesKey(data, "down") ? 1 : 0;
419
+ }
420
+ function moveSelection(index, direction, count) {
421
+ return count === 0 ? 0 : (index + direction + count) % count;
422
+ }
423
+ function searchInput(query, data) {
424
+ if (matchesKey(data, "backspace")) {
425
+ return query.slice(0, -1);
1151
426
  }
427
+ return data.length === 1 && data.charCodeAt(0) >= 32 ? query + data : query;
1152
428
  }