pi-agent-flow 1.0.8 → 1.2.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.
@@ -27,12 +27,12 @@ export function formatFixedTokens(count: number): string {
27
27
  }
28
28
 
29
29
  /**
30
- * Format flow type name to fixed width (10 chars) in lowercase with space padding.
31
- * Examples: "debug" → "debug ", "architect" → "architect ", "brainstorm" → "brainstorm"
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 = 10;
35
+ const targetWidth = 5;
36
36
  if (lower.length >= targetWidth) return lower.slice(0, targetWidth);
37
37
  return lower.padEnd(targetWidth, " ");
38
38
  }
@@ -50,8 +50,8 @@ export function formatCompactStats(usage: Partial<UsageStats>, model?: string, m
50
50
  parts.push(`tps: ${formatTps(usage.smoothedTps)}`);
51
51
  parts.push(`ctx: ${formatFixedTokens(usage.contextTokens || 0)}`);
52
52
 
53
- let result = parts.join(" · ") + (model ? ` · ${model}` : "");
54
-
53
+ const displayModel = model ? model.replace(/^[^/]+\//, "") : undefined;
54
+ let result = parts.join(" · ") + (displayModel ? ` · ${displayModel}` : "");
55
55
  if (maxWidth && visibleLength(result) > maxWidth) {
56
56
  // Drop model first
57
57
  let narrow = parts.join(" · ");
@@ -22,7 +22,7 @@ import {
22
22
  isFlowError,
23
23
  isFlowSuccess,
24
24
  } from "./types.js";
25
- import { formatCompactStats, formatFlowTypeName, truncateChars, contentBudget } from "./render-utils.js";
25
+ import { formatCompactStats, formatFlowTypeName, truncateChars, tailText, contentBudget } from "./render-utils.js";
26
26
 
27
27
  function shortenPath(p: string): string {
28
28
  const home = os.homedir();
@@ -66,6 +66,27 @@ 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
+ case "batch_read": {
71
+ const ops = Array.isArray(args.o) ? args.o : Array.isArray(args.op) ? args.op : Array.isArray(args.operations) ? args.operations : Array.isArray(args) ? args : [];
72
+ if (ops.length === 0) return fg("muted", `${toolName} (empty)`);
73
+ const parts: string[] = [];
74
+ for (const op of ops) {
75
+ const opObj = op as Record<string, unknown>;
76
+ const opName = (opObj.o ?? opObj.op ?? "?") as string;
77
+ const opPath = (opObj.p ?? opObj.path ?? "?") as string;
78
+ const shortPath = shortenPath(opPath);
79
+ if (opName === "edit") {
80
+ const edits = (opObj.e ?? opObj.edits) as unknown[] | undefined;
81
+ const blockInfo = edits && edits.length > 1 ? ` (${edits.length} blocks)` : "";
82
+ parts.push(`edit ${shortPath}${blockInfo}`);
83
+ } else {
84
+ parts.push(`${opName} ${shortPath}`);
85
+ }
86
+ }
87
+ const summary = parts.length <= 3 ? parts.join(", ") : `${parts.slice(0, 2).join(", ")} +${parts.length - 2} more`;
88
+ return fg("muted", `${toolName} `) + fg("accent", summary);
89
+ }
69
90
  default:
70
91
  return fg("accent", toolName) + fg("dim", ` ${JSON.stringify(args)}`);
71
92
  }
@@ -107,6 +128,16 @@ function flowStatusIcon(r: SingleResult, theme: { fg: ThemeFg }): string {
107
128
  return isFlowError(r) ? theme.fg("error", "✗") : theme.fg("success", "✓");
108
129
  }
109
130
 
131
+ /** Center a label in a fixed-width header using em-dashes. Total width = 20. */
132
+ function sectionHeader(label: string): string {
133
+ const total = 20;
134
+ const innerLen = label.length + 2; // account for spaces around label
135
+ const side = (total - innerLen) / 2;
136
+ const left = "─".repeat(Math.floor(side));
137
+ const right = "─".repeat(Math.ceil(side));
138
+ return `${left} ${label} ${right}`;
139
+ }
140
+
110
141
  // ---------------------------------------------------------------------------
111
142
  // renderFlowCall — shown while the flow is being invoked
112
143
  // ---------------------------------------------------------------------------
@@ -139,6 +170,7 @@ export function renderFlowResult(
139
170
  type: flowRequest.type || "unknown",
140
171
  agentSource: "user",
141
172
  intent: flowRequest.intent || "Processing...",
173
+ aim: flowRequest.aim || flowRequest.intent || "Processing...",
142
174
  exitCode: -1, // In progress
143
175
  messages: [],
144
176
  stderr: "",
@@ -203,12 +235,12 @@ function renderFlowExpanded(
203
235
 
204
236
  // Intent
205
237
  container.addChild(new Spacer(1));
206
- container.addChild(new Text(theme.fg("muted", "─── intent ───"), 0, 0));
238
+ container.addChild(new Text(theme.fg("muted", sectionHeader("intent")), 0, 0));
207
239
  container.addChild(new Text(theme.fg("dim", r.intent), 0, 0));
208
240
 
209
241
  // Flow report (structured output)
210
242
  container.addChild(new Spacer(1));
211
- container.addChild(new Text(theme.fg("muted", "─── report ───"), 0, 0));
243
+ container.addChild(new Text(theme.fg("muted", sectionHeader("report")), 0, 0));
212
244
  if (flowOutput) {
213
245
  container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
214
246
  } else {
@@ -220,7 +252,7 @@ function renderFlowExpanded(
220
252
  const toolTraces = renderToolTraces(displayItems, theme);
221
253
  if (toolTraces) {
222
254
  container.addChild(new Spacer(1));
223
- container.addChild(new Text(theme.fg("muted", "─── tool calls ───"), 0, 0));
255
+ container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
224
256
  container.addChild(new Text(toolTraces, 0, 0));
225
257
  }
226
258
 
@@ -239,14 +271,14 @@ function renderFlowCollapsed(
239
271
  const maxWidth = process.stdout.columns ?? 80;
240
272
  const stats = formatCompactStats(r.usage, r.model, maxWidth);
241
273
  const typeName = formatFlowTypeName(r.type);
242
- let header = `${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", "─")} ${theme.fg("dim", stats)}`;
274
+ let header = `${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", stats)}`;
243
275
  if (error && r.stopReason) header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
244
276
  container.addChild(new TruncatedText(header, 0, 0));
245
277
 
246
- // dir: line (intent/objective)
247
- if (r.intent) {
248
- const dirContent = truncateChars(r.intent, contentBudget(10));
249
- container.addChild(new TruncatedText(`${theme.fg("dim", "├─ dir:")} ${theme.fg("dim", dirContent)}`, 0, 0));
278
+ // aim: line (short headline)
279
+ if (r.aim) {
280
+ const dirContent = truncateChars(r.aim, contentBudget(10));
281
+ container.addChild(new TruncatedText(`${theme.fg("dim", "├─ aim:")} ${theme.fg("dim", dirContent)}`, 0, 0));
250
282
  }
251
283
 
252
284
  // act: line (last tool call with count)
@@ -258,18 +290,21 @@ function renderFlowCollapsed(
258
290
  container.addChild(new TruncatedText(`${theme.fg("dim", "├─ " + actPrefix)}${actContent}`, 0, 0));
259
291
  }
260
292
 
261
- // log: line (last assistant text or streaming)
262
- if (flowOutput) {
263
- const logContent = truncateChars(flowOutput, contentBudget(10));
264
- container.addChild(new TruncatedText(`${theme.fg("dim", "└─ log:")} ${theme.fg("dim", logContent)}`, 0, 0));
293
+ // msg: line (last assistant text or streaming)
294
+ if (r.exitCode === -1 && streamingText) {
295
+ const logContent = tailText(streamingText, contentBudget(10));
296
+ container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
297
+ } else if (flowOutput) {
298
+ const logContent = tailText(flowOutput, contentBudget(10));
299
+ container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
265
300
  } else if (streamingText) {
266
- const logContent = truncateChars(streamingText, contentBudget(10));
267
- container.addChild(new TruncatedText(`${theme.fg("dim", "└─ log:")} ${theme.fg("dim", logContent)}`, 0, 0));
301
+ const logContent = tailText(streamingText, contentBudget(10));
302
+ container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
268
303
  } else if (error && r.errorMessage) {
269
304
  const logContent = truncateChars(r.errorMessage, contentBudget(10));
270
- container.addChild(new TruncatedText(`${theme.fg("dim", "└─ log:")} ${theme.fg("error", logContent)}`, 0, 0));
305
+ container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("error", logContent)}`, 0, 0));
271
306
  } else {
272
- container.addChild(new TruncatedText(`${theme.fg("dim", "└─ log:")} ${theme.fg("dim", "[n/a]")}`, 0, 0));
307
+ container.addChild(new TruncatedText(`${theme.fg("dim", "└─ msg:")} ${theme.fg("dim", "[n/a]")}`, 0, 0));
273
308
  }
274
309
 
275
310
  return container;
@@ -317,7 +352,7 @@ function renderMultiFlowExpanded(
317
352
 
318
353
  container.addChild(new Spacer(1));
319
354
  // Per-flow header: ─── EXPLORER (no icon)
320
- container.addChild(new Text(`${theme.fg("muted", "─── ")}${theme.fg("accent", typeName)}`, 0, 0));
355
+ container.addChild(new Text(theme.fg("muted", sectionHeader(typeName)), 0, 0));
321
356
 
322
357
  // Stats: dashboard format
323
358
  const flowStats = formatCompactStats(r.usage, r.model);
@@ -335,7 +370,7 @@ function renderMultiFlowExpanded(
335
370
  const toolTraces = renderToolTraces(displayItems, theme);
336
371
  if (toolTraces) {
337
372
  container.addChild(new Spacer(1));
338
- container.addChild(new Text(theme.fg("muted", "─── tool calls ───"), 0, 0));
373
+ container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
339
374
  container.addChild(new Text(toolTraces, 0, 0));
340
375
  }
341
376
  }
@@ -366,7 +401,7 @@ function renderActivityPanel(
366
401
 
367
402
  // Header line
368
403
  const headerPrefix = isLast ? "└─" : "├─";
369
- let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", "─")} ${theme.fg("dim", stats)}`;
404
+ let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", stats)}`;
370
405
  if (error && r.stopReason) {
371
406
  headerLine += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
372
407
  }
@@ -375,10 +410,10 @@ function renderActivityPanel(
375
410
  // Continuation indent for sub-lines
376
411
  const indent = isLast ? " " : "│ ";
377
412
 
378
- // dir: line (intent/objective)
379
- if (r.intent) {
380
- const dirContent = truncateChars(r.intent, contentBudget(10));
381
- container.addChild(new TruncatedText(`${theme.fg("dim", indent + "├─ dir:")} ${theme.fg("dim", dirContent)}`, 0, 0));
413
+ // aim: line (short headline)
414
+ if (r.aim) {
415
+ const dirContent = truncateChars(r.aim, contentBudget(10));
416
+ container.addChild(new TruncatedText(`${theme.fg("dim", indent + "├─ aim:")} ${theme.fg("dim", dirContent)}`, 0, 0));
382
417
  }
383
418
 
384
419
  // act: line (last tool call with count)
@@ -390,16 +425,17 @@ function renderActivityPanel(
390
425
  container.addChild(new TruncatedText(`${theme.fg("dim", indent + "├─ " + actPrefix)}${actContent}`, 0, 0));
391
426
  }
392
427
 
393
- // log: line (last assistant text)
394
- const lastText = getLastAssistantText(r.messages);
428
+ // msg: line (live streaming text or last assistant text)
429
+ const liveText = r.exitCode === -1 ? r.streamingText : undefined;
430
+ const lastText = liveText || getLastAssistantText(r.messages);
395
431
  if (lastText) {
396
- const logContent = truncateChars(lastText, contentBudget(10));
397
- container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ log:")} ${theme.fg("dim", logContent)}`, 0, 0));
432
+ const logContent = tailText(lastText, contentBudget(10));
433
+ container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ msg:")} ${theme.fg("dim", logContent)}`, 0, 0));
398
434
  } else if (error && r.errorMessage) {
399
435
  const logContent = truncateChars(r.errorMessage, contentBudget(10));
400
- container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ log:")} ${theme.fg("error", logContent)}`, 0, 0));
436
+ container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ msg:")} ${theme.fg("error", logContent)}`, 0, 0));
401
437
  } else {
402
- container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ log:")} ${theme.fg("dim", "[n/a]")}`, 0, 0));
438
+ container.addChild(new TruncatedText(`${theme.fg("dim", indent + "└─ msg:")} ${theme.fg("dim", "[n/a]")}`, 0, 0));
403
439
  }
404
440
 
405
441
  // Add blank line separator between flows (with continuation pipe)