One-command dispatch of development tasks to Claude Code with automatic Telegram notification on completion. Zero polling, zero token waste.
An OpenClaw skill that wraps Claude Code CLI into a fire-and-forget workflow: dispatch a task, walk away, get notified when it's done.
- Fire & Forget β
nohupdispatch, automatic callback via Stop Hook - Agent Teams β Multi-agent parallel development with dedicated Testing Agent via structured
--agentsJSON - Cost Controls β
--max-budget-usdspend cap +--max-turnslimit +--fallback-modelfor overload resilience - Git Worktree Isolation β
--worktreefor parallel tasks in isolated branches - Custom Subagents β Define specialized agents (security reviewer, testing agent, etc.) via
--agents-json - Auto-Callback β Group notifications, DM callbacks, webhook wake events
- Rich Notifications β Task status, duration, test results, file tree β all in one Telegram message
- PTY Wrapper β Reliable execution even in non-TTY environments (CI, exec, cron)
- MCP Integration β Load MCP servers for tasks via
--mcp-config - System Prompt Customization β
--append-system-prompt/--append-system-prompt-file
dispatch.sh
β write task-meta.json
β launch Claude Code via claude_code_run.py (PTY)
β [Agent Teams: --agents JSON defines Testing Agent + custom subagents]
β Claude Code finishes β Stop/TaskCompleted hook fires automatically
β notify-agi.sh reads meta + output
β writes latest.json
β sends Telegram notification
β writes pending-wake.json (heartbeat fallback)
- OpenClaw installed and configured
- Claude Code CLI (
claude) installed - A Telegram bot configured in OpenClaw (for notifications)
Copy the skill into your OpenClaw skills directory:
cp -r claude-code-dispatch ~/.openclaw/skills/
# Or symlink
ln -s /path/to/claude-code-dispatch ~/.openclaw/skills/claude-code-dispatchSet up the Stop Hook (see Hook Setup):
mkdir -p ~/.claude/hooks
cp scripts/notify-agi.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/notify-agi.shConfigure hooks in ~/.claude/settings.json:
{
"hooks": {
"Stop": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify-agi.sh" }] }],
"TaskCompleted": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify-agi.sh" }] }],
"SessionEnd": [{ "hooks": [{ "type": "command", "command": "~/.claude/hooks/notify-agi.sh" }] }]
}
}nohup + background (&) β dispatch runs until Claude Code finishes (minutes to hours).
# Simple task
nohup bash scripts/dispatch.sh \
-p "Build a Python REST API with FastAPI" \
-n "my-api" \
-g "-5006066016" \
--permission-mode bypassPermissions \
--workdir /home/ubuntu/projects/my-api \
> /tmp/dispatch-my-api.log 2>&1 &
# With Agent Teams (parallel dev + testing)
nohup bash scripts/dispatch.sh \
-p "Build a Python REST API with FastAPI" \
-n "my-api" \
--agent-teams \
--permission-mode bypassPermissions \
--workdir /home/ubuntu/projects/my-api \
> /tmp/dispatch-my-api.log 2>&1 &
# With cost controls + fallback
nohup bash scripts/dispatch.sh \
-p "Refactor the auth module" \
-n "auth-refactor" \
--max-budget-usd 5.00 \
--max-turns 50 \
--fallback-model sonnet \
--permission-mode bypassPermissions \
--workdir /home/ubuntu/projects/my-app \
> /tmp/dispatch-auth.log 2>&1 &
# With git worktree isolation
nohup bash scripts/dispatch.sh \
-p "Implement feature X" \
-n "feature-x" \
--worktree feature-x \
--permission-mode bypassPermissions \
--workdir /home/ubuntu/projects/my-app \
> /tmp/dispatch-feature.log 2>&1 &| Param | Short | Required | Description |
|---|---|---|---|
--prompt |
-p |
β * | Task description |
--prompt-file |
β * | Read prompt from file | |
--name |
-n |
Task name for tracking | |
--group |
-g |
Telegram group ID for notifications | |
--workdir |
-w |
Working directory (default: cwd) | |
--agent-teams |
Enable Agent Teams mode | ||
--agents-json |
Custom subagent definitions (JSON string) | ||
--teammate-mode |
Display: auto / in-process / tmux |
||
--permission-mode |
bypassPermissions / plan / acceptEdits / default |
||
--allowed-tools |
Tool allowlist (e.g. "Read,Bash") |
||
--disallowed-tools |
Tool denylist | ||
--model |
Model override (sonnet/opus/haiku/full name) | ||
--fallback-model |
Fallback model when primary is overloaded | ||
--max-budget-usd |
Maximum dollar spend before auto-stopping | ||
--max-turns |
Maximum agentic turns before stopping | ||
--worktree |
Git worktree name for isolation | ||
--no-session-persistence |
Don't save session to disk | ||
--append-system-prompt |
Append to default system prompt | ||
--append-system-prompt-file |
Append system prompt from file | ||
--mcp-config |
MCP servers JSON file path | ||
--verbose |
Enable verbose logging | ||
--callback-group |
Telegram group for dispatching agent callback | ||
--callback-dm |
Telegram user ID for DM callback | ||
--callback-account |
Telegram bot account for DM callback |
* One of --prompt or --prompt-file is required.
When --agent-teams is enabled without --agents-json, the dispatch script automatically defines a structured Testing Agent via the --agents CLI flag:
{
"testing-agent": {
"description": "Dedicated testing agent for comprehensive test coverage",
"prompt": "Write and run tests for all code changes...",
"tools": ["Read", "Edit", "Write", "Bash", "Glob", "Grep"],
"model": "sonnet"
}
}This replaces the older prompt-injection approach with Claude Code's native --agents flag, giving the Testing Agent its own context window, tool restrictions, and model selection.
Pass --agents-json to define your own team:
--agents-json '{
"security-reviewer": {
"description": "Reviews code for security vulnerabilities",
"prompt": "You are a security expert. Focus on OWASP top 10...",
"tools": ["Read", "Grep", "Glob"],
"model": "opus"
},
"perf-analyst": {
"description": "Analyzes and optimizes performance",
"prompt": "Profile code and suggest optimizations...",
"tools": ["Read", "Bash", "Grep"],
"model": "sonnet"
}
}'Each subagent is an independent Claude Code process with its own context window, sharing the same filesystem.
Control spending with:
| Flag | Description |
|---|---|
--max-budget-usd 5.00 |
Hard spend cap in dollars |
--max-turns 50 |
Maximum agentic turns |
--fallback-model sonnet |
Auto-switch when primary model is overloaded |
These are especially important for Agent Teams, which consume significantly more tokens.
Use --worktree <name> to run the task in an isolated git worktree:
--worktree feature-auth
# Claude Code runs at <repo>/.claude/worktrees/feature-authThis allows parallel dispatch tasks to work on the same repo without conflicts.
If no --callback-group or --callback-dm is passed, the script looks for dispatch-callback.json in the working directory:
// Group callback
{ "type": "group", "group": "-5189558203" }
// DM callback
{ "type": "dm", "dm": "8009709280", "account": "coding-bot" }
// Wake hook (for main agent)
{ "type": "wake" }The notification hook (notify-agi.sh) handles multiple Claude Code lifecycle events:
| Event | When | Purpose |
|---|---|---|
Stop |
Claude finishes responding | Primary completion signal |
TaskCompleted |
Task explicitly marked done | Precise completion (Agent Teams) |
SessionEnd |
Session terminates | Fallback signal |
Built-in deduplication (.hook-lock, 30s window) prevents double notifications.
HTTP hooks are also supported as an alternative β see Hook Setup.
All results are written to data/claude-code-results/:
| File | Content |
|---|---|
latest.json |
Full result (output, task name, group, timestamp) |
task-meta.json |
Task metadata (prompt, workdir, status, cost params) |
task-output.txt |
Raw Claude Code stdout |
pending-wake.json |
Heartbeat fallback notification |
hook.log |
Hook execution log |
# Watch hook log
tail -f data/claude-code-results/hook.log
# Check latest result
cat data/claude-code-results/latest.json | jq .
# Check task metadata
cat data/claude-code-results/task-meta.json | jq .
# Test Telegram delivery
openclaw message send --channel telegram --target "-5006066016" --message "test"- Must use PTY wrapper β Direct
claude -phangs in exec environments - Hook fires twice β Stop + SessionEnd both trigger;
.hook-lockdeduplicates (30s window) - Hook stdin is empty in PTY β Output is read from
task-output.txt, not stdin - tee pipe race β Hook sleeps 1s to wait for pipe flush before reading output
- Meta freshness β Hook validates meta age (<2h) and session ID to avoid stale notifications
- Agent Teams cost β Multi-agent tasks use significantly more tokens; always use
--max-budget-usd - Rate limits β Claude Code has daily rate limits (reset at 11:00 UTC); the stop hook still fires with
status=done, making it look like success
See Prompt Guide for examples and best practices.
MIT