* Handle codegraph_status
(args: Record<string, unknown>)
| 4054 | * Handle codegraph_status |
| 4055 | */ |
| 4056 | private async handleStatus(args: Record<string, unknown>): Promise<ToolResult> { |
| 4057 | let cg = this.getCodeGraph(args.projectPath as string | undefined); |
| 4058 | // Same trick as withStalenessNotice — when an explicit projectPath |
| 4059 | // resolves to the same project as the default session cg, prefer the |
| 4060 | // default so getPendingFiles() (only populated by the default's watcher) |
| 4061 | // is non-empty when there are pending edits. |
| 4062 | if (this.cg && cg !== this.cg) { |
| 4063 | try { |
| 4064 | if (resolvePath(this.cg.getProjectRoot()) === resolvePath(cg.getProjectRoot())) { |
| 4065 | cg = this.cg; |
| 4066 | } |
| 4067 | } catch { /* closed instance — leave as is */ } |
| 4068 | } |
| 4069 | const stats = cg.getStats(); |
| 4070 | |
| 4071 | // Warn when this index actually belongs to a different git working tree |
| 4072 | // (e.g. the server resolved up from a nested worktree to the main checkout). |
| 4073 | // Queries then reflect that tree's branch, not the worktree being edited. |
| 4074 | // status shows the verbose, multi-line form; the read tools get the compact |
| 4075 | // one-liner via withWorktreeNotice. Both share the cached detection. |
| 4076 | const mismatch = this.worktreeMismatchFor(args.projectPath as string | undefined); |
| 4077 | |
| 4078 | const lines: string[] = [ |
| 4079 | '**CodeGraph Status**', |
| 4080 | '', |
| 4081 | ]; |
| 4082 | if (mismatch) { |
| 4083 | lines.push(`> ⚠ ${worktreeMismatchWarning(mismatch).replace(/\n/g, '\n> ')}`, ''); |
| 4084 | } |
| 4085 | lines.push( |
| 4086 | `**Files indexed:** ${stats.fileCount}`, |
| 4087 | `**Total nodes:** ${stats.nodeCount}`, |
| 4088 | `**Total edges:** ${stats.edgeCount}`, |
| 4089 | `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`, |
| 4090 | ); |
| 4091 | |
| 4092 | // Surface the active SQLite backend (node:sqlite, Node's built-in real |
| 4093 | // SQLite — full WAL + FTS5, no native build). |
| 4094 | lines.push(`**Backend:** node:sqlite (Node built-in) — full WAL + FTS5`); |
| 4095 | |
| 4096 | // Effective journal mode. 'wal' ⇒ concurrent reads never block on a writer; |
| 4097 | // anything else ⇒ they can ("database is locked"). node:sqlite supports WAL |
| 4098 | // everywhere, so a non-wal mode means the filesystem can't (network/ |
| 4099 | // virtualized mounts, WSL2 /mnt). See issue #238. |
| 4100 | const journalMode = cg.getJournalMode(); |
| 4101 | if (journalMode === 'wal') { |
| 4102 | lines.push(`**Journal mode:** wal (concurrent reads safe)`); |
| 4103 | } else { |
| 4104 | lines.push( |
| 4105 | `**Journal mode:** ⚠ ${journalMode || 'unknown'} — WAL not active, so reads ` + |
| 4106 | `can block on a concurrent write (WAL appears unsupported on this filesystem)` |
| 4107 | ); |
| 4108 | } |
| 4109 | |
| 4110 | // A newer release exists (#1243) — status is where users and agents look |
| 4111 | // when something seems off, so surface the drift here too. Cheap memoized |
| 4112 | // cache read; absent entirely when up to date or opted out. |
| 4113 | const updateNotice = getUpdateNotice(); |
no test coverage detected