ds-delegate · git:20261018.b6cdaec · 2026-10-18 · sha256 cb1bc6e8d8d69d33

ds-delegate git:20261018.b6cdaecB

Immutable. This exact content never changes and is served at /api/v1/blob/cb1bc6e8d8d69d33.

---
name: ds-delegate
description: |
  Delegate a coding or agentic task to claude-ds — a DeepSeek-backed Claude Code
  CLI — as a worker. Use to run/delegate work via
  claude-ds or DeepSeek. Covers invocation
  (generation vs full-agentic), running as a background task, isolating real-repo work
  in a git worktree, and review/verify/merge of the output. The built-in Agent/subagent
  tool canNOT use DeepSeek (model enum is Anthropic-only) — claude-ds is the only path.
  cli-dispatch is multi-backend: a second worker, **Antigravity (agy / Gemini)**, is
  available via `ag-agent` / `ag-stream`, a third, **Codex (OpenAI Codex CLI)**, via
  `cx-agent` / `cx-stream`, a fourth, **OpenCode (via OpenRouter)**, via
  `oc-agent` / `oc-stream`, a fifth, **GitHub Copilot**, via `cp-agent` /
  `cp-stream`, and a sixth, **Ollama (a local model through claude)**, via `ol-agent`
  (see the "Other backends" section below).
  Codex adds a real OS-level read-only sandbox (`cx-agent --read-only`).
  Triggers: "claude-ds", "delegate to claude-ds", "run with deepseek", "delegate to
  antigravity/gemini", "run with agy", "delegate to codex", "run with codex/openai",
  "delegate to opencode", "run with opencode/openrouter", "delegate to copilot",
  "run with github copilot", "delegate to ollama", "run with a local model" (also
  Turkish: "deepseek ile yap/çalıştır", "gemini/antigravity ile yap", "codex/openai ile
  yap", "opencode/openrouter ile yap", "copilot ile yap/çalıştır", "ollama ile yap", "delegate to copilot",
  "delege et claude-ds").
user-invocable: true
---

# claude-ds — DeepSeek delegation worker

`claude-ds` is a portable wrapper installed to `~/.local/bin` by `/cli-dispatch:setup`;
it runs the Claude Code CLI against DeepSeek's Anthropic-compatible API. Since it's on
PATH, call it **directly as `claude-ds`** (no old `zsh -ic` function trick needed).

## When / when not
- The built-in `Agent`/subagent tool does **NOT support** DeepSeek (`model` enum: sonnet/opus/haiku/fable).
  This is the only way to hand work to DeepSeek.
- Conversation context is **not shared** → the prompt must be **self-contained**.

## Wrappers
- **`ds-agent`** (SIMPLEST — subagent-style) — one synchronous command: give it a task, it
  runs to completion, streams tool activity to stderr, and prints **only the final answer to
  stdout**. Default agentic (may write/run in `--cwd`); `--read-only` for analysis-only.
  Best when you just want "delegate this and give me the result" in a single call.
- **`claude-ds-stream`** — runs `claude` with stream-json, parses output into a **session
  directory** (live + observable + resumable). Use when you want to run in the background and
  poll, or need the session id / `--resume` / `/cli-dispatch:watch` workflow.
- **`claude-ds`** — plain env wrapper (`claude "$@"`). No parsing/session; fast one-shot only.

### ds-agent — single command (subagent-style)
```bash
ds-agent "<task>"                     # agentic in cwd; live progress on stderr; answer on stdout
ds-agent --read-only "<question>"     # no writes / no bash
ds-agent --cwd <dir> "<task>"         # work in <dir> (use an isolated dir for safety)
ds-agent --resume <id> "<follow-up>"  # continue a session
echo "<task>" | ds-agent              # task via stdin
```
stdout = final answer only (safe to capture/pipe); stderr = banner + live tool activity.
Exit code is the worker's. `-q` silences the banner/progress. It forwards
`--max-runtime`/`--idle-timeout` to the underlying `claude-ds-stream`.

Session directory: `${XDG_CACHE_HOME:-$HOME/.cache}/cli-dispatch/sessions/<id>/` (legacy `claude-ds` path still read as a fallback)
- `status.json` — compact rolling summary (**the only file to poll**: state, lastTool, toolCounts, finalResultPreview)
- `progress.log` — terse human-readable stream (`▸ Edit foo.ts`, `✓/✗`, truncated text)
- `transcript.jsonl` — raw stream-json (resume/audit; **NOT read while polling**)
- `meta.json` — prompt preview, cwd, branch, model, start/end

## The deterministic runner — `mcp__cli-dispatch__dispatch` tool (preferred), `cli-dispatch:runner` agent (older Claude Code) and `/cli-dispatch:run` (direct)
Delegation runs through `cli-dispatch-run`: it launches the worker, isolates repo work in a git
worktree, blocks until it finishes, runs `--verify` itself and writes a compact verdict. All of
that is plain shell. An LLM *watching* a worker is what measured ~9x the worker's own output in
Anthropic tokens (issue #114, ~62 turns per run), so the default path is a **thin forwarder**, not
a babysitter. For mechanical work with a machine-checkable verify command, if the `mcp__cli-dispatch__dispatch` tool is available (Claude Code 2.1.287+), call it first — it runs the detached runner and blocks on it itself, with no subagent:
```
mcp__cli-dispatch__dispatch({"backend": "ds", "cwd": "/abs/path", "task": "<self-contained brief>", "verify": ["<cmd>"]})
```
`model` is optional. It returns the runner's compact summary verbatim; it does not read the verdict or diff for you. Otherwise (older Claude Code) use the thin agent:
```
Agent(subagent_type: "cli-dispatch:runner", prompt:
  "backend: ds\ncwd: /abs/path\nverify: <cmd>\n---\n<self-contained brief>")
```
The agent (haiku, Bash-only) writes the brief to a file, starts the runner with `--detach` (so a
run longer than the Bash tool's 10-minute ceiling is not killed), blocks on
`cli-dispatch-wait --run <id>` and returns the compact verdict verbatim — about 3-4 turns. A
failing verify is retried once inside the runner (`--fix-attempts 1`), never by the agent. Then
verify the result yourself.

For direct use (no agent, zero LLM tokens, you background it yourself):
```
/cli-dispatch:run ds "<self-contained task>" --verify '<cmd>'
```

**Escalation path** (judgment-heavy work, no verify command): call `ds-agent` directly (or
still go through `/cli-dispatch:run` without `--verify`), then read the compact result and the
diff yourself, following up with `/cli-dispatch:resume <session-id> "<prompt>"` if it needs
another pass. For a quick one-shot with no repo changes, `ds-agent` alone is enough.

## Run rules
- **Always run as a background task**: Bash tool `run_in_background: true` (don't block).
- For a **long prompt**, write the brief to a file and pass it with `-p "$(cat <brieffile>)"`.
- **Cost-conscious monitoring (MANDATORY):** track progress by reading only the small `status.json`
  (`/cli-dispatch:watch <id>`). Don't read the raw `transcript.jsonl`; don't tail it repeatedly in a
  tight loop; check once per orchestration step. When the task finishes you get re-invoked anyway.
- **Windows:** after setup, `claude-ds` / `claude-ds-stream` are called directly (`.cmd` shim);
  the parser `.mjs` is shared cross-platform. On macOS/Linux/WSL the `.sh` variants apply.

> **Not a sandbox by default.** The wrapper always runs with `--permission-mode
> bypassPermissions` (the CLI can't prompt in non-interactive `--print` mode), so the
> worker **can write files and run bash even without `--dangerously-skip-permissions`**.
> "Generation mode" is a convention (you didn't give it a file task), not an enforced
> sandbox. For real-repo tasks, isolate in a worktree. For guaranteed no-writes, use `--read-only`.

### Mode 1 — Generation (code/text/analysis)
```bash
claude-ds-stream -p "<self-contained prompt>"
```
The final text goes to stdout, progress goes to the session directory. Session id on stderr.
The worker *can* still write files if the prompt leads it to — add `--read-only` to forbid that.

### Mode 1-safe — True read-only (denies Write/Edit/Bash; nothing mutated)
```bash
claude-ds-stream --read-only -p "<analysis/generation prompt>"
```
Use when the output must be text-only and the worker must not touch disk.

### Mode 2 — Full agentic (writes files + runs bash)
```bash
claude-ds-stream --cwd <dir> --dangerously-skip-permissions -p "$(cat /tmp/ds-brief.txt)"
```
Writes files / runs bash → **you MUST isolate it** (worktree). (`--dangerously-skip-permissions`
is largely redundant with the default bypassPermissions; it signals intent and matches the worktree helper.)

### Follow-up / resume (continue the same DeepSeek session)
```bash
claude-ds-stream --resume <session-id> -p "<follow-up>"
```
The transcript is appended to the same session; `status.json` is updated. See sessions: `/cli-dispatch:sessions`.

### Timeouts (safety net for hung/runaway workers)
```bash
claude-ds-stream --max-runtime 600 --idle-timeout 90 -p "<prompt>"   # seconds; 0 = off (default)
```
A background watchdog kills the worker (and its child processes) if it exceeds the overall
runtime cap (`--max-runtime`) or stalls with no new output (`--idle-timeout`, measured from
`transcript.jsonl` activity). Timed-out sessions are marked `state: error` with
`error: "timeout: …"`. Env fallbacks: `CLAUDE_DS_MAX_RUNTIME`, `CLAUDE_DS_IDLE_TIMEOUT`.
Both default off. Enforced on both wrappers — bash via a `kill_tree` watchdog, PowerShell
via a background-job watchdog that locates the worker by its session id and kills the tree
with `taskkill /T /F`.

## Safe operation for a real repo task (MANDATORY)
Use the bundled helper:
```bash
"${CLAUDE_PLUGIN_ROOT}/scripts/ds-worktree-run.sh" <repo> <branch> <brief-file>
```
This script: opens an isolated git worktree (origin/main), symlinks `node_modules` if present,
runs **claude-ds-stream** in Mode 2 inside the worktree (session-tracked), and leaves the diff
**UNCOMMITTED**. The session id is printed on stderr → watch it with `/cli-dispatch:watch <id>`.
The same helper exists for each other backend with the same signature: `ag-worktree-run.sh`,
`cx-worktree-run.sh`, `oc-worktree-run.sh`, `cp-worktree-run.sh`.

Then **YOU are the reviewer:**
1. Review the FULL diff with `git -C <worktree> status && git -C <worktree> diff` — check for
   side effects, confirm only the target files were touched.
2. Run tsc/build/test **yourself** (independent verification).
3. If all good, YOU do the git: commit → push → PR → merge → `git pull origin main` on the main checkout.
   Note in the commit body that "implementation was delegated to claude-ds (DeepSeek)" (transparency).
4. Cleanup: `rm <worktree>/node_modules` → `git worktree remove <worktree> --force` → `git worktree prune`.

## Other backends — Antigravity, Codex, OpenCode, GitHub Copilot, Ollama

Each is a *different binary* from `claude`, with its own auth/config — the DeepSeek "swap the
env var" trick does NOT apply to any of them. The exception is **Ollama** (`ol-agent`): it IS that
trick — the same `claude` worker and the same `ds-agent`/`claude-ds-stream`, pointed at a local
Ollama. Enable any at `/cli-dispatch:setup`. Every
`*-agent`/`*-stream` family mirrors `ds-agent`/`claude-ds-stream` exactly (same flags, same
Modes 1/1-safe/2, same session dir/format) — only the command name and the specifics below
differ:

```bash
ag-agent "<task>"      # Antigravity/Gemini
cx-agent "<task>"      # Codex/OpenAI
oc-agent "<task>"      # OpenCode/OpenRouter
cp-agent "<task>"      # GitHub Copilot
ol-agent "<task>"      # Ollama (a local model)
<prefix>-agent -q "<task>"                   # answer only on stdout
<prefix>-agent --cwd <dir> "<task>"          # agentic, isolated dir
<prefix>-agent --resume <id> "<follow-up>"   # continue the same session
<prefix>-stream --cwd <dir> -p "<task>"      # background/session-tracked variant (poll status.json)
```

| Backend | Model select | Auth | Sandbox | Notable difference |
|---|---|---|---|---|
| **Antigravity** (`ag-*`) | `--model "<slug or display name>"` / `AG_MODEL` — e.g. `--model gemini-3.6-flash-high` or `--model "Gemini 3.6 Flash (High)"` (agy accepts both); live list: `agy models` (proxies multiple families, incl. Claude — verified cross-vendor routing) | Google sign-in (run `agy` once) or `GEMINI_API_KEY`/`ANTIGRAVITY_API_KEY` | **None** — `--read-only` is rejected (tested: `--sandbox` restricts the terminal, not writes) | Runs under a pseudo-TTY (`script`) tailing agy's on-disk JSONL (needs `script` + `node`; no `--output-format json` on agy). `--max-runtime` is enforced via agy's own `--print-timeout` — a best-effort backstop only, since agy spawns detached workers under a pty and an external tree-kill is unreliable (verified); wall time may exceed the cap and a capped run may report `done` with partial output instead of a guaranteed `error`. Worktree isolation also avoids agy's per-workspace conv-id race. |
| **Codex** (`cx-*`) | `--model <name>` / `CX_MODEL` — e.g. `--model gpt-5.4-mini`; no CLI list command, check `~/.codex/models_cache.json` or `/model` inside codex | `codex login` (ChatGPT/OAuth) or `CODEX_API_KEY` (takes precedence over `OPENAI_API_KEY`) | **Real OS-level** — `--read-only` passes `-s read-only` → macOS Seatbelt / Linux bwrap+seccomp, a kernel-enforced hard-block on writes; genuine no-writes guarantee, no worktree needed for pure analysis. Default `workspace-write` for agentic work; override with `--sandbox read-only\|workspace-write\|danger-full-access`. | `codex exec --json` emits a clean JSONL stream — no pseudo-TTY/file-tail needed. Session id is the codex **thread-id**; `--resume` does not accept `--cwd`. |
| **OpenCode** (`oc-*`) | `--model <bare-slug>` / `OC_MODEL` — e.g. `--model google/gemma-4-31b-it:free`; live list: `OPENROUTER_API_KEY=<key> opencode models openrouter` | `OPENROUTER_API_KEY` in the config (pasted by the user — never written by Claude) | **None** — `--auto` (always passed) is a functional headless requirement, not a safety opt-in | Unix-only (macOS/Linux/WSL). Invalid slugs fail loudly with an OpenRouter API error. `--resume` verified to target the named session. |
| **Ollama** (`ol-agent`) | `OL_MODEL` in the config — **required**, no default, a model already pulled on that Ollama (`ollama pull <name>`; cli-dispatch never pulls); `--model` on `/cli-dispatch:run`/dispatch sets it per run. `OLLAMA_BASE_URL` (default `http://127.0.0.1:11434`) | **None** — no key | Same as DeepSeek: `--read-only` is a real read-only tool set | Unix-only. The first turn takes ~40 s (Claude Code's ~29k start prompt is processed locally). Ollama truncates an overlong conversation **silently** (HTTP 200, no error), so the worker is told the server's real window (`OL_CONTEXT` → `/api/ps` → 32768) — set `OLLAMA_CONTEXT_LENGTH` on the server. Prefer `ol` for small briefs with a `--verify`. An unreachable server or unpulled model fails before any worktree (runner exit 5). |
| **GitHub Copilot** (`cp-*`) | `--model <slug>` / `CP_MODEL` (e.g. `gpt-5.4`, `auto`); `--effort low\|medium\|high` → `--reasoning-effort=<level>` | `COPILOT_GITHUB_TOKEN` > `GH_TOKEN` > `GITHUB_TOKEN` (cli-dispatch reuses `gh auth token` when available); active Copilot subscription required | **None** — `--allow-all-tools --no-ask-user` always passed for headless use, not a safety opt-in | Unix-only. Model list only visible via `/model` in the copilot TUI or GitHub Copilot docs — slugs change over time. Balance not queryable from the CLI; use https://github.com/settings/billing. |

**Same session dir as DeepSeek** for all five (`…/cli-dispatch/sessions/<id>/`), so
`sessions`/`watch`/`resume`/`kill` work across every backend. **Isolation:** the same worktree
rule applies (`--cwd <worktree>`, review the diff yourself) — for Antigravity, OpenCode, and
Copilot, worktree isolation is the *only* safety boundary, since none of the three has any
OS- or tool-level write-deny.

**Delegation path (all five):** use the `mcp__cli-dispatch__dispatch` tool with
`backend: "ag"|"cx"|"oc"|"cp"|"ol"` when available, else `Agent(subagent_type: "cli-dispatch:runner")` with
`backend: ag|cx|oc|cp|ol` for mechanical work (or `/cli-dispatch:run <backend> "<task>" --verify '<cmd>'`
directly), or the escalation path (call the `*-agent` CLI directly and verify the result yourself) for judgment-heavy work.

## Triviality gate

Before delegating, ask three questions: (1) single file? (2) expected diff under
~50 lines? (3) zero discovery/ambiguity — you could write the exact edit right
now? If all three are yes, do NOT delegate — do it inline; the delegation's
fixed cost (worker spin-up + isolation + your own merge/verify cycle) exceeds
the work. If several such trivial fixes accumulate, batch them into one
delegation instead (see "Batch small fixes" below).

## Batch small fixes

When the orchestrator sees a delegation prompt with several small related fixes
(expected ~50 lines per fix, fixes are related), combine them into a SINGLE
delegation with an itemized checklist rather than multiple delegations. The
economics favor one worker, one diff, one verify cycle — per-delegation fixed
costs (worker spin-up + orchestrator merge/verify) dominate for
small changes, so splitting them across separate delegations multiplies that
overhead for no benefit.

## Role
The worker (claude-ds = DeepSeek, ag-agent = Antigravity/Gemini, cx-agent = Codex/OpenAI,
oc-agent = OpenCode/OpenRouter, cp-agent = GitHub Copilot, or ol-agent = a local Ollama model) does the work; you =
orchestrator + reviewer + git/merge owner.
Don't trust any output until verified.

## Commands
- `/cli-dispatch:setup` — install worker backends (DeepSeek / Antigravity / Codex / OpenCode / Copilot / Ollama); choose at setup + config + smoke test.
- `/cli-dispatch:ask <backend> "<prompt>"` — one-shot question to a worker, no repo changes (`ds`/`cx`/`ol` run `--read-only`; `ag`/`oc`/`cp` run in a throwaway temp dir). Backend: `ds|deepseek`, `ag|antigravity`, `cx|codex`, `oc|opencode`, `cp|copilot`, `ol|ollama`. For repo work use `run` or the runner agent.
- `/cli-dispatch:run <backend> "<task>" --verify '<cmd>'` — the deterministic runner: launch + worktree-isolate + block + verify, zero LLM tokens. The direct path; the default from an orchestrator is the `cli-dispatch:runner` agent (see above).
- `/cli-dispatch:sessions` — list past/active sessions (all backends; shows a `backend` column). Optional backend filter: `/cli-dispatch:sessions <backend>`.
- `/cli-dispatch:watch <id> [--wait [--timeout S]]` — show a session's compact live status (cost-conscious); `--wait` blocks until a terminal state (or timeout) and prints a compact summary — one blocking call instead of polling.
- `/cli-dispatch:resume <id> <prompt>` — continue a worker session with a follow-up prompt (auto-detects backend).
- `/cli-dispatch:kill <id>` — stop a running worker session (SIGTERM + state → killed).
- `/cli-dispatch:clean` — remove stale worker dirs (a `running` session whose process died before finalize, so `status.json` is stuck). Dry-run by default; `--remove` deletes, `--older-than DAYS` also prunes old finished sessions. `--schedule [install|status|uninstall]` registers/inspects a daily OS-level auto-clean (launchd/cron/Scheduled Tasks) that runs `cli-dispatch-clean --remove`; bare `--schedule` = `status`.
- `/cli-dispatch:balance [backend]` — DeepSeek balance + Antigravity quota + Codex rate limits + OpenCode/OpenRouter credits + Copilot usage note at once, or one backend.
- `/cli-dispatch:gain [--drift]` — worker token totals by backend, plus Anthropic cost of the `cli-dispatch:runner` agent and of legacy runner-subagent sessions; `--drift` adds the drift report.
- `/cli-dispatch:doctor [backend]` — health check (PATH, keys, CLI auth ✓/✗, configured model, stale-install warning).
- `/cli-dispatch:help` — one-screen command reference.