swarmdo 1.58.18 → 1.58.23
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/agents/base-template-generator.md +1 -1
- package/.claude/agents/goal/code-goal-planner.md +1 -1
- package/.claude/helpers/hook-handler.cjs +11 -7
- package/README.md +1 -1
- package/package.json +1 -1
- package/v3/@swarmdo/cli/.claude/helpers/agent-bridge-hook.cjs +174 -0
- package/v3/@swarmdo/cli/.claude/helpers/auto-memory-hook.mjs +274 -27
- package/v3/@swarmdo/cli/.claude/helpers/hook-handler.cjs +150 -107
- package/v3/@swarmdo/cli/.claude/helpers/intelligence.cjs +196 -989
- package/v3/@swarmdo/cli/.claude/helpers/{memory.js → memory.cjs} +10 -9
- package/v3/@swarmdo/cli/.claude/helpers/router.cjs +88 -0
- package/v3/@swarmdo/cli/.claude/helpers/session.cjs +125 -0
- package/v3/@swarmdo/cli/dist/src/commands/config.js +5 -4
- package/v3/@swarmdo/cli/dist/src/init/executor.d.ts +15 -0
- package/v3/@swarmdo/cli/dist/src/init/executor.js +24 -1
- package/v3/@swarmdo/cli/dist/src/mcp-tools/agent-tools.js +1 -1
- package/v3/@swarmdo/cli/dist/src/mcp-tools/neural-tools.d.ts +1 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/neural-tools.js +132 -78
- package/v3/@swarmdo/cli/package.json +1 -1
- package/v3/@swarmdo/cli/.claude/helpers/router.js +0 -105
- package/v3/@swarmdo/cli/.claude/helpers/session.js +0 -157
- package/v3/@swarmdo/cli/.claude/helpers/statusline.js +0 -352
|
@@ -2,15 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Swarmdo Hook Handler (Cross-Platform)
|
|
4
4
|
* Dispatches hook events to the appropriate helper modules.
|
|
5
|
-
*
|
|
6
|
-
* Usage: node hook-handler.cjs <command> [args...]
|
|
7
|
-
*
|
|
8
|
-
* Commands:
|
|
9
|
-
* route - Route a task to optimal agent (reads PROMPT from env/stdin)
|
|
10
|
-
* pre-bash - Validate command safety before execution
|
|
11
|
-
* post-edit - Record edit outcome for learning
|
|
12
|
-
* session-restore - Restore previous session state
|
|
13
|
-
* session-end - End session and persist state
|
|
14
5
|
*/
|
|
15
6
|
|
|
16
7
|
const path = require('path');
|
|
@@ -18,9 +9,6 @@ const fs = require('fs');
|
|
|
18
9
|
|
|
19
10
|
const helpersDir = __dirname;
|
|
20
11
|
|
|
21
|
-
// Safe require with stdout suppression - the helper modules have CLI
|
|
22
|
-
// sections that run unconditionally on require(), so we mute console
|
|
23
|
-
// during the require to prevent noisy output.
|
|
24
12
|
function safeRequire(modulePath) {
|
|
25
13
|
try {
|
|
26
14
|
if (fs.existsSync(modulePath)) {
|
|
@@ -42,43 +30,52 @@ function safeRequire(modulePath) {
|
|
|
42
30
|
return null;
|
|
43
31
|
}
|
|
44
32
|
|
|
45
|
-
const router = safeRequire(path.join(helpersDir, 'router.
|
|
46
|
-
const session = safeRequire(path.join(helpersDir, 'session.
|
|
47
|
-
const memory = safeRequire(path.join(helpersDir, 'memory.
|
|
33
|
+
const router = safeRequire(path.join(helpersDir, 'router.cjs'));
|
|
34
|
+
const session = safeRequire(path.join(helpersDir, 'session.cjs'));
|
|
35
|
+
const memory = safeRequire(path.join(helpersDir, 'memory.cjs'));
|
|
48
36
|
const intelligence = safeRequire(path.join(helpersDir, 'intelligence.cjs'));
|
|
37
|
+
const agentBridge = safeRequire(path.join(helpersDir, 'agent-bridge-hook.cjs'));
|
|
49
38
|
|
|
50
39
|
// ── Intelligence timeout protection (fixes #1530, #1531) ───────────────────
|
|
51
|
-
|
|
52
|
-
// Race the (possibly-async) work against a real timeout.
|
|
53
|
-
// called fn() and clearTimeout(timer) immediately, so an async fn returned a
|
|
54
|
-
// pending promise that resolved THROUGH the race — the timeout protected
|
|
55
|
-
// nothing. This settles on whichever finishes first, then clears the timer.
|
|
40
|
+
var INTELLIGENCE_TIMEOUT_MS = 3000;
|
|
41
|
+
// Race the (possibly-async) work against a real timeout.
|
|
56
42
|
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
43
|
+
// The previous implementation called `fn()` and then `clearTimeout(timer)`
|
|
44
|
+
// immediately — but if `fn()` returns a *pending* promise (async work), the
|
|
45
|
+
// timer was cancelled before the work settled and the promise resolved with the
|
|
46
|
+
// still-pending promise, so the timeout protected nothing. This version only
|
|
47
|
+
// settles when the work resolves OR the timeout fires, whichever comes first,
|
|
48
|
+
// and clears the timer afterwards.
|
|
49
|
+
//
|
|
50
|
+
// LIMITATION (be honest): a *synchronous* CPU-bound `fn` (e.g. the current
|
|
51
|
+
// intelligence.init(), which does blocking fs reads) cannot be interrupted by
|
|
52
|
+
// any in-process timer — the event loop is blocked, so the timeout callback
|
|
53
|
+
// can't run until `fn` already returned. The real guard for that case lives in
|
|
54
|
+
// intelligence.cjs (MAX_DATA_FILE_SIZE / MAX_GRAPH_NODES caps). This util only
|
|
55
|
+
// bounds work that yields to the event loop (async I/O, dynamic import, etc.).
|
|
61
56
|
function runWithTimeout(fn, label) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
timer = setTimeout(()
|
|
57
|
+
var timer;
|
|
58
|
+
var timeout = new Promise(function(resolve) {
|
|
59
|
+
timer = setTimeout(function() {
|
|
65
60
|
process.stderr.write("[WARN] " + label + " timed out after " + INTELLIGENCE_TIMEOUT_MS + "ms, skipping\n");
|
|
66
61
|
resolve(null);
|
|
67
62
|
}, INTELLIGENCE_TIMEOUT_MS);
|
|
68
63
|
});
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
// Promise.resolve().then(fn) turns a synchronous throw into a rejection so it
|
|
65
|
+
// is handled here rather than escaping the Promise constructor.
|
|
66
|
+
var work = Promise.resolve().then(fn).catch(function() { return null; });
|
|
67
|
+
return Promise.race([work, timeout]).then(function(result) {
|
|
71
68
|
clearTimeout(timer);
|
|
72
69
|
return result;
|
|
73
70
|
});
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
|
|
77
|
-
// Get the command from argv
|
|
78
74
|
const [,, command, ...args] = process.argv;
|
|
79
75
|
|
|
80
|
-
// Read stdin
|
|
81
|
-
//
|
|
76
|
+
// Read stdin — Claude Code sends hook data as JSON via stdin
|
|
77
|
+
// Uses a timeout to prevent hanging when stdin is in an ambiguous state
|
|
78
|
+
// (not TTY, not a proper pipe) which happens with Claude Code hook invocations.
|
|
82
79
|
async function readStdin() {
|
|
83
80
|
if (process.stdin.isTTY) return '';
|
|
84
81
|
return new Promise((resolve) => {
|
|
@@ -98,11 +95,11 @@ async function readStdin() {
|
|
|
98
95
|
|
|
99
96
|
async function main() {
|
|
100
97
|
// Global safety timeout: hooks must NEVER hang (#1530, #1531)
|
|
101
|
-
|
|
98
|
+
var safetyTimer = setTimeout(function() {
|
|
102
99
|
process.stderr.write("[WARN] Hook handler global timeout (5s), forcing exit\n");
|
|
103
100
|
process.exit(0);
|
|
104
101
|
}, 5000);
|
|
105
|
-
safetyTimer.unref();
|
|
102
|
+
safetyTimer.unref();
|
|
106
103
|
|
|
107
104
|
let stdinData = '';
|
|
108
105
|
try { stdinData = await readStdin(); } catch (e) { /* ignore stdin errors */ }
|
|
@@ -112,21 +109,21 @@ async function main() {
|
|
|
112
109
|
try { hookInput = JSON.parse(stdinData); } catch (e) { /* ignore parse errors */ }
|
|
113
110
|
}
|
|
114
111
|
|
|
112
|
+
// Merge stdin data into prompt resolution: prefer stdin fields, then env vars.
|
|
113
|
+
// NEVER fall back to argv args — shell glob expansion of braces in bash output
|
|
114
|
+
// creates junk files (#1342). Use env vars or stdin only.
|
|
115
115
|
// Normalize snake_case/camelCase: Claude Code sends tool_input/tool_name (snake_case)
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
var toolInput = hookInput.toolInput || hookInput.tool_input || {};
|
|
117
|
+
var toolName = hookInput.toolName || hookInput.tool_name || '';
|
|
118
118
|
|
|
119
|
-
//
|
|
120
|
-
// `
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const prompt = hookInput.prompt || hookInput.command || toolInput.command
|
|
125
|
-
|| process.env.PROMPT || process.env.TOOL_INPUT_command || args.join(' ') || '';
|
|
119
|
+
// `toolInput` is an object (e.g. {command:"ls"}) — falling back to it
|
|
120
|
+
// directly bound `prompt` to the object and tripped `.toLowerCase()` /
|
|
121
|
+
// `.substring()` on every Bash hook (#1944). Use the `.command` field.
|
|
122
|
+
var prompt = hookInput.prompt || hookInput.command || toolInput.command
|
|
123
|
+
|| process.env.PROMPT || process.env.TOOL_INPUT_command || '';
|
|
126
124
|
|
|
127
125
|
const handlers = {
|
|
128
126
|
'route': () => {
|
|
129
|
-
// Inject ranked intelligence context before routing
|
|
130
127
|
if (intelligence && intelligence.getContext) {
|
|
131
128
|
try {
|
|
132
129
|
const ctx = intelligence.getContext(prompt);
|
|
@@ -135,33 +132,49 @@ const handlers = {
|
|
|
135
132
|
}
|
|
136
133
|
if (router && router.routeTask) {
|
|
137
134
|
const result = router.routeTask(prompt);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
'+--------------------------------------------------------------+',
|
|
147
|
-
];
|
|
135
|
+
var output = [];
|
|
136
|
+
output.push('[INFO] Routing task: ' + (prompt.substring(0, 80) || '(no prompt)'));
|
|
137
|
+
output.push('');
|
|
138
|
+
output.push('+------------------- Primary Recommendation -------------------+');
|
|
139
|
+
output.push('| Agent: ' + result.agent.padEnd(53) + '|');
|
|
140
|
+
output.push('| Confidence: ' + (result.confidence * 100).toFixed(1) + '%' + ' '.repeat(44) + '|');
|
|
141
|
+
output.push('| Reason: ' + result.reason.substring(0, 53).padEnd(53) + '|');
|
|
142
|
+
output.push('+--------------------------------------------------------------+');
|
|
148
143
|
console.log(output.join('\n'));
|
|
149
144
|
} else {
|
|
150
145
|
console.log('[INFO] Router not available, using default routing');
|
|
151
146
|
}
|
|
147
|
+
// Agentic prompts → suggest spawning a swarm, and which roles. Swarmdo
|
|
148
|
+
// cannot call Claude Code's Agent tool itself, so this advisory is the only
|
|
149
|
+
// way it can ask. Fully guarded — never breaks the prompt.
|
|
150
|
+
//
|
|
151
|
+
// Deliberately does NOT mention `agent bridge register` (#108): the
|
|
152
|
+
// SubagentStart hook registers every subagent on spawn, so telling the main
|
|
153
|
+
// agent to register by hand is both wrong and redundant work. This fires on
|
|
154
|
+
// every agentic prompt, so it is kept to one line — the three lines of
|
|
155
|
+
// manual-registration instructions it replaced were pure token cost.
|
|
156
|
+
try {
|
|
157
|
+
if (router && router.classifyAgentic) {
|
|
158
|
+
const intent = router.classifyAgentic(prompt);
|
|
159
|
+
if (intent.requiresAgents) {
|
|
160
|
+
const roles = intent.roles.join(', ');
|
|
161
|
+
console.log('');
|
|
162
|
+
console.log('[SWARMDO] Agentic task — consider spawning agents: ' + roles
|
|
163
|
+
+ ' (auto-registered on spawn; see `swarmdo swarm status`)');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch (e) { /* advisory only — never break the prompt */ }
|
|
152
167
|
},
|
|
153
168
|
|
|
154
169
|
'pre-bash': () => {
|
|
155
|
-
// Basic command safety check — prefer stdin command data from Claude Code.
|
|
156
170
|
// String() wrap is belt-and-suspenders for #2017: even if a future regression
|
|
157
171
|
// re-binds `prompt` or `hookInput.command` to a non-string, `.toLowerCase()`
|
|
158
|
-
// can no longer throw a TypeError that the global try/catch would swallow
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
console.error(`[BLOCKED] Dangerous command detected: ${d}`);
|
|
172
|
+
// can no longer throw a TypeError that the global try/catch would swallow.
|
|
173
|
+
var cmd = String(hookInput.command || toolInput.command || prompt || '').toLowerCase();
|
|
174
|
+
var dangerous = ['rm -rf /', 'format c:', 'del /s /q c:\\', ':(){:|:&};:'];
|
|
175
|
+
for (var i = 0; i < dangerous.length; i++) {
|
|
176
|
+
if (cmd.includes(dangerous[i])) {
|
|
177
|
+
console.error('[BLOCKED] Dangerous command detected: ' + dangerous[i]);
|
|
165
178
|
process.exit(1);
|
|
166
179
|
}
|
|
167
180
|
}
|
|
@@ -169,14 +182,12 @@ const handlers = {
|
|
|
169
182
|
},
|
|
170
183
|
|
|
171
184
|
'post-edit': () => {
|
|
172
|
-
// Record edit for session metrics
|
|
173
185
|
if (session && session.metric) {
|
|
174
186
|
try { session.metric('edits'); } catch (e) { /* no active session */ }
|
|
175
187
|
}
|
|
176
|
-
// Record edit for intelligence consolidation — prefer stdin data from Claude Code
|
|
177
188
|
if (intelligence && intelligence.recordEdit) {
|
|
178
189
|
try {
|
|
179
|
-
|
|
190
|
+
var file = hookInput.file_path || toolInput.file_path
|
|
180
191
|
|| process.env.TOOL_INPUT_file_path || args[0] || '';
|
|
181
192
|
intelligence.recordEdit(file);
|
|
182
193
|
} catch (e) { /* non-fatal */ }
|
|
@@ -186,43 +197,31 @@ const handlers = {
|
|
|
186
197
|
|
|
187
198
|
'session-restore': async () => {
|
|
188
199
|
if (session) {
|
|
189
|
-
|
|
190
|
-
const existing = session.restore && session.restore();
|
|
200
|
+
var existing = session.restore && session.restore();
|
|
191
201
|
if (!existing) {
|
|
192
202
|
session.start && session.start();
|
|
193
203
|
}
|
|
194
204
|
} else {
|
|
195
|
-
|
|
196
|
-
const sessionId = `session-${Date.now()}`;
|
|
197
|
-
console.log(`[INFO] Restoring session: %SESSION_ID%`);
|
|
198
|
-
console.log('');
|
|
199
|
-
console.log(`[OK] Session restored from %SESSION_ID%`);
|
|
200
|
-
console.log(`New session ID: ${sessionId}`);
|
|
201
|
-
console.log('');
|
|
202
|
-
console.log('Restored State');
|
|
203
|
-
console.log('+----------------+-------+');
|
|
204
|
-
console.log('| Item | Count |');
|
|
205
|
-
console.log('+----------------+-------+');
|
|
206
|
-
console.log('| Tasks | 0 |');
|
|
207
|
-
console.log('| Agents | 0 |');
|
|
208
|
-
console.log('| Memory Entries | 0 |');
|
|
209
|
-
console.log('+----------------+-------+');
|
|
205
|
+
console.log('[OK] Session restored: session-' + Date.now());
|
|
210
206
|
}
|
|
211
|
-
// Initialize intelligence
|
|
207
|
+
// Initialize intelligence (with timeout — #1530)
|
|
212
208
|
if (intelligence && intelligence.init) {
|
|
213
|
-
|
|
209
|
+
var initResult = await runWithTimeout(function() { return intelligence.init(); }, 'intelligence.init()');
|
|
214
210
|
if (initResult && initResult.nodes > 0) {
|
|
215
|
-
console.log(
|
|
211
|
+
console.log('[INTELLIGENCE] Loaded ' + initResult.nodes + ' patterns, ' + initResult.edges + ' edges');
|
|
216
212
|
}
|
|
217
213
|
}
|
|
218
214
|
},
|
|
219
215
|
|
|
220
216
|
'session-end': async () => {
|
|
221
|
-
// Consolidate intelligence
|
|
217
|
+
// Consolidate intelligence (with timeout — #1530)
|
|
222
218
|
if (intelligence && intelligence.consolidate) {
|
|
223
|
-
|
|
219
|
+
var consResult = await runWithTimeout(function() { return intelligence.consolidate(); }, 'intelligence.consolidate()');
|
|
224
220
|
if (consResult && consResult.entries > 0) {
|
|
225
|
-
|
|
221
|
+
var msg = '[INTELLIGENCE] Consolidated: ' + consResult.entries + ' entries, ' + consResult.edges + ' edges';
|
|
222
|
+
if (consResult.newEntries > 0) msg += ', ' + consResult.newEntries + ' new';
|
|
223
|
+
msg += ', PageRank recomputed';
|
|
224
|
+
console.log(msg);
|
|
226
225
|
}
|
|
227
226
|
}
|
|
228
227
|
if (session && session.end) {
|
|
@@ -236,17 +235,15 @@ const handlers = {
|
|
|
236
235
|
if (session && session.metric) {
|
|
237
236
|
try { session.metric('tasks'); } catch (e) { /* no active session */ }
|
|
238
237
|
}
|
|
239
|
-
// Route the task if router is available
|
|
240
238
|
if (router && router.routeTask && prompt) {
|
|
241
|
-
|
|
242
|
-
console.log(
|
|
239
|
+
var result = router.routeTask(prompt);
|
|
240
|
+
console.log('[INFO] Task routed to: ' + result.agent + ' (confidence: ' + result.confidence + ')');
|
|
243
241
|
} else {
|
|
244
242
|
console.log('[OK] Task started');
|
|
245
243
|
}
|
|
246
244
|
},
|
|
247
245
|
|
|
248
246
|
'post-task': () => {
|
|
249
|
-
// Implicit success feedback for intelligence
|
|
250
247
|
if (intelligence && intelligence.feedback) {
|
|
251
248
|
try {
|
|
252
249
|
intelligence.feedback(true);
|
|
@@ -255,6 +252,50 @@ const handlers = {
|
|
|
255
252
|
console.log('[OK] Task completed');
|
|
256
253
|
},
|
|
257
254
|
|
|
255
|
+
'compact-manual': () => {
|
|
256
|
+
console.log('PreCompact Guidance:');
|
|
257
|
+
console.log('IMPORTANT: Review CLAUDE.md in project root for:');
|
|
258
|
+
console.log(' - Available agents and concurrent usage patterns');
|
|
259
|
+
console.log(' - Swarm coordination strategies (hierarchical, mesh, adaptive)');
|
|
260
|
+
console.log(' - Critical concurrent execution rules (1 MESSAGE = ALL OPERATIONS)');
|
|
261
|
+
console.log('Ready for compact operation');
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
'compact-auto': () => {
|
|
265
|
+
console.log('Auto-Compact Guidance (Context Window Full):');
|
|
266
|
+
console.log('CRITICAL: Before compacting, ensure you understand:');
|
|
267
|
+
console.log(' - All agents available in .claude/agents/ directory');
|
|
268
|
+
console.log(' - Concurrent execution patterns from CLAUDE.md');
|
|
269
|
+
console.log(' - Swarm coordination strategies for complex tasks');
|
|
270
|
+
console.log('Apply GOLDEN RULE: Always batch operations in single messages');
|
|
271
|
+
console.log('Auto-compact proceeding with full agent context');
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
'status': () => {
|
|
275
|
+
console.log('[OK] Status check');
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
// SubagentStart — bind the spawned Claude Code subagent into Swarmdo's
|
|
279
|
+
// canonical registry so `swarmdo agent list` and the statusline's
|
|
280
|
+
// `🐝 Swarms N 🤖 Agents M` reflect agents that actually exist (#108).
|
|
281
|
+
// Registering also auto-forms the swarm, so both counters move.
|
|
282
|
+
'agent-register': () => {
|
|
283
|
+
if (!agentBridge) return;
|
|
284
|
+
if (!agentBridge.isSubagentEvent(hookInput)) return; // main-thread call
|
|
285
|
+
var ok = agentBridge.register(hookInput);
|
|
286
|
+
console.log(ok
|
|
287
|
+
? '[OK] Bridged subagent ' + (hookInput.agent_type || 'agent') + ' into Swarmdo'
|
|
288
|
+
: '[WARN] Could not bridge subagent (no installed CLI found)');
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// SubagentStop — mark the bound record terminated so the Agents count
|
|
292
|
+
// decrements. Same deterministic id the register side derived (#108).
|
|
293
|
+
'agent-terminate': () => {
|
|
294
|
+
if (!agentBridge) return;
|
|
295
|
+
if (!agentBridge.isSubagentEvent(hookInput)) return;
|
|
296
|
+
agentBridge.terminate(hookInput);
|
|
297
|
+
},
|
|
298
|
+
|
|
258
299
|
'stats': () => {
|
|
259
300
|
if (intelligence && intelligence.stats) {
|
|
260
301
|
intelligence.stats(args.includes('--json'));
|
|
@@ -264,27 +305,29 @@ const handlers = {
|
|
|
264
305
|
},
|
|
265
306
|
};
|
|
266
307
|
|
|
267
|
-
|
|
268
|
-
if (command && handlers[command]) {
|
|
308
|
+
if (command && handlers[command]) {
|
|
269
309
|
try {
|
|
270
310
|
await Promise.resolve(handlers[command]());
|
|
271
311
|
} catch (e) {
|
|
272
|
-
|
|
273
|
-
console.log(`[WARN] Hook ${command} encountered an error: ${e.message}`);
|
|
312
|
+
console.log('[WARN] Hook ' + command + ' encountered an error: ' + e.message);
|
|
274
313
|
}
|
|
275
314
|
} else if (command) {
|
|
276
|
-
|
|
277
|
-
console.log(`[OK] Hook: ${command}`);
|
|
315
|
+
console.log('[OK] Hook: ' + command);
|
|
278
316
|
} else {
|
|
279
|
-
console.log('Usage: hook-handler.cjs <route|pre-bash|post-edit|session-restore|session-end|pre-task|post-task|stats>');
|
|
317
|
+
console.log('Usage: hook-handler.cjs <route|pre-bash|post-edit|session-restore|session-end|pre-task|post-task|agent-register|agent-terminate|compact-manual|compact-auto|status|stats>');
|
|
280
318
|
}
|
|
281
319
|
}
|
|
282
320
|
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
process.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
321
|
+
// Only dispatch hooks when run directly (node hook-handler.cjs ...). When
|
|
322
|
+
// require()'d by a test, just expose the internals so they can be unit-tested
|
|
323
|
+
// without triggering stdin reads / process.exit.
|
|
324
|
+
if (require.main === module) {
|
|
325
|
+
main().catch(function(e) {
|
|
326
|
+
console.log('[WARN] Hook handler error: ' + e.message);
|
|
327
|
+
}).finally(function() {
|
|
328
|
+
// Ensure clean exit for Claude Code hooks
|
|
329
|
+
process.exit(0);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
module.exports = { runWithTimeout: runWithTimeout, INTELLIGENCE_TIMEOUT_MS: INTELLIGENCE_TIMEOUT_MS };
|