pi-cc-extensions 0.8.26 → 0.8.27
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/extensions/claude-code-style.ts +209 -94
- package/extensions/index.ts +20 -0
- package/extensions/subagent-notification.ts +114 -0
- package/extensions/tool-diff/diff-renderer.ts +95 -55
- package/extensions/tool-diff/index.ts +2 -1
- package/extensions/tool-diff/shiki-highlight.ts +90 -0
- package/extensions/tool-grouping.ts +444 -0
- package/extensions/tool-loading-icon.ts +9 -0
- package/extensions/tool-result-sanitize.ts +2 -2
- package/package.json +3 -8
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import { AssistantMessageComponent, ToolExecutionComponent } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Container, Spacer, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import { TOOL_LOADING_INTERVAL_MS, toolLoadingIcon } from "./tool-loading-icon.ts";
|
|
4
|
+
import { sanitizeToolResultText } from "./tool-result-sanitize.ts";
|
|
5
|
+
|
|
6
|
+
const PATCH_KEY = Symbol.for("pi.ccstyle.tool-grouping-patch");
|
|
7
|
+
const PARENT_KEY = Symbol.for("pi.ccstyle.tool-grouping-parent");
|
|
8
|
+
const GENERATION_KEY = Symbol.for("pi.ccstyle.tool-grouping-generation");
|
|
9
|
+
const NON_GROUPABLE = new Set(["edit", "write", "apply_patch"]);
|
|
10
|
+
|
|
11
|
+
type Patch = {
|
|
12
|
+
owner: object;
|
|
13
|
+
active: boolean;
|
|
14
|
+
prototype: any;
|
|
15
|
+
original: { addChild: Function; removeChild: Function; clear: Function };
|
|
16
|
+
installed: { addChild: Function; removeChild: Function; clear: Function };
|
|
17
|
+
groups: Set<ToolGroupComponent>;
|
|
18
|
+
enabled: () => boolean;
|
|
19
|
+
generation: number;
|
|
20
|
+
lastEnabled: boolean;
|
|
21
|
+
theme?: any;
|
|
22
|
+
animationTimer: ReturnType<typeof setTimeout> | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function toolName(tool: any): string {
|
|
26
|
+
return String(tool?.toolName ?? tool?.toolDefinition?.name ?? "tool");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isGroupable(value: unknown): value is any {
|
|
30
|
+
return value instanceof ToolExecutionComponent && !NON_GROUPABLE.has(toolName(value));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isIgnorable(value: unknown): boolean {
|
|
34
|
+
if (value instanceof Spacer) return true;
|
|
35
|
+
if (!(value instanceof AssistantMessageComponent)) return false;
|
|
36
|
+
const children = (value as any).contentContainer?.children;
|
|
37
|
+
return Array.isArray(children) && children.length === 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function previousSibling(
|
|
41
|
+
children: any[],
|
|
42
|
+
start: number,
|
|
43
|
+
): { child: any; index: number } | undefined {
|
|
44
|
+
let skipped = 0;
|
|
45
|
+
for (let index = start; index >= 0; index--) {
|
|
46
|
+
const child = children[index];
|
|
47
|
+
if (isIgnorable(child) && skipped < 3) {
|
|
48
|
+
skipped++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
return { child, index };
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type ToolStatus = "pending" | "success" | "error";
|
|
57
|
+
|
|
58
|
+
function status(tool: any): ToolStatus {
|
|
59
|
+
if (tool?.result?.isError) return "error";
|
|
60
|
+
if (tool?.isPartial === true || (tool?.executionStarted && !tool?.result)) return "pending";
|
|
61
|
+
return tool?.result ? "success" : "pending";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function statusIcon(value: ToolStatus): string {
|
|
65
|
+
if (value === "success") return "✓";
|
|
66
|
+
if (value === "error") return "✗";
|
|
67
|
+
return toolLoadingIcon();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function scheduleGroupAnimation(patch: Patch): void {
|
|
71
|
+
if (patch.animationTimer || !patch.active) return;
|
|
72
|
+
patch.animationTimer = setTimeout(() => {
|
|
73
|
+
patch.animationTimer = null;
|
|
74
|
+
if (!patch.active) return;
|
|
75
|
+
for (const group of patch.groups) {
|
|
76
|
+
if (group.children.some((tool) => status(tool) === "pending")) group.invalidate();
|
|
77
|
+
}
|
|
78
|
+
}, TOOL_LOADING_INTERVAL_MS);
|
|
79
|
+
patch.animationTimer.unref?.();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function stripAnsi(line: string): string {
|
|
83
|
+
return line.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function visibleLines(lines: string[]): string[] {
|
|
87
|
+
return lines.filter((line) => stripAnsi(line).trim());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stripLeadingStatusIcon(line: string): string {
|
|
91
|
+
return line.replace(
|
|
92
|
+
/^((?:\x1b\[[0-9;]*m|[ \t]|[├└│─])*)(?:\x1b\[[0-9;]*m)*(?:[✓✗●○■⬤•·])(?:\x1b\[[0-9;]*m)*\s+/,
|
|
93
|
+
"$1",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function oneLine(value: unknown, max = 96): string {
|
|
98
|
+
const text = sanitizeToolResultText(String(value ?? ""), 4096)
|
|
99
|
+
.replace(/\s+/g, " ")
|
|
100
|
+
.trim();
|
|
101
|
+
return text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function humanizeToolName(name: string): string {
|
|
105
|
+
return name
|
|
106
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
107
|
+
.replace(/[_-]+/g, " ")
|
|
108
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function toolSummary(tool: any): { main: string; detail: string } {
|
|
112
|
+
const name = toolName(tool);
|
|
113
|
+
const args = tool?.args ?? {};
|
|
114
|
+
if (name === "read") {
|
|
115
|
+
const details = [
|
|
116
|
+
args.offset !== undefined ? `offset=${args.offset}` : "",
|
|
117
|
+
args.limit !== undefined ? `limit=${args.limit}` : "",
|
|
118
|
+
].filter(Boolean);
|
|
119
|
+
return {
|
|
120
|
+
main: `Read ${oneLine(args.path || "...")}`,
|
|
121
|
+
detail: details.length ? ` (${details.join(", ")})` : "",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (name === "bash") return { main: `Bash ${oneLine(args.command || "...")}`, detail: "" };
|
|
125
|
+
if (name === "grep") {
|
|
126
|
+
const pattern = oneLine(args.pattern || "...");
|
|
127
|
+
return {
|
|
128
|
+
main: `Grep ${JSON.stringify(pattern)}${args.path ? ` in ${oneLine(args.path)}` : ""}`,
|
|
129
|
+
detail: "",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (name === "find") {
|
|
133
|
+
const pattern = oneLine(args.pattern || "...");
|
|
134
|
+
return {
|
|
135
|
+
main: `Find ${JSON.stringify(pattern)}${args.path ? ` in ${oneLine(args.path)}` : ""}`,
|
|
136
|
+
detail: "",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const preferred =
|
|
140
|
+
args.agent_id ??
|
|
141
|
+
args.path ??
|
|
142
|
+
args.file_path ??
|
|
143
|
+
args.url ??
|
|
144
|
+
args.description ??
|
|
145
|
+
args.query ??
|
|
146
|
+
args.name ??
|
|
147
|
+
args.prompt;
|
|
148
|
+
return {
|
|
149
|
+
main: `${humanizeToolName(name)}${preferred === undefined ? "" : ` ${oneLine(preferred)}`}`,
|
|
150
|
+
detail: "",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function toolNameList(tools: any[]): string {
|
|
155
|
+
const counts = new Map<string, number>();
|
|
156
|
+
for (const tool of tools) counts.set(toolName(tool), (counts.get(toolName(tool)) ?? 0) + 1);
|
|
157
|
+
return [...counts].map(([name, count]) => `${name}${count > 1 ? `×${count}` : ""}`).join(", ");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let nextGroupId = 1;
|
|
161
|
+
|
|
162
|
+
export class ToolGroupComponent extends Container {
|
|
163
|
+
readonly toolCallId = `ccstyle-tool-group-${nextGroupId++}`;
|
|
164
|
+
readonly toolName = "Tool group";
|
|
165
|
+
private expanded = false;
|
|
166
|
+
private hintHovered = false;
|
|
167
|
+
private readonly patch: Patch;
|
|
168
|
+
|
|
169
|
+
constructor(patch: Patch) {
|
|
170
|
+
super();
|
|
171
|
+
this.patch = patch;
|
|
172
|
+
patch.groups.add(this);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
addTool(tool: any): void {
|
|
176
|
+
this.children.push(tool);
|
|
177
|
+
tool[PARENT_KEY] = this;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
releaseTools(): any[] {
|
|
181
|
+
const tools = [...this.children];
|
|
182
|
+
this.children.length = 0;
|
|
183
|
+
this.patch.groups.delete(this);
|
|
184
|
+
return tools;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
removeTool(tool: any): void {
|
|
188
|
+
const index = this.children.indexOf(tool);
|
|
189
|
+
if (index >= 0) this.children.splice(index, 1);
|
|
190
|
+
if (tool?.[PARENT_KEY] === this) delete tool[PARENT_KEY];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
setExpanded(expanded: boolean): void {
|
|
194
|
+
this.expanded = expanded;
|
|
195
|
+
for (const tool of this.children) tool.setExpanded?.(expanded);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
setHintHovered(hovered: boolean): void {
|
|
199
|
+
this.hintHovered = hovered;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
invalidate(): void {
|
|
203
|
+
for (const tool of this.children) tool.invalidate?.();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
render(width: number): string[] {
|
|
207
|
+
const theme = this.patch.theme;
|
|
208
|
+
const fg = (color: string, text: string) => theme?.fg?.(color, text) ?? text;
|
|
209
|
+
const counts = { pending: 0, success: 0, error: 0 };
|
|
210
|
+
for (const tool of this.children) counts[status(tool)]++;
|
|
211
|
+
const countText = (["pending", "success", "error"] as const)
|
|
212
|
+
.filter((key) => counts[key])
|
|
213
|
+
.map((key) => {
|
|
214
|
+
const label = key === "pending" ? "running" : key === "success" ? "done" : "failed";
|
|
215
|
+
const color = key === "pending" ? "accent" : key;
|
|
216
|
+
return `${fg(color, String(counts[key]))} ${label}`;
|
|
217
|
+
})
|
|
218
|
+
.join(` ${fg("dim", "•")} `);
|
|
219
|
+
const names = new Set(this.children.map(toolName));
|
|
220
|
+
const label =
|
|
221
|
+
names.size === 1 ? humanizeToolName(toolName(this.children[0])) : "Multiple Tools";
|
|
222
|
+
const overall: ToolStatus = counts.error ? "error" : counts.pending ? "pending" : "success";
|
|
223
|
+
if (overall === "pending") scheduleGroupAnimation(this.patch);
|
|
224
|
+
const overallColor = overall === "pending" ? "accent" : overall;
|
|
225
|
+
const nameList = names.size > 1 ? ` ${fg("dim", `• ${toolNameList(this.children)}`)}` : "";
|
|
226
|
+
const hint = fg(this.hintHovered ? "text" : "dim", "• click to show more");
|
|
227
|
+
const lines = [
|
|
228
|
+
"",
|
|
229
|
+
truncateToWidth(
|
|
230
|
+
` ${fg(overallColor, statusIcon(overall))} ${label}: ${countText}${nameList} ${hint}`,
|
|
231
|
+
width,
|
|
232
|
+
"…",
|
|
233
|
+
),
|
|
234
|
+
];
|
|
235
|
+
const total = this.children.length;
|
|
236
|
+
for (let index = 0; index < total; index++) {
|
|
237
|
+
const tool = this.children[index];
|
|
238
|
+
const toolStatus = status(tool);
|
|
239
|
+
const color = toolStatus === "pending" ? "accent" : toolStatus;
|
|
240
|
+
const branch = index === total - 1 ? "└" : "├";
|
|
241
|
+
const continuation = index === total - 1 ? " " : "│ ";
|
|
242
|
+
if (!this.expanded) {
|
|
243
|
+
const summary = toolSummary(tool);
|
|
244
|
+
lines.push(
|
|
245
|
+
truncateToWidth(
|
|
246
|
+
` ${fg("dim", branch)} ${fg(color, "●")} ${fg("toolTitle", summary.main)}${fg("dim", summary.detail)}`,
|
|
247
|
+
width,
|
|
248
|
+
"…",
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const rendered = visibleLines(tool.render(Math.max(1, width - 5)));
|
|
254
|
+
if (rendered.length) {
|
|
255
|
+
rendered[0] = stripLeadingStatusIcon(rendered[0])
|
|
256
|
+
.replace(/^ +/, "")
|
|
257
|
+
.replace(/^((?:\x1b\[[0-?]*[ -/]*[@-~])*) +/, "$1");
|
|
258
|
+
}
|
|
259
|
+
const childLines = rendered.length ? rendered : [toolSummary(tool).main];
|
|
260
|
+
for (let lineIndex = 0; lineIndex < childLines.length; lineIndex++) {
|
|
261
|
+
const prefix =
|
|
262
|
+
lineIndex === 0
|
|
263
|
+
? ` ${fg("dim", branch)} ${fg(color, "●")} `
|
|
264
|
+
: ` ${fg("dim", continuation)} `;
|
|
265
|
+
lines.push(truncateToWidth(prefix + childLines[lineIndex], width, ""));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
lines.push("");
|
|
269
|
+
return lines;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function ungroup(patch: Patch): void {
|
|
274
|
+
for (const group of [...patch.groups]) {
|
|
275
|
+
const parent = (group as any)[PARENT_KEY];
|
|
276
|
+
const children = parent?.children;
|
|
277
|
+
if (!Array.isArray(children)) {
|
|
278
|
+
patch.groups.delete(group);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const index = children.indexOf(group);
|
|
282
|
+
if (index < 0) {
|
|
283
|
+
patch.groups.delete(group);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const tools = group.releaseTools();
|
|
287
|
+
for (const tool of tools) tool[PARENT_KEY] = parent;
|
|
288
|
+
children.splice(index, 1, ...tools);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function normalizeGroup(patch: Patch, group: ToolGroupComponent): void {
|
|
293
|
+
if (group.children.length > 1) return;
|
|
294
|
+
const parent = (group as any)[PARENT_KEY];
|
|
295
|
+
const index = parent?.children?.indexOf(group) ?? -1;
|
|
296
|
+
const tools = group.releaseTools();
|
|
297
|
+
delete (group as any)[PARENT_KEY];
|
|
298
|
+
if (index < 0) {
|
|
299
|
+
for (const tool of tools) delete tool[PARENT_KEY];
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (tools.length === 1) {
|
|
303
|
+
tools[0][PARENT_KEY] = parent;
|
|
304
|
+
parent.children.splice(index, 1, tools[0]);
|
|
305
|
+
} else {
|
|
306
|
+
parent.children.splice(index, 1);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function maybeGroup(patch: Patch, parent: any, component: any): void {
|
|
311
|
+
if (
|
|
312
|
+
!patch.active ||
|
|
313
|
+
!patch.enabled() ||
|
|
314
|
+
parent instanceof ToolGroupComponent ||
|
|
315
|
+
!isGroupable(component)
|
|
316
|
+
)
|
|
317
|
+
return;
|
|
318
|
+
component[GENERATION_KEY] = patch.generation;
|
|
319
|
+
const children = parent?.children;
|
|
320
|
+
if (!Array.isArray(children)) return;
|
|
321
|
+
const index = children.indexOf(component);
|
|
322
|
+
const prior = previousSibling(children, index - 1);
|
|
323
|
+
if (!prior) return;
|
|
324
|
+
if (prior.child instanceof ToolGroupComponent && (prior.child as any).patch === patch) {
|
|
325
|
+
children.splice(index, 1);
|
|
326
|
+
prior.child.addTool(component);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (!isGroupable(prior.child) || prior.child[GENERATION_KEY] !== patch.generation) return;
|
|
330
|
+
const group = new ToolGroupComponent(patch);
|
|
331
|
+
group.addTool(prior.child);
|
|
332
|
+
group.addTool(component);
|
|
333
|
+
(group as any)[PARENT_KEY] = parent;
|
|
334
|
+
children[prior.index] = group;
|
|
335
|
+
children.splice(index, 1);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export type ToolGroupingHooks = {
|
|
339
|
+
setTheme(theme: any): void;
|
|
340
|
+
refresh(): void;
|
|
341
|
+
shutdown(): void;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export function installToolGrouping(getEnabled: () => boolean): ToolGroupingHooks {
|
|
345
|
+
const prototype = Container.prototype as any;
|
|
346
|
+
const host = globalThis as any;
|
|
347
|
+
const previous = host[PATCH_KEY] as Patch | undefined;
|
|
348
|
+
if (previous) {
|
|
349
|
+
previous.active = false;
|
|
350
|
+
previous.enabled = () => false;
|
|
351
|
+
if (previous.animationTimer) clearTimeout(previous.animationTimer);
|
|
352
|
+
previous.animationTimer = null;
|
|
353
|
+
ungroup(previous);
|
|
354
|
+
}
|
|
355
|
+
const original = {
|
|
356
|
+
addChild:
|
|
357
|
+
previous && prototype.addChild === previous.installed.addChild
|
|
358
|
+
? previous.original.addChild
|
|
359
|
+
: prototype.addChild,
|
|
360
|
+
removeChild:
|
|
361
|
+
previous && prototype.removeChild === previous.installed.removeChild
|
|
362
|
+
? previous.original.removeChild
|
|
363
|
+
: prototype.removeChild,
|
|
364
|
+
clear:
|
|
365
|
+
previous && prototype.clear === previous.installed.clear
|
|
366
|
+
? previous.original.clear
|
|
367
|
+
: prototype.clear,
|
|
368
|
+
};
|
|
369
|
+
const patch: Patch = {
|
|
370
|
+
owner: {},
|
|
371
|
+
active: true,
|
|
372
|
+
prototype,
|
|
373
|
+
original,
|
|
374
|
+
installed: undefined as any,
|
|
375
|
+
groups: new Set(),
|
|
376
|
+
enabled: getEnabled,
|
|
377
|
+
generation: 0,
|
|
378
|
+
lastEnabled: getEnabled(),
|
|
379
|
+
animationTimer: null,
|
|
380
|
+
};
|
|
381
|
+
patch.installed = {
|
|
382
|
+
addChild: function (this: any, component: any) {
|
|
383
|
+
const result = patch.original.addChild.call(this, component);
|
|
384
|
+
if (component && typeof component === "object") component[PARENT_KEY] = this;
|
|
385
|
+
maybeGroup(patch, this, component);
|
|
386
|
+
return result;
|
|
387
|
+
},
|
|
388
|
+
removeChild: function (this: any, component: any) {
|
|
389
|
+
const group = component?.[PARENT_KEY];
|
|
390
|
+
if (group instanceof ToolGroupComponent && (group as any)[PARENT_KEY] === this) {
|
|
391
|
+
group.removeTool(component);
|
|
392
|
+
normalizeGroup(patch, group);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const result = patch.original.removeChild.call(this, component);
|
|
396
|
+
if (component?.[PARENT_KEY] === this) delete component[PARENT_KEY];
|
|
397
|
+
if (this instanceof ToolGroupComponent) normalizeGroup(patch, this);
|
|
398
|
+
if (component instanceof ToolGroupComponent) {
|
|
399
|
+
for (const tool of component.releaseTools()) delete tool[PARENT_KEY];
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
},
|
|
403
|
+
clear: function (this: any) {
|
|
404
|
+
for (const child of [...(this.children ?? [])]) {
|
|
405
|
+
if (child instanceof ToolGroupComponent) {
|
|
406
|
+
for (const tool of child.releaseTools()) delete tool[PARENT_KEY];
|
|
407
|
+
}
|
|
408
|
+
if (child?.[PARENT_KEY] === this) delete child[PARENT_KEY];
|
|
409
|
+
}
|
|
410
|
+
if (this instanceof ToolGroupComponent) patch.groups.delete(this);
|
|
411
|
+
return patch.original.clear.call(this);
|
|
412
|
+
},
|
|
413
|
+
};
|
|
414
|
+
prototype.addChild = patch.installed.addChild;
|
|
415
|
+
prototype.removeChild = patch.installed.removeChild;
|
|
416
|
+
prototype.clear = patch.installed.clear;
|
|
417
|
+
host[PATCH_KEY] = patch;
|
|
418
|
+
return {
|
|
419
|
+
setTheme(theme: any) {
|
|
420
|
+
patch.theme = theme;
|
|
421
|
+
},
|
|
422
|
+
refresh() {
|
|
423
|
+
const enabled = patch.enabled();
|
|
424
|
+
if (enabled !== patch.lastEnabled) {
|
|
425
|
+
patch.lastEnabled = enabled;
|
|
426
|
+
if (enabled) patch.generation++;
|
|
427
|
+
}
|
|
428
|
+
if (!enabled) ungroup(patch);
|
|
429
|
+
},
|
|
430
|
+
shutdown() {
|
|
431
|
+
if (!patch.active) return;
|
|
432
|
+
patch.active = false;
|
|
433
|
+
if (patch.animationTimer) clearTimeout(patch.animationTimer);
|
|
434
|
+
patch.animationTimer = null;
|
|
435
|
+
patch.enabled = () => false;
|
|
436
|
+
ungroup(patch);
|
|
437
|
+
if (prototype.addChild === patch.installed.addChild)
|
|
438
|
+
prototype.addChild = patch.original.addChild;
|
|
439
|
+
if (prototype.removeChild === patch.installed.removeChild)
|
|
440
|
+
prototype.removeChild = patch.original.removeChild;
|
|
441
|
+
if (prototype.clear === patch.installed.clear) prototype.clear = patch.original.clear;
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const TOOL_LOADING_INTERVAL_MS = 80;
|
|
2
|
+
|
|
3
|
+
const BRAILLE_LOADING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
4
|
+
|
|
5
|
+
export function toolLoadingIcon(now = Date.now()): string {
|
|
6
|
+
return BRAILLE_LOADING_FRAMES[
|
|
7
|
+
Math.floor(now / TOOL_LOADING_INTERVAL_MS) % BRAILLE_LOADING_FRAMES.length
|
|
8
|
+
]!;
|
|
9
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const UNSAFE_TERMINAL_ESCAPE = new RegExp(
|
|
2
|
-
"
|
|
3
|
-
"
|
|
2
|
+
"(?:\\u001B\\]|\\u009D)[\\s\\S]*?(?:\\u0007|\\u001B\\x5C|\\u009C)" +
|
|
3
|
+
"|(?:\\u001B[PX^_]|\\u0090)[\\s\\S]*?(?:\\u001B\\x5C|\\u009C)" +
|
|
4
4
|
"|(?:\\u001B\\[|\\u009B)[0-?]*[ -/]*[@-~]" +
|
|
5
5
|
"|\\u001B[@-_]",
|
|
6
6
|
"g",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cc-extensions",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.27",
|
|
4
4
|
"description": "A Pi productivity suite with Claude Code-style UI, structured questionnaires, context inspection, and agent/session references",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -27,13 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"pi": {
|
|
29
29
|
"extensions": [
|
|
30
|
-
"./extensions/
|
|
31
|
-
"./extensions/pi-startup-header.ts",
|
|
32
|
-
"./extensions/claude-code-style.ts",
|
|
33
|
-
"./extensions/context.ts",
|
|
34
|
-
"./extensions/session-reference.ts",
|
|
35
|
-
"./extensions/agent-autocomplete.ts",
|
|
36
|
-
"./extensions/ask-user-question/index.ts"
|
|
30
|
+
"./extensions/index.ts"
|
|
37
31
|
],
|
|
38
32
|
"themes": [
|
|
39
33
|
"./themes/github-dark-default.json"
|
|
@@ -52,6 +46,7 @@
|
|
|
52
46
|
},
|
|
53
47
|
"dependencies": {
|
|
54
48
|
"@juicesharp/rpiv-config": "^2.1.0",
|
|
49
|
+
"@shikijs/cli": "^4.0.2",
|
|
55
50
|
"typebox": "^1.1.24"
|
|
56
51
|
},
|
|
57
52
|
"peerDependencies": {
|