From aad44413bd0abb58c870ee5e8bc2885c577ef78b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 20:05:47 +0000 Subject: [PATCH] fix: attach git notes to base_commit and add PATH to hook scripts Two fixes: - checkpoint now reads base_commit from session frontmatter so notes attach to the commit where the session started, not whatever HEAD happens to be when the hook runs. Fixes notes being overwritten when multiple sessions exist across branches. - post-commit hook now exports $HOME/.bun/bin in PATH so ghost is findable in non-interactive shell contexts. https://claude.ai/code/session_01HicrquPN9RkwKVT5LDaaSe --- src/session.ts | 19 ++++++++++++++----- src/setup.ts | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/session.ts b/src/session.ts index a8beeb7..45f8aa9 100644 --- a/src/session.ts +++ b/src/session.ts @@ -314,7 +314,7 @@ export function finalizeSession(repoRoot: string, claudeSessionId?: string): { p // Git Notes Checkpoint // ============================================================================= -/** Attach the most recent completed session as a git note to HEAD */ +/** Attach the most recent completed session as a git note to the relevant commit */ export async function checkpoint(repoRoot: string): Promise { const idPath = currentIdPath(repoRoot); let sessionId: string | null = null; @@ -332,12 +332,21 @@ export async function checkpoint(repoRoot: string): Promise { if (!sessionId) return; - const compPath = completedSessionPath(repoRoot, sessionId); - if (!existsSync(compPath)) return; + // Prefer completed session, fall back to active + let sessionPath = completedSessionPath(repoRoot, sessionId); + if (!existsSync(sessionPath)) { + sessionPath = sessionFilePath(repoRoot, sessionId); + } + if (!existsSync(sessionPath)) return; try { - const sha = await headSha(); - await addNoteFromFile(compPath, sha); + // Use base_commit from frontmatter so the note lands on the commit + // where the session started, not just whatever HEAD is now + const content = readFileSync(sessionPath, "utf8"); + const { frontmatter } = parseFrontmatter(content); + const baseCommit = frontmatter.base_commit as string | undefined; + const sha = baseCommit && baseCommit !== "none" ? baseCommit : await headSha(); + await addNoteFromFile(sessionPath, sha); } catch { // Silently fail — non-blocking } diff --git a/src/setup.ts b/src/setup.ts index 2c88706..e6a8648 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -156,11 +156,11 @@ export async function enable(root: string, opts?: { install?: boolean; genesis?: const hooksDir = join(root, ".git", "hooks"); mkdirSync(hooksDir, { recursive: true }); const postCommitPath = join(hooksDir, "post-commit"); - const hookContent = "#!/bin/sh\nghost checkpoint &\n"; + const hookContent = '#!/bin/sh\nexport PATH="$HOME/.bun/bin:$PATH"\nghost checkpoint &\n'; if (existsSync(postCommitPath)) { const existing = readFileSync(postCommitPath, "utf8"); if (!existing.includes("ghost checkpoint")) { - writeFileSync(postCommitPath, `${existing.trimEnd()}\nghost checkpoint &\n`); + writeFileSync(postCommitPath, `${existing.trimEnd()}\nexport PATH="$HOME/.bun/bin:$PATH"\nghost checkpoint &\n`); } } else { writeFileSync(postCommitPath, hookContent);