tamer-cli
v2.125.3
Published
Transparent PTY proxy + worker for AI coding agents (Claude Code, Gemini, Codex, Copilot, Vibe, OpenCode, Cursor, Windsurf, …). Supervised by a Tamer master agent with sandboxing, MCP wiring, skills, and approval policy.
Maintainers
Readme
tamer-cli
Wrap any coding-agent CLI as a supervised worker that connects to a Tamer
server and is driven by a master agent. Tamer-cli is the worker-side
binary: a transparent PTY proxy that ferries the agent's terminal IO to the
master while enforcing approval policy, sandboxing, MCP wiring, skills,
checkpoints, and event reporting. The same tamer command runs Anthropic's
Claude Code, Google's Gemini CLI, OpenAI's Codex, GitHub Copilot, Mistral
Vibe, OpenCode, Cursor's CLI, Codeium's Windsurf — and a generic fallback for
anything else that speaks a PTY.
Install
npm install -g tamer-cliRequires Node ≥ 20. Linux gets the full bwrap sandbox; macOS/Windows get the cross-platform layers (env sanitization, job objects, low integrity).
Quickstart
# 1. Initialise the project — pick agents in the interactive picker,
# writes .tamer/config.yaml + agent files (AGENT.md, CLAUDE.md, …),
# then auto-chains to `tamer login` if no API key is set.
tamer init
# 2. Launch an agent under tamer supervision.
tamer claudeThat's it: the agent runs as if you'd typed claude directly, but every PTY
chunk flows through tamer's detection pipeline and the master agent on the
other end of the WebSocket sees an event stream — tool uses, approval
prompts, token deltas, errors — and can drive the worker remotely.
Launching agents
tamer <agent> [agent-args...] # single agent
tamer claude,gemini # FALLBACK CHAIN (run claude; on quota
# exhaustion, swap to gemini in the SAME PTY)
tamer claude -- --resume # everything after `--` goes to the agent
tamer vibe claude # docker-run convention: first non-option
# = agent binary (`vibe`), rest = its args
# (so `claude` here is an arg to vibe)Argv parsing follows the same convention as docker run / ssh / env:
the first non-option token is the agent binary (or a comma-separated
fallback chain), everything after it is agent args. Use -- to make the
split explicit when you need to pass flags that look like tamer's own.
Comma-list = sequential fallback, not parallel
tamer claude,gemini does not spawn two processes or two PTYs. The
worker opens one PTY with the first agent (claude); if/when it hits
quota exhaustion or unrecoverable failure, the orchestrator terminates that
slot and spawns the next agent (gemini) in the same PTY with a state-blob
handoff. Run order is left-to-right; when the last agent exhausts, the
worker exits with code 78. Single-agent usage and the comma form share the
same code path — tamer claude is just agentList = ["claude"].
Per-agent arguments — what works and what doesn't
There is no syntax for distinct per-agent arguments in the comma form. Any arg you pass after the agent list is applied uniformly to every slot in the chain:
tamer claude,gemini -- --resume
# → claude gets --resume; if it exhausts, gemini also gets --resume.
tamer claude --session,gemini
# → claude is the agent; "--session,gemini" is a single arg passed to claude.
# Gemini is NEVER launched (it's not in the agent list — argv parsing
# sees only `claude` as the first non-option token).When you need distinct args per agent, run separate tamer processes
(one per agent), each with its own argv tail. They register independently
with the server and the master routes work by --labels / --role:
# Two separate workers, distinct args, both supervised:
tamer --labels primary --server wss://… claude -- --resume &
tamer --labels backup --server wss://… gemini -- --model gemini-2.5-pro &That two-process pattern is also the right answer when you genuinely want two agents running concurrently (e.g. one reviewer + one dev on the same repo) — the in-process comma form is one-at-a-time by design.
Supported agents
The agent list is driven by templates/agents.yaml and resolved at run time:
| id | Name | Reads |
|---|---|---|
| claude | Claude Code | CLAUDE.md + .claude/rules/ |
| gemini | Gemini CLI | GEMINI.md + .geminiignore |
| cursor | Cursor | .cursorrules + .cursorignore |
| opencode | OpenCode | .opencode/agents/ |
| codex | OpenAI Codex | CODEX.md + .agents/skills/ |
| copilot | GitHub Copilot | .github/copilot-instructions.md |
| windsurf | Windsurf | .windsurfrules + .windsurfignore |
| vibe | Mistral Vibe | ~/.vibe/config.toml (MCP auto-wired at spawn) + .agents/skills/ |
Anything else falls back to a generic adapter — tamer aider, tamer
my-custom-agent, etc. work too, they just get no agent-specific MCP /
prompt wiring.
List the registry from the CLI:
tamer init --list-agentsKey features
- Sandbox — bwrap on Linux (PID/UTS namespaces,
$HOMEtmpfs mask, granular re-binds for agent state, secrets-aware deny list), Job Object + Low Integrity on Windows, env sanitization everywhere. Configure with--sandbox <none|relaxed|strict|paranoid>orTAMER_SANDBOX_LEVEL. - MCP wiring — per-agent adapters drop the right config in the right
place:
.mcp.jsonfor Claude,~/.codex/config.tomlfor Codex,~/.vibe/config.tomlfor Vibe, an env-driven block for OpenCode, etc. The MCP child stays credential-free; the orchestrator holds the auth. - Skills — discover, install, and consult portable skill bundles
(
.tamer/skills/,.claude/skills/,.agents/skills/).tank_show_meandconsult_tankMCP tools let the agent ask the Tamer master for expert skill content over the supervised channel. - Approval policy — every detected tool use is classified
safe | medium | dangerousand auto-approved or escalated to the master / human according to the policy bound to the worker's role. - Multi-agent fallback —
tamer claude,geminiruns Claude as the primary; on quota exhaustion or atank_error, the next slot is picked up transparently with state-blob handoff. - Master supervision — the master sees PTY chunks redacted by a
shared redaction engine, drives the agent via WS envelopes
(
user_message,user_input,approval_response,terminal_resize,agent_abort, …), and can rewind via git checkpoints. - Token monitor & loop detector — token-spend thresholds with USD caps, plus a near-duplicate command/file loop detector that pauses agents stuck in self-similar trajectories.
- Multi-repo — workers register with
labelsandroles; the master routes WIs by role (dev,reviewer,tester,architect,doc-writer,security) and label match.
Common commands
# Setup
tamer init [--agents <ids>] [--list-agents] [--refresh-templates] [--dry-run] [--no-login]
tamer login # interactive auth (writes .tamer/config.yaml api_key)
tamer logout
# Run an agent
tamer [options] <agent> [agent-args...]
# Maintenance
tamer config <subcommand> # config show/set helpers
tamer sync-rules [--dry-run] [--force] # pull updated rule templates into .claude/rules/
tamer rules diff [--since=<git-ref>] # show drift between bundled and project rules
tamer policy show [agent] # print the active approval policy
tamer policy validate # static-check the policy fiches
# Skills
tamer skill list
tamer skill find <query>
tamer skill install <name>
tamer skill consult <topic> # conversational tank consult
# Reporting & ops
tamer journal show [filters] # local worker journal
tamer journal export --format=csv|json [filters]
tamer report # KPI/ROI rollup
tamer roi report
tamer frugality report
tamer degraded-mode list # WI degraded-mode inventory
tamer path-check <path> # check a path against path-protection rules
tamer sandbox check # run sandbox diagnostics
tamer mcp-server # spawn the tamer-worker MCP server (driven by the agent host)
tamer worker-patch-permissions [--dry-run] # repair .claude/settings.json hooksCommon top-level options
| Flag | Purpose |
|---|---|
| -c, --config <path> | Path to .tamer config file or directory |
| -s, --server <url> | WebSocket server URL (overrides config) |
| -n, --name <name> | Instance name (overrides config) |
| --api-key <key> | API key (overrides config) |
| -l, --labels <csv> | Instance labels (comma-separated) |
| --role <csv> | Worker roles: dev,reviewer,tester,architect,doc-writer,security |
| --sandbox <level> | none\|relaxed\|strict\|paranoid (default relaxed on Linux) |
| --no-sandbox | Disable all sandbox layers |
| --no-low-integrity | Disable the Windows Low Integrity layer |
| --no-job-object | Disable Job Object isolation |
| --no-env-sanitize | Disable env sanitization |
| --output-mode <mode> | raw (ANSI) or text (stripped) — default raw |
| --debug [file] | Debug logging (to file, or stderr if no path) |
| --diag | JSONL cross-layer tracing (correlation metadata only — no payload) |
| --verbose | Show debug lines on screen (requires --debug) |
| --debug-pwa [path] | PWA diagnostic feedback log |
| --ci | CI/CD mode: headless, no prompts, JSON alerts, exit 1 on block |
| --test | Test server connection and exit |
| -p, --passphrase <s> | E2E encryption passphrase |
| -h, --help | Show help |
| -V, --version | Show version |
Configuration
tamer init writes .tamer/config.yaml with sane defaults. The key fields:
server: wss://server.tamer-ai.dev
api_key: <set by `tamer login`>
# project_name: my-project
# encryption_passphrase: my-secret-passphrase
token_monitor: { warning_tokens: 50000, critical_tokens: 200000, hard_limit_tokens: 500000, ... }
loop_detector: { enabled: true, command_repeat_threshold: 3, ... }
checkpoint: { enabled: true, before_dangerous: true, periodic_minutes: 10, ... }
supply_chain: { enabled: true, ci_protection: { mode: approve } }
sandbox:
enabled: true
bwrap: # Linux only
hide_home: true
isolate_pid: true
isolate_net: false
home_binds: [~/.tamer/mcp-*, ~/.tamer/reply-*, ~/.claude, ~/.gemini, ~/.gitconfig, ...]
file_deny: [~/.ssh, ~/.aws, ~/.npmrc, ~/.netrc, ...]
job_object: { enabled: true, process_limit: 32, memory_limit_mb: 4096 }
low_integrity: { enabled: true }
prompt_setup:
enabled: true
agents:
claude: { setup: null } # reads CLAUDE.md automatically
codex: { setup: "Read CODEX.md and AGENT.md ...", wait_for: READY, timeout_ms: 30000 }
gemini: { setup: "Read GEMINI.md and AGENT.md ...", wait_for: READY, timeout_ms: 30000 }
opencode: { setup: "Read AGENT.md ...", wait_for: READY, timeout_ms: 30000 }See tamer init --refresh-templates to pull updated defaults into an
existing project (the previous file is kept as <name>.bak-<timestamp>).
Bundled binaries
The package installs two executables:
tamer— the main CLI.tamer-log— a tiny HTTP server that streams a PWA diagnostic log (/tmp/tamer-pwa-debug-*.log) in real time. Useful when working with the Tamer PWA dashboard.
License
MIT — see LICENSE.
