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.
- package/bin/install.cjs +116 -72
- 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: '
|
|
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
|
-
//
|
|
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
|
-
|
|
425
|
-
|
|
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 (
|
|
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: '
|
|
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.
|
|
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 (
|
|
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 (
|
|
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
|
-
//
|
|
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
|
-
|
|
589
|
-
claudeJson.mcpServers.merlin.args
|
|
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 (
|
|
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/
|
|
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:
|
|
721
|
-
logStep('1/
|
|
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
|
|
729
|
-
logStep('
|
|
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
|
|
739
|
-
logStep('
|
|
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
|
|
749
|
-
logStep('
|
|
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
|
|
759
|
-
logStep('
|
|
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
|
|
771
|
-
logStep('
|
|
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
|
-
//
|
|
797
|
-
|
|
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
|
|
920
|
-
logStep('
|
|
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