Skip to content
rvemaPublic
forked from provasign/mason

About

Model-agnostic coding agent with the prism/grove code-graph engine and shale evidence trail baked in — no steering

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

 
 

Repository files navigation

mason

The incubating reference coding agent for Prism and Shale. Mason works with any model — local (Ollama), Anthropic, or OpenAI — with Prism change intelligence built into the harness and Shale evidence recorded when available. No steering files: the behaviors that make agents accurate and cheap are properties of the harness, not requests to the model.

mason "Rename the Status method of the ResponseWriter interface to StatusCode.
       Apply the plan including ambiguous edits, then verify with 'go build ./...'"

That task runs end-to-end — complete type-resolved rename plan, 24 edits applied, build verified — on a local 14B model at $0.

Why a harness instead of steering

Measured across model tiers (see the provasign research), steered agents fail in two ways that prompts cannot fix: they relay engine results lossily (re-typing a site list drops sites), and they re-derive solved traversals through grep. mason makes both structurally impossible:

  • Relay fidelity by construction — graph-operation payloads (site lists, edit plans) render directly to you; the model receives only counts and flags. It cannot drop what it never holds.
  • Invocation wall — tasks shaped like measured graph-wins (renames, signature changes, "all callers", dead code) are walled onto the graph tools for their first turn. Routing cannot wander into text search.
  • Harness-applied edits — rename plans are applied by mason with per-line drift checks, never re-typed by the model.
  • Graph-aware reads — read_file is prism's session-compressed read: a repeat read of an unchanged file costs a ~10-token pointer instead of the body. Savings are ledgered and shown after each session.
  • Edit-ready context — for a change or a bug fix, code_context returns prism's source delivery: the relevant code as verbatim, line-numbered windows plus each anchor's callers, in one call. The harness picks that delivery from the task's intent, not the model's phrasing, and the model is told those windows are reads it already holds — so it edits instead of re-reading. This extends mason past change-impact to localized bug fixes.
  • Honesty guard — if a task asked for a change and the working tree is untouched when the model claims success, the claim is rejected once and any remaining fabrication is flagged to you, never silently accepted.
  • Evidence trail — when shale is on PATH, every task logs intent → tool notes → done.

Install

go install github.com/provasign/mason/cmd/mason@latest   # or grab a release binary

Use

mason                          # interactive REPL in the current repo
mason "task…"                  # one-shot
mason --continue "follow-up…"  # resume this repo's latest conversation
mason --resume                 # pick any saved conversation (list + prompt)
mason --plan "how would we…"   # plan mode: read-only, mutations refused by the harness
mason init                     # generate MASON.md (project map) from the tree + code graph
mason --model sonnet "task…"   # friendly names: sonnet · haiku · opus · gpt · gpt-mini
mason --model ollama:qwen3-coder:30b "task…"           # full specs still work
mason --model auto "task…"             # explicit two-tier local routing by task shape
mason --model openrouter:qwen/qwen3-coder "task…"      # OpenRouter
mason --model lmstudio:qwen2.5-coder-32b "task…"       # LM Studio local server
mason --model oai:http://localhost:8000#my-model "task…" # any OpenAI-compatible server
mason --dir ~/src/project --yes "task…"   # --yes skips bash/edit prompts
mason --json --yes "task…"     # CI/SDK: one JSON object on stdout (reply, usage, changedFiles)
mason --max-cost 0.50 "task…"  # hard cost budget — the task stops at the estimate

--json keeps stdout machine-clean (narration goes to stderr) and reports ok, reply, usage (tokens + estimated cost), changedFiles (from git), and durationMs — pipe it straight into jq in a pipeline. --max-cost is enforced by the harness between model calls: a finished answer that is already paid for is still delivered; pending tool work is not started.

Per-task checkpointing: every task snapshots the tree first (tracked + untracked, via unreferenced git objects — your HEAD/index never move); /undo reverts the last task's file changes. Checkpoint objects persist until Git prunes unreachable objects. Mason refuses to create a checkpoint when credential-bearing paths such as .env, private keys, or cloud configuration would be captured; /undo is unavailable for that task rather than storing the sensitive content.

Standing permissions in .mason/config.json — allow-listed commands run without prompting, denied paths are hard lines that even --yes cannot cross:

{"permissions": {
  "bash": "ask", "edit": "allow", "write": "ask",
  "bash_allow": ["go test *", "go build *", "git status"],
  "bash_deny":  ["git push *"],
  "paths_deny": [".env*", "secrets/**", "*.pem"],
  "fetch": "ask",
  "fetch_allow": ["https://pkg.go.dev/*"]
}}

Plan mode (--plan, or /plan in a session) makes the session structurally read-only: the agent investigates with every read tool and answers with a concrete plan, while edit_file, write_file, apply_rename_plan, MCP calls, and non-read-only bash are refused by the harness — not by asking the model to behave. /plan off re-enables edits to apply the plan.

Web fetch: the model can pull an http(s) page as plain text (web_fetch) for docs and changelogs — permission-gated like bash, secret-redacted like everything else, 30s/2MB bounded, and private/loopback addresses are refused unless fetch_allow explicitly lists them.

Image input: name a screenshot in the task ("why does shot.png look broken?") and mason attaches it to the message — base64, harness-side, on all three provider wire formats (Ollama / Anthropic / OpenAI-compatible). --image <path> attaches explicitly (repeatable). Needs a vision-capable model; png/jpg/gif/webp, 8 MB bound.

Custom slash commands: every .mason/commands/<name>.md becomes /<name> in the REPL and TUI — the file body is the prompt, $ARGUMENTS is replaced with whatever follows the command. Edits apply instantly (loaded fresh per use); /help lists them.

Hooks — deterministic shell commands the harness runs around tool calls, configured under "hooks" in .mason/config.json:

{"hooks": {
  "pre_bash":   [{"match": "git push*", "run": "./ci/guard.sh", "block_on_fail": true}],
  "post_edit":  [{"match": "*.go", "run": "gofmt -w \"$MASON_FILE\""}],
  "post_task":  [{"run": "afplay /System/Library/Sounds/Glass.aiff"}]
}}

A block_on_fail pre_bash guard refuses the command and feeds its output to the model — it cannot be talked out of it. post_edit/post_write run formatters after each write (failures become warnings in the tool result); post_task fires when a task ends. Env: MASON_ROOT, MASON_FILE, MASON_COMMAND.

LSP diagnostics at edit time: mason auto-detects the project's language server (gopls, typescript-language-server, pyright/pylsp, rust-analyzer — whichever is installed), starts it lazily on the first edit, and pipes its errors/warnings for each written file into the same tool result as the edit. The model sees undefined: frobnicate the moment it writes the call — not three turns later through a failing build. Configure or disable under "lsp" in .mason/config.json ({"lsp": {"command": "gopls"}} / {"lsp": {"disabled": true}}).

Free local models — zero setup knowledge required

mason models

shows what is already installed, what your machine can run (filtered by memory), and downloads a pick on a single keypress — including installing the Ollama runtime itself if it's missing. If you start mason with no model anywhere, it offers this setup instead of an error. /model does the same inside the REPL. The catalog is curated to coding models with tool-calling support (the measured floor for driving mason); ★ entries are measured in the provasign study.

When --model is omitted, model detection chooses one model: the last available model used for this repository, then the best installed local model, then Anthropic, then OpenAI. Explicit --model auto is different: it routes graph-shaped tasks to the measured small local tier and other coding tasks to the strongest installed local model. It does not currently route hard tasks to an API tier.

Nobody types model IDs. /model shows ONE numbered list — installed local models, downloadable local models, a curated API shortlist (Claude Fable/Sonnet/Haiku/Opus, GPT-4.1/-mini), and — for any vendor you've already keyed — every current model straight from that vendor's own live model-list API, so the picker can never go stale the way a hand-maintained table would. Friendly names work everywhere a spec does: --model sonnet, /model haiku, fable, opus, gpt, gpt-mini. Picking an API model without a stored key starts a guided setup right there: the vendor's key page opens in your browser, you paste the key (input hidden — in the TUI it's collected masked in the input box), and it lands only in the OS keychain (macOS Keychain / Windows Credential Manager / Linux Secret Service). Next time it just switches.

Slash-command autocomplete. Type / in the TUI and a popup lists every matching command — built-in and project-defined (.mason/commands/) — with a one-line description, narrowing as you type. ↑/↓ moves the highlight, Tab/Enter fills it in (a second Enter runs it — the filled command still needs its arguments), Esc dismisses. /model continues into a second stage: model suggestions (your installed local models first, then sonnet/haiku/opus/fable/gpt aliases, then the rest) narrow as you type — /model hai → Tab → /model haiku → Enter. The line-mode REPL gets both stages via Tab-completion.

Native text selection. Mouse capture is off by default, so drag-select and copy work exactly like a normal terminal (PgUp/PgDn scroll). /mouse enables mouse-wheel scrolling when you want it; /mouse off returns to native selection.

Works on an existing repo or an empty directory — "start a brand new Go project with a module, a package, and tests" scaffolds, builds, and tests a real project from nothing (verified E2E with a local model at $0).

Interactive sessions open a full-screen TUI: scrolling transcript, input box, spinner, and a live status bar (state · tokens · cost · model). Assistant replies render as markdown. Permission prompts render inline (y/n) with a preview of the exact change — the −/+ diff for an edit, new-file/overwrite disclosure for a write, edit counts for a rename apply. /model numbers downloadable models too: picking one hands the screen to ollama's progress bars, then switches to the model automatically. Ctrl+C cancels the running task without killing the session, ↑/↓/PgUp/PgDn scroll history. --no-tui keeps the plain line-based REPL (also used automatically when stdout is not a terminal). Assistant text streams as it is generated (Ollama, Anthropic, OpenAI). In either mode: /model <spec> switches models mid-conversation, /cost shows session tokens and an estimated $ figure, /savings the graph-read ledger, /compact condenses old history (also automatic as context fills) — deterministically: the harness builds a factual ledger of what happened (tasks, tool calls, result heads, replies), no summarizing model call, no cost, nothing misremembered. /plan toggles read-only mode, /clear, /help, /exit.

code_context gives the model one-call task context from the graph: the symbols matching its terms plus their callers, callees, and tests, compressed to budget — replacing a chain of read_file/grep round-trips (measured 52% token savings vs raw reads on its first live use).

Sessions persist per repo — every conversation is kept. --continue resumes the latest; mason --resume (or /sessions + /resume N inside a session) lists them with auto-titles and lets you pick; /resume name <x> names the current one. mason sessions prints the list headlessly. AGENTS.md / MASON.md at the root are loaded as project instructions — mason init generates a MASON.md project map (layout, build/test commands, entry points, graph stats) deterministically from the tree and code graph.

Subagents: the model can delegate a self-contained subtask ("survey how X is structured", an isolated analysis) to a fresh agent with its own empty context and the same tools. Only the subagent's final summary returns — its intermediate reads never consume the parent's context. One level deep, half the parent's turn budget, optional cheaper model (model: ollama:qwen2.5-coder:14b) for exploration.

Which model tier do I need?

Task shape Tier that suffices
Renames, signature changes, callers, missing impls, test gaps, dead code local model — measured at the engine ceiling (recall 1.00 with qwen3-coder:30b across 4 languages; renames verified E2E on a 14B)
General editing/bugfixing local 30B or a small API tier (haiku)

Numbers and methodology: github.com/provasign/research (RESULTS.md — the with/without-graph agent grid, the tier-invariance result, and the named-tool comparison).

Production behavior

  • Retries: transient provider failures (429/5xx/network) retry with backoff; auth and bad-request errors fail fast. Streaming retries only until the first byte has been shown.
  • Ctrl+C cancels the current task, not the session; the conversation stays consistent and is saved.
  • bash timeout: one hung command cannot hang mason (default 5m, MASON_BASH_TIMEOUT seconds to change).
  • Path confinement: model-supplied paths for read/edit/write/grep/list cannot escape the project root.
  • Secret redaction (default on): every tool result (file reads, grep hits, bash output, web fetches) is scanned before it reaches the model; detected credentials become [REDACTED:kind] and the notice itemizes by kind — 6×credential reads as test fixtures, 1×anthropic-key as a real leak. /secrets off disables (loudly).
  • Engine-optional: if the code graph cannot open, mason warns and runs with the coding tools — it never bricks.
  • Turn limit ends in a forced wrap-up summary flagged for verification, not a hard failure; crashes save the session before exiting.

Credentials

Local models need no credentials. For paid APIs, keys resolve in order: environment variable (ANTHROPIC_API_KEY / OPENAI_API_KEY) → OS keychain → interactive prompt (echo off, offer to store). The keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service) is the only place mason ever persists a key. Keys are never written to config files, sessions, logs, or the shale trail, and provider error paths scrub the key value.

mason login anthropic    # store a key in the OS keychain
mason logout anthropic   # remove it

Subscriptions are not a backend. Mason's harness needs raw tool-calling model access, and Claude Pro/Max (Claude Code) and ChatGPT/Codex subscriptions are licensed for use inside the vendor's own products, not as a BYO model backend for a third-party agent. So the sanctioned raw-model paths are API keys (Anthropic/OpenAI) and local models — there is no compliant way to drive Mason's loop off a subscription login. (An earlier experimental "Sign in with ChatGPT" flow reused the Codex CLI's client_id to hit an endpoint directly; that is outside OpenAI's terms, so it was removed.) A ToS-clean way to use a subscription is to delegate a whole task to the vendor's own CLI (claude -p, codex exec) as a subprocess — but that runs the vendor's agent loop, not Mason's harness. To get graph-backed correctness on a subscription you already pay for, install Prism as an MCP server in Claude Code or Codex (prism init --global) rather than routing the model through Mason.

Testing

Run before committing or releasing:

  • go test ./... — unit suite; must be green (CI runs it on push).
  • Agent-level benchmarks (the four-tier model x arm grid, the e2e bug-fix set) are LLM-in-the-loop, noisy, and expensive, so they are not a CI gate. If you change agent steering or context delivery, re-run the relevant harness in provasign/research (harness/bench_matrix.py or harness/run_e2e.py) with multiple trials and eyeball for regressions — never trust a single trial (measured: single-trial e2e reads are coin-flips). Do not tag a release with unit tests red.

License

Apache-2.0

About

Model-agnostic coding agent with the prism/grove code-graph engine and shale evidence trail baked in — no steering

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages