rig
One tool. One config. The whole dev culture of the coding-agent era — installed.
rig is the single front door to an entire ecosystem of agent-native tooling. From one
committed, declarative rig.yaml it sets up a repository — and a developer's machine — wiring
in the skills, agent-hooks, global git-hook dispatcher, CI gates, and MCP
servers that keep a team's engineering discipline intact when most of the code is written by
agents. One command, the same guardrails, every time, on every machine.
In the coding-agent era the bottleneck isn't writing code — it's keeping a hundred parallel
agent sessions on-culture: tests first, secrets never committed, review before merge, an
auto-mode that's actually safe. rig installs and reconciles that culture from the portable
catalog in agent-tools — the WHAT (the
content) to rig's HOW (apply it, reconcile it, prove it).
It's a peer to the rest of the ecosystem — tg-cli,
review-cli,
draw-cli,
3d-cli,
task-cli — composable, agent-native CLIs that
share one config-and-skills backbone. agent-tools is the WHAT (portable skills, guards,
CI gates, MCP); rig is the HOW — it reads your rig.yaml, converges the repo and the
machine to it (idempotently, with backups), and surfaces drift in both directions.
One development culture, from one control plane
Rig treats development setup and engineering policy as one declarative system rather than a pile of unrelated dotfiles. A global machine layer can establish defaults; committed rig.yaml files make repository-specific differences reviewable and reproducible. The same engine previews, applies, verifies, and reports drift.
That control plane already spans coding-agent skills and hooks, git hooks, CI gates, MCP servers, harness permissions/auto-mode, GitHub/repository settings, project-tool integrations, model/tool maintenance, and now JS/TS lint/format policy through Oxlint/Oxfmt + anti-slop. The intent is that humans and coding agents encounter the same constraints and preferred practices instead of each harness or repository inventing its own culture.
Where the surrounding tool exposes a preventative boundary, Rig installs a guardrail; where it cannot, CI/status/verification can still detect drift or unsafe state. The next cross-domain enforcement/advise model is tracked in #225, with consistent better-practice recommendations in #229.
This is useful for a team, but also for one developer with many repos and several agents: one policy source reduces setup drift, makes a new checkout predictable, and makes agent behavior less dependent on whichever harness happened to start the session. A dedicated onboarding/attestation command is tracked in #228.
Today Rig already has machine-wide global defaults plus per-repository overrides. The broader “change once everywhere” layer is explicit roadmap work rather than a hidden promise: fleet reconciliation #222, repository/stack/tag targeting #227 and #233, shareable team policy packs #223, and cross-domain rig rules / explain #224. The goal is to change lint, CI, hooks, agent capabilities, skills, MCP and other development policy globally—or only for the relevant stacks/projects—with one previewable operation.
Install
Pick one — each ends with rig on your PATH:
# 1. uv — recommended; macOS, Linux and Windows (incl. Cygwin / Git Bash)
uv tool install hyper-rig
# 2. pipx
pipx install hyper-rig
# 3. one-liner — clones to ~/.local/share/rig-cli, links rig into ~/.local/bin, registers the agent skill
curl -fsSL https://git.hyperide.ai/ultrabricks/rig-cli/raw/branch/main/install.sh | bash
# 4. from a clone — to hack on rig; `git pull` updates the installed tool
git clone https://git.hyperide.ai/ultrabricks/rig-cli && cd rig-cli && ./install.sh
- After 1 or 2, run
rig install-skillonce so coding agents discoverrig(3 and 4 do it for you). - Unreleased
main:uv tool install git+https://git.hyperide.ai/ultrabricks/rig-cli(or the same withpipx install). - Update:
uv tool upgrade hyper-rig·pipx upgrade hyper-rig· re-run the one-liner ·git pull. - Installed before the rename (as
rig-cli, from git)? Remove that first —uv tool uninstall rig-cliorpipx uninstall rig-cli— or two installs both claimrig. - Run without installing:
uvx --from git+https://git.hyperide.ai/ultrabricks/rig-cli rig doctor, or from a checkoutuv run bin/rig …/python3 bin/rig ….
On PyPI as
hyper-rig(the command is stillrig).rig-clion PyPI is an unrelated project —pipx install rig-cliwould install that. The canonical repo is git.hyperide.ai/ultrabricks/rig-cli;github.com/alex-mextner/rig-cliis a frozen, archived mirror.
textual (the rig init setup wizard) and rich (the rig stats report) are core
dependencies — every install above brings them, so rig init from a terminal launches the
wizard with no extra install step and no "go install textual" prompt.
Windows (Cygwin, Git Bash, PowerShell)
Use uv (option 1; get uv with winget install astral-sh.uv). ./install.sh and the one-liner
detect Cygwin/MSYS and do the same thing for you (uv tool install --editable from a clone,
from git otherwise).
- uv puts a native
rig.exeinto%USERPROFILE%\.local\bin, which is already on PATH in every Windows shell. The POSIX symlink install can't work there: Python is a native Windows program, it can't run a#!/usr/bin/env python3script, and Cygwin's own~/.local/binis not on PATH. - rig's home is your Windows profile (
%USERPROFILE%), not Cygwin's~: the global config is%USERPROFILE%\.config\rig\config.yaml, skills go to%USERPROFILE%\.agents\skills. rig install-skilllinks the skill into~/.claude/skillsas a directory junction when Windows refuses a symlink.rig applymakes many more symlinks — turn on Developer Mode (Windows 10: Settings → Update & Security → For developers; Windows 11: Settings → System → For developers) so Windows lets you create them without admin rights.- mintty (the Cygwin / Git Bash terminal) isn't a Windows console, so
rig initprints the non-destructive preview instead of the wizard: userig init --yes, or runrig initfrom Windows Terminal / PowerShell for the TUI. - Keep an agent-tools checkout where rig looks for one (
~/work/agent-tools,~/xp/agent-tools,~/agent-tools), or point at it:rig config set --global agent_tools_source <path> --commit.
Forgejo (fj) — log in with one click
The ecosystem lives on Forgejo at git.hyperide.ai; its CLI is fj
(forgejo-cli). Put the host in the global
config once — rig config set --global fj.host git.hyperide.ai --commit, or keep it in your
.rig-profile — and rig apply:
- installs fj —
brew install forgejo-clion macOS; on Linux and Windows the sha256-pinned release binary into rig's own dir~/.local/share/rig/fj/<version>/(not on PATH — see below); - adds
fj ship— the same ship gate asgh ship:fjon PATH is a thin rig wrapper in~/.local/binwhosefj ship <PR>runs exactly what thegh shipalias runs (the repo's.claude/scripts/pr-ship.sh, else agent-tools'ci/ship/ship.sh, else exit 127), and whose every otherfj …runs the real fj with the arguments, stdin and exit code untouched; - turns
fj auth logininto a browser login — every Forgejo instance ships built-in public OAuth apps; rig lists one for the host in fj'sclient_ids, so there is no token to mint and no app for an admin to register. fj refreshes the token itself; - does the same for git — with Git Credential Manager (bundled with Git for Windows) rig adds its
OAuth settings for
https://<host>: the firstgit pushopens the browser once; - drops
--host—FJ_FALLBACK_HOST=https://<host>in every shell (theenvarea on macOS/Linux, a user environment variable on Windows). Inside a clone fj still follows its remote.
Then, once per machine:
fj auth login # browser opens → Authorize
A repo whose rig.yaml names another instance (fj: { host: … }) gets that host set up too when
you rig apply in it. Headless machines: fj auth add-token with a token from
https://<host>/user/settings/applications. rig status shows anything not yet set up. Full
reference: docs/config-schema.md.
Where the real fj lives (fj itself is always the wrapper in bin_dir, ~/.local/bin):
Linux — ${XDG_DATA_HOME:-~/.local/share}/rig/fj/<version>/fj; macOS — Homebrew's, found on PATH
(so ~/.local/bin must come before Homebrew on PATH — rig status says so when it doesn't);
Windows — %USERPROFILE%\.local\share\rig\fj\<version>\fj.exe, with two wrappers in
~/.local/bin: fj (sh) for Cygwin / Git Bash and fj.cmd for cmd / PowerShell, where fj ship
runs through the same sh gh uses for its aliases (Git for Windows'). No fj.exe stays in
~/.local/bin: cmd, PowerShell and Python would pick it over fj.cmd. An fj that an older rig put
into ~/.local/bin is recognised by its pinned sha256 and moved; any other file there is left alone
(and reported). fj.install: false leaves the fj command to you — no wrapper, so no fj ship.
Commands
| Command | One-line |
|---|---|
rig init |
First-run onboarding. Scaffold rig.yaml and preview the agent-tools catalog it would wire in (with opt-out) — the front door for a repo/machine with no config yet. init never applies on its own: a bare rig init (no TUI/flags) writes nothing (pure preview); rig init --yes writes rig.yaml (config only); rig apply commit is what applies it (or rig init --yes --apply to scaffold + apply in one step). |
rig apply |
Declarative reconcile (kubectl-style): read rig.yaml, compute the diff vs the repo's state, converge, idempotently. Preview-by-default: a bare rig apply is an alias for rig apply info — it prints the plan and mutates nothing; rig apply commit actually executes it (with per-phase progress and a ✓ applied N (C changed, M unchanged) completion line). The steady-state command you re-run on every machine; hand-edits that drift from the config are surfaced by rig status. --dry-run previews; --only skills,ci scopes; -v lists already-in-sync no-ops; a bare rig apply --yes executes (automation back-compat). |
rig status |
Detect + report drift in both directions, grouped by GLOBAL/REPO layer and by every area rig reconciles (skills, all configured agent-hook targets, CI, MCP, symlinks, repo settings, auto-mode, tmux, model cron). |
rig doctor |
Detect + (offer to) install every tool rig/agent-tools need, across brew / apt / dnf / pacman / zypper. --yes installs non-interactively. |
rig export |
Write a starter rig.yaml from detected defaults without a TUI (recommends auto-mode on). |
rig setup |
The interactive configuration wizard. In a terminal it shows what is enabled across every reconciled area, lets you change any option (with an inline hint per option) in the local rig.yaml AND the global ~/.config/rig/config.yaml, then applies. Non-interactive (piped / no TTY) it prints usage for init/apply/config get|set. |
rig config get|set |
The headless counterpart to the wizard. get reads one nested key. set <dot.path> <value> is preview-by-default: it validates the prospective config entirely in memory, shows the config change + resulting reconcile plan, and writes nothing. Add --commit to write the change and execute that same validated plan; --no-apply is the explicit legacy write-only mode. --global targets ~/.config/rig/config.yaml. |
rig env list|set|del |
Machine-wide env.vars keys (search API keys, COLORTERM, …). Always writes ~/.config/rig/config.yaml (the same path as rig config set --global env.vars.KEY). set/del write + reconcile by default so ~/.config/rig/env/rig.env.sh updates; omit VALUE to read stdin (keeps the secret out of argv/history). list masks values (*** + last 4); --show prints them in full. Both files are plaintext — this is not a secret store. |
rig profile init|sync|status |
Carry your global config between machines through a private git repo (<forge>/<login>/.rig-profile) holding ~/.config/rig/config.yaml. init [URL] clones it (or seeds an empty repo with this machine's file and pushes) and records profile.repo; without a URL it uses profile.repo, else https://<host>/<fj login>/.rig-profile when fj whoami knows the login. sync is two-way: only local changed → commit + push; only remote changed → pull (the local file is backed up first); both changed → refused, nothing written — pick a side with --take local|remote. status [--fetch] says which side changed, writing nothing (exit 3 when not in sync). Once a profile is set up, rig apply commit syncs it first on every machine (non-fatal; profile.auto_sync: false turns that off). Pushes warn about env.vars secrets and are refused to a repo the forge reports as public. See below. |
rig config-web |
A machine-wide web console — one browser tab per rig-managed repo (discovered from the repository registry) plus a Global tab for ~/.config/rig/config.yaml alone. Each tab renders every area with its live effective value, tagged with the layer an edit lands in, next to a drift panel (the same two-way engine rig status uses). An edit routes to the owning layer and is written by the same engine as rig config set/the wizard (fail-closed validation) — then the page offers an interactive apply: a plan preview (mirrors rig apply info, each action tagged with what kind of change it is — creates a file, installs a hook, changes a permission, etc.), confirm-or-skip individual actions, then live per-phase progress as the same shared engine (plan.build + run_plan) rig apply/rig init use actually applies them. A stale preview (config changed since you looked) is refused rather than silently applied; only one apply runs at a time. Lifecycle is the shared agenttools-service manager: run (foreground) / start (background daemon) / status / stop / enable (install launchd-macOS / systemd---user-Linux autostart + start) / disable. Binds 127.0.0.1 only, with a same-origin (CSRF) + Host (DNS-rebinding) guard on every mutating/compute-triggering request. A bare rig config-web prints help, never launches. --port, -C <repo>. The lifecycle verbs need the agenttools-service lib (an agent-tools nested lib, not on PyPI): uv pip install --python <rig's interpreter> -e <agent-tools>/lib/agenttools_daemon -e <agent-tools>/lib/agenttools_service (the --python target makes the libs land where rig imports them; the error message prints the exact interpreter path). Without it, rig --help and every other command still work; a lifecycle verb fails closed with that install hint. |
rig evolve |
Project evolution portal. Serve a local browser UI with a git activity histogram, proportional file treemap, clickable selection, and provider health. The first slice is file-level and read-only; symbol/LSP/provider overlays build on the same API. Lifecycle uses the shared agenttools-service verbs: run / start / status / stop / enable / disable. A bare rig evolve prints help, never launches. --port, -C <repo>. |
rig install-skill |
Register the rig agent skill so skills-directory harnesses auto-discover it (currently Claude Code and Codex). |
rig stats show |
Tool-adoption and escape-hatch analytics. Parse the session logs of every agent harness on the machine and report how often each tool is invoked, bucketed into baseline / ours / review-cycles / external-advertised / other — so you can see whether the rig + agent-tools ecosystem is actually being used vs the built-in baseline. Also reads ~/.config/agent-tools/overrides.log and hatch-relevant ship-audit.jsonl bypass lines, grouping escape hatches by hook and splitting broken-gate vs lazy. `` `--format json |
rig worktree create |
Standardized agent-worktree creation. Idempotently registers .worktrees/ in the repo's .git/info/exclude (never the committed .gitignore), THEN creates a linked git worktree at <repo>/.worktrees/<name> — the one convention rig converges every repo on, replacing the several ad hoc locations (Claude Code's own .claude/worktrees/, a bare in-repo .worktrees/, a sibling-of-repo .worktrees/) that had grown up around the ecosystem — so a freshly created worktree never dirties git status in the primary checkout, and a broken exclude file is caught before any worktree exists. --branch overrides the branch name (default: <name>); --from <ref> overrides the base ref (default: HEAD). In a bun repo with bun.enabled, it then runs bun install --frozen-lockfile in the new tree, as the repo's own bunfig configures it (bounded at 15 min; only the global config turns it on, bun.worktree_install: false turns it off globally or per repo), and rig status flags broken worktree installs — a node_modules symlink, broken links into the store — and notes off-store ones (see docs/config-schema.md bun). |
rig worktree remove |
The inverse of create. Force-removes the linked worktree at <repo>/.worktrees/<name> (git worktree remove --force), then deletes the branch it had checked out (git branch -D) — read from git worktree list --porcelain -z, not assumed to equal <name>, since create --branch can diverge from the directory name. git worktree remove alone leaves the branch behind, so skipping this step would make a bare retry of create with the same name fail with "a branch already exists". Refuses outright (no worktree removed) if the branch can't be reliably identified first, if .worktrees/ is a symlink pointing outside the repo, or if .worktrees/<name> ITSELF is a symlink (a real worktree root is never a symlink, so this refusal never blocks legitimate use). A genuinely detached-HEAD worktree (no branch to find) is removed cleanly with no second step. Also recovers a worktree whose directory was deleted by hand (rm -rf instead of git worktree remove) but is still registered with a live branch. Requires git >= 2.36. |
rig worktree gc |
Classify and clean up worktree sprawl. Lists every worktree git knows about for a repo — wherever it physically lives, not just the standardized .worktrees/<name> — and classifies each as live (a running claude/codex/opencode process has it as its cwd — checked FIRST, absolutely, before anything else), prunable (its directory is gone but git still has it registered), dirty (uncommitted changes — never auto-removed), merged/closed (its PR resolved via gh pr list), no-pr-stale (clean, no PR, older than --older-than-days, default 14), or active (an open PR, or recent activity). Report-only by default; --yes actually removes merged/closed/prunable (plus the branch, mirroring remove's two-step contract); no-pr-stale additionally needs --include-stale. --dry-run always forces a report even with --yes, mirroring rig apply --dry-run. --repo <path> targets one repo; omitted, it fans out over every repo the machine-local repository registry (rig config-web's same discovery) already knows about. rig status reports a cheap (no disk-size scan) stale-worktree count using the same classifier. Caveat: a merged/closed worktree is only auto-removed once its branch's commits are unreachable from no surviving ref — with squash/rebase-merge + fetch --prune (a common GitHub setup) that's never true, so merged worktrees classify dirty and are kept instead; see the module's "Known limitations" for the full trade-off. |
rig daily |
Merged-PR "what shipped" report, ready to paste into a Slack Daily channel. Source of truth is gh pr list --state merged — never an LLM call, never a ticket status alone. Grouped into Security / Infra-CI / Performance / Product-UX / Other, one plain-language fact per line, ticket/PR reference last in parentheses. Default repos: hyperide/hyper-saas, hyperide/hyper-ext-e2e (override with repeatable --repo or ~/.config/rig/daily.yaml's repos:). Tracks a PER-REPO watermark at ~/.config/rig/daily-state.json so a plain rig daily run never repeats a PR — each repo's watermark only advances when THAT repo's own fetch succeeded and returned a complete page, so one repo's outage or a newly-added repo never borrows another repo's cursor; if every configured repo fails, the command exits non-zero instead of a misleading empty report. --since (relative 24h/7d or an ISO-8601 timestamp) and --dry-run are always read-only. rig daily install-skill registers the daily agent skill the same way rig install-skill does. |
rig report |
HTML completion report from git history since the merge-base with main — the HyperIDE /result-report gather, as a rig command. Writes a self-contained HTML file you can open (file:// path printed on stdout) plus a text summary of commits and files. Does not publish to GitHub Pages. rig report [TASK_ID], --title, --out PATH, --base REF, -C. |
rig usage |
Claude token/cost usage across accounts. Aggregates real per-message token usage from ~/.claude/projects and every ~/.claude-accounts/account-*/projects (the accounts managed by the separate claude-rotate tool), by model, account, and token type (input/output/cache-write/cache-read). Cost is a HYPOTHETICAL estimate at published Claude API list prices — this is a Claude.ai subscription, not pay-per-token billing, so it is never a real bill; a model ID not in the priced table is reported separately as "unpriced", never guessed at. Bare rig usage reports the current week AND current month; --period day|week|month narrows to one window. --json emits the stable, versioned contract (schema, generated_at, disclaimer, accounts_scanned, periods) that a separate, independently-built tg-cli command invokes on a schedule: rig usage --json --period week at end-of-week, rig usage --json --period month at end-of-month. Read-only; parsing is streaming/line-by-line with mtime-based file pruning, so a scheduled run only re-parses the files actually touched in the requested window, not the whole history. |
Not rig subcommands, but provisioned by rig apply: gh ship <PR> (a gh alias, see
ship_delegator) and fj ship <PR> (the fj wrapper
the fj block installs) — one command under two names: both
run the same dispatch to the repo's ship delegator / agent-tools' ship.sh, which picks GitHub or
Forgejo by the origin's host (its Forgejo provider: agent-tools#793).
The Codex updater that used to be rig codex update moved to harness-cli as harness codex update (same options); rig reconciles the dev environment and no longer updates agent-harness binaries.
Quick start — init then apply
There are two commands, and they are not the same thing: rig init is first-run
onboarding (no config yet → scaffold one + preview the catalog it would wire in);
rig apply is the steady-state reconcile (config exists → converge the disk to it), and it too
is preview-by-default — a bare rig apply prints the plan and applies nothing; rig apply
commit is what actually executes. You run init once to scaffold + review the plan, then rig
apply commit to apply (and re-apply forever after; rig apply alone to re-preview). The default
rig.yaml init writes provisions auto-mode (the agent
runs autonomously with minimum babysitting) — recommended on by default, and safe because the
agent-hook guards are applied alongside it.
init does NOT apply by default — that is deliberate. A bare rig init with no TUI and no
flags writes nothing and applies nothing; it prints a non-destructive PREVIEW of the plan
and how to proceed (it should never "do a bunch of things" with no instruction). rig init --yes
scaffolds rig.yaml (config only — still nothing applied), then you run rig apply commit. To do
both in one step, use rig init --yes --apply (the explicit one-shot).
How init decides its mode (TTY + flags). A bare rig init runs the interactive TUI wizard
(with Export-config-only vs Apply buttons) whenever there is a TTY — textual ships WITH rig
as a core dependency, so the wizard is always available; no install step. With no TTY (piped / CI
/ agent), or with --no-tui / RIG_NO_TUI=1, it falls back to the non-destructive PREVIEW instead
of hanging on a wizard nothing can drive. Any explicit signal (--yes / --config … --yes /
--apply) is non-interactive. (rig apply is never interactive — it has no wizard; rig apply
commit executes headlessly.)
rig doctor # check deps; rig doctor --yes to install
rig init # no config yet: scaffold rig.yaml + PREVIEW the plan
rig apply # PREVIEW what apply would do (mutates nothing)
rig apply commit # execute it (and re-apply on every machine, identically)
rig init --yes --apply # or scaffold + apply in one step (the explicit one-shot)
rig status # later: has the repo drifted from rig.yaml?
rig setup # interactive wizard: see + change every area, then apply
To edit the config before applying: rig export -o rig.yaml, tweak it, then rig apply commit.
rig setup — the interactive config wizard. In a terminal it shows what is enabled across
every reconciled area (the rig status rows), lets you toggle/change any option in the local
rig.yaml AND the global ~/.config/rig/config.yaml — each option with an inline hint of how
and why — then applies the change on the spot. Run from a non-TTY (a pipe/redirect) it prints
usage for the core commands instead of a half-wizard. For scripted single-value edits use its
headless counterpart rig config get <dot.path> / rig config set <dot.path> <value> — a
dot-path editor whose writes are previewed by default. Add --commit to write + reconcile;
--global targets the global config and --no-apply is explicit write-only mode.
Headless / agent path (no TUI):
rig init --yes # scaffold rig.yaml (config only; nothing applied)
rig apply commit # apply it; re-apply identically on every machine
# or, the explicit one-shot:
rig init --yes --apply # scaffold rig.yaml AND apply in one step
rig stats — is the ecosystem actually being adopted?
rig apply installs the tooling; rig stats tells you whether anyone is using it. It
reads the on-disk session logs of every agent harness on the machine and counts how often
each tool is invoked, sorting every invocation into five buckets:
- baseline — the harness built-ins (
Bash,Read,Write,Edit/MultiEdit,Grep,Glob,NotebookEdit,Task/Agent,WebFetch/WebSearch). The yardstick. - ours — the agent-tools ecosystem only: the CLIs
rig/tg/draw/3d/task/dev/pm/research/harness/qa/stt(detected inside a shell command — aBashcall runningtg …is pulled out of the baseline shell count and re-labelledtg (cli)), plus our skills exceptreview. Not the review MCP. - review-cycles — the review CLI inside Bash (re-labelled
review (cli)),mcp__review__*, andskill:review. The adoption ratio (ours / (ours+baseline)) excludes this bucket, so review volume does not inflate agent-tools adoption. - external-advertised — the third-party tooling we ship/recommend: MCP servers (serena,
sverklo, context7, playwright, …) via the
mcp__<server>__<tool>prefix, plus external skills (agent-browser, superpowers, h-*, debate-swarm, …). - other — everything else.
rig stats show # default: rich terminal UI (tui)
rig stats show --format json # canonical machine-readable data
rig stats show --format web # self-contained local HTML dashboard
rig stats show --since 2026-06-01 --until 2026-06-15 # window + period comparison
rig stats show --harness claude-code --repo /path/to/repo # filter by harness / repo
Harnesses parsed: Claude Code (~/.claude/projects/<enc>/<session>.jsonl — the richest
source), Codex (~/.codex/sessions/.../rollout-*.jsonl, or
$RIG_CODEX_HOME/sessions/.../rollout-*.jsonl), Gemini
(~/.gemini/tmp/<hash>/chats/session-*.json), omp
(~/.omp/agent/sessions/<enc>/**/*.jsonl, or $PI_CODING_AGENT_DIR/sessions/...), and opencode
(~/.local/share/opencode/storage/). The supported-harness list is data-driven: each
parser self-registers, and a harness whose logs aren't on the machine is reported as
"not found" rather than failing. Adding a harness is one file in riglib/stats/sources/.
Parsed session files are cached per-source under ~/.cache/rig/stats/<harness>.json,
keyed by each file's (mtime, size): a closed session (the overwhelming majority) is never
re-parsed once cached, so a repeat rig stats show only pays for genuinely new/changed
session activity — the cache is purely an optimization and is transparent to every filter
(--repo/--harness/--since); delete the directory any time to force a full rescan.
Outputs: json is the canonical shape every other renderer draws from; tui (default)
is a rich table-and-bar-chart report that degrades to plain text if rich isn't installed;
web serves a self-contained HTML page (inline SVG charts, no CDN, no JS deps) on a local
port (--web-port, default auto). All three break the counts down by repo and by
harness and render a daily trend; the json document additionally exposes the
weekly series. --since yields a before/after period comparison: the selected window
against the equally-long window immediately before it.
Escape hatches: the same command also reads the agent-tools hatch audit
(~/.config/agent-tools/overrides.log — primary; field hatch is the hook id) and
hatch-relevant lines in ~/.config/agent-tools/ship-audit.jsonl whose decision
contains bypass: (mapped to ship-external-review or ship-review-quorum). Events
are grouped by hook and split broken-gate (the gate fired wrong — e.g.
ship-review-quorum with models: 0 / quota / 2 of 3, or orchestrator-stays-thin
on a dispatched leaf worker) from lazy (the agent reached for a hatch instead of
the sanctioned alternative). The same ts+hook in both files counts once (overrides.log
wins); duplicate rows inside overrides.log are not collapsed. Missing files are an
honest zero, never a crash. JSON exposes a first-class "hatches" key; tui/plain
always print an Escape hatches section (Escape hatches: 0
when empty). --home and --since/--until apply the same way they do for tool
adoption. Telegram history is not parsed separately — overrides.log is the tg-ctl
hatch-question audit sink.
Config — rig.yaml
rig.yaml is committed by default. It is the reproducible source of truth: commit it,
and rig apply reproduces the same install on any machine and in any agent session.
The config cascades by location (no scope flag):
- Global —
~/.config/rig/config.yaml(machine-wide defaults you carry across repos). - Per-repo —
./rig.yaml(overrides the global layer; committed).
Dicts merge recursively (per-repo wins); lists/scalars replace wholesale. See
docs/config-schema.md for every key. A worked example is
rig.yaml at the repo root (this repo dogfoods its own config).
Global git settings — git:
The global config can provision git config --global too, so a fresh machine gets your identity
and git defaults from the same file as everything else:
git:
config:
user.name: Alex Ultra
user.email: someone@example.com
pull.rebase: true
init.defaultBranch: main
rig apply commit sets each key that is missing or different (bools as true/false), records the
prior value of anything it overwrote, and never unsets a key you set by hand; rig status reports
each drifting key. The block is global-only — a repo rig.yaml that declares git: is rejected,
because keys like core.sshCommand run code. Details:
docs/config-schema.md#git.
rig profile — carry your global config between machines
Your global config lives in one file, so it can travel through one private git repo:
fj repo create .rig-profile --private # once, on the forge (rig does not create repos)
rig profile init https://git.hyperide.ai/<login>/.rig-profile # machine A: seeds the empty repo
rig profile init https://git.hyperide.ai/<login>/.rig-profile # machine B: adopts it
rig profile sync # after a change on either side
rig profile status --fetch # which side changed? (writes nothing)
- Two-way, never a silent merge. rig compares the local file, the repo's copy, and the snapshot
of the last sync. Only local changed → commit + push. Only the repo changed → pull, after backing
the local file up as
config.yaml.rig-bak-<stamp>. Both changed → refused with both paths printed; merge by hand into the local file andrig profile sync --take local, or keep the repo's with--take remote. Both files are validated before they cross, so a broken config is never pushed or adopted. - The repo must be private.
env.vars(rig env) often holds API keys and the file is plaintext. Every push of a config withenv.varswarns (naming the keys), and a push is refused when the forge reports the repo as public to an anonymous API request. - Commits use this machine's git identity — provision it with the
git:block above. With none, the push fails and namesgit.config.user.name/user.email. - Every machine with a profile keeps it in sync:
rig apply commitsyncs before it builds the plan; a failure warns and applies the local file, and a pull is flagged as a warning (that plan comes from a config you did not preview). A preview never syncs.profile.auto_sync: false(rig profile init --no-auto-sync) turns this off — for every machine, since the setting travels in the synced file. - The checkout is
~/.config/rig/profile/repo/(rig-owned) and the snapshot~/.config/rig/profile/state.json(never committed).initrecordsprofile.repothrough the same writer asrig config set --global, which does not keep YAML comments. The agent-toolsprotect-mainpre-push hook is switched off in the profile checkout only (git config hooks.skipGlobal protect-mainthere — a one-file config repo has no PR flow); every other repo keeps it, and the secret scan still runs on profile commits.
Autonomous mode — global agent operating policy
mode.name: autonomous belongs in the global config (~/.config/rig/config.yaml). It declares
how an agent should keep working before it asks for help: review/fix iterations until a clean
state, review quorum for decisions, escalation through the configured framework skill, parallel
worktree comparison before escalation, allowlisted development-tool flows, and limit-aware
parallelism caps.
mode:
name: autonomous
autonomous:
review_fix: { enabled: true, max_iterations: 5, until: clean }
decisions:
review_quorum: { enabled: true, min_iterations: 2, min_models: 3 }
escalation:
framework_skill: decision-request-discipline
require_parallel_worktree_comparison: true
parallel_worktree_comparison: { enabled: true, candidates: 2 }
development_tools:
allow: [Bash(dev:*), Bash(review:*), Bash(task:*)]
parallelism: { max_agents: 4, max_worktrees: 4, reserve_slots: 1, limit_aware: true }
rig apply --dry-run surfaces that policy as plan notes, and the development-tool allow rules
flow into the existing additive permissions.allow merge for supported harnesses. Raw
development-tool allow rules are currently applied only to Claude Code's verified permission-rule
dialect; unsupported harnesses get a plan note and the rules are skipped. framework_skill is a
named behavioral skill for agents to follow during escalation, not a callable interface invoked by
rig.
Auto-mode — provisioned by the reconciler
A harness: block tells rig apply to write the agent harness's auto/permission setting,
so autonomy is part of the reproducible config — not a manual per-machine toggle:
harness:
enabled: true
kind: claude-code # skills-dir: claude-code|codex · native: opencode|omp · instruction-file: pi|commandcode (codex also reads AGENTS.md)
auto_mode: true # RECOMMENDED: writes permissions.defaultMode=auto (user scope)
hook_bridge: { enabled: true } # wire the agents-hooks/v1 → harness dispatcher (default ON)
For claude-code, auto_mode: true writes permissions.defaultMode=auto to the user
settings (~/.claude/settings.json) — Claude Code honors auto only at user scope (it ignores
it in a repo's project settings), so auto-mode is a per-machine setting: declare the
harness: block in the global config (~/.config/rig/config.yaml), not per repo. auto
(a safety-classifier preview) auto-approves but a classifier blocks anything that escalates
beyond your request, touches unrecognized infrastructure, or looks prompt-injected — strictly
safer than bypassPermissions (which skips every check; pin mode: bypassPermissions to opt
into full bypass at project scope, e.g. inside a container). rig apply merges only that one
key (everything else is preserved), idempotently with a backup on conflict, and rig status
flags drift. Defense-in-depth: the agent-hook guards rig installs in the same pass
(block-secrets-write, block-no-verify, enforce-timeout-on-bash, block-raw-process-env,
block-raw-pr-merge, block-reset-hard) catch dangerous tool calls before the side
effect, complementing the classifier.
Those guards only fire because of the hook bridge. Harnesses run hooks declared in their own
native config/plugin surfaces, not the descriptor files agent_hooks installs, so a bridge is
required to make the descriptors actually execute (agent-tools#18). The same harness block
therefore registers the matching bridge: Claude Code gets cc_hook_bridge in settings.json,
Codex gets codex_hook_bridge in ~/.codex/config.toml (or
$RIG_CODEX_HOME/config.toml), and opencode gets
opencode_hook_bridge/plugin.js symlinked into the repo-local
.opencode/plugins/zz-agent-tools-hook-bridge.js ordered plugin path. Without that bridge the
guards above would be inert files. Set hook_bridge: { enabled: false } to opt out.
If agent_hooks.target points at a custom descriptor directory, the bridge remains registered
with that descriptor-dir override; opencode uses a small managed wrapper plugin for this case.
Because that plugin path is machine-local, rig also adds it to the repo's .git/info/exclude; when
upgrading from the prior global opencode bridge path, rig removes the old managed global symlink
if it still points at an agent-tools opencode bridge plugin.
See docs/config-schema.md for the full harness schema and the
per-harness event coverage.
Model-freshness schedule — a daily cron, provisioned by the reconciler
A models: block tells rig to provision a daily cron that runs the agent-tools
model-freshness checker (lib/checker/model_freshness.py) — which polls provider
model-list endpoints and proposes version bumps to the model board. On rig init AND
rig apply, rig checks whether the schedule is installed and installs it if missing
(idempotent):
models:
enabled: true
schedule: { time: "12:00" } # daily at noon (default)
Cross-platform: macOS → launchd (a ~/Library/LaunchAgents/ai.hyperide.model-freshness.plist
loaded via launchctl), Linux → crontab (a sentinel-fenced managed line). rig status
reports whether the schedule is installed or drifted; rig doctor flags a missing scheduler
binary. See docs/config-schema.md for the full schema.
Drift — surfaced both ways, never silently reconciled
rig status reports two directions:
- config→disk — declared in
rig.yamlbut missing/modified on disk.rig applyconverges these. - disk→config — installed on disk but not declared (orphan / hand-added). These are reported, not deleted — you decide whether to adopt them into the config or remove them.
The status headline is grouped by reconciled area under the GLOBAL machine-wide layer and, when
you are inside a git repository, the REPO layer from ./rig.yaml. Outside a git repository,
rig status ignores any auto-discovered local rig.yaml, shows only GLOBAL areas, and prints
that the repo layer / rig.yaml is N/A; it does not tell you to commit a repo config where no
repo exists. An explicit --config can still declare GLOBAL areas in that mode, but repo-scoped
areas remain N/A until you run status inside a git repository.
How rig consumes agent-tools (the integration seam)
rig never vendors agent-tools content. At runtime it locates an agent-tools checkout —
agent_tools_source in config, else $RIG_AGENT_TOOLS_SOURCE, else ~/xp/agent-tools /
~/work/agent-tools / ~/agent-tools — and scans it live into a catalog
(riglib/catalog.py):
| agent-tools path | becomes |
|---|---|
skills/universal/<name>/SKILL.md |
a skills item (group universal) |
skills/by-type/<kind>/<name>/SKILL.md |
a skills item (group by-type/<kind>) |
agent-hooks/<name>/<name>.<point>.json |
an agent_hooks item |
ci/<name>/{workflow.yml,*.sh} |
a ci item |
git-hooks/global-dispatcher/ |
the git_hooks dispatcher item |
mcp/<name>/ |
an mcp item |
The catalog drives config validation (unknown item names fail closed), the wizard's
description panes, and the install actions. Update agent-tools, and rig picks up new
items on the next scan — no code change in rig.
Universal skills vs. a project's AGENTS.md
rig is the universal skill layer. Cross-project, always-apply MANDATORY skills (for
example visual-proof-cycle or task-completion-selfcheck) are provisioned by rig from
the agent-tools catalog and meant to reach every project and every user through the
SessionStart blurb, the rig-installed skills, and each skill's own trigger description.
That layer is their single source of truth.
A project's AGENTS.md (or a repo-level CLAUDE.md) is for project-specific guidance
only — how this repo builds, its layout, its local conventions. Never duplicate a
universal mandatory skill into an individual AGENTS.md: it pins a stale copy to one repo,
hides the real source, and goes stale the moment the skill changes. The universal layer is
the one place that carries these mandates — let it, and keep AGENTS.md project-specific.
Architecture
riglib/
cli.py argparse + subcommand dispatch (lazy imports)
catalog.py scan an agent-tools checkout → item registry ← the integration seam
config.py cascade loader + fail-closed schema validation
detect.py env/project + OS/package-manager detection
plan.py (config + catalog) → ordered InstallPlan ← shared by init & apply
schedule.py pure planning of the model-freshness cron artifact (launchd/crontab)
drift.py two-way drift detection
doctor.py dependency diagnosis + bootstrap across package managers
state.py SetupState ⇄ rig.yaml (the single serializer)
install.py install-skill (agent discovery)
logging.py opt-in JSONL structured logging (stdlib)
actions/ stdlib-only install actions (the executor)
runner.py run_plan: copy_skill / install_agent_hook / install_dispatcher /
install_ci / register_mcp / apply_harness / provision_schedule —
idempotent, backup-noted
fsutil.py conflict-policy + idempotency + backup helpers
stats/ tool-adoption analytics (`rig stats show`) — a 3-stage pipeline
sources/ one pluggable parser per harness (@register); CC / codex / gemini /
omp / opencode → a normalized ToolInvocation stream
taxonomy.py the data-driven baseline / ours / external-advertised / other rules
aggregate.py pure reductions → counts / breakdowns / day+week trend series
render/ json (canonical) / tui (rich, lazy) / web (http.server + inline SVG)
tui/app.py the textual wizard — a thin front-end over the same engine
setup and apply share one plan builder and one executor; the TUI just wraps
the executor with a progress view. One code path, two front-ends — the wizard can't drift
from apply.
Development
uv venv && . .venv/bin/activate
uv pip install -e '.[test]' # core deps (pyyaml, textual, rich) + pytest
python -m pytest -q # unit suite
bash tests/smoke.sh # end-to-end smoke (needs an agent-tools checkout)
bash tests/smoke.sh --fast # the seconds-cheap pre-commit subset
scripts/install-smoke-precommit.sh # wire the fast smoke into .git/hooks (once per clone)
python docs/gen_svgs.py # regenerate the diagrams
Run scripts/install-smoke-precommit.sh once after cloning to gate your commits on the fast
smoke locally — a commit that breaks the real rig status flow is then blocked before push,
not just in CI.
How rig compares
Most setup tools fall into three buckets. Dotfile managers (chezmoi, yadm) version a
person's config across machines — ~/.gitconfig, shell rc, secrets. Scaffolders
(cookiecutter) stamp a project once from a template and walk away. Config-as-code
(Projen, Nix home-manager) regenerate managed files from a typed/declarative source and
keep them in sync.
rig is config-as-code, but aimed at a different target: a repository's agent
guardrails — skills, agent-hooks, the global git-hook dispatcher, CI gates, and MCP
registrations — sourced live from the agent-tools
umbrella. It is declarative + idempotent (one rig.yaml, re-apply identically on any
machine), it detects drift in both directions (config→disk and orphan disk→config,
reported not silently overwritten), and it bootstraps the dependencies those guards
need across brew/apt/dnf/pacman/zypper.
| Tool | Target | Declarative config | Idempotent re-apply | Bidirectional drift | Agent skills / hooks / CI gates | Dep bootstrap |
|---|---|---|---|---|---|---|
| rig | a repo's agent guardrails | ✓ (rig.yaml) |
✓ | ✓ (both ways, reported) | ✓ | ✓ (multi-PM) |
| chezmoi | personal dotfiles | ✓ | ✓ | ~ (diff vs source) | — | — |
| yadm | personal dotfiles | ~ (git + alt files) | ✓ | ~ (git status) | — | — |
| cookiecutter | new project from template | — (prompts once) | — (one-shot) | — | — | — |
| Projen | project build/CI config | ✓ (typed JS) | ✓ (synth) | — (overwrites) | — | — |
| Nix home-manager | a user's whole env | ✓ (Nix) | ✓ | ~ (rebuild) | — | ✓ (Nix store) |
~ = partial. Dotfile managers and home-manager are per-user; cookiecutter is one-shot;
Projen reconciles build config but overwrites rather than reporting drift and knows nothing
of agent skills/hooks. rig is the only one of these whose unit of work is a repo's
agent-facing guardrails — and the only one that surfaces hand-added orphans instead of
clobbering them.
Ecosystem
Part of the HyperIDE.ai agent toolchain. Sibling tools that already solve adjacent problems — check before building your own:
- rig-cli — sets up a repo (and a dev machine) from a committed rig.yaml: skills, agent-hooks, git-hook dispatcher, CI gates, MCP, and the agent harness's auto/permission mode
- tg-cli — simple Telegram CLI to send messages, photos & files, and a two-way agent bridge (reports, Q -> buttons, voice/rich)
- review-cli — multi-model read-only code review from one command: diff review, cited quorum, brainstorm, visual review, and interactive spec-review tooling
- agent-tools — the shared catalog rig applies: portable agent skills, agent-hooks, the global git-hook dispatcher, CI gates, and MCP servers
- draw-cli — text-to-image via Hugging Face
- 3d-cli — scriptable CLI for the full 3D FDM lifecycle: modeling, mesh repair, slicing, and print monitoring
- task-cli — enforced ticket-system CLI for agents (GitHub Issues / Linear): acceptance criteria, motivation, and user-impact gates before work starts
- dev-cli — project-scoped dev/e2e process runner: start/list/stop dev servers and e2e jobs
- hyperide.ai — Figma replacement inside VS Code — edit React components directly through AST/LSP without AI hallucinations, token waste, or context-window limits
This section is kept in sync by rig apply from a canonical registry — edit it in rig-cli's riglib/ecosystem_readme.py, not here; a hand-edit here does not survive the next apply that reconciles this block.
Each CLI registers a skill into your agent harnesses (<tool> install-skill) so agents know it exists — see Install.
A machine opts into provisioning these tools with a global tools: block (see the config), and rig apply runs each tool's own install.sh. It also keeps them fresh: if a tool's repo ships a scripts/deploy.sh, rig apply runs it (a safe fast-forward-only git pull) on every apply — even when the tool is already installed — so a provisioned checkout doesn't silently drift behind origin. Freshness is opt-in per tool (no deploy.sh → skipped) and non-fatal (an offline/dirty/diverged tree is a warning, never an apply error). Both install.sh and deploy.sh run under the RIG_TOOL_INSTALL_TIMEOUT_S budget (default 300s) so a hung script can't wedge apply; raise it for a slow network fetch.
License
MIT — see LICENSE.
Release files for hyper-rig 0.53.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hyper_rig-0.53.0.tar.gz | 1.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hyper_rig-0.53.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.7 MB
Release files / hyper_rig-0.53.0.tar.gz
| Download URL | hyper_rig-0.53.0.tar.gz |
|---|---|
| Size | 1.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1e99386d31e7ff30ea204a0b63a5f91a88f0a77e11e38b6e325c0bbcf0036c77
|
|
BLAKE2b-256 checksum How to use checksums |
69bd74f6993a49b943e2773edf3eaed8211dd4272d27ba680b3d39c04f1b1bf6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / hyper_rig-0.53.0-py3-none-any.whl
| Download URL | hyper_rig-0.53.0-py3-none-any.whl |
|---|---|
| Size | 1.0 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
25bace63059868367d857c0f99e8d769c6b3fee7fd66ceb8f0b11a703d9155a7
|
|
BLAKE2b-256 checksum How to use checksums |
efb85210777c17a3d103a64b484691fa53ef8ce6f158290781efa30a1bc846f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|