lsd-pi 1.3.2 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2 -1
- package/dist/lsd-settings-manager.d.ts +2 -0
- package/dist/lsd-settings-manager.js +5 -0
- package/dist/resource-loader.js +33 -3
- package/dist/resources/extensions/browser-tools/tools/codegen.js +5 -5
- package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
- package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
- package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
- package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
- package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
- package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
- package/dist/resources/extensions/browser-tools/utils.js +1 -1
- package/dist/resources/extensions/cache-timer/index.js +3 -2
- package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
- package/dist/resources/extensions/slash-commands/fast.js +73 -0
- package/dist/resources/extensions/slash-commands/index.js +2 -0
- package/dist/resources/extensions/slash-commands/plan.js +37 -12
- package/dist/resources/extensions/subagent/background-job-manager.js +13 -0
- package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
- package/dist/resources/extensions/subagent/index.js +278 -626
- package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
- package/dist/resources/extensions/voice/index.js +96 -36
- package/dist/resources/extensions/voice/push-to-talk.js +26 -0
- package/dist/welcome-screen.js +2 -2
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +19 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +16 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.ts +32 -2
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +34 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -4
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts +8 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
- package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +2 -0
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +5 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +47 -4
- package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
- package/packages/pi-ai/src/providers/openai-responses.ts +26 -3
- package/packages/pi-ai/src/providers/simple-options.ts +2 -0
- package/packages/pi-ai/src/types.ts +5 -0
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -2
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js +35 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js +24 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
- package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +21 -0
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +34 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +7 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +86 -28
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +23 -10
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +52 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +19 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +127 -14
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +14 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +93 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +328 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +123 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +9 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +103 -23
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -4
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +4 -2
- package/packages/pi-coding-agent/src/core/settings-manager.collapse-tool-calls.test.ts +46 -0
- package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +36 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -1
- package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
- package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +20 -0
- package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +26 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +41 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +105 -28
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +21 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1262 -1138
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +120 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +396 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +530 -398
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +4 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +109 -23
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -4
- package/packages/pi-tui/dist/components/editor.js +3 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +3 -3
- package/pkg/dist/modes/interactive/theme/themes.js +4 -4
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
- package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
- package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
- package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
- package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
- package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
- package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
- package/src/resources/extensions/browser-tools/utils.ts +1 -1
- package/src/resources/extensions/cache-timer/index.ts +3 -2
- package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
- package/src/resources/extensions/slash-commands/fast.ts +89 -0
- package/src/resources/extensions/slash-commands/index.ts +2 -0
- package/src/resources/extensions/slash-commands/plan.ts +42 -12
- package/src/resources/extensions/subagent/background-job-manager.ts +28 -0
- package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
- package/src/resources/extensions/subagent/index.ts +489 -799
- package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
- package/src/resources/extensions/voice/index.ts +308 -238
- package/src/resources/extensions/voice/push-to-talk.ts +42 -0
- package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
|
|
7
|
+
import { SettingsManager } from "./settings-manager.js";
|
|
8
|
+
|
|
9
|
+
describe("SettingsManager fastMode", () => {
|
|
10
|
+
it("defaults fastMode to false", () => {
|
|
11
|
+
const manager = SettingsManager.inMemory();
|
|
12
|
+
assert.equal(manager.getFastMode(), false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("persists fastMode changes to settings.json", async () => {
|
|
16
|
+
const root = mkdtempSync(join(tmpdir(), "pi-fast-mode-settings-"));
|
|
17
|
+
const cwd = join(root, "project");
|
|
18
|
+
const agentDir = join(root, "agent");
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const manager = SettingsManager.create(cwd, agentDir);
|
|
22
|
+
assert.equal(manager.getFastMode(), false);
|
|
23
|
+
|
|
24
|
+
manager.setFastMode(true);
|
|
25
|
+
await manager.flush();
|
|
26
|
+
|
|
27
|
+
const rawEnabled = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as {
|
|
28
|
+
fastMode?: boolean;
|
|
29
|
+
};
|
|
30
|
+
assert.equal(rawEnabled.fastMode, true);
|
|
31
|
+
|
|
32
|
+
const reloaded = SettingsManager.create(cwd, agentDir);
|
|
33
|
+
assert.equal(reloaded.getFastMode(), true);
|
|
34
|
+
|
|
35
|
+
reloaded.setFastMode(false);
|
|
36
|
+
await reloaded.flush();
|
|
37
|
+
|
|
38
|
+
const rawDisabled = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8")) as {
|
|
39
|
+
fastMode?: boolean;
|
|
40
|
+
};
|
|
41
|
+
assert.equal(rawDisabled.fastMode, false);
|
|
42
|
+
} finally {
|
|
43
|
+
rmSync(root, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -159,7 +159,9 @@ export interface Settings {
|
|
|
159
159
|
images?: ImageSettings;
|
|
160
160
|
enabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)
|
|
161
161
|
codexRotate?: boolean; // Enable the bundled codex-rotate extension (default: false)
|
|
162
|
+
fastMode?: boolean; // Enable OpenAI/Codex fast tier routing (priority service tier where supported)
|
|
162
163
|
cacheTimer?: boolean; // Show elapsed time since last response in the footer (default: true)
|
|
164
|
+
verboseFooter?: boolean; // Show extra footer usage stats like input/output/cache tokens (default: false)
|
|
163
165
|
pinLastPrompt?: boolean; // Pin last sent prompt above the editor as a reminder (default: false)
|
|
164
166
|
doubleEscapeAction?: "fork" | "tree" | "none"; // Action for double-escape with empty editor (default: "tree")
|
|
165
167
|
treeFilterMode?: "default" | "no-tools" | "user-only" | "labeled-only" | "all"; // Default filter when opening /tree
|
|
@@ -180,12 +182,14 @@ export interface Settings {
|
|
|
180
182
|
editMode?: "standard" | "hashline"; // Edit tool mode: "standard" (text match) or "hashline" (LINE#ID anchors). Default: "standard"
|
|
181
183
|
timestampFormat?: "date-time-iso" | "date-time-us"; // Timestamp display format for messages. Default: "date-time-iso"
|
|
182
184
|
toolOutputMode?: "minimal" | "normal"; // Collapsed tool rendering mode. "minimal" hides previews until expanded.
|
|
185
|
+
collapseToolCalls?: boolean; // default: false — group low-priority tool calls into collapsed summary lines
|
|
183
186
|
lspAutoInstall?: boolean; // default: false — whether to auto-install missing language servers during onboarding
|
|
184
187
|
lspInstalledServers?: string[]; // list of server names installed via the onboarding wizard
|
|
185
188
|
rtk?: boolean; // default: false — enable RTK shell-command compression (requires restart)
|
|
186
189
|
editorScheme?: "auto" | "vscode" | "cursor" | "zed" | "jetbrains" | "sublime" | "file"; // URI scheme for Cmd+click file links (default: "auto")
|
|
187
190
|
autoDream?: boolean; // default: false — enable automatic memory consolidation (dream) after sessions
|
|
188
191
|
autoMemory?: boolean; // default: false — enable automatic memory extraction from session transcripts
|
|
192
|
+
notificationSound?: boolean; // default: false — play terminal bell when agent finishes responding
|
|
189
193
|
telegramLiveRelayAutoConnect?: boolean; // default: false — auto-run /lsd telegram connect on startup
|
|
190
194
|
}
|
|
191
195
|
|
|
@@ -1049,6 +1053,14 @@ export class SettingsManager {
|
|
|
1049
1053
|
this.setGlobalSetting("toolOutputMode", mode);
|
|
1050
1054
|
}
|
|
1051
1055
|
|
|
1056
|
+
getCollapseToolCalls(): boolean {
|
|
1057
|
+
return this.settings.collapseToolCalls ?? false;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
setCollapseToolCalls(enabled: boolean): void {
|
|
1061
|
+
this.setGlobalSetting("collapseToolCalls", enabled);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1052
1064
|
getPackages(): PackageSource[] {
|
|
1053
1065
|
return [...(this.settings.packages ?? [])];
|
|
1054
1066
|
}
|
|
@@ -1187,6 +1199,14 @@ export class SettingsManager {
|
|
|
1187
1199
|
this.setGlobalSetting("codexRotate", enabled);
|
|
1188
1200
|
}
|
|
1189
1201
|
|
|
1202
|
+
getFastMode(): boolean {
|
|
1203
|
+
return this.settings.fastMode ?? false;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
setFastMode(enabled: boolean): void {
|
|
1207
|
+
this.setGlobalSetting("fastMode", enabled);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1190
1210
|
getCacheTimer(): boolean {
|
|
1191
1211
|
return this.settings.cacheTimer ?? true;
|
|
1192
1212
|
}
|
|
@@ -1195,6 +1215,14 @@ export class SettingsManager {
|
|
|
1195
1215
|
this.setGlobalSetting("cacheTimer", enabled);
|
|
1196
1216
|
}
|
|
1197
1217
|
|
|
1218
|
+
getVerboseFooter(): boolean {
|
|
1219
|
+
return this.settings.verboseFooter ?? false;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
setVerboseFooter(enabled: boolean): void {
|
|
1223
|
+
this.setGlobalSetting("verboseFooter", enabled);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1198
1226
|
getPinLastPrompt(): boolean {
|
|
1199
1227
|
return this.settings.pinLastPrompt ?? false;
|
|
1200
1228
|
}
|
|
@@ -1445,6 +1473,14 @@ export class SettingsManager {
|
|
|
1445
1473
|
this.setGlobalSetting("autoMemory", enabled);
|
|
1446
1474
|
}
|
|
1447
1475
|
|
|
1476
|
+
getNotificationSound(): boolean {
|
|
1477
|
+
return this.settings.notificationSound ?? false;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
setNotificationSound(enabled: boolean): void {
|
|
1481
|
+
this.setGlobalSetting("notificationSound", enabled);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1448
1484
|
getTelegramLiveRelayAutoConnect(): boolean {
|
|
1449
1485
|
return this.settings.telegramLiveRelayAutoConnect ?? false;
|
|
1450
1486
|
}
|
|
@@ -27,6 +27,7 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
27
27
|
{ name: "session", description: "Show session info and stats" },
|
|
28
28
|
{ name: "changelog", description: "Show changelog entries" },
|
|
29
29
|
{ name: "hotkeys", description: "Show all keyboard shortcuts" },
|
|
30
|
+
{ name: "btw", description: "Ask a side question without interrupting the current task" },
|
|
30
31
|
{ name: "fork", description: "Create a new fork from a previous message" },
|
|
31
32
|
{ name: "tree", description: "Navigate session tree (switch branches)" },
|
|
32
33
|
{ name: "provider", description: "Manage provider configuration" },
|
|
@@ -149,7 +149,12 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
|
|
|
149
149
|
const hasLsp = tools.includes("lsp");
|
|
150
150
|
|
|
151
151
|
// Priority-ordered compact guidelines
|
|
152
|
-
addGuideline("
|
|
152
|
+
addGuideline("Terse output. All technical substance stays. Only fluff dies.");
|
|
153
|
+
addGuideline("Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging (might be worth/could consider).");
|
|
154
|
+
addGuideline("Fragments OK. Short synonyms (big not extensive, fix not implement a solution for). Pattern: [thing] [action] [reason]. [next step].");
|
|
155
|
+
addGuideline("Never announce what you're about to do — just do it. Never summarize what you just did unless the user asks.");
|
|
156
|
+
addGuideline("Code blocks, error messages, file paths, and technical terms stay exact. Only compress prose.");
|
|
157
|
+
addGuideline("For security warnings, irreversible actions, or multi-step sequences where fragments risk misread — use full clear sentences. Resume terse after.");
|
|
153
158
|
addGuideline("For conceptual, product, or UX questions, answer first; inspect code only if implementation detail is needed.");
|
|
154
159
|
|
|
155
160
|
const hasSubagent = tools.includes("subagent");
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
|
|
4
|
+
import { getToolPriority, shouldCollapse } from "./tool-priority.js";
|
|
5
|
+
|
|
6
|
+
describe("tool priority", () => {
|
|
7
|
+
it("keeps edit and write always visible", () => {
|
|
8
|
+
assert.equal(getToolPriority("edit"), "always");
|
|
9
|
+
assert.equal(getToolPriority("write"), "always");
|
|
10
|
+
assert.equal(shouldCollapse("edit", false), false);
|
|
11
|
+
assert.equal(shouldCollapse("write", true), false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("collapses bash and bg_shell only on success", () => {
|
|
15
|
+
assert.equal(getToolPriority("bash"), "on-error");
|
|
16
|
+
assert.equal(getToolPriority("bg_shell"), "on-error");
|
|
17
|
+
assert.equal(shouldCollapse("bash", false), true);
|
|
18
|
+
assert.equal(shouldCollapse("bash", true), false);
|
|
19
|
+
assert.equal(shouldCollapse("bg_shell", false), true);
|
|
20
|
+
assert.equal(shouldCollapse("bg_shell", true), false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("collapses read, grep, and unknown tools", () => {
|
|
24
|
+
assert.equal(getToolPriority("read"), "collapse");
|
|
25
|
+
assert.equal(getToolPriority("grep"), "collapse");
|
|
26
|
+
assert.equal(getToolPriority("unknown-tool"), "collapse");
|
|
27
|
+
assert.equal(shouldCollapse("read", false), true);
|
|
28
|
+
assert.equal(shouldCollapse("unknown-tool", true), true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ToolPriority = "always" | "on-error" | "collapse";
|
|
2
|
+
|
|
3
|
+
const ALWAYS_VISIBLE = new Set(["edit", "write"]);
|
|
4
|
+
const ON_ERROR = new Set(["bash", "bg_shell"]);
|
|
5
|
+
|
|
6
|
+
export function getToolPriority(toolName: string): ToolPriority {
|
|
7
|
+
if (ALWAYS_VISIBLE.has(toolName)) return "always";
|
|
8
|
+
if (ON_ERROR.has(toolName)) return "on-error";
|
|
9
|
+
return "collapse";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function shouldCollapse(toolName: string, isError: boolean): boolean {
|
|
13
|
+
const priority = getToolPriority(toolName);
|
|
14
|
+
if (priority === "always") return false;
|
|
15
|
+
if (priority === "on-error") return !isError;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
@@ -6,6 +6,7 @@ import { describe, it } from "node:test";
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
computeEditDiff,
|
|
9
|
+
computeWriteDiff,
|
|
9
10
|
fuzzyFindText,
|
|
10
11
|
generateDiffString,
|
|
11
12
|
normalizeForFuzzyMatch,
|
|
@@ -60,6 +61,25 @@ describe("edit-diff", () => {
|
|
|
60
61
|
assert.match(result.diff, /CHANGED/);
|
|
61
62
|
});
|
|
62
63
|
|
|
64
|
+
it("computes diffs for write preview against existing files", async (t) => {
|
|
65
|
+
const dir = mkdtempSync(join(tmpdir(), "write-diff-test-"));
|
|
66
|
+
t.after(() => {
|
|
67
|
+
rmSync(dir, { recursive: true, force: true });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const file = join(dir, "sample.ts");
|
|
71
|
+
writeFileSync(file, "const title = \"Hello\";\n", "utf-8");
|
|
72
|
+
|
|
73
|
+
const result = await computeWriteDiff(file, "const title = \"Hi\";\n", dir);
|
|
74
|
+
|
|
75
|
+
assert.ok(!("error" in result), "expected a diff result");
|
|
76
|
+
if (!("error" in result)) {
|
|
77
|
+
assert.equal(result.firstChangedLine, 1);
|
|
78
|
+
assert.match(result.diff, /-1 const title = "Hello";/);
|
|
79
|
+
assert.match(result.diff, /\+1 const title = "Hi";/);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
63
83
|
it("computes diffs for preview without native helpers", async (t) => {
|
|
64
84
|
const dir = mkdtempSync(join(tmpdir(), "edit-diff-test-"));
|
|
65
85
|
t.after(() => {
|
|
@@ -329,6 +329,32 @@ function buildLineDiffLinear(oldLines: string[], newLines: string[]): LineDiffOp
|
|
|
329
329
|
return ops;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Compute the diff for a write operation without applying it.
|
|
334
|
+
* Used for preview rendering in the TUI before the tool executes.
|
|
335
|
+
*/
|
|
336
|
+
export async function computeWriteDiff(
|
|
337
|
+
path: string,
|
|
338
|
+
newContent: string,
|
|
339
|
+
cwd: string,
|
|
340
|
+
): Promise<EditDiffResult | EditDiffError> {
|
|
341
|
+
const absolutePath = resolveToCwd(path, cwd);
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
let oldContent = "";
|
|
345
|
+
try {
|
|
346
|
+
await access(absolutePath, constants.R_OK);
|
|
347
|
+
oldContent = await readFile(absolutePath, "utf-8");
|
|
348
|
+
} catch {
|
|
349
|
+
oldContent = "";
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return generateDiffString(normalizeToLF(stripBom(oldContent).text), normalizeToLF(newContent));
|
|
353
|
+
} catch (err) {
|
|
354
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
332
358
|
/**
|
|
333
359
|
* Compute the diff for an edit operation without applying it.
|
|
334
360
|
* Used for preview rendering in the TUI before the tool executes.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import stripAnsi from "strip-ansi";
|
|
4
|
+
|
|
5
|
+
import { ToolSummaryLine } from "../tool-summary-line.js";
|
|
6
|
+
import { initTheme } from "../../theme/theme.js";
|
|
7
|
+
|
|
8
|
+
initTheme("dark");
|
|
9
|
+
|
|
10
|
+
describe("ToolSummaryLine", () => {
|
|
11
|
+
it("renders action-based summaries for grouped identical tools", () => {
|
|
12
|
+
const summary = new ToolSummaryLine();
|
|
13
|
+
summary.addTool("read", 600);
|
|
14
|
+
summary.addTool("read", 150);
|
|
15
|
+
|
|
16
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
17
|
+
assert.match(rendered, /^ ● /);
|
|
18
|
+
assert.ok(rendered.includes("reading 2 files · 0.8s"));
|
|
19
|
+
assert.equal(summary.canGroupWith("read"), true);
|
|
20
|
+
assert.equal(summary.canGroupWith("find"), false);
|
|
21
|
+
assert.equal(rendered.includes("collapsed tools"), false);
|
|
22
|
+
assert.equal(rendered.includes("⎯"), false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("keeps fallback format for unknown tools", () => {
|
|
26
|
+
const summary = new ToolSummaryLine();
|
|
27
|
+
summary.addTool("custom_tool", 100);
|
|
28
|
+
|
|
29
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
30
|
+
assert.ok(rendered.includes("custom_tool · 0.1s"));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("renders nothing when empty or hidden", () => {
|
|
34
|
+
const summary = new ToolSummaryLine();
|
|
35
|
+
assert.deepEqual(summary.render(80), []);
|
|
36
|
+
|
|
37
|
+
summary.addTool("grep", 100);
|
|
38
|
+
summary.setHidden(true);
|
|
39
|
+
assert.deepEqual(summary.render(80), []);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import stripAnsi from "strip-ansi";
|
|
4
|
+
import type { Context, Model, SimpleStreamOptions, Message } from "@gsd/pi-ai";
|
|
5
|
+
import type { TUI } from "@gsd/pi-tui";
|
|
6
|
+
import { BtwOverlayComponent } from "./btw-overlay.js";
|
|
7
|
+
import { getMarkdownTheme, initTheme } from "../theme/theme.js";
|
|
8
|
+
|
|
9
|
+
initTheme();
|
|
10
|
+
|
|
11
|
+
function makeUi(rows = 40): TUI {
|
|
12
|
+
return { terminal: { rows } } as unknown as TUI;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function makeModel(): Model<any> {
|
|
16
|
+
return {
|
|
17
|
+
id: "test-model",
|
|
18
|
+
name: "Test Model",
|
|
19
|
+
api: "openai-completions",
|
|
20
|
+
provider: "test",
|
|
21
|
+
baseUrl: "",
|
|
22
|
+
reasoning: false,
|
|
23
|
+
input: ["text"],
|
|
24
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
25
|
+
contextWindow: 4096,
|
|
26
|
+
maxTokens: 1024,
|
|
27
|
+
} as unknown as Model<any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function makeMessages(): Message[] {
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
role: "user",
|
|
34
|
+
content: [{ type: "text", text: "hello" }],
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function renderText(component: BtwOverlayComponent): string {
|
|
41
|
+
return stripAnsi(component.render(80).join("\n"));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function flushTurns(count = 3): Promise<void> {
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
await Promise.resolve();
|
|
47
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test("BtwOverlayComponent renders initial question and placeholder before response", async () => {
|
|
52
|
+
const component = new BtwOverlayComponent(
|
|
53
|
+
"What is btw?",
|
|
54
|
+
makeModel(),
|
|
55
|
+
"system",
|
|
56
|
+
makeMessages(),
|
|
57
|
+
getMarkdownTheme(),
|
|
58
|
+
makeUi(),
|
|
59
|
+
() => undefined,
|
|
60
|
+
() => undefined,
|
|
61
|
+
() => ({
|
|
62
|
+
async *[Symbol.asyncIterator]() {
|
|
63
|
+
await new Promise(() => undefined);
|
|
64
|
+
},
|
|
65
|
+
}) as any,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
await flushTurns();
|
|
69
|
+
const text = renderText(component);
|
|
70
|
+
assert.match(text, /btw/);
|
|
71
|
+
assert.match(text, /You/);
|
|
72
|
+
assert.match(text, /What is btw\?/);
|
|
73
|
+
assert.match(text, /Awaiting response/);
|
|
74
|
+
assert.match(text, /Ask follow-up/);
|
|
75
|
+
component.dispose();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("BtwOverlayComponent supports follow-up turns and reuses overlay-local history", async () => {
|
|
79
|
+
const calls: Array<{ context: Context; options?: SimpleStreamOptions }> = [];
|
|
80
|
+
const responses = [
|
|
81
|
+
[
|
|
82
|
+
{ type: "text_delta", delta: "First answer" },
|
|
83
|
+
{ type: "done" },
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
{ type: "text_delta", delta: "Second answer" },
|
|
87
|
+
{ type: "done" },
|
|
88
|
+
],
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const streamFn = ((_: Model<any>, context: Context, options?: SimpleStreamOptions) => {
|
|
92
|
+
calls.push({ context, options });
|
|
93
|
+
const response = responses[calls.length - 1] ?? [];
|
|
94
|
+
return {
|
|
95
|
+
async *[Symbol.asyncIterator]() {
|
|
96
|
+
for (const event of response) {
|
|
97
|
+
yield event;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}) as any;
|
|
102
|
+
|
|
103
|
+
const component = new BtwOverlayComponent(
|
|
104
|
+
"What is btw?",
|
|
105
|
+
makeModel(),
|
|
106
|
+
"system",
|
|
107
|
+
makeMessages(),
|
|
108
|
+
getMarkdownTheme(),
|
|
109
|
+
makeUi(),
|
|
110
|
+
() => undefined,
|
|
111
|
+
() => undefined,
|
|
112
|
+
streamFn,
|
|
113
|
+
{ apiKey: "test-key", sessionId: "session-1" },
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
await flushTurns();
|
|
117
|
+
for (const ch of "next") {
|
|
118
|
+
component.handleInput(ch);
|
|
119
|
+
}
|
|
120
|
+
component.handleInput("\n");
|
|
121
|
+
await flushTurns();
|
|
122
|
+
|
|
123
|
+
assert.equal(calls.length, 2);
|
|
124
|
+
assert.equal(calls[0]?.options?.apiKey, "test-key");
|
|
125
|
+
assert.equal(calls[1]?.options?.sessionId, "session-1");
|
|
126
|
+
assert.equal(calls[0]?.context.messages.length, 2);
|
|
127
|
+
assert.equal(calls[1]?.context.messages.length, 4);
|
|
128
|
+
assert.equal(calls[1]?.context.messages[1]?.role, "user");
|
|
129
|
+
assert.equal(calls[1]?.context.messages[2]?.role, "assistant");
|
|
130
|
+
assert.deepEqual(calls[1]?.context.messages[3]?.content, [{ type: "text", text: "next" }]);
|
|
131
|
+
|
|
132
|
+
const text = renderText(component);
|
|
133
|
+
assert.match(text, /First answer/);
|
|
134
|
+
assert.match(text, /Second answer/);
|
|
135
|
+
component.dispose();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("BtwOverlayComponent aborts active stream on Escape", async () => {
|
|
139
|
+
let capturedSignal: AbortSignal | undefined;
|
|
140
|
+
let dismissed = false;
|
|
141
|
+
const streamFn = ((_model: any, _context: any, options?: { signal?: AbortSignal }) => {
|
|
142
|
+
capturedSignal = options?.signal;
|
|
143
|
+
return {
|
|
144
|
+
async *[Symbol.asyncIterator]() {
|
|
145
|
+
await new Promise<void>((resolve) => {
|
|
146
|
+
capturedSignal?.addEventListener("abort", () => resolve(), { once: true });
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}) as any;
|
|
151
|
+
|
|
152
|
+
const component = new BtwOverlayComponent(
|
|
153
|
+
"What is btw?",
|
|
154
|
+
makeModel(),
|
|
155
|
+
"system",
|
|
156
|
+
makeMessages(),
|
|
157
|
+
getMarkdownTheme(),
|
|
158
|
+
makeUi(),
|
|
159
|
+
() => {
|
|
160
|
+
dismissed = true;
|
|
161
|
+
},
|
|
162
|
+
() => undefined,
|
|
163
|
+
streamFn,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
await flushTurns();
|
|
167
|
+
component.handleInput("\u001b");
|
|
168
|
+
await flushTurns();
|
|
169
|
+
|
|
170
|
+
assert.equal(dismissed, true);
|
|
171
|
+
assert.equal(capturedSignal?.aborted, true);
|
|
172
|
+
});
|