agileflow 2.79.0 → 2.80.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.
@@ -114,27 +114,9 @@ function parsePathRules(content) {
114
114
  */
115
115
  function getDefaultPathRules() {
116
116
  return {
117
- zeroAccessPaths: [
118
- '~/.ssh/',
119
- '~/.aws/credentials',
120
- '.env',
121
- '.env.local',
122
- '.env.production',
123
- ],
124
- readOnlyPaths: [
125
- '/etc/',
126
- '~/.bashrc',
127
- '~/.zshrc',
128
- 'package-lock.json',
129
- 'yarn.lock',
130
- '.git/',
131
- ],
132
- noDeletePaths: [
133
- '.agileflow/',
134
- '.claude/',
135
- 'docs/09-agents/status.json',
136
- 'CLAUDE.md',
137
- ],
117
+ zeroAccessPaths: ['~/.ssh/', '~/.aws/credentials', '.env', '.env.local', '.env.production'],
118
+ readOnlyPaths: ['/etc/', '~/.bashrc', '~/.zshrc', 'package-lock.json', 'yarn.lock', '.git/'],
119
+ noDeletePaths: ['.agileflow/', '.claude/', 'docs/09-agents/status.json', 'CLAUDE.md'],
138
120
  };
139
121
  }
140
122
 
@@ -247,9 +229,7 @@ function main() {
247
229
  }
248
230
 
249
231
  // Resolve to absolute path
250
- const absolutePath = path.isAbsolute(filePath)
251
- ? filePath
252
- : path.join(projectDir, filePath);
232
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectDir, filePath);
253
233
 
254
234
  // Load rules
255
235
  const rules = loadPathRules(projectDir);
@@ -17,14 +17,7 @@
17
17
 
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
-
21
- // Color codes for output
22
- const c = {
23
- red: '\x1b[38;5;203m',
24
- yellow: '\x1b[38;5;215m',
25
- reset: '\x1b[0m',
26
- dim: '\x1b[2m'
27
- };
20
+ const { c } = require('../lib/colors');
28
21
 
29
22
  /**
30
23
  * Find project root by looking for .agileflow directory
@@ -48,7 +41,7 @@ function parseSimpleYAML(content) {
48
41
  const config = {
49
42
  bashToolPatterns: [],
50
43
  askPatterns: [],
51
- agileflowProtections: []
44
+ agileflowProtections: [],
52
45
  };
53
46
 
54
47
  let currentSection = null;
@@ -76,13 +69,22 @@ function parseSimpleYAML(content) {
76
69
  currentPattern = null;
77
70
  } else if (trimmed.startsWith('- pattern:') && currentSection) {
78
71
  // New pattern entry
79
- const patternValue = trimmed.replace('- pattern:', '').trim().replace(/^["']|["']$/g, '');
72
+ const patternValue = trimmed
73
+ .replace('- pattern:', '')
74
+ .trim()
75
+ .replace(/^["']|["']$/g, '');
80
76
  currentPattern = { pattern: patternValue };
81
77
  config[currentSection].push(currentPattern);
82
78
  } else if (trimmed.startsWith('reason:') && currentPattern) {
83
- currentPattern.reason = trimmed.replace('reason:', '').trim().replace(/^["']|["']$/g, '');
79
+ currentPattern.reason = trimmed
80
+ .replace('reason:', '')
81
+ .trim()
82
+ .replace(/^["']|["']$/g, '');
84
83
  } else if (trimmed.startsWith('flags:') && currentPattern) {
85
- currentPattern.flags = trimmed.replace('flags:', '').trim().replace(/^["']|["']$/g, '');
84
+ currentPattern.flags = trimmed
85
+ .replace('flags:', '')
86
+ .trim()
87
+ .replace(/^["']|["']$/g, '');
86
88
  }
87
89
  }
88
90
 
@@ -96,7 +98,7 @@ function loadPatterns(projectRoot) {
96
98
  const configPaths = [
97
99
  path.join(projectRoot, '.agileflow/config/damage-control-patterns.yaml'),
98
100
  path.join(projectRoot, '.agileflow/config/damage-control-patterns.yml'),
99
- path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml')
101
+ path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml'),
100
102
  ];
101
103
 
102
104
  for (const configPath of configPaths) {
@@ -135,14 +137,14 @@ function validateCommand(command, config) {
135
137
  // Check blocked patterns (bashToolPatterns + agileflowProtections)
136
138
  const blockedPatterns = [
137
139
  ...(config.bashToolPatterns || []),
138
- ...(config.agileflowProtections || [])
140
+ ...(config.agileflowProtections || []),
139
141
  ];
140
142
 
141
143
  for (const rule of blockedPatterns) {
142
144
  if (matchesPattern(command, rule)) {
143
145
  return {
144
146
  action: 'block',
145
- reason: rule.reason || 'Command blocked by damage control'
147
+ reason: rule.reason || 'Command blocked by damage control',
146
148
  };
147
149
  }
148
150
  }
@@ -152,7 +154,7 @@ function validateCommand(command, config) {
152
154
  if (matchesPattern(command, rule)) {
153
155
  return {
154
156
  action: 'ask',
155
- reason: rule.reason || 'Please confirm this command'
157
+ reason: rule.reason || 'Please confirm this command',
156
158
  };
157
159
  }
158
160
  }
@@ -192,17 +194,21 @@ function main() {
192
194
  switch (result.action) {
193
195
  case 'block':
194
196
  // Output error message and block
195
- console.error(`${c.red}[BLOCKED]${c.reset} ${result.reason}`);
196
- console.error(`${c.dim}Command: ${command.substring(0, 100)}${command.length > 100 ? '...' : ''}${c.reset}`);
197
+ console.error(`${c.coral}[BLOCKED]${c.reset} ${result.reason}`);
198
+ console.error(
199
+ `${c.dim}Command: ${command.substring(0, 100)}${command.length > 100 ? '...' : ''}${c.reset}`
200
+ );
197
201
  process.exit(2);
198
202
  break;
199
203
 
200
204
  case 'ask':
201
205
  // Output JSON to trigger user confirmation
202
- console.log(JSON.stringify({
203
- result: 'ask',
204
- message: result.reason
205
- }));
206
+ console.log(
207
+ JSON.stringify({
208
+ result: 'ask',
209
+ message: result.reason,
210
+ })
211
+ );
206
212
  process.exit(0);
207
213
  break;
208
214
 
@@ -15,13 +15,7 @@
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
17
  const os = require('os');
18
-
19
- // Color codes for output
20
- const c = {
21
- red: '\x1b[38;5;203m',
22
- reset: '\x1b[0m',
23
- dim: '\x1b[2m'
24
- };
18
+ const { c } = require('../lib/colors');
25
19
 
26
20
  /**
27
21
  * Find project root by looking for .agileflow directory
@@ -54,7 +48,7 @@ function parseSimpleYAML(content) {
54
48
  const config = {
55
49
  zeroAccessPaths: [],
56
50
  readOnlyPaths: [],
57
- noDeletePaths: []
51
+ noDeletePaths: [],
58
52
  };
59
53
 
60
54
  let currentSection = null;
@@ -92,7 +86,7 @@ function loadPatterns(projectRoot) {
92
86
  const configPaths = [
93
87
  path.join(projectRoot, '.agileflow/config/damage-control-patterns.yaml'),
94
88
  path.join(projectRoot, '.agileflow/config/damage-control-patterns.yml'),
95
- path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml')
89
+ path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml'),
96
90
  ];
97
91
 
98
92
  for (const configPath of configPaths) {
@@ -168,7 +162,7 @@ function validatePath(filePath, config) {
168
162
  return {
169
163
  action: 'block',
170
164
  reason: `Zero-access path: ${zeroMatch}`,
171
- detail: 'This file is protected and cannot be accessed'
165
+ detail: 'This file is protected and cannot be accessed',
172
166
  };
173
167
  }
174
168
 
@@ -178,7 +172,7 @@ function validatePath(filePath, config) {
178
172
  return {
179
173
  action: 'block',
180
174
  reason: `Read-only path: ${readOnlyMatch}`,
181
- detail: 'This file is read-only and cannot be edited'
175
+ detail: 'This file is read-only and cannot be edited',
182
176
  };
183
177
  }
184
178
 
@@ -215,7 +209,7 @@ function main() {
215
209
  const result = validatePath(filePath, config);
216
210
 
217
211
  if (result.action === 'block') {
218
- console.error(`${c.red}[BLOCKED]${c.reset} ${result.reason}`);
212
+ console.error(`${c.coral}[BLOCKED]${c.reset} ${result.reason}`);
219
213
  console.error(`${c.dim}${result.detail}${c.reset}`);
220
214
  console.error(`${c.dim}File: ${filePath}${c.reset}`);
221
215
  process.exit(2);
@@ -15,13 +15,7 @@
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
17
  const os = require('os');
18
-
19
- // Color codes for output
20
- const c = {
21
- red: '\x1b[38;5;203m',
22
- reset: '\x1b[0m',
23
- dim: '\x1b[2m'
24
- };
18
+ const { c } = require('../lib/colors');
25
19
 
26
20
  /**
27
21
  * Find project root by looking for .agileflow directory
@@ -54,7 +48,7 @@ function parseSimpleYAML(content) {
54
48
  const config = {
55
49
  zeroAccessPaths: [],
56
50
  readOnlyPaths: [],
57
- noDeletePaths: []
51
+ noDeletePaths: [],
58
52
  };
59
53
 
60
54
  let currentSection = null;
@@ -92,7 +86,7 @@ function loadPatterns(projectRoot) {
92
86
  const configPaths = [
93
87
  path.join(projectRoot, '.agileflow/config/damage-control-patterns.yaml'),
94
88
  path.join(projectRoot, '.agileflow/config/damage-control-patterns.yml'),
95
- path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml')
89
+ path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml'),
96
90
  ];
97
91
 
98
92
  for (const configPath of configPaths) {
@@ -168,7 +162,7 @@ function validatePath(filePath, config) {
168
162
  return {
169
163
  action: 'block',
170
164
  reason: `Zero-access path: ${zeroMatch}`,
171
- detail: 'This file is protected and cannot be accessed'
165
+ detail: 'This file is protected and cannot be accessed',
172
166
  };
173
167
  }
174
168
 
@@ -178,7 +172,7 @@ function validatePath(filePath, config) {
178
172
  return {
179
173
  action: 'block',
180
174
  reason: `Read-only path: ${readOnlyMatch}`,
181
- detail: 'This file is read-only and cannot be written to'
175
+ detail: 'This file is read-only and cannot be written to',
182
176
  };
183
177
  }
184
178
 
@@ -215,7 +209,7 @@ function main() {
215
209
  const result = validatePath(filePath, config);
216
210
 
217
211
  if (result.action === 'block') {
218
- console.error(`${c.red}[BLOCKED]${c.reset} ${result.reason}`);
212
+ console.error(`${c.coral}[BLOCKED]${c.reset} ${result.reason}`);
219
213
  console.error(`${c.dim}${result.detail}${c.reset}`);
220
214
  console.error(`${c.dim}File: ${filePath}${c.reset}`);
221
215
  process.exit(2);
@@ -156,12 +156,12 @@ function formatOutput(info, asJson = false, compact = false) {
156
156
  brand: '\x1b[38;2;232;104;58m', // #e8683a - AgileFlow brand orange
157
157
 
158
158
  // Vibrant 256-color palette (modern, sleek look)
159
- mintGreen: '\x1b[38;5;158m', // Healthy/success states
160
- peach: '\x1b[38;5;215m', // Warning states
161
- coral: '\x1b[38;5;203m', // Critical/error states
162
- lightGreen: '\x1b[38;5;194m', // Session healthy
163
- skyBlue: '\x1b[38;5;117m', // Directories/paths
164
- lavender: '\x1b[38;5;147m', // Model info
159
+ mintGreen: '\x1b[38;5;158m', // Healthy/success states
160
+ peach: '\x1b[38;5;215m', // Warning states
161
+ coral: '\x1b[38;5;203m', // Critical/error states
162
+ lightGreen: '\x1b[38;5;194m', // Session healthy
163
+ skyBlue: '\x1b[38;5;117m', // Directories/paths
164
+ lavender: '\x1b[38;5;147m', // Model info
165
165
  };
166
166
 
167
167
  // Beautiful compact colorful format (using vibrant 256-color palette)
@@ -18,6 +18,7 @@
18
18
  const fs = require('fs');
19
19
  const path = require('path');
20
20
  const { execSync } = require('child_process');
21
+ const { c: C, box } = require('../lib/colors');
21
22
 
22
23
  const DISPLAY_LIMIT = 30000; // Claude Code's Bash tool display limit
23
24
 
@@ -44,8 +45,10 @@ if (commandName) {
44
45
  state: {},
45
46
  });
46
47
 
47
- // Keep backwards compatibility - also set singular active_command to most recent
48
- state.active_command = state.active_commands[state.active_commands.length - 1];
48
+ // Remove legacy active_command field (only use active_commands array now)
49
+ if (state.active_command !== undefined) {
50
+ delete state.active_command;
51
+ }
49
52
 
50
53
  fs.writeFileSync(sessionStatePath, JSON.stringify(state, null, 2) + '\n');
51
54
  } catch (e) {
@@ -54,33 +57,6 @@ if (commandName) {
54
57
  }
55
58
  }
56
59
 
57
- // ANSI colors
58
- const C = {
59
- reset: '\x1b[0m',
60
- dim: '\x1b[2m',
61
- bold: '\x1b[1m',
62
- cyan: '\x1b[36m',
63
- yellow: '\x1b[33m',
64
- green: '\x1b[32m',
65
- red: '\x1b[31m',
66
- magenta: '\x1b[35m',
67
- blue: '\x1b[34m',
68
- brightCyan: '\x1b[96m',
69
- brightYellow: '\x1b[93m',
70
- brightGreen: '\x1b[92m',
71
- brand: '\x1b[38;2;232;104;58m', // AgileFlow brand orange
72
-
73
- // Vibrant 256-color palette (modern, sleek look)
74
- mintGreen: '\x1b[38;5;158m', // Healthy/success states
75
- peach: '\x1b[38;5;215m', // Warning states
76
- coral: '\x1b[38;5;203m', // Critical/error states
77
- lightGreen: '\x1b[38;5;194m', // Session healthy
78
- lightYellow: '\x1b[38;5;228m', // Session warning
79
- skyBlue: '\x1b[38;5;117m', // Directories/paths
80
- lavender: '\x1b[38;5;147m', // Model info
81
- softGold: '\x1b[38;5;222m', // Cost/money
82
- };
83
-
84
60
  function safeRead(filePath) {
85
61
  try {
86
62
  return fs.readFileSync(filePath, 'utf8');
@@ -225,7 +201,8 @@ function generateSummary() {
225
201
 
226
202
  // Header row (full width, no column divider)
227
203
  const title = commandName ? `Context [${commandName}]` : 'Context Summary';
228
- const branchColor = branch === 'main' ? C.mintGreen : branch.startsWith('fix') ? C.coral : C.skyBlue;
204
+ const branchColor =
205
+ branch === 'main' ? C.mintGreen : branch.startsWith('fix') ? C.coral : C.skyBlue;
229
206
  const maxBranchLen = 20;
230
207
  const branchDisplay =
231
208
  branch.length > maxBranchLen ? branch.substring(0, maxBranchLen - 2) + '..' : branch;
@@ -300,7 +277,12 @@ function generateSummary() {
300
277
 
301
278
  // Research
302
279
  const researchText = researchFiles.length > 0 ? `${researchFiles.length} notes` : 'none';
303
- summary += row('Research', researchText, C.lavender, researchFiles.length > 0 ? C.skyBlue : C.dim);
280
+ summary += row(
281
+ 'Research',
282
+ researchText,
283
+ C.lavender,
284
+ researchFiles.length > 0 ? C.skyBlue : C.dim
285
+ );
304
286
 
305
287
  // Epics
306
288
  const epicText = epicFiles.length > 0 ? `${epicFiles.length} epics` : 'none';
@@ -396,7 +378,36 @@ function generateFullContent() {
396
378
  content += `${C.dim}No session-state.json found${C.reset}\n`;
397
379
  }
398
380
 
399
- // 4. DOCS STRUCTURE (using vibrant 256-color palette)
381
+ // 4. INTERACTION MODE (AskUserQuestion guidance)
382
+ const metadata = safeReadJSON('docs/00-meta/agileflow-metadata.json');
383
+ const askUserQuestionConfig = metadata?.features?.askUserQuestion;
384
+
385
+ if (askUserQuestionConfig?.enabled) {
386
+ content += `\n${C.brand}${C.bold}═══ ⚡ INTERACTION MODE: AskUserQuestion ENABLED ═══${C.reset}\n`;
387
+ content += `${C.dim}${'─'.repeat(60)}${C.reset}\n`;
388
+ content += `${C.bold}CRITICAL RULE:${C.reset} End ${C.skyBlue}EVERY${C.reset} response with the AskUserQuestion tool.\n\n`;
389
+ content += `${C.mintGreen}✓ CORRECT:${C.reset} Call the actual AskUserQuestion tool\n`;
390
+ content += `${C.coral}✗ WRONG:${C.reset} Text like "Want me to continue?" or "What's next?"\n\n`;
391
+ content += `${C.lavender}Required format:${C.reset}\n`;
392
+ content += `${C.dim}\`\`\`xml
393
+ <invoke name="AskUserQuestion">
394
+ <parameter name="questions">[{
395
+ "question": "What would you like to do next?",
396
+ "header": "Next step",
397
+ "multiSelect": false,
398
+ "options": [
399
+ {"label": "Option A (Recommended)", "description": "Why this is best"},
400
+ {"label": "Option B", "description": "Alternative approach"},
401
+ {"label": "Pause", "description": "Stop here for now"}
402
+ ]
403
+ }]</parameter>
404
+ </invoke>
405
+ \`\`\`${C.reset}\n`;
406
+ content += `${C.dim}${'─'.repeat(60)}${C.reset}\n`;
407
+ content += `${C.dim}Mode: ${askUserQuestionConfig.mode || 'all'} | Configure: /agileflow:configure${C.reset}\n\n`;
408
+ }
409
+
410
+ // 5. DOCS STRUCTURE (using vibrant 256-color palette)
400
411
  content += `\n${C.skyBlue}${C.bold}═══ Documentation ═══${C.reset}\n`;
401
412
  const docsDir = 'docs';
402
413
  const docFolders = safeLs(docsDir).filter(f => {
@@ -420,7 +431,7 @@ function generateFullContent() {
420
431
  });
421
432
  }
422
433
 
423
- // 5. RESEARCH NOTES - List + Full content of most recent (using vibrant 256-color palette)
434
+ // 6. RESEARCH NOTES - List + Full content of most recent (using vibrant 256-color palette)
424
435
  content += `\n${C.skyBlue}${C.bold}═══ Research Notes ═══${C.reset}\n`;
425
436
  const researchDir = 'docs/10-research';
426
437
  const researchFiles = safeLs(researchDir).filter(f => f.endsWith('.md') && f !== 'README.md');
@@ -443,7 +454,7 @@ function generateFullContent() {
443
454
  content += `${C.dim}No research notes${C.reset}\n`;
444
455
  }
445
456
 
446
- // 6. BUS MESSAGES (using vibrant 256-color palette)
457
+ // 7. BUS MESSAGES (using vibrant 256-color palette)
447
458
  content += `\n${C.skyBlue}${C.bold}═══ Recent Agent Messages ═══${C.reset}\n`;
448
459
  const busPath = 'docs/09-agents/bus/log.jsonl';
449
460
  const busContent = safeRead(busPath);
@@ -467,7 +478,7 @@ function generateFullContent() {
467
478
  content += `${C.dim}No bus log found${C.reset}\n`;
468
479
  }
469
480
 
470
- // 7. KEY FILES - Full content
481
+ // 8. KEY FILES - Full content
471
482
  content += `\n${C.cyan}${C.bold}═══ Key Context Files (Full Content) ═══${C.reset}\n`;
472
483
 
473
484
  const keyFilesToRead = [
@@ -493,7 +504,7 @@ function generateFullContent() {
493
504
  const settingsExists = fs.existsSync('.claude/settings.json');
494
505
  content += `\n ${settingsExists ? `${C.green}✓${C.reset}` : `${C.dim}○${C.reset}`} .claude/settings.json\n`;
495
506
 
496
- // 8. EPICS FOLDER
507
+ // 9. EPICS FOLDER
497
508
  content += `\n${C.cyan}${C.bold}═══ Epic Files ═══${C.reset}\n`;
498
509
  const epicFiles = safeLs('docs/05-epics').filter(f => f.endsWith('.md') && f !== 'README.md');
499
510
  if (epicFiles.length > 0) {
@@ -134,7 +134,7 @@ function verifyScreenshots(rootDir) {
134
134
  const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
135
135
  let files;
136
136
  try {
137
- files = fs.readdirSync(fullPath).filter((file) => {
137
+ files = fs.readdirSync(fullPath).filter(file => {
138
138
  const ext = path.extname(file).toLowerCase();
139
139
  return imageExtensions.includes(ext);
140
140
  });
@@ -320,11 +320,13 @@ function handleLoop(rootDir) {
320
320
  console.log(`${c.yellow}⚠ ${screenshotResult.output}${c.reset}`);
321
321
  if (screenshotResult.unverified.length > 0) {
322
322
  console.log(`${c.dim}Unverified screenshots:${c.reset}`);
323
- screenshotResult.unverified.slice(0, 5).forEach((file) => {
323
+ screenshotResult.unverified.slice(0, 5).forEach(file => {
324
324
  console.log(` ${c.yellow}- ${file}${c.reset}`);
325
325
  });
326
326
  if (screenshotResult.unverified.length > 5) {
327
- console.log(` ${c.dim}... and ${screenshotResult.unverified.length - 5} more${c.reset}`);
327
+ console.log(
328
+ ` ${c.dim}... and ${screenshotResult.unverified.length - 5} more${c.reset}`
329
+ );
328
330
  }
329
331
  }
330
332
  state.ralph_loop.screenshots_verified = false;
@@ -334,8 +336,12 @@ function handleLoop(rootDir) {
334
336
  // Visual Mode: Enforce minimum iterations
335
337
  if (visualMode && iteration < minIterations) {
336
338
  console.log('');
337
- console.log(`${c.yellow}⚠ Visual Mode requires ${minIterations}+ iterations for confirmation${c.reset}`);
338
- console.log(`${c.dim}Current: iteration ${iteration}. Let loop run once more to confirm.${c.reset}`);
339
+ console.log(
340
+ `${c.yellow} Visual Mode requires ${minIterations}+ iterations for confirmation${c.reset}`
341
+ );
342
+ console.log(
343
+ `${c.dim}Current: iteration ${iteration}. Let loop run once more to confirm.${c.reset}`
344
+ );
339
345
 
340
346
  state.ralph_loop.iteration = iteration;
341
347
  saveSessionState(rootDir, state);
@@ -458,7 +464,9 @@ function handleCLI() {
458
464
  console.log(` Current Story: ${loop.current_story}`);
459
465
  console.log(` Iteration: ${loop.iteration || 0}/${loop.max_iterations || 20}`);
460
466
  if (loop.visual_mode) {
461
- const verified = loop.screenshots_verified ? `${c.green}yes${c.reset}` : `${c.yellow}no${c.reset}`;
467
+ const verified = loop.screenshots_verified
468
+ ? `${c.green}yes${c.reset}`
469
+ : `${c.yellow}no${c.reset}`;
462
470
  console.log(` Screenshots Verified: ${verified}`);
463
471
  }
464
472
  }
@@ -97,7 +97,7 @@ function getImageFiles(dir) {
97
97
 
98
98
  try {
99
99
  const files = fs.readdirSync(dir);
100
- return files.filter((file) => {
100
+ return files.filter(file => {
101
101
  const ext = path.extname(file).toLowerCase();
102
102
  return imageExtensions.includes(ext);
103
103
  });
@@ -172,7 +172,9 @@ function formatResult(result, options) {
172
172
 
173
173
  if (result.success) {
174
174
  console.log(`${c.green}${c.bold}All screenshots verified${c.reset}`);
175
- console.log(`${c.dim}${result.verified}/${result.total} screenshots have 'verified-' prefix${c.reset}`);
175
+ console.log(
176
+ `${c.dim}${result.verified}/${result.total} screenshots have 'verified-' prefix${c.reset}`
177
+ );
176
178
  } else {
177
179
  console.log(`${c.red}${c.bold}Unverified screenshots found${c.reset}`);
178
180
  console.log(`${c.dim}${result.verified}/${result.total} verified${c.reset}`);
@@ -10,7 +10,7 @@ compact_context:
10
10
  - "CRITICAL: If 🔄 OUTDATED shown → offer --upgrade to re-deploy latest scripts"
11
11
  - "MUST backup created on migrate: .claude/settings.json.backup"
12
12
  - "MUST show RED RESTART banner after ANY changes (quit, wait 5s, restart)"
13
- - "Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate"
13
+ - "Features: sessionstart, precompact, ralphloop, selfimprove, archival, statusline, autoupdate, askuserquestion"
14
14
  - "Stop hooks (ralphloop, selfimprove) run when Claude completes or pauses"
15
15
  state_fields:
16
16
  - detection_status
@@ -52,7 +52,7 @@ node .agileflow/scripts/agileflow-configure.js --repair=statusline # Fix specif
52
52
 
53
53
  ### Features
54
54
 
55
- `sessionstart`, `precompact`, `ralphloop`, `selfimprove`, `archival`, `statusline`, `autoupdate`
55
+ `sessionstart`, `precompact`, `ralphloop`, `selfimprove`, `archival`, `statusline`, `autoupdate`, `askuserquestion`
56
56
 
57
57
  **Stop hooks** (ralphloop, selfimprove) run when Claude completes or pauses work.
58
58
 
@@ -180,12 +180,12 @@ node .agileflow/scripts/agileflow-configure.js --enable=archival --archival-days
180
180
 
181
181
  ## Profile Details
182
182
 
183
- | Profile | SessionStart | PreCompact | RalphLoop | SelfImprove | Archival | StatusLine |
184
- |---------|-------------|------------|-----------|-------------|----------|------------|
185
- | `full` | ✅ | ✅ | ✅ | ✅ | ✅ 30 days | ✅ |
186
- | `basic` | ✅ | ✅ | ❌ | ❌ | ✅ 30 days | ❌ |
187
- | `minimal` | ✅ | ❌ | ❌ | ❌ | ✅ 30 days | ❌ |
188
- | `none` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
183
+ | Profile | SessionStart | PreCompact | RalphLoop | SelfImprove | Archival | StatusLine | AskUserQuestion |
184
+ |---------|-------------|------------|-----------|-------------|----------|------------|-----------------|
185
+ | `full` | ✅ | ✅ | ✅ | ✅ | ✅ 30 days | ✅ | ✅ |
186
+ | `basic` | ✅ | ✅ | ❌ | ❌ | ✅ 30 days | ❌ | ✅ |
187
+ | `minimal` | ✅ | ❌ | ❌ | ❌ | ✅ 30 days | ❌ | ❌ |
188
+ | `none` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
189
189
 
190
190
  ## Interactive Mode (via /configure command)
191
191
 
@@ -226,7 +226,8 @@ Based on selection, run appropriate command.
226
226
  {"label": "SelfImprove (Stop Hook)", "description": "Auto-update agent expertise from work"},
227
227
  {"label": "Archival", "description": "Auto-archive old completed stories"},
228
228
  {"label": "Status Line", "description": "Custom status bar"},
229
- {"label": "Auto-Update", "description": "Automatically update AgileFlow on session start"}
229
+ {"label": "Auto-Update", "description": "Automatically update AgileFlow on session start"},
230
+ {"label": "AskUserQuestion Mode", "description": "End all responses with AskUserQuestion tool for guided interaction"}
230
231
  ]
231
232
  }]</parameter>
232
233
  </invoke>
@@ -240,6 +241,7 @@ Map selections:
240
241
  - "Archival" → `archival`
241
242
  - "Status Line" → `statusline`
242
243
  - "Auto-Update" → `autoupdate`
244
+ - "AskUserQuestion Mode" → `askuserquestion`
243
245
 
244
246
  ## Auto-Update Configuration
245
247
 
@@ -263,6 +265,41 @@ node .agileflow/scripts/agileflow-configure.js --enable=autoupdate
263
265
 
264
266
  **Check frequencies:** `hourly`, `daily`, `weekly`, `never`
265
267
 
268
+ ## AskUserQuestion Mode
269
+
270
+ Enable AskUserQuestion to have all commands end with guided options:
271
+
272
+ ```bash
273
+ # Enable AskUserQuestion mode
274
+ node .agileflow/scripts/agileflow-configure.js --enable=askuserquestion
275
+
276
+ # Disable AskUserQuestion mode
277
+ node .agileflow/scripts/agileflow-configure.js --disable=askuserquestion
278
+ ```
279
+
280
+ **What it does:**
281
+ - When enabled: All commands end with AskUserQuestion tool call (guided options)
282
+ - When disabled: Commands can end with natural text questions
283
+
284
+ **Storage in metadata:**
285
+ ```json
286
+ {
287
+ "features": {
288
+ "askUserQuestion": {
289
+ "enabled": true,
290
+ "mode": "all"
291
+ }
292
+ }
293
+ }
294
+ ```
295
+
296
+ **Modes:**
297
+ - `all`: All commands use AskUserQuestion (default)
298
+ - `interactive`: Only interactive commands (babysit, mentor, configure, epic-planner)
299
+ - `none`: Disabled
300
+
301
+ The guidance is injected via `obtain-context.js` when commands run.
302
+
266
303
  ## Stop Hook Features
267
304
 
268
305
  Stop hooks run when Claude completes a task or pauses for user input. These enable autonomous workflows.
@@ -150,3 +150,28 @@ learnings:
150
150
  total_lines: 2009
151
151
  source: "packages/cli/src/core/commands/*.md"
152
152
  notes: "All files follow consistent documentation template: frontmatter, quick start, purpose, parameters table, examples with explanations, workflow steps, output/files, and related commands"
153
+
154
+ - date: 2026-01-09
155
+ context: "Created IDE Integrations documentation for AgileFlow"
156
+ insight: "IDE handler system supports 4 IDEs (Claude Code, Cursor, Windsurf, Codex CLI) through plugin architecture. Each IDE has custom config directory (.claude/, .cursor/, .windsurf/, .codex/) and different installation models. Claude Code most advanced (damage control, spawnable agents), Codex CLI unique (per-repo skills, user-level prompts, AGENTS.md instructions)"
157
+ created_files:
158
+ - "apps/docs/content/docs/features/ide-integrations.mdx (700+ lines)"
159
+ - "apps/docs/content/docs/features/index.mdx (updated with IDE section)"
160
+ total_lines: 700
161
+ source: "packages/cli/tools/cli/installers/ide/ (_base-ide.js, claude-code.js, cursor.js, windsurf.js, codex.js, manager.js)"
162
+ coverage:
163
+ - "IDE overview table with config dirs, types, status"
164
+ - "Quick start for Claude Code (default IDE)"
165
+ - "Multi-IDE installation instructions"
166
+ - "Detailed setup for each IDE with directory structure"
167
+ - "Command access patterns by IDE"
168
+ - "Claude Code subagent spawning, damage control hooks"
169
+ - "Cursor unique features, differences from Claude Code"
170
+ - "Windsurf workflow management, VSCode integration"
171
+ - "Codex CLI dual installation model (per-repo skills, user-level prompts, AGENTS.md)"
172
+ - "Dynamic IDE handler system, extensibility guide"
173
+ - "Content injection system explanation"
174
+ - "Troubleshooting guide for common issues"
175
+ - "Best practices for IDE selection and multi-IDE usage"
176
+ - "Configuration management and version control"
177
+ notes: "Comprehensive 700+ line documentation covering all 4 IDEs with detailed setup, command access, and unique features. Includes handler architecture for IDE developers extending with new IDEs."