crbro-memory 1.14.0 → 1.16.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/README.md +19 -13
- package/bin/crbro.mjs +621 -578
- package/dist/engine/cortex.d.ts +8 -0
- package/dist/engine/cortex.d.ts.map +1 -1
- package/dist/engine/cortex.js +33 -2
- package/dist/engine/cortex.js.map +1 -1
- package/dist/search/index.d.ts +16 -1
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +78 -7
- package/dist/search/index.js.map +1 -1
- package/dist/search/semantic.d.ts +21 -3
- package/dist/search/semantic.d.ts.map +1 -1
- package/dist/search/semantic.js +68 -27
- package/dist/search/semantic.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +20 -4
- package/dist/server.js.map +1 -1
- package/dist/sync/materialize.d.ts.map +1 -1
- package/dist/sync/materialize.js +4 -0
- package/dist/sync/materialize.js.map +1 -1
- package/dist/sync/ops.d.ts +2 -0
- package/dist/sync/ops.d.ts.map +1 -1
- package/dist/sync/ops.js.map +1 -1
- package/dist/sync/space.d.ts.map +1 -1
- package/dist/sync/space.js +4 -2
- package/dist/sync/space.js.map +1 -1
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +56 -54
package/bin/crbro.mjs
CHANGED
|
@@ -1,578 +1,621 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// ─── CRBRO CLI ───────────────────────────────────────────────────
|
|
4
|
-
// Command-line interface for CRBRO memory system
|
|
5
|
-
// Supports: init, status, mine, setup-miner, miner-status,
|
|
6
|
-
// remove-miner, and MCP server mode (default)
|
|
7
|
-
|
|
8
|
-
import { platform, homedir } from 'os';
|
|
9
|
-
import { join, dirname } from 'path';
|
|
10
|
-
import { existsSync, readFileSync } from 'fs';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
// The release that is running, read from the package itself. The manifest
|
|
14
|
-
// version stamps the brain format and has not moved since 1.0.0, so showing
|
|
15
|
-
// only that one told everyone they were on 1.0.0 forever.
|
|
16
|
-
function pkgVersion() {
|
|
17
|
-
try {
|
|
18
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
return JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')).version;
|
|
20
|
-
} catch {
|
|
21
|
-
return 'unknown';
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const args = process.argv.slice(2);
|
|
26
|
-
const command = args[0];
|
|
27
|
-
|
|
28
|
-
// ─── IDE Detection ──────────────────────────────────────────────
|
|
29
|
-
const IDE_CONFIGS = [
|
|
30
|
-
{
|
|
31
|
-
name: 'Antigravity (Google Gemini)',
|
|
32
|
-
id: 'antigravity',
|
|
33
|
-
configPath: () => join(homedir(), '.gemini', 'antigravity', 'mcp_config.json'),
|
|
34
|
-
configFormat: 'mcpServers',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
name: 'Cursor',
|
|
38
|
-
id: 'cursor',
|
|
39
|
-
configPath: () => join(homedir(), '.cursor', 'mcp.json'),
|
|
40
|
-
configFormat: 'mcpServers',
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: 'Windsurf',
|
|
44
|
-
id: 'windsurf',
|
|
45
|
-
configPath: () => join(homedir(), '.windsurf', 'mcp.json'),
|
|
46
|
-
configFormat: 'mcpServers',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: 'Claude Desktop',
|
|
50
|
-
id: 'claude-desktop',
|
|
51
|
-
configPath: () => {
|
|
52
|
-
if (platform() === 'win32') {
|
|
53
|
-
return join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
|
|
54
|
-
}
|
|
55
|
-
return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
56
|
-
},
|
|
57
|
-
configFormat: 'mcpServers',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'Claude Code (user scope)',
|
|
61
|
-
id: 'claude-code',
|
|
62
|
-
configPath: () => join(homedir(), '.claude.json'),
|
|
63
|
-
configFormat: 'mcpServers',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'VS Code + Continue',
|
|
67
|
-
id: 'continue',
|
|
68
|
-
configPath: () => join(homedir(), '.continue', 'config.json'),
|
|
69
|
-
configFormat: 'mcpServers',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'ChatGPT Desktop',
|
|
73
|
-
id: 'chatgpt',
|
|
74
|
-
configPath: () => {
|
|
75
|
-
if (platform() === 'win32') {
|
|
76
|
-
return join(process.env.APPDATA || '', 'ChatGPT', 'mcp_config.json');
|
|
77
|
-
}
|
|
78
|
-
return join(homedir(), 'Library', 'Application Support', 'ChatGPT', 'mcp_config.json');
|
|
79
|
-
},
|
|
80
|
-
configFormat: 'mcpServers',
|
|
81
|
-
},
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
function detectIDEs() {
|
|
85
|
-
const detected = [];
|
|
86
|
-
for (const ide of IDE_CONFIGS) {
|
|
87
|
-
try {
|
|
88
|
-
const configPath = ide.configPath();
|
|
89
|
-
if (existsSync(configPath)) {
|
|
90
|
-
detected.push({ ...ide, configPath: configPath, exists: true });
|
|
91
|
-
} else {
|
|
92
|
-
// Check if the parent directory exists (IDE installed but no config yet)
|
|
93
|
-
const parentDir = configPath.split(/[/\\]/).slice(0, -1).join(platform() === 'win32' ? '\\' : '/');
|
|
94
|
-
if (existsSync(parentDir)) {
|
|
95
|
-
detected.push({ ...ide, configPath: configPath, exists: false });
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
} catch { /* skip */ }
|
|
99
|
-
}
|
|
100
|
-
return detected;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function generateMCPSnippet(envVars) {
|
|
104
|
-
return JSON.stringify({
|
|
105
|
-
"crbro": {
|
|
106
|
-
"command": "npx",
|
|
107
|
-
"args": ["-y", "crbro-memory"],
|
|
108
|
-
...(envVars ? { "env": envVars } : {})
|
|
109
|
-
}
|
|
110
|
-
}, null, 2);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ─── Commands ───────────────────────────────────────────────────
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
console.log(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
console.log('');
|
|
183
|
-
console.log('
|
|
184
|
-
|
|
185
|
-
console.log('');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
console.log(
|
|
213
|
-
console.log(
|
|
214
|
-
console.log(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
console.log('');
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
console.log(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
console.log(
|
|
307
|
-
console.log('
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
console.log('');
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
console.log('');
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
console.log('');
|
|
403
|
-
console.log('
|
|
404
|
-
|
|
405
|
-
console.log(
|
|
406
|
-
console.log('
|
|
407
|
-
console.log('');
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
console.log(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const
|
|
527
|
-
fs.
|
|
528
|
-
fs.
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ─── CRBRO CLI ───────────────────────────────────────────────────
|
|
4
|
+
// Command-line interface for CRBRO memory system
|
|
5
|
+
// Supports: init, status, mine, setup-miner, miner-status,
|
|
6
|
+
// remove-miner, and MCP server mode (default)
|
|
7
|
+
|
|
8
|
+
import { platform, homedir } from 'os';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { existsSync, readFileSync } from 'fs';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
// The release that is running, read from the package itself. The manifest
|
|
14
|
+
// version stamps the brain format and has not moved since 1.0.0, so showing
|
|
15
|
+
// only that one told everyone they were on 1.0.0 forever.
|
|
16
|
+
function pkgVersion() {
|
|
17
|
+
try {
|
|
18
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
return JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf8')).version;
|
|
20
|
+
} catch {
|
|
21
|
+
return 'unknown';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const command = args[0];
|
|
27
|
+
|
|
28
|
+
// ─── IDE Detection ──────────────────────────────────────────────
|
|
29
|
+
const IDE_CONFIGS = [
|
|
30
|
+
{
|
|
31
|
+
name: 'Antigravity (Google Gemini)',
|
|
32
|
+
id: 'antigravity',
|
|
33
|
+
configPath: () => join(homedir(), '.gemini', 'antigravity', 'mcp_config.json'),
|
|
34
|
+
configFormat: 'mcpServers',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Cursor',
|
|
38
|
+
id: 'cursor',
|
|
39
|
+
configPath: () => join(homedir(), '.cursor', 'mcp.json'),
|
|
40
|
+
configFormat: 'mcpServers',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Windsurf',
|
|
44
|
+
id: 'windsurf',
|
|
45
|
+
configPath: () => join(homedir(), '.windsurf', 'mcp.json'),
|
|
46
|
+
configFormat: 'mcpServers',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Claude Desktop',
|
|
50
|
+
id: 'claude-desktop',
|
|
51
|
+
configPath: () => {
|
|
52
|
+
if (platform() === 'win32') {
|
|
53
|
+
return join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
|
|
54
|
+
}
|
|
55
|
+
return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
56
|
+
},
|
|
57
|
+
configFormat: 'mcpServers',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'Claude Code (user scope)',
|
|
61
|
+
id: 'claude-code',
|
|
62
|
+
configPath: () => join(homedir(), '.claude.json'),
|
|
63
|
+
configFormat: 'mcpServers',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'VS Code + Continue',
|
|
67
|
+
id: 'continue',
|
|
68
|
+
configPath: () => join(homedir(), '.continue', 'config.json'),
|
|
69
|
+
configFormat: 'mcpServers',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'ChatGPT Desktop',
|
|
73
|
+
id: 'chatgpt',
|
|
74
|
+
configPath: () => {
|
|
75
|
+
if (platform() === 'win32') {
|
|
76
|
+
return join(process.env.APPDATA || '', 'ChatGPT', 'mcp_config.json');
|
|
77
|
+
}
|
|
78
|
+
return join(homedir(), 'Library', 'Application Support', 'ChatGPT', 'mcp_config.json');
|
|
79
|
+
},
|
|
80
|
+
configFormat: 'mcpServers',
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
function detectIDEs() {
|
|
85
|
+
const detected = [];
|
|
86
|
+
for (const ide of IDE_CONFIGS) {
|
|
87
|
+
try {
|
|
88
|
+
const configPath = ide.configPath();
|
|
89
|
+
if (existsSync(configPath)) {
|
|
90
|
+
detected.push({ ...ide, configPath: configPath, exists: true });
|
|
91
|
+
} else {
|
|
92
|
+
// Check if the parent directory exists (IDE installed but no config yet)
|
|
93
|
+
const parentDir = configPath.split(/[/\\]/).slice(0, -1).join(platform() === 'win32' ? '\\' : '/');
|
|
94
|
+
if (existsSync(parentDir)) {
|
|
95
|
+
detected.push({ ...ide, configPath: configPath, exists: false });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch { /* skip */ }
|
|
99
|
+
}
|
|
100
|
+
return detected;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function generateMCPSnippet(envVars) {
|
|
104
|
+
return JSON.stringify({
|
|
105
|
+
"crbro": {
|
|
106
|
+
"command": "npx",
|
|
107
|
+
"args": ["-y", "crbro-memory"],
|
|
108
|
+
...(envVars ? { "env": envVars } : {})
|
|
109
|
+
}
|
|
110
|
+
}, null, 2);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ─── Commands ───────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
// ─── Semantic recall: install, download, embed ──────────────────────
|
|
116
|
+
// Installed by `init` since 1.16 (skip with --no-semantic, turn off with
|
|
117
|
+
// CRBRO_SEMANTIC=0). The runtime (~380 MB) and the model (~118 MB) live in
|
|
118
|
+
// ~/.crbro/.semantic once per machine, outside the package.
|
|
119
|
+
async function semanticInstall(sem) {
|
|
120
|
+
const { spawnSync } = await import('child_process');
|
|
121
|
+
const fs = await import('fs');
|
|
122
|
+
const os = await import('os');
|
|
123
|
+
const home = sem.semanticHome();
|
|
124
|
+
fs.mkdirSync(home, { recursive: true });
|
|
125
|
+
const pkg = join(home, 'package.json');
|
|
126
|
+
if (!fs.existsSync(pkg)) {
|
|
127
|
+
fs.writeFileSync(pkg, JSON.stringify({ name: 'crbro-semantic', private: true }, null, 2));
|
|
128
|
+
}
|
|
129
|
+
if (!sem.resolveRuntime()) {
|
|
130
|
+
console.log(` ⬇️ Installing transformers.js into ${home} (~380 MB with onnxruntime)...`);
|
|
131
|
+
const r = spawnSync('npm', ['install', '--no-audit', '--no-fund', '--loglevel=error', '@huggingface/transformers@3'],
|
|
132
|
+
{ cwd: home, stdio: 'inherit', shell: true });
|
|
133
|
+
if (r.status !== 0) return false;
|
|
134
|
+
}
|
|
135
|
+
const st = sem.semanticStatus();
|
|
136
|
+
if (!st.model_downloaded) {
|
|
137
|
+
console.log(` ⬇️ Downloading the model (${st.model}, ~118 MB)...`);
|
|
138
|
+
const idx = new sem.SemanticIndex(fs.mkdtempSync(join(os.tmpdir(), 'crbro-warm-')));
|
|
139
|
+
await idx.upsert([{ id: 'warm', text: 'hello' }]); // the first use downloads and caches it
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function semanticBuild() {
|
|
145
|
+
process.env.CRBRO_SEMANTIC = '1';
|
|
146
|
+
const [{ Brain }, { SearchEngine }] = await Promise.all([
|
|
147
|
+
import('../dist/engine/brain.js'),
|
|
148
|
+
import('../dist/search/index.js'),
|
|
149
|
+
]);
|
|
150
|
+
const brain = new Brain();
|
|
151
|
+
const engine = new SearchEngine(brain);
|
|
152
|
+
const started = Date.now();
|
|
153
|
+
await engine.init(); // loads the stored vectors: unchanged lines are not embedded twice
|
|
154
|
+
const chunks = await engine.rebuild();
|
|
155
|
+
await engine.awaitEmbeddings();
|
|
156
|
+
await engine.persist();
|
|
157
|
+
return { chunks, vectors: engine.semanticCount(), seconds: ((Date.now() - started) / 1000).toFixed(1) };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (command === 'init') {
|
|
161
|
+
// ─── Initialize brain + IDE detection ──────────────────────────
|
|
162
|
+
import('../dist/engine/brain.js').then(async ({ Brain }) => {
|
|
163
|
+
const brain = new Brain();
|
|
164
|
+
const manifest = await brain.initialize();
|
|
165
|
+
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log(' 🧠 CRBRO brain initialized!');
|
|
168
|
+
console.log(` Path: ${manifest.brain_path}`);
|
|
169
|
+
console.log('');
|
|
170
|
+
|
|
171
|
+
// Detect IDEs
|
|
172
|
+
const ides = detectIDEs();
|
|
173
|
+
|
|
174
|
+
if (ides.length > 0) {
|
|
175
|
+
console.log(' 📡 Detected IDEs:');
|
|
176
|
+
console.log('');
|
|
177
|
+
for (const ide of ides) {
|
|
178
|
+
const status = ide.exists ? '✅ config exists' : '📝 needs config';
|
|
179
|
+
console.log(` ${ide.name}: ${status}`);
|
|
180
|
+
console.log(` → ${ide.configPath}`);
|
|
181
|
+
}
|
|
182
|
+
console.log('');
|
|
183
|
+
console.log(' Add this to your MCP config (inside "mcpServers"):');
|
|
184
|
+
} else {
|
|
185
|
+
console.log(' ⚠️ No IDE detected. Add this to your MCP config manually:');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log('');
|
|
189
|
+
console.log(' ' + generateMCPSnippet().split('\n').join('\n '));
|
|
190
|
+
// Semantic recall, installed by default since 1.16: once per machine,
|
|
191
|
+
// about 500 MB on disk, ~0.5 GB of RAM while a server runs.
|
|
192
|
+
const skipSemantic = args.includes('--no-semantic')
|
|
193
|
+
|| ['0', 'off', 'false'].includes(String(process.env.CRBRO_SEMANTIC || '').toLowerCase());
|
|
194
|
+
console.log('');
|
|
195
|
+
if (skipSemantic) {
|
|
196
|
+
console.log(' npx crbro-memory semantic status Semantic recall (installed by init; --no-semantic skips it): status | install | build');
|
|
197
|
+
} else {
|
|
198
|
+
console.log(' 🧭 Semantic recall: installing (once per machine, ~500 MB)...');
|
|
199
|
+
try {
|
|
200
|
+
const sem = await import('../dist/search/semantic.js');
|
|
201
|
+
const ok = await semanticInstall(sem);
|
|
202
|
+
if (ok) {
|
|
203
|
+
const r = await semanticBuild();
|
|
204
|
+
console.log(` ✅ Semantic recall ready · ${r.vectors} lines embedded in ${r.seconds}s · CRBRO_SEMANTIC=0 turns it off`);
|
|
205
|
+
} else {
|
|
206
|
+
console.log(' ⚠️ Could not install it; recall stays keyword-only. Retry: npx crbro-memory semantic install');
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.log(` ⚠️ Semantic recall not installed (${err instanceof Error ? err.message : err}); recall stays keyword-only.`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
console.log('');
|
|
213
|
+
console.log(' Next steps:');
|
|
214
|
+
console.log(' 1. Add the config above to your IDE\'s MCP settings');
|
|
215
|
+
console.log(' 2. (Optional) npx crbro-memory setup-miner');
|
|
216
|
+
console.log(' 3. Restart your IDE — CRBRO boots automatically!');
|
|
217
|
+
console.log('');
|
|
218
|
+
}).catch(console.error);
|
|
219
|
+
|
|
220
|
+
} else if (command === 'status') {
|
|
221
|
+
// ─── Show brain status ─────────────────────────────────────────
|
|
222
|
+
import('../dist/engine/brain.js').then(async ({ Brain }) => {
|
|
223
|
+
const brain = new Brain();
|
|
224
|
+
try {
|
|
225
|
+
const manifest = await brain.getManifest();
|
|
226
|
+
console.log('');
|
|
227
|
+
console.log(' 🧠 CRBRO Brain Status');
|
|
228
|
+
console.log(' ─────────────────────');
|
|
229
|
+
console.log(` CRBRO: ${pkgVersion()}`);
|
|
230
|
+
console.log(` Brain format: ${manifest.version}`);
|
|
231
|
+
console.log(` Path: ${manifest.brain_path}`);
|
|
232
|
+
console.log(` Neurons: ${manifest.total_neurons}`);
|
|
233
|
+
console.log(` Synapses: ${manifest.total_synapses}`);
|
|
234
|
+
console.log(` Sessions: ${manifest.total_sessions}`);
|
|
235
|
+
console.log(` Last Boot: ${manifest.last_boot || 'never'}`);
|
|
236
|
+
console.log(` Last Consolidate: ${manifest.last_consolidation || 'never'}`);
|
|
237
|
+
console.log('');
|
|
238
|
+
|
|
239
|
+
// Show detected IDEs
|
|
240
|
+
const ides = detectIDEs();
|
|
241
|
+
if (ides.length > 0) {
|
|
242
|
+
console.log(' 📡 Connected IDEs:');
|
|
243
|
+
for (const ide of ides) {
|
|
244
|
+
console.log(` ${ide.exists ? '✅' : '⚠️ '} ${ide.name}`);
|
|
245
|
+
}
|
|
246
|
+
console.log('');
|
|
247
|
+
}
|
|
248
|
+
} catch {
|
|
249
|
+
console.log('');
|
|
250
|
+
console.log(' 🧠 CRBRO brain not initialized.');
|
|
251
|
+
console.log(' Run: npx crbro-memory init');
|
|
252
|
+
console.log('');
|
|
253
|
+
}
|
|
254
|
+
}).catch(console.error);
|
|
255
|
+
|
|
256
|
+
} else if (command === 'activate') {
|
|
257
|
+
// ─── Legacy command (pre-1.4.0) — CRBRO is now fully free ──────
|
|
258
|
+
console.log('');
|
|
259
|
+
console.log(' ✅ Good news: since v1.4.0 CRBRO is fully free.');
|
|
260
|
+
console.log(' All 15 tools are available — no license key needed.');
|
|
261
|
+
console.log('');
|
|
262
|
+
|
|
263
|
+
} else if (command === 'mine') {
|
|
264
|
+
// ─── One-shot mining ───────────────────────────────────────────
|
|
265
|
+
const targetDir = args[1];
|
|
266
|
+
|
|
267
|
+
import('../dist/miner/index.js').then(async ({ Miner }) => {
|
|
268
|
+
console.log('');
|
|
269
|
+
console.log(' ⛏️ CRBRO Miner — Scanning for knowledge...');
|
|
270
|
+
console.log('');
|
|
271
|
+
|
|
272
|
+
const miner = new Miner();
|
|
273
|
+
const result = await miner.mine(targetDir);
|
|
274
|
+
|
|
275
|
+
console.log(' ────────────────────────────────');
|
|
276
|
+
console.log(` Files scanned: ${result.scanned}`);
|
|
277
|
+
console.log(` New files mined: ${result.new_files}`);
|
|
278
|
+
console.log(` Neurons created: ${result.neurons_created}`);
|
|
279
|
+
console.log(` Neurons updated: ${result.neurons_updated}`);
|
|
280
|
+
console.log(` Facts added: ${result.facts_added}`);
|
|
281
|
+
console.log(` Decisions found: ${result.decisions_added}`);
|
|
282
|
+
|
|
283
|
+
if (result.technologies_found.length > 0) {
|
|
284
|
+
console.log(` Technologies: ${result.technologies_found.slice(0, 10).join(', ')}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (result.errors.length > 0) {
|
|
288
|
+
console.log('');
|
|
289
|
+
console.log(' ⚠️ Errors:');
|
|
290
|
+
for (const err of result.errors.slice(0, 5)) {
|
|
291
|
+
console.log(` ${err}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
console.log('');
|
|
296
|
+
}).catch(console.error);
|
|
297
|
+
|
|
298
|
+
} else if (command === 'setup-miner') {
|
|
299
|
+
// ─── Setup automatic mining ────────────────────────────────────
|
|
300
|
+
import('../dist/miner/scheduler.js').then(async ({ setupScheduler }) => {
|
|
301
|
+
console.log('');
|
|
302
|
+
console.log(' ⏰ Setting up CRBRO Auto-Miner...');
|
|
303
|
+
console.log('');
|
|
304
|
+
|
|
305
|
+
const result = await setupScheduler();
|
|
306
|
+
console.log(result.message);
|
|
307
|
+
console.log('');
|
|
308
|
+
}).catch(console.error);
|
|
309
|
+
|
|
310
|
+
} else if (command === 'miner-status') {
|
|
311
|
+
// ─── Check miner status ────────────────────────────────────────
|
|
312
|
+
Promise.all([
|
|
313
|
+
import('../dist/miner/scheduler.js'),
|
|
314
|
+
import('../dist/miner/index.js'),
|
|
315
|
+
]).then(async ([{ getSchedulerStatus }, { Miner }]) => {
|
|
316
|
+
console.log('');
|
|
317
|
+
console.log(' ⛏️ CRBRO Miner Status');
|
|
318
|
+
console.log(' ─────────────────────');
|
|
319
|
+
|
|
320
|
+
// Scheduler status
|
|
321
|
+
const schedStatus = await getSchedulerStatus();
|
|
322
|
+
console.log(` Scheduler: ${schedStatus.installed ? '✅ Installed' : '❌ Not installed'}`);
|
|
323
|
+
console.log(` Platform: ${schedStatus.platform}`);
|
|
324
|
+
if (schedStatus.details) {
|
|
325
|
+
console.log(` Details: ${schedStatus.details}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Miner state
|
|
329
|
+
const miner = new Miner();
|
|
330
|
+
const status = await miner.getStatus();
|
|
331
|
+
console.log('');
|
|
332
|
+
console.log(` Last run: ${status.state.last_run || 'never'}`);
|
|
333
|
+
console.log(` Total mined: ${status.state.total_mined} files`);
|
|
334
|
+
console.log(` Tracked: ${Object.keys(status.state.mined_files).length} files`);
|
|
335
|
+
console.log('');
|
|
336
|
+
|
|
337
|
+
if (status.detected_dirs.length > 0) {
|
|
338
|
+
console.log(' 📂 Scan directories:');
|
|
339
|
+
for (const dir of status.detected_dirs) {
|
|
340
|
+
console.log(` ${dir}`);
|
|
341
|
+
}
|
|
342
|
+
} else {
|
|
343
|
+
console.log(' ⚠️ No IDE directories detected.');
|
|
344
|
+
}
|
|
345
|
+
console.log('');
|
|
346
|
+
}).catch(console.error);
|
|
347
|
+
|
|
348
|
+
} else if (command === 'remove-miner') {
|
|
349
|
+
// ─── Remove automatic mining ───────────────────────────────────
|
|
350
|
+
import('../dist/miner/scheduler.js').then(async ({ removeScheduler }) => {
|
|
351
|
+
const result = await removeScheduler();
|
|
352
|
+
console.log('');
|
|
353
|
+
console.log(result.success ? ` ✅ ${result.message}` : ` ❌ ${result.message}`);
|
|
354
|
+
console.log('');
|
|
355
|
+
}).catch(console.error);
|
|
356
|
+
|
|
357
|
+
} else if (command === 'reindex') {
|
|
358
|
+
// ─── Rebuild the search index from the cortex ──────────────────
|
|
359
|
+
Promise.all([
|
|
360
|
+
import('../dist/engine/brain.js'),
|
|
361
|
+
import('../dist/search/index.js'),
|
|
362
|
+
]).then(async ([{ Brain }, { SearchEngine }]) => {
|
|
363
|
+
const brain = new Brain();
|
|
364
|
+
const engine = new SearchEngine(brain);
|
|
365
|
+
|
|
366
|
+
console.log('');
|
|
367
|
+
console.log(' 🔁 Rebuilding the CRBRO search index...');
|
|
368
|
+
const started = Date.now();
|
|
369
|
+
const indexed = await engine.rebuild();
|
|
370
|
+
const seconds = ((Date.now() - started) / 1000).toFixed(1);
|
|
371
|
+
|
|
372
|
+
console.log('');
|
|
373
|
+
console.log(` ✅ ${indexed} chunks indexed in ${seconds}s`);
|
|
374
|
+
console.log(' Every fact, decision and pattern is now searchable on its own,');
|
|
375
|
+
console.log(' so a big neuron is no longer buried by short ones.');
|
|
376
|
+
console.log('');
|
|
377
|
+
}).catch(console.error);
|
|
378
|
+
|
|
379
|
+
} else if (command === 'semantic') {
|
|
380
|
+
// ─── Semantic recall: status | install | build ─────────────────
|
|
381
|
+
const sub = args[1];
|
|
382
|
+
import('../dist/search/semantic.js').then(async (sem) => {
|
|
383
|
+
if (sub === 'install') {
|
|
384
|
+
console.log('');
|
|
385
|
+
const ok = await semanticInstall(sem);
|
|
386
|
+
if (!ok) {
|
|
387
|
+
console.log(' ❌ npm install failed. Nothing else changed.');
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
const r = await semanticBuild();
|
|
391
|
+
console.log(` ✅ Semantic recall ready · ${r.chunks} chunks indexed · ${r.vectors} vectors · ${r.seconds}s`);
|
|
392
|
+
console.log(' It is on whenever this runtime is present; CRBRO_SEMANTIC=0 turns it off.');
|
|
393
|
+
console.log('');
|
|
394
|
+
} else if (sub === 'build') {
|
|
395
|
+
const st = sem.semanticStatus();
|
|
396
|
+
if (!st.installed) {
|
|
397
|
+
console.log('');
|
|
398
|
+
console.log(' ❌ Runtime not installed. Run: npx crbro-memory semantic install');
|
|
399
|
+
console.log('');
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
console.log('');
|
|
403
|
+
console.log(' 🧭 Embedding the brain...');
|
|
404
|
+
const r = await semanticBuild();
|
|
405
|
+
console.log(` ✅ ${r.chunks} chunks indexed · ${r.vectors} vectors stored · ${r.seconds}s`);
|
|
406
|
+
console.log(' From now on each new line is embedded when it is saved.');
|
|
407
|
+
console.log('');
|
|
408
|
+
} else {
|
|
409
|
+
const st = sem.semanticStatus();
|
|
410
|
+
const enabled = st.enabled
|
|
411
|
+
? (st.mode === 'forced' ? '✅ on (CRBRO_SEMANTIC=1)' : '✅ on (installed; CRBRO_SEMANTIC=0 turns it off)')
|
|
412
|
+
: (st.mode === 'disabled' ? '⚪ off (CRBRO_SEMANTIC=0)' : '⚪ off (not installed)');
|
|
413
|
+
console.log('');
|
|
414
|
+
console.log(' 🧭 CRBRO semantic recall');
|
|
415
|
+
console.log(' ────────────────────────');
|
|
416
|
+
console.log(` Runtime: ${st.installed ? '✅ installed' : '❌ not installed → npx crbro-memory init (or: semantic install)'}`);
|
|
417
|
+
console.log(` Model: ${st.model}${st.model_downloaded ? '' : ' (downloads on first use)'}`);
|
|
418
|
+
console.log(` Enabled: ${enabled}`);
|
|
419
|
+
console.log(` Home: ${st.home}`);
|
|
420
|
+
console.log('');
|
|
421
|
+
}
|
|
422
|
+
}).catch(console.error);
|
|
423
|
+
|
|
424
|
+
} else if (command === 'eval') {
|
|
425
|
+
// ─── Measure retrieval quality against a query set ─────────────
|
|
426
|
+
//
|
|
427
|
+
// Without a number you cannot tell a fix from a feeling. The file is
|
|
428
|
+
// .crbro/.eval/queries.json — a list of { query, expect_neuron } and
|
|
429
|
+
// optionally expect_contains, the substring the matched fact should carry.
|
|
430
|
+
Promise.all([
|
|
431
|
+
import('../dist/engine/brain.js'),
|
|
432
|
+
import('../dist/search/index.js'),
|
|
433
|
+
import('fs/promises'),
|
|
434
|
+
]).then(async ([{ Brain }, { SearchEngine }, fsp]) => {
|
|
435
|
+
const brain = new Brain();
|
|
436
|
+
const evalPath = join(brain.paths.root, '.eval', 'queries.json');
|
|
437
|
+
|
|
438
|
+
let queries;
|
|
439
|
+
try {
|
|
440
|
+
queries = JSON.parse(await fsp.readFile(evalPath, 'utf-8'));
|
|
441
|
+
} catch {
|
|
442
|
+
console.log('');
|
|
443
|
+
console.log(` No query set found at ${evalPath}`);
|
|
444
|
+
console.log(' Create it as a JSON array, for example:');
|
|
445
|
+
console.log('');
|
|
446
|
+
console.log(' [');
|
|
447
|
+
console.log(' { "query": "how we deploy the api", "expect_neuron": "project_octochat",');
|
|
448
|
+
console.log(' "expect_contains": "Cloud Run" }');
|
|
449
|
+
console.log(' ]');
|
|
450
|
+
console.log('');
|
|
451
|
+
console.log(' Build it from facts you already saved: take six or eight words');
|
|
452
|
+
console.log(' out of a real fact and name the neuron that holds it.');
|
|
453
|
+
console.log('');
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const engine = new SearchEngine(brain);
|
|
458
|
+
await engine.init();
|
|
459
|
+
|
|
460
|
+
let atOne = 0, atThree = 0, reciprocal = 0, contentOk = 0;
|
|
461
|
+
const misses = [];
|
|
462
|
+
|
|
463
|
+
for (const q of queries) {
|
|
464
|
+
const results = await engine.search(q.query, { limit: 10 });
|
|
465
|
+
const rank = results.findIndex(r => r.neuron_id === q.expect_neuron);
|
|
466
|
+
|
|
467
|
+
if (rank === 0) atOne++;
|
|
468
|
+
if (rank >= 0 && rank < 3) atThree++;
|
|
469
|
+
if (rank >= 0) reciprocal += 1 / (rank + 1);
|
|
470
|
+
|
|
471
|
+
if (rank === 0 && q.expect_contains) {
|
|
472
|
+
if (results[0].matching_content.includes(q.expect_contains)) contentOk++;
|
|
473
|
+
else misses.push(` ~ "${q.query}" — right neuron, wrong fact returned`);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (rank !== 0) {
|
|
477
|
+
const got = results[0] ? results[0].neuron_id : '(nothing)';
|
|
478
|
+
misses.push(` ✗ "${q.query}" — expected ${q.expect_neuron}, got ${got}` +
|
|
479
|
+
(rank > 0 ? ` (it was #${rank + 1})` : ''));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const n = queries.length;
|
|
484
|
+
const pct = (x) => `${((x / n) * 100).toFixed(1)}%`;
|
|
485
|
+
|
|
486
|
+
console.log('');
|
|
487
|
+
console.log(' 📊 CRBRO retrieval eval');
|
|
488
|
+
console.log(' ───────────────────────');
|
|
489
|
+
console.log(` Queries: ${n}`);
|
|
490
|
+
console.log(` Right first hit: ${atOne}/${n} (${pct(atOne)})`);
|
|
491
|
+
console.log(` In the top 3: ${atThree}/${n} (${pct(atThree)})`);
|
|
492
|
+
console.log(` MRR: ${(reciprocal / n).toFixed(3)}`);
|
|
493
|
+
if (queries.some(q => q.expect_contains)) {
|
|
494
|
+
const withContent = queries.filter(q => q.expect_contains).length;
|
|
495
|
+
console.log(` Right fact shown: ${contentOk}/${withContent}`);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (misses.length > 0) {
|
|
499
|
+
console.log('');
|
|
500
|
+
console.log(' Misses:');
|
|
501
|
+
for (const m of misses.slice(0, 25)) console.log(m);
|
|
502
|
+
if (misses.length > 25) console.log(` ... and ${misses.length - 25} more`);
|
|
503
|
+
}
|
|
504
|
+
console.log('');
|
|
505
|
+
}).catch(console.error);
|
|
506
|
+
|
|
507
|
+
} else if (command === 'install-hooks') {
|
|
508
|
+
// ─── Wire the SubagentStart hook into Claude Code ──────────────
|
|
509
|
+
//
|
|
510
|
+
// SessionStart context never reaches Task-spawned subagents, so without
|
|
511
|
+
// this every subagent runs without the behavioral protocols the session
|
|
512
|
+
// was booted with. This registers hooks/crbro-subagent.mjs, which reads
|
|
513
|
+
// the same protocol neurons crbro_boot reads — one source of truth.
|
|
514
|
+
//
|
|
515
|
+
// Merges into ~/.claude/settings.json without touching anything else.
|
|
516
|
+
// Idempotent: running it twice changes nothing the second time.
|
|
517
|
+
import('fs').then(async fs => {
|
|
518
|
+
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
519
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
520
|
+
const source = join(here, '..', 'hooks', 'crbro-subagent.mjs');
|
|
521
|
+
|
|
522
|
+
// Copy the hook to a stable location. When CRBRO runs from the npx
|
|
523
|
+
// cache, `here` changes with every release and the stale path would
|
|
524
|
+
// break the hook silently on the next update.
|
|
525
|
+
const hookDir = join(homedir(), '.claude', 'crbro-hooks');
|
|
526
|
+
const hookScript = join(hookDir, 'crbro-subagent.mjs');
|
|
527
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
528
|
+
fs.copyFileSync(source, hookScript);
|
|
529
|
+
// Injection is OPT-IN since 1.12 (pre-registered consequence of three
|
|
530
|
+
// clean-control benchmark runs: no measured benefit in any model, harm
|
|
531
|
+
// and fabricated compliance in small ones — see the benchmarks in the
|
|
532
|
+
// card repo). `install-hooks` alone installs the machinery inert;
|
|
533
|
+
// `install-hooks --inject` enables injection for every subagent.
|
|
534
|
+
const conInyeccion = process.argv.includes('--inject');
|
|
535
|
+
const hookCmd = (conInyeccion ? 'CRBRO_SUBAGENT_INJECT=full ' : '') + `node "${hookScript.split('\\').join('/')}"`;
|
|
536
|
+
|
|
537
|
+
let settings = {};
|
|
538
|
+
try {
|
|
539
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8').replace(/^/, ''));
|
|
540
|
+
} catch (e) {
|
|
541
|
+
if (fs.existsSync(settingsPath)) {
|
|
542
|
+
console.error(` ❌ ${settingsPath} exists but could not be parsed — not touching it.`);
|
|
543
|
+
console.error(` ${e.message}`);
|
|
544
|
+
process.exit(1);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
settings.hooks = settings.hooks || {};
|
|
549
|
+
const list = settings.hooks.SubagentStart = settings.hooks.SubagentStart || [];
|
|
550
|
+
const yaEsta = JSON.stringify(list).includes('crbro-subagent');
|
|
551
|
+
if (yaEsta) {
|
|
552
|
+
console.log(' ✅ SubagentStart hook already installed. Script refreshed.');
|
|
553
|
+
if (conInyeccion && !JSON.stringify(list).includes('CRBRO_SUBAGENT_INJECT')) {
|
|
554
|
+
console.log(' ⚠️ The installed entry does NOT enable injection. To enable it,');
|
|
555
|
+
console.log(' remove the SubagentStart entry from settings.json and re-run');
|
|
556
|
+
console.log(' install-hooks --inject.');
|
|
557
|
+
}
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
list.push({
|
|
561
|
+
hooks: [{
|
|
562
|
+
type: 'command',
|
|
563
|
+
command: hookCmd,
|
|
564
|
+
timeout: 5,
|
|
565
|
+
statusMessage: 'Inyectando protocolos CRBRO en el subagente...',
|
|
566
|
+
}],
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
const tmp = settingsPath + '.' + process.pid + '.tmp';
|
|
570
|
+
fs.writeFileSync(tmp, JSON.stringify(settings, null, 2), 'utf8');
|
|
571
|
+
fs.renameSync(tmp, settingsPath);
|
|
572
|
+
console.log(' ✅ SubagentStart hook installed.');
|
|
573
|
+
console.log(` ${settingsPath}`);
|
|
574
|
+
if (conInyeccion) {
|
|
575
|
+
console.log(' Injection ENABLED: every Task-spawned subagent receives the');
|
|
576
|
+
console.log(' protocol block. Scope it with CRBRO_SUBAGENT_MATCHER (regex on');
|
|
577
|
+
console.log(' agent_type) if needed. Measured caveat: on small models the');
|
|
578
|
+
console.log(' block bought no benchmarked benefit and induced fabricated');
|
|
579
|
+
console.log(' compliance in some runs — see the benchmarks before scoping.');
|
|
580
|
+
} else {
|
|
581
|
+
console.log(' Injection is OPT-IN and currently OFF (the measured default:');
|
|
582
|
+
console.log(' three clean benchmark runs found no benefit in any model and');
|
|
583
|
+
console.log(' harm in small ones). Re-run with --inject to enable it.');
|
|
584
|
+
}
|
|
585
|
+
}).catch(console.error);
|
|
586
|
+
|
|
587
|
+
} else if (command === '--help' || command === '-h') {
|
|
588
|
+
// ─── Help ──────────────────────────────────────────────────────
|
|
589
|
+
console.log('');
|
|
590
|
+
console.log(' 🧠 CRBRO — Persistent Neural Memory for AI');
|
|
591
|
+
console.log(' ═══════════════════════════════════════════');
|
|
592
|
+
console.log('');
|
|
593
|
+
console.log(' Setup:');
|
|
594
|
+
console.log(' npx crbro-memory init Initialize brain + detect IDEs');
|
|
595
|
+
console.log(' npx crbro-memory status Show brain status');
|
|
596
|
+
console.log('');
|
|
597
|
+
console.log(' Auto-Mining:');
|
|
598
|
+
console.log(' npx crbro-memory mine [dir] One-shot mining of artifacts');
|
|
599
|
+
console.log(' npx crbro-memory setup-miner Install scheduled auto-miner');
|
|
600
|
+
console.log(' npx crbro-memory miner-status Check auto-miner status');
|
|
601
|
+
console.log(' npx crbro-memory remove-miner Remove auto-miner');
|
|
602
|
+
console.log('');
|
|
603
|
+
console.log(' Search:');
|
|
604
|
+
console.log(' npx crbro-memory reindex Rebuild the search index');
|
|
605
|
+
console.log(' npx crbro-memory eval Measure retrieval against .crbro/.eval/queries.json');
|
|
606
|
+
console.log('');
|
|
607
|
+
console.log(' Semantic layer (opt-in, ~500 MB on disk, measured in benchmarks/):');
|
|
608
|
+
console.log(' npx crbro-memory semantic install Install transformers.js into ~/.crbro/.semantic');
|
|
609
|
+
console.log(' npx crbro-memory semantic build Embed the whole brain once (needs CRBRO_SEMANTIC=1)');
|
|
610
|
+
console.log(' npx crbro-memory semantic status Runtime, model and whether it is enabled');
|
|
611
|
+
console.log('');
|
|
612
|
+
console.log(' Server:');
|
|
613
|
+
console.log(' npx crbro-memory Start MCP server (stdio)');
|
|
614
|
+
console.log('');
|
|
615
|
+
console.log(' Open source (MIT) — https://github.com/Octonove/crbro-memory');
|
|
616
|
+
console.log('');
|
|
617
|
+
|
|
618
|
+
} else {
|
|
619
|
+
// ─── Default: start MCP server ─────────────────────────────────
|
|
620
|
+
import('../dist/index.js').catch(console.error);
|
|
621
|
+
}
|