@sayknow-cli/coding-agent 0.3.12 → 0.3.15
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/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +2 -2
- package/dist/types/cli/classify-install-target.d.ts +0 -10
- package/dist/types/cli/daemon-cli.d.ts +2 -0
- package/dist/types/cli/initial-message.d.ts +1 -1
- package/dist/types/cli/malloc-env-guard.d.ts +65 -0
- package/dist/types/cli/malloc-env-guard.test.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +3 -1
- package/dist/types/cli/session-picker.d.ts +4 -3
- package/dist/types/cli/update-cli.d.ts +22 -3
- package/dist/types/cli.d.ts +5 -0
- package/dist/types/commands/daemon.d.ts +6 -2
- package/dist/types/commands/launch.d.ts +1 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/commands/web-search.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +4 -4
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +9 -9
- package/dist/types/commit/agentic/tools/schemas.d.ts +9 -9
- package/dist/types/commit/agentic/tools/split-commit.d.ts +9 -9
- package/dist/types/commit/analysis/conventional.d.ts +1 -1
- package/dist/types/commit/analysis/summary.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +3 -3
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/map-phase.d.ts +1 -1
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +9 -9
- package/dist/types/config/keybindings.d.ts +134 -129
- package/dist/types/config/model-profile-activation.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +35 -35
- package/dist/types/config/models-config-schema.d.ts +111 -111
- package/dist/types/config/settings-schema.d.ts +56 -16
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/model-preset.d.ts +45 -0
- package/dist/types/coordinator-mcp/policy.d.ts +3 -0
- package/dist/types/coordinator-mcp/server.d.ts +79 -6
- package/dist/types/coordinator-mcp/session-reaper.d.ts +53 -0
- package/dist/types/daemon/control-types.d.ts +10 -0
- package/dist/types/daemon/operator-contract.d.ts +47 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.d.ts +4 -0
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/errors.d.ts +12 -12
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -4
- package/dist/types/edit/modes/patch.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -1
- package/dist/types/goals/tools/goal-tool.d.ts +6 -6
- package/dist/types/i18n/messages/en.d.ts +8 -8
- package/dist/types/internal-urls/local-protocol.d.ts +13 -9
- package/dist/types/lsp/index.d.ts +9 -9
- package/dist/types/lsp/types.d.ts +9 -9
- package/dist/types/main.d.ts +66 -2
- package/dist/types/modes/acp/acp-agent.d.ts +19 -2
- package/dist/types/modes/acp/acp-mode.d.ts +12 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -1
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +32 -0
- package/dist/types/modes/components/pet-selector.d.ts +11 -0
- package/dist/types/modes/components/sayknow-pet-widget.d.ts +59 -0
- package/dist/types/modes/components/session-selector.d.ts +22 -9
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +0 -10
- package/dist/types/modes/components/tool-execution.d.ts +1 -0
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/controllers/event-controller.d.ts +4 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +24 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +22 -0
- package/dist/types/modes/print-mode.d.ts +3 -4
- package/dist/types/modes/rpc/rpc-client.d.ts +7 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +17 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +8 -0
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +5 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +45 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +42 -2
- package/dist/types/modes/theme/defaults/index.d.ts +568 -568
- package/dist/types/modes/types.d.ts +28 -2
- package/dist/types/modes/utils/abort-message.d.ts +1 -1
- package/dist/types/modes/utils/context-usage.d.ts +4 -19
- package/dist/types/modes/utils/irc-message.d.ts +38 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +13 -4
- package/dist/types/notifications/config.d.ts +34 -0
- package/dist/types/notifications/daemon-paths.d.ts +7 -0
- package/dist/types/notifications/index.d.ts +27 -0
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +12 -4
- package/dist/types/notifications/notification-service.d.ts +144 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +27 -9
- package/dist/types/notifications/telegram-daemon-control.d.ts +6 -0
- package/dist/types/notifications/telegram-daemon.d.ts +130 -3
- package/dist/types/notifications/telegram-reference.d.ts +19 -2
- package/dist/types/notifications/topic-registry.d.ts +24 -4
- package/dist/types/registry/agent-registry.d.ts +2 -0
- package/dist/types/rlm/index.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +40 -3
- package/dist/types/session/cache-economics.d.ts +79 -0
- package/dist/types/session/context-estimation.d.ts +31 -0
- package/dist/types/session/provider-safety-stop.d.ts +5 -0
- package/dist/types/session/session-manager.d.ts +124 -4
- package/dist/types/session/session-storage.d.ts +155 -13
- package/dist/types/setup/credential-auto-import.d.ts +2 -2
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +34 -3
- package/dist/types/skc-runtime/state-schema.d.ts +19 -19
- package/dist/types/skc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/skc-runtime/tmux-owner-isolation-cli.d.ts +5 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +309 -0
- package/dist/types/skc-runtime/tmux-sessions.d.ts +49 -17
- package/dist/types/task/agents.d.ts +2 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +35 -27
- package/dist/types/tools/ask-answer-registry.d.ts +5 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/browser/tab-supervisor.d.ts +6 -0
- package/dist/types/tools/browser.d.ts +28 -28
- package/dist/types/tools/computer.d.ts +20 -20
- package/dist/types/tools/cron.d.ts +2 -2
- package/dist/types/tools/debug.d.ts +40 -40
- package/dist/types/tools/gh.d.ts +10 -10
- package/dist/types/tools/image-gen.d.ts +2 -2
- package/dist/types/tools/index.d.ts +58 -6
- package/dist/types/tools/monitor.d.ts +6 -6
- package/dist/types/tools/skill-discovery.d.ts +4 -4
- package/dist/types/tools/subagent.d.ts +12 -12
- package/dist/types/tools/telegram-send.d.ts +7 -0
- package/dist/types/tools/todo-write.d.ts +8 -8
- package/dist/types/utils/external-editor.d.ts +2 -0
- package/dist/types/web/insane/bridge.d.ts +9 -9
- package/dist/types/web/search/index.d.ts +4 -4
- package/package.json +8 -8
- package/scripts/g004-tmux-smoke.ts +196 -36
- package/src/cli/daemon-cli.ts +19 -29
- package/src/cli/malloc-env-guard.test.ts +104 -0
- package/src/cli/malloc-env-guard.ts +149 -0
- package/src/cli/notify-cli.ts +78 -14
- package/src/cli/session-picker.ts +23 -40
- package/src/cli/update-cli.ts +56 -37
- package/src/cli.ts +51 -10
- package/src/commands/daemon.ts +19 -5
- package/src/commands/harness.ts +515 -47
- package/src/commands/launch.ts +11 -3
- package/src/commands/notify.ts +5 -1
- package/src/commands/session.ts +6 -1
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-profile-activation.ts +1 -1
- package/src/config/model-profiles.ts +14 -14
- package/src/config/settings-schema.ts +41 -1
- package/src/config/settings.ts +96 -45
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/model-preset.ts +100 -0
- package/src/coordinator-mcp/policy.ts +30 -0
- package/src/coordinator-mcp/server.ts +2958 -430
- package/src/coordinator-mcp/session-reaper.ts +129 -0
- package/src/daemon/control-types.ts +11 -0
- package/src/daemon/operator-contract.ts +121 -0
- package/src/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.ts +57 -17
- package/src/defaults/skc/extensions/grok-cli-vendor/src/payload/sanitize.ts +15 -2
- package/src/defaults/skc/extensions/grok-cli-vendor/src/provider/register.ts +5 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +10 -9
- package/src/internal-urls/local-protocol.ts +29 -10
- package/src/main.ts +301 -80
- package/src/modes/acp/acp-agent.ts +730 -100
- package/src/modes/acp/acp-mode.ts +29 -2
- package/src/modes/components/assistant-message.ts +32 -3
- package/src/modes/components/bash-execution.ts +43 -13
- package/src/modes/components/custom-editor.ts +5 -0
- package/src/modes/components/footer.ts +3 -3
- package/src/modes/components/hook-selector.ts +12 -0
- package/src/modes/components/irc-sidebar.ts +206 -0
- package/src/modes/components/pet-selector.ts +55 -0
- package/src/modes/components/sayknow-pet-widget.ts +360 -0
- package/src/modes/components/session-selector.ts +222 -221
- package/src/modes/components/settings-selector.ts +10 -0
- package/src/modes/components/status-line/segments.ts +14 -7
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +4 -171
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/components/user-message.ts +26 -9
- package/src/modes/controllers/command-controller.ts +28 -2
- package/src/modes/controllers/event-controller.ts +159 -36
- package/src/modes/controllers/extension-ui-controller.ts +37 -18
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/controllers/selector-controller.ts +62 -15
- package/src/modes/interactive-mode.ts +199 -7
- package/src/modes/irc-observation-ledger.ts +127 -0
- package/src/modes/print-mode.ts +228 -92
- package/src/modes/rpc/rpc-client.ts +58 -1
- package/src/modes/rpc/rpc-mode.ts +286 -85
- package/src/modes/rpc/rpc-types.ts +15 -1
- package/src/modes/shared/agent-wire/command-dispatch.ts +33 -1
- package/src/modes/shared/agent-wire/command-validation.ts +10 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +45 -2
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +288 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +130 -20
- package/src/modes/types.ts +34 -2
- package/src/modes/utils/context-usage.ts +56 -92
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/irc-message.ts +138 -0
- package/src/modes/utils/ui-helpers.ts +216 -50
- package/src/notifications/config.ts +44 -0
- package/src/notifications/daemon-paths.ts +8 -0
- package/src/notifications/index.ts +535 -75
- package/src/notifications/lifecycle-control-runtime.ts +930 -77
- package/src/notifications/notification-service.ts +710 -0
- package/src/notifications/rate-limit-pool.ts +46 -12
- package/src/notifications/telegram-daemon-control.ts +27 -2
- package/src/notifications/telegram-daemon.ts +635 -94
- package/src/notifications/telegram-reference.ts +73 -13
- package/src/notifications/topic-registry.ts +86 -9
- package/src/prompts/system/irc-peer-roster.md +1 -0
- package/src/prompts/system/subagent-system-prompt.md +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/irc.md +1 -1
- package/src/registry/agent-registry.ts +3 -0
- package/src/sdk.ts +52 -1
- package/src/secrets/obfuscator.ts +4 -2
- package/src/session/agent-session.ts +670 -217
- package/src/session/cache-economics.ts +275 -0
- package/src/session/context-estimation.ts +91 -0
- package/src/session/provider-safety-stop.ts +9 -0
- package/src/session/session-dump-format.ts +6 -0
- package/src/session/session-manager.ts +809 -39
- package/src/session/session-storage.ts +582 -38
- package/src/skc-runtime/session-state-sidecar.ts +756 -135
- package/src/skc-runtime/tmux-common.ts +26 -7
- package/src/skc-runtime/tmux-gc.ts +52 -9
- package/src/skc-runtime/tmux-owner-isolation-cli.ts +185 -0
- package/src/skc-runtime/tmux-owner-isolation.ts +2060 -0
- package/src/skc-runtime/tmux-sessions.ts +871 -68
- package/src/slash-commands/builtin-registry.ts +153 -9
- package/src/slash-commands/helpers/context-report.ts +26 -4
- package/src/task/executor.ts +12 -3
- package/src/task/index.ts +8 -0
- package/src/task/receipt.ts +7 -2
- package/src/task/types.ts +26 -0
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/browser/tab-supervisor.ts +63 -6
- package/src/tools/browser.ts +8 -3
- package/src/tools/index.ts +81 -6
- package/src/tools/sqlite-reader.ts +8 -4
- package/src/tui/output-block.ts +18 -6
- package/src/utils/external-editor.ts +6 -1
|
@@ -14,22 +14,38 @@ import {
|
|
|
14
14
|
import { formatBytes } from "@sayknow-cli/utils";
|
|
15
15
|
import { theme } from "../../modes/theme/theme";
|
|
16
16
|
import { matchesAppInterrupt } from "../../modes/utils/keybinding-matchers";
|
|
17
|
-
import type { SessionInfo } from "../../session/session-manager";
|
|
17
|
+
import type { ResumeSessionIdentity, ResumeTailInspection, SessionInfo } from "../../session/session-manager";
|
|
18
18
|
import { DynamicBorder } from "./dynamic-border";
|
|
19
19
|
import { HookSelectorComponent } from "./hook-selector";
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export interface SelectedSession {
|
|
22
|
+
kind: "selected";
|
|
23
|
+
path: string;
|
|
24
|
+
identity: ResumeSessionIdentity;
|
|
25
|
+
action: "continue-tail" | "open-idle";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CancelledSessionSelection {
|
|
29
|
+
kind: "cancelled";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type SessionSelectionResult = SelectedSession | CancelledSessionSelection;
|
|
33
|
+
export type SessionInspector = (sessionPath: string) => Promise<ResumeTailInspection>;
|
|
34
|
+
|
|
35
|
+
type SelectorState =
|
|
36
|
+
| { kind: "browsing" }
|
|
37
|
+
| { kind: "checking"; session: SessionInfo; token: number }
|
|
38
|
+
| { kind: "confirming"; session: SessionInfo; token: number; identity: ResumeSessionIdentity }
|
|
39
|
+
| { kind: "settled" };
|
|
40
|
+
|
|
24
41
|
class SessionList implements Component {
|
|
25
42
|
#filteredSessions: SessionInfo[] = [];
|
|
26
|
-
#selectedIndex
|
|
27
|
-
readonly #searchInput
|
|
28
|
-
|
|
43
|
+
#selectedIndex = 0;
|
|
44
|
+
readonly #searchInput = new Input();
|
|
45
|
+
#inputFrozen = false;
|
|
46
|
+
onSelect?: (session: SessionInfo) => void;
|
|
29
47
|
onCancel?: () => void;
|
|
30
48
|
onExit: () => void = () => {};
|
|
31
|
-
#maxVisible: number = 5; // Max sessions visible (each session is 3 lines: msg + metadata + blank)
|
|
32
|
-
|
|
33
49
|
onDeleteRequest?: (session: SessionInfo) => void;
|
|
34
50
|
|
|
35
51
|
constructor(
|
|
@@ -37,255 +53,173 @@ class SessionList implements Component {
|
|
|
37
53
|
private readonly showCwd = false,
|
|
38
54
|
) {
|
|
39
55
|
this.#filteredSessions = allSessions;
|
|
40
|
-
this.#searchInput = new Input();
|
|
41
|
-
|
|
42
|
-
// Handle Enter in search input - select current item
|
|
43
56
|
this.#searchInput.onSubmit = () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (this.onSelect) {
|
|
47
|
-
this.onSelect(selected.path);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
57
|
+
const selected = this.#filteredSessions[this.#selectedIndex];
|
|
58
|
+
if (selected) this.onSelect?.(selected);
|
|
50
59
|
};
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
setInputFrozen(frozen: boolean): void {
|
|
63
|
+
this.#inputFrozen = frozen;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
#clampSelectedIndex(): void {
|
|
54
|
-
|
|
55
|
-
this.#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.#selectedIndex = Math.max(0, Math.min(this.#selectedIndex, this.#filteredSessions.length - 1));
|
|
67
|
+
this.#selectedIndex =
|
|
68
|
+
this.#filteredSessions.length === 0
|
|
69
|
+
? 0
|
|
70
|
+
: Math.max(0, Math.min(this.#selectedIndex, this.#filteredSessions.length - 1));
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
#filterSessions(query: string): void {
|
|
62
|
-
this.#filteredSessions = fuzzyFilter(this.allSessions, query, session =>
|
|
63
|
-
|
|
74
|
+
this.#filteredSessions = fuzzyFilter(this.allSessions, query, session =>
|
|
75
|
+
[
|
|
64
76
|
session.id,
|
|
65
77
|
session.title ?? "",
|
|
66
78
|
session.cwd ?? "",
|
|
67
79
|
session.firstMessage ?? "",
|
|
68
80
|
session.allMessagesText,
|
|
69
81
|
session.path,
|
|
70
|
-
]
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
]
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.join(" "),
|
|
85
|
+
);
|
|
73
86
|
this.#clampSelectedIndex();
|
|
74
87
|
}
|
|
75
88
|
|
|
76
89
|
removeSession(sessionPath: string): void {
|
|
77
|
-
const index = this.allSessions.findIndex(
|
|
90
|
+
const index = this.allSessions.findIndex(session => session.path === sessionPath);
|
|
78
91
|
if (index === -1) return;
|
|
79
92
|
this.allSessions.splice(index, 1);
|
|
80
|
-
// Re-filter to update filteredSessions
|
|
81
93
|
this.#filterSessions(this.#searchInput.getValue());
|
|
82
94
|
}
|
|
83
95
|
|
|
84
|
-
invalidate(): void {
|
|
85
|
-
// No cached state to invalidate currently
|
|
86
|
-
}
|
|
96
|
+
invalidate(): void {}
|
|
87
97
|
|
|
88
98
|
render(width: number): string[] {
|
|
89
|
-
const lines
|
|
90
|
-
|
|
91
|
-
// Render search input
|
|
92
|
-
lines.push(...this.#searchInput.render(width));
|
|
93
|
-
lines.push(""); // Blank line after search
|
|
94
|
-
|
|
99
|
+
const lines = [...this.#searchInput.render(width), ""];
|
|
95
100
|
if (this.#filteredSessions.length === 0) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
|
|
101
|
+
lines.push(
|
|
102
|
+
truncateToWidth(
|
|
103
|
+
theme.fg(
|
|
104
|
+
"muted",
|
|
105
|
+
this.showCwd ? " No sessions found" : " No sessions in current folder. Press Tab to view all.",
|
|
106
|
+
),
|
|
107
|
+
width,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
105
110
|
return lines;
|
|
106
111
|
}
|
|
107
|
-
|
|
108
|
-
// Format dates
|
|
109
112
|
const formatDate = (date: Date): string => {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (diffMins < 60) return `${diffMins} minute${diffMins !== 1 ? "s" : ""} ago`;
|
|
118
|
-
if (diffHours < 24) return `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
|
|
119
|
-
if (diffDays === 1) return "1 day ago";
|
|
120
|
-
if (diffDays < 7) return `${diffDays} days ago`;
|
|
121
|
-
|
|
122
|
-
return date.toLocaleDateString();
|
|
113
|
+
const minutes = Math.floor((Date.now() - date.getTime()) / 60000);
|
|
114
|
+
if (minutes < 1) return "just now";
|
|
115
|
+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
|
|
116
|
+
const hours = Math.floor(minutes / 60);
|
|
117
|
+
if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
118
|
+
const days = Math.floor(hours / 24);
|
|
119
|
+
return days === 1 ? "1 day ago" : days < 7 ? `${days} days ago` : date.toLocaleDateString();
|
|
123
120
|
};
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const startIndex = Math.max(
|
|
121
|
+
const maxVisible = 5;
|
|
122
|
+
const start = Math.max(
|
|
127
123
|
0,
|
|
128
|
-
Math.min(
|
|
129
|
-
this.#selectedIndex - Math.floor(this.#maxVisible / 2),
|
|
130
|
-
this.#filteredSessions.length - this.#maxVisible,
|
|
131
|
-
),
|
|
124
|
+
Math.min(this.#selectedIndex - Math.floor(maxVisible / 2), this.#filteredSessions.length - maxVisible),
|
|
132
125
|
);
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Second line: dimmed first message preview
|
|
156
|
-
const truncatedPreview = truncateToWidth(normalizedMessage, maxWidth);
|
|
157
|
-
lines.push(` ${theme.fg("dim", truncatedPreview)}`);
|
|
158
|
-
} else {
|
|
159
|
-
// No title: show first message as main line
|
|
160
|
-
const truncatedMsg = truncateToWidth(normalizedMessage, maxWidth);
|
|
161
|
-
const messageLine = cursor + (isSelected ? theme.bold(truncatedMsg) : truncatedMsg);
|
|
162
|
-
lines.push(messageLine);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Metadata line: date + file size
|
|
166
|
-
const modified = formatDate(session.modified);
|
|
167
|
-
const metadata = ` ${modified} ${theme.sep.dot} ${formatBytes(session.size)}`;
|
|
168
|
-
const metadataLine = theme.fg("dim", truncateToWidth(metadata, width));
|
|
169
|
-
|
|
170
|
-
lines.push(metadataLine);
|
|
171
|
-
lines.push(""); // Blank line between sessions
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Add scroll indicator if needed
|
|
175
|
-
if (startIndex > 0 || endIndex < this.#filteredSessions.length) {
|
|
176
|
-
const scrollText = ` (${this.#selectedIndex + 1}/${this.#filteredSessions.length})`;
|
|
177
|
-
const scrollInfo = theme.fg("muted", truncateToWidth(scrollText, width));
|
|
178
|
-
lines.push(scrollInfo);
|
|
126
|
+
const end = Math.min(start + maxVisible, this.#filteredSessions.length);
|
|
127
|
+
for (let index = start; index < end; index++) {
|
|
128
|
+
const session = this.#filteredSessions[index];
|
|
129
|
+
const selected = index === this.#selectedIndex;
|
|
130
|
+
const cursor = `${theme.nav.cursor} `;
|
|
131
|
+
const cursorWidth = visibleWidth(cursor);
|
|
132
|
+
const prefix = selected ? theme.fg("accent", cursor) : padding(cursorWidth);
|
|
133
|
+
const message = session.firstMessage.replace(/\n/g, " ").trim();
|
|
134
|
+
const title = truncateToWidth(session.title ?? message, width - cursorWidth);
|
|
135
|
+
lines.push(prefix + (selected ? theme.bold(title) : title));
|
|
136
|
+
if (session.title) lines.push(` ${theme.fg("dim", truncateToWidth(message, width - cursorWidth))}`);
|
|
137
|
+
lines.push(
|
|
138
|
+
theme.fg(
|
|
139
|
+
"dim",
|
|
140
|
+
truncateToWidth(
|
|
141
|
+
` ${formatDate(session.modified)} ${theme.sep.dot} ${formatBytes(session.size)}`,
|
|
142
|
+
width,
|
|
143
|
+
),
|
|
144
|
+
),
|
|
145
|
+
);
|
|
146
|
+
lines.push("");
|
|
179
147
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
lines.push(
|
|
183
|
-
|
|
184
|
-
|
|
148
|
+
if (start > 0 || end < this.#filteredSessions.length)
|
|
149
|
+
lines.push(theme.fg("muted", ` (${this.#selectedIndex + 1}/${this.#filteredSessions.length})`));
|
|
150
|
+
lines.push(
|
|
151
|
+
"",
|
|
152
|
+
theme.fg("muted", " [Del to delete selected transcript/artifacts, Enter to select, Esc to cancel]"),
|
|
153
|
+
);
|
|
185
154
|
return lines;
|
|
186
155
|
}
|
|
187
156
|
|
|
188
157
|
handleInput(keyData: string): void {
|
|
189
|
-
|
|
158
|
+
if (matchesAppInterrupt(keyData)) {
|
|
159
|
+
this.onCancel?.();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (matchesKey(keyData, "ctrl+c")) {
|
|
163
|
+
this.onExit();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (this.#inputFrozen) return;
|
|
167
|
+
const move = (delta: number): void => {
|
|
190
168
|
if (this.#filteredSessions.length === 0) return;
|
|
191
169
|
this.#selectedIndex += delta;
|
|
192
170
|
this.#clampSelectedIndex();
|
|
193
171
|
};
|
|
194
|
-
|
|
195
|
-
// Delete key - request delete confirmation from parent
|
|
196
172
|
if (matchesKey(keyData, "delete")) {
|
|
197
173
|
const selected = this.#filteredSessions[this.#selectedIndex];
|
|
198
|
-
if (selected
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// Up arrow
|
|
205
|
-
if (matchesKey(keyData, "up")) {
|
|
206
|
-
moveSelection(-1);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
// Down arrow
|
|
210
|
-
if (matchesKey(keyData, "down")) {
|
|
211
|
-
moveSelection(1);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
// Page up - jump up by maxVisible items
|
|
215
|
-
if (matchesKey(keyData, "pageUp")) {
|
|
216
|
-
moveSelection(-this.#maxVisible);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
// Page down - jump down by maxVisible items
|
|
220
|
-
if (matchesKey(keyData, "pageDown")) {
|
|
221
|
-
moveSelection(this.#maxVisible);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
// Enter
|
|
225
|
-
if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
174
|
+
if (selected) this.onDeleteRequest?.(selected);
|
|
175
|
+
} else if (matchesKey(keyData, "up")) move(-1);
|
|
176
|
+
else if (matchesKey(keyData, "down")) move(1);
|
|
177
|
+
else if (matchesKey(keyData, "pageUp")) move(-5);
|
|
178
|
+
else if (matchesKey(keyData, "pageDown")) move(5);
|
|
179
|
+
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
226
180
|
const selected = this.#filteredSessions[this.#selectedIndex];
|
|
227
|
-
if (selected
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
// Escape - cancel
|
|
233
|
-
if (matchesAppInterrupt(keyData)) {
|
|
234
|
-
if (this.onCancel) {
|
|
235
|
-
this.onCancel();
|
|
236
|
-
}
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
// Ctrl+C - exit
|
|
240
|
-
if (matchesKey(keyData, "ctrl+c")) {
|
|
241
|
-
this.onExit();
|
|
242
|
-
return;
|
|
181
|
+
if (selected) this.onSelect?.(selected);
|
|
182
|
+
} else {
|
|
183
|
+
this.#searchInput.handleInput(keyData);
|
|
184
|
+
this.#filterSessions(this.#searchInput.getValue());
|
|
243
185
|
}
|
|
244
|
-
// Pass everything else to search input
|
|
245
|
-
this.#searchInput.handleInput(keyData);
|
|
246
|
-
this.#filterSessions(this.#searchInput.getValue());
|
|
247
186
|
}
|
|
248
187
|
}
|
|
249
188
|
|
|
250
|
-
/**
|
|
251
|
-
* Component that renders a session selector with optional confirmation dialog
|
|
252
|
-
*/
|
|
189
|
+
/** A one-shot resume consent selector. It never opens or mutates sessions. */
|
|
253
190
|
export class SessionSelectorComponent extends Container {
|
|
254
191
|
#sessionList: SessionList;
|
|
255
192
|
#confirmationDialog: HookSelectorComponent | null = null;
|
|
256
|
-
#messageContainer
|
|
257
|
-
#onDelete?: (session: SessionInfo) => Promise<boolean>;
|
|
193
|
+
#messageContainer = new Container();
|
|
258
194
|
#onRequestRender?: () => void;
|
|
195
|
+
#state: SelectorState = { kind: "browsing" };
|
|
196
|
+
#nextToken = 0;
|
|
259
197
|
|
|
260
198
|
constructor(
|
|
261
199
|
sessions: SessionInfo[],
|
|
262
|
-
onSelect: (sessionPath: string) => void,
|
|
263
|
-
onCancel: () => void,
|
|
264
|
-
onExit: () => void,
|
|
265
|
-
onDelete?: (session: SessionInfo) => Promise<boolean>,
|
|
200
|
+
private readonly onSelect: (sessionPath: string) => void,
|
|
201
|
+
private readonly onCancel: () => void,
|
|
202
|
+
private readonly onExit: () => void,
|
|
203
|
+
private readonly onDelete?: (session: SessionInfo) => Promise<boolean>,
|
|
204
|
+
private readonly inspector?: SessionInspector,
|
|
205
|
+
private readonly onSelection?: (selection: SessionSelectionResult) => void,
|
|
266
206
|
) {
|
|
267
207
|
super();
|
|
268
|
-
|
|
269
|
-
this.#messageContainer = new Container();
|
|
270
|
-
this.#onDelete = onDelete;
|
|
271
|
-
// Add header
|
|
272
208
|
this.addChild(new Spacer(1));
|
|
273
209
|
this.addChild(new Text(theme.bold("Resume Session"), 1, 0));
|
|
274
210
|
this.addChild(new Spacer(1));
|
|
275
211
|
this.addChild(new DynamicBorder());
|
|
276
212
|
this.addChild(new Spacer(1));
|
|
277
213
|
this.addChild(this.#messageContainer);
|
|
278
|
-
// Create session list
|
|
279
214
|
this.#sessionList = new SessionList(sessions);
|
|
280
|
-
this.#sessionList.onSelect =
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.#sessionList.onDeleteRequest = (session: SessionInfo) => {
|
|
284
|
-
this.#showDeleteConfirmation(session);
|
|
215
|
+
this.#sessionList.onSelect = session => {
|
|
216
|
+
if (this.inspector) void this.#inspect(session);
|
|
217
|
+
else this.onSelect(session.path);
|
|
285
218
|
};
|
|
219
|
+
this.#sessionList.onCancel = () => this.#cancel();
|
|
220
|
+
this.#sessionList.onExit = () => this.#exit();
|
|
221
|
+
this.#sessionList.onDeleteRequest = session => this.#showDeleteConfirmation(session);
|
|
286
222
|
this.addChild(this.#sessionList);
|
|
287
|
-
|
|
288
|
-
// Add bottom border
|
|
289
223
|
this.addChild(new Spacer(1));
|
|
290
224
|
this.addChild(new DynamicBorder());
|
|
291
225
|
}
|
|
@@ -293,60 +227,127 @@ export class SessionSelectorComponent extends Container {
|
|
|
293
227
|
setOnRequestRender(callback: () => void): void {
|
|
294
228
|
this.#onRequestRender = callback;
|
|
295
229
|
}
|
|
296
|
-
|
|
297
230
|
#clearError(): void {
|
|
298
231
|
this.#messageContainer.clear();
|
|
299
232
|
}
|
|
300
|
-
|
|
301
233
|
#showError(message: string): void {
|
|
302
234
|
this.#messageContainer.clear();
|
|
303
|
-
this.#messageContainer.addChild(
|
|
235
|
+
this.#messageContainer.addChild(
|
|
236
|
+
new Text(theme.fg("error", `Error: ${replaceTabs(message).slice(0, 200)}`), 1, 0),
|
|
237
|
+
);
|
|
304
238
|
this.#messageContainer.addChild(new Spacer(1));
|
|
305
239
|
}
|
|
240
|
+
#requestRender(): void {
|
|
241
|
+
this.#onRequestRender?.();
|
|
242
|
+
}
|
|
306
243
|
|
|
244
|
+
#settle(selection: SessionSelectionResult): void {
|
|
245
|
+
if (this.#state.kind === "settled") return;
|
|
246
|
+
this.#state = { kind: "settled" };
|
|
247
|
+
this.#sessionList.setInputFrozen(true);
|
|
248
|
+
const dialog = this.#confirmationDialog;
|
|
249
|
+
this.#confirmationDialog = null;
|
|
250
|
+
if (dialog) this.removeChild(dialog);
|
|
251
|
+
if (this.onSelection) this.onSelection(selection);
|
|
252
|
+
else if (selection.kind === "selected") this.onSelect(selection.path);
|
|
253
|
+
else this.onCancel();
|
|
254
|
+
}
|
|
255
|
+
#cancel(): void {
|
|
256
|
+
if (this.#state.kind === "settled") return;
|
|
257
|
+
if (this.#state.kind === "checking") this.#nextToken++;
|
|
258
|
+
this.#settle({ kind: "cancelled" });
|
|
259
|
+
}
|
|
260
|
+
#exit(): void {
|
|
261
|
+
if (this.#state.kind !== "settled") this.#nextToken++;
|
|
262
|
+
this.#state = { kind: "settled" };
|
|
263
|
+
this.#sessionList.setInputFrozen(true);
|
|
264
|
+
this.onExit();
|
|
265
|
+
}
|
|
266
|
+
async #inspect(session: SessionInfo): Promise<void> {
|
|
267
|
+
const inspector = this.inspector;
|
|
268
|
+
if (!inspector || this.#state.kind !== "browsing") return;
|
|
269
|
+
const token = ++this.#nextToken;
|
|
270
|
+
this.#state = { kind: "checking", session, token };
|
|
271
|
+
this.#sessionList.setInputFrozen(true);
|
|
272
|
+
this.#clearError();
|
|
273
|
+
this.#requestRender();
|
|
274
|
+
try {
|
|
275
|
+
const inspection = await inspector(session.path);
|
|
276
|
+
if (this.#state.kind !== "checking" || this.#state.token !== token) return;
|
|
277
|
+
if (inspection.kind === "error") {
|
|
278
|
+
this.#state = { kind: "browsing" };
|
|
279
|
+
this.#sessionList.setInputFrozen(false);
|
|
280
|
+
this.#showError(`Unable to inspect session (${inspection.reason}).`);
|
|
281
|
+
this.#requestRender();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (inspection.kind === "terminal") {
|
|
285
|
+
this.#settle({ kind: "selected", path: session.path, identity: inspection.identity, action: "open-idle" });
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
this.#showResumeConfirmation(session, token, inspection.identity);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (this.#state.kind !== "checking" || this.#state.token !== token) return;
|
|
291
|
+
this.#state = { kind: "browsing" };
|
|
292
|
+
this.#sessionList.setInputFrozen(false);
|
|
293
|
+
this.#showError(error instanceof Error ? error.message : String(error));
|
|
294
|
+
this.#requestRender();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
#showResumeConfirmation(session: SessionInfo, token: number, identity: ResumeSessionIdentity): void {
|
|
298
|
+
this.#state = { kind: "confirming", session, token, identity };
|
|
299
|
+
const dialog = new HookSelectorComponent(
|
|
300
|
+
"Resume this session?",
|
|
301
|
+
["Yes", "No"],
|
|
302
|
+
option => {
|
|
303
|
+
if (this.#confirmationDialog !== dialog || this.#state.kind !== "confirming") return;
|
|
304
|
+
if (option === "Yes")
|
|
305
|
+
this.#settle({ kind: "selected", path: session.path, identity, action: "continue-tail" });
|
|
306
|
+
else this.#settle({ kind: "cancelled" });
|
|
307
|
+
},
|
|
308
|
+
() => this.#settle({ kind: "cancelled" }),
|
|
309
|
+
{ acceleratorMap: { y: "Yes", n: "No" } },
|
|
310
|
+
);
|
|
311
|
+
this.#confirmationDialog = dialog;
|
|
312
|
+
this.addChild(dialog);
|
|
313
|
+
this.#requestRender();
|
|
314
|
+
}
|
|
307
315
|
#showDeleteConfirmation(session: SessionInfo): void {
|
|
316
|
+
if (this.#state.kind !== "browsing" || this.#confirmationDialog) return;
|
|
308
317
|
const displayName = session.title || session.firstMessage.slice(0, 40) || session.id;
|
|
309
|
-
|
|
318
|
+
let deleting = false;
|
|
319
|
+
const dialog = new HookSelectorComponent(
|
|
310
320
|
`Delete selected session transcript and artifacts?\n${displayName}\nThis cannot be undone. Other sessions and topic/history metadata are not deleted.`,
|
|
311
321
|
["Yes", "No"],
|
|
312
|
-
async
|
|
313
|
-
if (
|
|
322
|
+
async option => {
|
|
323
|
+
if (this.#confirmationDialog !== dialog || deleting) return;
|
|
324
|
+
if (option === "Yes" && this.onDelete) {
|
|
325
|
+
deleting = true;
|
|
314
326
|
this.#clearError();
|
|
315
327
|
try {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
} catch (err) {
|
|
321
|
-
this.#showError(err instanceof Error ? err.message : String(err));
|
|
328
|
+
if (await this.onDelete(session)) this.#sessionList.removeSession(session.path);
|
|
329
|
+
} catch (error) {
|
|
330
|
+
this.#showError(error instanceof Error ? error.message : String(error));
|
|
322
331
|
}
|
|
323
332
|
}
|
|
324
|
-
|
|
325
|
-
this.removeChild(this.#confirmationDialog!);
|
|
333
|
+
this.removeChild(dialog);
|
|
326
334
|
this.#confirmationDialog = null;
|
|
327
|
-
|
|
328
|
-
this.#onRequestRender?.();
|
|
335
|
+
this.#requestRender();
|
|
329
336
|
},
|
|
330
337
|
() => {
|
|
331
|
-
|
|
332
|
-
this.removeChild(
|
|
338
|
+
if (this.#confirmationDialog !== dialog || deleting) return;
|
|
339
|
+
this.removeChild(dialog);
|
|
333
340
|
this.#confirmationDialog = null;
|
|
334
|
-
|
|
335
|
-
this.#onRequestRender?.();
|
|
341
|
+
this.#requestRender();
|
|
336
342
|
},
|
|
337
343
|
);
|
|
338
|
-
|
|
339
|
-
this.addChild(
|
|
344
|
+
this.#confirmationDialog = dialog;
|
|
345
|
+
this.addChild(dialog);
|
|
340
346
|
}
|
|
341
|
-
|
|
342
347
|
handleInput(keyData: string): void {
|
|
343
|
-
if (this.#confirmationDialog)
|
|
344
|
-
|
|
345
|
-
} else {
|
|
346
|
-
this.#sessionList.handleInput(keyData);
|
|
347
|
-
}
|
|
348
|
+
if (this.#confirmationDialog) this.#confirmationDialog.handleInput(keyData);
|
|
349
|
+
else this.#sessionList.handleInput(keyData);
|
|
348
350
|
}
|
|
349
|
-
|
|
350
351
|
getSessionList(): SessionList {
|
|
351
352
|
return this.#sessionList;
|
|
352
353
|
}
|
|
@@ -694,6 +694,8 @@ export interface SettingsCallbacks {
|
|
|
694
694
|
onThemePreview?: (theme: string) => void | Promise<void>;
|
|
695
695
|
/** Called to restore the rendered theme when theme settings preview is cancelled */
|
|
696
696
|
onThemePreviewCancel?: (theme: string) => void | Promise<void>;
|
|
697
|
+
/** Called to live-preview the sayknow pet skin while browsing the pet setting. */
|
|
698
|
+
onPetPreview?: (mode: string) => void;
|
|
697
699
|
/** Called for status line preview while configuring */
|
|
698
700
|
onStatusLinePreview?: (settings: StatusLinePreviewSettings) => void;
|
|
699
701
|
/** Get current rendered status line for inline preview */
|
|
@@ -939,6 +941,14 @@ export class SettingsSelectorComponent extends Container {
|
|
|
939
941
|
});
|
|
940
942
|
this.#updateStatusPreview();
|
|
941
943
|
};
|
|
944
|
+
} else if (def.path === "pet.mode") {
|
|
945
|
+
const savedPetMode = currentValue;
|
|
946
|
+
onPreview = value => {
|
|
947
|
+
this.callbacks.onPetPreview?.(value);
|
|
948
|
+
};
|
|
949
|
+
onPreviewCancel = () => {
|
|
950
|
+
this.callbacks.onPetPreview?.(savedPetMode);
|
|
951
|
+
};
|
|
942
952
|
}
|
|
943
953
|
|
|
944
954
|
// Provide status line preview for theme selection
|
|
@@ -115,9 +115,13 @@ const modelSegment: StatusLineSegment = {
|
|
|
115
115
|
if (opts.showContextPercent !== false && ctx.contextPctSegmentActive !== true) {
|
|
116
116
|
const pct = ctx.contextPercent;
|
|
117
117
|
const window = ctx.contextWindow;
|
|
118
|
-
if (window > 0
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
if (window > 0) {
|
|
119
|
+
if (typeof pct === "number" && Number.isFinite(pct)) {
|
|
120
|
+
const color = getContextUsageThemeColor(getContextUsageLevel(pct, window));
|
|
121
|
+
content += `${theme.sep.dot}${theme.fg(color, `${pct.toFixed(1)}%`)}`;
|
|
122
|
+
} else {
|
|
123
|
+
content += `${theme.sep.dot}${theme.fg("statusLineContext", "?")}`;
|
|
124
|
+
}
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
|
|
@@ -383,11 +387,14 @@ const contextPctSegment: StatusLineSegment = {
|
|
|
383
387
|
render(ctx) {
|
|
384
388
|
const pct = ctx.contextPercent;
|
|
385
389
|
const window = ctx.contextWindow;
|
|
390
|
+
const knownPct = typeof pct === "number" && Number.isFinite(pct) ? pct : undefined;
|
|
386
391
|
|
|
387
392
|
const autoIcon = ctx.autoCompactEnabled && theme.icon.auto ? ` ${theme.icon.auto}` : "";
|
|
388
|
-
const text = `${
|
|
389
|
-
|
|
390
|
-
|
|
393
|
+
const text = `${knownPct === undefined ? "?" : `${knownPct.toFixed(1)}%`}/${formatNumber(window)}${autoIcon}`;
|
|
394
|
+
const color =
|
|
395
|
+
knownPct === undefined
|
|
396
|
+
? "statusLineContext"
|
|
397
|
+
: getContextUsageThemeColor(getContextUsageLevel(knownPct, window));
|
|
391
398
|
const content = withIcon(theme.icon.context, theme.fg(color, text));
|
|
392
399
|
|
|
393
400
|
return { content, visible: true };
|
|
@@ -399,7 +406,7 @@ const contextBarSegment: StatusLineSegment = {
|
|
|
399
406
|
render(ctx) {
|
|
400
407
|
const pct = ctx.contextPercent;
|
|
401
408
|
const window = ctx.contextWindow;
|
|
402
|
-
if (!window) return { content: "", visible: false };
|
|
409
|
+
if (!window || pct === null) return { content: "", visible: false };
|
|
403
410
|
const width = 10;
|
|
404
411
|
const filled = Math.min(width, Math.max(0, Math.round((pct / 100) * width)));
|
|
405
412
|
const bar = "█".repeat(filled) + "░".repeat(width - filled);
|
|
@@ -39,7 +39,7 @@ export interface SegmentContext {
|
|
|
39
39
|
cost: number;
|
|
40
40
|
tokensPerSecond: number | null;
|
|
41
41
|
};
|
|
42
|
-
contextPercent: number;
|
|
42
|
+
contextPercent: number | null;
|
|
43
43
|
contextWindow: number;
|
|
44
44
|
/**
|
|
45
45
|
* True when a standalone `context_pct` segment is also part of the active
|