UNPKG

claude-flow-novice

Version:

Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.

133 lines (132 loc) 4.69 kB
/** * RuVectorIndex - Core API for semantic codebase search */ import { glob } from 'glob'; import * as path from 'path'; import * as fs from 'fs/promises'; import { createEmbeddingProvider } from '../embeddings/index.js'; import { createFileProcessor } from './file-processor.js'; export class RuVectorIndex { embeddingProvider; fileProcessor; db = null; workingDir; storagePath; include; exclude; batchSize; verbose; constructor(options = {}, workingDir = process.cwd()){ this.workingDir = workingDir; this.storagePath = options.storagePath ?? './data/codebase.db'; this.include = options.indexing?.include ?? [ '**/*.ts', '**/*.js', '**/*.md' ]; this.exclude = options.indexing?.exclude ?? [ 'node_modules/**', 'dist/**', '.git/**' ]; this.batchSize = options.indexing?.batchSize ?? 50; this.verbose = options.verbose ?? false; this.embeddingProvider = createEmbeddingProvider({ provider: options.embedding?.provider ?? 'openai', model: options.embedding?.model, apiKey: options.embedding?.apiKey, baseUrl: options.embedding?.baseUrl }); this.fileProcessor = createFileProcessor(options.indexing?.maxFileSize); } async getDb() { if (this.db) return this.db; const { VectorDB } = await import('@ruvector/core'); const storagePath = path.resolve(this.workingDir, this.storagePath); await fs.mkdir(path.dirname(storagePath), { recursive: true }); this.db = new VectorDB({ dimensions: 1536, storagePath }); return this.db; } log(msg) { if (this.verbose) console.log(`[ruvector] ${msg}`); } async indexDirectory(directory = '.') { const absoluteDir = path.resolve(this.workingDir, directory); this.log(`Indexing: ${absoluteDir}`); const patterns = this.include.map((p)=>path.join(absoluteDir, p)); const files = await glob(patterns, { ignore: this.exclude, nodir: true, absolute: true }); this.log(`Found ${files.length} files`); const stats = { totalFiles: files.length, indexedFiles: 0, failedFiles: 0, lastIndexedAt: new Date() }; const db = await this.getDb(); for(let i = 0; i < files.length; i += this.batchSize){ const batch = files.slice(i, i + this.batchSize); const processed = []; for (const file of batch){ const result = await this.fileProcessor.process(file); if (result) processed.push(result); else stats.failedFiles++; } if (processed.length === 0) continue; try { const texts = processed.map((p)=>p.content); const embeddings = await this.embeddingProvider.embedBatch(texts); const items = processed.map((p, idx)=>({ id: path.relative(this.workingDir, p.path), vector: new Float32Array(embeddings[idx].embedding) })); await db.upsert(items); stats.indexedFiles += processed.length; this.log(`Indexed ${stats.indexedFiles}/${stats.totalFiles}`); } catch (error) { this.log(`Batch failed: ${error.message}`); stats.failedFiles += processed.length; } } return stats; } async search(query, options = {}) { const limit = options.limit ?? 10; const { embedding } = await this.embeddingProvider.embed(query); const db = await this.getDb(); const results = await db.search({ vector: new Float32Array(embedding), k: limit }); return results.map((r)=>({ path: r.id, score: r.score })); } async getStats() { const db = await this.getDb(); const count = await db.count(); return { totalFiles: count, indexedFiles: count, failedFiles: 0, lastIndexedAt: null }; } async clear() { const storagePath = path.resolve(this.workingDir, this.storagePath); try { await fs.unlink(storagePath); this.db = null; } catch {} } } export { createFileProcessor } from './file-processor.js'; //# sourceMappingURL=index.js.map