@cavelang/cli 0.27.3 → 0.29.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/Authors.md +5 -0
- package/License.md +6 -0
- package/README.md +122 -24
- package/dist/internal/act/action.d.ts +69 -0
- package/dist/internal/act/action.d.ts.map +1 -0
- package/dist/internal/act/action.js +201 -0
- package/dist/internal/act/action.js.map +1 -0
- package/dist/internal/act/declare.d.ts +102 -0
- package/dist/internal/act/declare.d.ts.map +1 -0
- package/dist/internal/act/declare.js +270 -0
- package/dist/internal/act/declare.js.map +1 -0
- package/dist/internal/act/engine.d.ts +103 -0
- package/dist/internal/act/engine.d.ts.map +1 -0
- package/dist/internal/act/engine.js +298 -0
- package/dist/internal/act/engine.js.map +1 -0
- package/dist/internal/act/index.d.ts +27 -0
- package/dist/internal/act/index.d.ts.map +1 -0
- package/dist/internal/act/index.js +25 -0
- package/dist/internal/act/index.js.map +1 -0
- package/dist/internal/automate/automation.d.ts +78 -0
- package/dist/internal/automate/automation.d.ts.map +1 -0
- package/dist/internal/automate/automation.js +135 -0
- package/dist/internal/automate/automation.js.map +1 -0
- package/dist/internal/automate/declare.d.ts +84 -0
- package/dist/internal/automate/declare.d.ts.map +1 -0
- package/dist/internal/automate/declare.js +233 -0
- package/dist/internal/automate/declare.js.map +1 -0
- package/dist/internal/automate/engine.d.ts +125 -0
- package/dist/internal/automate/engine.d.ts.map +1 -0
- package/dist/internal/automate/engine.js +388 -0
- package/dist/internal/automate/engine.js.map +1 -0
- package/dist/internal/automate/index.d.ts +28 -0
- package/dist/internal/automate/index.d.ts.map +1 -0
- package/dist/internal/automate/index.js +26 -0
- package/dist/internal/automate/index.js.map +1 -0
- package/dist/internal/automate/main.d.ts +26 -0
- package/dist/internal/automate/main.d.ts.map +1 -0
- package/dist/internal/automate/main.js +298 -0
- package/dist/internal/automate/main.js.map +1 -0
- package/dist/internal/connect/index.d.ts +26 -0
- package/dist/internal/connect/index.d.ts.map +1 -0
- package/dist/internal/connect/index.js +24 -0
- package/dist/internal/connect/index.js.map +1 -0
- package/dist/internal/connect/main.d.ts +23 -0
- package/dist/internal/connect/main.d.ts.map +1 -0
- package/dist/internal/connect/main.js +330 -0
- package/dist/internal/connect/main.js.map +1 -0
- package/dist/internal/connect/run.d.ts +85 -0
- package/dist/internal/connect/run.d.ts.map +1 -0
- package/dist/internal/connect/run.js +242 -0
- package/dist/internal/connect/run.js.map +1 -0
- package/dist/internal/connect/source.d.ts +44 -0
- package/dist/internal/connect/source.d.ts.map +1 -0
- package/dist/internal/connect/source.js +215 -0
- package/dist/internal/connect/source.js.map +1 -0
- package/dist/internal/connect/template.d.ts +69 -0
- package/dist/internal/connect/template.d.ts.map +1 -0
- package/dist/internal/connect/template.js +221 -0
- package/dist/internal/connect/template.js.map +1 -0
- package/dist/internal/eval/index.d.ts +28 -0
- package/dist/internal/eval/index.d.ts.map +1 -0
- package/dist/internal/eval/index.js +27 -0
- package/dist/internal/eval/index.js.map +1 -0
- package/dist/internal/eval/judge.d.ts +30 -0
- package/dist/internal/eval/judge.d.ts.map +1 -0
- package/dist/internal/eval/judge.js +101 -0
- package/dist/internal/eval/judge.js.map +1 -0
- package/dist/internal/eval/loop.d.ts +64 -0
- package/dist/internal/eval/loop.d.ts.map +1 -0
- package/dist/internal/eval/loop.js +123 -0
- package/dist/internal/eval/loop.js.map +1 -0
- package/dist/internal/eval/main.d.ts +17 -0
- package/dist/internal/eval/main.d.ts.map +1 -0
- package/dist/internal/eval/main.js +236 -0
- package/dist/internal/eval/main.js.map +1 -0
- package/dist/internal/eval/queries.d.ts +74 -0
- package/dist/internal/eval/queries.d.ts.map +1 -0
- package/dist/internal/eval/queries.js +161 -0
- package/dist/internal/eval/queries.js.map +1 -0
- package/dist/internal/eval/run.d.ts +143 -0
- package/dist/internal/eval/run.d.ts.map +1 -0
- package/dist/internal/eval/run.js +376 -0
- package/dist/internal/eval/run.js.map +1 -0
- package/dist/internal/eval/score.d.ts +81 -0
- package/dist/internal/eval/score.d.ts.map +1 -0
- package/dist/internal/eval/score.js +126 -0
- package/dist/internal/eval/score.js.map +1 -0
- package/dist/internal/eval/suite.d.ts +66 -0
- package/dist/internal/eval/suite.d.ts.map +1 -0
- package/dist/internal/eval/suite.js +130 -0
- package/dist/internal/eval/suite.js.map +1 -0
- package/dist/internal/ingest/context.d.ts +20 -0
- package/dist/internal/ingest/context.d.ts.map +1 -0
- package/dist/internal/ingest/context.js +59 -0
- package/dist/internal/ingest/context.js.map +1 -0
- package/dist/internal/ingest/files.d.ts +46 -0
- package/dist/internal/ingest/files.d.ts.map +1 -0
- package/dist/internal/ingest/files.js +115 -0
- package/dist/internal/ingest/files.js.map +1 -0
- package/dist/internal/ingest/index.d.ts +31 -0
- package/dist/internal/ingest/index.d.ts.map +1 -0
- package/dist/internal/ingest/index.js +29 -0
- package/dist/internal/ingest/index.js.map +1 -0
- package/dist/internal/ingest/main.d.ts +12 -0
- package/dist/internal/ingest/main.d.ts.map +1 -0
- package/dist/internal/ingest/main.js +180 -0
- package/dist/internal/ingest/main.js.map +1 -0
- package/dist/internal/ingest/prompt.d.ts +32 -0
- package/dist/internal/ingest/prompt.d.ts.map +1 -0
- package/dist/internal/ingest/prompt.js +75 -0
- package/dist/internal/ingest/prompt.js.map +1 -0
- package/dist/internal/ingest/run.d.ts +142 -0
- package/dist/internal/ingest/run.d.ts.map +1 -0
- package/dist/internal/ingest/run.js +350 -0
- package/dist/internal/ingest/run.js.map +1 -0
- package/dist/internal/ingest/web.d.ts +48 -0
- package/dist/internal/ingest/web.d.ts.map +1 -0
- package/dist/internal/ingest/web.js +140 -0
- package/dist/internal/ingest/web.js.map +1 -0
- package/dist/internal/loop/demo.d.ts +22 -0
- package/dist/internal/loop/demo.d.ts.map +1 -0
- package/dist/internal/loop/demo.js +67 -0
- package/dist/internal/loop/demo.js.map +1 -0
- package/dist/internal/loop/index.d.ts +20 -0
- package/dist/internal/loop/index.d.ts.map +1 -0
- package/dist/internal/loop/index.js +17 -0
- package/dist/internal/loop/index.js.map +1 -0
- package/dist/internal/loop/llm.d.ts +99 -0
- package/dist/internal/loop/llm.d.ts.map +1 -0
- package/dist/internal/loop/llm.js +231 -0
- package/dist/internal/loop/llm.js.map +1 -0
- package/dist/internal/loop/reconstruct.d.ts +87 -0
- package/dist/internal/loop/reconstruct.d.ts.map +1 -0
- package/dist/internal/loop/reconstruct.js +157 -0
- package/dist/internal/loop/reconstruct.js.map +1 -0
- package/dist/internal/loop/sqlite.d.ts +13 -0
- package/dist/internal/loop/sqlite.d.ts.map +1 -0
- package/dist/internal/loop/sqlite.js +33 -0
- package/dist/internal/loop/sqlite.js.map +1 -0
- package/dist/internal/loop/store.d.ts +50 -0
- package/dist/internal/loop/store.d.ts.map +1 -0
- package/dist/internal/loop/store.js +100 -0
- package/dist/internal/loop/store.js.map +1 -0
- package/dist/internal/mcp/bin.d.ts +7 -0
- package/dist/internal/mcp/bin.d.ts.map +1 -0
- package/dist/internal/mcp/bin.js +8 -0
- package/dist/internal/mcp/bin.js.map +1 -0
- package/dist/internal/mcp/index.d.ts +25 -0
- package/dist/internal/mcp/index.d.ts.map +1 -0
- package/dist/internal/mcp/index.js +23 -0
- package/dist/internal/mcp/index.js.map +1 -0
- package/dist/internal/mcp/main.d.ts +23 -0
- package/dist/internal/mcp/main.d.ts.map +1 -0
- package/dist/internal/mcp/main.js +156 -0
- package/dist/internal/mcp/main.js.map +1 -0
- package/dist/internal/mcp/server.d.ts +92 -0
- package/dist/internal/mcp/server.d.ts.map +1 -0
- package/dist/internal/mcp/server.js +251 -0
- package/dist/internal/mcp/server.js.map +1 -0
- package/dist/internal/mcp/tools.d.ts +91 -0
- package/dist/internal/mcp/tools.d.ts.map +1 -0
- package/dist/internal/mcp/tools.js +584 -0
- package/dist/internal/mcp/tools.js.map +1 -0
- package/dist/internal/rules/declare.d.ts +58 -0
- package/dist/internal/rules/declare.d.ts.map +1 -0
- package/dist/internal/rules/declare.js +152 -0
- package/dist/internal/rules/declare.js.map +1 -0
- package/dist/internal/rules/engine.d.ts +125 -0
- package/dist/internal/rules/engine.d.ts.map +1 -0
- package/dist/internal/rules/engine.js +575 -0
- package/dist/internal/rules/engine.js.map +1 -0
- package/dist/internal/rules/index.d.ts +23 -0
- package/dist/internal/rules/index.d.ts.map +1 -0
- package/dist/internal/rules/index.js +21 -0
- package/dist/internal/rules/index.js.map +1 -0
- package/dist/internal/rules/rule.d.ts +88 -0
- package/dist/internal/rules/rule.d.ts.map +1 -0
- package/dist/internal/rules/rule.js +221 -0
- package/dist/internal/rules/rule.js.map +1 -0
- package/dist/internal/shape/check.d.ts +118 -0
- package/dist/internal/shape/check.d.ts.map +1 -0
- package/dist/internal/shape/check.js +331 -0
- package/dist/internal/shape/check.js.map +1 -0
- package/dist/internal/shape/client.d.ts +31 -0
- package/dist/internal/shape/client.d.ts.map +1 -0
- package/dist/internal/shape/client.js +170 -0
- package/dist/internal/shape/client.js.map +1 -0
- package/dist/internal/shape/gate.d.ts +33 -0
- package/dist/internal/shape/gate.d.ts.map +1 -0
- package/dist/internal/shape/gate.js +0 -0
- package/dist/internal/shape/gate.js.map +1 -0
- package/dist/internal/shape/index.d.ts +27 -0
- package/dist/internal/shape/index.d.ts.map +1 -0
- package/dist/internal/shape/index.js +23 -0
- package/dist/internal/shape/index.js.map +1 -0
- package/dist/internal/shape/suggest.d.ts +82 -0
- package/dist/internal/shape/suggest.d.ts.map +1 -0
- package/dist/internal/shape/suggest.js +374 -0
- package/dist/internal/shape/suggest.js.map +1 -0
- package/dist/internal/sync/index.d.ts +22 -0
- package/dist/internal/sync/index.d.ts.map +1 -0
- package/dist/internal/sync/index.js +21 -0
- package/dist/internal/sync/index.js.map +1 -0
- package/dist/internal/sync/sync.d.ts +95 -0
- package/dist/internal/sync/sync.d.ts.map +1 -0
- package/dist/internal/sync/sync.js +342 -0
- package/dist/internal/sync/sync.js.map +1 -0
- package/dist/internal/view/api.d.ts +196 -0
- package/dist/internal/view/api.d.ts.map +1 -0
- package/dist/internal/view/api.js +189 -0
- package/dist/internal/view/api.js.map +1 -0
- package/dist/internal/view/index.d.ts +33 -0
- package/dist/internal/view/index.d.ts.map +1 -0
- package/dist/internal/view/index.js +30 -0
- package/dist/internal/view/index.js.map +1 -0
- package/dist/internal/view/main.d.ts +11 -0
- package/dist/internal/view/main.d.ts.map +1 -0
- package/dist/internal/view/main.js +98 -0
- package/dist/internal/view/main.js.map +1 -0
- package/dist/internal/view/page.d.ts +14 -0
- package/dist/internal/view/page.d.ts.map +1 -0
- package/dist/internal/view/page.js +370 -0
- package/dist/internal/view/page.js.map +1 -0
- package/dist/internal/view/report.d.ts +52 -0
- package/dist/internal/view/report.d.ts.map +1 -0
- package/dist/internal/view/report.js +367 -0
- package/dist/internal/view/report.js.map +1 -0
- package/dist/internal/view/scope.d.ts +11 -0
- package/dist/internal/view/scope.d.ts.map +1 -0
- package/dist/internal/view/scope.js +41 -0
- package/dist/internal/view/scope.js.map +1 -0
- package/dist/internal/view/server.d.ts +35 -0
- package/dist/internal/view/server.d.ts.map +1 -0
- package/dist/internal/view/server.js +141 -0
- package/dist/internal/view/server.js.map +1 -0
- package/dist/src/act.d.ts +3 -0
- package/dist/src/act.d.ts.map +1 -0
- package/dist/src/act.js +3 -0
- package/dist/src/act.js.map +1 -0
- package/dist/src/automate.d.ts +3 -0
- package/dist/src/automate.d.ts.map +1 -0
- package/dist/src/automate.js +3 -0
- package/dist/src/automate.js.map +1 -0
- package/dist/src/cli.d.ts +24 -17
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +416 -109
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands.d.ts +104 -0
- package/dist/src/commands.d.ts.map +1 -0
- package/dist/src/commands.js +33 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/connect.d.ts +3 -0
- package/dist/src/connect.d.ts.map +1 -0
- package/dist/src/connect.js +3 -0
- package/dist/src/connect.js.map +1 -0
- package/dist/src/dispatch.d.ts +14 -0
- package/dist/src/dispatch.d.ts.map +1 -0
- package/dist/src/dispatch.js +116 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/doctor.d.ts +45 -0
- package/dist/src/doctor.d.ts.map +1 -0
- package/dist/src/doctor.js +284 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/eval.d.ts +3 -0
- package/dist/src/eval.d.ts.map +1 -0
- package/dist/src/eval.js +3 -0
- package/dist/src/eval.js.map +1 -0
- package/dist/src/index.d.ts +9 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/ingest.d.ts +3 -0
- package/dist/src/ingest.d.ts.map +1 -0
- package/dist/src/ingest.js +3 -0
- package/dist/src/ingest.js.map +1 -0
- package/dist/src/loop.d.ts +3 -0
- package/dist/src/loop.d.ts.map +1 -0
- package/dist/src/loop.js +3 -0
- package/dist/src/loop.js.map +1 -0
- package/dist/src/main.d.ts +1 -1
- package/dist/src/main.d.ts.map +1 -1
- package/dist/src/main.js +3 -75
- package/dist/src/main.js.map +1 -1
- package/dist/src/mcp.d.ts +3 -0
- package/dist/src/mcp.d.ts.map +1 -0
- package/dist/src/mcp.js +3 -0
- package/dist/src/mcp.js.map +1 -0
- package/dist/src/rules.d.ts +3 -0
- package/dist/src/rules.d.ts.map +1 -0
- package/dist/src/rules.js +3 -0
- package/dist/src/rules.js.map +1 -0
- package/dist/src/shape.d.ts +3 -0
- package/dist/src/shape.d.ts.map +1 -0
- package/dist/src/shape.js +3 -0
- package/dist/src/shape.js.map +1 -0
- package/dist/src/sync.d.ts +3 -0
- package/dist/src/sync.d.ts.map +1 -0
- package/dist/src/sync.js +3 -0
- package/dist/src/sync.js.map +1 -0
- package/dist/src/view.d.ts +3 -0
- package/dist/src/view.d.ts.map +1 -0
- package/dist/src/view.js +3 -0
- package/dist/src/view.js.map +1 -0
- package/package.json +81 -21
- package/src/act.ts +2 -0
- package/src/automate.ts +2 -0
- package/src/cli.ts +423 -104
- package/src/commands.ts +48 -0
- package/src/connect.ts +2 -0
- package/src/dispatch.ts +141 -0
- package/src/doctor.ts +370 -0
- package/src/eval.ts +2 -0
- package/src/index.ts +9 -2
- package/src/ingest.ts +2 -0
- package/src/loop.ts +2 -0
- package/src/main.ts +3 -66
- package/src/mcp.ts +2 -0
- package/src/rules.ts +2 -0
- package/src/shape.ts +2 -0
- package/src/sync.ts +2 -0
- package/src/view.ts +2 -0
package/src/cli.ts
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `cave` command implementation — pure functions from parsed arguments to
|
|
3
|
-
* `{ code, out, err }`, so tests can call commands directly
|
|
4
|
-
*
|
|
3
|
+
* `{ code, out, err }`, so tests can call commands directly while
|
|
4
|
+
* `dispatch.ts` gives every command one awaited process lifecycle.
|
|
5
5
|
*
|
|
6
6
|
* Commands:
|
|
7
7
|
*
|
|
8
8
|
* - `cave parse [file]` — lint / dump the AST (`--json`)
|
|
9
|
-
* - `cave highlight [file]` — ANSI syntax colors (async
|
|
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
12
|
* - `cave resolve [--db <path>]` — contested facts under the §26 policy (`--aliases`, `--policy`, `--json`)
|
|
13
13
|
* - `cave derive [--db <path>] [rules.cave…]` — fire rules (`--dry-run`, `--full`, `--list`, `--retract`)
|
|
14
14
|
* - `cave act [--db <path>] <name> [param=value…]` — execute an action (spec §25; `--declare`, `--list`, `--retract`)
|
|
15
|
-
* - `cave automate [--db <path>]` — the event-driven loop (spec §29; async
|
|
15
|
+
* - `cave automate [--db <path>]` — the event-driven loop (spec §29; async)
|
|
16
16
|
* - `cave check [--db <path>]` — knowledge health report (`--stale`, `--json`)
|
|
17
|
-
* - `cave
|
|
17
|
+
* - `cave backup [--db <path>] --out <path>` — exact verified SQLite snapshot
|
|
18
|
+
* - `cave restore <snapshot> --db <path>` — verify and atomically restore a snapshot
|
|
19
|
+
* - `cave generate [--db <path>]` — versioned TypeScript client from EXPECTS declarations
|
|
20
|
+
* - `cave suggest-alias [--db <path>]` — same-entity candidates (spec §27; async)
|
|
18
21
|
* - `cave sync [--db <path>] <source>` — merge another store by row identity (spec §28; `--dry-run`, `--as`, `--into`)
|
|
19
22
|
* - `cave export [--db <path>]` — canonical text out (`--current`, `--tx`)
|
|
20
|
-
* - `cave serve [--db <path>]` — the human read surface in a browser (spec §30; async
|
|
23
|
+
* - `cave serve [--db <path>]` — the human read surface in a browser (spec §30; async)
|
|
21
24
|
* - `cave report [--db <path>] [template.md…]` — cited markdown from CAVE-Q template blocks (spec §31)
|
|
22
|
-
* - `cave connect [--db <path>] <source>` — deterministic structured ingestion (async
|
|
23
|
-
* - `cave eval <suite...>` — golden-fixture extraction/query/reconstruction evals (async
|
|
24
|
-
* - `cave reconstruct [--db <path>] <seed...>` — §18 reconstruction from seed cues (async
|
|
25
|
+
* - `cave connect [--db <path>] <source>` — deterministic structured ingestion (async)
|
|
26
|
+
* - `cave eval <suite...>` — golden-fixture extraction/query/reconstruction evals (async)
|
|
27
|
+
* - `cave reconstruct [--db <path>] <seed...>` — §18 reconstruction from seed cues (async)
|
|
28
|
+
* - `cave doctor [--db <path>]` — read-only runtime, installation, and store diagnostics
|
|
25
29
|
* - `cave demo` — the cave-loop multi-hop recovery demo
|
|
26
30
|
* - `cave version` — print the cave version
|
|
27
31
|
* - `cave help [command]` — overview, or one command's help
|
|
@@ -30,17 +34,18 @@
|
|
|
30
34
|
* `cave.db`. Every command answers `--help` with options and examples.
|
|
31
35
|
*/
|
|
32
36
|
|
|
33
|
-
import { readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
37
|
+
import { readFileSync, readSync, statSync, writeFileSync } from 'node:fs'
|
|
34
38
|
import { resolve } from 'node:path'
|
|
35
39
|
import { parseArgs } from 'node:util'
|
|
36
40
|
import { Version } from '@cavelang/core'
|
|
37
41
|
import { parseDocument } from '@cavelang/parser'
|
|
38
42
|
import { Registry, standardRegistry } from '@cavelang/canonical'
|
|
39
|
-
import { defaultDbPath, open } from '@cavelang/store'
|
|
40
|
-
import {
|
|
43
|
+
import { Sensitivity, backup as backupStore, defaultDbPath, open, restoreBackup, verifyBackup } from '@cavelang/store'
|
|
44
|
+
import type { Store } from '@cavelang/store'
|
|
45
|
+
import { defaultLimit as defaultQueryLimit, page as caveQueryPage } from '@cavelang/query'
|
|
41
46
|
import {
|
|
42
47
|
check as caveCheck, defaultMinScore, defaultStaleDays, gatedIngest, judgePrompt, parseJudgeReply,
|
|
43
|
-
suggestAliases, writeSuggestions
|
|
48
|
+
generateClient, suggestAliases, writeSuggestions
|
|
44
49
|
} from '@cavelang/shape'
|
|
45
50
|
import type { Report, Violation } from '@cavelang/shape'
|
|
46
51
|
import { declareRules, defaultMaxPasses, defaultMinConf, derive, listRules, retractRule } from '@cavelang/rules'
|
|
@@ -53,7 +58,9 @@ import {
|
|
|
53
58
|
import { labelOf, sanitizeLabel, syncFile, syncText } from '@cavelang/sync'
|
|
54
59
|
import type { SyncReport } from '@cavelang/sync'
|
|
55
60
|
import { report as caveReport } from '@cavelang/view'
|
|
56
|
-
import { emitClaim
|
|
61
|
+
import { emitClaim } from '@cavelang/canonical'
|
|
62
|
+
import { doctorCommand } from './doctor.ts'
|
|
63
|
+
import { delegatedCommandNames } from './commands.ts'
|
|
57
64
|
|
|
58
65
|
export type Output = {
|
|
59
66
|
readonly code: number
|
|
@@ -61,6 +68,39 @@ export type Output = {
|
|
|
61
68
|
readonly err: string
|
|
62
69
|
}
|
|
63
70
|
|
|
71
|
+
const storageRow = (value: unknown): value is Parameters<Store['recordOf']>[0] =>
|
|
72
|
+
typeof value === 'object' && value !== null &&
|
|
73
|
+
typeof (value as Record<string, unknown>)['id'] === 'string' &&
|
|
74
|
+
typeof (value as Record<string, unknown>)['tx'] === 'string' &&
|
|
75
|
+
typeof (value as Record<string, unknown>)['claim_key'] === 'string' &&
|
|
76
|
+
typeof (value as Record<string, unknown>)['raw_line'] === 'string'
|
|
77
|
+
|
|
78
|
+
/** Recursively replace storage rows in JSON reports with cave.claim/v1 records. */
|
|
79
|
+
const stableJsonValue = (store: Store, value: unknown): unknown => {
|
|
80
|
+
if (storageRow(value)) {
|
|
81
|
+
const record = store.recordOf(value)
|
|
82
|
+
const ranked = value as typeof value & Record<string, unknown>
|
|
83
|
+
return typeof ranked['res_rank'] === 'number' ? {
|
|
84
|
+
...record,
|
|
85
|
+
resolution: {
|
|
86
|
+
group: ranked['res_group'],
|
|
87
|
+
class: ranked['res_class'],
|
|
88
|
+
effectiveConfidence: ranked['res_conf'],
|
|
89
|
+
rank: ranked['res_rank'],
|
|
90
|
+
},
|
|
91
|
+
} : record
|
|
92
|
+
}
|
|
93
|
+
if (Array.isArray(value)) return value.map(entry => stableJsonValue(store, entry))
|
|
94
|
+
if (typeof value === 'object' && value !== null) {
|
|
95
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) =>
|
|
96
|
+
[key, stableJsonValue(store, entry)]))
|
|
97
|
+
}
|
|
98
|
+
return value
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const stableJson = (store: Store, value: unknown): string =>
|
|
102
|
+
JSON.stringify(stableJsonValue(store, value), undefined, 2)
|
|
103
|
+
|
|
64
104
|
const ok = (out: string): Output =>
|
|
65
105
|
({ code: 0, out, err: '' })
|
|
66
106
|
|
|
@@ -74,23 +114,27 @@ Usage:
|
|
|
74
114
|
cave highlight [file...] print CAVE text with ANSI syntax colors
|
|
75
115
|
cave add [--db <path>] [file...] ingest into a store [--strict] [--check] [--no-prelude] [--no-src]
|
|
76
116
|
cave import [--db <path>] [file...] restore/merge from CAVE text (add without @src: stamping)
|
|
77
|
-
cave query [--db <path>] <pattern> run a CAVE-Q
|
|
117
|
+
cave query [--db <path>] <pattern> run a bounded CAVE-Q page [--limit <n>] [--cursor <token>] [--json]
|
|
78
118
|
cave resolve [--db <path>] contested facts + winners (spec §26) [--aliases] [--policy] [--json]
|
|
79
119
|
cave derive [--db <path>] [rules.cave..] declare + fire rules (spec §24) [--dry-run] [--full] [--list] [--retract <rule>]
|
|
80
120
|
cave act [--db <path>] <name> [p=v...] execute an action (spec §25) [--dry-run] [--no-check] [--hooks <file>]
|
|
81
121
|
cave act --declare [file...] declare actions from a CAVE document; --list / --retract <name> manage them
|
|
82
122
|
cave automate [--db <path>] event-driven loop (spec §29): new claims fire rules/actions/hooks/agent prompts [--once]
|
|
83
123
|
cave check [--db <path>] knowledge health report (spec §20) [--stale <days>] [--json]
|
|
124
|
+
cave backup [--db <path>] --out <file> exact verified SQLite snapshot [--force]
|
|
125
|
+
cave restore <snapshot> --db <path> verify and atomically restore an exact snapshot [--force]
|
|
126
|
+
cave generate [--db <path>] generate a typed client from EXPECTS [--out <file>] [--version <n>]
|
|
84
127
|
cave suggest-alias [--db <path>] propose same-entity ALIAS candidates (spec §27) [--min <s>] [--agent] [--write]
|
|
85
128
|
cave sync [--db <path>] <source> merge another store by row identity (spec §28) [--dry-run] [--as] [--into]
|
|
86
|
-
cave export [--db <path>] [--out <file>] emit canonical CAVE text [--
|
|
87
|
-
cave serve [--db <path>] browse
|
|
88
|
-
cave report [--db <path>] [template...] render cited markdown
|
|
129
|
+
cave export [--db <path>] [--out <file>] emit filtered canonical CAVE text [--max-sensitivity <level>]
|
|
130
|
+
cave serve [--db <path>] browse a filtered store (spec §30) [--max-sensitivity <level>]
|
|
131
|
+
cave report [--db <path>] [template...] render filtered cited markdown (spec §31) [--max-sensitivity <level>]
|
|
89
132
|
cave mcp [--db <path>] serve the engine as an MCP server on stdio [--no-prelude]
|
|
90
133
|
cave ingest [--db <path>] <globs/urls..> LLM-driven ingestion of files and web pages
|
|
91
|
-
cave eval <suite..> --agent '<command>' golden-fixture extraction/query/reconstruction evals
|
|
134
|
+
cave eval <suite..> --agent '<command>' golden-fixture extraction/query/reconstruction evals
|
|
92
135
|
cave connect <source> --map <file> deterministic structured ingestion (CSV/JSON/SQLite/URL, spec §23)
|
|
93
136
|
cave reconstruct [--db <path>] <seed..> reconstruct memory from seed cues (spec §18) [--agent] [--query] [--trace]
|
|
137
|
+
cave doctor [--db <path>] diagnose runtime, installation, and store health [--hooks <file>] [--json]
|
|
94
138
|
cave demo run the cave-loop reconstruction demo
|
|
95
139
|
cave version print the cave version
|
|
96
140
|
cave help [command] this text, or one command's options and examples
|
|
@@ -103,6 +147,26 @@ const dbHelp = `--db <path> database file (default: $CAVE_DB, or cave.db)`
|
|
|
103
147
|
|
|
104
148
|
/** Per-command help, printed for \`cave <command> --help\` and \`cave help <command>\`. */
|
|
105
149
|
export const commandHelp: Record<string, string> = {
|
|
150
|
+
doctor: `cave doctor — diagnose runtime, installation, and store health
|
|
151
|
+
|
|
152
|
+
Usage:
|
|
153
|
+
cave doctor [--db <path>] [--hooks <file>] [--json]
|
|
154
|
+
|
|
155
|
+
Options:
|
|
156
|
+
${dbHelp}
|
|
157
|
+
--hooks <file> validate an optional hooks JSON file
|
|
158
|
+
--json emit the versioned cave.doctor/v1 report
|
|
159
|
+
|
|
160
|
+
The command is read-only: it never creates or migrates a database and never
|
|
161
|
+
runs hooks. Its output is safe to share — database and hooks paths, hook names
|
|
162
|
+
and commands, URLs, and environment values are not included. Warnings such as
|
|
163
|
+
a not-yet-created database exit 0; failed required checks exit 1.
|
|
164
|
+
|
|
165
|
+
Examples:
|
|
166
|
+
cave doctor
|
|
167
|
+
cave doctor --db knowledge.db
|
|
168
|
+
cave doctor --hooks hooks.json --json`,
|
|
169
|
+
|
|
106
170
|
parse: `cave parse — lint CAVE text, or dump the parsed document
|
|
107
171
|
|
|
108
172
|
Usage:
|
|
@@ -178,24 +242,28 @@ file read as ordinary comments; to merge under the recorded row
|
|
|
178
242
|
identity, use \`cave sync\` (spec §28.4).
|
|
179
243
|
|
|
180
244
|
Examples:
|
|
181
|
-
cave export --db old.db --out backup.cave
|
|
245
|
+
cave export --db old.db --max-sensitivity restricted --out backup.cave
|
|
182
246
|
cave import --db new.db backup.cave`,
|
|
183
247
|
|
|
184
248
|
query: `cave query — run a CAVE-Q pattern against a store
|
|
185
249
|
|
|
186
250
|
Usage:
|
|
187
|
-
cave query [--db <path>] <pattern> [WHERE <filter>] [--json] [--all] [--aliases] [--as-of <t>] [--at <t>] [--resolve] [--no-prelude]
|
|
251
|
+
cave query [--db <path>] <pattern> [WHERE <filter>] [--limit <n>] [--cursor <token>] [--json] [--all] [--aliases] [--as-of <t>] [--at <t>] [--resolve] [--no-prelude]
|
|
188
252
|
|
|
189
253
|
Options:
|
|
190
254
|
${dbHelp}
|
|
191
|
-
--json emit
|
|
255
|
+
--json emit a versioned cave.query-page/v1 JSON object
|
|
256
|
+
--limit <n> matches per page (default ${defaultQueryLimit}, maximum 1000)
|
|
257
|
+
--cursor <t> continue the same frozen query snapshot
|
|
192
258
|
--all match all beliefs, not just current ones
|
|
193
259
|
--aliases resolve entities through current ALIAS claims (spec §13.6)
|
|
194
260
|
--as-of <t> resolve beliefs as of a past moment (spec §12.3): a date
|
|
195
261
|
(whole day included), a timestamp (whole second), or a
|
|
196
|
-
transaction id — rows recorded later are invisible
|
|
262
|
+
transaction id — rows recorded later are invisible;
|
|
263
|
+
timestamps without an offset mean UTC
|
|
197
264
|
--at <t> anchor in valid time (spec §32.4): a date-like period
|
|
198
|
-
(read as its start instant) or a timestamp
|
|
265
|
+
(read as its start instant) or a timestamp (offset-less
|
|
266
|
+
means UTC). Claims whose
|
|
199
267
|
time contexts — @2026-Q1 points, @2025..2028 ranges — do
|
|
200
268
|
not cover the instant are invisible; timeless claims
|
|
201
269
|
always match; trajectory values (20B -> 40B USD/yr)
|
|
@@ -355,7 +423,8 @@ Options:
|
|
|
355
423
|
--no-prelude open the store without the standard verb registry
|
|
356
424
|
|
|
357
425
|
Reads the store against its own in-band EXPECTS declarations (spec §20.1)
|
|
358
|
-
and reports
|
|
426
|
+
and reports actionable presence, #cardinality:one, and #unit:<unit>
|
|
427
|
+
violations, stale current beliefs, review candidates
|
|
359
428
|
(conf 0.3–0.7), alias disagreements (spec §13.6) and coverage stats.
|
|
360
429
|
Exits 1 when violations exist; everything else is advisory.
|
|
361
430
|
|
|
@@ -364,6 +433,69 @@ Examples:
|
|
|
364
433
|
cave check --db k.db --stale 30
|
|
365
434
|
cave check --db k.db --json | jq '.violations'`,
|
|
366
435
|
|
|
436
|
+
backup: `cave backup — exact, verified SQLite snapshot (spec §13.2.2)
|
|
437
|
+
|
|
438
|
+
Usage:
|
|
439
|
+
cave backup [--db <path>] --out <file> [--force]
|
|
440
|
+
cave backup --verify <file> [--sha256 <hex>]
|
|
441
|
+
|
|
442
|
+
Options:
|
|
443
|
+
${dbHelp}
|
|
444
|
+
--out <file> destination snapshot (required when creating)
|
|
445
|
+
--force atomically replace an existing snapshot
|
|
446
|
+
--verify <file> verify integrity, foreign keys, schema and SHA-256 only
|
|
447
|
+
--sha256 <hex> expected digest for --verify
|
|
448
|
+
|
|
449
|
+
The source remains online. SQLite VACUUM INTO takes one consistent committed
|
|
450
|
+
snapshot, including WAL-visible rows; CAVE writes to a temporary file,
|
|
451
|
+
validates it, fsyncs it, then publishes it atomically. The summary SHA-256 is
|
|
452
|
+
the restore verification token.
|
|
453
|
+
|
|
454
|
+
Examples:
|
|
455
|
+
cave backup --db k.db --out backups/k.db
|
|
456
|
+
cave backup --verify backups/k.db --sha256 <hex>`,
|
|
457
|
+
|
|
458
|
+
restore: `cave restore — verify and atomically restore an exact SQLite snapshot (spec §13.2.2)
|
|
459
|
+
|
|
460
|
+
Usage:
|
|
461
|
+
cave restore <snapshot> --db <path> [--force] [--sha256 <hex>]
|
|
462
|
+
|
|
463
|
+
Options:
|
|
464
|
+
--db <path> destination database (required)
|
|
465
|
+
--force atomically replace an existing destination
|
|
466
|
+
--sha256 <hex> require the recorded backup digest
|
|
467
|
+
|
|
468
|
+
Stop every process using the destination first. Restore refuses WAL/SHM
|
|
469
|
+
sidecars, verifies the input and copied temporary database, then publishes one
|
|
470
|
+
atomic file. WAL, SHM, or rollback-journal sidecars cause a refusal. On
|
|
471
|
+
failure the prior destination remains untouched.
|
|
472
|
+
|
|
473
|
+
Example:
|
|
474
|
+
cave restore backups/k.db --db restored.db --sha256 <hex>`,
|
|
475
|
+
|
|
476
|
+
generate: `cave generate — deterministic TypeScript client from EXPECTS declarations (spec §20.4)
|
|
477
|
+
|
|
478
|
+
Usage:
|
|
479
|
+
cave generate [--db <path>] [--out <file>] [--version <n>] [--no-prelude]
|
|
480
|
+
|
|
481
|
+
Options:
|
|
482
|
+
${dbHelp}
|
|
483
|
+
--out <file> write the generated module instead of stdout
|
|
484
|
+
--version <n> generated-client format version (default and currently
|
|
485
|
+
supported: 1)
|
|
486
|
+
--no-prelude open the store without the standard verb registry
|
|
487
|
+
|
|
488
|
+
Current positive EXPECTS claims become TypeScript interfaces plus store-backed
|
|
489
|
+
reader functions. #cardinality:one emits a scalar checked at runtime;
|
|
490
|
+
attributes preserve text, number and unit; relation readers honor declared
|
|
491
|
+
inverse verbs. The file embeds its format version and deterministic schema
|
|
492
|
+
SHA-256. Conflicting declarations, invalid cardinality/unit tags, unsupported
|
|
493
|
+
versions and generated type-name collisions fail without writing output.
|
|
494
|
+
|
|
495
|
+
Examples:
|
|
496
|
+
cave generate --db k.db > cave-client.ts
|
|
497
|
+
cave generate --db k.db --out src/generated/cave-client.ts`,
|
|
498
|
+
|
|
367
499
|
'suggest-alias': `cave suggest-alias — propose same-entity ALIAS candidates for review (spec §27)
|
|
368
500
|
|
|
369
501
|
Usage:
|
|
@@ -447,7 +579,7 @@ a merge driver on *.cave files.
|
|
|
447
579
|
Examples:
|
|
448
580
|
cave sync --db main.db laptop.db
|
|
449
581
|
cave sync --db main.db laptop.db --dry-run --json
|
|
450
|
-
cave export --db laptop.db --tx | cave sync --db main.db -
|
|
582
|
+
cave export --db laptop.db --tx --max-sensitivity restricted | cave sync --db main.db -
|
|
451
583
|
cave sync --db a.db b.db && cave sync --db b.db a.db # converge both
|
|
452
584
|
cave sync --db work.db knowledge.cave --no-record # checkout (§28.6)
|
|
453
585
|
cave sync --db main.db knowledge.cave --as reorg-auth # land the branch`,
|
|
@@ -455,32 +587,43 @@ Examples:
|
|
|
455
587
|
export: `cave export — emit canonical CAVE text from a store
|
|
456
588
|
|
|
457
589
|
Usage:
|
|
458
|
-
cave export [--db <path>] [--out <file>] [--current] [--tx]
|
|
590
|
+
cave export [--db <path>] [--out <file>] [--current] [--tx]
|
|
591
|
+
[--max-sensitivity <level>] [--no-prelude]
|
|
459
592
|
|
|
460
593
|
Options:
|
|
461
594
|
${dbHelp}
|
|
462
595
|
--out <file> write to a file instead of stdout — refused when it
|
|
463
596
|
names the store's own database file
|
|
464
|
-
--current current beliefs only (skip superseded rows)
|
|
597
|
+
--current current beliefs only (skip superseded rows); compacting
|
|
598
|
+
view, not sanitization or selective erasure (spec §9.6)
|
|
599
|
+
--max-sensitivity <level>
|
|
600
|
+
public, internal, confidential, or restricted (default
|
|
601
|
+
internal; restricted includes complete portable history)
|
|
465
602
|
--tx precede every claim line with its ;@ transaction
|
|
466
603
|
annotation (spec §28.4) — the text carries row identity,
|
|
467
604
|
so cave sync replays it idempotently; other readers see
|
|
468
605
|
ordinary comments
|
|
469
606
|
--no-prelude open the store without the standard verb registry
|
|
470
607
|
|
|
608
|
+
Retention: claim history is permanent. Retraction and --current do not
|
|
609
|
+
guarantee erasure from the store, exports, peers, or backups (spec §9.6).
|
|
610
|
+
|
|
471
611
|
Examples:
|
|
472
612
|
cave export --db k.db
|
|
473
|
-
cave export --db k.db --out
|
|
613
|
+
cave export --db k.db --out internal-export.cave
|
|
614
|
+
cave export --db k.db --max-sensitivity restricted --out complete-history.cave
|
|
474
615
|
cave export --db k.db --current --out snapshot.cave
|
|
475
|
-
cave export --db k.db --tx | cave sync --db other.db -
|
|
476
|
-
cave export --db work.db --tx --out knowledge.cave
|
|
616
|
+
cave export --db k.db --tx --max-sensitivity restricted | cave sync --db other.db -
|
|
617
|
+
cave export --db work.db --tx --max-sensitivity restricted --out knowledge.cave
|
|
618
|
+
# the committed,
|
|
477
619
|
# reviewable store text (spec §28.6)`,
|
|
478
620
|
|
|
479
621
|
report: `cave report — render cited markdown from a CAVE-Q template (spec §31)
|
|
480
622
|
|
|
481
623
|
Usage:
|
|
482
624
|
cave report [--db <path>] [template.md...] [--out <file>]
|
|
483
|
-
[--aliases] [--resolve] [--as-of <t>] [--at <t>]
|
|
625
|
+
[--aliases] [--resolve] [--as-of <t>] [--at <t>]
|
|
626
|
+
[--max-sensitivity <level>] [--no-prelude]
|
|
484
627
|
|
|
485
628
|
Options:
|
|
486
629
|
${dbHelp}
|
|
@@ -493,6 +636,9 @@ Options:
|
|
|
493
636
|
--at <t> anchor every query in valid time (spec §32.4): claims
|
|
494
637
|
whose time contexts don't cover the instant drop out,
|
|
495
638
|
trajectory values render interpolated
|
|
639
|
+
--max-sensitivity <level>
|
|
640
|
+
public, internal, confidential, or restricted (default
|
|
641
|
+
internal; malformed/unknown claim labels fail closed)
|
|
496
642
|
--no-prelude open the store without the standard verb registry
|
|
497
643
|
|
|
498
644
|
Reads stdin when no file (or \`-\`) is given. A template is ordinary
|
|
@@ -568,9 +714,27 @@ Usage:
|
|
|
568
714
|
cave version`
|
|
569
715
|
}
|
|
570
716
|
|
|
717
|
+
const readStdin = (): string => {
|
|
718
|
+
const chunks: Buffer[] = []
|
|
719
|
+
const buffer = Buffer.allocUnsafe(64 * 1024)
|
|
720
|
+
const pause = new Int32Array(new SharedArrayBuffer(4))
|
|
721
|
+
for (;;) {
|
|
722
|
+
try {
|
|
723
|
+
const size = readSync(0, buffer, 0, buffer.length, null)
|
|
724
|
+
if (size === 0) return Buffer.concat(chunks).toString('utf8')
|
|
725
|
+
chunks.push(Buffer.from(buffer.subarray(0, size)))
|
|
726
|
+
} catch (error) {
|
|
727
|
+
if ((error as NodeJS.ErrnoException).code !== 'EAGAIN') throw error
|
|
728
|
+
// A pipe may be non-blocking on macOS. The producer is another process,
|
|
729
|
+
// so a short synchronous retry does not prevent it from making progress.
|
|
730
|
+
Atomics.wait(pause, 0, 0, 5)
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
571
735
|
const readInput = (files: readonly string[]): string =>
|
|
572
736
|
files.length === 0 || (files.length === 1 && files[0] === '-') ?
|
|
573
|
-
|
|
737
|
+
readStdin() :
|
|
574
738
|
files.map(file => readFileSync(file, 'utf8')).join('\n')
|
|
575
739
|
|
|
576
740
|
export const parseCommand = (argv: readonly string[]): Output => {
|
|
@@ -602,8 +766,7 @@ export const parseCommand = (argv: readonly string[]): Output => {
|
|
|
602
766
|
|
|
603
767
|
/**
|
|
604
768
|
* `cave highlight` — ANSI syntax colors from the tree-sitter grammar's own
|
|
605
|
-
* highlight query. Async
|
|
606
|
-
* routes it separately, like `mcp` and `ingest`.
|
|
769
|
+
* highlight query. Async because the grammar WASM loads on first use.
|
|
607
770
|
*/
|
|
608
771
|
export const highlightCommand = async (argv: readonly string[]): Promise<Output> => {
|
|
609
772
|
const { positionals } = parseArgs({ args: [...argv], options: {}, allowPositionals: true })
|
|
@@ -620,9 +783,25 @@ export const highlightCommand = async (argv: readonly string[]): Promise<Output>
|
|
|
620
783
|
* replays interchange text, which must preserve claim keys as exported,
|
|
621
784
|
* so it never stamps.
|
|
622
785
|
*/
|
|
786
|
+
const formatViolationProblem = (violation: Violation): string => {
|
|
787
|
+
const { entity, expectation, actualCount, actualUnits } = violation
|
|
788
|
+
if (actualCount === 0) {
|
|
789
|
+
return `${entity} missing ${expectation.kind} ${expectation.name}`
|
|
790
|
+
}
|
|
791
|
+
const problems: string[] = []
|
|
792
|
+
if (expectation.cardinality === 'one' && actualCount !== 1) {
|
|
793
|
+
problems.push(`${entity} has ${actualCount} ${expectation.kind}s ${expectation.name}; expected exactly one`)
|
|
794
|
+
}
|
|
795
|
+
if (expectation.unit !== undefined && actualUnits.some(unit => unit !== expectation.unit)) {
|
|
796
|
+
const units = actualUnits.map(unit => unit ?? '(none)').join(', ')
|
|
797
|
+
problems.push(`${entity} attribute ${expectation.name} has unit${actualUnits.length === 1 ? '' : 's'} ${units}; expected ${expectation.unit}`)
|
|
798
|
+
}
|
|
799
|
+
return problems.join('; ')
|
|
800
|
+
}
|
|
801
|
+
|
|
623
802
|
const formatViolation = (violation: Violation): string =>
|
|
624
|
-
`${violation
|
|
625
|
-
`(${violation.entity} IS ${violation.via}; ${violation.expectation.
|
|
803
|
+
`${formatViolationProblem(violation)} ` +
|
|
804
|
+
`(${violation.entity} IS ${violation.via}; ${violation.expectation.row.raw_line})`
|
|
626
805
|
|
|
627
806
|
const ingestCommand = (name: 'add' | 'import') => (argv: readonly string[]): Output => {
|
|
628
807
|
const { values, positionals } = parseArgs({
|
|
@@ -693,6 +872,8 @@ export const queryCommand = (argv: readonly string[]): Output => {
|
|
|
693
872
|
'as-of': { type: 'string' },
|
|
694
873
|
at: { type: 'string' },
|
|
695
874
|
resolve: { type: 'boolean' },
|
|
875
|
+
limit: { type: 'string' },
|
|
876
|
+
cursor: { type: 'string' },
|
|
696
877
|
'no-prelude': { type: 'boolean' }
|
|
697
878
|
},
|
|
698
879
|
allowPositionals: true
|
|
@@ -703,18 +884,22 @@ export const queryCommand = (argv: readonly string[]): Output => {
|
|
|
703
884
|
const pattern = positionals.join('\n')
|
|
704
885
|
const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
705
886
|
try {
|
|
706
|
-
const
|
|
887
|
+
const limit = values.limit === undefined ? defaultQueryLimit : Number(values.limit)
|
|
888
|
+
const result = caveQueryPage(store, pattern, {
|
|
707
889
|
all: values.all === true,
|
|
708
890
|
aliases: values.aliases === true,
|
|
709
891
|
resolve: values.resolve === true,
|
|
892
|
+
limit,
|
|
893
|
+
...values.cursor === undefined ? {} : { cursor: values.cursor },
|
|
710
894
|
...values['as-of'] === undefined ? {} : { asOf: values['as-of'] },
|
|
711
895
|
...values.at === undefined ? {} : { at: values.at }
|
|
712
896
|
})
|
|
713
897
|
if (values.json === true) {
|
|
714
|
-
return ok(`${JSON.stringify(
|
|
898
|
+
return ok(`${JSON.stringify(result, undefined, 2)}\n`)
|
|
715
899
|
}
|
|
900
|
+
const matches = result.matches
|
|
716
901
|
if (matches.length === 0) {
|
|
717
|
-
return ok(
|
|
902
|
+
return ok(`no matches\n${result.next === undefined ? '' : `next: ${result.next}\n`}`)
|
|
718
903
|
}
|
|
719
904
|
const lines = matches.map(match => {
|
|
720
905
|
const bindings = Object.entries(match.bindings)
|
|
@@ -722,14 +907,14 @@ export const queryCommand = (argv: readonly string[]): Output => {
|
|
|
722
907
|
.join(' ')
|
|
723
908
|
// A fully bound pattern has no bindings to print; transitive matches
|
|
724
909
|
// additionally carry no row — confirm with the pattern itself.
|
|
725
|
-
const line = bindings !== '' ? bindings : match.
|
|
910
|
+
const line = bindings !== '' ? bindings : match.claim?.claim.raw ?? pattern.split('\n')[0]!
|
|
726
911
|
// An interpolated trajectory shows its value at the anchor
|
|
727
912
|
// (spec §32.4) — bindings already carry it in the value slot.
|
|
728
913
|
return match.at !== undefined && bindings === '' ?
|
|
729
914
|
`${line} ; at ${values.at}: ${match.at.text}` :
|
|
730
915
|
line
|
|
731
916
|
})
|
|
732
|
-
return ok(`${lines.join('\n')}\n`)
|
|
917
|
+
return ok(`${lines.join('\n')}\n${result.next === undefined ? '' : `next: ${result.next}\n`}`)
|
|
733
918
|
} catch (error) {
|
|
734
919
|
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
735
920
|
} finally {
|
|
@@ -775,7 +960,7 @@ export const resolveCommand = (argv: readonly string[]): Output => {
|
|
|
775
960
|
}
|
|
776
961
|
const contested = store.contested({ aliases: values.aliases === true })
|
|
777
962
|
if (values.json === true) {
|
|
778
|
-
return ok(`${
|
|
963
|
+
return ok(`${stableJson(store, contested)}\n`)
|
|
779
964
|
}
|
|
780
965
|
if (contested.length === 0) {
|
|
781
966
|
return ok('no contested facts\n')
|
|
@@ -823,7 +1008,7 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
823
1008
|
if (values.list === true) {
|
|
824
1009
|
const rules = listRules(store)
|
|
825
1010
|
if (values.json === true) {
|
|
826
|
-
return ok(`${
|
|
1011
|
+
return ok(`${stableJson(store, rules)}\n`)
|
|
827
1012
|
}
|
|
828
1013
|
if (rules.length === 0) {
|
|
829
1014
|
return ok('no rules\n')
|
|
@@ -885,7 +1070,7 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
885
1070
|
(declaration.prelude > 0 ? `, +${declaration.prelude} prelude claim(s)` : ''))
|
|
886
1071
|
}
|
|
887
1072
|
if (values.json === true) {
|
|
888
|
-
return { code: report.problems.length > 0 ? 1 : 0, out: `${
|
|
1073
|
+
return { code: report.problems.length > 0 || !report.complete ? 1 : 0, out: `${stableJson(store, report)}\n`, err: err.join('\n') + (err.length > 0 ? '\n' : '') }
|
|
889
1074
|
}
|
|
890
1075
|
for (const problem of report.problems) {
|
|
891
1076
|
err.push(`${problem.subject}: ${problem.problems.join('; ')}`)
|
|
@@ -902,7 +1087,7 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
902
1087
|
`derived${values['dry-run'] === true ? ' (dry run)' : ''}: ` +
|
|
903
1088
|
`+${report.appended} appended, ${report.updated} updated, ${report.retracted} retracted, ` +
|
|
904
1089
|
`${report.unchanged} unchanged (${report.passes} pass(es))`)
|
|
905
|
-
const hasProblems = report.problems.length > 0 || report.rules.some(rule => rule.problems.length > 0)
|
|
1090
|
+
const hasProblems = !report.complete || report.problems.length > 0 || report.rules.some(rule => rule.problems.length > 0)
|
|
906
1091
|
return { code: hasProblems ? 1 : 0, out: `${out.join('\n')}\n`, err: err.length === 0 ? '' : `${err.join('\n')}\n` }
|
|
907
1092
|
} catch (error) {
|
|
908
1093
|
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
@@ -989,7 +1174,7 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
989
1174
|
if (values.list === true) {
|
|
990
1175
|
const actions = listActions(store)
|
|
991
1176
|
if (values.json === true) {
|
|
992
|
-
return ok(`${
|
|
1177
|
+
return ok(`${stableJson(store, actions)}\n`)
|
|
993
1178
|
}
|
|
994
1179
|
if (actions.length === 0) {
|
|
995
1180
|
return ok('no actions\n')
|
|
@@ -1030,7 +1215,7 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1030
1215
|
...hooksPath === undefined ? {} : { hooks: readHooks(hooksPath) }
|
|
1031
1216
|
})
|
|
1032
1217
|
if (values.json === true) {
|
|
1033
|
-
return { code: report.ok && report.hook?.error === undefined ? 0 : 1, out: `${
|
|
1218
|
+
return { code: report.ok && report.hook?.error === undefined ? 0 : 1, out: `${stableJson(store, report)}\n`, err: '' }
|
|
1034
1219
|
}
|
|
1035
1220
|
const { lines, code } = renderActReport(report)
|
|
1036
1221
|
return report.ok ?
|
|
@@ -1097,7 +1282,7 @@ export const checkCommand = (argv: readonly string[]): Output => {
|
|
|
1097
1282
|
try {
|
|
1098
1283
|
const report = caveCheck(store, { staleDays })
|
|
1099
1284
|
const out = values.json === true ?
|
|
1100
|
-
`${
|
|
1285
|
+
`${stableJson(store, report)}\n` :
|
|
1101
1286
|
renderReport(report, staleDays)
|
|
1102
1287
|
// Violations fail the check (spec §20.2); the other sections are advisory.
|
|
1103
1288
|
return { code: report.violations.length > 0 ? 1 : 0, out, err: '' }
|
|
@@ -1106,11 +1291,87 @@ export const checkCommand = (argv: readonly string[]): Output => {
|
|
|
1106
1291
|
}
|
|
1107
1292
|
}
|
|
1108
1293
|
|
|
1294
|
+
const snapshotLine = (
|
|
1295
|
+
action: 'created' | 'verified' | 'restored',
|
|
1296
|
+
snapshot: ReturnType<typeof verifyBackup>
|
|
1297
|
+
): string =>
|
|
1298
|
+
`${action} exact backup (${snapshot.rows} row(s), schema v${snapshot.schemaVersion}, ` +
|
|
1299
|
+
`${snapshot.bytes} bytes, sha256:${snapshot.sha256}) at ${snapshot.path}\n`
|
|
1300
|
+
|
|
1301
|
+
/** `cave backup` — create or verify an exact SQLite snapshot (§13.2.2). */
|
|
1302
|
+
export const backupCommand = (argv: readonly string[]): Output => {
|
|
1303
|
+
const { values } = parseArgs({
|
|
1304
|
+
args: [...argv],
|
|
1305
|
+
options: {
|
|
1306
|
+
db: { type: 'string' },
|
|
1307
|
+
out: { type: 'string' },
|
|
1308
|
+
force: { type: 'boolean' },
|
|
1309
|
+
verify: { type: 'string' },
|
|
1310
|
+
sha256: { type: 'string' }
|
|
1311
|
+
},
|
|
1312
|
+
allowPositionals: false
|
|
1313
|
+
})
|
|
1314
|
+
if (values.sha256 !== undefined && !/^[0-9a-f]{64}$/i.test(values.sha256)) {
|
|
1315
|
+
return fail('cave backup: --sha256 must be 64 hexadecimal characters\n')
|
|
1316
|
+
}
|
|
1317
|
+
try {
|
|
1318
|
+
if (values.verify !== undefined) {
|
|
1319
|
+
if (values.out !== undefined || values.force === true || values.db !== undefined) {
|
|
1320
|
+
return fail('cave backup: --verify excludes --db, --out, and --force\n')
|
|
1321
|
+
}
|
|
1322
|
+
return ok(snapshotLine('verified', verifyBackup(values.verify, values.sha256)))
|
|
1323
|
+
}
|
|
1324
|
+
if (values.sha256 !== undefined) {
|
|
1325
|
+
return fail('cave backup: --sha256 requires --verify\n')
|
|
1326
|
+
}
|
|
1327
|
+
if (values.out === undefined) {
|
|
1328
|
+
return fail('cave backup: --out <file> is required\n')
|
|
1329
|
+
}
|
|
1330
|
+
const store = open(values.db ?? defaultDbPath())
|
|
1331
|
+
try {
|
|
1332
|
+
return ok(snapshotLine('created', backupStore(store, values.out, { force: values.force === true })))
|
|
1333
|
+
} finally {
|
|
1334
|
+
store.close()
|
|
1335
|
+
}
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/** `cave restore` — verify and atomically restore an exact snapshot (§13.2.2). */
|
|
1342
|
+
export const restoreCommand = (argv: readonly string[]): Output => {
|
|
1343
|
+
const { values, positionals } = parseArgs({
|
|
1344
|
+
args: [...argv],
|
|
1345
|
+
options: {
|
|
1346
|
+
db: { type: 'string' },
|
|
1347
|
+
force: { type: 'boolean' },
|
|
1348
|
+
sha256: { type: 'string' }
|
|
1349
|
+
},
|
|
1350
|
+
allowPositionals: true
|
|
1351
|
+
})
|
|
1352
|
+
if (positionals.length !== 1) {
|
|
1353
|
+
return fail('cave restore: exactly one snapshot file is required\n')
|
|
1354
|
+
}
|
|
1355
|
+
if (values.db === undefined) {
|
|
1356
|
+
return fail('cave restore: --db <path> destination is required\n')
|
|
1357
|
+
}
|
|
1358
|
+
if (values.sha256 !== undefined && !/^[0-9a-f]{64}$/i.test(values.sha256)) {
|
|
1359
|
+
return fail('cave restore: --sha256 must be 64 hexadecimal characters\n')
|
|
1360
|
+
}
|
|
1361
|
+
try {
|
|
1362
|
+
return ok(snapshotLine('restored', restoreBackup(positionals[0]!, values.db, {
|
|
1363
|
+
force: values.force === true,
|
|
1364
|
+
...values.sha256 === undefined ? {} : { expectedSha256: values.sha256 }
|
|
1365
|
+
})))
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1109
1371
|
/**
|
|
1110
1372
|
* `cave suggest-alias` — alias discovery (spec §27): same-entity
|
|
1111
|
-
* candidates as suggested `ALIAS` claims for review. Async
|
|
1112
|
-
* judge runs a shell agent
|
|
1113
|
-
* `reconstruct`.
|
|
1373
|
+
* candidates as suggested `ALIAS` claims for review. Async when the optional
|
|
1374
|
+
* judge runs a shell agent.
|
|
1114
1375
|
*/
|
|
1115
1376
|
export const suggestAliasCommand = async (argv: readonly string[]): Promise<Output> => {
|
|
1116
1377
|
try {
|
|
@@ -1209,7 +1470,7 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1209
1470
|
...values.as === undefined ? {} : { from: sanitizeLabel(values.as) }
|
|
1210
1471
|
}
|
|
1211
1472
|
const report: SyncReport = source === '-' ?
|
|
1212
|
-
syncText(store,
|
|
1473
|
+
syncText(store, readStdin(), { from: 'stdin', ...options }) :
|
|
1213
1474
|
syncFile(store, source, options)
|
|
1214
1475
|
if (values.json === true) {
|
|
1215
1476
|
return { code: report.problems.length > 0 ? 1 : 0, out: `${JSON.stringify(report, undefined, 2)}\n`, err: '' }
|
|
@@ -1251,16 +1512,22 @@ export const reportCommand = (argv: readonly string[]): Output => {
|
|
|
1251
1512
|
resolve: { type: 'boolean' },
|
|
1252
1513
|
'as-of': { type: 'string' },
|
|
1253
1514
|
at: { type: 'string' },
|
|
1515
|
+
'max-sensitivity': { type: 'string' },
|
|
1254
1516
|
'no-prelude': { type: 'boolean' }
|
|
1255
1517
|
},
|
|
1256
1518
|
allowPositionals: true
|
|
1257
1519
|
})
|
|
1258
1520
|
const template = readInput(positionals)
|
|
1521
|
+
const maximum = Sensitivity.parse(values['max-sensitivity'] ?? Sensitivity.defaultMaximum)
|
|
1522
|
+
if (maximum === undefined) {
|
|
1523
|
+
return fail(`cave report: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`)
|
|
1524
|
+
}
|
|
1259
1525
|
const store = open(values.db ?? defaultDbPath(), values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
1260
1526
|
try {
|
|
1261
1527
|
const rendered = caveReport(store, template, {
|
|
1262
1528
|
aliases: values.aliases === true,
|
|
1263
1529
|
resolve: values.resolve === true,
|
|
1530
|
+
maxSensitivity: maximum,
|
|
1264
1531
|
...values['as-of'] === undefined ? {} : { asOf: values['as-of'] },
|
|
1265
1532
|
...values.at === undefined ? {} : { at: values.at }
|
|
1266
1533
|
})
|
|
@@ -1310,23 +1577,72 @@ export const exportCommand = (argv: readonly string[]): Output => {
|
|
|
1310
1577
|
current: { type: 'boolean' },
|
|
1311
1578
|
tx: { type: 'boolean' },
|
|
1312
1579
|
out: { type: 'string' },
|
|
1580
|
+
'max-sensitivity': { type: 'string' },
|
|
1313
1581
|
'no-prelude': { type: 'boolean' }
|
|
1314
1582
|
},
|
|
1315
1583
|
allowPositionals: false
|
|
1316
1584
|
})
|
|
1317
1585
|
const db = values.db ?? defaultDbPath()
|
|
1586
|
+
const maximum = Sensitivity.parse(values['max-sensitivity'] ?? Sensitivity.defaultMaximum)
|
|
1587
|
+
if (maximum === undefined) {
|
|
1588
|
+
return fail(`cave export: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`)
|
|
1589
|
+
}
|
|
1318
1590
|
if (values.out !== undefined && sameFile(db, values.out)) {
|
|
1319
1591
|
return fail(`cave export: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1320
1592
|
}
|
|
1321
1593
|
const store = open(db, values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
1322
1594
|
try {
|
|
1323
|
-
const text = store.exportText({ current: values.current === true, tx: values.tx === true })
|
|
1595
|
+
const text = store.exportText({ current: values.current === true, tx: values.tx === true, maxSensitivity: maximum })
|
|
1324
1596
|
if (values.out === undefined) {
|
|
1325
1597
|
return ok(text)
|
|
1326
1598
|
}
|
|
1327
1599
|
writeFileSync(values.out, text)
|
|
1328
|
-
|
|
1600
|
+
// Root claims only: indented qualifier/grouping lines are part of their
|
|
1601
|
+
// parent claim, and §28.4 annotations parse as comments.
|
|
1602
|
+
const claims = parseDocument(text).lines
|
|
1603
|
+
.filter(line => line.kind === 'claim' && line.depth === 0).length
|
|
1329
1604
|
return ok(`exported ${claims} claim(s) to ${values.out}\n`)
|
|
1605
|
+
} catch (error) {
|
|
1606
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1607
|
+
} finally {
|
|
1608
|
+
store.close()
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/** `cave generate` — spec §20.4's versioned TypeScript client artifact. */
|
|
1613
|
+
export const generateCommand = (argv: readonly string[]): Output => {
|
|
1614
|
+
const { values } = parseArgs({
|
|
1615
|
+
args: [...argv],
|
|
1616
|
+
options: {
|
|
1617
|
+
db: { type: 'string' },
|
|
1618
|
+
out: { type: 'string' },
|
|
1619
|
+
version: { type: 'string' },
|
|
1620
|
+
'no-prelude': { type: 'boolean' }
|
|
1621
|
+
},
|
|
1622
|
+
allowPositionals: false
|
|
1623
|
+
})
|
|
1624
|
+
const version = values.version === undefined ? undefined : Number(values.version)
|
|
1625
|
+
if (version !== undefined && (!Number.isInteger(version) || version < 1)) {
|
|
1626
|
+
return fail(`cave generate: --version expects a positive integer, got ${JSON.stringify(values.version)}\n`)
|
|
1627
|
+
}
|
|
1628
|
+
const db = values.db ?? defaultDbPath()
|
|
1629
|
+
if (values.out !== undefined && sameFile(db, values.out)) {
|
|
1630
|
+
return fail(`cave generate: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1631
|
+
}
|
|
1632
|
+
const store = open(db, values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
1633
|
+
try {
|
|
1634
|
+
const generated = generateClient(store, version === undefined ? {} : { version })
|
|
1635
|
+
if (!generated.ok) {
|
|
1636
|
+
return fail(`cave generate: schema cannot be generated:\n${generated.problems.map(problem => ` ${problem}`).join('\n')}\n`)
|
|
1637
|
+
}
|
|
1638
|
+
if (values.out === undefined) {
|
|
1639
|
+
return ok(generated.code)
|
|
1640
|
+
}
|
|
1641
|
+
writeFileSync(values.out, generated.code)
|
|
1642
|
+
return ok(`generated typed client v${generated.version} (${generated.fields.length} field(s), ` +
|
|
1643
|
+
`sha256:${generated.digest}) to ${values.out}\n`)
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1330
1646
|
} finally {
|
|
1331
1647
|
store.close()
|
|
1332
1648
|
}
|
|
@@ -1335,8 +1651,7 @@ export const exportCommand = (argv: readonly string[]): Output => {
|
|
|
1335
1651
|
/**
|
|
1336
1652
|
* `cave reconstruct` — the §18 loop over the store: heuristic policy by
|
|
1337
1653
|
* default (the eval baseline), the LLM policy over a shell-agent template
|
|
1338
|
-
* with `--agent`. Async
|
|
1339
|
-
* routes it separately, like `connect`.
|
|
1654
|
+
* with `--agent`. Async because the agent runs once per step.
|
|
1340
1655
|
*/
|
|
1341
1656
|
export const reconstructCommand = async (argv: readonly string[]): Promise<Output> => {
|
|
1342
1657
|
try {
|
|
@@ -1420,8 +1735,8 @@ export const helpCommand = (argv: readonly string[]): Output => {
|
|
|
1420
1735
|
if (text !== undefined) {
|
|
1421
1736
|
return ok(`${text}\n`)
|
|
1422
1737
|
}
|
|
1423
|
-
//
|
|
1424
|
-
if (topic
|
|
1738
|
+
// Delegated commands own their help; dispatch.ts forwards `help X` to `X --help`.
|
|
1739
|
+
if (delegatedCommandNames.includes(topic)) {
|
|
1425
1740
|
return ok(`see: cave ${topic} --help\n`)
|
|
1426
1741
|
}
|
|
1427
1742
|
return fail(`cave help: unknown command ${JSON.stringify(topic)}\n\n${usage}\n`, 2)
|
|
@@ -1431,50 +1746,54 @@ export const helpCommand = (argv: readonly string[]): Output => {
|
|
|
1431
1746
|
export const cave = (argv: readonly string[]): Output => {
|
|
1432
1747
|
const [command, ...rest] = argv
|
|
1433
1748
|
const canonical = command === 'q' ? 'query' : command
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1749
|
+
if (canonical !== undefined && canonical in commandHelp &&
|
|
1750
|
+
(rest.includes('--help') || rest.includes('-h'))) {
|
|
1751
|
+
return ok(`${commandHelp[canonical]}\n`)
|
|
1752
|
+
}
|
|
1753
|
+
switch (canonical) {
|
|
1754
|
+
case 'parse':
|
|
1755
|
+
return parseCommand(rest)
|
|
1756
|
+
case 'add':
|
|
1757
|
+
return addCommand(rest)
|
|
1758
|
+
case 'import':
|
|
1759
|
+
return importCommand(rest)
|
|
1760
|
+
case 'query':
|
|
1761
|
+
return queryCommand(rest)
|
|
1762
|
+
case 'resolve':
|
|
1763
|
+
return resolveCommand(rest)
|
|
1764
|
+
case 'derive':
|
|
1765
|
+
return deriveCommand(rest)
|
|
1766
|
+
case 'act':
|
|
1767
|
+
return actCommand(rest)
|
|
1768
|
+
case 'check':
|
|
1769
|
+
return checkCommand(rest)
|
|
1770
|
+
case 'backup':
|
|
1771
|
+
return backupCommand(rest)
|
|
1772
|
+
case 'restore':
|
|
1773
|
+
return restoreCommand(rest)
|
|
1774
|
+
case 'generate':
|
|
1775
|
+
return generateCommand(rest)
|
|
1776
|
+
case 'sync':
|
|
1777
|
+
return syncCommand(rest)
|
|
1778
|
+
case 'export':
|
|
1779
|
+
return exportCommand(rest)
|
|
1780
|
+
case 'report':
|
|
1781
|
+
return reportCommand(rest)
|
|
1782
|
+
case 'doctor':
|
|
1783
|
+
return doctorCommand(rest)
|
|
1784
|
+
case 'demo':
|
|
1785
|
+
return demoCommand()
|
|
1786
|
+
case 'version':
|
|
1787
|
+
case '--version':
|
|
1788
|
+
case '-v':
|
|
1789
|
+
return versionCommand()
|
|
1790
|
+
case 'help':
|
|
1791
|
+
return helpCommand(rest)
|
|
1792
|
+
case undefined:
|
|
1793
|
+
case '--help':
|
|
1794
|
+
case '-h':
|
|
1795
|
+
return ok(`${usage}\n`)
|
|
1796
|
+
default:
|
|
1797
|
+
return fail(`cave: unknown command ${JSON.stringify(command)}\n\n${usage}\n`, 2)
|
|
1479
1798
|
}
|
|
1480
1799
|
}
|