@intutic/sync-daemon 1.6.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 (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/brainIndexer.d.ts +51 -0
  4. package/dist/brainIndexer.d.ts.map +1 -0
  5. package/dist/brainIndexer.js +148 -0
  6. package/dist/brainIndexer.js.map +1 -0
  7. package/dist/configReader.d.ts +46 -0
  8. package/dist/configReader.d.ts.map +1 -0
  9. package/dist/configReader.js +133 -0
  10. package/dist/configReader.js.map +1 -0
  11. package/dist/configWriter.d.ts +53 -0
  12. package/dist/configWriter.d.ts.map +1 -0
  13. package/dist/configWriter.js +503 -0
  14. package/dist/configWriter.js.map +1 -0
  15. package/dist/harness/aiderConfigMerger.d.ts +27 -0
  16. package/dist/harness/aiderConfigMerger.d.ts.map +1 -0
  17. package/dist/harness/aiderConfigMerger.js +139 -0
  18. package/dist/harness/aiderConfigMerger.js.map +1 -0
  19. package/dist/harness/antigravityHooks.d.ts +43 -0
  20. package/dist/harness/antigravityHooks.d.ts.map +1 -0
  21. package/dist/harness/antigravityHooks.js +193 -0
  22. package/dist/harness/antigravityHooks.js.map +1 -0
  23. package/dist/harness/claudeCodeHooks.d.ts +59 -0
  24. package/dist/harness/claudeCodeHooks.d.ts.map +1 -0
  25. package/dist/harness/claudeCodeHooks.js +414 -0
  26. package/dist/harness/claudeCodeHooks.js.map +1 -0
  27. package/dist/harness/claudeDesktopHooks.d.ts +30 -0
  28. package/dist/harness/claudeDesktopHooks.d.ts.map +1 -0
  29. package/dist/harness/claudeDesktopHooks.js +267 -0
  30. package/dist/harness/claudeDesktopHooks.js.map +1 -0
  31. package/dist/harness/clineHooks.d.ts +23 -0
  32. package/dist/harness/clineHooks.d.ts.map +1 -0
  33. package/dist/harness/clineHooks.js +169 -0
  34. package/dist/harness/clineHooks.js.map +1 -0
  35. package/dist/harness/continueHooks.d.ts +33 -0
  36. package/dist/harness/continueHooks.d.ts.map +1 -0
  37. package/dist/harness/continueHooks.js +124 -0
  38. package/dist/harness/continueHooks.js.map +1 -0
  39. package/dist/harness/cursorHooks.d.ts +27 -0
  40. package/dist/harness/cursorHooks.d.ts.map +1 -0
  41. package/dist/harness/cursorHooks.js +221 -0
  42. package/dist/harness/cursorHooks.js.map +1 -0
  43. package/dist/harness/cursorHooksJson.d.ts +29 -0
  44. package/dist/harness/cursorHooksJson.d.ts.map +1 -0
  45. package/dist/harness/cursorHooksJson.js +130 -0
  46. package/dist/harness/cursorHooksJson.js.map +1 -0
  47. package/dist/harness/gooseHardener.d.ts +39 -0
  48. package/dist/harness/gooseHardener.d.ts.map +1 -0
  49. package/dist/harness/gooseHardener.js +152 -0
  50. package/dist/harness/gooseHardener.js.map +1 -0
  51. package/dist/harness/gooseHooks.d.ts +24 -0
  52. package/dist/harness/gooseHooks.d.ts.map +1 -0
  53. package/dist/harness/gooseHooks.js +175 -0
  54. package/dist/harness/gooseHooks.js.map +1 -0
  55. package/dist/harness/gooseHooksWriter.d.ts +27 -0
  56. package/dist/harness/gooseHooksWriter.d.ts.map +1 -0
  57. package/dist/harness/gooseHooksWriter.js +147 -0
  58. package/dist/harness/gooseHooksWriter.js.map +1 -0
  59. package/dist/harness/hermesHooks.d.ts +31 -0
  60. package/dist/harness/hermesHooks.d.ts.map +1 -0
  61. package/dist/harness/hermesHooks.js +245 -0
  62. package/dist/harness/hermesHooks.js.map +1 -0
  63. package/dist/harness/mcpAutoWrite.d.ts +35 -0
  64. package/dist/harness/mcpAutoWrite.d.ts.map +1 -0
  65. package/dist/harness/mcpAutoWrite.js +334 -0
  66. package/dist/harness/mcpAutoWrite.js.map +1 -0
  67. package/dist/harness/n8nHooks.d.ts +28 -0
  68. package/dist/harness/n8nHooks.d.ts.map +1 -0
  69. package/dist/harness/n8nHooks.js +205 -0
  70. package/dist/harness/n8nHooks.js.map +1 -0
  71. package/dist/harness/openWebuiHooks.d.ts +31 -0
  72. package/dist/harness/openWebuiHooks.d.ts.map +1 -0
  73. package/dist/harness/openWebuiHooks.js +166 -0
  74. package/dist/harness/openWebuiHooks.js.map +1 -0
  75. package/dist/harness/openclawHooks.d.ts +34 -0
  76. package/dist/harness/openclawHooks.d.ts.map +1 -0
  77. package/dist/harness/openclawHooks.js +296 -0
  78. package/dist/harness/openclawHooks.js.map +1 -0
  79. package/dist/harness/openhandsHooks.d.ts +24 -0
  80. package/dist/harness/openhandsHooks.d.ts.map +1 -0
  81. package/dist/harness/openhandsHooks.js +178 -0
  82. package/dist/harness/openhandsHooks.js.map +1 -0
  83. package/dist/harness/openhandsHooksWriter.d.ts +23 -0
  84. package/dist/harness/openhandsHooksWriter.d.ts.map +1 -0
  85. package/dist/harness/openhandsHooksWriter.js +146 -0
  86. package/dist/harness/openhandsHooksWriter.js.map +1 -0
  87. package/dist/harness/piHooks.d.ts +32 -0
  88. package/dist/harness/piHooks.d.ts.map +1 -0
  89. package/dist/harness/piHooks.js +257 -0
  90. package/dist/harness/piHooks.js.map +1 -0
  91. package/dist/harness/rooCodeHooks.d.ts +31 -0
  92. package/dist/harness/rooCodeHooks.d.ts.map +1 -0
  93. package/dist/harness/rooCodeHooks.js +300 -0
  94. package/dist/harness/rooCodeHooks.js.map +1 -0
  95. package/dist/harness/windsurfHooks.d.ts +27 -0
  96. package/dist/harness/windsurfHooks.d.ts.map +1 -0
  97. package/dist/harness/windsurfHooks.js +202 -0
  98. package/dist/harness/windsurfHooks.js.map +1 -0
  99. package/dist/hashReporter.d.ts +33 -0
  100. package/dist/hashReporter.d.ts.map +1 -0
  101. package/dist/hashReporter.js +69 -0
  102. package/dist/hashReporter.js.map +1 -0
  103. package/dist/index.d.ts +43 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +43 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/integrityStore.d.ts +43 -0
  108. package/dist/integrityStore.d.ts.map +1 -0
  109. package/dist/integrityStore.js +94 -0
  110. package/dist/integrityStore.js.map +1 -0
  111. package/dist/lib/complianceProbes.d.ts +39 -0
  112. package/dist/lib/complianceProbes.d.ts.map +1 -0
  113. package/dist/lib/complianceProbes.js +115 -0
  114. package/dist/lib/complianceProbes.js.map +1 -0
  115. package/dist/lib/processPoller.d.ts +2 -0
  116. package/dist/lib/processPoller.d.ts.map +1 -0
  117. package/dist/lib/processPoller.js +87 -0
  118. package/dist/lib/processPoller.js.map +1 -0
  119. package/dist/lib/runtimeEnv.d.ts +49 -0
  120. package/dist/lib/runtimeEnv.d.ts.map +1 -0
  121. package/dist/lib/runtimeEnv.js +70 -0
  122. package/dist/lib/runtimeEnv.js.map +1 -0
  123. package/dist/skillWriter.d.ts +24 -0
  124. package/dist/skillWriter.d.ts.map +1 -0
  125. package/dist/skillWriter.js +112 -0
  126. package/dist/skillWriter.js.map +1 -0
  127. package/dist/syncLoop.d.ts +70 -0
  128. package/dist/syncLoop.d.ts.map +1 -0
  129. package/dist/syncLoop.js +546 -0
  130. package/dist/syncLoop.js.map +1 -0
  131. package/dist/trajectoryMonitor.d.ts +126 -0
  132. package/dist/trajectoryMonitor.d.ts.map +1 -0
  133. package/dist/trajectoryMonitor.js +291 -0
  134. package/dist/trajectoryMonitor.js.map +1 -0
  135. package/dist/watcher/driftWatcher.d.ts +25 -0
  136. package/dist/watcher/driftWatcher.d.ts.map +1 -0
  137. package/dist/watcher/driftWatcher.js +72 -0
  138. package/dist/watcher/driftWatcher.js.map +1 -0
  139. package/dist/watcher/settingsGuard.d.ts +32 -0
  140. package/dist/watcher/settingsGuard.d.ts.map +1 -0
  141. package/dist/watcher/settingsGuard.js +250 -0
  142. package/dist/watcher/settingsGuard.js.map +1 -0
  143. package/dist/wsClient.d.ts +49 -0
  144. package/dist/wsClient.d.ts.map +1 -0
  145. package/dist/wsClient.js +149 -0
  146. package/dist/wsClient.js.map +1 -0
  147. package/package.json +96 -0
@@ -0,0 +1,414 @@
1
+ /**
2
+ * claudeCodeHooks.ts — Claude Code PreToolUse hook generator.
3
+ *
4
+ * Compiles SOP evaluation criteria and high risk tools into native Claude Code
5
+ * settings.json configurations (both permissions.deny rules and PreToolUse hooks).
6
+ * Writes configurations locally to the workspace and globally to ~/.claude/.
7
+ *
8
+ * LLD #14 — claudeCodeHooks.ts
9
+ * HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
10
+ *
11
+ * @module
12
+ */
13
+ import * as node_fs from 'node:fs/promises';
14
+ import * as node_path from 'node:path';
15
+ import * as node_os from 'node:os';
16
+ import { z } from 'zod';
17
+ import { createLogger } from '@intutic/logger';
18
+ const log = createLogger('sync-claude-hooks');
19
+ /** Path to the local hook event log file appended by generated hook scripts. */
20
+ export const HOOK_EVENTS_LOG = '.intutic/events/hook-events.jsonl';
21
+ // Zod schema to parse SOP evaluation criteria and tool restrictions
22
+ export const SopHookConstraintsSchema = z.object({
23
+ highRiskTools: z.array(z.string()).default([]),
24
+ patterns: z.array(z.string()).default([]),
25
+ });
26
+ /**
27
+ * Extracts constraints from SOP registry entries.
28
+ * Looks inside SOP markdown content for structured JSON/YAML blocks or parses settings.
29
+ */
30
+ export function parseSopConstraints(sops, settings) {
31
+ const highRiskToolsSet = new Set();
32
+ const patternsSet = new Set();
33
+ // 1. Parse from workspace settings if present
34
+ // Treat settings as Record<string, unknown> for legacy field access (highRiskTools, patterns)
35
+ if (settings) {
36
+ const s = settings;
37
+ if (Array.isArray(s['highRiskTools'])) {
38
+ s['highRiskTools'].forEach((t) => typeof t === 'string' && highRiskToolsSet.add(t));
39
+ }
40
+ if (Array.isArray(s['patterns'])) {
41
+ s['patterns'].forEach((p) => typeof p === 'string' && patternsSet.add(p));
42
+ }
43
+ }
44
+ // 2. Parse from SOP content
45
+ for (const sop of sops) {
46
+ const content = sop.content;
47
+ const startIdx = content.indexOf('```json');
48
+ if (startIdx !== -1) {
49
+ const jsonStart = content.indexOf('\n', startIdx);
50
+ const endIdx = content.indexOf('```', jsonStart !== -1 ? jsonStart : startIdx + 7);
51
+ if (jsonStart !== -1 && endIdx !== -1 && endIdx > jsonStart) {
52
+ const jsonText = content.slice(jsonStart + 1, endIdx).trim();
53
+ try {
54
+ const parsed = JSON.parse(jsonText);
55
+ const validated = SopHookConstraintsSchema.safeParse(parsed);
56
+ if (validated.success) {
57
+ validated.data.highRiskTools.forEach((t) => highRiskToolsSet.add(t));
58
+ validated.data.patterns.forEach((p) => patternsSet.add(p));
59
+ }
60
+ }
61
+ catch {
62
+ // Ignore parsing failures
63
+ }
64
+ }
65
+ }
66
+ // Also look for inline patterns
67
+ // e.g., "Blacklist Pattern: rm -rf"
68
+ const patternRegexes = [/Blacklist Pattern:\s*`?([^`\n]+)`?/gi, /Deny Pattern:\s*`?([^`\n]+)`?/gi];
69
+ for (const regex of patternRegexes) {
70
+ let match;
71
+ while ((match = regex.exec(content)) !== null) {
72
+ if (match[1]) {
73
+ patternsSet.add(match[1].trim());
74
+ }
75
+ }
76
+ }
77
+ // e.g., "High Risk Tool: Bash"
78
+ const toolRegex = /High Risk Tool:\s*`?([^`\n]+)`?/gi;
79
+ let toolMatch;
80
+ while ((toolMatch = toolRegex.exec(content)) !== null) {
81
+ if (toolMatch[1]) {
82
+ highRiskToolsSet.add(toolMatch[1].trim());
83
+ }
84
+ }
85
+ }
86
+ return {
87
+ highRiskTools: Array.from(highRiskToolsSet),
88
+ patterns: Array.from(patternsSet),
89
+ };
90
+ }
91
+ /**
92
+ * Compiles constraints into Claude Code settings.json format and writes it.
93
+ *
94
+ * @param workspaceRoot - Workspace root path.
95
+ * @param sops - The sync SOP list.
96
+ * @param settings - Passthrough settings.
97
+ * @param controlPlaneUrl - Control plane base URL embedded into the hook events log path comment.
98
+ * @param workspaceId - Workspace ID embedded into hook event payloads.
99
+ * @param harnessType - Harness type tag written into each hook event.
100
+ */
101
+ export async function updatePreToolUseHooks(workspaceRoot, sops, settings, controlPlaneUrl, workspaceId, harnessType = 'claude-code') {
102
+ const constraints = parseSopConstraints(sops, settings);
103
+ log.info({ action: 'update_hooks', toolCount: constraints.highRiskTools.length, patternCount: constraints.patterns.length }, 'Compiling PreToolUse hook configurations for Claude Code');
104
+ // Build the permissions.deny rules and hooks structure
105
+ const denyRules = [];
106
+ // High risk tools deny rules
107
+ for (const tool of constraints.highRiskTools) {
108
+ denyRules.push(tool);
109
+ }
110
+ // Pattern-based deny rules (e.g. Bash(rm -rf *))
111
+ for (const pattern of constraints.patterns) {
112
+ denyRules.push(`Bash(*${pattern}*)`);
113
+ }
114
+ // Build settings object matching Claude Code schema.
115
+ // We hook Bash, Edit, Write, and MultiEdit so that the policy gate
116
+ // covers file-modification tools in addition to shell execution.
117
+ const hookEntry = (matcher) => ({
118
+ matcher,
119
+ hooks: [
120
+ {
121
+ type: 'command',
122
+ command: `node ${node_path.join(workspaceRoot, '.intutic', 'hooks', 'pre-tool-check.js')}`,
123
+ timeout: 10,
124
+ statusMessage: 'Verifying tool execution against Intutic SOP policy...',
125
+ },
126
+ ],
127
+ });
128
+ const newSettings = {
129
+ permissions: {
130
+ deny: denyRules,
131
+ },
132
+ hooks: {
133
+ PreToolUse: [
134
+ hookEntry('Bash'),
135
+ hookEntry('Edit'),
136
+ hookEntry('Write'),
137
+ hookEntry('MultiEdit'),
138
+ ],
139
+ },
140
+ };
141
+ // Create hook script locally in workspace root (.intutic/hooks/pre-tool-check.js)
142
+ const hookScriptDir = node_path.join(workspaceRoot, '.intutic', 'hooks');
143
+ await node_fs.mkdir(hookScriptDir, { recursive: true });
144
+ // Ensure hook events log directory exists
145
+ const hookEventsDir = node_path.join(workspaceRoot, '.intutic', 'events');
146
+ await node_fs.mkdir(hookEventsDir, { recursive: true });
147
+ const hookEventsLog = node_path.join(hookEventsDir, 'hook-events.jsonl');
148
+ const hookScriptContent = `
149
+ /**
150
+ * Intutic PreToolUse execution gate.
151
+ * Auto-generated by intutic sync-daemon. DO NOT EDIT.
152
+ *
153
+ * Governance events are appended to .intutic/events/hook-events.jsonl
154
+ * and drained to the control plane by the sync-daemon on each cycle.
155
+ * Control plane: ${controlPlaneUrl ?? 'https://api.intutic.ai'}
156
+ */
157
+ const path = require('path');
158
+ const os = require('os');
159
+ const fs = require('fs');
160
+ const crypto = require('crypto');
161
+ const https = require('https');
162
+
163
+ // ── Runtime credentials (sourced from ~/.intutic/env/runtime.env at invocation time) ──
164
+ const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
165
+ let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId ?? '')};
166
+ try {
167
+ fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
168
+ const eq = line.indexOf('='); if (eq < 0) return;
169
+ const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
170
+ if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
171
+ if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
172
+ if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
173
+ });
174
+ } catch { /* runtime.env not yet written — use defaults */ }
175
+
176
+ // Governance-sensitive paths that no agent may modify.
177
+ // Editing these files is how privilege escalation occurs.
178
+ const PROTECTED_PATHS = [
179
+ path.join(os.homedir(), '.claude', 'settings.json'),
180
+ path.join(os.homedir(), '.claude', 'settings.local.json'),
181
+ '.claude/settings.json',
182
+ '.claude/settings.local.json',
183
+ '.intutic/hooks',
184
+ '.intutic/integrity.json',
185
+ '.intutic/context_integrity.json',
186
+ ];
187
+
188
+ /**
189
+ * Appends a governance event to the local hook-events log.
190
+ * Uses synchronous append (O_APPEND is atomic for single-write < PIPE_BUF).
191
+ * The sync-daemon drains this file on each cycle and POSTs to the control plane.
192
+ */
193
+ function logEvent(verdict, toolName, reason) {
194
+ try {
195
+ const ts = new Date().toISOString();
196
+ const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
197
+ const entry = JSON.stringify({
198
+ event: verdict === 'blocked' ? 'tool_blocked' : 'tool_allowed',
199
+ toolName,
200
+ reason: reason || '',
201
+ workspaceId: _intuticWsId,
202
+ harnessType: ${JSON.stringify(harnessType)},
203
+ timestamp: ts,
204
+ incidentId,
205
+ }) + '\\n';
206
+ // Path B: reliable file append (sync-daemon drains on FSEvents change)
207
+ fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
208
+ // Path A: fire-and-forget HTTP POST (near-real-time dashboard, non-blocking)
209
+ if (_intuticKey) {
210
+ try {
211
+ const body = JSON.stringify({ events: [JSON.parse(entry)] });
212
+ const urlObj = new URL('/api/v1/hook-events', _intuticHost);
213
+ const isHttps = urlObj.protocol === 'https:';
214
+ const mod = isHttps ? https : require('http');
215
+ const req = mod.request({
216
+ hostname: urlObj.hostname,
217
+ port: urlObj.port || (isHttps ? 443 : 80),
218
+ path: urlObj.pathname,
219
+ method: 'POST',
220
+ headers: {
221
+ 'Content-Type': 'application/json',
222
+ 'Content-Length': Buffer.byteLength(body),
223
+ 'Authorization': 'Bearer ' + _intuticKey,
224
+ },
225
+ });
226
+ req.on('error', () => { /* fire-and-forget — ignore errors */ });
227
+ req.write(body);
228
+ req.end();
229
+ } catch { /* never crash the hook */ }
230
+ }
231
+ } catch { /* never crash the hook */ }
232
+ }
233
+
234
+ // Read stdin containing Claude's tool context
235
+ let inputData = '';
236
+ process.stdin.on('data', (chunk) => { inputData += chunk; });
237
+ process.stdin.on('end', () => {
238
+ try {
239
+ const ctx = JSON.parse(inputData);
240
+ const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase();
241
+ const toolInput = ctx.tool_input || ctx.toolInput || {};
242
+ const toolInputStr = JSON.stringify(toolInput);
243
+
244
+ // 1. Protected-path guard — block any file-modification tool targeting
245
+ // governance-sensitive paths regardless of SOP pattern list.
246
+ if (['edit', 'write', 'multiedit'].includes(toolName)) {
247
+ const targetPath = toolInput.path || toolInput.file_path ||
248
+ toolInput.new_path || toolInput.target || '';
249
+ for (const p of PROTECTED_PATHS) {
250
+ if (targetPath.includes(p) || String(targetPath).startsWith(p)) {
251
+ const reason = \`Attempt to modify governance-protected path: "\${targetPath}". \` +
252
+ 'To change governance policy, update your SOP via the Intutic control plane.';
253
+ console.error(\`[Intutic Guardrail] BLOCKED: \${reason}\`);
254
+ logEvent('blocked', toolName, reason);
255
+ process.exit(2);
256
+ }
257
+ }
258
+ }
259
+
260
+ // 2. SOP-compiled pattern blacklist
261
+ const patterns = ${JSON.stringify(constraints.patterns)};
262
+ for (const pattern of patterns) {
263
+ if (toolInputStr.includes(pattern)) {
264
+ const reason = \`Policy violation matching pattern: "\${pattern}"\`;
265
+ console.error(\`[Intutic Guardrail] Blocked execution of tool "\${toolName}" due to \${reason}\`);
266
+ logEvent('blocked', toolName, reason);
267
+ process.exit(2);
268
+ }
269
+ }
270
+
271
+ logEvent('allowed', toolName, '');
272
+ process.exit(0);
273
+ } catch (err) {
274
+ // Fail CLOSED — any hook execution error blocks the tool call.
275
+ // The sync daemon will rewrite this hook on the next sync cycle.
276
+ console.error('[Intutic Guardrail] Hook error (blocking for safety):', err);
277
+ logEvent('blocked', 'unknown', String(err));
278
+ process.exit(2);
279
+ }
280
+ });
281
+ `.trim();
282
+ await node_fs.writeFile(node_path.join(hookScriptDir, 'pre-tool-check.js'), hookScriptContent, 'utf-8');
283
+ // Write local settings config
284
+ const localClaudeDir = node_path.join(workspaceRoot, '.claude');
285
+ await node_fs.mkdir(localClaudeDir, { recursive: true });
286
+ const localSettingsPath = node_path.join(localClaudeDir, 'settings.json');
287
+ let existingLocal = {};
288
+ try {
289
+ const raw = await node_fs.readFile(localSettingsPath, 'utf-8');
290
+ existingLocal = JSON.parse(raw);
291
+ }
292
+ catch {
293
+ // Ignore
294
+ }
295
+ const mergedLocal = {
296
+ ...existingLocal,
297
+ permissions: {
298
+ ...existingLocal.permissions,
299
+ deny: denyRules,
300
+ },
301
+ hooks: {
302
+ ...existingLocal.hooks,
303
+ PreToolUse: newSettings.hooks.PreToolUse,
304
+ },
305
+ };
306
+ await node_fs.writeFile(localSettingsPath, JSON.stringify(mergedLocal, null, 2) + '\n', 'utf-8');
307
+ // Write global settings config ~/.claude/settings.json
308
+ //
309
+ // GUARD (bugfix): never touch the user's real global settings from a test
310
+ // run. Vitest sets VITEST=1; without this guard, every run of
311
+ // __tests__/harness/claudeCodeHooks.test.ts merged the MOCK SOP denies
312
+ // (Bash/Write/rm -rf/…) into the developer's real ~/.claude/settings.json
313
+ // and pointed global hooks at a mkdtemp dir the test then deleted —
314
+ // leaving dead fail-closed hooks and a denied Bash/Write toolset.
315
+ // INTUTIC_SKIP_GLOBAL_HOOKS=1 is an explicit opt-out for dev machines.
316
+ if (process.env['VITEST'] || process.env['INTUTIC_SKIP_GLOBAL_HOOKS']) {
317
+ log.info({ action: 'global_hooks_skipped' }, 'Skipping global ~/.claude settings write (test or opt-out environment)');
318
+ return;
319
+ }
320
+ const globalClaudeDir = node_path.join(node_os.homedir(), '.claude');
321
+ await node_fs.mkdir(globalClaudeDir, { recursive: true });
322
+ const globalSettingsPath = node_path.join(globalClaudeDir, 'settings.json');
323
+ let existingGlobal = {};
324
+ try {
325
+ const raw = await node_fs.readFile(globalSettingsPath, 'utf-8');
326
+ existingGlobal = JSON.parse(raw);
327
+ }
328
+ catch {
329
+ // Ignore
330
+ }
331
+ // Merge, prioritizing global user settings but updating permissions/hooks
332
+ const mergedGlobal = {
333
+ ...existingGlobal,
334
+ permissions: {
335
+ ...existingGlobal.permissions,
336
+ deny: Array.from(new Set([...(existingGlobal.permissions?.deny || []), ...denyRules])),
337
+ },
338
+ hooks: {
339
+ ...existingGlobal.hooks,
340
+ PreToolUse: newSettings.hooks.PreToolUse,
341
+ },
342
+ };
343
+ await node_fs.writeFile(globalSettingsPath, JSON.stringify(mergedGlobal, null, 2) + '\n', 'utf-8');
344
+ log.info({ action: 'hooks_written' }, 'Successfully updated settings.json hooks globally and locally');
345
+ }
346
+ // ─── Event Drain Helper ──────────────────────────────────────────────────────
347
+ /**
348
+ * Drains the local hook-events log file and POSTs all accumulated governance
349
+ * events to the control plane in a single batch request.
350
+ *
351
+ * Called by the sync-daemon's main loop (syncLoop.ts) on every cycle, after
352
+ * the main sync operations. On success, the log file is truncated to prevent
353
+ * unbounded growth. On network failure, events remain in the log and will be
354
+ * retried on the next cycle.
355
+ *
356
+ * @param workspaceRoot - Workspace root (log file is at workspaceRoot/.intutic/events/hook-events.jsonl)
357
+ * @param controlPlaneUrl - Control plane base URL
358
+ * @param apiKey - Workspace API key (Bearer token)
359
+ * @returns number of events drained (0 if log was empty or network failed)
360
+ */
361
+ export async function drainHookEvents(workspaceRoot, controlPlaneUrl, apiKey) {
362
+ const logPath = node_path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
363
+ let raw;
364
+ try {
365
+ raw = await node_fs.readFile(logPath, 'utf-8');
366
+ }
367
+ catch {
368
+ return 0; // File doesn't exist yet — nothing to drain
369
+ }
370
+ const lines = raw.trim().split('\n').filter(Boolean);
371
+ if (lines.length === 0)
372
+ return 0;
373
+ const events = [];
374
+ for (const line of lines) {
375
+ try {
376
+ events.push(JSON.parse(line));
377
+ }
378
+ catch {
379
+ log.warn({ line }, 'Skipping malformed hook event log line');
380
+ }
381
+ }
382
+ if (events.length === 0) {
383
+ await node_fs.writeFile(logPath, '', 'utf-8');
384
+ return 0;
385
+ }
386
+ try {
387
+ // Use native fetch (Node 18+ — required minimum for this monorepo)
388
+ const response = await fetch(`${controlPlaneUrl}/api/v1/hook-events`, {
389
+ method: 'POST',
390
+ headers: {
391
+ 'Content-Type': 'application/json',
392
+ 'Authorization': `Bearer ${apiKey}`,
393
+ },
394
+ body: JSON.stringify({ events }),
395
+ signal: AbortSignal.timeout(10_000),
396
+ });
397
+ if (response.ok) {
398
+ // Truncate the log — events successfully delivered
399
+ await node_fs.writeFile(logPath, '', 'utf-8');
400
+ log.info({ count: events.length }, 'Hook events drained to control plane');
401
+ return events.length;
402
+ }
403
+ else {
404
+ log.warn({ status: response.status, count: events.length }, 'Control plane rejected hook events — retaining log for next cycle');
405
+ return 0;
406
+ }
407
+ }
408
+ catch (err) {
409
+ // Network failure — retain log for retry on next cycle
410
+ log.warn({ err, count: events.length }, 'Failed to drain hook events — will retry on next sync cycle');
411
+ return 0;
412
+ }
413
+ }
414
+ //# sourceMappingURL=claudeCodeHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudeCodeHooks.js","sourceRoot":"","sources":["../../src/harness/claudeCodeHooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
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
+ /**
19
+ * Write the Claude Desktop governance hook script and merge hook configuration
20
+ * into the OS-specific claude_desktop_config.json.
21
+ *
22
+ * Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
23
+ * Does NOT touch the `mcpServers` section of the config.
24
+ *
25
+ * @param workspaceRoot - Absolute workspace root path.
26
+ * @param proxyUrl - Intutic proxy URL written into the hook script header.
27
+ * @param workspaceId - Workspace ID embedded in every hook event payload.
28
+ */
29
+ export declare function writeClaudeDesktopHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
30
+ //# sourceMappingURL=claudeDesktopHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudeDesktopHooks.d.ts","sourceRoot":"","sources":["../../src/harness/claudeDesktopHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAqMH;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAsEf"}