pi-compact-tools 0.6.3 → 0.7.8
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 +151 -183
- package/examples/compact-tools-2.json +1 -4
- package/examples/compact-tools.json +1 -4
- package/extensions/compact-tools-color.ts +65 -0
- package/extensions/compact-tools-config.ts +7 -42
- package/extensions/compact-tools-core.ts +26 -0
- package/extensions/compact-tools-invocation.ts +402 -117
- package/extensions/compact-tools-layout.ts +23 -22
- package/extensions/compact-tools-progress.ts +158 -0
- package/extensions/compact-tools-runtime.ts +44 -70
- package/extensions/compact-tools-thinking.ts +280 -337
- package/extensions/compact-tools-types.ts +9 -8
- package/extensions/compact-tools.ts +350 -322
- package/package.json +64 -64
- package/schemas/compact-tools.schema.json +8 -24
- package/scripts/check-resources.mjs +13 -16
- package/extensions/compact-tools-input.ts +0 -8
|
@@ -1,322 +1,350 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentToolResult,
|
|
3
|
-
BashToolDetails,
|
|
4
|
-
BashToolInput,
|
|
5
|
-
ExtensionAPI,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from "@earendil-works/pi-
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function
|
|
145
|
-
definition: BuiltInDefinition,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const component =
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
runtime.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
1
|
+
import type {
|
|
2
|
+
AgentToolResult,
|
|
3
|
+
BashToolDetails,
|
|
4
|
+
BashToolInput,
|
|
5
|
+
ExtensionAPI,
|
|
6
|
+
Theme,
|
|
7
|
+
ToolDefinition,
|
|
8
|
+
ToolRenderResultOptions,
|
|
9
|
+
} from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import {
|
|
11
|
+
createBashToolDefinition,
|
|
12
|
+
createEditToolDefinition,
|
|
13
|
+
createFindToolDefinition,
|
|
14
|
+
createGrepToolDefinition,
|
|
15
|
+
createLsToolDefinition,
|
|
16
|
+
createPowerShellToolDefinition,
|
|
17
|
+
createReadToolDefinition,
|
|
18
|
+
createWriteToolDefinition,
|
|
19
|
+
} from "@earendil-works/pi-coding-agent";
|
|
20
|
+
import type { Component, Container } from "@earendil-works/pi-tui";
|
|
21
|
+
import {
|
|
22
|
+
classifyCallStatus,
|
|
23
|
+
formatDurationMs,
|
|
24
|
+
indicatorGlyph,
|
|
25
|
+
indicatorStrength,
|
|
26
|
+
indicatorTone,
|
|
27
|
+
normalizeLineEndings,
|
|
28
|
+
type RowStatus,
|
|
29
|
+
} from "./compact-tools-core.ts";
|
|
30
|
+
import { colorizeRgb, interpolateRgb, themeColorRgb } from "./compact-tools-color.ts";
|
|
31
|
+
import { loadConfig } from "./compact-tools-config.ts";
|
|
32
|
+
import {
|
|
33
|
+
formatResultLineSummary,
|
|
34
|
+
getArgumentDetails,
|
|
35
|
+
getCallDetails,
|
|
36
|
+
getEditChanges,
|
|
37
|
+
getFileOutput,
|
|
38
|
+
getTextResult,
|
|
39
|
+
summarizeShellCommand,
|
|
40
|
+
} from "./compact-tools-invocation.ts";
|
|
41
|
+
import {
|
|
42
|
+
CachedContainer,
|
|
43
|
+
limitComponentLines,
|
|
44
|
+
prefixedText,
|
|
45
|
+
renderArguments,
|
|
46
|
+
renderEditChanges,
|
|
47
|
+
renderOutput,
|
|
48
|
+
renderToolCall,
|
|
49
|
+
styleMultiline,
|
|
50
|
+
} from "./compact-tools-layout.ts";
|
|
51
|
+
import { ProgressController } from "./compact-tools-progress.ts";
|
|
52
|
+
import { ToolRuntime } from "./compact-tools-runtime.ts";
|
|
53
|
+
import { ThinkingCycleController } from "./compact-tools-thinking.ts";
|
|
54
|
+
import type {
|
|
55
|
+
BuiltInDefinition,
|
|
56
|
+
CompactToolName,
|
|
57
|
+
RenderContext,
|
|
58
|
+
RowState,
|
|
59
|
+
ShellToolName,
|
|
60
|
+
ToolArgs,
|
|
61
|
+
} from "./compact-tools-types.ts";
|
|
62
|
+
|
|
63
|
+
const runtime = new ToolRuntime();
|
|
64
|
+
const registeredTools = new Set<CompactToolName>();
|
|
65
|
+
let registeredConfiguration: string | undefined;
|
|
66
|
+
|
|
67
|
+
function callStatus(ctx: RenderContext, state: RowState): RowStatus {
|
|
68
|
+
// write/edit arguments can stream for much longer than their eventual filesystem
|
|
69
|
+
// operation. Treat that active tool-call phase as running so every built-in row
|
|
70
|
+
// animates consistently instead of waiting for execute() to begin.
|
|
71
|
+
const active = ctx.executionStarted || !ctx.argsComplete;
|
|
72
|
+
return classifyCallStatus(ctx.isError, active, state.endedAt !== undefined);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function renderIndicator(theme: Theme, ctx: RenderContext, status: RowStatus): string {
|
|
76
|
+
const frame = runtime.syncIndicator(ctx.toolCallId, status === "running", () => ctx.invalidate());
|
|
77
|
+
const glyph = indicatorGlyph(status, frame);
|
|
78
|
+
if (status === "running") {
|
|
79
|
+
const bright = themeColorRgb(theme, "borderAccent");
|
|
80
|
+
const dim = themeColorRgb(theme, "borderMuted");
|
|
81
|
+
if (bright && dim) {
|
|
82
|
+
return colorizeRgb(theme, interpolateRgb(dim, bright, indicatorStrength(status, frame)), glyph);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return theme.fg(indicatorTone(status, frame), glyph);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function formatDuration(state: RowState): string | undefined {
|
|
89
|
+
if (state.startedAt === undefined) return undefined;
|
|
90
|
+
return formatDurationMs((state.endedAt ?? Date.now()) - state.startedAt);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function canReuseResult<TDetails, TArgs>(
|
|
94
|
+
state: RowState,
|
|
95
|
+
result: AgentToolResult<TDetails>,
|
|
96
|
+
options: ToolRenderResultOptions,
|
|
97
|
+
ctx: RenderContext<TArgs>,
|
|
98
|
+
): ctx is RenderContext<TArgs> & { lastComponent: Component } {
|
|
99
|
+
return ctx.lastComponent !== undefined
|
|
100
|
+
&& state.lastResultContent === result.content
|
|
101
|
+
&& state.lastResultDetails === result.details
|
|
102
|
+
&& state.lastResultPartial === options.isPartial
|
|
103
|
+
&& state.lastResultExpanded === state.expanded
|
|
104
|
+
&& state.lastResultPreview === state.preview
|
|
105
|
+
&& state.lastResultError === ctx.isError
|
|
106
|
+
&& state.lastResultConfigRevision === state.configRevision;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function rememberResult<TDetails, TArgs>(
|
|
110
|
+
state: RowState,
|
|
111
|
+
result: AgentToolResult<TDetails>,
|
|
112
|
+
options: ToolRenderResultOptions,
|
|
113
|
+
ctx: RenderContext<TArgs>,
|
|
114
|
+
): void {
|
|
115
|
+
state.lastResultContent = result.content;
|
|
116
|
+
state.lastResultDetails = result.details;
|
|
117
|
+
state.lastResultPartial = options.isPartial;
|
|
118
|
+
state.lastResultExpanded = state.expanded;
|
|
119
|
+
state.lastResultPreview = state.preview;
|
|
120
|
+
state.lastResultError = ctx.isError;
|
|
121
|
+
state.lastResultConfigRevision = state.configRevision;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function renderControls(
|
|
125
|
+
theme: Theme,
|
|
126
|
+
state: RowState,
|
|
127
|
+
running: boolean,
|
|
128
|
+
isError: boolean,
|
|
129
|
+
lineSummary?: string,
|
|
130
|
+
): Component {
|
|
131
|
+
const duration = running ? undefined : formatDuration(state);
|
|
132
|
+
const status = running
|
|
133
|
+
? (duration ?? "Running")
|
|
134
|
+
: `${isError ? "Failed" : "Done"}${duration ? ` in ${duration}` : ""}`;
|
|
135
|
+
let details = theme.fg("borderAccent", status);
|
|
136
|
+
if (lineSummary && !running) details += theme.fg("borderAccent", ` (${lineSummary})`);
|
|
137
|
+
return prefixedText(details, theme.fg("border", " └─ "), " ");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function renderCallTitle(name: string, theme: Theme, ctx: RenderContext, state: RowState): string {
|
|
141
|
+
return `${renderIndicator(theme, ctx, callStatus(ctx, state))} ${theme.fg("toolTitle", theme.bold(name))}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function renderFileCall(
|
|
145
|
+
definition: BuiltInDefinition,
|
|
146
|
+
args: ToolArgs,
|
|
147
|
+
theme: Theme,
|
|
148
|
+
ctx: RenderContext,
|
|
149
|
+
): Container {
|
|
150
|
+
const name = definition.name as CompactToolName;
|
|
151
|
+
const state = runtime.syncRow(ctx, ctx.state.endedAt === undefined);
|
|
152
|
+
runtime.syncExpansion(state, ctx.expanded, name);
|
|
153
|
+
const callDetails = getCallDetails(name, args);
|
|
154
|
+
const details = callDetails
|
|
155
|
+
? styleMultiline(callDetails, (line) => theme.fg("toolOutput", line))
|
|
156
|
+
: undefined;
|
|
157
|
+
const container = new CachedContainer();
|
|
158
|
+
container.addChild(renderToolCall(renderCallTitle(name, theme, ctx, state), details, theme));
|
|
159
|
+
const arguments_ = getArgumentDetails(name, args);
|
|
160
|
+
if (Object.keys(arguments_).length > 0) container.addChild(renderArguments(arguments_, theme));
|
|
161
|
+
return container;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function appendFileResult(
|
|
165
|
+
container: Container,
|
|
166
|
+
definition: BuiltInDefinition,
|
|
167
|
+
state: RowState,
|
|
168
|
+
output: string,
|
|
169
|
+
editChanges: string,
|
|
170
|
+
theme: Theme,
|
|
171
|
+
isError: boolean,
|
|
172
|
+
): void {
|
|
173
|
+
if (!state.expanded && !state.preview) return;
|
|
174
|
+
const component = definition.name === "edit" && editChanges
|
|
175
|
+
? renderEditChanges(editChanges, theme)
|
|
176
|
+
: renderOutput(output, theme, isError);
|
|
177
|
+
if (!component) return;
|
|
178
|
+
container.addChild(state.expanded ? component : limitComponentLines(component, runtime.config.previewLines, theme));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function renderFileResult(
|
|
182
|
+
definition: BuiltInDefinition,
|
|
183
|
+
result: AgentToolResult<unknown>,
|
|
184
|
+
options: ToolRenderResultOptions,
|
|
185
|
+
theme: Theme,
|
|
186
|
+
ctx: RenderContext,
|
|
187
|
+
): Component {
|
|
188
|
+
const name = definition.name as CompactToolName;
|
|
189
|
+
const state = runtime.syncRow(ctx, options.isPartial, !options.isPartial);
|
|
190
|
+
runtime.syncExpansion(state, ctx.expanded, name);
|
|
191
|
+
if (canReuseResult(state, result, options, ctx)) return ctx.lastComponent;
|
|
192
|
+
const output = getFileOutput(name, ctx.args, result, ctx.isError);
|
|
193
|
+
const editChanges = name === "edit" ? getEditChanges(result) : "";
|
|
194
|
+
runtime.setResultAvailable(state, name, editChanges.length > 0 || output.length > 0);
|
|
195
|
+
const container = new CachedContainer();
|
|
196
|
+
appendFileResult(container, definition, state, output, editChanges, theme, ctx.isError);
|
|
197
|
+
if (!options.isPartial && !state.resultLineSummaryComputed) {
|
|
198
|
+
state.resultLineSummary = formatResultLineSummary(name, ctx.args, result, output);
|
|
199
|
+
state.resultLineSummaryComputed = true;
|
|
200
|
+
}
|
|
201
|
+
container.addChild(renderControls(theme, state, options.isPartial, ctx.isError, state.resultLineSummary));
|
|
202
|
+
rememberResult(state, result, options, ctx);
|
|
203
|
+
return container;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function renderShellCall(
|
|
207
|
+
name: ShellToolName,
|
|
208
|
+
args: BashToolInput,
|
|
209
|
+
theme: Theme,
|
|
210
|
+
ctx: RenderContext<BashToolInput>,
|
|
211
|
+
): Component {
|
|
212
|
+
const state = runtime.syncRow(ctx, ctx.state.endedAt === undefined);
|
|
213
|
+
runtime.syncExpansion(state, ctx.expanded, name);
|
|
214
|
+
const command = normalizeLineEndings(args.command ?? "");
|
|
215
|
+
const details = state.expanded
|
|
216
|
+
? styleMultiline(command || "…", (line) => theme.fg("toolOutput", line))
|
|
217
|
+
: theme.fg("toolOutput", summarizeShellCommand(name, command));
|
|
218
|
+
return renderToolCall(renderCallTitle(name, theme, ctx, state), details, theme);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function renderShellResult(
|
|
222
|
+
name: ShellToolName,
|
|
223
|
+
result: AgentToolResult<BashToolDetails | undefined>,
|
|
224
|
+
options: ToolRenderResultOptions,
|
|
225
|
+
theme: Theme,
|
|
226
|
+
ctx: RenderContext<BashToolInput>,
|
|
227
|
+
): Component {
|
|
228
|
+
const state = runtime.syncRow(ctx, options.isPartial, !options.isPartial);
|
|
229
|
+
runtime.syncExpansion(state, ctx.expanded, name);
|
|
230
|
+
if (canReuseResult(state, result, options, ctx)) return ctx.lastComponent;
|
|
231
|
+
const output = getTextResult(result);
|
|
232
|
+
runtime.setResultAvailable(state, name, output.length > 0);
|
|
233
|
+
if (!options.isPartial && !state.resultLineSummaryComputed) {
|
|
234
|
+
state.resultLineSummary = formatResultLineSummary(name, ctx.args, result, output);
|
|
235
|
+
state.resultLineSummaryComputed = true;
|
|
236
|
+
}
|
|
237
|
+
if (!state.expanded && !state.preview) {
|
|
238
|
+
const controls = renderControls(theme, state, options.isPartial, ctx.isError, state.resultLineSummary);
|
|
239
|
+
rememberResult(state, result, options, ctx);
|
|
240
|
+
return controls;
|
|
241
|
+
}
|
|
242
|
+
const container = new CachedContainer();
|
|
243
|
+
const component = renderOutput(output, theme, ctx.isError);
|
|
244
|
+
if (component) {
|
|
245
|
+
container.addChild(state.expanded ? component : limitComponentLines(component, runtime.config.previewLines, theme));
|
|
246
|
+
}
|
|
247
|
+
container.addChild(renderControls(theme, state, options.isPartial, ctx.isError, state.resultLineSummary));
|
|
248
|
+
rememberResult(state, result, options, ctx);
|
|
249
|
+
return container;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const toolFactories: Record<CompactToolName, (cwd: string) => BuiltInDefinition> = {
|
|
253
|
+
read: createReadToolDefinition,
|
|
254
|
+
write: createWriteToolDefinition,
|
|
255
|
+
edit: createEditToolDefinition,
|
|
256
|
+
bash: createBashToolDefinition,
|
|
257
|
+
powershell: createPowerShellToolDefinition,
|
|
258
|
+
grep: createGrepToolDefinition,
|
|
259
|
+
find: createFindToolDefinition,
|
|
260
|
+
ls: createLsToolDefinition,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
function createBuiltInDefinition(name: CompactToolName, cwd: string): BuiltInDefinition {
|
|
264
|
+
return toolFactories[name](cwd);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function registerFileTool(pi: ExtensionAPI, definition: BuiltInDefinition): void {
|
|
268
|
+
pi.registerTool({
|
|
269
|
+
...definition,
|
|
270
|
+
execute: runtime.createTimedExecute(definition),
|
|
271
|
+
renderShell: "self",
|
|
272
|
+
renderCall: (args: ToolArgs, theme: Theme, ctx: RenderContext) => renderFileCall(definition, args, theme, ctx),
|
|
273
|
+
renderResult: (
|
|
274
|
+
result: AgentToolResult<unknown>,
|
|
275
|
+
options: ToolRenderResultOptions,
|
|
276
|
+
theme: Theme,
|
|
277
|
+
ctx: RenderContext,
|
|
278
|
+
) => renderFileResult(definition, result, options, theme, ctx),
|
|
279
|
+
} as ToolDefinition<any, any, RowState>);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function registerShellTool(pi: ExtensionAPI, definition: BuiltInDefinition): void {
|
|
283
|
+
const name = definition.name as ShellToolName;
|
|
284
|
+
pi.registerTool({
|
|
285
|
+
...definition,
|
|
286
|
+
execute: runtime.createTimedExecute(definition),
|
|
287
|
+
renderShell: "self",
|
|
288
|
+
renderCall: (args: BashToolInput, theme: Theme, ctx: RenderContext<BashToolInput>) =>
|
|
289
|
+
renderShellCall(name, args, theme, ctx),
|
|
290
|
+
renderResult: (
|
|
291
|
+
result: AgentToolResult<BashToolDetails | undefined>,
|
|
292
|
+
options: ToolRenderResultOptions,
|
|
293
|
+
theme: Theme,
|
|
294
|
+
ctx: RenderContext<BashToolInput>,
|
|
295
|
+
) => renderShellResult(name, result, options, theme, ctx),
|
|
296
|
+
} as ToolDefinition<any, BashToolDetails | undefined, RowState>);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function registerTools(pi: ExtensionAPI, cwd: string): void {
|
|
300
|
+
const enabledTools = new Set(runtime.config.tools);
|
|
301
|
+
for (const name of registeredTools) {
|
|
302
|
+
// Pi exposes no unregister API, so restoring a disabled tool means re-registering
|
|
303
|
+
// the built-in definition to release this extension's renderers.
|
|
304
|
+
if (!enabledTools.has(name)) pi.registerTool(createBuiltInDefinition(name, cwd));
|
|
305
|
+
}
|
|
306
|
+
registeredTools.clear();
|
|
307
|
+
for (const name of runtime.config.tools) {
|
|
308
|
+
const definition = createBuiltInDefinition(name, cwd);
|
|
309
|
+
if (name === "bash" || name === "powershell") registerShellTool(pi, definition);
|
|
310
|
+
else registerFileTool(pi, definition);
|
|
311
|
+
registeredTools.add(name);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function configure(pi: ExtensionAPI, cwd?: string, projectTrusted = false): void {
|
|
316
|
+
const resolvedCwd = cwd ?? process.cwd();
|
|
317
|
+
const config = loadConfig(resolvedCwd, projectTrusted);
|
|
318
|
+
const signature = JSON.stringify([resolvedCwd, config]);
|
|
319
|
+
if (signature === registeredConfiguration) return;
|
|
320
|
+
runtime.configure(config);
|
|
321
|
+
registerTools(pi, resolvedCwd);
|
|
322
|
+
registeredConfiguration = signature;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export default function compactTools(pi: ExtensionAPI): void {
|
|
326
|
+
const thinkingCycle = new ThinkingCycleController(pi);
|
|
327
|
+
const progress = new ProgressController(pi);
|
|
328
|
+
// Register once while the extension runtime is being built. In particular, this
|
|
329
|
+
// makes the overrides available before Pi restores the active tool set on /reload.
|
|
330
|
+
configure(pi, process.cwd());
|
|
331
|
+
pi.on("session_start", (event, ctx) => {
|
|
332
|
+
if (event.reason !== "reload") runtime.clearTimings();
|
|
333
|
+
configure(pi, ctx.cwd, ctx.isProjectTrusted());
|
|
334
|
+
if (ctx.mode === "tui") {
|
|
335
|
+
progress.bind(ctx);
|
|
336
|
+
thinkingCycle.bind(ctx);
|
|
337
|
+
} else {
|
|
338
|
+
progress.dispose();
|
|
339
|
+
thinkingCycle.dispose();
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
pi.on("session_shutdown", (event) => {
|
|
343
|
+
thinkingCycle.dispose();
|
|
344
|
+
progress.dispose();
|
|
345
|
+
runtime.reset(event.reason !== "reload");
|
|
346
|
+
// Some Pi hosts rebuild the active tool registry during reload while keeping
|
|
347
|
+
// this module instance. Force the next session to restore our renderers.
|
|
348
|
+
if (event.reason === "reload") registeredConfiguration = undefined;
|
|
349
|
+
});
|
|
350
|
+
}
|