Ship code in one command. Repo + task in, PR out.
laptop → server → chosen agent provider → PR
Also runs locally with --local, no server needed.
Requires Bun. macOS and Linux.
bun install -g oneshot-shiponeshot init # configure
oneshot doctor # check local + remote setup
oneshot doctor --repo my-org/my-app # verify a checkout target
oneshot my-org/my-app "fix the login timeout" # shiponeshot runs an 8-step pipeline. Each run gets its own git worktree in /tmp, so your main branch is never touched. Parallel runs on the same repo are safe.
| Step | Engine | What it does |
|---|---|---|
| 1. Validate | git | Checks the repo exists, fetches latest |
| 2. Worktree | git | Creates an isolated /tmp worktree from origin/main |
| 3. Route | Homebrew | Picks provider, reasoning, context shape, execution style, and fast/deep mode |
| 4. Plan | Routed | Reads the codebase + CLAUDE.md, outputs an implementation plan |
| 5. Execute | Routed | Implements the plan |
| 6. Draft PR | Configurable | Creates branch, commits, and writes PR metadata; the runtime opens the draft PR |
| 7. Review | Configurable | Reviews the diff for bugs, types, security. Fixes issues directly |
| 8. Finalize | git/gh | Pushes review fixes, marks PR ready |
If execute times out with partial changes, the draft PR is still created so nothing is lost.
oneshot <repo> "<task>" # ship a task
oneshot <repo> <linear-url> # ship from a Linear ticket
oneshot <repo> "<task>" --bg # fire and forget
oneshot <repo> "<task>" --local # run locally, no SSH
oneshot <repo> "<task>" --mode deep # skip classification and force deep mode
oneshot <repo> "<task>" --deep-review # force exhaustive review
oneshot <repo> "<task>" --model gpt-5.5 # override configured plan/PR model
oneshot <repo> "<task>" --branch dev # target a different branch
oneshot <repo> "<task>" --base-path /srv/workspaces # override repo root for this run
oneshot <repo> --dry-run # validate only
oneshot init # configure
oneshot stats # recent runs + timing
oneshot doctor # setup and remote health checks
oneshot doctor --repo my-org/my-app # setup + checkout health
oneshot route "fix failing CI and publish" --json # inspect the hidden route| Flag | Short | Description |
|---|---|---|
--model |
-m |
Override configured plan/PR model |
--branch |
-b |
Base branch (default: main) |
--base-path |
Override the workspace path used to locate the repo | |
--worktree-root |
Override where temporary git worktrees are created | |
--mode |
Skip classification and force fast or deep mode |
|
--deep-review |
Force exhaustive review mode | |
--local |
Run locally instead of over SSH | |
--bg |
Run detached in background (returns PID + log path) | |
--dry-run |
-d |
Validate only |
--events-file |
Mirror JSONL events to an additional file | |
--repo |
With doctor, verify a specific owner/repo checkout exists |
|
--provider |
With route, choose the fallback provider (codex or claude) |
On your laptop: Bun, SSH access to your server
On your server (or local machine with --local):
- Bun
- Either Codex CLI or Claude Code CLI
- GitHub CLI (authenticated)
OPENAI_API_KEYfor Codex mode, orANTHROPIC_API_KEYfor Claude mode
~/.oneshot/config.json, created by oneshot init:
{
"host": "user@100.x.x.x",
"basePath": "~/projects",
"provider": "codex",
"routing": { "enabled": true },
"linearApiKey": "lin_api_...",
"claude": {
"model": "opus",
"timeoutMinutes": 180
},
"codex": {
"model": "gpt-5.5",
"reasoningEffort": "xhigh",
"reviewModel": "gpt-5.5",
"reviewReasoningEffort": "xhigh",
"timeoutMinutes": 180
},
"phases": {
"classify": { "model": "gpt-5.5", "reasoningEffort": "medium" },
"plan": { "model": "gpt-5.5", "reasoningEffort": "xhigh" },
"execute": {
"model": "gpt-5.5",
"reasoningEffort": "xhigh"
},
"review": {
"model": "gpt-5.5",
"reasoningEffort": "xhigh"
},
"deepReview": {
"model": "gpt-5.5",
"reasoningEffort": "xhigh"
},
"pr": { "model": "gpt-5.5", "reasoningEffort": "high" }
},
"stepTimeouts": {
"planMinutes": 20,
"executeMinutes": 60,
"reviewMinutes": 20,
"deepReviewMinutes": 20,
"prMinutes": 20
}
}Only host is required for SSH runs. Local mode works without a config file.
Remote SSH runs stream the active oneshot config to the server for that run, so basePath, provider defaults, timeout settings, and configured Linear credentials stay aligned without requiring a duplicate ~/.oneshot/config.json on the server.
| Key | Required | Description |
|---|---|---|
host |
SSH only | SSH target, e.g. user@192.168.1.10 |
basePath |
No | Where repos live. Default: ~/projects |
worktreeRoot |
No | Scratch directory for temporary git worktrees. Default: /tmp |
provider |
No | Fallback agent provider when adaptive routing is off or no route rule wins. Default: codex |
routing.enabled |
No | Enables invisible provider/reasoning routing. Codex and Claude still use their configured frontier model; the router varies provider and effort, not model class |
anthropicApiKey |
Claude only | Falls back to ANTHROPIC_API_KEY env var |
linearApiKey |
No | Enables Linear ticket integration |
claude.model |
Claude only | Default Claude model. Default: opus |
codex.model |
Codex only | Default Codex model. Default: gpt-5.5 |
codex.reasoningEffort |
Codex only | Default Codex reasoning effort. Default: xhigh |
codex.reviewModel |
Codex only | Default for review phases. Default: same as codex.model |
codex.reviewReasoningEffort |
Codex only | Default review reasoning effort. Default: same as codex.reasoningEffort |
phases.<phase>.model |
No | Exact model for that phase under the selected provider |
phases.<phase>.reasoningEffort |
No | Reasoning effort for that phase, e.g. medium, high, xhigh. Passed to Codex and to Claude via --effort |
stepTimeouts |
No | Per-step timeout overrides in minutes |
phases is optional. If it is omitted, every agent phase uses the selected provider and its default model settings. Any stale phases.<phase>.provider values from older configs are ignored when adaptive routing is off. With routing.enabled: true, the homebrew router can silently choose Codex or Claude per task while preserving each provider's configured frontier model.
Adaptive routing is intentionally invisible during normal use. Code edits, tests, refactors, PR work, and ship requests route to Codex by default. Tool-heavy operations, browser/admin/log/service work, and external workflow orchestration can route to Claude. If code will be edited, Codex wins the tie. Use oneshot route "<task>" --json only when you want to inspect the decision.
Repos on the server should live as <org>/<repo> under the base path. Repo slugs are intentionally strict: exactly owner/repo, using only letters, numbers, dot, underscore, and hyphen. Nested paths and .. are rejected before any filesystem access.
~/projects/
acme/api/
acme/web/
Pass a Linear URL instead of a task string:
oneshot acme/api https://linear.app/acme/issue/ENG-142- Fetches issue title, description, and comments via GraphQL
- Uses ticket as context for the planning step
- Uses the issue ID in the branch name (
oneshot/eng-142-...) - Moves the ticket to "In Review" and comments the PR URL
Requires linearApiKey in config.
CLAUDE.md: put one in any repo root. oneshot passes it to the configured agents for planning and execution. Use it for coding standards, architecture decisions, test requirements.
Prompt templates: edit these to change pipeline behavior:
| File | Controls |
|---|---|
prompts/plan.txt |
How the plan agent explores and plans |
prompts/execute.txt |
How the execute agent implements changes |
prompts/review.txt |
How the review agent reviews the diff |
prompts/pr.txt |
How the PR agent writes branch/commit/PR metadata |
Templates use {{variable}} placeholders replaced at runtime.
The repo's CLAUDE.md is also supplied to the planning and execution steps, so the task string is the primary operator input, not the only context the agents receive.
For dense specs, explainers, review maps, incident reports, design sheets, or one-off editors, the templates allow a self-contained HTML artifact instead of a long markdown document. Durable artifacts should live under docs/artifacts/; throwaway local artifacts should stay under /tmp/oneshot-html-artifacts/.
Every run writes JSONL events to /tmp/oneshot-<runId>.events.jsonl. Use --events-file <path> to mirror to another file:
oneshot acme/api "fix bug" --local --events-file /tmp/run.events.jsonlEvents:
started(includes runtime metadata such as CLI version, host, pid, cwd, platform, and worktree root),classified,step(running/done/failed),completed(success/failed/dry-run)agentfor live agent activity: commands, tools, file changes, todos, web searches, warnings, draft PR creation, and turn/session markers
oneshot doctor checks the installed package freshness against npm, local prerequisites, config file, recent event stream, SSH reachability, and remote binaries when a remote host is configured. Use oneshot doctor --local --json for machine-readable local checks.
Add --repo <owner/repo> to verify the configured local or remote base path actually contains the checkout before dispatch:
oneshot doctor --repo zkp2p/pay
oneshot doctor --local --repo zkp2p/pay --jsonFailed runs preserve the worktree under the configured worktreeRoot and write a failed completed event with the error code and completed step timings. Start with oneshot stats, then inspect the event file or preserved worktree path printed in the logs.
Works as an Agent Skill in Claude Code, Codex CLI, Cursor, and other compatible agents.
npx skills add ADWilkinson/oneshot-cliOr via ClawHub:
clawhub install oneshot-shipAgents pick it up automatically, or call /oneshot-ship directly.
MIT