@mariozechner/pi-coding-agent 0.45.2 → 0.45.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -1
- package/README.md +2 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +2 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +33 -6
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +6 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +7 -5
- package/dist/core/sdk.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/theme/light.json +9 -9
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +11 -4
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +47 -25
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/vips.d.ts +11 -0
- package/dist/utils/vips.d.ts.map +1 -0
- package/dist/utils/vips.js +35 -0
- package/dist/utils/vips.js.map +1 -0
- package/docs/extensions.md +18 -17
- package/docs/sdk.md +21 -48
- package/examples/README.md +5 -2
- package/examples/extensions/README.md +19 -2
- package/examples/extensions/plan-mode/README.md +65 -0
- package/examples/extensions/plan-mode/index.ts +340 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/question.ts +211 -13
- package/examples/extensions/questionnaire.ts +427 -0
- package/examples/extensions/summarize.ts +195 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/README.md +3 -4
- package/package.json +6 -6
- package/examples/extensions/plan-mode.ts +0 -548
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { complete, getModel } from "@mariozechner/pi-ai";
|
|
2
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import { DynamicBorder, getMarkdownTheme } from "@mariozechner/pi-coding-agent";
|
|
4
|
+
import { Container, Markdown, matchesKey, Text } from "@mariozechner/pi-tui";
|
|
5
|
+
|
|
6
|
+
type ContentBlock = {
|
|
7
|
+
type?: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
arguments?: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type SessionEntry = {
|
|
14
|
+
type: string;
|
|
15
|
+
message?: {
|
|
16
|
+
role?: string;
|
|
17
|
+
content?: unknown;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const extractTextParts = (content: unknown): string[] => {
|
|
22
|
+
if (typeof content === "string") {
|
|
23
|
+
return [content];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!Array.isArray(content)) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const textParts: string[] = [];
|
|
31
|
+
for (const part of content) {
|
|
32
|
+
if (!part || typeof part !== "object") {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const block = part as ContentBlock;
|
|
37
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
38
|
+
textParts.push(block.text);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return textParts;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const extractToolCallLines = (content: unknown): string[] => {
|
|
46
|
+
if (!Array.isArray(content)) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const toolCalls: string[] = [];
|
|
51
|
+
for (const part of content) {
|
|
52
|
+
if (!part || typeof part !== "object") {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const block = part as ContentBlock;
|
|
57
|
+
if (block.type !== "toolCall" || typeof block.name !== "string") {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const args = block.arguments ?? {};
|
|
62
|
+
toolCalls.push(`Tool ${block.name} was called with args ${JSON.stringify(args)}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return toolCalls;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const buildConversationText = (entries: SessionEntry[]): string => {
|
|
69
|
+
const sections: string[] = [];
|
|
70
|
+
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
if (entry.type !== "message" || !entry.message?.role) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const role = entry.message.role;
|
|
77
|
+
const isUser = role === "user";
|
|
78
|
+
const isAssistant = role === "assistant";
|
|
79
|
+
|
|
80
|
+
if (!isUser && !isAssistant) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const entryLines: string[] = [];
|
|
85
|
+
const textParts = extractTextParts(entry.message.content);
|
|
86
|
+
if (textParts.length > 0) {
|
|
87
|
+
const roleLabel = isUser ? "User" : "Assistant";
|
|
88
|
+
const messageText = textParts.join("\n").trim();
|
|
89
|
+
if (messageText.length > 0) {
|
|
90
|
+
entryLines.push(`${roleLabel}: ${messageText}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (isAssistant) {
|
|
95
|
+
entryLines.push(...extractToolCallLines(entry.message.content));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (entryLines.length > 0) {
|
|
99
|
+
sections.push(entryLines.join("\n"));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return sections.join("\n\n");
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const buildSummaryPrompt = (conversationText: string): string =>
|
|
107
|
+
[
|
|
108
|
+
"Summarize this conversation so I can resume it later.",
|
|
109
|
+
"Include goals, key decisions, progress, open questions, and next steps.",
|
|
110
|
+
"Keep it concise and structured with headings.",
|
|
111
|
+
"",
|
|
112
|
+
"<conversation>",
|
|
113
|
+
conversationText,
|
|
114
|
+
"</conversation>",
|
|
115
|
+
].join("\n");
|
|
116
|
+
|
|
117
|
+
const showSummaryUi = async (summary: string, ctx: ExtensionCommandContext) => {
|
|
118
|
+
if (!ctx.hasUI) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await ctx.ui.custom((_tui, theme, _kb, done) => {
|
|
123
|
+
const container = new Container();
|
|
124
|
+
const border = new DynamicBorder((s: string) => theme.fg("accent", s));
|
|
125
|
+
const mdTheme = getMarkdownTheme();
|
|
126
|
+
|
|
127
|
+
container.addChild(border);
|
|
128
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("Conversation Summary")), 1, 0));
|
|
129
|
+
container.addChild(new Markdown(summary, 1, 1, mdTheme));
|
|
130
|
+
container.addChild(new Text(theme.fg("dim", "Press Enter or Esc to close"), 1, 0));
|
|
131
|
+
container.addChild(border);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
render: (width: number) => container.render(width),
|
|
135
|
+
invalidate: () => container.invalidate(),
|
|
136
|
+
handleInput: (data: string) => {
|
|
137
|
+
if (matchesKey(data, "enter") || matchesKey(data, "escape")) {
|
|
138
|
+
done(undefined);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export default function (pi: ExtensionAPI) {
|
|
146
|
+
pi.registerCommand("summarize", {
|
|
147
|
+
description: "Summarize the current conversation in a custom UI",
|
|
148
|
+
handler: async (_args, ctx) => {
|
|
149
|
+
const branch = ctx.sessionManager.getBranch();
|
|
150
|
+
const conversationText = buildConversationText(branch);
|
|
151
|
+
|
|
152
|
+
if (!conversationText.trim()) {
|
|
153
|
+
if (ctx.hasUI) {
|
|
154
|
+
ctx.ui.notify("No conversation text found", "warning");
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (ctx.hasUI) {
|
|
160
|
+
ctx.ui.notify("Preparing summary...", "info");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const model = getModel("openai", "gpt-5.2");
|
|
164
|
+
if (!model && ctx.hasUI) {
|
|
165
|
+
ctx.ui.notify("Model openai/gpt-5.2 not found", "warning");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const apiKey = model ? await ctx.modelRegistry.getApiKey(model) : undefined;
|
|
169
|
+
if (!apiKey && ctx.hasUI) {
|
|
170
|
+
ctx.ui.notify("No API key for openai/gpt-5.2", "warning");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!model || !apiKey) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const summaryMessages = [
|
|
178
|
+
{
|
|
179
|
+
role: "user" as const,
|
|
180
|
+
content: [{ type: "text" as const, text: buildSummaryPrompt(conversationText) }],
|
|
181
|
+
timestamp: Date.now(),
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
const response = await complete(model, { messages: summaryMessages }, { apiKey, reasoningEffort: "high" });
|
|
186
|
+
|
|
187
|
+
const summary = response.content
|
|
188
|
+
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
189
|
+
.map((c) => c.text)
|
|
190
|
+
.join("\n");
|
|
191
|
+
|
|
192
|
+
await showSummaryUi(summary, ctx);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-with-deps",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-with-deps",
|
|
9
|
-
"version": "1.9.
|
|
9
|
+
"version": "1.9.4",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ms": "^2.1.3"
|
|
12
12
|
},
|
package/examples/sdk/README.md
CHANGED
|
@@ -37,9 +37,8 @@ import {
|
|
|
37
37
|
discoverModels,
|
|
38
38
|
discoverSkills,
|
|
39
39
|
discoverExtensions,
|
|
40
|
-
discoverCustomTools,
|
|
41
40
|
discoverContextFiles,
|
|
42
|
-
|
|
41
|
+
discoverPromptTemplates,
|
|
43
42
|
loadSettings,
|
|
44
43
|
buildSystemPrompt,
|
|
45
44
|
ModelRegistry,
|
|
@@ -92,7 +91,7 @@ const { session } = await createAgentSession({
|
|
|
92
91
|
extensions: [{ factory: myExtension }],
|
|
93
92
|
skills: [],
|
|
94
93
|
contextFiles: [],
|
|
95
|
-
|
|
94
|
+
promptTemplates: [],
|
|
96
95
|
sessionManager: SessionManager.inMemory(),
|
|
97
96
|
});
|
|
98
97
|
|
|
@@ -123,7 +122,7 @@ await session.prompt("Hello");
|
|
|
123
122
|
| `additionalExtensionPaths` | `[]` | Merge with discovery |
|
|
124
123
|
| `skills` | Discovered | Skills for prompt |
|
|
125
124
|
| `contextFiles` | Discovered | AGENTS.md files |
|
|
126
|
-
| `
|
|
125
|
+
| `promptTemplates` | Discovered | Prompt templates (slash commands) |
|
|
127
126
|
| `sessionManager` | `SessionManager.create(cwd)` | Persistence |
|
|
128
127
|
| `settingsManager` | From agentDir | Settings overrides |
|
|
129
128
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mariozechner/pi-coding-agent",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.4",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -39,19 +39,19 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@mariozechner/clipboard": "^0.3.0",
|
|
42
|
-
"@mariozechner/
|
|
43
|
-
"@mariozechner/pi-
|
|
44
|
-
"@mariozechner/pi-
|
|
42
|
+
"@mariozechner/jiti": "^2.6.2",
|
|
43
|
+
"@mariozechner/pi-agent-core": "^0.45.4",
|
|
44
|
+
"@mariozechner/pi-ai": "^0.45.4",
|
|
45
|
+
"@mariozechner/pi-tui": "^0.45.4",
|
|
45
46
|
"chalk": "^5.5.0",
|
|
46
47
|
"cli-highlight": "^2.1.11",
|
|
47
48
|
"diff": "^8.0.2",
|
|
48
49
|
"file-type": "^21.1.1",
|
|
49
50
|
"glob": "^11.0.3",
|
|
50
|
-
"jiti": "^2.6.1",
|
|
51
51
|
"marked": "^15.0.12",
|
|
52
52
|
"minimatch": "^10.1.1",
|
|
53
53
|
"proper-lockfile": "^4.1.2",
|
|
54
|
-
"
|
|
54
|
+
"wasm-vips": "^0.0.16"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/diff": "^7.0.2",
|