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.
Files changed (99) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +3 -3
  3. package/lib/README.md +178 -0
  4. package/lib/codebase-indexer.js +31 -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 +435 -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 +43 -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 +122 -14
  46. package/scripts/ralph-loop.js +5 -5
  47. package/scripts/session-manager.js +220 -42
  48. package/scripts/spawn-parallel.js +651 -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 +113 -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 +86 -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/lib/config-manager.js +17 -2
  94. package/tools/cli/lib/content-transformer.js +271 -0
  95. package/tools/cli/lib/error-handler.js +14 -22
  96. package/tools/cli/lib/ide-error-factory.js +421 -0
  97. package/tools/cli/lib/ide-health-monitor.js +364 -0
  98. package/tools/cli/lib/ide-registry.js +114 -1
  99. package/tools/cli/lib/ui.js +14 -25
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # Pre-push validation script
3
+ # Run before pushing to catch CI failures locally
4
+ # Usage: npm run check (quick: lint + tests only, ~30s)
5
+ # npm run check:full (full: lint + tests + docs build, ~5m)
6
+
7
+ set -e
8
+
9
+ echo "🔍 Running pre-push checks..."
10
+ echo ""
11
+
12
+ # Track start time
13
+ START_TIME=$(date +%s)
14
+
15
+ # 1. Lint check (fastest, catches most issues)
16
+ echo "📋 Step 1/2: Linting..."
17
+ cd packages/cli
18
+ npm run lint 2>&1 | tail -30 || {
19
+ echo "❌ Lint failed! Fix errors before pushing."
20
+ exit 1
21
+ }
22
+ cd ../..
23
+ echo "✅ Lint passed"
24
+ echo ""
25
+
26
+ # 2. Run tests (catches logic errors)
27
+ echo "🧪 Step 2/2: Running tests..."
28
+ cd packages/cli
29
+ npm test 2>&1 | tail -20 || {
30
+ echo "❌ Tests failed! Fix errors before pushing."
31
+ exit 1
32
+ }
33
+ cd ../..
34
+ echo "✅ Tests passed"
35
+ echo ""
36
+
37
+ # Calculate elapsed time
38
+ END_TIME=$(date +%s)
39
+ ELAPSED=$((END_TIME - START_TIME))
40
+
41
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
42
+ echo "✅ Pre-push checks passed! (${ELAPSED}s)"
43
+ echo " Safe to push."
44
+ echo ""
45
+ echo "💡 Tip: Run 'npm run check:full' to include docs build"
46
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -74,16 +74,29 @@ if [ -f "docs/09-agents/session-state.json" ]; then
74
74
  fi
75
75
 
76
76
  if [ ! -z "$COMMAND_FILE" ]; then
77
- SUMMARY=$(node -e "
78
- const fs = require('fs');
79
- const content = fs.readFileSync('$COMMAND_FILE', 'utf8');
80
- const match = content.match(/<!-- COMPACT_SUMMARY_START[\\s\\S]*?-->([\\s\\S]*?)<!-- COMPACT_SUMMARY_END -->/);
81
- if (match) {
82
- console.log('## ACTIVE COMMAND: /agileflow:${ACTIVE_COMMAND}');
83
- console.log('');
84
- console.log(match[1].trim());
85
- }
86
- " 2>/dev/null || echo "")
77
+ # Security: Validate COMMAND_FILE contains only safe characters (alphanumeric, /, -, _, .)
78
+ # and doesn't contain path traversal sequences
79
+ if [[ "$COMMAND_FILE" =~ ^[a-zA-Z0-9/_.-]+$ ]] && [[ ! "$COMMAND_FILE" =~ \.\. ]]; then
80
+ SUMMARY=$(COMMAND_FILE_PATH="$COMMAND_FILE" ACTIVE_CMD="$ACTIVE_COMMAND" node -e "
81
+ const fs = require('fs');
82
+ const filePath = process.env.COMMAND_FILE_PATH;
83
+ const activeCmd = process.env.ACTIVE_CMD;
84
+ // Double-check: only allow paths within expected directories
85
+ const allowedPrefixes = ['packages/cli/src/core/commands/', '.agileflow/commands/', '.claude/commands/agileflow/'];
86
+ if (!allowedPrefixes.some(p => filePath.startsWith(p))) {
87
+ process.exit(1);
88
+ }
89
+ try {
90
+ const content = fs.readFileSync(filePath, 'utf8');
91
+ const match = content.match(/<!-- COMPACT_SUMMARY_START[\\s\\S]*?-->([\\s\\S]*?)<!-- COMPACT_SUMMARY_END -->/);
92
+ if (match) {
93
+ console.log('## ACTIVE COMMAND: /agileflow:' + activeCmd);
94
+ console.log('');
95
+ console.log(match[1].trim());
96
+ }
97
+ } catch (e) {}
98
+ " 2>/dev/null || echo "")
99
+ fi
87
100
 
88
101
  if [ ! -z "$SUMMARY" ]; then
89
102
  COMMAND_SUMMARIES="${COMMAND_SUMMARIES}
@@ -21,6 +21,7 @@
21
21
  * --budget=<chars> Token budget for output (default: 15000)
22
22
  * --json Output as JSON
23
23
  * --verbose Show debug info
24
+ * --explain Show equivalent bash workflow (ls/find/grep/cat)
24
25
  *
25
26
  * Exit codes:
26
27
  * 0 = Success
@@ -56,6 +57,7 @@ function parseArgs(argv) {
56
57
  budget: DEFAULT_BUDGET,
57
58
  json: false,
58
59
  verbose: false,
60
+ explain: false, // Show equivalent bash workflow
59
61
  };
60
62
 
61
63
  for (const arg of argv.slice(2)) {
@@ -65,6 +67,8 @@ function parseArgs(argv) {
65
67
  args.json = true;
66
68
  } else if (arg === '--verbose') {
67
69
  args.verbose = true;
70
+ } else if (arg === '--explain') {
71
+ args.explain = true;
68
72
  } else if (arg.startsWith('--query=')) {
69
73
  args.query = arg.slice(8);
70
74
  } else if (arg.startsWith('--deps=')) {
@@ -85,6 +89,73 @@ function parseArgs(argv) {
85
89
  return args;
86
90
  }
87
91
 
92
+ // Generate bash workflow explanation (research: ls → find → grep → cat pattern)
93
+ function explainWorkflow(queryType, queryValue, projectRoot) {
94
+ const lines = ['', '📖 Equivalent Bash Workflow:', ''];
95
+
96
+ switch (queryType) {
97
+ case 'query':
98
+ lines.push('# Step 1: List available directories (ls)');
99
+ lines.push(`ls -la ${projectRoot}/src/`);
100
+ lines.push('');
101
+ lines.push('# Step 2: Find files matching pattern (find)');
102
+ lines.push(`find ${projectRoot} -name "*${queryValue}*" -type f`);
103
+ lines.push('');
104
+ lines.push('# Step 3: Search content within files (grep)');
105
+ lines.push(`grep -rl "${queryValue}" ${projectRoot}/src/`);
106
+ lines.push('');
107
+ lines.push('# This tool combines all three with indexing for speed.');
108
+ break;
109
+
110
+ case 'content':
111
+ lines.push('# Equivalent to grep with context:');
112
+ lines.push(`grep -rn "${queryValue}" ${projectRoot}/src/ --include="*.{js,ts,tsx}"`);
113
+ lines.push('');
114
+ lines.push('# This tool adds: index awareness, budget truncation, structured output.');
115
+ break;
116
+
117
+ case 'tag':
118
+ const tagPatterns = {
119
+ api: '/api/|/routes/|/controllers/',
120
+ ui: '/components/|/views/|/pages/',
121
+ auth: '/auth/|/login/|/jwt/',
122
+ database: '/db/|/models/|/migrations/',
123
+ test: '/test/|/__tests__/|/spec/',
124
+ };
125
+ lines.push('# Equivalent to find with path patterns:');
126
+ lines.push(`find ${projectRoot} -type f | grep -E "${tagPatterns[queryValue] || queryValue}"`);
127
+ lines.push('');
128
+ lines.push('# This tool uses pre-indexed tags for instant lookup.');
129
+ break;
130
+
131
+ case 'export':
132
+ lines.push('# Equivalent to grep for export statements:');
133
+ lines.push(`grep -rn "export.*${queryValue}" ${projectRoot}/src/ --include="*.{js,ts,tsx}"`);
134
+ lines.push('');
135
+ lines.push('# This tool tracks exports in index for instant symbol lookup.');
136
+ break;
137
+
138
+ case 'deps':
139
+ lines.push('# Equivalent to grep for imports:');
140
+ lines.push(`grep -n "import.*from" ${queryValue}`);
141
+ lines.push('');
142
+ lines.push('# Plus reverse search for files importing this one:');
143
+ lines.push(`grep -rl "${path.basename(queryValue, path.extname(queryValue))}" ${projectRoot}/src/`);
144
+ lines.push('');
145
+ lines.push('# This tool tracks bidirectional dependencies in index.');
146
+ break;
147
+
148
+ default:
149
+ lines.push('# Generic file system exploration:');
150
+ lines.push(`ls -la ${projectRoot}/`);
151
+ lines.push(`find ${projectRoot} -type f -name "*.ts" | head -20`);
152
+ }
153
+
154
+ lines.push('');
155
+ lines.push('─'.repeat(50));
156
+ return lines.join('\n');
157
+ }
158
+
88
159
  // Search file content using grep-style regex
89
160
  function queryContent(projectRoot, pattern, budget) {
90
161
  const results = [];
@@ -135,7 +206,14 @@ function queryContent(projectRoot, pattern, budget) {
135
206
  if (totalChars + resultChars > budget) {
136
207
  results.push({
137
208
  file: '...',
138
- matches: [{ line: 0, context: [{ lineNumber: 0, content: `[Truncated: budget exceeded]`, isMatch: false }] }],
209
+ matches: [
210
+ {
211
+ line: 0,
212
+ context: [
213
+ { lineNumber: 0, content: `[Truncated: budget exceeded]`, isMatch: false },
214
+ ],
215
+ },
216
+ ],
139
217
  });
140
218
  break;
141
219
  }
@@ -258,14 +336,21 @@ async function main() {
258
336
 
259
337
  // Handle --query (glob pattern search)
260
338
  if (args.query) {
261
- if (args.verbose) console.error(`Querying: ${args.query}`);
339
+ if (args.verbose) {
340
+ console.error(`\n🔍 Step 1: Querying for "${args.query}"`);
341
+ console.error(` Project: ${args.project}`);
342
+ }
262
343
 
263
344
  // First try to get/update index
345
+ if (args.verbose) console.error(`📂 Step 2: Checking/updating index...`);
264
346
  const indexResult = updateIndex(args.project);
265
347
  if (!indexResult.ok) {
266
348
  console.error(`Error: ${indexResult.error}`);
267
349
  process.exit(1);
268
350
  }
351
+ if (args.verbose) {
352
+ console.error(` Index has ${Object.keys(indexResult.data.files).length} files`);
353
+ }
269
354
 
270
355
  // Interpret query
271
356
  const query = args.query.toLowerCase();
@@ -273,29 +358,41 @@ async function main() {
273
358
 
274
359
  // If query looks like a glob, use it directly
275
360
  if (query.includes('*') || query.includes('/')) {
361
+ if (args.verbose) console.error(`🔎 Step 3: Glob pattern search: ${args.query}`);
276
362
  files = queryFiles(indexResult.data, args.query);
277
363
  } else {
278
364
  // Otherwise, search multiple ways:
365
+ if (args.verbose) console.error(`🔎 Step 3: Multi-strategy search...`);
366
+
279
367
  // 1. Files containing query in name
368
+ if (args.verbose) console.error(` 3a. Filename match: **/*${args.query}*`);
280
369
  files = queryFiles(indexResult.data, `**/*${args.query}*`);
370
+ if (args.verbose) console.error(` Found ${files.length} files by name`);
281
371
 
282
372
  // 2. Files with matching tag
373
+ if (args.verbose) console.error(` 3b. Tag search: ${query}`);
283
374
  const tagFiles = queryByTag(indexResult.data, query);
375
+ if (args.verbose) console.error(` Found ${tagFiles.length} files by tag`);
284
376
  files = [...new Set([...files, ...tagFiles])];
285
377
 
286
378
  // 3. Files exporting symbol
379
+ if (args.verbose) console.error(` 3c. Export search: ${args.query}`);
287
380
  const exportFiles = queryByExport(indexResult.data, args.query);
381
+ if (args.verbose) console.error(` Found ${exportFiles.length} files by export`);
288
382
  files = [...new Set([...files, ...exportFiles])];
289
383
  }
384
+ if (args.verbose) console.error(`✅ Step 4: Total unique results: ${files.length}\n`);
290
385
 
291
386
  if (files.length === 0) {
292
387
  console.error(`No files found matching: ${args.query}`);
293
388
  process.exit(2);
294
389
  }
295
390
 
296
- const output = args.json
297
- ? JSON.stringify(files, null, 2)
298
- : formatResults(files, 'files');
391
+ if (args.explain) {
392
+ console.log(explainWorkflow('query', args.query, args.project));
393
+ }
394
+
395
+ const output = args.json ? JSON.stringify(files, null, 2) : formatResults(files, 'files');
299
396
  console.log(truncateOutput(output, args.budget));
300
397
  process.exit(0);
301
398
  }
@@ -316,6 +413,10 @@ async function main() {
316
413
  process.exit(2);
317
414
  }
318
415
 
416
+ if (args.explain) {
417
+ console.log(explainWorkflow('content', args.content, args.project));
418
+ }
419
+
319
420
  const output = args.json
320
421
  ? JSON.stringify(result.data, null, 2)
321
422
  : formatResults(result.data, 'content');
@@ -340,9 +441,11 @@ async function main() {
340
441
  process.exit(2);
341
442
  }
342
443
 
343
- const output = args.json
344
- ? JSON.stringify(files, null, 2)
345
- : formatResults(files, 'files');
444
+ if (args.explain) {
445
+ console.log(explainWorkflow('tag', args.tag, args.project));
446
+ }
447
+
448
+ const output = args.json ? JSON.stringify(files, null, 2) : formatResults(files, 'files');
346
449
  console.log(truncateOutput(output, args.budget));
347
450
  process.exit(0);
348
451
  }
@@ -364,9 +467,11 @@ async function main() {
364
467
  process.exit(2);
365
468
  }
366
469
 
367
- const output = args.json
368
- ? JSON.stringify(files, null, 2)
369
- : formatResults(files, 'files');
470
+ if (args.explain) {
471
+ console.log(explainWorkflow('export', args.export, args.project));
472
+ }
473
+
474
+ const output = args.json ? JSON.stringify(files, null, 2) : formatResults(files, 'files');
370
475
  console.log(truncateOutput(output, args.budget));
371
476
  process.exit(0);
372
477
  }
@@ -388,9 +493,11 @@ async function main() {
388
493
  process.exit(2);
389
494
  }
390
495
 
391
- const output = args.json
392
- ? JSON.stringify(deps, null, 2)
393
- : formatResults(deps, 'deps');
496
+ if (args.explain) {
497
+ console.log(explainWorkflow('deps', args.deps, args.project));
498
+ }
499
+
500
+ const output = args.json ? JSON.stringify(deps, null, 2) : formatResults(deps, 'deps');
394
501
  console.log(truncateOutput(output, args.budget));
395
502
  process.exit(0);
396
503
  }
@@ -411,6 +518,7 @@ Options:
411
518
  --budget=<chars> Output budget in characters (default: 15000)
412
519
  --json Output as JSON
413
520
  --verbose Show debug info
521
+ --explain Show equivalent bash workflow (ls/find/grep/cat)
414
522
 
415
523
  Exit codes:
416
524
  0 = Success
@@ -34,33 +34,33 @@ const { execSync, spawnSync } = require('child_process');
34
34
 
35
35
  // Shared utilities
36
36
  const { c } = require('../lib/colors');
37
- const { getProjectRoot } = require('../lib/paths');
37
+ const { getProjectRoot, getStatusPath, getSessionStatePath } = require('../lib/paths');
38
38
  const { safeReadJSON, safeWriteJSON } = require('../lib/errors');
39
39
  const { isValidEpicId, parseIntBounded } = require('../lib/validate');
40
40
 
41
41
  // Read session state
42
42
  function getSessionState(rootDir) {
43
- const statePath = path.join(rootDir, 'docs/09-agents/session-state.json');
43
+ const statePath = getSessionStatePath(rootDir);
44
44
  const result = safeReadJSON(statePath, { defaultValue: {} });
45
45
  return result.ok ? result.data : {};
46
46
  }
47
47
 
48
48
  // Write session state
49
49
  function saveSessionState(rootDir, state) {
50
- const statePath = path.join(rootDir, 'docs/09-agents/session-state.json');
50
+ const statePath = getSessionStatePath(rootDir);
51
51
  safeWriteJSON(statePath, state, { createDir: true });
52
52
  }
53
53
 
54
54
  // Read status.json for stories
55
55
  function getStatus(rootDir) {
56
- const statusPath = path.join(rootDir, 'docs/09-agents/status.json');
56
+ const statusPath = getStatusPath(rootDir);
57
57
  const result = safeReadJSON(statusPath, { defaultValue: { stories: {}, epics: {} } });
58
58
  return result.ok ? result.data : { stories: {}, epics: {} };
59
59
  }
60
60
 
61
61
  // Save status.json
62
62
  function saveStatus(rootDir, status) {
63
- const statusPath = path.join(rootDir, 'docs/09-agents/status.json');
63
+ const statusPath = getStatusPath(rootDir);
64
64
  safeWriteJSON(statusPath, status);
65
65
  }
66
66