CLI tool

oneshot

Repo + task in, PR out. Plans with Claude, executes with Codex, reviews the diff, opens the PR.

laptop your server Claude → Codex → PR
bun install -g oneshot-ship

Dry-run setup

oneshot acme/api --dry-run

Check repo access and remote setup first.

Ship one task

oneshot acme/api "fix auth timeout"

Full pipeline, one command.

Fire and forget

oneshot acme/api "add caching" --bg

Detaches immediately, returns PID + log path.

The Pipeline

Eight steps. Each run gets its own git worktree, so your main branch stays clean.

1

Validate

git

Checks the repo exists and fetches latest from origin

2

Worktree

git

Creates an isolated /tmp worktree from origin/main

3

Classify

Haiku

Decides fast vs deep review mode based on task complexity

4

Plan

Claude

Reads the codebase, outputs an implementation plan

5

Execute

Codex

Implements the plan following repo conventions

6

Draft PR

Claude

Creates branch, commits, pushes, and opens a draft PR

7

Review

Codex

Reviews the diff for bugs, types, and security. Fixes issues directly.

8

Finalize

Claude

Pushes review fixes and marks the PR ready

Worktrees are cleaned up after every run. Parallel runs are safe.

Quick Start

1

Prerequisites

On your laptop

  • Bun
  • SSH access to your server

On your server

2

Install

Install globally on both your laptop and server:

bun install -g oneshot-ship

Or from source:

git clone https://github.com/ADWilkinson/oneshot-cli.git
cd oneshot-cli && bun install && bun link
3

Configure

oneshot init

Sets up SSH host, workspace path, API keys, model preferences. Saves to ~/.oneshot/config.json.

Repos should live as <org>/<repo> under your workspace path:

~/projects/
  acme/api/
  acme/web/
4

Ship

oneshot acme/api "fix the login timeout bug"

That's it. oneshot handles the rest.

Usage

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>" --model sonnet   # override Claude model
oneshot <repo> "<task>" --deep-review    # force exhaustive review
oneshot <repo> "<task>" --branch staging # target a different branch
oneshot <repo> --dry-run                 # validate only
oneshot init                             # configure
oneshot stats                            # recent runs + timing

Flags

Flag Short Description
--model -m Override the Claude model (e.g. sonnet, opus)
--branch -b Base branch (default: main)
--deep-review Force exhaustive review mode (bypasses auto-classification)
--local Run locally instead of over SSH
--bg Run detached in background (returns PID + log path)
--dry-run -d Validate repo exists without running the pipeline
--events-file Mirror JSONL events to an additional file for machine consumption
--help -h Show help
--version -v Show version

Examples

Ship a feature

oneshot acme/api "add rate limiting to /auth endpoints"

From a Linear ticket

oneshot acme/api https://linear.app/acme/issue/ENG-142

Parallel background runs

oneshot acme/api "fix auth bug" --bg
oneshot acme/web "add dark mode" --bg

Local execution

oneshot acme/api "fix typo in README" --local

Configuration

~/.oneshot/config.json, created by oneshot init. Only host is required for SSH. Local mode works without a config file.

{
  "host": "user@100.x.x.x",
  "basePath": "~/projects",
  "anthropicApiKey": "sk-ant-...",
  "linearApiKey": "lin_api_...",
  "claude": {
    "model": "opus",
    "timeoutMinutes": 180
  },
  "codex": {
    "model": "gpt-5.4-mini",
    "reasoningEffort": "xhigh",
    "reviewModel": "gpt-5.4-mini",
    "reviewReasoningEffort": "xhigh",
    "timeoutMinutes": 180
  },
  "stepTimeouts": {
    "planMinutes": 20,
    "executeMinutes": 60,
    "reviewMinutes": 20,
    "deepReviewMinutes": 20,
    "prMinutes": 20
  }
}

Options

Key Required Description
host SSH only SSH target (e.g. user@192.168.1.10)
basePath No Root directory containing your repos. Default: ~/projects
anthropicApiKey No Falls back to ANTHROPIC_API_KEY env var
linearApiKey No Enables Linear ticket integration
claude.model No Claude model for Plan, Classify, and PR steps. Default: opus
codex.model No Codex model for Execute step. Default: gpt-5.4-mini
codex.reasoningEffort No Reasoning effort for execution. Default: xhigh
codex.reviewModel No Separate model for the Review step. Default: same as codex.model
codex.reviewReasoningEffort No Reasoning effort for review. Default: same as codex.reasoningEffort
stepTimeouts No Per-step timeout overrides in minutes

Linear Integration

Pass a Linear URL instead of a task string. oneshot fetches the ticket and uses it as context.

oneshot acme/api https://linear.app/acme/issue/ENG-142
1

Fetches issue title, description, and comments via GraphQL

2

Formats ticket as context for the planning step

3

Uses the issue ID in the PR branch name (e.g. oneshot/eng-142-1712345678901)

4

After PR creation, moves the ticket to "In Review" and comments the PR URL

Requires linearApiKey in your config.

Customization

CLAUDE.md

Put a CLAUDE.md in any repo root. oneshot passes it to Claude and Codex at every step.

Coding standards, architecture decisions, test requirements, whatever.

Prompt Templates

Edit the prompt files to change pipeline behavior:

prompts/plan.txt How Claude explores and plans
prompts/execute.txt How Codex implements changes
prompts/review.txt How Codex reviews the diff
prompts/pr.txt How Claude creates the PR

Templates use {{variable}} placeholders replaced at runtime.

Agent Skill

Works as an Agent Skill in Claude Code, Codex CLI, Cursor, and other compatible agents.

Install

npx skills add ADWilkinson/oneshot-cli

Or via ClawHub:

clawhub install oneshot-ship

Agents pick it up automatically, or call /oneshot-ship directly.