@cavelang/cli 0.35.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 +224 -12
- 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 +238 -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 +6 -27
- package/dist/internal/connect/declared.d.ts.map +1 -1
- package/dist/internal/connect/declared.js +165 -76
- package/dist/internal/connect/declared.js.map +1 -1
- 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/main.d.ts +1 -0
- package/dist/internal/connect/main.d.ts.map +1 -1
- package/dist/internal/connect/main.js +288 -114
- 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 +1 -18
- package/dist/internal/connect/run.d.ts.map +1 -1
- package/dist/internal/connect/run.js +115 -55
- package/dist/internal/connect/run.js.map +1 -1
- package/dist/internal/connect/source.d.ts +3 -1
- package/dist/internal/connect/source.d.ts.map +1 -1
- package/dist/internal/connect/source.js +226 -47
- package/dist/internal/connect/source.js.map +1 -1
- package/dist/internal/connect/template.d.ts +2 -2
- package/dist/internal/connect/template.d.ts.map +1 -1
- package/dist/internal/connect/template.js +25 -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 +46 -16
- 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 +42 -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/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 +61 -8
- 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 +3 -2
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +188 -173
- package/dist/src/cli.js.map +1 -1
- package/dist/src/dispatch.d.ts.map +1 -1
- package/dist/src/dispatch.js +3 -2
- package/dist/src/dispatch.js.map +1 -1
- package/dist/src/doctor.d.ts.map +1 -1
- package/dist/src/doctor.js +178 -102
- 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/package.json +23 -20
- package/src/atomic-output.ts +47 -0
- package/src/cli.ts +170 -147
- package/src/dispatch.ts +3 -2
- package/src/doctor.ts +192 -111
- package/src/error-message.ts +13 -0
- package/src/hooks-config.ts +22 -0
package/src/cli.ts
CHANGED
|
@@ -35,13 +35,16 @@
|
|
|
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 { backup as backupStore, defaultDbPath, kindOf, LocateError, openAt, restoreBackup, Sensitivity, verifyBackup } from '@cavelang/store'
|
|
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'
|
|
46
49
|
import { Declared, assemble as assembleSources } from '@cavelang/connect'
|
|
47
50
|
import { defaultLimit as defaultQueryLimit, maxLimit as maxQueryLimit, page as caveQueryPage } from '@cavelang/query'
|
|
@@ -529,7 +532,7 @@ Usage:
|
|
|
529
532
|
|
|
530
533
|
Options:
|
|
531
534
|
${dbHelp}
|
|
532
|
-
--out <file>
|
|
535
|
+
--out <file> atomically replace a file with the generated module
|
|
533
536
|
--version <n> generated-client format version (default and currently
|
|
534
537
|
supported: 1)
|
|
535
538
|
--no-prelude open the store without the standard verb registry
|
|
@@ -553,7 +556,7 @@ Usage:
|
|
|
553
556
|
Options:
|
|
554
557
|
${dbHelp}
|
|
555
558
|
--min <s> minimum evidence score, 0..1 or N% (default 60%)
|
|
556
|
-
--limit <n> at most n suggestions, strongest first
|
|
559
|
+
--limit <n> positive safe integer; at most n suggestions, strongest first
|
|
557
560
|
--agent <template> LLM judge filtering the candidates — the cave
|
|
558
561
|
ingest/eval shell contract: the prompt (each pair
|
|
559
562
|
with both sides' current claims) is piped to stdin
|
|
@@ -584,6 +587,10 @@ claim, so the opt-in §13.6 closure links it until reviewed:
|
|
|
584
587
|
claim identity, §9.2, so a plain @ 0% append would start a new series
|
|
585
588
|
and leave the suggested link standing).
|
|
586
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
|
+
|
|
587
594
|
Examples:
|
|
588
595
|
cave suggest-alias --db k.db
|
|
589
596
|
cave suggest-alias --db k.db --min 80% --limit 10
|
|
@@ -641,7 +648,7 @@ Usage:
|
|
|
641
648
|
|
|
642
649
|
Options:
|
|
643
650
|
${dbHelp}
|
|
644
|
-
--out <file>
|
|
651
|
+
--out <file> atomically replace a file instead of stdout — refused when it
|
|
645
652
|
names the store's own database file
|
|
646
653
|
--current current beliefs only (skip superseded rows); compacting
|
|
647
654
|
view, not sanitization or selective erasure (spec §9.6)
|
|
@@ -657,6 +664,10 @@ Options:
|
|
|
657
664
|
Retention: claim history is permanent. Retraction and --current do not
|
|
658
665
|
guarantee erasure from the store, exports, peers, or backups (spec §9.6).
|
|
659
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
|
+
|
|
660
671
|
Examples:
|
|
661
672
|
cave export --db k.db
|
|
662
673
|
cave export --db k.db --out internal-export.cave
|
|
@@ -676,7 +687,7 @@ Usage:
|
|
|
676
687
|
|
|
677
688
|
Options:
|
|
678
689
|
${dbHelp}
|
|
679
|
-
--out <file>
|
|
690
|
+
--out <file> atomically replace a file with the rendered markdown
|
|
680
691
|
--aliases queries match through the alias closure (spec §13.6)
|
|
681
692
|
--resolve queries match resolved winners only (spec §26) — the fix
|
|
682
693
|
when an inline splice reports an ambiguous fact
|
|
@@ -775,6 +786,15 @@ Examples:
|
|
|
775
786
|
cave help serve`
|
|
776
787
|
}
|
|
777
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
|
+
|
|
778
798
|
const readStdin = (): string => {
|
|
779
799
|
const chunks: Buffer[] = []
|
|
780
800
|
const buffer = Buffer.allocUnsafe(64 * 1024)
|
|
@@ -782,7 +802,7 @@ const readStdin = (): string => {
|
|
|
782
802
|
for (;;) {
|
|
783
803
|
try {
|
|
784
804
|
const size = readSync(0, buffer, 0, buffer.length, null)
|
|
785
|
-
if (size === 0) return Buffer.concat(chunks)
|
|
805
|
+
if (size === 0) return decodeInput(Buffer.concat(chunks), 'stdin')
|
|
786
806
|
chunks.push(Buffer.from(buffer.subarray(0, size)))
|
|
787
807
|
} catch (error) {
|
|
788
808
|
if ((error as NodeJS.ErrnoException).code !== 'EAGAIN') throw error
|
|
@@ -810,6 +830,32 @@ const openDb = (values: { db?: string, 'no-prelude'?: boolean }, intent: OpenInt
|
|
|
810
830
|
...values['no-prelude'] === true ? { registry: Registry.empty } : {}
|
|
811
831
|
})
|
|
812
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
|
+
|
|
813
859
|
const sourcesRollback = Symbol('cave query --sources rollback')
|
|
814
860
|
|
|
815
861
|
/**
|
|
@@ -847,7 +893,7 @@ const withSources = <T>(store: Store, discovered: Declared.Discovery, body: () =
|
|
|
847
893
|
const readInput = (files: readonly string[]): string =>
|
|
848
894
|
files.length === 0 || (files.length === 1 && files[0] === '-') ?
|
|
849
895
|
readStdin() :
|
|
850
|
-
files.map(file => readFileSync(file,
|
|
896
|
+
files.map(file => decodeInput(readFileSync(file), file)).join('\n')
|
|
851
897
|
|
|
852
898
|
export const parseCommand = (argv: readonly string[]): Output => {
|
|
853
899
|
const { values, positionals } = parseArgs({
|
|
@@ -928,7 +974,7 @@ const ingestCommand = (name: 'add' | 'import') => (argv: readonly string[]): Out
|
|
|
928
974
|
})
|
|
929
975
|
const input = readInput(positionals)
|
|
930
976
|
const store = openDb(values, 'write')
|
|
931
|
-
|
|
977
|
+
return withCommandStore(store, () => {
|
|
932
978
|
const options = {
|
|
933
979
|
strict: values.strict === true,
|
|
934
980
|
...name === 'add' && values['no-src'] !== true ? { source: 'cli' } : {}
|
|
@@ -950,11 +996,7 @@ const ingestCommand = (name: 'add' | 'import') => (argv: readonly string[]): Out
|
|
|
950
996
|
out: `added ${outcome.result.ids.length} claim(s), ${outcome.result.edges} edge(s)\n`,
|
|
951
997
|
err: problems === '' ? '' : `${problems}\n`
|
|
952
998
|
}
|
|
953
|
-
}
|
|
954
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
955
|
-
} finally {
|
|
956
|
-
store.close()
|
|
957
|
-
}
|
|
999
|
+
})
|
|
958
1000
|
}
|
|
959
1001
|
|
|
960
1002
|
export const addCommand = ingestCommand('add')
|
|
@@ -1038,7 +1080,7 @@ export const querySourcesCommand = async (
|
|
|
1038
1080
|
// The overlay appends inside a transaction it rolls back: writable, but
|
|
1039
1081
|
// never creating or migrating (spec §13.7).
|
|
1040
1082
|
const store = openDb(values, 'scratch')
|
|
1041
|
-
|
|
1083
|
+
return await withAsyncCommandStore(store, async () => {
|
|
1042
1084
|
for (let attempt = 0; attempt < Declared.overlayAttempts; attempt += 1) {
|
|
1043
1085
|
const discovered = await Declared.discovery(store, root, {
|
|
1044
1086
|
// The overlay applies every source it loads (force), so discovery
|
|
@@ -1053,11 +1095,7 @@ export const querySourcesCommand = async (
|
|
|
1053
1095
|
}
|
|
1054
1096
|
}
|
|
1055
1097
|
throw Declared.staleOverlay()
|
|
1056
|
-
}
|
|
1057
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1058
|
-
} finally {
|
|
1059
|
-
store.close()
|
|
1060
|
-
}
|
|
1098
|
+
})
|
|
1061
1099
|
}
|
|
1062
1100
|
|
|
1063
1101
|
export const queryCommand = (argv: readonly string[]): Output => {
|
|
@@ -1072,13 +1110,9 @@ export const queryCommand = (argv: readonly string[]): Output => {
|
|
|
1072
1110
|
return fail('cave query: --sources loads the declared sources first and runs asynchronously — use querySourcesCommand\n')
|
|
1073
1111
|
}
|
|
1074
1112
|
const store = openDb(values, 'read')
|
|
1075
|
-
|
|
1113
|
+
return withCommandStore(store, () => {
|
|
1076
1114
|
return renderQueryPage(store, values, pattern, () => queryPage(store, values, pattern))
|
|
1077
|
-
}
|
|
1078
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1079
|
-
} finally {
|
|
1080
|
-
store.close()
|
|
1081
|
-
}
|
|
1115
|
+
})
|
|
1082
1116
|
}
|
|
1083
1117
|
|
|
1084
1118
|
/** Every match of the overlay: the pages are followed here, inside the transaction that holds it. */
|
|
@@ -1177,7 +1211,7 @@ export const searchCommand = (argv: readonly string[]): Output => {
|
|
|
1177
1211
|
return fail(`cave search: --limit must be an integer from 1 to ${maxQueryLimit}\n`)
|
|
1178
1212
|
}
|
|
1179
1213
|
const store = openDb(values, 'read')
|
|
1180
|
-
|
|
1214
|
+
return withCommandStore(store, () => {
|
|
1181
1215
|
const raw = values.raw === true
|
|
1182
1216
|
const found = store.search(query, { raw, limit: limit + 1 })
|
|
1183
1217
|
const matches = found.slice(0, limit)
|
|
@@ -1201,11 +1235,7 @@ export const searchCommand = (argv: readonly string[]): Output => {
|
|
|
1201
1235
|
lines.push(`more matches beyond ${limit}; raise --limit`)
|
|
1202
1236
|
}
|
|
1203
1237
|
return ok(`${lines.join('\n')}\n`)
|
|
1204
|
-
}
|
|
1205
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1206
|
-
} finally {
|
|
1207
|
-
store.close()
|
|
1208
|
-
}
|
|
1238
|
+
})
|
|
1209
1239
|
}
|
|
1210
1240
|
|
|
1211
1241
|
/**
|
|
@@ -1225,15 +1255,20 @@ export const resolveCommand = (argv: readonly string[]): Output => {
|
|
|
1225
1255
|
},
|
|
1226
1256
|
allowPositionals: false
|
|
1227
1257
|
})
|
|
1228
|
-
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
|
+
}
|
|
1229
1264
|
const store = openDb(values, 'read')
|
|
1230
|
-
|
|
1265
|
+
return withCommandStore(store, () => {
|
|
1231
1266
|
if (values.policy === true) {
|
|
1232
1267
|
const policy = store.resolutionPolicy()
|
|
1233
1268
|
if (values.json === true) {
|
|
1234
1269
|
return ok(`${JSON.stringify(policy, undefined, 2)}\n`)
|
|
1235
1270
|
}
|
|
1236
|
-
const width =
|
|
1271
|
+
const width = policy.reduce((maximum, entry) => Math.max(maximum, entry.prefix.length + 7), 0)
|
|
1237
1272
|
const lines = policy.map(entry => {
|
|
1238
1273
|
const subject = entry.prefix === '' ? 'source' : `source/${entry.prefix}`
|
|
1239
1274
|
const dimensions = [
|
|
@@ -1255,11 +1290,7 @@ export const resolveCommand = (argv: readonly string[]): Output => {
|
|
|
1255
1290
|
group.rows.map((row, at) =>
|
|
1256
1291
|
`${at === 0 ? '' : ' over '}${row.raw_line} ; class ${row.res_class}, effective ${percent(row.res_conf)}`))
|
|
1257
1292
|
return ok(`${lines.join('\n')}\n`)
|
|
1258
|
-
}
|
|
1259
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1260
|
-
} finally {
|
|
1261
|
-
store.close()
|
|
1262
|
-
}
|
|
1293
|
+
})
|
|
1263
1294
|
}
|
|
1264
1295
|
|
|
1265
1296
|
export const deriveCommand = (argv: readonly string[]): Output => {
|
|
@@ -1286,8 +1317,8 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1286
1317
|
return fail(`cave derive: --min-conf expects a confidence in 0..1 or N%, got ${JSON.stringify(values['min-conf'])}\n`)
|
|
1287
1318
|
}
|
|
1288
1319
|
const maxPasses = values['max-passes'] === undefined ? undefined : Number(values['max-passes'])
|
|
1289
|
-
if (maxPasses !== undefined && (!Number.
|
|
1290
|
-
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`)
|
|
1291
1322
|
}
|
|
1292
1323
|
// The intent follows the branch order below: listing reads, retracting
|
|
1293
1324
|
// writes, a dry run appends inside a rolled-back transaction.
|
|
@@ -1295,7 +1326,7 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1295
1326
|
values.list === true ? 'read' :
|
|
1296
1327
|
values.retract !== undefined ? 'write' :
|
|
1297
1328
|
values['dry-run'] === true ? 'scratch' : 'write')
|
|
1298
|
-
|
|
1329
|
+
return withCommandStore(store, () => {
|
|
1299
1330
|
if (values.list === true) {
|
|
1300
1331
|
const rules = listRules(store)
|
|
1301
1332
|
if (values.json === true) {
|
|
@@ -1355,13 +1386,15 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1355
1386
|
report = derive(store, options)
|
|
1356
1387
|
}
|
|
1357
1388
|
if (declaration !== undefined) {
|
|
1358
|
-
|
|
1389
|
+
for (const problem of declaration.problems) err.push(`rules line ${problem.line}: ${problem.message}`)
|
|
1359
1390
|
out.push(`declared ${declaration.declared} rule(s)` +
|
|
1360
1391
|
(declaration.unchanged > 0 ? `, ${declaration.unchanged} unchanged` : '') +
|
|
1361
1392
|
(declaration.prelude > 0 ? `, +${declaration.prelude} prelude claim(s)` : ''))
|
|
1362
1393
|
}
|
|
1394
|
+
const hasProblems = (declaration?.problems.length ?? 0) > 0 || !report.complete ||
|
|
1395
|
+
report.problems.length > 0 || report.rules.some(rule => rule.problems.length > 0)
|
|
1363
1396
|
if (values.json === true) {
|
|
1364
|
-
return { code:
|
|
1397
|
+
return { code: hasProblems ? 1 : 0, out: `${stableJson(store, report)}\n`, err: err.join('\n') + (err.length > 0 ? '\n' : '') }
|
|
1365
1398
|
}
|
|
1366
1399
|
for (const problem of report.problems) {
|
|
1367
1400
|
err.push(`${problem.subject}: ${problem.problems.join('; ')}`)
|
|
@@ -1371,33 +1404,15 @@ export const deriveCommand = (argv: readonly string[]): Output => {
|
|
|
1371
1404
|
`${rule.solutions} solution(s), +${rule.appended} appended, ${rule.updated} updated, ${rule.retracted} retracted, ${rule.unchanged} unchanged` :
|
|
1372
1405
|
'unchanged premises, skipped'
|
|
1373
1406
|
out.push(`${rule.subject}: ${state}${rule.label === undefined ? '' : ` ; ${rule.label}`}`)
|
|
1374
|
-
|
|
1407
|
+
for (const problem of rule.problems) err.push(`${rule.subject}: ${problem}`)
|
|
1375
1408
|
}
|
|
1376
|
-
|
|
1409
|
+
for (const note of report.notes) out.push(`note: ${note}`)
|
|
1377
1410
|
out.push(
|
|
1378
|
-
`derived${values['dry-run'] === true ? ' (dry run)' : ''}: ` +
|
|
1411
|
+
`derived${report.complete ? '' : ' (incomplete)'}${values['dry-run'] === true ? ' (dry run)' : ''}: ` +
|
|
1379
1412
|
`+${report.appended} appended, ${report.updated} updated, ${report.retracted} retracted, ` +
|
|
1380
1413
|
`${report.unchanged} unchanged (${report.passes} pass(es))`)
|
|
1381
|
-
const hasProblems = !report.complete || report.problems.length > 0 || report.rules.some(rule => rule.problems.length > 0)
|
|
1382
1414
|
return { code: hasProblems ? 1 : 0, out: `${out.join('\n')}\n`, err: err.length === 0 ? '' : `${err.join('\n')}\n` }
|
|
1383
|
-
}
|
|
1384
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1385
|
-
} finally {
|
|
1386
|
-
store.close()
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
/**
|
|
1391
|
-
* Loads a hooks configuration file (spec §25.4): a JSON object mapping
|
|
1392
|
-
* hook names to shell command templates.
|
|
1393
|
-
*/
|
|
1394
|
-
const readHooks = (path: string): Record<string, string> => {
|
|
1395
|
-
const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))
|
|
1396
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed) ||
|
|
1397
|
-
Object.values(parsed).some(value => typeof value !== 'string')) {
|
|
1398
|
-
throw new Error(`${path}: hooks must be a JSON object of name → shell template strings`)
|
|
1399
|
-
}
|
|
1400
|
-
return parsed as Record<string, string>
|
|
1415
|
+
})
|
|
1401
1416
|
}
|
|
1402
1417
|
|
|
1403
1418
|
/** Text rendering of a spec §25.2 execution report. */
|
|
@@ -1417,15 +1432,14 @@ const renderActReport = (report: ActReport): { lines: string[], code: number } =
|
|
|
1417
1432
|
]
|
|
1418
1433
|
let code = 0
|
|
1419
1434
|
if (report.hook !== undefined) {
|
|
1420
|
-
if (
|
|
1421
|
-
|
|
1422
|
-
} else if (report.hook.error === undefined) {
|
|
1423
|
-
lines.push(`hook ${report.hook.name}: ok`)
|
|
1424
|
-
} else {
|
|
1425
|
-
// The claims committed before the hook ran (spec §25.4) — the
|
|
1426
|
-
// 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.
|
|
1427
1437
|
lines.push(`hook ${report.hook.name}: ${report.hook.error}`)
|
|
1428
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`)
|
|
1429
1443
|
}
|
|
1430
1444
|
}
|
|
1431
1445
|
return { lines, code }
|
|
@@ -1456,7 +1470,7 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1456
1470
|
values.list === true ? 'read' :
|
|
1457
1471
|
values.retract !== undefined ? 'write' :
|
|
1458
1472
|
values['dry-run'] === true ? 'scratch' : 'write')
|
|
1459
|
-
|
|
1473
|
+
return withCommandStore(store, () => {
|
|
1460
1474
|
if (values.declare === true) {
|
|
1461
1475
|
const declaration = declareActions(store, readInput(positionals))
|
|
1462
1476
|
const err = declaration.problems.map(problem => `line ${problem.line}: ${problem.message}`)
|
|
@@ -1503,7 +1517,13 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1503
1517
|
if (at <= 0) {
|
|
1504
1518
|
return fail(`cave act: expected param=value, got ${JSON.stringify(pair)}\n`)
|
|
1505
1519
|
}
|
|
1506
|
-
|
|
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
|
+
})
|
|
1507
1527
|
}
|
|
1508
1528
|
const hooksPath = values.hooks ?? process.env['CAVE_HOOKS']
|
|
1509
1529
|
const report = act(store, name, args, {
|
|
@@ -1519,11 +1539,7 @@ export const actCommand = (argv: readonly string[]): Output => {
|
|
|
1519
1539
|
return report.ok ?
|
|
1520
1540
|
{ code, out: `${lines.join('\n')}\n`, err: '' } :
|
|
1521
1541
|
{ code, out: '', err: `${lines.join('\n')}\n` }
|
|
1522
|
-
}
|
|
1523
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1524
|
-
} finally {
|
|
1525
|
-
store.close()
|
|
1526
|
-
}
|
|
1542
|
+
})
|
|
1527
1543
|
}
|
|
1528
1544
|
|
|
1529
1545
|
/** Text rendering of the §20.2 report: always shape + coverage, non-empty advisory sections. */
|
|
@@ -1533,26 +1549,32 @@ const renderReport = (report: Report, staleDays: number): string => {
|
|
|
1533
1549
|
lines.push(`shape: ${coverage.expectations} expectation(s), ${coverage.instances} instance(s), ${coverage.satisfied}/${coverage.checks} satisfied`)
|
|
1534
1550
|
if (report.violations.length > 0) {
|
|
1535
1551
|
lines.push(`violations (${report.violations.length}):`)
|
|
1536
|
-
|
|
1552
|
+
for (const violation of report.violations) lines.push(` ${formatViolation(violation)}`)
|
|
1537
1553
|
}
|
|
1538
1554
|
if (report.stale.length > 0) {
|
|
1539
1555
|
lines.push(`stale (${report.stale.length}, older than ${staleDays} day(s)):`)
|
|
1540
|
-
|
|
1556
|
+
for (const { row, ageDays } of report.stale) lines.push(` ${row.raw_line} (${ageDays}d)`)
|
|
1541
1557
|
}
|
|
1542
1558
|
if (report.review.length > 0) {
|
|
1543
1559
|
lines.push(`review candidates (${report.review.length}, conf 0.3-0.7):`)
|
|
1544
|
-
|
|
1560
|
+
for (const row of report.review) lines.push(` ${row.raw_line}`)
|
|
1545
1561
|
}
|
|
1546
1562
|
if (report.disagreements.length > 0) {
|
|
1547
1563
|
lines.push(`alias disagreements (${report.disagreements.length}):`)
|
|
1548
1564
|
for (const disagreement of report.disagreements) {
|
|
1549
1565
|
lines.push(` ${disagreement.about} across ${disagreement.entities.join(', ')}:`)
|
|
1550
|
-
|
|
1566
|
+
for (const row of disagreement.rows) lines.push(` ${row.raw_line}`)
|
|
1551
1567
|
}
|
|
1552
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
|
+
}
|
|
1553
1575
|
const confidence = coverage.averageConfidence === null ?
|
|
1554
1576
|
'' :
|
|
1555
|
-
`; avg conf ${
|
|
1577
|
+
`; avg conf ${confidencePercent(coverage.averageConfidence)}, ${coverage.lowConfidence} low (< 0.3)`
|
|
1556
1578
|
lines.push(
|
|
1557
1579
|
`coverage: ${coverage.rows} row(s), ${coverage.facts} fact(s) — ` +
|
|
1558
1580
|
`${coverage.current} current, ${coverage.retracted} retracted, ${coverage.negated} negated` +
|
|
@@ -1577,16 +1599,14 @@ export const checkCommand = (argv: readonly string[]): Output => {
|
|
|
1577
1599
|
return fail(`cave check: --stale expects a non-negative number of days, got ${JSON.stringify(values.stale)}\n`)
|
|
1578
1600
|
}
|
|
1579
1601
|
const store = openDb(values, 'read')
|
|
1580
|
-
|
|
1602
|
+
return withCommandStore(store, () => {
|
|
1581
1603
|
const report = caveCheck(store, { staleDays })
|
|
1582
1604
|
const out = values.json === true ?
|
|
1583
1605
|
`${stableJson(store, report)}\n` :
|
|
1584
1606
|
renderReport(report, staleDays)
|
|
1585
1607
|
// Violations fail the check (spec §20.2); the other sections are advisory.
|
|
1586
1608
|
return { code: report.violations.length > 0 ? 1 : 0, out, err: '' }
|
|
1587
|
-
}
|
|
1588
|
-
store.close()
|
|
1589
|
-
}
|
|
1609
|
+
})
|
|
1590
1610
|
}
|
|
1591
1611
|
|
|
1592
1612
|
const snapshotLine = (
|
|
@@ -1630,17 +1650,16 @@ export const backupCommand = (argv: readonly string[]): Output => {
|
|
|
1630
1650
|
// the destination aliases the source: refuse before the file could be
|
|
1631
1651
|
// replaced by a SQLite snapshot of itself (a SQLite source is covered by
|
|
1632
1652
|
// the API's own check).
|
|
1633
|
-
if (
|
|
1653
|
+
if (sameStoreFile(dbPath, values.out)) {
|
|
1634
1654
|
return fail(`cave backup: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1635
1655
|
}
|
|
1656
|
+
const destination = values.out
|
|
1636
1657
|
const store = openAt(dbPath, { intent: 'read', assemble: assembleSources })
|
|
1637
|
-
|
|
1638
|
-
return ok(snapshotLine('created', backupStore(store,
|
|
1639
|
-
}
|
|
1640
|
-
store.close()
|
|
1641
|
-
}
|
|
1658
|
+
return withCommandStore(store, () => {
|
|
1659
|
+
return ok(snapshotLine('created', backupStore(store, destination, { force: values.force === true })))
|
|
1660
|
+
})
|
|
1642
1661
|
} catch (error) {
|
|
1643
|
-
return fail(`${
|
|
1662
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1644
1663
|
}
|
|
1645
1664
|
}
|
|
1646
1665
|
|
|
@@ -1670,7 +1689,7 @@ export const restoreCommand = (argv: readonly string[]): Output => {
|
|
|
1670
1689
|
...values.sha256 === undefined ? {} : { expectedSha256: values.sha256 }
|
|
1671
1690
|
})))
|
|
1672
1691
|
} catch (error) {
|
|
1673
|
-
return fail(`${
|
|
1692
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1674
1693
|
}
|
|
1675
1694
|
}
|
|
1676
1695
|
|
|
@@ -1709,15 +1728,15 @@ export const suggestAliasCommand = async (
|
|
|
1709
1728
|
return fail(`cave suggest-alias: --min expects a score in 0..1 or N%, got ${JSON.stringify(values.min)}\n`)
|
|
1710
1729
|
}
|
|
1711
1730
|
const limit = values.limit === undefined ? undefined : Number(values.limit)
|
|
1712
|
-
if (limit !== undefined && (!Number.
|
|
1713
|
-
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`)
|
|
1714
1733
|
}
|
|
1715
1734
|
const timeoutSeconds = values.timeout === undefined ? undefined : Number(values.timeout)
|
|
1716
1735
|
if (timeoutSeconds !== undefined && (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0)) {
|
|
1717
1736
|
return fail(`cave suggest-alias: --timeout must be a positive number of seconds, got '${values.timeout}'\n`)
|
|
1718
1737
|
}
|
|
1719
1738
|
const store = openDb(values, values.write === true ? 'write' : 'read')
|
|
1720
|
-
|
|
1739
|
+
return await withAsyncCommandStore(store, async () => {
|
|
1721
1740
|
let suggestions = suggestAliases(store, { minScore, ...limit === undefined ? {} : { limit } })
|
|
1722
1741
|
if (values.agent !== undefined && suggestions.length > 0) {
|
|
1723
1742
|
const reply = await shellComplete(values.agent, {
|
|
@@ -1729,7 +1748,9 @@ export const suggestAliasCommand = async (
|
|
|
1729
1748
|
suggestions = parseJudgeReply(reply, suggestions.length).map(index => suggestions[index]!)
|
|
1730
1749
|
}
|
|
1731
1750
|
if (values.json === true) {
|
|
1732
|
-
|
|
1751
|
+
const result = values.write === true ?
|
|
1752
|
+
{ suggestions, ...writeSuggestions(store, suggestions) } : suggestions
|
|
1753
|
+
return ok(`${JSON.stringify(result, undefined, 2)}\n`)
|
|
1733
1754
|
}
|
|
1734
1755
|
if (suggestions.length === 0) {
|
|
1735
1756
|
return ok('no alias suggestions\n')
|
|
@@ -1740,11 +1761,9 @@ export const suggestAliasCommand = async (
|
|
|
1740
1761
|
return ok(`${lines.join('\n')}\nappended ${appended} suggested alias claim(s)\n`)
|
|
1741
1762
|
}
|
|
1742
1763
|
return ok(`${lines.join('\n')}\n`)
|
|
1743
|
-
}
|
|
1744
|
-
store.close()
|
|
1745
|
-
}
|
|
1764
|
+
})
|
|
1746
1765
|
} catch (error) {
|
|
1747
|
-
return fail(`${
|
|
1766
|
+
return fail(`${errorMessage(error)}\n`)
|
|
1748
1767
|
}
|
|
1749
1768
|
}
|
|
1750
1769
|
|
|
@@ -1774,7 +1793,7 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1774
1793
|
}
|
|
1775
1794
|
const dbPath = values.db ?? defaultDbPath()
|
|
1776
1795
|
const store = openDb(values, values['dry-run'] === true ? 'scratch' : 'write')
|
|
1777
|
-
|
|
1796
|
+
return withCommandStore(store, () => {
|
|
1778
1797
|
const options = {
|
|
1779
1798
|
into: values.into === undefined ? labelOf(dbPath) : sanitizeLabel(values.into),
|
|
1780
1799
|
record: values['no-record'] !== true,
|
|
@@ -1788,7 +1807,7 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1788
1807
|
return { code: report.problems.length > 0 ? 1 : 0, out: `${JSON.stringify(report, undefined, 2)}\n`, err: '' }
|
|
1789
1808
|
}
|
|
1790
1809
|
if (report.problems.length > 0) {
|
|
1791
|
-
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')
|
|
1792
1811
|
return fail(`cave sync: ${source}: ${report.problems.length} problem(s), nothing merged\n${detail}\n`)
|
|
1793
1812
|
}
|
|
1794
1813
|
const lines = [
|
|
@@ -1800,17 +1819,14 @@ export const syncCommand = (argv: readonly string[]): Output => {
|
|
|
1800
1819
|
lines.push(`record: ${report.record}`)
|
|
1801
1820
|
}
|
|
1802
1821
|
return ok(`${lines.join('\n')}\n`)
|
|
1803
|
-
}
|
|
1804
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1805
|
-
} finally {
|
|
1806
|
-
store.close()
|
|
1807
|
-
}
|
|
1822
|
+
})
|
|
1808
1823
|
}
|
|
1809
1824
|
|
|
1810
1825
|
/**
|
|
1811
1826
|
* `cave report` — the §31 deliverable: a markdown template's cave-q
|
|
1812
|
-
* blocks and inline splices render from the store,
|
|
1813
|
-
* 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
|
|
1814
1830
|
* template line numbers, and fail the exit code — the render never
|
|
1815
1831
|
* silently drops a fact.
|
|
1816
1832
|
*/
|
|
@@ -1829,16 +1845,22 @@ export const reportCommand = (argv: readonly string[]): Output => {
|
|
|
1829
1845
|
},
|
|
1830
1846
|
allowPositionals: true
|
|
1831
1847
|
})
|
|
1832
|
-
|
|
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
|
+
}
|
|
1833
1854
|
const maximum = Sensitivity.parse(values['max-sensitivity'] ?? Sensitivity.defaultMaximum)
|
|
1834
1855
|
if (maximum === undefined) {
|
|
1835
1856
|
return fail(`cave report: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`)
|
|
1836
1857
|
}
|
|
1837
|
-
if (values.out !== undefined &&
|
|
1858
|
+
if (values.out !== undefined && sameStoreFile(values.db ?? defaultDbPath(), values.out)) {
|
|
1838
1859
|
return fail(`cave report: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1839
1860
|
}
|
|
1861
|
+
const template = readInput(positionals)
|
|
1840
1862
|
const store = openDb(values, 'read')
|
|
1841
|
-
|
|
1863
|
+
return withCommandStore(store, () => {
|
|
1842
1864
|
const rendered = caveReport(store, template, {
|
|
1843
1865
|
aliases: values.aliases === true,
|
|
1844
1866
|
resolve: values.resolve === true,
|
|
@@ -1853,17 +1875,13 @@ export const reportCommand = (argv: readonly string[]): Output => {
|
|
|
1853
1875
|
if (values.out === undefined) {
|
|
1854
1876
|
return { code, out: rendered.markdown, err: err === '' ? '' : `${err}\n` }
|
|
1855
1877
|
}
|
|
1856
|
-
|
|
1878
|
+
writeOutput(values.out, rendered.markdown)
|
|
1857
1879
|
return {
|
|
1858
1880
|
code,
|
|
1859
1881
|
out: `rendered ${rendered.citations} citation(s) to ${values.out}\n`,
|
|
1860
1882
|
err: err === '' ? '' : `${err}\n`
|
|
1861
1883
|
}
|
|
1862
|
-
}
|
|
1863
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1864
|
-
} finally {
|
|
1865
|
-
store.close()
|
|
1866
|
-
}
|
|
1884
|
+
})
|
|
1867
1885
|
}
|
|
1868
1886
|
|
|
1869
1887
|
/**
|
|
@@ -1884,6 +1902,21 @@ const sameFile = (a: string, b: string): boolean => {
|
|
|
1884
1902
|
}
|
|
1885
1903
|
}
|
|
1886
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
|
+
|
|
1887
1920
|
export const exportCommand = (argv: readonly string[]): Output => {
|
|
1888
1921
|
const { values } = parseArgs({
|
|
1889
1922
|
args: [...argv],
|
|
@@ -1902,26 +1935,22 @@ export const exportCommand = (argv: readonly string[]): Output => {
|
|
|
1902
1935
|
if (maximum === undefined) {
|
|
1903
1936
|
return fail(`cave export: --max-sensitivity expects ${Sensitivity.levels.join(', ')}, got ${JSON.stringify(values['max-sensitivity'])}\n`)
|
|
1904
1937
|
}
|
|
1905
|
-
if (values.out !== undefined &&
|
|
1938
|
+
if (values.out !== undefined && sameStoreFile(db, values.out)) {
|
|
1906
1939
|
return fail(`cave export: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1907
1940
|
}
|
|
1908
1941
|
const store = openDb(values, 'read')
|
|
1909
|
-
|
|
1942
|
+
return withCommandStore(store, () => {
|
|
1910
1943
|
const text = store.exportText({ current: values.current === true, tx: values.tx === true, maxSensitivity: maximum })
|
|
1911
1944
|
if (values.out === undefined) {
|
|
1912
1945
|
return ok(text)
|
|
1913
1946
|
}
|
|
1914
|
-
|
|
1947
|
+
writeOutput(values.out, text)
|
|
1915
1948
|
// Root claims only: shorthand prefixes and §28.4 annotations are not
|
|
1916
1949
|
// claims, while qualifier/grouping leaves have a materialized parent.
|
|
1917
1950
|
const claims = parseDocument(text).lines
|
|
1918
1951
|
.filter(line => line.kind === 'claim' && line.parent === undefined).length
|
|
1919
1952
|
return ok(`exported ${claims} claim(s) to ${values.out}\n`)
|
|
1920
|
-
}
|
|
1921
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1922
|
-
} finally {
|
|
1923
|
-
store.close()
|
|
1924
|
-
}
|
|
1953
|
+
})
|
|
1925
1954
|
}
|
|
1926
1955
|
|
|
1927
1956
|
/** `cave generate` — spec §20.4's versioned TypeScript client artifact. */
|
|
@@ -1941,11 +1970,11 @@ export const generateCommand = (argv: readonly string[]): Output => {
|
|
|
1941
1970
|
return fail(`cave generate: --version expects a positive integer, got ${JSON.stringify(values.version)}\n`)
|
|
1942
1971
|
}
|
|
1943
1972
|
const db = values.db ?? defaultDbPath()
|
|
1944
|
-
if (values.out !== undefined &&
|
|
1973
|
+
if (values.out !== undefined && sameStoreFile(db, values.out)) {
|
|
1945
1974
|
return fail(`cave generate: --out '${values.out}' is the source database — refusing to overwrite it\n`)
|
|
1946
1975
|
}
|
|
1947
1976
|
const store = openDb(values, 'read')
|
|
1948
|
-
|
|
1977
|
+
return withCommandStore(store, () => {
|
|
1949
1978
|
const generated = generateClient(store, version === undefined ? {} : { version })
|
|
1950
1979
|
if (!generated.ok) {
|
|
1951
1980
|
return fail(`cave generate: schema cannot be generated:\n${generated.problems.map(problem => ` ${problem}`).join('\n')}\n`)
|
|
@@ -1953,14 +1982,10 @@ export const generateCommand = (argv: readonly string[]): Output => {
|
|
|
1953
1982
|
if (values.out === undefined) {
|
|
1954
1983
|
return ok(generated.code)
|
|
1955
1984
|
}
|
|
1956
|
-
|
|
1985
|
+
writeOutput(values.out, generated.code)
|
|
1957
1986
|
return ok(`generated typed client v${generated.version} (${generated.fields.length} field(s), ` +
|
|
1958
1987
|
`sha256:${generated.digest}) to ${values.out}\n`)
|
|
1959
|
-
}
|
|
1960
|
-
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
1961
|
-
} finally {
|
|
1962
|
-
store.close()
|
|
1963
|
-
}
|
|
1988
|
+
})
|
|
1964
1989
|
}
|
|
1965
1990
|
|
|
1966
1991
|
/**
|
|
@@ -2010,7 +2035,7 @@ export const reconstructCommand = async (
|
|
|
2010
2035
|
return fail(`cave reconstruct: --timeout must be a positive number of seconds, got '${values.timeout}'\n`)
|
|
2011
2036
|
}
|
|
2012
2037
|
const store = openDb(values, 'read')
|
|
2013
|
-
|
|
2038
|
+
return await withAsyncCommandStore(store, async () => {
|
|
2014
2039
|
const graph = sqliteStore(store)
|
|
2015
2040
|
const result = values.agent === undefined ?
|
|
2016
2041
|
reconstruct(graph, heuristicPolicy(budgets), positionals) :
|
|
@@ -2034,11 +2059,9 @@ export const reconstructCommand = async (
|
|
|
2034
2059
|
...result.claims.map(claim => emitClaim(claim))
|
|
2035
2060
|
]
|
|
2036
2061
|
return ok(lines.length === 0 ? '' : `${lines.join('\n')}\n`)
|
|
2037
|
-
}
|
|
2038
|
-
store.close()
|
|
2039
|
-
}
|
|
2062
|
+
})
|
|
2040
2063
|
} catch (error) {
|
|
2041
|
-
return fail(`${
|
|
2064
|
+
return fail(`${errorMessage(error)}\n`)
|
|
2042
2065
|
}
|
|
2043
2066
|
}
|
|
2044
2067
|
|