@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.
- package/CHANGELOG.md +12 -0
- package/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- 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';
|