Local launcher scripts for running repeatable pair-programming loops between Claude Code and Codex.
The tools share a workspace, maintain structured loop state, write per-turn logs and handoffs, and support validation-driven stop conditions. They are designed for local use with authenticated CLIs already installed.
workspace/ and logs/ are generated runtime directories. They are disposable by default and ignored by Git.
This repository is best suited to users who already run both CLIs locally and want a repeatable way to hand work back and forth between them.
- Alternating Claude Code and Codex turns against the same shared workspace
- Standard mode for predictable runs and MCP mode for cross-agent delegation inside each turn
- Structured run artifacts including per-turn logs, handoffs,
run_summary.json, and logs-based state files - Validation-aware stopping with
--validation-command,--validation-preset, and project layout auto-detection - Stop conditions for passing validation, completing checklist items, and reaching a clean Git worktree
- Optional checkpoint commits and tags after each iteration
- Resume and non-destructive modes for continuing existing runs without clearing workspace or logs
- Per-turn timeout control and startup health-check-only mode
- Local deterministic regression checks plus optional live authenticated local E2E hooks
- Lightweight wrapper skills for Claude-first and Codex-first execution flows
| Tool | Purpose | Recommended use |
|---|---|---|
pair_loop.sh |
Alternating turns between Claude and Codex | Default choice for most runs |
pair_loop_mcp.sh |
Alternating turns plus cross-agent MCP delegation inside each turn | Experimental runs that need richer agent-to-agent interaction |
skills/claude-first-pair-loop |
Wrapper skill for Claude-first runs | Reusable agent workflow |
skills/codex-first-pair-loop |
Wrapper skill for Codex-first runs | Reusable agent workflow |
If you want the most predictable execution path, start with pair_loop.sh.
These scripts are intended for a local machine, not a hosted CI environment.
- Bash
- Git
- Node.js v20+
claudeCLIcodexCLInpx
- The entrypoints are Bash scripts and assume a POSIX-style local shell environment.
- The documented workflow is aimed at macOS or Linux machines with local CLI authentication already available.
- Hosted CI, sandboxed runners, and Windows-native shells are not the primary target environments.
- Runtime behavior depends on the installed
claudeandcodexCLI versions and the flags they currently support.
Expected local setup:
pair_loop.shexpectsclaude -p --dangerously-skip-permissionsto work.pair_loop.shexpectscodex exec --full-autoto work.pair_loop_mcp.shexpects Claude to load the repo-local.mcp.json.pair_loop_mcp.shmay register aclaude-codeMCP server for Codex if it does not already exist.
The current Codex-side MCP registration command is:
codex mcp add claude-code -- npx -y @steipete/claude-code-mcp@latestIf the MCP packages are not already cached, npx may need network access on the first run.
Recommended preflight checks:
bash --version
git --version
node --version
claude -p "Reply with: ok"
codex login statusNotes:
- The Claude check above consumes a lightweight request.
- Run these commands from the same shell profile you plan to use for the pair loop.
These tools run autonomous coding agents against your local filesystem.
- The runners allow Claude and Codex to edit files in
workspace/, run shell commands, and optionally create checkpoint commits and tags. - Standard mode relies on
claude -p --dangerously-skip-permissionsandcodex exec --full-auto. - MCP mode adds external MCP servers and may fetch packages on first use through
npx. - Start in a disposable repository or throwaway task until you are comfortable with the runtime behavior and generated artifacts.
If the scripts are not executable in your clone:
chmod +x pair_loop.sh pair_loop_mcp.shRun the standard loop:
./pair_loop.sh "Build a Python CLI that parses Markdown front matter and outputs JSON" 3Run the MCP-enabled loop:
./pair_loop_mcp.sh "Build a Python CLI that parses Markdown front matter and outputs JSON" 3Resume an existing run:
./pair_loop.sh --resume --workspace ./workspace --log-dir ./logsRun in non-destructive mode:
./pair_loop.sh --non-destructive --task "Improve an existing CLI tool"Run with Codex starting first:
./pair_loop.sh --codex-first --task "Improve an existing CLI tool"Run with explicit model and effort settings:
./pair_loop_mcp.sh \
--claude-model sonnet \
--claude-effort high \
--codex-model gpt-5.3-codex \
--codex-effort xhigh \
--task "Continue the current project" \
--resumeRun with session grouping, validation, stop conditions, and checkpoints:
./pair_loop.sh \
--session-name hardening-pass \
--role-preset docs-refactor \
--validation-preset pytest \
--until-tests-pass \
--until-checklist-complete \
--checkpoint-commits \
--task "Stabilize the service"Run through the skill wrappers:
./skills/claude-first-pair-loop/scripts/run-pair-loop.sh \
--task "Build a CLI tool" \
--max-iterations 3
./skills/codex-first-pair-loop/scripts/run-pair-loop.sh \
--mcp \
--task "Continue the current project" \
--resumeRun startup checks only:
./pair_loop.sh --healthcheck-onlyWhat to expect after a run:
- Generated project files appear under
workspace/. - Session artifacts appear under
logs/orlogs/<session>/when you use--session-name. - The fastest summary is
run_summary.json. - Human-readable state is recorded in
logs/.../state/loop_state.md. - Machine-readable state is recorded in
logs/.../state/loop_state.json.
Positional arguments:
- First argument: task description passed to both agents.
- Second argument: maximum number of iterations.
If you omit the task, each script uses its built-in default Python CLI prompt. If you omit max_iterations, both scripts default to 999999.
Task and execution control:
--task TEXT--max-iterations N--workspace PATH--log-dir PATH--session-name NAME--first-agent claude|codex--claude-first--codex-first--turn-timeout SECONDS--healthcheck-only
Model, effort, and role configuration:
--profile fast|balanced|deep--fast--balanced--deep--claude-model MODEL--codex-model MODEL--claude-effort low|medium|high--codex-effort low|medium|high|xhigh--role-preset balanced|docs-refactor|reviewer-builder
Validation, stop conditions, and checkpoints:
--validation-command CMD--validation-preset auto|pytest|unittest|custom--validation-auto--until-tests-pass--until-checklist-complete--until-clean-git--checkpoint-commits--checkpoint-tags
State preservation:
--resume--keep-logs--keep-workspace--non-destructive
Behavior notes:
--resumeimplies preserving both workspace and logs.- Profile presets only affect effort defaults. They do not force a specific model.
- Explicit
--claude-effortor--codex-effortoverrides the profile default for that agent. --validation-commandis for full manual control. Use it when you know the exact command you want.--validation-preset pytestis the safest default for Python work generated in the workspace.--validation-preset unittestassumes an importabletests/package and is stricter about project layout.--validation-autoand the default auto mode detect common layouts such aspytest.ini,conftest.py, top-leveltest_*.py,package.json,Cargo.toml, andgo.mod.- When a manual validation command looks inconsistent with the detected layout, the runner records a warning and a suggested command in the validation log,
loop_state.json, andrun_summary.json. - Claude effort is passed through as
claude --effort. - Codex effort is passed through as
codex exec -c model_reasoning_effort="...".
At startup, both scripts:
- Parse flags and resolve runtime configuration.
- Run startup health checks for Claude, Codex, Node.js, and MCP availability.
- Clean or preserve
workspace/andlogs/based on the selected flags. - Ensure the workspace is a Git repository because Codex expects one.
- Create or reuse structured state files under
logs/.../state/loop_state.mdandlogs/.../state/loop_state.json. - Initialize
run_summary.jsonfor the active session.
During each iteration:
- The scripts perform a lightweight availability check for both agents.
- The first agent takes a turn or is skipped if unavailable.
- The runner writes a per-turn log and a diff-aware handoff summary.
- The second agent takes a turn or is skipped if unavailable.
- The runner resolves validation from
--validation-command,--validation-preset, or auto-detection, then runs it and records any preflight mismatch warning. - Stop conditions are evaluated and the state files are regenerated.
- The loop either continues, stops because conditions were met, or exits when interrupted or capped by
max_iterations.
Availability checks currently work like this:
- Claude: a lightweight
claude -pping - Codex:
codex login status
If a check fails, the runner writes a skip log and continues with the next available agent instead of aborting immediately.
Primary runtime artifacts:
logs/.../state/loop_state.md: human-readable loop statelogs/.../state/loop_state.json: machine-readable loop statelogs/.../*.log: per-turn and validation logslogs/.../*handoff_iterN.md: handoff summaries between turnslogs/.../run_summary.json: runner-owned final summary for the sessionlogs/<session>/state/: session-scoped mirrors of the state files when--session-nameis used
State behavior:
loop_state.mdpreserves human-managed sections such as Success Criteria, File Focus, Open Decisions, and Risks.- The runner regenerates Session, Current Status, Next Handoff, Iteration Ledger, and stop-condition status.
- Handoff files are diff-aware. They include a change summary, current Git status, workspace snapshot, and a runner-owned state snapshot.
- Turn logs record both configured and resolved runtime model and effort when the underlying CLI exposes those values.
loop_state.jsonandrun_summary.jsonsplit stop-condition data intoconfigured,current, andsummaryso tooling can distinguish enabled checks from checks that are currently met.- Validation logs include the selected preset, selection mode, detected project layout, warning, and suggested command when the runner can infer one.
- The workspace is reserved for generated project files. Runner state and handoff artifacts are kept under
logs/.
Session behavior:
- Without
--session-name, logs are written directly underlogs/. - With
--session-name, logs, handoffs, validation logs,run_summary.json, and state mirrors are grouped underlogs/<session>/.
pair_loop.sh is the simpler and more explicit mode.
- Claude starts first by default.
- You can switch to Codex-first with
--first-agent codexor--codex-first. - The agents alternate direct turns against the same workspace.
- The first agent receives the previous handoff summary from the other agent.
- The second agent receives the current handoff summary from the first agent.
Use this mode when you want clearer logs, fewer moving parts, and easier debugging.
pair_loop_mcp.sh keeps the same outer loop, but changes what happens inside each turn.
- Claude can delegate back to Codex through MCP during Claude's turn.
- Codex can delegate back to Claude through MCP during Codex's turn.
- The outer script still alternates turns, so MCP delegation happens inside the turn rather than replacing the loop.
Use this mode when you want richer collaboration patterns and you are comfortable with a more fragile runtime model.
As configured in this repository, MCP mode depends on external MCP projects:
| Direction | Local config | Upstream project |
|---|---|---|
| Claude -> Codex | .mcp.json starts npx -y codex-mcp-server under the MCP server name codex-cli |
codex-mcp-server |
| Codex -> Claude | pair_loop_mcp.sh runs codex mcp add claude-code -- npx -y @steipete/claude-code-mcp@latest when needed |
@steipete/claude-code-mcp |
Operational notes:
- These MCP dependencies are referenced from upstream packages and are not vendored into this repository.
- The current setup does not pin a specific
codex-mcp-serverversion. - The current setup uses
@latestfor@steipete/claude-code-mcp, so future runs may pick up newer upstream behavior.
This repository includes a local-only workflow for deterministic checks and live authenticated smoke tests:
scripts/local-ci.sh: local runner for deterministic checks, live E2E, and hook installation.githooks/pre-commit: fast deterministic checks before each commit.githooks/post-commit: background live E2E after each commit
Enable the hooks for this clone:
chmod +x scripts/local-ci.sh .githooks/pre-commit .githooks/post-commit
bash scripts/local-ci.sh install-hooksUseful commands:
bash scripts/local-ci.sh deterministic
bash scripts/local-ci.sh live
bash scripts/local-ci.sh allOperational notes:
- The post-commit hook is non-blocking.
- Background live E2E output is written under
logs/local-ci/. - Only one background live E2E is started at a time.
- Live runs use your local authenticated
claudeandcodexCLIs and consume real usage. - You can copy
.local-ci.env.exampleto.local-ci.envto override local defaults.
The repository includes a live smoke test at tests/e2e_live_pair_loop.sh.
What it does:
- runs
pair_loop.shby default, orpair_loop_mcp.shwith--mode mcp - uses a throwaway workspace and log directory under
/tmp - asks the loop to create
smoke.txtwith exact content - verifies the state files under
logs/.../state/, final-iteration validation logs, andrun_summary.json - checks the runner-owned summary to confirm validation finished with
passed - removes generated artifacts only when
--cleanupis requested and the run succeeds
Useful options:
--mode standard|mcp--first-agent claude|codex--require-claude-turn--require-codex-turn--cleanup--to pass additional flags through to the underlying loop script
Important limitations:
- this is a real integration test, not a mocked unit test
- it consumes real Claude and Codex usage
- it requires authenticated local CLIs and working network access
- it is not suitable for sandboxed or offline CI by default
The repository includes two lightweight skill packages under skills/:
| Skill | Path | Purpose |
|---|---|---|
claude-first-pair-loop |
skills/claude-first-pair-loop |
Runs the pair loop with Claude taking the first turn |
codex-first-pair-loop |
skills/codex-first-pair-loop |
Runs the pair loop with Codex taking the first turn |
Each skill contains:
SKILL.mdwith usage instructionsagents/openai.yamlwith agent metadatascripts/run-pair-loop.shas the wrapper entrypoint
The wrapper scripts accept the same core flags as the underlying tools. Pass --mcp to switch from standard mode to MCP mode.
These scripts are intentionally aggressive about cleaning generated state.
- By default they clean the contents of
workspace/at startup. - By default they clean the contents of
logs/at startup. - Use
--keep-workspace,--keep-logs,--non-destructive, or--resumeif you want to preserve existing state. - Anything stored in those directories should be treated as disposable unless you explicitly preserve it.
Other practical caveats:
- The scripts use
set -euo pipefail. - Claude availability checks consume a lightweight Claude request because there is no equivalent local status command for account and usage.
- Shared context is diff-aware and state-aware, but still summary-based.
- Checkpoint commits and tags are optional and only run when enabled.
- MCP mode assumes the installed CLI behavior matches what the scripts expect.
If a run fails, start with the generated files under logs/.
Common issues:
claude: command not foundorcodex: command not found- authentication failures
- MCP server not available in
pair_loop_mcp.sh - expected files missing from
workspace/ - loop keeps running longer than expected
Typical fixes:
- Install the missing CLI and ensure it is on
PATH. - Re-authenticate the affected CLI.
- Check
.mcp.json, runcodex mcp list, and confirmnpxcan launch the required packages. - Re-run with
--keep-workspace,--keep-logs,--non-destructive, or--resumeif you need to preserve debugging artifacts. - Use
Ctrl-Cto stop a long-running loop manually.
For security-sensitive issues, do not open a public bug report. See SECURITY.md.
Contribution guidance now lives in CONTRIBUTING.md.
- Run
bash scripts/local-ci.sh deterministicbefore opening a PR. - Keep changes focused and update docs when runner behavior changes.
- Add deterministic regression coverage for new flags, state fields, or lifecycle behavior.
Community expectations now live in CODE_OF_CONDUCT.md.
This project is licensed under the MIT License.
Use pair_loop.sh when you want:
- simpler execution
- clearer logs
- fewer moving parts
- easier debugging
Use pair_loop_mcp.sh when you want:
- Claude to call Codex during Claude's turn
- Codex to call Claude during Codex's turn
- to experiment with more autonomous cross-agent workflows
If you plan to keep evolving these tools, the highest-value follow-ups are:
- Pin MCP package versions instead of relying on
@latest. - Improve validation auto-detection with more ecosystems and project-specific overrides.
- Add richer checkpoint strategies such as branches or worktrees in addition to commits and tags.
- Record token and cost metrics if future CLI versions expose them reliably.
- Expand regression coverage as new loop behaviors are added.
