Install and try Standpat
Version 0.15.0 is a preview. The install takes 5 minutes and the walk 20.
Get it: the download
In your own terminal. This unpacks to a folder standpat-0.15.0/ and moves it to ~/standpat, the path every command on this page assumes:
cd ~ curl -LO https://www.standpat.ai/standpat-0.15.0.zip unzip standpat-0.15.0.zip mv standpat-0.15.0 ~/standpat cd ~/standpat
If ~/standpat already exists, move it aside first, or leave the folder where it unpacked and change ~/standpat in each path below. Then go on at Install, skipping its git clone line.
Or, for invited testers: the repository
If you were invited to the private repository, clone it with the first line under Install instead. You need the invitation first.
What Standpat is
Standpat is a brake for coding agents, run as a hook by the agent app. Every chat starts free, and typing brake in a chat turns the brake on there. From then on it holds any change to the lines you named until you type a word to let it through, and it keeps a log of the decisions you typed.
Before you install
Install changes three things, and only these:
~/.claude/settings.json, the agent app's settings file: one block of entries is added after yours (eight hook entries and a status line).- A hook file and a copy of the command line, in
/Library/Application Support/Standpat/<version>/. - A folder
~/.standpat, with Standpat's state and your user log.
Before it writes, install proves that taking its own lines back out of the new settings file gives your file back byte for byte, so nothing of yours is removed or changed; the one byte it adds to your part is a comma after the item its block follows. uninstall takes the entries out again under the same proof. --system asks for your password through sudo once, to copy the hook into a folder owned by root, so the agent, which runs as you, cannot change the gate it has to pass.
You need git and Python 3.9 or later. On a Mac the hook runs on the Command Line Tools' Python (xcode-select --install adds it if it is missing); on Linux, on /usr/bin/python3. If your settings file already has a status line of your own, install keeps yours and adds none; then run python3 ~/standpat/standpat/standpat.py status ~/standpat-play in a terminal wherever this page mentions the status line.
Install
In your own terminal, not inside an agent session (from inside one, install refuses and changes nothing). The first line takes the address of the repository you were invited to; skip it if you used the download and are already in ~/standpat.
git clone https://github.com/CHRSRCH/standpat.git ~/standpat cd ~/standpat [ -f ~/.claude/settings.json ] && cp ~/.claude/settings.json ~/settings.before-standpat.json python3 standpat/standpat.py install --system
The cp keeps a copy of your settings file for the check below; with no ~/.claude/settings.json it does nothing, and install creates one. Every command on this page assumes Standpat is at ~/standpat; if you put it elsewhere, change that part of each path.
It worked when install prints a line starting installed: 8 entries in, followed by the hook's path (the folder name after Standpat/ is the version) and the interpreter it chose. diff ~/settings.before-standpat.json ~/.claude/settings.json then shows one block of added lines. The status line under the agent's prompt shows only in a repository with a handbrake file (step 1 below); elsewhere it stays blank.
If install refuses with one line about the file's layout, the repository's README explains it under "Install": --reformat clears it. If it says no interpreter qualifies, it has changed nothing.
A 20-minute walk
The card's words (brake, goal, stamp, park, free and the rest) go to Standpat and never to the agent; redirect is the one whose words reach it. Anything else you type is a message for the agent. The replies below are copied from a rehearsal; your times, version and session ids will differ. The status line starts Standpat ·.
1. A throwaway repository 2 minutes
In your terminal:
mkdir ~/standpat-play && cd ~/standpat-play && git init -q
printf 'print("hello")\n' > app.py && printf '# Notes\n' > notes.md
git add . && git commit -qm start
python3 ~/standpat/standpat/standpat.py init ~/standpat-play
init writes .standpat/handbrake listing nothing yet, and prints Chats in this repository start free and hold nothing until you type `brake` in one. among its lines.
2. A chat starts free 2 minutes
cd ~/standpat-play, then start your agent there. The status line reads Standpat · handbrake off · handbrake lists nothing: write a line in it, then type brake. Ask the agent What does app.py do?: it answers, and Standpat says nothing. Edits pass too.
3. A handbrake naming one file 2 minutes
In your terminal:
cat > ~/standpat-play/.standpat/handbrake <<'EOF'
{
"standpat_format": 1,
"project": "Play",
"must_not_change": [
{"id": "app", "label": "The app", "matcher": "path", "pattern": "app.py", "class": "other"}
],
"done": "",
"who_may_widen": ["owner"]
}
EOF
python3 ~/standpat/standpat/standpat.py check ~/standpat-play
check prints Standpat: the handbrake at ~/standpat-play/.standpat/handbrake is valid, version <version>. and the words to type. Note the version (eight characters).
4. brake, then goal demo, then handbrake accept <version> 2 minutes
In the chat. The replies:
Standpat: the handbrake is on in this chat from the agent's next action; type `free` to turn it off. Type `goal <name>` to say what this chat is for, then `handbrake accept <version>` to accept what must not change, then your message for the agent. Nothing was sent to the agent. Standpat: goal demo is open. Type `handbrake accept <version>` to accept the handbrake, then your message for the agent. Nothing was sent to the agent. Standpat: handbrake version <version> accepted. Type your message for the agent. Nothing was sent to the agent.
The accept also prints the card of every word (the same as python3 ~/standpat/standpat/standpat.py card). The status line: Standpat · demo · moves 0 of 3 today · 0 of 240 min this sitting · nothing held · load 4 ■□□□□.
5. The hold 2 minutes
Ask the agent Change app.py so it prints hello, world. Its edit is not run, and the agent's output shows:
Standpat: hold 1, held for a stamp: Edit on app.py.
Lines it crosses: The app.
- print("hello")
+ print("hello, world")
Cost: 1 handbrake move. Handbrake moves today so far: 0 of 3.
Type `stamp` to allow exactly this, once, or `park` to stop.
The status line says type stamp.
6. stamp 1 minute
The reply:
Standpat: stamped Edit on app.py, held at <time>, crossing The app. The action is allowed for 30 minutes. Send the agent any message and it will run it.
Send Go ahead. and the agent makes the edit; the status line says moves 1 of 3 today and nothing held.
7. Slices and the review stop 4 minutes
Type slice add docs notes.md says what app.py prints, then slice add usage notes.md has a usage line. The replies:
Standpat: slice docs is 1 of 1 and is open now. Type `slices` to see the list. Nothing was sent to the agent. Standpat: slice usage is 2 of 2 and waits its turn. Type `slices` to see the list. Nothing was sent to the agent.
Ask Do the docs slice. Standpat tells the agent to end its reply with the line Slice done: docs when it believes the slice is done. When it does, the status line reads slice 1 of 2: docs waits for review · review waits: type accept or redirect, and any further change the agent tries is not run. Type slices to see the review card:
Standpat review: slice 1 of 2, docs. Why: the agent says the slice is done Done when: notes.md says what app.py prints Changed: notes.md Shell commands run: 0 Stamped: nothing Refused: nothing Type `accept` to close the slice, or `redirect <what instead>` to send the agent another way.
accept replies Standpat: slice docs is done, and slice usage is open now. Type your message for the agent. Nothing was sent to the agent. Then ask Do the usage slice., and when it says Slice done: usage, type redirect put the usage line at the top instead. Those words go to the agent, with Standpat's note that the slice stays open until its done line holds; the status line goes back to slice 2 of 2: usage.
8. reserve 25 1 minute
The reply, with no usage reading:
Standpat: your reserve is 25% of the five-hour window, kept until you change it. Standpat has no usage reading, so it cannot say what jobs have left. Type `reserve <n>` to change it. Nothing was sent to the agent.
The status line ends yours 25% · usage unknown. The usage reading comes only on a Pro or Max account, and only after the chat's first reply; with it, the status line ends like used 62% · yours 25% · jobs 13% left and the reply says Used now 62%, so jobs have 13% left until 09:29. with your own figures.
9. park, and the return card 2 minutes
The reply: Standpat: parked; anything held for a stamp was not run, and this sitting used 1 handbrake move. When you are back, send your next message as usual and the card comes with it. Nothing was sent to the agent. The status line says parked since <time>. Send I'm back.: the message goes through, and the agent is told to show you the card first. It runs from Goal: demo through Agent may not: The app and the budget to Done while parked: nothing and Refused while parked: nothing.
10. The garage 1 minute
In your terminal:
cd ~/standpat-play && python3 ~/standpat/standpat/standpat.py garage
One page: Chats, Goal, Slices (1. docs: done at <time>, 2. usage: running), Decisions this sitting (each word you typed with its weight, the load, and The agent's claims: 2), Tokens and Jobs.
11. free 1 minute
The reply:
Standpat: the handbrake is off in this chat, so Standpat holds nothing in it. Type `brake` to turn it on again. Nothing was sent to the agent.
The status line reads handbrake off: type brake to turn it on, and edits to app.py pass again.
Uninstall
In your own terminal:
python3 ~/standpat/standpat/standpat.py uninstall
It prints uninstalled: 8 entries removed from your settings file, then settings: Standpat's status line removed. and settings: restored byte for byte.; cmp ~/settings.before-standpat.json ~/.claude/settings.json then prints nothing if you changed nothing else in the file. What stays: ~/.standpat with your user log, each repository's .standpat/ with its handbrake and log, and the hook copy in /Library/Application Support/Standpat/, which sudo rm -r "/Library/Application Support/Standpat" removes.
Feedback
Testers give feedback only through the issue form "Tester feedback" on the repository they were invited to (Issues, New issue); not by email. What helps most: the steps you took, in order; what you expected; and Standpat's exact words, copied from the reply or the status line. For the version, give the folder name from the install line and, from a clone, the output of git -C ~/standpat log -1 --format=%h.
Known limits
- Linux is untried. No one has run Standpat on Linux yet, and
--systemputs the hook in/Library/Application Support/Standpat/there too, as on a Mac. - What the always-on guard cannot see. In a free chat, deleting by name a folder that holds Standpat's state (
rm -rf ~/standpat-play) is not refused. A second agent session started by a script in another language, or by a command built at run time, is not caught. - Merge reviews. With
"review": {"merge": true}in a handbrake,env git merge,command git merge,/usr/bin/git mergeandsh -c 'git merge ...'are not recognised as merges and run with no review stop. The handbrake's lines still hold. - The garage during a park prints the park twice on its Goal line (
demo, parked since 07:29; parked since 07:29).