---
name: claude-code-delegate
description: Delegate coding tasks to the Claude Code CLI using a dedicated non-root claude user, UUID-backed prompt files, and persistent tmux sessions.
compatibility: Created for Zo Computer
metadata:
  author: rob.zo.computer
---

# claude-code-delegate

Delegate coding tasks to Claude Code CLI via tmux-backed sessions. Zo creates a session, writes prompts to files, and dispatches them into a live interactive Claude chat.

## First-Time Install

After unpacking into `Skills/`, run the bootstrap script:

```bash
python3 Skills/claude-code-delegate/scripts/bootstrap.py
```

This handles everything: ensures a single `claude` binary, checks auth, creates the `claude` user with copied credentials, and runs an end-to-end smoke test.

If bootstrap reports auth failure, the user must run `claude auth login` in their Zo terminal, then rerun the bootstrap.

## Usage

### Create a session

```bash
python3 Skills/claude-code-delegate/scripts/claude_delegate.py create-session --repo /home/workspace
```

Outputs `SESSION_ID`, `TMUX_SESSION`, `REPO_DIR`. The session auto-accepts the bypass permissions prompt.

### Send a prompt (one-step)

```bash
python3 Skills/claude-code-delegate/scripts/claude_delegate.py send \
  --session-id <session-id> \
  --text "fix the failing tests in src/"
```

Creates a prompt file and dispatches it in one call. Equivalent to `create-prompt` + `dispatch`.

### Create and dispatch a prompt (two-step)

```bash
# Write prompt
python3 Skills/claude-code-delegate/scripts/claude_delegate.py create-prompt \
  --session-id <session-id> \
  --text "fix the failing tests in src/"

# Dispatch it
python3 Skills/claude-code-delegate/scripts/claude_delegate.py dispatch \
  -p <session-id>/<prompt-id>
```

### Read session output

```bash
python3 Skills/claude-code-delegate/scripts/claude_delegate.py read-output <session-id>
```

Captures the tmux pane content (last 500 lines by default) and prints a `STATUS=idle` or `STATUS=busy` line. Use `--lines N` to adjust capture depth.

### Watch for completion

```bash
python3 Skills/claude-code-delegate/scripts/claude_delegate.py watch <session-id>
```

Polls the session until Claude finishes (idle `❯` prompt with new content). Options:
- `--timeout 300` — max seconds to wait (default 300)
- `--interval 2` — poll interval in seconds (default 2)
- `--quiet` — suppress intermediate output, only print final status

Exits 0 on completion, 1 on timeout.

### List sessions

```bash
python3 Skills/claude-code-delegate/scripts/claude_delegate.py list-sessions
```

### Close a session

```bash
python3 Skills/claude-code-delegate/scripts/claude_delegate.py close-session <session-id>
```

## Best Practices for Callers

This section is for AI agents delegating work to Claude Code. Getting the timing right avoids wasted round-trips.

### Recommended workflow

```bash
# 1. Create session
python3 Skills/claude-code-delegate/scripts/claude_delegate.py create-session --repo /home/workspace
# 2. Send one prompt
python3 Skills/claude-code-delegate/scripts/claude_delegate.py send --session-id <sid> --text "..."
# 3. Wait patiently
python3 Skills/claude-code-delegate/scripts/claude_delegate.py watch <sid> --timeout 180 --quiet
# 4. Read the full result
python3 Skills/claude-code-delegate/scripts/claude_delegate.py read-output <sid> --lines 2000
# 5. Close
python3 Skills/claude-code-delegate/scripts/claude_delegate.py close-session <sid>
```

Only send a second prompt if the first truly completed — not because output looks truncated mid-stream.

### Extended thinking ("Hyperspacing")

Claude Code has a thinking phase that can take **30–90+ seconds** before any visible output appears. During this time the tmux pane may look idle or show "Hyperspacing…" / "thinking". This is normal. Use generous timeouts: **at least 120–180s** on `watch` before assuming work is done.

### Never send prompts while Claude Code is busy

If `read-output` shows Claude Code mid-thought (partial output, "Hyperspacing…", "thinking", or tool calls in progress), do **not** send another prompt. Messages sent while busy get queued in the tmux input buffer and execute after the current task finishes, causing redundant back-and-forth. The correct response is to simply wait longer.

### How to tell if Claude Code is actually done

| Signal | Meaning |
|--------|---------|
| `STATUS=idle` + visible `❯` prompt + substantive response in output | **Done.** Safe to read results or send a follow-up. |
| `STATUS=idle` but output shows "Hyperspacing…", "thinking", partial tool calls, or cuts off mid-sentence | **Still working.** The idle detection caught a brief pause. Wait and re-check. |
| `STATUS=busy` | **Obviously still working.** Just wait. |

When in doubt, wait 10–15 seconds and call `read-output` again. If the output hasn't changed and STATUS is idle, it's done.

### Use `--lines 2000` on `read-output`

The default 500 lines is often not enough to capture a full response, especially when Claude Code reads multiple files and produces detailed analysis. Use `--lines 2000` (or higher) as your default.

## Architecture

```
Zo (root)
  │
  ├─ setup.py          Creates `claude` user, copies creds, installs stub
  │
  ├─ claude_delegate.py
  │    ├─ create-session   → tmux new-session as claude user, auto-accepts bypass
  │    ├─ create-prompt    → writes prompt to /home/claude/.prompts/<sid>/<pid>.md
  │    ├─ dispatch         → tmux send-keys into live Claude chat
  │    ├─ send             → create-prompt + dispatch in one step
  │    ├─ read-output      → capture tmux pane, report idle/busy status
  │    ├─ watch            → poll until Claude finishes, return output
  │    ├─ list-sessions    → shows all sessions with active/stopped status
  │    └─ close-session    → tmux kill-session
  │
  └─ /home/claude/
       ├─ .claude/              OAuth credentials + settings (copied from root)
       ├─ .claude.json          Claude state (copied from root)
       ├─ .prompts/<sid>/*.md   Prompt files
       ├─ .delegate-claude-code/sessions/<sid>/meta.env
       └─ .tmux-tmp/            tmux socket directory
```

- Our UUIDs are wrapper-level only — Claude CLI runs as a plain interactive session
- Each session is a persistent tmux window with a live `claude --dangerously-skip-permissions` process
- Prompts are injected via `tmux send-keys`

## Troubleshooting

**Token expired**: Run `claude auth login` as root in Zo terminal, then `python3 Skills/claude-code-delegate/scripts/setup.py` to copy fresh creds.

**Bypass prompt not auto-accepted**: The script sends Down+Enter automatically. If it stalls, the tmux timing may need adjustment — increase the sleep in `ensure_session_started`.

**Session died**: Check `list-sessions` for `stopped` status. Create a new session — Claude doesn't persist chat history across sessions anyway.
