From ddeabb1a8b99c0a76c29d76bd2071444c900fcbf Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 2 Jan 2026 20:29:01 +0900 Subject: [PATCH] fix(claude-code-hooks): handle UserPromptSubmit on first message properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Allow UserPromptSubmit hooks to run on first message (used for title generation) - For first message: prepend hook content directly to message parts - For subsequent messages: use file system injection as before - Preserves hook injection integrity while enabling title generation hooks 🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode) --- src/hooks/claude-code-hooks/index.ts | 48 +++++++++++++++------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/hooks/claude-code-hooks/index.ts b/src/hooks/claude-code-hooks/index.ts index 954fd73..6c77cf5 100644 --- a/src/hooks/claude-code-hooks/index.ts +++ b/src/hooks/claude-code-hooks/index.ts @@ -112,11 +112,6 @@ export function createClaudeCodeHooksHook(ctx: PluginInput, config: PluginConfig const isFirstMessage = !sessionFirstMessageProcessed.has(input.sessionID) sessionFirstMessageProcessed.add(input.sessionID) - if (isFirstMessage) { - log("Skipping UserPromptSubmit hooks on first message for title generation", { sessionID: input.sessionID }) - return - } - if (!isHookDisabled(config, "UserPromptSubmit")) { const userPromptCtx: UserPromptSubmitContext = { sessionId: input.sessionID, @@ -144,24 +139,33 @@ export function createClaudeCodeHooksHook(ctx: PluginInput, config: PluginConfig if (result.messages.length > 0) { const hookContent = result.messages.join("\n\n") - log(`[claude-code-hooks] Injecting ${result.messages.length} hook messages`, { sessionID: input.sessionID, contentLength: hookContent.length }) - const message = output.message as { - agent?: string - model?: { modelID?: string; providerID?: string } - path?: { cwd?: string; root?: string } - tools?: Record + log(`[claude-code-hooks] Injecting ${result.messages.length} hook messages`, { sessionID: input.sessionID, contentLength: hookContent.length, isFirstMessage }) + + if (isFirstMessage) { + const idx = output.parts.findIndex((p) => p.type === "text" && p.text) + if (idx >= 0) { + output.parts[idx].text = `${hookContent}\n\n${output.parts[idx].text ?? ""}` + log("UserPromptSubmit hooks prepended to first message parts directly", { sessionID: input.sessionID }) + } + } else { + const message = output.message as { + agent?: string + model?: { modelID?: string; providerID?: string } + path?: { cwd?: string; root?: string } + tools?: Record + } + + const success = injectHookMessage(input.sessionID, hookContent, { + agent: message.agent, + model: message.model, + path: message.path ?? { cwd: ctx.directory, root: "/" }, + tools: message.tools, + }) + + log(success ? "Hook message injected via file system" : "File injection failed", { + sessionID: input.sessionID, + }) } - - const success = injectHookMessage(input.sessionID, hookContent, { - agent: message.agent, - model: message.model, - path: message.path ?? { cwd: ctx.directory, root: "/" }, - tools: message.tools, - }) - - log(success ? "Hook message injected via file system" : "File injection failed", { - sessionID: input.sessionID, - }) } } },