pi-agent-flow 0.1.0-alpha.4 → 0.1.0-alpha.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.
Files changed (2) hide show
  1. package/package.json +2 -1
  2. package/render-utils.ts +53 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-agent-flow",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
4
4
  "description": "Flow-state delegation extension for Pi coding agent.",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -11,6 +11,7 @@
11
11
  "runner-cli.js",
12
12
  "runner-events.js",
13
13
  "render.ts",
14
+ "render-utils.ts",
14
15
  "types.ts",
15
16
  "agents/",
16
17
  "README.md"
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Pure utility functions for rendering — extracted for testability.
3
+ */
4
+
5
+ import type { UsageStats } from "./types.js";
6
+
7
+ export function formatTokens(count: number): string {
8
+ if (count < 1000) return count.toString();
9
+ if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
10
+ if (count < 1000000) return `${Math.round(count / 1000)}k`;
11
+ return `${(count / 1000000).toFixed(1)}M`;
12
+ }
13
+
14
+ export function formatFlowUsage(usage: Partial<UsageStats>, model?: string): string {
15
+ const parts: string[] = [];
16
+ if (usage.toolCalls && usage.toolCalls > 0) parts.push(`${usage.toolCalls} calls`);
17
+ if (usage.turns) parts.push(`${usage.turns} turn${usage.turns > 1 ? "s" : ""}`);
18
+ if (usage.input) parts.push(`↑${formatTokens(usage.input)}`);
19
+ if (usage.output) parts.push(`↓${formatTokens(usage.output)}`);
20
+ if (usage.cacheRead) parts.push(`CR:${formatTokens(usage.cacheRead)}`);
21
+ if (usage.cacheWrite) parts.push(`CW:${formatTokens(usage.cacheWrite)}`);
22
+ if (usage.cost) parts.push(`$${usage.cost.toFixed(4)}`);
23
+ if (usage.contextTokens && usage.contextTokens > 0) parts.push(`ctx:${formatTokens(usage.contextTokens)}`);
24
+ if (model) parts.push(model);
25
+ return parts.join(" ");
26
+ }
27
+
28
+ export function formatCompactStats(usage: Partial<UsageStats>, model?: string): string {
29
+ const io: string[] = [];
30
+ if (usage.input) io.push(`↑${formatTokens(usage.input)}`);
31
+ if (usage.output) io.push(`↓${formatTokens(usage.output)}`);
32
+ const meta: string[] = [];
33
+ if (usage.cacheRead) meta.push(`CR:${formatTokens(usage.cacheRead)}`);
34
+ if (usage.contextTokens && usage.contextTokens > 0) meta.push(`ctx:${formatTokens(usage.contextTokens)}`);
35
+ if (model) meta.push(model);
36
+ const parts: string[] = [];
37
+ if (io.length) parts.push(io.join(" "));
38
+ if (meta.length) parts.push(meta.join(" "));
39
+ return parts.join(" │ ");
40
+ }
41
+
42
+ export function truncateChars(text: string, max: number): string {
43
+ if (text.length <= max) return text;
44
+ const head = Math.ceil(max * 0.6);
45
+ const tail = max - head - 3;
46
+ return text.slice(0, head) + " ... " + text.slice(-tail);
47
+ }
48
+
49
+ export function tailText(text: string, max: number): string {
50
+ const flat = text.replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
51
+ if (flat.length <= max) return flat;
52
+ return flat.slice(-max);
53
+ }