No models, no network. Build the graph, query it, verify authored intent, and render docs. This alone is a complete tool.
# Scaffold the store, git hooks, an AGENTS.md block and an agent skill
# (.agents/skills/roteiro/SKILL.md; also .github/skills when the repo uses .github)
roteiro init
# Build / incrementally update the graph for the working tree
roteiro sync
# Explain a node — its provenance-labelled neighbourhood
roteiro query 'sym:rust:crates/rto-graph/src/store.rs#Store'
roteiro query --kind fn # list every function node
# Verify authored links against the code; non-zero exit on drift (CI gate)
roteiro check
# Shortest path between two nodes, and outstanding intent-debt markers
roteiro path 'file:src/main.rs' 'adr:0001'
roteiro debt
# Render the graph to a docs site or an OKF bundle
roteiro render docs
roteiro render okf
# …or one bundle spanning a whole workspace, members and all
roteiro render okf --workspace-name payments
Without --workspace-name, render okf renders the current project with its
sections at the bundle root, even when that repository is a member of a
configured workspace. With one, each member's concepts nest under /<member>/.
That is what keeps them apart: node keys are repository-relative, so every
repository's README.md is the same key — file:README.md — and the directory
does the separating rather than a naming scheme. Each member is read with its
own roteiro.toml.
Two things to know before you rely on a bundle: the output directory is
deleted and rebuilt on every render, so your own notes belong outside it and
link into it by name — and the format changed in 4.0.0, with no migration:
render obsidian and the vault it wrote are gone. Both are explained on the
OKF bundle page.
A bundle is shareable, and what it shares is the graph. It names every file, symbol, configuration key and intent-debt marker in the repository, and carries the whole text of the prose it ingested — the ADRs, the blueprints, the documents — so a bundle of a private repository is private. Two things it does not carry, because neither is a graph node: analyzer findings and agent memory live in stores of their own and no render reaches them. A configuration key appears by name, never with its value.
The workspace vault's _Home had two summaries a bundle has no home for yet —
the cross-member findings summary and the version-pin table. They are recorded as
not-yet-reimplemented in
ADR-0021
rather than left to be noticed.
Pull a real embedding or generative model once (with consent), then run
everything locally — the “online” is a one-time, explicit download, after which
inference is offline again. Requires --features inference-local-models.
# See models curated for THIS machine's resources, then pull one
roteiro model list
roteiro model pull bge-small-en-v1.5-gguf
# Download bge-small-en-v1.5-gguf (~65 MB, MIT) from …? [y/N]
# Suggest inferred similarity edges using the pulled embedding model
roteiro infer --model bge-small-en-v1.5-gguf --min-confidence 0.5
# Report likely-duplicate content (same blob, or near-identical embeddings)
roteiro duplicates --min-similarity 0.9
# Draft a house-style, graph-grounded ADR/blueprint with a generative model
roteiro spec scaffold "rate limiting" --kind adr
roteiro spec draft draft-adr.md # fills placeholder sections
model pull is the only thing that touches the network here; after
it, every model above runs on this machine and nothing you ask is sent anywhere. The
separate, default-off capability that does call a hosted model at use time is the
remote model tier — a different feature, a
different name, and a consent model of its own. If you enabled
inference-local-models, you did not enable that.roteiro serve is the networked server: an OpenAI-compatible /v1 API over your
installed models, the read-only /v1/graph API and the explorer web
UI — all on one loopback port. Built with --features serve and a model
installed, it also enables the Ask tab; without the model feature (or none
installed) it degrades gracefully to the model-free graph API + UI. Binds
[serve] addr (default 127.0.0.1:8017); set [serve] tls_cert/tls_key for
in-process HTTPS.
# Network server on 127.0.0.1:8017 — /v1 + /v1/graph + the web UI
roteiro serve # → prints the URL to open
# Call /v1 on that default bind like any OpenAI endpoint
curl http://127.0.0.1:8017/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"qwen3-0.6b","messages":[{"role":"user","content":"hello"}]}'
# Or override the bind address/port and terminate TLS in-process (then use https://…)
roteiro serve --addr 0.0.0.0:8443 --tls-cert fullchain.pem --tls-key privkey.pem
/v1. A non-loopback bind like
0.0.0.0:8443 is warned about — use in-process TLS
([serve] tls_cert/tls_key) or a reverse proxy. In-process TLS is a
serve feature; roteiro explorer serves plain HTTP only.Expose the knowledge graph to an MCP-capable agent (Claude, editors, custom
tooling). Agents get typed tools to query nodes, fetch a node's context bundle,
find paths and list debt — grounded in the same graph humans review.
Requires --features mcp.
# Default: the MCP graph server over stdio (for a local agent to spawn)
roteiro mcp
# Or networked over streamable HTTP (terminate TLS at a reverse proxy)
roteiro mcp --http 127.0.0.1:8080
# Restrict what this server exposes — by class, by name, or to everything read-only
roteiro mcp --tools query,quality # drops the security + sandbox prose: about two fifths of the surface
roteiro mcp --tools search,explain,context,path
roteiro mcp --tools read-only # drops sandbox_clear, the one mutating tool
--tools
(and [mcp] tools in config) bounds both tools/list and
tools/call — a tool that is not advertised is not callable either, because a client
that already knows the name is exactly the case a restriction is for. An unknown name, or a
restriction leaving nothing to serve, is a startup error rather than a server that quietly
advertises everything. It bounds the served-chat tools on the same terms, so one server
never tells its MCP client and its chat client different things about itself.query (search, explain,
context, path, list_kind, list_projects),
quality (check, debt, debt_density,
coupling, config_secrets), security and
sandbox. Every advertised tool costs tokens on every turn whether or not the session
could ever reach it, and security + sandbox are roughly two fifths of
the advertised bytes — a code-navigation session pays that every turn to advertise tools it will
never call. The default is still every class; narrowing is opt-in.
list_tool_classes is advertised whatever you withhold, so a model asked about
analyzer findings on a query-only server answers "that class is not loaded in
this session" rather than "Roteiro cannot do that".roteiro tools: advertising 7 of 16 tools — 4803 of 19155 advertised bytes — withheld: …
That line is the authority, and it is printed whether or not --mcp is passed,
because the selection bounds the served-chat tools too.roteiro serve to roteiro mcp; bare roteiro serve is now
the network HTTP server. The old serve --http and
serve --models spellings still work as deprecated aliases (with a notice).An interactive web view of the graph. Start at a workspace overview — the
cross-repo topology, the config override matrix and drift — then click any repo
to drop into its own node/edge graph: hotspots, intent-debt with reasons, a node
detail panel, and a follow-the-link hop that jumps from a spoke's config key
straight to the hub struct that defines it. Fully offline and model-free;
requires --features explorer.
# Serve the explorer UI on loopback (no model needed)
roteiro explorer # → this directory's repo (--scope here, the default)
# Every configured workspace — what the default used to be
roteiro explorer --scope all
# One configured workspace, and only it
roteiro explorer --scope workspace payments
# One OKF bundle, ours or somebody else's — no graph, just the viewer
roteiro explorer --scope bundle ./their-bundle
# Pick which workspace the flat /v1/graph routes bind to (does NOT narrow)
roteiro explorer --workspace-name payments
--scope defaults to here.
Serving every configured workspace is --scope all — a choice you make
rather than what you get by standing in the wrong directory. If a config defines
workspaces and you did not name a scope, the server says so on one line and names
--scope all. A server started by a service manager has no useful
working directory, so set [serve] scope in config instead of passing a
flag. roteiro serve takes the same flag and the same default.--features serve,explorer and run roteiro serve:
it serves the same explorer UI and the model endpoint on one port, which
enables the Ask tab — natural-language questions answered by a
local model calling the graph's tools. roteiro explorer on its own
stays model-free, with Ask disabled.