* Start file watching on the active CodeGraph instance. Idempotent — the * watcher is per-engine, not per-session, which is why the daemon path * collapses N inotify sets to one. The wording of the disabled-reason log * exactly matches the prior in-tree implementation so log-driven dashboar
()
| 276 | * keep working. |
| 277 | */ |
| 278 | private startWatching(): void { |
| 279 | if (!this.cg || this.watcherStarted || !this.opts.watch) return; |
| 280 | |
| 281 | const disabledReason = watchDisabledReason(this.projectPath ?? process.cwd()); |
| 282 | if (disabledReason) { |
| 283 | process.stderr.write( |
| 284 | `[CodeGraph MCP] File watcher disabled — ${disabledReason}. ` + |
| 285 | `The graph will not auto-update; run \`codegraph sync\` (or install the git sync hooks via \`codegraph init\`) to refresh.\n` |
| 286 | ); |
| 287 | this.watcherStarted = true; |
| 288 | return; |
| 289 | } |
| 290 | |
| 291 | // Optional override for the debounce window via env var (issue #403). |
| 292 | // Useful for workspaces with bursty writes (formatter-on-save chains, |
| 293 | // large generated outputs) where the 2s default fires too often. Clamped |
| 294 | // to [100ms, 60s]; out-of-range / non-numeric values fall back to the |
| 295 | // FileWatcher default. We log the active value so it's discoverable. |
| 296 | const debounceMs = parseDebounceEnv(process.env.CODEGRAPH_WATCH_DEBOUNCE_MS); |
| 297 | if (debounceMs !== undefined) { |
| 298 | process.stderr.write(`[CodeGraph MCP] File watcher debounce: ${debounceMs}ms (CODEGRAPH_WATCH_DEBOUNCE_MS)\n`); |
| 299 | } |
| 300 | |
| 301 | const started = this.cg.watch({ |
| 302 | debounceMs, |
| 303 | onSyncComplete: (result) => { |
| 304 | if (result.filesChanged > 0) { |
| 305 | process.stderr.write( |
| 306 | `[CodeGraph MCP] Auto-synced ${result.filesChanged} file(s) in ${result.durationMs}ms\n` |
| 307 | ); |
| 308 | } |
| 309 | }, |
| 310 | onSyncError: (err) => { |
| 311 | process.stderr.write(`[CodeGraph MCP] Auto-sync error: ${err.message}\n`); |
| 312 | }, |
| 313 | onDegraded: (reason) => { |
| 314 | // Live watching gave up permanently (watch-resource exhaustion or a |
| 315 | // write lock held past the retry budget). Say so loudly and ONCE — the |
| 316 | // graph will no longer auto-update, so a long-running MCP session must |
| 317 | // not keep assuming it's fresh. The reason already names the remedy |
| 318 | // (`codegraph sync` / git sync hooks). |
| 319 | process.stderr.write(`[CodeGraph MCP] File watcher degraded — ${reason}\n`); |
| 320 | }, |
| 321 | }); |
| 322 | |
| 323 | this.watcherStarted = true; |
| 324 | if (started) { |
| 325 | process.stderr.write('[CodeGraph MCP] File watcher active — graph will auto-sync on changes\n'); |
| 326 | } else { |
| 327 | process.stderr.write( |
| 328 | '[CodeGraph MCP] File watcher unavailable on this platform — run `codegraph sync` to refresh the graph after changes.\n' |
| 329 | ); |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | /** |
| 334 | * Reconcile the index with the current filesystem once, right after open — |
no test coverage detected