@mnemonik/cursor-hooks 0.7.66 → 0.7.71
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/hook.js +230 -87
- package/dist/hook.js.map +1 -1
- package/dist/install.js +4 -5
- package/dist/install.js.map +1 -1
- package/dist/lib/http.d.ts +22 -0
- package/dist/lib/http.js +127 -0
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/mcpPayload.d.ts +30 -0
- package/dist/lib/mcpPayload.js +73 -0
- package/dist/lib/mcpPayload.js.map +1 -0
- package/dist/lib/resultCue.d.ts +37 -0
- package/dist/lib/resultCue.js +356 -0
- package/dist/lib/resultCue.js.map +1 -0
- package/dist/lib/sessionState.d.ts +6 -0
- package/dist/lib/sessionState.js +29 -1
- package/dist/lib/sessionState.js.map +1 -1
- package/dist/lib/types.d.ts +31 -14
- package/dist/lib/types.js.map +1 -1
- package/package.json +2 -7
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmodSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { sessionStateFilePath } from './sessionState.js';
|
|
5
|
+
const RESULT_CUE_CHARS = 1_024;
|
|
6
|
+
const RESULT_CUE_FILE_RE = /^result-cue-[a-f0-9]{64}\.json$/u;
|
|
7
|
+
const MAX_RESULT_CUE_FILE_BYTES = 8 * 1_024;
|
|
8
|
+
const READ_COMMAND_PREFIX = /^\s*(?:cat(?:\s|$)|head(?:\s|$)|sed\s+-n(?:\s|$)|grep(?:\s|$)|rg(?:\s|$))/u;
|
|
9
|
+
export const RESULT_CUE_TTL_MS = 2 * 60 * 1_000;
|
|
10
|
+
export const MAX_PENDING_RESULT_CUES = 16;
|
|
11
|
+
function hash(value) {
|
|
12
|
+
return createHash('sha256').update(value).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
function hasDisallowedTextControl(text) {
|
|
15
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
16
|
+
const code = text.charCodeAt(index);
|
|
17
|
+
const disallowedC0 = code < 0x20 && code !== 0x09 && code !== 0x0a && code !== 0x0d;
|
|
18
|
+
if (disallowedC0 || (code >= 0x7f && code <= 0x9f))
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
function isRecognizableBinaryText(text) {
|
|
24
|
+
const leading = text.trimStart();
|
|
25
|
+
const lower = leading.toLowerCase();
|
|
26
|
+
return (leading.startsWith('GIF87a') ||
|
|
27
|
+
leading.startsWith('GIF89a') ||
|
|
28
|
+
leading.startsWith('%PDF-') ||
|
|
29
|
+
/^(?:iVBORw0KGgo|\/9j\/|R0lGOD|UklGR[\s\S]{4}WEBP)/u.test(leading) ||
|
|
30
|
+
/^(?:<\?xml[\s\S]*?\?>\s*)?(?:(?:<!--[\s\S]*?-->|<!doctype\s+svg(?:\s[^>]*)?>)\s*)*<svg(?:\s|>)/iu.test(leading) ||
|
|
31
|
+
lower.startsWith('data:image') ||
|
|
32
|
+
lower.startsWith('data:application/octet-stream'));
|
|
33
|
+
}
|
|
34
|
+
/** Bound and validate text before it may become a server-side recall cue. */
|
|
35
|
+
export function safeCursorResultText(value) {
|
|
36
|
+
if (typeof value !== 'string')
|
|
37
|
+
return undefined;
|
|
38
|
+
if (value.trim().length === 0 ||
|
|
39
|
+
value.includes('\ufffd') ||
|
|
40
|
+
hasDisallowedTextControl(value) ||
|
|
41
|
+
isRecognizableBinaryText(value)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
// The server's z.string().max(1024) counts UTF-16 code units, as does
|
|
45
|
+
// String#slice. Keep the client bound identical so astral text cannot turn a
|
|
46
|
+
// locally valid excerpt into a rejected request.
|
|
47
|
+
return value.slice(0, RESULT_CUE_CHARS);
|
|
48
|
+
}
|
|
49
|
+
function hasBinaryResultShape(value, depth = 0) {
|
|
50
|
+
if (!value || typeof value !== 'object' || depth > 3)
|
|
51
|
+
return false;
|
|
52
|
+
if (Array.isArray(value))
|
|
53
|
+
return value.some((item) => hasBinaryResultShape(item, depth + 1));
|
|
54
|
+
const record = value;
|
|
55
|
+
if (record.isImage === true)
|
|
56
|
+
return true;
|
|
57
|
+
const source = record.source;
|
|
58
|
+
const tags = [
|
|
59
|
+
record.type,
|
|
60
|
+
record.mimeType,
|
|
61
|
+
record.mime_type,
|
|
62
|
+
record.media_type,
|
|
63
|
+
record.content_type,
|
|
64
|
+
source && typeof source === 'object' ? source.type : undefined,
|
|
65
|
+
source && typeof source === 'object'
|
|
66
|
+
? source.media_type
|
|
67
|
+
: undefined,
|
|
68
|
+
];
|
|
69
|
+
if (tags.some((tag) => /^(?:image|audio|video|binary|application\/octet-stream)(?:\/|$)/iu.test(String(tag ?? '')))) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return Object.values(record).some((item) => hasBinaryResultShape(item, depth + 1));
|
|
73
|
+
}
|
|
74
|
+
function textualCursorResult(value) {
|
|
75
|
+
if (typeof value === 'string')
|
|
76
|
+
return value;
|
|
77
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
78
|
+
return undefined;
|
|
79
|
+
if (hasBinaryResultShape(value))
|
|
80
|
+
return undefined;
|
|
81
|
+
const record = value;
|
|
82
|
+
for (const key of ['content', 'output', 'stdout', 'text', 'result']) {
|
|
83
|
+
if (typeof record[key] === 'string')
|
|
84
|
+
return record[key];
|
|
85
|
+
}
|
|
86
|
+
const file = record.file;
|
|
87
|
+
if (file && typeof file === 'object' && !Array.isArray(file)) {
|
|
88
|
+
const content = file.content;
|
|
89
|
+
if (typeof content === 'string')
|
|
90
|
+
return content;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(record.content)) {
|
|
93
|
+
const blocks = [];
|
|
94
|
+
for (const block of record.content) {
|
|
95
|
+
if (!block || typeof block !== 'object' || Array.isArray(block))
|
|
96
|
+
continue;
|
|
97
|
+
const typed = block;
|
|
98
|
+
if (typed.type === 'text' && typeof typed.text === 'string')
|
|
99
|
+
blocks.push(typed.text);
|
|
100
|
+
}
|
|
101
|
+
return blocks.length > 0 ? blocks.join('\n') : undefined;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
export function isReadResultTool(toolName, toolInput) {
|
|
106
|
+
if (toolName === 'Read' || toolName === 'Grep')
|
|
107
|
+
return true;
|
|
108
|
+
if (toolName !== 'Shell')
|
|
109
|
+
return false;
|
|
110
|
+
const command = toolInput?.command;
|
|
111
|
+
return typeof command === 'string' && READ_COMMAND_PREFIX.test(command);
|
|
112
|
+
}
|
|
113
|
+
/** Extract direct textual content from Cursor's JSON-string PostToolUse result. */
|
|
114
|
+
export function extractCursorResultExcerpt(input) {
|
|
115
|
+
if (!isReadResultTool(input.toolName, input.toolInput) || typeof input.toolOutput !== 'string') {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
let parsed;
|
|
119
|
+
try {
|
|
120
|
+
parsed = JSON.parse(input.toolOutput);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
const raw = textualCursorResult(parsed);
|
|
126
|
+
if (raw === undefined)
|
|
127
|
+
return undefined;
|
|
128
|
+
const text = input.toolName === 'Read' ? raw.replace(/^[ \t]*\d+→ ?/gmu, '') : raw;
|
|
129
|
+
return safeCursorResultText(text);
|
|
130
|
+
}
|
|
131
|
+
/** A newline may delimit provider/native ids; every segment remains printable. */
|
|
132
|
+
export function validCursorToolUseId(value) {
|
|
133
|
+
if (typeof value !== 'string' || value.length === 0 || value.length > 255)
|
|
134
|
+
return undefined;
|
|
135
|
+
if (value.includes('\ufffd'))
|
|
136
|
+
return undefined;
|
|
137
|
+
const segments = value.split('\n');
|
|
138
|
+
if (segments.some((segment) => segment.length === 0 ||
|
|
139
|
+
// eslint-disable-next-line no-control-regex -- rejecting control bytes is the point
|
|
140
|
+
/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/u.test(segment))) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
function cuePath(sessionId, toolUseId) {
|
|
146
|
+
return sessionStateFilePath(sessionId, `result-cue-${hash(toolUseId)}.json`);
|
|
147
|
+
}
|
|
148
|
+
function ensurePrivateDirectory(filePath) {
|
|
149
|
+
const directory = dirname(filePath);
|
|
150
|
+
const root = dirname(directory);
|
|
151
|
+
try {
|
|
152
|
+
if (existsSync(root) && lstatSync(root).isSymbolicLink())
|
|
153
|
+
return false;
|
|
154
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
155
|
+
chmodSync(root, 0o700);
|
|
156
|
+
if (existsSync(directory) && lstatSync(directory).isSymbolicLink())
|
|
157
|
+
return false;
|
|
158
|
+
if (!existsSync(directory))
|
|
159
|
+
mkdirSync(directory, { mode: 0o700 });
|
|
160
|
+
chmodSync(directory, 0o700);
|
|
161
|
+
return lstatSync(directory).isDirectory() && !lstatSync(directory).isSymbolicLink();
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function readCue(filePath) {
|
|
168
|
+
try {
|
|
169
|
+
const state = lstatSync(filePath);
|
|
170
|
+
if (!state.isFile() || state.isSymbolicLink() || state.size > MAX_RESULT_CUE_FILE_BYTES) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
const value = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
174
|
+
if (value.version !== 1 ||
|
|
175
|
+
typeof value.targetKey !== 'string' ||
|
|
176
|
+
typeof value.createdAt !== 'number' ||
|
|
177
|
+
typeof value.expiresAt !== 'number' ||
|
|
178
|
+
!Number.isFinite(value.createdAt) ||
|
|
179
|
+
!Number.isFinite(value.expiresAt) ||
|
|
180
|
+
!/^[a-f0-9]{64}$/u.test(value.targetKey) ||
|
|
181
|
+
(value.excerpt !== undefined && typeof value.excerpt !== 'string')) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
if (value.excerpt !== undefined && safeCursorResultText(value.excerpt) !== value.excerpt) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function writeCue(filePath, value) {
|
|
194
|
+
if (!ensurePrivateDirectory(filePath))
|
|
195
|
+
return false;
|
|
196
|
+
const directory = dirname(filePath);
|
|
197
|
+
const temporary = join(directory, `.result-cue-${process.pid}-${randomUUID()}.tmp`);
|
|
198
|
+
let descriptor;
|
|
199
|
+
try {
|
|
200
|
+
if (existsSync(filePath) && lstatSync(filePath).isSymbolicLink())
|
|
201
|
+
return false;
|
|
202
|
+
descriptor = openSync(temporary, 'wx', 0o600);
|
|
203
|
+
writeFileSync(descriptor, JSON.stringify(value), 'utf8');
|
|
204
|
+
closeSync(descriptor);
|
|
205
|
+
descriptor = undefined;
|
|
206
|
+
chmodSync(temporary, 0o600);
|
|
207
|
+
renameSync(temporary, filePath);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
if (descriptor !== undefined) {
|
|
212
|
+
try {
|
|
213
|
+
closeSync(descriptor);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Best effort only.
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
rmSync(temporary, { force: true });
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function cueFiles(sessionId) {
|
|
224
|
+
const directory = dirname(sessionStateFilePath(sessionId, 'state.json'));
|
|
225
|
+
try {
|
|
226
|
+
if (!existsSync(directory))
|
|
227
|
+
return [];
|
|
228
|
+
const state = lstatSync(directory);
|
|
229
|
+
if (!state.isDirectory() || state.isSymbolicLink())
|
|
230
|
+
return [];
|
|
231
|
+
return readdirSync(directory)
|
|
232
|
+
.filter((name) => RESULT_CUE_FILE_RE.test(name))
|
|
233
|
+
.map((name) => join(directory, name));
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function removeExpiredAndBound(sessionId, now) {
|
|
240
|
+
const retained = cueFiles(sessionId)
|
|
241
|
+
.map((filePath) => ({ filePath, cue: readCue(filePath) }))
|
|
242
|
+
.filter((entry) => {
|
|
243
|
+
if (!entry.cue || entry.cue.expiresAt <= now) {
|
|
244
|
+
rmSync(entry.filePath, { force: true });
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
})
|
|
249
|
+
.sort((left, right) => (left.cue?.createdAt ?? 0) - (right.cue?.createdAt ?? 0));
|
|
250
|
+
while (retained.length >= MAX_PENDING_RESULT_CUES) {
|
|
251
|
+
const oldest = retained.shift();
|
|
252
|
+
if (oldest)
|
|
253
|
+
rmSync(oldest.filePath, { force: true });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export function rememberPendingResultCue(input) {
|
|
257
|
+
const toolUseId = validCursorToolUseId(input.toolUseId);
|
|
258
|
+
if (!input.sessionId || !toolUseId || !input.target)
|
|
259
|
+
return false;
|
|
260
|
+
const now = input.now ?? Date.now();
|
|
261
|
+
const filePath = cuePath(input.sessionId, toolUseId);
|
|
262
|
+
removeExpiredAndBound(input.sessionId, now);
|
|
263
|
+
return writeCue(filePath, {
|
|
264
|
+
version: 1,
|
|
265
|
+
targetKey: hash(input.target),
|
|
266
|
+
createdAt: now,
|
|
267
|
+
expiresAt: now + RESULT_CUE_TTL_MS,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
export function attachPendingResultCue(input) {
|
|
271
|
+
if (!input.sessionId || !input.target)
|
|
272
|
+
return false;
|
|
273
|
+
const excerpt = safeCursorResultText(input.excerpt);
|
|
274
|
+
if (!excerpt)
|
|
275
|
+
return false;
|
|
276
|
+
const now = input.now ?? Date.now();
|
|
277
|
+
const targetKey = hash(input.target);
|
|
278
|
+
const candidates = cueFiles(input.sessionId)
|
|
279
|
+
.map((filePath) => ({ filePath, cue: readCue(filePath) }))
|
|
280
|
+
.filter((entry) => !!entry.cue &&
|
|
281
|
+
entry.cue.expiresAt > now &&
|
|
282
|
+
entry.cue.targetKey === targetKey &&
|
|
283
|
+
entry.cue.excerpt === undefined)
|
|
284
|
+
.sort((left, right) => right.cue.createdAt - left.cue.createdAt);
|
|
285
|
+
const matched = candidates[0];
|
|
286
|
+
if (!matched) {
|
|
287
|
+
// Dedicated BeforeReadFile/AfterShellExecution payloads do not carry the
|
|
288
|
+
// generic tool_use_id. Keep one target-keyed fallback so the later generic
|
|
289
|
+
// PostToolUse can bind it to its validated native action id. The key and
|
|
290
|
+
// filename are still hashes; no path or command becomes a path segment.
|
|
291
|
+
removeExpiredAndBound(input.sessionId, now);
|
|
292
|
+
return writeCue(cuePath(input.sessionId, `target:${targetKey}`), {
|
|
293
|
+
version: 1,
|
|
294
|
+
targetKey,
|
|
295
|
+
excerpt,
|
|
296
|
+
createdAt: now,
|
|
297
|
+
expiresAt: now + RESULT_CUE_TTL_MS,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return writeCue(matched.filePath, { ...matched.cue, excerpt });
|
|
301
|
+
}
|
|
302
|
+
export function takePendingResultCue(input) {
|
|
303
|
+
const toolUseId = validCursorToolUseId(input.toolUseId);
|
|
304
|
+
if (!input.sessionId || !toolUseId || !input.target)
|
|
305
|
+
return undefined;
|
|
306
|
+
const filePath = cuePath(input.sessionId, toolUseId);
|
|
307
|
+
const cue = readCue(filePath);
|
|
308
|
+
const now = input.now ?? Date.now();
|
|
309
|
+
if (!cue) {
|
|
310
|
+
const targetKey = hash(input.target);
|
|
311
|
+
const fallback = cueFiles(input.sessionId)
|
|
312
|
+
.map((candidatePath) => ({ candidatePath, cue: readCue(candidatePath) }))
|
|
313
|
+
.filter((entry) => !!entry.cue &&
|
|
314
|
+
entry.cue.expiresAt > now &&
|
|
315
|
+
entry.cue.targetKey === targetKey &&
|
|
316
|
+
entry.cue.excerpt !== undefined)
|
|
317
|
+
.sort((left, right) => right.cue.createdAt - left.cue.createdAt)[0];
|
|
318
|
+
if (!fallback)
|
|
319
|
+
return undefined;
|
|
320
|
+
rmSync(fallback.candidatePath, { force: true });
|
|
321
|
+
if (fallback.cue.excerpt === undefined)
|
|
322
|
+
return undefined;
|
|
323
|
+
return { excerpt: fallback.cue.excerpt };
|
|
324
|
+
}
|
|
325
|
+
if (cue.expiresAt <= now) {
|
|
326
|
+
rmSync(filePath, { force: true });
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
if (cue.targetKey !== hash(input.target))
|
|
330
|
+
return undefined;
|
|
331
|
+
rmSync(filePath, { force: true });
|
|
332
|
+
return cue.excerpt === undefined ? undefined : { excerpt: cue.excerpt };
|
|
333
|
+
}
|
|
334
|
+
export function discardPendingResultCue(input) {
|
|
335
|
+
if (!input.sessionId)
|
|
336
|
+
return false;
|
|
337
|
+
const toolUseId = validCursorToolUseId(input.toolUseId);
|
|
338
|
+
let removed = false;
|
|
339
|
+
if (toolUseId) {
|
|
340
|
+
const filePath = cuePath(input.sessionId, toolUseId);
|
|
341
|
+
const existed = existsSync(filePath);
|
|
342
|
+
rmSync(filePath, { force: true });
|
|
343
|
+
removed = existed;
|
|
344
|
+
}
|
|
345
|
+
if (!input.target)
|
|
346
|
+
return removed;
|
|
347
|
+
const targetKey = hash(input.target);
|
|
348
|
+
for (const filePath of cueFiles(input.sessionId)) {
|
|
349
|
+
if (readCue(filePath)?.targetKey !== targetKey)
|
|
350
|
+
continue;
|
|
351
|
+
rmSync(filePath, { force: true });
|
|
352
|
+
removed = true;
|
|
353
|
+
}
|
|
354
|
+
return removed;
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=resultCue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultCue.js","sourceRoot":"","sources":["../../src/lib/resultCue.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
export declare const STALE_SESSION_MS: number;
|
|
16
16
|
/** A named file inside this session's private directory. */
|
|
17
17
|
export declare function sessionStateFilePath(sessionId: string, fileName: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Remove exactly one hashed session directory without following links.
|
|
20
|
+
* SessionEnd cleanup is best effort: a hostile or malformed state path must
|
|
21
|
+
* never block the host from closing the session.
|
|
22
|
+
*/
|
|
23
|
+
export declare function removeSessionState(sessionId: string): boolean;
|
|
18
24
|
/**
|
|
19
25
|
* Remove abandoned session directories. Runs once per SessionStart and checks
|
|
20
26
|
* the directory plus each direct child before deletion. Never follows links.
|
package/dist/lib/sessionState.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* short-TTL per-event dedup markers and is not per-session state.
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from 'node:crypto';
|
|
15
|
-
import { existsSync, lstatSync, readdirSync, rmSync } from 'node:fs';
|
|
15
|
+
import { existsSync, lstatSync, readdirSync, rmSync, unlinkSync } from 'node:fs';
|
|
16
16
|
import { tmpdir } from 'node:os';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
18
|
/** Sessions older than this are abandoned; Cursor fires no reliable end event. */
|
|
@@ -25,6 +25,34 @@ function privateRoot() {
|
|
|
25
25
|
export function sessionStateFilePath(sessionId, fileName) {
|
|
26
26
|
return join(privateRoot(), createHash('sha256').update(sessionId).digest('hex'), fileName);
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Remove exactly one hashed session directory without following links.
|
|
30
|
+
* SessionEnd cleanup is best effort: a hostile or malformed state path must
|
|
31
|
+
* never block the host from closing the session.
|
|
32
|
+
*/
|
|
33
|
+
export function removeSessionState(sessionId) {
|
|
34
|
+
if (!sessionId)
|
|
35
|
+
return false;
|
|
36
|
+
const root = privateRoot();
|
|
37
|
+
const directory = join(root, createHash('sha256').update(sessionId).digest('hex'));
|
|
38
|
+
try {
|
|
39
|
+
if (!existsSync(root) || lstatSync(root).isSymbolicLink() || !existsSync(directory)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const state = lstatSync(directory);
|
|
43
|
+
if (state.isSymbolicLink()) {
|
|
44
|
+
unlinkSync(directory);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (!state.isDirectory())
|
|
48
|
+
return false;
|
|
49
|
+
rmSync(directory, { recursive: true, force: true });
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
28
56
|
/**
|
|
29
57
|
* Remove abandoned session directories. Runs once per SessionStart and checks
|
|
30
58
|
* the directory plus each direct child before deletion. Never follows links.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionState.js","sourceRoot":"","sources":["../../src/lib/sessionState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"sessionState.js","sourceRoot":"","sources":["../../src/lib/sessionState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpD,SAAS,WAAW;IAClB,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7E,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,GAAG,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,QAAgB;IACtE,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC7F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnF,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACpF,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3B,UAAU,CAAC,SAAS,CAAC,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;YAAE,OAAO,KAAK,CAAC;QACvC,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAC5D,MAAM,IAAI,GAAG,WAAW,EAAE,CAAC;IAC3B,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE;YAAE,OAAO,CAAC,CAAC;QACpE,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;gBAC3B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,cAAc,EAAE;oBAAE,SAAS;gBACvD,IAAI,aAAa,GAAG,EAAE,CAAC,OAAO,CAAC;gBAC/B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAChF,CAAC;gBACD,IAAI,GAAG,GAAG,aAAa,GAAG,gBAAgB;oBAAE,SAAS;gBACrD,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,OAAO,IAAI,CAAC,CAAC;YACf,CAAC;YAAC,MAAM,CAAC;gBACP,yBAAyB;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export type CursorHookEvent = 'sessionStart' | 'beforeSubmitPrompt' | 'preToolUse' | 'postToolUse' | 'beforeShellExecution' | 'beforeMCPExecution' | 'afterMCPExecution' | 'afterFileEdit' | 'stop' | 'subagentStart' | 'subagentStop' | 'preCompact';
|
|
1
|
+
export type CursorHookEvent = 'sessionStart' | 'sessionEnd' | 'beforeSubmitPrompt' | 'preToolUse' | 'postToolUse' | 'postToolUseFailure' | 'beforeShellExecution' | 'afterShellExecution' | 'beforeMCPExecution' | 'afterMCPExecution' | 'beforeReadFile' | 'afterFileEdit' | 'stop' | 'subagentStart' | 'subagentStop' | 'preCompact';
|
|
2
2
|
/**
|
|
3
|
-
* Stdin payload Cursor delivers to a hook.
|
|
4
|
-
* 2026-
|
|
5
|
-
* union-typed and accessed by the dispatcher branch that
|
|
3
|
+
* Stdin payload Cursor delivers to a hook. Captured from Cursor CLI
|
|
4
|
+
* 2026.09.02-c22c1a3 on 2026-09-03. Common fields appear on every event;
|
|
5
|
+
* per-event fields are union-typed and accessed by the dispatcher branch that
|
|
6
|
+
* recognises them.
|
|
6
7
|
*/
|
|
7
8
|
export interface CursorHookInput {
|
|
8
9
|
session_id?: string;
|
|
@@ -22,12 +23,20 @@ export interface CursorHookInput {
|
|
|
22
23
|
messages_to_compact?: number;
|
|
23
24
|
is_first_compaction?: boolean;
|
|
24
25
|
tool_name?: string;
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
tool_use_id?: string;
|
|
27
|
+
tool_input?: string | Record<string, unknown>;
|
|
28
|
+
tool_output?: string;
|
|
27
29
|
command?: string;
|
|
30
|
+
output?: string;
|
|
31
|
+
content?: string;
|
|
32
|
+
attachments?: unknown[];
|
|
28
33
|
mcp_tool?: string;
|
|
29
34
|
mcp_args?: Record<string, unknown>;
|
|
30
35
|
mcp_result?: unknown;
|
|
36
|
+
result_json?: string;
|
|
37
|
+
mcp_server_name?: string;
|
|
38
|
+
mcp_server_url?: string;
|
|
39
|
+
url?: string;
|
|
31
40
|
file_path?: string;
|
|
32
41
|
followup_count?: number;
|
|
33
42
|
/** Stop-loop counter Cursor actually sends (docs say followup_count; real payloads carry loop_count). */
|
|
@@ -42,6 +51,16 @@ export interface CursorHookInput {
|
|
|
42
51
|
git_branch?: string;
|
|
43
52
|
status?: string;
|
|
44
53
|
duration_ms?: number;
|
|
54
|
+
duration?: number;
|
|
55
|
+
context_tokens?: number;
|
|
56
|
+
context_window_size?: number;
|
|
57
|
+
message_count?: number;
|
|
58
|
+
trigger?: string;
|
|
59
|
+
reason?: string;
|
|
60
|
+
final_status?: string;
|
|
61
|
+
error_message?: string;
|
|
62
|
+
failure_type?: string;
|
|
63
|
+
is_interrupt?: boolean;
|
|
45
64
|
}
|
|
46
65
|
/**
|
|
47
66
|
* Cursor accepts a small family of output shapes depending on the event.
|
|
@@ -137,9 +156,8 @@ export interface McpPrecheckResponse {
|
|
|
137
156
|
* docs/development/cursor-channel-probe.md): `agent_message` on the gate
|
|
138
157
|
* hooks (`preToolUse`, `beforeShellExecution`, `beforeMCPExecution`);
|
|
139
158
|
* `additional_context` on `beforeSubmitPrompt` (undocumented by Cursor, the
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* remains bugged/not model-visible); `followup_message` on `stop`
|
|
159
|
+
* compatibility per-prompt channel) and on `sessionStart` / `postToolUse`
|
|
160
|
+
* (current CLI 2026.09.02); `followup_message` on `stop`
|
|
143
161
|
* (auto-submitted as the next user message); and a synchronous `deny` on the
|
|
144
162
|
* gate hooks. On deny/ask, Cursor forwards `user_message` to the model and
|
|
145
163
|
* drops `agent_message` — the reverse of Cursor's own docs — so
|
|
@@ -157,9 +175,8 @@ export interface InjectionsResponse {
|
|
|
157
175
|
confirmedJitDeliveryRefs?: string[];
|
|
158
176
|
discardedJitDeliveryRefs?: string[];
|
|
159
177
|
/**
|
|
160
|
-
* Server-selected rendering channel (Cursor only). 'agent_message'
|
|
161
|
-
*
|
|
162
|
-
* field (verified channel; additional_context is bugged in Cursor).
|
|
178
|
+
* Server-selected rendering channel (Cursor only). 'agent_message' renders
|
|
179
|
+
* injections through gate-hook agent_message fields for compatibility modes.
|
|
163
180
|
* Absent or 'legacy' — historical rendering. Controlled server-side by
|
|
164
181
|
* MNEMONIK_CURSOR_INJECTION_CHANNEL so a rollback needs no republish.
|
|
165
182
|
*/
|
|
@@ -167,8 +184,8 @@ export interface InjectionsResponse {
|
|
|
167
184
|
/**
|
|
168
185
|
* Files the server asks the hook to write to the workspace, idempotently
|
|
169
186
|
* (skip-if-unchanged). Currently only the Cursor memory-tools rule file on
|
|
170
|
-
* SessionStart. Independent of `injections` — present
|
|
171
|
-
*
|
|
187
|
+
* SessionStart and UserPromptSubmit. Independent of `injections` — present
|
|
188
|
+
* even when `injections` is empty.
|
|
172
189
|
*/
|
|
173
190
|
filesToWrite?: Array<{
|
|
174
191
|
path: string;
|
package/dist/lib/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AA8PA,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnemonik/cursor-hooks",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.71",
|
|
4
4
|
"description": "Cursor IDE hooks for Mnemonik memory awareness and host-side grounding gates. Hooks only: does not install skills, rules, AGENTS.md, .mnemonik.json, or MCP config.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,11 +33,6 @@
|
|
|
33
33
|
],
|
|
34
34
|
"license": "SEE LICENSE IN LICENSE",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@mnemonik/shared": "7.
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"@types/node": "^20.19.43",
|
|
40
|
-
"@typescript/native": "npm:typescript@7.0.2",
|
|
41
|
-
"vitest": "^4.1.11"
|
|
36
|
+
"@mnemonik/shared": "7.81.1"
|
|
42
37
|
}
|
|
43
38
|
}
|