fix(hooks): fire UserPromptSubmitHooks on every prompt, not just first (#594)
This commit is contained in:
parent
8e92704316
commit
d1659152bc
107
src/hooks/claude-code-hooks/user-prompt-submit.test.ts
Normal file
107
src/hooks/claude-code-hooks/user-prompt-submit.test.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { describe, it, expect } from "bun:test"
|
||||||
|
import {
|
||||||
|
executeUserPromptSubmitHooks,
|
||||||
|
type UserPromptSubmitContext,
|
||||||
|
} from "./user-prompt-submit"
|
||||||
|
|
||||||
|
describe("executeUserPromptSubmitHooks", () => {
|
||||||
|
it("returns early when no config provided", async () => {
|
||||||
|
// given
|
||||||
|
const ctx: UserPromptSubmitContext = {
|
||||||
|
sessionId: "test-session",
|
||||||
|
prompt: "test prompt",
|
||||||
|
parts: [{ type: "text", text: "test prompt" }],
|
||||||
|
cwd: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await executeUserPromptSubmitHooks(ctx, null)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.block).toBe(false)
|
||||||
|
expect(result.messages).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns early when hook tags present in user input", async () => {
|
||||||
|
// given
|
||||||
|
const ctx: UserPromptSubmitContext = {
|
||||||
|
sessionId: "test-session",
|
||||||
|
prompt: "<user-prompt-submit-hook>previous output</user-prompt-submit-hook>",
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: "<user-prompt-submit-hook>previous output</user-prompt-submit-hook>",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cwd: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await executeUserPromptSubmitHooks(ctx, null)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.block).toBe(false)
|
||||||
|
expect(result.messages).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("does not return early when hook tags in prompt but not in user input", async () => {
|
||||||
|
// given - simulates case where hook output was injected into session context
|
||||||
|
// but current user input does not contain tags
|
||||||
|
const ctx: UserPromptSubmitContext = {
|
||||||
|
sessionId: "test-session",
|
||||||
|
prompt:
|
||||||
|
"<user-prompt-submit-hook>previous output</user-prompt-submit-hook>\n\nuser message",
|
||||||
|
parts: [{ type: "text", text: "user message" }],
|
||||||
|
cwd: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await executeUserPromptSubmitHooks(ctx, null)
|
||||||
|
|
||||||
|
// then - should not return early, should continue to config check
|
||||||
|
expect(result.block).toBe(false)
|
||||||
|
expect(result.messages).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fire on first prompt", async () => {
|
||||||
|
// given
|
||||||
|
const ctx: UserPromptSubmitContext = {
|
||||||
|
sessionId: "test-session-1",
|
||||||
|
prompt: "first prompt",
|
||||||
|
parts: [{ type: "text", text: "first prompt" }],
|
||||||
|
cwd: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await executeUserPromptSubmitHooks(ctx, null)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.block).toBe(false)
|
||||||
|
expect(result.messages).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fire on second prompt in same session", async () => {
|
||||||
|
// given
|
||||||
|
const ctx1: UserPromptSubmitContext = {
|
||||||
|
sessionId: "test-session-2",
|
||||||
|
prompt: "first prompt",
|
||||||
|
parts: [{ type: "text", text: "first prompt" }],
|
||||||
|
cwd: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx2: UserPromptSubmitContext = {
|
||||||
|
sessionId: "test-session-2",
|
||||||
|
prompt: "second prompt",
|
||||||
|
parts: [{ type: "text", text: "second prompt" }],
|
||||||
|
cwd: "/tmp",
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result1 = await executeUserPromptSubmitHooks(ctx1, null)
|
||||||
|
const result2 = await executeUserPromptSubmitHooks(ctx2, null)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result1.block).toBe(false)
|
||||||
|
expect(result2.block).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -44,9 +44,16 @@ export async function executeUserPromptSubmitHooks(
|
|||||||
return { block: false, modifiedParts, messages }
|
return { block: false, modifiedParts, messages }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if hook tags are in the current user input only (not in injected context)
|
||||||
|
// by checking only the text parts that were provided in this message
|
||||||
|
const userInputText = ctx.parts
|
||||||
|
.filter((p) => p.type === "text" && p.text)
|
||||||
|
.map((p) => p.text ?? "")
|
||||||
|
.join("\n")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ctx.prompt.includes(USER_PROMPT_SUBMIT_TAG_OPEN) &&
|
userInputText.includes(USER_PROMPT_SUBMIT_TAG_OPEN) &&
|
||||||
ctx.prompt.includes(USER_PROMPT_SUBMIT_TAG_CLOSE)
|
userInputText.includes(USER_PROMPT_SUBMIT_TAG_CLOSE)
|
||||||
) {
|
) {
|
||||||
return { block: false, modifiedParts, messages }
|
return { block: false, modifiedParts, messages }
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user