UNPKG

giga-code

Version:

A personal AI CLI assistant powered by Grok for local development.

292 lines 9.89 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.IndexingService = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const events_1 = require("events"); const rag_context_service_1 = require("./rag-context-service"); const rag_config_1 = require("../utils/rag-config"); class IndexingService extends events_1.EventEmitter { constructor(projectPath = process.cwd()) { super(); this.fileWatcher = null; this.isWatching = false; this.jobQueue = []; this.currentJob = null; this.jobIdCounter = 1; this.lastFullIndexTime = null; // Debounce file changes to avoid excessive indexing this.pendingChanges = new Set(); this.changeDebounceTimer = null; this.DEBOUNCE_DELAY = 2000; // 2 seconds this.projectPath = projectPath; this.ragService = new rag_context_service_1.RAGContextService(projectPath); } async initialize() { try { await this.ragService.initialize(); this.emit('initialized'); } catch (error) { this.emit('error', error); throw error; } } async startWatching() { if (this.isWatching) { return; } const config = rag_config_1.RAGConfigManager.loadConfig(this.projectPath); if (!config.enabled) { return; } try { await this.initialize(); // Watch for file changes this.fileWatcher = fs.watch(this.projectPath, { recursive: true }, (eventType, filename) => { if (filename && this.shouldWatchFile(filename)) { this.handleFileChange(eventType, filename); } }); this.isWatching = true; this.emit('watching-started'); // Check if we need to do an initial full index const indexInfo = await this.ragService.getIndexInfo(); if (indexInfo.count === 0) { this.scheduleFullIndex(); } } catch (error) { this.emit('error', error); throw error; } } async stopWatching() { if (this.fileWatcher) { this.fileWatcher.close(); this.fileWatcher = null; } if (this.changeDebounceTimer) { clearTimeout(this.changeDebounceTimer); this.changeDebounceTimer = null; } this.isWatching = false; this.emit('watching-stopped'); } shouldWatchFile(filename) { const config = rag_config_1.RAGConfigManager.loadConfig(this.projectPath); const fullPath = path.join(this.projectPath, filename); // Skip directories try { const stat = fs.statSync(fullPath); if (stat.isDirectory()) { return false; } } catch { return false; // File doesn't exist or can't be accessed } // Check include patterns const includeMatch = config.includePatterns.some(pattern => { const regex = this.globToRegex(pattern); return regex.test(filename) || regex.test(fullPath); }); if (!includeMatch) { return false; } // Check exclude patterns const excludeMatch = config.excludePatterns.some(pattern => { const regex = this.globToRegex(pattern); return regex.test(filename) || regex.test(fullPath); }); return !excludeMatch; } globToRegex(pattern) { const regexPattern = pattern .replace(/\*\*/g, '.*') .replace(/\*/g, '[^/]*') .replace(/\?/g, '[^/]'); return new RegExp(`^${regexPattern}$`); } handleFileChange(eventType, filename) { const fullPath = path.join(this.projectPath, filename); // Skip if file doesn't exist (was deleted) if (!fs.existsSync(fullPath)) { return; } this.pendingChanges.add(fullPath); // Reset debounce timer if (this.changeDebounceTimer) { clearTimeout(this.changeDebounceTimer); } this.changeDebounceTimer = setTimeout(() => { this.processFileChanges(); }, this.DEBOUNCE_DELAY); } async processFileChanges() { if (this.pendingChanges.size === 0) { return; } const changedFiles = Array.from(this.pendingChanges); this.pendingChanges.clear(); this.scheduleIncrementalIndex(changedFiles); } scheduleFullIndex() { const jobId = `full-${this.jobIdCounter++}`; const job = { id: jobId, type: 'full', status: 'pending' }; this.jobQueue.push(job); this.processJobQueue(); return jobId; } scheduleIncrementalIndex(filePaths) { const jobId = `incremental-${this.jobIdCounter++}`; const job = { id: jobId, type: 'incremental', filePaths, status: 'pending' }; this.jobQueue.push(job); this.processJobQueue(); return jobId; } scheduleFileIndex(filePath) { const jobId = `file-${this.jobIdCounter++}`; const job = { id: jobId, type: 'file', filePaths: [filePath], status: 'pending' }; this.jobQueue.push(job); this.processJobQueue(); return jobId; } async processJobQueue() { if (this.currentJob || this.jobQueue.length === 0) { return; } const job = this.jobQueue.shift(); this.currentJob = job; job.status = 'running'; job.startTime = new Date(); this.emit('job-started', job); try { await this.executeJob(job); job.status = 'completed'; job.endTime = new Date(); this.emit('job-completed', job); if (job.type === 'full') { this.lastFullIndexTime = job.endTime; } } catch (error) { job.status = 'failed'; job.endTime = new Date(); job.error = error.message; this.emit('job-failed', job); } finally { this.currentJob = null; // Process next job in queue if (this.jobQueue.length > 0) { setImmediate(() => this.processJobQueue()); } } } async executeJob(job) { const startTime = Date.now(); switch (job.type) { case 'full': await this.ragService.indexProject(); break; case 'incremental': case 'file': if (job.filePaths && job.filePaths.length > 0) { // For now, we'll do a simple re-index of changed files // In the future, we could implement more sophisticated incremental updates await this.ragService.indexProject(); } break; } const endTime = Date.now(); const indexInfo = await this.ragService.getIndexInfo(); job.stats = { filesProcessed: job.filePaths?.length || 0, chunksCreated: indexInfo.count, duration: endTime - startTime }; } getCurrentJob() { return this.currentJob; } getJobQueue() { return [...this.jobQueue]; } getJobHistory() { // In a real implementation, you might want to persist this return []; } async getIndexingStatus() { const indexInfo = await this.ragService.getIndexInfo(); return { isWatching: this.isWatching, currentJob: this.currentJob, queueLength: this.jobQueue.length, lastFullIndex: this.lastFullIndexTime, indexInfo }; } async clearIndex() { // Stop current job if running if (this.currentJob) { } // Clear job queue this.jobQueue = []; this.currentJob = null; // Clear the actual index await this.ragService.clearIndex(); this.lastFullIndexTime = null; this.emit('index-cleared'); } // Static method to create and start a background indexing service static async createAndStart(projectPath = process.cwd()) { const service = new IndexingService(projectPath); try { await service.startWatching(); return service; } catch (error) { return service; } } } exports.IndexingService = IndexingService; //# sourceMappingURL=indexing-service.js.map