agileflow 2.75.0 → 2.77.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.
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ralph-loop.js - Autonomous Story Processing Loop
5
+ *
6
+ * This script is the brain of AgileFlow's autonomous work mode.
7
+ * It runs as a Stop hook and handles:
8
+ * 1. Checking if loop mode is enabled
9
+ * 2. Running test validation
10
+ * 3. Updating story status on success
11
+ * 4. Feeding context back for next iteration
12
+ * 5. Tracking iterations and enforcing limits
13
+ *
14
+ * Named after the "Ralph Wiggum" pattern from Anthropic.
15
+ *
16
+ * Usage (as Stop hook):
17
+ * node scripts/ralph-loop.js
18
+ *
19
+ * Manual control:
20
+ * node scripts/ralph-loop.js --status # Check loop status
21
+ * node scripts/ralph-loop.js --stop # Stop the loop
22
+ * node scripts/ralph-loop.js --reset # Reset loop state
23
+ */
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+ const { execSync, spawnSync } = require('child_process');
28
+
29
+ // ANSI colors
30
+ const c = {
31
+ reset: '\x1b[0m',
32
+ bold: '\x1b[1m',
33
+ dim: '\x1b[2m',
34
+ red: '\x1b[31m',
35
+ green: '\x1b[32m',
36
+ yellow: '\x1b[33m',
37
+ blue: '\x1b[34m',
38
+ cyan: '\x1b[36m',
39
+ brand: '\x1b[38;2;232;104;58m',
40
+ };
41
+
42
+ // Find project root
43
+ function getProjectRoot() {
44
+ let dir = process.cwd();
45
+ while (!fs.existsSync(path.join(dir, '.agileflow')) && dir !== '/') {
46
+ dir = path.dirname(dir);
47
+ }
48
+ return dir !== '/' ? dir : process.cwd();
49
+ }
50
+
51
+ // Read session state
52
+ function getSessionState(rootDir) {
53
+ const statePath = path.join(rootDir, 'docs/09-agents/session-state.json');
54
+ try {
55
+ if (fs.existsSync(statePath)) {
56
+ return JSON.parse(fs.readFileSync(statePath, 'utf8'));
57
+ }
58
+ } catch (e) {}
59
+ return {};
60
+ }
61
+
62
+ // Write session state
63
+ function saveSessionState(rootDir, state) {
64
+ const statePath = path.join(rootDir, 'docs/09-agents/session-state.json');
65
+ const dir = path.dirname(statePath);
66
+ if (!fs.existsSync(dir)) {
67
+ fs.mkdirSync(dir, { recursive: true });
68
+ }
69
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n');
70
+ }
71
+
72
+ // Read status.json for stories
73
+ function getStatus(rootDir) {
74
+ const statusPath = path.join(rootDir, 'docs/09-agents/status.json');
75
+ try {
76
+ if (fs.existsSync(statusPath)) {
77
+ return JSON.parse(fs.readFileSync(statusPath, 'utf8'));
78
+ }
79
+ } catch (e) {}
80
+ return { stories: {}, epics: {} };
81
+ }
82
+
83
+ // Save status.json
84
+ function saveStatus(rootDir, status) {
85
+ const statusPath = path.join(rootDir, 'docs/09-agents/status.json');
86
+ fs.writeFileSync(statusPath, JSON.stringify(status, null, 2) + '\n');
87
+ }
88
+
89
+ // Get test command from package.json or metadata
90
+ function getTestCommand(rootDir) {
91
+ // Check agileflow metadata first
92
+ try {
93
+ const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
94
+ if (fs.existsSync(metadataPath)) {
95
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
96
+ if (metadata.ralph_loop?.test_command) {
97
+ return metadata.ralph_loop.test_command;
98
+ }
99
+ }
100
+ } catch (e) {}
101
+
102
+ // Default to npm test
103
+ return 'npm test';
104
+ }
105
+
106
+ // Run tests and return result
107
+ function runTests(rootDir, testCommand) {
108
+ const result = { passed: false, output: '', duration: 0 };
109
+ const startTime = Date.now();
110
+
111
+ try {
112
+ const output = execSync(testCommand, {
113
+ cwd: rootDir,
114
+ encoding: 'utf8',
115
+ stdio: ['pipe', 'pipe', 'pipe'],
116
+ timeout: 300000, // 5 minute timeout
117
+ });
118
+ result.passed = true;
119
+ result.output = output;
120
+ } catch (e) {
121
+ result.passed = false;
122
+ result.output = e.stdout || '' + '\n' + (e.stderr || '');
123
+ if (e.message) {
124
+ result.output += '\n' + e.message;
125
+ }
126
+ }
127
+
128
+ result.duration = Date.now() - startTime;
129
+ return result;
130
+ }
131
+
132
+ // Get next ready story in epic
133
+ function getNextStory(status, epicId, currentStoryId) {
134
+ const stories = status.stories || {};
135
+
136
+ // Get all stories in this epic that are ready
137
+ const readyStories = Object.entries(stories)
138
+ .filter(([id, story]) => {
139
+ return story.epic === epicId && story.status === 'ready' && id !== currentStoryId;
140
+ })
141
+ .sort((a, b) => {
142
+ // Sort by story number if possible
143
+ const numA = parseInt(a[0].replace(/\D/g, '')) || 0;
144
+ const numB = parseInt(b[0].replace(/\D/g, '')) || 0;
145
+ return numA - numB;
146
+ });
147
+
148
+ if (readyStories.length > 0) {
149
+ return { id: readyStories[0][0], ...readyStories[0][1] };
150
+ }
151
+ return null;
152
+ }
153
+
154
+ // Mark story as completed
155
+ function markStoryComplete(rootDir, storyId) {
156
+ const status = getStatus(rootDir);
157
+ if (status.stories && status.stories[storyId]) {
158
+ status.stories[storyId].status = 'completed';
159
+ status.stories[storyId].completed_at = new Date().toISOString();
160
+ saveStatus(rootDir, status);
161
+ return true;
162
+ }
163
+ return false;
164
+ }
165
+
166
+ // Mark story as in_progress
167
+ function markStoryInProgress(rootDir, storyId) {
168
+ const status = getStatus(rootDir);
169
+ if (status.stories && status.stories[storyId]) {
170
+ status.stories[storyId].status = 'in_progress';
171
+ status.stories[storyId].started_at = new Date().toISOString();
172
+ saveStatus(rootDir, status);
173
+ return true;
174
+ }
175
+ return false;
176
+ }
177
+
178
+ // Get story details
179
+ function getStoryDetails(rootDir, storyId) {
180
+ const status = getStatus(rootDir);
181
+ if (status.stories && status.stories[storyId]) {
182
+ return { id: storyId, ...status.stories[storyId] };
183
+ }
184
+ return null;
185
+ }
186
+
187
+ // Count stories in epic by status
188
+ function getEpicProgress(status, epicId) {
189
+ const stories = status.stories || {};
190
+ const epicStories = Object.entries(stories).filter(([_, s]) => s.epic === epicId);
191
+
192
+ return {
193
+ total: epicStories.length,
194
+ completed: epicStories.filter(([_, s]) => s.status === 'completed').length,
195
+ in_progress: epicStories.filter(([_, s]) => s.status === 'in_progress').length,
196
+ ready: epicStories.filter(([_, s]) => s.status === 'ready').length,
197
+ blocked: epicStories.filter(([_, s]) => s.status === 'blocked').length,
198
+ };
199
+ }
200
+
201
+ // Main loop logic
202
+ function handleLoop(rootDir) {
203
+ const state = getSessionState(rootDir);
204
+ const loop = state.ralph_loop;
205
+
206
+ // Check if loop mode is enabled
207
+ if (!loop || !loop.enabled) {
208
+ return; // Silent exit - not in loop mode
209
+ }
210
+
211
+ const status = getStatus(rootDir);
212
+ const iteration = (loop.iteration || 0) + 1;
213
+ const maxIterations = loop.max_iterations || 20;
214
+
215
+ console.log('');
216
+ console.log(
217
+ `${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`
218
+ );
219
+ console.log(
220
+ `${c.brand}${c.bold} RALPH LOOP - Iteration ${iteration}/${maxIterations}${c.reset}`
221
+ );
222
+ console.log(
223
+ `${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`
224
+ );
225
+ console.log('');
226
+
227
+ // Check iteration limit
228
+ if (iteration > maxIterations) {
229
+ console.log(`${c.yellow}⚠ Max iterations (${maxIterations}) reached. Stopping loop.${c.reset}`);
230
+ console.log(`${c.dim}Run /agileflow:babysit MODE=loop to restart${c.reset}`);
231
+ state.ralph_loop.enabled = false;
232
+ state.ralph_loop.stopped_reason = 'max_iterations';
233
+ saveSessionState(rootDir, state);
234
+ return;
235
+ }
236
+
237
+ // Get current story
238
+ const currentStoryId = loop.current_story;
239
+ const currentStory = getStoryDetails(rootDir, currentStoryId);
240
+ const epicId = loop.epic;
241
+
242
+ if (!currentStory) {
243
+ console.log(`${c.red}✗ Current story ${currentStoryId} not found${c.reset}`);
244
+ state.ralph_loop.enabled = false;
245
+ state.ralph_loop.stopped_reason = 'story_not_found';
246
+ saveSessionState(rootDir, state);
247
+ return;
248
+ }
249
+
250
+ console.log(
251
+ `${c.cyan}Current Story:${c.reset} ${currentStoryId} - ${currentStory.title || 'Untitled'}`
252
+ );
253
+ console.log('');
254
+
255
+ // Run tests
256
+ const testCommand = getTestCommand(rootDir);
257
+ console.log(`${c.blue}Running:${c.reset} ${testCommand}`);
258
+ console.log(`${c.dim}${'─'.repeat(58)}${c.reset}`);
259
+
260
+ const testResult = runTests(rootDir, testCommand);
261
+
262
+ if (testResult.passed) {
263
+ console.log(`${c.green}✓ Tests passed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
264
+ console.log('');
265
+
266
+ // Mark story complete
267
+ markStoryComplete(rootDir, currentStoryId);
268
+ console.log(`${c.green}✓ Marked ${currentStoryId} as completed${c.reset}`);
269
+
270
+ // Get next story
271
+ const nextStory = getNextStory(status, epicId, currentStoryId);
272
+
273
+ if (nextStory) {
274
+ // Move to next story
275
+ markStoryInProgress(rootDir, nextStory.id);
276
+ state.ralph_loop.current_story = nextStory.id;
277
+ state.ralph_loop.iteration = iteration;
278
+ saveSessionState(rootDir, state);
279
+
280
+ const progress = getEpicProgress(getStatus(rootDir), epicId);
281
+ console.log('');
282
+ console.log(`${c.cyan}━━━ Next Story ━━━${c.reset}`);
283
+ console.log(`${c.bold}${nextStory.id}:${c.reset} ${nextStory.title || 'Untitled'}`);
284
+ if (nextStory.acceptance_criteria) {
285
+ console.log(`${c.dim}Acceptance Criteria:${c.reset}`);
286
+ const criteria = Array.isArray(nextStory.acceptance_criteria)
287
+ ? nextStory.acceptance_criteria
288
+ : [nextStory.acceptance_criteria];
289
+ criteria.slice(0, 3).forEach(ac => console.log(` • ${ac}`));
290
+ }
291
+ console.log('');
292
+ console.log(
293
+ `${c.dim}Epic Progress: ${progress.completed}/${progress.total} stories complete${c.reset}`
294
+ );
295
+ console.log('');
296
+ console.log(`${c.brand}▶ Continue implementing ${nextStory.id}${c.reset}`);
297
+ console.log(`${c.dim} Run tests when ready. Loop will validate and continue.${c.reset}`);
298
+ } else {
299
+ // No more stories - epic complete!
300
+ const progress = getEpicProgress(getStatus(rootDir), epicId);
301
+ state.ralph_loop.enabled = false;
302
+ state.ralph_loop.stopped_reason = 'epic_complete';
303
+ state.ralph_loop.completed_at = new Date().toISOString();
304
+ saveSessionState(rootDir, state);
305
+
306
+ console.log('');
307
+ console.log(
308
+ `${c.green}${c.bold}════════════════════════════════════════════════════════${c.reset}`
309
+ );
310
+ console.log(`${c.green}${c.bold} 🎉 EPIC COMPLETE!${c.reset}`);
311
+ console.log(
312
+ `${c.green}${c.bold}════════════════════════════════════════════════════════${c.reset}`
313
+ );
314
+ console.log('');
315
+ console.log(`${c.green}Epic ${epicId} finished in ${iteration} iterations${c.reset}`);
316
+ console.log(`${c.dim}${progress.completed} stories completed${c.reset}`);
317
+ console.log('');
318
+ }
319
+ } else {
320
+ // Tests failed - feed back to Claude
321
+ console.log(`${c.red}✗ Tests failed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
322
+ console.log('');
323
+
324
+ state.ralph_loop.iteration = iteration;
325
+ state.ralph_loop.last_failure = new Date().toISOString();
326
+ saveSessionState(rootDir, state);
327
+
328
+ console.log(`${c.yellow}━━━ Test Failures ━━━${c.reset}`);
329
+
330
+ // Show truncated output (last 50 lines most relevant)
331
+ const outputLines = testResult.output.split('\n');
332
+ const relevantLines = outputLines.slice(-50);
333
+ console.log(relevantLines.join('\n'));
334
+
335
+ console.log('');
336
+ console.log(`${c.brand}▶ Fix the failing tests and continue${c.reset}`);
337
+ console.log(`${c.dim} Loop will re-run tests when you stop.${c.reset}`);
338
+ console.log(`${c.dim} Iteration ${iteration}/${maxIterations}${c.reset}`);
339
+ }
340
+
341
+ console.log('');
342
+ }
343
+
344
+ // Handle CLI arguments
345
+ function handleCLI() {
346
+ const args = process.argv.slice(2);
347
+ const rootDir = getProjectRoot();
348
+
349
+ if (args.includes('--status')) {
350
+ const state = getSessionState(rootDir);
351
+ const loop = state.ralph_loop;
352
+
353
+ if (!loop || !loop.enabled) {
354
+ console.log(`${c.dim}Ralph Loop: not active${c.reset}`);
355
+ } else {
356
+ console.log(`${c.green}Ralph Loop: active${c.reset}`);
357
+ console.log(` Epic: ${loop.epic}`);
358
+ console.log(` Current Story: ${loop.current_story}`);
359
+ console.log(` Iteration: ${loop.iteration || 0}/${loop.max_iterations || 20}`);
360
+ }
361
+ return true;
362
+ }
363
+
364
+ if (args.includes('--stop')) {
365
+ const state = getSessionState(rootDir);
366
+ if (state.ralph_loop) {
367
+ state.ralph_loop.enabled = false;
368
+ state.ralph_loop.stopped_reason = 'manual';
369
+ saveSessionState(rootDir, state);
370
+ console.log(`${c.yellow}Ralph Loop stopped${c.reset}`);
371
+ } else {
372
+ console.log(`${c.dim}Ralph Loop was not active${c.reset}`);
373
+ }
374
+ return true;
375
+ }
376
+
377
+ if (args.includes('--reset')) {
378
+ const state = getSessionState(rootDir);
379
+ delete state.ralph_loop;
380
+ saveSessionState(rootDir, state);
381
+ console.log(`${c.green}Ralph Loop state reset${c.reset}`);
382
+ return true;
383
+ }
384
+
385
+ // Handle --init
386
+ if (args.some(a => a.startsWith('--init'))) {
387
+ const epicArg = args.find(a => a.startsWith('--epic='));
388
+ const maxArg = args.find(a => a.startsWith('--max='));
389
+
390
+ if (!epicArg) {
391
+ console.log(`${c.red}Error: --epic=EP-XXXX is required${c.reset}`);
392
+ return true;
393
+ }
394
+
395
+ const epicId = epicArg.split('=')[1];
396
+ const maxIterations = maxArg ? parseInt(maxArg.split('=')[1]) : 20;
397
+
398
+ // Find first ready story in epic
399
+ const status = getStatus(rootDir);
400
+ const stories = status.stories || {};
401
+ const readyStories = Object.entries(stories)
402
+ .filter(([_, s]) => s.epic === epicId && s.status === 'ready')
403
+ .sort((a, b) => {
404
+ const numA = parseInt(a[0].replace(/\D/g, '')) || 0;
405
+ const numB = parseInt(b[0].replace(/\D/g, '')) || 0;
406
+ return numA - numB;
407
+ });
408
+
409
+ if (readyStories.length === 0) {
410
+ console.log(`${c.yellow}No ready stories found in ${epicId}${c.reset}`);
411
+ console.log(`${c.dim}Create stories with status "ready" first${c.reset}`);
412
+ return true;
413
+ }
414
+
415
+ const firstStory = readyStories[0];
416
+ const storyId = firstStory[0];
417
+
418
+ // Mark first story as in_progress
419
+ markStoryInProgress(rootDir, storyId);
420
+
421
+ // Initialize loop state
422
+ const state = getSessionState(rootDir);
423
+ state.ralph_loop = {
424
+ enabled: true,
425
+ epic: epicId,
426
+ current_story: storyId,
427
+ iteration: 0,
428
+ max_iterations: maxIterations,
429
+ started_at: new Date().toISOString(),
430
+ };
431
+ saveSessionState(rootDir, state);
432
+
433
+ const progress = getEpicProgress(status, epicId);
434
+
435
+ console.log('');
436
+ console.log(`${c.green}${c.bold}Ralph Loop Initialized${c.reset}`);
437
+ console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
438
+ console.log(` Epic: ${c.cyan}${epicId}${c.reset}`);
439
+ console.log(` Stories: ${progress.ready} ready, ${progress.total} total`);
440
+ console.log(` Max Iterations: ${maxIterations}`);
441
+ console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
442
+ console.log('');
443
+ console.log(`${c.brand}▶ Starting Story:${c.reset} ${storyId}`);
444
+ console.log(` ${firstStory[1].title || 'Untitled'}`);
445
+ if (firstStory[1].acceptance_criteria) {
446
+ const criteria = Array.isArray(firstStory[1].acceptance_criteria)
447
+ ? firstStory[1].acceptance_criteria
448
+ : [firstStory[1].acceptance_criteria];
449
+ console.log(`${c.dim} Acceptance Criteria:${c.reset}`);
450
+ criteria.slice(0, 3).forEach(ac => console.log(` • ${ac}`));
451
+ }
452
+ console.log('');
453
+ console.log(
454
+ `${c.dim}Work on this story. When you stop, tests will run automatically.${c.reset}`
455
+ );
456
+ console.log(`${c.dim}If tests pass, the next story will be loaded.${c.reset}`);
457
+ console.log('');
458
+
459
+ return true;
460
+ }
461
+
462
+ if (args.includes('--help')) {
463
+ console.log(`
464
+ ${c.brand}${c.bold}ralph-loop.js${c.reset} - Autonomous Story Processing
465
+
466
+ ${c.bold}Usage:${c.reset}
467
+ node scripts/ralph-loop.js Run loop check (Stop hook)
468
+ node scripts/ralph-loop.js --init --epic=EP-XXX Initialize loop for epic
469
+ node scripts/ralph-loop.js --status Check loop status
470
+ node scripts/ralph-loop.js --stop Stop the loop
471
+ node scripts/ralph-loop.js --reset Reset loop state
472
+
473
+ ${c.bold}Options:${c.reset}
474
+ --epic=EP-XXXX Epic ID to process (required for --init)
475
+ --max=N Max iterations (default: 20)
476
+
477
+ ${c.bold}How it works:${c.reset}
478
+ 1. Start loop with /agileflow:babysit EPIC=EP-XXX MODE=loop
479
+ 2. Work on the current story
480
+ 3. When you stop, this hook runs tests
481
+ 4. If tests pass → story marked complete, next story loaded
482
+ 5. If tests fail → failures shown, you continue fixing
483
+ 6. Loop repeats until epic done or max iterations
484
+ `);
485
+ return true;
486
+ }
487
+
488
+ return false;
489
+ }
490
+
491
+ // Main
492
+ function main() {
493
+ // Handle CLI commands first
494
+ if (handleCLI()) {
495
+ return;
496
+ }
497
+
498
+ // Otherwise run the loop handler
499
+ const rootDir = getProjectRoot();
500
+ handleLoop(rootDir);
501
+ }
502
+
503
+ main();
@@ -22,12 +22,16 @@
22
22
 
23
23
  set -e
24
24
 
25
- # Colors
26
- RED='\033[0;31m'
27
- YELLOW='\033[1;33m'
28
- GREEN='\033[0;32m'
29
- BLUE='\033[0;34m'
30
- NC='\033[0m' # No Color
25
+ # Colors (using vibrant 256-color palette)
26
+ NC='\033[0m' # No Color / Reset
27
+ RED='\033[0;31m' # Standard red (fallback)
28
+ GREEN='\033[0;32m' # Standard green (fallback)
29
+
30
+ # Vibrant 256-color palette
31
+ MINT_GREEN='\033[38;5;158m' # Healthy/success states
32
+ PEACH='\033[38;5;215m' # Warning states
33
+ CORAL='\033[38;5;203m' # Critical/error states
34
+ SKY_BLUE='\033[38;5;117m' # Headers/titles
31
35
 
32
36
  # Configuration
33
37
  EXPERTS_DIR="packages/cli/src/core/experts"
@@ -180,18 +184,18 @@ validate_expertise() {
180
184
  fi
181
185
  fi
182
186
 
183
- # Output result
187
+ # Output result (using vibrant 256-color palette)
184
188
  case "$status" in
185
189
  PASS)
186
- echo -e "${GREEN}PASS${NC} $domain"
190
+ echo -e "${MINT_GREEN}PASS${NC} $domain"
187
191
  PASSED=$((PASSED + 1))
188
192
  ;;
189
193
  WARN)
190
- echo -e "${YELLOW}WARN${NC} $domain - ${issues[*]}"
194
+ echo -e "${PEACH}WARN${NC} $domain - ${issues[*]}"
191
195
  WARNINGS=$((WARNINGS + 1))
192
196
  ;;
193
197
  FAIL)
194
- echo -e "${RED}FAIL${NC} $domain - ${issues[*]}"
198
+ echo -e "${CORAL}FAIL${NC} $domain - ${issues[*]}"
195
199
  FAILED=$((FAILED + 1))
196
200
  ;;
197
201
  esac
@@ -212,12 +216,12 @@ main() {
212
216
 
213
217
  # Check experts directory exists
214
218
  if [ ! -d "$EXPERTS_DIR" ]; then
215
- echo -e "${RED}Error:${NC} Experts directory not found: $EXPERTS_DIR"
219
+ echo -e "${CORAL}Error:${NC} Experts directory not found: $EXPERTS_DIR"
216
220
  echo "Are you running this from the repository root?"
217
221
  exit 1
218
222
  fi
219
223
 
220
- echo -e "${BLUE}Validating Agent Expert Files${NC}"
224
+ echo -e "${SKY_BLUE}Validating Agent Expert Files${NC}"
221
225
  echo "================================"
222
226
  echo ""
223
227
 
@@ -225,7 +229,7 @@ main() {
225
229
  if [ -n "$1" ]; then
226
230
  # Single domain
227
231
  if [ ! -d "$EXPERTS_DIR/$1" ]; then
228
- echo -e "${RED}Error:${NC} Domain not found: $1"
232
+ echo -e "${CORAL}Error:${NC} Domain not found: $1"
229
233
  echo "Available domains:"
230
234
  ls -1 "$EXPERTS_DIR" | grep -v templates | grep -v README
231
235
  exit 1
@@ -244,10 +248,10 @@ main() {
244
248
  done
245
249
  fi
246
250
 
247
- # Summary
251
+ # Summary (using vibrant 256-color palette)
248
252
  echo ""
249
253
  echo "================================"
250
- echo -e "Total: $TOTAL | ${GREEN}Passed: $PASSED${NC} | ${YELLOW}Warnings: $WARNINGS${NC} | ${RED}Failed: $FAILED${NC}"
254
+ echo -e "Total: $TOTAL | ${MINT_GREEN}Passed: $PASSED${NC} | ${PEACH}Warnings: $WARNINGS${NC} | ${CORAL}Failed: $FAILED${NC}"
251
255
 
252
256
  # Exit code
253
257
  if [ "$FAILED" -gt 0 ]; then
@@ -70,7 +70,7 @@ FIRST ACTION PROTOCOL:
70
70
  4. For complete features: Use workflow.md (Plan → Build → Self-Improve)
71
71
  5. After work: Run self-improve.md to update expertise
72
72
 
73
- SLASH COMMANDS: /agileflow:context, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:status
73
+ SLASH COMMANDS: /agileflow:context:full, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:status
74
74
  <!-- COMPACT_SUMMARY_END -->
75
75
 
76
76
  You are AG-DESIGN, the Design Specialist for AgileFlow projects.
@@ -76,7 +76,7 @@ DOCUMENTATION PRINCIPLES:
76
76
  - Include troubleshooting (users will have problems)
77
77
  - Document breaking changes (critical for users)
78
78
 
79
- SLASH COMMANDS: /agileflow:context, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:status
79
+ SLASH COMMANDS: /agileflow:context:full, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:status
80
80
  <!-- COMPACT_SUMMARY_END -->
81
81
 
82
82
  You are AG-DOCUMENTATION, the Documentation Specialist for AgileFlow projects.
@@ -82,7 +82,7 @@ FIRST ACTION PROTOCOL:
82
82
  4. For complete features: Use workflow.md (Plan → Build → Self-Improve)
83
83
  5. After work: Run self-improve.md to update expertise
84
84
 
85
- SLASH COMMANDS: /agileflow:context, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:tech-debt, /agileflow:status
85
+ SLASH COMMANDS: /agileflow:context:full, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:tech-debt, /agileflow:status
86
86
  <!-- COMPACT_SUMMARY_END -->
87
87
 
88
88
  You are AG-INTEGRATIONS, the Integration Specialist for AgileFlow projects.
@@ -84,7 +84,7 @@ FIRST ACTION PROTOCOL:
84
84
  4. For complete features: Use workflow.md (Plan → Build → Self-Improve)
85
85
  5. After work: Run self-improve.md to update expertise
86
86
 
87
- SLASH COMMANDS: /agileflow:context, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:tech-debt, /agileflow:status
87
+ SLASH COMMANDS: /agileflow:context:full, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:tech-debt, /agileflow:status
88
88
  <!-- COMPACT_SUMMARY_END -->
89
89
 
90
90
  You are AG-MOBILE, the Mobile Specialist for AgileFlow projects.
@@ -84,7 +84,7 @@ FIRST ACTION PROTOCOL:
84
84
  4. For complete features: Use workflow.md (Plan → Build → Self-Improve)
85
85
  5. After work: Run self-improve.md to update expertise
86
86
 
87
- SLASH COMMANDS: /agileflow:context, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:status
87
+ SLASH COMMANDS: /agileflow:context:full, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:status
88
88
  <!-- COMPACT_SUMMARY_END -->
89
89
 
90
90
  You are AG-MONITORING, the Monitoring & Observability Specialist for AgileFlow projects.
@@ -91,7 +91,7 @@ FIRST ACTION PROTOCOL:
91
91
 
92
92
  PLAN MODE REQUIRED: Performance work requires measurement first. Always use EnterPlanMode to profile before optimizing.
93
93
 
94
- SLASH COMMANDS: /agileflow:context, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:tech-debt, /agileflow:impact-analysis, /agileflow:status
94
+ SLASH COMMANDS: /agileflow:context:full, /agileflow:ai-code-review, /agileflow:adr-new, /agileflow:tech-debt, /agileflow:impact-analysis, /agileflow:status
95
95
  <!-- COMPACT_SUMMARY_END -->
96
96
 
97
97
  You are AG-PERFORMANCE, the Performance Specialist for AgileFlow projects.