diff --git a/hooks/hooks.json b/hooks/hooks.json index 4c912b7..eda5fcd 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -12,6 +12,7 @@ ], "SessionStart": [ { + "matcher": "compact", "hooks": [ { "type": "command", diff --git a/src/hook.ts b/src/hook.ts index 8bf7133..906c459 100644 --- a/src/hook.ts +++ b/src/hook.ts @@ -52,7 +52,9 @@ export async function hookPreCompact(): Promise { if (!input.transcript_path) throw new Error("no transcript_path in hook input"); - log(`PreCompact triggered: trigger=${input.trigger} session=${input.session_id}`); + log( + `PreCompact triggered: trigger=${input.trigger} session=${input.session_id}`, + ); if (!(await Bun.file(input.transcript_path).exists())) { throw new Error(`transcript not found: ${input.transcript_path}`); @@ -73,19 +75,29 @@ export async function hookPreCompact(): Promise { try { const messages = await parseTranscript(input.transcript_path); const inputChars = messages.reduce((n, m) => n + m.content.length, 0); - log(`PreCompact: parsed ${messages.length} messages (${inputChars} chars), calling Morph API...`); + log( + `PreCompact: parsed ${messages.length} messages (${inputChars} chars), calling Morph API...`, + ); const start = performance.now(); const summary = await compact(messages); const durationMs = Math.round(performance.now() - start); - const ratio = inputChars > 0 ? ((summary.length / inputChars) * 100).toFixed(1) : "N/A"; + const ratio = + inputChars > 0 ? ((summary.length / inputChars) * 100).toFixed(1) : "N/A"; - log(`PreCompact: compaction complete in ${durationMs}ms — ${inputChars} → ${summary.length} chars (${ratio}% ratio)`); + log( + `PreCompact: compaction complete in ${durationMs}ms — ${inputChars} → ${summary.length} chars (${ratio}% ratio)`, + ); const state: StateData = { summary, warn: input.trigger === "manual" && !input.custom_instructions, - stats: { messageCount: messages.length, inputChars, outputChars: summary.length, durationMs }, + stats: { + messageCount: messages.length, + inputChars, + outputChars: summary.length, + durationMs, + }, }; await Bun.write(sf, JSON.stringify(state)); @@ -100,17 +112,6 @@ export async function hookPreCompact(): Promise { } } -function emitContext(data: string): void { - console.log( - JSON.stringify({ - hookSpecificOutput: { - hookEventName: "SessionStart", - additionalContext: data, - }, - }), - ); -} - export async function hookSessionStart(): Promise { const input = await readStdin(); if (!input.session_id) throw new Error("no session_id in hook input"); @@ -125,13 +126,17 @@ export async function hookSessionStart(): Promise { } const state = (await file.json()) as StateData; - log(`SessionStart: state=${JSON.stringify({ error: state.error, warn: state.warn, summaryLen: state.summary?.length ?? 0, stats: state.stats })}`); + log( + `SessionStart: state=${JSON.stringify({ error: state.error, warn: state.warn, summaryLen: state.summary?.length ?? 0, stats: state.stats })}`, + ); if (state.error) { log(`SessionStart: injecting error — ${state.error}`); - emitContext( - "ERROR: Morph compaction failed: " + state.error + "\n" + - "Inform the user about this error. Context from the previous conversation was NOT preserved.", + process.stdout.write( + "ERROR: Morph compaction failed: " + + state.error + + "\n" + + "Inform the user about this error. Context from the previous conversation was NOT preserved.", ); await unlink(sf).catch(() => {}); return; @@ -154,12 +159,15 @@ export async function hookSessionStart(): Promise { if (state.stats) { const { messageCount, inputChars, outputChars, durationMs } = state.stats; - const ratio = inputChars > 0 ? ((outputChars / inputChars) * 100).toFixed(1) : "N/A"; - log(`SessionStart: injecting summary — ${messageCount} messages, ${inputChars} → ${outputChars} chars (${ratio}%), took ${durationMs}ms`); + const ratio = + inputChars > 0 ? ((outputChars / inputChars) * 100).toFixed(1) : "N/A"; + log( + `SessionStart: injecting summary — ${messageCount} messages, ${inputChars} → ${outputChars} chars (${ratio}%), took ${durationMs}ms`, + ); } else { log(`SessionStart: injecting summary (${data.length} chars)`); } - emitContext(data); + process.stdout.write(data); await unlink(sf).catch(() => {}); } diff --git a/src/morph.ts b/src/morph.ts index fd4ede2..d248391 100644 --- a/src/morph.ts +++ b/src/morph.ts @@ -21,14 +21,14 @@ function loadApiKey(): string { try { const text = readFileSync(ENV_FILE, "utf-8"); for (const line of text.split("\n")) { - const m = line.match(/^MORPH_API_KEY=(.+)$/); - if (m) return m[1].trim(); + const key = line.match(/^MORPH_API_KEY=(.+)$/)?.[1]; + if (key) return key.trim(); } } catch {} throw new Error( "Morph API key not found. Run /morph-compact:install to configure it, " + - "or set MORPH_API_KEY environment variable.", + "or set MORPH_API_KEY environment variable.", ); }