@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,27 @@
1
+ /**
2
+ * cursorHooks.ts — Cursor beforeShellExecution/beforeMCPExecution hook injection.
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
8
+ *
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.
11
+ *
12
+ * LLD #14 — Phase 3 cross-harness defence
13
+ * HLD §3.14 — Three-Tier Defense Cascade
14
+ *
15
+ * @module
16
+ */
17
+ /**
18
+ * Write Cursor hooks at user-level and project-level.
19
+ * System-level (/etc/cursor) is handled by the corporate policy installer.
20
+ *
21
+ * @param workspaceRoot - Absolute workspace root.
22
+ * @param proxyUrl - Intutic proxy URL.
23
+ * @param workspaceId - Workspace ID embedded in hook event payloads.
24
+ * @param writeSystemLevel - If true, also attempt /etc/cursor (requires root).
25
+ */
26
+ export declare function writeCursorHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string, writeSystemLevel?: boolean): Promise<void>;
27
+ //# sourceMappingURL=cursorHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorHooks.d.ts","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAqKH;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,EAChB,gBAAgB,UAAQ,GACvB,OAAO,CAAC,IAAI,CAAC,CAwCf"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * cursorHooks.ts — Cursor beforeShellExecution/beforeMCPExecution hook injection.
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
8
+ *
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.
11
+ *
12
+ * LLD #14 — Phase 3 cross-harness defence
13
+ * HLD §3.14 — Three-Tier Defense Cascade
14
+ *
15
+ * @module
16
+ */
17
+ import * as fs from 'node:fs/promises';
18
+ import * as path from 'node:path';
19
+ import * as os from 'node:os';
20
+ import { createLogger } from '@intutic/logger';
21
+ import { newIso } from '@intutic/id';
22
+ const log = createLogger('sync-cursor-hooks');
23
+ /** Hook events Cursor fires before execution. */
24
+ const HOOK_EVENTS = [
25
+ 'beforeShellExecution',
26
+ 'beforeMCPExecution',
27
+ 'beforeFileEdit',
28
+ ];
29
+ /**
30
+ * Build a Cursor hooks.json config object.
31
+ *
32
+ * @param hookScriptPath - Absolute path to the pre-tool-check script.
33
+ */
34
+ function buildHooksConfig(hookScriptPath) {
35
+ return {
36
+ _comment: 'Intutic governance hooks — auto-generated. DO NOT EDIT.',
37
+ _lastSync: newIso(),
38
+ failClosed: true,
39
+ hooks: Object.fromEntries(HOOK_EVENTS.map((event) => [
40
+ event,
41
+ {
42
+ command: `node "${hookScriptPath}"`,
43
+ failClosed: true,
44
+ },
45
+ ])),
46
+ };
47
+ }
48
+ /**
49
+ * Governance-sensitive paths — mirrors the list in claudeCodeHooks.ts.
50
+ * Any write/edit targeting these paths is blocked.
51
+ */
52
+ const PROTECTED_PATHS = [
53
+ '.cursor/hooks.json',
54
+ '.claude/settings.json',
55
+ '.cline/hooks',
56
+ '.intutic/hooks',
57
+ '.intutic/integrity.json',
58
+ '.codeium/windsurf/hooks.json',
59
+ '.agents/plugins/intutic-governance',
60
+ '.openhands/hooks.json',
61
+ ];
62
+ /**
63
+ * The pre-tool-check.js script content — receives JSON context from Cursor
64
+ * on stdin, exits 0 to allow or exits 2 to block.
65
+ */
66
+ function buildHookScript(proxyUrl, workspaceRoot, workspaceId) {
67
+ const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
68
+ return `#!/usr/bin/env node
69
+ /**
70
+ * Intutic Cursor governance gate.
71
+ * Auto-generated by intutic sync-daemon. DO NOT EDIT.
72
+ * Proxy: ${proxyUrl}
73
+ * Generated: ${newIso()}
74
+ */
75
+ const path = require('path');
76
+ const os = require('os');
77
+ const fs = require('fs');
78
+ const crypto = require('crypto');
79
+ const https = require('https');
80
+
81
+ // Runtime credentials
82
+ const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
83
+ let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId)};
84
+ try {
85
+ fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
86
+ const eq = line.indexOf('='); if (eq < 0) return;
87
+ const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
88
+ if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
89
+ if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
90
+ if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
91
+ });
92
+ } catch {}
93
+
94
+ const PROTECTED_PATHS = ${JSON.stringify(PROTECTED_PATHS)};
95
+
96
+ function logEvent(verdict, toolName, reason) {
97
+ try {
98
+ const ts = new Date().toISOString();
99
+ const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
100
+ const entry = JSON.stringify({
101
+ event: verdict === 'blocked' ? 'tool_blocked' : 'tool_allowed',
102
+ toolName, reason: reason || '',
103
+ workspaceId: _intuticWsId,
104
+ harnessType: 'cursor',
105
+ timestamp: ts,
106
+ incidentId,
107
+ }) + '\\n';
108
+ fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
109
+ if (_intuticKey) {
110
+ try {
111
+ const body = JSON.stringify({ events: [JSON.parse(entry)] });
112
+ const urlObj = new URL('/api/v1/hook-events', _intuticHost);
113
+ const mod = urlObj.protocol === 'https:' ? https : require('http');
114
+ 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 } });
115
+ req.on('error', () => { /* fire-and-forget */ });
116
+ req.write(body); req.end();
117
+ } catch { /* never crash the hook */ }
118
+ }
119
+ } catch { /* never crash the hook */ }
120
+ }
121
+
122
+ let raw = '';
123
+ process.stdin.on('data', (c) => { raw += c; });
124
+ process.stdin.on('end', () => {
125
+ try {
126
+ const ctx = JSON.parse(raw);
127
+ const event = (ctx.event || '').toLowerCase();
128
+ const input = ctx.input || ctx.tool_input || ctx;
129
+
130
+ // For file edits: check target path
131
+ const targetPath = input.path || input.file || input.target || '';
132
+ if (event === 'beforefileedit' || (input.path && input.path.length > 0)) {
133
+ for (const p of PROTECTED_PATHS) {
134
+ if (String(targetPath).includes(p)) {
135
+ const reason = 'Attempt to modify governance-protected path "' + targetPath + '"';
136
+ process.stderr.write('[Intutic Governance] BLOCKED: ' + reason + '\\n');
137
+ logEvent('blocked', 'edit', reason);
138
+ process.exit(2);
139
+ }
140
+ }
141
+ }
142
+
143
+ // Shell execution: scan command for dangerous patterns
144
+ const command = input.command || input.cmd || '';
145
+ const dangerPatterns = [
146
+ /chmod\\s+[0-9]*7[0-9]*\\s+.*hooks/, // chmod on hook files
147
+ /chattr\\s+-i\\s+/, // remove immutable flag
148
+ /chflags\\s+nouchg/, // macOS remove uchg
149
+ ];
150
+ for (const pat of dangerPatterns) {
151
+ if (pat.test(command)) {
152
+ const reason = 'Command matches governance bypass pattern: "' + command + '"';
153
+ process.stderr.write('[Intutic Governance] BLOCKED: ' + reason + '\\n');
154
+ logEvent('blocked', 'bash', reason);
155
+ process.exit(2);
156
+ }
157
+ }
158
+
159
+ // Allow
160
+ logEvent('allowed', event || 'tool', '');
161
+ process.exit(0);
162
+ } catch (err) {
163
+ // Fail CLOSED
164
+ const errMsg = String(err);
165
+ process.stderr.write('[Intutic Governance] Hook error (fail-closed): ' + errMsg + '\\n');
166
+ logEvent('blocked', 'unknown', errMsg);
167
+ process.exit(2);
168
+ }
169
+ });
170
+ `;
171
+ }
172
+ /**
173
+ * Write Cursor hooks at user-level and project-level.
174
+ * System-level (/etc/cursor) is handled by the corporate policy installer.
175
+ *
176
+ * @param workspaceRoot - Absolute workspace root.
177
+ * @param proxyUrl - Intutic proxy URL.
178
+ * @param workspaceId - Workspace ID embedded in hook event payloads.
179
+ * @param writeSystemLevel - If true, also attempt /etc/cursor (requires root).
180
+ */
181
+ export async function writeCursorHooks(workspaceRoot, proxyUrl, workspaceId = '', writeSystemLevel = false) {
182
+ const hookScriptDir = path.join(workspaceRoot, '.intutic', 'hooks');
183
+ await fs.mkdir(hookScriptDir, { recursive: true });
184
+ // Ensure hook events log directory exists
185
+ await fs.mkdir(path.join(workspaceRoot, '.intutic', 'events'), { recursive: true });
186
+ const hookScriptPath = path.join(hookScriptDir, 'pre-tool-check.js');
187
+ // Write the hook script
188
+ const tmpScript = hookScriptPath + '.cursor-tmp';
189
+ await fs.writeFile(tmpScript, buildHookScript(proxyUrl, workspaceRoot, workspaceId), 'utf-8');
190
+ await fs.rename(tmpScript, hookScriptPath);
191
+ await fs.chmod(hookScriptPath, 0o755);
192
+ const config = buildHooksConfig(hookScriptPath);
193
+ // 1. Project-level: .cursor/hooks.json
194
+ const projectCursorDir = path.join(workspaceRoot, '.cursor');
195
+ await fs.mkdir(projectCursorDir, { recursive: true });
196
+ await atomicWriteJson(path.join(projectCursorDir, 'hooks.json'), config);
197
+ log.info({ action: 'cursor_hooks_written', level: 'project', path: projectCursorDir }, 'Cursor project-level hooks written');
198
+ // 2. User-level: ~/.cursor/hooks.json
199
+ const userCursorDir = path.join(os.homedir(), '.cursor');
200
+ await fs.mkdir(userCursorDir, { recursive: true });
201
+ await atomicWriteJson(path.join(userCursorDir, 'hooks.json'), config);
202
+ log.info({ action: 'cursor_hooks_written', level: 'user', path: userCursorDir }, 'Cursor user-level hooks written');
203
+ // 3. System-level: /etc/cursor/hooks.json (system administrator installation only)
204
+ if (writeSystemLevel) {
205
+ const systemCursorDir = '/etc/cursor';
206
+ try {
207
+ await fs.mkdir(systemCursorDir, { recursive: true });
208
+ await atomicWriteJson(path.join(systemCursorDir, 'hooks.json'), config);
209
+ log.info({ action: 'cursor_hooks_written', level: 'system', path: systemCursorDir }, 'Cursor system-level hooks written');
210
+ }
211
+ catch (err) {
212
+ log.error({ action: 'cursor_system_hooks_failed', err }, 'System-level Cursor hooks require root — skipping');
213
+ }
214
+ }
215
+ }
216
+ async function atomicWriteJson(filePath, data) {
217
+ const tmp = filePath + '.intutic-tmp';
218
+ await fs.writeFile(tmp, JSON.stringify(data, null, 2) + '\n', 'utf-8');
219
+ await fs.rename(tmp, filePath);
220
+ }
221
+ //# sourceMappingURL=cursorHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorHooks.js","sourceRoot":"","sources":["../../src/harness/cursorHooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * cursorHooksJson.ts — Cursor native hooks.json writer (v1.7+ hook API).
3
+ *
4
+ * Writes Cursor's native hooks.json at two levels:
5
+ * 1. User-level: ~/.cursor/hooks.json
6
+ * 2. Project-level: <workspaceRoot>/.cursor/hooks.json
7
+ *
8
+ * The hook command references the shared pre-tool-check.js script installed
9
+ * by WS-4A. On beforeMCPExecution and beforeShellExecution events, Cursor
10
+ * invokes this script with the tool context on stdin; exit 0 allows,
11
+ * exit 2 blocks (fail-closed).
12
+ *
13
+ * Deep-merges into any existing hooks.json so other hooks are preserved.
14
+ *
15
+ * LLD #14 — Phase 4 WS-4C native hook script writers
16
+ * HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
17
+ *
18
+ * @module
19
+ */
20
+ /**
21
+ * Write Cursor native hooks.json at user-level and project-level.
22
+ *
23
+ * The hook command (`node ~/.intutic/hooks/pre-tool-check.js`) is installed
24
+ * by WS-4A; this writer only registers it in hooks.json.
25
+ *
26
+ * @param workspaceRoot - Absolute path to the workspace root.
27
+ */
28
+ export declare function writeCursorHooksJson(workspaceRoot: string): Promise<void>;
29
+ //# sourceMappingURL=cursorHooksJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorHooksJson.d.ts","sourceRoot":"","sources":["../../src/harness/cursorHooksJson.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AA+GH;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB/E"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * cursorHooksJson.ts — Cursor native hooks.json writer (v1.7+ hook API).
3
+ *
4
+ * Writes Cursor's native hooks.json at two levels:
5
+ * 1. User-level: ~/.cursor/hooks.json
6
+ * 2. Project-level: <workspaceRoot>/.cursor/hooks.json
7
+ *
8
+ * The hook command references the shared pre-tool-check.js script installed
9
+ * by WS-4A. On beforeMCPExecution and beforeShellExecution events, Cursor
10
+ * invokes this script with the tool context on stdin; exit 0 allows,
11
+ * exit 2 blocks (fail-closed).
12
+ *
13
+ * Deep-merges into any existing hooks.json so other hooks are preserved.
14
+ *
15
+ * LLD #14 — Phase 4 WS-4C native hook script writers
16
+ * HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
17
+ *
18
+ * @module
19
+ */
20
+ import * as node_fs from 'node:fs/promises';
21
+ import * as node_path from 'node:path';
22
+ import * as node_os from 'node:os';
23
+ import { createLogger } from '@intutic/logger';
24
+ const log = createLogger('sync-cursor-hooks-json');
25
+ /** Absolute path to the shared pre-tool-check.js installed by WS-4A. */
26
+ const HOOK_SCRIPT = node_path.join(node_os.homedir(), '.intutic', 'hooks', 'pre-tool-check.js');
27
+ /** Hook events covered by this writer (Cursor v1.7+ API). */
28
+ const HOOK_EVENTS = ['beforeMCPExecution', 'beforeShellExecution'];
29
+ /**
30
+ * Build the Intutic hook entry for a given event.
31
+ *
32
+ * @param hookScriptPath - Absolute path to the pre-tool-check.js script.
33
+ */
34
+ function buildHookEntry(hookScriptPath) {
35
+ return { command: `node ${hookScriptPath}` };
36
+ }
37
+ /**
38
+ * Read and parse an existing hooks.json, returning a default document if
39
+ * the file does not exist or is not valid JSON.
40
+ */
41
+ async function readExistingHooksJson(filePath) {
42
+ try {
43
+ const raw = await node_fs.readFile(filePath, 'utf-8');
44
+ const parsed = JSON.parse(raw);
45
+ if (parsed !== null &&
46
+ typeof parsed === 'object' &&
47
+ !Array.isArray(parsed) &&
48
+ 'hooks' in parsed &&
49
+ typeof parsed.hooks === 'object') {
50
+ return parsed;
51
+ }
52
+ }
53
+ catch {
54
+ // File absent or malformed — start from scratch
55
+ }
56
+ return { version: 1, hooks: {} };
57
+ }
58
+ /**
59
+ * Deep-merge our Intutic hook entries into an existing hooks map.
60
+ *
61
+ * For each covered event, we append our entry if a hook with the same
62
+ * command is not already present, preserving all other hooks.
63
+ *
64
+ * @param existing - The existing hooks map (mutated in place).
65
+ * @param hookScriptPath - Absolute path to the governance hook script.
66
+ * @returns The merged hooks map.
67
+ */
68
+ function mergeHooks(existing, hookScriptPath) {
69
+ const entry = buildHookEntry(hookScriptPath);
70
+ for (const event of HOOK_EVENTS) {
71
+ const current = Array.isArray(existing[event])
72
+ ? existing[event]
73
+ : [];
74
+ const alreadyPresent = current.some((h) => h.command === entry.command);
75
+ if (!alreadyPresent) {
76
+ existing[event] = [...current, entry];
77
+ }
78
+ else {
79
+ existing[event] = current;
80
+ }
81
+ }
82
+ return existing;
83
+ }
84
+ /**
85
+ * Write hooks.json atomically (tmp → rename) to the given path,
86
+ * deep-merging our entries into any existing configuration.
87
+ *
88
+ * @param filePath - Absolute path to the hooks.json to write.
89
+ * @param hookScriptPath - Absolute path to the governance hook script.
90
+ * @param level - Human-readable label for log context.
91
+ */
92
+ async function writeHooksJson(filePath, hookScriptPath, level) {
93
+ await node_fs.mkdir(node_path.dirname(filePath), { recursive: true });
94
+ const doc = await readExistingHooksJson(filePath);
95
+ doc.version = 1;
96
+ doc.hooks = mergeHooks(doc.hooks, hookScriptPath);
97
+ const tmp = filePath + '.intutic-tmp';
98
+ await node_fs.writeFile(tmp, JSON.stringify(doc, null, 2) + '\n', 'utf-8');
99
+ await node_fs.rename(tmp, filePath);
100
+ log.info({ action: 'cursor_hooks_json_written', level, path: filePath }, `Cursor hooks.json written (${level})`);
101
+ }
102
+ /**
103
+ * Write Cursor native hooks.json at user-level and project-level.
104
+ *
105
+ * The hook command (`node ~/.intutic/hooks/pre-tool-check.js`) is installed
106
+ * by WS-4A; this writer only registers it in hooks.json.
107
+ *
108
+ * @param workspaceRoot - Absolute path to the workspace root.
109
+ */
110
+ export async function writeCursorHooksJson(workspaceRoot) {
111
+ // 1. User-level: ~/.cursor/hooks.json
112
+ //
113
+ // GUARD (bugfix): never touch the user's real global Cursor config from a
114
+ // test run — the same failure already fixed for ~/.claude in
115
+ // claudeCodeHooks.ts. Vitest sets VITEST=1; without this guard every run of
116
+ // the sync-daemon suite rewrote the developer's real ~/.cursor/hooks.json
117
+ // and pointed it at a temp dir the test then deleted, leaving a dead hook.
118
+ // INTUTIC_SKIP_GLOBAL_HOOKS=1 is an explicit opt-out for dev machines.
119
+ if (process.env['VITEST'] || process.env['INTUTIC_SKIP_GLOBAL_HOOKS']) {
120
+ log.info({ action: 'cursor_user_hooks_skipped' }, 'Skipping global ~/.cursor/hooks.json write (test or opt-out environment)');
121
+ }
122
+ else {
123
+ const userHooksPath = node_path.join(node_os.homedir(), '.cursor', 'hooks.json');
124
+ await writeHooksJson(userHooksPath, HOOK_SCRIPT, 'user');
125
+ }
126
+ // 2. Project-level: <workspaceRoot>/.cursor/hooks.json
127
+ const projectHooksPath = node_path.join(workspaceRoot, '.cursor', 'hooks.json');
128
+ await writeHooksJson(projectHooksPath, HOOK_SCRIPT, 'project');
129
+ }
130
+ //# sourceMappingURL=cursorHooksJson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursorHooksJson.js","sourceRoot":"","sources":["../../src/harness/cursorHooksJson.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * gooseHardener.ts — Cross-platform immutable flag management for Goose plugin files.
3
+ *
4
+ * After the Goose governance plugin is written, this module applies:
5
+ * 1. chmod 444 — no write for owner, group, or others
6
+ * 2. OS-level immutable flag:
7
+ * macOS: chflags uchg (user immutable, does not require root)
8
+ * Linux: chattr +i (requires root; graceful fallback on permission error)
9
+ *
10
+ * The drift watcher checks for immutable status before auto-restoring.
11
+ * If a file is immutable but has been tampered with, a governance_override_attempt
12
+ * incident is emitted to the control plane instead of a silent restore.
13
+ *
14
+ * LLD #14 — Phase 3 cross-harness defence
15
+ * HLD §3.14 — Three-Tier Defense Cascade
16
+ *
17
+ * @module
18
+ */
19
+ /**
20
+ * Apply chmod 444 and OS immutable flags to all files in the
21
+ * Goose governance plugin directory.
22
+ *
23
+ * @param pluginDir - Absolute path to the plugin directory
24
+ * (e.g. ~/.agents/plugins/intutic-governance)
25
+ */
26
+ export declare function hardenGoosePlugin(pluginDir: string): Promise<void>;
27
+ /**
28
+ * Remove immutable flags from a file (needed before sync daemon can overwrite
29
+ * during a legitimate governance refresh).
30
+ *
31
+ * @param filePath - Absolute path to the file
32
+ */
33
+ export declare function unharden(filePath: string): Promise<void>;
34
+ /**
35
+ * Check whether a file has an OS immutable flag set.
36
+ * Used by the drift watcher to decide whether to restore or emit an incident.
37
+ */
38
+ export declare function isImmutable(filePath: string): Promise<boolean>;
39
+ //# sourceMappingURL=gooseHardener.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gooseHardener.d.ts","sourceRoot":"","sources":["../../src/harness/gooseHardener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAWH;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBxE;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB9D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBpE"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * gooseHardener.ts — Cross-platform immutable flag management for Goose plugin files.
3
+ *
4
+ * After the Goose governance plugin is written, this module applies:
5
+ * 1. chmod 444 — no write for owner, group, or others
6
+ * 2. OS-level immutable flag:
7
+ * macOS: chflags uchg (user immutable, does not require root)
8
+ * Linux: chattr +i (requires root; graceful fallback on permission error)
9
+ *
10
+ * The drift watcher checks for immutable status before auto-restoring.
11
+ * If a file is immutable but has been tampered with, a governance_override_attempt
12
+ * incident is emitted to the control plane instead of a silent restore.
13
+ *
14
+ * LLD #14 — Phase 3 cross-harness defence
15
+ * HLD §3.14 — Three-Tier Defense Cascade
16
+ *
17
+ * @module
18
+ */
19
+ import * as fs from 'node:fs/promises';
20
+ import * as path from 'node:path';
21
+ import { execFile } from 'node:child_process';
22
+ import { promisify } from 'node:util';
23
+ import { createLogger } from '@intutic/logger';
24
+ const log = createLogger('sync-goose-hardener');
25
+ const execFileAsync = promisify(execFile);
26
+ /**
27
+ * Apply chmod 444 and OS immutable flags to all files in the
28
+ * Goose governance plugin directory.
29
+ *
30
+ * @param pluginDir - Absolute path to the plugin directory
31
+ * (e.g. ~/.agents/plugins/intutic-governance)
32
+ */
33
+ export async function hardenGoosePlugin(pluginDir) {
34
+ const targets = [
35
+ path.join(pluginDir, 'hooks', 'hooks.json'),
36
+ path.join(pluginDir, 'scripts', 'intutic-check.sh'),
37
+ ];
38
+ for (const target of targets) {
39
+ try {
40
+ await fs.access(target);
41
+ }
42
+ catch {
43
+ log.warn({ action: 'harden_skip', target }, 'Goose plugin file not found — skipping hardening');
44
+ continue;
45
+ }
46
+ // Step 1: chmod 444
47
+ try {
48
+ await fs.chmod(target, 0o444);
49
+ log.debug({ action: 'chmod_444', target }, 'chmod 444 applied');
50
+ }
51
+ catch (err) {
52
+ log.error({ action: 'chmod_failed', target, err }, 'chmod 444 failed');
53
+ }
54
+ // Step 2: OS immutable flag
55
+ await applyImmutableFlag(target);
56
+ }
57
+ }
58
+ /**
59
+ * Remove immutable flags from a file (needed before sync daemon can overwrite
60
+ * during a legitimate governance refresh).
61
+ *
62
+ * @param filePath - Absolute path to the file
63
+ */
64
+ export async function unharden(filePath) {
65
+ if (process.platform === 'darwin') {
66
+ try {
67
+ await execFileAsync('chflags', ['nouchg', filePath]);
68
+ log.debug({ action: 'chflags_nouchg', filePath }, 'Removed macOS uchg flag');
69
+ }
70
+ catch (err) {
71
+ log.warn({ action: 'chflags_nouchg_failed', filePath, err }, 'chflags nouchg failed');
72
+ }
73
+ }
74
+ else if (process.platform === 'linux') {
75
+ try {
76
+ await execFileAsync('chattr', ['-i', filePath]);
77
+ log.debug({ action: 'chattr_minus_i', filePath }, 'Removed Linux immutable flag');
78
+ }
79
+ catch {
80
+ // Best-effort — may not have root
81
+ }
82
+ }
83
+ // Restore writable mode temporarily
84
+ try {
85
+ await fs.chmod(filePath, 0o644);
86
+ }
87
+ catch { /* ignore */ }
88
+ }
89
+ /**
90
+ * Check whether a file has an OS immutable flag set.
91
+ * Used by the drift watcher to decide whether to restore or emit an incident.
92
+ */
93
+ export async function isImmutable(filePath) {
94
+ if (process.platform === 'darwin') {
95
+ try {
96
+ const { stdout } = await execFileAsync('ls', ['-lO', filePath]);
97
+ return stdout.includes('uchg') || stdout.includes('uimmutable');
98
+ }
99
+ catch {
100
+ return false;
101
+ }
102
+ }
103
+ else if (process.platform === 'linux') {
104
+ try {
105
+ const { stdout } = await execFileAsync('lsattr', [filePath]);
106
+ return stdout.includes('-i-') || /\bi\b/.test(stdout.split(' ')[0] ?? '');
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ return false;
113
+ }
114
+ // ─── Internal ────────────────────────────────────────────────────────
115
+ async function applyImmutableFlag(target) {
116
+ if (process.platform === 'darwin') {
117
+ try {
118
+ await execFileAsync('chflags', ['uchg', target]);
119
+ log.info({ action: 'chflags_uchg', target }, 'macOS uchg immutable flag applied');
120
+ }
121
+ catch (err) {
122
+ log.error({ action: 'chflags_uchg_failed', target, err }, 'chflags uchg failed');
123
+ }
124
+ }
125
+ else if (process.platform === 'linux') {
126
+ try {
127
+ await execFileAsync('chattr', ['+i', target]);
128
+ log.info({ action: 'chattr_plus_i', target }, 'Linux immutable flag applied');
129
+ }
130
+ catch (err) {
131
+ // chattr requires root on most distros; fail gracefully
132
+ const msg = err instanceof Error ? err.message : String(err);
133
+ if (msg.includes('Operation not permitted') || msg.includes('EPERM')) {
134
+ log.warn({ action: 'chattr_needs_root', target }, 'chattr +i requires root — immutable flag not applied (chmod 444 still in effect)');
135
+ }
136
+ else {
137
+ log.error({ action: 'chattr_failed', target, err }, 'chattr +i failed unexpectedly');
138
+ }
139
+ }
140
+ }
141
+ else if (process.platform === 'win32') {
142
+ // Windows: SetFileAttributes READONLY via attrib
143
+ try {
144
+ await execFileAsync('attrib', ['+R', target]);
145
+ log.info({ action: 'attrib_readonly', target }, 'Windows read-only attribute applied');
146
+ }
147
+ catch (err) {
148
+ log.warn({ action: 'attrib_failed', target, err }, 'Windows attrib +R failed');
149
+ }
150
+ }
151
+ }
152
+ //# sourceMappingURL=gooseHardener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gooseHardener.js","sourceRoot":"","sources":["../../src/harness/gooseHardener.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * gooseHooks.ts — Goose PreToolUse governance plugin injection.
3
+ *
4
+ * Writes the Intutic governance plugin to the Goose plugin directory:
5
+ * ~/.agents/plugins/intutic-governance/hooks/hooks.json
6
+ * ~/.agents/plugins/intutic-governance/scripts/intutic-check.sh
7
+ *
8
+ * Also merges the Intutic proxy URL into ~/.config/goose/config.yaml.
9
+ * After writing, calls gooseHardener to apply chmod 444 + immutable flags.
10
+ *
11
+ * LLD #14 — Phase 3 cross-harness defence
12
+ * HLD §3.14 — Three-Tier Defense Cascade
13
+ *
14
+ * @module
15
+ */
16
+ /**
17
+ * Write Goose governance plugin and harden it.
18
+ *
19
+ * @param proxyUrl - Intutic proxy URL written into the plugin script.
20
+ * @param workspaceRoot - Absolute workspace root (for hook events log path).
21
+ * @param workspaceId - Workspace ID embedded in hook event payloads.
22
+ */
23
+ export declare function writeGooseHooks(proxyUrl: string, workspaceRoot?: string, workspaceId?: string): Promise<void>;
24
+ //# sourceMappingURL=gooseHooks.d.ts.map