context-vault 3.4.4 → 3.5.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/assets/agent-rules.md +50 -0
- package/assets/setup-prompt.md +58 -0
- package/assets/skills/vault-setup/skill.md +81 -0
- package/bin/cli.js +753 -126
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +23 -0
- package/dist/helpers.js.map +1 -1
- package/dist/server.js +26 -2
- package/dist/server.js.map +1 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +29 -0
- package/dist/status.js.map +1 -1
- package/dist/tools/context-status.d.ts.map +1 -1
- package/dist/tools/context-status.js +64 -29
- package/dist/tools/context-status.js.map +1 -1
- package/dist/tools/get-context.js +23 -18
- package/dist/tools/get-context.js.map +1 -1
- package/dist/tools/list-context.d.ts +2 -1
- package/dist/tools/list-context.d.ts.map +1 -1
- package/dist/tools/list-context.js +27 -10
- package/dist/tools/list-context.js.map +1 -1
- package/dist/tools/save-context.d.ts +2 -1
- package/dist/tools/save-context.d.ts.map +1 -1
- package/dist/tools/save-context.js +95 -26
- package/dist/tools/save-context.js.map +1 -1
- package/dist/tools/session-start.d.ts.map +1 -1
- package/dist/tools/session-start.js +230 -11
- package/dist/tools/session-start.js.map +1 -1
- package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/capture.js +13 -0
- package/node_modules/@context-vault/core/dist/capture.js.map +1 -1
- package/node_modules/@context-vault/core/dist/config.d.ts +8 -0
- package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/config.js +47 -2
- package/node_modules/@context-vault/core/dist/config.js.map +1 -1
- package/node_modules/@context-vault/core/dist/constants.d.ts +13 -0
- package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/constants.js +13 -0
- package/node_modules/@context-vault/core/dist/constants.js.map +1 -1
- package/node_modules/@context-vault/core/dist/db.d.ts +1 -1
- package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/db.js +73 -9
- package/node_modules/@context-vault/core/dist/db.js.map +1 -1
- package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/frontmatter.js +2 -0
- package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -1
- package/node_modules/@context-vault/core/dist/index.d.ts +4 -1
- package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/index.js +58 -10
- package/node_modules/@context-vault/core/dist/index.js.map +1 -1
- package/node_modules/@context-vault/core/dist/indexing.d.ts +8 -0
- package/node_modules/@context-vault/core/dist/indexing.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/indexing.js +22 -0
- package/node_modules/@context-vault/core/dist/indexing.js.map +1 -0
- package/node_modules/@context-vault/core/dist/main.d.ts +3 -2
- package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/main.js +3 -1
- package/node_modules/@context-vault/core/dist/main.js.map +1 -1
- package/node_modules/@context-vault/core/dist/search.d.ts +2 -0
- package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/search.js +82 -6
- package/node_modules/@context-vault/core/dist/search.js.map +1 -1
- package/node_modules/@context-vault/core/dist/types.d.ts +24 -0
- package/node_modules/@context-vault/core/dist/types.d.ts.map +1 -1
- package/node_modules/@context-vault/core/package.json +5 -1
- package/node_modules/@context-vault/core/src/capture.ts +11 -0
- package/node_modules/@context-vault/core/src/config.ts +40 -2
- package/node_modules/@context-vault/core/src/constants.ts +15 -0
- package/node_modules/@context-vault/core/src/db.ts +73 -9
- package/node_modules/@context-vault/core/src/frontmatter.ts +2 -0
- package/node_modules/@context-vault/core/src/index.ts +65 -11
- package/node_modules/@context-vault/core/src/indexing.ts +35 -0
- package/node_modules/@context-vault/core/src/main.ts +5 -0
- package/node_modules/@context-vault/core/src/search.ts +96 -6
- package/node_modules/@context-vault/core/src/types.ts +26 -0
- package/package.json +2 -2
- package/scripts/prepack.js +17 -0
- package/src/helpers.ts +25 -0
- package/src/server.ts +28 -2
- package/src/status.ts +35 -0
- package/src/tools/context-status.ts +65 -30
- package/src/tools/get-context.ts +24 -24
- package/src/tools/list-context.ts +25 -13
- package/src/tools/save-context.ts +106 -29
- package/src/tools/session-start.ts +257 -13
package/bin/cli.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Suppress Node.js ExperimentalWarning for built-in SQLite (used by context-vault)
|
|
4
|
+
const originalEmit = process.emit;
|
|
5
|
+
process.emit = function (name, data, ...args) {
|
|
6
|
+
if (name === 'warning' && typeof data === 'object' && data?.name === 'ExperimentalWarning' &&
|
|
7
|
+
typeof data?.message === 'string' && data.message.includes('SQLite')) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return originalEmit.call(process, name, data, ...args);
|
|
11
|
+
};
|
|
12
|
+
|
|
3
13
|
// Node.js version guard — must run before any ESM imports
|
|
4
14
|
const nodeVersion = parseInt(process.versions.node.split('.')[0], 10);
|
|
5
15
|
if (nodeVersion < 22) {
|
|
@@ -47,6 +57,7 @@ import { homedir, platform } from 'node:os';
|
|
|
47
57
|
import { execSync, execFile, execFileSync, spawn } from 'node:child_process';
|
|
48
58
|
import { fileURLToPath } from 'node:url';
|
|
49
59
|
import { APP_URL, API_URL, MARKETING_URL } from '@context-vault/core/constants';
|
|
60
|
+
import { assertNotTestMode } from '@context-vault/core/config';
|
|
50
61
|
|
|
51
62
|
const __filename = fileURLToPath(import.meta.url);
|
|
52
63
|
const __dirname = dirname(__filename);
|
|
@@ -241,12 +252,16 @@ const TOOLS = [
|
|
|
241
252
|
name: 'Claude Code',
|
|
242
253
|
detect: () => commandExistsAsync('claude'),
|
|
243
254
|
configType: 'cli',
|
|
255
|
+
rulesPath: join(HOME, '.claude', 'rules', 'context-vault.md'),
|
|
256
|
+
rulesMethod: 'write',
|
|
244
257
|
},
|
|
245
258
|
{
|
|
246
259
|
id: 'codex',
|
|
247
260
|
name: 'Codex',
|
|
248
261
|
detect: () => commandExistsAsync('codex'),
|
|
249
262
|
configType: 'cli',
|
|
263
|
+
rulesPath: null,
|
|
264
|
+
rulesMethod: null,
|
|
250
265
|
},
|
|
251
266
|
{
|
|
252
267
|
id: 'claude-desktop',
|
|
@@ -255,6 +270,8 @@ const TOOLS = [
|
|
|
255
270
|
configType: 'json',
|
|
256
271
|
configPath: join(appDataDir(), 'Claude', 'claude_desktop_config.json'),
|
|
257
272
|
configKey: 'mcpServers',
|
|
273
|
+
rulesPath: null,
|
|
274
|
+
rulesMethod: null,
|
|
258
275
|
},
|
|
259
276
|
{
|
|
260
277
|
id: 'cursor',
|
|
@@ -263,6 +280,8 @@ const TOOLS = [
|
|
|
263
280
|
configType: 'json',
|
|
264
281
|
configPath: join(HOME, '.cursor', 'mcp.json'),
|
|
265
282
|
configKey: 'mcpServers',
|
|
283
|
+
rulesPath: join(HOME, '.cursor', 'rules', 'context-vault.mdc'),
|
|
284
|
+
rulesMethod: 'write',
|
|
266
285
|
},
|
|
267
286
|
{
|
|
268
287
|
id: 'windsurf',
|
|
@@ -275,6 +294,8 @@ const TOOLS = [
|
|
|
275
294
|
: join(HOME, '.codeium', 'windsurf', 'mcp_config.json');
|
|
276
295
|
},
|
|
277
296
|
configKey: 'mcpServers',
|
|
297
|
+
rulesPath: join(HOME, '.windsurfrules'),
|
|
298
|
+
rulesMethod: 'append',
|
|
278
299
|
},
|
|
279
300
|
{
|
|
280
301
|
id: 'antigravity',
|
|
@@ -283,6 +304,8 @@ const TOOLS = [
|
|
|
283
304
|
configType: 'json',
|
|
284
305
|
configPath: join(HOME, '.gemini', 'antigravity', 'mcp_config.json'),
|
|
285
306
|
configKey: 'mcpServers',
|
|
307
|
+
rulesPath: null,
|
|
308
|
+
rulesMethod: null,
|
|
286
309
|
},
|
|
287
310
|
{
|
|
288
311
|
id: 'cline',
|
|
@@ -296,6 +319,8 @@ const TOOLS = [
|
|
|
296
319
|
'cline_mcp_settings.json'
|
|
297
320
|
),
|
|
298
321
|
configKey: 'mcpServers',
|
|
322
|
+
rulesPath: null,
|
|
323
|
+
rulesMethod: null,
|
|
299
324
|
},
|
|
300
325
|
{
|
|
301
326
|
id: 'roo-code',
|
|
@@ -309,6 +334,8 @@ const TOOLS = [
|
|
|
309
334
|
'cline_mcp_settings.json'
|
|
310
335
|
),
|
|
311
336
|
configKey: 'mcpServers',
|
|
337
|
+
rulesPath: null,
|
|
338
|
+
rulesMethod: null,
|
|
312
339
|
},
|
|
313
340
|
];
|
|
314
341
|
|
|
@@ -353,12 +380,14 @@ ${bold('Commands:')}
|
|
|
353
380
|
${cyan('hooks')} install|uninstall Install or remove Claude Code memory hook
|
|
354
381
|
${cyan('claude')} install|uninstall Alias for hooks install|uninstall
|
|
355
382
|
${cyan('skills')} install Install bundled Claude Code skills
|
|
383
|
+
${cyan('rules')} install Install agent rules for detected AI tools
|
|
356
384
|
${cyan('health')} Quick health check — vault, DB, entry count
|
|
357
385
|
${cyan('status')} Show vault diagnostics
|
|
358
386
|
${cyan('doctor')} Diagnose and repair common issues
|
|
359
387
|
${cyan('debug')} Generate AI-pasteable debug report
|
|
360
388
|
${cyan('daemon')} start|stop|status Run vault as a shared HTTP daemon (one process, all sessions)
|
|
361
389
|
${cyan('restart')} Stop running MCP server processes (client auto-restarts)
|
|
390
|
+
${cyan('reconnect')} Fix vault path, kill stale servers, re-register MCP, reindex
|
|
362
391
|
${cyan('search')} Search vault entries from CLI
|
|
363
392
|
${cyan('save')} Save an entry to the vault from CLI
|
|
364
393
|
${cyan('import')} <path> Import entries from file, directory, or .zip archive
|
|
@@ -545,7 +574,7 @@ async function runSetup() {
|
|
|
545
574
|
}
|
|
546
575
|
} catch {}
|
|
547
576
|
|
|
548
|
-
console.log(`\n ${dim('[2/
|
|
577
|
+
console.log(`\n ${dim('[2/3]')}${bold(' Configuring tools...\n')}`);
|
|
549
578
|
for (const tool of selected) {
|
|
550
579
|
try {
|
|
551
580
|
if (tool.configType === 'cli' && tool.id === 'codex') {
|
|
@@ -561,6 +590,40 @@ async function runSetup() {
|
|
|
561
590
|
}
|
|
562
591
|
}
|
|
563
592
|
|
|
593
|
+
// Offer rules installation for users who previously skipped or used an older version
|
|
594
|
+
console.log(`\n ${dim('[3/3]')}${bold(' Agent rules...\n')}`);
|
|
595
|
+
const rulesContent = loadAgentRules();
|
|
596
|
+
if (rulesContent && !flags.has('--no-rules')) {
|
|
597
|
+
const missingRules = selected.filter((t) => {
|
|
598
|
+
const p = getRulesPathForTool(t);
|
|
599
|
+
return p && !existsSync(p);
|
|
600
|
+
});
|
|
601
|
+
if (missingRules.length > 0) {
|
|
602
|
+
console.log(dim(' Agent rules teach your AI when to save knowledge automatically.'));
|
|
603
|
+
console.log(dim(' No rules file detected for: ' + missingRules.map((t) => t.name).join(', ')));
|
|
604
|
+
console.log();
|
|
605
|
+
const rulesAnswer = await prompt(' Install agent rules? (Y/n):', 'Y');
|
|
606
|
+
if (rulesAnswer.toLowerCase() !== 'n') {
|
|
607
|
+
for (const tool of missingRules) {
|
|
608
|
+
try {
|
|
609
|
+
const ok = installAgentRulesForTool(tool, rulesContent);
|
|
610
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
611
|
+
if (ok) {
|
|
612
|
+
console.log(` ${green('+')} ${tool.name} — agent rules installed`);
|
|
613
|
+
if (rulesPath) console.log(` ${dim(rulesPath)}`);
|
|
614
|
+
}
|
|
615
|
+
} catch (e) {
|
|
616
|
+
console.log(` ${red('x')} ${tool.name} — ${e.message}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
} else {
|
|
620
|
+
console.log(dim(' Skipped — install later: context-vault rules install'));
|
|
621
|
+
}
|
|
622
|
+
} else {
|
|
623
|
+
console.log(dim(' Agent rules already installed — skipping.'));
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
564
627
|
console.log();
|
|
565
628
|
console.log(green(' ✓ Tool configs updated.'));
|
|
566
629
|
console.log(dim(' Restart your AI tools to apply the changes.'));
|
|
@@ -572,7 +635,7 @@ async function runSetup() {
|
|
|
572
635
|
}
|
|
573
636
|
|
|
574
637
|
// Detect tools
|
|
575
|
-
console.log(dim(` [1/
|
|
638
|
+
console.log(dim(` [1/7]`) + bold(' Detecting tools...\n'));
|
|
576
639
|
verbose(userLevel, 'Scanning for AI tools on this machine.');
|
|
577
640
|
if (userLevel === 'beginner') console.log();
|
|
578
641
|
const { detected, results: detectionResults } = await detectAllTools();
|
|
@@ -643,8 +706,21 @@ async function runSetup() {
|
|
|
643
706
|
}
|
|
644
707
|
}
|
|
645
708
|
|
|
709
|
+
// Fast path for new users: recommended defaults
|
|
710
|
+
let useRecommendedDefaults = false;
|
|
711
|
+
const existingConfigForFastPath = join(HOME, '.context-mcp', 'config.json');
|
|
712
|
+
const isNewInstall = !existsSync(existingConfigForFastPath);
|
|
713
|
+
if (isNewInstall && !isNonInteractive) {
|
|
714
|
+
console.log(dim(' Install with recommended settings?'));
|
|
715
|
+
console.log(dim(' Vault in default location, all hooks, skills, and rules installed.'));
|
|
716
|
+
console.log();
|
|
717
|
+
const fastAnswer = await prompt(' Install with recommended settings? (Y/n):', 'Y');
|
|
718
|
+
useRecommendedDefaults = fastAnswer.toLowerCase() !== 'n';
|
|
719
|
+
if (useRecommendedDefaults) console.log();
|
|
720
|
+
}
|
|
721
|
+
|
|
646
722
|
// Vault directory (content files)
|
|
647
|
-
console.log(dim(` [2/
|
|
723
|
+
console.log(dim(` [2/7]`) + bold(' Configuring vault...\n'));
|
|
648
724
|
verbose(userLevel, 'Your vault is a folder of plain markdown files — you own it.');
|
|
649
725
|
if (userLevel === 'beginner') console.log();
|
|
650
726
|
|
|
@@ -664,7 +740,7 @@ async function runSetup() {
|
|
|
664
740
|
}
|
|
665
741
|
}
|
|
666
742
|
|
|
667
|
-
if (!getFlag('--vault-dir') && !isNonInteractive) {
|
|
743
|
+
if (!getFlag('--vault-dir') && !isNonInteractive && !useRecommendedDefaults) {
|
|
668
744
|
const existingVaults = scanForVaults();
|
|
669
745
|
if (existingVaults.length === 1) {
|
|
670
746
|
console.log(
|
|
@@ -696,9 +772,16 @@ async function runSetup() {
|
|
|
696
772
|
}
|
|
697
773
|
console.log();
|
|
698
774
|
}
|
|
775
|
+
} else if (!getFlag('--vault-dir') && useRecommendedDefaults) {
|
|
776
|
+
// Fast path: still use detected vaults if found
|
|
777
|
+
const existingVaults = scanForVaults();
|
|
778
|
+
if (existingVaults.length >= 1) {
|
|
779
|
+
defaultVaultDir = existingVaults[0].path;
|
|
780
|
+
console.log(` ${green('+')} Using existing vault at ${defaultVaultDir}`);
|
|
781
|
+
}
|
|
699
782
|
}
|
|
700
783
|
|
|
701
|
-
const vaultDir = isNonInteractive
|
|
784
|
+
const vaultDir = (isNonInteractive || useRecommendedDefaults)
|
|
702
785
|
? defaultVaultDir
|
|
703
786
|
: await prompt(` Vault directory:`, defaultVaultDir);
|
|
704
787
|
let resolvedVaultDir = resolve(vaultDir);
|
|
@@ -710,7 +793,7 @@ async function runSetup() {
|
|
|
710
793
|
console.error(dim(` Remove or rename the file, then run setup again.\n`));
|
|
711
794
|
process.exit(1);
|
|
712
795
|
}
|
|
713
|
-
} else if (isNonInteractive) {
|
|
796
|
+
} else if (isNonInteractive || useRecommendedDefaults) {
|
|
714
797
|
mkdirSync(resolvedVaultDir, { recursive: true });
|
|
715
798
|
console.log(`\n ${green('+')} Created ${resolvedVaultDir}`);
|
|
716
799
|
} else {
|
|
@@ -786,41 +869,18 @@ async function runSetup() {
|
|
|
786
869
|
vaultConfig.devDir = join(HOME, 'dev');
|
|
787
870
|
vaultConfig.mode = 'local';
|
|
788
871
|
|
|
789
|
-
|
|
790
|
-
console.log(`\n ${dim('[3/6]')}${bold(' Anonymous error reporting\n')}`);
|
|
791
|
-
verbose(userLevel, 'Entirely optional — works identically either way.\n');
|
|
792
|
-
console.log(dim(' When enabled, unhandled errors send a minimal event (type, tool name,'));
|
|
793
|
-
console.log(dim(' version, platform) to help diagnose issues. No vault content,'));
|
|
794
|
-
console.log(dim(' file paths, or personal data is ever sent. Off by default.'));
|
|
795
|
-
console.log(dim(` Full schema: ${MARKETING_URL}/telemetry`));
|
|
796
|
-
console.log();
|
|
797
|
-
|
|
798
|
-
let telemetryEnabled = vaultConfig.telemetry === true;
|
|
799
|
-
if (!isNonInteractive) {
|
|
800
|
-
const defaultChoice = telemetryEnabled ? 'Y' : 'n';
|
|
801
|
-
const telemetryAnswer = await prompt(
|
|
802
|
-
` Enable anonymous error reporting? (y/N):`,
|
|
803
|
-
defaultChoice
|
|
804
|
-
);
|
|
805
|
-
telemetryEnabled =
|
|
806
|
-
telemetryAnswer.toLowerCase() === 'y' || telemetryAnswer.toLowerCase() === 'yes';
|
|
807
|
-
}
|
|
808
|
-
vaultConfig.telemetry = telemetryEnabled;
|
|
809
|
-
console.log(
|
|
810
|
-
` ${telemetryEnabled ? green('+') : dim('-')} Telemetry: ${telemetryEnabled ? 'enabled' : 'disabled'}`
|
|
811
|
-
);
|
|
812
|
-
|
|
872
|
+
assertNotTestMode(configPath);
|
|
813
873
|
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + '\n');
|
|
814
874
|
console.log(`\n ${green('+')} Wrote ${configPath}`);
|
|
815
875
|
|
|
816
876
|
// Pre-download embedding model with spinner (skip with --skip-embeddings)
|
|
817
877
|
const skipEmbeddings = flags.has('--skip-embeddings');
|
|
818
878
|
if (skipEmbeddings) {
|
|
819
|
-
console.log(`\n ${dim('[
|
|
879
|
+
console.log(`\n ${dim('[3/7]')}${bold(' Embedding model')} ${dim('(skipped)')}`);
|
|
820
880
|
console.log(dim(' FTS-only mode — full-text search works, semantic search disabled.'));
|
|
821
881
|
console.log(dim(' To enable later: context-vault setup (without --skip-embeddings)'));
|
|
822
882
|
} else {
|
|
823
|
-
console.log(`\n ${dim('[
|
|
883
|
+
console.log(`\n ${dim('[3/7]')}${bold(' Downloading embedding model...')}`);
|
|
824
884
|
verbose(userLevel, 'Enables meaning-based search. ~22MB download, runs fully offline.');
|
|
825
885
|
console.log(dim(' all-MiniLM-L6-v2 (~22MB, one-time download)'));
|
|
826
886
|
console.log(dim(` Slow connection? Re-run with --skip-embeddings (enables FTS-only mode)\n`));
|
|
@@ -902,12 +962,11 @@ async function runSetup() {
|
|
|
902
962
|
} catch {}
|
|
903
963
|
}
|
|
904
964
|
|
|
905
|
-
// Configure each tool — pass vault dir
|
|
906
|
-
console.log(`\n ${dim('[
|
|
965
|
+
// Configure each tool — always pass vault dir explicitly to prevent config drift
|
|
966
|
+
console.log(`\n ${dim('[4/7]')}${bold(' Configuring tools...\n')}`);
|
|
907
967
|
verbose(userLevel, 'Writing config so your AI tool can find your vault.\n');
|
|
908
968
|
const results = [];
|
|
909
|
-
const
|
|
910
|
-
const customVaultDir = resolvedVaultDir !== resolve(defaultVDir) ? resolvedVaultDir : null;
|
|
969
|
+
const customVaultDir = resolvedVaultDir;
|
|
911
970
|
|
|
912
971
|
for (const tool of selected) {
|
|
913
972
|
try {
|
|
@@ -926,103 +985,107 @@ async function runSetup() {
|
|
|
926
985
|
}
|
|
927
986
|
}
|
|
928
987
|
|
|
929
|
-
// Claude Code hooks (
|
|
988
|
+
// Claude Code extras: hooks, skills, rules (bundled into one step)
|
|
989
|
+
console.log(`\n ${dim('[5/7]')}${bold(' Extras...\n')}`);
|
|
930
990
|
const claudeConfigured = results.some((r) => r.ok && r.tool.id === 'claude-code');
|
|
931
991
|
const hookFlag = flags.has('--hooks');
|
|
992
|
+
const configuredTools = results.filter((r) => r.ok).map((r) => r.tool);
|
|
993
|
+
const installedRulesPaths = [];
|
|
994
|
+
|
|
932
995
|
if (claudeConfigured) {
|
|
933
|
-
//
|
|
934
|
-
let
|
|
935
|
-
if (!hookFlag && !isNonInteractive) {
|
|
996
|
+
// Bundled hooks prompt: one Y/n for all three hooks
|
|
997
|
+
let installHooks = hookFlag || useRecommendedDefaults;
|
|
998
|
+
if (!hookFlag && !isNonInteractive && !useRecommendedDefaults) {
|
|
999
|
+
console.log(dim(' Install Claude Code hooks? (recommended)'));
|
|
1000
|
+
console.log(dim(' Memory recall, session capture, and auto-capture.'));
|
|
936
1001
|
console.log();
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
console.log(dim(" as additional context alongside Claude's native memory."));
|
|
940
|
-
console.log();
|
|
941
|
-
const answer = await prompt(' Install Claude Code memory hook? (y/N):', 'N');
|
|
942
|
-
installHook = answer.toLowerCase() === 'y';
|
|
1002
|
+
const answer = await prompt(' Install Claude Code hooks? (Y/n):', 'Y');
|
|
1003
|
+
installHooks = answer.toLowerCase() !== 'n';
|
|
943
1004
|
}
|
|
944
|
-
if (
|
|
1005
|
+
if (installHooks) {
|
|
945
1006
|
try {
|
|
946
|
-
const
|
|
947
|
-
if (
|
|
948
|
-
console.log(`\n ${green('+')} Memory hook installed`);
|
|
949
|
-
}
|
|
1007
|
+
const hookInstalled = installClaudeHook();
|
|
1008
|
+
if (hookInstalled) console.log(` ${green('+')} Memory recall hook installed`);
|
|
950
1009
|
} catch (e) {
|
|
951
|
-
console.log(
|
|
1010
|
+
console.log(` ${red('x')} Memory hook failed: ${e.message}`);
|
|
952
1011
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
console.log(
|
|
958
|
-
console.log(dim(' Captures files touched, tools used, and decisions made per session.'));
|
|
959
|
-
console.log();
|
|
960
|
-
const captureAnswer = await prompt(' Install session capture hook? (Y/n):', 'Y');
|
|
961
|
-
if (captureAnswer.toLowerCase() !== 'n') {
|
|
962
|
-
try {
|
|
963
|
-
const captureInstalled = installSessionCaptureHook();
|
|
964
|
-
if (captureInstalled) {
|
|
965
|
-
console.log(` ${green('+')} Session capture hook installed`);
|
|
966
|
-
}
|
|
967
|
-
} catch (e) {
|
|
968
|
-
console.log(` ${red('x')} Session capture hook failed: ${e.message}`);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
} else if (hookFlag) {
|
|
972
|
-
try {
|
|
973
|
-
installSessionCaptureHook();
|
|
974
|
-
} catch {}
|
|
1012
|
+
try {
|
|
1013
|
+
const captureInstalled = installSessionCaptureHook();
|
|
1014
|
+
if (captureInstalled) console.log(` ${green('+')} Session capture hook installed`);
|
|
1015
|
+
} catch (e) {
|
|
1016
|
+
console.log(` ${red('x')} Session capture hook failed: ${e.message}`);
|
|
975
1017
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
console.log(
|
|
981
|
-
console.log(dim(' Records tool names and file paths after each tool call (lightweight).'));
|
|
982
|
-
console.log();
|
|
983
|
-
const autoCaptureAnswer = await prompt(' Install auto-capture hook? (Y/n):', 'Y');
|
|
984
|
-
if (autoCaptureAnswer.toLowerCase() !== 'n') {
|
|
985
|
-
try {
|
|
986
|
-
const acInstalled = installPostToolCallHook();
|
|
987
|
-
if (acInstalled) {
|
|
988
|
-
console.log(` ${green('+')} Auto-capture hook installed`);
|
|
989
|
-
}
|
|
990
|
-
} catch (e) {
|
|
991
|
-
console.log(` ${red('x')} Auto-capture hook failed: ${e.message}`);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
} else if (hookFlag) {
|
|
995
|
-
try {
|
|
996
|
-
installPostToolCallHook();
|
|
997
|
-
} catch {}
|
|
1018
|
+
try {
|
|
1019
|
+
const acInstalled = installPostToolCallHook();
|
|
1020
|
+
if (acInstalled) console.log(` ${green('+')} Auto-capture hook installed`);
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
console.log(` ${red('x')} Auto-capture hook failed: ${e.message}`);
|
|
998
1023
|
}
|
|
999
|
-
} else
|
|
1000
|
-
console.log(dim(`
|
|
1024
|
+
} else {
|
|
1025
|
+
console.log(dim(` Hooks skipped. Install later: context-vault hooks install`));
|
|
1001
1026
|
}
|
|
1002
|
-
}
|
|
1003
1027
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1028
|
+
// Skills (bundled, no separate prompt unless not using fast path)
|
|
1029
|
+
let installSkillsFlag = useRecommendedDefaults || isNonInteractive;
|
|
1030
|
+
if (!isNonInteractive && !useRecommendedDefaults) {
|
|
1031
|
+
console.log();
|
|
1032
|
+
console.log(dim(' Install Claude Code skills? (recommended)'));
|
|
1033
|
+
console.log(dim(' compile-context, vault-setup'));
|
|
1034
|
+
console.log();
|
|
1035
|
+
const skillAnswer = await prompt(' Install Claude Code skills? (Y/n):', 'Y');
|
|
1036
|
+
installSkillsFlag = skillAnswer.toLowerCase() !== 'n';
|
|
1037
|
+
}
|
|
1012
1038
|
if (installSkillsFlag) {
|
|
1013
1039
|
try {
|
|
1014
1040
|
const names = installSkills();
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
console.log(`\n ${green('+')} ${name} skill installed`);
|
|
1018
|
-
}
|
|
1041
|
+
for (const name of names) {
|
|
1042
|
+
console.log(` ${green('+')} ${name} skill installed`);
|
|
1019
1043
|
}
|
|
1020
1044
|
} catch (e) {
|
|
1021
|
-
console.log(
|
|
1045
|
+
console.log(` ${red('x')} Skills install failed: ${e.message}`);
|
|
1046
|
+
}
|
|
1047
|
+
} else {
|
|
1048
|
+
console.log(dim(` Skills skipped. Install later: context-vault skills install`));
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Agent rules installation
|
|
1053
|
+
if (configuredTools.length > 0 && !flags.has('--no-rules')) {
|
|
1054
|
+
let installRules = isNonInteractive || useRecommendedDefaults;
|
|
1055
|
+
if (!isNonInteractive && !useRecommendedDefaults) {
|
|
1056
|
+
console.log();
|
|
1057
|
+
console.log(dim(' Install agent rules? (recommended)'));
|
|
1058
|
+
console.log(dim(' Teaches your AI agent when and how to save knowledge to the vault.'));
|
|
1059
|
+
console.log();
|
|
1060
|
+
const rulesAnswer = await prompt(' Install agent rules? (Y/n):', 'Y');
|
|
1061
|
+
installRules = rulesAnswer.toLowerCase() !== 'n';
|
|
1062
|
+
}
|
|
1063
|
+
if (installRules) {
|
|
1064
|
+
const rulesContent = loadAgentRules();
|
|
1065
|
+
if (rulesContent) {
|
|
1066
|
+
for (const tool of configuredTools) {
|
|
1067
|
+
try {
|
|
1068
|
+
const installed = installAgentRulesForTool(tool, rulesContent);
|
|
1069
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
1070
|
+
if (installed) {
|
|
1071
|
+
console.log(` ${green('+')} ${tool.name} agent rules installed`);
|
|
1072
|
+
if (rulesPath) {
|
|
1073
|
+
console.log(` ${dim(rulesPath)}`);
|
|
1074
|
+
installedRulesPaths.push({ tool: tool.name, path: rulesPath });
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
} catch (e) {
|
|
1078
|
+
console.log(` ${red('x')} ${tool.name} rules: ${e.message}`);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
} else {
|
|
1082
|
+
console.log(dim(' Agent rules file not found in package.'));
|
|
1022
1083
|
}
|
|
1023
1084
|
} else {
|
|
1024
|
-
console.log(dim(
|
|
1085
|
+
console.log(dim(' Rules skipped. Install later: context-vault rules install'));
|
|
1025
1086
|
}
|
|
1087
|
+
} else if (flags.has('--no-rules')) {
|
|
1088
|
+
console.log(dim(' Agent rules skipped (--no-rules)'));
|
|
1026
1089
|
}
|
|
1027
1090
|
|
|
1028
1091
|
// Seed entry
|
|
@@ -1033,8 +1096,36 @@ async function runSetup() {
|
|
|
1033
1096
|
);
|
|
1034
1097
|
}
|
|
1035
1098
|
|
|
1099
|
+
// Telemetry opt-in (moved to end, after user has seen value)
|
|
1100
|
+
console.log(`\n ${dim('[6/7]')}${bold(' Anonymous error reporting\n')}`);
|
|
1101
|
+
verbose(userLevel, 'Entirely optional. Works identically either way.\n');
|
|
1102
|
+
console.log(dim(' When enabled, unhandled errors send a minimal event (type, tool name,'));
|
|
1103
|
+
console.log(dim(' version, platform) to help diagnose issues. No vault content,'));
|
|
1104
|
+
console.log(dim(' file paths, or personal data is ever sent. Off by default.'));
|
|
1105
|
+
console.log(dim(` Full schema: ${MARKETING_URL}/telemetry`));
|
|
1106
|
+
console.log();
|
|
1107
|
+
|
|
1108
|
+
let telemetryEnabled = vaultConfig.telemetry === true;
|
|
1109
|
+
if (!isNonInteractive && !useRecommendedDefaults) {
|
|
1110
|
+
const defaultChoice = telemetryEnabled ? 'Y' : 'n';
|
|
1111
|
+
const telemetryAnswer = await prompt(
|
|
1112
|
+
` Enable anonymous error reporting? (y/N):`,
|
|
1113
|
+
defaultChoice
|
|
1114
|
+
);
|
|
1115
|
+
telemetryEnabled =
|
|
1116
|
+
telemetryAnswer.toLowerCase() === 'y' || telemetryAnswer.toLowerCase() === 'yes';
|
|
1117
|
+
}
|
|
1118
|
+
vaultConfig.telemetry = telemetryEnabled;
|
|
1119
|
+
console.log(
|
|
1120
|
+
` ${telemetryEnabled ? green('+') : dim('-')} Telemetry: ${telemetryEnabled ? 'enabled' : 'disabled'}`
|
|
1121
|
+
);
|
|
1122
|
+
|
|
1123
|
+
// Re-write config with telemetry setting
|
|
1124
|
+
assertNotTestMode(configPath);
|
|
1125
|
+
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + '\n');
|
|
1126
|
+
|
|
1036
1127
|
// Health check
|
|
1037
|
-
console.log(`\n ${dim('[
|
|
1128
|
+
console.log(`\n ${dim('[7/7]')}${bold(' Health check...')}\n`);
|
|
1038
1129
|
verbose(userLevel, 'Verifying vault, config, and database are accessible.\n');
|
|
1039
1130
|
const okResults = results.filter((r) => r.ok);
|
|
1040
1131
|
|
|
@@ -1128,6 +1219,21 @@ async function runSetup() {
|
|
|
1128
1219
|
: []),
|
|
1129
1220
|
];
|
|
1130
1221
|
}
|
|
1222
|
+
if (installedRulesPaths.length > 0) {
|
|
1223
|
+
boxLines.push(``, ` ${bold('Agent rules installed:')}`);
|
|
1224
|
+
for (const { path } of installedRulesPaths) {
|
|
1225
|
+
boxLines.push(` ${dim(path)}`);
|
|
1226
|
+
}
|
|
1227
|
+
boxLines.push(
|
|
1228
|
+
``,
|
|
1229
|
+
` ${dim(`View: ${cli} rules show`)}`,
|
|
1230
|
+
` ${dim(`Remove: ${cli} uninstall`)}`,
|
|
1231
|
+
` ${dim(`Skip: ${cli} setup --no-rules`)}`
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
if (claudeConfigured) {
|
|
1235
|
+
boxLines.push(``, ` ${dim('Personalize: run /vault-setup in your next session')}`);
|
|
1236
|
+
}
|
|
1131
1237
|
const innerWidth = Math.max(...boxLines.map((l) => l.length)) + 2;
|
|
1132
1238
|
const pad = (s) => s + ' '.repeat(Math.max(0, innerWidth - s.length));
|
|
1133
1239
|
console.log();
|
|
@@ -1368,8 +1474,8 @@ function configureJsonTool(tool, vaultDir) {
|
|
|
1368
1474
|
function createSeedEntries(vaultDir) {
|
|
1369
1475
|
let created = 0;
|
|
1370
1476
|
|
|
1371
|
-
// Entry 1: Getting started
|
|
1372
|
-
const insightDir = join(vaultDir, 'knowledge', '
|
|
1477
|
+
// Entry 1: Getting started
|
|
1478
|
+
const insightDir = join(vaultDir, 'knowledge', 'insight');
|
|
1373
1479
|
const insightPath = join(insightDir, 'getting-started.md');
|
|
1374
1480
|
if (!existsSync(insightPath)) {
|
|
1375
1481
|
mkdirSync(insightDir, { recursive: true });
|
|
@@ -1379,6 +1485,9 @@ function createSeedEntries(vaultDir) {
|
|
|
1379
1485
|
insightPath,
|
|
1380
1486
|
`---
|
|
1381
1487
|
id: ${id1}
|
|
1488
|
+
title: Getting started with your context vault
|
|
1489
|
+
kind: insight
|
|
1490
|
+
tier: durable
|
|
1382
1491
|
tags: ["getting-started", "vault"]
|
|
1383
1492
|
source: context-vault-setup
|
|
1384
1493
|
created: ${now}
|
|
@@ -1402,7 +1511,7 @@ ${insightPath}
|
|
|
1402
1511
|
}
|
|
1403
1512
|
|
|
1404
1513
|
// Entry 2: Example decision
|
|
1405
|
-
const decisionDir = join(vaultDir, 'knowledge', '
|
|
1514
|
+
const decisionDir = join(vaultDir, 'knowledge', 'decision');
|
|
1406
1515
|
const decisionPath = join(decisionDir, 'example-local-first-data.md');
|
|
1407
1516
|
if (!existsSync(decisionPath)) {
|
|
1408
1517
|
mkdirSync(decisionDir, { recursive: true });
|
|
@@ -1412,6 +1521,9 @@ ${insightPath}
|
|
|
1412
1521
|
decisionPath,
|
|
1413
1522
|
`---
|
|
1414
1523
|
id: ${id2}
|
|
1524
|
+
title: Use local-first data storage over cloud databases
|
|
1525
|
+
kind: decision
|
|
1526
|
+
tier: durable
|
|
1415
1527
|
tags: ["example", "architecture"]
|
|
1416
1528
|
source: context-vault-setup
|
|
1417
1529
|
created: ${now}
|
|
@@ -1584,6 +1696,7 @@ async function runConnect() {
|
|
|
1584
1696
|
modeConfig.mode = 'hosted';
|
|
1585
1697
|
modeConfig.hostedUrl = hostedUrl;
|
|
1586
1698
|
mkdirSync(join(HOME, '.context-mcp'), { recursive: true });
|
|
1699
|
+
assertNotTestMode(modeConfigPath);
|
|
1587
1700
|
writeFileSync(modeConfigPath, JSON.stringify(modeConfig, null, 2) + '\n');
|
|
1588
1701
|
|
|
1589
1702
|
console.log();
|
|
@@ -1696,6 +1809,7 @@ async function runSwitch() {
|
|
|
1696
1809
|
if (target === 'local') {
|
|
1697
1810
|
vaultConfig.mode = 'local';
|
|
1698
1811
|
mkdirSync(dataDir, { recursive: true });
|
|
1812
|
+
assertNotTestMode(configPath);
|
|
1699
1813
|
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + '\n');
|
|
1700
1814
|
|
|
1701
1815
|
console.log();
|
|
@@ -1756,6 +1870,7 @@ async function runSwitch() {
|
|
|
1756
1870
|
vaultConfig.hostedUrl = hostedUrl;
|
|
1757
1871
|
vaultConfig.apiKey = apiKey;
|
|
1758
1872
|
mkdirSync(dataDir, { recursive: true });
|
|
1873
|
+
assertNotTestMode(configPath);
|
|
1759
1874
|
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + '\n');
|
|
1760
1875
|
|
|
1761
1876
|
for (const tool of detected) {
|
|
@@ -1780,7 +1895,11 @@ async function runSwitch() {
|
|
|
1780
1895
|
}
|
|
1781
1896
|
|
|
1782
1897
|
async function runReindex() {
|
|
1783
|
-
|
|
1898
|
+
const dryRun = flags.has('--dry-run');
|
|
1899
|
+
const kindIdx = args.indexOf('--kind');
|
|
1900
|
+
const kindFilter = kindIdx !== -1 && args[kindIdx + 1] ? args[kindIdx + 1] : null;
|
|
1901
|
+
|
|
1902
|
+
console.log(dim(dryRun ? 'Analyzing vault (dry run)...' : 'Loading vault...'));
|
|
1784
1903
|
|
|
1785
1904
|
const { resolveConfig } = await import('@context-vault/core/config');
|
|
1786
1905
|
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
@@ -1806,14 +1925,34 @@ async function runReindex() {
|
|
|
1806
1925
|
deleteVec: (r) => deleteVec(stmts, r),
|
|
1807
1926
|
};
|
|
1808
1927
|
|
|
1809
|
-
const
|
|
1928
|
+
const reindexOpts = {
|
|
1929
|
+
fullSync: true,
|
|
1930
|
+
indexingConfig: config.indexing,
|
|
1931
|
+
dryRun,
|
|
1932
|
+
kindFilter,
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1935
|
+
const stats = await reindex(ctx, reindexOpts);
|
|
1810
1936
|
|
|
1811
1937
|
db.close();
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1938
|
+
|
|
1939
|
+
if (dryRun) {
|
|
1940
|
+
console.log(yellow('Dry run results (no changes made):'));
|
|
1941
|
+
console.log(` Would index: ${stats.added}`);
|
|
1942
|
+
console.log(` Would skip: ${stats.skippedIndexing ?? 0}`);
|
|
1943
|
+
} else {
|
|
1944
|
+
console.log(green('✓ Reindex complete'));
|
|
1945
|
+
console.log(` ${green('+')} ${stats.added} added`);
|
|
1946
|
+
console.log(` ${yellow('~')} ${stats.updated} updated`);
|
|
1947
|
+
console.log(` ${red('-')} ${stats.removed} removed`);
|
|
1948
|
+
console.log(` ${dim('·')} ${stats.unchanged} unchanged`);
|
|
1949
|
+
if (stats.skippedIndexing) {
|
|
1950
|
+
console.log(` ${dim('○')} ${stats.skippedIndexing} skipped indexing`);
|
|
1951
|
+
}
|
|
1952
|
+
if (stats.embeddingsCleared) {
|
|
1953
|
+
console.log(` ${dim('⊘')} ${stats.embeddingsCleared} embeddings cleared`);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1817
1956
|
}
|
|
1818
1957
|
|
|
1819
1958
|
async function runMigrateDirs() {
|
|
@@ -2131,6 +2270,11 @@ async function runStatus() {
|
|
|
2131
2270
|
}
|
|
2132
2271
|
|
|
2133
2272
|
const status = gatherVaultStatus({ db, config });
|
|
2273
|
+
let schemaVersion = 'unknown';
|
|
2274
|
+
try {
|
|
2275
|
+
const row = db.prepare('PRAGMA user_version').get();
|
|
2276
|
+
schemaVersion = String(row?.user_version ?? 'unknown');
|
|
2277
|
+
} catch {}
|
|
2134
2278
|
|
|
2135
2279
|
db.close();
|
|
2136
2280
|
|
|
@@ -2148,7 +2292,7 @@ async function runStatus() {
|
|
|
2148
2292
|
` Config: ${config.configPath} ${dim(`(${existsSync(config.configPath) ? 'exists' : 'missing'})`)}`
|
|
2149
2293
|
);
|
|
2150
2294
|
console.log(` Resolved: ${status.resolvedFrom}`);
|
|
2151
|
-
console.log(` Schema:
|
|
2295
|
+
console.log(` Schema: v${schemaVersion}`);
|
|
2152
2296
|
|
|
2153
2297
|
if (status.kindCounts.length) {
|
|
2154
2298
|
const BAR_WIDTH = 20;
|
|
@@ -2159,7 +2303,9 @@ async function runStatus() {
|
|
|
2159
2303
|
const filled = maxCount > 0 ? Math.round((c / maxCount) * BAR_WIDTH) : 0;
|
|
2160
2304
|
const bar = '█'.repeat(filled) + '░'.repeat(BAR_WIDTH - filled);
|
|
2161
2305
|
const countStr = String(c).padStart(4);
|
|
2162
|
-
|
|
2306
|
+
const IRREGULAR_PLURALS = { activity: 'activities', inbox: 'inboxes', index: 'indexes', match: 'matches' };
|
|
2307
|
+
const plural = IRREGULAR_PLURALS[kind] || (kind.endsWith('s') ? kind : kind + 's');
|
|
2308
|
+
console.log(` ${dim(bar)} ${countStr} ${plural}`);
|
|
2163
2309
|
}
|
|
2164
2310
|
} else {
|
|
2165
2311
|
console.log(`\n ${dim('(empty — no entries indexed)')}`);
|
|
@@ -2325,6 +2471,34 @@ async function runUninstall() {
|
|
|
2325
2471
|
console.log(` ${green('+')} Removed installed skills`);
|
|
2326
2472
|
}
|
|
2327
2473
|
|
|
2474
|
+
// Remove agent rules files
|
|
2475
|
+
const claudeRulesPath = join(HOME, '.claude', 'rules', 'context-vault.md');
|
|
2476
|
+
const cursorRulesPath = join(HOME, '.cursor', 'rules', 'context-vault.mdc');
|
|
2477
|
+
const windsurfRulesPath = join(HOME, '.windsurfrules');
|
|
2478
|
+
|
|
2479
|
+
if (existsSync(claudeRulesPath)) {
|
|
2480
|
+
unlinkSync(claudeRulesPath);
|
|
2481
|
+
console.log(` ${green('+')} Removed agent rules (Claude Code: ${claudeRulesPath})`);
|
|
2482
|
+
}
|
|
2483
|
+
if (existsSync(cursorRulesPath)) {
|
|
2484
|
+
unlinkSync(cursorRulesPath);
|
|
2485
|
+
console.log(` ${green('+')} Removed agent rules (Cursor: ${cursorRulesPath})`);
|
|
2486
|
+
}
|
|
2487
|
+
if (existsSync(windsurfRulesPath)) {
|
|
2488
|
+
const content = readFileSync(windsurfRulesPath, 'utf-8');
|
|
2489
|
+
if (content.includes(RULES_DELIMITER_START)) {
|
|
2490
|
+
const cleaned = content
|
|
2491
|
+
.replace(new RegExp(`\n?${RULES_DELIMITER_START}[\\s\\S]*?${RULES_DELIMITER_END}\n?`, 'g'), '\n')
|
|
2492
|
+
.trim();
|
|
2493
|
+
if (cleaned) {
|
|
2494
|
+
writeFileSync(windsurfRulesPath, cleaned + '\n');
|
|
2495
|
+
} else {
|
|
2496
|
+
unlinkSync(windsurfRulesPath);
|
|
2497
|
+
}
|
|
2498
|
+
console.log(` ${green('+')} Removed agent rules section from ${windsurfRulesPath}`);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2328
2502
|
// Optionally remove data directory
|
|
2329
2503
|
const dataDir = join(HOME, '.context-mcp');
|
|
2330
2504
|
if (existsSync(dataDir)) {
|
|
@@ -3648,6 +3822,78 @@ async function runSessionEnd() {
|
|
|
3648
3822
|
meta: { session_id: session_id ?? null, cwd, message_count },
|
|
3649
3823
|
});
|
|
3650
3824
|
console.log(`context-vault session captured — id: ${entry.id}`);
|
|
3825
|
+
|
|
3826
|
+
// ── Auto-insight extraction ──────────────────────────────────────────────
|
|
3827
|
+
const aiConfig = config.autoInsights ?? { enabled: true, patterns: ['★ Insight'], minChars: 50, maxPerSession: 5, tier: 'working' };
|
|
3828
|
+
if (aiConfig.enabled !== false) {
|
|
3829
|
+
try {
|
|
3830
|
+
const patterns = aiConfig.patterns ?? ['★ Insight'];
|
|
3831
|
+
const minChars = aiConfig.minChars ?? 50;
|
|
3832
|
+
const maxInsights = aiConfig.maxPerSession ?? 5;
|
|
3833
|
+
const defaultTier = aiConfig.tier ?? 'working';
|
|
3834
|
+
|
|
3835
|
+
// Build regex for all configured patterns
|
|
3836
|
+
const escapedPatterns = patterns.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
|
3837
|
+
const patternRe = new RegExp(
|
|
3838
|
+
`(?:${escapedPatterns.join('|')})[─\\s]*\`?\\n([\\s\\S]*?)\\n\`?─{10,}`,
|
|
3839
|
+
'g'
|
|
3840
|
+
);
|
|
3841
|
+
|
|
3842
|
+
const insightBlocks = [];
|
|
3843
|
+
for (const turn of turns) {
|
|
3844
|
+
if (turn.role !== 'assistant') continue;
|
|
3845
|
+
const text = extractText(turn);
|
|
3846
|
+
if (!text) continue;
|
|
3847
|
+
for (const m of text.matchAll(patternRe)) {
|
|
3848
|
+
const insightBody = m[1].trim();
|
|
3849
|
+
if (insightBody.length >= minChars && insightBlocks.length < maxInsights) {
|
|
3850
|
+
insightBlocks.push(insightBody);
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
if (insightBlocks.length > 0) {
|
|
3856
|
+
// Check existing auto-insight entries for dedup (by title, lightweight)
|
|
3857
|
+
const existingTitles = new Set();
|
|
3858
|
+
try {
|
|
3859
|
+
const rows = db.prepare(
|
|
3860
|
+
`SELECT title FROM entries WHERE tags LIKE '%auto-insight%' ORDER BY created_at DESC LIMIT 100`
|
|
3861
|
+
).all();
|
|
3862
|
+
for (const r of rows) {
|
|
3863
|
+
if (r.title) existingTitles.add(r.title.toLowerCase());
|
|
3864
|
+
}
|
|
3865
|
+
} catch {}
|
|
3866
|
+
|
|
3867
|
+
let savedCount = 0;
|
|
3868
|
+
for (const insightBody of insightBlocks) {
|
|
3869
|
+
const boldMatch = insightBody.match(/\*\*(.+?)\*\*/);
|
|
3870
|
+
const firstLine = insightBody.split('\n')[0].replace(/\*\*/g, '').trim();
|
|
3871
|
+
const insightTitle = boldMatch ? boldMatch[1].slice(0, 80) : firstLine.slice(0, 80);
|
|
3872
|
+
|
|
3873
|
+
// Skip near-duplicates by title
|
|
3874
|
+
if (existingTitles.has(insightTitle.toLowerCase())) continue;
|
|
3875
|
+
|
|
3876
|
+
const insightTags = ['auto-insight', 'session-insight', `bucket:${project}`];
|
|
3877
|
+
await captureAndIndex(ctx, {
|
|
3878
|
+
kind: 'insight',
|
|
3879
|
+
title: insightTitle,
|
|
3880
|
+
body: insightBody,
|
|
3881
|
+
tags: insightTags,
|
|
3882
|
+
source: `claude-code session ${new Date().toISOString().slice(0, 10)}`,
|
|
3883
|
+
tier: defaultTier,
|
|
3884
|
+
meta: { auto_extracted: true, session_id: session_id ?? null },
|
|
3885
|
+
});
|
|
3886
|
+
existingTitles.add(insightTitle.toLowerCase());
|
|
3887
|
+
savedCount++;
|
|
3888
|
+
}
|
|
3889
|
+
if (savedCount > 0) {
|
|
3890
|
+
console.log(`context-vault auto-insights — ${savedCount} insight${savedCount === 1 ? '' : 's'} saved`);
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
} catch {
|
|
3894
|
+
// Auto-insight extraction is best-effort
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3651
3897
|
} catch {
|
|
3652
3898
|
// fail silently — never block session end
|
|
3653
3899
|
} finally {
|
|
@@ -3936,6 +4182,73 @@ function installSkills() {
|
|
|
3936
4182
|
return installed;
|
|
3937
4183
|
}
|
|
3938
4184
|
|
|
4185
|
+
const RULES_DELIMITER_START = '<!-- context-vault agent rules -->';
|
|
4186
|
+
const RULES_DELIMITER_END = '<!-- /context-vault agent rules -->';
|
|
4187
|
+
|
|
4188
|
+
/**
|
|
4189
|
+
* Load agent-rules.md from the assets directory.
|
|
4190
|
+
* Returns the file content or null if not found.
|
|
4191
|
+
*/
|
|
4192
|
+
function loadAgentRules() {
|
|
4193
|
+
const rulesPath = join(ROOT, 'assets', 'agent-rules.md');
|
|
4194
|
+
if (!existsSync(rulesPath)) return null;
|
|
4195
|
+
return readFileSync(rulesPath, 'utf-8');
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
/**
|
|
4199
|
+
* Return the path where agent rules are/would be installed for a given tool.
|
|
4200
|
+
* Returns null for tools with no rules install path.
|
|
4201
|
+
*/
|
|
4202
|
+
function getRulesPathForTool(tool) {
|
|
4203
|
+
return tool.rulesPath || null;
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
/**
|
|
4207
|
+
* Install agent rules for a specific tool.
|
|
4208
|
+
* Uses tool.rulesPath and tool.rulesMethod from the TOOLS array.
|
|
4209
|
+
* - 'write' method: writes the file directly (Claude Code, Cursor)
|
|
4210
|
+
* - 'append' method: appends with delimiter markers (Windsurf)
|
|
4211
|
+
* Returns true if installed/updated, false if already up to date or skipped.
|
|
4212
|
+
*/
|
|
4213
|
+
function installAgentRulesForTool(tool, rulesContent) {
|
|
4214
|
+
const rulesPath = tool.rulesPath;
|
|
4215
|
+
if (!rulesPath) return false;
|
|
4216
|
+
|
|
4217
|
+
if (tool.rulesMethod === 'write') {
|
|
4218
|
+
if (existsSync(rulesPath)) {
|
|
4219
|
+
const existing = readFileSync(rulesPath, 'utf-8');
|
|
4220
|
+
if (existing.trim() === rulesContent.trim()) return false;
|
|
4221
|
+
}
|
|
4222
|
+
mkdirSync(dirname(rulesPath), { recursive: true });
|
|
4223
|
+
writeFileSync(rulesPath, rulesContent);
|
|
4224
|
+
return true;
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
if (tool.rulesMethod === 'append') {
|
|
4228
|
+
const delimited = `\n${RULES_DELIMITER_START}\n${rulesContent}\n${RULES_DELIMITER_END}\n`;
|
|
4229
|
+
if (existsSync(rulesPath)) {
|
|
4230
|
+
const existing = readFileSync(rulesPath, 'utf-8');
|
|
4231
|
+
if (existing.includes(RULES_DELIMITER_START)) {
|
|
4232
|
+
const delimiterRegex = new RegExp(
|
|
4233
|
+
`\n?${RULES_DELIMITER_START}[\\s\\S]*?${RULES_DELIMITER_END}\n?`,
|
|
4234
|
+
'g'
|
|
4235
|
+
);
|
|
4236
|
+
const existingSection = existing.match(delimiterRegex)?.[0] || '';
|
|
4237
|
+
if (existingSection.includes(rulesContent.trim())) return false;
|
|
4238
|
+
const cleaned = existing.replace(delimiterRegex, '');
|
|
4239
|
+
writeFileSync(rulesPath, cleaned + delimited);
|
|
4240
|
+
return true;
|
|
4241
|
+
}
|
|
4242
|
+
writeFileSync(rulesPath, existing + delimited);
|
|
4243
|
+
} else {
|
|
4244
|
+
writeFileSync(rulesPath, delimited.trimStart());
|
|
4245
|
+
}
|
|
4246
|
+
return true;
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4249
|
+
return false;
|
|
4250
|
+
}
|
|
4251
|
+
|
|
3939
4252
|
/** Returns the path to Claude Code's global settings.json */
|
|
3940
4253
|
function claudeSettingsPath() {
|
|
3941
4254
|
return join(HOME, '.claude', 'settings.json');
|
|
@@ -4291,6 +4604,160 @@ ${bold('Commands:')}
|
|
|
4291
4604
|
|
|
4292
4605
|
${bold('Bundled skills:')}
|
|
4293
4606
|
${cyan('compile-context')} Compile vault entries into a project brief using create_snapshot
|
|
4607
|
+
${cyan('vault-setup')} Agent-assisted vault customization (run /vault-setup)
|
|
4608
|
+
`);
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
|
|
4612
|
+
async function runRules() {
|
|
4613
|
+
const sub = args[1];
|
|
4614
|
+
|
|
4615
|
+
if (sub === 'install') {
|
|
4616
|
+
console.log();
|
|
4617
|
+
const rulesContent = loadAgentRules();
|
|
4618
|
+
if (!rulesContent) {
|
|
4619
|
+
console.log(` ${yellow('!')} Agent rules file not found in package.\n`);
|
|
4620
|
+
process.exit(1);
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
const { detected } = await detectAllTools();
|
|
4624
|
+
if (detected.length === 0) {
|
|
4625
|
+
console.log(` ${yellow('!')} No supported tools detected.\n`);
|
|
4626
|
+
process.exit(1);
|
|
4627
|
+
}
|
|
4628
|
+
|
|
4629
|
+
let installed = 0;
|
|
4630
|
+
for (const tool of detected) {
|
|
4631
|
+
try {
|
|
4632
|
+
const ok = installAgentRulesForTool(tool, rulesContent);
|
|
4633
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
4634
|
+
if (ok) {
|
|
4635
|
+
console.log(` ${green('+')} ${tool.name} — agent rules installed`);
|
|
4636
|
+
if (rulesPath) console.log(` ${dim(rulesPath)}`);
|
|
4637
|
+
installed++;
|
|
4638
|
+
} else {
|
|
4639
|
+
const hasPath = !!rulesPath;
|
|
4640
|
+
const alreadyExists = hasPath && existsSync(rulesPath);
|
|
4641
|
+
if (alreadyExists) {
|
|
4642
|
+
console.log(` ${dim('-')} ${tool.name} — already installed`);
|
|
4643
|
+
} else if (hasPath) {
|
|
4644
|
+
console.log(` ${dim('-')} ${tool.name} — skipped (up to date)`);
|
|
4645
|
+
} else {
|
|
4646
|
+
console.log(` ${dim('-')} ${tool.name} — not supported`);
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
} catch (e) {
|
|
4650
|
+
console.log(` ${red('x')} ${tool.name} — ${e.message}`);
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
|
|
4654
|
+
console.log();
|
|
4655
|
+
if (installed > 0) {
|
|
4656
|
+
console.log(dim(' Rules teach your AI agent when to save knowledge automatically.'));
|
|
4657
|
+
console.log(dim(' Restart your AI tools to apply.'));
|
|
4658
|
+
console.log(dim(` View: context-vault rules show`));
|
|
4659
|
+
console.log(dim(` Remove: context-vault uninstall`));
|
|
4660
|
+
}
|
|
4661
|
+
console.log();
|
|
4662
|
+
} else if (sub === 'show') {
|
|
4663
|
+
const { detected } = await detectAllTools();
|
|
4664
|
+
const toolsWithRules = detected.filter((t) => getRulesPathForTool(t));
|
|
4665
|
+
if (toolsWithRules.length === 0) {
|
|
4666
|
+
console.log(`\n ${yellow('!')} No supported tool detected.\n`);
|
|
4667
|
+
process.exit(1);
|
|
4668
|
+
}
|
|
4669
|
+
let anyShown = false;
|
|
4670
|
+
for (const tool of toolsWithRules) {
|
|
4671
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
4672
|
+
if (!rulesPath || !existsSync(rulesPath)) {
|
|
4673
|
+
console.log(`\n ${yellow('!')} No rules file installed for ${tool.name}.`);
|
|
4674
|
+
console.log(dim(` Run: context-vault rules install`));
|
|
4675
|
+
continue;
|
|
4676
|
+
}
|
|
4677
|
+
if (anyShown) console.log(dim(' ' + '─'.repeat(40)));
|
|
4678
|
+
console.log(`\n ${dim(`${tool.name}: ${rulesPath}`)}\n`);
|
|
4679
|
+
console.log(readFileSync(rulesPath, 'utf-8'));
|
|
4680
|
+
anyShown = true;
|
|
4681
|
+
}
|
|
4682
|
+
if (!anyShown) {
|
|
4683
|
+
console.log(dim(`\n Run: context-vault rules install\n`));
|
|
4684
|
+
process.exit(1);
|
|
4685
|
+
}
|
|
4686
|
+
} else if (sub === 'path') {
|
|
4687
|
+
const { detected } = await detectAllTools();
|
|
4688
|
+
const supportedTools = detected.filter((t) => getRulesPathForTool(t));
|
|
4689
|
+
if (supportedTools.length === 0) {
|
|
4690
|
+
console.log(`\n ${yellow('!')} No supported tool detected.\n`);
|
|
4691
|
+
process.exit(1);
|
|
4692
|
+
}
|
|
4693
|
+
console.log();
|
|
4694
|
+
for (const tool of supportedTools) {
|
|
4695
|
+
const p = getRulesPathForTool(tool);
|
|
4696
|
+
const installed = existsSync(p);
|
|
4697
|
+
console.log(` ${tool.name}: ${p} ${installed ? green('(installed)') : dim('(not installed)')}`);
|
|
4698
|
+
}
|
|
4699
|
+
console.log();
|
|
4700
|
+
} else if (sub === 'diff') {
|
|
4701
|
+
const bundled = loadAgentRules();
|
|
4702
|
+
if (!bundled) {
|
|
4703
|
+
console.log(`\n ${yellow('!')} Agent rules file not found in package.\n`);
|
|
4704
|
+
process.exit(1);
|
|
4705
|
+
}
|
|
4706
|
+
const { detected } = await detectAllTools();
|
|
4707
|
+
const toolsWithRules = detected.filter((t) => getRulesPathForTool(t));
|
|
4708
|
+
if (toolsWithRules.length === 0) {
|
|
4709
|
+
console.log(`\n ${yellow('!')} No supported tool detected.\n`);
|
|
4710
|
+
process.exit(1);
|
|
4711
|
+
}
|
|
4712
|
+
for (const tool of toolsWithRules) {
|
|
4713
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
4714
|
+
if (!rulesPath || !existsSync(rulesPath)) {
|
|
4715
|
+
console.log(`\n ${yellow('!')} No rules file installed for ${tool.name}.`);
|
|
4716
|
+
console.log(dim(` Run: context-vault rules install`));
|
|
4717
|
+
continue;
|
|
4718
|
+
}
|
|
4719
|
+
const installed = readFileSync(rulesPath, 'utf-8');
|
|
4720
|
+
if (installed.trim() === bundled.trim()) {
|
|
4721
|
+
console.log(`\n ${green('✓')} ${tool.name}: rules are up to date (${rulesPath})`);
|
|
4722
|
+
} else {
|
|
4723
|
+
console.log(`\n ${yellow('!')} ${tool.name}: installed rules differ from bundled version.`);
|
|
4724
|
+
console.log(` ${dim(rulesPath)}\n`);
|
|
4725
|
+
const installedLines = installed.split('\n');
|
|
4726
|
+
const bundledLines = bundled.split('\n');
|
|
4727
|
+
const maxLines = Math.max(installedLines.length, bundledLines.length);
|
|
4728
|
+
for (let i = 0; i < maxLines; i++) {
|
|
4729
|
+
const a = installedLines[i];
|
|
4730
|
+
const b = bundledLines[i];
|
|
4731
|
+
if (a === undefined) {
|
|
4732
|
+
console.log(` ${green('+')} ${b}`);
|
|
4733
|
+
} else if (b === undefined) {
|
|
4734
|
+
console.log(` ${red('-')} ${a}`);
|
|
4735
|
+
} else if (a !== b) {
|
|
4736
|
+
console.log(` ${red('-')} ${a}`);
|
|
4737
|
+
console.log(` ${green('+')} ${b}`);
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4740
|
+
console.log();
|
|
4741
|
+
console.log(dim(' To upgrade: context-vault rules install'));
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
console.log();
|
|
4745
|
+
} else {
|
|
4746
|
+
console.log(`
|
|
4747
|
+
${bold('context-vault rules')} <command>
|
|
4748
|
+
|
|
4749
|
+
Manage agent rules that teach AI tools when and how to use the vault.
|
|
4750
|
+
|
|
4751
|
+
${bold('Commands:')}
|
|
4752
|
+
${cyan('rules install')} Install agent rules for all detected AI tools
|
|
4753
|
+
${cyan('rules show')} Print the currently installed rules file
|
|
4754
|
+
${cyan('rules diff')} Show diff between installed rules and bundled version
|
|
4755
|
+
${cyan('rules path')} Print the path where rules are/would be installed
|
|
4756
|
+
|
|
4757
|
+
${bold('Installed to:')}
|
|
4758
|
+
${cyan('Claude Code')} ~/.claude/rules/context-vault.md
|
|
4759
|
+
${cyan('Cursor')} ~/.cursor/rules/context-vault.mdc
|
|
4760
|
+
${cyan('Windsurf')} ~/.windsurfrules (appended with delimiters)
|
|
4294
4761
|
`);
|
|
4295
4762
|
}
|
|
4296
4763
|
}
|
|
@@ -5105,6 +5572,160 @@ async function runRestart() {
|
|
|
5105
5572
|
console.log();
|
|
5106
5573
|
}
|
|
5107
5574
|
|
|
5575
|
+
async function runReconnect() {
|
|
5576
|
+
console.log();
|
|
5577
|
+
console.log(` ${bold('◇ context-vault reconnect')}`);
|
|
5578
|
+
console.log();
|
|
5579
|
+
|
|
5580
|
+
// 1. Read current config to get the correct vault dir
|
|
5581
|
+
const { resolveConfig } = await import('@context-vault/core/config');
|
|
5582
|
+
const config = resolveConfig();
|
|
5583
|
+
const vaultDir = config.vaultDir;
|
|
5584
|
+
|
|
5585
|
+
console.log(` Vault dir: ${cyan(vaultDir)}`);
|
|
5586
|
+
if (!existsSync(vaultDir)) {
|
|
5587
|
+
console.error(red(` Vault directory does not exist: ${vaultDir}`));
|
|
5588
|
+
console.error(dim(` Run context-vault setup to configure.`));
|
|
5589
|
+
process.exit(1);
|
|
5590
|
+
}
|
|
5591
|
+
|
|
5592
|
+
// Count entries to confirm it's a real vault
|
|
5593
|
+
const mdFiles = readdirSync(vaultDir, { recursive: true })
|
|
5594
|
+
.filter(f => String(f).endsWith('.md'));
|
|
5595
|
+
console.log(` Found ${mdFiles.length} markdown files`);
|
|
5596
|
+
console.log();
|
|
5597
|
+
|
|
5598
|
+
// 2. Kill all running context-vault serve processes (they have stale --vault-dir)
|
|
5599
|
+
const isWin = platform() === 'win32';
|
|
5600
|
+
let psOutput;
|
|
5601
|
+
try {
|
|
5602
|
+
const psCmd = isWin
|
|
5603
|
+
? 'wmic process where "CommandLine like \'%context-vault%\'" get ProcessId,CommandLine /format:list'
|
|
5604
|
+
: 'ps aux';
|
|
5605
|
+
psOutput = execSync(psCmd, { encoding: 'utf-8', timeout: 5000 });
|
|
5606
|
+
} catch (e) {
|
|
5607
|
+
console.error(red(` Failed to list processes: ${e.message}`));
|
|
5608
|
+
process.exit(1);
|
|
5609
|
+
}
|
|
5610
|
+
|
|
5611
|
+
const currentPid = process.pid;
|
|
5612
|
+
const serverPids = [];
|
|
5613
|
+
|
|
5614
|
+
if (isWin) {
|
|
5615
|
+
const pidMatches = psOutput.matchAll(/ProcessId=(\d+)/g);
|
|
5616
|
+
for (const m of pidMatches) {
|
|
5617
|
+
const pid = parseInt(m[1], 10);
|
|
5618
|
+
if (pid !== currentPid) serverPids.push(pid);
|
|
5619
|
+
}
|
|
5620
|
+
} else {
|
|
5621
|
+
const lines = psOutput.split('\n');
|
|
5622
|
+
for (const line of lines) {
|
|
5623
|
+
const match = line.match(/^\S+\s+(\d+)\s/);
|
|
5624
|
+
if (!match) continue;
|
|
5625
|
+
const pid = parseInt(match[1], 10);
|
|
5626
|
+
if (pid === currentPid) continue;
|
|
5627
|
+
if (
|
|
5628
|
+
/context-vault.*(serve|stdio|server\/index)/.test(line) ||
|
|
5629
|
+
/server\/index\.js.*context-vault/.test(line)
|
|
5630
|
+
) {
|
|
5631
|
+
serverPids.push(pid);
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
|
|
5636
|
+
if (serverPids.length > 0) {
|
|
5637
|
+
console.log(` Stopping ${serverPids.length} stale server process${serverPids.length === 1 ? '' : 'es'}...`);
|
|
5638
|
+
for (const pid of serverPids) {
|
|
5639
|
+
try {
|
|
5640
|
+
process.kill(pid, 'SIGTERM');
|
|
5641
|
+
console.log(` ${green('✓')} Stopped PID ${pid}`);
|
|
5642
|
+
} catch (e) {
|
|
5643
|
+
if (e.code !== 'ESRCH') {
|
|
5644
|
+
console.log(` ${yellow('!')} Could not stop PID ${pid}: ${e.message}`);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
// Wait for graceful shutdown
|
|
5649
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
5650
|
+
// Force-kill any survivors
|
|
5651
|
+
for (const pid of serverPids) {
|
|
5652
|
+
try { process.kill(pid, 0); process.kill(pid, 'SIGKILL'); } catch {}
|
|
5653
|
+
}
|
|
5654
|
+
console.log();
|
|
5655
|
+
} else {
|
|
5656
|
+
console.log(dim(' No running server processes found.'));
|
|
5657
|
+
console.log();
|
|
5658
|
+
}
|
|
5659
|
+
|
|
5660
|
+
// 3. Re-register MCP server with correct vault-dir for each detected tool
|
|
5661
|
+
const env = { ...process.env };
|
|
5662
|
+
delete env.CLAUDECODE;
|
|
5663
|
+
|
|
5664
|
+
const tools = [];
|
|
5665
|
+
try { execSync('which claude', { stdio: 'pipe' }); tools.push('claude'); } catch {}
|
|
5666
|
+
try { execSync('which codex', { stdio: 'pipe' }); tools.push('codex'); } catch {}
|
|
5667
|
+
|
|
5668
|
+
for (const tool of tools) {
|
|
5669
|
+
try {
|
|
5670
|
+
execFileSync(tool, ['mcp', 'remove', 'context-vault', '-s', 'user'], { stdio: 'pipe', env });
|
|
5671
|
+
} catch {}
|
|
5672
|
+
|
|
5673
|
+
try {
|
|
5674
|
+
if (isInstalledPackage()) {
|
|
5675
|
+
execFileSync(
|
|
5676
|
+
tool,
|
|
5677
|
+
['mcp', 'add', '-s', 'user', 'context-vault', '--', 'context-vault', 'serve', '--vault-dir', vaultDir],
|
|
5678
|
+
{ stdio: 'pipe', env }
|
|
5679
|
+
);
|
|
5680
|
+
} else if (isNpx()) {
|
|
5681
|
+
execFileSync(
|
|
5682
|
+
tool,
|
|
5683
|
+
['mcp', 'add', '-s', 'user', 'context-vault', '-e', 'NODE_OPTIONS=--no-warnings=ExperimentalWarning',
|
|
5684
|
+
'--', 'npx', '-y', 'context-vault', 'serve', '--vault-dir', vaultDir],
|
|
5685
|
+
{ stdio: 'pipe', env }
|
|
5686
|
+
);
|
|
5687
|
+
} else {
|
|
5688
|
+
execFileSync(
|
|
5689
|
+
tool,
|
|
5690
|
+
['mcp', 'add', '-s', 'user', 'context-vault', '-e', 'NODE_OPTIONS=--no-warnings=ExperimentalWarning',
|
|
5691
|
+
'--', process.execPath, SERVER_PATH, '--vault-dir', vaultDir],
|
|
5692
|
+
{ stdio: 'pipe', env }
|
|
5693
|
+
);
|
|
5694
|
+
}
|
|
5695
|
+
console.log(` ${green('✓')} ${tool} MCP re-registered with vault-dir: ${vaultDir}`);
|
|
5696
|
+
} catch (e) {
|
|
5697
|
+
console.log(` ${red('✘')} Failed to register ${tool}: ${e.stderr?.toString().trim() || e.message}`);
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
|
|
5701
|
+
// 4. Reindex to ensure DB matches vault dir
|
|
5702
|
+
console.log();
|
|
5703
|
+
console.log(` Reindexing...`);
|
|
5704
|
+
try {
|
|
5705
|
+
const { initDatabase, prepareStatements, insertVec, deleteVec } =
|
|
5706
|
+
await import('@context-vault/core/db');
|
|
5707
|
+
const { embed } = await import('@context-vault/core/embed');
|
|
5708
|
+
const { reindex } = await import('@context-vault/core/index');
|
|
5709
|
+
const db = await initDatabase(config.dbPath);
|
|
5710
|
+
const stmts = prepareStatements(db);
|
|
5711
|
+
const ctx = {
|
|
5712
|
+
db, config, stmts, embed,
|
|
5713
|
+
insertVec: (r, e) => insertVec(stmts, r, e),
|
|
5714
|
+
deleteVec: (r) => deleteVec(stmts, r),
|
|
5715
|
+
};
|
|
5716
|
+
const stats = await reindex(ctx, { fullSync: true });
|
|
5717
|
+
db.close();
|
|
5718
|
+
console.log(` ${green('✓')} Reindex: +${stats.added} added, ~${stats.updated} updated, -${stats.removed} removed`);
|
|
5719
|
+
} catch (e) {
|
|
5720
|
+
console.log(` ${yellow('!')} Reindex failed: ${e.message}`);
|
|
5721
|
+
console.log(dim(` Run 'context-vault reindex --vault-dir ${vaultDir}' manually.`));
|
|
5722
|
+
}
|
|
5723
|
+
|
|
5724
|
+
console.log();
|
|
5725
|
+
console.log(green(' Reconnected.') + dim(' Start a new Claude session to use the updated vault.'));
|
|
5726
|
+
console.log();
|
|
5727
|
+
}
|
|
5728
|
+
|
|
5108
5729
|
async function runConsolidate() {
|
|
5109
5730
|
const dryRun = flags.has('--dry-run');
|
|
5110
5731
|
const tagArg = getFlag('--tag');
|
|
@@ -5657,6 +6278,9 @@ async function main() {
|
|
|
5657
6278
|
case 'skills':
|
|
5658
6279
|
await runSkills();
|
|
5659
6280
|
break;
|
|
6281
|
+
case 'rules':
|
|
6282
|
+
await runRules();
|
|
6283
|
+
break;
|
|
5660
6284
|
case 'flush':
|
|
5661
6285
|
await runFlush();
|
|
5662
6286
|
break;
|
|
@@ -5726,6 +6350,9 @@ async function main() {
|
|
|
5726
6350
|
case 'restart':
|
|
5727
6351
|
await runRestart();
|
|
5728
6352
|
break;
|
|
6353
|
+
case 'reconnect':
|
|
6354
|
+
await runReconnect();
|
|
6355
|
+
break;
|
|
5729
6356
|
case 'consolidate':
|
|
5730
6357
|
await runConsolidate();
|
|
5731
6358
|
break;
|