pi-ui-extend 0.1.9 → 0.1.13
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/README.md +23 -2
- package/dist/app/app.d.ts +4 -0
- package/dist/app/app.js +76 -7
- package/dist/app/cli/install.d.ts +16 -0
- package/dist/app/cli/install.js +34 -7
- package/dist/app/cli/startup-info.js +5 -2
- package/dist/app/cli/update.d.ts +7 -0
- package/dist/app/cli/update.js +11 -3
- package/dist/app/commands/command-controller.js +4 -0
- package/dist/app/commands/command-host.d.ts +4 -0
- package/dist/app/commands/command-model-actions.d.ts +5 -0
- package/dist/app/commands/command-model-actions.js +104 -0
- package/dist/app/commands/command-navigation-actions.d.ts +6 -1
- package/dist/app/commands/command-navigation-actions.js +37 -14
- package/dist/app/commands/command-registry.d.ts +4 -0
- package/dist/app/commands/command-registry.js +32 -0
- package/dist/app/commands/command-session-actions.d.ts +1 -0
- package/dist/app/commands/command-session-actions.js +15 -5
- package/dist/app/commands/shell-command.d.ts +7 -0
- package/dist/app/commands/shell-command.js +12 -4
- package/dist/app/commands/shell-controller.d.ts +1 -0
- package/dist/app/commands/shell-controller.js +1 -1
- package/dist/app/constants.d.ts +1 -1
- package/dist/app/constants.js +1 -1
- package/dist/app/icons.d.ts +1 -0
- package/dist/app/icons.js +3 -1
- package/dist/app/input/autocomplete-controller.d.ts +52 -0
- package/dist/app/input/autocomplete-controller.js +352 -0
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +21 -0
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +2 -0
- package/dist/app/input/input-paste-handler.d.ts +1 -0
- package/dist/app/input/input-paste-handler.js +22 -18
- package/dist/app/input/prompt-enhancer-controller.d.ts +7 -1
- package/dist/app/input/prompt-enhancer-controller.js +12 -3
- package/dist/app/input/voice-controller.d.ts +51 -1
- package/dist/app/input/voice-controller.js +42 -19
- package/dist/app/model/model-usage-status.d.ts +9 -0
- package/dist/app/model/model-usage-status.js +124 -34
- package/dist/app/popup/popup-action-controller.js +1 -1
- package/dist/app/process.d.ts +17 -0
- package/dist/app/process.js +68 -0
- package/dist/app/rendering/conversation-entry-renderer.js +8 -6
- package/dist/app/rendering/conversation-tool-renderer.js +3 -2
- package/dist/app/rendering/editor-layout-renderer.d.ts +1 -0
- package/dist/app/rendering/editor-layout-renderer.js +11 -1
- package/dist/app/rendering/message-content.js +65 -7
- package/dist/app/rendering/render-controller.js +6 -1
- package/dist/app/rendering/render-text.d.ts +3 -0
- package/dist/app/rendering/render-text.js +51 -3
- package/dist/app/rendering/status-line-renderer.d.ts +5 -1
- package/dist/app/rendering/status-line-renderer.js +61 -25
- package/dist/app/rendering/toast-renderer.js +10 -13
- package/dist/app/rendering/tool-block-renderer.d.ts +1 -0
- package/dist/app/rendering/tool-block-renderer.js +16 -33
- package/dist/app/runtime.d.ts +6 -1
- package/dist/app/runtime.js +35 -2
- package/dist/app/screen/clipboard.d.ts +11 -2
- package/dist/app/screen/clipboard.js +29 -21
- package/dist/app/screen/file-link-opener.d.ts +8 -0
- package/dist/app/screen/file-link-opener.js +11 -3
- package/dist/app/screen/file-links.js +3 -3
- package/dist/app/screen/image-opener.d.ts +12 -0
- package/dist/app/screen/image-opener.js +13 -5
- package/dist/app/screen/mouse-controller.d.ts +5 -2
- package/dist/app/screen/mouse-controller.js +16 -1
- package/dist/app/screen/screen-styler.d.ts +4 -1
- package/dist/app/screen/screen-styler.js +3 -2
- package/dist/app/screen/status-controller.d.ts +3 -0
- package/dist/app/screen/status-controller.js +23 -8
- package/dist/app/session/queued-message-controller.d.ts +7 -1
- package/dist/app/session/queued-message-controller.js +36 -21
- package/dist/app/session/resume-session-loader.d.ts +15 -0
- package/dist/app/session/resume-session-loader.js +204 -0
- package/dist/app/session/session-event-controller.d.ts +5 -1
- package/dist/app/session/session-event-controller.js +72 -5
- package/dist/app/session/session-history.js +4 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +5 -0
- package/dist/app/session/session-lifecycle-controller.js +9 -1
- package/dist/app/session/tabs-controller.d.ts +10 -1
- package/dist/app/session/tabs-controller.js +101 -5
- package/dist/app/terminal/nerd-font-controller.d.ts +16 -0
- package/dist/app/terminal/nerd-font-controller.js +30 -23
- package/dist/app/terminal/terminal-controller.d.ts +1 -0
- package/dist/app/terminal/terminal-controller.js +1 -0
- package/dist/app/types.d.ts +14 -0
- package/dist/app/workspace/workspace-actions-controller.d.ts +1 -1
- package/dist/app/workspace/workspace-actions-controller.js +3 -3
- package/dist/app/workspace/workspace-undo.d.ts +1 -1
- package/dist/app/workspace/workspace-undo.js +22 -20
- package/dist/config.d.ts +27 -0
- package/dist/config.js +174 -1
- package/dist/default-pix-config.js +39 -353
- package/dist/input-editor.d.ts +7 -1
- package/dist/input-editor.js +47 -6
- package/dist/markdown-format.d.ts +1 -0
- package/dist/markdown-format.js +26 -1
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.js +5 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +177 -0
- package/dist/schemas/pi-tools-suite-schema.js +218 -0
- package/dist/schemas/pix-schema.d.ts +65 -0
- package/dist/schemas/pix-schema.js +91 -0
- package/dist/terminal-width.js +73 -56
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +3 -0
- package/external/pi-tools-suite/src/dcp/compression-blocks.ts +1 -0
- package/external/pi-tools-suite/src/dcp/prompts.ts +1 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +46 -195
- package/external/pi-tools-suite/src/lib/lsp.ts +2 -1
- package/external/pi-tools-suite/src/lsp/_shared/output.ts +8 -7
- package/external/pi-tools-suite/src/lsp/manager.ts +4 -4
- package/external/pi-tools-suite/src/repo-discovery/index.ts +49 -2
- package/external/pi-tools-suite/src/todo/index.ts +4 -2
- package/external/pi-tools-suite/src/todo/state/selectors.ts +4 -0
- package/external/pi-tools-suite/src/todo/todo.ts +2 -6
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +9 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +1 -1
- package/package.json +12 -3
- package/schemas/pi-tools-suite.json +881 -0
- package/schemas/pix.json +298 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getIdleRuntime, getRuntime } from "./command-runtime.js";
|
|
2
|
+
import { savePixAutocompleteModel, savePixDefaultModel, savePixDefaultThinking } from "../../config.js";
|
|
2
3
|
import { createId } from "../id.js";
|
|
3
4
|
import { isThinkingLevel, parseScopedModelRef } from "../model/model-ref.js";
|
|
4
5
|
export class ModelCommandActions {
|
|
@@ -22,6 +23,7 @@ export class ModelCommandActions {
|
|
|
22
23
|
`session: ${runtime.session.sessionFile ?? "in-memory"}`,
|
|
23
24
|
`model: ${currentModel}`,
|
|
24
25
|
`prompt enhancer model: ${this.host.promptEnhancerModelRef()}`,
|
|
26
|
+
`autocomplete model: ${this.host.autocompleteModelRef() || "disabled"}`,
|
|
25
27
|
`thinking: ${runtime.session.thinkingLevel}`,
|
|
26
28
|
`theme: ${settings.getTheme() ?? this.host.options.themeName}`,
|
|
27
29
|
`skill commands: ${settings.getEnableSkillCommands() ? "enabled" : "disabled"}`,
|
|
@@ -70,6 +72,59 @@ export class ModelCommandActions {
|
|
|
70
72
|
this.host.setSessionStatus(runtime.session);
|
|
71
73
|
}
|
|
72
74
|
}
|
|
75
|
+
async runDefaultModelSlashCommand(argumentsText) {
|
|
76
|
+
const modelRef = argumentsText.trim();
|
|
77
|
+
if (!modelRef) {
|
|
78
|
+
const selected = await this.host.showMenu(this.host.getModelMenuItems(""), {
|
|
79
|
+
title: "Select default model",
|
|
80
|
+
placeholder: "Search models",
|
|
81
|
+
emptyText: "No matching models",
|
|
82
|
+
});
|
|
83
|
+
if (!selected) {
|
|
84
|
+
this.host.setSessionStatus(this.host.runtime()?.session);
|
|
85
|
+
this.host.render();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.saveDefaultModel(this.host.modelRef(selected.model));
|
|
89
|
+
this.host.render();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const runtime = getRuntime(this.host, "default-model");
|
|
93
|
+
if (!runtime)
|
|
94
|
+
return;
|
|
95
|
+
const parsed = parseScopedModelRef(modelRef);
|
|
96
|
+
if (!parsed)
|
|
97
|
+
throw new Error("Model must use provider/model[:thinking] format");
|
|
98
|
+
runtime.services.modelRegistry.refresh();
|
|
99
|
+
const model = runtime.services.modelRegistry.find(parsed.provider, parsed.modelId);
|
|
100
|
+
if (!model)
|
|
101
|
+
throw new Error(`Model not found: ${parsed.provider}/${parsed.modelId}`);
|
|
102
|
+
this.saveDefaultModel(modelRef);
|
|
103
|
+
this.host.setSessionStatus(runtime.session);
|
|
104
|
+
}
|
|
105
|
+
async runAutocompleteSlashCommand(argumentsText) {
|
|
106
|
+
const modelRef = argumentsText.trim();
|
|
107
|
+
if (!modelRef) {
|
|
108
|
+
const saved = savePixAutocompleteModel("");
|
|
109
|
+
this.host.setAutocompleteModelRef(saved.modelRef);
|
|
110
|
+
this.host.addEntry({ id: createId("system"), kind: "system", text: saved.modelRef ? `Autocomplete model set to ${saved.modelRef}` : "Inline autocomplete disabled." });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const runtime = getRuntime(this.host, "autocomplete");
|
|
114
|
+
if (!runtime)
|
|
115
|
+
return;
|
|
116
|
+
const parsed = parseScopedModelRef(modelRef);
|
|
117
|
+
if (!parsed)
|
|
118
|
+
throw new Error("Model must use provider/model[:thinking] format, or run /autocomplete with no arguments to disable");
|
|
119
|
+
runtime.services.modelRegistry.refresh();
|
|
120
|
+
const model = runtime.services.modelRegistry.find(parsed.provider, parsed.modelId);
|
|
121
|
+
if (!model)
|
|
122
|
+
throw new Error(`Model not found: ${parsed.provider}/${parsed.modelId}`);
|
|
123
|
+
const saved = savePixAutocompleteModel(modelRef);
|
|
124
|
+
this.host.setAutocompleteModelRef(saved.modelRef);
|
|
125
|
+
this.host.addEntry({ id: createId("system"), kind: "system", text: `Autocomplete model set to ${saved.modelRef}.` });
|
|
126
|
+
this.host.setSessionStatus(runtime.session);
|
|
127
|
+
}
|
|
73
128
|
async runScopedModelsCommand(argumentsText) {
|
|
74
129
|
const runtime = getIdleRuntime(this.host, "scoped-models");
|
|
75
130
|
if (!runtime)
|
|
@@ -152,6 +207,27 @@ export class ModelCommandActions {
|
|
|
152
207
|
throw new Error(`Unknown thinking level: ${level}`);
|
|
153
208
|
await this.runThinkingCommand(level);
|
|
154
209
|
}
|
|
210
|
+
async runDefaultThinkingSlashCommand(argumentsText) {
|
|
211
|
+
const level = argumentsText.trim();
|
|
212
|
+
if (!level) {
|
|
213
|
+
const selected = await this.host.showMenu(this.host.getThinkingMenuItems(""), {
|
|
214
|
+
title: "Select default thinking level",
|
|
215
|
+
placeholder: "Search thinking levels",
|
|
216
|
+
emptyText: "No matching thinking levels",
|
|
217
|
+
});
|
|
218
|
+
if (!selected) {
|
|
219
|
+
this.host.setSessionStatus(this.host.runtime()?.session);
|
|
220
|
+
this.host.render();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
this.saveDefaultThinking(selected.level);
|
|
224
|
+
this.host.render();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (!isThinkingLevel(level))
|
|
228
|
+
throw new Error(`Unknown thinking level: ${level}`);
|
|
229
|
+
this.saveDefaultThinking(level);
|
|
230
|
+
}
|
|
155
231
|
async runModelCommand(model) {
|
|
156
232
|
const runtime = getRuntime(this.host, "model");
|
|
157
233
|
if (!runtime)
|
|
@@ -173,4 +249,32 @@ export class ModelCommandActions {
|
|
|
173
249
|
this.host.addEntry({ id: createId("system"), kind: "system", text: `Selected thinking level ${runtime.session.thinkingLevel}` });
|
|
174
250
|
this.host.setSessionStatus(runtime.session);
|
|
175
251
|
}
|
|
252
|
+
saveDefaultModel(modelRef) {
|
|
253
|
+
const saved = savePixDefaultModel(modelRef);
|
|
254
|
+
if (!saved)
|
|
255
|
+
throw new Error("Model must use provider/model[:thinking] format");
|
|
256
|
+
this.host.addEntry({
|
|
257
|
+
id: createId("system"),
|
|
258
|
+
kind: "system",
|
|
259
|
+
text: `Default model set to ${formatDefaultModelRef(saved)}. New sessions will use it unless --model is provided.`,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
saveDefaultThinking(level) {
|
|
263
|
+
const runtime = getRuntime(this.host, "default-thinking");
|
|
264
|
+
if (!runtime)
|
|
265
|
+
return;
|
|
266
|
+
const fallbackModelRef = runtime.session.model ? this.host.modelRef(runtime.session.model) : undefined;
|
|
267
|
+
const saved = savePixDefaultThinking(level, fallbackModelRef);
|
|
268
|
+
if (!saved)
|
|
269
|
+
throw new Error("Set /default-model first or select a session model before setting default thinking");
|
|
270
|
+
this.host.addEntry({
|
|
271
|
+
id: createId("system"),
|
|
272
|
+
kind: "system",
|
|
273
|
+
text: `Default thinking level set to ${saved.thinking ?? level} for ${saved.modelRef}. New sessions will use it unless --model is provided.`,
|
|
274
|
+
});
|
|
275
|
+
this.host.setSessionStatus(runtime.session);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function formatDefaultModelRef(defaultModel) {
|
|
279
|
+
return defaultModel.thinking ? `${defaultModel.modelRef}:${defaultModel.thinking}` : defaultModel.modelRef;
|
|
176
280
|
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import type { SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
1
2
|
import type { CommandControllerHost } from "./command-host.js";
|
|
3
|
+
import { type ResumeSessionLoaderOptions } from "../session/resume-session-loader.js";
|
|
2
4
|
import type { PopupMenuPlacement } from "../types.js";
|
|
3
5
|
export declare class NavigationCommandActions {
|
|
4
6
|
private readonly host;
|
|
5
|
-
|
|
7
|
+
private readonly resumeSessionLoader;
|
|
8
|
+
private resumeLoadId;
|
|
9
|
+
constructor(host: CommandControllerHost, resumeSessionLoader?: (options: ResumeSessionLoaderOptions) => Promise<readonly SessionInfo[]>);
|
|
6
10
|
runForkCommand(argumentsText: string): Promise<void>;
|
|
7
11
|
runCloneCommand(): Promise<void>;
|
|
8
12
|
runTreeCommand(argumentsText: string): Promise<void>;
|
|
@@ -14,6 +18,7 @@ export declare class NavigationCommandActions {
|
|
|
14
18
|
preserveStatus?: boolean;
|
|
15
19
|
placement?: PopupMenuPlacement;
|
|
16
20
|
}): Promise<void>;
|
|
21
|
+
private loadResumeSessionsInBackground;
|
|
17
22
|
private formatSessionTree;
|
|
18
23
|
private describeSessionTreeEntry;
|
|
19
24
|
}
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
-
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
3
2
|
import { getIdleRuntime, getRuntime } from "./command-runtime.js";
|
|
4
3
|
import { createId } from "../id.js";
|
|
5
4
|
import { isRecord } from "../guards.js";
|
|
6
5
|
import { renderContent } from "../rendering/message-content.js";
|
|
7
6
|
import { sanitizeText } from "../rendering/render-text.js";
|
|
8
7
|
import { createSessionSearchMenuItems, searchSessions } from "../session/session-search.js";
|
|
8
|
+
import { loadResumeSessionsInChunks } from "../session/resume-session-loader.js";
|
|
9
|
+
function nextTick() {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
setImmediate(resolve);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
9
14
|
export class NavigationCommandActions {
|
|
10
15
|
host;
|
|
11
|
-
|
|
16
|
+
resumeSessionLoader;
|
|
17
|
+
resumeLoadId = 0;
|
|
18
|
+
constructor(host, resumeSessionLoader = loadResumeSessionsInChunks) {
|
|
12
19
|
this.host = host;
|
|
20
|
+
this.resumeSessionLoader = resumeSessionLoader;
|
|
13
21
|
}
|
|
14
22
|
async runForkCommand(argumentsText) {
|
|
15
23
|
const runtime = getIdleRuntime(this.host, "fork");
|
|
@@ -197,29 +205,44 @@ export class NavigationCommandActions {
|
|
|
197
205
|
this.host.setStatus("loading sessions…");
|
|
198
206
|
this.host.openDirectPopupMenu("resume", { preserveStatus, ...(placement === undefined ? {} : { placement }) });
|
|
199
207
|
this.host.setDirectPopupMenuQuery(initialQuery);
|
|
208
|
+
if (this.host.getResumeSessions().length > 0) {
|
|
209
|
+
this.host.openResumeMenuWithQuery(initialQuery);
|
|
210
|
+
}
|
|
200
211
|
this.host.render();
|
|
212
|
+
const loadId = ++this.resumeLoadId;
|
|
213
|
+
void this.loadResumeSessionsInBackground({ loadId, preserveStatus, session: runtime.session });
|
|
214
|
+
}
|
|
215
|
+
async loadResumeSessionsInBackground(options) {
|
|
201
216
|
try {
|
|
202
|
-
|
|
203
|
-
this.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
await nextTick();
|
|
218
|
+
await this.resumeSessionLoader({
|
|
219
|
+
cwd: this.host.options.cwd,
|
|
220
|
+
onChunk: (sessions, progress) => {
|
|
221
|
+
if (options.loadId !== this.resumeLoadId)
|
|
222
|
+
return;
|
|
223
|
+
this.host.setResumeSessions([...sessions]);
|
|
224
|
+
if (progress.done) {
|
|
225
|
+
this.host.setResumeLoading(false);
|
|
226
|
+
if (!options.preserveStatus)
|
|
227
|
+
this.host.setSessionStatus(options.session);
|
|
228
|
+
}
|
|
229
|
+
this.host.render();
|
|
230
|
+
},
|
|
231
|
+
});
|
|
211
232
|
}
|
|
212
233
|
catch (error) {
|
|
234
|
+
if (options.loadId !== this.resumeLoadId)
|
|
235
|
+
return;
|
|
213
236
|
this.host.setResumeLoading(false);
|
|
214
237
|
this.host.setDirectPopupMenu(undefined);
|
|
215
238
|
this.host.setDirectPopupMenuPreserveStatus(false);
|
|
216
239
|
this.host.setDirectPopupMenuQuery("");
|
|
217
240
|
this.host.closeResumeMenu();
|
|
218
241
|
this.host.addEntry({ id: createId("error"), kind: "error", text: `Session list failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
219
|
-
if (!preserveStatus)
|
|
242
|
+
if (!options.preserveStatus)
|
|
220
243
|
this.host.toast.error("Failed to load sessions");
|
|
221
|
-
if (!preserveStatus)
|
|
222
|
-
this.host.setSessionStatus(
|
|
244
|
+
if (!options.preserveStatus)
|
|
245
|
+
this.host.setSessionStatus(options.session);
|
|
223
246
|
this.host.render();
|
|
224
247
|
}
|
|
225
248
|
}
|
|
@@ -3,13 +3,17 @@ import type { SlashCommand } from "../types.js";
|
|
|
3
3
|
export type CommandRegistryActions = {
|
|
4
4
|
runSettingsCommand(): Promise<void>;
|
|
5
5
|
runModelSlashCommand(argumentsText: string): Promise<void>;
|
|
6
|
+
runDefaultModelSlashCommand(argumentsText: string): Promise<void>;
|
|
7
|
+
runAutocompleteSlashCommand(argumentsText: string): Promise<void>;
|
|
6
8
|
runScopedModelsCommand(argumentsText: string): Promise<void>;
|
|
7
9
|
runThinkingSlashCommand(argumentsText: string): Promise<void>;
|
|
10
|
+
runDefaultThinkingSlashCommand(argumentsText: string): Promise<void>;
|
|
8
11
|
runEnhanceCommand(): Promise<void>;
|
|
9
12
|
runExportCommand(argumentsText: string): Promise<void>;
|
|
10
13
|
runImportCommand(argumentsText: string): Promise<void>;
|
|
11
14
|
runShareCommand(): Promise<void>;
|
|
12
15
|
runCopyCommand(): Promise<void>;
|
|
16
|
+
runQueueCommand(argumentsText: string): Promise<void>;
|
|
13
17
|
runNameCommand(argumentsText: string): Promise<void>;
|
|
14
18
|
runSessionInfoCommand(): Promise<void>;
|
|
15
19
|
runUsageCommand(): Promise<void>;
|
|
@@ -16,6 +16,22 @@ export function createSlashCommands(actions, host) {
|
|
|
16
16
|
allowArguments: true,
|
|
17
17
|
run: (argumentsText) => actions.runModelSlashCommand(argumentsText),
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
name: "default-model",
|
|
21
|
+
description: "Set the default model for new sessions",
|
|
22
|
+
kind: "builtin",
|
|
23
|
+
keywords: ["model", "provider", "startup", "config"],
|
|
24
|
+
allowArguments: true,
|
|
25
|
+
run: (argumentsText) => actions.runDefaultModelSlashCommand(argumentsText),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "autocomplete",
|
|
29
|
+
description: "Set inline autocomplete model, or empty to disable",
|
|
30
|
+
kind: "builtin",
|
|
31
|
+
keywords: ["complete", "ghost", "suggest", "llm", "model"],
|
|
32
|
+
allowArguments: true,
|
|
33
|
+
run: (argumentsText) => actions.runAutocompleteSlashCommand(argumentsText),
|
|
34
|
+
},
|
|
19
35
|
{
|
|
20
36
|
name: "scoped-models",
|
|
21
37
|
description: "Show or set models used by the model selector/cycling",
|
|
@@ -32,6 +48,14 @@ export function createSlashCommands(actions, host) {
|
|
|
32
48
|
allowArguments: true,
|
|
33
49
|
run: (argumentsText) => actions.runThinkingSlashCommand(argumentsText),
|
|
34
50
|
},
|
|
51
|
+
{
|
|
52
|
+
name: "default-thinking",
|
|
53
|
+
description: "Set the default thinking level for new sessions",
|
|
54
|
+
kind: "builtin",
|
|
55
|
+
keywords: ["thinking", "reasoning", "startup", "config"],
|
|
56
|
+
allowArguments: true,
|
|
57
|
+
run: (argumentsText) => actions.runDefaultThinkingSlashCommand(argumentsText),
|
|
58
|
+
},
|
|
35
59
|
{
|
|
36
60
|
name: "enhance",
|
|
37
61
|
description: "Improve the current prompt draft",
|
|
@@ -69,6 +93,14 @@ export function createSlashCommands(actions, host) {
|
|
|
69
93
|
keywords: ["clipboard", "assistant", "message"],
|
|
70
94
|
run: () => actions.runCopyCommand(),
|
|
71
95
|
},
|
|
96
|
+
{
|
|
97
|
+
name: "queue",
|
|
98
|
+
description: "Queue a message for manual sending later",
|
|
99
|
+
kind: "builtin",
|
|
100
|
+
keywords: ["defer", "delayed", "message", "send later"],
|
|
101
|
+
allowArguments: true,
|
|
102
|
+
run: (argumentsText) => actions.runQueueCommand(argumentsText),
|
|
103
|
+
},
|
|
72
104
|
{
|
|
73
105
|
name: "name",
|
|
74
106
|
description: "Show or set the session display name",
|
|
@@ -6,6 +6,7 @@ export declare class SessionCommandActions {
|
|
|
6
6
|
runImportCommand(argumentsText: string): Promise<void>;
|
|
7
7
|
runShareCommand(): Promise<void>;
|
|
8
8
|
runCopyCommand(): Promise<void>;
|
|
9
|
+
runQueueCommand(argumentsText: string): Promise<void>;
|
|
9
10
|
runNameCommand(argumentsText: string): Promise<void>;
|
|
10
11
|
runSessionInfoCommand(): Promise<void>;
|
|
11
12
|
runUsageCommand(): Promise<void>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
4
3
|
import { dirname, join, resolve } from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
5
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7
6
|
import { getIdleRuntime, getRuntime, parsePathArgument } from "./command-runtime.js";
|
|
8
7
|
import { createId } from "../id.js";
|
|
8
|
+
import { runProcess } from "../process.js";
|
|
9
|
+
import { copyTextToClipboard } from "../screen/clipboard.js";
|
|
9
10
|
import { formatAccountUsageReport, queryAccountUsageReport } from "../model/model-usage-status.js";
|
|
10
11
|
import { checkPixUpdate, formatPixUpdateCheck, parsePixUpdateArgs, pixUpdateUsage } from "../cli/update.js";
|
|
11
12
|
export class SessionCommandActions {
|
|
@@ -50,7 +51,7 @@ export class SessionCommandActions {
|
|
|
50
51
|
const runtime = getIdleRuntime(this.host, "share");
|
|
51
52
|
if (!runtime)
|
|
52
53
|
return;
|
|
53
|
-
const authResult =
|
|
54
|
+
const authResult = await runProcess("gh", ["auth", "status"], { maxBufferBytes: 32 * 1024 });
|
|
54
55
|
if (authResult.status !== 0)
|
|
55
56
|
throw new Error("GitHub CLI is not installed or is not logged in. Run `gh auth login` first.");
|
|
56
57
|
const shareDir = join(getAgentDir(), "pix");
|
|
@@ -60,7 +61,7 @@ export class SessionCommandActions {
|
|
|
60
61
|
this.host.setStatus("creating share gist");
|
|
61
62
|
this.host.render();
|
|
62
63
|
await runtime.session.exportToHtml(tmpFile);
|
|
63
|
-
const gistResult =
|
|
64
|
+
const gistResult = await runProcess("gh", ["gist", "create", "--public=false", tmpFile], { maxBufferBytes: 64 * 1024 });
|
|
64
65
|
if (gistResult.status !== 0)
|
|
65
66
|
throw new Error(gistResult.stderr?.trim() || "Failed to create gist");
|
|
66
67
|
const gistUrl = gistResult.stdout.trim();
|
|
@@ -79,11 +80,19 @@ export class SessionCommandActions {
|
|
|
79
80
|
const text = runtime.session.getLastAssistantText();
|
|
80
81
|
if (!text)
|
|
81
82
|
throw new Error("No agent messages to copy yet");
|
|
82
|
-
await
|
|
83
|
+
await copyTextToClipboard(text);
|
|
83
84
|
this.host.addEntry({ id: createId("system"), kind: "system", text: "Copied last agent message to clipboard." });
|
|
84
85
|
this.host.setSessionStatus(runtime.session);
|
|
85
86
|
this.host.toast.success("Copied last agent message");
|
|
86
87
|
}
|
|
88
|
+
async runQueueCommand(argumentsText) {
|
|
89
|
+
const text = argumentsText.trim();
|
|
90
|
+
if (!text)
|
|
91
|
+
throw new Error("Usage: /queue <message to send later>");
|
|
92
|
+
this.host.queueUserMessage(text);
|
|
93
|
+
this.host.addEntry({ id: createId("system"), kind: "system", text: "Queued message for manual sending." });
|
|
94
|
+
this.host.setSessionStatus(this.host.runtime()?.session);
|
|
95
|
+
}
|
|
87
96
|
async runNameCommand(argumentsText) {
|
|
88
97
|
const runtime = getRuntime(this.host, "name");
|
|
89
98
|
if (!runtime)
|
|
@@ -201,6 +210,7 @@ export class SessionCommandActions {
|
|
|
201
210
|
"Enter: send message / run selected command",
|
|
202
211
|
"!command: run a local shell command in chat (not saved to the session)",
|
|
203
212
|
"while shell is running: Enter sends editor text to stdin; Ctrl-C interrupts; !!command uses the raw terminal",
|
|
213
|
+
"/queue <message>: save a delayed message; send it later from the queued-message menu or status button",
|
|
204
214
|
"Tab: autocomplete selected popup item",
|
|
205
215
|
"Esc: close popup; abort running work when input is empty",
|
|
206
216
|
"Up/Down: history or popup navigation",
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
type ShellCommandDeps = {
|
|
3
|
+
spawn: typeof spawn;
|
|
4
|
+
waitForReturnToPix: () => Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
export declare function setShellCommandTestDeps(overrides: Partial<ShellCommandDeps>): () => void;
|
|
1
7
|
export type InteractiveShellCommandResult = {
|
|
2
8
|
exitCode: number | null;
|
|
3
9
|
signal: NodeJS.Signals | null;
|
|
@@ -25,3 +31,4 @@ export declare function shellCommandFromBangInput(text: string): string | undefi
|
|
|
25
31
|
export declare function runChatShellCommand(command: string, cwd: string, handlers?: ChatShellCommandHandlers): RunningChatShellCommand;
|
|
26
32
|
export declare function runInteractiveShellCommand(command: string, cwd: string): Promise<InteractiveShellCommandResult>;
|
|
27
33
|
export declare function formatShellCommandEntry(command: string, result: InteractiveShellCommandResult, prefix?: string): string;
|
|
34
|
+
export {};
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
let deps = { spawn, waitForReturnToPix: waitForReturnToPixImpl };
|
|
3
|
+
export function setShellCommandTestDeps(overrides) {
|
|
4
|
+
const previous = deps;
|
|
5
|
+
deps = { ...deps, ...overrides };
|
|
6
|
+
return () => {
|
|
7
|
+
deps = previous;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
2
10
|
export function bangShellCommandFromInput(text) {
|
|
3
11
|
const trimmed = text.trimStart();
|
|
4
12
|
if (!trimmed.startsWith("!"))
|
|
@@ -12,7 +20,7 @@ export function shellCommandFromBangInput(text) {
|
|
|
12
20
|
export function runChatShellCommand(command, cwd, handlers = {}) {
|
|
13
21
|
let child;
|
|
14
22
|
try {
|
|
15
|
-
child = spawn(command, {
|
|
23
|
+
child = deps.spawn(command, {
|
|
16
24
|
cwd,
|
|
17
25
|
env: process.env,
|
|
18
26
|
shell: shellOption(),
|
|
@@ -79,7 +87,7 @@ export async function runInteractiveShellCommand(command, cwd) {
|
|
|
79
87
|
try {
|
|
80
88
|
const result = await spawnShellCommand(command, cwd);
|
|
81
89
|
process.stdout.write(`\n[pix] ${formatInteractiveShellResult(result)}\n`);
|
|
82
|
-
await waitForReturnToPix();
|
|
90
|
+
await deps.waitForReturnToPix();
|
|
83
91
|
return result;
|
|
84
92
|
}
|
|
85
93
|
finally {
|
|
@@ -93,7 +101,7 @@ export function formatShellCommandEntry(command, result, prefix = "!") {
|
|
|
93
101
|
}
|
|
94
102
|
async function spawnShellCommand(command, cwd) {
|
|
95
103
|
try {
|
|
96
|
-
const child = spawn(command, {
|
|
104
|
+
const child = deps.spawn(command, {
|
|
97
105
|
cwd,
|
|
98
106
|
env: process.env,
|
|
99
107
|
shell: shellOption(),
|
|
@@ -160,7 +168,7 @@ function formatInteractiveShellResult(result) {
|
|
|
160
168
|
return `terminated by ${result.signal}`;
|
|
161
169
|
return `exit ${result.exitCode ?? 0}`;
|
|
162
170
|
}
|
|
163
|
-
async function
|
|
171
|
+
async function waitForReturnToPixImpl() {
|
|
164
172
|
if (!process.stdin.isTTY || !process.stdin.readable)
|
|
165
173
|
return;
|
|
166
174
|
process.stdout.write("[pix] Press Enter to return to pix…");
|
|
@@ -89,7 +89,7 @@ export class AppShellController {
|
|
|
89
89
|
this.renderTimer = setTimeout(() => {
|
|
90
90
|
this.renderTimer = undefined;
|
|
91
91
|
if (this.host.isRunning())
|
|
92
|
-
this.host.
|
|
92
|
+
this.host.scheduleRender();
|
|
93
93
|
}, SHELL_RENDER_THROTTLE_MS);
|
|
94
94
|
this.renderTimer.unref?.();
|
|
95
95
|
}
|
package/dist/app/constants.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export declare const SUBAGENTS_POLL_INTERVAL_MS = 1500;
|
|
|
38
38
|
export declare const SUBAGENTS_WIDGET_MAX_ROWS = 8;
|
|
39
39
|
export declare const DEFAULT_THINKING_TOOL_RULE: ResolvedToolRule;
|
|
40
40
|
export declare const TERMINAL_COMMAND_MODIFIER_FLAG = 8;
|
|
41
|
-
export declare const GIT_BRANCH_CACHE_MS =
|
|
41
|
+
export declare const GIT_BRANCH_CACHE_MS = 30000;
|
|
42
42
|
export declare const TODO_TOOL_NAME = "todo";
|
|
43
43
|
export declare const TODO_ACTIONS: readonly ["create", "update", "batch_create", "batch_update", "list", "get", "delete", "clear", "export", "import"];
|
|
44
44
|
export declare const TODO_STATUSES: readonly ["pending", "in_progress", "deferred", "completed", "deleted"];
|
package/dist/app/constants.js
CHANGED
|
@@ -75,7 +75,7 @@ export const DEFAULT_THINKING_TOOL_RULE = {
|
|
|
75
75
|
color: "accent",
|
|
76
76
|
};
|
|
77
77
|
export const TERMINAL_COMMAND_MODIFIER_FLAG = 8;
|
|
78
|
-
export const GIT_BRANCH_CACHE_MS =
|
|
78
|
+
export const GIT_BRANCH_CACHE_MS = 30_000;
|
|
79
79
|
export const TODO_TOOL_NAME = "todo";
|
|
80
80
|
export const TODO_ACTIONS = [
|
|
81
81
|
"create",
|
package/dist/app/icons.d.ts
CHANGED
package/dist/app/icons.js
CHANGED
|
@@ -20,6 +20,7 @@ const NERD_FONT_ICONS = {
|
|
|
20
20
|
info: "\u{f02fc}",
|
|
21
21
|
microphone: "\u{f036c}",
|
|
22
22
|
plus: "\u{f0415}",
|
|
23
|
+
pause: "\u{f03e4}",
|
|
23
24
|
record: "\u{f044a}",
|
|
24
25
|
refresh: "\u{f0450}",
|
|
25
26
|
volumeHigh: "\u{f057e}",
|
|
@@ -45,6 +46,7 @@ const FALLBACK_ICONS = {
|
|
|
45
46
|
info: "i",
|
|
46
47
|
microphone: "m",
|
|
47
48
|
plus: "+",
|
|
49
|
+
pause: "⏸",
|
|
48
50
|
record: "●",
|
|
49
51
|
refresh: "↻",
|
|
50
52
|
volumeHigh: "♪",
|
|
@@ -53,7 +55,7 @@ const FALLBACK_ICONS = {
|
|
|
53
55
|
compactTools: "≡",
|
|
54
56
|
thinkingExpanded: ">",
|
|
55
57
|
stopCircle: "■",
|
|
56
|
-
timerSand: "
|
|
58
|
+
timerSand: "⏳",
|
|
57
59
|
down: "v",
|
|
58
60
|
};
|
|
59
61
|
export const APP_ICON_THEMES = {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { AutocompleteConfig } from "../../config.js";
|
|
3
|
+
import type { InputEditor } from "../../input-editor.js";
|
|
4
|
+
export type AppAutocompleteControllerHost = {
|
|
5
|
+
runtime(): AgentSessionRuntime | undefined;
|
|
6
|
+
inputEditor(): InputEditor;
|
|
7
|
+
autocompleteConfig(): AutocompleteConfig;
|
|
8
|
+
isRunning(): boolean;
|
|
9
|
+
render(): void;
|
|
10
|
+
};
|
|
11
|
+
export type AutocompleteHistoryMessage = {
|
|
12
|
+
role: "user" | "assistant";
|
|
13
|
+
text: string;
|
|
14
|
+
};
|
|
15
|
+
type AutocompleteRunner = typeof completeInputWithPi;
|
|
16
|
+
export type AppAutocompleteControllerOptions = {
|
|
17
|
+
completeInputWithPi?: AutocompleteRunner;
|
|
18
|
+
debounceMs?: number;
|
|
19
|
+
};
|
|
20
|
+
export declare class AppAutocompleteController {
|
|
21
|
+
private readonly host;
|
|
22
|
+
private timer;
|
|
23
|
+
private lastObservedKey;
|
|
24
|
+
private requestSeq;
|
|
25
|
+
private suggestion;
|
|
26
|
+
private activeAbortController;
|
|
27
|
+
private readonly completeInputWithPi;
|
|
28
|
+
private readonly debounceOverrideMs;
|
|
29
|
+
constructor(host: AppAutocompleteControllerHost, options?: AppAutocompleteControllerOptions);
|
|
30
|
+
observeInput(): void;
|
|
31
|
+
suggestionText(): string | undefined;
|
|
32
|
+
acceptSuggestion(): boolean;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
private runAutocomplete;
|
|
35
|
+
private currentTarget;
|
|
36
|
+
private targetKey;
|
|
37
|
+
private sameTarget;
|
|
38
|
+
private currentDebounceMs;
|
|
39
|
+
private clearTimer;
|
|
40
|
+
private cancelInFlight;
|
|
41
|
+
}
|
|
42
|
+
export declare function completeInputWithPi(runtime: AgentSessionRuntime, draft: string, config: AutocompleteConfig, signal?: AbortSignal): Promise<string>;
|
|
43
|
+
export declare function autocompleteHistoryFromMessages(messages: readonly unknown[], includeRecentMessages: number): AutocompleteHistoryMessage[];
|
|
44
|
+
export declare function buildAutocompletePrompt(input: {
|
|
45
|
+
cwd: string;
|
|
46
|
+
draft: string;
|
|
47
|
+
history: readonly AutocompleteHistoryMessage[];
|
|
48
|
+
maxPromptTokens?: number;
|
|
49
|
+
}): string;
|
|
50
|
+
export declare function autocompletePromptTokenEstimate(prompt: string, systemPrompt?: string): number;
|
|
51
|
+
export declare function cleanupCompletion(output: string, draft: string, config?: Pick<AutocompleteConfig, "maxTokens">): string;
|
|
52
|
+
export {};
|