import chokidar, { FSWatcher } from 'chokidar'; import path from 'path'; import { NOTEBOOK_ROOT } from '../config/paths.js'; import { eventBus } from '../events/eventBus.js'; import { logger } from '../utils/logger.js'; import { toPosixPath } from '../../shared/utils/path.js'; let watcher: FSWatcher | null = null; export const startWatcher = (): void => { if (watcher) return; logger.info(`Starting file watcher for: ${NOTEBOOK_ROOT}`); watcher = chokidar.watch(NOTEBOOK_ROOT, { ignored: /(^|[\/\\])\../, persistent: true, ignoreInitial: true, }); const broadcast = (event: string, changedPath: string) => { const rel = path.relative(NOTEBOOK_ROOT, changedPath); if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return; logger.info(`File event: ${event} - ${rel}`); eventBus.broadcast({ event, path: toPosixPath(rel) }); }; watcher .on('add', (p) => broadcast('add', p)) .on('change', (p) => broadcast('change', p)) .on('unlink', (p) => broadcast('unlink', p)) .on('addDir', (p) => broadcast('addDir', p)) .on('unlinkDir', (p) => broadcast('unlinkDir', p)) .on('ready', () => logger.info('File watcher ready')) .on('error', (err) => logger.error('File watcher error:', err)); }; export const stopWatcher = async (): Promise => { if (watcher) { await watcher.close(); watcher = null; } };