wogiflow 2.4.2 → 2.4.3

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 (196) hide show
  1. package/.claude/docs/claude-code-compatibility.md +27 -0
  2. package/.claude/settings.json +1 -1
  3. package/.workflow/models/registry.json +1 -1
  4. package/package.json +4 -4
  5. package/scripts/MEMORY-ARCHITECTURE.md +1 -1
  6. package/scripts/base-workflow-step.js +136 -0
  7. package/scripts/flow-adaptive-learning.js +8 -9
  8. package/scripts/flow-aggregate.js +11 -6
  9. package/scripts/flow-api-index.js +4 -6
  10. package/scripts/flow-assumption-detector.js +0 -2
  11. package/scripts/flow-audit.js +15 -2
  12. package/scripts/flow-auto-context.js +8 -12
  13. package/scripts/flow-auto-learn.js +49 -49
  14. package/scripts/flow-background.js +5 -6
  15. package/scripts/flow-bridge-state.js +8 -10
  16. package/scripts/flow-bulk-loop.js +1 -3
  17. package/scripts/flow-bulk-orchestrator.js +1 -3
  18. package/scripts/flow-cascade-completion.js +0 -2
  19. package/scripts/flow-cascade.js +4 -4
  20. package/scripts/flow-checkpoint.js +10 -13
  21. package/scripts/flow-code-intelligence.js +10 -12
  22. package/scripts/flow-community-sync.js +4 -4
  23. package/scripts/flow-community.js +12 -20
  24. package/scripts/flow-config-defaults.js +0 -2
  25. package/scripts/flow-config-interactive.js +9 -5
  26. package/scripts/flow-config-loader.js +49 -92
  27. package/scripts/flow-config-substitution.js +0 -2
  28. package/scripts/flow-context-estimator.js +4 -4
  29. package/scripts/flow-context-init.js +10 -12
  30. package/scripts/flow-context-manager.js +0 -2
  31. package/scripts/flow-context-scoring.js +2 -2
  32. package/scripts/flow-contract-scan.js +6 -9
  33. package/scripts/flow-correct.js +29 -27
  34. package/scripts/flow-correction-detector.js +5 -1
  35. package/scripts/flow-damage-control.js +47 -54
  36. package/scripts/flow-decisions-merge.js +4 -14
  37. package/scripts/flow-diff.js +5 -8
  38. package/scripts/flow-done-gates.js +786 -0
  39. package/scripts/flow-done-report.js +123 -0
  40. package/scripts/flow-done.js +71 -717
  41. package/scripts/flow-entropy-monitor.js +1 -3
  42. package/scripts/flow-eval.js +5 -5
  43. package/scripts/flow-extraction-review.js +1 -0
  44. package/scripts/flow-failure-categories.js +0 -2
  45. package/scripts/flow-figma-confirm.js +5 -9
  46. package/scripts/flow-figma-generate.js +8 -10
  47. package/scripts/flow-figma-index.js +8 -10
  48. package/scripts/flow-figma-match.js +3 -5
  49. package/scripts/flow-figma-mcp-server.js +2 -4
  50. package/scripts/flow-figma-orchestrator.js +2 -3
  51. package/scripts/flow-figma-registry.js +2 -3
  52. package/scripts/flow-framework-resolver.js +0 -2
  53. package/scripts/flow-function-index.js +4 -6
  54. package/scripts/flow-gate-confidence.js +2 -2
  55. package/scripts/flow-gitignore.js +0 -2
  56. package/scripts/flow-guided-edit.js +5 -6
  57. package/scripts/flow-health.js +5 -6
  58. package/scripts/flow-hook-errors.js +6 -0
  59. package/scripts/flow-hook-status.js +263 -0
  60. package/scripts/flow-hooks.js +17 -29
  61. package/scripts/flow-http-client.js +9 -8
  62. package/scripts/flow-hybrid-interactive.js +7 -12
  63. package/scripts/flow-hybrid-test.js +12 -13
  64. package/scripts/flow-instruction-richness.js +1 -1
  65. package/scripts/flow-io.js +21 -4
  66. package/scripts/flow-knowledge-router.js +9 -3
  67. package/scripts/flow-learning-orchestrator.js +318 -13
  68. package/scripts/flow-links.js +5 -7
  69. package/scripts/flow-long-input-association.js +275 -0
  70. package/scripts/flow-long-input-chunking.js +1 -0
  71. package/scripts/flow-long-input-cli.js +0 -2
  72. package/scripts/flow-long-input-complexity.js +0 -2
  73. package/scripts/flow-long-input-constants.js +0 -2
  74. package/scripts/flow-long-input-contradictions.js +351 -0
  75. package/scripts/flow-long-input-detection.js +0 -2
  76. package/scripts/flow-long-input-passes.js +885 -0
  77. package/scripts/flow-long-input-stories.js +1 -1
  78. package/scripts/flow-long-input-voice.js +0 -2
  79. package/scripts/flow-long-input.js +425 -3005
  80. package/scripts/flow-loop-retry-learning.js +2 -3
  81. package/scripts/flow-lsp.js +3 -3
  82. package/scripts/flow-mcp-docs.js +3 -4
  83. package/scripts/flow-memory-db.js +6 -8
  84. package/scripts/flow-memory-sync.js +18 -11
  85. package/scripts/flow-metrics.js +1 -2
  86. package/scripts/flow-model-adapter.js +2 -3
  87. package/scripts/flow-model-config.js +72 -104
  88. package/scripts/flow-model-router.js +2 -2
  89. package/scripts/flow-model-types.js +0 -2
  90. package/scripts/flow-multi-approach.js +5 -6
  91. package/scripts/flow-orchestrate-context.js +3 -7
  92. package/scripts/flow-orchestrate-rollback.js +3 -8
  93. package/scripts/flow-orchestrate-state.js +8 -14
  94. package/scripts/flow-orchestrate-templates.js +2 -6
  95. package/scripts/flow-orchestrate-validator.js +5 -9
  96. package/scripts/flow-orchestrate.js +126 -103
  97. package/scripts/flow-output.js +0 -2
  98. package/scripts/flow-parallel.js +1 -1
  99. package/scripts/flow-paths.js +23 -2
  100. package/scripts/flow-pattern-enforcer.js +30 -28
  101. package/scripts/flow-pattern-extractor.js +3 -4
  102. package/scripts/flow-pending.js +0 -2
  103. package/scripts/flow-permissions.js +2 -3
  104. package/scripts/flow-plugin-registry.js +10 -12
  105. package/scripts/flow-prd-manager.js +1 -1
  106. package/scripts/flow-progress.js +7 -9
  107. package/scripts/flow-prompt-composer.js +3 -3
  108. package/scripts/flow-prompt-template.js +2 -2
  109. package/scripts/flow-providers.js +7 -4
  110. package/scripts/flow-registry-manager.js +7 -12
  111. package/scripts/flow-regression.js +9 -11
  112. package/scripts/flow-roadmap.js +2 -2
  113. package/scripts/flow-run-trace.js +16 -15
  114. package/scripts/flow-safety.js +2 -5
  115. package/scripts/flow-scanner-base.js +5 -7
  116. package/scripts/flow-scenario-engine.js +1 -5
  117. package/scripts/flow-security.js +29 -0
  118. package/scripts/flow-session-end.js +32 -41
  119. package/scripts/flow-session-learning.js +53 -49
  120. package/scripts/flow-setup-hooks.js +2 -3
  121. package/scripts/flow-skill-create.js +7 -12
  122. package/scripts/flow-skill-generator.js +12 -16
  123. package/scripts/flow-skill-learn.js +17 -8
  124. package/scripts/flow-skill-matcher.js +1 -2
  125. package/scripts/flow-spec-generator.js +2 -4
  126. package/scripts/flow-stack-wizard.js +5 -7
  127. package/scripts/flow-standards-learner.js +35 -16
  128. package/scripts/flow-start.js +2 -0
  129. package/scripts/flow-stats-collector.js +2 -2
  130. package/scripts/flow-status.js +10 -10
  131. package/scripts/flow-statusline-setup.js +2 -2
  132. package/scripts/flow-step-changelog.js +2 -3
  133. package/scripts/flow-step-comments.js +66 -81
  134. package/scripts/flow-step-complexity.js +50 -70
  135. package/scripts/flow-step-coverage.js +3 -5
  136. package/scripts/flow-step-knowledge.js +2 -3
  137. package/scripts/flow-step-pr-tests.js +64 -74
  138. package/scripts/flow-step-regression.js +3 -5
  139. package/scripts/flow-step-review.js +86 -103
  140. package/scripts/flow-step-security.js +111 -121
  141. package/scripts/flow-step-silent-failures.js +56 -83
  142. package/scripts/flow-step-simplifier.js +52 -70
  143. package/scripts/flow-story.js +4 -7
  144. package/scripts/flow-strict-adherence.js +3 -4
  145. package/scripts/flow-task-checkpoint.js +36 -5
  146. package/scripts/flow-task-enforcer.js +2 -24
  147. package/scripts/flow-tech-debt.js +1 -1
  148. package/scripts/flow-template-extractor.js +1 -0
  149. package/scripts/flow-templates.js +11 -13
  150. package/scripts/flow-test-api.js +9 -13
  151. package/scripts/flow-test-discovery.js +1 -1
  152. package/scripts/flow-test-generate.js +5 -9
  153. package/scripts/flow-test-integrity.js +3 -7
  154. package/scripts/flow-test-ui.js +5 -9
  155. package/scripts/flow-testing-deps.js +1 -3
  156. package/scripts/flow-tiered-learning.js +4 -4
  157. package/scripts/flow-todowrite-sync.js +1 -1
  158. package/scripts/flow-tokens.js +0 -2
  159. package/scripts/flow-verification-profile.js +6 -10
  160. package/scripts/flow-verify.js +12 -16
  161. package/scripts/flow-version-check.js +4 -12
  162. package/scripts/flow-webmcp-generator.js +3 -5
  163. package/scripts/flow-workflow-steps.js +0 -2
  164. package/scripts/flow-workflow.js +9 -11
  165. package/scripts/hooks/adapters/claude-code.js +2 -0
  166. package/scripts/hooks/core/config-change.js +1 -0
  167. package/scripts/hooks/core/extension-registry.js +0 -2
  168. package/scripts/hooks/core/instructions-loaded.js +1 -1
  169. package/scripts/hooks/core/observation-capture.js +5 -5
  170. package/scripts/hooks/core/phase-gate.js +5 -0
  171. package/scripts/hooks/core/post-compact.js +1 -12
  172. package/scripts/hooks/core/research-gate.js +2 -12
  173. package/scripts/hooks/core/routing-gate.js +6 -0
  174. package/scripts/hooks/core/task-completed.js +12 -0
  175. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  176. package/scripts/hooks/entry/claude-code/config-change.js +6 -29
  177. package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
  178. package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
  179. package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
  180. package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
  181. package/scripts/hooks/entry/claude-code/session-end.js +4 -28
  182. package/scripts/hooks/entry/claude-code/session-start.js +205 -243
  183. package/scripts/hooks/entry/claude-code/setup.js +8 -49
  184. package/scripts/hooks/entry/claude-code/stop.js +40 -72
  185. package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
  186. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
  187. package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
  188. package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
  189. package/scripts/hooks/entry/shared/hook-runner.js +99 -0
  190. package/scripts/hooks/entry/shared/read-stdin.js +0 -2
  191. package/scripts/registries/api-registry.js +0 -2
  192. package/scripts/registries/component-registry.js +5 -9
  193. package/scripts/registries/contract-scanner.js +2 -9
  194. package/scripts/registries/function-registry.js +0 -2
  195. package/scripts/registries/schema-registry.js +14 -18
  196. package/scripts/registries/service-registry.js +23 -27
@@ -22,15 +22,12 @@
22
22
  const fs = require('node:fs');
23
23
  const path = require('node:path');
24
24
  const { spawn, execSync } = require('node:child_process');
25
- const { getProjectRoot, getConfig, colors: c, readJson } = require('./flow-utils');
25
+ const { getProjectRoot, getConfig, colors: c, readJson, PATHS } = require('./flow-utils');
26
26
  const { success: printSuccess, error: printError } = require('./flow-output');
27
27
  const { recordCommandResult } = require('./flow-metrics');
28
28
  const { detectPackageManager, getExecCommand, getRunPrefix } = require('./flow-script-resolver');
29
29
  const { CREDENTIAL_SCAN_PATTERNS } = require('./flow-security');
30
30
 
31
- const PROJECT_ROOT = getProjectRoot();
32
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
33
-
34
31
  /**
35
32
  * Gate result structure
36
33
  */
@@ -197,7 +194,7 @@ const ERROR_PARSERS = {
197
194
  if (match) {
198
195
  const [, file, lineNum, col, message, rule] = match;
199
196
  errors.push({
200
- file: path.relative(PROJECT_ROOT, file),
197
+ file: path.relative(PATHS.root, file),
201
198
  line: parseInt(lineNum),
202
199
  column: parseInt(col),
203
200
  message: message.trim(),
@@ -220,7 +217,7 @@ const ERROR_PARSERS = {
220
217
  if (match) {
221
218
  const [, file, lineNum, col, severity, code, message] = match;
222
219
  errors.push({
223
- file: path.relative(PROJECT_ROOT, file),
220
+ file: path.relative(PATHS.root, file),
224
221
  line: parseInt(lineNum),
225
222
  column: parseInt(col),
226
223
  message,
@@ -250,7 +247,7 @@ const ERROR_PARSERS = {
250
247
  const [, file, line, col] = match;
251
248
  if (!file.includes('node_modules')) {
252
249
  errors.push({
253
- file: path.relative(PROJECT_ROOT, file),
250
+ file: path.relative(PATHS.root, file),
254
251
  line: parseInt(line),
255
252
  column: parseInt(col),
256
253
  message: 'Test assertion failed',
@@ -363,12 +360,11 @@ const ERROR_PARSERS = {
363
360
  }
364
361
  };
365
362
 
366
-
367
363
  /**
368
364
  * Detect which command to use based on installed packages
369
365
  */
370
366
  function detectCommand(gate) {
371
- const packageJsonPath = path.join(PROJECT_ROOT, 'package.json');
367
+ const packageJsonPath = path.join(PATHS.root, 'package.json');
372
368
  let deps = {};
373
369
 
374
370
  if (fs.existsSync(packageJsonPath)) {
@@ -405,7 +401,7 @@ function runCommand(cmd, args, timeout = 120000) {
405
401
  let stderrTruncated = false;
406
402
 
407
403
  const proc = spawn(cmd, args, {
408
- cwd: PROJECT_ROOT,
404
+ cwd: PATHS.root,
409
405
  timeout,
410
406
  stdio: ['pipe', 'pipe', 'pipe']
411
407
  });
@@ -687,7 +683,7 @@ function generateFixSuggestions(gateName, errors) {
687
683
  function getStagedFiles() {
688
684
  try {
689
685
  const output = execSync('git diff --cached --name-only', {
690
- cwd: PROJECT_ROOT,
686
+ cwd: PATHS.root,
691
687
  encoding: 'utf-8'
692
688
  });
693
689
  return output.split('\n').filter(f => f.trim() && /\.(ts|tsx|js|jsx|json|env)$/i.test(f));
@@ -720,7 +716,7 @@ function findSourceFiles(dir, extensions, limit) {
720
716
  if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
721
717
 
722
718
  const fullPath = path.join(currentDir, entry.name);
723
- const relativePath = path.relative(PROJECT_ROOT, fullPath);
719
+ const relativePath = path.relative(PATHS.root, fullPath);
724
720
 
725
721
  if (entry.isDirectory()) {
726
722
  walk(fullPath, depth + 1);
@@ -756,7 +752,7 @@ function checkForSecrets(files) {
756
752
  });
757
753
  if (shouldIgnore) continue;
758
754
 
759
- const filePath = path.join(PROJECT_ROOT, file);
755
+ const filePath = path.join(PATHS.root, file);
760
756
  if (!fs.existsSync(filePath)) continue;
761
757
 
762
758
  try {
@@ -809,7 +805,7 @@ function checkForInjection(files) {
809
805
  });
810
806
  if (shouldIgnore) continue;
811
807
 
812
- const filePath = path.join(PROJECT_ROOT, file);
808
+ const filePath = path.join(PATHS.root, file);
813
809
  if (!fs.existsSync(filePath)) continue;
814
810
 
815
811
  try {
@@ -853,7 +849,7 @@ async function runSecurityChecks(gateResult) {
853
849
  // Fall back to src directory if no staged files
854
850
  // Use fs.readdirSync instead of shell find to prevent command injection
855
851
  try {
856
- const srcDir = path.join(PROJECT_ROOT, 'src');
852
+ const srcDir = path.join(PATHS.root, 'src');
857
853
  if (fs.existsSync(srcDir)) {
858
854
  files = findSourceFiles(srcDir, ['.ts', '.tsx', '.js', '.jsx'], 100);
859
855
  }
@@ -1075,7 +1071,7 @@ function formatResults(results, options = {}) {
1075
1071
  * Save results to run artifacts
1076
1072
  */
1077
1073
  function saveResults(runId, results) {
1078
- const runDir = path.join(WORKFLOW_DIR, 'runs', runId);
1074
+ const runDir = path.join(PATHS.workflow, 'runs', runId);
1079
1075
  if (!fs.existsSync(runDir)) {
1080
1076
  return false;
1081
1077
  }
@@ -16,7 +16,7 @@
16
16
  const fs = require('node:fs');
17
17
  const path = require('node:path');
18
18
  const { execSync } = require('node:child_process');
19
- const { PATHS, meetsVersion, readJson } = require('./flow-utils');
19
+ const { PATHS, meetsVersion, readJson, safeJsonParse } = require('./flow-utils');
20
20
 
21
21
  const VERSION_CHECK_PATH = path.join(PATHS.state, '.version-check.json');
22
22
 
@@ -46,12 +46,7 @@ function getClaudeCodeVersion() {
46
46
  * @returns {{ version: string, checkedAt: string, wogiflowVersion: string } | null}
47
47
  */
48
48
  function readLastCheck() {
49
- try {
50
- const content = fs.readFileSync(VERSION_CHECK_PATH, 'utf-8');
51
- return JSON.parse(content);
52
- } catch (err) {
53
- return null;
54
- }
49
+ return safeJsonParse(VERSION_CHECK_PATH, null);
55
50
  }
56
51
 
57
52
  /**
@@ -195,9 +190,8 @@ function checkWogiFlowUpdateOnce() {
195
190
  if (localVersion === 'unknown') return null;
196
191
 
197
192
  // Check cached result
198
- try {
199
- const cached = fs.readFileSync(UPDATE_CHECK_PATH, 'utf-8');
200
- const data = JSON.parse(cached);
193
+ const data = safeJsonParse(UPDATE_CHECK_PATH, null);
194
+ if (data) {
201
195
  const age = Date.now() - new Date(data.checkedAt).getTime();
202
196
  if (age < UPDATE_CHECK_TTL_MS && data.localVersion === localVersion) {
203
197
  // Still within TTL and same local version — return cached result
@@ -206,8 +200,6 @@ function checkWogiFlowUpdateOnce() {
206
200
  }
207
201
  return null;
208
202
  }
209
- } catch (err) {
210
- // No cache or invalid — proceed with fresh check
211
203
  }
212
204
 
213
205
  // Fetch from npm
@@ -16,15 +16,13 @@
16
16
  const fs = require('node:fs');
17
17
  const path = require('node:path');
18
18
  const crypto = require('node:crypto');
19
- const { getProjectRoot, getConfig, color, success, warn, error } = require('./flow-utils');
19
+ const { getProjectRoot, getConfig, color, success, warn, error, PATHS } = require('./flow-utils');
20
20
  const { readJson, writeJson, ensureDir, fileExists } = require('./flow-utils');
21
21
  const { BaseScanner, PROJECT_ROOT } = require('./flow-scanner-base');
22
22
 
23
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
24
- const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
25
- const WEBMCP_DIR = path.join(WORKFLOW_DIR, 'webmcp');
23
+ const WEBMCP_DIR = path.join(PATHS.workflow, 'webmcp');
26
24
  const TOOLS_PATH = path.join(WEBMCP_DIR, 'tools.json');
27
- const APP_MAP_PATH = path.join(STATE_DIR, 'app-map.md');
25
+ const APP_MAP_PATH = PATHS.appMap;
28
26
 
29
27
  // ============================================================
30
28
  // Configuration
@@ -15,8 +15,6 @@ const fs = require('node:fs');
15
15
  const path = require('node:path');
16
16
  const { getProjectRoot, colors, getConfig, invalidateConfigCache, writeJson, PATHS } = require('./flow-utils');
17
17
 
18
- const PROJECT_ROOT = getProjectRoot();
19
-
20
18
  // ============================================================
21
19
  // Step Registry - All available workflow steps
22
20
  // ============================================================
@@ -24,18 +24,16 @@
24
24
  const fs = require('node:fs');
25
25
  const path = require('node:path');
26
26
  const { spawn } = require('node:child_process');
27
- const { getProjectRoot, colors: c, readJson } = require('./flow-utils');
27
+ const { getProjectRoot, colors: c, readJson, PATHS } = require('./flow-utils');
28
28
  const { success: printSuccess, error: printError } = require('./flow-output');
29
29
  const { detectPackageManager } = require('./flow-script-resolver');
30
30
 
31
- const PROJECT_ROOT = getProjectRoot();
32
- const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
33
- const WORKFLOWS_DIR = path.join(WORKFLOW_DIR, 'workflows');
31
+ const WORKFLOWS_DIR = path.join(PATHS.workflow, 'workflows');
34
32
 
35
33
  /**
36
34
  * Validate that a path is within the project root (prevent path traversal)
37
35
  */
38
- function validatePathWithinProject(targetPath, baseRoot = PROJECT_ROOT) {
36
+ function validatePathWithinProject(targetPath, baseRoot = PATHS.root) {
39
37
  const resolvedPath = path.resolve(baseRoot, targetPath);
40
38
  const resolvedRoot = path.resolve(baseRoot);
41
39
 
@@ -62,11 +60,11 @@ const STEP_TYPES = {
62
60
  * Detect project type (language, package manager)
63
61
  * Returns { language, packageManager } with defaults to Node.js/npm
64
62
  */
65
- function detectProjectType(projectRoot = PROJECT_ROOT) {
63
+ function detectProjectType(projectRoot = PATHS.root) {
66
64
  // Validate projectRoot to prevent path traversal
67
- const safeRoot = projectRoot === PROJECT_ROOT
68
- ? PROJECT_ROOT
69
- : validatePathWithinProject(projectRoot, PROJECT_ROOT);
65
+ const safeRoot = projectRoot === PATHS.root
66
+ ? PATHS.root
67
+ : validatePathWithinProject(projectRoot, PATHS.root);
70
68
 
71
69
  // Check for Go
72
70
  if (fs.existsSync(path.join(safeRoot, 'go.mod'))) {
@@ -95,7 +93,7 @@ function detectProjectType(projectRoot = PROJECT_ROOT) {
95
93
  * Get quality gate command for an action (lint, test, build)
96
94
  * Adapts to detected package manager and language
97
95
  */
98
- function getQualityCommand(action, projectRoot = PROJECT_ROOT) {
96
+ function getQualityCommand(action, projectRoot = PATHS.root) {
99
97
  const { language, packageManager } = detectProjectType(projectRoot);
100
98
 
101
99
  const commands = {
@@ -312,7 +310,7 @@ function executeCommand(command, options = {}) {
312
310
  const startTime = Date.now();
313
311
 
314
312
  const proc = spawn('sh', ['-c', command], {
315
- cwd: options.cwd || PROJECT_ROOT,
313
+ cwd: options.cwd || PATHS.root,
316
314
  env: { ...process.env, ...options.env },
317
315
  timeout: options.timeout || 60000
318
316
  });
@@ -67,6 +67,8 @@ const CLAUDE_CODE_EVENTS = [
67
67
  // 'Notification', // Supported but not yet used by WogiFlow
68
68
  // 'Elicitation', // Claude Code 2.1.76+ — intercept MCP elicitation requests before dialog
69
69
  // 'ElicitationResult', // Claude Code 2.1.76+ — intercept/override elicitation responses before sending
70
+ // 'CwdChanged', // Claude Code 2.1.83+ — fires when working directory changes (e.g., direnv)
71
+ // 'FileChanged', // Claude Code 2.1.83+ — fires when watched files change on disk
70
72
  // ];
71
73
 
72
74
  /**
@@ -65,6 +65,7 @@ function handleConfigChange(options = {}) {
65
65
  let bridgeState = null;
66
66
  try {
67
67
  bridgeState = require('../../flow-bridge-state');
68
+ const { PATHS } = require('../../flow-utils');
68
69
  } catch (_err) {
69
70
  // Bridge state module unavailable
70
71
  }
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Extension Hook Registry
5
3
  *
@@ -14,7 +14,7 @@
14
14
  const fs = require('node:fs');
15
15
  const path = require('node:path');
16
16
 
17
- const { safeJsonParse } = require('../../flow-utils');
17
+ const { safeJsonParse, PATHS } = require('../../flow-utils');
18
18
 
19
19
  /**
20
20
  * Check if new packages have been added since last scan.
@@ -266,6 +266,11 @@ async function captureObservation(options) {
266
266
  return { skipped: true, reason: 'missing_tool_name' };
267
267
  }
268
268
 
269
+ // Fast path: skip everything if no memory DB file exists (avoids getConfig() + WASM load)
270
+ if (!fs.existsSync(MEMORY_DB_PATH)) {
271
+ return { skipped: true, reason: 'no_memory_db' };
272
+ }
273
+
269
274
  try {
270
275
  // Load settings ONCE and use directly (avoids 4x redundant getConfig() calls)
271
276
  const settings = getObservationSettings();
@@ -327,11 +332,6 @@ async function captureObservation(options) {
327
332
  fullOutput = '[serialization failed]';
328
333
  }
329
334
 
330
- // Fast path: skip DB init if no database file exists yet (avoids 200-800ms WASM load)
331
- if (!fs.existsSync(MEMORY_DB_PATH)) {
332
- return { skipped: true, reason: 'no_memory_db' };
333
- }
334
-
335
335
  // Store observation
336
336
  const db = getMemoryDb();
337
337
  const result = await db.storeObservation({
@@ -119,6 +119,11 @@ function writePhaseState(state) {
119
119
  fs.mkdirSync(dir, { recursive: true });
120
120
  }
121
121
  fs.writeFileSync(PHASE_FILE, JSON.stringify(state, null, 2) + '\n', 'utf-8');
122
+ // Update aggregated hook status
123
+ try {
124
+ const { setPhase } = require('../../flow-hook-status');
125
+ setPhase(state.phase);
126
+ } catch (_err) { /* non-blocking */ }
122
127
  return true;
123
128
  } catch (err) {
124
129
  if (process.env.DEBUG) {
@@ -22,18 +22,7 @@
22
22
  const path = require('node:path');
23
23
  const fs = require('node:fs');
24
24
  const { PATHS, safeJsonParse, getReadyData } = require('../../flow-utils');
25
-
26
- /**
27
- * Sanitize a string value before injecting into AI context.
28
- * Strips markdown heading markers and truncates to prevent prompt manipulation.
29
- *
30
- * @param {string} value - Raw string from state files
31
- * @param {number} [maxLen=200] - Maximum length
32
- * @returns {string} Sanitized string
33
- */
34
- function sanitize(value, maxLen = 200) {
35
- return String(value).replace(/^#+\s/gm, '').slice(0, maxLen);
36
- }
25
+ const { sanitizeForContext: sanitize } = require('../../flow-io');
37
26
 
38
27
  /**
39
28
  * Handle PostCompact event.
@@ -11,7 +11,7 @@
11
11
 
12
12
  const fs = require('node:fs');
13
13
  const path = require('node:path');
14
- const { PATHS } = require('../../flow-utils');
14
+ const { PATHS, safeJsonParse } = require('../../flow-utils');
15
15
  const {
16
16
  checkResearchGate,
17
17
  isResearchEnabled: _isResearchEnabled,
@@ -423,17 +423,7 @@ function getCachePath() {
423
423
  */
424
424
  function readCache() {
425
425
  const cachePath = getCachePath();
426
- try {
427
- if (fs.existsSync(cachePath)) {
428
- const raw = fs.readFileSync(cachePath, 'utf-8');
429
- return JSON.parse(raw);
430
- }
431
- } catch (err) {
432
- if (process.env.DEBUG) {
433
- console.error(`[Research Cache] Read failed: ${err.message}`);
434
- }
435
- }
436
- return { entries: {}, lastCleanup: null };
426
+ return safeJsonParse(cachePath, { entries: {}, lastCleanup: null });
437
427
  }
438
428
 
439
429
  /**
@@ -205,6 +205,12 @@ function clearRoutingPending() {
205
205
  }
206
206
  }
207
207
 
208
+ // Update aggregated hook status
209
+ try {
210
+ const { setRouting } = require('../../flow-hook-status');
211
+ setRouting({ pending: false, cleared: true, clearedAt: new Date().toISOString() });
212
+ } catch (_err) { /* non-blocking */ }
213
+
208
214
  return { cleared: flagDeleted, reason: flagDeleted ? 'flag_cleared' : 'unlink_error' };
209
215
  }
210
216
 
@@ -20,6 +20,7 @@ const fs = require('node:fs');
20
20
  // Import from parent scripts directory
21
21
  const { getConfig, PATHS, safeJsonParse, writeJson, withLock, validateTaskId, archiveCompletedTasksToLog } = require('../../flow-utils');
22
22
  const { resetPhase, isPhaseGateEnabled } = require('./phase-gate');
23
+ const { clearOnTaskComplete } = require('../../flow-hook-status');
23
24
 
24
25
  /**
25
26
  * Check if task completed handling is enabled
@@ -146,6 +147,17 @@ async function handleTaskCompleted(input) {
146
147
  }
147
148
  }
148
149
 
150
+ // Clear hook status on task completion (single aggregated state file)
151
+ if (result.completed) {
152
+ try {
153
+ clearOnTaskComplete();
154
+ } catch (err) {
155
+ if (process.env.DEBUG) {
156
+ console.error(`[Task Completed] Hook status clear failed: ${err.message}`);
157
+ }
158
+ }
159
+ }
160
+
149
161
  // Clear progress tracker state on task completion
150
162
  if (result.completed) {
151
163
  try {
@@ -25,7 +25,7 @@ const CORE_STATE_FILES = ['ready.json', 'decisions.md'];
25
25
 
26
26
  function getEssentialStateFiles() {
27
27
  try {
28
- const { getRegistryMapFiles } = require('../../flow-utils');
28
+ const { getRegistryMapFiles, PATHS } = require('../../flow-utils');
29
29
  return [...CORE_STATE_FILES, ...getRegistryMapFiles()];
30
30
  } catch (_err) {
31
31
  return [...CORE_STATE_FILES, 'app-map.md', 'function-map.md', 'api-map.md'];
@@ -11,33 +11,10 @@
11
11
  */
12
12
 
13
13
  const { handleConfigChange } = require('../../core/config-change');
14
- const { claudeCodeAdapter } = require('../../adapters/claude-code');
15
- const { readHookInput } = require('../shared/read-stdin');
14
+ const { runHook } = require('../shared/hook-runner');
16
15
 
17
- process.stdin.setEncoding('utf8');
18
-
19
- async function main() {
20
- try {
21
- const { input: parsedStdin } = await readHookInput();
22
- const input = parsedStdin || {};
23
-
24
- // Extract the changed file path from the hook input
25
- const filePath = input.file_path || input.filePath || '';
26
- const projectRoot = input.cwd || process.cwd();
27
-
28
- // Handle the config change
29
- const result = handleConfigChange({ filePath, projectRoot });
30
-
31
- // Transform to Claude Code format via adapter (consistent with other hooks)
32
- const output = claudeCodeAdapter.transformResult('ConfigChange', result);
33
-
34
- process.stdout.write(JSON.stringify(output));
35
- process.exit(0);
36
- } catch (err) {
37
- // Never block on config change errors
38
- process.stdout.write(JSON.stringify({ continue: true }));
39
- process.exit(0);
40
- }
41
- }
42
-
43
- main();
16
+ runHook('ConfigChange', async ({ input }) => {
17
+ const filePath = input.file_path || input.filePath || '';
18
+ const projectRoot = input.cwd || process.cwd();
19
+ return handleConfigChange({ filePath, projectRoot });
20
+ }, { failMode: 'silent', useStdoutWrite: true });
@@ -14,34 +14,9 @@
14
14
  */
15
15
 
16
16
  const { handleInstructionsLoaded } = require('../../core/instructions-loaded');
17
- const { claudeCodeAdapter } = require('../../adapters/claude-code');
18
- const { readHookInput } = require('../shared/read-stdin');
17
+ const { runHook } = require('../shared/hook-runner');
19
18
 
20
- process.stdin.setEncoding('utf8');
21
-
22
- async function main() {
23
- try {
24
- const { input: parsedStdin } = await readHookInput();
25
- const input = parsedStdin || {};
26
- const parsedInput = claudeCodeAdapter.parseInput(input);
27
- const projectRoot = parsedInput.cwd || process.cwd();
28
-
29
- // Handle the instructions loaded event
30
- const result = handleInstructionsLoaded({ projectRoot });
31
-
32
- // Transform to Claude Code format via adapter
33
- const output = claudeCodeAdapter.transformResult('InstructionsLoaded', result);
34
-
35
- process.stdout.write(JSON.stringify(output));
36
- process.exit(0);
37
- } catch (err) {
38
- // Never block on errors
39
- if (process.env.DEBUG) {
40
- console.error(`[instructions-loaded] Error: ${err.message}`);
41
- }
42
- process.stdout.write(JSON.stringify({ continue: true }));
43
- process.exit(0);
44
- }
45
- }
46
-
47
- main();
19
+ runHook('InstructionsLoaded', async ({ parsedInput }) => {
20
+ const projectRoot = parsedInput.cwd || process.cwd();
21
+ return handleInstructionsLoaded({ projectRoot });
22
+ }, { failMode: 'silent', useStdoutWrite: true });
@@ -11,35 +11,8 @@
11
11
  */
12
12
 
13
13
  const { handlePostCompact } = require('../../core/post-compact');
14
- const { claudeCodeAdapter } = require('../../adapters/claude-code');
15
- const { readHookInput } = require('../shared/read-stdin');
14
+ const { runHook } = require('../shared/hook-runner');
16
15
 
17
- process.stdin.setEncoding('utf8');
18
-
19
- async function main() {
20
- try {
21
- // Consume stdin (required by hook protocol, even if we don't use the input)
22
- await readHookInput();
23
-
24
- // Handle post-compaction state recovery
25
- const result = handlePostCompact();
26
-
27
- // Transform to Claude Code format via adapter
28
- const output = claudeCodeAdapter.transformResult('PostCompact', result);
29
-
30
- process.stdout.write(JSON.stringify(output));
31
- process.exit(0);
32
- } catch (err) {
33
- // Never block on post-compact errors — fail open
34
- try {
35
- const { logHookError } = require('../../../flow-hook-errors');
36
- logHookError('PostCompact', err, { failMode: 'open', operation: 'post-compaction-recovery' });
37
- } catch (logErr) {
38
- console.error(`[WogiFlow] PostCompact hook error: ${err.message}`);
39
- }
40
- process.stdout.write(JSON.stringify({ continue: true }));
41
- process.exit(0);
42
- }
43
- }
44
-
45
- main();
16
+ runHook('PostCompact', async () => {
17
+ return handlePostCompact();
18
+ }, { failMode: 'warn', useStdoutWrite: true });