fix: prefer repo-relative hook file paths

This commit is contained in:
Affaan Mustafa
2026-04-12 22:13:16 -07:00
parent f1249d3915
commit a36d85e807
2 changed files with 73 additions and 14 deletions

View File

@@ -359,17 +359,10 @@ function gitRepoRoot(cwd) {
return runGit(['rev-parse', '--show-toplevel'], cwd); return runGit(['rev-parse', '--show-toplevel'], cwd);
} }
const MAX_RELEVANT_PATCH_LINES = 6;
function candidateGitPaths(repoRoot, filePath) { function candidateGitPaths(repoRoot, filePath) {
const resolvedRepoRoot = path.resolve(repoRoot); const resolvedRepoRoot = path.resolve(repoRoot);
const absolute = path.isAbsolute(filePath)
? path.resolve(filePath)
: path.resolve(process.cwd(), filePath);
const relative = path.relative(resolvedRepoRoot, absolute);
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
return [];
}
const candidates = []; const candidates = [];
const pushCandidate = value => { const pushCandidate = value => {
const candidate = String(value || '').trim(); const candidate = String(value || '').trim();
@@ -379,10 +372,24 @@ function candidateGitPaths(repoRoot, filePath) {
candidates.push(candidate); candidates.push(candidate);
}; };
pushCandidate(relative); const absoluteCandidates = path.isAbsolute(filePath)
pushCandidate(relative.split(path.sep).join('/')); ? [path.resolve(filePath)]
pushCandidate(absolute); : [
pushCandidate(absolute.split(path.sep).join('/')); path.resolve(resolvedRepoRoot, filePath),
path.resolve(process.cwd(), filePath),
];
for (const absolute of absoluteCandidates) {
const relative = path.relative(resolvedRepoRoot, absolute);
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) {
continue;
}
pushCandidate(relative);
pushCandidate(relative.split(path.sep).join('/'));
pushCandidate(absolute);
pushCandidate(absolute.split(path.sep).join('/'));
}
return candidates; return candidates;
} }
@@ -404,7 +411,7 @@ function patchPreviewFromGitDiff(repoRoot, pathCandidates) {
|| (line.startsWith('+') && !line.startsWith('+++')) || (line.startsWith('+') && !line.startsWith('+++'))
|| (line.startsWith('-') && !line.startsWith('---')) || (line.startsWith('-') && !line.startsWith('---'))
) )
.slice(0, 6); .slice(0, MAX_RELEVANT_PATCH_LINES);
if (relevant.length > 0) { if (relevant.length > 0) {
return relevant.join('\n'); return relevant.join('\n');

View File

@@ -309,6 +309,58 @@ function runTests() {
fs.rmSync(repoDir, { recursive: true, force: true }); fs.rmSync(repoDir, { recursive: true, force: true });
}) ? passed++ : failed++); }) ? passed++ : failed++);
(test('resolves repo-relative paths even when the hook runs from a nested cwd', () => {
const tmpHome = makeTempDir();
const repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'session-activity-tracker-nested-repo-'));
spawnSync('git', ['init'], { cwd: repoDir, encoding: 'utf8' });
spawnSync('git', ['config', 'user.email', 'ecc@example.com'], { cwd: repoDir, encoding: 'utf8' });
spawnSync('git', ['config', 'user.name', 'ECC Tests'], { cwd: repoDir, encoding: 'utf8' });
const srcDir = path.join(repoDir, 'src');
const nestedCwd = path.join(repoDir, 'subdir');
fs.mkdirSync(srcDir, { recursive: true });
fs.mkdirSync(nestedCwd, { recursive: true });
const trackedFile = path.join(srcDir, 'app.ts');
fs.writeFileSync(trackedFile, 'const count = 1;\n', 'utf8');
spawnSync('git', ['add', 'src/app.ts'], { cwd: repoDir, encoding: 'utf8' });
spawnSync('git', ['commit', '-m', 'init'], { cwd: repoDir, encoding: 'utf8' });
fs.writeFileSync(trackedFile, 'const count = 2;\n', 'utf8');
const input = {
tool_name: 'Write',
tool_input: {
file_path: 'src/app.ts',
content: 'const count = 2;\n',
},
tool_output: { output: 'updated src/app.ts' },
};
const result = runScript(input, {
...withTempHome(tmpHome),
CLAUDE_HOOK_EVENT_NAME: 'PostToolUse',
ECC_SESSION_ID: 'ecc-session-nested-cwd',
}, {
cwd: nestedCwd,
});
assert.strictEqual(result.code, 0);
const metricsFile = path.join(tmpHome, '.claude', 'metrics', 'tool-usage.jsonl');
const row = JSON.parse(fs.readFileSync(metricsFile, 'utf8').trim());
assert.deepStrictEqual(row.file_events, [
{
path: 'src/app.ts',
action: 'modify',
diff_preview: 'const count = 1; -> const count = 2;',
patch_preview: '@@ -1 +1 @@\n-const count = 1;\n+const count = 2;',
},
]);
fs.rmSync(tmpHome, { recursive: true, force: true });
fs.rmSync(repoDir, { recursive: true, force: true });
}) ? passed++ : failed++);
(test('prefers ECC_SESSION_ID over CLAUDE_SESSION_ID and redacts bash summaries', () => { (test('prefers ECC_SESSION_ID over CLAUDE_SESSION_ID and redacts bash summaries', () => {
const tmpHome = makeTempDir(); const tmpHome = makeTempDir();
const input = { const input = {