@mnemonik/grok-hooks 0.1.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/dispatcher.d.ts +14 -0
- package/dist/dispatcher.js +289 -0
- package/dist/dispatcher.js.map +1 -0
- package/dist/hook.d.ts +2 -0
- package/dist/hook.js +42 -0
- package/dist/hook.js.map +1 -0
- package/dist/install.d.ts +28 -0
- package/dist/install.js +364 -0
- package/dist/install.js.map +1 -0
- package/dist/lib/env.d.ts +4 -0
- package/dist/lib/env.js +65 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/http.d.ts +39 -0
- package/dist/lib/http.js +92 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/identity.d.ts +6 -0
- package/dist/lib/identity.js +34 -0
- package/dist/lib/identity.js.map +1 -0
- package/dist/lib/shellTokens.d.ts +1 -0
- package/dist/lib/shellTokens.js +28 -0
- package/dist/lib/shellTokens.js.map +1 -0
- package/dist/lib/types.d.ts +63 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GrokDecision, GrokHookInput } from './lib/types.js';
|
|
2
|
+
export declare function captureHostTz(): string | undefined;
|
|
3
|
+
export declare function resolveTargetFile(input: GrokHookInput): string | null;
|
|
4
|
+
export declare function isTrivialSearchReplace(toolInput: Record<string, unknown> | undefined): boolean;
|
|
5
|
+
export declare function extractMcpCall(input: GrokHookInput): {
|
|
6
|
+
tool: string;
|
|
7
|
+
args: Record<string, unknown>;
|
|
8
|
+
} | null;
|
|
9
|
+
export declare function extractMcpResult(toolResult: unknown): unknown;
|
|
10
|
+
/**
|
|
11
|
+
* Return the exact JSON Grok should receive on stdout, or null for an empty
|
|
12
|
+
* successful response. All passive events deliberately return null.
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleHook(input: GrokHookInput): Promise<GrokDecision | null>;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { isNonInteractive, parseBypass, resolveConfig } from './lib/env.js';
|
|
4
|
+
import { postBypass, postEdit, postInjections, postMcpPrecheck, postMcpRecord, postPreCompact, } from './lib/http.js';
|
|
5
|
+
import { findProjectIdentity } from './lib/identity.js';
|
|
6
|
+
import { matchIrreducibleDeny } from './lib/shellTokens.js';
|
|
7
|
+
const MCP_TOOL = /^mnemonik__(.+)$/;
|
|
8
|
+
const GROK_PRETOOL_DIRECTIVES = new Set([
|
|
9
|
+
'file_context_required',
|
|
10
|
+
'policy_check_required',
|
|
11
|
+
'checkpoint_required',
|
|
12
|
+
]);
|
|
13
|
+
export function captureHostTz() {
|
|
14
|
+
try {
|
|
15
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return process.env.TZ;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function resolveTargetFile(input) {
|
|
22
|
+
const raw = typeof input.toolInput?.file_path === 'string' ? input.toolInput.file_path : null;
|
|
23
|
+
if (!raw)
|
|
24
|
+
return null;
|
|
25
|
+
return isAbsolute(raw) ? raw : resolve(input.cwd, raw);
|
|
26
|
+
}
|
|
27
|
+
export function isTrivialSearchReplace(toolInput) {
|
|
28
|
+
if (!toolInput)
|
|
29
|
+
return false;
|
|
30
|
+
const oldText = typeof toolInput.old_string === 'string' ? toolInput.old_string : null;
|
|
31
|
+
const newText = typeof toolInput.new_string === 'string' ? toolInput.new_string : null;
|
|
32
|
+
if (oldText === null || newText === null || oldText.length === 0 || newText.length === 0) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (oldText.includes('\n') || newText.includes('\n'))
|
|
36
|
+
return false;
|
|
37
|
+
if (oldText.length > 100 || newText.length > 100)
|
|
38
|
+
return false;
|
|
39
|
+
return Math.min(oldText.length, newText.length) / Math.max(oldText.length, newText.length) >= 0.6;
|
|
40
|
+
}
|
|
41
|
+
export function extractMcpCall(input) {
|
|
42
|
+
const match = MCP_TOOL.exec(input.toolName ?? '');
|
|
43
|
+
if (!match?.[1])
|
|
44
|
+
return null;
|
|
45
|
+
const wrapped = input.toolInput?.tool_input;
|
|
46
|
+
const args = wrapped !== null && typeof wrapped === 'object' && !Array.isArray(wrapped)
|
|
47
|
+
? wrapped
|
|
48
|
+
: (input.toolInput ?? {});
|
|
49
|
+
return { tool: match[1], args };
|
|
50
|
+
}
|
|
51
|
+
export function extractMcpResult(toolResult) {
|
|
52
|
+
if (toolResult === null || typeof toolResult !== 'object' || Array.isArray(toolResult)) {
|
|
53
|
+
return toolResult;
|
|
54
|
+
}
|
|
55
|
+
const result = toolResult;
|
|
56
|
+
const output = result.output;
|
|
57
|
+
if (output === null || typeof output !== 'object' || Array.isArray(output))
|
|
58
|
+
return toolResult;
|
|
59
|
+
const okay = output.OkayOutput;
|
|
60
|
+
if (typeof okay !== 'string')
|
|
61
|
+
return output;
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(okay);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return okay;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function eventName(input) {
|
|
70
|
+
return input.hookEventName.replace(/_/g, '').toLowerCase();
|
|
71
|
+
}
|
|
72
|
+
function denyFromInjection(injection) {
|
|
73
|
+
const action = injection.hostDecision === 'ask'
|
|
74
|
+
? 'Pause and ask the user for confirmation. Retry only after they approve.'
|
|
75
|
+
: 'Apply this required Mnemonik context or policy before retrying.';
|
|
76
|
+
return {
|
|
77
|
+
decision: 'deny',
|
|
78
|
+
reason: `${action}\n\n${injection.envelope}`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Defense in depth for Grok's deny-only PreToolUse channel. The server filters
|
|
83
|
+
* candidates before bus evaluation, but the published client must not turn a
|
|
84
|
+
* future advisory response into a blocking decision if that contract drifts.
|
|
85
|
+
*/
|
|
86
|
+
function firstDeliverablePreToolDirective(result) {
|
|
87
|
+
if (!result?.ok)
|
|
88
|
+
return null;
|
|
89
|
+
return (result.injections.find((injection) => injection.kind === 'directive' && GROK_PRETOOL_DIRECTIVES.has(injection.signal)) ?? null);
|
|
90
|
+
}
|
|
91
|
+
async function common(input) {
|
|
92
|
+
const [config, identity] = await Promise.all([resolveConfig(), findProjectIdentity(input.cwd)]);
|
|
93
|
+
return { config, projectId: identity?.projectId };
|
|
94
|
+
}
|
|
95
|
+
async function observe(input, event, config, projectId, toolName, toolArgs) {
|
|
96
|
+
await postInjections({
|
|
97
|
+
config,
|
|
98
|
+
cwd: input.cwd,
|
|
99
|
+
projectId,
|
|
100
|
+
sessionId: input.sessionId,
|
|
101
|
+
event,
|
|
102
|
+
observeOnly: true,
|
|
103
|
+
prompt: event === 'UserPromptSubmit' ? input.prompt : undefined,
|
|
104
|
+
toolName,
|
|
105
|
+
toolArgs,
|
|
106
|
+
tz: captureHostTz(),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async function handlePreToolUse(input, config, projectId) {
|
|
110
|
+
const mcp = extractMcpCall(input);
|
|
111
|
+
if (mcp) {
|
|
112
|
+
if (!config.apiKey)
|
|
113
|
+
return null;
|
|
114
|
+
const precheck = await postMcpPrecheck({
|
|
115
|
+
config,
|
|
116
|
+
cwd: input.cwd,
|
|
117
|
+
projectId,
|
|
118
|
+
sessionId: input.sessionId,
|
|
119
|
+
...mcp,
|
|
120
|
+
});
|
|
121
|
+
if (precheck?.ok && (precheck.verdict === 'deny' || precheck.verdict === 'ask')) {
|
|
122
|
+
const instruction = precheck.verdict === 'ask'
|
|
123
|
+
? 'Ask the user for confirmation, then retry this Mnemonik call if approved.'
|
|
124
|
+
: 'Do not run this Mnemonik call.';
|
|
125
|
+
return {
|
|
126
|
+
decision: 'deny',
|
|
127
|
+
reason: `${instruction}\n\n${precheck.agentMessage ?? precheck.ruleId ?? 'Mnemonik ordering rule blocked this call.'}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
if (input.toolName === 'run_terminal_command') {
|
|
133
|
+
const command = typeof input.toolInput?.command === 'string' ? input.toolInput.command : '';
|
|
134
|
+
const irreducible = matchIrreducibleDeny(command);
|
|
135
|
+
if (irreducible) {
|
|
136
|
+
return {
|
|
137
|
+
decision: 'deny',
|
|
138
|
+
reason: `Mnemonik blocked catastrophic command pattern ${irreducible}.`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (!config.apiKey)
|
|
142
|
+
return null;
|
|
143
|
+
const result = await postInjections({
|
|
144
|
+
config,
|
|
145
|
+
cwd: input.cwd,
|
|
146
|
+
projectId,
|
|
147
|
+
sessionId: input.sessionId,
|
|
148
|
+
event: 'PreToolUse',
|
|
149
|
+
toolName: input.toolName,
|
|
150
|
+
toolArgs: input.toolInput,
|
|
151
|
+
tz: captureHostTz(),
|
|
152
|
+
});
|
|
153
|
+
const directive = firstDeliverablePreToolDirective(result);
|
|
154
|
+
return directive ? denyFromInjection(directive) : null;
|
|
155
|
+
}
|
|
156
|
+
if (input.toolName === 'search_replace') {
|
|
157
|
+
if (!config.apiKey)
|
|
158
|
+
return null;
|
|
159
|
+
const target = resolveTargetFile(input);
|
|
160
|
+
if ((target && !existsSync(target)) || isTrivialSearchReplace(input.toolInput))
|
|
161
|
+
return null;
|
|
162
|
+
const result = await postInjections({
|
|
163
|
+
config,
|
|
164
|
+
cwd: input.cwd,
|
|
165
|
+
projectId,
|
|
166
|
+
sessionId: input.sessionId,
|
|
167
|
+
event: 'PreToolUse',
|
|
168
|
+
toolName: input.toolName,
|
|
169
|
+
toolArgs: input.toolInput,
|
|
170
|
+
filePaths: target ? [target] : [],
|
|
171
|
+
tz: captureHostTz(),
|
|
172
|
+
});
|
|
173
|
+
const directive = firstDeliverablePreToolDirective(result);
|
|
174
|
+
return directive ? denyFromInjection(directive) : null;
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
async function handlePostToolUse(input, config, projectId) {
|
|
179
|
+
if (!config.apiKey)
|
|
180
|
+
return;
|
|
181
|
+
const mcp = extractMcpCall(input);
|
|
182
|
+
if (mcp) {
|
|
183
|
+
await postMcpRecord({
|
|
184
|
+
config,
|
|
185
|
+
cwd: input.cwd,
|
|
186
|
+
projectId,
|
|
187
|
+
sessionId: input.sessionId,
|
|
188
|
+
...mcp,
|
|
189
|
+
result: extractMcpResult(input.toolResult),
|
|
190
|
+
});
|
|
191
|
+
await observe(input, 'PostToolUse', config, projectId, mcp.tool, mcp.args);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (input.toolName === 'search_replace') {
|
|
195
|
+
const target = resolveTargetFile(input);
|
|
196
|
+
if (target) {
|
|
197
|
+
await postEdit({
|
|
198
|
+
config,
|
|
199
|
+
cwd: input.cwd,
|
|
200
|
+
projectId,
|
|
201
|
+
sessionId: input.sessionId,
|
|
202
|
+
filePath: target,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
await observe(input, 'PostToolUse', config, projectId, input.toolName, input.toolInput);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function handleStop(input, config, projectId) {
|
|
209
|
+
// Grok emits one final observe-only Stop during shutdown/channel close.
|
|
210
|
+
// It also re-fires Stop after a continuation; allow that second stop to avoid
|
|
211
|
+
// an agent loop if the first reminder could not be acted on.
|
|
212
|
+
if (!config.apiKey || input.reason !== 'end_turn' || input.stopHookActive)
|
|
213
|
+
return null;
|
|
214
|
+
const result = await postInjections({
|
|
215
|
+
config,
|
|
216
|
+
cwd: input.cwd,
|
|
217
|
+
projectId,
|
|
218
|
+
sessionId: input.sessionId,
|
|
219
|
+
event: 'Stop',
|
|
220
|
+
stopHookActive: input.stopHookActive,
|
|
221
|
+
tz: captureHostTz(),
|
|
222
|
+
});
|
|
223
|
+
const checkpoint = result?.ok
|
|
224
|
+
? result.injections.find((injection) => injection.kind === 'directive' && injection.signal === 'checkpoint_required')
|
|
225
|
+
: undefined;
|
|
226
|
+
return checkpoint
|
|
227
|
+
? {
|
|
228
|
+
hookSpecificOutput: {
|
|
229
|
+
hookEventName: 'Stop',
|
|
230
|
+
additionalContext: checkpoint.envelope,
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
: null;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Return the exact JSON Grok should receive on stdout, or null for an empty
|
|
237
|
+
* successful response. All passive events deliberately return null.
|
|
238
|
+
*/
|
|
239
|
+
export async function handleHook(input) {
|
|
240
|
+
if (isNonInteractive())
|
|
241
|
+
return null;
|
|
242
|
+
const bypass = parseBypass();
|
|
243
|
+
if (bypass.invalidReason) {
|
|
244
|
+
process.stderr.write(`mnemonik-grok-hook: invalid bypass envelope - ${bypass.invalidReason}\n`);
|
|
245
|
+
}
|
|
246
|
+
const { config, projectId } = await common(input);
|
|
247
|
+
if (bypass.active) {
|
|
248
|
+
await postBypass({
|
|
249
|
+
config,
|
|
250
|
+
cwd: input.cwd,
|
|
251
|
+
projectId,
|
|
252
|
+
sessionId: input.sessionId,
|
|
253
|
+
tool: input.toolName ?? input.hookEventName,
|
|
254
|
+
bypassType: bypass.type ?? 'unknown',
|
|
255
|
+
detail: bypass.detail,
|
|
256
|
+
});
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
switch (eventName(input)) {
|
|
260
|
+
case 'pretooluse':
|
|
261
|
+
return handlePreToolUse(input, config, projectId);
|
|
262
|
+
case 'posttooluse':
|
|
263
|
+
await handlePostToolUse(input, config, projectId);
|
|
264
|
+
return null;
|
|
265
|
+
case 'sessionstart':
|
|
266
|
+
if (config.apiKey)
|
|
267
|
+
await observe(input, 'SessionStart', config, projectId);
|
|
268
|
+
return null;
|
|
269
|
+
case 'userpromptsubmit':
|
|
270
|
+
if (config.apiKey)
|
|
271
|
+
await observe(input, 'UserPromptSubmit', config, projectId);
|
|
272
|
+
return null;
|
|
273
|
+
case 'precompact':
|
|
274
|
+
if (config.apiKey) {
|
|
275
|
+
await postPreCompact({
|
|
276
|
+
config,
|
|
277
|
+
cwd: input.cwd,
|
|
278
|
+
projectId,
|
|
279
|
+
sessionId: input.sessionId,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
case 'stop':
|
|
284
|
+
return handleStop(input, config, projectId);
|
|
285
|
+
default:
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"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"}
|
package/dist/hook.d.ts
ADDED
package/dist/hook.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { stderr, stdin, stdout } from 'node:process';
|
|
3
|
+
import { handleHook } from './dispatcher.js';
|
|
4
|
+
async function readInput() {
|
|
5
|
+
let body = '';
|
|
6
|
+
stdin.setEncoding('utf8');
|
|
7
|
+
for await (const chunk of stdin) {
|
|
8
|
+
body += chunk;
|
|
9
|
+
if (body.length > 1_048_576)
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const value = JSON.parse(body);
|
|
14
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
15
|
+
return null;
|
|
16
|
+
const input = value;
|
|
17
|
+
if (typeof input.hookEventName !== 'string' ||
|
|
18
|
+
typeof input.sessionId !== 'string' ||
|
|
19
|
+
typeof input.cwd !== 'string') {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return input;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const input = await readInput();
|
|
30
|
+
if (input) {
|
|
31
|
+
const decision = await handleHook(input);
|
|
32
|
+
if (decision)
|
|
33
|
+
stdout.write(JSON.stringify(decision));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// Every Grok hook failure is fail-open. Keep stdout empty: malformed JSON on
|
|
38
|
+
// an active hook also fails open, but an empty success is cleaner and leaves
|
|
39
|
+
// Grok's hook diagnostics unambiguous.
|
|
40
|
+
stderr.write(`mnemonik-grok-hook: fail-open — ${error instanceof Error ? error.message : String(error)}\n`);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=hook.js.map
|
package/dist/hook.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,KAAK,UAAU,SAAS;IACtB,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAChC,IAAI,IAAI,KAAK,CAAC;QACd,IAAI,IAAI,CAAC,MAAM,GAAG,SAAS;YAAE,OAAO,IAAI,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;QAC1C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACrF,MAAM,KAAK,GAAG,KAA+B,CAAC;QAC9C,IACE,OAAO,KAAK,CAAC,aAAa,KAAK,QAAQ;YACvC,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;YACnC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,EAC7B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAsB,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,IAAI,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAChC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,QAAQ;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,6EAA6E;IAC7E,6EAA6E;IAC7E,uCAAuC;IACvC,MAAM,CAAC,KAAK,CACV,mCAAmC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAC9F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
type Scope = 'host' | 'project';
|
|
3
|
+
interface HookHandler {
|
|
4
|
+
type: string;
|
|
5
|
+
command?: string;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
interface HookGroup {
|
|
10
|
+
matcher?: string;
|
|
11
|
+
hooks?: HookHandler[];
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface GrokHookFile {
|
|
15
|
+
hooks?: Record<string, HookGroup[]>;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
export declare function parseArgs(argv: string[]): {
|
|
19
|
+
scope: Scope;
|
|
20
|
+
};
|
|
21
|
+
export declare function resolveHookConfigPath(scope: Scope, cwd: string, env?: Partial<Record<'HOME' | 'USERPROFILE' | 'GROK_HOME', string | undefined>>): string;
|
|
22
|
+
export declare function buildManagedCommand(dispatcherPath: string): string;
|
|
23
|
+
/** Pure merge used by installer tests and the CLI. */
|
|
24
|
+
export declare function patchHookFile(file: GrokHookFile, command: string, install: boolean): GrokHookFile;
|
|
25
|
+
/** An absent alternate scope must stay absent; only clean up a file that exists. */
|
|
26
|
+
export declare function shouldRewriteAlternate(raw: string | null, next: string | null): boolean;
|
|
27
|
+
export declare function runInstaller(argv: string[]): Promise<number>;
|
|
28
|
+
export {};
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
3
|
+
import { cp, lstat, mkdir, open, readFile, readdir, realpath, rename, rm, stat, } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
const PACKAGE_NAME = '@mnemonik/grok-hooks';
|
|
7
|
+
const OWNER = 'grok-hooks';
|
|
8
|
+
const LAUNCHER_SOURCE = "const u=Buffer.from(process.argv[1],'base64url').toString('utf8');process.argv[1]=require('node:url').fileURLToPath(u);import(u).catch(e=>{console.error(e);process.exitCode=1})";
|
|
9
|
+
function code(error) {
|
|
10
|
+
return error && typeof error === 'object' && 'code' in error
|
|
11
|
+
? String(error.code)
|
|
12
|
+
: undefined;
|
|
13
|
+
}
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
export function parseArgs(argv) {
|
|
18
|
+
let scope = 'host';
|
|
19
|
+
let explicit = null;
|
|
20
|
+
let sawInstall = false;
|
|
21
|
+
for (const argument of argv) {
|
|
22
|
+
if (argument === 'install' && !sawInstall) {
|
|
23
|
+
sawInstall = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const parsed = argument === '--scope=project'
|
|
27
|
+
? 'project'
|
|
28
|
+
: argument === '--scope=user' || argument === '--scope=host'
|
|
29
|
+
? 'host'
|
|
30
|
+
: null;
|
|
31
|
+
if (!parsed)
|
|
32
|
+
throw new Error(`Unknown installer argument: ${argument}`);
|
|
33
|
+
if (explicit && explicit !== parsed)
|
|
34
|
+
throw new Error('Conflicting --scope arguments');
|
|
35
|
+
explicit = parsed;
|
|
36
|
+
scope = parsed;
|
|
37
|
+
}
|
|
38
|
+
return { scope };
|
|
39
|
+
}
|
|
40
|
+
function resolveHome(env = process.env) {
|
|
41
|
+
const home = env.HOME?.trim() || env.USERPROFILE?.trim();
|
|
42
|
+
if (!home)
|
|
43
|
+
throw new Error('Could not determine home directory for Grok hook install');
|
|
44
|
+
return home;
|
|
45
|
+
}
|
|
46
|
+
export function resolveHookConfigPath(scope, cwd, env = process.env) {
|
|
47
|
+
if (scope === 'project')
|
|
48
|
+
return join(cwd, '.grok', 'hooks', 'mnemonik.json');
|
|
49
|
+
const grokHome = env.GROK_HOME?.trim() || join(resolveHome(env), '.grok');
|
|
50
|
+
return join(grokHome, 'hooks', 'mnemonik.json');
|
|
51
|
+
}
|
|
52
|
+
export function buildManagedCommand(dispatcherPath) {
|
|
53
|
+
const token = Buffer.from(pathToFileURL(resolve(dispatcherPath)).href, 'utf8').toString('base64url');
|
|
54
|
+
return `node -e ${JSON.stringify(LAUNCHER_SOURCE)} -- ${token} --mnemonik-owner=${OWNER}`;
|
|
55
|
+
}
|
|
56
|
+
function isManagedCommand(command) {
|
|
57
|
+
if (command.endsWith(` --mnemonik-owner=${OWNER}`))
|
|
58
|
+
return true;
|
|
59
|
+
const normalized = command.replace(/\\/g, '/');
|
|
60
|
+
return (normalized.includes(`/node_modules/${PACKAGE_NAME}/dist/hook.js`) ||
|
|
61
|
+
normalized.includes('/packages/grok-hooks/dist/hook.js'));
|
|
62
|
+
}
|
|
63
|
+
function validateHookFile(value) {
|
|
64
|
+
if (!isRecord(value))
|
|
65
|
+
throw new Error('expected a top-level JSON object');
|
|
66
|
+
if (value.hooks === undefined)
|
|
67
|
+
return value;
|
|
68
|
+
if (!isRecord(value.hooks))
|
|
69
|
+
throw new Error('expected "hooks" to be a JSON object');
|
|
70
|
+
for (const [event, groups] of Object.entries(value.hooks)) {
|
|
71
|
+
if (!Array.isArray(groups) || !groups.every(isRecord)) {
|
|
72
|
+
throw new Error(`expected "hooks.${event}" to be an array of hook groups`);
|
|
73
|
+
}
|
|
74
|
+
for (const group of groups) {
|
|
75
|
+
if (!Array.isArray(group.hooks) || !group.hooks.every(isRecord)) {
|
|
76
|
+
throw new Error(`expected "hooks.${event}[].hooks" to be an array`);
|
|
77
|
+
}
|
|
78
|
+
for (const handler of group.hooks) {
|
|
79
|
+
if (handler.command !== undefined && typeof handler.command !== 'string') {
|
|
80
|
+
throw new Error(`expected "hooks.${event}[].hooks[].command" to be a string`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
function removeManaged(file) {
|
|
88
|
+
if (!file.hooks)
|
|
89
|
+
return;
|
|
90
|
+
for (const [event, groups] of Object.entries(file.hooks)) {
|
|
91
|
+
const remaining = [];
|
|
92
|
+
for (const group of groups) {
|
|
93
|
+
const handlers = (group.hooks ?? []).filter((handler) => typeof handler.command !== 'string' || !isManagedCommand(handler.command));
|
|
94
|
+
if (handlers.length > 0)
|
|
95
|
+
remaining.push({ ...group, hooks: handlers });
|
|
96
|
+
}
|
|
97
|
+
if (remaining.length > 0)
|
|
98
|
+
file.hooks[event] = remaining;
|
|
99
|
+
else
|
|
100
|
+
delete file.hooks[event];
|
|
101
|
+
}
|
|
102
|
+
if (Object.keys(file.hooks).length === 0)
|
|
103
|
+
delete file.hooks;
|
|
104
|
+
}
|
|
105
|
+
function managedGroup(command, matcher) {
|
|
106
|
+
return {
|
|
107
|
+
...(matcher ? { matcher } : {}),
|
|
108
|
+
hooks: [{ type: 'command', command, timeout: 5 }],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Pure merge used by installer tests and the CLI. */
|
|
112
|
+
export function patchHookFile(file, command, install) {
|
|
113
|
+
const next = structuredClone(file);
|
|
114
|
+
removeManaged(next);
|
|
115
|
+
if (!install)
|
|
116
|
+
return next;
|
|
117
|
+
const hooks = (next.hooks ??= {});
|
|
118
|
+
hooks.PreToolUse = [
|
|
119
|
+
...(hooks.PreToolUse ?? []),
|
|
120
|
+
managedGroup(command, 'search_replace|run_terminal_command|mnemonik__.*'),
|
|
121
|
+
];
|
|
122
|
+
hooks.PostToolUse = [
|
|
123
|
+
...(hooks.PostToolUse ?? []),
|
|
124
|
+
managedGroup(command, 'search_replace|mnemonik__.*'),
|
|
125
|
+
];
|
|
126
|
+
hooks.SessionStart = [...(hooks.SessionStart ?? []), managedGroup(command)];
|
|
127
|
+
hooks.UserPromptSubmit = [...(hooks.UserPromptSubmit ?? []), managedGroup(command)];
|
|
128
|
+
hooks.Stop = [...(hooks.Stop ?? []), managedGroup(command)];
|
|
129
|
+
hooks.PreCompact = [...(hooks.PreCompact ?? []), managedGroup(command)];
|
|
130
|
+
return next;
|
|
131
|
+
}
|
|
132
|
+
async function readHookFile(path) {
|
|
133
|
+
try {
|
|
134
|
+
const raw = await readFile(path, 'utf8');
|
|
135
|
+
try {
|
|
136
|
+
return { file: validateHookFile(JSON.parse(raw)), raw };
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new Error(`Could not parse ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (code(error) === 'ENOENT')
|
|
144
|
+
return { file: {}, raw: null };
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function writableTarget(path) {
|
|
149
|
+
try {
|
|
150
|
+
return await realpath(path);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (code(error) !== 'ENOENT')
|
|
154
|
+
throw error;
|
|
155
|
+
try {
|
|
156
|
+
if ((await lstat(path)).isSymbolicLink()) {
|
|
157
|
+
throw new Error(`Refusing dangling configuration symlink ${path}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (inspectError) {
|
|
161
|
+
if (code(inspectError) !== 'ENOENT')
|
|
162
|
+
throw inspectError;
|
|
163
|
+
}
|
|
164
|
+
return path;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function atomicWrite(path, next, expected) {
|
|
168
|
+
const target = await writableTarget(path);
|
|
169
|
+
await mkdir(dirname(target), { recursive: true });
|
|
170
|
+
let current = null;
|
|
171
|
+
try {
|
|
172
|
+
current = await readFile(target, 'utf8');
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (code(error) !== 'ENOENT')
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
if (current !== expected)
|
|
179
|
+
throw new Error(`Configuration changed concurrently: ${path}`);
|
|
180
|
+
let mode = 0o600;
|
|
181
|
+
try {
|
|
182
|
+
mode = (await stat(target)).mode & 0o777;
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (code(error) !== 'ENOENT')
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
const temporary = join(dirname(target), `.mnemonik-${process.pid}-${randomBytes(6).toString('hex')}.json`);
|
|
189
|
+
const handle = await open(temporary, 'wx', mode);
|
|
190
|
+
try {
|
|
191
|
+
await handle.writeFile(next, 'utf8');
|
|
192
|
+
await handle.sync();
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
await handle.close();
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
let beforeRename = null;
|
|
199
|
+
try {
|
|
200
|
+
beforeRename = await readFile(target, 'utf8');
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (code(error) !== 'ENOENT')
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
if (beforeRename !== expected) {
|
|
207
|
+
throw new Error(`Configuration changed concurrently: ${path}`);
|
|
208
|
+
}
|
|
209
|
+
await rename(temporary, target);
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
await rm(temporary, { force: true }).catch(() => undefined);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function withLock(path, action) {
|
|
216
|
+
await mkdir(dirname(path), { recursive: true });
|
|
217
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
218
|
+
try {
|
|
219
|
+
await mkdir(path, { mode: 0o700 });
|
|
220
|
+
try {
|
|
221
|
+
return await action();
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
await rm(path, { recursive: true, force: true });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
if (code(error) !== 'EEXIST')
|
|
229
|
+
throw error;
|
|
230
|
+
const age = Date.now() - (await stat(path)).mtimeMs;
|
|
231
|
+
if (age > 120_000) {
|
|
232
|
+
await rm(path, { recursive: true, force: true });
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
await new Promise((done) => setTimeout(done, 20));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
throw new Error(`Timed out waiting for installer lock ${path}`);
|
|
239
|
+
}
|
|
240
|
+
async function hashTree(root) {
|
|
241
|
+
const hash = createHash('sha256');
|
|
242
|
+
const walk = async (directory, relative) => {
|
|
243
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
244
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
245
|
+
for (const entry of entries) {
|
|
246
|
+
const childRelative = relative ? join(relative, entry.name) : entry.name;
|
|
247
|
+
const child = join(directory, entry.name);
|
|
248
|
+
if (entry.isDirectory())
|
|
249
|
+
await walk(child, childRelative);
|
|
250
|
+
else if (entry.isFile()) {
|
|
251
|
+
hash.update(`${childRelative}\0`);
|
|
252
|
+
hash.update(await readFile(child));
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
throw new Error(`Unsupported runtime artifact ${child}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
await walk(root, '');
|
|
260
|
+
return hash.digest('hex');
|
|
261
|
+
}
|
|
262
|
+
async function installRuntime(home) {
|
|
263
|
+
// dist/install.js -> package root
|
|
264
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
265
|
+
const manifest = JSON.parse(await readFile(join(packageRoot, 'package.json'), 'utf8'));
|
|
266
|
+
if (manifest.name !== PACKAGE_NAME || typeof manifest.version !== 'string') {
|
|
267
|
+
throw new Error(`Invalid ${PACKAGE_NAME} package manifest`);
|
|
268
|
+
}
|
|
269
|
+
const digest = (await hashTree(join(packageRoot, 'dist'))).slice(0, 16);
|
|
270
|
+
const container = join(home, '.mnemonik', 'hooks', OWNER);
|
|
271
|
+
const root = join(container, `${manifest.version}-${digest}`);
|
|
272
|
+
const installedPackage = join(root, 'package');
|
|
273
|
+
const installedHook = join(installedPackage, 'dist', 'hook.js');
|
|
274
|
+
await mkdir(container, { recursive: true });
|
|
275
|
+
await withLock(join(container, '.install.lock'), async () => {
|
|
276
|
+
try {
|
|
277
|
+
if ((await stat(installedHook)).isFile())
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
if (code(error) !== 'ENOENT')
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
const staging = join(container, `.stage-${process.pid}-${randomBytes(6).toString('hex')}`);
|
|
285
|
+
try {
|
|
286
|
+
await mkdir(join(staging, 'package'), { recursive: true });
|
|
287
|
+
await cp(join(packageRoot, 'dist'), join(staging, 'package', 'dist'), { recursive: true });
|
|
288
|
+
await cp(join(packageRoot, 'package.json'), join(staging, 'package', 'package.json'));
|
|
289
|
+
await rename(staging, root);
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
if (code(error) !== 'EEXIST' && code(error) !== 'ENOTEMPTY')
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
await rm(staging, { recursive: true, force: true }).catch(() => undefined);
|
|
297
|
+
}
|
|
298
|
+
if (!(await stat(installedHook)).isFile()) {
|
|
299
|
+
throw new Error(`Durable Grok hook runtime did not install at ${installedHook}`);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
return installedHook;
|
|
303
|
+
}
|
|
304
|
+
/** An absent alternate scope must stay absent; only clean up a file that exists. */
|
|
305
|
+
export function shouldRewriteAlternate(raw, next) {
|
|
306
|
+
return raw !== null && next !== null && next !== raw;
|
|
307
|
+
}
|
|
308
|
+
export async function runInstaller(argv) {
|
|
309
|
+
const { scope } = parseArgs(argv);
|
|
310
|
+
const cwd = process.cwd();
|
|
311
|
+
const home = resolveHome();
|
|
312
|
+
const targetPath = resolveHookConfigPath(scope, cwd);
|
|
313
|
+
const alternatePath = resolveHookConfigPath(scope === 'host' ? 'project' : 'host', cwd);
|
|
314
|
+
const dispatcher = await installRuntime(home);
|
|
315
|
+
const command = buildManagedCommand(dispatcher);
|
|
316
|
+
const lockKey = createHash('sha256')
|
|
317
|
+
.update([resolve(targetPath), resolve(alternatePath)].sort().join('\0'))
|
|
318
|
+
.digest('hex')
|
|
319
|
+
.slice(0, 32);
|
|
320
|
+
await withLock(join(home, '.mnemonik', 'hooks', '.config-locks', `${lockKey}.lock`), async () => {
|
|
321
|
+
const target = await readHookFile(targetPath);
|
|
322
|
+
const samePath = resolve(targetPath) === resolve(alternatePath);
|
|
323
|
+
const alternate = samePath ? null : await readHookFile(alternatePath);
|
|
324
|
+
const nextTarget = `${JSON.stringify(patchHookFile(target.file, command, true), null, 2)}\n`;
|
|
325
|
+
const nextAlternate = alternate
|
|
326
|
+
? `${JSON.stringify(patchHookFile(alternate.file, command, false), null, 2)}\n`
|
|
327
|
+
: null;
|
|
328
|
+
const alternateChanged = alternate !== null && shouldRewriteAlternate(alternate.raw, nextAlternate);
|
|
329
|
+
if (alternateChanged) {
|
|
330
|
+
await atomicWrite(alternatePath, nextAlternate, alternate.raw);
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
if (nextTarget !== target.raw)
|
|
334
|
+
await atomicWrite(targetPath, nextTarget, target.raw);
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
if (alternate && alternateChanged) {
|
|
338
|
+
await atomicWrite(alternatePath, alternate.raw, nextAlternate);
|
|
339
|
+
}
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
console.log(`Mnemonik Grok hooks installed (${scope} scope).`);
|
|
344
|
+
console.log(' hooks only -> does not install or modify skills, rules, AGENTS.md, .mnemonik.json, or MCP config');
|
|
345
|
+
console.log(` config -> ${targetPath}`);
|
|
346
|
+
console.log(` dispatcher -> ${dispatcher}`);
|
|
347
|
+
if (scope === 'project') {
|
|
348
|
+
console.log(' trust -> run /hooks-trust in Grok (project hooks require folder trust)');
|
|
349
|
+
}
|
|
350
|
+
console.log('Restart Grok, then run /hooks to verify the loaded Mnemonik entries.');
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
const invokedPath = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : '';
|
|
354
|
+
if (invokedPath === import.meta.url) {
|
|
355
|
+
runInstaller(process.argv.slice(2))
|
|
356
|
+
.then((exitCode) => {
|
|
357
|
+
process.exitCode = exitCode;
|
|
358
|
+
})
|
|
359
|
+
.catch((error) => {
|
|
360
|
+
console.error(`mnemonik-grok-hooks: ${error instanceof Error ? error.message : String(error)}`);
|
|
361
|
+
process.exitCode = 1;
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type BypassEnvelope, type HookConfig } from './types.js';
|
|
2
|
+
export declare function isNonInteractive(env?: NodeJS.ProcessEnv): boolean;
|
|
3
|
+
export declare function parseBypass(env?: NodeJS.ProcessEnv): BypassEnvelope;
|
|
4
|
+
export declare function resolveConfig(env?: NodeJS.ProcessEnv): Promise<HookConfig>;
|
package/dist/lib/env.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { DEFAULT_SERVER } from './types.js';
|
|
5
|
+
const VALID_BYPASS_TYPES = new Set([
|
|
6
|
+
'typo',
|
|
7
|
+
'formatting',
|
|
8
|
+
'regenerated',
|
|
9
|
+
'already_grounded',
|
|
10
|
+
'other',
|
|
11
|
+
]);
|
|
12
|
+
export function isNonInteractive(env = process.env) {
|
|
13
|
+
return env.CI === 'true' || env.GITHUB_ACTIONS === 'true' || env.MNEMONIK_NONINTERACTIVE === '1';
|
|
14
|
+
}
|
|
15
|
+
export function parseBypass(env = process.env) {
|
|
16
|
+
if (env.MNEMONIK_BYPASS_HOOKS !== '1') {
|
|
17
|
+
return { active: false, type: null, detail: null, invalidReason: null };
|
|
18
|
+
}
|
|
19
|
+
const rawType = (env.MNEMONIK_BYPASS_TYPE ?? '').trim().toLowerCase();
|
|
20
|
+
if (!rawType) {
|
|
21
|
+
return {
|
|
22
|
+
active: false,
|
|
23
|
+
type: null,
|
|
24
|
+
detail: null,
|
|
25
|
+
invalidReason: 'MNEMONIK_BYPASS_TYPE is required when MNEMONIK_BYPASS_HOOKS=1',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (!VALID_BYPASS_TYPES.has(rawType)) {
|
|
29
|
+
return {
|
|
30
|
+
active: false,
|
|
31
|
+
type: null,
|
|
32
|
+
detail: null,
|
|
33
|
+
invalidReason: `MNEMONIK_BYPASS_TYPE must be one of ${[...VALID_BYPASS_TYPES].join('|')}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const type = rawType;
|
|
37
|
+
if (type === 'other') {
|
|
38
|
+
const detail = (env.MNEMONIK_BYPASS_DETAIL ?? '').trim();
|
|
39
|
+
if (detail.length < 50 || detail.length > 200) {
|
|
40
|
+
return {
|
|
41
|
+
active: false,
|
|
42
|
+
type: null,
|
|
43
|
+
detail: null,
|
|
44
|
+
invalidReason: "MNEMONIK_BYPASS_TYPE='other' requires MNEMONIK_BYPASS_DETAIL of 50-200 chars",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return { active: true, type, detail, invalidReason: null };
|
|
48
|
+
}
|
|
49
|
+
return { active: true, type, detail: null, invalidReason: null };
|
|
50
|
+
}
|
|
51
|
+
export async function resolveConfig(env = process.env) {
|
|
52
|
+
let scanner = null;
|
|
53
|
+
const home = env.HOME?.trim() || env.USERPROFILE?.trim() || homedir();
|
|
54
|
+
try {
|
|
55
|
+
scanner = JSON.parse(await readFile(join(home, '.mnemonik', 'scanner.json'), 'utf8'));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
scanner = null;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
apiKey: env.MNEMONIK_API_KEY?.trim() || scanner?.apiKey?.trim() || null,
|
|
62
|
+
server: (env.MNEMONIK_SERVER?.trim() || scanner?.server?.trim() || DEFAULT_SERVER).replace(/\/$/, ''),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { HookConfig, InjectionsResponse, McpPrecheckResponse } from './types.js';
|
|
2
|
+
interface BaseRequest {
|
|
3
|
+
config: HookConfig;
|
|
4
|
+
cwd: string;
|
|
5
|
+
projectId?: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PostInjectionsInput extends BaseRequest {
|
|
9
|
+
event: 'SessionStart' | 'UserPromptSubmit' | 'PreToolUse' | 'PostToolUse' | 'Stop';
|
|
10
|
+
observeOnly?: boolean;
|
|
11
|
+
prompt?: string;
|
|
12
|
+
toolName?: string;
|
|
13
|
+
toolArgs?: Record<string, unknown>;
|
|
14
|
+
filePaths?: string[];
|
|
15
|
+
stopHookActive?: boolean;
|
|
16
|
+
tz?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function postInjections(input: PostInjectionsInput): Promise<InjectionsResponse | null>;
|
|
19
|
+
export interface PostMcpPrecheckInput extends BaseRequest {
|
|
20
|
+
tool: string;
|
|
21
|
+
args: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export declare function postMcpPrecheck(input: PostMcpPrecheckInput): Promise<McpPrecheckResponse | null>;
|
|
24
|
+
export interface PostMcpRecordInput extends PostMcpPrecheckInput {
|
|
25
|
+
result: unknown;
|
|
26
|
+
}
|
|
27
|
+
export declare function postMcpRecord(input: PostMcpRecordInput): Promise<void>;
|
|
28
|
+
export interface PostEditInput extends BaseRequest {
|
|
29
|
+
filePath: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function postEdit(input: PostEditInput): Promise<void>;
|
|
32
|
+
export declare function postPreCompact(input: BaseRequest): Promise<void>;
|
|
33
|
+
export interface PostBypassInput extends BaseRequest {
|
|
34
|
+
tool: string;
|
|
35
|
+
bypassType: string;
|
|
36
|
+
detail: string | null;
|
|
37
|
+
}
|
|
38
|
+
export declare function postBypass(input: PostBypassInput): Promise<void>;
|
|
39
|
+
export {};
|
package/dist/lib/http.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Kept local because the durable npx runtime is intentionally dependency-free.
|
|
2
|
+
// These match the shared hook budgets used by the other host packages.
|
|
3
|
+
const FETCH_TIMEOUT_MS = 2_000;
|
|
4
|
+
const MCP_PRECHECK_TIMEOUT_MS = 2_000;
|
|
5
|
+
const TELEMETRY_TIMEOUT_MS = 500;
|
|
6
|
+
async function postJson(config, path, body, timeoutMs) {
|
|
7
|
+
if (!config.apiKey)
|
|
8
|
+
return null;
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch(`${config.server}${path}`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'content-type': 'application/json',
|
|
16
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify(body),
|
|
19
|
+
signal: controller.signal,
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok)
|
|
22
|
+
return null;
|
|
23
|
+
return (await response.json());
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function postInjections(input) {
|
|
33
|
+
return postJson(input.config, '/api/v1/hooks/injections', {
|
|
34
|
+
cwd: input.cwd,
|
|
35
|
+
projectId: input.projectId,
|
|
36
|
+
sessionId: input.sessionId,
|
|
37
|
+
event: input.event,
|
|
38
|
+
host: 'grok',
|
|
39
|
+
observeOnly: input.observeOnly,
|
|
40
|
+
prompt: input.prompt,
|
|
41
|
+
toolName: input.toolName,
|
|
42
|
+
toolArgs: input.toolArgs,
|
|
43
|
+
filePaths: input.filePaths,
|
|
44
|
+
stopHookActive: input.stopHookActive,
|
|
45
|
+
tz: input.tz,
|
|
46
|
+
}, FETCH_TIMEOUT_MS);
|
|
47
|
+
}
|
|
48
|
+
export function postMcpPrecheck(input) {
|
|
49
|
+
return postJson(input.config, '/api/v1/hooks/mcp-precheck', {
|
|
50
|
+
cwd: input.cwd,
|
|
51
|
+
projectId: input.projectId,
|
|
52
|
+
cursorSessionId: input.sessionId,
|
|
53
|
+
tool: input.tool,
|
|
54
|
+
args: input.args,
|
|
55
|
+
host: 'grok',
|
|
56
|
+
}, MCP_PRECHECK_TIMEOUT_MS);
|
|
57
|
+
}
|
|
58
|
+
export async function postMcpRecord(input) {
|
|
59
|
+
await postJson(input.config, '/api/v1/hooks/mcp-record', {
|
|
60
|
+
cwd: input.cwd,
|
|
61
|
+
projectId: input.projectId,
|
|
62
|
+
cursorSessionId: input.sessionId,
|
|
63
|
+
tool: input.tool,
|
|
64
|
+
args: input.args,
|
|
65
|
+
result: input.result,
|
|
66
|
+
host: 'grok',
|
|
67
|
+
}, TELEMETRY_TIMEOUT_MS);
|
|
68
|
+
}
|
|
69
|
+
export async function postEdit(input) {
|
|
70
|
+
await postJson(input.config, '/api/v1/session/track-ide-edit', { sessionId: input.sessionId, filePath: input.filePath, cwd: input.cwd }, TELEMETRY_TIMEOUT_MS);
|
|
71
|
+
}
|
|
72
|
+
export async function postPreCompact(input) {
|
|
73
|
+
await postJson(input.config, '/api/v1/hooks/pre-compact', {
|
|
74
|
+
cwd: input.cwd,
|
|
75
|
+
projectId: input.projectId,
|
|
76
|
+
claudeSessionId: input.sessionId,
|
|
77
|
+
}, 4_000);
|
|
78
|
+
}
|
|
79
|
+
export async function postBypass(input) {
|
|
80
|
+
await postJson(input.config, '/api/v1/hooks/bypass', {
|
|
81
|
+
cwd: input.cwd,
|
|
82
|
+
projectId: input.projectId,
|
|
83
|
+
host: 'grok',
|
|
84
|
+
// The telemetry endpoint's legacy normalized field is still named
|
|
85
|
+
// claudeSessionId for every host.
|
|
86
|
+
claudeSessionId: input.sessionId,
|
|
87
|
+
tool: input.tool,
|
|
88
|
+
bypassType: input.bypassType,
|
|
89
|
+
detail: input.detail,
|
|
90
|
+
}, TELEMETRY_TIMEOUT_MS);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/lib/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
3
|
+
const cache = new Map();
|
|
4
|
+
export async function findProjectIdentity(startCwd) {
|
|
5
|
+
if (!startCwd)
|
|
6
|
+
return null;
|
|
7
|
+
if (cache.has(startCwd))
|
|
8
|
+
return cache.get(startCwd);
|
|
9
|
+
let directory = isAbsolute(startCwd) ? startCwd : resolve(startCwd);
|
|
10
|
+
for (let depth = 0; depth <= 10; depth += 1) {
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(await readFile(join(directory, '.mnemonik.json'), 'utf8'));
|
|
13
|
+
if (typeof parsed.projectId === 'string' && parsed.projectId.length > 0) {
|
|
14
|
+
const identity = {
|
|
15
|
+
projectId: parsed.projectId,
|
|
16
|
+
projectName: typeof parsed.projectName === 'string' ? parsed.projectName : undefined,
|
|
17
|
+
projectRoot: directory,
|
|
18
|
+
};
|
|
19
|
+
cache.set(startCwd, identity);
|
|
20
|
+
return identity;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Missing, unreadable, or malformed: keep walking upward.
|
|
25
|
+
}
|
|
26
|
+
const parent = dirname(directory);
|
|
27
|
+
if (parent === directory)
|
|
28
|
+
break;
|
|
29
|
+
directory = parent;
|
|
30
|
+
}
|
|
31
|
+
cache.set(startCwd, null);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/lib/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQ/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkC,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;IACrD,IAAI,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpE,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAC/B,CAAC;YAC7B,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxE,MAAM,QAAQ,GAAoB;oBAChC,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,WAAW,EAAE,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;oBACpF,WAAW,EAAE,SAAS;iBACvB,CAAC;gBACF,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC9B,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,0DAA0D;QAC5D,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM;QAChC,SAAS,GAAG,MAAM,CAAC;IACrB,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function matchIrreducibleDeny(command: string): string | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Irreducible deny-floor for catastrophic, irreversible commands.
|
|
3
|
+
* Kept byte-for-byte equivalent to the other host packages' three patterns.
|
|
4
|
+
* Every other policy match is server-owned; this local floor is the only
|
|
5
|
+
* protection that remains available during a network outage.
|
|
6
|
+
*/
|
|
7
|
+
const IRREDUCIBLE_DENY = [
|
|
8
|
+
{
|
|
9
|
+
name: 'rm-rf-root-or-home',
|
|
10
|
+
pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f?[a-zA-Z]*\s+(?:\/|~|\$HOME)(?![A-Za-z0-9/_-])/,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'dd-of-whole-disk',
|
|
14
|
+
pattern: /\bdd\b[^|;&]*\bof=\/dev\/(?:sd[a-z]|nvme\d+n\d+|hd[a-z]|vd[a-z])(?![A-Za-z0-9])/,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'mkfs-whole-disk',
|
|
18
|
+
pattern: /\bmkfs(?:\.[a-z0-9]+)?\b[^|;&]*?\s\/dev\/(?:sd[a-z]|nvme\d+n\d+|hd[a-z]|vd[a-z])(?![A-Za-z0-9])/,
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
export function matchIrreducibleDeny(command) {
|
|
22
|
+
for (const entry of IRREDUCIBLE_DENY) {
|
|
23
|
+
if (entry.pattern.test(command))
|
|
24
|
+
return entry.name;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=shellTokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellTokens.js","sourceRoot":"","sources":["../../src/lib/shellTokens.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,gBAAgB,GAAqD;IACzE;QACE,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,4EAA4E;KACtF;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,iFAAiF;KAC3F;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,OAAO,EACL,iGAAiG;KACpG;CACF,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IACrD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Narrow subset of Grok's native hook wire format used by this package. */
|
|
2
|
+
export type GrokHookEventName = 'session_start' | 'user_prompt_submit' | 'pre_tool_use' | 'post_tool_use' | 'stop' | 'pre_compact' | string;
|
|
3
|
+
export interface GrokHookInput {
|
|
4
|
+
hookEventName: GrokHookEventName;
|
|
5
|
+
sessionId: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
workspaceRoot?: string;
|
|
8
|
+
timestamp?: string;
|
|
9
|
+
permissionMode?: 'default' | 'auto' | 'plan' | 'bypassPermissions' | string;
|
|
10
|
+
prompt?: string;
|
|
11
|
+
promptId?: string;
|
|
12
|
+
toolName?: string;
|
|
13
|
+
toolInput?: Record<string, unknown>;
|
|
14
|
+
toolInputTruncated?: boolean;
|
|
15
|
+
toolResult?: unknown;
|
|
16
|
+
toolResultTruncated?: boolean;
|
|
17
|
+
toolUseId?: string;
|
|
18
|
+
stopHookActive?: boolean;
|
|
19
|
+
reason?: string;
|
|
20
|
+
lastAssistantMessage?: string;
|
|
21
|
+
}
|
|
22
|
+
export type GrokDecision = {
|
|
23
|
+
decision: 'allow';
|
|
24
|
+
} | {
|
|
25
|
+
decision: 'deny';
|
|
26
|
+
reason: string;
|
|
27
|
+
} | {
|
|
28
|
+
hookSpecificOutput: {
|
|
29
|
+
hookEventName: 'Stop';
|
|
30
|
+
additionalContext: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export interface HookConfig {
|
|
34
|
+
apiKey: string | null;
|
|
35
|
+
server: string;
|
|
36
|
+
}
|
|
37
|
+
export type BypassType = 'typo' | 'formatting' | 'regenerated' | 'already_grounded' | 'other';
|
|
38
|
+
export interface BypassEnvelope {
|
|
39
|
+
active: boolean;
|
|
40
|
+
type: BypassType | null;
|
|
41
|
+
detail: string | null;
|
|
42
|
+
invalidReason: string | null;
|
|
43
|
+
}
|
|
44
|
+
export interface Injection {
|
|
45
|
+
signal: string;
|
|
46
|
+
kind: 'directive' | 'nudge';
|
|
47
|
+
envelope: string;
|
|
48
|
+
deliveryHint: 'additional_context' | 'system_message';
|
|
49
|
+
hostDecision?: 'deny' | 'ask';
|
|
50
|
+
}
|
|
51
|
+
export interface InjectionsResponse {
|
|
52
|
+
ok: boolean;
|
|
53
|
+
reason?: string;
|
|
54
|
+
injections: Injection[];
|
|
55
|
+
}
|
|
56
|
+
export interface McpPrecheckResponse {
|
|
57
|
+
ok: boolean;
|
|
58
|
+
reason?: string;
|
|
59
|
+
verdict?: 'allow' | 'deny' | 'ask';
|
|
60
|
+
ruleId?: string;
|
|
61
|
+
agentMessage?: string;
|
|
62
|
+
}
|
|
63
|
+
export declare const DEFAULT_SERVER = "https://api.mnemonik.dev";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AA4EA,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mnemonik/grok-hooks",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Grok lifecycle hooks for Mnemonik host-side gates and session bookkeeping. Uses Grok-native PreToolUse and Stop decisions; passive hooks never claim to inject model context.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mnemonik-grok-hooks": "dist/install.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/install.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "node ../../node_modules/@typescript/native/bin/tsc",
|
|
15
|
+
"typecheck": "node ../../node_modules/@typescript/native/bin/tsc --noEmit -p tsconfig.test.json",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mnemonik",
|
|
25
|
+
"grok",
|
|
26
|
+
"grok-build",
|
|
27
|
+
"hooks"
|
|
28
|
+
],
|
|
29
|
+
"license": "UNLICENSED",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.19.43",
|
|
32
|
+
"@typescript/native": "npm:typescript@7.0.2",
|
|
33
|
+
"vitest": "^4.1.10"
|
|
34
|
+
}
|
|
35
|
+
}
|