conductor-bridge 1.0.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 (134) hide show
  1. package/README.md +69 -0
  2. package/dist/bridge/index.d.ts +8 -0
  3. package/dist/bridge/index.d.ts.map +1 -0
  4. package/dist/bridge/index.js +8 -0
  5. package/dist/bridge/index.js.map +1 -0
  6. package/dist/bridge/json-interchange.d.ts +94 -0
  7. package/dist/bridge/json-interchange.d.ts.map +1 -0
  8. package/dist/bridge/json-interchange.js +301 -0
  9. package/dist/bridge/json-interchange.js.map +1 -0
  10. package/dist/cli/commands/amend.d.ts +12 -0
  11. package/dist/cli/commands/amend.d.ts.map +1 -0
  12. package/dist/cli/commands/amend.js +205 -0
  13. package/dist/cli/commands/amend.js.map +1 -0
  14. package/dist/cli/commands/daemon.d.ts +12 -0
  15. package/dist/cli/commands/daemon.d.ts.map +1 -0
  16. package/dist/cli/commands/daemon.js +60 -0
  17. package/dist/cli/commands/daemon.js.map +1 -0
  18. package/dist/cli/commands/dispatch.d.ts +12 -0
  19. package/dist/cli/commands/dispatch.d.ts.map +1 -0
  20. package/dist/cli/commands/dispatch.js +207 -0
  21. package/dist/cli/commands/dispatch.js.map +1 -0
  22. package/dist/cli/commands/handoff.d.ts +31 -0
  23. package/dist/cli/commands/handoff.d.ts.map +1 -0
  24. package/dist/cli/commands/handoff.js +273 -0
  25. package/dist/cli/commands/handoff.js.map +1 -0
  26. package/dist/cli/commands/init.d.ts +12 -0
  27. package/dist/cli/commands/init.d.ts.map +1 -0
  28. package/dist/cli/commands/init.js +301 -0
  29. package/dist/cli/commands/init.js.map +1 -0
  30. package/dist/cli/commands/status.d.ts +12 -0
  31. package/dist/cli/commands/status.d.ts.map +1 -0
  32. package/dist/cli/commands/status.js +206 -0
  33. package/dist/cli/commands/status.js.map +1 -0
  34. package/dist/cli/index.d.ts +17 -0
  35. package/dist/cli/index.d.ts.map +1 -0
  36. package/dist/cli/index.js +148 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/handoff/encryption.d.ts +85 -0
  39. package/dist/handoff/encryption.d.ts.map +1 -0
  40. package/dist/handoff/encryption.js +308 -0
  41. package/dist/handoff/encryption.js.map +1 -0
  42. package/dist/handoff/index.d.ts +8 -0
  43. package/dist/handoff/index.d.ts.map +1 -0
  44. package/dist/handoff/index.js +10 -0
  45. package/dist/handoff/index.js.map +1 -0
  46. package/dist/handoff/mycelium-arc.d.ts +116 -0
  47. package/dist/handoff/mycelium-arc.d.ts.map +1 -0
  48. package/dist/handoff/mycelium-arc.js +410 -0
  49. package/dist/handoff/mycelium-arc.js.map +1 -0
  50. package/dist/index.d.ts +38 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +71 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/parsers/index.d.ts +10 -0
  55. package/dist/parsers/index.d.ts.map +1 -0
  56. package/dist/parsers/index.js +12 -0
  57. package/dist/parsers/index.js.map +1 -0
  58. package/dist/parsers/plan-parser.d.ts +29 -0
  59. package/dist/parsers/plan-parser.d.ts.map +1 -0
  60. package/dist/parsers/plan-parser.js +503 -0
  61. package/dist/parsers/plan-parser.js.map +1 -0
  62. package/dist/parsers/spec-parser.d.ts +10 -0
  63. package/dist/parsers/spec-parser.d.ts.map +1 -0
  64. package/dist/parsers/spec-parser.js +382 -0
  65. package/dist/parsers/spec-parser.js.map +1 -0
  66. package/dist/parsers/state-parser.d.ts +21 -0
  67. package/dist/parsers/state-parser.d.ts.map +1 -0
  68. package/dist/parsers/state-parser.js +378 -0
  69. package/dist/parsers/state-parser.js.map +1 -0
  70. package/dist/parsers/types.d.ts +190 -0
  71. package/dist/parsers/types.d.ts.map +1 -0
  72. package/dist/parsers/types.js +7 -0
  73. package/dist/parsers/types.js.map +1 -0
  74. package/dist/schemas/cognitive-state.d.ts +1523 -0
  75. package/dist/schemas/cognitive-state.d.ts.map +1 -0
  76. package/dist/schemas/cognitive-state.js +328 -0
  77. package/dist/schemas/cognitive-state.js.map +1 -0
  78. package/dist/schemas/enums.d.ts +124 -0
  79. package/dist/schemas/enums.d.ts.map +1 -0
  80. package/dist/schemas/enums.js +108 -0
  81. package/dist/schemas/enums.js.map +1 -0
  82. package/dist/schemas/index.d.ts +9 -0
  83. package/dist/schemas/index.d.ts.map +1 -0
  84. package/dist/schemas/index.js +9 -0
  85. package/dist/schemas/index.js.map +1 -0
  86. package/dist/sync/adhd-continuity.d.ts +91 -0
  87. package/dist/sync/adhd-continuity.d.ts.map +1 -0
  88. package/dist/sync/adhd-continuity.js +302 -0
  89. package/dist/sync/adhd-continuity.js.map +1 -0
  90. package/dist/sync/convergence-tracker.d.ts +111 -0
  91. package/dist/sync/convergence-tracker.d.ts.map +1 -0
  92. package/dist/sync/convergence-tracker.js +299 -0
  93. package/dist/sync/convergence-tracker.js.map +1 -0
  94. package/dist/sync/index.d.ts +11 -0
  95. package/dist/sync/index.d.ts.map +1 -0
  96. package/dist/sync/index.js +15 -0
  97. package/dist/sync/index.js.map +1 -0
  98. package/dist/sync/state-sync.d.ts +105 -0
  99. package/dist/sync/state-sync.d.ts.map +1 -0
  100. package/dist/sync/state-sync.js +403 -0
  101. package/dist/sync/state-sync.js.map +1 -0
  102. package/dist/sync/watcher.d.ts +90 -0
  103. package/dist/sync/watcher.d.ts.map +1 -0
  104. package/dist/sync/watcher.js +281 -0
  105. package/dist/sync/watcher.js.map +1 -0
  106. package/dist/utils/atomic-write.d.ts +31 -0
  107. package/dist/utils/atomic-write.d.ts.map +1 -0
  108. package/dist/utils/atomic-write.js +69 -0
  109. package/dist/utils/atomic-write.js.map +1 -0
  110. package/dist/utils/error-handling.d.ts +70 -0
  111. package/dist/utils/error-handling.d.ts.map +1 -0
  112. package/dist/utils/error-handling.js +109 -0
  113. package/dist/utils/error-handling.js.map +1 -0
  114. package/dist/utils/file-lock.d.ts +46 -0
  115. package/dist/utils/file-lock.d.ts.map +1 -0
  116. package/dist/utils/file-lock.js +117 -0
  117. package/dist/utils/file-lock.js.map +1 -0
  118. package/dist/utils/index.d.ts +10 -0
  119. package/dist/utils/index.d.ts.map +1 -0
  120. package/dist/utils/index.js +12 -0
  121. package/dist/utils/index.js.map +1 -0
  122. package/dist/utils/rlm-navigator.d.ts +160 -0
  123. package/dist/utils/rlm-navigator.d.ts.map +1 -0
  124. package/dist/utils/rlm-navigator.js +368 -0
  125. package/dist/utils/rlm-navigator.js.map +1 -0
  126. package/dist/utils/safe-path.d.ts +44 -0
  127. package/dist/utils/safe-path.d.ts.map +1 -0
  128. package/dist/utils/safe-path.js +96 -0
  129. package/dist/utils/safe-path.js.map +1 -0
  130. package/dist/utils/timed-io.d.ts +51 -0
  131. package/dist/utils/timed-io.d.ts.map +1 -0
  132. package/dist/utils/timed-io.js +128 -0
  133. package/dist/utils/timed-io.js.map +1 -0
  134. package/package.json +88 -0
@@ -0,0 +1,281 @@
1
+ /**
2
+ * File Watcher Daemon
3
+ *
4
+ * Watches .conductor/ directory for changes and triggers auto-sync.
5
+ * Provides real-time bidirectional state synchronization.
6
+ */
7
+ import { watch } from 'chokidar';
8
+ import { join, basename } from 'path';
9
+ import { EventEmitter } from 'events';
10
+ import { syncConductorToUSD, syncUSDToConductor, computeFileChecksum, hasFileChanged, } from './state-sync.js';
11
+ import { ConductorConvergenceTracker } from './convergence-tracker.js';
12
+ import { parsePlan } from '../parsers/plan-parser.js';
13
+ import { parseState } from '../parsers/state-parser.js';
14
+ import { readFile } from 'fs/promises';
15
+ import { withFallback } from '../utils/error-handling.js';
16
+ // ============================================================================
17
+ // Default Configuration
18
+ // ============================================================================
19
+ const DEFAULT_CONFIG = {
20
+ debounceMs: 500,
21
+ syncOnStart: true,
22
+ watchPatterns: [
23
+ '**/spec.md',
24
+ '**/plan.md',
25
+ '**/state.md',
26
+ '**/amendments/*.md',
27
+ ],
28
+ ignorePatterns: [
29
+ '**/node_modules/**',
30
+ '**/.git/**',
31
+ '**/dispatch.md',
32
+ ],
33
+ };
34
+ // ============================================================================
35
+ // Watcher Class
36
+ // ============================================================================
37
+ export class ConductorWatcher extends EventEmitter {
38
+ config;
39
+ watcher = null;
40
+ checksums = new Map();
41
+ debounceTimer = null;
42
+ stats;
43
+ convergenceTracker;
44
+ isRunning = false;
45
+ constructor(config) {
46
+ super();
47
+ this.config = {
48
+ conductorDir: config.conductorDir || '.conductor',
49
+ cognitiveStatePath: config.cognitiveStatePath || '.conductor/cognitive_state.json',
50
+ autoSave: config.autoSave ?? true,
51
+ autoSaveIntervalMs: config.autoSaveIntervalMs ?? 300000, // 5 min
52
+ debounceMs: config.debounceMs ?? DEFAULT_CONFIG.debounceMs,
53
+ syncOnStart: config.syncOnStart ?? DEFAULT_CONFIG.syncOnStart,
54
+ watchPatterns: config.watchPatterns ?? DEFAULT_CONFIG.watchPatterns,
55
+ ignorePatterns: config.ignorePatterns ?? DEFAULT_CONFIG.ignorePatterns,
56
+ };
57
+ this.stats = {
58
+ startTime: new Date(),
59
+ syncCount: 0,
60
+ lastSyncTime: null,
61
+ lastSyncResult: null,
62
+ errors: [],
63
+ exchangeCount: 0,
64
+ };
65
+ this.convergenceTracker = new ConductorConvergenceTracker();
66
+ }
67
+ /**
68
+ * Start watching for file changes.
69
+ */
70
+ async start() {
71
+ if (this.isRunning) {
72
+ return;
73
+ }
74
+ this.stats.startTime = new Date();
75
+ this.isRunning = true;
76
+ // Initialize file watcher
77
+ this.watcher = watch(this.config.watchPatterns, {
78
+ cwd: this.config.conductorDir,
79
+ ignored: this.config.ignorePatterns,
80
+ persistent: true,
81
+ ignoreInitial: true,
82
+ awaitWriteFinish: {
83
+ stabilityThreshold: 100,
84
+ pollInterval: 50,
85
+ },
86
+ });
87
+ // Set up event handlers
88
+ this.watcher.on('change', (path) => this.handleChange(path));
89
+ this.watcher.on('add', (path) => this.handleChange(path));
90
+ this.watcher.on('error', (error) => this.handleError(error));
91
+ // Initial sync
92
+ if (this.config.syncOnStart) {
93
+ await this.sync();
94
+ }
95
+ // Initial checksum capture
96
+ await this.captureChecksums();
97
+ this.emit('started', { config: this.config });
98
+ console.log(`[Watcher] Started watching ${this.config.conductorDir}`);
99
+ }
100
+ /**
101
+ * Stop watching for file changes.
102
+ */
103
+ async stop() {
104
+ if (!this.isRunning) {
105
+ return;
106
+ }
107
+ if (this.debounceTimer) {
108
+ clearTimeout(this.debounceTimer);
109
+ this.debounceTimer = null;
110
+ }
111
+ if (this.watcher) {
112
+ await this.watcher.close();
113
+ this.watcher = null;
114
+ }
115
+ this.isRunning = false;
116
+ this.emit('stopped', { stats: this.stats });
117
+ console.log('[Watcher] Stopped');
118
+ }
119
+ /**
120
+ * Handle file change event.
121
+ */
122
+ handleChange(path) {
123
+ const filename = basename(path);
124
+ this.emit('change', { path, filename });
125
+ // Debounce sync
126
+ if (this.debounceTimer) {
127
+ clearTimeout(this.debounceTimer);
128
+ }
129
+ this.debounceTimer = setTimeout(() => {
130
+ this.onDebouncedChange(path);
131
+ }, this.config.debounceMs);
132
+ }
133
+ /**
134
+ * Handle debounced file change.
135
+ */
136
+ async onDebouncedChange(path) {
137
+ const filename = basename(path);
138
+ const fullPath = join(this.config.conductorDir, path);
139
+ try {
140
+ // Check if file actually changed (via checksum)
141
+ const newChecksum = await computeFileChecksum(fullPath);
142
+ const oldChecksum = this.checksums.get(path) || '';
143
+ if (!hasFileChanged(newChecksum, oldChecksum)) {
144
+ return;
145
+ }
146
+ // Update checksum
147
+ this.checksums.set(path, newChecksum);
148
+ // Determine sync direction based on changed file
149
+ if (filename === 'plan.md' || filename === 'state.md') {
150
+ // Conductor changed → sync to USD
151
+ await this.syncWithConvergence();
152
+ }
153
+ else if (filename === 'cognitive_state.json') {
154
+ // USD changed → sync to Conductor
155
+ const result = await syncUSDToConductor(this.config);
156
+ this.recordSyncResult(result);
157
+ }
158
+ }
159
+ catch (error) {
160
+ this.handleError(error);
161
+ }
162
+ }
163
+ /**
164
+ * Perform sync with convergence tracking.
165
+ */
166
+ async syncWithConvergence() {
167
+ try {
168
+ // Read current state with proper error logging
169
+ const statePath = join(this.config.conductorDir, 'state.md');
170
+ const stateContent = await withFallback(() => readFile(statePath, 'utf-8'), '', { operation: 'readStateFile', path: statePath });
171
+ const stateResult = parseState(stateContent);
172
+ if (!stateResult.success || !stateResult.data) {
173
+ await this.sync();
174
+ return;
175
+ }
176
+ const planPath = join(this.config.conductorDir, stateResult.data.frontmatter.plan || 'plan.md');
177
+ const planContent = await withFallback(() => readFile(planPath, 'utf-8'), '', { operation: 'readPlanFile', path: planPath });
178
+ const planResult = parsePlan(planContent);
179
+ if (!planResult.success || !planResult.data) {
180
+ await this.sync();
181
+ return;
182
+ }
183
+ // Track convergence
184
+ this.stats.exchangeCount++;
185
+ const convergence = this.convergenceTracker.recordExchange(planResult.data);
186
+ this.emit('convergence', {
187
+ xi_n: convergence.xi_n,
188
+ attractor: convergence.attractor,
189
+ stability: convergence.stability,
190
+ glyph: this.convergenceTracker.getGlyph(),
191
+ isConverged: convergence.isConverged,
192
+ });
193
+ // Perform sync
194
+ await this.sync();
195
+ }
196
+ catch (error) {
197
+ this.handleError(error);
198
+ }
199
+ }
200
+ /**
201
+ * Perform bidirectional sync.
202
+ */
203
+ async sync() {
204
+ const result = await syncConductorToUSD(this.config);
205
+ this.recordSyncResult(result);
206
+ return result;
207
+ }
208
+ /**
209
+ * Record sync result in stats.
210
+ */
211
+ recordSyncResult(result) {
212
+ this.stats.syncCount++;
213
+ this.stats.lastSyncTime = new Date();
214
+ this.stats.lastSyncResult = result;
215
+ if (!result.success) {
216
+ this.stats.errors.push(...result.warnings);
217
+ }
218
+ this.emit('sync', result);
219
+ }
220
+ /**
221
+ * Handle errors.
222
+ */
223
+ handleError(error) {
224
+ const message = error instanceof Error ? error.message : String(error);
225
+ this.stats.errors.push(message);
226
+ this.emit('error', { message, timestamp: new Date().toISOString() });
227
+ console.error(`[Watcher] Error: ${message}`);
228
+ }
229
+ /**
230
+ * Capture initial file checksums.
231
+ */
232
+ async captureChecksums() {
233
+ const files = ['state.md', 'plan.md', 'spec.md'];
234
+ for (const file of files) {
235
+ try {
236
+ const fullPath = join(this.config.conductorDir, file);
237
+ const checksum = await computeFileChecksum(fullPath);
238
+ if (checksum) {
239
+ this.checksums.set(file, checksum);
240
+ }
241
+ }
242
+ catch {
243
+ // File doesn't exist, ignore
244
+ }
245
+ }
246
+ }
247
+ /**
248
+ * Get current stats.
249
+ */
250
+ getStats() {
251
+ return { ...this.stats };
252
+ }
253
+ /**
254
+ * Get convergence glyph.
255
+ */
256
+ getConvergenceGlyph() {
257
+ return this.convergenceTracker.getGlyph();
258
+ }
259
+ /**
260
+ * Check if watcher is running.
261
+ */
262
+ isActive() {
263
+ return this.isRunning;
264
+ }
265
+ }
266
+ // ============================================================================
267
+ // Factory Function
268
+ // ============================================================================
269
+ /**
270
+ * Create and start a conductor watcher.
271
+ */
272
+ export async function createWatcher(conductorDir, options) {
273
+ const watcher = new ConductorWatcher({
274
+ conductorDir,
275
+ cognitiveStatePath: join(conductorDir, 'cognitive_state.json'),
276
+ ...options,
277
+ });
278
+ await watcher.start();
279
+ return watcher;
280
+ }
281
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/sync/watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Atomic Write Utility
3
+ *
4
+ * Provides atomic file writes using temp file + rename pattern.
5
+ * This ensures file integrity even during crashes or power failures.
6
+ */
7
+ export interface AtomicWriteOptions {
8
+ /** File encoding (default: 'utf-8') */
9
+ encoding?: BufferEncoding;
10
+ /** Temp file suffix (default: '.tmp') */
11
+ tempSuffix?: string;
12
+ /** Whether to fsync after write (default: true) */
13
+ fsync?: boolean;
14
+ }
15
+ /**
16
+ * Write file atomically using temp file + rename pattern.
17
+ *
18
+ * On POSIX systems, rename() is atomic within the same filesystem.
19
+ * On Windows, we unlink the target first if it exists (not fully atomic
20
+ * but prevents data corruption).
21
+ *
22
+ * @param filePath - Target file path
23
+ * @param data - Data to write
24
+ * @param options - Write options
25
+ */
26
+ export declare function atomicWriteFile(filePath: string, data: string | Buffer, options?: AtomicWriteOptions): Promise<void>;
27
+ /**
28
+ * Check if a file exists
29
+ */
30
+ export declare function fileExists(filePath: string): Promise<boolean>;
31
+ //# sourceMappingURL=atomic-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../src/utils/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Atomic Write Utility
3
+ *
4
+ * Provides atomic file writes using temp file + rename pattern.
5
+ * This ensures file integrity even during crashes or power failures.
6
+ */
7
+ import { writeFile, rename, unlink, stat, mkdir } from 'fs/promises';
8
+ import { join, dirname, basename } from 'path';
9
+ import { randomBytes } from 'crypto';
10
+ /**
11
+ * Write file atomically using temp file + rename pattern.
12
+ *
13
+ * On POSIX systems, rename() is atomic within the same filesystem.
14
+ * On Windows, we unlink the target first if it exists (not fully atomic
15
+ * but prevents data corruption).
16
+ *
17
+ * @param filePath - Target file path
18
+ * @param data - Data to write
19
+ * @param options - Write options
20
+ */
21
+ export async function atomicWriteFile(filePath, data, options = {}) {
22
+ const { encoding = 'utf-8', tempSuffix = '.tmp', } = options;
23
+ // Generate unique temp file name to avoid collisions
24
+ const uniqueId = randomBytes(6).toString('hex');
25
+ const tempPath = join(dirname(filePath), `.${basename(filePath)}.${uniqueId}${tempSuffix}`);
26
+ try {
27
+ // Ensure parent directory exists
28
+ await mkdir(dirname(filePath), { recursive: true });
29
+ // Write to temp file
30
+ await writeFile(tempPath, data, { encoding });
31
+ // On Windows, rename fails if target exists, so unlink first
32
+ if (process.platform === 'win32') {
33
+ try {
34
+ await unlink(filePath);
35
+ }
36
+ catch (err) {
37
+ // File doesn't exist, that's fine
38
+ if (err.code !== 'ENOENT') {
39
+ throw err;
40
+ }
41
+ }
42
+ }
43
+ // Rename temp to target (atomic on most filesystems)
44
+ await rename(tempPath, filePath);
45
+ }
46
+ catch (error) {
47
+ // Clean up temp file on failure
48
+ try {
49
+ await unlink(tempPath);
50
+ }
51
+ catch {
52
+ // Ignore cleanup errors
53
+ }
54
+ throw error;
55
+ }
56
+ }
57
+ /**
58
+ * Check if a file exists
59
+ */
60
+ export async function fileExists(filePath) {
61
+ try {
62
+ await stat(filePath);
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ //# sourceMappingURL=atomic-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/utils/atomic-write.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAWrC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,IAAqB,EACrB,UAA8B,EAAE;IAEhC,MAAM,EACJ,QAAQ,GAAG,OAAO,EAClB,UAAU,GAAG,MAAM,GACpB,GAAG,OAAO,CAAC;IAEZ,qDAAqD;IACrD,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,CACnB,OAAO,CAAC,QAAQ,CAAC,EACjB,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,UAAU,EAAE,CAClD,CAAC;IAEF,IAAI,CAAC;QACH,iCAAiC;QACjC,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEpD,qBAAqB;QACrB,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAE9C,6DAA6D;QAC7D,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,kCAAkC;gBAClC,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACrD,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAED,qDAAqD;QACrD,MAAM,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,gCAAgC;QAChC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Error Handling Utility
3
+ *
4
+ * Provides structured error handling with logging before fallbacks.
5
+ * Replaces silent .catch(() => '') patterns with logged fallbacks.
6
+ */
7
+ export interface FallbackContext {
8
+ /** Name of the operation that failed */
9
+ operation: string;
10
+ /** File path or other identifier */
11
+ path?: string;
12
+ /** Additional context */
13
+ details?: Record<string, unknown>;
14
+ }
15
+ export interface FallbackOptions {
16
+ /** Whether to log warnings (default: true) */
17
+ logWarning?: boolean;
18
+ /** Custom logger function */
19
+ logger?: (message: string, context: FallbackContext, error: unknown) => void;
20
+ /** Whether to include stack trace in logs (default: false in production) */
21
+ includeStack?: boolean;
22
+ }
23
+ /**
24
+ * Execute an async operation with a fallback value, logging errors before fallback.
25
+ *
26
+ * This replaces patterns like:
27
+ * ```ts
28
+ * const content = await readFile(path).catch(() => '');
29
+ * ```
30
+ *
31
+ * With structured error handling:
32
+ * ```ts
33
+ * const content = await withFallback(
34
+ * () => readFile(path, 'utf-8'),
35
+ * '',
36
+ * { operation: 'readStateFile', path }
37
+ * );
38
+ * ```
39
+ *
40
+ * @param operation - Async operation to execute
41
+ * @param fallback - Value to return if operation fails
42
+ * @param context - Context for logging
43
+ * @param options - Options for error handling
44
+ * @returns Result of operation or fallback value
45
+ */
46
+ export declare function withFallback<T>(operation: () => Promise<T>, fallback: T, context: FallbackContext, options?: FallbackOptions): Promise<T>;
47
+ /**
48
+ * Synchronous version of withFallback.
49
+ */
50
+ export declare function withFallbackSync<T>(operation: () => T, fallback: T, context: FallbackContext, options?: FallbackOptions): T;
51
+ /**
52
+ * Execute an operation with retry logic and exponential backoff.
53
+ *
54
+ * @param operation - Async operation to execute
55
+ * @param context - Context for logging
56
+ * @param maxRetries - Maximum number of retries (default: 3)
57
+ * @param baseDelayMs - Base delay between retries in ms (default: 100)
58
+ * @returns Result of operation
59
+ * @throws Last error if all retries fail
60
+ */
61
+ export declare function withRetry<T>(operation: () => Promise<T>, context: FallbackContext, maxRetries?: number, baseDelayMs?: number): Promise<T>;
62
+ /**
63
+ * Create a logged fallback wrapper for a specific operation.
64
+ *
65
+ * @param operationName - Name of the operation
66
+ * @param fallbackValue - Default fallback value
67
+ * @returns A function that wraps operations with logging
68
+ */
69
+ export declare function createFallbackWrapper<T>(operationName: string, fallbackValue: T): (operation: () => Promise<T>, path?: string) => Promise<T>;
70
+ //# sourceMappingURL=error-handling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7E,4EAA4E;IAC5E,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAcD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,CAAC,CAAC,CAWZ;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,SAAS,EAAE,MAAM,CAAC,EAClB,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,eAAoB,GAC5B,CAAC,CAWH;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,GAAE,MAAU,EACtB,WAAW,GAAE,MAAY,GACxB,OAAO,CAAC,CAAC,CAAC,CAqBZ;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,CAAC,GACf,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAO5D"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Error Handling Utility
3
+ *
4
+ * Provides structured error handling with logging before fallbacks.
5
+ * Replaces silent .catch(() => '') patterns with logged fallbacks.
6
+ */
7
+ const defaultLogger = (message, context, error) => {
8
+ const errorMessage = error instanceof Error ? error.message : String(error);
9
+ console.warn(`[${context.operation}] ${message}: ${errorMessage}`, {
10
+ path: context.path,
11
+ ...context.details,
12
+ });
13
+ };
14
+ /**
15
+ * Execute an async operation with a fallback value, logging errors before fallback.
16
+ *
17
+ * This replaces patterns like:
18
+ * ```ts
19
+ * const content = await readFile(path).catch(() => '');
20
+ * ```
21
+ *
22
+ * With structured error handling:
23
+ * ```ts
24
+ * const content = await withFallback(
25
+ * () => readFile(path, 'utf-8'),
26
+ * '',
27
+ * { operation: 'readStateFile', path }
28
+ * );
29
+ * ```
30
+ *
31
+ * @param operation - Async operation to execute
32
+ * @param fallback - Value to return if operation fails
33
+ * @param context - Context for logging
34
+ * @param options - Options for error handling
35
+ * @returns Result of operation or fallback value
36
+ */
37
+ export async function withFallback(operation, fallback, context, options = {}) {
38
+ const { logWarning = true, logger = defaultLogger } = options;
39
+ try {
40
+ return await operation();
41
+ }
42
+ catch (error) {
43
+ if (logWarning) {
44
+ logger('Operation failed, using fallback', context, error);
45
+ }
46
+ return fallback;
47
+ }
48
+ }
49
+ /**
50
+ * Synchronous version of withFallback.
51
+ */
52
+ export function withFallbackSync(operation, fallback, context, options = {}) {
53
+ const { logWarning = true, logger = defaultLogger } = options;
54
+ try {
55
+ return operation();
56
+ }
57
+ catch (error) {
58
+ if (logWarning) {
59
+ logger('Operation failed, using fallback', context, error);
60
+ }
61
+ return fallback;
62
+ }
63
+ }
64
+ /**
65
+ * Execute an operation with retry logic and exponential backoff.
66
+ *
67
+ * @param operation - Async operation to execute
68
+ * @param context - Context for logging
69
+ * @param maxRetries - Maximum number of retries (default: 3)
70
+ * @param baseDelayMs - Base delay between retries in ms (default: 100)
71
+ * @returns Result of operation
72
+ * @throws Last error if all retries fail
73
+ */
74
+ export async function withRetry(operation, context, maxRetries = 3, baseDelayMs = 100) {
75
+ let lastError;
76
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
77
+ try {
78
+ return await operation();
79
+ }
80
+ catch (error) {
81
+ lastError = error;
82
+ if (attempt < maxRetries) {
83
+ const delay = baseDelayMs * Math.pow(2, attempt);
84
+ console.warn(`[${context.operation}] Attempt ${attempt + 1} failed, retrying in ${delay}ms...`, { path: context.path, error: error instanceof Error ? error.message : String(error) });
85
+ await sleep(delay);
86
+ }
87
+ }
88
+ }
89
+ throw lastError;
90
+ }
91
+ /**
92
+ * Create a logged fallback wrapper for a specific operation.
93
+ *
94
+ * @param operationName - Name of the operation
95
+ * @param fallbackValue - Default fallback value
96
+ * @returns A function that wraps operations with logging
97
+ */
98
+ export function createFallbackWrapper(operationName, fallbackValue) {
99
+ return async (operation, path) => {
100
+ return withFallback(operation, fallbackValue, {
101
+ operation: operationName,
102
+ path,
103
+ });
104
+ };
105
+ }
106
+ function sleep(ms) {
107
+ return new Promise((resolve) => setTimeout(resolve, ms));
108
+ }
109
+ //# sourceMappingURL=error-handling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handling.js","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * File Lock Utility
3
+ *
4
+ * Provides cross-platform file locking with retry logic.
5
+ * Uses proper-lockfile for reliable locking behavior.
6
+ */
7
+ export interface LockOptions {
8
+ /** Maximum time to wait for lock (ms) */
9
+ timeout?: number;
10
+ /** Time between retry attempts (ms) */
11
+ retryInterval?: number;
12
+ /** Maximum number of retries */
13
+ maxRetries?: number;
14
+ /** Stale lock timeout (ms) - locks older than this are considered stale */
15
+ stale?: number;
16
+ }
17
+ export declare class FileLockError extends Error {
18
+ readonly filePath: string;
19
+ readonly code: 'ELOCKED' | 'ETIMEOUT' | 'EUNLOCK';
20
+ constructor(message: string, filePath: string, code: 'ELOCKED' | 'ETIMEOUT' | 'EUNLOCK');
21
+ }
22
+ /**
23
+ * Acquire a lock on a file, execute an operation, then release the lock.
24
+ *
25
+ * @param filePath - Path to the file to lock
26
+ * @param operation - Async operation to perform while holding the lock
27
+ * @param options - Lock options
28
+ * @returns Result of the operation
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const result = await withFileLock('/path/to/file.json', async () => {
33
+ * const data = await readFile('/path/to/file.json', 'utf-8');
34
+ * const parsed = JSON.parse(data);
35
+ * parsed.count++;
36
+ * await writeFile('/path/to/file.json', JSON.stringify(parsed));
37
+ * return parsed;
38
+ * });
39
+ * ```
40
+ */
41
+ export declare function withFileLock<T>(filePath: string, operation: () => Promise<T>, options?: LockOptions): Promise<T>;
42
+ /**
43
+ * Check if a file is currently locked.
44
+ */
45
+ export declare function isFileLocked(filePath: string): boolean;
46
+ //# sourceMappingURL=file-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../../src/utils/file-lock.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAYD,qBAAa,aAAc,SAAQ,KAAK;aAGpB,QAAQ,EAAE,MAAM;aAChB,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS;gBAFxD,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS;CAK3D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,CAAC,CAAC,CA2EZ;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGtD"}