create-claude-workspace 1.1.130 → 1.1.132
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/dist/scripts/lib/tui.mjs +125 -109
- package/package.json +1 -1
package/dist/scripts/lib/tui.mjs
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
// ─── Terminal UI for autonomous loop ───
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// Interactive (--interactive): adds input prompt + keyboard controls (needs capable terminal)
|
|
2
|
+
// Events push lines to a buffer. Render loop (4 fps) draws pending lines
|
|
3
|
+
// + status bar. No cursor tricks — just append lines and \r overwrite status.
|
|
5
4
|
import { appendFileSync } from 'node:fs';
|
|
6
5
|
// ─── ANSI ───
|
|
7
6
|
const a = {
|
|
8
|
-
reset: '\x1b[0m',
|
|
9
|
-
bold: '\x1b[1m',
|
|
10
|
-
dim: '\x1b[2m',
|
|
7
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
11
8
|
fg: {
|
|
12
9
|
red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
|
|
13
10
|
blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m', white: '\x1b[37m',
|
|
@@ -17,7 +14,7 @@ const a = {
|
|
|
17
14
|
},
|
|
18
15
|
bg: { gray: '\x1b[48;5;236m' },
|
|
19
16
|
};
|
|
20
|
-
// ───
|
|
17
|
+
// ─── Shared utilities ───
|
|
21
18
|
const AGENT_PALETTE = [a.fg.brightCyan, a.fg.brightMagenta, a.fg.brightGreen, a.fg.brightYellow, a.fg.brightBlue, a.fg.brightRed];
|
|
22
19
|
const agentColors = new Map();
|
|
23
20
|
let nextColor = 0;
|
|
@@ -26,13 +23,10 @@ function agentColor(name) {
|
|
|
26
23
|
agentColors.set(name, AGENT_PALETTE[nextColor++ % AGENT_PALETTE.length]);
|
|
27
24
|
return agentColors.get(name);
|
|
28
25
|
}
|
|
29
|
-
// ─── Tool icons ───
|
|
30
26
|
const ICONS = {
|
|
31
27
|
Bash: '⚡', Read: '📖', Write: '✏️ ', Edit: '🔧', Glob: '🔍', Grep: '🔎',
|
|
32
|
-
Agent: '🤖', TodoRead: '📋', TodoWrite: '📝', WebSearch: '🌐', WebFetch: '🌐',
|
|
33
|
-
AskUserQuestion: '❓',
|
|
28
|
+
Agent: '🤖', TodoRead: '📋', TodoWrite: '📝', WebSearch: '🌐', WebFetch: '🌐', AskUserQuestion: '❓',
|
|
34
29
|
};
|
|
35
|
-
// ─── Helpers ───
|
|
36
30
|
function ts() { return new Date().toLocaleTimeString('en-GB', { hour12: false }); }
|
|
37
31
|
function strip(s) { return s.replace(/\x1b\[[0-9;]*m/g, ''); }
|
|
38
32
|
function trunc(s, n) { const c = s.replace(/\n/g, ' ').trim(); return c.length > n ? c.slice(0, n) + '…' : c; }
|
|
@@ -46,6 +40,10 @@ export class TUI {
|
|
|
46
40
|
onInput = null;
|
|
47
41
|
onHotkey = null;
|
|
48
42
|
paused_ = false;
|
|
43
|
+
// Render state
|
|
44
|
+
pendingLines = [];
|
|
45
|
+
renderTimer = null;
|
|
46
|
+
lastStatusLen = 0;
|
|
49
47
|
// Stats
|
|
50
48
|
loopStart = Date.now();
|
|
51
49
|
iteration_ = 0;
|
|
@@ -57,21 +55,61 @@ export class TUI {
|
|
|
57
55
|
tokens_ = { input: 0, output: 0 };
|
|
58
56
|
iterStart_ = 0;
|
|
59
57
|
agents = [];
|
|
60
|
-
lastModel_ = '';
|
|
61
58
|
inputBuf = '';
|
|
62
|
-
ticker = null;
|
|
63
59
|
constructor(logFile, interactive = false) {
|
|
64
60
|
this.logFile = logFile;
|
|
65
61
|
this.interactive = interactive && process.stdin.isTTY === true;
|
|
66
62
|
if (this.interactive) {
|
|
67
63
|
this.setupInput();
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
// Tick every second to update elapsed time in status bar
|
|
71
|
-
this.ticker = setInterval(() => this.refreshStatusBar(), 1000);
|
|
64
|
+
// 4 fps render loop
|
|
65
|
+
this.renderTimer = setInterval(() => this.render(), 250);
|
|
72
66
|
}
|
|
73
67
|
}
|
|
74
|
-
// ───
|
|
68
|
+
// ─── Render loop ───
|
|
69
|
+
render() {
|
|
70
|
+
const out = process.stdout;
|
|
71
|
+
// Clear current status line
|
|
72
|
+
if (this.lastStatusLen > 0) {
|
|
73
|
+
out.write('\r\x1b[2K');
|
|
74
|
+
}
|
|
75
|
+
// Flush pending log lines
|
|
76
|
+
for (const line of this.pendingLines) {
|
|
77
|
+
out.write(line + '\n');
|
|
78
|
+
}
|
|
79
|
+
this.pendingLines = [];
|
|
80
|
+
// Draw status bar (single line, no \n — stays overwritable)
|
|
81
|
+
const status = this.buildStatus();
|
|
82
|
+
out.write(status);
|
|
83
|
+
this.lastStatusLen = strip(status).length;
|
|
84
|
+
}
|
|
85
|
+
buildStatus() {
|
|
86
|
+
const elapsed = fmtDur(Date.now() - this.loopStart);
|
|
87
|
+
const iterTime = this.iterStart_ ? fmtDur(Date.now() - this.iterStart_) : '—';
|
|
88
|
+
const pct = this.maxIter > 0 ? Math.round((this.iteration_ / this.maxIter) * 100) : 0;
|
|
89
|
+
const tok = fmtTok(this.tokens_.input + this.tokens_.output);
|
|
90
|
+
const parts = [
|
|
91
|
+
`${a.fg.white}${elapsed}`,
|
|
92
|
+
`Iter ${this.iteration_}/${this.maxIter} ${bar(pct, 8)}`,
|
|
93
|
+
`${iterTime}`,
|
|
94
|
+
`${a.fg.cyan}${this.tools}${a.reset} tools`,
|
|
95
|
+
`${a.fg.yellow}${tok}${a.reset} tok`,
|
|
96
|
+
];
|
|
97
|
+
if (this.agents.length > 0) {
|
|
98
|
+
const cur = this.agents[this.agents.length - 1];
|
|
99
|
+
parts.push(`${agentColor(cur)}${cur}${a.reset}`);
|
|
100
|
+
}
|
|
101
|
+
if (this.taskName_)
|
|
102
|
+
parts.push(`${a.fg.cyan}${trunc(this.taskName_, 20)}${a.reset}`);
|
|
103
|
+
if (this.paused_)
|
|
104
|
+
parts.push(`${a.fg.yellow}⏸ PAUSED${a.reset}`);
|
|
105
|
+
const info = parts.join(`${a.fg.gray} │ ${a.reset}`);
|
|
106
|
+
const input = this.inputBuf ? ` ${a.fg.gray}›${a.reset} ${this.inputBuf}` : '';
|
|
107
|
+
const cols = process.stdout.columns || 120;
|
|
108
|
+
const line = ` ${info}${input} `;
|
|
109
|
+
const pad = Math.max(0, cols - strip(line).length);
|
|
110
|
+
return `\r${a.bg.gray}${line}${' '.repeat(pad)}${a.reset}`;
|
|
111
|
+
}
|
|
112
|
+
// ─── Input ───
|
|
75
113
|
setupInput() {
|
|
76
114
|
if (!process.stdin.isTTY)
|
|
77
115
|
return;
|
|
@@ -82,8 +120,8 @@ export class TUI {
|
|
|
82
120
|
if (key === '\x03') {
|
|
83
121
|
this.onHotkey?.('quit');
|
|
84
122
|
return;
|
|
85
|
-
}
|
|
86
|
-
if (key === '\x1a') {
|
|
123
|
+
}
|
|
124
|
+
if (key === '\x1a') {
|
|
87
125
|
this.paused_ = !this.paused_;
|
|
88
126
|
this.onHotkey?.(this.paused_ ? 'pause' : 'resume');
|
|
89
127
|
return;
|
|
@@ -91,59 +129,42 @@ export class TUI {
|
|
|
91
129
|
if (key === '\x13') {
|
|
92
130
|
this.onHotkey?.('stop');
|
|
93
131
|
return;
|
|
94
|
-
}
|
|
132
|
+
}
|
|
95
133
|
if (key === '\r' || key === '\n') {
|
|
96
|
-
if (this.inputBuf.trim())
|
|
134
|
+
if (this.inputBuf.trim())
|
|
97
135
|
this.onInput?.(this.inputBuf.trim());
|
|
98
|
-
}
|
|
99
136
|
this.inputBuf = '';
|
|
100
|
-
this.printInputPrompt(this.inputBuf);
|
|
101
137
|
return;
|
|
102
138
|
}
|
|
103
139
|
if (key === '\x7f' || key === '\b') {
|
|
104
140
|
this.inputBuf = this.inputBuf.slice(0, -1);
|
|
105
|
-
this.printInputPrompt(this.inputBuf);
|
|
106
141
|
return;
|
|
107
142
|
}
|
|
108
143
|
if (key.length === 1 && key >= ' ') {
|
|
109
144
|
this.inputBuf += key;
|
|
110
|
-
this.printInputPrompt(this.inputBuf);
|
|
111
145
|
}
|
|
112
146
|
});
|
|
113
147
|
}
|
|
114
|
-
printInputPrompt(buf) {
|
|
115
|
-
const prompt = `${a.fg.gray} › ${a.fg.white}${buf}${a.reset}`;
|
|
116
|
-
process.stdout.write(`\r\x1b[2K${prompt}`);
|
|
117
|
-
}
|
|
118
148
|
destroy() {
|
|
119
|
-
if (this.
|
|
120
|
-
clearInterval(this.
|
|
121
|
-
this.
|
|
149
|
+
if (this.renderTimer) {
|
|
150
|
+
clearInterval(this.renderTimer);
|
|
151
|
+
this.renderTimer = null;
|
|
122
152
|
}
|
|
123
|
-
if (this.interactive
|
|
124
|
-
|
|
153
|
+
if (this.interactive) {
|
|
154
|
+
// Final flush
|
|
155
|
+
this.render();
|
|
125
156
|
process.stdout.write('\n');
|
|
157
|
+
if (process.stdin.isTTY)
|
|
158
|
+
process.stdin.setRawMode(false);
|
|
126
159
|
}
|
|
127
160
|
}
|
|
128
|
-
refreshStatusBar() {
|
|
129
|
-
// Overwrite status bar + input in place (no new log line)
|
|
130
|
-
process.stdout.write('\x1b[2K\r'); // clear input line
|
|
131
|
-
process.stdout.write('\x1b[A\x1b[2K\r'); // move up, clear status line
|
|
132
|
-
this.renderStatusBar();
|
|
133
|
-
this.printInputPrompt(this.inputBuf);
|
|
134
|
-
}
|
|
135
161
|
setInputHandler(handler) { this.onInput = handler; }
|
|
136
162
|
setHotkeyHandler(handler) { this.onHotkey = handler; }
|
|
137
163
|
isPaused() { return this.paused_; }
|
|
138
|
-
// ─── Output ───
|
|
139
|
-
|
|
164
|
+
// ─── Output (buffered) ───
|
|
165
|
+
push(formatted, raw) {
|
|
140
166
|
if (this.interactive) {
|
|
141
|
-
|
|
142
|
-
process.stdout.write('\x1b[2K\r'); // clear input line
|
|
143
|
-
process.stdout.write('\x1b[A\x1b[2K\r'); // move up, clear status line
|
|
144
|
-
console.log(formatted);
|
|
145
|
-
this.renderStatusBar();
|
|
146
|
-
this.printInputPrompt(this.inputBuf);
|
|
167
|
+
this.pendingLines.push(formatted);
|
|
147
168
|
}
|
|
148
169
|
else {
|
|
149
170
|
console.log(formatted);
|
|
@@ -156,16 +177,6 @@ export class TUI {
|
|
|
156
177
|
catch { /* */ }
|
|
157
178
|
}
|
|
158
179
|
}
|
|
159
|
-
renderStatusBar() {
|
|
160
|
-
const elapsed = this.loopStart ? fmtDur(Date.now() - this.loopStart) : '—';
|
|
161
|
-
const iterElapsed = this.iterStart_ ? fmtDur(Date.now() - this.iterStart_) : '—';
|
|
162
|
-
const pct = this.maxIter > 0 ? Math.round((this.iteration_ / this.maxIter) * 100) : 0;
|
|
163
|
-
const tok = fmtTok(this.tokens_.input + this.tokens_.output);
|
|
164
|
-
const taskInfo = this.taskName_ ? ` │ ${a.fg.cyan}${trunc(this.taskName_, 30)}${a.reset}` : '';
|
|
165
|
-
const pauseLabel = this.paused_ ? ` │ ${a.fg.yellow}⏸ PAUSED${a.reset}` : '';
|
|
166
|
-
const line = ` ${a.fg.gray}${elapsed}${a.reset} │ Iter ${this.iteration_}/${this.maxIter} ${bar(pct, 10)} │ ${iterElapsed} │ ${a.fg.cyan}${this.tools}${a.reset} tools │ ${a.fg.yellow}${tok}${a.reset} tok${taskInfo}${pauseLabel}`;
|
|
167
|
-
process.stdout.write(`${a.bg.gray}${line}${' '.repeat(Math.max(0, (process.stdout.columns || 80) - strip(line).length))}${a.reset}\n`);
|
|
168
|
-
}
|
|
169
180
|
fileOnly(msg) {
|
|
170
181
|
if (this.logFile) {
|
|
171
182
|
try {
|
|
@@ -174,21 +185,37 @@ export class TUI {
|
|
|
174
185
|
catch { /* */ }
|
|
175
186
|
}
|
|
176
187
|
}
|
|
177
|
-
// ───
|
|
188
|
+
// ─── Agent prefix ───
|
|
189
|
+
agentPrefix() {
|
|
190
|
+
if (this.agents.length === 0)
|
|
191
|
+
return '';
|
|
192
|
+
const cur = this.agents[this.agents.length - 1];
|
|
193
|
+
const col = agentColor(cur);
|
|
194
|
+
const short = cur.length > 14 ? cur.slice(0, 14) : cur;
|
|
195
|
+
return `${col}${short}${a.reset} `;
|
|
196
|
+
}
|
|
197
|
+
indent() {
|
|
198
|
+
const prefix = this.agentPrefix();
|
|
199
|
+
if (this.agents.length <= 1)
|
|
200
|
+
return ` ${prefix}`;
|
|
201
|
+
const pipes = this.agents.slice(0, -1).map(n => `${agentColor(n)}│${a.reset}`).join('');
|
|
202
|
+
return ` ${pipes} ${prefix}`;
|
|
203
|
+
}
|
|
204
|
+
// ─── Public API ───
|
|
178
205
|
banner() {
|
|
179
|
-
this.
|
|
180
|
-
this.
|
|
181
|
-
this.
|
|
182
|
-
this.
|
|
206
|
+
this.push('');
|
|
207
|
+
this.push(` ${a.fg.cyan}${a.bold}╔══════════════════════════════════════════════╗${a.reset}`);
|
|
208
|
+
this.push(` ${a.fg.cyan}${a.bold}║ ${a.fg.white}Claude Starter Kit — Autonomous Loop${a.fg.cyan} ║${a.reset}`);
|
|
209
|
+
this.push(` ${a.fg.cyan}${a.bold}╚══════════════════════════════════════════════╝${a.reset}`);
|
|
183
210
|
if (this.interactive) {
|
|
184
|
-
this.
|
|
211
|
+
this.push(` ${a.fg.gray} Ctrl+Z pause │ Ctrl+S stop │ Ctrl+C quit │ Type to send input${a.reset}`);
|
|
185
212
|
}
|
|
186
|
-
this.
|
|
213
|
+
this.push('');
|
|
187
214
|
}
|
|
188
|
-
info(msg) { this.
|
|
189
|
-
warn(msg) { this.
|
|
190
|
-
error(msg) { this.
|
|
191
|
-
success(msg) { this.
|
|
215
|
+
info(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.blue}ℹ${a.reset} ${msg}`); }
|
|
216
|
+
warn(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.yellow}⚠${a.reset} ${a.fg.yellow}${msg}${a.reset}`); }
|
|
217
|
+
error(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.red}✗${a.reset} ${a.fg.red}${msg}${a.reset}`); }
|
|
218
|
+
success(msg) { this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.green}✓${a.reset} ${a.fg.green}${msg}${a.reset}`); }
|
|
192
219
|
setIteration(i, max) {
|
|
193
220
|
this.iteration_ = i;
|
|
194
221
|
this.maxIter = max;
|
|
@@ -198,13 +225,13 @@ export class TUI {
|
|
|
198
225
|
this.agents = [];
|
|
199
226
|
const pct = Math.round((i / max) * 100);
|
|
200
227
|
const elapsed = fmtDur(Date.now() - this.loopStart);
|
|
201
|
-
this.
|
|
202
|
-
this.
|
|
228
|
+
this.push('');
|
|
229
|
+
this.push(` ${a.bold}${a.fg.white}━━━ Iteration ${i}/${max} ${bar(pct, 20)} ${pct}% ${a.fg.gray}│ ${elapsed} elapsed${a.fg.white} ━━━${a.reset}`);
|
|
203
230
|
if (this.taskName_) {
|
|
204
231
|
const tPct = this.tasksTotal_ > 0 ? Math.round((this.tasksDone_ / this.tasksTotal_) * 100) : 0;
|
|
205
|
-
this.
|
|
232
|
+
this.push(` ${a.fg.cyan}📋 ${this.taskName_}${a.reset} ${a.fg.gray}(${this.tasksDone_}/${this.tasksTotal_} tasks ${bar(tPct, 10)} ${tPct}%)${a.reset}`);
|
|
206
233
|
}
|
|
207
|
-
this.
|
|
234
|
+
this.push('');
|
|
208
235
|
}
|
|
209
236
|
setTask(name, done, total) {
|
|
210
237
|
this.taskName_ = name;
|
|
@@ -215,8 +242,8 @@ export class TUI {
|
|
|
215
242
|
const iterElapsed = fmtDur(Date.now() - this.iterStart_);
|
|
216
243
|
const totalElapsed = fmtDur(Date.now() - this.loopStart);
|
|
217
244
|
const tok = fmtTok(this.tokens_.input + this.tokens_.output);
|
|
218
|
-
this.
|
|
219
|
-
this.
|
|
245
|
+
this.push('');
|
|
246
|
+
this.push(` ${a.fg.gray}━━━━ ${iterElapsed} (iter) │ ${totalElapsed} (total) │ ${this.tools} tools │ ${tok} tokens ━━━━${a.reset}`);
|
|
220
247
|
}
|
|
221
248
|
// ─── SDK message handler ───
|
|
222
249
|
handleMessage(message) {
|
|
@@ -243,14 +270,9 @@ export class TUI {
|
|
|
243
270
|
this.tokens_.input += msg.message.usage.input_tokens || 0;
|
|
244
271
|
this.tokens_.output += msg.message.usage.output_tokens || 0;
|
|
245
272
|
}
|
|
246
|
-
// Show model from message if available
|
|
247
|
-
const model = msg.message?.model;
|
|
248
|
-
if (model && !this.lastModel_) {
|
|
249
|
-
this.lastModel_ = model;
|
|
250
|
-
}
|
|
251
273
|
for (const block of content) {
|
|
252
274
|
if (block.type === 'text' && block.text?.trim()) {
|
|
253
|
-
this.
|
|
275
|
+
this.push(`${this.indent()}${a.fg.white}${trunc(block.text, 300)}${a.reset}`, `TEXT: ${trunc(block.text, 300)}`);
|
|
254
276
|
}
|
|
255
277
|
if (block.type === 'tool_use')
|
|
256
278
|
this.onToolUse(block);
|
|
@@ -263,14 +285,13 @@ export class TUI {
|
|
|
263
285
|
const input = block.input || {};
|
|
264
286
|
const pre = this.indent();
|
|
265
287
|
const time = `${a.fg.gray}${ts()}${a.reset}`;
|
|
266
|
-
// Agent delegation
|
|
267
288
|
if (name === 'Agent') {
|
|
268
289
|
const type = input.subagent_type || input.type || 'agent';
|
|
269
290
|
const model = input.model ? ` ${a.fg.gray}(${input.model})${a.reset}` : '';
|
|
270
291
|
const desc = trunc(input.description || input.prompt || '', 50);
|
|
271
292
|
const col = agentColor(type);
|
|
272
293
|
this.agents.push(type);
|
|
273
|
-
this.
|
|
294
|
+
this.push(`${pre}${time} ${icon} ${col}${a.bold}${type}${a.reset}${model} ${a.fg.gray}${desc}${a.reset}`, `AGENT: ${type} ${input.model || ''} — ${desc}`);
|
|
274
295
|
return;
|
|
275
296
|
}
|
|
276
297
|
let detail;
|
|
@@ -293,7 +314,7 @@ export class TUI {
|
|
|
293
314
|
break;
|
|
294
315
|
default: detail = `${a.fg.gray}${trunc(JSON.stringify(input), 60)}${a.reset}`;
|
|
295
316
|
}
|
|
296
|
-
this.
|
|
317
|
+
this.push(`${pre}${time} ${icon} ${a.bold}${name}${a.reset} ${detail}`, `TOOL: ${name} ${JSON.stringify(input).slice(0, 200)}`);
|
|
297
318
|
}
|
|
298
319
|
onToolResult(msg) {
|
|
299
320
|
const content = msg.message?.content;
|
|
@@ -307,52 +328,53 @@ export class TUI {
|
|
|
307
328
|
continue;
|
|
308
329
|
const pre = this.indent();
|
|
309
330
|
if (block.is_error) {
|
|
310
|
-
this.
|
|
331
|
+
this.push(`${pre} ${a.fg.red}✗ ${trunc(output, 100)}${a.reset}`, `ERROR: ${trunc(output, 100)}`);
|
|
311
332
|
}
|
|
312
333
|
else if (output.length < 150) {
|
|
313
|
-
this.
|
|
334
|
+
this.push(`${pre} ${a.fg.green}✓${a.reset} ${a.fg.gray}${trunc(output, 100)}${a.reset}`, `OK: ${trunc(output, 100)}`);
|
|
314
335
|
}
|
|
315
336
|
else {
|
|
316
337
|
const n = output.split('\n').length;
|
|
317
|
-
this.
|
|
338
|
+
this.push(`${pre} ${a.fg.green}✓${a.reset} ${a.fg.gray}${n} lines${a.reset}`, `OK: (${n} lines)`);
|
|
318
339
|
}
|
|
319
340
|
}
|
|
320
341
|
}
|
|
321
342
|
onSystem(msg) {
|
|
322
|
-
// Init message — show model, agents, version
|
|
323
343
|
if (msg.subtype === 'init') {
|
|
324
|
-
const model = msg.model || '
|
|
325
|
-
const agents = msg.agents?.length ? msg.agents.join(', ') : 'none';
|
|
344
|
+
const model = msg.model || '?';
|
|
326
345
|
const ver = msg.claude_code_version || '';
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
346
|
+
const agentList = msg.agents?.join(', ') || 'none';
|
|
347
|
+
this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.cyan}⚙${a.reset} Claude Code ${ver} │ Model: ${a.bold}${model}${a.reset} │ Agents: ${a.fg.cyan}${agentList}${a.reset}`);
|
|
348
|
+
// Set top-level agent name from permissionMode or first agent
|
|
349
|
+
if (this.agents.length === 0 && msg.agents?.length) {
|
|
350
|
+
const topAgent = msg.agents[0];
|
|
351
|
+
this.agents.push(topAgent);
|
|
352
|
+
this.push(` ${a.fg.gray}${ts()}${a.reset} 🤖 ${agentColor(topAgent)}${a.bold}${topAgent}${a.reset} ${a.fg.gray}(${model})${a.reset}`, `AGENT_START: ${topAgent} (${model})`);
|
|
330
353
|
}
|
|
331
354
|
return;
|
|
332
355
|
}
|
|
333
356
|
if (msg.subtype === 'task_started') {
|
|
334
357
|
const desc = msg.description || '';
|
|
335
|
-
const taskType = msg.task_type || '';
|
|
336
358
|
const col = agentColor(desc);
|
|
337
359
|
this.agents.push(desc);
|
|
338
|
-
this.
|
|
360
|
+
this.push(` ${a.fg.gray}${ts()}${a.reset} 🤖 ${col}${a.bold}${desc}${a.reset}`, `AGENT_START: ${desc}`);
|
|
339
361
|
return;
|
|
340
362
|
}
|
|
341
363
|
if (msg.subtype === 'task_notification') {
|
|
342
364
|
const status = msg.status || '';
|
|
365
|
+
const name = this.agents.length > 0 ? this.agents.pop() : 'agent';
|
|
366
|
+
const col = agentColor(name);
|
|
343
367
|
const icon = status === 'completed' ? `${a.fg.green}✓` : `${a.fg.red}✗`;
|
|
344
|
-
const summary = msg.summary ? ` ${a.fg.gray}${trunc(msg.summary,
|
|
345
|
-
|
|
346
|
-
this.agents.pop();
|
|
347
|
-
this.out(` ${a.fg.gray}${ts()}${a.reset} ${icon}${a.reset} Agent ${status}${summary}`, `AGENT_END: ${status} ${msg.summary || ''}`);
|
|
368
|
+
const summary = msg.summary ? ` ${a.fg.gray}${trunc(msg.summary, 60)}${a.reset}` : '';
|
|
369
|
+
this.push(` ${a.fg.gray}${ts()}${a.reset} ${icon}${a.reset} ${col}${name}${a.reset} ${status}${summary}`, `AGENT_END: ${name} ${status}`);
|
|
348
370
|
return;
|
|
349
371
|
}
|
|
350
372
|
if (msg.subtype === 'task_progress' && msg.description) {
|
|
351
|
-
this.
|
|
373
|
+
this.push(`${this.indent()} ${a.dim}${trunc(msg.description, 70)}${a.reset}`, `PROGRESS: ${msg.description}`);
|
|
352
374
|
return;
|
|
353
375
|
}
|
|
354
376
|
if (msg.subtype === 'api_retry') {
|
|
355
|
-
this.
|
|
377
|
+
this.push(` ${a.fg.gray}${ts()}${a.reset} ${a.fg.yellow}↻${a.reset} ${a.fg.yellow}API retry ${msg.attempt}/${msg.max_retries} (${msg.error || ''})${a.reset}`);
|
|
356
378
|
return;
|
|
357
379
|
}
|
|
358
380
|
}
|
|
@@ -360,12 +382,6 @@ export class TUI {
|
|
|
360
382
|
if (msg.session_id)
|
|
361
383
|
this.fileOnly(`SESSION: ${msg.session_id}`);
|
|
362
384
|
}
|
|
363
|
-
// ─── Indent ───
|
|
364
|
-
indent() {
|
|
365
|
-
if (this.agents.length === 0)
|
|
366
|
-
return ' ';
|
|
367
|
-
return ' ' + this.agents.map(n => `${agentColor(n)}│${a.reset} `).join('');
|
|
368
|
-
}
|
|
369
385
|
resetAgentStack() { this.agents = []; }
|
|
370
386
|
getSessionId(msg) { return msg.session_id ?? null; }
|
|
371
387
|
}
|