Reference: verb ↔ CLI ↔ MCP ↔ library#
Every verb ultimately calls one library function.
The CLI and MCP server are thin skins over the same core (tact/cli.py and tact/mcp_server.py).
They handle argument parsing, dispatch, and rendering only.
This table is the map.
Each function’s own docstring (linked via its module) is the deep, load-bearing reference.
Read it before relying on an edge case not spelled out below.
Verb |
CLI |
MCP tool |
Library call |
One-line contract |
|---|---|---|---|---|
Read |
|
|
|
Content-hash dedup ( |
Edit |
|
|
|
Resolve every edit via the ladder, interval-check for overlap, splice descending, write atomically. |
Check |
|
|
|
Identical resolution pipeline to |
Apply patch |
|
|
|
Parses a unified diff or fenced old/new blocks; writes resolve and stage all files before committing, while checks remain read-only. |
Heal |
|
|
|
Read-only fix for a failed builtin |
Undo |
|
|
|
Restores the newest journaled pre-image; a toggle of the most recent write, journaling the pre-undo state so undo is itself redoable. |
History |
|
(not exposed) |
|
Lists a file’s journaled pre-images, newest first. |
Definition |
|
|
|
Name-first (or |
References |
|
|
|
Same resolution, |
Hover |
|
|
|
Type/docstring info at the resolved position. |
Symbols |
|
|
|
LSP |
Diagnostics |
|
|
|
Every diagnostic currently known for the file, merged across pyrefly + ruff; |
Rename |
|
|
|
LSP-computed |
Anchor |
|
|
|
Captures a block’s position + content for later re-finding; optionally persists it under a name. |
Resolve anchor |
|
|
|
Byte-identical short-circuit if the file’s hash hasn’t moved; otherwise re-runs the ladder biased toward the anchor’s remembered index. |
Delta |
|
|
|
“What changed since this hash” — a real unified diff if the undo journal still has the baseline blob, else an honest full-content fallback with a note. |
Restart |
|
|
|
Clears a project root’s LSP blacklist and drops live servers, forcing a fresh spawn on next use. |
Doctor |
|
(not exposed) |
|
Reports CLI, optional MCP, state, and telemetry readiness in human or stable JSON form. |
Adoption |
|
(not exposed) |
|
Summarizes the event stream into an auditable adoption denominator: per-harness eligible counts, refusals, and hook p95. |
Hook route |
|
(hook adapter) |
|
Parses a native pre-edit payload and renders the selected harness’s native response; |
Routing modes#
Mode |
Eligible existing-file edit |
|---|---|
|
Observe and record; emit no intervention. |
|
Observe and record without injecting agent-facing context. |
|
Refuse the builtin edit, state that it was not applied, name the tact CLI/MCP recovery action, and name the way back. |
route_hook uses warn when no mode is given.
The library is advisory by default, and only a caller’s explicit --mode deny blocks anything.
Routing is content-blind. Existing ordinary files are eligible. Creation, notebooks, unrecognized tools, and malformed payloads remain native. So does any payload tact cannot even normalize into a path: classification failures resolve to allow rather than propagating out of the hook.
Exit codes (CLI only)#
Code |
Meaning |
|---|---|
|
Success. |
|
A structured refusal the library itself reported (ambiguous match, no match, drift-blocked transaction, unavailable LSP capability) — the result is still printed to stdout, only the exit code marks it. |
|
A usage error — bad arguments, a missing/non-regular file, anything the library raises |
|
An unexpected failure — any other exception |
--json is accepted either before or after the verb (tact --json edit ... and tact edit ... --json are equivalent).
The selected result, and nothing else, goes to stdout.
Supplementary notes (such as a truncation notice or no-live-LSP note) go to stderr.
MCP coverage gap#
history, doctor, and adoption are CLI-only today.
The MCP server exposes no tact_history/tact_doctor/tact_adoption tools.
This is a real gap, not a documentation oversight.
Use the CLI or the library directly for those three verbs from an MCP-only harness until it closes.
hook is deliberately absent from MCP.
It is a harness adapter invoked by a hook runner, not a verb an agent calls.
Deep reference#
Every module carries its full contract in its own docstring.
Run python -c "import tact.X; help(tact.X)", or read the source directly.
Start with tact/__init__.py’s package docstring for the overall shape, then the module for the verb you’re using.