mrplex 0.0.1
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/LICENSE +21 -0
- package/README.md +284 -0
- package/dist/cli/config.d.ts +28 -0
- package/dist/cli/config.js +48 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +13 -0
- package/dist/cli/exit-codes.js +39 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/cli/format.d.ts +22 -0
- package/dist/cli/format.js +148 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/main.d.ts +16 -0
- package/dist/cli/main.js +1518 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/client/kernel-client.d.ts +97 -0
- package/dist/client/kernel-client.js +14 -0
- package/dist/client/kernel-client.js.map +1 -0
- package/dist/client/local.d.ts +26 -0
- package/dist/client/local.js +103 -0
- package/dist/client/local.js.map +1 -0
- package/dist/client/remote-mcp.d.ts +35 -0
- package/dist/client/remote-mcp.js +174 -0
- package/dist/client/remote-mcp.js.map +1 -0
- package/dist/embed/backfill.d.ts +24 -0
- package/dist/embed/backfill.js +33 -0
- package/dist/embed/backfill.js.map +1 -0
- package/dist/embed/chunker.d.ts +31 -0
- package/dist/embed/chunker.js +121 -0
- package/dist/embed/chunker.js.map +1 -0
- package/dist/embed/cmd-hook.d.ts +21 -0
- package/dist/embed/cmd-hook.js +100 -0
- package/dist/embed/cmd-hook.js.map +1 -0
- package/dist/embed/config.d.ts +61 -0
- package/dist/embed/config.js +109 -0
- package/dist/embed/config.js.map +1 -0
- package/dist/embed/hook.d.ts +38 -0
- package/dist/embed/hook.js +56 -0
- package/dist/embed/hook.js.map +1 -0
- package/dist/embed/http-hook.d.ts +13 -0
- package/dist/embed/http-hook.js +39 -0
- package/dist/embed/http-hook.js.map +1 -0
- package/dist/embed/worker.d.ts +62 -0
- package/dist/embed/worker.js +228 -0
- package/dist/embed/worker.js.map +1 -0
- package/dist/fixtures/hwe/characters/albion-ever.md +12 -0
- package/dist/fixtures/hwe/characters/cent.md +11 -0
- package/dist/fixtures/hwe/concepts/cent-demiurge-resonance.md +10 -0
- package/dist/fixtures/hwe/concepts/phase-looper.md +10 -0
- package/dist/fixtures/hwe/entities/demiurge.md +11 -0
- package/dist/fixtures/hwe/entities/monad.md +11 -0
- package/dist/fixtures/hwe/moc/globocorp-network.md +19 -0
- package/dist/fixtures/hwe/organizations/globocorp.md +11 -0
- package/dist/fixtures/hwe/readme.md +24 -0
- package/dist/fixtures/starship/crew/aria-okonkwo.md +14 -0
- package/dist/fixtures/starship/crew/bexley-orr.md +14 -0
- package/dist/fixtures/starship/crew/dax-thorne.md +14 -0
- package/dist/fixtures/starship/crew/isolde-marsh.md +16 -0
- package/dist/fixtures/starship/crew/kestrel-vance.md +15 -0
- package/dist/fixtures/starship/crew/quill-vasquez.md +14 -0
- package/dist/fixtures/starship/crew/soren-halloway.md +16 -0
- package/dist/fixtures/starship/encounters/the-drift-choir.md +14 -0
- package/dist/fixtures/starship/encounters/the-hollow-signal.md +16 -0
- package/dist/fixtures/starship/encounters/the-wandering-buoy.md +14 -0
- package/dist/fixtures/starship/equipment/coolant-loop-b.md +13 -0
- package/dist/fixtures/starship/equipment/emergency-ration-bars.md +12 -0
- package/dist/fixtures/starship/equipment/long-range-sensors.md +13 -0
- package/dist/fixtures/starship/equipment/plasma-manifold-3.md +15 -0
- package/dist/fixtures/starship/equipment/shuttle-corvid.md +13 -0
- package/dist/fixtures/starship/logs/marsh-4419-1.md +13 -0
- package/dist/fixtures/starship/logs/okonkwo-4415-1.md +13 -0
- package/dist/fixtures/starship/logs/orr-4413-1.md +13 -0
- package/dist/fixtures/starship/logs/orr-4420-1.md +12 -0
- package/dist/fixtures/starship/logs/thorne-4413-2.md +13 -0
- package/dist/fixtures/starship/logs/vance-4419-2.md +13 -0
- package/dist/fixtures/starship/misc/the-galley-ficus.md +11 -0
- package/dist/fixtures/starship/missions/the-cinder-run.md +17 -0
- package/dist/fixtures/starship/missions/the-drift-choir.md +17 -0
- package/dist/fixtures/starship/missions/the-hollow-signal.md +17 -0
- package/dist/fixtures/starship/missions/the-silent-beacon.md +17 -0
- package/dist/fixtures/starship/moc/crew.md +19 -0
- package/dist/fixtures/starship/moc/encounters.md +14 -0
- package/dist/fixtures/starship/moc/missions.md +15 -0
- package/dist/fixtures/starship/readme.md +60 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/auth/glob.d.ts +46 -0
- package/dist/kernel/auth/glob.js +112 -0
- package/dist/kernel/auth/glob.js.map +1 -0
- package/dist/kernel/auth/scope.d.ts +53 -0
- package/dist/kernel/auth/scope.js +83 -0
- package/dist/kernel/auth/scope.js.map +1 -0
- package/dist/kernel/casefold.d.ts +37 -0
- package/dist/kernel/casefold.js +44 -0
- package/dist/kernel/casefold.js.map +1 -0
- package/dist/kernel/constants.d.ts +21 -0
- package/dist/kernel/constants.js +22 -0
- package/dist/kernel/constants.js.map +1 -0
- package/dist/kernel/context.d.ts +70 -0
- package/dist/kernel/context.js +114 -0
- package/dist/kernel/context.js.map +1 -0
- package/dist/kernel/deletion.d.ts +50 -0
- package/dist/kernel/deletion.js +83 -0
- package/dist/kernel/deletion.js.map +1 -0
- package/dist/kernel/diff.d.ts +36 -0
- package/dist/kernel/diff.js +61 -0
- package/dist/kernel/diff.js.map +1 -0
- package/dist/kernel/errors.d.ts +37 -0
- package/dist/kernel/errors.js +55 -0
- package/dist/kernel/errors.js.map +1 -0
- package/dist/kernel/frontmatter-input.d.ts +32 -0
- package/dist/kernel/frontmatter-input.js +85 -0
- package/dist/kernel/frontmatter-input.js.map +1 -0
- package/dist/kernel/graph.d.ts +34 -0
- package/dist/kernel/graph.js +559 -0
- package/dist/kernel/graph.js.map +1 -0
- package/dist/kernel/history.d.ts +74 -0
- package/dist/kernel/history.js +143 -0
- package/dist/kernel/history.js.map +1 -0
- package/dist/kernel/kernel.d.ts +162 -0
- package/dist/kernel/kernel.js +675 -0
- package/dist/kernel/kernel.js.map +1 -0
- package/dist/kernel/path-config.d.ts +101 -0
- package/dist/kernel/path-config.js +171 -0
- package/dist/kernel/path-config.js.map +1 -0
- package/dist/kernel/query/ast.d.ts +24 -0
- package/dist/kernel/query/ast.js +52 -0
- package/dist/kernel/query/ast.js.map +1 -0
- package/dist/kernel/query/cel-parse.d.ts +37 -0
- package/dist/kernel/query/cel-parse.js +88 -0
- package/dist/kernel/query/cel-parse.js.map +1 -0
- package/dist/kernel/query/degrees.d.ts +31 -0
- package/dist/kernel/query/degrees.js +135 -0
- package/dist/kernel/query/degrees.js.map +1 -0
- package/dist/kernel/query/graph-ast.d.ts +51 -0
- package/dist/kernel/query/graph-ast.js +139 -0
- package/dist/kernel/query/graph-ast.js.map +1 -0
- package/dist/kernel/query/query.d.ts +56 -0
- package/dist/kernel/query/query.js +276 -0
- package/dist/kernel/query/query.js.map +1 -0
- package/dist/kernel/validation.d.ts +64 -0
- package/dist/kernel/validation.js +183 -0
- package/dist/kernel/validation.js.map +1 -0
- package/dist/kernel/version-id.d.ts +10 -0
- package/dist/kernel/version-id.js +21 -0
- package/dist/kernel/version-id.js.map +1 -0
- package/dist/kernel/wire.d.ts +148 -0
- package/dist/kernel/wire.js +6 -0
- package/dist/kernel/wire.js.map +1 -0
- package/dist/links/backfill.d.ts +28 -0
- package/dist/links/backfill.js +33 -0
- package/dist/links/backfill.js.map +1 -0
- package/dist/links/extract.d.ts +29 -0
- package/dist/links/extract.js +233 -0
- package/dist/links/extract.js.map +1 -0
- package/dist/links/link-config.d.ts +70 -0
- package/dist/links/link-config.js +107 -0
- package/dist/links/link-config.js.map +1 -0
- package/dist/links/maintain.d.ts +37 -0
- package/dist/links/maintain.js +91 -0
- package/dist/links/maintain.js.map +1 -0
- package/dist/links/repair.d.ts +46 -0
- package/dist/links/repair.js +96 -0
- package/dist/links/repair.js.map +1 -0
- package/dist/links/resolve.d.ts +41 -0
- package/dist/links/resolve.js +121 -0
- package/dist/links/resolve.js.map +1 -0
- package/dist/links/stale.d.ts +32 -0
- package/dist/links/stale.js +73 -0
- package/dist/links/stale.js.map +1 -0
- package/dist/markdown/content-hash.d.ts +34 -0
- package/dist/markdown/content-hash.js +46 -0
- package/dist/markdown/content-hash.js.map +1 -0
- package/dist/markdown/frontmatter.d.ts +69 -0
- package/dist/markdown/frontmatter.js +147 -0
- package/dist/markdown/frontmatter.js.map +1 -0
- package/dist/markdown/hash-backfill.d.ts +22 -0
- package/dist/markdown/hash-backfill.js +43 -0
- package/dist/markdown/hash-backfill.js.map +1 -0
- package/dist/mcp/query-syntax.d.ts +20 -0
- package/dist/mcp/query-syntax.js +194 -0
- package/dist/mcp/query-syntax.js.map +1 -0
- package/dist/mcp/render.d.ts +31 -0
- package/dist/mcp/render.js +110 -0
- package/dist/mcp/render.js.map +1 -0
- package/dist/mcp/server.d.ts +60 -0
- package/dist/mcp/server.js +170 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +93 -0
- package/dist/mcp/tools.js +1218 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/rest/conditional.d.ts +37 -0
- package/dist/rest/conditional.js +58 -0
- package/dist/rest/conditional.js.map +1 -0
- package/dist/rest/negotiate.d.ts +33 -0
- package/dist/rest/negotiate.js +54 -0
- package/dist/rest/negotiate.js.map +1 -0
- package/dist/rest/routes.d.ts +37 -0
- package/dist/rest/routes.js +744 -0
- package/dist/rest/routes.js.map +1 -0
- package/dist/seed/fixture-seed.d.ts +35 -0
- package/dist/seed/fixture-seed.js +75 -0
- package/dist/seed/fixture-seed.js.map +1 -0
- package/dist/seed/starship-config.d.ts +3 -0
- package/dist/seed/starship-config.js +5 -0
- package/dist/seed/starship-config.js.map +1 -0
- package/dist/server/headers.d.ts +36 -0
- package/dist/server/headers.js +30 -0
- package/dist/server/headers.js.map +1 -0
- package/dist/server/http-error.d.ts +53 -0
- package/dist/server/http-error.js +104 -0
- package/dist/server/http-error.js.map +1 -0
- package/dist/server/serve.d.ts +56 -0
- package/dist/server/serve.js +135 -0
- package/dist/server/serve.js.map +1 -0
- package/dist/shell/audit.d.ts +32 -0
- package/dist/shell/audit.js +28 -0
- package/dist/shell/audit.js.map +1 -0
- package/dist/shell/guard.d.ts +50 -0
- package/dist/shell/guard.js +176 -0
- package/dist/shell/guard.js.map +1 -0
- package/dist/shell/keys.d.ts +36 -0
- package/dist/shell/keys.js +64 -0
- package/dist/shell/keys.js.map +1 -0
- package/dist/shell/login.d.ts +56 -0
- package/dist/shell/login.js +130 -0
- package/dist/shell/login.js.map +1 -0
- package/dist/shell/oidc.d.ts +62 -0
- package/dist/shell/oidc.js +102 -0
- package/dist/shell/oidc.js.map +1 -0
- package/dist/shell/policy.d.ts +100 -0
- package/dist/shell/policy.js +232 -0
- package/dist/shell/policy.js.map +1 -0
- package/dist/shell/proxy-policy.d.ts +34 -0
- package/dist/shell/proxy-policy.js +120 -0
- package/dist/shell/proxy-policy.js.map +1 -0
- package/dist/shell/proxy.d.ts +47 -0
- package/dist/shell/proxy.js +253 -0
- package/dist/shell/proxy.js.map +1 -0
- package/dist/shell/serve.d.ts +86 -0
- package/dist/shell/serve.js +0 -0
- package/dist/shell/serve.js.map +1 -0
- package/dist/shell/stdio.d.ts +55 -0
- package/dist/shell/stdio.js +63 -0
- package/dist/shell/stdio.js.map +1 -0
- package/dist/storage/registry.d.ts +20 -0
- package/dist/storage/registry.js +38 -0
- package/dist/storage/registry.js.map +1 -0
- package/dist/storage/search-plan.d.ts +93 -0
- package/dist/storage/search-plan.js +15 -0
- package/dist/storage/search-plan.js.map +1 -0
- package/dist/storage/types.d.ts +448 -0
- package/dist/storage/types.js +14 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/storage/versions-since.d.ts +72 -0
- package/dist/storage/versions-since.js +92 -0
- package/dist/storage/versions-since.js.map +1 -0
- package/dist/storage-postgres/adapter.d.ts +17 -0
- package/dist/storage-postgres/adapter.js +782 -0
- package/dist/storage-postgres/adapter.js.map +1 -0
- package/dist/storage-postgres/compile-postgres.d.ts +22 -0
- package/dist/storage-postgres/compile-postgres.js +830 -0
- package/dist/storage-postgres/compile-postgres.js.map +1 -0
- package/dist/storage-postgres/errors.d.ts +34 -0
- package/dist/storage-postgres/errors.js +50 -0
- package/dist/storage-postgres/errors.js.map +1 -0
- package/dist/storage-postgres/migrations/0001_init.sql +113 -0
- package/dist/storage-postgres/migrations/0002_content_hash.sql +7 -0
- package/dist/storage-postgres/migrations/index.d.ts +22 -0
- package/dist/storage-postgres/migrations/index.js +70 -0
- package/dist/storage-postgres/migrations/index.js.map +1 -0
- package/dist/storage-sqlite/adapter.d.ts +2 -0
- package/dist/storage-sqlite/adapter.js +688 -0
- package/dist/storage-sqlite/adapter.js.map +1 -0
- package/dist/storage-sqlite/compile-filter.d.ts +41 -0
- package/dist/storage-sqlite/compile-filter.js +881 -0
- package/dist/storage-sqlite/compile-filter.js.map +1 -0
- package/dist/storage-sqlite/compile-sqlite.d.ts +19 -0
- package/dist/storage-sqlite/compile-sqlite.js +176 -0
- package/dist/storage-sqlite/compile-sqlite.js.map +1 -0
- package/dist/storage-sqlite/migrations/0001_init.sql +123 -0
- package/dist/storage-sqlite/migrations/0002_content_hash.sql +7 -0
- package/dist/storage-sqlite/migrations/index.d.ts +6 -0
- package/dist/storage-sqlite/migrations/index.js +41 -0
- package/dist/storage-sqlite/migrations/index.js.map +1 -0
- package/dist/storage-sqlite/vec.d.ts +40 -0
- package/dist/storage-sqlite/vec.js +62 -0
- package/dist/storage-sqlite/vec.js.map +1 -0
- package/dist/sync/cursor.d.ts +36 -0
- package/dist/sync/cursor.js +66 -0
- package/dist/sync/cursor.js.map +1 -0
- package/dist/sync/daemon.d.ts +40 -0
- package/dist/sync/daemon.js +202 -0
- package/dist/sync/daemon.js.map +1 -0
- package/dist/sync/feed.d.ts +38 -0
- package/dist/sync/feed.js +125 -0
- package/dist/sync/feed.js.map +1 -0
- package/dist/sync/fs-store.d.ts +8 -0
- package/dist/sync/fs-store.js +76 -0
- package/dist/sync/fs-store.js.map +1 -0
- package/dist/sync/intrinsics.d.ts +52 -0
- package/dist/sync/intrinsics.js +76 -0
- package/dist/sync/intrinsics.js.map +1 -0
- package/dist/sync/paths.d.ts +38 -0
- package/dist/sync/paths.js +56 -0
- package/dist/sync/paths.js.map +1 -0
- package/dist/sync/push.d.ts +48 -0
- package/dist/sync/push.js +328 -0
- package/dist/sync/push.js.map +1 -0
- package/dist/sync/reconcile.d.ts +54 -0
- package/dist/sync/reconcile.js +252 -0
- package/dist/sync/reconcile.js.map +1 -0
- package/dist/sync/run.d.ts +27 -0
- package/dist/sync/run.js +46 -0
- package/dist/sync/run.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split, parse, and join Markdown files with YAML frontmatter.
|
|
3
|
+
*
|
|
4
|
+
* The design (§3.2) stores frontmatter twice: `frontmatter_raw` (byte-verbatim
|
|
5
|
+
* YAML source) and `frontmatter` (parsed JSON, a derived query index). The
|
|
6
|
+
* utilities here are the source of truth for that split/join contract.
|
|
7
|
+
*
|
|
8
|
+
* The delimiter grammar we recognize is deliberately narrow and LF-based:
|
|
9
|
+
*
|
|
10
|
+
* ---LF
|
|
11
|
+
* <raw YAML>
|
|
12
|
+
* ---LF
|
|
13
|
+
* <body>
|
|
14
|
+
*
|
|
15
|
+
* That is, a file has frontmatter iff it starts with the literal `---\n` AND a
|
|
16
|
+
* subsequent line consisting of exactly `---\n` (or `---` at EOF). Files that
|
|
17
|
+
* don't match are treated as pure body, with `frontmatter_raw = ""`. Round-trip
|
|
18
|
+
* (`join(split(x)) === x`) holds exactly for the canonical form; a
|
|
19
|
+
* present-but-empty frontmatter (`---\n---\n<body>`) collapses to no-frontmatter
|
|
20
|
+
* (semantically equivalent — `parse("") === {}`).
|
|
21
|
+
*/
|
|
22
|
+
import { parse as parseYaml } from "yaml";
|
|
23
|
+
import { INTRINSIC_SIGIL } from "../kernel/constants.js";
|
|
24
|
+
const DELIM = "---";
|
|
25
|
+
const DELIM_LINE = `${DELIM}\n`;
|
|
26
|
+
/**
|
|
27
|
+
* Match a line that starts with a system-property key: `<sigil>identifier:`
|
|
28
|
+
* at column 0, followed by a colon. Kept intentionally strict — only
|
|
29
|
+
* top-level scalar keys are treated as system props, so nested `$foo:`
|
|
30
|
+
* inside a map body (which would start with whitespace) is untouched.
|
|
31
|
+
*/
|
|
32
|
+
const SYSTEM_PROP_LINE = new RegExp(`^${escapeRegex(INTRINSIC_SIGIL)}([A-Za-z_][A-Za-z0-9_]*):[^\\n]*(?:\\n|$)`, "gm");
|
|
33
|
+
function escapeRegex(s) {
|
|
34
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Split a raw Markdown file into `{ frontmatter_raw, body }`. No parsing yet —
|
|
38
|
+
* `frontmatter_raw` is the exact YAML source between the delimiters.
|
|
39
|
+
*/
|
|
40
|
+
export function split(text) {
|
|
41
|
+
if (!text.startsWith(DELIM_LINE)) {
|
|
42
|
+
return { frontmatter_raw: "", body: text };
|
|
43
|
+
}
|
|
44
|
+
const bodyStartMarker = `\n${DELIM_LINE}`;
|
|
45
|
+
// Search from position (DELIM_LINE.length - 1) — the \n of the opening
|
|
46
|
+
// delimiter's terminator — so the immediately-adjacent form `---\n---\n<body>`
|
|
47
|
+
// (empty frontmatter, closing delim right after opening) is recognized.
|
|
48
|
+
// Starting one character later would skip the `\n---\n` sequence.
|
|
49
|
+
const idx = text.indexOf(bodyStartMarker, DELIM_LINE.length - 1);
|
|
50
|
+
if (idx === -1) {
|
|
51
|
+
// The trailing `---` at EOF (no final newline) also closes the block.
|
|
52
|
+
if (text.endsWith(`\n${DELIM}`)) {
|
|
53
|
+
const closingStart = text.length - DELIM.length - 1;
|
|
54
|
+
return {
|
|
55
|
+
frontmatter_raw: text.slice(DELIM_LINE.length, closingStart + 1),
|
|
56
|
+
body: "",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { frontmatter_raw: "", body: text };
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
frontmatter_raw: text.slice(DELIM_LINE.length, idx + 1),
|
|
63
|
+
body: text.slice(idx + bodyStartMarker.length),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Join back to the canonical file form. Inverse of `split` for canonical inputs.
|
|
68
|
+
*/
|
|
69
|
+
export function join(parts) {
|
|
70
|
+
if (parts.frontmatter_raw === "")
|
|
71
|
+
return parts.body;
|
|
72
|
+
const raw = parts.frontmatter_raw.endsWith("\n")
|
|
73
|
+
? parts.frontmatter_raw
|
|
74
|
+
: `${parts.frontmatter_raw}\n`;
|
|
75
|
+
return `${DELIM_LINE}${raw}${DELIM_LINE}${parts.body}`;
|
|
76
|
+
}
|
|
77
|
+
export class FrontmatterInvalidError extends Error {
|
|
78
|
+
constructor(message) {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = "FrontmatterInvalidError";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Parse the raw YAML source into a JSON object. Empty raw → `{}`. Anything that
|
|
85
|
+
* doesn't parse to a plain map throws `FrontmatterInvalidError` (surfaced as
|
|
86
|
+
* kernel error `frontmatter_invalid` in M1 writes, per design §4.3).
|
|
87
|
+
*/
|
|
88
|
+
export function parse(frontmatterRaw) {
|
|
89
|
+
if (frontmatterRaw === "")
|
|
90
|
+
return {};
|
|
91
|
+
let parsed;
|
|
92
|
+
try {
|
|
93
|
+
parsed = parseYaml(frontmatterRaw);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
throw new FrontmatterInvalidError(`YAML parse error: ${err.message}`);
|
|
97
|
+
}
|
|
98
|
+
if (parsed === null || parsed === undefined)
|
|
99
|
+
return {};
|
|
100
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
101
|
+
throw new FrontmatterInvalidError("frontmatter must parse to a map");
|
|
102
|
+
}
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Append a system property (`$key: value`) as a plain text line to raw YAML
|
|
107
|
+
* source. Non-destructive — never round-trips through the YAML parser, so
|
|
108
|
+
* existing comments, key order, and whitespace are preserved byte-exact.
|
|
109
|
+
*
|
|
110
|
+
* `value` must be a simple scalar (string / number / boolean). Strings are
|
|
111
|
+
* emitted unquoted; callers are responsible for supplying values that are
|
|
112
|
+
* safe as bare YAML scalars (all current system props — version ids, ISO
|
|
113
|
+
* timestamps, user slugs — satisfy this).
|
|
114
|
+
*/
|
|
115
|
+
export function appendSystemProperty(frontmatterRaw, key, value) {
|
|
116
|
+
const line = `${INTRINSIC_SIGIL}${key}: ${value}\n`;
|
|
117
|
+
if (frontmatterRaw === "")
|
|
118
|
+
return line;
|
|
119
|
+
const base = frontmatterRaw.endsWith("\n") ? frontmatterRaw : `${frontmatterRaw}\n`;
|
|
120
|
+
return `${base}${line}`;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Strip every `$key: value` line from raw YAML source and return the parsed
|
|
124
|
+
* key→value map alongside the cleaned source. Non-destructive on the
|
|
125
|
+
* remainder — only the matched lines are removed, everything else stays
|
|
126
|
+
* byte-exact.
|
|
127
|
+
*
|
|
128
|
+
* Values are returned as trimmed strings (leading/trailing whitespace after
|
|
129
|
+
* the colon removed). Callers coerce as needed.
|
|
130
|
+
*/
|
|
131
|
+
export function extractSystemProperties(frontmatterRaw) {
|
|
132
|
+
if (frontmatterRaw === "" || !frontmatterRaw.includes(INTRINSIC_SIGIL)) {
|
|
133
|
+
return { raw: frontmatterRaw, props: {} };
|
|
134
|
+
}
|
|
135
|
+
const props = {};
|
|
136
|
+
const cleaned = frontmatterRaw.replace(SYSTEM_PROP_LINE, (line, key) => {
|
|
137
|
+
const colonIdx = line.indexOf(":");
|
|
138
|
+
const value = line
|
|
139
|
+
.slice(colonIdx + 1)
|
|
140
|
+
.replace(/\n$/, "")
|
|
141
|
+
.trim();
|
|
142
|
+
props[key] = value;
|
|
143
|
+
return "";
|
|
144
|
+
});
|
|
145
|
+
return { raw: cleaned, props };
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/markdown/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-hash backfill (sync/history plan §2.6) — walk rows with
|
|
3
|
+
* `content_hash IS NULL`, compute via the shared hash function, and write them
|
|
4
|
+
* back in batches. Mirrors `links backfill` in shape. Idempotent: rows already
|
|
5
|
+
* hashed are skipped by the `IS NULL` predicate, so re-running only touches
|
|
6
|
+
* what's left.
|
|
7
|
+
*
|
|
8
|
+
* Until a repo is fully backfilled, reads still compute the hash on the fly
|
|
9
|
+
* (kernel `toVersionWire`, §2.6) and the feed/index emit "" for null — so the
|
|
10
|
+
* system is correct throughout the transition; backfill just makes the column
|
|
11
|
+
* authoritative and the `$content_hash` filter complete.
|
|
12
|
+
*/
|
|
13
|
+
import type { Storage } from "../storage/types.js";
|
|
14
|
+
export type HashBackfillReport = {
|
|
15
|
+
hashed: number;
|
|
16
|
+
};
|
|
17
|
+
/** Default rows per batch — bounded so no single tx grows unbounded. */
|
|
18
|
+
export declare const HASH_BACKFILL_BATCH = 500;
|
|
19
|
+
export declare function backfillContentHashes(storage: Storage, opts?: {
|
|
20
|
+
repo_id?: number;
|
|
21
|
+
batch?: number;
|
|
22
|
+
}): Promise<HashBackfillReport>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-hash backfill (sync/history plan §2.6) — walk rows with
|
|
3
|
+
* `content_hash IS NULL`, compute via the shared hash function, and write them
|
|
4
|
+
* back in batches. Mirrors `links backfill` in shape. Idempotent: rows already
|
|
5
|
+
* hashed are skipped by the `IS NULL` predicate, so re-running only touches
|
|
6
|
+
* what's left.
|
|
7
|
+
*
|
|
8
|
+
* Until a repo is fully backfilled, reads still compute the hash on the fly
|
|
9
|
+
* (kernel `toVersionWire`, §2.6) and the feed/index emit "" for null — so the
|
|
10
|
+
* system is correct throughout the transition; backfill just makes the column
|
|
11
|
+
* authoritative and the `$content_hash` filter complete.
|
|
12
|
+
*/
|
|
13
|
+
import { contentHash } from "./content-hash.js";
|
|
14
|
+
/** Default rows per batch — bounded so no single tx grows unbounded. */
|
|
15
|
+
export const HASH_BACKFILL_BATCH = 500;
|
|
16
|
+
export async function backfillContentHashes(storage, opts) {
|
|
17
|
+
const batch = opts?.batch ?? HASH_BACKFILL_BATCH;
|
|
18
|
+
let afterId = 0;
|
|
19
|
+
let hashed = 0;
|
|
20
|
+
for (;;) {
|
|
21
|
+
const rows = await storage.versions_missing_content_hash({
|
|
22
|
+
repo_id: opts?.repo_id,
|
|
23
|
+
after_id: afterId,
|
|
24
|
+
limit: batch,
|
|
25
|
+
});
|
|
26
|
+
if (rows.length === 0)
|
|
27
|
+
break;
|
|
28
|
+
const updates = rows.map((r) => ({
|
|
29
|
+
id: r.id,
|
|
30
|
+
content_hash: contentHash(r.frontmatter_raw, r.body),
|
|
31
|
+
}));
|
|
32
|
+
await storage.versions_set_content_hash(updates);
|
|
33
|
+
hashed += updates.length;
|
|
34
|
+
// Keyset advance: the last id in this batch is the next cursor. Rows just
|
|
35
|
+
// written no longer match `IS NULL`, but keying by id avoids re-scanning
|
|
36
|
+
// them regardless.
|
|
37
|
+
afterId = rows[rows.length - 1]?.id ?? afterId;
|
|
38
|
+
if (rows.length < batch)
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
return { hashed };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=hash-backfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-backfill.js","sourceRoot":"","sources":["../../src/markdown/hash-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhD,wEAAwE;AACxE,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAgB,EAChB,IAA2C;IAE3C,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,mBAAmB,CAAC;IACjD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,6BAA6B,CAAC;YACvD,OAAO,EAAE,IAAI,EAAE,OAAO;YACtB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/B,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC;SACrD,CAAC,CAAC,CAAC;QACJ,MAAM,OAAO,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC;QACjD,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;QACzB,0EAA0E;QAC1E,yEAAyE;QACzE,mBAAmB;QACnB,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,OAAO,CAAC;QAC/C,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK;YAAE,MAAM;IACjC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query-language reference — the single user-facing source of truth for the
|
|
3
|
+
* `query` tool's filter language. Served whole by the `query_syntax` MCP
|
|
4
|
+
* tool, summarized in the `query` tool's description (tools.ts), and pointed
|
|
5
|
+
* at by `filter_invalid` error hints.
|
|
6
|
+
*
|
|
7
|
+
* The implementation truth lives in the compilers (storage-sqlite/
|
|
8
|
+
* compile-filter.ts, storage-postgres/compile-postgres.ts) and the graph
|
|
9
|
+
* recognizers (kernel/query/graph-ast.ts). query-syntax.test.ts compiles
|
|
10
|
+
* every documented construct against the real parser+compiler so this doc
|
|
11
|
+
* can't silently drift from the code: add an intrinsic or graph predicate
|
|
12
|
+
* to the compilers and the test forces an update here.
|
|
13
|
+
*/
|
|
14
|
+
/** Kernel intrinsics, without the `$` sigil. Mirrors INTRINSIC_COLUMNS. */
|
|
15
|
+
export declare const DOCUMENTED_INTRINSICS: readonly ["path", "updated_at", "body", "content_hash"];
|
|
16
|
+
/** Graph membership predicates, without the `$` sigil. */
|
|
17
|
+
export declare const DOCUMENTED_GRAPH_MEMBERSHIP: readonly ["in", "has", "in_static", "has_static"];
|
|
18
|
+
/** Graph collections, without the `$` sigil (call with `()`). */
|
|
19
|
+
export declare const DOCUMENTED_GRAPH_COLLECTIONS: readonly ["backlinks", "links", "backlinks_static", "links_static"];
|
|
20
|
+
export declare const QUERY_SYNTAX_DOC = "# mrplex query language\n\nA query composes up to three modes; when more than one is present they\nintersect (AND):\n\n- `filter` \u2014 a CEL boolean expression over frontmatter fields and\n `$`-prefixed intrinsics\n- `text` \u2014 full-text search over document bodies\n- `semantic` \u2014 semantic similarity over embeddings\n\nResults are **current document versions only** (history is not searched).\nOrdering: semantic score when `semantic` is present, else text relevance when\n`text` is present, else `$updated_at` descending. Default `limit` is 50.\n\n`repo` selects which repos to search: a slug (`\"notes\"`), a\ngitignore-style glob (`\"team-*\"`), or a list of either. Omitted = every\nrepo the caller can see.\n\n## Result shape \u2014 `select`\n\n`query` returns **lean projected hits**, not full documents. `select` names\nthe fields you want; each hit is an object of just those fields. Names are\neither bare frontmatter keys (`title`, `status`) or `$`-intrinsics\n(`$path`, `$repo`, `$version_id`, `$prev_version_id`, `$next_version_id`,\n`$updated_at`, `$author`, `$body`, `$content_hash`, `$semantic_score`). Any field that isn't\nuser frontmatter carries the `$` sigil, so a frontmatter key literally named\n`path` never collides with the system's `$path`.\n\n`select` defaults to `[\"$path\"]` \u2014 **that is all you get** unless you pass\n`select`. Each default hit is `{ \"$path\": \"\u2026\" }` only: no body, no\nfrontmatter, no version id. Document bodies travel only when you ask for\n`$body`, so listing/filtering is cheap by default. When querying more than\none repo, add `$repo` to tell the paths apart. To recover whole documents,\nuse `docs.get` (one path) or `docs.get_many` (batch).\n\n## CEL filter basics\n\nBare identifiers name frontmatter keys: `status == \"published\"`. Nested\nmaps use dots: `meta.owner == \"alice\"`. A missing key never matches (the\npredicate is false, not an error).\n\nOperators: `==` `!=` `<` `<=` `>` `>=` `&&` `||` `!`, with\nparentheses. String values are single- or double-quoted.\n\nString functions (free-standing or method form):\n\n- `contains(x, \"sub\")` / `x.contains(\"sub\")` \u2014 substring match\n- `x.startsWith(\"prefix\")`, `x.endsWith(\"suffix\")`\n- `x.matches(\"^regex$\")` \u2014 regular-expression match\n- `size(x)` \u2014 string length (also list length; see `list()`)\n\nNot supported: list/struct literals, arithmetic, ternary, `in` without\n`list()`.\n\n## $-intrinsics (document properties)\n\nFrontmatter keys are user territory; kernel-owned document properties are\n`$`-prefixed, so they can never collide with a frontmatter field (a doc\nwith a frontmatter key literally named `path` stays queryable as bare\n`path`):\n\n- `$path` \u2014 the document's current path. `$path.startsWith(\"guides/\")`,\n `$path.matches(\"^guides/[^/]+\\\\.md$\")`, `$path.endsWith(\".md\")`\n- `$updated_at` \u2014 when the document was last written, an ISO-8601 UTC\n string; compare lexicographically: `$updated_at >= \"2026-08-01\"`\n- `$body` \u2014 the markdown body text: `contains($body, \"pricing\")`\n- `$content_hash` \u2014 the SHA-256 (bare hex) of the document's canonical\n content, the same fingerprint injected into materialized files;\n `$content_hash == \"1a2b\u2026\"` matches an exact known version's content\n\nIntrinsics have no subfields. There is no separate path-glob query\nargument; express path matching in the filter via `$path`.\n\n## list() \u2014 scalar-or-list frontmatter\n\nFrontmatter fields like `tags` may hold a scalar or a list\n(`tags: pricing` vs `tags: [pricing, saas]`). Wrap the field in\n`list()` to treat both shapes uniformly; missing and null coerce to\n`[]`:\n\n- `\"pricing\" in list(tags)` \u2014 membership, either shape\n- `size(list(tags)) > 2` \u2014 count (a scalar counts as 1, missing as 0)\n- `list(tags).all(t, t.startsWith(\"p\"))`\n- `list(authors).exists(a, a == \"alice\")`\n\n`list()` only makes sense inside `in`, `size(...)`, `.all`, or\n`.exists`; a bare `list(tags) == \"x\"` is rejected.\n\n## Link-graph predicates\n\nThe link graph indexes markdown links (inline, reference,\n`[[wikilinks]]`, and configured frontmatter fields). All graph\npredicates respect the caller's read scope \u2014 the visible graph equals the\nreadable graph. Globs are gitignore-style: a bare name matches at any\ndepth, `*` matches within a path segment, `**` crosses segments, a\nleading `/` anchors to the repo root.\n\nBoolean membership:\n\n- `$in(glob [, field])` \u2014 true when some doc matching `glob` links TO\n this doc (this doc is a member of that doc's set). The optional\n `field` restricts to links written in that frontmatter field, or\n `\"$body\"` for body links. `$in(\"moc/employees.md\")` finds everything\n the employees map-of-content references.\n- `$has(glob [, field])` \u2014 true when this doc links to a target matching\n `glob`. Dangling targets (paths that aren't currently live docs) still\n count. `$has(\"projects/**\", \"parent\")` \u2014 docs whose `parent` field\n references any project.\n\nCollections \u2014 only usable with `.size()`, `.exists()`, `.all()`:\n\n- `$backlinks()` \u2014 the docs linking to this doc\n- `$links()` \u2014 the docs this doc links to\n- `$links().size() == 0` \u2014 leaf docs\n- `.exists(d, pred)` / `.all(d, pred)` \u2014 `d.field` reads the other\n doc's frontmatter; `d.$path` / `d.$updated_at` / `d.$body` read its\n intrinsics: `$backlinks().exists(d, d.status == \"draft\")`\n\nSet algebra composes: `$in(\"moc/**\") && !$in(\"moc/contractors.md\")`;\norphans (linked from nowhere) are `!$in(\"**\")`.\n\nThe `_static` variants (`$in_static`, `$has_static`,\n`$backlinks_static()`, `$links_static()`) match only\nstatically-written links, now and forever. The bare forms are identical\ntoday, but will transparently widen to include query-derived (dynamic)\nmembership in a future release. `_dyn` forms are reserved and rejected.\n\n### `$degrees` \u2014 graph mode only\n\n`$degrees` is a scalar available **only inside a `graph` call's\n`filter`**, never in a `query` filter (using it there is a\n`filter_invalid` error). It is the minimum number of hops from the\nnearest root at which the current document was reached, under that call's\ndirection lens. In `graph`, `filter` is *visibility*, not selection: a\nnon-matching document is hidden and blocks paths through itself. This lets\na filter shape the traversal, e.g. `$degrees <= 1 || type == \"person\"` \u2014\ninclude everything within one hop, but keep following `person` documents\ndeeper. See the `graph` tool for the full read surface.\n\n## text \u2014 full-text search\n\nPortable syntax across storage backends: space-separated terms (implicit\nAND) and `\"quoted phrases\"`. Anything fancier (OR, NEAR, prefix `*`)\nis backend-specific \u2014 it may error or behave differently between SQLite\nFTS5 and Postgres.\n\n## semantic \u2014 embedding search\n\n`semantic: \"tiered SaaS pricing\"` retrieves by embedding similarity and\ncomposes with `filter`/`text` (they prune its candidates). Requires\nthe server to be configured with an embedding hook; otherwise the query\nfails with `semantic_unavailable`.\n\nWhen `semantic` is active, `$semantic_score` in `select` projects the\ncosine similarity used for ordering (1 = identical, -1 = opposite). Result\narray order is the final ordinal rank.\n\n## Visibility flags\n\nBy default, documents whose path contains a hidden segment (e.g.\n`.drafts/\u2026`) or a system segment (e.g. `:deleted/\u2026`) are excluded.\nSet `include_hidden: true` or `include_system: true` to surface them \u2014\nbrowsing `:deleted/` is how you find documents to restore.\n\n## Errors\n\nA bad filter fails with code `filter_invalid` and a `data.reason`\nsaying what was wrong; fix the expression and retry.\n";
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query-language reference — the single user-facing source of truth for the
|
|
3
|
+
* `query` tool's filter language. Served whole by the `query_syntax` MCP
|
|
4
|
+
* tool, summarized in the `query` tool's description (tools.ts), and pointed
|
|
5
|
+
* at by `filter_invalid` error hints.
|
|
6
|
+
*
|
|
7
|
+
* The implementation truth lives in the compilers (storage-sqlite/
|
|
8
|
+
* compile-filter.ts, storage-postgres/compile-postgres.ts) and the graph
|
|
9
|
+
* recognizers (kernel/query/graph-ast.ts). query-syntax.test.ts compiles
|
|
10
|
+
* every documented construct against the real parser+compiler so this doc
|
|
11
|
+
* can't silently drift from the code: add an intrinsic or graph predicate
|
|
12
|
+
* to the compilers and the test forces an update here.
|
|
13
|
+
*/
|
|
14
|
+
/** Kernel intrinsics, without the `$` sigil. Mirrors INTRINSIC_COLUMNS. */
|
|
15
|
+
export const DOCUMENTED_INTRINSICS = ["path", "updated_at", "body", "content_hash"];
|
|
16
|
+
/** Graph membership predicates, without the `$` sigil. */
|
|
17
|
+
export const DOCUMENTED_GRAPH_MEMBERSHIP = ["in", "has", "in_static", "has_static"];
|
|
18
|
+
/** Graph collections, without the `$` sigil (call with `()`). */
|
|
19
|
+
export const DOCUMENTED_GRAPH_COLLECTIONS = [
|
|
20
|
+
"backlinks",
|
|
21
|
+
"links",
|
|
22
|
+
"backlinks_static",
|
|
23
|
+
"links_static",
|
|
24
|
+
];
|
|
25
|
+
export const QUERY_SYNTAX_DOC = `# mrplex query language
|
|
26
|
+
|
|
27
|
+
A query composes up to three modes; when more than one is present they
|
|
28
|
+
intersect (AND):
|
|
29
|
+
|
|
30
|
+
- \`filter\` — a CEL boolean expression over frontmatter fields and
|
|
31
|
+
\`$\`-prefixed intrinsics
|
|
32
|
+
- \`text\` — full-text search over document bodies
|
|
33
|
+
- \`semantic\` — semantic similarity over embeddings
|
|
34
|
+
|
|
35
|
+
Results are **current document versions only** (history is not searched).
|
|
36
|
+
Ordering: semantic score when \`semantic\` is present, else text relevance when
|
|
37
|
+
\`text\` is present, else \`$updated_at\` descending. Default \`limit\` is 50.
|
|
38
|
+
|
|
39
|
+
\`repo\` selects which repos to search: a slug (\`"notes"\`), a
|
|
40
|
+
gitignore-style glob (\`"team-*"\`), or a list of either. Omitted = every
|
|
41
|
+
repo the caller can see.
|
|
42
|
+
|
|
43
|
+
## Result shape — \`select\`
|
|
44
|
+
|
|
45
|
+
\`query\` returns **lean projected hits**, not full documents. \`select\` names
|
|
46
|
+
the fields you want; each hit is an object of just those fields. Names are
|
|
47
|
+
either bare frontmatter keys (\`title\`, \`status\`) or \`$\`-intrinsics
|
|
48
|
+
(\`$path\`, \`$repo\`, \`$version_id\`, \`$prev_version_id\`, \`$next_version_id\`,
|
|
49
|
+
\`$updated_at\`, \`$author\`, \`$body\`, \`$content_hash\`, \`$semantic_score\`). Any field that isn't
|
|
50
|
+
user frontmatter carries the \`$\` sigil, so a frontmatter key literally named
|
|
51
|
+
\`path\` never collides with the system's \`$path\`.
|
|
52
|
+
|
|
53
|
+
\`select\` defaults to \`["$path"]\` — **that is all you get** unless you pass
|
|
54
|
+
\`select\`. Each default hit is \`{ "$path": "…" }\` only: no body, no
|
|
55
|
+
frontmatter, no version id. Document bodies travel only when you ask for
|
|
56
|
+
\`$body\`, so listing/filtering is cheap by default. When querying more than
|
|
57
|
+
one repo, add \`$repo\` to tell the paths apart. To recover whole documents,
|
|
58
|
+
use \`docs.get\` (one path) or \`docs.get_many\` (batch).
|
|
59
|
+
|
|
60
|
+
## CEL filter basics
|
|
61
|
+
|
|
62
|
+
Bare identifiers name frontmatter keys: \`status == "published"\`. Nested
|
|
63
|
+
maps use dots: \`meta.owner == "alice"\`. A missing key never matches (the
|
|
64
|
+
predicate is false, not an error).
|
|
65
|
+
|
|
66
|
+
Operators: \`==\` \`!=\` \`<\` \`<=\` \`>\` \`>=\` \`&&\` \`||\` \`!\`, with
|
|
67
|
+
parentheses. String values are single- or double-quoted.
|
|
68
|
+
|
|
69
|
+
String functions (free-standing or method form):
|
|
70
|
+
|
|
71
|
+
- \`contains(x, "sub")\` / \`x.contains("sub")\` — substring match
|
|
72
|
+
- \`x.startsWith("prefix")\`, \`x.endsWith("suffix")\`
|
|
73
|
+
- \`x.matches("^regex$")\` — regular-expression match
|
|
74
|
+
- \`size(x)\` — string length (also list length; see \`list()\`)
|
|
75
|
+
|
|
76
|
+
Not supported: list/struct literals, arithmetic, ternary, \`in\` without
|
|
77
|
+
\`list()\`.
|
|
78
|
+
|
|
79
|
+
## $-intrinsics (document properties)
|
|
80
|
+
|
|
81
|
+
Frontmatter keys are user territory; kernel-owned document properties are
|
|
82
|
+
\`$\`-prefixed, so they can never collide with a frontmatter field (a doc
|
|
83
|
+
with a frontmatter key literally named \`path\` stays queryable as bare
|
|
84
|
+
\`path\`):
|
|
85
|
+
|
|
86
|
+
- \`$path\` — the document's current path. \`$path.startsWith("guides/")\`,
|
|
87
|
+
\`$path.matches("^guides/[^/]+\\\\.md$")\`, \`$path.endsWith(".md")\`
|
|
88
|
+
- \`$updated_at\` — when the document was last written, an ISO-8601 UTC
|
|
89
|
+
string; compare lexicographically: \`$updated_at >= "2026-08-01"\`
|
|
90
|
+
- \`$body\` — the markdown body text: \`contains($body, "pricing")\`
|
|
91
|
+
- \`$content_hash\` — the SHA-256 (bare hex) of the document's canonical
|
|
92
|
+
content, the same fingerprint injected into materialized files;
|
|
93
|
+
\`$content_hash == "1a2b…"\` matches an exact known version's content
|
|
94
|
+
|
|
95
|
+
Intrinsics have no subfields. There is no separate path-glob query
|
|
96
|
+
argument; express path matching in the filter via \`$path\`.
|
|
97
|
+
|
|
98
|
+
## list() — scalar-or-list frontmatter
|
|
99
|
+
|
|
100
|
+
Frontmatter fields like \`tags\` may hold a scalar or a list
|
|
101
|
+
(\`tags: pricing\` vs \`tags: [pricing, saas]\`). Wrap the field in
|
|
102
|
+
\`list()\` to treat both shapes uniformly; missing and null coerce to
|
|
103
|
+
\`[]\`:
|
|
104
|
+
|
|
105
|
+
- \`"pricing" in list(tags)\` — membership, either shape
|
|
106
|
+
- \`size(list(tags)) > 2\` — count (a scalar counts as 1, missing as 0)
|
|
107
|
+
- \`list(tags).all(t, t.startsWith("p"))\`
|
|
108
|
+
- \`list(authors).exists(a, a == "alice")\`
|
|
109
|
+
|
|
110
|
+
\`list()\` only makes sense inside \`in\`, \`size(...)\`, \`.all\`, or
|
|
111
|
+
\`.exists\`; a bare \`list(tags) == "x"\` is rejected.
|
|
112
|
+
|
|
113
|
+
## Link-graph predicates
|
|
114
|
+
|
|
115
|
+
The link graph indexes markdown links (inline, reference,
|
|
116
|
+
\`[[wikilinks]]\`, and configured frontmatter fields). All graph
|
|
117
|
+
predicates respect the caller's read scope — the visible graph equals the
|
|
118
|
+
readable graph. Globs are gitignore-style: a bare name matches at any
|
|
119
|
+
depth, \`*\` matches within a path segment, \`**\` crosses segments, a
|
|
120
|
+
leading \`/\` anchors to the repo root.
|
|
121
|
+
|
|
122
|
+
Boolean membership:
|
|
123
|
+
|
|
124
|
+
- \`$in(glob [, field])\` — true when some doc matching \`glob\` links TO
|
|
125
|
+
this doc (this doc is a member of that doc's set). The optional
|
|
126
|
+
\`field\` restricts to links written in that frontmatter field, or
|
|
127
|
+
\`"$body"\` for body links. \`$in("moc/employees.md")\` finds everything
|
|
128
|
+
the employees map-of-content references.
|
|
129
|
+
- \`$has(glob [, field])\` — true when this doc links to a target matching
|
|
130
|
+
\`glob\`. Dangling targets (paths that aren't currently live docs) still
|
|
131
|
+
count. \`$has("projects/**", "parent")\` — docs whose \`parent\` field
|
|
132
|
+
references any project.
|
|
133
|
+
|
|
134
|
+
Collections — only usable with \`.size()\`, \`.exists()\`, \`.all()\`:
|
|
135
|
+
|
|
136
|
+
- \`$backlinks()\` — the docs linking to this doc
|
|
137
|
+
- \`$links()\` — the docs this doc links to
|
|
138
|
+
- \`$links().size() == 0\` — leaf docs
|
|
139
|
+
- \`.exists(d, pred)\` / \`.all(d, pred)\` — \`d.field\` reads the other
|
|
140
|
+
doc's frontmatter; \`d.$path\` / \`d.$updated_at\` / \`d.$body\` read its
|
|
141
|
+
intrinsics: \`$backlinks().exists(d, d.status == "draft")\`
|
|
142
|
+
|
|
143
|
+
Set algebra composes: \`$in("moc/**") && !$in("moc/contractors.md")\`;
|
|
144
|
+
orphans (linked from nowhere) are \`!$in("**")\`.
|
|
145
|
+
|
|
146
|
+
The \`_static\` variants (\`$in_static\`, \`$has_static\`,
|
|
147
|
+
\`$backlinks_static()\`, \`$links_static()\`) match only
|
|
148
|
+
statically-written links, now and forever. The bare forms are identical
|
|
149
|
+
today, but will transparently widen to include query-derived (dynamic)
|
|
150
|
+
membership in a future release. \`_dyn\` forms are reserved and rejected.
|
|
151
|
+
|
|
152
|
+
### \`$degrees\` — graph mode only
|
|
153
|
+
|
|
154
|
+
\`$degrees\` is a scalar available **only inside a \`graph\` call's
|
|
155
|
+
\`filter\`**, never in a \`query\` filter (using it there is a
|
|
156
|
+
\`filter_invalid\` error). It is the minimum number of hops from the
|
|
157
|
+
nearest root at which the current document was reached, under that call's
|
|
158
|
+
direction lens. In \`graph\`, \`filter\` is *visibility*, not selection: a
|
|
159
|
+
non-matching document is hidden and blocks paths through itself. This lets
|
|
160
|
+
a filter shape the traversal, e.g. \`$degrees <= 1 || type == "person"\` —
|
|
161
|
+
include everything within one hop, but keep following \`person\` documents
|
|
162
|
+
deeper. See the \`graph\` tool for the full read surface.
|
|
163
|
+
|
|
164
|
+
## text — full-text search
|
|
165
|
+
|
|
166
|
+
Portable syntax across storage backends: space-separated terms (implicit
|
|
167
|
+
AND) and \`"quoted phrases"\`. Anything fancier (OR, NEAR, prefix \`*\`)
|
|
168
|
+
is backend-specific — it may error or behave differently between SQLite
|
|
169
|
+
FTS5 and Postgres.
|
|
170
|
+
|
|
171
|
+
## semantic — embedding search
|
|
172
|
+
|
|
173
|
+
\`semantic: "tiered SaaS pricing"\` retrieves by embedding similarity and
|
|
174
|
+
composes with \`filter\`/\`text\` (they prune its candidates). Requires
|
|
175
|
+
the server to be configured with an embedding hook; otherwise the query
|
|
176
|
+
fails with \`semantic_unavailable\`.
|
|
177
|
+
|
|
178
|
+
When \`semantic\` is active, \`$semantic_score\` in \`select\` projects the
|
|
179
|
+
cosine similarity used for ordering (1 = identical, -1 = opposite). Result
|
|
180
|
+
array order is the final ordinal rank.
|
|
181
|
+
|
|
182
|
+
## Visibility flags
|
|
183
|
+
|
|
184
|
+
By default, documents whose path contains a hidden segment (e.g.
|
|
185
|
+
\`.drafts/…\`) or a system segment (e.g. \`:deleted/…\`) are excluded.
|
|
186
|
+
Set \`include_hidden: true\` or \`include_system: true\` to surface them —
|
|
187
|
+
browsing \`:deleted/\` is how you find documents to restore.
|
|
188
|
+
|
|
189
|
+
## Errors
|
|
190
|
+
|
|
191
|
+
A bad filter fails with code \`filter_invalid\` and a \`data.reason\`
|
|
192
|
+
saying what was wrong; fix the expression and retry.
|
|
193
|
+
`;
|
|
194
|
+
//# sourceMappingURL=query-syntax.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-syntax.js","sourceRoot":"","sources":["../../src/mcp/query-syntax.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,CAAU,CAAC;AAE7F,0DAA0D;AAC1D,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,CAAU,CAAC;AAE7F,iEAAiE;AACjE,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,WAAW;IACX,OAAO;IACP,kBAAkB;IAClB,cAAc;CACN,CAAC;AAEX,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwK/B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-text rendering of kernel results for MCP tool `content`. The
|
|
3
|
+
* structuredContent carries the wire object verbatim; this is the
|
|
4
|
+
* best-effort text shape an agent (or a debug console) would want to see
|
|
5
|
+
* alongside.
|
|
6
|
+
*
|
|
7
|
+
* Kept intentionally small — the CLI's pretty-printers already speak to
|
|
8
|
+
* humans; the MCP text rendering just has to be *readable*, not pretty.
|
|
9
|
+
*/
|
|
10
|
+
import type { GraphResult, QueryHit, Repo, Version } from "../kernel/wire.js";
|
|
11
|
+
export declare function renderJson(x: unknown): string;
|
|
12
|
+
export declare function renderRepoList(repos: Repo[]): string;
|
|
13
|
+
export declare function renderVersion(v: Version): string;
|
|
14
|
+
export declare function renderVersionList(versions: Version[]): string;
|
|
15
|
+
/** Text half for `docs_get_many`: versions plus a trailing errors block. */
|
|
16
|
+
export declare function renderDocGetManyText(items: Version[], errors: {
|
|
17
|
+
path: string;
|
|
18
|
+
code: string;
|
|
19
|
+
}[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* Text half for `query`'s projected hits. Since `select` decides which fields
|
|
22
|
+
* are present, render each hit as its `key: value` pairs in projection order —
|
|
23
|
+
* a compact, readable line per hit rather than a fixed column layout.
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderQueryHitList(hits: QueryHit[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* The graph tool's fixed text half (docs/graph-plan.md §2.3): a dense
|
|
28
|
+
* adjacency listing grouped by field, one block per document. Pure function
|
|
29
|
+
* of the structured result — the same rendering the CLI uses by default.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderGraphSummary(result: GraphResult): string;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human-text rendering of kernel results for MCP tool `content`. The
|
|
3
|
+
* structuredContent carries the wire object verbatim; this is the
|
|
4
|
+
* best-effort text shape an agent (or a debug console) would want to see
|
|
5
|
+
* alongside.
|
|
6
|
+
*
|
|
7
|
+
* Kept intentionally small — the CLI's pretty-printers already speak to
|
|
8
|
+
* humans; the MCP text rendering just has to be *readable*, not pretty.
|
|
9
|
+
*/
|
|
10
|
+
export function renderJson(x) {
|
|
11
|
+
return JSON.stringify(x, null, 2);
|
|
12
|
+
}
|
|
13
|
+
export function renderRepoList(repos) {
|
|
14
|
+
if (repos.length === 0)
|
|
15
|
+
return "(no repos)";
|
|
16
|
+
return repos.map((r) => `${r.repo}${r.path_config ? " (custom path_config)" : ""}`).join("\n");
|
|
17
|
+
}
|
|
18
|
+
export function renderVersion(v) {
|
|
19
|
+
return `${v.repo}/${v.path} @${v.version_id} by ${v.author} ${v.created_at}`;
|
|
20
|
+
}
|
|
21
|
+
export function renderVersionList(versions) {
|
|
22
|
+
if (versions.length === 0)
|
|
23
|
+
return "(no results)";
|
|
24
|
+
return versions.map(renderVersion).join("\n");
|
|
25
|
+
}
|
|
26
|
+
/** Text half for `docs_get_many`: versions plus a trailing errors block. */
|
|
27
|
+
export function renderDocGetManyText(items, errors) {
|
|
28
|
+
const parts = [renderVersionList(items)];
|
|
29
|
+
if (errors.length > 0) {
|
|
30
|
+
parts.push("errors:");
|
|
31
|
+
for (const e of errors)
|
|
32
|
+
parts.push(`${e.path}: ${e.code}`);
|
|
33
|
+
}
|
|
34
|
+
return parts.join("\n");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Text half for `query`'s projected hits. Since `select` decides which fields
|
|
38
|
+
* are present, render each hit as its `key: value` pairs in projection order —
|
|
39
|
+
* a compact, readable line per hit rather than a fixed column layout.
|
|
40
|
+
*/
|
|
41
|
+
export function renderQueryHitList(hits) {
|
|
42
|
+
if (hits.length === 0)
|
|
43
|
+
return "(no results)";
|
|
44
|
+
return hits
|
|
45
|
+
.map((hit) => Object.entries(hit)
|
|
46
|
+
.map(([key, value]) => `${key}: ${renderHitValue(value)}`)
|
|
47
|
+
.join(" "))
|
|
48
|
+
.join("\n");
|
|
49
|
+
}
|
|
50
|
+
function renderHitValue(value) {
|
|
51
|
+
if (value === null)
|
|
52
|
+
return "null";
|
|
53
|
+
if (typeof value === "string")
|
|
54
|
+
return value;
|
|
55
|
+
return JSON.stringify(value);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The graph tool's fixed text half (docs/graph-plan.md §2.3): a dense
|
|
59
|
+
* adjacency listing grouped by field, one block per document. Pure function
|
|
60
|
+
* of the structured result — the same rendering the CLI uses by default.
|
|
61
|
+
*/
|
|
62
|
+
export function renderGraphSummary(result) {
|
|
63
|
+
if (result.documents.length === 0)
|
|
64
|
+
return "(no documents)";
|
|
65
|
+
// Group each document's outgoing/incoming links by field for compact display.
|
|
66
|
+
// out: source == doc; in: target == doc.
|
|
67
|
+
const outByDoc = new Map();
|
|
68
|
+
const inByDoc = new Map();
|
|
69
|
+
const add = (byDoc, doc, field, other) => {
|
|
70
|
+
let byField = byDoc.get(doc);
|
|
71
|
+
if (!byField) {
|
|
72
|
+
byField = new Map();
|
|
73
|
+
byDoc.set(doc, byField);
|
|
74
|
+
}
|
|
75
|
+
const list = byField.get(field);
|
|
76
|
+
if (list)
|
|
77
|
+
list.push(other);
|
|
78
|
+
else
|
|
79
|
+
byField.set(field, [other]);
|
|
80
|
+
};
|
|
81
|
+
for (const l of result.links) {
|
|
82
|
+
add(outByDoc, l.source, l.field, l.target);
|
|
83
|
+
add(inByDoc, l.target, l.field, l.source);
|
|
84
|
+
}
|
|
85
|
+
const lines = [];
|
|
86
|
+
for (const doc of result.documents) {
|
|
87
|
+
lines.push(`${doc.$path} (${doc.$degrees})`);
|
|
88
|
+
const out = outByDoc.get(doc.$path);
|
|
89
|
+
if (out) {
|
|
90
|
+
for (const field of [...out.keys()].sort()) {
|
|
91
|
+
lines.push(` →(${field}) ${out.get(field).sort().join(", ")}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const inc = inByDoc.get(doc.$path);
|
|
95
|
+
if (inc) {
|
|
96
|
+
for (const field of [...inc.keys()].sort()) {
|
|
97
|
+
lines.push(` ←(${field}) ${inc.get(field).sort().join(", ")}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const trailer = [];
|
|
102
|
+
if (result.frontier.length > 0)
|
|
103
|
+
trailer.push(`frontier: ${result.frontier.join(" · ")}`);
|
|
104
|
+
trailer.push(`complete through ${result.complete_degrees} degree${result.complete_degrees === 1 ? "" : "s"}`);
|
|
105
|
+
if (result.truncated)
|
|
106
|
+
trailer.push("truncated");
|
|
107
|
+
lines.push(trailer.join(" · "));
|
|
108
|
+
return lines.join("\n");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/mcp/render.ts"],"names":[],"mappings":"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"}
|