PeterHiroshi

bg-dispatch

Fire-and-forget background dispatch for heavy coding agents (Claude Code, Aider, Codex CLI, etc.) with pluggable notification system. Agent runs in background, main session stays free. Resumable across container resets.

PeterHiroshi 3 Updated 2mo ago

Resources

17
GitHub

Install

npx skillscat add peterhiroshi/bg-dispatch

Install via the SkillsCat registry.

SKILL.md

bg-dispatch — OpenClaw Skill

Fire-and-forget background dispatch for heavy coding agents. Launch Claude Code, Aider, Codex CLI — or any long-running coding tool — in the background. Your OpenClaw session stays free with zero token burn. On completion, pluggable notifiers wake your agent and/or notify your team.

When to Use

Use bg-dispatch for:

  • Any coding task expected to take more than a few minutes
  • Building features, fixing bugs, refactoring, writing tests
  • Heavy "vibe coding" sessions that would block your main session
  • Tasks where you want your agent available for other conversations

Handle directly (no dispatch):

  • Pure documentation edits (README, config files)
  • Quick one-liner shell commands
  • Tasks where you need the result immediately

Quick Reference

Dispatch a New Task

bg-dispatch \
  --adapter claude-code \
  --prompt "Build a REST API with user authentication and rate limiting" \
  --name "user-auth-api" \
  --workdir /path/to/project \
  --source-session "$CURRENT_SESSION_KEY"

Resume After Interruption

bg-dispatch --adapter claude-code --workdir /path/to/project --resume --force

Check Task Status (Heartbeat)

node task-check.mjs --data-dir ./data --projects-dir /path/to/projects

Available Adapters

claude-code (Claude Code)

The primary adapter. Supports Superpowers plugin, Agent Teams, and Bedrock.

bg-dispatch -a claude-code \
  -p "Implement the payment module with Stripe integration" \
  -n "payment-stripe" \
  -w /path/to/project \
  --model "claude-opus-4-1-20250805" \
  --agent-teams \
  --allowed-tools "Bash(*),Read,Write,Edit,MultiEdit,Glob,Grep,Skill,Agent"

Adapter-specific options:

  • --opt permission-mode=none — Claude Code permission mode

Environment variables:

  • CLAUDE_BIN — Path to claude binary (default: auto-detect)
  • CLAUDE_CODE_USE_BEDROCK=1 — Use AWS Bedrock
  • AWS_PROFILE, AWS_REGION — Bedrock auth

aider (Aider)

bg-dispatch -a aider \
  -p "Refactor the database layer to use connection pooling" \
  -n "db-pool-refactor" \
  -w /path/to/project \
  --model "claude-sonnet-4-20250514"

Adapter-specific options:

  • --opt edit-format=diff — Edit format (whole/diff/udiff)
  • --opt auto-commits=true — Enable Aider's auto-commits
  • --opt lint-cmd="ruff check" — Lint after edits
  • --opt test-cmd="pytest" — Test after edits

codex (OpenAI Codex CLI)

bg-dispatch -a codex \
  -p "Add comprehensive test coverage for the auth module" \
  -n "auth-tests" \
  -w /path/to/project

Adapter-specific options:

  • --opt approval-mode=full-auto — Approval mode (full-auto/suggest/ask)

Notification System

bg-dispatch uses a pluggable notifier system. Configure in bg-dispatch.json:

{
  "notifiers": [
    {
      "type": "openclaw",
      "config": { "session": "main" }
    },
    {
      "type": "webhook",
      "config": {
        "url_env": "SLACK_WEBHOOK_URL",
        "template": "slack"
      }
    },
    {
      "type": "webhook",
      "config": {
        "url_env": "FEISHU_WEBHOOK_URL",
        "template": "feishu",
        "secret_env": "FEISHU_WEBHOOK_SECRET"
      }
    },
    {
      "type": "command",
      "config": {
        "command": "echo Task $BGD_TASK_NAME completed with status $BGD_STATUS"
      }
    }
  ]
}

Notifier Types

Type Description Use Case
openclaw Wakes OpenClaw session via direct system event Primary — agent receives completion event
webhook HTTP POST to any webhook URL Team notifications (Slack, Feishu, Discord)
command Execute arbitrary shell command Custom integrations, scripts, logging

Webhook Templates

  • slack — Slack Block Kit message with status, duration, model info
  • feishu — Feishu rich-text post with emoji status indicators
  • discord — Discord embed with color-coded status
  • generic — Simple JSON payload (works with any webhook receiver)

Config File Search Order

  1. <workdir>/bg-dispatch.json — per-project config
  2. <data-dir>/bg-dispatch.json — instance config
  3. <install-dir>/bg-dispatch.json — global config
  4. ~/.bg-dispatch.json — user config

If no config file found, defaults to openclaw notifier.

Writing Custom Notifiers

Create a script in notifiers/ implementing:

notifier_validate() {
  # Check prerequisites, return 1 on failure
}

notifier_send() {
  local META_FILE="$1"  # Path to task meta.json
  local CONFIG="$2"     # JSON string with notifier config
  # Send notification, return 0 on success
}

The command notifier exposes these env vars in the command context:

  • BGD_TASK_NAME, BGD_STATUS, BGD_EXIT_CODE
  • BGD_WORKDIR, BGD_MODEL, BGD_ADAPTER
  • BGD_STARTED_AT, BGD_COMPLETED_AT
  • BGD_META_FILE, BGD_PROGRESS_FILE

How It Works

Architecture

Your Agent (OpenClaw session)
  │
  ├─ Receives coding task
  ├─ Runs: bg-dispatch --adapter <name> --prompt <task> --workdir <dir>
  │   ├─ Creates progress.md + meta.json in data/tasks/<task-id>/
  │   ├─ Launches coding agent in background (setsid + PTY)
  │   ├─ Starts watchdog (stall detection + max runtime)
  │   └─ Returns immediately ← your agent is FREE
  │
  ... zero token consumption while agent works ...
  │
  ├─ On completion:
  │   ├─ Hook fires → notify.sh → all configured notifiers
  │   ├─ Watchdog provides fallback if hook fails
  │   └─ OpenClaw notifier wakes your agent session (instant, no cron delay)
  │
  ├─ Your agent reads data/tasks/<task-id>/progress.md
  └─ Sends summary to user, creates PR, etc.

Three-Layer Notification Guarantee

  1. Hook (primary) — Claude Code's Stop hook fires → on-complete.shnotify.sh
  2. Process fallback — Inline completion handler in the background process
  3. Watchdog (safety net) — Detects process exit, waits 35s for hook, sends if missed

All three paths call notify.sh, which is idempotent: each notifier checks meta.json's notified.* fields before sending. First writer wins; subsequent calls are safe no-ops. No duplicate notifications even when hook and setsid block race.

Cascading Session Wake (openclaw notifier)

The openclaw notifier uses a three-level cascade to guarantee the agent session is woken, even when the target session is temporarily unavailable:

Level 1: Targeted session wake (source_session)
  │ ✅ → done (notification delivered to exact originating session)
  │ ❌ ↓
Level 2: Broadcast system event (main session)
  │ ✅ → done (main session picks up the completion)
  │ ❌ ↓
Level 3: Heartbeat pending marker
  │ → writes pending_session_notify to meta.json
  │ → next heartbeat poll (task-check.mjs) surfaces it
  └ → agent reads meta.json and delivers the notification

--source-session: Pass the originating session key when dispatching. The completion notification will be routed directly to that session (Level 1), rather than broadcasting to the main session. This is critical for multi-channel setups where the dispatching session (e.g., a Slack channel) is different from the main agent session.

bg-dispatch -a claude-code \
  -p "Fix auth bug" \
  -n "auth-fix" \
  -w /path/to/project \
  --source-session "channel:C0ANTPRBBJ4"

Idempotency tracking in meta.json:

{
  "notified": {
    "openclaw": true,
    "webhook_slack": true,
    "webhook_feishu": false
  },
  "pending_session_notify": null
}

When pending_session_notify is non-null, task-check.mjs includes it in the output so the heartbeat can retry delivery.

Watchdog Behavior

  • Stall detection — No file changes for 15 min (configurable) → kill + notify
  • Max runtime — Hard 2-hour limit (configurable) → kill + notify
  • Progress.md check — If ## Status: COMPLETE appears → clean shutdown + notify
  • Exit detection — When process exits, waits for hook, sends fallback if needed

Progress Monitoring

bg-dispatch provides intermediate progress reporting between dispatch and completion:

  • Periodic polling — With --progress-interval 300, the watchdog runs git diff --stat every 5 minutes and appends to progress.md
  • File activity signals — The watchdog logs file changes to watchdog.log and updates last_activity in meta.json (always-on)
  • Progress notifications — Notifiers can subscribe to progress events via the events config array
  • Live outputbgd logs -f follows output in real-time while the task is running

Notifier Event Configuration

Each notifier can subscribe to specific events. Default is ["complete"] only:

{
  "notifiers": [
    { "type": "openclaw", "config": { "session": "main" } },
    { "type": "webhook", "config": { "url_env": "SLACK_WEBHOOK_URL", "template": "slack" }, "events": ["complete", "progress"] }
  ]
}

Event types:

  • complete — Task finished (default, always sent unless filtered)
  • progress — Lightweight mid-task update (must opt-in via events array)

Resume Protocol

Progress is centralized in data/tasks/<task-id>/ — outside the project workdir to avoid polluting repos:

data/tasks/<task-id>/
├── meta.json          # Full task definition (prompt, adapter, config, status)
├── progress.md        # What's done, in progress, remaining
├── output.txt         # Agent output log
└── watchdog.log       # Watchdog monitoring log

On resume, bg-dispatch searches data/tasks/ for a task matching the workdir and rebuilds the prompt from meta.json. The agent reads progress.md + git log and continues from the last checkpoint. Legacy .dev-progress/ in the workdir is auto-migrated if found.

CLI Reference

Usage: bg-dispatch [OPTIONS]

Required:
  -a, --adapter NAME         Adapter name (claude-code, aider, codex)
  -p, --prompt TEXT          Task prompt (required unless --resume)

Options:
  -n, --name NAME            Task name (default: task-<timestamp>)
  -w, --workdir DIR          Working directory (default: cwd)
  --resume                   Resume from data/tasks/
  --force                    Kill existing task for same workdir
  --model MODEL              Model override
  --allowed-tools TOOLS      Allowed tools (adapter-specific)
  --agent-teams              Enable multi-agent mode
  --stall-timeout SECS       Stall timeout (default: 900)
  --max-runtime SECS         Max runtime (default: 7200)
  --progress-interval SECS   Progress polling interval (default: 0 = disabled)
  --callback-session KEY     OpenClaw session key
  --source-session KEY       Originating session key (for targeted notification routing)
  --opt KEY=VALUE            Adapter-specific option (repeatable)
  -h, --help                 Show help

Integration Guide for Agent Developers

Step 1: Install

git clone https://github.com/PeterHiroshi/bg-dispatch.git
cd bg-dispatch && bash install.sh
export PATH="$(pwd):$PATH"
export BG_DISPATCH_DIR="$(pwd)"

Step 2: Configure Notifications

Create bg-dispatch.json with your preferred notifiers. At minimum, use openclaw to wake your agent:

{
  "notifiers": [
    { "type": "openclaw", "config": { "session": "main" } }
  ]
}

Step 3: Dispatch from Your Agent

Your OpenClaw agent dispatches tasks like this:

bg-dispatch \
  -a claude-code \
  -p "$(cat <<'EOF'
Build a user authentication system with:
- JWT tokens with refresh
- Rate limiting per IP
- Password hashing with bcrypt
- Integration tests
EOF
)" \
  -n "auth-system" \
  -w /path/to/project

Step 4: Handle Completion

When the task completes, your agent receives a system event. Read the progress from the centralized task directory (path is included in the system event message):

cat /path/to/bg-dispatch/data/tasks/<task-id>/progress.md

Then take action: create a PR, notify the user, dispatch the next task.

Step 5: Heartbeat Integration

In your agent's heartbeat, check for completed/interrupted tasks:

node bg-dispatch/task-check.mjs \
  --data-dir bg-dispatch/data \
  --projects-dir /path/to/projects

The output JSON tells you what needs attention:

  • unnotified — Completed tasks the user hasn't been told about
  • interrupted — Tasks that need resuming
  • stalled — Tasks that may be stuck

Monitoring Commands (bgd CLI)

The bgd CLI provides interactive task monitoring. Use it to check on background tasks, view logs, and manage task lifecycle.

List Tasks

bgd tasks                         # All tasks
bgd tasks --status running        # Only running tasks
bgd tasks --status done           # Only completed tasks
bgd tasks --limit 5               # Limit output

Quick Status Overview

bgd status                        # Summary counts + active task details

Show Task Details

bgd show auth-api                 # Full metadata + progress + git log
bgd show db                       # Partial name match OK

View Logs

bgd logs auth-api                 # Last 50 lines of output (ANSI stripped)
bgd logs auth-api -n 100          # Last 100 lines
bgd logs auth-api -f              # Follow in real-time
bgd logs auth-api --watchdog      # Watchdog log instead

View Progress

bgd progress auth-api             # Show progress.md from task data

Cancel a Task

bgd cancel auth-api               # Kill process tree, update meta, notify

Resume a Task

bgd resume auth-api               # Resume interrupted/killed task

Clean Up

bgd clean                         # Remove done/killed tasks older than 24h
bgd clean --all                   # Remove all done/killed tasks
bgd clean --dry-run               # Preview what would be removed

File Layout

bg-dispatch/
├── bg-dispatch              # Main dispatcher
├── watchdog.sh              # Stall detection + max runtime
├── notify.sh                # Notification dispatcher
├── task-check.mjs           # Heartbeat integration
├── install.sh               # Setup script
├── bg-dispatch.example.json # Example config
├── SKILL.md                 # This file (OpenClaw Skill)
├── adapters/
│   ├── claude-code.sh       # Claude Code adapter
│   ├── aider.sh             # Aider adapter
│   └── codex.sh             # Codex CLI adapter
├── notifiers/
│   ├── openclaw.sh          # OpenClaw system event notifier
│   ├── webhook.sh           # Generic webhook (Slack/Feishu/Discord)
│   └── command.sh           # Custom command notifier
├── hooks/
│   ├── on-complete.sh       # Claude Code Stop hook
│   └── settings.json        # Hook config
├── docs/
│   └── writing-adapters.md  # Adapter development guide
└── data/tasks/              # Runtime data (gitignored)