import { execFileSync } from 'child_process';/** * Normalize a repo-relative path for git index lookups. */function normalizeRepoRelativePath(repoRelativePath: string): string { return repoRelativePath.replace(/\\/g, '/').replace(/^\.\/+/, '').replace(/\/+$/, '');}/** * True when the path is recorded in the index as a git submodule (mode 160000, gitlink). * * WHY: Review bots often anchor threads on submodule roots (e.g. `plugins/plugin-sql`). * There is no regular file text at line N; snippet reads fail and PRR used to dismiss as * generic stale / "unreadable". This check uses the index so it works even when the * submodule is not checked out in the worktree. */export function isTrackedGitSubmodulePath(workdir: string, repoRelativePath: string): boolean { const normalized = normalizeRepoRelativePath(repoRelativePath); if (!normalized) return false; try { const out = execFileSync('git', ['ls-files', '-s', '--', normalized], { cwd: workdir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 256 * 1024, }); for (const line of out.split('\n')) { const trimmed = line.trim(); if (!trimmed) continue; const m = trimmed.match(/^160000\s+\S+\s+\d\t(.+)$/); if (m) { const indexedPath = normalizeRepoRelativePath(m[1]!); if (indexedPath === normalized) return true; } } return false; } catch { return false; }}
import { execFileSync } from 'child_process';
/**
* Normalize a repo-relative path for git index lookups.
*/
function normalizeRepoRelativePath(repoRelativePath: string): string {
return repoRelativePath.replace(/\\/g, '/').replace(/^\.\/+/, '').replace(/\/+$/, '');
}
/**
* True when the path is recorded in the index as a git submodule (mode 160000, gitlink).
*
* WHY: Review bots often anchor threads on submodule roots (e.g. `plugins/plugin-sql`).
* There is no regular file text at line N; snippet reads fail and PRR used to dismiss as
* generic stale / "unreadable". This check uses the index so it works even when the
* submodule is not checked out in the worktree.
*/
export function isTrackedGitSubmodulePath(workdir: string, repoRelativePath: string): boolean {
const normalized = normalizeRepoRelativePath(repoRelativePath);
if (!normalized) return false;
try {
const out = execFileSync('git', ['ls-files', '-s', '--', normalized], {
cwd: workdir,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
maxBuffer: 256 * 1024,
});
for (const line of out.split('\n')) {
const trimmed = line.trim();
if (!trimmed) continue;
const m = trimmed.match(/^160000\s+\S+\s+\d\t(.+)$/);
if (m) {
const indexedPath = normalizeRepoRelativePath(m[1]!);
if (indexedPath === normalized) return true;
}
}
return false;
} catch {
return false;
}
}