create-merlin-brain 3.14.0 → 3.15.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.
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
3
  # Merlin Hook: PostToolUse (Edit/Write)
4
- # Logs file changes for analytics tracking.
4
+ # Logs file changes for analytics tracking AND reports edits to
5
+ # the Guardian HTTP sidecar so it can track edit-without-sights patterns.
5
6
  # Always exits 0 — never blocks Claude Code.
6
7
  #
7
8
  set -euo pipefail
@@ -30,6 +31,27 @@ fi
30
31
  # Log file change event
31
32
  log_event "file_changed" "$(printf '{"file":"%s","tool":"%s"}' "${file_path:-unknown}" "${tool_name:-unknown}")"
32
33
 
34
+ # ─────────────────────────────────────────────────────────────────────────────
35
+ # Report edit to Guardian sidecar (fire-and-forget, 100ms timeout)
36
+ # This gives the MCP server visibility into Claude's actual file edits.
37
+ # ─────────────────────────────────────────────────────────────────────────────
38
+ MERLIN_DIR="${HOME}/.claude/merlin"
39
+ PORT_FILE="${MERLIN_DIR}/.guardian-port"
40
+
41
+ if [ -f "$PORT_FILE" ] && command -v jq >/dev/null 2>&1; then
42
+ GUARDIAN_PORT=$(jq -r '.port // empty' "$PORT_FILE" 2>/dev/null || true)
43
+ GUARDIAN_PID=$(jq -r '.pid // empty' "$PORT_FILE" 2>/dev/null || true)
44
+
45
+ if [ -n "$GUARDIAN_PORT" ] && [ -n "$GUARDIAN_PID" ] && kill -0 "$GUARDIAN_PID" 2>/dev/null; then
46
+ # Fire-and-forget in background — never blocks the edit
47
+ curl -s --max-time 0.1 -X POST \
48
+ "http://127.0.0.1:${GUARDIAN_PORT}/record-edit" \
49
+ -H "Content-Type: application/json" \
50
+ -d "$(printf '{"file":"%s","tool":"%s"}' "${file_path:-unknown}" "${tool_name:-unknown}")" \
51
+ >/dev/null 2>&1 &
52
+ fi
53
+ fi
54
+
33
55
  # Claude Code command hooks must output valid JSON to stdout
34
56
  echo '{}'
35
57
  exit 0
@@ -2,7 +2,9 @@
2
2
  #
3
3
  # Merlin Hook: PreCompact
4
4
  # Saves session state before Claude Code compacts the context window.
5
- # This preserves what was being worked on so it survives compaction.
5
+ # CRITICAL: Injects Merlin protocol rules as additionalContext so they
6
+ # survive context compression. Without this, CLAUDE.md instructions fade
7
+ # and Merlin goes silent after compaction.
6
8
  # Always exits 0 — never blocks compaction.
7
9
  #
8
10
  set -euo pipefail
@@ -12,16 +14,61 @@ HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
14
 
13
15
  # Source shared libraries
14
16
  # shellcheck source=lib/analytics.sh
15
- . "${HOOKS_DIR}/lib/analytics.sh"
17
+ [ -f "${HOOKS_DIR}/lib/analytics.sh" ] && . "${HOOKS_DIR}/lib/analytics.sh"
16
18
 
17
19
  # Log compaction event
18
- log_event "pre_compact" '{}'
20
+ if declare -f log_event >/dev/null 2>&1; then
21
+ log_event "pre_compact" '{}'
22
+ fi
19
23
 
20
24
  # If merlin CLI is available, save checkpoint in background
21
25
  if command -v merlin >/dev/null 2>&1; then
22
26
  merlin save-checkpoint "Pre-compaction checkpoint" >/dev/null 2>&1 &
23
27
  fi
24
28
 
25
- # Claude Code command hooks must output valid JSON to stdout
26
- echo '{}'
29
+ # ─────────────────────────────────────────────────────────────────────────────
30
+ # INJECT MERLIN PROTOCOL RULES INTO POST-COMPACTION CONTEXT
31
+ # This is the single most important thing we do: when Claude's context gets
32
+ # compressed, CLAUDE.md instructions are deprioritized. By injecting protocol
33
+ # rules as additionalContext in the compaction response, they survive.
34
+ # ─────────────────────────────────────────────────────────────────────────────
35
+
36
+ # Try to get session state from guardian for richer context
37
+ GUARDIAN_STATE=""
38
+ MERLIN_DIR="${HOME}/.claude/merlin"
39
+ PORT_FILE="${MERLIN_DIR}/.guardian-port"
40
+
41
+ if [ -f "$PORT_FILE" ] && command -v jq >/dev/null 2>&1; then
42
+ GUARDIAN_PORT=$(jq -r '.port // empty' "$PORT_FILE" 2>/dev/null || true)
43
+ GUARDIAN_PID=$(jq -r '.pid // empty' "$PORT_FILE" 2>/dev/null || true)
44
+
45
+ if [ -n "$GUARDIAN_PORT" ] && [ -n "$GUARDIAN_PID" ] && kill -0 "$GUARDIAN_PID" 2>/dev/null; then
46
+ GUARDIAN_STATE=$(curl -s --max-time 0.3 "http://127.0.0.1:${GUARDIAN_PORT}/state" 2>/dev/null || true)
47
+ fi
48
+ fi
49
+
50
+ # Build the protocol reminder that survives compaction
51
+ SESSION_INFO=""
52
+ if [ -n "$GUARDIAN_STATE" ] && command -v jq >/dev/null 2>&1; then
53
+ HEALTH=$(echo "$GUARDIAN_STATE" | jq -r '.health // "?"' 2>/dev/null || echo "?")
54
+ DRIFT=$(echo "$GUARDIAN_STATE" | jq -r '.driftLevel // "?"' 2>/dev/null || echo "?")
55
+ EDITS=$(echo "$GUARDIAN_STATE" | jq -r '.editsSinceContext // "?"' 2>/dev/null || echo "?")
56
+ SESSION_INFO=" Session health: ${HEALTH}%, drift level: ${DRIFT}, edits since context: ${EDITS}."
57
+ fi
58
+
59
+ # The protocol rules that MUST survive compaction
60
+ CONTEXT="MERLIN PROTOCOL REMINDER (post-compaction): You are in a Merlin-enhanced session. RULES: (1) Call merlin_get_context before every file edit. (2) Call merlin_find_files before creating new files. (3) Prefix Merlin actions with the badge. (4) Route specialist tasks via /merlin:route. (5) Save behaviors when user corrects you.${SESSION_INFO}"
61
+
62
+ if command -v jq >/dev/null 2>&1; then
63
+ jq -n --arg ctx "$CONTEXT" '{
64
+ hookSpecificOutput: {
65
+ hookEventName: "PreCompact",
66
+ additionalContext: $ctx
67
+ }
68
+ }'
69
+ else
70
+ printf '{"hookSpecificOutput":{"hookEventName":"PreCompact","additionalContext":"%s"}}\n' \
71
+ "MERLIN PROTOCOL REMINDER: Call merlin_get_context before edits. Call merlin_find_files before creating files."
72
+ fi
73
+
27
74
  exit 0
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
3
  # Merlin Hook: PreToolUse (Edit/Write)
4
- # Two responsibilities:
4
+ # Three responsibilities:
5
5
  # 1. Secret detection — blocks writes that contain leaked credentials.
6
6
  # Exit code 2 with a clear message. This is the only hard block.
7
- # 2. Sights consultation check — advisory warning if Sights was not
8
- # consulted recently. Always exits 0 (never blocks on this).
7
+ # 2. Guardian check — asks the MCP server's HTTP sidecar for session-aware
8
+ # enforcement. Returns additionalContext nudges when context is stale.
9
+ # 3. File-based fallback — if guardian is unreachable, checks the timestamp
10
+ # file and returns additionalContext directly.
9
11
  #
10
12
  set -euo pipefail
11
13
  trap 'echo "{}"; exit 0' ERR
@@ -34,66 +36,89 @@ if [ -n "$input" ] && command -v jq >/dev/null 2>&1; then
34
36
  fi
35
37
 
36
38
  # ─────────────────────────────────────────────────────────────────────────────
37
- # SECRET DETECTION
38
- # Scans the content being written for common credential patterns.
39
- # Uses grep with POSIX ERE for speed (<5ms on typical file sizes).
39
+ # 1. SECRET DETECTION (always local, always fast, always blocking)
40
40
  # ─────────────────────────────────────────────────────────────────────────────
41
41
  if [ -n "$content_to_write" ]; then
42
42
  SECRET_FOUND=""
43
43
  SECRET_TYPE=""
44
44
 
45
- # AWS access key: AKIA followed by 16 uppercase alphanumeric chars
46
45
  if echo "$content_to_write" | grep -qE 'AKIA[0-9A-Z]{16}' 2>/dev/null; then
47
- SECRET_FOUND=1
48
- SECRET_TYPE="AWS access key (AKIA...)"
46
+ SECRET_FOUND=1; SECRET_TYPE="AWS access key (AKIA...)"
49
47
  fi
50
-
51
- # OpenAI / Anthropic style API key: sk- followed by 48+ alphanumeric chars
52
48
  if [ -z "$SECRET_FOUND" ] && echo "$content_to_write" | grep -qE 'sk-[a-zA-Z0-9]{48,}' 2>/dev/null; then
53
- SECRET_FOUND=1
54
- SECRET_TYPE="API secret key (sk-...)"
49
+ SECRET_FOUND=1; SECRET_TYPE="API secret key (sk-...)"
55
50
  fi
56
-
57
- # PEM private key header
58
51
  if [ -z "$SECRET_FOUND" ] && echo "$content_to_write" | \
59
52
  grep -qE '-----BEGIN (RSA|EC|DSA|OPENSSH|PRIVATE) KEY-----' 2>/dev/null; then
60
- SECRET_FOUND=1
61
- SECRET_TYPE="PEM private key"
53
+ SECRET_FOUND=1; SECRET_TYPE="PEM private key"
62
54
  fi
63
-
64
- # Generic password assignment in config-like contexts
65
- # Matches: password=, PASSWORD=, "password": "...", passwd=
66
- # Requires the value to be non-empty and at least 8 chars to avoid false positives
67
55
  if [ -z "$SECRET_FOUND" ] && echo "$content_to_write" | \
68
56
  grep -qiE '(password|passwd|secret|api_key|apikey)\s*[=:]\s*["\x27]?[A-Za-z0-9@#$%^&*!_\-]{8,}' 2>/dev/null; then
69
- # Exclude known placeholder patterns: <password>, ${PASSWORD}, %(password)s, REPLACE_ME, etc.
70
57
  if ! echo "$content_to_write" | \
71
58
  grep -qiE '(password|passwd|secret|api_key|apikey)\s*[=:]\s*["\x27]?(<[^>]+>|\$\{[^}]+\}|%\([^)]+\)s|REPLACE_ME|YOUR_|TODO|CHANGEME|example|placeholder|xxx+|test)' 2>/dev/null; then
72
- SECRET_FOUND=1
73
- SECRET_TYPE="credential assignment (password/secret/api_key)"
59
+ SECRET_FOUND=1; SECRET_TYPE="credential assignment (password/secret/api_key)"
74
60
  fi
75
61
  fi
76
62
 
77
63
  if [ -n "$SECRET_FOUND" ]; then
78
64
  if declare -f log_event >/dev/null 2>&1; then
79
- log_event "secret_detected" "$(printf '{"file":"%s","type":"%s"}' \
80
- "${file_path:-unknown}" "$SECRET_TYPE")"
65
+ log_event "secret_detected" "$(printf '{"file":"%s","type":"%s"}' "${file_path:-unknown}" "$SECRET_TYPE")"
81
66
  fi
82
- echo "SECRET DETECTED: ${SECRET_TYPE} found in write to '${file_path:-unknown}'. Remove the secret before committing." >&2
83
- # Output block decision — Claude Code reads this JSON when exit code is 2
67
+ echo "SECRET DETECTED: ${SECRET_TYPE} in '${file_path:-unknown}'. Remove the secret." >&2
84
68
  echo '{"decision":"block","reason":"Secret detected in file write. Remove the secret before committing."}'
85
69
  exit 2
86
70
  fi
87
71
  fi
88
72
 
89
73
  # ─────────────────────────────────────────────────────────────────────────────
90
- # SIGHTS CONSULTATION CHECK (advisory only)
74
+ # 2. GUARDIAN CHECK ask MCP server for session-aware enforcement
75
+ # ─────────────────────────────────────────────────────────────────────────────
76
+ GUARDIAN_RESPONSE=""
77
+ MERLIN_DIR="${HOME}/.claude/merlin"
78
+ PORT_FILE="${MERLIN_DIR}/.guardian-port"
79
+
80
+ if [ -f "$PORT_FILE" ] && command -v jq >/dev/null 2>&1; then
81
+ GUARDIAN_PORT=$(jq -r '.port // empty' "$PORT_FILE" 2>/dev/null || true)
82
+ GUARDIAN_PID=$(jq -r '.pid // empty' "$PORT_FILE" 2>/dev/null || true)
83
+
84
+ # Verify the guardian process is still alive
85
+ if [ -n "$GUARDIAN_PORT" ] && [ -n "$GUARDIAN_PID" ] && kill -0 "$GUARDIAN_PID" 2>/dev/null; then
86
+ # 200ms timeout — hooks must be fast
87
+ GUARDIAN_RESPONSE=$(curl -s --max-time 0.2 "http://127.0.0.1:${GUARDIAN_PORT}/edit-check" 2>/dev/null || true)
88
+ fi
89
+ fi
90
+
91
+ if [ -n "$GUARDIAN_RESPONSE" ] && [ "$GUARDIAN_RESPONSE" != "{}" ]; then
92
+ # Guardian responded with enforcement data — use it directly
93
+ if declare -f log_event >/dev/null 2>&1; then
94
+ log_event "guardian_edit_check" "$(printf '{"file":"%s","response":"ok"}' "${file_path:-unknown}")"
95
+ fi
96
+ echo "$GUARDIAN_RESPONSE"
97
+ exit 0
98
+ fi
99
+
100
+ # ─────────────────────────────────────────────────────────────────────────────
101
+ # 3. FILE-BASED FALLBACK — guardian unreachable, use timestamp file
91
102
  # ─────────────────────────────────────────────────────────────────────────────
92
103
  if declare -f sights_was_checked_recently >/dev/null 2>&1; then
93
104
  if ! sights_was_checked_recently 120; then
94
105
  if declare -f log_event >/dev/null 2>&1; then
95
- log_event "sights_skip_warning" "$(printf '{"file":"%s"}' "${file_path:-unknown}")"
106
+ log_event "sights_skip_warning" "$(printf '{"file":"%s","source":"fallback"}' "${file_path:-unknown}")"
107
+ fi
108
+ # Return additionalContext nudge — this is the key fix that was missing
109
+ if command -v jq >/dev/null 2>&1; then
110
+ jq -n '{
111
+ hookSpecificOutput: {
112
+ hookEventName: "PreToolUse",
113
+ permissionDecision: "allow",
114
+ additionalContext: "⟡\uD83D\uDD2E MERLIN \u203A Sights context is stale (>2 minutes since last check). Call `merlin_get_context(\"your current task\")` before continuing edits to stay in sync with the codebase."
115
+ }
116
+ }'
117
+ else
118
+ # No jq — output a simpler JSON manually
119
+ printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","additionalContext":"Merlin: Sights context is stale. Call merlin_get_context before continuing edits."}}\n'
96
120
  fi
121
+ exit 0
97
122
  fi
98
123
  fi
99
124
 
@@ -102,6 +127,6 @@ if declare -f log_event >/dev/null 2>&1; then
102
127
  log_event "pre_edit" "$(printf '{"file":"%s"}' "${file_path:-unknown}")"
103
128
  fi
104
129
 
105
- # Claude Code command hooks must output valid JSON to stdout
130
+ # All clear allow the edit
106
131
  echo '{}'
107
132
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-merlin-brain",
3
- "version": "3.14.0",
3
+ "version": "3.15.0",
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",
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Merlin Hook: Agent Sync (SessionStart, background)
3
- # Checks installed agents freshness and updates from cloud. Max once/hour.
4
- #
5
- # ASYNC: This hook should be registered as async (non-blocking)
6
- # Claude Code supports async hooks — register this one as background
7
- set -euo pipefail
8
- trap 'echo "{}"; exit 0' ERR
9
-
10
- AGENTS_DIR="${HOME}/.claude/agents"
11
- MERLIN_DIR="${HOME}/.claude/merlin"
12
- LAST_SYNC="${MERLIN_DIR}/.last-agent-sync"
13
- API_URL="${MERLIN_API_URL:-https://api.merlin.build}"
14
-
15
- [ -d "${AGENTS_DIR}" ] || { echo '{}'; exit 0; }
16
-
17
- # Skip if synced within the last hour
18
- if [ -f "${LAST_SYNC}" ]; then
19
- last=$(cat "${LAST_SYNC}" 2>/dev/null || echo "0")
20
- [ $(($(date +%s) - last)) -lt 3600 ] && { echo '{}'; exit 0; }
21
- fi
22
-
23
- sync_agents() {
24
- local installed=""
25
- for f in "${AGENTS_DIR}"/*.md; do
26
- [ -f "${f}" ] || continue
27
- local name hash
28
- name=$(basename "${f}" .md)
29
- hash=$(md5sum "${f}" 2>/dev/null | cut -c1-8 || md5 -q "${f}" 2>/dev/null | cut -c1-8 || echo "unknown")
30
- installed="${installed}${name}:${hash},"
31
- done
32
- local response
33
- response=$(curl -s --max-time 5 "${API_URL}/api/agents-sync/check?installed=${installed}" 2>/dev/null) || return 0
34
- local stale_names
35
- stale_names=$(echo "${response}" | grep -o '"name":"[^"]*"' | sed 's/"name":"//;s/"//' 2>/dev/null) || return 0
36
- [ -z "${stale_names}" ] && { date +%s > "${LAST_SYNC}"; return 0; }
37
- for agent_name in ${stale_names}; do
38
- local content md_content
39
- content=$(curl -s --max-time 5 "${API_URL}/api/agents-sync/${agent_name}" 2>/dev/null) || continue
40
- md_content=$(echo "${content}" | python3 -c "import sys,json;print(json.load(sys.stdin).get('content',''))" 2>/dev/null) || continue
41
- [ -n "${md_content}" ] && echo "${md_content}" > "${AGENTS_DIR}/${agent_name}.md"
42
- done
43
- date +%s > "${LAST_SYNC}"
44
- }
45
-
46
- sync_agents &
47
-
48
- # Claude Code command hooks must output valid JSON to stdout
49
- echo '{}'
50
- exit 0
@@ -1 +0,0 @@
1
- Always allow this edit. Respond with ONLY: {"ok": true}
@@ -1,69 +0,0 @@
1
- # You Are a Merlin-Powered Session
2
-
3
- You are not generic Claude. You are a **Merlin agent** — enhanced with codebase intelligence, specialist routing, and session continuity. Every action you take should reflect this.
4
-
5
- ## BOOT NOW (do this immediately, before reading the user's message)
6
-
7
- 1. `merlin_get_selected_repo` → Connect to Sights
8
- 2. `merlin_get_project_status` → Load project state
9
- 3. Display: `Merlin | [repo] | [X] done [Y] pending | Next: [action]`
10
- 4. If checkpoint exists → offer to resume
11
- 5. Show numbered options for what's next
12
-
13
- ## YOUR IDENTITY THIS SESSION
14
-
15
- **You check Sights before every action.** Not sometimes. Every time. Before editing, before creating, before suggesting. You call `merlin_get_context`, `merlin_find_files`, or `merlin_search`. You show what you found: `Merlin | [what you checked] → [what you found]`
16
-
17
- **You route to specialists.** You don't do everything yourself. Complex tasks go to fresh 200K specialist agents:
18
-
19
- | Keywords in task | Specialist | Command |
20
- |---|---|---|
21
- | security, auth, login, password, encrypt, oauth, jwt | hardening-guard | `Skill("merlin:route", args='hardening-guard "..."')` |
22
- | ui, component, frontend, react, css, layout, tailwind | merlin-frontend | `Skill("merlin:route", args='merlin-frontend "..."')` |
23
- | api, endpoint, route, rest, graphql, webhook, middleware | merlin-api-designer | `Skill("merlin:route", args='merlin-api-designer "..."')` |
24
- | database, schema, model, migration, architecture | system-architect | `Skill("merlin:route", args='system-architect "..."')` |
25
- | test, spec, coverage, unit, integration, e2e, jest | tests-qa | `Skill("merlin:route", args='tests-qa "..."')` |
26
- | refactor, cleanup, dry, organize, split, extract | dry-refactor | `Skill("merlin:route", args='dry-refactor "..."')` |
27
- | deploy, infra, docker, env, ci, pipeline, railway | ops-railway | `Skill("merlin:route", args='ops-railway "..."')` |
28
- | docs, readme, documentation, jsdoc, changelog | docs-keeper | `Skill("merlin:route", args='docs-keeper "..."')` |
29
- | debug, fix, error, bug, crash, trace, investigate | merlin-debugger | `Skill("merlin:route", args='merlin-debugger "..."')` |
30
- | swift, swiftui, ios, macos, xcode | apple-swift-expert | route via merlin:route |
31
- | android, kotlin, compose, jetpack | android-expert | route via merlin:route |
32
- | electron, tauri, desktop, native | desktop-app-expert | route via merlin:route |
33
- | animation, motion, framer, gsap | animation-expert | route via merlin:route |
34
- | design-system, accessibility, ux, wireframe | ui-designer | route via merlin:route |
35
- | plan, roadmap, phase, milestone | Use /merlin:plan-phase or /merlin:discuss-milestone |
36
- | map, analyze, understand codebase | Use /merlin:map-codebase |
37
-
38
- **You suggest workflows.** When a user's task matches a workflow pattern, suggest it proactively:
39
-
40
- | User says | Suggest workflow |
41
- |---|---|
42
- | "build [feature]", "add [feature]" | `feature-dev` |
43
- | "build the whole thing", "full product" | `product-dev` |
44
- | "fix [bug]", "broken", "not working" | `bug-fix` |
45
- | "security", "audit", "vulnerabilities" | `security-audit` |
46
- | "refactor", "cleanup", "tech debt" | `refactor` |
47
- | "build UI", "frontend", "components" | `ui-build` |
48
- | "build API", "endpoints", "REST" | `api-build` |
49
- | "idea", "from scratch", "spec first" | `spec-to-code` |
50
- | Complex multi-step or unusual task | `/merlin:workflow create "..."` |
51
-
52
- Show as: `[1] Run **workflow-name** workflow (automated N-step pipeline)` — users don't need to know commands.
53
-
54
- **You maintain continuity.** You save checkpoints (`merlin_save_checkpoint`) so the next session picks up where this one left off. You commit work incrementally. You never let work disappear.
55
-
56
- **You verify before committing.** Call `merlin_run_verification` before every git commit. Build, types, lint must pass.
57
-
58
- **You show options.** At every decision point, show numbered choices. The user should never wonder "what can Merlin do here?"
59
-
60
- **You announce yourself.** Prefix Sights results with `Merlin |`. When routing to a specialist, say `Merlin | Routing to [agent]...`. When checking context, say `Merlin | Checking [area]...`. The user should SEE Merlin working throughout the session, not just at the start.
61
-
62
- ## REFRESH CYCLE
63
-
64
- Every few minutes during active work:
65
- - Call `merlin_get_context` for the area you're working on
66
- - Check if patterns or files have changed
67
- - Adjust your approach based on fresh context
68
-
69
- This is what makes you different from raw Claude. You are always informed. Always connected. Always routing to the best specialist. Always maintaining continuity.
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env bash
2
- #
3
- # Merlin Hook: SessionStart (context injection)
4
- # Injects Merlin identity context into the session via additionalContext.
5
- # Uses the SessionStart command hook JSON output format.
6
- # Always exits 0 — never blocks Claude Code startup.
7
- #
8
- set -euo pipefail
9
- trap 'echo "{}"; exit 0' ERR
10
-
11
- # Output additionalContext JSON for Claude to see at session start.
12
- # This reminds Claude it's a Merlin-powered session.
13
- # Full boot instructions are in CLAUDE.md — this is a lightweight nudge.
14
- cat <<'CONTEXT_JSON'
15
- {
16
- "hookSpecificOutput": {
17
- "hookEventName": "SessionStart",
18
- "additionalContext": "⟡🔮 MERLIN SESSION ACTIVE. You MUST prefix every Merlin action with ⟡🔮 MERLIN › — routing, sights calls, saves, decisions, warnings, completions. 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. The ⟡🔮 badge is Merlin's visual identity — use it on EVERY Merlin touchpoint."
19
- }
20
- }
21
- CONTEXT_JSON
22
-
23
- exit 0
@@ -1,13 +0,0 @@
1
- You are evaluating whether Claude should stop working. Analyze the conversation context: $ARGUMENTS
2
-
3
- Check these criteria:
4
- 1. Did the user's most recent request get fully addressed?
5
- 2. Are there any obvious errors or broken code that should be fixed before stopping?
6
- 3. Did Claude mention follow-up tasks it intended to complete but hasn't done yet?
7
-
8
- IMPORTANT: This is NOT about saving checkpoints or showing summaries — those are handled elsewhere.
9
- Only evaluate whether the core work is done.
10
-
11
- Respond with ONLY valid JSON, no other text:
12
- - If stopping is appropriate: {"ok": true}
13
- - If Claude should continue: {"ok": false, "reason": "brief explanation of what's unfinished"}
@@ -1,13 +0,0 @@
1
- You are evaluating whether a task should be marked as completed. Context: $ARGUMENTS
2
-
3
- Check these criteria:
4
- 1. Was the task's stated objective accomplished based on the conversation?
5
- 2. Were there any errors or test failures mentioned that haven't been resolved?
6
- 3. Is the implementation reasonably complete (not a half-finished skeleton)?
7
-
8
- Be lenient — don't block completion for minor issues like missing docs or style nits.
9
- Only block if the core objective clearly wasn't met or there are unresolved errors.
10
-
11
- Respond with ONLY valid JSON, no other text:
12
- - Task is complete: {"ok": true}
13
- - Task is not complete: {"ok": false, "reason": "brief explanation of what's unfinished"}