Getting started with anvil¶
anvil is a local-first, backend-neutral project-state layer for humans and AI coding agents — the durable record of every requirement, task, claim, and piece of evidence in your project, stored in SQLite in a per-project workspace under
~/.anvil/and exposed through a CLI and an MCP server. This walkthrough takes you from an empty directory to a shipped task in about five minutes.
New to anvil's vocabulary — packet, claim, lease, gate? Keep the glossary open in another tab.
What you'll do¶
In ~5 minutes, you will:
- Initialize state in an empty project directory.
- Author a 12-line PRD against the template.
- Parse, review, and approve the PRD through its two-step gate.
- Generate, score, and promote tasks to
ready. - Claim the first task, get a work packet, submit evidence, and apply it.
By the end you will have one task in done, one claim recorded in events.jsonl, and an agent/t001-<slug> git branch holding the work.
Where your state lives¶
By default anvil keeps state outside your project, in a per-project HOME workspace: ~/.anvil/workspaces/<dirname>-<hash8>/.anvil/ (e.g. ~/.anvil/workspaces/my-project-183a2542/.anvil). Your repo stays clean — no .anvil/ directory appears inside it. anvil status prints the exact location on its Path: line, and anvil init prints the absolute PRD path in its next-step hint. Two environment variables override the default: ANVIL_STATE_LAYOUT=local restores the in-repo ./.anvil/ layout, and ANVIL_ROOT=<dir> pins state to <dir>/.anvil literally. Wherever this guide writes .anvil/…, it means the state directory anvil status reports.
Prerequisites¶
- Claude Code 1.x (or any MCP-compatible runtime).
uvinstalled — see docs.astral.sh/uv. The plugin auto-resolves Python deps on first invocation; no manualpip install.gitavailable on PATH —claimcreates anagent/<task>-<slug>branch.- An empty or existing project directory you can write to.
Step 1 — Install the plugin¶
Add the marketplace once, then install the plugin, both inside Claude Code:
/plugin marketplace add fakoli/anvil
/plugin install anvil@anvil
The install registers five hooks, wires the MCP server, and makes the five plugin agents discoverable at next session start. Verify with:
anvil --version
# → anvil 0.6.13 (schema 22)
Not using Claude Code? Install the CLI + MCP server from PyPI instead —
uv tool install anvil-state(orpipx install anvil-state) — then wire your harness withanvil install <harness>. See using anvil on any harness.
Step 2 — Initialize state in your project¶
cd /path/to/your/project
anvil init --name "My Project"
Output:
Initialized anvil for 'My Project' (id: my-project)
~/.anvil/workspaces/my-project-183a2542/.anvil/config.yaml
~/.anvil/workspaces/my-project-183a2542/.anvil/state.db
~/.anvil/workspaces/my-project-183a2542/.anvil/events.jsonl
~/.anvil/workspaces/my-project-183a2542/.anvil/packets/
Next step: author your PRD at ~/.anvil/workspaces/my-project-183a2542/.anvil/prd.md, then run `anvil prd parse`.
Your prd.md must contain these required sections:
# Project: <Name>
## Summary
## Goals
## Requirements
Optional ## Features / ## Tasks use bold-inline fields, e.g.
**Feature:** F001 (under a ### Txxx task heading)
**Requirements:** R001, R002 (under a ### Fxxx feature heading)
See docs/prd-template.md for the full template.
Note the paths: state landed in the HOME workspace, not in your repo. prd.md is intentionally NOT auto-created — you author it next, at the absolute path init just printed, against the template.
Step 3 — Author your PRD¶
Open the prd.md path that init printed (under ~/.anvil/workspaces/…) in your editor and paste a minimal valid PRD. The required sections are # Project:, ## Summary, ## Goals, ## Requirements, plus at least one task in ## Tasks to actually have something to claim. Any task that declares a **Feature:** F00N line must have a matching ### F00N: block in ## Features. Full schema in ../prd-template.md.
Requirement IDs are strict. Every requirement must use the canonical
R0NNform —R001,R002,R003. Suffixed or ad-hoc IDs likeR003aare refused by the parser, so number requirements canonically before you runanvil prd parse— splitting a requirement means renumbering, not suffixing.Multi-PRD note. A project can hold several release-scoped PRDs in one
state.db, each separately gated; runanvil prd listto see them. The default PRD's source is the bare.anvil/prd.mdused throughout this guide (conceptually.anvil/prds/default.md); a named release PRD uses its portable source under.anvil/prds/and is parsed withanvil prd parse --prd <prd_id>. Lowercase non-reserved IDs keep the familiar<prd_id>.mdspelling; see the PRD template for uppercase and Windows-reserved filename rules. Re-parsing a PRD replaces theRequirementrows in that PRD's partition only, leaving sibling PRDs untouched (Features and Tasks are (re)generated byanvil plan, which prunes orphans). Single-PRD projects can ignore all of this and keep using.anvil/prd.md.
# Project: My Project
## Summary
A small utility that uppercases the contents of a text file in place.
## Goals
- Convert any UTF-8 text file to uppercase with one command.
- Exit non-zero with a clear message on missing or unreadable files.
## Requirements
- R001: The CLI accepts one positional argument: the file path.
- R002: The file is read as UTF-8 and rewritten in uppercase in place.
- R003: Missing or unreadable files exit 1 with a message naming the file.
## Features
### F001: Uppercase CLI
The CLI entry point that reads a text file as UTF-8 and rewrites it in place
with uppercase contents.
**Requirements:** R001, R002, R003
## Tasks
### T001: Implement uppercase CLI
**Feature:** F001
**Priority:** high
**Likely files:** src/upper/cli.py
Parse the positional file argument, read as UTF-8, write back uppercased.
**Acceptance criteria:**
- `upper sample.txt` rewrites `sample.txt` with uppercase contents.
- `upper missing.txt` exits 1 and prints a message naming the file.
**Verification:**
- `pytest tests/test_cli.py -v`
Step 4 — Parse and review the PRD¶
anvil prd parse
# → Parsed 3 requirements, 1 features, 1 tasks.
# → PRD source: default
anvil prd assess # optional, advisory, and read-only
anvil prd review # draft → reviewed
# → PRD reviewed by 'human'.
# → Run `anvil prd review --approve` to approve.
anvil prd review --approve # reviewed → approved
# → PRD approved by 'human'.
The assessment highlights missing user context, outcomes, observable behavior,
boundaries, acceptance scenarios, and verification with a suggested challenge
question. Its findings are advisory: they do not alter parsing, approval, or
claiming. The two-step review gate remains deliberate. prd review records
that a human has read the PRD; prd review --approve unlocks task claiming.
The claims manager refuses to claim any new task while the PRD is in draft or
reviewed status. Only an approved PRD whose exact source/material binding is
still current lets work begin. Existing active claims are not revoked by a
later PRD edit.
Step 5 — Generate and score tasks¶
anvil plan
# → Planned 1 features, 1 tasks.
anvil score
# TaskID Complexity Parallel CtxLoad Blast Review Agent
# -----------------------------------------------------------
# T001 2 4 2 4 3 2
#
# Scored 1 task(s).
anvil review tasks
# → Promoted 1 task(s) to reviewed.
# → Promoted 1 task(s) to ready.
# → 2 total promotion(s). No tasks blocked.
anvil list --status ready
# TaskID Title Status Priority Type Score Feature
# ----------------------------------------------------------------------------------
# T001 Implement uppercase CLI ready high feature 2/2 F001
#
# 1 task(s) listed.
Six dimensions: complexity, parallelizability, context load, blast radius, review risk, agent suitability — each 1–5. Scores drive anvil next routing and expand recommendations. In the list table, Type is the kind of change the task represents (feature, bugfix, refactor, or modify) and Score is shorthand for complexity/agent-suitability.
Step 6 — Claim and ship the first task¶
anvil next
# → Next recommended task: T001
# Title: Implement uppercase CLI
# Priority: high
# Complexity: 2
anvil claim T001
# → Claimed task 'T001' as 'cc80db5f1e33f5f6'.
# Claim ID: CBA2432F4
# Lease until: 2026-07-02T06:33:13.691911+00:00
# Branch: agent/t001-implement-uppercase-cli
Claim IDs are random — yours will not be CBA2432F4 — and the lease defaults to 240 minutes; run anvil renew <claim-id> to extend it on long-running work. The claim also created the agent/t001-implement-uppercase-cli branch in your project's git repo (state lives in the workspace, branches live where the code is).
anvil packet T001
Wrote packet to ~/.anvil/workspaces/my-project-183a2542/.anvil/packets/T001.md
# T001 — Implement uppercase CLI
**Feature:** F001 — Uppercase CLI
**Status:** claimed
**Priority:** high
**Type:** feature
**Agent suitability:** 2/5
**Complexity:** 2/5
## Goal
Parse the positional file argument, read as UTF-8, write back uppercased.
## Acceptance criteria
- `upper sample.txt` rewrites `sample.txt` with uppercase contents.
- `upper missing.txt` exits 1 and prints a message naming the file.
...
## Verification
Commands:
- `pytest tests/test_cli.py -v`
Required proofs (typed — captured by the run hooks):
- `pytest tests/test_cli.py -v` exits 0
...
The packet — printed to stdout and written under packets/ in the workspace — contains the exact intent, acceptance criteria, verification commands, and non-goals the agent (or you) need to execute the work. No issue thread to summarize.
Do the work on the agent/t001-* branch, then submit evidence and apply:
anvil submit T001 \
--commands "pytest tests/test_cli.py" \
--files-changed src/upper/cli.py
Evidence submitted for task 'T001'.
Evidence ID: EV893EFA1D
Claim ID: CBA2432F4 (auto-released)
Submitted by: cc80db5f1e33f5f6
Commands: ['pytest tests/test_cli.py']
Files: ['src/upper/cli.py']
Task 'T001' status → needs_review.
Run `anvil apply T001` when ready for human review.
Evidence gate: INCOMPLETE — missing items for required_evidence:
- `pytest tests/test_cli.py -v` exits 0
The Evidence gate: INCOMPLETE line is advisory, not an error — the submit succeeded and the task moved to needs_review. A plain-CLI submit records your commands as strings; the typed exit-code proofs the gate checks for are captured by the run hooks (e.g. when a harness executes the verification commands), so a bare CLI walkthrough is expected to show this line.
anvil apply T001 --approve
# → Task 'T001' approved by 'human' → done.
# → Signed proof: ~/.anvil/workspaces/my-project-183a2542/.anvil/proofs/T001-E000015.json
What just happened?¶
state.db now records T001=done and the claim released. events.jsonl has
the full audit trail: project.created, state.initialized, prd.parsed,
prd.reviewed, prd.approved, one planning.batch_applied event containing
the ordered feature/task/status/conflict graph, followed by scoring, task
review, claim, evidence, and apply events. Replaying that log from an empty
database reconstructs state.db byte-for-byte — the audit guarantee that
makes .anvil/ safe to back up by copy.
anvil status sums it up, including the Path: line pointing at the workspace and a per-bucket task breakdown:
anvil for "My Project" (id: my-project)
Path: ~/.anvil/workspaces/my-project-183a2542/.anvil
Initialized: 2026-07-02T02:32:37.623329Z
PRD default (approved)
Tasks: 1 total (0 ready, 0 claimed, 0 in_progress, 0 needs_review, 0 blocked, 1 done)
Active claims: 0
PROJECT TOTAL
PRD: approved
Tasks: 1 total (0 ready, 0 claimed, 0 in_progress, 0 needs_review, 0 blocked, 1 done)
Active claims: 0
Sync: off
Schema: 22
The work packet under .anvil/packets/T001.md is the contract that drove the work. For the full picture of how transitions, gates, claims, and the event log fit together, see ../architecture.md.
Upgrading and uninstalling¶
Anvil can be loaded from several places at once: the anvil CLI and
anvil-mcp server from the Python tool install, plus a harness plugin or
generated MCP configuration. Upgrade them as one unit. A long-lived harness
does not replace its already-running MCP process just because the executable
on disk changed.
Before upgrading: identify the active executable¶
Run these checks in the project whose state you intend to open.
On Bash:
command -v anvil
command -v anvil-mcp
anvil --version
On PowerShell:
Get-Command anvil, anvil-mcp | Select-Object Name, Source | Out-Host
anvil --version
Upgrade the CLI and MCP executable¶
| Installation method | Upgrade command |
|---|---|
uv tool |
uv tool upgrade anvil-state |
pipx |
pipx upgrade anvil-state |
These tool-manager commands replace the executable environment. They do not
open this project's state.db.
Before resuming a skill-driven workflow, verify that the upgraded executable exposes the named-PRD capability required by current skills:
anvil prd source-name --help
A nonzero result means the active executable is still stale; do not infer a PRD filename or continue with a mutating command.
Resolve and back up state with the upgraded CLI¶
The target release provides --path-only, which resolves the state directory
without opening its database. Use the upgraded CLI to make the backup before
running ordinary status or any other backend-initializing command.
On Bash:
STATE_DIR=$(anvil status --path-only)
printf 'State directory: %s\n' "$STATE_DIR"
BACKUP_DIR="${STATE_DIR}.pre-upgrade-$(date +%Y%m%d-%H%M%S)"
cp -a "$STATE_DIR" "$BACKUP_DIR"
On PowerShell:
$stateDir = anvil status --path-only
"State directory: $stateDir"
$backupDir = "$stateDir.pre-upgrade-$(Get-Date -Format yyyyMMdd-HHmmss)"
Copy-Item -Recurse -LiteralPath $stateDir -Destination $backupDir
If the upgraded engine is newer and you want a deliberate migration, choose
that path now, before ordinary status initializes the backend:
anvil migrate state # dry run; review the reported backup path
anvil migrate state --yes # apply only after reviewing the dry run
Otherwise continue with ordinary status and allow the supported automatic migration. In either case, inspect the resulting version boundary.
On Bash:
anvil --version
STATUS_JSON=$(anvil status --json || true)
printf '%s\n' "$STATUS_JSON" | python -c 'import json,sys; p=json.load(sys.stdin); s=p["data"] if p["ok"] else p["error"]; print({"status":"compatible" if p["ok"] else s["code"],"engine_schema":s["schema_version"] if p["ok"] else s.get("supported_schema"),("pre_open_database_schema" if p["ok"] else "database_schema"):s["db_schema_version"] if p["ok"] else s.get("database_schema")})'
On PowerShell:
anvil --version
$status = anvil status --json | ConvertFrom-Json
if ($status.ok) {
[pscustomobject]@{
status = "compatible"
engine_schema = $status.data.schema_version
pre_open_database_schema = $status.data.db_schema_version
}
} else {
[pscustomobject]@{
status = $status.error.code
engine_schema = $status.error.supported_schema
database_schema = $status.error.database_schema
}
}
anvil --version identifies the CLI engine and supported schema, for example
anvil 0.6.13 (schema 22). schema_version is that engine schema.
db_schema_version (shown as pre_open_database_schema) is the database stamp
observed before the backend opens. If the command succeeds with a lower
pre-open value, that same call completed a supported migration; rerun the
status block to confirm the values are now equal. On mismatch, no migration
occurs and the comparison comes from supported_schema and database_schema
in the closed error envelope. --path-only never opens the database, so it
still identifies the backup target when the installed engine cannot open that
schema.
Refresh the harness integration¶
| Installed integration | Refresh command | Required follow-up |
|---|---|---|
| Claude Code plugin | claude plugin marketplace update anvil, then claude plugin update anvil@anvil |
Fully restart Claude Code so SessionStart and MCP load the new plugin. |
| Codex native integration | codex plugin marketplace upgrade anvil, then anvil install codex --write |
Restart Codex after the refreshed marketplace and CLI/MCP install are verified. |
| OpenClaw native integration | anvil install openclaw --write |
Restart OpenClaw after the refreshed CLI/MCP install is verified. |
| Other MCP client | anvil mcp-config <client> and replace the managed config block |
Restart the client so it launches a fresh anvil-mcp. |
Use this order:
- Stop state mutations and identify the active executables.
- Upgrade the Python CLI/MCP install; this does not open project state.
- With the upgraded CLI, resolve the state path and make the backup.
- Run the engine-version and schema checks.
- Refresh the plugin or harness integration that launches the MCP server.
- Fully restart every harness and MCP server process.
- Verify the live MCP initialize metadata below.
Verify the live MCP process¶
This sends one MCP initialize request to the same anvil-mcp executable a
harness launches and prints its serverInfo. The reported version must match
the engine version from anvil --version, not the FastMCP dependency version.
On Bash:
MCP_INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"anvil-version-check","version":"1"}}}'
printf '%s\n' "$MCP_INIT" | anvil-mcp 2>/dev/null |
python -c 'import json,sys; print(json.loads(sys.stdin.readline())["result"]["serverInfo"])'
On PowerShell:
$mcpInit = '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"anvil-version-check","version":"1"}}}'
($mcpInit | anvil-mcp 2>$null | Select-Object -First 1 |
ConvertFrom-Json).result.serverInfo
If SessionStart names different plugin and PATH versions, refresh only the
stale component it identifies, then restart the harness. If MCP still reports
the old version, an old server process is still alive or the harness is
launching a different executable than Get-Command/command -v found.
If the database schema is newer than the engine, do not delete state.db or
the state directory. Upgrade the stale CLI/plugin/MCP component and restart
the harness. Routine version recovery never requires deleting state; see
Migrations for the supported migration ladder.
Uninstall or roll back¶
- Roll back a harness install:
anvil install <harness> --rollbackrestores every fileanvil install <harness> --writemodified from its backup and deletes anything anvil created (native installs also run the harness's own removal command, e.g.codex mcp remove). To remove the Claude Code plugin itself, use Claude Code's own/pluginmanagement UI — the plugin never writes files outside Claude Code's own config, so there is nothing else on disk to clean up.
Common stumbles¶
- "PRD must be in 'reviewed' status to approve" — you ran
prd review --approvewithout first runningprd review. The two-step pattern is intentional. Runanvil prd reviewfirst, thenanvil prd review --approve. - "No ready tasks" — your PRD's
## Taskssection is empty, orreview tasksblocked promotion because**Acceptance criteria:**or**Verification:**is missing on a task. Both fields are required by thedrafted → reviewedgate. Re-check../prd-template.md. - "PRD file not found" —
initdoes not createprd.md. Author it at the absolute pathinitprinted (under~/.anvil/workspaces/…) before runningprd parse. Warning: git branch not created — not a git repositoryon claim — the claim itself still succeeded, but there is no repo to hold theagent/<task>-<slug>branch. Rungit init(plus a first commit) in your project, thenanvil release <claim-id>and re-claim.uvnot found — install it:pip install uvor follow docs.astral.sh/uv.- Want to start over? — run
anvil init --forceto wipe and re-scaffold. Don't reach forrm -rf .anvil/in your project: in the default layout there is no.anvil/there, so it's a no-op — state lives in the HOME workspace.