@intutic/sync-daemon 1.9.0 → 1.10.1
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/README.md +2 -2
- package/dist/agentReporter.d.ts +205 -0
- package/dist/agentReporter.d.ts.map +1 -0
- package/dist/agentReporter.js +394 -0
- package/dist/agentReporter.js.map +1 -0
- package/dist/configReader.d.ts +39 -2
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +56 -2
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +51 -1
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +490 -63
- package/dist/configWriter.js.map +1 -1
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +33 -55
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/claudeCodeHooks.d.ts +47 -13
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +323 -55
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
- package/dist/harness/claudeDesktopHooks.js +35 -48
- package/dist/harness/claudeDesktopHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +42 -34
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +36 -0
- package/dist/harness/codexHooks.d.ts.map +1 -0
- package/dist/harness/codexHooks.js +227 -0
- package/dist/harness/codexHooks.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +45 -5
- package/dist/harness/continueHooks.d.ts.map +1 -1
- package/dist/harness/continueHooks.js +225 -5
- package/dist/harness/continueHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +31 -0
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +78 -49
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +198 -0
- package/dist/harness/dshHooks.d.ts.map +1 -0
- package/dist/harness/dshHooks.js +569 -0
- package/dist/harness/dshHooks.js.map +1 -0
- package/dist/harness/gateBody.d.ts +422 -0
- package/dist/harness/gateBody.d.ts.map +1 -0
- package/dist/harness/gateBody.js +1816 -0
- package/dist/harness/gateBody.js.map +1 -0
- package/dist/harness/gateKind.d.ts +164 -0
- package/dist/harness/gateKind.d.ts.map +1 -0
- package/dist/harness/gateKind.js +143 -0
- package/dist/harness/gateKind.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts +54 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
- package/dist/harness/githubCopilotHooks.js +218 -0
- package/dist/harness/githubCopilotHooks.js.map +1 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +67 -37
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +77 -0
- package/dist/harness/grokHooks.d.ts.map +1 -0
- package/dist/harness/grokHooks.js +350 -0
- package/dist/harness/grokHooks.js.map +1 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +26 -55
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/holdRedaction.d.ts +69 -0
- package/dist/harness/holdRedaction.d.ts.map +1 -0
- package/dist/harness/holdRedaction.js +211 -0
- package/dist/harness/holdRedaction.js.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.js +151 -0
- package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +100 -3
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +712 -90
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +53 -0
- package/dist/harness/museHooks.d.ts.map +1 -0
- package/dist/harness/museHooks.js +273 -0
- package/dist/harness/museHooks.js.map +1 -0
- package/dist/harness/n8nHooks.d.ts +25 -9
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +174 -9
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +93 -0
- package/dist/harness/openCodeHooks.d.ts.map +1 -0
- package/dist/harness/openCodeHooks.js +248 -0
- package/dist/harness/openCodeHooks.js.map +1 -0
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +56 -11
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +30 -54
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +35 -41
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +31 -58
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +353 -0
- package/dist/harness/protectedPaths.d.ts.map +1 -0
- package/dist/harness/protectedPaths.js +805 -0
- package/dist/harness/protectedPaths.js.map +1 -0
- package/dist/harness/rooCodeHooks.d.ts.map +1 -1
- package/dist/harness/rooCodeHooks.js +34 -48
- package/dist/harness/rooCodeHooks.js.map +1 -1
- package/dist/harness/windsurfHooks.d.ts +72 -0
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +172 -58
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
- package/dist/harness/windsurfJetBrainsProxy.js +159 -0
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
- package/dist/hashReporter.js +1 -1
- package/dist/hashReporter.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/index.js.map +1 -1
- package/dist/integrityStore.d.ts +0 -8
- package/dist/integrityStore.d.ts.map +1 -1
- package/dist/integrityStore.js +6 -29
- package/dist/integrityStore.js.map +1 -1
- package/dist/lib/approvedBypasses.d.ts +76 -0
- package/dist/lib/approvedBypasses.d.ts.map +1 -0
- package/dist/lib/approvedBypasses.js +140 -0
- package/dist/lib/approvedBypasses.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +5 -0
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +99 -0
- package/dist/lib/decisionsDigest.d.ts.map +1 -0
- package/dist/lib/decisionsDigest.js +199 -0
- package/dist/lib/decisionsDigest.js.map +1 -0
- package/dist/lib/egressPolicy.d.ts +69 -0
- package/dist/lib/egressPolicy.d.ts.map +1 -0
- package/dist/lib/egressPolicy.js +126 -0
- package/dist/lib/egressPolicy.js.map +1 -0
- package/dist/lib/gitWorktrees.d.ts +140 -0
- package/dist/lib/gitWorktrees.d.ts.map +1 -0
- package/dist/lib/gitWorktrees.js +191 -0
- package/dist/lib/gitWorktrees.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +224 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +637 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/processPoller.d.ts +44 -0
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +176 -1
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/runtimeEnv.d.ts +8 -0
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +3 -0
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/localTrajectoryHeuristics.d.ts +51 -0
- package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
- package/dist/localTrajectoryHeuristics.js +90 -0
- package/dist/localTrajectoryHeuristics.js.map +1 -0
- package/dist/sessionReporter.d.ts +70 -0
- package/dist/sessionReporter.d.ts.map +1 -0
- package/dist/sessionReporter.js +140 -0
- package/dist/sessionReporter.js.map +1 -0
- package/dist/skillWriter.d.ts +1 -1
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +6 -3
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncLoop.d.ts +105 -3
- package/dist/syncLoop.d.ts.map +1 -1
- package/dist/syncLoop.js +397 -65
- package/dist/syncLoop.js.map +1 -1
- package/dist/trajectoryMonitor.d.ts +2 -0
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +57 -0
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +12 -3
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +23 -0
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +202 -17
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/package.json +37 -6
- package/dist/brainIndexer.d.ts +0 -51
- package/dist/brainIndexer.d.ts.map +0 -1
- package/dist/brainIndexer.js +0 -148
- package/dist/brainIndexer.js.map +0 -1
- package/dist/harness/cursorHooksJson.d.ts +0 -29
- package/dist/harness/cursorHooksJson.d.ts.map +0 -1
- package/dist/harness/cursorHooksJson.js +0 -130
- package/dist/harness/cursorHooksJson.js.map +0 -1
- package/dist/harness/gooseHooksWriter.d.ts +0 -27
- package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
- package/dist/harness/gooseHooksWriter.js +0 -147
- package/dist/harness/gooseHooksWriter.js.map +0 -1
- package/dist/harness/openhandsHooksWriter.d.ts +0 -23
- package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
- package/dist/harness/openhandsHooksWriter.js +0 -146
- package/dist/harness/openhandsHooksWriter.js.map +0 -1
|
@@ -1,9 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* continueHooks.ts — Continue
|
|
2
|
+
* continueHooks.ts — Continue governance: proxy routing + CLI PreToolUse gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Two mechanisms, for two different Continue surfaces:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Proxy routing** (both surfaces): each model's `apiBase` field in
|
|
7
|
+
* `~/.continue/config.json` is set to `proxyUrl`, so LLM egress traverses
|
|
8
|
+
* the Intutic proxy.
|
|
9
|
+
*
|
|
10
|
+
* 2. **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
11
|
+
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
12
|
+
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
13
|
+
* (project), with a Claude-Code-compatible stdin contract —
|
|
14
|
+
* {tool_name, tool_input, tool_use_id} — refused via exit code 2 (or a
|
|
15
|
+
* stdout `{"permissionDecision":"deny"}`; exit 2 is used here, the contract
|
|
16
|
+
* every other exit-2 harness shares).
|
|
17
|
+
*
|
|
18
|
+
* **LIVE-VERIFIED LIMITS (cn 1.5.47, 2026-08-10), do not oversell this
|
|
19
|
+
* gate:**
|
|
20
|
+
* - **Headless `-p`/`--print` mode does not execute PreToolUse hooks at
|
|
21
|
+
* all.** Verified against a real install with a bare marker hook: the
|
|
22
|
+
* terminal tool ran, the marker never fired, with the hook registered in
|
|
23
|
+
* every path the bundle's own search list names. The hooks engine (and
|
|
24
|
+
* this exact search list, including `.claude/settings.json`) is present
|
|
25
|
+
* in the shipped bundle — headless mode simply bypasses it. Interactive
|
|
26
|
+
* TUI sessions are the mode this registration targets; a headless `cn
|
|
27
|
+
* -p` run is governed by the proxy layer only.
|
|
28
|
+
* - **cn's hook engine fails OPEN on hook errors by its own design** —
|
|
29
|
+
* the bundled dispatcher catches a failed hook event and returns
|
|
30
|
+
* `{blocked: false}`. Our gate's own crash posture (fail-closed exit 2)
|
|
31
|
+
* cannot override the harness swallowing the failure; that is cn's
|
|
32
|
+
* contract, recorded here so the security matrix can say it honestly.
|
|
33
|
+
*
|
|
34
|
+
* **Overlap, stated so nobody "fixes" it:** the Continue CLI ALSO reads
|
|
35
|
+
* `.claude/settings.json`, so on a machine where claude-code is governed,
|
|
36
|
+
* `cn` may already run the claude-code gate by accident. This writer makes
|
|
37
|
+
* that deliberate by registering in Continue's own settings files — the
|
|
38
|
+
* dedicated registration is still correct because a user who runs continue
|
|
39
|
+
* but not claude-code has no `.claude/settings.json` and would otherwise
|
|
40
|
+
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
41
|
+
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
7
42
|
*
|
|
8
43
|
* JSONC handling: ~/.continue/config.json allows `// comments`. We strip
|
|
9
44
|
* single-line comments with a regex before parsing, then write back as
|
|
@@ -15,13 +50,18 @@
|
|
|
15
50
|
* @module
|
|
16
51
|
*/
|
|
17
52
|
/**
|
|
18
|
-
* Configure Continue
|
|
53
|
+
* Configure Continue: proxy routing for both surfaces, and the CLI's
|
|
54
|
+
* PreToolUse gate.
|
|
19
55
|
*
|
|
20
56
|
* 1. Reads `~/.continue/config.json` (JSONC — strips // comments before parsing).
|
|
21
57
|
* 2. Sets `apiBase = proxyUrl` on each model that doesn't already use it.
|
|
22
58
|
* 3. Adds a `customInstructions` governance notice.
|
|
23
59
|
* 4. Writes the merged result back atomically.
|
|
24
60
|
* 5. Writes `.intutic/env/continue.env` with the proxy URL.
|
|
61
|
+
* 6. Writes the gate script `.intutic/hooks/continue-check.js` and registers
|
|
62
|
+
* it in `~/.continue/settings.json` and `<root>/.continue/settings.json`
|
|
63
|
+
* (Continue CLI PreToolUse — see the module header for the
|
|
64
|
+
* `.claude/settings.json` overlap).
|
|
25
65
|
*
|
|
26
66
|
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
27
67
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"continueHooks.d.ts","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"continueHooks.d.ts","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AA2MH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAoHf"}
|
|
@@ -1,9 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* continueHooks.ts — Continue
|
|
2
|
+
* continueHooks.ts — Continue governance: proxy routing + CLI PreToolUse gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Two mechanisms, for two different Continue surfaces:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Proxy routing** (both surfaces): each model's `apiBase` field in
|
|
7
|
+
* `~/.continue/config.json` is set to `proxyUrl`, so LLM egress traverses
|
|
8
|
+
* the Intutic proxy.
|
|
9
|
+
*
|
|
10
|
+
* 2. **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
11
|
+
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
12
|
+
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
13
|
+
* (project), with a Claude-Code-compatible stdin contract —
|
|
14
|
+
* {tool_name, tool_input, tool_use_id} — refused via exit code 2 (or a
|
|
15
|
+
* stdout `{"permissionDecision":"deny"}`; exit 2 is used here, the contract
|
|
16
|
+
* every other exit-2 harness shares).
|
|
17
|
+
*
|
|
18
|
+
* **LIVE-VERIFIED LIMITS (cn 1.5.47, 2026-08-10), do not oversell this
|
|
19
|
+
* gate:**
|
|
20
|
+
* - **Headless `-p`/`--print` mode does not execute PreToolUse hooks at
|
|
21
|
+
* all.** Verified against a real install with a bare marker hook: the
|
|
22
|
+
* terminal tool ran, the marker never fired, with the hook registered in
|
|
23
|
+
* every path the bundle's own search list names. The hooks engine (and
|
|
24
|
+
* this exact search list, including `.claude/settings.json`) is present
|
|
25
|
+
* in the shipped bundle — headless mode simply bypasses it. Interactive
|
|
26
|
+
* TUI sessions are the mode this registration targets; a headless `cn
|
|
27
|
+
* -p` run is governed by the proxy layer only.
|
|
28
|
+
* - **cn's hook engine fails OPEN on hook errors by its own design** —
|
|
29
|
+
* the bundled dispatcher catches a failed hook event and returns
|
|
30
|
+
* `{blocked: false}`. Our gate's own crash posture (fail-closed exit 2)
|
|
31
|
+
* cannot override the harness swallowing the failure; that is cn's
|
|
32
|
+
* contract, recorded here so the security matrix can say it honestly.
|
|
33
|
+
*
|
|
34
|
+
* **Overlap, stated so nobody "fixes" it:** the Continue CLI ALSO reads
|
|
35
|
+
* `.claude/settings.json`, so on a machine where claude-code is governed,
|
|
36
|
+
* `cn` may already run the claude-code gate by accident. This writer makes
|
|
37
|
+
* that deliberate by registering in Continue's own settings files — the
|
|
38
|
+
* dedicated registration is still correct because a user who runs continue
|
|
39
|
+
* but not claude-code has no `.claude/settings.json` and would otherwise
|
|
40
|
+
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
41
|
+
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
7
42
|
*
|
|
8
43
|
* JSONC handling: ~/.continue/config.json allows `// comments`. We strip
|
|
9
44
|
* single-line comments with a regex before parsing, then write back as
|
|
@@ -19,6 +54,7 @@ import * as path from 'node:path';
|
|
|
19
54
|
import * as os from 'node:os';
|
|
20
55
|
import { createLogger } from '@intutic/logger';
|
|
21
56
|
import { newIso } from '@intutic/id';
|
|
57
|
+
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
22
58
|
const log = createLogger('sync-continue-hooks');
|
|
23
59
|
/** Default path to the Continue.dev global config file. */
|
|
24
60
|
const CONTINUE_CONFIG_PATH = path.join(os.homedir(), '.continue', 'config.json');
|
|
@@ -39,15 +75,177 @@ function stripJsoncComments(raw) {
|
|
|
39
75
|
// Simple approach: replace // up to end-of-line (acceptable for Continue config).
|
|
40
76
|
return raw.replace(/\/\/[^\n]*/g, '');
|
|
41
77
|
}
|
|
78
|
+
// ─── PreToolUse gate (Continue CLI `cn`) ──────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* The pre-tool gate script — receives the Continue CLI's Claude-Code-compatible
|
|
81
|
+
* PreToolUse JSON on stdin, exits 0 to allow or 2 to block.
|
|
82
|
+
*/
|
|
83
|
+
function buildHookScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
84
|
+
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
85
|
+
return `#!/usr/bin/env node
|
|
86
|
+
/**
|
|
87
|
+
* Intutic Continue CLI governance gate.
|
|
88
|
+
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
89
|
+
*
|
|
90
|
+
* Applies to the Continue CLI (cn) only — the IDE extension has no hook
|
|
91
|
+
* system. Note the CLI also reads .claude/settings.json, so a machine governed
|
|
92
|
+
* for claude-code may run that gate too; both evaluate the same rules.
|
|
93
|
+
*
|
|
94
|
+
* Proxy: ${proxyUrl}
|
|
95
|
+
* Generated: ${newIso()}
|
|
96
|
+
*/
|
|
97
|
+
${emitJsFailClosedPrelude({ harness: 'continue', contract: 'exit2' })}
|
|
98
|
+
const path = require('path');
|
|
99
|
+
const os = require('os');
|
|
100
|
+
const fs = require('fs');
|
|
101
|
+
const crypto = require('crypto');
|
|
102
|
+
const https = require('https');
|
|
103
|
+
|
|
104
|
+
// Runtime credentials
|
|
105
|
+
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
106
|
+
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
|
|
107
|
+
try {
|
|
108
|
+
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
109
|
+
const eq = line.indexOf('='); if (eq < 0) return;
|
|
110
|
+
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
111
|
+
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
112
|
+
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
113
|
+
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
114
|
+
});
|
|
115
|
+
} catch {}
|
|
116
|
+
|
|
117
|
+
${emitJsGate({ harness: 'continue', contract: 'exit2' })}
|
|
118
|
+
|
|
119
|
+
let _intuticSessionId = '';
|
|
120
|
+
function logEvent(verdict, toolName, reason) {
|
|
121
|
+
try {
|
|
122
|
+
const ts = new Date().toISOString();
|
|
123
|
+
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
124
|
+
const entry = JSON.stringify({
|
|
125
|
+
// Passed through, not collapsed to two values: the advisory tier emits
|
|
126
|
+
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
127
|
+
event: verdict,
|
|
128
|
+
toolName, reason: reason || '',
|
|
129
|
+
workspaceId: _intuticWsId,
|
|
130
|
+
harnessType: 'continue',
|
|
131
|
+
timestamp: ts,
|
|
132
|
+
incidentId,
|
|
133
|
+
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
134
|
+
}) + '\\n';
|
|
135
|
+
fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
|
|
136
|
+
if (_intuticKey) {
|
|
137
|
+
try {
|
|
138
|
+
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
139
|
+
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
140
|
+
const mod = urlObj.protocol === 'https:' ? https : require('http');
|
|
141
|
+
const req = mod.request({ hostname: urlObj.hostname, port: urlObj.port || 443, path: urlObj.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'Authorization': 'Bearer ' + _intuticKey } });
|
|
142
|
+
req.on('error', () => { /* fire-and-forget */ });
|
|
143
|
+
req.write(body); req.end();
|
|
144
|
+
} catch { /* never crash the hook */ }
|
|
145
|
+
}
|
|
146
|
+
} catch { /* never crash the hook */ }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let raw = '';
|
|
150
|
+
process.stdin.on('data', (c) => { raw += c; });
|
|
151
|
+
process.stdin.on('end', () => {
|
|
152
|
+
try {
|
|
153
|
+
const ctx = JSON.parse(raw);
|
|
154
|
+
_intuticSessionId = ctx.session_id || ctx.sessionId || '';
|
|
155
|
+
// Verified during the v5 audit: this writer did NOT have the envelope
|
|
156
|
+
// guard its Claude-Code-compatible contract implied it inherited — a
|
|
157
|
+
// payload with neither a tool name nor a tool_input defaulted to toolName
|
|
158
|
+
// 'tool' with empty fields, matched no rule, and was allowed. Refused now.
|
|
159
|
+
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
|
|
160
|
+
const input = ctx.tool_input || ctx.toolInput || {};
|
|
161
|
+
const toolName = ctx.tool_name || ctx.toolName || 'tool';
|
|
162
|
+
|
|
163
|
+
// Every spelling any harness uses, matching the bash extractor. A field
|
|
164
|
+
// name this list misses is a path guard that silently does not run — and
|
|
165
|
+
// the caller controls which spelling it sends.
|
|
166
|
+
const targetPath = input.path || input.file_path || input.filePath || input.file ||
|
|
167
|
+
input.target || input.notebook_path || '';
|
|
168
|
+
const command = input.command || input.cmd || input.script || '';
|
|
169
|
+
|
|
170
|
+
intuticGate(toolName, targetPath, command, logEvent, _intuticWsId, input);
|
|
171
|
+
|
|
172
|
+
// Allow
|
|
173
|
+
logEvent('tool_allowed', toolName, '');
|
|
174
|
+
process.exit(0);
|
|
175
|
+
} catch (err) {
|
|
176
|
+
// Fail CLOSED
|
|
177
|
+
const errMsg = String(err);
|
|
178
|
+
process.stderr.write('[Intutic Governance] Hook error (fail-closed): ' + errMsg + '\\n');
|
|
179
|
+
logEvent('tool_blocked', 'unknown', errMsg);
|
|
180
|
+
process.exit(2);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
`;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Merge the Intutic PreToolUse entry into a Continue settings.json, preserving
|
|
187
|
+
* everything the user registered themselves.
|
|
188
|
+
*
|
|
189
|
+
* De-duplication is by command (any entry whose hooks all invoke this gate
|
|
190
|
+
* script is ours and is replaced) — the claudeDesktopHooks convention — so
|
|
191
|
+
* repeated syncs never stack duplicate entries.
|
|
192
|
+
*/
|
|
193
|
+
async function mergeContinueSettings(settingsPath, hookScriptPath) {
|
|
194
|
+
let existing = {};
|
|
195
|
+
try {
|
|
196
|
+
existing = JSON.parse(stripJsoncComments(await fs.readFile(settingsPath, 'utf-8')));
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
// File doesn't exist or is malformed — start fresh
|
|
200
|
+
}
|
|
201
|
+
const existingHooks = existing.hooks ?? {};
|
|
202
|
+
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
|
|
203
|
+
? existingHooks.PreToolUse
|
|
204
|
+
: [];
|
|
205
|
+
const intuticCmd = `node "${hookScriptPath}"`;
|
|
206
|
+
const filtered = existingPreToolUse.filter((entry) => {
|
|
207
|
+
if (typeof entry !== 'object' || entry === null)
|
|
208
|
+
return true;
|
|
209
|
+
const inner = entry.hooks ?? [];
|
|
210
|
+
return !(inner.length > 0 &&
|
|
211
|
+
inner.every((h) => typeof h === 'object' && h !== null && h.command === intuticCmd));
|
|
212
|
+
});
|
|
213
|
+
const merged = {
|
|
214
|
+
...existing,
|
|
215
|
+
_intutic_comment: 'Intutic governance PreToolUse hook (Continue CLI) — auto-managed entries only; your own hooks are preserved.',
|
|
216
|
+
_intutic_last_sync: newIso(),
|
|
217
|
+
hooks: {
|
|
218
|
+
...existingHooks,
|
|
219
|
+
PreToolUse: [
|
|
220
|
+
...filtered,
|
|
221
|
+
{
|
|
222
|
+
// One catch-all matcher rather than a tool list: the gate itself
|
|
223
|
+
// decides from the arguments, so a tool name nobody anticipated is
|
|
224
|
+
// still evaluated rather than silently unmatched.
|
|
225
|
+
matcher: '.*',
|
|
226
|
+
hooks: [{ type: 'command', command: intuticCmd }],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
const tmp = settingsPath + '.intutic-tmp';
|
|
232
|
+
await fs.writeFile(tmp, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
233
|
+
await fs.rename(tmp, settingsPath);
|
|
234
|
+
}
|
|
42
235
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
43
236
|
/**
|
|
44
|
-
* Configure Continue
|
|
237
|
+
* Configure Continue: proxy routing for both surfaces, and the CLI's
|
|
238
|
+
* PreToolUse gate.
|
|
45
239
|
*
|
|
46
240
|
* 1. Reads `~/.continue/config.json` (JSONC — strips // comments before parsing).
|
|
47
241
|
* 2. Sets `apiBase = proxyUrl` on each model that doesn't already use it.
|
|
48
242
|
* 3. Adds a `customInstructions` governance notice.
|
|
49
243
|
* 4. Writes the merged result back atomically.
|
|
50
244
|
* 5. Writes `.intutic/env/continue.env` with the proxy URL.
|
|
245
|
+
* 6. Writes the gate script `.intutic/hooks/continue-check.js` and registers
|
|
246
|
+
* it in `~/.continue/settings.json` and `<root>/.continue/settings.json`
|
|
247
|
+
* (Continue CLI PreToolUse — see the module header for the
|
|
248
|
+
* `.claude/settings.json` overlap).
|
|
51
249
|
*
|
|
52
250
|
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
53
251
|
*
|
|
@@ -120,5 +318,27 @@ export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
120
318
|
await fs.writeFile(tmpEnv, envContent, 'utf-8');
|
|
121
319
|
await fs.rename(tmpEnv, envFilePath);
|
|
122
320
|
log.info({ action: 'continue_env_written', path: envFilePath }, 'Continue.dev env snippet written');
|
|
321
|
+
// ── 7. PreToolUse gate for the Continue CLI (cn) ──────────────────────────
|
|
322
|
+
const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
|
|
323
|
+
await fs.mkdir(hookScriptDir, { recursive: true });
|
|
324
|
+
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
325
|
+
// Named after its writer — see the note in claudeCodeHooks.ts about two
|
|
326
|
+
// writers overwriting one `pre-tool-check.js`.
|
|
327
|
+
const hookScriptPath = path.join(hookScriptDir, 'continue-check.js');
|
|
328
|
+
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
329
|
+
await fs.writeFile(tmpScript, buildHookScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
|
|
330
|
+
await fs.rename(tmpScript, hookScriptPath);
|
|
331
|
+
await fs.chmod(hookScriptPath, 0o755);
|
|
332
|
+
// Project-level: <root>/.continue/settings.json
|
|
333
|
+
const projectContinueDir = path.join(workspaceRoot, '.continue');
|
|
334
|
+
await fs.mkdir(projectContinueDir, { recursive: true });
|
|
335
|
+
await mergeContinueSettings(path.join(projectContinueDir, 'settings.json'), hookScriptPath);
|
|
336
|
+
// User-level: ~/.continue/settings.json. homedir() read at call time (not the
|
|
337
|
+
// module-scope constant above) so tests that move HOME before invoking are
|
|
338
|
+
// honoured — see the gooseHooks note in generatedShellIntegrity.test.ts.
|
|
339
|
+
const userContinueDir = path.join(os.homedir(), '.continue');
|
|
340
|
+
await fs.mkdir(userContinueDir, { recursive: true });
|
|
341
|
+
await mergeContinueSettings(path.join(userContinueDir, 'settings.json'), hookScriptPath);
|
|
342
|
+
log.info({ action: 'continue_gate_written', path: hookScriptPath }, 'Continue CLI PreToolUse gate written and registered (project + user settings.json)');
|
|
123
343
|
}
|
|
124
344
|
//# sourceMappingURL=continueHooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"continueHooks.js","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"continueHooks.js","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,37 @@
|
|
|
14
14
|
*
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
|
+
/**
|
|
18
|
+
* Where system-level hooks.json lives for a given platform. Exported (not
|
|
19
|
+
* just inlined in `writeCursorHooks` below) so `mdmManifest.ts` can target
|
|
20
|
+
* the SAME path this file actually writes to — otherwise the MDM manifest's
|
|
21
|
+
* `target_path` and this file's real write path are two hand-typed copies
|
|
22
|
+
* of the same fact, which is exactly how they drifted before (see the
|
|
23
|
+
* `hookScriptPath` note below).
|
|
24
|
+
*/
|
|
25
|
+
export declare function systemHooksDirFor(platform: NodeJS.Platform): string;
|
|
26
|
+
/**
|
|
27
|
+
* Build a Cursor hooks.json config object.
|
|
28
|
+
*
|
|
29
|
+
* Exported so `enterprise install` (`tools/cli/src/lib/mdmManifest.ts`) can
|
|
30
|
+
* populate its generated MDM hooks manifest from the SAME shape this file
|
|
31
|
+
* writes, rather than a hand-retyped literal — which is exactly how that
|
|
32
|
+
* manifest drifted before (it named `pre-tool-check.js`, this file's real
|
|
33
|
+
* script is `cursor-check.js`; see the note in `writeCursorHooks` below).
|
|
34
|
+
*
|
|
35
|
+
* @param hookScriptPath - Absolute path to the pre-tool-check script.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildHooksConfig(hookScriptPath: string): {
|
|
38
|
+
_comment: string;
|
|
39
|
+
_lastSync: string;
|
|
40
|
+
failClosed: boolean;
|
|
41
|
+
hooks: {
|
|
42
|
+
[k: string]: {
|
|
43
|
+
command: string;
|
|
44
|
+
failClosed: boolean;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
17
48
|
/**
|
|
18
49
|
* Write Cursor hooks at user-level and project-level.
|
|
19
50
|
* System-level (/etc/cursor) is handled by the corporate policy installer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursorHooks.d.ts","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"cursorHooks.d.ts","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkBH;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM;;;;;;;;;;EAetD;AAoID;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,EAChB,gBAAgB,UAAQ,GACvB,OAAO,CAAC,IAAI,CAAC,CA8Cf"}
|
|
@@ -19,6 +19,7 @@ import * as path from 'node:path';
|
|
|
19
19
|
import * as os from 'node:os';
|
|
20
20
|
import { createLogger } from '@intutic/logger';
|
|
21
21
|
import { newIso } from '@intutic/id';
|
|
22
|
+
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
22
23
|
const log = createLogger('sync-cursor-hooks');
|
|
23
24
|
/** Hook events Cursor fires before execution. */
|
|
24
25
|
const HOOK_EVENTS = [
|
|
@@ -26,12 +27,29 @@ const HOOK_EVENTS = [
|
|
|
26
27
|
'beforeMCPExecution',
|
|
27
28
|
'beforeFileEdit',
|
|
28
29
|
];
|
|
30
|
+
/**
|
|
31
|
+
* Where system-level hooks.json lives for a given platform. Exported (not
|
|
32
|
+
* just inlined in `writeCursorHooks` below) so `mdmManifest.ts` can target
|
|
33
|
+
* the SAME path this file actually writes to — otherwise the MDM manifest's
|
|
34
|
+
* `target_path` and this file's real write path are two hand-typed copies
|
|
35
|
+
* of the same fact, which is exactly how they drifted before (see the
|
|
36
|
+
* `hookScriptPath` note below).
|
|
37
|
+
*/
|
|
38
|
+
export function systemHooksDirFor(platform) {
|
|
39
|
+
return platform === 'darwin' ? '/Library/Application Support/Cursor' : '/etc/cursor';
|
|
40
|
+
}
|
|
29
41
|
/**
|
|
30
42
|
* Build a Cursor hooks.json config object.
|
|
31
43
|
*
|
|
44
|
+
* Exported so `enterprise install` (`tools/cli/src/lib/mdmManifest.ts`) can
|
|
45
|
+
* populate its generated MDM hooks manifest from the SAME shape this file
|
|
46
|
+
* writes, rather than a hand-retyped literal — which is exactly how that
|
|
47
|
+
* manifest drifted before (it named `pre-tool-check.js`, this file's real
|
|
48
|
+
* script is `cursor-check.js`; see the note in `writeCursorHooks` below).
|
|
49
|
+
*
|
|
32
50
|
* @param hookScriptPath - Absolute path to the pre-tool-check script.
|
|
33
51
|
*/
|
|
34
|
-
function buildHooksConfig(hookScriptPath) {
|
|
52
|
+
export function buildHooksConfig(hookScriptPath) {
|
|
35
53
|
return {
|
|
36
54
|
_comment: 'Intutic governance hooks — auto-generated. DO NOT EDIT.',
|
|
37
55
|
_lastSync: newIso(),
|
|
@@ -45,20 +63,6 @@ function buildHooksConfig(hookScriptPath) {
|
|
|
45
63
|
])),
|
|
46
64
|
};
|
|
47
65
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Governance-sensitive paths — mirrors the list in claudeCodeHooks.ts.
|
|
50
|
-
* Any write/edit targeting these paths is blocked.
|
|
51
|
-
*/
|
|
52
|
-
const PROTECTED_PATHS = [
|
|
53
|
-
'.cursor/hooks.json',
|
|
54
|
-
'.claude/settings.json',
|
|
55
|
-
'.cline/hooks',
|
|
56
|
-
'.intutic/hooks',
|
|
57
|
-
'.intutic/integrity.json',
|
|
58
|
-
'.codeium/windsurf/hooks.json',
|
|
59
|
-
'.agents/plugins/intutic-governance',
|
|
60
|
-
'.openhands/hooks.json',
|
|
61
|
-
];
|
|
62
66
|
/**
|
|
63
67
|
* The pre-tool-check.js script content — receives JSON context from Cursor
|
|
64
68
|
* on stdin, exits 0 to allow or exits 2 to block.
|
|
@@ -72,6 +76,7 @@ function buildHookScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
|
72
76
|
* Proxy: ${proxyUrl}
|
|
73
77
|
* Generated: ${newIso()}
|
|
74
78
|
*/
|
|
79
|
+
${emitJsFailClosedPrelude({ harness: 'cursor', contract: 'exit2' })}
|
|
75
80
|
const path = require('path');
|
|
76
81
|
const os = require('os');
|
|
77
82
|
const fs = require('fs');
|
|
@@ -91,19 +96,23 @@ try {
|
|
|
91
96
|
});
|
|
92
97
|
} catch {}
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
${emitJsGate({ harness: 'cursor', contract: 'exit2' })}
|
|
95
100
|
|
|
101
|
+
let _intuticSessionId = '';
|
|
96
102
|
function logEvent(verdict, toolName, reason) {
|
|
97
103
|
try {
|
|
98
104
|
const ts = new Date().toISOString();
|
|
99
105
|
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
100
106
|
const entry = JSON.stringify({
|
|
101
|
-
|
|
107
|
+
// Passed through, not collapsed to two values: the advisory tier emits
|
|
108
|
+
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
109
|
+
event: verdict,
|
|
102
110
|
toolName, reason: reason || '',
|
|
103
111
|
workspaceId: _intuticWsId,
|
|
104
112
|
harnessType: 'cursor',
|
|
105
113
|
timestamp: ts,
|
|
106
114
|
incidentId,
|
|
115
|
+
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
107
116
|
}) + '\\n';
|
|
108
117
|
fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
|
|
109
118
|
if (_intuticKey) {
|
|
@@ -124,46 +133,60 @@ process.stdin.on('data', (c) => { raw += c; });
|
|
|
124
133
|
process.stdin.on('end', () => {
|
|
125
134
|
try {
|
|
126
135
|
const ctx = JSON.parse(raw);
|
|
127
|
-
|
|
136
|
+
_intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
|
|
137
|
+
// Cursor accepts FLAT payloads (fields at the top level, no tool_input
|
|
138
|
+
// wrapper — that is what the \`|| ctx\` fallback below reads), so the
|
|
139
|
+
// recognisable-envelope set is exactly the extractor's field set. Narrower
|
|
140
|
+
// would refuse real traffic; an envelope with none of these extracts to
|
|
141
|
+
// empty strings, matches no rule, and used to be allowed.
|
|
142
|
+
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput', 'input', 'event', 'hook_event_name',
|
|
143
|
+
'command', 'cmd', 'script', 'path', 'file_path', 'filePath', 'file', 'target', 'notebook_path'], logEvent);
|
|
144
|
+
// Cursor's real field is \`hook_event_name\` (confirmed against Cursor's
|
|
145
|
+
// hooks documentation and a live \`beforeMCPExecution\` payload example
|
|
146
|
+
// from Cursor's own bug tracker, 2026-08 — \`ctx.event\` is not a field
|
|
147
|
+
// Cursor actually sends). Both are read: the correct name, and the one
|
|
148
|
+
// this file read alone before, so a future payload shape carrying \`event\`
|
|
149
|
+
// instead is not silently dropped.
|
|
150
|
+
const event = (ctx.hook_event_name || ctx.event || '').toLowerCase();
|
|
128
151
|
const input = ctx.input || ctx.tool_input || ctx;
|
|
129
152
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
logEvent('blocked', 'edit', reason);
|
|
138
|
-
process.exit(2);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
153
|
+
// Every spelling any harness uses, matching the bash extractor. A field name
|
|
154
|
+
// this list misses is a path guard that silently does not run — and the
|
|
155
|
+
// caller controls which spelling it sends.
|
|
156
|
+
const targetPath = input.path || input.file_path || input.filePath || input.file ||
|
|
157
|
+
input.target || input.notebook_path || '';
|
|
158
|
+
const command = input.command || input.cmd || input.script || '';
|
|
159
|
+
let toolName = ctx.tool_name || ctx.toolName || event || 'tool';
|
|
142
160
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
161
|
+
// M3: a \`beforeMCPExecution\` payload's \`tool_name\` is the BARE MCP tool
|
|
162
|
+
// name (e.g. "create_issue"), never "beforeMCPExecution" itself — but it
|
|
163
|
+
// was never composed into the \`mcp__<server>__<tool>\` shape every other
|
|
164
|
+
// harness's MCP tool name already takes, so an allowlist rule or a
|
|
165
|
+
// workspace's own \`mcp__github__.*\`-shaped SOP rule silently never fired
|
|
166
|
+
// on a Cursor MCP call. The server is a TOP-LEVEL sibling field —
|
|
167
|
+
// \`command\` for a stdio server, \`url\` for a remote one (per Cursor's
|
|
168
|
+
// hooks documentation; verified live payload: \`{tool_name, command,
|
|
169
|
+
// hook_event_name: "beforeMCPExecution", ...}\`) — composed in here, once,
|
|
170
|
+
// before anything reads \`toolName\`.
|
|
171
|
+
if (event === 'beforemcpexecution' && ctx.tool_name) {
|
|
172
|
+
const mcpServer = ctx.command || ctx.url || ctx.server_name || ctx.serverName;
|
|
173
|
+
if (mcpServer) toolName = 'mcp__' + mcpServer + '__' + ctx.tool_name;
|
|
157
174
|
}
|
|
158
175
|
|
|
176
|
+
// The protected-path check used to be gated on \`event === 'beforefileedit'
|
|
177
|
+
// || input.path\`, so a shell command naming a protected path was only
|
|
178
|
+
// caught if it also carried a path argument. The shared gate tests the
|
|
179
|
+
// command and the target independently.
|
|
180
|
+
intuticGate(toolName, targetPath, command, logEvent, _intuticWsId, input);
|
|
181
|
+
|
|
159
182
|
// Allow
|
|
160
|
-
logEvent('
|
|
183
|
+
logEvent('tool_allowed', toolName, '');
|
|
161
184
|
process.exit(0);
|
|
162
185
|
} catch (err) {
|
|
163
186
|
// Fail CLOSED
|
|
164
187
|
const errMsg = String(err);
|
|
165
188
|
process.stderr.write('[Intutic Governance] Hook error (fail-closed): ' + errMsg + '\\n');
|
|
166
|
-
logEvent('
|
|
189
|
+
logEvent('tool_blocked', 'unknown', errMsg);
|
|
167
190
|
process.exit(2);
|
|
168
191
|
}
|
|
169
192
|
});
|
|
@@ -183,7 +206,9 @@ export async function writeCursorHooks(workspaceRoot, proxyUrl, workspaceId = ''
|
|
|
183
206
|
await fs.mkdir(hookScriptDir, { recursive: true });
|
|
184
207
|
// Ensure hook events log directory exists
|
|
185
208
|
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
186
|
-
|
|
209
|
+
// Named after its writer — see the note in claudeCodeHooks.ts. Both emitted
|
|
210
|
+
// `pre-tool-check.js` here and silently overwrote each other.
|
|
211
|
+
const hookScriptPath = path.join(hookScriptDir, 'cursor-check.js');
|
|
187
212
|
// Write the hook script
|
|
188
213
|
const tmpScript = hookScriptPath + '.cursor-tmp';
|
|
189
214
|
await fs.writeFile(tmpScript, buildHookScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
|
|
@@ -200,9 +225,13 @@ export async function writeCursorHooks(workspaceRoot, proxyUrl, workspaceId = ''
|
|
|
200
225
|
await fs.mkdir(userCursorDir, { recursive: true });
|
|
201
226
|
await atomicWriteJson(path.join(userCursorDir, 'hooks.json'), config);
|
|
202
227
|
log.info({ action: 'cursor_hooks_written', level: 'user', path: userCursorDir }, 'Cursor user-level hooks written');
|
|
203
|
-
// 3. System-level
|
|
228
|
+
// 3. System-level (system administrator installation only). macOS has no
|
|
229
|
+
// /etc/cursor convention Cursor itself reads — its own app-support
|
|
230
|
+
// directory is the closest machine-wide location. This branch was missing
|
|
231
|
+
// until now: the success log below always claimed "system-level hooks
|
|
232
|
+
// written" for a write that, on macOS, went to a path Cursor never reads.
|
|
204
233
|
if (writeSystemLevel) {
|
|
205
|
-
const systemCursorDir =
|
|
234
|
+
const systemCursorDir = systemHooksDirFor(process.platform);
|
|
206
235
|
try {
|
|
207
236
|
await fs.mkdir(systemCursorDir, { recursive: true });
|
|
208
237
|
await atomicWriteJson(path.join(systemCursorDir, 'hooks.json'), config);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursorHooks.js","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"cursorHooks.js","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"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"}
|