@gajae-code/coding-agent 0.16.3 → 0.16.6
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/CHANGELOG.md +113 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/args.d.ts +2 -2
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/cli/root-flags.d.ts +4 -2
- package/dist/types/cli.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +9 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
- package/dist/types/coordinator-mcp/server.d.ts +7 -0
- package/dist/types/discovery/claude-plugins.d.ts +3 -1
- package/dist/types/discovery/helpers.d.ts +35 -0
- package/dist/types/exec/bash-executor.d.ts +17 -1
- package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
- package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-signals.d.ts +2 -0
- package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
- package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
- package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
- package/dist/types/exec/bash-shell-worker.d.ts +1 -0
- package/dist/types/exec/isolated-shell.d.ts +19 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
- package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/runner.d.ts +46 -6
- package/dist/types/extensibility/extensions/types.d.ts +16 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
- package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +5 -2
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/extensibility/skill-management.d.ts +9 -4
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +14 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
- package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -1
- package/dist/types/modes/components/status-line/command.d.ts +35 -0
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -0
- package/dist/types/modes/components/tool-status-header.d.ts +14 -0
- package/dist/types/modes/controllers/event-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +7 -0
- package/dist/types/runtime-mcp/client.d.ts +9 -1
- package/dist/types/runtime-mcp/manager.d.ts +7 -1
- package/dist/types/runtime-mcp/pool.d.ts +22 -3
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/runtime-mcp/types.d.ts +22 -0
- package/dist/types/sdk/broker/ensure.d.ts +32 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
- package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
- package/dist/types/sdk/client/client.d.ts +6 -0
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/session-runtime.d.ts +26 -1
- package/dist/types/sdk/lifecycle/service.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +10 -2
- package/dist/types/sdk/session-api-key.d.ts +26 -0
- package/dist/types/sdk/session.d.ts +5 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +11 -0
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/setup/paseo/announce.d.ts +112 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +10 -0
- package/dist/types/task/executor.d.ts +5 -1
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/fetch.d.ts +7 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +10 -0
- package/dist/types/tools/path-utils.d.ts +2 -0
- package/dist/types/tools/search.d.ts +6 -0
- package/package.json +9 -7
- package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
- package/scripts/generate-sdk-operation-inventory.ts +11 -0
- package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/args.ts +20 -9
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli/root-flags.ts +5 -3
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +379 -31
- package/src/config/model-preset-registry.ts +120 -19
- package/src/config/model-profiles.ts +40 -0
- package/src/config/model-registry.ts +73 -23
- package/src/config/model-resolver.ts +48 -0
- package/src/config/settings-schema.ts +33 -1
- package/src/config/settings.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +33 -2
- package/src/coordinator-mcp/server.ts +477 -101
- package/src/crash/sanitize.ts +1 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +52 -55
- package/src/discovery/claude-plugins.ts +12 -4
- package/src/discovery/helpers.ts +307 -34
- package/src/exec/bash-executor.ts +330 -50
- package/src/exec/bash-shell-guardian.ts +388 -0
- package/src/exec/bash-shell-runtime-entry.ts +5 -0
- package/src/exec/bash-shell-signals.ts +29 -0
- package/src/exec/bash-shell-supervisor-entry.ts +3 -0
- package/src/exec/bash-shell-supervisor.ts +359 -0
- package/src/exec/bash-shell-worker-entry.ts +5 -0
- package/src/exec/bash-shell-worker-protocol.ts +36 -0
- package/src/exec/bash-shell-worker.ts +116 -0
- package/src/exec/isolated-shell.ts +393 -0
- package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
- package/src/extensibility/extensions/function-hooks.ts +959 -0
- package/src/extensibility/extensions/index.ts +22 -0
- package/src/extensibility/extensions/loader.ts +53 -2
- package/src/extensibility/extensions/runner.ts +895 -264
- package/src/extensibility/extensions/types.ts +42 -0
- package/src/extensibility/extensions/wrapper.ts +56 -15
- package/src/extensibility/gjc-plugins/compiler.ts +228 -60
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/installer.ts +13 -12
- package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
- package/src/extensibility/gjc-plugins/paths.ts +13 -0
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
- package/src/extensibility/gjc-plugins/schema.ts +121 -4
- package/src/extensibility/gjc-plugins/types.ts +25 -0
- package/src/extensibility/hooks/loader.ts +133 -16
- package/src/extensibility/hooks/types.ts +28 -0
- package/src/extensibility/runtime-skill-discovery.ts +149 -190
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +73 -25
- package/src/gjc-runtime/empty-delete-gc.ts +94 -4
- package/src/gjc-runtime/gc-runtime.ts +29 -0
- package/src/gjc-runtime/linux-proc.ts +14 -0
- package/src/gjc-runtime/repository-binding.ts +1 -1
- package/src/gjc-runtime/session-state-lock.ts +143 -10
- package/src/gjc-runtime/session-state-sidecar.ts +27 -11
- package/src/gjc-runtime/state-writer.ts +30 -29
- package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
- package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -0
- package/src/harness-control-plane/session-lease.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +12 -12
- package/src/lsp/config.ts +10 -3
- package/src/lsp/index.ts +1 -1
- package/src/lsp/path-trust.ts +69 -14
- package/src/main.ts +78 -35
- package/src/modes/DESIGN.md +54 -0
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/assistant-message.ts +22 -9
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/model-selector.ts +166 -33
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/settings-selector.ts +1075 -235
- package/src/modes/components/status-line/command.ts +190 -0
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/segments.ts +32 -5
- package/src/modes/components/status-line/types.ts +5 -0
- package/src/modes/components/tool-status-header.ts +396 -22
- package/src/modes/controllers/command-controller.ts +3 -0
- package/src/modes/controllers/event-controller.ts +151 -7
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/controllers/selector-controller.ts +21 -0
- package/src/modes/interactive-mode.ts +53 -6
- package/src/modes/print-mode.ts +5 -1
- package/src/modes/prompt-action-autocomplete.ts +54 -27
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/types.ts +17 -3
- package/src/modes/utils/ui-helpers.ts +2 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/search.md +2 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/client.ts +60 -17
- package/src/runtime-mcp/manager.ts +189 -14
- package/src/runtime-mcp/pool.test.ts +218 -0
- package/src/runtime-mcp/pool.ts +224 -23
- package/src/runtime-mcp/transports/stdio.ts +158 -24
- package/src/runtime-mcp/types.ts +24 -0
- package/src/sdk/broker/broker.ts +124 -32
- package/src/sdk/broker/ensure.ts +248 -41
- package/src/sdk/broker/lifecycle-ledger.ts +7 -17
- package/src/sdk/broker/lifecycle.ts +56 -6
- package/src/sdk/broker/startup-failure.ts +15 -6
- package/src/sdk/bus/chat-daemon-control.ts +3 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +72 -6
- package/src/sdk/bus/chat-effect-journal.ts +132 -7
- package/src/sdk/bus/conversation-store.ts +29 -0
- package/src/sdk/bus/lifecycle-commands.ts +17 -11
- package/src/sdk/bus/slack-conversation.ts +1 -0
- package/src/sdk/bus/slack-daemon.ts +478 -49
- package/src/sdk/bus/slack-live-provider.ts +12 -0
- package/src/sdk/bus/slack-provider.ts +5 -0
- package/src/sdk/bus/telegram-daemon.ts +16 -2
- package/src/sdk/bus/topic-registry.ts +19 -0
- package/src/sdk/cli/master-cli.ts +1 -1
- package/src/sdk/cli/session-cli.ts +11 -8
- package/src/sdk/client/client.ts +66 -17
- package/src/sdk/host/host.ts +29 -9
- package/src/sdk/host/query/revision-store.ts +19 -13
- package/src/sdk/host/reverse-leases.ts +7 -4
- package/src/sdk/host/session-runtime.test.ts +52 -10
- package/src/sdk/host/session-runtime.ts +383 -44
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +77 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session-api-key.ts +94 -0
- package/src/sdk/session.ts +228 -55
- package/src/sdk/transport/relay.ts +35 -13
- package/src/sdk/transport/serve-cli.ts +3 -3
- package/src/session/agent-session.ts +240 -90
- package/src/session/contribution-prep.ts +214 -21
- package/src/session/fallback-chain-controller.ts +38 -0
- package/src/session/internal/managed-session-storage.ts +19 -5
- package/src/session/messages.ts +5 -0
- package/src/session/session-manager.ts +29 -5
- package/src/session-import/redact.ts +5 -1
- package/src/setup/paseo/announce.ts +456 -0
- package/src/setup/paseo/setup-deps.ts +15 -3
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/system-prompt.ts +29 -3
- package/src/task/executor.ts +7 -1
- package/src/task/index.ts +21 -4
- package/src/tools/bash.ts +63 -19
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +29 -1
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +10 -0
- package/src/tools/job.ts +21 -4
- package/src/tools/monitor.ts +8 -2
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/read.ts +14 -2
- package/src/tools/render-utils.ts +1 -3
- package/src/tools/resource-gc.ts +28 -8
- package/src/tools/search.ts +116 -58
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +37 -2
- package/src/tools/subagent.ts +6 -3
- package/src/tools/tool-catalog.generated.ts +10 -3
- package/src/tools/ultragoal-ask-guard.ts +2 -2
- package/src/web/insane/bridge.ts +113 -23
- package/src/web/search/providers/perplexity.ts +102 -41
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
|
|
3
|
+
import { resolveOAuthStorageProvider } from "@gajae-code/ai/core";
|
|
3
4
|
import { type Component, truncateToWidth, visibleWidth } from "@gajae-code/tui";
|
|
4
|
-
import { formatCount, getProjectDir } from "@gajae-code/utils";
|
|
5
|
+
import { formatCount, getProjectDir, logger } from "@gajae-code/utils";
|
|
6
|
+
import { getShellConfig } from "@gajae-code/utils/shell-config";
|
|
5
7
|
import {
|
|
6
8
|
type AppKeybinding,
|
|
7
9
|
KEYBINDINGS,
|
|
@@ -19,6 +21,11 @@ import type { ActionRegistry, FocusDomain } from "../action-registry";
|
|
|
19
21
|
import { EMPTY_JOBS_SNAPSHOT, type JobsSnapshot } from "../jobs-observer";
|
|
20
22
|
import { sanitizeStatusText } from "../shared";
|
|
21
23
|
import { renderSkillHudBar } from "./skill-hud/render";
|
|
24
|
+
import {
|
|
25
|
+
normalizeStatusLineCommandOptions,
|
|
26
|
+
runStatusLineCommand,
|
|
27
|
+
type StatusLineCommandOptions,
|
|
28
|
+
} from "./status-line/command";
|
|
22
29
|
import { lookupCurrentPrCached } from "./status-line/gh";
|
|
23
30
|
import {
|
|
24
31
|
canReuseCachedPr,
|
|
@@ -40,6 +47,7 @@ export interface StatusLineSegmentOptions {
|
|
|
40
47
|
git?: { showBranch?: boolean; showStaged?: boolean; showUnstaged?: boolean; showUntracked?: boolean };
|
|
41
48
|
time?: { format?: "12h" | "24h"; showSeconds?: boolean };
|
|
42
49
|
usage?: { mode?: "used" | "remaining" };
|
|
50
|
+
command?: StatusLineCommandOptions;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
export interface StatusLineSettings {
|
|
@@ -49,6 +57,7 @@ export interface StatusLineSettings {
|
|
|
49
57
|
separator?: StatusLineSeparatorStyle;
|
|
50
58
|
segmentOptions?: StatusLineSegmentOptions;
|
|
51
59
|
previewHighlightSegment?: StatusLineSegmentId;
|
|
60
|
+
previewHighlightStyle?: "focus" | "selected";
|
|
52
61
|
showHookStatus?: boolean;
|
|
53
62
|
showSkillHud?: boolean;
|
|
54
63
|
showActionHints?: boolean;
|
|
@@ -62,6 +71,7 @@ export interface StatusLineComponentOptions {
|
|
|
62
71
|
getKeybindings?: () => KeybindingsManager;
|
|
63
72
|
focusDomain?: FocusDomain;
|
|
64
73
|
keyDisplayContext?: KeyDisplayContext;
|
|
74
|
+
onUpdate?: () => void;
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
export interface StatusLineActionHint {
|
|
@@ -69,6 +79,14 @@ export interface StatusLineActionHint {
|
|
|
69
79
|
content: string;
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
export interface StatusLinePreviewParts {
|
|
83
|
+
left: string[];
|
|
84
|
+
leftIds: StatusLineSegmentId[];
|
|
85
|
+
right: string[];
|
|
86
|
+
rightIds: (StatusLineSegmentId | null)[];
|
|
87
|
+
separator: SeparatorDef;
|
|
88
|
+
}
|
|
89
|
+
|
|
72
90
|
const ACTION_HINT_PRIORITY: readonly AppKeybinding[] = [
|
|
73
91
|
"app.message.sendNow",
|
|
74
92
|
"app.message.followUp",
|
|
@@ -136,6 +154,7 @@ interface CollectedStatusSegments {
|
|
|
136
154
|
/** Parallel to `right`; null for action hints, job counts and the version tag. */
|
|
137
155
|
rightSegIds: (StatusLineSegmentId | null)[];
|
|
138
156
|
previewHighlightSegment: StatusLineSegmentId | undefined;
|
|
157
|
+
previewHighlightStyle: "focus" | "selected";
|
|
139
158
|
sessionAccent: boolean | undefined;
|
|
140
159
|
leftSepWidth: number;
|
|
141
160
|
rightSepWidth: number;
|
|
@@ -155,6 +174,8 @@ export class StatusLineComponent implements Component {
|
|
|
155
174
|
#branchInFlight = false;
|
|
156
175
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
157
176
|
#onBranchChange: (() => void) | null = null;
|
|
177
|
+
#onUpdate: (() => void) | null = null;
|
|
178
|
+
#disposed = false;
|
|
158
179
|
#autoCompactEnabled: boolean = true;
|
|
159
180
|
#hookStatuses: Map<string, string> = new Map();
|
|
160
181
|
#subagentCount: number = 0;
|
|
@@ -195,9 +216,22 @@ export class StatusLineComponent implements Component {
|
|
|
195
216
|
|
|
196
217
|
// Provider usage caching (5-min TTL, OAuth/sub only)
|
|
197
218
|
#cachedUsage: SegmentContext["usage"] = null;
|
|
219
|
+
#cachedUsageReports: unknown = null;
|
|
220
|
+
#cachedUsageProvider: string | undefined;
|
|
198
221
|
#usageFetchedAt = 0;
|
|
199
222
|
#usageInFlight = false;
|
|
200
223
|
|
|
224
|
+
// Configured command segment cache. Command execution is always backgrounded;
|
|
225
|
+
// rendering only reads these bounded, last-known values.
|
|
226
|
+
#commandConfigKey: string | undefined;
|
|
227
|
+
#commandOutput: string | null = null;
|
|
228
|
+
#commandFailed = false;
|
|
229
|
+
#commandFetchedAt = 0;
|
|
230
|
+
#commandInFlightKey: string | undefined;
|
|
231
|
+
#commandAbortController: AbortController | undefined;
|
|
232
|
+
#commandRefreshTimer: ReturnType<typeof setTimeout> | undefined;
|
|
233
|
+
#commandDiagnosticKey: string | undefined;
|
|
234
|
+
|
|
201
235
|
constructor(
|
|
202
236
|
private readonly session: AgentSession,
|
|
203
237
|
options: StatusLineComponentOptions = {},
|
|
@@ -218,6 +252,7 @@ export class StatusLineComponent implements Component {
|
|
|
218
252
|
this.#getKeybindings = options.getKeybindings;
|
|
219
253
|
this.#focusDomain = options.focusDomain ?? "composer";
|
|
220
254
|
this.#keyDisplayContext = options.keyDisplayContext;
|
|
255
|
+
this.#onUpdate = options.onUpdate ?? null;
|
|
221
256
|
}
|
|
222
257
|
|
|
223
258
|
updateSettings(settings: StatusLineSettings): void {
|
|
@@ -300,12 +335,175 @@ export class StatusLineComponent implements Component {
|
|
|
300
335
|
}
|
|
301
336
|
|
|
302
337
|
dispose(): void {
|
|
338
|
+
this.#disposed = true;
|
|
339
|
+
this.#onBranchChange = null;
|
|
340
|
+
this.#onUpdate = null;
|
|
341
|
+
this.#cancelCommandExecution();
|
|
342
|
+
this.#clearCommandRefreshTimer();
|
|
303
343
|
if (this.#gitWatcher) {
|
|
304
344
|
this.#gitWatcher.close();
|
|
305
345
|
this.#gitWatcher = null;
|
|
306
346
|
}
|
|
307
347
|
}
|
|
308
348
|
|
|
349
|
+
#notifyUpdate(): void {
|
|
350
|
+
if (this.#onUpdate) this.#onUpdate();
|
|
351
|
+
else this.#onBranchChange?.();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#logCommandDiagnostic(key: string, message: string, details: Record<string, unknown> = {}): void {
|
|
355
|
+
if (this.#commandDiagnosticKey === key) return;
|
|
356
|
+
this.#commandDiagnosticKey = key;
|
|
357
|
+
logger.warn(message, details);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
#clearCommandRefreshTimer(): void {
|
|
361
|
+
if (this.#commandRefreshTimer) {
|
|
362
|
+
clearTimeout(this.#commandRefreshTimer);
|
|
363
|
+
this.#commandRefreshTimer = undefined;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#cancelCommandExecution(): void {
|
|
368
|
+
this.#commandAbortController?.abort();
|
|
369
|
+
this.#commandAbortController = undefined;
|
|
370
|
+
this.#commandInFlightKey = undefined;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
#disableCommandSegment(): void {
|
|
374
|
+
this.#clearCommandRefreshTimer();
|
|
375
|
+
this.#cancelCommandExecution();
|
|
376
|
+
this.#commandConfigKey = undefined;
|
|
377
|
+
this.#commandOutput = null;
|
|
378
|
+
this.#commandFailed = false;
|
|
379
|
+
this.#commandFetchedAt = 0;
|
|
380
|
+
this.#commandDiagnosticKey = undefined;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#armCommandRefreshTimer(key: string, refreshMs: number): void {
|
|
384
|
+
this.#clearCommandRefreshTimer();
|
|
385
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
386
|
+
this.#commandRefreshTimer = setTimeout(
|
|
387
|
+
() => {
|
|
388
|
+
this.#commandRefreshTimer = undefined;
|
|
389
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
390
|
+
this.#notifyUpdate();
|
|
391
|
+
},
|
|
392
|
+
Math.max(0, refreshMs),
|
|
393
|
+
);
|
|
394
|
+
this.#commandRefreshTimer.unref?.();
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
#trustedCommandOptions(): StatusLineCommandOptions | undefined {
|
|
398
|
+
const globalLeft = this.session.settings.getGlobal("statusLine.leftSegments");
|
|
399
|
+
const globalRight = this.session.settings.getGlobal("statusLine.rightSegments");
|
|
400
|
+
const includesCommand = (value: unknown): boolean => Array.isArray(value) && value.includes("command");
|
|
401
|
+
if (!includesCommand(globalLeft) && !includesCommand(globalRight)) return undefined;
|
|
402
|
+
|
|
403
|
+
const globalSegmentOptions = this.session.settings.getGlobal("statusLine.segmentOptions");
|
|
404
|
+
if (!globalSegmentOptions || typeof globalSegmentOptions !== "object" || Array.isArray(globalSegmentOptions)) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
const commandOptions = (globalSegmentOptions as Record<string, unknown>).command;
|
|
408
|
+
const resolved = normalizeStatusLineCommandOptions(commandOptions);
|
|
409
|
+
return resolved.command ? resolved : undefined;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#refreshCommandInBackground(options: StatusLineCommandOptions | undefined): string {
|
|
413
|
+
const resolved = normalizeStatusLineCommandOptions(options);
|
|
414
|
+
const key = JSON.stringify(resolved);
|
|
415
|
+
if (this.#commandConfigKey !== key) {
|
|
416
|
+
this.#clearCommandRefreshTimer();
|
|
417
|
+
this.#cancelCommandExecution();
|
|
418
|
+
this.#commandConfigKey = key;
|
|
419
|
+
this.#commandOutput = null;
|
|
420
|
+
this.#commandFailed = false;
|
|
421
|
+
this.#commandFetchedAt = 0;
|
|
422
|
+
this.#commandDiagnosticKey = undefined;
|
|
423
|
+
this.#renderedRowsCache = undefined;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (!resolved.command) {
|
|
427
|
+
this.#commandFailed = true;
|
|
428
|
+
this.#logCommandDiagnostic(key, "Status line command segment is configured without a command");
|
|
429
|
+
return key;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const now = Date.now();
|
|
433
|
+
if (this.#commandInFlightKey === key) return key;
|
|
434
|
+
const elapsed = now - this.#commandFetchedAt;
|
|
435
|
+
if (this.#commandFetchedAt > 0 && elapsed < resolved.refreshMs) {
|
|
436
|
+
this.#armCommandRefreshTimer(key, resolved.refreshMs - elapsed);
|
|
437
|
+
return key;
|
|
438
|
+
}
|
|
439
|
+
this.#clearCommandRefreshTimer();
|
|
440
|
+
|
|
441
|
+
this.#commandInFlightKey = key;
|
|
442
|
+
const abortController = new AbortController();
|
|
443
|
+
this.#commandAbortController = abortController;
|
|
444
|
+
let shellConfig: { shell: string; args: string[]; env: Record<string, string> };
|
|
445
|
+
try {
|
|
446
|
+
shellConfig = getShellConfig(this.session.settings.getGlobal("shellPath"));
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (this.#commandInFlightKey === key && this.#commandAbortController === abortController) {
|
|
449
|
+
this.#commandInFlightKey = undefined;
|
|
450
|
+
this.#commandAbortController = undefined;
|
|
451
|
+
}
|
|
452
|
+
this.#commandFailed = true;
|
|
453
|
+
this.#commandFetchedAt = Date.now();
|
|
454
|
+
this.#logCommandDiagnostic(key, "Status line command shell configuration failed", {
|
|
455
|
+
error: sanitizeStatusText(error instanceof Error ? error.message : String(error)).slice(0, 256),
|
|
456
|
+
});
|
|
457
|
+
this.#armCommandRefreshTimer(key, resolved.refreshMs);
|
|
458
|
+
return key;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
void runStatusLineCommand(resolved.command, {
|
|
462
|
+
cwd: getProjectDir(),
|
|
463
|
+
shell: shellConfig.shell,
|
|
464
|
+
shellArgs: shellConfig.args,
|
|
465
|
+
env: shellConfig.env,
|
|
466
|
+
timeoutMs: resolved.timeoutMs,
|
|
467
|
+
signal: abortController.signal,
|
|
468
|
+
})
|
|
469
|
+
.then(result => {
|
|
470
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
471
|
+
this.#commandFetchedAt = Date.now();
|
|
472
|
+
if (result.timedOut || result.exitCode !== 0) {
|
|
473
|
+
this.#commandFailed = true;
|
|
474
|
+
this.#logCommandDiagnostic(key, "Status line command failed", {
|
|
475
|
+
exitCode: result.exitCode,
|
|
476
|
+
timedOut: result.timedOut,
|
|
477
|
+
stderr: result.stderr,
|
|
478
|
+
outputTruncated: result.outputTruncated,
|
|
479
|
+
});
|
|
480
|
+
} else {
|
|
481
|
+
this.#commandOutput = result.stdout;
|
|
482
|
+
this.#commandFailed = false;
|
|
483
|
+
this.#commandDiagnosticKey = undefined;
|
|
484
|
+
}
|
|
485
|
+
this.#renderedRowsCache = undefined;
|
|
486
|
+
this.#notifyUpdate();
|
|
487
|
+
})
|
|
488
|
+
.catch(error => {
|
|
489
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
490
|
+
this.#commandFetchedAt = Date.now();
|
|
491
|
+
this.#commandFailed = true;
|
|
492
|
+
this.#logCommandDiagnostic(key, "Status line command could not start", {
|
|
493
|
+
error: sanitizeStatusText(error instanceof Error ? error.message : String(error)).slice(0, 256),
|
|
494
|
+
});
|
|
495
|
+
this.#renderedRowsCache = undefined;
|
|
496
|
+
this.#notifyUpdate();
|
|
497
|
+
})
|
|
498
|
+
.finally(() => {
|
|
499
|
+
if (this.#commandInFlightKey !== key || this.#commandAbortController !== abortController) return;
|
|
500
|
+
this.#commandInFlightKey = undefined;
|
|
501
|
+
this.#commandAbortController = undefined;
|
|
502
|
+
this.#armCommandRefreshTimer(key, resolved.refreshMs);
|
|
503
|
+
});
|
|
504
|
+
return key;
|
|
505
|
+
}
|
|
506
|
+
|
|
309
507
|
invalidate(): void {
|
|
310
508
|
this.#invalidateGitCaches();
|
|
311
509
|
}
|
|
@@ -502,6 +700,8 @@ export class StatusLineComponent implements Component {
|
|
|
502
700
|
void fetcher
|
|
503
701
|
.call(this.session)
|
|
504
702
|
.then(reports => {
|
|
703
|
+
this.#cachedUsageReports = reports;
|
|
704
|
+
this.#cachedUsageProvider = this.#activeUsageProvider();
|
|
505
705
|
this.#cachedUsage = this.#normalizeUsageReports(reports);
|
|
506
706
|
this.#usageFetchedAt = Date.now();
|
|
507
707
|
if (this.#onBranchChange) {
|
|
@@ -521,7 +721,9 @@ export class StatusLineComponent implements Component {
|
|
|
521
721
|
}
|
|
522
722
|
|
|
523
723
|
#normalizeUsageReports(reports: unknown): SegmentContext["usage"] {
|
|
524
|
-
|
|
724
|
+
const activeProvider = this.#activeUsageProvider();
|
|
725
|
+
|
|
726
|
+
if (!activeProvider || !Array.isArray(reports)) return null;
|
|
525
727
|
const windows: NonNullable<SegmentContext["usage"]>["windows"] = [];
|
|
526
728
|
const seen = new Set<string>();
|
|
527
729
|
const now = Date.now();
|
|
@@ -555,20 +757,24 @@ export class StatusLineComponent implements Component {
|
|
|
555
757
|
for (const report of reports) {
|
|
556
758
|
if (!report || typeof report !== "object") continue;
|
|
557
759
|
const provider = (report as { provider?: unknown }).provider;
|
|
558
|
-
const providerId = typeof provider === "string" ? provider : undefined;
|
|
760
|
+
const providerId = typeof provider === "string" ? resolveOAuthStorageProvider(provider) : undefined;
|
|
761
|
+
if (providerId !== activeProvider) continue;
|
|
559
762
|
const limits = (report as { limits?: unknown }).limits;
|
|
560
763
|
if (!Array.isArray(limits)) continue;
|
|
561
764
|
for (const limit of limits) {
|
|
562
765
|
if (!limit || typeof limit !== "object") continue;
|
|
563
766
|
const l = limit as {
|
|
564
767
|
id?: unknown;
|
|
565
|
-
scope?: { windowId?: string; tier?: string; modelId?: string };
|
|
768
|
+
scope?: { provider?: unknown; windowId?: string; tier?: string; modelId?: string };
|
|
566
769
|
window?: { id?: string; resetsAt?: number };
|
|
567
770
|
amount?: { usedFraction?: number };
|
|
568
771
|
};
|
|
569
772
|
const fraction = l.amount?.usedFraction;
|
|
570
773
|
if (typeof fraction !== "number") continue;
|
|
571
774
|
const id = typeof l.id === "string" ? l.id : "";
|
|
775
|
+
const scopeProvider =
|
|
776
|
+
typeof l.scope?.provider === "string" ? resolveOAuthStorageProvider(l.scope.provider) : undefined;
|
|
777
|
+
if (scopeProvider !== undefined && scopeProvider !== providerId) continue;
|
|
572
778
|
const windowId = l.scope?.windowId ?? l.window?.id;
|
|
573
779
|
const tier = l.scope?.tier;
|
|
574
780
|
const modelId = l.scope?.modelId;
|
|
@@ -584,9 +790,9 @@ export class StatusLineComponent implements Component {
|
|
|
584
790
|
}
|
|
585
791
|
} else if (windowId === "5h" && !tier) {
|
|
586
792
|
pushWindow(`${providerId ?? "provider"}:5h`, "5h", fraction, resetsAt, "m");
|
|
587
|
-
} else if (windowId === "7d" && !tier) {
|
|
793
|
+
} else if (windowId === "7d" && !tier && providerId !== "grok-build" && providerId !== "xai") {
|
|
588
794
|
pushWindow(`${providerId ?? "provider"}:7d`, "7d", fraction, resetsAt, "h");
|
|
589
|
-
} else if (providerId === "grok-build" && windowId === "weekly" && !tier) {
|
|
795
|
+
} else if (providerId === "grok-build" && id === "grok-build:weekly" && windowId === "weekly" && !tier) {
|
|
590
796
|
pushWindow("grok-build:weekly", "weekly", fraction, resetsAt, "h");
|
|
591
797
|
}
|
|
592
798
|
}
|
|
@@ -595,17 +801,32 @@ export class StatusLineComponent implements Component {
|
|
|
595
801
|
return windows.length > 0 ? { windows } : null;
|
|
596
802
|
}
|
|
597
803
|
|
|
804
|
+
#activeUsageProvider(): string | undefined {
|
|
805
|
+
const model = this.session.state.model ?? this.session.model;
|
|
806
|
+
if (!model || typeof model !== "object") return undefined;
|
|
807
|
+
const provider = (model as { provider?: unknown }).provider;
|
|
808
|
+
return typeof provider === "string" && provider.length > 0 ? resolveOAuthStorageProvider(provider) : undefined;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
#syncCachedUsageForActiveProvider(): void {
|
|
812
|
+
const activeProvider = this.#activeUsageProvider();
|
|
813
|
+
if (this.#cachedUsageProvider === activeProvider) return;
|
|
814
|
+
this.#cachedUsageProvider = activeProvider;
|
|
815
|
+
this.#cachedUsage = this.#normalizeUsageReports(this.#cachedUsageReports);
|
|
816
|
+
}
|
|
817
|
+
|
|
598
818
|
#buildSegmentContext(
|
|
599
819
|
width: number,
|
|
600
820
|
effectiveSettings: Required<
|
|
601
821
|
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
602
822
|
> &
|
|
603
823
|
StatusLineSettings,
|
|
824
|
+
previewOnly = false,
|
|
604
825
|
): SegmentContext {
|
|
605
826
|
const state = this.session.state;
|
|
606
827
|
|
|
607
|
-
|
|
608
|
-
this.refreshUsageInBackground();
|
|
828
|
+
this.#syncCachedUsageForActiveProvider();
|
|
829
|
+
if (!previewOnly) this.refreshUsageInBackground();
|
|
609
830
|
|
|
610
831
|
// Get usage statistics
|
|
611
832
|
const aggregateUsageStats = this.session.sessionManager?.getUsageStatistics() ?? {
|
|
@@ -633,6 +854,20 @@ export class StatusLineComponent implements Component {
|
|
|
633
854
|
// treats a null value as hidden, so gating here is behavior-identical.
|
|
634
855
|
const prSegmentActive =
|
|
635
856
|
effectiveSettings.leftSegments.includes("pr") || effectiveSettings.rightSegments.includes("pr");
|
|
857
|
+
const gitSegmentActive =
|
|
858
|
+
effectiveSettings.leftSegments.includes("git") || effectiveSettings.rightSegments.includes("git");
|
|
859
|
+
const commandSegmentActive =
|
|
860
|
+
effectiveSettings.leftSegments.includes("command") || effectiveSettings.rightSegments.includes("command");
|
|
861
|
+
const commandOptions = commandSegmentActive ? this.#trustedCommandOptions() : undefined;
|
|
862
|
+
let commandKey: string | undefined = this.#commandConfigKey;
|
|
863
|
+
if (!previewOnly) {
|
|
864
|
+
if (commandOptions) {
|
|
865
|
+
commandKey = this.#refreshCommandInBackground(commandOptions);
|
|
866
|
+
} else {
|
|
867
|
+
this.#disableCommandSegment();
|
|
868
|
+
commandKey = undefined;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
636
871
|
|
|
637
872
|
return {
|
|
638
873
|
session: this.session,
|
|
@@ -650,10 +885,20 @@ export class StatusLineComponent implements Component {
|
|
|
650
885
|
sessionStartTime: this.#sessionStartTime,
|
|
651
886
|
git: {
|
|
652
887
|
branch: this.#getCurrentBranch(),
|
|
653
|
-
status: this.#getGitStatus(),
|
|
654
|
-
pr: prSegmentActive ? this.#lookupPr() : null,
|
|
888
|
+
status: gitSegmentActive ? this.#getGitStatus() : null,
|
|
889
|
+
pr: prSegmentActive ? (previewOnly ? (this.#cachedPr ?? null) : this.#lookupPr()) : null,
|
|
655
890
|
},
|
|
656
891
|
usage: this.#cachedUsage,
|
|
892
|
+
command:
|
|
893
|
+
commandSegmentActive && (previewOnly || commandKey !== undefined)
|
|
894
|
+
? {
|
|
895
|
+
output: this.#commandOutput,
|
|
896
|
+
failed: this.#commandFailed,
|
|
897
|
+
pending: previewOnly
|
|
898
|
+
? this.#commandOutput === null && !this.#commandFailed
|
|
899
|
+
: this.#commandInFlightKey === commandKey,
|
|
900
|
+
}
|
|
901
|
+
: undefined,
|
|
657
902
|
};
|
|
658
903
|
}
|
|
659
904
|
|
|
@@ -730,7 +975,14 @@ export class StatusLineComponent implements Component {
|
|
|
730
975
|
if (available <= 0 || count <= 0) return "";
|
|
731
976
|
const withCount = `…+${count}`;
|
|
732
977
|
const text = visibleWidth(withCount) <= available ? withCount : "…";
|
|
733
|
-
return theme.fg("dim", text);
|
|
978
|
+
return truncateToWidth(theme.fg("dim", text), available);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
#renderPreviewHighlight(content: string, style: "focus" | "selected"): string {
|
|
982
|
+
const bgColor = style === "selected" ? "warning" : "text";
|
|
983
|
+
const bgAnsi = theme.getFgAnsi(bgColor).replace("\x1b[38;", "\x1b[48;");
|
|
984
|
+
const fgAnsi = theme.getBgAnsi("selectedBg").replace("\x1b[48;", "\x1b[38;");
|
|
985
|
+
return `${bgAnsi}${fgAnsi}> ${Bun.stripANSI(content)} <\x1b[0m`;
|
|
734
986
|
}
|
|
735
987
|
|
|
736
988
|
#renderStatusGroup(
|
|
@@ -795,8 +1047,9 @@ export class StatusLineComponent implements Component {
|
|
|
795
1047
|
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
796
1048
|
> &
|
|
797
1049
|
StatusLineSettings,
|
|
1050
|
+
previewOnly = false,
|
|
798
1051
|
): CollectedStatusSegments {
|
|
799
|
-
const ctx = this.#buildSegmentContext(width, effectiveSettings);
|
|
1052
|
+
const ctx = this.#buildSegmentContext(width, effectiveSettings, previewOnly);
|
|
800
1053
|
const separatorDef = getSeparator(effectiveSettings.separator ?? "powerline-thin", theme);
|
|
801
1054
|
|
|
802
1055
|
// Use the subtle surface tone (the same elevated background as user-message
|
|
@@ -808,12 +1061,14 @@ export class StatusLineComponent implements Component {
|
|
|
808
1061
|
const sepAnsi = theme.getFgAnsi("statusLineSep");
|
|
809
1062
|
|
|
810
1063
|
const previewHighlightSegment = effectiveSettings.previewHighlightSegment;
|
|
1064
|
+
const previewHighlightStyle = effectiveSettings.previewHighlightStyle ?? "focus";
|
|
811
1065
|
const highlightSegment = (segId: StatusLineSegmentId, content: string): string =>
|
|
812
|
-
previewHighlightSegment === segId ?
|
|
1066
|
+
previewHighlightSegment === segId ? this.#renderPreviewHighlight(content, previewHighlightStyle) : content;
|
|
813
1067
|
|
|
814
1068
|
const left: string[] = [];
|
|
815
1069
|
const leftSegIds: StatusLineSegmentId[] = [];
|
|
816
1070
|
for (const segId of effectiveSettings.leftSegments) {
|
|
1071
|
+
if (segId === "command" && !ctx.command) continue;
|
|
817
1072
|
const rendered = renderSegment(segId, ctx);
|
|
818
1073
|
if (rendered.visible && rendered.content) {
|
|
819
1074
|
left.push(highlightSegment(segId, rendered.content));
|
|
@@ -834,6 +1089,7 @@ export class StatusLineComponent implements Component {
|
|
|
834
1089
|
this.#keyDisplayContext,
|
|
835
1090
|
);
|
|
836
1091
|
for (const segId of effectiveSettings.rightSegments) {
|
|
1092
|
+
if (segId === "command" && !ctx.command) continue;
|
|
837
1093
|
const rendered = renderSegment(segId, ctx);
|
|
838
1094
|
if (rendered.visible && rendered.content) {
|
|
839
1095
|
right.push(highlightSegment(segId, rendered.content));
|
|
@@ -869,6 +1125,7 @@ export class StatusLineComponent implements Component {
|
|
|
869
1125
|
right,
|
|
870
1126
|
rightSegIds,
|
|
871
1127
|
previewHighlightSegment,
|
|
1128
|
+
previewHighlightStyle,
|
|
872
1129
|
sessionAccent: effectiveSettings.sessionAccent,
|
|
873
1130
|
leftSepWidth: visibleWidth(separatorDef.left),
|
|
874
1131
|
rightSepWidth: visibleWidth(separatorDef.right),
|
|
@@ -965,7 +1222,7 @@ export class StatusLineComponent implements Component {
|
|
|
965
1222
|
|
|
966
1223
|
#buildStatusLine(width: number, precollected?: CollectedStatusSegments): string {
|
|
967
1224
|
const seg = precollected ?? this.#collectStatusSegments(width, this.#resolveSettings());
|
|
968
|
-
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment } = seg;
|
|
1225
|
+
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment, previewHighlightStyle } = seg;
|
|
969
1226
|
const { leftSepWidth, rightSepWidth, leftCapWidth, rightCapWidth } = seg;
|
|
970
1227
|
const topFillWidth = Math.max(0, width);
|
|
971
1228
|
|
|
@@ -998,7 +1255,10 @@ export class StatusLineComponent implements Component {
|
|
|
998
1255
|
const overflow = totalWidth() - budget;
|
|
999
1256
|
const shrunk = this.#shrinkPathToWidth(left[pathIdx], ctx, overflow);
|
|
1000
1257
|
if (shrunk !== null) {
|
|
1001
|
-
left[pathIdx] =
|
|
1258
|
+
left[pathIdx] =
|
|
1259
|
+
previewHighlightSegment === "path"
|
|
1260
|
+
? this.#renderPreviewHighlight(shrunk, previewHighlightStyle)
|
|
1261
|
+
: shrunk;
|
|
1002
1262
|
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1003
1263
|
}
|
|
1004
1264
|
}
|
|
@@ -1087,9 +1347,9 @@ export class StatusLineComponent implements Component {
|
|
|
1087
1347
|
* being dropped. Falls back to the polished justified single row whenever
|
|
1088
1348
|
* everything fits on one line.
|
|
1089
1349
|
*/
|
|
1090
|
-
#buildStatusRows(width: number, maxRows: number): string[] {
|
|
1350
|
+
#buildStatusRows(width: number, maxRows: number, previewOnly = false): string[] {
|
|
1091
1351
|
const effectiveSettings = this.#resolveSettings();
|
|
1092
|
-
const seg = this.#collectStatusSegments(width, effectiveSettings);
|
|
1352
|
+
const seg = this.#collectStatusSegments(width, effectiveSettings, previewOnly);
|
|
1093
1353
|
const cacheKey = JSON.stringify({
|
|
1094
1354
|
width,
|
|
1095
1355
|
maxRows,
|
|
@@ -1124,14 +1384,14 @@ export class StatusLineComponent implements Component {
|
|
|
1124
1384
|
.join(","),
|
|
1125
1385
|
version: this.#version,
|
|
1126
1386
|
});
|
|
1127
|
-
if (this.#renderedRowsCache?.key === cacheKey) {
|
|
1387
|
+
if (!previewOnly && this.#renderedRowsCache?.key === cacheKey) {
|
|
1128
1388
|
this.#renderedRowsCacheHits++;
|
|
1129
1389
|
return [...this.#renderedRowsCache.rows];
|
|
1130
1390
|
}
|
|
1131
|
-
this.#renderedRowsCacheMisses++;
|
|
1391
|
+
if (!previewOnly) this.#renderedRowsCacheMisses++;
|
|
1132
1392
|
|
|
1133
1393
|
if (seg.left.length === 0 && seg.right.length === 0) {
|
|
1134
|
-
this.#renderedRowsCache = { key: cacheKey, rows: [] };
|
|
1394
|
+
if (!previewOnly) this.#renderedRowsCache = { key: cacheKey, rows: [] };
|
|
1135
1395
|
return [];
|
|
1136
1396
|
}
|
|
1137
1397
|
|
|
@@ -1209,7 +1469,7 @@ export class StatusLineComponent implements Component {
|
|
|
1209
1469
|
if (dropped > 0) {
|
|
1210
1470
|
const priorityRow = this.#priorityRowFor(seg, [], [], topFillWidth);
|
|
1211
1471
|
if (priorityRow !== null) {
|
|
1212
|
-
this.#renderedRowsCache = { key: cacheKey, rows: [priorityRow] };
|
|
1472
|
+
if (!previewOnly) this.#renderedRowsCache = { key: cacheKey, rows: [priorityRow] };
|
|
1213
1473
|
return [priorityRow];
|
|
1214
1474
|
}
|
|
1215
1475
|
}
|
|
@@ -1249,7 +1509,7 @@ export class StatusLineComponent implements Component {
|
|
|
1249
1509
|
if (packedRows.length === 0 && marker) rows = [marker];
|
|
1250
1510
|
}
|
|
1251
1511
|
|
|
1252
|
-
this.#renderedRowsCache = { key: cacheKey, rows };
|
|
1512
|
+
if (!previewOnly) this.#renderedRowsCache = { key: cacheKey, rows };
|
|
1253
1513
|
return [...rows];
|
|
1254
1514
|
}
|
|
1255
1515
|
|
|
@@ -1271,6 +1531,120 @@ export class StatusLineComponent implements Component {
|
|
|
1271
1531
|
return this.#buildStatusRows(width, this.#resolveMaxRows()).join("\n");
|
|
1272
1532
|
}
|
|
1273
1533
|
|
|
1534
|
+
getPreviewContentForSettings(width: number, previewSettings: StatusLineSettings): string {
|
|
1535
|
+
const previousSettings = this.#settings;
|
|
1536
|
+
this.#settings = { ...previousSettings, previewHighlightSegment: undefined, ...previewSettings };
|
|
1537
|
+
try {
|
|
1538
|
+
return this.#buildStatusRows(width, this.#resolveMaxRows(), true).join("\n");
|
|
1539
|
+
} finally {
|
|
1540
|
+
this.#settings = previousSettings;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
#placePreviewSegments(
|
|
1545
|
+
seg: CollectedStatusSegments,
|
|
1546
|
+
width: number,
|
|
1547
|
+
): {
|
|
1548
|
+
left: string[];
|
|
1549
|
+
leftIds: StatusLineSegmentId[];
|
|
1550
|
+
right: string[];
|
|
1551
|
+
rightIds: (StatusLineSegmentId | null)[];
|
|
1552
|
+
} {
|
|
1553
|
+
const {
|
|
1554
|
+
ctx,
|
|
1555
|
+
previewHighlightSegment,
|
|
1556
|
+
previewHighlightStyle,
|
|
1557
|
+
leftSepWidth,
|
|
1558
|
+
rightSepWidth,
|
|
1559
|
+
leftCapWidth,
|
|
1560
|
+
rightCapWidth,
|
|
1561
|
+
} = seg;
|
|
1562
|
+
const rank = this.#priorityRanker(seg);
|
|
1563
|
+
const layout = (budget: number) => {
|
|
1564
|
+
const left = [...seg.left];
|
|
1565
|
+
const leftIds = [...seg.leftSegIds];
|
|
1566
|
+
const right = [...seg.right];
|
|
1567
|
+
const rightIds = [...seg.rightSegIds];
|
|
1568
|
+
let leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1569
|
+
let rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1570
|
+
const totalWidth = () => leftWidth + rightWidth + (left.length > 0 && right.length > 0 ? 1 : 0);
|
|
1571
|
+
let dropped = 0;
|
|
1572
|
+
|
|
1573
|
+
const pathIdx = leftIds.indexOf("path");
|
|
1574
|
+
if (pathIdx >= 0 && totalWidth() > budget) {
|
|
1575
|
+
const shrunk = this.#shrinkPathToWidth(left[pathIdx], ctx, totalWidth() - budget);
|
|
1576
|
+
if (shrunk !== null) {
|
|
1577
|
+
left[pathIdx] =
|
|
1578
|
+
previewHighlightSegment === "path"
|
|
1579
|
+
? this.#renderPreviewHighlight(shrunk, previewHighlightStyle)
|
|
1580
|
+
: shrunk;
|
|
1581
|
+
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
while (totalWidth() > budget && (left.length > 0 || right.length > 0)) {
|
|
1585
|
+
let victimSide: "left" | "right" | null = null;
|
|
1586
|
+
let victimIndex = -1;
|
|
1587
|
+
let victimRank = Number.POSITIVE_INFINITY;
|
|
1588
|
+
for (let index = right.length - 1; index >= 0; index -= 1) {
|
|
1589
|
+
const candidate = rank(rightIds[index]);
|
|
1590
|
+
if (candidate < victimRank) {
|
|
1591
|
+
victimRank = candidate;
|
|
1592
|
+
victimSide = "right";
|
|
1593
|
+
victimIndex = index;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
for (let index = left.length - 1; index >= 0; index -= 1) {
|
|
1597
|
+
const candidate = rank(leftIds[index]);
|
|
1598
|
+
if (candidate < victimRank) {
|
|
1599
|
+
victimRank = candidate;
|
|
1600
|
+
victimSide = "left";
|
|
1601
|
+
victimIndex = index;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
if (victimSide === null) break;
|
|
1605
|
+
if (victimSide === "right") {
|
|
1606
|
+
right.splice(victimIndex, 1);
|
|
1607
|
+
rightIds.splice(victimIndex, 1);
|
|
1608
|
+
rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1609
|
+
} else {
|
|
1610
|
+
left.splice(victimIndex, 1);
|
|
1611
|
+
leftIds.splice(victimIndex, 1);
|
|
1612
|
+
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1613
|
+
}
|
|
1614
|
+
dropped += 1;
|
|
1615
|
+
}
|
|
1616
|
+
return { left, leftIds, right, rightIds, dropped };
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
let placed = layout(Math.max(0, width));
|
|
1620
|
+
let reserved = 0;
|
|
1621
|
+
if (placed.dropped > 0 && width > 0) {
|
|
1622
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
1623
|
+
const needed = Math.min(width, visibleWidth(`…+${placed.dropped}`));
|
|
1624
|
+
if (needed <= reserved) break;
|
|
1625
|
+
reserved = needed;
|
|
1626
|
+
placed = layout(Math.max(0, width - reserved));
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return placed;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
getPreviewPartsForSettings(width: number, previewSettings: StatusLineSettings): StatusLinePreviewParts {
|
|
1633
|
+
const previousSettings = this.#settings;
|
|
1634
|
+
this.#settings = { ...previousSettings, ...previewSettings };
|
|
1635
|
+
this.#resolvedSettingsCache = undefined;
|
|
1636
|
+
this.#resolvedSettingsFingerprint = undefined;
|
|
1637
|
+
try {
|
|
1638
|
+
const seg = this.#collectStatusSegments(width, this.#resolveSettings(), true);
|
|
1639
|
+
const placed = this.#placePreviewSegments(seg, width);
|
|
1640
|
+
return { ...placed, separator: seg.separatorDef };
|
|
1641
|
+
} finally {
|
|
1642
|
+
this.#settings = previousSettings;
|
|
1643
|
+
this.#resolvedSettingsCache = undefined;
|
|
1644
|
+
this.#resolvedSettingsFingerprint = undefined;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1274
1648
|
getCacheStatsForTest(): { rowHits: number; rowMisses: number } {
|
|
1275
1649
|
return { rowHits: this.#renderedRowsCacheHits, rowMisses: this.#renderedRowsCacheMisses };
|
|
1276
1650
|
}
|
|
@@ -1043,6 +1043,7 @@ export class CommandController {
|
|
|
1043
1043
|
this.ctx.ui.requestRender();
|
|
1044
1044
|
|
|
1045
1045
|
prepareTranscriptRebuild(this.ctx.ui, "replace-identity");
|
|
1046
|
+
this.ctx.resetAssistantTextPresentation();
|
|
1046
1047
|
this.ctx.chatContainer.clear();
|
|
1047
1048
|
this.ctx.pendingMessagesContainer.clear();
|
|
1048
1049
|
this.ctx.compactionQueuedMessages = [];
|
|
@@ -1083,6 +1084,7 @@ export class CommandController {
|
|
|
1083
1084
|
this.ctx.ui.requestRender();
|
|
1084
1085
|
|
|
1085
1086
|
prepareTranscriptRebuild(this.ctx.ui, "replace-identity");
|
|
1087
|
+
this.ctx.resetAssistantTextPresentation();
|
|
1086
1088
|
this.ctx.chatContainer.clear();
|
|
1087
1089
|
this.ctx.pendingMessagesContainer.clear();
|
|
1088
1090
|
this.ctx.compactionQueuedMessages = [];
|
|
@@ -1231,6 +1233,7 @@ export class CommandController {
|
|
|
1231
1233
|
const meta = outputMeta().truncationFromSummary(result, { direction: "tail" }).get();
|
|
1232
1234
|
this.ctx.bashComponent.setComplete(result.exitCode, result.cancelled, {
|
|
1233
1235
|
output: result.output,
|
|
1236
|
+
signal: result.signal,
|
|
1234
1237
|
truncation: meta?.truncation,
|
|
1235
1238
|
});
|
|
1236
1239
|
}
|