UNPKG

aiwg

Version:

Cognitive architecture for AI-augmented software development with structured memory, ensemble validation, and closed-loop correction. FAIR-aligned artifacts, 84% cost reduction via human-in-the-loop, standards adopted by 100+ organizations.

142 lines (118 loc) 3.33 kB
import fs from 'fs'; import path from 'path'; import { EventEmitter } from 'events'; export class FileWatcher extends EventEmitter { constructor(config, debounceMs = 500) { super(); this.config = config; this.debounceMs = debounceMs; this.watchers = []; this.timers = new Map(); this.eventsReceived = 0; } start() { if (!this.config.enabled) { return; } for (const watchPath of this.config.paths) { try { const watcher = this.watchPath(watchPath); this.watchers.push(watcher); } catch (error) { console.error(`Failed to watch ${watchPath.path}:`, error.message); } } } watchPath(watchConfig) { const { path: watchPath, events, ignore = [], extensions = [] } = watchConfig; if (!fs.existsSync(watchPath)) { console.warn(`Watch path does not exist: ${watchPath}`); return null; } const watcher = fs.watch(watchPath, { recursive: true }, (eventType, filename) => { if (!filename) return; if (this.shouldIgnore(filename, ignore)) { return; } if (extensions.length > 0 && !this.hasExtension(filename, extensions)) { return; } const filepath = path.join(watchPath, filename); const normalizedEvent = this.normalizeEventType(eventType, filepath); if (events && !events.includes(normalizedEvent)) { return; } this.handleRawEvent(normalizedEvent, filepath); }); return watcher; } handleRawEvent(eventType, filepath) { const key = `${eventType}:${filepath}`; if (this.timers.has(key)) { clearTimeout(this.timers.get(key)); } this.timers.set(key, setTimeout(() => { this.eventsReceived++; this.emit('event', { source: 'watch', type: `file.${eventType}`, payload: { path: filepath, event_type: eventType } }); this.timers.delete(key); }, this.debounceMs)); } shouldIgnore(filename, ignorePatterns) { for (const pattern of ignorePatterns) { if (pattern.includes('*')) { const regex = new RegExp(pattern.replace(/\*/g, '.*')); if (regex.test(filename)) { return true; } } else if (filename.includes(pattern)) { return true; } } if (filename.includes('node_modules') || filename.includes('.git') || filename.includes('.aiwg/working') || filename.includes('.aiwg/daemon')) { return true; } return false; } hasExtension(filename, extensions) { return extensions.some(ext => filename.endsWith(ext)); } normalizeEventType(eventType, filepath) { if (eventType === 'rename') { return fs.existsSync(filepath) ? 'create' : 'delete'; } if (eventType === 'change') { return 'modify'; } return eventType; } stop() { for (const watcher of this.watchers) { if (watcher) { watcher.close(); } } this.watchers = []; for (const timer of this.timers.values()) { clearTimeout(timer); } this.timers.clear(); } getStats() { return { enabled: this.config.enabled, paths: this.config.paths.map(p => p.path), events_received: this.eventsReceived }; } } export default FileWatcher;