brainbank 0.9.0 → 0.9.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/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SUPPORTED_EXTENSIONS,
4
+ VERSION,
4
5
  args,
5
6
  c,
6
7
  createBrain,
@@ -15,7 +16,7 @@ import {
15
16
  printResults,
16
17
  registerConfigCollections,
17
18
  stripFlags
18
- } from "./chunk-Z54MHEYW.js";
19
+ } from "./chunk-6NM6WRDX.js";
19
20
  import {
20
21
  DEFAULT_PORT,
21
22
  isServerRunning,
@@ -28,15 +29,121 @@ import {
28
29
  } from "./chunk-7QVYU63E.js";
29
30
 
30
31
  // src/cli/commands/index.ts
31
- import * as fs2 from "fs";
32
- import * as path2 from "path";
32
+ import * as fs3 from "fs";
33
+ import * as path3 from "path";
33
34
 
34
- // src/cli/commands/scan.ts
35
+ // src/cli/commands/mcp-export.ts
35
36
  import * as fs from "fs";
36
37
  import * as path from "path";
38
+ import { fileURLToPath } from "url";
39
+ var __filename = fileURLToPath(import.meta.url);
40
+ var __dirname = path.dirname(__filename);
41
+ var TARGETS = {
42
+ antigravity: {
43
+ configPath: path.join(process.env.HOME ?? "~", ".gemini", "antigravity", "mcp_config.json"),
44
+ label: "Gemini Antigravity"
45
+ }
46
+ };
47
+ function buildBrainbankMcpBlock(config) {
48
+ const nodeBin = process.execPath;
49
+ const globalCliJs = path.join(path.dirname(nodeBin), "..", "lib", "node_modules", "brainbank", "dist", "cli.js");
50
+ const localCliJs = path.resolve(__dirname, "..", "..", "dist", "cli.js");
51
+ const resolvedCliJs = fs.existsSync(globalCliJs) ? globalCliJs : localCliJs;
52
+ const env = {};
53
+ const keys = config?.keys;
54
+ const perplexityKey = keys?.perplexity ?? process.env.PERPLEXITY_API_KEY;
55
+ const anthropicKey = keys?.anthropic ?? process.env.ANTHROPIC_API_KEY;
56
+ const openaiKey = keys?.openai ?? process.env.OPENAI_API_KEY;
57
+ if (perplexityKey) env.PERPLEXITY_API_KEY = perplexityKey;
58
+ if (anthropicKey) env.ANTHROPIC_API_KEY = anthropicKey;
59
+ if (openaiKey) env.OPENAI_API_KEY = openaiKey;
60
+ const block = {
61
+ command: nodeBin,
62
+ args: [resolvedCliJs, "mcp"]
63
+ };
64
+ if (Object.keys(env).length > 0) {
65
+ block.env = env;
66
+ }
67
+ return block;
68
+ }
69
+ __name(buildBrainbankMcpBlock, "buildBrainbankMcpBlock");
70
+ function mergeAndWrite(targetPath, block) {
71
+ let existing = { mcpServers: {} };
72
+ const created = !fs.existsSync(targetPath);
73
+ if (!created) {
74
+ try {
75
+ const raw = fs.readFileSync(targetPath, "utf-8");
76
+ existing = JSON.parse(raw);
77
+ if (!existing.mcpServers) existing.mcpServers = {};
78
+ } catch {
79
+ existing = { mcpServers: {} };
80
+ }
81
+ }
82
+ existing.mcpServers.brainbank = block;
83
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
84
+ fs.writeFileSync(targetPath, JSON.stringify(existing, null, 2) + "\n");
85
+ return { created };
86
+ }
87
+ __name(mergeAndWrite, "mergeAndWrite");
88
+ function hasBrainbankMcpEntry(targetPath) {
89
+ if (!fs.existsSync(targetPath)) return false;
90
+ try {
91
+ const raw = fs.readFileSync(targetPath, "utf-8");
92
+ const config = JSON.parse(raw);
93
+ return !!config.mcpServers?.brainbank;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+ __name(hasBrainbankMcpEntry, "hasBrainbankMcpEntry");
99
+ async function autoExportMcp(repoPath) {
100
+ const target = TARGETS.antigravity;
101
+ if (!target) return;
102
+ const antigravityDir = path.dirname(target.configPath);
103
+ if (!fs.existsSync(antigravityDir)) return;
104
+ if (hasBrainbankMcpEntry(target.configPath)) return;
105
+ const config = await getConfig(repoPath);
106
+ const block = buildBrainbankMcpBlock(config);
107
+ mergeAndWrite(target.configPath, block);
108
+ console.log(` ${c.green("\u2713")} Exported MCP config to ${c.dim(path.relative(process.env.HOME ?? "", target.configPath))}`);
109
+ }
110
+ __name(autoExportMcp, "autoExportMcp");
111
+ async function cmdMcpExport() {
112
+ const targetName = args[1] || getFlag("target") || "antigravity";
113
+ const repoPath = getFlag("repo") || ".";
114
+ const target = TARGETS[targetName];
115
+ if (!target) {
116
+ console.error(c.red(`Unknown export target: ${targetName}`));
117
+ console.error(c.dim(` Available: ${Object.keys(TARGETS).join(", ")}`));
118
+ process.exit(1);
119
+ }
120
+ const config = await getConfig(repoPath);
121
+ const block = buildBrainbankMcpBlock(config);
122
+ const { created } = mergeAndWrite(target.configPath, block);
123
+ console.log(c.bold(`
124
+ \u2501\u2501\u2501 MCP Export: ${target.label} \u2501\u2501\u2501
125
+ `));
126
+ console.log(` ${c.green("\u2713")} ${created ? "Created" : "Updated"} ${c.dim(target.configPath)}`);
127
+ console.log(` ${c.dim("Node:")} ${block.command}`);
128
+ console.log(` ${c.dim("CLI:")} ${block.args[0]}`);
129
+ const envKeys = block.env ? Object.keys(block.env) : [];
130
+ if (envKeys.length > 0) {
131
+ console.log(` ${c.dim("Keys:")} ${envKeys.join(", ")}`);
132
+ } else {
133
+ console.log(` ${c.yellow("\u26A0")} No API keys found. Set env vars or add keys to .brainbank/config.json`);
134
+ }
135
+ console.log(`
136
+ ${c.dim("Restart your IDE to apply changes.")}
137
+ `);
138
+ }
139
+ __name(cmdMcpExport, "cmdMcpExport");
140
+
141
+ // src/cli/commands/scan.ts
142
+ import * as fs2 from "fs";
143
+ import * as path2 from "path";
37
144
  import { execSync } from "child_process";
38
145
  function scanRepo(repoPath) {
39
- const resolved = path.resolve(repoPath);
146
+ const resolved = path2.resolve(repoPath);
40
147
  const gitSubdirs = scanGitSubdirs(resolved);
41
148
  return {
42
149
  repoPath: resolved,
@@ -61,15 +168,15 @@ function scanCodeModule(repoPath) {
61
168
  function walk(dir) {
62
169
  let entries;
63
170
  try {
64
- entries = fs.readdirSync(dir, { withFileTypes: true });
171
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
65
172
  } catch {
66
173
  return;
67
174
  }
68
175
  for (const entry of entries) {
69
- const fullPath = path.join(dir, entry.name);
176
+ const fullPath = path2.join(dir, entry.name);
70
177
  const isDir = entry.isDirectory() || entry.isSymbolicLink() && (() => {
71
178
  try {
72
- return fs.statSync(fullPath).isDirectory();
179
+ return fs2.statSync(fullPath).isDirectory();
73
180
  } catch {
74
181
  return false;
75
182
  }
@@ -79,7 +186,7 @@ function scanCodeModule(repoPath) {
79
186
  walk(fullPath);
80
187
  } else if (entry.isFile()) {
81
188
  if (isIgnoredFile(entry.name)) continue;
82
- const ext = path.extname(entry.name).toLowerCase();
189
+ const ext = path2.extname(entry.name).toLowerCase();
83
190
  const lang = SUPPORTED_EXTENSIONS[ext];
84
191
  if (!lang) continue;
85
192
  byLanguage.set(lang, (byLanguage.get(lang) ?? 0) + 1);
@@ -158,7 +265,7 @@ function scanDocsModule(repoPath) {
158
265
  }
159
266
  __name(scanDocsModule, "scanDocsModule");
160
267
  function scanGitStats(repoPath, gitSubdirs) {
161
- if (fs.existsSync(path.join(repoPath, ".git"))) {
268
+ if (fs2.existsSync(path2.join(repoPath, ".git"))) {
162
269
  return gitStats(repoPath);
163
270
  }
164
271
  if (gitSubdirs.length === 0) return null;
@@ -166,7 +273,7 @@ function scanGitStats(repoPath, gitSubdirs) {
166
273
  let latestMessage = "";
167
274
  let latestDate = "";
168
275
  for (const sub of gitSubdirs) {
169
- const stats = gitStats(path.join(repoPath, sub.name));
276
+ const stats = gitStats(path2.join(repoPath, sub.name));
170
277
  if (stats) {
171
278
  totalCommits += stats.commitCount;
172
279
  if (!latestMessage) {
@@ -192,15 +299,15 @@ __name(gitStats, "gitStats");
192
299
  function scanDocsCollections(repoPath) {
193
300
  const results = [];
194
301
  const seen = /* @__PURE__ */ new Set();
195
- const configPath = path.join(repoPath, ".brainbank", "config.json");
302
+ const configPath = path2.join(repoPath, ".brainbank", "config.json");
196
303
  try {
197
- if (fs.existsSync(configPath)) {
198
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
304
+ if (fs2.existsSync(configPath)) {
305
+ const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
199
306
  const docsCfg = config?.docs;
200
307
  const collections = docsCfg?.collections;
201
308
  if (collections) {
202
309
  for (const coll of collections) {
203
- const absPath = path.resolve(repoPath, coll.path);
310
+ const absPath = path2.resolve(repoPath, coll.path);
204
311
  results.push({ name: coll.name, path: coll.path, fileCount: countDocs(absPath) });
205
312
  seen.add(absPath);
206
313
  }
@@ -213,11 +320,11 @@ function scanDocsCollections(repoPath) {
213
320
  results.push({ name: "(root)", path: ".", fileCount: rootDocs });
214
321
  }
215
322
  try {
216
- for (const entry of fs.readdirSync(repoPath, { withFileTypes: true })) {
323
+ for (const entry of fs2.readdirSync(repoPath, { withFileTypes: true })) {
217
324
  if (!entry.isDirectory()) continue;
218
325
  if (isIgnoredDir(entry.name)) continue;
219
326
  if (entry.name.startsWith(".")) continue;
220
- const dirPath = path.join(repoPath, entry.name);
327
+ const dirPath = path2.join(repoPath, entry.name);
221
328
  if (seen.has(dirPath)) continue;
222
329
  const count = countDocs(dirPath);
223
330
  if (count > 0) {
@@ -232,11 +339,11 @@ __name(scanDocsCollections, "scanDocsCollections");
232
339
  function countDocs(dir) {
233
340
  let count = 0;
234
341
  try {
235
- for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
236
- const ePath = path.join(dir, e.name);
342
+ for (const e of fs2.readdirSync(dir, { withFileTypes: true })) {
343
+ const ePath = path2.join(dir, e.name);
237
344
  const isDir = e.isDirectory() || e.isSymbolicLink() && (() => {
238
345
  try {
239
- return fs.statSync(ePath).isDirectory();
346
+ return fs2.statSync(ePath).isDirectory();
240
347
  } catch {
241
348
  return false;
242
349
  }
@@ -256,7 +363,7 @@ __name(countDocs, "countDocs");
256
363
  function countDocsShallow(dir) {
257
364
  let count = 0;
258
365
  try {
259
- for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
366
+ for (const e of fs2.readdirSync(dir, { withFileTypes: true })) {
260
367
  if (e.isFile() && /\.mdx?$/i.test(e.name)) count++;
261
368
  }
262
369
  } catch {
@@ -265,10 +372,10 @@ function countDocsShallow(dir) {
265
372
  }
266
373
  __name(countDocsShallow, "countDocsShallow");
267
374
  function scanConfig(repoPath) {
268
- const configPath = path.join(repoPath, ".brainbank", "config.json");
269
- if (!fs.existsSync(configPath)) return { exists: false };
375
+ const configPath = path2.join(repoPath, ".brainbank", "config.json");
376
+ if (!fs2.existsSync(configPath)) return { exists: false };
270
377
  try {
271
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
378
+ const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
272
379
  const codeCfg = config?.code;
273
380
  return {
274
381
  exists: true,
@@ -281,10 +388,10 @@ function scanConfig(repoPath) {
281
388
  }
282
389
  __name(scanConfig, "scanConfig");
283
390
  function scanDb(repoPath) {
284
- const dbPath = path.join(repoPath, ".brainbank", "data", "brainbank.db");
285
- if (!fs.existsSync(dbPath)) return { exists: false, sizeMB: 0 };
391
+ const dbPath = path2.join(repoPath, ".brainbank", "data", "brainbank.db");
392
+ if (!fs2.existsSync(dbPath)) return { exists: false, sizeMB: 0 };
286
393
  try {
287
- const stat = fs.statSync(dbPath);
394
+ const stat = fs2.statSync(dbPath);
288
395
  return {
289
396
  exists: true,
290
397
  sizeMB: Math.round(stat.size / 1024 / 1024 * 10) / 10,
@@ -296,19 +403,19 @@ function scanDb(repoPath) {
296
403
  }
297
404
  __name(scanDb, "scanDb");
298
405
  function scanGitSubdirs(repoPath) {
299
- if (fs.existsSync(path.join(repoPath, ".git"))) return [];
406
+ if (fs2.existsSync(path2.join(repoPath, ".git"))) return [];
300
407
  try {
301
- let subdirs = fs.readdirSync(repoPath, { withFileTypes: true }).filter((e) => {
408
+ let subdirs = fs2.readdirSync(repoPath, { withFileTypes: true }).filter((e) => {
302
409
  if (e.name.startsWith(".")) return false;
303
410
  const isDir = e.isDirectory() || e.isSymbolicLink() && (() => {
304
411
  try {
305
- return fs.statSync(path.join(repoPath, e.name)).isDirectory();
412
+ return fs2.statSync(path2.join(repoPath, e.name)).isDirectory();
306
413
  } catch {
307
414
  return false;
308
415
  }
309
416
  })();
310
417
  return isDir;
311
- }).filter((e) => fs.existsSync(path.join(repoPath, e.name, ".git"))).map((e) => ({ name: e.name }));
418
+ }).filter((e) => fs2.existsSync(path2.join(repoPath, e.name, ".git"))).map((e) => ({ name: e.name }));
312
419
  const configRepos = readReposFromConfig(repoPath);
313
420
  if (configRepos) {
314
421
  subdirs = subdirs.filter((s) => configRepos.includes(s.name));
@@ -320,10 +427,10 @@ function scanGitSubdirs(repoPath) {
320
427
  }
321
428
  __name(scanGitSubdirs, "scanGitSubdirs");
322
429
  function readReposFromConfig(repoPath) {
323
- const configPath = path.join(repoPath, ".brainbank", "config.json");
430
+ const configPath = path2.join(repoPath, ".brainbank", "config.json");
324
431
  try {
325
- if (!fs.existsSync(configPath)) return null;
326
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
432
+ if (!fs2.existsSync(configPath)) return null;
433
+ const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
327
434
  const repos = config.repos;
328
435
  if (Array.isArray(repos) && repos.every((r) => typeof r === "string")) {
329
436
  return repos;
@@ -383,8 +490,8 @@ async function cmdIndex() {
383
490
  const config = await getConfig(repoPath);
384
491
  await registerConfigCollections(brain, repoPath, config);
385
492
  if (docsPath) {
386
- const absDocsPath = path2.resolve(docsPath);
387
- const collName = path2.basename(absDocsPath);
493
+ const absDocsPath = path3.resolve(docsPath);
494
+ const collName = path3.basename(absDocsPath);
388
495
  try {
389
496
  const docsPlugin = findDocsPlugin(brain);
390
497
  await docsPlugin?.addCollection({
@@ -428,6 +535,7 @@ async function cmdIndex() {
428
535
  console.log(` ${name}: ${entries}`);
429
536
  }
430
537
  brain.close();
538
+ await autoExportMcp(repoPath);
431
539
  }
432
540
  __name(cmdIndex, "cmdIndex");
433
541
  function printScanTree(scan, depth) {
@@ -506,7 +614,7 @@ function capitalizeFirst(s) {
506
614
  }
507
615
  __name(capitalizeFirst, "capitalizeFirst");
508
616
  async function saveConfig(repoPath, modules) {
509
- const { select } = await import("@inquirer/prompts");
617
+ const { select, confirm } = await import("@inquirer/prompts");
510
618
  const envEmbedding = process.env.BRAINBANK_EMBEDDING;
511
619
  const embedding = await select({
512
620
  message: "Embedding provider:",
@@ -534,18 +642,18 @@ async function saveConfig(repoPath, modules) {
534
642
  message: "Noise pruner:",
535
643
  choices: [
536
644
  {
537
- name: "none \u2014 no pruning (default)",
538
- value: "none"
645
+ name: "haiku \u2014 AI-powered noise filter (recommended)",
646
+ value: "haiku"
539
647
  },
540
648
  {
541
- name: "haiku \u2014 AI-powered noise filter (Claude Haiku)",
542
- value: "haiku"
649
+ name: "none \u2014 no pruning",
650
+ value: "none"
543
651
  }
544
652
  ],
545
- default: "none"
653
+ default: "haiku"
546
654
  });
547
- const configDir = path2.join(repoPath, ".brainbank");
548
- const configPath = path2.join(configDir, "config.json");
655
+ const configDir = path3.join(repoPath, ".brainbank");
656
+ const configPath = path3.join(configDir, "config.json");
549
657
  const config = {
550
658
  plugins: modules,
551
659
  embedding
@@ -553,9 +661,32 @@ async function saveConfig(repoPath, modules) {
553
661
  if (pruner !== "none") {
554
662
  config.pruner = pruner;
555
663
  }
556
- fs2.mkdirSync(configDir, { recursive: true });
557
- fs2.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
558
- console.log(c.green(` \u2713 Saved ${path2.relative(process.cwd(), configPath)}`));
664
+ const detectedKeys = {};
665
+ const needsPerplexity = embedding.startsWith("perplexity");
666
+ const needsAnthropic = pruner === "haiku";
667
+ const needsOpenai = embedding === "openai";
668
+ if (needsPerplexity && process.env.PERPLEXITY_API_KEY) {
669
+ detectedKeys.perplexity = process.env.PERPLEXITY_API_KEY;
670
+ }
671
+ if (needsAnthropic && process.env.ANTHROPIC_API_KEY) {
672
+ detectedKeys.anthropic = process.env.ANTHROPIC_API_KEY;
673
+ }
674
+ if (needsOpenai && process.env.OPENAI_API_KEY) {
675
+ detectedKeys.openai = process.env.OPENAI_API_KEY;
676
+ }
677
+ if (Object.keys(detectedKeys).length > 0) {
678
+ const keyNames = Object.keys(detectedKeys).join(", ");
679
+ const saveKeys = await confirm({
680
+ message: `Save API keys (${keyNames}) to config.json? (portable, no env vars needed)`,
681
+ default: true
682
+ });
683
+ if (saveKeys) {
684
+ config.keys = detectedKeys;
685
+ }
686
+ }
687
+ fs3.mkdirSync(configDir, { recursive: true });
688
+ fs3.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
689
+ console.log(c.green(` \u2713 Saved ${path3.relative(process.cwd(), configPath)}`));
559
690
  }
560
691
  __name(saveConfig, "saveConfig");
561
692
 
@@ -564,12 +695,12 @@ async function cmdCollection() {
564
695
  const pos = stripFlags(args);
565
696
  const sub = pos[1];
566
697
  if (sub === "add") {
567
- const path3 = pos[2];
698
+ const path4 = pos[2];
568
699
  const name = getFlag("name");
569
700
  const pattern = getFlag("pattern") ?? "**/*.md";
570
701
  const context = getFlag("context");
571
702
  const ignoreRaw = getFlag("ignore");
572
- if (!path3 || !name) {
703
+ if (!path4 || !name) {
573
704
  console.log(c.red('Usage: brainbank collection add <path> --name <name> [--pattern "**/*.md"] [--ignore "glob"] [--context "description"]'));
574
705
  process.exit(1);
575
706
  }
@@ -581,12 +712,12 @@ async function cmdCollection() {
581
712
  }
582
713
  await docsPlugin.addCollection({
583
714
  name,
584
- path: path3,
715
+ path: path4,
585
716
  pattern,
586
717
  ignore: ignoreRaw ? ignoreRaw.split(",") : [],
587
718
  context: context ?? void 0
588
719
  });
589
- console.log(c.green(`\u2713 Collection '${name}' added: ${path3} (${pattern})`));
720
+ console.log(c.green(`\u2713 Collection '${name}' added: ${path4} (${pattern})`));
590
721
  if (context) console.log(c.dim(` Context: ${context}`));
591
722
  brain.close();
592
723
  return;
@@ -964,12 +1095,12 @@ async function serverHealth() {
964
1095
  }
965
1096
  }
966
1097
  __name(serverHealth, "serverHealth");
967
- function httpPost(port, path3, body) {
968
- return new Promise((resolve3, reject) => {
1098
+ function httpPost(port, path4, body) {
1099
+ return new Promise((resolve4, reject) => {
969
1100
  const req = http.request({
970
1101
  hostname: "127.0.0.1",
971
1102
  port,
972
- path: path3,
1103
+ path: path4,
973
1104
  method: "POST",
974
1105
  headers: {
975
1106
  "Content-Type": "application/json",
@@ -980,7 +1111,7 @@ function httpPost(port, path3, body) {
980
1111
  }, (res) => {
981
1112
  const chunks = [];
982
1113
  res.on("data", (chunk) => chunks.push(chunk));
983
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
1114
+ res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
984
1115
  });
985
1116
  req.on("error", reject);
986
1117
  req.on("timeout", () => {
@@ -992,18 +1123,18 @@ function httpPost(port, path3, body) {
992
1123
  });
993
1124
  }
994
1125
  __name(httpPost, "httpPost");
995
- function httpGet(port, path3) {
996
- return new Promise((resolve3, reject) => {
1126
+ function httpGet(port, path4) {
1127
+ return new Promise((resolve4, reject) => {
997
1128
  const req = http.request({
998
1129
  hostname: "127.0.0.1",
999
1130
  port,
1000
- path: path3,
1131
+ path: path4,
1001
1132
  method: "GET",
1002
1133
  timeout: 5e3
1003
1134
  }, (res) => {
1004
1135
  const chunks = [];
1005
1136
  res.on("data", (chunk) => chunks.push(chunk));
1006
- res.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
1137
+ res.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
1007
1138
  });
1008
1139
  req.on("error", reject);
1009
1140
  req.on("timeout", () => {
@@ -1062,9 +1193,9 @@ async function cmdContext() {
1062
1193
  const sub = pos[1];
1063
1194
  if (sub === "add") {
1064
1195
  const collection = pos[2];
1065
- const path3 = pos[3];
1196
+ const path4 = pos[3];
1066
1197
  const desc = pos.slice(4).join(" ");
1067
- if (!collection || !path3 || !desc) {
1198
+ if (!collection || !path4 || !desc) {
1068
1199
  console.log(c.red("Usage: brainbank context add <collection> <path> <description>"));
1069
1200
  process.exit(1);
1070
1201
  }
@@ -1075,8 +1206,8 @@ async function cmdContext() {
1075
1206
  console.log(c.red("Docs plugin not loaded."));
1076
1207
  process.exit(1);
1077
1208
  }
1078
- docsPlugin.addContext(collection, path3, desc);
1079
- console.log(c.green(`\u2713 Context added: ${collection}:${path3} \u2192 "${desc}"`));
1209
+ docsPlugin.addContext(collection, path4, desc);
1210
+ console.log(c.green(`\u2713 Context added: ${collection}:${path4} \u2192 "${desc}"`));
1080
1211
  brain2.close();
1081
1212
  return;
1082
1213
  }
@@ -1482,11 +1613,13 @@ function showHelp() {
1482
1613
  console.log(` ${c.cyan("reembed")} Re-embed all vectors`);
1483
1614
  console.log(` ${c.cyan("watch")} Watch files, auto-re-index`);
1484
1615
  console.log(` ${c.cyan("mcp")} Start MCP server (stdio)`);
1616
+ console.log(` ${c.cyan("mcp:export")} [target] Export MCP config (antigravity)`);
1485
1617
  console.log(` ${c.cyan("daemon")} Start HTTP daemon (foreground)`);
1486
1618
  console.log(` ${c.cyan("daemon start")} Start HTTP daemon (background)`);
1487
1619
  console.log(` ${c.cyan("daemon stop")} Stop background daemon`);
1488
1620
  console.log(` ${c.cyan("daemon restart")} Restart background daemon`);
1489
1621
  console.log(` ${c.cyan("status")} Show daemon status`);
1622
+ console.log(` ${c.cyan("--version")} ${c.dim("(-v)")} Show version`);
1490
1623
  console.log("");
1491
1624
  console.log(c.bold("Options:"));
1492
1625
  console.log(` ${c.dim("--repo <path>")} Repository path (default: .)`);
@@ -1515,6 +1648,7 @@ function showHelp() {
1515
1648
  console.log(c.dim(' brainbank context "auth flow" | pbcopy # \u2192 clipboard'));
1516
1649
  console.log(c.dim(" brainbank daemon start # background HTTP"));
1517
1650
  console.log(c.dim(" brainbank mcp # MCP stdio"));
1651
+ console.log(c.dim(" brainbank mcp:export antigravity # export MCP config"));
1518
1652
  }
1519
1653
  __name(showHelp, "showHelp");
1520
1654
 
@@ -1522,6 +1656,10 @@ __name(showHelp, "showHelp");
1522
1656
  var command = args[0];
1523
1657
  async function main() {
1524
1658
  switch (command) {
1659
+ case "--version":
1660
+ case "-v":
1661
+ console.log(`brainbank v${VERSION}`);
1662
+ break;
1525
1663
  case "i":
1526
1664
  case "index":
1527
1665
  return cmdIndex();
@@ -1551,6 +1689,8 @@ async function main() {
1551
1689
  return cmdWatch();
1552
1690
  case "mcp":
1553
1691
  return cmdMcp();
1692
+ case "mcp:export":
1693
+ return cmdMcpExport();
1554
1694
  case "serve":
1555
1695
  return cmdMcp();
1556
1696
  // backward compat