Create git worktrees and delegate tasks to AI agents.
Copy wt-spawn to a bin directory (eg, ~/.local/bin). You also need:
- bash >= 4.0 (macOS ships bash 3.2)
- worktrunk
- jq
- Claude Code or OpenCode (works out of the box); or configure your favourite agent
- GitHub CLI (
gh) — for draft PR creation
macOS users can use brew install bash jq gh max-sixty/tap/worktrunk to install prerequisites.
Use wt-spawn to start a new piece of work. Pass a prompt with a supported agent (eg, #sonnet is Claude Code with Sonnet).
wt-spawn "#sonnet implement the plan in ~/.plans/refactor-auth.md"
# or:
wt-spawn # open text editor
wt-spawn @prompt.txt # via file
echo "add dark mode" | wt-spawn - # via stdinThis will:
- Come up with a branch name automatically
- Create a branch and worktree via worktrunk
- Create an empty draft PR
- Spawn Claude Code with the Sonnet model in your multiplexer (eg, cmux, Herdr, Zellij, Tmux)
One shotting trivial work. For simple changes, it should be as easy as possible to open a new PR.
wt-spawn "#sonnet fix test failure in path/to/my.test.ts"
wt-spawn "#sonnet change spelling from American to British English in src/components/Onboarding/"Agentic orchestration. Ask your coding agent to use wt-spawn directly.
Read 'wt-spawn --help' for context.
Spawn 3 #opus agents for: create DB migration, update docs, and UI stub implementstion.
Implementing plans. Keep one Claude session for planning, then spin off implementation via wt-spawn.
/plan add rate limiting to /videos API
# ...many turns later...
approved. run wt-spawn "#sonnet #implement <plan file>"
wt-spawn automatically creates workspaces using the multiplexer being used. No configuration or parameters needed, it will auto-detect whatever may be running. Supported multiplexers:
With none of these running, wt-spawn runs the agent inline in the current terminal instead — which blocks the caller until the agent exits. --json (see below) refuses to run at all in that case, since it exists for non-interactive callers that can't block.
--json prints {"branch": "...", "pr": "https://github.com/.../pull/1" | null} to stdout after the agent launches — nothing else touches stdout, so wt-spawn --json ... | jq . is safe. Requires a multiplexer (see above); exits 2 immediately otherwise, before touching $EDITOR or making any inference call.
wt-spawn --json -a sonnet -b feat/add-redis "add redis caching"
# {"branch":"feat/add-redis","pr":"https://github.com/you/repo/pull/42"}Generate default config:
wt-spawn --initPrint defaults without writing:
wt-spawn --print-default-configConfig location: ${XDG_CONFIG_HOME:-~/.config}/wt-spawn/config.sh
Prompt templates are available via -t/--template or via hashtags. Both of these do the same thing:
wt-spawn "#plan fix issue XYZ-1234"
wt-spawn -t "plan" "fix issue XYZ-1234"
# Expands to the prompt:
#
# Create a plan. Details: fix issue XYZ-1234They can be configured. Here are the defaults:
# ~/.config/wt-spawn/config.sh
PROMPT_TEMPLATES[plan]='Create a plan. Details:'
PROMPT_TEMPLATES[implement]='/goal Implement this plan as described, ensure PR title and description are accurate and sensible. Ultrathink. Plan:'Supports the following agents by default, and more can be added via custom configuration.
- Claude Code
- Pi
- Codex
- OpenCode
- ...bring your own via config
Agents can be chosen using -a/--agent <name> or by hashtag. Both of these do the same thing:
wt-spawn -a "sonnet" "translate README.md to French, and save to README.fr.md"
wt-spawn "#sonnet translate README.md to French, and save to README.fr.md"Agents can be configured. Here are the defaults:
# ~/.config/wt-spawn/config.sh
AGENTS[claude]='claude'
AGENTS[codex]='codex --sandbox workspace-write --ask-for-approval never'
AGENTS[fable]='claude --model fable'
AGENTS[gpt-5.4]='codex --sandbox workspace-write --ask-for-approval never --model gpt-5.4'
AGENTS[gpt-5.5]='codex --sandbox workspace-write --ask-for-approval never --model gpt-5.5'
AGENTS[haiku]='claude --model haiku'
AGENTS[opencode]='opencode --auto --prompt'
AGENTS[opus]='claude --model opus'
AGENTS[pi]='pi'
AGENTS[sonnet]='claude --model sonnet'Branch/workspace name inference runs through a small, cheap model call. Configuration:
# Harness used to generate branch and workspace names
# "auto" (default), "pi", "claude", or "opencode"
# auto tries claude first, then opencode
INFER_HARNESS=auto
# empty = auto-pick per harness:
# claude → haiku, pi → openai-codex/gpt-5.4-mini, opencode → opencode/big-pickle
INFER_MODEL=
# String to prefix branches with (eg, `feat/otel-instrumentation`)
BRANCH_PREFIX=feat
# Customize the prompt used (see wt-spawn --print-default-config for full prompt)
INFER_PROMPT="..."Use --branch NAME (or -b) to bypass LLM inference and set the branch name directly.
The workspace display name is derived automatically from the branch.
wt-spawn -a sonnet --branch feat/add-redis "add redis caching"
# branch = feat/add-redis, workspace name = Add redisIf NAME already exists as a local branch, wt-spawn reuses its worktree instead of creating a new one — this only applies to an explicitly given -b, never an inferred name. This is a deliberate trade-off: unlike other flags, an existing branch no longer fails loudly — it's silently reused, which loses a typo-guard but is what tools resuming their own work need.
Every new worktree gets an empty draft PR so you can track work in GitHub as it happens. The PR title is the workspace name inferred from your prompt. Reusing an existing branch (see above) skips this and looks up its open PR instead — if auto PR creation is enabled and none is found, one gets created, same as new.
Configuration:
# ~/.config/wt-spawn/config.sh
# 0 = disable, 1 = enable
AUTO_CREATE_DRAFT_PR=1
# Message for the empty initial commit
INITIAL_COMMIT_MESSAGE="initial commit for branch"It's enabled by default. --pr and --no-pr flags are available to override this:
# Skip the draft PR for a quick one-off:
wt-spawn --no-pr "#sonnet fix typo in README"