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
@@ -14,85 +14,53 @@
14
14
 
15
15
  const { checkLoopExit } = require('../../core/loop-check');
16
16
  const { isRoutingPending, incrementStopAttempts } = require('../../core/routing-gate');
17
- const { claudeCodeAdapter } = require('../../adapters/claude-code');
18
- const { readHookInput } = require('../shared/read-stdin');
17
+ const { runHook } = require('../shared/hook-runner');
19
18
 
20
- async function main() {
19
+ runHook('Stop', async ({ parsedInput }) => {
20
+ // v6.2: Routing enforcement check — catches text-only response bypass
21
+ // If routing-pending flag is still set when the AI tries to stop, it means
22
+ // the AI responded to the user's message without ever invoking a /wogi-* command.
23
+ // This is the exact bypass we need to prevent (especially after context compaction).
21
24
  try {
22
- const { input: rawInput } = await readHookInput();
23
- const input = rawInput || {};
24
- const parsedInput = claudeCodeAdapter.parseInput(input);
25
+ if (isRoutingPending()) {
26
+ // Use counter-based approach instead of clearing immediately.
27
+ // This gives the AI multiple chances to comply before giving up.
28
+ // Gap 4 fix: clearing immediately made this single-shot protection.
29
+ const { cleared, attempts } = incrementStopAttempts(10);
25
30
 
26
- // v6.2: Routing enforcement check — catches text-only response bypass
27
- // If routing-pending flag is still set when the AI tries to stop, it means
28
- // the AI responded to the user's message without ever invoking a /wogi-* command.
29
- // This is the exact bypass we need to prevent (especially after context compaction).
30
- try {
31
- if (isRoutingPending()) {
32
- // Use counter-based approach instead of clearing immediately.
33
- // This gives the AI multiple chances to comply before giving up.
34
- // Gap 4 fix: clearing immediately made this single-shot protection.
35
- const { cleared, attempts } = incrementStopAttempts(10);
36
-
37
- if (cleared) {
38
- // Max attempts reached — allow stop to prevent infinite loop
39
- if (process.env.DEBUG) {
40
- console.error(`[Stop] Max routing enforcement attempts reached (${attempts}), allowing stop`);
41
- }
42
- // Fall through to normal stop logic
43
- } else {
44
- // Block the stop — force the AI to route through /wogi-start
45
- const routingMessage = [
46
- `ROUTING VIOLATION (attempt ${attempts}/10): You MUST call Skill(skill="wogi-start") before responding.`,
47
- '',
48
- 'Call Skill(skill="wogi-start", args="<user\'s message>") NOW. No text. No explanation. Just the Skill tool call.'
49
- ].join('\n');
50
-
51
- console.log(JSON.stringify({
52
- continue: true, // Force continue — don't let the AI stop
53
- stopReason: routingMessage
54
- }));
55
- process.exit(0);
56
- return;
31
+ if (cleared) {
32
+ // Max attempts reached allow stop to prevent infinite loop
33
+ if (process.env.DEBUG) {
34
+ console.error(`[Stop] Max routing enforcement attempts reached (${attempts}), allowing stop`);
57
35
  }
36
+ // Fall through to normal stop logic
37
+ } else {
38
+ // Block the stop — force the AI to route through /wogi-start
39
+ const routingMessage = [
40
+ `ROUTING VIOLATION (attempt ${attempts}/10): You MUST call Skill(skill="wogi-start") before responding.`,
41
+ '',
42
+ 'Call Skill(skill="wogi-start", args="<user\'s message>") NOW. No text. No explanation. Just the Skill tool call.'
43
+ ].join('\n');
44
+
45
+ // Return raw output — skip adapter transform for routing enforcement
46
+ // (this needs { continue: true, stopReason } format directly)
47
+ return { __raw: true, continue: true, stopReason: routingMessage };
58
48
  }
59
- } catch (err) {
60
- // Fail-CLOSED for routing check — force continuation on errors.
61
- // Gap 5 fix: failing open here disabled the last line of defense.
62
- // Worst case: AI retries and hits the 3-attempt limit, which clears naturally.
63
- if (process.env.DEBUG) {
64
- console.error(`[Stop] Routing check error (fail-closed, forcing continue): ${err.message}`);
65
- }
66
- console.log(JSON.stringify({
67
- continue: true,
68
- stopReason: 'Routing enforcement check encountered an error. Please invoke /wogi-start with your request.'
69
- }));
70
- process.exit(0);
71
- return;
72
49
  }
73
-
74
- // Check if loop can exit
75
- const coreResult = await checkLoopExit();
76
-
77
- // Transform to Claude Code format
78
- const output = claudeCodeAdapter.transformResult('Stop', coreResult);
79
-
80
- // Output JSON
81
- console.log(JSON.stringify(output));
82
- process.exit(0);
83
50
  } catch (err) {
84
- // On error, allow stop (don't block user) fail-open
85
- try {
86
- const { logHookError } = require('../../../flow-hook-errors');
87
- logHookError('Stop', err, { failMode: 'open', operation: 'stop-check' });
88
- } catch (logErr) {
89
- console.error(`[WogiFlow] Stop hook error: ${err.message}`);
51
+ // Fail-CLOSED for routing check force continuation on errors.
52
+ // Gap 5 fix: failing open here disabled the last line of defense.
53
+ // Worst case: AI retries and hits the 3-attempt limit, which clears naturally.
54
+ if (process.env.DEBUG) {
55
+ console.error(`[Stop] Routing check error (fail-closed, forcing continue): ${err.message}`);
90
56
  }
91
- console.log(JSON.stringify({ continue: false })); // Allow stop
92
- process.exit(0);
57
+ return {
58
+ __raw: true,
59
+ continue: true,
60
+ stopReason: 'Routing enforcement check encountered an error. Please invoke /wogi-start with your request.'
61
+ };
93
62
  }
94
- }
95
63
 
96
- // Handle stdin properly
97
- process.stdin.setEncoding('utf8');
98
- main();
64
+ // Check if loop can exit
65
+ return await checkLoopExit();
66
+ }, { failMode: 'warn', failOutput: { continue: false } });
@@ -8,32 +8,8 @@
8
8
  */
9
9
 
10
10
  const { handleTaskCompleted } = require('../../core/task-completed');
11
- const { claudeCodeAdapter } = require('../../adapters/claude-code');
12
- const { readHookInput } = require('../shared/read-stdin');
11
+ const { runHook } = require('../shared/hook-runner');
13
12
 
14
- async function main() {
15
- try {
16
- const { input: parsedStdin } = await readHookInput();
17
- const input = parsedStdin || {};
18
- const parsedInput = claudeCodeAdapter.parseInput(input);
19
-
20
- // Handle task completion
21
- const coreResult = await handleTaskCompleted(parsedInput);
22
-
23
- // Transform to Claude Code format
24
- const output = claudeCodeAdapter.transformResult('TaskCompleted', coreResult);
25
-
26
- // Output JSON
27
- console.log(JSON.stringify(output));
28
- process.exit(0);
29
- } catch (err) {
30
- // Non-blocking error - don't prevent task completion
31
- console.error(`[Wogi Flow Hook Error] ${err.message}`);
32
- console.log(JSON.stringify({ continue: true }));
33
- process.exit(0);
34
- }
35
- }
36
-
37
- // Handle stdin properly
38
- process.stdin.setEncoding('utf8');
39
- main();
13
+ runHook('TaskCompleted', async ({ parsedInput }) => {
14
+ return await handleTaskCompleted(parsedInput);
15
+ }, { failMode: 'silent' });
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Claude Code TaskCreated Hook
5
+ *
6
+ * Called when a native task is created via TaskCreate (Claude Code 2.1.84+).
7
+ * Links native tasks to the active WogiFlow task for tracking.
8
+ */
9
+
10
+ const { handleTaskCreated } = require('../../core/task-created');
11
+ const { runHook } = require('../shared/hook-runner');
12
+
13
+ runHook('TaskCreated', async ({ parsedInput }) => {
14
+ return await handleTaskCreated(parsedInput);
15
+ }, { failMode: 'silent' });
@@ -12,241 +12,159 @@ const { checkImplementationGate } = require('../../core/implementation-gate');
12
12
  const { checkResearchRequirement } = require('../../core/research-gate');
13
13
  const { setRoutingPending, clearRoutingPending, ROUTING_CLEARED_PATH } = require('../../core/routing-gate');
14
14
  const { getPhaseContextPrompt } = require('../../core/phase-gate');
15
- const { claudeCodeAdapter } = require('../../adapters/claude-code');
16
15
  const { markSkillPending, loadDurableSession } = require('../../../flow-durable-session');
17
16
  const { captureCurrentPrompt } = require('../../../flow-prompt-capture');
18
17
  const { spawnBackgroundDetection } = require('../../../flow-correction-detector');
19
18
  const { getConfig } = require('../../../flow-utils');
20
- const { readHookInput } = require('../shared/read-stdin');
19
+ const { runHook } = require('../shared/hook-runner');
21
20
 
22
- async function main() {
23
- try {
24
- const { input: parsedStdin } = await readHookInput();
25
-
26
- // Handle empty input gracefully
27
- if (!parsedStdin) {
28
- console.log(JSON.stringify({ continue: true, hookSpecificOutput: { hookEventName: 'UserPromptSubmit' } }));
29
- process.exit(0);
30
- return;
31
- }
32
-
33
- // Parse JSON safely with prototype pollution protection
34
- let input;
35
- try {
36
- input = parsedStdin;
37
- if (!input) {
38
- // Invalid JSON - allow through (graceful degradation)
39
- console.log(JSON.stringify({ continue: true, hookSpecificOutput: { hookEventName: 'UserPromptSubmit' } }));
40
- process.exit(0);
41
- return;
42
- }
43
- } catch (err) {
44
- // Parse error - allow through (graceful degradation)
45
- console.log(JSON.stringify({ continue: true, hookSpecificOutput: { hookEventName: 'UserPromptSubmit' } }));
46
- process.exit(0);
47
- return;
48
- }
49
-
50
- const parsedInput = claudeCodeAdapter.parseInput(input);
21
+ runHook('UserPromptSubmit', async ({ input, parsedInput }) => {
22
+ // Handle empty input gracefully
23
+ if (!input || Object.keys(input).length === 0) {
24
+ return { __raw: true, continue: true, hookSpecificOutput: { hookEventName: 'UserPromptSubmit' } };
25
+ }
51
26
 
52
- const prompt = parsedInput.prompt;
53
- const source = parsedInput.source;
27
+ const prompt = parsedInput.prompt;
28
+ const source = parsedInput.source;
54
29
 
55
- // v4.1: Detect skill commands that need execution tracking
56
- // This prevents premature exit when /wogi-bulk or /wogi-start is entered
57
- if (typeof prompt === 'string') {
58
- const skillMatch = prompt.match(/^\/(wogi-bulk|wogi-start)\b/i);
59
- if (skillMatch) {
60
- const skillName = skillMatch[1].toLowerCase();
61
- markSkillPending(skillName, { prompt });
62
- if (process.env.DEBUG) {
63
- console.error(`[Hook] Marked /${skillName} as pending execution`);
64
- }
30
+ // v4.1: Detect skill commands that need execution tracking
31
+ if (typeof prompt === 'string') {
32
+ const skillMatch = prompt.match(/^\/(wogi-bulk|wogi-start)\b/i);
33
+ if (skillMatch) {
34
+ const skillName = skillMatch[1].toLowerCase();
35
+ markSkillPending(skillName, { prompt });
36
+ if (process.env.DEBUG) {
37
+ console.error(`[Hook] Marked /${skillName} as pending execution`);
65
38
  }
66
39
  }
40
+ }
67
41
 
68
- // Load config once for feature flag checks
69
- let hookConfig;
70
- try {
71
- hookConfig = getConfig();
72
- } catch (err) {
73
- hookConfig = {};
74
- }
42
+ // Load config once for feature flag checks
43
+ let hookConfig;
44
+ try {
45
+ hookConfig = getConfig();
46
+ } catch (err) {
47
+ hookConfig = {};
48
+ }
75
49
 
76
- // v5.0: Capture prompt for learning system (non-blocking)
77
- // Controlled by hooks.rules.intelligence.promptCapture.enabled
78
- if (hookConfig.hooks?.rules?.intelligence?.promptCapture?.enabled !== false) {
79
- if (typeof prompt === 'string' && prompt.trim().length > 0) {
50
+ // v5.0: Capture prompt for learning system (non-blocking)
51
+ if (hookConfig.hooks?.rules?.intelligence?.promptCapture?.enabled !== false) {
52
+ if (typeof prompt === 'string' && prompt.trim().length > 0) {
53
+ setImmediate(() => {
80
54
  try {
81
55
  captureCurrentPrompt(prompt);
82
56
  } catch (err) {
83
- // Non-blocking - don't fail the hook if capture fails
84
57
  if (process.env.DEBUG) {
85
58
  console.error(`[Hook] Prompt capture failed: ${err.message}`);
86
59
  }
87
60
  }
88
- }
89
- }
90
-
91
- // v5.1→v7.0: Detect corrections for learning system (AI-only, non-blocking)
92
- // Controlled by hooks.rules.intelligence.correctionDetection.enabled
93
- if (hookConfig.hooks?.rules?.intelligence?.correctionDetection?.enabled !== false) {
94
- if (typeof prompt === 'string' && prompt.trim().length > 0) {
95
- try {
96
- const session = loadDurableSession();
97
- spawnBackgroundDetection(prompt, session?.taskId || '');
98
- } catch (err) {
99
- // Non-blocking - don't fail the hook if detection spawn fails
100
- if (process.env.DEBUG) {
101
- console.error(`[Hook] Correction detection spawn failed: ${err.message}`);
102
- }
103
- }
104
- }
61
+ });
105
62
  }
63
+ }
106
64
 
107
- // v6.0: Set routing-pending flag for routing gate enforcement
108
- // This blocks ALL gated tool calls until a /wogi-* skill is invoked
109
- // v8.0: Always set, even with active tasks every turn must route through /wogi-start.
110
- // Exception: skipped when the prompt IS a /wogi-* command (see isWogiCommand below).
111
- // v6.1: Also skip when the prompt IS a /wogi-* command — the user is already routing.
112
- // When users type "/wogi-start ..." directly, Claude Code expands the skill inline
113
- // (not through the Skill tool), so clearRoutingPending() in PreToolUse never fires.
114
- // Setting the flag here would create an uncleable block.
115
- // Tightened regex: only match /wogi-[lowercase-alphanumeric-hyphens] to prevent
116
- // injection via crafted prompts like "/wogi-<script>" or "/wogi-../../path"
117
- const isWogiCommand = typeof prompt === 'string' && /^\/wogi-[a-z0-9-]+\b/i.test(prompt.trim());
118
- if (!isWogiCommand) {
119
- // v8.1: Delete any stale cleared marker from previous turns.
120
- // The cleared marker prevents flag re-setting during skill chains (same AI response).
121
- // But across user turns, it must not persist — otherwise tools are unblocked without
122
- // routing for the duration of the marker's TTL. A new user prompt (non-wogi-command)
123
- // is unambiguously a new turn, so the old marker is invalidated.
124
- try {
125
- fs.unlinkSync(ROUTING_CLEARED_PATH);
126
- } catch (err) {
127
- // ENOENT is fine — no marker to delete
128
- if (err.code !== 'ENOENT' && process.env.DEBUG) {
129
- console.error(`[Hook] Failed to delete cleared marker: ${err.message}`);
130
- }
131
- }
132
-
133
- try {
134
- setRoutingPending();
135
- } catch (err) {
136
- // Non-blocking - don't fail the hook if routing gate fails (fail-open)
137
- if (process.env.DEBUG) {
138
- console.error(`[Hook] Routing gate set failed: ${err.message}`);
139
- }
140
- }
141
- } else {
142
- // v6.2: Actively CLEAR any existing routing flag when user explicitly types a /wogi-* command.
143
- // Previously we only skipped setting it, but a flag from a prior prompt would persist and
144
- // block tool calls inside the /wogi-* command when Claude Code expands it inline (not via Skill tool).
65
+ // v5.1->v7.0: Detect corrections for learning system (AI-only, non-blocking)
66
+ if (hookConfig.hooks?.rules?.intelligence?.correctionDetection?.enabled !== false) {
67
+ if (typeof prompt === 'string' && prompt.trim().length > 0) {
145
68
  try {
146
- clearRoutingPending();
147
- if (process.env.DEBUG) {
148
- console.error(`[Hook] Cleared routing flag — prompt is a /wogi-* command`);
149
- }
69
+ const session = loadDurableSession();
70
+ spawnBackgroundDetection(prompt, session?.taskId || '');
150
71
  } catch (err) {
151
72
  if (process.env.DEBUG) {
152
- console.error(`[Hook] Routing gate clear failed: ${err.message}`);
73
+ console.error(`[Hook] Correction detection spawn failed: ${err.message}`);
153
74
  }
154
75
  }
155
76
  }
77
+ }
156
78
 
157
- // Phase context injection (just-in-time phase-specific instructions)
158
- let phasePrompt = null;
79
+ // v6.0: Set routing-pending flag for routing gate enforcement
80
+ const isWogiCommand = typeof prompt === 'string' && /^\/wogi-[a-z0-9-]+\b/i.test(prompt.trim());
81
+ if (!isWogiCommand) {
82
+ // v8.1: Delete any stale cleared marker from previous turns.
159
83
  try {
160
- const phaseContext = getPhaseContextPrompt();
161
- if (phaseContext.inject && phaseContext.prompt) {
162
- phasePrompt = phaseContext.prompt;
84
+ fs.unlinkSync(ROUTING_CLEARED_PATH);
85
+ } catch (err) {
86
+ if (err.code !== 'ENOENT' && process.env.DEBUG) {
87
+ console.error(`[Hook] Failed to delete cleared marker: ${err.message}`);
163
88
  }
89
+ }
90
+
91
+ try {
92
+ setRoutingPending();
164
93
  } catch (err) {
165
- // Non-blocking - phase context is best-effort
166
94
  if (process.env.DEBUG) {
167
- console.error(`[Hook] Phase context injection failed: ${err.message}`);
95
+ console.error(`[Hook] Routing gate set failed: ${err.message}`);
168
96
  }
169
97
  }
170
-
171
- // Check research gate first (before implementation gate)
172
- // Auto-triggers research protocol for capability/existence/feasibility questions
173
- const researchResult = checkResearchRequirement({
174
- prompt,
175
- source
176
- });
177
-
178
- // Check implementation gate
179
- let coreResult = checkImplementationGate({
180
- prompt,
181
- source
182
- });
183
-
184
- // If research protocol should be injected, add it to system reminder
185
- if (researchResult.injectProtocol && researchResult.protocolSteps) {
186
- coreResult = {
187
- ...coreResult,
188
- systemReminder: researchResult.protocolSteps,
189
- researchTriggered: true,
190
- questionType: researchResult.questionType,
191
- suggestedDepth: researchResult.suggestedDepth
192
- };
193
- } else if (researchResult.warning && coreResult.allowed) {
194
- // Soft warning mode (not strict)
195
- coreResult = {
196
- ...coreResult,
197
- warning: true,
198
- researchWarning: researchResult.message,
199
- suggestedCommand: researchResult.suggestedCommand
200
- };
98
+ } else {
99
+ // v6.2: Actively CLEAR any existing routing flag when user explicitly types a /wogi-* command.
100
+ try {
101
+ clearRoutingPending();
102
+ if (process.env.DEBUG) {
103
+ console.error(`[Hook] Cleared routing flag — prompt is a /wogi-* command`);
104
+ }
105
+ } catch (err) {
106
+ if (process.env.DEBUG) {
107
+ console.error(`[Hook] Routing gate clear failed: ${err.message}`);
108
+ }
201
109
  }
110
+ }
202
111
 
203
- // Inject phase-specific context prompt (additionalSystemPrompt)
204
- if (phasePrompt) {
205
- coreResult = {
206
- ...coreResult,
207
- phasePrompt
208
- };
112
+ // Phase context injection
113
+ let phasePrompt = null;
114
+ try {
115
+ const phaseContext = getPhaseContextPrompt();
116
+ if (phaseContext.inject && phaseContext.prompt) {
117
+ phasePrompt = phaseContext.prompt;
209
118
  }
210
-
211
- // Transform to Claude Code format
212
- const output = claudeCodeAdapter.transformResult('UserPromptSubmit', coreResult);
213
-
214
- // Output JSON
215
- console.log(JSON.stringify(output));
216
- process.exit(0);
217
119
  } catch (err) {
218
- // Fail-closed: block the prompt on hook errors to prevent untracked implementation
219
- // Users installed WogiFlow to enforce task tracking - failing open would bypass that
220
120
  if (process.env.DEBUG) {
221
- console.error(`[Wogi Flow Hook Error] ${err.message}`);
222
- } else {
223
- console.error('[Wogi Flow Hook] Validation error occurred');
121
+ console.error(`[Hook] Phase context injection failed: ${err.message}`);
224
122
  }
225
- console.log(JSON.stringify({
226
- decision: 'block',
227
- reason: 'WogiFlow validation error. Please check your WogiFlow setup or use /wogi-start to route your request.'
228
- }));
229
- process.exit(0);
230
123
  }
231
- }
232
124
 
233
- // Handle stdin properly
234
- process.stdin.setEncoding('utf8');
125
+ // Check research gate first (before implementation gate)
126
+ const researchResult = checkResearchRequirement({
127
+ prompt,
128
+ source
129
+ });
130
+
131
+ // Check implementation gate
132
+ let coreResult = checkImplementationGate({
133
+ prompt,
134
+ source
135
+ });
136
+
137
+ // If research protocol should be injected, add it to system reminder
138
+ if (researchResult.injectProtocol && researchResult.protocolSteps) {
139
+ coreResult = {
140
+ ...coreResult,
141
+ systemReminder: researchResult.protocolSteps,
142
+ researchTriggered: true,
143
+ questionType: researchResult.questionType,
144
+ suggestedDepth: researchResult.suggestedDepth
145
+ };
146
+ } else if (researchResult.warning && coreResult.allowed) {
147
+ coreResult = {
148
+ ...coreResult,
149
+ warning: true,
150
+ researchWarning: researchResult.message,
151
+ suggestedCommand: researchResult.suggestedCommand
152
+ };
153
+ }
235
154
 
236
- // Must await async main() to prevent race conditions
237
- // Without await, Node.js may exit before stdin finishes reading
238
- (async () => {
239
- try {
240
- await main();
241
- } catch (err) {
242
- // Fail-closed: block on unexpected errors to prevent untracked implementation
243
- if (process.env.DEBUG) {
244
- console.error(`[Wogi Flow Hook] Unexpected error: ${err.message}`);
245
- }
246
- console.log(JSON.stringify({
247
- decision: 'block',
248
- reason: 'WogiFlow hook error. Use /wogi-start to route your request.'
249
- }));
250
- process.exit(0);
155
+ // Inject phase-specific context prompt
156
+ if (phasePrompt) {
157
+ coreResult = {
158
+ ...coreResult,
159
+ phasePrompt
160
+ };
161
+ }
162
+
163
+ return coreResult;
164
+ }, {
165
+ failMode: 'block',
166
+ failOutput: {
167
+ decision: 'block',
168
+ reason: 'WogiFlow validation error. Please check your WogiFlow setup or use /wogi-start to route your request.'
251
169
  }
252
- })();
170
+ });
@@ -11,29 +11,10 @@
11
11
  */
12
12
 
13
13
  const { handleWorktreeCreate } = require('../../core/worktree-lifecycle');
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
- const worktreePath = input.worktree_path || input.worktreePath || '';
25
- const projectRoot = input.cwd || process.cwd();
26
-
27
- const result = handleWorktreeCreate({ worktreePath, projectRoot });
28
- const output = claudeCodeAdapter.transformResult('WorktreeCreate', result);
29
-
30
- process.stdout.write(JSON.stringify(output));
31
- process.exit(0);
32
- } catch (err) {
33
- // Never block on worktree lifecycle errors
34
- process.stdout.write(JSON.stringify({ continue: true }));
35
- process.exit(0);
36
- }
37
- }
38
-
39
- main();
16
+ runHook('WorktreeCreate', async ({ input }) => {
17
+ const worktreePath = input.worktree_path || input.worktreePath || '';
18
+ const projectRoot = input.cwd || process.cwd();
19
+ return handleWorktreeCreate({ worktreePath, projectRoot });
20
+ }, { failMode: 'silent', useStdoutWrite: true });
@@ -11,29 +11,10 @@
11
11
  */
12
12
 
13
13
  const { handleWorktreeRemove } = require('../../core/worktree-lifecycle');
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
- const worktreePath = input.worktree_path || input.worktreePath || '';
25
- const projectRoot = input.cwd || process.cwd();
26
-
27
- const result = handleWorktreeRemove({ worktreePath, projectRoot });
28
- const output = claudeCodeAdapter.transformResult('WorktreeRemove', result);
29
-
30
- process.stdout.write(JSON.stringify(output));
31
- process.exit(0);
32
- } catch (err) {
33
- // Never block on worktree lifecycle errors
34
- process.stdout.write(JSON.stringify({ continue: true }));
35
- process.exit(0);
36
- }
37
- }
38
-
39
- main();
16
+ runHook('WorktreeRemove', async ({ input }) => {
17
+ const worktreePath = input.worktree_path || input.worktreePath || '';
18
+ const projectRoot = input.cwd || process.cwd();
19
+ return handleWorktreeRemove({ worktreePath, projectRoot });
20
+ }, { failMode: 'silent', useStdoutWrite: true });