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
@@ -17,6 +17,7 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import CodeGraph, { findNearestCodeGraphRoot } from '../index';
|
||||
import { watchDisabledReason } from '../sync';
|
||||
import { StdioTransport, JsonRpcRequest, JsonRpcNotification, ErrorCodes } from './transport';
|
||||
import { tools, ToolHandler } from './tools';
|
||||
import { SERVER_INSTRUCTIONS } from './server-instructions';
|
||||
@@ -173,6 +174,18 @@ export class MCPServer {
|
||||
private startWatching(): void {
|
||||
if (!this.cg) return;
|
||||
|
||||
// When the watcher is intentionally disabled (e.g. WSL2 /mnt drives, or
|
||||
// CODEGRAPH_NO_WATCH=1), say so explicitly and tell the user how to keep
|
||||
// the graph fresh — otherwise the silent staleness is hard to diagnose.
|
||||
const disabledReason = watchDisabledReason(this.projectPath ?? process.cwd());
|
||||
if (disabledReason) {
|
||||
process.stderr.write(
|
||||
`[CodeGraph MCP] File watcher disabled — ${disabledReason}. ` +
|
||||
`The graph will not auto-update; run \`codegraph sync\` (or install the git sync hooks via \`codegraph init\`) to refresh.\n`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const started = this.cg.watch({
|
||||
onSyncComplete: (result) => {
|
||||
if (result.filesChanged > 0) {
|
||||
@@ -188,6 +201,11 @@ export class MCPServer {
|
||||
|
||||
if (started) {
|
||||
process.stderr.write('[CodeGraph MCP] File watcher active — graph will auto-sync on changes\n');
|
||||
} else {
|
||||
// start() can also return false when recursive fs.watch isn't supported.
|
||||
process.stderr.write(
|
||||
'[CodeGraph MCP] File watcher unavailable on this platform — run `codegraph sync` to refresh the graph after changes.\n'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user