agileflow 2.91.0 → 2.92.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.
- package/CHANGELOG.md +5 -0
- package/README.md +3 -3
- package/lib/README.md +178 -0
- package/lib/codebase-indexer.js +31 -23
- package/lib/colors.js +190 -12
- package/lib/consent.js +232 -0
- package/lib/correlation.js +277 -0
- package/lib/error-codes.js +46 -0
- package/lib/errors.js +48 -6
- package/lib/file-cache.js +182 -0
- package/lib/format-error.js +156 -0
- package/lib/path-resolver.js +155 -7
- package/lib/paths.js +212 -20
- package/lib/placeholder-registry.js +205 -0
- package/lib/registry-di.js +358 -0
- package/lib/result-schema.js +363 -0
- package/lib/result.js +210 -0
- package/lib/session-registry.js +13 -0
- package/lib/session-state-machine.js +465 -0
- package/lib/validate-commands.js +308 -0
- package/lib/validate.js +116 -52
- package/package.json +1 -1
- package/scripts/af +34 -0
- package/scripts/agent-loop.js +63 -9
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +435 -23
- package/scripts/archive-completed-stories.sh +57 -11
- package/scripts/claude-tmux.sh +102 -0
- package/scripts/damage-control-bash.js +3 -70
- package/scripts/damage-control-edit.js +3 -20
- package/scripts/damage-control-write.js +3 -20
- package/scripts/dependency-check.js +310 -0
- package/scripts/get-env.js +11 -4
- package/scripts/lib/configure-detect.js +23 -1
- package/scripts/lib/configure-features.js +43 -2
- package/scripts/lib/context-formatter.js +771 -0
- package/scripts/lib/context-loader.js +699 -0
- package/scripts/lib/damage-control-utils.js +107 -0
- package/scripts/lib/json-utils.sh +162 -0
- package/scripts/lib/state-migrator.js +353 -0
- package/scripts/lib/story-state-machine.js +437 -0
- package/scripts/obtain-context.js +80 -1248
- package/scripts/pre-push-check.sh +46 -0
- package/scripts/precompact-context.sh +23 -10
- package/scripts/query-codebase.js +122 -14
- package/scripts/ralph-loop.js +5 -5
- package/scripts/session-manager.js +220 -42
- package/scripts/spawn-parallel.js +651 -0
- package/scripts/tui/blessed/data/watcher.js +20 -15
- package/scripts/tui/blessed/index.js +2 -2
- package/scripts/tui/blessed/panels/output.js +14 -8
- package/scripts/tui/blessed/panels/sessions.js +22 -15
- package/scripts/tui/blessed/panels/trace.js +14 -8
- package/scripts/tui/blessed/ui/help.js +3 -3
- package/scripts/tui/blessed/ui/screen.js +4 -4
- package/scripts/tui/blessed/ui/statusbar.js +5 -9
- package/scripts/tui/blessed/ui/tabbar.js +11 -11
- package/scripts/validators/component-validator.js +41 -14
- package/scripts/validators/json-schema-validator.js +11 -4
- package/scripts/validators/markdown-validator.js +1 -2
- package/scripts/validators/migration-validator.js +17 -5
- package/scripts/validators/security-validator.js +137 -33
- package/scripts/validators/story-format-validator.js +31 -10
- package/scripts/validators/test-result-validator.js +19 -4
- package/scripts/validators/workflow-validator.js +12 -5
- package/src/core/agents/codebase-query.md +24 -0
- package/src/core/commands/adr.md +114 -0
- package/src/core/commands/agent.md +120 -0
- package/src/core/commands/assign.md +145 -0
- package/src/core/commands/babysit.md +32 -5
- package/src/core/commands/changelog.md +118 -0
- package/src/core/commands/configure.md +42 -6
- package/src/core/commands/diagnose.md +114 -0
- package/src/core/commands/epic.md +113 -0
- package/src/core/commands/handoff.md +128 -0
- package/src/core/commands/help.md +75 -0
- package/src/core/commands/pr.md +96 -0
- package/src/core/commands/roadmap/analyze.md +400 -0
- package/src/core/commands/session/new.md +113 -6
- package/src/core/commands/session/spawn.md +197 -0
- package/src/core/commands/sprint.md +22 -0
- package/src/core/commands/status.md +74 -0
- package/src/core/commands/story.md +143 -4
- package/src/core/templates/agileflow-metadata.json +55 -2
- package/src/core/templates/plan-template.md +125 -0
- package/src/core/templates/story-lifecycle.md +213 -0
- package/src/core/templates/story-template.md +4 -0
- package/src/core/templates/tdd-test-template.js +241 -0
- package/tools/cli/commands/setup.js +86 -0
- package/tools/cli/installers/core/installer.js +94 -0
- package/tools/cli/installers/ide/_base-ide.js +20 -11
- package/tools/cli/installers/ide/codex.js +29 -47
- package/tools/cli/lib/config-manager.js +17 -2
- package/tools/cli/lib/content-transformer.js +271 -0
- package/tools/cli/lib/error-handler.js +14 -22
- package/tools/cli/lib/ide-error-factory.js +421 -0
- package/tools/cli/lib/ide-health-monitor.js +364 -0
- package/tools/cli/lib/ide-registry.js +114 -1
- package/tools/cli/lib/ui.js +14 -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 = [
|
|
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
|
-
{
|
|
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
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{
|
|
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
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{
|
|
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
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
{
|
|
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
|
-
{
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
{
|
|
206
|
-
|
|
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
|
-
{
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
{
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
{
|
|
266
|
-
|
|
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(
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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(
|
|
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 = [
|
|
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(
|
|
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 = [
|
|
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(
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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(
|
|
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
|
```
|
package/src/core/commands/adr.md
CHANGED
|
@@ -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
|