Skip to content

KerryRitter/programmatic-agent-router

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Programmatic Agent Router

par is a small Rust CLI that gives automation one stable prompt interface and routes the call to a local AI agent harness.

It is designed around the useful claude -p "prompt" style of workflow:

par -p "review this repository" --harness codex --model gpt-5.4
par -p "fix the failing tests" --harness opencode --provider anthropic --model claude-sonnet-4-6
git diff | par -p "review this patch" --harness goose --provider openai --model gpt-5.4

The router does not call model APIs directly. It starts an already-installed harness CLI, maps shared router flags into that harness's command surface, streams stdout/stderr, and exits with the child process status.

Why This Exists

Programmatic agent CLIs all solve a similar problem, but their headless interfaces differ:

  • Claude Code uses claude -p.
  • Codex uses codex exec.
  • Cursor uses cursor-agent -p.
  • Gemini uses gemini --prompt.
  • Goose uses goose run -t.
  • OpenCode uses opencode run.
  • Aider uses aider --message.
  • Antigravity uses the agy CLI surface.

That makes scripts brittle. Switching harnesses means editing commands, model flags, provider syntax, JSON output flags, and environment variables. par keeps scripts focused on intent:

par --harness <name> --provider <provider> --model <model> -p "<task>"

The harness adapter owns the translation.

Project Status

This is early, working infrastructure for local automation.

Implemented:

  • Rust-native CLI with no runtime dependencies.
  • Shared claude -p-style prompt surface.
  • Harness factory pattern with isolated adapters.
  • Harness installers via par install <harness>.
  • Provider/model resolution layer.
  • Dry-run mode for deterministic routing tests.
  • Setup script that validates Rust, tests, linting, release build, and installed downstream harness CLIs.

Not implemented yet:

  • GitHub Actions release builds.
  • Homebrew formula.
  • End-to-end smoke tests against every vendor CLI.
  • Stable semver contract for every harness mapping.

Install

Quick Install

curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/KerryRitter/programmatic-agent-router/main/install.sh | sh

This installs par into ~/.local/bin and creates an agent-router compatibility alias.

Prerequisites

You need Rust for source installs:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Make sure Cargo is available:

cargo --version

The setup script will also find Cargo at ~/.cargo/bin even if that directory has not been added to your shell PATH yet.

Validate the System

scripts/setup.sh

This checks:

  • Repository layout.
  • cargo and rustc.
  • cargo fmt --check.
  • cargo test.
  • cargo clippy --all-targets -- -D warnings.
  • cargo build --release.
  • Which supported downstream harness CLIs are installed.

Use strict harness validation if this machine should already have at least one supported agent CLI:

scripts/setup.sh --strict-harnesses

Install Harness CLIs

par can install supported downstream harness CLIs:

par install list
par install claude
par install codex
par install antigravity
par install --dry-run all

The installer registry is intentionally transparent: --dry-run prints the exact upstream install command without executing it. For harnesses that do not expose a stable terminal one-liner, such as Amazon Q, par install <name> prints the official install page and verification command instead of guessing.

Current installer coverage:

Harness Installer
claude curl -fsSL https://claude.ai/install.sh | bash
codex npm install -g @openai/codex
cursor curl https://cursor.com/install -fsS | bash
gemini npm install -g @google/gemini-cli
goose curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | bash
opencode curl -fsSL https://opencode.ai/install | bash
qwen curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen.sh | bash
aider curl -LsSf https://aider.chat/install.sh | sh
amazon-q Manual official installer page; verify with q --version
copilot curl -fsSL https://gh.io/copilot-install | bash
antigravity curl -fsSL https://antigravity.google/cli/install.sh | bash

Install from Source

scripts/setup.sh --install

This runs validation, installs the binary with:

cargo install --path . --force

and creates:

~/.local/bin/par
~/.local/bin/agent-router -> ~/.local/bin/par

Make sure both Cargo's bin directory and ~/.local/bin are on your PATH:

export PATH="$HOME/.cargo/bin:$HOME/.local/bin:$PATH"

Install from GitHub

The quick install command is:

curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/KerryRitter/programmatic-agent-router/main/install.sh | sh

The script tries to install a release binary for your platform first. Until release binaries exist, it falls back to:

cargo install --git https://github.com/KerryRitter/programmatic-agent-router.git --branch main --force

Useful options:

curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/KerryRitter/programmatic-agent-router/main/install.sh | sh -s -- --install-dir /usr/local/bin
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/KerryRitter/programmatic-agent-router/main/install.sh | sh -s -- --from-source
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/KerryRitter/programmatic-agent-router/main/install.sh | sh -s -- --from-source --git-protocol ssh
curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/KerryRitter/programmatic-agent-router/main/install.sh | sh -s -- --no-agent-router

Install from a Release Binary

The intended public release flow is to publish platform archives from CI:

  • par-aarch64-apple-darwin.tar.gz
  • par-x86_64-apple-darwin.tar.gz
  • par-x86_64-unknown-linux-gnu.tar.gz
  • par-x86_64-pc-windows-msvc.zip

Then installation is:

tar -xzf par-x86_64-unknown-linux-gnu.tar.gz
install -m 0755 par ~/.local/bin/par
ln -sf ~/.local/bin/par ~/.local/bin/agent-router

Homebrew Later

A public distribution should add a tap:

brew tap <org>/tap
brew install par

That should install the same release binary and provide par as the primary CLI name.

Quick Start

Default harness is claude:

par
par -p "summarize this repository"

Equivalent routed command:

claude
claude -p "summarize this repository"

Use Codex:

par --harness codex --model gpt-5.4 -p "add parser tests"

Set the default harness on this machine:

par default codex
par
par -p "add parser tests"

Persist a default harness with permission bypass enabled:

par default claude --yolo
par current

The default is stored in ~/.config/par/default, or $XDG_CONFIG_HOME/par/default when XDG_CONFIG_HOME is set. Override the path with PAR_DEFAULT_FILE.

Create shortcut scripts for yolo-capable harnesses:

par shims install
claudey -p "work in this sandbox"
codexy "work in this sandbox"

By default, shims are written to ~/.local/bin. Override this with par shims install --dir <dir> or PAR_SHIM_DIR.

Use OpenCode with provider-qualified model routing:

par \
  --harness opencode \
  --provider anthropic \
  --model claude-sonnet-4-6 \
  -p "review this branch"

Use Goose with environment-backed configuration:

par \
  --harness goose \
  --provider openai \
  --model gpt-5.4 \
  --permission-mode auto \
  --max-turns 50 \
  -p "fix the failing tests"

Pipe context:

git diff | par --harness aider -p "fix the problems in this patch"

Preview without execution:

par --harness qwen --model qwen3-coder-plus -p "review src" --dry-run

Pass harness-specific flags after --:

par --harness claude -p "review this" -- --verbose
par --harness aider -p "fix lint" -- --yes --no-auto-commits

CLI Reference

par [options]
par [options] [-p <prompt>] [positional prompt]
Option Purpose
-p, --prompt, --print Prompt text. --print is accepted for Claude compatibility.
--harness <name> Target CLI adapter. Defaults to claude.
--provider <name> Provider namespace when the harness uses provider-qualified model names or env configuration.
--model, -m <name> Model name.
--agent <name> Agent/persona/profile where supported.
--output-format <fmt> Output mode where supported by the target harness.
--input-format <fmt> Claude-compatible input format.
--permission-mode <mode> Permission/sandbox mode where supported.
--max-turns <n> Maximum agent turns where supported.
--cwd <path> Working directory for the child process.
--yolo Add the harness-specific permission bypass flag where supported.
--no-yolo Disable a persisted yolo default for this run.
--dry-run Print the routed invocation as JSON.
--help, -h Print help.
--version, -v Print version.
-- Pass all remaining args directly to the target CLI.

Environment defaults:

export AGENT_ROUTER_HARNESS=codex
export AGENT_ROUTER_PROVIDER=openai
export AGENT_ROUTER_MODEL=gpt-5.4
export AGENT_ROUTER_YOLO=true

Persisted default commands:

par default                  # show persisted defaults
par default codex --yolo     # set harness and yolo default
par default --no-yolo        # keep harness, disable yolo
par default --path           # print the default file path
par current                  # alias for showing defaults
par list                     # list supported harness names

This follows the useful part of nvm's command shape: a default alias, use-style setter, current, and list. par does not manage installed versions; downstream CLIs still own their own install and upgrade flows.

Prompt input rules:

  • If no prompt and no stdin are provided, par launches the default harness's interactive entrypoint.
  • If stdin is piped and -p is provided, stdin is placed before the prompt with a blank line between them.
  • If stdin is piped and no prompt is provided, stdin becomes the prompt.

Supported Harnesses

Harness Aliases Routed command
claude none claude -p "<prompt>"
codex openai codex exec "<prompt>"
cursor cursor-agent cursor-agent -p "<prompt>"
gemini google, google-gemini gemini --prompt "<prompt>"
goose none goose run -t "<prompt>"
opencode open-code opencode run "<prompt>"
qwen none qwen -p "<prompt>"
aider none aider --message "<prompt>"
amazon-q amazonq, aws-q, amazon q chat "<prompt>"
copilot github-copilot copilot -p "<prompt>"
antigravity agy, google-antigravity agy "<prompt>"

Harness Details

Claude:

  • Uses claude -p.
  • Supports --model, --output-format, --input-format, --permission-mode, and --max-turns.
  • --yolo maps to --dangerously-skip-permissions.

Codex:

  • Uses codex exec.
  • --output-format json and --output-format stream-json map to --json.
  • Provider is currently preserved in AGENT_ROUTER_PROVIDER for future policy, but Codex receives the plain model name.
  • --yolo maps to --dangerously-bypass-approvals-and-sandbox for routed noninteractive runs. The codexy shim uses the shorter codex --yolo entrypoint.

Cursor:

  • Uses cursor-agent -p.
  • Supports plain --model.
  • Supports --output-format when accepted by the installed Cursor agent.
  • --yolo maps to --force, which Cursor requires for print-mode file writes.

Gemini:

  • Uses gemini --prompt.
  • Supports plain --model.
  • Supports --output-format when accepted by the installed Gemini CLI.
  • --yolo maps to --yolo.

Goose:

  • Uses goose run -t.
  • --provider maps to GOOSE_PROVIDER.
  • --model maps to GOOSE_MODEL.
  • --permission-mode maps to GOOSE_MODE.
  • --max-turns maps to GOOSE_MAX_TURNS.
  • --agent <name> maps to goose run --with-builtin <name>.
  • --yolo sets GOOSE_MODE=auto unless --permission-mode is provided.

OpenCode:

  • Uses opencode run.
  • --provider anthropic --model claude-sonnet-4-6 becomes --model anthropic/claude-sonnet-4-6.
  • --output-format json and --output-format stream-json map to --format json.
  • --agent maps to --agent.
  • --yolo maps to --dangerously-skip-permissions.

Qwen:

  • Uses qwen -p.
  • Supports plain --model.
  • Supports --output-format when accepted by the installed Qwen CLI.
  • --yolo maps to --yolo.

Aider:

  • Uses aider --message.
  • Provider and model are joined for --model, such as anthropic/claude-sonnet-4-6.
  • Use -- for Aider-specific automation flags, for example --yes.
  • --yolo maps to --yes-always.

Amazon Q:

  • Uses q chat.
  • --agent maps to --agent.
  • Model selection is owned by Amazon Q CLI configuration.

Copilot:

  • Provisional adapter.
  • Uses copilot -p.
  • --yolo maps to --yolo.
  • Validate against the installed CLI before relying on it in automation.

Antigravity:

  • Experimental adapter.
  • Uses the agy command installed by Google Antigravity.
  • Official docs currently describe the interactive AGY CLI rather than a stable print/headless mode. The adapter passes the prompt to agy and leaves version-specific flags to --.
  • --yolo maps to --dangerously-skip-permissions.

Architecture

The code is intentionally small and explicit.

src/
  main.rs              entrypoint, stdin handling, dry-run, process dispatch
  cli.rs               shared command-line parser
  model.rs             provider/model resolution
  process.rs           child process execution
  harness/
    mod.rs             Harness trait, Request, HarnessFactory
    invocation.rs      command/args/env representation
    claude.rs          Claude adapter
    codex.rs           Codex adapter
    cursor.rs          Cursor adapter
    gemini.rs          Gemini adapter
    goose.rs           Goose adapter
    opencode.rs        OpenCode adapter
    qwen.rs            Qwen adapter
    aider.rs           Aider adapter
    amazon_q.rs        Amazon Q adapter
    antigravity.rs     Antigravity adapter
    copilot.rs         provisional Copilot adapter
  installer.rs         harness installer registry

Core types:

  • Request: normalized router input.
  • Invocation: concrete command, args, and environment.
  • Harness: adapter trait implemented by each harness.
  • HarnessFactory: registry that maps harness names to adapter constructors.
  • ModelFactory: central place for provider/model normalization and formatting.
  • installer.rs: explicit upstream install commands for downstream harness CLIs.

Design constraints:

  • No sh -c; adapters build argv directly.
  • No hidden API calls; the router only starts local CLIs.
  • No login handling; authenticate each downstream harness separately.
  • Harness-specific behavior stays in that harness module.
  • Provider/model transformation stays centralized in model.rs.
  • --dry-run output should be stable enough for tests and shell debugging.

Adding a Harness

  1. Create src/harness/<name>.rs.
  2. Implement Harness for a small adapter struct.
  3. Register the adapter in HarnessFactory::default().
  4. Add aliases in normalize_harness() if useful.
  5. Add dry-run tests for command, args, provider/model behavior, env vars, and passthrough.
  6. Document the harness and source docs in this README.

Adapter skeleton:

use super::{add_passthrough, plain_model, Harness, Invocation, Request};

pub(crate) fn new() -> Box<dyn Harness> {
    Box::new(ExampleHarness)
}

struct ExampleHarness;

impl Harness for ExampleHarness {
    fn build(&self, request: &Request) -> Result<Invocation, String> {
        let mut args = vec!["run".to_string(), request.prompt.clone()];

        if let Some(model) = plain_model(request) {
            args.extend(["--model".to_string(), model]);
        }

        Ok(Invocation::new("example", add_passthrough(args, request)))
    }
}

Development

Run the full local validation:

scripts/setup.sh

Install after validation:

scripts/setup.sh --install

Common direct commands:

cargo fmt
cargo test
cargo clippy --all-targets -- -D warnings
cargo build --release

Try a dry run:

cargo run -- --harness opencode --provider anthropic --model claude-sonnet-4-6 -p "review" --dry-run

Expected dry-run shape:

{
  "command": "opencode",
  "args": ["run", "--model", "anthropic/claude-sonnet-4-6", "review"],
  "env": {}
}

Release Plan

Recommended first public release checklist:

  1. Run scripts/setup.sh.
  2. Smoke-test installed CLIs that are available locally with --dry-run.
  3. Add GitHub Actions for:
    • cargo fmt --check
    • cargo test
    • cargo clippy --all-targets -- -D warnings
    • release binaries for Linux, macOS, and Windows
  4. Add archive checksums.
  5. Publish a GitHub release.
  6. Add a Homebrew tap once the release artifact names are stable.

Security and Privacy

par does not inspect or redact prompt content. Anything passed to stdin or -p is forwarded to the selected harness. The selected harness may send that content to its configured provider.

The router does not weaken or bypass harness sandboxing unless you explicitly opt into --yolo or persist yolo=true in the default file. Permission behavior is delegated to the downstream CLI. When a shared option like --permission-mode or --yolo is mapped, it uses the target harness's command surface.

Use --dry-run when validating automation that may include secrets or proprietary context.

Recommended Next Harnesses

High priority:

  • crush: add once its one-shot/headless command surface is pinned.
  • amp: useful commercial harness, but verify whether it has a true non-interactive one-shot mode before adding.
  • windsurf: support only if/when the agent CLI exposes a stable non-interactive prompt command.

Medium priority:

  • qoder, trae, kimi, openclaw, droid: support if they expose either ACP or a stable direct CLI.
  • factory: worth tracking if the local CLI is scriptable outside its hosted workflow.
  • continue, cline, roo, kilo: these are often extension-first rather than CLI-first; only add if there is a real headless command.

Out of scope for now:

  • Web-only agents with no local CLI.
  • Interactive-only CLIs that block on a TTY.
  • Raw model providers where there is no agent harness. This project routes harnesses, not chat completions.

Source Notes

This README was written against public docs checked on May 19, 2026:

Installer commands are kept in src/installer.rs and checked against the official install docs where a stable one-liner exists. Amazon Q remains manual because the official AWS CLI docs point users through platform-specific installer flows rather than a single shell installer command.

Re-check these command surfaces before a public release. Agent CLIs change fast.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors