agileflow 2.90.0 → 2.90.1

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.
@@ -0,0 +1,390 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * AgileFlow Simple TUI - Terminal User Interface
6
+ *
7
+ * A simple terminal-based dashboard that works without React/ink dependencies.
8
+ * Uses ANSI escape codes for styling and keyboard input.
9
+ *
10
+ * Usage:
11
+ * node scripts/tui/simple-tui.js
12
+ * npx agileflow tui
13
+ *
14
+ * Key bindings:
15
+ * q - Quit TUI
16
+ * s - Start loop on current story
17
+ * p - Pause active loop
18
+ * r - Resume paused loop
19
+ * t - Toggle trace panel
20
+ * 1-9 - Switch session focus
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+ const readline = require('readline');
26
+
27
+ // ANSI escape codes
28
+ const ANSI = {
29
+ clear: '\x1b[2J',
30
+ home: '\x1b[H',
31
+ reset: '\x1b[0m',
32
+ bold: '\x1b[1m',
33
+ dim: '\x1b[2m',
34
+ italic: '\x1b[3m',
35
+ underline: '\x1b[4m',
36
+ // Colors
37
+ black: '\x1b[30m',
38
+ red: '\x1b[31m',
39
+ green: '\x1b[32m',
40
+ yellow: '\x1b[33m',
41
+ blue: '\x1b[34m',
42
+ magenta: '\x1b[35m',
43
+ cyan: '\x1b[36m',
44
+ white: '\x1b[37m',
45
+ gray: '\x1b[90m',
46
+ // Backgrounds
47
+ bgBlack: '\x1b[40m',
48
+ bgRed: '\x1b[41m',
49
+ bgGreen: '\x1b[42m',
50
+ bgYellow: '\x1b[43m',
51
+ bgBlue: '\x1b[44m',
52
+ bgCyan: '\x1b[46m',
53
+ // Cursor
54
+ hideCursor: '\x1b[?25l',
55
+ showCursor: '\x1b[?25h',
56
+ saveCursor: '\x1b[s',
57
+ restoreCursor: '\x1b[u',
58
+ };
59
+
60
+ // Get project root
61
+ function getProjectRoot() {
62
+ return process.cwd();
63
+ }
64
+
65
+ // Get sessions from registry
66
+ function getSessions() {
67
+ try {
68
+ const registryPath = path.join(getProjectRoot(), '.agileflow', 'sessions', 'registry.json');
69
+ if (!fs.existsSync(registryPath)) {
70
+ return [];
71
+ }
72
+ const data = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
73
+ const sessions = data.sessions || {};
74
+ // Convert object to array (registry stores sessions as object with ID keys)
75
+ if (Array.isArray(sessions)) {
76
+ return sessions;
77
+ }
78
+ return Object.entries(sessions).map(([id, session]) => ({
79
+ id,
80
+ ...session,
81
+ }));
82
+ } catch (e) {
83
+ return [];
84
+ }
85
+ }
86
+
87
+ // Get loop status
88
+ function getLoopStatus() {
89
+ try {
90
+ const statePath = path.join(getProjectRoot(), 'docs', '09-agents', 'session-state.json');
91
+ if (!fs.existsSync(statePath)) {
92
+ return { active: false };
93
+ }
94
+ const data = JSON.parse(fs.readFileSync(statePath, 'utf8'));
95
+ const loop = data.ralph_loop;
96
+ if (!loop || !loop.enabled) {
97
+ return { active: false };
98
+ }
99
+ return {
100
+ active: true,
101
+ paused: loop.paused || false,
102
+ epic: loop.epic,
103
+ currentStory: loop.current_story,
104
+ iteration: loop.iteration || 0,
105
+ maxIterations: loop.max_iterations || 20,
106
+ };
107
+ } catch (e) {
108
+ return { active: false };
109
+ }
110
+ }
111
+
112
+ // Get recent agent events
113
+ function getAgentEvents(limit = 10) {
114
+ try {
115
+ const logPath = path.join(getProjectRoot(), 'docs', '09-agents', 'bus', 'log.jsonl');
116
+ if (!fs.existsSync(logPath)) {
117
+ return [];
118
+ }
119
+ const content = fs.readFileSync(logPath, 'utf8');
120
+ const lines = content.trim().split('\n').filter(Boolean);
121
+ const events = [];
122
+ for (const line of lines.slice(-limit)) {
123
+ try {
124
+ events.push(JSON.parse(line));
125
+ } catch (e) {
126
+ // Skip invalid JSON
127
+ }
128
+ }
129
+ return events;
130
+ } catch (e) {
131
+ return [];
132
+ }
133
+ }
134
+
135
+ // Draw box with border
136
+ function drawBox(x, y, width, height, title = '', color = 'cyan') {
137
+ const colorCode = ANSI[color] || ANSI.cyan;
138
+ const lines = [];
139
+
140
+ // Top border
141
+ lines.push(`${colorCode}+${'─'.repeat(width - 2)}+${ANSI.reset}`);
142
+
143
+ // Title if provided
144
+ if (title) {
145
+ const titleStr = ` ${title} `;
146
+ const paddingLeft = Math.floor((width - 2 - titleStr.length) / 2);
147
+ const paddingRight = width - 2 - titleStr.length - paddingLeft;
148
+ lines[0] = `${colorCode}+${'─'.repeat(paddingLeft)}${ANSI.bold}${titleStr}${ANSI.reset}${colorCode}${'─'.repeat(paddingRight)}+${ANSI.reset}`;
149
+ }
150
+
151
+ // Content lines
152
+ for (let i = 0; i < height - 2; i++) {
153
+ lines.push(`${colorCode}│${ANSI.reset}${' '.repeat(width - 2)}${colorCode}│${ANSI.reset}`);
154
+ }
155
+
156
+ // Bottom border
157
+ lines.push(`${colorCode}+${'─'.repeat(width - 2)}+${ANSI.reset}`);
158
+
159
+ return lines;
160
+ }
161
+
162
+ // Pad string to width
163
+ function pad(str, width, align = 'left') {
164
+ const s = String(str).slice(0, width);
165
+ const padding = width - s.length;
166
+ if (align === 'right') return ' '.repeat(padding) + s;
167
+ if (align === 'center') return ' '.repeat(Math.floor(padding / 2)) + s + ' '.repeat(Math.ceil(padding / 2));
168
+ return s + ' '.repeat(padding);
169
+ }
170
+
171
+ // Progress bar
172
+ function progressBar(value, max, width = 20) {
173
+ const percent = Math.min(100, Math.max(0, (value / max) * 100));
174
+ const filled = Math.round((percent / 100) * width);
175
+ const empty = width - filled;
176
+
177
+ let color = ANSI.red;
178
+ if (percent >= 80) color = ANSI.green;
179
+ else if (percent >= 50) color = ANSI.yellow;
180
+
181
+ return `${color}${'█'.repeat(filled)}${ANSI.dim}${'░'.repeat(empty)}${ANSI.reset} ${percent.toFixed(0)}%`;
182
+ }
183
+
184
+ // Main TUI class
185
+ class SimpleTUI {
186
+ constructor() {
187
+ this.running = false;
188
+ this.showTrace = true;
189
+ this.lastUpdate = new Date();
190
+ this.messages = [];
191
+ }
192
+
193
+ start() {
194
+ this.running = true;
195
+
196
+ // Set up terminal
197
+ process.stdout.write(ANSI.clear + ANSI.home + ANSI.hideCursor);
198
+
199
+ // Enable raw mode for keyboard input
200
+ if (process.stdin.isTTY) {
201
+ readline.emitKeypressEvents(process.stdin);
202
+ process.stdin.setRawMode(true);
203
+ }
204
+
205
+ // Handle keyboard input
206
+ process.stdin.on('keypress', (str, key) => {
207
+ this.handleKey(key);
208
+ });
209
+
210
+ // Handle terminal resize
211
+ process.stdout.on('resize', () => {
212
+ this.render();
213
+ });
214
+
215
+ // Initial render
216
+ this.render();
217
+
218
+ // Update loop
219
+ this.updateInterval = setInterval(() => {
220
+ this.render();
221
+ }, 2000);
222
+
223
+ this.addMessage('TUI', 'Dashboard started');
224
+ }
225
+
226
+ stop() {
227
+ this.running = false;
228
+
229
+ if (this.updateInterval) {
230
+ clearInterval(this.updateInterval);
231
+ }
232
+
233
+ // Restore terminal
234
+ process.stdout.write(ANSI.showCursor + ANSI.clear + ANSI.home);
235
+
236
+ if (process.stdin.isTTY) {
237
+ process.stdin.setRawMode(false);
238
+ }
239
+
240
+ console.log('AgileFlow TUI closed.');
241
+ process.exit(0);
242
+ }
243
+
244
+ handleKey(key) {
245
+ if (!key) return;
246
+
247
+ const k = key.name || key.sequence;
248
+
249
+ switch (k) {
250
+ case 'q':
251
+ case 'escape':
252
+ this.stop();
253
+ break;
254
+ case 't':
255
+ this.showTrace = !this.showTrace;
256
+ this.addMessage('TUI', `Trace panel ${this.showTrace ? 'shown' : 'hidden'}`);
257
+ this.render();
258
+ break;
259
+ case 's':
260
+ this.addMessage('TUI', 'Start loop requested (not implemented)');
261
+ this.render();
262
+ break;
263
+ case 'p':
264
+ this.addMessage('TUI', 'Pause requested (not implemented)');
265
+ this.render();
266
+ break;
267
+ case 'r':
268
+ this.addMessage('TUI', 'Resume requested (not implemented)');
269
+ this.render();
270
+ break;
271
+ case 'c':
272
+ if (key.ctrl) {
273
+ this.stop();
274
+ }
275
+ break;
276
+ }
277
+ }
278
+
279
+ addMessage(agent, message) {
280
+ const timestamp = new Date().toLocaleTimeString();
281
+ this.messages.push({ timestamp, agent, message });
282
+ if (this.messages.length > 50) {
283
+ this.messages.shift();
284
+ }
285
+ }
286
+
287
+ render() {
288
+ const width = process.stdout.columns || 80;
289
+ const height = process.stdout.rows || 24;
290
+ const output = [];
291
+
292
+ // Clear screen
293
+ output.push(ANSI.clear + ANSI.home);
294
+
295
+ // Header
296
+ const title = ' AgileFlow TUI ';
297
+ const headerPadding = Math.floor((width - title.length) / 2);
298
+ output.push(`${ANSI.bgCyan}${ANSI.black}${'═'.repeat(headerPadding)}${ANSI.bold}${title}${ANSI.reset}${ANSI.bgCyan}${ANSI.black}${'═'.repeat(width - headerPadding - title.length)}${ANSI.reset}`);
299
+ output.push('');
300
+
301
+ // Calculate panel widths
302
+ const leftWidth = Math.floor(width * 0.4);
303
+ const rightWidth = width - leftWidth - 1;
304
+ const panelHeight = height - 6; // Leave room for header and footer
305
+
306
+ // Get data
307
+ const sessions = getSessions();
308
+ const loopStatus = getLoopStatus();
309
+ const agentEvents = getAgentEvents(8);
310
+
311
+ // Build left panel (sessions)
312
+ output.push(`${ANSI.cyan}${ANSI.bold}┌─ SESSIONS ─${'─'.repeat(leftWidth - 14)}┐${ANSI.reset}`);
313
+
314
+ if (sessions.length === 0) {
315
+ output.push(`${ANSI.cyan}│${ANSI.reset} ${ANSI.dim}No active sessions${ANSI.reset}${' '.repeat(leftWidth - 21)}${ANSI.cyan}│${ANSI.reset}`);
316
+ } else {
317
+ for (const session of sessions.slice(0, 5)) {
318
+ const indicator = session.current ? `${ANSI.green}>` : ' ';
319
+ const name = `Session ${session.id}${session.is_main ? ' [main]' : ''}`;
320
+ const branch = session.branch || 'unknown';
321
+ const story = session.story || 'none';
322
+
323
+ output.push(`${ANSI.cyan}│${ANSI.reset} ${indicator} ${ANSI.bold}${pad(name, leftWidth - 6)}${ANSI.reset}${ANSI.cyan}│${ANSI.reset}`);
324
+ output.push(`${ANSI.cyan}│${ANSI.reset} ${ANSI.dim}Branch:${ANSI.reset} ${ANSI.cyan}${pad(branch, leftWidth - 12)}${ANSI.reset}${ANSI.cyan}│${ANSI.reset}`);
325
+ output.push(`${ANSI.cyan}│${ANSI.reset} ${ANSI.dim}Story:${ANSI.reset} ${ANSI.yellow}${pad(story, leftWidth - 12)}${ANSI.reset}${ANSI.cyan}│${ANSI.reset}`);
326
+ }
327
+ }
328
+
329
+ // Fill remaining space in left panel
330
+ const usedRows = sessions.length === 0 ? 1 : Math.min(sessions.length, 5) * 3;
331
+ const remainingRows = Math.max(0, panelHeight - usedRows - 2);
332
+ for (let i = 0; i < remainingRows; i++) {
333
+ output.push(`${ANSI.cyan}│${ANSI.reset}${' '.repeat(leftWidth - 2)}${ANSI.cyan}│${ANSI.reset}`);
334
+ }
335
+
336
+ output.push(`${ANSI.cyan}└${'─'.repeat(leftWidth - 2)}┘${ANSI.reset}`);
337
+
338
+ // Move cursor to right panel position and draw
339
+ // For simplicity, we'll draw the right panel below the left panel
340
+ output.push('');
341
+ output.push(`${ANSI.green}${ANSI.bold}┌─ AGENT OUTPUT ─${'─'.repeat(width - 19)}┐${ANSI.reset}`);
342
+
343
+ if (agentEvents.length === 0 && this.messages.length === 0) {
344
+ output.push(`${ANSI.green}│${ANSI.reset} ${ANSI.dim}Waiting for agent activity...${ANSI.reset}${' '.repeat(width - 34)}${ANSI.green}│${ANSI.reset}`);
345
+ } else {
346
+ // Show recent events
347
+ const allMessages = [...agentEvents.map(e => ({
348
+ timestamp: e.timestamp ? new Date(e.timestamp).toLocaleTimeString() : '',
349
+ agent: e.agent || 'unknown',
350
+ message: e.message || (e.event === 'iteration' ? `Iteration ${e.iter}` : e.event || JSON.stringify(e))
351
+ })), ...this.messages].slice(-8);
352
+
353
+ for (const msg of allMessages) {
354
+ const line = `[${msg.timestamp}] [${ANSI.cyan}${msg.agent}${ANSI.reset}] ${msg.message}`;
355
+ const cleanLine = `[${msg.timestamp}] [${msg.agent}] ${msg.message}`;
356
+ const padding = width - cleanLine.length - 4;
357
+ output.push(`${ANSI.green}│${ANSI.reset} ${line}${' '.repeat(Math.max(0, padding))}${ANSI.green}│${ANSI.reset}`);
358
+ }
359
+ }
360
+
361
+ output.push(`${ANSI.green}└${'─'.repeat(width - 2)}┘${ANSI.reset}`);
362
+
363
+ // Loop status (if active)
364
+ if (loopStatus.active) {
365
+ output.push('');
366
+ const statusIcon = loopStatus.paused ? `${ANSI.yellow}||${ANSI.reset}` : `${ANSI.green}>${ANSI.reset}`;
367
+ output.push(`${statusIcon} ${ANSI.bold}Loop:${ANSI.reset} ${loopStatus.epic || 'unknown'} | Story: ${loopStatus.currentStory || 'none'} | ${progressBar(loopStatus.iteration, loopStatus.maxIterations, 15)}`);
368
+ }
369
+
370
+ // Footer with key bindings
371
+ output.push('');
372
+ output.push(`${ANSI.dim}[Q]uit [S]tart [P]ause [R]esume [T]race [1-9]Sessions ${ANSI.reset}${ANSI.cyan}AgileFlow v2.90.0${ANSI.reset}`);
373
+
374
+ // Output everything
375
+ process.stdout.write(output.join('\n'));
376
+ }
377
+ }
378
+
379
+ // Main entry point
380
+ function main() {
381
+ const tui = new SimpleTUI();
382
+ tui.start();
383
+ }
384
+
385
+ // Run if executed directly
386
+ if (require.main === module) {
387
+ main();
388
+ }
389
+
390
+ module.exports = { SimpleTUI, main };
@@ -283,4 +283,3 @@ async function handleSet(directory, status, manifestPath, key, value) {
283
283
 
284
284
  console.log();
285
285
  }
286
-
@@ -369,7 +369,6 @@ function compareVersions(a, b) {
369
369
  return 0;
370
370
  }
371
371
 
372
-
373
372
  /**
374
373
  * Count files in directory recursively
375
374
  * @param {string} dirPath - Directory path
@@ -402,16 +401,26 @@ function printSummary(issues, warnings) {
402
401
  if (issues === 0 && warnings === 0) {
403
402
  console.log(chalk.green.bold('No issues found.\n'));
404
403
  } else if (issues === 0) {
405
- console.log(formatKeyValue({
406
- Warnings: chalk.yellow(warnings),
407
- Issues: chalk.green('0'),
408
- }, { separator: ':', alignValues: false }));
404
+ console.log(
405
+ formatKeyValue(
406
+ {
407
+ Warnings: chalk.yellow(warnings),
408
+ Issues: chalk.green('0'),
409
+ },
410
+ { separator: ':', alignValues: false }
411
+ )
412
+ );
409
413
  console.log();
410
414
  } else {
411
- console.log(formatKeyValue({
412
- Issues: chalk.red(issues),
413
- Warnings: chalk.yellow(warnings),
414
- }, { separator: ':', alignValues: false }));
415
+ console.log(
416
+ formatKeyValue(
417
+ {
418
+ Issues: chalk.red(issues),
419
+ Warnings: chalk.yellow(warnings),
420
+ },
421
+ { separator: ':', alignValues: false }
422
+ )
423
+ );
415
424
  console.log();
416
425
  }
417
426
  }
@@ -36,17 +36,24 @@ module.exports = {
36
36
  }
37
37
 
38
38
  // Show installation info using formatKeyValue
39
- console.log(formatKeyValue({
40
- Location: status.path,
41
- Version: status.version,
42
- }));
39
+ console.log(
40
+ formatKeyValue({
41
+ Location: status.path,
42
+ Version: status.version,
43
+ })
44
+ );
43
45
 
44
46
  // Count installed items
45
47
  const counts = await installer.countInstalledItems(status.path);
46
48
 
47
- console.log(formatKeyValue({
48
- '\nCore': chalk.green('✓ Installed'),
49
- }, { alignValues: false }));
49
+ console.log(
50
+ formatKeyValue(
51
+ {
52
+ '\nCore': chalk.green('✓ Installed'),
53
+ },
54
+ { alignValues: false }
55
+ )
56
+ );
50
57
  info(`${counts.agents} agents`);
51
58
  info(`${counts.commands} commands`);
52
59
  info(`${counts.skills} skills`);
@@ -92,4 +99,3 @@ module.exports = {
92
99
  }
93
100
  },
94
101
  };
95
-
@@ -0,0 +1,59 @@
1
+ /**
2
+ * AgileFlow CLI - TUI Command
3
+ *
4
+ * Launches the Terminal User Interface for real-time session monitoring.
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('node:path');
9
+ const { spawn } = require('node:child_process');
10
+ const fs = require('fs-extra');
11
+
12
+ module.exports = {
13
+ name: 'tui',
14
+ description: 'Launch Terminal User Interface for session monitoring',
15
+ options: [['-d, --directory <path>', 'Project directory (default: current directory)']],
16
+ action: async options => {
17
+ try {
18
+ const directory = path.resolve(options.directory || '.');
19
+
20
+ // Check if AgileFlow is installed
21
+ const agileflowDir = path.join(directory, '.agileflow');
22
+ if (!(await fs.pathExists(agileflowDir))) {
23
+ console.error(chalk.red('Error:'), 'AgileFlow is not installed in this directory');
24
+ console.log(chalk.dim(`Run 'npx agileflow setup' first\n`));
25
+ process.exit(1);
26
+ }
27
+
28
+ // Find the TUI script
29
+ const tuiScript = path.join(__dirname, '../../..', 'scripts', 'tui', 'index.js');
30
+
31
+ if (!(await fs.pathExists(tuiScript))) {
32
+ console.error(chalk.red('Error:'), 'TUI script not found');
33
+ process.exit(1);
34
+ }
35
+
36
+ // Spawn the TUI process with inherited stdio for interactive mode
37
+ const child = spawn('node', [tuiScript], {
38
+ cwd: directory,
39
+ stdio: 'inherit',
40
+ env: { ...process.env, FORCE_COLOR: '1' },
41
+ });
42
+
43
+ child.on('error', err => {
44
+ console.error(chalk.red('Error launching TUI:'), err.message);
45
+ process.exit(1);
46
+ });
47
+
48
+ child.on('exit', code => {
49
+ process.exit(code || 0);
50
+ });
51
+ } catch (err) {
52
+ console.error(chalk.red('Error:'), err.message);
53
+ if (process.env.DEBUG) {
54
+ console.error(err.stack);
55
+ }
56
+ process.exit(1);
57
+ }
58
+ },
59
+ };
@@ -51,7 +51,10 @@ module.exports = {
51
51
 
52
52
  // Confirm removal
53
53
  if (!options.force) {
54
- const proceed = await confirm(`Remove ${IdeRegistry.getDisplayName(ideName)} configuration?`, false);
54
+ const proceed = await confirm(
55
+ `Remove ${IdeRegistry.getDisplayName(ideName)} configuration?`,
56
+ false
57
+ );
55
58
  if (!proceed) {
56
59
  console.log(chalk.dim('\nCancelled\n'));
57
60
  process.exit(0);
@@ -150,4 +153,3 @@ module.exports = {
150
153
  }
151
154
  },
152
155
  };
153
-
@@ -193,7 +193,15 @@ async function executeMiddleware(middlewares, ctx) {
193
193
  * @returns {Object} Commander.js compatible command object
194
194
  */
195
195
  function createCommand(definition) {
196
- const { name, description, options = [], arguments: args = [], middleware: mw = [], validate, action } = definition;
196
+ const {
197
+ name,
198
+ description,
199
+ options = [],
200
+ arguments: args = [],
201
+ middleware: mw = [],
202
+ validate,
203
+ action,
204
+ } = definition;
197
205
 
198
206
  // Build middleware pipeline
199
207
  const pipeline = [...mw];
@@ -80,7 +80,8 @@ async function getLatestVersion(packageName) {
80
80
  suggestion: 'Check network connection. If error persists, try: npm cache clean --force',
81
81
  };
82
82
  if (err.code === 'CERT_HAS_EXPIRED' || err.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
83
- errorInfo.suggestion = 'TLS certificate error - check system time or update CA certificates';
83
+ errorInfo.suggestion =
84
+ 'TLS certificate error - check system time or update CA certificates';
84
85
  }
85
86
  debugLog('Network error', errorInfo);
86
87
  resolve(null);
@@ -1,91 +0,0 @@
1
- ---
2
- description: Launch Terminal User Interface for real-time session monitoring
3
- argument-hint:
4
- ---
5
-
6
- # /agileflow:tui
7
-
8
- Launch the AgileFlow Terminal User Interface for real-time session visualization.
9
-
10
- ---
11
-
12
- ## Purpose
13
-
14
- The TUI provides a unified dashboard for:
15
- - **Session monitoring** - See all active sessions and their current stories
16
- - **Agent visualization** - Watch multi-agent orchestration in real-time
17
- - **Loop control** - Pause, resume, and manage autonomous loops
18
- - **Event streaming** - Live feed of agent activity and test results
19
-
20
- ---
21
-
22
- ## IMMEDIATE ACTIONS
23
-
24
- Upon invocation, execute these steps:
25
-
26
- ### Step 1: Launch TUI
27
-
28
- ```bash
29
- node packages/cli/scripts/tui/index.js
30
- ```
31
-
32
- ### Step 2: Display Key Bindings
33
-
34
- The TUI shows key bindings in the footer:
35
- - **Q** - Quit TUI
36
- - **S** - Start loop on current story
37
- - **P** - Pause active loop
38
- - **R** - Resume paused loop
39
- - **T** - Toggle trace panel
40
- - **1-9** - Switch session focus
41
-
42
- ---
43
-
44
- ## Key Features
45
-
46
- ### Session Overview Panel
47
- - Lists all active sessions with ID, branch, and current story
48
- - Color coding by thread type (base=green, parallel=cyan)
49
- - Shows loop progress and iteration count
50
-
51
- ### Agent Output Panel
52
- - Real-time stream of agent messages
53
- - Timestamps and agent identification
54
- - Auto-scroll with history buffer
55
-
56
- ### Trace Panel (Toggle with T)
57
- - Detailed view of agent loops and quality gates
58
- - Progress bars for each gate (tests, coverage, lint, types)
59
- - Pass/fail status indicators
60
-
61
- ---
62
-
63
- ## Example Session
64
-
65
- ```
66
- ┌─────────────────────────────────────────────────────────────────────┐
67
- │ AgileFlow TUI │
68
- ├─────────────────────────────┬───────────────────────────────────────┤
69
- │ SESSIONS │ AGENT OUTPUT │
70
- │ ───────── │ ──────────── │
71
- │ ▶ Session 1 [main] │ [agileflow-api] Reading status.json │
72
- │ Story: US-0115 │ [agileflow-api] Running tests... │
73
- │ Loop: 3/20 iterations │ ✓ 47 tests passed │
74
- ├─────────────────────────────┴───────────────────────────────────────┤
75
- │ [S]tart [P]ause [R]esume [T]race [Q]uit │
76
- └─────────────────────────────────────────────────────────────────────┘
77
- ```
78
-
79
- ---
80
-
81
- ## Related Commands
82
-
83
- - `/agileflow:babysit MODE=loop` - Autonomous story processing
84
- - `/agileflow:session:status` - Session information (non-TUI)
85
- - `/agileflow:session:new` - Create parallel sessions
86
-
87
- ---
88
-
89
- ## Research
90
-
91
- Based on Ralph TUI research: `docs/10-research/20260114-ralph-loop-ralph-tui.md`