A safety gradient for pi, from auto-approve-everything to ask-about-anything — with an undo trail so YOLO never means unrecoverable.
Part of the Pify suite. Install with pify install yolo or pi install npm:@pify/yolo.
Approving every command is exhausting and you stop reading them; approving none of them means an agent that cannot work. Both ends are wrong, and which end you want changes several times a day — scaffolding a prototype is not the same as touching production config.
So this is a gradient rather than a switch, with two properties that hold at every setting: catastrophic commands are refused outright, and anything touching credentials asks. Those two make the rest safe to slide.
The second half is the undo trail. A guard that only says no is a guard you turn off; a guard that lets you take the risk and take it back is one you can live with.
/yolo <mode> moves along a gradient. Two things hold in every mode, which is what makes the gradient safe to move along: catastrophic commands block, and secret material asks.
| Mode | Catastrophic | Built-in destructive | Your .pi/yolo.json ask-rules |
Everything else |
|---|---|---|---|---|
⚡ yolo |
blocked | runs | runs | runs |
⚙ auto |
blocked | runs | asks | runs |
🛡 approve (default) |
blocked | asks | asks | runs |
🔒 strict |
blocked | asks | asks | asks unless plainly read-only |
Bare /yolo still flips between yolo and approve — the two ends people actually toggle between. Sessions saved before the gradient existed carried guard; that is what approve is now called, and they reopen there.
A behaviour change worth knowing about: yolo used to stand the whole gate down, catastrophic patterns included, which contradicted the rules' own claim that the floor is never overridable. The floor now holds in yolo mode too. If you were relying on rm -rf / auto-approving, you were relying on a bug.
| Tier | Examples | Behavior |
|---|---|---|
| BLOCK | rm -rf /, rm -rf ~, rm -rf .git, mkfs, dd of=/dev/…, fork bomb, > /dev/sda |
Refused outright, in every mode. Never overridable — not by user rules, not by a mode. |
| ASK | rm -rf <path>, git push --force, git reset --hard, git clean -f, curl | sh, find -delete, chmod 777, history rewrites |
Confirmation dialog with the command shown. Denials can carry your reason back to the agent. |
| ALLOW | everything else | Runs untouched (unless you are in strict). |
Fail-closed everywhere: rule-evaluation errors block; ASK without a UI (headless/CI) denies.
Credentials are the one thing no mode waves through — auto-approving speed is worth it, auto-approving your AWS keys into a prompt is not. Any read/edit/write on secret material, and any bash command that names it, asks first in every mode:
.env (and .env.*, but not .env.example/.sample/.template) · ~/.ssh/* and id_rsa/id_ed25519-style keys (.pub halves are fine) · .aws/credentials · .pi/agent/auth.json, .claude/.credentials.json · .npmrc, .pypirc, .netrc, .git-credentials · ~/.config/gh/hosts.yml · *.pem, *.key, *.p12, *.pfx · secrets.json/credentials.yaml
A user rule opts a project out: { "pattern": "*/.env", "action": "allow" }.
.pi/yolo.json is only read once you have approved it. A repository ships that file, and a user rule can relax the destructive tier — so a repo you just cloned could otherwise turn the guard down on its own say-so, silently, on the first command it runs.
pi's own project trust is necessary but not sufficient here. pi asks about trust only when the repository ships one of the resources pi itself loads — .pi/settings.json, .pi/extensions, .pi/skills, .pi/prompts, .pi/themes, SYSTEM.md, APPEND_SYSTEM.md. A repo carrying only .pi/yolo.json triggers no prompt, and isProjectTrusted() then returns true by default — measured, not assumed. So the question is this extension's to ask: once per project, remembered afterwards, refused outright in a headless run with no answer on record, and never able to override a project pi itself refused. /yolo status says the file was found and refused rather than pretending it does not exist. Global rules are unaffected.
For CI, set PIFY_TRUST_PROJECT=1 — an environment variable, because the repository being read cannot set one for itself.
The trail is kept 30 days. Before this, nothing was ever deleted: every edit copied a whole file into the trail, and every checkpoint pinned a whole-tree stash commit under refs/pify/yolo/* — and git cannot reclaim an object a ref still points at, so the object store grew for the life of the machine. Pruning runs once per session and deletes the refs it releases.
Before every risky bash command in a git repo, the trail records a git stash create checkpoint — a dangling commit holding the working tree exactly as it was, kept alive under refs/pify/yolo/. It writes nothing to your tree, index, or stash list. /yolo trail prints the recovery line next to the command:
#7 2026-09-06 11:00:12 bash git reset --hard @a1b2c3d4
↩ git stash apply 9f8e7d6c5b4a
That covers what /yolo undo can't: damage done by a command rather than by an edit/write.
Always on, in every mode:
- Every
edit/writesaves the file's pre-image first (per-project trail under the agent dir — survives restarts). - Risky bash commands are logged with cwd, timestamp, and git HEAD.
/yolo trailshows history;/yolo undo [n]restores the newest n file changes (with a confirmation listing exactly what will be touched). Files that didn't exist before are deleted; bash effects are logged but not undoable.
/yolo classifier on adds a third tier behind the regexes. Regexes only know the destructive shapes someone thought to write down — find . -name '*.ts' -exec sed -i … {} + is not one of them. When no rule matches, a model reads the command and can raise it to a confirmation.
Two rules keep it honest:
- Escalation only. It can turn
allowintoask. It can never turn anaskor ablockinto anallow, so a classifier that gets talked into approving something cannot open the gate. - A broken classifier changes nothing. Timeout (20s), unreadable answer, no model available → the deterministic verdict stands. Safety comes from the rules; this is a second pair of eyes, not the gate.
Obviously-safe commands (git status, ls, cat, bun test, …) skip the call entirely, so the cost lands only on unfamiliar ones.
Measured over OpenRouter on six commands (three genuinely destructive, three read-only):
| Model | Correct | Unreadable → no opinion |
|---|---|---|
| GPT-5.6 luna | 6/6 | 0 |
| GPT-5.5 | 6/6 | 0 |
| Claude Opus 4.8 | 6/6 | 0 |
| GPT-5.6 terra / sol | 5/6 | 1 |
| Claude Opus 5 | 4/6 | 1 |
| Gemini 3.1 Pro | 2/6 | 4 |
| Qwen3 235B | 2/6 | 4 |
Every miss fell back to allow — no run ever downgraded a command the rules had already flagged. Weaker models simply give you less extra protection.
.pi/yolo.json — wildcard patterns, last-match-wins, may retune ASK/ALLOW but never the BLOCK floor:
{
"rules": [
{ "pattern": "git push origin dev*", "action": "allow" },
{ "pattern": "npm run deploy*", "action": "ask" }
]
}/yolo # flip between yolo and approve
/yolo strict # or: yolo | auto | approve | strict
/yolo status # mode, the other modes, rule count, trail size
/yolo trail # recent trail entries
/yolo undo 3 # restore the newest 3 file pre-images
/yolo classifier on # let a model flag unfamiliar commands
MIT © Pify maintainers