feat(mcp): detect borrowed git worktree index and surface on read tools (#312)
When a worktree is nested inside the main checkout (e.g. agent tools that place worktrees under .claude/worktrees/<name>/), the nearest-.codegraph walk resolves UP to the main checkout's index and queries silently return that tree's code — usually a different branch. Symbols changed only in the worktree are invisible, and nothing tells the user (#155). Two layers: - **Detection** (src/sync/worktree.ts): detectWorktreeIndexMismatch() compares the caller's git working-tree root vs the resolved index root via 'git rev-parse --show-toplevel'. Best-effort; no git / not a repo / monorepo subdir / plain-ancestor index → no warning. - **Surface**: codegraph status (CLI + MCP) embeds a verbose multi-line warning; every MCP read tool (search/context/trace/callers/callees/impact/explore/node/ files) prefixes a compact one-line notice naming the borrowed index and the fix (codegraph init -i in the worktree). Detection is cached per session per start path, so it costs at most a single pair of 'git rev-parse' spawns per project no matter how many tool calls — respects the wall-clock-latency invariant. Real-git tests (no mocking) cover both layers. Validated on macOS / Linux (Docker) / Windows (Parallels VM); 11/11 worktree tests green on all three. Closes #155 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import { Command } from 'commander';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { getCodeGraphDir, isInitialized } from '../directory';
|
||||
import { detectWorktreeIndexMismatch, worktreeMismatchWarning } from '../sync/worktree';
|
||||
import { createShimmerProgress } from '../ui/shimmer-progress';
|
||||
import { getGlyphs } from '../ui/glyphs';
|
||||
|
||||
@@ -692,6 +693,11 @@ program
|
||||
.option('-j, --json', 'Output as JSON')
|
||||
.action(async (pathArg: string | undefined, options: { json?: boolean }) => {
|
||||
const projectPath = resolveProjectPath(pathArg);
|
||||
// The directory the user actually ran from, before walking up to the index
|
||||
// root. Used to detect when the resolved index lives in a different git
|
||||
// working tree (e.g. a nested worktree borrowing the main checkout's index).
|
||||
const startPath = path.resolve(pathArg || process.cwd());
|
||||
const worktreeMismatch = detectWorktreeIndexMismatch(startPath, projectPath);
|
||||
|
||||
try {
|
||||
if (!isInitialized(projectPath)) {
|
||||
@@ -731,6 +737,9 @@ program
|
||||
modified: changes.modified.length,
|
||||
removed: changes.removed.length,
|
||||
},
|
||||
worktreeMismatch: worktreeMismatch
|
||||
? { worktreeRoot: worktreeMismatch.worktreeRoot, indexRoot: worktreeMismatch.indexRoot }
|
||||
: null,
|
||||
}));
|
||||
cg.destroy();
|
||||
return;
|
||||
@@ -740,6 +749,9 @@ program
|
||||
|
||||
// Project info
|
||||
console.log(chalk.cyan('Project:'), projectPath);
|
||||
if (worktreeMismatch) {
|
||||
warn(worktreeMismatchWarning(worktreeMismatch));
|
||||
}
|
||||
console.log();
|
||||
|
||||
// Index stats
|
||||
|
||||
Reference in New Issue
Block a user