← All Skills

snappy-dispatch

v1.0.0
6 files, 37.2 KB ~1,565 words · 7 min read Updated 2026-09-09

snappy-dispatch skill

32 of 34 checks pass
What does not pass yet
$ npx snappy-skills install snappy-dispatch
zip ↓
Documents
AGENTS.md

snappy-dispatch — Agent Loader#

This is the cheap-labor lever for the whole Snappy system. The orchestrator (Sonnet/Opus) stays the central point. Grunt work — file listings, greps, small refactors, data extraction, running scripts, parsing JSON, retry loops, quick classifications — dispatches here. Every dispatch costs sub-penny and completes in 2–4 seconds.

The rule of thumb#

If a task is mechanical and verifiable, dispatch it. If it needs judgment, keep it.

Mechanical + verifiable:

  • "count files matching X"
  • "grep for pattern Y and return matches"
  • "run npm test and extract the failing test names"
  • "parse this JSON log, return errors only"
  • "is the URL still 200?"

Needs judgment (do NOT dispatch):

  • architectural decisions
  • writing the content Robert will publish
  • classifying ambiguous human messages
  • choosing which fix to apply
  • writing the plan the user aligns on

API (TypeScript)#

typescriptimport { dispatch } from "../snappy-dispatch/api.ts";

const r = await dispatch({
  prompt: "Run npm test in /path/to/repo and list failing tests one per line.",
  model: "haiku",                        // alias or "provider/model-id"
  tools: ["bash", "read", "grep"],       // default: read,bash,grep,ls
  timeoutMs: 60_000,                     // default: 120_000
});

if (!r.ok) throw new Error(r.error || r.stderr);
console.log(r.output);

CLI#

bash# Shortest form — defaults to haiku
npx tsx ~/.claude/skills/snappy-dispatch/api.ts "count files in /tmp"

# Pick a different brain
npx tsx ~/.claude/skills/snappy-dispatch/api.ts --model gemini "grep foo in bar.ts"

# Limit tools
npx tsx ~/.claude/skills/snappy-dispatch/api.ts --model haiku --tools bash "..."

Model aliases (validated 2026-04-13)#

Alias Provider Model When to use
gemini openrouter google/gemini-2.5-flash Default. Fast, cheap, OpenRouter-backed. Works out of the box.
haiku anthropic claude-haiku-4-5 Only works if ANTHROPIC_API_KEY is set in .env.cache (normally empty in Claude Code — parent session reuse).
sonnet anthropic claude-sonnet-4-6 Same caveat as haiku. Use only when judgment > speed.
gemini-pro openrouter google/gemini-2.5-pro Hard reasoning at Gemini prices.
llama openrouter meta-llama/llama-3.3-70b-instruct Fully OSS path, no proprietary dependency.
qwen openrouter qwen/qwen-2.5-72b-instruct OSS alternative, strong tool use.
deepseek openrouter deepseek/deepseek-chat Very cheap OSS option.

Full provider/model-id strings also work (e.g. anthropic/claude-haiku-4-5, openrouter/mistralai/mistral-large).

Logging#

Every dispatch appends one line to ~/.claude/logs/dispatches.ndjson:

json{"ts":"2026-04-13T...","promptPreview":"count files...","tools":"read,bash,grep,ls","ok":true,"output":"15","durationMs":2743,"provider":"anthropic","model":"claude-haiku-4-5","exitCode":0,"outputPreview":"15"}

Use this for cost audits, latency profiling, and debugging when a dispatch returns an empty string.

How it works underneath#

dispatch() shells to pi (the @mariozechner/pi-coding-agent CLI from badlogic/pi-mono) with --print --no-session and the chosen provider/model/tools. pi runs the agent loop, executes tool calls against the real shell/filesystem on this host, and writes the final answer to stdout. We capture stdout, log the run, and return.

Credentials come from snappy-settings/.env.cacheANTHROPIC_API_KEY for Anthropic aliases, OPENROUTER_API_KEY for everything else.

Zero vendor lock-in. Swap providers by changing one string. The orchestrator never has to know.

Orchestrator picker discipline#

The kernel rule says use AskUserQuestion to drive forward at genuine forks. That rule has a strong exception for routine progression once direction is set.

When Robert has set a direction ("make M1 perfect", "push as hard as you can", "keep going", or any instruction that implies sequential execution across obvious next units), do not stop after each pod wave to surface a picker menu. Dispatch the next logical wave and report outcomes in-line.

Why: Robert's system (kernel + skills + memory + positioning) already encodes his preferences. Interrupting to confirm what is already documented is a tax on him and signals the system is not doing its job. He called this out explicitly on 2026-04-14 after the Skool M1 audit: "just keep going launch more pods — the point of this system is that I don't have to be asked for things all the time because the system has everything already explained."

How to apply:

  • Use AskUserQuestion ONLY for genuinely ambiguous forks the system cannot resolve from memory/skills/CLAUDE.md. Content direction pivots, scope redefinitions, or truly blocking choices.
  • Routine progression (verify → fix → next module → publish flow, inspect → draft → lint → push, M1 → M2 → M3) should just run. Report progress as short in-line status lines, not questions.
  • Exception — irreversible blast radius: actions that post publicly, save to Skool, send DMs, or otherwise cannot be un-done still get a confirm per CLAUDE.md "Executing actions with care." Frame as a short single yes/no ("about to push M2 to live Skool, stop me if not") — never a 4-option menu.
  • When you do surface something, prefer short status + single confirm over a picker with alternatives.

Gotchas#

  • The dispatched agent has full shell access with whichever tools you grant. Only pass tools it needs.
  • There is no session memory across dispatches. If you need multi-turn, either make the prompt self-contained or run multiple dispatches and assemble in the orchestrator.
  • pi occasionally emits the "Provider finish_reason: error" line in text mode on the first try — if r.ok === false and stderr is empty, retry once before failing.
  • The default 120s timeout is aggressive — raise it for browser automation or multi-file refactors.
  • Don't dispatch anything that requires Robert's judgment. That's what the orchestrator is for.

<!-- SKILL-INDEX-START -->

[snappy-dispatch Index]|root: ~/.claude/skills/snappy-dispatch|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md}

<!-- SKILL-INDEX-END -->

Used by#

  • snappy-cleanshot
  • snappy-os-operator

<!-- SNAPPY-CONTRACT-VERBS-START -->

Contract verbs#

Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.

This skill declares no executable verbs. Its instruction-only label is intentional.

Show the result#

When an answer carries face_hint, show it with one snappy_present(<answer>) call.

See /snappy-faces for face selection. Human-facing images must crop to the

element, render at 2x on Retina, and fill the destination channel instead of

placing a small card in a full-page screenshot.

<!-- SNAPPY-CONTRACT-VERBS-END -->

---
name: snappy-dispatch
role: Sub-agent dispatch to cheap models via pi-coding-agent
loaded-by: preload-skill-context hook
---

# snappy-dispatch — Agent Loader

**This is the cheap-labor lever for the whole Snappy system.** The orchestrator (Sonnet/Opus) stays the central point. Grunt work — file listings, greps, small refactors, data extraction, running scripts, parsing JSON, retry loops, quick classifications — dispatches here. Every dispatch costs sub-penny and completes in 2–4 seconds.

## The rule of thumb

**If a task is mechanical and verifiable, dispatch it. If it needs judgment, keep it.**

Mechanical + verifiable:
- "count files matching X"
- "grep for pattern Y and return matches"
- "run `npm test` and extract the failing test names"
- "parse this JSON log, return errors only"
- "is the URL still 200?"

Needs judgment (do NOT dispatch):
- architectural decisions
- writing the content Robert will publish
- classifying ambiguous human messages
- choosing which fix to apply
- writing the plan the user aligns on

## API (TypeScript)

```typescript
import { dispatch } from "../snappy-dispatch/api.ts";

const r = await dispatch({
  prompt: "Run npm test in /path/to/repo and list failing tests one per line.",
  model: "haiku",                        // alias or "provider/model-id"
  tools: ["bash", "read", "grep"],       // default: read,bash,grep,ls
  timeoutMs: 60_000,                     // default: 120_000
});

if (!r.ok) throw new Error(r.error || r.stderr);
console.log(r.output);
```

## CLI

```bash
# Shortest form — defaults to haiku
npx tsx ~/.claude/skills/snappy-dispatch/api.ts "count files in /tmp"

# Pick a different brain
npx tsx ~/.claude/skills/snappy-dispatch/api.ts --model gemini "grep foo in bar.ts"

# Limit tools
npx tsx ~/.claude/skills/snappy-dispatch/api.ts --model haiku --tools bash "..."
```

## Model aliases (validated 2026-04-13)

| Alias       | Provider   | Model                                  | When to use |
|-------------|-----------|----------------------------------------|-------------|
| `gemini`    | openrouter| google/gemini-2.5-flash                | **Default.** Fast, cheap, OpenRouter-backed. Works out of the box. |
| `haiku`     | anthropic | claude-haiku-4-5                       | Only works if `ANTHROPIC_API_KEY` is set in `.env.cache` (normally empty in Claude Code — parent session reuse). |
| `sonnet`    | anthropic | claude-sonnet-4-6                      | Same caveat as haiku. Use only when judgment > speed. |
| `gemini-pro`| openrouter| google/gemini-2.5-pro                  | Hard reasoning at Gemini prices. |
| `llama`     | openrouter| meta-llama/llama-3.3-70b-instruct      | Fully OSS path, no proprietary dependency. |
| `qwen`      | openrouter| qwen/qwen-2.5-72b-instruct             | OSS alternative, strong tool use. |
| `deepseek`  | openrouter| deepseek/deepseek-chat                 | Very cheap OSS option. |

Full `provider/model-id` strings also work (e.g. `anthropic/claude-haiku-4-5`, `openrouter/mistralai/mistral-large`).

## Logging

Every dispatch appends one line to `~/.claude/logs/dispatches.ndjson`:

```json
{"ts":"2026-04-13T...","promptPreview":"count files...","tools":"read,bash,grep,ls","ok":true,"output":"15","durationMs":2743,"provider":"anthropic","model":"claude-haiku-4-5","exitCode":0,"outputPreview":"15"}
```

Use this for cost audits, latency profiling, and debugging when a dispatch returns an empty string.

## How it works underneath

`dispatch()` shells to `pi` (the `@mariozechner/pi-coding-agent` CLI from `badlogic/pi-mono`) with `--print --no-session` and the chosen provider/model/tools. `pi` runs the agent loop, executes tool calls against the real shell/filesystem on this host, and writes the final answer to stdout. We capture stdout, log the run, and return.

Credentials come from `snappy-settings/.env.cache` — `ANTHROPIC_API_KEY` for Anthropic aliases, `OPENROUTER_API_KEY` for everything else.

**Zero vendor lock-in.** Swap providers by changing one string. The orchestrator never has to know.

## Orchestrator picker discipline

The kernel rule says use `AskUserQuestion` to drive forward at genuine forks. That rule has a **strong exception for routine progression once direction is set.**

**When Robert has set a direction** ("make M1 perfect", "push as hard as you can", "keep going", or any instruction that implies sequential execution across obvious next units), **do not stop after each pod wave to surface a picker menu.** Dispatch the next logical wave and report outcomes in-line.

**Why:** Robert's system (kernel + skills + memory + positioning) already encodes his preferences. Interrupting to confirm what is already documented is a tax on him and signals the system is not doing its job. He called this out explicitly on 2026-04-14 after the Skool M1 audit: *"just keep going launch more pods — the point of this system is that I don't have to be asked for things all the time because the system has everything already explained."*

**How to apply:**
- Use `AskUserQuestion` ONLY for genuinely ambiguous forks the system cannot resolve from memory/skills/CLAUDE.md. Content direction pivots, scope redefinitions, or truly blocking choices.
- Routine progression (verify → fix → next module → publish flow, inspect → draft → lint → push, M1 → M2 → M3) should just run. Report progress as short in-line status lines, not questions.
- **Exception — irreversible blast radius:** actions that post publicly, save to Skool, send DMs, or otherwise cannot be un-done still get a confirm per CLAUDE.md "Executing actions with care." Frame as a short single yes/no ("about to push M2 to live Skool, stop me if not") — never a 4-option menu.
- When you do surface something, prefer short status + single confirm over a picker with alternatives.

## Gotchas

- The dispatched agent has **full shell access** with whichever tools you grant. Only pass tools it needs.
- There is no session memory across dispatches. If you need multi-turn, either make the prompt self-contained or run multiple dispatches and assemble in the orchestrator.
- `pi` occasionally emits the "Provider finish_reason: error" line in text mode on the first try — if `r.ok === false` and stderr is empty, retry once before failing.
- The default 120s timeout is aggressive — raise it for browser automation or multi-file refactors.
- Don't dispatch anything that requires Robert's judgment. That's what the orchestrator is for.

<!-- SKILL-INDEX-START -->
[snappy-dispatch Index]|root: ~/.claude/skills/snappy-dispatch|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md}
<!-- SKILL-INDEX-END -->

## Used by

- `snappy-cleanshot`
- `snappy-os-operator`

<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs

Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.

This skill declares no executable verbs. Its `instruction-only` label is intentional.

## Show the result

When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->

Keyboard Shortcuts

Search in document⌘K
Focus search/
Previous file tab
Next file tab
Close overlayEsc
Show shortcuts?