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.
- package/README.md +16 -5
- package/agents/general-purpose.md +2 -2
- package/docs/guide/README.md +2 -1
- package/docs/guide/bring-your-claude-code-setup.md +60 -9
- package/docs/guide/configuration.md +48 -14
- package/docs/guide/differences-from-claude-code.md +32 -23
- package/docs/guide/doctor.md +20 -13
- package/docs/guide/hooks.md +18 -3
- package/docs/guide/installation.md +27 -4
- package/docs/guide/permissions-modes-and-auto-mode.md +225 -49
- package/docs/guide/providers-and-models.md +137 -54
- package/docs/guide/reference.md +27 -15
- package/docs/guide/sessions-and-context.md +69 -16
- package/docs/guide/skills-plugins-and-mcp.md +57 -20
- package/docs/guide/subagents-and-workflows.md +95 -29
- package/docs/guide/tasks-and-background-work.md +101 -26
- package/docs/guide/terminal-interface.md +24 -12
- package/docs/guide/tools.md +46 -5
- package/docs/guide/troubleshooting.md +13 -9
- package/docs/guide/windows.md +58 -31
- package/extensions/artifacts/gallery.ts +227 -0
- package/extensions/artifacts/index.ts +288 -0
- package/extensions/artifacts/store.ts +570 -0
- package/extensions/artifacts/viewer.ts +199 -0
- package/extensions/ask-user/description.ts +67 -0
- package/extensions/ask-user/dialogs.ts +80 -0
- package/extensions/ask-user/index.ts +37 -33
- package/extensions/ask-user/questions.ts +32 -7
- package/extensions/ask-user/widget.ts +45 -25
- package/extensions/auto-mode/actions.ts +6 -0
- package/extensions/auto-mode/cache.ts +88 -0
- package/extensions/auto-mode/classifier-prompt.ts +5 -4
- package/extensions/auto-mode/classifier.ts +290 -65
- package/extensions/auto-mode/config.ts +72 -20
- package/extensions/auto-mode/decision-log.ts +2 -0
- package/extensions/auto-mode/defaults.ts +1 -1
- package/extensions/auto-mode/denials.ts +123 -0
- package/extensions/auto-mode/git-checkout-programs.ts +187 -0
- package/extensions/auto-mode/git-status-meta.ts +159 -0
- package/extensions/auto-mode/history.ts +189 -0
- package/extensions/auto-mode/instructions.ts +58 -52
- package/extensions/auto-mode/model-select.ts +159 -134
- package/extensions/auto-mode/paths.ts +57 -5
- package/extensions/auto-mode/pause.ts +57 -2
- package/extensions/auto-mode/prompt.ts +37 -21
- package/extensions/auto-mode/read-only-options.ts +724 -0
- package/extensions/auto-mode/resolved-paths-meta.ts +133 -0
- package/extensions/auto-mode/rules.ts +18 -1
- package/extensions/auto-mode/safety-floor.ts +361 -18
- package/extensions/auto-mode/setup-run.ts +20 -14
- package/extensions/auto-mode/shell-analysis.ts +764 -490
- package/extensions/auto-mode/shell-parse.ts +885 -0
- package/extensions/auto-mode/system-note.ts +33 -0
- package/extensions/auto-mode/transcript.ts +80 -51
- package/extensions/background/cron.ts +515 -0
- package/extensions/background/index.ts +514 -236
- package/extensions/background/loop-fire.ts +136 -0
- package/extensions/background/loop-preamble.ts +59 -0
- package/extensions/background/loop-skill.ts +163 -0
- package/extensions/background/monitor-batch.ts +53 -0
- package/extensions/background/noop-fold.ts +128 -0
- package/extensions/background/switch-guard.ts +52 -0
- package/extensions/background/wakeup.ts +159 -55
- package/extensions/bash/background.ts +53 -17
- package/extensions/bash/child.ts +7 -0
- package/extensions/bash/description.ts +188 -0
- package/extensions/bash/guards.ts +15 -67
- package/extensions/bash/index.ts +88 -35
- package/extensions/branding/compaction-keep.ts +63 -0
- package/extensions/branding/declined.ts +22 -0
- package/extensions/branding/index.ts +133 -9
- package/extensions/branding/prompt-editor.ts +53 -6
- package/extensions/branding/queued-edit.ts +31 -0
- package/extensions/branding/replaced-builtins.ts +68 -0
- package/extensions/branding/startup.ts +33 -71
- package/extensions/btw/index.ts +229 -71
- package/extensions/btw/panel.ts +228 -100
- package/extensions/btw/prompt.ts +41 -4
- package/extensions/claude-compat/index.ts +7 -11
- package/extensions/claude-context/index.ts +446 -58
- package/extensions/compaction/cut.ts +157 -0
- package/extensions/compaction/index.ts +216 -75
- package/extensions/compaction/kept-thinking.ts +62 -0
- package/extensions/compaction/prompt.ts +15 -12
- package/extensions/context-budget/index.ts +24 -5
- package/extensions/context-management/index.ts +25 -0
- package/extensions/doctor/build.ts +59 -8
- package/extensions/doctor/builtins.ts +84 -0
- package/extensions/doctor/cli.ts +7 -1
- package/extensions/doctor/compat.ts +88 -23
- package/extensions/doctor/dependencies.ts +41 -5
- package/extensions/doctor/fix-prompt.ts +9 -2
- package/extensions/doctor/index.ts +102 -58
- package/extensions/doctor/models.ts +102 -63
- package/extensions/doctor/presets.ts +42 -27
- package/extensions/doctor/report.ts +19 -2
- package/extensions/doctor/viewer.ts +49 -27
- package/extensions/effort/index.ts +72 -19
- package/extensions/effort/settings.ts +21 -0
- package/extensions/effort/slider.ts +24 -20
- package/extensions/exit/index.ts +32 -1
- package/extensions/file-tracker/index.ts +337 -26
- package/extensions/file-tracker/replay.ts +96 -10
- package/extensions/file-tracker/restore.ts +63 -0
- package/extensions/file-tracker/results.ts +66 -0
- package/extensions/file-tracker/shell-reads.ts +211 -0
- package/extensions/file-tracker/tracker.ts +32 -15
- package/extensions/footer/footer-line.ts +45 -24
- package/extensions/footer/index.ts +47 -2
- package/extensions/footer/pr.ts +34 -5
- package/extensions/hooks/compaction.ts +7 -0
- package/extensions/hooks/executor.ts +32 -30
- package/extensions/hooks/index.ts +198 -31
- package/extensions/hooks/matcher.ts +3 -0
- package/extensions/hooks/protocol.ts +41 -3
- package/extensions/hooks/settings.ts +50 -19
- package/extensions/hooks/subagent-bridge.ts +2 -0
- package/extensions/hooks/trust.ts +13 -5
- package/extensions/idle-compact/index.ts +119 -0
- package/extensions/idle-compact/policy.ts +143 -0
- package/extensions/init/index.ts +11 -10
- package/extensions/init/prompt.ts +25 -32
- package/extensions/interrupted/index.ts +13 -1
- package/extensions/kept-thinking/index.ts +27 -0
- package/extensions/lib/agent-cron.ts +130 -0
- package/extensions/lib/agent-loader.ts +15 -1
- package/extensions/lib/agent-view.ts +85 -0
- package/extensions/lib/anthropic-payload.ts +82 -1
- package/extensions/lib/anthropic-server-call.ts +230 -0
- package/extensions/lib/anthropic-server-tools.ts +485 -0
- package/extensions/lib/app-launch.mjs +94 -0
- package/extensions/lib/argument-hints.ts +190 -0
- package/extensions/lib/atomic-write.ts +6 -2
- package/extensions/lib/auto-memory.ts +75 -0
- package/extensions/lib/bash-parser.ts +84 -0
- package/extensions/lib/btw-fork.ts +93 -0
- package/extensions/lib/child-extensions.ts +42 -0
- package/extensions/lib/child-writes.ts +45 -0
- package/extensions/lib/claude-context.ts +515 -100
- package/extensions/lib/claude-external-includes.ts +68 -0
- package/extensions/lib/claude-id.ts +22 -0
- package/extensions/lib/claude-rules.ts +323 -0
- package/extensions/lib/claude-settings.ts +76 -4
- package/extensions/lib/compaction-boundary.ts +36 -0
- package/extensions/lib/compaction-keep.mjs +83 -0
- package/extensions/lib/config-mode.ts +112 -0
- package/extensions/lib/consent-dialogs.ts +63 -0
- package/extensions/lib/consent-preview.ts +5 -1
- package/extensions/lib/consent-stores.ts +48 -0
- package/extensions/lib/context-facts.ts +78 -0
- package/extensions/lib/context-stack.ts +81 -0
- package/extensions/lib/deferred.ts +219 -25
- package/extensions/lib/dock-rows.ts +60 -0
- package/extensions/lib/empty-tool-result.ts +27 -0
- package/extensions/lib/environment-block.ts +76 -0
- package/extensions/lib/fetch-timeout.ts +24 -6
- package/extensions/{system-prompt → lib}/git-status.ts +51 -13
- package/extensions/lib/git.ts +75 -13
- package/extensions/lib/grammars/LICENSE-tree-sitter-bash +21 -0
- package/extensions/lib/grammars/README.md +2 -0
- package/extensions/lib/grammars/tree-sitter-bash.wasm +0 -0
- package/extensions/lib/guide-docs.ts +45 -0
- package/extensions/lib/headless-output.ts +46 -0
- package/extensions/lib/hook-gate.ts +7 -7
- package/extensions/lib/interrupt.ts +20 -4
- package/extensions/lib/key-input.ts +455 -0
- package/extensions/lib/keys.ts +14 -1
- package/extensions/lib/local-command.ts +47 -13
- package/extensions/lib/mcp-overrides.ts +1 -1
- package/extensions/lib/mcp-share.ts +46 -0
- package/extensions/lib/memory-content.ts +146 -0
- package/extensions/lib/memory-index.ts +18 -0
- package/extensions/lib/memory.ts +134 -123
- package/extensions/lib/model-catalog/huggingface.json +174 -0
- package/extensions/lib/model-catalog/models-dev.json +18297 -0
- package/extensions/lib/model-catalog/openrouter.json +5252 -0
- package/extensions/lib/model-catalog-data.ts +513 -0
- package/extensions/lib/model-catalog.ts +462 -0
- package/extensions/lib/model-choice-warnings.ts +137 -0
- package/extensions/lib/model-pairings.ts +152 -0
- package/extensions/{auto-mode → lib}/model-picker.ts +26 -43
- package/extensions/lib/model-policy.ts +112 -15
- package/extensions/lib/model-tier.ts +283 -355
- package/extensions/lib/model-unusable.ts +1 -1
- package/extensions/lib/newer-model.ts +100 -0
- package/extensions/lib/notices.ts +87 -0
- package/extensions/lib/notifications.ts +308 -64
- package/extensions/lib/one-code-settings.ts +74 -8
- package/extensions/lib/open-browser.ts +63 -0
- package/extensions/lib/openrouter-generation.ts +94 -0
- package/extensions/lib/operation-deadline.ts +30 -0
- package/extensions/lib/original-command.ts +4 -4
- package/extensions/lib/package-version.ts +1 -1
- package/extensions/lib/panel-focus.ts +134 -0
- package/extensions/lib/paths.ts +17 -2
- package/extensions/lib/permission-gate.ts +72 -6
- package/extensions/lib/persisted-output.ts +60 -8
- package/extensions/lib/pi-ai-estimate.ts +19 -3
- package/extensions/lib/pi-version.ts +21 -2
- package/extensions/lib/plan-mode-channels.ts +20 -5
- package/extensions/lib/plugins.ts +26 -17
- package/extensions/lib/powershell-parse-script.ts +208 -0
- package/extensions/lib/powershell-parser.ts +344 -0
- package/extensions/lib/process-tree.ts +131 -8
- package/extensions/lib/prompt-options.ts +82 -0
- package/extensions/lib/queued-delivery.ts +44 -0
- package/extensions/lib/reminders.ts +364 -58
- package/extensions/lib/replaced-builtins.mjs +199 -0
- package/extensions/lib/replay-call.ts +70 -0
- package/extensions/lib/request-replay.ts +439 -0
- package/extensions/lib/scratchpad.ts +81 -14
- package/extensions/lib/send-now-channel.ts +7 -0
- package/extensions/lib/session-model-tier.ts +51 -0
- package/extensions/lib/session-work.ts +64 -0
- package/extensions/lib/settings-channels.ts +4 -4
- package/extensions/lib/shell-exit-semantics.ts +170 -0
- package/extensions/lib/shell-quote.ts +11 -0
- package/extensions/lib/shell-result.ts +70 -0
- package/extensions/lib/shell-spawn.ts +70 -6
- package/extensions/lib/shell-tool.ts +75 -25
- package/extensions/lib/side-call-cache.ts +124 -0
- package/extensions/lib/side-call-run.ts +96 -0
- package/extensions/lib/side-call-usage.ts +38 -0
- package/extensions/lib/side-call.ts +13 -1
- package/extensions/lib/single-flight.ts +18 -0
- package/extensions/lib/skill-body.ts +42 -0
- package/extensions/lib/skill-scan.ts +63 -24
- package/extensions/lib/spill-file.ts +140 -0
- package/extensions/lib/subagent-handback.ts +113 -0
- package/extensions/lib/system-role.ts +156 -0
- package/extensions/lib/terminal-text.ts +114 -0
- package/extensions/lib/text-width.ts +47 -16
- package/extensions/lib/timer-ops.ts +20 -0
- package/extensions/lib/tool-additions.ts +149 -0
- package/extensions/lib/tool-call-corruption.ts +134 -0
- package/extensions/lib/tool-path.ts +66 -0
- package/extensions/lib/tool-variants.ts +69 -0
- package/extensions/lib/tui-render.ts +255 -27
- package/extensions/lib/turn-budget-layout.ts +143 -0
- package/extensions/lib/update-check.mjs +15 -2
- package/extensions/lib/usage-bus.ts +1 -1
- package/extensions/lib/user-denial.ts +38 -0
- package/extensions/lib/workspace-channel.ts +16 -0
- package/extensions/lib/worktree-channel.ts +31 -3
- package/extensions/lib/worktree-isolation.ts +40 -10
- package/extensions/lsp/client.ts +8 -9
- package/extensions/lsp/index.ts +70 -4
- package/extensions/lsp/keep-alive.ts +1 -1
- package/extensions/lsp/trust.ts +119 -0
- package/extensions/lsp/watcher.ts +1 -1
- package/extensions/mcp/announce.ts +87 -5
- package/extensions/mcp/client.ts +161 -48
- package/extensions/mcp/config.ts +132 -8
- package/extensions/mcp/discovery.ts +19 -0
- package/extensions/mcp/index.ts +209 -71
- package/extensions/mcp/oauth/browser.ts +38 -17
- package/extensions/mcp/oauth/flow.ts +25 -17
- package/extensions/mcp/oauth/provider.ts +14 -8
- package/extensions/mcp/oauth/store.ts +34 -16
- package/extensions/mcp/panel/keys.ts +12 -11
- package/extensions/mcp/panel/render.ts +9 -6
- package/extensions/mcp/schema.ts +104 -5
- package/extensions/mcp/trust.ts +151 -67
- package/extensions/memory/entries.ts +23 -14
- package/extensions/memory/index.ts +134 -28
- package/extensions/memory/open-external.ts +33 -18
- package/extensions/memory/panel.ts +135 -26
- package/extensions/newer-model/index.ts +31 -0
- package/extensions/notebook/description.ts +18 -0
- package/extensions/notebook/index.ts +6 -5
- package/extensions/notebook/notebook.ts +1 -1
- package/extensions/permissions/index.ts +1300 -381
- package/extensions/permissions/matcher.ts +425 -74
- package/extensions/permissions/modes.ts +6 -4
- package/extensions/permissions/panel/host.ts +130 -0
- package/extensions/permissions/panel/keys.ts +48 -0
- package/extensions/permissions/panel/render.ts +450 -0
- package/extensions/permissions/panel/state.ts +550 -0
- package/extensions/permissions/powershell-cmdlets.ts +198 -0
- package/extensions/permissions/powershell-paths.ts +197 -0
- package/extensions/permissions/powershell-rules.ts +207 -280
- package/extensions/permissions/powershell-tree.ts +557 -0
- package/extensions/permissions/project-trust.ts +27 -11
- package/extensions/permissions/protected-paths.ts +14 -2
- package/extensions/permissions/session-grant.ts +6 -0
- package/extensions/permissions/settings.ts +166 -31
- package/extensions/permissions/subagent-gate.ts +25 -0
- package/extensions/permissions/workspace.ts +78 -0
- package/extensions/plan-mode/index.ts +156 -69
- package/extensions/plan-mode/reminder.ts +89 -21
- package/extensions/plan-mode/texts.ts +177 -0
- package/extensions/plan-mode/viewer.ts +92 -27
- package/extensions/plugins/counts.ts +3 -3
- package/extensions/plugins/index.ts +61 -46
- package/extensions/plugins/install/install.ts +16 -0
- package/extensions/plugins/marketplace/sync.ts +9 -3
- package/extensions/plugins/marketplace/types.ts +3 -1
- package/extensions/plugins/panel/keys.ts +21 -24
- package/extensions/plugins/panel/render.ts +12 -6
- package/extensions/plugins/shell-expand.ts +107 -0
- package/extensions/plugins/template.ts +32 -15
- package/extensions/powershell/child.ts +7 -0
- package/extensions/powershell/description.ts +38 -6
- package/extensions/powershell/guards.ts +1 -1
- package/extensions/powershell/index.ts +12 -7
- package/extensions/recap/index.ts +65 -60
- package/extensions/recap/prompt.ts +2 -2
- package/extensions/recap/scheduler.ts +3 -5
- package/extensions/search-tools/grep-merge.ts +39 -0
- package/extensions/search-tools/index.ts +0 -0
- package/extensions/send-now/chord.ts +62 -0
- package/extensions/send-now/index.ts +133 -0
- package/extensions/session-title/index.ts +3 -0
- package/extensions/session-title/title.ts +4 -1
- package/extensions/skill/code-review.ts +97 -0
- package/extensions/skill/index.ts +421 -101
- package/extensions/skill/invoke.ts +128 -18
- package/extensions/skill/listing.ts +133 -9
- package/extensions/skill/panel/keys.ts +15 -16
- package/extensions/skill/panel/render.ts +5 -2
- package/extensions/skill/panel/state.ts +12 -1
- package/extensions/subagents/agent-args.ts +39 -0
- package/extensions/subagents/agent-description.ts +132 -0
- package/extensions/subagents/agents.ts +116 -23
- package/extensions/subagents/announce.ts +69 -0
- package/extensions/subagents/default-model.ts +2 -3
- package/extensions/subagents/delegation-steer.ts +4 -4
- package/extensions/subagents/fork-cache.ts +76 -0
- package/extensions/subagents/guide-agent.ts +189 -0
- package/extensions/subagents/hand-back-review.ts +3 -0
- package/extensions/subagents/index.ts +1493 -824
- package/extensions/subagents/live-runs.ts +26 -6
- package/extensions/subagents/model-select.ts +38 -25
- package/extensions/subagents/outcome.ts +2 -2
- package/extensions/subagents/panel-keys.ts +73 -31
- package/extensions/subagents/panel-render.ts +120 -96
- package/extensions/subagents/panel-widget.ts +60 -89
- package/extensions/subagents/runner.ts +108 -80
- package/extensions/subagents/runs.ts +39 -5
- package/extensions/subagents/send-to-main-tool.ts +1 -1
- package/extensions/subagents/session-turns.ts +32 -8
- package/extensions/subagents/shell-panel.ts +36 -178
- package/extensions/subagents/tasks-dialog.ts +170 -0
- package/extensions/subagents/viewport-mirror.ts +95 -0
- package/extensions/subagents/worktree.ts +57 -13
- package/extensions/system-prompt/environment.ts +19 -19
- package/extensions/system-prompt/idle-turn.ts +65 -0
- package/extensions/system-prompt/index.ts +131 -47
- package/extensions/system-prompt/template.ts +24 -50
- package/extensions/system-prompt/tiers/common.ts +23 -64
- package/extensions/system-prompt/tiers/frontier.ts +32 -11
- package/extensions/system-prompt/tiers/low.ts +67 -45
- package/extensions/system-prompt/tiers/mid.ts +96 -38
- package/extensions/system-reminder/index.ts +175 -13
- package/extensions/tasks/descriptions.ts +190 -0
- package/extensions/tasks/index.ts +120 -41
- package/extensions/tasks/store.ts +83 -16
- package/extensions/tool-call-corruption/child.ts +7 -0
- package/extensions/tool-call-corruption/index.ts +237 -0
- package/extensions/tool-search/announce.ts +16 -0
- package/extensions/tool-search/index.ts +189 -41
- package/extensions/tool-style/index.ts +68 -10
- package/extensions/tool-style/write-description.ts +40 -0
- package/extensions/tool-style/write-render.ts +237 -0
- package/extensions/turn-duration/index.ts +7 -3
- package/extensions/turn-duration/line.ts +47 -4
- package/extensions/web/backends.ts +6 -9
- package/extensions/web/description.ts +50 -0
- package/extensions/web/index.ts +118 -53
- package/extensions/web/native-call.ts +15 -0
- package/extensions/web/native-results.ts +41 -0
- package/extensions/web-fetch/description.ts +42 -0
- package/extensions/web-fetch/extract.ts +53 -6
- package/extensions/web-fetch/index.ts +125 -76
- package/extensions/web-fetch/page-content.ts +19 -0
- package/extensions/web-fetch/response.ts +49 -0
- package/extensions/web-fetch/summarize.ts +9 -10
- package/extensions/workflow/agent-session.ts +105 -18
- package/extensions/workflow/args.ts +24 -0
- package/extensions/workflow/description.ts +67 -0
- package/extensions/workflow/globals.ts +17 -3
- package/extensions/workflow/index.ts +145 -162
- package/extensions/workflow/records.ts +1 -0
- package/extensions/workflow/run-manager.ts +29 -6
- package/extensions/workflow/saved-workflows.ts +3 -2
- package/extensions/workflow/types.ts +22 -1
- package/extensions/workflow/viewer.ts +54 -24
- package/extensions/workflow/widget.ts +20 -0
- package/extensions/worktree/descriptions.ts +91 -0
- package/extensions/worktree/guards.ts +316 -54
- package/extensions/worktree/index.ts +121 -39
- package/extensions/worktree/powershell-guards.ts +167 -0
- package/extensions/worktree/rewrite.ts +14 -16
- package/package.json +18 -7
- package/skills/artifact-design/SKILL.md +85 -0
- package/skills/code-review/SKILL.md +20 -20
- package/skills/code-review/cells/arguments-and-flags.md +19 -0
- package/skills/code-review/cells/high-3x5-recall-verify.md +137 -0
- package/skills/code-review/cells/high-8-inline.md +122 -0
- package/skills/code-review/cells/low-1-pass-max4.md +27 -0
- package/skills/code-review/cells/low-1-pass-min-files-4.md +28 -0
- package/skills/code-review/cells/max-5x5-verify-sweep.md +162 -0
- package/skills/code-review/cells/medium-3x5-verify.md +132 -0
- package/skills/code-review/cells/minimal-prompt.md +32 -0
- package/skills/code-review/cells/xhigh-10-inline-sweep.md +149 -0
- package/skills/code-review/cells/xhigh-5x5-verify-sweep.md +162 -0
- package/skills/fewer-permission-prompts/SKILL.md +1 -1
- package/skills/loop/SKILL.md +11 -0
- package/skills/security-review/SKILL.md +2 -5
- package/skills/simplify/SKILL.md +7 -5
- package/skills/workflow-authoring/SKILL.md +143 -0
- package/themes/onecode-light.json +1 -1
- package/themes/onecode.json +1 -1
- package/types/app.d.ts +58 -0
- package/types/community-packages.d.ts +26 -1
- package/extensions/auto-mode/recoverability.ts +0 -229
- package/extensions/lib/capability-index.ts +0 -466
- package/extensions/lib/model-facts.generated.json +0 -2675
- 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
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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
|
|
45
|
-
* idle resident whose turn ended
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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,
|
|
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
|
|
84
|
-
activity: run.
|
|
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
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
/**
|
|
132
|
-
|
|
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
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
|
|
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
|
-
|
|
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
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
row.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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}${
|
|
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
|
|
286
|
-
const
|
|
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 ? "
|
|
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-
|
|
4
|
-
*
|
|
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,
|
|
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
|
-
/**
|
|
45
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
|
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
|
-
|
|
161
|
-
|
|
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
|
-
/**
|
|
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.
|
|
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
|
|
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
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
this.syncTicker(rows.length > 1 ||
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
|
317
|
-
*
|
|
318
|
-
* windows),
|
|
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) {
|