pi-agent-flow 1.0.8 → 1.1.0
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 +2 -2
- package/agents/audit.md +36 -0
- package/agents/{code.md → build.md} +5 -4
- package/agents/craft.md +37 -0
- package/agents/debug.md +2 -1
- package/agents/{brainstorm.md → ideas.md} +4 -4
- package/agents/scout.md +31 -0
- package/agents.ts +5 -8
- package/batch.ts +1083 -0
- package/config.ts +35 -0
- package/flow.ts +84 -18
- package/hooks.ts +31 -13
- package/index.ts +126 -28
- package/package.json +11 -3
- package/render-utils.ts +3 -3
- package/render.ts +55 -24
- package/runner-events.js +280 -126
- package/types.ts +3 -2
- package/web-tool.ts +663 -0
- package/agents/architect.md +0 -35
- package/agents/explore.md +0 -29
- package/agents/review.md +0 -35
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-agent-flow",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Flow-state delegation extension for Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"index.ts",
|
|
9
|
+
"web-tool.ts",
|
|
9
10
|
"agents.ts",
|
|
10
11
|
"config.ts",
|
|
11
12
|
"flow.ts",
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"render.ts",
|
|
16
17
|
"render-utils.ts",
|
|
17
18
|
"types.ts",
|
|
19
|
+
"batch.ts",
|
|
18
20
|
"agents/",
|
|
19
21
|
"README.md"
|
|
20
22
|
],
|
|
@@ -35,7 +37,7 @@
|
|
|
35
37
|
"url": "https://github.com/tuanhung303/pi-agent-flow"
|
|
36
38
|
},
|
|
37
39
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
40
|
+
"node": ">=20.19.0"
|
|
39
41
|
},
|
|
40
42
|
"scripts": {
|
|
41
43
|
"lint": "tsc --noEmit",
|
|
@@ -43,7 +45,9 @@
|
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"typescript": "^5.0.0",
|
|
46
|
-
"vitest": "^3.2.4"
|
|
48
|
+
"vitest": "^3.2.4",
|
|
49
|
+
"@types/jsdom": "^28.0.1",
|
|
50
|
+
"@types/turndown": "^5.0.6"
|
|
47
51
|
},
|
|
48
52
|
"peerDependencies": {
|
|
49
53
|
"@mariozechner/pi-agent-core": ">=0.37.0",
|
|
@@ -68,5 +72,9 @@
|
|
|
68
72
|
"@sinclair/typebox": {
|
|
69
73
|
"optional": true
|
|
70
74
|
}
|
|
75
|
+
},
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"jsdom": "^29.1.0",
|
|
78
|
+
"turndown": "^7.2.4"
|
|
71
79
|
}
|
|
72
80
|
}
|
package/render-utils.ts
CHANGED
|
@@ -27,12 +27,12 @@ export function formatFixedTokens(count: number): string {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Format flow type name to fixed width (
|
|
31
|
-
* Examples: "debug" → "debug
|
|
30
|
+
* Format flow type name to fixed width (5 chars) in lowercase with space padding.
|
|
31
|
+
* Examples: "debug" → "debug", "scout" → "scout", "build" → "build"
|
|
32
32
|
*/
|
|
33
33
|
export function formatFlowTypeName(type: string): string {
|
|
34
34
|
const lower = type.toLowerCase();
|
|
35
|
-
const targetWidth =
|
|
35
|
+
const targetWidth = 5;
|
|
36
36
|
if (lower.length >= targetWidth) return lower.slice(0, targetWidth);
|
|
37
37
|
return lower.padEnd(targetWidth, " ");
|
|
38
38
|
}
|
package/render.ts
CHANGED
|
@@ -66,6 +66,26 @@ function formatFlowToolCall(toolName: string, args: Record<string, unknown>, fg:
|
|
|
66
66
|
return fg("muted", "find ") + fg("accent", (args.pattern || "*") as string) + fg("dim", ` in ${shortenPath((args.path || ".") as string)}`);
|
|
67
67
|
case "grep":
|
|
68
68
|
return fg("muted", "grep ") + fg("accent", `/${(args.pattern || "") as string}/`) + fg("dim", ` in ${shortenPath((args.path || ".") as string)}`);
|
|
69
|
+
case "batch": {
|
|
70
|
+
const ops = Array.isArray(args.o) ? args.o : Array.isArray(args.op) ? args.op : Array.isArray(args.operations) ? args.operations : Array.isArray(args) ? args : [];
|
|
71
|
+
if (ops.length === 0) return fg("muted", "batch (empty)");
|
|
72
|
+
const parts: string[] = [];
|
|
73
|
+
for (const op of ops) {
|
|
74
|
+
const opObj = op as Record<string, unknown>;
|
|
75
|
+
const opName = (opObj.o ?? opObj.op ?? "?") as string;
|
|
76
|
+
const opPath = (opObj.p ?? opObj.path ?? "?") as string;
|
|
77
|
+
const shortPath = shortenPath(opPath);
|
|
78
|
+
if (opName === "edit") {
|
|
79
|
+
const edits = (opObj.e ?? opObj.edits) as unknown[] | undefined;
|
|
80
|
+
const blockInfo = edits && edits.length > 1 ? ` (${edits.length} blocks)` : "";
|
|
81
|
+
parts.push(`edit ${shortPath}${blockInfo}`);
|
|
82
|
+
} else {
|
|
83
|
+
parts.push(`${opName} ${shortPath}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const summary = parts.length <= 3 ? parts.join(", ") : `${parts.slice(0, 2).join(", ")} +${parts.length - 2} more`;
|
|
87
|
+
return fg("muted", "batch ") + fg("accent", summary);
|
|
88
|
+
}
|
|
69
89
|
default:
|
|
70
90
|
return fg("accent", toolName) + fg("dim", ` ${JSON.stringify(args)}`);
|
|
71
91
|
}
|
|
@@ -107,6 +127,16 @@ function flowStatusIcon(r: SingleResult, theme: { fg: ThemeFg }): string {
|
|
|
107
127
|
return isFlowError(r) ? theme.fg("error", "✗") : theme.fg("success", "✓");
|
|
108
128
|
}
|
|
109
129
|
|
|
130
|
+
/** Center a label in a fixed-width header using em-dashes. Total width = 20. */
|
|
131
|
+
function sectionHeader(label: string): string {
|
|
132
|
+
const total = 20;
|
|
133
|
+
const innerLen = label.length + 2; // account for spaces around label
|
|
134
|
+
const side = (total - innerLen) / 2;
|
|
135
|
+
const left = "─".repeat(Math.floor(side));
|
|
136
|
+
const right = "─".repeat(Math.ceil(side));
|
|
137
|
+
return `${left} ${label} ${right}`;
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
// ---------------------------------------------------------------------------
|
|
111
141
|
// renderFlowCall — shown while the flow is being invoked
|
|
112
142
|
// ---------------------------------------------------------------------------
|
|
@@ -139,6 +169,7 @@ export function renderFlowResult(
|
|
|
139
169
|
type: flowRequest.type || "unknown",
|
|
140
170
|
agentSource: "user",
|
|
141
171
|
intent: flowRequest.intent || "Processing...",
|
|
172
|
+
aim: flowRequest.aim || flowRequest.intent || "Processing...",
|
|
142
173
|
exitCode: -1, // In progress
|
|
143
174
|
messages: [],
|
|
144
175
|
stderr: "",
|
|
@@ -203,12 +234,12 @@ function renderFlowExpanded(
|
|
|
203
234
|
|
|
204
235
|
// Intent
|
|
205
236
|
container.addChild(new Spacer(1));
|
|
206
|
-
container.addChild(new Text(theme.fg("muted", "
|
|
237
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("intent")), 0, 0));
|
|
207
238
|
container.addChild(new Text(theme.fg("dim", r.intent), 0, 0));
|
|
208
239
|
|
|
209
240
|
// Flow report (structured output)
|
|
210
241
|
container.addChild(new Spacer(1));
|
|
211
|
-
container.addChild(new Text(theme.fg("muted", "
|
|
242
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("report")), 0, 0));
|
|
212
243
|
if (flowOutput) {
|
|
213
244
|
container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
|
|
214
245
|
} else {
|
|
@@ -220,7 +251,7 @@ function renderFlowExpanded(
|
|
|
220
251
|
const toolTraces = renderToolTraces(displayItems, theme);
|
|
221
252
|
if (toolTraces) {
|
|
222
253
|
container.addChild(new Spacer(1));
|
|
223
|
-
container.addChild(new Text(theme.fg("muted", "
|
|
254
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
|
|
224
255
|
container.addChild(new Text(toolTraces, 0, 0));
|
|
225
256
|
}
|
|
226
257
|
|
|
@@ -239,14 +270,14 @@ function renderFlowCollapsed(
|
|
|
239
270
|
const maxWidth = process.stdout.columns ?? 80;
|
|
240
271
|
const stats = formatCompactStats(r.usage, r.model, maxWidth);
|
|
241
272
|
const typeName = formatFlowTypeName(r.type);
|
|
242
|
-
let header = `${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim",
|
|
273
|
+
let header = `${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", stats)}`;
|
|
243
274
|
if (error && r.stopReason) header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
244
275
|
container.addChild(new TruncatedText(header, 0, 0));
|
|
245
276
|
|
|
246
|
-
//
|
|
247
|
-
if (r.
|
|
248
|
-
const dirContent = truncateChars(r.
|
|
249
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", "├─
|
|
277
|
+
// aim: line (short headline)
|
|
278
|
+
if (r.aim) {
|
|
279
|
+
const dirContent = truncateChars(r.aim, contentBudget(10));
|
|
280
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", "├─ aim:")} ${theme.fg("dim", dirContent)}`, 0, 0));
|
|
250
281
|
}
|
|
251
282
|
|
|
252
283
|
// act: line (last tool call with count)
|
|
@@ -258,18 +289,18 @@ function renderFlowCollapsed(
|
|
|
258
289
|
container.addChild(new TruncatedText(`${theme.fg("dim", "├─ " + actPrefix)}${actContent}`, 0, 0));
|
|
259
290
|
}
|
|
260
291
|
|
|
261
|
-
//
|
|
292
|
+
// msg: line (last assistant text or streaming)
|
|
262
293
|
if (flowOutput) {
|
|
263
294
|
const logContent = truncateChars(flowOutput, contentBudget(10));
|
|
264
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", "└─
|
|
295
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
|
|
265
296
|
} else if (streamingText) {
|
|
266
297
|
const logContent = truncateChars(streamingText, contentBudget(10));
|
|
267
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", "└─
|
|
298
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
|
|
268
299
|
} else if (error && r.errorMessage) {
|
|
269
300
|
const logContent = truncateChars(r.errorMessage, contentBudget(10));
|
|
270
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", "└─
|
|
301
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("error", logContent)}`, 0, 0));
|
|
271
302
|
} else {
|
|
272
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", "└─
|
|
303
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", "[n/a]")}`, 0, 0));
|
|
273
304
|
}
|
|
274
305
|
|
|
275
306
|
return container;
|
|
@@ -317,7 +348,7 @@ function renderMultiFlowExpanded(
|
|
|
317
348
|
|
|
318
349
|
container.addChild(new Spacer(1));
|
|
319
350
|
// Per-flow header: ─── EXPLORER (no icon)
|
|
320
|
-
container.addChild(new Text(
|
|
351
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader(typeName)), 0, 0));
|
|
321
352
|
|
|
322
353
|
// Stats: dashboard format
|
|
323
354
|
const flowStats = formatCompactStats(r.usage, r.model);
|
|
@@ -335,7 +366,7 @@ function renderMultiFlowExpanded(
|
|
|
335
366
|
const toolTraces = renderToolTraces(displayItems, theme);
|
|
336
367
|
if (toolTraces) {
|
|
337
368
|
container.addChild(new Spacer(1));
|
|
338
|
-
container.addChild(new Text(theme.fg("muted", "
|
|
369
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
|
|
339
370
|
container.addChild(new Text(toolTraces, 0, 0));
|
|
340
371
|
}
|
|
341
372
|
}
|
|
@@ -366,7 +397,7 @@ function renderActivityPanel(
|
|
|
366
397
|
|
|
367
398
|
// Header line
|
|
368
399
|
const headerPrefix = isLast ? "└─" : "├─";
|
|
369
|
-
let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim",
|
|
400
|
+
let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", stats)}`;
|
|
370
401
|
if (error && r.stopReason) {
|
|
371
402
|
headerLine += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
372
403
|
}
|
|
@@ -375,10 +406,10 @@ function renderActivityPanel(
|
|
|
375
406
|
// Continuation indent for sub-lines
|
|
376
407
|
const indent = isLast ? " " : "│ ";
|
|
377
408
|
|
|
378
|
-
//
|
|
379
|
-
if (r.
|
|
380
|
-
const dirContent = truncateChars(r.
|
|
381
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "├─
|
|
409
|
+
// aim: line (short headline)
|
|
410
|
+
if (r.aim) {
|
|
411
|
+
const dirContent = truncateChars(r.aim, contentBudget(10));
|
|
412
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "├─ aim:")} ${theme.fg("dim", dirContent)}`, 0, 0));
|
|
382
413
|
}
|
|
383
414
|
|
|
384
415
|
// act: line (last tool call with count)
|
|
@@ -390,16 +421,16 @@ function renderActivityPanel(
|
|
|
390
421
|
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "├─ " + actPrefix)}${actContent}`, 0, 0));
|
|
391
422
|
}
|
|
392
423
|
|
|
393
|
-
//
|
|
424
|
+
// msg: line (last assistant text)
|
|
394
425
|
const lastText = getLastAssistantText(r.messages);
|
|
395
426
|
if (lastText) {
|
|
396
427
|
const logContent = truncateChars(lastText, contentBudget(10));
|
|
397
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─
|
|
428
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
|
|
398
429
|
} else if (error && r.errorMessage) {
|
|
399
430
|
const logContent = truncateChars(r.errorMessage, contentBudget(10));
|
|
400
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─
|
|
431
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ msg:")} ${theme.fg("error", logContent)}`, 0, 0));
|
|
401
432
|
} else {
|
|
402
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─
|
|
433
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ msg:")} ${theme.fg("dim", "[n/a]")}`, 0, 0));
|
|
403
434
|
}
|
|
404
435
|
|
|
405
436
|
// Add blank line separator between flows (with continuation pipe)
|