create-merlin-brain 3.11.0 → 3.13.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.
- package/bin/install.cjs +156 -32
- package/bin/runtime-adapters.cjs +396 -0
- package/dist/server/api/types.d.ts +7 -0
- package/dist/server/api/types.d.ts.map +1 -1
- package/dist/server/cost/tracker.d.ts +38 -2
- package/dist/server/cost/tracker.d.ts.map +1 -1
- package/dist/server/cost/tracker.js +87 -15
- package/dist/server/cost/tracker.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +74 -30
- package/dist/server/server.js.map +1 -1
- package/dist/server/tools/__tests__/augmentation.test.d.ts +8 -0
- package/dist/server/tools/__tests__/augmentation.test.d.ts.map +1 -0
- package/dist/server/tools/__tests__/augmentation.test.js +76 -0
- package/dist/server/tools/__tests__/augmentation.test.js.map +1 -0
- package/dist/server/tools/__tests__/route-helpers.test.d.ts +5 -0
- package/dist/server/tools/__tests__/route-helpers.test.d.ts.map +1 -0
- package/dist/server/tools/__tests__/route-helpers.test.js +49 -0
- package/dist/server/tools/__tests__/route-helpers.test.js.map +1 -0
- package/dist/server/tools/adaptive.js +1 -1
- package/dist/server/tools/adaptive.js.map +1 -1
- package/dist/server/tools/agent-spawn.d.ts +25 -0
- package/dist/server/tools/agent-spawn.d.ts.map +1 -0
- package/dist/server/tools/agent-spawn.js +95 -0
- package/dist/server/tools/agent-spawn.js.map +1 -0
- package/dist/server/tools/agents-index.js +3 -3
- package/dist/server/tools/agents-index.js.map +1 -1
- package/dist/server/tools/agents.js +5 -5
- package/dist/server/tools/agents.js.map +1 -1
- package/dist/server/tools/augmentation.d.ts +45 -0
- package/dist/server/tools/augmentation.d.ts.map +1 -0
- package/dist/server/tools/augmentation.js +167 -0
- package/dist/server/tools/augmentation.js.map +1 -0
- package/dist/server/tools/behaviors.js +4 -4
- package/dist/server/tools/behaviors.js.map +1 -1
- package/dist/server/tools/context.js +7 -7
- package/dist/server/tools/context.js.map +1 -1
- package/dist/server/tools/cost.d.ts +3 -1
- package/dist/server/tools/cost.d.ts.map +1 -1
- package/dist/server/tools/cost.js +66 -13
- package/dist/server/tools/cost.js.map +1 -1
- package/dist/server/tools/discoveries.js +6 -6
- package/dist/server/tools/discoveries.js.map +1 -1
- package/dist/server/tools/index.d.ts +4 -0
- package/dist/server/tools/index.d.ts.map +1 -1
- package/dist/server/tools/index.js +4 -0
- package/dist/server/tools/index.js.map +1 -1
- package/dist/server/tools/learning.d.ts +12 -0
- package/dist/server/tools/learning.d.ts.map +1 -0
- package/dist/server/tools/learning.js +269 -0
- package/dist/server/tools/learning.js.map +1 -0
- package/dist/server/tools/project.js +7 -7
- package/dist/server/tools/project.js.map +1 -1
- package/dist/server/tools/promote.d.ts +11 -0
- package/dist/server/tools/promote.d.ts.map +1 -0
- package/dist/server/tools/promote.js +315 -0
- package/dist/server/tools/promote.js.map +1 -0
- package/dist/server/tools/route-helpers.d.ts +45 -0
- package/dist/server/tools/route-helpers.d.ts.map +1 -0
- package/dist/server/tools/route-helpers.js +93 -0
- package/dist/server/tools/route-helpers.js.map +1 -0
- package/dist/server/tools/route.d.ts +4 -3
- package/dist/server/tools/route.d.ts.map +1 -1
- package/dist/server/tools/route.js +80 -284
- package/dist/server/tools/route.js.map +1 -1
- package/dist/server/tools/session-restore.d.ts +18 -0
- package/dist/server/tools/session-restore.d.ts.map +1 -0
- package/dist/server/tools/session-restore.js +154 -0
- package/dist/server/tools/session-restore.js.map +1 -0
- package/dist/server/tools/session-search.d.ts +16 -0
- package/dist/server/tools/session-search.d.ts.map +1 -0
- package/dist/server/tools/session-search.js +240 -0
- package/dist/server/tools/session-search.js.map +1 -0
- package/dist/server/tools/sights-index.js +2 -2
- package/dist/server/tools/sights-index.js.map +1 -1
- package/dist/server/tools/smart-route.d.ts.map +1 -1
- package/dist/server/tools/smart-route.js +4 -5
- package/dist/server/tools/smart-route.js.map +1 -1
- package/dist/server/tools/verification.js +1 -1
- package/dist/server/tools/verification.js.map +1 -1
- package/files/agents/code-organization-supervisor.md +1 -0
- package/files/agents/context-guardian.md +1 -0
- package/files/agents/docs-keeper.md +1 -0
- package/files/agents/dry-refactor.md +1 -0
- package/files/agents/elite-code-refactorer.md +1 -0
- package/files/agents/hardening-guard.md +1 -0
- package/files/agents/implementation-dev.md +1 -0
- package/files/agents/merlin-access-control-reviewer.md +248 -0
- package/files/agents/merlin-codebase-mapper.md +1 -1
- package/files/agents/merlin-dependency-auditor.md +216 -0
- package/files/agents/merlin-executor.md +1 -0
- package/files/agents/merlin-input-validator.md +247 -0
- package/files/agents/merlin-reviewer.md +1 -0
- package/files/agents/merlin-sast-reviewer.md +182 -0
- package/files/agents/merlin-secret-scanner.md +203 -0
- package/files/agents/tests-qa.md +1 -0
- package/files/commands/merlin/execute-phase.md +94 -197
- package/files/commands/merlin/execute-plan.md +116 -180
- package/files/commands/merlin/health.md +385 -0
- package/files/commands/merlin/loop-recipes.md +93 -36
- package/files/commands/merlin/optimize-prompts.md +158 -0
- package/files/commands/merlin/profiles.md +215 -0
- package/files/commands/merlin/promote.md +176 -0
- package/files/commands/merlin/quick.md +229 -0
- package/files/commands/merlin/resume-work.md +27 -1
- package/files/commands/merlin/route.md +43 -1
- package/files/commands/merlin/sandbox.md +359 -0
- package/files/commands/merlin/usage.md +55 -0
- package/files/docker/Dockerfile.merlin +20 -0
- package/files/docker/docker-compose.merlin.yml +23 -0
- package/files/hook-templates/auto-commit.sh +64 -0
- package/files/hook-templates/auto-format.sh +95 -0
- package/files/hook-templates/auto-test.sh +117 -0
- package/files/hook-templates/branch-protection.sh +72 -0
- package/files/hook-templates/changelog-reminder.sh +76 -0
- package/files/hook-templates/complexity-check.sh +112 -0
- package/files/hook-templates/import-audit.sh +83 -0
- package/files/hook-templates/license-header.sh +84 -0
- package/files/hook-templates/pr-description.sh +100 -0
- package/files/hook-templates/todo-tracker.sh +80 -0
- package/files/hooks/check-file-size.sh +17 -4
- package/files/hooks/config-change.sh +44 -16
- package/files/hooks/instructions-loaded.sh +22 -5
- package/files/hooks/notify-desktop.sh +157 -0
- package/files/hooks/notify-webhook.sh +141 -0
- package/files/hooks/pre-edit-sights-check.sh +76 -9
- package/files/hooks/security-scanner.sh +153 -0
- package/files/hooks/session-end-memory-sync.sh +97 -0
- package/files/hooks/session-end.sh +274 -1
- package/files/hooks/session-start.sh +19 -6
- package/files/hooks/smart-approve.sh +270 -0
- package/files/hooks/teammate-idle-verify.sh +87 -12
- package/files/hooks/worktree-create.sh +20 -3
- package/files/hooks/worktree-remove.sh +21 -3
- package/files/merlin/references/plan-format.md +37 -9
- package/files/merlin/sandbox.json +9 -0
- package/files/merlin/security.json +11 -0
- package/files/merlin/templates/ci/docs-update.yml +81 -0
- package/files/merlin/templates/ci/pr-review.yml +50 -0
- package/files/merlin/templates/ci/security-audit.yml +74 -0
- package/files/merlin/templates/config.json +9 -1
- package/files/rules/api-rules.md +30 -0
- package/files/rules/frontend-rules.md +25 -0
- package/files/rules/hooks-rules.md +36 -0
- package/files/rules/mcp-rules.md +30 -0
- package/files/rules/worker-rules.md +29 -0
- package/package.json +5 -2
package/bin/install.cjs
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
const isServeMode = process.argv.includes('serve');
|
|
12
12
|
const isInjectMode = process.argv.includes('inject-headers');
|
|
13
13
|
|
|
14
|
+
// --runtime flag: 'all' | 'claude' | 'codex' | 'opencode' | 'gemini'
|
|
15
|
+
// Default: 'all' (configure all detected runtimes)
|
|
16
|
+
const runtimeFlagIdx = process.argv.indexOf('--runtime');
|
|
17
|
+
const RUNTIME_FLAG = runtimeFlagIdx !== -1 ? (process.argv[runtimeFlagIdx + 1] || 'all') : 'all';
|
|
18
|
+
|
|
14
19
|
// =============================================================================
|
|
15
20
|
// MODE: inject-headers - Add Merlin reminder to CLAUDE.md files
|
|
16
21
|
// =============================================================================
|
|
@@ -101,12 +106,14 @@ const fs = require('fs');
|
|
|
101
106
|
const path = require('path');
|
|
102
107
|
const os = require('os');
|
|
103
108
|
const readline = require('readline');
|
|
109
|
+
const { detectRuntimes, configureRuntimes } = require('./runtime-adapters.cjs');
|
|
104
110
|
|
|
105
111
|
const CLAUDE_DIR = path.join(os.homedir(), '.claude');
|
|
106
112
|
const MERLIN_DIR = path.join(CLAUDE_DIR, 'merlin');
|
|
107
113
|
const AGENTS_DIR = path.join(CLAUDE_DIR, 'agents');
|
|
108
114
|
const COMMANDS_DIR = path.join(CLAUDE_DIR, 'commands', 'merlin');
|
|
109
115
|
const LOOP_DIR = path.join(CLAUDE_DIR, 'loop');
|
|
116
|
+
const RULES_DIR = path.join(CLAUDE_DIR, 'rules');
|
|
110
117
|
|
|
111
118
|
const colors = {
|
|
112
119
|
reset: '\x1b[0m',
|
|
@@ -138,7 +145,7 @@ function logError(msg) {
|
|
|
138
145
|
log(` ${colors.red}✗${colors.reset} ${msg}`);
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
// Minimum Claude Code version required (
|
|
148
|
+
// Minimum Claude Code version required (MCP server support)
|
|
142
149
|
const MIN_CLAUDE_VERSION = '2.0.0';
|
|
143
150
|
|
|
144
151
|
function parseVersion(v) {
|
|
@@ -192,7 +199,7 @@ function ensureClaudeCode() {
|
|
|
192
199
|
}
|
|
193
200
|
|
|
194
201
|
// Needs update
|
|
195
|
-
logWarn(`Claude Code v${currentVersion} is too old (need >= ${MIN_CLAUDE_VERSION}
|
|
202
|
+
logWarn(`Claude Code v${currentVersion} is too old (need >= ${MIN_CLAUDE_VERSION})`);
|
|
196
203
|
log(` ${colors.cyan}Updating Claude Code...${colors.reset}`);
|
|
197
204
|
|
|
198
205
|
try {
|
|
@@ -333,23 +340,17 @@ function setupShellIntegration() {
|
|
|
333
340
|
} else {
|
|
334
341
|
logWarn(`Unknown shell (${shell}), skipping shell setup`);
|
|
335
342
|
log(` Add manually to your shell rc file:`);
|
|
336
|
-
log(` ${colors.cyan}function claude() { command claude --agent merlin "$@"; }${colors.reset}`);
|
|
337
343
|
log(` ${colors.cyan}alias cc='claude'${colors.reset}`);
|
|
338
344
|
return false;
|
|
339
345
|
}
|
|
340
346
|
|
|
341
|
-
//
|
|
347
|
+
// Merlin shell integration - shortcut alias and loop command
|
|
342
348
|
const merlinBlock = `
|
|
343
349
|
# ══════════════════════════════════════════════════════════════
|
|
344
350
|
# Merlin Brain - The AI Brain for Claude Code
|
|
345
351
|
# https://merlin.build
|
|
346
352
|
# ══════════════════════════════════════════════════════════════
|
|
347
353
|
|
|
348
|
-
# Simple claude wrapper - run with merlin agent
|
|
349
|
-
claude() {
|
|
350
|
-
command claude --agent merlin "$@"
|
|
351
|
-
}
|
|
352
|
-
|
|
353
354
|
# Shortcut alias
|
|
354
355
|
alias cc="claude"
|
|
355
356
|
|
|
@@ -380,11 +381,17 @@ merlin-loop() {
|
|
|
380
381
|
fs.writeFileSync(rcFile, rcContent);
|
|
381
382
|
logSuccess('Updating existing Merlin shell integration');
|
|
382
383
|
}
|
|
384
|
+
|
|
385
|
+
// Remove broken claude() wrapper from older installs (--agent flag doesn't exist)
|
|
386
|
+
if (rcContent.includes('command claude --agent')) {
|
|
387
|
+
rcContent = rcContent.replace(/\n?# Simple claude wrapper.*\nclaude\(\) \{\n\s*command claude --agent.*\n\}\n?/g, '\n');
|
|
388
|
+
fs.writeFileSync(rcFile, rcContent);
|
|
389
|
+
logSuccess('Removed broken claude() wrapper (--agent flag is not valid)');
|
|
390
|
+
}
|
|
383
391
|
}
|
|
384
392
|
|
|
385
393
|
// Add new Merlin integration block
|
|
386
394
|
fs.appendFileSync(rcFile, merlinBlock);
|
|
387
|
-
logSuccess(`Configured 'claude' command to use Merlin`);
|
|
388
395
|
logSuccess(`Added 'cc' shortcut`);
|
|
389
396
|
logSuccess(`Version check enabled (run /merlin:update to update)`);
|
|
390
397
|
return true;
|
|
@@ -838,7 +845,7 @@ async function install() {
|
|
|
838
845
|
}
|
|
839
846
|
|
|
840
847
|
// Step 0: Clean up legacy GSD/ccwiki artifacts
|
|
841
|
-
logStep('0/
|
|
848
|
+
logStep('0/13', 'Cleaning up legacy installations...');
|
|
842
849
|
const cleaned = cleanupLegacy();
|
|
843
850
|
if (cleaned.length > 0) {
|
|
844
851
|
for (const item of cleaned) {
|
|
@@ -849,11 +856,24 @@ async function install() {
|
|
|
849
856
|
}
|
|
850
857
|
|
|
851
858
|
// Step 1: Ensure Claude Code is installed and up to date
|
|
852
|
-
logStep('1/
|
|
859
|
+
logStep('1/13', 'Checking Claude Code...');
|
|
853
860
|
const claudeCheck = ensureClaudeCode();
|
|
854
861
|
|
|
855
|
-
// Step 2:
|
|
856
|
-
logStep('2/
|
|
862
|
+
// Step 2: Detect runtimes
|
|
863
|
+
logStep('2/13', 'Detecting runtimes...');
|
|
864
|
+
const detectedRuntimes = detectRuntimes();
|
|
865
|
+
log(` ${colors.green}✅${colors.reset} Claude Code (primary)`);
|
|
866
|
+
for (const rt of detectedRuntimes) {
|
|
867
|
+
const icon = rt.found ? `${colors.green}✅${colors.reset}` : `${colors.yellow}⬚${colors.reset}`;
|
|
868
|
+
const suffix = rt.found ? '' : ' (not found)';
|
|
869
|
+
log(` ${icon} ${rt.label}${suffix}`);
|
|
870
|
+
}
|
|
871
|
+
if (RUNTIME_FLAG === 'claude') {
|
|
872
|
+
log(` ${colors.yellow}--runtime claude:${colors.reset} other runtimes will not be configured`);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Step 3: Install globally for instant startup across all terminals
|
|
876
|
+
logStep('3/13', 'Installing globally (fast startup for all terminals)...');
|
|
857
877
|
try {
|
|
858
878
|
const { execSync } = require('child_process');
|
|
859
879
|
// Check if already installed globally and up-to-date
|
|
@@ -893,16 +913,16 @@ async function install() {
|
|
|
893
913
|
useGlobalBinary = false;
|
|
894
914
|
}
|
|
895
915
|
|
|
896
|
-
// Step
|
|
897
|
-
logStep('
|
|
916
|
+
// Step 4: Create directories
|
|
917
|
+
logStep('4/13', 'Creating directories...');
|
|
898
918
|
ensureDir(CLAUDE_DIR);
|
|
899
919
|
ensureDir(MERLIN_DIR);
|
|
900
920
|
ensureDir(AGENTS_DIR);
|
|
901
921
|
ensureDir(COMMANDS_DIR);
|
|
902
922
|
logSuccess('Directories created');
|
|
903
923
|
|
|
904
|
-
// Step
|
|
905
|
-
logStep('
|
|
924
|
+
// Step 5: Install Merlin core (workflows, references, templates)
|
|
925
|
+
logStep('5/13', 'Installing Merlin workflows...');
|
|
906
926
|
const merlinSrc = path.join(filesDir, 'merlin');
|
|
907
927
|
if (fs.existsSync(merlinSrc)) {
|
|
908
928
|
const count = copyDirRecursive(merlinSrc, MERLIN_DIR);
|
|
@@ -914,8 +934,8 @@ async function install() {
|
|
|
914
934
|
logWarn('Merlin workflows not found in package');
|
|
915
935
|
}
|
|
916
936
|
|
|
917
|
-
// Step
|
|
918
|
-
logStep('
|
|
937
|
+
// Step 6: Install agents (tiered)
|
|
938
|
+
logStep('6/13', 'Installing Merlin agents...');
|
|
919
939
|
const agentsSrc = path.join(filesDir, 'agents');
|
|
920
940
|
if (fs.existsSync(agentsSrc)) {
|
|
921
941
|
// Load agent manifest for tiered display
|
|
@@ -942,8 +962,32 @@ async function install() {
|
|
|
942
962
|
logWarn('Agents not found in package');
|
|
943
963
|
}
|
|
944
964
|
|
|
945
|
-
// Step
|
|
946
|
-
logStep('
|
|
965
|
+
// Step 7: Install path-scoped rules
|
|
966
|
+
logStep('7/13', 'Installing path-scoped rules...');
|
|
967
|
+
const rulesSrc = path.join(filesDir, 'rules');
|
|
968
|
+
if (fs.existsSync(rulesSrc)) {
|
|
969
|
+
ensureDir(RULES_DIR);
|
|
970
|
+
const ruleFiles = fs.readdirSync(rulesSrc).filter(f => f.endsWith('.md'));
|
|
971
|
+
let installedCount = 0;
|
|
972
|
+
let skippedCount = 0;
|
|
973
|
+
for (const ruleFile of ruleFiles) {
|
|
974
|
+
const destPath = path.join(RULES_DIR, ruleFile);
|
|
975
|
+
// Never overwrite existing user rules — they may have been customized
|
|
976
|
+
if (fs.existsSync(destPath)) {
|
|
977
|
+
skippedCount++;
|
|
978
|
+
} else {
|
|
979
|
+
fs.copyFileSync(path.join(rulesSrc, ruleFile), destPath);
|
|
980
|
+
installedCount++;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (installedCount > 0) logSuccess(`Installed ${installedCount} path-scoped rule files`);
|
|
984
|
+
if (skippedCount > 0) logSuccess(`Skipped ${skippedCount} existing rule files (user customizations preserved)`);
|
|
985
|
+
} else {
|
|
986
|
+
logWarn('Rules not found in package');
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// Step 8: Install commands
|
|
990
|
+
logStep('8/13', 'Installing /merlin:* commands...');
|
|
947
991
|
const commandsSrc = path.join(filesDir, 'commands', 'merlin');
|
|
948
992
|
if (fs.existsSync(commandsSrc)) {
|
|
949
993
|
const count = copyDirRecursive(commandsSrc, COMMANDS_DIR);
|
|
@@ -952,8 +996,8 @@ async function install() {
|
|
|
952
996
|
logWarn('Commands not found in package');
|
|
953
997
|
}
|
|
954
998
|
|
|
955
|
-
// Step
|
|
956
|
-
logStep('
|
|
999
|
+
// Step 9: Install CLAUDE.md
|
|
1000
|
+
logStep('9/13', 'Configuring Claude Code...');
|
|
957
1001
|
const claudeMdSrc = path.join(filesDir, 'CLAUDE.md');
|
|
958
1002
|
const claudeMdDest = path.join(CLAUDE_DIR, 'CLAUDE.md');
|
|
959
1003
|
|
|
@@ -964,12 +1008,12 @@ async function install() {
|
|
|
964
1008
|
logSuccess('Installed CLAUDE.md (Merlin instructions)');
|
|
965
1009
|
}
|
|
966
1010
|
|
|
967
|
-
// Step
|
|
1011
|
+
// Step 10: Install Merlin Loop (legacy — kept for backward compatibility)
|
|
968
1012
|
// NOTE: Claude Code now has a native /loop command that replaces these scripts.
|
|
969
1013
|
// Use /merlin:loop-recipes in Claude Code for pre-built loop patterns.
|
|
970
1014
|
// These scripts are still copied so existing users and terminal workflows
|
|
971
1015
|
// (merlin-loop, merlin session) continue to work without interruption.
|
|
972
|
-
logStep('
|
|
1016
|
+
logStep('10/13', 'Installing Merlin Loop (legacy scripts)...');
|
|
973
1017
|
const loopSrc = path.join(filesDir, 'loop');
|
|
974
1018
|
if (fs.existsSync(loopSrc)) {
|
|
975
1019
|
ensureDir(LOOP_DIR);
|
|
@@ -996,12 +1040,13 @@ async function install() {
|
|
|
996
1040
|
logSuccess(`Installed ${count} loop files`);
|
|
997
1041
|
logSuccess(`Added 'merlin-loop' command`);
|
|
998
1042
|
logSuccess(`Added 'merlin session' command (interactive orchestrator)`);
|
|
1043
|
+
logWarn(`Note: Custom loop scripts are deprecated. Use /loop with /merlin:loop-recipes instead.`);
|
|
999
1044
|
} else {
|
|
1000
1045
|
logWarn('Merlin Loop not found in package');
|
|
1001
1046
|
}
|
|
1002
1047
|
|
|
1003
|
-
// Step
|
|
1004
|
-
logStep('
|
|
1048
|
+
// Step 11: Install Claude Code hooks
|
|
1049
|
+
logStep('11/13', 'Installing Claude Code hooks...');
|
|
1005
1050
|
const HOOKS_DIR = path.join(CLAUDE_DIR, 'hooks');
|
|
1006
1051
|
const hooksSrc = path.join(filesDir, 'hooks');
|
|
1007
1052
|
if (fs.existsSync(hooksSrc)) {
|
|
@@ -1100,6 +1145,27 @@ async function install() {
|
|
|
1100
1145
|
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-end.sh' }]
|
|
1101
1146
|
});
|
|
1102
1147
|
|
|
1148
|
+
// Stop hook: desktop notification (fires after session-end analytics)
|
|
1149
|
+
addHookIfMissing(settings.hooks.Stop, {
|
|
1150
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/notify-desktop.sh' }]
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
// Stop hook: webhook notification (Slack / Discord, fire-and-forget)
|
|
1154
|
+
addHookIfMissing(settings.hooks.Stop, {
|
|
1155
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/notify-webhook.sh' }]
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
// Stop hook: auto-memory sync (extracts session decisions → Sights cloud)
|
|
1159
|
+
addHookIfMissing(settings.hooks.Stop, {
|
|
1160
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/session-end-memory-sync.sh' }]
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
// Notification hook: desktop alert when Claude needs input
|
|
1164
|
+
settings.hooks.Notification = settings.hooks.Notification || [];
|
|
1165
|
+
addHookIfMissing(settings.hooks.Notification, {
|
|
1166
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/notify-desktop.sh' }]
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1103
1169
|
// TeammateIdle hook (Agent Teams quality gate — only fires when Teams active)
|
|
1104
1170
|
settings.hooks.TeammateIdle = settings.hooks.TeammateIdle || [];
|
|
1105
1171
|
addHookIfMissing(settings.hooks.TeammateIdle, {
|
|
@@ -1124,6 +1190,48 @@ async function install() {
|
|
|
1124
1190
|
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/task-completed-verify.sh' }]
|
|
1125
1191
|
});
|
|
1126
1192
|
|
|
1193
|
+
// InstructionsLoaded hook (cold-start Sights pre-warm)
|
|
1194
|
+
settings.hooks.InstructionsLoaded = settings.hooks.InstructionsLoaded || [];
|
|
1195
|
+
addHookIfMissing(settings.hooks.InstructionsLoaded, {
|
|
1196
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/instructions-loaded.sh' }]
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
// WorktreeCreate hook (propagate Merlin config into new worktrees)
|
|
1200
|
+
settings.hooks.WorktreeCreate = settings.hooks.WorktreeCreate || [];
|
|
1201
|
+
addHookIfMissing(settings.hooks.WorktreeCreate, {
|
|
1202
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/worktree-create.sh' }]
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
// WorktreeRemove hook (cleanup + lifecycle analytics)
|
|
1206
|
+
settings.hooks.WorktreeRemove = settings.hooks.WorktreeRemove || [];
|
|
1207
|
+
addHookIfMissing(settings.hooks.WorktreeRemove, {
|
|
1208
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/worktree-remove.sh' }]
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
// ConfigChange hook (validate API key + audit trail)
|
|
1212
|
+
settings.hooks.ConfigChange = settings.hooks.ConfigChange || [];
|
|
1213
|
+
addHookIfMissing(settings.hooks.ConfigChange, {
|
|
1214
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/config-change.sh' }]
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
// PostToolUse: file size enforcement (implementation agents only)
|
|
1218
|
+
addHookIfMissing(settings.hooks.PostToolUse, {
|
|
1219
|
+
matcher: 'Edit|Write',
|
|
1220
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/check-file-size.sh' }]
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
// PreToolUse: security scanner (prompt injection, secrets, data exfiltration)
|
|
1224
|
+
addHookIfMissing(settings.hooks.PreToolUse, {
|
|
1225
|
+
matcher: 'Write|Edit|Bash|NotebookEdit',
|
|
1226
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/security-scanner.sh' }]
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
// PreToolUse: smart bash auto-approval (read-only safe, dangerous blocked, unknown pass-through)
|
|
1230
|
+
addHookIfMissing(settings.hooks.PreToolUse, {
|
|
1231
|
+
matcher: 'Bash',
|
|
1232
|
+
hooks: [{ type: 'command', command: 'bash ~/.claude/hooks/smart-approve.sh' }]
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1127
1235
|
// --- Prompt-based hooks cleanup & registration ---
|
|
1128
1236
|
// Remove ALL prompt-type hooks from Merlin (identified by type: 'prompt').
|
|
1129
1237
|
// This prevents duplicates across version upgrades and removes broken hooks.
|
|
@@ -1144,6 +1252,10 @@ async function install() {
|
|
|
1144
1252
|
settings.hooks.Notification = removeAllPromptHooks(settings.hooks.Notification);
|
|
1145
1253
|
settings.hooks.TaskCompleted = settings.hooks.TaskCompleted || [];
|
|
1146
1254
|
settings.hooks.TaskCompleted = removeAllPromptHooks(settings.hooks.TaskCompleted);
|
|
1255
|
+
settings.hooks.InstructionsLoaded = removeAllPromptHooks(settings.hooks.InstructionsLoaded || []);
|
|
1256
|
+
settings.hooks.WorktreeCreate = removeAllPromptHooks(settings.hooks.WorktreeCreate || []);
|
|
1257
|
+
settings.hooks.WorktreeRemove = removeAllPromptHooks(settings.hooks.WorktreeRemove || []);
|
|
1258
|
+
settings.hooks.ConfigChange = removeAllPromptHooks(settings.hooks.ConfigChange || []);
|
|
1147
1259
|
|
|
1148
1260
|
// NOTE: The PreToolUse prompt hook has been REMOVED. It caused an infinite
|
|
1149
1261
|
// rejection loop because the evaluator model cannot see conversation history
|
|
@@ -1189,8 +1301,8 @@ async function install() {
|
|
|
1189
1301
|
return cfg;
|
|
1190
1302
|
}
|
|
1191
1303
|
|
|
1192
|
-
// Step
|
|
1193
|
-
logStep('
|
|
1304
|
+
// Step 12: Optional Merlin Sights configuration
|
|
1305
|
+
logStep('12/13', 'Merlin Sights configuration...');
|
|
1194
1306
|
|
|
1195
1307
|
// Check if API key is already configured (skip prompt on updates)
|
|
1196
1308
|
let existingApiKey = '';
|
|
@@ -1321,8 +1433,20 @@ async function install() {
|
|
|
1321
1433
|
logWarn('Skipped Merlin Sights (you can configure it later)');
|
|
1322
1434
|
}
|
|
1323
1435
|
|
|
1324
|
-
// Step
|
|
1325
|
-
logStep('
|
|
1436
|
+
// Step 13: Configure non-Claude-Code runtimes
|
|
1437
|
+
logStep('13/13', 'Configuring additional runtimes...');
|
|
1438
|
+
configureRuntimes({
|
|
1439
|
+
runtimeFlag: RUNTIME_FLAG,
|
|
1440
|
+
useGlobalBinary,
|
|
1441
|
+
apiKey,
|
|
1442
|
+
logSuccess,
|
|
1443
|
+
logWarn,
|
|
1444
|
+
logInfo: (msg) => log(msg),
|
|
1445
|
+
quiet: true, // step 2 already printed the detection summary
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
// Shell integration (runs after runtime adapters)
|
|
1449
|
+
log(`\n${colors.cyan}[shell]${colors.reset} Setting up shell integration...`);
|
|
1326
1450
|
const shellConfigured = setupShellIntegration();
|
|
1327
1451
|
if (shellConfigured) {
|
|
1328
1452
|
log(`\n ${colors.yellow}IMPORTANT:${colors.reset} Run ${colors.cyan}source ~/.zshrc${colors.reset} or ${colors.bright}restart your terminal${colors.reset}`);
|