@hheei/pi-ext-ui 0.4.2 → 0.5.1
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/README.md +6 -0
- package/dist/extension.js +55 -2
- package/dist/host-transcript-patch.d.ts +6 -0
- package/dist/host-transcript-patch.js +462 -0
- package/dist/payload-parser.d.ts +1 -1
- package/dist/payload-parser.js +19 -2
- package/dist/renderer.d.ts +5 -1
- package/dist/renderer.js +4 -8
- package/dist/settings.d.ts +6 -0
- package/dist/settings.js +36 -0
- package/dist/tool-view.d.ts +4 -0
- package/dist/tool-view.js +24 -10
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -7,3 +7,9 @@ Pi UI primitives and a small set of specialized tool renderers.
|
|
|
7
7
|
The shared view API owns fold levels, compact sections, status glyphs, copy feedback, and width-safe rendering. It does not read files, execute tools, infer outcomes, or import a concrete extension.
|
|
8
8
|
|
|
9
9
|
Background Bash and subagent tools own their display formats through ext-core `ToolPresentation` providers. This resolver consumes those providers automatically, including nested codemode calls, and adds shared folding and copy controls. Without `pi-ext-ui`, the same tools retain native Pi renderers. No direct dependency on `pi-ext-ui` is required by the tool extensions.
|
|
10
|
+
|
|
11
|
+
Activity groups are on by default for Pi **>=1.0.1** TUI sessions. Turn them off with the Tool UI setting `toolGroups.enabled` in `ext_settings.json` (the ext-core settings page). The change applies to the current session. A heading such as `▸ codemode ×2 · grep ×3` keeps one live summary row per tool beneath a single outer rail. Each thinking round and notification also keeps a summary row; notifications are not counted. Groups form while tools run and can span consecutive tool/thinking rounds. Prose, user messages, unknown components, images, and unknown tool renderers break groups. Streaming arguments, partial results, completion, and errors update the existing row.
|
|
12
|
+
|
|
13
|
+
Fullscreen: click the group heading to restore the original component layouts and interactions beneath the outer rail. Both terminal modes: Ctrl+O forces groups open; closing global expansion restores each group’s local choice. Appending members preserves that choice. Original per-tool fold choices remain intact. Groups are presentation only and are rebuilt on history navigation; session data, model context, and HTML export remain unchanged. Thinking summaries keep a label; expanding respects native thinking visibility. Each `ctx.ui.notify` call retains its full text for expansion. Notifications remain session-local, as in Pi.
|
|
14
|
+
|
|
15
|
+
Grouping uses two centralized, version-checked host prototype hooks: tool lookup and extension notifications. Thinking uses the existing assistant message, with no extra hook. Unsupported hosts or incompatible layouts report a warning and retain normal rendering. Cleanup restores the original methods; if another extension replaces the same methods, restart Pi to unload the conflicting patch. For workspace development use `pnpm run pi-dev`: running the bundled CLI beside physical host devDependencies can resolve a second host class, unlike the published installation.
|
package/dist/extension.js
CHANGED
|
@@ -1,12 +1,50 @@
|
|
|
1
|
-
import { messageViewPoint, registerExtensionHook, registerExtensionLifecycle, toolRendererPoint, } from "@hheei/pi-ext-core";
|
|
1
|
+
import { getRuntimeSettingsRegistry, messageViewPoint, registerExtensionHook, registerExtensionLifecycle, registerSettings, toolRendererPoint, } from "@hheei/pi-ext-core";
|
|
2
|
+
import { installHostToolGroups } from "./host-transcript-patch.js";
|
|
2
3
|
import { createMessageView } from "./message-view.js";
|
|
3
4
|
import { createOptionalToolRendererFactory, registerCollapsibleToolRenderer } from "./renderer.js";
|
|
5
|
+
import { activityGroupsEnabled, createToolGroupSettings } from "./settings.js";
|
|
4
6
|
import { clearCapturedNestedCalls } from "./tracker.js";
|
|
7
|
+
function sessionId(extension) {
|
|
8
|
+
const manager = extension.sessionManager;
|
|
9
|
+
if (typeof manager !== "object" || manager === null || !("getSessionId" in manager))
|
|
10
|
+
return "";
|
|
11
|
+
const read = manager.getSessionId;
|
|
12
|
+
return typeof read === "function" ? String(read.call(manager)) : "";
|
|
13
|
+
}
|
|
5
14
|
export default function (pi) {
|
|
15
|
+
let redrawGroups;
|
|
16
|
+
pi.on("agent_settled", () => redrawGroups?.());
|
|
6
17
|
registerCollapsibleToolRenderer(pi);
|
|
7
18
|
registerExtensionLifecycle(pi, {
|
|
8
19
|
key: "@hheei/pi-ext-ui",
|
|
9
|
-
start(context) {
|
|
20
|
+
async start(context) {
|
|
21
|
+
let patch;
|
|
22
|
+
const apply = (enabled) => {
|
|
23
|
+
if (context.extension.mode !== "tui")
|
|
24
|
+
return;
|
|
25
|
+
if (!enabled) {
|
|
26
|
+
redrawGroups = undefined;
|
|
27
|
+
patch?.dispose();
|
|
28
|
+
patch = undefined;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (patch !== undefined)
|
|
32
|
+
return;
|
|
33
|
+
try {
|
|
34
|
+
patch = installHostToolGroups(context.extension);
|
|
35
|
+
redrawGroups = patch.requestRender;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
context.extension.ui.notify(error instanceof Error ? error.message : String(error), "warning");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const provider = createToolGroupSettings({ onEnabled: apply });
|
|
42
|
+
context.resources.add("tool-group-settings", registerSettings(provider, getRuntimeSettingsRegistry(pi)));
|
|
43
|
+
context.resources.add("tool-groups", () => {
|
|
44
|
+
redrawGroups = undefined;
|
|
45
|
+
patch?.dispose();
|
|
46
|
+
patch = undefined;
|
|
47
|
+
});
|
|
10
48
|
context.resources.add("tracker-cleanup", () => {
|
|
11
49
|
clearCapturedNestedCalls();
|
|
12
50
|
});
|
|
@@ -14,6 +52,21 @@ export default function (pi) {
|
|
|
14
52
|
context.resources.add("tool-renderer", () => toolRenderers.dispose());
|
|
15
53
|
const handle = registerExtensionHook(context.pi, messageViewPoint, createMessageView);
|
|
16
54
|
context.resources.add("message-view", () => handle.dispose());
|
|
55
|
+
try {
|
|
56
|
+
const state = await provider.storage.load({
|
|
57
|
+
sessionId: sessionId(context.extension),
|
|
58
|
+
signal: context.signal,
|
|
59
|
+
});
|
|
60
|
+
if (!context.signal.aborted)
|
|
61
|
+
apply(activityGroupsEnabled(state));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (!context.signal.aborted) {
|
|
65
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
66
|
+
context.extension.ui.notify(`Tool activity groups settings unreadable: ${detail}`, "warning");
|
|
67
|
+
apply(true);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
17
70
|
},
|
|
18
71
|
});
|
|
19
72
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
/** Private to pi-ext-ui: two process hooks, scoped by the actual host UI facade. */
|
|
3
|
+
export declare function installHostToolGroups(context: ExtensionContext): {
|
|
4
|
+
dispose(): void;
|
|
5
|
+
requestRender(): void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { AssistantMessageComponent, InteractiveMode, ToolExecutionComponent, VERSION, } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Container, MouseRegion, Spacer, stripTerminalSequences, Text, truncateToWidth, } from "@earendil-works/pi-tui";
|
|
3
|
+
import { renderWithoutToolRails, ToolView, toSingleRow } from "./tool-view.js";
|
|
4
|
+
const SEAMS = ["getRegisteredToolDefinition", "showExtensionNotify"];
|
|
5
|
+
function record(value) {
|
|
6
|
+
return typeof value === "object" && value !== null;
|
|
7
|
+
}
|
|
8
|
+
// Audited Pi >=1.0.1 compiles these TS-private members to ordinary JS properties.
|
|
9
|
+
// All host-private access stays here; VERSION comes from the running host virtual module.
|
|
10
|
+
function readHost(value) {
|
|
11
|
+
if (!record(value))
|
|
12
|
+
throw new Error("Invalid InteractiveMode instance");
|
|
13
|
+
const chat = value.chatContainer;
|
|
14
|
+
const session = value.session;
|
|
15
|
+
const tui = value.ui;
|
|
16
|
+
if (!record(session) || !record(tui) || typeof tui.requestRender !== "function") {
|
|
17
|
+
throw new Error("Pi host session/TUI shape changed");
|
|
18
|
+
}
|
|
19
|
+
const runner = session.extensionRunner;
|
|
20
|
+
if (!(chat instanceof Container) ||
|
|
21
|
+
!record(runner) ||
|
|
22
|
+
typeof runner.getUIContext !== "function") {
|
|
23
|
+
throw new Error("Pi host transcript/runner shape changed");
|
|
24
|
+
}
|
|
25
|
+
const ui = runner.getUIContext();
|
|
26
|
+
const requestRender = tui.requestRender;
|
|
27
|
+
return { chat, ui, requestRender: () => requestRender.call(tui) };
|
|
28
|
+
}
|
|
29
|
+
function assistantInfo(component) {
|
|
30
|
+
const message = Reflect.get(component, "lastMessage");
|
|
31
|
+
if (!record(message) || !Array.isArray(message.content)) {
|
|
32
|
+
throw new Error("Pi assistant message shape changed");
|
|
33
|
+
}
|
|
34
|
+
const calls = new Set();
|
|
35
|
+
let eligible = !["length", "aborted", "error"].includes(String(message.stopReason));
|
|
36
|
+
let thinking = false;
|
|
37
|
+
for (const block of message.content) {
|
|
38
|
+
if (!record(block))
|
|
39
|
+
throw new Error("Pi assistant content shape changed");
|
|
40
|
+
if (block.type === "toolCall" && typeof block.id === "string")
|
|
41
|
+
calls.add(block.id);
|
|
42
|
+
else if (block.type === "thinking" && typeof block.thinking === "string")
|
|
43
|
+
thinking ||= block.thinking.trim() !== "";
|
|
44
|
+
else if (block.type !== "text" || typeof block.text !== "string" || block.text.trim() !== "")
|
|
45
|
+
eligible = false;
|
|
46
|
+
}
|
|
47
|
+
return { calls, eligible, thinking };
|
|
48
|
+
}
|
|
49
|
+
function toolMember(component, calls) {
|
|
50
|
+
const id = Reflect.get(component, "toolCallId");
|
|
51
|
+
const name = Reflect.get(component, "toolName");
|
|
52
|
+
const partial = Reflect.get(component, "isPartial");
|
|
53
|
+
const result = Reflect.get(component, "result");
|
|
54
|
+
const renderer = Reflect.get(component, "resultRendererComponent");
|
|
55
|
+
const callRenderer = Reflect.get(component, "callRendererComponent");
|
|
56
|
+
if (typeof id !== "string" || typeof name !== "string" || typeof partial !== "boolean") {
|
|
57
|
+
throw new Error("Pi tool component shape changed");
|
|
58
|
+
}
|
|
59
|
+
if (!calls.has(id) || !(callRenderer instanceof ToolView))
|
|
60
|
+
return undefined;
|
|
61
|
+
if (result !== undefined &&
|
|
62
|
+
(!record(result) ||
|
|
63
|
+
!Array.isArray(result.content) ||
|
|
64
|
+
!result.content.every((block) => record(block) && block.type === "text") ||
|
|
65
|
+
!(renderer instanceof ToolView)))
|
|
66
|
+
return undefined;
|
|
67
|
+
const summary = renderer instanceof ToolView
|
|
68
|
+
? (renderer.summary ?? callRenderer.summary)
|
|
69
|
+
: callRenderer.summary;
|
|
70
|
+
if (summary === undefined)
|
|
71
|
+
return undefined;
|
|
72
|
+
return { component, name: toSingleRow(stripTerminalSequences(name)), summary };
|
|
73
|
+
}
|
|
74
|
+
class NotifyView extends Container {
|
|
75
|
+
#text = new Text("", 1, 0);
|
|
76
|
+
#context;
|
|
77
|
+
#message;
|
|
78
|
+
#type;
|
|
79
|
+
constructor(message, type, context) {
|
|
80
|
+
super();
|
|
81
|
+
this.#context = context;
|
|
82
|
+
this.#message = message;
|
|
83
|
+
this.#type = type;
|
|
84
|
+
this.addChild(new Spacer(1));
|
|
85
|
+
this.addChild(this.#text);
|
|
86
|
+
}
|
|
87
|
+
get summary() {
|
|
88
|
+
return this.#context.ui.theme.fg(this.#type === "info" ? "dim" : this.#type, toSingleRow(`notify · ${this.#content}`));
|
|
89
|
+
}
|
|
90
|
+
get #content() {
|
|
91
|
+
const prefix = this.#type === "error" ? "Error: " : this.#type === "warning" ? "Warning: " : "";
|
|
92
|
+
return `${prefix}${this.#message}`;
|
|
93
|
+
}
|
|
94
|
+
render(width) {
|
|
95
|
+
this.#text.setText(this.#context.ui.theme.fg(this.#type === "info" ? "dim" : this.#type, this.#content));
|
|
96
|
+
return super.render(width);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class ToolGroup extends Container {
|
|
100
|
+
#header = new Text("", 0, 0);
|
|
101
|
+
#originals = new Container();
|
|
102
|
+
#context;
|
|
103
|
+
#members = [];
|
|
104
|
+
#expanded = false;
|
|
105
|
+
#renderedExpanded = false;
|
|
106
|
+
constructor(context, redraw) {
|
|
107
|
+
super();
|
|
108
|
+
this.#context = context;
|
|
109
|
+
const heading = new MouseRegion(this.#header, (event) => {
|
|
110
|
+
if (event.type !== "click" || event.button !== "left")
|
|
111
|
+
return undefined;
|
|
112
|
+
this.#expanded = this.#context.ui.getToolsExpanded() ? false : !this.#expanded;
|
|
113
|
+
redraw();
|
|
114
|
+
return { handled: true, render: true };
|
|
115
|
+
});
|
|
116
|
+
const body = {
|
|
117
|
+
render: (width) => {
|
|
118
|
+
const rows = this.#renderedExpanded
|
|
119
|
+
? renderWithoutToolRails(this.#originals, Math.max(1, width - 2))
|
|
120
|
+
: this.#members.flatMap((member) => member.summary === undefined ? [] : [member.summary]);
|
|
121
|
+
const rail = this.#context.ui.theme.fg("dim", "▎");
|
|
122
|
+
return rows.map((row) => truncateToWidth(`${rail} ${row}`, Math.max(0, width), "…"));
|
|
123
|
+
},
|
|
124
|
+
handleMouse: (event) => {
|
|
125
|
+
if (!this.#renderedExpanded || event.x < 2)
|
|
126
|
+
return undefined;
|
|
127
|
+
return this.#originals.handleMouse({
|
|
128
|
+
...event,
|
|
129
|
+
x: event.x - 2,
|
|
130
|
+
width: Math.max(1, event.width - 2),
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
invalidate() { },
|
|
134
|
+
};
|
|
135
|
+
this.children = [new Spacer(1), heading, body];
|
|
136
|
+
}
|
|
137
|
+
update(members) {
|
|
138
|
+
this.#members = members;
|
|
139
|
+
this.#originals.children = members.map(({ component, name }) => {
|
|
140
|
+
if (name === undefined)
|
|
141
|
+
return component;
|
|
142
|
+
let offset = 0;
|
|
143
|
+
return {
|
|
144
|
+
render(width) {
|
|
145
|
+
const rows = component.render(width);
|
|
146
|
+
// Pi adds one leading separator per tool; the group supplies its own spacing.
|
|
147
|
+
offset = rows[0] === "" ? 1 : 0;
|
|
148
|
+
return rows.slice(offset);
|
|
149
|
+
},
|
|
150
|
+
handleMouse(event) {
|
|
151
|
+
return component.handleMouse?.({
|
|
152
|
+
...event,
|
|
153
|
+
y: event.y + offset,
|
|
154
|
+
height: event.height + offset,
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
invalidate() { },
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
invalidate() {
|
|
162
|
+
// Original chatContainer.invalidate invalidates original members exactly once.
|
|
163
|
+
this.#header.invalidate();
|
|
164
|
+
}
|
|
165
|
+
render(width) {
|
|
166
|
+
this.#renderedExpanded = this.#expanded || this.#context.ui.getToolsExpanded();
|
|
167
|
+
const theme = this.#context.ui.theme;
|
|
168
|
+
const counts = new Map();
|
|
169
|
+
for (const member of this.#members) {
|
|
170
|
+
if (member.name !== undefined)
|
|
171
|
+
counts.set(member.name, (counts.get(member.name) ?? 0) + 1);
|
|
172
|
+
}
|
|
173
|
+
const heading = [...counts]
|
|
174
|
+
.map(([name, count]) => `${theme.fg("toolTitle", theme.bold(name))} ${theme.fg("dim", `×${count}`)}`)
|
|
175
|
+
.join(theme.fg("dim", " · "));
|
|
176
|
+
this.#header.setText(truncateToWidth(`${theme.fg("dim", this.#renderedExpanded ? "▾" : "▸")} ${heading}`, Math.max(0, width), "…"));
|
|
177
|
+
return super.render(width);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function attach(host, context) {
|
|
181
|
+
const chat = host.chat;
|
|
182
|
+
const original = {
|
|
183
|
+
render: chat.render,
|
|
184
|
+
handleMouse: chat.handleMouse,
|
|
185
|
+
invalidate: chat.invalidate,
|
|
186
|
+
};
|
|
187
|
+
const descriptors = Object.getOwnPropertyDescriptors(chat);
|
|
188
|
+
for (const key of ["render", "handleMouse", "invalidate"]) {
|
|
189
|
+
const descriptor = descriptors[key];
|
|
190
|
+
if (descriptor !== undefined && descriptor.writable !== true) {
|
|
191
|
+
throw new Error(`Pi transcript ${key} cannot be replaced`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!Object.isExtensible(chat))
|
|
195
|
+
throw new Error("Pi transcript cannot accept instance methods");
|
|
196
|
+
const projection = new Container();
|
|
197
|
+
let groups = new Map();
|
|
198
|
+
let active = true;
|
|
199
|
+
const rebuild = () => {
|
|
200
|
+
const next = new Map();
|
|
201
|
+
const children = [];
|
|
202
|
+
let calls = new Set();
|
|
203
|
+
let members = [];
|
|
204
|
+
const flush = () => {
|
|
205
|
+
const first = members[0]?.component;
|
|
206
|
+
if (first !== undefined && members.filter((member) => member.name !== undefined).length > 1) {
|
|
207
|
+
const group = groups.get(first) ?? new ToolGroup(context, host.requestRender);
|
|
208
|
+
group.update(members);
|
|
209
|
+
next.set(first, group);
|
|
210
|
+
children.push(group);
|
|
211
|
+
}
|
|
212
|
+
else
|
|
213
|
+
children.push(...members.map((member) => member.component));
|
|
214
|
+
members = [];
|
|
215
|
+
};
|
|
216
|
+
for (const child of chat.children) {
|
|
217
|
+
if (child instanceof AssistantMessageComponent) {
|
|
218
|
+
const info = assistantInfo(child);
|
|
219
|
+
calls = info.calls;
|
|
220
|
+
if (info.eligible) {
|
|
221
|
+
members.push({
|
|
222
|
+
component: child,
|
|
223
|
+
...(info.thinking ? { summary: context.ui.theme.fg("thinkingText", "thinking…") } : {}),
|
|
224
|
+
});
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (child instanceof ToolExecutionComponent) {
|
|
229
|
+
const member = toolMember(child, calls);
|
|
230
|
+
if (member !== undefined) {
|
|
231
|
+
members.push(member);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else if (child instanceof NotifyView) {
|
|
236
|
+
members.push({ component: child, summary: child.summary });
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
flush();
|
|
240
|
+
children.push(child);
|
|
241
|
+
}
|
|
242
|
+
flush();
|
|
243
|
+
groups = next;
|
|
244
|
+
projection.children = children;
|
|
245
|
+
};
|
|
246
|
+
const wrappers = {
|
|
247
|
+
render(width) {
|
|
248
|
+
if (!active)
|
|
249
|
+
return original.render.call(chat, width);
|
|
250
|
+
try {
|
|
251
|
+
rebuild();
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
active = false;
|
|
255
|
+
groups.clear();
|
|
256
|
+
projection.clear();
|
|
257
|
+
context.ui.notify(`Tool grouping disabled: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
258
|
+
return original.render.call(chat, width);
|
|
259
|
+
}
|
|
260
|
+
// Real renderer errors propagate; never hide a tool-rendering failure.
|
|
261
|
+
return projection.render(width);
|
|
262
|
+
},
|
|
263
|
+
handleMouse(event) {
|
|
264
|
+
return active ? projection.handleMouse(event) : original.handleMouse.call(chat, event);
|
|
265
|
+
},
|
|
266
|
+
invalidate() {
|
|
267
|
+
original.invalidate.call(chat);
|
|
268
|
+
for (const group of groups.values())
|
|
269
|
+
group.invalidate();
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
Object.assign(chat, wrappers);
|
|
273
|
+
host.requestRender();
|
|
274
|
+
let disposed = false;
|
|
275
|
+
return {
|
|
276
|
+
notify(message, type) {
|
|
277
|
+
if (!active)
|
|
278
|
+
return false;
|
|
279
|
+
chat.addChild(new NotifyView(message, type, context));
|
|
280
|
+
host.requestRender();
|
|
281
|
+
return true;
|
|
282
|
+
},
|
|
283
|
+
dispose() {
|
|
284
|
+
if (disposed)
|
|
285
|
+
return;
|
|
286
|
+
disposed = true;
|
|
287
|
+
active = false;
|
|
288
|
+
groups.clear();
|
|
289
|
+
projection.clear();
|
|
290
|
+
let conflict = false;
|
|
291
|
+
for (const key of ["render", "handleMouse", "invalidate"]) {
|
|
292
|
+
if (chat[key] !== wrappers[key]) {
|
|
293
|
+
conflict = true;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const descriptor = descriptors[key];
|
|
297
|
+
if (descriptor === undefined)
|
|
298
|
+
Reflect.deleteProperty(chat, key);
|
|
299
|
+
else
|
|
300
|
+
Object.defineProperty(chat, key, descriptor);
|
|
301
|
+
}
|
|
302
|
+
if (conflict)
|
|
303
|
+
context.ui.notify("Tool grouping patch changed by another extension; restart Pi to fully unload it.", "warning");
|
|
304
|
+
host.requestRender();
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function connect(owner, host) {
|
|
309
|
+
if (owner.hosts.has(host.chat) || owner.failed.has(host.chat))
|
|
310
|
+
return;
|
|
311
|
+
try {
|
|
312
|
+
owner.hosts.set(host.chat, { ...owner.attach(host), requestRender: host.requestRender });
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
owner.failed.add(host.chat);
|
|
316
|
+
owner.context.ui.notify(`Tool grouping unavailable: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function patchState() {
|
|
320
|
+
const prototype = InteractiveMode.prototype;
|
|
321
|
+
const key = Symbol.for("@hheei/pi-ext-ui/host-tool-groups/2");
|
|
322
|
+
const existing = Reflect.get(prototype, key);
|
|
323
|
+
if (existing !== undefined) {
|
|
324
|
+
if (!record(existing) ||
|
|
325
|
+
!(existing.owners instanceof Map) ||
|
|
326
|
+
!(existing.hosts instanceof Set) ||
|
|
327
|
+
!record(existing.originals) ||
|
|
328
|
+
!record(existing.wrappers) ||
|
|
329
|
+
SEAMS.some((seam) => !record(existing.originals[seam]) || typeof existing.wrappers[seam] !== "function"))
|
|
330
|
+
throw new Error("Invalid tool grouping patch owner");
|
|
331
|
+
return existing;
|
|
332
|
+
}
|
|
333
|
+
const lookup = Object.getOwnPropertyDescriptor(prototype, SEAMS[0]);
|
|
334
|
+
const notify = Object.getOwnPropertyDescriptor(prototype, SEAMS[1]);
|
|
335
|
+
if (typeof lookup?.value !== "function" ||
|
|
336
|
+
!lookup.writable ||
|
|
337
|
+
lookup.value.length !== 1 ||
|
|
338
|
+
typeof notify?.value !== "function" ||
|
|
339
|
+
!notify.writable ||
|
|
340
|
+
notify.value.length !== 2)
|
|
341
|
+
throw new Error("Pi tool/notification seam changed or is already patched");
|
|
342
|
+
const owners = new Map();
|
|
343
|
+
const hosts = new Set();
|
|
344
|
+
const seen = new WeakSet();
|
|
345
|
+
const failed = new WeakSet();
|
|
346
|
+
const locate = (instance) => {
|
|
347
|
+
if (owners.size === 0 || (record(instance) && failed.has(instance)))
|
|
348
|
+
return undefined;
|
|
349
|
+
let host;
|
|
350
|
+
try {
|
|
351
|
+
host = readHost(instance);
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
if (record(instance))
|
|
355
|
+
failed.add(instance);
|
|
356
|
+
for (const owner of owners.values())
|
|
357
|
+
owner.context.ui.notify(`Tool grouping unavailable: ${error instanceof Error ? error.message : String(error)}`, "warning");
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
if (record(instance) && !seen.has(instance)) {
|
|
361
|
+
seen.add(instance);
|
|
362
|
+
hosts.add(new WeakRef(instance));
|
|
363
|
+
}
|
|
364
|
+
const owner = owners.get(host.ui);
|
|
365
|
+
if (owner === undefined)
|
|
366
|
+
return undefined;
|
|
367
|
+
connect(owner, host);
|
|
368
|
+
return { owner, host };
|
|
369
|
+
};
|
|
370
|
+
const state = {
|
|
371
|
+
owners,
|
|
372
|
+
hosts,
|
|
373
|
+
originals: { getRegisteredToolDefinition: lookup, showExtensionNotify: notify },
|
|
374
|
+
wrappers: {
|
|
375
|
+
getRegisteredToolDefinition(name) {
|
|
376
|
+
const result = Reflect.apply(lookup.value, this, [name]);
|
|
377
|
+
locate(this);
|
|
378
|
+
return result;
|
|
379
|
+
},
|
|
380
|
+
showExtensionNotify(message, type) {
|
|
381
|
+
const match = locate(this);
|
|
382
|
+
if (match?.owner.hosts.get(match.host.chat)?.notify(message, type ?? "info"))
|
|
383
|
+
return;
|
|
384
|
+
return Reflect.apply(notify.value, this, [message, type]);
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
Object.defineProperty(prototype, key, { value: state });
|
|
389
|
+
return state;
|
|
390
|
+
}
|
|
391
|
+
/** Private to pi-ext-ui: two process hooks, scoped by the actual host UI facade. */
|
|
392
|
+
export function installHostToolGroups(context) {
|
|
393
|
+
const parts = VERSION.split(".").map(Number);
|
|
394
|
+
const [major = 0, minor = 0, patch = 0] = parts;
|
|
395
|
+
if (parts.length !== 3 ||
|
|
396
|
+
!parts.every(Number.isInteger) ||
|
|
397
|
+
!(major > 1 || (major === 1 && (minor > 0 || (minor === 0 && patch >= 1)))))
|
|
398
|
+
throw new Error(`Tool grouping requires Pi >=1.0.1; running ${VERSION}`);
|
|
399
|
+
const state = patchState();
|
|
400
|
+
if (state.owners.has(context.ui))
|
|
401
|
+
throw new Error("Tool grouping already owns this UI runtime");
|
|
402
|
+
if (state.owners.size === 0) {
|
|
403
|
+
for (const seam of SEAMS) {
|
|
404
|
+
if (Object.getOwnPropertyDescriptor(InteractiveMode.prototype, seam)?.value !==
|
|
405
|
+
state.originals[seam].value)
|
|
406
|
+
throw new Error("Pi tool/notification seam was changed by another extension");
|
|
407
|
+
}
|
|
408
|
+
for (const seam of SEAMS)
|
|
409
|
+
Object.defineProperty(InteractiveMode.prototype, seam, {
|
|
410
|
+
...state.originals[seam],
|
|
411
|
+
value: state.wrappers[seam],
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
const owner = {
|
|
415
|
+
context,
|
|
416
|
+
hosts: new Map(),
|
|
417
|
+
failed: new WeakSet(),
|
|
418
|
+
attach: (host) => attach(host, context),
|
|
419
|
+
};
|
|
420
|
+
state.owners.set(context.ui, owner);
|
|
421
|
+
// Reload rebuilds history before session_start; reconnect already discovered instances.
|
|
422
|
+
for (const reference of state.hosts) {
|
|
423
|
+
const instance = reference.deref();
|
|
424
|
+
if (instance === undefined) {
|
|
425
|
+
state.hosts.delete(reference);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
const host = readHost(instance);
|
|
430
|
+
if (host.ui === context.ui)
|
|
431
|
+
connect(owner, host);
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
state.hosts.delete(reference);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
let disposed = false;
|
|
438
|
+
return {
|
|
439
|
+
requestRender() {
|
|
440
|
+
for (const host of owner.hosts.values())
|
|
441
|
+
host.requestRender();
|
|
442
|
+
},
|
|
443
|
+
dispose() {
|
|
444
|
+
if (disposed)
|
|
445
|
+
return;
|
|
446
|
+
disposed = true;
|
|
447
|
+
state.owners.delete(context.ui);
|
|
448
|
+
for (const host of owner.hosts.values())
|
|
449
|
+
host.dispose();
|
|
450
|
+
owner.hosts.clear();
|
|
451
|
+
if (state.owners.size !== 0)
|
|
452
|
+
return;
|
|
453
|
+
for (const seam of SEAMS) {
|
|
454
|
+
if (Object.getOwnPropertyDescriptor(InteractiveMode.prototype, seam)?.value ===
|
|
455
|
+
state.wrappers[seam])
|
|
456
|
+
Object.defineProperty(InteractiveMode.prototype, seam, state.originals[seam]);
|
|
457
|
+
else
|
|
458
|
+
context.ui.notify("Tool grouping seam changed by another extension; restart Pi to fully unload it.", "warning");
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
}
|
package/dist/payload-parser.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { type DiffSource } from "./diff-view.js";
|
|
3
3
|
import type { ToolStatusKind } from "./tool-view.js";
|
|
4
4
|
export declare const EDIT_TOOL_NAME = "edit";
|
package/dist/payload-parser.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { formatReadLineRange } from "@earendil-works/pi-coding-agent";
|
|
2
1
|
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
3
2
|
import { diffStats } from "./diff-view.js";
|
|
4
3
|
export const EDIT_TOOL_NAME = "edit";
|
|
5
4
|
export const WRITE_TOOL_NAME = "write";
|
|
6
5
|
export const CODEMODE_OPTIONS_PREFIX = "// @options:";
|
|
7
6
|
export const CODEMODE_SCRIPT_HEADER = /^Script (?:completed|failed)\r?\nWall time ([\d.]+) seconds\r?\nOutput:\r?\n/;
|
|
7
|
+
// Pi <1.3 has no exported read-range formatter. Keep this UI summary independent of that export.
|
|
8
|
+
function readLineNumber(value) {
|
|
9
|
+
const parsed = typeof value === "number"
|
|
10
|
+
? value
|
|
11
|
+
: typeof value === "string" && value.trim() !== ""
|
|
12
|
+
? Number(value)
|
|
13
|
+
: Number.NaN;
|
|
14
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
15
|
+
}
|
|
8
16
|
export function isRecord(value) {
|
|
9
17
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
18
|
}
|
|
@@ -280,7 +288,16 @@ export function fileToolPresentation(toolName, args, _details, isError) {
|
|
|
280
288
|
if (path === "")
|
|
281
289
|
return undefined;
|
|
282
290
|
const parsed = parseCallArgs(args);
|
|
283
|
-
const
|
|
291
|
+
const offset = parsed?.offset;
|
|
292
|
+
const limit = parsed?.limit;
|
|
293
|
+
const start = offset == null ? 1 : readLineNumber(offset);
|
|
294
|
+
const count = limit == null ? undefined : readLineNumber(limit);
|
|
295
|
+
const end = start !== undefined && count !== undefined ? start + count - 1 : undefined;
|
|
296
|
+
const range = (offset == null && limit == null) ||
|
|
297
|
+
start === undefined ||
|
|
298
|
+
(limit != null && count === undefined)
|
|
299
|
+
? ""
|
|
300
|
+
: `:${start}${end ? `-${end}` : ""}`;
|
|
284
301
|
return { summary: `${path}${range}`, status: isError ? "error" : "ok" };
|
|
285
302
|
}
|
|
286
303
|
if (toolName === WRITE_TOOL_NAME) {
|
package/dist/renderer.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type ExtensionAPI, type ToolRendererResolver, type ToolRenderers } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
2
|
+
/** Optional host metadata is absent on early supported Pi versions. */
|
|
3
|
+
export type ToolRenderContext = Parameters<NonNullable<ToolRenderers["renderCall"]>>[2] & {
|
|
4
|
+
readonly durationMs?: number | undefined;
|
|
5
|
+
readonly outputPad?: number | undefined;
|
|
6
|
+
};
|
|
3
7
|
/**
|
|
4
8
|
* Upstream tools whose call and result views pi-ext-ui still frames itself. Concrete tool packages
|
|
5
9
|
* own their own presentation through ext-core's optional renderer point, so their names never
|
package/dist/renderer.js
CHANGED
|
@@ -107,12 +107,10 @@ function rememberCallView(context, view) {
|
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
109
|
* A result view takes over from the call view. A container tool yields on any result, because its
|
|
110
|
-
* own row would duplicate the nested rows and output it now carries
|
|
111
|
-
* call
|
|
110
|
+
* own row would duplicate the nested rows and output it now carries. A mounted result view takes
|
|
111
|
+
* over from its call view, including during partial updates. A provider states container semantics.
|
|
112
112
|
*/
|
|
113
|
-
function hideCallView(context
|
|
114
|
-
if (!replaceCallOnPartial && isPartial)
|
|
115
|
-
return;
|
|
113
|
+
function hideCallView(context) {
|
|
116
114
|
const view = isRecord(context.state) ? context.state[CALL_VIEW_STATE_KEY] : undefined;
|
|
117
115
|
if (view instanceof ToolView)
|
|
118
116
|
view.hide();
|
|
@@ -652,9 +650,7 @@ function createRendererResolver(options, resolvePresentation, resolveNativeRende
|
|
|
652
650
|
: provider(resultContext.args, result, theme, resultContext);
|
|
653
651
|
// Without an owner, a container frame is the only name-based decision left, and it
|
|
654
652
|
// belongs to upstream codemode.
|
|
655
|
-
|
|
656
|
-
(presentation === undefined && SPECIALIZED_TOOL_SET.has(toolName));
|
|
657
|
-
hideCallView(context, replacesCallOnPartial, renderOptions.isPartial);
|
|
653
|
+
hideCallView(context);
|
|
658
654
|
if (presentation !== undefined) {
|
|
659
655
|
return renderOwnedPresentation({
|
|
660
656
|
toolName,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type SettingsProvider, type SettingsState } from "@hheei/pi-ext-core";
|
|
2
|
+
/** Missing or non-boolean values stay on; only an explicit false disables groups. */
|
|
3
|
+
export declare function activityGroupsEnabled(state: SettingsState | undefined): boolean;
|
|
4
|
+
export declare function createToolGroupSettings(options: {
|
|
5
|
+
readonly onEnabled: (enabled: boolean) => void;
|
|
6
|
+
}): SettingsProvider;
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createJsonSettingsStorage, } from "@hheei/pi-ext-core";
|
|
2
|
+
const GROUP = "toolGroups";
|
|
3
|
+
const FIELD = "enabled";
|
|
4
|
+
/** Missing or non-boolean values stay on; only an explicit false disables groups. */
|
|
5
|
+
export function activityGroupsEnabled(state) {
|
|
6
|
+
const value = state?.[GROUP]?.[FIELD];
|
|
7
|
+
return value !== false;
|
|
8
|
+
}
|
|
9
|
+
export function createToolGroupSettings(options) {
|
|
10
|
+
return {
|
|
11
|
+
id: "pi-ext-ui",
|
|
12
|
+
title: "Tool UI",
|
|
13
|
+
origin: "@hheei/pi-ext-ui",
|
|
14
|
+
groups: [
|
|
15
|
+
{
|
|
16
|
+
id: GROUP,
|
|
17
|
+
title: "Tool activity",
|
|
18
|
+
fields: [
|
|
19
|
+
{
|
|
20
|
+
id: FIELD,
|
|
21
|
+
label: "Activity groups",
|
|
22
|
+
type: "boolean",
|
|
23
|
+
defaultValue: true,
|
|
24
|
+
description: "Group consecutive tool, thinking, and notification rows in the TUI transcript.",
|
|
25
|
+
parse: (draft) => draft === "true",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
storage: createJsonSettingsStorage({ group: GROUP }),
|
|
31
|
+
onChange(change) {
|
|
32
|
+
if (change.groupId === GROUP && change.fieldId === FIELD)
|
|
33
|
+
options.onEnabled(change.value === true);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
package/dist/tool-view.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface ToolViewSpec {
|
|
|
45
45
|
export declare function createToolView(spec: ToolViewSpec): ToolView;
|
|
46
46
|
export declare const MAX_LEVEL2_SECTION_LINES = 500;
|
|
47
47
|
export declare const MAX_LEVEL2_BYTES: number;
|
|
48
|
+
/** Internal transcript composition: original components keep their layout and interactions. */
|
|
49
|
+
export declare function renderWithoutToolRails(component: Component, width: number): string[];
|
|
48
50
|
export declare function transitionFoldLevel(current: FoldLevel, ctrlKey: boolean): FoldLevel;
|
|
49
51
|
export declare function formatStatusGlyph(status: ToolStatusKind | undefined, theme: Theme): string;
|
|
50
52
|
/**
|
|
@@ -83,6 +85,8 @@ export declare class ToolView implements Component {
|
|
|
83
85
|
get foldLevel(): FoldLevel;
|
|
84
86
|
get effectiveLevel(): FoldLevel;
|
|
85
87
|
get collapsed(): boolean;
|
|
88
|
+
/** Current semantic header, without fold/copy controls or body; hidden slots have no summary. */
|
|
89
|
+
get summary(): string | undefined;
|
|
86
90
|
setFoldLevel(next: FoldLevel): void;
|
|
87
91
|
setCollapsed(collapsed: boolean): void;
|
|
88
92
|
toggle(ctrlKey?: boolean): void;
|
package/dist/tool-view.js
CHANGED
|
@@ -18,6 +18,17 @@ const DEFAULT_MAX_SECTION_LINES = 9;
|
|
|
18
18
|
export const MAX_LEVEL2_SECTION_LINES = 500;
|
|
19
19
|
export const MAX_LEVEL2_BYTES = 64 * 1024;
|
|
20
20
|
const DEFAULT_COPY_FEEDBACK_MS = 1200;
|
|
21
|
+
let outerRailDepth = 0;
|
|
22
|
+
/** Internal transcript composition: original components keep their layout and interactions. */
|
|
23
|
+
export function renderWithoutToolRails(component, width) {
|
|
24
|
+
outerRailDepth++;
|
|
25
|
+
try {
|
|
26
|
+
return component.render(width);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
outerRailDepth--;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
21
32
|
function isRecord(value) {
|
|
22
33
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23
34
|
}
|
|
@@ -209,6 +220,7 @@ export class ToolView {
|
|
|
209
220
|
#localTimer;
|
|
210
221
|
#lastWidth = 80;
|
|
211
222
|
#lastHeight = 0;
|
|
223
|
+
#lastNoRail = false;
|
|
212
224
|
#cache;
|
|
213
225
|
constructor(spec) {
|
|
214
226
|
this.#spec = spec;
|
|
@@ -247,16 +259,15 @@ export class ToolView {
|
|
|
247
259
|
return readFoldLevel(this.#spec.stateHolder, this.#localLevel);
|
|
248
260
|
}
|
|
249
261
|
get effectiveLevel() {
|
|
250
|
-
|
|
251
|
-
if (base === 0)
|
|
252
|
-
return 0;
|
|
253
|
-
if (this.#spec.globalExpanded === true)
|
|
254
|
-
return 2;
|
|
255
|
-
return base;
|
|
262
|
+
return this.#spec.globalExpanded === true ? 2 : this.foldLevel;
|
|
256
263
|
}
|
|
257
264
|
get collapsed() {
|
|
258
265
|
return this.foldLevel === 0;
|
|
259
266
|
}
|
|
267
|
+
/** Current semantic header, without fold/copy controls or body; hidden slots have no summary. */
|
|
268
|
+
get summary() {
|
|
269
|
+
return this.#hidden ? undefined : formatToolHeaderText(this.#spec.header, this.#spec.theme);
|
|
270
|
+
}
|
|
260
271
|
#collapseChildren() {
|
|
261
272
|
const { collapsedRows, collapsibleChildren } = this.#spec;
|
|
262
273
|
if (collapsibleChildren !== undefined) {
|
|
@@ -284,7 +295,7 @@ export class ToolView {
|
|
|
284
295
|
this.setFoldLevel(collapsed ? 0 : 1);
|
|
285
296
|
}
|
|
286
297
|
toggle(ctrlKey = false) {
|
|
287
|
-
this.setFoldLevel(transitionFoldLevel(this.
|
|
298
|
+
this.setFoldLevel(transitionFoldLevel(this.effectiveLevel, ctrlKey));
|
|
288
299
|
}
|
|
289
300
|
#triggerCopy(shiftKey) {
|
|
290
301
|
const copySpec = this.#spec.copy;
|
|
@@ -363,7 +374,7 @@ export class ToolView {
|
|
|
363
374
|
const safeWidth = Math.max(1, width - pad * 2);
|
|
364
375
|
const level = this.effectiveLevel;
|
|
365
376
|
const { theme, collapsedRows, sections } = this.#spec;
|
|
366
|
-
const noRail = options?.noRail ?? this.#spec.noRail
|
|
377
|
+
const noRail = options?.noRail ?? (outerRailDepth > 0 || this.#spec.noRail === true);
|
|
367
378
|
const innerWidth = noRail ? safeWidth : Math.max(1, safeWidth - 2);
|
|
368
379
|
const output = [];
|
|
369
380
|
if (level === 0) {
|
|
@@ -420,15 +431,18 @@ export class ToolView {
|
|
|
420
431
|
return [];
|
|
421
432
|
const level = this.effectiveLevel;
|
|
422
433
|
const copied = isCopiedActive(this.#spec.stateHolder, this.#localCopiedUntil);
|
|
434
|
+
const noRail = outerRailDepth > 0 || this.#spec.noRail === true;
|
|
423
435
|
if (this.#cache !== undefined &&
|
|
424
436
|
this.#cache.width === width &&
|
|
425
437
|
this.#cache.level === level &&
|
|
438
|
+
this.#cache.noRail === noRail &&
|
|
426
439
|
this.#cache.copied === copied) {
|
|
427
440
|
return this.#cache.lines;
|
|
428
441
|
}
|
|
429
442
|
const pad = Math.max(0, this.#spec.outputPad ?? 0);
|
|
430
443
|
const safeWidth = Math.max(1, width - pad * 2);
|
|
431
444
|
this.#lastWidth = width;
|
|
445
|
+
this.#lastNoRail = noRail;
|
|
432
446
|
if (level !== 0) {
|
|
433
447
|
this.#collapseChildren();
|
|
434
448
|
}
|
|
@@ -447,7 +461,7 @@ export class ToolView {
|
|
|
447
461
|
}
|
|
448
462
|
}
|
|
449
463
|
this.#lastHeight = safeLines.length;
|
|
450
|
-
this.#cache = { width, level, copied, lines: safeLines };
|
|
464
|
+
this.#cache = { width, level, copied, noRail, lines: safeLines };
|
|
451
465
|
return safeLines;
|
|
452
466
|
}
|
|
453
467
|
handleMouse(event) {
|
|
@@ -488,7 +502,7 @@ export class ToolView {
|
|
|
488
502
|
"handleMouse" in rows &&
|
|
489
503
|
typeof rows.handleMouse === "function") {
|
|
490
504
|
const handler = rows.handleMouse;
|
|
491
|
-
const railOffset = this.#
|
|
505
|
+
const railOffset = this.#lastNoRail ? 0 : 2;
|
|
492
506
|
const subEvent = {
|
|
493
507
|
...event,
|
|
494
508
|
y: event.y - 1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hheei/pi-ext-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Collapsible tool rendering frame and tool renderer pilot for Pi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@hheei/pi-ext-core": "^0.6.
|
|
39
|
+
"@hheei/pi-ext-core": "^0.6.4"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@earendil-works/pi-coding-agent": ">=1.
|
|
43
|
-
"@earendil-works/pi-tui": ">=1.
|
|
42
|
+
"@earendil-works/pi-coding-agent": ">=1.0.1",
|
|
43
|
+
"@earendil-works/pi-tui": ">=1.0.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@earendil-works/pi-coding-agent": "1.3.0",
|