@cavelang/cli 0.32.3 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +16 -3
  2. package/dist/internal/automate/main.d.ts.map +1 -1
  3. package/dist/internal/automate/main.js +7 -2
  4. package/dist/internal/automate/main.js.map +1 -1
  5. package/dist/internal/connect/main.d.ts.map +1 -1
  6. package/dist/internal/connect/main.js +16 -10
  7. package/dist/internal/connect/main.js.map +1 -1
  8. package/dist/internal/connect/template.d.ts +9 -2
  9. package/dist/internal/connect/template.d.ts.map +1 -1
  10. package/dist/internal/connect/template.js +36 -5
  11. package/dist/internal/connect/template.js.map +1 -1
  12. package/dist/internal/ingest/main.d.ts.map +1 -1
  13. package/dist/internal/ingest/main.js +18 -2
  14. package/dist/internal/ingest/main.js.map +1 -1
  15. package/dist/internal/loop/demo.d.ts.map +1 -1
  16. package/dist/internal/loop/demo.js +2 -1
  17. package/dist/internal/loop/demo.js.map +1 -1
  18. package/dist/internal/mcp/guide.js +3 -3
  19. package/dist/internal/mcp/guide.js.map +1 -1
  20. package/dist/internal/mcp/main.d.ts +1 -1
  21. package/dist/internal/mcp/main.d.ts.map +1 -1
  22. package/dist/internal/mcp/main.js +13 -3
  23. package/dist/internal/mcp/main.js.map +1 -1
  24. package/dist/internal/mcp/tools.d.ts.map +1 -1
  25. package/dist/internal/mcp/tools.js +39 -10
  26. package/dist/internal/mcp/tools.js.map +1 -1
  27. package/dist/internal/sync/sync.d.ts +2 -3
  28. package/dist/internal/sync/sync.d.ts.map +1 -1
  29. package/dist/internal/sync/sync.js +3 -15
  30. package/dist/internal/sync/sync.js.map +1 -1
  31. package/dist/internal/view/main.js +3 -3
  32. package/dist/internal/view/main.js.map +1 -1
  33. package/dist/internal/view/report.d.ts.map +1 -1
  34. package/dist/internal/view/report.js +6 -1
  35. package/dist/internal/view/report.js.map +1 -1
  36. package/dist/src/cli.d.ts +14 -5
  37. package/dist/src/cli.d.ts.map +1 -1
  38. package/dist/src/cli.js +224 -34
  39. package/dist/src/cli.js.map +1 -1
  40. package/dist/src/commands.d.ts +3 -0
  41. package/dist/src/commands.d.ts.map +1 -1
  42. package/dist/src/commands.js +1 -0
  43. package/dist/src/commands.js.map +1 -1
  44. package/dist/src/dispatch.d.ts.map +1 -1
  45. package/dist/src/dispatch.js +3 -1
  46. package/dist/src/dispatch.js.map +1 -1
  47. package/dist/src/doctor.js +1 -1
  48. package/dist/src/doctor.js.map +1 -1
  49. package/dist/src/index.d.ts +2 -2
  50. package/dist/src/index.d.ts.map +1 -1
  51. package/dist/src/index.js +2 -2
  52. package/dist/src/index.js.map +1 -1
  53. package/package.json +19 -19
  54. package/src/cli.ts +232 -37
  55. package/src/commands.ts +1 -0
  56. package/src/dispatch.ts +3 -1
  57. package/src/doctor.ts +1 -1
  58. package/src/index.ts +2 -2
package/dist/src/cli.d.ts CHANGED
@@ -9,12 +9,13 @@
9
9
  * - `cave highlight [file]` — ANSI syntax colors (async)
10
10
  * - `cave add [--db <path>] [file…]` — ingest into a store (`--strict`, `--check`)
11
11
  * - `cave query [--db <path>] '<pattern>'` — CAVE-Q (`--json`, `--all`, `--aliases`, `--as-of`, `--resolve`)
12
+ * - `cave search [--db <path>] <terms..>` — FTS5 full-text search over claims and comments (`--raw`, `--limit`, `--json`)
12
13
  * - `cave resolve [--db <path>]` — contested facts under the §26 policy (`--aliases`, `--policy`, `--json`)
13
14
  * - `cave derive [--db <path>] [rules.cave…]` — fire rules (`--dry-run`, `--full`, `--list`, `--retract`)
14
15
  * - `cave act [--db <path>] <name> [param=value…]` — execute an action (spec §25; `--declare`, `--list`, `--retract`)
15
16
  * - `cave automate [--db <path>]` — the event-driven loop (spec §29; async)
16
17
  * - `cave check [--db <path>]` — knowledge health report (`--stale`, `--json`)
17
- * - `cave backup [--db <path>] --out <path>` — exact verified SQLite snapshot
18
+ * - `cave backup [--db <path>] --out <path>` — exact verified store snapshot
18
19
  * - `cave restore <snapshot> --db <path>` — verify and atomically restore a snapshot
19
20
  * - `cave generate [--db <path>]` — versioned TypeScript client from EXPECTS declarations
20
21
  * - `cave suggest-alias [--db <path>]` — same-entity candidates (spec §27; async)
@@ -38,7 +39,7 @@ export type Output = {
38
39
  readonly out: string;
39
40
  readonly err: string;
40
41
  };
41
- 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 bounded CAVE-Q page [--limit <n>] [--cursor <token>] [--json]\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 automate [--db <path>] event-driven loop (spec \u00A729): new claims fire rules/actions/hooks/agent prompts [--once]\n cave check [--db <path>] knowledge health report (spec \u00A720) [--stale <days>] [--json]\n cave backup [--db <path>] --out <file> exact verified SQLite snapshot [--force]\n cave restore <snapshot> --db <path> verify and atomically restore an exact snapshot [--force]\n cave generate [--db <path>] generate a typed client from EXPECTS [--out <file>] [--version <n>]\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 filtered canonical CAVE text [--max-sensitivity <level>]\n cave serve [--db <path>] browse a filtered store (spec \u00A730) [--max-sensitivity <level>]\n cave report [--db <path>] [template...] render filtered cited markdown (spec \u00A731) [--max-sensitivity <level>]\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\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 doctor [--db <path>] diagnose runtime, installation, and store health [--hooks <file>] [--json]\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).";
42
+ 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 bounded CAVE-Q page [--limit <n>] [--cursor <token>] [--json]\n cave search [--db <path>] <terms..> full-text search over claims and comments (FTS5) [--raw] [--limit <n>] [--json]\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 automate [--db <path>] event-driven loop (spec \u00A729): new claims fire rules/actions/hooks/agent prompts [--once]\n cave check [--db <path>] knowledge health report (spec \u00A720) [--stale <days>] [--json]\n cave backup [--db <path>] --out <file> exact verified store snapshot [--force]\n cave restore <snapshot> --db <path> verify and atomically restore an exact snapshot [--force]\n cave generate [--db <path>] generate a typed client from EXPECTS [--out <file>] [--version <n>]\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 filtered canonical CAVE text [--max-sensitivity <level>]\n cave serve [--db <path>] browse a filtered store (spec \u00A730) [--max-sensitivity <level>]\n cave report [--db <path>] [template...] render filtered cited markdown (spec \u00A731) [--max-sensitivity <level>]\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\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 doctor [--db <path>] diagnose runtime, installation, and store health [--hooks <file>] [--json]\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, and names a SQLite store or a CAVE text file: text\nreplays into an in-memory store for commands that only read (writes refuse\nit), and a read never creates a missing database. The spec lives in the .claude/skills/ directory at\nthe repository root (section index in README.md).";
42
43
  /** Per-command help, printed for \`cave <command> --help\` and \`cave help <command>\`. */
43
44
  export declare const commandHelp: Record<string, string>;
44
45
  export declare const parseCommand: (argv: readonly string[]) => Output;
@@ -62,6 +63,14 @@ export declare const addCommand: (argv: readonly string[]) => Output;
62
63
  */
63
64
  export declare const importCommand: (argv: readonly string[]) => Output;
64
65
  export declare const queryCommand: (argv: readonly string[]) => Output;
66
+ export declare const searchFormat: "cave.search";
67
+ export declare const searchVersion: 1;
68
+ /**
69
+ * `cave search` — the store's FTS5 index (spec §13.2) from the shell:
70
+ * one literal phrase by default, raw MATCH syntax on request, newest
71
+ * matches first. Lines print as written, so comments come along.
72
+ */
73
+ export declare const searchCommand: (argv: readonly string[]) => Output;
65
74
  /**
66
75
  * `cave resolve` — the human view of contradiction resolution (spec §26.4):
67
76
  * contested facts with their candidates ranked as the policy sees them,
@@ -71,7 +80,7 @@ export declare const resolveCommand: (argv: readonly string[]) => Output;
71
80
  export declare const deriveCommand: (argv: readonly string[]) => Output;
72
81
  export declare const actCommand: (argv: readonly string[]) => Output;
73
82
  export declare const checkCommand: (argv: readonly string[]) => Output;
74
- /** `cave backup` — create or verify an exact SQLite snapshot (§13.2.2). */
83
+ /** `cave backup` — create or verify an exact store snapshot (§13.2.2). */
75
84
  export declare const backupCommand: (argv: readonly string[]) => Output;
76
85
  /** `cave restore` — verify and atomically restore an exact snapshot (§13.2.2). */
77
86
  export declare const restoreCommand: (argv: readonly string[]) => Output;
@@ -109,8 +118,8 @@ export declare const generateCommand: (argv: readonly string[]) => Output;
109
118
  export declare const reconstructCommand: (argv: readonly string[], runtime?: {
110
119
  readonly signal?: AbortSignal;
111
120
  }) => Promise<Output>;
112
- export declare const demoCommand: () => Output;
113
- export declare const versionCommand: () => Output;
121
+ export declare const demoCommand: (argv?: readonly string[]) => Output;
122
+ export declare const versionCommand: (argv?: readonly string[]) => Output;
114
123
  /** `cave help [command]` — the overview, or one command's help text. */
115
124
  export declare const helpCommand: (argv: readonly string[]) => Output;
116
125
  /** Dispatches one invocation. */
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AA8BH,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;AAyCD,eAAO,MAAM,KAAK,+rGAkCgC,CAAA;AAIlD,2FAA2F;AAC3F,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAujB9C,CAAA;AAyBD,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAyBtD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAU,MAAM,SAAS,MAAM,EAAE,KAAG,OAAO,CAAC,MAAM,CAM9E,CAAA;AAuED,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,MA2DtD,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;AASD,2EAA2E;AAC3E,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAqCvD,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA2BxD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,MAAM,SAAS,MAAM,EAAE,EACvB,UAAS;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,KAC9C,OAAO,CAAC,MAAM,CAgEhB,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAmDrD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA+CvD,CAAA;AAoBD,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAsCvD,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,eAAe,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAoCzD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,SAAS,MAAM,EAAE,EACvB,UAAS;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,KAC9C,OAAO,CAAC,MAAM,CAmEhB,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,MAqD9C,CAAA"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/src/cli.js CHANGED
@@ -9,12 +9,13 @@
9
9
  * - `cave highlight [file]` — ANSI syntax colors (async)
10
10
  * - `cave add [--db <path>] [file…]` — ingest into a store (`--strict`, `--check`)
11
11
  * - `cave query [--db <path>] '<pattern>'` — CAVE-Q (`--json`, `--all`, `--aliases`, `--as-of`, `--resolve`)
12
+ * - `cave search [--db <path>] <terms..>` — FTS5 full-text search over claims and comments (`--raw`, `--limit`, `--json`)
12
13
  * - `cave resolve [--db <path>]` — contested facts under the §26 policy (`--aliases`, `--policy`, `--json`)
13
14
  * - `cave derive [--db <path>] [rules.cave…]` — fire rules (`--dry-run`, `--full`, `--list`, `--retract`)
14
15
  * - `cave act [--db <path>] <name> [param=value…]` — execute an action (spec §25; `--declare`, `--list`, `--retract`)
15
16
  * - `cave automate [--db <path>]` — the event-driven loop (spec §29; async)
16
17
  * - `cave check [--db <path>]` — knowledge health report (`--stale`, `--json`)
17
- * - `cave backup [--db <path>] --out <path>` — exact verified SQLite snapshot
18
+ * - `cave backup [--db <path>] --out <path>` — exact verified store snapshot
18
19
  * - `cave restore <snapshot> --db <path>` — verify and atomically restore a snapshot
19
20
  * - `cave generate [--db <path>]` — versioned TypeScript client from EXPECTS declarations
20
21
  * - `cave suggest-alias [--db <path>]` — same-entity candidates (spec §27; async)
@@ -37,10 +38,10 @@ import { readFileSync, readSync, statSync, writeFileSync } from 'node:fs';
37
38
  import { resolve } from 'node:path';
38
39
  import { parseArgs } from 'node:util';
39
40
  import { Version } from '@cavelang/core';
40
- import { parseDocument } from '@cavelang/parser';
41
+ import { parseDocument, Token } from '@cavelang/parser';
41
42
  import { Registry, standardRegistry } from '@cavelang/canonical';
42
- import { Sensitivity, backup as backupStore, defaultDbPath, open, restoreBackup, verifyBackup } from '@cavelang/store';
43
- import { defaultLimit as defaultQueryLimit, page as caveQueryPage } from '@cavelang/query';
43
+ import { LocateError, Sensitivity, backup as backupStore, defaultDbPath, openAt, restoreBackup, verifyBackup } from '@cavelang/store';
44
+ import { defaultLimit as defaultQueryLimit, maxLimit as maxQueryLimit, page as caveQueryPage } from '@cavelang/query';
44
45
  import { check as caveCheck, defaultMinScore, defaultStaleDays, gatedIngest, judgePrompt, parseJudgeReply, generateClient, suggestAliases, writeSuggestions } from '@cavelang/cli/shape';
45
46
  import { declareRules, defaultMaxPasses, defaultMinConf, derive, listRules, retractRule } from '@cavelang/cli/rules';
46
47
  import { act, declareActions, listActions, retractAction } from '@cavelang/cli/act';
@@ -88,13 +89,14 @@ Usage:
88
89
  cave add [--db <path>] [file...] ingest into a store [--strict] [--check] [--no-prelude] [--no-src]
89
90
  cave import [--db <path>] [file...] restore/merge from CAVE text (add without @src: stamping)
90
91
  cave query [--db <path>] <pattern> run a bounded CAVE-Q page [--limit <n>] [--cursor <token>] [--json]
92
+ cave search [--db <path>] <terms..> full-text search over claims and comments (FTS5) [--raw] [--limit <n>] [--json]
91
93
  cave resolve [--db <path>] contested facts + winners (spec §26) [--aliases] [--policy] [--json]
92
94
  cave derive [--db <path>] [rules.cave..] declare + fire rules (spec §24) [--dry-run] [--full] [--list] [--retract <rule>]
93
95
  cave act [--db <path>] <name> [p=v...] execute an action (spec §25) [--dry-run] [--no-check] [--hooks <file>]
94
96
  cave act --declare [file...] declare actions from a CAVE document; --list / --retract <name> manage them
95
97
  cave automate [--db <path>] event-driven loop (spec §29): new claims fire rules/actions/hooks/agent prompts [--once]
96
98
  cave check [--db <path>] knowledge health report (spec §20) [--stale <days>] [--json]
97
- cave backup [--db <path>] --out <file> exact verified SQLite snapshot [--force]
99
+ cave backup [--db <path>] --out <file> exact verified store snapshot [--force]
98
100
  cave restore <snapshot> --db <path> verify and atomically restore an exact snapshot [--force]
99
101
  cave generate [--db <path>] generate a typed client from EXPECTS [--out <file>] [--version <n>]
100
102
  cave suggest-alias [--db <path>] propose same-entity ALIAS candidates (spec §27) [--min <s>] [--agent] [--write]
@@ -113,9 +115,11 @@ Usage:
113
115
  cave help [command] this text, or one command's options and examples
114
116
 
115
117
  Every command answers --help. --db defaults to $CAVE_DB, or cave.db in
116
- the current directory. The spec lives in the .claude/skills/ directory at
118
+ the current directory, and names a SQLite store or a CAVE text file: text
119
+ replays into an in-memory store for commands that only read (writes refuse
120
+ it), and a read never creates a missing database. The spec lives in the .claude/skills/ directory at
117
121
  the repository root (section index in README.md).`;
118
- const dbHelp = `--db <path> database file (default: $CAVE_DB, or cave.db)`;
122
+ const dbHelp = `--db <path> store: a SQLite file, or CAVE text read into memory (default: $CAVE_DB, or cave.db)`;
119
123
  /** Per-command help, printed for \`cave <command> --help\` and \`cave help <command>\`. */
120
124
  export const commandHelp = {
121
125
  doctor: `cave doctor — diagnose runtime, installation, and store health
@@ -244,10 +248,13 @@ Options:
244
248
 
245
249
  Patterns are claim triples with ?variables and optional metadata filters
246
250
  (spec §12). A second positional starting with WHERE filters on conf,
247
- value or tx.
251
+ value or tx. A binding line carries the matched claim's comment
252
+ (?x = value ; comment), so evidence written next to a claim travels
253
+ with the answer; fully bound patterns print the raw line, comment included.
248
254
 
249
255
  Examples:
250
256
  cave query '?x USES jwt'
257
+ cave query --db notes.cave '?x USES jwt'
251
258
  cave query --db k.db '?x HAS bug: ?bug #security'
252
259
  cave query --db k.db '?cause CAUSE app/crash' 'WHERE conf >= 0.5'
253
260
  cave query --db k.db 'terrier EXTENDS+ animal'
@@ -257,6 +264,38 @@ Examples:
257
264
  cave query --db k.db 'alice WORKS-AT ?org' --at 2021
258
265
  cave query --db k.db 'service HAS owner: ?who' --resolve
259
266
  cave query --db k.db '?x ?verb ?y @production' --json`,
267
+ search: `cave search — full-text search over claims and comments (spec §13.2)
268
+
269
+ Usage:
270
+ cave search [--db <path>] <terms...> [--raw] [--limit <n>] [--json] [--no-prelude]
271
+
272
+ Options:
273
+ ${dbHelp}
274
+ --raw treat the terms as FTS5 MATCH syntax: AND / OR / NOT,
275
+ NEAR(a b), prefix* and column filters such as
276
+ comment:heap or subject:auth
277
+ --limit <n> matches to print (default ${defaultQueryLimit}, maximum ${maxQueryLimit})
278
+ --json emit a versioned cave.search/v1 JSON object whose matches
279
+ are cave.claim/v1 records
280
+ --no-prelude open the store without the standard verb registry
281
+
282
+ Searches the full-text index the store maintains over every claim:
283
+ subject, verb, object, attribute name, value text, comment, and the raw
284
+ line as written (so tags, contexts, and inverse spellings match too).
285
+ Terms are one literal phrase by default — safe for hyphenated names such
286
+ as token-expiry, which raw FTS5 syntax would read as a column filter.
287
+ Tokens split on punctuation, so heap-dump also matches "heap dump" and
288
+ auth/middleware matches auth middleware. Results are newest first, one
289
+ raw line each, comments included; retracted and superseded rows stay
290
+ searchable (spec §9.6), so pair a hit with cave query or cave export
291
+ --current when currency matters.
292
+
293
+ Examples:
294
+ cave search 'heap dump'
295
+ cave search --db k.db token-expiry
296
+ cave search --db k.db --raw 'comment:heap AND subject:auth'
297
+ cave search --db k.db --raw 'jwt OR oauth*' --limit 20
298
+ cave search --db k.db 'rotated weekly' --json`,
260
299
  resolve: `cave resolve — contested facts and their winners (spec §26)
261
300
 
262
301
  Usage:
@@ -395,7 +434,7 @@ Examples:
395
434
  cave check --db k.db
396
435
  cave check --db k.db --stale 30
397
436
  cave check --db k.db --json | jq '.violations'`,
398
- backup: `cave backup — exact, verified SQLite snapshot (spec §13.2.2)
437
+ backup: `cave backup — exact, verified store snapshot (spec §13.2.2)
399
438
 
400
439
  Usage:
401
440
  cave backup [--db <path>] --out <file> [--force]
@@ -416,7 +455,7 @@ the restore verification token.
416
455
  Examples:
417
456
  cave backup --db k.db --out backups/k.db
418
457
  cave backup --verify backups/k.db --sha256 <hex>`,
419
- restore: `cave restore — verify and atomically restore an exact SQLite snapshot (spec §13.2.2)
458
+ restore: `cave restore — verify and atomically restore an exact store snapshot (spec §13.2.2)
420
459
 
421
460
  Usage:
422
461
  cave restore <snapshot> --db <path> [--force] [--sha256 <hex>]
@@ -664,7 +703,18 @@ topic → fix over a small in-memory store.`,
664
703
  version: `cave version — print the cave version
665
704
 
666
705
  Usage:
667
- cave version`
706
+ cave version`,
707
+ help: `cave help — the overview, or one command's options and examples
708
+
709
+ Usage:
710
+ cave help
711
+ cave help <command>
712
+
713
+ Every command answers --help with the same text as cave help <command>.
714
+
715
+ Examples:
716
+ cave help query
717
+ cave help serve`
668
718
  };
669
719
  const readStdin = () => {
670
720
  const chunks = [];
@@ -686,6 +736,19 @@ const readStdin = () => {
686
736
  }
687
737
  }
688
738
  };
739
+ /**
740
+ * Opens the store `--db` names (spec §13.7): a SQLite file, or a CAVE text
741
+ * file replayed into memory. `read` never creates, migrates, or changes a
742
+ * file — a missing path is an error, not an empty database; `scratch` is a
743
+ * dry run that appends inside a rolled-back transaction, so it may write
744
+ * but creates and migrates nothing; `write` creates a missing SQLite store,
745
+ * migrates an older one, and refuses text files with the materialization
746
+ * hint.
747
+ */
748
+ const openDb = (values, intent) => openAt(values.db ?? defaultDbPath(), {
749
+ intent,
750
+ ...values['no-prelude'] === true ? { registry: Registry.empty } : {}
751
+ });
689
752
  const readInput = (files) => files.length === 0 || (files.length === 1 && files[0] === '-') ?
690
753
  readStdin() :
691
754
  files.map(file => readFileSync(file, 'utf8')).join('\n');
@@ -762,7 +825,7 @@ const ingestCommand = (name) => (argv) => {
762
825
  allowPositionals: true
763
826
  });
764
827
  const input = readInput(positionals);
765
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
828
+ const store = openDb(values, 'write');
766
829
  try {
767
830
  const options = {
768
831
  strict: values.strict === true,
@@ -828,7 +891,7 @@ export const queryCommand = (argv) => {
828
891
  return fail('cave query: a pattern is required (spec §12.1)\n');
829
892
  }
830
893
  const pattern = positionals.join('\n');
831
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
894
+ const store = openDb(values, 'read');
832
895
  try {
833
896
  const limit = values.limit === undefined ? defaultQueryLimit : Number(values.limit);
834
897
  const result = caveQueryPage(store, pattern, {
@@ -853,7 +916,14 @@ export const queryCommand = (argv) => {
853
916
  .join(' ');
854
917
  // A fully bound pattern has no bindings to print; transitive matches
855
918
  // additionally carry no row — confirm with the pattern itself.
856
- const line = bindings !== '' ? bindings : match.claim?.claim.raw ?? pattern.split('\n')[0];
919
+ // Bindings carry the matched claim's comment so the evidence written
920
+ // next to a claim travels with the answer; raw lines already do. A
921
+ // multi-line comment (§6.4) opens above the binding line, as it does
922
+ // above a claim.
923
+ const comment = match.claim?.claim.comment;
924
+ const line = bindings !== '' ?
925
+ comment === undefined ? bindings : Token.joinComment(`${bindings} `, comment) :
926
+ match.claim?.claim.raw ?? pattern.split('\n')[0];
857
927
  // An interpolated trajectory shows its value at the anchor
858
928
  // (spec §32.4) — bindings already carry it in the value slot.
859
929
  return match.at !== undefined && bindings === '' ?
@@ -869,6 +939,66 @@ export const queryCommand = (argv) => {
869
939
  store.close();
870
940
  }
871
941
  };
942
+ export const searchFormat = 'cave.search';
943
+ export const searchVersion = 1;
944
+ /**
945
+ * `cave search` — the store's FTS5 index (spec §13.2) from the shell:
946
+ * one literal phrase by default, raw MATCH syntax on request, newest
947
+ * matches first. Lines print as written, so comments come along.
948
+ */
949
+ export const searchCommand = (argv) => {
950
+ const { values, positionals } = parseArgs({
951
+ args: [...argv],
952
+ options: {
953
+ db: { type: 'string' },
954
+ raw: { type: 'boolean' },
955
+ limit: { type: 'string' },
956
+ json: { type: 'boolean' },
957
+ 'no-prelude': { type: 'boolean' }
958
+ },
959
+ allowPositionals: true
960
+ });
961
+ const query = positionals.join(' ').trim();
962
+ if (query === '') {
963
+ return fail('cave search: search terms are required\n');
964
+ }
965
+ const limit = values.limit === undefined ? defaultQueryLimit : Number(values.limit);
966
+ if (!Number.isInteger(limit) || limit < 1 || limit > maxQueryLimit) {
967
+ return fail(`cave search: --limit must be an integer from 1 to ${maxQueryLimit}\n`);
968
+ }
969
+ const store = openDb(values, 'read');
970
+ try {
971
+ const raw = values.raw === true;
972
+ const found = store.search(query, { raw, limit: limit + 1 });
973
+ const matches = found.slice(0, limit);
974
+ const truncated = found.length > limit;
975
+ if (values.json === true) {
976
+ return ok(`${JSON.stringify({
977
+ format: searchFormat,
978
+ version: searchVersion,
979
+ query,
980
+ raw,
981
+ limit,
982
+ truncated,
983
+ matches: matches.map(row => store.recordOf(row))
984
+ }, undefined, 2)}\n`);
985
+ }
986
+ if (matches.length === 0) {
987
+ return ok('no matches\n');
988
+ }
989
+ const lines = matches.map(row => row.raw_line);
990
+ if (truncated) {
991
+ lines.push(`more matches beyond ${limit}; raise --limit`);
992
+ }
993
+ return ok(`${lines.join('\n')}\n`);
994
+ }
995
+ catch (error) {
996
+ return fail(`${error instanceof Error ? error.message : String(error)}\n`);
997
+ }
998
+ finally {
999
+ store.close();
1000
+ }
1001
+ };
872
1002
  /**
873
1003
  * `cave resolve` — the human view of contradiction resolution (spec §26.4):
874
1004
  * contested facts with their candidates ranked as the policy sees them,
@@ -887,7 +1017,7 @@ export const resolveCommand = (argv) => {
887
1017
  allowPositionals: false
888
1018
  });
889
1019
  const percent = (value) => `${Math.round(value * 1000) / 10}%`;
890
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1020
+ const store = openDb(values, 'read');
891
1021
  try {
892
1022
  if (values.policy === true) {
893
1023
  const policy = store.resolutionPolicy();
@@ -949,7 +1079,11 @@ export const deriveCommand = (argv) => {
949
1079
  if (maxPasses !== undefined && (!Number.isInteger(maxPasses) || maxPasses < 1)) {
950
1080
  return fail(`cave derive: --max-passes expects a positive integer, got ${JSON.stringify(values['max-passes'])}\n`);
951
1081
  }
952
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1082
+ // The intent follows the branch order below: listing reads, retracting
1083
+ // writes, a dry run appends inside a rolled-back transaction.
1084
+ const store = openDb(values, values.list === true ? 'read' :
1085
+ values.retract !== undefined ? 'write' :
1086
+ values['dry-run'] === true ? 'scratch' : 'write');
953
1087
  try {
954
1088
  if (values.list === true) {
955
1089
  const rules = listRules(store);
@@ -1103,7 +1237,13 @@ export const actCommand = (argv) => {
1103
1237
  },
1104
1238
  allowPositionals: true
1105
1239
  });
1106
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1240
+ // The intent follows the branch order below: declaring and retracting
1241
+ // write, listing reads, a dry-run execution appends inside a rolled-back
1242
+ // transaction.
1243
+ const store = openDb(values, values.declare === true ? 'write' :
1244
+ values.list === true ? 'read' :
1245
+ values.retract !== undefined ? 'write' :
1246
+ values['dry-run'] === true ? 'scratch' : 'write');
1107
1247
  try {
1108
1248
  if (values.declare === true) {
1109
1249
  const declaration = declareActions(store, readInput(positionals));
@@ -1221,7 +1361,7 @@ export const checkCommand = (argv) => {
1221
1361
  if (!Number.isFinite(staleDays) || staleDays < 0) {
1222
1362
  return fail(`cave check: --stale expects a non-negative number of days, got ${JSON.stringify(values.stale)}\n`);
1223
1363
  }
1224
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1364
+ const store = openDb(values, 'read');
1225
1365
  try {
1226
1366
  const report = caveCheck(store, { staleDays });
1227
1367
  const out = values.json === true ?
@@ -1236,7 +1376,7 @@ export const checkCommand = (argv) => {
1236
1376
  };
1237
1377
  const snapshotLine = (action, snapshot) => `${action} exact backup (${snapshot.rows} row(s), schema v${snapshot.schemaVersion}, ` +
1238
1378
  `${snapshot.bytes} bytes, sha256:${snapshot.sha256}) at ${snapshot.path}\n`;
1239
- /** `cave backup` — create or verify an exact SQLite snapshot (§13.2.2). */
1379
+ /** `cave backup` — create or verify an exact store snapshot (§13.2.2). */
1240
1380
  export const backupCommand = (argv) => {
1241
1381
  const { values } = parseArgs({
1242
1382
  args: [...argv],
@@ -1265,7 +1405,15 @@ export const backupCommand = (argv) => {
1265
1405
  if (values.out === undefined) {
1266
1406
  return fail('cave backup: --out <file> is required\n');
1267
1407
  }
1268
- const store = open(values.db ?? defaultDbPath());
1408
+ const dbPath = values.db ?? defaultDbPath();
1409
+ // A text store is replayed into memory, so the backup API cannot see that
1410
+ // the destination aliases the source: refuse before the file could be
1411
+ // replaced by a SQLite snapshot of itself (a SQLite source is covered by
1412
+ // the API's own check).
1413
+ if (sameFile(dbPath, values.out)) {
1414
+ return fail(`cave backup: --out '${values.out}' is the source database — refusing to overwrite it\n`);
1415
+ }
1416
+ const store = openAt(dbPath, { intent: 'read' });
1269
1417
  try {
1270
1418
  return ok(snapshotLine('created', backupStore(store, values.out, { force: values.force === true })));
1271
1419
  }
@@ -1346,7 +1494,7 @@ export const suggestAliasCommand = async (argv, runtime = {}) => {
1346
1494
  if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
1347
1495
  return fail(`cave suggest-alias: --timeout must be a positive number of seconds, got '${values.timeout}'\n`);
1348
1496
  }
1349
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1497
+ const store = openDb(values, values.write === true ? 'write' : 'read');
1350
1498
  try {
1351
1499
  let suggestions = suggestAliases(store, { minScore, ...limit === undefined ? {} : { limit } });
1352
1500
  if (values.agent !== undefined && suggestions.length > 0) {
@@ -1402,7 +1550,7 @@ export const syncCommand = (argv) => {
1402
1550
  return fail('cave sync: exactly one source is required — a CAVE store file, ;@-annotated text, or - for stdin (spec §28.5)\n');
1403
1551
  }
1404
1552
  const dbPath = values.db ?? defaultDbPath();
1405
- const store = open(dbPath, values['no-prelude'] === true ? { registry: Registry.empty } : {});
1553
+ const store = openDb(values, values['dry-run'] === true ? 'scratch' : 'write');
1406
1554
  try {
1407
1555
  const options = {
1408
1556
  into: values.into === undefined ? labelOf(dbPath) : sanitizeLabel(values.into),
@@ -1464,7 +1612,10 @@ export const reportCommand = (argv) => {
1464
1612
  if (maximum === undefined) {
1465
1613
  return fail(`cave report: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`);
1466
1614
  }
1467
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1615
+ if (values.out !== undefined && sameFile(values.db ?? defaultDbPath(), values.out)) {
1616
+ return fail(`cave report: --out '${values.out}' is the source database — refusing to overwrite it\n`);
1617
+ }
1618
+ const store = openDb(values, 'read');
1468
1619
  try {
1469
1620
  const rendered = caveReport(store, template, {
1470
1621
  aliases: values.aliases === true,
@@ -1533,7 +1684,7 @@ export const exportCommand = (argv) => {
1533
1684
  if (values.out !== undefined && sameFile(db, values.out)) {
1534
1685
  return fail(`cave export: --out '${values.out}' is the source database — refusing to overwrite it\n`);
1535
1686
  }
1536
- const store = open(db, values['no-prelude'] === true ? { registry: Registry.empty } : {});
1687
+ const store = openDb(values, 'read');
1537
1688
  try {
1538
1689
  const text = store.exportText({ current: values.current === true, tx: values.tx === true, maxSensitivity: maximum });
1539
1690
  if (values.out === undefined) {
@@ -1573,7 +1724,7 @@ export const generateCommand = (argv) => {
1573
1724
  if (values.out !== undefined && sameFile(db, values.out)) {
1574
1725
  return fail(`cave generate: --out '${values.out}' is the source database — refusing to overwrite it\n`);
1575
1726
  }
1576
- const store = open(db, values['no-prelude'] === true ? { registry: Registry.empty } : {});
1727
+ const store = openDb(values, 'read');
1577
1728
  try {
1578
1729
  const generated = generateClient(store, version === undefined ? {} : { version });
1579
1730
  if (!generated.ok) {
@@ -1636,7 +1787,7 @@ export const reconstructCommand = async (argv, runtime = {}) => {
1636
1787
  if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
1637
1788
  return fail(`cave reconstruct: --timeout must be a positive number of seconds, got '${values.timeout}'\n`);
1638
1789
  }
1639
- const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {});
1790
+ const store = openDb(values, 'read');
1640
1791
  try {
1641
1792
  const graph = sqliteStore(store);
1642
1793
  const result = values.agent === undefined ?
@@ -1645,9 +1796,11 @@ export const reconstructCommand = async (argv, runtime = {}) => {
1645
1796
  ...timeoutSeconds === undefined ? {} : { timeoutSeconds },
1646
1797
  ...runtime.signal === undefined ? {} : { signal: runtime.signal }
1647
1798
  }), { ...values.query === undefined ? {} : { query: values.query }, ...budgets }), positionals);
1799
+ // The trace is a documentary comment block: the blank line after it
1800
+ // keeps it off the first claim when the output is added back (§6.4).
1648
1801
  const lines = [
1649
- ...values.trace === true ?
1650
- result.trace.map(step => `; ${step.step}. ${step.cue.entity} @ ${step.cue.score.toFixed(2)} +${step.collected} claim(s)`) :
1802
+ ...values.trace === true && result.trace.length > 0 ?
1803
+ [...result.trace.map(step => `; ${step.step}. ${step.cue.entity} @ ${step.cue.score.toFixed(2)} +${step.collected} claim(s)`), ''] :
1651
1804
  [],
1652
1805
  ...result.claims.map(claim => emitClaim(claim))
1653
1806
  ];
@@ -1661,11 +1814,33 @@ export const reconstructCommand = async (argv, runtime = {}) => {
1661
1814
  return fail(`${error instanceof Error ? error.message : String(error)}\n`);
1662
1815
  }
1663
1816
  };
1664
- export const demoCommand = () => ok(`${Demo.run().lines.join('\n')}\n`);
1665
- export const versionCommand = () => ok(`${Version.current()}\n`);
1817
+ /**
1818
+ * Positional arguments of a command that declares no options: an unknown
1819
+ * option throws, which the dispatcher reports as an ordinary problem
1820
+ * (status 1).
1821
+ */
1822
+ const positionalsOf = (argv) => parseArgs({ args: [...argv], options: {}, allowPositionals: true }).positionals;
1823
+ /** The usage error (status 2) for more positionals than a command takes. */
1824
+ const surplusPositionals = (command, positionals, max) => positionals.length > max ? fail(`cave ${command}: unexpected positional arguments\n`, 2) : undefined;
1825
+ export const demoCommand = (argv = []) => {
1826
+ const problem = surplusPositionals('demo', positionalsOf(argv), 0);
1827
+ if (problem !== undefined)
1828
+ return problem;
1829
+ return ok(`${Demo.run().lines.join('\n')}\n`);
1830
+ };
1831
+ export const versionCommand = (argv = []) => {
1832
+ const problem = surplusPositionals('version', positionalsOf(argv), 0);
1833
+ if (problem !== undefined)
1834
+ return problem;
1835
+ return ok(`${Version.current()}\n`);
1836
+ };
1666
1837
  /** `cave help [command]` — the overview, or one command's help text. */
1667
1838
  export const helpCommand = (argv) => {
1668
- const [topic] = argv;
1839
+ const positionals = positionalsOf(argv);
1840
+ const problem = surplusPositionals('help', positionals, 1);
1841
+ if (problem !== undefined)
1842
+ return problem;
1843
+ const [topic] = positionals;
1669
1844
  if (topic === undefined) {
1670
1845
  return ok(`${usage}\n`);
1671
1846
  }
@@ -1687,6 +1862,19 @@ export const cave = (argv) => {
1687
1862
  (rest.includes('--help') || rest.includes('-h'))) {
1688
1863
  return ok(`${commandHelp[canonical]}\n`);
1689
1864
  }
1865
+ try {
1866
+ return run(canonical, rest);
1867
+ }
1868
+ catch (error) {
1869
+ // A --db path that is not what the command needs (spec §13.7) is a
1870
+ // usage failure, reported the way dispatch reports thrown errors.
1871
+ if (error instanceof LocateError) {
1872
+ return fail(`cave ${canonical}: ${error.message}\n`);
1873
+ }
1874
+ throw error;
1875
+ }
1876
+ };
1877
+ const run = (canonical, rest) => {
1690
1878
  switch (canonical) {
1691
1879
  case 'parse':
1692
1880
  return parseCommand(rest);
@@ -1696,6 +1884,8 @@ export const cave = (argv) => {
1696
1884
  return importCommand(rest);
1697
1885
  case 'query':
1698
1886
  return queryCommand(rest);
1887
+ case 'search':
1888
+ return searchCommand(rest);
1699
1889
  case 'resolve':
1700
1890
  return resolveCommand(rest);
1701
1891
  case 'derive':
@@ -1719,11 +1909,11 @@ export const cave = (argv) => {
1719
1909
  case 'doctor':
1720
1910
  return doctorCommand(rest);
1721
1911
  case 'demo':
1722
- return demoCommand();
1912
+ return demoCommand(rest);
1723
1913
  case 'version':
1724
1914
  case '--version':
1725
1915
  case '-v':
1726
- return versionCommand();
1916
+ return versionCommand(rest);
1727
1917
  case 'help':
1728
1918
  return helpCommand(rest);
1729
1919
  case undefined:
@@ -1731,7 +1921,7 @@ export const cave = (argv) => {
1731
1921
  case '-h':
1732
1922
  return ok(`${usage}\n`);
1733
1923
  default:
1734
- return fail(`cave: unknown command ${JSON.stringify(command)}\n\n${usage}\n`, 2);
1924
+ return fail(`cave: unknown command ${JSON.stringify(canonical)}\n\n${usage}\n`, 2);
1735
1925
  }
1736
1926
  };
1737
1927
  //# sourceMappingURL=cli.js.map