@omgbase/core 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +10 -10
  2. package/dist/src/core/read/document.d.ts +18 -5
  3. package/dist/src/core/read/document.js +21 -7
  4. package/dist/src/core/read/nodes.d.ts +7 -7
  5. package/dist/src/core/read/nodes.js +5 -3
  6. package/dist/src/core/read/reader.d.ts +11 -7
  7. package/dist/src/core/read/reader.js +8 -10
  8. package/dist/src/format/adapter.d.ts +1 -1
  9. package/dist/src/graph/history.d.ts +20 -14
  10. package/dist/src/graph/history.js +12 -12
  11. package/dist/src/graph/inbound-links.d.ts +4 -4
  12. package/dist/src/graph/inbound-links.js +4 -4
  13. package/dist/src/graph/link-health.d.ts +8 -8
  14. package/dist/src/graph/link-health.js +6 -6
  15. package/dist/src/graph/links.d.ts +3 -3
  16. package/dist/src/graph/links.js +4 -4
  17. package/dist/src/index.d.ts +2 -1
  18. package/dist/src/index.js +2 -1
  19. package/dist/src/mcp/arguments.d.ts +27 -0
  20. package/dist/src/mcp/arguments.js +147 -0
  21. package/dist/src/mcp/errors.d.ts +1 -1
  22. package/dist/src/mcp/graph.d.ts +16 -9
  23. package/dist/src/mcp/graph.js +58 -35
  24. package/dist/src/mcp/reference.d.ts +1 -1
  25. package/dist/src/mcp/reference.js +215 -121
  26. package/dist/src/mcp/server.d.ts +9 -1
  27. package/dist/src/mcp/server.js +359 -268
  28. package/dist/src/mutate/apply.d.ts +11 -4
  29. package/dist/src/mutate/apply.js +3 -2
  30. package/dist/src/mutate/docs.js +7 -7
  31. package/dist/src/mutate/macros.d.ts +4 -1
  32. package/dist/src/mutate/macros.js +1 -1
  33. package/dist/src/mutate/ops.js +2 -2
  34. package/dist/src/mutate/opset.d.ts +8 -5
  35. package/dist/src/mutate/plan-update.js +9 -8
  36. package/dist/src/oqx-js/context.d.ts +17 -4
  37. package/dist/src/oqx-js/context.js +268 -131
  38. package/dist/src/oqx-js/planner.d.ts +3 -3
  39. package/dist/src/oqx-js/planner.js +14 -15
  40. package/dist/src/oqx-js/root-row.d.ts +10 -3
  41. package/dist/src/oqx-js/root-row.js +18 -8
  42. package/dist/src/oqx-js/run.d.ts +6 -0
  43. package/dist/src/oqx-js/run.js +71 -24
  44. package/dist/src/oqx-js/sql/scan.js +7 -7
  45. package/dist/src/oqx-js/sql/translate.d.ts +12 -9
  46. package/dist/src/oqx-js/sql/translate.js +183 -75
  47. package/dist/src/oqx-js/store-index.d.ts +1 -1
  48. package/dist/src/oqx-js/store-index.js +28 -26
  49. package/dist/src/search/cel/ast.d.ts +1 -1
  50. package/dist/src/search/cel/compile.d.ts +1 -1
  51. package/dist/src/search/cel/compile.js +43 -70
  52. package/dist/src/search/cel/parser.js +1 -1
  53. package/dist/src/search/resolve.d.ts +2 -0
  54. package/dist/src/search/rrf.js +2 -2
  55. package/dist/src/search/text.d.ts +2 -2
  56. package/dist/src/search/text.js +1 -1
  57. package/dist/src/spec-versions.d.ts +6 -6
  58. package/dist/src/spec-versions.js +5 -5
  59. package/dist/src/surface-paths.d.ts +41 -8
  60. package/dist/src/surface-paths.js +34 -18
  61. package/dist/src/sync/admin.d.ts +2 -1
  62. package/dist/src/sync/admin.js +1 -1
  63. package/package.json +2 -2
package/README.md CHANGED
@@ -51,16 +51,16 @@ const repoId = ensureRepo(ws.store, "notes", root); // repo row + an `fs` sour
51
51
  const sweep = freshnessSweep(ws.store, repoId, root); // ingest new/changed *.md as one checkpoint
52
52
  sweep.ingested; // ["todo.md"]
53
53
 
54
- // Query with OQX. Nodes are projections of blocks; $block_id is the anchor block.
54
+ // Query with OQX. Nodes are projections of blocks; block_id is the anchor block.
55
55
  const open = oqxRun(ws.store, repoId,
56
- 'select block: $block_id from nodes where kind == "md:task" && !attrs.checked');
57
- open.hits; // [{ id: "n_…", path: "todo.md", block: "b_…" }]
56
+ 'select block: block_id from nodes where kind == "md:task" && !attrs.checked');
57
+ open.hits; // [{ id: "n_…", path: "/todo.md", block: "b_…" }]
58
58
 
59
59
  // Read a whole document back byte-exact, with block ids + CAS hashes.
60
60
  const doc = findDoc(ws.store, { repoId, path: "todo.md" });
61
61
  const read = docsRead(ws.store, doc.docId, { includeIds: true });
62
- read.content; // "# Todo\n\n- [ ] write the README\n- [x] ship 0.1\n"
63
- read.ids; // ["b_…", "b_…", "b_…", "b_…"] read.hashes[id] → content_hash
62
+ read.raw; // "# Todo\n\n- [ ] write the README\n- [x] ship 0.1\n"
63
+ read.ids; // ["b_…", "b_…", "b_…", "b_…"] read.hashes[id] → the block's hash (the expect.content_hash value)
64
64
 
65
65
  // Mutate. The macro expands to kernel `update` ops pinned by content_hash;
66
66
  // apply() renders, writes the file, and commits a new revision.
@@ -71,7 +71,7 @@ const result = apply(ws.store, {
71
71
  ops: tasksComplete(ws.store, open.hits.map((h) => h.block)),
72
72
  origin: { actor: "agent:quickstart", reason: "demo" },
73
73
  });
74
- result; // { results: [{ ids: ["b_…"] }], revisions: [{ doc: "d_…", path: "todo.md" }], committed: true }
74
+ result; // { results: [{ ids: ["b_…"] }], revisions: [{ id: "d_…", path: "todo.md", hash: "…" }], committed: true }
75
75
  // todo.md on disk now reads "- [x] write the README"
76
76
 
77
77
  historyNode(ws.store, open.hits[0].block); // [{ origin: "import", kind: "edited", … }, { origin: "observed", kind: "inserted", … }]
@@ -126,7 +126,7 @@ server in your own process with `buildServer(ctx)` and any MCP SDK transport.
126
126
  3. A doc is an ordered tree of **blocks** (`b_`): paragraphs, headings, list items, fences, YAML entries, JSON properties…
127
127
  4. Block ids are minted once and threaded through re-ingests by the reconciler, so they survive edits and moves.
128
128
  5. Blocks are the **mutation anchors**: every kernel op addresses `b_` ids; `expect.content_hash` (sha256 of the block's raw bytes) is the CAS token.
129
- 6. **Nodes** (`n_`) are semantic projections of blocks — `md:task`, `md:heading`, `md:section`, `md:link`, `md:wikilink`, `md:anchor`, `md:inline_field` — queryable but derived; `$block_id` points back at the anchor.
129
+ 6. **Nodes** (`n_`) are semantic projections of blocks — `md:task`, `md:heading`, `md:section`, `md:link`, `md:wikilink`, `md:anchor`, `md:inline_field` — queryable but derived; `block_id` points back at the anchor.
130
130
  7. **Edges** are typed links between blocks/docs (wikilinks, `$ref`, frontmatter relations) with commit intervals.
131
131
  8. Every ingest or mutation appends a **commit** (`c_`) and a per-doc **revision** (`r_`) whose rendered hash equals the file's bytes.
132
132
  9. Commits carry `origin` (`observed` from disk, `import` from `apply`) and an actor; `historyNode`/`changesSince` read them back.
@@ -144,11 +144,11 @@ and `semantic("…")` (embedding score) are omgbase additions.
144
144
 
145
145
  ```js
146
146
  oqxRun(ws.store, repoId, `
147
- select $path, $title, open: nodes collect { select $block_id where kind == "md:task" && !attrs.checked }
147
+ select path, title, open: nodes collect { select block_id where kind == "md:task" && !attrs.checked }
148
148
  from docs where nodes count { where kind == "md:task" && !attrs.checked } >= 2
149
- order by $path limit 10
149
+ order by path limit 10
150
150
  `);
151
- // hits: [{ id: "d_…", path: "more.md", $path: "more.md", $title: "More", open: [{ $block_id: "b_…" }, …] }]
151
+ // hits: [{ id: "d_…", path: "/more.md", title: "More", open: [{ block_id: "b_…" }, …] }]
152
152
  ```
153
153
 
154
154
  See [`docs/query-language.md`](https://github.com/omgbase/omgbase/blob/main/docs/query-language.md)
@@ -1,13 +1,15 @@
1
1
  import type { Database } from "better-sqlite3";
2
2
  import type { Store } from "../store/store.js";
3
+ import { type PropertySource } from "../store/properties.js";
3
4
  export interface DocsReadResult {
5
+ id: string;
4
6
  /** The reference form (`/a/b.md`, spec/surface §1 "Paths"). */
5
7
  path: string;
6
- docId: string;
7
8
  rev: string | null;
8
9
  /** Properties grouped by source: { frontmatter, inline, computed }. */
9
10
  properties: Record<string, Record<string, unknown>>;
10
- content: string;
11
+ /** The reconstructed file bytes (spec/store §6.1) — the document's `raw`. */
12
+ raw: string;
11
13
  /** Block ids in document order, for follow-up edits (include_ids). */
12
14
  ids?: string[];
13
15
  /**
@@ -15,7 +17,7 @@ export interface DocsReadResult {
15
17
  * `ids` (include_ids). This is the exact value the raw `apply` kernel wants in
16
18
  * an update/split/merge/remove op's `expect.content_hash`, so a single read
17
19
  * yields both the stable ids AND the CAS tokens to mutate them — no follow-up
18
- * nodes_get_many(resolution:"full") hydration round trip.
20
+ * blocks_read_many(resolution:"full") hydration round trip.
19
21
  */
20
22
  hashes?: Record<string, string>;
21
23
  /**
@@ -36,6 +38,15 @@ export interface DocsReadOptions {
36
38
  * convergence check reloads through this) as well as from a Store.
37
39
  */
38
40
  export declare function reconstructContent(db: Database, docId: string): string | null;
41
+ /**
42
+ * The surface's `properties` (spec/surface §2, 3.0): the `spec/properties` §5
43
+ * grouped shape with the computed group's keys spelled as the attributes that
44
+ * read them — the store writes the computed rows under `$title`/`$tags`
45
+ * (spec/properties §3.3, unchanged), the surface has no `$` names, so
46
+ * `properties.computed` is `{ title, tags }`. Authored groups are untouched (an
47
+ * authored `$x:` key keeps its sigil, as `frontmatter.$x` reads it).
48
+ */
49
+ export declare function surfaceProperties(grouped: Record<PropertySource, Record<string, unknown>>): Record<string, Record<string, unknown>>;
39
50
  /** Read a whole document by id: full file bytes + properties. Null if missing. */
40
51
  export declare function docsRead(store: Store, docId: string, opts?: DocsReadOptions): DocsReadResult | null;
41
52
  /** Max doc refs honored per docsReadMany call (matches nodesGetMany's cap). */
@@ -66,11 +77,13 @@ export declare function docsReadMany(store: Store, repoId: string, refs: string[
66
77
  budgetTokens?: number;
67
78
  }): DocsReadManyResult;
68
79
  export interface DocsReadAtResult {
80
+ id: string;
81
+ /** The path as of that revision, in the reference form. */
69
82
  path: string;
70
- docId: string;
71
83
  /** The revision travelled to (the argument, echoed for confirmation). */
72
84
  rev: string;
73
- content: string;
85
+ /** The reconstructed file bytes at `rev` — the document's `raw` then. */
86
+ raw: string;
74
87
  /** True when the reconstructed bytes' sha256 matched the revision's stored
75
88
  * rendered_hash — a byte-faithful round-trip. False flags the fidelity gap
76
89
  * described below (doc-level trivia may differ for an old revision). */
@@ -41,6 +41,20 @@ export function reconstructContent(db, docId) {
41
41
  }
42
42
  return out.join("");
43
43
  }
44
+ /**
45
+ * The surface's `properties` (spec/surface §2, 3.0): the `spec/properties` §5
46
+ * grouped shape with the computed group's keys spelled as the attributes that
47
+ * read them — the store writes the computed rows under `$title`/`$tags`
48
+ * (spec/properties §3.3, unchanged), the surface has no `$` names, so
49
+ * `properties.computed` is `{ title, tags }`. Authored groups are untouched (an
50
+ * authored `$x:` key keeps its sigil, as `frontmatter.$x` reads it).
51
+ */
52
+ export function surfaceProperties(grouped) {
53
+ const computed = {};
54
+ for (const [k, v] of Object.entries(grouped.computed))
55
+ computed[k.startsWith("$") ? k.slice(1) : k] = v;
56
+ return { frontmatter: grouped.frontmatter, inline: grouped.inline, computed };
57
+ }
44
58
  /** Read a whole document by id: full file bytes + properties. Null if missing. */
45
59
  export function docsRead(store, docId, opts = {}) {
46
60
  const info = findDoc(store, { docId });
@@ -50,11 +64,11 @@ export function docsRead(store, docId, opts = {}) {
50
64
  if (content === null)
51
65
  return null;
52
66
  const result = {
67
+ id: info.docId,
53
68
  path: referencePath(info.path),
54
- docId: info.docId,
55
69
  rev: info.currentRev,
56
- properties: docPropertiesGrouped(store.db, docId),
57
- content,
70
+ properties: surfaceProperties(docPropertiesGrouped(store.db, docId)),
71
+ raw: content,
58
72
  };
59
73
  if (opts.includeIds) {
60
74
  const ids = [];
@@ -81,7 +95,7 @@ export function docsRead(store, docId, opts = {}) {
81
95
  // analog of docsRead: given several doc refs (each an id OR a path, via the
82
96
  // shared findDocByRef dispatch), it returns `items` (the full reads, identical
83
97
  // in shape to docsRead) alongside `errors` (per-ref misses). A miss NEVER fails
84
- // the whole call — mirrors nodes_getMany's tolerance of unknown ids, but made
98
+ // the whole call — mirrors nodesGetMany's tolerance of unknown ids, but made
85
99
  // explicit as an errors array so a caller can tell which refs didn't resolve.
86
100
  // Duplicate refs collapse first-seen (a repeated ref yields one item). Capped
87
101
  // at MANY_DOCS_CAP refs per call (matching nodesGetMany's 100), with the same
@@ -184,13 +198,13 @@ export function readDocumentAtRevision(store, docId, revId) {
184
198
  const content = out.join("");
185
199
  const renderedHashMatch = sha256(content).equals(rev.rendered_hash);
186
200
  return {
201
+ id: docId,
187
202
  // The revision records the path as of that revision (docs may be moved).
188
203
  path: referencePath(rev.path),
189
- docId,
190
204
  rev: revId,
191
- content,
205
+ raw: content,
192
206
  renderedHashMatch,
193
- properties: docPropertiesGrouped(db, docId),
207
+ properties: surfaceProperties(docPropertiesGrouped(db, docId)),
194
208
  propertiesAreCurrent: true,
195
209
  };
196
210
  }
@@ -5,15 +5,15 @@ export interface GetNode {
5
5
  type: string;
6
6
  label?: string;
7
7
  text?: string;
8
+ /** The block's raw source bytes (`raw` / `full`). */
8
9
  raw?: string;
9
- /** The block's raw_hash (hex) — the value update/split expect as content_hash. */
10
- content_hash?: string;
10
+ /** The block's raw_hash (hex) — the value update/split expect as `expect.content_hash`. */
11
+ hash?: string;
11
12
  attrs?: Record<string, unknown>;
12
- placement?: {
13
- parent: string | null;
14
- ordinal: number;
15
- depth: number;
16
- };
13
+ ordinal?: number;
14
+ depth?: number;
15
+ /** The parent block's id, null at the top level (`full`). */
16
+ parent_id?: string | null;
17
17
  children?: GetNode[];
18
18
  }
19
19
  export interface NodesGetOptions {
@@ -17,14 +17,16 @@ function project(store, node, resolution, includeChildren) {
17
17
  break;
18
18
  case "raw":
19
19
  out.raw = blockRaw(store, node.rawHashHex);
20
- out.content_hash = node.rawHashHex;
20
+ out.hash = node.rawHashHex;
21
21
  break;
22
22
  case "full":
23
23
  out.raw = blockRaw(store, node.rawHashHex);
24
- out.content_hash = node.rawHashHex;
24
+ out.hash = node.rawHashHex;
25
25
  out.text = node.text;
26
26
  out.attrs = node.attrs;
27
- out.placement = { parent: node.parentBlock, ordinal: node.ordinal, depth: node.depth };
27
+ out.ordinal = node.ordinal;
28
+ out.depth = node.depth;
29
+ out.parent_id = node.parentBlock;
28
30
  break;
29
31
  }
30
32
  if (includeChildren && node.children.length > 0) {
@@ -1,10 +1,12 @@
1
1
  import type { Store } from "../store/store.js";
2
2
  /** One row of the `ls` / docs_list surface: a live document with its block
3
- * count and last-commit timestamp. */
3
+ * count and last-commit timestamp, under the row attributes' own names
4
+ * (spec/surface §1.2, 3.0: `id`, `path`, `updated_at`). */
4
5
  export interface DocListRow {
6
+ id: string;
5
7
  path: string;
8
+ updated_at: string | null;
6
9
  blocks: number;
7
- ts: string | null;
8
10
  }
9
11
  /** A page of the docs_list surface. Honors the uniform list contract (mcp-api
10
12
  * §1): `truncated` is honest, and when true `cursor` resumes after the last
@@ -32,16 +34,18 @@ export declare const DOCS_LIST_DEFAULT_LIMIT = 200;
32
34
  * implementation for both surfaces). For a directory-aware summary use
33
35
  * `docsTree`. */
34
36
  export declare function docsList(store: Store, repoId: string, opts?: DocListOptions): DocListPage;
35
- /** One entry of the docs_tree surface: a live document (`kind: "doc"`) or a
36
- * collapsed directory (`kind: "dir"`, path ends in `/`) at the requested
37
- * depth. `docs`/`blocks` are totals under the entry (a doc counts 1 doc);
38
- * `ts` is the latest last-commit timestamp under it. */
37
+ /** One entry of the docs_tree surface: a live document (`kind: "doc"`, with
38
+ * its `id`) or a collapsed directory (`kind: "dir"`, `path` ends in `/`, no
39
+ * id) at the requested depth. `docs`/`blocks` are totals under the entry (a
40
+ * doc counts 1 doc); `updated_at` is the latest last-commit timestamp under
41
+ * it (spec/surface §1.2, 3.0 names). */
39
42
  export interface DocTreeEntry {
43
+ id?: string;
40
44
  path: string;
41
45
  kind: "dir" | "doc";
42
46
  docs: number;
43
47
  blocks: number;
44
- ts: string | null;
48
+ updated_at: string | null;
45
49
  }
46
50
  export interface DocTreePage {
47
51
  /** The normalized directory prefix the tree was taken under, in the
@@ -23,18 +23,16 @@ function liveDocRows(store, repoId, like, after, limit) {
23
23
  params.push(limit);
24
24
  }
25
25
  const rows = store.db
26
- .prepare(`SELECT d.path AS path,
27
- (SELECT count(*) FROM blocks b WHERE b.doc_id = d.doc_id AND b.deleted_commit IS NULL) AS blocks,
28
- (SELECT c.ts FROM revisions r JOIN commits c ON c.commit_id = r.commit_id WHERE r.rev_id = d.current_rev) AS ts
26
+ .prepare(`SELECT d.doc_id AS id, d.path AS path,
27
+ (SELECT c.ts FROM revisions r JOIN commits c ON c.commit_id = r.commit_id WHERE r.rev_id = d.current_rev) AS ts,
28
+ (SELECT count(*) FROM blocks b WHERE b.doc_id = d.doc_id AND b.deleted_commit IS NULL) AS blocks
29
29
  FROM docs d
30
30
  WHERE d.repo_id = ? AND d.deleted_commit IS NULL AND d.path LIKE ? ESCAPE '\\'${where}
31
31
  ORDER BY d.path${tail}`)
32
32
  .all(...params);
33
33
  // The surface speaks the reference form (spec/surface §1 "Paths"): rooted on
34
34
  // the way out, the storage form never leaves this module.
35
- for (const r of rows)
36
- r.path = referencePath(r.path);
37
- return rows;
35
+ return rows.map((r) => ({ id: r.id, path: referencePath(r.path), updated_at: r.ts, blocks: r.blocks }));
38
36
  }
39
37
  // Path-keyset cursors for the path-ordered list surfaces: the shared kernel
40
38
  // encoding (core/cursor.ts) with a one-part tuple holding the row's (rooted)
@@ -115,7 +113,7 @@ export function docsTree(store, repoId, opts = {}) {
115
113
  total.blocks += row.blocks;
116
114
  const segs = storage.slice(prefix.length).split("/");
117
115
  if (segs.length <= depth) {
118
- byPath.set(storage, { path: row.path, kind: "doc", docs: 1, blocks: row.blocks, ts: row.ts });
116
+ byPath.set(storage, { id: row.id, path: row.path, kind: "doc", docs: 1, blocks: row.blocks, updated_at: row.updated_at });
119
117
  continue;
120
118
  }
121
119
  const dir = referencePath(prefix + segs.slice(0, depth).join("/") + "/");
@@ -123,11 +121,11 @@ export function docsTree(store, repoId, opts = {}) {
123
121
  if (cur) {
124
122
  cur.docs += 1;
125
123
  cur.blocks += row.blocks;
126
- if (row.ts !== null && (cur.ts === null || row.ts > cur.ts))
127
- cur.ts = row.ts;
124
+ if (row.updated_at !== null && (cur.updated_at === null || row.updated_at > cur.updated_at))
125
+ cur.updated_at = row.updated_at;
128
126
  }
129
127
  else {
130
- byPath.set(dir, { path: dir, kind: "dir", docs: 1, blocks: row.blocks, ts: row.ts });
128
+ byPath.set(dir, { path: dir, kind: "dir", docs: 1, blocks: row.blocks, updated_at: row.updated_at });
131
129
  }
132
130
  }
133
131
  let entries = [...byPath.values()].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
@@ -17,7 +17,7 @@ export interface ProjectedNode {
17
17
  /**
18
18
  * `[spanStart, spanEnd)` of the feature within the block's raw, as JavaScript
19
19
  * string indices (UTF-16 code units). Ingest converts to UTF-8 byte offsets
20
- * before the `nodes` row is written (spec/graph §2.3), and `node_set`
20
+ * before the `nodes` row is written (spec/graph §2.3), and `nodes_set`
21
21
  * converts back before an editor slices the raw.
22
22
  */
23
23
  spanStart?: number | undefined;
@@ -8,7 +8,7 @@ export interface NodeChange {
8
8
  confidence: number | null;
9
9
  reason: string | null;
10
10
  }
11
- /** history_node: a block's biography from block_changes + commits (06 §3). */
11
+ /** blocks_history: a block's biography from block_changes + commits (06 §3). */
12
12
  export declare function historyNode(store: Store, blockId: string, opts?: {
13
13
  limit?: number;
14
14
  }): NodeChange[];
@@ -18,15 +18,17 @@ export declare class RevisionNotFound extends Error {
18
18
  readonly rev: string;
19
19
  constructor(docId: string, rev: string);
20
20
  }
21
+ /** One block-grain diff entry: the block's `id` (spec/surface §1.2, 3.0) and
22
+ * its raw `before`/`after` as applicable. */
21
23
  export interface DiffEntry {
22
24
  kind: "added" | "removed" | "changed" | "unchanged";
23
- blockId: string;
25
+ id: string;
24
26
  before?: string;
25
27
  after?: string;
26
28
  }
27
29
  export declare function diffBlocks(store: Store, docId: string, fromRev: string, toRev: string): DiffEntry[];
28
30
  /**
29
- * `diff_unified` (spec/surface §3): a unified diff of the two revisions'
31
+ * `docs_diff_unified` (spec/surface §3): a unified diff of the two revisions'
30
32
  * **reconstructed file text** — exactly the bytes `docs_read_at` returns for
31
33
  * each revision ({@link readDocumentAtRevision}: frontmatter, trivia and
32
34
  * top-level order included) — via {@link unifiedDiff}, so hunk line numbers
@@ -36,16 +38,16 @@ export declare function diffBlocks(store: Store, docId: string, fromRev: string,
36
38
  * first item (§9). An unknown revision is {@link RevisionNotFound}.
37
39
  */
38
40
  export declare function diffUnified(store: Store, docId: string, fromRev: string, toRev: string): string;
39
- /** The `diff_unified` tool's result (spec/surface §3): the resolved revision pair and the diff. */
41
+ /** The `docs_diff_unified` tool's result (spec/surface §3): the resolved revision pair and the diff. */
40
42
  export interface DocDiffUnified {
41
- doc: string;
43
+ id: string;
42
44
  path: string;
43
45
  from: string;
44
46
  to: string;
45
47
  diff: string;
46
48
  }
47
49
  /**
48
- * `diff_unified` over a resolved document: `to` defaults to the current revision,
50
+ * `docs_diff_unified` over a resolved document: `to` defaults to the current revision,
49
51
  * `from` to the one before it (or the current one again when there is only one:
50
52
  * an empty diff). Shared by the MCP tool and `omg diff` so the shape cannot
51
53
  * drift. `label` is how the caller named the document, for the error message.
@@ -95,12 +97,15 @@ export interface CommitDigest {
95
97
  origin: string;
96
98
  actor: string | null;
97
99
  summary: string;
98
- /** `contentHash` (hex of the revision's rendered_hash) lets a puller decide
99
- * "changed vs echo" without a follow-up docs_read/docs_history (ADR-014 §4.2). */
100
+ /** The revisions the commit wrote, each the document's row attributes
101
+ * (spec/surface §1.2, 3.0): `id`, `path` as of the commit, `hash` (hex of
102
+ * the revision's rendered_hash) — the hash lets a puller decide "changed vs
103
+ * echo" without a follow-up docs_read/docs_history (ADR-014 §4.2). The same
104
+ * three keys an `apply` revision carries. */
100
105
  revisions: {
101
- doc: string;
106
+ id: string;
102
107
  path: string;
103
- contentHash: string;
108
+ hash: string;
104
109
  }[];
105
110
  }
106
111
  /** changes_since: commit digests after a cursor (repo commit seq) — the change
@@ -124,17 +129,18 @@ export interface DocVersion {
124
129
  rev: string;
125
130
  seq: number;
126
131
  commit: string;
127
- ts: string;
132
+ /** the commit's ts — the document's `updated_at` as of this version (spec/surface §1.2, 3.0). */
133
+ updated_at: string;
128
134
  origin: string;
129
135
  actor: string | null;
130
- /** hex of the revision's rendered_hash — spot no-op vs real change, feed into docs_read_at/diff. */
131
- contentHash: string;
136
+ /** hex of the revision's rendered_hash — the document's `hash` as of this version; spot no-op vs real change, feed into docs_read_at/diff. */
137
+ hash: string;
132
138
  /** true when this rev == the doc's current_rev. */
133
139
  isCurrent: boolean;
134
140
  }
135
141
  /** A matching document with its ordered version history. */
136
142
  export interface DocVersions {
137
- docId: string;
143
+ id: string;
138
144
  path: string;
139
145
  deleted: boolean;
140
146
  currentRev: string | null;
@@ -3,7 +3,7 @@ import { readDocumentAtRevision } from "../core/read/document.js";
3
3
  import { isValidId } from "../core/ids.js";
4
4
  import { EngineError } from "../mcp/errors.js";
5
5
  import { referencePath, storagePath } from "../core/paths.js";
6
- /** history_node: a block's biography from block_changes + commits (06 §3). */
6
+ /** blocks_history: a block's biography from block_changes + commits (06 §3). */
7
7
  export function historyNode(store, blockId, opts = {}) {
8
8
  const limit = opts.limit ?? 100;
9
9
  return store.db
@@ -36,13 +36,13 @@ export function diffBlocks(store, docId, fromRev, toRev) {
36
36
  const entries = [];
37
37
  for (const [id, raw] of before) {
38
38
  if (!after.has(id))
39
- entries.push({ kind: "removed", blockId: id, before: raw });
39
+ entries.push({ kind: "removed", id: id, before: raw });
40
40
  else if (after.get(id) !== raw)
41
- entries.push({ kind: "changed", blockId: id, before: raw, after: after.get(id) });
41
+ entries.push({ kind: "changed", id: id, before: raw, after: after.get(id) });
42
42
  }
43
43
  for (const [id, raw] of after)
44
44
  if (!before.has(id))
45
- entries.push({ kind: "added", blockId: id, after: raw });
45
+ entries.push({ kind: "added", id: id, after: raw });
46
46
  return entries;
47
47
  }
48
48
  // Reconstruct the (block id → raw) map for a document at a revision by walking
@@ -78,7 +78,7 @@ function blocksAtRevision(store, docId, revId) {
78
78
  return out;
79
79
  }
80
80
  /**
81
- * `diff_unified` (spec/surface §3): a unified diff of the two revisions'
81
+ * `docs_diff_unified` (spec/surface §3): a unified diff of the two revisions'
82
82
  * **reconstructed file text** — exactly the bytes `docs_read_at` returns for
83
83
  * each revision ({@link readDocumentAtRevision}: frontmatter, trivia and
84
84
  * top-level order included) — via {@link unifiedDiff}, so hunk line numbers
@@ -92,12 +92,12 @@ export function diffUnified(store, docId, fromRev, toRev) {
92
92
  const at = readDocumentAtRevision(store, docId, rev);
93
93
  if (!at)
94
94
  throw new RevisionNotFound(docId, rev);
95
- return at.content;
95
+ return at.raw;
96
96
  };
97
97
  return unifiedDiff(rendered(fromRev), rendered(toRev));
98
98
  }
99
99
  /**
100
- * `diff_unified` over a resolved document: `to` defaults to the current revision,
100
+ * `docs_diff_unified` over a resolved document: `to` defaults to the current revision,
101
101
  * `from` to the one before it (or the current one again when there is only one:
102
102
  * an empty diff). Shared by the MCP tool and `omg diff` so the shape cannot
103
103
  * drift. `label` is how the caller named the document, for the error message.
@@ -113,7 +113,7 @@ export function docDiffUnified(store, docId, opts = {}) {
113
113
  if (!to || !from)
114
114
  throw new EngineError("target_missing", `no revisions to diff for ${opts.label ?? docId}`);
115
115
  const path = store.db.prepare("SELECT path FROM docs WHERE doc_id = ?").get(docId)?.path;
116
- return { doc: docId, path: path === undefined ? "" : referencePath(path), from, to, diff: diffUnified(store, docId, from, to) };
116
+ return { id: docId, path: path === undefined ? "" : referencePath(path), from, to, diff: diffUnified(store, docId, from, to) };
117
117
  }
118
118
  // ---- unified diff (spec/surface §3) ------------------------------------------
119
119
  // A line-grain unified diff with a deterministic Myers script, so both engines
@@ -276,7 +276,7 @@ export function changesSince(store, repoId, opts = {}) {
276
276
  // Every path in a digest — the revisions', a deletion's, a move's two, and
277
277
  // the summary's — is the reference form (spec/surface §1 "Paths").
278
278
  const digests = page.map((c) => {
279
- const revs = store.db.prepare("SELECT r.doc_id AS doc, r.path AS path, r.rendered_hash AS rendered_hash FROM revisions r WHERE r.commit_id = ?").all(c.commit_id).map((r) => ({ doc: r.doc, path: referencePath(r.path), contentHash: r.rendered_hash.toString("hex") }));
279
+ const revs = store.db.prepare("SELECT r.doc_id AS doc, r.path AS path, r.rendered_hash AS rendered_hash FROM revisions r WHERE r.commit_id = ?").all(c.commit_id).map((r) => ({ id: r.doc, path: referencePath(r.path), hash: r.rendered_hash.toString("hex") }));
280
280
  const dispCounts = store.db.prepare("SELECT kind, count(*) n FROM dispositions WHERE commit_id = ? GROUP BY kind").all(c.commit_id);
281
281
  // A deletion or a move writes no revision: the tombstone names the commit
282
282
  // (`docs.deleted_commit`), a move only its reason (`move <from> -> <to>`).
@@ -352,13 +352,13 @@ export function docHistory(store, repoId, opts = {}) {
352
352
  rev: r.rev,
353
353
  seq: r.seq,
354
354
  commit: r.commitId,
355
- ts: r.ts,
355
+ updated_at: r.ts,
356
356
  origin: r.origin,
357
357
  actor: r.actor,
358
- contentHash: r.rendered_hash.toString("hex"),
358
+ hash: r.rendered_hash.toString("hex"),
359
359
  isCurrent: r.rev === d.current_rev,
360
360
  }));
361
- return { docId: d.doc_id, path: referencePath(d.path), deleted: d.deleted_commit !== null, currentRev: d.current_rev, versions };
361
+ return { id: d.doc_id, path: referencePath(d.path), deleted: d.deleted_commit !== null, currentRev: d.current_rev, versions };
362
362
  });
363
363
  return { docs, truncated };
364
364
  }
@@ -1,11 +1,11 @@
1
1
  import type { Store } from "../core/store/store.js";
2
2
  export interface InboundLink {
3
3
  /** source document id */
4
- doc: string;
5
- /** source document repo-relative path */
4
+ doc_id: string;
5
+ /** source document repo-relative path (the surface roots it as `path`) */
6
6
  path: string;
7
7
  /** source block id; null for a frontmatter-level edge */
8
- block: string | null;
8
+ block_id: string | null;
9
9
  /** the link's destination path as written (e.g. "/old.md", "../old.md", "old.md") */
10
10
  target: string;
11
11
  /** #heading / ^ref fragment, if any */
@@ -19,7 +19,7 @@ export interface InboundLink {
19
19
  * re-scanned from the block raw so `target`/`anchor` are as written (and so a
20
20
  * self-doc pure-fragment link `#H`, which also has dst_node == docId, is NOT
21
21
  * reported — it does not depend on the path). Frontmatter edges are reported
22
- * with `block: null`, `target: docPath`, and their `field`.
22
+ * with `block_id: null`, `target: docPath`, and their `field`.
23
23
  */
24
24
  export declare function inboundLinksTo(store: Store, repoId: string, docId: string, docPath: string): InboundLink[];
25
25
  /**
@@ -17,7 +17,7 @@ function resolvesTo(dest, srcDir, docPath) {
17
17
  * re-scanned from the block raw so `target`/`anchor` are as written (and so a
18
18
  * self-doc pure-fragment link `#H`, which also has dst_node == docId, is NOT
19
19
  * reported — it does not depend on the path). Frontmatter edges are reported
20
- * with `block: null`, `target: docPath`, and their `field`.
20
+ * with `block_id: null`, `target: docPath`, and their `field`.
21
21
  */
22
22
  export function inboundLinksTo(store, repoId, docId, docPath) {
23
23
  const rows = store.db
@@ -30,7 +30,7 @@ export function inboundLinksTo(store, repoId, docId, docPath) {
30
30
  const seenBlocks = new Set();
31
31
  for (const r of rows) {
32
32
  if (r.srcBlock === null) {
33
- out.push({ doc: r.srcDoc, path: r.srcPath, block: null, target: docPath, anchor: null, ...(r.srcField ? { field: r.srcField } : {}) });
33
+ out.push({ doc_id: r.srcDoc, path: r.srcPath, block_id: null, target: docPath, anchor: null, ...(r.srcField ? { field: r.srcField } : {}) });
34
34
  continue;
35
35
  }
36
36
  if (seenBlocks.has(r.srcBlock))
@@ -46,9 +46,9 @@ export function inboundLinksTo(store, repoId, docId, docPath) {
46
46
  if (e.dstKind === "external" || !resolvesTo(e.target, srcDir, docPath))
47
47
  continue;
48
48
  out.push({
49
- doc: r.srcDoc,
49
+ doc_id: r.srcDoc,
50
50
  path: r.srcPath,
51
- block: r.srcBlock,
51
+ block_id: r.srcBlock,
52
52
  target: e.target,
53
53
  anchor: e.anchor,
54
54
  ...(e.srcField ? { field: e.srcField } : {}),
@@ -2,14 +2,14 @@ import type { Store } from "../core/store/store.js";
2
2
  export type StaleReason = "dangling_doc" | "dangling_block" | "broken_anchor";
3
3
  export interface StaleLink {
4
4
  /** source document id */
5
- srcDoc: string;
6
- /** source document repo-relative path */
7
- srcPath: string;
5
+ doc_id: string;
6
+ /** source document path in the reference form */
7
+ path: string;
8
8
  /** source block id (null for frontmatter-level edges) */
9
- srcBlock: string | null;
9
+ block_id: string | null;
10
10
  predicate: string;
11
11
  provenance: string;
12
- dstKind: string;
12
+ dst_kind: string;
13
13
  /** the canonical target path in the reference form (phantom prefix stripped,
14
14
  * rooted), e.g. "/guides/old.md" — spec/surface §1 "Paths" */
15
15
  target: string;
@@ -29,16 +29,16 @@ export interface LinkHealthSummary {
29
29
  target: string;
30
30
  count: number;
31
31
  }[];
32
- /** source documents carrying dangling links, most first (ties by path) */
32
+ /** source documents carrying dangling links, most first (ties by path); `path` the reference form */
33
33
  bySource: {
34
- srcPath: string;
34
+ path: string;
35
35
  count: number;
36
36
  }[];
37
37
  externalCount: number;
38
38
  totalOpenEdges: number;
39
39
  }
40
40
  export interface LinkHealth {
41
- /** dangling internal links, ordered by srcPath then target */
41
+ /** dangling internal links, ordered by source path then target */
42
42
  stale: StaleLink[];
43
43
  /** open external (http(s)) edges — unverifiable, not counted as stale */
44
44
  externalCount: number;
@@ -88,15 +88,15 @@ export function linksStale(store, repoId, opts = {}) {
88
88
  const stale = page.map((r) => {
89
89
  // Strip the "phantom:" prefix to expose the canonical path.
90
90
  const target = r.dstNode.slice("phantom:".length);
91
- // The surface speaks the reference form: `srcPath` and `target` rooted;
91
+ // The surface speaks the reference form: `path` and `target` rooted;
92
92
  // `authored` stays exactly as written in the source.
93
93
  return {
94
- srcDoc: r.srcDoc,
95
- srcPath: referencePath(r.srcPath),
96
- srcBlock: r.srcBlock,
94
+ doc_id: r.srcDoc,
95
+ path: referencePath(r.srcPath),
96
+ block_id: r.srcBlock,
97
97
  predicate: r.predicate,
98
98
  provenance: r.provenance,
99
- dstKind: r.dstKind,
99
+ dst_kind: r.dstKind,
100
100
  target: referencePath(target),
101
101
  authored: authoredFor(r.srcBlock, r.srcPath, target, r.anchor),
102
102
  anchor: r.anchor,
@@ -140,7 +140,7 @@ export function linksStaleSummary(store, repoId, opts = {}) {
140
140
  const bySource = store.db
141
141
  .prepare(`SELECT d.path AS srcPath, count(*) AS count ${phantom} GROUP BY d.path ORDER BY count DESC, d.path`)
142
142
  .all(...params)
143
- .map((r) => ({ srcPath: referencePath(r.srcPath), count: r.count }));
143
+ .map((r) => ({ path: referencePath(r.srcPath), count: r.count }));
144
144
  return {
145
145
  staleCount: byTarget.reduce((n, t) => n + t.count, 0),
146
146
  byTarget,
@@ -1,15 +1,15 @@
1
1
  import type { Store } from "../core/store/store.js";
2
2
  export interface LinkEdge {
3
3
  predicate: string;
4
- /** the other endpoint (dst for out-edges, src for in-edges) */
5
- node: string;
4
+ /** the other endpoint (dst for out-edges, src for in-edges): a doc id, an external node id or `phantom:<path>` */
5
+ doc_id: string;
6
6
  kind: string;
7
7
  /** how many underlying block-grain edges this doc-grain row rolls up */
8
8
  count: number;
9
9
  /** sample source block ids (out-edges only) */
10
10
  samples?: string[];
11
11
  /** block-grain (`blocks: true`): the source block of this edge (null for a frontmatter edge) */
12
- block?: string | null;
12
+ block_id?: string | null;
13
13
  }
14
14
  export interface LinksResult {
15
15
  out: LinkEdge[];