fix(hooks): add null guard for tool.execute.after output (#1054)
/review command and some Claude Code built-in commands trigger tool.execute.after hooks with undefined output, causing crashes when accessing output.metadata or output.output. Fixes #1035 Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
This commit is contained in:
parent
01500f1ebe
commit
8f6ed5b20f
@ -66,6 +66,20 @@ describe("atlas hook", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("tool.execute.after handler", () => {
|
describe("tool.execute.after handler", () => {
|
||||||
|
test("should handle undefined output gracefully (issue #1035)", async () => {
|
||||||
|
// #given - hook and undefined output (e.g., from /review command)
|
||||||
|
const hook = createAtlasHook(createMockPluginInput())
|
||||||
|
|
||||||
|
// #when - calling with undefined output
|
||||||
|
const result = await hook["tool.execute.after"](
|
||||||
|
{ tool: "delegate_task", sessionID: "session-123" },
|
||||||
|
undefined as unknown as { title: string; output: string; metadata: Record<string, unknown> }
|
||||||
|
)
|
||||||
|
|
||||||
|
// #then - returns undefined without throwing
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
test("should ignore non-delegate_task tools", async () => {
|
test("should ignore non-delegate_task tools", async () => {
|
||||||
// #given - hook and non-delegate_task tool
|
// #given - hook and non-delegate_task tool
|
||||||
const hook = createAtlasHook(createMockPluginInput())
|
const hook = createAtlasHook(createMockPluginInput())
|
||||||
|
|||||||
@ -663,6 +663,11 @@ export function createAtlasHook(
|
|||||||
input: ToolExecuteAfterInput,
|
input: ToolExecuteAfterInput,
|
||||||
output: ToolExecuteAfterOutput
|
output: ToolExecuteAfterOutput
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
|
// Guard against undefined output (e.g., from /review command - see issue #1035)
|
||||||
|
if (!output) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!isCallerOrchestrator(input.sessionID)) {
|
if (!isCallerOrchestrator(input.sessionID)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -237,6 +237,11 @@ export function createClaudeCodeHooksHook(
|
|||||||
input: { tool: string; sessionID: string; callID: string },
|
input: { tool: string; sessionID: string; callID: string },
|
||||||
output: { title: string; output: string; metadata: unknown }
|
output: { title: string; output: string; metadata: unknown }
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
|
// Guard against undefined output (e.g., from /review command - see issue #1035)
|
||||||
|
if (!output) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const claudeConfig = await loadClaudeHooksConfig()
|
const claudeConfig = await loadClaudeHooksConfig()
|
||||||
const extendedConfig = await loadPluginExtendedConfig()
|
const extendedConfig = await loadPluginExtendedConfig()
|
||||||
|
|
||||||
|
|||||||
@ -657,6 +657,10 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"tool.execute.after": async (input, output) => {
|
"tool.execute.after": async (input, output) => {
|
||||||
|
// Guard against undefined output (e.g., from /review command - see issue #1035)
|
||||||
|
if (!output) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await claudeCodeHooks["tool.execute.after"](input, output);
|
await claudeCodeHooks["tool.execute.after"](input, output);
|
||||||
await toolOutputTruncator?.["tool.execute.after"](input, output);
|
await toolOutputTruncator?.["tool.execute.after"](input, output);
|
||||||
await contextWindowMonitor?.["tool.execute.after"](input, output);
|
await contextWindowMonitor?.["tool.execute.after"](input, output);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user