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
@@ -4,6 +4,9 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const chokidar = require('chokidar');
6
6
 
7
+ // Import centralized path utilities
8
+ const { getStatusPath, getBusLogPath, getSessionStatePath } = require('../../../lib/paths');
9
+
7
10
  /**
8
11
  * DataWatcher - watches status.json and log files for changes
9
12
  * Triggers refresh callback when data updates
@@ -15,10 +18,10 @@ class DataWatcher {
15
18
  this.watcher = null;
16
19
  this.cwd = process.cwd();
17
20
 
18
- // Paths to watch
19
- this.statusPath = path.join(this.cwd, 'docs/09-agents/status.json');
20
- this.logPath = path.join(this.cwd, 'docs/09-agents/bus/log.jsonl');
21
- this.sessionStatePath = path.join(this.cwd, '.agileflow/session-state.json');
21
+ // Paths to watch (using centralized path utilities)
22
+ this.statusPath = getStatusPath(this.cwd);
23
+ this.logPath = getBusLogPath(this.cwd);
24
+ this.sessionStatePath = getSessionStatePath(this.cwd);
22
25
  }
23
26
 
24
27
  start() {
@@ -50,8 +53,8 @@ class DataWatcher {
50
53
  usePolling: false,
51
54
  awaitWriteFinish: {
52
55
  stabilityThreshold: 100,
53
- pollInterval: 50
54
- }
56
+ pollInterval: 50,
57
+ },
55
58
  });
56
59
 
57
60
  this.watcher.on('change', filePath => {
@@ -85,20 +88,22 @@ class DataWatcher {
85
88
  story: s.id || s.title || 'Unknown',
86
89
  status: 'active',
87
90
  duration: this.formatDuration(s.started_at),
88
- progress: s.progress || '--'
91
+ progress: s.progress || '--',
89
92
  }));
90
93
 
91
94
  // If no active stories, show all stories summary
92
95
  if (this.state.sessions.length === 0) {
93
96
  const ready = stories.filter(s => s.status === 'ready').length;
94
97
  const completed = stories.filter(s => s.status === 'completed').length;
95
- this.state.sessions = [{
96
- id: '--',
97
- story: `${ready} ready, ${completed} completed`,
98
- status: 'idle',
99
- duration: '--',
100
- progress: '--'
101
- }];
98
+ this.state.sessions = [
99
+ {
100
+ id: '--',
101
+ story: `${ready} ready, ${completed} completed`,
102
+ status: 'idle',
103
+ duration: '--',
104
+ progress: '--',
105
+ },
106
+ ];
102
107
  }
103
108
  }
104
109
  } catch (err) {
@@ -138,7 +143,7 @@ class DataWatcher {
138
143
  action: cmd.command || cmd,
139
144
  status: 'running',
140
145
  duration: '--',
141
- details: cmd.args || ''
146
+ details: cmd.args || '',
142
147
  }));
143
148
  }
144
149
  }
@@ -39,7 +39,7 @@ class AgileFlowTUI {
39
39
  sessions: [],
40
40
  logs: [],
41
41
  traces: [],
42
- showHelp: false
42
+ showHelp: false,
43
43
  };
44
44
  this.components = {};
45
45
  this.watcher = null;
@@ -90,7 +90,7 @@ class AgileFlowTUI {
90
90
  });
91
91
 
92
92
  // Tab switching with number keys
93
- this.screen.key(['1', '2', '3'], (ch) => {
93
+ this.screen.key(['1', '2', '3'], ch => {
94
94
  if (this.components.help.isVisible()) return;
95
95
  this.switchTab(parseInt(ch) - 1);
96
96
  });
@@ -16,22 +16,22 @@ module.exports = function createOutputPanel(grid, state) {
16
16
  tags: true,
17
17
  border: {
18
18
  type: 'line',
19
- fg: 'green'
19
+ fg: 'green',
20
20
  },
21
21
  style: {
22
22
  fg: 'white',
23
23
  bg: 'black',
24
- border: { fg: 'green' }
24
+ border: { fg: 'green' },
25
25
  },
26
26
  scrollable: true,
27
27
  alwaysScroll: true,
28
28
  scrollbar: {
29
29
  ch: '│',
30
- style: { fg: 'green' }
30
+ style: { fg: 'green' },
31
31
  },
32
32
  keys: true,
33
33
  vi: true,
34
- mouse: true
34
+ mouse: true,
35
35
  });
36
36
 
37
37
  let logs = [];
@@ -75,9 +75,15 @@ module.exports = function createOutputPanel(grid, state) {
75
75
 
76
76
  return {
77
77
  element: box,
78
- show() { box.show(); },
79
- hide() { box.hide(); },
80
- focus() { box.focus(); },
78
+ show() {
79
+ box.show();
80
+ },
81
+ hide() {
82
+ box.hide();
83
+ },
84
+ focus() {
85
+ box.focus();
86
+ },
81
87
  setData(data) {
82
88
  logs = data || [];
83
89
  render();
@@ -90,6 +96,6 @@ module.exports = function createOutputPanel(grid, state) {
90
96
  clear() {
91
97
  logs = [];
92
98
  render();
93
- }
99
+ },
94
100
  };
95
101
  };
@@ -17,21 +17,21 @@ module.exports = function createSessionsPanel(grid, state) {
17
17
  tags: true,
18
18
  border: {
19
19
  type: 'line',
20
- fg: 'cyan'
20
+ fg: 'cyan',
21
21
  },
22
22
  style: {
23
23
  fg: 'white',
24
24
  bg: 'black',
25
- border: { fg: 'cyan' }
25
+ border: { fg: 'cyan' },
26
26
  },
27
27
  scrollable: true,
28
28
  alwaysScroll: true,
29
29
  scrollbar: {
30
30
  ch: '│',
31
- style: { fg: 'cyan' }
31
+ style: { fg: 'cyan' },
32
32
  },
33
33
  keys: true,
34
- vi: true
34
+ vi: true,
35
35
  });
36
36
 
37
37
  // Header row
@@ -41,11 +41,12 @@ module.exports = function createSessionsPanel(grid, state) {
41
41
  left: 0,
42
42
  width: '100%-2',
43
43
  height: 1,
44
- content: '{bold}{cyan-fg} ID Story Status Time{/}{/}',
44
+ content:
45
+ '{bold}{cyan-fg} ID Story Status Time{/}{/}',
45
46
  tags: true,
46
47
  style: {
47
- bg: '#222222'
48
- }
48
+ bg: '#222222',
49
+ },
49
50
  });
50
51
 
51
52
  // Divider
@@ -57,8 +58,8 @@ module.exports = function createSessionsPanel(grid, state) {
57
58
  height: 1,
58
59
  content: ' ────────── ───────────────────────────────── ──────────── ──────',
59
60
  style: {
60
- fg: 'gray'
61
- }
61
+ fg: 'gray',
62
+ },
62
63
  });
63
64
 
64
65
  // Content area
@@ -70,8 +71,8 @@ module.exports = function createSessionsPanel(grid, state) {
70
71
  height: '100%-3',
71
72
  tags: true,
72
73
  style: {
73
- fg: 'white'
74
- }
74
+ fg: 'white',
75
+ },
75
76
  });
76
77
 
77
78
  let sessions = [];
@@ -117,9 +118,15 @@ module.exports = function createSessionsPanel(grid, state) {
117
118
 
118
119
  return {
119
120
  element: box,
120
- show() { box.show(); },
121
- hide() { box.hide(); },
122
- focus() { box.focus(); },
121
+ show() {
122
+ box.show();
123
+ },
124
+ hide() {
125
+ box.hide();
126
+ },
127
+ focus() {
128
+ box.focus();
129
+ },
123
130
  setData(data) {
124
131
  sessions = data || [];
125
132
  render();
@@ -138,6 +145,6 @@ module.exports = function createSessionsPanel(grid, state) {
138
145
  selectedIndex--;
139
146
  render();
140
147
  }
141
- }
148
+ },
142
149
  };
143
150
  };
@@ -16,21 +16,21 @@ module.exports = function createTracePanel(grid, state) {
16
16
  tags: true,
17
17
  border: {
18
18
  type: 'line',
19
- fg: 'yellow'
19
+ fg: 'yellow',
20
20
  },
21
21
  style: {
22
22
  fg: 'white',
23
23
  bg: 'black',
24
- border: { fg: 'yellow' }
24
+ border: { fg: 'yellow' },
25
25
  },
26
26
  scrollable: true,
27
27
  alwaysScroll: true,
28
28
  scrollbar: {
29
29
  ch: '│',
30
- style: { fg: 'yellow' }
30
+ style: { fg: 'yellow' },
31
31
  },
32
32
  keys: true,
33
- vi: true
33
+ vi: true,
34
34
  });
35
35
 
36
36
  let traces = [];
@@ -76,9 +76,15 @@ module.exports = function createTracePanel(grid, state) {
76
76
 
77
77
  return {
78
78
  element: box,
79
- show() { box.show(); },
80
- hide() { box.hide(); },
81
- focus() { box.focus(); },
79
+ show() {
80
+ box.show();
81
+ },
82
+ hide() {
83
+ box.hide();
84
+ },
85
+ focus() {
86
+ box.focus();
87
+ },
82
88
  setData(data) {
83
89
  traces = data || [];
84
90
  render();
@@ -86,6 +92,6 @@ module.exports = function createTracePanel(grid, state) {
86
92
  addStep(action, status = 'running', details = '') {
87
93
  traces.push({ action, status, details, duration: '--' });
88
94
  render();
89
- }
95
+ },
90
96
  };
91
97
  };
@@ -18,7 +18,7 @@ module.exports = function createHelpOverlay(screen, state) {
18
18
  style: {
19
19
  fg: 'white',
20
20
  bg: 'black',
21
- border: { fg: 'yellow' }
21
+ border: { fg: 'yellow' },
22
22
  },
23
23
  label: ' {yellow-fg}Help{/yellow-fg} ',
24
24
  hidden: true,
@@ -44,7 +44,7 @@ module.exports = function createHelpOverlay(screen, state) {
44
44
  {cyan-fg}q{/} Quit TUI
45
45
  {cyan-fg}Ctrl+C{/} Force quit
46
46
 
47
- {gray-fg}Press Escape or ? to close{/gray-fg}`
47
+ {gray-fg}Press Escape or ? to close{/gray-fg}`,
48
48
  });
49
49
 
50
50
  // Close help on various keys
@@ -72,6 +72,6 @@ module.exports = function createHelpOverlay(screen, state) {
72
72
  },
73
73
  isVisible() {
74
74
  return !help.hidden;
75
- }
75
+ },
76
76
  };
77
77
  };
@@ -11,18 +11,18 @@ const blessed = require('blessed');
11
11
  */
12
12
  module.exports = function createScreen() {
13
13
  const screen = blessed.screen({
14
- smartCSR: true, // Key for flicker-free differential rendering
15
- fullUnicode: true, // Support Unicode characters
14
+ smartCSR: true, // Key for flicker-free differential rendering
15
+ fullUnicode: true, // Support Unicode characters
16
16
  title: 'AgileFlow TUI',
17
17
  cursor: {
18
18
  artificial: true,
19
19
  blink: true,
20
- shape: 'line'
20
+ shape: 'line',
21
21
  },
22
22
  debug: false,
23
23
  warnings: false,
24
24
  autoPadding: true,
25
- dockBorders: true
25
+ dockBorders: true,
26
26
  });
27
27
 
28
28
  // Enable synchronized output mode for atomic updates
@@ -16,8 +16,8 @@ module.exports = function createStatusBar(screen, state) {
16
16
  tags: true,
17
17
  style: {
18
18
  fg: 'white',
19
- bg: 'blue'
20
- }
19
+ bg: 'blue',
20
+ },
21
21
  });
22
22
 
23
23
  // Always-visible key hints (nano-style for user-friendliness)
@@ -27,7 +27,7 @@ module.exports = function createStatusBar(screen, state) {
27
27
  '{bold}j/k{/bold}:Nav',
28
28
  '{bold}r{/bold}:Refresh',
29
29
  '{bold}?{/bold}:Help',
30
- '{bold}q{/bold}:Quit'
30
+ '{bold}q{/bold}:Quit',
31
31
  ];
32
32
 
33
33
  const hintText = ' ' + hints.join(' ');
@@ -37,15 +37,11 @@ module.exports = function createStatusBar(screen, state) {
37
37
  element: statusBar,
38
38
  setStatus(text) {
39
39
  // Show custom status with key hints
40
- const shortHints = [
41
- '{bold}r{/bold}:Refresh',
42
- '{bold}?{/bold}:Help',
43
- '{bold}q{/bold}:Quit'
44
- ];
40
+ const shortHints = ['{bold}r{/bold}:Refresh', '{bold}?{/bold}:Help', '{bold}q{/bold}:Quit'];
45
41
  statusBar.setContent(` ${text} | ${shortHints.join(' ')}`);
46
42
  },
47
43
  resetHints() {
48
44
  statusBar.setContent(hintText);
49
- }
45
+ },
50
46
  };
51
47
  };
@@ -14,8 +14,8 @@ module.exports = function createTabBar(screen, state) {
14
14
  width: '100%',
15
15
  height: 3,
16
16
  style: {
17
- bg: 'black'
18
- }
17
+ bg: 'black',
18
+ },
19
19
  });
20
20
 
21
21
  // Logo/title
@@ -28,8 +28,8 @@ module.exports = function createTabBar(screen, state) {
28
28
  content: '{bold}{#e8683a-fg}▄▀▄ AgileFlow{/}',
29
29
  tags: true,
30
30
  style: {
31
- bg: 'black'
32
- }
31
+ bg: 'black',
32
+ },
33
33
  });
34
34
 
35
35
  // Tab container
@@ -40,8 +40,8 @@ module.exports = function createTabBar(screen, state) {
40
40
  width: '100%-20',
41
41
  height: 3,
42
42
  style: {
43
- bg: 'black'
44
- }
43
+ bg: 'black',
44
+ },
45
45
  });
46
46
 
47
47
  // Create styled tabs
@@ -56,8 +56,8 @@ module.exports = function createTabBar(screen, state) {
56
56
  tags: true,
57
57
  style: {
58
58
  fg: 'white',
59
- bg: 'black'
60
- }
59
+ bg: 'black',
60
+ },
61
61
  });
62
62
  return tab;
63
63
  });
@@ -72,8 +72,8 @@ module.exports = function createTabBar(screen, state) {
72
72
  content: '{gray-fg}v2.90.7{/}',
73
73
  tags: true,
74
74
  style: {
75
- bg: 'black'
76
- }
75
+ bg: 'black',
76
+ },
77
77
  });
78
78
 
79
79
  return {
@@ -94,6 +94,6 @@ module.exports = function createTabBar(screen, state) {
94
94
  tab.setContent(`[${i + 1}] ${state.tabs[i]}`);
95
95
  }
96
96
  });
97
- }
97
+ },
98
98
  };
99
99
  };
@@ -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);
@@ -62,9 +62,11 @@ function isComponentFile(filePath) {
62
62
  // Also check for .js/.ts files in component directories
63
63
  if (['.js', '.ts'].includes(ext)) {
64
64
  const normalizedPath = filePath.toLowerCase();
65
- return normalizedPath.includes('/components/') ||
66
- normalizedPath.includes('/pages/') ||
67
- normalizedPath.includes('/views/');
65
+ return (
66
+ normalizedPath.includes('/components/') ||
67
+ normalizedPath.includes('/pages/') ||
68
+ normalizedPath.includes('/views/')
69
+ );
68
70
  }
69
71
 
70
72
  return componentExtensions.includes(ext);
@@ -101,7 +103,6 @@ function validateComponent(filePath) {
101
103
 
102
104
  // General accessibility checks
103
105
  issues.push(...validateAccessibility(content));
104
-
105
106
  } catch (e) {
106
107
  issues.push(`Read error: ${e.message}`);
107
108
  }
@@ -113,12 +114,22 @@ function validateReactComponent(content, fileName) {
113
114
  const issues = [];
114
115
 
115
116
  // Check for component export
116
- if (!content.includes('export default') && !content.includes('export function') && !content.includes('export const')) {
117
- issues.push('Component should have an export (export default, export function, or export const)');
117
+ if (
118
+ !content.includes('export default') &&
119
+ !content.includes('export function') &&
120
+ !content.includes('export const')
121
+ ) {
122
+ issues.push(
123
+ 'Component should have an export (export default, export function, or export const)'
124
+ );
118
125
  }
119
126
 
120
127
  // Check for React import in JSX files
121
- if (content.includes('React.') && !content.includes("from 'react'") && !content.includes('from "react"')) {
128
+ if (
129
+ content.includes('React.') &&
130
+ !content.includes("from 'react'") &&
131
+ !content.includes('from "react"')
132
+ ) {
122
133
  issues.push('Using React. prefix but React is not imported');
123
134
  }
124
135
 
@@ -132,12 +143,18 @@ function validateReactComponent(content, fileName) {
132
143
  // Check for inline styles (prefer CSS modules or styled-components)
133
144
  const inlineStyleCount = (content.match(/style=\{\{/g) || []).length;
134
145
  if (inlineStyleCount > 5) {
135
- issues.push(`Too many inline styles (${inlineStyleCount}) - consider using CSS modules or styled-components`);
146
+ issues.push(
147
+ `Too many inline styles (${inlineStyleCount}) - consider using CSS modules or styled-components`
148
+ );
136
149
  }
137
150
 
138
151
  // Check for console.log in production code
139
- if (content.includes('console.log') && !content.includes('// debug') && !content.includes('// DEBUG')) {
140
- console.log('Note: console.log found - ensure it\'s removed before production');
152
+ if (
153
+ content.includes('console.log') &&
154
+ !content.includes('// debug') &&
155
+ !content.includes('// DEBUG')
156
+ ) {
157
+ console.log("Note: console.log found - ensure it's removed before production");
141
158
  }
142
159
 
143
160
  // Check for missing key prop in map
@@ -163,7 +180,11 @@ function validateVueComponent(content) {
163
180
  }
164
181
 
165
182
  // Check for scoped styles (recommended)
166
- if (content.includes('<style>') && !content.includes('<style scoped') && !content.includes('scoped>')) {
183
+ if (
184
+ content.includes('<style>') &&
185
+ !content.includes('<style scoped') &&
186
+ !content.includes('scoped>')
187
+ ) {
167
188
  console.log('Note: Consider using scoped styles to prevent CSS leaks');
168
189
  }
169
190
 
@@ -200,11 +221,17 @@ function validateAccessibility(content) {
200
221
  // Check for click handlers on non-interactive elements
201
222
  const clickOnDiv = content.match(/onClick[^>]*>[^<]*<\/div>/gi) || [];
202
223
  if (clickOnDiv.length > 0) {
203
- issues.push('onClick on <div> detected - use <button> for interactive elements (accessibility)');
224
+ issues.push(
225
+ 'onClick on <div> detected - use <button> for interactive elements (accessibility)'
226
+ );
204
227
  }
205
228
 
206
229
  // Check for form inputs without labels
207
- if (content.includes('<input') && !content.includes('<label') && !content.includes('aria-label')) {
230
+ if (
231
+ content.includes('<input') &&
232
+ !content.includes('<label') &&
233
+ !content.includes('aria-label')
234
+ ) {
208
235
  console.log('Note: <input> elements should have associated <label> or aria-label');
209
236
  }
210
237
 
@@ -21,8 +21,16 @@
21
21
  const fs = require('fs');
22
22
  const path = require('path');
23
23
 
24
+ // Import status constants from single source of truth
25
+ const { VALID_STATUSES } = require('../lib/story-state-machine');
26
+
27
+ // Extended statuses for backward compatibility (maps to canonical values)
28
+ // These legacy values are accepted but should be migrated to canonical values
29
+ const LEGACY_STATUSES = ['pending', 'done', 'in-progress', 'in-review'];
30
+ const ALL_ACCEPTED_STATUSES = [...VALID_STATUSES, ...LEGACY_STATUSES];
31
+
24
32
  let input = '';
25
- process.stdin.on('data', chunk => input += chunk);
33
+ process.stdin.on('data', chunk => (input += chunk));
26
34
  process.stdin.on('end', () => {
27
35
  try {
28
36
  const context = JSON.parse(input);
@@ -85,7 +93,6 @@ function validateJson(filePath) {
85
93
  } else if (fileName === 'tsconfig.json') {
86
94
  issues.push(...validateTsConfig(data));
87
95
  }
88
-
89
96
  } catch (e) {
90
97
  if (e instanceof SyntaxError) {
91
98
  issues.push(`Invalid JSON syntax: ${e.message}`);
@@ -132,7 +139,7 @@ function validateStatusJson(data) {
132
139
  if (!story.title && !story.name) {
133
140
  issues.push(`Story ${story.id || index} missing 'title' or 'name' field`);
134
141
  }
135
- if (story.status && !['pending', 'in_progress', 'completed', 'done', 'blocked', 'ready', 'in-progress', 'in_review', 'in-review', 'archived'].includes(story.status)) {
142
+ if (story.status && !ALL_ACCEPTED_STATUSES.includes(story.status)) {
136
143
  issues.push(`Story ${story.id || index} has invalid status: ${story.status}`);
137
144
  }
138
145
  });
@@ -142,7 +149,7 @@ function validateStatusJson(data) {
142
149
  if (!story.title && !story.name) {
143
150
  issues.push(`Story ${storyId} missing 'title' or 'name' field`);
144
151
  }
145
- if (story.status && !['pending', 'in_progress', 'completed', 'done', 'blocked', 'ready', 'in-progress', 'in_review', 'in-review', 'archived'].includes(story.status)) {
152
+ if (story.status && !ALL_ACCEPTED_STATUSES.includes(story.status)) {
146
153
  issues.push(`Story ${storyId} has invalid status: ${story.status}`);
147
154
  }
148
155
  });
@@ -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);
@@ -108,7 +108,6 @@ function validateMarkdown(filePath) {
108
108
  } else if (filePath.includes('/10-research/')) {
109
109
  issues.push(...validateResearchNote(content));
110
110
  }
111
-
112
111
  } catch (e) {
113
112
  issues.push(`Read error: ${e.message}`);
114
113
  }
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  let input = '';
22
- process.stdin.on('data', chunk => input += chunk);
22
+ process.stdin.on('data', chunk => (input += chunk));
23
23
  process.stdin.on('end', () => {
24
24
  try {
25
25
  const context = JSON.parse(input);
@@ -71,10 +71,19 @@ function validateMigration(command, result) {
71
71
 
72
72
  // Check for destructive operations without safeguards
73
73
  const destructivePatterns = [
74
- { pattern: /DROP\s+TABLE/i, message: 'DROP TABLE detected - ensure backup exists and this is intentional' },
75
- { pattern: /DROP\s+DATABASE/i, message: 'DROP DATABASE detected - this is extremely destructive!' },
74
+ {
75
+ pattern: /DROP\s+TABLE/i,
76
+ message: 'DROP TABLE detected - ensure backup exists and this is intentional',
77
+ },
78
+ {
79
+ pattern: /DROP\s+DATABASE/i,
80
+ message: 'DROP DATABASE detected - this is extremely destructive!',
81
+ },
76
82
  { pattern: /TRUNCATE/i, message: 'TRUNCATE detected - this removes all data permanently' },
77
- { pattern: /DELETE\s+FROM.*WHERE\s*$/i, message: 'DELETE without WHERE clause detected - will delete all rows' },
83
+ {
84
+ pattern: /DELETE\s+FROM.*WHERE\s*$/i,
85
+ message: 'DELETE without WHERE clause detected - will delete all rows',
86
+ },
78
87
  { pattern: /--force|--skip-safe/i, message: 'Force flag used - bypassing safety checks' },
79
88
  ];
80
89
 
@@ -94,7 +103,10 @@ function validateMigration(command, result) {
94
103
  { pattern: /error.*migration/i, message: 'Migration error detected in output' },
95
104
  { pattern: /rollback.*failed/i, message: 'Rollback failure detected' },
96
105
  { pattern: /constraint.*violation/i, message: 'Database constraint violation' },
97
- { pattern: /duplicate.*key/i, message: 'Duplicate key error - migration may have partially applied' },
106
+ {
107
+ pattern: /duplicate.*key/i,
108
+ message: 'Duplicate key error - migration may have partially applied',
109
+ },
98
110
  { pattern: /already exists/i, message: 'Object already exists - migration may need cleanup' },
99
111
  ];
100
112