@mariozechner/pi-coding-agent 0.59.0 → 0.61.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/CHANGELOG.md +71 -0
- package/README.md +15 -2
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +7 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +2 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +18 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +83 -8
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts +6 -7
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +8 -107
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +7 -3
- package/dist/core/exec.js.map +1 -1
- package/dist/core/export-html/template.css +43 -13
- package/dist/core/export-html/template.html +1 -0
- package/dist/core/export-html/template.js +107 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -4
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +6 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +62 -33
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +4 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +9 -2
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +85 -13
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.d.ts +270 -50
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +222 -134
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +23 -14
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +15 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +194 -15
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/bash.d.ts +8 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +77 -69
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +3 -2
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +37 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/index.d.ts +2 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +6 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +60 -24
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +4 -4
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +2 -2
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +2 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +8 -8
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +6 -6
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +9 -9
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +5 -5
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +8 -8
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +3 -36
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +5 -44
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +6 -6
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +12 -8
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +6 -6
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +4 -4
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +32 -35
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +7 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +51 -6
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +15 -15
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +6 -6
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +184 -87
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +49 -37
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +4 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/child-process.d.ts +11 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +78 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +11 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/docs/extensions.md +59 -8
- package/docs/keybindings.md +103 -112
- package/docs/providers.md +7 -0
- package/docs/rpc.md +4 -4
- package/docs/sdk.md +2 -2
- package/examples/extensions/antigravity-image-gen.ts +5 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/subagent/index.ts +7 -5
- package/examples/extensions/tool-override.ts +9 -7
- package/examples/extensions/truncated-tool.ts +6 -3
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -5
|
@@ -6,14 +6,15 @@ import * as crypto from "node:crypto";
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
|
|
9
|
+
import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
|
|
10
10
|
import { spawn, spawnSync } from "child_process";
|
|
11
|
-
import { APP_NAME, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
11
|
+
import { APP_NAME, getAgentDir, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
12
12
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
13
13
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
14
14
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
15
15
|
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
16
16
|
import { findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
|
|
17
|
+
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
17
18
|
import { SessionManager } from "../../core/session-manager.js";
|
|
18
19
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
19
20
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
@@ -34,7 +35,7 @@ import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
|
34
35
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
35
36
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
36
37
|
import { FooterComponent } from "./components/footer.js";
|
|
37
|
-
import {
|
|
38
|
+
import { keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
38
39
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
39
40
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
40
41
|
import { OAuthSelectorComponent } from "./components/oauth-selector.js";
|
|
@@ -64,6 +65,7 @@ export class InteractiveMode {
|
|
|
64
65
|
editorContainer;
|
|
65
66
|
footer;
|
|
66
67
|
footerDataProvider;
|
|
68
|
+
// Stored so the same manager can be injected into custom editors, selectors, and extension UI.
|
|
67
69
|
keybindings;
|
|
68
70
|
version;
|
|
69
71
|
isInitialized = false;
|
|
@@ -147,6 +149,7 @@ export class InteractiveMode {
|
|
|
147
149
|
this.widgetContainerAbove = new Container();
|
|
148
150
|
this.widgetContainerBelow = new Container();
|
|
149
151
|
this.keybindings = KeybindingsManager.create();
|
|
152
|
+
setKeybindings(this.keybindings);
|
|
150
153
|
const editorPaddingX = this.settingsManager.getEditorPaddingX();
|
|
151
154
|
const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
|
|
152
155
|
this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
|
|
@@ -241,27 +244,26 @@ export class InteractiveMode {
|
|
|
241
244
|
if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
|
|
242
245
|
const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
|
|
243
246
|
// Build startup instructions using keybinding hint helpers
|
|
244
|
-
const
|
|
245
|
-
const hint = (action, desc) => appKeyHint(kb, action, desc);
|
|
247
|
+
const hint = (keybinding, description) => keyHint(keybinding, description);
|
|
246
248
|
const instructions = [
|
|
247
|
-
hint("interrupt", "to interrupt"),
|
|
248
|
-
hint("clear", "to clear"),
|
|
249
|
-
rawKeyHint(`${
|
|
250
|
-
hint("exit", "to exit (empty)"),
|
|
251
|
-
hint("suspend", "to suspend"),
|
|
252
|
-
keyHint("deleteToLineEnd", "to delete to end"),
|
|
253
|
-
hint("
|
|
254
|
-
rawKeyHint(`${
|
|
255
|
-
hint("
|
|
256
|
-
hint("
|
|
257
|
-
hint("
|
|
258
|
-
hint("
|
|
249
|
+
hint("app.interrupt", "to interrupt"),
|
|
250
|
+
hint("app.clear", "to clear"),
|
|
251
|
+
rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
|
|
252
|
+
hint("app.exit", "to exit (empty)"),
|
|
253
|
+
hint("app.suspend", "to suspend"),
|
|
254
|
+
keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
|
|
255
|
+
hint("app.thinking.cycle", "to cycle thinking level"),
|
|
256
|
+
rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
|
|
257
|
+
hint("app.model.select", "to select model"),
|
|
258
|
+
hint("app.tools.expand", "to expand tools"),
|
|
259
|
+
hint("app.thinking.toggle", "to expand thinking"),
|
|
260
|
+
hint("app.editor.external", "for external editor"),
|
|
259
261
|
rawKeyHint("/", "for commands"),
|
|
260
262
|
rawKeyHint("!", "to run bash"),
|
|
261
263
|
rawKeyHint("!!", "to run bash (no context)"),
|
|
262
|
-
hint("followUp", "to queue follow-up"),
|
|
263
|
-
hint("dequeue", "to edit all queued messages"),
|
|
264
|
-
hint("pasteImage", "to paste image"),
|
|
264
|
+
hint("app.message.followUp", "to queue follow-up"),
|
|
265
|
+
hint("app.message.dequeue", "to edit all queued messages"),
|
|
266
|
+
hint("app.clipboard.pasteImage", "to paste image"),
|
|
265
267
|
rawKeyHint("drop files", "to attach"),
|
|
266
268
|
].join("\n");
|
|
267
269
|
this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
|
|
@@ -360,6 +362,12 @@ export class InteractiveMode {
|
|
|
360
362
|
this.showNewVersionNotification(newVersion);
|
|
361
363
|
}
|
|
362
364
|
});
|
|
365
|
+
// Start package update check asynchronously
|
|
366
|
+
this.checkForPackageUpdates().then((updates) => {
|
|
367
|
+
if (updates.length > 0) {
|
|
368
|
+
this.showPackageUpdateNotification(updates);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
363
371
|
// Check tmux keyboard setup asynchronously
|
|
364
372
|
this.checkTmuxKeyboardSetup().then((warning) => {
|
|
365
373
|
if (warning) {
|
|
@@ -434,6 +442,23 @@ export class InteractiveMode {
|
|
|
434
442
|
return undefined;
|
|
435
443
|
}
|
|
436
444
|
}
|
|
445
|
+
async checkForPackageUpdates() {
|
|
446
|
+
if (process.env.PI_OFFLINE) {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
const packageManager = new DefaultPackageManager({
|
|
451
|
+
cwd: process.cwd(),
|
|
452
|
+
agentDir: getAgentDir(),
|
|
453
|
+
settingsManager: this.settingsManager,
|
|
454
|
+
});
|
|
455
|
+
const updates = await packageManager.checkForAvailableUpdates();
|
|
456
|
+
return updates.map((update) => update.displayName);
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
437
462
|
async checkTmuxKeyboardSetup() {
|
|
438
463
|
if (!process.env.TMUX)
|
|
439
464
|
return undefined;
|
|
@@ -464,6 +489,9 @@ export class InteractiveMode {
|
|
|
464
489
|
runTmuxShow("extended-keys"),
|
|
465
490
|
runTmuxShow("extended-keys-format"),
|
|
466
491
|
]);
|
|
492
|
+
// If we couldn't query tmux (timeout, sandbox, etc.), don't warn
|
|
493
|
+
if (extendedKeys === undefined)
|
|
494
|
+
return undefined;
|
|
467
495
|
if (extendedKeys !== "on" && extendedKeys !== "always") {
|
|
468
496
|
return "tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";
|
|
469
497
|
}
|
|
@@ -1022,7 +1050,7 @@ export class InteractiveMode {
|
|
|
1022
1050
|
this.defaultEditor.onExtensionShortcut = undefined;
|
|
1023
1051
|
this.updateTerminalTitle();
|
|
1024
1052
|
if (this.loadingAnimation) {
|
|
1025
|
-
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${
|
|
1053
|
+
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
|
|
1026
1054
|
}
|
|
1027
1055
|
}
|
|
1028
1056
|
// Maximum total widget lines to prevent viewport overflow
|
|
@@ -1145,7 +1173,7 @@ export class InteractiveMode {
|
|
|
1145
1173
|
this.loadingAnimation.setMessage(message);
|
|
1146
1174
|
}
|
|
1147
1175
|
else {
|
|
1148
|
-
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${
|
|
1176
|
+
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
|
|
1149
1177
|
}
|
|
1150
1178
|
}
|
|
1151
1179
|
else {
|
|
@@ -1503,24 +1531,24 @@ export class InteractiveMode {
|
|
|
1503
1531
|
}
|
|
1504
1532
|
};
|
|
1505
1533
|
// Register app action handlers
|
|
1506
|
-
this.defaultEditor.onAction("clear", () => this.handleCtrlC());
|
|
1534
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
1507
1535
|
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
1508
|
-
this.defaultEditor.onAction("suspend", () => this.handleCtrlZ());
|
|
1509
|
-
this.defaultEditor.onAction("
|
|
1510
|
-
this.defaultEditor.onAction("
|
|
1511
|
-
this.defaultEditor.onAction("
|
|
1536
|
+
this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
|
|
1537
|
+
this.defaultEditor.onAction("app.thinking.cycle", () => this.cycleThinkingLevel());
|
|
1538
|
+
this.defaultEditor.onAction("app.model.cycleForward", () => this.cycleModel("forward"));
|
|
1539
|
+
this.defaultEditor.onAction("app.model.cycleBackward", () => this.cycleModel("backward"));
|
|
1512
1540
|
// Global debug handler on TUI (works regardless of focus)
|
|
1513
1541
|
this.ui.onDebug = () => this.handleDebugCommand();
|
|
1514
|
-
this.defaultEditor.onAction("
|
|
1515
|
-
this.defaultEditor.onAction("
|
|
1516
|
-
this.defaultEditor.onAction("
|
|
1517
|
-
this.defaultEditor.onAction("
|
|
1518
|
-
this.defaultEditor.onAction("followUp", () => this.handleFollowUp());
|
|
1519
|
-
this.defaultEditor.onAction("dequeue", () => this.handleDequeue());
|
|
1520
|
-
this.defaultEditor.onAction("
|
|
1521
|
-
this.defaultEditor.onAction("tree", () => this.showTreeSelector());
|
|
1522
|
-
this.defaultEditor.onAction("fork", () => this.showUserMessageSelector());
|
|
1523
|
-
this.defaultEditor.onAction("resume", () => this.showSessionSelector());
|
|
1542
|
+
this.defaultEditor.onAction("app.model.select", () => this.showModelSelector());
|
|
1543
|
+
this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
|
|
1544
|
+
this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
|
|
1545
|
+
this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
|
|
1546
|
+
this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
|
|
1547
|
+
this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
|
|
1548
|
+
this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
|
|
1549
|
+
this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
|
|
1550
|
+
this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
|
|
1551
|
+
this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
|
|
1524
1552
|
this.defaultEditor.onChange = (text) => {
|
|
1525
1553
|
const wasBashMode = this.isBashMode;
|
|
1526
1554
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
@@ -1580,13 +1608,18 @@ export class InteractiveMode {
|
|
|
1580
1608
|
this.editor.setText("");
|
|
1581
1609
|
return;
|
|
1582
1610
|
}
|
|
1611
|
+
if (text.startsWith("/import")) {
|
|
1612
|
+
await this.handleImportCommand(text);
|
|
1613
|
+
this.editor.setText("");
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1583
1616
|
if (text === "/share") {
|
|
1584
1617
|
await this.handleShareCommand();
|
|
1585
1618
|
this.editor.setText("");
|
|
1586
1619
|
return;
|
|
1587
1620
|
}
|
|
1588
1621
|
if (text === "/copy") {
|
|
1589
|
-
this.handleCopyCommand();
|
|
1622
|
+
await this.handleCopyCommand();
|
|
1590
1623
|
this.editor.setText("");
|
|
1591
1624
|
return;
|
|
1592
1625
|
}
|
|
@@ -1834,15 +1867,17 @@ export class InteractiveMode {
|
|
|
1834
1867
|
this.ui.requestRender();
|
|
1835
1868
|
break;
|
|
1836
1869
|
case "tool_execution_start": {
|
|
1837
|
-
|
|
1838
|
-
|
|
1870
|
+
let component = this.pendingTools.get(event.toolCallId);
|
|
1871
|
+
if (!component) {
|
|
1872
|
+
component = new ToolExecutionComponent(event.toolName, event.args, {
|
|
1839
1873
|
showImages: this.settingsManager.getShowImages(),
|
|
1840
1874
|
}, this.getRegisteredToolDefinition(event.toolName), this.ui);
|
|
1841
1875
|
component.setExpanded(this.toolOutputExpanded);
|
|
1842
1876
|
this.chatContainer.addChild(component);
|
|
1843
1877
|
this.pendingTools.set(event.toolCallId, component);
|
|
1844
|
-
this.ui.requestRender();
|
|
1845
1878
|
}
|
|
1879
|
+
component.markExecutionStarted();
|
|
1880
|
+
this.ui.requestRender();
|
|
1846
1881
|
break;
|
|
1847
1882
|
}
|
|
1848
1883
|
case "tool_execution_update": {
|
|
@@ -1887,7 +1922,7 @@ export class InteractiveMode {
|
|
|
1887
1922
|
// Show compacting indicator with reason
|
|
1888
1923
|
this.statusContainer.clear();
|
|
1889
1924
|
const reasonText = event.reason === "overflow" ? "Context overflow detected, " : "";
|
|
1890
|
-
this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `${reasonText}Auto-compacting... (${
|
|
1925
|
+
this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `${reasonText}Auto-compacting... (${keyText("app.interrupt")} to cancel)`);
|
|
1891
1926
|
this.statusContainer.addChild(this.autoCompactionLoader);
|
|
1892
1927
|
this.ui.requestRender();
|
|
1893
1928
|
break;
|
|
@@ -1939,7 +1974,7 @@ export class InteractiveMode {
|
|
|
1939
1974
|
// Show retry indicator
|
|
1940
1975
|
this.statusContainer.clear();
|
|
1941
1976
|
const delaySeconds = Math.round(event.delayMs / 1000);
|
|
1942
|
-
this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (${
|
|
1977
|
+
this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (${keyText("app.interrupt")} to cancel)`);
|
|
1943
1978
|
this.statusContainer.addChild(this.retryLoader);
|
|
1944
1979
|
this.ui.requestRender();
|
|
1945
1980
|
break;
|
|
@@ -2397,6 +2432,16 @@ export class InteractiveMode {
|
|
|
2397
2432
|
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2398
2433
|
this.ui.requestRender();
|
|
2399
2434
|
}
|
|
2435
|
+
showPackageUpdateNotification(packages) {
|
|
2436
|
+
const action = theme.fg("accent", `${APP_NAME} update`);
|
|
2437
|
+
const updateInstruction = theme.fg("muted", "Package updates are available. Run ") + action;
|
|
2438
|
+
const packageLines = packages.map((pkg) => `- ${pkg}`).join("\n");
|
|
2439
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2440
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2441
|
+
this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n${updateInstruction}\n${theme.fg("muted", "Packages:")}\n${packageLines}`, 1, 0));
|
|
2442
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2443
|
+
this.ui.requestRender();
|
|
2444
|
+
}
|
|
2400
2445
|
/**
|
|
2401
2446
|
* Get all queued messages (read-only).
|
|
2402
2447
|
* Combines session queue and compaction queue.
|
|
@@ -2444,7 +2489,7 @@ export class InteractiveMode {
|
|
|
2444
2489
|
const text = theme.fg("dim", `Follow-up: ${message}`);
|
|
2445
2490
|
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
|
|
2446
2491
|
}
|
|
2447
|
-
const dequeueHint = this.getAppKeyDisplay("dequeue");
|
|
2492
|
+
const dequeueHint = this.getAppKeyDisplay("app.message.dequeue");
|
|
2448
2493
|
const hintText = theme.fg("dim", `↳ ${dequeueHint} to edit all queued messages`);
|
|
2449
2494
|
this.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
|
|
2450
2495
|
}
|
|
@@ -2966,7 +3011,7 @@ export class InteractiveMode {
|
|
|
2966
3011
|
this.session.abortBranchSummary();
|
|
2967
3012
|
};
|
|
2968
3013
|
this.chatContainer.addChild(new Spacer(1));
|
|
2969
|
-
summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${
|
|
3014
|
+
summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${keyText("app.interrupt")} to cancel)`);
|
|
2970
3015
|
this.statusContainer.addChild(summaryLoader);
|
|
2971
3016
|
this.ui.requestRender();
|
|
2972
3017
|
}
|
|
@@ -3186,7 +3231,7 @@ export class InteractiveMode {
|
|
|
3186
3231
|
return;
|
|
3187
3232
|
}
|
|
3188
3233
|
this.resetExtensionUI();
|
|
3189
|
-
const loader = new BorderedLoader(this.ui, theme, "Reloading extensions, skills, prompts, themes...", {
|
|
3234
|
+
const loader = new BorderedLoader(this.ui, theme, "Reloading keybindings, extensions, skills, prompts, themes...", {
|
|
3190
3235
|
cancellable: false,
|
|
3191
3236
|
});
|
|
3192
3237
|
const previousEditor = this.editor;
|
|
@@ -3203,6 +3248,7 @@ export class InteractiveMode {
|
|
|
3203
3248
|
};
|
|
3204
3249
|
try {
|
|
3205
3250
|
await this.session.reload();
|
|
3251
|
+
this.keybindings.reload();
|
|
3206
3252
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
3207
3253
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
3208
3254
|
const themeName = this.settingsManager.getTheme();
|
|
@@ -3236,7 +3282,7 @@ export class InteractiveMode {
|
|
|
3236
3282
|
if (modelsJsonError) {
|
|
3237
3283
|
this.showError(`models.json error: ${modelsJsonError}`);
|
|
3238
3284
|
}
|
|
3239
|
-
this.showStatus("Reloaded extensions, skills, prompts, themes");
|
|
3285
|
+
this.showStatus("Reloaded keybindings, extensions, skills, prompts, themes");
|
|
3240
3286
|
}
|
|
3241
3287
|
catch (error) {
|
|
3242
3288
|
dismissLoader(previousEditor);
|
|
@@ -3247,13 +3293,58 @@ export class InteractiveMode {
|
|
|
3247
3293
|
const parts = text.split(/\s+/);
|
|
3248
3294
|
const outputPath = parts.length > 1 ? parts[1] : undefined;
|
|
3249
3295
|
try {
|
|
3250
|
-
|
|
3251
|
-
|
|
3296
|
+
if (outputPath?.endsWith(".jsonl")) {
|
|
3297
|
+
const filePath = this.session.exportToJsonl(outputPath);
|
|
3298
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3299
|
+
}
|
|
3300
|
+
else {
|
|
3301
|
+
const filePath = await this.session.exportToHtml(outputPath);
|
|
3302
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3303
|
+
}
|
|
3252
3304
|
}
|
|
3253
3305
|
catch (error) {
|
|
3254
3306
|
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3255
3307
|
}
|
|
3256
3308
|
}
|
|
3309
|
+
async handleImportCommand(text) {
|
|
3310
|
+
const parts = text.split(/\s+/);
|
|
3311
|
+
if (parts.length < 2 || !parts[1]) {
|
|
3312
|
+
this.showError("Usage: /import <path.jsonl>");
|
|
3313
|
+
return;
|
|
3314
|
+
}
|
|
3315
|
+
const inputPath = parts[1];
|
|
3316
|
+
const confirmed = await this.showExtensionConfirm("Import session", `Replace current session with ${inputPath}?`);
|
|
3317
|
+
if (!confirmed) {
|
|
3318
|
+
this.showStatus("Import cancelled");
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
try {
|
|
3322
|
+
// Stop loading animation
|
|
3323
|
+
if (this.loadingAnimation) {
|
|
3324
|
+
this.loadingAnimation.stop();
|
|
3325
|
+
this.loadingAnimation = undefined;
|
|
3326
|
+
}
|
|
3327
|
+
this.statusContainer.clear();
|
|
3328
|
+
// Clear UI state
|
|
3329
|
+
this.pendingMessagesContainer.clear();
|
|
3330
|
+
this.compactionQueuedMessages = [];
|
|
3331
|
+
this.streamingComponent = undefined;
|
|
3332
|
+
this.streamingMessage = undefined;
|
|
3333
|
+
this.pendingTools.clear();
|
|
3334
|
+
const success = await this.session.importFromJsonl(inputPath);
|
|
3335
|
+
if (!success) {
|
|
3336
|
+
this.showWarning("Import cancelled");
|
|
3337
|
+
return;
|
|
3338
|
+
}
|
|
3339
|
+
// Clear and re-render the chat
|
|
3340
|
+
this.chatContainer.clear();
|
|
3341
|
+
this.renderInitialMessages();
|
|
3342
|
+
this.showStatus(`Session imported from: ${inputPath}`);
|
|
3343
|
+
}
|
|
3344
|
+
catch (error) {
|
|
3345
|
+
this.showError(`Failed to import session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3257
3348
|
async handleShareCommand() {
|
|
3258
3349
|
// Check if gh is available and logged in
|
|
3259
3350
|
try {
|
|
@@ -3341,14 +3432,14 @@ export class InteractiveMode {
|
|
|
3341
3432
|
}
|
|
3342
3433
|
}
|
|
3343
3434
|
}
|
|
3344
|
-
handleCopyCommand() {
|
|
3435
|
+
async handleCopyCommand() {
|
|
3345
3436
|
const text = this.session.getLastAssistantText();
|
|
3346
3437
|
if (!text) {
|
|
3347
3438
|
this.showError("No agent messages to copy yet.");
|
|
3348
3439
|
return;
|
|
3349
3440
|
}
|
|
3350
3441
|
try {
|
|
3351
|
-
copyToClipboard(text);
|
|
3442
|
+
await copyToClipboard(text);
|
|
3352
3443
|
this.showStatus("Copied last agent message to clipboard");
|
|
3353
3444
|
}
|
|
3354
3445
|
catch (error) {
|
|
@@ -3441,53 +3532,59 @@ export class InteractiveMode {
|
|
|
3441
3532
|
* Get capitalized display string for an app keybinding action.
|
|
3442
3533
|
*/
|
|
3443
3534
|
getAppKeyDisplay(action) {
|
|
3444
|
-
return this.capitalizeKey(
|
|
3535
|
+
return this.capitalizeKey(keyText(action));
|
|
3445
3536
|
}
|
|
3446
3537
|
/**
|
|
3447
3538
|
* Get capitalized display string for an editor keybinding action.
|
|
3448
3539
|
*/
|
|
3449
3540
|
getEditorKeyDisplay(action) {
|
|
3450
|
-
return this.capitalizeKey(
|
|
3541
|
+
return this.capitalizeKey(keyText(action));
|
|
3451
3542
|
}
|
|
3452
3543
|
handleHotkeysCommand() {
|
|
3453
3544
|
// Navigation keybindings
|
|
3454
|
-
const
|
|
3455
|
-
const
|
|
3456
|
-
const
|
|
3457
|
-
const
|
|
3458
|
-
const
|
|
3459
|
-
const
|
|
3460
|
-
const
|
|
3461
|
-
const
|
|
3545
|
+
const cursorUp = this.getEditorKeyDisplay("tui.editor.cursorUp");
|
|
3546
|
+
const cursorDown = this.getEditorKeyDisplay("tui.editor.cursorDown");
|
|
3547
|
+
const cursorLeft = this.getEditorKeyDisplay("tui.editor.cursorLeft");
|
|
3548
|
+
const cursorRight = this.getEditorKeyDisplay("tui.editor.cursorRight");
|
|
3549
|
+
const cursorWordLeft = this.getEditorKeyDisplay("tui.editor.cursorWordLeft");
|
|
3550
|
+
const cursorWordRight = this.getEditorKeyDisplay("tui.editor.cursorWordRight");
|
|
3551
|
+
const cursorLineStart = this.getEditorKeyDisplay("tui.editor.cursorLineStart");
|
|
3552
|
+
const cursorLineEnd = this.getEditorKeyDisplay("tui.editor.cursorLineEnd");
|
|
3553
|
+
const jumpForward = this.getEditorKeyDisplay("tui.editor.jumpForward");
|
|
3554
|
+
const jumpBackward = this.getEditorKeyDisplay("tui.editor.jumpBackward");
|
|
3555
|
+
const pageUp = this.getEditorKeyDisplay("tui.editor.pageUp");
|
|
3556
|
+
const pageDown = this.getEditorKeyDisplay("tui.editor.pageDown");
|
|
3462
3557
|
// Editing keybindings
|
|
3463
|
-
const submit = this.getEditorKeyDisplay("submit");
|
|
3464
|
-
const newLine = this.getEditorKeyDisplay("newLine");
|
|
3465
|
-
const deleteWordBackward = this.getEditorKeyDisplay("deleteWordBackward");
|
|
3466
|
-
const deleteWordForward = this.getEditorKeyDisplay("deleteWordForward");
|
|
3467
|
-
const deleteToLineStart = this.getEditorKeyDisplay("deleteToLineStart");
|
|
3468
|
-
const deleteToLineEnd = this.getEditorKeyDisplay("deleteToLineEnd");
|
|
3469
|
-
const yank = this.getEditorKeyDisplay("yank");
|
|
3470
|
-
const yankPop = this.getEditorKeyDisplay("yankPop");
|
|
3471
|
-
const undo = this.getEditorKeyDisplay("undo");
|
|
3472
|
-
const tab = this.getEditorKeyDisplay("tab");
|
|
3558
|
+
const submit = this.getEditorKeyDisplay("tui.input.submit");
|
|
3559
|
+
const newLine = this.getEditorKeyDisplay("tui.input.newLine");
|
|
3560
|
+
const deleteWordBackward = this.getEditorKeyDisplay("tui.editor.deleteWordBackward");
|
|
3561
|
+
const deleteWordForward = this.getEditorKeyDisplay("tui.editor.deleteWordForward");
|
|
3562
|
+
const deleteToLineStart = this.getEditorKeyDisplay("tui.editor.deleteToLineStart");
|
|
3563
|
+
const deleteToLineEnd = this.getEditorKeyDisplay("tui.editor.deleteToLineEnd");
|
|
3564
|
+
const yank = this.getEditorKeyDisplay("tui.editor.yank");
|
|
3565
|
+
const yankPop = this.getEditorKeyDisplay("tui.editor.yankPop");
|
|
3566
|
+
const undo = this.getEditorKeyDisplay("tui.editor.undo");
|
|
3567
|
+
const tab = this.getEditorKeyDisplay("tui.input.tab");
|
|
3473
3568
|
// App keybindings
|
|
3474
|
-
const interrupt = this.getAppKeyDisplay("interrupt");
|
|
3475
|
-
const clear = this.getAppKeyDisplay("clear");
|
|
3476
|
-
const exit = this.getAppKeyDisplay("exit");
|
|
3477
|
-
const suspend = this.getAppKeyDisplay("suspend");
|
|
3478
|
-
const cycleThinkingLevel = this.getAppKeyDisplay("
|
|
3479
|
-
const cycleModelForward = this.getAppKeyDisplay("
|
|
3480
|
-
const selectModel = this.getAppKeyDisplay("
|
|
3481
|
-
const expandTools = this.getAppKeyDisplay("
|
|
3482
|
-
const toggleThinking = this.getAppKeyDisplay("
|
|
3483
|
-
const externalEditor = this.getAppKeyDisplay("
|
|
3484
|
-
const
|
|
3485
|
-
const
|
|
3569
|
+
const interrupt = this.getAppKeyDisplay("app.interrupt");
|
|
3570
|
+
const clear = this.getAppKeyDisplay("app.clear");
|
|
3571
|
+
const exit = this.getAppKeyDisplay("app.exit");
|
|
3572
|
+
const suspend = this.getAppKeyDisplay("app.suspend");
|
|
3573
|
+
const cycleThinkingLevel = this.getAppKeyDisplay("app.thinking.cycle");
|
|
3574
|
+
const cycleModelForward = this.getAppKeyDisplay("app.model.cycleForward");
|
|
3575
|
+
const selectModel = this.getAppKeyDisplay("app.model.select");
|
|
3576
|
+
const expandTools = this.getAppKeyDisplay("app.tools.expand");
|
|
3577
|
+
const toggleThinking = this.getAppKeyDisplay("app.thinking.toggle");
|
|
3578
|
+
const externalEditor = this.getAppKeyDisplay("app.editor.external");
|
|
3579
|
+
const cycleModelBackward = this.getAppKeyDisplay("app.model.cycleBackward");
|
|
3580
|
+
const followUp = this.getAppKeyDisplay("app.message.followUp");
|
|
3581
|
+
const dequeue = this.getAppKeyDisplay("app.message.dequeue");
|
|
3582
|
+
const pasteImage = this.getAppKeyDisplay("app.clipboard.pasteImage");
|
|
3486
3583
|
let hotkeys = `
|
|
3487
3584
|
**Navigation**
|
|
3488
3585
|
| Key | Action |
|
|
3489
3586
|
|-----|--------|
|
|
3490
|
-
| \`
|
|
3587
|
+
| \`${cursorUp}\` / \`${cursorDown}\` / \`${cursorLeft}\` / \`${cursorRight}\` | Move cursor / browse history (Up when empty) |
|
|
3491
3588
|
| \`${cursorWordLeft}\` / \`${cursorWordRight}\` | Move by word |
|
|
3492
3589
|
| \`${cursorLineStart}\` | Start of line |
|
|
3493
3590
|
| \`${cursorLineEnd}\` | End of line |
|
|
@@ -3517,14 +3614,14 @@ export class InteractiveMode {
|
|
|
3517
3614
|
| \`${exit}\` | Exit (when editor is empty) |
|
|
3518
3615
|
| \`${suspend}\` | Suspend to background |
|
|
3519
3616
|
| \`${cycleThinkingLevel}\` | Cycle thinking level |
|
|
3520
|
-
| \`${cycleModelForward}\` | Cycle models |
|
|
3617
|
+
| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
|
|
3521
3618
|
| \`${selectModel}\` | Open model selector |
|
|
3522
3619
|
| \`${expandTools}\` | Toggle tool output expansion |
|
|
3523
3620
|
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
3524
3621
|
| \`${externalEditor}\` | Edit message in external editor |
|
|
3525
3622
|
| \`${followUp}\` | Queue follow-up message |
|
|
3526
3623
|
| \`${dequeue}\` | Restore queued messages |
|
|
3527
|
-
| \`
|
|
3624
|
+
| \`${pasteImage}\` | Paste image from clipboard |
|
|
3528
3625
|
| \`/\` | Slash commands |
|
|
3529
3626
|
| \`!\` | Run bash command |
|
|
3530
3627
|
| \`!!\` | Run bash command (excluded from context) |
|
|
@@ -3707,7 +3804,7 @@ export class InteractiveMode {
|
|
|
3707
3804
|
};
|
|
3708
3805
|
// Show compacting status
|
|
3709
3806
|
this.chatContainer.addChild(new Spacer(1));
|
|
3710
|
-
const cancelHint = `(${
|
|
3807
|
+
const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
|
|
3711
3808
|
const label = isAuto ? `Auto-compacting context... ${cancelHint}` : `Compacting context... ${cancelHint}`;
|
|
3712
3809
|
const compactingLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
|
|
3713
3810
|
this.statusContainer.addChild(compactingLoader);
|