@intutic/sync-daemon 1.10.1 → 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.
Files changed (107) hide show
  1. package/dist/agentReporter.d.ts +4 -12
  2. package/dist/agentReporter.d.ts.map +1 -1
  3. package/dist/agentReporter.js +25 -4
  4. package/dist/agentReporter.js.map +1 -1
  5. package/dist/configWriter.d.ts.map +1 -1
  6. package/dist/configWriter.js +20 -6
  7. package/dist/configWriter.js.map +1 -1
  8. package/dist/harness/aiderConfigMerger.d.ts +41 -5
  9. package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
  10. package/dist/harness/aiderConfigMerger.js +110 -86
  11. package/dist/harness/aiderConfigMerger.js.map +1 -1
  12. package/dist/harness/antigravityHooks.d.ts +13 -8
  13. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  14. package/dist/harness/antigravityHooks.js +51 -39
  15. package/dist/harness/antigravityHooks.js.map +1 -1
  16. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  17. package/dist/harness/claudeCodeHooks.js +42 -28
  18. package/dist/harness/claudeCodeHooks.js.map +1 -1
  19. package/dist/harness/clineHooks.d.ts +39 -9
  20. package/dist/harness/clineHooks.d.ts.map +1 -1
  21. package/dist/harness/clineHooks.js +139 -60
  22. package/dist/harness/clineHooks.js.map +1 -1
  23. package/dist/harness/codexConfigMerger.d.ts +38 -0
  24. package/dist/harness/codexConfigMerger.d.ts.map +1 -0
  25. package/dist/harness/codexConfigMerger.js +101 -0
  26. package/dist/harness/codexConfigMerger.js.map +1 -0
  27. package/dist/harness/codexHooks.d.ts +4 -2
  28. package/dist/harness/codexHooks.d.ts.map +1 -1
  29. package/dist/harness/codexHooks.js +11 -11
  30. package/dist/harness/codexHooks.js.map +1 -1
  31. package/dist/harness/continueConfigMerger.d.ts +46 -0
  32. package/dist/harness/continueConfigMerger.d.ts.map +1 -0
  33. package/dist/harness/continueConfigMerger.js +90 -0
  34. package/dist/harness/continueConfigMerger.js.map +1 -0
  35. package/dist/harness/continueHooks.d.ts +15 -25
  36. package/dist/harness/continueHooks.d.ts.map +1 -1
  37. package/dist/harness/continueHooks.js +22 -119
  38. package/dist/harness/continueHooks.js.map +1 -1
  39. package/dist/harness/cursorHooks.d.ts +33 -16
  40. package/dist/harness/cursorHooks.d.ts.map +1 -1
  41. package/dist/harness/cursorHooks.js +66 -25
  42. package/dist/harness/cursorHooks.js.map +1 -1
  43. package/dist/harness/dshHooks.d.ts +59 -52
  44. package/dist/harness/dshHooks.d.ts.map +1 -1
  45. package/dist/harness/dshHooks.js +145 -147
  46. package/dist/harness/dshHooks.js.map +1 -1
  47. package/dist/harness/gateKind.d.ts.map +1 -1
  48. package/dist/harness/gateKind.js +1 -0
  49. package/dist/harness/gateKind.js.map +1 -1
  50. package/dist/harness/githubCopilotHooks.js +1 -1
  51. package/dist/harness/grokHooks.d.ts.map +1 -1
  52. package/dist/harness/grokHooks.js +7 -2
  53. package/dist/harness/grokHooks.js.map +1 -1
  54. package/dist/harness/hermesHooks.d.ts +9 -0
  55. package/dist/harness/hermesHooks.d.ts.map +1 -1
  56. package/dist/harness/hermesHooks.js +32 -31
  57. package/dist/harness/hermesHooks.js.map +1 -1
  58. package/dist/harness/jsonMergeTarget.d.ts +23 -0
  59. package/dist/harness/jsonMergeTarget.d.ts.map +1 -0
  60. package/dist/harness/jsonMergeTarget.js +50 -0
  61. package/dist/harness/jsonMergeTarget.js.map +1 -0
  62. package/dist/harness/mcpAutoWrite.d.ts +28 -6
  63. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  64. package/dist/harness/mcpAutoWrite.js +258 -32
  65. package/dist/harness/mcpAutoWrite.js.map +1 -1
  66. package/dist/harness/n8nHooks.d.ts.map +1 -1
  67. package/dist/harness/n8nHooks.js +6 -7
  68. package/dist/harness/n8nHooks.js.map +1 -1
  69. package/dist/harness/openCodeHooks.d.ts +37 -6
  70. package/dist/harness/openCodeHooks.d.ts.map +1 -1
  71. package/dist/harness/openCodeHooks.js +101 -8
  72. package/dist/harness/openCodeHooks.js.map +1 -1
  73. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  74. package/dist/harness/openWebuiHooks.js +5 -4
  75. package/dist/harness/openWebuiHooks.js.map +1 -1
  76. package/dist/harness/openhandsHooks.d.ts +13 -0
  77. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  78. package/dist/harness/openhandsHooks.js +70 -0
  79. package/dist/harness/openhandsHooks.js.map +1 -1
  80. package/dist/harness/piHooks.d.ts +17 -0
  81. package/dist/harness/piHooks.d.ts.map +1 -1
  82. package/dist/harness/piHooks.js +28 -29
  83. package/dist/harness/piHooks.js.map +1 -1
  84. package/dist/harness/protectedPaths.d.ts +64 -6
  85. package/dist/harness/protectedPaths.d.ts.map +1 -1
  86. package/dist/harness/protectedPaths.js +161 -11
  87. package/dist/harness/protectedPaths.js.map +1 -1
  88. package/dist/harness/windsurfHooks.d.ts +3 -1
  89. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  90. package/dist/harness/windsurfHooks.js +64 -42
  91. package/dist/harness/windsurfHooks.js.map +1 -1
  92. package/dist/harness/windsurfJetBrainsProxy.d.ts +9 -6
  93. package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -1
  94. package/dist/harness/windsurfJetBrainsProxy.js +17 -6
  95. package/dist/harness/windsurfJetBrainsProxy.js.map +1 -1
  96. package/dist/index.d.ts +3 -0
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +3 -0
  99. package/dist/index.js.map +1 -1
  100. package/dist/lib/policySnapshot.d.ts +19 -0
  101. package/dist/lib/policySnapshot.d.ts.map +1 -1
  102. package/dist/lib/policySnapshot.js +29 -2
  103. package/dist/lib/policySnapshot.js.map +1 -1
  104. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  105. package/dist/watcher/settingsGuard.js +14 -15
  106. package/dist/watcher/settingsGuard.js.map +1 -1
  107. 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
- let existing = {};
143
- try {
144
- existing = JSON.parse(await fs.readFile(configPath, 'utf-8'));
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: ~/.codex/hooks.json
222
- const userCodexDir = path.join(os.homedir(), '.codex');
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":"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"}
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":"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"}
@@ -1,13 +1,11 @@
1
1
  /**
2
- * continueHooks.ts — Continue governance: proxy routing + CLI PreToolUse gate.
2
+ * continueHooks.ts — Continue CLI PreToolUse gate.
3
3
  *
4
- * Two mechanisms, for two different Continue surfaces:
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
- * 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
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
- * JSONC handling: ~/.continue/config.json allows `// comments`. We strip
44
- * single-line comments with a regex before parsing, then write back as
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
- * Configure Continue: proxy routing for both surfaces, and the CLI's
54
- * PreToolUse gate.
55
- *
56
- * 1. Reads `~/.continue/config.json` (JSONC — strips // comments before parsing).
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 to set as model apiBase.
70
- * @param workspaceId - Workspace ID stored in the env snippet.
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AA2MH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAoHf"}
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 governance: proxy routing + CLI PreToolUse gate.
2
+ * continueHooks.ts — Continue CLI PreToolUse gate.
3
3
  *
4
- * Two mechanisms, for two different Continue surfaces:
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
- * 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
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
- * JSONC handling: ~/.continue/config.json allows `// comments`. We strip
44
- * single-line comments with a regex before parsing, then write back as
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
- 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
- }
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
- * Configure Continue: proxy routing for both surfaces, and the CLI's
238
- * PreToolUse gate.
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
- * 1. Reads `~/.continue/config.json` (JSONC — strips // comments before parsing).
241
- * 2. Sets `apiBase = proxyUrl` on each model that doesn't already use it.
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 to set as model apiBase.
254
- * @param workspaceId - Workspace ID stored in the env snippet.
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 (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.
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":"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"}
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 beforeShellExecution/beforeMCPExecution hook injection.
2
+ * cursorHooks.ts — Cursor hook injection.
3
3
  *
4
- * Writes hooks.json at THREE levels per enterprise-readiness requirement:
5
- * 1. Project-level: <workspaceRoot>/.cursor/hooks.json
6
- * 2. User-level: ~/.cursor/hooks.json
7
- * 3. System-level: /etc/cursor/hooks.json (Linux) — written only by system administrators
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
- * All three levels use failClosed: true so that if the governance script
10
- * fails or is missing the tool call is blocked, not allowed.
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
- _comment: string;
39
- _lastSync: string;
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;;;;;;;;;;;;;;;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"}
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"}