pi-agent-flow 1.8.1 → 1.8.3

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 (154) hide show
  1. package/README.md +4 -30
  2. package/agents/audit.md +1 -2
  3. package/agents/build.md +1 -0
  4. package/agents/craft.md +12 -8
  5. package/agents/debug.md +2 -2
  6. package/agents/ideas.md +1 -0
  7. package/agents/scout.md +1 -0
  8. package/dist/agents.d.ts +41 -0
  9. package/dist/agents.d.ts.map +1 -0
  10. package/dist/agents.js +283 -0
  11. package/dist/agents.js.map +1 -0
  12. package/dist/batch/batch-bash.d.ts +87 -0
  13. package/dist/batch/batch-bash.d.ts.map +1 -0
  14. package/dist/batch/batch-bash.js +369 -0
  15. package/dist/batch/batch-bash.js.map +1 -0
  16. package/dist/batch/constants.d.ts +100 -0
  17. package/dist/batch/constants.d.ts.map +1 -0
  18. package/dist/batch/constants.js +15 -0
  19. package/dist/batch/constants.js.map +1 -0
  20. package/dist/batch/execute.d.ts +21 -0
  21. package/dist/batch/execute.d.ts.map +1 -0
  22. package/dist/batch/execute.js +440 -0
  23. package/dist/batch/execute.js.map +1 -0
  24. package/dist/batch/fuzzy-edit.d.ts +29 -0
  25. package/dist/batch/fuzzy-edit.d.ts.map +1 -0
  26. package/dist/batch/fuzzy-edit.js +257 -0
  27. package/dist/batch/fuzzy-edit.js.map +1 -0
  28. package/dist/batch/index.d.ts +85 -0
  29. package/dist/batch/index.d.ts.map +1 -0
  30. package/dist/batch/index.js +422 -0
  31. package/dist/batch/index.js.map +1 -0
  32. package/dist/batch/render.d.ts +14 -0
  33. package/dist/batch/render.d.ts.map +1 -0
  34. package/dist/batch/render.js +74 -0
  35. package/dist/batch/render.js.map +1 -0
  36. package/dist/batch/symbols.d.ts +9 -0
  37. package/dist/batch/symbols.d.ts.map +1 -0
  38. package/dist/batch/symbols.js +310 -0
  39. package/dist/batch/symbols.js.map +1 -0
  40. package/dist/batch.d.ts +12 -0
  41. package/dist/batch.d.ts.map +1 -0
  42. package/dist/batch.js +11 -0
  43. package/dist/batch.js.map +1 -0
  44. package/dist/cli-args.d.ts +27 -0
  45. package/dist/cli-args.d.ts.map +1 -0
  46. package/dist/cli-args.js +265 -0
  47. package/dist/cli-args.js.map +1 -0
  48. package/dist/config.d.ts +58 -0
  49. package/dist/config.d.ts.map +1 -0
  50. package/dist/config.js +296 -0
  51. package/dist/config.js.map +1 -0
  52. package/dist/depth.d.ts +25 -0
  53. package/dist/depth.d.ts.map +1 -0
  54. package/dist/depth.js +160 -0
  55. package/dist/depth.js.map +1 -0
  56. package/dist/executor.d.ts +87 -0
  57. package/dist/executor.d.ts.map +1 -0
  58. package/dist/executor.js +295 -0
  59. package/dist/executor.js.map +1 -0
  60. package/dist/flow-prompt.d.ts +23 -0
  61. package/dist/flow-prompt.d.ts.map +1 -0
  62. package/dist/flow-prompt.js +99 -0
  63. package/dist/flow-prompt.js.map +1 -0
  64. package/dist/flow.d.ts +76 -0
  65. package/dist/flow.d.ts.map +1 -0
  66. package/dist/flow.js +704 -0
  67. package/dist/flow.js.map +1 -0
  68. package/dist/index.d.ts +10 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +327 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/reasoning-strip.d.ts +26 -0
  73. package/dist/reasoning-strip.d.ts.map +1 -0
  74. package/dist/reasoning-strip.js +58 -0
  75. package/dist/reasoning-strip.js.map +1 -0
  76. package/dist/render-utils.d.ts +42 -0
  77. package/dist/render-utils.d.ts.map +1 -0
  78. package/dist/render-utils.js +182 -0
  79. package/dist/render-utils.js.map +1 -0
  80. package/dist/render.d.ts +24 -0
  81. package/dist/render.d.ts.map +1 -0
  82. package/dist/render.js +409 -0
  83. package/dist/render.js.map +1 -0
  84. package/dist/runner-events.d.ts +59 -0
  85. package/dist/runner-events.d.ts.map +1 -0
  86. package/dist/runner-events.js +539 -0
  87. package/dist/runner-events.js.map +1 -0
  88. package/dist/session-mode.d.ts +10 -0
  89. package/dist/session-mode.d.ts.map +1 -0
  90. package/dist/session-mode.js +25 -0
  91. package/dist/session-mode.js.map +1 -0
  92. package/dist/settings-resolver.d.ts +28 -0
  93. package/dist/settings-resolver.d.ts.map +1 -0
  94. package/dist/settings-resolver.js +148 -0
  95. package/dist/settings-resolver.js.map +1 -0
  96. package/dist/sliding-prompt.d.ts +40 -0
  97. package/dist/sliding-prompt.d.ts.map +1 -0
  98. package/dist/sliding-prompt.js +121 -0
  99. package/dist/sliding-prompt.js.map +1 -0
  100. package/dist/snapshot.d.ts +29 -0
  101. package/dist/snapshot.d.ts.map +1 -0
  102. package/dist/snapshot.js +199 -0
  103. package/dist/snapshot.js.map +1 -0
  104. package/dist/structured-output.d.ts +36 -0
  105. package/dist/structured-output.d.ts.map +1 -0
  106. package/dist/structured-output.js +244 -0
  107. package/dist/structured-output.js.map +1 -0
  108. package/dist/timed-bash.d.ts +45 -0
  109. package/dist/timed-bash.d.ts.map +1 -0
  110. package/dist/timed-bash.js +219 -0
  111. package/dist/timed-bash.js.map +1 -0
  112. package/dist/tool-utils.d.ts +20 -0
  113. package/dist/tool-utils.d.ts.map +1 -0
  114. package/dist/tool-utils.js +38 -0
  115. package/dist/tool-utils.js.map +1 -0
  116. package/dist/transitions.d.ts +39 -0
  117. package/dist/transitions.d.ts.map +1 -0
  118. package/dist/transitions.js +59 -0
  119. package/dist/transitions.js.map +1 -0
  120. package/dist/types.d.ts +207 -0
  121. package/dist/types.d.ts.map +1 -0
  122. package/dist/types.js +143 -0
  123. package/dist/types.js.map +1 -0
  124. package/dist/web-tool.d.ts +35 -0
  125. package/dist/web-tool.d.ts.map +1 -0
  126. package/dist/web-tool.js +545 -0
  127. package/dist/web-tool.js.map +1 -0
  128. package/package.json +7 -5
  129. package/src/agents.ts +0 -299
  130. package/src/ambient.d.ts +0 -107
  131. package/src/batch/batch-bash.ts +0 -443
  132. package/src/batch/constants.ts +0 -128
  133. package/src/batch/execute.ts +0 -551
  134. package/src/batch/fuzzy-edit.ts +0 -323
  135. package/src/batch/index.ts +0 -494
  136. package/src/batch/render.ts +0 -81
  137. package/src/batch/symbols.ts +0 -341
  138. package/src/batch.ts +0 -28
  139. package/src/cli-args.ts +0 -315
  140. package/src/config.ts +0 -391
  141. package/src/executor.ts +0 -445
  142. package/src/flow.ts +0 -834
  143. package/src/hooks.ts +0 -294
  144. package/src/index.ts +0 -1132
  145. package/src/render-utils.ts +0 -205
  146. package/src/render.ts +0 -524
  147. package/src/runner-events.ts +0 -692
  148. package/src/session-mode.ts +0 -33
  149. package/src/sliding-prompt.ts +0 -144
  150. package/src/structured-output.ts +0 -195
  151. package/src/timed-bash.ts +0 -270
  152. package/src/transitions.ts +0 -86
  153. package/src/types.ts +0 -386
  154. package/src/web-tool.ts +0 -663
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Pure utility functions for rendering - extracted for testability.
3
+ */
4
+ /**
5
+ * Format a token count to exactly 5 characters with leading spaces.
6
+ * Shifts from k to M when value would exceed 5 chars.
7
+ * Examples: 500 → " 500", 1300 → " 1.3k", 32000 → "32.0k", 950500 → "0.95M"
8
+ */
9
+ export function formatFixedTokens(count) {
10
+ if (count < 1000) {
11
+ return count.toString().padStart(5);
12
+ }
13
+ const k = count / 1000;
14
+ if (k < 100) {
15
+ return (k.toFixed(1) + "k").padStart(5);
16
+ }
17
+ else if (k < 1000) {
18
+ const m = count / 1000000;
19
+ return (m.toFixed(2) + "M").padStart(5);
20
+ }
21
+ else {
22
+ const m = count / 1000000;
23
+ return (m.toFixed(2) + "M").padStart(5);
24
+ }
25
+ }
26
+ /**
27
+ * Format flow type name to fixed width (5 chars) in lowercase with space padding.
28
+ * Examples: "debug" → "debug", "scout" → "scout", "build" → "build"
29
+ */
30
+ export function formatFlowTypeName(type) {
31
+ const lower = type.toLowerCase();
32
+ const targetWidth = 5;
33
+ if (lower.length >= targetWidth)
34
+ return lower.slice(0, targetWidth);
35
+ return lower.padEnd(targetWidth, " ");
36
+ }
37
+ /** Format tokens-per-second to a 5-char display (e.g., " 42.3", " -"). */
38
+ function formatTps(value) {
39
+ if (!value || value <= 0)
40
+ return " -";
41
+ return value.toFixed(1).padStart(5);
42
+ }
43
+ export function formatCompactTokenPair(usage) {
44
+ return `↑ ${formatFixedTokens(usage.input || 0)} · ↓ ${formatFixedTokens(usage.output || 0)}`;
45
+ }
46
+ export function formatCompactStats(usage, model, maxWidth, options = {}) {
47
+ const tokenParts = [`↑ ${formatFixedTokens(usage.input || 0)}`, `↓ ${formatFixedTokens(usage.output || 0)}`];
48
+ let runtimeParts = [`tps: ${formatTps(usage.smoothedTps)}`];
49
+ if (!options.skipContext) {
50
+ runtimeParts.push(`ctx: ${formatFixedTokens(usage.contextTokens || 0)}`);
51
+ }
52
+ const parts = options.skipTokens ? runtimeParts : [...tokenParts, ...runtimeParts];
53
+ const displayModel = options.hideModel ? undefined : (model ? model.replace(/^[^/]+\//, "") : undefined);
54
+ let result = parts.join(" · ") + (displayModel ? ` · ${displayModel}` : "");
55
+ if (maxWidth && visibleLength(result) > maxWidth) {
56
+ // Drop model first.
57
+ let narrow = parts.join(" · ");
58
+ if (visibleLength(narrow) <= maxWidth)
59
+ return narrow;
60
+ // Drop context tokens next.
61
+ const withoutContext = parts.filter((part) => !part.startsWith("ctx:"));
62
+ narrow = withoutContext.join(" · ");
63
+ if (visibleLength(narrow) <= maxWidth)
64
+ return narrow;
65
+ // Bare minimum: token pair for normal stats, tps for token-free headers.
66
+ narrow = options.skipTokens ? runtimeParts[0] : tokenParts.join(" · ");
67
+ if (visibleLength(narrow) <= maxWidth)
68
+ return narrow;
69
+ return truncateChars(result, maxWidth);
70
+ }
71
+ return result;
72
+ }
73
+ export function formatCountdown(ms) {
74
+ const totalSeconds = Math.max(0, Math.ceil(ms / 1000));
75
+ const minutes = Math.floor(totalSeconds / 60);
76
+ const seconds = totalSeconds % 60;
77
+ return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
78
+ }
79
+ /** Regex matching ANSI escape sequences. */
80
+ const ANSI_RE = /\x1b\[[0-9;]*m/g;
81
+ /** Return the visible (ANSI-stripped) character count. */
82
+ export function visibleLength(text) {
83
+ return text.replace(ANSI_RE, "").length;
84
+ }
85
+ /**
86
+ * Compute the remaining visible-character budget for a line,
87
+ * given the length of its prefix (indent + label + space).
88
+ * Respects `process.stdout.columns` with a floor of 40 and default of 80.
89
+ */
90
+ export function getTruncationBudget(prefixLength) {
91
+ const cols = process.stdout.columns ?? 80;
92
+ const width = Math.max(cols, 40);
93
+ return Math.max(width - prefixLength, 1);
94
+ }
95
+ /** Fixed content budget for collapsed-line text (dir/act/log). */
96
+ export const CONTENT_MAX = 60;
97
+ /** Longer budget for detail lines (act/msg) to show more content. */
98
+ export const DETAIL_CONTENT_MAX = 80;
99
+ /**
100
+ * Compute how many visible chars of content fit after a prefix,
101
+ * using the given budget (defaults to CONTENT_MAX). Floor of 8 to keep things readable.
102
+ */
103
+ export function contentBudget(prefixVisibleLen, max = CONTENT_MAX) {
104
+ return Math.max(max - prefixVisibleLen, 8);
105
+ }
106
+ /**
107
+ * Truncate an ANSI-colored string to at most `max` visible characters,
108
+ * preserving ANSI codes in the kept portions. Does not inject reset codes
109
+ * — the caller is responsible for closing any open styles.
110
+ *
111
+ * Refactored to perform head-truncation (start + ...) instead of middle-truncation.
112
+ */
113
+ function truncateAnsi(text, max) {
114
+ if (visibleLength(text) <= max)
115
+ return text;
116
+ // Walk through the string, collecting raw chars until we've consumed
117
+ // `count` visible characters. ANSI sequences are copied through without
118
+ // counting toward the limit.
119
+ function takeVisible(src, count) {
120
+ let raw = "";
121
+ let visible = 0;
122
+ let i = 0;
123
+ while (i < src.length && visible < count) {
124
+ // Check for ANSI escape sequence at current position
125
+ if (src[i] === "\x1b" && src[i + 1] === "[") {
126
+ const end = src.indexOf("m", i + 2);
127
+ if (end !== -1) {
128
+ const seq = src.slice(i, end + 1);
129
+ if (/^\x1b\[[0-9;]*m$/.test(seq)) {
130
+ raw += seq;
131
+ i = end + 1;
132
+ continue;
133
+ }
134
+ }
135
+ }
136
+ raw += src[i];
137
+ visible++;
138
+ i++;
139
+ }
140
+ return { raw, consumed: visible };
141
+ }
142
+ if (max < 3) {
143
+ // Not enough room for '...' — just truncate without ellipsis
144
+ const { raw } = takeVisible(text, max);
145
+ return raw;
146
+ }
147
+ const keep = max - 3; // 3 = '...'.length
148
+ const ellipsis = "...";
149
+ // Take head from the start
150
+ const headResult = takeVisible(text, keep);
151
+ return headResult.raw + ellipsis;
152
+ }
153
+ export function truncateChars(text, max) {
154
+ text = text.replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
155
+ if (visibleLength(text) <= max)
156
+ return text;
157
+ return truncateAnsi(text, max);
158
+ }
159
+ export function tailText(text, max) {
160
+ const flat = text.replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
161
+ if (visibleLength(flat) <= max)
162
+ return flat;
163
+ // Take last `max` visible characters from the end
164
+ let visible = 0;
165
+ let i = flat.length - 1;
166
+ while (i >= 0 && visible < max) {
167
+ if (flat[i] === "m") {
168
+ const escStart = flat.lastIndexOf("\x1b[", i);
169
+ if (escStart !== -1 && escStart < i) {
170
+ const seq = flat.slice(escStart, i + 1);
171
+ if (/^\x1b\[[0-9;]*m$/.test(seq)) {
172
+ i = escStart - 1;
173
+ continue;
174
+ }
175
+ }
176
+ }
177
+ visible++;
178
+ i--;
179
+ }
180
+ return flat.slice(i + 1);
181
+ }
182
+ //# sourceMappingURL=render-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../src/render-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC9C,IAAI,KAAK,GAAG,IAAI,EAAE,CAAC;QAClB,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;SAAM,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,KAAK,GAAG,OAAO,CAAC;QAC1B,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;SAAM,CAAC;QACP,MAAM,CAAC,GAAG,KAAK,GAAG,OAAO,CAAC;QAC1B,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,MAAM,WAAW,GAAG,CAAC,CAAC;IACtB,IAAI,KAAK,CAAC,MAAM,IAAI,WAAW;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACpE,OAAO,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AACvC,CAAC;AAED,6EAA6E;AAC7E,SAAS,SAAS,CAAC,KAAyB;IAC3C,IAAI,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAA0B;IAChE,OAAO,KAAK,iBAAiB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,QAAQ,iBAAiB,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC;AAC/F,CAAC;AAED,MAAM,UAAU,kBAAkB,CACjC,KAA0B,EAC1B,KAAc,EACd,QAAiB,EACjB,UAAgF,EAAE;IAElF,MAAM,UAAU,GAAG,CAAC,KAAK,iBAAiB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,EAAE,KAAK,iBAAiB,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7G,IAAI,YAAY,GAAG,CAAC,QAAQ,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC5D,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAC1B,YAAY,CAAC,IAAI,CAAC,QAAQ,iBAAiB,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC;IAEnF,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACzG,IAAI,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC5E,IAAI,QAAQ,IAAI,aAAa,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;QAClD,oBAAoB;QACpB,IAAI,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,QAAQ;YAAE,OAAO,MAAM,CAAC;QAErD,4BAA4B;QAC5B,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACxE,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,QAAQ;YAAE,OAAO,MAAM,CAAC;QAErD,yEAAyE;QACzE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvE,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,QAAQ;YAAE,OAAO,MAAM,CAAC;QAErD,OAAO,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU;IACzC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AAClF,CAAC;AAED,4CAA4C;AAC5C,MAAM,OAAO,GAAG,iBAAiB,CAAC;AAElC,0DAA0D;AAC1D,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,gBAAwB,EAAE,GAAG,GAAG,WAAW;IACxE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,gBAAgB,EAAE,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,GAAW;IAC9C,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IAE5C,qEAAqE;IACrE,wEAAwE;IACxE,6BAA6B;IAC7B,SAAS,WAAW,CAAC,GAAW,EAAE,KAAa;QAC9C,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,OAAO,GAAG,KAAK,EAAE,CAAC;YAC1C,qDAAqD;YACrD,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC7C,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBACpC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;oBAChB,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;oBAClC,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;wBAClC,GAAG,IAAI,GAAG,CAAC;wBACX,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;wBACZ,SAAS;oBACV,CAAC;gBACF,CAAC;YACF,CAAC;YACD,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACd,OAAO,EAAE,CAAC;YACV,CAAC,EAAE,CAAC;QACL,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IACnC,CAAC;IAED,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;QACb,6DAA6D;QAC7D,MAAM,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,mBAAmB;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC;IAExB,2BAA2B;IAC1B,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE3C,OAAO,UAAU,CAAC,GAAG,GAAG,QAAQ,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,GAAW;IACtD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,GAAW;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACxE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IAE5C,kDAAkD;IAClD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACxB,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC9C,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;gBACrC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBACxC,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAClC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC;oBACjB,SAAS;gBACV,CAAC;YACF,CAAC;QACF,CAAC;QACD,OAAO,EAAE,CAAC;QACV,CAAC,EAAE,CAAC;IACL,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * TUI rendering for flow-state tool calls and results.
3
+ *
4
+ * Option B: collapsed view shows structured report (Summary/Done/Not Done/Next Steps).
5
+ * Expanded view adds raw tool call traces.
6
+ */
7
+ import { Container, Text } from "@mariozechner/pi-tui";
8
+ type ThemeFg = (color: string, text: string) => string;
9
+ type ThemeBg = (color: string, text: string) => string;
10
+ type FlowTheme = {
11
+ fg: ThemeFg;
12
+ bold: (s: string) => string;
13
+ bg: ThemeBg;
14
+ };
15
+ export declare function renderFlowCall(args: Record<string, any>, theme: FlowTheme): Text;
16
+ export declare function renderFlowResult(result: {
17
+ content: Array<{
18
+ type: string;
19
+ text?: string;
20
+ }>;
21
+ details?: unknown;
22
+ }, expanded: boolean, theme: FlowTheme, args?: Record<string, any>): Container | Text;
23
+ export {};
24
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,SAAS,EAAoB,IAAI,EAAiB,MAAM,sBAAsB,CAAC;AAsBxF,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AACvD,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AACvD,KAAK,SAAS,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,CAAC;AAiI3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAKhF;AAMD,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9E,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,SAAS,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,SAAS,GAAG,IAAI,CA4BlB"}
package/dist/render.js ADDED
@@ -0,0 +1,409 @@
1
+ /**
2
+ * TUI rendering for flow-state tool calls and results.
3
+ *
4
+ * Option B: collapsed view shows structured report (Summary/Done/Not Done/Next Steps).
5
+ * Expanded view adds raw tool call traces.
6
+ */
7
+ import * as os from "node:os";
8
+ import { getMarkdownTheme } from "@mariozechner/pi-coding-agent";
9
+ import { Container, Markdown, Spacer, Text, TruncatedText } from "@mariozechner/pi-tui";
10
+ import { getFlowSummaryText } from "./runner-events.js";
11
+ import { aggregateFlowUsage, getFlowDisplayItems, getFlowOutput, getLastToolCall, getLastAssistantText, isFlowError, isFlowSuccess, } from "./types.js";
12
+ import { formatCompactStats, formatCompactTokenPair, formatCountdown, formatFlowTypeName, truncateChars, tailText, contentBudget, visibleLength, DETAIL_CONTENT_MAX } from "./render-utils.js";
13
+ function shortenPath(p) {
14
+ const home = os.homedir();
15
+ return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
16
+ }
17
+ function formatCollapsedFlowHeaderTypeName(type) {
18
+ return type.toLowerCase();
19
+ }
20
+ function formatFlowToolCall(toolName, args, fg) {
21
+ const pathArg = (args.file_path || args.path || "...");
22
+ switch (toolName) {
23
+ case "bash": {
24
+ const cmd = (args.command || "...").replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
25
+ return fg("muted", "$ ") + fg("toolOutput", cmd);
26
+ }
27
+ case "read": {
28
+ let text = fg("accent", shortenPath(pathArg));
29
+ const offset = args.offset;
30
+ const limit = args.limit;
31
+ if (offset !== undefined || limit !== undefined) {
32
+ const start = offset ?? 1;
33
+ const end = limit !== undefined ? start + limit - 1 : "";
34
+ text += fg("warning", `:${start}${end ? `-${end}` : ""}`);
35
+ }
36
+ return fg("muted", "read ") + text;
37
+ }
38
+ case "write": {
39
+ const lines = (args.content || "").split("\n").length;
40
+ let text = fg("muted", "write ") + fg("accent", shortenPath(pathArg));
41
+ if (lines > 1)
42
+ text += fg("dim", ` (${lines} lines)`);
43
+ return text;
44
+ }
45
+ case "edit":
46
+ return fg("muted", "edit ") + fg("accent", shortenPath(pathArg));
47
+ case "ls":
48
+ return fg("muted", "ls ") + fg("accent", shortenPath((args.path || ".")));
49
+ case "find":
50
+ return fg("muted", "find ") + fg("accent", (args.pattern || "*")) + fg("dim", ` in ${shortenPath((args.path || "."))}`);
51
+ case "grep":
52
+ return fg("muted", "grep ") + fg("accent", `/${(args.pattern || "")}/`) + fg("dim", ` in ${shortenPath((args.path || "."))}`);
53
+ case "batch":
54
+ case "batch_read": {
55
+ const ops = Array.isArray(args.o) ? args.o : Array.isArray(args.op) ? args.op : Array.isArray(args.operations) ? args.operations : Array.isArray(args) ? args : [];
56
+ if (ops.length === 0)
57
+ return fg("muted", `${toolName} (empty)`);
58
+ const parts = [];
59
+ for (const op of ops) {
60
+ const opObj = op;
61
+ const opName = (opObj.o ?? opObj.op ?? "?");
62
+ const opPath = (opObj.p ?? opObj.path ?? "?");
63
+ const shortPath = shortenPath(opPath);
64
+ if (opName === "edit") {
65
+ const edits = (opObj.e ?? opObj.edits);
66
+ const blockInfo = edits && edits.length > 1 ? ` (${edits.length} blocks)` : "";
67
+ parts.push(`edit ${shortPath}${blockInfo}`);
68
+ }
69
+ else {
70
+ parts.push(`${opName} ${shortPath}`);
71
+ }
72
+ }
73
+ const summary = parts.length <= 3 ? parts.join(", ") : `${parts.slice(0, 2).join(", ")} +${parts.length - 2} more`;
74
+ return fg("muted", `${toolName} `) + fg("accent", summary);
75
+ }
76
+ default:
77
+ return fg("accent", toolName) + fg("dim", ` ${JSON.stringify(args)}`);
78
+ }
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Shared rendering building blocks
82
+ // ---------------------------------------------------------------------------
83
+ function splitOutputLines(text) {
84
+ const lines = text.replace(/\r\n?/g, "\n").split("\n");
85
+ if (lines.length > 1 && lines[lines.length - 1] === "")
86
+ lines.pop();
87
+ return lines;
88
+ }
89
+ function renderToolTraces(items, theme) {
90
+ const lines = [];
91
+ for (const item of items) {
92
+ if (item.type === "toolCall") {
93
+ lines.push(theme.fg("muted", "→ ") + formatFlowToolCall(item.name, item.args, theme.fg.bind(theme)));
94
+ }
95
+ }
96
+ return lines.join("\n");
97
+ }
98
+ function renderFlowReport(output, theme) {
99
+ const lines = splitOutputLines(output);
100
+ return lines.map((line) => theme.fg("toolOutput", line)).join("\n");
101
+ }
102
+ function flowStatusIcon(r, theme) {
103
+ if (r.exitCode === -1)
104
+ return theme.fg("warning", "⏳");
105
+ return isFlowError(r) ? theme.fg("error", "✗") : theme.fg("success", "✓");
106
+ }
107
+ /** Center a label in a fixed-width header using em-dashes. Total width = 20. */
108
+ function sectionHeader(label) {
109
+ const total = 20;
110
+ const innerLen = label.length + 2; // account for spaces around label
111
+ const side = (total - innerLen) / 2;
112
+ const left = "─".repeat(Math.floor(side));
113
+ const right = "─".repeat(Math.ceil(side));
114
+ return `${left} ${label} ${right}`;
115
+ }
116
+ function getLiveCountdown(r) {
117
+ if (r.exitCode !== -1 || typeof r.deadlineAtMs !== "number")
118
+ return undefined;
119
+ return formatCountdown(r.deadlineAtMs - Date.now());
120
+ }
121
+ function formatAimLinePrefix(treePrefix, r) {
122
+ const countdown = getLiveCountdown(r);
123
+ return countdown ? `${treePrefix} aim: [${countdown}] - ` : `${treePrefix} aim: `;
124
+ }
125
+ function formatMsgLinePrefix(treePrefix, r) {
126
+ return `${treePrefix} msg: [${formatCompactTokenPair(r.usage)}] - `;
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // renderFlowCall — shown while the flow is being invoked
130
+ // ---------------------------------------------------------------------------
131
+ export function renderFlowCall(args, theme) {
132
+ const flows = args.flow;
133
+ // Minimal — renderFlowResult owns the full display
134
+ return new Text("", 0, 0);
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // renderFlowResult — shown after the flow completes
138
+ // ---------------------------------------------------------------------------
139
+ export function renderFlowResult(result, expanded, theme, args) {
140
+ const details = result.details;
141
+ const streamingText = result.content?.[0]?.type === "text" ? result.content[0].text : undefined;
142
+ if (!details || details.results.length === 0) {
143
+ // Ghost Dashboard: render a placeholder status line during the zero state
144
+ const flowRequest = args?.flow?.[0];
145
+ if (flowRequest) {
146
+ const ghostResult = {
147
+ type: flowRequest.type || "unknown",
148
+ agentSource: "user",
149
+ intent: flowRequest.intent || "Processing...",
150
+ aim: flowRequest.aim || flowRequest.intent || "Processing...",
151
+ exitCode: -1, // In progress
152
+ messages: [],
153
+ stderr: "",
154
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0, toolCalls: 0 },
155
+ };
156
+ return renderFlowCollapsed(ghostResult, flowStatusIcon(ghostResult, theme), false, streamingText || "", theme);
157
+ }
158
+ return new Text(streamingText || "", 0, 0);
159
+ }
160
+ if (details.results.length === 1) {
161
+ return renderSingleFlowResult(details.results[0], expanded, theme, streamingText);
162
+ }
163
+ return renderMultiFlowResult(details, expanded, theme);
164
+ }
165
+ // ---------------------------------------------------------------------------
166
+ // Single flow result
167
+ // ---------------------------------------------------------------------------
168
+ function renderSingleFlowResult(r, expanded, theme, streamingText) {
169
+ const error = isFlowError(r);
170
+ const icon = flowStatusIcon(r, theme);
171
+ const displayItems = getFlowDisplayItems(r.messages);
172
+ const flowOutput = getFlowOutput(r.messages);
173
+ if (expanded) {
174
+ return renderFlowExpanded(r, icon, error, displayItems, flowOutput, theme);
175
+ }
176
+ return renderFlowCollapsed(r, icon, error, flowOutput, theme, streamingText);
177
+ }
178
+ function renderFlowExpanded(r, icon, error, displayItems, flowOutput, theme) {
179
+ const mdTheme = getMarkdownTheme();
180
+ const container = new Container();
181
+ // Header: uppercase type name with dots, no icon, no source
182
+ const typeName = formatFlowTypeName(r.type);
183
+ let header = theme.fg("toolTitle", theme.bold(typeName));
184
+ if (error && r.stopReason)
185
+ header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
186
+ container.addChild(new Text(header, 0, 0));
187
+ if (error && r.errorMessage) {
188
+ container.addChild(new Text(theme.fg("error", `Error: ${r.errorMessage}`), 0, 0));
189
+ }
190
+ // Stats: dashboard format
191
+ const inlineStats = formatCompactStats(r.usage, r.model);
192
+ container.addChild(new Text(theme.fg("dim", inlineStats), 0, 0));
193
+ // Intent
194
+ container.addChild(new Spacer(1));
195
+ container.addChild(new Text(theme.fg("muted", sectionHeader("intent")), 0, 0));
196
+ container.addChild(new Text(theme.fg("dim", r.intent), 0, 0));
197
+ // Flow report (structured output)
198
+ container.addChild(new Spacer(1));
199
+ container.addChild(new Text(theme.fg("muted", sectionHeader("report")), 0, 0));
200
+ // Structured output summary (compact badge when available)
201
+ if (r.structuredOutput) {
202
+ const so = r.structuredOutput;
203
+ const statusColor = so.status === "complete" ? "success" : so.status === "partial" ? "warning" : "error";
204
+ container.addChild(new Text(`${theme.fg(statusColor, `[${so.status}]`)} ${theme.fg("dim", so.summary)}`, 0, 0));
205
+ if (so.files.length > 0) {
206
+ container.addChild(new Text(theme.fg("dim", `Files: ${so.files.map((f) => f.path).join(", ")}`), 0, 0));
207
+ }
208
+ if (so.commands?.length > 0) {
209
+ const cmdLabels = so.commands.map((c) => {
210
+ const short = c.command.length > 30 ? c.command.slice(0, 30) + "..." : c.command;
211
+ return `${c.tool ?? "cmd"}: ${short}`;
212
+ });
213
+ container.addChild(new Text(theme.fg("dim", `Commands: ${cmdLabels.join(", ")}`), 0, 0));
214
+ }
215
+ if (so.notDone.length > 0) {
216
+ const notDoneText = so.notDone.map((item) => {
217
+ const details = [
218
+ item.reason ? `reason: ${item.reason}` : undefined,
219
+ item.blocker ? `blocker: ${item.blocker}` : undefined,
220
+ item.nextStep ? `next: ${item.nextStep}` : undefined,
221
+ ].filter(Boolean).join("; ");
222
+ return details ? `${item.item} (${details})` : item.item;
223
+ }).join("; ");
224
+ container.addChild(new Text(theme.fg("dim", `Not Done: ${notDoneText}`), 0, 0));
225
+ }
226
+ if (so.nextSteps.length > 0) {
227
+ container.addChild(new Text(theme.fg("dim", `Next: ${so.nextSteps.join("; ")}`), 0, 0));
228
+ }
229
+ container.addChild(new Spacer(1));
230
+ }
231
+ if (flowOutput) {
232
+ container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
233
+ }
234
+ else {
235
+ const summary = getFlowSummaryText(r);
236
+ container.addChild(new Text(theme.fg("muted", summary), 0, 0));
237
+ }
238
+ // Tool traces (expanded only)
239
+ const toolTraces = renderToolTraces(displayItems, theme);
240
+ if (toolTraces) {
241
+ container.addChild(new Spacer(1));
242
+ container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
243
+ container.addChild(new Text(toolTraces, 0, 0));
244
+ }
245
+ return container;
246
+ }
247
+ function renderFlowCollapsed(r, icon, error, flowOutput, theme, streamingText) {
248
+ const container = new Container();
249
+ const maxWidth = process.stdout.columns ?? 80;
250
+ const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true, skipContext: true, hideModel: true });
251
+ const typeName = formatCollapsedFlowHeaderTypeName(r.type);
252
+ const modelLabel = r.model ? r.model.replace(/^[^/]+\//, "") : "";
253
+ let header = `${theme.fg("accent", theme.bold(typeName))}${theme.fg("dim", modelLabel ? ` · ${modelLabel} · ` : " · ")}${theme.fg("dim", stats)}`;
254
+ if (error && r.stopReason)
255
+ header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
256
+ container.addChild(new TruncatedText(header, 0, 0));
257
+ // aim: line (short headline)
258
+ if (r.aim) {
259
+ const aimPrefix = formatAimLinePrefix("├─", r);
260
+ const dirContent = truncateChars(r.aim, contentBudget(visibleLength(aimPrefix)));
261
+ container.addChild(new TruncatedText(`${theme.fg("dim", aimPrefix)}${theme.fg("dim", dirContent)}`, 0, 0));
262
+ }
263
+ // act: line (last tool call with count)
264
+ const lastTool = getLastToolCall(r.messages);
265
+ if (lastTool) {
266
+ const actStr = formatFlowToolCall(lastTool.name, lastTool.args, theme.fg.bind(theme));
267
+ const actPrefix = `├─ act: [${r.usage.toolCalls}] - `;
268
+ const actContent = truncateChars(actStr, contentBudget(visibleLength(actPrefix), DETAIL_CONTENT_MAX));
269
+ container.addChild(new TruncatedText(`${theme.fg("dim", actPrefix)}${actContent}`, 0, 0));
270
+ }
271
+ // msg: line (last assistant text or streaming)
272
+ const msgPrefix = formatMsgLinePrefix("└─", r);
273
+ const msgBudget = contentBudget(visibleLength(msgPrefix), DETAIL_CONTENT_MAX);
274
+ if (r.exitCode === -1 && streamingText) {
275
+ const logContent = tailText(streamingText, msgBudget);
276
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
277
+ }
278
+ else if (r.structuredOutput?.summary) {
279
+ const logContent = truncateChars(r.structuredOutput.summary, msgBudget);
280
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
281
+ }
282
+ else if (flowOutput) {
283
+ const logContent = tailText(flowOutput, msgBudget);
284
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
285
+ }
286
+ else if (streamingText) {
287
+ const logContent = tailText(streamingText, msgBudget);
288
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
289
+ }
290
+ else if (error && r.errorMessage) {
291
+ const logContent = truncateChars(r.errorMessage, msgBudget);
292
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("error", logContent)}`, 0, 0));
293
+ }
294
+ else {
295
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", "[n/a]")}`, 0, 0));
296
+ }
297
+ return container;
298
+ }
299
+ // ---------------------------------------------------------------------------
300
+ // Multi-flow result
301
+ // ---------------------------------------------------------------------------
302
+ function renderMultiFlowResult(details, expanded, theme) {
303
+ const results = details.results;
304
+ const successCount = results.filter((r) => isFlowSuccess(r)).length;
305
+ const failCount = results.filter((r) => isFlowError(r)).length;
306
+ const icon = failCount > 0 ? theme.fg("warning", "◐") : theme.fg("success", "✓");
307
+ if (expanded) {
308
+ return renderMultiFlowExpanded(results, successCount, icon, theme);
309
+ }
310
+ return renderMultiFlowCollapsed(results, theme);
311
+ }
312
+ function renderMultiFlowExpanded(results, successCount, icon, theme) {
313
+ const mdTheme = getMarkdownTheme();
314
+ const container = new Container();
315
+ // Summary: just show count, no icon
316
+ container.addChild(new Text(theme.fg("accent", `${results.length} flows`), 0, 0));
317
+ for (const r of results) {
318
+ const displayItems = getFlowDisplayItems(r.messages);
319
+ const flowOutput = getFlowOutput(r.messages);
320
+ const typeName = formatFlowTypeName(r.type);
321
+ container.addChild(new Spacer(1));
322
+ // Per-flow header: ─── EXPLORER (no icon)
323
+ container.addChild(new Text(theme.fg("muted", sectionHeader(typeName)), 0, 0));
324
+ // Stats: dashboard format
325
+ const flowStats = formatCompactStats(r.usage, r.model);
326
+ container.addChild(new Text(theme.fg("dim", flowStats), 0, 0));
327
+ // Intent: just show text, no prefix
328
+ container.addChild(new Text(theme.fg("dim", r.intent), 0, 0));
329
+ if (flowOutput) {
330
+ container.addChild(new Spacer(1));
331
+ container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
332
+ }
333
+ // Tool traces in expanded view
334
+ const toolTraces = renderToolTraces(displayItems, theme);
335
+ if (toolTraces) {
336
+ container.addChild(new Spacer(1));
337
+ container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
338
+ container.addChild(new Text(toolTraces, 0, 0));
339
+ }
340
+ }
341
+ // Total stats: dashboard format
342
+ const totalUsage = aggregateFlowUsage(results);
343
+ const totalModel = results[0]?.model;
344
+ const totalStats = formatCompactStats(totalUsage, totalModel);
345
+ container.addChild(new Spacer(1));
346
+ container.addChild(new Text(theme.fg("dim", totalStats), 0, 0));
347
+ return container;
348
+ }
349
+ function renderActivityPanel(results, theme) {
350
+ const container = new Container();
351
+ const maxWidth = process.stdout.columns ?? 80;
352
+ for (let i = 0; i < results.length; i++) {
353
+ const r = results[i];
354
+ const isLast = i === results.length - 1;
355
+ const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true, skipContext: true, hideModel: true });
356
+ const error = isFlowError(r);
357
+ const typeName = formatCollapsedFlowHeaderTypeName(r.type);
358
+ // Header line
359
+ const headerPrefix = isLast ? "└─" : "├─";
360
+ const modelLabel = r.model ? r.model.replace(/^[^/]+\//, "") : "";
361
+ let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))}${theme.fg("dim", modelLabel ? ` · ${modelLabel} · ` : " · ")}${theme.fg("dim", stats)}`;
362
+ if (error && r.stopReason) {
363
+ headerLine += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
364
+ }
365
+ container.addChild(new TruncatedText(headerLine, 0, 0));
366
+ // Continuation indent for sub-lines
367
+ const indent = isLast ? " " : "│ ";
368
+ // aim: line (short headline)
369
+ if (r.aim) {
370
+ const aimPrefix = formatAimLinePrefix(indent + "├─", r);
371
+ const dirContent = truncateChars(r.aim, contentBudget(visibleLength(aimPrefix)));
372
+ container.addChild(new TruncatedText(`${theme.fg("dim", aimPrefix)}${theme.fg("dim", dirContent)}`, 0, 0));
373
+ }
374
+ // act: line (last tool call with count)
375
+ const lastTool = getLastToolCall(r.messages);
376
+ if (lastTool) {
377
+ const actStr = formatFlowToolCall(lastTool.name, lastTool.args, theme.fg.bind(theme));
378
+ const actPrefix = `${indent}├─ act: [${r.usage.toolCalls}] - `;
379
+ const actContent = truncateChars(actStr, contentBudget(visibleLength(actPrefix), DETAIL_CONTENT_MAX));
380
+ container.addChild(new TruncatedText(`${theme.fg("dim", actPrefix)}${actContent}`, 0, 0));
381
+ }
382
+ // msg: line (live streaming text or last assistant text)
383
+ const msgPrefix = formatMsgLinePrefix(indent + "└─", r);
384
+ const msgBudget = contentBudget(visibleLength(msgPrefix), DETAIL_CONTENT_MAX);
385
+ const liveText = r.exitCode === -1 ? r.streamingText : undefined;
386
+ const lastText = liveText || getLastAssistantText(r.messages);
387
+ if (lastText) {
388
+ const logContent = tailText(lastText, msgBudget);
389
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
390
+ }
391
+ else if (error && r.errorMessage) {
392
+ const logContent = truncateChars(r.errorMessage, msgBudget);
393
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("error", logContent)}`, 0, 0));
394
+ }
395
+ else {
396
+ container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", "[n/a]")}`, 0, 0));
397
+ }
398
+ // Add blank line separator between flows (with continuation pipe)
399
+ if (!isLast) {
400
+ container.addChild(new TruncatedText(theme.fg("dim", "│"), 0, 0));
401
+ }
402
+ }
403
+ container.addChild(new TruncatedText(theme.fg("muted", "(Ctrl+O to expand tool traces)"), 0, 0));
404
+ return container;
405
+ }
406
+ function renderMultiFlowCollapsed(results, theme) {
407
+ return renderActivityPanel(results, theme);
408
+ }
409
+ //# sourceMappingURL=render.js.map