@aliou/pi-dev-kit 0.6.4 → 0.6.5
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/package.json +11 -7
- package/src/skills/pi-extension/SKILL.md +18 -12
- package/src/skills/pi-extension/references/additional-apis.md +42 -3
- package/src/skills/pi-extension/references/hooks.md +4 -4
- package/src/skills/pi-extension/references/messages.md +27 -10
- package/src/skills/pi-extension/references/structure.md +92 -38
- package/src/skills/pi-extension/references/tools.md +46 -25
- package/src/tools/changelog-tool.ts +225 -230
- package/src/tools/docs-tool.ts +125 -128
- package/src/tools/package-manager-tool.ts +146 -145
- package/src/tools/version-tool.ts +45 -49
|
@@ -16,15 +16,27 @@ import type {
|
|
|
16
16
|
Theme,
|
|
17
17
|
ToolRenderResultOptions,
|
|
18
18
|
} from "@mariozechner/pi-coding-agent";
|
|
19
|
-
import { getMarkdownTheme, keyHint, truncateHead, formatSize } from "@mariozechner/pi-coding-agent";
|
|
19
|
+
import { defineTool, getMarkdownTheme, keyHint, truncateHead, formatSize } from "@mariozechner/pi-coding-agent";
|
|
20
20
|
import { Container, Markdown, Text } from "@mariozechner/pi-tui";
|
|
21
|
-
import { type Static, Type } from "
|
|
21
|
+
import { type Static, Type } from "typebox";
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
## Registration
|
|
25
25
|
|
|
26
|
+
Tool files are extension entry points. Put the tool registration in `src/tools/index.ts`, export a default function, and list that file in `package.json` `pi.extensions`.
|
|
27
|
+
|
|
26
28
|
```typescript
|
|
27
|
-
const
|
|
29
|
+
const parameters = Type.Object({
|
|
30
|
+
query: Type.String({ description: "Search query" }),
|
|
31
|
+
limit: Type.Optional(Type.Number({ description: "Max results", default: 10 })),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
type MyToolParams = Static<typeof parameters>;
|
|
35
|
+
interface MyToolDetails {
|
|
36
|
+
results: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const myTool = defineTool({
|
|
28
40
|
name: "my_tool",
|
|
29
41
|
label: "My Tool", // Required: human-readable name for UI
|
|
30
42
|
description: "What this tool does. The LLM reads this to decide when to call it.",
|
|
@@ -33,17 +45,14 @@ const myTool = {
|
|
|
33
45
|
"Use my_tool when the user asks about search.",
|
|
34
46
|
"Prefer specific queries over broad ones when calling my_tool.",
|
|
35
47
|
],
|
|
36
|
-
parameters
|
|
37
|
-
query: Type.String({ description: "Search query" }),
|
|
38
|
-
limit: Type.Optional(Type.Number({ description: "Max results", default: 10 })),
|
|
39
|
-
}),
|
|
48
|
+
parameters,
|
|
40
49
|
|
|
41
50
|
async execute(
|
|
42
|
-
toolCallId
|
|
43
|
-
params
|
|
44
|
-
signal
|
|
45
|
-
onUpdate
|
|
46
|
-
ctx
|
|
51
|
+
toolCallId,
|
|
52
|
+
params,
|
|
53
|
+
signal,
|
|
54
|
+
onUpdate,
|
|
55
|
+
ctx,
|
|
47
56
|
): Promise<AgentToolResult<MyToolDetails>> {
|
|
48
57
|
const results = await doSomething(params.query, params.limit);
|
|
49
58
|
return {
|
|
@@ -51,15 +60,13 @@ const myTool = {
|
|
|
51
60
|
details: { results },
|
|
52
61
|
};
|
|
53
62
|
},
|
|
54
|
-
};
|
|
63
|
+
});
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
65
|
+
export default async function (pi: ExtensionAPI) {
|
|
66
|
+
await configLoader.load();
|
|
67
|
+
const config = configLoader.getConfig();
|
|
68
|
+
if (!config.enabled) return;
|
|
61
69
|
|
|
62
|
-
export default function (pi: ExtensionAPI) {
|
|
63
70
|
pi.registerTool(myTool);
|
|
64
71
|
}
|
|
65
72
|
```
|
|
@@ -78,7 +85,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
78
85
|
| `renderCall` | `function` | No | Custom call rendering |
|
|
79
86
|
| `renderResult` | `function` | No | Custom result rendering |
|
|
80
87
|
|
|
81
|
-
## Typed Param Alias
|
|
88
|
+
## `defineTool()` and Typed Param Alias
|
|
89
|
+
|
|
90
|
+
Use `defineTool()` for standalone tool definitions. It infers parameter types from the `parameters` field and preserves them for `execute`, `renderCall`, and `renderResult` so you do not need casts or explicit generic arguments at registration/rendering boundaries.
|
|
91
|
+
|
|
92
|
+
Inside the `defineTool({...})` object, do not annotate callback parameter types unless TypeScript actually needs help. The contextual type from Pi already provides `toolCallId`, `params`, `signal`, `onUpdate`, `ctx`, `options`, and `theme`. If you want `renderResult` to see a specific `details` shape, annotate the `execute` return type as `Promise<AgentToolResult<MyToolDetails>>`; that is the useful annotation.
|
|
82
93
|
|
|
83
94
|
Define a type alias at the top of your file instead of repeating `Static<typeof parameters>`:
|
|
84
95
|
|
|
@@ -89,21 +100,29 @@ const parameters = Type.Object({
|
|
|
89
100
|
});
|
|
90
101
|
|
|
91
102
|
type MyToolParams = Static<typeof parameters>;
|
|
92
|
-
// Use MyToolParams
|
|
103
|
+
// Use MyToolParams in helper functions and exported action APIs. Inside defineTool callbacks, prefer inference.
|
|
93
104
|
```
|
|
94
105
|
|
|
95
106
|
## Execute Signature
|
|
96
107
|
|
|
108
|
+
```typescript
|
|
109
|
+
execute(toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<TDetails>>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Pi's `ToolDefinition` type is:
|
|
113
|
+
|
|
97
114
|
```typescript
|
|
98
115
|
execute(
|
|
99
116
|
toolCallId: string,
|
|
100
|
-
params: Static<TParams>,
|
|
117
|
+
params: Static<TParams>,
|
|
101
118
|
signal: AbortSignal | undefined,
|
|
102
119
|
onUpdate: AgentToolUpdateCallback<TDetails> | undefined,
|
|
103
120
|
ctx: ExtensionContext,
|
|
104
121
|
): Promise<AgentToolResult<TDetails>>
|
|
105
122
|
```
|
|
106
123
|
|
|
124
|
+
You usually do not need to write those parameter types yourself inside `defineTool()`.
|
|
125
|
+
|
|
107
126
|
**Parameter order matters.** The signal comes before onUpdate.
|
|
108
127
|
|
|
109
128
|
Always use optional chaining when calling `onUpdate`:
|
|
@@ -126,11 +145,13 @@ Prompt metadata is not inherited automatically when you override a built-in tool
|
|
|
126
145
|
return {
|
|
127
146
|
content: (TextContent | ImageContent)[], // Content blocks sent to the LLM
|
|
128
147
|
details?: TDetails, // Arbitrary data available in the renderer
|
|
148
|
+
terminate?: boolean, // Optional: skip follow-up LLM call when all finalized results in the batch terminate
|
|
129
149
|
};
|
|
130
150
|
```
|
|
131
151
|
|
|
132
152
|
- `content` is what the LLM sees. Each block is `{ type: "text", text: "..." }` or an image. Keep it structured and concise.
|
|
133
153
|
- `details` is what the renderer sees. Put rich data here for custom display.
|
|
154
|
+
- `terminate: true` is for final/structured-output tools that should end the turn without an automatic follow-up LLM call. It only applies when every finalized tool result in the current batch also returns `terminate: true`.
|
|
134
155
|
|
|
135
156
|
Common pattern:
|
|
136
157
|
|
|
@@ -221,10 +242,10 @@ Both approaches work. Approach 1 is more common in published extensions. Approac
|
|
|
221
242
|
|
|
222
243
|
## Parameters Schema
|
|
223
244
|
|
|
224
|
-
Use TypeBox (`Type.*`) for parameter schemas. The LLM sees the schema to know what arguments to provide.
|
|
245
|
+
Use TypeBox 1.x (`Type.*`) from `typebox` for parameter schemas. The LLM sees the schema to know what arguments to provide. Do not import from `@sinclair/typebox` in new extensions.
|
|
225
246
|
|
|
226
247
|
```typescript
|
|
227
|
-
import { Type } from "
|
|
248
|
+
import { Type } from "typebox";
|
|
228
249
|
|
|
229
250
|
// Required string
|
|
230
251
|
Type.String({ description: "File path to read" })
|
|
@@ -838,7 +859,7 @@ import type {
|
|
|
838
859
|
} from "@mariozechner/pi-coding-agent";
|
|
839
860
|
import { keyHint, formatSize } from "@mariozechner/pi-coding-agent";
|
|
840
861
|
import { Container, Text } from "@mariozechner/pi-tui";
|
|
841
|
-
import { type Static, Type } from "
|
|
862
|
+
import { type Static, Type } from "typebox";
|
|
842
863
|
|
|
843
864
|
// Schema
|
|
844
865
|
const parameters = Type.Object({
|
|
@@ -4,13 +4,11 @@ import { ToolBody, ToolCallHeader, ToolFooter } from "@aliou/pi-utils-ui";
|
|
|
4
4
|
import type {
|
|
5
5
|
AgentToolResult,
|
|
6
6
|
ExtensionAPI,
|
|
7
|
-
ExtensionContext,
|
|
8
7
|
Theme,
|
|
9
|
-
ToolRenderResultOptions,
|
|
10
8
|
} from "@mariozechner/pi-coding-agent";
|
|
11
|
-
import { keyHint, VERSION } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import { defineTool, keyHint, VERSION } from "@mariozechner/pi-coding-agent";
|
|
12
10
|
import { Text } from "@mariozechner/pi-tui";
|
|
13
|
-
import { type Static, Type } from "
|
|
11
|
+
import { type Static, Type } from "typebox";
|
|
14
12
|
import { findPiInstallation } from "./utils";
|
|
15
13
|
|
|
16
14
|
const GITHUB_RAW_CHANGELOG_URL =
|
|
@@ -32,7 +30,7 @@ const ChangelogParamsSchema = Type.Object({
|
|
|
32
30
|
type ChangelogParams = Static<typeof ChangelogParamsSchema>;
|
|
33
31
|
|
|
34
32
|
const ChangelogVersionsParamsSchema = Type.Object({});
|
|
35
|
-
type ChangelogVersionsParams =
|
|
33
|
+
type ChangelogVersionsParams = Static<typeof ChangelogVersionsParamsSchema>;
|
|
36
34
|
|
|
37
35
|
// ---------------------------------------------------------------------------
|
|
38
36
|
// Types
|
|
@@ -200,6 +198,23 @@ const COLLAPSED_LINES = 8;
|
|
|
200
198
|
// Render helpers
|
|
201
199
|
// ---------------------------------------------------------------------------
|
|
202
200
|
|
|
201
|
+
function renderChangelogCall(args: ChangelogParams, theme: Theme) {
|
|
202
|
+
return new ToolCallHeader(
|
|
203
|
+
{
|
|
204
|
+
toolName: "Pi Changelog",
|
|
205
|
+
mainArg: args.version ? `v${args.version}` : "latest",
|
|
206
|
+
},
|
|
207
|
+
theme,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function renderChangelogVersionsCall(
|
|
212
|
+
_args: ChangelogVersionsParams,
|
|
213
|
+
theme: Theme,
|
|
214
|
+
) {
|
|
215
|
+
return new ToolCallHeader({ toolName: "Pi Changelog Versions" }, theme);
|
|
216
|
+
}
|
|
217
|
+
|
|
203
218
|
function renderChangelogContent(
|
|
204
219
|
content: string,
|
|
205
220
|
theme: Theme,
|
|
@@ -235,250 +250,230 @@ function renderChangelogContent(
|
|
|
235
250
|
// pi_changelog
|
|
236
251
|
// ---------------------------------------------------------------------------
|
|
237
252
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
promptSnippet: `pi_changelog version="1.2.3" // Get changelog for specific version
|
|
253
|
+
const changelogTool = defineTool({
|
|
254
|
+
name: "pi_changelog",
|
|
255
|
+
label: "Pi Changelog",
|
|
256
|
+
description:
|
|
257
|
+
"Get changelog entry for a Pi version. Returns latest by default. Use pi_changelog_versions to list all available versions.",
|
|
258
|
+
promptSnippet: `pi_changelog version="1.2.3" // Get changelog for specific version
|
|
245
259
|
pi_changelog // Get latest changelog`,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return {
|
|
268
|
-
content: [{ type: "text", text: message }],
|
|
269
|
-
details: {
|
|
270
|
-
changelog,
|
|
271
|
-
source: "github",
|
|
272
|
-
},
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Local
|
|
277
|
-
const local = readLocalChangelog();
|
|
278
|
-
const changelog = findChangelogEntry(local.content, params.version);
|
|
279
|
-
|
|
280
|
-
const message = `Changelog for ${changelog.version}\n\n## ${changelog.version}\n\n${changelog.content}`;
|
|
260
|
+
promptGuidelines: [
|
|
261
|
+
"Use pi_changelog to check what's new in a Pi version",
|
|
262
|
+
"Use pi_changelog_versions first to list available versions",
|
|
263
|
+
"Leave version empty for pi_changelog to get the latest changelog",
|
|
264
|
+
],
|
|
265
|
+
|
|
266
|
+
parameters: ChangelogParamsSchema,
|
|
267
|
+
|
|
268
|
+
async execute(
|
|
269
|
+
_toolCallId,
|
|
270
|
+
params,
|
|
271
|
+
_signal,
|
|
272
|
+
_onUpdate,
|
|
273
|
+
_ctx,
|
|
274
|
+
): Promise<AgentToolResult<ChangelogDetails>> {
|
|
275
|
+
// Newer than installed -> fetch from GitHub
|
|
276
|
+
if (params.version && isNewerThanInstalled(params.version)) {
|
|
277
|
+
const githubContent = await fetchGithubChangelog();
|
|
278
|
+
const changelog = findChangelogEntry(githubContent, params.version);
|
|
279
|
+
|
|
280
|
+
const message = `Changelog for ${changelog.version} (from GitHub)\n\n## ${changelog.version}\n\n${changelog.content}`;
|
|
281
281
|
return {
|
|
282
282
|
content: [{ type: "text", text: message }],
|
|
283
283
|
details: {
|
|
284
284
|
changelog,
|
|
285
|
-
source: "
|
|
285
|
+
source: "github",
|
|
286
286
|
},
|
|
287
287
|
};
|
|
288
|
-
}
|
|
288
|
+
}
|
|
289
289
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
290
|
+
// Local
|
|
291
|
+
const local = readLocalChangelog();
|
|
292
|
+
const changelog = findChangelogEntry(local.content, params.version);
|
|
293
|
+
|
|
294
|
+
const message = `Changelog for ${changelog.version}\n\n## ${changelog.version}\n\n${changelog.content}`;
|
|
295
|
+
return {
|
|
296
|
+
content: [{ type: "text", text: message }],
|
|
297
|
+
details: {
|
|
298
|
+
changelog,
|
|
299
|
+
source: "local",
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
renderCall(args, theme) {
|
|
305
|
+
return renderChangelogCall(args, theme);
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
renderResult(result, options, theme) {
|
|
309
|
+
const { details } = result;
|
|
310
|
+
|
|
311
|
+
// Check for missing expected fields to detect errors
|
|
312
|
+
if (!details?.changelog) {
|
|
313
|
+
const text = result.content[0];
|
|
314
|
+
return new Text(
|
|
315
|
+
text?.type === "text" && text.text ? text.text : "No result",
|
|
316
|
+
0,
|
|
317
|
+
0,
|
|
297
318
|
);
|
|
298
|
-
}
|
|
319
|
+
}
|
|
299
320
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
theme: Theme,
|
|
304
|
-
) {
|
|
305
|
-
const { details } = result;
|
|
306
|
-
|
|
307
|
-
// Check for missing expected fields to detect errors
|
|
308
|
-
if (!details?.changelog) {
|
|
309
|
-
const text = result.content[0];
|
|
310
|
-
return new Text(
|
|
311
|
-
text?.type === "text" && text.text ? text.text : "No result",
|
|
312
|
-
0,
|
|
313
|
-
0,
|
|
314
|
-
);
|
|
315
|
-
}
|
|
321
|
+
const fields: Array<
|
|
322
|
+
{ label: string; value: string; showCollapsed?: boolean } | Text
|
|
323
|
+
> = [];
|
|
316
324
|
|
|
317
|
-
|
|
318
|
-
{ label: string; value: string; showCollapsed?: boolean } | Text
|
|
319
|
-
> = [];
|
|
320
|
-
|
|
321
|
-
const lines: string[] = [];
|
|
322
|
-
|
|
323
|
-
if (options.expanded) {
|
|
324
|
-
// Expanded view: show full changelog content
|
|
325
|
-
lines.push(
|
|
326
|
-
theme.fg(
|
|
327
|
-
"accent",
|
|
328
|
-
theme.bold(`Version: ${details.changelog.version}`),
|
|
329
|
-
),
|
|
330
|
-
"",
|
|
331
|
-
);
|
|
332
|
-
lines.push(...renderChangelogContent(details.changelog.content, theme));
|
|
333
|
-
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
334
|
-
} else {
|
|
335
|
-
// Collapsed view: show version + first few lines of changelog + expand hint
|
|
336
|
-
lines.push(
|
|
337
|
-
theme.fg(
|
|
338
|
-
"accent",
|
|
339
|
-
theme.bold(`Version: ${details.changelog.version}`),
|
|
340
|
-
),
|
|
341
|
-
"",
|
|
342
|
-
);
|
|
343
|
-
lines.push(
|
|
344
|
-
...renderChangelogContent(
|
|
345
|
-
details.changelog.content,
|
|
346
|
-
theme,
|
|
347
|
-
COLLAPSED_LINES,
|
|
348
|
-
),
|
|
349
|
-
);
|
|
350
|
-
lines.push(
|
|
351
|
-
"",
|
|
352
|
-
theme.fg("muted", `${keyHint("app.tools.expand", "to expand")}`),
|
|
353
|
-
);
|
|
354
|
-
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
355
|
-
}
|
|
325
|
+
const lines: string[] = [];
|
|
356
326
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
value: details.source ?? "local",
|
|
363
|
-
tone: "accent",
|
|
364
|
-
},
|
|
365
|
-
],
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
return new ToolBody(
|
|
369
|
-
{
|
|
370
|
-
fields,
|
|
371
|
-
footer,
|
|
372
|
-
},
|
|
373
|
-
options,
|
|
374
|
-
theme,
|
|
327
|
+
if (options.expanded) {
|
|
328
|
+
// Expanded view: show full changelog content
|
|
329
|
+
lines.push(
|
|
330
|
+
theme.fg("accent", theme.bold(`Version: ${details.changelog.version}`)),
|
|
331
|
+
"",
|
|
375
332
|
);
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
_signal: AbortSignal | undefined,
|
|
398
|
-
_onUpdate: unknown,
|
|
399
|
-
_ctx: ExtensionContext,
|
|
400
|
-
): Promise<AgentToolResult<ChangelogVersionsDetails>> {
|
|
401
|
-
const local = readLocalChangelog();
|
|
402
|
-
const { entries } = parseChangelogEntries(local.content);
|
|
403
|
-
|
|
404
|
-
if (entries.length === 0) {
|
|
405
|
-
throw new Error("No version entries found in changelog");
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const versions = entries.map((e) => e.version);
|
|
409
|
-
const message = `${versions.length} versions available:\n${versions.join(", ")}`;
|
|
333
|
+
lines.push(...renderChangelogContent(details.changelog.content, theme));
|
|
334
|
+
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
335
|
+
} else {
|
|
336
|
+
// Collapsed view: show version + first few lines of changelog + expand hint
|
|
337
|
+
lines.push(
|
|
338
|
+
theme.fg("accent", theme.bold(`Version: ${details.changelog.version}`)),
|
|
339
|
+
"",
|
|
340
|
+
);
|
|
341
|
+
lines.push(
|
|
342
|
+
...renderChangelogContent(
|
|
343
|
+
details.changelog.content,
|
|
344
|
+
theme,
|
|
345
|
+
COLLAPSED_LINES,
|
|
346
|
+
),
|
|
347
|
+
);
|
|
348
|
+
lines.push(
|
|
349
|
+
"",
|
|
350
|
+
theme.fg("muted", `${keyHint("app.tools.expand", "to expand")}`),
|
|
351
|
+
);
|
|
352
|
+
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
353
|
+
}
|
|
410
354
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
355
|
+
// Footer: show source tag only
|
|
356
|
+
const footer = new ToolFooter(theme, {
|
|
357
|
+
items: [
|
|
358
|
+
{
|
|
359
|
+
label: "source",
|
|
360
|
+
value: details.source ?? "local",
|
|
361
|
+
tone: "accent",
|
|
416
362
|
},
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
// Check for missing expected fields to detect errors
|
|
432
|
-
if (!details?.versions) {
|
|
433
|
-
const text = result.content[0];
|
|
434
|
-
return new Text(
|
|
435
|
-
text?.type === "text" && text.text ? text.text : "No result",
|
|
436
|
-
0,
|
|
437
|
-
0,
|
|
438
|
-
);
|
|
439
|
-
}
|
|
363
|
+
],
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return new ToolBody(
|
|
367
|
+
{
|
|
368
|
+
fields,
|
|
369
|
+
footer,
|
|
370
|
+
},
|
|
371
|
+
options,
|
|
372
|
+
theme,
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
});
|
|
440
376
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
377
|
+
// -------------------------------------------------------------------------
|
|
378
|
+
// pi_changelog_versions
|
|
379
|
+
// -------------------------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
const changelogVersionsTool = defineTool({
|
|
382
|
+
name: "pi_changelog_versions",
|
|
383
|
+
label: "Pi Changelog Versions",
|
|
384
|
+
description: "List all available Pi changelog versions",
|
|
385
|
+
promptSnippet: `pi_changelog_versions // List all available versions`,
|
|
386
|
+
|
|
387
|
+
parameters: ChangelogVersionsParamsSchema,
|
|
388
|
+
|
|
389
|
+
async execute(
|
|
390
|
+
_toolCallId,
|
|
391
|
+
_params,
|
|
392
|
+
_signal,
|
|
393
|
+
_onUpdate,
|
|
394
|
+
_ctx,
|
|
395
|
+
): Promise<AgentToolResult<ChangelogVersionsDetails>> {
|
|
396
|
+
const local = readLocalChangelog();
|
|
397
|
+
const { entries } = parseChangelogEntries(local.content);
|
|
398
|
+
|
|
399
|
+
if (entries.length === 0) {
|
|
400
|
+
throw new Error("No version entries found in changelog");
|
|
401
|
+
}
|
|
444
402
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
403
|
+
const versions = entries.map((e) => e.version);
|
|
404
|
+
const message = `${versions.length} versions available:\n${versions.join(", ")}`;
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
content: [{ type: "text", text: message }],
|
|
408
|
+
details: {
|
|
409
|
+
versions,
|
|
410
|
+
source: "local",
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
renderCall(args, theme) {
|
|
416
|
+
return renderChangelogVersionsCall(args, theme);
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
renderResult(result, options, theme) {
|
|
420
|
+
const { details } = result;
|
|
421
|
+
|
|
422
|
+
// Check for missing expected fields to detect errors
|
|
423
|
+
if (!details?.versions) {
|
|
424
|
+
const text = result.content[0];
|
|
425
|
+
return new Text(
|
|
426
|
+
text?.type === "text" && text.text ? text.text : "No result",
|
|
427
|
+
0,
|
|
428
|
+
0,
|
|
452
429
|
);
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const fields: Array<
|
|
433
|
+
{ label: string; value: string; showCollapsed?: boolean } | Text
|
|
434
|
+
> = [];
|
|
435
|
+
|
|
436
|
+
const lines: string[] = [
|
|
437
|
+
theme.fg("accent", `${details.versions.length} versions available:`),
|
|
438
|
+
"",
|
|
439
|
+
];
|
|
440
|
+
const cols = 6;
|
|
441
|
+
const maxLen = Math.max(
|
|
442
|
+
...details.versions.map((version) => version.length),
|
|
443
|
+
);
|
|
444
|
+
const colWidth = maxLen + 2;
|
|
445
|
+
for (let i = 0; i < details.versions.length; i += cols) {
|
|
446
|
+
const row = details.versions
|
|
447
|
+
.slice(i, i + cols)
|
|
448
|
+
.map((version) => version.padEnd(colWidth))
|
|
449
|
+
.join("");
|
|
450
|
+
lines.push(theme.fg("dim", row));
|
|
451
|
+
}
|
|
452
|
+
fields.push(new Text(lines.join("\n"), 0, 0));
|
|
462
453
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
{
|
|
467
|
-
label: "count",
|
|
468
|
-
value: String(details.versions.length),
|
|
469
|
-
tone: "accent",
|
|
470
|
-
},
|
|
471
|
-
],
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
return new ToolBody(
|
|
454
|
+
// Footer: just show version count
|
|
455
|
+
const footer = new ToolFooter(theme, {
|
|
456
|
+
items: [
|
|
475
457
|
{
|
|
476
|
-
|
|
477
|
-
|
|
458
|
+
label: "count",
|
|
459
|
+
value: String(details.versions.length),
|
|
460
|
+
tone: "accent",
|
|
478
461
|
},
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
462
|
+
],
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
return new ToolBody(
|
|
466
|
+
{
|
|
467
|
+
fields,
|
|
468
|
+
footer,
|
|
469
|
+
},
|
|
470
|
+
options,
|
|
471
|
+
theme,
|
|
472
|
+
);
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
export function setupChangelogTool(pi: ExtensionAPI) {
|
|
477
|
+
pi.registerTool(changelogTool);
|
|
478
|
+
pi.registerTool(changelogVersionsTool);
|
|
484
479
|
}
|