* 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
()
| 229 | * keep working. |
| 230 | */ |
| 231 | private startWatching(): void { |
| 232 | if (!this.cg || this.watcherStarted || !this.opts.watch) return; |
| 233 | |
| 234 | const disabledReason = watchDisabledReason(this.projectPath ?? process.cwd()); |
| 235 | if (disabledReason) { |
| 236 | process.stderr.write( |
| 237 | `[CodeGraph MCP] File watcher disabled — ${disabledReason}. ` + |
| 238 | `The graph will not auto-update; run \`codegraph sync\` (or install the git sync hooks via \`codegraph init\`) to refresh.\n` |
| 239 | ); |
| 240 | this.watcherStarted = true; |
| 241 | return; |
| 242 | } |
| 243 | |
| 244 | // Optional override for the debounce window via env var (issue #403). |
| 245 | // Useful for workspaces with bursty writes (formatter-on-save chains, |
| 246 | // large generated outputs) where the 2s default fires too often. Clamped |
| 247 | // to [100ms, 60s]; out-of-range / non-numeric values fall back to the |
| 248 | // FileWatcher default. We log the active value so it's discoverable. |
| 249 | const debounceMs = parseDebounceEnv(process.env.CODEGRAPH_WATCH_DEBOUNCE_MS); |
| 250 | if (debounceMs !== undefined) { |
| 251 | process.stderr.write(`[CodeGraph MCP] File watcher debounce: ${debounceMs}ms (CODEGRAPH_WATCH_DEBOUNCE_MS)\n`); |
| 252 | } |
| 253 | |
| 254 | const started = this.cg.watch({ |
| 255 | debounceMs, |
| 256 | onSyncComplete: (result) => { |
| 257 | if (result.filesChanged > 0) { |
| 258 | process.stderr.write( |
| 259 | `[CodeGraph MCP] Auto-synced ${result.filesChanged} file(s) in ${result.durationMs}ms\n` |
| 260 | ); |
| 261 | } |
| 262 | }, |
| 263 | onSyncError: (err) => { |
| 264 | process.stderr.write(`[CodeGraph MCP] Auto-sync error: ${err.message}\n`); |
| 265 | }, |
| 266 | onDegraded: (reason) => { |
| 267 | // Live watching gave up permanently (watch-resource exhaustion or a |
| 268 | // write lock held past the retry budget). Say so loudly and ONCE — the |
| 269 | // graph will no longer auto-update, so a long-running MCP session must |
| 270 | // not keep assuming it's fresh. The reason already names the remedy |
| 271 | // (`codegraph sync` / git sync hooks). |
| 272 | process.stderr.write(`[CodeGraph MCP] File watcher degraded — ${reason}\n`); |
| 273 | }, |
| 274 | }); |
| 275 | |
| 276 | this.watcherStarted = true; |
| 277 | if (started) { |
| 278 | process.stderr.write('[CodeGraph MCP] File watcher active — graph will auto-sync on changes\n'); |
| 279 | } else { |
| 280 | process.stderr.write( |
| 281 | '[CodeGraph MCP] File watcher unavailable on this platform — run `codegraph sync` to refresh the graph after changes.\n' |
| 282 | ); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | /** |
| 287 | * Reconcile the index with the current filesystem once, right after open — |
no test coverage detected