pi-open-tui 0.4.0 → 0.4.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 +29 -0
- package/README.md +238 -197
- package/README.zh-CN.md +238 -197
- package/assets/preview_dashboard_1.png +0 -0
- package/extensions/open-tui/config.ts +9 -2
- package/extensions/open-tui/copy-buttons.ts +391 -0
- package/extensions/open-tui/editor.ts +311 -119
- package/extensions/open-tui/footer.ts +13 -11
- package/extensions/open-tui/git.ts +10 -32
- package/extensions/open-tui/header.ts +133 -244
- package/extensions/open-tui/index.ts +34 -33
- package/extensions/open-tui/runtime.ts +10 -11
- package/extensions/open-tui/session-lifecycle.ts +23 -43
- package/extensions/open-tui/settings-command.ts +54 -45
- package/extensions/open-tui/state.ts +11 -14
- package/extensions/open-tui/tool-preview.ts +167 -0
- package/extensions/open-tui/tool-view.ts +121 -36
- package/extensions/open-tui/utils.ts +3 -3
- package/package.json +8 -11
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { isAbsolute, relative, sep } from "node:path";
|
|
3
3
|
import type { ExtensionAPI, Theme, ThemeColor, ToolRenderers } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { Box, type Component, Spacer,
|
|
4
|
+
import { Box, type Component, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
5
5
|
import type { ToolViewMode } from "./config.ts";
|
|
6
6
|
import type { IconGlyphs } from "./icons.ts";
|
|
7
|
+
import { codeLines, prepareToolPreview } from "./tool-preview.ts";
|
|
8
|
+
import { sanitizeStatus, stripAnsi } from "./utils.ts";
|
|
7
9
|
|
|
8
10
|
type RenderCall = NonNullable<ToolRenderers["renderCall"]>;
|
|
9
11
|
type RenderResult = NonNullable<ToolRenderers["renderResult"]>;
|
|
@@ -32,6 +34,14 @@ interface RowState {
|
|
|
32
34
|
callCardBottom?: Spacer;
|
|
33
35
|
resultCard?: Box;
|
|
34
36
|
startedAt?: number;
|
|
37
|
+
pulse?: ReturnType<typeof setInterval>;
|
|
38
|
+
pulseBright?: boolean;
|
|
39
|
+
preview?: {
|
|
40
|
+
source: string;
|
|
41
|
+
path: string;
|
|
42
|
+
colors: Theme["colors"];
|
|
43
|
+
render: ReturnType<typeof prepareToolPreview>;
|
|
44
|
+
};
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
const str = (value: unknown): string => (typeof value === "string" ? value : "");
|
|
@@ -45,7 +55,7 @@ function plural(count: number, one: string, many = `${one}s`): string {
|
|
|
45
55
|
* and keep only the text after a lone `\r` so progress bars show their latest frame.
|
|
46
56
|
*/
|
|
47
57
|
function cleanOutput(text: string): string {
|
|
48
|
-
return
|
|
58
|
+
return stripAnsi(text)
|
|
49
59
|
.replace(/\r\n/g, "\n")
|
|
50
60
|
.split("\n")
|
|
51
61
|
.map((line) => {
|
|
@@ -60,9 +70,26 @@ export function textOutput(result: ToolResult): string {
|
|
|
60
70
|
return cleanOutput(result.content.map((block) => (block.type === "text" ? block.text : "")).join("\n"));
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
const NOTICE_SIZE = String.raw`\d+(?:\.\d+)?(?:B|KB|MB)`;
|
|
74
|
+
const BYTE_LIMIT_NOTICE = `${NOTICE_SIZE} limit reached`;
|
|
75
|
+
const GREP_LINES_NOTICE = String.raw`Some lines truncated to \d+ chars\. Use read tool to see full lines`;
|
|
76
|
+
const SHOWN_LINES_NOTICE = String.raw`Showing lines \d+-\d+ of \d+(?: \(${NOTICE_SIZE} limit\))?`;
|
|
77
|
+
// Pi 1.2–1.4 appends these tool-specific notices, not arbitrary bracketed output.
|
|
78
|
+
const TOOL_NOTICES: Record<string, RegExp> = {
|
|
79
|
+
read: new RegExp(String.raw`^(?:${SHOWN_LINES_NOTICE}\. Use offset=\d+ to continue\.|\d+ more lines in file\. Use offset=\d+ to continue\.)$`),
|
|
80
|
+
bash: new RegExp(String.raw`^(?:${SHOWN_LINES_NOTICE}|Showing last ${NOTICE_SIZE} of line \d+ \(line is ${NOTICE_SIZE}\))\. Full output: [^\n]+$`),
|
|
81
|
+
grep: new RegExp(String.raw`^(?:\d+ matches limit reached\. Use limit=\d+ for more, or refine pattern(?:\. ${BYTE_LIMIT_NOTICE})?(?:\. ${GREP_LINES_NOTICE})?|${BYTE_LIMIT_NOTICE}(?:\. ${GREP_LINES_NOTICE})?|${GREP_LINES_NOTICE})$`),
|
|
82
|
+
find: new RegExp(String.raw`^(?:\d+ results limit reached(?:\. Use limit=\d+ for more, or refine pattern)?(?:\. ${BYTE_LIMIT_NOTICE})?|${BYTE_LIMIT_NOTICE})$`),
|
|
83
|
+
ls: new RegExp(String.raw`^(?:\d+ entries limit reached\. Use limit=\d+ for more(?:\. ${BYTE_LIMIT_NOTICE})?|${BYTE_LIMIT_NOTICE})$`),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** Drop only a recognized notice for this tool, preserving bracketed content and errors. */
|
|
87
|
+
export function stripNotice(toolName: string, text: string): string {
|
|
88
|
+
const notice = /\n\n\[([^\n]+)\]\s*$/.exec(text);
|
|
89
|
+
if (notice && TOOL_NOTICES[toolName === "powershell" ? "bash" : toolName]?.test(notice[1]!)) {
|
|
90
|
+
text = text.slice(0, notice.index);
|
|
91
|
+
}
|
|
92
|
+
return text.replace(/\n+$/, "");
|
|
66
93
|
}
|
|
67
94
|
|
|
68
95
|
function countLines(text: string): number {
|
|
@@ -154,7 +181,8 @@ function truncation(result: ToolResult): { truncated: boolean; totalLines?: numb
|
|
|
154
181
|
function incomplete(result: ToolResult): string {
|
|
155
182
|
const info = details(result);
|
|
156
183
|
const limit = ["matchLimitReached", "resultLimitReached", "entryLimitReached"].some((key) => info[key] !== undefined);
|
|
157
|
-
return (limit ? " · limit reached" : "")
|
|
184
|
+
return (limit ? " · limit reached" : "")
|
|
185
|
+
+ (truncation(result).truncated || info.linesTruncated === true ? " · truncated" : "");
|
|
158
186
|
}
|
|
159
187
|
|
|
160
188
|
/** Line count of shell output plus any incomplete-result hint. */
|
|
@@ -167,12 +195,12 @@ function shellLines(output: string, result: ToolResult): string {
|
|
|
167
195
|
/** One-line result summary for a finished, successful call. */
|
|
168
196
|
export function summarizeResult(toolName: string, result: ToolResult, args: Args): string {
|
|
169
197
|
const raw = textOutput(result);
|
|
170
|
-
const text = stripNotice(raw);
|
|
198
|
+
const text = stripNotice(toolName, raw);
|
|
171
199
|
switch (toolName) {
|
|
172
200
|
case "read": {
|
|
173
201
|
if (result.content.some((block) => block.type === "image")) return "image";
|
|
174
202
|
// An explicit `limit` that stops early adds this notice without setting details.truncation.
|
|
175
|
-
const more = /\n\n\[\d+ more lines in file\.
|
|
203
|
+
const more = /\n\n\[\d+ more lines in file\. Use offset=\d+ to continue\.\]\s*$/.test(raw) ? " · more" : "";
|
|
176
204
|
return plural(countLines(text), "line") + incomplete(result) + more;
|
|
177
205
|
}
|
|
178
206
|
case "bash":
|
|
@@ -185,7 +213,7 @@ export function summarizeResult(toolName: string, result: ToolResult, args: Args
|
|
|
185
213
|
return `+${added} −${removed}`;
|
|
186
214
|
}
|
|
187
215
|
case "write":
|
|
188
|
-
return `${plural(
|
|
216
|
+
return `${plural(codeLines(str(args.content)).length, "line")} written`;
|
|
189
217
|
case "grep": {
|
|
190
218
|
if (text === "No matches found") return "No matches";
|
|
191
219
|
const files = new Set<string>();
|
|
@@ -217,7 +245,7 @@ export function summarizeError(toolName: string, result: ToolResult, duration =
|
|
|
217
245
|
const status = toolName === "bash" || toolName === "powershell" ? /(?:^|\n\n)Command ([^\n]+)$/.exec(text) : null;
|
|
218
246
|
if (status) {
|
|
219
247
|
const reason = status[1]!;
|
|
220
|
-
const output = stripNotice(text.slice(0, status.index));
|
|
248
|
+
const output = stripNotice(toolName, text.slice(0, status.index));
|
|
221
249
|
// The last output line usually names the failure; it goes last so narrow rows cut it first.
|
|
222
250
|
const last = tailLines(output, 1)[0]?.trim();
|
|
223
251
|
const tail = last && last !== "(no output)" ? ` · ${last}` : "";
|
|
@@ -230,11 +258,25 @@ function formatElapsed(ms: number): string {
|
|
|
230
258
|
return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
231
259
|
}
|
|
232
260
|
|
|
233
|
-
/**
|
|
234
|
-
function lines(
|
|
261
|
+
/** Stable rows reuse their final lines until width, theme, icons, or invalidation changes them. */
|
|
262
|
+
function lines(
|
|
263
|
+
build: (width: number, icons: IconGlyphs) => string[],
|
|
264
|
+
theme: Theme,
|
|
265
|
+
glyphs: () => IconGlyphs,
|
|
266
|
+
cacheable = true,
|
|
267
|
+
): Component {
|
|
268
|
+
let cache: { width: number; colors: Theme["colors"]; toolCall: string; toolResult: string; lines: string[] } | undefined;
|
|
235
269
|
return {
|
|
236
|
-
render
|
|
237
|
-
|
|
270
|
+
render(width) {
|
|
271
|
+
const icons = glyphs();
|
|
272
|
+
const colors = theme.colors;
|
|
273
|
+
if (cacheable && cache && cache.width === width && cache.colors === colors
|
|
274
|
+
&& cache.toolCall === icons.toolCall && cache.toolResult === icons.toolResult) return cache.lines;
|
|
275
|
+
const rendered = build(width, icons).map((line) => truncateToWidth(line, Math.max(0, width), "…"));
|
|
276
|
+
if (cacheable) cache = { width, colors, toolCall: icons.toolCall, toolResult: icons.toolResult, lines: rendered };
|
|
277
|
+
return rendered;
|
|
278
|
+
},
|
|
279
|
+
invalidate() { cache = undefined; },
|
|
238
280
|
};
|
|
239
281
|
}
|
|
240
282
|
|
|
@@ -253,10 +295,16 @@ function statusBg(theme: Theme, context: RenderContext): (text: string) => strin
|
|
|
253
295
|
return (text) => theme.bg(bg, text);
|
|
254
296
|
}
|
|
255
297
|
|
|
298
|
+
function stopPulse(row: RowState): void {
|
|
299
|
+
if (row.pulse) clearInterval(row.pulse);
|
|
300
|
+
row.pulse = undefined;
|
|
301
|
+
}
|
|
302
|
+
|
|
256
303
|
export function createCompactRenderers(
|
|
257
304
|
toolName: string,
|
|
258
305
|
native: ToolRenderers,
|
|
259
306
|
glyphs: () => IconGlyphs,
|
|
307
|
+
running?: Map<string, RowState | undefined>,
|
|
260
308
|
): ToolRenderers {
|
|
261
309
|
const label = TOOL_LABELS[toolName] ?? toolName;
|
|
262
310
|
const isShell = toolName === "bash" || toolName === "powershell";
|
|
@@ -267,9 +315,26 @@ export function createCompactRenderers(
|
|
|
267
315
|
renderShell: "self",
|
|
268
316
|
renderCall(args, theme, context) {
|
|
269
317
|
const row = rowState(context);
|
|
270
|
-
//
|
|
271
|
-
|
|
318
|
+
// Keep native timers/edit state current; write rebuilds its full highlight cache only on expansion.
|
|
319
|
+
if (toolName !== "write" || context.expanded) {
|
|
320
|
+
row.nativeCall = native.renderCall?.(args, theme, nativeContext(context, row, row.nativeCall));
|
|
321
|
+
}
|
|
272
322
|
if (context.executionStarted) row.startedAt ??= Date.now();
|
|
323
|
+
const live = context.executionStarted && context.isPartial && !context.isError;
|
|
324
|
+
// Actual execution events exclude history rebuilding and HTML export's partial call context.
|
|
325
|
+
if (live && !context.expanded && running?.has(context.toolCallId)) {
|
|
326
|
+
running.set(context.toolCallId, row);
|
|
327
|
+
if (!row.pulse) {
|
|
328
|
+
row.pulseBright = true;
|
|
329
|
+
row.pulse = setInterval(() => {
|
|
330
|
+
row.pulseBright = !row.pulseBright;
|
|
331
|
+
context.invalidate();
|
|
332
|
+
}, 1000);
|
|
333
|
+
row.pulse.unref();
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
stopPulse(row);
|
|
337
|
+
}
|
|
273
338
|
if (showNative(context) && row.nativeCall) {
|
|
274
339
|
if (native.renderShell === "self") return row.nativeCall;
|
|
275
340
|
// Recreate Pi's default card: top padding, call, then result (or bottom padding while pending).
|
|
@@ -283,17 +348,21 @@ export function createCompactRenderers(
|
|
|
283
348
|
row.callCard.addChild(row.callCardBottom);
|
|
284
349
|
return row.callCard;
|
|
285
350
|
}
|
|
286
|
-
const color: ThemeColor = context.isError ? "error" : context.isPartial
|
|
287
|
-
|
|
351
|
+
const color: ThemeColor = context.isError ? "error" : context.isPartial
|
|
352
|
+
? live && row.pulseBright !== false ? "accent" : "dim"
|
|
353
|
+
: "success";
|
|
354
|
+
const titleColor = context.isPartial || context.isError ? "toolTitle" : "dim";
|
|
355
|
+
const detail = sanitizeStatus(callDetail(toolName, (args ?? {}) as Args, context.cwd));
|
|
288
356
|
const pad = " ".repeat(context.outputPad);
|
|
289
|
-
return lines(() => [
|
|
290
|
-
pad + theme.fg(color,
|
|
291
|
-
+ (detail ? theme.fg("muted", `(${detail})`) : ""),
|
|
292
|
-
]);
|
|
357
|
+
return lines((_width, icons) => [
|
|
358
|
+
pad + theme.fg(color, icons.toolCall) + " " + theme.fg(titleColor, titleColor === "dim" ? label : theme.bold(label))
|
|
359
|
+
+ (detail ? theme.fg(context.isPartial || context.isError ? "muted" : "dim", `(${detail})`) : ""),
|
|
360
|
+
], theme, glyphs, !context.isPartial);
|
|
293
361
|
},
|
|
294
362
|
|
|
295
363
|
renderResult(result, options, theme, context) {
|
|
296
364
|
const row = rowState(context);
|
|
365
|
+
if (!options.isPartial || context.isError) stopPulse(row);
|
|
297
366
|
row.nativeResult = native.renderResult?.(result, options, theme, nativeContext(context, row, row.nativeResult));
|
|
298
367
|
if (showNative(context) && row.nativeResult) {
|
|
299
368
|
if (native.renderShell === "self") return row.nativeResult;
|
|
@@ -309,26 +378,30 @@ export function createCompactRenderers(
|
|
|
309
378
|
}
|
|
310
379
|
const pad = " ".repeat(context.outputPad);
|
|
311
380
|
const args = (context.args ?? {}) as Args;
|
|
312
|
-
|
|
313
|
-
const
|
|
381
|
+
if (!options.isPartial && !context.isError && (toolName === "edit" || toolName === "write")) {
|
|
382
|
+
const source = toolName === "edit" ? str(details(result).diff) : str(args.content);
|
|
383
|
+
const path = str(args.file_path ?? args.path);
|
|
384
|
+
if (!row.preview || row.preview.source !== source || row.preview.path !== path || row.preview.colors !== theme.colors) {
|
|
385
|
+
row.preview = { source, path, colors: theme.colors, render: prepareToolPreview(toolName, source, path, theme) };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const duration = isShell && context.durationMs !== undefined ? ` · ${formatElapsed(context.durationMs)}` : "";
|
|
389
|
+
const summary = options.isPartial ? "" : context.isError
|
|
390
|
+
? summarizeError(toolName, result, duration)
|
|
391
|
+
: summarizeResult(toolName, result, args) + duration;
|
|
392
|
+
const tail = options.isPartial && isShell ? tailLines(textOutput(result), LIVE_TAIL_LINES) : [];
|
|
393
|
+
return lines((width, { toolResult }) => {
|
|
314
394
|
const gutter = `${pad} ${theme.fg("dim", toolResult)} `;
|
|
315
395
|
const indent = `${pad} ${" ".repeat(visibleWidth(toolResult))} `;
|
|
316
396
|
if (options.isPartial) {
|
|
317
397
|
if (!isShell) return [gutter + theme.fg("warning", "Running…")];
|
|
318
|
-
const tail = tailLines(textOutput(result), LIVE_TAIL_LINES).map((line) => theme.fg("toolOutput", line));
|
|
319
398
|
const elapsed = row.startedAt === undefined ? "" : ` ${formatElapsed(Date.now() - row.startedAt)}`;
|
|
320
|
-
return [...tail, theme.fg("warning", `Running…${elapsed}`)]
|
|
399
|
+
return [...tail.map((line) => theme.fg("toolOutput", line)), theme.fg("warning", `Running…${elapsed}`)]
|
|
321
400
|
.map((line, index) => (index === 0 ? gutter : indent) + line);
|
|
322
401
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (toolName === "edit" && /^\+\d+ −\d+$/.test(summary)) {
|
|
327
|
-
const [added, removed] = summary.split(" ");
|
|
328
|
-
return [gutter + theme.fg("toolDiffAdded", added!) + " " + theme.fg("toolDiffRemoved", removed!)];
|
|
329
|
-
}
|
|
330
|
-
return [gutter + theme.fg("muted", summary + duration)];
|
|
331
|
-
});
|
|
402
|
+
if (context.isError) return [gutter + theme.fg("error", summary)];
|
|
403
|
+
return [gutter + theme.fg("dim", summary), ...(row.preview?.render(width, indent) ?? [])];
|
|
404
|
+
}, theme, glyphs, !options.isPartial);
|
|
332
405
|
},
|
|
333
406
|
};
|
|
334
407
|
}
|
|
@@ -340,9 +413,21 @@ const OWNED_TOOLS = new Set(Object.keys(TOOL_LABELS));
|
|
|
340
413
|
* when Pi builds a row, so switching it affects new rows only.
|
|
341
414
|
*/
|
|
342
415
|
export function registerToolView(pi: ExtensionAPI, getMode: () => ToolViewMode, glyphs: () => IconGlyphs): void {
|
|
416
|
+
const running = new Map<string, RowState | undefined>();
|
|
417
|
+
pi.on("tool_execution_start", (event) => { running.set(event.toolCallId, undefined); });
|
|
418
|
+
pi.on("tool_execution_end", (event) => {
|
|
419
|
+
const row = running.get(event.toolCallId);
|
|
420
|
+
if (row) stopPulse(row);
|
|
421
|
+
running.delete(event.toolCallId);
|
|
422
|
+
});
|
|
423
|
+
pi.on("session_shutdown", () => {
|
|
424
|
+
for (const row of running.values()) if (row) stopPulse(row);
|
|
425
|
+
running.clear();
|
|
426
|
+
});
|
|
343
427
|
pi.registerToolRenderer((toolName, next) => {
|
|
344
428
|
const native = next();
|
|
345
429
|
if (!native || !OWNED_TOOLS.has(toolName) || getMode() !== "compact") return native;
|
|
346
|
-
|
|
430
|
+
if (pi.getAllTools().find((tool) => tool.name === toolName)?.sourceInfo?.source !== "builtin") return native;
|
|
431
|
+
return createCompactRenderers(toolName, native, glyphs, running);
|
|
347
432
|
});
|
|
348
433
|
}
|
|
@@ -135,8 +135,8 @@ export type PrioritizedSegment = {
|
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Pack segments into maxWidth: compact segments first, then shrink/drop the
|
|
138
|
-
* lowest-priority segments (higher priority = survives longer). Returns
|
|
139
|
-
*
|
|
138
|
+
* lowest-priority segments (higher priority = survives longer). Returns one
|
|
139
|
+
* text per input segment, preserving positions with empty strings for dropped items.
|
|
140
140
|
*/
|
|
141
141
|
export function fitSegmentsByPriority(
|
|
142
142
|
segs: readonly PrioritizedSegment[],
|
|
@@ -205,7 +205,7 @@ export function fitSegmentsByPriority(
|
|
|
205
205
|
break;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
-
return items.
|
|
208
|
+
return items.map((it) => it.text);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
export function stressColor(value: number, warn = 70, danger = 90): ThemeColor {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-open-tui",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "A polished TUI for Pi coding agent:
|
|
3
|
+
"version": "0.4.2",
|
|
4
|
+
"description": "A polished TUI for Pi coding agent: branded header, responsive footer, framed editor, thinking preview, and compact tool views.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -20,12 +20,13 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"extensions/",
|
|
22
22
|
"assets/",
|
|
23
|
+
"CHANGELOG.md",
|
|
23
24
|
"README.md",
|
|
24
25
|
"README.zh-CN.md",
|
|
25
26
|
"LICENSE"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|
|
28
|
-
"test": "node --test tests/settings-command.test.ts tests/footer.test.ts tests/editor.test.ts tests/telemetry.test.ts tests/peek.test.ts tests/tool-view.test.ts",
|
|
29
|
+
"test": "node --test tests/settings-command.test.ts tests/footer.test.ts tests/editor.test.ts tests/telemetry.test.ts tests/peek.test.ts tests/tool-view.test.ts tests/copy-buttons.test.ts",
|
|
29
30
|
"typecheck": "tsc --noEmit"
|
|
30
31
|
},
|
|
31
32
|
"pi": {
|
|
@@ -35,8 +36,8 @@
|
|
|
35
36
|
"image": "https://raw.githubusercontent.com/OldSuns/pi-open-tui/main/assets/preview_dashboard_1.png"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
|
-
"@earendil-works/pi-coding-agent": ">=1.2.0",
|
|
39
39
|
"@earendil-works/pi-ai": ">=1.2.0",
|
|
40
|
+
"@earendil-works/pi-coding-agent": ">=1.2.0",
|
|
40
41
|
"@earendil-works/pi-tui": ">=1.2.0"
|
|
41
42
|
},
|
|
42
43
|
"peerDependenciesMeta": {
|
|
@@ -51,17 +52,13 @@
|
|
|
51
52
|
}
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
|
-
"@earendil-works/pi-coding-agent": "^1.2.0",
|
|
55
55
|
"@earendil-works/pi-ai": "^1.2.0",
|
|
56
|
+
"@earendil-works/pi-coding-agent": "^1.2.0",
|
|
56
57
|
"@earendil-works/pi-tui": "^1.2.0",
|
|
57
58
|
"@types/node": "^22.0.0",
|
|
58
59
|
"typescript": "^5.5.0"
|
|
59
60
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"protobufjs@7.6.4": true,
|
|
63
|
-
"@google/genai@1.52.0": true,
|
|
64
|
-
"koffi@2.16.2": true,
|
|
65
|
-
"tree-sitter-bash@0.25.1": true
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"diff": "^9.0.0"
|
|
66
63
|
}
|
|
67
64
|
}
|