@sylphx/flow 1.1.1 → 1.3.0

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 (47) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/package.json +1 -1
  3. package/src/commands/flow-command.ts +28 -0
  4. package/src/commands/hook-command.ts +10 -230
  5. package/src/composables/index.ts +0 -1
  6. package/src/config/servers.ts +35 -78
  7. package/src/core/interfaces.ts +0 -33
  8. package/src/domains/index.ts +0 -2
  9. package/src/index.ts +0 -4
  10. package/src/services/mcp-service.ts +0 -16
  11. package/src/targets/claude-code.ts +3 -9
  12. package/src/targets/functional/claude-code-logic.ts +4 -22
  13. package/src/targets/opencode.ts +0 -6
  14. package/src/types/mcp.types.ts +29 -38
  15. package/src/types/target.types.ts +0 -2
  16. package/src/types.ts +0 -1
  17. package/src/utils/sync-utils.ts +106 -0
  18. package/src/commands/codebase-command.ts +0 -168
  19. package/src/commands/knowledge-command.ts +0 -161
  20. package/src/composables/useTargetConfig.ts +0 -45
  21. package/src/core/formatting/bytes.test.ts +0 -115
  22. package/src/core/validation/limit.test.ts +0 -155
  23. package/src/core/validation/query.test.ts +0 -44
  24. package/src/domains/codebase/index.ts +0 -5
  25. package/src/domains/codebase/tools.ts +0 -139
  26. package/src/domains/knowledge/index.ts +0 -10
  27. package/src/domains/knowledge/resources.ts +0 -537
  28. package/src/domains/knowledge/tools.ts +0 -174
  29. package/src/services/search/base-indexer.ts +0 -156
  30. package/src/services/search/codebase-indexer-types.ts +0 -38
  31. package/src/services/search/codebase-indexer.ts +0 -647
  32. package/src/services/search/embeddings-provider.ts +0 -455
  33. package/src/services/search/embeddings.ts +0 -316
  34. package/src/services/search/functional-indexer.ts +0 -323
  35. package/src/services/search/index.ts +0 -27
  36. package/src/services/search/indexer.ts +0 -380
  37. package/src/services/search/knowledge-indexer.ts +0 -422
  38. package/src/services/search/semantic-search.ts +0 -244
  39. package/src/services/search/tfidf.ts +0 -559
  40. package/src/services/search/unified-search-service.ts +0 -888
  41. package/src/services/storage/cache-storage.ts +0 -487
  42. package/src/services/storage/drizzle-storage.ts +0 -581
  43. package/src/services/storage/index.ts +0 -15
  44. package/src/services/storage/lancedb-vector-storage.ts +0 -494
  45. package/src/services/storage/memory-storage.ts +0 -268
  46. package/src/services/storage/separated-storage.ts +0 -467
  47. package/src/services/storage/vector-storage.ts +0 -13
@@ -1,380 +0,0 @@
1
- /**
2
- * Unified Indexer Service - 統一索引服務
3
- * 處理所有domain嘅索引建立和維護
4
- */
5
-
6
- import fs from 'node:fs';
7
- import path from 'node:path';
8
- import type { EmbeddingProvider } from '../../utils/embeddings.js';
9
- import { VectorStorage } from '../../utils/lancedb-vector-storage.js';
10
- import { buildSearchIndex, type SearchIndex } from '../../utils/tfidf.js';
11
- import type { ContentMetadata } from './search-service.js';
12
-
13
- export interface IndexingOptions {
14
- batchSize?: number;
15
- includeVectorIndex?: boolean;
16
- forceRebuild?: boolean;
17
- }
18
-
19
- export interface IndexingProgress {
20
- domain: string;
21
- totalFiles: number;
22
- processedFiles: number;
23
- status: 'scanning' | 'indexing' | 'vectorizing' | 'complete' | 'error';
24
- error?: string;
25
- }
26
-
27
- /**
28
- * Internal state for IndexerService
29
- */
30
- interface IndexerServiceState {
31
- readonly embeddingProvider?: EmbeddingProvider;
32
- readonly vectorStorages: ReadonlyMap<string, VectorStorage>;
33
- readonly progressCallbacks: ReadonlySet<(progress: IndexingProgress) => void>;
34
- }
35
-
36
- /**
37
- * IndexerService Interface
38
- * Unified indexer for building and maintaining search indices
39
- */
40
- export interface IndexerService {
41
- readonly initialize: () => Promise<void>;
42
- readonly onProgress: (callback: (progress: IndexingProgress) => void) => void;
43
- readonly offProgress: (callback: (progress: IndexingProgress) => void) => void;
44
- readonly buildIndex: (
45
- domain: 'knowledge' | 'codebase',
46
- options?: IndexingOptions
47
- ) => Promise<SearchIndex>;
48
- readonly updateIndex: (domain: string, content: ContentMetadata) => Promise<void>;
49
- readonly removeFromIndex: (domain: string, uri: string) => Promise<void>;
50
- }
51
-
52
- /**
53
- * Create Indexer Service (Factory Function)
54
- * Handles all domain indexing and maintenance
55
- */
56
- export const createIndexerService = (embeddingProvider?: EmbeddingProvider): IndexerService => {
57
- // Mutable state in closure (will be updated immutably)
58
- let state: IndexerServiceState = {
59
- embeddingProvider,
60
- vectorStorages: new Map(),
61
- progressCallbacks: new Set(),
62
- };
63
-
64
- // Helper: Update state immutably
65
- const updateState = (updates: Partial<IndexerServiceState>): void => {
66
- state = { ...state, ...updates };
67
- };
68
-
69
- /**
70
- * 初始化索引服務
71
- */
72
- const initialize = async (): Promise<void> => {
73
- if (!state.embeddingProvider) {
74
- // Import here to avoid circular dependencies
75
- const { getDefaultEmbeddingProvider } = await import('../../utils/embeddings.js');
76
- const provider = await getDefaultEmbeddingProvider();
77
- updateState({ embeddingProvider: provider });
78
- }
79
- };
80
-
81
- /**
82
- * 註冊進度回調
83
- */
84
- const onProgress = (callback: (progress: IndexingProgress) => void): void => {
85
- const newCallbacks = new Set(state.progressCallbacks);
86
- newCallbacks.add(callback);
87
- updateState({ progressCallbacks: newCallbacks });
88
- };
89
-
90
- /**
91
- * 移除進度回調
92
- */
93
- const offProgress = (callback: (progress: IndexingProgress) => void): void => {
94
- const newCallbacks = new Set(state.progressCallbacks);
95
- newCallbacks.delete(callback);
96
- updateState({ progressCallbacks: newCallbacks });
97
- };
98
-
99
- /**
100
- * 報告進度
101
- */
102
- const reportProgress = (progress: IndexingProgress): void => {
103
- state.progressCallbacks.forEach((callback) => callback(progress));
104
- };
105
-
106
- /**
107
- * 為指定domain建立索引
108
- */
109
- const buildIndex = async (
110
- domain: 'knowledge' | 'codebase',
111
- options: IndexingOptions = {}
112
- ): Promise<SearchIndex> => {
113
- const { batchSize = 10, includeVectorIndex = true, forceRebuild = false } = options;
114
-
115
- reportProgress({
116
- domain,
117
- totalFiles: 0,
118
- processedFiles: 0,
119
- status: 'scanning',
120
- });
121
-
122
- try {
123
- // 掃描文件
124
- const files = await scanDomainFiles(domain);
125
-
126
- reportProgress({
127
- domain,
128
- totalFiles: files.length,
129
- processedFiles: 0,
130
- status: 'indexing',
131
- });
132
-
133
- // 建立TF-IDF索引
134
- const searchIndex = buildSearchIndex(files);
135
-
136
- reportProgress({
137
- domain,
138
- totalFiles: files.length,
139
- processedFiles: files.length,
140
- status: includeVectorIndex ? 'vectorizing' : 'complete',
141
- });
142
-
143
- // 建立向量索引
144
- if (includeVectorIndex && state.embeddingProvider) {
145
- await buildVectorIndex(domain, files, batchSize);
146
- }
147
-
148
- reportProgress({
149
- domain,
150
- totalFiles: files.length,
151
- processedFiles: files.length,
152
- status: 'complete',
153
- });
154
-
155
- return searchIndex;
156
- } catch (error) {
157
- reportProgress({
158
- domain,
159
- totalFiles: 0,
160
- processedFiles: 0,
161
- status: 'error',
162
- error: error instanceof Error ? error.message : String(error),
163
- });
164
- throw error;
165
- }
166
- };
167
-
168
- /**
169
- * 掃描指定domain嘅文件
170
- */
171
- const scanDomainFiles = async (
172
- domain: string
173
- ): Promise<Array<{ uri: string; content: string }>> => {
174
- switch (domain) {
175
- case 'knowledge':
176
- return scanKnowledgeFiles();
177
- case 'codebase':
178
- return scanCodebaseFiles();
179
- default:
180
- throw new Error(`Unknown domain: ${domain}`);
181
- }
182
- };
183
-
184
- /**
185
- * 掃描knowledge文件
186
- */
187
- const scanKnowledgeFiles = async (): Promise<Array<{ uri: string; content: string }>> => {
188
- const { getKnowledgeDir } = await import('../../utils/paths.js');
189
- const knowledgeDir = getKnowledgeDir();
190
-
191
- if (!fs.existsSync(knowledgeDir)) {
192
- return [];
193
- }
194
-
195
- const files: Array<{ uri: string; content: string }> = [];
196
-
197
- const scan = (currentDir: string, baseDir: string) => {
198
- const entries = fs.readdirSync(currentDir, { withFileTypes: true });
199
-
200
- for (const entry of entries) {
201
- const fullPath = path.join(currentDir, entry.name);
202
-
203
- if (entry.isDirectory()) {
204
- scan(fullPath, baseDir);
205
- } else if (entry.isFile() && entry.name.endsWith('.md')) {
206
- const relativePath = path.relative(baseDir, fullPath);
207
- const uriPath = relativePath.replace(/\.md$/, '').replace(/\\/g, '/');
208
- const content = fs.readFileSync(fullPath, 'utf8');
209
-
210
- files.push({
211
- uri: `knowledge://${uriPath}`,
212
- content,
213
- });
214
- }
215
- }
216
- };
217
-
218
- scan(knowledgeDir, knowledgeDir);
219
- return files;
220
- };
221
-
222
- /**
223
- * 掃描codebase文件
224
- */
225
- const scanCodebaseFiles = async (): Promise<Array<{ uri: string; content: string }>> => {
226
- // 實現codebase文件掃描邏輯
227
- // 這裡需要根據實際需求實現
228
- return [];
229
- };
230
-
231
- /**
232
- * 建立向量索引
233
- */
234
- const buildVectorIndex = async (
235
- domain: string,
236
- files: Array<{ uri: string; content: string }>,
237
- batchSize: number
238
- ): Promise<void> => {
239
- if (!state.embeddingProvider) {
240
- throw new Error('Embedding provider not available');
241
- }
242
-
243
- // 初始化向量存儲
244
- const vectorPath = path.join(process.cwd(), '.sylphx-flow', `${domain}-vectors.hnsw`);
245
-
246
- const vectorStorage = new VectorStorage(vectorPath, state.embeddingProvider.dimensions || 1536);
247
- await vectorStorage.initialize();
248
-
249
- // FUNCTIONAL: Update map immutably
250
- const newStorages = new Map(state.vectorStorages);
251
- newStorages.set(domain, vectorStorage);
252
- updateState({ vectorStorages: newStorages });
253
-
254
- // 批量處理文件
255
- for (let i = 0; i < files.length; i += batchSize) {
256
- const batch = files.slice(i, i + batchSize);
257
- const embeddings = await state.embeddingProvider.generateEmbeddings(
258
- batch.map((file) => file.content)
259
- );
260
-
261
- for (let j = 0; j < batch.length; j++) {
262
- const file = batch[j];
263
- const embedding = embeddings[j];
264
-
265
- await vectorStorage.addDocument({
266
- id: file.uri,
267
- embedding,
268
- metadata: {
269
- type: domain,
270
- content: file.content.slice(0, 500),
271
- language: detectLanguage(file.uri),
272
- },
273
- });
274
- }
275
-
276
- // 報告向量化進度
277
- const progress = Math.min(i + batchSize, files.length);
278
- reportProgress({
279
- domain,
280
- totalFiles: files.length,
281
- processedFiles: progress,
282
- status: 'vectorizing',
283
- });
284
- }
285
-
286
- await vectorStorage.save();
287
- };
288
-
289
- /**
290
- * 檢測文件語言
291
- */
292
- const detectLanguage = (uri: string): string => {
293
- const ext = path.extname(uri).toLowerCase();
294
- const languageMap: Record<string, string> = {
295
- '.ts': 'typescript',
296
- '.js': 'javascript',
297
- '.jsx': 'jsx',
298
- '.tsx': 'tsx',
299
- '.py': 'python',
300
- '.java': 'java',
301
- '.cpp': 'cpp',
302
- '.c': 'c',
303
- '.cs': 'csharp',
304
- '.go': 'go',
305
- '.rs': 'rust',
306
- '.php': 'php',
307
- '.rb': 'ruby',
308
- '.swift': 'swift',
309
- '.kt': 'kotlin',
310
- '.scala': 'scala',
311
- '.md': 'markdown',
312
- '.txt': 'text',
313
- '.json': 'json',
314
- '.yaml': 'yaml',
315
- '.yml': 'yaml',
316
- '.xml': 'xml',
317
- '.html': 'html',
318
- '.css': 'css',
319
- '.scss': 'scss',
320
- '.sass': 'sass',
321
- '.less': 'less',
322
- '.sql': 'sql',
323
- };
324
-
325
- return languageMap[ext] || 'unknown';
326
- };
327
-
328
- /**
329
- * 更新索引
330
- */
331
- const updateIndex = async (domain: string, content: ContentMetadata): Promise<void> => {
332
- // 更新TF-IDF索引(需要重新建立整個index)
333
- // 更新向量索引(可以單獨更新)
334
- if (state.vectorStorages.has(domain) && state.embeddingProvider) {
335
- const vectorStorage = state.vectorStorages.get(domain)!;
336
- const embedding = await state.embeddingProvider.generateEmbeddings([content.content]);
337
-
338
- await vectorStorage.addDocument({
339
- id: content.uri,
340
- embedding: embedding[0],
341
- metadata: {
342
- type: content.type,
343
- content: content.content.slice(0, 500),
344
- category: content.category,
345
- language: content.language,
346
- path: content.path,
347
- },
348
- });
349
-
350
- await vectorStorage.save();
351
- }
352
- };
353
-
354
- /**
355
- * 從索引中移除文檔
356
- */
357
- const removeFromIndex = async (domain: string, uri: string): Promise<void> => {
358
- // 從向量索引中移除
359
- if (state.vectorStorages.has(domain)) {
360
- const vectorStorage = state.vectorStorages.get(domain)!;
361
- await vectorStorage.removeDocument(uri);
362
- await vectorStorage.save();
363
- }
364
- };
365
-
366
- // Return service interface
367
- return {
368
- initialize,
369
- onProgress,
370
- offProgress,
371
- buildIndex,
372
- updateIndex,
373
- removeFromIndex,
374
- };
375
- };
376
-
377
- // Factory function for creating default instance
378
- export function getIndexerService(): IndexerService {
379
- return createIndexerService();
380
- }