@sylphx/flow 1.1.1 → 1.2.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 (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -1
  3. package/src/commands/hook-command.ts +10 -230
  4. package/src/composables/index.ts +0 -1
  5. package/src/config/servers.ts +35 -78
  6. package/src/core/interfaces.ts +0 -33
  7. package/src/domains/index.ts +0 -2
  8. package/src/index.ts +0 -4
  9. package/src/services/mcp-service.ts +0 -16
  10. package/src/targets/claude-code.ts +3 -9
  11. package/src/targets/functional/claude-code-logic.ts +4 -22
  12. package/src/targets/opencode.ts +0 -6
  13. package/src/types/mcp.types.ts +29 -38
  14. package/src/types/target.types.ts +0 -2
  15. package/src/types.ts +0 -1
  16. package/src/commands/codebase-command.ts +0 -168
  17. package/src/commands/knowledge-command.ts +0 -161
  18. package/src/composables/useTargetConfig.ts +0 -45
  19. package/src/core/formatting/bytes.test.ts +0 -115
  20. package/src/core/validation/limit.test.ts +0 -155
  21. package/src/core/validation/query.test.ts +0 -44
  22. package/src/domains/codebase/index.ts +0 -5
  23. package/src/domains/codebase/tools.ts +0 -139
  24. package/src/domains/knowledge/index.ts +0 -10
  25. package/src/domains/knowledge/resources.ts +0 -537
  26. package/src/domains/knowledge/tools.ts +0 -174
  27. package/src/services/search/base-indexer.ts +0 -156
  28. package/src/services/search/codebase-indexer-types.ts +0 -38
  29. package/src/services/search/codebase-indexer.ts +0 -647
  30. package/src/services/search/embeddings-provider.ts +0 -455
  31. package/src/services/search/embeddings.ts +0 -316
  32. package/src/services/search/functional-indexer.ts +0 -323
  33. package/src/services/search/index.ts +0 -27
  34. package/src/services/search/indexer.ts +0 -380
  35. package/src/services/search/knowledge-indexer.ts +0 -422
  36. package/src/services/search/semantic-search.ts +0 -244
  37. package/src/services/search/tfidf.ts +0 -559
  38. package/src/services/search/unified-search-service.ts +0 -888
  39. package/src/services/storage/cache-storage.ts +0 -487
  40. package/src/services/storage/drizzle-storage.ts +0 -581
  41. package/src/services/storage/index.ts +0 -15
  42. package/src/services/storage/lancedb-vector-storage.ts +0 -494
  43. package/src/services/storage/memory-storage.ts +0 -268
  44. package/src/services/storage/separated-storage.ts +0 -467
  45. 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
- }