@heyhuynhgiabuu/pi-pretty 0.6.16 → 0.6.17
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 +14 -0
- package/README.md +6 -16
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -18
- package/dist/config.js.map +1 -1
- package/dist/expand.d.ts +0 -6
- package/dist/expand.d.ts.map +1 -1
- package/dist/expand.js +0 -13
- package/dist/expand.js.map +1 -1
- package/dist/image.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -21
- package/dist/index.js.map +1 -1
- package/dist/render.d.ts +3 -5
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +9 -9
- package/dist/render.js.map +1 -1
- package/dist/tools/bash.d.ts +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +18 -23
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/find.d.ts +2 -2
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +12 -8
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/grep.d.ts +2 -2
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +5 -5
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/labels.d.ts +2 -0
- package/dist/tools/labels.d.ts.map +1 -0
- package/dist/tools/labels.js +7 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/ls.d.ts +1 -1
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +1 -1
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +22 -40
- package/dist/tools/read.js.map +1 -1
- package/dist/tui-text.d.ts.map +1 -1
- package/dist/tui-text.js +1 -5
- package/dist/tui-text.js.map +1 -1
- package/dist/types.d.ts +3 -27
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/collapsed-hint.ts +1 -1
- package/src/config.ts +67 -22
- package/src/expand.ts +0 -14
- package/src/find-glob.ts +1 -1
- package/src/image.ts +1 -1
- package/src/index.ts +25 -34
- package/src/notices.ts +1 -1
- package/src/render.ts +37 -35
- package/src/tools/bash.ts +24 -48
- package/src/tools/find.ts +22 -36
- package/src/tools/grep.ts +12 -15
- package/src/tools/labels.ts +3 -0
- package/src/tools/ls.ts +16 -10
- package/src/tools/metrics.ts +1 -1
- package/src/tools/read.ts +39 -59
- package/src/tui-text.ts +1 -5
- package/src/types.ts +3 -35
- package/dist/multi-grep-fallback.d.ts +0 -30
- package/dist/multi-grep-fallback.d.ts.map +0 -1
- package/dist/multi-grep-fallback.js +0 -205
- package/dist/multi-grep-fallback.js.map +0 -1
- package/dist/tools/multi-grep.d.ts +0 -7
- package/dist/tools/multi-grep.d.ts.map +0 -1
- package/dist/tools/multi-grep.js +0 -298
- package/dist/tools/multi-grep.js.map +0 -1
- package/src/multi-grep-fallback.ts +0 -248
- package/src/tools/multi-grep.ts +0 -339
package/src/tools/bash.ts
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
/* pi-pretty: bash tool -- command execution with styled output. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type AgentToolResult,
|
|
8
|
-
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import type { SdkToolDef, BashDetails, TextContent, ComponentLike, ThemeLike, RenderCtxLike } from "../types.js";
|
|
10
|
-
import {
|
|
11
|
-
TOOL_RESULT_INDENT,
|
|
12
|
-
resolveBaseBackground,
|
|
13
|
-
termWidth,
|
|
14
|
-
MAX_PREVIEW_LINES,
|
|
15
|
-
BG_BASE,
|
|
16
|
-
BG_ERROR,
|
|
17
|
-
FG_DIM,
|
|
18
|
-
FG_RULE,
|
|
19
|
-
RST,
|
|
20
|
-
} from "../config.js";
|
|
21
|
-
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
22
|
-
import { collapsedExpandFooter } from "../collapsed-hint.js";
|
|
23
|
-
import { renderBashOutput, renderToolError, renderToolMetrics, fillToolBackground } from "../render.js";
|
|
3
|
+
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { BG_BASE, FG_DIM, MAX_PREVIEW_LINES, resolveBaseBackground, TOOL_RESULT_INDENT, termWidth } from "../config.js";
|
|
5
|
+
import { compactErrorLines, inferBashExitCode, stripBashExitStatusLine } from "../helpers.js";
|
|
6
|
+
import { fillToolBackground, renderToolDuration, renderToolError } from "../render.js";
|
|
24
7
|
import { resolveTextCtor } from "../tui-text.js";
|
|
25
|
-
import {
|
|
8
|
+
import type { BashDetails, ComponentLike, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
9
|
+
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
26
10
|
|
|
27
11
|
type Result = AgentToolResult<Record<string, unknown>>;
|
|
28
12
|
|
|
@@ -40,8 +24,8 @@ export function registerBashTool(
|
|
|
40
24
|
label: "Bash",
|
|
41
25
|
description: sdkTool.description
|
|
42
26
|
? `${sdkTool.description} For text search: \`rg -n\`.`
|
|
43
|
-
: "Execute shell commands. For text search:
|
|
44
|
-
promptSnippet: "Execute commands via bash. For text search:
|
|
27
|
+
: "Execute shell commands. For text search: `rg -n`.",
|
|
28
|
+
promptSnippet: "Execute commands via bash. For text search: `rg -n`.",
|
|
45
29
|
promptGuidelines: [
|
|
46
30
|
"For text search: `rg -n`. If no results, try `rg -u` (respects .gitignore by default).",
|
|
47
31
|
"In rg: | means alternation, \\| means literal pipe. Opposite of GNU grep. Never use \\| for alternation.",
|
|
@@ -78,14 +62,11 @@ export function registerBashTool(
|
|
|
78
62
|
rawCmd.length === 0
|
|
79
63
|
? theme.fg("toolOutput", "...")
|
|
80
64
|
: !ctx.expanded && rawCmd.length > headerBudget
|
|
81
|
-
? rawCmd.slice(0, Math.max(1, headerBudget))
|
|
65
|
+
? `${rawCmd.slice(0, Math.max(1, headerBudget))}…`
|
|
82
66
|
: rawCmd;
|
|
67
|
+
const commandLabel = theme.fg(ctx.isError ? "error" : "toolTitle", theme.bold(`$ ${cmd}`));
|
|
83
68
|
text.setText(
|
|
84
|
-
fillToolBackground(
|
|
85
|
-
`\n${TOOL_RESULT_INDENT}${theme.fg("toolTitle", theme.bold(`$ ${cmd}`))}${t}`,
|
|
86
|
-
ctx.isError ? BG_ERROR : undefined,
|
|
87
|
-
ctx.expanded ? undefined : tw,
|
|
88
|
-
),
|
|
69
|
+
fillToolBackground(`\n${TOOL_RESULT_INDENT}${commandLabel}${t}`, undefined, ctx.expanded ? undefined : tw),
|
|
89
70
|
);
|
|
90
71
|
return text;
|
|
91
72
|
},
|
|
@@ -111,26 +92,22 @@ export function registerBashTool(
|
|
|
111
92
|
|
|
112
93
|
if (d?._type === "bashResult") {
|
|
113
94
|
const isErr = ctx.isError || (d.exitCode !== null && d.exitCode !== 0);
|
|
114
|
-
const bg = isErr ? BG_ERROR : undefined;
|
|
115
95
|
const cleaned = stripBashExitStatusLine(d.text);
|
|
116
96
|
const output = isErr ? compactErrorLines(cleaned).join("\n") : cleaned;
|
|
117
|
-
const { summary } = renderBashOutput(output, d.exitCode);
|
|
118
97
|
const lineCount = output.split("\n").length;
|
|
119
|
-
const info =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const header = `${TOOL_RESULT_INDENT}${
|
|
98
|
+
const info = [`${lineCount} lines`, renderToolDuration(result), !ctx.expanded ? "ctrl+o to expand" : ""]
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.map((part) => theme.fg("dim", part))
|
|
101
|
+
.join(theme.fg("dim", " · "));
|
|
102
|
+
const header = `${TOOL_RESULT_INDENT}${info}`;
|
|
124
103
|
const rw = termWidth();
|
|
125
104
|
|
|
126
105
|
const renderFn = (w: number) => {
|
|
127
|
-
if (!ctx.expanded) {
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
if (!output.trim()) return fillToolBackground(header, bg, w);
|
|
106
|
+
if (!ctx.expanded) return fillToolBackground(`${header}\n`, undefined, w);
|
|
107
|
+
if (!output.trim()) return fillToolBackground(`${header}\n`, undefined, w);
|
|
131
108
|
const show = output.split("\n");
|
|
132
|
-
const out = [header,
|
|
133
|
-
return fillToolBackground(out.join("\n")
|
|
109
|
+
const out = [header, "", ...show.map((line: string) => `${TOOL_RESULT_INDENT}${line}`)];
|
|
110
|
+
return fillToolBackground(`${out.join("\n")}\n`, undefined, w);
|
|
134
111
|
};
|
|
135
112
|
|
|
136
113
|
text.setText(renderFn(rw));
|
|
@@ -140,7 +117,7 @@ export function registerBashTool(
|
|
|
140
117
|
let key: string | undefined;
|
|
141
118
|
(text as unknown as Record<string, unknown>).render = (w: number) => {
|
|
142
119
|
const width = Math.max(1, Math.floor(w || termWidth()));
|
|
143
|
-
const k = `bash:${ctx.expanded ? "1" : "0"}:${width}:${d.exitCode ?? "killed"}:${output.length}:${
|
|
120
|
+
const k = `bash:${ctx.expanded ? "1" : "0"}:${width}:${d.exitCode ?? "killed"}:${output.length}:${renderToolDuration(result)}`;
|
|
144
121
|
if (key !== k) {
|
|
145
122
|
text.setText(renderFn(width));
|
|
146
123
|
key = k;
|
|
@@ -157,16 +134,15 @@ export function registerBashTool(
|
|
|
157
134
|
}
|
|
158
135
|
const fc = result.content?.[0];
|
|
159
136
|
text.setText(
|
|
160
|
-
fillToolBackground(
|
|
137
|
+
fillToolBackground(
|
|
138
|
+
`${TOOL_RESULT_INDENT}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 120) : "done")}`,
|
|
139
|
+
),
|
|
161
140
|
);
|
|
162
141
|
return text;
|
|
163
142
|
},
|
|
164
143
|
} as unknown as ToolDefinition<any, any, any>);
|
|
165
144
|
}
|
|
166
145
|
|
|
167
|
-
function rule(w: number): string {
|
|
168
|
-
return `${FG_RULE}${"\u2500".repeat(w)}${RST}`;
|
|
169
|
-
}
|
|
170
146
|
function getText(result: Result): string {
|
|
171
147
|
return (
|
|
172
148
|
((result.content ?? []) as TextContent[])
|
package/src/tools/find.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
/* pi-pretty: find tool -- FFF-backed file search with SDK (fd) fallback. */
|
|
2
2
|
|
|
3
3
|
import { isAbsolute, relative } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type ExtensionContext,
|
|
8
|
-
type AgentToolResult,
|
|
9
|
-
} from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import type { SdkToolDef, FindDetails, FffServiceWithCursor, TextContent, ThemeLike, RenderCtxLike } from "../types.js";
|
|
11
|
-
import { TOOL_RESULT_INDENT, BG_ERROR, FG_DIM, RST, resolveBaseBackground } from "../config.js";
|
|
4
|
+
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { BG_ERROR, FG_DIM, RST, resolveBaseBackground, TOOL_RESULT_INDENT } from "../config.js";
|
|
6
|
+
import { isLikelyGlobPattern, normalizeFindGlobPattern } from "../find-glob.js";
|
|
12
7
|
import { shortPath } from "../helpers.js";
|
|
13
|
-
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
14
|
-
import { renderFindResults, renderToolError, renderToolMetrics, fillToolBackground } from "../render.js";
|
|
15
|
-
import { resolveTextCtor } from "../tui-text.js";
|
|
16
8
|
import { NOTICE_PARTIAL_FILE_INDEX } from "../notices.js";
|
|
17
|
-
import {
|
|
9
|
+
import { fillToolBackground, renderFindResults, renderToolDuration, renderToolError } from "../render.js";
|
|
10
|
+
import { resolveTextCtor } from "../tui-text.js";
|
|
11
|
+
import type { FffServiceWithCursor, FindDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
12
|
+
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
18
13
|
|
|
19
14
|
type Result = AgentToolResult<Record<string, unknown>>;
|
|
20
15
|
|
|
@@ -105,7 +100,9 @@ export function registerFindTool(
|
|
|
105
100
|
const basePathResult = fff.getBasePath();
|
|
106
101
|
const basePath = basePathResult.ok ? basePathResult.value : null;
|
|
107
102
|
const globPattern = buildGlobPattern(pattern, path, basePath);
|
|
108
|
-
const searchResult = fff.glob(globPattern, {
|
|
103
|
+
const searchResult = fff.glob(globPattern, {
|
|
104
|
+
pageSize: effectiveLimit,
|
|
105
|
+
});
|
|
109
106
|
|
|
110
107
|
if (searchResult.ok) {
|
|
111
108
|
const items = searchResult.value.items.slice(0, effectiveLimit);
|
|
@@ -153,22 +150,15 @@ export function registerFindTool(
|
|
|
153
150
|
const a = args as { pattern?: unknown; path?: unknown; limit?: unknown };
|
|
154
151
|
const text = (ctx as RenderCtxLike).lastComponent ?? new TC("", 0, 0);
|
|
155
152
|
const pattern = a.pattern == null ? "" : String(a.pattern);
|
|
156
|
-
const pathArg =
|
|
157
|
-
a.path == null ? "<missing>" : shortPath(cwd, home, String(a.path));
|
|
153
|
+
const pathArg = a.path == null ? "<missing>" : shortPath(cwd, home, String(a.path));
|
|
158
154
|
const limit = a.limit;
|
|
159
|
-
const findLabel = theme.fg("toolTitle", theme.bold("find"));
|
|
160
|
-
const patternPart = pattern ? theme.fg("
|
|
155
|
+
const findLabel = theme.fg(ctx.isError ? "error" : "toolTitle", theme.bold("✱ find"));
|
|
156
|
+
const patternPart = pattern ? theme.fg("toolTitle", pattern) : "";
|
|
161
157
|
const inPart = theme.fg("dim", " in ");
|
|
162
158
|
const pathPart = theme.fg("toolOutput", pathArg);
|
|
163
|
-
const limitPart =
|
|
164
|
-
limit !== undefined && limit !== null ? theme.fg("dim", ` limit ${limit}`) : "";
|
|
159
|
+
const limitPart = limit !== undefined && limit !== null ? theme.fg("dim", ` limit ${limit}`) : "";
|
|
165
160
|
const out = `${findLabel} ${patternPart}${inPart}${pathPart}${limitPart}`;
|
|
166
|
-
text.setText(
|
|
167
|
-
fillToolBackground(
|
|
168
|
-
`\n${TOOL_RESULT_INDENT}${out}`,
|
|
169
|
-
ctx.isError ? BG_ERROR : undefined,
|
|
170
|
-
),
|
|
171
|
-
);
|
|
161
|
+
text.setText(fillToolBackground(`\n${TOOL_RESULT_INDENT}${out}`, ctx.isError ? BG_ERROR : undefined));
|
|
172
162
|
return text;
|
|
173
163
|
},
|
|
174
164
|
|
|
@@ -186,17 +176,14 @@ export function registerFindTool(
|
|
|
186
176
|
const noticeStr = d.notices?.length
|
|
187
177
|
? `\n${TOOL_RESULT_INDENT}${theme.fg("warning", `[${d.notices.join(". ")}]`)}`
|
|
188
178
|
: "";
|
|
189
|
-
text.setText(
|
|
190
|
-
fillToolBackground(
|
|
191
|
-
`\n${TOOL_RESULT_INDENT}${theme.fg("dim", "0 files")}${noticeStr}\n`,
|
|
192
|
-
),
|
|
193
|
-
);
|
|
179
|
+
text.setText(fillToolBackground(`\n${TOOL_RESULT_INDENT}${theme.fg("dim", "0 files")}${noticeStr}\n`));
|
|
194
180
|
return text;
|
|
195
181
|
}
|
|
196
182
|
if (!ctx.expanded) {
|
|
183
|
+
const duration = renderToolDuration(r);
|
|
197
184
|
text.setText(
|
|
198
185
|
fillToolBackground(
|
|
199
|
-
`${TOOL_RESULT_INDENT}${FG_DIM}${d.matchCount} files — ctrl+o to expand${RST}${
|
|
186
|
+
`${TOOL_RESULT_INDENT}${FG_DIM}${d.matchCount} files — ctrl+o to expand${RST}${duration ? `${FG_DIM}· ${duration}${RST}` : ""}\n`,
|
|
200
187
|
),
|
|
201
188
|
);
|
|
202
189
|
return text;
|
|
@@ -208,20 +195,19 @@ export function registerFindTool(
|
|
|
208
195
|
const noticeStr = d.notices?.length
|
|
209
196
|
? `\n${TOOL_RESULT_INDENT}${theme.fg("warning", `[${d.notices.join(". ")}]`)}`
|
|
210
197
|
: "";
|
|
198
|
+
const duration = renderToolDuration(r);
|
|
211
199
|
text.setText(
|
|
212
200
|
fillToolBackground(
|
|
213
|
-
`\n${TOOL_RESULT_INDENT}${theme.fg("dim", `${d.matchCount} files`)}${
|
|
201
|
+
`\n${TOOL_RESULT_INDENT}${theme.fg("dim", `${d.matchCount} files`)}${duration ? `${FG_DIM}· ${duration}${RST}` : ""}\n${rendered}${noticeStr}\n`,
|
|
214
202
|
),
|
|
215
203
|
);
|
|
216
204
|
return text;
|
|
217
205
|
}
|
|
218
206
|
const fc = r.content?.[0] as TextContent | undefined;
|
|
219
207
|
text.setText(
|
|
220
|
-
fillToolBackground(
|
|
221
|
-
`\n${TOOL_RESULT_INDENT}${theme.fg("dim", fc?.text?.slice(0, 120) ?? "0 files")}\n`,
|
|
222
|
-
),
|
|
208
|
+
fillToolBackground(`\n${TOOL_RESULT_INDENT}${theme.fg("dim", fc?.text?.slice(0, 120) ?? "0 files")}\n`),
|
|
223
209
|
);
|
|
224
210
|
return text;
|
|
225
211
|
},
|
|
226
212
|
} as unknown as ToolDefinition);
|
|
227
|
-
}
|
|
213
|
+
}
|
package/src/tools/grep.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/* pi-pretty: grep tool -- FFF-backed text search with SDK fallback. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type ExtensionAPI,
|
|
6
|
-
type ExtensionContext,
|
|
7
|
-
type AgentToolResult,
|
|
8
|
-
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import type { SdkToolDef, GrepDetails, FffServiceWithCursor, TextContent, ThemeLike, RenderCtxLike } from "../types.js";
|
|
10
|
-
import { TOOL_RESULT_INDENT, MAX_PREVIEW_LINES, BG_ERROR, FG_DIM, RST, resolveBaseBackground } from "../config.js";
|
|
11
|
-
import { shortPath, normalizeLineEndings } from "../helpers.js";
|
|
12
|
-
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
13
|
-
import { renderToolError, fillToolBackground } from "../render.js";
|
|
14
|
-
import { resolveTextCtor } from "../tui-text.js";
|
|
3
|
+
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { BG_ERROR, FG_DIM, MAX_PREVIEW_LINES, RST, resolveBaseBackground, TOOL_RESULT_INDENT } from "../config.js";
|
|
15
5
|
import { fffFormatGrepText } from "../fff-helpers.js";
|
|
6
|
+
import { normalizeLineEndings, shortPath } from "../helpers.js";
|
|
16
7
|
import { NOTICE_PARTIAL_FILE_INDEX } from "../notices.js";
|
|
8
|
+
import { fillToolBackground, renderToolError } from "../render.js";
|
|
9
|
+
import { resolveTextCtor } from "../tui-text.js";
|
|
10
|
+
import type { FffServiceWithCursor, GrepDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
11
|
+
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
17
12
|
|
|
18
13
|
const invalidArg = "<missing>";
|
|
19
14
|
|
|
@@ -111,7 +106,7 @@ export function registerGrepTool(
|
|
|
111
106
|
const limit = args.limit;
|
|
112
107
|
const literal = args.literal === true;
|
|
113
108
|
const caseInsensitive = args.caseInsensitive === true || args.ignoreCase === true;
|
|
114
|
-
let out = `${theme.fg("toolTitle", theme.bold("grep"))} ${theme.fg("
|
|
109
|
+
let out = `${theme.fg(ctx.isError ? "error" : "toolTitle", theme.bold("✱ grep"))} ${theme.fg("toolTitle", `/${pattern || ""}/`)}${theme.fg("toolOutput", ` in ${path}`)}`;
|
|
115
110
|
if (glob) out += theme.fg("dim", ` (${String(glob)})`);
|
|
116
111
|
if (limit !== undefined && limit !== null) out += theme.fg("dim", ` limit ${limit}`);
|
|
117
112
|
if (literal) out += theme.fg("dim", ` (literal)`);
|
|
@@ -158,13 +153,15 @@ export function registerGrepTool(
|
|
|
158
153
|
if (remaining > 0) {
|
|
159
154
|
out.push(theme.fg("muted", `… (${remaining} more ${remaining === 1 ? "line" : "lines"}, to expand)`));
|
|
160
155
|
}
|
|
161
|
-
const body = out.map((l) => `${TOOL_RESULT_INDENT}${l}`).join("\n")
|
|
156
|
+
const body = `${out.map((l) => `${TOOL_RESULT_INDENT}${l}`).join("\n")}\n\n`;
|
|
162
157
|
text.setText(fillToolBackground(body, ctx.isError ? BG_ERROR : undefined));
|
|
163
158
|
return text;
|
|
164
159
|
}
|
|
165
160
|
const fc = result.content?.[0];
|
|
166
161
|
const fallback = fc && "text" in fc ? String(fc.text).slice(0, 120) : "no matches";
|
|
167
|
-
text.setText(
|
|
162
|
+
text.setText(
|
|
163
|
+
fillToolBackground(`${TOOL_RESULT_INDENT}${theme.fg("dim", fallback)}`, ctx.isError ? BG_ERROR : undefined),
|
|
164
|
+
);
|
|
168
165
|
return text;
|
|
169
166
|
},
|
|
170
167
|
} as unknown as ToolDefinition<any, any, any>);
|
package/src/tools/ls.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/* pi-pretty: ls tool -- directory listing with styled output. */
|
|
2
2
|
|
|
3
|
+
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
BG_BASE,
|
|
6
|
+
BG_ERROR,
|
|
7
|
+
FG_DIM,
|
|
8
|
+
MAX_PREVIEW_LINES,
|
|
9
|
+
RST,
|
|
10
|
+
resolveBaseBackground,
|
|
11
|
+
TOOL_RESULT_INDENT,
|
|
12
|
+
termWidth,
|
|
13
|
+
} from "../config.js";
|
|
11
14
|
import { shortPath } from "../helpers.js";
|
|
12
|
-
import {
|
|
13
|
-
import { renderTree, renderToolError, renderToolMetrics, fillToolBackground } from "../render.js";
|
|
15
|
+
import { fillToolBackground, renderToolError, renderToolMetrics, renderTree } from "../render.js";
|
|
14
16
|
import { resolveTextCtor } from "../tui-text.js";
|
|
17
|
+
import type { ComponentLike, LsDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
18
|
+
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
15
19
|
|
|
16
20
|
type Result = AgentToolResult<Record<string, unknown>>;
|
|
17
21
|
|
|
@@ -91,7 +95,9 @@ export function registerLsTool(
|
|
|
91
95
|
}
|
|
92
96
|
const fc = result.content?.[0];
|
|
93
97
|
text.setText(
|
|
94
|
-
fillToolBackground(
|
|
98
|
+
fillToolBackground(
|
|
99
|
+
`${TOOL_RESULT_INDENT}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 120) : "done")}`,
|
|
100
|
+
),
|
|
95
101
|
);
|
|
96
102
|
return text;
|
|
97
103
|
},
|
package/src/tools/metrics.ts
CHANGED
package/src/tools/read.ts
CHANGED
|
@@ -1,40 +1,24 @@
|
|
|
1
1
|
/* pi-pretty: read tool -- file reading with syntax highlighting and inline image support. */
|
|
2
2
|
|
|
3
|
+
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { Image as TuiImage } from "@earendil-works/pi-tui";
|
|
3
5
|
import {
|
|
4
|
-
type ToolDefinition,
|
|
5
|
-
type ExtensionAPI,
|
|
6
|
-
type ExtensionContext,
|
|
7
|
-
type AgentToolResult,
|
|
8
|
-
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import type { SdkToolDef, ReadDetails, TextContent, ComponentLike, ThemeLike, RenderCtxLike } from "../types.js";
|
|
10
|
-
import {
|
|
11
|
-
TOOL_RESULT_INDENT,
|
|
12
|
-
resolveBaseBackground,
|
|
13
|
-
termWidth,
|
|
14
|
-
MAX_PREVIEW_LINES,
|
|
15
6
|
BG_BASE,
|
|
16
7
|
BG_ERROR,
|
|
17
8
|
FG_DIM,
|
|
18
9
|
FG_LNUM,
|
|
19
10
|
FG_RULE,
|
|
11
|
+
MAX_PREVIEW_LINES,
|
|
20
12
|
RST,
|
|
13
|
+
resolveBaseBackground,
|
|
14
|
+
TOOL_RESULT_INDENT,
|
|
15
|
+
termWidth,
|
|
21
16
|
} from "../config.js";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { renderToolError, renderToolMetrics, fillToolBackground, renderFileContent } from "../render.js";
|
|
17
|
+
import { normalizeLineEndings, shortPath } from "../helpers.js";
|
|
18
|
+
import { fillToolBackground, renderFileContent, renderToolError, renderToolMetrics } from "../render.js";
|
|
25
19
|
import { resolveTextCtor } from "../tui-text.js";
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
function isImageTerminal(): boolean {
|
|
29
|
-
const term = (process.env.TERM_PROGRAM ?? process.env.TERM ?? "").toLowerCase();
|
|
30
|
-
const proto = (process.env.PRETTY_IMAGE_PROTOCOL ?? "").toLowerCase();
|
|
31
|
-
if (proto === "kitty" || proto === "iterm2") return true;
|
|
32
|
-
if (proto === "none") return false;
|
|
33
|
-
return (
|
|
34
|
-
["ghostty", "kitty", "iterm.app", "wezterm", "mintty"].some((t) => term.includes(t)) ||
|
|
35
|
-
process.env.LC_TERMINAL === "iTerm2"
|
|
36
|
-
);
|
|
37
|
-
}
|
|
20
|
+
import type { ReadDetails, RenderCtxLike, SdkToolDef, TextContent, ThemeLike } from "../types.js";
|
|
21
|
+
import { wrapExecuteWithMetrics } from "./metrics.js";
|
|
38
22
|
|
|
39
23
|
type Result = AgentToolResult<Record<string, unknown>>;
|
|
40
24
|
|
|
@@ -85,7 +69,14 @@ export function registerReadTool(
|
|
|
85
69
|
resolveBaseBackground(theme);
|
|
86
70
|
|
|
87
71
|
const text = ctx.lastComponent ?? new TC("", 0, 0);
|
|
88
|
-
|
|
72
|
+
if (!ctx.isError) {
|
|
73
|
+
text.setText("");
|
|
74
|
+
return text;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const path = String(args.path ?? "");
|
|
78
|
+
const label = theme.fg("error", theme.bold("→ read"));
|
|
79
|
+
text.setText(fillToolBackground(`\n${TOOL_RESULT_INDENT}${label} ${theme.fg("toolTitle", path)}`, BG_ERROR));
|
|
89
80
|
return text;
|
|
90
81
|
},
|
|
91
82
|
|
|
@@ -95,40 +86,25 @@ export function registerReadTool(
|
|
|
95
86
|
const text = ctx.lastComponent ?? new TC("", 0, 0);
|
|
96
87
|
|
|
97
88
|
if (ctx.isError) {
|
|
98
|
-
text.setText(renderToolError(getText(result) || "Error", theme));
|
|
89
|
+
text.setText(fillToolBackground(renderToolError(getText(result) || "Error", theme), BG_ERROR));
|
|
99
90
|
return text;
|
|
100
91
|
}
|
|
101
92
|
|
|
102
93
|
const d = result.details as ReadDetails | undefined;
|
|
103
94
|
|
|
104
|
-
// Image rendering
|
|
95
|
+
// Image rendering — use pi-tui Image component
|
|
105
96
|
if (d?._type === "readImage") {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
} else {
|
|
117
|
-
const pngData = (require("@earendil-works/pi-coding-agent") as any).convertToPng?.(d.data) ?? d.data;
|
|
118
|
-
img.setText(`\x1b_Ga=T,f=100,m=${d.mimeType === "image/png" ? "1" : "0"};${pngData}\x1b\\\\`);
|
|
119
|
-
}
|
|
120
|
-
return img;
|
|
121
|
-
} catch {
|
|
122
|
-
/* fall through */
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const fc = result.content?.[0];
|
|
126
|
-
text.setText(
|
|
127
|
-
fillToolBackground(
|
|
128
|
-
`${TOOL_RESULT_INDENT}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 80) : `[image: ${d.filePath}]`)}`,
|
|
129
|
-
),
|
|
97
|
+
const mimeType = d.mimeType.startsWith("image/svg") ? "image/svg+xml" : d.mimeType;
|
|
98
|
+
return new TuiImage(
|
|
99
|
+
d.data,
|
|
100
|
+
mimeType,
|
|
101
|
+
{
|
|
102
|
+
fallbackColor: (text) => theme.fg("toolTitle", text),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
filename: d.filePath,
|
|
106
|
+
},
|
|
130
107
|
);
|
|
131
|
-
return text;
|
|
132
108
|
}
|
|
133
109
|
|
|
134
110
|
// File content — line-numbered display
|
|
@@ -141,7 +117,8 @@ export function registerReadTool(
|
|
|
141
117
|
if (!ctx.expanded) {
|
|
142
118
|
text.setText(
|
|
143
119
|
fillToolBackground(
|
|
144
|
-
`\n${TOOL_RESULT_INDENT}${theme.fg("toolTitle", theme.bold("read"))} ${theme.fg("
|
|
120
|
+
`\n${TOOL_RESULT_INDENT}${theme.fg("toolTitle", theme.bold("→ read"))} ${theme.fg("toolTitle", p2)}${theme.fg("dim", off2)}\n${TOOL_RESULT_INDENT}${FG_DIM}${total} lines — ctrl+o to expand${RST}\n`,
|
|
121
|
+
BG_BASE,
|
|
145
122
|
),
|
|
146
123
|
);
|
|
147
124
|
return text;
|
|
@@ -152,7 +129,7 @@ export function registerReadTool(
|
|
|
152
129
|
const gw = nw + 3;
|
|
153
130
|
const cw = Math.max(1, tw - gw);
|
|
154
131
|
|
|
155
|
-
const header = `${theme.fg("toolTitle", theme.bold("read"))} ${theme.fg("
|
|
132
|
+
const header = `${theme.fg("toolTitle", theme.bold("→ read"))} ${theme.fg("toolTitle", p2)}${theme.fg("dim", off2)}`;
|
|
156
133
|
const out: string[] = ["", `${TOOL_RESULT_INDENT}${header}`];
|
|
157
134
|
out.push(`${TOOL_RESULT_INDENT}${FG_RULE}${"─".repeat(tw - 1)}${RST}`);
|
|
158
135
|
for (let i = 0; i < show.length; i++) {
|
|
@@ -169,7 +146,7 @@ export function registerReadTool(
|
|
|
169
146
|
}
|
|
170
147
|
out.push("");
|
|
171
148
|
const rendered = out.join("\n");
|
|
172
|
-
text.setText(fillToolBackground(rendered));
|
|
149
|
+
text.setText(fillToolBackground(rendered, BG_BASE));
|
|
173
150
|
(ctx as any).state._rt = rendered;
|
|
174
151
|
|
|
175
152
|
// Async syntax highlighting via Shiki
|
|
@@ -180,7 +157,7 @@ export function registerReadTool(
|
|
|
180
157
|
.map((l) => `${TOOL_RESULT_INDENT}${l}`)
|
|
181
158
|
.join("\n");
|
|
182
159
|
const rendered = `\n${TOOL_RESULT_INDENT}${header}\n${padded}\n`;
|
|
183
|
-
text.setText(fillToolBackground(rendered));
|
|
160
|
+
text.setText(fillToolBackground(rendered, BG_BASE));
|
|
184
161
|
(ctx as any).state._rt = rendered;
|
|
185
162
|
})
|
|
186
163
|
.catch(() => {});
|
|
@@ -190,7 +167,10 @@ export function registerReadTool(
|
|
|
190
167
|
|
|
191
168
|
const fc = result.content?.[0];
|
|
192
169
|
text.setText(
|
|
193
|
-
fillToolBackground(
|
|
170
|
+
fillToolBackground(
|
|
171
|
+
`${TOOL_RESULT_INDENT}${theme.fg("dim", fc && "text" in fc ? String(fc.text).slice(0, 120) : "done")}`,
|
|
172
|
+
BG_BASE,
|
|
173
|
+
),
|
|
194
174
|
);
|
|
195
175
|
return text;
|
|
196
176
|
},
|
package/src/tui-text.ts
CHANGED
|
@@ -13,10 +13,6 @@ type TextCtor = new (t?: string, x?: number, y?: number) => { setText(v: string)
|
|
|
13
13
|
|
|
14
14
|
/** No-op stub that satisfies the Text interface so rendering doesn't crash. */
|
|
15
15
|
class StubText {
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
17
|
-
constructor(_t?: string, _x?: number, _y?: number) {
|
|
18
|
-
// no-op
|
|
19
|
-
}
|
|
20
16
|
setText(_v: string): void {
|
|
21
17
|
// no-op — pi-tui not available
|
|
22
18
|
}
|
|
@@ -26,7 +22,7 @@ let _ctor: TextCtor | null = null;
|
|
|
26
22
|
let _resolved = false;
|
|
27
23
|
|
|
28
24
|
function resolve(): TextCtor {
|
|
29
|
-
if (
|
|
25
|
+
if (_ctor) return _ctor;
|
|
30
26
|
_resolved = true;
|
|
31
27
|
try {
|
|
32
28
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
package/src/types.ts
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
* pi-pretty shared types.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
|
|
6
6
|
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
// Re-export FFF types needed by tools
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
|
|
12
|
-
export type { FileFinder, FileItem,
|
|
12
|
+
export type { FileFinder, FileItem, GrepCursor, GrepMatch, GrepResult, SearchResult } from "@ff-labs/fff-node";
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
// Content / Result types
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
|
|
18
18
|
export type ToolContent = TextContent | ImageContent;
|
|
19
|
-
export type {
|
|
19
|
+
export type { ImageContent, TextContent };
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
21
|
// Theme / rendering context types
|
|
22
22
|
// ---------------------------------------------------------------------------
|
|
@@ -120,14 +120,6 @@ export interface GrepInput {
|
|
|
120
120
|
ignoreCase?: boolean;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
export interface MultiGrepInput {
|
|
124
|
-
patterns: string[];
|
|
125
|
-
path?: string;
|
|
126
|
-
constraints?: string;
|
|
127
|
-
context?: number;
|
|
128
|
-
limit?: number;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
123
|
// ---------------------------------------------------------------------------
|
|
132
124
|
// SDK tool definition shape (DI-friendly — accepts both mock and real SDK)
|
|
133
125
|
// ---------------------------------------------------------------------------
|
|
@@ -153,29 +145,6 @@ export interface SdkTools {
|
|
|
153
145
|
getAgentDir?: () => string;
|
|
154
146
|
}
|
|
155
147
|
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
// Multi-grep fallback types
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
|
|
160
|
-
export interface MultiGrepFallbackParams {
|
|
161
|
-
cwd: string;
|
|
162
|
-
patterns: string[];
|
|
163
|
-
path?: string;
|
|
164
|
-
constraints?: string;
|
|
165
|
-
context?: number;
|
|
166
|
-
limit: number;
|
|
167
|
-
ignoreCase: boolean;
|
|
168
|
-
signal?: AbortSignal;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export interface MultiGrepFallbackResult {
|
|
172
|
-
text: string;
|
|
173
|
-
matchCount: number;
|
|
174
|
-
limitReached: boolean;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export type MultiGrepFallback = (params: MultiGrepFallbackParams) => Promise<MultiGrepFallbackResult>;
|
|
178
|
-
|
|
179
148
|
// ---------------------------------------------------------------------------
|
|
180
149
|
// FFF service interfaces
|
|
181
150
|
// ---------------------------------------------------------------------------
|
|
@@ -224,5 +193,4 @@ export interface PiPrettyDeps {
|
|
|
224
193
|
sdk?: SdkTools;
|
|
225
194
|
TextComponent?: new (text?: string, x?: number, y?: number) => ComponentLike;
|
|
226
195
|
fffModule?: typeof import("@ff-labs/fff-node");
|
|
227
|
-
multiGrepRipgrepFallback?: MultiGrepFallback;
|
|
228
196
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export type ConstraintParseResult = {
|
|
2
|
-
ok: true;
|
|
3
|
-
globs: string[];
|
|
4
|
-
tokens: string[];
|
|
5
|
-
} | {
|
|
6
|
-
ok: false;
|
|
7
|
-
error: string;
|
|
8
|
-
};
|
|
9
|
-
export type MultiGrepRipgrepFallbackParams = {
|
|
10
|
-
cwd: string;
|
|
11
|
-
patterns: string[];
|
|
12
|
-
path?: string;
|
|
13
|
-
constraints?: string;
|
|
14
|
-
context?: number;
|
|
15
|
-
limit: number;
|
|
16
|
-
ignoreCase: boolean;
|
|
17
|
-
signal?: AbortSignal;
|
|
18
|
-
};
|
|
19
|
-
export type MultiGrepRipgrepFallbackResult = {
|
|
20
|
-
text: string;
|
|
21
|
-
matchCount: number;
|
|
22
|
-
limitReached: boolean;
|
|
23
|
-
};
|
|
24
|
-
export type MultiGrepRipgrepFallback = (params: MultiGrepRipgrepFallbackParams) => Promise<MultiGrepRipgrepFallbackResult>;
|
|
25
|
-
export declare function parseMultiGrepConstraints(constraints: string | undefined): ConstraintParseResult;
|
|
26
|
-
export declare function getMultiGrepRipgrepArgs(params: MultiGrepRipgrepFallbackParams): ConstraintParseResult & {
|
|
27
|
-
args?: string[];
|
|
28
|
-
};
|
|
29
|
-
export declare function runMultiGrepRipgrepFallback(params: MultiGrepRipgrepFallbackParams): Promise<MultiGrepRipgrepFallbackResult>;
|
|
30
|
-
//# sourceMappingURL=multi-grep-fallback.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"multi-grep-fallback.d.ts","sourceRoot":"","sources":["../src/multi-grep-fallback.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnH,MAAM,MAAM,8BAA8B,GAAG;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,CACtC,MAAM,EAAE,8BAA8B,KAClC,OAAO,CAAC,8BAA8B,CAAC,CAAC;AA8E7C,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,qBAAqB,CAehG;AAqBD,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,8BAA8B,GACpC,qBAAqB,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAI7C;AAED,wBAAsB,2BAA2B,CAChD,MAAM,EAAE,8BAA8B,GACpC,OAAO,CAAC,8BAA8B,CAAC,CAoGzC"}
|