wogiflow 2.4.2 → 2.4.4

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 (210) hide show
  1. package/.claude/commands/wogi-start.md +124 -0
  2. package/.claude/docs/claude-code-compatibility.md +51 -0
  3. package/.claude/docs/explore-agents.md +11 -0
  4. package/.claude/settings.json +12 -1
  5. package/.workflow/models/registry.json +1 -1
  6. package/bin/flow +11 -1
  7. package/lib/workspace-contracts.js +599 -0
  8. package/lib/workspace-intelligence.js +600 -0
  9. package/lib/workspace-messages.js +441 -0
  10. package/lib/workspace-routing.js +485 -0
  11. package/lib/workspace-sync.js +339 -0
  12. package/lib/workspace.js +1073 -0
  13. package/package.json +4 -4
  14. package/scripts/MEMORY-ARCHITECTURE.md +1 -1
  15. package/scripts/base-workflow-step.js +136 -0
  16. package/scripts/flow-adaptive-learning.js +8 -9
  17. package/scripts/flow-aggregate.js +11 -6
  18. package/scripts/flow-api-index.js +4 -6
  19. package/scripts/flow-assumption-detector.js +0 -2
  20. package/scripts/flow-audit.js +15 -2
  21. package/scripts/flow-auto-context.js +8 -12
  22. package/scripts/flow-auto-learn.js +49 -49
  23. package/scripts/flow-background.js +5 -6
  24. package/scripts/flow-bridge-state.js +8 -10
  25. package/scripts/flow-bulk-loop.js +1 -3
  26. package/scripts/flow-bulk-orchestrator.js +1 -3
  27. package/scripts/flow-cascade-completion.js +0 -2
  28. package/scripts/flow-cascade.js +4 -4
  29. package/scripts/flow-checkpoint.js +10 -13
  30. package/scripts/flow-code-intelligence.js +10 -12
  31. package/scripts/flow-community-sync.js +4 -4
  32. package/scripts/flow-community.js +12 -20
  33. package/scripts/flow-config-defaults.js +28 -2
  34. package/scripts/flow-config-interactive.js +9 -5
  35. package/scripts/flow-config-loader.js +49 -92
  36. package/scripts/flow-config-substitution.js +0 -2
  37. package/scripts/flow-context-estimator.js +4 -4
  38. package/scripts/flow-context-init.js +10 -12
  39. package/scripts/flow-context-manager.js +0 -2
  40. package/scripts/flow-context-scoring.js +2 -2
  41. package/scripts/flow-contract-scan.js +6 -9
  42. package/scripts/flow-correct.js +29 -27
  43. package/scripts/flow-correction-detector.js +5 -1
  44. package/scripts/flow-damage-control.js +47 -54
  45. package/scripts/flow-decisions-merge.js +4 -14
  46. package/scripts/flow-diff.js +5 -8
  47. package/scripts/flow-done-gates.js +786 -0
  48. package/scripts/flow-done-report.js +123 -0
  49. package/scripts/flow-done.js +71 -717
  50. package/scripts/flow-entropy-monitor.js +1 -3
  51. package/scripts/flow-eval-calibration.js +257 -0
  52. package/scripts/flow-eval-judge.js +10 -1
  53. package/scripts/flow-eval.js +14 -5
  54. package/scripts/flow-extraction-review.js +1 -0
  55. package/scripts/flow-failure-categories.js +0 -2
  56. package/scripts/flow-figma-confirm.js +5 -9
  57. package/scripts/flow-figma-generate.js +8 -10
  58. package/scripts/flow-figma-index.js +8 -10
  59. package/scripts/flow-figma-match.js +3 -5
  60. package/scripts/flow-figma-mcp-server.js +2 -4
  61. package/scripts/flow-figma-orchestrator.js +2 -3
  62. package/scripts/flow-figma-registry.js +2 -3
  63. package/scripts/flow-framework-resolver.js +0 -2
  64. package/scripts/flow-function-index.js +4 -6
  65. package/scripts/flow-gate-confidence.js +2 -2
  66. package/scripts/flow-gitignore.js +0 -2
  67. package/scripts/flow-guided-edit.js +5 -6
  68. package/scripts/flow-health.js +5 -6
  69. package/scripts/flow-hook-errors.js +6 -0
  70. package/scripts/flow-hook-status.js +263 -0
  71. package/scripts/flow-hooks.js +17 -29
  72. package/scripts/flow-http-client.js +9 -8
  73. package/scripts/flow-hybrid-interactive.js +7 -12
  74. package/scripts/flow-hybrid-test.js +12 -13
  75. package/scripts/flow-instruction-richness.js +1 -1
  76. package/scripts/flow-io.js +21 -4
  77. package/scripts/flow-knowledge-router.js +9 -3
  78. package/scripts/flow-learning-orchestrator.js +318 -13
  79. package/scripts/flow-links.js +5 -7
  80. package/scripts/flow-long-input-association.js +275 -0
  81. package/scripts/flow-long-input-chunking.js +1 -0
  82. package/scripts/flow-long-input-cli.js +0 -2
  83. package/scripts/flow-long-input-complexity.js +0 -2
  84. package/scripts/flow-long-input-constants.js +0 -2
  85. package/scripts/flow-long-input-contradictions.js +351 -0
  86. package/scripts/flow-long-input-detection.js +0 -2
  87. package/scripts/flow-long-input-passes.js +885 -0
  88. package/scripts/flow-long-input-stories.js +1 -1
  89. package/scripts/flow-long-input-voice.js +0 -2
  90. package/scripts/flow-long-input.js +425 -3005
  91. package/scripts/flow-loop-retry-learning.js +2 -3
  92. package/scripts/flow-lsp.js +3 -3
  93. package/scripts/flow-mcp-docs.js +3 -4
  94. package/scripts/flow-memory-db.js +6 -8
  95. package/scripts/flow-memory-sync.js +18 -11
  96. package/scripts/flow-metrics.js +1 -2
  97. package/scripts/flow-model-adapter.js +2 -3
  98. package/scripts/flow-model-config.js +72 -104
  99. package/scripts/flow-model-router.js +2 -2
  100. package/scripts/flow-model-types.js +0 -2
  101. package/scripts/flow-multi-approach.js +5 -6
  102. package/scripts/flow-orchestrate-context.js +3 -7
  103. package/scripts/flow-orchestrate-rollback.js +3 -8
  104. package/scripts/flow-orchestrate-state.js +8 -14
  105. package/scripts/flow-orchestrate-templates.js +2 -6
  106. package/scripts/flow-orchestrate-validator.js +5 -9
  107. package/scripts/flow-orchestrate.js +126 -103
  108. package/scripts/flow-output.js +0 -2
  109. package/scripts/flow-parallel.js +1 -1
  110. package/scripts/flow-paths.js +23 -2
  111. package/scripts/flow-pattern-enforcer.js +30 -28
  112. package/scripts/flow-pattern-extractor.js +3 -4
  113. package/scripts/flow-pending.js +0 -2
  114. package/scripts/flow-permissions.js +2 -3
  115. package/scripts/flow-plugin-registry.js +10 -12
  116. package/scripts/flow-prd-manager.js +1 -1
  117. package/scripts/flow-progress.js +7 -9
  118. package/scripts/flow-prompt-composer.js +3 -3
  119. package/scripts/flow-prompt-template.js +2 -2
  120. package/scripts/flow-providers.js +7 -4
  121. package/scripts/flow-registry-manager.js +7 -12
  122. package/scripts/flow-regression.js +9 -11
  123. package/scripts/flow-roadmap.js +2 -2
  124. package/scripts/flow-run-trace.js +16 -15
  125. package/scripts/flow-safety.js +2 -5
  126. package/scripts/flow-scanner-base.js +5 -7
  127. package/scripts/flow-scenario-engine.js +1 -5
  128. package/scripts/flow-security.js +29 -0
  129. package/scripts/flow-session-end.js +32 -41
  130. package/scripts/flow-session-learning.js +53 -49
  131. package/scripts/flow-setup-hooks.js +2 -3
  132. package/scripts/flow-skill-create.js +7 -12
  133. package/scripts/flow-skill-generator.js +12 -16
  134. package/scripts/flow-skill-learn.js +17 -8
  135. package/scripts/flow-skill-matcher.js +1 -2
  136. package/scripts/flow-spec-generator.js +2 -4
  137. package/scripts/flow-stack-wizard.js +5 -7
  138. package/scripts/flow-standards-learner.js +35 -16
  139. package/scripts/flow-start.js +2 -0
  140. package/scripts/flow-stats-collector.js +2 -2
  141. package/scripts/flow-status.js +10 -10
  142. package/scripts/flow-statusline-setup.js +2 -2
  143. package/scripts/flow-step-changelog.js +2 -3
  144. package/scripts/flow-step-comments.js +66 -81
  145. package/scripts/flow-step-complexity.js +50 -70
  146. package/scripts/flow-step-coverage.js +3 -5
  147. package/scripts/flow-step-knowledge.js +2 -3
  148. package/scripts/flow-step-pr-tests.js +64 -74
  149. package/scripts/flow-step-regression.js +3 -5
  150. package/scripts/flow-step-review.js +86 -103
  151. package/scripts/flow-step-security.js +111 -121
  152. package/scripts/flow-step-silent-failures.js +56 -83
  153. package/scripts/flow-step-simplifier.js +52 -70
  154. package/scripts/flow-story.js +4 -7
  155. package/scripts/flow-strict-adherence.js +3 -4
  156. package/scripts/flow-task-checkpoint.js +36 -5
  157. package/scripts/flow-task-enforcer.js +2 -24
  158. package/scripts/flow-tech-debt.js +1 -1
  159. package/scripts/flow-template-extractor.js +1 -0
  160. package/scripts/flow-templates.js +11 -13
  161. package/scripts/flow-test-api.js +9 -13
  162. package/scripts/flow-test-discovery.js +1 -1
  163. package/scripts/flow-test-generate.js +5 -9
  164. package/scripts/flow-test-integrity.js +3 -7
  165. package/scripts/flow-test-ui.js +5 -9
  166. package/scripts/flow-testing-deps.js +1 -3
  167. package/scripts/flow-tiered-learning.js +4 -4
  168. package/scripts/flow-todowrite-sync.js +1 -1
  169. package/scripts/flow-tokens.js +0 -2
  170. package/scripts/flow-verification-profile.js +6 -10
  171. package/scripts/flow-verify.js +12 -16
  172. package/scripts/flow-version-check.js +4 -12
  173. package/scripts/flow-webmcp-generator.js +3 -5
  174. package/scripts/flow-workflow-steps.js +0 -2
  175. package/scripts/flow-workflow.js +9 -11
  176. package/scripts/hooks/adapters/claude-code.js +31 -0
  177. package/scripts/hooks/core/config-change.js +1 -0
  178. package/scripts/hooks/core/extension-registry.js +0 -2
  179. package/scripts/hooks/core/instructions-loaded.js +1 -1
  180. package/scripts/hooks/core/observation-capture.js +5 -5
  181. package/scripts/hooks/core/phase-gate.js +5 -0
  182. package/scripts/hooks/core/post-compact.js +1 -12
  183. package/scripts/hooks/core/research-gate.js +2 -12
  184. package/scripts/hooks/core/routing-gate.js +6 -0
  185. package/scripts/hooks/core/task-completed.js +12 -0
  186. package/scripts/hooks/core/task-created.js +83 -0
  187. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  188. package/scripts/hooks/entry/claude-code/config-change.js +6 -29
  189. package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
  190. package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
  191. package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
  192. package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
  193. package/scripts/hooks/entry/claude-code/session-end.js +4 -28
  194. package/scripts/hooks/entry/claude-code/session-start.js +205 -243
  195. package/scripts/hooks/entry/claude-code/setup.js +8 -49
  196. package/scripts/hooks/entry/claude-code/stop.js +40 -72
  197. package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
  198. package/scripts/hooks/entry/claude-code/task-created.js +15 -0
  199. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
  200. package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
  201. package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
  202. package/scripts/hooks/entry/shared/hook-runner.js +99 -0
  203. package/scripts/hooks/entry/shared/read-stdin.js +0 -2
  204. package/scripts/postinstall.js +2 -0
  205. package/scripts/registries/api-registry.js +0 -2
  206. package/scripts/registries/component-registry.js +5 -9
  207. package/scripts/registries/contract-scanner.js +2 -9
  208. package/scripts/registries/function-registry.js +0 -2
  209. package/scripts/registries/schema-registry.js +14 -18
  210. package/scripts/registries/service-registry.js +23 -27
@@ -8,150 +8,140 @@
8
8
  */
9
9
 
10
10
  const { execSync } = require('node:child_process');
11
- const fs = require('node:fs');
12
- const path = require('node:path');
13
- const { getProjectRoot } = require('./flow-utils');
11
+ const { BaseWorkflowStep } = require('./base-workflow-step');
12
+ const { PATHS } = require('./flow-utils');
14
13
  const { CREDENTIAL_SCAN_PATTERNS } = require('./flow-security');
15
14
 
16
- const PROJECT_ROOT = getProjectRoot();
15
+ class SecurityStep extends BaseWorkflowStep {
16
+ constructor() {
17
+ // Security step checks ALL file types (not just code), so use broad extensions
18
+ super('securityScan', {
19
+ extensions: ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.rs', '.json', '.yml', '.yaml', '.env', '.sh'],
20
+ excludeTests: true,
21
+ excludeDts: true,
22
+ });
23
+ }
17
24
 
18
- /**
19
- * Run security scan as a workflow step
20
- *
21
- * @param {object} options
22
- * @param {string[]} options.files - Files modified
23
- * @param {object} options.stepConfig - Step configuration
24
- * @param {string} options.mode - Step mode (block/warn/prompt/auto)
25
- * @returns {object} - { passed: boolean, message: string, details?: object }
26
- */
27
- async function run(options = {}) {
28
- const { files = [], stepConfig = {}, mode } = options;
29
- const severity = stepConfig.severity || 'high';
30
- const issues = [];
31
-
32
- // 1. Check for secrets in modified files (using centralized patterns)
33
- for (const file of files) {
34
- const filePath = path.join(PROJECT_ROOT, file);
35
- if (!fs.existsSync(filePath)) continue;
36
-
37
- // Skip test files and config examples
38
- if (file.includes('.test.') || file.includes('.spec.') || file.includes('.example')) {
39
- continue;
40
- }
25
+ // Override filterFiles for security-specific exclusions
26
+ filterFiles(files) {
27
+ return files.filter(f => !f.includes('.example'));
28
+ }
29
+
30
+ async execute(files, options) {
31
+ const { stepConfig = {} } = options;
32
+ const severity = stepConfig.severity || 'high';
33
+ const issues = [];
41
34
 
42
- try {
43
- const content = fs.readFileSync(filePath, 'utf8');
44
- for (const { pattern, name } of CREDENTIAL_SCAN_PATTERNS) {
45
- // Reset lastIndex for regex with /g flag
46
- pattern.lastIndex = 0;
47
- if (pattern.test(content)) {
48
- issues.push({
49
- type: 'secret',
50
- severity: 'high',
51
- file,
52
- message: name || 'Potential secret or credential detected',
53
- });
54
- break; // One issue per file is enough
35
+ // 1. Check for secrets in modified files (using centralized patterns)
36
+ for (const file of files) {
37
+ const content = this.readFile(file);
38
+ if (!content) continue;
39
+
40
+ try {
41
+ for (const { pattern, name } of CREDENTIAL_SCAN_PATTERNS) {
42
+ pattern.lastIndex = 0;
43
+ if (pattern.test(content)) {
44
+ issues.push({
45
+ type: 'secret',
46
+ severity: 'high',
47
+ file,
48
+ message: name || 'Potential secret or credential detected',
49
+ });
50
+ break;
51
+ }
55
52
  }
53
+ } catch (_err) {
54
+ // Skip unreadable files
56
55
  }
57
- } catch (err) {
58
- // Skip unreadable files
59
56
  }
60
- }
61
57
 
62
- // 2. Run npm audit if package.json was modified
63
- const packageModified = files.some(f => f.endsWith('package.json') || f.endsWith('package-lock.json'));
64
-
65
- if (packageModified || stepConfig.alwaysAudit) {
66
- try {
67
- const auditResult = execSync('npm audit --json 2>/dev/null', {
68
- cwd: PROJECT_ROOT,
69
- encoding: 'utf8',
70
- stdio: ['pipe', 'pipe', 'pipe'],
71
- });
72
-
73
- const audit = JSON.parse(auditResult);
74
-
75
- if (audit.metadata && audit.metadata.vulnerabilities) {
76
- const vulns = audit.metadata.vulnerabilities;
77
-
78
- if (severity === 'critical' && vulns.critical > 0) {
79
- issues.push({
80
- type: 'npm_audit',
81
- severity: 'critical',
82
- message: `${vulns.critical} critical vulnerabilities found`,
83
- count: vulns.critical,
84
- });
85
- } else if (severity === 'high' && (vulns.critical > 0 || vulns.high > 0)) {
86
- const count = vulns.critical + vulns.high;
87
- issues.push({
88
- type: 'npm_audit',
89
- severity: 'high',
90
- message: `${count} high/critical vulnerabilities found`,
91
- count,
92
- });
93
- } else if (severity === 'moderate') {
94
- const count = vulns.critical + vulns.high + vulns.moderate;
95
- if (count > 0) {
58
+ // 2. Run npm audit if package.json was modified
59
+ const packageModified = files.some(f => f.endsWith('package.json') || f.endsWith('package-lock.json'));
60
+
61
+ if (packageModified || stepConfig.alwaysAudit) {
62
+ try {
63
+ const auditResult = execSync('npm audit --json 2>/dev/null', {
64
+ cwd: PATHS.root,
65
+ encoding: 'utf8',
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ });
68
+
69
+ const audit = JSON.parse(auditResult);
70
+
71
+ if (audit.metadata && audit.metadata.vulnerabilities) {
72
+ const vulns = audit.metadata.vulnerabilities;
73
+
74
+ if (severity === 'critical' && vulns.critical > 0) {
96
75
  issues.push({
97
76
  type: 'npm_audit',
98
- severity: 'moderate',
99
- message: `${count} moderate+ vulnerabilities found`,
100
- count,
77
+ severity: 'critical',
78
+ message: `${vulns.critical} critical vulnerabilities found`,
79
+ count: vulns.critical,
101
80
  });
102
- }
103
- }
104
- }
105
- } catch (err) {
106
- // npm audit failed or returned non-zero
107
- if (e.stdout) {
108
- try {
109
- const audit = JSON.parse(e.stdout);
110
- if (audit.metadata && audit.metadata.vulnerabilities) {
111
- const vulns = audit.metadata.vulnerabilities;
81
+ } else if (severity === 'high' && (vulns.critical > 0 || vulns.high > 0)) {
112
82
  const count = vulns.critical + vulns.high;
113
- if (count > 0 && (severity === 'high' || severity === 'critical')) {
83
+ issues.push({
84
+ type: 'npm_audit',
85
+ severity: 'high',
86
+ message: `${count} high/critical vulnerabilities found`,
87
+ count,
88
+ });
89
+ } else if (severity === 'moderate') {
90
+ const count = vulns.critical + vulns.high + vulns.moderate;
91
+ if (count > 0) {
114
92
  issues.push({
115
93
  type: 'npm_audit',
116
- severity: 'high',
117
- message: `${count} high/critical vulnerabilities`,
94
+ severity: 'moderate',
95
+ message: `${count} moderate+ vulnerabilities found`,
118
96
  count,
119
97
  });
120
98
  }
121
99
  }
122
- } catch (parseError) {
123
- // Ignore parse errors
100
+ }
101
+ } catch (err) {
102
+ // npm audit failed or returned non-zero
103
+ if (err.stdout) {
104
+ try {
105
+ const audit = JSON.parse(err.stdout);
106
+ if (audit.metadata && audit.metadata.vulnerabilities) {
107
+ const vulns = audit.metadata.vulnerabilities;
108
+ const count = vulns.critical + vulns.high;
109
+ if (count > 0 && (severity === 'high' || severity === 'critical')) {
110
+ issues.push({
111
+ type: 'npm_audit',
112
+ severity: 'high',
113
+ message: `${count} high/critical vulnerabilities`,
114
+ count,
115
+ });
116
+ }
117
+ }
118
+ } catch (_parseError) {
119
+ // Ignore parse errors
120
+ }
124
121
  }
125
122
  }
126
123
  }
127
- }
128
124
 
129
- // 3. Evaluate results
130
- if (issues.length === 0) {
131
- return { passed: true, message: 'Security scan passed' };
132
- }
125
+ // 3. Evaluate results
126
+ if (issues.length === 0) {
127
+ return this.pass('Security scan passed');
128
+ }
133
129
 
134
- // Filter by severity for blocking
135
- const blockingIssues = issues.filter(i => {
136
- if (severity === 'critical') return i.severity === 'critical';
137
- if (severity === 'high') return i.severity === 'high' || i.severity === 'critical';
138
- return true;
139
- });
140
-
141
- if (blockingIssues.length > 0) {
142
- return {
143
- passed: false,
144
- message: `${blockingIssues.length} security issue(s) found`,
145
- details: blockingIssues,
146
- };
147
- }
130
+ // Filter by severity for blocking
131
+ const blockingIssues = issues.filter(i => {
132
+ if (severity === 'critical') return i.severity === 'critical';
133
+ if (severity === 'high') return i.severity === 'high' || i.severity === 'critical';
134
+ return true;
135
+ });
136
+
137
+ if (blockingIssues.length > 0) {
138
+ return this.fail(`${blockingIssues.length} security issue(s) found`, blockingIssues);
139
+ }
148
140
 
149
- // Non-blocking issues
150
- return {
151
- passed: true,
152
- message: `${issues.length} low-severity issue(s) found`,
153
- details: issues,
154
- };
141
+ // Non-blocking issues
142
+ return this.pass(`${issues.length} low-severity issue(s) found`);
143
+ }
155
144
  }
156
145
 
157
- module.exports = { run };
146
+ const step = new SecurityStep();
147
+ module.exports = { run: (opts) => step.run(opts) };
@@ -11,86 +11,68 @@
11
11
  * - try-finally without catch
12
12
  */
13
13
 
14
- const fs = require('node:fs');
15
- const path = require('node:path');
16
- const { getProjectRoot, colors } = require('./flow-utils');
17
-
18
- const PROJECT_ROOT = getProjectRoot();
19
-
20
- /**
21
- * Run silent failure detection as a workflow step
22
- *
23
- * @param {object} options
24
- * @param {string[]} options.files - Files modified
25
- * @param {object} options.stepConfig - Step configuration
26
- * @param {string} options.mode - Step mode (block/warn/prompt/auto)
27
- * @returns {object} - { passed: boolean, message: string, details?: object[] }
28
- */
29
- async function run(options = {}) {
30
- const { files = [], stepConfig = {} } = options;
31
- const checkEmptyCatch = stepConfig.checkEmptyCatch !== false;
32
- const checkLogOnlyCatch = stepConfig.checkLogOnlyCatch !== false;
33
- const checkUnhandledAsync = stepConfig.checkUnhandledAsync !== false;
34
- const checkPromiseChains = stepConfig.checkPromiseChains !== false;
35
-
36
- // Filter to analyzable files
37
- const analyzableExtensions = ['.js', '.ts', '.jsx', '.tsx'];
38
- const analyzableFiles = files.filter(f =>
39
- analyzableExtensions.some(ext => f.endsWith(ext)) &&
40
- !f.includes('.test.') &&
41
- !f.includes('.spec.') &&
42
- !f.includes('.d.ts')
43
- );
44
-
45
- if (analyzableFiles.length === 0) {
46
- return { passed: true, message: 'No files to analyze' };
14
+ const { BaseWorkflowStep } = require('./base-workflow-step');
15
+ const { colors } = require('./flow-utils');
16
+
17
+ class SilentFailureStep extends BaseWorkflowStep {
18
+ constructor() {
19
+ super('silentFailureHunter', {
20
+ extensions: ['.js', '.ts', '.jsx', '.tsx'],
21
+ excludeTests: true,
22
+ excludeDts: true,
23
+ });
47
24
  }
48
25
 
49
- const issues = [];
50
-
51
- for (const file of analyzableFiles) {
52
- const filePath = path.join(PROJECT_ROOT, file);
53
- if (!fs.existsSync(filePath)) continue;
54
-
55
- try {
56
- const content = fs.readFileSync(filePath, 'utf8');
57
- const fileIssues = analyzeForSilentFailures(content, file, {
58
- checkEmptyCatch,
59
- checkLogOnlyCatch,
60
- checkUnhandledAsync,
61
- checkPromiseChains,
62
- });
63
- issues.push(...fileIssues);
64
- } catch (err) {
65
- // Skip unreadable files
26
+ async execute(files, options) {
27
+ const { stepConfig = {} } = options;
28
+ const checkEmptyCatch = stepConfig.checkEmptyCatch !== false;
29
+ const checkLogOnlyCatch = stepConfig.checkLogOnlyCatch !== false;
30
+ const checkUnhandledAsync = stepConfig.checkUnhandledAsync !== false;
31
+ const checkPromiseChains = stepConfig.checkPromiseChains !== false;
32
+
33
+ const issues = [];
34
+
35
+ for (const file of files) {
36
+ const content = this.readFile(file);
37
+ if (!content) continue;
38
+
39
+ try {
40
+ const fileIssues = analyzeForSilentFailures(content, file, {
41
+ checkEmptyCatch,
42
+ checkLogOnlyCatch,
43
+ checkUnhandledAsync,
44
+ checkPromiseChains,
45
+ });
46
+ issues.push(...fileIssues);
47
+ } catch (_err) {
48
+ // Skip unreadable files
49
+ }
66
50
  }
67
- }
68
51
 
69
- if (issues.length === 0) {
70
- return {
71
- passed: true,
72
- message: 'No silent failure patterns detected',
73
- };
74
- }
52
+ if (issues.length === 0) {
53
+ return this.pass('No silent failure patterns detected');
54
+ }
75
55
 
76
- // Report issues
77
- console.log(colors.yellow + '\n Silent Failure Patterns Detected:' + colors.reset);
78
- for (const issue of issues) {
79
- const icon = issue.severity === 'high' ? '\u{1F534}' : '\u{1F7E1}';
80
- console.log(` ${icon} ${issue.file}:${issue.line}`);
81
- console.log(` ${issue.type}: ${issue.message}`);
82
- if (issue.suggestion) {
83
- console.log(colors.dim + ` \u{2192} ${issue.suggestion}` + colors.reset);
56
+ // Report issues
57
+ console.log(colors.yellow + '\n Silent Failure Patterns Detected:' + colors.reset);
58
+ for (const issue of issues) {
59
+ const icon = issue.severity === 'high' ? '\u{1F534}' : '\u{1F7E1}';
60
+ console.log(` ${icon} ${issue.file}:${issue.line}`);
61
+ console.log(` ${issue.type}: ${issue.message}`);
62
+ if (issue.suggestion) {
63
+ console.log(colors.dim + ` \u{2192} ${issue.suggestion}` + colors.reset);
64
+ }
84
65
  }
85
- }
86
66
 
87
- const highSeverity = issues.filter(i => i.severity === 'high');
67
+ const highSeverity = issues.filter(i => i.severity === 'high');
88
68
 
89
- return {
90
- passed: highSeverity.length === 0,
91
- message: `${issues.length} silent failure pattern(s) found (${highSeverity.length} high severity)`,
92
- details: issues,
93
- };
69
+ return highSeverity.length === 0
70
+ ? this.pass(`${issues.length} silent failure pattern(s) found (${highSeverity.length} high severity)`)
71
+ : this.fail(
72
+ `${issues.length} silent failure pattern(s) found (${highSeverity.length} high severity)`,
73
+ issues
74
+ );
75
+ }
94
76
  }
95
77
 
96
78
  /**
@@ -159,9 +141,7 @@ function analyzeForSilentFailures(content, fileName, config) {
159
141
 
160
142
  // Check for unhandled promise chains (config.checkPromiseChains)
161
143
  if (config.checkPromiseChains) {
162
- // Look for .then() without .catch()
163
144
  if (/\.then\s*\(/.test(line) && !line.includes('.catch(') && !line.includes('await')) {
164
- // Check if .catch() is on the next few lines
165
145
  const nextLines = lines.slice(i, i + 5).join(' ');
166
146
  if (!nextLines.includes('.catch(') && !nextLines.includes('.finally(')) {
167
147
  issues.push({
@@ -180,7 +160,6 @@ function analyzeForSilentFailures(content, fileName, config) {
180
160
  if (config.checkUnhandledAsync) {
181
161
  const asyncMatch = line.match(/async\s+(?:function\s+)?(\w+)?/);
182
162
  if (asyncMatch && !line.includes('test') && !line.includes('spec')) {
183
- // Find the function body
184
163
  const funcStart = i;
185
164
  let funcBraceDepth = 0;
186
165
  let funcStarted = false;
@@ -195,10 +174,8 @@ function analyzeForSilentFailures(content, fileName, config) {
195
174
  if (funcStarted && funcBraceDepth === 0) break;
196
175
  }
197
176
 
198
- // Check if function has await but no try-catch
199
177
  if (/\bawait\b/.test(funcContent) && !/\btry\s*\{/.test(funcContent)) {
200
178
  const funcName = asyncMatch[1] || 'anonymous';
201
- // Don't flag if it's a short function (likely a wrapper)
202
179
  if (funcContent.split('\n').length > 5) {
203
180
  issues.push({
204
181
  file: fileName,
@@ -224,10 +201,8 @@ function analyzeCatchBlock(catchLines, startLine, fileName, config) {
224
201
  const issues = [];
225
202
  const catchBody = catchLines.join('\n').trim();
226
203
 
227
- // Remove the closing brace if present
228
204
  const cleanBody = catchBody.replace(/\}\s*$/, '').trim();
229
205
 
230
- // Check for empty catch block
231
206
  if (config.checkEmptyCatch && cleanBody.length === 0) {
232
207
  issues.push({
233
208
  file: fileName,
@@ -240,7 +215,6 @@ function analyzeCatchBlock(catchLines, startLine, fileName, config) {
240
215
  return issues;
241
216
  }
242
217
 
243
- // Check for catch blocks that only log
244
218
  if (config.checkLogOnlyCatch) {
245
219
  const onlyLogging = /^(?:console\.(?:log|error|warn)|logger\.(?:log|error|warn|info))\s*\(/i.test(cleanBody);
246
220
  const hasOtherStatements = cleanBody.split(';').filter(s => {
@@ -260,7 +234,6 @@ function analyzeCatchBlock(catchLines, startLine, fileName, config) {
260
234
  }
261
235
  }
262
236
 
263
- // Check for catch blocks that swallow specific errors
264
237
  if (/return\s*(?:null|undefined|false|''|""|``)/.test(cleanBody)) {
265
238
  issues.push({
266
239
  file: fileName,
@@ -272,7 +245,6 @@ function analyzeCatchBlock(catchLines, startLine, fileName, config) {
272
245
  });
273
246
  }
274
247
 
275
- // Check for catch block with only a comment
276
248
  if (/^\/[/*]/.test(cleanBody) && cleanBody.split('\n').every(l => l.trim().startsWith('//') || l.trim().startsWith('*') || l.trim() === '')) {
277
249
  issues.push({
278
250
  file: fileName,
@@ -287,4 +259,5 @@ function analyzeCatchBlock(catchLines, startLine, fileName, config) {
287
259
  return issues;
288
260
  }
289
261
 
290
- module.exports = { run, analyzeForSilentFailures };
262
+ const step = new SilentFailureStep();
263
+ module.exports = { run: (opts) => step.run(opts), analyzeForSilentFailures };
@@ -11,85 +11,66 @@
11
11
  * Both can be enabled together for comprehensive analysis.
12
12
  */
13
13
 
14
- const fs = require('node:fs');
15
- const path = require('node:path');
16
- const { getProjectRoot, colors } = require('./flow-utils');
17
-
18
- const PROJECT_ROOT = getProjectRoot();
19
-
20
- /**
21
- * Run code simplifier analysis step
22
- *
23
- * @param {object} options
24
- * @param {string[]} options.files - Files modified
25
- * @param {object} options.stepConfig - Step configuration
26
- * @param {string} options.mode - Step mode (block/warn/prompt/auto)
27
- * @returns {object} - { passed: boolean, message: string, details?: object[] }
28
- */
29
- async function run(options = {}) {
30
- const { files = [], stepConfig = {} } = options;
31
- const maxFunctionLines = stepConfig.maxFunctionLines || 50;
32
- const maxNestingDepth = stepConfig.maxNestingDepth || 3;
33
- const suggestExtraction = stepConfig.suggestExtraction !== false;
34
-
35
- // Filter to analyzable files
36
- const analyzableExtensions = ['.js', '.ts', '.jsx', '.tsx'];
37
- const analyzableFiles = files.filter(f =>
38
- analyzableExtensions.some(ext => f.endsWith(ext)) &&
39
- !f.includes('.test.') &&
40
- !f.includes('.spec.') &&
41
- !f.includes('.d.ts')
42
- );
43
-
44
- if (analyzableFiles.length === 0) {
45
- return { passed: true, message: 'No analyzable files modified' };
14
+ const { BaseWorkflowStep } = require('./base-workflow-step');
15
+ const { colors } = require('./flow-utils');
16
+
17
+ class SimplifierStep extends BaseWorkflowStep {
18
+ constructor() {
19
+ super('codeSimplifier', {
20
+ extensions: ['.js', '.ts', '.jsx', '.tsx'],
21
+ excludeTests: true,
22
+ excludeDts: true,
23
+ });
46
24
  }
47
25
 
48
- const suggestions = [];
26
+ async execute(files, options) {
27
+ const { stepConfig = {} } = options;
28
+ const maxFunctionLines = stepConfig.maxFunctionLines || 50;
29
+ const maxNestingDepth = stepConfig.maxNestingDepth || 3;
30
+ const suggestExtraction = stepConfig.suggestExtraction !== false;
49
31
 
50
- for (const file of analyzableFiles) {
51
- const filePath = path.join(PROJECT_ROOT, file);
52
- if (!fs.existsSync(filePath)) continue;
32
+ const suggestions = [];
53
33
 
54
- try {
55
- const content = fs.readFileSync(filePath, 'utf8');
56
- const fileSuggestions = analyzeForSimplification(content, file, {
57
- maxFunctionLines,
58
- maxNestingDepth,
59
- suggestExtraction,
60
- });
61
- suggestions.push(...fileSuggestions);
62
- } catch (err) {
63
- // Skip files that can't be analyzed
34
+ for (const file of files) {
35
+ const content = this.readFile(file);
36
+ if (!content) continue;
37
+
38
+ try {
39
+ const fileSuggestions = analyzeForSimplification(content, file, {
40
+ maxFunctionLines,
41
+ maxNestingDepth,
42
+ suggestExtraction,
43
+ });
44
+ suggestions.push(...fileSuggestions);
45
+ } catch (_err) {
46
+ // Skip files that can't be analyzed
47
+ }
64
48
  }
65
- }
66
49
 
67
- if (suggestions.length === 0) {
68
- return {
69
- passed: true,
70
- message: 'No simplification suggestions',
71
- };
72
- }
50
+ if (suggestions.length === 0) {
51
+ return this.pass('No simplification suggestions');
52
+ }
73
53
 
74
- // Report suggestions
75
- console.log(colors.cyan + '\n Code Simplification Suggestions:' + colors.reset);
76
- for (const suggestion of suggestions) {
77
- const icon = suggestion.severity === 'high' ? '\u{1F534}' : '\u{1F7E1}';
78
- console.log(` ${icon} ${suggestion.file}:${suggestion.line}`);
79
- console.log(` ${suggestion.type}: ${suggestion.message}`);
80
- if (suggestion.suggestion) {
81
- console.log(colors.dim + ` \u{2192} ${suggestion.suggestion}` + colors.reset);
54
+ // Report suggestions
55
+ console.log(colors.cyan + '\n Code Simplification Suggestions:' + colors.reset);
56
+ for (const suggestion of suggestions) {
57
+ const icon = suggestion.severity === 'high' ? '\u{1F534}' : '\u{1F7E1}';
58
+ console.log(` ${icon} ${suggestion.file}:${suggestion.line}`);
59
+ console.log(` ${suggestion.type}: ${suggestion.message}`);
60
+ if (suggestion.suggestion) {
61
+ console.log(colors.dim + ` \u{2192} ${suggestion.suggestion}` + colors.reset);
62
+ }
82
63
  }
83
- }
84
64
 
85
- // Mode determines if this blocks
86
- const highSeverity = suggestions.filter(s => s.severity === 'high');
65
+ const highSeverity = suggestions.filter(s => s.severity === 'high');
87
66
 
88
- return {
89
- passed: highSeverity.length === 0,
90
- message: `${suggestions.length} simplification suggestion(s) (${highSeverity.length} high severity)`,
91
- details: suggestions,
92
- };
67
+ return highSeverity.length === 0
68
+ ? this.pass(`${suggestions.length} simplification suggestion(s) (${highSeverity.length} high severity)`)
69
+ : this.fail(
70
+ `${suggestions.length} simplification suggestion(s) (${highSeverity.length} high severity)`,
71
+ suggestions
72
+ );
73
+ }
93
74
  }
94
75
 
95
76
  /**
@@ -343,4 +324,5 @@ function findDuplicationPatterns(content, fileName) {
343
324
  return suggestions;
344
325
  }
345
326
 
346
- module.exports = { run, analyzeForSimplification };
327
+ const step = new SimplifierStep();
328
+ module.exports = { run: (opts) => step.run(opts), analyzeForSimplification };
@@ -24,7 +24,7 @@ const {
24
24
  outputJson,
25
25
  withLock,
26
26
  safeJsonParse,
27
- isPathWithinProject
27
+ isPathWithinProject, PATHS
28
28
  } = require('./flow-utils');
29
29
  const { success, warn, error, info, print, printHeader } = require('./flow-output');
30
30
 
@@ -39,11 +39,8 @@ try {
39
39
  // Import parallel execution detection
40
40
  const { findParallelizable, getParallelConfig } = require('./flow-parallel');
41
41
 
42
- const PROJECT_ROOT = getProjectRoot();
43
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
44
- const CHANGES_DIR = path.join(WORKFLOW_DIR, 'changes');
45
- const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
46
- const READY_PATH = path.join(STATE_DIR, 'ready.json');
42
+ const CHANGES_DIR = PATHS.changes;
43
+ const READY_PATH = PATHS.ready;
47
44
 
48
45
  function log(color, ...args) {
49
46
  console.log(colors[color] + args.join(' ') + colors.reset);
@@ -335,7 +332,7 @@ async function createStory(title, options = {}) {
335
332
  targetDir = path.join(CHANGES_DIR, featureFolder);
336
333
 
337
334
  // Security: Validate path is within project (defense against path traversal)
338
- if (!isPathWithinProject(targetDir, PROJECT_ROOT)) {
335
+ if (!isPathWithinProject(targetDir, PATHS.root)) {
339
336
  throw new Error(`Security: Target directory "${targetDir}" is outside project root`);
340
337
  }
341
338
 
@@ -27,16 +27,15 @@ const {
27
27
  warn,
28
28
  error,
29
29
  info,
30
- success
30
+ success, PATHS
31
31
  } = require('./flow-utils');
32
32
 
33
33
  // ============================================================
34
34
  // Constants
35
35
  // ============================================================
36
36
 
37
- const PROJECT_ROOT = getProjectRoot();
38
- const STANDARDS_PATH = path.join(PROJECT_ROOT, '.workflow/state/project-standards.json');
39
- const OVERRIDES_PATH = path.join(PROJECT_ROOT, '.workflow/state/adherence-overrides.json');
37
+ const STANDARDS_PATH = path.join(PATHS.root, '.workflow/state/project-standards.json');
38
+ const OVERRIDES_PATH = path.join(PATHS.root, '.workflow/state/adherence-overrides.json');
40
39
 
41
40
  // Escape special regex characters for safe dynamic regex construction
42
41
  function escapeRegExp(string) {