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/{chunk-Z54MHEYW.js → chunk-6NM6WRDX.js} +6 -1
- package/dist/chunk-6NM6WRDX.js.map +1 -0
- package/dist/cli.js +203 -63
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/help.ts +3 -0
- package/src/cli/commands/index.ts +37 -6
- package/src/cli/commands/mcp-export.ts +163 -0
- package/src/cli/index.ts +7 -0
- package/src/constants.ts +8 -0
- package/dist/chunk-Z54MHEYW.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainbank",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Pluggable semantic memory for AI agents — hybrid search (vector + BM25) in a single SQLite file. Built-in code, git, and docs indexers. Bring your own.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/cli/commands/help.ts
CHANGED
|
@@ -36,11 +36,13 @@ export function showHelp(): void {
|
|
|
36
36
|
console.log(` ${c.cyan('reembed')} Re-embed all vectors`);
|
|
37
37
|
console.log(` ${c.cyan('watch')} Watch files, auto-re-index`);
|
|
38
38
|
console.log(` ${c.cyan('mcp')} Start MCP server (stdio)`);
|
|
39
|
+
console.log(` ${c.cyan('mcp:export')} [target] Export MCP config (antigravity)`);
|
|
39
40
|
console.log(` ${c.cyan('daemon')} Start HTTP daemon (foreground)`);
|
|
40
41
|
console.log(` ${c.cyan('daemon start')} Start HTTP daemon (background)`);
|
|
41
42
|
console.log(` ${c.cyan('daemon stop')} Stop background daemon`);
|
|
42
43
|
console.log(` ${c.cyan('daemon restart')} Restart background daemon`);
|
|
43
44
|
console.log(` ${c.cyan('status')} Show daemon status`);
|
|
45
|
+
console.log(` ${c.cyan('--version')} ${c.dim('(-v)')} Show version`);
|
|
44
46
|
console.log('');
|
|
45
47
|
console.log(c.bold('Options:'));
|
|
46
48
|
console.log(` ${c.dim('--repo <path>')} Repository path (default: .)`);
|
|
@@ -69,4 +71,5 @@ export function showHelp(): void {
|
|
|
69
71
|
console.log(c.dim(' brainbank context "auth flow" | pbcopy # → clipboard'));
|
|
70
72
|
console.log(c.dim(' brainbank daemon start # background HTTP'));
|
|
71
73
|
console.log(c.dim(' brainbank mcp # MCP stdio'));
|
|
74
|
+
console.log(c.dim(' brainbank mcp:export antigravity # export MCP config'));
|
|
72
75
|
}
|
|
@@ -12,6 +12,7 @@ import * as path from 'node:path';
|
|
|
12
12
|
import { c, args, getFlag, hasFlag, stripFlags } from '@/cli/utils.ts';
|
|
13
13
|
import { createBrain, getConfig, registerConfigCollections } from '@/cli/factory/index.ts';
|
|
14
14
|
import { findDocsPlugin } from '@/cli/utils.ts';
|
|
15
|
+
import { autoExportMcp } from './mcp-export.ts';
|
|
15
16
|
import { scanRepo } from './scan.ts';
|
|
16
17
|
|
|
17
18
|
export async function cmdIndex(): Promise<void> {
|
|
@@ -126,6 +127,9 @@ export async function cmdIndex(): Promise<void> {
|
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
brain.close();
|
|
130
|
+
|
|
131
|
+
// Auto-export MCP config to Antigravity if detected and not already configured
|
|
132
|
+
await autoExportMcp(repoPath);
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
|
|
@@ -220,7 +224,7 @@ function capitalizeFirst(s: string): string {
|
|
|
220
224
|
|
|
221
225
|
/** Generate .brainbank/config.json from the selected modules. */
|
|
222
226
|
async function saveConfig(repoPath: string, modules: string[]): Promise<void> {
|
|
223
|
-
const { select } = await import('@inquirer/prompts');
|
|
227
|
+
const { select, confirm } = await import('@inquirer/prompts');
|
|
224
228
|
|
|
225
229
|
// Embedding provider selection
|
|
226
230
|
const envEmbedding = process.env.BRAINBANK_EMBEDDING;
|
|
@@ -252,15 +256,15 @@ async function saveConfig(repoPath: string, modules: string[]): Promise<void> {
|
|
|
252
256
|
message: 'Noise pruner:',
|
|
253
257
|
choices: [
|
|
254
258
|
{
|
|
255
|
-
name: '
|
|
256
|
-
value: '
|
|
259
|
+
name: 'haiku — AI-powered noise filter (recommended)',
|
|
260
|
+
value: 'haiku',
|
|
257
261
|
},
|
|
258
262
|
{
|
|
259
|
-
name: '
|
|
260
|
-
value: '
|
|
263
|
+
name: 'none — no pruning',
|
|
264
|
+
value: 'none',
|
|
261
265
|
},
|
|
262
266
|
],
|
|
263
|
-
default: '
|
|
267
|
+
default: 'haiku',
|
|
264
268
|
});
|
|
265
269
|
|
|
266
270
|
const configDir = path.join(repoPath, '.brainbank');
|
|
@@ -275,6 +279,33 @@ async function saveConfig(repoPath: string, modules: string[]): Promise<void> {
|
|
|
275
279
|
config.pruner = pruner;
|
|
276
280
|
}
|
|
277
281
|
|
|
282
|
+
// Key management: detect available keys and offer to save
|
|
283
|
+
const detectedKeys: Record<string, string> = {};
|
|
284
|
+
const needsPerplexity = embedding.startsWith('perplexity');
|
|
285
|
+
const needsAnthropic = pruner === 'haiku';
|
|
286
|
+
const needsOpenai = embedding === 'openai';
|
|
287
|
+
|
|
288
|
+
if (needsPerplexity && process.env.PERPLEXITY_API_KEY) {
|
|
289
|
+
detectedKeys.perplexity = process.env.PERPLEXITY_API_KEY;
|
|
290
|
+
}
|
|
291
|
+
if (needsAnthropic && process.env.ANTHROPIC_API_KEY) {
|
|
292
|
+
detectedKeys.anthropic = process.env.ANTHROPIC_API_KEY;
|
|
293
|
+
}
|
|
294
|
+
if (needsOpenai && process.env.OPENAI_API_KEY) {
|
|
295
|
+
detectedKeys.openai = process.env.OPENAI_API_KEY;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (Object.keys(detectedKeys).length > 0) {
|
|
299
|
+
const keyNames = Object.keys(detectedKeys).join(', ');
|
|
300
|
+
const saveKeys = await confirm({
|
|
301
|
+
message: `Save API keys (${keyNames}) to config.json? (portable, no env vars needed)`,
|
|
302
|
+
default: true,
|
|
303
|
+
});
|
|
304
|
+
if (saveKeys) {
|
|
305
|
+
config.keys = detectedKeys;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
278
309
|
fs.mkdirSync(configDir, { recursive: true });
|
|
279
310
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
280
311
|
console.log(c.green(` ✓ Saved ${path.relative(process.cwd(), configPath)}`));
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* brainbank mcp:export [target] — Export MCP server config for AI IDEs.
|
|
3
|
+
*
|
|
4
|
+
* Generates the MCP server config block for brainbank and merges it into
|
|
5
|
+
* the target IDE's config file. Currently supports: antigravity.
|
|
6
|
+
*
|
|
7
|
+
* Detects: node path, cli.js path, API keys from config or env vars.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ProjectConfig } from '@/cli/factory/config-loader.ts';
|
|
11
|
+
|
|
12
|
+
import * as fs from 'node:fs';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { c, args, getFlag } from '@/cli/utils.ts';
|
|
16
|
+
import { getConfig } from '@/cli/factory/index.ts';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
21
|
+
/** Supported export targets and their config file paths. */
|
|
22
|
+
const TARGETS: Record<string, { configPath: string; label: string }> = {
|
|
23
|
+
antigravity: {
|
|
24
|
+
configPath: path.join(process.env.HOME ?? '~', '.gemini', 'antigravity', 'mcp_config.json'),
|
|
25
|
+
label: 'Gemini Antigravity',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
interface McpServerConfig {
|
|
30
|
+
command: string;
|
|
31
|
+
args: string[];
|
|
32
|
+
env?: Record<string, string>;
|
|
33
|
+
cwd?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface McpConfig {
|
|
37
|
+
mcpServers: Record<string, McpServerConfig>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build the brainbank MCP server config block.
|
|
42
|
+
* Resolves node binary, dist/cli.js path, and API keys.
|
|
43
|
+
*/
|
|
44
|
+
function buildBrainbankMcpBlock(config: ProjectConfig | null): McpServerConfig {
|
|
45
|
+
const nodeBin = process.execPath;
|
|
46
|
+
|
|
47
|
+
// Resolve dist/cli.js from the global install location (node_prefix/lib/node_modules/brainbank/dist/cli.js)
|
|
48
|
+
const globalCliJs = path.join(path.dirname(nodeBin), '..', 'lib', 'node_modules', 'brainbank', 'dist', 'cli.js');
|
|
49
|
+
// Fallback: relative to this file (dev / npm link)
|
|
50
|
+
const localCliJs = path.resolve(__dirname, '..', '..', 'dist', 'cli.js');
|
|
51
|
+
const resolvedCliJs = fs.existsSync(globalCliJs) ? globalCliJs : localCliJs;
|
|
52
|
+
|
|
53
|
+
const env: Record<string, string> = {};
|
|
54
|
+
|
|
55
|
+
// Resolve API keys: config.keys > env vars
|
|
56
|
+
const keys = config?.keys;
|
|
57
|
+
const perplexityKey = keys?.perplexity ?? process.env.PERPLEXITY_API_KEY;
|
|
58
|
+
const anthropicKey = keys?.anthropic ?? process.env.ANTHROPIC_API_KEY;
|
|
59
|
+
const openaiKey = keys?.openai ?? process.env.OPENAI_API_KEY;
|
|
60
|
+
|
|
61
|
+
if (perplexityKey) env.PERPLEXITY_API_KEY = perplexityKey;
|
|
62
|
+
if (anthropicKey) env.ANTHROPIC_API_KEY = anthropicKey;
|
|
63
|
+
if (openaiKey) env.OPENAI_API_KEY = openaiKey;
|
|
64
|
+
|
|
65
|
+
const block: McpServerConfig = {
|
|
66
|
+
command: nodeBin,
|
|
67
|
+
args: [resolvedCliJs, 'mcp'],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (Object.keys(env).length > 0) {
|
|
71
|
+
block.env = env;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return block;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Load existing MCP config, merge brainbank entry, and write back.
|
|
79
|
+
* Preserves all other server entries.
|
|
80
|
+
*/
|
|
81
|
+
function mergeAndWrite(targetPath: string, block: McpServerConfig): { created: boolean } {
|
|
82
|
+
let existing: McpConfig = { mcpServers: {} };
|
|
83
|
+
const created = !fs.existsSync(targetPath);
|
|
84
|
+
|
|
85
|
+
if (!created) {
|
|
86
|
+
try {
|
|
87
|
+
const raw = fs.readFileSync(targetPath, 'utf-8');
|
|
88
|
+
existing = JSON.parse(raw) as McpConfig;
|
|
89
|
+
if (!existing.mcpServers) existing.mcpServers = {};
|
|
90
|
+
} catch {
|
|
91
|
+
// Corrupt or empty file — start fresh
|
|
92
|
+
existing = { mcpServers: {} };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
existing.mcpServers.brainbank = block;
|
|
97
|
+
|
|
98
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
99
|
+
fs.writeFileSync(targetPath, JSON.stringify(existing, null, 2) + '\n');
|
|
100
|
+
|
|
101
|
+
return { created };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Check if an MCP config already has a brainbank entry. */
|
|
105
|
+
export function hasBrainbankMcpEntry(targetPath: string): boolean {
|
|
106
|
+
if (!fs.existsSync(targetPath)) return false;
|
|
107
|
+
try {
|
|
108
|
+
const raw = fs.readFileSync(targetPath, 'utf-8');
|
|
109
|
+
const config = JSON.parse(raw) as McpConfig;
|
|
110
|
+
return !!config.mcpServers?.brainbank;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Auto-export: called after index when Antigravity is detected. */
|
|
117
|
+
export async function autoExportMcp(repoPath: string): Promise<void> {
|
|
118
|
+
const target = TARGETS.antigravity;
|
|
119
|
+
if (!target) return;
|
|
120
|
+
|
|
121
|
+
// Only auto-export if Antigravity dir exists
|
|
122
|
+
const antigravityDir = path.dirname(target.configPath);
|
|
123
|
+
if (!fs.existsSync(antigravityDir)) return;
|
|
124
|
+
|
|
125
|
+
// Only auto-export if brainbank isn't already configured
|
|
126
|
+
if (hasBrainbankMcpEntry(target.configPath)) return;
|
|
127
|
+
|
|
128
|
+
const config = await getConfig(repoPath);
|
|
129
|
+
const block = buildBrainbankMcpBlock(config);
|
|
130
|
+
mergeAndWrite(target.configPath, block);
|
|
131
|
+
console.log(` ${c.green('✓')} Exported MCP config to ${c.dim(path.relative(process.env.HOME ?? '', target.configPath))}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** CLI command: brainbank mcp:export [target] */
|
|
135
|
+
export async function cmdMcpExport(): Promise<void> {
|
|
136
|
+
const targetName = args[1] || getFlag('target') || 'antigravity';
|
|
137
|
+
const repoPath = getFlag('repo') || '.';
|
|
138
|
+
|
|
139
|
+
const target = TARGETS[targetName];
|
|
140
|
+
if (!target) {
|
|
141
|
+
console.error(c.red(`Unknown export target: ${targetName}`));
|
|
142
|
+
console.error(c.dim(` Available: ${Object.keys(TARGETS).join(', ')}`));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const config = await getConfig(repoPath);
|
|
147
|
+
const block = buildBrainbankMcpBlock(config);
|
|
148
|
+
const { created } = mergeAndWrite(target.configPath, block);
|
|
149
|
+
|
|
150
|
+
console.log(c.bold(`\n━━━ MCP Export: ${target.label} ━━━\n`));
|
|
151
|
+
console.log(` ${c.green('✓')} ${created ? 'Created' : 'Updated'} ${c.dim(target.configPath)}`);
|
|
152
|
+
console.log(` ${c.dim('Node:')} ${block.command}`);
|
|
153
|
+
console.log(` ${c.dim('CLI:')} ${block.args[0]}`);
|
|
154
|
+
|
|
155
|
+
const envKeys = block.env ? Object.keys(block.env) : [];
|
|
156
|
+
if (envKeys.length > 0) {
|
|
157
|
+
console.log(` ${c.dim('Keys:')} ${envKeys.join(', ')}`);
|
|
158
|
+
} else {
|
|
159
|
+
console.log(` ${c.yellow('⚠')} No API keys found. Set env vars or add keys to .brainbank/config.json`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`\n ${c.dim('Restart your IDE to apply changes.')}\n`);
|
|
163
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -18,14 +18,20 @@ import { cmdStats } from './commands/stats.ts';
|
|
|
18
18
|
import { cmdReembed } from './commands/reembed.ts';
|
|
19
19
|
import { cmdWatch } from './commands/watch.ts';
|
|
20
20
|
import { cmdMcp } from './commands/mcp.ts';
|
|
21
|
+
import { cmdMcpExport } from './commands/mcp-export.ts';
|
|
21
22
|
import { cmdDaemon } from './commands/daemon.ts';
|
|
22
23
|
import { cmdStatus } from './commands/status.ts';
|
|
23
24
|
import { showHelp } from './commands/help.ts';
|
|
25
|
+
import { VERSION } from '@/constants.ts';
|
|
24
26
|
|
|
25
27
|
const command = args[0];
|
|
26
28
|
|
|
27
29
|
async function main(): Promise<void> {
|
|
28
30
|
switch (command) {
|
|
31
|
+
case '--version':
|
|
32
|
+
case '-v':
|
|
33
|
+
console.log(`brainbank v${VERSION}`);
|
|
34
|
+
break;
|
|
29
35
|
case 'i':
|
|
30
36
|
case 'index': return cmdIndex();
|
|
31
37
|
case 'collection': return cmdCollection();
|
|
@@ -41,6 +47,7 @@ async function main(): Promise<void> {
|
|
|
41
47
|
case 'reembed': return cmdReembed();
|
|
42
48
|
case 'watch': return cmdWatch();
|
|
43
49
|
case 'mcp': return cmdMcp();
|
|
50
|
+
case 'mcp:export': return cmdMcpExport();
|
|
44
51
|
case 'serve': return cmdMcp(); // backward compat
|
|
45
52
|
case 'daemon': return cmdDaemon();
|
|
46
53
|
case 'status': return cmdStatus();
|
package/src/constants.ts
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* to their respective packages. Only keys owned by the core live here.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
9
|
+
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
const pkg = require('../package.json') as { version: string };
|
|
12
|
+
|
|
13
|
+
/** Package version from package.json. */
|
|
14
|
+
export const VERSION: string = pkg.version;
|
|
15
|
+
|
|
8
16
|
/** HNSW index key for KV collections (core-owned). */
|
|
9
17
|
export const HNSW = {
|
|
10
18
|
KV: 'kv',
|