create-merlin-brain 3.4.4 → 3.4.6

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 CHANGED
@@ -794,7 +794,10 @@ async function install() {
794
794
  const merlinSrc = path.join(filesDir, 'merlin');
795
795
  if (fs.existsSync(merlinSrc)) {
796
796
  const count = copyDirRecursive(merlinSrc, MERLIN_DIR);
797
- logSuccess(`Installed ${count} workflow files`);
797
+ // Always write VERSION from package.json (the static file may be stale)
798
+ const currentVersion = require('../package.json').version;
799
+ fs.writeFileSync(path.join(MERLIN_DIR, 'VERSION'), currentVersion);
800
+ logSuccess(`Installed ${count} workflow files (v${currentVersion})`);
798
801
  } else {
799
802
  logWarn('Merlin workflows not found in package');
800
803
  }
@@ -944,36 +947,48 @@ async function install() {
944
947
  });
945
948
 
946
949
  // --- Prompt-based hooks (LLM evaluates .md content) ---
947
- const addPromptHookIfMissing = (hookArray, hookConfig) => {
948
- const exists = hookArray.some(h => h.type === 'prompt' && h.prompt === hookConfig.prompt);
949
- if (!exists) hookArray.push(hookConfig);
950
- return hookArray;
950
+ // On update, old prompt hooks have stale content. Remove all Merlin prompt hooks
951
+ // first (identified by containing 'merlin' or 'Merlin' in their prompt text),
952
+ // then re-add fresh ones. This prevents duplicate hooks across version upgrades.
953
+ const removeMerlinPromptHooks = (hookArray) => {
954
+ return hookArray.filter(h => {
955
+ if (h.type !== 'prompt') return true; // keep non-prompt hooks
956
+ const prompt = (h.prompt || '').toLowerCase();
957
+ // Merlin prompt hooks contain these markers
958
+ return !(prompt.includes('merlin') || prompt.includes('sights'));
959
+ });
951
960
  };
952
961
 
962
+ settings.hooks.SessionStart = removeMerlinPromptHooks(settings.hooks.SessionStart);
963
+ settings.hooks.PreToolUse = removeMerlinPromptHooks(settings.hooks.PreToolUse);
964
+ settings.hooks.Stop = removeMerlinPromptHooks(settings.hooks.Stop);
965
+ settings.hooks.Notification = settings.hooks.Notification || [];
966
+ settings.hooks.Notification = removeMerlinPromptHooks(settings.hooks.Notification);
967
+
968
+ // Now add fresh prompt hooks
953
969
  // Prompt-based SessionStart hook: FULL Merlin boot sequence
954
970
  // This is the key hook that makes every session feel like Loop
955
- addPromptHookIfMissing(settings.hooks.SessionStart, {
971
+ settings.hooks.SessionStart.push({
956
972
  type: 'prompt',
957
973
  prompt: fs.readFileSync(path.join(HOOKS_DIR, 'session-start-boot.md'), 'utf8')
958
974
  });
959
975
 
960
976
  // Prompt-based PreToolUse hook: enforce Sights check before edits
961
977
  // This is the Sights guarantee — no edit without context
962
- addPromptHookIfMissing(settings.hooks.PreToolUse, {
978
+ settings.hooks.PreToolUse.push({
963
979
  type: 'prompt',
964
980
  prompt: fs.readFileSync(path.join(HOOKS_DIR, 'pre-edit-sights-enforce.md'), 'utf8'),
965
981
  matcher: 'Edit|Write'
966
982
  });
967
983
 
968
984
  // Prompt-based Stop hook: quality gate before session ends
969
- addPromptHookIfMissing(settings.hooks.Stop, {
985
+ settings.hooks.Stop.push({
970
986
  type: 'prompt',
971
987
  prompt: fs.readFileSync(path.join(HOOKS_DIR, 'stop-check.md'), 'utf8')
972
988
  });
973
989
 
974
990
  // Prompt-based Notification hook: verify task completion quality
975
- settings.hooks.Notification = settings.hooks.Notification || [];
976
- addPromptHookIfMissing(settings.hooks.Notification, {
991
+ settings.hooks.Notification.push({
977
992
  type: 'prompt',
978
993
  prompt: fs.readFileSync(path.join(HOOKS_DIR, 'task-completed-verify.md'), 'utf8'),
979
994
  matcher: 'task_completed'
@@ -996,11 +1011,32 @@ async function install() {
996
1011
  }
997
1012
 
998
1013
  // Step 9: Optional Merlin Sights configuration
999
- logStep('9/10', 'Optional: Merlin Sights configuration...');
1000
- console.log(`Merlin Sights provides instant codebase context.`);
1001
- console.log(`Get your API key at: ${colors.cyan}https://merlin.build${colors.reset}`);
1014
+ logStep('9/10', 'Merlin Sights configuration...');
1002
1015
 
1003
- const apiKey = await promptApiKey();
1016
+ // Check if API key is already configured (skip prompt on updates)
1017
+ let existingApiKey = '';
1018
+ const configCheckPaths = [
1019
+ path.join(CLAUDE_DIR, 'config.json'),
1020
+ path.join(os.homedir(), '.claude.json'),
1021
+ ];
1022
+ for (const cfgPath of configCheckPaths) {
1023
+ if (existingApiKey) break;
1024
+ if (fs.existsSync(cfgPath)) {
1025
+ try {
1026
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
1027
+ existingApiKey = cfg.mcpServers?.merlin?.env?.MERLIN_API_KEY || '';
1028
+ } catch (e) { /* ignore */ }
1029
+ }
1030
+ }
1031
+
1032
+ let apiKey = existingApiKey;
1033
+ if (existingApiKey) {
1034
+ logSuccess(`Sights API key already configured (skipping prompt)`);
1035
+ } else {
1036
+ console.log(`Merlin Sights provides instant codebase context.`);
1037
+ console.log(`Get your API key at: ${colors.cyan}https://merlin.build${colors.reset}`);
1038
+ apiKey = await promptApiKey();
1039
+ }
1004
1040
 
1005
1041
  if (apiKey) {
1006
1042
  const configPath = path.join(CLAUDE_DIR, 'config.json');
@@ -41,7 +41,7 @@ function loadSavedConfig() {
41
41
  export function createServer() {
42
42
  const server = new McpServer({
43
43
  name: 'merlin',
44
- version: '3.2.0',
44
+ version: VERSION,
45
45
  description: 'Merlin - AI-powered codebase intelligence. Syncs every 5 min. ALWAYS fetch fresh context before coding.',
46
46
  });
47
47
  const client = getClient();
@@ -1,6 +1,2 @@
1
- /**
2
- * Merlin Brain Version
3
- * Single source of truth for version number
4
- */
5
- export declare const VERSION = "3.2.0";
1
+ export declare const VERSION: string;
6
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/server/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/server/version.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,OAAO,QAAe,CAAC"}
@@ -1,6 +1,29 @@
1
1
  /**
2
2
  * Merlin Brain Version
3
- * Single source of truth for version number
3
+ * Reads from package.json at runtime so it's always correct after npm publish.
4
4
  */
5
- export const VERSION = '3.2.0';
5
+ import { readFileSync } from 'fs';
6
+ import { resolve, dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ function getVersion() {
9
+ try {
10
+ // Walk up from dist/server/ or src/server/ to find package.json
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ // Try two levels up (dist/server -> package root, or src/server -> package root)
14
+ for (const rel of ['../..', '../../..']) {
15
+ const pkgPath = resolve(__dirname, rel, 'package.json');
16
+ try {
17
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
18
+ if (pkg.name === 'create-merlin-brain' && pkg.version) {
19
+ return pkg.version;
20
+ }
21
+ }
22
+ catch { /* try next */ }
23
+ }
24
+ }
25
+ catch { /* fallback */ }
26
+ return '0.0.0'; // Should never happen — means package.json is missing
27
+ }
28
+ export const VERSION = getVersion();
6
29
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/server/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/server/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,gEAAgE;QAChE,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,iFAAiF;QACjF,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;YACxC,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;YACxD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;gBACtD,IAAI,GAAG,CAAC,IAAI,KAAK,qBAAqB,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;oBACtD,OAAO,GAAG,CAAC,OAAO,CAAC;gBACrB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;IAC1B,OAAO,OAAO,CAAC,CAAC,sDAAsD;AACxE,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC"}
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.4.6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-merlin-brain",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
4
4
  "description": "Merlin - The Ultimate AI Brain for Claude Code. One install: workflows, agents, loop, and Sights MCP server.",
5
5
  "type": "module",
6
6
  "main": "./dist/server/index.js",