agileflow 2.74.0 → 2.76.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.
Files changed (44) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/scripts/agileflow-configure.js +105 -27
  4. package/scripts/agileflow-welcome.js +95 -2
  5. package/scripts/auto-self-improve.js +301 -0
  6. package/scripts/ralph-loop.js +491 -0
  7. package/src/core/agents/accessibility.md +1 -1
  8. package/src/core/agents/adr-writer.md +6 -6
  9. package/src/core/agents/analytics.md +1 -1
  10. package/src/core/agents/api.md +130 -41
  11. package/src/core/agents/ci.md +3 -3
  12. package/src/core/agents/compliance.md +1 -1
  13. package/src/core/agents/database.md +121 -36
  14. package/src/core/agents/datamigration.md +1 -1
  15. package/src/core/agents/design.md +2 -2
  16. package/src/core/agents/devops.md +2 -2
  17. package/src/core/agents/documentation.md +2 -2
  18. package/src/core/agents/epic-planner.md +4 -4
  19. package/src/core/agents/integrations.md +4 -4
  20. package/src/core/agents/mentor.md +6 -6
  21. package/src/core/agents/mobile.md +2 -2
  22. package/src/core/agents/monitoring.md +2 -2
  23. package/src/core/agents/performance.md +2 -2
  24. package/src/core/agents/product.md +3 -3
  25. package/src/core/agents/qa.md +1 -1
  26. package/src/core/agents/refactor.md +1 -1
  27. package/src/core/agents/security.md +4 -4
  28. package/src/core/agents/testing.md +2 -2
  29. package/src/core/agents/ui.md +129 -44
  30. package/src/core/commands/babysit.md +210 -0
  31. package/src/core/commands/blockers.md +3 -3
  32. package/src/core/commands/configure.md +50 -7
  33. package/src/core/commands/context/export.md +99 -0
  34. package/src/core/commands/context/full.md +172 -0
  35. package/src/core/commands/context/note.md +128 -0
  36. package/src/core/commands/research/ask.md +453 -0
  37. package/src/core/commands/research/import.md +287 -0
  38. package/src/core/commands/research/list.md +93 -0
  39. package/src/core/commands/research/view.md +113 -0
  40. package/src/core/experts/documentation/expertise.yaml +4 -0
  41. package/src/core/experts/research/expertise.yaml +4 -4
  42. package/tools/cli/lib/docs-setup.js +1 -1
  43. package/src/core/commands/context.md +0 -417
  44. package/src/core/commands/research.md +0 -124
@@ -0,0 +1,491 @@
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 &&
140
+ story.status === 'ready' &&
141
+ id !== currentStoryId;
142
+ })
143
+ .sort((a, b) => {
144
+ // Sort by story number if possible
145
+ const numA = parseInt(a[0].replace(/\D/g, '')) || 0;
146
+ const numB = parseInt(b[0].replace(/\D/g, '')) || 0;
147
+ return numA - numB;
148
+ });
149
+
150
+ if (readyStories.length > 0) {
151
+ return { id: readyStories[0][0], ...readyStories[0][1] };
152
+ }
153
+ return null;
154
+ }
155
+
156
+ // Mark story as completed
157
+ function markStoryComplete(rootDir, storyId) {
158
+ const status = getStatus(rootDir);
159
+ if (status.stories && status.stories[storyId]) {
160
+ status.stories[storyId].status = 'completed';
161
+ status.stories[storyId].completed_at = new Date().toISOString();
162
+ saveStatus(rootDir, status);
163
+ return true;
164
+ }
165
+ return false;
166
+ }
167
+
168
+ // Mark story as in_progress
169
+ function markStoryInProgress(rootDir, storyId) {
170
+ const status = getStatus(rootDir);
171
+ if (status.stories && status.stories[storyId]) {
172
+ status.stories[storyId].status = 'in_progress';
173
+ status.stories[storyId].started_at = new Date().toISOString();
174
+ saveStatus(rootDir, status);
175
+ return true;
176
+ }
177
+ return false;
178
+ }
179
+
180
+ // Get story details
181
+ function getStoryDetails(rootDir, storyId) {
182
+ const status = getStatus(rootDir);
183
+ if (status.stories && status.stories[storyId]) {
184
+ return { id: storyId, ...status.stories[storyId] };
185
+ }
186
+ return null;
187
+ }
188
+
189
+ // Count stories in epic by status
190
+ function getEpicProgress(status, epicId) {
191
+ const stories = status.stories || {};
192
+ const epicStories = Object.entries(stories).filter(([_, s]) => s.epic === epicId);
193
+
194
+ return {
195
+ total: epicStories.length,
196
+ completed: epicStories.filter(([_, s]) => s.status === 'completed').length,
197
+ in_progress: epicStories.filter(([_, s]) => s.status === 'in_progress').length,
198
+ ready: epicStories.filter(([_, s]) => s.status === 'ready').length,
199
+ blocked: epicStories.filter(([_, s]) => s.status === 'blocked').length,
200
+ };
201
+ }
202
+
203
+ // Main loop logic
204
+ function handleLoop(rootDir) {
205
+ const state = getSessionState(rootDir);
206
+ const loop = state.ralph_loop;
207
+
208
+ // Check if loop mode is enabled
209
+ if (!loop || !loop.enabled) {
210
+ return; // Silent exit - not in loop mode
211
+ }
212
+
213
+ const status = getStatus(rootDir);
214
+ const iteration = (loop.iteration || 0) + 1;
215
+ const maxIterations = loop.max_iterations || 20;
216
+
217
+ console.log('');
218
+ console.log(`${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`);
219
+ console.log(`${c.brand}${c.bold} RALPH LOOP - Iteration ${iteration}/${maxIterations}${c.reset}`);
220
+ console.log(`${c.brand}${c.bold}══════════════════════════════════════════════════════════${c.reset}`);
221
+ console.log('');
222
+
223
+ // Check iteration limit
224
+ if (iteration > maxIterations) {
225
+ console.log(`${c.yellow}⚠ Max iterations (${maxIterations}) reached. Stopping loop.${c.reset}`);
226
+ console.log(`${c.dim}Run /agileflow:babysit MODE=loop to restart${c.reset}`);
227
+ state.ralph_loop.enabled = false;
228
+ state.ralph_loop.stopped_reason = 'max_iterations';
229
+ saveSessionState(rootDir, state);
230
+ return;
231
+ }
232
+
233
+ // Get current story
234
+ const currentStoryId = loop.current_story;
235
+ const currentStory = getStoryDetails(rootDir, currentStoryId);
236
+ const epicId = loop.epic;
237
+
238
+ if (!currentStory) {
239
+ console.log(`${c.red}✗ Current story ${currentStoryId} not found${c.reset}`);
240
+ state.ralph_loop.enabled = false;
241
+ state.ralph_loop.stopped_reason = 'story_not_found';
242
+ saveSessionState(rootDir, state);
243
+ return;
244
+ }
245
+
246
+ console.log(`${c.cyan}Current Story:${c.reset} ${currentStoryId} - ${currentStory.title || 'Untitled'}`);
247
+ console.log('');
248
+
249
+ // Run tests
250
+ const testCommand = getTestCommand(rootDir);
251
+ console.log(`${c.blue}Running:${c.reset} ${testCommand}`);
252
+ console.log(`${c.dim}${'─'.repeat(58)}${c.reset}`);
253
+
254
+ const testResult = runTests(rootDir, testCommand);
255
+
256
+ if (testResult.passed) {
257
+ console.log(`${c.green}✓ Tests passed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
258
+ console.log('');
259
+
260
+ // Mark story complete
261
+ markStoryComplete(rootDir, currentStoryId);
262
+ console.log(`${c.green}✓ Marked ${currentStoryId} as completed${c.reset}`);
263
+
264
+ // Get next story
265
+ const nextStory = getNextStory(status, epicId, currentStoryId);
266
+
267
+ if (nextStory) {
268
+ // Move to next story
269
+ markStoryInProgress(rootDir, nextStory.id);
270
+ state.ralph_loop.current_story = nextStory.id;
271
+ state.ralph_loop.iteration = iteration;
272
+ saveSessionState(rootDir, state);
273
+
274
+ const progress = getEpicProgress(getStatus(rootDir), epicId);
275
+ console.log('');
276
+ console.log(`${c.cyan}━━━ Next Story ━━━${c.reset}`);
277
+ console.log(`${c.bold}${nextStory.id}:${c.reset} ${nextStory.title || 'Untitled'}`);
278
+ if (nextStory.acceptance_criteria) {
279
+ console.log(`${c.dim}Acceptance Criteria:${c.reset}`);
280
+ const criteria = Array.isArray(nextStory.acceptance_criteria)
281
+ ? nextStory.acceptance_criteria
282
+ : [nextStory.acceptance_criteria];
283
+ criteria.slice(0, 3).forEach(ac => console.log(` • ${ac}`));
284
+ }
285
+ console.log('');
286
+ console.log(`${c.dim}Epic Progress: ${progress.completed}/${progress.total} stories complete${c.reset}`);
287
+ console.log('');
288
+ console.log(`${c.brand}▶ Continue implementing ${nextStory.id}${c.reset}`);
289
+ console.log(`${c.dim} Run tests when ready. Loop will validate and continue.${c.reset}`);
290
+
291
+ } else {
292
+ // No more stories - epic complete!
293
+ const progress = getEpicProgress(getStatus(rootDir), epicId);
294
+ state.ralph_loop.enabled = false;
295
+ state.ralph_loop.stopped_reason = 'epic_complete';
296
+ state.ralph_loop.completed_at = new Date().toISOString();
297
+ saveSessionState(rootDir, state);
298
+
299
+ console.log('');
300
+ console.log(`${c.green}${c.bold}════════════════════════════════════════════════════════${c.reset}`);
301
+ console.log(`${c.green}${c.bold} 🎉 EPIC COMPLETE!${c.reset}`);
302
+ console.log(`${c.green}${c.bold}════════════════════════════════════════════════════════${c.reset}`);
303
+ console.log('');
304
+ console.log(`${c.green}Epic ${epicId} finished in ${iteration} iterations${c.reset}`);
305
+ console.log(`${c.dim}${progress.completed} stories completed${c.reset}`);
306
+ console.log('');
307
+ }
308
+
309
+ } else {
310
+ // Tests failed - feed back to Claude
311
+ console.log(`${c.red}✗ Tests failed${c.reset} (${(testResult.duration / 1000).toFixed(1)}s)`);
312
+ console.log('');
313
+
314
+ state.ralph_loop.iteration = iteration;
315
+ state.ralph_loop.last_failure = new Date().toISOString();
316
+ saveSessionState(rootDir, state);
317
+
318
+ console.log(`${c.yellow}━━━ Test Failures ━━━${c.reset}`);
319
+
320
+ // Show truncated output (last 50 lines most relevant)
321
+ const outputLines = testResult.output.split('\n');
322
+ const relevantLines = outputLines.slice(-50);
323
+ console.log(relevantLines.join('\n'));
324
+
325
+ console.log('');
326
+ console.log(`${c.brand}▶ Fix the failing tests and continue${c.reset}`);
327
+ console.log(`${c.dim} Loop will re-run tests when you stop.${c.reset}`);
328
+ console.log(`${c.dim} Iteration ${iteration}/${maxIterations}${c.reset}`);
329
+ }
330
+
331
+ console.log('');
332
+ }
333
+
334
+ // Handle CLI arguments
335
+ function handleCLI() {
336
+ const args = process.argv.slice(2);
337
+ const rootDir = getProjectRoot();
338
+
339
+ if (args.includes('--status')) {
340
+ const state = getSessionState(rootDir);
341
+ const loop = state.ralph_loop;
342
+
343
+ if (!loop || !loop.enabled) {
344
+ console.log(`${c.dim}Ralph Loop: not active${c.reset}`);
345
+ } else {
346
+ console.log(`${c.green}Ralph Loop: active${c.reset}`);
347
+ console.log(` Epic: ${loop.epic}`);
348
+ console.log(` Current Story: ${loop.current_story}`);
349
+ console.log(` Iteration: ${loop.iteration || 0}/${loop.max_iterations || 20}`);
350
+ }
351
+ return true;
352
+ }
353
+
354
+ if (args.includes('--stop')) {
355
+ const state = getSessionState(rootDir);
356
+ if (state.ralph_loop) {
357
+ state.ralph_loop.enabled = false;
358
+ state.ralph_loop.stopped_reason = 'manual';
359
+ saveSessionState(rootDir, state);
360
+ console.log(`${c.yellow}Ralph Loop stopped${c.reset}`);
361
+ } else {
362
+ console.log(`${c.dim}Ralph Loop was not active${c.reset}`);
363
+ }
364
+ return true;
365
+ }
366
+
367
+ if (args.includes('--reset')) {
368
+ const state = getSessionState(rootDir);
369
+ delete state.ralph_loop;
370
+ saveSessionState(rootDir, state);
371
+ console.log(`${c.green}Ralph Loop state reset${c.reset}`);
372
+ return true;
373
+ }
374
+
375
+ // Handle --init
376
+ if (args.some(a => a.startsWith('--init'))) {
377
+ const epicArg = args.find(a => a.startsWith('--epic='));
378
+ const maxArg = args.find(a => a.startsWith('--max='));
379
+
380
+ if (!epicArg) {
381
+ console.log(`${c.red}Error: --epic=EP-XXXX is required${c.reset}`);
382
+ return true;
383
+ }
384
+
385
+ const epicId = epicArg.split('=')[1];
386
+ const maxIterations = maxArg ? parseInt(maxArg.split('=')[1]) : 20;
387
+
388
+ // Find first ready story in epic
389
+ const status = getStatus(rootDir);
390
+ const stories = status.stories || {};
391
+ const readyStories = Object.entries(stories)
392
+ .filter(([_, s]) => s.epic === epicId && s.status === 'ready')
393
+ .sort((a, b) => {
394
+ const numA = parseInt(a[0].replace(/\D/g, '')) || 0;
395
+ const numB = parseInt(b[0].replace(/\D/g, '')) || 0;
396
+ return numA - numB;
397
+ });
398
+
399
+ if (readyStories.length === 0) {
400
+ console.log(`${c.yellow}No ready stories found in ${epicId}${c.reset}`);
401
+ console.log(`${c.dim}Create stories with status "ready" first${c.reset}`);
402
+ return true;
403
+ }
404
+
405
+ const firstStory = readyStories[0];
406
+ const storyId = firstStory[0];
407
+
408
+ // Mark first story as in_progress
409
+ markStoryInProgress(rootDir, storyId);
410
+
411
+ // Initialize loop state
412
+ const state = getSessionState(rootDir);
413
+ state.ralph_loop = {
414
+ enabled: true,
415
+ epic: epicId,
416
+ current_story: storyId,
417
+ iteration: 0,
418
+ max_iterations: maxIterations,
419
+ started_at: new Date().toISOString(),
420
+ };
421
+ saveSessionState(rootDir, state);
422
+
423
+ const progress = getEpicProgress(status, epicId);
424
+
425
+ console.log('');
426
+ console.log(`${c.green}${c.bold}Ralph Loop Initialized${c.reset}`);
427
+ console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
428
+ console.log(` Epic: ${c.cyan}${epicId}${c.reset}`);
429
+ console.log(` Stories: ${progress.ready} ready, ${progress.total} total`);
430
+ console.log(` Max Iterations: ${maxIterations}`);
431
+ console.log(`${c.dim}${'─'.repeat(40)}${c.reset}`);
432
+ console.log('');
433
+ console.log(`${c.brand}▶ Starting Story:${c.reset} ${storyId}`);
434
+ console.log(` ${firstStory[1].title || 'Untitled'}`);
435
+ if (firstStory[1].acceptance_criteria) {
436
+ const criteria = Array.isArray(firstStory[1].acceptance_criteria)
437
+ ? firstStory[1].acceptance_criteria
438
+ : [firstStory[1].acceptance_criteria];
439
+ console.log(`${c.dim} Acceptance Criteria:${c.reset}`);
440
+ criteria.slice(0, 3).forEach(ac => console.log(` • ${ac}`));
441
+ }
442
+ console.log('');
443
+ console.log(`${c.dim}Work on this story. When you stop, tests will run automatically.${c.reset}`);
444
+ console.log(`${c.dim}If tests pass, the next story will be loaded.${c.reset}`);
445
+ console.log('');
446
+
447
+ return true;
448
+ }
449
+
450
+ if (args.includes('--help')) {
451
+ console.log(`
452
+ ${c.brand}${c.bold}ralph-loop.js${c.reset} - Autonomous Story Processing
453
+
454
+ ${c.bold}Usage:${c.reset}
455
+ node scripts/ralph-loop.js Run loop check (Stop hook)
456
+ node scripts/ralph-loop.js --init --epic=EP-XXX Initialize loop for epic
457
+ node scripts/ralph-loop.js --status Check loop status
458
+ node scripts/ralph-loop.js --stop Stop the loop
459
+ node scripts/ralph-loop.js --reset Reset loop state
460
+
461
+ ${c.bold}Options:${c.reset}
462
+ --epic=EP-XXXX Epic ID to process (required for --init)
463
+ --max=N Max iterations (default: 20)
464
+
465
+ ${c.bold}How it works:${c.reset}
466
+ 1. Start loop with /agileflow:babysit EPIC=EP-XXX MODE=loop
467
+ 2. Work on the current story
468
+ 3. When you stop, this hook runs tests
469
+ 4. If tests pass → story marked complete, next story loaded
470
+ 5. If tests fail → failures shown, you continue fixing
471
+ 6. Loop repeats until epic done or max iterations
472
+ `);
473
+ return true;
474
+ }
475
+
476
+ return false;
477
+ }
478
+
479
+ // Main
480
+ function main() {
481
+ // Handle CLI commands first
482
+ if (handleCLI()) {
483
+ return;
484
+ }
485
+
486
+ // Otherwise run the loop handler
487
+ const rootDir = getProjectRoot();
488
+ handleLoop(rootDir);
489
+ }
490
+
491
+ main();
@@ -428,7 +428,7 @@ COORDINATION WITH OTHER AGENTS
428
428
 
429
429
  SLASH COMMANDS
430
430
 
431
- - `/agileflow:context MODE=research TOPIC=...` → Research accessibility best practices
431
+ - `/agileflow:research:ask TOPIC=...` → Research accessibility best practices
432
432
  - `/agileflow:ai-code-review` → Review code for accessibility issues
433
433
  - `/agileflow:adr-new` → Document accessibility decisions
434
434
  - `/agileflow:status STORY=... STATUS=...` → Update status
@@ -43,7 +43,7 @@ node .agileflow/scripts/obtain-context.js adr-writer
43
43
 
44
44
  **Workflow**:
45
45
  1. Load expertise: `packages/cli/src/core/experts/adr-writer/expertise.yaml`
46
- 2. Check docs/10-research/ for existing research (or invoke `/agileflow:context MODE=research`)
46
+ 2. Check docs/10-research/ for existing research (or invoke `/agileflow:research:ask`)
47
47
  3. Check docs/03-decisions/ for related ADRs
48
48
  4. Get next ADR number from docs/03-decisions/README.md (sequential: 0001, 0002, etc.)
49
49
  5. Gather decision context and alternatives
@@ -135,13 +135,13 @@ Create an ADR when deciding:
135
135
  SLASH COMMANDS (Proactive Use)
136
136
 
137
137
  **Research**:
138
- - `/agileflow:context MODE=research TOPIC=...` → Generate research for alternatives before writing ADR
138
+ - `/agileflow:research:ask TOPIC=...` → Generate research for alternatives before writing ADR
139
139
 
140
140
  RESEARCH INTEGRATION
141
141
 
142
142
  **Before Writing ADR**:
143
143
  1. Check docs/10-research/ for existing research on the decision topic
144
- 2. If research is missing or stale, invoke `/agileflow:context MODE=research TOPIC=...`
144
+ 2. If research is missing or stale, invoke `/agileflow:research:ask TOPIC=...`
145
145
  3. Research should cover all alternatives with pros/cons, benchmarks, trade-offs
146
146
 
147
147
  **After User Provides Research**:
@@ -151,7 +151,7 @@ RESEARCH INTEGRATION
151
151
 
152
152
  WORKFLOW
153
153
  1. **[KNOWLEDGE LOADING]** Before writing:
154
- - Check docs/10-research/ for relevant research (or invoke `/agileflow:context MODE=research`)
154
+ - Check docs/10-research/ for relevant research (or invoke `/agileflow:research:ask`)
155
155
  - Check existing ADRs in docs/03-decisions/ for related decisions
156
156
  - Check CLAUDE.md for current architecture context
157
157
  2. Ask for decision context (what's being decided and why now?)
@@ -268,7 +268,7 @@ This contains your mental model of:
268
268
  **Then Output**:
269
269
  1. ADR context: "Next ADR: ADR-<NUMBER>, recent decisions: <list of last 3 ADRs>"
270
270
  2. If research exists: "Found research: <topic> (docs/10-research/<file>)"
271
- 3. If no research: "No research found. I can invoke `/agileflow:context MODE=research` to gather alternatives."
271
+ 3. If no research: "No research found. I can invoke `/agileflow:research:ask` to gather alternatives."
272
272
  4. Ask: "What technical decision would you like to document?"
273
273
  5. Clarify: "I'll document context, alternatives considered, decision, and consequences."
274
274
 
@@ -288,7 +288,7 @@ This updates your expertise with what you learned, so you're faster next time.
288
288
 
289
289
  **After User Describes Decision**:
290
290
  1. Clarify context (why now? what forces?)
291
- 2. Check docs/10-research/ for supporting research (or invoke `/agileflow:context MODE=research`)
291
+ 2. Check docs/10-research/ for supporting research (or invoke `/agileflow:research:ask`)
292
292
  3. Identify 2-5 alternatives with pros/cons
293
293
  4. Propose ADR structure (show preview)
294
294
  5. Get approval (YES/NO)
@@ -514,7 +514,7 @@ COORDINATION WITH OTHER AGENTS
514
514
 
515
515
  SLASH COMMANDS
516
516
 
517
- - `/agileflow:context MODE=research TOPIC=...` → Research analytics best practices
517
+ - `/agileflow:research:ask TOPIC=...` → Research analytics best practices
518
518
  - `/agileflow:ai-code-review` → Review analytics implementation for data quality
519
519
  - `/agileflow:adr-new` → Document analytics decisions
520
520
  - `/agileflow:status STORY=... STATUS=...` → Update status