forked from thedotmack/claude-mem
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnew-hook.ts
More file actions
100 lines (80 loc) · 3.6 KB
/
new-hook.ts
File metadata and controls
100 lines (80 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { stdin } from 'process';
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
import { getProjectName } from '../utils/project-name.js';
import { logger } from '../utils/logger.js';
import { fetchWithRetry } from '../shared/fetch-with-retry.js';
export interface UserPromptSubmitInput {
session_id: string;
cwd: string;
prompt: string;
}
/**
* New Hook Main Logic
*/
async function newHook(input?: UserPromptSubmitInput): Promise<void> {
// Ensure worker is running before any other logic
await ensureWorkerRunning();
if (!input) {
throw new Error('newHook requires input');
}
const { session_id, cwd, prompt } = input;
const project = getProjectName(cwd);
logger.info('HOOK', 'new-hook: Received hook input', { session_id, has_prompt: !!prompt, cwd });
const port = getWorkerPort();
logger.info('HOOK', 'new-hook: Calling /api/sessions/init', { contentSessionId: session_id, project, prompt_length: prompt?.length });
// Initialize session via HTTP - handles DB operations and privacy checks
// Uses fetchWithRetry to handle transient ECONNRESET errors during worker restarts
const initResponse = await fetchWithRetry(`http://127.0.0.1:${port}/api/sessions/init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contentSessionId: session_id,
project,
prompt
})
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
});
if (!initResponse.ok) {
throw new Error(`Session initialization failed: ${initResponse.status}`);
}
const initResult = await initResponse.json();
const sessionDbId = initResult.sessionDbId;
const promptNumber = initResult.promptNumber;
logger.info('HOOK', 'new-hook: Received from /api/sessions/init', { sessionDbId, promptNumber, skipped: initResult.skipped });
// Check if prompt was entirely private (worker performs privacy check)
if (initResult.skipped && initResult.reason === 'private') {
logger.info('HOOK', `new-hook: Session ${sessionDbId}, prompt #${promptNumber} (fully private - skipped)`);
console.log(STANDARD_HOOK_RESPONSE);
return;
}
logger.info('HOOK', `new-hook: Session ${sessionDbId}, prompt #${promptNumber}`);
// Strip leading slash from commands for memory agent
// /review 101 → review 101 (more semantic for observations)
const cleanedPrompt = prompt.startsWith('/') ? prompt.substring(1) : prompt;
logger.info('HOOK', 'new-hook: Calling /sessions/{sessionDbId}/init', { sessionDbId, promptNumber, userPrompt_length: cleanedPrompt?.length });
// Initialize SDK agent session via HTTP (starts the agent!)
// Uses fetchWithRetry to handle transient ECONNRESET errors during worker restarts
const response = await fetchWithRetry(`http://127.0.0.1:${port}/sessions/${sessionDbId}/init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userPrompt: cleanedPrompt, promptNumber })
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
});
if (!response.ok) {
throw new Error(`SDK agent start failed: ${response.status}`);
}
console.log(STANDARD_HOOK_RESPONSE);
}
// Entry Point
let input = '';
stdin.on('data', (chunk) => input += chunk);
stdin.on('end', async () => {
let parsed: UserPromptSubmitInput | undefined;
try {
parsed = input ? JSON.parse(input) : undefined;
} catch (error) {
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
}
await newHook(parsed);
});