Recursive fs.watch on a WSL2 /mnt NTFS/9p mount walks the directory tree with every readdir/stat crossing the Windows boundary, stalling the event loop long enough to blow past opencode's 30s MCP handshake timeout so the tools never appear. This is the file-watcher half of the #172 fix, which moved the DB/WASM open off the handshake but left the watcher on the critical path. - Add watchDisabledReason() policy: CODEGRAPH_NO_WATCH (off) > CODEGRAPH_FORCE_WATCH (force on) > WSL2 + /mnt auto-detect (off). FileWatcher.start() and the MCP server both honor it; the server now logs why watching is off and how to refresh. - Add `codegraph serve --mcp --no-watch`. - When watching is off, init/install offer git sync hooks (post-commit, post-merge, post-checkout) that run `codegraph sync` in the background, or fall back to manual sync; either way the user is told the index stays frozen until re-synced. uninit removes the hooks. - Tests: watch-policy + git-hooks (idempotency, user-content preservation, core.hooksPath). Root-cause analysis and workaround by @mengfanbo123. Closes #199 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
79b9601aae
commit
cf7db7cb98
@@ -13,6 +13,7 @@ import { CodeGraphConfig } from '../types';
|
||||
import { shouldIncludeFile } from '../extraction';
|
||||
import { logDebug, logWarn } from '../errors';
|
||||
import { normalizePath } from '../utils';
|
||||
import { watchDisabledReason } from './watch-policy';
|
||||
|
||||
/**
|
||||
* Options for the file watcher
|
||||
@@ -82,6 +83,16 @@ export class FileWatcher {
|
||||
if (this.watcher) return true; // Already watching
|
||||
this.stopped = false;
|
||||
|
||||
// Some environments make recursive fs.watch unusable — most notably WSL2
|
||||
// /mnt/ drives, where setup blocks long enough to break MCP startup
|
||||
// handshakes (issue #199). Skip watching there; callers fall back to
|
||||
// manual `codegraph sync` or the git sync hooks.
|
||||
const disabledReason = watchDisabledReason(this.projectRoot);
|
||||
if (disabledReason) {
|
||||
logDebug('File watcher disabled', { reason: disabledReason, projectRoot: this.projectRoot });
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.watcher = fs.watch(
|
||||
this.projectRoot,
|
||||
|
||||
Reference in New Issue
Block a user