//! The tool descriptions of the catalog (`spec/surface/README.md` §4) —
//! GENERATED from `spec/surface/tool-descriptions.json` by
//! `scripts/gen-tool-descriptions.mjs`; do not edit by hand. That file is
//! written by the reference (`SURFACE_SPEC_UPDATE=1` on
//! `packages/core/corpus/surface/spec.test.ts`) from the registrations in
//! `packages/core/src/mcp/server.ts`, so both engines publish the same text
//! byte for byte; `tests/spec.rs` fails when either side drifts.
/// `docs_outline`
pub const DOCS_OUTLINE: &str = r#"Orientation call. Returns a document's compact indented outline (alias/type/label per line, § marks section headings). IDs are stable; prefer them in follow-ups. Address the document by `id` or `path` (at least one; a `path` in either form). For the whole document body in one call, use docs_read."#;
/// `docs_read`
pub const DOCS_READ: &str = r#"docs_read — One document, whole: raw bytes plus properties { frontmatter, inline, computed } and id/path/rev.
{"path":"/processes/calcination.md"} by path (either form)
{"path":"/processes/calcination.md","include_ids":true} plus ids (pre-order), hashes (the CAS tokens expect.content_hash takes), parents (null = top level)
{"id":"d_14"} by id (a query hit's id)
{"path":"/processes/calcination.md","at":"r_7"} as of a revision (rev ids: from revisions where path == "/processes/calcination.md")
Structure only: query md:section nodes. Many documents: a query with select raw. Grammar: query_syntax."#;
/// `docs_read_many`
pub const DOCS_READ_MANY: &str = r#"Batch whole-document read — the hydrate half of query→hydrate. The plural of docs_read: pass `docs`, a list of refs (each a document id OR a path, in either form), and get back one full read per ref. Returns `{ items, errors, truncated }`: each found doc is a full docs_read projection (`raw` = complete file bytes verbatim, `properties` grouped by source, plus `id`/`path`/`rev`, and — with include_ids:true — the document's ordered block `ids`, a `hashes` {block `id` → block `hash`} map for raw-`apply` CAS pinning, and a `parents` {block id → parent block id | null} map so top-level vs nested blocks are distinguishable without an outline read); a ref that resolves to no live document lands in `errors` as {ref, error:"doc_not_found"} WITHOUT failing the call, so one bad ref never sinks the batch. Duplicate refs collapse first-seen (a repeated ref yields a single item). Capped at 100 refs per call; excess refs are dropped and `truncated` is set. Pass budget_tokens to cap total hydrated size — the batch stops early and flags `truncated` when the next doc would exceed it; the FIRST doc always ships even when it alone exceeds the budget (the same at-least-one-row contract as docs_list/docs_tree, so a budgeted caller always makes progress). Use this after query/blocks_search/blocks_resolve to pull N whole docs in ONE round-trip instead of N serial docs_read calls; for a single doc use docs_read, and for lean structure-only orientation use docs_outline (docs_get_many before surface 3.0)."#;
/// `blocks_read`
pub const BLOCKS_READ: &str = r#"Hydrate one block subtree at a resolution (skeleton|outline|text|raw|full). `id` is the block's id (`b_…`) and its document is inferred from it; an `n_` node id is not a block id — nodes_block_read reads the block a node was projected from (nodes_get before surface 3.0: blocks used to be called nodes here, and `nodes` now names the extracted-entity rows). Every resolution carries the block's `id` and `type`; raw and full add `raw` (its source bytes) and `hash` (its own raw hash) — the value update/split need in expect.content_hash, so you can fetch it before editing rather than reading it back from a conflict; full adds `text`, `attrs`, `ordinal`, `depth` and `parent_id` (null at the top level). Use this to expand the lean ids returned by query/blocks_resolve. To read a whole document in one call, use docs_read (blocks_read on a heading's id returns only that block, not its section)."#;
/// `nodes_block_read`
pub const NODES_BLOCK_READ: &str = r#"Read the block a NODE was projected from: `id` is the node's id (`n_…`, from `query` over `from nodes`) and the result is exactly what blocks_read returns for that block — the same resolutions (skeleton|outline|text|raw|full, default full), the same `id`/`type`/`raw`/`hash`/`text`/`attrs`/`ordinal`/`depth`/`parent_id`. The hop from an occurrence (an `md:link`, `md:task`, `md:section`, … node) to the editable block that holds it, in one call; blocks_read itself takes a block id only. An unknown node, or a node that names no live block (a block-less node, such as one derived from the frontmatter), is block_missing."#;
/// `blocks_read_many`
pub const BLOCKS_READ_MANY: &str = r#"Fetch up to 100 blocks by id (in request order) with budget truncation — the plural of blocks_read (nodes_get_many before surface 3.0). Block ids are globally unique, so `ids` may span ANY number of documents — each id is resolved to its owning doc server-side. Result carries `nodes` (the hydrated blocks, one per resolved id), `truncated` (cap or `budget_tokens` hit — the first resolved block always ships even when it alone exceeds the budget, so a budgeted caller always makes progress), and `unresolved` — the requested ids that name no live block (never silently dropped)."#;
/// `docs_tree`
pub const DOCS_TREE: &str = r#"docs_tree — Orientation: the shape of a repo in one small call, like tree -L depth with document and block counts per entry.
{} the root at depth 1: one entry per directory (path ends in /) or shallow document, plus total
{"path":"/processes/","depth":1} descend into a directory entry
{"depth":2,"limit":5} collapse deeper; limit/cursor page the entries, total always covers everything under path
Ranked or attribute-based discovery: blocks_resolve, or query (select path from docs where ...). Grammar: query_syntax."#;
/// `docs_list`
pub const DOCS_LIST: &str = r#"ENUMERATE live documents (the `omg ls` operation) as a page: `{ items: [{ id, path, updated_at, blocks }], truncated, cursor }` — the document's id, its path (`/`-rooted, as every path the surface returns), last-commit timestamp (ISO, or null) and live block count — ordered by path. This is a flat `ls`, not an orientation primitive: unscoped on a large repo it is hundreds of rows, and the engine has no directory concept here — `path_glob` is a simple LIKE match where `*` matches ANY run INCLUDING `/` (so `projects/*` is the whole subtree; there is no 'immediate children only'). To learn a repo's shape, call docs_tree first, then scope this with a `path_glob` once you know where to look. Paged under the uniform list contract: `limit` (default 200) caps rows, `cursor` (from a truncated page) resumes after the last row returned, `budget_tokens` caps the page's estimated size (at least one row is always returned), and `truncated` is honest — when true you have NOT seen everything. For structured/ranked discovery use `query` or `blocks_resolve`."#;
/// `query_syntax`
pub const QUERY_SYNTAX: &str = r#"query_syntax — The full OQX reference: clause order, targets and fields, scalar semantics, nested queries and the root row, follow, paging, worked examples.
{} no arguments; returns { syntax }
Call it before writing a non-trivial query; every other tool's examples end here, at query_syntax."#;
/// `query`
pub const QUERY: &str = r#"query — rows (id, path, projection) from docs, blocks, nodes or links.
{"query":"select title, stage from docs where \"process\" in tags order by path limit 5"} frontmatter; membership; order, limit
{"query":"select type, text from blocks where path == \"/processes/calcination.md\""} a document's blocks in order
{"query":"select block_id from nodes where kind == \"md:section\" and name == \"Tasks\" and path == \"/processes/calcination.md\""} a heading's block id
{"query":"select path, value from nodes where kind == \"md:task\" and not checked"} open tasks
{"query":"select path, block_id from links where dst_path == \"/processes/calcination.md\""} inbound links (path = source)
{"query":"select path from docs where title ~* \"salt\""} regex (~* ci, !~ not); substring: .contains(\"s\")
Also docs count { where … }, follow, ^docs joins, text()/semantic(): query_syntax."#;
/// `graph`
pub const GRAPH: &str = r#"Neighborhood macro: the bounded graph AROUND one or more root documents in ONE call — { documents, links, frontier } — so you can orient without hand-writing an OQX `follow`. This is a CONVENIENCE WRAPPER, not a new engine: it compiles its args into an OQX `follow out_links collect { from dst }` / `follow in_links collect { from doc }` query and runs it through the same `query` path (the returned `queries` field is the exact follow query it generated). It does NOT replace `follow` or `from links` — reach for the `query` tool directly when you need successor/frontier predicates, `$key`-keyed identity, `$ordinal` budgets, cross-document correlation, or the links scan with its own projections. `roots` are one or more doc refs (paths and/or ids); they are depth 0. `degrees` is the max hop distance (root = 0; maps to `follow … collect { … depth degrees+1 }`, capped so degrees+1 ≤ 8; default 1). `direction` is `out` (outgoing links), `in` (backlinks), or `both` (default). `predicate` restricts the neighborhood to documents reachable from a root via edges of that predicate (both the documents and the edges are filtered). `select` adds document projections (OQX select expressions, e.g. "layer", "path"). `max_documents` caps the distinct document set (default 200) with a `truncated` flag. Returns: `documents` (each with `id`/`path`/`degree` = min hops from a root/`frontier` bool + your projections — a bare-name selection such as "title" is its own key, `id`/`path` land on the envelope keys, and any other expression is `sel_<i>`), `links` (the traversed links as rows of the `links` target under its own field names — `id`, `doc_id`/`block_id` (the source), `via_id`, `dst_id`, `dst_path`, `uri`, `dst_kind`, `predicate`, `provenance`, `anchor`, `field`; external `x_…` and dangling phantom endpoints are preserved as edge stubs, exactly as `from links` surfaces them), and `frontier` (the documents on the outer boundary — `{ id, path, degree }`, min degree == degrees; at degrees 0 that is the roots themselves)."#;
/// `blocks_search`
pub const BLOCKS_SEARCH: &str = r#"Full-text (FTS5, bm25-ranked) keyword search over block text: `{ hits: [{ id (the block), doc_id, path, type, text, score }], truncated }`. Input is treated as a search box — plain words are ANDed, "quoted phrases" match adjacency, punctuation like / is safe (no query DSL). For structured filtering or frontmatter projection use `query` instead (see query_syntax). (text_search before surface 3.0.)"#;
/// `blocks_resolve`
pub const BLOCKS_RESOLVE: &str = r#"blocks_resolve — "the id of the thing I mean": a title or term, ranked to blocks { id, locator, preview, evidence }.
{"query":"calcination"} best on short, entity-shaped input
{"query":"Emerald Tablet","limit":3} use the returned id in the next call (docs_read, a plan target)
Open questions or passages: query with text("...") or order by semantic("...") desc. Grammar: query_syntax."#;
/// `apply`
pub const APPLY: &str = r###"apply — plan and commit in one call; returns the Receipt { plan_id, commit, revisions, new_ids, committed, summary }.
{"effects":[{"op":"set","target":"/substances/salt.md","field":"frontmatter.verified","value":false}],"reason":"re-check salt"} one scalar; a bare path is a document target
{"effects":[{"op":"insert","into":"/lab/2026-02-notes.md","at":"end","markdown":"## Follow-ups\n\n- [ ] re-run cycle 4"}]} append at a document's end
{"select":"from blocks where type == \"task\" and not checked and path == \"/processes/calcination.md\"","effects":[{"op":"set","field":"attrs.checked","value":true}],"expect":{"max":10}} bulk check-off; expect.max is REQUIRED with select
Plan first for anything you cannot undo by hand; every error's data.fix is a corrected call to copy. Grammar: query_syntax."###;
/// `plan`
pub const PLAN: &str = r##"plan — Preview a write: the Plan (summary, $new.n ids) burns no id; execute next. diffs: true for file bodies.
{"effects":[{"op":"set","target":"/processes/calcination.md","field":"frontmatter.verified","value":false}]} path = doc
{"effects":[{"op":"insert","into":{"kind":"section","heading_id":"b_153"},"markdown":"- [ ] Weigh the calx"}]} in a section
{"effects":[{"op":"create","path":"lab/march.md","markdown":"# March\n"},{"op":"insert","into":"$new.0","markdown":"Hi."}]} fill it: $new.0
{"select":"from docs where stage == \"nigredo\"","effects":[{"op":"set","field":"frontmatter.ok","value":true}]} no target: the hit
{"select":"from docs where type == \"lab-note\"","effects":[{"op":"insert","into":"-> `/processes/${subject}.md`","markdown":"-> `[${title}](${path})`"}]} -> reads the row
Ops: create insert set unset move remove split merge retarget; grammar: query_syntax."##;
/// `execute`
pub const EXECUTE: &str = r#"execute — Commit exactly a plan from plan: the plan form (the whole Plan object) or the digest form (plan_id plus the same effects, re-planned and committed only if the digest still matches).
{"plan_id":"3ece126452106400210f07047029a961691b814252ba4437f5a5f1c3301629da","effects":[{"op":"set","target":"/processes/calcination.md","field":"frontmatter.verified","value":false}]} digest form: the plan_id plan returned for these effects
Or pass the whole object: {"plan": the Plan as returned}. A document that moved on fails stale_plan (retriable: call plan again). Grammar: query_syntax."#;
/// `blocks_insert`
pub const BLOCKS_INSERT: &str = r###"Insert new block(s) parsed from `markdown` under a parent, at a position. `to` names the parent: a BLOCK ref (id or locator) nests the new blocks under that block — it must be a CONTAINER (a list, list item, task, blockquote or table); a leaf such as a heading or paragraph fails `type_mismatch` (spec/mutate §1.1) — use blocks_append for "into this heading's section / this container" or `at: {after: <ref>}` for "right after this block"; or a DOCUMENT ref (doc id or path) places them at the document's top level — so appending a new section to a document is `{ to: "<path>", markdown: "## New\n\n…" }` (the positional generalization of docs_append). `at` places among the parent's children: "end" (default) / "start" / {before|after: <block ref>}. Optional `expect.parent_children_hash` is the destination-parent CAS: the parent's CURRENT direct child ids (the document's top-level ids for a document `to`) joined by "," and sha256-hexed — compute it from docs_read include_ids (`ids` filtered by `parents`) — and the insert fails `stale_expectation` (with `data.current.parent_children_hash`) if the siblings changed under you; `content_hash` has no meaning here (there is no target block) and is not accepted. Sugar for `apply { effects: [{ op: "insert", into, at, markdown, expect? }] }` — returns the Receipt (`new_ids` holds the inserted blocks' ids, `$new.0` first); preview with `plan` over that effect."###;
/// `blocks_update`
pub const BLOCKS_UPDATE: &str = r#"Replace a block's markdown and/or set a task's `checked` state, with compare-and-swap. `id` is the block's id (or any ref resolve accepts); `expect.content_hash` is pinned to the block's current bytes server-side when omitted (protects against a concurrent edit). `markdown` may parse to SEVERAL sibling blocks (e.g. a paragraph followed by a list): the target keeps its id and takes the first, the rest are inserted right after it with fresh ids — the Receipt's `new_ids` lists them (`$new.0`, …). A list item may likewise be replaced by a multi-item list (`- a\n- b`), yielding sibling items. Task state is settable flat as `checked` (the same name reads flatten to — `from blocks where checked`), synced to the `[ ]`/`[x]` in the raw; every OTHER block attr (heading `level`, code `lang`, list `ordered`) is derived from the markdown, so set those by editing `markdown` (the 3.x `attrs` bag is retired and refused by name). Sugar for `apply { effects: [{ op: "set", target: { kind: "block", id }, field: "markdown" | "attrs.checked", value, expect? }] }` (both when both are given) — returns the Receipt; preview with `plan`."#;
/// `blocks_move`
pub const BLOCKS_MOVE: &str = r#"Move block(s) under a new parent at a position. `blocks` are block refs; `to` (the parent) is a block ref (a container — a leaf parent is `type_mismatch`, as for blocks_insert), or the blocks' OWN document (id or path) to move them to its top level — moving to ANOTHER document's root is not expressible (target_missing): anchor on a block in that document with `at` {before|after} instead. `at` is "end"/"start"/{before|after: <ref>}. Optional `expect.parent_children_hash` is the destination-parent CAS (as blocks_insert: the destination parent's CURRENT direct child ids joined by ",", sha256 hex), checked once for the whole run before anything moves — `stale_expectation` with `data.current.parent_children_hash` if the destination's children changed; `content_hash` is meaningless here and not accepted. Sugar for `apply { effects: [{ op: "move", targets, into, at, expect? }] }` — returns the Receipt; preview with `plan`."#;
/// `blocks_remove`
pub const BLOCKS_REMOVE: &str = r#"Remove block(s) (the resurrection pool catches regret). `blocks` are refs; removing a block removes its whole subtree, and a set that names both a container and some of its descendants (e.g. every id of a section straight from docs_read include_ids) is fine — it collapses to the top-most blocks. Sugar for `apply { effects: [{ op: "remove", targets }] }` — returns the Receipt; preview with `plan` (its `preview.results[0].removed` lists everything that would leave). To delete a whole document use docs_delete."#;
/// `blocks_split`
pub const BLOCKS_SPLIT: &str = r#"Split a block at UTF-8 byte offset(s) into consecutive blocks. `id` is the block's id (or a ref); `at` is a list of integer byte offsets into the block's raw bytes (spec/mutate §2.5; an offset inside a multi-byte character rounds down to its start). CAS is pinned to the block's current bytes server-side. Sugar for `apply { effects: [{ op: "split", target, at }] }` — returns the Receipt (`new_ids` holds the new siblings); preview with `plan`."#;
/// `blocks_merge`
pub const BLOCKS_MERGE: &str = r#"Merge adjacent blocks into the first, joined by `separator` (default a blank line). `blocks` are refs (≥2). Sugar for `apply { effects: [{ op: "merge", targets, separator? }] }` — returns the Receipt; preview with `plan`."#;
/// `blocks_check`
pub const BLOCKS_CHECK: &str = r#"Macro: check (or uncheck with checked:false) the given task blocks. `ids` are the blocks' ids (or refs). Sugar for `apply { effects: [{ op: "set", target: { kind: "block", id }, field: "attrs.checked", value }, …] }`, one per block, each CAS-pinned — returns the Receipt; preview with `plan` (tasks_complete { blocks } before surface 3.0)."#;
/// `nodes_set`
pub const NODES_SET: &str = r#"Macro: surgically set one editable property of a projected node (e.g. a link's `name` text or `value` target, a task's `checked`). `id` is the node's id (from `query` over `from nodes` — the one tool that addresses `nodes` rows), `prop` the property, `value` its new value. Resolves the node to its block and rewrites only that node's span. Nodes are read-only projections — this is the affordance to edit what a node represents without hand-rewriting the block. Errors node_not_editable (with the editable prop list) when the kind/prop has no editor. Sugar for `apply { effects: [{ op: "set", target: { kind: "node", id }, field: prop, value }] }` — returns the Receipt; preview with `plan` (node_set before surface 3.0)."#;
/// `blocks_append`
pub const BLOCKS_APPEND: &str = r#"Macro: append markdown INTO a block, by what the block is. `id` is a block's id (or a ref). A HEADING: the `text` is inserted at the end of its section range (before the next heading of equal or higher level) — `insert into { kind: "section", heading_id }`. A CONTAINER (a list, list item, task, blockquote or table — anything the kernel accepts as a parent): inserted at the end of its children (`insert into { kind: "block", id } at end`). A LEAF (a paragraph, code fence, …) is `block_not_container` — append means "into", never a sibling insert; place after it with `blocks_insert { to: <its parent>, at: { after: <id> } }`. Sugar for that one `insert` effect — returns the Receipt; preview with `plan`. To append at a whole document's end, or inside a section named by its heading TEXT, use docs_append."#;
/// `docs_append`
pub const DOCS_APPEND: &str = r###"Macro: append markdown to a document — at its END, or at the end of one of its sections when `section` names a heading by its TEXT (`"Setup"`; an ATX spelling `"## Setup"` is accepted). The journal/log/running-note primitive. ADDITIVE and identity-preserving, NOT a whole-body replace: the `text` is parsed into blocks and inserted as NEW blocks after the existing ones (at the document's top level, or inside the section's range), so every existing block keeps its stable `b_` id (omgbase deliberately has no whole-body `docs_put` — see mcp-api). Sugar for one `insert at end` effect into the document or the resolved section — returns the Receipt (`new_ids` the appended blocks); preview with `plan`. Address the document by `id` or `path` (at least one). The document must already exist — a missing doc errors `doc_missing` (creating one is docs_create's job, never this); a `section` matching no heading in it is `parent_missing`, one matching several is `ambiguous_heading` with `candidates[{ id, doc_id }]` — pass that heading's id to blocks_append, which appends into a block by its id."###;
/// `links_retarget`
pub const LINKS_RETARGET: &str = r#"Macro: rewrite ONE link destination everywhere it is linked — the single-pair form of links_repair (same matching rules: whole link destinations only, leading `/` optional, fragments preserved; prose/inline code/code fences untouched). Scope source docs with `path_glob`. COMMITS: sugar for `apply { effects: [{ op: "retarget", repairs: [{ from, to }], path_glob? }] }`; returns `hits[{id,path,oldRaw,newRaw}]` (the rewritten block and its document) and `pairs[{from,to,hits}]` plus the Receipt (`commit` null when nothing matched). To PREVIEW first, call `plan` with that one effect: its `preview.retarget[0]` carries the same `hits` and `pairs` and `preview.diffs` the files, and it fails exactly where the commit would (through surface 3.x this tool was a dry run by default, with `applied`)."#;
/// `links_stale`
pub const LINKS_STALE: &str = r#"READ-ONLY link health: surfaces DANGLING internal links — links whose target path has NO live document (stored as a `phantom:` edge; a doc created at that path auto-resolves them). Returns `stale[]` (each a link row under the `links` target's names: `doc_id`, `path` (the source document), `block_id` (null for a frontmatter edge), predicate, provenance, dst_kind, `target` = the canonical missing path, `/`-rooted like every path the surface returns (e.g. `/guides/old.md`), `authored` = the destination text exactly as written in the source block (e.g. `/guides/old.md#Setup`; null for frontmatter edges), `anchor`, and reason `dangling_doc`), plus `externalCount` (http(s) links — UNVERIFIABLE here, never marked broken, since reachability needs network I/O the engine won't do), `totalOpenEdges`, and `truncated` (capped by `limit`, default 500). `summary:true` returns counts only — `staleCount`, `byTarget[{target,count}]`, `bySource[{path,count}]`, `externalCount`, `totalOpenEdges` — for a repo-wide audit in one small call. Scope the SOURCE docs with `path_glob` (e.g. "journal/*"; `*` matches across `/`). Fix the reported targets with `links_repair` (batch) or `links_retarget` (single): either `target` or `authored` works as `from`. Anchors (#heading/^ref) into an existing doc are NOT verified in v1. Contrast docs_read/query which answer 'what does this doc say', not 'which of its links are broken'."#;
/// `links_repair`
pub const LINKS_REPAIR: &str = r#"Macro: BULK stale-link repair — rewrite one or MANY LINK DESTINATIONS in ONE changeset. Pass `repairs` (an array of {from,to}) to fix several dangling targets — e.g. those surfaced by links_stale — at once; a single {from_target,to_target} pair is also accepted. MATCHING: `from` must be the WHOLE destination of a Markdown link/image `[t](dest)`, a wikilink `[[dest]]`/`[[dest|alias]]`, or a bare-path inline field `key:: /dest` — a leading `/` is optional on either side (so links_stale's `target` or `authored` both work), and a trailing `#heading`/`^ref` fragment on the link is kept and re-appended to `to`. NOT rewritten: prose mentions, inline code, `code_fence` blocks, longer paths that merely contain `from`, and frontmatter values (use docs_set_meta). Scope source docs with `path_glob` (as in links_stale). Ops coalesce to one update per TOP-MOST block (a list and its items count once), so a batch never trips over its own re-minted children. COMMITS: sugar for `apply { effects: [{ op: "retarget", repairs, path_glob? }] }`; returns `hits[{id,path,oldRaw,newRaw}]` (the rewritten block and its document) and `pairs[{from,to,hits}]` (per-pair destination counts; 0 = nothing matched) plus the Receipt (`commit` null when nothing matched). To PREVIEW first, call `plan` with that one effect — its `preview.retarget[0]` carries the same `hits`/`pairs`, `preview.diffs` the files — and it fails exactly where the commit would (through surface 3.x this tool was a dry run by default, with `applied`)."#;
/// `docs_create`
pub const DOCS_CREATE: &str = r#"Create a new document at `path` (the new document's path, either form) from complete file bytes (`markdown`), with optional structured `frontmatter`. Sugar for `apply { effects: [{ op: "create", path, markdown, frontmatter? }] }` — returns the Receipt: `revisions[0]` is the created document (its `id`, rooted `path`, `hash`), `new_ids["$new.0"]` the same id and `$new.1…` its blocks in pre-order. Fails path_taken if it already exists. Preview with `plan` over that effect (`preview.diffs[path]` is `"" → the bytes`)."#;
/// `docs_move`
pub const DOCS_MOVE: &str = r#"Rename a document to a new path (`to_path`, `/`-rooted or bare); the document is addressed by `id` or `path`. Block identity and history are preserved, and the inbound links FOLLOW it: every link written against the old path (Markdown links/images, wikilinks, bare-path inline fields — anchors, link text, titles and code spans preserved, absolute vs relative style kept) is rewritten to the new path in the same call as one CAS-checked commit, so a move never leaves the graph broken. Fails path_taken if the destination exists. Sugar for `apply { effects: [{ op: "move", target: { kind: "document", id }, to, retarget_inbound? }] }` — returns the Receipt (one revision per rewritten source; the moved document's own revision does not advance unless it links to itself) plus `retargeted` (the touched blocks/docs) and `dangling` — what could NOT be rewritten, i.e. frontmatter relations (block null; fix with docs_set_meta). Pass `retarget_inbound:false` to opt out for the rarer intent that the old path become unbound: the links are left as written, listed in `dangling` ({doc_id, path, block_id, target, anchor, field?} per occurrence — a link row under the `links` target's names) and reported by links_stale (their edges become `phantom:<old path>`); links already written against the new path start resolving to this doc either way. Preview with `plan` over that effect: `preview.diffs` holds the old path emptied, the new path filled and the rewritten sources."#;
/// `docs_delete`
pub const DOCS_DELETE: &str = r#"Delete a document: tombstone it and its live blocks (resurrection-poolable) and remove the file. Address it by `id` or `path`. Sugar for `apply { effects: [{ op: "remove", target: { kind: "document", id } }] }` — returns the Receipt (a tombstone advances no revision: `revisions` is empty, `commit` the one commit). Preview with `plan` over that effect (`preview.diffs[path]` is `the bytes → ""`)."#;
/// `docs_set_meta`
pub const DOCS_SET_META: &str = r#"Surgical frontmatter patch: set the given keys and/or unset named keys on the document addressed by `id` or `path`. Other frontmatter and the whole body are preserved (every block keeps its id). Sugar for `apply { effects: [{ op: "set", target: { kind: "document", id }, field: "frontmatter.<key>", value }, { op: "unset", …, field: "frontmatter.<key>" }, …] }`, one effect per key — returns the Receipt; at least one of `set`/`unset` is required. Preview with `plan` over those effects (`preview.diffs[path]` is the file before → after)."#;
/// `docs_update`
pub const DOCS_UPDATE: &str = r#"Whole-document update with smart identity preservation — sugar for `apply { effects: [{ op: "set", target: { kind: "document", id }, field: "content", value: content }] }` (surface 4.0). Submit the complete proposed `content` for a document (`id` or `path`); the engine reconciles it against the current tree, preserving stable block ids for structure that is recognizably the same (edits, moves, reorders), minting for new structure, and tombstoning removals — frontmatter changes included — and commits the derived ops as ONE transaction. Returns the Receipt `{ plan_id, commit, revisions: [{ id, path, hash }], new_ids, committed: true }`; content that already equals the document is a plan with nothing to do — no commit (`commit: null`, no revision). To INSPECT the identity consequences before committing, call `plan { effects: [that set] }`: its `preview.identity[<doc id>]` is `{ matcher_v, summary: { preserved, updated, moved, created, removed, split, merged, ambiguous }, ops: [{ op_index, disposition, blocks, confidence, reason }], diagnostics }` — each planned kernel op (`preview`'s `op_index` into the plan's `opset.ops`) annotated with the disposition it realizes — and the plan's `preconditions.docs` pin the document's revision and hash; then `execute` that plan, or call this tool. A document that changed since planning fails stale_plan (re-plan). Content the planner cannot reproduce byte-for-byte fails plan_not_convergent; the message names the first divergent byte and the proposed block (index/type) that failed to round-trip (full diagnostics in `data.diagnostics`)."#;
/// `observe`
pub const OBSERVE: &str = r#"SYNC INGEST (ADR-014): record `content` as the current authoritative bytes for `path`, committed as an OBSERVED-origin revision — a write *around* the engine, the way a human/external edit is recorded. Contrast docs_update, which is api-origin (a write *through* the engine, agent intent); both reconcile the new bytes against the current block tree and preserve stable ids, but the origin differs (and thus the change-feed semantics and matcher path). This is the file→DB direction for an out-of-process synchronizer: it never writes a file, so it works on a headless/sourceless server with no working tree. Idempotent: bytes whose hash already equals the stored revision are an ECHO — no commit (`echo:true`, `rev:null`). Bytes with git conflict markers are still ingested (opaque) and the doc is flagged (`conflicted:true`). To mirror a deletion, use `observe_delete` (an observed, resurrection-pooled tombstone — not `docs_delete`, which is an intentional api-origin removal that also unlinks the file). Returns `{ id, path, rev, commitId, converged, echo, conflicted, dispositions }` — the document's row attributes plus a disposition summary (how identity threaded). `path` is a FILE event's path from the store's layer, not a row locator, so it keeps its name."#;
/// `observe_many`
pub const OBSERVE_MANY: &str = r#"BATCH sync ingest (ADR-014): observe several files at once — the batch form of `observe`. Give `files` as an array of `{path, content}`; each is echo-gated and reconciled independently, all under one timestamp and a single resurrection-pool sweep (cheaper than N `observe` calls for an initial walk or a large checkpoint). Returns one result per input file (same shape as `observe`). Mirror deletions with `observe_delete`, one path at a time."#;
/// `observe_delete`
pub const OBSERVE_DELETE: &str = r#"SYNC DELETE (ADR-014): record that `path` left the source scope — tombstone the live doc as an OBSERVED deletion (its blocks are pooled for resurrection if the path reappears; no file is removed, since it is already gone from the source). The deletion counterpart to `observe`, for a synchronizer mirroring an external delete. Idempotent: a path with no live doc is a no-op (`deleted:false`). Contrast `docs_delete` — an api-origin, intentional, non-pooled removal that also unlinks the working-tree file."#;
/// `blocks_history`
pub const BLOCKS_HISTORY: &str = r#"A block's biography: the commits that CHANGED the block `id` names — inserted, edited, moved, split/merge lineage, resurrected — with disposition kind/confidence/reason, newest first. A commit that rewrote the document but left this block byte-identical in place is not listed (store 14.2: `same` verdicts are not persisted; through 14.1 every commit to the document appeared here), so the newest entry's `ts` is the block's `updated_at`; to learn whether the block was present in some other commit, diff two revisions with docs_diff (history_node before surface 3.0 — it always took a block id and returned the block's changes). Not repo-scoped: block ids are global."#;
/// `docs_diff`
pub const DOCS_DIFF: &str = r#"Block-grain diff between two revisions of a document: `[{ kind: added|removed|changed, id (the block), before?, after? }]` in that order. Address the document by `id` or `path` (at least one); `from_rev`/`to_rev` are revision ids (from docs_history, changes_since or blocks_history). Contrast docs_diff_unified, the unified text diff. (diff before surface 3.0.)"#;
/// `docs_diff_unified`
pub const DOCS_DIFF_UNIFIED: &str = r#"Unified diff between two revisions of a document (the `omg diff` rendering): returns `{ id, path, from, to, diff }` where `diff` is a unified diff of the two revisions' file texts — for each revision exactly the bytes docs_read_at returns, so hunk line numbers are real file lines (Myers shortest edit script; `@@ -a,b +c,d @@` hunks with 3 lines of context; lines prefixed `-`, `+` or a space; no file header; identical texts → ""). Address the document by `id` or `path` (at least one). `from_rev`/`to_rev` default to the previous and current revisions ('what did the last commit change here'). Contrast `docs_diff`, which returns block-grain added/removed/changed entries. (diff_unified before surface 3.0.)"#;
/// `docs_read_at`
pub const DOCS_READ_AT: &str = r#"TIME-TRAVEL read: the whole file bytes of a document AS OF a past revision — `raw` is the reconstructed source at that `rev` (fences/tables/list markers preserved), plus `id`/`path` (the path as of that revision)/`rev`. Sugar for `docs_read { id | path, at: rev }` (surface 4.7) — the same result, byte for byte. Use this to audit or answer 'what did this doc say at revision N', or to fetch the exact prior text before reverting. Get a `rev` from `query` over the `revisions` target (`from revisions where path == … order by seq`), docs_diff, blocks_history, docs_history or changes_since. Contrast: docs_read = current bytes; docs_diff = block-grain changes BETWEEN two revisions. `renderedHashMatch` is true when the reconstruction is byte-for-byte the file at that revision; it can be false for an old revision ONLY when the document's leading/frontmatter trivia (separator bytes, not block content) changed since — block content always reconstructs faithfully. No `properties`: the property table is current-state only (through 4.6 the current bag rode along flagged propertiesAreCurrent). Address the document by `id` or `path` plus a `rev` id."#;
/// `docs_history`
pub const DOCS_HISTORY: &str = r#"VERSION HISTORY of matching documents, grouped BY DOCUMENT: for each doc (`id`, `path`, `deleted`, `currentRev`), its ordered revision list (oldest→newest) with `rev`/`seq`/`commit`/`updated_at`/`origin`/`actor`/`hash`/`isCurrent`. Give `path_glob` (e.g. "journal/*" — `*` matches ACROSS `/`, so journal/* and journal/** are equivalent) OR a single document by `id` or `path`; at least one is required. `hash` (hex of the revision's rendered file hash) lets you spot no-op vs real changes. Feed a returned `rev` into `docs_read_at` (whole doc AT that rev) or `docs_diff` (block-grain changes BETWEEN two revs). By default only live docs; `include_deleted:true` also returns tombstoned docs (their history is intact for audit). `limit` caps DOCUMENTS returned (default 50) with a `truncated` flag. Contrast `changes_since`: a repo-wide COMMIT feed, not this per-document view."#;
/// `changes_since`
pub const CHANGES_SINCE: &str = r#"changes_since — Re-orient after time away: the commit feed after a cursor, each digest with its revisions [{ id, path, hash }].
{} everything from the start; head is the current max seq
{"cursor":20,"limit":5} commits after seq 20, five at a time (cursor pages while truncated)
{"origin":"api"} only the writes made through the engine (observed = edits on disk; import)
Poll with your last cursor: an empty page with cursor == head means nothing new. Read a changed document with docs_read, its blocks with query (grammar: query_syntax)."#;
/// `repos_status`
pub const REPOS_STATUS: &str = r#"Repo counts (`{ id, slug, rootPath, docs, blocks, commits, openEdges, unconverged, disk }`): docs, blocks, commits, open edges, unconverged doc count, and on-disk drift. `disk` reports a READ-ONLY scan of the working tree vs the DB: `changed` (files edited on disk but not re-ingested), `deleted` (docs whose file is gone from disk), `untracked` (new *.md not yet ingested), and `checked` (false when the server has no working-tree path — disk agreement is then UNVERIFIED, not clean)."#;
/// `sync_status`
pub const SYNC_STATUS: &str = r#"Watcher/sync state: last commit seq, last checkpoint, and whether the repo is convergent. `convergent` is true ONLY when the DB is internally converged AND a working-tree scan ran (`diskChecked`) AND found no drift (`disk.changed`/`deleted`/`untracked` all 0). If the server has no working-tree path, `diskChecked` is false and `convergent` is false — a green light is never shown while disk freshness is unverified, so an agent can trust `convergent: true` to mean not stale."#;
/// `repos`
pub const REPOS: &str = r#"List the repos in this workspace: `{ repos: [{ slug, hasSource }] }`. A client addresses any of them by passing that `slug` as the `repo` argument to another tool (omit `repo` ⇒ the server's default repo). Use repos_status for per-repo counts/convergence."#;
/// `version`
pub const VERSION: &str = r#"version — Which engine and which versions you are talking to, and which tools profile it serves.
{} engine, version, components, specs, schema, mcp, runtime, commit, built and profile ("full" or "minimal")
Under the minimal profile ten tools are listed; calling an unlisted one is the JSON-RPC error unknown tool. Reads go through query (grammar: query_syntax)."#;