Programmatic T3 Code thread launcher. Spawn Claude Code agents in isolated git worktrees via the T3 Code API — one at a time or in configurable batches.
- Spawn T3 threads with any prompt, inline or from files
- Full T3 settings control — model, mode, effort, context window, thinking, fast mode
- Branch management — work on existing branches or create new ones (with fork support)
- Batch processing — launch 30+ tasks with configurable batch size and delays
- Plan approval — freeze and approve captured T3 plans in quota-aware batches
- PR review — fetch GitHub PR review threads and spawn agents to address them
- Local PR review — spawn a reviewer thread per PR that generates a full review (verdict + paste-ready comments) you can read
- PR triage — one-shot status report across all open PRs (conflicts, CI, reviews, ready)
- Conflict resolution — resolve merge conflicts across every conflicting branch with one command
- Auto-detection — T3 connection, project ID, and GitHub repo detected automatically
- Config system — TOML config files (global + per-project) with env var and CLI overrides
- Python 3.11+ (uses
tomllibfrom stdlib) - T3 Code running locally
gitCLIghCLI (for thepr,triage, andconflictscommands)- No pip dependencies — stdlib only
Models & effort. The
opusalias maps to Claude Opus 4.8, which needs T3 Code's bundled Claude Code CLI ≥ 2.1.154 (Opus 4.7 needs ≥ 2.1.111). d3 prefers T3's cached provider metadata when present and sends only the options each known model supports. If a configured alias/known built-in model disappears from T3's cache, d3 fails before dispatch; raw custom/new model ids pass through without option assumptions. Unsupported known-model effort values normalize to the highest real effort; unsupportedcontext_window = "1m"falls back to200k. See Model Validation for the monthly ping-pong test workflow.
# Clone the repo
git clone https://github.com/dvddvd300/d3-thread-spawner.git
cd d3-thread-spawner
# Make the entry point executable (already done if you cloned)
chmod +x d3-spawn
# Spawn a single thread
./d3-spawn spawn "Fix the authentication bug" --repo ~/my-project
# Dry run — see what would launch without doing it
./d3-spawn spawn "Fix it" --dry-run
# Create a project config
./d3-spawn config --initNo installation needed. Clone and run directly:
# Option 1: Run the entry point
./d3-spawn spawn "your prompt"
# Option 2: Run as Python module
python3 -m d3_thread_spawner spawn "your prompt"
# Option 3: Symlink to your PATH
ln -s $(pwd)/d3-spawn ~/.local/bin/d3-spawn# Inline prompt
d3-spawn spawn "Refactor the payment service error handling"
# From a file
d3-spawn spawn --file ~/prompts/refactor-payments.txt
# Batch from JSONL (one task per line)
d3-spawn spawn --from-file tasks.jsonl
# With a custom name and branch
d3-spawn spawn "Add test coverage" --name add-tests --new-branch feature/tests
# Work on an existing branch (no new branch)
d3-spawn spawn "Fix lint errors" --branch feature/my-feature
# Fork from a specific branch
d3-spawn spawn "Cherry-pick fix" --new-branch hotfix/auth --fork-from release/v2
# Use a custom prompt template
d3-spawn spawn "PROJ-123: Fix login timeout" --template ~/prompts/my-template.txt
# Override settings
d3-spawn --model sonnet --mode build --access full --effort max spawn "Quick fix"Fetches unresolved review threads from GitHub PRs and spawns agents to address them. Works with CodeRabbit, human reviewers, or any tool that leaves PR comments.
# All unresolved comments on PR #58
d3-spawn pr 58
# Only CodeRabbit comments
d3-spawn pr 58 --reviewer coderabbitai
# Multiple PRs
d3-spawn pr 58 61
# All my open PRs with pending reviews
d3-spawn pr --open --mine
# One agent per review thread (most granular)
d3-spawn pr 58 --per-thread
# Include resolved/outdated threads
d3-spawn pr 58 --include-resolved --include-outdated
# Wait out a short rate-limit reset and resume (instead of aborting)
d3-spawn pr --open --mine --reviewer coderabbitai --wait
# Ignore the local cache and re-fetch everything
d3-spawn pr --open --no-cacheRate limits & caching. Review threads are fetched with batched GraphQL
queries and cached locally (keyed by each PR's updatedAt), so repeated runs
only re-fetch PRs that actually changed. When GraphQL is rate-limited the tool
automatically falls back to the REST API (a separate budget); --wait instead
sleeps until the GraphQL budget resets (capped by --wait-max-seconds, default
300) and resumes, which preserves resolved/outdated filtering. If a hard limit is
hit mid-run, already-fetched PRs are still launched and a resume command for the
rest is printed.
pr flag |
Description |
|---|---|
--reviewer LOGIN |
Only threads whose author matches LOGIN ([bot] suffix ignored) |
--per-thread |
One agent per thread (default: one per PR) |
--include-resolved / --include-outdated |
Include resolved / outdated threads |
--wait |
On rate-limit, sleep until reset and resume on GraphQL |
--wait-max-seconds N |
Cap for --wait; beyond it, fall back to REST (default 300) |
--no-cache |
Ignore the local PR-thread cache and re-fetch |
Where pr addresses existing review comments, review generates the
review. It spawns one autonomous T3 thread per pull request that acts as a
senior code reviewer: each thread checks out the PR branch read-only,
follows a thorough review methodology (scope, sync, N+1 / efficiency, data-type
& contract analysis, bug-risk audit, comment coverage, conventions), and posts a
complete review — verdict, paste-ready comments tagged 🔴/🟡/🟢, and an action
items table — as its thread output. One thread per PR, so you can read each
review on its own.
# Review one PR
d3-spawn review 58
# Review several
d3-spawn review 58 61
# Review all my open PRs (one reviewer thread each)
d3-spawn review --open --mine
# Review every open PR
d3-spawn review --open
# Use your own reviewer methodology instead of the bundled one
d3-spawn review --open --mine --review-prompt ~/my-review-guide.mdThe reviewer threads are read-only — they don't edit, commit, or push; the
review itself is the deliverable. The methodology is shipped with the tool
(d3_thread_spawner/review_prompt.md) and
is stack-agnostic (it detects the language/framework/ORM from the repo). Point
--review-prompt (or [review] prompt_file) at your own file to customize it.
review flag |
Description |
|---|---|
--open |
Review all open PRs (combine with --mine) |
--mine |
Only my PRs |
--review-prompt PATH |
Custom reviewer methodology file (default: bundled guide) |
prvsreview:pr --reviewer coderabbitaifetches CodeRabbit's comments and spawns agents to fix them;reviewspawns agents to be the reviewer and write the review for you to read.
Prints a grouped status report for every open PR — merge conflicts, failing CI,
changes requested, behind base, awaiting review, ready to merge, draft — from a
single gh call. Read-only by default; add --resolve-conflicts to also spawn
conflict-resolution threads for the conflicting ones.
# Status report for all open PRs
d3-spawn triage
# Only my PRs
d3-spawn triage --mine
# Specific PRs
d3-spawn triage 58 61
# Report, then resolve every conflicting PR in one go
d3-spawn triage --resolve-conflictsExample output:
🔴 CONFLICTS (2)
#58 ← feature/auth Fix auth timeout @alice ci:✅ rev:✋
#61 ← feature/pages Add pagination @bob ci:❌
🟢 READY TO MERGE (3)
...
Summary: 2 conflicts · 1 ci failing · 3 ready to merge
💡 Resolve all 2 conflict(s): d3-spawn conflicts
Finds open PRs that conflict with their base branch (or the PRs you name) and spawns one autonomous T3 thread per branch. Each agent merges the base in, resolves the conflicts (preserving both sides' intent), runs tests/lint, and pushes — stopping only if a conflict is genuinely ambiguous or verification fails. One command, all the branches.
# Resolve conflicts on every conflicting open PR (merge strategy)
d3-spawn conflicts
# Only my PRs
d3-spawn conflicts --mine
# Specific PRs
d3-spawn conflicts 58 61
# Rebase onto base instead of merging (force-pushes with lease)
d3-spawn conflicts --rebase
# Go slow: one branch at a time, two minutes between launches
d3-spawn conflicts --mine --rebase --batch-size 1 --batch-delay 2Conflict launches are batched like everything else, but can be paced
independently of ordinary spawn runs via the [conflicts] config keys
(batch_size, batch_delay, launch_delay, initial_wait) — unset keys
inherit [batch]. This is handy for --rebase, which force-pushes each branch:
set [conflicts] batch_size = 1 and a batch_delay to roll them out one at a
time. The global --batch-size/--batch-delay flags also apply for a one-off
slow run.
conflicts / triage flag |
Description |
|---|---|
--mine |
Only my PRs |
--merge |
Merge base into the branch (no force-push) — the default |
--rebase |
Rebase the branch onto base (force-pushes with --force-with-lease) |
--force-rebase-protected |
Let --rebase force-push a protected (shared/long-lived) branch; off by default |
--resolve-conflicts |
(triage only) launch conflict resolution after the report |
Shared-branch guard.
--rebaseforce-pushes, which rewrites history. On a shared/long-lived branch (dev,main,develop,release/*, …) that breaks every open PR based on it (their diffs explode) and forces teammates' clones to diverge. So under--rebase, a head branch matching[conflicts] protected_branchesis automatically downgraded to merge (a normal push, no rewrite) — the conflict is still resolved, safely. Pass--force-rebase-protected(or set[conflicts] rebase_protected = true) only when you truly intend to rewrite that branch. Feature/PR branches rebase as normal.
GitHub computes mergeability asynchronously, so PRs that report UNKNOWN are
re-checked once before the conflicting/clean split is finalized.
approve-plans freezes the selected actionable plans before it waits, then uses
T3's native plan-implementation turn in configurable batches. Full thread ids or
unique prefixes are accepted in the exact order supplied; with no ids it freezes
all currently actionable plans in the selected project.
Start with a dry run. This is also the easiest way to discover the short thread ids: it reads T3's current project state and prints the exact manifest without waiting, authenticating, or approving anything.
d3-spawn --repo /path/to/repo --dry-run approve-plansTargeting by T3 project id works too and does not require a local Git checkout:
d3-spawn --project-id PROJECT_UUID --dry-run approve-plansPass the printed thread ids in the order they should run. Global batch flags
must appear before approve-plans; command-specific flags go after it.
# Approve now: five plans, wait ten minutes, check quota, then continue
d3-spawn --repo /path/to/repo \
--batch-size 5 --batch-delay 10 --launch-delay 0.5 \
approve-plans abc12345 def67890 0123abcd --quota-threshold 90The interactive confirmation prints and freezes the complete manifest. Passing
--yes confirms it without a prompt, which is required for unattended runs.
Omit thread ids only when every actionable plan in the project should be
approved; the list is still frozen before any wait begins.
Use an offset-aware ISO 8601 timestamp for an absolute start time:
d3-spawn --repo /path/to/repo \
--batch-size 5 --batch-delay 10 --launch-delay 0.5 \
approve-plans abc12345 def67890 0123abcd \
--start-at 2026-07-10T02:50:00-06:00 \
--quota-threshold 90 --yesA past --start-at starts immediately. For a relative delay, use the global
--initial-wait flag instead:
d3-spawn --repo /path/to/repo \
--initial-wait 161 --batch-size 5 --batch-delay 10 \
approve-plans abc12345 def67890 --quota-threshold 90 --yes--start-at and --initial-wait are mutually exclusive.
After each non-final batch, d3 waits --batch-delay minutes and reads fresh
Claude account.rate-limits.updated events from T3's local provider logs. The
next batch starts only when all safety checks pass.
| Condition | Result |
|---|---|
Five-hour utilization is below --quota-threshold |
Continue |
| Quota is rejected or reaches the threshold | Stop; leave remaining plans untouched |
| No fresh Claude quota signal exists | Stop instead of guessing |
| A linked implementation turn errors | Stop subsequent batches |
| A frozen plan was changed or manually approved | Skip it; never substitute a newer plan |
| A thread is already running or starting | Skip it; never interrupt the thread |
The command does not wait for another quota reset after a safety stop. Run a new dry run later; already implemented plans disappear from the actionable list, so you can schedule only the remaining ids.
The command normally stays in the foreground and prints the countdown, each
approval, quota decisions, and its final approved/skipped/failed/remaining
summary. Keep that terminal open, use tmux/screen, or detach it from a normal
user shell while saving a log:
mkdir -p ~/.config/d3ts/logs
PYTHONUNBUFFERED=1 nohup d3-spawn --repo /path/to/repo \
--batch-size 5 --batch-delay 10 --launch-delay 0.5 \
approve-plans abc12345 def67890 0123abcd \
--start-at 2026-07-10T02:50:00-06:00 \
--quota-threshold 90 --yes \
> ~/.config/d3ts/logs/approve-plans.log 2>&1 &
pid=$!Monitor or cancel that detached process with:
tail -f ~/.config/d3ts/logs/approve-plans.log
kill "$pid"T3 Code and the machine must remain running. System sleep delays the process until the machine wakes; closing T3 before dispatch makes the command fail safely without marking the plan implemented.
| Option | Meaning |
|---|---|
THREAD_REF ... |
Ordered full thread ids or unique prefixes; default is all actionable plans |
--start-at TIME |
Offset-aware absolute time for the first batch |
--quota-threshold PERCENT |
Stop at this five-hour utilization; default 90 |
--yes |
Confirm the frozen manifest without prompting |
Global --batch-size N |
Plans per batch; default 5 |
Global --batch-delay M |
Minutes between non-final batches |
Global --launch-delay S |
Seconds between approvals within a batch |
Global --initial-wait M |
Relative minutes before the first batch |
Global --dry-run |
Print the frozen manifest without waits or mutations |
d3-spawn statusspawn dispatches a turn and returns a thread id but does not read the reply.
output reads it back from T3's local state DB (read-only), so you can view or
poll for what an agent produced. Accepts a full thread id or the short prefix
status prints.
d3-spawn output 1a2b3c4d # print the latest turn's assistant output
d3-spawn output 1a2b3c4d --wait # block until the turn finishes, then print
d3-spawn output 1a2b3c4d --json # machine-readable {state, text, ...}d3-spawn clean # remove launcher scripts
d3-spawn clean --worktrees # also remove ALL worktrees (destructive!)d3-spawn config # show resolved configuration
d3-spawn config --init # create .d3ts.toml template in CWD
d3-spawn config --path # show which config files are loadedConfiguration is resolved in order (later wins):
- Built-in defaults
- Global config:
~/.config/d3ts/config.toml - Project config:
.d3ts.tomlin your repo root - Environment variables:
D3TS_*prefix - CLI flags
Create with d3-spawn config --init or manually:
# .d3ts.toml (in your repo root)
[general]
model = "opus" # opus, sonnet, haiku, mini, or full model ID
mode = "build" # build | plan (interaction mode)
access = "full" # full | auto-accept | supervised (access level)
effort = "high" # Codex: low | medium | high | xhigh
# GPT-5.6 Sol/Terra also allow max | ultra;
# Luna also allows max (never ultrathink)
# Claude: low | medium | high | xhigh | max | ultracode | ultrathink
base_branch = "main"
[batch]
size = 5 # threads per batch
delay = 0 # minutes between batches
launch_delay = 0.5 # seconds between individual launches
initial_wait = 0 # minutes to wait before first batch
[t3]
project_id = "" # auto-detected from T3 state if empty
[worktree]
dir = "~/d3ts-worktrees/{project}" # {project} = repo dir name
[github]
# repo = "owner/name" # auto-detected from git remote
[review]
# prompt_file = "~/my-review-guide.md" # custom reviewer methodology for the
# `review` command (default: the bundled generic guide)
[conflicts]
strategy = "merge" # "merge" (base into branch) or "rebase" (onto base)
# Safety guard: under "rebase", a head branch matching protected_branches is
# auto-downgraded to merge (force-pushing a shared branch rewrites history that
# dependent PRs and clones rely on). Override with --force-rebase-protected.
# protected_branches = ["main", "master", "develop", "dev", "staging", "stage", "production", "prod", "release", "next", "trunk"]
# rebase_protected = false # true ⇒ allow rebasing protected branches anyway
# Batch pacing for conflict resolution, overriding [batch] for this command only.
# Unset keys inherit [batch], so conflicts run at the normal pace unless slowed
# here — useful for --rebase (force-pushes each branch) to avoid hammering CI.
# batch_size = 1 # conflict threads per batch (default: inherit [batch])
# batch_delay = 2 # minutes between conflict batches (default: inherit)
# launch_delay = 1.0 # seconds between individual conflict launches (default: inherit)
# initial_wait = 0 # minutes before the first conflict batch (default: inherit)
[models]
opus = "claude-opus-4-8" # needs T3's Claude Code CLI >= 2.1.154
sonnet = "claude-sonnet-4-6"
haiku = "claude-haiku-4-5"
mini = "gpt-5.4-mini"
[model_options]
context_window = "1m" # 1m when supported; otherwise d3 uses 200k
thinking = true # Haiku 4.5 only
fast_mode = false # Opus 4.5/4.6 only| Variable | Description |
|---|---|
D3TS_T3_TOKEN |
Explicit T3 session token (skips cookies DB lookup) |
D3TS_T3_PROJECT_ID |
T3 project UUID |
D3TS_MODEL |
Default model |
D3TS_MODE |
Interaction mode (build/plan) |
D3TS_ACCESS |
Access level (full/auto-accept/supervised) |
D3TS_EFFORT |
Default effort level |
D3TS_BASE_BRANCH |
Default base branch |
D3TS_BATCH_SIZE |
Default batch size |
D3TS_INITIAL_WAIT |
Minutes to wait before first batch |
D3TS_GITHUB_REPO |
GitHub repo (owner/name) |
D3TS_WAIT |
Auto-wait for rate-limit reset (true/false) |
D3TS_WAIT_MAX_SECONDS |
Cap for auto-wait (default 300) |
D3TS_CACHE |
Use the local PR-thread cache (true/false) |
D3TS_CACHE_DIR |
PR-thread cache location |
D3TS_REVIEW_PROMPT |
Custom reviewer methodology file for the review command |
D3TS_CONFLICT_STRATEGY |
Conflict resolution strategy (merge or rebase) |
D3TS_CONFLICT_REBASE_PROTECTED |
Allow rebasing protected/shared branches (true/false; default false) |
D3TS_CONFLICT_BATCH_SIZE |
Conflict threads per batch (default: inherit [batch]) |
D3TS_CONFLICT_BATCH_DELAY |
Minutes between conflict batches (default: inherit) |
D3TS_CONFLICT_LAUNCH_DELAY |
Seconds between individual conflict launches (default: inherit) |
D3TS_CONFLICT_INITIAL_WAIT |
Minutes before the first conflict batch (default: inherit) |
For launching many tasks, create a JSONL file (one JSON object per line):
{"name": "fix-auth", "prompt": "Fix the auth timeout bug", "new_branch": "bugfix/auth"}
{"name": "add-pagination", "prompt": "Add pagination to /users", "new_branch": "feature/pagination"}
{"name": "update-tests", "prompt": "Update payment service tests", "branch": "dev"}| Field | Required | Description |
|---|---|---|
name |
yes | Thread name (used for worktree directory) |
prompt |
one of these | Inline prompt text |
prompt_file |
one of these | Path to a prompt file |
branch |
no | Existing branch to work on |
new_branch |
no | Create a new branch |
fork_from |
no | Branch to fork from (with new_branch) |
model |
no | Override model for this task |
mode |
no | Override interaction mode (build/plan) |
access |
no | Override access level (full/auto-accept/supervised) |
effort |
no | Override effort for this task |
context_window |
no | Override context window for this task |
thinking |
no | Override thinking for this task |
fast_mode |
no | Override fast mode for this task |
Launch with:
d3-spawn spawn --from-file tasks.jsonl --batch-size 10 --batch-delay 5
# Wait 2 hours before starting, then launch 2 threads every 32 minutes
d3-spawn spawn --from-file tasks.jsonl --initial-wait 120 --batch-size 2 --batch-delay 32d3-spawn connects to T3 Code's local HTTP API. Connection details are auto-detected:
- Host/Port: Read from
~/.t3/userdata/server-runtime.json - Session Token: Extracted from T3's cookies database, or set
D3TS_T3_TOKEN - Project ID: Matched from
~/.t3/userdata/state.sqliteby repo path, or set in config
- Creates a git worktree for the task
- Dispatches
thread.createto T3's orchestration API - Dispatches
thread.turn.startwith the prompt - The thread appears in T3 Code's sidebar, running autonomously
All flags go before the subcommand:
d3-spawn [flags] <command> [command-flags]| Flag | Description |
|---|---|
--model MODEL |
Model alias (opus→4.8, sonnet, haiku, mini→gpt-5.4-mini) or full ID |
--mode MODE |
Interaction mode: build or plan |
--access LEVEL |
Access level: full, auto-accept, or supervised |
--effort LEVEL |
Codex values vary by model: low, medium, high, xhigh; GPT-5.6 Sol/Terra add max and ultra, Luna adds max. Claude: low, medium, high, xhigh, max, ultracode, ultrathink. Unsupported known-model values normalize to the model max |
--context-window SIZE |
200k or 1m; unsupported values normalize before launch |
--thinking / --no-thinking |
Enable/disable thinking (Haiku 4.5 only) |
--fast-mode / --no-fast-mode |
Enable/disable fast mode (Opus 4.5/4.6 only) |
--batch-size N |
Threads per batch |
--batch-delay M |
Minutes between batches |
--launch-delay S |
Seconds between launches |
--initial-wait M |
Minutes to wait before first batch |
--base-branch BRANCH |
Base git branch |
--repo PATH |
Path to repository |
--project-id UUID |
T3 project ID |
--dry-run |
Preview without launching or approving |
--verbose / -v |
Verbose output |
--config PATH |
Explicit config file |
Create your own prompt templates with {variable} placeholders:
You are working on a NestJS backend. Read CLAUDE.md for conventions.
TASK: {task}
Follow these steps:
1. Understand the problem
2. Plan your approach
3. Implement and test
4. Commit and push
Use with:
d3-spawn spawn "Fix the login bug" --template my-template.txt
d3-spawn spawn "PROJ-123" --template my-template.txt --var task_id=PROJ-123See examples/prompts/ for more template examples.
GPLv3 — see LICENSE.