feat: Add file watcher with debounced auto-sync and comprehensive test coverage
Addresses the need for automatic graph synchronization on file changes. Implements FileWatcher using native OS file events (FSEvents/inotify/ReadDirectoryChangesW) with 2-second debouncing to prevent thrashing on rapid saves. Filters changes against include/exclude patterns and ignores .codegraph directory modifications. Integrates with CodeGraph API (watch/unwatch/isWatching methods) and MCP server for automatic activation. Updates documentation to reflect shift from semantic to full-text search and removal of manual hook installation requirements.
This commit is contained in:
@@ -48,6 +48,7 @@ import {
|
||||
import { GraphTraverser, GraphQueryManager } from './graph';
|
||||
import { ContextBuilder, createContextBuilder } from './context';
|
||||
import { Mutex, FileLock } from './utils';
|
||||
import { FileWatcher, WatchOptions } from './sync';
|
||||
|
||||
// Re-export types for consumers
|
||||
export * from './types';
|
||||
@@ -77,6 +78,7 @@ export {
|
||||
defaultLogger,
|
||||
} from './errors';
|
||||
export { Mutex, FileLock, processInBatches, debounce, throttle, MemoryMonitor } from './utils';
|
||||
export { FileWatcher, WatchOptions } from './sync';
|
||||
export { MCPServer } from './mcp';
|
||||
|
||||
/**
|
||||
@@ -140,6 +142,9 @@ export class CodeGraph {
|
||||
// File lock for preventing concurrent writes across processes (CLI, MCP, git hooks)
|
||||
private fileLock: FileLock;
|
||||
|
||||
// File watcher for auto-sync on file changes
|
||||
private watcher: FileWatcher | null = null;
|
||||
|
||||
private constructor(
|
||||
db: DatabaseConnection,
|
||||
queries: QueryBuilder,
|
||||
@@ -319,6 +324,7 @@ export class CodeGraph {
|
||||
* Close the CodeGraph instance and release resources
|
||||
*/
|
||||
close(): void {
|
||||
this.unwatch();
|
||||
// Release file lock if held
|
||||
this.fileLock.release();
|
||||
this.db.close();
|
||||
@@ -491,6 +497,53 @@ export class CodeGraph {
|
||||
return this.indexMutex.isLocked();
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// File Watching
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Start watching for file changes and auto-syncing.
|
||||
*
|
||||
* Uses native OS file events (FSEvents on macOS, inotify on Linux 19+,
|
||||
* ReadDirectoryChangesW on Windows) with debouncing to avoid thrashing.
|
||||
*
|
||||
* @param options - Watch options (debounce delay, callbacks)
|
||||
* @returns true if watching started successfully
|
||||
*/
|
||||
watch(options: WatchOptions = {}): boolean {
|
||||
if (this.watcher?.isActive()) return true;
|
||||
|
||||
this.watcher = new FileWatcher(
|
||||
this.projectRoot,
|
||||
this.config,
|
||||
async () => {
|
||||
const result = await this.sync();
|
||||
const filesChanged = result.filesAdded + result.filesModified + result.filesRemoved;
|
||||
return { filesChanged, durationMs: result.durationMs };
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
return this.watcher.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop watching for file changes.
|
||||
*/
|
||||
unwatch(): void {
|
||||
if (this.watcher) {
|
||||
this.watcher.stop();
|
||||
this.watcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file watcher is active.
|
||||
*/
|
||||
isWatching(): boolean {
|
||||
return this.watcher?.isActive() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files that have changed since last index
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user