The Claude Code Dispatch skill relies on Claude Code's hook system to automatically notify you when tasks complete.
| Event | Purpose |
|---|---|
Stop |
Primary: fires when Claude finishes responding |
TaskCompleted |
Enhanced: fires when a task is explicitly marked complete (Agent Teams) |
SessionEnd |
Fallback: fires when session terminates |
The notify-agi.sh script handles all three events with built-in deduplication (.hook-lock, 30s window).
mkdir -p ~/.claude/hooks
cp scripts/notify-agi.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/notify-agi.shEdit ~/.claude/settings.json and add the hooks configuration:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/home/ubuntu/.claude/hooks/notify-agi.sh"
}
]
}
],
"TaskCompleted": [
{
"hooks": [
{
"type": "command",
"command": "/home/ubuntu/.claude/hooks/notify-agi.sh"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "/home/ubuntu/.claude/hooks/notify-agi.sh"
}
]
}
]
}
}# Check settings
cat ~/.claude/settings.json | jq '.hooks'
# Test a dispatch
nohup bash scripts/dispatch.sh \
-p "echo hello world" \
-n "test-hook" \
--permission-mode bypassPermissions \
> /tmp/test-dispatch.log 2>&1 &
# Watch the hook log
tail -f data/claude-code-results/hook.logAll events receive JSON with these common fields:
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "bypassPermissions",
"hook_event_name": "Stop"
}- Fires when Claude finishes a response turn
- Most common trigger for task completion
- No matcher support (fires on every stop)
- Fires when a task is explicitly marked as completed
- More precise than Stop for Agent Teams workflows
- Can be blocked (exit code 2) to prevent premature completion
- No matcher support
- Fires when session terminates
- Matchers:
clear,logout,prompt_input_exit,bypass_permissions_disabled,other - Used as fallback; deduplication prevents double notifications
Instead of a shell script, you can send task completion events to an HTTP endpoint:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/task-complete",
"timeout": 30,
"headers": {
"Authorization": "Bearer $WEBHOOK_TOKEN"
},
"allowedEnvVars": ["WEBHOOK_TOKEN"]
}
]
}
]
}
}HTTP hooks receive the same JSON as the POST body. Non-2xx responses are non-blocking errors.
Hooks can also be defined in the SKILL.md frontmatter (scoped to skill lifetime):
---
name: my-dispatch-task
hooks:
Stop:
- hooks:
- type: command
command: "/path/to/notify-agi.sh"
TaskCompleted:
- hooks:
- type: command
command: "/path/to/notify-agi.sh"
---The hook script uses a .hook-lock file to prevent double notifications:
- Stop and SessionEnd both fire on normal completion
- Only the first event within 30s is processed
- The lock file is in
data/claude-code-results/.hook-lock
- Check
~/.claude/settings.jsonhas valid JSON:jq . ~/.claude/settings.json - Hooks are snapshot at session start — restart Claude Code after config changes
- Check
data/claude-code-results/hook.logfor errors
- PTY mode: hook reads from
task-output.txt, not stdin - Hook sleeps 1s to wait for
teepipe flush - Check
data/claude-code-results/task-output.txtexists and has content
- Check
openclawbinary is accessible:which openclaw - Verify group ID:
openclaw message send --channel telegram --target "<group_id>" --message "test" - Check
task-meta.jsonhas validtelegram_group
- Meta file age check: >2h old meta is ignored
- Session ID mismatch: meta session_id must match current session
- Clear stale meta:
rm data/claude-code-results/task-meta.json