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
|
@@ -8,6 +8,8 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
export const MANAGED_MARKER = "// javi-forge-managed: claude-pretooluse v1";
|
|
9
9
|
export const INPUT_LIMIT_BYTES = 1_048_576;
|
|
10
10
|
export const SUPPORTED_TOOLS = Object.freeze(["Bash", "PowerShell", "Read", "Write", "Edit"]);
|
|
11
|
+
export function mapGrokToolName(toolName, platform = process.platform) { return toolName === "run_terminal_command" ? (platform === "win32" ? "PowerShell" : "Bash") : toolName === "read_file" ? "Read" : toolName === "search_replace" ? "Edit" : toolName; }
|
|
12
|
+
export function mapCursorToolName(toolName, platform = process.platform) { return toolName === "Shell" ? (platform === "win32" ? "PowerShell" : "Bash") : toolName === "Delete" ? "Edit" : toolName; }
|
|
11
13
|
export const POLICY_REGISTRY = Object.freeze({ schemaVersion: 1, policyVersion: 2, diagnosticsMaxBytes: 240 });
|
|
12
14
|
export function resolvePlatformSupport(platform = process.platform) { return platform === "linux" || platform === "win32" ? { supported: true } : { supported: false, reason: "unsupported-platform" }; }
|
|
13
15
|
const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
@@ -15,6 +17,7 @@ const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)),
|
|
|
15
17
|
// never to an event field or a command token. This is intentionally an exact,
|
|
16
18
|
// two-file allowlist rather than a whole-home or whole-.codex policy.
|
|
17
19
|
const HOST_HOME = os.homedir();
|
|
20
|
+
const GROK_HOME_ROOT = process.env.GROK_HOME ? path.resolve(process.env.GROK_HOME) : path.join(HOST_HOME, ".grok");
|
|
18
21
|
// Per-agent adapter config (S0 core-extraction): every agent-specific input the guard needs
|
|
19
22
|
// (the isManaged protected-path set, the project-dir source, the managed marker) is resolved by
|
|
20
23
|
// the --agent selector instead of a baked-in literal. The pure evaluate*/utility engine stays
|
|
@@ -23,8 +26,12 @@ const HOST_HOME = os.homedir();
|
|
|
23
26
|
const CLAUDE_MANAGED_SET = Object.freeze({ exact: Object.freeze([".claude/settings.json", ".claude/settings.local.json", ".claude/CLAUDE.md", "CLAUDE.md", ".javi-forge/ci.yaml"]), prefixes: Object.freeze([".claude/hooks/", ".claude/agents/", ".claude/skills/"]), caseFoldExact: Object.freeze(["claude.md", ".claude/claude.md"]) });
|
|
24
27
|
const CODEX_MANAGED_SET = Object.freeze({ exact: Object.freeze([".codex/hooks.json", ".claude/settings.json", ".claude/settings.local.json", ".claude/CLAUDE.md", "CLAUDE.md", ".javi-forge/ci.yaml"]), prefixes: Object.freeze([".claude/hooks/", ".claude/agents/", ".claude/skills/"]), caseFoldExact: Object.freeze(["claude.md", ".claude/claude.md"]) });
|
|
25
28
|
const OPENCODE_MANAGED_SET = Object.freeze({ exact: Object.freeze(["opencode.json", "opencode.jsonc", ".opencode/opencode.json", ".opencode/opencode.jsonc", ".opencode/AGENTS.md", "AGENTS.md", ".javi-forge/ci.yaml"]), prefixes: Object.freeze([".opencode/plugins/", ".opencode/agents/", ".opencode/skills/", ".agents/skills/", ".claude/hooks/", ".claude/agents/", ".claude/skills/"]), caseFoldExact: Object.freeze(["agents.md", ".opencode/agents.md", "claude.md", ".claude/claude.md"]) });
|
|
29
|
+
const GROK_MANAGED_SET = Object.freeze({ exact: Object.freeze([".grok/config.toml", "AGENTS.md", ".claude/settings.json", ".claude/settings.local.json", ".claude/CLAUDE.md", "CLAUDE.md", ".javi-forge/ci.yaml"]), prefixes: Object.freeze([".grok/hooks/", ".claude/hooks/", ".claude/agents/", ".claude/skills/"]), caseFoldExact: Object.freeze(["agents.md", "claude.md", ".claude/claude.md"]) });
|
|
30
|
+
const CURSOR_MANAGED_SET = Object.freeze({ exact: Object.freeze([".cursor/hooks.json", ".cursor/rules/", "AGENTS.md", ".claude/settings.json", ".claude/settings.local.json", ".claude/CLAUDE.md", "CLAUDE.md", ".javi-forge/ci.yaml"]), prefixes: Object.freeze([".cursor/hooks/", ".cursor/rules/", ".claude/hooks/", ".claude/agents/", ".claude/skills/"]), caseFoldExact: Object.freeze(["agents.md", "claude.md", ".claude/claude.md"]) });
|
|
26
31
|
const CODEX_GLOBAL_MANAGED_PATHS = Object.freeze([".codex/hooks.json", ".codex/config.toml"]);
|
|
27
|
-
|
|
32
|
+
const GROK_GLOBAL_MANAGED_PATHS = Object.freeze([path.join(GROK_HOME_ROOT, "hooks", "javi-forge-skillguard-pre-tool-use.json"), path.join(GROK_HOME_ROOT, "hooks", "javi-forge-skillguard-pre-tool-use.mjs")]);
|
|
33
|
+
const CURSOR_GLOBAL_MANAGED_PATHS = Object.freeze([path.join(HOST_HOME, ".cursor", "hooks.json"), path.join(HOST_HOME, ".cursor", "hooks", "javi-forge-skillguard-pre-tool-use.mjs")]);
|
|
34
|
+
export const AGENT_CONFIGS = Object.freeze({ claude: Object.freeze({ id: "claude", managedSet: CLAUDE_MANAGED_SET, globalManagedPaths: Object.freeze([]), projectDir: Object.freeze({ envVar: "CLAUDE_PROJECT_DIR", fallback: "asset-root" }), marker: MANAGED_MARKER }), codex: Object.freeze({ id: "codex", managedSet: CODEX_MANAGED_SET, globalManagedPaths: CODEX_GLOBAL_MANAGED_PATHS, projectDir: Object.freeze({ envVar: null, fallback: "cwd" }), marker: "// javi-forge-managed: codex-pretooluse v1" }), opencode: Object.freeze({ id: "opencode", managedSet: OPENCODE_MANAGED_SET, globalManagedPaths: Object.freeze([]), projectDir: Object.freeze({ envVar: null, fallback: "cwd" }), marker: "// javi-forge-managed: opencode-skillguard v1" }), grok: Object.freeze({ id: "grok", managedSet: GROK_MANAGED_SET, globalManagedPaths: GROK_GLOBAL_MANAGED_PATHS, projectDir: Object.freeze({ envVar: null, fallback: "cwd" }), marker: "javi-forge-managed: grok-pretooluse v1" }), cursor: Object.freeze({ id: "cursor", managedSet: CURSOR_MANAGED_SET, globalManagedPaths: CURSOR_GLOBAL_MANAGED_PATHS, projectDir: Object.freeze({ envVar: "CURSOR_PROJECT_DIR", fallback: "cwd" }), marker: "// javi-forge-managed: cursor-pretooluse v1" }) });
|
|
28
35
|
// Fail-closed agent selector: a missing/unknown --agent means we cannot know what to protect, so refuse.
|
|
29
36
|
function resolveAgentConfig(argv) { const arg = argv.find((value) => typeof value === "string" && value.startsWith("--agent=")); const id = arg === undefined ? undefined : arg.slice("--agent=".length); const config = id === undefined ? undefined : AGENT_CONFIGS[id]; if (!config) fail("invalid-config"); return config; }
|
|
30
37
|
// Project root per agent: the env var when set (Claude = CLAUDE_PROJECT_DIR); otherwise the per-agent
|
|
@@ -87,7 +94,7 @@ export function lexicalizePolicyPath(input, options = {}) {
|
|
|
87
94
|
let expanded = input;
|
|
88
95
|
if (options.base) {
|
|
89
96
|
expanded = expanded.replace(/^\$\{CLAUDE_PROJECT_DIR\}|^\$CLAUDE_PROJECT_DIR/, options.projectRoot ?? PROJECT_ROOT);
|
|
90
|
-
expanded = expanded.replace(/^~(?=[\\/]|$)
|
|
97
|
+
expanded = expanded.replace(/^~(?=[\\/]|$)|^(?:\$HOME|\$\{HOME\})(?=[\\/]|$)/, os.homedir());
|
|
91
98
|
if (!POSIX_ABSOLUTE.test(expanded) && !WINDOWS_DRIVE.test(expanded) && !WINDOWS_UNC.test(expanded)) {
|
|
92
99
|
expanded = path.resolve(options.base, expanded);
|
|
93
100
|
}
|
|
@@ -142,7 +149,7 @@ export function isSensitivePolicyKey(key, platform = process.platform) {
|
|
|
142
149
|
return platform === "win32" ? basename.toLowerCase() === "serviceaccountkey.json" : basename === "serviceAccountKey.json";
|
|
143
150
|
}
|
|
144
151
|
function isManaged(key, managedSet = CLAUDE_MANAGED_SET, projectRoot = PROJECT_ROOT, globalManagedPaths = []) {
|
|
145
|
-
if (globalManagedPaths.some((
|
|
152
|
+
if (globalManagedPaths.some((targetPath) => key === canonicalizePolicyPath(path.isAbsolute(targetPath) ? targetPath : path.join(HOST_HOME, targetPath)))) return true;
|
|
146
153
|
const project = canonicalizePolicyPath(projectRoot);
|
|
147
154
|
if (!key.startsWith(`${project}/`) && key !== project) return false;
|
|
148
155
|
const relative = key.slice(project.length + 1);
|
|
@@ -952,30 +959,39 @@ export function parseApplyPatchPaths(command) {
|
|
|
952
959
|
return paths;
|
|
953
960
|
}
|
|
954
961
|
export function evaluateEvent(input, config = AGENT_CONFIGS.claude) {
|
|
955
|
-
if (!isObject(input)
|
|
956
|
-
const
|
|
957
|
-
if (
|
|
958
|
-
const
|
|
962
|
+
if (!isObject(input)) fail("invalid-event");
|
|
963
|
+
const eventName = input.hook_event_name;
|
|
964
|
+
if (eventName !== "PreToolUse" && !(config.id === "cursor" && eventName === "preToolUse")) fail("invalid-event");
|
|
965
|
+
const rawToolName = typeof input.tool_name === "string" ? input.tool_name : input.toolName;
|
|
966
|
+
const toolName = config.id === "grok" && typeof rawToolName === "string" ? mapGrokToolName(rawToolName) : config.id === "cursor" && typeof rawToolName === "string" ? mapCursorToolName(rawToolName) : rawToolName;
|
|
967
|
+
const toolInput = isObject(input.tool_input) ? input.tool_input : input.toolInput;
|
|
968
|
+
if (typeof toolName !== "string" || !isObject(toolInput)) fail("invalid-event");
|
|
969
|
+
if (input.toolInputTruncated === true || input.tool_input_truncated === true) return { allowed: false, ruleId: "tool-input-truncated" };
|
|
970
|
+
const applyPatch = toolName === "apply_patch";
|
|
971
|
+
if (!applyPatch && !SUPPORTED_TOOLS.includes(toolName)) fail("invalid-event");
|
|
972
|
+
const toolWorkingDirectory = isObject(toolInput) && typeof toolInput.working_directory === "string" ? toolInput.working_directory : undefined;
|
|
973
|
+
const cwd = typeof input.cwd === "string" && isAbsolutePolicyPath(input.cwd) ? input.cwd : typeof toolWorkingDirectory === "string" && isAbsolutePolicyPath(toolWorkingDirectory) ? toolWorkingDirectory : PROJECT_ROOT;
|
|
959
974
|
const projectRoot = resolveProjectRoot(config, cwd);
|
|
960
975
|
if (applyPatch) {
|
|
961
|
-
if (typeof
|
|
976
|
+
if (typeof toolInput.command !== "string") fail("invalid-event");
|
|
962
977
|
// apply_patch is a WRITE tool (not "Read"), so the managed-config rule fires. Each header
|
|
963
978
|
// path is made absolute LEXICALLY (no realpath) vs the envelope cwd and handed to evaluateFile
|
|
964
979
|
// exactly like Write/Edit's raw file_path, so policyPathKeys owns the dual lexical+realpath
|
|
965
980
|
// canonicalization. Realpath-ing here first would collapse a symlinked managed dir onto its
|
|
966
981
|
// target and drop the protective lexical key -> managed-config write bypass.
|
|
967
|
-
for (const target of parseApplyPatchPaths(
|
|
982
|
+
for (const target of parseApplyPatchPaths(toolInput.command)) {
|
|
968
983
|
const decision = evaluateFile("apply_patch", lexicalizePolicyPath(target, { base: cwd, projectRoot }), config, projectRoot);
|
|
969
984
|
if (!decision.allowed) return decision;
|
|
970
985
|
}
|
|
971
986
|
return { allowed: true };
|
|
972
987
|
}
|
|
973
|
-
if (
|
|
974
|
-
if (typeof
|
|
975
|
-
return
|
|
988
|
+
if (toolName === "Bash" || toolName === "PowerShell") {
|
|
989
|
+
if (typeof toolInput.command !== "string") fail("invalid-event");
|
|
990
|
+
return toolName === "Bash" ? evaluateBash(toolInput.command, cwd, config, projectRoot) : evaluatePowerShell(toolInput.command, cwd, config, projectRoot);
|
|
976
991
|
}
|
|
977
|
-
|
|
978
|
-
|
|
992
|
+
const filePath = typeof toolInput.file_path === "string" ? toolInput.file_path : typeof toolInput.filePath === "string" ? toolInput.filePath : (config.id === "grok" || config.id === "cursor") && typeof toolInput.path === "string" ? lexicalizePolicyPath(toolInput.path, { base: cwd, projectRoot }) : undefined;
|
|
993
|
+
if (typeof filePath !== "string" || !isAbsolutePolicyPath(filePath)) fail("invalid-event");
|
|
994
|
+
return evaluateFile(toolName, filePath, config, projectRoot);
|
|
979
995
|
}
|
|
980
996
|
export function parseAndEvaluateInput(input) {
|
|
981
997
|
if (!Buffer.isBuffer(input) || input.length === 0) fail("invalid-json");
|
|
@@ -1022,6 +1038,20 @@ function denyAndExit(message) {
|
|
|
1022
1038
|
process.exit(2);
|
|
1023
1039
|
}
|
|
1024
1040
|
}
|
|
1041
|
+
function cursorDecisionAndExit(permission, message) {
|
|
1042
|
+
try {
|
|
1043
|
+
const body = permission === "allow" ? { permission: "allow" } : { permission: "deny", user_message: message, agent_message: message };
|
|
1044
|
+
fs.writeSync(1, `${JSON.stringify(body)}\n`);
|
|
1045
|
+
} finally {
|
|
1046
|
+
process.stdin.destroy();
|
|
1047
|
+
process.stdin.unref?.();
|
|
1048
|
+
process.exit(permission === "allow" ? 0 : 2);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
function failClosed(message, config) {
|
|
1052
|
+
if (config?.id === "cursor") cursorDecisionAndExit("deny", message);
|
|
1053
|
+
denyAndExit(message);
|
|
1054
|
+
}
|
|
1025
1055
|
export function readBoundedStdin(stream = process.stdin) {
|
|
1026
1056
|
return new Promise((resolve, reject) => {
|
|
1027
1057
|
const chunks = [];
|
|
@@ -1072,10 +1102,15 @@ export async function main() {
|
|
|
1072
1102
|
}
|
|
1073
1103
|
if (fault === "evaluator-throw") throw new Error("internal-error");
|
|
1074
1104
|
const decision = evaluateEvent(parsed, config);
|
|
1075
|
-
|
|
1105
|
+
const rawToolName = typeof parsed.tool_name === "string" ? parsed.tool_name : parsed.toolName;
|
|
1106
|
+
const toolName = config.id === "grok" && typeof rawToolName === "string" ? mapGrokToolName(rawToolName) : config.id === "cursor" && typeof rawToolName === "string" ? mapCursorToolName(rawToolName) : rawToolName;
|
|
1107
|
+
if (!decision.allowed) failClosed(denialDiagnostic(toolName, decision), config);
|
|
1108
|
+
if (config.id === "cursor") cursorDecisionAndExit("allow", "");
|
|
1076
1109
|
process.exitCode = 0;
|
|
1077
1110
|
} catch (error) {
|
|
1078
|
-
|
|
1111
|
+
let config;
|
|
1112
|
+
try { config = resolveAgentConfig(process.argv); } catch {}
|
|
1113
|
+
failClosed(diagnostic(error), config);
|
|
1079
1114
|
}
|
|
1080
1115
|
}
|
|
1081
1116
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "javi-forge-skillguard-pre-tool-use.mjs",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"policyVersion": 2,
|
|
7
|
-
"sha256": "
|
|
7
|
+
"sha256": "9c175bb1daed3055420cc6b1d7b4f90a7779da3525b23315e2a66767b8de96dc",
|
|
8
8
|
"historical": [
|
|
9
9
|
"78be7e6613c012280b7ad17886462ba166b63ebd031e34565d757b3a0796d7cc",
|
|
10
10
|
"5dc2a5c31131f4ac7d8657c78b950de52776aad6eaefe78ea0d764a9963c4425",
|
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
"59fc4224975ad64cfc85bab50ec60d9bd4948070e9d41f42ab43e6d8231c19a1",
|
|
16
16
|
"6edfbb31ce0551b27e38ae1ffd1daf9cc4bea54f2c86687c5124132af5b8c0af",
|
|
17
17
|
"507a57a15f1b967103bb1eefe701a74813d5a9603fa7bcc15579a215b03621e3",
|
|
18
|
-
"f55ece1aea76251ec005f2b731fa44a58b6245fef8150b58486ae062167a2377"
|
|
18
|
+
"f55ece1aea76251ec005f2b731fa44a58b6245fef8150b58486ae062167a2377",
|
|
19
|
+
"6fe1ef55363e6e3f38253ca94b17ec50c942352c00d8431cd2ff2f6858c094b6",
|
|
20
|
+
"b79ee8dc3df525810b07215927590366ed2669255666fdc817e9637cf0e10cf2",
|
|
21
|
+
"a352b5b6c29fcdeecf2a014ab0e271cab9373119460134d65134f16a19222e47",
|
|
22
|
+
"044f6816d18a2894532fde52a145f10347f617cbb361fc10075d04f885327d22"
|
|
19
23
|
]
|
|
20
24
|
},
|
|
21
25
|
"settingsEntries": {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Subcommands:
|
|
8
8
|
* - `hooks run <pre-commit|pre-push>` → runHook, exits with its code.
|
|
9
|
-
* - `hooks <install|doctor|repair> <claude|codex|opencode> [--force]` → the matching
|
|
9
|
+
* - `hooks <install|doctor|repair> <claude|codex|opencode|grok|cursor> [--force]` → the matching
|
|
10
10
|
* agent command, exits with its code (unknown/missing agent → usage + exit 1).
|
|
11
11
|
* Any other subcommand or a missing name → usage + exit 1.
|
|
12
12
|
*
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Subcommands:
|
|
8
8
|
* - `hooks run <pre-commit|pre-push>` → runHook, exits with its code.
|
|
9
|
-
* - `hooks <install|doctor|repair> <claude|codex|opencode> [--force]` → the matching
|
|
9
|
+
* - `hooks <install|doctor|repair> <claude|codex|opencode|grok|cursor> [--force]` → the matching
|
|
10
10
|
* agent command, exits with its code (unknown/missing agent → usage + exit 1).
|
|
11
11
|
* Any other subcommand or a missing name → usage + exit 1.
|
|
12
12
|
*
|
|
@@ -24,6 +24,8 @@ const AGENT_COMMAND_LOADERS = {
|
|
|
24
24
|
claude: async () => (await import("../../commands/claude-hooks.js")).runClaudeHookCommand,
|
|
25
25
|
codex: async () => (await import("../../commands/codex-hooks.js")).runCodexHookCommand,
|
|
26
26
|
opencode: async () => (await import("../../commands/opencode-hooks.js")).runOpenCodeHookCommand,
|
|
27
|
+
grok: async () => (await import("../../commands/grok-hooks.js")).runGrokHookCommand,
|
|
28
|
+
cursor: async () => (await import("../../commands/cursor-hooks.js")).runCursorHookCommand,
|
|
27
29
|
};
|
|
28
30
|
export async function handleHooks(cli) {
|
|
29
31
|
if (cli.flags.help === true) {
|
|
@@ -46,7 +48,7 @@ export async function handleHooks(cli) {
|
|
|
46
48
|
// Validity is decided by the agent registry (isAgentId), the single source
|
|
47
49
|
// of truth; the loader map is exhaustive over AgentId by type.
|
|
48
50
|
if (!isAgentId(agent)) {
|
|
49
|
-
console.error(`Usage: javi-forge hooks ${sub} <claude|codex|opencode>`);
|
|
51
|
+
console.error(`Usage: javi-forge hooks ${sub} <claude|codex|opencode|grok|cursor>`);
|
|
50
52
|
process.exit(1);
|
|
51
53
|
}
|
|
52
54
|
const run = await AGENT_COMMAND_LOADERS[agent]();
|
package/dist/cli/help.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export declare const CI_HELP_TEXT = "\n Usage\n $ javi-forge ci [subcommand]
|
|
|
19
19
|
* Per-command help for `hooks`, shown by `javi-forge hooks --help` (or when
|
|
20
20
|
* `hooks` is given an unknown subcommand). Whitespace is significant.
|
|
21
21
|
*/
|
|
22
|
-
export declare const HOOKS_HELP_TEXT = "\n Usage\n $ javi-forge hooks run <pre-commit|pre-push>\n $ javi-forge hooks <install|doctor|repair> <claude|codex|opencode> [--force]\n\n Run the sections enabled under hooks: in .javi-forge/ci.yaml, in a fixed\n cheap\u2192expensive order, fail-fast. With no hooks: config the default is the\n quick native CI gate (setup + lint + compile + gates \u2014 no tests, no coverage).\n\n Subcommands\n run pre-commit Run the composed pre-commit sections\n run pre-push Run the composed pre-push sections\n install claude Install the managed Claude PreToolUse guard (.claude/)\n doctor claude Report Claude PreToolUse guard health (informational)\n repair claude Repair the managed guard; --force overwrites edited assets\n install codex Install the managed Codex PreToolUse guard (~/.codex/)\n doctor codex Report Codex hook execution readiness and trust boundary\n repair codex Repair the managed Codex guard; --force overwrites edits\n install opencode Install the managed OpenCode global plugin (~/.config/opencode/plugins/)\n doctor opencode Report OpenCode plugin and policy file currency (informational)\n repair opencode Repair the OpenCode plugin pair; --force overwrites edits\n\n Notes\n A blocking section failure exits non-zero and blocks the commit/push.\n A broken .javi-forge/ci.yaml exits 1 (fail-closed \u2014 never skips a gate).\n To skip: git commit --no-verify (pre-push: git push --no-verify)\n doctor claude is informational (always exits 0); install/repair exit 0 on\n success, non-zero on refusal/failure. Use repair claude --force to overwrite\n a locally edited managed asset.\n doctor codex exits 0 when runnable, 1 when blocked, and 2 when inconclusive;\n it does not prove provider trust or runtime execution. Use repair codex\n --force only to overwrite an edited managed asset.\n doctor opencode is informational and inspects installed files only; it does\n not prove OpenCode discovered, loaded, or executed the plugin.\n Linux: install/repair claude need the acl package (getfacl) to
|
|
22
|
+
export declare const HOOKS_HELP_TEXT = "\n Usage\n $ javi-forge hooks run <pre-commit|pre-push>\n $ javi-forge hooks <install|doctor|repair> <claude|codex|opencode|grok|cursor> [--force]\n\n Run the sections enabled under hooks: in .javi-forge/ci.yaml, in a fixed\n cheap\u2192expensive order, fail-fast. With no hooks: config the default is the\n quick native CI gate (setup + lint + compile + gates \u2014 no tests, no coverage).\n\n Subcommands\n run pre-commit Run the composed pre-commit sections\n run pre-push Run the composed pre-push sections\n install claude Install the managed Claude PreToolUse guard (.claude/)\n doctor claude Report Claude PreToolUse guard health (informational)\n repair claude Repair the managed guard; --force overwrites edited assets\n install codex Install the managed Codex PreToolUse guard (~/.codex/)\n doctor codex Report Codex hook execution readiness and trust boundary\n repair codex Repair the managed Codex guard; --force overwrites edits\n install opencode Install the managed OpenCode global plugin (~/.config/opencode/plugins/)\n doctor opencode Report OpenCode plugin and policy file currency (informational)\n repair opencode Repair the OpenCode plugin pair; --force overwrites edits\n install grok Install the Grok Build global PreToolUse hook (~/.grok/hooks/)\n doctor grok Report Grok hook registration and policy file currency (informational)\n repair grok Repair the Grok hook pair; --force overwrites edits\n install cursor Install the Cursor global preToolUse hook (~/.cursor/)\n doctor cursor Report Cursor hook registration and policy file currency (informational)\n repair cursor Repair the Cursor hook pair; --force overwrites edits\n\n Notes\n A blocking section failure exits non-zero and blocks the commit/push.\n A broken .javi-forge/ci.yaml exits 1 (fail-closed \u2014 never skips a gate).\n To skip: git commit --no-verify (pre-push: git push --no-verify)\n doctor claude is informational (always exits 0); install/repair exit 0 on\n success, non-zero on refusal/failure. Use repair claude --force to overwrite\n a locally edited managed asset.\n doctor codex exits 0 when runnable, 1 when blocked, and 2 when inconclusive;\n it does not prove provider trust or runtime execution. Use repair codex\n --force only to overwrite an edited managed asset.\n doctor opencode is informational and inspects installed files only; it does\n not prove OpenCode discovered, loaded, or executed the plugin.\n doctor grok is informational and inspects the global hook registration and\n adjacent policy runtime only; it does not prove Grok loaded or executed them.\n doctor cursor is informational and inspects the global hook registration and\n adjacent policy runtime only; it does not prove Cursor loaded or executed them.\n Linux: install/repair claude, grok, and cursor need the acl package (getfacl) to\n prove the parent chain \u2014 apt install acl / apk add acl / dnf install acl.\n Without it\n they refuse fail-closed; an already-installed guard keeps firing, and\n doctor claude reports the acl capability as its own row.\n Claude Code spawns the guard with node from ITS path, so node must resolve\n there, not only inside javi-forge.\n\n Examples\n $ javi-forge hooks run pre-commit\n $ javi-forge hooks run pre-push\n $ javi-forge hooks install claude\n $ javi-forge hooks doctor claude\n $ javi-forge hooks repair claude --force\n $ javi-forge hooks install codex\n $ javi-forge hooks doctor codex\n $ javi-forge hooks repair codex --force\n $ javi-forge hooks install opencode\n $ javi-forge hooks doctor opencode\n $ javi-forge hooks repair opencode --force\n $ javi-forge hooks install grok\n $ javi-forge hooks doctor grok\n $ javi-forge hooks repair grok --force\n $ javi-forge hooks install cursor\n $ javi-forge hooks doctor cursor\n $ javi-forge hooks repair cursor --force\n";
|
|
23
23
|
export declare const FLAGS_SCHEMA: {
|
|
24
24
|
readonly help: {
|
|
25
25
|
readonly type: "boolean";
|
package/dist/cli/help.js
CHANGED
|
@@ -167,7 +167,7 @@ export const CI_HELP_TEXT = `
|
|
|
167
167
|
export const HOOKS_HELP_TEXT = `
|
|
168
168
|
Usage
|
|
169
169
|
$ javi-forge hooks run <pre-commit|pre-push>
|
|
170
|
-
$ javi-forge hooks <install|doctor|repair> <claude|codex|opencode> [--force]
|
|
170
|
+
$ javi-forge hooks <install|doctor|repair> <claude|codex|opencode|grok|cursor> [--force]
|
|
171
171
|
|
|
172
172
|
Run the sections enabled under hooks: in .javi-forge/ci.yaml, in a fixed
|
|
173
173
|
cheap→expensive order, fail-fast. With no hooks: config the default is the
|
|
@@ -185,6 +185,12 @@ export const HOOKS_HELP_TEXT = `
|
|
|
185
185
|
install opencode Install the managed OpenCode global plugin (~/.config/opencode/plugins/)
|
|
186
186
|
doctor opencode Report OpenCode plugin and policy file currency (informational)
|
|
187
187
|
repair opencode Repair the OpenCode plugin pair; --force overwrites edits
|
|
188
|
+
install grok Install the Grok Build global PreToolUse hook (~/.grok/hooks/)
|
|
189
|
+
doctor grok Report Grok hook registration and policy file currency (informational)
|
|
190
|
+
repair grok Repair the Grok hook pair; --force overwrites edits
|
|
191
|
+
install cursor Install the Cursor global preToolUse hook (~/.cursor/)
|
|
192
|
+
doctor cursor Report Cursor hook registration and policy file currency (informational)
|
|
193
|
+
repair cursor Repair the Cursor hook pair; --force overwrites edits
|
|
188
194
|
|
|
189
195
|
Notes
|
|
190
196
|
A blocking section failure exits non-zero and blocks the commit/push.
|
|
@@ -198,8 +204,13 @@ export const HOOKS_HELP_TEXT = `
|
|
|
198
204
|
--force only to overwrite an edited managed asset.
|
|
199
205
|
doctor opencode is informational and inspects installed files only; it does
|
|
200
206
|
not prove OpenCode discovered, loaded, or executed the plugin.
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
doctor grok is informational and inspects the global hook registration and
|
|
208
|
+
adjacent policy runtime only; it does not prove Grok loaded or executed them.
|
|
209
|
+
doctor cursor is informational and inspects the global hook registration and
|
|
210
|
+
adjacent policy runtime only; it does not prove Cursor loaded or executed them.
|
|
211
|
+
Linux: install/repair claude, grok, and cursor need the acl package (getfacl) to
|
|
212
|
+
prove the parent chain — apt install acl / apk add acl / dnf install acl.
|
|
213
|
+
Without it
|
|
203
214
|
they refuse fail-closed; an already-installed guard keeps firing, and
|
|
204
215
|
doctor claude reports the acl capability as its own row.
|
|
205
216
|
Claude Code spawns the guard with node from ITS path, so node must resolve
|
|
@@ -217,6 +228,12 @@ export const HOOKS_HELP_TEXT = `
|
|
|
217
228
|
$ javi-forge hooks install opencode
|
|
218
229
|
$ javi-forge hooks doctor opencode
|
|
219
230
|
$ javi-forge hooks repair opencode --force
|
|
231
|
+
$ javi-forge hooks install grok
|
|
232
|
+
$ javi-forge hooks doctor grok
|
|
233
|
+
$ javi-forge hooks repair grok --force
|
|
234
|
+
$ javi-forge hooks install cursor
|
|
235
|
+
$ javi-forge hooks doctor cursor
|
|
236
|
+
$ javi-forge hooks repair cursor --force
|
|
220
237
|
`;
|
|
221
238
|
export const FLAGS_SCHEMA = {
|
|
222
239
|
// `--help` is handled manually (autoHelp is disabled at the entrypoint so
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Console renderer for the Cursor global SkillGuard hook manager. */
|
|
2
|
+
import { doctorCursorSkillGuard, installCursorSkillGuard, repairCursorSkillGuard } from "../lib/cursor-hook-manager.js";
|
|
3
|
+
export type CursorHookSub = "install" | "doctor" | "repair";
|
|
4
|
+
export interface CursorHookCmdDeps {
|
|
5
|
+
install?: typeof installCursorSkillGuard;
|
|
6
|
+
doctor?: typeof doctorCursorSkillGuard;
|
|
7
|
+
repair?: typeof repairCursorSkillGuard;
|
|
8
|
+
homeDir?: string;
|
|
9
|
+
log?: (msg: string) => void;
|
|
10
|
+
logError?: (msg: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function runCursorHookCommand(sub: CursorHookSub, _cwd: string, opts: {
|
|
13
|
+
force?: boolean;
|
|
14
|
+
}, deps?: CursorHookCmdDeps): Promise<number>;
|
|
15
|
+
//# sourceMappingURL=cursor-hooks.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Console renderer for the Cursor global SkillGuard hook manager. */
|
|
2
|
+
import { doctorCursorSkillGuard, installCursorSkillGuard, repairCursorSkillGuard, } from "../lib/cursor-hook-manager.js";
|
|
3
|
+
function renderMutation(verb, result, log, logError) {
|
|
4
|
+
if (!result.ok) {
|
|
5
|
+
logError(`${verb} cursor: refused`);
|
|
6
|
+
for (const error of result.errors)
|
|
7
|
+
logError(` ${error}`);
|
|
8
|
+
for (const backup of result.backups)
|
|
9
|
+
logError(` backup: ${backup}`);
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
log(`${verb} cursor: ok`);
|
|
13
|
+
if (result.changed.length === 0)
|
|
14
|
+
log("changed: nothing (already up to date)");
|
|
15
|
+
else
|
|
16
|
+
for (const changed of result.changed)
|
|
17
|
+
log(`changed: ${changed}`);
|
|
18
|
+
for (const backup of result.backups)
|
|
19
|
+
log(`backup: ${backup}`);
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
function renderDoctor(report, log) {
|
|
23
|
+
log(`doctor cursor: ${report.healthy ? "healthy" : "unhealthy"}`);
|
|
24
|
+
log(` hooks.json: ${report.hooksJson.state}`);
|
|
25
|
+
log(` policy: ${report.policy.state}`);
|
|
26
|
+
log(" runtime: not verified (installed files only)");
|
|
27
|
+
for (const remediation of report.remediation)
|
|
28
|
+
log(` remediation: ${remediation}`);
|
|
29
|
+
// Informational: installed files cannot prove Cursor discovered or invoked them.
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
export async function runCursorHookCommand(sub, _cwd, opts, deps = {}) {
|
|
33
|
+
const log = deps.log ?? ((message) => console.log(message));
|
|
34
|
+
const logError = deps.logError ?? ((message) => console.error(message));
|
|
35
|
+
const home = deps.homeDir;
|
|
36
|
+
if (sub === "install")
|
|
37
|
+
return renderMutation("install", await (deps.install ?? installCursorSkillGuard)(home), log, logError);
|
|
38
|
+
if (sub === "repair")
|
|
39
|
+
return renderMutation("repair", await (deps.repair ?? repairCursorSkillGuard)(home, {
|
|
40
|
+
force: opts.force === true,
|
|
41
|
+
}), log, logError);
|
|
42
|
+
return renderDoctor(await (deps.doctor ?? doctorCursorSkillGuard)(home), log);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=cursor-hooks.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Console renderer for the Grok Build global SkillGuard hook manager. */
|
|
2
|
+
import { doctorGrokSkillGuard, installGrokSkillGuard, repairGrokSkillGuard } from "../lib/grok-hook-manager.js";
|
|
3
|
+
export type GrokHookSub = "install" | "doctor" | "repair";
|
|
4
|
+
export interface GrokHookCmdDeps {
|
|
5
|
+
install?: typeof installGrokSkillGuard;
|
|
6
|
+
doctor?: typeof doctorGrokSkillGuard;
|
|
7
|
+
repair?: typeof repairGrokSkillGuard;
|
|
8
|
+
homeDir?: string;
|
|
9
|
+
log?: (msg: string) => void;
|
|
10
|
+
logError?: (msg: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function runGrokHookCommand(sub: GrokHookSub, _cwd: string, opts: {
|
|
13
|
+
force?: boolean;
|
|
14
|
+
}, deps?: GrokHookCmdDeps): Promise<number>;
|
|
15
|
+
//# sourceMappingURL=grok-hooks.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Console renderer for the Grok Build global SkillGuard hook manager. */
|
|
2
|
+
import { doctorGrokSkillGuard, installGrokSkillGuard, repairGrokSkillGuard, } from "../lib/grok-hook-manager.js";
|
|
3
|
+
function renderMutation(verb, result, log, logError) {
|
|
4
|
+
if (!result.ok) {
|
|
5
|
+
logError(`${verb} grok: refused`);
|
|
6
|
+
for (const error of result.errors)
|
|
7
|
+
logError(` ${error}`);
|
|
8
|
+
for (const backup of result.backups)
|
|
9
|
+
logError(` backup: ${backup}`);
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
log(`${verb} grok: ok`);
|
|
13
|
+
if (result.changed.length === 0)
|
|
14
|
+
log("changed: nothing (already up to date)");
|
|
15
|
+
else
|
|
16
|
+
for (const changed of result.changed)
|
|
17
|
+
log(`changed: ${changed}`);
|
|
18
|
+
for (const backup of result.backups)
|
|
19
|
+
log(`backup: ${backup}`);
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
function renderDoctor(report, log) {
|
|
23
|
+
log(`doctor grok: ${report.healthy ? "healthy" : "unhealthy"}`);
|
|
24
|
+
log(` hook: ${report.hook.state}`);
|
|
25
|
+
log(` policy: ${report.policy.state}`);
|
|
26
|
+
log(" runtime: not verified (installed files only)");
|
|
27
|
+
for (const remediation of report.remediation)
|
|
28
|
+
log(` remediation: ${remediation}`);
|
|
29
|
+
// This is intentionally informational: local files cannot prove that Grok
|
|
30
|
+
// discovered, loaded, or invoked a plugin in a real session.
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
export async function runGrokHookCommand(sub, _cwd, opts, deps = {}) {
|
|
34
|
+
const log = deps.log ?? ((message) => console.log(message));
|
|
35
|
+
const logError = deps.logError ?? ((message) => console.error(message));
|
|
36
|
+
const home = deps.homeDir;
|
|
37
|
+
if (sub === "install")
|
|
38
|
+
return renderMutation("install", await (deps.install ?? installGrokSkillGuard)(home), log, logError);
|
|
39
|
+
if (sub === "repair")
|
|
40
|
+
return renderMutation("repair", await (deps.repair ?? repairGrokSkillGuard)(home, {
|
|
41
|
+
force: opts.force === true,
|
|
42
|
+
}), log, logError);
|
|
43
|
+
return renderDoctor(await (deps.doctor ?? doctorGrokSkillGuard)(home), log);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=grok-hooks.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent adapter registry (agent-agnostic slice 2). One descriptor per host
|
|
3
|
-
* (`claude`, `codex`, `opencode`) capturing the per-agent facts the SkillGuard installer and
|
|
3
|
+
* (`claude`, `codex`, `opencode`, `grok`, `cursor`) capturing the per-agent facts the SkillGuard installer and
|
|
4
4
|
* doctor need: config paths, the protected managed set, the project-root source,
|
|
5
5
|
* the host registration schema, the managed marker, the deny protocol, and the
|
|
6
6
|
* trust model.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* map is typed `Record<AgentId, …>`, so omitting a loader is a compile error.
|
|
15
15
|
*/
|
|
16
16
|
import { validateSettingsShape } from "./claude-hook-settings.js";
|
|
17
|
-
export type AgentId = "claude" | "codex" | "opencode";
|
|
17
|
+
export type AgentId = "claude" | "codex" | "opencode" | "grok" | "cursor";
|
|
18
18
|
export type TrustState = "trusted" | "untrusted" | "unknown";
|
|
19
19
|
export interface TrustDescriptor {
|
|
20
20
|
/** Detect whether the installed hook is TRUSTED from the host config text. */
|
|
@@ -40,13 +40,15 @@ export interface AgentAdapter {
|
|
|
40
40
|
/** The managed asset marker for this host. */
|
|
41
41
|
marker: string;
|
|
42
42
|
/** Deny protocol emitted by the host integration. */
|
|
43
|
-
emitDeny: "exit2+stderr" | "throw-error";
|
|
43
|
+
emitDeny: "exit2+stderr" | "throw-error" | "json-stdout";
|
|
44
44
|
/** Hook-trust model, or null when the host has none (Claude). */
|
|
45
45
|
trust: TrustDescriptor | null;
|
|
46
46
|
}
|
|
47
47
|
export declare const claudeAdapter: AgentAdapter;
|
|
48
48
|
export declare const codexAdapter: AgentAdapter;
|
|
49
49
|
export declare const opencodeAdapter: AgentAdapter;
|
|
50
|
+
export declare const grokAdapter: AgentAdapter;
|
|
51
|
+
export declare const cursorAdapter: AgentAdapter;
|
|
50
52
|
export declare const AGENT_ADAPTERS: Record<AgentId, AgentAdapter>;
|
|
51
53
|
/**
|
|
52
54
|
* Whether a raw CLI token names a known agent adapter. Backed by the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent adapter registry (agent-agnostic slice 2). One descriptor per host
|
|
3
|
-
* (`claude`, `codex`, `opencode`) capturing the per-agent facts the SkillGuard installer and
|
|
3
|
+
* (`claude`, `codex`, `opencode`, `grok`, `cursor`) capturing the per-agent facts the SkillGuard installer and
|
|
4
4
|
* doctor need: config paths, the protected managed set, the project-root source,
|
|
5
5
|
* the host registration schema, the managed marker, the deny protocol, and the
|
|
6
6
|
* trust model.
|
|
@@ -52,6 +52,36 @@ const OPENCODE_MANAGED_SET = [
|
|
|
52
52
|
".claude/agents/",
|
|
53
53
|
".claude/skills/",
|
|
54
54
|
];
|
|
55
|
+
const GROK_MANAGED_SET = [
|
|
56
|
+
".grok/hooks/javi-forge-skillguard-pre-tool-use.json",
|
|
57
|
+
".grok/hooks/javi-forge-skillguard-pre-tool-use.mjs",
|
|
58
|
+
".grok/config.toml",
|
|
59
|
+
"AGENTS.md",
|
|
60
|
+
".claude/settings.json",
|
|
61
|
+
".claude/settings.local.json",
|
|
62
|
+
".claude/CLAUDE.md",
|
|
63
|
+
"CLAUDE.md",
|
|
64
|
+
".javi-forge/ci.yaml",
|
|
65
|
+
".grok/hooks/",
|
|
66
|
+
".claude/hooks/",
|
|
67
|
+
".claude/agents/",
|
|
68
|
+
".claude/skills/",
|
|
69
|
+
];
|
|
70
|
+
const CURSOR_MANAGED_SET = [
|
|
71
|
+
".cursor/hooks.json",
|
|
72
|
+
".cursor/hooks/javi-forge-skillguard-pre-tool-use.mjs",
|
|
73
|
+
"AGENTS.md",
|
|
74
|
+
".claude/settings.json",
|
|
75
|
+
".claude/settings.local.json",
|
|
76
|
+
".claude/CLAUDE.md",
|
|
77
|
+
"CLAUDE.md",
|
|
78
|
+
".javi-forge/ci.yaml",
|
|
79
|
+
".cursor/hooks/",
|
|
80
|
+
".cursor/rules/",
|
|
81
|
+
".claude/hooks/",
|
|
82
|
+
".claude/agents/",
|
|
83
|
+
".claude/skills/",
|
|
84
|
+
];
|
|
55
85
|
export const claudeAdapter = {
|
|
56
86
|
id: "claude",
|
|
57
87
|
configPaths(projectDir) {
|
|
@@ -103,10 +133,44 @@ export const opencodeAdapter = {
|
|
|
103
133
|
emitDeny: "throw-error",
|
|
104
134
|
trust: null,
|
|
105
135
|
};
|
|
136
|
+
export const grokAdapter = {
|
|
137
|
+
id: "grok",
|
|
138
|
+
configPaths(homeDir) {
|
|
139
|
+
const hooksDir = path.join(homeDir, ".grok", "hooks");
|
|
140
|
+
return {
|
|
141
|
+
hooksFile: path.join(hooksDir, "javi-forge-skillguard-pre-tool-use.json"),
|
|
142
|
+
settingsFile: path.join(hooksDir, "javi-forge-skillguard-pre-tool-use.mjs"),
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
managedSet: GROK_MANAGED_SET,
|
|
146
|
+
projectDir: { envVar: null },
|
|
147
|
+
settingsSchema: null,
|
|
148
|
+
marker: "javi-forge-managed: grok-pretooluse v1",
|
|
149
|
+
emitDeny: "exit2+stderr",
|
|
150
|
+
trust: null,
|
|
151
|
+
};
|
|
152
|
+
export const cursorAdapter = {
|
|
153
|
+
id: "cursor",
|
|
154
|
+
configPaths(homeDir) {
|
|
155
|
+
const cursorDir = path.join(homeDir, ".cursor");
|
|
156
|
+
return {
|
|
157
|
+
hooksFile: path.join(cursorDir, "hooks.json"),
|
|
158
|
+
settingsFile: path.join(cursorDir, "hooks", "javi-forge-skillguard-pre-tool-use.mjs"),
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
managedSet: CURSOR_MANAGED_SET,
|
|
162
|
+
projectDir: { envVar: "CURSOR_PROJECT_DIR" },
|
|
163
|
+
settingsSchema: null,
|
|
164
|
+
marker: "// javi-forge-managed: cursor-pretooluse v1",
|
|
165
|
+
emitDeny: "json-stdout",
|
|
166
|
+
trust: null,
|
|
167
|
+
};
|
|
106
168
|
export const AGENT_ADAPTERS = {
|
|
107
169
|
claude: claudeAdapter,
|
|
108
170
|
codex: codexAdapter,
|
|
109
171
|
opencode: opencodeAdapter,
|
|
172
|
+
grok: grokAdapter,
|
|
173
|
+
cursor: cursorAdapter,
|
|
110
174
|
};
|
|
111
175
|
/**
|
|
112
176
|
* Whether a raw CLI token names a known agent adapter. Backed by the
|
|
@@ -0,0 +1,75 @@
|
|
|
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 type { AssetManifestEntry } from "./claude-hook-manager.js";
|
|
9
|
+
import type { ClaudeHookComponentState } from "./claude-hook-settings.js";
|
|
10
|
+
import { type PlatformSecureFs } from "./secure-fs-transaction.js";
|
|
11
|
+
export declare const CURSOR_HOOKS_NAME = "hooks.json";
|
|
12
|
+
export declare const CURSOR_POLICY_NAME = "javi-forge-skillguard-pre-tool-use.mjs";
|
|
13
|
+
export declare const CURSOR_HOOK_MARKER = "javi-forge-managed: cursor-pretooluse v1";
|
|
14
|
+
export declare const CURSOR_POLICY_MARKER = "// javi-forge-managed: claude-pretooluse v1";
|
|
15
|
+
export declare const CURSOR_HOOK_VERSION = 1;
|
|
16
|
+
export declare const CURSOR_TIMEOUT = 30;
|
|
17
|
+
export declare const CURSOR_TOOL_MATCHER = "Shell|Read|Write|Delete";
|
|
18
|
+
export declare const SHIPPED_CURSOR_POLICY: string;
|
|
19
|
+
export interface CursorConfigPaths {
|
|
20
|
+
cursorDir: string;
|
|
21
|
+
hooksDir: string;
|
|
22
|
+
hooksFile: string;
|
|
23
|
+
policyFile: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function cursorConfigPaths(homeDir: string): CursorConfigPaths;
|
|
26
|
+
export interface CursorAssetClassification {
|
|
27
|
+
state: ClaudeHookComponentState;
|
|
28
|
+
sha256?: string;
|
|
29
|
+
version?: number;
|
|
30
|
+
detail?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function classifyCursorAsset(target: string, marker: string, manifest: AssetManifestEntry): Promise<CursorAssetClassification>;
|
|
33
|
+
export declare function expectedCursorHooksJson(policyFile: string): string;
|
|
34
|
+
export declare function mergeCursorHooks(existing: unknown, policyFile: string): Record<string, unknown>;
|
|
35
|
+
export declare function classifyCursorHooksJson(value: unknown, policyFile: string): CursorAssetClassification;
|
|
36
|
+
interface CursorManifest {
|
|
37
|
+
policy: AssetManifestEntry;
|
|
38
|
+
}
|
|
39
|
+
export interface CursorHookDoctorReport {
|
|
40
|
+
healthy: boolean;
|
|
41
|
+
hooksJson: CursorAssetClassification;
|
|
42
|
+
policy: CursorAssetClassification;
|
|
43
|
+
remediation: string[];
|
|
44
|
+
/** Installed files and registration only; no Cursor execution is observed. */
|
|
45
|
+
runtimeEvidence: "not-implemented";
|
|
46
|
+
}
|
|
47
|
+
export interface CursorDoctorOptions {
|
|
48
|
+
manifest?: CursorManifest;
|
|
49
|
+
policySource?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare function doctorCursorSkillGuard(homeDir?: string, options?: CursorDoctorOptions): Promise<CursorHookDoctorReport>;
|
|
52
|
+
interface CursorMutationSuccess {
|
|
53
|
+
ok: boolean;
|
|
54
|
+
changed: string[];
|
|
55
|
+
backups: string[];
|
|
56
|
+
errors: string[];
|
|
57
|
+
warnings: string[];
|
|
58
|
+
report: CursorHookDoctorReport;
|
|
59
|
+
}
|
|
60
|
+
export type CursorHookMutationResult = CursorMutationSuccess;
|
|
61
|
+
export interface CursorHookRunDeps extends CursorDoctorOptions {
|
|
62
|
+
secureFs?: PlatformSecureFs | null;
|
|
63
|
+
clock?: () => Date;
|
|
64
|
+
nonce?: () => string;
|
|
65
|
+
doctor?: typeof doctorCursorSkillGuard;
|
|
66
|
+
}
|
|
67
|
+
export declare function _runCursor(homeDir: string, mode: "install" | "repair", options: {
|
|
68
|
+
force?: boolean;
|
|
69
|
+
}, deps?: CursorHookRunDeps): Promise<CursorHookMutationResult>;
|
|
70
|
+
export declare function installCursorSkillGuard(homeDir?: string, deps?: CursorHookRunDeps): Promise<CursorHookMutationResult>;
|
|
71
|
+
export declare function repairCursorSkillGuard(homeDir?: string, options?: {
|
|
72
|
+
force?: boolean;
|
|
73
|
+
}, deps?: CursorHookRunDeps): Promise<CursorHookMutationResult>;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=cursor-hook-manager.d.ts.map
|