Your agent reads the rules. This checks whether it followed them.
# Add to your Claude Code skills
git clone https://github.com/0xwilliamortiz/ratchetratchet is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by 0xwilliamortiz. Your agent reads the rules. This checks whether it followed them. It has 383 GitHub stars.
ratchet's catalog security scan is still queued. You can run an instant dependency and prompt-injection check now with the "Scan for vulnerabilities" button above.
Clone the repository with "git clone https://github.com/0xwilliamortiz/ratchet" and add it to your Claude Code skills directory (see the Installation section above).
ratchet is primarily written in JavaScript. It is open-source under 0xwilliamortiz on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh ratchet against similar tools.
No comments yet. Be the first to share your thoughts!
Unlocks once the catalog security scan passes (runs nightly).
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
Your agent reads the rules. This checks whether it followed them.
A ratchet turns one way. Complexity in your codebase goes down or stays flat, unless someone deliberately turns it the other way and writes down why.
Every minimalism ruleset for coding agents is an open loop. You inject prefer the standard library, do not add dependencies, keep the diff small, the model reads it, and then nothing checks whether any of it happened.
Compliance is assumed. When the model drifts, which it does over a long session, nobody finds out until review.
The ruleset still goes in. But a PostToolUse hook reads every edit the agent
makes, measures it, and reports what it found back into the same session, while
the agent is still working.
you add a date picker to the settings page
agent [installs flatpickr, writes a wrapper component, adds a stylesheet]
hook ratchet guard · +71 -0 lines (net +71/150) · 2/3 new files · 1/1 new deps
certain
package.json:14 dep new dependency flatpickr
justify it against the stdlib and the platform, or drop it
src/DatePicker.jsx:4 native date picker component library
<input type="date">
likely
src/DatePicker.jsx:22 wrapper DatePicker only forwards to Flatpickr
call it directly and delete the wrapper
agent [reverts, ships <input type="date">]
Nothing here depends on the model having felt persuaded by a system prompt.
git clone https://github.com/0xwilliamortiz/ratchet.git
cd ratchet
npm install -g .
Then once per repository you want watched:
cd your-project
ratchet
That is the whole setup. It registers the hooks, starts measuring, accepts everything already in the code as a baseline, and opens the window:
ratchet is watching your-project
hooks C:\your-project\.claude\settings.json
measuring .ratchet\ (mark at 1284 lines)
baseline 31 findings from before now accepted, only new ones fire
window opened ratchetui.exe
plugin C:\Users\you\ratchet
restart your agent so it picks the hooks up.
Restart your agent. Done.
Requirements. Node 20 or newer. Git on PATH for the measuring half; without
it the ruleset and the detectors still work, but the mark, the ledger and the
audit do not. On Windows, hooks/ratchetui.exe ships with the repository and
opens automatically.
ratchet init and ratchet baseline still exist for doing those steps alone.
Not moods. Numbers a hook can compare against.
| Mode | New files | New deps | Net added lines | On overrun |
|---|---|---|---|---|
advise |
8 | 3 | 400 | findings only |
guard |
3 | 1 | 150 | findings and budget warnings |
strict |
1 | 0 | 60 | the edit is blocked |
off |
nothing runs |
Default is guard. Switch mid-session with /ratchet strict, persist with
/ratchet default advise, or set RATCHET_MODE.
Every Edit, MultiEdit and Write goes through the detectors.
| Tag | Detects |
|---|---|
dep |
A name that appeared in package.json, requirements.txt, pyproject.toml, go.mod, Cargo.toml, Gemfile or composer.json and was not there before |
exists |
A new symbol whose normalised name already lives elsewhere in the repository, so formatDuration finds format_duration |
stdlib |
Hand-rolled versions of things the standard library ships |
native |
A dependency, or code, doing what the platform already does |
wrapper |
A function whose body only forwards to another function with the same arguments |
yagni |
An interface, abstract class or protocol with a single implementation |
validation |
A bespoke email regex, which is always wrong |
budget |
Running totals for new files, new dependencies and net added lines |
This is the difference between a tool people keep and one they mute.
| Grade | Means | Example |
|---|---|---|
certain |
Parsed, not guessed | A dependency read out of a manifest |
likely |
Structural | A function that forwards and nothing else |
heuristic |
Shape matching on a regex | A manual group-by loop |
Output is grouped by grade. strict only blocks on certain.
Added lines are computed against the previous content rather than the whole
file, so the detectors only ever see what this session actually wrote. For an
Edit that is the replacement string; for a Write, the file against its
committed version. Manifests are always read whole, because a partial edit is
not parseable JSON or TOML.
Deletions count too, and the budget uses net lines. Removing fifty lines buys you fifty. A session that ends smaller than it started is the tool working.
The symbol index is cached in the temp directory, keyed on file count plus newest mtime. Cold it costs a few hundred milliseconds, warm about 4ms, and it is capped at 3000 files.
Two ways, and they mean different things.
One line, for a reason you accept:
// ratchet-ignore: profiled, the clone is the hot path
const copy = JSON.parse(JSON.stringify(frame));
A repository with history:
ratchet baseline
Findings are fingerprinted by tag, path and shape rather than line number, so a baseline survives reformatting and code moving down the file.
This is the whole point of the name. Existing debt is grandfathered. New debt is not.
.ratchet/mark.json records an accepted size for the codebase: source lines,
source files, a date, and a written reason. Session start compares the repo to
the mark and says when it is above. Session end appends a row to
.ratchet/ledger.jsonl.
Raising the mark is deliberate and needs a sentence:
node scripts/accept-mark.js "vendored the parser until upstream ships the fix"
Read it all back with ratchet report:
complexity trend, last 3 sessions
─────────────────────────────────
▁█▆ 18 to 22 lines
when mode added removed deps flagged repo
2026-07-31 guard +3 -0 0 0 18 new
2026-07-31 guard +5 -0 1 1 24 ▲ 6
2026-07-31 guard +0 -2 0 0 22 ▼ 2
mark 14 lines, 8 lines above the mark
reason: initial mark
Prompt-only tools refuse to report per-repo savings, and they are right to: the version that was never written has no baseline to subtract from. But the repository before the session is a real baseline, and the ledger is a real record. No estimates, no invented percentages, just the trend.
# ratchet: single global lock, split per account if write throughput matters
Ceiling, then trigger. /ratchet-ledger collects them and flags any that name
no trigger, since those are the ones that quietly become permanent.
| Command | Does |
|---|---|
ratchet |
Set up this repository: hooks, measuring, baseline, window |
ratchet --global |
Hooks for every project on this machine |
ratchet audit |
Scan the whole repository and list what to cut |
ratchet baseline |
Accept what exists today, flag only what is new |
ratchet report |
The measured trend from the ledger |
ratchet status |
What is installed and what has been measured |
ratchet doctor |
Drive every hook with real payloads and prove it works |
ratchet log |
What the hooks actually said during your real sessions |
ratchet ui |
Reopen the window, --debug to see why it will not open |
ratchet uninstall |
Remove the hooks and the session state |
--dry-run prints what would change and writes nothing.
| Command | Does |
|---|---|
/ratchet [advise|guard|strict|off] |
Set the budget, or report the current one |
/ratchet-review |
Over-engineering review of the current diff |
/ratchet-audit |
The same across the whole repository |
/ratchet-ledger |
Trend, mark, and every deferred shortcut |
/ratchet-accept |
Record a new mark with a reason |
/ratchet-help |
Reference card |
stop ratchet turns it off for the session. It has to be the whole message, so
asking the agent to "add a stop ratchet button" does not disable it mid-task.
.ratchet/config.json, per project and meant to be committed:
{
"mode": "guard",
"budget": { "newFiles": 5, "newDeps": 1, "addedLines": 200 },
"ignore": ["migrations", "generated"],
"scanTests": false
}
Per-user defaults live in ~/.config/ratchet/config.json.
Environment: RATCHET_MODE, RATCHET_LOG, RATCHET_UI, and
RATCHET_SUBAGENT_MATCHER, a case-insensitive regex tested against a
subagent's type so read-only search agents can be left out of the ruleset.
Validation at trust boundaries. Error handling on paths where failure loses data. Security controls. Accessibility basics. Hardware calibration. Anything the user explicitly asked for. One runnable check per piece of non-trivial logic.
These are in the ruleset and out of scope for every detector. A review that suggests deleting a smoke test has failed.
npm test proves the code is correct. These two prove the loop is wired up on
your machine.
ratchet doctor builds a scratch repository, plants a file with three known
problems, drives the session, guard and report hooks as real