Skip to content

Repository files navigation

wand

An agent toolkit for repositories: it sets up and maintains a repo's agent machinery — its covenant, and the blessing path work travels along.

Status: early. init bootstraps a Linear team to the covenant and installs the guard hook; guard enforces the covenant's authorization rules; queue and ticket are the read layer; doctor reports a team's drift from the covenant. The covenant and bless verbs are stubs.

Install

brew install mattwalters/wand/wand

Or with Go:

go install github.com/mattwalters/wand@latest

That puts wand in $(go env GOPATH)/bin, which needs to be on your PATH. Prebuilt binaries for macOS, Linux and Windows (amd64 and arm64) are on the releases page; the v1 tag always points at the latest v1.x.y.

Usage

wand                        # help
wand ui                     # the interactive interface
wand queue --team-key WND   # the ranked, vetted Todo queue
wand ticket WND-3           # one ticket whole, for a cold reader
wand doctor --team-key WND  # report the team's drift from the covenant
wand version                # build info, and the covenant schema this binary speaks

wand version reports the covenant schema version alongside the build: a repo's covenant file declares the schema it was written against, and comparing the two is how you learn whether a given binary can read it.

The covenant file

The state graph — Triage → Backlog → Scoping → Todo → Needs Input → In Progress → In Review → Done — is wand's opinion, gofmt-style. What a repo customizes are the parameters of the machine, never its shape: a checked-in wand.toml at the repo root carries status names over the fixed semantics, caps (review rounds, CI attempts, worker timeouts), the estimate scale, toggles, the three pluggable commands (verify, provision, run agent), ticket templates, and a schema version so topology upgrades ship centrally as wand releases. TOML, so the rationale for a value survives as a comment next to the value it justifies:

schema = 1

[statuses]
# Our board predates wand and the team reads "Ready" as blessed-to-build.
todo = "Ready"

[caps]
review_rounds = 5

[commands]
verify = "make check"

wand init and wand doctor read it when present and fall back to the stock covenant when absent. Validation refuses unknown keys loudly — a misspelled cap silently defaulting is the failure mode — and an invalid file is an error, never quietly the defaults.

The file must never contain a secret, a machine path, or a harness name: those are machine config, not covenant. The test for the split: if two clones could legitimately differ, it is config; if a difference means two different processes, it is covenant.

The doctor

wand doctor --team-key WAND reads the team's statuses, labels, PR automations and settings, diffs them against the covenant, and reports the drift. It writes nothing — wand init is the verb that repairs — and it exits diff-style, so scripts and CI can hold a team to the covenant continuously instead of a human verifying the settings pages once:

  • 0 — the team satisfies the covenant
  • 1 — drift found (each finding on its own drift: line)
  • 2 — the check could not run (no API key, no such team, API failure)

The covenant, not Linear's settings pages, is the source of truth — the API exposes everything doctor needs, git automations included. Extra statuses outside the machine's path (a team's own "Design" column upstream of Backlog), extra labels, and automations on events the covenant does not mention are tolerated strangers, not drift. Renamed or missing covenant statuses, repointed automations, missing labels, and changed team settings are drift.

The guard

Some ticket transitions hand out authorization an agent does not have: promoting to Todo blesses building, promoting to Scoping blesses research, and Done, Canceled and Duplicate close a ticket. Those are a human's call, so wand guard refuses them — by status name or by Linear state type — while leaving every legitimate agent move alone (In Progress, In Review, Needs Input, Backlog, Triage).

It speaks the Claude Code PreToolUse hook protocol: the pending tool call arrives as JSON on stdin, and exit code 2 blocks it with the reason on stderr. Input that does not parse passes — a broken guard must never wedge a session.

wand init installs the hook: an entry in .claude/settings.json routing every Linear save_issue through wand guard. That entry is a build artifact — regenerated by init, never hand-edited — and it assumes wand is on your PATH.

The read layer

wand queue prints a team's Todo issues in start order: priority ascending with "No priority" last, oldest first within a priority, identifier as the final tiebreak so two racing readers agree. Issues an agent may not start are vetted out and printed with the reason — labeled human-only, or blocked by an issue not yet completed or canceled (a started blocker still blocks). Nothing is dropped silently: a queue that quietly comes up short reads as a queue in order.

wand ticket WND-3 renders one issue for someone with no context — a worker being prompted, or a human catching up. The description is passed whole; comments follow oldest-first, every page of them, each held to a per-comment budget so a long early comment cannot crowd out the short answer a human left last.

Both need LINEAR_API_KEY in the environment, and both respect a wand.toml's status renames — a board whose blessed column is called "Ready" queues from "Ready".

Docs

The doctrine — the why behind every covenant state and rule, which deliberately does not live in the covenant file — is at wandcli.com. The docs are versioned with the tool: every release publishes a frozen copy under its own tag next to the moving latest, so a repo pinned to wand vX.Y.Z reads the doctrine matching its covenant schema.

The site lives in docs/ — plain Markdown built by Hugo (a single Go binary, keeping with the no-Node rule) with a hand-rolled minimal theme. make docs-serve previews it locally.

Publishing is two parts: the release workflow commits the built site to the gh-pages branch on every tag, and a one-time repo setting (Settings → Pages → deploy from the gh-pages branch, with wandcli.com as the verified custom domain) tells GitHub to serve it — after that, every release publishes on its own.

Run from a clone

You do not need to install anything to try it:

go run . ui

make run does the same. To get a binary instead, make build writes bin/wand (gitignored), and make install puts wand on your PATH.

Testing a TUI

Terminal UIs are awkward to test and nearly impossible for an AI agent to review, because the thing under test is a picture. wand renders screens to plain text instead:

$ wand ui --script "j,enter" --dump-screen
 wand covenant

  Read and edit the repository covenant

  "covenant" is not implemented yet.

  esc back • q quit

--script applies a key sequence first, so any screen is reachable from one command, and no terminal is needed. From a clone that is go run . ui --script "j,enter" --dump-screen, or make screen SCRIPT=j,enter.

The test suite renders through the same code path, so those snapshots are stored as golden files that read as pictures of the UI:

tuitest.AssertScreen(t, "detail", tui.New(80, 24), "j,enter")

When a screen changes, the failure is a unified diff of the screen itself rather than a wall of escape codes. Under the hood a real tea.Program runs with its output captured, and those ANSI bytes are replayed through a virtual terminal emulator to recover the grid of characters a user would see.

Four tiers cover the app: pure Update tests, runtime wiring tests, golden screens, and one end-to-end test that drives the compiled binary through a real pseudo-terminal. See CLAUDE.md for the details and the rules that keep it deterministic.

Development

go run . ui          # run the TUI from source
make test            # fast suite
make test-e2e        # pty smoke test
make check           # gofmt + vet + test
make update-goldens  # regenerate screens (then read the diff)
make docs            # build the docs site into docs/public (needs hugo: brew install hugo)
make docs-serve      # serve the docs at http://localhost:1313/ with live reload
make release VERSION=v0.1.0  # tag and push a release (CI does the rest)
make help            # every target

Built with Bubble Tea, Lip Gloss, Bubbles and fang.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages