create-merlin-brain 3.13.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.
Files changed (36) hide show
  1. package/dist/server/server.d.ts.map +1 -1
  2. package/dist/server/server.js +18 -8
  3. package/dist/server/server.js.map +1 -1
  4. package/dist/server/session-coach.d.ts +21 -16
  5. package/dist/server/session-coach.d.ts.map +1 -1
  6. package/dist/server/session-coach.js +89 -64
  7. package/dist/server/session-coach.js.map +1 -1
  8. package/dist/server/session-guardian.d.ts +28 -0
  9. package/dist/server/session-guardian.d.ts.map +1 -0
  10. package/dist/server/session-guardian.js +168 -0
  11. package/dist/server/session-guardian.js.map +1 -0
  12. package/dist/server/stats.js +7 -7
  13. package/dist/server/stats.js.map +1 -1
  14. package/dist/server/tools/context.d.ts.map +1 -1
  15. package/dist/server/tools/context.js +24 -0
  16. package/dist/server/tools/context.js.map +1 -1
  17. package/dist/server/tools/project.js +1 -1
  18. package/dist/server/tools/project.js.map +1 -1
  19. package/files/CLAUDE.md +66 -5
  20. package/files/agents/merlin.md +21 -13
  21. package/files/hooks/config-change.sh +2 -2
  22. package/files/hooks/notify-desktop.sh +2 -2
  23. package/files/hooks/notify-webhook.sh +1 -1
  24. package/files/hooks/post-edit-logger.sh +23 -1
  25. package/files/hooks/pre-compact.sh +52 -5
  26. package/files/hooks/pre-edit-sights-check.sh +56 -31
  27. package/files/hooks/task-completed-verify.sh +2 -2
  28. package/files/hooks/worktree-create.sh +1 -1
  29. package/files/hooks/worktree-remove.sh +1 -1
  30. package/package.json +1 -1
  31. package/files/hooks/agent-sync.sh +0 -50
  32. package/files/hooks/pre-edit-sights-enforce.md +0 -1
  33. package/files/hooks/session-start-boot.md +0 -69
  34. package/files/hooks/session-start-context.sh +0 -23
  35. package/files/hooks/stop-check.md +0 -13
  36. package/files/hooks/task-completed-verify.md +0 -13
@@ -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
@@ -24,7 +24,7 @@ if [ -f "package.json" ] && command -v jq >/dev/null 2>&1; then
24
24
  build_exit=$?
25
25
  if [ "$build_exit" -ne 0 ]; then
26
26
  log_event "build_failed" "$(printf '{"exit_code":%d}' "$build_exit")"
27
- echo "Merlin: Build check failed (exit $build_exit)" >&2
27
+ echo "⟡🔮 MERLIN › Build check failed (exit $build_exit)" >&2
28
28
  else
29
29
  log_event "build_passed" '{}'
30
30
  fi
@@ -37,7 +37,7 @@ if [ -f "tsconfig.json" ] && command -v npx >/dev/null 2>&1; then
37
37
  tsc_exit=$?
38
38
  if [ "$tsc_exit" -ne 0 ]; then
39
39
  log_event "typecheck_failed" "$(printf '{"exit_code":%d}' "$tsc_exit")"
40
- echo "Merlin: Type check failed (exit $tsc_exit)" >&2
40
+ echo "⟡🔮 MERLIN › Type check failed (exit $tsc_exit)" >&2
41
41
  else
42
42
  log_event "typecheck_passed" '{}'
43
43
  fi
@@ -55,7 +55,7 @@ if declare -f log_event >/dev/null 2>&1; then
55
55
  "$WORKTREE_PATH" "$AGENT_ID" "$AGENT_TYPE")"
56
56
  fi
57
57
 
58
- echo "Merlin: propagated config to worktree ${WORKTREE_PATH} (agent: ${AGENT_TYPE})" >&2
58
+ echo "⟡🔮 MERLIN › propagated config to worktree ${WORKTREE_PATH} (agent: ${AGENT_TYPE})" >&2
59
59
 
60
60
  echo '{}'
61
61
  exit 0
@@ -48,7 +48,7 @@ fi
48
48
 
49
49
  LIFETIME_MSG=""
50
50
  [ -n "$LIFETIME_S" ] && LIFETIME_MSG=" (lifetime: ${LIFETIME_S}s)"
51
- echo "Merlin: cleaned up worktree ${WORKTREE_PATH}${LIFETIME_MSG} (agent: ${AGENT_TYPE})" >&2
51
+ echo "⟡🔮 MERLIN › cleaned up worktree ${WORKTREE_PATH}${LIFETIME_MSG} (agent: ${AGENT_TYPE})" >&2
52
52
 
53
53
  echo '{}'
54
54
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-merlin-brain",
3
- "version": "3.13.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": "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."
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"}