* Handle codegraph_status
(args: Record<string, unknown>)
| 3946 | * Handle codegraph_status |
| 3947 | */ |
| 3948 | private async handleStatus(args: Record<string, unknown>): Promise<ToolResult> { |
| 3949 | let cg = this.getCodeGraph(args.projectPath as string | undefined); |
| 3950 | // Same trick as withStalenessNotice — when an explicit projectPath |
| 3951 | // resolves to the same project as the default session cg, prefer the |
| 3952 | // default so getPendingFiles() (only populated by the default's watcher) |
| 3953 | // is non-empty when there are pending edits. |
| 3954 | if (this.cg && cg !== this.cg) { |
| 3955 | try { |
| 3956 | if (resolvePath(this.cg.getProjectRoot()) === resolvePath(cg.getProjectRoot())) { |
| 3957 | cg = this.cg; |
| 3958 | } |
| 3959 | } catch { /* closed instance — leave as is */ } |
| 3960 | } |
| 3961 | const stats = cg.getStats(); |
| 3962 | |
| 3963 | // Warn when this index actually belongs to a different git working tree |
| 3964 | // (e.g. the server resolved up from a nested worktree to the main checkout). |
| 3965 | // Queries then reflect that tree's branch, not the worktree being edited. |
| 3966 | // status shows the verbose, multi-line form; the read tools get the compact |
| 3967 | // one-liner via withWorktreeNotice. Both share the cached detection. |
| 3968 | const mismatch = this.worktreeMismatchFor(args.projectPath as string | undefined); |
| 3969 | |
| 3970 | const lines: string[] = [ |
| 3971 | '**CodeGraph Status**', |
| 3972 | '', |
| 3973 | ]; |
| 3974 | if (mismatch) { |
| 3975 | lines.push(`> ⚠ ${worktreeMismatchWarning(mismatch).replace(/\n/g, '\n> ')}`, ''); |
| 3976 | } |
| 3977 | lines.push( |
| 3978 | `**Files indexed:** ${stats.fileCount}`, |
| 3979 | `**Total nodes:** ${stats.nodeCount}`, |
| 3980 | `**Total edges:** ${stats.edgeCount}`, |
| 3981 | `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`, |
| 3982 | ); |
| 3983 | |
| 3984 | // Surface the active SQLite backend (node:sqlite, Node's built-in real |
| 3985 | // SQLite — full WAL + FTS5, no native build). |
| 3986 | lines.push(`**Backend:** node:sqlite (Node built-in) — full WAL + FTS5`); |
| 3987 | |
| 3988 | // Effective journal mode. 'wal' ⇒ concurrent reads never block on a writer; |
| 3989 | // anything else ⇒ they can ("database is locked"). node:sqlite supports WAL |
| 3990 | // everywhere, so a non-wal mode means the filesystem can't (network/ |
| 3991 | // virtualized mounts, WSL2 /mnt). See issue #238. |
| 3992 | const journalMode = cg.getJournalMode(); |
| 3993 | if (journalMode === 'wal') { |
| 3994 | lines.push(`**Journal mode:** wal (concurrent reads safe)`); |
| 3995 | } else { |
| 3996 | lines.push( |
| 3997 | `**Journal mode:** ⚠ ${journalMode || 'unknown'} — WAL not active, so reads ` + |
| 3998 | `can block on a concurrent write (WAL appears unsupported on this filesystem)` |
| 3999 | ); |
| 4000 | } |
| 4001 | |
| 4002 | lines.push('', '**Nodes by Kind:**'); |
| 4003 | |
| 4004 | for (const [kind, count] of Object.entries(stats.nodesByKind)) { |
| 4005 | if ((count as number) > 0) { |
no test coverage detected