@sylphx/flow 1.0.1 → 1.0.3

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 (229) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +10 -9
  3. package/src/commands/codebase-command.ts +168 -0
  4. package/src/commands/flow-command.ts +1137 -0
  5. package/src/commands/flow-orchestrator.ts +296 -0
  6. package/src/commands/hook-command.ts +444 -0
  7. package/src/commands/init-command.ts +92 -0
  8. package/src/commands/init-core.ts +322 -0
  9. package/src/commands/knowledge-command.ts +161 -0
  10. package/src/commands/run-command.ts +120 -0
  11. package/src/components/benchmark-monitor.tsx +331 -0
  12. package/src/components/reindex-progress.tsx +261 -0
  13. package/src/composables/functional/index.ts +14 -0
  14. package/src/composables/functional/useEnvironment.ts +171 -0
  15. package/src/composables/functional/useFileSystem.ts +139 -0
  16. package/src/composables/index.ts +5 -0
  17. package/src/composables/useEnv.ts +13 -0
  18. package/src/composables/useRuntimeConfig.ts +27 -0
  19. package/src/composables/useTargetConfig.ts +45 -0
  20. package/src/config/ai-config.ts +376 -0
  21. package/src/config/constants.ts +35 -0
  22. package/src/config/index.ts +27 -0
  23. package/src/config/rules.ts +43 -0
  24. package/src/config/servers.ts +371 -0
  25. package/src/config/targets.ts +126 -0
  26. package/src/core/agent-loader.ts +141 -0
  27. package/src/core/agent-manager.ts +174 -0
  28. package/src/core/ai-sdk.ts +603 -0
  29. package/src/core/app-factory.ts +381 -0
  30. package/src/core/builtin-agents.ts +9 -0
  31. package/src/core/command-system.ts +550 -0
  32. package/src/core/config-system.ts +550 -0
  33. package/src/core/connection-pool.ts +390 -0
  34. package/src/core/di-container.ts +155 -0
  35. package/src/core/error-handling.ts +519 -0
  36. package/src/core/formatting/bytes.test.ts +115 -0
  37. package/src/core/formatting/bytes.ts +64 -0
  38. package/src/core/functional/async.ts +313 -0
  39. package/src/core/functional/either.ts +109 -0
  40. package/src/core/functional/error-handler.ts +135 -0
  41. package/src/core/functional/error-types.ts +311 -0
  42. package/src/core/functional/index.ts +19 -0
  43. package/src/core/functional/option.ts +142 -0
  44. package/src/core/functional/pipe.ts +189 -0
  45. package/src/core/functional/result.ts +204 -0
  46. package/src/core/functional/validation.ts +138 -0
  47. package/src/core/headless-display.ts +96 -0
  48. package/src/core/index.ts +6 -0
  49. package/src/core/installers/file-installer.ts +303 -0
  50. package/src/core/installers/mcp-installer.ts +213 -0
  51. package/src/core/interfaces/index.ts +22 -0
  52. package/src/core/interfaces/repository.interface.ts +91 -0
  53. package/src/core/interfaces/service.interface.ts +133 -0
  54. package/src/core/interfaces.ts +129 -0
  55. package/src/core/loop-controller.ts +200 -0
  56. package/src/core/result.ts +351 -0
  57. package/src/core/rule-loader.ts +147 -0
  58. package/src/core/rule-manager.ts +240 -0
  59. package/src/core/service-config.ts +252 -0
  60. package/src/core/session-service.ts +121 -0
  61. package/src/core/state-detector.ts +389 -0
  62. package/src/core/storage-factory.ts +115 -0
  63. package/src/core/stream-handler.ts +288 -0
  64. package/src/core/target-manager.ts +161 -0
  65. package/src/core/type-utils.ts +427 -0
  66. package/src/core/unified-storage.ts +456 -0
  67. package/src/core/upgrade-manager.ts +300 -0
  68. package/src/core/validation/limit.test.ts +155 -0
  69. package/src/core/validation/limit.ts +46 -0
  70. package/src/core/validation/query.test.ts +44 -0
  71. package/src/core/validation/query.ts +20 -0
  72. package/src/db/auto-migrate.ts +322 -0
  73. package/src/db/base-database-client.ts +144 -0
  74. package/src/db/cache-db.ts +218 -0
  75. package/src/db/cache-schema.ts +75 -0
  76. package/src/db/database.ts +70 -0
  77. package/src/db/index.ts +252 -0
  78. package/src/db/memory-db.ts +153 -0
  79. package/src/db/memory-schema.ts +29 -0
  80. package/src/db/schema.ts +289 -0
  81. package/src/db/session-repository.ts +733 -0
  82. package/src/domains/codebase/index.ts +5 -0
  83. package/src/domains/codebase/tools.ts +139 -0
  84. package/src/domains/index.ts +8 -0
  85. package/src/domains/knowledge/index.ts +10 -0
  86. package/src/domains/knowledge/resources.ts +537 -0
  87. package/src/domains/knowledge/tools.ts +174 -0
  88. package/src/domains/utilities/index.ts +6 -0
  89. package/src/domains/utilities/time/index.ts +5 -0
  90. package/src/domains/utilities/time/tools.ts +291 -0
  91. package/src/index.ts +211 -0
  92. package/src/services/agent-service.ts +273 -0
  93. package/src/services/claude-config-service.ts +252 -0
  94. package/src/services/config-service.ts +258 -0
  95. package/src/services/evaluation-service.ts +271 -0
  96. package/src/services/functional/evaluation-logic.ts +296 -0
  97. package/src/services/functional/file-processor.ts +273 -0
  98. package/src/services/functional/index.ts +12 -0
  99. package/src/services/index.ts +13 -0
  100. package/src/services/mcp-service.ts +432 -0
  101. package/src/services/memory.service.ts +476 -0
  102. package/src/services/search/base-indexer.ts +156 -0
  103. package/src/services/search/codebase-indexer-types.ts +38 -0
  104. package/src/services/search/codebase-indexer.ts +647 -0
  105. package/src/services/search/embeddings-provider.ts +455 -0
  106. package/src/services/search/embeddings.ts +316 -0
  107. package/src/services/search/functional-indexer.ts +323 -0
  108. package/src/services/search/index.ts +27 -0
  109. package/src/services/search/indexer.ts +380 -0
  110. package/src/services/search/knowledge-indexer.ts +422 -0
  111. package/src/services/search/semantic-search.ts +244 -0
  112. package/src/services/search/tfidf.ts +559 -0
  113. package/src/services/search/unified-search-service.ts +888 -0
  114. package/src/services/smart-config-service.ts +385 -0
  115. package/src/services/storage/cache-storage.ts +487 -0
  116. package/src/services/storage/drizzle-storage.ts +581 -0
  117. package/src/services/storage/index.ts +15 -0
  118. package/src/services/storage/lancedb-vector-storage.ts +494 -0
  119. package/src/services/storage/memory-storage.ts +268 -0
  120. package/src/services/storage/separated-storage.ts +467 -0
  121. package/src/services/storage/vector-storage.ts +13 -0
  122. package/src/shared/agents/index.ts +63 -0
  123. package/src/shared/files/index.ts +99 -0
  124. package/src/shared/index.ts +32 -0
  125. package/src/shared/logging/index.ts +24 -0
  126. package/src/shared/processing/index.ts +153 -0
  127. package/src/shared/types/index.ts +25 -0
  128. package/src/targets/claude-code.ts +574 -0
  129. package/src/targets/functional/claude-code-logic.ts +185 -0
  130. package/src/targets/functional/index.ts +6 -0
  131. package/src/targets/opencode.ts +529 -0
  132. package/src/types/agent.types.ts +32 -0
  133. package/src/types/api/batch.ts +108 -0
  134. package/src/types/api/errors.ts +118 -0
  135. package/src/types/api/index.ts +55 -0
  136. package/src/types/api/requests.ts +76 -0
  137. package/src/types/api/responses.ts +180 -0
  138. package/src/types/api/websockets.ts +85 -0
  139. package/src/types/api.types.ts +9 -0
  140. package/src/types/benchmark.ts +49 -0
  141. package/src/types/cli.types.ts +87 -0
  142. package/src/types/common.types.ts +35 -0
  143. package/src/types/database.types.ts +510 -0
  144. package/src/types/mcp-config.types.ts +448 -0
  145. package/src/types/mcp.types.ts +69 -0
  146. package/src/types/memory-types.ts +63 -0
  147. package/src/types/provider.types.ts +28 -0
  148. package/src/types/rule.types.ts +24 -0
  149. package/src/types/session.types.ts +214 -0
  150. package/src/types/target-config.types.ts +295 -0
  151. package/src/types/target.types.ts +140 -0
  152. package/src/types/todo.types.ts +25 -0
  153. package/src/types.ts +40 -0
  154. package/src/utils/advanced-tokenizer.ts +191 -0
  155. package/src/utils/agent-enhancer.ts +114 -0
  156. package/src/utils/ai-model-fetcher.ts +19 -0
  157. package/src/utils/async-file-operations.ts +516 -0
  158. package/src/utils/audio-player.ts +345 -0
  159. package/src/utils/cli-output.ts +266 -0
  160. package/src/utils/codebase-helpers.ts +211 -0
  161. package/src/utils/console-ui.ts +79 -0
  162. package/src/utils/database-errors.ts +140 -0
  163. package/src/utils/debug-logger.ts +49 -0
  164. package/src/utils/error-handler.ts +53 -0
  165. package/src/utils/file-operations.ts +310 -0
  166. package/src/utils/file-scanner.ts +259 -0
  167. package/src/utils/functional/array.ts +355 -0
  168. package/src/utils/functional/index.ts +15 -0
  169. package/src/utils/functional/object.ts +279 -0
  170. package/src/utils/functional/string.ts +281 -0
  171. package/src/utils/functional.ts +543 -0
  172. package/src/utils/help.ts +20 -0
  173. package/src/utils/immutable-cache.ts +106 -0
  174. package/src/utils/index.ts +78 -0
  175. package/src/utils/jsonc.ts +158 -0
  176. package/src/utils/logger.ts +396 -0
  177. package/src/utils/mcp-config.ts +249 -0
  178. package/src/utils/memory-tui.ts +414 -0
  179. package/src/utils/models-dev.ts +91 -0
  180. package/src/utils/notifications.ts +169 -0
  181. package/src/utils/object-utils.ts +51 -0
  182. package/src/utils/parallel-operations.ts +487 -0
  183. package/src/utils/paths.ts +143 -0
  184. package/src/utils/process-manager.ts +155 -0
  185. package/src/utils/prompts.ts +120 -0
  186. package/src/utils/search-tool-builder.ts +214 -0
  187. package/src/utils/secret-utils.ts +179 -0
  188. package/src/utils/security.ts +537 -0
  189. package/src/utils/session-manager.ts +168 -0
  190. package/src/utils/session-title.ts +87 -0
  191. package/src/utils/settings.ts +182 -0
  192. package/src/utils/simplified-errors.ts +410 -0
  193. package/src/utils/sync-utils.ts +159 -0
  194. package/src/utils/target-config.ts +570 -0
  195. package/src/utils/target-utils.ts +394 -0
  196. package/src/utils/template-engine.ts +94 -0
  197. package/src/utils/test-audio.ts +71 -0
  198. package/src/utils/todo-context.ts +46 -0
  199. package/src/utils/token-counter.ts +288 -0
  200. package/dist/index.d.ts +0 -10
  201. package/dist/index.js +0 -59554
  202. package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
  203. package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
  204. package/dist/shared/chunk-25dwp0dp.js +0 -89
  205. package/dist/shared/chunk-3pjb6063.js +0 -208
  206. package/dist/shared/chunk-4d6ydpw7.js +0 -2854
  207. package/dist/shared/chunk-4wjcadjk.js +0 -225
  208. package/dist/shared/chunk-5j4w74t6.js +0 -30
  209. package/dist/shared/chunk-5j8m3dh3.js +0 -58
  210. package/dist/shared/chunk-5thh3qem.js +0 -91
  211. package/dist/shared/chunk-6g9xy73m.js +0 -252
  212. package/dist/shared/chunk-7eq34c42.js +0 -23
  213. package/dist/shared/chunk-c2gwgx3r.js +0 -115
  214. package/dist/shared/chunk-cjd3mk4c.js +0 -1320
  215. package/dist/shared/chunk-g5cv6703.js +0 -368
  216. package/dist/shared/chunk-hpkhykhq.js +0 -574
  217. package/dist/shared/chunk-m2322pdk.js +0 -122
  218. package/dist/shared/chunk-nd5fdvaq.js +0 -26
  219. package/dist/shared/chunk-pgd3m6zf.js +0 -108
  220. package/dist/shared/chunk-qk8n91hw.js +0 -494
  221. package/dist/shared/chunk-rkkn8szp.js +0 -16855
  222. package/dist/shared/chunk-t16rfxh0.js +0 -61
  223. package/dist/shared/chunk-t4fbfa5v.js +0 -19
  224. package/dist/shared/chunk-t77h86w6.js +0 -276
  225. package/dist/shared/chunk-v0ez4aef.js +0 -71
  226. package/dist/shared/chunk-v29j2r3s.js +0 -32051
  227. package/dist/shared/chunk-vfbc6ew5.js +0 -765
  228. package/dist/shared/chunk-vmeqwm1c.js +0 -204
  229. package/dist/shared/chunk-x66eh37x.js +0 -137
@@ -0,0 +1,516 @@
1
+ /**
2
+ * Async File Operations Utility
3
+ *
4
+ * Provides async wrappers for common file operations with better error handling,
5
+ * concurrency control, and performance optimizations
6
+ */
7
+
8
+ import { createHash } from 'node:crypto';
9
+ import * as fs from 'node:fs/promises';
10
+ import * as path from 'node:path';
11
+
12
+ export interface FileOperationOptions {
13
+ encoding?: BufferEncoding;
14
+ signal?: AbortSignal;
15
+ retryAttempts?: number;
16
+ retryDelay?: number;
17
+ timeout?: number;
18
+ }
19
+
20
+ export interface FileStats {
21
+ size: number;
22
+ created: Date;
23
+ modified: Date;
24
+ accessed: Date;
25
+ isFile: boolean;
26
+ isDirectory: boolean;
27
+ permissions: string;
28
+ }
29
+
30
+ export interface DirectoryEntry {
31
+ name: string;
32
+ path: string;
33
+ isFile: boolean;
34
+ isDirectory: boolean;
35
+ stats?: FileStats;
36
+ }
37
+
38
+ export interface CopyOptions {
39
+ overwrite?: boolean;
40
+ preserveTimestamps?: boolean;
41
+ filter?: (source: string, dest: string) => boolean;
42
+ concurrency?: number;
43
+ }
44
+
45
+ export interface ReadDirOptions {
46
+ withFileTypes?: boolean;
47
+ recursive?: boolean;
48
+ includeStats?: boolean;
49
+ filter?: (entry: DirectoryEntry) => boolean;
50
+ maxDepth?: number;
51
+ }
52
+
53
+ /**
54
+ * Async file operations with error handling and retries
55
+ */
56
+ export class AsyncFileOperations {
57
+ private readonly defaultOptions: Required<FileOperationOptions> = {
58
+ encoding: 'utf8',
59
+ retryAttempts: 3,
60
+ retryDelay: 1000,
61
+ timeout: 30000,
62
+ };
63
+
64
+ /**
65
+ * Read file content with retries
66
+ */
67
+ async readFile(filePath: string, options: FileOperationOptions = {}): Promise<string | Buffer> {
68
+ const opts = { ...this.defaultOptions, ...options };
69
+
70
+ return this.withRetry(
71
+ async () => {
72
+ if (opts.timeout) {
73
+ return await this.withTimeout(() => fs.readFile(filePath, opts.encoding), opts.timeout);
74
+ }
75
+ return await fs.readFile(filePath, opts.encoding);
76
+ },
77
+ opts.retryAttempts,
78
+ opts.retryDelay
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Write file content with retries and backup
84
+ */
85
+ async writeFile(
86
+ filePath: string,
87
+ content: string | Buffer | NodeJS.ArrayBufferView,
88
+ options: FileOperationOptions & { createBackup?: boolean } = {}
89
+ ): Promise<void> {
90
+ const opts = { ...this.defaultOptions, createBackup: true, ...options };
91
+
92
+ // Create backup if requested and file exists
93
+ if (opts.createBackup && (await this.exists(filePath))) {
94
+ await this.createBackup(filePath);
95
+ }
96
+
97
+ return this.withRetry(
98
+ async () => {
99
+ // Ensure directory exists
100
+ await this.ensureDir(path.dirname(filePath));
101
+
102
+ if (opts.timeout) {
103
+ return await this.withTimeout(
104
+ () => fs.writeFile(filePath, content, opts.encoding),
105
+ opts.timeout
106
+ );
107
+ }
108
+ return await fs.writeFile(filePath, content, opts.encoding);
109
+ },
110
+ opts.retryAttempts,
111
+ opts.retryDelay
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Append to file with retries
117
+ */
118
+ async appendFile(
119
+ filePath: string,
120
+ content: string | Buffer,
121
+ options: FileOperationOptions = {}
122
+ ): Promise<void> {
123
+ const opts = { ...this.defaultOptions, ...options };
124
+
125
+ return this.withRetry(
126
+ async () => {
127
+ // Ensure directory exists
128
+ await this.ensureDir(path.dirname(filePath));
129
+
130
+ if (opts.timeout) {
131
+ return await this.withTimeout(
132
+ () => fs.appendFile(filePath, content, opts.encoding),
133
+ opts.timeout
134
+ );
135
+ }
136
+ return await fs.appendFile(filePath, content, opts.encoding);
137
+ },
138
+ opts.retryAttempts,
139
+ opts.retryDelay
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Check if file or directory exists
145
+ */
146
+ async exists(filePath: string): Promise<boolean> {
147
+ try {
148
+ await fs.access(filePath);
149
+ return true;
150
+ } catch {
151
+ return false;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Get file statistics
157
+ */
158
+ async getStats(filePath: string): Promise<FileStats> {
159
+ const stats = await fs.stat(filePath);
160
+
161
+ return {
162
+ size: stats.size,
163
+ created: stats.birthtime,
164
+ modified: stats.mtime,
165
+ accessed: stats.atime,
166
+ isFile: stats.isFile(),
167
+ isDirectory: stats.isDirectory(),
168
+ permissions: stats.mode.toString(8),
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Create directory recursively
174
+ */
175
+ async ensureDir(dirPath: string): Promise<void> {
176
+ try {
177
+ await fs.mkdir(dirPath, { recursive: true });
178
+ } catch (error) {
179
+ // Check if directory already exists
180
+ if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST')) {
181
+ throw error;
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Remove file or directory recursively
188
+ */
189
+ async remove(
190
+ targetPath: string,
191
+ options: { recursive?: boolean; force?: boolean } = {}
192
+ ): Promise<void> {
193
+ const { recursive = true, force = false } = options;
194
+
195
+ try {
196
+ const stats = await fs.lstat(targetPath);
197
+
198
+ if (stats.isDirectory() && recursive) {
199
+ await fs.rm(targetPath, { recursive: true, force });
200
+ } else if (stats.isDirectory() && !recursive) {
201
+ throw new Error('Cannot remove directory without recursive option');
202
+ } else {
203
+ await fs.unlink(targetPath);
204
+ }
205
+ } catch (error) {
206
+ if (!force || !(error instanceof Error && 'code' in error && error.code === 'ENOENT')) {
207
+ throw error;
208
+ }
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Copy file or directory
214
+ */
215
+ async copy(source: string, destination: string, options: CopyOptions = {}): Promise<void> {
216
+ const { overwrite = false, preserveTimestamps = true, filter, concurrency = 10 } = options;
217
+
218
+ const sourceStats = await fs.lstat(source);
219
+
220
+ if (sourceStats.isDirectory()) {
221
+ await this.copyDirectory(source, destination, {
222
+ overwrite,
223
+ preserveTimestamps,
224
+ filter,
225
+ concurrency,
226
+ });
227
+ } else {
228
+ await this.copyFile(source, destination, {
229
+ overwrite,
230
+ preserveTimestamps,
231
+ filter,
232
+ });
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Move file or directory
238
+ */
239
+ async move(
240
+ source: string,
241
+ destination: string,
242
+ options: { overwrite?: boolean } = {}
243
+ ): Promise<void> {
244
+ const { overwrite = false } = options;
245
+
246
+ // Check if destination exists
247
+ if ((await this.exists(destination)) && !overwrite) {
248
+ throw new Error(`Destination already exists: ${destination}`);
249
+ }
250
+
251
+ try {
252
+ await fs.rename(source, destination);
253
+ } catch (error) {
254
+ // Fallback to copy + delete if rename fails (cross-device)
255
+ if (error instanceof Error && 'code' in error && error.code === 'EXDEV') {
256
+ await this.copy(source, destination, { overwrite });
257
+ await this.remove(source, { recursive: true });
258
+ } else {
259
+ throw error;
260
+ }
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Read directory contents
266
+ */
267
+ async readDir(dirPath: string, options: ReadDirOptions = {}): Promise<DirectoryEntry[]> {
268
+ const {
269
+ withFileTypes = false,
270
+ recursive = false,
271
+ includeStats = false,
272
+ filter,
273
+ maxDepth = 10,
274
+ } = options;
275
+
276
+ const results: DirectoryEntry[] = [];
277
+
278
+ const processDirectory = async (currentPath: string, currentDepth: number): Promise<void> => {
279
+ if (currentDepth > maxDepth) {
280
+ return;
281
+ }
282
+
283
+ try {
284
+ const entries = await fs.readdir(currentPath, { withFileTypes });
285
+
286
+ const promises = entries.map(async (entry) => {
287
+ const fullPath = path.join(currentPath, entry.name);
288
+ const directoryEntry: DirectoryEntry = {
289
+ name: entry.name,
290
+ path: fullPath,
291
+ isFile: entry.isFile(),
292
+ isDirectory: entry.isDirectory(),
293
+ };
294
+
295
+ if (includeStats) {
296
+ try {
297
+ directoryEntry.stats = await this.getStats(fullPath);
298
+ } catch (_error) {
299
+ // Skip if stats can't be retrieved
300
+ }
301
+ }
302
+
303
+ // Apply filter if provided
304
+ if (filter && !filter(directoryEntry)) {
305
+ return;
306
+ }
307
+
308
+ results.push(directoryEntry);
309
+
310
+ // Recursively process subdirectories
311
+ if (recursive && entry.isDirectory()) {
312
+ await processDirectory(fullPath, currentDepth + 1);
313
+ }
314
+ });
315
+
316
+ await Promise.all(promises);
317
+ } catch (error) {
318
+ throw new Error(`Failed to read directory ${currentPath}: ${error}`);
319
+ }
320
+ };
321
+
322
+ await processDirectory(dirPath, 0);
323
+ return results;
324
+ }
325
+
326
+ /**
327
+ * Calculate file hash
328
+ */
329
+ async calculateHash(filePath: string, algorithm = 'sha256'): Promise<string> {
330
+ const content = await this.readFile(filePath);
331
+ return createHash(algorithm)
332
+ .update(content as Buffer)
333
+ .digest('hex');
334
+ }
335
+
336
+ /**
337
+ * Watch file or directory for changes
338
+ */
339
+ async watch(
340
+ targetPath: string,
341
+ callback: (eventType: string, filename: string | null) => void,
342
+ options: { recursive?: boolean } = {}
343
+ ): Promise<fs.FSWatcher> {
344
+ const { recursive = false } = options;
345
+
346
+ return new Promise((resolve, reject) => {
347
+ try {
348
+ const watcher = fs.watch(targetPath, { recursive }, callback);
349
+ resolve(watcher);
350
+ } catch (error) {
351
+ reject(error);
352
+ }
353
+ });
354
+ }
355
+
356
+ /**
357
+ * Create backup of file
358
+ */
359
+ private async createBackup(filePath: string): Promise<string> {
360
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
361
+ const backupPath = `${filePath}.backup.${timestamp}`;
362
+ await this.copy(filePath, backupPath);
363
+ return backupPath;
364
+ }
365
+
366
+ /**
367
+ * Copy file with options
368
+ */
369
+ private async copyFile(
370
+ source: string,
371
+ destination: string,
372
+ options: {
373
+ overwrite?: boolean;
374
+ preserveTimestamps?: boolean;
375
+ filter?: (source: string, dest: string) => boolean;
376
+ }
377
+ ): Promise<void> {
378
+ const { overwrite = false, preserveTimestamps = true, filter } = options;
379
+
380
+ // Apply filter
381
+ if (filter && !filter(source, destination)) {
382
+ return;
383
+ }
384
+
385
+ // Check if destination exists
386
+ if ((await this.exists(destination)) && !overwrite) {
387
+ throw new Error(`Destination already exists: ${destination}`);
388
+ }
389
+
390
+ // Ensure destination directory exists
391
+ await this.ensureDir(path.dirname(destination));
392
+
393
+ // Copy file
394
+ await fs.copyFile(source, destination);
395
+
396
+ // Preserve timestamps if requested
397
+ if (preserveTimestamps) {
398
+ const sourceStats = await fs.stat(source);
399
+ await fs.utimes(destination, sourceStats.atime, sourceStats.mtime);
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Copy directory with options
405
+ */
406
+ private async copyDirectory(
407
+ source: string,
408
+ destination: string,
409
+ options: {
410
+ overwrite?: boolean;
411
+ preserveTimestamps?: boolean;
412
+ filter?: (source: string, dest: string) => boolean;
413
+ concurrency?: number;
414
+ }
415
+ ): Promise<void> {
416
+ const { concurrency = 10 } = options;
417
+
418
+ // Ensure destination directory exists
419
+ await this.ensureDir(destination);
420
+
421
+ // Get all entries
422
+ const entries = await this.readDir(source, { recursive: true });
423
+
424
+ // Process entries in batches
425
+ for (let i = 0; i < entries.length; i += concurrency) {
426
+ const batch = entries.slice(i, i + concurrency);
427
+
428
+ await Promise.all(
429
+ batch.map(async (entry) => {
430
+ const relativePath = path.relative(source, entry.path);
431
+ const destPath = path.join(destination, relativePath);
432
+
433
+ if (entry.isFile) {
434
+ await this.copyFile(entry.path, destPath, options);
435
+ } else if (entry.isDirectory) {
436
+ await this.ensureDir(destPath);
437
+ }
438
+ })
439
+ );
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Execute operation with retry logic
445
+ */
446
+ private async withRetry<T>(
447
+ operation: () => Promise<T>,
448
+ attempts: number,
449
+ delay: number
450
+ ): Promise<T> {
451
+ let lastError: Error;
452
+
453
+ for (let i = 0; i < attempts; i++) {
454
+ try {
455
+ return await operation();
456
+ } catch (error) {
457
+ lastError = error instanceof Error ? error : new Error(String(error));
458
+
459
+ if (i < attempts - 1) {
460
+ await this.sleep(delay * 2 ** i); // Exponential backoff
461
+ }
462
+ }
463
+ }
464
+
465
+ throw lastError!;
466
+ }
467
+
468
+ /**
469
+ * Execute operation with timeout
470
+ */
471
+ private async withTimeout<T>(operation: () => Promise<T>, timeoutMs: number): Promise<T> {
472
+ const timeoutPromise = new Promise<never>((_, reject) => {
473
+ setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs);
474
+ });
475
+
476
+ return Promise.race([operation(), timeoutPromise]);
477
+ }
478
+
479
+ /**
480
+ * Sleep utility
481
+ */
482
+ private sleep(ms: number): Promise<void> {
483
+ return new Promise((resolve) => setTimeout(resolve, ms));
484
+ }
485
+ }
486
+
487
+ // Export singleton instance
488
+ export const asyncFileOps = new AsyncFileOperations();
489
+
490
+ /**
491
+ * Convenience functions for common operations
492
+ */
493
+ export const readFile = (filePath: string, options?: FileOperationOptions) =>
494
+ asyncFileOps.readFile(filePath, options);
495
+
496
+ export const writeFile = (
497
+ filePath: string,
498
+ content: string | Buffer,
499
+ options?: FileOperationOptions
500
+ ) => asyncFileOps.writeFile(filePath, content, options);
501
+
502
+ export const exists = (filePath: string) => asyncFileOps.exists(filePath);
503
+
504
+ export const ensureDir = (dirPath: string) => asyncFileOps.ensureDir(dirPath);
505
+
506
+ export const remove = (targetPath: string, options?: { recursive?: boolean; force?: boolean }) =>
507
+ asyncFileOps.remove(targetPath, options);
508
+
509
+ export const copy = (source: string, destination: string, options?: CopyOptions) =>
510
+ asyncFileOps.copy(source, destination, options);
511
+
512
+ export const move = (source: string, destination: string, options?: { overwrite?: boolean }) =>
513
+ asyncFileOps.move(source, destination, options);
514
+
515
+ export const readDir = (dirPath: string, options?: ReadDirOptions) =>
516
+ asyncFileOps.readDir(dirPath, options);