create-merlin-brain 2.6.0 → 2.7.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 (2) hide show
  1. package/bin/install.cjs +116 -72
  2. package/package.json +1 -1
package/bin/install.cjs CHANGED
@@ -395,7 +395,7 @@ function cleanupLegacy() {
395
395
  }
396
396
  }
397
397
 
398
- // Clean config.json - remove briefed server, fix merlin server
398
+ // Clean config.json - remove briefed server, fix merlin server, migrate to global binary
399
399
  const configPath = path.join(CLAUDE_DIR, 'config.json');
400
400
  if (fs.existsSync(configPath)) {
401
401
  try {
@@ -403,26 +403,26 @@ function cleanupLegacy() {
403
403
  let modified = false;
404
404
 
405
405
  if (config.mcpServers?.briefed) {
406
- // Save API key before deleting
407
406
  const apiKey = config.mcpServers.briefed.env?.CCWIKI_API_KEY ||
408
407
  config.mcpServers.briefed.env?.MERLIN_API_KEY;
409
408
  delete config.mcpServers.briefed;
410
- // Ensure merlin server exists with correct config
411
409
  if (apiKey) {
412
410
  config.mcpServers.merlin = {
413
- command: 'npx',
414
- args: ['create-merlin-brain@latest', 'serve'],
411
+ command: 'merlin-brain',
415
412
  env: { MERLIN_API_KEY: apiKey }
416
413
  };
417
414
  }
418
415
  modified = true;
419
416
  }
420
417
 
421
- // Fix merlin server if it has wrong command
418
+ // Migrate merlin server to global binary
422
419
  if (config.mcpServers?.merlin) {
420
+ const cmd = config.mcpServers.merlin.command;
423
421
  const args = config.mcpServers.merlin.args;
424
- if (args && (args[0] === 'create-merlin-brain' || args[0] === 'ccwiki-mcp')) {
425
- config.mcpServers.merlin.args = ['create-merlin-brain@latest', 'serve'];
422
+ // Migrate from npx to global binary
423
+ if (cmd === 'npx' || (args && (args[0] === 'create-merlin-brain' || args[0] === 'ccwiki-mcp'))) {
424
+ config.mcpServers.merlin.command = 'merlin-brain';
425
+ delete config.mcpServers.merlin.args;
426
426
  modified = true;
427
427
  }
428
428
  if (config.mcpServers.merlin.env?.CCWIKI_API_KEY) {
@@ -435,7 +435,7 @@ function cleanupLegacy() {
435
435
 
436
436
  if (modified) {
437
437
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
438
- cleaned.push('~/.claude/config.json (fixed MCP config)');
438
+ cleaned.push('~/.claude/config.json (migrated to global binary)');
439
439
  }
440
440
  } catch (e) { /* ignore */ }
441
441
  }
@@ -456,8 +456,7 @@ function cleanupLegacy() {
456
456
  delete config.mcpServers.briefed;
457
457
  if (apiKey) {
458
458
  config.mcpServers.merlin = {
459
- command: 'npx',
460
- args: ['create-merlin-brain@latest', 'serve'],
459
+ command: 'merlin-brain',
461
460
  env: { MERLIN_API_KEY: apiKey }
462
461
  };
463
462
  }
@@ -465,9 +464,11 @@ function cleanupLegacy() {
465
464
  }
466
465
 
467
466
  if (config.mcpServers?.merlin) {
467
+ const cmd = config.mcpServers.merlin.command;
468
468
  const args = config.mcpServers.merlin.args;
469
- if (args && (args[0] === 'create-merlin-brain' || args[0] === 'ccwiki-mcp')) {
470
- config.mcpServers.merlin.args = ['create-merlin-brain@latest', 'serve'];
469
+ if (cmd === 'npx' || (args && (args[0] === 'create-merlin-brain' || args[0] === 'ccwiki-mcp'))) {
470
+ config.mcpServers.merlin.command = 'merlin-brain';
471
+ delete config.mcpServers.merlin.args;
471
472
  modified = true;
472
473
  }
473
474
  if (config.mcpServers.merlin.env?.CCWIKI_API_KEY) {
@@ -480,7 +481,7 @@ function cleanupLegacy() {
480
481
 
481
482
  if (modified) {
482
483
  fs.writeFileSync(desktopConfigPath, JSON.stringify(config, null, 2));
483
- cleaned.push('~/.claude/claude_desktop_config.json (fixed MCP config)');
484
+ cleaned.push('~/.claude/claude_desktop_config.json (migrated to global binary)');
484
485
  }
485
486
  } catch (e) { /* ignore */ }
486
487
  }
@@ -573,7 +574,7 @@ function cleanupLegacy() {
573
574
  }
574
575
 
575
576
  // ═══════════════════════════════════════════════════════════════
576
- // FIX ~/.claude.json MCP REGISTRY (update old merlin-mcp to create-merlin-brain)
577
+ // FIX ~/.claude.json MCP REGISTRY (migrate to global binary)
577
578
  // ═══════════════════════════════════════════════════════════════
578
579
  const claudeJsonPath = path.join(os.homedir(), '.claude.json');
579
580
  if (fs.existsSync(claudeJsonPath)) {
@@ -581,14 +582,15 @@ function cleanupLegacy() {
581
582
  let claudeJson = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf8'));
582
583
  let modified = false;
583
584
 
584
- // Check if merlin MCP server uses old package name
585
+ // Migrate merlin MCP server to global binary
585
586
  if (claudeJson.mcpServers?.merlin) {
587
+ const cmd = claudeJson.mcpServers.merlin.command;
586
588
  const args = claudeJson.mcpServers.merlin.args;
587
- if (args && (args[0] === 'merlin-mcp' || args[0] === 'merlin-mcp@latest' || args[0] === 'ccwiki-mcp')) {
588
- // Update to new package name
589
- claudeJson.mcpServers.merlin.args = ['create-merlin-brain@latest', 'serve'];
589
+ if (cmd === 'npx' || (args && (args[0] === 'merlin-mcp' || args[0] === 'merlin-mcp@latest' || args[0] === 'ccwiki-mcp' || args[0] === 'create-merlin-brain@latest'))) {
590
+ claudeJson.mcpServers.merlin.command = 'merlin-brain';
591
+ delete claudeJson.mcpServers.merlin.args;
590
592
  modified = true;
591
- cleaned.push('~/.claude.json (updated merlin-mcp create-merlin-brain)');
593
+ cleaned.push('~/.claude.json (migrated to global binary)');
592
594
  }
593
595
  }
594
596
 
@@ -605,6 +607,25 @@ function cleanupLegacy() {
605
607
  } catch (e) { /* ignore */ }
606
608
  }
607
609
 
610
+ // ═══════════════════════════════════════════════════════════════
611
+ // CLEAN NPX CACHE (remove old cached versions of merlin packages)
612
+ // ═══════════════════════════════════════════════════════════════
613
+ try {
614
+ const npxCacheDir = path.join(os.homedir(), '.npm', '_npx');
615
+ if (fs.existsSync(npxCacheDir)) {
616
+ const entries = fs.readdirSync(npxCacheDir, { withFileTypes: true });
617
+ for (const entry of entries) {
618
+ if (!entry.isDirectory()) continue;
619
+ const pkgJsonPath = path.join(npxCacheDir, entry.name, 'node_modules', 'create-merlin-brain', 'package.json');
620
+ const pkgJsonPath2 = path.join(npxCacheDir, entry.name, 'node_modules', 'create-merlin-pro', 'package.json');
621
+ if (fs.existsSync(pkgJsonPath) || fs.existsSync(pkgJsonPath2)) {
622
+ removeDirRecursive(path.join(npxCacheDir, entry.name));
623
+ cleaned.push('~/.npm/_npx/' + entry.name + ' (old npx cache)');
624
+ }
625
+ }
626
+ }
627
+ } catch (e) { /* ignore - npx cache cleanup is best-effort */ }
628
+
608
629
  // ═══════════════════════════════════════════════════════════════
609
630
  // PATTERN-BASED CLEANUP IN AGENTS DIR
610
631
  // ═══════════════════════════════════════════════════════════════
@@ -707,7 +728,7 @@ async function install() {
707
728
  }
708
729
 
709
730
  // Step 0: Clean up legacy GSD/ccwiki artifacts
710
- logStep('0/8', 'Cleaning up legacy installations...');
731
+ logStep('0/9', 'Cleaning up legacy installations...');
711
732
  const cleaned = cleanupLegacy();
712
733
  if (cleaned.length > 0) {
713
734
  for (const item of cleaned) {
@@ -717,16 +738,53 @@ async function install() {
717
738
  logSuccess('No legacy artifacts found');
718
739
  }
719
740
 
720
- // Step 1: Create directories
721
- logStep('1/8', 'Creating directories...');
741
+ // Step 1: Install globally for instant startup across all terminals
742
+ logStep('1/9', 'Installing globally (fast startup for all terminals)...');
743
+ try {
744
+ const { execSync } = require('child_process');
745
+ // Check if already installed globally and up-to-date
746
+ let needsInstall = true;
747
+ try {
748
+ const globalVersion = execSync('merlin-brain --version 2>/dev/null || echo "none"', { encoding: 'utf8' }).trim();
749
+ const localVersion = require('../package.json').version;
750
+ if (globalVersion === localVersion) {
751
+ logSuccess(`Already installed globally (v${localVersion})`);
752
+ needsInstall = false;
753
+ }
754
+ } catch (e) { /* not installed globally yet */ }
755
+
756
+ if (needsInstall) {
757
+ const localVersion = require('../package.json').version;
758
+ execSync(`npm install -g create-merlin-brain@${localVersion}`, { stdio: 'pipe' });
759
+ logSuccess('Installed merlin-brain globally');
760
+ logSuccess('All terminals now share the same fast binary');
761
+ }
762
+ } catch (e) {
763
+ logWarn('Could not install globally (permissions issue?)');
764
+ logWarn('Falling back to npx mode. To fix, run: sudo npm install -g create-merlin-brain');
765
+ logWarn('Or use: npm config set prefix ~/.npm-global && export PATH=~/.npm-global/bin:$PATH');
766
+ }
767
+
768
+ // Check if global binary is available (determines MCP config style)
769
+ let useGlobalBinary = false;
770
+ try {
771
+ const { execSync } = require('child_process');
772
+ execSync('which merlin-brain', { stdio: 'pipe' });
773
+ useGlobalBinary = true;
774
+ } catch (e) {
775
+ useGlobalBinary = false;
776
+ }
777
+
778
+ // Step 2: Create directories
779
+ logStep('2/9', 'Creating directories...');
722
780
  ensureDir(CLAUDE_DIR);
723
781
  ensureDir(MERLIN_DIR);
724
782
  ensureDir(AGENTS_DIR);
725
783
  ensureDir(COMMANDS_DIR);
726
784
  logSuccess('Directories created');
727
785
 
728
- // Step 2: Install Merlin core (workflows, references, templates)
729
- logStep('2/8', 'Installing Merlin workflows...');
786
+ // Step 3: Install Merlin core (workflows, references, templates)
787
+ logStep('3/9', 'Installing Merlin workflows...');
730
788
  const merlinSrc = path.join(filesDir, 'merlin');
731
789
  if (fs.existsSync(merlinSrc)) {
732
790
  const count = copyDirRecursive(merlinSrc, MERLIN_DIR);
@@ -735,8 +793,8 @@ async function install() {
735
793
  logWarn('Merlin workflows not found in package');
736
794
  }
737
795
 
738
- // Step 3: Install agents
739
- logStep('3/8', 'Installing Merlin agents...');
796
+ // Step 4: Install agents
797
+ logStep('4/9', 'Installing Merlin agents...');
740
798
  const agentsSrc = path.join(filesDir, 'agents');
741
799
  if (fs.existsSync(agentsSrc)) {
742
800
  const count = copyDirRecursive(agentsSrc, AGENTS_DIR);
@@ -745,8 +803,8 @@ async function install() {
745
803
  logWarn('Agents not found in package');
746
804
  }
747
805
 
748
- // Step 4: Install commands
749
- logStep('4/8', 'Installing /merlin:* commands...');
806
+ // Step 5: Install commands
807
+ logStep('5/9', 'Installing /merlin:* commands...');
750
808
  const commandsSrc = path.join(filesDir, 'commands', 'merlin');
751
809
  if (fs.existsSync(commandsSrc)) {
752
810
  const count = copyDirRecursive(commandsSrc, COMMANDS_DIR);
@@ -755,8 +813,8 @@ async function install() {
755
813
  logWarn('Commands not found in package');
756
814
  }
757
815
 
758
- // Step 5: Install CLAUDE.md
759
- logStep('5/8', 'Configuring Claude Code...');
816
+ // Step 6: Install CLAUDE.md
817
+ logStep('6/9', 'Configuring Claude Code...');
760
818
  const claudeMdSrc = path.join(filesDir, 'CLAUDE.md');
761
819
  const claudeMdDest = path.join(CLAUDE_DIR, 'CLAUDE.md');
762
820
 
@@ -767,8 +825,8 @@ async function install() {
767
825
  logSuccess('Installed CLAUDE.md (Merlin instructions)');
768
826
  }
769
827
 
770
- // Step 6: Install Merlin Loop (autonomous orchestration)
771
- logStep('6/8', 'Installing Merlin Loop...');
828
+ // Step 7: Install Merlin Loop (autonomous orchestration)
829
+ logStep('7/9', 'Installing Merlin Loop...');
772
830
  const loopSrc = path.join(filesDir, 'loop');
773
831
  if (fs.existsSync(loopSrc)) {
774
832
  ensureDir(LOOP_DIR);
@@ -793,8 +851,18 @@ async function install() {
793
851
  logWarn('Merlin Loop not found in package');
794
852
  }
795
853
 
796
- // Step 7: Optional Merlin Sights configuration
797
- logStep('7/8', 'Optional: Merlin Sights configuration...');
854
+ // Helper: create MCP config object based on available binary
855
+ function mcpConfig(apiKey, includeType) {
856
+ const cfg = useGlobalBinary
857
+ ? { command: 'merlin-brain' }
858
+ : { command: 'npx', args: ['create-merlin-brain@latest', 'serve'] };
859
+ if (includeType) cfg.type = 'stdio';
860
+ if (apiKey) cfg.env = { MERLIN_API_KEY: apiKey };
861
+ return cfg;
862
+ }
863
+
864
+ // Step 8: Optional Merlin Sights configuration
865
+ logStep('8/9', 'Optional: Merlin Sights configuration...');
798
866
  console.log(`Merlin Sights provides instant codebase context.`);
799
867
  console.log(`Get your API key at: ${colors.cyan}https://merlin.build${colors.reset}`);
800
868
 
@@ -813,13 +881,7 @@ async function install() {
813
881
  }
814
882
 
815
883
  config.mcpServers = config.mcpServers || {};
816
- config.mcpServers.merlin = {
817
- command: 'npx',
818
- args: ['create-merlin-brain@latest', 'serve'],
819
- env: {
820
- MERLIN_API_KEY: apiKey
821
- }
822
- };
884
+ config.mcpServers.merlin = mcpConfig(apiKey, false);
823
885
 
824
886
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
825
887
  logSuccess('Merlin Sights configured for Claude Code CLI');
@@ -839,13 +901,7 @@ async function install() {
839
901
  }
840
902
 
841
903
  desktopConfig.mcpServers = desktopConfig.mcpServers || {};
842
- desktopConfig.mcpServers.merlin = {
843
- command: 'npx',
844
- args: ['create-merlin-brain@latest', 'serve'],
845
- env: {
846
- MERLIN_API_KEY: apiKey
847
- }
848
- };
904
+ desktopConfig.mcpServers.merlin = mcpConfig(apiKey, false);
849
905
 
850
906
  // Ensure directory exists
851
907
  const desktopConfigDir = path.dirname(desktopConfigPath);
@@ -868,11 +924,7 @@ async function install() {
868
924
  }
869
925
  }
870
926
  claudeDirConfig.mcpServers = claudeDirConfig.mcpServers || {};
871
- claudeDirConfig.mcpServers.merlin = {
872
- command: 'npx',
873
- args: ['create-merlin-brain@latest', 'serve'],
874
- env: { MERLIN_API_KEY: apiKey }
875
- };
927
+ claudeDirConfig.mcpServers.merlin = mcpConfig(apiKey, false);
876
928
  fs.writeFileSync(claudeDirDesktopConfig, JSON.stringify(claudeDirConfig, null, 2));
877
929
  logSuccess('Merlin Sights configured for ~/.claude/claude_desktop_config.json');
878
930
 
@@ -882,12 +934,7 @@ async function install() {
882
934
  try {
883
935
  let claudeJson = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf8'));
884
936
  claudeJson.mcpServers = claudeJson.mcpServers || {};
885
- claudeJson.mcpServers.merlin = {
886
- type: 'stdio',
887
- command: 'npx',
888
- args: ['create-merlin-brain@latest', 'serve'],
889
- env: { MERLIN_API_KEY: apiKey }
890
- };
937
+ claudeJson.mcpServers.merlin = mcpConfig(apiKey, true);
891
938
  fs.writeFileSync(claudeJsonPath, JSON.stringify(claudeJson, null, 2));
892
939
  logSuccess('Merlin Sights configured for ~/.claude.json (Claude MCP registry)');
893
940
  } catch (e) {
@@ -896,28 +943,25 @@ async function install() {
896
943
  } else {
897
944
  // Create ~/.claude.json with MCP config if it doesn't exist
898
945
  try {
899
- const claudeJson = {
900
- mcpServers: {
901
- merlin: {
902
- type: 'stdio',
903
- command: 'npx',
904
- args: ['create-merlin-brain@latest', 'serve'],
905
- env: { MERLIN_API_KEY: apiKey }
906
- }
907
- }
908
- };
946
+ const claudeJson = { mcpServers: { merlin: mcpConfig(apiKey, true) } };
909
947
  fs.writeFileSync(claudeJsonPath, JSON.stringify(claudeJson, null, 2));
910
948
  logSuccess('Merlin Sights configured for ~/.claude.json (Claude MCP registry - created)');
911
949
  } catch (e) {
912
950
  logWarn('Could not create ~/.claude.json');
913
951
  }
914
952
  }
953
+
954
+ if (useGlobalBinary) {
955
+ logSuccess(`Using global binary (merlin-brain) — instant startup in all terminals!`);
956
+ } else {
957
+ logWarn('Using npx fallback — run "npm install -g create-merlin-brain" for faster startup');
958
+ }
915
959
  } else {
916
960
  logWarn('Skipped Merlin Sights (you can configure it later)');
917
961
  }
918
962
 
919
- // Step 7: Set up shell integration
920
- logStep('8/8', 'Setting up shell integration...');
963
+ // Step 9: Set up shell integration
964
+ logStep('9/9', 'Setting up shell integration...');
921
965
  const shellConfigured = setupShellIntegration();
922
966
  if (shellConfigured) {
923
967
  log(`\n ${colors.yellow}IMPORTANT:${colors.reset} Run ${colors.cyan}source ~/.zshrc${colors.reset} or ${colors.bright}restart your terminal${colors.reset}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-merlin-brain",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "Merlin - The Ultimate AI Brain for Claude Code. Installs workflows, agents, and Sights MCP server.",
5
5
  "type": "module",
6
6
  "main": "./dist/server/index.js",