Harnex
Run multiple AI coding agents from your terminal and coordinate them.
Harnex wraps Claude Code, OpenAI Codex, Pi, OpenCode, or any terminal CLI in a local harness so you can launch agents, send them tasks, inspect panes, transcripts, and events, and stop them cleanly -- all from the command line.
gem install harnex
Harnex itself requires Ruby 3.x and uses only the Ruby standard
library. Install the CLIs you want to wrap separately; Codex JSON-RPC
support requires Codex CLI 0.128.0 or newer, and tmux-backed
workflows require tmux.
Then ask the CLI what to do next:
harnex
harnex --help
harnex agents-guide
If you use Codex, run harnex doctor after installing or upgrading the
Codex CLI. It verifies the local codex app-server prerequisite.
harnex agents-guide is the agent-facing reference for dispatch, chain,
buddy, monitoring, and naming patterns. It is packaged in the gem; no skills
or project-local docs are required.
What it does
# Start an agent in tmux
harnex run pi --id planner --tmux planner
# Send it a task and wait for it to finish
harnex send --id planner --message "Write a plan to /tmp/plan.md" --wait-for-idle
# Peek at what it's doing
harnex pane --id planner --lines 30
# Stop it
harnex stop --id planner
That's the core loop. Start a fresh agent for each step, hand it one job, watch it work, stop it when done.
Why use this
You want agents to plan, implement, review, and fix — in sequence. Pi writes code. Claude reviews it. Pi (or another adapter) fixes the review findings. Each step is a fresh agent with clean context.
You want to see what agents are doing.
harnex panecaptures a tmux-backed terminal,harnex logstails the persisted transcript, andharnex eventsstreams structured JSONL lifecycle events.You don't want to babysit. Send a task with
--wait-for-idle, walk away, check back when it's done.You want local-only orchestration. Everything runs on your machine. No cloud services, no API keys beyond what the agents need.
When you wouldn't use this
- You only use one agent at a time (just run it directly)
- You need cloud-hosted orchestration
- Your agents aren't terminal-based
Supported agents
| Agent | Support |
|---|---|
| Claude Code | PTY adapter with prompt detection, stop sequence, workspace trust, and vim mode handling |
| OpenAI Codex | JSON-RPC codex app-server adapter by default; PTY mode remains supported for TUI/interactive use via --legacy-pty |
| Pi | JSONL RPC adapter (pi --mode rpc) with structured completion, tool events, extension-UI auto-cancel, and session stats telemetry |
| OpenCode | PTY adapter with native Ctrl+C stop handling and OpenCode-specific prompt/readiness heuristics |
| Any terminal CLI | Generic PTY wrapping with local API, logs, status, and best-effort prompt detection |
harnex run codex uses JSON-RPC by default. That path provides
structured task-completion events, approval mediation, and token usage
capture. Default JSON-RPC Codex does not accept -m / --model; pass
model settings as child CLI config, for example harnex run codex -- -c model=NAME.
Harnex forces Codex app-server service_tier="flex" unless you opt into
service_tier="fast" with harnex run codex --fast.
Use harnex run codex --legacy-pty when you specifically want Codex's
terminal UI or PTY-only Codex flags. The flag name is historical; the PTY path
is still supported.
harnex run pi launches pi --mode rpc and sends --context as a structured
prompt command (not a CLI positional argument). Pass Pi child flags after the
separator, for example:
harnex run pi --context "Implement X" -- --model anthropic/claude-sonnet-4-5 --thinking high.
Multi-agent workflows
The real power is chaining agents together:
# 1. Pi writes a plan
harnex run pi --id pi-plan --tmux pi-plan
harnex send --id pi-plan --message "Plan the auth module, write to /tmp/plan.md" --wait-for-idle
harnex stop --id pi-plan
# 2. Fresh Pi implements the plan
harnex run pi --id pi-impl --tmux pi-impl
harnex send --id pi-impl --message "Implement /tmp/plan.md, run tests" --wait-for-idle
harnex stop --id pi-impl
# 3. Claude reviews the implementation
harnex run claude --id cl-review --tmux cl-review
harnex send --id cl-review --message "Review changes against /tmp/plan.md, write /tmp/review.md" --wait-for-idle
harnex stop --id cl-review
For delegated work, pass the same value to --id and --tmux so
harnex status, harnex pane, logs, and the tmux window name all line up.
Harnex ships CLI-readable agent guides for this pattern:
- Dispatch — the fire-and-watch pattern: spawn an agent, poll its screen, stop it when done
- Chain — end-to-end issue-to-code workflow: plan, review plan, implement, review code, fix
- Buddy — spawn an accountability partner for long-running or overnight work
- Monitoring — completion signals and poll/watch patterns
- Naming — session IDs, task files, done markers
Read them from the installed CLI:
harnex agents-guide dispatch
harnex agents-guide monitoring
Built-in dispatch monitoring
For unattended dispatches, use --watch instead of writing a bash poll loop:
harnex run pi --id pi-impl-42 --watch --preset impl \
--context "Implement koder/plans/42_plan.md. Run tests and commit when done."
--watch runs a foreground babysitter that checks session activity every 60s,
force-resumes on stall up to a cap, and exits when the target session exits or
the resume cap is reached. It is foreground-only; use --tmux or --detach
for visible/background sessions, and --watch when the current command should
block as the monitor.
Presets map to stall policy defaults:
impl->--stall-after 8m --max-resumes 1plan->--stall-after 3m --max-resumes 2gate->--stall-after 15m --max-resumes 0
Explicit --stall-after and --max-resumes flags override preset defaults.
For file-change hooks, use --watch-file PATH. The older
--watch PATH/--watch=PATH form is still accepted for compatibility, while
bare --watch means babysitter mode.
For one-shot startup prompts, add --auto-stop. It requires --context
and stops the session after the first task completion or PTY prompt return.
For structured subscriptions, stream JSONL events:
harnex events --id pi-impl-42
Schema details and compatibility policy are documented in docs/events.md.
Dispatch history
Every finished harnex run writes dispatch records. In a git repo, the
default path is <repo>/.harnex/dispatch.jsonl; outside a git repo, the
compact history record falls back to ~/.local/state/harnex/dispatch.jsonl.
harnex history reads the compact records from that location.
Use harnex history to inspect it:
harnex history
harnex history --json | jq .
Dispatch briefs can declare soft budget metadata through --meta:
harnex run pi --meta '{"read_budget_lines":2000,"output_ceiling_lines":800}' ...
Those declared values are copied into summary meta. Terminal summary
actual records timing, exit classification, token usage when the adapter can
capture it, adapter-reported cost_usd when reliably available, git deltas,
task-completion state, operational counters (stalls, force_resumes,
disconnections, tool_calls, commands_executed), output/event log paths,
and rough volume measurements such as lines_changed, output_lines,
output_bytes, and event_records.
Harnex records the data only; consumers decide whether to fail closed. See
docs/dispatch-telemetry.md for the field
contract.
Long-running and overnight work
For plain "force-resume on stall" recovery, use
harnex run pi --watch --preset impl --context "Read /tmp/task.md".
A buddy is for richer reasoning: doc drift checks, semantic sanity checks, and multi-session correlation. It's still just another harnex session.
Example: keep a worker from stalling
Spawn a buddy alongside a long-running implementation worker:
harnex run pi --id worker-42 --tmux worker-42
harnex run pi --id buddy-42 --tmux buddy-42
harnex send --id buddy-42 --message "$(cat <<'EOF'
Watch harnex session worker-42.
Every 5 minutes: run `harnex pane --id worker-42 --lines 30`.
If it looks stuck at a prompt with no progress for 10+ minutes,
nudge it: `harnex send --id worker-42 --message "Continue your task."`.
When it exits, report back:
tmux send-keys -t "$HARNEX_SPAWNER_PANE" "worker-42 done" Enter
EOF
)"
Example: watch for doc drift during implementation
A buddy that checks whether a worker's code changes have left docs out of date:
harnex run pi --id worker-99 --tmux worker-99
harnex run pi --id buddy-99 --tmux buddy-99
harnex send --id buddy-99 --message "$(cat <<'EOF'
Watch harnex session worker-99.
Every 5 minutes: run `harnex pane --id worker-99 --lines 30`.
When the worker goes idle after making changes, run `git diff --name-only`
and check whether any changed code has corresponding docs (README, GUIDE,
inline comments) that are now stale. If so, nudge the worker:
harnex send --id worker-99 --message "Docs may be stale — check README
sections related to <specific area>."
When the worker exits, report a summary to the invoker:
tmux send-keys -t "$HARNEX_SPAWNER_PANE" "worker-99 done. Doc drift: <yes/no>" Enter
EOF
)"
The invoker doesn't need to be a harnex session
Every spawned session gets $HARNEX_SPAWNER_PANE — the tmux pane ID
of whoever ran harnex run. The buddy can report back to a plain
Claude Code session, a Codex session, or any tmux pane:
tmux send-keys -t "$HARNEX_SPAWNER_PANE" "worker-42 finished" Enter
See recipes/03_buddy.md for the full pattern.
All commands
| Command | What it does |
|---|---|
harnex run <cli> |
Start an agent (--tmux visible, --detach background, --watch built-in monitoring) |
harnex send --id <id> |
Send a message (queues if busy, --wait-for-idle to block until done) |
harnex stop --id <id> |
Send the agent's native exit sequence |
harnex status |
List running sessions (--json for full payloads) |
harnex pane --id <id> |
Capture a tmux-backed session's screen (--follow for live) |
harnex logs --id <id> |
Read session transcript (--follow to tail) |
harnex events --id <id> |
Stream structured session events (--snapshot for non-blocking dump) |
harnex history |
List completed dispatches from .harnex/dispatch.jsonl |
harnex wait --id <id> |
Block until exit, a target state, or --until task_complete |
harnex doctor |
Run adapter dependency preflight checks; add --sweep for read-only session drift diagnostics |
harnex guide |
Getting started walkthrough |
harnex agents-guide |
Agent-facing dispatch, chain, buddy, monitoring, and naming guides |
harnex recipes |
List and read tested workflow patterns (show 01, show buddy) |
Uninstalling
gem uninstall harnex
If you installed harnex skills with an older release, those copies are no
longer used. Remove stale ~/.claude/skills/harnex-* or
~/.codex/skills/harnex-* entries manually if you want to clean them up.
Going deeper
- GUIDE.md — getting started walkthrough with examples
- TECHNICAL.md — full command reference, flags, HTTP API, architecture