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
@@ -308,17 +308,48 @@ function updateScenarioProgress(scenarioIndex, scenarioTitle, passed) {
308
308
  * @param {string} filePath - Path of the changed file
309
309
  * @returns {boolean} True if added
310
310
  */
311
- function trackChangedFile(filePath) {
311
+ // In-process batch for trackChangedFile — accumulates changes and flushes once
312
+ let _pendingFiles = [];
313
+ let _flushScheduled = false;
314
+
315
+ function _flushPendingFiles() {
316
+ if (_pendingFiles.length === 0) return;
312
317
  try {
313
318
  const checkpoint = loadCheckpoint();
314
- if (!checkpoint) return false;
315
-
316
- if (!checkpoint.changedFiles.includes(filePath)) {
317
- checkpoint.changedFiles.push(filePath);
319
+ if (!checkpoint) { _pendingFiles = []; return; }
320
+ let changed = false;
321
+ for (const fp of _pendingFiles) {
322
+ if (!checkpoint.changedFiles.includes(fp)) {
323
+ checkpoint.changedFiles.push(fp);
324
+ changed = true;
325
+ }
326
+ }
327
+ if (changed) {
318
328
  checkpoint.lastUpdated = new Date().toISOString();
319
329
  writeJson(CHECKPOINT_PATH, checkpoint);
320
330
  }
331
+ } catch (err) {
332
+ if (process.env.DEBUG) {
333
+ console.error(`[checkpoint] Batch flush failed: ${err.message}`);
334
+ }
335
+ }
336
+ _pendingFiles = [];
337
+ }
338
+
339
+ // Flush on process exit to ensure no data loss
340
+ process.on('exit', _flushPendingFiles);
321
341
 
342
+ function trackChangedFile(filePath) {
343
+ try {
344
+ if (!_pendingFiles.includes(filePath)) _pendingFiles.push(filePath);
345
+ // Schedule a single flush at end of event loop tick (batches multiple calls)
346
+ if (!_flushScheduled) {
347
+ _flushScheduled = true;
348
+ setImmediate(() => {
349
+ _flushScheduled = false;
350
+ _flushPendingFiles();
351
+ });
352
+ }
322
353
  return true;
323
354
  } catch (err) {
324
355
  if (process.env.DEBUG) {
@@ -14,8 +14,9 @@
14
14
 
15
15
  const fs = require('node:fs');
16
16
  const path = require('node:path');
17
- const { getConfig, getProjectRoot, writeJson, readJson, safeJsonParse } = require('./flow-utils');
17
+ const { getConfig, getProjectRoot, writeJson, readJson, safeJsonParse, PATHS } = require('./flow-utils');
18
18
  const { getCommand } = require('./flow-script-resolver');
19
+ const { sanitizeShellArg } = require('./flow-security');
19
20
 
20
21
  // v2.0: Import durable session for unified tracking
21
22
  const durableSession = require('./flow-durable-session');
@@ -30,29 +31,6 @@ function getTaskConfig() {
30
31
  return config.tasks || config.loops || {};
31
32
  }
32
33
 
33
- /**
34
- * Sanitize a string for safe use in shell commands
35
- * Only allows alphanumeric, underscore, hyphen, and dot characters
36
- * @param {string} str - String to sanitize
37
- * @returns {string} - Sanitized string
38
- */
39
- function sanitizeShellArg(str) {
40
- if (!str || typeof str !== 'string') return '';
41
- // Only allow safe characters: alphanumeric, underscore, hyphen, dot
42
- return str.replace(/[^a-zA-Z0-9_.-]/g, '');
43
- }
44
-
45
- /**
46
- * Escape a path for safe use in shell commands
47
- * @param {string} p - Path to escape
48
- * @returns {string} - Escaped path
49
- */
50
- function escapeShellPath(p) {
51
- if (!p || typeof p !== 'string') return '';
52
- // Escape special shell characters in paths
53
- return p.replace(/(["\s'$`\\!*?#~<>^()[\]{}|;&])/g, '\\$1');
54
- }
55
-
56
34
  /**
57
35
  * Check if task enforcement is enabled
58
36
  */
@@ -35,7 +35,7 @@ const {
35
35
  fileExists,
36
36
  getProjectRoot,
37
37
  ensureDir,
38
- generateTaskId
38
+ generateTaskId, PATHS
39
39
  } = require('./flow-utils');
40
40
  const { success: printSuccess, warn: printWarn } = require('./flow-output');
41
41
 
@@ -382,6 +382,7 @@ function generateTemplate(file, type) {
382
382
  function isStructuralLine(trimmed, type) {
383
383
  // Import statements
384
384
  if (trimmed.startsWith('import ') || trimmed.startsWith('const ') && trimmed.includes('require(')) return true;
385
+ const { PATHS } = require('./flow-utils');
385
386
  if (trimmed.startsWith('from ')) return true;
386
387
 
387
388
  // Export statements
@@ -13,11 +13,9 @@
13
13
 
14
14
  const fs = require('node:fs');
15
15
  const path = require('node:path');
16
- const { getProjectRoot, readJson, info } = require('./flow-utils');
16
+ const { getProjectRoot, readJson, info, PATHS } = require('./flow-utils');
17
17
 
18
- const PROJECT_ROOT = getProjectRoot();
19
- const TEMPLATES_DIR = path.join(PROJECT_ROOT, 'templates', 'hybrid');
20
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
18
+ const TEMPLATES_DIR = path.join(PATHS.root, 'templates', 'hybrid');
21
19
 
22
20
  // ============================================================
23
21
  // Project Analyzer
@@ -139,7 +137,7 @@ class ProjectAnalyzer {
139
137
 
140
138
  const componentDirs = ['src/components', 'components', 'app/components'];
141
139
  for (const dir of componentDirs) {
142
- if (fs.existsSync(path.join(PROJECT_ROOT, dir))) {
140
+ if (fs.existsSync(path.join(PATHS.root, dir))) {
143
141
  this.patterns.components = this.findFilesWithExports(dir, /\.(tsx|jsx)$/);
144
142
  console.log(` Components: ${this.patterns.components.length} found`);
145
143
  break;
@@ -148,7 +146,7 @@ class ProjectAnalyzer {
148
146
 
149
147
  const hookDirs = ['src/hooks', 'hooks', 'app/hooks'];
150
148
  for (const dir of hookDirs) {
151
- if (fs.existsSync(path.join(PROJECT_ROOT, dir))) {
149
+ if (fs.existsSync(path.join(PATHS.root, dir))) {
152
150
  this.patterns.hooks = this.findFilesWithExports(dir, /^use.*\.(ts|tsx)$/);
153
151
  console.log(` Hooks: ${this.patterns.hooks.length} found`);
154
152
  break;
@@ -157,7 +155,7 @@ class ProjectAnalyzer {
157
155
 
158
156
  const serviceDirs = ['src/services', 'services', 'src/api', 'api', 'src/lib'];
159
157
  for (const dir of serviceDirs) {
160
- if (fs.existsSync(path.join(PROJECT_ROOT, dir))) {
158
+ if (fs.existsSync(path.join(PATHS.root, dir))) {
161
159
  this.patterns.services = this.findFilesWithExports(dir, /\.(ts|js)$/);
162
160
  console.log(` Services: ${this.patterns.services.length} found`);
163
161
  break;
@@ -166,7 +164,7 @@ class ProjectAnalyzer {
166
164
 
167
165
  const utilDirs = ['src/utils', 'utils', 'src/lib/utils', 'lib/utils'];
168
166
  for (const dir of utilDirs) {
169
- if (fs.existsSync(path.join(PROJECT_ROOT, dir))) {
167
+ if (fs.existsSync(path.join(PATHS.root, dir))) {
170
168
  this.patterns.utilities = this.findFilesWithExports(dir, /\.(ts|js)$/);
171
169
  console.log(` Utilities: ${this.patterns.utilities.length} found`);
172
170
  break;
@@ -176,7 +174,7 @@ class ProjectAnalyzer {
176
174
 
177
175
  findFilesWithExports(dir, pattern) {
178
176
  const results = [];
179
- const fullDir = path.join(PROJECT_ROOT, dir);
177
+ const fullDir = path.join(PATHS.root, dir);
180
178
 
181
179
  if (!fs.existsSync(fullDir)) return results;
182
180
 
@@ -191,7 +189,7 @@ class ProjectAnalyzer {
191
189
  walkDir(filePath);
192
190
  } else if (stat.isFile() && pattern.test(file)) {
193
191
  const content = fs.readFileSync(filePath, 'utf-8');
194
- const relativePath = path.relative(PROJECT_ROOT, filePath);
192
+ const relativePath = path.relative(PATHS.root, filePath);
195
193
 
196
194
  const exports = this.extractExports(content);
197
195
  if (exports.length > 0) {
@@ -242,7 +240,7 @@ class ProjectAnalyzer {
242
240
  }
243
241
 
244
242
  extractImportPatterns() {
245
- const tsconfigPath = path.join(PROJECT_ROOT, 'tsconfig.json');
243
+ const tsconfigPath = path.join(PATHS.root, 'tsconfig.json');
246
244
  if (fs.existsSync(tsconfigPath)) {
247
245
  const tsconfig = readJson(tsconfigPath, null);
248
246
  if (tsconfig) {
@@ -259,7 +257,7 @@ class ProjectAnalyzer {
259
257
  }
260
258
 
261
259
  loadPackageJson() {
262
- const pkgPath = path.join(PROJECT_ROOT, 'package.json');
260
+ const pkgPath = path.join(PATHS.root, 'package.json');
263
261
  return readJson(pkgPath, null);
264
262
  }
265
263
  }
@@ -770,7 +768,7 @@ async function main() {
770
768
  const generator = new TemplateGenerator(patterns);
771
769
  generator.generateAll();
772
770
 
773
- const stateDir = path.join(WORKFLOW_DIR, 'state');
771
+ const stateDir = path.join(PATHS.workflow, 'state');
774
772
  if (!fs.existsSync(stateDir)) {
775
773
  fs.mkdirSync(stateDir, { recursive: true });
776
774
  }
@@ -16,8 +16,6 @@
16
16
  * const { runAPITests, parseAPIMap, executeAPITest, validateResponseSchema } = require('./flow-test-api');
17
17
  */
18
18
 
19
- 'use strict';
20
-
21
19
  const fs = require('node:fs');
22
20
  const path = require('node:path');
23
21
  const { spawn } = require('node:child_process');
@@ -32,8 +30,6 @@ try {
32
30
  scenarioEngine = null;
33
31
  }
34
32
 
35
- const PROJECT_ROOT = getProjectRoot();
36
-
37
33
  // ============================================================
38
34
  // Constants
39
35
  // ============================================================
@@ -609,7 +605,7 @@ async function startAPIServer(command, baseUrl, timeout = SERVER_READY_TIMEOUT)
609
605
  const args = parts.slice(1);
610
606
 
611
607
  const serverProcess = spawn(cmd, args, {
612
- cwd: PROJECT_ROOT,
608
+ cwd: PATHS.root,
613
609
  stdio: ['ignore', 'pipe', 'pipe'],
614
610
  detached: true,
615
611
  env: { ...process.env, NODE_ENV: 'test' }
@@ -649,7 +645,7 @@ async function startAPIServer(command, baseUrl, timeout = SERVER_READY_TIMEOUT)
649
645
  // Check if process already exited with error
650
646
  const exitResult = await Promise.race([
651
647
  processExited,
652
- new Promise(resolve => setTimeout(() => resolve(null), SERVER_POLL_INTERVAL))
648
+ require('node:timers/promises').setTimeout(SERVER_POLL_INTERVAL, null)
653
649
  ]);
654
650
 
655
651
  if (exitResult && exitResult.exitError) {
@@ -737,7 +733,7 @@ function loadTestData(taskId) {
737
733
  const result = { setup: [], teardown: [], fixtures: {} };
738
734
 
739
735
  // Check for task-specific fixtures
740
- const taskFixturesPath = path.join(PROJECT_ROOT, '.workflow', 'tests', 'generated', taskId, 'api-fixtures.json');
736
+ const taskFixturesPath = path.join(PATHS.workflow, 'tests', 'generated', taskId, 'api-fixtures.json');
741
737
  const taskFixtures = safeJsonParse(taskFixturesPath, null);
742
738
  if (taskFixtures) {
743
739
  if (taskFixtures.setup) result.setup = taskFixtures.setup;
@@ -746,7 +742,7 @@ function loadTestData(taskId) {
746
742
  }
747
743
 
748
744
  // Check for global fixtures
749
- const globalFixturesPath = path.join(PROJECT_ROOT, '.workflow', 'tests', 'api-fixtures.json');
745
+ const globalFixturesPath = path.join(PATHS.workflow, 'tests', 'api-fixtures.json');
750
746
  const globalFixtures = safeJsonParse(globalFixturesPath, null);
751
747
  if (globalFixtures) {
752
748
  // Merge — task-specific takes precedence
@@ -978,7 +974,7 @@ function generateReport(taskId, testGroups, schemaInfo) {
978
974
  * @returns {string} Path to the written report
979
975
  */
980
976
  function writeReport(taskId, report) {
981
- const verificationsDir = path.join(PATHS.workflow, 'verifications');
977
+ const verificationsDir = PATHS.verifications;
982
978
  ensureDir(verificationsDir);
983
979
 
984
980
  const reportPath = path.join(verificationsDir, `${taskId}-api.json`);
@@ -1018,7 +1014,7 @@ function hasStateDependencies(taskId, endpoints) {
1018
1014
  function loadOrGenerateScenarios(taskId, options = {}) {
1019
1015
  const scenarios = [];
1020
1016
 
1021
- const scenarioDir = path.join(PROJECT_ROOT, '.workflow', 'tests', 'generated', taskId);
1017
+ const scenarioDir = path.join(PATHS.workflow, 'tests', 'generated', taskId);
1022
1018
  const scenarioFile = path.join(scenarioDir, 'scenarios.json');
1023
1019
  const predefined = safeJsonParse(scenarioFile, null);
1024
1020
  if (predefined && Array.isArray(predefined.scenarios)) {
@@ -1032,7 +1028,7 @@ function loadOrGenerateScenarios(taskId, options = {}) {
1032
1028
  return scenarios;
1033
1029
  }
1034
1030
 
1035
- const readyPath = path.join(PATHS.state, 'ready.json');
1031
+ const readyPath = PATHS.ready;
1036
1032
  const readyData = safeJsonParse(readyPath, null);
1037
1033
  if (!readyData) return scenarios;
1038
1034
 
@@ -1103,7 +1099,7 @@ async function runScenarioTests(taskId, scenarios, options = {}) {
1103
1099
  };
1104
1100
 
1105
1101
  if (!dryRun) {
1106
- const verificationsDir = path.join(PATHS.workflow, 'verifications');
1102
+ const verificationsDir = PATHS.verifications;
1107
1103
  ensureDir(verificationsDir);
1108
1104
  const reportPath = path.join(verificationsDir, `${taskId}-scenarios.json`);
1109
1105
  try {
@@ -1170,7 +1166,7 @@ async function runAPITests(taskId, options = {}) {
1170
1166
  // 2. Load OpenAPI spec if available
1171
1167
  let openAPISpec = { endpoints: [], raw: null };
1172
1168
  if (specFile) {
1173
- const specPath = path.isAbsolute(specFile) ? specFile : path.join(PROJECT_ROOT, specFile);
1169
+ const specPath = path.isAbsolute(specFile) ? specFile : path.join(PATHS.root, specFile);
1174
1170
  openAPISpec = parseOpenAPISpec(specPath);
1175
1171
  }
1176
1172
 
@@ -744,7 +744,7 @@ function runFailToPass(beforeResults, afterResults, matchedCriteria) {
744
744
  * @returns {string} Path to saved report
745
745
  */
746
746
  function generateDiscoveryReport(taskId, discoveryResults, gateResults) {
747
- const verificationsDir = path.join(PATHS.workflow, 'verifications');
747
+ const verificationsDir = PATHS.verifications;
748
748
 
749
749
  // Ensure verifications directory exists
750
750
  try {
@@ -16,15 +16,11 @@
16
16
  * const { parseSpecCriteria, detectTestConventions, categoriseCriterion, generateTestScaffold } = require('./flow-test-generate');
17
17
  */
18
18
 
19
- 'use strict';
20
-
21
19
  const fs = require('node:fs');
22
20
  const path = require('node:path');
23
- const { getProjectRoot, safeJsonParse } = require('./flow-utils');
21
+ const { getProjectRoot, safeJsonParse, PATHS } = require('./flow-utils');
24
22
  const { getConfig } = require('./flow-config-loader');
25
23
 
26
- const PROJECT_ROOT = getProjectRoot();
27
-
28
24
  // ============================================================
29
25
  // Criterion Categorisation Keywords
30
26
  // ============================================================
@@ -154,7 +150,7 @@ function parseSpecCriteria(specPath) {
154
150
  * @returns {{ framework: string, importStyle: 'esm'|'cjs', fileExtension: string, hasDescribeBlocks: boolean, assertionLib: string }}
155
151
  */
156
152
  function detectTestConventions(projectRoot) {
157
- const root = projectRoot || PROJECT_ROOT;
153
+ const root = projectRoot || PATHS.root;
158
154
  const result = {
159
155
  framework: 'vitest',
160
156
  importStyle: 'esm',
@@ -403,7 +399,7 @@ function escapeSingleQuotes(str) {
403
399
  function ensureTestDir(taskId, outputDir) {
404
400
  const config = getConfig();
405
401
  const baseDir = outputDir || (config.testing && config.testing.generation && config.testing.generation.outputDir) || '.workflow/tests/generated';
406
- const fullDir = path.join(PROJECT_ROOT, baseDir, taskId);
402
+ const fullDir = path.join(PATHS.root, baseDir, taskId);
407
403
 
408
404
  if (!fs.existsSync(fullDir)) {
409
405
  fs.mkdirSync(fullDir, { recursive: true });
@@ -552,7 +548,7 @@ if (require.main === module) {
552
548
  }
553
549
 
554
550
  // Parse spec
555
- const specPath = path.join(PROJECT_ROOT, '.workflow', 'specs', `${taskId}.md`);
551
+ const specPath = path.join(PATHS.workflow, 'specs', `${taskId}.md`);
556
552
 
557
553
  if (!fs.existsSync(specPath)) {
558
554
  console.error(`Spec not found: ${specPath}`);
@@ -584,7 +580,7 @@ if (require.main === module) {
584
580
  console.log('');
585
581
  console.log(`Generated ${result.files.length} test file(s) with ${result.totalTests} total test(s):`);
586
582
  for (const file of result.files) {
587
- console.log(` ${path.relative(PROJECT_ROOT, file.path)} (${file.category}: ${file.testCount} tests)`);
583
+ console.log(` ${path.relative(PATHS.root, file.path)} (${file.category}: ${file.testCount} tests)`);
588
584
  }
589
585
  }
590
586
 
@@ -24,8 +24,6 @@
24
24
  * buildEndpointPageMapping } = require('./flow-test-integrity');
25
25
  */
26
26
 
27
- 'use strict';
28
-
29
27
  const fs = require('node:fs');
30
28
  const path = require('node:path');
31
29
  const { getProjectRoot, PATHS, ensureDir, safeJsonParse } = require('./flow-utils');
@@ -34,8 +32,6 @@ const { loadProfile } = require('./flow-verification-profile');
34
32
  const { parseAPIMap, executeAPITest, startAPIServer, stopAPIServer } = require('./flow-test-api');
35
33
  const { startDevServer, stopDevServer, assertDataInTree, flattenTreeToText } = require('./flow-test-ui');
36
34
 
37
- const PROJECT_ROOT = getProjectRoot();
38
-
39
35
  // ============================================================
40
36
  // Constants
41
37
  // ============================================================
@@ -549,7 +545,7 @@ function generateIntegrityPlan(taskId) {
549
545
  }
550
546
 
551
547
  // Load task spec if available
552
- const specPath = path.join(PROJECT_ROOT, '.workflow', 'specs', `${taskId}.json`);
548
+ const specPath = path.join(PATHS.workflow, 'specs', `${taskId}.json`);
553
549
  const spec = safeJsonParse(specPath, null);
554
550
 
555
551
  const mappings = buildEndpointPageMapping(endpoints, spec);
@@ -575,7 +571,7 @@ function generateIntegrityPlan(taskId) {
575
571
  * @returns {string} Path to written report
576
572
  */
577
573
  function writeIntegrityReport(taskId, results) {
578
- const verificationsDir = path.join(PATHS.workflow, 'verifications');
574
+ const verificationsDir = PATHS.verifications;
579
575
  ensureDir(verificationsDir);
580
576
 
581
577
  const reportPath = path.join(verificationsDir, `${taskId}-integrity.json`);
@@ -765,7 +761,7 @@ async function runIntegrityTests(taskId, options = {}) {
765
761
  const endpoints = parseAPIMap(apiMapPath);
766
762
 
767
763
  let spec = null;
768
- const specPath = path.join(PROJECT_ROOT, '.workflow', 'specs', `${taskId}.json`);
764
+ const specPath = path.join(PATHS.workflow, 'specs', `${taskId}.json`);
769
765
  if (fs.existsSync(specPath)) {
770
766
  spec = safeJsonParse(specPath, null);
771
767
  }
@@ -30,20 +30,16 @@
30
30
  * checkStateCoverage, getPlaywrightMCPConfig } = require('./flow-test-ui');
31
31
  */
32
32
 
33
- 'use strict';
34
-
35
33
  const { spawn } = require('node:child_process');
36
34
  const fs = require('node:fs');
37
35
  const path = require('node:path');
38
36
  const http = require('node:http');
39
37
  const https = require('node:https');
40
- const { getProjectRoot } = require('./flow-paths');
38
+ const { getProjectRoot, PATHS } = require('./flow-paths');
41
39
  const { getConfig } = require('./flow-config-loader');
42
40
  const { ensureDir } = require('./flow-io');
43
41
  const { loadProfile } = require('./flow-verification-profile');
44
42
 
45
- const PROJECT_ROOT = getProjectRoot();
46
-
47
43
  // ============================================================
48
44
  // Constants
49
45
  // ============================================================
@@ -119,7 +115,7 @@ async function startDevServer(command, baseUrl, timeout = DEFAULT_SERVER_TIMEOUT
119
115
  let serverProcess;
120
116
  try {
121
117
  serverProcess = spawn(cmd, args, {
122
- cwd: PROJECT_ROOT,
118
+ cwd: PATHS.root,
123
119
  stdio: ['ignore', 'pipe', 'pipe'],
124
120
  detached: false,
125
121
  shell: true
@@ -368,7 +364,7 @@ function loadTestFlows(taskId) {
368
364
  const config = getConfig();
369
365
  const testingConfig = config.testing || {};
370
366
  const outputDir = (testingConfig.generation && testingConfig.generation.outputDir) || '.workflow/tests/generated';
371
- const testDir = path.join(PROJECT_ROOT, outputDir, taskId);
367
+ const testDir = path.join(PATHS.root, outputDir, taskId);
372
368
 
373
369
  if (!fs.existsSync(testDir)) {
374
370
  return [];
@@ -510,7 +506,7 @@ function generateReport(taskId, assertions, stateCoverage, accessibility) {
510
506
  * @returns {string} Path to the saved report file
511
507
  */
512
508
  function saveReport(report) {
513
- const verificationsDir = path.join(PROJECT_ROOT, '.workflow', 'verifications');
509
+ const verificationsDir = PATHS.verifications;
514
510
  ensureDir(verificationsDir);
515
511
 
516
512
  const reportPath = path.join(verificationsDir, `${report.taskId}-ui.json`);
@@ -728,7 +724,7 @@ if (require.main === module) {
728
724
  console.log(`Accessibility violations: ${report.accessibility.violations.length}`);
729
725
  }
730
726
 
731
- const reportPath = path.join(PROJECT_ROOT, '.workflow', 'verifications', `${taskId}-ui.json`);
727
+ const reportPath = path.join(PATHS.workflow, 'verifications', `${taskId}-ui.json`);
732
728
  console.log(`Report: ${reportPath}`);
733
729
 
734
730
  process.exit(report.summary.failed > 0 ? 1 : 0);
@@ -23,12 +23,10 @@
23
23
  * const ready = await ensureDeps('ui');
24
24
  */
25
25
 
26
- 'use strict';
27
-
28
26
  const { execSync } = require('node:child_process');
29
27
  const path = require('node:path');
30
28
  const fs = require('node:fs');
31
- const { getProjectRoot } = require('./flow-utils');
29
+ const { getProjectRoot, PATHS } = require('./flow-utils');
32
30
 
33
31
  // ============================================================
34
32
  // Dependency Definitions
@@ -29,7 +29,7 @@ const {
29
29
  fileExists,
30
30
  safeJsonParse,
31
31
  printHeader,
32
- printSection
32
+ printSection, PATHS
33
33
  } = require('./flow-utils');
34
34
 
35
35
  // ============================================================
@@ -91,9 +91,9 @@ const DEFAULT_TIERED_LEARNING_CONFIG = {
91
91
  // Paths
92
92
  // ============================================================
93
93
 
94
- const PATTERNS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'learning-patterns.json');
95
- const DECISIONS_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'decisions.md');
96
- const FEEDBACK_PATH = path.join(PROJECT_ROOT, '.workflow', 'state', 'feedback-patterns.md');
94
+ const PATTERNS_PATH = path.join(PATHS.state, 'learning-patterns.json');
95
+ const DECISIONS_PATH = PATHS.decisions;
96
+ const FEEDBACK_PATH = PATHS.feedbackPatterns;
97
97
 
98
98
  // ============================================================
99
99
  // Configuration
@@ -16,7 +16,7 @@
16
16
 
17
17
  const fs = require('node:fs');
18
18
  const path = require('node:path');
19
- const { getProjectRoot, safeJsonParse } = require('./flow-utils');
19
+ const { getProjectRoot, safeJsonParse, PATHS } = require('./flow-utils');
20
20
 
21
21
  // ============================================================================
22
22
  // Constants
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Wogi Flow - Token Estimation
5
3
  *
@@ -20,16 +20,12 @@
20
20
  * const { probeProject, loadProfile, getVerificationStrategy, hasCapability } = require('./flow-verification-profile');
21
21
  */
22
22
 
23
- 'use strict';
24
-
25
23
  const fs = require('node:fs');
26
24
  const path = require('node:path');
27
25
  const { spawnSync } = require('node:child_process');
28
26
  const { getProjectRoot, PATHS, ensureDir, safeJsonParse } = require('./flow-utils');
29
27
  const { getConfig } = require('./flow-config-loader');
30
28
 
31
- const PROJECT_ROOT = getProjectRoot();
32
-
33
29
  // ============================================================
34
30
  // Constants
35
31
  // ============================================================
@@ -127,7 +123,7 @@ const FRAMEWORK_PORTS = {
127
123
  */
128
124
  function existsInProject(relativePath) {
129
125
  try {
130
- return fs.existsSync(path.join(PROJECT_ROOT, relativePath));
126
+ return fs.existsSync(path.join(PATHS.root, relativePath));
131
127
  } catch (err) {
132
128
  return false;
133
129
  }
@@ -138,7 +134,7 @@ function existsInProject(relativePath) {
138
134
  * @returns {object|null}
139
135
  */
140
136
  function readPackageJson() {
141
- const pkgPath = path.join(PROJECT_ROOT, 'package.json');
137
+ const pkgPath = path.join(PATHS.root, 'package.json');
142
138
  return safeJsonParse(pkgPath, null);
143
139
  }
144
140
 
@@ -149,7 +145,7 @@ function readPackageJson() {
149
145
  */
150
146
  function readProjectFile(relativePath) {
151
147
  try {
152
- return fs.readFileSync(path.join(PROJECT_ROOT, relativePath), 'utf-8');
148
+ return fs.readFileSync(path.join(PATHS.root, relativePath), 'utf-8');
153
149
  } catch (err) {
154
150
  return null;
155
151
  }
@@ -552,7 +548,7 @@ function detectFixtures() {
552
548
  };
553
549
 
554
550
  for (const fixtureDir of FIXTURE_DIRS) {
555
- const fullPath = path.join(PROJECT_ROOT, fixtureDir);
551
+ const fullPath = path.join(PATHS.root, fixtureDir);
556
552
 
557
553
  // Check if it's a file (e.g., prisma/seed.ts)
558
554
  try {
@@ -602,7 +598,7 @@ function detectCI() {
602
598
  for (const ci of CI_CONFIGS) {
603
599
  if (ci.glob) {
604
600
  // Directory-based detection (e.g., .github/workflows)
605
- const dirPath = path.join(PROJECT_ROOT, ci.glob);
601
+ const dirPath = path.join(PATHS.root, ci.glob);
606
602
  try {
607
603
  const stat = fs.statSync(dirPath);
608
604
  if (stat.isDirectory()) {
@@ -779,7 +775,7 @@ function buildVerificationStrategy(profile) {
779
775
  * @returns {Promise<object>} The complete verification profile
780
776
  */
781
777
  async function probeProject(projectRoot) {
782
- const root = projectRoot || PROJECT_ROOT;
778
+ const root = projectRoot || PATHS.root;
783
779
  const pkg = readPackageJson();
784
780
 
785
781
  // Run all detections