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.
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +18 -8
- package/dist/server/server.js.map +1 -1
- package/dist/server/session-coach.d.ts +21 -16
- package/dist/server/session-coach.d.ts.map +1 -1
- package/dist/server/session-coach.js +89 -64
- package/dist/server/session-coach.js.map +1 -1
- package/dist/server/session-guardian.d.ts +28 -0
- package/dist/server/session-guardian.d.ts.map +1 -0
- package/dist/server/session-guardian.js +168 -0
- package/dist/server/session-guardian.js.map +1 -0
- package/dist/server/stats.js +7 -7
- package/dist/server/stats.js.map +1 -1
- package/dist/server/tools/context.d.ts.map +1 -1
- package/dist/server/tools/context.js +24 -0
- package/dist/server/tools/context.js.map +1 -1
- package/dist/server/tools/project.js +1 -1
- package/dist/server/tools/project.js.map +1 -1
- package/files/CLAUDE.md +66 -5
- package/files/agents/merlin.md +21 -13
- package/files/hooks/config-change.sh +2 -2
- package/files/hooks/notify-desktop.sh +2 -2
- package/files/hooks/notify-webhook.sh +1 -1
- package/files/hooks/post-edit-logger.sh +23 -1
- package/files/hooks/pre-compact.sh +52 -5
- package/files/hooks/pre-edit-sights-check.sh +56 -31
- package/files/hooks/task-completed-verify.sh +2 -2
- package/files/hooks/worktree-create.sh +1 -1
- package/files/hooks/worktree-remove.sh +1 -1
- package/package.json +1 -1
- package/files/hooks/agent-sync.sh +0 -50
- package/files/hooks/pre-edit-sights-enforce.md +0 -1
- package/files/hooks/session-start-boot.md +0 -69
- package/files/hooks/session-start-context.sh +0 -23
- package/files/hooks/stop-check.md +0 -13
- package/files/hooks/task-completed-verify.md +0 -13
|
@@ -1,22 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session Coach — Persistent Protocol Enforcement
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Solution: The MCP server IS the persistent thing in every session. Every tool
|
|
9
|
-
* response goes through us. We use that channel to inject targeted, escalating
|
|
10
|
-
* behavioral nudges that Claude's attention mechanism can't ignore.
|
|
11
|
-
*
|
|
12
|
-
* The key insight: <sub> tags and decorative boxes get ignored. What works is
|
|
13
|
-
* content that looks like instructions — direct, specific, actionable.
|
|
14
|
-
*
|
|
15
|
-
* Escalation levels:
|
|
16
|
-
* 0 - Green: Claude is following the protocol. Light footer.
|
|
17
|
-
* 1 - Yellow: Starting to drift (5+ min since context check). Nudge.
|
|
18
|
-
* 2 - Orange: Significant drift (10+ min or 12+ queries). Strong nudge.
|
|
19
|
-
* 3 - Red: Protocol abandoned (20+ min). Hard interrupt.
|
|
4
|
+
* Tracks Claude's behavior and injects escalating nudges into MCP tool responses.
|
|
5
|
+
* Also provides the guardian HTTP sidecar with edit-check responses for hooks.
|
|
6
|
+
* Escalation: 0=Green, 1=Yellow(5m), 2=Orange(10m), 3=Red(20m)
|
|
20
7
|
*/
|
|
21
8
|
// Thresholds
|
|
22
9
|
const DRIFT_MINUTES_YELLOW = 5;
|
|
@@ -54,6 +41,8 @@ const state = {
|
|
|
54
41
|
totalCalls: 0,
|
|
55
42
|
contextCheckCount: 0,
|
|
56
43
|
driftWarnings: 0,
|
|
44
|
+
externalEdits: [],
|
|
45
|
+
editsSinceContext: 0,
|
|
57
46
|
};
|
|
58
47
|
/**
|
|
59
48
|
* Record a tool call. Called by every Merlin tool handler.
|
|
@@ -69,6 +58,7 @@ export function recordToolCall(toolName) {
|
|
|
69
58
|
if (CONTEXT_TOOLS.has(toolName)) {
|
|
70
59
|
state.lastContextCheck = now;
|
|
71
60
|
state.callsSinceContext = 0;
|
|
61
|
+
state.editsSinceContext = 0; // Reset external edit counter too
|
|
72
62
|
state.contextCheckCount++;
|
|
73
63
|
}
|
|
74
64
|
else {
|
|
@@ -147,10 +137,10 @@ export function getProtocolNudge(currentTool) {
|
|
|
147
137
|
if (isContextTool) {
|
|
148
138
|
if (state.contextCheckCount <= 1) {
|
|
149
139
|
// First context check of the session
|
|
150
|
-
return '\n\n---\n
|
|
140
|
+
return '\n\n---\n**⟡🔮 MERLIN ›** Context loaded. Call `merlin_get_context` again before each new file modification.';
|
|
151
141
|
}
|
|
152
142
|
// Regular context check — brief confirmation
|
|
153
|
-
return '\n\n---\n
|
|
143
|
+
return '\n\n---\n**⟡🔮 MERLIN ›** ✓ Context fresh. Next check: before your next file edit.';
|
|
154
144
|
}
|
|
155
145
|
// For non-context tools, escalate based on drift
|
|
156
146
|
switch (driftLevel) {
|
|
@@ -173,60 +163,36 @@ export function getProtocolNudge(currentTool) {
|
|
|
173
163
|
return '';
|
|
174
164
|
}
|
|
175
165
|
}
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
-
|
|
166
|
+
/** Minutes since last context check (-1 if never) */
|
|
167
|
+
function minutesSinceContext() {
|
|
168
|
+
return state.lastContextCheck > 0
|
|
169
|
+
? Math.floor((Date.now() - state.lastContextCheck) / (1000 * 60))
|
|
170
|
+
: -1;
|
|
171
|
+
}
|
|
179
172
|
function getGreenNudge() {
|
|
180
173
|
const nudges = [
|
|
181
|
-
'
|
|
182
|
-
'
|
|
183
|
-
'
|
|
174
|
+
'`merlin_get_context` before modifying files.',
|
|
175
|
+
'Check Sights before creating new files to avoid duplicates.',
|
|
176
|
+
'Call `merlin_get_context("your task")` before each edit.',
|
|
184
177
|
];
|
|
185
|
-
|
|
186
|
-
return `\n\n---\n${pick}`;
|
|
178
|
+
return `\n\n---\n**⟡🔮 MERLIN ›** ${nudges[state.totalCalls % nudges.length]}`;
|
|
187
179
|
}
|
|
188
|
-
/**
|
|
189
|
-
* Yellow nudge — starting to drift, clear instruction
|
|
190
|
-
*/
|
|
191
180
|
function getYellowNudge() {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
:
|
|
195
|
-
return `\n\n---\n` +
|
|
196
|
-
`**⚡ REFRESH CONTEXT** — ${minutesSince}min since last check, ${state.callsSinceContext} calls without context refresh.\n` +
|
|
197
|
-
`Before your next file edit, run: \`merlin_get_context("what you're working on")\`\n` +
|
|
198
|
-
`This prevents duplicating existing code and catches recent changes.`;
|
|
181
|
+
const m = minutesSinceContext();
|
|
182
|
+
return `\n\n---\n**⟡🔮 MERLIN › REFRESH CONTEXT** — ${m}min since last check, ${state.callsSinceContext} calls without refresh.\n` +
|
|
183
|
+
`Run: \`merlin_get_context("what you're working on")\` before your next edit.`;
|
|
199
184
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Orange nudge — significant drift, strong instruction
|
|
202
|
-
*/
|
|
203
185
|
function getOrangeNudge() {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
`**⚠️ PROTOCOL DRIFT DETECTED** — ${minutesSince}min and ${state.callsSinceContext} tool calls without checking Sights.\n\n` +
|
|
209
|
-
`**REQUIRED before continuing:** Call \`merlin_get_context("your current task")\` now.\n\n` +
|
|
210
|
-
`Without fresh context you risk:\n` +
|
|
211
|
-
`- Duplicating code that already exists\n` +
|
|
212
|
-
`- Missing recent changes by other agents\n` +
|
|
213
|
-
`- Breaking patterns the codebase expects`;
|
|
186
|
+
const m = minutesSinceContext();
|
|
187
|
+
return `\n\n---\n**⟡🔮 MERLIN › ⚠️ PROTOCOL DRIFT** — ${m}min, ${state.callsSinceContext} calls without Sights.\n` +
|
|
188
|
+
`**REQUIRED:** Call \`merlin_get_context("your current task")\` now.\n` +
|
|
189
|
+
`Risk: duplicating code, missing changes, breaking patterns.`;
|
|
214
190
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Red nudge — protocol abandoned, hard interrupt
|
|
217
|
-
*/
|
|
218
191
|
function getRedNudge() {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return `\n\n---\n` +
|
|
224
|
-
`**🛑 STOP — CONTEXT STALE (${minutesSince}min, session health: ${health}%)**\n\n` +
|
|
225
|
-
`You have made ${state.callsSinceContext} tool calls without checking codebase context.\n` +
|
|
226
|
-
`This is exactly how duplicate code and broken patterns happen.\n\n` +
|
|
227
|
-
`**DO THIS NOW:**\n` +
|
|
228
|
-
`\`\`\`\nmerlin_get_context("describe what you are currently working on")\n\`\`\`\n\n` +
|
|
229
|
-
`Then continue your work with fresh understanding of the codebase.`;
|
|
192
|
+
const m = minutesSinceContext();
|
|
193
|
+
return `\n\n---\n**⟡🔮 MERLIN › 🛑 STOP — CONTEXT STALE (${m}min, health: ${getSessionHealth()}%)**\n` +
|
|
194
|
+
`${state.callsSinceContext} tool calls without context check.\n` +
|
|
195
|
+
`**DO THIS NOW:** \`merlin_get_context("what you are working on")\``;
|
|
230
196
|
}
|
|
231
197
|
/**
|
|
232
198
|
* Get a compact protocol status line for tool responses
|
|
@@ -245,7 +211,62 @@ export function getProtocolStatus() {
|
|
|
245
211
|
: minutesSince === 0
|
|
246
212
|
? 'just now'
|
|
247
213
|
: `${minutesSince}m ago`;
|
|
248
|
-
return `${icon} Protocol: ${health}% │ Last context: ${lastCheck} │ Calls since: ${state.callsSinceContext}`;
|
|
214
|
+
return `${icon} ⟡🔮 Protocol: ${health}% │ Last context: ${lastCheck} │ Calls since: ${state.callsSinceContext}`;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Record an external edit reported by a hook via the guardian HTTP sidecar.
|
|
218
|
+
* This gives us visibility into Claude's actual Edit/Write tool usage.
|
|
219
|
+
*/
|
|
220
|
+
export function recordExternalEdit(filePath, tool) {
|
|
221
|
+
state.externalEdits.push({ file: filePath, tool, timestamp: Date.now() });
|
|
222
|
+
state.editsSinceContext++;
|
|
223
|
+
// Keep only last 30 edits
|
|
224
|
+
if (state.externalEdits.length > 30) {
|
|
225
|
+
state.externalEdits = state.externalEdits.slice(-30);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** Generate graduated pre-edit hook response. Always allows (never blocks — that caused loops in v3.5.2). */
|
|
229
|
+
export function getEditCheckResponse() {
|
|
230
|
+
const drift = getDriftLevel();
|
|
231
|
+
const minutesSince = state.lastContextCheck > 0
|
|
232
|
+
? Math.floor((Date.now() - state.lastContextCheck) / (1000 * 60))
|
|
233
|
+
: -1;
|
|
234
|
+
const edits = state.editsSinceContext;
|
|
235
|
+
// Also factor in external edits for drift calculation
|
|
236
|
+
// 5+ edits without context = at least Yellow, regardless of time
|
|
237
|
+
const editDrift = edits >= 10 ? 3 : edits >= 7 ? 2 : edits >= 4 ? 1 : 0;
|
|
238
|
+
const effectiveDrift = Math.max(drift, editDrift);
|
|
239
|
+
if (effectiveDrift === 0) {
|
|
240
|
+
// Green: allow silently — no additionalContext needed
|
|
241
|
+
return {};
|
|
242
|
+
}
|
|
243
|
+
const timeStr = minutesSince < 0 ? 'never checked' : `${minutesSince}m ago`;
|
|
244
|
+
if (effectiveDrift === 1) {
|
|
245
|
+
return {
|
|
246
|
+
hookSpecificOutput: {
|
|
247
|
+
hookEventName: 'PreToolUse',
|
|
248
|
+
permissionDecision: 'allow',
|
|
249
|
+
additionalContext: `⟡🔮 MERLIN › Sights context is getting stale (last check: ${timeStr}, ${edits} edits since). Call \`merlin_get_context("your current task")\` before your next edit to stay in sync with the codebase.`,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (effectiveDrift === 2) {
|
|
254
|
+
return {
|
|
255
|
+
hookSpecificOutput: {
|
|
256
|
+
hookEventName: 'PreToolUse',
|
|
257
|
+
permissionDecision: 'allow',
|
|
258
|
+
additionalContext: `⟡🔮 MERLIN › ⚠️ CONTEXT DRIFT — Last Sights check: ${timeStr}, ${edits} edits without refresh. You MUST call \`merlin_get_context("what you're working on")\` NOW. Without fresh context you risk duplicating existing code or breaking established patterns.`,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
// Red (3): strongest nudge, still allowing
|
|
263
|
+
return {
|
|
264
|
+
hookSpecificOutput: {
|
|
265
|
+
hookEventName: 'PreToolUse',
|
|
266
|
+
permissionDecision: 'allow',
|
|
267
|
+
additionalContext: `⟡🔮 MERLIN › 🛑 PROTOCOL VIOLATION — ${edits} file edits without ANY Sights context check (last: ${timeStr}). STOP editing and call \`merlin_get_context("describe your current task")\` IMMEDIATELY. This is exactly how duplicate code, broken patterns, and regressions happen. The codebase may have changed since you last checked.`,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
249
270
|
}
|
|
250
271
|
/**
|
|
251
272
|
* Reset session state (for testing or session restart)
|
|
@@ -259,6 +280,8 @@ export function resetCoach() {
|
|
|
259
280
|
state.totalCalls = 0;
|
|
260
281
|
state.contextCheckCount = 0;
|
|
261
282
|
state.driftWarnings = 0;
|
|
283
|
+
state.externalEdits = [];
|
|
284
|
+
state.editsSinceContext = 0;
|
|
262
285
|
}
|
|
263
286
|
/**
|
|
264
287
|
* Get coach stats for debugging
|
|
@@ -268,6 +291,8 @@ export function getCoachStats() {
|
|
|
268
291
|
totalCalls: state.totalCalls,
|
|
269
292
|
contextChecks: state.contextCheckCount,
|
|
270
293
|
callsSinceContext: state.callsSinceContext,
|
|
294
|
+
editsSinceContext: state.editsSinceContext,
|
|
295
|
+
totalExternalEdits: state.externalEdits.length,
|
|
271
296
|
driftLevel: getDriftLevel(),
|
|
272
297
|
health: getSessionHealth(),
|
|
273
298
|
driftWarnings: state.driftWarnings,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-coach.js","sourceRoot":"","sources":["../../src/server/session-coach.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"session-coach.js","sourceRoot":"","sources":["../../src/server/session-coach.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Guardian — HTTP sidecar for hook↔MCP communication.
|
|
3
|
+
*
|
|
4
|
+
* The MCP server communicates via stdio (JSON-RPC). Shell hooks can't
|
|
5
|
+
* talk stdio. This lightweight HTTP server bridges the gap: hooks curl
|
|
6
|
+
* localhost, the guardian reads MCP-side session state and returns
|
|
7
|
+
* hook-compatible JSON responses.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle:
|
|
10
|
+
* - Started asynchronously AFTER MCP transport connects (zero startup cost)
|
|
11
|
+
* - Listens on port 0 (OS-assigned) to avoid conflicts
|
|
12
|
+
* - Writes { port, pid } to ~/.claude/merlin/.guardian-PORT.json
|
|
13
|
+
* - Cleans up on exit
|
|
14
|
+
*
|
|
15
|
+
* Endpoints:
|
|
16
|
+
* GET /health — Liveness check
|
|
17
|
+
* GET /edit-check — Pre-edit hook calls this; returns graduated enforcement
|
|
18
|
+
* POST /record-edit — Post-edit hook reports that an edit happened
|
|
19
|
+
* GET /state — Full session state dump (for pre-compact injection)
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Start the guardian HTTP sidecar.
|
|
23
|
+
* Call AFTER MCP transport is connected (non-blocking).
|
|
24
|
+
*/
|
|
25
|
+
export declare function startGuardian(): Promise<number>;
|
|
26
|
+
/** Get the guardian port (0 if not started) */
|
|
27
|
+
export declare function getGuardianPort(): number;
|
|
28
|
+
//# sourceMappingURL=session-guardian.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-guardian.d.ts","sourceRoot":"","sources":["../../src/server/session-guardian.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgGH;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAuDrD;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Guardian — HTTP sidecar for hook↔MCP communication.
|
|
3
|
+
*
|
|
4
|
+
* The MCP server communicates via stdio (JSON-RPC). Shell hooks can't
|
|
5
|
+
* talk stdio. This lightweight HTTP server bridges the gap: hooks curl
|
|
6
|
+
* localhost, the guardian reads MCP-side session state and returns
|
|
7
|
+
* hook-compatible JSON responses.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle:
|
|
10
|
+
* - Started asynchronously AFTER MCP transport connects (zero startup cost)
|
|
11
|
+
* - Listens on port 0 (OS-assigned) to avoid conflicts
|
|
12
|
+
* - Writes { port, pid } to ~/.claude/merlin/.guardian-PORT.json
|
|
13
|
+
* - Cleans up on exit
|
|
14
|
+
*
|
|
15
|
+
* Endpoints:
|
|
16
|
+
* GET /health — Liveness check
|
|
17
|
+
* GET /edit-check — Pre-edit hook calls this; returns graduated enforcement
|
|
18
|
+
* POST /record-edit — Post-edit hook reports that an edit happened
|
|
19
|
+
* GET /state — Full session state dump (for pre-compact injection)
|
|
20
|
+
*/
|
|
21
|
+
import { createServer } from 'http';
|
|
22
|
+
import { writeFileSync, unlinkSync, mkdirSync } from 'fs';
|
|
23
|
+
import { join } from 'path';
|
|
24
|
+
import { homedir } from 'os';
|
|
25
|
+
import { getDriftLevel, getSessionHealth, getCoachStats, recordExternalEdit, getEditCheckResponse, } from './session-coach.js';
|
|
26
|
+
const MERLIN_DIR = join(homedir(), '.claude', 'merlin');
|
|
27
|
+
let guardianPort = 0;
|
|
28
|
+
let portFilePath = '';
|
|
29
|
+
/** Parse JSON body from a POST request (max 4KB) */
|
|
30
|
+
function parseBody(req) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
let data = '';
|
|
33
|
+
req.on('data', (chunk) => {
|
|
34
|
+
data += chunk.toString();
|
|
35
|
+
if (data.length > 4096) {
|
|
36
|
+
resolve({});
|
|
37
|
+
req.destroy();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
req.on('end', () => {
|
|
41
|
+
try {
|
|
42
|
+
resolve(JSON.parse(data));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
resolve({});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
req.on('error', () => resolve({}));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** Handle incoming HTTP requests */
|
|
52
|
+
async function handleRequest(req, res) {
|
|
53
|
+
const url = req.url || '/';
|
|
54
|
+
const method = req.method || 'GET';
|
|
55
|
+
// CORS-like: allow any local caller
|
|
56
|
+
res.setHeader('Content-Type', 'application/json');
|
|
57
|
+
try {
|
|
58
|
+
if (url === '/health' && method === 'GET') {
|
|
59
|
+
res.writeHead(200);
|
|
60
|
+
res.end(JSON.stringify({
|
|
61
|
+
ok: true,
|
|
62
|
+
pid: process.pid,
|
|
63
|
+
uptime: Math.floor(process.uptime()),
|
|
64
|
+
}));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (url === '/edit-check' && method === 'GET') {
|
|
68
|
+
// Pre-edit hook calls this before every Edit/Write
|
|
69
|
+
const response = getEditCheckResponse();
|
|
70
|
+
res.writeHead(200);
|
|
71
|
+
res.end(JSON.stringify(response));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (url === '/record-edit' && method === 'POST') {
|
|
75
|
+
// Post-edit hook reports an edit happened
|
|
76
|
+
const body = await parseBody(req);
|
|
77
|
+
const filePath = body.file || 'unknown';
|
|
78
|
+
const tool = body.tool || 'unknown';
|
|
79
|
+
recordExternalEdit(filePath, tool);
|
|
80
|
+
res.writeHead(200);
|
|
81
|
+
res.end('{"ok":true}');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (url === '/state' && method === 'GET') {
|
|
85
|
+
// Full state dump for pre-compact hook
|
|
86
|
+
const stats = getCoachStats();
|
|
87
|
+
const health = getSessionHealth();
|
|
88
|
+
const drift = getDriftLevel();
|
|
89
|
+
res.writeHead(200);
|
|
90
|
+
res.end(JSON.stringify({
|
|
91
|
+
...stats,
|
|
92
|
+
health,
|
|
93
|
+
driftLevel: drift,
|
|
94
|
+
guardianPort,
|
|
95
|
+
pid: process.pid,
|
|
96
|
+
}));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Unknown endpoint
|
|
100
|
+
res.writeHead(404);
|
|
101
|
+
res.end('{"error":"not found"}');
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
res.writeHead(500);
|
|
105
|
+
res.end(`{"error":"${err instanceof Error ? err.message : 'unknown'}"}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Start the guardian HTTP sidecar.
|
|
110
|
+
* Call AFTER MCP transport is connected (non-blocking).
|
|
111
|
+
*/
|
|
112
|
+
export async function startGuardian() {
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const server = createServer((req, res) => {
|
|
115
|
+
handleRequest(req, res).catch(() => {
|
|
116
|
+
if (!res.headersSent) {
|
|
117
|
+
res.writeHead(500);
|
|
118
|
+
res.end('{"error":"internal"}');
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
// Listen on port 0 = OS assigns a free port
|
|
123
|
+
server.listen(0, '127.0.0.1', () => {
|
|
124
|
+
const addr = server.address();
|
|
125
|
+
if (!addr || typeof addr === 'string') {
|
|
126
|
+
reject(new Error('Guardian: could not determine port'));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
guardianPort = addr.port;
|
|
130
|
+
// Write port file so hooks can find us
|
|
131
|
+
try {
|
|
132
|
+
mkdirSync(MERLIN_DIR, { recursive: true });
|
|
133
|
+
portFilePath = join(MERLIN_DIR, `.guardian-port`);
|
|
134
|
+
writeFileSync(portFilePath, JSON.stringify({
|
|
135
|
+
port: guardianPort,
|
|
136
|
+
pid: process.pid,
|
|
137
|
+
started: Date.now(),
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Non-fatal — hooks will fall back to file-based checks
|
|
142
|
+
}
|
|
143
|
+
console.error(`Merlin Guardian listening on 127.0.0.1:${guardianPort}`);
|
|
144
|
+
resolve(guardianPort);
|
|
145
|
+
});
|
|
146
|
+
server.on('error', (err) => {
|
|
147
|
+
console.error(`Merlin Guardian failed to start: ${err.message}`);
|
|
148
|
+
reject(err);
|
|
149
|
+
});
|
|
150
|
+
// Cleanup on exit
|
|
151
|
+
const cleanup = () => {
|
|
152
|
+
try {
|
|
153
|
+
if (portFilePath)
|
|
154
|
+
unlinkSync(portFilePath);
|
|
155
|
+
}
|
|
156
|
+
catch { /* already gone */ }
|
|
157
|
+
server.close();
|
|
158
|
+
};
|
|
159
|
+
process.on('exit', cleanup);
|
|
160
|
+
process.on('SIGTERM', cleanup);
|
|
161
|
+
process.on('SIGINT', cleanup);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/** Get the guardian port (0 if not started) */
|
|
165
|
+
export function getGuardianPort() {
|
|
166
|
+
return guardianPort;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=session-guardian.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-guardian.js","sourceRoot":"","sources":["../../src/server/session-guardian.ts"],"names":[],"mappings":"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"}
|
package/dist/server/stats.js
CHANGED
|
@@ -82,7 +82,7 @@ export function getSessionEnforcementReminder() {
|
|
|
82
82
|
if (stats.sessionStatusShown) {
|
|
83
83
|
return null;
|
|
84
84
|
}
|
|
85
|
-
return
|
|
85
|
+
return `**⟡🔮 MERLIN › STOP — BOOT REQUIRED.** You must call \`merlin_get_selected_repo\` and \`merlin_get_project_status\` before doing any work. Call them now.\n\n`;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Mark that context was refreshed (called for get_context, search, find_files)
|
|
@@ -115,7 +115,7 @@ export function getStaleContextReminder() {
|
|
|
115
115
|
: `${stats.queriesSinceRefresh} queries since last context refresh`;
|
|
116
116
|
return `
|
|
117
117
|
┌──────────────────────────────────────────────────────────────────┐
|
|
118
|
-
│
|
|
118
|
+
│ ⟡🔮 MERLIN › CONTEXT MAY BE STALE │
|
|
119
119
|
├──────────────────────────────────────────────────────────────────┤
|
|
120
120
|
│ ${reason.padEnd(62)}│
|
|
121
121
|
│ │
|
|
@@ -157,7 +157,7 @@ export function formatHeader(toolName, options = {}) {
|
|
|
157
157
|
const { task, filesFound = 0, responseTokens = 0, isSuccess = true, summary } = options;
|
|
158
158
|
const shortName = toolName.replace('merlin_', '');
|
|
159
159
|
// Line 1: Tool and task
|
|
160
|
-
let header =
|
|
160
|
+
let header = `**⟡🔮 MERLIN** › ${shortName}`;
|
|
161
161
|
if (task) {
|
|
162
162
|
const truncatedTask = task.length > 35 ? task.slice(0, 35) + '...' : task;
|
|
163
163
|
header += ` ("${truncatedTask}")`;
|
|
@@ -260,7 +260,7 @@ export function getSessionSummary() {
|
|
|
260
260
|
? Math.round((saved / stats.wouldHaveRead) * 100)
|
|
261
261
|
: 0;
|
|
262
262
|
let summary = `\n---\n`;
|
|
263
|
-
summary +=
|
|
263
|
+
summary += `**⟡🔮 MERLIN › Session: ${stats.queries} queries**\n`;
|
|
264
264
|
summary += `Merlin: ${formatTokens(stats.merlinTokens)} tokens │ `;
|
|
265
265
|
summary += `Without: ~${formatTokens(stats.wouldHaveRead)} tokens │ `;
|
|
266
266
|
summary += `**${savedPercent}% smaller context**\n`;
|
|
@@ -279,7 +279,7 @@ export function getCompactStats() {
|
|
|
279
279
|
const savedPercent = stats.wouldHaveRead > 0
|
|
280
280
|
? Math.round((saved / stats.wouldHaveRead) * 100)
|
|
281
281
|
: 0;
|
|
282
|
-
return
|
|
282
|
+
return `⟡🔮 ${stats.queries} queries │ ${formatTokens(stats.merlinTokens)} vs ~${formatTokens(stats.wouldHaveRead)} tokens (${savedPercent}% saved)`;
|
|
283
283
|
}
|
|
284
284
|
/**
|
|
285
285
|
* Human-readable labels for save types
|
|
@@ -311,10 +311,10 @@ export function formatSaveNotification(saveType, options = {}) {
|
|
|
311
311
|
let notification = '';
|
|
312
312
|
// Line 1: Type header
|
|
313
313
|
if (isSuccess) {
|
|
314
|
-
notification +=
|
|
314
|
+
notification += `⟡🔮 **MERLIN › SAVED** › ${label}\n`;
|
|
315
315
|
}
|
|
316
316
|
else {
|
|
317
|
-
notification +=
|
|
317
|
+
notification += `⟡🔮 **MERLIN › FAILED** › ${label}\n`;
|
|
318
318
|
}
|
|
319
319
|
// Line 2: What was saved
|
|
320
320
|
if (isSuccess) {
|
package/dist/server/stats.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/server/stats.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAgBzF,uCAAuC;AACvC,MAAM,qBAAqB,GAAG,CAAC,CAAC,CAAM,uCAAuC;AAC7E,MAAM,uBAAuB,GAAG,CAAC,CAAC,CAAI,oCAAoC;AAE1E,uBAAuB;AACvB,MAAM,KAAK,GAAiB;IAC1B,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;IACxB,OAAO,EAAE,CAAC;IACV,YAAY,EAAE,CAAC;IACf,aAAa,EAAE,CAAC;IAChB,UAAU,EAAE,CAAC;IACb,aAAa,EAAE,CAAC;IAChB,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;IACxB,eAAe,EAAE,KAAK;IACtB,kBAAkB,EAAE,KAAK;IACzB,kBAAkB,EAAE,CAAC;IACrB,mBAAmB,EAAE,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,YAAoB,EACpB,aAAqB,CAAC;IAEtB,KAAK,CAAC,OAAO,EAAE,CAAC;IAChB,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACjC,KAAK,CAAC,UAAU,IAAI,UAAU,CAAC;IAE/B,uBAAuB;IACvB,MAAM,YAAY,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IAClD,KAAK,CAAC,YAAY,IAAI,YAAY,CAAC;IAEnC,2CAA2C;IAC3C,iDAAiD;IACjD,0EAA0E;IAC1E,MAAM,aAAa,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;IAChD,KAAK,CAAC,aAAa,IAAI,aAAa,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,KAAK,CAAC,kBAAkB,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B;IAC3C,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/server/stats.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAgBzF,uCAAuC;AACvC,MAAM,qBAAqB,GAAG,CAAC,CAAC,CAAM,uCAAuC;AAC7E,MAAM,uBAAuB,GAAG,CAAC,CAAC,CAAI,oCAAoC;AAE1E,uBAAuB;AACvB,MAAM,KAAK,GAAiB;IAC1B,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;IACxB,OAAO,EAAE,CAAC;IACV,YAAY,EAAE,CAAC;IACf,aAAa,EAAE,CAAC;IAChB,UAAU,EAAE,CAAC;IACb,aAAa,EAAE,CAAC;IAChB,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;IACxB,eAAe,EAAE,KAAK;IACtB,kBAAkB,EAAE,KAAK;IACzB,kBAAkB,EAAE,CAAC;IACrB,mBAAmB,EAAE,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,YAAoB,EACpB,aAAqB,CAAC;IAEtB,KAAK,CAAC,OAAO,EAAE,CAAC;IAChB,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACjC,KAAK,CAAC,UAAU,IAAI,UAAU,CAAC;IAE/B,uBAAuB;IACvB,MAAM,YAAY,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IAClD,KAAK,CAAC,YAAY,IAAI,YAAY,CAAC;IAEnC,2CAA2C;IAC3C,iDAAiD;IACjD,0EAA0E;IAC1E,MAAM,aAAa,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;IAChD,KAAK,CAAC,aAAa,IAAI,aAAa,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,KAAK,CAAC,kBAAkB,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B;IAC3C,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,+JAA+J,CAAC;AACzK,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtC,KAAK,CAAC,mBAAmB,GAAG,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,4BAA4B;IAC1C,KAAK,CAAC,mBAAmB,EAAE,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,6CAA6C;IAC7C,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,mBAAmB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;IAClF,MAAM,OAAO,GAAG,mBAAmB,GAAG,qBAAqB,CAAC;IAC5D,MAAM,WAAW,GAAG,KAAK,CAAC,mBAAmB,IAAI,uBAAuB,CAAC;IAEzE,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO;YACpB,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,qCAAqC;YACzE,CAAC,CAAC,GAAG,KAAK,CAAC,mBAAmB,qCAAqC,CAAC;QAEtE,OAAO;;;;KAIN,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;;;;;;;;CAQrB,CAAC;IACA,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,CAAS;IAC7B,IAAI,CAAC,IAAI,OAAO;QAAE,OAAO,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACxD,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAClD,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAgB,EAChB,UAMI,EAAE;IAEN,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,CAAC,EAAE,cAAc,GAAG,CAAC,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACxF,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAElD,wBAAwB;IACxB,IAAI,MAAM,GAAG,oBAAoB,SAAS,EAAE,CAAC;IAC7C,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1E,MAAM,IAAI,MAAM,aAAa,IAAI,CAAC;IACpC,CAAC;IACD,MAAM,IAAI,IAAI,CAAC;IAEf,kCAAkC;IAClC,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,IAAI,CAAC;QACf,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,OAAO,CAAC;QACpB,CAAC;aAAM,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,SAAS,UAAU,QAAQ,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,gBAAgB,CAAC;QAC7B,CAAC;QAED,mBAAmB;QACnB,IAAI,cAAc,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,aAAa,GAAG,UAAU,GAAG,IAAI,CAAC;YACxC,MAAM,IAAI,QAAQ,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC;YAC1D,MAAM,IAAI,SAAS,YAAY,CAAC,aAAa,CAAC,iBAAiB,CAAC;YAEhE,uDAAuD;YACvD,MAAM,YAAY,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,YAAY,GAAG,EAAE,EAAE,CAAC;gBACtB,MAAM,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,SAAS,CAAC;YAC1D,CAAC;iBAAM,IAAI,YAAY,GAAG,EAAE,EAAE,CAAC;gBAC7B,MAAM,IAAI,OAAO,YAAY,SAAS,CAAC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;IACjD,CAAC;IACD,MAAM,IAAI,IAAI,CAAC;IAEf,6CAA6C;IAC7C,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,MAAM,IAAI,uCAAuC,CAAC;QAClD,gBAAgB,EAAE,CAAC;IACrB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAgB,EAChB,OAAe,EACf,UAOI,EAAE;IAEN,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,CAAC,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,EAAE,eAAe,GAAG,KAAK,EAAE,gBAAgB,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAEvH,mBAAmB;IACnB,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAE/C,4BAA4B;IAC5B,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAE3C,uDAAuD;IACvD,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEzB,0DAA0D;IAC1D,IAAI,gBAAgB,EAAE,CAAC;QACrB,kBAAkB,EAAE,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,4BAA4B,EAAE,CAAC;IACjC,CAAC;IAED,2BAA2B;IAC3B,IAAI,QAAQ,GAAG,EAAE,CAAC;IAElB,2DAA2D;IAC3D,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,6BAA6B,EAAE,CAAC;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,IAAI,QAAQ,CAAC;QACvB,CAAC;IACH,CAAC;IAED,yDAAyD;IACzD,QAAQ,IAAI,YAAY,CAAC,QAAQ,EAAE;QACjC,IAAI;QACJ,UAAU;QACV,cAAc;QACd,SAAS;QACT,OAAO;KACR,CAAC,CAAC;IAEH,2FAA2F;IAC3F,QAAQ,IAAI,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAEvC,YAAY;IACZ,QAAQ,IAAI,WAAW,CAAC;IAExB,iBAAiB;IACjB,QAAQ,IAAI,OAAO,CAAC;IAEpB,6DAA6D;IAC7D,6EAA6E;IAC7E,QAAQ,IAAI,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEvC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC;IACvD,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC;QAC1C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;QACjD,CAAC,CAAC,CAAC,CAAC;IAEN,IAAI,OAAO,GAAG,SAAS,CAAC;IACxB,OAAO,IAAI,2BAA2B,KAAK,CAAC,OAAO,cAAc,CAAC;IAClE,OAAO,IAAI,WAAW,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC;IACnE,OAAO,IAAI,aAAa,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC;IACtE,OAAO,IAAI,KAAK,YAAY,uBAAuB,CAAC;IAEpD,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,aAAa,YAAY,yBAAyB,KAAK,CAAC,UAAU,IAAI,CAAC;IACpF,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnC,MAAM,KAAK,GAAG,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC;IACvD,MAAM,YAAY,GAAG,KAAK,CAAC,aAAa,GAAG,CAAC;QAC1C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;QACjD,CAAC,CAAC,CAAC,CAAC;IAEN,OAAO,OAAO,KAAK,CAAC,OAAO,cAAc,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,YAAY,YAAY,UAAU,CAAC;AACvJ,CAAC;AAsBD;;GAEG;AACH,MAAM,gBAAgB,GAA6B;IACjD,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,aAAa;IACnB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,cAAc;IAC5B,QAAQ,EAAE,kBAAkB;IAC5B,KAAK,EAAE,kBAAkB;CAC1B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAkB,EAClB,UAOI,EAAE;IAEN,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC9E,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;IAErD,IAAI,YAAY,GAAG,EAAE,CAAC;IAEtB,sBAAsB;IACtB,IAAI,SAAS,EAAE,CAAC;QACd,YAAY,IAAI,4BAA4B,KAAK,IAAI,CAAC;IACxD,CAAC;SAAM,CAAC;QACN,YAAY,IAAI,6BAA6B,KAAK,IAAI,CAAC;IACzD,CAAC;IAED,yBAAyB;IACzB,IAAI,SAAS,EAAE,CAAC;QACd,YAAY,IAAI,IAAI,CAAC;QACrB,IAAI,GAAG,EAAE,CAAC;YACR,YAAY,IAAI,IAAI,GAAG,yBAAyB,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,YAAY,IAAI,GAAG,KAAK,wBAAwB,CAAC;QACnD,CAAC;QACD,YAAY,IAAI,IAAI,CAAC;IACvB,CAAC;SAAM,IAAI,KAAK,EAAE,CAAC;QACjB,YAAY,IAAI,KAAK,KAAK,IAAI,CAAC;IACjC,CAAC;IAED,wCAAwC;IACxC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC,IAAI,CAAC,YAAY,SAAS,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC1C,CAAC;IAED,6BAA6B;IAC7B,IAAI,OAAO,EAAE,CAAC;QACZ,YAAY,IAAI,OAAO,GAAG,IAAI,CAAC;IACjC,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,gBAAwB,EACxB,UAOI,EAAE;IAEN,MAAM,YAAY,GAAG,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/D,OAAO,YAAY,GAAG,WAAW,GAAG,gBAAgB,CAAC;AACvD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/server/tools/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/server/tools/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAyC9C,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAqkB3D"}
|
|
@@ -3,8 +3,27 @@
|
|
|
3
3
|
* Tools for getting codebase context, searching, and asking questions
|
|
4
4
|
*/
|
|
5
5
|
import { z } from 'zod';
|
|
6
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { homedir } from 'os';
|
|
6
9
|
import { detectLanguage } from '../lang/detector.js';
|
|
7
10
|
import { getLanguageRules } from '../lang/rules.js';
|
|
11
|
+
/**
|
|
12
|
+
* Record that Sights was just consulted.
|
|
13
|
+
* Writes epoch timestamp to ~/.claude/merlin/.last-sights-check
|
|
14
|
+
* so that pre-edit hooks know context is fresh.
|
|
15
|
+
* Also notifies the guardian HTTP sidecar if running.
|
|
16
|
+
*/
|
|
17
|
+
function recordSightsCall() {
|
|
18
|
+
try {
|
|
19
|
+
const dir = join(homedir(), '.claude', 'merlin');
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
writeFileSync(join(dir, '.last-sights-check'), Math.floor(Date.now() / 1000).toString());
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Non-fatal — hooks will fall back to stale detection
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
/** Module-level cache: repoId → detected language profile */
|
|
9
28
|
const langProfileCache = new Map();
|
|
10
29
|
/** Detect language for a repo, using cached result if available */
|
|
@@ -317,6 +336,8 @@ export function registerContextTools(ctx) {
|
|
|
317
336
|
catch {
|
|
318
337
|
// Silently skip if team state unavailable
|
|
319
338
|
}
|
|
339
|
+
// Bridge: update file-based timestamp so pre-edit hooks know Sights is fresh
|
|
340
|
+
recordSightsCall();
|
|
320
341
|
return { content: [{ type: 'text', text: context }] };
|
|
321
342
|
}
|
|
322
343
|
catch (error) {
|
|
@@ -356,9 +377,11 @@ export function registerContextTools(ctx) {
|
|
|
356
377
|
}
|
|
357
378
|
result += '\n';
|
|
358
379
|
});
|
|
380
|
+
recordSightsCall();
|
|
359
381
|
return { content: [{ type: 'text', text: result }] };
|
|
360
382
|
}
|
|
361
383
|
const findResult = await client.findFiles(repoId, query);
|
|
384
|
+
recordSightsCall();
|
|
362
385
|
return { content: [{ type: 'text', text: findResult }] };
|
|
363
386
|
}
|
|
364
387
|
catch (error) {
|
|
@@ -471,6 +494,7 @@ export function registerContextTools(ctx) {
|
|
|
471
494
|
};
|
|
472
495
|
}
|
|
473
496
|
const results = await client.search(repoId, query);
|
|
497
|
+
recordSightsCall();
|
|
474
498
|
return { content: [{ type: 'text', text: results }] };
|
|
475
499
|
}
|
|
476
500
|
catch (error) {
|