fix: capture_diff uses base commit to handle agent self-commits
Claude in agentic mode (interactive, no -p flag) commits its own changes, advancing HEAD. This made `git diff --cached HEAD` return empty, triggering false EMPTY_DIFF errors every time. Now capture_diff diffs against the base commit SHA recorded at worktree creation, so changes are captured regardless of whether the agent committed them. Also adds UX_IMPROVEMENT_PLAN.md for guided message improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,18 +37,31 @@ def make_worktree_dir(base_cwd: Path, branch_name: str) -> Path:
|
||||
)
|
||||
|
||||
|
||||
def create_worktree(base_cwd: Path, work_dir: Path, branch_name: str) -> Path:
|
||||
def create_worktree(base_cwd: Path, work_dir: Path, branch_name: str) -> tuple[Path, str]:
|
||||
"""Create a git worktree on a new branch from HEAD.
|
||||
|
||||
1. Create branch from HEAD
|
||||
2. Create worktree checked out to that branch
|
||||
|
||||
The branch lives in the original repo, so it survives worktree removal.
|
||||
Returns (worktree_path, base_commit_sha).
|
||||
"""
|
||||
work_dir = work_dir.resolve()
|
||||
if work_dir.exists():
|
||||
shutil.rmtree(work_dir)
|
||||
|
||||
# Record the base commit SHA before creating the branch.
|
||||
# This is the anchor for all diffs — even if the agent makes its own commits,
|
||||
# we always diff against this base to capture the full set of changes.
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "HEAD"],
|
||||
cwd=base_cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
base_commit = result.stdout.strip()
|
||||
|
||||
# Create the branch at HEAD
|
||||
try:
|
||||
subprocess.run(
|
||||
@@ -83,15 +96,24 @@ def create_worktree(base_cwd: Path, work_dir: Path, branch_name: str) -> Path:
|
||||
f"Failed to create worktree at {work_dir}: {e.stderr.strip()}"
|
||||
) from e
|
||||
|
||||
logger.debug("Created worktree on branch '%s': %s", branch_name, work_dir)
|
||||
return work_dir
|
||||
logger.debug("Created worktree on branch '%s': %s (base: %s)", branch_name, work_dir, base_commit[:8])
|
||||
return work_dir, base_commit
|
||||
|
||||
|
||||
def capture_diff(worktree_path: Path) -> str:
|
||||
def capture_diff(worktree_path: Path, base_commit: str | None = None) -> str:
|
||||
"""Capture all changes made in the worktree as a unified diff.
|
||||
|
||||
Includes both tracked modifications and new untracked files.
|
||||
Includes both tracked modifications, new untracked files, and changes
|
||||
that the agent may have committed on its own.
|
||||
|
||||
Args:
|
||||
base_commit: The commit SHA from when the worktree was created.
|
||||
If provided, diffs against this fixed base instead of HEAD.
|
||||
This is critical because agents (e.g. Claude in interactive
|
||||
mode) may create their own commits, advancing HEAD and
|
||||
making ``git diff --cached HEAD`` return empty.
|
||||
"""
|
||||
# Stage any uncommitted changes so they're included in the diff
|
||||
subprocess.run(
|
||||
["git", "add", "-A"],
|
||||
cwd=worktree_path,
|
||||
@@ -99,6 +121,30 @@ def capture_diff(worktree_path: Path) -> str:
|
||||
check=True,
|
||||
)
|
||||
|
||||
if base_commit:
|
||||
# Diff everything (committed + staged) against the original base.
|
||||
# This captures changes regardless of whether the agent committed them.
|
||||
result = subprocess.run(
|
||||
["git", "diff", base_commit, "--cached"],
|
||||
cwd=worktree_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
diff = result.stdout.strip()
|
||||
if diff:
|
||||
return diff
|
||||
|
||||
# Also check committed changes (agent may have committed and left
|
||||
# nothing staged)
|
||||
result = subprocess.run(
|
||||
["git", "diff", base_commit, "HEAD"],
|
||||
cwd=worktree_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
# Fallback: no base_commit, use original behavior
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--cached", "HEAD"],
|
||||
cwd=worktree_path,
|
||||
|
||||
Reference in New Issue
Block a user