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
+26
-1
@@ -415,6 +415,10 @@ program
|
||||
clack.log.success(`${target.displayName}: ${file.action} ${file.path}`);
|
||||
}
|
||||
} catch { /* non-fatal */ }
|
||||
try {
|
||||
const { offerWatchFallback } = await import('../installer');
|
||||
await offerWatchFallback(clack, projectPath);
|
||||
} catch { /* non-fatal */ }
|
||||
clack.outro('');
|
||||
return;
|
||||
}
|
||||
@@ -459,6 +463,11 @@ program
|
||||
clack.log.info('Run "codegraph index" to index the project');
|
||||
}
|
||||
|
||||
try {
|
||||
const { offerWatchFallback } = await import('../installer');
|
||||
await offerWatchFallback(clack, projectPath);
|
||||
} catch { /* non-fatal */ }
|
||||
|
||||
clack.outro('Done');
|
||||
cg.destroy();
|
||||
} catch (err) {
|
||||
@@ -505,6 +514,15 @@ program
|
||||
const cg = CodeGraph.openSync(projectPath);
|
||||
cg.uninitialize();
|
||||
|
||||
// Clean up any git sync hooks we installed (no-op if none / not a repo).
|
||||
try {
|
||||
const { removeGitSyncHook } = await import('../sync/git-hooks');
|
||||
const removed = removeGitSyncHook(projectPath);
|
||||
if (removed.installed.length > 0) {
|
||||
info(`Removed git ${removed.installed.join(', ')} sync hook${removed.installed.length > 1 ? 's' : ''}`);
|
||||
}
|
||||
} catch { /* non-fatal */ }
|
||||
|
||||
success(`Removed CodeGraph from ${projectPath}`);
|
||||
} catch (err) {
|
||||
error(`Failed to uninitialize: ${err instanceof Error ? err.message : String(err)}`);
|
||||
@@ -1085,9 +1103,16 @@ program
|
||||
.description('Start CodeGraph as an MCP server for AI assistants')
|
||||
.option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
|
||||
.option('--mcp', 'Run as MCP server (stdio transport)')
|
||||
.action(async (options: { path?: string; mcp?: boolean }) => {
|
||||
.option('--no-watch', 'Disable the file watcher (no auto-sync; useful on slow filesystems like WSL2 /mnt drives)')
|
||||
.action(async (options: { path?: string; mcp?: boolean; watch?: boolean }) => {
|
||||
const projectPath = options.path ? resolveProjectPath(options.path) : undefined;
|
||||
|
||||
// Commander sets watch=false when --no-watch is passed. Route it through
|
||||
// the same env-var chokepoint the watcher and MCP server already honor.
|
||||
if (options.watch === false) {
|
||||
process.env.CODEGRAPH_NO_WATCH = '1';
|
||||
}
|
||||
|
||||
try {
|
||||
if (options.mcp) {
|
||||
// Start MCP server - it handles initialization lazily based on rootUri from client
|
||||
|
||||
Reference in New Issue
Block a user