# Hooks

Two hooks, both small, both readable in a minute, both safe to delete. They exist because a few of this kit's rules are the kind a model reliably rationalizes past under momentum — and those are exactly the rules worth making deterministic.

| Hook | Event | What it does |
| --- | --- | --- |
| `session-context.mjs` | `sessionStart` | Injects the session's starting facts: whether the model lanes are bound (and whether the picker drifted from the recorded lead), the handoff's next action, and any of the four capped context files currently over its cap. |
| `guard-destructive.mjs` | `beforeShellExecution` | Returns `ask` — never `deny` — for force pushes, `rm -rf`, migrations, deploys, infra changes, pipe-to-shell, and friends, with the reason named and the gate quoted back to the agent. Routine `git push` is deliberately not on the list. |

## Why these two

The lead-model check is the one that can only be done here. Cursor's model picker is a UI setting no project file can read or set, but the `sessionStart` payload carries the session's `model_id` — so this is the only place the recorded lane and the running model can actually be compared. Without it, a drifted picker shows up as a surprising invoice.

The cap check is deterministic arithmetic. A rule that says "keep `MEMORY.md` under 60 lines" is a request; counting the lines is an observation. Same for the shell gate: "get authorization before destructive actions" is advice, and `ask` is a stop.

## Safety properties

- **Fail-open by construction.** Neither hook sets `failClosed`, and both catch their own errors and exit 0. If Node is missing, if a file is malformed, if the script throws — Cursor logs it and the session continues. The worst case is losing the report, never losing the session.
- **`ask`, not `deny`.** The shell guard can only insert a confirmation. It cannot block you out of your own repository, and it has no way to be silently stricter than you expect.
- **Read-only.** Neither hook writes a file, phones home, or reads anything outside the workspace root Cursor hands it. `session-context.mjs` reads four project files (`docs/MODEL_ROUTING.md`, `docs/HANDOFF.md`, `docs/MEMORY.md`, `docs/TASKS.md`) plus `AGENTS.md` for the Lessons count; `guard-destructive.mjs` reads only the command string.
- **No dependencies.** Plain Node ESM, no `node_modules`. `node --version` is the entire requirement, which is also why they are `.mjs` and invoked as `node .cursor/hooks/…` rather than shell scripts — that runs identically on Windows, macOS, and Linux.

## Editing them

The destructive-command list in `guard-destructive.mjs` is a starting point, not a policy. Add your project's real hazards (a `deploy.sh`, a data-export command, a billing CLI) and remove what does not apply — a gate you approve reflexively every time has stopped meaning anything and should go. Routine `git push` was cut from the default list for exactly that reason; add it back if pushing is genuinely consequential in your repo.

Cursor runs project hooks from the project root, so paths in `hooks.json` are written `.cursor/hooks/…` rather than `./hooks/…`.

## Removing them

Delete `.cursor/hooks.json` and this directory. Nothing else in the kit depends on them — the rules they enforce are still written in `AGENTS.md`; they just go back to being advice.
