Skip to content

Repository files navigation

Writ

Use your agent. Record and limit its tool calls.

Writ connects to Claude, Codex, Cursor, or OpenCode through the agent's native hook. You keep using the same agent. Writ records the tool calls that the hook reports. It can stop calls before they run.

The first connection needs no writ file, model key, or MCP configuration.

you → your agent → native hook → Writ decision + record
                        ↓
                   agent runs tool

Three levels of use

Level Start with You get
Basic writ connect <agent> A record of visible built-in, MCP, remote MCP, and hosted tool calls. Nothing is blocked.
Normal Add --read-only or --allow Calls outside the tool limit stop for approval.
Advanced writ sessions --agent <agent> Select one observed session. A writ adds path, command, domain, or exact-tool grants when the hook can enforce them.

Start with basic use. Add a limit after the record shows which tools the project needs. Add a writ file when the task needs more exact authority.

Install

On macOS, Linux, or Windows Git Bash:

curl -fsSL https://writ.build/install.sh | sh

On Windows PowerShell:

irm https://writ.build/install.ps1 | iex

The installer selects the release for the operating system and processor. It verifies the SHA-256 checksum before it installs Writ.

writ --version

Basic use: connect and review

Run one command for your agent:

writ connect claude
writ connect codex
writ connect cursor
writ connect opencode

Choose the command for your agent. Start or restart the agent. Then use it as usual.

$ writ connect codex
Connected Codex to Writ.
  Built-in tools: limited
  MCP tools: watching
  Remote MCP tools: watching
  Hosted tools: not visible
Scope: global.
Writ now records visible tool calls.
Nothing is blocked.
Start or restart Codex. Use it as usual.
After a tool call, review what happened:
  writ review

The status output tells you what the selected agent reports to its hook. Coverage differs by agent. Tool results come from the agent. Writ does not independently prove the effects of a connected tool call.

Tool-call records contain normalized arguments and a digest of the exact provider arguments. Writ keeps the exact provider arguments transient unless it creates an explicit approval request. The approval request can store the exact arguments after redaction.

Normal use: add one limit

Allow normalized read and search tools:

writ connect codex --read-only

Or allow exact agent tool names or normalized tool names:

writ connect cursor --allow read --allow search

A call outside the limit stops and creates an approval request:

writ approvals
writ approve <request-id>

Ask the agent to retry the call after approval. The approval is bound to the tool name and arguments.

Check or remove a connection:

writ connect status codex
writ disconnect codex

The default connection applies in every project for your user account. Limit the connection to the current project when needed:

writ connect codex --project
writ connect status codex --project
writ disconnect codex --project

When both scopes exist, Writ applies both limits. A project connection can narrow a global limit. It cannot widen one.

State and reusable writs

Writ stores its state outside the repository:

~/.writ/
  config.yaml
  connections/
  writs/
  projects/<project-id>/
    record.db
    connections/
    organization.json

Set WRIT_HOME to use another directory. Writ does not create a project .writ directory. When you connect from a Git worktree, Writ adds /.writ/ to the local .git/info/exclude file. This keeps a local .writ directory out of Git without changing the shared .gitignore.

writ issue and writ start reject a Writ home that is the project root or is inside it. Set WRIT_HOME to a directory outside the project before you start a managed or attached run.

Create a writ that can be used from any project:

writ init bug-fixer --global
# edit ~/.writ/writs/bug-fixer.yaml
writ check bug-fixer
writ plan bug-fixer --agent claude
writ sessions --agent claude
writ start bug-fixer --agent claude --session <session-id>

Fill in the mandate and grants before you run writ check. A writ file in the current project takes precedence over a global writ with the same name.

Advanced use: apply a writ to your agent

Use a writ file when a tool list is too broad. This example limits reads to source and test files. It also allows Claude's visible WebSearch tool.

writ: source-review
mandate: "Find the code that controls retry behavior."

grants:
  - read: { paths: ["src/**", "tests/**"] }
  - web_search: {}

Make one tool call in the intended Claude session so Writ can observe its ID. Then check the writ, select that session, and start the writ:

writ check source-review.yaml --explain
writ plan source-review.yaml --agent claude
writ sessions --agent claude
writ start source-review.yaml --agent claude --session <session-id>
# Continue in Claude.
writ finish --agent claude
writ review source-review

writ start does not launch an agent or replace its harness. It applies the writ only to the selected session. Other sessions keep their normal connection policy. One agent can have one active writ in a project.

Keep using your existing workflow to give the task to the agent. Writ records the mandate, but the native hook does not insert it into the agent prompt.

Run writ finish --agent <agent> only when you accept that the selected session satisfied the writ. That command writes a human satisfaction attestation. Run writ finish --agent <agent> --cancel to stop the attached writ without an attestation.

Writ checks the complete grants allowlist and every authored clause before activation. The command stops and names each unsupported entry and missing event. It does not silently ignore an unobserved tool source, cost bound, completion check, worktree-effect rule, or another term that the selected hook cannot enforce.

An attached writ is default deny across the complete agent tool surface. The hook must provide complete pre-tool coverage. Claude currently provides this coverage. Codex, Cursor, and OpenCode remain available for basic and normal connections, but their current hook contracts cannot run an attached writ.

An omitted bound is authored-unbounded. The managed writ issue adapter adds operational defaults of 25 iterations, $1.00, and 15 minutes when those bounds are omitted. An attached writ starts only when its /grants coverage row and every authored clause are enforced.

Do not put secrets in a writ document or a --set value. Attached activation state stores the bound document.

Use writ issue <file> when you explicitly want Writ's managed model-loop adapter. That adapter can count model turns and cost, verify worktree changes, run completion checks, and apply the clauses that native hooks cannot support.

Advanced deployments can also:

  • connect a local or remote MCP server with writ proxy;
  • inherit a signed organization policy with writ policy;
  • export and verify a signed record with writ record export and writ record verify;
  • attach a receipt from an independent witness with writ record witness.

writ gate is a model API observer. It routes the configured model and records admitted turns plus provider-reported usage. It does not enforce tools, results, effects, authored cost or wall-clock bounds, remedies, or complete measured cost. Fixed relay safety guards remain separate from authored clauses. A valid writ contains a grant, so gate use needs writ plan --target gate and explicit --allow-partial acceptance.

Agent coverage

Agent Built-in tools Local MCP Remote MCP Hosted tools Full attached writ
Claude Covered Covered Covered WebSearch and WebFetch are covered. Supported
Codex Most local tools Covered Covered Hosted tools such as WebSearch are not visible. Not supported
Cursor Covered in trusted workspaces Covered Covered Early cloud turns may run before hooks load. Not supported
OpenCode Permissioned tools Covered Covered Coverage depends on permission events. Not supported

Run writ connect status <agent> for the installed connection. Writ records only events that the agent sends to its hook.

Commands

Command Purpose
writ connect Add Writ to Claude, Codex, Cursor, or OpenCode.
writ disconnect Remove the Writ-owned hook.
writ approvals List stopped calls.
writ approve / writ deny Decide an argument-bound call.
writ review Read the latest run in the terminal or a local browser.
writ record Export, witness, or verify a signed run record.
writ init Create a writ file.
writ check Validate and explain a writ file.
writ plan Show whether a target can enforce the grants policy and authored clauses.
writ sessions List recently observed sessions for a connected agent.
writ start Apply a writ to an existing connected agent.
writ finish Attest satisfaction or cancel an attached writ.
writ configure Configure the model, judge, sandbox, and assent.
writ issue Run a writ with Writ's managed model loop.
writ proxy Govern a local or remote MCP server.
writ policy Configure a signed organization policy.
writ witness Run an independent checkpoint witness.
writ gate Observe model API traffic for an external agent.
writ amend Change the terms of an active writ.

Documentation

Read Install Writ, Connect and review, or Run one bounded task.

The documentation source is in docs/.

Development

task check
task build

Read CONTRIBUTING.md before you send a change.

License

Writ uses the Apache License 2.0.

Brand files are in blog/brand/.

About

Keep your agent and workflow. Add visibility first, limits when needed, and enforceable contracts for high-risk work.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages