Getting started
Install
cagr ships as one compiled binary for macOS (arm64, x64), Linux (x64, arm64, glibc and musl) and Windows (x64).
With the install script, from GitHub releases:
curl -fsSL https://github.com/krtinshet/cagr/releases/latest/download/install.sh | sh
The script picks the binary for your system, checks it against the release’s SHA256SUMS, and puts it in ~/.local/bin. If that folder is not on your PATH, it tells you to add it. You can set these variables before sh:
| Variable | Default | Meaning |
|---|---|---|
CAGR_VERSION | latest | The release to install, such as 0.1.0 (without the v). |
CAGR_INSTALL_DIR | ~/.local/bin | Where the binary goes. |
CAGR_REPO | krtinshet/cagr | The GitHub repo to download from. |
CAGR_DOWNLOAD_URL | the release URL | A base URL that holds the binaries and SHA256SUMS, used instead of the release. |
On Windows the script stops with a note. Download cagr-windows-x64.exe from the release page instead.
With a package manager, as a dev dependency:
bun add -d @krtinshet/cagr
# or
npm i -D @krtinshet/cagr
The @krtinshet/cagr package is a small wrapper that puts the cagr command on your PATH. It pulls in the binary for your system as an optional package named cagr-<os>-<arch>, or cagr-linux-<arch>-musl on musl. If you installed with --no-optional (or your package manager skipped optional packages), the wrapper cannot find the binary and says so. Reinstall without --no-optional, or use the install script.
Alpine and other musl systems
The binary needs libstdc++ and libgcc. Install them first:
apk add libstdc++ libgcc
The install script detects musl and prints this command.
Make sure the hooks can find cagr
The generated hook files call cagr by name, so it must be on PATH when your agent runs. A dev dependency lives in node_modules/.bin, which is often not on PATH. In that case, set [agents] command to a path the hooks can run and run cagr sync:
[agents]
command = "{root}/node_modules/.bin/cagr"
{root} becomes the repo root for each agent. cagr doctor tells you when the program is not on PATH.
Updating
For a compiled binary install, run cagr update. It checks the release checksum before replacing the binary. Use cagr update --check to check without installing (exit 1 when an update is available, 0 when current), or cagr update --version 0.3.0 to install a specific version, including a downgrade.
For a package manager install, run bun add -d @krtinshet/cagr@latest or npm i -D @krtinshet/cagr@latest. After updating, run cagr sync in each repo and commit the changed files.
Set up a repo
cd your-repo
cagr init # detects your stacks, writes cagr.toml and the hook files
cagr doctor # lists guards and checks the wiring, tools and hook timings
What cagr init detects
- Stacks. It looks at the repo and at project folders up to three levels deep, skipping folders like
node_modules. Each project becomes a[[stack]]entry with itspaths. - JavaScript and TypeScript. The package manager comes from
packageManagerinpackage.json, or from the lockfile. Biome, ESLint and Prettier becomeon_editcommands. Thelintandtypecheckscripts (ortsc) becomeon_turn_endcommands. Averifyscript becomesgit.pre_push. - Go.
go.modadds file-scopedgofmt -wafter edits and module-scopedgo vet ./...andgo test ./...at turn end. A.golangci.yml,.golangci.yaml,.golangci.tomlor.golangci.jsonin the module addsgolangci-lint run ./...at turn end. Install golangci-lint yourself when configured. Package linting and its fixes stay out of edit hooks to keep them fast. Rungolangci-lint run --fix ./...from the module directory when fixes are needed. - Rust.
Cargo.tomlpackages addrustfmt --edition <edition> --config skip_children=true {files}after edits, thencargo clippy --all-targets -- -D warningsandcargo testwith--manifest-pathat turn end. Install the rustfmt and clippy toolchain components. Each workspace package gets its own stack and edition, includingedition.workspace = true; an omitted edition defaults to 2015.rustfmtaccepts individual files, whilecargo fmtselects crate targets.skip_childrenkeeps module traversal from formatting files outside the edit. Virtual workspace manifests have no stack of their own. - Python. The package manager is uv, Poetry, Pipenv or pip. Ruff becomes an
on_editcommand. mypy and pytest becomeon_turn_endcommands. - Agents. Claude Code if there is a
.claudefolder or aCLAUDE.md, orclaudeis onPATH. Codex if there is a.codexfolder or anAGENTS.md, orcodexis onPATH. If it finds neither, it sets up both. - Protected branch. From
origin/HEAD. Otherwisemain, ormasterif you are onmaster.
The config it writes starts with extends = ["recommended"]. See Configuration for what that turns on.
After Found:, init lists what the resolved config will deny, hold for review and require, then the commands it will run after edits, before the agent stops and before pushes. Empty command slots include instructions for adding checks. Existing .cagr/ fragments are included.
cagr init --dry-run prints the same enforcement summary and the config it would write, and writes nothing. If cagr.toml already exists, init stops. Use --force to overwrite it.
What cagr init writes
| File | What it is | Commit it? |
|---|---|---|
cagr.toml | The config: presets, the stack and single settings. | Yes |
.cagr/guards.toml, .cagr/risk.toml | Starter lists, commented out: protected files, blocked commands and risk tiers. Written only when missing. See Where config lives. | Yes |
.claude/settings.json | Claude Code hooks (only if Claude Code is enabled). | Yes |
.codex/hooks.json | Codex hooks (only if Codex is enabled). | Yes |
.githooks/commit-msg, .githooks/pre-push | Git hooks. | Yes |
.cagr/review.md | A starter review guide, written only when review is on and no guide exists. An existing REVIEW.md is used instead. | Yes |
.github/workflows/cagr.yml, .github/pull_request_template.md, .github/CODEOWNERS | Only when [ci] github = true. See CI. | Yes |
Then it runs your on_edit commands on the JSON files it wrote, so your formatter is happy with them, and sets git config core.hooksPath .githooks. If the folder is not a git repo, it writes the git hooks but does not set core.hooksPath. Run cagr install-git-hooks after git init.
Commit all of these files. Every teammate and every agent then gets the same guardrails. A teammate who does not have cagr installed can still commit and push: the git hooks print a warning and let the call through.
If you already have hooks of your own in .claude/settings.json or .codex/hooks.json, init and sync keep them and only manage cagr’s entries.
Trust the hooks in Codex
Codex asks you to trust project hooks once. Open Codex in the repo and approve them with /hooks. Until you do, Codex does not run them. cagr init prints this reminder when Codex is enabled.
Check the setup
cagr doctor prints one line per check, in these sections:
- Config: the config and the code rules load. An
Also config:line lists the.cagr/*.tomlfragments that joinedcagr.toml. - Guards: each built-in guard id and its configured policy, including guards turned off. An off guard is information, not a failed check.
- Wiring: the generated files match the config,
core.hooksPathpoints at the hooks folder, and the hooks are executable. - Tools:
cagris onPATH, and the program in everyon_edit,on_turn_endandpre_pushcommand can be found (including innode_modules/.binand.venv/bin). - Review (when review is on): a reviewer agent is installed, and the guide and gate are set.
- Timing: the measured time of each hook against its budget.
cagr doctor --github also checks the branch protection on GitHub. See CI.
Each failing line says what to fix.
Next steps
- Read the brief your agent gets at the start of each session: it lists the rules in plain words.
- Change
cagr.tomlor the files in.cagr/, then runcagr sync. See Configuration. - Add code rules in
.cagr/rules/. See Code rules. - Fill in
.cagr/review.md. See Review.
Go modules and Rust packages are found within the same three-directory scan as JS and Python packages. Nested modules and crates are excluded from their parent stacks so each file uses its own commands and edition. Rerun init when the package layout changes. Go and Rust do not set package_manager; JS tooling in a mixed repo still uses the existing JS guard. All Go and Rust checks stay strict in [findings] mode = "new".