@codex-infinity/pi-infinity 0.52.2 → 0.60.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 +386 -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 +28 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +289 -69
- 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/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 +126 -43
- 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 +1 -1
- 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 +75 -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 +4 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +160 -66
- 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/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.d.ts.map +1 -1
- package/dist/utils/clipboard.js +16 -15
- 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);
|
|
@@ -1720,7 +1803,6 @@ export class InteractiveMode {
|
|
|
1720
1803
|
for (const content of this.streamingMessage.content) {
|
|
1721
1804
|
if (content.type === "toolCall") {
|
|
1722
1805
|
if (!this.pendingTools.has(content.id)) {
|
|
1723
|
-
this.chatContainer.addChild(new Text("", 0, 0));
|
|
1724
1806
|
const component = new ToolExecutionComponent(content.name, content.arguments, {
|
|
1725
1807
|
showImages: this.settingsManager.getShowImages(),
|
|
1726
1808
|
}, this.getRegisteredToolDefinition(content.name), this.ui);
|
|
@@ -2153,8 +2235,13 @@ export class InteractiveMode {
|
|
|
2153
2235
|
await this.shutdown();
|
|
2154
2236
|
}
|
|
2155
2237
|
handleCtrlZ() {
|
|
2238
|
+
// Ignore SIGINT while suspended so Ctrl+C in the terminal does not
|
|
2239
|
+
// kill the backgrounded process. The handler is removed on resume.
|
|
2240
|
+
const ignoreSigint = () => { };
|
|
2241
|
+
process.on("SIGINT", ignoreSigint);
|
|
2156
2242
|
// Set up handler to restore TUI when resumed
|
|
2157
2243
|
process.once("SIGCONT", () => {
|
|
2244
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2158
2245
|
this.ui.start();
|
|
2159
2246
|
this.ui.requestRender(true);
|
|
2160
2247
|
});
|
|
@@ -2286,6 +2373,7 @@ export class InteractiveMode {
|
|
|
2286
2373
|
// Spawn editor synchronously with inherited stdio for interactive editing
|
|
2287
2374
|
const result = spawnSync(editor, [...editorArgs, tmpFile], {
|
|
2288
2375
|
stdio: "inherit",
|
|
2376
|
+
shell: process.platform === "win32",
|
|
2289
2377
|
});
|
|
2290
2378
|
// On successful exit (status 0), replace editor content
|
|
2291
2379
|
if (result.status === 0) {
|
|
@@ -2336,6 +2424,16 @@ export class InteractiveMode {
|
|
|
2336
2424
|
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2337
2425
|
this.ui.requestRender();
|
|
2338
2426
|
}
|
|
2427
|
+
showPackageUpdateNotification(packages) {
|
|
2428
|
+
const action = theme.fg("accent", `${APP_NAME} update`);
|
|
2429
|
+
const updateInstruction = theme.fg("muted", "Package updates are available. Run ") + action;
|
|
2430
|
+
const packageLines = packages.map((pkg) => `- ${pkg}`).join("\n");
|
|
2431
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2432
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2433
|
+
this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n${updateInstruction}\n${theme.fg("muted", "Packages:")}\n${packageLines}`, 1, 0));
|
|
2434
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2435
|
+
this.ui.requestRender();
|
|
2436
|
+
}
|
|
2339
2437
|
/**
|
|
2340
2438
|
* Get all queued messages (read-only).
|
|
2341
2439
|
* Combines session queue and compaction queue.
|
|
@@ -2539,6 +2637,7 @@ export class InteractiveMode {
|
|
|
2539
2637
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
2540
2638
|
collapseChangelog: this.settingsManager.getCollapseChangelog(),
|
|
2541
2639
|
doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
|
|
2640
|
+
treeFilterMode: this.settingsManager.getTreeFilterMode(),
|
|
2542
2641
|
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
2543
2642
|
editorPaddingX: this.settingsManager.getEditorPaddingX(),
|
|
2544
2643
|
autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
|
|
@@ -2617,6 +2716,9 @@ export class InteractiveMode {
|
|
|
2617
2716
|
onDoubleEscapeActionChange: (action) => {
|
|
2618
2717
|
this.settingsManager.setDoubleEscapeAction(action);
|
|
2619
2718
|
},
|
|
2719
|
+
onTreeFilterModeChange: (mode) => {
|
|
2720
|
+
this.settingsManager.setTreeFilterMode(mode);
|
|
2721
|
+
},
|
|
2620
2722
|
onShowHardwareCursorChange: (enabled) => {
|
|
2621
2723
|
this.settingsManager.setShowHardwareCursor(enabled);
|
|
2622
2724
|
this.ui.setShowHardwareCursor(enabled);
|
|
@@ -2669,28 +2771,8 @@ export class InteractiveMode {
|
|
|
2669
2771
|
this.showModelSelector(searchTerm);
|
|
2670
2772
|
}
|
|
2671
2773
|
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
2774
|
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;
|
|
2775
|
+
return findExactModelReferenceMatch(searchTerm, models);
|
|
2694
2776
|
}
|
|
2695
2777
|
async getModelCandidates() {
|
|
2696
2778
|
if (this.session.scopedModels.length > 0) {
|
|
@@ -2770,12 +2852,10 @@ export class InteractiveMode {
|
|
|
2770
2852
|
// Helper to update session's scoped models (session-only, no persist)
|
|
2771
2853
|
const updateSessionModels = async (enabledIds) => {
|
|
2772
2854
|
if (enabledIds.size > 0 && enabledIds.size < allModels.length) {
|
|
2773
|
-
// Use current session thinking level, not settings default
|
|
2774
|
-
const currentThinkingLevel = this.session.thinkingLevel;
|
|
2775
2855
|
const newScopedModels = await resolveModelScope(Array.from(enabledIds), this.session.modelRegistry);
|
|
2776
2856
|
this.session.setScopedModels(newScopedModels.map((sm) => ({
|
|
2777
2857
|
model: sm.model,
|
|
2778
|
-
thinkingLevel: sm.thinkingLevel
|
|
2858
|
+
thinkingLevel: sm.thinkingLevel,
|
|
2779
2859
|
})));
|
|
2780
2860
|
}
|
|
2781
2861
|
else {
|
|
@@ -2872,6 +2952,7 @@ export class InteractiveMode {
|
|
|
2872
2952
|
showTreeSelector(initialSelectedId) {
|
|
2873
2953
|
const tree = this.sessionManager.getTree();
|
|
2874
2954
|
const realLeafId = this.sessionManager.getLeafId();
|
|
2955
|
+
const initialFilterMode = this.settingsManager.getTreeFilterMode();
|
|
2875
2956
|
if (tree.length === 0) {
|
|
2876
2957
|
this.showStatus("No entries in session");
|
|
2877
2958
|
return;
|
|
@@ -2889,27 +2970,30 @@ export class InteractiveMode {
|
|
|
2889
2970
|
// Loop until user makes a complete choice or cancels to tree
|
|
2890
2971
|
let wantsSummary = false;
|
|
2891
2972
|
let customInstructions;
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
"Summarize",
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
if (summaryChoice === "Summarize with custom prompt") {
|
|
2905
|
-
customInstructions = await this.showExtensionEditor("Custom summarization instructions");
|
|
2906
|
-
if (customInstructions === undefined) {
|
|
2907
|
-
// User cancelled - loop back to summary selector
|
|
2908
|
-
continue;
|
|
2973
|
+
// Check if we should skip the prompt (user preference to always default to no summary)
|
|
2974
|
+
if (!this.settingsManager.getBranchSummarySkipPrompt()) {
|
|
2975
|
+
while (true) {
|
|
2976
|
+
const summaryChoice = await this.showExtensionSelector("Summarize branch?", [
|
|
2977
|
+
"No summary",
|
|
2978
|
+
"Summarize",
|
|
2979
|
+
"Summarize with custom prompt",
|
|
2980
|
+
]);
|
|
2981
|
+
if (summaryChoice === undefined) {
|
|
2982
|
+
// User pressed escape - re-show tree selector with same selection
|
|
2983
|
+
this.showTreeSelector(entryId);
|
|
2984
|
+
return;
|
|
2909
2985
|
}
|
|
2986
|
+
wantsSummary = summaryChoice !== "No summary";
|
|
2987
|
+
if (summaryChoice === "Summarize with custom prompt") {
|
|
2988
|
+
customInstructions = await this.showExtensionEditor("Custom summarization instructions");
|
|
2989
|
+
if (customInstructions === undefined) {
|
|
2990
|
+
// User cancelled - loop back to summary selector
|
|
2991
|
+
continue;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
// User made a complete choice
|
|
2995
|
+
break;
|
|
2910
2996
|
}
|
|
2911
|
-
// User made a complete choice
|
|
2912
|
-
break;
|
|
2913
2997
|
}
|
|
2914
2998
|
// Set up escape handler and loader if summarizing
|
|
2915
2999
|
let summaryLoader;
|
|
@@ -2962,7 +3046,7 @@ export class InteractiveMode {
|
|
|
2962
3046
|
}, (entryId, label) => {
|
|
2963
3047
|
this.sessionManager.appendLabelChange(entryId, label);
|
|
2964
3048
|
this.ui.requestRender();
|
|
2965
|
-
}, initialSelectedId);
|
|
3049
|
+
}, initialSelectedId, initialFilterMode);
|
|
2966
3050
|
return { component: selector, focus: selector };
|
|
2967
3051
|
});
|
|
2968
3052
|
}
|
|
@@ -3027,7 +3111,9 @@ export class InteractiveMode {
|
|
|
3027
3111
|
}
|
|
3028
3112
|
else {
|
|
3029
3113
|
// Logout flow
|
|
3030
|
-
const providerInfo =
|
|
3114
|
+
const providerInfo = this.session.modelRegistry.authStorage
|
|
3115
|
+
.getOAuthProviders()
|
|
3116
|
+
.find((p) => p.id === providerId);
|
|
3031
3117
|
const providerName = providerInfo?.name || providerId;
|
|
3032
3118
|
try {
|
|
3033
3119
|
this.session.modelRegistry.authStorage.logout(providerId);
|
|
@@ -3047,7 +3133,7 @@ export class InteractiveMode {
|
|
|
3047
3133
|
});
|
|
3048
3134
|
}
|
|
3049
3135
|
async showLoginDialog(providerId) {
|
|
3050
|
-
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
|
3136
|
+
const providerInfo = this.session.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);
|
|
3051
3137
|
const providerName = providerInfo?.name || providerId;
|
|
3052
3138
|
// Providers that use callback servers (can paste redirect URL)
|
|
3053
3139
|
const usesCallbackServer = providerInfo?.usesCallbackServer ?? false;
|
|
@@ -3137,7 +3223,7 @@ export class InteractiveMode {
|
|
|
3137
3223
|
return;
|
|
3138
3224
|
}
|
|
3139
3225
|
this.resetExtensionUI();
|
|
3140
|
-
const loader = new BorderedLoader(this.ui, theme, "Reloading extensions, skills, prompts, themes...", {
|
|
3226
|
+
const loader = new BorderedLoader(this.ui, theme, "Reloading keybindings, extensions, skills, prompts, themes...", {
|
|
3141
3227
|
cancellable: false,
|
|
3142
3228
|
});
|
|
3143
3229
|
const previousEditor = this.editor;
|
|
@@ -3154,6 +3240,7 @@ export class InteractiveMode {
|
|
|
3154
3240
|
};
|
|
3155
3241
|
try {
|
|
3156
3242
|
await this.session.reload();
|
|
3243
|
+
this.keybindings.reload();
|
|
3157
3244
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
3158
3245
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
3159
3246
|
const themeName = this.settingsManager.getTheme();
|
|
@@ -3187,7 +3274,7 @@ export class InteractiveMode {
|
|
|
3187
3274
|
if (modelsJsonError) {
|
|
3188
3275
|
this.showError(`models.json error: ${modelsJsonError}`);
|
|
3189
3276
|
}
|
|
3190
|
-
this.showStatus("Reloaded extensions, skills, prompts, themes");
|
|
3277
|
+
this.showStatus("Reloaded keybindings, extensions, skills, prompts, themes");
|
|
3191
3278
|
}
|
|
3192
3279
|
catch (error) {
|
|
3193
3280
|
dismissLoader(previousEditor);
|
|
@@ -3402,6 +3489,10 @@ export class InteractiveMode {
|
|
|
3402
3489
|
}
|
|
3403
3490
|
handleHotkeysCommand() {
|
|
3404
3491
|
// 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");
|
|
3405
3496
|
const cursorWordLeft = this.getEditorKeyDisplay("cursorWordLeft");
|
|
3406
3497
|
const cursorWordRight = this.getEditorKeyDisplay("cursorWordRight");
|
|
3407
3498
|
const cursorLineStart = this.getEditorKeyDisplay("cursorLineStart");
|
|
@@ -3432,13 +3523,15 @@ export class InteractiveMode {
|
|
|
3432
3523
|
const expandTools = this.getAppKeyDisplay("expandTools");
|
|
3433
3524
|
const toggleThinking = this.getAppKeyDisplay("toggleThinking");
|
|
3434
3525
|
const externalEditor = this.getAppKeyDisplay("externalEditor");
|
|
3526
|
+
const cycleModelBackward = this.getAppKeyDisplay("cycleModelBackward");
|
|
3435
3527
|
const followUp = this.getAppKeyDisplay("followUp");
|
|
3436
3528
|
const dequeue = this.getAppKeyDisplay("dequeue");
|
|
3529
|
+
const pasteImage = this.getAppKeyDisplay("pasteImage");
|
|
3437
3530
|
let hotkeys = `
|
|
3438
3531
|
**Navigation**
|
|
3439
3532
|
| Key | Action |
|
|
3440
3533
|
|-----|--------|
|
|
3441
|
-
| \`
|
|
3534
|
+
| \`${cursorUp}\` / \`${cursorDown}\` / \`${cursorLeft}\` / \`${cursorRight}\` | Move cursor / browse history (Up when empty) |
|
|
3442
3535
|
| \`${cursorWordLeft}\` / \`${cursorWordRight}\` | Move by word |
|
|
3443
3536
|
| \`${cursorLineStart}\` | Start of line |
|
|
3444
3537
|
| \`${cursorLineEnd}\` | End of line |
|
|
@@ -3468,14 +3561,14 @@ export class InteractiveMode {
|
|
|
3468
3561
|
| \`${exit}\` | Exit (when editor is empty) |
|
|
3469
3562
|
| \`${suspend}\` | Suspend to background |
|
|
3470
3563
|
| \`${cycleThinkingLevel}\` | Cycle thinking level |
|
|
3471
|
-
| \`${cycleModelForward}\` | Cycle models |
|
|
3564
|
+
| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
|
|
3472
3565
|
| \`${selectModel}\` | Open model selector |
|
|
3473
3566
|
| \`${expandTools}\` | Toggle tool output expansion |
|
|
3474
3567
|
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
3475
3568
|
| \`${externalEditor}\` | Edit message in external editor |
|
|
3476
3569
|
| \`${followUp}\` | Queue follow-up message |
|
|
3477
3570
|
| \`${dequeue}\` | Restore queued messages |
|
|
3478
|
-
| \`
|
|
3571
|
+
| \`${pasteImage}\` | Paste image from clipboard |
|
|
3479
3572
|
| \`/\` | Slash commands |
|
|
3480
3573
|
| \`!\` | Run bash command |
|
|
3481
3574
|
| \`!!\` | Run bash command (excluded from context) |
|
|
@@ -3515,6 +3608,7 @@ export class InteractiveMode {
|
|
|
3515
3608
|
// New session via session (emits extension session events)
|
|
3516
3609
|
await this.session.newSession();
|
|
3517
3610
|
// Clear UI state
|
|
3611
|
+
this.headerContainer.clear();
|
|
3518
3612
|
this.chatContainer.clear();
|
|
3519
3613
|
this.pendingMessagesContainer.clear();
|
|
3520
3614
|
this.compactionQueuedMessages = [];
|