@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,296 @@
1
+ /**
2
+ * Business logic for evaluation service
3
+ * Pure functions for evaluation processing
4
+ *
5
+ * DESIGN RATIONALE:
6
+ * - Pure functions for data transformation
7
+ * - Testable without file system or child processes
8
+ * - Clear separation of I/O and logic
9
+ * - Composable evaluation pipeline
10
+ */
11
+
12
+ import type { FileSystemError } from '../../core/functional/error-types.js';
13
+ import { fileSystemError } from '../../core/functional/error-types.js';
14
+ import { pipe } from '../../core/functional/pipe.js';
15
+ import type { Result } from '../../core/functional/result.js';
16
+ import { failure, success } from '../../core/functional/result.js';
17
+ import { Arr, Obj, Str } from '../../utils/functional/index.js';
18
+
19
+ /**
20
+ * Domain types
21
+ */
22
+
23
+ export interface AgentTiming {
24
+ duration: number;
25
+ }
26
+
27
+ export interface AgentTimings {
28
+ [agentName: string]: AgentTiming;
29
+ }
30
+
31
+ export interface PerformanceScoreRange {
32
+ max: number;
33
+ score: number;
34
+ label: string;
35
+ }
36
+
37
+ export interface AgentPerformanceData {
38
+ agent: string;
39
+ duration: number;
40
+ score: number;
41
+ label: string;
42
+ }
43
+
44
+ export interface EvaluationTemplate {
45
+ content: string;
46
+ variables: string[];
47
+ }
48
+
49
+ /**
50
+ * Pure functions for timing processing
51
+ */
52
+
53
+ /**
54
+ * Parse duration from execution time text
55
+ */
56
+ export const parseDuration = (content: string): number => {
57
+ const match = content.match(/Duration:\s*(\d+)\s*seconds/);
58
+ return match ? Number.parseInt(match[1], 10) : 0;
59
+ };
60
+
61
+ /**
62
+ * Parse timing from JSON
63
+ */
64
+ export const parseTimingJSON = (content: string): Result<AgentTiming, FileSystemError> => {
65
+ try {
66
+ const data = JSON.parse(content);
67
+ return success({ duration: data.duration || 0 });
68
+ } catch (error) {
69
+ return failure(
70
+ fileSystemError('Failed to parse timing JSON', '', 'read', {
71
+ cause: error instanceof Error ? error : undefined,
72
+ })
73
+ );
74
+ }
75
+ };
76
+
77
+ /**
78
+ * Find performance score range for duration
79
+ */
80
+ export const findScoreRange = (
81
+ duration: number,
82
+ ranges: PerformanceScoreRange[]
83
+ ): PerformanceScoreRange => {
84
+ const found = ranges.find((range) => duration <= range.max);
85
+ return found || ranges[ranges.length - 1];
86
+ };
87
+
88
+ /**
89
+ * Calculate agent performance data
90
+ */
91
+ export const calculatePerformance = (
92
+ agentName: string,
93
+ timing: AgentTiming,
94
+ scoreRanges: PerformanceScoreRange[]
95
+ ): AgentPerformanceData => {
96
+ const scoreRange = findScoreRange(timing.duration, scoreRanges);
97
+ return {
98
+ agent: agentName,
99
+ duration: timing.duration,
100
+ score: scoreRange.score,
101
+ label: scoreRange.label,
102
+ };
103
+ };
104
+
105
+ /**
106
+ * Format agent performance as string
107
+ */
108
+ export const formatPerformance = (data: AgentPerformanceData): string => {
109
+ return `- ${data.agent}: ${data.duration}s execution time (Performance: ${data.score}/10)`;
110
+ };
111
+
112
+ /**
113
+ * Generate performance data section
114
+ */
115
+ export const generatePerformanceSection = (
116
+ timings: AgentTimings,
117
+ scoreRanges: PerformanceScoreRange[]
118
+ ): string => {
119
+ return pipe(
120
+ Obj.entries(timings),
121
+ Arr.map(([agent, timing]) => calculatePerformance(agent, timing, scoreRanges)),
122
+ Arr.map(formatPerformance),
123
+ Str.join('\n')
124
+ );
125
+ };
126
+
127
+ /**
128
+ * Template variable replacement (pure)
129
+ */
130
+ export const replaceTemplateVariables = (
131
+ template: string,
132
+ variables: Record<string, string>
133
+ ): string => {
134
+ let result = template;
135
+
136
+ for (const [key, value] of Obj.entries(variables)) {
137
+ const placeholder = `{{${String(key)}}}`;
138
+ result = Str.replaceAll(placeholder, value)(result);
139
+ }
140
+
141
+ return result;
142
+ };
143
+
144
+ /**
145
+ * Parse template and find variables
146
+ */
147
+ export const parseTemplate = (content: string): EvaluationTemplate => {
148
+ const variablePattern = /\{\{([A-Z_]+)\}\}/g;
149
+ const variables: string[] = [];
150
+
151
+ let match;
152
+ while ((match = variablePattern.exec(content)) !== null) {
153
+ if (!variables.includes(match[1])) {
154
+ variables.push(match[1]);
155
+ }
156
+ }
157
+
158
+ return {
159
+ content,
160
+ variables,
161
+ };
162
+ };
163
+
164
+ /**
165
+ * Build evaluation prompt from template
166
+ */
167
+ export const buildEvaluationPrompt = (
168
+ template: string,
169
+ timings: AgentTimings,
170
+ scoreRanges: PerformanceScoreRange[]
171
+ ): string => {
172
+ const performanceData = generatePerformanceSection(timings, scoreRanges);
173
+
174
+ return replaceTemplateVariables(template, {
175
+ AGENT_PERFORMANCE_DATA: performanceData,
176
+ });
177
+ };
178
+
179
+ /**
180
+ * Format agent work content
181
+ */
182
+ export const formatAgentWork = (agentName: string, files: string[]): string => {
183
+ const header = `=== ${agentName} WORK ===\n\n`;
184
+ const fileContents = files.map((content) => content).join('\n');
185
+ return header + fileContents;
186
+ };
187
+
188
+ /**
189
+ * Format file content
190
+ */
191
+ export const formatFileContent = (fileName: string, content: string): string => {
192
+ return `\n--- File: ${fileName} ---\n${content}\n`;
193
+ };
194
+
195
+ /**
196
+ * Combine agent work sections
197
+ */
198
+ export const combineAgentWork = (workSections: string[]): string => {
199
+ const separator = `\n${'='.repeat(80)}\n`;
200
+ return workSections.join(separator);
201
+ };
202
+
203
+ /**
204
+ * Build full evaluation input
205
+ */
206
+ export const buildEvaluationInput = (prompt: string, agentWork: string): string => {
207
+ return `${prompt}\n\nAGENT WORK TO EVALUATE:\n${agentWork}`;
208
+ };
209
+
210
+ /**
211
+ * Validate evaluation template
212
+ */
213
+ export const validateTemplate = (template: string): Result<string, FileSystemError> => {
214
+ if (Str.isBlank(template)) {
215
+ return failure(fileSystemError('Template is empty', '', 'read'));
216
+ }
217
+
218
+ const parsed = parseTemplate(template);
219
+ if (!parsed.variables.includes('AGENT_PERFORMANCE_DATA')) {
220
+ return failure(
221
+ fileSystemError('Template missing required variable: AGENT_PERFORMANCE_DATA', '', 'read')
222
+ );
223
+ }
224
+
225
+ return success(template);
226
+ };
227
+
228
+ /**
229
+ * Extract summary statistics from agent work
230
+ */
231
+ export const extractSummaryStats = (agentWork: Record<string, string>): Record<string, number> => {
232
+ return pipe(
233
+ Obj.entries(agentWork),
234
+ Arr.map(([agent, content]) => {
235
+ const fileCount = (content.match(/--- File: /g) || []).length;
236
+ return [agent, fileCount] as [string, number];
237
+ }),
238
+ Obj.fromEntries
239
+ );
240
+ };
241
+
242
+ /**
243
+ * Format summary statistics
244
+ */
245
+ export const formatSummaryStats = (stats: Record<string, number>): string => {
246
+ return pipe(
247
+ Obj.entries(stats),
248
+ Arr.map(([agent, count]) => `${agent}: ${count} files created`),
249
+ Str.join('\n')
250
+ );
251
+ };
252
+
253
+ /**
254
+ * Parse evaluation output (extract sections)
255
+ */
256
+ export const parseEvaluationOutput = (
257
+ output: string
258
+ ): {
259
+ sections: Array<{ title: string; content: string }>;
260
+ } => {
261
+ const sections: Array<{ title: string; content: string }> = [];
262
+ const lines = Str.lines(output);
263
+
264
+ let currentSection: { title: string; content: string } | null = null;
265
+
266
+ for (const line of lines) {
267
+ if (line.startsWith('##')) {
268
+ if (currentSection) {
269
+ sections.push(currentSection);
270
+ }
271
+ currentSection = {
272
+ title: line.replace(/^##\s*/, '').trim(),
273
+ content: '',
274
+ };
275
+ } else if (currentSection) {
276
+ currentSection.content += `${line}\n`;
277
+ }
278
+ }
279
+
280
+ if (currentSection) {
281
+ sections.push(currentSection);
282
+ }
283
+
284
+ return { sections };
285
+ };
286
+
287
+ /**
288
+ * Default performance score ranges
289
+ */
290
+ export const DEFAULT_SCORE_RANGES: PerformanceScoreRange[] = [
291
+ { max: 30, score: 10, label: 'Excellent' },
292
+ { max: 60, score: 8, label: 'Good' },
293
+ { max: 120, score: 6, label: 'Fair' },
294
+ { max: 240, score: 4, label: 'Slow' },
295
+ { max: Number.POSITIVE_INFINITY, score: 2, label: 'Very Slow' },
296
+ ];
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Functional file processing service
3
+ * Pure file transformations separated from I/O
4
+ *
5
+ * DESIGN RATIONALE:
6
+ * - Pure transformation functions
7
+ * - Composable processing pipeline
8
+ * - Side effects (I/O) explicit and isolated
9
+ * - Testable without file system
10
+ */
11
+
12
+ import type { FileSystemError } from '../../core/functional/error-types.js';
13
+ import { fileSystemError } from '../../core/functional/error-types.js';
14
+ import type { Result } from '../../core/functional/result.js';
15
+ import { all, failure, success } from '../../core/functional/result.js';
16
+
17
+ /**
18
+ * Domain types
19
+ */
20
+
21
+ export interface FileContent {
22
+ path: string;
23
+ content: string;
24
+ metadata?: Record<string, unknown>;
25
+ }
26
+
27
+ export interface ProcessedFile {
28
+ originalPath: string;
29
+ processedContent: string;
30
+ metadata: Record<string, unknown>;
31
+ }
32
+
33
+ export type FileTransform = (content: string) => string;
34
+ export type FileValidator = (content: string) => Result<string, FileSystemError>;
35
+
36
+ /**
37
+ * Pure transformation functions
38
+ */
39
+
40
+ /**
41
+ * Trim whitespace from content
42
+ */
43
+ export const trimContent: FileTransform = (content: string): string => {
44
+ return content.trim();
45
+ };
46
+
47
+ /**
48
+ * Normalize line endings to \n
49
+ */
50
+ export const normalizeLineEndings: FileTransform = (content: string): string => {
51
+ return content.replace(/\r\n/g, '\n');
52
+ };
53
+
54
+ /**
55
+ * Remove trailing whitespace from each line
56
+ */
57
+ export const removeTrailingWhitespace: FileTransform = (content: string): string => {
58
+ return content
59
+ .split('\n')
60
+ .map((line) => line.trimEnd())
61
+ .join('\n');
62
+ };
63
+
64
+ /**
65
+ * Ensure file ends with newline
66
+ */
67
+ export const ensureTrailingNewline: FileTransform = (content: string): string => {
68
+ if (!content.endsWith('\n')) {
69
+ return `${content}\n`;
70
+ }
71
+ return content;
72
+ };
73
+
74
+ /**
75
+ * Remove consecutive blank lines (keep max 1)
76
+ */
77
+ export const collapseBlankLines: FileTransform = (content: string): string => {
78
+ return content.replace(/\n\n\n+/g, '\n\n');
79
+ };
80
+
81
+ /**
82
+ * Apply multiple transforms in sequence
83
+ */
84
+ export const composeTransforms = (...transforms: FileTransform[]): FileTransform => {
85
+ return (content: string) => transforms.reduce((acc, transform) => transform(acc), content);
86
+ };
87
+
88
+ /**
89
+ * Standard cleanup pipeline
90
+ */
91
+ export const standardCleanup: FileTransform = composeTransforms(
92
+ normalizeLineEndings,
93
+ removeTrailingWhitespace,
94
+ collapseBlankLines,
95
+ ensureTrailingNewline
96
+ );
97
+
98
+ /**
99
+ * Validation functions
100
+ */
101
+
102
+ /**
103
+ * Validate content is not empty
104
+ */
105
+ export const validateNotEmpty: FileValidator = (
106
+ content: string
107
+ ): Result<string, FileSystemError> => {
108
+ if (content.trim().length === 0) {
109
+ return failure(fileSystemError('File content is empty', '', 'read'));
110
+ }
111
+ return success(content);
112
+ };
113
+
114
+ /**
115
+ * Validate content size
116
+ */
117
+ export const validateSize =
118
+ (maxBytes: number): FileValidator =>
119
+ (content: string): Result<string, FileSystemError> => {
120
+ const bytes = Buffer.byteLength(content, 'utf-8');
121
+ if (bytes > maxBytes) {
122
+ return failure(
123
+ fileSystemError(`File content exceeds maximum size: ${bytes} > ${maxBytes}`, '', 'read', {
124
+ context: { bytes, maxBytes },
125
+ })
126
+ );
127
+ }
128
+ return success(content);
129
+ };
130
+
131
+ /**
132
+ * Validate content matches pattern
133
+ */
134
+ export const validatePattern =
135
+ (pattern: RegExp, message: string): FileValidator =>
136
+ (content: string): Result<string, FileSystemError> => {
137
+ if (!pattern.test(content)) {
138
+ return failure(fileSystemError(message, '', 'read'));
139
+ }
140
+ return success(content);
141
+ };
142
+
143
+ /**
144
+ * Compose validators
145
+ */
146
+ export const composeValidators =
147
+ (...validators: FileValidator[]): FileValidator =>
148
+ (content: string): Result<string, FileSystemError> => {
149
+ for (const validator of validators) {
150
+ const result = validator(content);
151
+ if (result._tag === 'Failure') {
152
+ return result;
153
+ }
154
+ }
155
+ return success(content);
156
+ };
157
+
158
+ /**
159
+ * File processing operations
160
+ */
161
+
162
+ /**
163
+ * Process a single file (pure transformation)
164
+ */
165
+ export const processFileContent = (
166
+ file: FileContent,
167
+ transform: FileTransform,
168
+ validator?: FileValidator
169
+ ): Result<ProcessedFile, FileSystemError> => {
170
+ // Validate if validator provided
171
+ if (validator) {
172
+ const validationResult = validator(file.content);
173
+ if (validationResult._tag === 'Failure') {
174
+ return validationResult;
175
+ }
176
+ }
177
+
178
+ // Apply transformation
179
+ const processedContent = transform(file.content);
180
+
181
+ return success({
182
+ originalPath: file.path,
183
+ processedContent,
184
+ metadata: {
185
+ ...file.metadata,
186
+ processed: true,
187
+ originalLength: file.content.length,
188
+ processedLength: processedContent.length,
189
+ },
190
+ });
191
+ };
192
+
193
+ /**
194
+ * Process multiple files
195
+ */
196
+ export const processFiles = (
197
+ files: FileContent[],
198
+ transform: FileTransform,
199
+ validator?: FileValidator
200
+ ): Result<ProcessedFile[], FileSystemError> => {
201
+ const results = files.map((file) => processFileContent(file, transform, validator));
202
+
203
+ return all(results);
204
+ };
205
+
206
+ /**
207
+ * Filter files by extension (pure)
208
+ */
209
+ export const filterByExtension =
210
+ (extensions: string[]) =>
211
+ (files: FileContent[]): FileContent[] => {
212
+ return files.filter((file) => {
213
+ const ext = file.path.split('.').pop()?.toLowerCase();
214
+ return ext && extensions.includes(ext);
215
+ });
216
+ };
217
+
218
+ /**
219
+ * Filter files by pattern (pure)
220
+ */
221
+ export const filterByPattern =
222
+ (pattern: RegExp) =>
223
+ (files: FileContent[]): FileContent[] => {
224
+ return files.filter((file) => pattern.test(file.path));
225
+ };
226
+
227
+ /**
228
+ * Sort files by path (pure)
229
+ */
230
+ export const sortByPath = (files: FileContent[]): FileContent[] => {
231
+ return [...files].sort((a, b) => a.path.localeCompare(b.path));
232
+ };
233
+
234
+ /**
235
+ * Group files by directory (pure)
236
+ */
237
+ export const groupByDirectory = (files: FileContent[]): Record<string, FileContent[]> => {
238
+ return files.reduce(
239
+ (acc, file) => {
240
+ const dir = file.path.split('/').slice(0, -1).join('/') || '.';
241
+ if (!acc[dir]) {
242
+ acc[dir] = [];
243
+ }
244
+ acc[dir].push(file);
245
+ return acc;
246
+ },
247
+ {} as Record<string, FileContent[]>
248
+ );
249
+ };
250
+
251
+ /**
252
+ * Extract metadata from content (pure)
253
+ */
254
+ export const extractMetadata =
255
+ (extractor: (content: string) => Record<string, unknown>) =>
256
+ (file: FileContent): FileContent => {
257
+ return {
258
+ ...file,
259
+ metadata: {
260
+ ...file.metadata,
261
+ ...extractor(file.content),
262
+ },
263
+ };
264
+ };
265
+
266
+ /**
267
+ * Building block for file processing pipelines
268
+ */
269
+ export const createFileProcessor = (transform: FileTransform, validator?: FileValidator) => {
270
+ return (files: FileContent[]): Result<ProcessedFile[], FileSystemError> => {
271
+ return processFiles(files, transform, validator);
272
+ };
273
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Functional services
3
+ * Business logic as composable pure functions
4
+ *
5
+ * DESIGN RATIONALE:
6
+ * - Services as collections of pure functions
7
+ * - Side effects isolated to service boundaries
8
+ * - Composable through functional patterns
9
+ * - Testable without mocking
10
+ */
11
+
12
+ export * from './file-processor.js';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Service modules barrel export
3
+ * Centralized access to service layer functionality
4
+ */
5
+
6
+ // Re-export commonly used service functions
7
+ export {
8
+ configureServers,
9
+ default as mcpService,
10
+ installServers,
11
+ listAvailableServers,
12
+ validateServerConfiguration,
13
+ } from './mcp-service';