@cavelang/cli 0.15.0 → 0.20.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 +7 -3
- package/dist/src/cli.d.ts +26 -3
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +338 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/main.js +6 -1
- package/dist/src/main.js.map +1 -1
- package/package.json +16 -15
- package/src/cli.ts +346 -12
- package/src/index.ts +2 -2
- package/src/main.ts +5 -1
package/README.md
CHANGED
|
@@ -31,15 +31,19 @@ Every command answers `--help` with its options and examples (also
|
|
|
31
31
|
| `highlight [file…]` | | ANSI syntax colors from the tree-sitter grammar's own highlight query (see [`@cavelang/highlight`](../highlight)); `cave export` colors the same way on a terminal. |
|
|
32
32
|
| `add [--db p] [file…]` | `--strict`, `--check`, `--no-prelude`, `--no-src` | Ingest. Lenient by default (problems on stderr, valid lines land); `--strict` rolls back on any problem; `--check` is the shape gate (spec §20.3) — the append rolls back if it introduces new expectation violations; `--no-prelude` starts from an empty registry instead of the standard §5.5 pairs. Claims without a `@src:` context are stamped `@src:cli` (spec §9.5); `--no-src` opts out. |
|
|
33
33
|
| `import [--db p] [file…]` | `--strict`, `--no-prelude` | Restore/merge a database from CAVE text — `add` minus provenance stamping, because canonical text *is* the interchange format and replayed claims must keep their exported claim keys (spec §9.5). |
|
|
34
|
-
| `query [--db p] <pattern…>` | `--json`, `--all`, `--no-prelude` | CAVE-Q. Extra positionals join as lines, so `WHERE` filters ride as separate arguments. Bindings print as `?x = value`; fully bound patterns print the matched raw line (or the pattern itself for transitive matches, which carry no row). `--no-prelude` aligns the read-time registry with a store written via `add --no-prelude`. |
|
|
34
|
+
| `query [--db p] <pattern…>` | `--json`, `--all`, `--aliases`, `--as-of <t>`, `--resolve`, `--no-prelude` | CAVE-Q. Extra positionals join as lines, so `WHERE` filters ride as separate arguments. Bindings print as `?x = value`; fully bound patterns print the matched raw line (or the pattern itself for transitive matches, which carry no row). `--aliases` widens matching through the §13.6 closure; `--as-of <t>` reconstructs belief at a past date/timestamp/tx (spec §12.3); `--resolve` matches only the winners the §26 resolution policy picks among contested facts (incompatible with `--all`). `--no-prelude` aligns the read-time registry with a store written via `add --no-prelude`. |
|
|
35
|
+
| `resolve [--db p]` | `--aliases`, `--policy`, `--json`, `--no-prelude` | Contested facts under the §26 resolution policy: every fact more than one belief series speaks about, candidates ranked (precedence class, reliability-weighted confidence, tx) with the winner first — what `query --resolve` would match. `--policy` prints the effective policy instead: the built-in precedence ladder merged with in-band `source/<name> HAS precedence:` / `HAS reliability:` declarations. |
|
|
35
36
|
| `derive [--db p] [rules.cave…]` | `--dry-run`, `--full`, `--aliases`, `--min-conf <p>`, `--max-passes <n>`, `--list`, `--retract <r>`, `--json`, `--no-prelude` | Declare and fire rules (spec §24, see [`@cavelang/rules`](../rules)): `premises => conclusion` forward chaining over current beliefs, `BECAUSE`/`VIA` lineage, watermark-incremental and idempotent; retracts conclusions whose premises no longer hold. |
|
|
36
37
|
| `act [--db p] <name> [p=v…]` | `--declare`, `--list`, `--retract <n>`, `--dry-run`, `--no-check`, `--aliases`, `--hooks <file>`, `--json`, `--no-prelude` | Execute an action template (spec §25, see [`@cavelang/act`](../act)): validate parameters, check CAVE-Q preconditions against current belief, append effects atomically with lineage inside the §20.3 shape gate; out-of-band hooks fire after commit. |
|
|
37
38
|
| `check [--db p]` | `--stale <days>`, `--json`, `--no-prelude` | Knowledge health report (spec §20, see [`@cavelang/shape`](../shape)): shape violations against in-band `EXPECTS` declarations, stale current beliefs (default horizon 90 days), review candidates (conf 0.3–0.7), alias disagreements, coverage stats. Exit 1 on violations; everything else is advisory. |
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
39
|
+
| `suggest-alias [--db p]` | `--min <s>`, `--limit <n>`, `--agent <template>`, `--timeout <s>`, `--write`, `--json`, `--no-prelude` | Alias discovery (spec §27, see [`@cavelang/shape`](../shape)): same-entity candidates from string/graph similarity as suggested `ALIAS` claims at review-band confidence (0.3–0.5). Prints pipeable CAVE text; `--write` appends stamped `@src:suggest/alias`; `--agent` runs an LLM judge over the candidates (the ingest/eval shell contract). Pairs with any recorded `ALIAS` history are never re-suggested. |
|
|
40
|
+
| `sync [--db p] <source>` | `--as <label>`, `--into <label>`, `--dry-run`, `--no-record`, `--json`, `--no-prelude` | Store merge (spec §28, see [`@cavelang/sync`](../sync)): another CAVE store file — or `;@`-annotated canonical text, `-` for stdin — merges by row identity; present rows skip, re-runs merge nothing, effective merges append a `SYNCED-INTO` record (`--no-record` for checkouts, spec §28.6). |
|
|
41
|
+
| `export [--db p]` | `--out <file>`, `--current`, `--tx`, `--no-prelude` | Canonical CAVE text — all rows in tx order, or current beliefs only. `--tx` precedes every claim line with its `;@` transaction annotation (spec §28.4), so the text carries row identity: the committed, reviewable store text of the §28.6 branching convention. Stdout by default; `--out` writes a file and reports the claim count. |
|
|
42
|
+
| `mcp [--db p]` | `--read-only`, `--tools <list>`, `--no-prelude`, `--src <ctx>`, `--no-src` | Serve the engine as an MCP server on stdio (see [`@cavelang/mcp`](../mcp)) — tools for add/query/fuse/search/about/neighbors/reconstruct/derive/export/lint, with the §22 spec card as server instructions. `--read-only` / `--tools <list>` narrow the served tool surface (the agent permission boundary), composing by intersection. Appends are stamped `@src:agent/<client-name>` (spec §9.5); `--src` replaces the stamp, `--no-src` disables it. |
|
|
40
43
|
| `ingest [--db p] <globs/urls…>` | see `cave ingest --help` | LLM-driven ingestion of files and web pages (fetched and readability-extracted) through any headless agent (see [`@cavelang/ingest`](../ingest)): batching, instructions markdown, hybrid knowledge context, MCP or stdout agents, incremental digests, `--plan` NDJSON for SDK drivers. |
|
|
41
44
|
| `eval <suite…>` | see `cave eval --help` | Golden-fixture extraction/query evals (ROADMAP item 9, see [`@cavelang/eval`](../eval)): N fresh-store runs against any agent, claim-key + value scoring with §9.5 actor-stamp normalization, CAVE-Q expectations, optional LLM judge, `--min` CI gate. |
|
|
42
45
|
| `connect <source>` | see `cave connect --help` | Deterministic structured ingestion (spec §23, see [`@cavelang/connect`](../connect)): CSV/TSV/JSON/JSONL/SQLite/URL records through `?field` mapping templates, per-record digests, `--watch`, `--prune`, query-time `--query` overlay. |
|
|
46
|
+
| `reconstruct [--db p] <seed…>` | `--query <text>`, `--agent <template>`, `--steps <n>`, `--claims <n>`, `--timeout <s>`, `--trace`, `--no-prelude` | Active memory reconstruction from seed cues (spec §18, see [`@cavelang/loop`](../loop)): best-first traversal collecting related claims as canonical CAVE text; the heuristic policy by default, an LLM select/stop policy with `--agent` (ROADMAP item 10). |
|
|
43
47
|
| `demo` | | The cave-loop multi-hop recovery demo (§18). |
|
|
44
48
|
| `version` | | Print the cave version. |
|
|
45
49
|
| `help [command]` | | The overview, or one command's options and examples. |
|
package/dist/src/cli.d.ts
CHANGED
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
* - `cave parse [file]` — lint / dump the AST (`--json`)
|
|
9
9
|
* - `cave highlight [file]` — ANSI syntax colors (async, routed in `main.ts`)
|
|
10
10
|
* - `cave add [--db <path>] [file…]` — ingest into a store (`--strict`, `--check`)
|
|
11
|
-
* - `cave query [--db <path>] '<pattern>'` — CAVE-Q (`--json`, `--all`, `--aliases`, `--as-of`)
|
|
11
|
+
* - `cave query [--db <path>] '<pattern>'` — CAVE-Q (`--json`, `--all`, `--aliases`, `--as-of`, `--resolve`)
|
|
12
|
+
* - `cave resolve [--db <path>]` — contested facts under the §26 policy (`--aliases`, `--policy`, `--json`)
|
|
12
13
|
* - `cave derive [--db <path>] [rules.cave…]` — fire rules (`--dry-run`, `--full`, `--list`, `--retract`)
|
|
13
14
|
* - `cave act [--db <path>] <name> [param=value…]` — execute an action (spec §25; `--declare`, `--list`, `--retract`)
|
|
14
15
|
* - `cave check [--db <path>]` — knowledge health report (`--stale`, `--json`)
|
|
15
|
-
* - `cave
|
|
16
|
+
* - `cave suggest-alias [--db <path>]` — same-entity candidates (spec §27; async, routed in `main.ts`)
|
|
17
|
+
* - `cave sync [--db <path>] <source>` — merge another store by row identity (spec §28; `--dry-run`, `--as`, `--into`)
|
|
18
|
+
* - `cave export [--db <path>]` — canonical text out (`--current`, `--tx`)
|
|
16
19
|
* - `cave connect [--db <path>] <source>` — deterministic structured ingestion (async, routed in `main.ts`)
|
|
17
20
|
* - `cave eval <suite...>` — golden-fixture extraction/query/reconstruction evals (async, routed in `main.ts`)
|
|
18
21
|
* - `cave reconstruct [--db <path>] <seed...>` — §18 reconstruction from seed cues (async, routed in `main.ts`)
|
|
@@ -28,7 +31,7 @@ export type Output = {
|
|
|
28
31
|
readonly out: string;
|
|
29
32
|
readonly err: string;
|
|
30
33
|
};
|
|
31
|
-
export declare const usage = "cave \u2014 Compressed Atomic Verb Expressions\n\nUsage:\n cave parse [file...] [--json] lint CAVE text (stdin when no file)\n cave highlight [file...] print CAVE text with ANSI syntax colors\n cave add [--db <path>] [file...] ingest into a store [--strict] [--check] [--no-prelude] [--no-src]\n cave import [--db <path>] [file...] restore/merge from CAVE text (add without @src: stamping)\n cave query [--db <path>] <pattern> run a CAVE-Q pattern [--json] [--all] [--aliases] [--as-of <t>] [--no-prelude]\n cave derive [--db <path>] [rules.cave..] declare + fire rules (spec \u00A724) [--dry-run] [--full] [--list] [--retract <rule>]\n cave act [--db <path>] <name> [p=v...] execute an action (spec \u00A725) [--dry-run] [--no-check] [--hooks <file>]\n cave act --declare [file...] declare actions from a CAVE document; --list / --retract <name> manage them\n cave check [--db <path>] knowledge health report (spec \u00A720) [--stale <days>] [--json]\n cave export [--db <path>] [--out <file>] emit canonical CAVE text [--current] [--no-prelude]\n cave mcp [--db <path>] serve the engine as an MCP server on stdio [--no-prelude]\n cave ingest [--db <path>] <globs/urls..> LLM-driven ingestion of files and web pages\n cave eval <suite..> --agent '<command>' golden-fixture extraction/query/reconstruction evals (items 9, 10)\n cave connect <source> --map <file> deterministic structured ingestion (CSV/JSON/SQLite/URL, spec \u00A723)\n cave reconstruct [--db <path>] <seed..> reconstruct memory from seed cues (spec \u00A718) [--agent] [--query] [--trace]\n cave demo run the cave-loop reconstruction demo\n cave version print the cave version\n cave help [command] this text, or one command's options and examples\n\nEvery command answers --help. --db defaults to $CAVE_DB, or cave.db in\nthe current directory. The spec lives in the .claude/skills/ directory at\nthe repository root (section index in README.md).";
|
|
34
|
+
export declare const usage = "cave \u2014 Compressed Atomic Verb Expressions\n\nUsage:\n cave parse [file...] [--json] lint CAVE text (stdin when no file)\n cave highlight [file...] print CAVE text with ANSI syntax colors\n cave add [--db <path>] [file...] ingest into a store [--strict] [--check] [--no-prelude] [--no-src]\n cave import [--db <path>] [file...] restore/merge from CAVE text (add without @src: stamping)\n cave query [--db <path>] <pattern> run a CAVE-Q pattern [--json] [--all] [--aliases] [--as-of <t>] [--resolve] [--no-prelude]\n cave resolve [--db <path>] contested facts + winners (spec \u00A726) [--aliases] [--policy] [--json]\n cave derive [--db <path>] [rules.cave..] declare + fire rules (spec \u00A724) [--dry-run] [--full] [--list] [--retract <rule>]\n cave act [--db <path>] <name> [p=v...] execute an action (spec \u00A725) [--dry-run] [--no-check] [--hooks <file>]\n cave act --declare [file...] declare actions from a CAVE document; --list / --retract <name> manage them\n cave check [--db <path>] knowledge health report (spec \u00A720) [--stale <days>] [--json]\n cave suggest-alias [--db <path>] propose same-entity ALIAS candidates (spec \u00A727) [--min <s>] [--agent] [--write]\n cave sync [--db <path>] <source> merge another store by row identity (spec \u00A728) [--dry-run] [--as] [--into]\n cave export [--db <path>] [--out <file>] emit canonical CAVE text [--current] [--tx] [--no-prelude]\n cave mcp [--db <path>] serve the engine as an MCP server on stdio [--no-prelude]\n cave ingest [--db <path>] <globs/urls..> LLM-driven ingestion of files and web pages\n cave eval <suite..> --agent '<command>' golden-fixture extraction/query/reconstruction evals (items 9, 10)\n cave connect <source> --map <file> deterministic structured ingestion (CSV/JSON/SQLite/URL, spec \u00A723)\n cave reconstruct [--db <path>] <seed..> reconstruct memory from seed cues (spec \u00A718) [--agent] [--query] [--trace]\n cave demo run the cave-loop reconstruction demo\n cave version print the cave version\n cave help [command] this text, or one command's options and examples\n\nEvery command answers --help. --db defaults to $CAVE_DB, or cave.db in\nthe current directory. The spec lives in the .claude/skills/ directory at\nthe repository root (section index in README.md).";
|
|
32
35
|
/** Per-command help, printed for \`cave <command> --help\` and \`cave help <command>\`. */
|
|
33
36
|
export declare const commandHelp: Record<string, string>;
|
|
34
37
|
export declare const parseCommand: (argv: readonly string[]) => Output;
|
|
@@ -53,9 +56,29 @@ export declare const addCommand: (argv: readonly string[]) => Output;
|
|
|
53
56
|
*/
|
|
54
57
|
export declare const importCommand: (argv: readonly string[]) => Output;
|
|
55
58
|
export declare const queryCommand: (argv: readonly string[]) => Output;
|
|
59
|
+
/**
|
|
60
|
+
* `cave resolve` — the human view of contradiction resolution (spec §26.4):
|
|
61
|
+
* contested facts with their candidates ranked as the policy sees them,
|
|
62
|
+
* or the effective policy itself under `--policy`.
|
|
63
|
+
*/
|
|
64
|
+
export declare const resolveCommand: (argv: readonly string[]) => Output;
|
|
56
65
|
export declare const deriveCommand: (argv: readonly string[]) => Output;
|
|
57
66
|
export declare const actCommand: (argv: readonly string[]) => Output;
|
|
58
67
|
export declare const checkCommand: (argv: readonly string[]) => Output;
|
|
68
|
+
/**
|
|
69
|
+
* `cave suggest-alias` — alias discovery (spec §27): same-entity
|
|
70
|
+
* candidates as suggested `ALIAS` claims for review. Async (the optional
|
|
71
|
+
* judge runs a shell agent), so `main.ts` routes it separately, like
|
|
72
|
+
* `reconstruct`.
|
|
73
|
+
*/
|
|
74
|
+
export declare const suggestAliasCommand: (argv: readonly string[]) => Promise<Output>;
|
|
75
|
+
/**
|
|
76
|
+
* `cave sync` — store merge (spec §28): another store's rows arrive by
|
|
77
|
+
* identity — a store file merges through SQL, `;@`-annotated canonical
|
|
78
|
+
* text (`cave export --tx`, or `-` for stdin) replays under its recorded
|
|
79
|
+
* ids. Idempotent: re-runs merge nothing and append no record.
|
|
80
|
+
*/
|
|
81
|
+
export declare const syncCommand: (argv: readonly string[]) => Output;
|
|
59
82
|
export declare const exportCommand: (argv: readonly string[]) => Output;
|
|
60
83
|
/**
|
|
61
84
|
* `cave reconstruct` — the §18 loop over the store: heuristic policy by
|
package/dist/src/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAyBH,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAA;AAQD,eAAO,MAAM,KAAK,88EA2BgC,CAAA;AAIlD,2FAA2F;AAC3F,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAqZ9C,CAAA;AAOD,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAyBtD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CAM9E,CAAA;AAuDD,eAAO,MAAM,UAAU,SA1CkC,SAAS,MAAM,EAAE,KAAG,MA0C/B,CAAA;AAE9C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,SAxD+B,SAAS,MAAM,EAAE,KAAG,MAwDzB,CAAA;AAEpD,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA8CtD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA+CxD,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAsHvD,CAAA;AA8CD,eAAO,MAAM,UAAU,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAsFpD,CAAA;AAqCD,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA0BtD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CA6DjF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAmDrD,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAwBvD,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CAgEhF,CAAA;AAED,eAAO,MAAM,WAAW,QAAO,MACS,CAAA;AAExC,eAAO,MAAM,cAAc,QAAO,MACJ,CAAA;AAE9B,wEAAwE;AACxE,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAcrD,CAAA;AAED,iCAAiC;AACjC,eAAO,MAAM,IAAI,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA+C9C,CAAA"}
|
package/dist/src/cli.js
CHANGED
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
* - `cave parse [file]` — lint / dump the AST (`--json`)
|
|
9
9
|
* - `cave highlight [file]` — ANSI syntax colors (async, routed in `main.ts`)
|
|
10
10
|
* - `cave add [--db <path>] [file…]` — ingest into a store (`--strict`, `--check`)
|
|
11
|
-
* - `cave query [--db <path>] '<pattern>'` — CAVE-Q (`--json`, `--all`, `--aliases`, `--as-of`)
|
|
11
|
+
* - `cave query [--db <path>] '<pattern>'` — CAVE-Q (`--json`, `--all`, `--aliases`, `--as-of`, `--resolve`)
|
|
12
|
+
* - `cave resolve [--db <path>]` — contested facts under the §26 policy (`--aliases`, `--policy`, `--json`)
|
|
12
13
|
* - `cave derive [--db <path>] [rules.cave…]` — fire rules (`--dry-run`, `--full`, `--list`, `--retract`)
|
|
13
14
|
* - `cave act [--db <path>] <name> [param=value…]` — execute an action (spec §25; `--declare`, `--list`, `--retract`)
|
|
14
15
|
* - `cave check [--db <path>]` — knowledge health report (`--stale`, `--json`)
|
|
15
|
-
* - `cave
|
|
16
|
+
* - `cave suggest-alias [--db <path>]` — same-entity candidates (spec §27; async, routed in `main.ts`)
|
|
17
|
+
* - `cave sync [--db <path>] <source>` — merge another store by row identity (spec §28; `--dry-run`, `--as`, `--into`)
|
|
18
|
+
* - `cave export [--db <path>]` — canonical text out (`--current`, `--tx`)
|
|
16
19
|
* - `cave connect [--db <path>] <source>` — deterministic structured ingestion (async, routed in `main.ts`)
|
|
17
20
|
* - `cave eval <suite...>` — golden-fixture extraction/query/reconstruction evals (async, routed in `main.ts`)
|
|
18
21
|
* - `cave reconstruct [--db <path>] <seed...>` — §18 reconstruction from seed cues (async, routed in `main.ts`)
|
|
@@ -30,11 +33,12 @@ import { parseDocument } from '@cavelang/parser';
|
|
|
30
33
|
import { Registry, standardRegistry } from '@cavelang/canonical';
|
|
31
34
|
import { defaultDbPath, open } from '@cavelang/store';
|
|
32
35
|
import { query as caveQuery } from '@cavelang/query';
|
|
33
|
-
import { check as caveCheck, defaultStaleDays, gatedIngest } from '@cavelang/shape';
|
|
36
|
+
import { check as caveCheck, defaultMinScore, defaultStaleDays, gatedIngest, judgePrompt, parseJudgeReply, suggestAliases, writeSuggestions } from '@cavelang/shape';
|
|
34
37
|
import { declareRules, defaultMaxPasses, defaultMinConf, derive, listRules, retractRule } from '@cavelang/rules';
|
|
35
38
|
import { act, declareActions, listActions, retractAction } from '@cavelang/act';
|
|
36
39
|
import { Demo, heuristicPolicy, llmPolicy, reconstruct, reconstructAsync, shellComplete, sqliteStore } from '@cavelang/loop';
|
|
37
|
-
import {
|
|
40
|
+
import { labelOf, sanitizeLabel, syncFile, syncText } from '@cavelang/sync';
|
|
41
|
+
import { emitClaim, txOfLine } from '@cavelang/canonical';
|
|
38
42
|
const ok = (out) => ({ code: 0, out, err: '' });
|
|
39
43
|
const fail = (err, code = 1) => ({ code, out: '', err });
|
|
40
44
|
export const usage = `cave — Compressed Atomic Verb Expressions
|
|
@@ -44,12 +48,15 @@ Usage:
|
|
|
44
48
|
cave highlight [file...] print CAVE text with ANSI syntax colors
|
|
45
49
|
cave add [--db <path>] [file...] ingest into a store [--strict] [--check] [--no-prelude] [--no-src]
|
|
46
50
|
cave import [--db <path>] [file...] restore/merge from CAVE text (add without @src: stamping)
|
|
47
|
-
cave query [--db <path>] <pattern> run a CAVE-Q pattern [--json] [--all] [--aliases] [--as-of <t>] [--no-prelude]
|
|
51
|
+
cave query [--db <path>] <pattern> run a CAVE-Q pattern [--json] [--all] [--aliases] [--as-of <t>] [--resolve] [--no-prelude]
|
|
52
|
+
cave resolve [--db <path>] contested facts + winners (spec §26) [--aliases] [--policy] [--json]
|
|
48
53
|
cave derive [--db <path>] [rules.cave..] declare + fire rules (spec §24) [--dry-run] [--full] [--list] [--retract <rule>]
|
|
49
54
|
cave act [--db <path>] <name> [p=v...] execute an action (spec §25) [--dry-run] [--no-check] [--hooks <file>]
|
|
50
55
|
cave act --declare [file...] declare actions from a CAVE document; --list / --retract <name> manage them
|
|
51
56
|
cave check [--db <path>] knowledge health report (spec §20) [--stale <days>] [--json]
|
|
52
|
-
cave
|
|
57
|
+
cave suggest-alias [--db <path>] propose same-entity ALIAS candidates (spec §27) [--min <s>] [--agent] [--write]
|
|
58
|
+
cave sync [--db <path>] <source> merge another store by row identity (spec §28) [--dry-run] [--as] [--into]
|
|
59
|
+
cave export [--db <path>] [--out <file>] emit canonical CAVE text [--current] [--tx] [--no-prelude]
|
|
53
60
|
cave mcp [--db <path>] serve the engine as an MCP server on stdio [--no-prelude]
|
|
54
61
|
cave ingest [--db <path>] <globs/urls..> LLM-driven ingestion of files and web pages
|
|
55
62
|
cave eval <suite..> --agent '<command>' golden-fixture extraction/query/reconstruction evals (items 9, 10)
|
|
@@ -131,7 +138,10 @@ Canonical CAVE text is the interchange format (spec §2.2): importing a
|
|
|
131
138
|
file written by \`cave export\` replays its claims append-only, preserving
|
|
132
139
|
the belief series, qualifier edges and in-band declarations. Unlike
|
|
133
140
|
\`cave add\`, import never stamps actor provenance — replayed claims must
|
|
134
|
-
keep the claim keys they were exported with (spec §9.5).
|
|
141
|
+
keep the claim keys they were exported with (spec §9.5). Import always
|
|
142
|
+
mints fresh transaction ids — ;@ annotations in a \`cave export --tx\`
|
|
143
|
+
file read as ordinary comments; to merge under the recorded row
|
|
144
|
+
identity, use \`cave sync\` (spec §28.4).
|
|
135
145
|
|
|
136
146
|
Examples:
|
|
137
147
|
cave export --db old.db --out backup.cave
|
|
@@ -139,7 +149,7 @@ Examples:
|
|
|
139
149
|
query: `cave query — run a CAVE-Q pattern against a store
|
|
140
150
|
|
|
141
151
|
Usage:
|
|
142
|
-
cave query [--db <path>] <pattern> [WHERE <filter>] [--json] [--all] [--aliases] [--as-of <t>] [--no-prelude]
|
|
152
|
+
cave query [--db <path>] <pattern> [WHERE <filter>] [--json] [--all] [--aliases] [--as-of <t>] [--resolve] [--no-prelude]
|
|
143
153
|
|
|
144
154
|
Options:
|
|
145
155
|
${dbHelp}
|
|
@@ -149,6 +159,11 @@ Options:
|
|
|
149
159
|
--as-of <t> resolve beliefs as of a past moment (spec §12.3): a date
|
|
150
160
|
(whole day included), a timestamp (whole second), or a
|
|
151
161
|
transaction id — rows recorded later are invisible
|
|
162
|
+
--resolve match resolved winners only (spec §26): contested facts —
|
|
163
|
+
same fact from several sources, or opposite polarity —
|
|
164
|
+
collapse to the row the resolution policy picks
|
|
165
|
+
(precedence class, reliability-weighted confidence, tx);
|
|
166
|
+
incompatible with --all
|
|
152
167
|
--no-prelude open the store without the standard verb registry
|
|
153
168
|
|
|
154
169
|
Patterns are claim triples with ?variables and optional metadata filters
|
|
@@ -162,7 +177,38 @@ Examples:
|
|
|
162
177
|
cave query --db k.db 'terrier EXTENDS+ animal'
|
|
163
178
|
cave query --db k.db '?x USES postgres' --aliases
|
|
164
179
|
cave query --db k.db 'server IS compromised' --as-of 2026-01-15
|
|
180
|
+
cave query --db k.db 'service HAS owner: ?who' --resolve
|
|
165
181
|
cave query --db k.db '?x ?verb ?y @production' --json`,
|
|
182
|
+
resolve: `cave resolve — contested facts and their winners (spec §26)
|
|
183
|
+
|
|
184
|
+
Usage:
|
|
185
|
+
cave resolve [--db <path>] [--aliases] [--policy] [--json] [--no-prelude]
|
|
186
|
+
|
|
187
|
+
Options:
|
|
188
|
+
${dbHelp}
|
|
189
|
+
--aliases widen resolution groups through current ALIAS claims
|
|
190
|
+
(spec §13.6) — aliased names contest one fact
|
|
191
|
+
--policy print the effective resolution policy instead: the
|
|
192
|
+
built-in precedence ladder merged with in-band
|
|
193
|
+
source/<name> HAS precedence: / HAS reliability: claims
|
|
194
|
+
--json emit the report as JSON
|
|
195
|
+
--no-prelude open the store without the standard verb registry
|
|
196
|
+
|
|
197
|
+
Lists every fact more than one belief series currently speaks about —
|
|
198
|
+
same fact from different sources (§9.5 actor stamps, content sources)
|
|
199
|
+
or opposite polarity — with the candidates ranked as the resolution
|
|
200
|
+
policy sees them: precedence class first, reliability-weighted
|
|
201
|
+
confidence second, latest transaction last. The winner is what
|
|
202
|
+
\`cave query --resolve\` matches; the rest are invisible there. Declare
|
|
203
|
+
policy overrides in-band, e.g.:
|
|
204
|
+
|
|
205
|
+
source/scanner-a HAS reliability: 60%
|
|
206
|
+
source/ingest HAS precedence: 1
|
|
207
|
+
|
|
208
|
+
Examples:
|
|
209
|
+
cave resolve --db k.db
|
|
210
|
+
cave resolve --db k.db --aliases
|
|
211
|
+
cave resolve --db k.db --policy`,
|
|
166
212
|
derive: `cave derive — declare and fire rules (spec §24)
|
|
167
213
|
|
|
168
214
|
Usage:
|
|
@@ -269,21 +315,113 @@ Examples:
|
|
|
269
315
|
cave check --db k.db
|
|
270
316
|
cave check --db k.db --stale 30
|
|
271
317
|
cave check --db k.db --json | jq '.violations'`,
|
|
318
|
+
'suggest-alias': `cave suggest-alias — propose same-entity ALIAS candidates for review (spec §27)
|
|
319
|
+
|
|
320
|
+
Usage:
|
|
321
|
+
cave suggest-alias [--db <path>] [options]
|
|
322
|
+
|
|
323
|
+
Options:
|
|
324
|
+
${dbHelp}
|
|
325
|
+
--min <s> minimum evidence score, 0..1 or N% (default 60%)
|
|
326
|
+
--limit <n> at most n suggestions, strongest first
|
|
327
|
+
--agent <template> LLM judge filtering the candidates — the cave
|
|
328
|
+
ingest/eval shell contract: the prompt (each pair
|
|
329
|
+
with both sides' current claims) is piped to stdin
|
|
330
|
+
and {prompt-file} is substituted; stdout replies
|
|
331
|
+
with a JSON array of confirmed suggestion numbers
|
|
332
|
+
--timeout <seconds> judge timeout (default 120)
|
|
333
|
+
--write append the suggestions, stamped @src:suggest/alias,
|
|
334
|
+
instead of only printing them
|
|
335
|
+
--json print suggestions with scores and signals as JSON
|
|
336
|
+
--no-prelude open the store without the standard verb registry
|
|
337
|
+
|
|
338
|
+
Candidates come from string similarity (case/separator drift, segment
|
|
339
|
+
containment, prefixes, typos) and shared rare attribute values; shared
|
|
340
|
+
relations strengthen a candidate but never create one. Suggested claims
|
|
341
|
+
are questions, not merges: confidence 30–50% puts them in cave check's
|
|
342
|
+
review band, and a pair with any recorded ALIAS history — merged,
|
|
343
|
+
rejected or unmerged — is never suggested again.
|
|
344
|
+
|
|
345
|
+
Output is CAVE text: review it, edit it, pipe it into cave add — or
|
|
346
|
+
--write it and review afterwards. A written suggestion is a positive
|
|
347
|
+
claim, so the opt-in §13.6 closure links it until reviewed:
|
|
348
|
+
|
|
349
|
+
confirm echo 'dupe ALIAS canonical' | cave add --db k.db
|
|
350
|
+
reject echo 'dupe ALIAS canonical @src:suggest/alias @ 0%' | cave add --db k.db
|
|
351
|
+
|
|
352
|
+
(rejection retracts the suggestion's own series — contexts are part of
|
|
353
|
+
claim identity, §9.2, so a plain @ 0% append would start a new series
|
|
354
|
+
and leave the suggested link standing).
|
|
355
|
+
|
|
356
|
+
Examples:
|
|
357
|
+
cave suggest-alias --db k.db
|
|
358
|
+
cave suggest-alias --db k.db --min 80% --limit 10
|
|
359
|
+
cave suggest-alias --db k.db | cave add --db k.db
|
|
360
|
+
cave suggest-alias --db k.db --agent 'claude -p' --write`,
|
|
361
|
+
sync: `cave sync — merge another store into this one by row identity (spec §28)
|
|
362
|
+
|
|
363
|
+
Usage:
|
|
364
|
+
cave sync [--db <path>] <source> [--as <label>] [--into <label>]
|
|
365
|
+
[--dry-run] [--no-record] [--json] [--no-prelude]
|
|
366
|
+
|
|
367
|
+
Options:
|
|
368
|
+
${dbHelp}
|
|
369
|
+
--as <label> origin label in the merge record (default: the source
|
|
370
|
+
file's basename stem; stdin defaults to "stdin")
|
|
371
|
+
--into <label> target label in the merge record (default: the database
|
|
372
|
+
file's basename stem)
|
|
373
|
+
--dry-run compute the full report inside a rolled-back transaction
|
|
374
|
+
--no-record do not append the store/<from> SYNCED-INTO store/<into>
|
|
375
|
+
merge record
|
|
376
|
+
--json emit the report as JSON
|
|
377
|
+
--no-prelude open the store without the standard verb registry
|
|
378
|
+
|
|
379
|
+
The source is a CAVE store file (recognized by the SQLite header),
|
|
380
|
+
canonical text with ;@ transaction annotations (cave export --tx), or -
|
|
381
|
+
for annotated text on stdin. Rows merge by identity: a row's UUIDv7 is
|
|
382
|
+
its id everywhere, so present rows skip, re-runs merge nothing, and two
|
|
383
|
+
stores syncing each other converge. Contradictions coexist (spec §9.4)
|
|
384
|
+
and resolve at read time (cave resolve, --resolve); everything appended
|
|
385
|
+
after a merge outsorts everything merged, whatever the origin machine's
|
|
386
|
+
clock read (spec §28.2). An effective merge appends one record claim,
|
|
387
|
+
stamped @src:sync — the sync log is its belief series.
|
|
388
|
+
|
|
389
|
+
Branching (spec §28.6): commit the full annotated export — the text is
|
|
390
|
+
the store — and rebuild working stores from it. A checkout is plumbing
|
|
391
|
+
(--no-record); landing reviewed text into a live store is a real merge
|
|
392
|
+
event (let it record). Never hand-merge exports: sync both sides into
|
|
393
|
+
a fresh store and re-export — the union — which git can run for you as
|
|
394
|
+
a merge driver on *.cave files.
|
|
395
|
+
|
|
396
|
+
Examples:
|
|
397
|
+
cave sync --db main.db laptop.db
|
|
398
|
+
cave sync --db main.db laptop.db --dry-run --json
|
|
399
|
+
cave export --db laptop.db --tx | cave sync --db main.db -
|
|
400
|
+
cave sync --db a.db b.db && cave sync --db b.db a.db # converge both
|
|
401
|
+
cave sync --db work.db knowledge.cave --no-record # checkout (§28.6)
|
|
402
|
+
cave sync --db main.db knowledge.cave --as reorg-auth # land the branch`,
|
|
272
403
|
export: `cave export — emit canonical CAVE text from a store
|
|
273
404
|
|
|
274
405
|
Usage:
|
|
275
|
-
cave export [--db <path>] [--out <file>] [--current] [--no-prelude]
|
|
406
|
+
cave export [--db <path>] [--out <file>] [--current] [--tx] [--no-prelude]
|
|
276
407
|
|
|
277
408
|
Options:
|
|
278
409
|
${dbHelp}
|
|
279
410
|
--out <file> write to a file instead of stdout
|
|
280
411
|
--current current beliefs only (skip superseded rows)
|
|
412
|
+
--tx precede every claim line with its ;@ transaction
|
|
413
|
+
annotation (spec §28.4) — the text carries row identity,
|
|
414
|
+
so cave sync replays it idempotently; other readers see
|
|
415
|
+
ordinary comments
|
|
281
416
|
--no-prelude open the store without the standard verb registry
|
|
282
417
|
|
|
283
418
|
Examples:
|
|
284
419
|
cave export --db k.db
|
|
285
420
|
cave export --db k.db --out backup.cave
|
|
286
|
-
cave export --db k.db --current --out snapshot.cave
|
|
421
|
+
cave export --db k.db --current --out snapshot.cave
|
|
422
|
+
cave export --db k.db --tx | cave sync --db other.db -
|
|
423
|
+
cave export --db work.db --tx --out knowledge.cave # the committed,
|
|
424
|
+
# reviewable store text (spec §28.6)`,
|
|
287
425
|
reconstruct: `cave reconstruct — active memory reconstruction from seed cues (spec §18)
|
|
288
426
|
|
|
289
427
|
Usage:
|
|
@@ -441,6 +579,7 @@ export const queryCommand = (argv) => {
|
|
|
441
579
|
all: { type: 'boolean' },
|
|
442
580
|
aliases: { type: 'boolean' },
|
|
443
581
|
'as-of': { type: 'string' },
|
|
582
|
+
resolve: { type: 'boolean' },
|
|
444
583
|
'no-prelude': { type: 'boolean' }
|
|
445
584
|
},
|
|
446
585
|
allowPositionals: true
|
|
@@ -454,6 +593,7 @@ export const queryCommand = (argv) => {
|
|
|
454
593
|
const matches = caveQuery(store, pattern, {
|
|
455
594
|
all: values.all === true,
|
|
456
595
|
aliases: values.aliases === true,
|
|
596
|
+
resolve: values.resolve === true,
|
|
457
597
|
...values['as-of'] === undefined ? {} : { asOf: values['as-of'] }
|
|
458
598
|
});
|
|
459
599
|
if (values.json === true) {
|
|
@@ -479,6 +619,59 @@ export const queryCommand = (argv) => {
|
|
|
479
619
|
store.close();
|
|
480
620
|
}
|
|
481
621
|
};
|
|
622
|
+
/**
|
|
623
|
+
* `cave resolve` — the human view of contradiction resolution (spec §26.4):
|
|
624
|
+
* contested facts with their candidates ranked as the policy sees them,
|
|
625
|
+
* or the effective policy itself under `--policy`.
|
|
626
|
+
*/
|
|
627
|
+
export const resolveCommand = (argv) => {
|
|
628
|
+
const { values } = parseArgs({
|
|
629
|
+
args: [...argv],
|
|
630
|
+
options: {
|
|
631
|
+
db: { type: 'string' },
|
|
632
|
+
aliases: { type: 'boolean' },
|
|
633
|
+
policy: { type: 'boolean' },
|
|
634
|
+
json: { type: 'boolean' },
|
|
635
|
+
'no-prelude': { type: 'boolean' }
|
|
636
|
+
},
|
|
637
|
+
allowPositionals: false
|
|
638
|
+
});
|
|
639
|
+
const percent = (value) => `${Math.round(value * 1000) / 10}%`;
|
|
640
|
+
const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
641
|
+
try {
|
|
642
|
+
if (values.policy === true) {
|
|
643
|
+
const policy = store.resolutionPolicy();
|
|
644
|
+
if (values.json === true) {
|
|
645
|
+
return ok(`${JSON.stringify(policy, undefined, 2)}\n`);
|
|
646
|
+
}
|
|
647
|
+
const width = Math.max(...policy.map(entry => entry.prefix.length + 7));
|
|
648
|
+
const lines = policy.map(entry => {
|
|
649
|
+
const subject = entry.prefix === '' ? 'source' : `source/${entry.prefix}`;
|
|
650
|
+
const dimensions = [
|
|
651
|
+
...entry.precedence === undefined ? [] : [`precedence ${entry.precedence}`],
|
|
652
|
+
...entry.reliability === undefined ? [] : [`reliability ${percent(entry.reliability)}`]
|
|
653
|
+
];
|
|
654
|
+
return `${subject.padEnd(width)} ${dimensions.join(', ')}`;
|
|
655
|
+
});
|
|
656
|
+
return ok(`${lines.join('\n')}\n`);
|
|
657
|
+
}
|
|
658
|
+
const contested = store.contested({ aliases: values.aliases === true });
|
|
659
|
+
if (values.json === true) {
|
|
660
|
+
return ok(`${JSON.stringify(contested, undefined, 2)}\n`);
|
|
661
|
+
}
|
|
662
|
+
if (contested.length === 0) {
|
|
663
|
+
return ok('no contested facts\n');
|
|
664
|
+
}
|
|
665
|
+
const lines = contested.flatMap(group => group.rows.map((row, at) => `${at === 0 ? '' : ' over '}${row.raw_line} ; class ${row.res_class}, effective ${percent(row.res_conf)}`));
|
|
666
|
+
return ok(`${lines.join('\n')}\n`);
|
|
667
|
+
}
|
|
668
|
+
catch (error) {
|
|
669
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
670
|
+
}
|
|
671
|
+
finally {
|
|
672
|
+
store.close();
|
|
673
|
+
}
|
|
674
|
+
};
|
|
482
675
|
export const deriveCommand = (argv) => {
|
|
483
676
|
const { values, positionals } = parseArgs({
|
|
484
677
|
args: [...argv],
|
|
@@ -791,12 +984,141 @@ export const checkCommand = (argv) => {
|
|
|
791
984
|
store.close();
|
|
792
985
|
}
|
|
793
986
|
};
|
|
987
|
+
/**
|
|
988
|
+
* `cave suggest-alias` — alias discovery (spec §27): same-entity
|
|
989
|
+
* candidates as suggested `ALIAS` claims for review. Async (the optional
|
|
990
|
+
* judge runs a shell agent), so `main.ts` routes it separately, like
|
|
991
|
+
* `reconstruct`.
|
|
992
|
+
*/
|
|
993
|
+
export const suggestAliasCommand = async (argv) => {
|
|
994
|
+
try {
|
|
995
|
+
const { values } = parseArgs({
|
|
996
|
+
args: [...argv],
|
|
997
|
+
options: {
|
|
998
|
+
db: { type: 'string' },
|
|
999
|
+
min: { type: 'string' },
|
|
1000
|
+
limit: { type: 'string' },
|
|
1001
|
+
agent: { type: 'string' },
|
|
1002
|
+
timeout: { type: 'string' },
|
|
1003
|
+
write: { type: 'boolean' },
|
|
1004
|
+
json: { type: 'boolean' },
|
|
1005
|
+
'no-prelude': { type: 'boolean' },
|
|
1006
|
+
help: { type: 'boolean', short: 'h' }
|
|
1007
|
+
},
|
|
1008
|
+
allowPositionals: false
|
|
1009
|
+
});
|
|
1010
|
+
if (values.help === true) {
|
|
1011
|
+
return ok(`${commandHelp['suggest-alias']}\n`);
|
|
1012
|
+
}
|
|
1013
|
+
const minScore = values.min === undefined ?
|
|
1014
|
+
defaultMinScore :
|
|
1015
|
+
values.min.endsWith('%') ? Number(values.min.slice(0, -1)) / 100 : Number(values.min);
|
|
1016
|
+
if (!Number.isFinite(minScore) || minScore < 0 || minScore > 1) {
|
|
1017
|
+
return fail(`cave suggest-alias: --min expects a score in 0..1 or N%, got ${JSON.stringify(values.min)}\n`);
|
|
1018
|
+
}
|
|
1019
|
+
const limit = values.limit === undefined ? undefined : Number(values.limit);
|
|
1020
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit < 1)) {
|
|
1021
|
+
return fail(`cave suggest-alias: --limit expects a positive integer, got ${JSON.stringify(values.limit)}\n`);
|
|
1022
|
+
}
|
|
1023
|
+
const timeoutSeconds = values.timeout === undefined ? undefined : Number(values.timeout);
|
|
1024
|
+
if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
|
|
1025
|
+
return fail(`cave suggest-alias: --timeout must be a positive number of seconds, got '${values.timeout}'\n`);
|
|
1026
|
+
}
|
|
1027
|
+
const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
1028
|
+
try {
|
|
1029
|
+
let suggestions = suggestAliases(store, { minScore, ...limit === undefined ? {} : { limit } });
|
|
1030
|
+
if (values.agent !== undefined && suggestions.length > 0) {
|
|
1031
|
+
const reply = await shellComplete(values.agent, timeoutSeconds === undefined ? {} : { timeoutSeconds })(judgePrompt(store, suggestions));
|
|
1032
|
+
suggestions = parseJudgeReply(reply, suggestions.length).map(index => suggestions[index]);
|
|
1033
|
+
}
|
|
1034
|
+
if (values.json === true) {
|
|
1035
|
+
return ok(`${JSON.stringify(suggestions, undefined, 2)}\n`);
|
|
1036
|
+
}
|
|
1037
|
+
if (suggestions.length === 0) {
|
|
1038
|
+
return ok('no alias suggestions\n');
|
|
1039
|
+
}
|
|
1040
|
+
const lines = suggestions.map(suggestion => suggestion.line);
|
|
1041
|
+
if (values.write === true) {
|
|
1042
|
+
const { appended } = writeSuggestions(store, suggestions);
|
|
1043
|
+
return ok(`${lines.join('\n')}\nappended ${appended} suggested alias claim(s)\n`);
|
|
1044
|
+
}
|
|
1045
|
+
return ok(`${lines.join('\n')}\n`);
|
|
1046
|
+
}
|
|
1047
|
+
finally {
|
|
1048
|
+
store.close();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
catch (error) {
|
|
1052
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
/**
|
|
1056
|
+
* `cave sync` — store merge (spec §28): another store's rows arrive by
|
|
1057
|
+
* identity — a store file merges through SQL, `;@`-annotated canonical
|
|
1058
|
+
* text (`cave export --tx`, or `-` for stdin) replays under its recorded
|
|
1059
|
+
* ids. Idempotent: re-runs merge nothing and append no record.
|
|
1060
|
+
*/
|
|
1061
|
+
export const syncCommand = (argv) => {
|
|
1062
|
+
const { values, positionals } = parseArgs({
|
|
1063
|
+
args: [...argv],
|
|
1064
|
+
options: {
|
|
1065
|
+
db: { type: 'string' },
|
|
1066
|
+
as: { type: 'string' },
|
|
1067
|
+
into: { type: 'string' },
|
|
1068
|
+
'dry-run': { type: 'boolean' },
|
|
1069
|
+
'no-record': { type: 'boolean' },
|
|
1070
|
+
json: { type: 'boolean' },
|
|
1071
|
+
'no-prelude': { type: 'boolean' }
|
|
1072
|
+
},
|
|
1073
|
+
allowPositionals: true
|
|
1074
|
+
});
|
|
1075
|
+
const [source, ...extra] = positionals;
|
|
1076
|
+
if (source === undefined || extra.length > 0) {
|
|
1077
|
+
return fail('cave sync: exactly one source is required — a CAVE store file, ;@-annotated text, or - for stdin (spec §28.5)\n');
|
|
1078
|
+
}
|
|
1079
|
+
const dbPath = values.db ?? defaultDbPath();
|
|
1080
|
+
const store = open(dbPath, values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
1081
|
+
try {
|
|
1082
|
+
const options = {
|
|
1083
|
+
into: values.into === undefined ? labelOf(dbPath) : sanitizeLabel(values.into),
|
|
1084
|
+
record: values['no-record'] !== true,
|
|
1085
|
+
dryRun: values['dry-run'] === true,
|
|
1086
|
+
...values.as === undefined ? {} : { from: sanitizeLabel(values.as) }
|
|
1087
|
+
};
|
|
1088
|
+
const report = source === '-' ?
|
|
1089
|
+
syncText(store, readFileSync(0, 'utf8'), { from: 'stdin', ...options }) :
|
|
1090
|
+
syncFile(store, source, options);
|
|
1091
|
+
if (values.json === true) {
|
|
1092
|
+
return { code: report.problems.length > 0 ? 1 : 0, out: `${JSON.stringify(report, undefined, 2)}\n`, err: '' };
|
|
1093
|
+
}
|
|
1094
|
+
if (report.problems.length > 0) {
|
|
1095
|
+
const detail = report.problems.map(problem => ` line ${problem.line}: ${problem.message}`).join('\n');
|
|
1096
|
+
return fail(`cave sync: ${source}: ${report.problems.length} problem(s), nothing merged\n${detail}\n`);
|
|
1097
|
+
}
|
|
1098
|
+
const lines = [
|
|
1099
|
+
`merged ${report.merged} claim(s), ${report.edges} edge(s)` +
|
|
1100
|
+
(report.skipped > 0 ? `, ${report.skipped} already present` : '') +
|
|
1101
|
+
(report.dryRun ? ' (dry run)' : '')
|
|
1102
|
+
];
|
|
1103
|
+
if (report.record !== undefined) {
|
|
1104
|
+
lines.push(`record: ${report.record}`);
|
|
1105
|
+
}
|
|
1106
|
+
return ok(`${lines.join('\n')}\n`);
|
|
1107
|
+
}
|
|
1108
|
+
catch (error) {
|
|
1109
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
1110
|
+
}
|
|
1111
|
+
finally {
|
|
1112
|
+
store.close();
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
794
1115
|
export const exportCommand = (argv) => {
|
|
795
1116
|
const { values } = parseArgs({
|
|
796
1117
|
args: [...argv],
|
|
797
1118
|
options: {
|
|
798
1119
|
db: { type: 'string' },
|
|
799
1120
|
current: { type: 'boolean' },
|
|
1121
|
+
tx: { type: 'boolean' },
|
|
800
1122
|
out: { type: 'string' },
|
|
801
1123
|
'no-prelude': { type: 'boolean' }
|
|
802
1124
|
},
|
|
@@ -804,12 +1126,12 @@ export const exportCommand = (argv) => {
|
|
|
804
1126
|
});
|
|
805
1127
|
const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
806
1128
|
try {
|
|
807
|
-
const text = store.exportText({ current: values.current === true });
|
|
1129
|
+
const text = store.exportText({ current: values.current === true, tx: values.tx === true });
|
|
808
1130
|
if (values.out === undefined) {
|
|
809
1131
|
return ok(text);
|
|
810
1132
|
}
|
|
811
1133
|
writeFileSync(values.out, text);
|
|
812
|
-
const claims = text === '' ? 0 : text.trimEnd().split('\n').length;
|
|
1134
|
+
const claims = text === '' ? 0 : text.trimEnd().split('\n').filter(line => txOfLine(line) === undefined).length;
|
|
813
1135
|
return ok(`exported ${claims} claim(s) to ${values.out}\n`);
|
|
814
1136
|
}
|
|
815
1137
|
finally {
|
|
@@ -918,12 +1240,16 @@ export const cave = (argv) => {
|
|
|
918
1240
|
return importCommand(rest);
|
|
919
1241
|
case 'query':
|
|
920
1242
|
return queryCommand(rest);
|
|
1243
|
+
case 'resolve':
|
|
1244
|
+
return resolveCommand(rest);
|
|
921
1245
|
case 'derive':
|
|
922
1246
|
return deriveCommand(rest);
|
|
923
1247
|
case 'act':
|
|
924
1248
|
return actCommand(rest);
|
|
925
1249
|
case 'check':
|
|
926
1250
|
return checkCommand(rest);
|
|
1251
|
+
case 'sync':
|
|
1252
|
+
return syncCommand(rest);
|
|
927
1253
|
case 'export':
|
|
928
1254
|
return exportCommand(rest);
|
|
929
1255
|
case 'demo':
|