javi-forge 1.40.0 → 1.42.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/assets/claude-hooks/javi-forge-skillguard-pre-tool-use.mjs +51 -16
- package/assets/claude-hooks/manifest.json +6 -2
- package/assets/grok-hooks/manifest.json +9 -0
- package/dist/cli/dispatch/hooks.d.ts +1 -1
- package/dist/cli/dispatch/hooks.js +4 -2
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +20 -3
- package/dist/commands/cursor-hooks.d.ts +15 -0
- package/dist/commands/cursor-hooks.js +44 -0
- package/dist/commands/grok-hooks.d.ts +15 -0
- package/dist/commands/grok-hooks.js +45 -0
- package/dist/lib/agent-adapter.d.ts +5 -3
- package/dist/lib/agent-adapter.js +65 -1
- package/dist/lib/cursor-hook-manager.d.ts +75 -0
- package/dist/lib/cursor-hook-manager.js +379 -0
- package/dist/lib/grok-hook-manager.d.ts +88 -0
- package/dist/lib/grok-hook-manager.js +327 -0
- package/package.json +1 -1
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor SkillGuard hook ownership manager. Cursor reads per-user hooks from
|
|
3
|
+
* ~/.cursor/hooks.json and hook scripts from ~/.cursor/hooks/, so installation is
|
|
4
|
+
* a two-file transaction: a merged preToolUse registration plus the shared
|
|
5
|
+
* policy runtime copied beside it. Doctor reports installed bytes/registration
|
|
6
|
+
* only; it cannot prove Cursor loaded or executed the hook in a live session.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
9
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { CLAUDE_HOOK_ASSETS_DIR } from "../constants.js";
|
|
13
|
+
import { safeReadFile } from "./safe-read.js";
|
|
14
|
+
import { selectSecureFs } from "./secure-fs-posix.js";
|
|
15
|
+
import { runTransaction, } from "./secure-fs-transaction.js";
|
|
16
|
+
const MAX_BYTES = 1024 * 1024;
|
|
17
|
+
const READ_OPTS = {
|
|
18
|
+
maxBytes: MAX_BYTES,
|
|
19
|
+
hardRejectOverBytes: MAX_BYTES,
|
|
20
|
+
maxLineLength: Number.POSITIVE_INFINITY,
|
|
21
|
+
};
|
|
22
|
+
export const CURSOR_HOOKS_NAME = "hooks.json";
|
|
23
|
+
export const CURSOR_POLICY_NAME = "javi-forge-skillguard-pre-tool-use.mjs";
|
|
24
|
+
export const CURSOR_HOOK_MARKER = "javi-forge-managed: cursor-pretooluse v1";
|
|
25
|
+
export const CURSOR_POLICY_MARKER = "// javi-forge-managed: claude-pretooluse v1";
|
|
26
|
+
export const CURSOR_HOOK_VERSION = 1;
|
|
27
|
+
export const CURSOR_TIMEOUT = 30;
|
|
28
|
+
export const CURSOR_TOOL_MATCHER = "Shell|Read|Write|Delete";
|
|
29
|
+
export const SHIPPED_CURSOR_POLICY = path.join(CLAUDE_HOOK_ASSETS_DIR, CURSOR_POLICY_NAME);
|
|
30
|
+
export function cursorConfigPaths(homeDir) {
|
|
31
|
+
const cursorDir = path.join(homeDir, ".cursor");
|
|
32
|
+
const hooksDir = path.join(cursorDir, "hooks");
|
|
33
|
+
return {
|
|
34
|
+
cursorDir,
|
|
35
|
+
hooksDir,
|
|
36
|
+
hooksFile: path.join(cursorDir, CURSOR_HOOKS_NAME),
|
|
37
|
+
policyFile: path.join(hooksDir, CURSOR_POLICY_NAME),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function fileKind(target) {
|
|
41
|
+
try {
|
|
42
|
+
const stat = await lstat(target);
|
|
43
|
+
if (stat.isSymbolicLink())
|
|
44
|
+
return { kind: "symlink" };
|
|
45
|
+
return stat.isFile() ? { kind: "file" } : { kind: "non-regular" };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const code = error.code;
|
|
49
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
50
|
+
return { kind: "absent" };
|
|
51
|
+
return { kind: "error", detail: code ?? String(error) };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function sha256(content) {
|
|
55
|
+
return createHash("sha256").update(content).digest("hex");
|
|
56
|
+
}
|
|
57
|
+
function isObject(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
59
|
+
}
|
|
60
|
+
export async function classifyCursorAsset(target, marker, manifest) {
|
|
61
|
+
const stat = await fileKind(target);
|
|
62
|
+
if (stat.kind === "absent")
|
|
63
|
+
return { state: "absent" };
|
|
64
|
+
if (stat.kind === "symlink")
|
|
65
|
+
return { state: "symlink" };
|
|
66
|
+
if (stat.kind !== "file")
|
|
67
|
+
return { state: "non-regular", detail: stat.detail };
|
|
68
|
+
const read = await safeReadFile(target, READ_OPTS);
|
|
69
|
+
if (!read.ok) {
|
|
70
|
+
if (read.reason === "not-found")
|
|
71
|
+
return { state: "absent" };
|
|
72
|
+
if (read.reason === "binary" || read.reason === "too-large")
|
|
73
|
+
return { state: "foreign", detail: read.reason };
|
|
74
|
+
return { state: "non-regular", detail: read.reason };
|
|
75
|
+
}
|
|
76
|
+
if (!read.content.startsWith(`${marker}\n`))
|
|
77
|
+
return { state: "foreign" };
|
|
78
|
+
const observed = sha256(read.content);
|
|
79
|
+
if (observed === manifest.sha256)
|
|
80
|
+
return {
|
|
81
|
+
state: "managed-current",
|
|
82
|
+
sha256: observed,
|
|
83
|
+
version: manifest.version,
|
|
84
|
+
};
|
|
85
|
+
if (manifest.historical.includes(observed))
|
|
86
|
+
return {
|
|
87
|
+
state: "released-outdated",
|
|
88
|
+
sha256: observed,
|
|
89
|
+
version: manifest.version,
|
|
90
|
+
};
|
|
91
|
+
return { state: "edited-managed", sha256: observed };
|
|
92
|
+
}
|
|
93
|
+
function expectedCursorCommand(policyFile) {
|
|
94
|
+
return `${JSON.stringify(process.execPath)} ${JSON.stringify(policyFile)} --agent=cursor`;
|
|
95
|
+
}
|
|
96
|
+
function buildCursorHook(policyFile) {
|
|
97
|
+
return {
|
|
98
|
+
command: expectedCursorCommand(policyFile),
|
|
99
|
+
type: "command",
|
|
100
|
+
matcher: CURSOR_TOOL_MATCHER,
|
|
101
|
+
timeout: CURSOR_TIMEOUT,
|
|
102
|
+
failClosed: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export function expectedCursorHooksJson(policyFile) {
|
|
106
|
+
return `${JSON.stringify({
|
|
107
|
+
"javi-forge-managed": CURSOR_HOOK_MARKER,
|
|
108
|
+
version: CURSOR_HOOK_VERSION,
|
|
109
|
+
hooks: { preToolUse: [buildCursorHook(policyFile)] },
|
|
110
|
+
}, null, 2)}\n`;
|
|
111
|
+
}
|
|
112
|
+
const CURSOR_CMD_RE = /(?:^|\s)(?:"[^"]*node[^"]*"|node)\s+(?:"[^"]*javi-forge-skillguard-pre-tool-use\.mjs"|\S*javi-forge-skillguard-pre-tool-use\.mjs)\s+--agent=cursor(?:\s|$)/;
|
|
113
|
+
function preToolUseHandlers(value) {
|
|
114
|
+
if (!isObject(value))
|
|
115
|
+
return null;
|
|
116
|
+
const hooks = value.hooks;
|
|
117
|
+
if (hooks === undefined)
|
|
118
|
+
return [];
|
|
119
|
+
if (!isObject(hooks))
|
|
120
|
+
return null;
|
|
121
|
+
const groups = hooks.preToolUse;
|
|
122
|
+
if (groups === undefined)
|
|
123
|
+
return [];
|
|
124
|
+
if (!Array.isArray(groups))
|
|
125
|
+
return null;
|
|
126
|
+
const handlers = [];
|
|
127
|
+
for (const group of groups) {
|
|
128
|
+
if (!isObject(group))
|
|
129
|
+
return null;
|
|
130
|
+
handlers.push(group);
|
|
131
|
+
}
|
|
132
|
+
return handlers;
|
|
133
|
+
}
|
|
134
|
+
export function mergeCursorHooks(existing, policyFile) {
|
|
135
|
+
const container = isObject(existing)
|
|
136
|
+
? structuredClone(existing)
|
|
137
|
+
: {};
|
|
138
|
+
if (!isObject(container.hooks))
|
|
139
|
+
container.hooks = {};
|
|
140
|
+
const hooks = container.hooks;
|
|
141
|
+
const groups = Array.isArray(hooks.preToolUse) ? hooks.preToolUse : [];
|
|
142
|
+
hooks.preToolUse = [
|
|
143
|
+
...groups.filter((group) => !(isObject(group) &&
|
|
144
|
+
typeof group.command === "string" &&
|
|
145
|
+
CURSOR_CMD_RE.test(group.command))),
|
|
146
|
+
buildCursorHook(policyFile),
|
|
147
|
+
];
|
|
148
|
+
container["javi-forge-managed"] = CURSOR_HOOK_MARKER;
|
|
149
|
+
container.version = CURSOR_HOOK_VERSION;
|
|
150
|
+
return container;
|
|
151
|
+
}
|
|
152
|
+
export function classifyCursorHooksJson(value, policyFile) {
|
|
153
|
+
const handlers = preToolUseHandlers(value);
|
|
154
|
+
if (handlers === null)
|
|
155
|
+
return { state: "malformed" };
|
|
156
|
+
const expected = buildCursorHook(policyFile);
|
|
157
|
+
const ours = handlers.filter((handler) => typeof handler.command === "string" &&
|
|
158
|
+
CURSOR_CMD_RE.test(handler.command));
|
|
159
|
+
if (ours.some((handler) => JSON.stringify(handler) === JSON.stringify(expected) &&
|
|
160
|
+
isObject(value) &&
|
|
161
|
+
value["javi-forge-managed"] === CURSOR_HOOK_MARKER &&
|
|
162
|
+
value.version === CURSOR_HOOK_VERSION)) {
|
|
163
|
+
return { state: "managed-current", version: CURSOR_HOOK_VERSION };
|
|
164
|
+
}
|
|
165
|
+
if (ours.length > 0)
|
|
166
|
+
return { state: "released-outdated" };
|
|
167
|
+
if (handlers.length > 0)
|
|
168
|
+
return { state: "foreign" };
|
|
169
|
+
return { state: "absent" };
|
|
170
|
+
}
|
|
171
|
+
async function readManifest() {
|
|
172
|
+
const read = await safeReadFile(path.join(CLAUDE_HOOK_ASSETS_DIR, "manifest.json"), READ_OPTS);
|
|
173
|
+
if (!read.ok)
|
|
174
|
+
throw new Error(`unreadable claude-hooks manifest: ${read.reason}`);
|
|
175
|
+
const manifest = JSON.parse(read.content);
|
|
176
|
+
return { policy: manifest.asset };
|
|
177
|
+
}
|
|
178
|
+
async function readJsonFile(target) {
|
|
179
|
+
const stat = await fileKind(target);
|
|
180
|
+
if (stat.kind === "absent")
|
|
181
|
+
return { ok: false, state: "absent" };
|
|
182
|
+
if (stat.kind === "symlink")
|
|
183
|
+
return { ok: false, state: "symlink" };
|
|
184
|
+
if (stat.kind !== "file")
|
|
185
|
+
return { ok: false, state: "non-regular", detail: stat.detail };
|
|
186
|
+
const read = await safeReadFile(target, READ_OPTS);
|
|
187
|
+
if (!read.ok) {
|
|
188
|
+
if (read.reason === "not-found")
|
|
189
|
+
return { ok: false, state: "absent" };
|
|
190
|
+
if (read.reason === "binary" || read.reason === "too-large")
|
|
191
|
+
return { ok: false, state: "foreign", detail: read.reason };
|
|
192
|
+
return { ok: false, state: "non-regular", detail: read.reason };
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
return { ok: true, value: JSON.parse(read.content) };
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return { ok: false, state: "malformed", detail: "invalid-json" };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
export async function doctorCursorSkillGuard(homeDir = os.homedir(), options = {}) {
|
|
202
|
+
const manifest = options.manifest ?? (await readManifest());
|
|
203
|
+
const paths = cursorConfigPaths(homeDir);
|
|
204
|
+
const hooksRead = await readJsonFile(paths.hooksFile);
|
|
205
|
+
const hooksJson = hooksRead.ok
|
|
206
|
+
? classifyCursorHooksJson(hooksRead.value, paths.policyFile)
|
|
207
|
+
: { state: hooksRead.state, detail: hooksRead.detail };
|
|
208
|
+
const policy = await classifyCursorAsset(paths.policyFile, CURSOR_POLICY_MARKER, manifest.policy);
|
|
209
|
+
const remediation = [];
|
|
210
|
+
for (const [name, asset] of [
|
|
211
|
+
["hook registration", hooksJson],
|
|
212
|
+
["policy runtime", policy],
|
|
213
|
+
]) {
|
|
214
|
+
if (asset.state === "absent" ||
|
|
215
|
+
asset.state === "released-outdated" ||
|
|
216
|
+
asset.state === "foreign") {
|
|
217
|
+
remediation.push(`install the Cursor ${name} with: javi-forge hooks install cursor`);
|
|
218
|
+
}
|
|
219
|
+
else if (asset.state === "edited-managed") {
|
|
220
|
+
remediation.push(`repair the edited Cursor ${name} with: javi-forge hooks repair cursor --force`);
|
|
221
|
+
}
|
|
222
|
+
else if (asset.state !== "managed-current") {
|
|
223
|
+
remediation.push(`manually review the Cursor ${name} (${asset.state}); it will not be overwritten`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
healthy: hooksJson.state === "managed-current" &&
|
|
228
|
+
policy.state === "managed-current",
|
|
229
|
+
hooksJson,
|
|
230
|
+
policy,
|
|
231
|
+
remediation: [...new Set(remediation)],
|
|
232
|
+
runtimeEvidence: "not-implemented",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function hookWritePlan(state, force, name) {
|
|
236
|
+
if (state === "absent" ||
|
|
237
|
+
state === "released-outdated" ||
|
|
238
|
+
state === "foreign")
|
|
239
|
+
return { write: true };
|
|
240
|
+
if (state === "managed-current")
|
|
241
|
+
return { write: false };
|
|
242
|
+
if (state === "edited-managed" && force)
|
|
243
|
+
return { write: true };
|
|
244
|
+
if (state === "edited-managed")
|
|
245
|
+
return {
|
|
246
|
+
write: false,
|
|
247
|
+
refusal: `refuse edited ${name}; rerun repair with --force`,
|
|
248
|
+
};
|
|
249
|
+
return {
|
|
250
|
+
write: false,
|
|
251
|
+
refusal: `refuse ${name} in state ${state} — manual review`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function policyWritePlan(state, force, name) {
|
|
255
|
+
if (state === "absent" || state === "released-outdated")
|
|
256
|
+
return { write: true };
|
|
257
|
+
if (state === "managed-current")
|
|
258
|
+
return { write: false };
|
|
259
|
+
if (state === "edited-managed" && force)
|
|
260
|
+
return { write: true };
|
|
261
|
+
if (state === "edited-managed")
|
|
262
|
+
return {
|
|
263
|
+
write: false,
|
|
264
|
+
refusal: `refuse edited ${name}; rerun repair with --force`,
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
write: false,
|
|
268
|
+
refusal: `refuse ${name} in state ${state} — manual review`,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function serialize(container) {
|
|
272
|
+
return Buffer.from(`${JSON.stringify(container, null, 2)}\n`, "utf8");
|
|
273
|
+
}
|
|
274
|
+
export async function _runCursor(homeDir, mode, options, deps = {}) {
|
|
275
|
+
const manifest = deps.manifest ?? (await readManifest());
|
|
276
|
+
const paths = cursorConfigPaths(homeDir);
|
|
277
|
+
const doctorFn = deps.doctor ?? doctorCursorSkillGuard;
|
|
278
|
+
const doctor = () => doctorFn(homeDir, {
|
|
279
|
+
manifest,
|
|
280
|
+
policySource: deps.policySource,
|
|
281
|
+
});
|
|
282
|
+
const force = mode === "repair" && options.force === true;
|
|
283
|
+
const hooksRead = await readJsonFile(paths.hooksFile);
|
|
284
|
+
const hooksState = hooksRead.ok
|
|
285
|
+
? classifyCursorHooksJson(hooksRead.value, paths.policyFile)
|
|
286
|
+
: { state: hooksRead.state, detail: hooksRead.detail };
|
|
287
|
+
const policyState = await classifyCursorAsset(paths.policyFile, CURSOR_POLICY_MARKER, manifest.policy);
|
|
288
|
+
const hookPlan = hookWritePlan(hooksState.state, force, "Cursor hook registration");
|
|
289
|
+
const policyPlan = policyWritePlan(policyState.state, force, "Cursor policy runtime");
|
|
290
|
+
if (hookPlan.refusal || policyPlan.refusal) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
changed: [],
|
|
294
|
+
backups: [],
|
|
295
|
+
errors: [hookPlan.refusal ?? policyPlan.refusal ?? "refused"],
|
|
296
|
+
warnings: [],
|
|
297
|
+
report: await doctor(),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (!hookPlan.write && !policyPlan.write) {
|
|
301
|
+
return {
|
|
302
|
+
ok: true,
|
|
303
|
+
changed: [],
|
|
304
|
+
backups: [],
|
|
305
|
+
errors: [],
|
|
306
|
+
warnings: [],
|
|
307
|
+
report: await doctor(),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
const secureFs = deps.secureFs !== undefined
|
|
311
|
+
? deps.secureFs
|
|
312
|
+
: selectSecureFs(process.platform);
|
|
313
|
+
if (!secureFs) {
|
|
314
|
+
return {
|
|
315
|
+
ok: false,
|
|
316
|
+
changed: [],
|
|
317
|
+
backups: [],
|
|
318
|
+
errors: ["windows-secure-object-unavailable"],
|
|
319
|
+
warnings: [],
|
|
320
|
+
report: await doctor(),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const policySource = deps.policySource ?? SHIPPED_CURSOR_POLICY;
|
|
324
|
+
const policyBytes = policyPlan.write ? await readFile(policySource) : null;
|
|
325
|
+
if (policyBytes && sha256(policyBytes) !== manifest.policy.sha256) {
|
|
326
|
+
return {
|
|
327
|
+
ok: false,
|
|
328
|
+
changed: [],
|
|
329
|
+
backups: [],
|
|
330
|
+
errors: [
|
|
331
|
+
"refuse packaged Cursor policy asset with manifest hash mismatch",
|
|
332
|
+
],
|
|
333
|
+
warnings: [],
|
|
334
|
+
report: await doctor(),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const existing = hooksRead.ok ? hooksRead.value : undefined;
|
|
338
|
+
const hookBytes = hookPlan.write
|
|
339
|
+
? serialize(mergeCursorHooks(existing, paths.policyFile))
|
|
340
|
+
: null;
|
|
341
|
+
const components = [
|
|
342
|
+
{
|
|
343
|
+
path: paths.hooksFile,
|
|
344
|
+
desired: hookBytes,
|
|
345
|
+
capturePrior: hookPlan.write && hooksState.state !== "absent",
|
|
346
|
+
forceBackup: force && hooksState.state === "edited-managed",
|
|
347
|
+
wasAbsent: hooksState.state === "absent",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
path: paths.policyFile,
|
|
351
|
+
desired: policyBytes,
|
|
352
|
+
capturePrior: policyPlan.write && policyState.state !== "absent",
|
|
353
|
+
forceBackup: force && policyState.state === "edited-managed",
|
|
354
|
+
wasAbsent: policyState.state === "absent",
|
|
355
|
+
},
|
|
356
|
+
];
|
|
357
|
+
const tx = await runTransaction({
|
|
358
|
+
secureFs,
|
|
359
|
+
clock: deps.clock ?? (() => new Date()),
|
|
360
|
+
nonce: deps.nonce ?? (() => randomBytes(4).toString("hex")),
|
|
361
|
+
projectDir: homeDir,
|
|
362
|
+
layout: { containers: [paths.cursorDir, paths.hooksDir], components },
|
|
363
|
+
});
|
|
364
|
+
return {
|
|
365
|
+
ok: tx.ok,
|
|
366
|
+
changed: tx.committed,
|
|
367
|
+
backups: tx.backups,
|
|
368
|
+
errors: tx.errors,
|
|
369
|
+
warnings: [],
|
|
370
|
+
report: await doctor(),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
export function installCursorSkillGuard(homeDir, deps = {}) {
|
|
374
|
+
return _runCursor(homeDir ?? os.homedir(), "install", {}, deps);
|
|
375
|
+
}
|
|
376
|
+
export function repairCursorSkillGuard(homeDir, options, deps = {}) {
|
|
377
|
+
return _runCursor(homeDir ?? os.homedir(), "repair", options ?? {}, deps);
|
|
378
|
+
}
|
|
379
|
+
//# sourceMappingURL=cursor-hook-manager.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grok Build SkillGuard hook ownership manager. Grok reads trusted global hook
|
|
3
|
+
* definitions from ~/.grok/hooks, so installation is a two-file transaction:
|
|
4
|
+
* the PreToolUse JSON registration and the adjacent policy runtime. Doctor
|
|
5
|
+
* deliberately reports installed bytes/registration only, not runtime loading.
|
|
6
|
+
*/
|
|
7
|
+
import type { AssetManifestEntry } from "./claude-hook-manager.js";
|
|
8
|
+
import type { ClaudeHookComponentState } from "./claude-hook-settings.js";
|
|
9
|
+
import { type PlatformSecureFs } from "./secure-fs-transaction.js";
|
|
10
|
+
export declare const GROK_HOOK_NAME = "javi-forge-skillguard-pre-tool-use.json";
|
|
11
|
+
export declare const GROK_POLICY_NAME = "javi-forge-skillguard-pre-tool-use.mjs";
|
|
12
|
+
export declare const GROK_HOOK_MARKER = "javi-forge-managed: grok-pretooluse v1";
|
|
13
|
+
export declare const GROK_POLICY_MARKER = "// javi-forge-managed: claude-pretooluse v1";
|
|
14
|
+
export declare const GROK_HOOK_VERSION = 1;
|
|
15
|
+
export declare const GROK_TOOL_MATCHER = "run_terminal_command|read_file|search_replace";
|
|
16
|
+
export declare const GROK_ASSETS_DIR: string;
|
|
17
|
+
export declare const SHIPPED_GROK_POLICY: string;
|
|
18
|
+
export interface GrokHookManifestEntry {
|
|
19
|
+
name: string;
|
|
20
|
+
version: number;
|
|
21
|
+
matcher: string;
|
|
22
|
+
historicalVersions: number[];
|
|
23
|
+
}
|
|
24
|
+
export interface GrokManifest {
|
|
25
|
+
hook: GrokHookManifestEntry;
|
|
26
|
+
policy: AssetManifestEntry;
|
|
27
|
+
}
|
|
28
|
+
export interface GrokConfigPaths {
|
|
29
|
+
grokDir: string;
|
|
30
|
+
hooksDir: string;
|
|
31
|
+
hookFile: string;
|
|
32
|
+
policyFile: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveGrokHomeRoot(baseDir: string, env?: NodeJS.ProcessEnv): string;
|
|
35
|
+
export declare function grokConfigPaths(baseDir: string, env?: NodeJS.ProcessEnv): GrokConfigPaths;
|
|
36
|
+
/** Generate the complete standalone Grok hook registration for an adjacent asset. */
|
|
37
|
+
export declare function expectedGrokHookJson(policyFile: string): string;
|
|
38
|
+
export interface GrokAssetClassification {
|
|
39
|
+
state: ClaudeHookComponentState;
|
|
40
|
+
sha256?: string;
|
|
41
|
+
version?: number;
|
|
42
|
+
detail?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Classify an installed policy runtime with the packaged manifest identity. */
|
|
45
|
+
export declare function classifyGrokAsset(target: string, marker: string, manifest: AssetManifestEntry): Promise<GrokAssetClassification>;
|
|
46
|
+
/**
|
|
47
|
+
* Classify a generated hook registration semantically. JSON whitespace does not
|
|
48
|
+
* matter; the command is compared against the supplied baseDir's adjacent asset.
|
|
49
|
+
*/
|
|
50
|
+
export declare function classifyGrokHookJson(target: string, policyFile: string, manifest: GrokHookManifestEntry): Promise<GrokAssetClassification>;
|
|
51
|
+
export interface GrokHookDoctorReport {
|
|
52
|
+
healthy: boolean;
|
|
53
|
+
hook: GrokAssetClassification;
|
|
54
|
+
policy: GrokAssetClassification;
|
|
55
|
+
remediation: string[];
|
|
56
|
+
/** Installed files and registration only; no Grok execution is observed. */
|
|
57
|
+
runtimeEvidence: "not-implemented";
|
|
58
|
+
}
|
|
59
|
+
export interface GrokDoctorOptions {
|
|
60
|
+
manifest?: GrokManifest;
|
|
61
|
+
policySource?: string;
|
|
62
|
+
env?: NodeJS.ProcessEnv;
|
|
63
|
+
}
|
|
64
|
+
export declare function doctorGrokSkillGuard(baseDir?: string, options?: GrokDoctorOptions): Promise<GrokHookDoctorReport>;
|
|
65
|
+
interface GrokMutationSuccess {
|
|
66
|
+
ok: boolean;
|
|
67
|
+
changed: string[];
|
|
68
|
+
backups: string[];
|
|
69
|
+
errors: string[];
|
|
70
|
+
warnings: string[];
|
|
71
|
+
report: GrokHookDoctorReport;
|
|
72
|
+
}
|
|
73
|
+
export type GrokHookMutationResult = GrokMutationSuccess;
|
|
74
|
+
export interface GrokHookRunDeps extends GrokDoctorOptions {
|
|
75
|
+
secureFs?: PlatformSecureFs | null;
|
|
76
|
+
clock?: () => Date;
|
|
77
|
+
nonce?: () => string;
|
|
78
|
+
doctor?: typeof doctorGrokSkillGuard;
|
|
79
|
+
}
|
|
80
|
+
export declare function _runGrok(baseDir: string, mode: "install" | "repair", options: {
|
|
81
|
+
force?: boolean;
|
|
82
|
+
}, deps?: GrokHookRunDeps): Promise<GrokHookMutationResult>;
|
|
83
|
+
export declare function installGrokSkillGuard(baseDir?: string, deps?: GrokHookRunDeps): Promise<GrokHookMutationResult>;
|
|
84
|
+
export declare function repairGrokSkillGuard(baseDir?: string, options?: {
|
|
85
|
+
force?: boolean;
|
|
86
|
+
}, deps?: GrokHookRunDeps): Promise<GrokHookMutationResult>;
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=grok-hook-manager.d.ts.map
|