clawvault 1.11.2 → 2.0.1
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 +135 -1
- package/bin/clawvault.js +51 -1252
- package/bin/command-registration.test.js +148 -0
- package/bin/command-runtime.js +42 -0
- package/bin/command-runtime.test.js +102 -0
- package/bin/help-contract.test.js +23 -0
- package/bin/register-core-commands.js +139 -0
- package/bin/register-maintenance-commands.js +137 -0
- package/bin/register-query-commands.js +225 -0
- package/bin/register-resilience-commands.js +147 -0
- package/bin/register-session-lifecycle-commands.js +204 -0
- package/bin/register-template-commands.js +72 -0
- package/bin/register-vault-operations-commands.js +295 -0
- package/bin/test-helpers/cli-command-fixtures.js +94 -0
- package/dashboard/lib/graph-diff.js +3 -1
- package/dashboard/lib/graph-diff.test.js +19 -0
- package/dashboard/lib/vault-parser.js +330 -26
- package/dashboard/lib/vault-parser.test.js +191 -11
- package/dashboard/public/app.js +22 -9
- package/dist/chunk-MXSSG3QU.js +42 -0
- package/dist/chunk-O5V7SD5C.js +398 -0
- package/dist/chunk-PAYUH64O.js +284 -0
- package/dist/{chunk-3HFB7EMU.js → chunk-QFBKWDYR.js} +12 -0
- package/dist/{chunk-UBRYOIII.js → chunk-TBVI4N53.js} +210 -21
- package/dist/chunk-TXO34J3O.js +56 -0
- package/dist/commands/compat.d.ts +28 -0
- package/dist/commands/compat.js +10 -0
- package/dist/commands/context.d.ts +2 -33
- package/dist/commands/context.js +3 -2
- package/dist/commands/doctor.js +61 -3
- package/dist/commands/entities.d.ts +1 -0
- package/dist/commands/entities.js +4 -4
- package/dist/commands/graph.d.ts +21 -0
- package/dist/commands/graph.js +10 -0
- package/dist/commands/link.d.ts +1 -0
- package/dist/commands/link.js +14 -5
- package/dist/commands/sleep.js +7 -6
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +63 -3
- package/dist/commands/wake.js +5 -4
- package/dist/context-COo8oq1k.d.ts +45 -0
- package/dist/index.d.ts +63 -2
- package/dist/index.js +53 -15
- package/dist/lib/config.d.ts +6 -1
- package/dist/lib/config.js +7 -3
- package/hooks/clawvault/HOOK.md +6 -1
- package/hooks/clawvault/handler.js +44 -3
- package/hooks/clawvault/handler.test.js +161 -0
- package/package.json +34 -2
- package/dashboard/public/graph.js +0 -376
- package/dashboard/public/style.css +0 -154
- package/dist/chunk-4KDZZW4X.js +0 -13
package/bin/clawvault.js
CHANGED
|
@@ -9,14 +9,22 @@ import { Command } from 'commander';
|
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import * as fs from 'fs';
|
|
11
11
|
import * as path from 'path';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { registerMaintenanceCommands } from './register-maintenance-commands.js';
|
|
13
|
+
import { registerCoreCommands } from './register-core-commands.js';
|
|
14
|
+
import { registerQueryCommands } from './register-query-commands.js';
|
|
15
|
+
import { registerResilienceCommands } from './register-resilience-commands.js';
|
|
16
|
+
import { registerSessionLifecycleCommands } from './register-session-lifecycle-commands.js';
|
|
17
|
+
import { registerTemplateCommands } from './register-template-commands.js';
|
|
18
|
+
import { registerVaultOperationsCommands } from './register-vault-operations-commands.js';
|
|
14
19
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
getVault,
|
|
21
|
+
resolveVaultPath,
|
|
22
|
+
runQmd,
|
|
23
|
+
printQmdMissing,
|
|
24
|
+
QmdUnavailableError
|
|
25
|
+
} from './command-runtime.js';
|
|
26
|
+
import {
|
|
27
|
+
createVault
|
|
20
28
|
} from '../dist/index.js';
|
|
21
29
|
|
|
22
30
|
const program = new Command();
|
|
@@ -31,1257 +39,48 @@ const CLI_VERSION = (() => {
|
|
|
31
39
|
}
|
|
32
40
|
})();
|
|
33
41
|
|
|
34
|
-
// Helper to get vault (required for most commands)
|
|
35
|
-
// Checks: 1) explicit path, 2) CLAWVAULT_PATH env, 3) walk up from cwd
|
|
36
|
-
async function getVault(vaultPath) {
|
|
37
|
-
// Explicit path takes priority
|
|
38
|
-
if (vaultPath) {
|
|
39
|
-
const vault = new ClawVault(path.resolve(vaultPath));
|
|
40
|
-
await vault.load();
|
|
41
|
-
return vault;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Check environment variable
|
|
45
|
-
const envPath = process.env.CLAWVAULT_PATH;
|
|
46
|
-
if (envPath) {
|
|
47
|
-
const vault = new ClawVault(path.resolve(envPath));
|
|
48
|
-
await vault.load();
|
|
49
|
-
return vault;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Walk up from cwd
|
|
53
|
-
const vault = await findVault();
|
|
54
|
-
if (!vault) {
|
|
55
|
-
console.error(chalk.red('Error: No ClawVault found. Run `clawvault init` first.'));
|
|
56
|
-
console.log(chalk.dim('Tip: Set CLAWVAULT_PATH environment variable to your vault path'));
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
return vault;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function resolveVaultPath(vaultPath) {
|
|
63
|
-
if (vaultPath) {
|
|
64
|
-
return path.resolve(vaultPath);
|
|
65
|
-
}
|
|
66
|
-
if (process.env.CLAWVAULT_PATH) {
|
|
67
|
-
return path.resolve(process.env.CLAWVAULT_PATH);
|
|
68
|
-
}
|
|
69
|
-
let current = process.cwd();
|
|
70
|
-
while (true) {
|
|
71
|
-
if (fs.existsSync(path.join(current, '.clawvault.json'))) {
|
|
72
|
-
return current;
|
|
73
|
-
}
|
|
74
|
-
const parent = path.dirname(current);
|
|
75
|
-
if (parent === current) {
|
|
76
|
-
console.error(chalk.red('Error: No ClawVault found. Run `clawvault init` first.'));
|
|
77
|
-
console.log(chalk.dim('Tip: Set CLAWVAULT_PATH environment variable to your vault path'));
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
current = parent;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function runQmd(args) {
|
|
85
|
-
return new Promise((resolve, reject) => {
|
|
86
|
-
const proc = spawn('qmd', args, { stdio: 'inherit' });
|
|
87
|
-
proc.on('close', (code) => {
|
|
88
|
-
if (code === 0) resolve();
|
|
89
|
-
else reject(new Error(`qmd exited with code ${code}`));
|
|
90
|
-
});
|
|
91
|
-
proc.on('error', (err) => {
|
|
92
|
-
if (err?.code === 'ENOENT') {
|
|
93
|
-
reject(new QmdUnavailableError());
|
|
94
|
-
} else {
|
|
95
|
-
reject(err);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function printQmdMissing() {
|
|
102
|
-
console.error(chalk.red('Error: ClawVault requires qmd.'));
|
|
103
|
-
console.log(chalk.dim(`Install: ${QMD_INSTALL_COMMAND}`));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function parseBooleanInput(value, defaultValue = true) {
|
|
107
|
-
const normalized = value.trim().toLowerCase();
|
|
108
|
-
if (!normalized) {
|
|
109
|
-
return defaultValue;
|
|
110
|
-
}
|
|
111
|
-
if (['y', 'yes', 'true', '1'].includes(normalized)) {
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
if (['n', 'no', 'false', '0'].includes(normalized)) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
42
|
program
|
|
121
43
|
.name('clawvault')
|
|
122
44
|
.description('🐘 An elephant never forgets. Structured memory for AI agents.')
|
|
123
45
|
.version(CLI_VERSION);
|
|
124
46
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
console.log(chalk.green('✓ Vault created'));
|
|
143
|
-
console.log(chalk.dim(` Categories: ${vault.getCategories().join(', ')}`));
|
|
144
|
-
|
|
145
|
-
// Always set up qmd collection (qmd is required)
|
|
146
|
-
console.log(chalk.cyan('\nSetting up qmd collection...'));
|
|
147
|
-
try {
|
|
148
|
-
await runQmd([
|
|
149
|
-
'collection',
|
|
150
|
-
'add',
|
|
151
|
-
vault.getQmdRoot(),
|
|
152
|
-
'--name',
|
|
153
|
-
vault.getQmdCollection(),
|
|
154
|
-
'--mask',
|
|
155
|
-
'**/*.md'
|
|
156
|
-
]);
|
|
157
|
-
console.log(chalk.green('✓ qmd collection created'));
|
|
158
|
-
} catch (err) {
|
|
159
|
-
// Collection might already exist
|
|
160
|
-
console.log(chalk.yellow('⚠ qmd collection may already exist'));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
console.log(chalk.green('\n✅ ClawVault ready!\n'));
|
|
164
|
-
console.log(chalk.dim('Next steps:'));
|
|
165
|
-
console.log(chalk.dim(' clawvault store --category inbox --title "My note" --content "Hello world"'));
|
|
166
|
-
console.log(chalk.dim(' clawvault search "hello"'));
|
|
167
|
-
console.log();
|
|
168
|
-
} catch (err) {
|
|
169
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// === SETUP ===
|
|
175
|
-
program
|
|
176
|
-
.command('setup')
|
|
177
|
-
.description('Auto-discover and configure a ClawVault')
|
|
178
|
-
.action(async () => {
|
|
179
|
-
try {
|
|
180
|
-
const { setupCommand } = await import('../dist/commands/setup.js');
|
|
181
|
-
await setupCommand();
|
|
182
|
-
} catch (err) {
|
|
183
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
// === STORE ===
|
|
189
|
-
program
|
|
190
|
-
.command('store')
|
|
191
|
-
.description('Store a new memory')
|
|
192
|
-
.requiredOption('-c, --category <category>', 'Category (preferences, decisions, patterns, people, projects, goals, transcripts, inbox)')
|
|
193
|
-
.requiredOption('-t, --title <title>', 'Document title')
|
|
194
|
-
.option('--content <content>', 'Content body')
|
|
195
|
-
.option('-f, --file <file>', 'Read content from file')
|
|
196
|
-
.option('--stdin', 'Read content from stdin')
|
|
197
|
-
.option('--overwrite', 'Overwrite if exists')
|
|
198
|
-
.option('--no-index', 'Skip qmd index update (auto-updates by default)')
|
|
199
|
-
.option('--embed', 'Also update qmd embeddings for vector search')
|
|
200
|
-
.option('-v, --vault <path>', 'Vault path (default: find nearest)')
|
|
201
|
-
.action(async (options) => {
|
|
202
|
-
try {
|
|
203
|
-
const vault = await getVault(options.vault);
|
|
204
|
-
|
|
205
|
-
let content = options.content || '';
|
|
206
|
-
|
|
207
|
-
if (options.file) {
|
|
208
|
-
content = fs.readFileSync(options.file, 'utf-8');
|
|
209
|
-
} else if (options.stdin) {
|
|
210
|
-
content = fs.readFileSync(0, 'utf-8');
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const doc = await vault.store({
|
|
214
|
-
category: options.category,
|
|
215
|
-
title: options.title,
|
|
216
|
-
content,
|
|
217
|
-
overwrite: options.overwrite
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
console.log(chalk.green(`✓ Stored: ${doc.id}`));
|
|
221
|
-
console.log(chalk.dim(` Path: ${doc.path}`));
|
|
222
|
-
|
|
223
|
-
// Auto-update qmd index unless --no-index
|
|
224
|
-
if (options.index !== false) {
|
|
225
|
-
const collection = vault.getQmdCollection();
|
|
226
|
-
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
227
|
-
if (options.embed) {
|
|
228
|
-
await runQmd(collection ? ['embed', '-c', collection] : ['embed']);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
} catch (err) {
|
|
232
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// === CAPTURE ===
|
|
238
|
-
program
|
|
239
|
-
.command('capture <note>')
|
|
240
|
-
.description('Quick capture to inbox')
|
|
241
|
-
.option('-t, --title <title>', 'Note title')
|
|
242
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
243
|
-
.option('--no-index', 'Skip qmd index update')
|
|
244
|
-
.action(async (note, options) => {
|
|
245
|
-
try {
|
|
246
|
-
const vault = await getVault(options.vault);
|
|
247
|
-
const doc = await vault.capture(note, options.title);
|
|
248
|
-
console.log(chalk.green(`✓ Captured: ${doc.id}`));
|
|
249
|
-
|
|
250
|
-
// Auto-update qmd index unless --no-index
|
|
251
|
-
if (options.index !== false) {
|
|
252
|
-
const collection = vault.getQmdCollection();
|
|
253
|
-
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
254
|
-
}
|
|
255
|
-
} catch (err) {
|
|
256
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// === SEARCH ===
|
|
262
|
-
program
|
|
263
|
-
.command('search <query>')
|
|
264
|
-
.description('Search the vault via qmd (BM25)')
|
|
265
|
-
.option('-n, --limit <n>', 'Max results', '10')
|
|
266
|
-
.option('-c, --category <category>', 'Filter by category')
|
|
267
|
-
.option('--tags <tags>', 'Filter by tags (comma-separated)')
|
|
268
|
-
.option('--recent', 'Boost recent documents')
|
|
269
|
-
.option('--full', 'Include full content in results')
|
|
270
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
271
|
-
.option('--json', 'Output as JSON')
|
|
272
|
-
.action(async (query, options) => {
|
|
273
|
-
try {
|
|
274
|
-
const vault = await getVault(options.vault);
|
|
275
|
-
|
|
276
|
-
const results = await vault.find(query, {
|
|
277
|
-
limit: parseInt(options.limit),
|
|
278
|
-
category: options.category,
|
|
279
|
-
tags: options.tags?.split(',').map(t => t.trim()),
|
|
280
|
-
fullContent: options.full,
|
|
281
|
-
temporalBoost: options.recent
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
if (options.json) {
|
|
285
|
-
console.log(JSON.stringify(results, null, 2));
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (results.length === 0) {
|
|
290
|
-
console.log(chalk.yellow('No results found.'));
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
console.log(chalk.cyan(`\n🔍 Found ${results.length} result(s) for "${query}":\n`));
|
|
295
|
-
|
|
296
|
-
for (const result of results) {
|
|
297
|
-
const scoreBar = '█'.repeat(Math.round(result.score * 10)).padEnd(10, '░');
|
|
298
|
-
console.log(chalk.green(`📄 ${result.document.title}`));
|
|
299
|
-
console.log(chalk.dim(` ${result.document.category}/${result.document.id.split('/').pop()}`));
|
|
300
|
-
console.log(chalk.dim(` Score: ${scoreBar} ${(result.score * 100).toFixed(0)}%`));
|
|
301
|
-
if (result.snippet) {
|
|
302
|
-
console.log(chalk.white(` ${result.snippet.split('\n')[0].slice(0, 80)}...`));
|
|
303
|
-
}
|
|
304
|
-
console.log();
|
|
305
|
-
}
|
|
306
|
-
} catch (err) {
|
|
307
|
-
if (err instanceof QmdUnavailableError) {
|
|
308
|
-
printQmdMissing();
|
|
309
|
-
process.exit(1);
|
|
310
|
-
}
|
|
311
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
312
|
-
process.exit(1);
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// === VSEARCH (qmd semantic search) ===
|
|
317
|
-
program
|
|
318
|
-
.command('vsearch <query>')
|
|
319
|
-
.description('Semantic search via qmd (requires qmd installed)')
|
|
320
|
-
.option('-n, --limit <n>', 'Max results', '5')
|
|
321
|
-
.option('-c, --category <category>', 'Filter by category')
|
|
322
|
-
.option('--tags <tags>', 'Filter by tags (comma-separated)')
|
|
323
|
-
.option('--recent', 'Boost recent documents')
|
|
324
|
-
.option('--full', 'Include full content in results')
|
|
325
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
326
|
-
.option('--json', 'Output as JSON')
|
|
327
|
-
.action(async (query, options) => {
|
|
328
|
-
try {
|
|
329
|
-
const vault = await getVault(options.vault);
|
|
330
|
-
|
|
331
|
-
const results = await vault.vsearch(query, {
|
|
332
|
-
limit: parseInt(options.limit),
|
|
333
|
-
category: options.category,
|
|
334
|
-
tags: options.tags?.split(',').map(t => t.trim()),
|
|
335
|
-
fullContent: options.full,
|
|
336
|
-
temporalBoost: options.recent
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
if (options.json) {
|
|
340
|
-
console.log(JSON.stringify(results, null, 2));
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (results.length === 0) {
|
|
345
|
-
console.log(chalk.yellow('No results found.'));
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
console.log(chalk.cyan(`\n🧠 Found ${results.length} result(s) for "${query}":\n`));
|
|
350
|
-
|
|
351
|
-
for (const result of results) {
|
|
352
|
-
const scoreBar = '█'.repeat(Math.round(result.score * 10)).padEnd(10, '░');
|
|
353
|
-
console.log(chalk.green(`📄 ${result.document.title}`));
|
|
354
|
-
console.log(chalk.dim(` ${result.document.category}/${result.document.id.split('/').pop()}`));
|
|
355
|
-
console.log(chalk.dim(` Score: ${scoreBar} ${(result.score * 100).toFixed(0)}%`));
|
|
356
|
-
if (result.snippet) {
|
|
357
|
-
console.log(chalk.white(` ${result.snippet.split('\n')[0].slice(0, 80)}...`));
|
|
358
|
-
}
|
|
359
|
-
console.log();
|
|
360
|
-
}
|
|
361
|
-
} catch (err) {
|
|
362
|
-
if (err instanceof QmdUnavailableError) {
|
|
363
|
-
printQmdMissing();
|
|
364
|
-
process.exit(1);
|
|
365
|
-
}
|
|
366
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
367
|
-
console.log(chalk.dim(`\nTip: Install qmd: ${QMD_INSTALL_COMMAND}`));
|
|
368
|
-
process.exit(1);
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// === CONTEXT ===
|
|
373
|
-
program
|
|
374
|
-
.command('context <task>')
|
|
375
|
-
.description('Generate task-relevant context for prompt injection')
|
|
376
|
-
.option('-n, --limit <n>', 'Max results', '5')
|
|
377
|
-
.option('--format <format>', 'Output format (markdown|json)', 'markdown')
|
|
378
|
-
.option('--recent', 'Boost recent documents (enabled by default)', true)
|
|
379
|
-
.option('--include-observations', 'Include observation memories in output', true)
|
|
380
|
-
.option('--budget <number>', 'Optional token budget for assembled context')
|
|
381
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
382
|
-
.action(async (task, options) => {
|
|
383
|
-
try {
|
|
384
|
-
const vaultPath = resolveVaultPath(options.vault);
|
|
385
|
-
const format = options.format === 'json' ? 'json' : 'markdown';
|
|
386
|
-
const parsedBudget = options.budget ? Number.parseInt(options.budget, 10) : undefined;
|
|
387
|
-
if (options.budget && (!Number.isFinite(parsedBudget) || parsedBudget <= 0)) {
|
|
388
|
-
throw new Error(`Invalid --budget value: ${options.budget}`);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const { contextCommand } = await import('../dist/commands/context.js');
|
|
392
|
-
await contextCommand(task, {
|
|
393
|
-
vaultPath,
|
|
394
|
-
limit: parseInt(options.limit),
|
|
395
|
-
format,
|
|
396
|
-
recent: options.recent,
|
|
397
|
-
includeObservations: options.includeObservations,
|
|
398
|
-
budget: parsedBudget
|
|
399
|
-
});
|
|
400
|
-
} catch (err) {
|
|
401
|
-
if (err instanceof QmdUnavailableError) {
|
|
402
|
-
printQmdMissing();
|
|
403
|
-
process.exit(1);
|
|
404
|
-
}
|
|
405
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
406
|
-
process.exit(1);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
// === OBSERVE ===
|
|
411
|
-
program
|
|
412
|
-
.command('observe')
|
|
413
|
-
.description('Observe session files and build observational memory')
|
|
414
|
-
.option('--watch <path>', 'Watch session file or directory')
|
|
415
|
-
.option('--threshold <n>', 'Compression token threshold', '30000')
|
|
416
|
-
.option('--reflect-threshold <n>', 'Reflection token threshold', '40000')
|
|
417
|
-
.option('--model <model>', 'LLM model override')
|
|
418
|
-
.option('--compress <file>', 'One-shot compression for a conversation file')
|
|
419
|
-
.option('--daemon', 'Run in detached background mode')
|
|
420
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
421
|
-
.action(async (options) => {
|
|
422
|
-
try {
|
|
423
|
-
const { observeCommand } = await import('../dist/commands/observe.js');
|
|
424
|
-
const threshold = Number.parseInt(options.threshold, 10);
|
|
425
|
-
const reflectThreshold = Number.parseInt(options.reflectThreshold, 10);
|
|
426
|
-
if (Number.isNaN(threshold) || threshold <= 0) {
|
|
427
|
-
throw new Error(`Invalid --threshold value: ${options.threshold}`);
|
|
428
|
-
}
|
|
429
|
-
if (Number.isNaN(reflectThreshold) || reflectThreshold <= 0) {
|
|
430
|
-
throw new Error(`Invalid --reflect-threshold value: ${options.reflectThreshold}`);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
await observeCommand({
|
|
434
|
-
watch: options.watch,
|
|
435
|
-
threshold,
|
|
436
|
-
reflectThreshold,
|
|
437
|
-
model: options.model,
|
|
438
|
-
compress: options.compress,
|
|
439
|
-
daemon: options.daemon,
|
|
440
|
-
vaultPath: resolveVaultPath(options.vault)
|
|
441
|
-
});
|
|
442
|
-
} catch (err) {
|
|
443
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
444
|
-
process.exit(1);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
// === SESSION-RECAP ===
|
|
449
|
-
program
|
|
450
|
-
.command('session-recap <sessionKey>')
|
|
451
|
-
.description('Generate recap from a specific OpenClaw session transcript')
|
|
452
|
-
.option('-n, --limit <n>', 'Number of messages to include', '15')
|
|
453
|
-
.option('--format <format>', 'Output format (markdown|json)', 'markdown')
|
|
454
|
-
.option('-a, --agent <id>', 'Agent ID (default: OPENCLAW_AGENT_ID or clawdious)')
|
|
455
|
-
.action(async (sessionKey, options) => {
|
|
456
|
-
try {
|
|
457
|
-
const { sessionRecapCommand } = await import('../dist/commands/session-recap.js');
|
|
458
|
-
const format = options.format === 'json' ? 'json' : 'markdown';
|
|
459
|
-
const parsedLimit = Number.parseInt(options.limit, 10);
|
|
460
|
-
await sessionRecapCommand(sessionKey, {
|
|
461
|
-
limit: Number.isNaN(parsedLimit) ? 15 : parsedLimit,
|
|
462
|
-
format,
|
|
463
|
-
agentId: options.agent
|
|
464
|
-
});
|
|
465
|
-
} catch (err) {
|
|
466
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
467
|
-
process.exit(1);
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
// === LIST ===
|
|
472
|
-
program
|
|
473
|
-
.command('list [category]')
|
|
474
|
-
.description('List documents')
|
|
475
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
476
|
-
.option('--json', 'Output as JSON')
|
|
477
|
-
.action(async (category, options) => {
|
|
478
|
-
try {
|
|
479
|
-
const vault = await getVault(options.vault);
|
|
480
|
-
const docs = await vault.list(category);
|
|
481
|
-
|
|
482
|
-
if (options.json) {
|
|
483
|
-
console.log(JSON.stringify(docs.map(d => ({
|
|
484
|
-
id: d.id,
|
|
485
|
-
title: d.title,
|
|
486
|
-
category: d.category,
|
|
487
|
-
tags: d.tags,
|
|
488
|
-
modified: d.modified
|
|
489
|
-
})), null, 2));
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (docs.length === 0) {
|
|
494
|
-
console.log(chalk.yellow('No documents found.'));
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
console.log(chalk.cyan(`\n📚 ${docs.length} document(s)${category ? ` in ${category}` : ''}:\n`));
|
|
499
|
-
|
|
500
|
-
// Group by category
|
|
501
|
-
const grouped = {};
|
|
502
|
-
for (const doc of docs) {
|
|
503
|
-
grouped[doc.category] = grouped[doc.category] || [];
|
|
504
|
-
grouped[doc.category].push(doc);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
for (const [cat, catDocs] of Object.entries(grouped)) {
|
|
508
|
-
console.log(chalk.yellow(`${cat}/`));
|
|
509
|
-
for (const doc of catDocs) {
|
|
510
|
-
console.log(chalk.dim(` - ${doc.title}`));
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
console.log();
|
|
514
|
-
} catch (err) {
|
|
515
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
516
|
-
process.exit(1);
|
|
517
|
-
}
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
// === GET ===
|
|
521
|
-
program
|
|
522
|
-
.command('get <id>')
|
|
523
|
-
.description('Get a document by ID')
|
|
524
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
525
|
-
.option('--json', 'Output as JSON')
|
|
526
|
-
.action(async (id, options) => {
|
|
527
|
-
try {
|
|
528
|
-
const vault = await getVault(options.vault);
|
|
529
|
-
const doc = await vault.get(id);
|
|
530
|
-
|
|
531
|
-
if (!doc) {
|
|
532
|
-
console.error(chalk.red(`Document not found: ${id}`));
|
|
533
|
-
process.exit(1);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (options.json) {
|
|
537
|
-
console.log(JSON.stringify(doc, null, 2));
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
console.log(chalk.cyan(`\n📄 ${doc.title}\n`));
|
|
542
|
-
console.log(chalk.dim(`Category: ${doc.category}`));
|
|
543
|
-
console.log(chalk.dim(`Path: ${doc.path}`));
|
|
544
|
-
console.log(chalk.dim(`Tags: ${doc.tags.join(', ') || 'none'}`));
|
|
545
|
-
console.log(chalk.dim(`Links: ${doc.links.join(', ') || 'none'}`));
|
|
546
|
-
console.log(chalk.dim(`Modified: ${doc.modified.toISOString()}`));
|
|
547
|
-
console.log(chalk.dim('---'));
|
|
548
|
-
console.log(doc.content);
|
|
549
|
-
} catch (err) {
|
|
550
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
551
|
-
process.exit(1);
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
// === STATS ===
|
|
556
|
-
program
|
|
557
|
-
.command('stats')
|
|
558
|
-
.description('Show vault statistics')
|
|
559
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
560
|
-
.option('--json', 'Output as JSON')
|
|
561
|
-
.action(async (options) => {
|
|
562
|
-
try {
|
|
563
|
-
const vault = await getVault(options.vault);
|
|
564
|
-
const stats = await vault.stats();
|
|
565
|
-
|
|
566
|
-
if (options.json) {
|
|
567
|
-
console.log(JSON.stringify(stats, null, 2));
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
console.log(chalk.cyan(`\n🐘 ${vault.getName()} Stats\n`));
|
|
572
|
-
console.log(chalk.dim(`Path: ${vault.getPath()}`));
|
|
573
|
-
console.log(`Documents: ${chalk.green(stats.documents)}`);
|
|
574
|
-
console.log(`Links: ${chalk.blue(stats.links)}`);
|
|
575
|
-
console.log(`Tags: ${chalk.yellow(stats.tags.length)}`);
|
|
576
|
-
console.log();
|
|
577
|
-
console.log(chalk.dim('By category:'));
|
|
578
|
-
for (const [cat, count] of Object.entries(stats.categories)) {
|
|
579
|
-
console.log(chalk.dim(` ${cat}: ${count}`));
|
|
580
|
-
}
|
|
581
|
-
console.log();
|
|
582
|
-
} catch (err) {
|
|
583
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
584
|
-
process.exit(1);
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
// === SYNC (vault file sync only) ===
|
|
589
|
-
program
|
|
590
|
-
.command('sync <target>')
|
|
591
|
-
.description('Sync vault files to target path')
|
|
592
|
-
.option('--delete', 'Delete orphan files in target')
|
|
593
|
-
.option('--dry-run', "Show what would be synced without syncing")
|
|
594
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
595
|
-
.action(async (target, options) => {
|
|
596
|
-
try {
|
|
597
|
-
const vault = await getVault(options.vault);
|
|
598
|
-
|
|
599
|
-
console.log(chalk.cyan(`\n🔄 Syncing to ${target}...\n`));
|
|
600
|
-
|
|
601
|
-
const result = await vault.sync({
|
|
602
|
-
target,
|
|
603
|
-
deleteOrphans: options.delete,
|
|
604
|
-
dryRun: options.dryRun
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
if (options.dryRun) {
|
|
608
|
-
console.log(chalk.yellow('DRY RUN - no changes made\n'));
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
if (result.copied.length > 0) {
|
|
612
|
-
console.log(chalk.green(`Copied: ${result.copied.length} files`));
|
|
613
|
-
for (const f of result.copied.slice(0, 5)) {
|
|
614
|
-
console.log(chalk.dim(` + ${f}`));
|
|
615
|
-
}
|
|
616
|
-
if (result.copied.length > 5) {
|
|
617
|
-
console.log(chalk.dim(` ... and ${result.copied.length - 5} more`));
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (result.deleted.length > 0) {
|
|
622
|
-
console.log(chalk.red(`Deleted: ${result.deleted.length} files`));
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
if (result.unchanged.length > 0) {
|
|
626
|
-
console.log(chalk.dim(`Unchanged: ${result.unchanged.length} files`));
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
if (result.errors.length > 0) {
|
|
630
|
-
console.log(chalk.red(`\nErrors:`));
|
|
631
|
-
for (const e of result.errors) {
|
|
632
|
-
console.log(chalk.red(` ${e}`));
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
console.log();
|
|
637
|
-
} catch (err) {
|
|
638
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
639
|
-
process.exit(1);
|
|
640
|
-
}
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
// === REINDEX ===
|
|
644
|
-
program
|
|
645
|
-
.command('reindex')
|
|
646
|
-
.description('Rebuild the search index')
|
|
647
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
648
|
-
.option('--qmd', 'Also update qmd embeddings')
|
|
649
|
-
.action(async (options) => {
|
|
650
|
-
try {
|
|
651
|
-
const vault = await getVault(options.vault);
|
|
652
|
-
|
|
653
|
-
console.log(chalk.cyan('\n🔄 Reindexing...\n'));
|
|
654
|
-
|
|
655
|
-
const count = await vault.reindex();
|
|
656
|
-
console.log(chalk.green(`✓ Indexed ${count} documents`));
|
|
657
|
-
|
|
658
|
-
if (options.qmd) {
|
|
659
|
-
console.log(chalk.cyan('Updating qmd embeddings...'));
|
|
660
|
-
const collection = vault.getQmdCollection();
|
|
661
|
-
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
662
|
-
console.log(chalk.green('✓ qmd updated'));
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
console.log();
|
|
666
|
-
} catch (err) {
|
|
667
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
668
|
-
process.exit(1);
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
// === REMEMBER (type-based storage) ===
|
|
673
|
-
program
|
|
674
|
-
.command('remember <type> <title>')
|
|
675
|
-
.description('Store a memory with type classification (fact|feeling|decision|lesson|commitment|preference|relationship|project)')
|
|
676
|
-
.option('--content <content>', 'Content body')
|
|
677
|
-
.option('-f, --file <file>', 'Read content from file')
|
|
678
|
-
.option('--stdin', 'Read content from stdin')
|
|
679
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
680
|
-
.option('--no-index', 'Skip qmd index update')
|
|
681
|
-
.action(async (type, title, options) => {
|
|
682
|
-
const validTypes = ['fact', 'feeling', 'decision', 'lesson', 'commitment', 'preference', 'relationship', 'project'];
|
|
683
|
-
if (!validTypes.includes(type)) {
|
|
684
|
-
console.error(chalk.red(`Invalid type: ${type}`));
|
|
685
|
-
console.error(chalk.dim(`Valid types: ${validTypes.join(', ')}`));
|
|
686
|
-
process.exit(1);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
try {
|
|
690
|
-
const vault = await getVault(options.vault);
|
|
691
|
-
|
|
692
|
-
let content = options.content || '';
|
|
693
|
-
if (options.file) {
|
|
694
|
-
content = fs.readFileSync(options.file, 'utf-8');
|
|
695
|
-
} else if (options.stdin) {
|
|
696
|
-
content = fs.readFileSync(0, 'utf-8');
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
const doc = await vault.remember(type, title, content);
|
|
700
|
-
console.log(chalk.green(`✓ Remembered (${type}): ${doc.id}`));
|
|
701
|
-
|
|
702
|
-
// Auto-update qmd index unless --no-index
|
|
703
|
-
if (options.index !== false) {
|
|
704
|
-
const collection = vault.getQmdCollection();
|
|
705
|
-
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
706
|
-
}
|
|
707
|
-
} catch (err) {
|
|
708
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
709
|
-
process.exit(1);
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
|
|
713
|
-
// === WAKE (session start) ===
|
|
714
|
-
program
|
|
715
|
-
.command('wake')
|
|
716
|
-
.description('Start a session (recover + recap + summary)')
|
|
717
|
-
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include', '3')
|
|
718
|
-
.option('--full', 'Show full recap (default: brief)')
|
|
719
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
720
|
-
.action(async (options) => {
|
|
721
|
-
try {
|
|
722
|
-
const vaultPath = resolveVaultPath(options.vault);
|
|
723
|
-
const { wake } = await import('../dist/commands/wake.js');
|
|
724
|
-
const { formatRecoveryInfo } = await import('../dist/commands/recover.js');
|
|
725
|
-
const result = await wake({
|
|
726
|
-
vaultPath,
|
|
727
|
-
handoffLimit: parseInt(options.handoffLimit),
|
|
728
|
-
brief: !options.full
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
console.log(chalk.cyan('\n🌅 ClawVault Wake\n'));
|
|
732
|
-
console.log(formatRecoveryInfo(result.recovery));
|
|
733
|
-
console.log();
|
|
734
|
-
console.log(chalk.cyan('Recap'));
|
|
735
|
-
console.log(result.recapMarkdown.trim());
|
|
736
|
-
console.log();
|
|
737
|
-
console.log(chalk.green(`You were working on: ${result.summary}`));
|
|
738
|
-
|
|
739
|
-
process.exitCode = result.recovery.died ? 1 : 0;
|
|
740
|
-
} catch (err) {
|
|
741
|
-
if (err instanceof QmdUnavailableError) {
|
|
742
|
-
printQmdMissing();
|
|
743
|
-
process.exit(1);
|
|
744
|
-
}
|
|
745
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
746
|
-
process.exit(1);
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
|
|
750
|
-
// === SLEEP (session end) ===
|
|
751
|
-
program
|
|
752
|
-
.command('sleep <summary>')
|
|
753
|
-
.description('End a session with a handoff (and optional git commit)')
|
|
754
|
-
.option('-n, --next <items>', 'Next steps (comma-separated)')
|
|
755
|
-
.option('-b, --blocked <items>', 'Blocked items (comma-separated)')
|
|
756
|
-
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
757
|
-
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
758
|
-
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
759
|
-
.option('-s, --session <key>', 'Session key')
|
|
760
|
-
.option('--session-transcript <path>', 'Session transcript path for auto-observe')
|
|
761
|
-
.option('--index', 'Update qmd index after handoff')
|
|
762
|
-
.option('--no-git', 'Skip git commit prompt')
|
|
763
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
764
|
-
.action(async (summary, options) => {
|
|
765
|
-
try {
|
|
766
|
-
const vaultPath = resolveVaultPath(options.vault);
|
|
767
|
-
const { sleep } = await import('../dist/commands/sleep.js');
|
|
768
|
-
const result = await sleep({
|
|
769
|
-
workingOn: summary,
|
|
770
|
-
next: options.next,
|
|
771
|
-
blocked: options.blocked,
|
|
772
|
-
decisions: options.decisions,
|
|
773
|
-
questions: options.questions,
|
|
774
|
-
feeling: options.feeling,
|
|
775
|
-
sessionKey: options.session,
|
|
776
|
-
sessionTranscript: options.sessionTranscript,
|
|
777
|
-
vaultPath,
|
|
778
|
-
index: options.index,
|
|
779
|
-
git: options.git
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
console.log(chalk.green(`✓ Handoff saved: ${result.document.id}`));
|
|
783
|
-
console.log(chalk.dim(` Path: ${result.document.path}`));
|
|
784
|
-
console.log(chalk.dim(` Working on: ${result.handoff.workingOn.join(', ')}`));
|
|
785
|
-
if (result.handoff.nextSteps.length > 0) {
|
|
786
|
-
console.log(chalk.dim(` Next: ${result.handoff.nextSteps.join(', ')}`));
|
|
787
|
-
} else {
|
|
788
|
-
console.log(chalk.dim(' Next: (none)'));
|
|
789
|
-
}
|
|
790
|
-
if (result.handoff.blocked.length > 0) {
|
|
791
|
-
console.log(chalk.dim(` Blocked: ${result.handoff.blocked.join(', ')}`));
|
|
792
|
-
} else {
|
|
793
|
-
console.log(chalk.dim(' Blocked: (none)'));
|
|
794
|
-
}
|
|
795
|
-
if (result.handoff.decisions?.length) {
|
|
796
|
-
console.log(chalk.dim(` Decisions: ${result.handoff.decisions.join(', ')}`));
|
|
797
|
-
}
|
|
798
|
-
if (result.handoff.openQuestions?.length) {
|
|
799
|
-
console.log(chalk.dim(` Questions: ${result.handoff.openQuestions.join(', ')}`));
|
|
800
|
-
}
|
|
801
|
-
if (result.handoff.feeling) {
|
|
802
|
-
console.log(chalk.dim(` Feeling: ${result.handoff.feeling}`));
|
|
803
|
-
}
|
|
804
|
-
if (options.index) {
|
|
805
|
-
console.log(chalk.dim(' qmd: index updated'));
|
|
806
|
-
}
|
|
807
|
-
if (result.git) {
|
|
808
|
-
if (result.git.committed) {
|
|
809
|
-
console.log(chalk.green(`✓ Git commit created${result.git.message ? `: ${result.git.message}` : ''}`));
|
|
810
|
-
} else if (result.git.skippedReason === 'clean') {
|
|
811
|
-
console.log(chalk.dim(' Git: clean'));
|
|
812
|
-
} else if (result.git.skippedReason === 'declined') {
|
|
813
|
-
console.log(chalk.dim(' Git: commit skipped'));
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
if (result.observationRoutingSummary) {
|
|
817
|
-
console.log(chalk.dim(` Observe: ${result.observationRoutingSummary}`));
|
|
818
|
-
}
|
|
819
|
-
} catch (err) {
|
|
820
|
-
if (err instanceof QmdUnavailableError) {
|
|
821
|
-
printQmdMissing();
|
|
822
|
-
process.exit(1);
|
|
823
|
-
}
|
|
824
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
825
|
-
process.exit(1);
|
|
826
|
-
}
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
// === HANDOFF (session bridge) ===
|
|
830
|
-
program
|
|
831
|
-
.command('handoff')
|
|
832
|
-
.description('Create a session handoff document')
|
|
833
|
-
.requiredOption('-w, --working-on <items>', 'What I was working on (comma-separated)')
|
|
834
|
-
.option('-b, --blocked <items>', 'What is blocked (comma-separated)')
|
|
835
|
-
.option('-n, --next <items>', 'What comes next (comma-separated)')
|
|
836
|
-
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
837
|
-
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
838
|
-
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
839
|
-
.option('-s, --session <key>', 'Session key')
|
|
840
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
841
|
-
.option('--no-index', 'Skip qmd index update (auto-updates by default)')
|
|
842
|
-
.option('--json', 'Output as JSON')
|
|
843
|
-
.action(async (options) => {
|
|
844
|
-
try {
|
|
845
|
-
const vault = await getVault(options.vault);
|
|
846
|
-
|
|
847
|
-
const handoff = {
|
|
848
|
-
workingOn: options.workingOn.split(',').map(s => s.trim()),
|
|
849
|
-
blocked: options.blocked ? options.blocked.split(',').map(s => s.trim()) : [],
|
|
850
|
-
nextSteps: options.next ? options.next.split(',').map(s => s.trim()) : [],
|
|
851
|
-
decisions: options.decisions ? options.decisions.split(',').map(s => s.trim()) : undefined,
|
|
852
|
-
openQuestions: options.questions ? options.questions.split(',').map(s => s.trim()) : undefined,
|
|
853
|
-
feeling: options.feeling,
|
|
854
|
-
sessionKey: options.session
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
const doc = await vault.createHandoff(handoff);
|
|
858
|
-
|
|
859
|
-
if (!options.json) {
|
|
860
|
-
console.log(chalk.green(`✓ Handoff created: ${doc.id}`));
|
|
861
|
-
console.log(chalk.dim(` Path: ${doc.path}`));
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// Auto-update qmd index unless --no-index
|
|
865
|
-
if (options.index !== false) {
|
|
866
|
-
const collection = vault.getQmdCollection();
|
|
867
|
-
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
if (options.json) {
|
|
871
|
-
console.log(JSON.stringify({ id: doc.id, path: doc.path, handoff }, null, 2));
|
|
872
|
-
}
|
|
873
|
-
} catch (err) {
|
|
874
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
875
|
-
process.exit(1);
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
// === RECAP (session bootstrap) ===
|
|
880
|
-
program
|
|
881
|
-
.command('recap')
|
|
882
|
-
.description('Generate a session recap - who I was (bootstrap hook)')
|
|
883
|
-
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include', '3')
|
|
884
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
885
|
-
.option('--json', 'Output as JSON')
|
|
886
|
-
.option('--markdown', 'Output as markdown (default)')
|
|
887
|
-
.option('--brief', 'Minimal output for token savings')
|
|
888
|
-
.action(async (options) => {
|
|
889
|
-
try {
|
|
890
|
-
const vault = await getVault(options.vault);
|
|
891
|
-
|
|
892
|
-
const recap = await vault.generateRecap({
|
|
893
|
-
handoffLimit: parseInt(options.handoffLimit),
|
|
894
|
-
brief: options.brief
|
|
895
|
-
});
|
|
896
|
-
|
|
897
|
-
if (options.json) {
|
|
898
|
-
console.log(JSON.stringify(recap, null, 2));
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// Output as markdown (default)
|
|
903
|
-
const md = vault.formatRecap(recap, { brief: options.brief });
|
|
904
|
-
console.log(md);
|
|
905
|
-
} catch (err) {
|
|
906
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
907
|
-
process.exit(1);
|
|
908
|
-
}
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
// === SHELL INIT ===
|
|
912
|
-
program
|
|
913
|
-
.command('shell-init')
|
|
914
|
-
.description('Output shell integration for ClawVault')
|
|
915
|
-
.action(async () => {
|
|
916
|
-
try {
|
|
917
|
-
const { shellInit } = await import('../dist/commands/shell-init.js');
|
|
918
|
-
console.log(shellInit());
|
|
919
|
-
} catch (err) {
|
|
920
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
921
|
-
process.exit(1);
|
|
922
|
-
}
|
|
923
|
-
});
|
|
924
|
-
|
|
925
|
-
// === TEMPLATE ===
|
|
926
|
-
const template = program
|
|
927
|
-
.command('template')
|
|
928
|
-
.description('Manage templates');
|
|
929
|
-
|
|
930
|
-
template
|
|
931
|
-
.command('list')
|
|
932
|
-
.description('List available templates')
|
|
933
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
934
|
-
.action(async (options) => {
|
|
935
|
-
try {
|
|
936
|
-
const { listTemplates } = await import('../dist/commands/template.js');
|
|
937
|
-
const templates = listTemplates({ vaultPath: options.vault });
|
|
938
|
-
if (templates.length === 0) {
|
|
939
|
-
console.log(chalk.yellow('No templates found.'));
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
console.log(chalk.cyan('\n📄 Templates:\n'));
|
|
943
|
-
for (const name of templates) {
|
|
944
|
-
console.log(`- ${name}`);
|
|
945
|
-
}
|
|
946
|
-
console.log();
|
|
947
|
-
} catch (err) {
|
|
948
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
949
|
-
process.exit(1);
|
|
950
|
-
}
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
template
|
|
954
|
-
.command('create <name>')
|
|
955
|
-
.description('Create a file from a template')
|
|
956
|
-
.option('-t, --title <title>', 'Document title')
|
|
957
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
958
|
-
.action(async (name, options) => {
|
|
959
|
-
try {
|
|
960
|
-
const { createFromTemplate } = await import('../dist/commands/template.js');
|
|
961
|
-
const result = createFromTemplate(name, {
|
|
962
|
-
title: options.title,
|
|
963
|
-
vaultPath: options.vault
|
|
964
|
-
});
|
|
965
|
-
console.log(chalk.green(`✓ Created from template: ${name}`));
|
|
966
|
-
console.log(chalk.dim(` Output: ${result.outputPath}`));
|
|
967
|
-
} catch (err) {
|
|
968
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
969
|
-
process.exit(1);
|
|
970
|
-
}
|
|
971
|
-
});
|
|
972
|
-
|
|
973
|
-
template
|
|
974
|
-
.command('add <file>')
|
|
975
|
-
.description('Add a custom template')
|
|
976
|
-
.requiredOption('--name <name>', 'Template name')
|
|
977
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
978
|
-
.action(async (file, options) => {
|
|
979
|
-
try {
|
|
980
|
-
const { addTemplate } = await import('../dist/commands/template.js');
|
|
981
|
-
const result = addTemplate(file, {
|
|
982
|
-
name: options.name,
|
|
983
|
-
vaultPath: options.vault
|
|
984
|
-
});
|
|
985
|
-
console.log(chalk.green(`✓ Template added: ${result.name}`));
|
|
986
|
-
console.log(chalk.dim(` Path: ${result.templatePath}`));
|
|
987
|
-
} catch (err) {
|
|
988
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
989
|
-
process.exit(1);
|
|
990
|
-
}
|
|
991
|
-
});
|
|
992
|
-
|
|
993
|
-
// === DOCTOR (health check) ===
|
|
994
|
-
program
|
|
995
|
-
.command('doctor')
|
|
996
|
-
.description('Check ClawVault setup health')
|
|
997
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
998
|
-
.action(async (options) => {
|
|
999
|
-
try {
|
|
1000
|
-
const { doctor } = await import('../dist/commands/doctor.js');
|
|
1001
|
-
const report = await doctor(options.vault);
|
|
1002
|
-
|
|
1003
|
-
console.log(chalk.cyan('\n🩺 ClawVault Health Check\n'));
|
|
1004
|
-
if (report.vaultPath) {
|
|
1005
|
-
console.log(chalk.dim(`Vault: ${report.vaultPath}`));
|
|
1006
|
-
console.log();
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
for (const check of report.checks) {
|
|
1010
|
-
const prefix = check.status === 'ok'
|
|
1011
|
-
? chalk.green('✓')
|
|
1012
|
-
: check.status === 'warn'
|
|
1013
|
-
? chalk.yellow('⚠')
|
|
1014
|
-
: chalk.red('✗');
|
|
1015
|
-
const detail = check.detail ? ` — ${check.detail}` : '';
|
|
1016
|
-
console.log(`${prefix} ${check.label}${detail}`);
|
|
1017
|
-
if (check.hint) {
|
|
1018
|
-
console.log(chalk.dim(` ${check.hint}`));
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
const issues = report.warnings + report.errors;
|
|
1023
|
-
console.log();
|
|
1024
|
-
if (issues === 0) {
|
|
1025
|
-
console.log(chalk.green('✅ ClawVault is healthy!\n'));
|
|
1026
|
-
} else {
|
|
1027
|
-
console.log(chalk.yellow(`⚠ ${issues} issue(s) found\n`));
|
|
1028
|
-
}
|
|
1029
|
-
} catch (err) {
|
|
1030
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1031
|
-
process.exit(1);
|
|
1032
|
-
}
|
|
1033
|
-
});
|
|
1034
|
-
|
|
1035
|
-
// === ENTITIES ===
|
|
1036
|
-
program
|
|
1037
|
-
.command('entities')
|
|
1038
|
-
.description('List all linkable entities in the vault')
|
|
1039
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1040
|
-
.option('--json', 'Output as JSON')
|
|
1041
|
-
.action(async (options) => {
|
|
1042
|
-
try {
|
|
1043
|
-
const vaultPath = options.vault || process.env.CLAWVAULT_PATH;
|
|
1044
|
-
if (!vaultPath) {
|
|
1045
|
-
console.error(chalk.red('Error: No vault path. Set CLAWVAULT_PATH or use -v'));
|
|
1046
|
-
process.exit(1);
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
const { entitiesCommand } = await import('../dist/commands/entities.js');
|
|
1050
|
-
await entitiesCommand({ json: options.json });
|
|
1051
|
-
} catch (err) {
|
|
1052
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1053
|
-
process.exit(1);
|
|
1054
|
-
}
|
|
1055
|
-
});
|
|
1056
|
-
|
|
1057
|
-
// === LINK ===
|
|
1058
|
-
program
|
|
1059
|
-
.command('link [file]')
|
|
1060
|
-
.description('Auto-link entity mentions in markdown files')
|
|
1061
|
-
.option('--all', 'Link all files in vault')
|
|
1062
|
-
.option('--backlinks <file>', 'Show backlinks to a file')
|
|
1063
|
-
.option('--dry-run', 'Show what would be linked without changing files')
|
|
1064
|
-
.option('--orphans', 'List broken wiki-links')
|
|
1065
|
-
.option('--rebuild', 'Rebuild backlinks index')
|
|
1066
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1067
|
-
.action(async (file, options) => {
|
|
1068
|
-
try {
|
|
1069
|
-
const vaultPath = options.vault || process.env.CLAWVAULT_PATH;
|
|
1070
|
-
if (!vaultPath) {
|
|
1071
|
-
console.error(chalk.red('Error: No vault path. Set CLAWVAULT_PATH or use -v'));
|
|
1072
|
-
process.exit(1);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
const { linkCommand } = await import('../dist/commands/link.js');
|
|
1076
|
-
await linkCommand(file, {
|
|
1077
|
-
all: options.all,
|
|
1078
|
-
dryRun: options.dryRun,
|
|
1079
|
-
backlinks: options.backlinks,
|
|
1080
|
-
orphans: options.orphans,
|
|
1081
|
-
rebuild: options.rebuild
|
|
1082
|
-
});
|
|
1083
|
-
} catch (err) {
|
|
1084
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1085
|
-
process.exit(1);
|
|
1086
|
-
}
|
|
1087
|
-
});
|
|
1088
|
-
|
|
1089
|
-
// === CHECKPOINT ===
|
|
1090
|
-
program
|
|
1091
|
-
.command('checkpoint')
|
|
1092
|
-
.description('Quick state checkpoint for context death resilience')
|
|
1093
|
-
.option('--working-on <text>', 'What you are currently working on')
|
|
1094
|
-
.option('--focus <text>', 'Current focus area')
|
|
1095
|
-
.option('--blocked <text>', 'What is blocking progress')
|
|
1096
|
-
.option('--urgent', 'Trigger OpenClaw wake after checkpoint')
|
|
1097
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1098
|
-
.option('--json', 'Output as JSON')
|
|
1099
|
-
.action(async (options) => {
|
|
1100
|
-
try {
|
|
1101
|
-
const vaultPath = options.vault || process.env.CLAWVAULT_PATH;
|
|
1102
|
-
if (!vaultPath) {
|
|
1103
|
-
console.error(chalk.red('Error: No vault path. Set CLAWVAULT_PATH or use -v'));
|
|
1104
|
-
process.exit(1);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
const { checkpoint } = await import('../dist/commands/checkpoint.js');
|
|
1108
|
-
const data = await checkpoint({
|
|
1109
|
-
vaultPath: path.resolve(vaultPath),
|
|
1110
|
-
workingOn: options.workingOn,
|
|
1111
|
-
focus: options.focus,
|
|
1112
|
-
blocked: options.blocked,
|
|
1113
|
-
urgent: options.urgent
|
|
1114
|
-
});
|
|
1115
|
-
|
|
1116
|
-
if (options.json) {
|
|
1117
|
-
console.log(JSON.stringify(data, null, 2));
|
|
1118
|
-
} else {
|
|
1119
|
-
console.log(chalk.green('✓ Checkpoint saved'));
|
|
1120
|
-
console.log(chalk.dim(` Timestamp: ${data.timestamp}`));
|
|
1121
|
-
if (data.workingOn) console.log(chalk.dim(` Working on: ${data.workingOn}`));
|
|
1122
|
-
if (data.focus) console.log(chalk.dim(` Focus: ${data.focus}`));
|
|
1123
|
-
if (data.blocked) console.log(chalk.dim(` Blocked: ${data.blocked}`));
|
|
1124
|
-
if (data.urgent) console.log(chalk.dim(' Urgent: yes'));
|
|
1125
|
-
}
|
|
1126
|
-
} catch (err) {
|
|
1127
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1128
|
-
process.exit(1);
|
|
1129
|
-
}
|
|
1130
|
-
});
|
|
1131
|
-
|
|
1132
|
-
// === RECOVER ===
|
|
1133
|
-
program
|
|
1134
|
-
.command('recover')
|
|
1135
|
-
.description('Check for context death and recover state')
|
|
1136
|
-
.option('--clear', 'Clear the dirty death flag after recovery')
|
|
1137
|
-
.option('--verbose', 'Show full checkpoint and handoff content')
|
|
1138
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1139
|
-
.option('--json', 'Output as JSON')
|
|
1140
|
-
.action(async (options) => {
|
|
1141
|
-
try {
|
|
1142
|
-
const vaultPath = options.vault || process.env.CLAWVAULT_PATH;
|
|
1143
|
-
if (!vaultPath) {
|
|
1144
|
-
console.error(chalk.red('Error: No vault path. Set CLAWVAULT_PATH or use -v'));
|
|
1145
|
-
process.exit(1);
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
const { recover, formatRecoveryInfo } = await import('../dist/commands/recover.js');
|
|
1149
|
-
const info = await recover(path.resolve(vaultPath), {
|
|
1150
|
-
clearFlag: options.clear,
|
|
1151
|
-
verbose: options.verbose
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
if (options.json) {
|
|
1155
|
-
console.log(JSON.stringify(info, null, 2));
|
|
1156
|
-
} else {
|
|
1157
|
-
console.log(formatRecoveryInfo(info, { verbose: options.verbose }));
|
|
1158
|
-
}
|
|
1159
|
-
} catch (err) {
|
|
1160
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1161
|
-
process.exit(1);
|
|
1162
|
-
}
|
|
1163
|
-
});
|
|
1164
|
-
|
|
1165
|
-
// === STATUS ===
|
|
1166
|
-
program
|
|
1167
|
-
.command('status')
|
|
1168
|
-
.description('Show vault health and status')
|
|
1169
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1170
|
-
.option('--json', 'Output as JSON')
|
|
1171
|
-
.action(async (options) => {
|
|
1172
|
-
try {
|
|
1173
|
-
const vaultPath = options.vault || process.env.CLAWVAULT_PATH;
|
|
1174
|
-
if (!vaultPath) {
|
|
1175
|
-
console.error(chalk.red('Error: No vault path. Set CLAWVAULT_PATH or use -v'));
|
|
1176
|
-
process.exit(1);
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
const { statusCommand } = await import('../dist/commands/status.js');
|
|
1180
|
-
await statusCommand(path.resolve(vaultPath), { json: options.json });
|
|
1181
|
-
} catch (err) {
|
|
1182
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1183
|
-
process.exit(1);
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
|
|
1187
|
-
// === CLEAN-EXIT ===
|
|
1188
|
-
program
|
|
1189
|
-
.command('clean-exit')
|
|
1190
|
-
.description('Mark session as cleanly exited (clears dirty death flag)')
|
|
1191
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1192
|
-
.action(async (options) => {
|
|
1193
|
-
try {
|
|
1194
|
-
const vaultPath = options.vault || process.env.CLAWVAULT_PATH;
|
|
1195
|
-
if (!vaultPath) {
|
|
1196
|
-
console.error(chalk.red('Error: No vault path. Set CLAWVAULT_PATH or use -v'));
|
|
1197
|
-
process.exit(1);
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
const { cleanExit } = await import('../dist/commands/checkpoint.js');
|
|
1201
|
-
await cleanExit(path.resolve(vaultPath));
|
|
1202
|
-
console.log(chalk.green('✓ Clean exit recorded'));
|
|
1203
|
-
} catch (err) {
|
|
1204
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1205
|
-
process.exit(1);
|
|
1206
|
-
}
|
|
1207
|
-
});
|
|
1208
|
-
|
|
1209
|
-
// === REPAIR-SESSION ===
|
|
1210
|
-
program
|
|
1211
|
-
.command('repair-session')
|
|
1212
|
-
.description('Repair corrupted OpenClaw session transcripts')
|
|
1213
|
-
.option('-s, --session <id>', 'Session ID (defaults to current main session)')
|
|
1214
|
-
.option('-a, --agent <id>', 'Agent ID (defaults to configured agent)')
|
|
1215
|
-
.option('--backup', 'Create backup before repair (default: true)', true)
|
|
1216
|
-
.option('--no-backup', 'Skip backup creation')
|
|
1217
|
-
.option('--dry-run', 'Show what would be repaired without writing')
|
|
1218
|
-
.option('--list', 'List available sessions')
|
|
1219
|
-
.option('--json', 'Output as JSON')
|
|
1220
|
-
.action(async (options) => {
|
|
1221
|
-
try {
|
|
1222
|
-
const {
|
|
1223
|
-
repairSessionCommand,
|
|
1224
|
-
formatRepairResult,
|
|
1225
|
-
listAgentSessions
|
|
1226
|
-
} = await import('../dist/commands/repair-session.js');
|
|
1227
|
-
|
|
1228
|
-
// List mode
|
|
1229
|
-
if (options.list) {
|
|
1230
|
-
console.log(listAgentSessions(options.agent));
|
|
1231
|
-
return;
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
const result = await repairSessionCommand({
|
|
1235
|
-
sessionId: options.session,
|
|
1236
|
-
agentId: options.agent,
|
|
1237
|
-
backup: options.backup,
|
|
1238
|
-
dryRun: options.dryRun
|
|
1239
|
-
});
|
|
1240
|
-
|
|
1241
|
-
if (options.json) {
|
|
1242
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1243
|
-
} else {
|
|
1244
|
-
console.log(formatRepairResult(result, { dryRun: options.dryRun }));
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// Exit with code 1 if corruption was found but not fixed (dry-run)
|
|
1248
|
-
if (result.corruptedEntries.length > 0 && !result.repaired) {
|
|
1249
|
-
process.exit(1);
|
|
1250
|
-
}
|
|
1251
|
-
} catch (err) {
|
|
1252
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1253
|
-
process.exit(1);
|
|
1254
|
-
}
|
|
1255
|
-
});
|
|
1256
|
-
|
|
1257
|
-
// === DASHBOARD ===
|
|
1258
|
-
program
|
|
1259
|
-
.command('dashboard')
|
|
1260
|
-
.description('Run local vault graph dashboard')
|
|
1261
|
-
.option('-p, --port <port>', 'Dashboard port', '3377')
|
|
1262
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1263
|
-
.action(async (options) => {
|
|
1264
|
-
try {
|
|
1265
|
-
const parsedPort = Number.parseInt(options.port, 10);
|
|
1266
|
-
if (Number.isNaN(parsedPort)) {
|
|
1267
|
-
console.error(chalk.red(`Error: Invalid port: ${options.port}`));
|
|
1268
|
-
process.exit(1);
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
const vaultPath = options.vault
|
|
1272
|
-
? path.resolve(options.vault)
|
|
1273
|
-
: resolveVaultPath(undefined);
|
|
47
|
+
registerCoreCommands(program, {
|
|
48
|
+
chalk,
|
|
49
|
+
path,
|
|
50
|
+
fs,
|
|
51
|
+
createVault,
|
|
52
|
+
getVault,
|
|
53
|
+
runQmd
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
registerQueryCommands(program, {
|
|
57
|
+
chalk,
|
|
58
|
+
getVault,
|
|
59
|
+
resolveVaultPath,
|
|
60
|
+
QmdUnavailableError,
|
|
61
|
+
printQmdMissing
|
|
62
|
+
});
|
|
1274
63
|
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
64
|
+
registerSessionLifecycleCommands(program, {
|
|
65
|
+
chalk,
|
|
66
|
+
resolveVaultPath,
|
|
67
|
+
QmdUnavailableError,
|
|
68
|
+
printQmdMissing,
|
|
69
|
+
getVault,
|
|
70
|
+
runQmd
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
registerTemplateCommands(program, { chalk });
|
|
74
|
+
registerMaintenanceCommands(program, { chalk });
|
|
75
|
+
registerResilienceCommands(program, { chalk, resolveVaultPath });
|
|
76
|
+
registerVaultOperationsCommands(program, {
|
|
77
|
+
chalk,
|
|
78
|
+
fs,
|
|
79
|
+
getVault,
|
|
80
|
+
runQmd,
|
|
81
|
+
resolveVaultPath,
|
|
82
|
+
path
|
|
83
|
+
});
|
|
1285
84
|
|
|
1286
85
|
// Parse and run
|
|
1287
86
|
program.parse();
|