@cavelang/cli 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -13
- package/dist/internal/act/action.js +2 -1
- package/dist/internal/act/action.js.map +1 -1
- package/dist/internal/act/declare.d.ts.map +1 -1
- package/dist/internal/act/declare.js +82 -53
- package/dist/internal/act/declare.js.map +1 -1
- package/dist/internal/act/engine.d.ts +4 -3
- package/dist/internal/act/engine.d.ts.map +1 -1
- package/dist/internal/act/engine.js +121 -32
- package/dist/internal/act/engine.js.map +1 -1
- package/dist/internal/automate/cancellation.d.ts +3 -0
- package/dist/internal/automate/cancellation.d.ts.map +1 -0
- package/dist/internal/automate/cancellation.js +38 -0
- package/dist/internal/automate/cancellation.js.map +1 -0
- package/dist/internal/automate/declare.d.ts.map +1 -1
- package/dist/internal/automate/declare.js +49 -24
- package/dist/internal/automate/declare.js.map +1 -1
- package/dist/internal/automate/engine.d.ts +7 -3
- package/dist/internal/automate/engine.d.ts.map +1 -1
- package/dist/internal/automate/engine.js +196 -93
- package/dist/internal/automate/engine.js.map +1 -1
- package/dist/internal/automate/error-message.d.ts +3 -0
- package/dist/internal/automate/error-message.d.ts.map +1 -0
- package/dist/internal/automate/error-message.js +10 -0
- package/dist/internal/automate/error-message.js.map +1 -0
- package/dist/internal/automate/main.d.ts.map +1 -1
- package/dist/internal/automate/main.js +240 -35
- package/dist/internal/automate/main.js.map +1 -1
- package/dist/internal/automate/options.d.ts +4 -0
- package/dist/internal/automate/options.d.ts.map +1 -0
- package/dist/internal/automate/options.js +19 -0
- package/dist/internal/automate/options.js.map +1 -0
- package/dist/internal/connect/declared.d.ts +204 -0
- package/dist/internal/connect/declared.d.ts.map +1 -0
- package/dist/internal/connect/declared.js +610 -0
- package/dist/internal/connect/declared.js.map +1 -0
- package/dist/internal/connect/error-message.d.ts +3 -0
- package/dist/internal/connect/error-message.d.ts.map +1 -0
- package/dist/internal/connect/error-message.js +10 -0
- package/dist/internal/connect/error-message.js.map +1 -0
- package/dist/internal/connect/federation.d.ts +9 -0
- package/dist/internal/connect/federation.d.ts.map +1 -0
- package/dist/internal/connect/federation.js +19 -0
- package/dist/internal/connect/federation.js.map +1 -0
- package/dist/internal/connect/fetch-failure.d.ts +3 -0
- package/dist/internal/connect/fetch-failure.d.ts.map +1 -0
- package/dist/internal/connect/fetch-failure.js +36 -0
- package/dist/internal/connect/fetch-failure.js.map +1 -0
- package/dist/internal/connect/index.d.ts +4 -2
- package/dist/internal/connect/index.d.ts.map +1 -1
- package/dist/internal/connect/index.js +3 -1
- package/dist/internal/connect/index.js.map +1 -1
- package/dist/internal/connect/main.d.ts +1 -0
- package/dist/internal/connect/main.d.ts.map +1 -1
- package/dist/internal/connect/main.js +575 -59
- package/dist/internal/connect/main.js.map +1 -1
- package/dist/internal/connect/options.d.ts +3 -0
- package/dist/internal/connect/options.d.ts.map +1 -0
- package/dist/internal/connect/options.js +9 -0
- package/dist/internal/connect/options.js.map +1 -0
- package/dist/internal/connect/records.d.ts +3 -0
- package/dist/internal/connect/records.d.ts.map +1 -0
- package/dist/internal/connect/records.js +21 -0
- package/dist/internal/connect/records.js.map +1 -0
- package/dist/internal/connect/run.d.ts +48 -19
- package/dist/internal/connect/run.d.ts.map +1 -1
- package/dist/internal/connect/run.js +185 -77
- package/dist/internal/connect/run.js.map +1 -1
- package/dist/internal/connect/source.d.ts +36 -3
- package/dist/internal/connect/source.d.ts.map +1 -1
- package/dist/internal/connect/source.js +378 -39
- package/dist/internal/connect/source.js.map +1 -1
- package/dist/internal/connect/template.d.ts +20 -2
- package/dist/internal/connect/template.d.ts.map +1 -1
- package/dist/internal/connect/template.js +53 -9
- package/dist/internal/connect/template.js.map +1 -1
- package/dist/internal/connect/utf8.d.ts +3 -0
- package/dist/internal/connect/utf8.d.ts.map +1 -0
- package/dist/internal/connect/utf8.js +12 -0
- package/dist/internal/connect/utf8.js.map +1 -0
- package/dist/internal/eval/cleanup.d.ts +5 -0
- package/dist/internal/eval/cleanup.d.ts.map +1 -0
- package/dist/internal/eval/cleanup.js +50 -0
- package/dist/internal/eval/cleanup.js.map +1 -0
- package/dist/internal/eval/error-message.d.ts +3 -0
- package/dist/internal/eval/error-message.d.ts.map +1 -0
- package/dist/internal/eval/error-message.js +10 -0
- package/dist/internal/eval/error-message.js.map +1 -0
- package/dist/internal/eval/judge.d.ts.map +1 -1
- package/dist/internal/eval/judge.js +3 -31
- package/dist/internal/eval/judge.js.map +1 -1
- package/dist/internal/eval/loop.d.ts.map +1 -1
- package/dist/internal/eval/loop.js +18 -4
- package/dist/internal/eval/loop.js.map +1 -1
- package/dist/internal/eval/main.d.ts.map +1 -1
- package/dist/internal/eval/main.js +22 -10
- package/dist/internal/eval/main.js.map +1 -1
- package/dist/internal/eval/options.d.ts +6 -0
- package/dist/internal/eval/options.d.ts.map +1 -0
- package/dist/internal/eval/options.js +31 -0
- package/dist/internal/eval/options.js.map +1 -0
- package/dist/internal/eval/queries.d.ts.map +1 -1
- package/dist/internal/eval/queries.js +9 -6
- package/dist/internal/eval/queries.js.map +1 -1
- package/dist/internal/eval/run.d.ts.map +1 -1
- package/dist/internal/eval/run.js +112 -24
- package/dist/internal/eval/run.js.map +1 -1
- package/dist/internal/eval/score.d.ts.map +1 -1
- package/dist/internal/eval/score.js +9 -2
- package/dist/internal/eval/score.js.map +1 -1
- package/dist/internal/eval/suite.d.ts.map +1 -1
- package/dist/internal/eval/suite.js +14 -2
- package/dist/internal/eval/suite.js.map +1 -1
- package/dist/internal/ingest/cleanup.d.ts +5 -0
- package/dist/internal/ingest/cleanup.d.ts.map +1 -0
- package/dist/internal/ingest/cleanup.js +50 -0
- package/dist/internal/ingest/cleanup.js.map +1 -0
- package/dist/internal/ingest/content.d.ts +6 -0
- package/dist/internal/ingest/content.d.ts.map +1 -0
- package/dist/internal/ingest/content.js +25 -0
- package/dist/internal/ingest/content.js.map +1 -0
- package/dist/internal/ingest/context.d.ts.map +1 -1
- package/dist/internal/ingest/context.js +25 -7
- package/dist/internal/ingest/context.js.map +1 -1
- package/dist/internal/ingest/error-message.d.ts +3 -0
- package/dist/internal/ingest/error-message.d.ts.map +1 -0
- package/dist/internal/ingest/error-message.js +10 -0
- package/dist/internal/ingest/error-message.js.map +1 -0
- package/dist/internal/ingest/files.d.ts +4 -12
- package/dist/internal/ingest/files.d.ts.map +1 -1
- package/dist/internal/ingest/files.js +22 -10
- package/dist/internal/ingest/files.js.map +1 -1
- package/dist/internal/ingest/main.d.ts.map +1 -1
- package/dist/internal/ingest/main.js +48 -17
- package/dist/internal/ingest/main.js.map +1 -1
- package/dist/internal/ingest/options.d.ts +6 -0
- package/dist/internal/ingest/options.d.ts.map +1 -0
- package/dist/internal/ingest/options.js +27 -0
- package/dist/internal/ingest/options.js.map +1 -0
- package/dist/internal/ingest/prompt.d.ts.map +1 -1
- package/dist/internal/ingest/prompt.js +14 -10
- package/dist/internal/ingest/prompt.js.map +1 -1
- package/dist/internal/ingest/run.d.ts +9 -2
- package/dist/internal/ingest/run.d.ts.map +1 -1
- package/dist/internal/ingest/run.js +206 -62
- package/dist/internal/ingest/run.js.map +1 -1
- package/dist/internal/ingest/timeout.d.ts +3 -0
- package/dist/internal/ingest/timeout.d.ts.map +1 -0
- package/dist/internal/ingest/timeout.js +14 -0
- package/dist/internal/ingest/timeout.js.map +1 -0
- package/dist/internal/ingest/web.d.ts +2 -11
- package/dist/internal/ingest/web.d.ts.map +1 -1
- package/dist/internal/ingest/web.js +131 -25
- package/dist/internal/ingest/web.js.map +1 -1
- package/dist/internal/loop/budgets.d.ts +3 -0
- package/dist/internal/loop/budgets.d.ts.map +1 -0
- package/dist/internal/loop/budgets.js +10 -0
- package/dist/internal/loop/budgets.js.map +1 -0
- package/dist/internal/loop/index.d.ts +2 -0
- package/dist/internal/loop/index.d.ts.map +1 -1
- package/dist/internal/loop/index.js +2 -0
- package/dist/internal/loop/index.js.map +1 -1
- package/dist/internal/loop/json-answer.d.ts +7 -0
- package/dist/internal/loop/json-answer.d.ts.map +1 -0
- package/dist/internal/loop/json-answer.js +79 -0
- package/dist/internal/loop/json-answer.js.map +1 -0
- package/dist/internal/loop/json-spans.d.ts +3 -0
- package/dist/internal/loop/json-spans.d.ts.map +1 -0
- package/dist/internal/loop/json-spans.js +88 -0
- package/dist/internal/loop/json-spans.js.map +1 -0
- package/dist/internal/loop/llm.d.ts +3 -2
- package/dist/internal/loop/llm.d.ts.map +1 -1
- package/dist/internal/loop/llm.js +115 -32
- package/dist/internal/loop/llm.js.map +1 -1
- package/dist/internal/loop/process.d.ts +3 -1
- package/dist/internal/loop/process.d.ts.map +1 -1
- package/dist/internal/loop/process.js +159 -59
- package/dist/internal/loop/process.js.map +1 -1
- package/dist/internal/loop/read-snapshot.d.ts +4 -0
- package/dist/internal/loop/read-snapshot.d.ts.map +1 -0
- package/dist/internal/loop/read-snapshot.js +29 -0
- package/dist/internal/loop/read-snapshot.js.map +1 -0
- package/dist/internal/loop/reconstruct.d.ts +6 -5
- package/dist/internal/loop/reconstruct.d.ts.map +1 -1
- package/dist/internal/loop/reconstruct.js +19 -8
- package/dist/internal/loop/reconstruct.js.map +1 -1
- package/dist/internal/loop/score-settings.d.ts +5 -0
- package/dist/internal/loop/score-settings.d.ts.map +1 -0
- package/dist/internal/loop/score-settings.js +14 -0
- package/dist/internal/loop/score-settings.js.map +1 -0
- package/dist/internal/loop/sqlite.d.ts.map +1 -1
- package/dist/internal/loop/sqlite.js +15 -7
- package/dist/internal/loop/sqlite.js.map +1 -1
- package/dist/internal/loop/store.d.ts.map +1 -1
- package/dist/internal/loop/store.js +11 -7
- package/dist/internal/loop/store.js.map +1 -1
- package/dist/internal/loop/timeout.d.ts +3 -0
- package/dist/internal/loop/timeout.d.ts.map +1 -0
- package/dist/internal/loop/timeout.js +13 -0
- package/dist/internal/loop/timeout.js.map +1 -0
- package/dist/internal/mcp/error-message.d.ts +3 -0
- package/dist/internal/mcp/error-message.d.ts.map +1 -0
- package/dist/internal/mcp/error-message.js +10 -0
- package/dist/internal/mcp/error-message.js.map +1 -0
- package/dist/internal/mcp/main.d.ts.map +1 -1
- package/dist/internal/mcp/main.js +44 -27
- package/dist/internal/mcp/main.js.map +1 -1
- package/dist/internal/mcp/read-snapshot.d.ts +4 -0
- package/dist/internal/mcp/read-snapshot.d.ts.map +1 -0
- package/dist/internal/mcp/read-snapshot.js +22 -0
- package/dist/internal/mcp/read-snapshot.js.map +1 -0
- package/dist/internal/mcp/server.d.ts.map +1 -1
- package/dist/internal/mcp/server.js +181 -21
- package/dist/internal/mcp/server.js.map +1 -1
- package/dist/internal/mcp/source.d.ts +3 -0
- package/dist/internal/mcp/source.d.ts.map +1 -0
- package/dist/internal/mcp/source.js +13 -0
- package/dist/internal/mcp/source.js.map +1 -0
- package/dist/internal/mcp/tools.d.ts.map +1 -1
- package/dist/internal/mcp/tools.js +135 -72
- package/dist/internal/mcp/tools.js.map +1 -1
- package/dist/internal/rules/declarations.d.ts +6 -0
- package/dist/internal/rules/declarations.d.ts.map +1 -0
- package/dist/internal/rules/declarations.js +14 -0
- package/dist/internal/rules/declarations.js.map +1 -0
- package/dist/internal/rules/declare.d.ts.map +1 -1
- package/dist/internal/rules/declare.js +30 -16
- package/dist/internal/rules/declare.js.map +1 -1
- package/dist/internal/rules/engine.d.ts +8 -7
- package/dist/internal/rules/engine.d.ts.map +1 -1
- package/dist/internal/rules/engine.js +150 -44
- package/dist/internal/rules/engine.js.map +1 -1
- package/dist/internal/rules/rule.d.ts.map +1 -1
- package/dist/internal/rules/rule.js +1 -22
- package/dist/internal/rules/rule.js.map +1 -1
- package/dist/internal/shape/alias-root.d.ts +3 -0
- package/dist/internal/shape/alias-root.d.ts.map +1 -0
- package/dist/internal/shape/alias-root.js +13 -0
- package/dist/internal/shape/alias-root.js.map +1 -0
- package/dist/internal/shape/check.d.ts +15 -3
- package/dist/internal/shape/check.d.ts.map +1 -1
- package/dist/internal/shape/check.js +230 -100
- package/dist/internal/shape/check.js.map +1 -1
- package/dist/internal/shape/client.d.ts.map +1 -1
- package/dist/internal/shape/client.js +42 -20
- package/dist/internal/shape/client.js.map +1 -1
- package/dist/internal/shape/constraints.d.ts +8 -0
- package/dist/internal/shape/constraints.d.ts.map +1 -0
- package/dist/internal/shape/constraints.js +17 -0
- package/dist/internal/shape/constraints.js.map +1 -0
- package/dist/internal/shape/edit-distance.d.ts +3 -0
- package/dist/internal/shape/edit-distance.d.ts.map +1 -0
- package/dist/internal/shape/edit-distance.js +33 -0
- package/dist/internal/shape/edit-distance.js.map +1 -0
- package/dist/internal/shape/gate.d.ts.map +1 -1
- package/dist/internal/shape/gate.js +0 -0
- package/dist/internal/shape/gate.js.map +1 -1
- package/dist/internal/shape/snapshot.d.ts +4 -0
- package/dist/internal/shape/snapshot.d.ts.map +1 -0
- package/dist/internal/shape/snapshot.js +20 -0
- package/dist/internal/shape/snapshot.js.map +1 -0
- package/dist/internal/shape/suggest.d.ts +8 -6
- package/dist/internal/shape/suggest.d.ts.map +1 -1
- package/dist/internal/shape/suggest.js +208 -92
- package/dist/internal/shape/suggest.js.map +1 -1
- package/dist/internal/sync/error-message.d.ts +3 -0
- package/dist/internal/sync/error-message.d.ts.map +1 -0
- package/dist/internal/sync/error-message.js +10 -0
- package/dist/internal/sync/error-message.js.map +1 -0
- package/dist/internal/sync/index.d.ts +3 -2
- package/dist/internal/sync/index.d.ts.map +1 -1
- package/dist/internal/sync/index.js +3 -2
- package/dist/internal/sync/index.js.map +1 -1
- package/dist/internal/sync/sync.d.ts +8 -5
- package/dist/internal/sync/sync.d.ts.map +1 -1
- package/dist/internal/sync/sync.js +322 -65
- package/dist/internal/sync/sync.js.map +1 -1
- package/dist/internal/view/api.d.ts +3 -1
- package/dist/internal/view/api.d.ts.map +1 -1
- package/dist/internal/view/api.js +154 -90
- package/dist/internal/view/api.js.map +1 -1
- package/dist/internal/view/error-message.d.ts +3 -0
- package/dist/internal/view/error-message.d.ts.map +1 -0
- package/dist/internal/view/error-message.js +10 -0
- package/dist/internal/view/error-message.js.map +1 -0
- package/dist/internal/view/main.d.ts.map +1 -1
- package/dist/internal/view/main.js +63 -9
- package/dist/internal/view/main.js.map +1 -1
- package/dist/internal/view/page.d.ts.map +1 -1
- package/dist/internal/view/page.js +119 -20
- package/dist/internal/view/page.js.map +1 -1
- package/dist/internal/view/report.d.ts.map +1 -1
- package/dist/internal/view/report.js +180 -145
- package/dist/internal/view/report.js.map +1 -1
- package/dist/internal/view/scope.d.ts +6 -2
- package/dist/internal/view/scope.d.ts.map +1 -1
- package/dist/internal/view/scope.js +107 -24
- package/dist/internal/view/scope.js.map +1 -1
- package/dist/internal/view/sensitivity.d.ts +4 -0
- package/dist/internal/view/sensitivity.d.ts.map +1 -0
- package/dist/internal/view/sensitivity.js +11 -0
- package/dist/internal/view/sensitivity.js.map +1 -0
- package/dist/internal/view/server.d.ts.map +1 -1
- package/dist/internal/view/server.js +57 -20
- package/dist/internal/view/server.js.map +1 -1
- package/dist/src/atomic-output.d.ts +3 -0
- package/dist/src/atomic-output.d.ts.map +1 -0
- package/dist/src/atomic-output.js +67 -0
- package/dist/src/atomic-output.js.map +1 -0
- package/dist/src/cli.d.ts +17 -2
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +333 -195
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands.d.ts +2 -2
- package/dist/src/commands.js +2 -2
- package/dist/src/commands.js.map +1 -1
- package/dist/src/dispatch.d.ts.map +1 -1
- package/dist/src/dispatch.js +10 -3
- package/dist/src/dispatch.js.map +1 -1
- package/dist/src/doctor.d.ts +1 -1
- package/dist/src/doctor.d.ts.map +1 -1
- package/dist/src/doctor.js +192 -91
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/error-message.d.ts +3 -0
- package/dist/src/error-message.d.ts.map +1 -0
- package/dist/src/error-message.js +20 -0
- package/dist/src/error-message.js.map +1 -0
- package/dist/src/hooks-config.d.ts +3 -0
- package/dist/src/hooks-config.d.ts.map +1 -0
- package/dist/src/hooks-config.js +23 -0
- package/dist/src/hooks-config.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/package.json +23 -20
- package/src/atomic-output.ts +47 -0
- package/src/cli.ts +345 -171
- package/src/commands.ts +2 -2
- package/src/dispatch.ts +10 -3
- package/src/doctor.ts +207 -100
- package/src/error-message.ts +13 -0
- package/src/hooks-config.ts +22 -0
- package/src/index.ts +1 -1
package/src/cli.ts
CHANGED
|
@@ -35,14 +35,18 @@
|
|
|
35
35
|
* `cave.db`. Every command answers `--help` with options and examples.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
import { readFileSync, readSync,
|
|
39
|
-
import {
|
|
38
|
+
import { readFileSync, readSync, realpathSync, statSync } from 'node:fs'
|
|
39
|
+
import { readHooks } from './hooks-config.ts'
|
|
40
|
+
import { basename, dirname, join, resolve } from 'node:path'
|
|
40
41
|
import { parseArgs } from 'node:util'
|
|
41
|
-
import {
|
|
42
|
+
import { writeOutput } from './atomic-output.ts'
|
|
43
|
+
import { errorMessage } from './error-message.ts'
|
|
44
|
+
import { Time, Version } from '@cavelang/core'
|
|
42
45
|
import { parseDocument, Token } from '@cavelang/parser'
|
|
43
46
|
import { Registry, standardRegistry } from '@cavelang/canonical'
|
|
44
|
-
import {
|
|
47
|
+
import { backup as backupStore, defaultDbPath, kindOf, LocateError, openAt, QuerySql, restoreBackup, Sensitivity, verifyBackup } from '@cavelang/store'
|
|
45
48
|
import type { OpenIntent, Store } from '@cavelang/store'
|
|
49
|
+
import { Declared, assemble as assembleSources } from '@cavelang/connect'
|
|
46
50
|
import { defaultLimit as defaultQueryLimit, maxLimit as maxQueryLimit, page as caveQueryPage } from '@cavelang/query'
|
|
47
51
|
import {
|
|
48
52
|
check as caveCheck, defaultMinScore, defaultStaleDays, gatedIngest, judgePrompt, parseJudgeReply,
|
|
@@ -252,7 +256,7 @@ Examples:
|
|
|
252
256
|
query: `cave query — run a CAVE-Q pattern against a store
|
|
253
257
|
|
|
254
258
|
Usage:
|
|
255
|
-
cave query [--db <path>] <pattern> [WHERE <filter>] [--limit <n>] [--cursor <token>] [--json] [--all] [--aliases] [--as-of <t>] [--at <t>] [--resolve] [--no-prelude]
|
|
259
|
+
cave query [--db <path>] <pattern> [WHERE <filter>] [--limit <n>] [--cursor <token>] [--json] [--all] [--aliases] [--as-of <t>] [--at <t>] [--resolve] [--sources] [--no-prelude]
|
|
256
260
|
|
|
257
261
|
Options:
|
|
258
262
|
${dbHelp}
|
|
@@ -278,6 +282,12 @@ Options:
|
|
|
278
282
|
collapse to the row the resolution policy picks
|
|
279
283
|
(precedence class, reliability-weighted confidence, tx);
|
|
280
284
|
incompatible with --all
|
|
285
|
+
--sources overlay the store's declared sources (spec §23.4) inside a
|
|
286
|
+
rolled-back transaction — federation-lite: external data is
|
|
287
|
+
consulted at query time, nothing persists. The answer is
|
|
288
|
+
whole (no --cursor, --limit is not a page size); a text
|
|
289
|
+
store followed its local sources on open, so only URL
|
|
290
|
+
sources are loaded here
|
|
281
291
|
--no-prelude open the store without the standard verb registry
|
|
282
292
|
|
|
283
293
|
Patterns are claim triples with ?variables and optional metadata filters
|
|
@@ -297,6 +307,7 @@ Examples:
|
|
|
297
307
|
cave query --db k.db 'revenue IS' --at 2026-07
|
|
298
308
|
cave query --db k.db 'alice WORKS-AT ?org' --at 2021
|
|
299
309
|
cave query --db k.db 'service HAS owner: ?who' --resolve
|
|
310
|
+
cave query --db k.db '?who WORKS-AT acme' --sources
|
|
300
311
|
cave query --db k.db '?x ?verb ?y @production' --json`,
|
|
301
312
|
|
|
302
313
|
search: `cave search — full-text search over claims and comments (spec §13.2)
|
|
@@ -521,7 +532,7 @@ Usage:
|
|
|
521
532
|
|
|
522
533
|
Options:
|
|
523
534
|
${dbHelp}
|
|
524
|
-
--out <file>
|
|
535
|
+
--out <file> atomically replace a file with the generated module
|
|
525
536
|
--version <n> generated-client format version (default and currently
|
|
526
537
|
supported: 1)
|
|
527
538
|
--no-prelude open the store without the standard verb registry
|
|
@@ -545,7 +556,7 @@ Usage:
|
|
|
545
556
|
Options:
|
|
546
557
|
${dbHelp}
|
|
547
558
|
--min <s> minimum evidence score, 0..1 or N% (default 60%)
|
|
548
|
-
--limit <n> at most n suggestions, strongest first
|
|
559
|
+
--limit <n> positive safe integer; at most n suggestions, strongest first
|
|
549
560
|
--agent <template> LLM judge filtering the candidates — the cave
|
|
550
561
|
ingest/eval shell contract: the prompt (each pair
|
|
551
562
|
with both sides' current claims) is piped to stdin
|
|
@@ -576,6 +587,10 @@ claim, so the opt-in §13.6 closure links it until reviewed:
|
|
|
576
587
|
claim identity, §9.2, so a plain @ 0% append would start a new series
|
|
577
588
|
and leave the suggested link standing).
|
|
578
589
|
|
|
590
|
+
Writes recheck pair history under their transaction reservation and skip
|
|
591
|
+
reviewed pairs. --json returns the selected suggestion array; --json --write
|
|
592
|
+
performs the write and returns { suggestions, appended }, with the actual count.
|
|
593
|
+
|
|
579
594
|
Examples:
|
|
580
595
|
cave suggest-alias --db k.db
|
|
581
596
|
cave suggest-alias --db k.db --min 80% --limit 10
|
|
@@ -633,7 +648,7 @@ Usage:
|
|
|
633
648
|
|
|
634
649
|
Options:
|
|
635
650
|
${dbHelp}
|
|
636
|
-
--out <file>
|
|
651
|
+
--out <file> atomically replace a file instead of stdout — refused when it
|
|
637
652
|
names the store's own database file
|
|
638
653
|
--current current beliefs only (skip superseded rows); compacting
|
|
639
654
|
view, not sanitization or selective erasure (spec §9.6)
|
|
@@ -649,6 +664,10 @@ Options:
|
|
|
649
664
|
Retention: claim history is permanent. Retraction and --current do not
|
|
650
665
|
guarantee erasure from the store, exports, peers, or backups (spec §9.6).
|
|
651
666
|
|
|
667
|
+
File output is staged and flushed before replacement. Existing permissions
|
|
668
|
+
and valid symlinks are preserved; hard-link aliases retain the previous file.
|
|
669
|
+
Dangling symlinks and non-regular output files are refused.
|
|
670
|
+
|
|
652
671
|
Examples:
|
|
653
672
|
cave export --db k.db
|
|
654
673
|
cave export --db k.db --out internal-export.cave
|
|
@@ -668,7 +687,7 @@ Usage:
|
|
|
668
687
|
|
|
669
688
|
Options:
|
|
670
689
|
${dbHelp}
|
|
671
|
-
--out <file>
|
|
690
|
+
--out <file> atomically replace a file with the rendered markdown
|
|
672
691
|
--aliases queries match through the alias closure (spec §13.6)
|
|
673
692
|
--resolve queries match resolved winners only (spec §26) — the fix
|
|
674
693
|
when an inline splice reports an ambiguous fact
|
|
@@ -767,6 +786,15 @@ Examples:
|
|
|
767
786
|
cave help serve`
|
|
768
787
|
}
|
|
769
788
|
|
|
789
|
+
const decodeInput = (bytes: Uint8Array, source: string): string => {
|
|
790
|
+
try {
|
|
791
|
+
// Keep the existing BOM behavior of Buffer.toString while rejecting loss.
|
|
792
|
+
return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes)
|
|
793
|
+
} catch (cause) {
|
|
794
|
+
throw new TypeError(`${source}: invalid UTF-8 input`, { cause })
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
770
798
|
const readStdin = (): string => {
|
|
771
799
|
const chunks: Buffer[] = []
|
|
772
800
|
const buffer = Buffer.allocUnsafe(64 * 1024)
|
|
@@ -774,7 +802,7 @@ const readStdin = (): string => {
|
|
|
774
802
|
for (;;) {
|
|
775
803
|
try {
|
|
776
804
|
const size = readSync(0, buffer, 0, buffer.length, null)
|
|
777
|
-
if (size === 0) return Buffer.concat(chunks)
|
|
805
|
+
if (size === 0) return decodeInput(Buffer.concat(chunks), 'stdin')
|
|
778
806
|
chunks.push(Buffer.from(buffer.subarray(0, size)))
|
|
779
807
|
} catch (error) {
|
|
780
808
|
if ((error as NodeJS.ErrnoException).code !== 'EAGAIN') throw error
|
|
@@ -797,13 +825,75 @@ const readStdin = (): string => {
|
|
|
797
825
|
const openDb = (values: { db?: string, 'no-prelude'?: boolean }, intent: OpenIntent): Store =>
|
|
798
826
|
openAt(values.db ?? defaultDbPath(), {
|
|
799
827
|
intent,
|
|
828
|
+
// A text store follows its declared sources on open (spec §23.4).
|
|
829
|
+
assemble: assembleSources,
|
|
800
830
|
...values['no-prelude'] === true ? { registry: Registry.empty } : {}
|
|
801
831
|
})
|
|
802
832
|
|
|
833
|
+
/** Retain command output and diagnostics when its owned store fails to close. */
|
|
834
|
+
const closeCommandStore = (store: Store, output: Output): Output => {
|
|
835
|
+
try { store.close() } catch (error) {
|
|
836
|
+
const separator = output.err === '' || output.err.endsWith('\n') ? '' : '\n'
|
|
837
|
+
return { ...output, code: output.code === 0 ? 1 : output.code,
|
|
838
|
+
err: `${output.err}${separator}store close failed: ${errorMessage(error)}\n` }
|
|
839
|
+
}
|
|
840
|
+
return output
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const withCommandStore = (store: Store, run: () => Output): Output => {
|
|
844
|
+
let output: Output
|
|
845
|
+
try { output = run() } catch (error) {
|
|
846
|
+
output = fail(`${errorMessage(error)}\n`)
|
|
847
|
+
}
|
|
848
|
+
return closeCommandStore(store, output)
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const withAsyncCommandStore = async (store: Store, run: () => Promise<Output>): Promise<Output> => {
|
|
852
|
+
let output: Output
|
|
853
|
+
try { output = await run() } catch (error) {
|
|
854
|
+
output = fail(`${errorMessage(error)}\n`)
|
|
855
|
+
}
|
|
856
|
+
return closeCommandStore(store, output)
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const sourcesRollback = Symbol('cave query --sources rollback')
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Runs `body` with the store's declared sources overlaid (spec §23.4): the
|
|
863
|
+
* sources, loaded beforehand (URLs included), append inside a transaction
|
|
864
|
+
* that always rolls back, so external data is consulted at query time and
|
|
865
|
+
* nothing persists, digests included.
|
|
866
|
+
*/
|
|
867
|
+
const withSources = <T>(store: Store, discovered: Declared.Discovery, body: () => T): undefined | { value: T } => {
|
|
868
|
+
let result: undefined | { value: T }
|
|
869
|
+
try {
|
|
870
|
+
store.transaction(() => {
|
|
871
|
+
// Discovered on a snapshot: if a writer changed the declarations
|
|
872
|
+
// since, the sequence is stale and the caller rediscovers.
|
|
873
|
+
if (!Declared.sameDeclarations(discovered.baseline, Declared.declarationState(store))) {
|
|
874
|
+
throw sourcesRollback
|
|
875
|
+
}
|
|
876
|
+
for (const source of discovered.sequence) {
|
|
877
|
+
const report = Declared.run(store, source, { force: true })
|
|
878
|
+
if (report.failures.length > 0) {
|
|
879
|
+
throw new LocateError(`source/${source.declared.name}: ${report.failures.map(failure => `${failure.record}: ${failure.problems.join('; ')}`).join('; ')}`)
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
result = { value: body() }
|
|
883
|
+
throw sourcesRollback
|
|
884
|
+
})
|
|
885
|
+
} catch (error) {
|
|
886
|
+
if (error !== sourcesRollback) {
|
|
887
|
+
throw error
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return result
|
|
891
|
+
}
|
|
892
|
+
|
|
803
893
|
const readInput = (files: readonly string[]): string =>
|
|
804
894
|
files.length === 0 || (files.length === 1 && files[0] === '-') ?
|
|
805
895
|
readStdin() :
|
|
806
|
-
files.map(file => readFileSync(file,
|
|
896
|
+
files.map(file => decodeInput(readFileSync(file), file)).join('\n')
|
|
807
897
|
|
|
808
898
|
export const parseCommand = (argv: readonly string[]): Output => {
|
|
809
899
|
const { values, positionals } = parseArgs({
|
|
@@ -884,7 +974,7 @@ const ingestCommand = (name: 'add' | 'import') => (argv: readonly string[]): Out
|
|
|
884
974
|
})
|
|
885
975
|
const input = readInput(positionals)
|
|
886
976
|
const store = openDb(values, 'write')
|
|
887
|
-
|
|
977
|
+
return withCommandStore(store, () => {
|
|
888
978
|
const options = {
|
|
889
979
|
strict: values.strict === true,
|
|
890
980
|
...name === 'add' && values['no-src'] !== true ? { source: 'cli' } : {}
|
|
@@ -906,11 +996,7 @@ const ingestCommand = (name: 'add' | 'import') => (argv: readonly string[]): Out
|
|
|
906
996
|
out: `added ${outcome.result.ids.length} claim(s), ${outcome.result.edges} edge(s)\n`,
|
|
907
997
|
err: problems === '' ? '' : `${problems}\n`
|
|
908
998
|
}
|
|
909
|
-
}
|
|
910
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
911
|
-
} finally {
|
|
912
|
-
store.close()
|
|
913
|
-
}
|
|
999
|
+
})
|
|
914
1000
|
}
|
|
915
1001
|
|
|
916
1002
|
export const addCommand = ingestCommand('add')
|
|
@@ -929,39 +1015,142 @@ export const addCommand = ingestCommand('add')
|
|
|
929
1015
|
*/
|
|
930
1016
|
export const importCommand = ingestCommand('import')
|
|
931
1017
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1018
|
+
const queryOptions = {
|
|
1019
|
+
db: { type: 'string' },
|
|
1020
|
+
json: { type: 'boolean' },
|
|
1021
|
+
all: { type: 'boolean' },
|
|
1022
|
+
aliases: { type: 'boolean' },
|
|
1023
|
+
'as-of': { type: 'string' },
|
|
1024
|
+
at: { type: 'string' },
|
|
1025
|
+
resolve: { type: 'boolean' },
|
|
1026
|
+
sources: { type: 'boolean' },
|
|
1027
|
+
limit: { type: 'string' },
|
|
1028
|
+
cursor: { type: 'string' },
|
|
1029
|
+
'no-prelude': { type: 'boolean' }
|
|
1030
|
+
} as const
|
|
1031
|
+
|
|
1032
|
+
type QueryValues = {
|
|
1033
|
+
db?: string
|
|
1034
|
+
json?: boolean
|
|
1035
|
+
all?: boolean
|
|
1036
|
+
aliases?: boolean
|
|
1037
|
+
'as-of'?: string
|
|
1038
|
+
at?: string
|
|
1039
|
+
resolve?: boolean
|
|
1040
|
+
sources?: boolean
|
|
1041
|
+
limit?: string
|
|
1042
|
+
cursor?: string
|
|
1043
|
+
'no-prelude'?: boolean
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const parseQuery = (argv: readonly string[]): { values: QueryValues, pattern: string } | { failure: Output } => {
|
|
1047
|
+
const { values, positionals } = parseArgs({ args: [...argv], options: queryOptions, allowPositionals: true })
|
|
949
1048
|
if (positionals.length === 0) {
|
|
950
|
-
return fail('cave query: a pattern is required (spec §12.1)\n')
|
|
1049
|
+
return { failure: fail('cave query: a pattern is required (spec §12.1)\n') }
|
|
1050
|
+
}
|
|
1051
|
+
if (values.sources === true && values.cursor !== undefined) {
|
|
1052
|
+
return { failure: fail('cave query: --cursor does not apply to --sources — the overlay exists for one invocation and is answered whole\n') }
|
|
1053
|
+
}
|
|
1054
|
+
return { values, pattern: positionals.join('\n') }
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* `cave query --sources` — the store with its declared sources overlaid
|
|
1059
|
+
* (spec §23.4). Asynchronous because the sources load first, URLs
|
|
1060
|
+
* included, before the overlay appends inside a transaction that rolls
|
|
1061
|
+
* back; the dispatcher routes `--sources` here. A text store followed its
|
|
1062
|
+
* local sources on open, so only what assembly could not follow — URL
|
|
1063
|
+
* sources and what they declare — is loaded. The overlay exists for this
|
|
1064
|
+
* invocation alone, so the answer is whole: pages are followed inside the
|
|
1065
|
+
* transaction and no cursor is handed out.
|
|
1066
|
+
*/
|
|
1067
|
+
export const querySourcesCommand = async (
|
|
1068
|
+
argv: readonly string[],
|
|
1069
|
+
runtime: { readonly fetchImpl?: Declared.DiscoverOptions['fetchImpl'] } = {}
|
|
1070
|
+
): Promise<Output> => {
|
|
1071
|
+
const parsed = parseQuery(argv)
|
|
1072
|
+
if ('failure' in parsed) {
|
|
1073
|
+
return parsed.failure
|
|
1074
|
+
}
|
|
1075
|
+
const { values, pattern } = parsed
|
|
1076
|
+
if (values.sources !== true) {
|
|
1077
|
+
return queryCommand(argv)
|
|
1078
|
+
}
|
|
1079
|
+
const root = values.db ?? defaultDbPath()
|
|
1080
|
+
// The overlay appends inside a transaction it rolls back: writable, but
|
|
1081
|
+
// never creating or migrating (spec §13.7).
|
|
1082
|
+
const store = openDb(values, 'scratch')
|
|
1083
|
+
return await withAsyncCommandStore(store, async () => {
|
|
1084
|
+
for (let attempt = 0; attempt < Declared.overlayAttempts; attempt += 1) {
|
|
1085
|
+
const discovered = await Declared.discovery(store, root, {
|
|
1086
|
+
// The overlay applies every source it loads (force), so discovery
|
|
1087
|
+
// simulates that; a text store skips what assembly already followed.
|
|
1088
|
+
force: true,
|
|
1089
|
+
...kindOf(root) === 'text' ? { skipFollowed: true } : {},
|
|
1090
|
+
...runtime.fetchImpl === undefined ? {} : { fetchImpl: runtime.fetchImpl }
|
|
1091
|
+
})
|
|
1092
|
+
const page = withSources(store, discovered, () => wholeQuery(store, values, pattern))
|
|
1093
|
+
if (page !== undefined) {
|
|
1094
|
+
return renderQueryPage(store, values, pattern, () => page.value)
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
throw Declared.staleOverlay()
|
|
1098
|
+
})
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
export const queryCommand = (argv: readonly string[]): Output => {
|
|
1102
|
+
const parsed = parseQuery(argv)
|
|
1103
|
+
if ('failure' in parsed) {
|
|
1104
|
+
return parsed.failure
|
|
1105
|
+
}
|
|
1106
|
+
const { values, pattern } = parsed
|
|
1107
|
+
if (values.sources === true) {
|
|
1108
|
+
// Loading sources cannot be synchronous: the in-process entry for the
|
|
1109
|
+
// overlay is `querySourcesCommand`, which the dispatcher uses.
|
|
1110
|
+
return fail('cave query: --sources loads the declared sources first and runs asynchronously — use querySourcesCommand\n')
|
|
951
1111
|
}
|
|
952
|
-
const pattern = positionals.join('\n')
|
|
953
1112
|
const store = openDb(values, 'read')
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1113
|
+
return withCommandStore(store, () => {
|
|
1114
|
+
return renderQueryPage(store, values, pattern, () => queryPage(store, values, pattern))
|
|
1115
|
+
})
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/** Every match of the overlay: the pages are followed here, inside the transaction that holds it. */
|
|
1119
|
+
const wholeQuery = (store: Store, values: QueryValues, pattern: string): ReturnType<typeof caveQueryPage> => {
|
|
1120
|
+
const matches: ReturnType<typeof caveQueryPage>['matches'][number][] = []
|
|
1121
|
+
let first: undefined | ReturnType<typeof caveQueryPage>
|
|
1122
|
+
let cursor: undefined | string
|
|
1123
|
+
for (;;) {
|
|
1124
|
+
const page = queryPage(store, { ...values, limit: '1000', ...cursor === undefined ? {} : { cursor } }, pattern)
|
|
1125
|
+
first ??= page
|
|
1126
|
+
matches.push(...page.matches)
|
|
1127
|
+
if (page.next === undefined) {
|
|
1128
|
+
const { next: _next, ...rest } = first
|
|
1129
|
+
return { ...rest, matches }
|
|
1130
|
+
}
|
|
1131
|
+
cursor = page.next
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
const queryPage = (store: Store, values: QueryValues, pattern: string): ReturnType<typeof caveQueryPage> =>
|
|
1136
|
+
caveQueryPage(store, pattern, {
|
|
1137
|
+
all: values.all === true,
|
|
1138
|
+
aliases: values.aliases === true,
|
|
1139
|
+
resolve: values.resolve === true,
|
|
1140
|
+
limit: values.limit === undefined ? defaultQueryLimit : Number(values.limit),
|
|
1141
|
+
...values.cursor === undefined ? {} : { cursor: values.cursor },
|
|
1142
|
+
...values['as-of'] === undefined ? {} : { asOf: values['as-of'] },
|
|
1143
|
+
...values.at === undefined ? {} : { at: values.at }
|
|
1144
|
+
})
|
|
1145
|
+
|
|
1146
|
+
const renderQueryPage = (
|
|
1147
|
+
store: Store,
|
|
1148
|
+
values: QueryValues,
|
|
1149
|
+
pattern: string,
|
|
1150
|
+
page: () => ReturnType<typeof caveQueryPage>
|
|
1151
|
+
): Output => {
|
|
1152
|
+
{
|
|
1153
|
+
const result = page()
|
|
965
1154
|
if (values.json === true) {
|
|
966
1155
|
return ok(`${JSON.stringify(result, undefined, 2)}\n`)
|
|
967
1156
|
}
|
|
@@ -990,10 +1179,6 @@ export const queryCommand = (argv: readonly string[]): Output => {
|
|
|
990
1179
|
line
|
|
991
1180
|
})
|
|
992
1181
|
return ok(`${lines.join('\n')}\n${result.next === undefined ? '' : `next: ${result.next}\n`}`)
|
|
993
|
-
} catch (error) {
|
|
994
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
995
|
-
} finally {
|
|
996
|
-
store.close()
|
|
997
1182
|
}
|
|
998
1183
|
}
|
|
999
1184
|
|
|
@@ -1026,7 +1211,7 @@ export const searchCommand = (argv: readonly string[]): Output => {
|
|
|
1026
1211
|
return fail(`cave search: --limit must be an integer from 1 to ${maxQueryLimit}\n`)
|
|
1027
1212
|
}
|
|
1028
1213
|
const store = openDb(values, 'read')
|
|
1029
|
-
|
|
1214
|
+
return withCommandStore(store, () => {
|
|
1030
1215
|
const raw = values.raw === true
|
|
1031
1216
|
const found = store.search(query, { raw, limit: limit + 1 })
|
|
1032
1217
|
const matches = found.slice(0, limit)
|
|
@@ -1050,11 +1235,7 @@ export const searchCommand = (argv: readonly string[]): Output => {
|
|
|
1050
1235
|
lines.push(`more matches beyond ${limit}; raise --limit`)
|
|
1051
1236
|
}
|
|
1052
1237
|
return ok(`${lines.join('\n')}\n`)
|
|
1053
|
-
}
|
|
1054
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1055
|
-
} finally {
|
|
1056
|
-
store.close()
|
|
1057
|
-
}
|
|
1238
|
+
})
|
|
1058
1239
|
}
|
|
1059
1240
|
|
|
1060
1241
|
/**
|
|
@@ -1074,15 +1255,20 @@ export const resolveCommand = (argv: readonly string[]): Output => {
|
|
|
1074
1255
|
},
|
|
1075
1256
|
allowPositionals: false
|
|
1076
1257
|
})
|
|
1077
|
-
const percent = (value: number): string =>
|
|
1258
|
+
const percent = (value: number): string => {
|
|
1259
|
+
const rounded = Math.round(value * 1000) / 10
|
|
1260
|
+
if (value > 0 && rounded === 0) return '<0.1%'
|
|
1261
|
+
if (value < 1 && rounded === 100) return '>99.9%'
|
|
1262
|
+
return `${rounded}%`
|
|
1263
|
+
}
|
|
1078
1264
|
const store = openDb(values, 'read')
|
|
1079
|
-
|
|
1265
|
+
return withCommandStore(store, () => {
|
|
1080
1266
|
if (values.policy === true) {
|
|
1081
1267
|
const policy = store.resolutionPolicy()
|
|
1082
1268
|
if (values.json === true) {
|
|
1083
1269
|
return ok(`${JSON.stringify(policy, undefined, 2)}\n`)
|
|
1084
1270
|
}
|
|
1085
|
-
const width =
|
|
1271
|
+
const width = policy.reduce((maximum, entry) => Math.max(maximum, entry.prefix.length + 7), 0)
|
|
1086
1272
|
const lines = policy.map(entry => {
|
|
1087
1273
|
const subject = entry.prefix === '' ? 'source' : `source/${entry.prefix}`
|
|
1088
1274
|
const dimensions = [
|
|
@@ -1104,11 +1290,7 @@ export const resolveCommand = (argv: readonly string[]): Output => {
|
|
|
1104
1290
|
group.rows.map((row, at) =>
|
|
1105
1291
|
`${at === 0 ? '' : ' over '}${row.raw_line} ; class ${row.res_class}, effective ${percent(row.res_conf)}`))
|
|
1106
1292
|
return ok(`${lines.join('\n')}\n`)
|
|
1107
|
-
}
|
|
1108
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1109
|
-
} finally {
|
|
1110
|
-
store.close()
|
|
1111
|
-
}
|
|
1293
|
+
})
|
|
1112
1294
|
}
|
|
1113
1295
|
|
|
1114
1296
|
export const deriveCommand = (argv: readonly string[]): Output => {
|
|
@@ -1135,8 +1317,8 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1135
1317
|
return fail(`cave derive: --min-conf expects a confidence in 0..1 or N%, got ${JSON.stringify(values['min-conf'])}\n`)
|
|
1136
1318
|
}
|
|
1137
1319
|
const maxPasses = values['max-passes'] === undefined ? undefined : Number(values['max-passes'])
|
|
1138
|
-
if (maxPasses !== undefined && (!Number.
|
|
1139
|
-
return fail(`cave derive: --max-passes expects a positive integer, got ${JSON.stringify(values['max-passes'])}\n`)
|
|
1320
|
+
if (maxPasses !== undefined && (!Number.isSafeInteger(maxPasses) || maxPasses < 1)) {
|
|
1321
|
+
return fail(`cave derive: --max-passes expects a positive safe integer, got ${JSON.stringify(values['max-passes'])}\n`)
|
|
1140
1322
|
}
|
|
1141
1323
|
// The intent follows the branch order below: listing reads, retracting
|
|
1142
1324
|
// writes, a dry run appends inside a rolled-back transaction.
|
|
@@ -1144,7 +1326,7 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1144
1326
|
values.list === true ? 'read' :
|
|
1145
1327
|
values.retract !== undefined ? 'write' :
|
|
1146
1328
|
values['dry-run'] === true ? 'scratch' : 'write')
|
|
1147
|
-
|
|
1329
|
+
return withCommandStore(store, () => {
|
|
1148
1330
|
if (values.list === true) {
|
|
1149
1331
|
const rules = listRules(store)
|
|
1150
1332
|
if (values.json === true) {
|
|
@@ -1204,13 +1386,15 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1204
1386
|
report = derive(store, options)
|
|
1205
1387
|
}
|
|
1206
1388
|
if (declaration !== undefined) {
|
|
1207
|
-
|
|
1389
|
+
for (const problem of declaration.problems) err.push(`rules line ${problem.line}: ${problem.message}`)
|
|
1208
1390
|
out.push(`declared ${declaration.declared} rule(s)` +
|
|
1209
1391
|
(declaration.unchanged > 0 ? `, ${declaration.unchanged} unchanged` : '') +
|
|
1210
1392
|
(declaration.prelude > 0 ? `, +${declaration.prelude} prelude claim(s)` : ''))
|
|
1211
1393
|
}
|
|
1394
|
+
const hasProblems = (declaration?.problems.length ?? 0) > 0 || !report.complete ||
|
|
1395
|
+
report.problems.length > 0 || report.rules.some(rule => rule.problems.length > 0)
|
|
1212
1396
|
if (values.json === true) {
|
|
1213
|
-
return { code:
|
|
1397
|
+
return { code: hasProblems ? 1 : 0, out: `${stableJson(store, report)}\n`, err: err.join('\n') + (err.length > 0 ? '\n' : '') }
|
|
1214
1398
|
}
|
|
1215
1399
|
for (const problem of report.problems) {
|
|
1216
1400
|
err.push(`${problem.subject}: ${problem.problems.join('; ')}`)
|
|
@@ -1220,33 +1404,15 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1220
1404
|
`${rule.solutions} solution(s), +${rule.appended} appended, ${rule.updated} updated, ${rule.retracted} retracted, ${rule.unchanged} unchanged` :
|
|
1221
1405
|
'unchanged premises, skipped'
|
|
1222
1406
|
out.push(`${rule.subject}: ${state}${rule.label === undefined ? '' : ` ; ${rule.label}`}`)
|
|
1223
|
-
|
|
1407
|
+
for (const problem of rule.problems) err.push(`${rule.subject}: ${problem}`)
|
|
1224
1408
|
}
|
|
1225
|
-
|
|
1409
|
+
for (const note of report.notes) out.push(`note: ${note}`)
|
|
1226
1410
|
out.push(
|
|
1227
|
-
`derived${values['dry-run'] === true ? ' (dry run)' : ''}: ` +
|
|
1411
|
+
`derived${report.complete ? '' : ' (incomplete)'}${values['dry-run'] === true ? ' (dry run)' : ''}: ` +
|
|
1228
1412
|
`+${report.appended} appended, ${report.updated} updated, ${report.retracted} retracted, ` +
|
|
1229
1413
|
`${report.unchanged} unchanged (${report.passes} pass(es))`)
|
|
1230
|
-
const hasProblems = !report.complete || report.problems.length > 0 || report.rules.some(rule => rule.problems.length > 0)
|
|
1231
1414
|
return { code: hasProblems ? 1 : 0, out: `${out.join('\n')}\n`, err: err.length === 0 ? '' : `${err.join('\n')}\n` }
|
|
1232
|
-
}
|
|
1233
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1234
|
-
} finally {
|
|
1235
|
-
store.close()
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
/**
|
|
1240
|
-
* Loads a hooks configuration file (spec §25.4): a JSON object mapping
|
|
1241
|
-
* hook names to shell command templates.
|
|
1242
|
-
*/
|
|
1243
|
-
const readHooks = (path: string): Record<string, string> => {
|
|
1244
|
-
const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))
|
|
1245
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed) ||
|
|
1246
|
-
Object.values(parsed).some(value => typeof value !== 'string')) {
|
|
1247
|
-
throw new Error(`${path}: hooks must be a JSON object of name → shell template strings`)
|
|
1248
|
-
}
|
|
1249
|
-
return parsed as Record<string, string>
|
|
1415
|
+
})
|
|
1250
1416
|
}
|
|
1251
1417
|
|
|
1252
1418
|
/** Text rendering of a spec §25.2 execution report. */
|
|
@@ -1266,15 +1432,14 @@ const renderActReport = (report: ActReport): { lines: string[], code: number } =
|
|
|
1266
1432
|
]
|
|
1267
1433
|
let code = 0
|
|
1268
1434
|
if (report.hook !== undefined) {
|
|
1269
|
-
if (
|
|
1270
|
-
|
|
1271
|
-
} else if (report.hook.error === undefined) {
|
|
1272
|
-
lines.push(`hook ${report.hook.name}: ok`)
|
|
1273
|
-
} else {
|
|
1274
|
-
// The claims committed before the hook ran (spec §25.4) — the
|
|
1275
|
-
// failure is reported, and the exit code carries it.
|
|
1435
|
+
if (report.hook.error !== undefined) {
|
|
1436
|
+
// Effects may already be committed even when hook lookup failed before spawn.
|
|
1276
1437
|
lines.push(`hook ${report.hook.name}: ${report.hook.error}`)
|
|
1277
1438
|
code = 1
|
|
1439
|
+
} else if (!report.hook.fired) {
|
|
1440
|
+
lines.push(`hook ${report.hook.name}: not fired (${report.hook.note})`)
|
|
1441
|
+
} else {
|
|
1442
|
+
lines.push(`hook ${report.hook.name}: ok`)
|
|
1278
1443
|
}
|
|
1279
1444
|
}
|
|
1280
1445
|
return { lines, code }
|
|
@@ -1305,7 +1470,7 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1305
1470
|
values.list === true ? 'read' :
|
|
1306
1471
|
values.retract !== undefined ? 'write' :
|
|
1307
1472
|
values['dry-run'] === true ? 'scratch' : 'write')
|
|
1308
|
-
|
|
1473
|
+
return withCommandStore(store, () => {
|
|
1309
1474
|
if (values.declare === true) {
|
|
1310
1475
|
const declaration = declareActions(store, readInput(positionals))
|
|
1311
1476
|
const err = declaration.problems.map(problem => `line ${problem.line}: ${problem.message}`)
|
|
@@ -1352,7 +1517,13 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1352
1517
|
if (at <= 0) {
|
|
1353
1518
|
return fail(`cave act: expected param=value, got ${JSON.stringify(pair)}\n`)
|
|
1354
1519
|
}
|
|
1355
|
-
|
|
1520
|
+
const parameter = pair.slice(0, at)
|
|
1521
|
+
if (Object.hasOwn(args, parameter)) {
|
|
1522
|
+
return fail(`cave act: duplicate parameter ${JSON.stringify(parameter)}\n`)
|
|
1523
|
+
}
|
|
1524
|
+
Object.defineProperty(args, parameter, {
|
|
1525
|
+
value: pair.slice(at + 1), enumerable: true, writable: true, configurable: true
|
|
1526
|
+
})
|
|
1356
1527
|
}
|
|
1357
1528
|
const hooksPath = values.hooks ?? process.env['CAVE_HOOKS']
|
|
1358
1529
|
const report = act(store, name, args, {
|
|
@@ -1368,11 +1539,7 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1368
1539
|
return report.ok ?
|
|
1369
1540
|
{ code, out: `${lines.join('\n')}\n`, err: '' } :
|
|
1370
1541
|
{ code, out: '', err: `${lines.join('\n')}\n` }
|
|
1371
|
-
}
|
|
1372
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1373
|
-
} finally {
|
|
1374
|
-
store.close()
|
|
1375
|
-
}
|
|
1542
|
+
})
|
|
1376
1543
|
}
|
|
1377
1544
|
|
|
1378
1545
|
/** Text rendering of the §20.2 report: always shape + coverage, non-empty advisory sections. */
|
|
@@ -1382,26 +1549,32 @@ const renderReport = (report: Report, staleDays: number): string => {
|
|
|
1382
1549
|
lines.push(`shape: ${coverage.expectations} expectation(s), ${coverage.instances} instance(s), ${coverage.satisfied}/${coverage.checks} satisfied`)
|
|
1383
1550
|
if (report.violations.length > 0) {
|
|
1384
1551
|
lines.push(`violations (${report.violations.length}):`)
|
|
1385
|
-
|
|
1552
|
+
for (const violation of report.violations) lines.push(` ${formatViolation(violation)}`)
|
|
1386
1553
|
}
|
|
1387
1554
|
if (report.stale.length > 0) {
|
|
1388
1555
|
lines.push(`stale (${report.stale.length}, older than ${staleDays} day(s)):`)
|
|
1389
|
-
|
|
1556
|
+
for (const { row, ageDays } of report.stale) lines.push(` ${row.raw_line} (${ageDays}d)`)
|
|
1390
1557
|
}
|
|
1391
1558
|
if (report.review.length > 0) {
|
|
1392
1559
|
lines.push(`review candidates (${report.review.length}, conf 0.3-0.7):`)
|
|
1393
|
-
|
|
1560
|
+
for (const row of report.review) lines.push(` ${row.raw_line}`)
|
|
1394
1561
|
}
|
|
1395
1562
|
if (report.disagreements.length > 0) {
|
|
1396
1563
|
lines.push(`alias disagreements (${report.disagreements.length}):`)
|
|
1397
1564
|
for (const disagreement of report.disagreements) {
|
|
1398
1565
|
lines.push(` ${disagreement.about} across ${disagreement.entities.join(', ')}:`)
|
|
1399
|
-
|
|
1566
|
+
for (const row of disagreement.rows) lines.push(` ${row.raw_line}`)
|
|
1400
1567
|
}
|
|
1401
1568
|
}
|
|
1569
|
+
const confidencePercent = (value: number): string => {
|
|
1570
|
+
const rounded = Math.round(value * 100)
|
|
1571
|
+
if (value > 0 && rounded === 0) return '<1%'
|
|
1572
|
+
if (value < 1 && rounded === 100) return '>99%'
|
|
1573
|
+
return `${rounded}%`
|
|
1574
|
+
}
|
|
1402
1575
|
const confidence = coverage.averageConfidence === null ?
|
|
1403
1576
|
'' :
|
|
1404
|
-
`; avg conf ${
|
|
1577
|
+
`; avg conf ${confidencePercent(coverage.averageConfidence)}, ${coverage.lowConfidence} low (< 0.3)`
|
|
1405
1578
|
lines.push(
|
|
1406
1579
|
`coverage: ${coverage.rows} row(s), ${coverage.facts} fact(s) — ` +
|
|
1407
1580
|
`${coverage.current} current, ${coverage.retracted} retracted, ${coverage.negated} negated` +
|
|
@@ -1426,16 +1599,14 @@ export const checkCommand = (argv: readonly string[]): Output => {
|
|
|
1426
1599
|
return fail(`cave check: --stale expects a non-negative number of days, got ${JSON.stringify(values.stale)}\n`)
|
|
1427
1600
|
}
|
|
1428
1601
|
const store = openDb(values, 'read')
|
|
1429
|
-
|
|
1602
|
+
return withCommandStore(store, () => {
|
|
1430
1603
|
const report = caveCheck(store, { staleDays })
|
|
1431
1604
|
const out = values.json === true ?
|
|
1432
1605
|
`${stableJson(store, report)}\n` :
|
|
1433
1606
|
renderReport(report, staleDays)
|
|
1434
1607
|
// Violations fail the check (spec §20.2); the other sections are advisory.
|
|
1435
1608
|
return { code: report.violations.length > 0 ? 1 : 0, out, err: '' }
|
|
1436
|
-
}
|
|
1437
|
-
store.close()
|
|
1438
|
-
}
|
|
1609
|
+
})
|
|
1439
1610
|
}
|
|
1440
1611
|
|
|
1441
1612
|
const snapshotLine = (
|
|
@@ -1479,17 +1650,16 @@ export const backupCommand = (argv: readonly string[]): Output => {
|
|
|
1479
1650
|
// the destination aliases the source: refuse before the file could be
|
|
1480
1651
|
// replaced by a SQLite snapshot of itself (a SQLite source is covered by
|
|
1481
1652
|
// the API's own check).
|
|
1482
|
-
if (
|
|
1653
|
+
if (sameStoreFile(dbPath, values.out)) {
|
|
1483
1654
|
return fail(`cave backup: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1484
1655
|
}
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
}
|
|
1656
|
+
const destination = values.out
|
|
1657
|
+
const store = openAt(dbPath, { intent: 'read', assemble: assembleSources })
|
|
1658
|
+
return withCommandStore(store, () => {
|
|
1659
|
+
return ok(snapshotLine('created', backupStore(store, destination, { force: values.force === true })))
|
|
1660
|
+
})
|
|
1491
1661
|
} catch (error) {
|
|
1492
|
-
return fail(`${
|
|
1662
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1493
1663
|
}
|
|
1494
1664
|
}
|
|
1495
1665
|
|
|
@@ -1519,7 +1689,7 @@ export const restoreCommand = (argv: readonly string[]): Output => {
|
|
|
1519
1689
|
...values.sha256 === undefined ? {} : { expectedSha256: values.sha256 }
|
|
1520
1690
|
})))
|
|
1521
1691
|
} catch (error) {
|
|
1522
|
-
return fail(`${
|
|
1692
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1523
1693
|
}
|
|
1524
1694
|
}
|
|
1525
1695
|
|
|
@@ -1558,15 +1728,15 @@ export const suggestAliasCommand = async (
|
|
|
1558
1728
|
return fail(`cave suggest-alias: --min expects a score in 0..1 or N%, got ${JSON.stringify(values.min)}\n`)
|
|
1559
1729
|
}
|
|
1560
1730
|
const limit = values.limit === undefined ? undefined : Number(values.limit)
|
|
1561
|
-
if (limit !== undefined && (!Number.
|
|
1562
|
-
return fail(`cave suggest-alias: --limit expects a positive integer, got ${JSON.stringify(values.limit)}\n`)
|
|
1731
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 1)) {
|
|
1732
|
+
return fail(`cave suggest-alias: --limit expects a positive safe integer, got ${JSON.stringify(values.limit)}\n`)
|
|
1563
1733
|
}
|
|
1564
1734
|
const timeoutSeconds = values.timeout === undefined ? undefined : Number(values.timeout)
|
|
1565
1735
|
if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
|
|
1566
1736
|
return fail(`cave suggest-alias: --timeout must be a positive number of seconds, got '${values.timeout}'\n`)
|
|
1567
1737
|
}
|
|
1568
1738
|
const store = openDb(values, values.write === true ? 'write' : 'read')
|
|
1569
|
-
|
|
1739
|
+
return await withAsyncCommandStore(store, async () => {
|
|
1570
1740
|
let suggestions = suggestAliases(store, { minScore, ...limit === undefined ? {} : { limit } })
|
|
1571
1741
|
if (values.agent !== undefined && suggestions.length > 0) {
|
|
1572
1742
|
const reply = await shellComplete(values.agent, {
|
|
@@ -1578,7 +1748,9 @@ export const suggestAliasCommand = async (
|
|
|
1578
1748
|
suggestions = parseJudgeReply(reply, suggestions.length).map(index => suggestions[index]!)
|
|
1579
1749
|
}
|
|
1580
1750
|
if (values.json === true) {
|
|
1581
|
-
|
|
1751
|
+
const result = values.write === true ?
|
|
1752
|
+
{ suggestions, ...writeSuggestions(store, suggestions) } : suggestions
|
|
1753
|
+
return ok(`${JSON.stringify(result, undefined, 2)}\n`)
|
|
1582
1754
|
}
|
|
1583
1755
|
if (suggestions.length === 0) {
|
|
1584
1756
|
return ok('no alias suggestions\n')
|
|
@@ -1589,11 +1761,9 @@ export const suggestAliasCommand = async (
|
|
|
1589
1761
|
return ok(`${lines.join('\n')}\nappended ${appended} suggested alias claim(s)\n`)
|
|
1590
1762
|
}
|
|
1591
1763
|
return ok(`${lines.join('\n')}\n`)
|
|
1592
|
-
}
|
|
1593
|
-
store.close()
|
|
1594
|
-
}
|
|
1764
|
+
})
|
|
1595
1765
|
} catch (error) {
|
|
1596
|
-
return fail(`${
|
|
1766
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1597
1767
|
}
|
|
1598
1768
|
}
|
|
1599
1769
|
|
|
@@ -1623,7 +1793,7 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1623
1793
|
}
|
|
1624
1794
|
const dbPath = values.db ?? defaultDbPath()
|
|
1625
1795
|
const store = openDb(values, values['dry-run'] === true ? 'scratch' : 'write')
|
|
1626
|
-
|
|
1796
|
+
return withCommandStore(store, () => {
|
|
1627
1797
|
const options = {
|
|
1628
1798
|
into: values.into === undefined ? labelOf(dbPath) : sanitizeLabel(values.into),
|
|
1629
1799
|
record: values['no-record'] !== true,
|
|
@@ -1637,7 +1807,7 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1637
1807
|
return { code: report.problems.length > 0 ? 1 : 0, out: `${JSON.stringify(report, undefined, 2)}\n`, err: '' }
|
|
1638
1808
|
}
|
|
1639
1809
|
if (report.problems.length > 0) {
|
|
1640
|
-
const detail = report.problems.map(problem => ` line ${problem.line}: ${problem.message}`).join('\n')
|
|
1810
|
+
const detail = report.problems.map(problem => ` ${problem.line === 0 ? '' : `line ${problem.line}: `}${problem.message}`).join('\n')
|
|
1641
1811
|
return fail(`cave sync: ${source}: ${report.problems.length} problem(s), nothing merged\n${detail}\n`)
|
|
1642
1812
|
}
|
|
1643
1813
|
const lines = [
|
|
@@ -1649,17 +1819,14 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1649
1819
|
lines.push(`record: ${report.record}`)
|
|
1650
1820
|
}
|
|
1651
1821
|
return ok(`${lines.join('\n')}\n`)
|
|
1652
|
-
}
|
|
1653
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1654
|
-
} finally {
|
|
1655
|
-
store.close()
|
|
1656
|
-
}
|
|
1822
|
+
})
|
|
1657
1823
|
}
|
|
1658
1824
|
|
|
1659
1825
|
/**
|
|
1660
1826
|
* `cave report` — the §31 deliverable: a markdown template's cave-q
|
|
1661
|
-
* blocks and inline splices render from the store,
|
|
1662
|
-
* cited back to
|
|
1827
|
+
* blocks and inline splices render from the store, with matched rows
|
|
1828
|
+
* cited back to their claims. Ordinary prose is not automatically checked
|
|
1829
|
+
* or cited. Problems mark the text, land on stderr with
|
|
1663
1830
|
* template line numbers, and fail the exit code — the render never
|
|
1664
1831
|
* silently drops a fact.
|
|
1665
1832
|
*/
|
|
@@ -1678,16 +1845,22 @@ export const reportCommand = (argv: readonly string[]): Output => {
|
|
|
1678
1845
|
},
|
|
1679
1846
|
allowPositionals: true
|
|
1680
1847
|
})
|
|
1681
|
-
|
|
1848
|
+
if (values.at !== undefined && Time.parseInstant(values.at) === undefined) {
|
|
1849
|
+
return fail(`cave report: --at cannot parse at anchor ${JSON.stringify(values.at)}\n`)
|
|
1850
|
+
}
|
|
1851
|
+
if (values['as-of'] !== undefined && QuerySql.asOfBoundary(values['as-of']) === undefined) {
|
|
1852
|
+
return fail(`cave report: --as-of cannot parse as-of boundary ${JSON.stringify(values['as-of'])}\n`)
|
|
1853
|
+
}
|
|
1682
1854
|
const maximum = Sensitivity.parse(values['max-sensitivity'] ?? Sensitivity.defaultMaximum)
|
|
1683
1855
|
if (maximum === undefined) {
|
|
1684
1856
|
return fail(`cave report: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`)
|
|
1685
1857
|
}
|
|
1686
|
-
if (values.out !== undefined &&
|
|
1858
|
+
if (values.out !== undefined && sameStoreFile(values.db ?? defaultDbPath(), values.out)) {
|
|
1687
1859
|
return fail(`cave report: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1688
1860
|
}
|
|
1861
|
+
const template = readInput(positionals)
|
|
1689
1862
|
const store = openDb(values, 'read')
|
|
1690
|
-
|
|
1863
|
+
return withCommandStore(store, () => {
|
|
1691
1864
|
const rendered = caveReport(store, template, {
|
|
1692
1865
|
aliases: values.aliases === true,
|
|
1693
1866
|
resolve: values.resolve === true,
|
|
@@ -1702,17 +1875,13 @@ export const reportCommand = (argv: readonly string[]): Output => {
|
|
|
1702
1875
|
if (values.out === undefined) {
|
|
1703
1876
|
return { code, out: rendered.markdown, err: err === '' ? '' : `${err}\n` }
|
|
1704
1877
|
}
|
|
1705
|
-
|
|
1878
|
+
writeOutput(values.out, rendered.markdown)
|
|
1706
1879
|
return {
|
|
1707
1880
|
code,
|
|
1708
1881
|
out: `rendered ${rendered.citations} citation(s) to ${values.out}\n`,
|
|
1709
1882
|
err: err === '' ? '' : `${err}\n`
|
|
1710
1883
|
}
|
|
1711
|
-
}
|
|
1712
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1713
|
-
} finally {
|
|
1714
|
-
store.close()
|
|
1715
|
-
}
|
|
1884
|
+
})
|
|
1716
1885
|
}
|
|
1717
1886
|
|
|
1718
1887
|
/**
|
|
@@ -1733,6 +1902,21 @@ const sameFile = (a: string, b: string): boolean => {
|
|
|
1733
1902
|
}
|
|
1734
1903
|
}
|
|
1735
1904
|
|
|
1905
|
+
/** Protect every physical file SQLite uses, including through a database symlink. */
|
|
1906
|
+
const sameStoreFile = (db: string, output: string): boolean => {
|
|
1907
|
+
if (sameFile(db, output)) return true
|
|
1908
|
+
try {
|
|
1909
|
+
if (kindOf(db) !== 'sqlite') return false
|
|
1910
|
+
const canonicalOutput = join(realpathSync(dirname(resolve(output))), basename(output))
|
|
1911
|
+
return [...new Set([db, realpathSync(db)])].some(base =>
|
|
1912
|
+
['-wal', '-shm', '-journal'].some(suffix =>
|
|
1913
|
+
sameFile(`${base}${suffix}`, output) || sameFile(`${base}${suffix}`, canonicalOutput)))
|
|
1914
|
+
} catch {
|
|
1915
|
+
// The normal store-open path reports missing or unreadable source files.
|
|
1916
|
+
return false
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1736
1920
|
export const exportCommand = (argv: readonly string[]): Output => {
|
|
1737
1921
|
const { values } = parseArgs({
|
|
1738
1922
|
args: [...argv],
|
|
@@ -1751,26 +1935,22 @@ export const exportCommand = (argv: readonly string[]): Output => {
|
|
|
1751
1935
|
if (maximum === undefined) {
|
|
1752
1936
|
return fail(`cave export: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`)
|
|
1753
1937
|
}
|
|
1754
|
-
if (values.out !== undefined &&
|
|
1938
|
+
if (values.out !== undefined && sameStoreFile(db, values.out)) {
|
|
1755
1939
|
return fail(`cave export: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1756
1940
|
}
|
|
1757
1941
|
const store = openDb(values, 'read')
|
|
1758
|
-
|
|
1942
|
+
return withCommandStore(store, () => {
|
|
1759
1943
|
const text = store.exportText({ current: values.current === true, tx: values.tx === true, maxSensitivity: maximum })
|
|
1760
1944
|
if (values.out === undefined) {
|
|
1761
1945
|
return ok(text)
|
|
1762
1946
|
}
|
|
1763
|
-
|
|
1947
|
+
writeOutput(values.out, text)
|
|
1764
1948
|
// Root claims only: shorthand prefixes and §28.4 annotations are not
|
|
1765
1949
|
// claims, while qualifier/grouping leaves have a materialized parent.
|
|
1766
1950
|
const claims = parseDocument(text).lines
|
|
1767
1951
|
.filter(line => line.kind === 'claim' && line.parent === undefined).length
|
|
1768
1952
|
return ok(`exported ${claims} claim(s) to ${values.out}\n`)
|
|
1769
|
-
}
|
|
1770
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1771
|
-
} finally {
|
|
1772
|
-
store.close()
|
|
1773
|
-
}
|
|
1953
|
+
})
|
|
1774
1954
|
}
|
|
1775
1955
|
|
|
1776
1956
|
/** `cave generate` — spec §20.4's versioned TypeScript client artifact. */
|
|
@@ -1790,11 +1970,11 @@ export const generateCommand = (argv: readonly string[]): Output => {
|
|
|
1790
1970
|
return fail(`cave generate: --version expects a positive integer, got ${JSON.stringify(values.version)}\n`)
|
|
1791
1971
|
}
|
|
1792
1972
|
const db = values.db ?? defaultDbPath()
|
|
1793
|
-
if (values.out !== undefined &&
|
|
1973
|
+
if (values.out !== undefined && sameStoreFile(db, values.out)) {
|
|
1794
1974
|
return fail(`cave generate: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1795
1975
|
}
|
|
1796
1976
|
const store = openDb(values, 'read')
|
|
1797
|
-
|
|
1977
|
+
return withCommandStore(store, () => {
|
|
1798
1978
|
const generated = generateClient(store, version === undefined ? {} : { version })
|
|
1799
1979
|
if (!generated.ok) {
|
|
1800
1980
|
return fail(`cave generate: schema cannot be generated:\n${generated.problems.map(problem => ` ${problem}`).join('\n')}\n`)
|
|
@@ -1802,14 +1982,10 @@ export const generateCommand = (argv: readonly string[]): Output => {
|
|
|
1802
1982
|
if (values.out === undefined) {
|
|
1803
1983
|
return ok(generated.code)
|
|
1804
1984
|
}
|
|
1805
|
-
|
|
1985
|
+
writeOutput(values.out, generated.code)
|
|
1806
1986
|
return ok(`generated typed client v${generated.version} (${generated.fields.length} field(s), ` +
|
|
1807
1987
|
`sha256:${generated.digest}) to ${values.out}\n`)
|
|
1808
|
-
}
|
|
1809
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1810
|
-
} finally {
|
|
1811
|
-
store.close()
|
|
1812
|
-
}
|
|
1988
|
+
})
|
|
1813
1989
|
}
|
|
1814
1990
|
|
|
1815
1991
|
/**
|
|
@@ -1859,7 +2035,7 @@ export const reconstructCommand = async (
|
|
|
1859
2035
|
return fail(`cave reconstruct: --timeout must be a positive number of seconds, got '${values.timeout}'\n`)
|
|
1860
2036
|
}
|
|
1861
2037
|
const store = openDb(values, 'read')
|
|
1862
|
-
|
|
2038
|
+
return await withAsyncCommandStore(store, async () => {
|
|
1863
2039
|
const graph = sqliteStore(store)
|
|
1864
2040
|
const result = values.agent === undefined ?
|
|
1865
2041
|
reconstruct(graph, heuristicPolicy(budgets), positionals) :
|
|
@@ -1883,11 +2059,9 @@ export const reconstructCommand = async (
|
|
|
1883
2059
|
...result.claims.map(claim => emitClaim(claim))
|
|
1884
2060
|
]
|
|
1885
2061
|
return ok(lines.length === 0 ? '' : `${lines.join('\n')}\n`)
|
|
1886
|
-
}
|
|
1887
|
-
store.close()
|
|
1888
|
-
}
|
|
2062
|
+
})
|
|
1889
2063
|
} catch (error) {
|
|
1890
|
-
return fail(`${
|
|
2064
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1891
2065
|
}
|
|
1892
2066
|
}
|
|
1893
2067
|
|