Installing
The binary first, then the wiring. They are separate acts on purpose: a
program that can refuse the commands your agent runs should not install itself
into your settings.json as a side effect of you fetching it.
The binary
curl -fsSL https://raw.githubusercontent.com/fredericrous/amont-agent/main/install/install.sh | sh
irm https://raw.githubusercontent.com/fredericrous/amont-agent/main/install/install.ps1 | iex
Either one downloads a release binary, verifies it against the published
SHA256SUMS, and puts it in ~/.local/bin. Neither wires anything in.
Or: brew install fredericrous/tap/amont-agent, cargo install amont-agent,
or a binary straight from
Releases — Linux
x86_64 (gnu and static musl) and aarch64 (gnu), macOS (Intel and Apple
silicon), and Windows x86_64.
Why there is no npm package
There was one, briefly, and removing it is the more useful answer than listing install methods.
install bakes the ABSOLUTE path of the running binary into
settings.json, deliberately: a PATH-resolved command exits 127 into
Claude Code's non-blocking bucket the moment PATH differs, which disables
the guard with nothing to notice it. npm cannot supply a stable absolute
path. Under npx the binary lives in npm's _npx cache, which npm garbage-
collects; under a project-local npm i -D it lives in one project's
node_modules, while the guard it configures is machine-global — so
rm -rf node_modules in one repository would silently disable the guard for
every session on the machine.
amont has an npm package for a reason that does not transfer: it is a
per-repository tool, and npm i -D amont plus a prepare script means the
hooks travel with the repository. This is per-developer machine
configuration — ~/.claude/settings.json, global git config, a journal in
~/.claude/. Nothing about it belongs to a project.
Every channel above hands install a path that stays put.
The wiring
amont-agent install # prints the settings block, writes nothing
amont-agent install --write # merges it into ~/.claude/settings.json
install refuses to guess. It will not patch a settings.json it could not
parse, and it will not write one whose formatting it cannot reproduce — a diff
full of reformatting hides the one line it added — so it prints the block and
changes nothing unless --reformat says otherwise. uninstall removes exactly
what it wrote and leaves everything else byte-identical.
amont-agent install --write --project # .claude/settings.json instead
amont-agent install --write --local # .claude/settings.local.json
Four kinds of entry are written: the guard on PreToolUse, for Bash and for
the file tools; the assertions on PostToolUse for Bash, which check what a
command that reported success actually did; a PostToolUse entry for Read,
which remembers a file only once it has actually been read; and a
SessionStart entry that leaves a heartbeat and states where the checkout
stands against the remote. Without the heartbeat, doctor cannot tell "nothing
fired this week" from "the guard has been dead since Tuesday".
Your settings.json keeps the mode it already had, and a file created here
starts at 0600: it can hold MCP environment blocks, and those hold
credentials.
Knowing it is alive
Claude Code hooks fail open quietly: a command that cannot be resolved exits
127, which is a non-blocking status, and nothing tells you. doctor exits
non-zero when the guard is inert, so it can run from cron:
✓ installed in /Users/you/.claude/settings.json
✓ amont-agent 2.0.0 at /Users/you/.local/bin/amont-agent
✓ a refused command produces a valid decision document
✓ last ran 4m ago
✓ acting on pipe-to-tail
Turning it off
AMONT_AGENT_OFF=1 # this shell only
git config --global amont.agent.enabled false # everywhere
amont-agent uninstall --write # remove the settings entries
Demoting one rule is almost always the better move than switching the guard off — see stances. A guard that is hard to back out of is one people uninstall instead of demoting, and uninstalling takes every rule with it.