You asked it to fix a 401.
It fixed the 401. It also refactored the auth module, renamed four variables, extracted a retry helper into a new file, added a dependency, reformatted two files it happened to open, and updated the README.
7 files. +203 −47.
You don't review that. Nobody reviews that. You skim it, you say "looks fine," and you merge 197 lines you have never read into a codebase you are responsible for.
That's the actual problem. Not that the model is wrong — the model is often right. The problem is that being right in 200 unrequested lines is indistinguishable from being wrong in 200 unrequested lines, because neither one gets read.
hands-off makes the agent commit to a scope before it touches anything,
then holds it there.
── SCOPE ─────────────────────────────────
Task fix the 401 after token refresh on login
Files src/auth/session.ts
Budget ~6 lines
Won't refactor the retry logic, touch useAuth
──────────────────────────────────────────
It works. Then it proves it kept its word:
── DIFF ──────────────────────────────────
Declared 1 file · ~6 lines
Actual 1 file · 6 lines ✓
──────────────────────────────────────────
── DEFERRED ──────────────────────────────
· token.ts retries without a timeout — will hang on a dead endpoint
· useAuth re-fetches on every render
──────────────────────────────────────────
Everything it noticed and didn't do comes back as a list. Four seconds to read, one word to approve. You keep the model's judgment — you just stop it from acting on that judgment unilaterally.
| Default | With hands-off |
|
|---|---|---|
| Files touched | 7 | 1 |
| Lines changed | 250 | 6 |
| Requested by you | 1 file | 1 file |
| Time to review | you didn't | 10 seconds |
git bisect still works |
no | yes |
Same bug fixed, both times.
Any client — Claude Code, Codex, Cursor, VS Code, Gemini CLI, Grok, opencode, Amp, and 26 more:
npx skills add Moresyl/hands-off # install
npx skills update # update
npx skills remove hands-off # uninstallClaude Code or Grok — as a plugin, which brings the enforcement hook with it:
/plugin marketplace add Moresyl/hands-off
/plugin install hands-off@hands-off
/plugin marketplace update hands-off # update: pull the listing…
/plugin update hands-off@hands-off # …then install it
Manual — copy SKILL.md into your skills directory. It's one
Markdown file with no dependencies.
Per-client paths, hook wiring, and update commands for each:
docs/clients.md.
Then just work normally. The skill activates when you ask for a change to an existing codebase.
Most behavior skills are a prompt and a hope. Prompts drift — over a long session, "stay in scope" quietly loses to "but this is clearly better."
So hands-off ships two layers:
| Layer | How it works | Strength |
|---|---|---|
SKILL.md |
The agent declares its scope and self-enforces | Soft |
hooks/scope_guard.py |
Out-of-scope writes are rejected by the tool layer | Hard |
The hook is a PreToolUse guard. When a scope file exists, any write to a path
outside it is denied before it happens — not by the model's restraint, but by the
runtime. The agent gets told why, and has to either stay in bounds or come back
and ask you.
Honest limitation: blocking a write before it lands needs a pre-write hook, and only Claude Code and Grok have one. Cursor has hooks, but its file-edit event fires after the write — it can report an out-of-scope edit, not prevent it. Codex, VS Code, and Gemini CLI have no pre-write hook at all. On those four you get the soft layer only: the full contract, enforced by the model rather than the runtime. That's still most of the value, but it is a promise, not a wall.
| Skill | Hook | |
|---|---|---|
| Claude Code, Grok | ✅ | ✅ |
| Codex, Cursor, VS Code, Gemini CLI, +27 | ✅ | — |
Setup and update commands for each: docs/clients.md.
Stating this plainly so nobody installs it expecting the wrong thing:
- It doesn't make the agent smarter. Same model, same reasoning. Narrower blast radius.
- It doesn't improve code quality. A bad 6-line fix is still a bad fix — you can just see that it's bad now.
- It doesn't block refactors. Ask for one and you get one. The rule is about unrequested change, not big change.
- It isn't a linter. It never touches your code. It constrains the agent's behavior, not your source.
- It doesn't save tokens. Different problem, different skill.
And it will get in your way on greenfield projects, migrations, and generated files. Turn it off there. It's a Markdown file — there's nothing to uninstall.
The better coding agents get, the worse this gets.
A weak model does what you asked because that's all it can manage. A strong model sees your whole codebase, forms opinions about it, and acts on them — helpfully, competently, and without asking. Every one of those opinions might be right. That isn't the point. The point is that you are responsible for code you never agreed to and never read.
The fix isn't a better model. It's a boundary.
Adapted this for another client? Found a case where the agent slips out of scope
anyway? Open an issue with the transcript — the forbidden list in
SKILL.md grows from real escapes, not from theory.
MIT. See LICENSE.
