@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Intutic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPORTED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @intutic/sync-daemon
2
+
3
+ > Bidirectional state sync between the Intutic control plane and developer workspaces.
4
+
5
+ ## Overview
6
+
7
+ The sync daemon runs as a background process on developer machines, maintaining a live bidirectional link between the Intutic control plane and local AI coding harness configurations. It ensures that governance SOPs are always reflected in harness config files, and that local configuration changes are captured and reported upstream.
8
+
9
+ ## Core Loop
10
+
11
+ ```
12
+ fetch config → write harness files → compute hashes → report drift
13
+ ```
14
+
15
+ On each sync cycle the daemon:
16
+
17
+ 1. **Fetches** the latest SOP configuration from the control plane
18
+ 2. **Writes** harness-specific config files using 18 format-aware config writers
19
+ 3. **Computes** content hashes for drift detection
20
+ 4. **Reports** any detected drift back to the control plane
21
+
22
+ ## Features
23
+
24
+ ### Config Writers
25
+
26
+ 18 harness-specific config writers that translate governance SOPs into native configuration formats (YAML, JSON, TOML, Markdown, environment variables).
27
+
28
+ ### Atomic Writes
29
+
30
+ All config file writes use a write-to-tmp + rename pattern to prevent partial writes from corrupting harness configurations.
31
+
32
+ ### macOS Immutable Flag Enforcement
33
+
34
+ On macOS, the daemon sets the immutable flag (`chflags uchg`) on managed config files to prevent accidental manual edits that would cause governance drift.
35
+
36
+ ### Drift Detection
37
+
38
+ Content-hash-based drift detection identifies when harness config files have been modified outside of the sync loop and reports the discrepancy to the control plane.
39
+
40
+ ### Trajectory Monitoring
41
+
42
+ Monitors active agent sessions and streams trajectory data (tool calls, file edits, terminal commands) to the control plane for real-time governance evaluation.
43
+
44
+ ### Brain Indexing
45
+
46
+ Indexes harness-specific context directories (BRAIN folders, memory files, skill definitions) and uploads structured metadata to the context graph.
47
+
48
+ ### Config Capture
49
+
50
+ Captures point-in-time snapshots of all harness configurations for audit, compliance, and historical comparison.
51
+
52
+ ## Usage
53
+
54
+ The daemon is typically started via the CLI:
55
+
56
+ ```bash
57
+ intutic connect # Start with default 30s interval
58
+ intutic connect --interval 10 # Custom sync interval (seconds)
59
+ ```
60
+
61
+ Or directly:
62
+
63
+ ```bash
64
+ npx @intutic/sync-daemon --api-url https://api.intutic.ai --interval 30
65
+ ```
66
+
67
+ ## Part of Intutic
68
+
69
+ This package is part of the [Intutic](https://github.com/intutic/intutic) monorepo — an open-core AI governance control plane for developer teams.
70
+
71
+ ## License
72
+
73
+ MIT — see [LICENSE](../../LICENSE) for details.
@@ -0,0 +1,51 @@
1
+ /**
2
+ * BrainIndexer — WS1: ContextGraph & Codebase RAG Federation
3
+ *
4
+ * Implements the client-side filesystem scanner that recursively walks agent folders
5
+ * (.gemini/, .claude/, .cursor/) and root config files (.cursorrules, .clauderules),
6
+ * computes SHA-256 hashes, categorizes file types, and determines sync deltas.
7
+ *
8
+ * LLD #16 — Contextual Data Indexing
9
+ * HLD §3.18
10
+ *
11
+ * @module
12
+ */
13
+ import type { IntegrityStore } from '@intutic/shared-types';
14
+ export interface ScanResult {
15
+ workspaceRoot: string;
16
+ files: Record<string, {
17
+ hash: string;
18
+ size: number;
19
+ lastModified: string;
20
+ name: string;
21
+ type: 'plan' | 'walkthrough' | 'rule' | 'scratch' | 'general';
22
+ }>;
23
+ }
24
+ export interface DeltaPayload {
25
+ upserted: Array<{
26
+ path: string;
27
+ name: string;
28
+ hash: string;
29
+ size: number;
30
+ type: 'plan' | 'walkthrough' | 'rule' | 'scratch' | 'general';
31
+ lastModified: string;
32
+ }>;
33
+ deleted: string[];
34
+ }
35
+ export declare class BrainIndexer {
36
+ private static EXCLUSIONS;
37
+ private static AGENT_FOLDERS;
38
+ /**
39
+ * Scans the workspace root recursively for agent config folders.
40
+ */
41
+ static scanWorkspace(workspaceRoot: string): Promise<ScanResult>;
42
+ /**
43
+ * Categorizes files into standard types.
44
+ */
45
+ static categorizeFile(filePath: string): 'plan' | 'walkthrough' | 'rule' | 'scratch' | 'general';
46
+ /**
47
+ * Compares the scan result against the local integrity store to calculate changes.
48
+ */
49
+ static computeDelta(scanResult: ScanResult, integrityStore: IntegrityStore | null): DeltaPayload;
50
+ }
51
+ //# sourceMappingURL=brainIndexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brainIndexer.d.ts","sourceRoot":"","sources":["../src/brainIndexer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,EAAE,MAAM,CACX,MAAM,EACN;QACE,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;KAC9D,CACF,CAAA;CACF;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;QAC7D,YAAY,EAAE,MAAM,CAAA;KACrB,CAAC,CAAA;IACF,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,UAAU,CAAwE;IACjG,OAAO,CAAC,MAAM,CAAC,aAAa,CAA6C;IAEzE;;OAEG;WACU,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAwEtE;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS;IA8BhG;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,IAAI,GAAG,YAAY;CA8BjG"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * BrainIndexer — WS1: ContextGraph & Codebase RAG Federation
3
+ *
4
+ * Implements the client-side filesystem scanner that recursively walks agent folders
5
+ * (.gemini/, .claude/, .cursor/) and root config files (.cursorrules, .clauderules),
6
+ * computes SHA-256 hashes, categorizes file types, and determines sync deltas.
7
+ *
8
+ * LLD #16 — Contextual Data Indexing
9
+ * HLD §3.18
10
+ *
11
+ * @module
12
+ */
13
+ import * as node_fs from 'node:fs/promises';
14
+ import * as node_path from 'node:path';
15
+ import * as crypto from 'node:crypto';
16
+ export class BrainIndexer {
17
+ static EXCLUSIONS = new Set(['node_modules', '.git', 'dist', 'build', '.dist', '.turbo']);
18
+ static AGENT_FOLDERS = new Set(['.gemini', '.claude', '.cursor']);
19
+ /**
20
+ * Scans the workspace root recursively for agent config folders.
21
+ */
22
+ static async scanWorkspace(workspaceRoot) {
23
+ const files = {};
24
+ async function walk(dir) {
25
+ let entries = [];
26
+ try {
27
+ entries = await node_fs.readdir(dir, { withFileTypes: true });
28
+ }
29
+ catch {
30
+ return; // Skip unreadable directories
31
+ }
32
+ for (const entry of entries) {
33
+ const fullPath = node_path.join(dir, entry.name);
34
+ if (entry.isDirectory()) {
35
+ if (BrainIndexer.EXCLUSIONS.has(entry.name)) {
36
+ continue;
37
+ }
38
+ // If we are at the workspace root, only traverse into .gemini, .claude, .cursor
39
+ const relativeDir = node_path.relative(workspaceRoot, fullPath);
40
+ const dirParts = relativeDir.split(node_path.sep);
41
+ if (dirParts.length === 1 && !BrainIndexer.AGENT_FOLDERS.has(entry.name)) {
42
+ continue;
43
+ }
44
+ await walk(fullPath);
45
+ }
46
+ else if (entry.isFile()) {
47
+ const relativePath = node_path.relative(workspaceRoot, fullPath);
48
+ const dirParts = relativePath.split(node_path.sep);
49
+ const isInAgentFolder = dirParts.length > 1 && BrainIndexer.AGENT_FOLDERS.has(dirParts[0]);
50
+ const isRootAgentFile = dirParts.length === 1 &&
51
+ (entry.name === '.cursorrules' ||
52
+ entry.name === '.clauderules' ||
53
+ entry.name === '.gemini');
54
+ if (!isInAgentFolder && !isRootAgentFile) {
55
+ continue;
56
+ }
57
+ try {
58
+ const stat = await node_fs.stat(fullPath);
59
+ if (stat.size > 5 * 1024 * 1024) {
60
+ continue; // 5MB cap limit
61
+ }
62
+ const hash = await computeFileHash(fullPath);
63
+ files[relativePath] = {
64
+ hash,
65
+ size: stat.size,
66
+ lastModified: stat.mtime.toISOString(),
67
+ name: entry.name,
68
+ type: BrainIndexer.categorizeFile(relativePath),
69
+ };
70
+ }
71
+ catch {
72
+ // Skip failed file reads
73
+ }
74
+ }
75
+ }
76
+ }
77
+ try {
78
+ await node_fs.access(workspaceRoot);
79
+ await walk(workspaceRoot);
80
+ }
81
+ catch {
82
+ // Root folder inaccessible
83
+ }
84
+ return { workspaceRoot, files };
85
+ }
86
+ /**
87
+ * Categorizes files into standard types.
88
+ */
89
+ static categorizeFile(filePath) {
90
+ const name = node_path.basename(filePath).toLowerCase();
91
+ if (name.includes('plan') || name.includes('roadmap') || name.includes('implementation')) {
92
+ return 'plan';
93
+ }
94
+ if (name.includes('walkthrough') || name.includes('summary') || name.includes('report')) {
95
+ return 'walkthrough';
96
+ }
97
+ if (name.includes('rules') ||
98
+ name.includes('instructions') ||
99
+ name.includes('guidelines') ||
100
+ name.includes('system') ||
101
+ name.includes('claude.md') ||
102
+ name.includes('agents.md')) {
103
+ return 'rule';
104
+ }
105
+ if (name.includes('scratch') ||
106
+ name.includes('temp') ||
107
+ name.includes('test') ||
108
+ name.includes('debug') ||
109
+ name.includes('simulate')) {
110
+ return 'scratch';
111
+ }
112
+ return 'general';
113
+ }
114
+ /**
115
+ * Compares the scan result against the local integrity store to calculate changes.
116
+ */
117
+ static computeDelta(scanResult, integrityStore) {
118
+ const upserted = [];
119
+ const deleted = [];
120
+ const storeFiles = integrityStore?.files ?? {};
121
+ // Identify added or modified files
122
+ for (const [path, file] of Object.entries(scanResult.files)) {
123
+ const cachedHash = storeFiles[path];
124
+ if (!cachedHash || cachedHash !== file.hash) {
125
+ upserted.push({
126
+ path,
127
+ name: file.name,
128
+ hash: file.hash,
129
+ size: file.size,
130
+ type: file.type,
131
+ lastModified: file.lastModified,
132
+ });
133
+ }
134
+ }
135
+ // Identify deleted files
136
+ for (const path of Object.keys(storeFiles)) {
137
+ if (!scanResult.files[path]) {
138
+ deleted.push(path);
139
+ }
140
+ }
141
+ return { upserted, deleted };
142
+ }
143
+ }
144
+ async function computeFileHash(filePath) {
145
+ const content = await node_fs.readFile(filePath);
146
+ return crypto.createHash('sha256').update(content).digest('hex');
147
+ }
148
+ //# sourceMappingURL=brainIndexer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brainIndexer.js","sourceRoot":"","sources":["../src/brainIndexer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * configReader.ts — Read harness config files and upload to control plane.
3
+ *
4
+ * The daemon reads config files (.cursorrules, CLAUDE.md, etc.) from the
5
+ * developer's workspace and POSTs them to the control plane for versioning,
6
+ * diff tracking, and SkillOpt analysis.
7
+ *
8
+ * LLD #51 — Harness Config Capture + SkillOpt Pipeline
9
+ *
10
+ * @module
11
+ */
12
+ import type { HarnessType, CapturedConfigFile } from '@intutic/shared-types';
13
+ /**
14
+ * Determine whether config capture should run on this iteration.
15
+ * Captures every Nth iteration to avoid flooding the control plane.
16
+ */
17
+ export declare function shouldCaptureThisIteration(iterationCount: number): boolean;
18
+ /**
19
+ * Read harness config files from the workspace.
20
+ * For each active harness, reads the config file, computes SHA-256 hash,
21
+ * and returns the content. Skips files that don't exist or are too large.
22
+ *
23
+ * @param workspaceRoot - Absolute path to the workspace root.
24
+ * @param harnesses - Active harness types detected in the workspace.
25
+ * @returns Array of captured config files with content and hashes.
26
+ */
27
+ export declare function readHarnessConfigs(workspaceRoot: string, harnesses: HarnessType[]): Promise<CapturedConfigFile[]>;
28
+ /**
29
+ * Upload captured config files to the control plane.
30
+ * Deduplicates locally: only uploads files whose content hash changed
31
+ * since the last upload.
32
+ *
33
+ * @param controlPlaneUrl - Control plane base URL.
34
+ * @param apiKey - API key for authentication.
35
+ * @param workspaceId - Workspace identifier.
36
+ * @param harnessType - Which harness these configs belong to.
37
+ * @param configs - Captured config files from readHarnessConfigs().
38
+ * @returns Number of files actually uploaded (after dedup).
39
+ */
40
+ export declare function uploadConfigCapture(controlPlaneUrl: string, apiKey: string, workspaceId: string, harnessType: HarnessType, configs: CapturedConfigFile[]): Promise<number>;
41
+ /**
42
+ * Full config capture cycle: read + upload.
43
+ * Groups by harness type for proper capture payloads.
44
+ */
45
+ export declare function captureAndUpload(controlPlaneUrl: string, apiKey: string, workspaceId: string, workspaceRoot: string, harnesses: HarnessType[]): Promise<void>;
46
+ //# sourceMappingURL=configReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configReader.d.ts","sourceRoot":"","sources":["../src/configReader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAA6B,MAAM,uBAAuB,CAAA;AAkBvG;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAG1E;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,WAAW,EAAE,GACvB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAgC/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,kBAAkB,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,CAsCjB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,WAAW,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * configReader.ts — Read harness config files and upload to control plane.
3
+ *
4
+ * The daemon reads config files (.cursorrules, CLAUDE.md, etc.) from the
5
+ * developer's workspace and POSTs them to the control plane for versioning,
6
+ * diff tracking, and SkillOpt analysis.
7
+ *
8
+ * LLD #51 — Harness Config Capture + SkillOpt Pipeline
9
+ *
10
+ * @module
11
+ */
12
+ import * as fs from 'node:fs/promises';
13
+ import * as path from 'node:path';
14
+ import * as crypto from 'node:crypto';
15
+ import { HARNESS_FILES } from './configWriter.js';
16
+ // ─── Constants ───────────────────────────────────────────────────────
17
+ /** Capture every Nth sync iteration (default: every 5th = ~2.5 min at 30s poll). */
18
+ const DEFAULT_CAPTURE_INTERVAL = 5;
19
+ /** Max file size to capture (prevent uploading massive files). */
20
+ const MAX_FILE_SIZE_BYTES = 512 * 1024; // 512 KB
21
+ // ─── Local hash cache ────────────────────────────────────────────────
22
+ /** Cache of last-uploaded content hashes per file path. Avoids redundant uploads. */
23
+ const lastUploadedHashes = new Map();
24
+ // ─── Public API ──────────────────────────────────────────────────────
25
+ /**
26
+ * Determine whether config capture should run on this iteration.
27
+ * Captures every Nth iteration to avoid flooding the control plane.
28
+ */
29
+ export function shouldCaptureThisIteration(iterationCount) {
30
+ const interval = parseInt(process.env.CONFIG_CAPTURE_INTERVAL ?? '', 10) || DEFAULT_CAPTURE_INTERVAL;
31
+ return iterationCount > 0 && iterationCount % interval === 0;
32
+ }
33
+ /**
34
+ * Read harness config files from the workspace.
35
+ * For each active harness, reads the config file, computes SHA-256 hash,
36
+ * and returns the content. Skips files that don't exist or are too large.
37
+ *
38
+ * @param workspaceRoot - Absolute path to the workspace root.
39
+ * @param harnesses - Active harness types detected in the workspace.
40
+ * @returns Array of captured config files with content and hashes.
41
+ */
42
+ export async function readHarnessConfigs(workspaceRoot, harnesses) {
43
+ const results = [];
44
+ for (const harness of harnesses) {
45
+ const filename = HARNESS_FILES[harness];
46
+ if (!filename)
47
+ continue;
48
+ const filePath = path.join(workspaceRoot, filename);
49
+ try {
50
+ const stat = await fs.stat(filePath);
51
+ if (stat.size > MAX_FILE_SIZE_BYTES) {
52
+ console.warn(`[config-reader] Skipping ${filename}: ${stat.size} bytes exceeds ${MAX_FILE_SIZE_BYTES} limit`);
53
+ continue;
54
+ }
55
+ const content = await fs.readFile(filePath, 'utf-8');
56
+ const contentHash = crypto.createHash('sha256').update(content).digest('hex');
57
+ results.push({
58
+ path: filename,
59
+ content,
60
+ contentHash,
61
+ });
62
+ }
63
+ catch (err) {
64
+ // File doesn't exist or unreadable — skip silently
65
+ if (err.code !== 'ENOENT') {
66
+ console.warn(`[config-reader] Could not read ${filename}:`, err);
67
+ }
68
+ }
69
+ }
70
+ return results;
71
+ }
72
+ /**
73
+ * Upload captured config files to the control plane.
74
+ * Deduplicates locally: only uploads files whose content hash changed
75
+ * since the last upload.
76
+ *
77
+ * @param controlPlaneUrl - Control plane base URL.
78
+ * @param apiKey - API key for authentication.
79
+ * @param workspaceId - Workspace identifier.
80
+ * @param harnessType - Which harness these configs belong to.
81
+ * @param configs - Captured config files from readHarnessConfigs().
82
+ * @returns Number of files actually uploaded (after dedup).
83
+ */
84
+ export async function uploadConfigCapture(controlPlaneUrl, apiKey, workspaceId, harnessType, configs) {
85
+ // Filter out files whose hash hasn't changed since last upload
86
+ const changed = configs.filter(f => {
87
+ const lastHash = lastUploadedHashes.get(f.path);
88
+ return lastHash !== f.contentHash;
89
+ });
90
+ if (changed.length === 0)
91
+ return 0;
92
+ const payload = {
93
+ workspaceId,
94
+ harnessType,
95
+ files: changed,
96
+ };
97
+ const url = `${controlPlaneUrl}/api/v1/config/capture`;
98
+ const res = await fetch(url, {
99
+ method: 'POST',
100
+ headers: {
101
+ 'content-type': 'application/json',
102
+ authorization: `Bearer ${apiKey}`,
103
+ },
104
+ body: JSON.stringify(payload),
105
+ signal: AbortSignal.timeout(10_000),
106
+ });
107
+ if (!res.ok) {
108
+ const body = await res.text().catch(() => '');
109
+ console.warn(`[config-reader] Upload failed (${res.status}): ${body}`);
110
+ return 0;
111
+ }
112
+ // Update local hash cache on success
113
+ for (const f of changed) {
114
+ lastUploadedHashes.set(f.path, f.contentHash);
115
+ }
116
+ return changed.length;
117
+ }
118
+ /**
119
+ * Full config capture cycle: read + upload.
120
+ * Groups by harness type for proper capture payloads.
121
+ */
122
+ export async function captureAndUpload(controlPlaneUrl, apiKey, workspaceId, workspaceRoot, harnesses) {
123
+ for (const harness of harnesses) {
124
+ const configs = await readHarnessConfigs(workspaceRoot, [harness]);
125
+ if (configs.length > 0) {
126
+ const uploaded = await uploadConfigCapture(controlPlaneUrl, apiKey, workspaceId, harness, configs);
127
+ if (uploaded > 0) {
128
+ console.log(`[config-reader] Captured ${uploaded} ${harness} config file(s)`);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ //# sourceMappingURL=configReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configReader.js","sourceRoot":"","sources":["../src/configReader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * configWriter.ts — Write SOP content to harness config files.
3
+ *
4
+ * Each harness (Cursor, Claude Code, Antigravity, Windsurf, Aider,
5
+ * OpenHands, Codex) has a distinct config file format. This module
6
+ * resolves paths, formats content, and performs atomic writes
7
+ * (write-to-tmp → rename) to prevent partial/corrupt files.
8
+ *
9
+ * HLD §3.14 — Real-Time State Mirroring
10
+ * LLD #8 — Sync Daemon / CLI
11
+ *
12
+ * @module
13
+ */
14
+ import type { HarnessType, SyncSopEntry, ConfigEdit } from '@intutic/shared-types';
15
+ /**
16
+ * Maps each harness type to its expected config filename relative to
17
+ * the workspace root. Empty string means the harness is deferred to
18
+ * Phase 2 (e.g., n8n — TD-037).
19
+ */
20
+ export declare const HARNESS_FILES: Record<HarnessType, string>;
21
+ /** Result of a config-write operation across one or more harnesses. */
22
+ export interface WriteResult {
23
+ /** Paths of files that were successfully written. */
24
+ filesWritten: string[];
25
+ /** Paths where the harness was skipped (not detected or deferred). */
26
+ filesSkipped: string[];
27
+ }
28
+ /**
29
+ * Write SOP content to all targeted harness config files.
30
+ *
31
+ * For each harness type, resolves the config file path, formats the
32
+ * content according to the harness-specific format, and performs an
33
+ * atomic write (tmp file → rename).
34
+ *
35
+ * @param workspaceRoot - Absolute path to the workspace root.
36
+ * @param sops - SOPs to write, each specifying which harnesses to target.
37
+ * @param proxyUrl - Intutic proxy URL for LLM API redirect.
38
+ * @param harnesses - Harness types detected in the workspace.
39
+ * @returns WriteResult with written and skipped file paths.
40
+ */
41
+ export declare function writeConfigFiles(workspaceRoot: string, sops: SyncSopEntry[], proxyUrl: string, harnesses: HarnessType[], workspaceId?: string,
42
+ /** WS-5 Q3: 'rewrite' (default) | 'immutable' | 'alert-only' */
43
+ bypassEnforcementTier?: string): Promise<WriteResult>;
44
+ /**
45
+ * Apply a list of custom config edits (ADD, DELETE, REPLACE) to a workspace rule file.
46
+ */
47
+ export declare function applyConfigEdits(workspaceRoot: string, appliedEdits: Array<{
48
+ suggestionId: string;
49
+ harnessType: string;
50
+ filePath: string;
51
+ edits: string | ConfigEdit[];
52
+ }>, bypassEnforcementTier?: string): Promise<void>;
53
+ //# sourceMappingURL=configWriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configWriter.d.ts","sourceRoot":"","sources":["../src/configWriter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAwClF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAmBrD,CAAA;AAID,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,sEAAsE;IACtE,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,YAAY,EAAE,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,WAAW,EAAE,EACxB,WAAW,SAAK;AAChB,gEAAgE;AAChE,qBAAqB,CAAC,EAAE,MAAM,GAC7B,OAAO,CAAC,WAAW,CAAC,CAwJtB;AAkCD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,KAAK,CAAC;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAAA;CAC7B,CAAC,EACF,qBAAqB,CAAC,EAAE,MAAM,GAC7B,OAAO,CAAC,IAAI,CAAC,CA2Df"}