one-code-extension 0.4.1 → 0.5.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 (419) hide show
  1. package/README.md +16 -5
  2. package/agents/general-purpose.md +2 -2
  3. package/docs/guide/README.md +2 -1
  4. package/docs/guide/bring-your-claude-code-setup.md +60 -9
  5. package/docs/guide/configuration.md +48 -14
  6. package/docs/guide/differences-from-claude-code.md +32 -23
  7. package/docs/guide/doctor.md +20 -13
  8. package/docs/guide/hooks.md +18 -3
  9. package/docs/guide/installation.md +27 -4
  10. package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
  11. package/docs/guide/providers-and-models.md +137 -54
  12. package/docs/guide/reference.md +27 -15
  13. package/docs/guide/sessions-and-context.md +69 -16
  14. package/docs/guide/skills-plugins-and-mcp.md +57 -20
  15. package/docs/guide/subagents-and-workflows.md +95 -29
  16. package/docs/guide/tasks-and-background-work.md +101 -26
  17. package/docs/guide/terminal-interface.md +24 -12
  18. package/docs/guide/tools.md +46 -5
  19. package/docs/guide/troubleshooting.md +13 -9
  20. package/docs/guide/windows.md +58 -31
  21. package/extensions/artifacts/gallery.ts +227 -0
  22. package/extensions/artifacts/index.ts +288 -0
  23. package/extensions/artifacts/store.ts +570 -0
  24. package/extensions/artifacts/viewer.ts +199 -0
  25. package/extensions/ask-user/description.ts +67 -0
  26. package/extensions/ask-user/dialogs.ts +80 -0
  27. package/extensions/ask-user/index.ts +37 -33
  28. package/extensions/ask-user/questions.ts +32 -7
  29. package/extensions/ask-user/widget.ts +45 -25
  30. package/extensions/auto-mode/actions.ts +6 -0
  31. package/extensions/auto-mode/cache.ts +88 -0
  32. package/extensions/auto-mode/classifier-prompt.ts +5 -4
  33. package/extensions/auto-mode/classifier.ts +290 -65
  34. package/extensions/auto-mode/config.ts +72 -20
  35. package/extensions/auto-mode/decision-log.ts +2 -0
  36. package/extensions/auto-mode/defaults.ts +1 -1
  37. package/extensions/auto-mode/denials.ts +123 -0
  38. package/extensions/auto-mode/git-checkout-programs.ts +187 -0
  39. package/extensions/auto-mode/git-status-meta.ts +159 -0
  40. package/extensions/auto-mode/history.ts +189 -0
  41. package/extensions/auto-mode/instructions.ts +58 -52
  42. package/extensions/auto-mode/model-select.ts +159 -134
  43. package/extensions/auto-mode/paths.ts +57 -5
  44. package/extensions/auto-mode/pause.ts +57 -2
  45. package/extensions/auto-mode/prompt.ts +37 -21
  46. package/extensions/auto-mode/read-only-options.ts +724 -0
  47. package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
  48. package/extensions/auto-mode/rules.ts +18 -1
  49. package/extensions/auto-mode/safety-floor.ts +361 -18
  50. package/extensions/auto-mode/setup-run.ts +20 -14
  51. package/extensions/auto-mode/shell-analysis.ts +764 -490
  52. package/extensions/auto-mode/shell-parse.ts +885 -0
  53. package/extensions/auto-mode/system-note.ts +33 -0
  54. package/extensions/auto-mode/transcript.ts +80 -51
  55. package/extensions/background/cron.ts +515 -0
  56. package/extensions/background/index.ts +514 -236
  57. package/extensions/background/loop-fire.ts +136 -0
  58. package/extensions/background/loop-preamble.ts +59 -0
  59. package/extensions/background/loop-skill.ts +163 -0
  60. package/extensions/background/monitor-batch.ts +53 -0
  61. package/extensions/background/noop-fold.ts +128 -0
  62. package/extensions/background/switch-guard.ts +52 -0
  63. package/extensions/background/wakeup.ts +159 -55
  64. package/extensions/bash/background.ts +53 -17
  65. package/extensions/bash/child.ts +7 -0
  66. package/extensions/bash/description.ts +188 -0
  67. package/extensions/bash/guards.ts +15 -67
  68. package/extensions/bash/index.ts +88 -35
  69. package/extensions/branding/compaction-keep.ts +63 -0
  70. package/extensions/branding/declined.ts +22 -0
  71. package/extensions/branding/index.ts +133 -9
  72. package/extensions/branding/prompt-editor.ts +53 -6
  73. package/extensions/branding/queued-edit.ts +31 -0
  74. package/extensions/branding/replaced-builtins.ts +68 -0
  75. package/extensions/branding/startup.ts +33 -71
  76. package/extensions/btw/index.ts +229 -71
  77. package/extensions/btw/panel.ts +228 -100
  78. package/extensions/btw/prompt.ts +41 -4
  79. package/extensions/claude-compat/index.ts +7 -11
  80. package/extensions/claude-context/index.ts +446 -58
  81. package/extensions/compaction/cut.ts +157 -0
  82. package/extensions/compaction/index.ts +216 -75
  83. package/extensions/compaction/kept-thinking.ts +62 -0
  84. package/extensions/compaction/prompt.ts +15 -12
  85. package/extensions/context-budget/index.ts +24 -5
  86. package/extensions/context-management/index.ts +25 -0
  87. package/extensions/doctor/build.ts +59 -8
  88. package/extensions/doctor/builtins.ts +84 -0
  89. package/extensions/doctor/cli.ts +7 -1
  90. package/extensions/doctor/compat.ts +88 -23
  91. package/extensions/doctor/dependencies.ts +41 -5
  92. package/extensions/doctor/fix-prompt.ts +9 -2
  93. package/extensions/doctor/index.ts +102 -58
  94. package/extensions/doctor/models.ts +102 -63
  95. package/extensions/doctor/presets.ts +42 -27
  96. package/extensions/doctor/report.ts +19 -2
  97. package/extensions/doctor/viewer.ts +49 -27
  98. package/extensions/effort/index.ts +72 -19
  99. package/extensions/effort/settings.ts +21 -0
  100. package/extensions/effort/slider.ts +24 -20
  101. package/extensions/exit/index.ts +32 -1
  102. package/extensions/file-tracker/index.ts +337 -26
  103. package/extensions/file-tracker/replay.ts +96 -10
  104. package/extensions/file-tracker/restore.ts +63 -0
  105. package/extensions/file-tracker/results.ts +66 -0
  106. package/extensions/file-tracker/shell-reads.ts +211 -0
  107. package/extensions/file-tracker/tracker.ts +32 -15
  108. package/extensions/footer/footer-line.ts +45 -24
  109. package/extensions/footer/index.ts +47 -2
  110. package/extensions/footer/pr.ts +34 -5
  111. package/extensions/hooks/compaction.ts +7 -0
  112. package/extensions/hooks/executor.ts +32 -30
  113. package/extensions/hooks/index.ts +198 -31
  114. package/extensions/hooks/matcher.ts +3 -0
  115. package/extensions/hooks/protocol.ts +41 -3
  116. package/extensions/hooks/settings.ts +50 -19
  117. package/extensions/hooks/subagent-bridge.ts +2 -0
  118. package/extensions/hooks/trust.ts +13 -5
  119. package/extensions/idle-compact/index.ts +119 -0
  120. package/extensions/idle-compact/policy.ts +143 -0
  121. package/extensions/init/index.ts +11 -10
  122. package/extensions/init/prompt.ts +25 -32
  123. package/extensions/interrupted/index.ts +13 -1
  124. package/extensions/kept-thinking/index.ts +27 -0
  125. package/extensions/lib/agent-cron.ts +130 -0
  126. package/extensions/lib/agent-loader.ts +15 -1
  127. package/extensions/lib/agent-view.ts +85 -0
  128. package/extensions/lib/anthropic-payload.ts +82 -1
  129. package/extensions/lib/anthropic-server-call.ts +230 -0
  130. package/extensions/lib/anthropic-server-tools.ts +485 -0
  131. package/extensions/lib/app-launch.mjs +94 -0
  132. package/extensions/lib/argument-hints.ts +190 -0
  133. package/extensions/lib/atomic-write.ts +6 -2
  134. package/extensions/lib/auto-memory.ts +75 -0
  135. package/extensions/lib/bash-parser.ts +84 -0
  136. package/extensions/lib/btw-fork.ts +93 -0
  137. package/extensions/lib/child-extensions.ts +42 -0
  138. package/extensions/lib/child-writes.ts +45 -0
  139. package/extensions/lib/claude-context.ts +515 -100
  140. package/extensions/lib/claude-external-includes.ts +68 -0
  141. package/extensions/lib/claude-id.ts +22 -0
  142. package/extensions/lib/claude-rules.ts +323 -0
  143. package/extensions/lib/claude-settings.ts +76 -4
  144. package/extensions/lib/compaction-boundary.ts +36 -0
  145. package/extensions/lib/compaction-keep.mjs +83 -0
  146. package/extensions/lib/config-mode.ts +112 -0
  147. package/extensions/lib/consent-dialogs.ts +63 -0
  148. package/extensions/lib/consent-preview.ts +5 -1
  149. package/extensions/lib/consent-stores.ts +48 -0
  150. package/extensions/lib/context-facts.ts +78 -0
  151. package/extensions/lib/context-stack.ts +81 -0
  152. package/extensions/lib/deferred.ts +219 -25
  153. package/extensions/lib/dock-rows.ts +60 -0
  154. package/extensions/lib/empty-tool-result.ts +27 -0
  155. package/extensions/lib/environment-block.ts +76 -0
  156. package/extensions/lib/fetch-timeout.ts +24 -6
  157. package/extensions/{system-prompt → lib}/git-status.ts +51 -13
  158. package/extensions/lib/git.ts +75 -13
  159. package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
  160. package/extensions/lib/grammars/README.md +2 -0
  161. package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
  162. package/extensions/lib/guide-docs.ts +45 -0
  163. package/extensions/lib/headless-output.ts +46 -0
  164. package/extensions/lib/hook-gate.ts +7 -7
  165. package/extensions/lib/interrupt.ts +20 -4
  166. package/extensions/lib/key-input.ts +455 -0
  167. package/extensions/lib/keys.ts +14 -1
  168. package/extensions/lib/local-command.ts +47 -13
  169. package/extensions/lib/mcp-overrides.ts +1 -1
  170. package/extensions/lib/mcp-share.ts +46 -0
  171. package/extensions/lib/memory-content.ts +146 -0
  172. package/extensions/lib/memory-index.ts +18 -0
  173. package/extensions/lib/memory.ts +134 -123
  174. package/extensions/lib/model-catalog/huggingface.json +174 -0
  175. package/extensions/lib/model-catalog/models-dev.json +18297 -0
  176. package/extensions/lib/model-catalog/openrouter.json +5252 -0
  177. package/extensions/lib/model-catalog-data.ts +513 -0
  178. package/extensions/lib/model-catalog.ts +462 -0
  179. package/extensions/lib/model-choice-warnings.ts +137 -0
  180. package/extensions/lib/model-pairings.ts +152 -0
  181. package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
  182. package/extensions/lib/model-policy.ts +112 -15
  183. package/extensions/lib/model-tier.ts +283 -355
  184. package/extensions/lib/model-unusable.ts +1 -1
  185. package/extensions/lib/newer-model.ts +100 -0
  186. package/extensions/lib/notices.ts +87 -0
  187. package/extensions/lib/notifications.ts +308 -64
  188. package/extensions/lib/one-code-settings.ts +74 -8
  189. package/extensions/lib/open-browser.ts +63 -0
  190. package/extensions/lib/openrouter-generation.ts +94 -0
  191. package/extensions/lib/operation-deadline.ts +30 -0
  192. package/extensions/lib/original-command.ts +4 -4
  193. package/extensions/lib/package-version.ts +1 -1
  194. package/extensions/lib/panel-focus.ts +134 -0
  195. package/extensions/lib/paths.ts +17 -2
  196. package/extensions/lib/permission-gate.ts +72 -6
  197. package/extensions/lib/persisted-output.ts +60 -8
  198. package/extensions/lib/pi-ai-estimate.ts +19 -3
  199. package/extensions/lib/pi-version.ts +21 -2
  200. package/extensions/lib/plan-mode-channels.ts +20 -5
  201. package/extensions/lib/plugins.ts +26 -17
  202. package/extensions/lib/powershell-parse-script.ts +208 -0
  203. package/extensions/lib/powershell-parser.ts +344 -0
  204. package/extensions/lib/process-tree.ts +131 -8
  205. package/extensions/lib/prompt-options.ts +82 -0
  206. package/extensions/lib/queued-delivery.ts +44 -0
  207. package/extensions/lib/reminders.ts +364 -58
  208. package/extensions/lib/replaced-builtins.mjs +199 -0
  209. package/extensions/lib/replay-call.ts +70 -0
  210. package/extensions/lib/request-replay.ts +439 -0
  211. package/extensions/lib/scratchpad.ts +81 -14
  212. package/extensions/lib/send-now-channel.ts +7 -0
  213. package/extensions/lib/session-model-tier.ts +51 -0
  214. package/extensions/lib/session-work.ts +64 -0
  215. package/extensions/lib/settings-channels.ts +4 -4
  216. package/extensions/lib/shell-exit-semantics.ts +170 -0
  217. package/extensions/lib/shell-quote.ts +11 -0
  218. package/extensions/lib/shell-result.ts +70 -0
  219. package/extensions/lib/shell-spawn.ts +70 -6
  220. package/extensions/lib/shell-tool.ts +75 -25
  221. package/extensions/lib/side-call-cache.ts +124 -0
  222. package/extensions/lib/side-call-run.ts +96 -0
  223. package/extensions/lib/side-call-usage.ts +38 -0
  224. package/extensions/lib/side-call.ts +13 -1
  225. package/extensions/lib/single-flight.ts +18 -0
  226. package/extensions/lib/skill-body.ts +42 -0
  227. package/extensions/lib/skill-scan.ts +63 -24
  228. package/extensions/lib/spill-file.ts +140 -0
  229. package/extensions/lib/subagent-handback.ts +113 -0
  230. package/extensions/lib/system-role.ts +156 -0
  231. package/extensions/lib/terminal-text.ts +114 -0
  232. package/extensions/lib/text-width.ts +47 -16
  233. package/extensions/lib/timer-ops.ts +20 -0
  234. package/extensions/lib/tool-additions.ts +149 -0
  235. package/extensions/lib/tool-call-corruption.ts +134 -0
  236. package/extensions/lib/tool-path.ts +66 -0
  237. package/extensions/lib/tool-variants.ts +69 -0
  238. package/extensions/lib/tui-render.ts +255 -27
  239. package/extensions/lib/turn-budget-layout.ts +143 -0
  240. package/extensions/lib/update-check.mjs +15 -2
  241. package/extensions/lib/usage-bus.ts +1 -1
  242. package/extensions/lib/user-denial.ts +38 -0
  243. package/extensions/lib/workspace-channel.ts +16 -0
  244. package/extensions/lib/worktree-channel.ts +31 -3
  245. package/extensions/lib/worktree-isolation.ts +40 -10
  246. package/extensions/lsp/client.ts +8 -9
  247. package/extensions/lsp/index.ts +70 -4
  248. package/extensions/lsp/keep-alive.ts +1 -1
  249. package/extensions/lsp/trust.ts +119 -0
  250. package/extensions/lsp/watcher.ts +1 -1
  251. package/extensions/mcp/announce.ts +87 -5
  252. package/extensions/mcp/client.ts +161 -48
  253. package/extensions/mcp/config.ts +132 -8
  254. package/extensions/mcp/discovery.ts +19 -0
  255. package/extensions/mcp/index.ts +209 -71
  256. package/extensions/mcp/oauth/browser.ts +38 -17
  257. package/extensions/mcp/oauth/flow.ts +25 -17
  258. package/extensions/mcp/oauth/provider.ts +14 -8
  259. package/extensions/mcp/oauth/store.ts +34 -16
  260. package/extensions/mcp/panel/keys.ts +12 -11
  261. package/extensions/mcp/panel/render.ts +9 -6
  262. package/extensions/mcp/schema.ts +104 -5
  263. package/extensions/mcp/trust.ts +151 -67
  264. package/extensions/memory/entries.ts +23 -14
  265. package/extensions/memory/index.ts +134 -28
  266. package/extensions/memory/open-external.ts +33 -18
  267. package/extensions/memory/panel.ts +135 -26
  268. package/extensions/newer-model/index.ts +31 -0
  269. package/extensions/notebook/description.ts +18 -0
  270. package/extensions/notebook/index.ts +6 -5
  271. package/extensions/notebook/notebook.ts +1 -1
  272. package/extensions/permissions/index.ts +1300 -381
  273. package/extensions/permissions/matcher.ts +425 -74
  274. package/extensions/permissions/modes.ts +6 -4
  275. package/extensions/permissions/panel/host.ts +130 -0
  276. package/extensions/permissions/panel/keys.ts +48 -0
  277. package/extensions/permissions/panel/render.ts +450 -0
  278. package/extensions/permissions/panel/state.ts +550 -0
  279. package/extensions/permissions/powershell-cmdlets.ts +198 -0
  280. package/extensions/permissions/powershell-paths.ts +197 -0
  281. package/extensions/permissions/powershell-rules.ts +207 -280
  282. package/extensions/permissions/powershell-tree.ts +557 -0
  283. package/extensions/permissions/project-trust.ts +27 -11
  284. package/extensions/permissions/protected-paths.ts +14 -2
  285. package/extensions/permissions/session-grant.ts +6 -0
  286. package/extensions/permissions/settings.ts +166 -31
  287. package/extensions/permissions/subagent-gate.ts +25 -0
  288. package/extensions/permissions/workspace.ts +78 -0
  289. package/extensions/plan-mode/index.ts +156 -69
  290. package/extensions/plan-mode/reminder.ts +89 -21
  291. package/extensions/plan-mode/texts.ts +177 -0
  292. package/extensions/plan-mode/viewer.ts +92 -27
  293. package/extensions/plugins/counts.ts +3 -3
  294. package/extensions/plugins/index.ts +61 -46
  295. package/extensions/plugins/install/install.ts +16 -0
  296. package/extensions/plugins/marketplace/sync.ts +9 -3
  297. package/extensions/plugins/marketplace/types.ts +3 -1
  298. package/extensions/plugins/panel/keys.ts +21 -24
  299. package/extensions/plugins/panel/render.ts +12 -6
  300. package/extensions/plugins/shell-expand.ts +107 -0
  301. package/extensions/plugins/template.ts +32 -15
  302. package/extensions/powershell/child.ts +7 -0
  303. package/extensions/powershell/description.ts +38 -6
  304. package/extensions/powershell/guards.ts +1 -1
  305. package/extensions/powershell/index.ts +12 -7
  306. package/extensions/recap/index.ts +65 -60
  307. package/extensions/recap/prompt.ts +2 -2
  308. package/extensions/recap/scheduler.ts +3 -5
  309. package/extensions/search-tools/grep-merge.ts +39 -0
  310. package/extensions/search-tools/index.ts +0 -0
  311. package/extensions/send-now/chord.ts +62 -0
  312. package/extensions/send-now/index.ts +133 -0
  313. package/extensions/session-title/index.ts +3 -0
  314. package/extensions/session-title/title.ts +4 -1
  315. package/extensions/skill/code-review.ts +97 -0
  316. package/extensions/skill/index.ts +421 -101
  317. package/extensions/skill/invoke.ts +128 -18
  318. package/extensions/skill/listing.ts +133 -9
  319. package/extensions/skill/panel/keys.ts +15 -16
  320. package/extensions/skill/panel/render.ts +5 -2
  321. package/extensions/skill/panel/state.ts +12 -1
  322. package/extensions/subagents/agent-args.ts +39 -0
  323. package/extensions/subagents/agent-description.ts +132 -0
  324. package/extensions/subagents/agents.ts +116 -23
  325. package/extensions/subagents/announce.ts +69 -0
  326. package/extensions/subagents/default-model.ts +2 -3
  327. package/extensions/subagents/delegation-steer.ts +4 -4
  328. package/extensions/subagents/fork-cache.ts +76 -0
  329. package/extensions/subagents/guide-agent.ts +189 -0
  330. package/extensions/subagents/hand-back-review.ts +3 -0
  331. package/extensions/subagents/index.ts +1493 -824
  332. package/extensions/subagents/live-runs.ts +26 -6
  333. package/extensions/subagents/model-select.ts +38 -25
  334. package/extensions/subagents/outcome.ts +2 -2
  335. package/extensions/subagents/panel-keys.ts +73 -31
  336. package/extensions/subagents/panel-render.ts +120 -96
  337. package/extensions/subagents/panel-widget.ts +60 -89
  338. package/extensions/subagents/runner.ts +108 -80
  339. package/extensions/subagents/runs.ts +39 -5
  340. package/extensions/subagents/send-to-main-tool.ts +1 -1
  341. package/extensions/subagents/session-turns.ts +32 -8
  342. package/extensions/subagents/shell-panel.ts +36 -178
  343. package/extensions/subagents/tasks-dialog.ts +170 -0
  344. package/extensions/subagents/viewport-mirror.ts +95 -0
  345. package/extensions/subagents/worktree.ts +57 -13
  346. package/extensions/system-prompt/environment.ts +19 -19
  347. package/extensions/system-prompt/idle-turn.ts +65 -0
  348. package/extensions/system-prompt/index.ts +131 -47
  349. package/extensions/system-prompt/template.ts +24 -50
  350. package/extensions/system-prompt/tiers/common.ts +23 -64
  351. package/extensions/system-prompt/tiers/frontier.ts +32 -11
  352. package/extensions/system-prompt/tiers/low.ts +67 -45
  353. package/extensions/system-prompt/tiers/mid.ts +96 -38
  354. package/extensions/system-reminder/index.ts +175 -13
  355. package/extensions/tasks/descriptions.ts +190 -0
  356. package/extensions/tasks/index.ts +120 -41
  357. package/extensions/tasks/store.ts +83 -16
  358. package/extensions/tool-call-corruption/child.ts +7 -0
  359. package/extensions/tool-call-corruption/index.ts +237 -0
  360. package/extensions/tool-search/announce.ts +16 -0
  361. package/extensions/tool-search/index.ts +189 -41
  362. package/extensions/tool-style/index.ts +68 -10
  363. package/extensions/tool-style/write-description.ts +40 -0
  364. package/extensions/tool-style/write-render.ts +237 -0
  365. package/extensions/turn-duration/index.ts +7 -3
  366. package/extensions/turn-duration/line.ts +47 -4
  367. package/extensions/web/backends.ts +6 -9
  368. package/extensions/web/description.ts +50 -0
  369. package/extensions/web/index.ts +118 -53
  370. package/extensions/web/native-call.ts +15 -0
  371. package/extensions/web/native-results.ts +41 -0
  372. package/extensions/web-fetch/description.ts +42 -0
  373. package/extensions/web-fetch/extract.ts +53 -6
  374. package/extensions/web-fetch/index.ts +125 -76
  375. package/extensions/web-fetch/page-content.ts +19 -0
  376. package/extensions/web-fetch/response.ts +49 -0
  377. package/extensions/web-fetch/summarize.ts +9 -10
  378. package/extensions/workflow/agent-session.ts +105 -18
  379. package/extensions/workflow/args.ts +24 -0
  380. package/extensions/workflow/description.ts +67 -0
  381. package/extensions/workflow/globals.ts +17 -3
  382. package/extensions/workflow/index.ts +145 -162
  383. package/extensions/workflow/records.ts +1 -0
  384. package/extensions/workflow/run-manager.ts +29 -6
  385. package/extensions/workflow/saved-workflows.ts +3 -2
  386. package/extensions/workflow/types.ts +22 -1
  387. package/extensions/workflow/viewer.ts +54 -24
  388. package/extensions/workflow/widget.ts +20 -0
  389. package/extensions/worktree/descriptions.ts +91 -0
  390. package/extensions/worktree/guards.ts +316 -54
  391. package/extensions/worktree/index.ts +121 -39
  392. package/extensions/worktree/powershell-guards.ts +167 -0
  393. package/extensions/worktree/rewrite.ts +14 -16
  394. package/package.json +18 -7
  395. package/skills/artifact-design/SKILL.md +85 -0
  396. package/skills/code-review/SKILL.md +20 -20
  397. package/skills/code-review/cells/arguments-and-flags.md +19 -0
  398. package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
  399. package/skills/code-review/cells/high-8-inline.md +122 -0
  400. package/skills/code-review/cells/low-1-pass-max4.md +27 -0
  401. package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
  402. package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
  403. package/skills/code-review/cells/medium-3x5-verify.md +132 -0
  404. package/skills/code-review/cells/minimal-prompt.md +32 -0
  405. package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
  406. package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
  407. package/skills/fewer-permission-prompts/SKILL.md +1 -1
  408. package/skills/loop/SKILL.md +11 -0
  409. package/skills/security-review/SKILL.md +2 -5
  410. package/skills/simplify/SKILL.md +7 -5
  411. package/skills/workflow-authoring/SKILL.md +143 -0
  412. package/themes/onecode-light.json +1 -1
  413. package/themes/onecode.json +1 -1
  414. package/types/app.d.ts +58 -0
  415. package/types/community-packages.d.ts +26 -1
  416. package/extensions/auto-mode/recoverability.ts +0 -229
  417. package/extensions/lib/capability-index.ts +0 -466
  418. package/extensions/lib/model-facts.generated.json +0 -2675
  419. package/extensions/lib/model-facts.ts +0 -162
@@ -6,10 +6,11 @@
6
6
  * index.ts; this file owns layout only, mirroring workflow/viewer.ts.
7
7
  */
8
8
 
9
- import { cutPlainText as cut, formatDuration, splitCell, splitRow, visibleWidth } from "../lib/tui-render.ts";
10
- import { hardWrapColumns } from "../lib/text-width.ts";
9
+ import { cutPlainText as cut, formatDuration, splitCell, splitRow, visibleWidth, wrapProse } from "../lib/tui-render.ts";
11
10
  import { formatTokenCount } from "./usage.ts";
11
+ import { sanitizeDisplayText } from "../lib/terminal-text.ts";
12
12
  import { type LiveRun, type LiveStatus, streamingText, type TranscriptBlock } from "./live-runs.ts";
13
+ import { FORK_AGENT } from "./runs.ts";
13
14
 
14
15
  export interface Paint {
15
16
  fg(color: string, text: string): string;
@@ -31,27 +32,33 @@ export interface PanelRow {
31
32
  }
32
33
 
33
34
  /**
34
- * A finished run stays in the strip this long (its "Completed"/"Failed" beat),
35
- * then drops out — Claude Code's tree only tracks live work. The run itself
36
- * stays in the registry: the viewer, /agents, and SendMessage still reach it.
35
+ * How long the strip says `/tasks to see subagents` after a run it was showing
36
+ * finishes. Claude Code 2.1.283 drops a finished agent from the strip at once
37
+ * and shows that hint in the footer for a few seconds (findings §40); the run
38
+ * stays in the registry, where `/tasks`, `/agents` and SendMessage reach it.
37
39
  */
38
40
  export const STRIP_LINGER_MS = 5000;
39
41
 
42
+ /** The strip's short notice after a run leaves it. */
43
+ export const TASKS_NOTICE = "/tasks to see subagents";
44
+
45
+ /** A row's left label: a fork's name, any other agent's type (Claude Code's strip). */
46
+ export function rowLabel(run: Pick<LiveRun, "agentType" | "name">): string {
47
+ return run.agentType === FORK_AGENT ? run.name : run.agentType;
48
+ }
49
+
40
50
  /**
41
51
  * Build the row list: `main` first, then the live children as a TREE — roots
42
52
  * newest-first, each root followed by its nested spawns (a child's own Agent
43
53
  * calls) in spawn order, indented one level (CC's `└` rows). `mainBusy` drives
44
- * main's dot. Running children always show; settled ones — done, failed, or an
45
- * idle resident whose turn ended — only within STRIP_LINGER_MS of finishing
46
- * (finishedAt is stamped by finish() AND settle()). A nested run whose parent
47
- * already left the strip surfaces at root level rather than vanishing.
48
- *
49
- * `pinnedId` keeps one settled run in the strip past its linger window — the
50
- * run whose transcript is currently open. Without it a finished child drops out
51
- * from under an open viewer, the strip empties, and the panel clears focus,
52
- * orphaning the overlay (no key then reaches read mode to close it).
54
+ * main's dot. Running children always show; a settled one (done, failed, or
55
+ * an idle resident whose turn ended) leaves at once, as in Claude Code, unless
56
+ * it is `pinnedId`: the run whose transcript is open stays, shown `idle`, until
57
+ * the user switches back (findings §40). Without the pin a finished child
58
+ * would drop out from under an open viewer and orphan the overlay. A nested
59
+ * run whose parent already left the strip surfaces at root level.
53
60
  */
54
- export function buildRows(runs: LiveRun[], mainBusy: boolean, now: number, pinnedId?: string): PanelRow[] {
61
+ export function buildRows(runs: LiveRun[], mainBusy: boolean, pinnedId?: string): PanelRow[] {
55
62
  const rows: PanelRow[] = [
56
63
  {
57
64
  label: "main",
@@ -62,9 +69,7 @@ export function buildRows(runs: LiveRun[], mainBusy: boolean, now: number, pinne
62
69
  },
63
70
  ];
64
71
  // `runs` arrives newest-first; children under a parent read best in spawn order.
65
- const visible = runs.filter(
66
- (run) => run.status === "running" || run.taskId === pinnedId || (run.finishedAt ?? now) > now - STRIP_LINGER_MS,
67
- );
72
+ const visible = runs.filter((run) => run.status === "running" || run.taskId === pinnedId);
68
73
  const visibleIds = new Set(visible.map((run) => run.taskId));
69
74
  const byParent = new Map<string, LiveRun[]>();
70
75
  const roots: LiveRun[] = [];
@@ -80,8 +85,8 @@ export function buildRows(runs: LiveRun[], mainBusy: boolean, now: number, pinne
80
85
  const emit = (run: LiveRun, depth: number) => {
81
86
  rows.push({
82
87
  run,
83
- label: run.agentType,
84
- activity: run.activity,
88
+ label: rowLabel(run),
89
+ activity: run.label,
85
90
  status: run.status,
86
91
  startedAt: run.startedAt,
87
92
  finishedAt: run.finishedAt,
@@ -95,18 +100,14 @@ export function buildRows(runs: LiveRun[], mainBusy: boolean, now: number, pinne
95
100
  }
96
101
 
97
102
  /**
98
- * The agent taskId after `taskId` in strip order, wrapping to the first and
99
- * skipping the synthetic `main` row; undefined when there are no agent rows.
100
- * Tab-to-next-agent in read mode uses this against the FULL row list, anchored
101
- * to the viewed run — not the windowed strip selection, which `anchor()` can
102
- * reassign once the viewed run scrolls past MAX_STRIP_ROWS (else Tab/x would
103
- * act on the wrong agent).
103
+ * Whether the strip should say `/tasks to see subagents`: a run spawned by
104
+ * main left it within the last STRIP_LINGER_MS (it settled and is not the
105
+ * viewed one).
104
106
  */
105
- export function nextAgentTaskId(rows: PanelRow[], taskId: string | undefined): string | undefined {
106
- const ids = rows.filter((row) => row.run).map((row) => row.run!.taskId);
107
- if (ids.length === 0) return undefined;
108
- const at = taskId ? ids.indexOf(taskId) : -1;
109
- return ids[(at + 1) % ids.length];
107
+ export function recentlyLeftStrip(runs: LiveRun[], now: number, pinnedId?: string): boolean {
108
+ return runs.some(
109
+ (run) => run.depth === 0 && run.status !== "running" && run.taskId !== pinnedId && run.finishedAt !== undefined && run.finishedAt > now - STRIP_LINGER_MS,
110
+ );
110
111
  }
111
112
 
112
113
  const STATUS_ROW_STYLE: Partial<Record<LiveStatus, string>> = { failed: "error" };
@@ -128,42 +129,56 @@ export interface StripInput {
128
129
  rows: PanelRow[];
129
130
  /** Soft-focused row index; undefined when the strip is not focused. */
130
131
  selected?: number;
131
- /** A transcript view is open → the strip is in "read" mode (scroll hints). */
132
- viewOpen?: boolean;
132
+ /** The run whose transcript is open; undefined when the main transcript shows. */
133
+ viewedId?: string;
133
134
  width: number;
134
135
  now: number;
135
136
  }
136
137
 
137
138
  /**
138
- * One line per row: `mark agentType activity elapsed · ↓ tokens`. The
139
- * selected row is bold and marked `❯` (Claude Code's selection caret); others
140
- * are unmarked. A focus hint precedes the list (only while focused) — its keys
141
- * reflect the mode: navigating the strip vs. reading an open transcript, where
142
- * `←` returns to selection so a different agent can be picked. Overflow past
143
- * MAX_STRIP_ROWS collapses to "+N more".
139
+ * The focused strip's hint. Claude Code's part (findings §40): `↑/↓ to select`
140
+ * while an agent is viewed, `Enter to view` when the selected row is not the
141
+ * one on screen, `x to stop` on a running agent. One Code adds the keys
142
+ * Claude Code leaves unhinted, so every panel key is discoverable: `esc back`,
143
+ * and `ctrl+x ctrl+k stop all` while any agent runs.
144
+ */
145
+ export function stripHint(rows: PanelRow[], selected: number, viewedId: string | undefined): string {
146
+ const row = rows[selected];
147
+ const parts: string[] = [];
148
+ if (viewedId !== undefined || (row?.run?.taskId ?? "main") === "main") parts.push("↑/↓ to select");
149
+ if ((row?.run?.taskId ?? "main") !== (viewedId ?? "main")) parts.push("Enter to view");
150
+ if (row?.run?.status === "running") parts.push("x to stop");
151
+ if (rows.some((r) => r.run?.status === "running")) parts.push("ctrl+x ctrl+k stop all");
152
+ parts.push("esc back");
153
+ return parts.join(" · ");
154
+ }
155
+
156
+ /**
157
+ * One line per row, Claude Code's: `❯ ◯ label description elapsed · ↓
158
+ * tokens`. The caret marks the selected row, the filled dot the one on screen
159
+ * (`main` when no transcript is open), and a settled viewed run reads `idle`.
160
+ * A focus hint precedes the list while focused. Overflow past MAX_STRIP_ROWS
161
+ * collapses to "+N more".
144
162
  */
145
163
  export function renderStrip(input: StripInput, paint: Paint): string[] {
146
164
  const width = Math.max(20, input.width);
147
165
  const out: string[] = [];
148
- if (input.selected !== undefined) {
149
- const hint = input.viewOpen
150
- ? "↑/↓ scroll · PgUp/PgDn page · ← agents · ⇥ next · x stop · esc back"
151
- : "↑/↓ select · ⏎ view · x stop · ctrl+x ctrl+k stop all · esc back";
152
- out.push(paint.fg("dim", cut(hint, width)));
153
- }
166
+ if (input.selected !== undefined) out.push(paint.fg("dim", cut(stripHint(input.rows, input.selected, input.viewedId), width)));
167
+ const current = input.viewedId ?? "main";
154
168
  const shown = input.rows.slice(0, MAX_STRIP_ROWS);
155
169
  for (const [index, row] of shown.entries()) {
156
170
  const selected = input.selected === index;
157
- const mark = selected ? "❯" : " ";
158
- const stats = [
159
- formatDuration(row.startedAt, row.finishedAt, input.now),
160
- row.tokens ? `↓ ${formatTokenCount(row.tokens)} tokens` : "",
161
- ]
162
- .filter(Boolean)
163
- .join(" · ");
171
+ const caret = selected ? "❯" : " ";
172
+ const dot = (row.run?.taskId ?? "main") === current ? "⏺" : "◯";
173
+ const stats =
174
+ row.run && row.status !== "running"
175
+ ? "idle"
176
+ : [formatDuration(row.startedAt, row.finishedAt, input.now), row.tokens ? `↓ ${formatTokenCount(row.tokens)} tokens` : ""]
177
+ .filter(Boolean)
178
+ .join(" · ");
164
179
  const elbow = row.depth > 0 ? `${" ".repeat(row.depth - 1)}└ ` : "";
165
- const left = `${elbow}${mark} ${row.label}${row.activity ? ` ${row.activity}` : ""}`;
166
- const line = splitCell(left, stats, width);
180
+ const left = `${caret} ${elbow}${dot} ${row.label}${row.activity ? ` ${row.activity}` : ""}`;
181
+ const line = splitCell(left, row.run ? stats : "", width);
167
182
  if (selected) out.push(paint.fg("accent", paint.bold(line)));
168
183
  else if (STATUS_ROW_STYLE[row.status]) out.push(paint.fg(STATUS_ROW_STYLE[row.status]!, line));
169
184
  else out.push(row.run ? line : paint.fg("dim", line));
@@ -186,43 +201,6 @@ export function spinnerVerb(startedAt: number, now: number): string {
186
201
  return SPINNER_VERBS[Math.floor(seconds / 3) % SPINNER_VERBS.length];
187
202
  }
188
203
 
189
- /**
190
- * Word-aware wrap for prose (task text, assistant text): breaks at spaces,
191
- * hard-breaking only words longer than the width; preserves blank lines. Plain
192
- * text in, plain lines out — painting happens after, per line.
193
- */
194
- export function wrapProse(text: string, width: number): string[] {
195
- const columns = Math.max(1, width);
196
- const out: string[] = [];
197
- for (const raw of text.replace(/\r\n/g, "\n").split("\n")) {
198
- if (raw.trim() === "") {
199
- out.push("");
200
- continue;
201
- }
202
- let line = "";
203
- for (const word of raw.split(" ")) {
204
- if (visibleWidth(word) > columns) {
205
- // A word wider than the screen (path, hash): flush, then hard-break it.
206
- // The last chunk becomes the running line so later words can join it.
207
- if (line) out.push(line);
208
- const chunks = hardWrapColumns(word, columns);
209
- line = chunks.pop() ?? "";
210
- out.push(...chunks);
211
- continue;
212
- }
213
- const candidate = line ? `${line} ${word}` : word;
214
- if (visibleWidth(candidate) > columns) {
215
- out.push(line);
216
- line = word;
217
- } else {
218
- line = candidate;
219
- }
220
- }
221
- out.push(line);
222
- }
223
- return out;
224
- }
225
-
226
204
  /**
227
205
  * Renders one prose unit (assistant text) to painted lines at width. `ref`
228
206
  * identifies the unit across frames so implementations can cache: a settled
@@ -282,8 +260,9 @@ export function renderTranscript(input: TranscriptInput, paint: Paint): Transcri
282
260
  // windowed ANCHORED TO THE TAIL — scroll counts lines back from the end, so
283
261
  // the default view follows streaming like Claude Code.
284
262
  const bodyRows = Math.max(0, input.height - out.length - 1);
285
- const blockLines = run.blocks.flatMap((block) => blockToLines(block, width, paint, prose));
286
- const partial = streamingText(run.streaming).trimEnd();
263
+ const fork = run.agentType === FORK_AGENT;
264
+ const blockLines = run.blocks.flatMap((block) => blockToLines(block, width, paint, prose, fork));
265
+ const partial = sanitizeDisplayText(streamingText(run.streaming)).trimEnd();
287
266
  if (partial) for (const line of prose(`stream:${run.taskId}`, partial, width)) blockLines.push(line);
288
267
  while (blockLines.length && blockLines.at(-1) === "") blockLines.pop();
289
268
  const maxScroll = Math.max(0, blockLines.length - bodyRows);
@@ -297,7 +276,7 @@ export function renderTranscript(input: TranscriptInput, paint: Paint): Transcri
297
276
  // discoverable right where the reader is looking (key hints otherwise live in
298
277
  // the strip). splitPaint fuses to the left alone when the hint is "" (body
299
278
  // fits) or the row is too narrow.
300
- const scrollHint = maxScroll > 0 ? "↑/↓ scroll" : "";
279
+ const scrollHint = maxScroll > 0 ? "PgUp/PgDn scroll" : "";
301
280
  const bottomRow = (text: string, color: string): string => splitPaint(paint, color, text, scrollHint, width, undefined, "dim", false);
302
281
  if (run.status === "running") {
303
282
  const verb = spinnerVerb(run.startedAt, input.now);
@@ -321,14 +300,59 @@ export function renderTranscript(input: TranscriptInput, paint: Paint): Transcri
321
300
  * a trailing blank for paragraph spacing. Call/result stay one line each, a
322
301
  * blank after the result so tool groups read like the main transcript.
323
302
  */
324
- function blockToLines(block: TranscriptBlock, width: number, paint: Paint, prose: ProseRenderer): string[] {
303
+ function blockToLines(block: TranscriptBlock, width: number, paint: Paint, prose: ProseRenderer, fork: boolean): string[] {
304
+ // A fork's directive opens its view as Claude Code's `⑂ <question>` row.
305
+ if (block.kind === "task" && fork) return [...wrapProse(`⑂ ${block.text}`, width).map((l) => paint.fg("dim", l)), ""];
325
306
  if (block.kind === "task") return [...wrapProse(block.text, width).map((l) => paint.fg("dim", l)), ""];
307
+ // A message the user typed at the agent, Claude Code's `❯ <text>` row.
308
+ if (block.kind === "user") return [...wrapProse(`❯ ${block.text}`, width), ""];
326
309
  if (block.kind === "text") return [...prose(block, block.text, width), ""];
327
- if (block.kind === "call") return [`${paint.fg("accent", "●")} ${cut(`${block.tool}(${block.text})`, width - 2)}`];
310
+ if (block.kind === "call") return [`${paint.fg("accent", "●")} ${cut(`${toolDisplayLabel(block.tool ?? "")}(${block.text})`, width - 2)}`];
328
311
  const body = cut(block.text, width - 4);
329
312
  return [` ${paint.fg("dim", "⎿")} ${block.isError ? paint.fg("error", body) : body}`, ""];
330
313
  }
331
314
 
315
+ /**
316
+ * Labels the main transcript gives tools whose name does not title-case to it
317
+ * (each tool's `ccToolRenderers` label; `agent-view-tool-labels.test.ts` keeps
318
+ * this table in step with the registrations).
319
+ */
320
+ const TOOL_LABELS: Record<string, string> = {
321
+ edit: "Update",
322
+ ls: "LS",
323
+ powershell: "PowerShell",
324
+ ask_user_question: "Ask User",
325
+ task_create: "Create Task",
326
+ task_get: "Get Task",
327
+ task_list: "List Tasks",
328
+ task_update: "Update Task",
329
+ task_stop: "Stop Task",
330
+ lsp_diagnostics: "Diagnostics",
331
+ enter_plan_mode: "Enter plan mode",
332
+ exit_plan_mode: "Exit plan mode",
333
+ list_mcp_resources: "MCP Resources",
334
+ read_mcp_resource: "Read MCP Resource",
335
+ read_mcp_resource_dir: "List MCP Resource Directory",
336
+ };
337
+
338
+ /**
339
+ * The label the main transcript shows for a tool (`Bash(…)`, `Update(…)`,
340
+ * `Web Fetch(…)`, `server: tool(…)`), so a call reads the same in an agent's
341
+ * view: the irregular ones from a table, an MCP tool as `server: tool`, and
342
+ * the rest title-cased from the snake_case name, as the renderers label them.
343
+ */
344
+ export function toolDisplayLabel(name: string): string {
345
+ const fixed = TOOL_LABELS[name];
346
+ if (fixed) return fixed;
347
+ const mcp = /^mcp__(.+?)__(.+)$/.exec(name);
348
+ if (mcp) return `${mcp[1]}: ${mcp[2]}`;
349
+ return name
350
+ .split("_")
351
+ .filter(Boolean)
352
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
353
+ .join(" ");
354
+ }
355
+
332
356
  /** A small inverse-video chip for the label, degrading to `[label]` when narrow. */
333
357
  function chip(label: string): string {
334
358
  return label ? ` ${label} ` : "";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The persistent panel below the editor: Claude Code's agent tree PLUS its
3
- * background-shell manager (chip → Background list → shell details, rendered
4
- * by shell-panel.ts). Mirrors workflow/widget.ts: a component-overload
3
+ * background-shells chip (shell-panel.ts; Enter on it opens the `/tasks`
4
+ * Background dialog). Mirrors workflow/widget.ts: a component-overload
5
5
  * setWidget so rows can right-align against the real width, debounced
6
6
  * re-renders, and a 1s ticker to keep elapsed time moving. Also carries both
7
7
  * soft-focus states for the down-arrow flow (wired in index.ts): the FIRST ↓
@@ -11,17 +11,12 @@
11
11
 
12
12
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
13
13
  import type { BackgroundTask } from "../background/registry.ts";
14
+ import { canShowCustomUi } from "../lib/headless-output.ts";
14
15
  import type { ShellTaskTracker } from "../lib/shell-tasks.ts";
15
16
  import { linesComponent, liveUiCtx, safeThemeBold, safeThemeInverse, safeThemePaint } from "../lib/tui-render.ts";
16
- import { buildRows, MAX_STRIP_ROWS, nextAgentTaskId, renderStrip, type PanelRow } from "./panel-render.ts";
17
+ import { buildRows, MAX_STRIP_ROWS, recentlyLeftStrip, renderStrip, TASKS_NOTICE, type PanelRow } from "./panel-render.ts";
17
18
  import type { LiveRunRegistry } from "./live-runs.ts";
18
- import {
19
- anchorShellFocus,
20
- renderShellSection,
21
- type ShellFocus,
22
- shellRows,
23
- shellSectionVisible,
24
- } from "./shell-panel.ts";
19
+ import { renderShellChip, shellRows } from "./shell-panel.ts";
25
20
 
26
21
  const WIDGET_KEY = "subagents";
27
22
  const DEBOUNCE_MS = 200;
@@ -41,8 +36,8 @@ export class SubagentWidget {
41
36
  * actually selected, never on whatever slid into its slot.
42
37
  */
43
38
  private focusId: string | undefined;
44
- /** The shell-manager side of the panel (chip → list → details). */
45
- shellFocus: ShellFocus | undefined;
39
+ /** Whether the shells chip holds soft focus. */
40
+ shellChipFocused = false;
46
41
  /**
47
42
  * The taskId whose transcript is open, or undefined when none is (set by
48
43
  * index.ts). Drives the strip's scroll hints AND pins that run in the strip
@@ -61,11 +56,15 @@ export class SubagentWidget {
61
56
  */
62
57
  private disposed = false;
63
58
  private readonly unsubscribes: Array<() => void> = [];
59
+ /** The text last sent as an RPC widget; undefined while none is shown. */
60
+ private rpcSent: string | undefined;
64
61
 
65
62
  constructor(
66
63
  private readonly registry: LiveRunRegistry,
67
64
  private readonly getCtx: () => ExtensionContext | undefined,
68
65
  private readonly shells?: ShellTaskTracker,
66
+ /** Told the rows this strip paints below the editor (lib/dock-rows.ts). */
67
+ private readonly onRows: (rows: number) => void = () => {},
69
68
  ) {
70
69
  this.unsubscribes.push(registry.subscribe(() => this.schedule()));
71
70
  if (shells) this.unsubscribes.push(shells.subscribe(() => this.schedule()));
@@ -84,7 +83,7 @@ export class SubagentWidget {
84
83
  } catch {
85
84
  mainBusy = false;
86
85
  }
87
- return buildRows(this.registry.list(), mainBusy, Date.now(), this.viewedId);
86
+ return buildRows(this.registry.list(), mainBusy, this.viewedId);
88
87
  }
89
88
 
90
89
  rowCount(): number {
@@ -130,64 +129,31 @@ export class SubagentWidget {
130
129
  return this.viewedId;
131
130
  }
132
131
 
133
- /** The agent to retarget to on Tab, relative to the viewed run in the FULL
134
- * row list (see nextAgentTaskId — avoids the windowed-selection desync). */
135
- nextAgentAfter(taskId: string | undefined): string | undefined {
136
- return nextAgentTaskId(this.rows(), taskId);
137
- }
138
-
139
- // --- Shell-manager state (Claude Code's ↓-to-manage flow) ---
132
+ // --- The shells chip (Claude Code's ↓-to-manage flow) ---
140
133
 
141
- /** Live shell rows for the list/details views. */
142
- shellTasks(): BackgroundTask[] {
134
+ /** The shells the chip counts: running, or finished inside the linger window. */
135
+ private shellTasks(): BackgroundTask[] {
143
136
  return this.shells ? shellRows(this.shells.list(), Date.now()) : [];
144
137
  }
145
138
 
146
- shellIds(): string[] {
147
- return this.shellTasks().map((t) => t.id);
148
- }
149
-
150
139
  private runningShellCount(): number {
151
140
  return this.shells?.running().length ?? 0;
152
141
  }
153
142
 
154
- /** Whether ↓ from the editor should land on the chip first — running shells
155
- * or finished ones still inside the linger window. */
143
+ /** Whether the chip shows, so ↓ from the editor lands on it first: running
144
+ * shells, or finished ones still inside the linger window. */
156
145
  shellChipAvailable(): boolean {
157
146
  return this.shellTasks().length > 0;
158
147
  }
159
148
 
160
- /** `ids`, when the caller already derived the row order, skips re-deriving it. */
161
- setShellFocus(focus: ShellFocus | undefined, ids?: string[]): void {
162
- this.shellFocus = focus && anchorShellFocus(focus, ids ?? this.shellIds());
149
+ setShellChipFocus(focused: boolean): void {
150
+ this.shellChipFocused = focused;
163
151
  this.render();
164
152
  }
165
153
 
166
- /** The anchored current focus (the selected shell may have lingered out). */
167
- anchoredShellFocus(ids?: string[]): ShellFocus | undefined {
168
- if (this.shellFocus) this.shellFocus = anchorShellFocus(this.shellFocus, ids ?? this.shellIds());
169
- return this.shellFocus;
170
- }
171
-
172
- /** Point lookup — no need for the linger-filtered, sorted row build. */
173
- shellTaskById(id: string): BackgroundTask | undefined {
174
- return this.shells?.list().find((t) => t.id === id);
175
- }
176
-
177
- selectedShellTask(): BackgroundTask | undefined {
178
- const focus = this.anchoredShellFocus();
179
- if (!focus || focus.stage === "chip") return undefined;
180
- return this.shellTaskById(focus.selectedId);
181
- }
182
-
183
- private shellVisible(): boolean {
184
- return shellSectionVisible(this.shellTasks().length, this.shellFocus);
185
- }
186
-
187
- /** Lines the shell section currently occupies (for overlay reserve math) —
188
- * measured off the real renderer so layout changes can never desync it. */
154
+ /** Lines the chip occupies (for overlay reserve math). */
189
155
  shellLineCount(): number {
190
- return this.shellVisible() ? this.shellSectionLines(80).length : 0;
156
+ return this.shellChipAvailable() ? 1 : 0;
191
157
  }
192
158
 
193
159
  /** Re-derive focusIndex from focusId against the given rows (see focusId doc). */
@@ -224,6 +190,7 @@ export class SubagentWidget {
224
190
  if (this.ticker) clearInterval(this.ticker);
225
191
  this.timer = this.ticker = undefined;
226
192
  for (const unsubscribe of this.unsubscribes.splice(0)) unsubscribe();
193
+ this.onRows(0);
227
194
  try {
228
195
  ctx?.ui.setWidget(WIDGET_KEY, undefined);
229
196
  } catch {
@@ -240,23 +207,6 @@ export class SubagentWidget {
240
207
  this.timer.unref?.();
241
208
  }
242
209
 
243
- /** The shell section's lines; identity paint when only counting. */
244
- private shellSectionLines(
245
- width: number,
246
- paint = { fg: (_c: string, t: string) => t, bold: (t: string) => t, inverse: (t: string) => t },
247
- ): string[] {
248
- return renderShellSection(
249
- {
250
- rows: this.shellTasks(),
251
- runningCount: this.runningShellCount(),
252
- focus: this.shellFocus,
253
- width,
254
- now: Date.now(),
255
- },
256
- paint,
257
- );
258
- }
259
-
260
210
  private render(): void {
261
211
  const ctx = this.liveCtx();
262
212
  if (!ctx) {
@@ -265,30 +215,50 @@ export class SubagentWidget {
265
215
  this.syncTicker(false);
266
216
  return;
267
217
  }
218
+ const now = Date.now();
268
219
  const rows = this.rows();
269
- const shellVisible = this.shellVisible();
220
+ const shellTasks = this.shellTasks();
221
+ const shellVisible = shellTasks.length > 0;
222
+ // The chip goes with its last shell: a focus left on it would read "0 shells".
223
+ if (!shellVisible) this.shellChipFocused = false;
224
+ const chipFocused = this.shellChipFocused;
270
225
  // The chip is time-invariant while every shell runs (count changes arrive
271
- // as tracker events), so the clock is needed only for the list (linger
272
- // expiry) and details (runtime, live output) stages — or when finished
273
- // shells are lingering, whose expiry is what hides the section.
274
- const shellStage = this.shellFocus?.stage;
275
- const lingering = shellVisible && this.shellTasks().length > this.runningShellCount();
276
- this.syncTicker(rows.length > 1 || shellStage === "list" || shellStage === "details" || lingering);
226
+ // as tracker events), so the clock is needed only while finished shells
227
+ // linger, whose expiry is what clears the chip.
228
+ const lingering = shellTasks.length > this.runningShellCount();
229
+ // A run that just left the strip leaves Claude Code's `/tasks` pointer behind for a moment.
230
+ const notice = rows.length <= 1 && recentlyLeftStrip(this.registry.list(), now, this.viewedId);
231
+ this.syncTicker(rows.length > 1 || notice || lingering);
277
232
  // Only the synthetic `main` row exists and no shells → nothing to show yet.
278
233
  // Never tear down while a transcript view is open (viewedId set): clearing
279
234
  // focus here would orphan the overlay with no key path to close it.
280
- if (rows.length <= 1 && !shellVisible && this.viewedId === undefined) {
235
+ if (rows.length <= 1 && !shellVisible && !notice && this.viewedId === undefined) {
236
+ this.rpcSent = undefined;
281
237
  ctx.ui.setWidget(WIDGET_KEY, undefined);
238
+ this.onRows(0);
282
239
  this.focusIndex = undefined;
283
240
  this.focusId = undefined;
284
- this.shellFocus = undefined;
285
241
  return;
286
242
  }
287
243
  this.anchor(rows);
288
- if (this.shellFocus) this.shellFocus = anchorShellFocus(this.shellFocus, this.shellIds());
289
244
  const selected = this.focusIndex;
290
245
  const showStrip = rows.length > 1;
291
- const now = Date.now();
246
+ // RPC forwards string-array widgets, not component factories or key handlers.
247
+ if (!canShowCustomUi(ctx)) {
248
+ const lines = rows.slice(1, MAX_STRIP_ROWS).map((row) =>
249
+ `${row.run!.name} (${row.run!.taskId}) — ${row.status}: ${row.activity}`,
250
+ );
251
+ if (rows.length > MAX_STRIP_ROWS) lines.push(`${rows.length - MAX_STRIP_ROWS} more agents — /tasks to list all`);
252
+ if (notice) lines.push(TASKS_NOTICE);
253
+ if (shellVisible) lines.push(...shellTasks.map((task) => `${task.id} [shell] ${task.status}: ${task.command ?? task.description}`));
254
+ // The ticker repaints every second; each send is a message to the client.
255
+ const text = lines.join("\n");
256
+ if (text === this.rpcSent) return;
257
+ this.rpcSent = text;
258
+ ctx.ui.setWidget(WIDGET_KEY, lines, { placement: "belowEditor" });
259
+ this.onRows(0); // no TUI view sizes itself in RPC
260
+ return;
261
+ }
292
262
  ctx.ui.setWidget(
293
263
  WIDGET_KEY,
294
264
  (tui, theme) => {
@@ -300,11 +270,13 @@ export class SubagentWidget {
300
270
  const paint = { fg: safeThemePaint(theme), bold: safeThemeBold(theme), inverse: safeThemeInverse(theme) };
301
271
  return linesComponent((width) => {
302
272
  const lines: string[] = [];
303
- if (showStrip) lines.push(...renderStrip({ rows, selected, viewOpen: this.viewedId !== undefined, width: width - 1, now }, paint));
273
+ if (showStrip) lines.push(...renderStrip({ rows, selected, viewedId: this.viewedId, width: width - 1, now }, paint));
274
+ else if (notice) lines.push(paint.fg("dim", TASKS_NOTICE));
304
275
  if (shellVisible) {
305
276
  if (lines.length) lines.push("");
306
- lines.push(...this.shellSectionLines(width - 1, paint));
277
+ lines.push(renderShellChip(shellTasks.length, chipFocused, width - 1, paint));
307
278
  }
279
+ this.onRows(lines.length);
308
280
  return lines.map((line) => ` ${line}`);
309
281
  });
310
282
  },
@@ -313,10 +285,9 @@ export class SubagentWidget {
313
285
  }
314
286
 
315
287
  /**
316
- * Tick while any child row is shown or the shell manager is focused —
317
- * elapsed times move, finished rows expire out of the strip/list (linger
318
- * windows), and the details view's output box follows the live spool,
319
- * all without needing another registry event.
288
+ * Tick while any child row is shown or a finished shell lingers: elapsed
289
+ * times move and finished rows expire out of the strip and the chip (linger
290
+ * windows), without needing another registry event.
320
291
  */
321
292
  private syncTicker(active: boolean): void {
322
293
  if (active && !this.ticker && !this.disposed) {