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
@@ -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,78 @@ 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(
127
+ `find ${projectRoot} -type f | grep -E "${tagPatterns[queryValue] || queryValue}"`
128
+ );
129
+ lines.push('');
130
+ lines.push('# This tool uses pre-indexed tags for instant lookup.');
131
+ break;
132
+ }
133
+
134
+ case 'export':
135
+ lines.push('# Equivalent to grep for export statements:');
136
+ lines.push(`grep -rn "export.*${queryValue}" ${projectRoot}/src/ --include="*.{js,ts,tsx}"`);
137
+ lines.push('');
138
+ lines.push('# This tool tracks exports in index for instant symbol lookup.');
139
+ break;
140
+
141
+ case 'deps':
142
+ lines.push('# Equivalent to grep for imports:');
143
+ lines.push(`grep -n "import.*from" ${queryValue}`);
144
+ lines.push('');
145
+ lines.push('# Plus reverse search for files importing this one:');
146
+ lines.push(
147
+ `grep -rl "${path.basename(queryValue, path.extname(queryValue))}" ${projectRoot}/src/`
148
+ );
149
+ lines.push('');
150
+ lines.push('# This tool tracks bidirectional dependencies in index.');
151
+ break;
152
+
153
+ default:
154
+ lines.push('# Generic file system exploration:');
155
+ lines.push(`ls -la ${projectRoot}/`);
156
+ lines.push(`find ${projectRoot} -type f -name "*.ts" | head -20`);
157
+ }
158
+
159
+ lines.push('');
160
+ lines.push('─'.repeat(50));
161
+ return lines.join('\n');
162
+ }
163
+
88
164
  // Search file content using grep-style regex
89
165
  function queryContent(projectRoot, pattern, budget) {
90
166
  const results = [];
@@ -135,7 +211,14 @@ function queryContent(projectRoot, pattern, budget) {
135
211
  if (totalChars + resultChars > budget) {
136
212
  results.push({
137
213
  file: '...',
138
- matches: [{ line: 0, context: [{ lineNumber: 0, content: `[Truncated: budget exceeded]`, isMatch: false }] }],
214
+ matches: [
215
+ {
216
+ line: 0,
217
+ context: [
218
+ { lineNumber: 0, content: `[Truncated: budget exceeded]`, isMatch: false },
219
+ ],
220
+ },
221
+ ],
139
222
  });
140
223
  break;
141
224
  }
@@ -258,14 +341,21 @@ async function main() {
258
341
 
259
342
  // Handle --query (glob pattern search)
260
343
  if (args.query) {
261
- if (args.verbose) console.error(`Querying: ${args.query}`);
344
+ if (args.verbose) {
345
+ console.error(`\n🔍 Step 1: Querying for "${args.query}"`);
346
+ console.error(` Project: ${args.project}`);
347
+ }
262
348
 
263
349
  // First try to get/update index
350
+ if (args.verbose) console.error(`📂 Step 2: Checking/updating index...`);
264
351
  const indexResult = updateIndex(args.project);
265
352
  if (!indexResult.ok) {
266
353
  console.error(`Error: ${indexResult.error}`);
267
354
  process.exit(1);
268
355
  }
356
+ if (args.verbose) {
357
+ console.error(` Index has ${Object.keys(indexResult.data.files).length} files`);
358
+ }
269
359
 
270
360
  // Interpret query
271
361
  const query = args.query.toLowerCase();
@@ -273,29 +363,41 @@ async function main() {
273
363
 
274
364
  // If query looks like a glob, use it directly
275
365
  if (query.includes('*') || query.includes('/')) {
366
+ if (args.verbose) console.error(`🔎 Step 3: Glob pattern search: ${args.query}`);
276
367
  files = queryFiles(indexResult.data, args.query);
277
368
  } else {
278
369
  // Otherwise, search multiple ways:
370
+ if (args.verbose) console.error(`🔎 Step 3: Multi-strategy search...`);
371
+
279
372
  // 1. Files containing query in name
373
+ if (args.verbose) console.error(` 3a. Filename match: **/*${args.query}*`);
280
374
  files = queryFiles(indexResult.data, `**/*${args.query}*`);
375
+ if (args.verbose) console.error(` Found ${files.length} files by name`);
281
376
 
282
377
  // 2. Files with matching tag
378
+ if (args.verbose) console.error(` 3b. Tag search: ${query}`);
283
379
  const tagFiles = queryByTag(indexResult.data, query);
380
+ if (args.verbose) console.error(` Found ${tagFiles.length} files by tag`);
284
381
  files = [...new Set([...files, ...tagFiles])];
285
382
 
286
383
  // 3. Files exporting symbol
384
+ if (args.verbose) console.error(` 3c. Export search: ${args.query}`);
287
385
  const exportFiles = queryByExport(indexResult.data, args.query);
386
+ if (args.verbose) console.error(` Found ${exportFiles.length} files by export`);
288
387
  files = [...new Set([...files, ...exportFiles])];
289
388
  }
389
+ if (args.verbose) console.error(`✅ Step 4: Total unique results: ${files.length}\n`);
290
390
 
291
391
  if (files.length === 0) {
292
392
  console.error(`No files found matching: ${args.query}`);
293
393
  process.exit(2);
294
394
  }
295
395
 
296
- const output = args.json
297
- ? JSON.stringify(files, null, 2)
298
- : formatResults(files, 'files');
396
+ if (args.explain) {
397
+ console.log(explainWorkflow('query', args.query, args.project));
398
+ }
399
+
400
+ const output = args.json ? JSON.stringify(files, null, 2) : formatResults(files, 'files');
299
401
  console.log(truncateOutput(output, args.budget));
300
402
  process.exit(0);
301
403
  }
@@ -316,6 +418,10 @@ async function main() {
316
418
  process.exit(2);
317
419
  }
318
420
 
421
+ if (args.explain) {
422
+ console.log(explainWorkflow('content', args.content, args.project));
423
+ }
424
+
319
425
  const output = args.json
320
426
  ? JSON.stringify(result.data, null, 2)
321
427
  : formatResults(result.data, 'content');
@@ -340,9 +446,11 @@ async function main() {
340
446
  process.exit(2);
341
447
  }
342
448
 
343
- const output = args.json
344
- ? JSON.stringify(files, null, 2)
345
- : formatResults(files, 'files');
449
+ if (args.explain) {
450
+ console.log(explainWorkflow('tag', args.tag, args.project));
451
+ }
452
+
453
+ const output = args.json ? JSON.stringify(files, null, 2) : formatResults(files, 'files');
346
454
  console.log(truncateOutput(output, args.budget));
347
455
  process.exit(0);
348
456
  }
@@ -364,9 +472,11 @@ async function main() {
364
472
  process.exit(2);
365
473
  }
366
474
 
367
- const output = args.json
368
- ? JSON.stringify(files, null, 2)
369
- : formatResults(files, 'files');
475
+ if (args.explain) {
476
+ console.log(explainWorkflow('export', args.export, args.project));
477
+ }
478
+
479
+ const output = args.json ? JSON.stringify(files, null, 2) : formatResults(files, 'files');
370
480
  console.log(truncateOutput(output, args.budget));
371
481
  process.exit(0);
372
482
  }
@@ -388,9 +498,11 @@ async function main() {
388
498
  process.exit(2);
389
499
  }
390
500
 
391
- const output = args.json
392
- ? JSON.stringify(deps, null, 2)
393
- : formatResults(deps, 'deps');
501
+ if (args.explain) {
502
+ console.log(explainWorkflow('deps', args.deps, args.project));
503
+ }
504
+
505
+ const output = args.json ? JSON.stringify(deps, null, 2) : formatResults(deps, 'deps');
394
506
  console.log(truncateOutput(output, args.budget));
395
507
  process.exit(0);
396
508
  }
@@ -411,6 +523,7 @@ Options:
411
523
  --budget=<chars> Output budget in characters (default: 15000)
412
524
  --json Output as JSON
413
525
  --verbose Show debug info
526
+ --explain Show equivalent bash workflow (ls/find/grep/cat)
414
527
 
415
528
  Exit codes:
416
529
  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