@cavelang/cli 0.1.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 +56 -0
- package/dist/src/cli.d.ts +40 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +218 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +6 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/main.d.ts +4 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +25 -0
- package/dist/src/main.js.map +1 -0
- package/package.json +56 -0
- package/src/cli.ts +234 -0
- package/src/index.ts +7 -0
- package/src/main.ts +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @cavelang/cli
|
|
2
|
+
|
|
3
|
+
The `cave` command — the whole stack behind one binary. Runs directly from
|
|
4
|
+
TypeScript sources via Node's type stripping; no build step.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
$ echo 'auth USES jwt @ 90%' | pnpm exec cave parse
|
|
8
|
+
ok: 1 claim
|
|
9
|
+
|
|
10
|
+
$ pnpm exec cave add notes.cave --db knowledge.db
|
|
11
|
+
added 12 claim(s), 3 edge(s)
|
|
12
|
+
|
|
13
|
+
$ pnpm exec cave query '?x USES jwt' --db knowledge.db
|
|
14
|
+
?x = auth/middleware
|
|
15
|
+
?x = api/gateway
|
|
16
|
+
|
|
17
|
+
$ pnpm exec cave query '?cause CAUSE app/crash' 'WHERE conf >= 0.7' --db knowledge.db
|
|
18
|
+
$ pnpm exec cave export --db knowledge.db --current
|
|
19
|
+
$ pnpm exec cave demo
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
| Command | Flags | Behavior |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `parse [file]` | `--json` | Lint (stdin by default). Exit 1 when diagnostics exist; `--json` dumps the AST document. |
|
|
27
|
+
| `add [file…] --db p` | `--strict`, `--no-prelude` | Ingest. Lenient by default (problems on stderr, valid lines land); `--strict` rolls back on any problem; `--no-prelude` starts from an empty registry instead of the standard §5.5 pairs. |
|
|
28
|
+
| `import [file…] --db p` | `--strict`, `--no-prelude` | Restore/merge a database from CAVE text — same operation as `add`, because canonical text *is* the interchange format. |
|
|
29
|
+
| `query <pattern…> --db p` | `--json`, `--all`, `--no-prelude` | CAVE-Q. Extra positionals join as lines, so `WHERE` filters ride as separate arguments. Bindings print as `?x = value`; fully bound patterns print the matched raw line (or the pattern itself for transitive matches, which carry no row). `--no-prelude` aligns the read-time registry with a store written via `add --no-prelude`. |
|
|
30
|
+
| `export --db p` | `--out <file>`, `--current`, `--no-prelude` | Canonical CAVE text — all rows in tx order, or current beliefs only. Stdout by default; `--out` writes a file and reports the claim count. |
|
|
31
|
+
| `mcp --db p` | `--no-prelude` | Serve the engine as an MCP server on stdio (see [`@cavelang/mcp`](../mcp)) — tools for add/query/search/about/neighbors/reconstruct/export/lint, with the §22 spec card as server instructions. |
|
|
32
|
+
| `ingest <globs…> --db p` | see `cave ingest --help` | LLM-driven ingestion of files through any headless agent (see [`@cavelang/ingest`](../ingest)): batching, instructions markdown, hybrid knowledge context, MCP or stdout agents, incremental digests, `--plan` NDJSON for SDK drivers. |
|
|
33
|
+
| `demo` | | The cave-loop multi-hop recovery demo (§18). |
|
|
34
|
+
|
|
35
|
+
## Text backup / interchange
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
$ cave export --db knowledge.db --out backup.cave
|
|
39
|
+
exported 812 claim(s) to backup.cave
|
|
40
|
+
|
|
41
|
+
$ cave import backup.cave --db restored.db
|
|
42
|
+
added 812 claim(s), 37 edge(s)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The text round trip preserves every claim with its metadata, the **full
|
|
46
|
+
belief-series order** (rows export in tx order and re-ingest with fresh
|
|
47
|
+
monotonic tx ids, so latest-tx-wins resolution is unchanged),
|
|
48
|
+
qualifier/grouping edges, and in-band registry declarations (`REVERSE`,
|
|
49
|
+
`X IS verb`) — a restored database answers queries identically, inverse
|
|
50
|
+
reads included. Original transaction timestamps are re-minted: canonical
|
|
51
|
+
CAVE text carries no transaction identity. Use `--current` for a compact
|
|
52
|
+
backup of current beliefs only (history intentionally dropped).
|
|
53
|
+
|
|
54
|
+
Everything is testable without spawning: each command is a pure function
|
|
55
|
+
`(argv) → { code, out, err }` (`@cavelang/cli` exports them), and `main.ts` is
|
|
56
|
+
a four-line dispatcher. Tests cover both layers.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cave` command implementation — pure functions from parsed arguments to
|
|
3
|
+
* `{ code, out, err }`, so tests can call commands directly and `main.ts`
|
|
4
|
+
* stays a thin dispatcher.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
*
|
|
8
|
+
* - `cave parse [file]` — lint / dump the AST (`--json`)
|
|
9
|
+
* - `cave add [file…] --db <path>` — ingest into a store (`--strict`)
|
|
10
|
+
* - `cave query '<pattern>' --db <path>` — CAVE-Q (`--json`, `--all`)
|
|
11
|
+
* - `cave export --db <path>` — canonical text out (`--current`)
|
|
12
|
+
* - `cave demo` — the cave-loop multi-hop recovery demo
|
|
13
|
+
*
|
|
14
|
+
* `file` defaults to stdin (`-`).
|
|
15
|
+
*/
|
|
16
|
+
export type Output = {
|
|
17
|
+
readonly code: number;
|
|
18
|
+
readonly out: string;
|
|
19
|
+
readonly err: string;
|
|
20
|
+
};
|
|
21
|
+
export declare const usage = "cave \u2014 Compressed Atomic Verb Expressions\n\nUsage:\n cave parse [file] [--json] lint CAVE text (stdin when no file)\n cave add [file...] --db <path> ingest into a store [--strict] [--no-prelude]\n cave import [file...] --db <path> restore/merge a database from CAVE text (same as add)\n cave query <pattern> --db <path> run a CAVE-Q pattern [--json] [--all] [--no-prelude]\n cave export --db <path> [--out <file>] emit canonical CAVE text [--current] [--no-prelude]\n cave mcp --db <path> serve the engine as an MCP server on stdio [--no-prelude]\n cave ingest <globs...> --db <path> LLM-driven ingestion of files (cave ingest --help)\n cave demo run the cave-loop reconstruction demo\n cave help this text\n\nThe spec lives in the .claude/skills/ directory at the repository root\n(section index in README.md).";
|
|
22
|
+
export declare const parseCommand: (argv: readonly string[]) => Output;
|
|
23
|
+
export declare const addCommand: (argv: readonly string[]) => Output;
|
|
24
|
+
/**
|
|
25
|
+
* `cave import` — restore/merge a knowledge database from CAVE text files.
|
|
26
|
+
* Identical to `add` by design: canonical CAVE text *is* the interchange
|
|
27
|
+
* format (spec §2.2), so importing a file exported with `cave export`
|
|
28
|
+
* replays its claims append-only. What the text round trip preserves:
|
|
29
|
+
* every claim with metadata, full belief-series order (rows export in tx
|
|
30
|
+
* order and re-ingest with fresh monotonic tx ids), qualifier/grouping
|
|
31
|
+
* edges, and in-band registry declarations. Original tx timestamps are
|
|
32
|
+
* re-minted — the text format carries no transaction identity.
|
|
33
|
+
*/
|
|
34
|
+
export declare const importCommand: (argv: readonly string[]) => Output;
|
|
35
|
+
export declare const queryCommand: (argv: readonly string[]) => Output;
|
|
36
|
+
export declare const exportCommand: (argv: readonly string[]) => Output;
|
|
37
|
+
export declare const demoCommand: () => Output;
|
|
38
|
+
/** Dispatches one invocation. */
|
|
39
|
+
export declare const cave: (argv: readonly string[]) => Output;
|
|
40
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAA;AAQD,eAAO,MAAM,KAAK,86BAcY,CAAA;AAO9B,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAyBtD,CAAA;AAkCD,eAAO,MAAM,UAAU,SAhCwB,SAAS,MAAM,EAAE,KAAG,MAgCrB,CAAA;AAE9C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,SA5CqB,SAAS,MAAM,EAAE,KAAG,MA4Cf,CAAA;AAEpD,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAyCtD,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA0BvD,CAAA;AAED,eAAO,MAAM,WAAW,QAAO,MACS,CAAA;AAExC,iCAAiC;AACjC,eAAO,MAAM,IAAI,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MA4B9C,CAAA"}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cave` command implementation — pure functions from parsed arguments to
|
|
3
|
+
* `{ code, out, err }`, so tests can call commands directly and `main.ts`
|
|
4
|
+
* stays a thin dispatcher.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
*
|
|
8
|
+
* - `cave parse [file]` — lint / dump the AST (`--json`)
|
|
9
|
+
* - `cave add [file…] --db <path>` — ingest into a store (`--strict`)
|
|
10
|
+
* - `cave query '<pattern>' --db <path>` — CAVE-Q (`--json`, `--all`)
|
|
11
|
+
* - `cave export --db <path>` — canonical text out (`--current`)
|
|
12
|
+
* - `cave demo` — the cave-loop multi-hop recovery demo
|
|
13
|
+
*
|
|
14
|
+
* `file` defaults to stdin (`-`).
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { parseArgs } from 'node:util';
|
|
18
|
+
import { parseDocument } from '@cavelang/parser';
|
|
19
|
+
import { Registry, standardRegistry } from '@cavelang/canonical';
|
|
20
|
+
import { open } from '@cavelang/store';
|
|
21
|
+
import { query as caveQuery } from '@cavelang/query';
|
|
22
|
+
import { Demo } from '@cavelang/loop';
|
|
23
|
+
const ok = (out) => ({ code: 0, out, err: '' });
|
|
24
|
+
const fail = (err, code = 1) => ({ code, out: '', err });
|
|
25
|
+
export const usage = `cave — Compressed Atomic Verb Expressions
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
cave parse [file] [--json] lint CAVE text (stdin when no file)
|
|
29
|
+
cave add [file...] --db <path> ingest into a store [--strict] [--no-prelude]
|
|
30
|
+
cave import [file...] --db <path> restore/merge a database from CAVE text (same as add)
|
|
31
|
+
cave query <pattern> --db <path> run a CAVE-Q pattern [--json] [--all] [--no-prelude]
|
|
32
|
+
cave export --db <path> [--out <file>] emit canonical CAVE text [--current] [--no-prelude]
|
|
33
|
+
cave mcp --db <path> serve the engine as an MCP server on stdio [--no-prelude]
|
|
34
|
+
cave ingest <globs...> --db <path> LLM-driven ingestion of files (cave ingest --help)
|
|
35
|
+
cave demo run the cave-loop reconstruction demo
|
|
36
|
+
cave help this text
|
|
37
|
+
|
|
38
|
+
The spec lives in the .claude/skills/ directory at the repository root
|
|
39
|
+
(section index in README.md).`;
|
|
40
|
+
const readInput = (files) => files.length === 0 || (files.length === 1 && files[0] === '-') ?
|
|
41
|
+
readFileSync(0, 'utf8') :
|
|
42
|
+
files.map(file => readFileSync(file, 'utf8')).join('\n');
|
|
43
|
+
export const parseCommand = (argv) => {
|
|
44
|
+
const { values, positionals } = parseArgs({
|
|
45
|
+
args: [...argv],
|
|
46
|
+
options: { json: { type: 'boolean' } },
|
|
47
|
+
allowPositionals: true
|
|
48
|
+
});
|
|
49
|
+
const input = readInput(positionals);
|
|
50
|
+
const document = parseDocument(input);
|
|
51
|
+
if (values.json === true) {
|
|
52
|
+
return ok(`${JSON.stringify(document, undefined, 2)}\n`);
|
|
53
|
+
}
|
|
54
|
+
const counts = new Map();
|
|
55
|
+
for (const line of document.lines) {
|
|
56
|
+
counts.set(line.kind, (counts.get(line.kind) ?? 0) + 1);
|
|
57
|
+
}
|
|
58
|
+
const summary = [...counts]
|
|
59
|
+
.map(([kind, count]) => `${count} ${kind}`)
|
|
60
|
+
.join(', ');
|
|
61
|
+
if (document.diagnostics.length === 0) {
|
|
62
|
+
return ok(`ok: ${summary}\n`);
|
|
63
|
+
}
|
|
64
|
+
const problems = document.diagnostics
|
|
65
|
+
.map(diagnostic => `line ${diagnostic.line}: ${diagnostic.message}`)
|
|
66
|
+
.join('\n');
|
|
67
|
+
return { code: 1, out: `${summary}\n`, err: `${problems}\n` };
|
|
68
|
+
};
|
|
69
|
+
const ingestCommand = (name) => (argv) => {
|
|
70
|
+
const { values, positionals } = parseArgs({
|
|
71
|
+
args: [...argv],
|
|
72
|
+
options: {
|
|
73
|
+
db: { type: 'string' },
|
|
74
|
+
strict: { type: 'boolean' },
|
|
75
|
+
'no-prelude': { type: 'boolean' }
|
|
76
|
+
},
|
|
77
|
+
allowPositionals: true
|
|
78
|
+
});
|
|
79
|
+
if (values.db === undefined) {
|
|
80
|
+
return fail(`cave ${name}: --db <path> is required\n`);
|
|
81
|
+
}
|
|
82
|
+
const input = readInput(positionals);
|
|
83
|
+
const store = open(values.db, values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
84
|
+
try {
|
|
85
|
+
const result = store.ingest(input, { strict: values.strict === true });
|
|
86
|
+
const problems = result.problems
|
|
87
|
+
.map(problem => `line ${problem.line}: ${problem.message}`)
|
|
88
|
+
.join('\n');
|
|
89
|
+
return {
|
|
90
|
+
code: 0,
|
|
91
|
+
out: `added ${result.ids.length} claim(s), ${result.edges} edge(s)\n`,
|
|
92
|
+
err: problems === '' ? '' : `${problems}\n`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
store.close();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
export const addCommand = ingestCommand('add');
|
|
103
|
+
/**
|
|
104
|
+
* `cave import` — restore/merge a knowledge database from CAVE text files.
|
|
105
|
+
* Identical to `add` by design: canonical CAVE text *is* the interchange
|
|
106
|
+
* format (spec §2.2), so importing a file exported with `cave export`
|
|
107
|
+
* replays its claims append-only. What the text round trip preserves:
|
|
108
|
+
* every claim with metadata, full belief-series order (rows export in tx
|
|
109
|
+
* order and re-ingest with fresh monotonic tx ids), qualifier/grouping
|
|
110
|
+
* edges, and in-band registry declarations. Original tx timestamps are
|
|
111
|
+
* re-minted — the text format carries no transaction identity.
|
|
112
|
+
*/
|
|
113
|
+
export const importCommand = ingestCommand('import');
|
|
114
|
+
export const queryCommand = (argv) => {
|
|
115
|
+
const { values, positionals } = parseArgs({
|
|
116
|
+
args: [...argv],
|
|
117
|
+
options: {
|
|
118
|
+
db: { type: 'string' },
|
|
119
|
+
json: { type: 'boolean' },
|
|
120
|
+
all: { type: 'boolean' },
|
|
121
|
+
'no-prelude': { type: 'boolean' }
|
|
122
|
+
},
|
|
123
|
+
allowPositionals: true
|
|
124
|
+
});
|
|
125
|
+
if (values.db === undefined) {
|
|
126
|
+
return fail('cave query: --db <path> is required\n');
|
|
127
|
+
}
|
|
128
|
+
if (positionals.length === 0) {
|
|
129
|
+
return fail('cave query: a pattern is required (spec §12.1)\n');
|
|
130
|
+
}
|
|
131
|
+
const pattern = positionals.join('\n');
|
|
132
|
+
const store = open(values.db, values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
133
|
+
try {
|
|
134
|
+
const matches = caveQuery(store, pattern, { all: values.all === true });
|
|
135
|
+
if (values.json === true) {
|
|
136
|
+
return ok(`${JSON.stringify(matches, undefined, 2)}\n`);
|
|
137
|
+
}
|
|
138
|
+
if (matches.length === 0) {
|
|
139
|
+
return ok('no matches\n');
|
|
140
|
+
}
|
|
141
|
+
const lines = matches.map(match => {
|
|
142
|
+
const bindings = Object.entries(match.bindings)
|
|
143
|
+
.map(([name, value]) => `?${name} = ${value}`)
|
|
144
|
+
.join(' ');
|
|
145
|
+
// A fully bound pattern has no bindings to print; transitive matches
|
|
146
|
+
// additionally carry no row — confirm with the pattern itself.
|
|
147
|
+
return bindings !== '' ? bindings : match.row?.raw_line ?? pattern.split('\n')[0];
|
|
148
|
+
});
|
|
149
|
+
return ok(`${lines.join('\n')}\n`);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
store.close();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
export const exportCommand = (argv) => {
|
|
159
|
+
const { values } = parseArgs({
|
|
160
|
+
args: [...argv],
|
|
161
|
+
options: {
|
|
162
|
+
db: { type: 'string' },
|
|
163
|
+
current: { type: 'boolean' },
|
|
164
|
+
out: { type: 'string' },
|
|
165
|
+
'no-prelude': { type: 'boolean' }
|
|
166
|
+
},
|
|
167
|
+
allowPositionals: false
|
|
168
|
+
});
|
|
169
|
+
if (values.db === undefined) {
|
|
170
|
+
return fail('cave export: --db <path> is required\n');
|
|
171
|
+
}
|
|
172
|
+
const store = open(values.db, values['no-prelude'] === true ? { registry: Registry.empty } : {});
|
|
173
|
+
try {
|
|
174
|
+
const text = store.exportText({ current: values.current === true });
|
|
175
|
+
if (values.out === undefined) {
|
|
176
|
+
return ok(text);
|
|
177
|
+
}
|
|
178
|
+
writeFileSync(values.out, text);
|
|
179
|
+
const claims = text === '' ? 0 : text.trimEnd().split('\n').length;
|
|
180
|
+
return ok(`exported ${claims} claim(s) to ${values.out}\n`);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
store.close();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
export const demoCommand = () => ok(`${Demo.run().lines.join('\n')}\n`);
|
|
187
|
+
/** Dispatches one invocation. */
|
|
188
|
+
export const cave = (argv) => {
|
|
189
|
+
const [command, ...rest] = argv;
|
|
190
|
+
try {
|
|
191
|
+
switch (command) {
|
|
192
|
+
case 'parse':
|
|
193
|
+
return parseCommand(rest);
|
|
194
|
+
case 'add':
|
|
195
|
+
return addCommand(rest);
|
|
196
|
+
case 'import':
|
|
197
|
+
return importCommand(rest);
|
|
198
|
+
case 'query':
|
|
199
|
+
case 'q':
|
|
200
|
+
return queryCommand(rest);
|
|
201
|
+
case 'export':
|
|
202
|
+
return exportCommand(rest);
|
|
203
|
+
case 'demo':
|
|
204
|
+
return demoCommand();
|
|
205
|
+
case undefined:
|
|
206
|
+
case 'help':
|
|
207
|
+
case '--help':
|
|
208
|
+
case '-h':
|
|
209
|
+
return ok(`${usage}\n`);
|
|
210
|
+
default:
|
|
211
|
+
return fail(`cave: unknown command ${JSON.stringify(command)}\n\n${usage}\n`, 2);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cavelang/cli` — the `cave` command (parse / add / query / export / demo).
|
|
3
|
+
* Programmatic access to the same implementations lives here.
|
|
4
|
+
*/
|
|
5
|
+
export { addCommand, cave, demoCommand, exportCommand, importCommand, parseCommand, queryCommand, usage } from './cli.ts';
|
|
6
|
+
export type { Output } from './cli.ts';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AACzH,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cavelang/cli` — the `cave` command (parse / add / query / export / demo).
|
|
3
|
+
* Programmatic access to the same implementations lives here.
|
|
4
|
+
*/
|
|
5
|
+
export { addCommand, cave, demoCommand, exportCommand, importCommand, parseCommand, queryCommand, usage } from "./cli.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":";AACA,gFAAgF"}
|
package/dist/src/main.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** `cave` binary entry point — see `cli.ts` for the command implementations. */
|
|
3
|
+
import { cave } from "./cli.js";
|
|
4
|
+
const argv = process.argv.slice(2);
|
|
5
|
+
if (argv[0] === 'mcp') {
|
|
6
|
+
// Long-running: serves MCP on stdio until the client disconnects.
|
|
7
|
+
const { runMcp } = await import('@cavelang/mcp');
|
|
8
|
+
process.exitCode = await runMcp(argv.slice(1));
|
|
9
|
+
}
|
|
10
|
+
else if (argv[0] === 'ingest') {
|
|
11
|
+
// Long-running: drives an LLM agent over batches of files.
|
|
12
|
+
const { runIngest } = await import('@cavelang/ingest');
|
|
13
|
+
process.exitCode = await runIngest(argv.slice(1));
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const { code, out, err } = cave(argv);
|
|
17
|
+
if (out !== '') {
|
|
18
|
+
process.stdout.write(out);
|
|
19
|
+
}
|
|
20
|
+
if (err !== '') {
|
|
21
|
+
process.stderr.write(err);
|
|
22
|
+
}
|
|
23
|
+
process.exitCode = code;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":";AACA,gFAAgF;AAEhF,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAE/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;IACtB,kEAAkE;IAClE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;IAChD,OAAO,CAAC,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AAChD,CAAC;KAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;IAChC,2DAA2D;IAC3D,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;IACtD,OAAO,CAAC,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AACnD,CAAC;KAAM,CAAC;IACN,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAA;IACrC,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;IACD,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3B,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA;AACzB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cavelang/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "cave — CLI for parsing, storing, querying and exporting CAVE knowledge.",
|
|
6
|
+
"license": "CC0-1.0",
|
|
7
|
+
"author": "Mirek Rusin (https://github.com/mirek)",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/mirek/cave.git",
|
|
11
|
+
"directory": "packages/cli"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22.18"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"cave": "./dist/src/main.js"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/src/index.d.ts",
|
|
22
|
+
"default": "./dist/src/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./main": {
|
|
25
|
+
"types": "./dist/src/main.d.ts",
|
|
26
|
+
"default": "./dist/src/main.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"!dist/test",
|
|
32
|
+
"!dist/tsconfig.tsbuildinfo",
|
|
33
|
+
"src",
|
|
34
|
+
"!src/**/*.test.ts",
|
|
35
|
+
"README.md",
|
|
36
|
+
"License.md"
|
|
37
|
+
],
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@cavelang/canonical": "0.1.0",
|
|
43
|
+
"@cavelang/loop": "0.1.0",
|
|
44
|
+
"@cavelang/core": "0.1.0",
|
|
45
|
+
"@cavelang/query": "0.1.0",
|
|
46
|
+
"@cavelang/parser": "0.1.0",
|
|
47
|
+
"@cavelang/store": "0.1.0",
|
|
48
|
+
"@cavelang/mcp": "0.1.0",
|
|
49
|
+
"@cavelang/ingest": "0.1.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -b",
|
|
53
|
+
"test": "node --disable-warning=ExperimentalWarning --test 'test/*.test.ts'",
|
|
54
|
+
"typecheck": "tsc -b"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cave` command implementation — pure functions from parsed arguments to
|
|
3
|
+
* `{ code, out, err }`, so tests can call commands directly and `main.ts`
|
|
4
|
+
* stays a thin dispatcher.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
*
|
|
8
|
+
* - `cave parse [file]` — lint / dump the AST (`--json`)
|
|
9
|
+
* - `cave add [file…] --db <path>` — ingest into a store (`--strict`)
|
|
10
|
+
* - `cave query '<pattern>' --db <path>` — CAVE-Q (`--json`, `--all`)
|
|
11
|
+
* - `cave export --db <path>` — canonical text out (`--current`)
|
|
12
|
+
* - `cave demo` — the cave-loop multi-hop recovery demo
|
|
13
|
+
*
|
|
14
|
+
* `file` defaults to stdin (`-`).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
18
|
+
import { parseArgs } from 'node:util'
|
|
19
|
+
import { parseDocument } from '@cavelang/parser'
|
|
20
|
+
import { Registry, standardRegistry } from '@cavelang/canonical'
|
|
21
|
+
import { open } from '@cavelang/store'
|
|
22
|
+
import { query as caveQuery } from '@cavelang/query'
|
|
23
|
+
import { Demo } from '@cavelang/loop'
|
|
24
|
+
|
|
25
|
+
export type Output = {
|
|
26
|
+
readonly code: number
|
|
27
|
+
readonly out: string
|
|
28
|
+
readonly err: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ok = (out: string): Output =>
|
|
32
|
+
({ code: 0, out, err: '' })
|
|
33
|
+
|
|
34
|
+
const fail = (err: string, code = 1): Output =>
|
|
35
|
+
({ code, out: '', err })
|
|
36
|
+
|
|
37
|
+
export const usage = `cave — Compressed Atomic Verb Expressions
|
|
38
|
+
|
|
39
|
+
Usage:
|
|
40
|
+
cave parse [file] [--json] lint CAVE text (stdin when no file)
|
|
41
|
+
cave add [file...] --db <path> ingest into a store [--strict] [--no-prelude]
|
|
42
|
+
cave import [file...] --db <path> restore/merge a database from CAVE text (same as add)
|
|
43
|
+
cave query <pattern> --db <path> run a CAVE-Q pattern [--json] [--all] [--no-prelude]
|
|
44
|
+
cave export --db <path> [--out <file>] emit canonical CAVE text [--current] [--no-prelude]
|
|
45
|
+
cave mcp --db <path> serve the engine as an MCP server on stdio [--no-prelude]
|
|
46
|
+
cave ingest <globs...> --db <path> LLM-driven ingestion of files (cave ingest --help)
|
|
47
|
+
cave demo run the cave-loop reconstruction demo
|
|
48
|
+
cave help this text
|
|
49
|
+
|
|
50
|
+
The spec lives in the .claude/skills/ directory at the repository root
|
|
51
|
+
(section index in README.md).`
|
|
52
|
+
|
|
53
|
+
const readInput = (files: readonly string[]): string =>
|
|
54
|
+
files.length === 0 || (files.length === 1 && files[0] === '-') ?
|
|
55
|
+
readFileSync(0, 'utf8') :
|
|
56
|
+
files.map(file => readFileSync(file, 'utf8')).join('\n')
|
|
57
|
+
|
|
58
|
+
export const parseCommand = (argv: readonly string[]): Output => {
|
|
59
|
+
const { values, positionals } = parseArgs({
|
|
60
|
+
args: [...argv],
|
|
61
|
+
options: { json: { type: 'boolean' } },
|
|
62
|
+
allowPositionals: true
|
|
63
|
+
})
|
|
64
|
+
const input = readInput(positionals)
|
|
65
|
+
const document = parseDocument(input)
|
|
66
|
+
if (values.json === true) {
|
|
67
|
+
return ok(`${JSON.stringify(document, undefined, 2)}\n`)
|
|
68
|
+
}
|
|
69
|
+
const counts = new Map<string, number>()
|
|
70
|
+
for (const line of document.lines) {
|
|
71
|
+
counts.set(line.kind, (counts.get(line.kind) ?? 0) + 1)
|
|
72
|
+
}
|
|
73
|
+
const summary = [...counts]
|
|
74
|
+
.map(([kind, count]) => `${count} ${kind}`)
|
|
75
|
+
.join(', ')
|
|
76
|
+
if (document.diagnostics.length === 0) {
|
|
77
|
+
return ok(`ok: ${summary}\n`)
|
|
78
|
+
}
|
|
79
|
+
const problems = document.diagnostics
|
|
80
|
+
.map(diagnostic => `line ${diagnostic.line}: ${diagnostic.message}`)
|
|
81
|
+
.join('\n')
|
|
82
|
+
return { code: 1, out: `${summary}\n`, err: `${problems}\n` }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const ingestCommand = (name: string) => (argv: readonly string[]): Output => {
|
|
86
|
+
const { values, positionals } = parseArgs({
|
|
87
|
+
args: [...argv],
|
|
88
|
+
options: {
|
|
89
|
+
db: { type: 'string' },
|
|
90
|
+
strict: { type: 'boolean' },
|
|
91
|
+
'no-prelude': { type: 'boolean' }
|
|
92
|
+
},
|
|
93
|
+
allowPositionals: true
|
|
94
|
+
})
|
|
95
|
+
if (values.db === undefined) {
|
|
96
|
+
return fail(`cave ${name}: --db <path> is required\n`)
|
|
97
|
+
}
|
|
98
|
+
const input = readInput(positionals)
|
|
99
|
+
const store = open(values.db, values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
100
|
+
try {
|
|
101
|
+
const result = store.ingest(input, { strict: values.strict === true })
|
|
102
|
+
const problems = result.problems
|
|
103
|
+
.map(problem => `line ${problem.line}: ${problem.message}`)
|
|
104
|
+
.join('\n')
|
|
105
|
+
return {
|
|
106
|
+
code: 0,
|
|
107
|
+
out: `added ${result.ids.length} claim(s), ${result.edges} edge(s)\n`,
|
|
108
|
+
err: problems === '' ? '' : `${problems}\n`
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
112
|
+
} finally {
|
|
113
|
+
store.close()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const addCommand = ingestCommand('add')
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* `cave import` — restore/merge a knowledge database from CAVE text files.
|
|
121
|
+
* Identical to `add` by design: canonical CAVE text *is* the interchange
|
|
122
|
+
* format (spec §2.2), so importing a file exported with `cave export`
|
|
123
|
+
* replays its claims append-only. What the text round trip preserves:
|
|
124
|
+
* every claim with metadata, full belief-series order (rows export in tx
|
|
125
|
+
* order and re-ingest with fresh monotonic tx ids), qualifier/grouping
|
|
126
|
+
* edges, and in-band registry declarations. Original tx timestamps are
|
|
127
|
+
* re-minted — the text format carries no transaction identity.
|
|
128
|
+
*/
|
|
129
|
+
export const importCommand = ingestCommand('import')
|
|
130
|
+
|
|
131
|
+
export const queryCommand = (argv: readonly string[]): Output => {
|
|
132
|
+
const { values, positionals } = parseArgs({
|
|
133
|
+
args: [...argv],
|
|
134
|
+
options: {
|
|
135
|
+
db: { type: 'string' },
|
|
136
|
+
json: { type: 'boolean' },
|
|
137
|
+
all: { type: 'boolean' },
|
|
138
|
+
'no-prelude': { type: 'boolean' }
|
|
139
|
+
},
|
|
140
|
+
allowPositionals: true
|
|
141
|
+
})
|
|
142
|
+
if (values.db === undefined) {
|
|
143
|
+
return fail('cave query: --db <path> is required\n')
|
|
144
|
+
}
|
|
145
|
+
if (positionals.length === 0) {
|
|
146
|
+
return fail('cave query: a pattern is required (spec §12.1)\n')
|
|
147
|
+
}
|
|
148
|
+
const pattern = positionals.join('\n')
|
|
149
|
+
const store = open(values.db, values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
150
|
+
try {
|
|
151
|
+
const matches = caveQuery(store, pattern, { all: values.all === true })
|
|
152
|
+
if (values.json === true) {
|
|
153
|
+
return ok(`${JSON.stringify(matches, undefined, 2)}\n`)
|
|
154
|
+
}
|
|
155
|
+
if (matches.length === 0) {
|
|
156
|
+
return ok('no matches\n')
|
|
157
|
+
}
|
|
158
|
+
const lines = matches.map(match => {
|
|
159
|
+
const bindings = Object.entries(match.bindings)
|
|
160
|
+
.map(([name, value]) => `?${name} = ${value}`)
|
|
161
|
+
.join(' ')
|
|
162
|
+
// A fully bound pattern has no bindings to print; transitive matches
|
|
163
|
+
// additionally carry no row — confirm with the pattern itself.
|
|
164
|
+
return bindings !== '' ? bindings : match.row?.raw_line ?? pattern.split('\n')[0]!
|
|
165
|
+
})
|
|
166
|
+
return ok(`${lines.join('\n')}\n`)
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
169
|
+
} finally {
|
|
170
|
+
store.close()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export const exportCommand = (argv: readonly string[]): Output => {
|
|
175
|
+
const { values } = parseArgs({
|
|
176
|
+
args: [...argv],
|
|
177
|
+
options: {
|
|
178
|
+
db: { type: 'string' },
|
|
179
|
+
current: { type: 'boolean' },
|
|
180
|
+
out: { type: 'string' },
|
|
181
|
+
'no-prelude': { type: 'boolean' }
|
|
182
|
+
},
|
|
183
|
+
allowPositionals: false
|
|
184
|
+
})
|
|
185
|
+
if (values.db === undefined) {
|
|
186
|
+
return fail('cave export: --db <path> is required\n')
|
|
187
|
+
}
|
|
188
|
+
const store = open(values.db, values['no-prelude'] === true ? { registry: Registry.empty } : {})
|
|
189
|
+
try {
|
|
190
|
+
const text = store.exportText({ current: values.current === true })
|
|
191
|
+
if (values.out === undefined) {
|
|
192
|
+
return ok(text)
|
|
193
|
+
}
|
|
194
|
+
writeFileSync(values.out, text)
|
|
195
|
+
const claims = text === '' ? 0 : text.trimEnd().split('\n').length
|
|
196
|
+
return ok(`exported ${claims} claim(s) to ${values.out}\n`)
|
|
197
|
+
} finally {
|
|
198
|
+
store.close()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export const demoCommand = (): Output =>
|
|
203
|
+
ok(`${Demo.run().lines.join('\n')}\n`)
|
|
204
|
+
|
|
205
|
+
/** Dispatches one invocation. */
|
|
206
|
+
export const cave = (argv: readonly string[]): Output => {
|
|
207
|
+
const [command, ...rest] = argv
|
|
208
|
+
try {
|
|
209
|
+
switch (command) {
|
|
210
|
+
case 'parse':
|
|
211
|
+
return parseCommand(rest)
|
|
212
|
+
case 'add':
|
|
213
|
+
return addCommand(rest)
|
|
214
|
+
case 'import':
|
|
215
|
+
return importCommand(rest)
|
|
216
|
+
case 'query':
|
|
217
|
+
case 'q':
|
|
218
|
+
return queryCommand(rest)
|
|
219
|
+
case 'export':
|
|
220
|
+
return exportCommand(rest)
|
|
221
|
+
case 'demo':
|
|
222
|
+
return demoCommand()
|
|
223
|
+
case undefined:
|
|
224
|
+
case 'help':
|
|
225
|
+
case '--help':
|
|
226
|
+
case '-h':
|
|
227
|
+
return ok(`${usage}\n`)
|
|
228
|
+
default:
|
|
229
|
+
return fail(`cave: unknown command ${JSON.stringify(command)}\n\n${usage}\n`, 2)
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
return fail(`${error instanceof Error ? error.message : String(error)}\n`)
|
|
233
|
+
}
|
|
234
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cavelang/cli` — the `cave` command (parse / add / query / export / demo).
|
|
3
|
+
* Programmatic access to the same implementations lives here.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { addCommand, cave, demoCommand, exportCommand, importCommand, parseCommand, queryCommand, usage } from './cli.ts'
|
|
7
|
+
export type { Output } from './cli.ts'
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** `cave` binary entry point — see `cli.ts` for the command implementations. */
|
|
3
|
+
|
|
4
|
+
import { cave } from './cli.ts'
|
|
5
|
+
|
|
6
|
+
const argv = process.argv.slice(2)
|
|
7
|
+
if (argv[0] === 'mcp') {
|
|
8
|
+
// Long-running: serves MCP on stdio until the client disconnects.
|
|
9
|
+
const { runMcp } = await import('@cavelang/mcp')
|
|
10
|
+
process.exitCode = await runMcp(argv.slice(1))
|
|
11
|
+
} else if (argv[0] === 'ingest') {
|
|
12
|
+
// Long-running: drives an LLM agent over batches of files.
|
|
13
|
+
const { runIngest } = await import('@cavelang/ingest')
|
|
14
|
+
process.exitCode = await runIngest(argv.slice(1))
|
|
15
|
+
} else {
|
|
16
|
+
const { code, out, err } = cave(argv)
|
|
17
|
+
if (out !== '') {
|
|
18
|
+
process.stdout.write(out)
|
|
19
|
+
}
|
|
20
|
+
if (err !== '') {
|
|
21
|
+
process.stderr.write(err)
|
|
22
|
+
}
|
|
23
|
+
process.exitCode = code
|
|
24
|
+
}
|