Problem
Two issues encountered when using dispatch-claude-code.sh + notify-agi.sh:
1. Race condition: hook fires before output is written
The dispatch script runs Claude Code with output piped through tee:
"${CMD[@]}" 2>&1 | tee "$TASK_OUTPUT"
The Stop hook triggers before the tee pipeline has finished flushing to task-output.txt. As a result, notify-agi.sh reads an empty or incomplete file:
# This sleep is a workaround, but unreliable for long outputs
sleep 1
OUTPUT=$(tail -c 4000 "$TASK_OUTPUT")
The sleep 1 helps on fast tasks but fails when the OS delays the flush. The hook ends up sending a notification with an empty summary.
2. ANSI escape codes appear as garbage in notifications
Claude Code emits extensive ANSI control sequences (color codes, cursor movement, OSC terminal title sequences, etc.). When the raw output is forwarded to Telegram or other messaging platforms, these appear as literal escape characters or mojibake.
Example garbage received:
�[1m�[32m✓�[0m �]0;claude� Analyzing files...
Root Cause
Issue 1: The tee child process and the Claude Code process share the same stdout. When the parent process exits and triggers the Stop hook, the tee side of the pipe may not have completed its write to disk yet — especially on systems with slow I/O or when outputs are large.
Issue 2: No output sanitization before forwarding to external services.
Suggested Fix
For the race condition
Send the notification from the dispatch script itself, after the pipeline returns (at which point the file is guaranteed to be fully written), rather than relying on the hook to read the file:
# After pipeline completes — tee is done, file is safe to read
"${CMD[@]}" 2>&1 | tee "$TASK_OUTPUT"
EXIT_CODE=${PIPESTATUS[0]}
# Now send notification directly
OUTPUT=$(tail -c 800 "$TASK_OUTPUT")
# ... clean and send
The hook can still serve as a fallback (e.g., for cases where dispatch is not used), but the primary notification path should be post-pipeline in the dispatch script.
For ANSI cleanup
Strip control sequences before forwarding output:
OUTPUT=$(cat "$TASK_OUTPUT" \
| sed 's/\x1b\[[0-9;]*[mGKHFABCDEFGHJKLMPSTfnrsu]//g' \
| sed 's/\x1b\][^\x07]*\x07//g' \
| sed 's/\x1b\][^\x1b]*\x1b\\//g' \
| tr -cd '[:print:]\n')
Environment
- Claude Code CLI (latest)
- Hook registered for both
Stop and SessionEnd
- Tested on Linux (Ubuntu 24.04)
Impact
Any user relying on notify-agi.sh to deliver task output to Telegram (or similar) will receive empty or garbled summaries. The hook fires correctly, but the output content is lost.
— LJ × Evo ⚡ (human-AI)
Problem
Two issues encountered when using
dispatch-claude-code.sh+notify-agi.sh:1. Race condition: hook fires before output is written
The dispatch script runs Claude Code with output piped through
tee:The Stop hook triggers before the
teepipeline has finished flushing totask-output.txt. As a result,notify-agi.shreads an empty or incomplete file:The
sleep 1helps on fast tasks but fails when the OS delays the flush. The hook ends up sending a notification with an empty summary.2. ANSI escape codes appear as garbage in notifications
Claude Code emits extensive ANSI control sequences (color codes, cursor movement, OSC terminal title sequences, etc.). When the raw output is forwarded to Telegram or other messaging platforms, these appear as literal escape characters or mojibake.
Example garbage received:
Root Cause
Issue 1: The
teechild process and the Claude Code process share the same stdout. When the parent process exits and triggers the Stop hook, theteeside of the pipe may not have completed its write to disk yet — especially on systems with slow I/O or when outputs are large.Issue 2: No output sanitization before forwarding to external services.
Suggested Fix
For the race condition
Send the notification from the dispatch script itself, after the pipeline returns (at which point the file is guaranteed to be fully written), rather than relying on the hook to read the file:
The hook can still serve as a fallback (e.g., for cases where dispatch is not used), but the primary notification path should be post-pipeline in the dispatch script.
For ANSI cleanup
Strip control sequences before forwarding output:
Environment
StopandSessionEndImpact
Any user relying on
notify-agi.shto deliver task output to Telegram (or similar) will receive empty or garbled summaries. The hook fires correctly, but the output content is lost.— LJ × Evo ⚡ (human-AI)