cursordoctrine
v1.2.1
Published
Cursor hooks: doctrine at session start, intent-precompile (.scope.json), scope refresh/drain, permission gate, final review at stop.
Maintainers
Readme
What this is
Six Cursor hooks across six events. No state machine the agent has to maintain; the only on-disk state is a one-shot brake and a scope stash under ~/.cursor/.hooks-pending/. The single repo file is .scope.json, written for you so you don't have to.
- Seed the contract at
beforeSubmitPrompt—intent-precompilewrites your prompt into.scope.json'spromptthe moment you hit send. On continuation it preserves agent-ownedintent,files[], andacceptance. Prefix/newornew task:resets for a fresh task. - Inject the doctrine at
sessionStart— every chat starts with the same short governing text (doctrine.md): smallest correct diff, YAGNI ultra, ask-don't-guess, conventional commits, the auditor mindset. ~80 lines, read once. - Track the blast radius at
afterFileEdit(^Write$) +postToolUse—scope-refreshrecords each edited file intofiles[]and stashes a one-line reminder;scope-draindelivers it asadditional_contexton the next tool boundary (Cursor doesn't consumeafterFileEditstdout, so the stash-and-drain pair carries it). Keeps the contract visible as a turn fills with code. - Gate blast radius at
beforeShellExecution— one permission gate denies a short explicit list of dangerous commands (rm -rf /,curl | sh, force-push,npm publish, ...). The script fails open on internal errors;failClosed: falseso a pwsh cold-start abort does not block all shell use. Everything else passes. - One final review at
stop— when an implementation finishes andgitsees changed files, Cursor auto-submits oneFINAL REVIEWfollow-up. Six axes: intent trace (tie every diff hunk to the original request — anything untraceable is a hallucinated requirement), correctness, reliability, coverage, anti-slop, wiring completeness. The hook readsgit diff --name-only HEAD+ untracked files; the only state is a per-conversation one-shot brake.
The model is the auditor. A self-review done by the model in its own context is free — it has the file, the diff, the user's intent, and the ability to fix. The harness's only non-advisory lever is the permission gate's hard deny.
Prerequisites
PowerShell 7 (
pwsh) is required on Windows. The hooks run viapwsh.exe -NoProfile -File .... Windows PowerShell 5.1 (powershell.exe) is not supported — install PowerShell 7 separately.
| Platform | Required | Optional (recommended) |
|---|---|---|
| Windows | git, PowerShell 7 (pwsh) on PATH | Python 3.9+ (powers the sweep scanner) |
| Linux / SSH remotes | bash, git, and jq or python3 | Python 3.9+ (sweep scanner) |
Install PowerShell 7:
- Windows:
winget install --id Microsoft.PowerShell --source winget(or grab the MSI from the PowerShell GitHub releases). Confirm withpwsh -Version. - Linux: follow the official package instructions — only needed if you run the
windows/pack on a Linux box (unusual); normal Linux installs use thelinux/bash pack.
Install
Node 18+:
npx cursordoctrine@latest install # copies the hook pack into ~/.agents/hooks + ~/.cursor, merges hooks.json
npx cursordoctrine verify # smoke-tests every hook with fake payloads, no restart needed
npx cursordoctrine sweep # whole-codebase anti-slop audit + fix-handoff (on demand)Restart Cursor after install — hooks.json is read at startup. install is idempotent; re-run to update. Entries you added to ~/.cursor/hooks.json yourself are kept. npx cursordoctrine uninstall removes the pack the same way. Re-running install after upgrading to 1.0 also reaps the old hooks (intent-precompile, intent-anchor, self-review-trigger, etc.) from your existing hooks.json automatically.
No Node? Open INSTALL.md, paste it into a Cursor agent chat on the target machine, and let the agent copy files and run the checklist.
The anti-slop skill (skills/anti-slop/ — SKILL.md and the duplication scanner) installs to ~/.cursor/skills/anti-slop/. The hook checklist (~/.agents/hooks/anti-slop.md, 40 items) is the canonical slop detector the final-review follow-up points the model at. The session final-review tells the model to apply it to the session diff; cursordoctrine sweep is the separate on-demand pass for accumulated slop across the whole repo.
Session review vs. sweep — two jobs, two scopes
- Session final-review (
stop): scans only the filesgitsees as changed this session. Fixes are limited to lines the agent added. Pre-existing slop is out of scope (axis 0 intent-trace; touching it is scope creep, and 100+ files won't fit in one bounded pass). npx cursordoctrine sweep: whole-repo cleanup. Runs the scanner in--allmode across every tracked file, prints a category-by-category breakdown, and hands off to the agent under a cleanup doctrine that authorizes fixing pre-existing slop, category by category, with a re-scan after each. Use it when you want a codebase cleanup, not on every session.
The flows
| Flow | Event | What happens |
|---|---|---|
| Prompt | beforeSubmitPrompt | intent-precompile writes prompt (verbatim). Preserves intent + files[] + acceptance on continuation. /new or new task: resets for a new task. Skips hook-generated auto-submits. Never blocks. |
| Session | sessionStart | inject-doctrine reads doctrine.md and emits it as additional_context. |
| Edit | afterFileEdit (^Write$) | scope-refresh records the edited file into files[] and stashes a one-line scope reminder. |
| Tool | postToolUse | scope-drain delivers the stashed reminder as additional_context (one-shot), then deletes it. |
| Shell | beforeShellExecution | permission-gate checks the command against a deny list. Allow by default, deny by list, fail open (failClosed: false — an internal error or pwsh abort does not block shell). |
| Stop | stop | final-review reads git diff --name-only HEAD + untracked files, pulls intent from .scope.json (with prompt as source), and emits one FINAL REVIEW follow-up if anything changed. Bounded by a per-cid one-shot brake (reviewed-<cid>.flag) and loop_limit: 2. |
Layout
windows/ PowerShell 7 hooks (pwsh) — install on Windows machines
hooks.json 6-event hook wiring for ~/.cursor/hooks.json
inject-doctrine.ps1, doctrine.md
hooks/ intent-precompile.ps1, scope-refresh.ps1, scope-drain.ps1,
permission-gate.ps1, final-review.ps1, hook-common.ps1 (shared)
+ 3 prompt files: anti-slop.md, final-review.md, cleanup-doctrine.md
linux/ bash hooks — install on Linux machines and SSH remotes
hooks.json, inject-doctrine.sh, doctrine.md
hooks/ same hooks, ported to bash (jq preferred, python3 fallback)
skills/ Cursor agent skills shipped with the package
anti-slop/ SKILL.md + the duplication scanner (`cursordoctrine sweep` runs it)
scripts/scan_slop.py, low_density.py
bin/ the npm CLI (npx cursordoctrine install / verify / sweep / uninstall)
INSTALL.md ready-to-paste prompt that tells a Cursor agent to
install the right folder and verify every hookBoth folders do the same thing. Windows runs everything through pwsh.exe (PowerShell 7 — Windows PowerShell 5.1 is not supported). Linux runs bash, which is what you want on a remote over SSH (check your ~/.ssh/config host — hooks live on the remote's $HOME, not your laptop).
Tuning and kill switches
All hooks fail open and always exit 0. Nothing here can block your session.
| Variable | Default | Effect |
|---|---|---|
| HOOKS_ENFORCE=0 | on | turns off all hooks at once |
| PERM_GATE_ENFORCE=0 | on | disables the permission gate |
| INTENT_PRECOMPILE_ENFORCE=0 | on | disables the .scope.json auto-write on prompt |
| SCOPE_REFRESH_ENFORCE=0 | on | disables per-edit files[] recording + re-injection (scope-refresh + scope-drain) |
| FINAL_REVIEW_ENFORCE=0 | on | disables the final review pass |
Design notes
- State lives under
$HOME, in~/.cursor/.hooks-pending/, keyed by conversation id:reviewed-<cid>.flag(the one-shot brake forfinal-review) and a transientscope-<cid>.txtstash (written byscope-refresh, deleted byscope-drainon the next tool boundary). Stale state older than 7 days gets swept on every stop. The only repo file is.scope.json. - Change detection is stateless.
final-reviewasksgitwhat changed — no marker file the agent maintains. It reads.scope.jsonfor intent trace (intentprimary,promptas source) and diffs declared vs touched files;intent-precompile+scope-refreshkeep the contract current without hand maintenance. - One review per implementation. The stop hook arms a per-conversation flag before emitting its follow-up, so a crash can't re-fire it and a long chat still gets a review after each implementation.
- The doctrine is short on purpose. 80 lines the agent reads at sessionStart and internalizes.
.scope.jsonis re-injected per edit (the contract, not the doctrine) so the blast radius stays visible — but the governing text itself is read once; re-injecting it 50 times doesn't fix a model that didn't internalize it.
Self-contained. No build. Open hooks.json and read it — that's the whole system in one file.
Built with Cursor.
License
MIT. See LICENSE.
