fix(parser): display all hook types in GUI (Stop, PreToolUse, PostToolUse, etc.)#23
Merged
fix(parser): display all hook types in GUI (Stop, PreToolUse, PostToolUse, etc.)#23
Conversation
…as HookMsg
Verified via --debug output and live JSONL inspection:
The actual hook entry format written to JSONL session files is:
{type:"system", subtype:"stop_hook_summary", hookCount:N,
hookInfos:[{command, durationMs}], preventedContinuation, ...}
This is written on EVERY Stop hook invocation (success or failure).
Previous code only rescued type:"progress" data.type:"hook_progress" entries,
which never appear in JSONL files — so hooks were never displayed.
When a Stop hook exits non-zero, Claude also injects a user meta entry:
{type:"user", isMeta:true, message:{content:"Stop hook feedback:\n[cmd]: output"}}
Both are now rescued before the NOISE_ENTRY_TYPES filter:
- system/stop_hook_summary (hookCount > 0) → HookMsg with hook_event:"Stop"
and hook_name extracted from hookInfos[0].command
- system/hook_progress → HookMsg (verbose/stream-json mode)
- user/isMeta "Stop hook feedback:" → HookMsg with parsed command and output
New Entry fields: hookCount, hookInfos, preventedContinuation.
Three new tests: stop_hook_summary rescue, zero-hook drop, feedback entry rescue.
https://claude.ai/code/session_01MK63kGRSmtkGSXCDaqh8p3
…e and all other hook events
Verified from cli.js bundle (v2.1.86):
All non-Stop hook events (PreToolUse, PostToolUse, PostToolUseFailure,
UserPromptSubmit, Notification, SessionStart, etc.) write their results
to the JSONL session file as type:"attachment" entries:
{type:"attachment", uuid, timestamp,
attachment:{type:"hook_success"|"hook_non_blocking_error"|
"hook_blocking_error"|"hook_cancelled",
hookEvent, hookName, toolUseID, ...}}
These were previously dropped because Entry had no attachment field
and the empty-role fallback silently discarded them.
Fix: add attachment: Option<Value> to Entry, rescue attachment entries
where attachment.hookEvent is non-empty as HookMsg. Blocking errors
also surface their error message in the command field.
This covers the full hook surface area:
- system/stop_hook_summary → Stop hooks (already fixed)
- type:attachment + hookEvent → ALL other hook events (this commit)
- system/hook_progress → verbose/stream-json mode hooks
Three new tests: hook_success attachment, hook_blocking_error with message,
non-hook attachment dropped.
https://claude.ai/code/session_01MK63kGRSmtkGSXCDaqh8p3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Verified the actual JSONL formats written by Claude Code v2.1.86 (
--debug+ live session inspection +cli.jsbundle analysis). Hooks were never displaying because the parser was looking for the wrong entry formats.Root causes found
Stop hooks →
system/stop_hook_summaryClaude Code writes a
stop_hook_summaryentry after every Stop hook invocation (success or failure):{"type":"system","subtype":"stop_hook_summary","hookCount":1, "hookInfos":[{"command":"~/.claude/stop-hook-git-check.sh","durationMs":59}]}This was silently dropped —
"system"is inNOISE_ENTRY_TYPES.When a Stop hook blocks (exit ≠ 0), Claude also injects:
{"type":"user","isMeta":true,"message":{"content":"Stop hook feedback:\n[cmd]: error output"}}This fell through to a meta AI message rather than being shown as a hook.
All other hooks →
type:"attachment"entriesPreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, Notification, SessionStart, etc. write their results as attachment entries:
{"type":"attachment","uuid":"...","timestamp":"...", "attachment":{"type":"hook_success"|"hook_non_blocking_error"|"hook_blocking_error"|"hook_cancelled", "hookEvent":"PreToolUse","hookName":"my-hook","toolUseID":"..."}}Entryhad noattachmentfield, so the hook data was silently discarded and the entry dropped by the empty-role fallback.Changes
entry.rs: Addhook_count,hook_infos,prevented_continuation,attachmentfieldsclassify.rs: Rescue before the noise filter:system/stop_hook_summary(hookCount > 0) →HookMsgwithhook_event:"Stop"and hook name fromhookInfos[0].commandsystem/hook_progress→HookMsg(verbose/stream-json mode)userisMeta"Stop hook feedback:"→HookMsgwith parsed command + error textattachmentwithhookEvent→HookMsgfor all non-Stop hook eventsTest plan
cargo test)stop_hook_summary, zero-hook drop, feedback entry,hook_successattachment,hook_blocking_errorwith message, non-hook attachment droppedhttps://claude.ai/code/session_01MK63kGRSmtkGSXCDaqh8p3