@theihtisham/agent-shadow-brain 6.3.0 → 7.0.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +411 -391
  2. package/LICENSE +21 -21
  3. package/README.md +428 -438
  4. package/dist/adapters/aider.js +13 -13
  5. package/dist/adapters/base-adapter.js +14 -14
  6. package/dist/adapters/codex.js +8 -8
  7. package/dist/adapters/roo-code.js +18 -18
  8. package/dist/brain/adr-engine.js +11 -11
  9. package/dist/brain/analyzer.js +30 -30
  10. package/dist/brain/auto-update.js +1 -1
  11. package/dist/brain/global-brain.d.ts +52 -63
  12. package/dist/brain/global-brain.d.ts.map +1 -1
  13. package/dist/brain/global-brain.js +577 -339
  14. package/dist/brain/global-brain.js.map +1 -1
  15. package/dist/brain/health-score.js +19 -19
  16. package/dist/brain/learning-engine.js +59 -59
  17. package/dist/brain/mcp-server.d.ts +1 -0
  18. package/dist/brain/mcp-server.d.ts.map +1 -1
  19. package/dist/brain/mcp-server.js +16 -6
  20. package/dist/brain/mcp-server.js.map +1 -1
  21. package/dist/brain/plugin-system.js +23 -23
  22. package/dist/brain/pr-generator.js +19 -19
  23. package/dist/brain/project-context.js +1 -1
  24. package/dist/brain/project-context.js.map +1 -1
  25. package/dist/brain/report-generator.js +270 -270
  26. package/dist/cli.js +105 -34
  27. package/dist/cli.js.map +1 -1
  28. package/dist/dashboard/v6-dashboard-html.d.ts +1 -1
  29. package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -1
  30. package/dist/dashboard/v6-dashboard-html.js +5 -5
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/types.d.ts +28 -10
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/watchers/git-watcher.d.ts.map +1 -1
  38. package/dist/watchers/git-watcher.js +1 -1
  39. package/dist/watchers/git-watcher.js.map +1 -1
  40. package/docs/launch/RELEASE_CHECKLIST_v7.0.0.md +24 -0
  41. package/docs/launch/RELEASE_NOTES_v7.0.0.md +44 -0
  42. package/package.json +17 -14
  43. package/docs/launch/README.md +0 -61
package/dist/cli.js CHANGED
@@ -8,22 +8,28 @@ import * as path from 'path';
8
8
  import * as os from 'os';
9
9
  import { Orchestrator } from './brain/orchestrator.js';
10
10
  import { detectRunningAgents, createAdapter } from './adapters/index.js';
11
- const VERSION = '6.3.0';
12
- const config = new Conf({
13
- projectName: 'shadow-brain',
14
- defaults: {
15
- provider: 'ollama',
16
- model: undefined,
17
- agents: ['claude-code', 'kilo-code', 'cline', 'opencode', 'codex'],
18
- projectDir: process.cwd(),
19
- watchMode: true,
20
- autoInject: true,
21
- reviewDepth: 'standard',
22
- brainPersonality: 'balanced',
23
- },
24
- });
11
+ const VERSION = '7.0.0';
12
+ // Defer config reads until a command actually needs provider settings.
13
+ let savedConfig;
14
+ function getConfig() {
15
+ savedConfig ?? (savedConfig = new Conf({
16
+ ...(process.env.SHADOW_BRAIN_CONFIG_DIR ? { cwd: process.env.SHADOW_BRAIN_CONFIG_DIR } : {}),
17
+ projectName: 'shadow-brain',
18
+ defaults: {
19
+ provider: 'ollama',
20
+ model: undefined,
21
+ agents: ['claude-code', 'kilo-code', 'cline', 'opencode', 'codex'],
22
+ projectDir: process.cwd(),
23
+ watchMode: true,
24
+ autoInject: true,
25
+ reviewDepth: 'standard',
26
+ brainPersonality: 'balanced',
27
+ },
28
+ }));
29
+ return savedConfig;
30
+ }
25
31
  function mergeConfig(cliOpts) {
26
- const saved = config.store;
32
+ const saved = getConfig().store;
27
33
  const watchMode = cliOpts.watchMode === false || cliOpts.watch === false
28
34
  ? false
29
35
  : saved.watchMode !== false;
@@ -393,7 +399,7 @@ const injectCmd = new Command('inject')
393
399
  const statusCmd = new Command('status')
394
400
  .description('Show current Shadow Brain configuration and status')
395
401
  .action(async () => {
396
- const saved = config.store;
402
+ const saved = getConfig().store;
397
403
  console.log(chalk.magenta.bold(`\n SHADOW BRAIN v${VERSION} Status\n`));
398
404
  console.log(chalk.dim(' Provider: ') + chalk.cyan(saved.provider || 'ollama'));
399
405
  console.log(chalk.dim(' Model: ') + chalk.cyan(saved.model || '(default)'));
@@ -431,7 +437,7 @@ const configCmd = new Command('config')
431
437
  .action(async (key, value, opts) => {
432
438
  if (opts?.list) {
433
439
  console.log(chalk.magenta.bold('\n SHADOW BRAIN Configuration\n'));
434
- const saved = config.store;
440
+ const saved = getConfig().store;
435
441
  for (const [k, v] of Object.entries(saved)) {
436
442
  const display = k === 'apiKey' && v ? '••••••••' : String(v);
437
443
  console.log(chalk.dim(` ${k}: `) + chalk.cyan(display));
@@ -440,7 +446,7 @@ const configCmd = new Command('config')
440
446
  return;
441
447
  }
442
448
  if (opts?.reset) {
443
- config.clear();
449
+ getConfig().clear();
444
450
  console.log(chalk.green(' Configuration reset to defaults.'));
445
451
  return;
446
452
  }
@@ -454,7 +460,7 @@ const configCmd = new Command('config')
454
460
  parsedValue = Number(value);
455
461
  else if (value.includes(','))
456
462
  parsedValue = value.split(',');
457
- config.set(key, parsedValue);
463
+ getConfig().set(key, parsedValue);
458
464
  console.log(chalk.green(` Set ${key} = ${parsedValue}`));
459
465
  return;
460
466
  }
@@ -479,12 +485,12 @@ const setupCmd = new Command('setup')
479
485
  const provider = await ask(chalk.yellow(' Choose provider [1-4] (default: 1): '));
480
486
  const providerMap = { '1': 'ollama', '2': 'anthropic', '3': 'openai', '4': 'openrouter' };
481
487
  const chosenProvider = providerMap[provider.trim() || '1'] || 'ollama';
482
- config.set('provider', chosenProvider);
488
+ getConfig().set('provider', chosenProvider);
483
489
  console.log(chalk.green(` ✓ Provider: ${chosenProvider}\n`));
484
490
  if (chosenProvider !== 'ollama') {
485
491
  const apiKey = await ask(chalk.yellow(` Enter ${chosenProvider} API key: `));
486
492
  if (apiKey.trim()) {
487
- config.set('apiKey', apiKey.trim());
493
+ getConfig().set('apiKey', apiKey.trim());
488
494
  console.log(chalk.green(' ✓ API key saved\n'));
489
495
  }
490
496
  else {
@@ -504,7 +510,7 @@ const setupCmd = new Command('setup')
504
510
  const personality = await ask(chalk.yellow(' Choose personality [1-6] (default: 1): '));
505
511
  const personalityMap = { '1': 'balanced', '2': 'mentor', '3': 'critic', '4': 'architect', '5': 'security', '6': 'performance' };
506
512
  const chosenPersonality = personalityMap[personality.trim() || '1'] || 'balanced';
507
- config.set('brainPersonality', chosenPersonality);
513
+ getConfig().set('brainPersonality', chosenPersonality);
508
514
  console.log(chalk.green(` ✓ Personality: ${chosenPersonality}\n`));
509
515
  console.log(chalk.cyan(' Review Depth:'));
510
516
  console.log(chalk.dim(' 1. standard (balanced — default)'));
@@ -512,14 +518,14 @@ const setupCmd = new Command('setup')
512
518
  console.log(chalk.dim(' 3. deep (thorough, full diffs)'));
513
519
  const depth = await ask(chalk.yellow(' Choose depth [1-3] (default: 1): '));
514
520
  const depthMap = { '1': 'standard', '2': 'quick', '3': 'deep' };
515
- config.set('reviewDepth', depthMap[depth.trim() || '1'] || 'standard');
521
+ getConfig().set('reviewDepth', depthMap[depth.trim() || '1'] || 'standard');
516
522
  console.log(chalk.green(' ✓ Review depth saved\n'));
517
523
  const inject = await ask(chalk.yellow(' Enable auto-injection? [Y/n] (default: Y): '));
518
- config.set('autoInject', inject.trim().toLowerCase() !== 'n');
524
+ getConfig().set('autoInject', inject.trim().toLowerCase() !== 'n');
519
525
  console.log(chalk.green(` ✓ Auto-injection: ${inject.trim().toLowerCase() !== 'n' ? 'enabled' : 'disabled'}\n`));
520
526
  rl.close();
521
527
  console.log(chalk.magenta.bold(' Setup complete!\n'));
522
- console.log(chalk.dim(' Configuration saved to: ') + chalk.cyan(config.path));
528
+ console.log(chalk.dim(' Configuration saved to: ') + chalk.cyan(getConfig().path));
523
529
  console.log();
524
530
  console.log(chalk.dim(' Next steps:'));
525
531
  console.log(chalk.cyan(' shadow-brain start .') + chalk.dim(' # Start watching a project'));
@@ -539,13 +545,13 @@ const doctorCmd = new Command('doctor')
539
545
  console.log(chalk.magenta.bold('\n SHADOW BRAIN Doctor\n'));
540
546
  const checks = [];
541
547
  try {
542
- const saved = config.store;
548
+ const saved = getConfig().store;
543
549
  checks.push({ name: 'Configuration', status: 'ok', detail: `provider=${saved.provider}, personality=${saved.brainPersonality}` });
544
550
  }
545
551
  catch {
546
552
  checks.push({ name: 'Configuration', status: 'fail', detail: 'Could not read config. Run `shadow-brain setup`.' });
547
553
  }
548
- const provider = config.get('provider') || 'ollama';
554
+ const provider = getConfig().get('provider') || 'ollama';
549
555
  if (provider === 'ollama') {
550
556
  try {
551
557
  const resp = await fetch('http://localhost:11434/api/tags', { signal: AbortSignal.timeout(3000) });
@@ -563,18 +569,19 @@ const doctorCmd = new Command('doctor')
563
569
  }
564
570
  }
565
571
  else {
566
- const apiKey = config.get('apiKey');
572
+ const apiKey = getConfig().get('apiKey');
567
573
  if (apiKey) {
568
- checks.push({ name: `${provider} API key`, status: 'ok', detail: 'Key is set (' + apiKey.slice(0, 4) + '...)' });
574
+ checks.push({ name: `${provider} API key`, status: 'ok', detail: 'Key is configured' });
569
575
  }
570
576
  else {
571
577
  checks.push({ name: `${provider} API key`, status: 'fail', detail: 'No API key configured. Run: shadow-brain config apiKey <key>' });
572
578
  }
573
579
  }
574
580
  const nodeVersion = process.version;
575
- const major = parseInt(nodeVersion.slice(1).split('.')[0]);
576
- checks.push({ name: 'Node.js', status: major >= 18 ? 'ok' : 'warn', detail: `${nodeVersion} ${major >= 18 ? '(supported)' : '(recommend v18+)'}` });
577
- const projectDir = config.get('projectDir') || process.cwd();
581
+ const [major, minor] = nodeVersion.slice(1).split('.').map(Number);
582
+ const nodeSupported = (major === 22 && minor >= 12) || major === 24 || major >= 26;
583
+ checks.push({ name: 'Node.js', status: nodeSupported ? 'ok' : 'warn', detail: `${nodeVersion} ${nodeSupported ? '(supported)' : '(requires Node 22.12+, 24.x, or 26+)'}` });
584
+ const projectDir = getConfig().get('projectDir') || process.cwd();
578
585
  try {
579
586
  const stat = fs.statSync(projectDir);
580
587
  checks.push({ name: 'Project directory', status: stat.isDirectory() ? 'ok' : 'fail', detail: projectDir });
@@ -2457,7 +2464,7 @@ const v5Cmd = new Command('v5')
2457
2464
  const program = new Command();
2458
2465
  program
2459
2466
  .name('shadow-brain')
2460
- .description('Shadow Brain v6.0.3 - local cross-agent memory, live brain health, resume context, and safety controls')
2467
+ .description('Shadow Brain - durable cross-agent memory, explained recall, and storage health')
2461
2468
  .version(VERSION);
2462
2469
  program.addCommand(startCmd);
2463
2470
  program.addCommand(reviewCmd);
@@ -5001,5 +5008,69 @@ program.addCommand(asciiCmd);
5001
5008
  program.addCommand(memesCmd);
5002
5009
  program.addCommand(brain3dCmd);
5003
5010
  program.addCommand(doppelgangerCmd);
5004
- program.parse();
5011
+ const storageHealthCmd = new Command('storage-health')
5012
+ .description('Check memory-store readability and report storage errors without loading provider settings')
5013
+ .option('--db <path>', 'Memory-store path (defaults to the global brain)')
5014
+ .option('--json', 'Print machine-readable JSON')
5015
+ .action(async (opts) => {
5016
+ const { getGlobalBrain } = await import('./brain/global-brain.js');
5017
+ const brain = getGlobalBrain(opts.db ? { dbPath: path.resolve(opts.db) } : undefined);
5018
+ try {
5019
+ await brain.init();
5020
+ }
5021
+ catch {
5022
+ process.exitCode = 1;
5023
+ }
5024
+ const health = brain.getStorageHealth();
5025
+ if (opts.json)
5026
+ console.log(JSON.stringify(health, null, 2));
5027
+ else {
5028
+ console.log(`Storage: ${health.status}\nDatabase: ${health.dbPath}\nPending operations: ${health.pendingOperations}`);
5029
+ if (health.lastError)
5030
+ console.log(`${health.lastError.code}: ${health.lastError.message}`);
5031
+ }
5032
+ if (health.initialized)
5033
+ await brain.shutdown();
5034
+ });
5035
+ const recallExplainCmd = new Command('recall-explain')
5036
+ .description('Recall memories with keyword matches, score components, and supporting agent identities')
5037
+ .argument('<query>', 'Keywords to search for')
5038
+ .option('--db <path>', 'Memory-store path (defaults to the global brain)')
5039
+ .option('--project <path>', 'Restrict recall to this project')
5040
+ .option('--limit <number>', 'Maximum results', '10')
5041
+ .option('--min-confidence <number>', 'Minimum heuristic score from 0 to 1', '0')
5042
+ .action(async (query, opts) => {
5043
+ const limit = Number(opts.limit);
5044
+ const minConfidence = Number(opts.minConfidence);
5045
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1000 || !Number.isFinite(minConfidence) || minConfidence < 0 || minConfidence > 1) {
5046
+ console.error('Use --limit between 1 and 1000 and --min-confidence between 0 and 1.');
5047
+ process.exitCode = 1;
5048
+ return;
5049
+ }
5050
+ const { getGlobalBrain, GlobalBrain } = await import('./brain/global-brain.js');
5051
+ const brain = getGlobalBrain(opts.db ? { dbPath: path.resolve(opts.db) } : undefined);
5052
+ try {
5053
+ await brain.init();
5054
+ console.log(JSON.stringify(brain.recallScored({
5055
+ keywords: query.split(/\s+/),
5056
+ projectId: opts.project ? GlobalBrain.projectIdFor(opts.project) : undefined,
5057
+ limit,
5058
+ minConfidence,
5059
+ }), null, 2));
5060
+ }
5061
+ catch (error) {
5062
+ console.error(error instanceof Error ? error.message : 'Recall failed.');
5063
+ process.exitCode = 1;
5064
+ }
5065
+ finally {
5066
+ if (brain.getStorageHealth().initialized)
5067
+ await brain.shutdown();
5068
+ }
5069
+ });
5070
+ program.addCommand(storageHealthCmd);
5071
+ program.addCommand(recallExplainCmd);
5072
+ program.parseAsync().catch((error) => {
5073
+ console.error(error instanceof Error ? error.message : 'Command failed.');
5074
+ process.exitCode = 1;
5075
+ });
5005
5076
  //# sourceMappingURL=cli.js.map