create-merlin-brain 3.5.2 → 3.5.3

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
@@ -937,15 +937,18 @@ async function install() {
937
937
  return hookArray;
938
938
  };
939
939
 
940
- // SessionStart hooks
940
+ // SessionStart: single consolidated hook (analytics + agent sync + context injection)
941
+ // Previously this was 3 separate hooks, causing 3x "startup hook success" messages.
942
+ // Now consolidated into session-start.sh which handles all three concerns.
941
943
  settings.hooks.SessionStart = settings.hooks.SessionStart || [];
942
- addHookIfMissing(settings.hooks.SessionStart, {
943
- hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-start.sh' }]
944
+ // Remove old separate hooks if present (from previous installs)
945
+ settings.hooks.SessionStart = settings.hooks.SessionStart.filter(entry => {
946
+ const cmd = entry.hooks?.[0]?.command || '';
947
+ if (cmd.includes('agent-sync.sh') || cmd.includes('session-start-context.sh')) return false;
948
+ return true;
944
949
  });
945
-
946
- // SessionStart hook: Agent sync (background, runs at most once/hour)
947
950
  addHookIfMissing(settings.hooks.SessionStart, {
948
- hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/agent-sync.sh' }]
951
+ hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-start.sh' }]
949
952
  });
950
953
 
951
954
  // PreToolUse hook (Edit/Write only)
@@ -1001,29 +1004,33 @@ async function install() {
1001
1004
  settings.hooks.TaskCompleted = settings.hooks.TaskCompleted || [];
1002
1005
  settings.hooks.TaskCompleted = removeAllPromptHooks(settings.hooks.TaskCompleted);
1003
1006
 
1004
- // Now add fresh hooks in new format
1005
- // NOTE: SessionStart does NOT support prompt hooks (only command hooks).
1006
- // The Merlin boot sequence is handled via CLAUDE.md instructions instead.
1007
- // We use a command hook to inject additionalContext at session start.
1008
- addHookIfMissing(settings.hooks.SessionStart, {
1009
- hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-start-context.sh' }]
1010
- });
1011
-
1012
1007
  // NOTE: The PreToolUse prompt hook has been REMOVED. It caused an infinite
1013
1008
  // rejection loop because the evaluator model cannot see conversation history
1014
1009
  // to verify whether merlin_get_context was called. The command-type hook
1015
1010
  // (pre-edit-sights-check.sh) handles advisory logging instead.
1016
1011
  // Sights enforcement is done via CLAUDE.md instructions, not hook blocking.
1017
1012
 
1018
- // Prompt-based Stop hook: evaluate whether stopping is appropriate
1019
- settings.hooks.Stop.push({
1020
- hooks: [{ type: 'prompt', prompt: fs.readFileSync(path.join(HOOKS_DIR, 'stop-check.md'), 'utf8') }]
1021
- });
1022
-
1023
- // Task completion verification hook (using dedicated TaskCompleted event)
1024
- settings.hooks.TaskCompleted.push({
1025
- hooks: [{ type: 'prompt', prompt: fs.readFileSync(path.join(HOOKS_DIR, 'task-completed-verify.md'), 'utf8') }]
1026
- });
1013
+ // ═══════════════════════════════════════════════════════════════
1014
+ // PROMPT HOOKS PERMANENTLY REMOVED (v3.5.3)
1015
+ // ═══════════════════════════════════════════════════════════════
1016
+ // Stop and TaskCompleted prompt hooks have been REMOVED because:
1017
+ //
1018
+ // 1. The evaluator model (Haiku) frequently wraps JSON responses
1019
+ // in markdown code fences or adds explanatory text, causing
1020
+ // "JSON validation failed" errors on every session stop.
1021
+ //
1022
+ // 2. This is inherent to prompt-type hooks — they depend on a
1023
+ // small model producing byte-perfect JSON output, which is
1024
+ // unreliable.
1025
+ //
1026
+ // 3. Stop behavior is already governed by CLAUDE.md instructions.
1027
+ // Task completion verification is handled by the session's own
1028
+ // Claude instance, which has full conversation context (unlike
1029
+ // the tiny evaluator model).
1030
+ //
1031
+ // The removeAllPromptHooks() calls above (lines ~996-1002) will
1032
+ // clean up existing prompt hooks from previous installs.
1033
+ // ═══════════════════════════════════════════════════════════════
1027
1034
 
1028
1035
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
1029
1036
  logSuccess('Configured hooks in settings.local.json');
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # Merlin Hook: SessionStart
4
- # Initializes analytics session and kicks off Sights context in background.
3
+ # Merlin Hook: SessionStart (consolidated)
4
+ # Does three things in a single hook (avoids 3x "success" messages):
5
+ # 1. Analytics: initialize session, log start event
6
+ # 2. Agent sync: refresh agent files from cloud (background, max once/hour)
7
+ # 3. Context injection: output additionalContext for the session
8
+ #
5
9
  # Always exits 0 — never blocks Claude Code startup.
6
10
  #
7
11
  set -euo pipefail
@@ -9,25 +13,73 @@ trap 'echo "{}"; exit 0' ERR
9
13
 
10
14
  HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
15
 
12
- # Source shared libraries
16
+ # ── 1. Analytics ────────────────────────────────────────────────
13
17
  # shellcheck source=lib/analytics.sh
14
18
  . "${HOOKS_DIR}/lib/analytics.sh"
15
19
  # shellcheck source=lib/sights-check.sh
16
20
  . "${HOOKS_DIR}/lib/sights-check.sh"
17
21
 
18
- # Initialize analytics session
19
22
  ensure_session_file
20
-
21
- # Log session start with working directory
22
23
  cwd="${PWD:-$(pwd)}"
23
24
  log_event "session_start" "$(printf '{"cwd":"%s"}' "$cwd")"
24
25
 
25
- # If merlin CLI is available, warm up Sights context in background
26
+ # Warm up Sights context in background
26
27
  if command -v merlin >/dev/null 2>&1; then
27
28
  merlin context "session start" >/dev/null 2>&1 &
28
29
  record_sights_call
29
30
  fi
30
31
 
31
- # Claude Code command hooks must output valid JSON to stdout
32
- echo '{}'
32
+ # ── 2. Agent sync (background, max once/hour) ──────────────────
33
+ _merlin_sync_agents() {
34
+ local agents_dir="${HOME}/.claude/agents"
35
+ local merlin_dir="${HOME}/.claude/merlin"
36
+ local last_sync="${merlin_dir}/.last-agent-sync"
37
+ local api_url="${MERLIN_API_URL:-https://api.merlin.build}"
38
+
39
+ [ -d "${agents_dir}" ] || return 0
40
+
41
+ # Skip if synced within the last hour
42
+ if [ -f "${last_sync}" ]; then
43
+ local last
44
+ last=$(cat "${last_sync}" 2>/dev/null || echo "0")
45
+ [ $(($(date +%s) - last)) -lt 3600 ] && return 0
46
+ fi
47
+
48
+ local installed=""
49
+ for f in "${agents_dir}"/*.md; do
50
+ [ -f "${f}" ] || continue
51
+ local name hash
52
+ name=$(basename "${f}" .md)
53
+ hash=$(md5sum "${f}" 2>/dev/null | cut -c1-8 || md5 -q "${f}" 2>/dev/null | cut -c1-8 || echo "unknown")
54
+ installed="${installed}${name}:${hash},"
55
+ done
56
+
57
+ local response
58
+ response=$(curl -s --max-time 5 "${api_url}/api/agents-sync/check?installed=${installed}" 2>/dev/null) || return 0
59
+ local stale_names
60
+ stale_names=$(echo "${response}" | grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"//' 2>/dev/null) || return 0
61
+ [ -z "${stale_names}" ] && { date +%s > "${last_sync}"; return 0; }
62
+
63
+ for agent_name in ${stale_names}; do
64
+ local content md_content
65
+ content=$(curl -s --max-time 5 "${api_url}/api/agents-sync/${agent_name}" 2>/dev/null) || continue
66
+ md_content=$(echo "${content}" | python3 -c "import sys,json;print(json.load(sys.stdin).get('content',''))" 2>/dev/null) || continue
67
+ [ -n "${md_content}" ] && echo "${md_content}" > "${agents_dir}/${agent_name}.md"
68
+ done
69
+ date +%s > "${last_sync}"
70
+ }
71
+ _merlin_sync_agents &
72
+
73
+ # ── 3. Context injection (the only stdout output) ──────────────
74
+ # Output additionalContext JSON for Claude to see at session start.
75
+ # Full boot instructions are in CLAUDE.md — this is a lightweight nudge.
76
+ cat <<'CONTEXT_JSON'
77
+ {
78
+ "hookSpecificOutput": {
79
+ "hookEventName": "SessionStart",
80
+ "additionalContext": "You are a Merlin-powered session. Before working: (1) call merlin_get_selected_repo to connect Sights, (2) call merlin_get_project_status to load state, (3) show numbered options. Check Sights before every edit. Route complex tasks to specialists via /merlin:route. Save checkpoints before stopping."
81
+ }
82
+ }
83
+ CONTEXT_JSON
84
+
33
85
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-merlin-brain",
3
- "version": "3.5.2",
3
+ "version": "3.5.3",
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",