Stop asking your agent to behave.
Enforce it.
agent-contract installs a .agent/ contract into any repo. Each task gets a scope — and every out-of-scope write is blocked by a shell check that exits non-zero, before it touches your code. Any agent, any model.
then agent-contract init in any repo · or one-shot with npx @semeton/agent-contract init
Prose is a request. A contract is a constraint.
CLAUDE.md, AGENTS.md and .cursorrules all rely on the model choosing to cooperate. There is no enforcement layer underneath them — so nothing actually stops an agent from touching what it shouldn't.
Prose & rules files
You write "please don't touch the auth module." The model skims it, the text drifts from the code, and on a long task it quietly refactors three services anyway. You find out in the diff.
The .agent/ contract
A PreToolUse hook reads the active role and blocks any write outside its declared scope. Fail-closed — no role declared, no source writes. The agent can't even widen its own scope: the role files are off-limits in-session.
Five gates between a prompt and your codebase.
Each is a shell script that exits non-zero on a violation. The orchestrator reads exit codes, not promises.
Boot
The agent reads the contract before it reads your source: latest handoff → manifest.yaml → codebase map → its role.
Declare scope
pre-generate.sh validates the task spec has what it needs, then writes the active role — which arms the enforcement hook.
Enforce live
Every Write / Edit is checked against the role's allowed paths. Out-of-scope writes are blocked before they land — not flagged after.
Verify
post-generate.sh runs the right tools for your stack — eslint + tsc + jest, or phpstan + pint + phpunit — plus your convention gates.
Hand off
A Stop hook writes a handoff note on any turn with changes, so the next session boots with the decisions instead of starting over.
Install once. Init per repo.
Installing globally puts the CLI on your PATH, so updates are one npm i -g away. Then init detects your stack, writes the contract and installs the hooks — idempotent, re-run any time. Prefer not to install globally? npx @semeton/agent-contract init runs it in one shot.
Adding a language is one file.
Every stack is a single detector that returns evidence and a score. The highest score wins. Write one, and an entire ecosystem gets a contract.
- Zero deps to learn — pure stdlib, no build step, no framework.
- Good first issues — new detectors, a read-back YAML parser, the orchestrator loop.
- Tested end to end — one smoke test gates every publish.
// what it is
- An enforcement layer that scopes each task and blocks out-of-scope writes by exit code.
- Stack-aware: it runs the correct linter, type-checker and tests for your project.
- Tool-agnostic shims for Claude Code, Cursor, Copilot and the API.
// what it isn't (yet)
- Live blocking runs inside Claude Code's hooks today; elsewhere, wire
post-generate.shinto a pre-commit hook or CI. - It enforces scope, not logic correctness — that's what the tester role and coverage gate are for.
- Convention files need ~30 min of tuning per service. One-time.