@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,487 @@
1
+ /**
2
+ * Parallel Operations Utility
3
+ * Provides utilities for executing async operations in parallel with proper error handling and resource management
4
+ */
5
+
6
+ import { chunk } from './functional/array.js';
7
+ import { logger } from './logger.js';
8
+
9
+ /**
10
+ * Configuration for parallel operations
11
+ */
12
+ export interface ParallelOptions {
13
+ /** Maximum number of concurrent operations */
14
+ concurrency?: number;
15
+ /** Whether to continue on error or stop immediately */
16
+ continueOnError?: boolean;
17
+ /** Timeout for individual operations (ms) */
18
+ timeout?: number;
19
+ /** Delay between batches (ms) */
20
+ batchDelay?: number;
21
+ /** Progress callback */
22
+ onProgress?: (completed: number, total: number, current?: unknown) => void;
23
+ }
24
+
25
+ /**
26
+ * Result of a parallel operation
27
+ */
28
+ export interface ParallelResult<T> {
29
+ /** Successful results */
30
+ successful: Array<{ index: number; result: T; item: unknown }>;
31
+ /** Failed operations */
32
+ failed: Array<{ index: number; error: Error; item: unknown }>;
33
+ /** Total number of operations */
34
+ total: number;
35
+ /** Success count */
36
+ successCount: number;
37
+ /** Failure count */
38
+ failureCount: number;
39
+ /** Execution time in milliseconds */
40
+ duration: number;
41
+ }
42
+
43
+ /**
44
+ * Batch operation configuration
45
+ */
46
+ export interface BatchOptions<T> extends ParallelOptions {
47
+ /** Function to process each batch */
48
+ processor: (batch: T[], batchIndex: number) => Promise<unknown[]>;
49
+ /** Batch size */
50
+ batchSize?: number;
51
+ }
52
+
53
+ /**
54
+ * Execute operations in parallel with controlled concurrency
55
+ */
56
+ export async function parallel<T>(
57
+ items: unknown[],
58
+ operation: (item: unknown, index: number) => Promise<T>,
59
+ options: ParallelOptions = {}
60
+ ): Promise<ParallelResult<T>> {
61
+ const startTime = Date.now();
62
+ const { concurrency = 10, continueOnError = true, timeout = 30000, onProgress } = options;
63
+
64
+ logger.debug('Starting parallel operations', {
65
+ itemCount: items.length,
66
+ concurrency,
67
+ continueOnError,
68
+ timeout,
69
+ });
70
+
71
+ // FUNCTIONAL: Use chunk and reduce to accumulate results instead of imperative loop
72
+ const batches = chunk(concurrency)(items);
73
+
74
+ const results = await batches.reduce<Promise<ParallelResult<T>>>(
75
+ async (accPromise, batch, batchIdx) => {
76
+ const acc = await accPromise;
77
+
78
+ const batchPromises = batch.map(async (item, batchIndex) => {
79
+ const globalIndex = batchIdx * concurrency + batchIndex;
80
+
81
+ try {
82
+ // Add timeout to individual operations
83
+ const operationPromise = operation(item, globalIndex);
84
+ const timeoutPromise = new Promise<never>((_, reject) => {
85
+ setTimeout(() => reject(new Error(`Operation timeout after ${timeout}ms`)), timeout);
86
+ });
87
+
88
+ const result = await Promise.race([operationPromise, timeoutPromise]);
89
+
90
+ // Report progress
91
+ if (onProgress) {
92
+ onProgress(acc.successCount + acc.failureCount + 1, items.length, item);
93
+ }
94
+
95
+ return { success: true as const, index: globalIndex, result, item };
96
+ } catch (error) {
97
+ const errorObj = error instanceof Error ? error : new Error(String(error));
98
+
99
+ logger.warn('Parallel operation failed', {
100
+ index: globalIndex,
101
+ error: errorObj.message,
102
+ item: typeof item === 'object' ? JSON.stringify(item) : item,
103
+ });
104
+
105
+ // Re-throw if not continuing on error
106
+ if (!continueOnError) {
107
+ throw errorObj;
108
+ }
109
+
110
+ return { success: false as const, index: globalIndex, error: errorObj, item };
111
+ }
112
+ });
113
+
114
+ // Wait for current batch to complete
115
+ const batchResults = await Promise.all(batchPromises);
116
+
117
+ // FUNCTIONAL: Partition results into successful/failed
118
+ const newSuccessful = batchResults
119
+ .filter((r) => r.success)
120
+ .map((r) => ({ index: r.index, result: r.result, item: r.item }));
121
+ const newFailed = batchResults
122
+ .filter((r) => !r.success)
123
+ .map((r) => ({ index: r.index, error: r.error, item: r.item }));
124
+
125
+ // Add delay between batches if specified
126
+ if (options.batchDelay && batchIdx < batches.length - 1) {
127
+ await new Promise((resolve) => setTimeout(resolve, options.batchDelay!));
128
+ }
129
+
130
+ return {
131
+ successful: [...acc.successful, ...newSuccessful],
132
+ failed: [...acc.failed, ...newFailed],
133
+ total: items.length,
134
+ successCount: acc.successCount + newSuccessful.length,
135
+ failureCount: acc.failureCount + newFailed.length,
136
+ duration: 0, // Will be set after reduce completes
137
+ };
138
+ },
139
+ Promise.resolve({
140
+ successful: [],
141
+ failed: [],
142
+ total: items.length,
143
+ successCount: 0,
144
+ failureCount: 0,
145
+ duration: 0,
146
+ })
147
+ );
148
+
149
+ // Set final duration
150
+ results.duration = Date.now() - startTime;
151
+
152
+ logger.info('Parallel operations completed', {
153
+ total: results.total,
154
+ successCount: results.successCount,
155
+ failureCount: results.failureCount,
156
+ duration: results.duration,
157
+ successRate: `${((results.successCount / results.total) * 100).toFixed(1)}%`,
158
+ });
159
+
160
+ return results;
161
+ }
162
+
163
+ /**
164
+ * Execute operations in parallel with automatic retry for failed operations
165
+ */
166
+ export async function parallelWithRetry<T>(
167
+ items: unknown[],
168
+ operation: (item: unknown, index: number) => Promise<T>,
169
+ options: ParallelOptions & { maxRetries?: number; retryDelay?: number } = {}
170
+ ): Promise<ParallelResult<T>> {
171
+ const { maxRetries = 3, retryDelay = 1000, ...parallelOptions } = options;
172
+
173
+ const result = await parallel(items, operation, parallelOptions);
174
+
175
+ // FUNCTIONAL: Retry failed operations using reduce instead of for loop
176
+ const attempts = Array.from({ length: maxRetries }, (_, i) => i + 1);
177
+
178
+ const finalResult = await attempts.reduce<Promise<ParallelResult<T>>>(
179
+ async (accPromise, attempt) => {
180
+ const acc = await accPromise;
181
+
182
+ if (acc.failed.length === 0) {
183
+ return acc; // No more failures to retry
184
+ }
185
+
186
+ logger.info(`Retrying failed operations (attempt ${attempt}/${maxRetries})`, {
187
+ failedCount: acc.failed.length,
188
+ retryDelay,
189
+ });
190
+
191
+ // Wait before retry
192
+ if (retryDelay > 0) {
193
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
194
+ }
195
+
196
+ // Retry only failed items
197
+ const failedItems = acc.failed.map((f) => f.item);
198
+ const retryResult = await parallel(failedItems, operation, parallelOptions);
199
+
200
+ // Merge results immutably
201
+ return {
202
+ successful: [...acc.successful, ...retryResult.successful],
203
+ failed: retryResult.failed,
204
+ total: acc.total,
205
+ successCount: acc.successful.length + retryResult.successful.length,
206
+ failureCount: retryResult.failed.length,
207
+ duration: acc.duration,
208
+ };
209
+ },
210
+ Promise.resolve(result)
211
+ );
212
+
213
+ return finalResult;
214
+ }
215
+
216
+ /**
217
+ * Process items in batches with parallel execution within each batch
218
+ */
219
+ export async function batchParallel<T, R>(
220
+ items: T[],
221
+ processor: (batch: T[], batchIndex: number) => Promise<R[]>,
222
+ options: BatchOptions<T> = {}
223
+ ): Promise<R[]> {
224
+ const { batchSize = 50, concurrency = 3, continueOnError = true, onProgress } = options;
225
+
226
+ // FUNCTIONAL: Use chunk utility instead of for loop
227
+ const batches = chunk(batchSize)(items);
228
+
229
+ logger.info('Starting batch parallel processing', {
230
+ totalItems: items.length,
231
+ batchSize,
232
+ batchCount: batches.length,
233
+ concurrency,
234
+ });
235
+
236
+ const processBatch = async (batch: T[], batchIndex: number): Promise<R[]> => {
237
+ try {
238
+ const result = await processor(batch, batchIndex);
239
+
240
+ if (onProgress) {
241
+ onProgress((batchIndex + 1) * batchSize, items.length, batch);
242
+ }
243
+
244
+ return result;
245
+ } catch (error) {
246
+ logger.error('Batch processing failed', {
247
+ batchIndex,
248
+ batchSize: batch.length,
249
+ error: (error as Error).message,
250
+ });
251
+
252
+ if (!continueOnError) {
253
+ throw error;
254
+ }
255
+
256
+ return [];
257
+ }
258
+ };
259
+
260
+ // Process batches in parallel
261
+ const results = await parallel(batches, (batch, index) => processBatch(batch as T[], index), {
262
+ concurrency,
263
+ continueOnError,
264
+ onProgress,
265
+ });
266
+
267
+ // Flatten successful results
268
+ return results.successful.flatMap((r) => r.result as R[]);
269
+ }
270
+
271
+ /**
272
+ * Parallel map operation
273
+ */
274
+ export async function parallelMap<T, R>(
275
+ items: T[],
276
+ mapper: (item: T, index: number) => Promise<R>,
277
+ options?: ParallelOptions
278
+ ): Promise<R[]> {
279
+ const result = await parallel(items, mapper, options);
280
+
281
+ if (result.failureCount > 0) {
282
+ logger.warn('parallelMap had failures', {
283
+ total: result.total,
284
+ failures: result.failureCount,
285
+ });
286
+ }
287
+
288
+ // Return results in original order
289
+ const orderedResults = new Array(result.total);
290
+ result.successful.forEach(({ index, result }) => {
291
+ orderedResults[index] = result;
292
+ });
293
+
294
+ return orderedResults.filter(Boolean);
295
+ }
296
+
297
+ /**
298
+ * Parallel filter operation
299
+ */
300
+ export async function parallelFilter<T>(
301
+ items: T[],
302
+ predicate: (item: T, index: number) => Promise<boolean>,
303
+ options?: ParallelOptions
304
+ ): Promise<T[]> {
305
+ const results = await parallelMap(
306
+ items,
307
+ async (item, index) => ({
308
+ item,
309
+ passes: await predicate(item, index),
310
+ }),
311
+ options
312
+ );
313
+
314
+ return results.filter((r) => r.passes).map((r) => r.item);
315
+ }
316
+
317
+ /**
318
+ * Parallel reduce operation (for associative operations)
319
+ */
320
+ export async function parallelReduce<T>(
321
+ items: T[],
322
+ reducer: (acc: T, item: T) => Promise<T>,
323
+ initialValue: T,
324
+ options?: ParallelOptions
325
+ ): Promise<T> {
326
+ if (items.length === 0) {
327
+ return initialValue;
328
+ }
329
+
330
+ // For small arrays, use regular reduce
331
+ if (items.length <= 100) {
332
+ // FUNCTIONAL: Use reduce instead of for-of loop
333
+ return await items.reduce(async (accPromise, item) => {
334
+ const acc = await accPromise;
335
+ return await reducer(acc, item);
336
+ }, Promise.resolve(initialValue));
337
+ }
338
+
339
+ // For large arrays, split into chunks and reduce in parallel
340
+ const chunkSize = Math.ceil(items.length / (options?.concurrency || 10));
341
+
342
+ // FUNCTIONAL: Use chunk utility instead of for loop
343
+ const chunks = chunk(chunkSize)(items);
344
+
345
+ const chunkResults = await parallelMap(
346
+ chunks,
347
+ async (chunkItems) => {
348
+ // FUNCTIONAL: Use reduce instead of for-of loop
349
+ return await chunkItems.reduce(async (accPromise, item) => {
350
+ const acc = await accPromise;
351
+ return await reducer(acc, item);
352
+ }, Promise.resolve(initialValue));
353
+ },
354
+ options
355
+ );
356
+
357
+ // FUNCTIONAL: Combine chunk results using reduce instead of for-of loop
358
+ return await chunkResults.reduce(async (accPromise, chunkResult) => {
359
+ const acc = await accPromise;
360
+ return await reducer(acc, chunkResult);
361
+ }, Promise.resolve(initialValue));
362
+ }
363
+
364
+ /**
365
+ * Execute multiple async operations in parallel and return all results
366
+ */
367
+ export async function all<T>(operations: Array<() => Promise<T>>): Promise<T[]> {
368
+ const promises = operations.map((op) => op());
369
+ return Promise.all(promises);
370
+ }
371
+
372
+ /**
373
+ * Execute multiple async operations in parallel and return first successful result
374
+ */
375
+ export async function any<T>(operations: Array<() => Promise<T>>): Promise<T> {
376
+ const promises = operations.map(async (op, index) => {
377
+ try {
378
+ return { success: true, result: await op(), index };
379
+ } catch (error) {
380
+ return { success: false, error, index };
381
+ }
382
+ });
383
+
384
+ const results = await Promise.all(promises);
385
+ const successful = results.find((r) => r.success);
386
+
387
+ if (successful) {
388
+ return successful.result as T;
389
+ }
390
+
391
+ // If none succeeded, throw the first error
392
+ const firstFailure = results.find((r) => !r.success);
393
+ if (firstFailure) {
394
+ throw firstFailure.error;
395
+ }
396
+
397
+ throw new Error('No operations provided');
398
+ }
399
+
400
+ /**
401
+ * Parallel queue interface
402
+ */
403
+ export interface ParallelQueueInstance<T> {
404
+ add(item: T): Promise<unknown>;
405
+ size(): number;
406
+ clear(): void;
407
+ }
408
+
409
+ /**
410
+ * Create a parallel execution queue with controlled concurrency
411
+ */
412
+ export function createParallelQueue<T>(
413
+ processor: (item: T) => Promise<unknown>,
414
+ concurrency = 10
415
+ ): ParallelQueueInstance<T> {
416
+ // Closure-based state
417
+ const queue: Array<{
418
+ item: T;
419
+ resolve: (value: unknown) => void;
420
+ reject: (error: Error) => void;
421
+ }> = [];
422
+ let running = 0;
423
+
424
+ const process = async (): Promise<void> => {
425
+ if (running >= concurrency || queue.length === 0) {
426
+ return;
427
+ }
428
+
429
+ running++;
430
+ const { item, resolve, reject } = queue.shift()!;
431
+
432
+ try {
433
+ const result = await processor(item);
434
+ resolve(result);
435
+ } catch (error) {
436
+ reject(error as Error);
437
+ } finally {
438
+ running--;
439
+ // Process next item in queue
440
+ setImmediate(() => process());
441
+ }
442
+ };
443
+
444
+ const add = async (item: T): Promise<unknown> => {
445
+ return new Promise((resolve, reject) => {
446
+ queue.push({ item, resolve, reject });
447
+ process();
448
+ });
449
+ };
450
+
451
+ const size = (): number => {
452
+ return queue.length;
453
+ };
454
+
455
+ const clear = (): void => {
456
+ queue.length = 0;
457
+ };
458
+
459
+ return {
460
+ add,
461
+ size,
462
+ clear,
463
+ };
464
+ }
465
+
466
+ /**
467
+ * @deprecated Use createParallelQueue() for new code
468
+ */
469
+ export class ParallelQueue<T> {
470
+ private instance: ParallelQueueInstance<T>;
471
+
472
+ constructor(processor: (item: T) => Promise<unknown>, concurrency = 10) {
473
+ this.instance = createParallelQueue(processor, concurrency);
474
+ }
475
+
476
+ async add(item: T): Promise<unknown> {
477
+ return this.instance.add(item);
478
+ }
479
+
480
+ size(): number {
481
+ return this.instance.size();
482
+ }
483
+
484
+ clear(): void {
485
+ return this.instance.clear();
486
+ }
487
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Centralized path resolution for all static assets
3
+ *
4
+ * Structure:
5
+ * assets/ (at project root) - single source of truth
6
+ *
7
+ * Path resolution:
8
+ * - Development: src/utils/paths.ts reads ../assets
9
+ * - Production: dist/xxx.js reads ../assets
10
+ * - No copying needed, both read same location
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { pathSecurity } from './security.js';
17
+
18
+ /**
19
+ * Find package root by walking up directory tree
20
+ * Pure function - finds package.json location
21
+ *
22
+ * @param context - Optional context for error message (e.g., 'assets', 'migrations')
23
+ * @returns Absolute path to package root directory
24
+ * @throws Error if package.json cannot be found within 10 levels
25
+ *
26
+ * @example
27
+ * const root = findPackageRoot(); // 'Cannot find package.json'
28
+ * const root = findPackageRoot('drizzle migrations'); // 'Cannot find package.json - drizzle migrations location unknown'
29
+ */
30
+ export function findPackageRoot(context?: string): string {
31
+ const __filename = fileURLToPath(import.meta.url);
32
+ let currentDir = path.dirname(__filename);
33
+
34
+ // Walk up max 10 levels to find package.json
35
+ for (let i = 0; i < 10; i++) {
36
+ const packageJsonPath = path.join(currentDir, 'package.json');
37
+ if (fs.existsSync(packageJsonPath)) {
38
+ return currentDir;
39
+ }
40
+
41
+ const parentDir = path.dirname(currentDir);
42
+ if (parentDir === currentDir) break; // reached filesystem root
43
+ currentDir = parentDir;
44
+ }
45
+
46
+ const errorMsg = context
47
+ ? `Cannot find package.json - ${context} location unknown`
48
+ : 'Cannot find package.json';
49
+ throw new Error(errorMsg);
50
+ }
51
+
52
+ // Find monorepo root (parent of packages/flow) for assets
53
+ const PACKAGE_ROOT = findPackageRoot();
54
+ const MONOREPO_ROOT = path.join(PACKAGE_ROOT, '..', '..');
55
+ const ASSETS_ROOT = fs.existsSync(path.join(MONOREPO_ROOT, 'assets'))
56
+ ? path.join(MONOREPO_ROOT, 'assets')
57
+ : path.join(PACKAGE_ROOT, 'assets');
58
+
59
+ /**
60
+ * Get path to agents directory
61
+ */
62
+ export function getAgentsDir(): string {
63
+ return path.join(ASSETS_ROOT, 'agents');
64
+ }
65
+
66
+ /**
67
+ * Get path to templates directory
68
+ */
69
+ export function getTemplatesDir(): string {
70
+ return path.join(ASSETS_ROOT, 'templates');
71
+ }
72
+
73
+ /**
74
+ * Get path to rules directory
75
+ */
76
+ export function getRulesDir(): string {
77
+ return path.join(ASSETS_ROOT, 'rules');
78
+ }
79
+
80
+ /**
81
+ * Get path to knowledge directory
82
+ */
83
+ export function getKnowledgeDir(): string {
84
+ return path.join(ASSETS_ROOT, 'knowledge');
85
+ }
86
+
87
+ /**
88
+ * Get path to output styles directory
89
+ */
90
+ export function getOutputStylesDir(): string {
91
+ return path.join(ASSETS_ROOT, 'output-styles');
92
+ }
93
+
94
+ /**
95
+ * Get path to slash commands directory
96
+ */
97
+ export function getSlashCommandsDir(): string {
98
+ return path.join(ASSETS_ROOT, 'slash-commands');
99
+ }
100
+
101
+ /**
102
+ * Get path to a specific rule file with path traversal protection
103
+ */
104
+ export function getRuleFile(filename: string): string {
105
+ // Validate filename to prevent path traversal
106
+ if (!filename || typeof filename !== 'string') {
107
+ throw new Error('Filename must be a non-empty string');
108
+ }
109
+
110
+ // Check for path traversal attempts
111
+ if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
112
+ throw new Error(`Invalid filename: ${filename}. Path traversal not allowed.`);
113
+ }
114
+
115
+ // Validate filename contains only safe characters
116
+ if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {
117
+ throw new Error(`Filename contains invalid characters: ${filename}`);
118
+ }
119
+
120
+ // Safely join paths
121
+ const rulesDir = getRulesDir();
122
+ const filePath = pathSecurity.safeJoin(rulesDir, filename);
123
+
124
+ if (!fs.existsSync(filePath)) {
125
+ throw new Error(`Rule file not found: ${filename} (looked in ${rulesDir})`);
126
+ }
127
+
128
+ return filePath;
129
+ }
130
+
131
+ /**
132
+ * Debug info - shows where assets are resolved from
133
+ */
134
+ export function getPathsInfo() {
135
+ return {
136
+ assetsRoot: ASSETS_ROOT,
137
+ agents: getAgentsDir(),
138
+ templates: getTemplatesDir(),
139
+ rules: getRulesDir(),
140
+ outputStyles: getOutputStylesDir(),
141
+ slashCommands: getSlashCommandsDir(),
142
+ };
143
+ }