Skip to content

Instantly share code, notes, and snippets.

@Leksat
Created June 28, 2026 10:01
Show Gist options
  • Select an option

  • Save Leksat/68918245bad8c548b5a30a2252e1adbd to your computer and use it in GitHub Desktop.

Select an option

Save Leksat/68918245bad8c548b5a30a2252e1adbd to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
# Mirrors the session to a log to work around Claude Code TUI limitations:
# - inability to see full bash output (the ctrl+o expander is unreliable)
# - inability to see web search results (the TUI shows only the query)
# - inability to copy agent replies (/copy grabs too much; manual select drops formatting)
# - inability to copy user prompts (the TUI adds indent and newline artifacts)
log_file="${CLAUDE_LOG:-/tmp/claude.log}"
input="$(cat)"
get() { jq -r "$1" <<<"$input"; }
event=$(get '.hook_event_name // ""')
tool=$(get '.tool_name // ""')
description=$(get '.tool_input.description // ""')
cwd=$(get '.cwd // ""')
cwd="${cwd/#$HOME/~}"
duration=$(get '.duration_ms // ""')
g=$'\033[32m'
r=$'\033[0m'
header() {
printf '\n\n\n%s========================= %s %s %s =========================%s\n' \
"$g" "$1" "$cwd" "$(date '+%Y-%m-%d %H:%M:%S')" "$r"
}
field() {
if [[ $2 == *$'\n'* ]]; then
printf '%s# %s:%s\n%s\n' "$g" "$1" "$r" "$2"
else
printf '%s# %s:%s %s\n' "$g" "$1" "$r" "$2"
fi
}
if [ "$event" = "MessageDisplay" ]; then
msg_buf="/tmp/claude-msg-$(get '.message_id // "unknown"')"
jq -j '.delta // ""' <<<"$input" >>"$msg_buf"
if [ "$(get '.final // false')" = "true" ]; then
{ header "$event"; cat "$msg_buf"; printf '\n'; } >>"$log_file"
rm -f "$msg_buf"
fi
exit 0
fi
if [ "$event" = "UserPromptSubmit" ]; then
{ header "$event"; printf '%s\n' "$(get '.prompt // ""')"; } >>"$log_file"
exit 0
fi
{
header "$tool"
[ -n "$description" ] && field "desc" "$description"
case "$tool" in
Bash)
if [ "$event" = "PostToolUseFailure" ]; then
output=$(get '.error // ""')
else
output=$(get 'if (.tool_response | type) == "string" then .tool_response else (.tool_response.stdout // "") end')
fi
field "command" "$(get '.tool_input.command // ""')"
[ "$(get '.is_interrupt // false')" = "true" ] && field "interrupt" "true"
field "output" "$output"
;;
Read)
field "file" "$(get '.tool_input.file_path // .tool_response.file.filePath // ""')"
field "lines" "$(get 'if .tool_response.file then (.tool_response.file | "\(.startLine)-\(.startLine + .numLines - 1) of \(.totalLines)") else "" end')"
;;
WebSearch)
field "query" "$(get '.tool_input.query // ""')"
field "summary" "$(get '[.tool_response.results[]? | select(type=="string")] | join("\n\n")')"
;;
Glob)
field "glob" "$(get '.tool_input.pattern // ""')"
field "results" "$(get 'if (.tool_response | type) == "string" then .tool_response
elif (.tool_response.filenames? != null) then (.tool_response.filenames | join("\n"))
else (.tool_response | tostring) end')"
;;
*)
field "tool_input" "$(get '.tool_input')"
field "tool_response" "$(get '.tool_response')"
;;
esac
[ -n "$duration" ] && field "duration" "${duration}ms"
} >>"$log_file"
exit 0
{
"PostToolUse": [
{ "matcher": "Bash|Read|WebSearch|Glob",
"hooks": [{ "type": "command", "command": "/path/to/log.sh" }] }
],
"PostToolUseFailure": [
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "/path/to/log.sh" }] }
],
"MessageDisplay": [
{ "hooks": [{ "type": "command", "command": "/path/to/log.sh" }] }
],
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "/path/to/log.sh" }] }
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment