context-vault 3.5.0 → 3.5.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/bin/cli.js +268 -183
- package/node_modules/@context-vault/core/package.json +1 -1
- package/package.json +2 -2
package/bin/cli.js
CHANGED
|
@@ -188,6 +188,7 @@ const args = process.argv.slice(2);
|
|
|
188
188
|
const command = args[0];
|
|
189
189
|
const flags = new Set(args.filter((a) => a.startsWith('--')));
|
|
190
190
|
const isNonInteractive = flags.has('--yes') || !process.stdin.isTTY;
|
|
191
|
+
const isDryRun = flags.has('--dry-run');
|
|
191
192
|
|
|
192
193
|
function getFlag(name) {
|
|
193
194
|
const idx = args.indexOf(name);
|
|
@@ -426,6 +427,7 @@ ${bold('Commands:')}
|
|
|
426
427
|
--yes Non-interactive mode (accept all defaults)
|
|
427
428
|
--force Overwrite existing config without confirmation
|
|
428
429
|
--skip-embeddings Skip embedding model download (FTS-only mode)
|
|
430
|
+
--dry-run Show what setup would do without writing anything
|
|
429
431
|
`);
|
|
430
432
|
}
|
|
431
433
|
|
|
@@ -437,11 +439,15 @@ async function runSetup() {
|
|
|
437
439
|
console.log();
|
|
438
440
|
console.log(` ${bold('◇ context-vault')} ${dim(`v${VERSION}`)}`);
|
|
439
441
|
console.log(dim(' Persistent memory for AI agents'));
|
|
442
|
+
if (isDryRun) {
|
|
443
|
+
console.log();
|
|
444
|
+
console.log(yellow(' [dry-run] No files will be written. Showing what setup would do.'));
|
|
445
|
+
}
|
|
440
446
|
console.log();
|
|
441
447
|
|
|
442
448
|
// Check for existing installation
|
|
443
449
|
const existingConfig = join(HOME, '.context-mcp', 'config.json');
|
|
444
|
-
if (existsSync(existingConfig) && !isNonInteractive) {
|
|
450
|
+
if (existsSync(existingConfig) && !isNonInteractive && !isDryRun) {
|
|
445
451
|
let existingVault = '(unknown)';
|
|
446
452
|
try {
|
|
447
453
|
const cfg = JSON.parse(readFileSync(existingConfig, 'utf-8'));
|
|
@@ -672,9 +678,9 @@ async function runSetup() {
|
|
|
672
678
|
${dim('}')}\n`);
|
|
673
679
|
}
|
|
674
680
|
|
|
675
|
-
// In non-interactive mode, continue setup without tools (vault, config, etc.)
|
|
676
|
-
if (isNonInteractive) {
|
|
677
|
-
console.log(dim(
|
|
681
|
+
// In non-interactive/dry-run mode, continue setup without tools (vault, config, etc.)
|
|
682
|
+
if (isDryRun || isNonInteractive) {
|
|
683
|
+
console.log(dim(` Continuing setup without tool configuration (${isDryRun ? '--dry-run' : '--yes'} mode).\n`));
|
|
678
684
|
} else {
|
|
679
685
|
return;
|
|
680
686
|
}
|
|
@@ -682,7 +688,7 @@ async function runSetup() {
|
|
|
682
688
|
|
|
683
689
|
// Select tools
|
|
684
690
|
let selected;
|
|
685
|
-
if (isNonInteractive || detected.length === 1) {
|
|
691
|
+
if (isDryRun || isNonInteractive || detected.length === 1) {
|
|
686
692
|
selected = detected;
|
|
687
693
|
if (detected.length === 1) {
|
|
688
694
|
console.log(` ${dim('→')} Auto-selected ${detected[0].name}\n`);
|
|
@@ -710,7 +716,9 @@ async function runSetup() {
|
|
|
710
716
|
let useRecommendedDefaults = false;
|
|
711
717
|
const existingConfigForFastPath = join(HOME, '.context-mcp', 'config.json');
|
|
712
718
|
const isNewInstall = !existsSync(existingConfigForFastPath);
|
|
713
|
-
if (
|
|
719
|
+
if (isDryRun) {
|
|
720
|
+
useRecommendedDefaults = true;
|
|
721
|
+
} else if (isNewInstall && !isNonInteractive) {
|
|
714
722
|
console.log(dim(' Install with recommended settings?'));
|
|
715
723
|
console.log(dim(' Vault in default location, all hooks, skills, and rules installed.'));
|
|
716
724
|
console.log();
|
|
@@ -781,7 +789,7 @@ async function runSetup() {
|
|
|
781
789
|
}
|
|
782
790
|
}
|
|
783
791
|
|
|
784
|
-
const vaultDir = (isNonInteractive || useRecommendedDefaults)
|
|
792
|
+
const vaultDir = (isNonInteractive || useRecommendedDefaults || isDryRun)
|
|
785
793
|
? defaultVaultDir
|
|
786
794
|
: await prompt(` Vault directory:`, defaultVaultDir);
|
|
787
795
|
let resolvedVaultDir = resolve(vaultDir);
|
|
@@ -793,6 +801,8 @@ async function runSetup() {
|
|
|
793
801
|
console.error(dim(` Remove or rename the file, then run setup again.\n`));
|
|
794
802
|
process.exit(1);
|
|
795
803
|
}
|
|
804
|
+
} else if (isDryRun) {
|
|
805
|
+
console.log(`\n ${yellow('[dry-run]')} Would create directory: ${resolvedVaultDir}`);
|
|
796
806
|
} else if (isNonInteractive || useRecommendedDefaults) {
|
|
797
807
|
mkdirSync(resolvedVaultDir, { recursive: true });
|
|
798
808
|
console.log(`\n ${green('+')} Created ${resolvedVaultDir}`);
|
|
@@ -808,11 +818,19 @@ async function runSetup() {
|
|
|
808
818
|
}
|
|
809
819
|
|
|
810
820
|
// Write marker file for vault auto-detection
|
|
811
|
-
|
|
821
|
+
if (isDryRun) {
|
|
822
|
+
console.log(` ${yellow('[dry-run]')} Would write marker file: ${join(resolvedVaultDir, MARKER_FILE)}`);
|
|
823
|
+
} else {
|
|
824
|
+
writeMarkerFile(resolvedVaultDir);
|
|
825
|
+
}
|
|
812
826
|
|
|
813
827
|
// Ensure data dir exists for DB storage
|
|
814
828
|
const dataDir = join(HOME, '.context-mcp');
|
|
815
|
-
|
|
829
|
+
if (isDryRun) {
|
|
830
|
+
console.log(` ${yellow('[dry-run]')} Would create directory: ${dataDir}`);
|
|
831
|
+
} else {
|
|
832
|
+
mkdirSync(dataDir, { recursive: true });
|
|
833
|
+
}
|
|
816
834
|
|
|
817
835
|
// Write config.json to data dir (persistent, survives reinstalls)
|
|
818
836
|
const configPath = join(dataDir, 'config.json');
|
|
@@ -848,18 +866,21 @@ async function runSetup() {
|
|
|
848
866
|
);
|
|
849
867
|
console.log(` Setup would change vaultDir to: ${resolvedVaultDir}`);
|
|
850
868
|
|
|
851
|
-
if (
|
|
869
|
+
if (isDryRun) {
|
|
870
|
+
console.log(` ${yellow('[dry-run]')} Would change vaultDir from ${resolve(existingVaultDir)} to ${resolvedVaultDir}`);
|
|
871
|
+
resolvedVaultDir = resolve(existingVaultDir);
|
|
872
|
+
} else if (isNonInteractive) {
|
|
852
873
|
console.log();
|
|
853
874
|
console.log(red(' Refusing to overwrite vaultDir in non-interactive mode.'));
|
|
854
875
|
console.log(dim(' Use --force to override, or --vault-dir to set explicitly.'));
|
|
855
876
|
process.exit(1);
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
877
|
+
} else {
|
|
878
|
+
console.log();
|
|
879
|
+
const overwrite = await prompt(' Overwrite? (y/N):', 'N');
|
|
880
|
+
if (overwrite.toLowerCase() !== 'y' && overwrite.toLowerCase() !== 'yes') {
|
|
881
|
+
console.log(dim(` Keeping existing vaultDir: ${resolve(existingVaultDir)}`));
|
|
882
|
+
resolvedVaultDir = resolve(existingVaultDir);
|
|
883
|
+
}
|
|
863
884
|
}
|
|
864
885
|
}
|
|
865
886
|
|
|
@@ -869,13 +890,21 @@ async function runSetup() {
|
|
|
869
890
|
vaultConfig.devDir = join(HOME, 'dev');
|
|
870
891
|
vaultConfig.mode = 'local';
|
|
871
892
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
893
|
+
if (isDryRun) {
|
|
894
|
+
console.log(`\n ${yellow('[dry-run]')} Would write config: ${configPath}`);
|
|
895
|
+
console.log(dim(` ${JSON.stringify(vaultConfig, null, 2)}`));
|
|
896
|
+
} else {
|
|
897
|
+
assertNotTestMode(configPath);
|
|
898
|
+
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + '\n');
|
|
899
|
+
console.log(`\n ${green('+')} Wrote ${configPath}`);
|
|
900
|
+
}
|
|
875
901
|
|
|
876
902
|
// Pre-download embedding model with spinner (skip with --skip-embeddings)
|
|
877
|
-
const skipEmbeddings = flags.has('--skip-embeddings');
|
|
878
|
-
if (
|
|
903
|
+
const skipEmbeddings = flags.has('--skip-embeddings') || isDryRun;
|
|
904
|
+
if (isDryRun) {
|
|
905
|
+
console.log(`\n ${dim('[3/7]')}${bold(' Embedding model')} ${yellow('(dry-run, skipped)')}`);
|
|
906
|
+
console.log(` ${yellow('[dry-run]')} Would download embedding model (~22MB)`);
|
|
907
|
+
} else if (skipEmbeddings) {
|
|
879
908
|
console.log(`\n ${dim('[3/7]')}${bold(' Embedding model')} ${dim('(skipped)')}`);
|
|
880
909
|
console.log(dim(' FTS-only mode — full-text search works, semantic search disabled.'));
|
|
881
910
|
console.log(dim(' To enable later: context-vault setup (without --skip-embeddings)'));
|
|
@@ -956,10 +985,14 @@ async function runSetup() {
|
|
|
956
985
|
// Clean up legacy project-root config.json if it exists
|
|
957
986
|
const legacyConfigPath = join(ROOT, 'config.json');
|
|
958
987
|
if (existsSync(legacyConfigPath)) {
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
988
|
+
if (isDryRun) {
|
|
989
|
+
console.log(` ${yellow('[dry-run]')} Would remove legacy config: ${legacyConfigPath}`);
|
|
990
|
+
} else {
|
|
991
|
+
try {
|
|
992
|
+
unlinkSync(legacyConfigPath);
|
|
993
|
+
console.log(` ${dim('Removed legacy config at ' + legacyConfigPath)}`);
|
|
994
|
+
} catch {}
|
|
995
|
+
}
|
|
963
996
|
}
|
|
964
997
|
|
|
965
998
|
// Configure each tool — always pass vault dir explicitly to prevent config drift
|
|
@@ -969,19 +1002,24 @@ async function runSetup() {
|
|
|
969
1002
|
const customVaultDir = resolvedVaultDir;
|
|
970
1003
|
|
|
971
1004
|
for (const tool of selected) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
await configureCodex(tool, customVaultDir);
|
|
975
|
-
} else if (tool.configType === 'cli') {
|
|
976
|
-
await configureClaude(tool, customVaultDir);
|
|
977
|
-
} else {
|
|
978
|
-
configureJsonTool(tool, customVaultDir);
|
|
979
|
-
}
|
|
1005
|
+
if (isDryRun) {
|
|
1006
|
+
console.log(` ${yellow('[dry-run]')} Would configure: ${tool.name} (${tool.configPath || tool.id})`);
|
|
980
1007
|
results.push({ tool, ok: true });
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1008
|
+
} else {
|
|
1009
|
+
try {
|
|
1010
|
+
if (tool.configType === 'cli' && tool.id === 'codex') {
|
|
1011
|
+
await configureCodex(tool, customVaultDir);
|
|
1012
|
+
} else if (tool.configType === 'cli') {
|
|
1013
|
+
await configureClaude(tool, customVaultDir);
|
|
1014
|
+
} else {
|
|
1015
|
+
configureJsonTool(tool, customVaultDir);
|
|
1016
|
+
}
|
|
1017
|
+
results.push({ tool, ok: true });
|
|
1018
|
+
console.log(` ${green('+')} ${tool.name} — configured`);
|
|
1019
|
+
} catch (e) {
|
|
1020
|
+
results.push({ tool, ok: false, error: e.message });
|
|
1021
|
+
console.log(` ${red('x')} ${tool.name} — ${e.message}`);
|
|
1022
|
+
}
|
|
985
1023
|
}
|
|
986
1024
|
}
|
|
987
1025
|
|
|
@@ -993,188 +1031,218 @@ async function runSetup() {
|
|
|
993
1031
|
const installedRulesPaths = [];
|
|
994
1032
|
|
|
995
1033
|
if (claudeConfigured) {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (hookInstalled) console.log(` ${green('+')} Memory recall hook installed`);
|
|
1009
|
-
} catch (e) {
|
|
1010
|
-
console.log(` ${red('x')} Memory hook failed: ${e.message}`);
|
|
1011
|
-
}
|
|
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}`);
|
|
1034
|
+
if (isDryRun) {
|
|
1035
|
+
console.log(` ${yellow('[dry-run]')} Would install Claude Code hooks (memory recall, session capture, auto-capture)`);
|
|
1036
|
+
console.log(` ${yellow('[dry-run]')} Would install Claude Code skills (compile-context, vault-setup)`);
|
|
1037
|
+
} else {
|
|
1038
|
+
// Bundled hooks prompt: one Y/n for all three hooks
|
|
1039
|
+
let installHooks = hookFlag || useRecommendedDefaults;
|
|
1040
|
+
if (!hookFlag && !isNonInteractive && !useRecommendedDefaults) {
|
|
1041
|
+
console.log(dim(' Install Claude Code hooks? (recommended)'));
|
|
1042
|
+
console.log(dim(' Memory recall, session capture, and auto-capture.'));
|
|
1043
|
+
console.log();
|
|
1044
|
+
const answer = await prompt(' Install Claude Code hooks? (Y/n):', 'Y');
|
|
1045
|
+
installHooks = answer.toLowerCase() !== 'n';
|
|
1017
1046
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1047
|
+
if (installHooks) {
|
|
1048
|
+
try {
|
|
1049
|
+
const hookInstalled = installClaudeHook();
|
|
1050
|
+
if (hookInstalled) console.log(` ${green('+')} Memory recall hook installed`);
|
|
1051
|
+
} catch (e) {
|
|
1052
|
+
console.log(` ${red('x')} Memory hook failed: ${e.message}`);
|
|
1053
|
+
}
|
|
1054
|
+
try {
|
|
1055
|
+
const captureInstalled = installSessionCaptureHook();
|
|
1056
|
+
if (captureInstalled) console.log(` ${green('+')} Session capture hook installed`);
|
|
1057
|
+
} catch (e) {
|
|
1058
|
+
console.log(` ${red('x')} Session capture hook failed: ${e.message}`);
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
const acInstalled = installPostToolCallHook();
|
|
1062
|
+
if (acInstalled) console.log(` ${green('+')} Auto-capture hook installed`);
|
|
1063
|
+
} catch (e) {
|
|
1064
|
+
console.log(` ${red('x')} Auto-capture hook failed: ${e.message}`);
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
console.log(dim(` Hooks skipped. Install later: context-vault hooks install`));
|
|
1023
1068
|
}
|
|
1024
|
-
} else {
|
|
1025
|
-
console.log(dim(` Hooks skipped. Install later: context-vault hooks install`));
|
|
1026
|
-
}
|
|
1027
1069
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1070
|
+
// Skills (bundled, no separate prompt unless not using fast path)
|
|
1071
|
+
let installSkillsFlag = useRecommendedDefaults || isNonInteractive;
|
|
1072
|
+
if (!isNonInteractive && !useRecommendedDefaults) {
|
|
1073
|
+
console.log();
|
|
1074
|
+
console.log(dim(' Install Claude Code skills? (recommended)'));
|
|
1075
|
+
console.log(dim(' compile-context, vault-setup'));
|
|
1076
|
+
console.log();
|
|
1077
|
+
const skillAnswer = await prompt(' Install Claude Code skills? (Y/n):', 'Y');
|
|
1078
|
+
installSkillsFlag = skillAnswer.toLowerCase() !== 'n';
|
|
1079
|
+
}
|
|
1080
|
+
if (installSkillsFlag) {
|
|
1081
|
+
try {
|
|
1082
|
+
const names = installSkills();
|
|
1083
|
+
for (const name of names) {
|
|
1084
|
+
console.log(` ${green('+')} ${name} skill installed`);
|
|
1085
|
+
}
|
|
1086
|
+
} catch (e) {
|
|
1087
|
+
console.log(` ${red('x')} Skills install failed: ${e.message}`);
|
|
1043
1088
|
}
|
|
1044
|
-
}
|
|
1045
|
-
console.log(`
|
|
1089
|
+
} else {
|
|
1090
|
+
console.log(dim(` Skills skipped. Install later: context-vault skills install`));
|
|
1046
1091
|
}
|
|
1047
|
-
} else {
|
|
1048
|
-
console.log(dim(` Skills skipped. Install later: context-vault skills install`));
|
|
1049
1092
|
}
|
|
1050
1093
|
}
|
|
1051
1094
|
|
|
1052
1095
|
// Agent rules installation
|
|
1053
1096
|
if (configuredTools.length > 0 && !flags.has('--no-rules')) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1097
|
+
if (isDryRun) {
|
|
1098
|
+
for (const tool of configuredTools) {
|
|
1099
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
1100
|
+
console.log(` ${yellow('[dry-run]')} Would install agent rules for ${tool.name}${rulesPath ? ': ' + rulesPath : ''}`);
|
|
1101
|
+
}
|
|
1102
|
+
} else {
|
|
1103
|
+
let installRules = isNonInteractive || useRecommendedDefaults;
|
|
1104
|
+
if (!isNonInteractive && !useRecommendedDefaults) {
|
|
1105
|
+
console.log();
|
|
1106
|
+
console.log(dim(' Install agent rules? (recommended)'));
|
|
1107
|
+
console.log(dim(' Teaches your AI agent when and how to save knowledge to the vault.'));
|
|
1108
|
+
console.log();
|
|
1109
|
+
const rulesAnswer = await prompt(' Install agent rules? (Y/n):', 'Y');
|
|
1110
|
+
installRules = rulesAnswer.toLowerCase() !== 'n';
|
|
1111
|
+
}
|
|
1112
|
+
if (installRules) {
|
|
1113
|
+
const rulesContent = loadAgentRules();
|
|
1114
|
+
if (rulesContent) {
|
|
1115
|
+
for (const tool of configuredTools) {
|
|
1116
|
+
try {
|
|
1117
|
+
const installed = installAgentRulesForTool(tool, rulesContent);
|
|
1118
|
+
const rulesPath = getRulesPathForTool(tool);
|
|
1119
|
+
if (installed) {
|
|
1120
|
+
console.log(` ${green('+')} ${tool.name} agent rules installed`);
|
|
1121
|
+
if (rulesPath) {
|
|
1122
|
+
console.log(` ${dim(rulesPath)}`);
|
|
1123
|
+
installedRulesPaths.push({ tool: tool.name, path: rulesPath });
|
|
1124
|
+
}
|
|
1075
1125
|
}
|
|
1126
|
+
} catch (e) {
|
|
1127
|
+
console.log(` ${red('x')} ${tool.name} rules: ${e.message}`);
|
|
1076
1128
|
}
|
|
1077
|
-
} catch (e) {
|
|
1078
|
-
console.log(` ${red('x')} ${tool.name} rules: ${e.message}`);
|
|
1079
1129
|
}
|
|
1130
|
+
} else {
|
|
1131
|
+
console.log(dim(' Agent rules file not found in package.'));
|
|
1080
1132
|
}
|
|
1081
1133
|
} else {
|
|
1082
|
-
console.log(dim('
|
|
1134
|
+
console.log(dim(' Rules skipped. Install later: context-vault rules install'));
|
|
1083
1135
|
}
|
|
1084
|
-
} else {
|
|
1085
|
-
console.log(dim(' Rules skipped. Install later: context-vault rules install'));
|
|
1086
1136
|
}
|
|
1087
1137
|
} else if (flags.has('--no-rules')) {
|
|
1088
1138
|
console.log(dim(' Agent rules skipped (--no-rules)'));
|
|
1089
1139
|
}
|
|
1090
1140
|
|
|
1091
1141
|
// Seed entry
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
)
|
|
1142
|
+
if (isDryRun) {
|
|
1143
|
+
console.log(`\n ${yellow('[dry-run]')} Would create seed entries in ${resolvedVaultDir}`);
|
|
1144
|
+
} else {
|
|
1145
|
+
const seeded = createSeedEntries(resolvedVaultDir);
|
|
1146
|
+
if (seeded > 0) {
|
|
1147
|
+
console.log(
|
|
1148
|
+
`\n ${green('+')} Created ${seeded} starter ${seeded === 1 ? 'entry' : 'entries'} in vault`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1097
1151
|
}
|
|
1098
1152
|
|
|
1099
1153
|
// Telemetry opt-in (moved to end, after user has seen value)
|
|
1100
1154
|
console.log(`\n ${dim('[6/7]')}${bold(' Anonymous error reporting\n')}`);
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1155
|
+
if (isDryRun) {
|
|
1156
|
+
console.log(` ${yellow('[dry-run]')} Would prompt for telemetry preference`);
|
|
1157
|
+
console.log(` ${yellow('[dry-run]')} Would update config: ${configPath}`);
|
|
1158
|
+
} else {
|
|
1159
|
+
verbose(userLevel, 'Entirely optional. Works identically either way.\n');
|
|
1160
|
+
console.log(dim(' When enabled, unhandled errors send a minimal event (type, tool name,'));
|
|
1161
|
+
console.log(dim(' version, platform) to help diagnose issues. No vault content,'));
|
|
1162
|
+
console.log(dim(' file paths, or personal data is ever sent. Off by default.'));
|
|
1163
|
+
console.log(dim(` Full schema: ${MARKETING_URL}/telemetry`));
|
|
1164
|
+
console.log();
|
|
1107
1165
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1166
|
+
let telemetryEnabled = vaultConfig.telemetry === true;
|
|
1167
|
+
if (!isNonInteractive && !useRecommendedDefaults) {
|
|
1168
|
+
const defaultChoice = telemetryEnabled ? 'Y' : 'n';
|
|
1169
|
+
const telemetryAnswer = await prompt(
|
|
1170
|
+
` Enable anonymous error reporting? (y/N):`,
|
|
1171
|
+
defaultChoice
|
|
1172
|
+
);
|
|
1173
|
+
telemetryEnabled =
|
|
1174
|
+
telemetryAnswer.toLowerCase() === 'y' || telemetryAnswer.toLowerCase() === 'yes';
|
|
1175
|
+
}
|
|
1176
|
+
vaultConfig.telemetry = telemetryEnabled;
|
|
1177
|
+
console.log(
|
|
1178
|
+
` ${telemetryEnabled ? green('+') : dim('-')} Telemetry: ${telemetryEnabled ? 'enabled' : 'disabled'}`
|
|
1114
1179
|
);
|
|
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
1180
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1181
|
+
// Re-write config with telemetry setting
|
|
1182
|
+
assertNotTestMode(configPath);
|
|
1183
|
+
writeFileSync(configPath, JSON.stringify(vaultConfig, null, 2) + '\n');
|
|
1184
|
+
}
|
|
1126
1185
|
|
|
1127
1186
|
// Health check
|
|
1128
1187
|
console.log(`\n ${dim('[7/7]')}${bold(' Health check...')}\n`);
|
|
1129
|
-
verbose(userLevel, 'Verifying vault, config, and database are accessible.\n');
|
|
1130
1188
|
const okResults = results.filter((r) => r.ok);
|
|
1189
|
+
let passed = 0;
|
|
1190
|
+
let checksTotal = 0;
|
|
1131
1191
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
const db = await initDatabase(vaultConfig.dbPath);
|
|
1138
|
-
db.prepare('SELECT 1').get();
|
|
1139
|
-
db.close();
|
|
1140
|
-
dbAccessible = true;
|
|
1141
|
-
} catch (e) {
|
|
1142
|
-
dbError = e;
|
|
1143
|
-
}
|
|
1192
|
+
if (isDryRun) {
|
|
1193
|
+
console.log(` ${yellow('[dry-run]')} Skipping health check (no files were written)`);
|
|
1194
|
+
console.log(` ${yellow('[dry-run]')} Skipping smoke test`);
|
|
1195
|
+
} else {
|
|
1196
|
+
verbose(userLevel, 'Verifying vault, config, and database are accessible.\n');
|
|
1144
1197
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
{
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1198
|
+
// Verify DB is accessible
|
|
1199
|
+
let dbAccessible = false;
|
|
1200
|
+
let dbError = null;
|
|
1201
|
+
try {
|
|
1202
|
+
const { initDatabase } = await import('@context-vault/core/db');
|
|
1203
|
+
const db = await initDatabase(vaultConfig.dbPath);
|
|
1204
|
+
db.prepare('SELECT 1').get();
|
|
1205
|
+
db.close();
|
|
1206
|
+
dbAccessible = true;
|
|
1207
|
+
} catch (e) {
|
|
1208
|
+
dbError = e;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
const checks = [
|
|
1212
|
+
{ label: 'Vault directory exists', pass: existsSync(resolvedVaultDir) },
|
|
1213
|
+
{ label: 'Config file written', pass: existsSync(configPath) },
|
|
1214
|
+
{ label: 'Database accessible', pass: dbAccessible, error: dbError },
|
|
1215
|
+
{ label: 'At least one tool configured', pass: okResults.length > 0 },
|
|
1216
|
+
];
|
|
1217
|
+
passed = checks.filter((c) => c.pass).length;
|
|
1218
|
+
checksTotal = checks.length;
|
|
1219
|
+
for (const c of checks) {
|
|
1220
|
+
console.log(` ${c.pass ? green('✓') : red('✗')} ${c.label}`);
|
|
1221
|
+
if (!c.pass && c.error) {
|
|
1222
|
+
console.log(` ${dim(c.error.message)}`);
|
|
1223
|
+
if (c.error.message.includes('EACCES') || c.error.message.includes('permission')) {
|
|
1224
|
+
console.log(` ${dim('Fix: check file permissions on ' + vaultConfig.dbPath)}`);
|
|
1225
|
+
}
|
|
1158
1226
|
}
|
|
1159
1227
|
}
|
|
1160
|
-
}
|
|
1161
1228
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1229
|
+
// Smoke test — write and read a test file to verify vault I/O
|
|
1230
|
+
{
|
|
1231
|
+
const testFile = join(resolvedVaultDir, '.smoke-test-' + Date.now() + '.md');
|
|
1232
|
+
try {
|
|
1233
|
+
writeFileSync(testFile, '# Smoke test\n');
|
|
1234
|
+
const content = readFileSync(testFile, 'utf-8');
|
|
1235
|
+
unlinkSync(testFile);
|
|
1236
|
+
if (content.includes('Smoke test')) {
|
|
1237
|
+
console.log(` ${green('✓')} Smoke test: vault read/write verified`);
|
|
1238
|
+
} else {
|
|
1239
|
+
console.log(` ${red('✗')} Smoke test: file written but content mismatch`);
|
|
1240
|
+
}
|
|
1241
|
+
} catch (e) {
|
|
1242
|
+
try { unlinkSync(testFile); } catch {}
|
|
1243
|
+
console.log(` ${red('✗')} Smoke test failed: ${e.message}`);
|
|
1244
|
+
console.log(` ${dim('Check permissions on ' + resolvedVaultDir)}`);
|
|
1173
1245
|
}
|
|
1174
|
-
} catch (e) {
|
|
1175
|
-
try { unlinkSync(testFile); } catch {}
|
|
1176
|
-
console.log(` ${red('✗')} Smoke test failed: ${e.message}`);
|
|
1177
|
-
console.log(` ${dim('Check permissions on ' + resolvedVaultDir)}`);
|
|
1178
1246
|
}
|
|
1179
1247
|
}
|
|
1180
1248
|
|
|
@@ -1184,9 +1252,26 @@ async function runSetup() {
|
|
|
1184
1252
|
const cli = isNpx() ? 'npx context-vault' : 'context-vault';
|
|
1185
1253
|
|
|
1186
1254
|
let boxLines;
|
|
1255
|
+
if (isDryRun) {
|
|
1256
|
+
boxLines = [
|
|
1257
|
+
` ${yellow('Dry run complete')} (${elapsed}s)`,
|
|
1258
|
+
``,
|
|
1259
|
+
` No files were written. Run without --dry-run to apply.`,
|
|
1260
|
+
];
|
|
1261
|
+
const innerWidth = Math.max(...boxLines.map((l) => l.length)) + 2;
|
|
1262
|
+
const pad = (s) => s + ' '.repeat(Math.max(0, innerWidth - s.length));
|
|
1263
|
+
console.log();
|
|
1264
|
+
console.log(` ${dim('┌' + '─'.repeat(innerWidth) + '┐')}`);
|
|
1265
|
+
for (const line of boxLines) {
|
|
1266
|
+
console.log(` ${dim('│')}${pad(line)}${dim('│')}`);
|
|
1267
|
+
}
|
|
1268
|
+
console.log(` ${dim('└' + '─'.repeat(innerWidth) + '┘')}`);
|
|
1269
|
+
console.log();
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1187
1272
|
if (userLevel === 'beginner') {
|
|
1188
1273
|
boxLines = [
|
|
1189
|
-
` ✓ Setup complete — ${passed}/${
|
|
1274
|
+
` ✓ Setup complete — ${passed}/${checksTotal} checks passed (${elapsed}s)`,
|
|
1190
1275
|
``,
|
|
1191
1276
|
` ${bold('What to do next:')}`,
|
|
1192
1277
|
``,
|
|
@@ -1204,7 +1289,7 @@ async function runSetup() {
|
|
|
1204
1289
|
];
|
|
1205
1290
|
} else {
|
|
1206
1291
|
boxLines = [
|
|
1207
|
-
` ✓ Setup complete — ${passed}/${
|
|
1292
|
+
` ✓ Setup complete — ${passed}/${checksTotal} checks passed (${elapsed}s)`,
|
|
1208
1293
|
``,
|
|
1209
1294
|
` ${bold('Next:')} restart ${toolName} to activate the vault`,
|
|
1210
1295
|
``,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-vault",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Persistent memory for AI agents — saves and searches knowledge across sessions",
|
|
6
6
|
"bin": {
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@context-vault/core"
|
|
64
64
|
],
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@context-vault/core": "^3.5.
|
|
66
|
+
"@context-vault/core": "^3.5.1",
|
|
67
67
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
68
68
|
"adm-zip": "^0.5.16",
|
|
69
69
|
"sqlite-vec": "^0.1.0"
|