@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.
- package/README.md +10 -10
- package/dist/src/core/read/document.d.ts +18 -5
- package/dist/src/core/read/document.js +21 -7
- package/dist/src/core/read/nodes.d.ts +7 -7
- package/dist/src/core/read/nodes.js +5 -3
- package/dist/src/core/read/reader.d.ts +11 -7
- package/dist/src/core/read/reader.js +8 -10
- package/dist/src/format/adapter.d.ts +1 -1
- package/dist/src/graph/history.d.ts +20 -14
- package/dist/src/graph/history.js +12 -12
- package/dist/src/graph/inbound-links.d.ts +4 -4
- package/dist/src/graph/inbound-links.js +4 -4
- package/dist/src/graph/link-health.d.ts +8 -8
- package/dist/src/graph/link-health.js +6 -6
- package/dist/src/graph/links.d.ts +3 -3
- package/dist/src/graph/links.js +4 -4
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +2 -1
- package/dist/src/mcp/arguments.d.ts +27 -0
- package/dist/src/mcp/arguments.js +147 -0
- package/dist/src/mcp/errors.d.ts +1 -1
- package/dist/src/mcp/graph.d.ts +16 -9
- package/dist/src/mcp/graph.js +58 -35
- package/dist/src/mcp/reference.d.ts +1 -1
- package/dist/src/mcp/reference.js +215 -121
- package/dist/src/mcp/server.d.ts +9 -1
- package/dist/src/mcp/server.js +359 -268
- package/dist/src/mutate/apply.d.ts +11 -4
- package/dist/src/mutate/apply.js +3 -2
- package/dist/src/mutate/docs.js +7 -7
- package/dist/src/mutate/macros.d.ts +4 -1
- package/dist/src/mutate/macros.js +1 -1
- package/dist/src/mutate/ops.js +2 -2
- package/dist/src/mutate/opset.d.ts +8 -5
- package/dist/src/mutate/plan-update.js +9 -8
- package/dist/src/oqx-js/context.d.ts +17 -4
- package/dist/src/oqx-js/context.js +268 -131
- package/dist/src/oqx-js/planner.d.ts +3 -3
- package/dist/src/oqx-js/planner.js +14 -15
- package/dist/src/oqx-js/root-row.d.ts +10 -3
- package/dist/src/oqx-js/root-row.js +18 -8
- package/dist/src/oqx-js/run.d.ts +6 -0
- package/dist/src/oqx-js/run.js +71 -24
- package/dist/src/oqx-js/sql/scan.js +7 -7
- package/dist/src/oqx-js/sql/translate.d.ts +12 -9
- package/dist/src/oqx-js/sql/translate.js +183 -75
- package/dist/src/oqx-js/store-index.d.ts +1 -1
- package/dist/src/oqx-js/store-index.js +28 -26
- package/dist/src/search/cel/ast.d.ts +1 -1
- package/dist/src/search/cel/compile.d.ts +1 -1
- package/dist/src/search/cel/compile.js +43 -70
- package/dist/src/search/cel/parser.js +1 -1
- package/dist/src/search/resolve.d.ts +2 -0
- package/dist/src/search/rrf.js +2 -2
- package/dist/src/search/text.d.ts +2 -2
- package/dist/src/search/text.js +1 -1
- package/dist/src/spec-versions.d.ts +6 -6
- package/dist/src/spec-versions.js +5 -5
- package/dist/src/surface-paths.d.ts +41 -8
- package/dist/src/surface-paths.js +34 -18
- package/dist/src/sync/admin.d.ts +2 -1
- package/dist/src/sync/admin.js +1 -1
- package/package.json +2 -2
|
@@ -10,20 +10,23 @@ export const QUERY_SYNTAX = `# query — OQX syntax reference
|
|
|
10
10
|
The \`query\` tool takes ONE plain OQX string (+ optional \`limit\`/\`cursor\`). There
|
|
11
11
|
is no {from, filter, order} envelope: every concern is a clause of the string.
|
|
12
12
|
|
|
13
|
-
[select <items>] from <target> [where <pred>] [follow <dest
|
|
13
|
+
[select <items>] from <target> [where <pred>] [follow <dest> [{ … }], …]
|
|
14
14
|
[order by <expr> [asc|desc], …] [limit N] [offset N]
|
|
15
15
|
<target> count|exists|none|first|single { <block> } (scalar/one-row form: a bare target at the root scope)
|
|
16
16
|
|
|
17
17
|
CLAUSE ORDER IS FIXED: select, from, where, follow, order by, limit, offset —
|
|
18
18
|
each at most once; an out-of-order clause is a parse error naming the order.
|
|
19
19
|
Only \`select\` may drop its keyword, and only as the first clause
|
|
20
|
-
(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
(\`path, era from docs where …\`). A top-level predicate needs \`where\`, because
|
|
21
|
+
\`where\` follows \`from\` (\`era > 1600 from docs\` is the clause-order error); a
|
|
22
|
+
block may LEAD with a predicate (see Sugar), but a predicate after a projection
|
|
23
|
+
still needs the keyword (\`{ name where kind == "md:task" }\`, not
|
|
24
|
+
\`{ name, kind == "md:task" }\`); \`from docs count\` is an error (write
|
|
25
|
+
\`docs count { … }\`). \`where\` and later \`select\` items may use the same body's
|
|
26
|
+
\`select\` aliases (\`select path, old: era < 1000 from docs where old\`; an alias
|
|
27
|
+
shadows a same-named field there). \`and\` / \`or\` / \`not\` are the connectives;
|
|
28
|
+
the symbols \`&&\` / \`||\` / \`!\` are accepted synonyms (the words are canonical and
|
|
29
|
+
what \`print\` writes).
|
|
27
30
|
|
|
28
31
|
## Sugar (OQX 0.17)
|
|
29
32
|
|
|
@@ -33,98 +36,155 @@ Every form here is shorthand for an explicit directive (the AST is the explicit
|
|
|
33
36
|
(comparison, call, !/is/not, in, literal, (…), a consumer test — anything
|
|
34
37
|
but a bare name, a dotted path or a ^lift) is where-first. Bare names still
|
|
35
38
|
project: nodes { name } ≡ nodes collect { name }; filter a bare field with
|
|
36
|
-
nodes { is checked }.
|
|
39
|
+
nodes { is checked }. Wherever a value stands (0.20), call arguments included:
|
|
40
|
+
size(children { is checked }); x distinct { … } ≡ x collect distinct { … }. After a
|
|
41
|
+
follow destination too: follow children { depth 2 } is that destination's block.
|
|
42
|
+
(a, b) a tuple (0.20): two or more expressions, concatenated one level flat — a value
|
|
43
|
+
(size((tags, aliases)), "x" in (tags, aliases)) and a receiver ((a, b) { … },
|
|
44
|
+
follow (before, after) { depth 2 }); (a) alone is grouping
|
|
37
45
|
refs(x)[0] ≡ refs(x) first { offset 0 } positional (integer literal or \${binding});
|
|
38
|
-
out of range ⇒ absent; refs(x)[0]
|
|
39
|
-
^docs[
|
|
40
|
-
^docs[
|
|
41
|
-
title! required: title, or an error naming the expression (and the row's $id)
|
|
46
|
+
out of range ⇒ absent; refs(x)[0].path navigates the row
|
|
47
|
+
^docs[path == ^company] ≡ ^docs first { where path == ^company } first match or absent
|
|
48
|
+
^docs[path == ^company]! ≡ … single { … } required: exactly one, else filter_invalid
|
|
49
|
+
title! required: title, or an error naming the expression (and the row's $key — its id)
|
|
42
50
|
— never a filter, never a coercion (0!, ""! are values); tightest: a!.b vs a.b!
|
|
43
|
-
is x / not x
|
|
44
|
-
x is y / x is not y identity (a row's id, else structural) — compares rows, which == does not;
|
|
51
|
+
is x / not x truthiness of x, and its negation (prefix; \`not\` ≡ \`!\`)
|
|
52
|
+
x is y / x is not y identity ($key: a store row's id, else structural) — compares rows, which == does not;
|
|
45
53
|
x is null = absent; for scalars is ≡ ==; comparison precedence, no chaining
|
|
46
|
-
a and b / a or b ≡
|
|
47
|
-
boss: ^docs[
|
|
54
|
+
a and b / a or b the connectives (≡ && / ||: same precedence, short-circuit, value: data.title or path coalesces)
|
|
55
|
+
boss: ^docs[path == ^manager], bossName: boss.title a select item may use the items to its LEFT
|
|
48
56
|
Reserved words (never a bare field name): from where select is not and or, true false null.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
Contextual words (limit order offset follow collect count first distinct desc by in frontier depth) parse as
|
|
58
|
+
fields in expression position (select o: order from docs).
|
|
59
|
+
|
|
60
|
+
Results are LEAN hits — { id, path } + whatever \`select\` projects (or \`values\`,
|
|
61
|
+
\`count\`, \`exists\`, \`none\`). The envelope keys ARE the attributes: copy a hit's
|
|
62
|
+
\`id\` straight into \`where id == "…"\` (no translation); \`select id, path\` is simply
|
|
63
|
+
the envelope (no duplicate columns); any OTHER projection whose output key would
|
|
64
|
+
be \`id\` or \`path\` must be aliased — \`select data.path\` is refused ("projection
|
|
65
|
+
\`path\` collides with the hit envelope — alias it (\`p: data.path\`)"); a row
|
|
66
|
+
surfacing as a value (an empty \`collect { }\`) is the same \`{ id, path }\`. Every
|
|
67
|
+
other tool spells a row the same way (\`docs_read\` → \`{ id, path, rev, properties,
|
|
68
|
+
raw }\`, \`blocks_read\` → \`{ id, type, text, raw, hash, … }\`, \`docs_list\` → \`{ id,
|
|
69
|
+
path, updated_at, blocks }\`, a revision → \`{ id, path, hash }\`): hydrate by id via
|
|
70
|
+
blocks_read/docs_read.
|
|
52
71
|
|
|
53
72
|
## Paths
|
|
54
73
|
|
|
55
74
|
Every path the surface returns is \`/\`-rooted — the form a reference is
|
|
56
75
|
written in (\`[x](/projects/oqx.md)\`, \`before: [/timeline/kickoff.md]\`): a
|
|
57
|
-
hit's \`path\`,
|
|
76
|
+
hit's \`path\`, an edge's \`dst_path\`, every tool's paths. Every path it
|
|
58
77
|
accepts tolerates both forms (\`/a.md\` or \`a.md\`): tool arguments, \`refs(x)\`,
|
|
59
|
-
\`within(...)\`, and a string LITERAL compared with
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
reference compares directly
|
|
63
|
-
\`where
|
|
78
|
+
\`within(...)\`, and a string LITERAL compared with \`path\`/\`dst_path\` (bare, or
|
|
79
|
+
through a row handle: \`doc.path\`) by \`==\`/\`!=\` or passed to their
|
|
80
|
+
\`.startsWith(...)\` (it is rooted first, so \`path == "a.md"\` and \`path == "/a.md"\`
|
|
81
|
+
both match). A property holding a reference compares directly — and is never
|
|
82
|
+
rooted: \`where ^path in list(after)\`, \`where customer == ^path\`,
|
|
83
|
+
\`where data.path == "/x.md"\` (the author's \`path:\` key, as written) — no
|
|
84
|
+
\`"/" + ...\` glue.
|
|
64
85
|
|
|
65
86
|
## Targets & field namespaces
|
|
66
87
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
THE NAME RULE: \`$\` belongs to OQX — $it, $key, $depth, $stop, $leaf, $frontier,
|
|
89
|
+
$ordinal are values the LANGUAGE injects into a scope; omgbase adds NO $ names
|
|
90
|
+
(a \`$id\` in a query is an unknown name, absent). Every engine attribute is a BARE
|
|
91
|
+
snake_case name that WINS on collision — the convention the relations (children,
|
|
92
|
+
blocks, out, in, doc, frontmatter, …) always followed: a bare name that is an
|
|
93
|
+
engine attribute reads the engine's value, never yours. Your data is bare too;
|
|
94
|
+
where a key of yours collides with an engine name, reach it through a source
|
|
95
|
+
handle: \`data.<k>\` (the merged authored view — frontmatter + inline, exactly the
|
|
96
|
+
read a bare non-colliding key performs), \`frontmatter.<k>\`, \`inline.<k>\`. So
|
|
97
|
+
\`path\` is the engine's /-rooted location and \`data.path\` your \`path:\` key;
|
|
98
|
+
\`tags\` is your tags ∪ the body #hashtags and \`data.tags\` the authored list alone;
|
|
99
|
+
\`title\` the computed title (the first H1) and \`data.title\` your \`title:\`;
|
|
100
|
+
\`entries(data)\` lists every authored top-level key. Same value, same name
|
|
101
|
+
everywhere: a hit's \`id\` is docs_read's \`id\` and the \`id\` argument of blocks_read.
|
|
102
|
+
|
|
103
|
+
Reserved bare names per kind (engine attributes; handles; self; relations):
|
|
104
|
+
docs id path raw hash updated_at title tags format | data frontmatter inline | doc | blocks nodes out in out_links in_links
|
|
105
|
+
blocks id path doc_id raw hash updated_at type text attrs depth ordinal | block doc | children nodes out_links section
|
|
106
|
+
nodes id path doc_id block_id kind name value attrs | section doc block | blocks subsections children
|
|
107
|
+
links id path doc_id block_id via_id dst_id dst_path uri field dst_kind predicate provenance anchor | doc
|
|
108
|
+
A key of yours with any of these names is \`data.<k>\` (or frontmatter.<k> /
|
|
109
|
+
inline.<k> by source; attrs.<k> on a block or node).
|
|
73
110
|
|
|
74
111
|
docs:
|
|
75
112
|
- bare identifier = a document PROPERTY (frontmatter + inline \`key:: value\`,
|
|
76
113
|
unioned; nested via dots: meta.owner). YAML/JSON docs
|
|
77
114
|
expose the parsed object's keys the same way.
|
|
78
|
-
- source
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
115
|
+
- source handles = data.<k> (merged authored view) / frontmatter.<k> /
|
|
116
|
+
inline.<k>; the whole bag as a collection via entries(data)
|
|
117
|
+
/ entries(frontmatter) / entries(inline) (see entries()).
|
|
118
|
+
- attributes = id, path (/-rooted, see Paths), updated_at (ISO-8601,
|
|
119
|
+
sorts chronologically), raw (the whole file, frontmatter
|
|
120
|
+
included), hash (its sha256, 64 hex), title (the computed
|
|
121
|
+
title: the first H1; data.title is the authored key), tags
|
|
122
|
+
(authored tags ∪ body #hashtags, deduped, authored first;
|
|
123
|
+
data.tags is the authored list), format (the doc's format —
|
|
124
|
+
"markdown"/"yaml"/"json"; present on EVERY doc, so
|
|
125
|
+
has(format) is always true and \`format == "yaml"\` is the
|
|
126
|
+
filter).
|
|
127
|
+
- relations = nodes, blocks, doc.out_links / doc.in_links (a doc's
|
|
128
|
+
links as rows; the documents at their other end are
|
|
129
|
+
out_links collect distinct { from dst } and
|
|
130
|
+
in_links collect distinct { from doc } — there is no
|
|
131
|
+
out / in relation).
|
|
88
132
|
|
|
89
133
|
blocks:
|
|
90
|
-
- bare fields =
|
|
91
|
-
(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
134
|
+
- bare fields = doc_id (the owning doc), type, text (normalized), ordinal
|
|
135
|
+
(0-based among siblings), depth (0 at the top level), and
|
|
136
|
+
every attrs key FLATTENED onto the row (bare \`checked\`
|
|
137
|
+
reads attrs.checked; \`attrs.<k>\` still works; the
|
|
138
|
+
structural fields win on collision; absent ⇒ false).
|
|
139
|
+
- attributes = id, path (the owning doc's path), raw (the block's raw
|
|
140
|
+
source — a container's raw holds its children), hash
|
|
141
|
+
(its sha256 — the value update/split expect as
|
|
142
|
+
expect.content_hash), updated_at.
|
|
143
|
+
- reach-through = doc.<key> / doc.path / doc.id / doc.format = the
|
|
144
|
+
CONTAINING doc (doc.id and doc.path cost nothing — they
|
|
145
|
+
read the row's own doc_id / joined path, no lookup).
|
|
146
|
+
- relations = block.children, block.nodes, block.out_links, section.
|
|
147
|
+
NOTE: inside a \`follow\`, $depth/$ordinal are ALWAYS the walk's metadata; a
|
|
148
|
+
block's own nesting is \`depth\` / \`ordinal\` (no $) everywhere.
|
|
98
149
|
|
|
99
150
|
nodes:
|
|
100
|
-
- bare fields =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
151
|
+
- bare fields = doc_id, block_id (null for a block-less node), kind
|
|
152
|
+
(md:task / md:link / md:section / yaml:…), name, value,
|
|
153
|
+
+ attrs keys flattened (checked, level).
|
|
154
|
+
- attributes = id, path.
|
|
155
|
+
- reach-through = doc.<key> / doc.id / doc.path, block.type / block.text
|
|
156
|
+
/ block.id / block.path.
|
|
104
157
|
- relations = section.blocks, section.children, section.subsections.
|
|
105
158
|
|
|
106
|
-
|
|
107
|
-
|
|
159
|
+
links: (the authored link graph as rows — one per open link: the relation a link
|
|
160
|
+
establishes; the md:link NODE is its occurrence in the text and via_id
|
|
161
|
+
points at it — a frontmatter link has a row and no node)
|
|
162
|
+
- bare fields = doc_id (the SOURCE doc), block_id (the source block; null
|
|
163
|
+
for a frontmatter edge), via_id, dst_id (the destination
|
|
164
|
+
node id: a doc id, an external node id or phantom:<path>),
|
|
165
|
+
dst_path (target doc path, /-rooted; NULL when
|
|
166
|
+
dangling/external), uri (external URL; NULL otherwise),
|
|
167
|
+
predicate ("references"/"embeds"/freeform), provenance
|
|
108
168
|
("link"/"frontmatter"/"inline_field"/…), dst_kind
|
|
109
|
-
("document"/"external"/"collection"), anchor,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- text()/semantic() are N/A on
|
|
169
|
+
("document"/"external"/"collection"), anchor, field (the
|
|
170
|
+
frontmatter key / inline field it was authored in).
|
|
171
|
+
- attributes = id, path (the SOURCE doc's path); doc.<key> reaches the
|
|
172
|
+
source document. There is no from_commit: a query sees
|
|
173
|
+
open edges only (history is blocks_history/changes_since).
|
|
174
|
+
- text()/semantic() are N/A on links.
|
|
115
175
|
|
|
116
176
|
## Scoping (ADR-015; the root row since surface 2.0): bare names are LOCAL
|
|
117
177
|
|
|
118
178
|
name the CURRENT row only — never falls through to an enclosing row
|
|
119
179
|
or the repository; an absent field is simply absent.
|
|
120
180
|
^name one scope OUT (the enclosing row's fields/intrinsics/lifts);
|
|
121
|
-
^^name two scopes out.
|
|
122
|
-
^docs ^nodes ^blocks ^
|
|
181
|
+
^^name two scopes out. \`^path\` = the enclosing row's path.
|
|
182
|
+
^docs ^nodes ^blocks ^links
|
|
123
183
|
THE ROOT ROW (surface 2.0, oqx 0.18): the repository is the root
|
|
124
184
|
scope's row, one scope out from a top-level row, so ^docs there is
|
|
125
185
|
the whole documents scan (uncorrelated until you add a ^ predicate);
|
|
126
186
|
one caret per enclosing block (^^docs from depth two), or the
|
|
127
|
-
absolute 0^docs from ANY depth.
|
|
187
|
+
absolute 0^docs from ANY depth. ^id / 0^id = the repository id.
|
|
128
188
|
^$it = the root object: ^$it.docs ≡ ^docs, entries(^$it) names the
|
|
129
189
|
four collections. At the root scope itself a bare target is the scan
|
|
130
190
|
(docs count { … }, from docs, entries(docs)).
|
|
@@ -137,7 +197,9 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
137
197
|
receiver is a list property); ^$it = the enclosing row.
|
|
138
198
|
$value is NOT an intrinsic (since oqx 0.15): it reads a
|
|
139
199
|
property literally named $value, absent when there is none.
|
|
140
|
-
$key
|
|
200
|
+
$key the row's identity (0.20): a store row's id (doc/block/node/edge), an entries(x)
|
|
201
|
+
entry's key, a follow occurrence's walk identity; project $key: <expr> to rekey the
|
|
202
|
+
rows a block yields (a destination block's $key: is not a column).
|
|
141
203
|
|
|
142
204
|
## Scalar semantics (@omgbase/oqx)
|
|
143
205
|
|
|
@@ -148,7 +210,7 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
148
210
|
Relational (< <= > >=) on an absent operand → FALSE. Bare \`f\` in
|
|
149
211
|
boolean position uses JS truthiness (absent/false/0/"" ⇒ false;
|
|
150
212
|
NOTE: "false", [] and {} are truthy). has(f) = explicit presence.
|
|
151
|
-
boolean
|
|
213
|
+
boolean and or not grouping ( … ) — && || ! are accepted synonyms
|
|
152
214
|
arithmetic + - * / % (+ concatenates if either side is a string)
|
|
153
215
|
membership x in y array → typed membership; string → substring;
|
|
154
216
|
object → key exists; range → coverage. Any list
|
|
@@ -160,7 +222,7 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
160
222
|
range-shaped STRING property as an interval: "2026-01-15" in range(window).
|
|
161
223
|
strings x.contains("s") x.startsWith("p") x.endsWith("s") x.matches("^re$")
|
|
162
224
|
(real regex) x.lower() x.upper() size(x). ALL CASE-SENSITIVE —
|
|
163
|
-
fold with .lower():
|
|
225
|
+
fold with .lower(): title.lower().contains("aurora").
|
|
164
226
|
functions has(f) size(x) list(x) entries(x) range(s)
|
|
165
227
|
literals "str" 'str' 123 1.5 true false null
|
|
166
228
|
determinism no now()/clock/random. Lists/structs are not literals; ternary is
|
|
@@ -174,7 +236,7 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
174
236
|
via the node index); composes anywhere, incl. nested scopes.
|
|
175
237
|
semantic("phrase") embedding cosine SCORE (docs/blocks): threshold it
|
|
176
238
|
(semantic("x") > 0.6), project it, or \`order by semantic("x")
|
|
177
|
-
desc\` for top-K. Needs a provider; else
|
|
239
|
+
desc\` for top-K. Needs a provider that has embedded the repository; else provider_unavailable.
|
|
178
240
|
blocks target only:
|
|
179
241
|
under(id_or_heading) block in the containment subtree / section range
|
|
180
242
|
under_heading("Launch") an ancestor section heading contains the text (ci)
|
|
@@ -195,8 +257,8 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
195
257
|
a source, a receiver or a follow destination:
|
|
196
258
|
follow refs(before), refs(after) walks a timeline both ways.
|
|
197
259
|
The reverse direction needs no function: a document's
|
|
198
|
-
|
|
199
|
-
^docs
|
|
260
|
+
path is already the reference form, so
|
|
261
|
+
^docs { where ^path in list(after) } is
|
|
200
262
|
"the documents whose \`after\` names me".
|
|
201
263
|
A HIT IS A STORE ROW: a top-level row that is not a doc/block/node/edge fails
|
|
202
264
|
(filter_invalid). \`follow before\` over a list of paths
|
|
@@ -205,14 +267,15 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
205
267
|
## Nested queries & consumers
|
|
206
268
|
|
|
207
269
|
<receiver> collect|exists|none|count|first|single { <block> }
|
|
208
|
-
receiver = a relation (nodes, blocks, doc.
|
|
270
|
+
receiver = a relation (nodes, blocks, doc.out_links, section.blocks, …), a
|
|
271
|
+
reach-through row (dst, doc: one row — dst { title }), a
|
|
209
272
|
list property (tags), entries(x), or a root collection (^docs; 0^docs at depth ≥ 2).
|
|
210
273
|
block = [select …] [where …] [follow …] [order by …] [limit N] [offset N]
|
|
211
274
|
— the same fixed order as the top level (\`from\` optional; the
|
|
212
275
|
receiver supplies the rows). A leading name/alias list is the
|
|
213
276
|
projection (\`select\` dropped); a predicate always needs \`where\`.
|
|
214
|
-
exists ≥1 row nodes exists { where kind == "md:task"
|
|
215
|
-
none 0 rows nodes none { where kind == "md:task"
|
|
277
|
+
exists ≥1 row nodes exists { where kind == "md:task" and not checked }
|
|
278
|
+
none 0 rows nodes none { where kind == "md:task" and not checked } ("every task done")
|
|
216
279
|
count a number nodes count { where kind == "md:task" } >= 2
|
|
217
280
|
collect rows (default) — in select for nested results; in where with a lift
|
|
218
281
|
first / single zero-or-one (single errors on >1) — lookups in select
|
|
@@ -221,62 +284,90 @@ edges: (the authored link graph as rows — one per open edge)
|
|
|
221
284
|
consumer reduces: nodes exists { offset 1 } = at least two;
|
|
222
285
|
first { … offset 1 } = the second.
|
|
223
286
|
lift \`^name:\` in a where-collect binds values outward:
|
|
224
|
-
select
|
|
225
|
-
joins ^<t> exists { where slug == ^ref } semi-join;
|
|
226
|
-
^nodes single { where kind == "person"
|
|
287
|
+
select path, open from docs where nodes collect { ^open: value where kind == "md:task" and not checked }
|
|
288
|
+
joins ^<t> exists { where slug == ^ref } semi-join; not … exists anti-join;
|
|
289
|
+
^nodes single { where kind == "person" and attrs.id == ^owner_id } lookup;
|
|
227
290
|
x in ^keys membership over a lifted set.
|
|
228
291
|
|
|
229
292
|
## select — projection
|
|
230
293
|
|
|
231
294
|
select a, alias: expr, nested: nodes collect { … }
|
|
232
|
-
bare key → the
|
|
233
|
-
|
|
234
|
-
|
|
295
|
+
bare key → the attribute when it is one (id, path, title, …), else the property
|
|
296
|
+
(scalar-authored → scalar; list → array); data.<k> forces the property;
|
|
297
|
+
a call/arithmetic needs an alias (n: size(tags)) unless in values mode;
|
|
298
|
+
an output key of id/path other than the attribute itself needs an alias.
|
|
299
|
+
raw whole file bytes (docs) — docs_read is cheaper for one doc.
|
|
235
300
|
distinct select distinct type (dedup hits by projected value)
|
|
236
301
|
values select <ONE item> values → bare values instead of records:
|
|
237
302
|
from docs select distinct type values → values: ["hub","lab-note",…]
|
|
238
|
-
select
|
|
303
|
+
select path values from docs → values: ["/index.md", …]
|
|
239
304
|
tags: tags collect { $it values } → a plain array
|
|
240
305
|
h: nodes first { name values where kind == "md:section" order by first_ordinal }
|
|
241
306
|
entries(x) a record as a collection ($key / $it per entry), key order:
|
|
242
|
-
select fm: entries(
|
|
243
|
-
where entries(frontmatter) exists { where $key == "era"
|
|
307
|
+
select fm: entries(data) collect { k: $key, v: $it }
|
|
308
|
+
where entries(frontmatter) exists { where $key == "era" and $it > 1600 }
|
|
244
309
|
entries(inline), entries(attrs), a nested map, or a list (index keys)
|
|
245
310
|
$semantic_score is not a field — project semantic("…") under an alias instead.
|
|
246
311
|
|
|
247
312
|
## order by, limit/offset, pagination
|
|
248
313
|
|
|
249
|
-
order by era desc,
|
|
314
|
+
order by era desc, path asc absent values sort LAST (asc); ties → (path, id)
|
|
250
315
|
limit N / offset N define the result SET (integer literals);
|
|
251
316
|
the tool's limit/cursor then page WITHIN it
|
|
252
317
|
cursor opaque keyset on (path,id); a custom order by
|
|
253
318
|
disables it (you get the top page + truncated)
|
|
254
319
|
|
|
255
|
-
## Graph traversal (follow) and
|
|
320
|
+
## Graph traversal (follow) and links
|
|
256
321
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
322
|
+
The citation graph is the LINK ROWS walked through a destination block:
|
|
323
|
+
dst is a link's destination document (absent for an external or dangling
|
|
324
|
+
link — it then contributes no successor), doc its source.
|
|
325
|
+
from docs where <seed> follow out_links collect { from dst } docs the seed links TO
|
|
326
|
+
from docs where <seed> follow in_links collect { from doc } backlinks
|
|
327
|
+
follow out_links collect { from dst depth 3 } bound (1..8, default 8) — a clause of the block
|
|
328
|
+
follow out_links collect { from dst where layer == "canon" } keep only matching successors
|
|
329
|
+
follow in_links collect { from doc where ^path in list(before) } ^ = the row being expanded
|
|
262
330
|
(^^ = the walk's enclosing scope)
|
|
263
|
-
follow
|
|
331
|
+
follow out_links collect { from dst where predicate == "depends_on" } one predicate only
|
|
332
|
+
follow out_links collect { from dst }, in_links collect { from doc }
|
|
333
|
+
several destinations: their union
|
|
264
334
|
(one step dedups by identity)
|
|
265
|
-
follow ^docs
|
|
266
|
-
a destination block
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
follow
|
|
270
|
-
|
|
335
|
+
follow ^docs { where out_links exists { where dst_path == ^^path } }
|
|
336
|
+
a destination block over the root's
|
|
337
|
+
documents, re-read per frontier row
|
|
338
|
+
(^ = that row) = the backlinks walk
|
|
339
|
+
follow out_links collect { from dst frontier type == "practitioner" } cut, keeping the frontier row
|
|
340
|
+
from docs where type == "process" frontier !verified follow out_links collect { from dst }
|
|
341
|
+
the BODY's frontier applies to the seeds:
|
|
342
|
+
an unverified seed is kept, not expanded
|
|
343
|
+
follow distinct out_links collect { from dst } / … { $key: group from dst } identity control ($key)
|
|
344
|
+
follow refs(before), ^docs { where ^path in list(after) } forward over \`before\`, backward
|
|
345
|
+
over every doc whose \`after\` names me
|
|
271
346
|
Per-occurrence metadata: $depth (seed = 1), $stop (interior|leaf|frontier|
|
|
272
|
-
depth|cycle), $leaf/$frontier, $ordinal — usable in select/order by and
|
|
273
|
-
top-level post-walk where, NOT metadata of the candidates
|
|
274
|
-
where
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
from
|
|
278
|
-
|
|
279
|
-
|
|
347
|
+
depth|cycle), $leaf/$frontier, $ordinal, $key — usable in select/order by and
|
|
348
|
+
the top-level post-walk where, NOT metadata of the candidates a destination
|
|
349
|
+
block's where reads.
|
|
350
|
+
Destination blocks (OQX 0.20): a brace after ANY destination opens that
|
|
351
|
+
destination's block — children { depth 2 }, refs(x) { depth 2 }, out_links
|
|
352
|
+
collect { from dst depth 2 }, ^docs { where ^path in list(after) } — with the
|
|
353
|
+
0.17 body rules (where-first, projections) and two clauses of its own after
|
|
354
|
+
\`where\`: frontier <pred> (occurrences reached through this destination that
|
|
355
|
+
match are frontiers; ^ there is the walk's enclosing scope) and depth <n>
|
|
356
|
+
(this destination expands a frontier row only while $depth < n). The body
|
|
357
|
+
that carries the follow may write frontier <pred> too, after its where and
|
|
358
|
+
before follow: it applies to the SEEDS (read like the body's where) — a
|
|
359
|
+
matching seed stays in the result with $stop == "frontier" and is not
|
|
360
|
+
expanded; each destination's frontier is its own. There is no options block;
|
|
361
|
+
the old \`by\` is $key. A bare follow ^docs (no brace, no consumer) is an
|
|
362
|
+
error naming both block spellings.
|
|
363
|
+
Type-preserving relations: block.children, section.children, section.subsections;
|
|
364
|
+
the citation graph is out_links collect { from dst } / in_links collect { from doc }.
|
|
365
|
+
from links where dst_path == "/notes/x.md" inbound links to a doc, as rows
|
|
366
|
+
select d: dst.title, k: dst_kind from links the destination document's title
|
|
367
|
+
from docs where out_links exists { where dst.layer == "draft" } links into drafts
|
|
368
|
+
select b: in_links collect distinct { from doc } from docs backlinks as { id, path }
|
|
369
|
+
from links where predicate == "depends_on" select doc_id, dst_path
|
|
370
|
+
from docs where not doc.in_links exists { } orphans (nothing links in)
|
|
280
371
|
(The \`graph\` tool is a convenience wrapper that compiles to a follow query.)
|
|
281
372
|
|
|
282
373
|
## semantic grain
|
|
@@ -287,29 +378,32 @@ text()/where prune the candidate set; only order by reweights.
|
|
|
287
378
|
## Examples
|
|
288
379
|
|
|
289
380
|
from docs where layer == "working"
|
|
290
|
-
from docs where
|
|
381
|
+
from docs where path.startsWith("/guides/") and updated_at >= "2026-08-01"
|
|
291
382
|
from docs where "pricing" in list(tags) select layer, tags
|
|
292
383
|
from docs where inline.owner == "alice"
|
|
293
|
-
from docs where
|
|
384
|
+
from docs where title.lower().contains("q3 plan")
|
|
294
385
|
from docs where format == "yaml"
|
|
295
386
|
from docs where type == "practitioner" order by era desc limit 2
|
|
296
|
-
from docs where nodes none { where kind == "md:task"
|
|
387
|
+
from docs where nodes none { where kind == "md:task" and not checked }
|
|
297
388
|
from docs where tags exists { where $it == "tria-prima" }
|
|
298
|
-
from docs where
|
|
299
|
-
from docs select owner_id, owner: ^nodes single { where kind == "person"
|
|
300
|
-
from blocks where type == "task"
|
|
301
|
-
from blocks where type == "paragraph"
|
|
302
|
-
select
|
|
303
|
-
select
|
|
304
|
-
select
|
|
305
|
-
select
|
|
306
|
-
|
|
389
|
+
from docs where path == "/x.md" select fm: entries(data) collect { k: $key, v: $it }
|
|
390
|
+
from docs select owner_id, owner: ^nodes single { where kind == "person" and attrs.id == ^owner_id }
|
|
391
|
+
from blocks where type == "task" and not checked and under_heading("Launch") and doc.layer == "working"
|
|
392
|
+
from blocks where type == "paragraph" and has_edge("references", "d_92aaaaa")
|
|
393
|
+
select path, $depth from docs where path == "/timeline/review.md" follow refs(before), refs(after)
|
|
394
|
+
select path, next: ^docs collect { path where ^path in list(before) } from docs where type == "milestone"
|
|
395
|
+
select path, n: size(^docs), repo: ^id from docs limit 1
|
|
396
|
+
select p: data.path, t: data.title from docs where has(data.path)
|
|
397
|
+
docs { type == "substance" and not verified }
|
|
398
|
+
select subject, process: ^docs[slug == ^subject]!.path from docs where type == "lab-note"
|
|
399
|
+
select path, back: nodes first { select p: 0^docs collect { path where ^^path in list(after) } values } from docs where type == "milestone"
|
|
400
|
+
select path, prior: refs(before) collect { path, when } from docs where type == "milestone"
|
|
307
401
|
from blocks order by semantic("identity preservation across edits") desc limit 10
|
|
308
|
-
from
|
|
402
|
+
from links where dst_kind == "external" select uri
|
|
309
403
|
docs count { where layer == "canon" }
|
|
310
404
|
|
|
311
|
-
Common mistake:
|
|
312
|
-
|
|
313
|
-
|
|
405
|
+
Common mistake: \`$path\`/\`$id\` (the 2.x spelling) — omgbase has no $ names; write
|
|
406
|
+
\`path\`/\`id\`. Another: \`where tags == "x"\` on a list-valued key is false — write
|
|
407
|
+
\`"x" in tags\`. Another: \`select data.path\` — alias it (\`p: data.path\`).
|
|
314
408
|
`;
|
|
315
409
|
//# sourceMappingURL=reference.js.map
|
package/dist/src/mcp/server.d.ts
CHANGED
|
@@ -12,13 +12,16 @@ export interface ServerContext {
|
|
|
12
12
|
rootPath?: string;
|
|
13
13
|
/**
|
|
14
14
|
* Embed a query string to a vector for semantic search. Absent ⇒ the `query`
|
|
15
|
-
* tool's `semantic` param yields `
|
|
15
|
+
* tool's `semantic` param yields `provider_unavailable` (no provider). The
|
|
16
16
|
* host (CLI `omg mcp`) supplies this from the repo's configured embedder.
|
|
17
17
|
*/
|
|
18
18
|
embedQuery?: (text: string) => Promise<{
|
|
19
19
|
model: string;
|
|
20
20
|
vec: Float32Array;
|
|
21
21
|
}>;
|
|
22
|
+
/** The configured provider's name (`embedding.provider`), for the
|
|
23
|
+
* `provider_unavailable` error's `data` when the host knows it. */
|
|
24
|
+
embedProvider?: string;
|
|
22
25
|
/**
|
|
23
26
|
* Called after any successful write tool (apply/macros/doc ops). The host
|
|
24
27
|
* (CLI `omg mcp`) uses this to schedule a background embed drain so a block's
|
|
@@ -35,4 +38,9 @@ export interface ServerContext {
|
|
|
35
38
|
*/
|
|
36
39
|
host?: HostInfo;
|
|
37
40
|
}
|
|
41
|
+
/** The message every tool uses for a `semantic(...)` call the server cannot rank. */
|
|
42
|
+
export declare const PROVIDER_UNAVAILABLE_MESSAGE = "semantic(...) needs an embedding provider; none is configured \u2014 set embedding.provider (omg config set embedding.provider <command|url>) and embed the repository (omg embed drain)";
|
|
38
43
|
export declare function buildServer(ctx: ServerContext): McpServer;
|
|
44
|
+
/** The catalog's tool names in registration order with their descriptions —
|
|
45
|
+
* what `spec/surface/tool-descriptions.json` pins for both engines. */
|
|
46
|
+
export declare function toolDescriptions(server: McpServer): Promise<Record<string, string>>;
|