@oh-my-pi/pi-coding-agent 6.2.0 → 6.7.67
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 +60 -0
- package/docs/sdk.md +1 -1
- package/package.json +5 -5
- package/scripts/generate-template.ts +6 -6
- package/src/cli/args.ts +3 -0
- package/src/core/agent-session.ts +39 -0
- package/src/core/bash-executor.ts +3 -3
- package/src/core/cursor/exec-bridge.ts +95 -88
- package/src/core/custom-commands/bundled/review/index.ts +142 -145
- package/src/core/custom-commands/bundled/wt/index.ts +68 -66
- package/src/core/custom-commands/loader.ts +4 -6
- package/src/core/custom-tools/index.ts +2 -2
- package/src/core/custom-tools/loader.ts +66 -61
- package/src/core/custom-tools/types.ts +4 -4
- package/src/core/custom-tools/wrapper.ts +61 -25
- package/src/core/event-bus.ts +19 -47
- package/src/core/extensions/index.ts +8 -4
- package/src/core/extensions/loader.ts +160 -120
- package/src/core/extensions/types.ts +4 -4
- package/src/core/extensions/wrapper.ts +149 -100
- package/src/core/hooks/index.ts +1 -1
- package/src/core/hooks/tool-wrapper.ts +96 -70
- package/src/core/hooks/types.ts +1 -2
- package/src/core/index.ts +1 -0
- package/src/core/mcp/index.ts +6 -2
- package/src/core/mcp/json-rpc.ts +88 -0
- package/src/core/mcp/loader.ts +22 -4
- package/src/core/mcp/manager.ts +202 -48
- package/src/core/mcp/tool-bridge.ts +143 -55
- package/src/core/mcp/tool-cache.ts +122 -0
- package/src/core/python-executor.ts +3 -9
- package/src/core/sdk.ts +33 -32
- package/src/core/session-manager.ts +30 -0
- package/src/core/settings-manager.ts +54 -1
- package/src/core/ssh/ssh-executor.ts +6 -84
- package/src/core/streaming-output.ts +107 -53
- package/src/core/tools/ask.ts +92 -93
- package/src/core/tools/bash.ts +103 -94
- package/src/core/tools/calculator.ts +41 -26
- package/src/core/tools/complete.ts +76 -66
- package/src/core/tools/context.ts +22 -24
- package/src/core/tools/exa/index.ts +1 -1
- package/src/core/tools/exa/mcp-client.ts +56 -101
- package/src/core/tools/find.ts +250 -253
- package/src/core/tools/git.ts +39 -33
- package/src/core/tools/grep.ts +440 -427
- package/src/core/tools/index.ts +63 -61
- package/src/core/tools/ls.ts +119 -114
- package/src/core/tools/lsp/clients/biome-client.ts +5 -7
- package/src/core/tools/lsp/clients/index.ts +4 -4
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +5 -7
- package/src/core/tools/lsp/config.ts +2 -2
- package/src/core/tools/lsp/index.ts +604 -578
- package/src/core/tools/notebook.ts +121 -119
- package/src/core/tools/output.ts +163 -147
- package/src/core/tools/patch/applicator.ts +1100 -0
- package/src/core/tools/patch/diff.ts +362 -0
- package/src/core/tools/patch/fuzzy.ts +647 -0
- package/src/core/tools/patch/index.ts +430 -0
- package/src/core/tools/patch/normalize.ts +220 -0
- package/src/core/tools/patch/normative.ts +73 -0
- package/src/core/tools/patch/parser.ts +528 -0
- package/src/core/tools/patch/shared.ts +257 -0
- package/src/core/tools/patch/types.ts +244 -0
- package/src/core/tools/python.ts +139 -136
- package/src/core/tools/read.ts +239 -216
- package/src/core/tools/render-utils.ts +196 -77
- package/src/core/tools/renderers.ts +6 -2
- package/src/core/tools/ssh.ts +99 -80
- package/src/core/tools/task/executor.ts +11 -7
- package/src/core/tools/task/index.ts +352 -343
- package/src/core/tools/task/worker.ts +13 -23
- package/src/core/tools/todo-write.ts +74 -59
- package/src/core/tools/web-fetch.ts +54 -47
- package/src/core/tools/web-search/index.ts +27 -16
- package/src/core/tools/write.ts +108 -47
- package/src/core/ttsr.ts +106 -152
- package/src/core/voice.ts +49 -39
- package/src/index.ts +16 -12
- package/src/lib/worktree/index.ts +1 -9
- package/src/modes/interactive/components/diff.ts +15 -8
- package/src/modes/interactive/components/settings-defs.ts +42 -0
- package/src/modes/interactive/components/tool-execution.ts +46 -8
- package/src/modes/interactive/controllers/event-controller.ts +6 -19
- package/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/src/modes/interactive/utils/ui-helpers.ts +5 -1
- package/src/modes/rpc/rpc-mode.ts +99 -81
- package/src/prompts/tools/patch.md +76 -0
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/{edit.md → replace.md} +1 -0
- package/src/utils/shell.ts +0 -40
- package/src/core/tools/edit-diff.ts +0 -574
- package/src/core/tools/edit.ts +0 -345
package/src/core/tools/edit.ts
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import type { AgentTool, AgentToolContext, ToolCallContext } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
|
-
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
|
-
import { Type } from "@sinclair/typebox";
|
|
5
|
-
import { getLanguageFromPath, type Theme } from "../../modes/interactive/theme/theme";
|
|
6
|
-
import editDescription from "../../prompts/tools/edit.md" with { type: "text" };
|
|
7
|
-
import type { RenderResultOptions } from "../custom-tools/types";
|
|
8
|
-
import { renderPromptTemplate } from "../prompt-templates";
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_FUZZY_THRESHOLD,
|
|
11
|
-
detectLineEnding,
|
|
12
|
-
type EditDiffError,
|
|
13
|
-
type EditDiffResult,
|
|
14
|
-
EditMatchError,
|
|
15
|
-
findEditMatch,
|
|
16
|
-
generateDiffString,
|
|
17
|
-
normalizeToLF,
|
|
18
|
-
restoreLineEndings,
|
|
19
|
-
stripBom,
|
|
20
|
-
} from "./edit-diff";
|
|
21
|
-
import type { ToolSession } from "./index";
|
|
22
|
-
import { createLspWritethrough, type FileDiagnosticsResult, writethroughNoop } from "./lsp/index";
|
|
23
|
-
import { resolveToCwd } from "./path-utils";
|
|
24
|
-
import { createToolUIKit, formatExpandHint, getDiffStats, shortenPath, truncateDiffByHunk } from "./render-utils";
|
|
25
|
-
|
|
26
|
-
const editSchema = Type.Object({
|
|
27
|
-
path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
|
|
28
|
-
oldText: Type.String({
|
|
29
|
-
description: "Text to find and replace (high-confidence fuzzy matching for whitespace/indentation is always on)",
|
|
30
|
-
}),
|
|
31
|
-
newText: Type.String({ description: "New text to replace the old text with" }),
|
|
32
|
-
all: Type.Optional(Type.Boolean({ description: "Replace all occurrences instead of requiring unique match" })),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export interface EditToolDetails {
|
|
36
|
-
/** Unified diff of the changes made */
|
|
37
|
-
diff: string;
|
|
38
|
-
/** Line number of the first change in the new file (for editor navigation) */
|
|
39
|
-
firstChangedLine?: number;
|
|
40
|
-
/** Diagnostic result (if available) */
|
|
41
|
-
diagnostics?: FileDiagnosticsResult;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const LSP_BATCH_TOOLS = new Set(["edit", "write"]);
|
|
45
|
-
|
|
46
|
-
function getLspBatchRequest(toolCall: ToolCallContext | undefined): { id: string; flush: boolean } | undefined {
|
|
47
|
-
if (!toolCall) {
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
const hasOtherWrites = toolCall.toolCalls.some(
|
|
51
|
-
(call, index) => index !== toolCall.index && LSP_BATCH_TOOLS.has(call.name),
|
|
52
|
-
);
|
|
53
|
-
if (!hasOtherWrites) {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some((call) => LSP_BATCH_TOOLS.has(call.name));
|
|
57
|
-
return { id: toolCall.batchId, flush: !hasLaterWrites };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function createEditTool(session: ToolSession): AgentTool<typeof editSchema> {
|
|
61
|
-
const allowFuzzy = session.settings?.getEditFuzzyMatch() ?? true;
|
|
62
|
-
const enableLsp = session.enableLsp ?? true;
|
|
63
|
-
const enableDiagnostics = enableLsp ? (session.settings?.getLspDiagnosticsOnEdit() ?? false) : false;
|
|
64
|
-
const enableFormat = enableLsp ? (session.settings?.getLspFormatOnWrite() ?? true) : false;
|
|
65
|
-
const writethrough = enableLsp
|
|
66
|
-
? createLspWritethrough(session.cwd, { enableFormat, enableDiagnostics })
|
|
67
|
-
: writethroughNoop;
|
|
68
|
-
return {
|
|
69
|
-
name: "edit",
|
|
70
|
-
label: "Edit",
|
|
71
|
-
description: renderPromptTemplate(editDescription),
|
|
72
|
-
parameters: editSchema,
|
|
73
|
-
execute: async (
|
|
74
|
-
_toolCallId: string,
|
|
75
|
-
{ path, oldText, newText, all }: { path: string; oldText: string; newText: string; all?: boolean },
|
|
76
|
-
signal?: AbortSignal,
|
|
77
|
-
_onUpdate?: unknown,
|
|
78
|
-
context?: AgentToolContext,
|
|
79
|
-
) => {
|
|
80
|
-
// Reject .ipynb files - use NotebookEdit tool instead
|
|
81
|
-
if (path.endsWith(".ipynb")) {
|
|
82
|
-
throw new Error("Cannot edit Jupyter notebooks with the Edit tool. Use the NotebookEdit tool instead.");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const absolutePath = resolveToCwd(path, session.cwd);
|
|
86
|
-
|
|
87
|
-
const file = Bun.file(absolutePath);
|
|
88
|
-
if (!(await file.exists())) {
|
|
89
|
-
throw new Error(`File not found: ${path}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const rawContent = await file.text();
|
|
93
|
-
|
|
94
|
-
// Strip BOM before matching (LLM won't include invisible BOM in oldText)
|
|
95
|
-
const { bom, text: content } = stripBom(rawContent);
|
|
96
|
-
|
|
97
|
-
const originalEnding = detectLineEnding(content);
|
|
98
|
-
const normalizedContent = normalizeToLF(content);
|
|
99
|
-
const normalizedOldText = normalizeToLF(oldText);
|
|
100
|
-
const normalizedNewText = normalizeToLF(newText);
|
|
101
|
-
|
|
102
|
-
let normalizedNewContent: string;
|
|
103
|
-
let replacementCount = 0;
|
|
104
|
-
|
|
105
|
-
if (all) {
|
|
106
|
-
// Replace all occurrences mode with fuzzy matching
|
|
107
|
-
normalizedNewContent = normalizedContent;
|
|
108
|
-
|
|
109
|
-
// First check: if exact matches exist, use simple replaceAll
|
|
110
|
-
const exactCount = normalizedContent.split(normalizedOldText).length - 1;
|
|
111
|
-
if (exactCount > 0) {
|
|
112
|
-
normalizedNewContent = normalizedContent.split(normalizedOldText).join(normalizedNewText);
|
|
113
|
-
replacementCount = exactCount;
|
|
114
|
-
} else {
|
|
115
|
-
// No exact matches - try fuzzy matching iteratively
|
|
116
|
-
while (true) {
|
|
117
|
-
const matchOutcome = findEditMatch(normalizedNewContent, normalizedOldText, {
|
|
118
|
-
allowFuzzy,
|
|
119
|
-
similarityThreshold: DEFAULT_FUZZY_THRESHOLD,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// In all mode, use closest match if it passes threshold (even with multiple matches)
|
|
123
|
-
const match =
|
|
124
|
-
matchOutcome.match ||
|
|
125
|
-
(allowFuzzy && matchOutcome.closest && matchOutcome.closest.confidence >= DEFAULT_FUZZY_THRESHOLD
|
|
126
|
-
? matchOutcome.closest
|
|
127
|
-
: undefined);
|
|
128
|
-
|
|
129
|
-
if (!match) {
|
|
130
|
-
if (replacementCount === 0) {
|
|
131
|
-
throw new EditMatchError(path, normalizedOldText, matchOutcome.closest, {
|
|
132
|
-
allowFuzzy,
|
|
133
|
-
similarityThreshold: DEFAULT_FUZZY_THRESHOLD,
|
|
134
|
-
fuzzyMatches: matchOutcome.fuzzyMatches,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
normalizedNewContent =
|
|
141
|
-
normalizedNewContent.substring(0, match.startIndex) +
|
|
142
|
-
normalizedNewText +
|
|
143
|
-
normalizedNewContent.substring(match.startIndex + match.actualText.length);
|
|
144
|
-
replacementCount++;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
// Single replacement mode with fuzzy matching
|
|
149
|
-
const matchOutcome = findEditMatch(normalizedContent, normalizedOldText, {
|
|
150
|
-
allowFuzzy,
|
|
151
|
-
similarityThreshold: DEFAULT_FUZZY_THRESHOLD,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
if (matchOutcome.occurrences && matchOutcome.occurrences > 1) {
|
|
155
|
-
throw new Error(
|
|
156
|
-
`Found ${matchOutcome.occurrences} occurrences of the text in ${path}. The text must be unique. Please provide more context to make it unique, or use all: true to replace all.`,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (!matchOutcome.match) {
|
|
161
|
-
throw new EditMatchError(path, normalizedOldText, matchOutcome.closest, {
|
|
162
|
-
allowFuzzy,
|
|
163
|
-
similarityThreshold: DEFAULT_FUZZY_THRESHOLD,
|
|
164
|
-
fuzzyMatches: matchOutcome.fuzzyMatches,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const match = matchOutcome.match;
|
|
169
|
-
normalizedNewContent =
|
|
170
|
-
normalizedContent.substring(0, match.startIndex) +
|
|
171
|
-
normalizedNewText +
|
|
172
|
-
normalizedContent.substring(match.startIndex + match.actualText.length);
|
|
173
|
-
replacementCount = 1;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Verify the replacement actually changed something
|
|
177
|
-
if (normalizedContent === normalizedNewContent) {
|
|
178
|
-
throw new Error(
|
|
179
|
-
`No changes made to ${path}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`,
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const finalContent = bom + restoreLineEndings(normalizedNewContent, originalEnding);
|
|
184
|
-
const batchRequest = getLspBatchRequest(context?.toolCall);
|
|
185
|
-
const diagnostics = await writethrough(absolutePath, finalContent, signal, file, batchRequest);
|
|
186
|
-
|
|
187
|
-
const diffResult = generateDiffString(normalizedContent, normalizedNewContent);
|
|
188
|
-
|
|
189
|
-
// Build result text
|
|
190
|
-
let resultText =
|
|
191
|
-
replacementCount > 1
|
|
192
|
-
? `Successfully replaced ${replacementCount} occurrences in ${path}.`
|
|
193
|
-
: `Successfully replaced text in ${path}.`;
|
|
194
|
-
|
|
195
|
-
const messages = diagnostics?.messages;
|
|
196
|
-
if (messages && messages.length > 0) {
|
|
197
|
-
resultText += `\n\nLSP Diagnostics (${diagnostics.summary}):\n`;
|
|
198
|
-
resultText += messages.map((d) => ` ${d}`).join("\n");
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
content: [
|
|
203
|
-
{
|
|
204
|
-
type: "text",
|
|
205
|
-
text: resultText,
|
|
206
|
-
},
|
|
207
|
-
],
|
|
208
|
-
details: {
|
|
209
|
-
diff: diffResult.diff,
|
|
210
|
-
firstChangedLine: diffResult.firstChangedLine,
|
|
211
|
-
diagnostics: diagnostics,
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// =============================================================================
|
|
219
|
-
// TUI Renderer
|
|
220
|
-
// =============================================================================
|
|
221
|
-
|
|
222
|
-
interface EditRenderArgs {
|
|
223
|
-
path?: string;
|
|
224
|
-
file_path?: string;
|
|
225
|
-
oldText?: string;
|
|
226
|
-
newText?: string;
|
|
227
|
-
all?: boolean;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** Extended context for edit tool rendering */
|
|
231
|
-
export interface EditRenderContext {
|
|
232
|
-
/** Pre-computed diff preview (computed before tool executes) */
|
|
233
|
-
editDiffPreview?: EditDiffResult | EditDiffError;
|
|
234
|
-
/** Function to render diff text with syntax highlighting */
|
|
235
|
-
renderDiff?: (diffText: string, options?: { filePath?: string }) => string;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const EDIT_DIFF_PREVIEW_HUNKS = 2;
|
|
239
|
-
const EDIT_DIFF_PREVIEW_LINES = 24;
|
|
240
|
-
|
|
241
|
-
function countLines(text: string): number {
|
|
242
|
-
if (!text) return 0;
|
|
243
|
-
return text.split("\n").length;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function formatMetadataLine(lineCount: number | null, language: string | undefined, uiTheme: Theme): string {
|
|
247
|
-
const icon = uiTheme.getLangIcon(language);
|
|
248
|
-
if (lineCount !== null) {
|
|
249
|
-
return uiTheme.fg("dim", `${icon} ${lineCount} lines`);
|
|
250
|
-
}
|
|
251
|
-
return uiTheme.fg("dim", `${icon}`);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export const editToolRenderer = {
|
|
255
|
-
mergeCallAndResult: true,
|
|
256
|
-
renderCall(args: EditRenderArgs, uiTheme: Theme): Component {
|
|
257
|
-
const ui = createToolUIKit(uiTheme);
|
|
258
|
-
const rawPath = args.file_path || args.path || "";
|
|
259
|
-
const filePath = shortenPath(rawPath);
|
|
260
|
-
const editLanguage = getLanguageFromPath(rawPath) ?? "text";
|
|
261
|
-
const editIcon = uiTheme.fg("muted", uiTheme.getLangIcon(editLanguage));
|
|
262
|
-
const pathDisplay = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", uiTheme.format.ellipsis);
|
|
263
|
-
|
|
264
|
-
const text = `${ui.title("Edit")} ${editIcon} ${pathDisplay}`;
|
|
265
|
-
return new Text(text, 0, 0);
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
renderResult(
|
|
269
|
-
result: { content: Array<{ type: string; text?: string }>; details?: EditToolDetails; isError?: boolean },
|
|
270
|
-
options: RenderResultOptions & { renderContext?: EditRenderContext },
|
|
271
|
-
uiTheme: Theme,
|
|
272
|
-
args?: EditRenderArgs,
|
|
273
|
-
): Component {
|
|
274
|
-
const ui = createToolUIKit(uiTheme);
|
|
275
|
-
const { expanded, renderContext } = options;
|
|
276
|
-
const rawPath = args?.file_path || args?.path || "";
|
|
277
|
-
const filePath = shortenPath(rawPath);
|
|
278
|
-
const editLanguage = getLanguageFromPath(rawPath) ?? "text";
|
|
279
|
-
const editIcon = uiTheme.fg("muted", uiTheme.getLangIcon(editLanguage));
|
|
280
|
-
const editDiffPreview = renderContext?.editDiffPreview;
|
|
281
|
-
const renderDiffFn = renderContext?.renderDiff ?? ((t: string) => t);
|
|
282
|
-
|
|
283
|
-
// Build path display with line number if available
|
|
284
|
-
let pathDisplay = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", uiTheme.format.ellipsis);
|
|
285
|
-
const firstChangedLine =
|
|
286
|
-
(editDiffPreview && "firstChangedLine" in editDiffPreview ? editDiffPreview.firstChangedLine : undefined) ||
|
|
287
|
-
(result.details && !result.isError ? result.details.firstChangedLine : undefined);
|
|
288
|
-
if (firstChangedLine) {
|
|
289
|
-
pathDisplay += uiTheme.fg("warning", `:${firstChangedLine}`);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
let text = `${uiTheme.fg("toolTitle", uiTheme.bold("Edit"))} ${editIcon} ${pathDisplay}`;
|
|
293
|
-
|
|
294
|
-
const editLineCount = countLines(args?.newText ?? args?.oldText ?? "");
|
|
295
|
-
text += `\n${formatMetadataLine(editLineCount, editLanguage, uiTheme)}`;
|
|
296
|
-
|
|
297
|
-
if (result.isError) {
|
|
298
|
-
// Show error from result
|
|
299
|
-
const errorText = result.content?.find((c) => c.type === "text")?.text ?? "";
|
|
300
|
-
if (errorText) {
|
|
301
|
-
text += `\n\n${uiTheme.fg("error", errorText)}`;
|
|
302
|
-
}
|
|
303
|
-
} else if (editDiffPreview) {
|
|
304
|
-
// Use cached diff preview (works both before and after execution)
|
|
305
|
-
if ("error" in editDiffPreview) {
|
|
306
|
-
text += `\n\n${uiTheme.fg("error", editDiffPreview.error)}`;
|
|
307
|
-
} else if (editDiffPreview.diff) {
|
|
308
|
-
const diffStats = getDiffStats(editDiffPreview.diff);
|
|
309
|
-
text += `\n${uiTheme.fg("dim", uiTheme.format.bracketLeft)}${ui.formatDiffStats(
|
|
310
|
-
diffStats.added,
|
|
311
|
-
diffStats.removed,
|
|
312
|
-
diffStats.hunks,
|
|
313
|
-
)}${uiTheme.fg("dim", uiTheme.format.bracketRight)}`;
|
|
314
|
-
|
|
315
|
-
const {
|
|
316
|
-
text: diffText,
|
|
317
|
-
hiddenHunks,
|
|
318
|
-
hiddenLines,
|
|
319
|
-
} = expanded
|
|
320
|
-
? { text: editDiffPreview.diff, hiddenHunks: 0, hiddenLines: 0 }
|
|
321
|
-
: truncateDiffByHunk(editDiffPreview.diff, EDIT_DIFF_PREVIEW_HUNKS, EDIT_DIFF_PREVIEW_LINES);
|
|
322
|
-
|
|
323
|
-
text += `\n\n${renderDiffFn(diffText, { filePath: rawPath })}`;
|
|
324
|
-
if (!expanded && (hiddenHunks > 0 || hiddenLines > 0)) {
|
|
325
|
-
const remainder: string[] = [];
|
|
326
|
-
if (hiddenHunks > 0) remainder.push(`${hiddenHunks} more hunks`);
|
|
327
|
-
if (hiddenLines > 0) remainder.push(`${hiddenLines} more lines`);
|
|
328
|
-
text += uiTheme.fg(
|
|
329
|
-
"toolOutput",
|
|
330
|
-
`\n${uiTheme.format.ellipsis} (${remainder.join(", ")}) ${formatExpandHint(uiTheme)}`,
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Show LSP diagnostics if available
|
|
337
|
-
if (result.details?.diagnostics) {
|
|
338
|
-
text += ui.formatDiagnostics(result.details.diagnostics, expanded, (fp: string) =>
|
|
339
|
-
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return new Text(text, 0, 0);
|
|
344
|
-
},
|
|
345
|
-
};
|