@oh-my-pi/pi-coding-agent 3.6.1337 → 3.8.1337
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 +27 -0
- package/package.json +4 -4
- package/src/core/bash-executor.ts +115 -154
- package/src/core/index.ts +2 -0
- package/src/core/session-manager.ts +16 -6
- package/src/core/tools/edit-diff.ts +45 -33
- package/src/core/tools/edit.ts +70 -182
- package/src/core/tools/find.ts +141 -160
- package/src/core/tools/index.ts +10 -9
- package/src/core/tools/ls.ts +64 -82
- package/src/core/tools/lsp/client.ts +63 -0
- package/src/core/tools/lsp/edits.ts +13 -4
- package/src/core/tools/lsp/index.ts +191 -85
- package/src/core/tools/notebook.ts +89 -144
- package/src/core/tools/read.ts +110 -158
- package/src/core/tools/write.ts +22 -115
- package/src/core/utils.ts +187 -0
- package/src/modes/interactive/components/tool-execution.ts +14 -14
- package/src/modes/interactive/interactive-mode.ts +23 -54
|
@@ -503,19 +503,19 @@ export class ToolExecutionComponent extends Container {
|
|
|
503
503
|
}
|
|
504
504
|
|
|
505
505
|
// Show LSP diagnostics if available
|
|
506
|
-
if (this.result?.details?.diagnostics
|
|
506
|
+
if (this.result?.details?.diagnostics) {
|
|
507
507
|
const diag = this.result.details.diagnostics;
|
|
508
|
-
if (diag.
|
|
509
|
-
const icon = diag.
|
|
508
|
+
if (diag.messages.length > 0) {
|
|
509
|
+
const icon = diag.errored ? theme.fg("error", "●") : theme.fg("warning", "●");
|
|
510
510
|
text += `\n\n${icon} ${theme.fg("toolTitle", "LSP Diagnostics")} ${theme.fg("dim", `(${diag.summary})`)}`;
|
|
511
|
-
const maxDiags = this.expanded ? diag.
|
|
512
|
-
const displayDiags = diag.
|
|
511
|
+
const maxDiags = this.expanded ? diag.messages.length : 5;
|
|
512
|
+
const displayDiags = diag.messages.slice(0, maxDiags);
|
|
513
513
|
for (const d of displayDiags) {
|
|
514
514
|
const color = d.includes("[error]") ? "error" : d.includes("[warning]") ? "warning" : "dim";
|
|
515
515
|
text += `\n ${theme.fg(color, d)}`;
|
|
516
516
|
}
|
|
517
|
-
if (diag.
|
|
518
|
-
text += theme.fg("dim", `\n ... (${diag.
|
|
517
|
+
if (diag.messages.length > maxDiags) {
|
|
518
|
+
text += theme.fg("dim", `\n ... (${diag.messages.length - maxDiags} more)`);
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
521
|
}
|
|
@@ -552,19 +552,19 @@ export class ToolExecutionComponent extends Container {
|
|
|
552
552
|
}
|
|
553
553
|
|
|
554
554
|
// Show LSP diagnostics if available
|
|
555
|
-
if (this.result?.details?.diagnostics
|
|
555
|
+
if (this.result?.details?.diagnostics) {
|
|
556
556
|
const diag = this.result.details.diagnostics;
|
|
557
|
-
if (diag.
|
|
558
|
-
const icon = diag.
|
|
557
|
+
if (diag.messages.length > 0) {
|
|
558
|
+
const icon = diag.errored ? theme.fg("error", "●") : theme.fg("warning", "●");
|
|
559
559
|
text += `\n\n${icon} ${theme.fg("toolTitle", "LSP Diagnostics")} ${theme.fg("dim", `(${diag.summary})`)}`;
|
|
560
|
-
const maxDiags = this.expanded ? diag.
|
|
561
|
-
const displayDiags = diag.
|
|
560
|
+
const maxDiags = this.expanded ? diag.messages.length : 5;
|
|
561
|
+
const displayDiags = diag.messages.slice(0, maxDiags);
|
|
562
562
|
for (const d of displayDiags) {
|
|
563
563
|
const color = d.includes("[error]") ? "error" : d.includes("[warning]") ? "warning" : "dim";
|
|
564
564
|
text += `\n ${theme.fg(color, d)}`;
|
|
565
565
|
}
|
|
566
|
-
if (diag.
|
|
567
|
-
text += theme.fg("dim", `\n ... (${diag.
|
|
566
|
+
if (diag.messages.length > maxDiags) {
|
|
567
|
+
text += theme.fg("dim", `\n ... (${diag.messages.length - maxDiags} more)`);
|
|
568
568
|
}
|
|
569
569
|
}
|
|
570
570
|
}
|
|
@@ -143,9 +143,6 @@ export class InteractiveMode {
|
|
|
143
143
|
// Custom tools for custom rendering
|
|
144
144
|
private customTools: Map<string, LoadedCustomTool>;
|
|
145
145
|
|
|
146
|
-
// Title generation state
|
|
147
|
-
private titleGenerationAttempted = false;
|
|
148
|
-
|
|
149
146
|
// Convenience accessors
|
|
150
147
|
private get agent() {
|
|
151
148
|
return this.session.agent;
|
|
@@ -261,7 +258,6 @@ export class InteractiveMode {
|
|
|
261
258
|
const existingTitle = this.sessionManager.getSessionTitle();
|
|
262
259
|
if (existingTitle) {
|
|
263
260
|
setTerminalTitle(`pi: ${existingTitle}`);
|
|
264
|
-
this.titleGenerationAttempted = true; // Don't try to generate again
|
|
265
261
|
}
|
|
266
262
|
|
|
267
263
|
// Setup UI layout
|
|
@@ -401,7 +397,6 @@ export class InteractiveMode {
|
|
|
401
397
|
this.streamingComponent = undefined;
|
|
402
398
|
this.streamingMessage = undefined;
|
|
403
399
|
this.pendingTools.clear();
|
|
404
|
-
this.titleGenerationAttempted = false;
|
|
405
400
|
|
|
406
401
|
this.chatContainer.addChild(new Spacer(1));
|
|
407
402
|
this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
|
|
@@ -903,6 +898,21 @@ export class InteractiveMode {
|
|
|
903
898
|
// First, move any pending bash components to chat
|
|
904
899
|
this.flushPendingBashComponents();
|
|
905
900
|
|
|
901
|
+
// Generate session title on first message
|
|
902
|
+
const hasUserMessages = this.agent.state.messages.some((m) => m.role === "user");
|
|
903
|
+
if (!hasUserMessages && !this.sessionManager.getSessionTitle()) {
|
|
904
|
+
const registry = this.session.modelRegistry;
|
|
905
|
+
const smolModel = this.settingsManager.getModelRole("smol");
|
|
906
|
+
generateSessionTitle(text, registry, smolModel)
|
|
907
|
+
.then((title) => {
|
|
908
|
+
if (title) {
|
|
909
|
+
this.sessionManager.setSessionTitle(title);
|
|
910
|
+
setTerminalTitle(`omp: ${title}`);
|
|
911
|
+
}
|
|
912
|
+
})
|
|
913
|
+
.catch(() => {});
|
|
914
|
+
}
|
|
915
|
+
|
|
906
916
|
if (this.onInputCallback) {
|
|
907
917
|
// Include any pending images from clipboard paste
|
|
908
918
|
const images = this.pendingImages.length > 0 ? [...this.pendingImages] : undefined;
|
|
@@ -1085,12 +1095,6 @@ export class InteractiveMode {
|
|
|
1085
1095
|
}
|
|
1086
1096
|
this.pendingTools.clear();
|
|
1087
1097
|
this.ui.requestRender();
|
|
1088
|
-
|
|
1089
|
-
// Generate session title after first turn (if not already titled)
|
|
1090
|
-
if (!this.titleGenerationAttempted && !this.sessionManager.getSessionTitle()) {
|
|
1091
|
-
this.titleGenerationAttempted = true;
|
|
1092
|
-
this.maybeGenerateTitle();
|
|
1093
|
-
}
|
|
1094
1098
|
break;
|
|
1095
1099
|
|
|
1096
1100
|
case "auto_compaction_start": {
|
|
@@ -1635,42 +1639,6 @@ export class InteractiveMode {
|
|
|
1635
1639
|
this.ui.requestRender();
|
|
1636
1640
|
}
|
|
1637
1641
|
|
|
1638
|
-
/**
|
|
1639
|
-
* Generate a title for the session based on the first user message.
|
|
1640
|
-
* Runs in background, doesn't block UI.
|
|
1641
|
-
*/
|
|
1642
|
-
private maybeGenerateTitle(): void {
|
|
1643
|
-
// Find the first user message
|
|
1644
|
-
const messages = this.agent.state.messages;
|
|
1645
|
-
const firstUserMessage = messages.find((m) => m.role === "user");
|
|
1646
|
-
if (!firstUserMessage) return;
|
|
1647
|
-
|
|
1648
|
-
// Extract text content
|
|
1649
|
-
let messageText = "";
|
|
1650
|
-
for (const content of firstUserMessage.content) {
|
|
1651
|
-
if (typeof content === "string") {
|
|
1652
|
-
messageText += content;
|
|
1653
|
-
} else if (content.type === "text") {
|
|
1654
|
-
messageText += content.text;
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
if (!messageText.trim()) return;
|
|
1658
|
-
|
|
1659
|
-
// Generate title in background
|
|
1660
|
-
const registry = this.session.modelRegistry;
|
|
1661
|
-
const smolModel = this.settingsManager.getModelRole("smol");
|
|
1662
|
-
generateSessionTitle(messageText, registry, smolModel)
|
|
1663
|
-
.then((title) => {
|
|
1664
|
-
if (title) {
|
|
1665
|
-
this.sessionManager.setSessionTitle(title);
|
|
1666
|
-
setTerminalTitle(`omp: ${title}`);
|
|
1667
|
-
}
|
|
1668
|
-
})
|
|
1669
|
-
.catch(() => {
|
|
1670
|
-
// Errors logged via logger in title-generator
|
|
1671
|
-
});
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
1642
|
private updatePendingMessagesDisplay(): void {
|
|
1675
1643
|
this.pendingMessagesContainer.clear();
|
|
1676
1644
|
const queuedMessages = this.session.getQueuedMessages();
|
|
@@ -2076,13 +2044,7 @@ export class InteractiveMode {
|
|
|
2076
2044
|
}
|
|
2077
2045
|
this.ui.requestRender();
|
|
2078
2046
|
|
|
2079
|
-
|
|
2080
|
-
process.platform === "darwin"
|
|
2081
|
-
? "open"
|
|
2082
|
-
: process.platform === "win32"
|
|
2083
|
-
? "start"
|
|
2084
|
-
: "xdg-open";
|
|
2085
|
-
Bun.spawn([openCmd, info.url], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
|
2047
|
+
this.openInBrowser(info.url);
|
|
2086
2048
|
},
|
|
2087
2049
|
onPrompt: async (prompt: { message: string; placeholder?: string }) => {
|
|
2088
2050
|
this.chatContainer.addChild(new Spacer(1));
|
|
@@ -2156,6 +2118,11 @@ export class InteractiveMode {
|
|
|
2156
2118
|
// Command handlers
|
|
2157
2119
|
// =========================================================================
|
|
2158
2120
|
|
|
2121
|
+
private openInBrowser(urlOrPath: string): void {
|
|
2122
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2123
|
+
Bun.spawn([openCmd, urlOrPath], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2159
2126
|
private async handleExportCommand(text: string): Promise<void> {
|
|
2160
2127
|
const parts = text.split(/\s+/);
|
|
2161
2128
|
const arg = parts.length > 1 ? parts[1] : undefined;
|
|
@@ -2180,6 +2147,7 @@ export class InteractiveMode {
|
|
|
2180
2147
|
try {
|
|
2181
2148
|
const filePath = await this.session.exportToHtml(arg);
|
|
2182
2149
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
2150
|
+
this.openInBrowser(filePath);
|
|
2183
2151
|
} catch (error: unknown) {
|
|
2184
2152
|
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2185
2153
|
}
|
|
@@ -2293,6 +2261,7 @@ export class InteractiveMode {
|
|
|
2293
2261
|
// Create the preview URL
|
|
2294
2262
|
const previewUrl = `https://gistpreview.github.io/?${gistId}`;
|
|
2295
2263
|
this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
|
|
2264
|
+
this.openInBrowser(previewUrl);
|
|
2296
2265
|
} catch (error: unknown) {
|
|
2297
2266
|
if (!loader.signal.aborted) {
|
|
2298
2267
|
restoreEditor();
|