@codex-infinity/pi-infinity 0.52.4 → 0.60.2
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 +387 -0
- package/README.md +97 -66
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +6 -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 +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +17 -6
- 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.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-session.d.ts +42 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +346 -72
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +27 -2
- package/dist/core/auth-storage.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/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +6 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +3 -0
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +16 -1
- package/dist/core/compaction/utils.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/index.d.ts +5 -2
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +4 -3
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/template.js +11 -14
- package/dist/core/export-html/tool-renderer.d.ts +5 -2
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +17 -4
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +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 +37 -11
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +8 -4
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +77 -8
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +56 -4
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +4 -11
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +4 -78
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +6 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +83 -37
- 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 +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +3 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +22 -12
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +11 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +56 -16
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +6 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +122 -39
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +19 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +290 -57
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +43 -8
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +4 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +7 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +21 -15
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +59 -5
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +3 -2
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +29 -8
- package/dist/core/skills.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/system-prompt.d.ts +4 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +43 -29
- package/dist/core/system-prompt.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-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +1 -0
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +6 -3
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -1
- package/dist/core/tools/index.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 +116 -36
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +5 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +9 -0
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -23
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +1 -1
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +1 -1
- 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 +1 -1
- package/dist/modes/interactive/components/oauth-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 +1 -1
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +15 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +5 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +5 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +5 -1
- package/dist/modes/interactive/components/thinking-selector.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 +158 -7
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +21 -2
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +127 -10
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +12 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +215 -71
- 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 +5 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -11
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +9 -11
- 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-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +94 -11
- package/dist/utils/clipboard-image.js.map +1 -1
- 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 +27 -16
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +5 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +6 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +66 -21
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +2 -0
- package/docs/custom-provider.md +57 -9
- package/docs/extensions.md +125 -12
- package/docs/keybindings.md +11 -1
- package/docs/models.md +44 -2
- package/docs/packages.md +9 -0
- package/docs/providers.md +10 -1
- package/docs/rpc.md +44 -7
- package/docs/sdk.md +2 -2
- package/docs/settings.md +11 -0
- package/docs/terminal-setup.md +39 -3
- package/docs/tmux.md +61 -0
- package/docs/tree.md +9 -0
- package/examples/extensions/README.md +2 -0
- package/examples/extensions/antigravity-image-gen.ts +8 -5
- package/examples/extensions/built-in-tool-renderer.ts +246 -0
- 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-gitlab-duo/test.ts +2 -2
- package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/overlay-qa-tests.ts +468 -1
- package/examples/extensions/preset.ts +2 -3
- package/examples/extensions/provider-payload.ts +14 -0
- package/examples/extensions/sandbox/index.ts +2 -3
- package/examples/extensions/subagent/agents.ts +2 -3
- package/examples/extensions/tool-override.ts +2 -3
- package/examples/extensions/with-deps/index.ts +1 -5
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +10 -7
|
@@ -6,15 +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 { getOAuthProviders, } from "@mariozechner/pi-ai";
|
|
10
9
|
import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, Text, TruncatedText, TUI, visibleWidth, } from "@mariozechner/pi-tui";
|
|
11
10
|
import { spawn, spawnSync } from "child_process";
|
|
12
|
-
import { APP_NAME, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
11
|
+
import { APP_NAME, getAgentDir, getAuthPath, getDebugLogPath, getShareViewerUrl, getUpdateInstruction, VERSION, } from "../../config.js";
|
|
13
12
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
14
13
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
15
14
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
16
15
|
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
17
|
-
import { resolveModelScope } from "../../core/model-resolver.js";
|
|
16
|
+
import { findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
|
|
17
|
+
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
18
18
|
import { SessionManager } from "../../core/session-manager.js";
|
|
19
19
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
20
20
|
import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
|
|
@@ -312,13 +312,13 @@ export class InteractiveMode {
|
|
|
312
312
|
this.ui.setFocus(this.editor);
|
|
313
313
|
this.setupKeyHandlers();
|
|
314
314
|
this.setupEditorSubmitHandler();
|
|
315
|
+
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
316
|
+
this.ui.start();
|
|
317
|
+
this.isInitialized = true;
|
|
315
318
|
// Initialize extensions first so resources are shown before messages
|
|
316
319
|
await this.initExtensions();
|
|
317
320
|
// Render initial messages AFTER showing loaded resources
|
|
318
321
|
this.renderInitialMessages();
|
|
319
|
-
// Start the UI
|
|
320
|
-
this.ui.start();
|
|
321
|
-
this.isInitialized = true;
|
|
322
322
|
// Set terminal title
|
|
323
323
|
this.updateTerminalTitle();
|
|
324
324
|
// Subscribe to agent events
|
|
@@ -361,6 +361,18 @@ export class InteractiveMode {
|
|
|
361
361
|
this.showNewVersionNotification(newVersion);
|
|
362
362
|
}
|
|
363
363
|
});
|
|
364
|
+
// Start package update check asynchronously
|
|
365
|
+
this.checkForPackageUpdates().then((updates) => {
|
|
366
|
+
if (updates.length > 0) {
|
|
367
|
+
this.showPackageUpdateNotification(updates);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
// Check tmux keyboard setup asynchronously
|
|
371
|
+
this.checkTmuxKeyboardSetup().then((warning) => {
|
|
372
|
+
if (warning) {
|
|
373
|
+
this.showWarning(warning);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
364
376
|
// Show startup warnings
|
|
365
377
|
const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
|
|
366
378
|
if (migratedProviders && migratedProviders.length > 0) {
|
|
@@ -410,10 +422,12 @@ export class InteractiveMode {
|
|
|
410
422
|
* Check npm registry for a newer version.
|
|
411
423
|
*/
|
|
412
424
|
async checkForNewVersion() {
|
|
413
|
-
if (process.env.PI_SKIP_VERSION_CHECK)
|
|
425
|
+
if (process.env.PI_SKIP_VERSION_CHECK || process.env.PI_OFFLINE)
|
|
414
426
|
return undefined;
|
|
415
427
|
try {
|
|
416
|
-
const response = await fetch("https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest"
|
|
428
|
+
const response = await fetch("https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest", {
|
|
429
|
+
signal: AbortSignal.timeout(10000),
|
|
430
|
+
});
|
|
417
431
|
if (!response.ok)
|
|
418
432
|
return undefined;
|
|
419
433
|
const data = (await response.json());
|
|
@@ -427,6 +441,64 @@ export class InteractiveMode {
|
|
|
427
441
|
return undefined;
|
|
428
442
|
}
|
|
429
443
|
}
|
|
444
|
+
async checkForPackageUpdates() {
|
|
445
|
+
if (process.env.PI_OFFLINE) {
|
|
446
|
+
return [];
|
|
447
|
+
}
|
|
448
|
+
try {
|
|
449
|
+
const packageManager = new DefaultPackageManager({
|
|
450
|
+
cwd: process.cwd(),
|
|
451
|
+
agentDir: getAgentDir(),
|
|
452
|
+
settingsManager: this.settingsManager,
|
|
453
|
+
});
|
|
454
|
+
const updates = await packageManager.checkForAvailableUpdates();
|
|
455
|
+
return updates.map((update) => update.displayName);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async checkTmuxKeyboardSetup() {
|
|
462
|
+
if (!process.env.TMUX)
|
|
463
|
+
return undefined;
|
|
464
|
+
const runTmuxShow = (option) => {
|
|
465
|
+
return new Promise((resolve) => {
|
|
466
|
+
const proc = spawn("tmux", ["show", "-gv", option], {
|
|
467
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
468
|
+
});
|
|
469
|
+
let stdout = "";
|
|
470
|
+
const timer = setTimeout(() => {
|
|
471
|
+
proc.kill();
|
|
472
|
+
resolve(undefined);
|
|
473
|
+
}, 2000);
|
|
474
|
+
proc.stdout?.on("data", (data) => {
|
|
475
|
+
stdout += data.toString();
|
|
476
|
+
});
|
|
477
|
+
proc.on("error", () => {
|
|
478
|
+
clearTimeout(timer);
|
|
479
|
+
resolve(undefined);
|
|
480
|
+
});
|
|
481
|
+
proc.on("close", (code) => {
|
|
482
|
+
clearTimeout(timer);
|
|
483
|
+
resolve(code === 0 ? stdout.trim() : undefined);
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
};
|
|
487
|
+
const [extendedKeys, extendedKeysFormat] = await Promise.all([
|
|
488
|
+
runTmuxShow("extended-keys"),
|
|
489
|
+
runTmuxShow("extended-keys-format"),
|
|
490
|
+
]);
|
|
491
|
+
// If we couldn't query tmux (timeout, sandbox, etc.), don't warn
|
|
492
|
+
if (extendedKeys === undefined)
|
|
493
|
+
return undefined;
|
|
494
|
+
if (extendedKeys !== "on" && extendedKeys !== "always") {
|
|
495
|
+
return "tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";
|
|
496
|
+
}
|
|
497
|
+
if (extendedKeysFormat === "xterm") {
|
|
498
|
+
return "tmux extended-keys-format is xterm. Pi works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.";
|
|
499
|
+
}
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
430
502
|
/**
|
|
431
503
|
* Get changelog entries to display on startup.
|
|
432
504
|
* Only shows new entries since last seen version, skips for resumed sessions.
|
|
@@ -540,7 +612,7 @@ export class InteractiveMode {
|
|
|
540
612
|
group.paths.push(p);
|
|
541
613
|
}
|
|
542
614
|
}
|
|
543
|
-
return [groups.
|
|
615
|
+
return [groups.project, groups.user, groups.path].filter((group) => group.paths.length > 0 || group.packages.size > 0);
|
|
544
616
|
}
|
|
545
617
|
formatScopeGroups(groups, options) {
|
|
546
618
|
const lines = [];
|
|
@@ -1115,7 +1187,7 @@ export class InteractiveMode {
|
|
|
1115
1187
|
custom: (factory, options) => this.showExtensionCustom(factory, options),
|
|
1116
1188
|
pasteToEditor: (text) => this.editor.handleInput(`\x1b[200~${text}\x1b[201~`),
|
|
1117
1189
|
setEditorText: (text) => this.editor.setText(text),
|
|
1118
|
-
getEditorText: () => this.editor.getText(),
|
|
1190
|
+
getEditorText: () => this.editor.getExpandedText?.() ?? this.editor.getText(),
|
|
1119
1191
|
editor: (title, prefill) => this.showExtensionEditor(title, prefill),
|
|
1120
1192
|
setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
|
|
1121
1193
|
get theme() {
|
|
@@ -1131,6 +1203,9 @@ export class InteractiveMode {
|
|
|
1131
1203
|
}
|
|
1132
1204
|
const result = setTheme(themeOrName, true);
|
|
1133
1205
|
if (result.success) {
|
|
1206
|
+
if (this.settingsManager.getTheme() !== themeOrName) {
|
|
1207
|
+
this.settingsManager.setTheme(themeOrName);
|
|
1208
|
+
}
|
|
1134
1209
|
this.ui.requestRender();
|
|
1135
1210
|
}
|
|
1136
1211
|
return result;
|
|
@@ -1285,10 +1360,18 @@ export class InteractiveMode {
|
|
|
1285
1360
|
// Use duck typing since instanceof fails across jiti module boundaries
|
|
1286
1361
|
const customEditor = newEditor;
|
|
1287
1362
|
if ("actionHandlers" in customEditor && customEditor.actionHandlers instanceof Map) {
|
|
1288
|
-
customEditor.onEscape
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
customEditor.
|
|
1363
|
+
if (!customEditor.onEscape) {
|
|
1364
|
+
customEditor.onEscape = () => this.defaultEditor.onEscape?.();
|
|
1365
|
+
}
|
|
1366
|
+
if (!customEditor.onCtrlD) {
|
|
1367
|
+
customEditor.onCtrlD = () => this.defaultEditor.onCtrlD?.();
|
|
1368
|
+
}
|
|
1369
|
+
if (!customEditor.onPasteImage) {
|
|
1370
|
+
customEditor.onPasteImage = () => this.defaultEditor.onPasteImage?.();
|
|
1371
|
+
}
|
|
1372
|
+
if (!customEditor.onExtensionShortcut) {
|
|
1373
|
+
customEditor.onExtensionShortcut = (data) => this.defaultEditor.onExtensionShortcut?.(data);
|
|
1374
|
+
}
|
|
1292
1375
|
// Copy action handlers (clear, suspend, model switching, etc.)
|
|
1293
1376
|
for (const [action, handler] of this.defaultEditor.actionHandlers) {
|
|
1294
1377
|
customEditor.actionHandlers.set(action, handler);
|
|
@@ -1524,13 +1607,18 @@ export class InteractiveMode {
|
|
|
1524
1607
|
this.editor.setText("");
|
|
1525
1608
|
return;
|
|
1526
1609
|
}
|
|
1610
|
+
if (text.startsWith("/import")) {
|
|
1611
|
+
await this.handleImportCommand(text);
|
|
1612
|
+
this.editor.setText("");
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1527
1615
|
if (text === "/share") {
|
|
1528
1616
|
await this.handleShareCommand();
|
|
1529
1617
|
this.editor.setText("");
|
|
1530
1618
|
return;
|
|
1531
1619
|
}
|
|
1532
1620
|
if (text === "/copy") {
|
|
1533
|
-
this.handleCopyCommand();
|
|
1621
|
+
await this.handleCopyCommand();
|
|
1534
1622
|
this.editor.setText("");
|
|
1535
1623
|
return;
|
|
1536
1624
|
}
|
|
@@ -1720,7 +1808,6 @@ export class InteractiveMode {
|
|
|
1720
1808
|
for (const content of this.streamingMessage.content) {
|
|
1721
1809
|
if (content.type === "toolCall") {
|
|
1722
1810
|
if (!this.pendingTools.has(content.id)) {
|
|
1723
|
-
this.chatContainer.addChild(new Text("", 0, 0));
|
|
1724
1811
|
const component = new ToolExecutionComponent(content.name, content.arguments, {
|
|
1725
1812
|
showImages: this.settingsManager.getShowImages(),
|
|
1726
1813
|
}, this.getRegisteredToolDefinition(content.name), this.ui);
|
|
@@ -2153,8 +2240,13 @@ export class InteractiveMode {
|
|
|
2153
2240
|
await this.shutdown();
|
|
2154
2241
|
}
|
|
2155
2242
|
handleCtrlZ() {
|
|
2243
|
+
// Ignore SIGINT while suspended so Ctrl+C in the terminal does not
|
|
2244
|
+
// kill the backgrounded process. The handler is removed on resume.
|
|
2245
|
+
const ignoreSigint = () => { };
|
|
2246
|
+
process.on("SIGINT", ignoreSigint);
|
|
2156
2247
|
// Set up handler to restore TUI when resumed
|
|
2157
2248
|
process.once("SIGCONT", () => {
|
|
2249
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2158
2250
|
this.ui.start();
|
|
2159
2251
|
this.ui.requestRender(true);
|
|
2160
2252
|
});
|
|
@@ -2286,6 +2378,7 @@ export class InteractiveMode {
|
|
|
2286
2378
|
// Spawn editor synchronously with inherited stdio for interactive editing
|
|
2287
2379
|
const result = spawnSync(editor, [...editorArgs, tmpFile], {
|
|
2288
2380
|
stdio: "inherit",
|
|
2381
|
+
shell: process.platform === "win32",
|
|
2289
2382
|
});
|
|
2290
2383
|
// On successful exit (status 0), replace editor content
|
|
2291
2384
|
if (result.status === 0) {
|
|
@@ -2336,6 +2429,16 @@ export class InteractiveMode {
|
|
|
2336
2429
|
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2337
2430
|
this.ui.requestRender();
|
|
2338
2431
|
}
|
|
2432
|
+
showPackageUpdateNotification(packages) {
|
|
2433
|
+
const action = theme.fg("accent", `${APP_NAME} update`);
|
|
2434
|
+
const updateInstruction = theme.fg("muted", "Package updates are available. Run ") + action;
|
|
2435
|
+
const packageLines = packages.map((pkg) => `- ${pkg}`).join("\n");
|
|
2436
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2437
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2438
|
+
this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n${updateInstruction}\n${theme.fg("muted", "Packages:")}\n${packageLines}`, 1, 0));
|
|
2439
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2440
|
+
this.ui.requestRender();
|
|
2441
|
+
}
|
|
2339
2442
|
/**
|
|
2340
2443
|
* Get all queued messages (read-only).
|
|
2341
2444
|
* Combines session queue and compaction queue.
|
|
@@ -2539,6 +2642,7 @@ export class InteractiveMode {
|
|
|
2539
2642
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
2540
2643
|
collapseChangelog: this.settingsManager.getCollapseChangelog(),
|
|
2541
2644
|
doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
|
|
2645
|
+
treeFilterMode: this.settingsManager.getTreeFilterMode(),
|
|
2542
2646
|
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
2543
2647
|
editorPaddingX: this.settingsManager.getEditorPaddingX(),
|
|
2544
2648
|
autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
|
|
@@ -2617,6 +2721,9 @@ export class InteractiveMode {
|
|
|
2617
2721
|
onDoubleEscapeActionChange: (action) => {
|
|
2618
2722
|
this.settingsManager.setDoubleEscapeAction(action);
|
|
2619
2723
|
},
|
|
2724
|
+
onTreeFilterModeChange: (mode) => {
|
|
2725
|
+
this.settingsManager.setTreeFilterMode(mode);
|
|
2726
|
+
},
|
|
2620
2727
|
onShowHardwareCursorChange: (enabled) => {
|
|
2621
2728
|
this.settingsManager.setShowHardwareCursor(enabled);
|
|
2622
2729
|
this.ui.setShowHardwareCursor(enabled);
|
|
@@ -2669,28 +2776,8 @@ export class InteractiveMode {
|
|
|
2669
2776
|
this.showModelSelector(searchTerm);
|
|
2670
2777
|
}
|
|
2671
2778
|
async findExactModelMatch(searchTerm) {
|
|
2672
|
-
const term = searchTerm.trim();
|
|
2673
|
-
if (!term)
|
|
2674
|
-
return undefined;
|
|
2675
|
-
let targetProvider;
|
|
2676
|
-
let targetModelId = "";
|
|
2677
|
-
if (term.includes("/")) {
|
|
2678
|
-
const parts = term.split("/", 2);
|
|
2679
|
-
targetProvider = parts[0]?.trim().toLowerCase();
|
|
2680
|
-
targetModelId = parts[1]?.trim().toLowerCase() ?? "";
|
|
2681
|
-
}
|
|
2682
|
-
else {
|
|
2683
|
-
targetModelId = term.toLowerCase();
|
|
2684
|
-
}
|
|
2685
|
-
if (!targetModelId)
|
|
2686
|
-
return undefined;
|
|
2687
2779
|
const models = await this.getModelCandidates();
|
|
2688
|
-
|
|
2689
|
-
const idMatch = item.id.toLowerCase() === targetModelId;
|
|
2690
|
-
const providerMatch = !targetProvider || item.provider.toLowerCase() === targetProvider;
|
|
2691
|
-
return idMatch && providerMatch;
|
|
2692
|
-
});
|
|
2693
|
-
return exactMatches.length === 1 ? exactMatches[0] : undefined;
|
|
2780
|
+
return findExactModelReferenceMatch(searchTerm, models);
|
|
2694
2781
|
}
|
|
2695
2782
|
async getModelCandidates() {
|
|
2696
2783
|
if (this.session.scopedModels.length > 0) {
|
|
@@ -2770,12 +2857,10 @@ export class InteractiveMode {
|
|
|
2770
2857
|
// Helper to update session's scoped models (session-only, no persist)
|
|
2771
2858
|
const updateSessionModels = async (enabledIds) => {
|
|
2772
2859
|
if (enabledIds.size > 0 && enabledIds.size < allModels.length) {
|
|
2773
|
-
// Use current session thinking level, not settings default
|
|
2774
|
-
const currentThinkingLevel = this.session.thinkingLevel;
|
|
2775
2860
|
const newScopedModels = await resolveModelScope(Array.from(enabledIds), this.session.modelRegistry);
|
|
2776
2861
|
this.session.setScopedModels(newScopedModels.map((sm) => ({
|
|
2777
2862
|
model: sm.model,
|
|
2778
|
-
thinkingLevel: sm.thinkingLevel
|
|
2863
|
+
thinkingLevel: sm.thinkingLevel,
|
|
2779
2864
|
})));
|
|
2780
2865
|
}
|
|
2781
2866
|
else {
|
|
@@ -2872,6 +2957,7 @@ export class InteractiveMode {
|
|
|
2872
2957
|
showTreeSelector(initialSelectedId) {
|
|
2873
2958
|
const tree = this.sessionManager.getTree();
|
|
2874
2959
|
const realLeafId = this.sessionManager.getLeafId();
|
|
2960
|
+
const initialFilterMode = this.settingsManager.getTreeFilterMode();
|
|
2875
2961
|
if (tree.length === 0) {
|
|
2876
2962
|
this.showStatus("No entries in session");
|
|
2877
2963
|
return;
|
|
@@ -2889,27 +2975,30 @@ export class InteractiveMode {
|
|
|
2889
2975
|
// Loop until user makes a complete choice or cancels to tree
|
|
2890
2976
|
let wantsSummary = false;
|
|
2891
2977
|
let customInstructions;
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
"Summarize",
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
if (
|
|
2907
|
-
|
|
2908
|
-
|
|
2978
|
+
// Check if we should skip the prompt (user preference to always default to no summary)
|
|
2979
|
+
if (!this.settingsManager.getBranchSummarySkipPrompt()) {
|
|
2980
|
+
while (true) {
|
|
2981
|
+
const summaryChoice = await this.showExtensionSelector("Summarize branch?", [
|
|
2982
|
+
"No summary",
|
|
2983
|
+
"Summarize",
|
|
2984
|
+
"Summarize with custom prompt",
|
|
2985
|
+
]);
|
|
2986
|
+
if (summaryChoice === undefined) {
|
|
2987
|
+
// User pressed escape - re-show tree selector with same selection
|
|
2988
|
+
this.showTreeSelector(entryId);
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
wantsSummary = summaryChoice !== "No summary";
|
|
2992
|
+
if (summaryChoice === "Summarize with custom prompt") {
|
|
2993
|
+
customInstructions = await this.showExtensionEditor("Custom summarization instructions");
|
|
2994
|
+
if (customInstructions === undefined) {
|
|
2995
|
+
// User cancelled - loop back to summary selector
|
|
2996
|
+
continue;
|
|
2997
|
+
}
|
|
2909
2998
|
}
|
|
2999
|
+
// User made a complete choice
|
|
3000
|
+
break;
|
|
2910
3001
|
}
|
|
2911
|
-
// User made a complete choice
|
|
2912
|
-
break;
|
|
2913
3002
|
}
|
|
2914
3003
|
// Set up escape handler and loader if summarizing
|
|
2915
3004
|
let summaryLoader;
|
|
@@ -2962,7 +3051,7 @@ export class InteractiveMode {
|
|
|
2962
3051
|
}, (entryId, label) => {
|
|
2963
3052
|
this.sessionManager.appendLabelChange(entryId, label);
|
|
2964
3053
|
this.ui.requestRender();
|
|
2965
|
-
}, initialSelectedId);
|
|
3054
|
+
}, initialSelectedId, initialFilterMode);
|
|
2966
3055
|
return { component: selector, focus: selector };
|
|
2967
3056
|
});
|
|
2968
3057
|
}
|
|
@@ -3027,7 +3116,9 @@ export class InteractiveMode {
|
|
|
3027
3116
|
}
|
|
3028
3117
|
else {
|
|
3029
3118
|
// Logout flow
|
|
3030
|
-
const providerInfo =
|
|
3119
|
+
const providerInfo = this.session.modelRegistry.authStorage
|
|
3120
|
+
.getOAuthProviders()
|
|
3121
|
+
.find((p) => p.id === providerId);
|
|
3031
3122
|
const providerName = providerInfo?.name || providerId;
|
|
3032
3123
|
try {
|
|
3033
3124
|
this.session.modelRegistry.authStorage.logout(providerId);
|
|
@@ -3047,7 +3138,7 @@ export class InteractiveMode {
|
|
|
3047
3138
|
});
|
|
3048
3139
|
}
|
|
3049
3140
|
async showLoginDialog(providerId) {
|
|
3050
|
-
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
|
3141
|
+
const providerInfo = this.session.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);
|
|
3051
3142
|
const providerName = providerInfo?.name || providerId;
|
|
3052
3143
|
// Providers that use callback servers (can paste redirect URL)
|
|
3053
3144
|
const usesCallbackServer = providerInfo?.usesCallbackServer ?? false;
|
|
@@ -3137,7 +3228,7 @@ export class InteractiveMode {
|
|
|
3137
3228
|
return;
|
|
3138
3229
|
}
|
|
3139
3230
|
this.resetExtensionUI();
|
|
3140
|
-
const loader = new BorderedLoader(this.ui, theme, "Reloading extensions, skills, prompts, themes...", {
|
|
3231
|
+
const loader = new BorderedLoader(this.ui, theme, "Reloading keybindings, extensions, skills, prompts, themes...", {
|
|
3141
3232
|
cancellable: false,
|
|
3142
3233
|
});
|
|
3143
3234
|
const previousEditor = this.editor;
|
|
@@ -3154,6 +3245,7 @@ export class InteractiveMode {
|
|
|
3154
3245
|
};
|
|
3155
3246
|
try {
|
|
3156
3247
|
await this.session.reload();
|
|
3248
|
+
this.keybindings.reload();
|
|
3157
3249
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
3158
3250
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
3159
3251
|
const themeName = this.settingsManager.getTheme();
|
|
@@ -3187,7 +3279,7 @@ export class InteractiveMode {
|
|
|
3187
3279
|
if (modelsJsonError) {
|
|
3188
3280
|
this.showError(`models.json error: ${modelsJsonError}`);
|
|
3189
3281
|
}
|
|
3190
|
-
this.showStatus("Reloaded extensions, skills, prompts, themes");
|
|
3282
|
+
this.showStatus("Reloaded keybindings, extensions, skills, prompts, themes");
|
|
3191
3283
|
}
|
|
3192
3284
|
catch (error) {
|
|
3193
3285
|
dismissLoader(previousEditor);
|
|
@@ -3198,13 +3290,58 @@ export class InteractiveMode {
|
|
|
3198
3290
|
const parts = text.split(/\s+/);
|
|
3199
3291
|
const outputPath = parts.length > 1 ? parts[1] : undefined;
|
|
3200
3292
|
try {
|
|
3201
|
-
|
|
3202
|
-
|
|
3293
|
+
if (outputPath?.endsWith(".jsonl")) {
|
|
3294
|
+
const filePath = this.session.exportToJsonl(outputPath);
|
|
3295
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3296
|
+
}
|
|
3297
|
+
else {
|
|
3298
|
+
const filePath = await this.session.exportToHtml(outputPath);
|
|
3299
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3300
|
+
}
|
|
3203
3301
|
}
|
|
3204
3302
|
catch (error) {
|
|
3205
3303
|
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3206
3304
|
}
|
|
3207
3305
|
}
|
|
3306
|
+
async handleImportCommand(text) {
|
|
3307
|
+
const parts = text.split(/\s+/);
|
|
3308
|
+
if (parts.length < 2 || !parts[1]) {
|
|
3309
|
+
this.showError("Usage: /import <path.jsonl>");
|
|
3310
|
+
return;
|
|
3311
|
+
}
|
|
3312
|
+
const inputPath = parts[1];
|
|
3313
|
+
const confirmed = await this.showExtensionConfirm("Import session", `Replace current session with ${inputPath}?`);
|
|
3314
|
+
if (!confirmed) {
|
|
3315
|
+
this.showStatus("Import cancelled");
|
|
3316
|
+
return;
|
|
3317
|
+
}
|
|
3318
|
+
try {
|
|
3319
|
+
// Stop loading animation
|
|
3320
|
+
if (this.loadingAnimation) {
|
|
3321
|
+
this.loadingAnimation.stop();
|
|
3322
|
+
this.loadingAnimation = undefined;
|
|
3323
|
+
}
|
|
3324
|
+
this.statusContainer.clear();
|
|
3325
|
+
// Clear UI state
|
|
3326
|
+
this.pendingMessagesContainer.clear();
|
|
3327
|
+
this.compactionQueuedMessages = [];
|
|
3328
|
+
this.streamingComponent = undefined;
|
|
3329
|
+
this.streamingMessage = undefined;
|
|
3330
|
+
this.pendingTools.clear();
|
|
3331
|
+
const success = await this.session.importFromJsonl(inputPath);
|
|
3332
|
+
if (!success) {
|
|
3333
|
+
this.showWarning("Import cancelled");
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
// Clear and re-render the chat
|
|
3337
|
+
this.chatContainer.clear();
|
|
3338
|
+
this.renderInitialMessages();
|
|
3339
|
+
this.showStatus(`Session imported from: ${inputPath}`);
|
|
3340
|
+
}
|
|
3341
|
+
catch (error) {
|
|
3342
|
+
this.showError(`Failed to import session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3208
3345
|
async handleShareCommand() {
|
|
3209
3346
|
// Check if gh is available and logged in
|
|
3210
3347
|
try {
|
|
@@ -3292,14 +3429,14 @@ export class InteractiveMode {
|
|
|
3292
3429
|
}
|
|
3293
3430
|
}
|
|
3294
3431
|
}
|
|
3295
|
-
handleCopyCommand() {
|
|
3432
|
+
async handleCopyCommand() {
|
|
3296
3433
|
const text = this.session.getLastAssistantText();
|
|
3297
3434
|
if (!text) {
|
|
3298
3435
|
this.showError("No agent messages to copy yet.");
|
|
3299
3436
|
return;
|
|
3300
3437
|
}
|
|
3301
3438
|
try {
|
|
3302
|
-
copyToClipboard(text);
|
|
3439
|
+
await copyToClipboard(text);
|
|
3303
3440
|
this.showStatus("Copied last agent message to clipboard");
|
|
3304
3441
|
}
|
|
3305
3442
|
catch (error) {
|
|
@@ -3402,6 +3539,10 @@ export class InteractiveMode {
|
|
|
3402
3539
|
}
|
|
3403
3540
|
handleHotkeysCommand() {
|
|
3404
3541
|
// Navigation keybindings
|
|
3542
|
+
const cursorUp = this.getEditorKeyDisplay("cursorUp");
|
|
3543
|
+
const cursorDown = this.getEditorKeyDisplay("cursorDown");
|
|
3544
|
+
const cursorLeft = this.getEditorKeyDisplay("cursorLeft");
|
|
3545
|
+
const cursorRight = this.getEditorKeyDisplay("cursorRight");
|
|
3405
3546
|
const cursorWordLeft = this.getEditorKeyDisplay("cursorWordLeft");
|
|
3406
3547
|
const cursorWordRight = this.getEditorKeyDisplay("cursorWordRight");
|
|
3407
3548
|
const cursorLineStart = this.getEditorKeyDisplay("cursorLineStart");
|
|
@@ -3432,13 +3573,15 @@ export class InteractiveMode {
|
|
|
3432
3573
|
const expandTools = this.getAppKeyDisplay("expandTools");
|
|
3433
3574
|
const toggleThinking = this.getAppKeyDisplay("toggleThinking");
|
|
3434
3575
|
const externalEditor = this.getAppKeyDisplay("externalEditor");
|
|
3576
|
+
const cycleModelBackward = this.getAppKeyDisplay("cycleModelBackward");
|
|
3435
3577
|
const followUp = this.getAppKeyDisplay("followUp");
|
|
3436
3578
|
const dequeue = this.getAppKeyDisplay("dequeue");
|
|
3579
|
+
const pasteImage = this.getAppKeyDisplay("pasteImage");
|
|
3437
3580
|
let hotkeys = `
|
|
3438
3581
|
**Navigation**
|
|
3439
3582
|
| Key | Action |
|
|
3440
3583
|
|-----|--------|
|
|
3441
|
-
| \`
|
|
3584
|
+
| \`${cursorUp}\` / \`${cursorDown}\` / \`${cursorLeft}\` / \`${cursorRight}\` | Move cursor / browse history (Up when empty) |
|
|
3442
3585
|
| \`${cursorWordLeft}\` / \`${cursorWordRight}\` | Move by word |
|
|
3443
3586
|
| \`${cursorLineStart}\` | Start of line |
|
|
3444
3587
|
| \`${cursorLineEnd}\` | End of line |
|
|
@@ -3468,14 +3611,14 @@ export class InteractiveMode {
|
|
|
3468
3611
|
| \`${exit}\` | Exit (when editor is empty) |
|
|
3469
3612
|
| \`${suspend}\` | Suspend to background |
|
|
3470
3613
|
| \`${cycleThinkingLevel}\` | Cycle thinking level |
|
|
3471
|
-
| \`${cycleModelForward}\` | Cycle models |
|
|
3614
|
+
| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
|
|
3472
3615
|
| \`${selectModel}\` | Open model selector |
|
|
3473
3616
|
| \`${expandTools}\` | Toggle tool output expansion |
|
|
3474
3617
|
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
3475
3618
|
| \`${externalEditor}\` | Edit message in external editor |
|
|
3476
3619
|
| \`${followUp}\` | Queue follow-up message |
|
|
3477
3620
|
| \`${dequeue}\` | Restore queued messages |
|
|
3478
|
-
| \`
|
|
3621
|
+
| \`${pasteImage}\` | Paste image from clipboard |
|
|
3479
3622
|
| \`/\` | Slash commands |
|
|
3480
3623
|
| \`!\` | Run bash command |
|
|
3481
3624
|
| \`!!\` | Run bash command (excluded from context) |
|
|
@@ -3515,6 +3658,7 @@ export class InteractiveMode {
|
|
|
3515
3658
|
// New session via session (emits extension session events)
|
|
3516
3659
|
await this.session.newSession();
|
|
3517
3660
|
// Clear UI state
|
|
3661
|
+
this.headerContainer.clear();
|
|
3518
3662
|
this.chatContainer.clear();
|
|
3519
3663
|
this.pendingMessagesContainer.clear();
|
|
3520
3664
|
this.compactionQueuedMessages = [];
|