@heyhuynhgiabuu/pi-pretty 0.6.29 → 0.6.31
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 +28 -0
- package/dist/render.d.ts +10 -7
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +41 -25
- package/dist/render.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +18 -13
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +15 -17
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +10 -8
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +11 -9
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +25 -21
- package/dist/tools/read.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/render.ts +54 -24
- package/src/tools/bash.ts +29 -23
- package/src/tools/find.ts +20 -16
- package/src/tools/grep.ts +10 -6
- package/src/tools/ls.ts +17 -8
- package/src/tools/read.ts +29 -30
- package/src/types.ts +5 -0
package/src/render.ts
CHANGED
|
@@ -200,7 +200,16 @@ export function preserveBoxBackground(ansi: string): string {
|
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
/** Left indent for self-rendered rows: host `ctx.outputPad` spaces, falling back to 1 on older/invalid hosts. */
|
|
204
|
+
export function toolIndent(ctx?: Pick<RenderContext, "outputPad">): string {
|
|
205
|
+
const pad = ctx?.outputPad;
|
|
206
|
+
// Reject unusable values, including pads wider than the terminal (avoids giant allocations).
|
|
207
|
+
return typeof pad === "number" && Number.isInteger(pad) && pad >= 0 && pad <= termWidth()
|
|
208
|
+
? " ".repeat(pad)
|
|
209
|
+
: TOOL_RESULT_INDENT;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function fillToolBackground(text: string, bg = BG_BASE, width?: number, indent = TOOL_RESULT_INDENT): string {
|
|
204
213
|
return text
|
|
205
214
|
.split("\n")
|
|
206
215
|
.map((line) => {
|
|
@@ -209,7 +218,8 @@ export function fillToolBackground(text: string, bg = BG_BASE, width?: number):
|
|
|
209
218
|
return bg ? bg + stripped : stripped;
|
|
210
219
|
}
|
|
211
220
|
const plainLead = line.replace(ANSI_CAPTURE_RE, "");
|
|
212
|
-
|
|
221
|
+
// Indented rows keep their natural width; with no indent every row is padded.
|
|
222
|
+
const skipPad = indent !== "" && (line.startsWith(indent) || plainLead.startsWith(indent));
|
|
213
223
|
const fitted = truncateToWidth(line, width, "", !skipPad);
|
|
214
224
|
const stripped = preserveBoxBackground(fitted);
|
|
215
225
|
return bg ? bg + stripped : stripped;
|
|
@@ -218,8 +228,8 @@ export function fillToolBackground(text: string, bg = BG_BASE, width?: number):
|
|
|
218
228
|
}
|
|
219
229
|
|
|
220
230
|
/** Add exactly the missing terminal row after a rendered tool body. */
|
|
221
|
-
export function fillToolBody(text: string, bg = BG_BASE, width?: number): string {
|
|
222
|
-
return fillToolBackground(text.endsWith("\n") ? text : `${text}\n`, bg, width);
|
|
231
|
+
export function fillToolBody(text: string, bg = BG_BASE, width?: number, indent = TOOL_RESULT_INDENT): string {
|
|
232
|
+
return fillToolBackground(text.endsWith("\n") ? text : `${text}\n`, bg, width, indent);
|
|
223
233
|
}
|
|
224
234
|
|
|
225
235
|
const TOOL_TITLE_STATE_KEY = "__piPrettyToolTitle";
|
|
@@ -250,29 +260,44 @@ function lnum(n: number, w: number): string {
|
|
|
250
260
|
// Tool metrics line
|
|
251
261
|
// ---------------------------------------------------------------------------
|
|
252
262
|
|
|
253
|
-
|
|
263
|
+
/** Prefer the host's final duration (0 is valid); fall back to wrapper-recorded metadata. */
|
|
264
|
+
function resolveDurationMs(
|
|
265
|
+
result: AgentToolResult<Record<string, unknown>>,
|
|
266
|
+
ctx?: Pick<RenderContext, "durationMs">,
|
|
267
|
+
): number | undefined {
|
|
268
|
+
const host = ctx?.durationMs;
|
|
269
|
+
if (typeof host === "number" && Number.isFinite(host) && host >= 0) return host;
|
|
270
|
+
const details = result.details as Record<string, unknown> | undefined;
|
|
271
|
+
return details?.[ELAPSED_KEY] as number | undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function renderToolMetrics(
|
|
275
|
+
result: AgentToolResult<Record<string, unknown>>,
|
|
276
|
+
ctx?: Pick<RenderContext, "durationMs">,
|
|
277
|
+
): string {
|
|
254
278
|
const details = result.details as Record<string, unknown> | undefined;
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
const chars = formatCharCount(details[CHARS_KEY] as number | undefined);
|
|
279
|
+
const elapsed = formatElapsedMs(resolveDurationMs(result, ctx));
|
|
280
|
+
const chars = formatCharCount(details?.[CHARS_KEY] as number | undefined);
|
|
258
281
|
if (!elapsed && !chars) return "";
|
|
259
282
|
return `${FG_DIM}· ${[elapsed, chars].filter(Boolean).join(" · ")}${RST}`;
|
|
260
283
|
}
|
|
261
284
|
|
|
262
|
-
export function renderToolDuration(
|
|
263
|
-
|
|
264
|
-
|
|
285
|
+
export function renderToolDuration(
|
|
286
|
+
result: AgentToolResult<Record<string, unknown>>,
|
|
287
|
+
ctx?: Pick<RenderContext, "durationMs">,
|
|
288
|
+
): string {
|
|
289
|
+
return formatElapsedMs(resolveDurationMs(result, ctx));
|
|
265
290
|
}
|
|
266
291
|
|
|
267
292
|
// ---------------------------------------------------------------------------
|
|
268
293
|
// Error renderer
|
|
269
294
|
// ---------------------------------------------------------------------------
|
|
270
295
|
|
|
271
|
-
export function renderToolError(error: string, theme: ThemeLike): string {
|
|
296
|
+
export function renderToolError(error: string, theme: ThemeLike, indent = TOOL_RESULT_INDENT): string {
|
|
272
297
|
const body = compactErrorLines(error)
|
|
273
|
-
.map((line) => `${
|
|
298
|
+
.map((line) => `${indent}${line ? theme.fg("error", line) : ""}`)
|
|
274
299
|
.join("\n");
|
|
275
|
-
return fillToolBody(body, BG_ERROR);
|
|
300
|
+
return fillToolBody(body, BG_ERROR, undefined, indent);
|
|
276
301
|
}
|
|
277
302
|
|
|
278
303
|
// ---------------------------------------------------------------------------
|
|
@@ -305,14 +330,14 @@ export async function renderFileContent(
|
|
|
305
330
|
// Bash — colored exit status
|
|
306
331
|
// ---------------------------------------------------------------------------
|
|
307
332
|
|
|
308
|
-
export function renderBashOutput(text: string): string {
|
|
333
|
+
export function renderBashOutput(text: string, indent = TOOL_RESULT_INDENT): string {
|
|
309
334
|
const lines = text.split("\n");
|
|
310
335
|
const maxShow = MAX_PREVIEW_LINES;
|
|
311
336
|
const show = lines.slice(0, maxShow);
|
|
312
337
|
const remaining = lines.length - maxShow;
|
|
313
338
|
|
|
314
339
|
let body = show.join("\n");
|
|
315
|
-
if (remaining > 0) body += `\n${
|
|
340
|
+
if (remaining > 0) body += `\n${indent}${FG_DIM}… ${remaining} more lines${RST}`;
|
|
316
341
|
|
|
317
342
|
return body;
|
|
318
343
|
}
|
|
@@ -351,6 +376,7 @@ export function renderTree(text: string, _basePath: string): string {
|
|
|
351
376
|
// Find — grouped file list (plain, no tree characters or icons)
|
|
352
377
|
// ---------------------------------------------------------------------------
|
|
353
378
|
|
|
379
|
+
/** File rows keep the structural one-space indent; the tool applies host outer padding. */
|
|
354
380
|
export function renderFindResults(text: string, theme?: ThemeLike): string {
|
|
355
381
|
const lines = text.trim().split("\n").filter(Boolean);
|
|
356
382
|
if (!lines.length) return theme ? theme.fg("dim", "(no matches)") : `${FG_DIM}(no matches)${RST}`;
|
|
@@ -392,7 +418,7 @@ export function renderFindResults(text: string, theme?: ThemeLike): string {
|
|
|
392
418
|
// Grep — highlighted matches with line numbers
|
|
393
419
|
// ---------------------------------------------------------------------------
|
|
394
420
|
|
|
395
|
-
export async function renderGrepResults(text: string, pattern: string): Promise<string> {
|
|
421
|
+
export async function renderGrepResults(text: string, pattern: string, indent = TOOL_RESULT_INDENT): Promise<string> {
|
|
396
422
|
const lines = normalizeLineEndings(text).split("\n");
|
|
397
423
|
if (!lines.length || (lines.length === 1 && !lines[0].trim())) return `${FG_DIM}(no matches)${RST}`;
|
|
398
424
|
|
|
@@ -423,10 +449,10 @@ export async function renderGrepResults(text: string, pattern: string): Promise<
|
|
|
423
449
|
const nw = Math.max(3, lineNo.length);
|
|
424
450
|
let display = content;
|
|
425
451
|
if (re) display = content.replace(re, `${RST}${FG_YELLOW}\x1b[1m$1${RST}`);
|
|
426
|
-
out.push(`${
|
|
452
|
+
out.push(`${indent}${lnum(Number(lineNo), nw)} ${FG_RULE}│${RST} ${display}${RST}`);
|
|
427
453
|
count++;
|
|
428
454
|
} else if (line.trim() === "--") {
|
|
429
|
-
out.push(`${
|
|
455
|
+
out.push(`${indent}${FG_DIM} ···${RST}`);
|
|
430
456
|
} else if (line.trim()) {
|
|
431
457
|
out.push(line);
|
|
432
458
|
count++;
|
|
@@ -445,7 +471,7 @@ export function makeRenderCall(toolName: string) {
|
|
|
445
471
|
resolveBaseBackground(theme);
|
|
446
472
|
const text = ctx.lastComponent ?? new TuiText("", 0, 0);
|
|
447
473
|
const bg = ctx.isError ? BG_ERROR : undefined;
|
|
448
|
-
text.setText(fillToolBackground(`${theme.fg("toolTitle", theme.bold(toolName))}`, bg));
|
|
474
|
+
text.setText(fillToolBackground(`${theme.fg("toolTitle", theme.bold(toolName))}`, bg, undefined, toolIndent(ctx)));
|
|
449
475
|
return text;
|
|
450
476
|
};
|
|
451
477
|
}
|
|
@@ -455,26 +481,30 @@ export function makeRenderResult() {
|
|
|
455
481
|
resolveBaseBackground(theme);
|
|
456
482
|
const text = ctx.lastComponent ?? new TuiText("", 0, 0);
|
|
457
483
|
if (ctx.isError) {
|
|
458
|
-
text.setText(renderToolError(getTextContent(result) || "Error", theme));
|
|
484
|
+
text.setText(renderToolError(getTextContent(result) || "Error", theme, toolIndent(ctx)));
|
|
459
485
|
return text;
|
|
460
486
|
}
|
|
461
487
|
const content = getTextContent(result);
|
|
462
488
|
if (content) {
|
|
463
489
|
const renderWidth = termWidth();
|
|
490
|
+
const indent = toolIndent(ctx);
|
|
464
491
|
const lines = content.split("\n");
|
|
465
492
|
const maxShow = ctx.expanded ? lines.length : Math.min(lines.length, MAX_PREVIEW_LINES);
|
|
466
493
|
const preview = lines.slice(0, maxShow).join("\n");
|
|
467
494
|
const more = lines.length > maxShow ? `\n${FG_DIM}... ${lines.length - maxShow} more lines${RST}` : "";
|
|
468
|
-
const metrics = renderToolMetrics(result);
|
|
495
|
+
const metrics = renderToolMetrics(result, ctx);
|
|
469
496
|
text.setText(
|
|
470
497
|
fillToolBody(
|
|
471
|
-
`${
|
|
498
|
+
`${indent}${preview}${more}${metrics ? `\n${indent}${metrics}` : ""}`,
|
|
472
499
|
undefined,
|
|
473
500
|
renderWidth,
|
|
501
|
+
indent,
|
|
474
502
|
),
|
|
475
503
|
);
|
|
476
504
|
} else {
|
|
477
|
-
text.setText(
|
|
505
|
+
text.setText(
|
|
506
|
+
fillToolBody(`${toolIndent(ctx)}${theme.fg("dim", "(no text output)")}`, undefined, undefined, toolIndent(ctx)),
|
|
507
|
+
);
|
|
478
508
|
}
|
|
479
509
|
return text;
|
|
480
510
|
};
|
package/src/tools/bash.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* pi-pretty: bash tool -- command execution with styled output. */
|
|
2
2
|
|
|
3
3
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { resolveBaseBackground,
|
|
4
|
+
import { resolveBaseBackground, termWidth } from "../config.js";
|
|
5
5
|
import {
|
|
6
6
|
CHARS_KEY,
|
|
7
7
|
compactErrorLines,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
renderToolDuration,
|
|
18
18
|
renderToolError,
|
|
19
19
|
setCollapsedToolTitle,
|
|
20
|
+
toolIndent,
|
|
20
21
|
} from "../render.js";
|
|
21
22
|
import { resolveTextCtor } from "../tui-text.js";
|
|
22
23
|
import type { BashDetails, ComponentLike, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
@@ -92,6 +93,7 @@ export function registerBashTool(
|
|
|
92
93
|
],
|
|
93
94
|
parameters: sdkTool.parameters,
|
|
94
95
|
constrainedSampling: sdkTool.constrainedSampling,
|
|
96
|
+
outputSchema: sdkTool.outputSchema,
|
|
95
97
|
renderShell: "self",
|
|
96
98
|
|
|
97
99
|
execute: wrapExecuteWithMetrics(async (tid, params, sig, upd, ctx: ExtensionContext) => {
|
|
@@ -105,6 +107,7 @@ export function registerBashTool(
|
|
|
105
107
|
resolveBaseBackground(theme);
|
|
106
108
|
const text = ctx.lastComponent ?? new TC("", 0, 0);
|
|
107
109
|
const t = typeof args.timeout === "number" ? ` ${theme.fg("muted", `(timeout ${args.timeout}s)`)}` : "";
|
|
110
|
+
const ind = toolIndent(ctx);
|
|
108
111
|
const tw = termWidth() || 80;
|
|
109
112
|
const rawCmd = String(args.command ?? "");
|
|
110
113
|
const headerBudget = ctx.expanded ? tw : Math.max(8, tw - 20);
|
|
@@ -116,11 +119,7 @@ export function registerBashTool(
|
|
|
116
119
|
: rawCmd;
|
|
117
120
|
const commandLabel = theme.fg(ctx.isError ? "error" : "toolTitle", theme.bold(`$ ${cmd}`));
|
|
118
121
|
const renderTitle = (suffix = ""): string =>
|
|
119
|
-
fillToolBackground(
|
|
120
|
-
`\n${TOOL_RESULT_INDENT}${commandLabel}${t}${suffix}\n`,
|
|
121
|
-
undefined,
|
|
122
|
-
ctx.expanded ? undefined : tw,
|
|
123
|
-
);
|
|
122
|
+
fillToolBackground(`\n${ind}${commandLabel}${t}${suffix}\n`, undefined, ctx.expanded ? undefined : tw, ind);
|
|
124
123
|
rememberToolTitle(ctx, text, renderTitle);
|
|
125
124
|
text.setText(renderTitle());
|
|
126
125
|
return text;
|
|
@@ -153,26 +152,29 @@ export function registerBashTool(
|
|
|
153
152
|
const cleaned = stripBashExitStatusLine(d.text);
|
|
154
153
|
const output = isErr ? compactErrorLines(cleaned).join("\n") : cleaned;
|
|
155
154
|
const lineCount = output.split("\n").length;
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
155
|
+
const buildInfo = (): string =>
|
|
156
|
+
[
|
|
157
|
+
`${lineCount} lines`,
|
|
158
|
+
renderToolDuration(displayResult, ctx),
|
|
159
|
+
rejectedMetrics ? formatCharCount(rejectedMetrics.chars) : "",
|
|
160
|
+
!ctx.expanded ? "ctrl+o to expand" : "",
|
|
161
|
+
]
|
|
162
|
+
.filter(Boolean)
|
|
163
|
+
.map((part) => theme.fg("dim", part))
|
|
164
|
+
.join(theme.fg("dim", " · "));
|
|
165
|
+
const info = buildInfo();
|
|
166
166
|
const rw = termWidth();
|
|
167
167
|
|
|
168
168
|
if (setCollapsedToolTitle(ctx, text, ` ${info}`)) return text;
|
|
169
169
|
|
|
170
170
|
const renderFn = (w: number) => {
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const ind = toolIndent(ctx);
|
|
172
|
+
const header = `${ind}${buildInfo()}`;
|
|
173
|
+
if (!ctx.expanded) return fillToolBody(header, undefined, w, ind);
|
|
174
|
+
if (!output.trim()) return fillToolBody(header, undefined, w, ind);
|
|
173
175
|
const show = output.split("\n");
|
|
174
|
-
const out = [header, "", ...show.map((line: string) => `${
|
|
175
|
-
return fillToolBody(out.join("\n"), undefined, w);
|
|
176
|
+
const out = [header, "", ...show.map((line: string) => `${ind}${line}`)];
|
|
177
|
+
return fillToolBody(out.join("\n"), undefined, w, ind);
|
|
176
178
|
};
|
|
177
179
|
|
|
178
180
|
text.setText(renderFn(rw));
|
|
@@ -183,7 +185,7 @@ export function registerBashTool(
|
|
|
183
185
|
let key: string | undefined;
|
|
184
186
|
(text as unknown as Record<string, unknown>).render = (w: number) => {
|
|
185
187
|
const width = Math.max(1, Math.floor(w || termWidth()));
|
|
186
|
-
const k = `bash:${ctx.expanded ? "1" : "0"}:${width}:${d.exitCode ?? "killed"}:${output.length}:${renderToolDuration(displayResult)}`;
|
|
188
|
+
const k = `bash:${ctx.expanded ? "1" : "0"}:${width}:${d.exitCode ?? "killed"}:${output.length}:${renderToolDuration(displayResult, ctx)}:${toolIndent(ctx).length}`;
|
|
187
189
|
if (key !== k) {
|
|
188
190
|
text.setText(renderFn(width));
|
|
189
191
|
key = k;
|
|
@@ -195,13 +197,17 @@ export function registerBashTool(
|
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
if (ctx.isError) {
|
|
198
|
-
text.setText(renderToolError(tc || "Error", theme));
|
|
200
|
+
text.setText(renderToolError(tc || "Error", theme, toolIndent(ctx)));
|
|
199
201
|
return text;
|
|
200
202
|
}
|
|
201
203
|
const fc = displayResult.content?.[0];
|
|
204
|
+
const ind = toolIndent(ctx);
|
|
202
205
|
text.setText(
|
|
203
206
|
fillToolBody(
|
|
204
|
-
`${
|
|
207
|
+
`${ind}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 120) : "done")}`,
|
|
208
|
+
undefined,
|
|
209
|
+
undefined,
|
|
210
|
+
ind,
|
|
205
211
|
),
|
|
206
212
|
);
|
|
207
213
|
return text;
|
package/src/tools/find.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { isAbsolute, relative } from "node:path";
|
|
4
4
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground
|
|
5
|
+
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground } from "../config.js";
|
|
6
6
|
import { isLikelyGlobPattern, normalizeFindGlobPattern } from "../find-glob.js";
|
|
7
7
|
import { shortPath } from "../helpers.js";
|
|
8
8
|
import { NOTICE_PARTIAL_FILE_INDEX } from "../notices.js";
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
renderToolDuration,
|
|
15
15
|
renderToolError,
|
|
16
16
|
setCollapsedToolTitle,
|
|
17
|
+
toolIndent,
|
|
17
18
|
} from "../render.js";
|
|
18
19
|
import { resolveTextCtor } from "../tui-text.js";
|
|
19
20
|
import type { FffServiceWithCursor, FindDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
@@ -171,8 +172,9 @@ export function registerFindTool(
|
|
|
171
172
|
const pathPart = theme.fg("toolOutput", pathArg);
|
|
172
173
|
const limitPart = limit !== undefined && limit !== null ? theme.fg("dim", ` limit ${limit}`) : "";
|
|
173
174
|
const out = `${findLabel} ${patternPart}${inPart}${pathPart}${limitPart}`;
|
|
175
|
+
const ind = toolIndent(ctx);
|
|
174
176
|
const renderTitle = (suffix = ""): string =>
|
|
175
|
-
fillToolBackground(`\n${
|
|
177
|
+
fillToolBackground(`\n${ind}${out}${suffix}\n`, ctx.isError ? BG_ERROR : undefined, undefined, ind);
|
|
176
178
|
rememberToolTitle(ctx, text, renderTitle);
|
|
177
179
|
text.setText(renderTitle());
|
|
178
180
|
return text;
|
|
@@ -182,8 +184,9 @@ export function registerFindTool(
|
|
|
182
184
|
resolveBaseBackground(theme);
|
|
183
185
|
const r = result;
|
|
184
186
|
const text = (ctx as RenderCtxLike).lastComponent ?? new TC("", 0, 0);
|
|
187
|
+
const ind = toolIndent(ctx);
|
|
185
188
|
if (ctx.isError) {
|
|
186
|
-
text.setText(renderToolError(getText(r) || "Error", theme));
|
|
189
|
+
text.setText(renderToolError(getText(r) || "Error", theme, ind));
|
|
187
190
|
return text;
|
|
188
191
|
}
|
|
189
192
|
const d = r.details as FindDetails | undefined;
|
|
@@ -191,36 +194,37 @@ export function registerFindTool(
|
|
|
191
194
|
if (!d.text.trim()) {
|
|
192
195
|
const notice = d.notices?.length ? ` ${theme.fg("warning", `[${d.notices.join(". ")}]`)}` : "";
|
|
193
196
|
if (setCollapsedToolTitle(ctx, text, ` ${FG_DIM}0 files${RST}${notice}`)) return text;
|
|
194
|
-
const noticeStr = d.notices?.length
|
|
195
|
-
|
|
196
|
-
: "";
|
|
197
|
-
text.setText(fillToolBody(`${TOOL_RESULT_INDENT}${theme.fg("dim", "0 files")}${noticeStr}`));
|
|
197
|
+
const noticeStr = d.notices?.length ? `\n${ind}${theme.fg("warning", `[${d.notices.join(". ")}]`)}` : "";
|
|
198
|
+
text.setText(fillToolBody(`${ind}${theme.fg("dim", "0 files")}${noticeStr}`, undefined, undefined, ind));
|
|
198
199
|
return text;
|
|
199
200
|
}
|
|
200
201
|
if (!ctx.expanded) {
|
|
201
|
-
const duration = renderToolDuration(r);
|
|
202
|
+
const duration = renderToolDuration(r, ctx);
|
|
202
203
|
const summary = `${FG_DIM}${d.matchCount} files — ctrl+o to expand${RST}${duration ? `${FG_DIM}· ${duration}${RST}` : ""}`;
|
|
203
204
|
if (setCollapsedToolTitle(ctx, text, ` ${summary}`)) return text;
|
|
204
|
-
text.setText(fillToolBody(`${
|
|
205
|
+
text.setText(fillToolBody(`${ind}${summary}`, undefined, undefined, ind));
|
|
205
206
|
return text;
|
|
206
207
|
}
|
|
207
208
|
const rendered = renderFindResults(d.text, theme)
|
|
208
209
|
.split("\n")
|
|
209
|
-
.map((l) => `${
|
|
210
|
+
.map((l) => `${ind}${l}`)
|
|
210
211
|
.join("\n");
|
|
211
|
-
const noticeStr = d.notices?.length
|
|
212
|
-
|
|
213
|
-
: "";
|
|
214
|
-
const duration = renderToolDuration(r);
|
|
212
|
+
const noticeStr = d.notices?.length ? `\n${ind}${theme.fg("warning", `[${d.notices.join(". ")}]`)}` : "";
|
|
213
|
+
const duration = renderToolDuration(r, ctx);
|
|
215
214
|
text.setText(
|
|
216
215
|
fillToolBody(
|
|
217
|
-
`${
|
|
216
|
+
`${ind}${theme.fg("dim", `${d.matchCount} files`)}${duration ? `${FG_DIM}· ${duration}${RST}` : ""}\n${rendered}${noticeStr}`,
|
|
217
|
+
undefined,
|
|
218
|
+
undefined,
|
|
219
|
+
ind,
|
|
218
220
|
),
|
|
219
221
|
);
|
|
220
222
|
return text;
|
|
221
223
|
}
|
|
222
224
|
const fc = r.content?.[0] as TextContent | undefined;
|
|
223
|
-
text.setText(
|
|
225
|
+
text.setText(
|
|
226
|
+
fillToolBody(`${ind}${theme.fg("dim", fc?.text?.slice(0, 120) ?? "0 files")}`, undefined, undefined, ind),
|
|
227
|
+
);
|
|
224
228
|
return text;
|
|
225
229
|
},
|
|
226
230
|
} as unknown as ToolDefinition);
|
package/src/tools/grep.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* pi-pretty: grep tool -- FFF-backed text search with SDK fallback. */
|
|
2
2
|
|
|
3
3
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground
|
|
4
|
+
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground } from "../config.js";
|
|
5
5
|
import { fffFormatGrepText } from "../fff-helpers.js";
|
|
6
6
|
import { normalizeLineEndings, shortPath } from "../helpers.js";
|
|
7
7
|
import { NOTICE_PARTIAL_FILE_INDEX } from "../notices.js";
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
rememberToolTitle,
|
|
12
12
|
renderToolError,
|
|
13
13
|
setCollapsedToolTitle,
|
|
14
|
+
toolIndent,
|
|
14
15
|
} from "../render.js";
|
|
15
16
|
import { resolveTextCtor } from "../tui-text.js";
|
|
16
17
|
import type { FffServiceWithCursor, GrepDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
@@ -122,8 +123,9 @@ export function registerGrepTool(
|
|
|
122
123
|
if (limit !== undefined && limit !== null) out += theme.fg("dim", ` limit ${limit}`);
|
|
123
124
|
if (literal) out += theme.fg("dim", ` (literal)`);
|
|
124
125
|
if (caseInsensitive) out += theme.fg("dim", ` (case-insensitive)`);
|
|
126
|
+
const ind = toolIndent(ctx);
|
|
125
127
|
const renderTitle = (suffix = ""): string =>
|
|
126
|
-
fillToolBackground(`\n${
|
|
128
|
+
fillToolBackground(`\n${ind}${out}${suffix}\n`, ctx.isError ? BG_ERROR : undefined, undefined, ind);
|
|
127
129
|
rememberToolTitle(ctx, text, renderTitle);
|
|
128
130
|
text.setText(renderTitle());
|
|
129
131
|
return text;
|
|
@@ -132,6 +134,7 @@ export function registerGrepTool(
|
|
|
132
134
|
renderResult(result: Result, _opt: unknown, theme: ThemeLike, ctx: RenderCtxLike) {
|
|
133
135
|
resolveBaseBackground(theme);
|
|
134
136
|
const text = ctx.lastComponent ?? new T("", 0, 0);
|
|
137
|
+
const ind = toolIndent(ctx);
|
|
135
138
|
if (ctx.isError) {
|
|
136
139
|
text.setText(
|
|
137
140
|
renderToolError(
|
|
@@ -140,6 +143,7 @@ export function registerGrepTool(
|
|
|
140
143
|
.map((c) => c.text)
|
|
141
144
|
.join("\n") || "Error",
|
|
142
145
|
theme,
|
|
146
|
+
ind,
|
|
143
147
|
),
|
|
144
148
|
);
|
|
145
149
|
return text;
|
|
@@ -150,7 +154,7 @@ export function registerGrepTool(
|
|
|
150
154
|
if (!ctx.expanded) {
|
|
151
155
|
const summary = `${FG_DIM}${lines.length} lines — ctrl+o to expand${RST}`;
|
|
152
156
|
if (setCollapsedToolTitle(ctx, text, ` ${summary}`)) return text;
|
|
153
|
-
text.setText(fillToolBody(`${
|
|
157
|
+
text.setText(fillToolBody(`${ind}${summary}`, ctx.isError ? BG_ERROR : undefined, undefined, ind));
|
|
154
158
|
return text;
|
|
155
159
|
}
|
|
156
160
|
const maxShow = lines.length;
|
|
@@ -164,14 +168,14 @@ export function registerGrepTool(
|
|
|
164
168
|
if (remaining > 0) {
|
|
165
169
|
out.push(theme.fg("muted", `… (${remaining} more ${remaining === 1 ? "line" : "lines"}, to expand)`));
|
|
166
170
|
}
|
|
167
|
-
const body = out.map((l) => `${
|
|
168
|
-
text.setText(fillToolBody(body, ctx.isError ? BG_ERROR : undefined));
|
|
171
|
+
const body = out.map((l) => `${ind}${l}`).join("\n");
|
|
172
|
+
text.setText(fillToolBody(body, ctx.isError ? BG_ERROR : undefined, undefined, ind));
|
|
169
173
|
return text;
|
|
170
174
|
}
|
|
171
175
|
const fc = result.content?.[0];
|
|
172
176
|
const fallback = fc && "text" in fc ? String(fc.text).slice(0, 120) : "no matches";
|
|
173
177
|
text.setText(
|
|
174
|
-
fillToolBody(`${
|
|
178
|
+
fillToolBody(`${ind}${theme.fg("dim", fallback)}`, ctx.isError ? BG_ERROR : undefined, undefined, ind),
|
|
175
179
|
);
|
|
176
180
|
return text;
|
|
177
181
|
},
|
package/src/tools/ls.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* pi-pretty: ls tool -- directory listing with styled output. */
|
|
2
2
|
|
|
3
3
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground
|
|
4
|
+
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground } from "../config.js";
|
|
5
5
|
import { shortPath } from "../helpers.js";
|
|
6
6
|
import {
|
|
7
7
|
fillToolBackground,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
renderToolMetrics,
|
|
12
12
|
renderTree,
|
|
13
13
|
setCollapsedToolTitle,
|
|
14
|
+
toolIndent,
|
|
14
15
|
} from "../render.js";
|
|
15
16
|
import { resolveTextCtor } from "../tui-text.js";
|
|
16
17
|
import type { LsDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
@@ -60,8 +61,9 @@ export function registerLsTool(
|
|
|
60
61
|
let out = theme.fg("toolTitle", theme.bold("ls"));
|
|
61
62
|
if (path) out += ` ${theme.fg("accent", path)}`;
|
|
62
63
|
if (limit !== undefined && limit !== null) out += theme.fg("toolOutput", ` (limit ${limit})`);
|
|
64
|
+
const ind = toolIndent(ctx);
|
|
63
65
|
const renderTitle = (suffix = ""): string =>
|
|
64
|
-
fillToolBackground(`\n${
|
|
66
|
+
fillToolBackground(`\n${ind}${out}${suffix}\n`, ctx.isError ? BG_ERROR : undefined, undefined, ind);
|
|
65
67
|
rememberToolTitle(ctx, text, renderTitle);
|
|
66
68
|
text.setText(renderTitle());
|
|
67
69
|
return text;
|
|
@@ -71,25 +73,29 @@ export function registerLsTool(
|
|
|
71
73
|
resolveBaseBackground(theme);
|
|
72
74
|
|
|
73
75
|
const text = ctx.lastComponent ?? new TC("", 0, 0);
|
|
76
|
+
const ind = toolIndent(ctx);
|
|
74
77
|
if (ctx.isError) {
|
|
75
|
-
text.setText(renderToolError(getText(result) || "Error", theme));
|
|
78
|
+
text.setText(renderToolError(getText(result) || "Error", theme, ind));
|
|
76
79
|
return text;
|
|
77
80
|
}
|
|
78
81
|
const d = result.details as LsDetails | undefined;
|
|
79
82
|
if (d?._type === "lsResult" && d.text) {
|
|
80
83
|
if (!ctx.expanded) {
|
|
81
|
-
const summary = `${FG_DIM}${d.entryCount} entries — ctrl+o to expand${RST}${renderToolMetrics(result)}`;
|
|
84
|
+
const summary = `${FG_DIM}${d.entryCount} entries — ctrl+o to expand${RST}${renderToolMetrics(result, ctx)}`;
|
|
82
85
|
if (setCollapsedToolTitle(ctx, text, ` ${summary}`)) return text;
|
|
83
|
-
text.setText(fillToolBody(`${
|
|
86
|
+
text.setText(fillToolBody(`${ind}${summary}`, undefined, undefined, ind));
|
|
84
87
|
return text;
|
|
85
88
|
}
|
|
86
89
|
const rendered = renderTree(d.text, d.path)
|
|
87
90
|
.split("\n")
|
|
88
|
-
.map((l) => `${
|
|
91
|
+
.map((l) => `${ind}${l}`)
|
|
89
92
|
.join("\n");
|
|
90
93
|
text.setText(
|
|
91
94
|
fillToolBody(
|
|
92
|
-
`${
|
|
95
|
+
`${ind}${FG_DIM}${d.entryCount} entries${RST}${renderToolMetrics(result, ctx)}\n${rendered}`,
|
|
96
|
+
undefined,
|
|
97
|
+
undefined,
|
|
98
|
+
ind,
|
|
93
99
|
),
|
|
94
100
|
);
|
|
95
101
|
return text;
|
|
@@ -97,7 +103,10 @@ export function registerLsTool(
|
|
|
97
103
|
const fc = result.content?.[0];
|
|
98
104
|
text.setText(
|
|
99
105
|
fillToolBody(
|
|
100
|
-
`${
|
|
106
|
+
`${ind}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 120) : "done")}`,
|
|
107
|
+
undefined,
|
|
108
|
+
undefined,
|
|
109
|
+
ind,
|
|
101
110
|
),
|
|
102
111
|
);
|
|
103
112
|
return text;
|