* Handle codegraph_status
(args: Record<string, unknown>)
| 4291 | * Handle codegraph_status |
| 4292 | */ |
| 4293 | private async handleStatus(args: Record<string, unknown>): Promise<ToolResult> { |
| 4294 | let cg = this.getCodeGraph(args.projectPath as string | undefined); |
| 4295 | // Same trick as withStalenessNotice — when an explicit projectPath |
| 4296 | // resolves to the same project as the default session cg, prefer the |
| 4297 | // default so getPendingFiles() (only populated by the default's watcher) |
| 4298 | // is non-empty when there are pending edits. |
| 4299 | if (this.cg && cg !== this.cg) { |
| 4300 | try { |
| 4301 | if (resolvePath(this.cg.getProjectRoot()) === resolvePath(cg.getProjectRoot())) { |
| 4302 | cg = this.cg; |
| 4303 | } |
| 4304 | } catch { /* closed instance — leave as is */ } |
| 4305 | } |
| 4306 | const stats = cg.getStats(); |
| 4307 | |
| 4308 | // Warn when this index actually belongs to a different git working tree |
| 4309 | // (e.g. the server resolved up from a nested worktree to the main checkout). |
| 4310 | // Queries then reflect that tree's branch, not the worktree being edited. |
| 4311 | // status shows the verbose, multi-line form; the read tools get the compact |
| 4312 | // one-liner via withWorktreeNotice. Both share the cached detection. |
| 4313 | const mismatch = this.worktreeMismatchFor(args.projectPath as string | undefined); |
| 4314 | |
| 4315 | const lines: string[] = [ |
| 4316 | '**CodeGraph Status**', |
| 4317 | '', |
| 4318 | ]; |
| 4319 | if (mismatch) { |
| 4320 | lines.push(`> ⚠ ${worktreeMismatchWarning(mismatch).replace(/\n/g, '\n> ')}`, ''); |
| 4321 | } |
| 4322 | lines.push( |
| 4323 | `**Files indexed:** ${stats.fileCount}`, |
| 4324 | `**Total nodes:** ${stats.nodeCount}`, |
| 4325 | `**Total edges:** ${stats.edgeCount}`, |
| 4326 | `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`, |
| 4327 | ); |
| 4328 | |
| 4329 | // Surface the active SQLite backend (node:sqlite, Node's built-in real |
| 4330 | // SQLite — full WAL + FTS5, no native build). |
| 4331 | lines.push(`**Backend:** node:sqlite (Node built-in) — full WAL + FTS5`); |
| 4332 | |
| 4333 | // Effective journal mode. 'wal' ⇒ concurrent reads never block on a writer; |
| 4334 | // anything else ⇒ they can ("database is locked"). node:sqlite supports WAL |
| 4335 | // everywhere, so a non-wal mode means the filesystem can't (network/ |
| 4336 | // virtualized mounts, WSL2 /mnt). See issue #238. |
| 4337 | const journalMode = cg.getJournalMode(); |
| 4338 | if (journalMode === 'wal') { |
| 4339 | lines.push(`**Journal mode:** wal (concurrent reads safe)`); |
| 4340 | } else { |
| 4341 | lines.push( |
| 4342 | `**Journal mode:** ⚠ ${journalMode || 'unknown'} — WAL not active, so reads ` + |
| 4343 | `can block on a concurrent write (WAL appears unsupported on this filesystem)` |
| 4344 | ); |
| 4345 | } |
| 4346 | |
| 4347 | // A newer release exists (#1243) — status is where users and agents look |
| 4348 | // when something seems off, so surface the drift here too. Cheap memoized |
| 4349 | // cache read; absent entirely when up to date or opted out. |
| 4350 | const updateNotice = getUpdateNotice(); |
no test coverage detected