@mariozechner/pi-coding-agent 0.60.0 → 0.61.1
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 +63 -0
- package/README.md +11 -1
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +1 -0
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/register-bedrock.d.ts.map +1 -1
- package/dist/bun/register-bedrock.js +3 -84
- package/dist/bun/register-bedrock.js.map +1 -1
- 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 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +17 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +61 -6
- package/dist/core/agent-session.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 +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +45 -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/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +268 -51
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +221 -143
- 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/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +4 -4
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +2 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +42 -1
- 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 +4 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +5 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +12 -10
- 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 +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- 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 +10 -5
- 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 +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +156 -91
- 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 +44 -7
- package/docs/keybindings.md +101 -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 +28 -6
- 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 +4 -4
|
@@ -6,7 +6,7 @@ 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
11
|
import { APP_NAME, getAgentDir, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
12
12
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
@@ -35,7 +35,7 @@ import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
|
35
35
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
36
36
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
37
37
|
import { FooterComponent } from "./components/footer.js";
|
|
38
|
-
import {
|
|
38
|
+
import { keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
39
39
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
40
40
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
41
41
|
import { OAuthSelectorComponent } from "./components/oauth-selector.js";
|
|
@@ -65,6 +65,7 @@ export class InteractiveMode {
|
|
|
65
65
|
editorContainer;
|
|
66
66
|
footer;
|
|
67
67
|
footerDataProvider;
|
|
68
|
+
// Stored so the same manager can be injected into custom editors, selectors, and extension UI.
|
|
68
69
|
keybindings;
|
|
69
70
|
version;
|
|
70
71
|
isInitialized = false;
|
|
@@ -148,6 +149,7 @@ export class InteractiveMode {
|
|
|
148
149
|
this.widgetContainerAbove = new Container();
|
|
149
150
|
this.widgetContainerBelow = new Container();
|
|
150
151
|
this.keybindings = KeybindingsManager.create();
|
|
152
|
+
setKeybindings(this.keybindings);
|
|
151
153
|
const editorPaddingX = this.settingsManager.getEditorPaddingX();
|
|
152
154
|
const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
|
|
153
155
|
this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
|
|
@@ -242,27 +244,26 @@ export class InteractiveMode {
|
|
|
242
244
|
if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
|
|
243
245
|
const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
|
|
244
246
|
// Build startup instructions using keybinding hint helpers
|
|
245
|
-
const
|
|
246
|
-
const hint = (action, desc) => appKeyHint(kb, action, desc);
|
|
247
|
+
const hint = (keybinding, description) => keyHint(keybinding, description);
|
|
247
248
|
const instructions = [
|
|
248
|
-
hint("interrupt", "to interrupt"),
|
|
249
|
-
hint("clear", "to clear"),
|
|
250
|
-
rawKeyHint(`${
|
|
251
|
-
hint("exit", "to exit (empty)"),
|
|
252
|
-
hint("suspend", "to suspend"),
|
|
253
|
-
keyHint("deleteToLineEnd", "to delete to end"),
|
|
254
|
-
hint("
|
|
255
|
-
rawKeyHint(`${
|
|
256
|
-
hint("
|
|
257
|
-
hint("
|
|
258
|
-
hint("
|
|
259
|
-
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"),
|
|
260
261
|
rawKeyHint("/", "for commands"),
|
|
261
262
|
rawKeyHint("!", "to run bash"),
|
|
262
263
|
rawKeyHint("!!", "to run bash (no context)"),
|
|
263
|
-
hint("followUp", "to queue follow-up"),
|
|
264
|
-
hint("dequeue", "to edit all queued messages"),
|
|
265
|
-
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"),
|
|
266
267
|
rawKeyHint("drop files", "to attach"),
|
|
267
268
|
].join("\n");
|
|
268
269
|
this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
|
|
@@ -1049,7 +1050,7 @@ export class InteractiveMode {
|
|
|
1049
1050
|
this.defaultEditor.onExtensionShortcut = undefined;
|
|
1050
1051
|
this.updateTerminalTitle();
|
|
1051
1052
|
if (this.loadingAnimation) {
|
|
1052
|
-
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${
|
|
1053
|
+
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
|
|
1053
1054
|
}
|
|
1054
1055
|
}
|
|
1055
1056
|
// Maximum total widget lines to prevent viewport overflow
|
|
@@ -1172,7 +1173,7 @@ export class InteractiveMode {
|
|
|
1172
1173
|
this.loadingAnimation.setMessage(message);
|
|
1173
1174
|
}
|
|
1174
1175
|
else {
|
|
1175
|
-
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${
|
|
1176
|
+
this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
|
|
1176
1177
|
}
|
|
1177
1178
|
}
|
|
1178
1179
|
else {
|
|
@@ -1530,24 +1531,24 @@ export class InteractiveMode {
|
|
|
1530
1531
|
}
|
|
1531
1532
|
};
|
|
1532
1533
|
// Register app action handlers
|
|
1533
|
-
this.defaultEditor.onAction("clear", () => this.handleCtrlC());
|
|
1534
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
1534
1535
|
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
1535
|
-
this.defaultEditor.onAction("suspend", () => this.handleCtrlZ());
|
|
1536
|
-
this.defaultEditor.onAction("
|
|
1537
|
-
this.defaultEditor.onAction("
|
|
1538
|
-
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"));
|
|
1539
1540
|
// Global debug handler on TUI (works regardless of focus)
|
|
1540
1541
|
this.ui.onDebug = () => this.handleDebugCommand();
|
|
1541
|
-
this.defaultEditor.onAction("
|
|
1542
|
-
this.defaultEditor.onAction("
|
|
1543
|
-
this.defaultEditor.onAction("
|
|
1544
|
-
this.defaultEditor.onAction("
|
|
1545
|
-
this.defaultEditor.onAction("followUp", () => this.handleFollowUp());
|
|
1546
|
-
this.defaultEditor.onAction("dequeue", () => this.handleDequeue());
|
|
1547
|
-
this.defaultEditor.onAction("
|
|
1548
|
-
this.defaultEditor.onAction("tree", () => this.showTreeSelector());
|
|
1549
|
-
this.defaultEditor.onAction("fork", () => this.showUserMessageSelector());
|
|
1550
|
-
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());
|
|
1551
1552
|
this.defaultEditor.onChange = (text) => {
|
|
1552
1553
|
const wasBashMode = this.isBashMode;
|
|
1553
1554
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
@@ -1607,13 +1608,18 @@ export class InteractiveMode {
|
|
|
1607
1608
|
this.editor.setText("");
|
|
1608
1609
|
return;
|
|
1609
1610
|
}
|
|
1611
|
+
if (text.startsWith("/import")) {
|
|
1612
|
+
await this.handleImportCommand(text);
|
|
1613
|
+
this.editor.setText("");
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1610
1616
|
if (text === "/share") {
|
|
1611
1617
|
await this.handleShareCommand();
|
|
1612
1618
|
this.editor.setText("");
|
|
1613
1619
|
return;
|
|
1614
1620
|
}
|
|
1615
1621
|
if (text === "/copy") {
|
|
1616
|
-
this.handleCopyCommand();
|
|
1622
|
+
await this.handleCopyCommand();
|
|
1617
1623
|
this.editor.setText("");
|
|
1618
1624
|
return;
|
|
1619
1625
|
}
|
|
@@ -1861,15 +1867,17 @@ export class InteractiveMode {
|
|
|
1861
1867
|
this.ui.requestRender();
|
|
1862
1868
|
break;
|
|
1863
1869
|
case "tool_execution_start": {
|
|
1864
|
-
|
|
1865
|
-
|
|
1870
|
+
let component = this.pendingTools.get(event.toolCallId);
|
|
1871
|
+
if (!component) {
|
|
1872
|
+
component = new ToolExecutionComponent(event.toolName, event.args, {
|
|
1866
1873
|
showImages: this.settingsManager.getShowImages(),
|
|
1867
1874
|
}, this.getRegisteredToolDefinition(event.toolName), this.ui);
|
|
1868
1875
|
component.setExpanded(this.toolOutputExpanded);
|
|
1869
1876
|
this.chatContainer.addChild(component);
|
|
1870
1877
|
this.pendingTools.set(event.toolCallId, component);
|
|
1871
|
-
this.ui.requestRender();
|
|
1872
1878
|
}
|
|
1879
|
+
component.markExecutionStarted();
|
|
1880
|
+
this.ui.requestRender();
|
|
1873
1881
|
break;
|
|
1874
1882
|
}
|
|
1875
1883
|
case "tool_execution_update": {
|
|
@@ -1914,7 +1922,7 @@ export class InteractiveMode {
|
|
|
1914
1922
|
// Show compacting indicator with reason
|
|
1915
1923
|
this.statusContainer.clear();
|
|
1916
1924
|
const reasonText = event.reason === "overflow" ? "Context overflow detected, " : "";
|
|
1917
|
-
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)`);
|
|
1918
1926
|
this.statusContainer.addChild(this.autoCompactionLoader);
|
|
1919
1927
|
this.ui.requestRender();
|
|
1920
1928
|
break;
|
|
@@ -1966,7 +1974,7 @@ export class InteractiveMode {
|
|
|
1966
1974
|
// Show retry indicator
|
|
1967
1975
|
this.statusContainer.clear();
|
|
1968
1976
|
const delaySeconds = Math.round(event.delayMs / 1000);
|
|
1969
|
-
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)`);
|
|
1970
1978
|
this.statusContainer.addChild(this.retryLoader);
|
|
1971
1979
|
this.ui.requestRender();
|
|
1972
1980
|
break;
|
|
@@ -2235,20 +2243,32 @@ export class InteractiveMode {
|
|
|
2235
2243
|
await this.shutdown();
|
|
2236
2244
|
}
|
|
2237
2245
|
handleCtrlZ() {
|
|
2246
|
+
// Keep the event loop alive while suspended. Without this, stopping the TUI
|
|
2247
|
+
// can leave Node with no ref'ed handles, causing the process to exit on fg
|
|
2248
|
+
// before the SIGCONT handler gets a chance to restore the terminal.
|
|
2249
|
+
const suspendKeepAlive = setInterval(() => { }, 2 ** 30);
|
|
2238
2250
|
// Ignore SIGINT while suspended so Ctrl+C in the terminal does not
|
|
2239
2251
|
// kill the backgrounded process. The handler is removed on resume.
|
|
2240
2252
|
const ignoreSigint = () => { };
|
|
2241
2253
|
process.on("SIGINT", ignoreSigint);
|
|
2242
2254
|
// Set up handler to restore TUI when resumed
|
|
2243
2255
|
process.once("SIGCONT", () => {
|
|
2256
|
+
clearInterval(suspendKeepAlive);
|
|
2244
2257
|
process.removeListener("SIGINT", ignoreSigint);
|
|
2245
2258
|
this.ui.start();
|
|
2246
2259
|
this.ui.requestRender(true);
|
|
2247
2260
|
});
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2261
|
+
try {
|
|
2262
|
+
// Stop the TUI (restore terminal to normal mode)
|
|
2263
|
+
this.ui.stop();
|
|
2264
|
+
// Send SIGTSTP to process group (pid=0 means all processes in group)
|
|
2265
|
+
process.kill(0, "SIGTSTP");
|
|
2266
|
+
}
|
|
2267
|
+
catch (error) {
|
|
2268
|
+
clearInterval(suspendKeepAlive);
|
|
2269
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2270
|
+
throw error;
|
|
2271
|
+
}
|
|
2252
2272
|
}
|
|
2253
2273
|
async handleFollowUp() {
|
|
2254
2274
|
const text = (this.editor.getExpandedText?.() ?? this.editor.getText()).trim();
|
|
@@ -2481,7 +2501,7 @@ export class InteractiveMode {
|
|
|
2481
2501
|
const text = theme.fg("dim", `Follow-up: ${message}`);
|
|
2482
2502
|
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
|
|
2483
2503
|
}
|
|
2484
|
-
const dequeueHint = this.getAppKeyDisplay("dequeue");
|
|
2504
|
+
const dequeueHint = this.getAppKeyDisplay("app.message.dequeue");
|
|
2485
2505
|
const hintText = theme.fg("dim", `↳ ${dequeueHint} to edit all queued messages`);
|
|
2486
2506
|
this.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
|
|
2487
2507
|
}
|
|
@@ -3003,7 +3023,7 @@ export class InteractiveMode {
|
|
|
3003
3023
|
this.session.abortBranchSummary();
|
|
3004
3024
|
};
|
|
3005
3025
|
this.chatContainer.addChild(new Spacer(1));
|
|
3006
|
-
summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${
|
|
3026
|
+
summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${keyText("app.interrupt")} to cancel)`);
|
|
3007
3027
|
this.statusContainer.addChild(summaryLoader);
|
|
3008
3028
|
this.ui.requestRender();
|
|
3009
3029
|
}
|
|
@@ -3285,13 +3305,58 @@ export class InteractiveMode {
|
|
|
3285
3305
|
const parts = text.split(/\s+/);
|
|
3286
3306
|
const outputPath = parts.length > 1 ? parts[1] : undefined;
|
|
3287
3307
|
try {
|
|
3288
|
-
|
|
3289
|
-
|
|
3308
|
+
if (outputPath?.endsWith(".jsonl")) {
|
|
3309
|
+
const filePath = this.session.exportToJsonl(outputPath);
|
|
3310
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3311
|
+
}
|
|
3312
|
+
else {
|
|
3313
|
+
const filePath = await this.session.exportToHtml(outputPath);
|
|
3314
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3315
|
+
}
|
|
3290
3316
|
}
|
|
3291
3317
|
catch (error) {
|
|
3292
3318
|
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3293
3319
|
}
|
|
3294
3320
|
}
|
|
3321
|
+
async handleImportCommand(text) {
|
|
3322
|
+
const parts = text.split(/\s+/);
|
|
3323
|
+
if (parts.length < 2 || !parts[1]) {
|
|
3324
|
+
this.showError("Usage: /import <path.jsonl>");
|
|
3325
|
+
return;
|
|
3326
|
+
}
|
|
3327
|
+
const inputPath = parts[1];
|
|
3328
|
+
const confirmed = await this.showExtensionConfirm("Import session", `Replace current session with ${inputPath}?`);
|
|
3329
|
+
if (!confirmed) {
|
|
3330
|
+
this.showStatus("Import cancelled");
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
try {
|
|
3334
|
+
// Stop loading animation
|
|
3335
|
+
if (this.loadingAnimation) {
|
|
3336
|
+
this.loadingAnimation.stop();
|
|
3337
|
+
this.loadingAnimation = undefined;
|
|
3338
|
+
}
|
|
3339
|
+
this.statusContainer.clear();
|
|
3340
|
+
// Clear UI state
|
|
3341
|
+
this.pendingMessagesContainer.clear();
|
|
3342
|
+
this.compactionQueuedMessages = [];
|
|
3343
|
+
this.streamingComponent = undefined;
|
|
3344
|
+
this.streamingMessage = undefined;
|
|
3345
|
+
this.pendingTools.clear();
|
|
3346
|
+
const success = await this.session.importFromJsonl(inputPath);
|
|
3347
|
+
if (!success) {
|
|
3348
|
+
this.showWarning("Import cancelled");
|
|
3349
|
+
return;
|
|
3350
|
+
}
|
|
3351
|
+
// Clear and re-render the chat
|
|
3352
|
+
this.chatContainer.clear();
|
|
3353
|
+
this.renderInitialMessages();
|
|
3354
|
+
this.showStatus(`Session imported from: ${inputPath}`);
|
|
3355
|
+
}
|
|
3356
|
+
catch (error) {
|
|
3357
|
+
this.showError(`Failed to import session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3295
3360
|
async handleShareCommand() {
|
|
3296
3361
|
// Check if gh is available and logged in
|
|
3297
3362
|
try {
|
|
@@ -3379,14 +3444,14 @@ export class InteractiveMode {
|
|
|
3379
3444
|
}
|
|
3380
3445
|
}
|
|
3381
3446
|
}
|
|
3382
|
-
handleCopyCommand() {
|
|
3447
|
+
async handleCopyCommand() {
|
|
3383
3448
|
const text = this.session.getLastAssistantText();
|
|
3384
3449
|
if (!text) {
|
|
3385
3450
|
this.showError("No agent messages to copy yet.");
|
|
3386
3451
|
return;
|
|
3387
3452
|
}
|
|
3388
3453
|
try {
|
|
3389
|
-
copyToClipboard(text);
|
|
3454
|
+
await copyToClipboard(text);
|
|
3390
3455
|
this.showStatus("Copied last agent message to clipboard");
|
|
3391
3456
|
}
|
|
3392
3457
|
catch (error) {
|
|
@@ -3479,54 +3544,54 @@ export class InteractiveMode {
|
|
|
3479
3544
|
* Get capitalized display string for an app keybinding action.
|
|
3480
3545
|
*/
|
|
3481
3546
|
getAppKeyDisplay(action) {
|
|
3482
|
-
return this.capitalizeKey(
|
|
3547
|
+
return this.capitalizeKey(keyText(action));
|
|
3483
3548
|
}
|
|
3484
3549
|
/**
|
|
3485
3550
|
* Get capitalized display string for an editor keybinding action.
|
|
3486
3551
|
*/
|
|
3487
3552
|
getEditorKeyDisplay(action) {
|
|
3488
|
-
return this.capitalizeKey(
|
|
3553
|
+
return this.capitalizeKey(keyText(action));
|
|
3489
3554
|
}
|
|
3490
3555
|
handleHotkeysCommand() {
|
|
3491
3556
|
// Navigation keybindings
|
|
3492
|
-
const cursorUp = this.getEditorKeyDisplay("cursorUp");
|
|
3493
|
-
const cursorDown = this.getEditorKeyDisplay("cursorDown");
|
|
3494
|
-
const cursorLeft = this.getEditorKeyDisplay("cursorLeft");
|
|
3495
|
-
const cursorRight = this.getEditorKeyDisplay("cursorRight");
|
|
3496
|
-
const cursorWordLeft = this.getEditorKeyDisplay("cursorWordLeft");
|
|
3497
|
-
const cursorWordRight = this.getEditorKeyDisplay("cursorWordRight");
|
|
3498
|
-
const cursorLineStart = this.getEditorKeyDisplay("cursorLineStart");
|
|
3499
|
-
const cursorLineEnd = this.getEditorKeyDisplay("cursorLineEnd");
|
|
3500
|
-
const jumpForward = this.getEditorKeyDisplay("jumpForward");
|
|
3501
|
-
const jumpBackward = this.getEditorKeyDisplay("jumpBackward");
|
|
3502
|
-
const pageUp = this.getEditorKeyDisplay("pageUp");
|
|
3503
|
-
const pageDown = this.getEditorKeyDisplay("pageDown");
|
|
3557
|
+
const cursorUp = this.getEditorKeyDisplay("tui.editor.cursorUp");
|
|
3558
|
+
const cursorDown = this.getEditorKeyDisplay("tui.editor.cursorDown");
|
|
3559
|
+
const cursorLeft = this.getEditorKeyDisplay("tui.editor.cursorLeft");
|
|
3560
|
+
const cursorRight = this.getEditorKeyDisplay("tui.editor.cursorRight");
|
|
3561
|
+
const cursorWordLeft = this.getEditorKeyDisplay("tui.editor.cursorWordLeft");
|
|
3562
|
+
const cursorWordRight = this.getEditorKeyDisplay("tui.editor.cursorWordRight");
|
|
3563
|
+
const cursorLineStart = this.getEditorKeyDisplay("tui.editor.cursorLineStart");
|
|
3564
|
+
const cursorLineEnd = this.getEditorKeyDisplay("tui.editor.cursorLineEnd");
|
|
3565
|
+
const jumpForward = this.getEditorKeyDisplay("tui.editor.jumpForward");
|
|
3566
|
+
const jumpBackward = this.getEditorKeyDisplay("tui.editor.jumpBackward");
|
|
3567
|
+
const pageUp = this.getEditorKeyDisplay("tui.editor.pageUp");
|
|
3568
|
+
const pageDown = this.getEditorKeyDisplay("tui.editor.pageDown");
|
|
3504
3569
|
// Editing keybindings
|
|
3505
|
-
const submit = this.getEditorKeyDisplay("submit");
|
|
3506
|
-
const newLine = this.getEditorKeyDisplay("newLine");
|
|
3507
|
-
const deleteWordBackward = this.getEditorKeyDisplay("deleteWordBackward");
|
|
3508
|
-
const deleteWordForward = this.getEditorKeyDisplay("deleteWordForward");
|
|
3509
|
-
const deleteToLineStart = this.getEditorKeyDisplay("deleteToLineStart");
|
|
3510
|
-
const deleteToLineEnd = this.getEditorKeyDisplay("deleteToLineEnd");
|
|
3511
|
-
const yank = this.getEditorKeyDisplay("yank");
|
|
3512
|
-
const yankPop = this.getEditorKeyDisplay("yankPop");
|
|
3513
|
-
const undo = this.getEditorKeyDisplay("undo");
|
|
3514
|
-
const tab = this.getEditorKeyDisplay("tab");
|
|
3570
|
+
const submit = this.getEditorKeyDisplay("tui.input.submit");
|
|
3571
|
+
const newLine = this.getEditorKeyDisplay("tui.input.newLine");
|
|
3572
|
+
const deleteWordBackward = this.getEditorKeyDisplay("tui.editor.deleteWordBackward");
|
|
3573
|
+
const deleteWordForward = this.getEditorKeyDisplay("tui.editor.deleteWordForward");
|
|
3574
|
+
const deleteToLineStart = this.getEditorKeyDisplay("tui.editor.deleteToLineStart");
|
|
3575
|
+
const deleteToLineEnd = this.getEditorKeyDisplay("tui.editor.deleteToLineEnd");
|
|
3576
|
+
const yank = this.getEditorKeyDisplay("tui.editor.yank");
|
|
3577
|
+
const yankPop = this.getEditorKeyDisplay("tui.editor.yankPop");
|
|
3578
|
+
const undo = this.getEditorKeyDisplay("tui.editor.undo");
|
|
3579
|
+
const tab = this.getEditorKeyDisplay("tui.input.tab");
|
|
3515
3580
|
// App keybindings
|
|
3516
|
-
const interrupt = this.getAppKeyDisplay("interrupt");
|
|
3517
|
-
const clear = this.getAppKeyDisplay("clear");
|
|
3518
|
-
const exit = this.getAppKeyDisplay("exit");
|
|
3519
|
-
const suspend = this.getAppKeyDisplay("suspend");
|
|
3520
|
-
const cycleThinkingLevel = this.getAppKeyDisplay("
|
|
3521
|
-
const cycleModelForward = this.getAppKeyDisplay("
|
|
3522
|
-
const selectModel = this.getAppKeyDisplay("
|
|
3523
|
-
const expandTools = this.getAppKeyDisplay("
|
|
3524
|
-
const toggleThinking = this.getAppKeyDisplay("
|
|
3525
|
-
const externalEditor = this.getAppKeyDisplay("
|
|
3526
|
-
const cycleModelBackward = this.getAppKeyDisplay("
|
|
3527
|
-
const followUp = this.getAppKeyDisplay("followUp");
|
|
3528
|
-
const dequeue = this.getAppKeyDisplay("dequeue");
|
|
3529
|
-
const pasteImage = this.getAppKeyDisplay("pasteImage");
|
|
3581
|
+
const interrupt = this.getAppKeyDisplay("app.interrupt");
|
|
3582
|
+
const clear = this.getAppKeyDisplay("app.clear");
|
|
3583
|
+
const exit = this.getAppKeyDisplay("app.exit");
|
|
3584
|
+
const suspend = this.getAppKeyDisplay("app.suspend");
|
|
3585
|
+
const cycleThinkingLevel = this.getAppKeyDisplay("app.thinking.cycle");
|
|
3586
|
+
const cycleModelForward = this.getAppKeyDisplay("app.model.cycleForward");
|
|
3587
|
+
const selectModel = this.getAppKeyDisplay("app.model.select");
|
|
3588
|
+
const expandTools = this.getAppKeyDisplay("app.tools.expand");
|
|
3589
|
+
const toggleThinking = this.getAppKeyDisplay("app.thinking.toggle");
|
|
3590
|
+
const externalEditor = this.getAppKeyDisplay("app.editor.external");
|
|
3591
|
+
const cycleModelBackward = this.getAppKeyDisplay("app.model.cycleBackward");
|
|
3592
|
+
const followUp = this.getAppKeyDisplay("app.message.followUp");
|
|
3593
|
+
const dequeue = this.getAppKeyDisplay("app.message.dequeue");
|
|
3594
|
+
const pasteImage = this.getAppKeyDisplay("app.clipboard.pasteImage");
|
|
3530
3595
|
let hotkeys = `
|
|
3531
3596
|
**Navigation**
|
|
3532
3597
|
| Key | Action |
|
|
@@ -3751,7 +3816,7 @@ export class InteractiveMode {
|
|
|
3751
3816
|
};
|
|
3752
3817
|
// Show compacting status
|
|
3753
3818
|
this.chatContainer.addChild(new Spacer(1));
|
|
3754
|
-
const cancelHint = `(${
|
|
3819
|
+
const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
|
|
3755
3820
|
const label = isAuto ? `Auto-compacting context... ${cancelHint}` : `Compacting context... ${cancelHint}`;
|
|
3756
3821
|
const compactingLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
|
|
3757
3822
|
this.statusContainer.addChild(compactingLoader);
|