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
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Wogi Flow - Shared Hook Runner
3
+ *
4
+ * Wraps the common boilerplate shared across hook entry files:
5
+ * stdin reading, JSON parsing, adapter invocation, error handling, output formatting.
6
+ *
7
+ * Usage:
8
+ * const { runHook } = require('../shared/hook-runner');
9
+ * runHook('SessionEnd', async ({ input, parsedInput }) => {
10
+ * const result = handleSessionEnd(parsedInput);
11
+ * return result;
12
+ * }, { failMode: 'silent' });
13
+ *
14
+ * @param {string} eventName - Hook event name (e.g., 'SessionEnd', 'PostCompact')
15
+ * @param {Function} handler - Async function receiving { input, parsedInput, raw }
16
+ * Must return a result object for claudeCodeAdapter.transformResult()
17
+ * @param {object} [options]
18
+ * @param {'silent'|'warn'|'block'} [options.failMode='silent']
19
+ * - 'silent': swallow errors, output { continue: true }
20
+ * - 'warn': log to stderr, output { continue: true }
21
+ * - 'block': log to stderr, output deny/block result
22
+ * @param {boolean} [options.useStdoutWrite=false] - Use process.stdout.write instead of console.log
23
+ * @param {boolean} [options.skipAdapter=false] - If true, handler returns raw output (no adapter transform)
24
+ * @param {object} [options.failOutput] - Custom error output object (overrides default error response)
25
+ */
26
+
27
+ const { readHookInput } = require('./read-stdin');
28
+ const { claudeCodeAdapter } = require('../../adapters/claude-code');
29
+
30
+ async function runHook(eventName, handler, { failMode = 'silent', useStdoutWrite = false, skipAdapter = false, failOutput = null } = {}) {
31
+ const write = useStdoutWrite
32
+ ? (data) => process.stdout.write(data)
33
+ : (data) => console.log(data);
34
+
35
+ process.stdin.setEncoding('utf8');
36
+
37
+ try {
38
+ const { input, raw } = await readHookInput();
39
+ const parsedInput = input ? claudeCodeAdapter.parseInput(input) : {};
40
+
41
+ const result = await handler({ input: input ?? {}, parsedInput, raw });
42
+
43
+ // Handler can return { __raw: true, ...payload } to bypass adapter transform
44
+ if (result && result.__raw) {
45
+ const { __raw, ...payload } = result;
46
+ write(JSON.stringify(payload));
47
+ } else if (skipAdapter) {
48
+ write(JSON.stringify(result));
49
+ } else {
50
+ const output = claudeCodeAdapter.transformResult(eventName, result);
51
+ write(JSON.stringify(output));
52
+ }
53
+ process.exit(0);
54
+ } catch (err) {
55
+ const errorOutput = failOutput ?? { continue: true };
56
+
57
+ if (failMode === 'silent') {
58
+ // Swallow error, allow through
59
+ write(JSON.stringify(errorOutput));
60
+ process.exit(0);
61
+ } else if (failMode === 'warn') {
62
+ // Log error, allow through
63
+ if (process.env.DEBUG) {
64
+ console.error(`[${eventName}] Hook error: ${err.message}`);
65
+ }
66
+ try {
67
+ const { logHookError } = require('../../../flow-hook-errors');
68
+ logHookError(eventName, err, { failMode: 'open', operation: eventName.toLowerCase() });
69
+ } catch (_logErr) {
70
+ console.error(`[WogiFlow] ${eventName} hook error: ${err.message}`);
71
+ }
72
+ write(JSON.stringify(errorOutput));
73
+ process.exit(0);
74
+ } else if (failMode === 'block') {
75
+ // Log error, deny/block
76
+ if (process.env.DEBUG) {
77
+ console.error(`[${eventName}] Hook error (blocking): ${err.message}`);
78
+ } else {
79
+ console.error(`[Wogi Flow Hook] Validation error occurred`);
80
+ }
81
+ const blockOutput = failOutput ?? {
82
+ continue: true,
83
+ hookSpecificOutput: {
84
+ hookEventName: eventName,
85
+ permissionDecision: 'deny',
86
+ permissionDecisionReason: `WogiFlow ${eventName} validation error. Please check your setup or use /wogi-start.`
87
+ }
88
+ };
89
+ write(JSON.stringify(blockOutput));
90
+ process.exit(0);
91
+ } else {
92
+ // Unknown failMode — default to silent behavior
93
+ write(JSON.stringify(errorOutput));
94
+ process.exit(0);
95
+ }
96
+ }
97
+ }
98
+
99
+ module.exports = { runHook };
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const { safeJsonParseString } = require('../../../flow-utils');
4
2
 
5
3
  const MAX_STDIN_SIZE = 100 * 1024;
@@ -267,6 +267,8 @@ const HOOK_VERSION_MAP = {
267
267
  InstructionsLoaded: { major: 2, minor: 1, patch: 72 },
268
268
  // Hooks added in 2.1.76+
269
269
  PostCompact: { major: 2, minor: 1, patch: 76 },
270
+ // Hooks added in 2.1.84+
271
+ TaskCreated: { major: 2, minor: 1, patch: 84 },
270
272
  };
271
273
 
272
274
  /**
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * API Registry Plugin
5
3
  *
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Component Registry Plugin
5
3
  *
@@ -16,14 +14,12 @@
16
14
 
17
15
  const fs = require('node:fs');
18
16
  const path = require('node:path');
19
- const { getProjectRoot, getConfig, color, success, warn } = require('../flow-utils');
17
+ const { getProjectRoot, getConfig, color, success, warn, PATHS } = require('../flow-utils');
20
18
  const { RegistryPlugin } = require('../flow-registry-manager');
21
19
  const { BaseScanner, PROJECT_ROOT } = require('../flow-scanner-base');
22
20
  const { info } = require('../flow-output');
23
21
 
24
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
25
- const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
26
- const INDEX_PATH = path.join(STATE_DIR, 'component-index.json');
22
+ const INDEX_PATH = path.join(PATHS.state, 'component-index.json');
27
23
 
28
24
  // ============================================================
29
25
  // Component Scanner (extends BaseScanner for directory walking)
@@ -284,13 +280,13 @@ class ComponentScanner extends BaseScanner {
284
280
 
285
281
  save() {
286
282
  this.prune();
287
- fs.mkdirSync(STATE_DIR, { recursive: true });
283
+ fs.mkdirSync(PATHS.state, { recursive: true });
288
284
  fs.writeFileSync(INDEX_PATH, JSON.stringify(this.registry, null, 2));
289
285
  success(`Saved to ${path.relative(PROJECT_ROOT, INDEX_PATH)}`);
290
286
  }
291
287
 
292
288
  generateMap() {
293
- const MAP_PATH = path.join(STATE_DIR, 'app-map.md');
289
+ const MAP_PATH = PATHS.appMap;
294
290
 
295
291
  // Check if app-map.md exists and has content
296
292
  let existing = '';
@@ -381,7 +377,7 @@ class ComponentScanner extends BaseScanner {
381
377
  */
382
378
  _mergeIntoAppMap(mapPath, existing) {
383
379
  // Path containment check (defense-in-depth)
384
- if (!mapPath.startsWith(STATE_DIR)) {
380
+ if (!mapPath.startsWith(PATHS.state)) {
385
381
  warn('Write target outside state directory — skipping merge');
386
382
  return;
387
383
  }
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Contract Surface Scanner
5
3
  *
@@ -20,6 +18,7 @@
20
18
  const fs = require('node:fs');
21
19
  const path = require('node:path');
22
20
  const { execSync } = require('node:child_process');
21
+ const { safeJsonParse } = require('../flow-io');
23
22
 
24
23
  const CONTRACT_SURFACE_VERSION = '1.0.0';
25
24
 
@@ -598,13 +597,7 @@ function detectProjectType(projectRoot) {
598
597
  }
599
598
 
600
599
  // Read package.json for dependency analysis
601
- let pkg = {};
602
- try {
603
- const pkgContent = fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf-8');
604
- pkg = JSON.parse(pkgContent);
605
- } catch (err) {
606
- // No package.json or invalid JSON
607
- }
600
+ const pkg = safeJsonParse(path.join(projectRoot, 'package.json'), {});
608
601
 
609
602
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
610
603
 
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Function Registry Plugin
5
3
  *
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Schema/Model Registry Plugin
5
3
  *
@@ -14,12 +12,10 @@
14
12
  const fs = require('node:fs');
15
13
  const path = require('node:path');
16
14
  const { RegistryPlugin } = require('../flow-registry-manager');
17
- const { getProjectRoot, safeJsonParse: safeJsonParseFile } = require('../flow-utils');
15
+ const { getProjectRoot, safeJsonParse: safeJsonParseFile, PATHS } = require('../flow-utils');
18
16
 
19
- const PROJECT_ROOT = getProjectRoot();
20
- const STATE_DIR = path.join(PROJECT_ROOT, '.workflow', 'state');
21
- const INDEX_PATH = path.join(STATE_DIR, 'schema-index.json');
22
- const MAP_PATH = path.join(STATE_DIR, 'schema-map.md');
17
+ const INDEX_PATH = path.join(PATHS.state, 'schema-index.json');
18
+ const MAP_PATH = path.join(PATHS.state, 'schema-map.md');
23
19
 
24
20
  class SchemaRegistry extends RegistryPlugin {
25
21
  static id = 'schemas';
@@ -44,7 +40,7 @@ class SchemaRegistry extends RegistryPlugin {
44
40
  if (stack.orm) return true;
45
41
 
46
42
  // Check package.json for ORM packages
47
- const pkgPath = path.join(PROJECT_ROOT, 'package.json');
43
+ const pkgPath = path.join(PATHS.root, 'package.json');
48
44
  if (fs.existsSync(pkgPath)) {
49
45
  try {
50
46
  const pkg = safeJsonParseFile(pkgPath, {});
@@ -57,7 +53,7 @@ class SchemaRegistry extends RegistryPlugin {
57
53
  }
58
54
 
59
55
  // Check for Django models (manage.py + models.py)
60
- if (fs.existsSync(path.join(PROJECT_ROOT, 'manage.py'))) return true;
56
+ if (fs.existsSync(path.join(PATHS.root, 'manage.py'))) return true;
61
57
 
62
58
  return false;
63
59
  }
@@ -90,11 +86,11 @@ class SchemaRegistry extends RegistryPlugin {
90
86
  const before = this.models.length;
91
87
  this.models = this.models.filter(m => {
92
88
  if (!m.file) return true;
93
- return fs.existsSync(path.join(PROJECT_ROOT, m.file));
89
+ return fs.existsSync(path.join(PATHS.root, m.file));
94
90
  });
95
91
  this.enums = this.enums.filter(e => {
96
92
  if (!e.file) return true;
97
- return fs.existsSync(path.join(PROJECT_ROOT, e.file));
93
+ return fs.existsSync(path.join(PATHS.root, e.file));
98
94
  });
99
95
  const removed = before - this.models.length;
100
96
  if (removed > 0) {
@@ -106,7 +102,7 @@ class SchemaRegistry extends RegistryPlugin {
106
102
 
107
103
  save() {
108
104
  const index = this._buildIndex();
109
- fs.mkdirSync(STATE_DIR, { recursive: true });
105
+ fs.mkdirSync(PATHS.state, { recursive: true });
110
106
  fs.writeFileSync(INDEX_PATH, JSON.stringify(index, null, 2));
111
107
  }
112
108
 
@@ -166,7 +162,7 @@ class SchemaRegistry extends RegistryPlugin {
166
162
 
167
163
  lines.push(`---\n*Auto-generated by Schema Registry — ${getTodayDate()}*\n`);
168
164
 
169
- fs.mkdirSync(STATE_DIR, { recursive: true });
165
+ fs.mkdirSync(PATHS.state, { recursive: true });
170
166
  fs.writeFileSync(MAP_PATH, lines.join('\n'));
171
167
  }
172
168
 
@@ -179,8 +175,8 @@ class SchemaRegistry extends RegistryPlugin {
179
175
  // ============================================================
180
176
 
181
177
  _scanPrisma() {
182
- const schemaDir = path.join(PROJECT_ROOT, 'prisma', 'schema');
183
- const singleFile = path.join(PROJECT_ROOT, 'prisma', 'schema.prisma');
178
+ const schemaDir = path.join(PATHS.root, 'prisma', 'schema');
179
+ const singleFile = path.join(PATHS.root, 'prisma', 'schema.prisma');
184
180
 
185
181
  let files = [];
186
182
 
@@ -199,7 +195,7 @@ class SchemaRegistry extends RegistryPlugin {
199
195
  for (const file of files) {
200
196
  try {
201
197
  const content = fs.readFileSync(file, 'utf-8');
202
- const relPath = path.relative(PROJECT_ROOT, file);
198
+ const relPath = path.relative(PATHS.root, file);
203
199
  this._parsePrismaFile(content, relPath);
204
200
  } catch (err) {
205
201
  // Skip unreadable files
@@ -327,7 +323,7 @@ class SchemaRegistry extends RegistryPlugin {
327
323
  const entityFiles = [];
328
324
 
329
325
  for (const dir of searchDirs) {
330
- const fullDir = path.join(PROJECT_ROOT, dir);
326
+ const fullDir = path.join(PATHS.root, dir);
331
327
  if (fs.existsSync(fullDir)) {
332
328
  this._findTypeORMEntities(fullDir, entityFiles);
333
329
  }
@@ -340,7 +336,7 @@ class SchemaRegistry extends RegistryPlugin {
340
336
  // Use cached content from discovery phase to avoid N+1 reads
341
337
  const filePath = typeof entry === 'string' ? entry : entry.path;
342
338
  const content = typeof entry === 'string' ? fs.readFileSync(entry, 'utf-8') : entry.content;
343
- const relPath = path.relative(PROJECT_ROOT, filePath);
339
+ const relPath = path.relative(PATHS.root, filePath);
344
340
  this._parseTypeORMFile(content, relPath);
345
341
  } catch (_err) {
346
342
  // Skip unreadable files
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Architecture/Service Registry Plugin
5
3
  *
@@ -15,12 +13,10 @@
15
13
  const fs = require('node:fs');
16
14
  const path = require('node:path');
17
15
  const { RegistryPlugin } = require('../flow-registry-manager');
18
- const { getProjectRoot, safeJsonParse: safeJsonParseFile } = require('../flow-utils');
16
+ const { getProjectRoot, safeJsonParse: safeJsonParseFile, PATHS } = require('../flow-utils');
19
17
 
20
- const PROJECT_ROOT = getProjectRoot();
21
- const STATE_DIR = path.join(PROJECT_ROOT, '.workflow', 'state');
22
- const INDEX_PATH = path.join(STATE_DIR, 'service-index.json');
23
- const MAP_PATH = path.join(STATE_DIR, 'service-map.md');
18
+ const INDEX_PATH = path.join(PATHS.state, 'service-index.json');
19
+ const MAP_PATH = path.join(PATHS.state, 'service-map.md');
24
20
 
25
21
  // Decorator/annotation patterns per framework
26
22
  const NESTJS_PATTERNS = {
@@ -62,7 +58,7 @@ class ServiceRegistry extends RegistryPlugin {
62
58
 
63
59
  _getPackageJson() {
64
60
  if (this._cachedPkg !== undefined) return this._cachedPkg;
65
- const pkgPath = path.join(PROJECT_ROOT, 'package.json');
61
+ const pkgPath = path.join(PATHS.root, 'package.json');
66
62
  try {
67
63
  this._cachedPkg = safeJsonParseFile(pkgPath, {});
68
64
  } catch (_err) {
@@ -94,10 +90,10 @@ class ServiceRegistry extends RegistryPlugin {
94
90
  }
95
91
 
96
92
  // Check for Django (manage.py)
97
- if (fs.existsSync(path.join(PROJECT_ROOT, 'manage.py'))) return true;
93
+ if (fs.existsSync(path.join(PATHS.root, 'manage.py'))) return true;
98
94
 
99
95
  // Check for Go (go.mod with net/http)
100
- const goModPath = path.join(PROJECT_ROOT, 'go.mod');
96
+ const goModPath = path.join(PATHS.root, 'go.mod');
101
97
  if (fs.existsSync(goModPath)) return true;
102
98
 
103
99
  return false;
@@ -147,7 +143,7 @@ class ServiceRegistry extends RegistryPlugin {
147
143
  const before = arr.length;
148
144
  const pruned = arr.filter(item => {
149
145
  if (!item.file) return true;
150
- return fs.existsSync(path.join(PROJECT_ROOT, item.file));
146
+ return fs.existsSync(path.join(PATHS.root, item.file));
151
147
  });
152
148
  removed += before - pruned.length;
153
149
  arr.length = 0;
@@ -163,7 +159,7 @@ class ServiceRegistry extends RegistryPlugin {
163
159
 
164
160
  save() {
165
161
  const index = this._buildIndex();
166
- fs.mkdirSync(STATE_DIR, { recursive: true });
162
+ fs.mkdirSync(PATHS.state, { recursive: true });
167
163
  fs.writeFileSync(INDEX_PATH, JSON.stringify(index, null, 2));
168
164
  }
169
165
 
@@ -237,7 +233,7 @@ class ServiceRegistry extends RegistryPlugin {
237
233
 
238
234
  lines.push(`---\n*Auto-generated by Service Registry — ${getTodayDate()}*\n`);
239
235
 
240
- fs.mkdirSync(STATE_DIR, { recursive: true });
236
+ fs.mkdirSync(PATHS.state, { recursive: true });
241
237
  fs.writeFileSync(MAP_PATH, lines.join('\n'));
242
238
  }
243
239
 
@@ -261,8 +257,8 @@ class ServiceRegistry extends RegistryPlugin {
261
257
  // ignore
262
258
  }
263
259
 
264
- if (fs.existsSync(path.join(PROJECT_ROOT, 'manage.py'))) return 'django';
265
- if (fs.existsSync(path.join(PROJECT_ROOT, 'go.mod'))) return 'go';
260
+ if (fs.existsSync(path.join(PATHS.root, 'manage.py'))) return 'django';
261
+ if (fs.existsSync(path.join(PATHS.root, 'go.mod'))) return 'go';
266
262
 
267
263
  return null;
268
264
  }
@@ -275,7 +271,7 @@ class ServiceRegistry extends RegistryPlugin {
275
271
  const searchDirs = ['src', 'apps', 'libs'];
276
272
 
277
273
  for (const dir of searchDirs) {
278
- const fullDir = path.join(PROJECT_ROOT, dir);
274
+ const fullDir = path.join(PATHS.root, dir);
279
275
  if (fs.existsSync(fullDir)) {
280
276
  this._findNestJSFiles(fullDir);
281
277
  }
@@ -291,7 +287,7 @@ class ServiceRegistry extends RegistryPlugin {
291
287
  const fullPath = path.resolve(dir, entry.name);
292
288
 
293
289
  // Ensure resolved path stays within project root
294
- if (!fullPath.startsWith(PROJECT_ROOT)) continue;
290
+ if (!fullPath.startsWith(PATHS.root)) continue;
295
291
 
296
292
  if (entry.isDirectory()) {
297
293
  this._findNestJSFiles(fullPath, depth + 1);
@@ -301,7 +297,7 @@ class ServiceRegistry extends RegistryPlugin {
301
297
  !entry.name.endsWith('.d.ts')) {
302
298
  try {
303
299
  const content = fs.readFileSync(fullPath, 'utf-8');
304
- const relPath = path.relative(PROJECT_ROOT, fullPath);
300
+ const relPath = path.relative(PATHS.root, fullPath);
305
301
 
306
302
  if (content.includes('@Controller')) {
307
303
  this._parseNestJSController(content, relPath);
@@ -430,7 +426,7 @@ class ServiceRegistry extends RegistryPlugin {
430
426
  const searchDirs = ['src', 'routes', 'middleware', 'src/routes', 'src/middleware', 'lib', 'app', 'app/routes', 'app/api', 'server', 'api'];
431
427
 
432
428
  for (const dir of searchDirs) {
433
- const fullDir = path.join(PROJECT_ROOT, dir);
429
+ const fullDir = path.join(PATHS.root, dir);
434
430
  if (fs.existsSync(fullDir)) {
435
431
  this._findExpressFiles(fullDir);
436
432
  }
@@ -446,14 +442,14 @@ class ServiceRegistry extends RegistryPlugin {
446
442
  const fullPath = path.resolve(dir, entry.name);
447
443
 
448
444
  // Ensure resolved path stays within project root
449
- if (!fullPath.startsWith(PROJECT_ROOT)) continue;
445
+ if (!fullPath.startsWith(PATHS.root)) continue;
450
446
 
451
447
  if (entry.isDirectory()) {
452
448
  this._findExpressFiles(fullPath, depth + 1);
453
449
  } else if (entry.name.endsWith('.ts') || entry.name.endsWith('.js')) {
454
450
  try {
455
451
  const content = fs.readFileSync(fullPath, 'utf-8');
456
- const relPath = path.relative(PROJECT_ROOT, fullPath);
452
+ const relPath = path.relative(PATHS.root, fullPath);
457
453
 
458
454
  // Detect route handlers
459
455
  if (content.match(/router\.(get|post|put|patch|delete)\s*\(/i) ||
@@ -506,7 +502,7 @@ class ServiceRegistry extends RegistryPlugin {
506
502
 
507
503
  _scanDjango() {
508
504
  // Find all apps by looking for views.py files
509
- this._findDjangoFiles(PROJECT_ROOT, 0);
505
+ this._findDjangoFiles(PATHS.root, 0);
510
506
  }
511
507
 
512
508
  _findDjangoFiles(dir, depth) {
@@ -518,14 +514,14 @@ class ServiceRegistry extends RegistryPlugin {
518
514
  const fullPath = path.resolve(dir, entry.name);
519
515
 
520
516
  // Ensure resolved path stays within project root
521
- if (!fullPath.startsWith(PROJECT_ROOT)) continue;
517
+ if (!fullPath.startsWith(PATHS.root)) continue;
522
518
 
523
519
  if (entry.isDirectory()) {
524
520
  this._findDjangoFiles(fullPath, depth + 1);
525
521
  } else if (entry.name === 'views.py' || entry.name === 'serializers.py') {
526
522
  try {
527
523
  const content = fs.readFileSync(fullPath, 'utf-8');
528
- const relPath = path.relative(PROJECT_ROOT, fullPath);
524
+ const relPath = path.relative(PATHS.root, fullPath);
529
525
  this._parseDjangoFile(content, relPath);
530
526
  } catch (err) {
531
527
  // Skip
@@ -570,7 +566,7 @@ class ServiceRegistry extends RegistryPlugin {
570
566
  // ============================================================
571
567
 
572
568
  _scanGo() {
573
- this._findGoHandlers(PROJECT_ROOT, 0);
569
+ this._findGoHandlers(PATHS.root, 0);
574
570
  }
575
571
 
576
572
  _findGoHandlers(dir, depth) {
@@ -582,7 +578,7 @@ class ServiceRegistry extends RegistryPlugin {
582
578
  const fullPath = path.resolve(dir, entry.name);
583
579
 
584
580
  // Ensure resolved path stays within project root
585
- if (!fullPath.startsWith(PROJECT_ROOT)) continue;
581
+ if (!fullPath.startsWith(PATHS.root)) continue;
586
582
 
587
583
  if (entry.isDirectory()) {
588
584
  this._findGoHandlers(fullPath, depth + 1);
@@ -590,7 +586,7 @@ class ServiceRegistry extends RegistryPlugin {
590
586
  try {
591
587
  const content = fs.readFileSync(fullPath, 'utf-8');
592
588
  if (content.includes('http.Handler') || content.includes('HandleFunc') || content.includes('gin.Context')) {
593
- const relPath = path.relative(PROJECT_ROOT, fullPath);
589
+ const relPath = path.relative(PATHS.root, fullPath);
594
590
  this._parseGoFile(content, relPath);
595
591
  }
596
592
  } catch (err) {