@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,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable file installer - shared installation logic
|
|
3
|
+
* Used by targets through composition
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import type { CommonOptions, ProcessResult } from '../../shared/index.js';
|
|
9
|
+
import { clearObsoleteFiles, collectFiles, getLocalFileInfo } from '../../shared/index.js';
|
|
10
|
+
|
|
11
|
+
export interface InstallOptions extends CommonOptions {
|
|
12
|
+
/** Custom file extension */
|
|
13
|
+
extension?: string;
|
|
14
|
+
/** Whether to flatten directory structure */
|
|
15
|
+
flatten?: boolean;
|
|
16
|
+
/** Show progress messages */
|
|
17
|
+
showProgress?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type FileTransformFn = (content: string, sourcePath?: string) => Promise<string>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Collect files from source directory
|
|
24
|
+
*/
|
|
25
|
+
async function collectSourceFiles(sourceDir: string, extension: string): Promise<string[]> {
|
|
26
|
+
if (!fs.existsSync(sourceDir)) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const allFiles: string[] = [];
|
|
31
|
+
|
|
32
|
+
// Collect files directly in root
|
|
33
|
+
const rootFiles = fs
|
|
34
|
+
.readdirSync(sourceDir, { withFileTypes: true })
|
|
35
|
+
.filter((dirent) => dirent.isFile() && dirent.name.endsWith(extension))
|
|
36
|
+
.map((dirent) => dirent.name);
|
|
37
|
+
|
|
38
|
+
allFiles.push(...rootFiles);
|
|
39
|
+
|
|
40
|
+
// Collect files from subdirectories (excluding 'archived')
|
|
41
|
+
const subdirs = fs
|
|
42
|
+
.readdirSync(sourceDir, { withFileTypes: true })
|
|
43
|
+
.filter((dirent) => dirent.isDirectory() && dirent.name !== 'archived')
|
|
44
|
+
.map((dirent) => dirent.name);
|
|
45
|
+
|
|
46
|
+
for (const subdir of subdirs) {
|
|
47
|
+
const subdirPath = path.join(sourceDir, subdir);
|
|
48
|
+
const files = await collectFiles(subdirPath, [extension]);
|
|
49
|
+
allFiles.push(...files.map((file) => path.join(subdir, file)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return allFiles;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Install files from source directory to target directory
|
|
57
|
+
*/
|
|
58
|
+
export async function installToDirectory(
|
|
59
|
+
sourceDir: string,
|
|
60
|
+
targetDir: string,
|
|
61
|
+
transform: FileTransformFn,
|
|
62
|
+
options: InstallOptions = {}
|
|
63
|
+
): Promise<ProcessResult[]> {
|
|
64
|
+
const results: ProcessResult[] = [];
|
|
65
|
+
|
|
66
|
+
// Collect source files
|
|
67
|
+
const files = await collectSourceFiles(sourceDir, options.extension || '.md');
|
|
68
|
+
|
|
69
|
+
if (files.length === 0) {
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Clear obsolete files if requested
|
|
74
|
+
if (options.clear && fs.existsSync(targetDir)) {
|
|
75
|
+
const expectedFiles = new Set(
|
|
76
|
+
files.map((file) => {
|
|
77
|
+
if (options.flatten) {
|
|
78
|
+
const parsed = path.parse(file);
|
|
79
|
+
const baseName = parsed.name;
|
|
80
|
+
const dir = parsed.dir;
|
|
81
|
+
const flatName = dir ? `${dir.replace(/[/\\]/g, '-')}-${baseName}` : baseName;
|
|
82
|
+
return `${flatName}${options.extension || '.md'}`;
|
|
83
|
+
}
|
|
84
|
+
return file;
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
clearObsoleteFiles(targetDir, expectedFiles, [options.extension || '.md'], results);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Create target directory
|
|
92
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
93
|
+
|
|
94
|
+
if (options.showProgress && !options.quiet) {
|
|
95
|
+
console.log(
|
|
96
|
+
`Installing ${files.length} file${files.length > 1 ? 's' : ''} to ${targetDir.replace(`${process.cwd()}/`, '')}`
|
|
97
|
+
);
|
|
98
|
+
console.log('');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (options.dryRun) {
|
|
102
|
+
if (!options.quiet) {
|
|
103
|
+
console.log('✓ Dry run completed - no files were modified');
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Process files in parallel
|
|
109
|
+
const processPromises = files.map(async (file) => {
|
|
110
|
+
const sourcePath = path.join(sourceDir, file);
|
|
111
|
+
const targetPath = path.join(targetDir, file);
|
|
112
|
+
|
|
113
|
+
// Ensure target directory exists
|
|
114
|
+
const targetFileDir = path.dirname(targetPath);
|
|
115
|
+
if (!fs.existsSync(targetFileDir)) {
|
|
116
|
+
fs.mkdirSync(targetFileDir, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const localInfo = await getLocalFileInfo(targetPath);
|
|
120
|
+
|
|
121
|
+
// Read and transform content
|
|
122
|
+
let content = fs.readFileSync(sourcePath, 'utf8');
|
|
123
|
+
content = await transform(content, file);
|
|
124
|
+
|
|
125
|
+
const localProcessed = localInfo ? await transform(localInfo.content, file) : '';
|
|
126
|
+
const contentChanged = !localInfo || localProcessed !== content;
|
|
127
|
+
|
|
128
|
+
if (contentChanged) {
|
|
129
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
130
|
+
results.push({
|
|
131
|
+
file,
|
|
132
|
+
status: localInfo ? 'updated' : 'added',
|
|
133
|
+
action: localInfo ? 'Updated' : 'Created',
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
results.push({
|
|
137
|
+
file,
|
|
138
|
+
status: 'current',
|
|
139
|
+
action: 'Already current',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await Promise.all(processPromises);
|
|
145
|
+
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Append files from source directory to a single target file
|
|
151
|
+
*/
|
|
152
|
+
export async function appendToFile(
|
|
153
|
+
sourceDir: string,
|
|
154
|
+
targetFile: string,
|
|
155
|
+
transform: FileTransformFn,
|
|
156
|
+
options: InstallOptions = {}
|
|
157
|
+
): Promise<void> {
|
|
158
|
+
// Collect source files
|
|
159
|
+
const files = await collectSourceFiles(sourceDir, options.extension || '.md');
|
|
160
|
+
|
|
161
|
+
if (files.length === 0) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (options.dryRun) {
|
|
166
|
+
if (!options.quiet) {
|
|
167
|
+
console.log(
|
|
168
|
+
`Dry run: Would append ${files.length} file${files.length > 1 ? 's' : ''} to ${targetFile.replace(`${process.cwd()}/`, '')}`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Read existing file content
|
|
175
|
+
let existingContent = '';
|
|
176
|
+
if (fs.existsSync(targetFile)) {
|
|
177
|
+
existingContent = fs.readFileSync(targetFile, 'utf8');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Build appended content
|
|
181
|
+
let appendContent = '';
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
const sourcePath = path.join(sourceDir, file);
|
|
184
|
+
let content = fs.readFileSync(sourcePath, 'utf8');
|
|
185
|
+
content = await transform(content, file);
|
|
186
|
+
appendContent += `${content}\n\n`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Write combined content
|
|
190
|
+
fs.writeFileSync(targetFile, existingContent + appendContent, 'utf8');
|
|
191
|
+
|
|
192
|
+
if (options.showProgress && !options.quiet) {
|
|
193
|
+
console.log(
|
|
194
|
+
`Appended ${files.length} file${files.length > 1 ? 's' : ''} to ${targetFile.replace(`${process.cwd()}/`, '')}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Install a single file from source to target
|
|
201
|
+
*/
|
|
202
|
+
export async function installFile(
|
|
203
|
+
sourceFile: string,
|
|
204
|
+
targetFile: string,
|
|
205
|
+
transform: FileTransformFn,
|
|
206
|
+
options: InstallOptions = {}
|
|
207
|
+
): Promise<void> {
|
|
208
|
+
if (options.dryRun) {
|
|
209
|
+
if (!options.quiet) {
|
|
210
|
+
console.log(`Dry run: Would install file to ${targetFile.replace(`${process.cwd()}/`, '')}`);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (!fs.existsSync(sourceFile)) {
|
|
216
|
+
if (!options.quiet) {
|
|
217
|
+
console.warn(`Source file not found: ${sourceFile}`);
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Ensure target directory exists
|
|
223
|
+
const targetDir = path.dirname(targetFile);
|
|
224
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
225
|
+
|
|
226
|
+
// Check if file already exists and is up to date
|
|
227
|
+
const localInfo = await getLocalFileInfo(targetFile);
|
|
228
|
+
let content = fs.readFileSync(sourceFile, 'utf8');
|
|
229
|
+
content = await transform(content);
|
|
230
|
+
|
|
231
|
+
if (localInfo && localInfo.content === content) {
|
|
232
|
+
if (!options.quiet) {
|
|
233
|
+
console.log(`File already current: ${targetFile.replace(`${process.cwd()}/`, '')}`);
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Write file
|
|
239
|
+
fs.writeFileSync(targetFile, content, 'utf8');
|
|
240
|
+
|
|
241
|
+
if (options.showProgress && !options.quiet) {
|
|
242
|
+
const action = localInfo ? 'Updated' : 'Created';
|
|
243
|
+
console.log(`${action} file: ${targetFile.replace(`${process.cwd()}/`, '')}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* File installer interface for backward compatibility
|
|
249
|
+
*/
|
|
250
|
+
export interface FileInstaller {
|
|
251
|
+
installToDirectory(
|
|
252
|
+
sourceDir: string,
|
|
253
|
+
targetDir: string,
|
|
254
|
+
transform: FileTransformFn,
|
|
255
|
+
options?: InstallOptions
|
|
256
|
+
): Promise<ProcessResult[]>;
|
|
257
|
+
appendToFile(
|
|
258
|
+
sourceDir: string,
|
|
259
|
+
targetFile: string,
|
|
260
|
+
transform: FileTransformFn,
|
|
261
|
+
options?: InstallOptions
|
|
262
|
+
): Promise<void>;
|
|
263
|
+
installFile(
|
|
264
|
+
sourceFile: string,
|
|
265
|
+
targetFile: string,
|
|
266
|
+
transform: FileTransformFn,
|
|
267
|
+
options?: InstallOptions
|
|
268
|
+
): Promise<void>;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Composable file installer
|
|
273
|
+
* Handles copying files from source to destination with optional transformation
|
|
274
|
+
* @deprecated Use standalone functions (installToDirectory, appendToFile, installFile) for new code
|
|
275
|
+
*/
|
|
276
|
+
export class FileInstaller {
|
|
277
|
+
async installToDirectory(
|
|
278
|
+
sourceDir: string,
|
|
279
|
+
targetDir: string,
|
|
280
|
+
transform: FileTransformFn,
|
|
281
|
+
options: InstallOptions = {}
|
|
282
|
+
): Promise<ProcessResult[]> {
|
|
283
|
+
return installToDirectory(sourceDir, targetDir, transform, options);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async appendToFile(
|
|
287
|
+
sourceDir: string,
|
|
288
|
+
targetFile: string,
|
|
289
|
+
transform: FileTransformFn,
|
|
290
|
+
options: InstallOptions = {}
|
|
291
|
+
): Promise<void> {
|
|
292
|
+
return appendToFile(sourceDir, targetFile, transform, options);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async installFile(
|
|
296
|
+
sourceFile: string,
|
|
297
|
+
targetFile: string,
|
|
298
|
+
transform: FileTransformFn,
|
|
299
|
+
options: InstallOptions = {}
|
|
300
|
+
): Promise<void> {
|
|
301
|
+
return installFile(sourceFile, targetFile, transform, options);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable MCP installer - shared MCP installation logic
|
|
3
|
+
* Used by targets through composition
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import ora from 'ora';
|
|
9
|
+
import { MCP_SERVER_REGISTRY, type MCPServerID } from '../../config/servers.js';
|
|
10
|
+
import { createMCPService } from '../../services/mcp-service.js';
|
|
11
|
+
import type { Target } from '../../types.js';
|
|
12
|
+
|
|
13
|
+
export interface MCPInstallResult {
|
|
14
|
+
selectedServers: MCPServerID[];
|
|
15
|
+
serverConfigsMap: Record<MCPServerID, Record<string, string>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* MCP Installer interface
|
|
20
|
+
*/
|
|
21
|
+
export interface MCPInstaller {
|
|
22
|
+
selectServers(options?: { quiet?: boolean }): Promise<MCPServerID[]>;
|
|
23
|
+
configureServers(
|
|
24
|
+
selectedServers: MCPServerID[],
|
|
25
|
+
options?: { quiet?: boolean }
|
|
26
|
+
): Promise<Record<MCPServerID, Record<string, string>>>;
|
|
27
|
+
installServers(
|
|
28
|
+
selectedServers: MCPServerID[],
|
|
29
|
+
serverConfigsMap: Record<MCPServerID, Record<string, string>>,
|
|
30
|
+
options?: { quiet?: boolean }
|
|
31
|
+
): Promise<void>;
|
|
32
|
+
setupMCP(options?: { quiet?: boolean; dryRun?: boolean }): Promise<MCPInstallResult>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create an MCP installer instance
|
|
37
|
+
* Handles server selection, configuration, and installation
|
|
38
|
+
*
|
|
39
|
+
* @param target - Target configuration object (dependency injection)
|
|
40
|
+
*/
|
|
41
|
+
export function createMCPInstaller(target: Target): MCPInstaller {
|
|
42
|
+
const mcpService = createMCPService({ target });
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Prompt user to select MCP servers
|
|
46
|
+
*/
|
|
47
|
+
const selectServers = async (options: { quiet?: boolean } = {}): Promise<MCPServerID[]> => {
|
|
48
|
+
const allServers = mcpService.getAllServerIds();
|
|
49
|
+
const installedServers = await mcpService.getInstalledServerIds();
|
|
50
|
+
|
|
51
|
+
if (!options.quiet) {
|
|
52
|
+
console.log(chalk.cyan.bold('━━━ Configure MCP Tools ━━━\n'));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Show server selection
|
|
56
|
+
const serverSelectionAnswer = await inquirer.prompt([
|
|
57
|
+
{
|
|
58
|
+
type: 'checkbox',
|
|
59
|
+
name: 'selectedServers',
|
|
60
|
+
message: 'Select MCP tools to install:',
|
|
61
|
+
choices: allServers.map((id) => {
|
|
62
|
+
const server = MCP_SERVER_REGISTRY[id];
|
|
63
|
+
const isInstalled = installedServers.includes(id);
|
|
64
|
+
return {
|
|
65
|
+
name: `${server.name} - ${server.description}`,
|
|
66
|
+
value: id,
|
|
67
|
+
checked: server.required || isInstalled || server.defaultInInit || false,
|
|
68
|
+
disabled: server.required ? '(required)' : false,
|
|
69
|
+
};
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
let selectedServers = serverSelectionAnswer.selectedServers as MCPServerID[];
|
|
75
|
+
|
|
76
|
+
// Ensure all required servers are included
|
|
77
|
+
const requiredServers = allServers.filter((id) => MCP_SERVER_REGISTRY[id].required);
|
|
78
|
+
selectedServers = [...new Set([...requiredServers, ...selectedServers])];
|
|
79
|
+
|
|
80
|
+
return selectedServers;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Configure selected servers
|
|
85
|
+
*/
|
|
86
|
+
const configureServers = async (
|
|
87
|
+
selectedServers: MCPServerID[],
|
|
88
|
+
options: { quiet?: boolean } = {}
|
|
89
|
+
): Promise<Record<MCPServerID, Record<string, string>>> => {
|
|
90
|
+
const serversNeedingConfig = selectedServers.filter((id) => {
|
|
91
|
+
const server = MCP_SERVER_REGISTRY[id];
|
|
92
|
+
return server.envVars && Object.keys(server.envVars).length > 0;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const serverConfigsMap: Record<MCPServerID, Record<string, string>> = {};
|
|
96
|
+
|
|
97
|
+
if (serversNeedingConfig.length > 0) {
|
|
98
|
+
if (!options.quiet) {
|
|
99
|
+
console.log(chalk.cyan.bold('\n━━━ Server Configuration ━━━\n'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const collectedEnv: Record<string, string> = {};
|
|
103
|
+
for (const serverId of serversNeedingConfig) {
|
|
104
|
+
const configValues = await mcpService.configureServer(serverId, collectedEnv);
|
|
105
|
+
serverConfigsMap[serverId] = configValues;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return serverConfigsMap;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Install servers with configuration
|
|
114
|
+
*/
|
|
115
|
+
const installServers = async (
|
|
116
|
+
selectedServers: MCPServerID[],
|
|
117
|
+
serverConfigsMap: Record<MCPServerID, Record<string, string>>,
|
|
118
|
+
options: { quiet?: boolean } = {}
|
|
119
|
+
): Promise<void> => {
|
|
120
|
+
if (selectedServers.length === 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Only show spinner if not in quiet mode
|
|
125
|
+
const spinner = options.quiet
|
|
126
|
+
? null
|
|
127
|
+
: ora({
|
|
128
|
+
text: `Installing ${selectedServers.length} MCP server${selectedServers.length > 1 ? 's' : ''}`,
|
|
129
|
+
color: 'cyan',
|
|
130
|
+
}).start();
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await mcpService.installServers(selectedServers, serverConfigsMap);
|
|
134
|
+
if (spinner) {
|
|
135
|
+
spinner.succeed(
|
|
136
|
+
chalk.green(
|
|
137
|
+
`Installed ${chalk.cyan(selectedServers.length)} MCP server${selectedServers.length > 1 ? 's' : ''}`
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (spinner) {
|
|
143
|
+
spinner.fail(chalk.red('Failed to install MCP servers'));
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Full MCP setup workflow: select, configure, and install
|
|
151
|
+
*/
|
|
152
|
+
const setupMCP = async (
|
|
153
|
+
options: { quiet?: boolean; dryRun?: boolean } = {}
|
|
154
|
+
): Promise<MCPInstallResult> => {
|
|
155
|
+
// Select servers
|
|
156
|
+
const selectedServers = await selectServers(options);
|
|
157
|
+
|
|
158
|
+
if (selectedServers.length === 0) {
|
|
159
|
+
return { selectedServers: [], serverConfigsMap: {} };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Configure servers
|
|
163
|
+
const serverConfigsMap = await configureServers(selectedServers, options);
|
|
164
|
+
|
|
165
|
+
// Install servers
|
|
166
|
+
if (!options.dryRun) {
|
|
167
|
+
await installServers(selectedServers, serverConfigsMap, options);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { selectedServers, serverConfigsMap };
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
selectServers,
|
|
175
|
+
configureServers,
|
|
176
|
+
installServers,
|
|
177
|
+
setupMCP,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @deprecated Use createMCPInstaller() for new code
|
|
183
|
+
*/
|
|
184
|
+
export class MCPInstaller {
|
|
185
|
+
private installer: ReturnType<typeof createMCPInstaller>;
|
|
186
|
+
|
|
187
|
+
constructor(target: Target) {
|
|
188
|
+
this.installer = createMCPInstaller(target);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async selectServers(options: { quiet?: boolean } = {}): Promise<MCPServerID[]> {
|
|
192
|
+
return this.installer.selectServers(options);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async configureServers(
|
|
196
|
+
selectedServers: MCPServerID[],
|
|
197
|
+
options: { quiet?: boolean } = {}
|
|
198
|
+
): Promise<Record<MCPServerID, Record<string, string>>> {
|
|
199
|
+
return this.installer.configureServers(selectedServers, options);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async installServers(
|
|
203
|
+
selectedServers: MCPServerID[],
|
|
204
|
+
serverConfigsMap: Record<MCPServerID, Record<string, string>>,
|
|
205
|
+
options: { quiet?: boolean } = {}
|
|
206
|
+
): Promise<void> {
|
|
207
|
+
return this.installer.installServers(selectedServers, serverConfigsMap, options);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async setupMCP(options: { quiet?: boolean; dryRun?: boolean } = {}): Promise<MCPInstallResult> {
|
|
211
|
+
return this.installer.setupMCP(options);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core interfaces for dependency inversion
|
|
3
|
+
* All abstractions in one place
|
|
4
|
+
*
|
|
5
|
+
* DESIGN RATIONALE:
|
|
6
|
+
* - Single source of truth for contracts
|
|
7
|
+
* - Enables dependency injection
|
|
8
|
+
* - Facilitates testing
|
|
9
|
+
* - Clear separation of interface and implementation
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Re-export commonly used types from existing interfaces
|
|
13
|
+
export type {
|
|
14
|
+
CoreService,
|
|
15
|
+
FileProcessor,
|
|
16
|
+
IDatabaseConnection,
|
|
17
|
+
ILogger,
|
|
18
|
+
InitializationOptions,
|
|
19
|
+
TargetManager,
|
|
20
|
+
} from '../interfaces.js';
|
|
21
|
+
export * from './repository.interface.js';
|
|
22
|
+
export * from './service.interface.js';
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository interface for dependency inversion
|
|
3
|
+
* Abstracts data access implementation
|
|
4
|
+
*
|
|
5
|
+
* DESIGN RATIONALE:
|
|
6
|
+
* - Depend on abstractions, not concrete implementations
|
|
7
|
+
* - Enables testing with in-memory implementations
|
|
8
|
+
* - Separates interface from implementation
|
|
9
|
+
* - Clear contract for data access
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { DatabaseError } from '../functional/error-types.js';
|
|
13
|
+
import type { Result } from '../functional/result.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Query options for filtering, sorting, pagination
|
|
17
|
+
*/
|
|
18
|
+
export interface QueryOptions {
|
|
19
|
+
limit?: number;
|
|
20
|
+
offset?: number;
|
|
21
|
+
orderBy?: string;
|
|
22
|
+
orderDirection?: 'ASC' | 'DESC';
|
|
23
|
+
where?: Record<string, any>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Paginated result wrapper
|
|
28
|
+
*/
|
|
29
|
+
export interface PaginatedResult<T> {
|
|
30
|
+
items: T[];
|
|
31
|
+
total: number;
|
|
32
|
+
page: number;
|
|
33
|
+
pageSize: number;
|
|
34
|
+
hasNext: boolean;
|
|
35
|
+
hasPrevious: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generic repository interface
|
|
40
|
+
* All operations return Result for explicit error handling
|
|
41
|
+
*/
|
|
42
|
+
export interface IRepository<T, ID = string | number> {
|
|
43
|
+
/**
|
|
44
|
+
* Find entity by ID
|
|
45
|
+
*/
|
|
46
|
+
findById(id: ID): Promise<Result<T | null, DatabaseError>>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Find multiple entities with filtering and sorting
|
|
50
|
+
*/
|
|
51
|
+
findMany(options?: QueryOptions): Promise<Result<T[], DatabaseError>>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Find entities with pagination
|
|
55
|
+
*/
|
|
56
|
+
findPaginated(
|
|
57
|
+
page: number,
|
|
58
|
+
pageSize: number,
|
|
59
|
+
options?: Omit<QueryOptions, 'limit' | 'offset'>
|
|
60
|
+
): Promise<Result<PaginatedResult<T>, DatabaseError>>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a new entity
|
|
64
|
+
*/
|
|
65
|
+
create(data: Partial<T>): Promise<Result<T, DatabaseError>>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Update an existing entity
|
|
69
|
+
*/
|
|
70
|
+
update(id: ID, data: Partial<T>): Promise<Result<T | null, DatabaseError>>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete an entity
|
|
74
|
+
*/
|
|
75
|
+
delete(id: ID): Promise<Result<boolean, DatabaseError>>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Count entities matching criteria
|
|
79
|
+
*/
|
|
80
|
+
count(options?: { where?: Record<string, any> }): Promise<Result<number, DatabaseError>>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Check if entity exists
|
|
84
|
+
*/
|
|
85
|
+
exists(id: ID): Promise<Result<boolean, DatabaseError>>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Factory function type for creating repositories
|
|
90
|
+
*/
|
|
91
|
+
export type RepositoryFactory<T, ID = string | number> = (tableName: string) => IRepository<T, ID>;
|