@intutic/sync-daemon 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentReporter.d.ts +4 -12
- package/dist/agentReporter.d.ts.map +1 -1
- package/dist/agentReporter.js +25 -4
- package/dist/agentReporter.js.map +1 -1
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +20 -6
- package/dist/configWriter.js.map +1 -1
- package/dist/harness/aiderConfigMerger.d.ts +41 -5
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
- package/dist/harness/aiderConfigMerger.js +110 -86
- package/dist/harness/aiderConfigMerger.js.map +1 -1
- package/dist/harness/antigravityHooks.d.ts +13 -8
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +51 -39
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +42 -28
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts +39 -9
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +139 -60
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexConfigMerger.d.ts +38 -0
- package/dist/harness/codexConfigMerger.d.ts.map +1 -0
- package/dist/harness/codexConfigMerger.js +101 -0
- package/dist/harness/codexConfigMerger.js.map +1 -0
- package/dist/harness/codexHooks.d.ts +4 -2
- package/dist/harness/codexHooks.d.ts.map +1 -1
- package/dist/harness/codexHooks.js +11 -11
- package/dist/harness/codexHooks.js.map +1 -1
- package/dist/harness/continueConfigMerger.d.ts +46 -0
- package/dist/harness/continueConfigMerger.d.ts.map +1 -0
- package/dist/harness/continueConfigMerger.js +90 -0
- package/dist/harness/continueConfigMerger.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +15 -25
- package/dist/harness/continueHooks.d.ts.map +1 -1
- package/dist/harness/continueHooks.js +22 -119
- package/dist/harness/continueHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +33 -16
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +66 -25
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +59 -52
- package/dist/harness/dshHooks.d.ts.map +1 -1
- package/dist/harness/dshHooks.js +145 -147
- package/dist/harness/dshHooks.js.map +1 -1
- package/dist/harness/gateKind.d.ts.map +1 -1
- package/dist/harness/gateKind.js +1 -0
- package/dist/harness/gateKind.js.map +1 -1
- package/dist/harness/githubCopilotHooks.js +1 -1
- package/dist/harness/grokHooks.d.ts.map +1 -1
- package/dist/harness/grokHooks.js +7 -2
- package/dist/harness/grokHooks.js.map +1 -1
- package/dist/harness/hermesHooks.d.ts +9 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +32 -31
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/jsonMergeTarget.d.ts +23 -0
- package/dist/harness/jsonMergeTarget.d.ts.map +1 -0
- package/dist/harness/jsonMergeTarget.js +50 -0
- package/dist/harness/jsonMergeTarget.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +28 -6
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +258 -32
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +6 -7
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +37 -6
- package/dist/harness/openCodeHooks.d.ts.map +1 -1
- package/dist/harness/openCodeHooks.js +101 -8
- package/dist/harness/openCodeHooks.js.map +1 -1
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +5 -4
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts +13 -0
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +70 -0
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts +17 -0
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +28 -29
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +64 -6
- package/dist/harness/protectedPaths.d.ts.map +1 -1
- package/dist/harness/protectedPaths.js +161 -11
- package/dist/harness/protectedPaths.js.map +1 -1
- package/dist/harness/windsurfHooks.d.ts +3 -1
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +64 -42
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +9 -6
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.js +17 -6
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/policySnapshot.d.ts +19 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -1
- package/dist/lib/policySnapshot.js +29 -2
- package/dist/lib/policySnapshot.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +14 -15
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/package.json +6 -6
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* codexHooks.ts — Codex CLI PreToolUse hook injection.
|
|
3
3
|
*
|
|
4
4
|
* Codex CLI executes PreToolUse hooks registered in `hooks.json`:
|
|
5
|
-
* - User-level: ~/.codex/hooks.json
|
|
6
|
-
* - Project-level: <workspaceRoot>/.codex/hooks.json
|
|
5
|
+
* - User-level: $CODEX_HOME/hooks.json (~/.codex/hooks.json by default)
|
|
6
|
+
* - Project-level: <workspaceRoot>/.codex/hooks.json — Codex loads this one
|
|
7
|
+
* only once the user has trusted the project's `.codex/` layer, so the
|
|
8
|
+
* user-level registration is the one that always applies.
|
|
7
9
|
*
|
|
8
10
|
* Each PreToolUse entry carries a `matcher` regex over tool names; the hook
|
|
9
11
|
* command receives JSON on stdin — {tool_name, tool_use_id, tool_input}, with
|
|
@@ -28,6 +30,7 @@ import * as os from 'node:os';
|
|
|
28
30
|
import { createLogger } from '@intutic/logger';
|
|
29
31
|
import { newIso } from '@intutic/id';
|
|
30
32
|
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
33
|
+
import { readJsonObjectForMerge } from './jsonMergeTarget.js';
|
|
31
34
|
const log = createLogger('sync-codex-hooks');
|
|
32
35
|
/**
|
|
33
36
|
* The pre-tool gate script — receives Codex's PreToolUse JSON on stdin,
|
|
@@ -139,13 +142,9 @@ process.stdin.on('end', () => {
|
|
|
139
142
|
* uses — so repeated syncs are idempotent and never stack duplicate entries.
|
|
140
143
|
*/
|
|
141
144
|
async function mergeCodexHooksJson(configPath, hookScriptPath) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
// File doesn't exist or is malformed — start fresh
|
|
148
|
-
}
|
|
145
|
+
const existing = await readJsonObjectForMerge(configPath);
|
|
146
|
+
if (existing === null)
|
|
147
|
+
return;
|
|
149
148
|
const existingHooks = existing.hooks ?? {};
|
|
150
149
|
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
|
|
151
150
|
? existingHooks.PreToolUse
|
|
@@ -218,8 +217,9 @@ export async function writeCodexHooks(workspaceRoot, proxyUrl, workspaceId = '')
|
|
|
218
217
|
await fs.mkdir(projectCodexDir, { recursive: true });
|
|
219
218
|
await mergeCodexHooksJson(path.join(projectCodexDir, 'hooks.json'), hookScriptPath);
|
|
220
219
|
log.info({ action: 'codex_hooks_written', level: 'project', path: projectCodexDir }, 'Codex project-level hooks written');
|
|
221
|
-
// 2. User-level:
|
|
222
|
-
|
|
220
|
+
// 2. User-level: $CODEX_HOME/hooks.json — Codex keeps its user config in
|
|
221
|
+
// CODEX_HOME when set, ~/.codex otherwise.
|
|
222
|
+
const userCodexDir = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
223
223
|
await fs.mkdir(userCodexDir, { recursive: true });
|
|
224
224
|
await mergeCodexHooksJson(path.join(userCodexDir, 'hooks.json'), hookScriptPath);
|
|
225
225
|
log.info({ action: 'codex_hooks_written', level: 'user', path: userCodexDir }, 'Codex user-level hooks written');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codexHooks.js","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"codexHooks.js","sourceRoot":"","sources":["../../src/harness/codexHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* continueConfigMerger.ts — point Continue's OpenAI and Anthropic models at
|
|
3
|
+
* the proxy.
|
|
4
|
+
*
|
|
5
|
+
* Continue reads its models from `~/.continue/config.yaml`. This sets
|
|
6
|
+
* `apiBase` on each model whose `provider` is `openai` or `anthropic` — the two
|
|
7
|
+
* wire protocols the proxy serves — and leaves every other model, and every
|
|
8
|
+
* other key, alone. Pointing an Ollama or Gemini model at the proxy would break
|
|
9
|
+
* it, so those are not touched.
|
|
10
|
+
*
|
|
11
|
+
* Continue joins its request path onto `apiBase` with URL resolution
|
|
12
|
+
* (`chat/completions` for OpenAI, `messages` for Anthropic), so the value is
|
|
13
|
+
* the proxy's `/v1/` with the trailing slash: without it the last path segment
|
|
14
|
+
* would be replaced instead of extended.
|
|
15
|
+
*
|
|
16
|
+
* The file is edited through the `yaml` library's document model, so comments,
|
|
17
|
+
* ordering and formatting survive. A file that does not parse, or has no
|
|
18
|
+
* `models` list, is left alone: inventing a model entry would give Continue a
|
|
19
|
+
* model with no provider or name, which it rejects.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
/** Result of merging the proxy URL into Continue's config.yaml text. */
|
|
24
|
+
export interface ContinueMergeResult {
|
|
25
|
+
/** New file content, or `null` when the file must be left untouched. */
|
|
26
|
+
content: string | null;
|
|
27
|
+
/** Number of models now pointing at the proxy. */
|
|
28
|
+
routed: number;
|
|
29
|
+
/** Why the file was left untouched, when `content` is `null`. */
|
|
30
|
+
skipped?: 'unparseable' | 'no-models';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set `apiBase` on every OpenAI/Anthropic model in a Continue config.yaml.
|
|
34
|
+
*
|
|
35
|
+
* @param raw - Current file content.
|
|
36
|
+
* @param apiBase - The proxy's OpenAI-style base URL (host + `/v1`).
|
|
37
|
+
*/
|
|
38
|
+
export declare function setContinueApiBase(raw: string, apiBase: string): ContinueMergeResult;
|
|
39
|
+
/**
|
|
40
|
+
* Merge the proxy URL into Continue's config.yaml at `configPath`.
|
|
41
|
+
*
|
|
42
|
+
* @returns the number of models routed through the proxy; 0 when the file was
|
|
43
|
+
* missing, unparseable or had no OpenAI/Anthropic model (logged).
|
|
44
|
+
*/
|
|
45
|
+
export declare function mergeContinueConfig(configPath: string, apiBase: string): Promise<number>;
|
|
46
|
+
//# sourceMappingURL=continueConfigMerger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continueConfigMerger.d.ts","sourceRoot":"","sources":["../../src/harness/continueConfigMerger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,aAAa,GAAG,WAAW,CAAA;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAkBpF;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2B9F"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* continueConfigMerger.ts — point Continue's OpenAI and Anthropic models at
|
|
3
|
+
* the proxy.
|
|
4
|
+
*
|
|
5
|
+
* Continue reads its models from `~/.continue/config.yaml`. This sets
|
|
6
|
+
* `apiBase` on each model whose `provider` is `openai` or `anthropic` — the two
|
|
7
|
+
* wire protocols the proxy serves — and leaves every other model, and every
|
|
8
|
+
* other key, alone. Pointing an Ollama or Gemini model at the proxy would break
|
|
9
|
+
* it, so those are not touched.
|
|
10
|
+
*
|
|
11
|
+
* Continue joins its request path onto `apiBase` with URL resolution
|
|
12
|
+
* (`chat/completions` for OpenAI, `messages` for Anthropic), so the value is
|
|
13
|
+
* the proxy's `/v1/` with the trailing slash: without it the last path segment
|
|
14
|
+
* would be replaced instead of extended.
|
|
15
|
+
*
|
|
16
|
+
* The file is edited through the `yaml` library's document model, so comments,
|
|
17
|
+
* ordering and formatting survive. A file that does not parse, or has no
|
|
18
|
+
* `models` list, is left alone: inventing a model entry would give Continue a
|
|
19
|
+
* model with no provider or name, which it rejects.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import * as fs from 'node:fs/promises';
|
|
24
|
+
import * as path from 'node:path';
|
|
25
|
+
import { parseDocument, isMap, isSeq } from 'yaml';
|
|
26
|
+
import { createLogger } from '@intutic/logger';
|
|
27
|
+
const log = createLogger('sync-continue-config');
|
|
28
|
+
/** Providers whose wire protocol the proxy serves. */
|
|
29
|
+
const PROXIED_PROVIDERS = new Set(['openai', 'anthropic']);
|
|
30
|
+
/**
|
|
31
|
+
* Set `apiBase` on every OpenAI/Anthropic model in a Continue config.yaml.
|
|
32
|
+
*
|
|
33
|
+
* @param raw - Current file content.
|
|
34
|
+
* @param apiBase - The proxy's OpenAI-style base URL (host + `/v1`).
|
|
35
|
+
*/
|
|
36
|
+
export function setContinueApiBase(raw, apiBase) {
|
|
37
|
+
const value = apiBase.endsWith('/') ? apiBase : `${apiBase}/`;
|
|
38
|
+
const doc = parseDocument(raw);
|
|
39
|
+
if (doc.errors.length > 0)
|
|
40
|
+
return { content: null, routed: 0, skipped: 'unparseable' };
|
|
41
|
+
const models = isMap(doc.contents) ? doc.contents.get('models', true) : undefined;
|
|
42
|
+
if (!isSeq(models))
|
|
43
|
+
return { content: null, routed: 0, skipped: 'no-models' };
|
|
44
|
+
let routed = 0;
|
|
45
|
+
for (const model of models.items) {
|
|
46
|
+
if (!isMap(model))
|
|
47
|
+
continue;
|
|
48
|
+
const provider = model.get('provider');
|
|
49
|
+
if (typeof provider !== 'string' || !PROXIED_PROVIDERS.has(provider))
|
|
50
|
+
continue;
|
|
51
|
+
model.set('apiBase', value);
|
|
52
|
+
routed++;
|
|
53
|
+
}
|
|
54
|
+
if (routed === 0)
|
|
55
|
+
return { content: null, routed: 0, skipped: 'no-models' };
|
|
56
|
+
return { content: doc.toString(), routed };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Merge the proxy URL into Continue's config.yaml at `configPath`.
|
|
60
|
+
*
|
|
61
|
+
* @returns the number of models routed through the proxy; 0 when the file was
|
|
62
|
+
* missing, unparseable or had no OpenAI/Anthropic model (logged).
|
|
63
|
+
*/
|
|
64
|
+
export async function mergeContinueConfig(configPath, apiBase) {
|
|
65
|
+
let raw;
|
|
66
|
+
try {
|
|
67
|
+
raw = await fs.readFile(configPath, 'utf-8');
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err.code === 'ENOENT')
|
|
71
|
+
return 0;
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
const result = setContinueApiBase(raw, apiBase);
|
|
75
|
+
if (result.content === null) {
|
|
76
|
+
log.warn({ action: 'continue_config_merge_skipped', path: configPath, reason: result.skipped }, result.skipped === 'unparseable'
|
|
77
|
+
? `${configPath} is not valid YAML — left untouched`
|
|
78
|
+
: `${configPath} has no openai or anthropic model to route — left untouched`);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
if (result.content !== raw) {
|
|
82
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
83
|
+
const tmp = configPath + '.intutic-tmp';
|
|
84
|
+
await fs.writeFile(tmp, result.content, 'utf-8');
|
|
85
|
+
await fs.rename(tmp, configPath);
|
|
86
|
+
}
|
|
87
|
+
log.info({ action: 'continue_config_merged', path: configPath, routed: result.routed }, 'Continue models routed through the Intutic proxy');
|
|
88
|
+
return result.routed;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=continueConfigMerger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continueConfigMerger.js","sourceRoot":"","sources":["../../src/harness/continueConfigMerger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,MAAM,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,GAAG,GAAG,YAAY,CAAC,sBAAsB,CAAC,CAAA;AAEhD,sDAAsD;AACtD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAA;AAY1D;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,OAAe;IAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAA;IAC7D,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;IAC9B,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAA;IAEtF,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACjF,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;IAE7E,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,SAAQ;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACtC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAQ;QAC9E,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QAC3B,MAAM,EAAE,CAAA;IACV,CAAC;IACD,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;IAC3E,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAA;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,UAAkB,EAAE,OAAe;IAC3E,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAA;QAC9D,MAAM,GAAG,CAAA;IACX,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAC/C,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC5B,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,+BAA+B,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,EACrF,MAAM,CAAC,OAAO,KAAK,aAAa;YAC9B,CAAC,CAAC,GAAG,UAAU,qCAAqC;YACpD,CAAC,CAAC,GAAG,UAAU,6DAA6D,CAC/E,CAAA;QACD,OAAO,CAAC,CAAA;IACV,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,KAAK,GAAG,EAAE,CAAC;QAC3B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC7D,MAAM,GAAG,GAAG,UAAU,GAAG,cAAc,CAAA;QACvC,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAChD,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAA;IAClC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,kDAAkD,CAAC,CAAA;IAC3I,OAAO,MAAM,CAAC,MAAM,CAAA;AACtB,CAAC"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* continueHooks.ts — Continue
|
|
2
|
+
* continueHooks.ts — Continue CLI PreToolUse gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Proxy routing for Continue (the `apiBase` of each OpenAI/Anthropic model in
|
|
5
|
+
* `~/.continue/config.yaml`) is written by the CLI's Continue adapter
|
|
6
|
+
* (tools/cli/src/harness/continue.ts). This module writes only the gate.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
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
|
|
8
|
+
* **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
11
9
|
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
12
10
|
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
13
11
|
* (project), with a Claude-Code-compatible stdin contract —
|
|
@@ -40,9 +38,8 @@
|
|
|
40
38
|
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
41
39
|
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
42
40
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* standard JSON with a managed-file header comment.
|
|
41
|
+
* The settings files are merged, never replaced: a file that is not a plain
|
|
42
|
+
* JSON object is left alone and reported (see jsonMergeTarget.ts).
|
|
46
43
|
*
|
|
47
44
|
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
48
45
|
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
@@ -50,24 +47,17 @@
|
|
|
50
47
|
* @module
|
|
51
48
|
*/
|
|
52
49
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* 2. Sets `apiBase = proxyUrl` on each model that doesn't already use it.
|
|
58
|
-
* 3. Adds a `customInstructions` governance notice.
|
|
59
|
-
* 4. Writes the merged result back atomically.
|
|
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).
|
|
50
|
+
* Write the Continue CLI gate script `.intutic/hooks/continue-check.js` and
|
|
51
|
+
* register it in `~/.continue/settings.json` and
|
|
52
|
+
* `<root>/.continue/settings.json` (see the module header for the
|
|
53
|
+
* `.claude/settings.json` overlap).
|
|
65
54
|
*
|
|
66
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
55
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
56
|
+
* and command-keyed de-duplication in both settings files.
|
|
67
57
|
*
|
|
68
58
|
* @param workspaceRoot - Absolute workspace root path.
|
|
69
|
-
* @param proxyUrl - Intutic proxy URL
|
|
70
|
-
* @param workspaceId - Workspace ID
|
|
59
|
+
* @param proxyUrl - Intutic proxy URL written into the gate header.
|
|
60
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
71
61
|
*/
|
|
72
62
|
export declare function writeContinueHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
73
63
|
//# sourceMappingURL=continueHooks.d.ts.map
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAiLH;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* continueHooks.ts — Continue
|
|
2
|
+
* continueHooks.ts — Continue CLI PreToolUse gate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Proxy routing for Continue (the `apiBase` of each OpenAI/Anthropic model in
|
|
5
|
+
* `~/.continue/config.yaml`) is written by the CLI's Continue adapter
|
|
6
|
+
* (tools/cli/src/harness/continue.ts). This module writes only the gate.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
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
|
|
8
|
+
* **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
|
|
11
9
|
* no hook system): the CLI executes PreToolUse hooks registered in
|
|
12
10
|
* `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
|
|
13
11
|
* (project), with a Claude-Code-compatible stdin contract —
|
|
@@ -40,9 +38,8 @@
|
|
|
40
38
|
* get no gate at all. Double registration is harmless: both gates evaluate
|
|
41
39
|
* the same rules, and an allow/allow or block/block pair is idempotent.
|
|
42
40
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* standard JSON with a managed-file header comment.
|
|
41
|
+
* The settings files are merged, never replaced: a file that is not a plain
|
|
42
|
+
* JSON object is left alone and reported (see jsonMergeTarget.ts).
|
|
46
43
|
*
|
|
47
44
|
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
48
45
|
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
@@ -55,26 +52,8 @@ import * as os from 'node:os';
|
|
|
55
52
|
import { createLogger } from '@intutic/logger';
|
|
56
53
|
import { newIso } from '@intutic/id';
|
|
57
54
|
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
55
|
+
import { readJsonObjectForMerge } from './jsonMergeTarget.js';
|
|
58
56
|
const log = createLogger('sync-continue-hooks');
|
|
59
|
-
/** Default path to the Continue.dev global config file. */
|
|
60
|
-
const CONTINUE_CONFIG_PATH = path.join(os.homedir(), '.continue', 'config.json');
|
|
61
|
-
/** Governance notice injected into customInstructions. */
|
|
62
|
-
const GOVERNANCE_NOTICE = [
|
|
63
|
-
'This workspace is governed by Intutic SOP policies.',
|
|
64
|
-
'All LLM API requests are routed through the Intutic governance proxy.',
|
|
65
|
-
'Do not remove or override the apiBase field in this configuration.',
|
|
66
|
-
].join(' ');
|
|
67
|
-
// ─── JSONC comment stripper ───────────────────────────────────────────────────
|
|
68
|
-
/**
|
|
69
|
-
* Strip single-line `// ...` comments from a JSONC string so it can be
|
|
70
|
-
* parsed by JSON.parse. Block comments (`/* ... *\/`) are not stripped
|
|
71
|
-
* because they are uncommon in Continue config files.
|
|
72
|
-
*/
|
|
73
|
-
function stripJsoncComments(raw) {
|
|
74
|
-
// Remove // comments that are not inside strings.
|
|
75
|
-
// Simple approach: replace // up to end-of-line (acceptable for Continue config).
|
|
76
|
-
return raw.replace(/\/\/[^\n]*/g, '');
|
|
77
|
-
}
|
|
78
57
|
// ─── PreToolUse gate (Continue CLI `cn`) ──────────────────────────────────────
|
|
79
58
|
/**
|
|
80
59
|
* The pre-tool gate script — receives the Continue CLI's Claude-Code-compatible
|
|
@@ -191,13 +170,9 @@ process.stdin.on('end', () => {
|
|
|
191
170
|
* repeated syncs never stack duplicate entries.
|
|
192
171
|
*/
|
|
193
172
|
async function mergeContinueSettings(settingsPath, hookScriptPath) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
// File doesn't exist or is malformed — start fresh
|
|
200
|
-
}
|
|
173
|
+
const existing = await readJsonObjectForMerge(settingsPath);
|
|
174
|
+
if (existing === null)
|
|
175
|
+
return;
|
|
201
176
|
const existingHooks = existing.hooks ?? {};
|
|
202
177
|
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
|
|
203
178
|
? existingHooks.PreToolUse
|
|
@@ -234,91 +209,19 @@ async function mergeContinueSettings(settingsPath, hookScriptPath) {
|
|
|
234
209
|
}
|
|
235
210
|
// ─── Main export ──────────────────────────────────────────────────────────────
|
|
236
211
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
212
|
+
* Write the Continue CLI gate script `.intutic/hooks/continue-check.js` and
|
|
213
|
+
* register it in `~/.continue/settings.json` and
|
|
214
|
+
* `<root>/.continue/settings.json` (see the module header for the
|
|
215
|
+
* `.claude/settings.json` overlap).
|
|
239
216
|
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* 3. Adds a `customInstructions` governance notice.
|
|
243
|
-
* 4. Writes the merged result back atomically.
|
|
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).
|
|
249
|
-
*
|
|
250
|
-
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
217
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
|
|
218
|
+
* and command-keyed de-duplication in both settings files.
|
|
251
219
|
*
|
|
252
220
|
* @param workspaceRoot - Absolute workspace root path.
|
|
253
|
-
* @param proxyUrl - Intutic proxy URL
|
|
254
|
-
* @param workspaceId - Workspace ID
|
|
221
|
+
* @param proxyUrl - Intutic proxy URL written into the gate header.
|
|
222
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
255
223
|
*/
|
|
256
224
|
export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
257
|
-
// ── 1. Ensure directories ──────────────────────────────────────────────────
|
|
258
|
-
const continueDir = path.join(os.homedir(), '.continue');
|
|
259
|
-
const envDir = path.join(workspaceRoot, '.intutic', 'env');
|
|
260
|
-
await Promise.all([
|
|
261
|
-
fs.mkdir(continueDir, { recursive: true }),
|
|
262
|
-
fs.mkdir(envDir, { recursive: true }),
|
|
263
|
-
]);
|
|
264
|
-
// ── 2. Read and parse ~/.continue/config.json ─────────────────────────────
|
|
265
|
-
let configObj = {};
|
|
266
|
-
try {
|
|
267
|
-
const raw = await fs.readFile(CONTINUE_CONFIG_PATH, 'utf-8');
|
|
268
|
-
const stripped = stripJsoncComments(raw);
|
|
269
|
-
configObj = JSON.parse(stripped);
|
|
270
|
-
}
|
|
271
|
-
catch {
|
|
272
|
-
// File doesn't exist or is malformed — start with empty config
|
|
273
|
-
log.warn({ action: 'continue_config_read_failed', path: CONTINUE_CONFIG_PATH }, 'Could not read ~/.continue/config.json — creating fresh');
|
|
274
|
-
}
|
|
275
|
-
// ── 3. Update model apiBase fields ────────────────────────────────────────
|
|
276
|
-
const models = Array.isArray(configObj.models) ? configObj.models : [];
|
|
277
|
-
let modifiedCount = 0;
|
|
278
|
-
const updatedModels = models.map((model) => {
|
|
279
|
-
if (typeof model !== 'object' || model === null)
|
|
280
|
-
return model;
|
|
281
|
-
const m = model;
|
|
282
|
-
if (m.apiBase === proxyUrl)
|
|
283
|
-
return m; // Already set — no-op
|
|
284
|
-
modifiedCount++;
|
|
285
|
-
return { ...m, apiBase: proxyUrl };
|
|
286
|
-
});
|
|
287
|
-
// ── 4. Build merged config ────────────────────────────────────────────────
|
|
288
|
-
const mergedConfig = {
|
|
289
|
-
...configObj,
|
|
290
|
-
// Re-attach updated models array (or keep original if empty)
|
|
291
|
-
...(models.length > 0 ? { models: updatedModels } : {}),
|
|
292
|
-
// Add/overwrite customInstructions with governance notice
|
|
293
|
-
customInstructions: GOVERNANCE_NOTICE,
|
|
294
|
-
// Metadata fields
|
|
295
|
-
_intutic_managed: true,
|
|
296
|
-
_intutic_last_sync: newIso(),
|
|
297
|
-
_intutic_workspace_id: workspaceId,
|
|
298
|
-
};
|
|
299
|
-
// ── 5. Write back atomically ──────────────────────────────────────────────
|
|
300
|
-
// Prepend a managed-file warning as a comment (will be lost on next read —
|
|
301
|
-
// that's acceptable; we re-add it on each sync).
|
|
302
|
-
const managedHeader = `// AUTO-MANAGED by Intutic sync-daemon. Last sync: ${newIso()}\n// Do not edit apiBase fields — they are governance proxy routes.\n`;
|
|
303
|
-
const serialized = managedHeader + JSON.stringify(mergedConfig, null, 2) + '\n';
|
|
304
|
-
const tmpConfig = CONTINUE_CONFIG_PATH + '.intutic-tmp';
|
|
305
|
-
await fs.writeFile(tmpConfig, serialized, 'utf-8');
|
|
306
|
-
await fs.rename(tmpConfig, CONTINUE_CONFIG_PATH);
|
|
307
|
-
log.info({ action: 'continue_config_written', path: CONTINUE_CONFIG_PATH, modifiedModels: modifiedCount }, 'Continue.dev config patched with Intutic proxy apiBase');
|
|
308
|
-
// ── 6. Write .intutic/env/continue.env ────────────────────────────────────
|
|
309
|
-
const envContent = [
|
|
310
|
-
`# Intutic Continue.dev governance env — auto-generated ${newIso()}`,
|
|
311
|
-
`# This file is managed by the Intutic sync-daemon. DO NOT EDIT.`,
|
|
312
|
-
`CONTINUE_PROXY_URL=${proxyUrl}`,
|
|
313
|
-
workspaceId ? `INTUTIC_WORKSPACE_ID=${workspaceId}` : '',
|
|
314
|
-
`# All Continue.dev model requests are routed through CONTINUE_PROXY_URL.`,
|
|
315
|
-
].filter((l) => l !== '').join('\n') + '\n';
|
|
316
|
-
const envFilePath = path.join(envDir, 'continue.env');
|
|
317
|
-
const tmpEnv = envFilePath + '.intutic-tmp';
|
|
318
|
-
await fs.writeFile(tmpEnv, envContent, 'utf-8');
|
|
319
|
-
await fs.rename(tmpEnv, envFilePath);
|
|
320
|
-
log.info({ action: 'continue_env_written', path: envFilePath }, 'Continue.dev env snippet written');
|
|
321
|
-
// ── 7. PreToolUse gate for the Continue CLI (cn) ──────────────────────────
|
|
322
225
|
const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
|
|
323
226
|
await fs.mkdir(hookScriptDir, { recursive: true });
|
|
324
227
|
await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
|
|
@@ -333,9 +236,9 @@ export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
333
236
|
const projectContinueDir = path.join(workspaceRoot, '.continue');
|
|
334
237
|
await fs.mkdir(projectContinueDir, { recursive: true });
|
|
335
238
|
await mergeContinueSettings(path.join(projectContinueDir, 'settings.json'), hookScriptPath);
|
|
336
|
-
// User-level: ~/.continue/settings.json. homedir() read at call time
|
|
337
|
-
//
|
|
338
|
-
//
|
|
239
|
+
// User-level: ~/.continue/settings.json. homedir() read at call time so
|
|
240
|
+
// tests that move HOME before invoking are honoured — see the gooseHooks
|
|
241
|
+
// note in generatedShellIntegrity.test.ts.
|
|
339
242
|
const userContinueDir = path.join(os.homedir(), '.continue');
|
|
340
243
|
await fs.mkdir(userContinueDir, { recursive: true });
|
|
341
244
|
await mergeContinueSettings(path.join(userContinueDir, 'settings.json'), hookScriptPath);
|
|
@@ -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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"}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cursorHooks.ts — Cursor
|
|
2
|
+
* cursorHooks.ts — Cursor hook injection.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Registers the gate in Cursor's hooks.json (schema per cursor.com/docs/agent/
|
|
5
|
+
* hooks: `{version: 1, hooks: {<event>: [{command, matcher?, failClosed?}]}}`)
|
|
6
|
+
* for three events:
|
|
7
|
+
* - `beforeShellExecution` — every shell command;
|
|
8
|
+
* - `beforeMCPExecution` — every MCP tool call;
|
|
9
|
+
* - `preToolUse` matching `Write|Delete` — file writes and deletions, which
|
|
10
|
+
* have no dedicated before-hook (`afterFileEdit` runs too late to refuse).
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
12
|
+
* The gate refuses with exit code 2 and allows by printing
|
|
13
|
+
* `{"permission":"allow"}`: with `failClosed: true` Cursor blocks on a hook
|
|
14
|
+
* that crashes, times out or prints nothing, so an allow must be explicit.
|
|
15
|
+
*
|
|
16
|
+
* Levels: project (`<workspaceRoot>/.cursor/hooks.json`) and user
|
|
17
|
+
* (`~/.cursor/hooks.json`) on every sync; system level
|
|
18
|
+
* (`/Library/Application Support/Cursor` on macOS, `/etc/cursor` elsewhere)
|
|
19
|
+
* only through `intutic enterprise install`, which needs root. The project and
|
|
20
|
+
* user files are merged — hooks the user registered are kept.
|
|
11
21
|
*
|
|
12
22
|
* LLD #14 — Phase 3 cross-harness defence
|
|
13
23
|
* HLD §3.14 — Three-Tier Defense Cascade
|
|
@@ -35,16 +45,23 @@ export declare function systemHooksDirFor(platform: NodeJS.Platform): string;
|
|
|
35
45
|
* @param hookScriptPath - Absolute path to the pre-tool-check script.
|
|
36
46
|
*/
|
|
37
47
|
export declare function buildHooksConfig(hookScriptPath: string): {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
failClosed: boolean;
|
|
41
|
-
hooks: {
|
|
42
|
-
[k: string]: {
|
|
43
|
-
command: string;
|
|
44
|
-
failClosed: boolean;
|
|
45
|
-
};
|
|
46
|
-
};
|
|
48
|
+
version: number;
|
|
49
|
+
hooks: Record<string, CursorHookEntry[]>;
|
|
47
50
|
};
|
|
51
|
+
/** One entry in a Cursor hooks.json event list. */
|
|
52
|
+
export interface CursorHookEntry {
|
|
53
|
+
command: string;
|
|
54
|
+
matcher?: string;
|
|
55
|
+
failClosed?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Merge the Intutic entries into an existing hooks.json object: the user's
|
|
59
|
+
* entries for every event are kept, entries running this gate are replaced,
|
|
60
|
+
* and the shape earlier versions wrote (an object per event, top-level
|
|
61
|
+
* `failClosed`/`_comment`/`_lastSync`, the nonexistent `beforeFileEdit`
|
|
62
|
+
* event) is dropped.
|
|
63
|
+
*/
|
|
64
|
+
export declare function mergeHooksConfig(existing: Record<string, unknown>, hookScriptPath: string): Record<string, unknown>;
|
|
48
65
|
/**
|
|
49
66
|
* Write Cursor hooks at user-level and project-level.
|
|
50
67
|
* 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
|
|
1
|
+
{"version":3,"file":"cursorHooks.d.ts","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAmBH;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAEnE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAA;CAAE,CAUtH;AAED,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBnH;AAsID;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,EAChB,gBAAgB,UAAQ,GACvB,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
|