create-merlin-brain 3.5.1 → 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
|
|
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
|
-
|
|
943
|
-
|
|
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/
|
|
951
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-start.sh' }]
|
|
949
952
|
});
|
|
950
953
|
|
|
951
954
|
// PreToolUse hook (Edit/Write only)
|
|
@@ -980,54 +983,54 @@ async function install() {
|
|
|
980
983
|
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/subagent-context.sh' }]
|
|
981
984
|
});
|
|
982
985
|
|
|
983
|
-
// --- Prompt-based hooks
|
|
984
|
-
//
|
|
985
|
-
//
|
|
986
|
-
|
|
987
|
-
const removeMerlinPromptHooks = (hookArray) => {
|
|
986
|
+
// --- Prompt-based hooks cleanup & registration ---
|
|
987
|
+
// Remove ALL prompt-type hooks from Merlin (identified by type: 'prompt').
|
|
988
|
+
// This prevents duplicates across version upgrades and removes broken hooks.
|
|
989
|
+
const removeAllPromptHooks = (hookArray) => {
|
|
988
990
|
return hookArray.filter(entry => {
|
|
989
991
|
if (!entry.hooks || !Array.isArray(entry.hooks)) return true;
|
|
990
|
-
// Remove entry if ALL its hooks are Merlin
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
return prompt.includes('merlin') || prompt.includes('sights');
|
|
995
|
-
});
|
|
996
|
-
return !allMerlinPrompts;
|
|
992
|
+
// Remove entry if ALL its hooks are prompt-type (Merlin is the only
|
|
993
|
+
// source of prompt hooks in settings.local.json)
|
|
994
|
+
const allPrompts = entry.hooks.every(h => h.type === 'prompt');
|
|
995
|
+
return !allPrompts;
|
|
997
996
|
});
|
|
998
997
|
};
|
|
999
998
|
|
|
1000
|
-
settings.hooks.SessionStart =
|
|
1001
|
-
settings.hooks.PreToolUse =
|
|
1002
|
-
settings.hooks.Stop =
|
|
999
|
+
settings.hooks.SessionStart = removeAllPromptHooks(settings.hooks.SessionStart);
|
|
1000
|
+
settings.hooks.PreToolUse = removeAllPromptHooks(settings.hooks.PreToolUse);
|
|
1001
|
+
settings.hooks.Stop = removeAllPromptHooks(settings.hooks.Stop);
|
|
1003
1002
|
settings.hooks.Notification = settings.hooks.Notification || [];
|
|
1004
|
-
settings.hooks.Notification =
|
|
1003
|
+
settings.hooks.Notification = removeAllPromptHooks(settings.hooks.Notification);
|
|
1005
1004
|
settings.hooks.TaskCompleted = settings.hooks.TaskCompleted || [];
|
|
1006
|
-
settings.hooks.TaskCompleted =
|
|
1005
|
+
settings.hooks.TaskCompleted = removeAllPromptHooks(settings.hooks.TaskCompleted);
|
|
1007
1006
|
|
|
1008
|
-
//
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
//
|
|
1012
|
-
|
|
1013
|
-
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-start-context.sh' }]
|
|
1014
|
-
});
|
|
1007
|
+
// NOTE: The PreToolUse prompt hook has been REMOVED. It caused an infinite
|
|
1008
|
+
// rejection loop because the evaluator model cannot see conversation history
|
|
1009
|
+
// to verify whether merlin_get_context was called. The command-type hook
|
|
1010
|
+
// (pre-edit-sights-check.sh) handles advisory logging instead.
|
|
1011
|
+
// Sights enforcement is done via CLAUDE.md instructions, not hook blocking.
|
|
1015
1012
|
|
|
1016
|
-
//
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
//
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1031
1034
|
|
|
1032
1035
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
1033
1036
|
logSuccess('Configured hooks in settings.local.json');
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Check: Has the assistant recently called merlin_get_context, merlin_find_files, or merlin_search for this file's area in the current conversation? Look at recent tool calls in the transcript.
|
|
4
|
-
|
|
5
|
-
Rules:
|
|
6
|
-
- If Sights was consulted recently (within the last few messages) for this area: allow the edit
|
|
7
|
-
- If this is a minor/trivial edit (formatting, typo fix, small change): allow the edit
|
|
8
|
-
- If this is a significant edit and Sights was NOT consulted: suggest checking first
|
|
9
|
-
|
|
10
|
-
Respond with ONLY valid JSON, no other text:
|
|
11
|
-
- Allow the edit: {"ok": true}
|
|
12
|
-
- Suggest Sights check first: {"ok": false, "reason": "Call merlin_get_context before editing this file to check for existing patterns and conventions"}
|
|
1
|
+
Always allow this edit. Respond with ONLY: {"ok": true}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
# Merlin Hook: SessionStart
|
|
4
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
32
|
-
|
|
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