agileflow 2.91.0 → 2.92.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +6 -6
  3. package/lib/README.md +178 -0
  4. package/lib/codebase-indexer.js +32 -23
  5. package/lib/colors.js +190 -12
  6. package/lib/consent.js +232 -0
  7. package/lib/correlation.js +277 -0
  8. package/lib/error-codes.js +46 -0
  9. package/lib/errors.js +48 -6
  10. package/lib/file-cache.js +182 -0
  11. package/lib/format-error.js +156 -0
  12. package/lib/path-resolver.js +155 -7
  13. package/lib/paths.js +212 -20
  14. package/lib/placeholder-registry.js +205 -0
  15. package/lib/registry-di.js +358 -0
  16. package/lib/result-schema.js +363 -0
  17. package/lib/result.js +210 -0
  18. package/lib/session-registry.js +13 -0
  19. package/lib/session-state-machine.js +465 -0
  20. package/lib/validate-commands.js +308 -0
  21. package/lib/validate.js +116 -52
  22. package/package.json +1 -1
  23. package/scripts/af +34 -0
  24. package/scripts/agent-loop.js +63 -9
  25. package/scripts/agileflow-configure.js +2 -2
  26. package/scripts/agileflow-welcome.js +491 -23
  27. package/scripts/archive-completed-stories.sh +57 -11
  28. package/scripts/claude-tmux.sh +102 -0
  29. package/scripts/damage-control-bash.js +3 -70
  30. package/scripts/damage-control-edit.js +3 -20
  31. package/scripts/damage-control-write.js +3 -20
  32. package/scripts/dependency-check.js +310 -0
  33. package/scripts/get-env.js +11 -4
  34. package/scripts/lib/configure-detect.js +23 -1
  35. package/scripts/lib/configure-features.js +50 -2
  36. package/scripts/lib/context-formatter.js +771 -0
  37. package/scripts/lib/context-loader.js +699 -0
  38. package/scripts/lib/damage-control-utils.js +107 -0
  39. package/scripts/lib/json-utils.sh +162 -0
  40. package/scripts/lib/state-migrator.js +353 -0
  41. package/scripts/lib/story-state-machine.js +437 -0
  42. package/scripts/obtain-context.js +80 -1248
  43. package/scripts/pre-push-check.sh +46 -0
  44. package/scripts/precompact-context.sh +23 -10
  45. package/scripts/query-codebase.js +127 -14
  46. package/scripts/ralph-loop.js +5 -5
  47. package/scripts/session-manager.js +408 -55
  48. package/scripts/spawn-parallel.js +666 -0
  49. package/scripts/tui/blessed/data/watcher.js +20 -15
  50. package/scripts/tui/blessed/index.js +2 -2
  51. package/scripts/tui/blessed/panels/output.js +14 -8
  52. package/scripts/tui/blessed/panels/sessions.js +22 -15
  53. package/scripts/tui/blessed/panels/trace.js +14 -8
  54. package/scripts/tui/blessed/ui/help.js +3 -3
  55. package/scripts/tui/blessed/ui/screen.js +4 -4
  56. package/scripts/tui/blessed/ui/statusbar.js +5 -9
  57. package/scripts/tui/blessed/ui/tabbar.js +11 -11
  58. package/scripts/validators/component-validator.js +41 -14
  59. package/scripts/validators/json-schema-validator.js +11 -4
  60. package/scripts/validators/markdown-validator.js +1 -2
  61. package/scripts/validators/migration-validator.js +17 -5
  62. package/scripts/validators/security-validator.js +137 -33
  63. package/scripts/validators/story-format-validator.js +31 -10
  64. package/scripts/validators/test-result-validator.js +19 -4
  65. package/scripts/validators/workflow-validator.js +12 -5
  66. package/src/core/agents/codebase-query.md +24 -0
  67. package/src/core/commands/adr.md +114 -0
  68. package/src/core/commands/agent.md +120 -0
  69. package/src/core/commands/assign.md +145 -0
  70. package/src/core/commands/babysit.md +32 -5
  71. package/src/core/commands/changelog.md +118 -0
  72. package/src/core/commands/configure.md +42 -6
  73. package/src/core/commands/diagnose.md +114 -0
  74. package/src/core/commands/epic.md +113 -0
  75. package/src/core/commands/handoff.md +128 -0
  76. package/src/core/commands/help.md +75 -0
  77. package/src/core/commands/pr.md +96 -0
  78. package/src/core/commands/roadmap/analyze.md +400 -0
  79. package/src/core/commands/session/new.md +132 -6
  80. package/src/core/commands/session/spawn.md +197 -0
  81. package/src/core/commands/sprint.md +22 -0
  82. package/src/core/commands/status.md +74 -0
  83. package/src/core/commands/story.md +143 -4
  84. package/src/core/templates/agileflow-metadata.json +55 -2
  85. package/src/core/templates/plan-template.md +125 -0
  86. package/src/core/templates/story-lifecycle.md +213 -0
  87. package/src/core/templates/story-template.md +4 -0
  88. package/src/core/templates/tdd-test-template.js +241 -0
  89. package/tools/cli/commands/setup.js +95 -0
  90. package/tools/cli/installers/core/installer.js +94 -0
  91. package/tools/cli/installers/ide/_base-ide.js +20 -11
  92. package/tools/cli/installers/ide/codex.js +29 -47
  93. package/tools/cli/installers/ide/windsurf.js +1 -1
  94. package/tools/cli/lib/config-manager.js +17 -2
  95. package/tools/cli/lib/content-transformer.js +271 -0
  96. package/tools/cli/lib/error-handler.js +14 -22
  97. package/tools/cli/lib/ide-error-factory.js +421 -0
  98. package/tools/cli/lib/ide-health-monitor.js +364 -0
  99. package/tools/cli/lib/ide-registry.js +113 -2
  100. package/tools/cli/lib/ui.js +15 -25
@@ -22,7 +22,7 @@ const fs = require('fs');
22
22
  const path = require('path');
23
23
 
24
24
  let input = '';
25
- process.stdin.on('data', chunk => input += chunk);
25
+ process.stdin.on('data', chunk => (input += chunk));
26
26
  process.stdin.on('end', () => {
27
27
  try {
28
28
  const context = JSON.parse(input);
@@ -60,7 +60,21 @@ process.stdin.on('end', () => {
60
60
  });
61
61
 
62
62
  function isBinaryFile(filePath) {
63
- const binaryExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2', '.ttf', '.eot', '.pdf', '.zip', '.tar', '.gz'];
63
+ const binaryExtensions = [
64
+ '.png',
65
+ '.jpg',
66
+ '.jpeg',
67
+ '.gif',
68
+ '.ico',
69
+ '.woff',
70
+ '.woff2',
71
+ '.ttf',
72
+ '.eot',
73
+ '.pdf',
74
+ '.zip',
75
+ '.tar',
76
+ '.gz',
77
+ ];
64
78
  const ext = path.extname(filePath).toLowerCase();
65
79
  return binaryExtensions.includes(ext);
66
80
  }
@@ -93,7 +107,6 @@ function validateSecurity(filePath) {
93
107
 
94
108
  // Check for insecure randomness
95
109
  issues.push(...checkInsecureRandom(content));
96
-
97
110
  } catch (e) {
98
111
  issues.push(`Read error: ${e.message}`);
99
112
  }
@@ -114,15 +127,33 @@ function checkSecrets(content, fileName) {
114
127
  { pattern: /['"]sk-[a-zA-Z0-9]{20,}['"]/, message: 'Possible OpenAI API key detected' },
115
128
  { pattern: /['"]AIza[a-zA-Z0-9_-]{35}['"]/, message: 'Possible Google API key detected' },
116
129
  { pattern: /['"]AKIA[A-Z0-9]{16}['"]/, message: 'Possible AWS access key detected' },
117
- { pattern: /['"]ghp_[a-zA-Z0-9]{36}['"]/, message: 'Possible GitHub personal access token detected' },
130
+ {
131
+ pattern: /['"]ghp_[a-zA-Z0-9]{36}['"]/,
132
+ message: 'Possible GitHub personal access token detected',
133
+ },
118
134
  { pattern: /['"]npm_[a-zA-Z0-9]{36}['"]/, message: 'Possible npm token detected' },
119
135
 
120
136
  // Generic patterns
121
- { pattern: /password\s*[:=]\s*['"][^'"${\s]{8,}['"](?!\s*;?\s*\/\/\s*example)/i, message: 'Possible hardcoded password detected' },
122
- { pattern: /api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]/i, message: 'Possible hardcoded API key detected' },
123
- { pattern: /secret\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]/i, message: 'Possible hardcoded secret detected' },
124
- { pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/, message: 'Private key detected in source code' },
125
- { pattern: /-----BEGIN\s+CERTIFICATE-----/, message: 'Certificate detected in source code (verify this is intentional)' },
137
+ {
138
+ pattern: /password\s*[:=]\s*['"][^'"${\s]{8,}['"](?!\s*;?\s*\/\/\s*example)/i,
139
+ message: 'Possible hardcoded password detected',
140
+ },
141
+ {
142
+ pattern: /api[_-]?key\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]/i,
143
+ message: 'Possible hardcoded API key detected',
144
+ },
145
+ {
146
+ pattern: /secret\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]/i,
147
+ message: 'Possible hardcoded secret detected',
148
+ },
149
+ {
150
+ pattern: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/,
151
+ message: 'Private key detected in source code',
152
+ },
153
+ {
154
+ pattern: /-----BEGIN\s+CERTIFICATE-----/,
155
+ message: 'Certificate detected in source code (verify this is intentional)',
156
+ },
126
157
  ];
127
158
 
128
159
  for (const { pattern, message } of secretPatterns) {
@@ -144,11 +175,27 @@ function checkSqlInjection(content, ext) {
144
175
 
145
176
  // Check for string concatenation in SQL
146
177
  const sqlInjectionPatterns = [
147
- { pattern: /['"`]\s*SELECT\s+.*\+\s*\w+/i, message: 'Possible SQL injection: string concatenation in SELECT query' },
148
- { pattern: /['"`]\s*INSERT\s+.*\+\s*\w+/i, message: 'Possible SQL injection: string concatenation in INSERT query' },
149
- { pattern: /['"`]\s*UPDATE\s+.*\+\s*\w+/i, message: 'Possible SQL injection: string concatenation in UPDATE query' },
150
- { pattern: /['"`]\s*DELETE\s+.*\+\s*\w+/i, message: 'Possible SQL injection: string concatenation in DELETE query' },
151
- { pattern: /\$\{[^}]+\}.*WHERE/i, message: 'Possible SQL injection: template literal in WHERE clause - use parameterized queries' },
178
+ {
179
+ pattern: /['"`]\s*SELECT\s+.*\+\s*\w+/i,
180
+ message: 'Possible SQL injection: string concatenation in SELECT query',
181
+ },
182
+ {
183
+ pattern: /['"`]\s*INSERT\s+.*\+\s*\w+/i,
184
+ message: 'Possible SQL injection: string concatenation in INSERT query',
185
+ },
186
+ {
187
+ pattern: /['"`]\s*UPDATE\s+.*\+\s*\w+/i,
188
+ message: 'Possible SQL injection: string concatenation in UPDATE query',
189
+ },
190
+ {
191
+ pattern: /['"`]\s*DELETE\s+.*\+\s*\w+/i,
192
+ message: 'Possible SQL injection: string concatenation in DELETE query',
193
+ },
194
+ {
195
+ pattern: /\$\{[^}]+\}.*WHERE/i,
196
+ message:
197
+ 'Possible SQL injection: template literal in WHERE clause - use parameterized queries',
198
+ },
152
199
  ];
153
200
 
154
201
  for (const { pattern, message } of sqlInjectionPatterns) {
@@ -169,11 +216,23 @@ function checkXss(content, ext) {
169
216
  }
170
217
 
171
218
  const xssPatterns = [
172
- { pattern: /innerHTML\s*=\s*[^'"`]/, message: 'Direct innerHTML assignment detected - sanitize content or use textContent' },
173
- { pattern: /dangerouslySetInnerHTML/, message: 'dangerouslySetInnerHTML used - ensure content is sanitized' },
174
- { pattern: /document\.write\s*\(/, message: 'document.write() is dangerous - use DOM manipulation instead' },
219
+ {
220
+ pattern: /innerHTML\s*=\s*[^'"`]/,
221
+ message: 'Direct innerHTML assignment detected - sanitize content or use textContent',
222
+ },
223
+ {
224
+ pattern: /dangerouslySetInnerHTML/,
225
+ message: 'dangerouslySetInnerHTML used - ensure content is sanitized',
226
+ },
227
+ {
228
+ pattern: /document\.write\s*\(/,
229
+ message: 'document.write() is dangerous - use DOM manipulation instead',
230
+ },
175
231
  { pattern: /v-html\s*=/, message: 'v-html directive detected - ensure content is sanitized' },
176
- { pattern: /\{@html\s+/, message: 'Svelte @html directive detected - ensure content is sanitized' },
232
+ {
233
+ pattern: /\{@html\s+/,
234
+ message: 'Svelte @html directive detected - ensure content is sanitized',
235
+ },
177
236
  ];
178
237
 
179
238
  for (const { pattern, message } of xssPatterns) {
@@ -196,14 +255,32 @@ function checkCommandInjection(content, ext) {
196
255
 
197
256
  const cmdInjectionPatterns = [
198
257
  // JavaScript/Node
199
- { pattern: /exec\s*\(\s*[`'"]\s*\$\{/, message: 'Possible command injection: template literal in exec()' },
200
- { pattern: /exec\s*\(\s*\w+\s*\+/, message: 'Possible command injection: string concatenation in exec()' },
201
- { pattern: /execSync\s*\(\s*[`'"]\s*\$\{/, message: 'Possible command injection: template literal in execSync()' },
202
- { pattern: /spawn\s*\(\s*[`'"]\s*\$\{/, message: 'Possible command injection: template literal in spawn()' },
258
+ {
259
+ pattern: /exec\s*\(\s*[`'"]\s*\$\{/,
260
+ message: 'Possible command injection: template literal in exec()',
261
+ },
262
+ {
263
+ pattern: /exec\s*\(\s*\w+\s*\+/,
264
+ message: 'Possible command injection: string concatenation in exec()',
265
+ },
266
+ {
267
+ pattern: /execSync\s*\(\s*[`'"]\s*\$\{/,
268
+ message: 'Possible command injection: template literal in execSync()',
269
+ },
270
+ {
271
+ pattern: /spawn\s*\(\s*[`'"]\s*\$\{/,
272
+ message: 'Possible command injection: template literal in spawn()',
273
+ },
203
274
 
204
275
  // Python
205
- { pattern: /os\.system\s*\(\s*f['"]/, message: 'Possible command injection: f-string in os.system()' },
206
- { pattern: /subprocess\.(call|run|Popen)\s*\(\s*f['"]/, message: 'Possible command injection: f-string in subprocess - use list args instead' },
276
+ {
277
+ pattern: /os\.system\s*\(\s*f['"]/,
278
+ message: 'Possible command injection: f-string in os.system()',
279
+ },
280
+ {
281
+ pattern: /subprocess\.(call|run|Popen)\s*\(\s*f['"]/,
282
+ message: 'Possible command injection: f-string in subprocess - use list args instead',
283
+ },
207
284
  ];
208
285
 
209
286
  for (const { pattern, message } of cmdInjectionPatterns) {
@@ -224,9 +301,18 @@ function checkPathTraversal(content, ext) {
224
301
  }
225
302
 
226
303
  const pathTraversalPatterns = [
227
- { pattern: /path\.join\s*\([^)]*req\.(params|query|body)/, message: 'Possible path traversal: user input in path.join()' },
228
- { pattern: /readFile(Sync)?\s*\([^)]*req\.(params|query|body)/, message: 'Possible path traversal: user input in file read' },
229
- { pattern: /open\s*\(\s*f['"].*\{.*\}/, message: 'Possible path traversal: user input in file open (Python)' },
304
+ {
305
+ pattern: /path\.join\s*\([^)]*req\.(params|query|body)/,
306
+ message: 'Possible path traversal: user input in path.join()',
307
+ },
308
+ {
309
+ pattern: /readFile(Sync)?\s*\([^)]*req\.(params|query|body)/,
310
+ message: 'Possible path traversal: user input in file read',
311
+ },
312
+ {
313
+ pattern: /open\s*\(\s*f['"].*\{.*\}/,
314
+ message: 'Possible path traversal: user input in file open (Python)',
315
+ },
230
316
  ];
231
317
 
232
318
  for (const { pattern, message } of pathTraversalPatterns) {
@@ -242,10 +328,22 @@ function checkInsecureCrypto(content) {
242
328
  const issues = [];
243
329
 
244
330
  const insecureCryptoPatterns = [
245
- { pattern: /createHash\s*\(\s*['"]md5['"]\s*\)/, message: 'MD5 is insecure for cryptographic use - use SHA-256 or better' },
246
- { pattern: /createHash\s*\(\s*['"]sha1['"]\s*\)/, message: 'SHA-1 is deprecated - use SHA-256 or better' },
247
- { pattern: /hashlib\.md5\s*\(/, message: 'MD5 is insecure for cryptographic use - use SHA-256 or better' },
248
- { pattern: /DES|3DES|RC4/, message: 'Insecure encryption algorithm detected - use AES-256-GCM' },
331
+ {
332
+ pattern: /createHash\s*\(\s*['"]md5['"]\s*\)/,
333
+ message: 'MD5 is insecure for cryptographic use - use SHA-256 or better',
334
+ },
335
+ {
336
+ pattern: /createHash\s*\(\s*['"]sha1['"]\s*\)/,
337
+ message: 'SHA-1 is deprecated - use SHA-256 or better',
338
+ },
339
+ {
340
+ pattern: /hashlib\.md5\s*\(/,
341
+ message: 'MD5 is insecure for cryptographic use - use SHA-256 or better',
342
+ },
343
+ {
344
+ pattern: /DES|3DES|RC4/,
345
+ message: 'Insecure encryption algorithm detected - use AES-256-GCM',
346
+ },
249
347
  { pattern: /ECB/, message: 'ECB mode is insecure - use GCM or CBC with proper IV' },
250
348
  ];
251
349
 
@@ -262,8 +360,14 @@ function checkInsecureRandom(content) {
262
360
  const issues = [];
263
361
 
264
362
  const insecureRandomPatterns = [
265
- { pattern: /Math\.random\s*\(\s*\).*(?:token|key|secret|password|auth|session)/i, message: 'Math.random() used for security-sensitive value - use crypto.randomBytes()' },
266
- { pattern: /random\.random\s*\(\s*\).*(?:token|key|secret|password|auth|session)/i, message: 'random.random() is not cryptographically secure - use secrets module' },
363
+ {
364
+ pattern: /Math\.random\s*\(\s*\).*(?:token|key|secret|password|auth|session)/i,
365
+ message: 'Math.random() used for security-sensitive value - use crypto.randomBytes()',
366
+ },
367
+ {
368
+ pattern: /random\.random\s*\(\s*\).*(?:token|key|secret|password|auth|session)/i,
369
+ message: 'random.random() is not cryptographically secure - use secrets module',
370
+ },
267
371
  ];
268
372
 
269
373
  for (const { pattern, message } of insecureRandomPatterns) {
@@ -22,8 +22,11 @@
22
22
  const fs = require('fs');
23
23
  const path = require('path');
24
24
 
25
+ // Import status constants from single source of truth
26
+ const { VALID_STATUSES } = require('../lib/story-state-machine');
27
+
25
28
  let input = '';
26
- process.stdin.on('data', chunk => input += chunk);
29
+ process.stdin.on('data', chunk => (input += chunk));
27
30
  process.stdin.on('end', () => {
28
31
  try {
29
32
  const context = JSON.parse(input);
@@ -110,7 +113,6 @@ function validateStoryFormat(filePath) {
110
113
  }
111
114
  }
112
115
  }
113
-
114
116
  } catch (e) {
115
117
  if (e instanceof SyntaxError) {
116
118
  issues.push(`Invalid JSON: ${e.message}`);
@@ -132,7 +134,9 @@ function validateSingleStory(story, index) {
132
134
  } else {
133
135
  // ID format validation (US-XXXX or EP-XXXX)
134
136
  if (!/^(US|EP|TECH|BUG)-\d{4}$/.test(story.id)) {
135
- issues.push(`Story ${storyRef}: ID should match pattern US-XXXX, EP-XXXX, TECH-XXXX, or BUG-XXXX`);
137
+ issues.push(
138
+ `Story ${storyRef}: ID should match pattern US-XXXX, EP-XXXX, TECH-XXXX, or BUG-XXXX`
139
+ );
136
140
  }
137
141
  }
138
142
 
@@ -140,23 +144,40 @@ function validateSingleStory(story, index) {
140
144
  issues.push(`Story ${storyRef}: missing 'title' or 'name' field`);
141
145
  }
142
146
 
143
- // Status validation
144
- const validStatuses = ['pending', 'ready', 'in_progress', 'in-progress', 'in_review', 'in-review', 'completed', 'blocked', 'archived'];
145
- if (story.status && !validStatuses.includes(story.status)) {
146
- issues.push(`Story ${storyRef}: invalid status "${story.status}". Valid: ${validStatuses.join(', ')}`);
147
+ // Status validation (using canonical values from story-state-machine.js)
148
+ // Also accept legacy formats for backward compatibility
149
+ const legacyStatuses = ['pending', 'in-progress', 'in-review'];
150
+ const allAcceptedStatuses = [...VALID_STATUSES, ...legacyStatuses];
151
+ if (story.status && !allAcceptedStatuses.includes(story.status)) {
152
+ issues.push(
153
+ `Story ${storyRef}: invalid status "${story.status}". Valid: ${VALID_STATUSES.join(', ')}`
154
+ );
147
155
  }
148
156
 
149
157
  // Priority validation
150
158
  const validPriorities = ['critical', 'high', 'medium', 'low'];
151
159
  if (story.priority && !validPriorities.includes(story.priority)) {
152
- issues.push(`Story ${storyRef}: invalid priority "${story.priority}". Valid: ${validPriorities.join(', ')}`);
160
+ issues.push(
161
+ `Story ${storyRef}: invalid priority "${story.priority}". Valid: ${validPriorities.join(', ')}`
162
+ );
153
163
  }
154
164
 
155
165
  // Owner validation (if present)
156
166
  if (story.owner) {
157
- const validOwners = ['AG-UI', 'AG-API', 'AG-CI', 'AG-DB', 'AG-TEST', 'AG-DOC', 'AG-SEC', 'human'];
167
+ const validOwners = [
168
+ 'AG-UI',
169
+ 'AG-API',
170
+ 'AG-CI',
171
+ 'AG-DB',
172
+ 'AG-TEST',
173
+ 'AG-DOC',
174
+ 'AG-SEC',
175
+ 'human',
176
+ ];
158
177
  if (!validOwners.includes(story.owner)) {
159
- issues.push(`Story ${storyRef}: unknown owner "${story.owner}". Valid: ${validOwners.join(', ')}`);
178
+ issues.push(
179
+ `Story ${storyRef}: unknown owner "${story.owner}". Valid: ${validOwners.join(', ')}`
180
+ );
160
181
  }
161
182
  }
162
183
 
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  let input = '';
23
- process.stdin.on('data', chunk => input += chunk);
23
+ process.stdin.on('data', chunk => (input += chunk));
24
24
  process.stdin.on('end', () => {
25
25
  try {
26
26
  const context = JSON.parse(input);
@@ -28,7 +28,16 @@ process.stdin.on('end', () => {
28
28
  const result = context.result || '';
29
29
 
30
30
  // Only validate test-related commands
31
- const testCommands = ['npm test', 'npm run test', 'jest', 'pytest', 'cargo test', 'go test', 'vitest', 'mocha'];
31
+ const testCommands = [
32
+ 'npm test',
33
+ 'npm run test',
34
+ 'jest',
35
+ 'pytest',
36
+ 'cargo test',
37
+ 'go test',
38
+ 'vitest',
39
+ 'mocha',
40
+ ];
32
41
  const isTestCommand = testCommands.some(tc => command.includes(tc));
33
42
 
34
43
  if (!isTestCommand) {
@@ -76,7 +85,9 @@ function validateTestResult(command, result) {
76
85
  // Check for coverage warnings (if coverage was run)
77
86
  if (resultLower.includes('coverage')) {
78
87
  // Look for coverage percentage
79
- const coverageMatch = result.match(/(\d+(?:\.\d+)?)\s*%\s*(?:coverage|statements|branches|functions|lines)/i);
88
+ const coverageMatch = result.match(
89
+ /(\d+(?:\.\d+)?)\s*%\s*(?:coverage|statements|branches|functions|lines)/i
90
+ );
80
91
  if (coverageMatch) {
81
92
  const coverage = parseFloat(coverageMatch[1]);
82
93
  if (coverage < 70) {
@@ -86,7 +97,11 @@ function validateTestResult(command, result) {
86
97
  }
87
98
 
88
99
  // Check for "no tests" scenarios
89
- if (resultLower.includes('no tests') || resultLower.includes('no test') || resultLower.includes('0 tests')) {
100
+ if (
101
+ resultLower.includes('no tests') ||
102
+ resultLower.includes('no test') ||
103
+ resultLower.includes('0 tests')
104
+ ) {
90
105
  issues.push('No tests were run - ensure test files exist and are properly configured');
91
106
  }
92
107
 
@@ -22,7 +22,7 @@ const fs = require('fs');
22
22
  const path = require('path');
23
23
 
24
24
  let input = '';
25
- process.stdin.on('data', chunk => input += chunk);
25
+ process.stdin.on('data', chunk => (input += chunk));
26
26
  process.stdin.on('end', () => {
27
27
  try {
28
28
  const context = JSON.parse(input);
@@ -77,7 +77,10 @@ function isWorkflowFile(filePath) {
77
77
  }
78
78
 
79
79
  // Azure Pipelines
80
- if (normalizedPath.endsWith('azure-pipelines.yml') || normalizedPath.endsWith('azure-pipelines.yaml')) {
80
+ if (
81
+ normalizedPath.endsWith('azure-pipelines.yml') ||
82
+ normalizedPath.endsWith('azure-pipelines.yaml')
83
+ ) {
81
84
  return true;
82
85
  }
83
86
 
@@ -115,7 +118,6 @@ function validateWorkflow(filePath) {
115
118
 
116
119
  // General CI/CD security checks
117
120
  issues.push(...validateCISecurity(content));
118
-
119
121
  } catch (e) {
120
122
  issues.push(`Read error: ${e.message}`);
121
123
  }
@@ -181,7 +183,10 @@ function validateGitHubActions(content) {
181
183
  }
182
184
 
183
185
  // Check for potentially dangerous permissions
184
- if (content.includes('permissions: write-all') || content.includes('permissions:\n contents: write')) {
186
+ if (
187
+ content.includes('permissions: write-all') ||
188
+ content.includes('permissions:\n contents: write')
189
+ ) {
185
190
  console.log('Note: Broad write permissions detected - ensure this is necessary');
186
191
  }
187
192
 
@@ -228,7 +233,9 @@ function validateCISecurity(content) {
228
233
 
229
234
  // Check for curl | bash pattern (security risk)
230
235
  if (content.includes('curl') && content.includes('| bash')) {
231
- issues.push('curl | bash pattern detected - this is a security risk, use verified installation methods');
236
+ issues.push(
237
+ 'curl | bash pattern detected - this is a security risk, use verified installation methods'
238
+ );
232
239
  }
233
240
 
234
241
  // Check for npm install without lockfile
@@ -108,7 +108,31 @@ node packages/cli/scripts/query-codebase.js --export="login"
108
108
 
109
109
  # Show dependencies
110
110
  node packages/cli/scripts/query-codebase.js --deps="src/auth.js"
111
+
112
+ # Show equivalent bash workflow (educational)
113
+ node packages/cli/scripts/query-codebase.js --query="auth" --explain
114
+
115
+ # Verbose mode shows step-by-step exploration
116
+ node packages/cli/scripts/query-codebase.js --query="auth" --verbose
117
+ ```
118
+
119
+ ### Understanding the Approach (--explain)
120
+ Use `--explain` to see the equivalent bash commands:
121
+ ```
122
+ 📖 Equivalent Bash Workflow:
123
+
124
+ # Step 1: List available directories (ls)
125
+ ls -la /project/src/
126
+
127
+ # Step 2: Find files matching pattern (find)
128
+ find /project -name "*auth*" -type f
129
+
130
+ # Step 3: Search content within files (grep)
131
+ grep -rl "auth" /project/src/
132
+
133
+ # This tool combines all three with indexing for speed.
111
134
  ```
135
+ This follows the Unix "everything is a file" philosophy - using file system navigation instead of vector databases (RAG).
112
136
 
113
137
  ### Result Format
114
138
  ```
@@ -269,6 +269,120 @@ After successfully creating the ADR, offer next steps:
269
269
 
270
270
  ---
271
271
 
272
+ ## Expected Output
273
+
274
+ ### Successful ADR Creation
275
+
276
+ ```
277
+ 📋 Creating ADR: ADR-0042
278
+
279
+ Checking existing ADRs...
280
+ ✅ Next sequential number: 0042
281
+
282
+ Title: Use PostgreSQL for persistence
283
+ Status: accepted
284
+
285
+ Files to create:
286
+ ───────────────────────────────────
287
+ 1. docs/03-decisions/adr-0042-postgresql.md
288
+
289
+ Preview:
290
+ ─────────────────────────────────────────────
291
+ ---
292
+ number: 0042
293
+ title: Use PostgreSQL for persistence
294
+ date: 2026-01-21
295
+ status: accepted
296
+ tags: [database, architecture]
297
+ ---
298
+
299
+ # ADR-0042: Use PostgreSQL for persistence
300
+
301
+ ## Context
302
+ Need reliable ACID-compliant database for financial transactions.
303
+ Evaluated MongoDB (eventual consistency), Redis (memory limits),
304
+ and PostgreSQL (full ACID with JSON support).
305
+
306
+ ## Decision
307
+ Use PostgreSQL 16 with native JSON support for document storage
308
+ while maintaining ACID guarantees.
309
+
310
+ ## Consequences
311
+ ### Positive
312
+ - Full ACID compliance for financial data
313
+ - Native JSON/JSONB for flexible schemas
314
+ - Mature ecosystem and tooling
315
+
316
+ ### Negative
317
+ - Team needs PostgreSQL training
318
+ - Slightly more complex operational setup
319
+ - Vertical scaling limitations
320
+
321
+ ## Related
322
+ - [ADR-0041](adr-0041-db-evaluation.md)
323
+ - [US-0055](../06-stories/US-0055.md)
324
+ ─────────────────────────────────────────────
325
+
326
+ [AskUserQuestion: "Create this ADR? (YES/NO)"]
327
+
328
+ ✅ ADR-0042 created successfully!
329
+ docs/03-decisions/adr-0042-postgresql.md
330
+
331
+ [AskUserQuestion: "What would you like to do next?"]
332
+ ```
333
+
334
+ ### Missing Required Inputs
335
+
336
+ ```
337
+ ❌ Missing required inputs
338
+
339
+ The following inputs are required:
340
+ • NUMBER - 4-digit sequential ID (e.g., 0042)
341
+ • TITLE - Decision title
342
+ • CONTEXT - Why this decision is needed
343
+ • DECISION - What was chosen
344
+ • CONSEQUENCES - Trade-offs (positive and negative)
345
+
346
+ Usage:
347
+ /agileflow:adr NUMBER=0042 TITLE="Use PostgreSQL" CONTEXT="Need ACID..." DECISION="PostgreSQL chosen" CONSEQUENCES="Better integrity, learning curve"
348
+ ```
349
+
350
+ ### Non-Sequential Number Warning
351
+
352
+ ```
353
+ ⚠️ Non-sequential ADR number
354
+
355
+ Requested: ADR-0100
356
+ Last ADR: ADR-0042
357
+ Next expected: ADR-0043
358
+
359
+ Do you want to:
360
+ 1. Use sequential number (0043) - Recommended
361
+ 2. Keep requested number (0100)
362
+ 3. Cancel
363
+
364
+ [AskUserQuestion: Select option]
365
+ ```
366
+
367
+ ### ADR Already Exists
368
+
369
+ ```
370
+ ❌ ADR-0042 already exists
371
+
372
+ Existing ADR:
373
+ Title: Use PostgreSQL for persistence
374
+ Date: 2026-01-15
375
+ Status: accepted
376
+
377
+ To update this ADR:
378
+ /agileflow:adr:update NUMBER=0042 STATUS=superseded
379
+
380
+ To create a new ADR:
381
+ /agileflow:adr NUMBER=0043 TITLE="..."
382
+ ```
383
+
384
+ ---
385
+
272
386
  ## Related Commands
273
387
 
274
388
  - `/agileflow:adr:list` - View all ADRs with status