pi-crew 0.6.4 → 0.7.2
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 +62 -0
- package/README.md +68 -0
- package/package.json +2 -1
- package/src/errors.ts +20 -2
- package/src/extension/knowledge-injection.ts +71 -0
- package/src/extension/pi-api.ts +47 -0
- package/src/extension/register.ts +19 -6
- package/src/extension/registration/commands.ts +40 -1
- package/src/extension/registration/compaction-guard.ts +154 -14
- package/src/extension/team-tool/handle-settings.ts +57 -0
- package/src/extension/team-tool/inspect.ts +4 -1
- package/src/extension/team-tool/plan.ts +8 -1
- package/src/runtime/intercom-bridge.ts +5 -1
- package/src/runtime/replace.ts +555 -0
- package/src/runtime/resilient-edit.ts +166 -0
- package/src/runtime/single-agent-compose.ts +87 -0
- package/src/runtime/task-runner/prompt-builder.ts +6 -0
- package/src/runtime/team-runner.ts +24 -7
- package/src/schema/team-tool-schema.ts +6 -0
- package/src/state/usage.ts +73 -0
- package/src/ui/card-colors.ts +126 -0
- package/src/ui/deploy-bundled-themes.ts +71 -0
- package/src/ui/render-diff.ts +37 -3
- package/src/ui/settings-overlay.ts +70 -7
- package/src/ui/syntax-highlight.ts +42 -23
- package/src/ui/theme-discovery.ts +188 -0
- package/src/ui/tool-renderers/index.ts +27 -14
- package/themes/crew-catppuccin-latte.json +96 -0
- package/themes/crew-catppuccin-mocha.json +93 -0
- package/themes/crew-dark.json +90 -0
- package/themes/crew-dracula.json +83 -0
- package/themes/crew-gruvbox-dark.json +83 -0
- package/themes/crew-gruvbox-light.json +90 -0
- package/themes/crew-nord.json +85 -0
- package/themes/crew-one-dark.json +89 -0
- package/themes/crew-solarized-dark.json +90 -0
- package/themes/crew-solarized-light.json +92 -0
- package/themes/crew-tokyo-night.json +85 -0
- package/src/extension/registration/brief-tool-overrides.ts +0 -400
- package/src/runtime/budget-tracker.ts +0 -354
- package/src/state/memory-store.ts +0 -244
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Brief tool overrides — re-registers built-in Pi tools with custom rendering.
|
|
3
|
-
*
|
|
4
|
-
* Inspired by oh-my-pi's pi-brief extension. Wraps each built-in tool
|
|
5
|
-
* (read, bash, edit, write, find, grep, ls) keeping the original execute
|
|
6
|
-
* but replacing renderCall/renderResult with themed, brief-aware versions.
|
|
7
|
-
*
|
|
8
|
-
* Brief mode shows CONTEXTUAL one-liners that preserve WHAT was done:
|
|
9
|
-
* read ~/file.ts:1-50 → 142 lines · 2.3s
|
|
10
|
-
* bash $ npm test → done · 12.5s
|
|
11
|
-
* edit ~/file.ts → +3 -1 · 0.8s
|
|
12
|
-
* write ~/file.ts (42 lines) → ✓ · 0.2s
|
|
13
|
-
* find *.ts in ~/src → 5 files · 0.5s
|
|
14
|
-
* grep /pattern/ in ~/src → 3 matches · 0.3s
|
|
15
|
-
* ls ~/src → 8 entries · 0.1s
|
|
16
|
-
*
|
|
17
|
-
* Timing: ctx.state.startedAt is set in renderCall, read in renderResult.
|
|
18
|
-
* NOTE: Pi's executionStarted is a boolean flag (NOT a timestamp).
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { homedir } from "node:os";
|
|
22
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
23
|
-
import {
|
|
24
|
-
createBashTool,
|
|
25
|
-
createEditTool,
|
|
26
|
-
createFindTool,
|
|
27
|
-
createGrepTool,
|
|
28
|
-
createLsTool,
|
|
29
|
-
createReadTool,
|
|
30
|
-
createWriteTool,
|
|
31
|
-
} from "@earendil-works/pi-coding-agent";
|
|
32
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
33
|
-
import { isBrief } from "../../ui/tool-renderers/brief-mode.ts";
|
|
34
|
-
|
|
35
|
-
// ── Path shortening ────────────────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
const HOME = homedir();
|
|
38
|
-
function shortenPath(p: string): string {
|
|
39
|
-
if (!p) return "";
|
|
40
|
-
if (p.startsWith(HOME)) return `~${p.slice(HOME.length)}`;
|
|
41
|
-
return p;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ── Types ──────────────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
interface TextResult {
|
|
47
|
-
content: Array<{ type: string; text?: string }>;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface Theme {
|
|
51
|
-
fg: (slot: string, text: string) => string;
|
|
52
|
-
bold: (text: string) => string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface RenderCtx {
|
|
56
|
-
args?: Record<string, unknown>;
|
|
57
|
-
state?: Record<string, unknown>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function fullText(result: TextResult): string | undefined {
|
|
61
|
-
const c = result.content.find((x): x is { type: "text"; text: string } => x.type === "text");
|
|
62
|
-
return c?.text;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function fullRender(result: TextResult, theme: Theme): Text {
|
|
66
|
-
const text = fullText(result);
|
|
67
|
-
if (!text) return new Text("", 0, 0);
|
|
68
|
-
const lines = text
|
|
69
|
-
.trim()
|
|
70
|
-
.split("\n")
|
|
71
|
-
.map((line) => theme.fg("toolOutput", line))
|
|
72
|
-
.join("\n");
|
|
73
|
-
return new Text(`\n${lines}`, 0, 0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Record start time in ctx.state during renderCall (called once per tool invocation). */
|
|
77
|
-
function noteStarted(ctx: RenderCtx | undefined): void {
|
|
78
|
-
if (ctx?.state && !ctx.state.briefStartedAt) {
|
|
79
|
-
ctx.state.briefStartedAt = Date.now();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Compute elapsed from ctx.state.briefStartedAt. Returns " · 1.2s" or "". */
|
|
84
|
-
function elapsed(ctx: RenderCtx | undefined): string {
|
|
85
|
-
const startedAt = ctx?.state?.briefStartedAt as number | undefined;
|
|
86
|
-
if (!startedAt || typeof startedAt !== "number") return "";
|
|
87
|
-
const ms = Date.now() - startedAt;
|
|
88
|
-
if (ms < 1000) return "";
|
|
89
|
-
if (ms < 60_000) return ` · ${(ms / 1000).toFixed(1)}s`;
|
|
90
|
-
const m = Math.floor(ms / 60_000), s = Math.floor((ms % 60_000) / 1000);
|
|
91
|
-
return ` · ${m}m${s}s`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Truncate to maxLen with ellipsis (visual). */
|
|
95
|
-
function trunc(text: string, maxLen: number): string {
|
|
96
|
-
if (text.length <= maxLen) return text;
|
|
97
|
-
return text.slice(0, maxLen - 1) + "…";
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Flatten multiline command to single line, truncated. */
|
|
101
|
-
function flatCmd(cmd: string, maxLen: number): string {
|
|
102
|
-
return trunc(cmd.replace(/\s+/g, " ").trim(), maxLen);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ── Tool registration ──────────────────────────────────────────────────
|
|
106
|
-
|
|
107
|
-
export function registerBriefToolOverrides(pi: ExtensionAPI, cwd: string): void {
|
|
108
|
-
const tools = {
|
|
109
|
-
read: createReadTool(cwd),
|
|
110
|
-
bash: createBashTool(cwd),
|
|
111
|
-
edit: createEditTool(cwd),
|
|
112
|
-
write: createWriteTool(cwd),
|
|
113
|
-
find: createFindTool(cwd),
|
|
114
|
-
grep: createGrepTool(cwd),
|
|
115
|
-
ls: createLsTool(cwd),
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// ─── Read ───
|
|
119
|
-
pi.registerTool({
|
|
120
|
-
name: "read",
|
|
121
|
-
label: "read",
|
|
122
|
-
description: tools.read.description,
|
|
123
|
-
parameters: tools.read.parameters,
|
|
124
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
126
|
-
return tools.read.execute(toolCallId, params, signal, onUpdate);
|
|
127
|
-
},
|
|
128
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
130
|
-
noteStarted(ctx);
|
|
131
|
-
const p = shortenPath(args.path || "");
|
|
132
|
-
const pathDisplay = p ? theme.fg("accent", p) : theme.fg("toolOutput", "...");
|
|
133
|
-
let text = `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`;
|
|
134
|
-
if (args.offset !== undefined || args.limit !== undefined) {
|
|
135
|
-
const startLine = args.offset ?? 1;
|
|
136
|
-
const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
|
|
137
|
-
text += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
|
138
|
-
}
|
|
139
|
-
return new Text(text, 0, 0);
|
|
140
|
-
},
|
|
141
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
142
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
143
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
144
|
-
const args = ctx?.args ?? {};
|
|
145
|
-
const p = shortenPath(args.path || "?");
|
|
146
|
-
let range = "";
|
|
147
|
-
if (args.offset || args.limit) {
|
|
148
|
-
const s = args.offset ?? 1;
|
|
149
|
-
const e = args.limit ? s + args.limit - 1 : "";
|
|
150
|
-
range = `:${s}${e ? `-${e}` : ""}`;
|
|
151
|
-
}
|
|
152
|
-
const text = fullText(result);
|
|
153
|
-
const count = text ? text.trim().split("\n").filter(Boolean).length : 0;
|
|
154
|
-
const time = elapsed(ctx);
|
|
155
|
-
const label = count === 0 ? "empty" : `${count} lines`;
|
|
156
|
-
return new Text(
|
|
157
|
-
`${theme.fg("toolTitle", "read")} ${theme.fg("accent", p)}${theme.fg("warning", range)} ${theme.fg("dim", "→")} ${theme.fg("muted", label)}${theme.fg("dim", time)}`,
|
|
158
|
-
0, 0,
|
|
159
|
-
);
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// ─── Bash ───
|
|
164
|
-
pi.registerTool({
|
|
165
|
-
name: "bash",
|
|
166
|
-
label: "bash",
|
|
167
|
-
description: tools.bash.description,
|
|
168
|
-
parameters: tools.bash.parameters,
|
|
169
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
170
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
171
|
-
return tools.bash.execute(toolCallId, params, signal, onUpdate);
|
|
172
|
-
},
|
|
173
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
175
|
-
noteStarted(ctx);
|
|
176
|
-
const command = args.command || "...";
|
|
177
|
-
const timeout = args.timeout as number | undefined;
|
|
178
|
-
const timeoutSuffix = timeout ? theme.fg("muted", ` (${timeout}s)`) : "";
|
|
179
|
-
return new Text(theme.fg("toolTitle", theme.bold(`$ ${command}`)) + timeoutSuffix, 0, 0);
|
|
180
|
-
},
|
|
181
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
182
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
183
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
184
|
-
const args = ctx?.args ?? {};
|
|
185
|
-
const cmd = flatCmd(String(args.command || "?"), 50);
|
|
186
|
-
const text = fullText(result);
|
|
187
|
-
const time = elapsed(ctx);
|
|
188
|
-
let label: string;
|
|
189
|
-
let color: string;
|
|
190
|
-
if (!text || !text.trim()) {
|
|
191
|
-
label = "done";
|
|
192
|
-
color = "muted";
|
|
193
|
-
} else {
|
|
194
|
-
const lines = text.trim().split("\n");
|
|
195
|
-
if (lines.length === 1) {
|
|
196
|
-
label = trunc(lines[0]!, 40);
|
|
197
|
-
color = "muted";
|
|
198
|
-
} else {
|
|
199
|
-
label = `${lines.length} lines`;
|
|
200
|
-
color = "muted";
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return new Text(
|
|
204
|
-
`${theme.fg("toolTitle", "$")} ${theme.fg("accent", cmd)} ${theme.fg("dim", "→")} ${theme.fg(color, label)}${theme.fg("dim", time)}`,
|
|
205
|
-
0, 0,
|
|
206
|
-
);
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// ─── Edit ───
|
|
211
|
-
pi.registerTool({
|
|
212
|
-
name: "edit",
|
|
213
|
-
label: "edit",
|
|
214
|
-
description: tools.edit.description,
|
|
215
|
-
parameters: tools.edit.parameters,
|
|
216
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
217
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
218
|
-
return tools.edit.execute(toolCallId, params, signal, onUpdate);
|
|
219
|
-
},
|
|
220
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
221
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
222
|
-
noteStarted(ctx);
|
|
223
|
-
const p = shortenPath(args.path || "");
|
|
224
|
-
const pathDisplay = p ? theme.fg("accent", p) : theme.fg("toolOutput", "...");
|
|
225
|
-
return new Text(`${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`, 0, 0);
|
|
226
|
-
},
|
|
227
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
228
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
229
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
230
|
-
const args = ctx?.args ?? {};
|
|
231
|
-
const p = shortenPath(args.path || "?");
|
|
232
|
-
const text = fullText(result);
|
|
233
|
-
const time = elapsed(ctx);
|
|
234
|
-
if (text && (text.includes("Error") || text.includes("error"))) {
|
|
235
|
-
return new Text(
|
|
236
|
-
`${theme.fg("toolTitle", "edit")} ${theme.fg("accent", p)} ${theme.fg("dim", "→")} ${theme.fg("error", "failed")}${theme.fg("dim", time)}`,
|
|
237
|
-
0, 0,
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
const added = text ? (text.match(/^\+ /gm) ?? []).length : 0;
|
|
241
|
-
const removed = text ? (text.match(/^- /gm) ?? []).length : 0;
|
|
242
|
-
if (added === 0 && removed === 0) {
|
|
243
|
-
return new Text(
|
|
244
|
-
`${theme.fg("toolTitle", "edit")} ${theme.fg("accent", p)} ${theme.fg("dim", "→")} ${theme.fg("success", "edited")}${theme.fg("dim", time)}`,
|
|
245
|
-
0, 0,
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
return new Text(
|
|
249
|
-
`${theme.fg("toolTitle", "edit")} ${theme.fg("accent", p)} ${theme.fg("dim", "→")} ${theme.fg("toolDiffAdded", `+${added} `)}${theme.fg("toolDiffRemoved", `-${removed}`)}${theme.fg("dim", time)}`,
|
|
250
|
-
0, 0,
|
|
251
|
-
);
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// ─── Write ───
|
|
256
|
-
pi.registerTool({
|
|
257
|
-
name: "write",
|
|
258
|
-
label: "write",
|
|
259
|
-
description: tools.write.description,
|
|
260
|
-
parameters: tools.write.parameters,
|
|
261
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
262
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
263
|
-
return tools.write.execute(toolCallId, params, signal, onUpdate);
|
|
264
|
-
},
|
|
265
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
266
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
267
|
-
noteStarted(ctx);
|
|
268
|
-
const p = shortenPath(args.path || "");
|
|
269
|
-
const pathDisplay = p ? theme.fg("accent", p) : theme.fg("toolOutput", "...");
|
|
270
|
-
const lineCount = args.content ? String(args.content).split("\n").length : 0;
|
|
271
|
-
const lineInfo = lineCount > 0 ? theme.fg("muted", ` (${lineCount} lines)`) : "";
|
|
272
|
-
return new Text(`${theme.fg("toolTitle", theme.bold("write"))} ${pathDisplay}${lineInfo}`, 0, 0);
|
|
273
|
-
},
|
|
274
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
275
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
276
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
277
|
-
const args = ctx?.args ?? {};
|
|
278
|
-
const p = shortenPath(args.path || "?");
|
|
279
|
-
const text = fullText(result);
|
|
280
|
-
const time = elapsed(ctx);
|
|
281
|
-
if (text) {
|
|
282
|
-
return new Text(
|
|
283
|
-
`${theme.fg("toolTitle", "write")} ${theme.fg("accent", p)} ${theme.fg("dim", "→")} ${theme.fg("error", trunc(text.trim().split("\n")[0] ?? "", 30))}${theme.fg("dim", time)}`,
|
|
284
|
-
0, 0,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
return new Text(
|
|
288
|
-
`${theme.fg("toolTitle", "write")} ${theme.fg("accent", p)} ${theme.fg("dim", "→")} ${theme.fg("success", "✓")}${theme.fg("dim", time)}`,
|
|
289
|
-
0, 0,
|
|
290
|
-
);
|
|
291
|
-
},
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
// ─── Find ───
|
|
295
|
-
pi.registerTool({
|
|
296
|
-
name: "find",
|
|
297
|
-
label: "find",
|
|
298
|
-
description: tools.find.description,
|
|
299
|
-
parameters: tools.find.parameters,
|
|
300
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
301
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
302
|
-
return tools.find.execute(toolCallId, params, signal, onUpdate);
|
|
303
|
-
},
|
|
304
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
305
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
306
|
-
noteStarted(ctx);
|
|
307
|
-
const pattern = args.pattern || "";
|
|
308
|
-
const p = shortenPath(args.path || ".");
|
|
309
|
-
let text = `${theme.fg("toolTitle", theme.bold("find"))} ${theme.fg("accent", pattern)}`;
|
|
310
|
-
text += theme.fg("toolOutput", ` in ${p}`);
|
|
311
|
-
return new Text(text, 0, 0);
|
|
312
|
-
},
|
|
313
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
314
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
315
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
316
|
-
const args = ctx?.args ?? {};
|
|
317
|
-
const pattern = trunc(String(args.pattern || "*"), 20);
|
|
318
|
-
const p = shortenPath(args.path || ".");
|
|
319
|
-
const text = fullText(result);
|
|
320
|
-
const count = text ? text.trim().split("\n").filter(Boolean).length : 0;
|
|
321
|
-
const time = elapsed(ctx);
|
|
322
|
-
const label = count === 0 ? "none" : `${count} files`;
|
|
323
|
-
const color = count === 0 ? "dim" : "muted";
|
|
324
|
-
return new Text(
|
|
325
|
-
`${theme.fg("toolTitle", "find")} ${theme.fg("accent", pattern)} ${theme.fg("dim", `in ${p} →`)} ${theme.fg(color, label)}${theme.fg("dim", time)}`,
|
|
326
|
-
0, 0,
|
|
327
|
-
);
|
|
328
|
-
},
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
// ─── Grep ───
|
|
332
|
-
pi.registerTool({
|
|
333
|
-
name: "grep",
|
|
334
|
-
label: "grep",
|
|
335
|
-
description: tools.grep.description,
|
|
336
|
-
parameters: tools.grep.parameters,
|
|
337
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
338
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
339
|
-
return tools.grep.execute(toolCallId, params, signal, onUpdate);
|
|
340
|
-
},
|
|
341
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
343
|
-
noteStarted(ctx);
|
|
344
|
-
const pattern = args.pattern || "";
|
|
345
|
-
const p = shortenPath(args.path || ".");
|
|
346
|
-
let text = `${theme.fg("toolTitle", theme.bold("grep"))} ${theme.fg("accent", `/${pattern}/`)}`;
|
|
347
|
-
text += theme.fg("toolOutput", ` in ${p}`);
|
|
348
|
-
return new Text(text, 0, 0);
|
|
349
|
-
},
|
|
350
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
351
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
352
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
353
|
-
const args = ctx?.args ?? {};
|
|
354
|
-
const pattern = trunc(String(args.pattern || "?"), 20);
|
|
355
|
-
const p = shortenPath(args.path || ".");
|
|
356
|
-
const text = fullText(result);
|
|
357
|
-
const count = text ? text.trim().split("\n").filter(Boolean).length : 0;
|
|
358
|
-
const time = elapsed(ctx);
|
|
359
|
-
const label = count === 0 ? "none" : `${count} matches`;
|
|
360
|
-
const color = count === 0 ? "dim" : "muted";
|
|
361
|
-
return new Text(
|
|
362
|
-
`${theme.fg("toolTitle", "grep")} ${theme.fg("accent", `/${pattern}/`)} ${theme.fg("dim", `in ${p} →`)} ${theme.fg(color, label)}${theme.fg("dim", time)}`,
|
|
363
|
-
0, 0,
|
|
364
|
-
);
|
|
365
|
-
},
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// ─── Ls ───
|
|
369
|
-
pi.registerTool({
|
|
370
|
-
name: "ls",
|
|
371
|
-
label: "ls",
|
|
372
|
-
description: tools.ls.description,
|
|
373
|
-
parameters: tools.ls.parameters,
|
|
374
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
375
|
-
async execute(toolCallId: string, params: any, signal: any, onUpdate: any, ctx: any) {
|
|
376
|
-
return tools.ls.execute(toolCallId, params, signal, onUpdate);
|
|
377
|
-
},
|
|
378
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
379
|
-
renderCall(args: any, theme: any, ctx: any): any {
|
|
380
|
-
noteStarted(ctx);
|
|
381
|
-
const p = shortenPath(args.path || ".");
|
|
382
|
-
return new Text(`${theme.fg("toolTitle", theme.bold("ls"))} ${theme.fg("accent", p)}`, 0, 0);
|
|
383
|
-
},
|
|
384
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
385
|
-
renderResult(result: any, options: any, theme: any, ctx: any): any {
|
|
386
|
-
if (!isBrief() || options.expanded) return fullRender(result, theme);
|
|
387
|
-
const args = ctx?.args ?? {};
|
|
388
|
-
const p = shortenPath(args.path || ".");
|
|
389
|
-
const text = fullText(result);
|
|
390
|
-
const count = text ? text.trim().split("\n").filter(Boolean).length : 0;
|
|
391
|
-
const time = elapsed(ctx);
|
|
392
|
-
const label = count === 0 ? "empty" : `${count} entries`;
|
|
393
|
-
const color = count === 0 ? "dim" : "muted";
|
|
394
|
-
return new Text(
|
|
395
|
-
`${theme.fg("toolTitle", "ls")} ${theme.fg("accent", p)} ${theme.fg("dim", "→")} ${theme.fg(color, label)}${theme.fg("dim", time)}`,
|
|
396
|
-
0, 0,
|
|
397
|
-
);
|
|
398
|
-
},
|
|
399
|
-
});
|
|
400
|
-
}
|