specweave 1.0.550 → 1.0.552
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.
- package/CLAUDE.md +1 -1
- package/bin/specweave.js +23 -1
- package/dist/src/cli/commands/hook.d.ts +15 -0
- package/dist/src/cli/commands/hook.d.ts.map +1 -0
- package/dist/src/cli/commands/hook.js +61 -0
- package/dist/src/cli/commands/hook.js.map +1 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +5 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.d.ts.map +1 -1
- package/dist/src/cli/commands/refresh-plugins.js +11 -1
- package/dist/src/cli/commands/refresh-plugins.js.map +1 -1
- package/dist/src/cli/commands/sync-setup.d.ts.map +1 -1
- package/dist/src/cli/commands/sync-setup.js +7 -3
- package/dist/src/cli/commands/sync-setup.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts +9 -0
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js +9 -3
- package/dist/src/cli/helpers/issue-tracker/project-mapping-wizard.js.map +1 -1
- package/dist/src/config/types.d.ts +2 -2
- package/dist/src/core/config/types.d.ts +18 -2
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/hooks/handlers/hook-router.d.ts +19 -0
- package/dist/src/core/hooks/handlers/hook-router.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/hook-router.js +75 -0
- package/dist/src/core/hooks/handlers/hook-router.js.map +1 -0
- package/dist/src/core/hooks/handlers/index.d.ts +10 -0
- package/dist/src/core/hooks/handlers/index.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/index.js +9 -0
- package/dist/src/core/hooks/handlers/index.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js +73 -0
- package/dist/src/core/hooks/handlers/post-tool-use-analytics.js.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/post-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js +76 -0
- package/dist/src/core/hooks/handlers/post-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-compact.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-compact.js +77 -0
- package/dist/src/core/hooks/handlers/pre-compact.js.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +11 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js +318 -0
- package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts +9 -0
- package/dist/src/core/hooks/handlers/session-start.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/session-start.js +111 -0
- package/dist/src/core/hooks/handlers/session-start.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts +16 -0
- package/dist/src/core/hooks/handlers/stop-auto.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-auto.js +122 -0
- package/dist/src/core/hooks/handlers/stop-auto.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts +14 -0
- package/dist/src/core/hooks/handlers/stop-reflect.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js +43 -0
- package/dist/src/core/hooks/handlers/stop-reflect.js.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts +15 -0
- package/dist/src/core/hooks/handlers/stop-sync.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/stop-sync.js +68 -0
- package/dist/src/core/hooks/handlers/stop-sync.js.map +1 -0
- package/dist/src/core/hooks/handlers/types.d.ts +63 -0
- package/dist/src/core/hooks/handlers/types.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/types.js +27 -0
- package/dist/src/core/hooks/handlers/types.js.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts +14 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js +173 -0
- package/dist/src/core/hooks/handlers/user-prompt-submit.js.map +1 -0
- package/dist/src/core/hooks/handlers/utils.d.ts +25 -0
- package/dist/src/core/hooks/handlers/utils.d.ts.map +1 -0
- package/dist/src/core/hooks/handlers/utils.js +64 -0
- package/dist/src/core/hooks/handlers/utils.js.map +1 -0
- package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
- package/dist/src/core/increment/completion-validator.js +32 -0
- package/dist/src/core/increment/completion-validator.js.map +1 -1
- package/dist/src/init/research/types.d.ts +1 -1
- package/dist/src/sync/sync-target-resolver.js.map +1 -1
- package/dist/src/utils/lock-manager.d.ts.map +1 -1
- package/dist/src/utils/lock-manager.js +5 -0
- package/dist/src/utils/lock-manager.js.map +1 -1
- package/dist/src/utils/plugin-copier.d.ts +10 -0
- package/dist/src/utils/plugin-copier.d.ts.map +1 -1
- package/dist/src/utils/plugin-copier.js +63 -35
- package/dist/src/utils/plugin-copier.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/agents/sw-closer.md +3 -2
- package/plugins/specweave/hooks/hooks.json +10 -10
- package/plugins/specweave/skills/code-reviewer/SKILL.md +180 -16
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-comments.md +83 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-silent-failures.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-spec-compliance.md +19 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-tests.md +101 -0
- package/plugins/specweave/skills/code-reviewer/agents/reviewer-types.md +20 -0
- package/plugins/specweave/skills/done/SKILL.md +56 -21
- package/plugins/specweave/skills/grill/SKILL.md +1 -1
- package/plugins/specweave/skills/team-lead/agents/reviewer-logic.md +19 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-performance.md +20 -0
- package/plugins/specweave/skills/team-lead/agents/reviewer-security.md +20 -0
- package/src/templates/CLAUDE.md.template +7 -4
- package/plugins/specweave/hooks/README.md +0 -493
- package/plugins/specweave/hooks/_archive/stop-auto-v4-legacy.sh +0 -1319
- package/plugins/specweave/hooks/lib/common-setup.sh +0 -144
- package/plugins/specweave/hooks/lib/hook-errors.sh +0 -414
- package/plugins/specweave/hooks/lib/migrate-increment-work.sh +0 -245
- package/plugins/specweave/hooks/lib/resolve-package.sh +0 -146
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +0 -135
- package/plugins/specweave/hooks/lib/score-increment.sh +0 -87
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +0 -193
- package/plugins/specweave/hooks/lib/update-active-increment.sh +0 -95
- package/plugins/specweave/hooks/lib/update-status-line.sh +0 -233
- package/plugins/specweave/hooks/lib/validate-spec-status.sh +0 -171
- package/plugins/specweave/hooks/llm-judge-validator.sh +0 -219
- package/plugins/specweave/hooks/log-decision.sh +0 -168
- package/plugins/specweave/hooks/pre-compact.sh +0 -64
- package/plugins/specweave/hooks/startup-health-check.sh +0 -64
- package/plugins/specweave/hooks/stop-auto-v5.sh +0 -276
- package/plugins/specweave/hooks/stop-reflect.sh +0 -336
- package/plugins/specweave/hooks/stop-sync.sh +0 -283
- package/plugins/specweave/hooks/tests/test-auto-context-integration.sh +0 -126
- package/plugins/specweave/hooks/tests/test-stop-auto-enriched.sh +0 -128
- package/plugins/specweave/hooks/universal/dispatcher.mjs +0 -336
- package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +0 -325
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +0 -26
- package/plugins/specweave/hooks/universal/hook-wrapper.sh +0 -69
- package/plugins/specweave/hooks/universal/run-hook.sh +0 -20
- package/plugins/specweave/hooks/universal/session-start.cmd +0 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +0 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +0 -2550
- package/plugins/specweave/hooks/v2/detectors/lifecycle-detector.sh +0 -87
- package/plugins/specweave/hooks/v2/detectors/us-completion-detector.sh +0 -186
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh +0 -83
- package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +0 -447
- package/plugins/specweave/hooks/v2/dispatchers/pre-tool-use.sh +0 -104
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +0 -270
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/increment-existence-guard.sh +0 -240
- package/plugins/specweave/hooks/v2/guards/interview-enforcement-guard.sh +0 -171
- package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/skill-chain-enforcement-guard.sh +0 -222
- package/plugins/specweave/hooks/v2/guards/spec-template-enforcement-guard.sh +0 -21
- package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +0 -14
- package/plugins/specweave/hooks/v2/guards/status-completion-guard.sh +0 -84
- package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +0 -475
- package/plugins/specweave/hooks/v2/guards/tdd-enforcement-guard.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/ac-sync-dispatcher.sh +0 -332
- package/plugins/specweave/hooks/v2/handlers/ac-validation-handler.sh +0 -50
- package/plugins/specweave/hooks/v2/handlers/github-sync-handler.sh +0 -347
- package/plugins/specweave/hooks/v2/handlers/living-docs-handler.sh +0 -83
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +0 -268
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +0 -104
- package/plugins/specweave/hooks/v2/handlers/status-line-handler.sh +0 -165
- package/plugins/specweave/hooks/v2/handlers/status-update.sh +0 -61
- package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +0 -270
- package/plugins/specweave/hooks/v2/integrations/ado-post-living-docs-update.sh +0 -367
- package/plugins/specweave/hooks/v2/integrations/ado-post-task.sh +0 -179
- package/plugins/specweave/hooks/v2/integrations/github-auto-create-handler.sh +0 -553
- package/plugins/specweave/hooks/v2/integrations/github-post-task.sh +0 -345
- package/plugins/specweave/hooks/v2/integrations/jira-post-task.sh +0 -180
- package/plugins/specweave/hooks/v2/lib/check-provider-enabled.sh +0 -52
- package/plugins/specweave/hooks/v2/queue/enqueue.sh +0 -81
- package/plugins/specweave/hooks/v2/session-end.sh +0 -139
- package/plugins/specweave/hooks/validate-skill-activations.sh +0 -227
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Universal Hook Dispatcher (Cross-Platform)
|
|
4
|
-
*
|
|
5
|
-
* Routes hook calls to the appropriate TypeScript implementation.
|
|
6
|
-
* Works on Windows, macOS, and Linux.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* node dispatcher.mjs <hook-type>
|
|
10
|
-
*
|
|
11
|
-
* Where hook-type is one of:
|
|
12
|
-
* - session-start
|
|
13
|
-
* - post-tool-use
|
|
14
|
-
* - completion-guard
|
|
15
|
-
* - increment-duplicate-guard
|
|
16
|
-
*
|
|
17
|
-
* @module hooks/universal/dispatcher
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { spawn } from 'child_process';
|
|
21
|
-
import { fileURLToPath } from 'url';
|
|
22
|
-
import { dirname, join, resolve } from 'path';
|
|
23
|
-
import { existsSync } from 'fs';
|
|
24
|
-
|
|
25
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
-
const __dirname = dirname(__filename);
|
|
27
|
-
|
|
28
|
-
// Hook type from arguments
|
|
29
|
-
const hookType = process.argv[2] || 'unknown';
|
|
30
|
-
|
|
31
|
-
// Global timeout for hook execution (30 seconds max)
|
|
32
|
-
const HOOK_TIMEOUT_MS = 30000;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Detect if we're in a development environment with duplicate hooks
|
|
36
|
-
*
|
|
37
|
-
* Problem: When working IN the specweave project AND having the marketplace
|
|
38
|
-
* plugin installed, Claude Code loads hooks from BOTH sources, causing:
|
|
39
|
-
* - Duplicate hook execution
|
|
40
|
-
* - Potential race conditions
|
|
41
|
-
* - Confusing output
|
|
42
|
-
*
|
|
43
|
-
* Solution: If we detect we're running from the marketplace copy while
|
|
44
|
-
* the CWD is the specweave project itself, skip execution (let local hooks handle it).
|
|
45
|
-
*
|
|
46
|
-
* @returns {boolean} true if we should skip this hook execution
|
|
47
|
-
*/
|
|
48
|
-
function shouldSkipDueToDevEnvironment() {
|
|
49
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || '';
|
|
50
|
-
const cwd = process.cwd();
|
|
51
|
-
|
|
52
|
-
// Check if running from marketplace (installed plugin)
|
|
53
|
-
const isFromMarketplace = pluginRoot.includes('.claude/plugins/marketplaces/');
|
|
54
|
-
|
|
55
|
-
// Check if CWD is the specweave project itself
|
|
56
|
-
const isInSpecweaveProject = existsSync(join(cwd, 'plugins', 'specweave', 'hooks', 'hooks.json'));
|
|
57
|
-
|
|
58
|
-
// Skip marketplace hooks when developing specweave locally
|
|
59
|
-
if (isFromMarketplace && isInSpecweaveProject) {
|
|
60
|
-
// Output diagnostic only for session-start (once per session)
|
|
61
|
-
if (hookType === 'session-start') {
|
|
62
|
-
console.log(JSON.stringify({
|
|
63
|
-
continue: true,
|
|
64
|
-
systemMessage: 'SpecWeave dev environment: Using local hooks (marketplace hooks skipped)'
|
|
65
|
-
}));
|
|
66
|
-
} else {
|
|
67
|
-
console.log(JSON.stringify({ continue: true }));
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Wrap a promise with a timeout
|
|
77
|
-
* @param promise - Promise to wrap
|
|
78
|
-
* @param timeoutMs - Timeout in milliseconds
|
|
79
|
-
* @param cleanup - Optional cleanup function (e.g., to kill child process)
|
|
80
|
-
*/
|
|
81
|
-
function withTimeout(promise, timeoutMs, cleanup) {
|
|
82
|
-
let timeoutId;
|
|
83
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
84
|
-
timeoutId = setTimeout(() => {
|
|
85
|
-
if (cleanup) cleanup();
|
|
86
|
-
reject(new Error(`Hook timeout after ${timeoutMs}ms`));
|
|
87
|
-
}, timeoutMs);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return Promise.race([promise, timeoutPromise]).finally(() => {
|
|
91
|
-
clearTimeout(timeoutId);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Find the dist/src/hooks directory (TypeScript compiled hooks)
|
|
97
|
-
*/
|
|
98
|
-
function findHooksDir() {
|
|
99
|
-
// Try multiple locations
|
|
100
|
-
const candidates = [
|
|
101
|
-
// Development: project root dist/src/hooks
|
|
102
|
-
join(__dirname, '..', '..', '..', '..', 'dist', 'src', 'hooks'),
|
|
103
|
-
// Fallback: older path without src/ (backward compatibility)
|
|
104
|
-
join(__dirname, '..', '..', '..', '..', 'dist', 'hooks'),
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
for (const candidate of candidates) {
|
|
108
|
-
const resolved = resolve(candidate);
|
|
109
|
-
if (existsSync(resolved)) {
|
|
110
|
-
return resolved;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Run a hook script with timeout protection
|
|
119
|
-
*/
|
|
120
|
-
async function runHook(scriptName) {
|
|
121
|
-
const hooksDir = findHooksDir();
|
|
122
|
-
if (!hooksDir) {
|
|
123
|
-
// No hooks directory - just continue
|
|
124
|
-
console.log(JSON.stringify({ continue: true }));
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const scriptPath = join(hooksDir, scriptName);
|
|
129
|
-
if (!existsSync(scriptPath) && !existsSync(scriptPath + '.js')) {
|
|
130
|
-
console.log(JSON.stringify({ continue: true }));
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Spawn node to run the script
|
|
135
|
-
const child = spawn(process.execPath, [scriptPath], {
|
|
136
|
-
stdio: ['inherit', 'inherit', 'inherit'],
|
|
137
|
-
windowsHide: true,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const execPromise = new Promise((resolve) => {
|
|
141
|
-
child.on('exit', (code) => resolve(code || 0));
|
|
142
|
-
child.on('error', () => {
|
|
143
|
-
console.log(JSON.stringify({ continue: true }));
|
|
144
|
-
resolve(1);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
return await withTimeout(execPromise, HOOK_TIMEOUT_MS, () => {
|
|
150
|
-
// Kill the child process on timeout
|
|
151
|
-
try {
|
|
152
|
-
child.kill('SIGTERM');
|
|
153
|
-
setTimeout(() => child.kill('SIGKILL'), 1000);
|
|
154
|
-
} catch { /* ignore */ }
|
|
155
|
-
});
|
|
156
|
-
} catch (err) {
|
|
157
|
-
console.log(JSON.stringify({ continue: true, error: err.message }));
|
|
158
|
-
return 1;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Cached Git Bash path (computed once per session)
|
|
164
|
-
* undefined = not yet checked, null = checked and not found, string = path found
|
|
165
|
-
*/
|
|
166
|
-
let gitBashCache = undefined;
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Find Git Bash on Windows (with caching)
|
|
170
|
-
*
|
|
171
|
-
* Checks common Git for Windows installation paths.
|
|
172
|
-
* Caches result to avoid repeated filesystem checks.
|
|
173
|
-
*/
|
|
174
|
-
function findGitBash() {
|
|
175
|
-
// Return cached result if available
|
|
176
|
-
if (gitBashCache !== undefined) {
|
|
177
|
-
return gitBashCache;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const gitBashPaths = [
|
|
181
|
-
// Environment variable paths with proper fallbacks
|
|
182
|
-
join(process.env.PROGRAMFILES || 'C:\\Program Files', 'Git', 'bin', 'bash.exe'),
|
|
183
|
-
join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'Git', 'bin', 'bash.exe'),
|
|
184
|
-
join(process.env.LOCALAPPDATA || '', 'Programs', 'Git', 'bin', 'bash.exe'),
|
|
185
|
-
// Hardcoded fallbacks for edge cases where env vars are missing
|
|
186
|
-
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
187
|
-
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
188
|
-
'C:\\Git\\bin\\bash.exe',
|
|
189
|
-
];
|
|
190
|
-
|
|
191
|
-
for (const p of gitBashPaths) {
|
|
192
|
-
if (p && existsSync(p)) {
|
|
193
|
-
gitBashCache = p;
|
|
194
|
-
return p;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
gitBashCache = null;
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Helper to spawn a bash script with timeout protection
|
|
204
|
-
*/
|
|
205
|
-
async function spawnBashWithTimeout(bashExe, args, options = {}) {
|
|
206
|
-
const child = spawn(bashExe, args, {
|
|
207
|
-
stdio: ['inherit', 'inherit', 'inherit'],
|
|
208
|
-
windowsHide: true,
|
|
209
|
-
...options,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const execPromise = new Promise((resolve) => {
|
|
213
|
-
child.on('exit', (code) => resolve(code || 0));
|
|
214
|
-
child.on('error', () => {
|
|
215
|
-
console.log(JSON.stringify({ continue: true }));
|
|
216
|
-
resolve(1);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
return await withTimeout(execPromise, HOOK_TIMEOUT_MS, () => {
|
|
222
|
-
// Kill the child process on timeout
|
|
223
|
-
try {
|
|
224
|
-
child.kill('SIGTERM');
|
|
225
|
-
setTimeout(() => child.kill('SIGKILL'), 1000);
|
|
226
|
-
} catch { /* ignore */ }
|
|
227
|
-
});
|
|
228
|
-
} catch (err) {
|
|
229
|
-
console.log(JSON.stringify({ continue: true, error: err.message }));
|
|
230
|
-
return 1;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Fallback to bash script if TypeScript not built (with timeout protection)
|
|
236
|
-
*
|
|
237
|
-
* @param bashScript - Name of the bash script (e.g., 'post-tool-use.sh')
|
|
238
|
-
* @param subdir - Subdirectory under v2 (e.g., 'dispatchers', 'guards')
|
|
239
|
-
*/
|
|
240
|
-
async function fallbackToBash(bashScript, subdir = 'dispatchers') {
|
|
241
|
-
const isWindows = process.platform === 'win32';
|
|
242
|
-
const bashDir = join(__dirname, '..', 'v2', subdir);
|
|
243
|
-
const scriptPath = join(bashDir, bashScript);
|
|
244
|
-
|
|
245
|
-
if (!existsSync(scriptPath)) {
|
|
246
|
-
console.log(JSON.stringify({ continue: true }));
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (isWindows) {
|
|
251
|
-
// Strategy 1: Git Bash (preferred - most common)
|
|
252
|
-
const bashExe = findGitBash();
|
|
253
|
-
if (bashExe) {
|
|
254
|
-
return spawnBashWithTimeout(bashExe, [scriptPath]);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Strategy 2: WSL (if Git Bash not available) - FALLBACK only
|
|
258
|
-
const wslPath = join(process.env.SYSTEMROOT || 'C:\\Windows', 'System32', 'wsl.exe');
|
|
259
|
-
if (existsSync(wslPath)) {
|
|
260
|
-
// Convert Windows path to WSL path:
|
|
261
|
-
// 1. Backslashes to forward slashes: C:\path\file -> C:/path/file
|
|
262
|
-
// 2. Drive letter to /mnt/: C:/path/file -> /mnt/c/path/file
|
|
263
|
-
// NOTE: Handle BOTH uppercase and lowercase drive letters (C: and c:)
|
|
264
|
-
const wslScriptPath = scriptPath
|
|
265
|
-
.replace(/\\/g, '/')
|
|
266
|
-
.replace(/^([A-Za-z]):/, (_, d) => `/mnt/${d.toLowerCase()}`);
|
|
267
|
-
return spawnBashWithTimeout('wsl', ['bash', wslScriptPath]);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Strategy 3: No bash available - output warning and continue
|
|
271
|
-
// Hooks should not block Claude Code operation
|
|
272
|
-
console.log(JSON.stringify({
|
|
273
|
-
continue: true,
|
|
274
|
-
systemMessage: 'SpecWeave hooks require Git Bash on Windows. Install from https://git-scm.com'
|
|
275
|
-
}));
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// POSIX (macOS, Linux) - run directly with timeout
|
|
280
|
-
return spawnBashWithTimeout('bash', [scriptPath]);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Main routing
|
|
284
|
-
async function main() {
|
|
285
|
-
// CRITICAL: Skip if we're in dev environment with duplicate hooks
|
|
286
|
-
// This prevents marketplace hooks from running when local hooks are available
|
|
287
|
-
if (shouldSkipDueToDevEnvironment()) {
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const isWindows = process.platform === 'win32';
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
switch (hookType) {
|
|
295
|
-
case 'session-start':
|
|
296
|
-
// CRITICAL: On Windows, ALWAYS use TypeScript implementation
|
|
297
|
-
// Bash nohup/disown is unreliable in Git Bash (process may terminate early)
|
|
298
|
-
// TypeScript uses proper Windows detached spawn with windowsHide
|
|
299
|
-
if (isWindows) {
|
|
300
|
-
await runHook('session-start.js');
|
|
301
|
-
} else {
|
|
302
|
-
// POSIX: try TypeScript first (preferred), fallback to bash for compatibility
|
|
303
|
-
try {
|
|
304
|
-
await runHook('session-start.js');
|
|
305
|
-
} catch {
|
|
306
|
-
await fallbackToBash('session-start.sh', 'dispatchers');
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
break;
|
|
310
|
-
|
|
311
|
-
case 'post-tool-use':
|
|
312
|
-
// Currently only bash implementation
|
|
313
|
-
// On Windows without Git Bash, will gracefully skip
|
|
314
|
-
await fallbackToBash('post-tool-use.sh', 'dispatchers');
|
|
315
|
-
break;
|
|
316
|
-
|
|
317
|
-
case 'completion-guard':
|
|
318
|
-
// Guards are in a different subdirectory
|
|
319
|
-
// On Windows without Git Bash, will gracefully skip
|
|
320
|
-
await fallbackToBash('completion-guard.sh', 'guards');
|
|
321
|
-
break;
|
|
322
|
-
|
|
323
|
-
case 'increment-duplicate-guard':
|
|
324
|
-
// Prevents duplicate increment IDs
|
|
325
|
-
await fallbackToBash('increment-duplicate-guard.sh', 'guards');
|
|
326
|
-
break;
|
|
327
|
-
|
|
328
|
-
default:
|
|
329
|
-
console.log(JSON.stringify({ continue: true, error: `Unknown hook type: ${hookType}` }));
|
|
330
|
-
}
|
|
331
|
-
} catch (err) {
|
|
332
|
-
console.log(JSON.stringify({ continue: true, error: String(err) }));
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
main();
|
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# fail-fast-wrapper.sh - Non-blocking timeout wrapper for hooks
|
|
3
|
-
#
|
|
4
|
-
# v1.0.153: Hook-specific timeouts (user-prompt-submit: 30s for LLM detection)
|
|
5
|
-
# v1.0.102+: Cross-platform support (Linux, macOS, BSD, Windows Git Bash)
|
|
6
|
-
#
|
|
7
|
-
# CRITICAL DESIGN PRINCIPLE:
|
|
8
|
-
# - NEVER block tool operations
|
|
9
|
-
# - All errors become warnings shown to user
|
|
10
|
-
# - Safe JSON output even on catastrophic failures
|
|
11
|
-
# - Universal timeout support (Perl fallback for all platforms)
|
|
12
|
-
#
|
|
13
|
-
# Usage: bash fail-fast-wrapper.sh <hook-script> [args...]
|
|
14
|
-
#
|
|
15
|
-
# Environment:
|
|
16
|
-
# HOOK_TIMEOUT - max seconds (default: 5)
|
|
17
|
-
# SPECWEAVE_HOOK_VERBOSE - show all warnings (default: 1)
|
|
18
|
-
#
|
|
19
|
-
# Returns safe JSON on timeout or error. Never hangs.
|
|
20
|
-
|
|
21
|
-
set +e # CRITICAL: Never exit on error
|
|
22
|
-
|
|
23
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT:-5}"
|
|
24
|
-
WRAPPER_VERSION="1.0.153"
|
|
25
|
-
|
|
26
|
-
# ============================================================================
|
|
27
|
-
# HOOK-SPECIFIC TIMEOUT OVERRIDES (v1.0.153)
|
|
28
|
-
# ============================================================================
|
|
29
|
-
# Some hooks require longer timeouts due to LLM calls or network operations.
|
|
30
|
-
# user-prompt-submit.sh: LLM-based detect-intent (~15s) + plugin installs (~4s)
|
|
31
|
-
# Override defaults here while keeping fast timeouts for simple hooks.
|
|
32
|
-
#
|
|
33
|
-
# Environment overrides (if you need custom values):
|
|
34
|
-
# HOOK_TIMEOUT_USER_PROMPT - timeout for user-prompt-submit (default: 30)
|
|
35
|
-
# HOOK_TIMEOUT_SESSION_START - timeout for session-start (default: 20)
|
|
36
|
-
|
|
37
|
-
script_name=$(basename "${1:-}" 2>/dev/null)
|
|
38
|
-
case "$script_name" in
|
|
39
|
-
user-prompt-submit.sh)
|
|
40
|
-
# LLM detection can timeout (30s) + keyword fallback + plugin installs (~10s)
|
|
41
|
-
# Total worst case: 30s LLM timeout + 10s installs + 5s buffer = 45s
|
|
42
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT_USER_PROMPT:-45}"
|
|
43
|
-
;;
|
|
44
|
-
session-start.sh)
|
|
45
|
-
# Project detection may involve file scanning and optional LLM
|
|
46
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT_SESSION_START:-20}"
|
|
47
|
-
;;
|
|
48
|
-
stop-auto.sh)
|
|
49
|
-
# May run tests + LLM evaluation + validation
|
|
50
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT_STOP_AUTO:-120}"
|
|
51
|
-
;;
|
|
52
|
-
post-tool-use.sh)
|
|
53
|
-
# Dispatcher runs task-ac-sync (awk/sed/jq) + spawns background detectors
|
|
54
|
-
# 5s is too tight for synchronous file processing + background spawning
|
|
55
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT_POST_TOOL_USE:-15}"
|
|
56
|
-
;;
|
|
57
|
-
pre-compact.sh)
|
|
58
|
-
# Pressure signal is ultra-fast (just writes a JSON file)
|
|
59
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT_PRE_COMPACT:-5}"
|
|
60
|
-
;;
|
|
61
|
-
stop-*.sh)
|
|
62
|
-
# Other stop hooks (reflect, sync, grill) are usually fast
|
|
63
|
-
HOOK_TIMEOUT="${HOOK_TIMEOUT_STOP:-15}"
|
|
64
|
-
;;
|
|
65
|
-
esac
|
|
66
|
-
|
|
67
|
-
# ============================================================================
|
|
68
|
-
# PROJECT ROOT DETECTION (for logging) - CRITICAL: must NOT fallback to pwd!
|
|
69
|
-
# ============================================================================
|
|
70
|
-
|
|
71
|
-
find_project_root() {
|
|
72
|
-
local dir="$PWD"
|
|
73
|
-
while [[ "$dir" != "/" ]]; do
|
|
74
|
-
if [[ -f "$dir/.specweave/config.json" ]]; then
|
|
75
|
-
echo "$dir"
|
|
76
|
-
return 0
|
|
77
|
-
fi
|
|
78
|
-
dir=$(dirname "$dir")
|
|
79
|
-
done
|
|
80
|
-
# Return empty - NOT pwd (prevents .specweave pollution)
|
|
81
|
-
return 1
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
PROJECT_ROOT=$(find_project_root)
|
|
85
|
-
|
|
86
|
-
# Exit early if not a SpecWeave project (prevents .specweave pollution)
|
|
87
|
-
if [[ -z "$PROJECT_ROOT" ]] || [[ ! -f "$PROJECT_ROOT/.specweave/config.json" ]]; then
|
|
88
|
-
# Not a SpecWeave project - output success JSON and exit
|
|
89
|
-
# UserPromptSubmit and Stop hooks need {"decision":"approve"}, others need {"continue":true}
|
|
90
|
-
if [[ "$script_name" == stop-* ]] || [[ "$script_name" == user-prompt-submit* ]]; then
|
|
91
|
-
echo '{"decision":"approve"}'
|
|
92
|
-
else
|
|
93
|
-
echo '{"continue": true}'
|
|
94
|
-
fi
|
|
95
|
-
exit 0
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
99
|
-
WARNING_LOG="$LOGS_DIR/hook-warnings.log"
|
|
100
|
-
mkdir -p "$LOGS_DIR" 2>/dev/null || true
|
|
101
|
-
|
|
102
|
-
# ============================================================================
|
|
103
|
-
# JSON UTILITIES (v1.0.102+)
|
|
104
|
-
# ============================================================================
|
|
105
|
-
|
|
106
|
-
# Escape string for safe JSON embedding
|
|
107
|
-
escape_json() {
|
|
108
|
-
local str="$1"
|
|
109
|
-
# Escape backslash, double quote, newline, carriage return, tab
|
|
110
|
-
str="${str//\\/\\\\}" # Backslash
|
|
111
|
-
str="${str//\"/\\\"}" # Double quote
|
|
112
|
-
str="${str//$'\n'/\\n}" # Newline
|
|
113
|
-
str="${str//$'\r'/\\r}" # Carriage return
|
|
114
|
-
str="${str//$'\t'/\\t}" # Tab
|
|
115
|
-
echo "$str"
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
# ============================================================================
|
|
119
|
-
# LOGGING
|
|
120
|
-
# ============================================================================
|
|
121
|
-
|
|
122
|
-
log_warning() {
|
|
123
|
-
local script="$1"
|
|
124
|
-
local message="$2"
|
|
125
|
-
local details="${3:-}"
|
|
126
|
-
|
|
127
|
-
local script_name
|
|
128
|
-
script_name=$(basename "$script" 2>/dev/null || echo "unknown")
|
|
129
|
-
|
|
130
|
-
# Log to file only (v1.0.102+: warnings now in JSON response)
|
|
131
|
-
{
|
|
132
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING [fail-fast-wrapper]: $message"
|
|
133
|
-
echo " Script: $script_name"
|
|
134
|
-
[[ -n "$details" ]] && echo " Details: $details"
|
|
135
|
-
} >> "$WARNING_LOG" 2>/dev/null || true
|
|
136
|
-
|
|
137
|
-
# NOTE: No console output - warnings are in JSON response
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
# ============================================================================
|
|
141
|
-
# SAFE OUTPUT GENERATION (v1.0.102+)
|
|
142
|
-
# ============================================================================
|
|
143
|
-
|
|
144
|
-
# Safe output with warnings (JSON-escaped)
|
|
145
|
-
get_safe_output_with_warnings() {
|
|
146
|
-
local script="$1"
|
|
147
|
-
local severity="$2"
|
|
148
|
-
local message="$3"
|
|
149
|
-
local recommendation="$4"
|
|
150
|
-
|
|
151
|
-
local script_name
|
|
152
|
-
script_name=$(basename "$script" 2>/dev/null || echo "unknown")
|
|
153
|
-
|
|
154
|
-
# Escape all strings for JSON safety
|
|
155
|
-
local escaped_name
|
|
156
|
-
local escaped_message
|
|
157
|
-
local escaped_recommendation
|
|
158
|
-
escaped_name=$(escape_json "$script_name")
|
|
159
|
-
escaped_message=$(escape_json "$message")
|
|
160
|
-
escaped_recommendation=$(escape_json "$recommendation")
|
|
161
|
-
|
|
162
|
-
# Build JSON with warnings array
|
|
163
|
-
# Detect correct response format based on hook lifecycle phase:
|
|
164
|
-
# - PreToolUse dispatchers/guards: {"decision":"allow"} or {"decision":"block"}
|
|
165
|
-
# - PostToolUse dispatchers: {"continue":true}
|
|
166
|
-
# - Stop/UserPromptSubmit: {"decision":"approve"}
|
|
167
|
-
local base_script
|
|
168
|
-
base_script=$(basename "$script" 2>/dev/null)
|
|
169
|
-
if [[ "$script" == *"guard"* ]] || [[ "$script" == *"validator"* ]] || [[ "$base_script" == pre-tool-use* ]]; then
|
|
170
|
-
cat <<EOF
|
|
171
|
-
{"decision":"allow","warnings":[{"severity":"${severity}","message":"${escaped_name}: ${escaped_message}","recommendation":"${escaped_recommendation}"}]}
|
|
172
|
-
EOF
|
|
173
|
-
elif [[ "$base_script" == stop-* ]] || [[ "$base_script" == user-prompt-submit* ]]; then
|
|
174
|
-
cat <<EOF
|
|
175
|
-
{"decision":"approve","warnings":[{"severity":"${severity}","message":"${escaped_name}: ${escaped_message}","recommendation":"${escaped_recommendation}"}]}
|
|
176
|
-
EOF
|
|
177
|
-
else
|
|
178
|
-
cat <<EOF
|
|
179
|
-
{"continue":true,"warnings":[{"severity":"${severity}","message":"${escaped_name}: ${escaped_message}","recommendation":"${escaped_recommendation}"}]}
|
|
180
|
-
EOF
|
|
181
|
-
fi
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
# Safe output without warnings (success case)
|
|
185
|
-
get_safe_output() {
|
|
186
|
-
local script="$1"
|
|
187
|
-
local base_script
|
|
188
|
-
base_script=$(basename "$script" 2>/dev/null)
|
|
189
|
-
if [[ "$script" == *"guard"* ]] || [[ "$script" == *"validator"* ]] || [[ "$base_script" == pre-tool-use* ]]; then
|
|
190
|
-
echo '{"decision":"allow","warnings":[]}'
|
|
191
|
-
elif [[ "$base_script" == stop-* ]] || [[ "$base_script" == user-prompt-submit* ]]; then
|
|
192
|
-
echo '{"decision":"approve","warnings":[]}'
|
|
193
|
-
else
|
|
194
|
-
echo '{"continue":true,"warnings":[]}'
|
|
195
|
-
fi
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
# ============================================================================
|
|
199
|
-
# MAIN EXECUTION
|
|
200
|
-
# ============================================================================
|
|
201
|
-
|
|
202
|
-
script="$1"
|
|
203
|
-
shift
|
|
204
|
-
|
|
205
|
-
# No script = safe default
|
|
206
|
-
if [[ -z "$script" ]]; then
|
|
207
|
-
echo '{"continue":true}'
|
|
208
|
-
exit 0
|
|
209
|
-
fi
|
|
210
|
-
|
|
211
|
-
# Script doesn't exist = safe default with warning
|
|
212
|
-
if [[ ! -f "$script" ]]; then
|
|
213
|
-
log_warning "$script" "Script not found (may be refreshing)" "Path: $script"
|
|
214
|
-
get_safe_output_with_warnings "$script" "WARNING" "Script not found (may be refreshing)" "Run 'specweave refresh-marketplace' to update plugins"
|
|
215
|
-
exit 0
|
|
216
|
-
fi
|
|
217
|
-
|
|
218
|
-
# Read stdin with non-blocking timeout (v1.0.102+)
|
|
219
|
-
# Uses Perl for universal cross-platform support
|
|
220
|
-
stdin_content=""
|
|
221
|
-
if command -v perl >/dev/null 2>&1; then
|
|
222
|
-
# Perl-based timeout (works on all Unix systems)
|
|
223
|
-
stdin_content=$(perl -e '
|
|
224
|
-
use strict;
|
|
225
|
-
use warnings;
|
|
226
|
-
|
|
227
|
-
eval {
|
|
228
|
-
local $SIG{ALRM} = sub { die "timeout\n" };
|
|
229
|
-
alarm(1);
|
|
230
|
-
local $/;
|
|
231
|
-
my $input = <STDIN>;
|
|
232
|
-
alarm(0);
|
|
233
|
-
print $input;
|
|
234
|
-
};
|
|
235
|
-
if ($@) {
|
|
236
|
-
print "{}";
|
|
237
|
-
}
|
|
238
|
-
' 2>/dev/null || echo '{}')
|
|
239
|
-
elif command -v gtimeout >/dev/null 2>&1; then
|
|
240
|
-
stdin_content=$(gtimeout 1 cat 2>/dev/null || echo '{}')
|
|
241
|
-
elif command -v timeout >/dev/null 2>&1; then
|
|
242
|
-
stdin_content=$(timeout 1 cat 2>/dev/null || echo '{}')
|
|
243
|
-
else
|
|
244
|
-
# Last resort: try reading without blocking (may hang if stdin has data)
|
|
245
|
-
stdin_content=$(cat 2>/dev/null || echo '{}')
|
|
246
|
-
fi
|
|
247
|
-
|
|
248
|
-
# Run with timeout (v1.0.102+: Universal cross-platform support)
|
|
249
|
-
tmp_out=$(mktemp -t hook-out.XXXXXX 2>/dev/null || echo "/tmp/hook-out-$$")
|
|
250
|
-
tmp_err=$(mktemp -t hook-err.XXXXXX 2>/dev/null || echo "/tmp/hook-err-$$")
|
|
251
|
-
|
|
252
|
-
# Try GNU timeout (Linux), then BSD timeout (macOS with coreutils), then Perl fallback
|
|
253
|
-
if command -v gtimeout >/dev/null 2>&1; then
|
|
254
|
-
# GNU timeout (Linux, coreutils on macOS)
|
|
255
|
-
echo "$stdin_content" | gtimeout --kill-after=2 "$HOOK_TIMEOUT" bash "$script" "$@" > "$tmp_out" 2> "$tmp_err"
|
|
256
|
-
exit_code=$?
|
|
257
|
-
elif command -v timeout >/dev/null 2>&1; then
|
|
258
|
-
# BSD timeout (some systems)
|
|
259
|
-
echo "$stdin_content" | timeout --kill-after=2 "$HOOK_TIMEOUT" bash "$script" "$@" > "$tmp_out" 2> "$tmp_err"
|
|
260
|
-
exit_code=$?
|
|
261
|
-
else
|
|
262
|
-
# Bash-only fallback (for systems without timeout/gtimeout - e.g., macOS)
|
|
263
|
-
# Run hook in background
|
|
264
|
-
echo "$stdin_content" | bash "$script" "$@" > "$tmp_out" 2> "$tmp_err" &
|
|
265
|
-
script_pid=$!
|
|
266
|
-
|
|
267
|
-
# Wait with timeout using simple loop
|
|
268
|
-
elapsed=0
|
|
269
|
-
while kill -0 $script_pid 2>/dev/null && [ $elapsed -lt $HOOK_TIMEOUT ]; do
|
|
270
|
-
sleep 1
|
|
271
|
-
elapsed=$((elapsed + 1))
|
|
272
|
-
done
|
|
273
|
-
|
|
274
|
-
# Check if still running (timeout occurred)
|
|
275
|
-
if kill -0 $script_pid 2>/dev/null; then
|
|
276
|
-
# Timeout - kill the process
|
|
277
|
-
kill -TERM $script_pid 2>/dev/null
|
|
278
|
-
sleep 2
|
|
279
|
-
kill -KILL $script_pid 2>/dev/null
|
|
280
|
-
wait $script_pid 2>/dev/null
|
|
281
|
-
exit_code=124 # Timeout exit code
|
|
282
|
-
else
|
|
283
|
-
# Process finished - get exit code
|
|
284
|
-
wait $script_pid
|
|
285
|
-
exit_code=$?
|
|
286
|
-
fi
|
|
287
|
-
fi
|
|
288
|
-
|
|
289
|
-
output=$(cat "$tmp_out" 2>/dev/null)
|
|
290
|
-
errors=$(cat "$tmp_err" 2>/dev/null)
|
|
291
|
-
rm -f "$tmp_out" "$tmp_err" 2>/dev/null
|
|
292
|
-
|
|
293
|
-
# Handle different exit scenarios
|
|
294
|
-
case $exit_code in
|
|
295
|
-
0)
|
|
296
|
-
# Success - return output or safe default with empty warnings
|
|
297
|
-
if [[ -n "$output" ]]; then
|
|
298
|
-
echo "$output"
|
|
299
|
-
else
|
|
300
|
-
get_safe_output "$script"
|
|
301
|
-
fi
|
|
302
|
-
;;
|
|
303
|
-
|
|
304
|
-
124|137)
|
|
305
|
-
# Timeout (124 = timeout, 137 = SIGKILL)
|
|
306
|
-
log_warning "$script" "Hook timed out after ${HOOK_TIMEOUT}s" "Consider optimizing or increasing HOOK_TIMEOUT"
|
|
307
|
-
get_safe_output_with_warnings "$script" "WARNING" "Hook timed out after ${HOOK_TIMEOUT}s" "Consider optimizing or increasing HOOK_TIMEOUT"
|
|
308
|
-
;;
|
|
309
|
-
|
|
310
|
-
*)
|
|
311
|
-
# Other error
|
|
312
|
-
error_msg="Hook failed (exit $exit_code)"
|
|
313
|
-
recommendation="Run 'specweave check-hooks' to diagnose issues"
|
|
314
|
-
|
|
315
|
-
if [[ -n "$errors" ]]; then
|
|
316
|
-
log_warning "$script" "$error_msg" "$errors"
|
|
317
|
-
get_safe_output_with_warnings "$script" "ERROR" "$error_msg: $errors" "$recommendation"
|
|
318
|
-
else
|
|
319
|
-
log_warning "$script" "$error_msg"
|
|
320
|
-
get_safe_output_with_warnings "$script" "ERROR" "$error_msg" "$recommendation"
|
|
321
|
-
fi
|
|
322
|
-
;;
|
|
323
|
-
esac
|
|
324
|
-
|
|
325
|
-
exit 0
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
REM hook-wrapper.cmd - Windows resilient hook launcher
|
|
3
|
-
REM Prevents crashes when dispatcher.mjs is temporarily unavailable
|
|
4
|
-
|
|
5
|
-
setlocal enabledelayedexpansion
|
|
6
|
-
|
|
7
|
-
set "HOOK_TYPE=%~1"
|
|
8
|
-
if "%HOOK_TYPE%"=="" set "HOOK_TYPE=unknown"
|
|
9
|
-
|
|
10
|
-
set "SCRIPT_DIR=%~dp0"
|
|
11
|
-
set "DISPATCHER=%SCRIPT_DIR%dispatcher.mjs"
|
|
12
|
-
|
|
13
|
-
REM Check if dispatcher exists
|
|
14
|
-
if not exist "%DISPATCHER%" (
|
|
15
|
-
echo {"continue":true,"systemMessage":"Hook skipped: dispatcher.mjs not found"}
|
|
16
|
-
exit /b 0
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
REM Run dispatcher with error suppression
|
|
20
|
-
node "%DISPATCHER%" "%HOOK_TYPE%" 2>nul
|
|
21
|
-
if errorlevel 1 (
|
|
22
|
-
echo {"continue":true,"systemMessage":"Hook error, continuing"}
|
|
23
|
-
exit /b 0
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
exit /b 0
|