10e980cd69
* fix: remove unrecognized fields from Claude Code Stop hook output
Claude Code validates Stop hook JSON output against its hook contract
schema which only accepts {decision?, reason?, systemMessage?}. The
formatOutput() function was returning {continue, suppressOutput} which
are not part of the Claude Code hook API, causing "JSON validation
failed" errors on every session stop.
Return an empty object {} for the default case (no hookSpecificOutput),
preserving only systemMessage when present. This is valid for all hook
event types and eliminates the schema validation error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add unhappy-path tests for formatOutput per PR review
Add edge case coverage for malformed input (undefined/null), falsy
systemMessage values, non-contract field stripping, and contract key
allowlist. Also add defensive null guard to formatOutput matching
normalizeInput pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Alex Worland <alexworland@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
36 lines
1.2 KiB
TypeScript
36 lines
1.2 KiB
TypeScript
import type { PlatformAdapter, NormalizedHookInput, HookResult } from '../types.js';
|
|
|
|
// Maps Claude Code stdin format (session_id, cwd, tool_name, etc.)
|
|
// SessionStart hooks receive no stdin, so we must handle undefined input gracefully
|
|
export const claudeCodeAdapter: PlatformAdapter = {
|
|
normalizeInput(raw) {
|
|
const r = (raw ?? {}) as any;
|
|
return {
|
|
sessionId: r.session_id ?? r.id ?? r.sessionId,
|
|
cwd: r.cwd ?? process.cwd(),
|
|
prompt: r.prompt,
|
|
toolName: r.tool_name,
|
|
toolInput: r.tool_input,
|
|
toolResponse: r.tool_response,
|
|
transcriptPath: r.transcript_path,
|
|
};
|
|
},
|
|
formatOutput(result) {
|
|
const r = result ?? ({} as HookResult);
|
|
if (r.hookSpecificOutput) {
|
|
const output: Record<string, unknown> = { hookSpecificOutput: result.hookSpecificOutput };
|
|
if (r.systemMessage) {
|
|
output.systemMessage = r.systemMessage;
|
|
}
|
|
return output;
|
|
}
|
|
// Only emit fields in the Claude Code hook contract — unrecognized fields
|
|
// cause "JSON validation failed" in Stop hooks.
|
|
const output: Record<string, unknown> = {};
|
|
if (r.systemMessage) {
|
|
output.systemMessage = r.systemMessage;
|
|
}
|
|
return output;
|
|
}
|
|
};
|