@intutic/sync-daemon 2.2.0 → 2.4.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 (235) hide show
  1. package/README.md +1 -1
  2. package/dist/agentReporter.d.ts +23 -11
  3. package/dist/agentReporter.d.ts.map +1 -1
  4. package/dist/agentReporter.js +20 -13
  5. package/dist/agentReporter.js.map +1 -1
  6. package/dist/configReader.d.ts +1 -3
  7. package/dist/configReader.d.ts.map +1 -1
  8. package/dist/configReader.js +0 -2
  9. package/dist/configReader.js.map +1 -1
  10. package/dist/configWriter.d.ts +11 -12
  11. package/dist/configWriter.d.ts.map +1 -1
  12. package/dist/configWriter.js +35 -58
  13. package/dist/configWriter.js.map +1 -1
  14. package/dist/disconnect/harnesses.d.ts +0 -2
  15. package/dist/disconnect/harnesses.d.ts.map +1 -1
  16. package/dist/disconnect/harnesses.js +157 -80
  17. package/dist/disconnect/harnesses.js.map +1 -1
  18. package/dist/disconnect/index.d.ts.map +1 -1
  19. package/dist/disconnect/index.js +11 -1
  20. package/dist/disconnect/index.js.map +1 -1
  21. package/dist/disconnect/mcp.d.ts +21 -0
  22. package/dist/disconnect/mcp.d.ts.map +1 -1
  23. package/dist/disconnect/mcp.js +71 -0
  24. package/dist/disconnect/mcp.js.map +1 -1
  25. package/dist/disconnect/recognise.d.ts +2 -2
  26. package/dist/disconnect/recognise.d.ts.map +1 -1
  27. package/dist/disconnect/recognise.js +15 -5
  28. package/dist/disconnect/recognise.js.map +1 -1
  29. package/dist/harness/aiderConfigMerger.d.ts +28 -8
  30. package/dist/harness/aiderConfigMerger.d.ts.map +1 -1
  31. package/dist/harness/aiderConfigMerger.js +59 -11
  32. package/dist/harness/aiderConfigMerger.js.map +1 -1
  33. package/dist/harness/antigravityCliHooks.d.ts +61 -0
  34. package/dist/harness/antigravityCliHooks.d.ts.map +1 -0
  35. package/dist/harness/antigravityCliHooks.js +207 -0
  36. package/dist/harness/antigravityCliHooks.js.map +1 -0
  37. package/dist/harness/antigravityHooks.d.ts +12 -2
  38. package/dist/harness/antigravityHooks.d.ts.map +1 -1
  39. package/dist/harness/antigravityHooks.js +36 -16
  40. package/dist/harness/antigravityHooks.js.map +1 -1
  41. package/dist/harness/antigravityProducts.d.ts +40 -0
  42. package/dist/harness/antigravityProducts.d.ts.map +1 -0
  43. package/dist/harness/antigravityProducts.js +109 -0
  44. package/dist/harness/antigravityProducts.js.map +1 -0
  45. package/dist/harness/claudeAgentsMd.d.ts +29 -0
  46. package/dist/harness/claudeAgentsMd.d.ts.map +1 -0
  47. package/dist/harness/claudeAgentsMd.js +80 -0
  48. package/dist/harness/claudeAgentsMd.js.map +1 -0
  49. package/dist/harness/claudeCodeHooks.d.ts +6 -5
  50. package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
  51. package/dist/harness/claudeCodeHooks.js +98 -24
  52. package/dist/harness/claudeCodeHooks.js.map +1 -1
  53. package/dist/harness/clineHooks.d.ts +0 -1
  54. package/dist/harness/clineHooks.d.ts.map +1 -1
  55. package/dist/harness/clineHooks.js +10 -4
  56. package/dist/harness/clineHooks.js.map +1 -1
  57. package/dist/harness/codexHooks.d.ts +2 -0
  58. package/dist/harness/codexHooks.d.ts.map +1 -1
  59. package/dist/harness/codexHooks.js +12 -4
  60. package/dist/harness/codexHooks.js.map +1 -1
  61. package/dist/harness/cursorHooks.d.ts +5 -2
  62. package/dist/harness/cursorHooks.d.ts.map +1 -1
  63. package/dist/harness/cursorHooks.js +9 -3
  64. package/dist/harness/cursorHooks.js.map +1 -1
  65. package/dist/harness/dshHooks.d.ts +5 -5
  66. package/dist/harness/dshHooks.d.ts.map +1 -1
  67. package/dist/harness/dshHooks.js +5 -5
  68. package/dist/harness/gateArtifacts.d.ts +35 -0
  69. package/dist/harness/gateArtifacts.d.ts.map +1 -0
  70. package/dist/harness/gateArtifacts.js +88 -0
  71. package/dist/harness/gateArtifacts.js.map +1 -0
  72. package/dist/harness/gateBody.d.ts +186 -69
  73. package/dist/harness/gateBody.d.ts.map +1 -1
  74. package/dist/harness/gateBody.js +820 -182
  75. package/dist/harness/gateBody.js.map +1 -1
  76. package/dist/harness/gateSightings.d.ts +47 -0
  77. package/dist/harness/gateSightings.d.ts.map +1 -0
  78. package/dist/harness/gateSightings.js +159 -0
  79. package/dist/harness/gateSightings.js.map +1 -0
  80. package/dist/harness/githubCopilotHooks.d.ts.map +1 -1
  81. package/dist/harness/githubCopilotHooks.js +7 -1
  82. package/dist/harness/githubCopilotHooks.js.map +1 -1
  83. package/dist/harness/gooseHardener.d.ts +0 -1
  84. package/dist/harness/gooseHardener.d.ts.map +1 -1
  85. package/dist/harness/gooseHardener.js +1 -1
  86. package/dist/harness/gooseHardener.js.map +1 -1
  87. package/dist/harness/gooseHooks.d.ts +14 -1
  88. package/dist/harness/gooseHooks.d.ts.map +1 -1
  89. package/dist/harness/gooseHooks.js +55 -29
  90. package/dist/harness/gooseHooks.js.map +1 -1
  91. package/dist/harness/grokHooks.d.ts +2 -2
  92. package/dist/harness/grokHooks.d.ts.map +1 -1
  93. package/dist/harness/grokHooks.js +11 -6
  94. package/dist/harness/grokHooks.js.map +1 -1
  95. package/dist/harness/hermesHooks.d.ts +18 -8
  96. package/dist/harness/hermesHooks.d.ts.map +1 -1
  97. package/dist/harness/hermesHooks.js +48 -17
  98. package/dist/harness/hermesHooks.js.map +1 -1
  99. package/dist/harness/mcpAutoWrite.d.ts +24 -9
  100. package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
  101. package/dist/harness/mcpAutoWrite.js +218 -25
  102. package/dist/harness/mcpAutoWrite.js.map +1 -1
  103. package/dist/harness/museHooks.d.ts +3 -3
  104. package/dist/harness/museHooks.d.ts.map +1 -1
  105. package/dist/harness/museHooks.js +9 -5
  106. package/dist/harness/museHooks.js.map +1 -1
  107. package/dist/harness/n8nHooks.d.ts +0 -1
  108. package/dist/harness/n8nHooks.d.ts.map +1 -1
  109. package/dist/harness/n8nHooks.js +4 -1
  110. package/dist/harness/n8nHooks.js.map +1 -1
  111. package/dist/harness/openCodeHooks.d.ts +2 -2
  112. package/dist/harness/openCodeHooks.d.ts.map +1 -1
  113. package/dist/harness/openCodeHooks.js +7 -3
  114. package/dist/harness/openCodeHooks.js.map +1 -1
  115. package/dist/harness/openWebuiHooks.d.ts +0 -1
  116. package/dist/harness/openWebuiHooks.d.ts.map +1 -1
  117. package/dist/harness/openWebuiHooks.js +5 -1
  118. package/dist/harness/openWebuiHooks.js.map +1 -1
  119. package/dist/harness/openclawHooks.d.ts +70 -18
  120. package/dist/harness/openclawHooks.d.ts.map +1 -1
  121. package/dist/harness/openclawHooks.js +213 -161
  122. package/dist/harness/openclawHooks.js.map +1 -1
  123. package/dist/harness/openhandsHooks.d.ts +24 -10
  124. package/dist/harness/openhandsHooks.d.ts.map +1 -1
  125. package/dist/harness/openhandsHooks.js +49 -48
  126. package/dist/harness/openhandsHooks.js.map +1 -1
  127. package/dist/harness/piHooks.d.ts +52 -17
  128. package/dist/harness/piHooks.d.ts.map +1 -1
  129. package/dist/harness/piHooks.js +177 -166
  130. package/dist/harness/piHooks.js.map +1 -1
  131. package/dist/harness/protectedPaths.d.ts +107 -25
  132. package/dist/harness/protectedPaths.d.ts.map +1 -1
  133. package/dist/harness/protectedPaths.js +342 -48
  134. package/dist/harness/protectedPaths.js.map +1 -1
  135. package/dist/harness/rulesSection.d.ts +62 -0
  136. package/dist/harness/rulesSection.d.ts.map +1 -0
  137. package/dist/harness/rulesSection.js +145 -0
  138. package/dist/harness/rulesSection.js.map +1 -0
  139. package/dist/harness/windsurfHooks.d.ts +0 -1
  140. package/dist/harness/windsurfHooks.d.ts.map +1 -1
  141. package/dist/harness/windsurfHooks.js +4 -1
  142. package/dist/harness/windsurfHooks.js.map +1 -1
  143. package/dist/index.d.ts +12 -8
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +11 -9
  146. package/dist/index.js.map +1 -1
  147. package/dist/inventory.d.ts +53 -0
  148. package/dist/inventory.d.ts.map +1 -0
  149. package/dist/inventory.js +133 -0
  150. package/dist/inventory.js.map +1 -0
  151. package/dist/lib/complianceProbes.d.ts +2 -1
  152. package/dist/lib/complianceProbes.d.ts.map +1 -1
  153. package/dist/lib/complianceProbes.js +1 -1
  154. package/dist/lib/complianceProbes.js.map +1 -1
  155. package/dist/lib/decisionsDigest.d.ts +34 -16
  156. package/dist/lib/decisionsDigest.d.ts.map +1 -1
  157. package/dist/lib/decisionsDigest.js +105 -46
  158. package/dist/lib/decisionsDigest.js.map +1 -1
  159. package/dist/lib/egressPolicy.d.ts +2 -2
  160. package/dist/lib/egressPolicy.js +2 -2
  161. package/dist/lib/mcpRegistryPy.d.ts +11 -0
  162. package/dist/lib/mcpRegistryPy.d.ts.map +1 -0
  163. package/dist/lib/mcpRegistryPy.js +173 -0
  164. package/dist/lib/mcpRegistryPy.js.map +1 -0
  165. package/dist/lib/phrasesPy.d.ts +11 -0
  166. package/dist/lib/phrasesPy.d.ts.map +1 -0
  167. package/dist/lib/phrasesPy.js +351 -0
  168. package/dist/lib/phrasesPy.js.map +1 -0
  169. package/dist/lib/policySnapshot.d.ts +46 -10
  170. package/dist/lib/policySnapshot.d.ts.map +1 -1
  171. package/dist/lib/policySnapshot.js +136 -47
  172. package/dist/lib/policySnapshot.js.map +1 -1
  173. package/dist/lib/processPoller.d.ts.map +1 -1
  174. package/dist/lib/processPoller.js +9 -2
  175. package/dist/lib/processPoller.js.map +1 -1
  176. package/dist/lib/regexLinearity.d.ts +43 -0
  177. package/dist/lib/regexLinearity.d.ts.map +1 -0
  178. package/dist/lib/regexLinearity.js +275 -0
  179. package/dist/lib/regexLinearity.js.map +1 -0
  180. package/dist/lib/runtimeEnv.d.ts +1 -3
  181. package/dist/lib/runtimeEnv.d.ts.map +1 -1
  182. package/dist/lib/runtimeEnv.js +0 -2
  183. package/dist/lib/runtimeEnv.js.map +1 -1
  184. package/dist/lib/sequencePy.d.ts +10 -0
  185. package/dist/lib/sequencePy.d.ts.map +1 -0
  186. package/dist/lib/sequencePy.js +102 -0
  187. package/dist/lib/sequencePy.js.map +1 -0
  188. package/dist/sessionReporter.d.ts +17 -11
  189. package/dist/sessionReporter.d.ts.map +1 -1
  190. package/dist/sessionReporter.js +36 -23
  191. package/dist/sessionReporter.js.map +1 -1
  192. package/dist/skillWriter.d.ts +23 -5
  193. package/dist/skillWriter.d.ts.map +1 -1
  194. package/dist/skillWriter.js +159 -16
  195. package/dist/skillWriter.js.map +1 -1
  196. package/dist/syncCycle.d.ts.map +1 -1
  197. package/dist/syncCycle.js +21 -9
  198. package/dist/syncCycle.js.map +1 -1
  199. package/dist/trajectoryMonitor.d.ts +1 -3
  200. package/dist/trajectoryMonitor.d.ts.map +1 -1
  201. package/dist/trajectoryMonitor.js +1 -4
  202. package/dist/trajectoryMonitor.js.map +1 -1
  203. package/dist/watcher/driftWatcher.d.ts +0 -1
  204. package/dist/watcher/driftWatcher.d.ts.map +1 -1
  205. package/dist/watcher/driftWatcher.js +1 -2
  206. package/dist/watcher/driftWatcher.js.map +1 -1
  207. package/dist/watcher/settingsGuard.d.ts +33 -7
  208. package/dist/watcher/settingsGuard.d.ts.map +1 -1
  209. package/dist/watcher/settingsGuard.js +373 -61
  210. package/dist/watcher/settingsGuard.js.map +1 -1
  211. package/dist/watcher/vscodeHookSettings.d.ts +30 -0
  212. package/dist/watcher/vscodeHookSettings.d.ts.map +1 -0
  213. package/dist/watcher/vscodeHookSettings.js +170 -0
  214. package/dist/watcher/vscodeHookSettings.js.map +1 -0
  215. package/dist/wsClient.d.ts +0 -1
  216. package/dist/wsClient.d.ts.map +1 -1
  217. package/dist/wsClient.js +0 -1
  218. package/dist/wsClient.js.map +1 -1
  219. package/package.json +5 -5
  220. package/dist/harness/claudeDesktopHooks.d.ts +0 -33
  221. package/dist/harness/claudeDesktopHooks.d.ts.map +0 -1
  222. package/dist/harness/claudeDesktopHooks.js +0 -256
  223. package/dist/harness/claudeDesktopHooks.js.map +0 -1
  224. package/dist/harness/continueHooks.d.ts +0 -63
  225. package/dist/harness/continueHooks.d.ts.map +0 -1
  226. package/dist/harness/continueHooks.js +0 -250
  227. package/dist/harness/continueHooks.js.map +0 -1
  228. package/dist/harness/gateKind.d.ts +0 -164
  229. package/dist/harness/gateKind.d.ts.map +0 -1
  230. package/dist/harness/gateKind.js +0 -144
  231. package/dist/harness/gateKind.js.map +0 -1
  232. package/dist/harness/rooCodeHooks.d.ts +0 -31
  233. package/dist/harness/rooCodeHooks.d.ts.map +0 -1
  234. package/dist/harness/rooCodeHooks.js +0 -281
  235. package/dist/harness/rooCodeHooks.js.map +0 -1
@@ -1,256 +0,0 @@
1
- /**
2
- * claudeDesktopHooks.ts — Claude Desktop PreToolUse hook generator.
3
- *
4
- * Claude Desktop shares the same hook format as Claude Code (PreToolUse,
5
- * exit 2 on stdin JSON) but reads its config from an OS-specific location:
6
- * macOS : ~/Library/Application Support/Claude/claude_desktop_config.json
7
- * Linux : ~/.config/Claude/claude_desktop_config.json
8
- *
9
- * Hook events are written via dual-path logEvent:
10
- * Path B (reliable) — synchronous append to .intutic/events/claude-desktop-hook-events.jsonl
11
- * Path A (telemetry) — fire-and-forget https.request to the control plane
12
- *
13
- * LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
14
- * HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
15
- *
16
- * @module
17
- */
18
- import * as fs from 'node:fs/promises';
19
- import * as path from 'node:path';
20
- import * as os from 'node:os';
21
- import { createLogger } from '@intutic/logger';
22
- import { keepOriginal } from '../disconnect/originals.js';
23
- import { newIso } from '@intutic/id';
24
- import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
25
- const log = createLogger('sync-claude-desktop-hooks');
26
- /** Governance-sensitive paths that the hook gate protects (mirrors claudeCodeHooks). */
27
- /** Resolve the OS-specific claude_desktop_config.json path. */
28
- export function resolveClaudeDesktopConfigPath() {
29
- switch (process.platform) {
30
- case 'darwin':
31
- return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
32
- case 'linux':
33
- return path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');
34
- default:
35
- // Fallback to macOS path — Windows is out of scope for now
36
- return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
37
- }
38
- }
39
- // ─── Node.js hook script template ─────────────────────────────────────────────
40
- function buildClaudeDesktopCheckScript(proxyUrl, workspaceRoot, workspaceId) {
41
- const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'claude-desktop-hook-events.jsonl');
42
- const runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
43
- return `/**
44
- * Intutic Claude Desktop PreToolUse execution gate.
45
- * Auto-generated by intutic sync-daemon. DO NOT EDIT.
46
- *
47
- * Dual-path logEvent:
48
- * Path B — synchronous file append to hook-events.jsonl
49
- * Path A — fire-and-forget https.request to the control plane
50
- *
51
- * Proxy: ${proxyUrl}
52
- * Generated: ${newIso()}
53
- * Workspace: ${workspaceId}
54
- */
55
- 'use strict';
56
- ${emitJsFailClosedPrelude({ harness: 'claude-desktop', contract: 'exit2' })}
57
- const path = require('path');
58
- const os = require('os');
59
- const fs = require('fs');
60
- const https = require('https');
61
- const crypto = require('crypto');
62
- const url = require('url');
63
-
64
- // ── Runtime env loader (Path A credentials) ──────────────────────────────────
65
- const RUNTIME_ENV_PATH = ${JSON.stringify(runtimeEnvPath)};
66
-
67
- function loadRuntimeEnv() {
68
- const env = {
69
- INTUTIC_HOST: 'https://api.intutic.ai',
70
- INTUTIC_API_KEY: '',
71
- INTUTIC_WORKSPACE_ID: ${JSON.stringify(workspaceId)},
72
- };
73
- try {
74
- const raw = fs.readFileSync(RUNTIME_ENV_PATH, 'utf-8');
75
- for (const line of raw.split('\\n')) {
76
- const stripped = line.trim();
77
- if (!stripped || stripped.startsWith('#') || !stripped.includes('=')) continue;
78
- const eq = stripped.indexOf('=');
79
- const k = stripped.slice(0, eq).trim();
80
- const v = stripped.slice(eq + 1).trim();
81
- env[k] = v;
82
- }
83
- } catch { /* file may not exist yet */ }
84
- return env;
85
- }
86
-
87
- // ── Governance-sensitive paths ────────────────────────────────────────────────
88
- ${emitJsGate({ harness: "claude-desktop", contract: 'exit2' })}
89
-
90
- // ── Dual-path logEvent ────────────────────────────────────────────────────────
91
- const HOOK_EVENTS_LOG = ${JSON.stringify(hookEventsLog)};
92
-
93
- let _intuticSessionId = '';
94
- function logEvent(env, verdict, toolName, reason) {
95
- const ts = new Date().toISOString();
96
- const incidentId = crypto.createHash('sha1')
97
- .update(ts + toolName + env.INTUTIC_WORKSPACE_ID)
98
- .digest('hex').slice(0, 16);
99
-
100
- const entry = {
101
- // Passed through, not collapsed to two values: the advisory tier emits
102
- // 'tool_flagged', and a ternary here silently recorded it as an allow.
103
- event: verdict,
104
- toolName,
105
- reason: reason || '',
106
- workspaceId: env.INTUTIC_WORKSPACE_ID,
107
- harnessType: 'claude-desktop',
108
- timestamp: ts,
109
- incidentId,
110
- ...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
111
- };
112
-
113
- // Path B — reliable file append
114
- try {
115
- const dir = path.dirname(HOOK_EVENTS_LOG);
116
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
117
- fs.appendFileSync(HOOK_EVENTS_LOG, JSON.stringify(entry) + '\\n', { flag: 'a' });
118
- } catch { /* never crash the hook */ }
119
-
120
- // Path A — fire-and-forget https.request
121
- if (env.INTUTIC_API_KEY) {
122
- try {
123
- const body = Buffer.from(JSON.stringify({ events: [entry] }));
124
- const parsedUrl = url.parse(env.INTUTIC_HOST + '/api/v1/hook-events');
125
- const req = https.request({
126
- hostname: parsedUrl.hostname,
127
- port: parsedUrl.port || 443,
128
- path: parsedUrl.path,
129
- method: 'POST',
130
- headers: {
131
- 'Content-Type': 'application/json',
132
- 'Content-Length': body.length,
133
- 'Authorization': 'Bearer ' + env.INTUTIC_API_KEY,
134
- },
135
- });
136
- req.on('error', () => {}); // fire-and-forget
137
- req.write(body);
138
- req.end();
139
- } catch { /* never crash the hook */ }
140
- }
141
- }
142
-
143
- // ── Main ──────────────────────────────────────────────────────────────────────
144
- const runtimeEnv = loadRuntimeEnv();
145
- let inputData = '';
146
- process.stdin.on('data', (chunk) => { inputData += chunk; });
147
- process.stdin.on('end', () => {
148
- try {
149
- const ctx = JSON.parse(inputData);
150
- _intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
151
- // An envelope carrying none of the tool fields extracts to empty strings,
152
- // which match no rule — an allow. Refused instead; see the guard itself.
153
- intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'],
154
- (v, t, r) => logEvent(runtimeEnv, v, t, r));
155
- const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase()
156
- // Case preserved for the gate. A BLOCK: SOP compiles to a tool-name
157
- // pattern the operator wrote as they see it (Bash, Write) and this
158
- // harness lowercases the tool for its own matching. Handing the gate the
159
- // lowercased form means every SOP tool rule silently matches nothing here
160
- // while appearing active everywhere else.
161
- const rawToolName = ctx.tool_name || ctx.toolName || '';
162
- const toolInput = ctx.tool_input || ctx.toolInput || {};
163
- const toolInputStr = JSON.stringify(toolInput);
164
-
165
- // Both guards used to be gated on the tool name — paths only for
166
- // edit/write/multiedit, commands only for a tool literally called "bash".
167
- // A harness that names its shell tool anything else (run_command, terminal,
168
- // execute) walked past both. The shared gate keys on the *arguments*, so a
169
- // tool name nobody anticipated is no longer a bypass.
170
- const targetPath = toolInput.path || toolInput.file_path || toolInput.filePath ||
171
- toolInput.new_path || toolInput.target || toolInput.notebook_path || '';
172
- const command = toolInput.command || toolInput.cmd || toolInput.script || '';
173
- intuticGate(rawToolName, targetPath, command, (v, t, r) => logEvent(runtimeEnv, v, t, r), runtimeEnv.INTUTIC_WORKSPACE_ID, toolInput);
174
-
175
- logEvent(runtimeEnv, 'tool_allowed', toolName, '');
176
- process.exit(0);
177
- } catch (err) {
178
- console.error('[Intutic Guardrail] Hook error (blocking for safety):', err);
179
- logEvent(runtimeEnv, 'tool_blocked', 'unknown', String(err));
180
- process.exit(2);
181
- }
182
- });
183
- `.trim();
184
- }
185
- // ─── Main export ──────────────────────────────────────────────────────────────
186
- /**
187
- * Write the Claude Desktop governance hook script and merge hook configuration
188
- * into the OS-specific claude_desktop_config.json.
189
- *
190
- * Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
191
- * Does NOT touch the `mcpServers` section of the config.
192
- *
193
- * @param workspaceRoot - Absolute workspace root path.
194
- * @param proxyUrl - Intutic proxy URL written into the hook script header.
195
- * @param workspaceId - Workspace ID embedded in every hook event payload.
196
- */
197
- export async function writeClaudeDesktopHooks(workspaceRoot, proxyUrl, workspaceId = '') {
198
- // ── 1. Ensure directories ──────────────────────────────────────────────────
199
- const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
200
- const hookEventsDir = path.join(workspaceRoot, '.intutic', 'events');
201
- const desktopConfigPath = resolveClaudeDesktopConfigPath();
202
- const desktopConfigDir = path.dirname(desktopConfigPath);
203
- await keepOriginal(desktopConfigPath, workspaceRoot);
204
- await Promise.all([
205
- fs.mkdir(hookScriptDir, { recursive: true }),
206
- fs.mkdir(hookEventsDir, { recursive: true }),
207
- fs.mkdir(desktopConfigDir, { recursive: true }),
208
- ]);
209
- // ── 2. Write hook script (atomic) ─────────────────────────────────────────
210
- const hookScriptPath = path.join(hookScriptDir, 'claude-desktop-check.js');
211
- const tmpScript = hookScriptPath + '.intutic-tmp';
212
- await fs.writeFile(tmpScript, buildClaudeDesktopCheckScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
213
- await fs.rename(tmpScript, hookScriptPath);
214
- log.info({ action: 'claude_desktop_hook_script_written', path: hookScriptPath }, 'Claude Desktop hook script written');
215
- // ── 3. Merge into claude_desktop_config.json (safe merge, preserve mcpServers) ──
216
- let existingConfig = {};
217
- try {
218
- const raw = await fs.readFile(desktopConfigPath, 'utf-8');
219
- existingConfig = JSON.parse(raw);
220
- }
221
- catch {
222
- // File doesn't exist yet — start fresh
223
- }
224
- const existingHooks = existingConfig.hooks ?? {};
225
- const existingPreToolUse = existingHooks.PreToolUse ?? [];
226
- // De-duplicate: remove any existing Intutic entry for Bash then re-add
227
- // M3: 'mcp__.*' routes Claude Desktop's mcp__<server>__<tool> calls into
228
- // this same gate script — see the identical note in claudeCodeHooks.ts,
229
- // which shares this harness's PreToolUse contract exactly.
230
- const HOOK_MATCHERS = ['Bash', 'Edit', 'Write', 'MultiEdit', 'mcp__.*'];
231
- const intuticCmd = `node ${hookScriptPath}`;
232
- const filteredPTU = existingPreToolUse.filter((entry) => {
233
- if (typeof entry !== 'object' || entry === null)
234
- return true;
235
- const e = entry;
236
- // Remove entries where all inner hooks use the intutic command
237
- const innerHooks = e.hooks ?? [];
238
- return !innerHooks.every((h) => typeof h === 'object' && h !== null && h.command === intuticCmd);
239
- });
240
- const newPreToolUseEntries = HOOK_MATCHERS.map((matcher) => ({
241
- matcher,
242
- hooks: [{ type: 'command', command: intuticCmd }],
243
- }));
244
- const mergedConfig = {
245
- ...existingConfig,
246
- hooks: {
247
- ...existingHooks,
248
- PreToolUse: [...filteredPTU, ...newPreToolUseEntries],
249
- },
250
- };
251
- const tmpConfig = desktopConfigPath + '.intutic-tmp';
252
- await fs.writeFile(tmpConfig, JSON.stringify(mergedConfig, null, 2) + '\n', 'utf-8');
253
- await fs.rename(tmpConfig, desktopConfigPath);
254
- log.info({ action: 'claude_desktop_config_written', path: desktopConfigPath }, 'Merged Intutic hooks into claude_desktop_config.json');
255
- }
256
- //# sourceMappingURL=claudeDesktopHooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeDesktopHooks.js","sourceRoot":"","sources":["../../src/harness/claudeDesktopHooks.ts"],"names":[],"mappings":"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"}
@@ -1,63 +0,0 @@
1
- /**
2
- * continueHooks.ts — Continue CLI PreToolUse gate.
3
- *
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.
7
- *
8
- * **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
9
- * no hook system): the CLI executes PreToolUse hooks registered in
10
- * `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
11
- * (project), with a Claude-Code-compatible stdin contract —
12
- * {tool_name, tool_input, tool_use_id} — refused via exit code 2 (or a
13
- * stdout `{"permissionDecision":"deny"}`; exit 2 is used here, the contract
14
- * every other exit-2 harness shares).
15
- *
16
- * **LIVE-VERIFIED LIMITS (cn 1.5.47, 2026-08-10), do not oversell this
17
- * gate:**
18
- * - **Headless `-p`/`--print` mode does not execute PreToolUse hooks at
19
- * all.** Verified against a real install with a bare marker hook: the
20
- * terminal tool ran, the marker never fired, with the hook registered in
21
- * every path the bundle's own search list names. The hooks engine (and
22
- * this exact search list, including `.claude/settings.json`) is present
23
- * in the shipped bundle — headless mode simply bypasses it. Interactive
24
- * TUI sessions are the mode this registration targets; a headless `cn
25
- * -p` run is governed by the proxy layer only.
26
- * - **cn's hook engine fails OPEN on hook errors by its own design** —
27
- * the bundled dispatcher catches a failed hook event and returns
28
- * `{blocked: false}`. Our gate's own crash posture (fail-closed exit 2)
29
- * cannot override the harness swallowing the failure; that is cn's
30
- * contract, recorded here so the security matrix can say it honestly.
31
- *
32
- * **Overlap, stated so nobody "fixes" it:** the Continue CLI ALSO reads
33
- * `.claude/settings.json`, so on a machine where claude-code is governed,
34
- * `cn` may already run the claude-code gate by accident. This writer makes
35
- * that deliberate by registering in Continue's own settings files — the
36
- * dedicated registration is still correct because a user who runs continue
37
- * but not claude-code has no `.claude/settings.json` and would otherwise
38
- * get no gate at all. Double registration is harmless: both gates evaluate
39
- * the same rules, and an allow/allow or block/block pair is idempotent.
40
- *
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).
43
- *
44
- * LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
45
- * HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
46
- *
47
- * @module
48
- */
49
- /**
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).
54
- *
55
- * Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
56
- * and command-keyed de-duplication in both settings files.
57
- *
58
- * @param workspaceRoot - Absolute workspace root path.
59
- * @param proxyUrl - Intutic proxy URL written into the gate header.
60
- * @param workspaceId - Workspace ID embedded in every hook event payload.
61
- */
62
- export declare function writeContinueHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
63
- //# sourceMappingURL=continueHooks.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"continueHooks.d.ts","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAkLH;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
@@ -1,250 +0,0 @@
1
- /**
2
- * continueHooks.ts — Continue CLI PreToolUse gate.
3
- *
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.
7
- *
8
- * **PreToolUse gate** (Continue **CLI `cn` only** — the IDE extension has
9
- * no hook system): the CLI executes PreToolUse hooks registered in
10
- * `~/.continue/settings.json` (user) and `<repo>/.continue/settings.json`
11
- * (project), with a Claude-Code-compatible stdin contract —
12
- * {tool_name, tool_input, tool_use_id} — refused via exit code 2 (or a
13
- * stdout `{"permissionDecision":"deny"}`; exit 2 is used here, the contract
14
- * every other exit-2 harness shares).
15
- *
16
- * **LIVE-VERIFIED LIMITS (cn 1.5.47, 2026-08-10), do not oversell this
17
- * gate:**
18
- * - **Headless `-p`/`--print` mode does not execute PreToolUse hooks at
19
- * all.** Verified against a real install with a bare marker hook: the
20
- * terminal tool ran, the marker never fired, with the hook registered in
21
- * every path the bundle's own search list names. The hooks engine (and
22
- * this exact search list, including `.claude/settings.json`) is present
23
- * in the shipped bundle — headless mode simply bypasses it. Interactive
24
- * TUI sessions are the mode this registration targets; a headless `cn
25
- * -p` run is governed by the proxy layer only.
26
- * - **cn's hook engine fails OPEN on hook errors by its own design** —
27
- * the bundled dispatcher catches a failed hook event and returns
28
- * `{blocked: false}`. Our gate's own crash posture (fail-closed exit 2)
29
- * cannot override the harness swallowing the failure; that is cn's
30
- * contract, recorded here so the security matrix can say it honestly.
31
- *
32
- * **Overlap, stated so nobody "fixes" it:** the Continue CLI ALSO reads
33
- * `.claude/settings.json`, so on a machine where claude-code is governed,
34
- * `cn` may already run the claude-code gate by accident. This writer makes
35
- * that deliberate by registering in Continue's own settings files — the
36
- * dedicated registration is still correct because a user who runs continue
37
- * but not claude-code has no `.claude/settings.json` and would otherwise
38
- * get no gate at all. Double registration is harmless: both gates evaluate
39
- * the same rules, and an allow/allow or block/block pair is idempotent.
40
- *
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).
43
- *
44
- * LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
45
- * HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
46
- *
47
- * @module
48
- */
49
- import * as fs from 'node:fs/promises';
50
- import * as path from 'node:path';
51
- import * as os from 'node:os';
52
- import { createLogger } from '@intutic/logger';
53
- import { keepOriginal } from '../disconnect/originals.js';
54
- import { newIso } from '@intutic/id';
55
- import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
56
- import { readJsonObjectForMerge } from './jsonMergeTarget.js';
57
- const log = createLogger('sync-continue-hooks');
58
- // ─── PreToolUse gate (Continue CLI `cn`) ──────────────────────────────────────
59
- /**
60
- * The pre-tool gate script — receives the Continue CLI's Claude-Code-compatible
61
- * PreToolUse JSON on stdin, exits 0 to allow or 2 to block.
62
- */
63
- function buildHookScript(proxyUrl, workspaceRoot, workspaceId) {
64
- const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
65
- return `#!/usr/bin/env node
66
- /**
67
- * Intutic Continue CLI governance gate.
68
- * Auto-generated by intutic sync-daemon. DO NOT EDIT.
69
- *
70
- * Applies to the Continue CLI (cn) only — the IDE extension has no hook
71
- * system. Note the CLI also reads .claude/settings.json, so a machine governed
72
- * for claude-code may run that gate too; both evaluate the same rules.
73
- *
74
- * Proxy: ${proxyUrl}
75
- * Generated: ${newIso()}
76
- */
77
- ${emitJsFailClosedPrelude({ harness: 'continue', contract: 'exit2' })}
78
- const path = require('path');
79
- const os = require('os');
80
- const fs = require('fs');
81
- const crypto = require('crypto');
82
- const https = require('https');
83
-
84
- // Runtime credentials
85
- const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
86
- let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
87
- try {
88
- fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
89
- const eq = line.indexOf('='); if (eq < 0) return;
90
- const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
91
- if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
92
- if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
93
- if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
94
- });
95
- } catch {}
96
-
97
- ${emitJsGate({ harness: 'continue', contract: 'exit2' })}
98
-
99
- let _intuticSessionId = '';
100
- function logEvent(verdict, toolName, reason) {
101
- try {
102
- const ts = new Date().toISOString();
103
- const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
104
- const entry = JSON.stringify({
105
- // Passed through, not collapsed to two values: the advisory tier emits
106
- // 'tool_flagged', and a ternary here silently recorded it as an allow.
107
- event: verdict,
108
- toolName, reason: reason || '',
109
- workspaceId: _intuticWsId,
110
- harnessType: 'continue',
111
- timestamp: ts,
112
- incidentId,
113
- ...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
114
- }) + '\\n';
115
- fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
116
- if (_intuticKey) {
117
- try {
118
- const body = JSON.stringify({ events: [JSON.parse(entry)] });
119
- const urlObj = new URL('/api/v1/hook-events', _intuticHost);
120
- const mod = urlObj.protocol === 'https:' ? https : require('http');
121
- 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 } });
122
- req.on('error', () => { /* fire-and-forget */ });
123
- req.write(body); req.end();
124
- } catch { /* never crash the hook */ }
125
- }
126
- } catch { /* never crash the hook */ }
127
- }
128
-
129
- let raw = '';
130
- process.stdin.on('data', (c) => { raw += c; });
131
- process.stdin.on('end', () => {
132
- try {
133
- const ctx = JSON.parse(raw);
134
- _intuticSessionId = ctx.session_id || ctx.sessionId || '';
135
- // Verified during the v5 audit: this writer did NOT have the envelope
136
- // guard its Claude-Code-compatible contract implied it inherited — a
137
- // payload with neither a tool name nor a tool_input defaulted to toolName
138
- // 'tool' with empty fields, matched no rule, and was allowed. Refused now.
139
- intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
140
- const input = ctx.tool_input || ctx.toolInput || {};
141
- const toolName = ctx.tool_name || ctx.toolName || 'tool';
142
-
143
- // Every spelling any harness uses, matching the bash extractor. A field
144
- // name this list misses is a path guard that silently does not run — and
145
- // the caller controls which spelling it sends.
146
- const targetPath = input.path || input.file_path || input.filePath || input.file ||
147
- input.target || input.notebook_path || '';
148
- const command = input.command || input.cmd || input.script || '';
149
-
150
- intuticGate(toolName, targetPath, command, logEvent, _intuticWsId, input);
151
-
152
- // Allow
153
- logEvent('tool_allowed', toolName, '');
154
- process.exit(0);
155
- } catch (err) {
156
- // Fail CLOSED
157
- const errMsg = String(err);
158
- process.stderr.write('[Intutic Governance] Hook error (fail-closed): ' + errMsg + '\\n');
159
- logEvent('tool_blocked', 'unknown', errMsg);
160
- process.exit(2);
161
- }
162
- });
163
- `;
164
- }
165
- /**
166
- * Merge the Intutic PreToolUse entry into a Continue settings.json, preserving
167
- * everything the user registered themselves.
168
- *
169
- * De-duplication is by command (any entry whose hooks all invoke this gate
170
- * script is ours and is replaced) — the claudeDesktopHooks convention — so
171
- * repeated syncs never stack duplicate entries.
172
- */
173
- async function mergeContinueSettings(settingsPath, hookScriptPath) {
174
- const existing = await readJsonObjectForMerge(settingsPath);
175
- if (existing === null)
176
- return;
177
- const existingHooks = existing.hooks ?? {};
178
- const existingPreToolUse = Array.isArray(existingHooks.PreToolUse)
179
- ? existingHooks.PreToolUse
180
- : [];
181
- const intuticCmd = `node "${hookScriptPath}"`;
182
- const filtered = existingPreToolUse.filter((entry) => {
183
- if (typeof entry !== 'object' || entry === null)
184
- return true;
185
- const inner = entry.hooks ?? [];
186
- return !(inner.length > 0 &&
187
- inner.every((h) => typeof h === 'object' && h !== null && h.command === intuticCmd));
188
- });
189
- const merged = {
190
- ...existing,
191
- _intutic_comment: 'Intutic governance PreToolUse hook (Continue CLI) — auto-managed entries only; your own hooks are preserved.',
192
- _intutic_last_sync: newIso(),
193
- hooks: {
194
- ...existingHooks,
195
- PreToolUse: [
196
- ...filtered,
197
- {
198
- // One catch-all matcher rather than a tool list: the gate itself
199
- // decides from the arguments, so a tool name nobody anticipated is
200
- // still evaluated rather than silently unmatched.
201
- matcher: '.*',
202
- hooks: [{ type: 'command', command: intuticCmd }],
203
- },
204
- ],
205
- },
206
- };
207
- const tmp = settingsPath + '.intutic-tmp';
208
- await fs.writeFile(tmp, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
209
- await fs.rename(tmp, settingsPath);
210
- }
211
- // ─── Main export ──────────────────────────────────────────────────────────────
212
- /**
213
- * Write the Continue CLI gate script `.intutic/hooks/continue-check.js` and
214
- * register it in `~/.continue/settings.json` and
215
- * `<root>/.continue/settings.json` (see the module header for the
216
- * `.claude/settings.json` overlap).
217
- *
218
- * Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path)
219
- * and command-keyed de-duplication in both settings files.
220
- *
221
- * @param workspaceRoot - Absolute workspace root path.
222
- * @param proxyUrl - Intutic proxy URL written into the gate header.
223
- * @param workspaceId - Workspace ID embedded in every hook event payload.
224
- */
225
- export async function writeContinueHooks(workspaceRoot, proxyUrl, workspaceId = '') {
226
- const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
227
- await fs.mkdir(hookScriptDir, { recursive: true });
228
- await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
229
- // Named after its writer — see the note in claudeCodeHooks.ts about two
230
- // writers overwriting one `pre-tool-check.js`.
231
- const hookScriptPath = path.join(hookScriptDir, 'continue-check.js');
232
- const tmpScript = hookScriptPath + '.intutic-tmp';
233
- await fs.writeFile(tmpScript, buildHookScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
234
- await fs.rename(tmpScript, hookScriptPath);
235
- await fs.chmod(hookScriptPath, 0o755);
236
- // Project-level: <root>/.continue/settings.json
237
- const projectContinueDir = path.join(workspaceRoot, '.continue');
238
- await keepOriginal(path.join(projectContinueDir, 'settings.json'), workspaceRoot);
239
- await fs.mkdir(projectContinueDir, { recursive: true });
240
- await mergeContinueSettings(path.join(projectContinueDir, 'settings.json'), hookScriptPath);
241
- // User-level: ~/.continue/settings.json. homedir() read at call time so
242
- // tests that move HOME before invoking are honoured — see the gooseHooks
243
- // note in generatedShellIntegrity.test.ts.
244
- const userContinueDir = path.join(os.homedir(), '.continue');
245
- await keepOriginal(path.join(userContinueDir, 'settings.json'), workspaceRoot);
246
- await fs.mkdir(userContinueDir, { recursive: true });
247
- await mergeContinueSettings(path.join(userContinueDir, 'settings.json'), hookScriptPath);
248
- log.info({ action: 'continue_gate_written', path: hookScriptPath }, 'Continue CLI PreToolUse gate written and registered (project + user settings.json)');
249
- }
250
- //# sourceMappingURL=continueHooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"continueHooks.js","sourceRoot":"","sources":["../../src/harness/continueHooks.ts"],"names":[],"mappings":"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"}