perf: optimize stop hook performance#141
Merged
gtrrz-victor merged 15 commits intomainfrom Feb 5, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Optimizes stop-hook performance by consolidating git status queries and avoiding redundant transcript parsing when calculating Claude Code token usage.
Changes:
- Introduces
ComputeFileChangesto compute new + deleted files from a singleworktree.Status()call and updates hook/debug call sites to use it. - Deprecates
ComputeNewFiles/ComputeDeletedFilesin favor of the consolidated implementation. - Updates
CalculateTotalTokenUsageto parse the transcript once and reuse the parsed result for both token usage and spawned-agent detection.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/entire/cli/state.go | Adds ComputeFileChanges and marks older file-change helpers as deprecated |
| cmd/entire/cli/state_test.go | Adds a minimal test stub for ComputeFileChanges |
| cmd/entire/cli/hooks_geminicli_handlers.go | Switches Gemini hook to consolidated file-change computation |
| cmd/entire/cli/hooks_claudecode_handlers.go | Switches Claude Code hook to consolidated file-change computation |
| cmd/entire/cli/debug.go | Updates debug output to use consolidated file-change computation |
| cmd/entire/cli/agent/claudecode/transcript.go | Removes double transcript parsing in CalculateTotalTokenUsage |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
ff006a0 to
3070690
Compare
Combine ComputeNewFiles and ComputeDeletedFiles into single ComputeFileChanges function that calls worktree.Status() once instead of twice. This reduces stop hook latency by ~40-80ms in repos with 400+ files. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: 6beaf3327136
Refactor CalculateTotalTokenUsage to parse the transcript once and extract both token usage and spawned agent IDs from the same parsed data. Previously it called parseTranscriptFromLine twice. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: 6beaf3327136
Address PR review feedback from Copilot and Cursor Bugbot: - ComputeFileChanges now always computes deleted files regardless of preState (deleted files don't depend on pre-prompt state) - Only newFiles computation requires preState to compare against - Update test name and comments to accurately reflect behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Address PR review feedback: - debug.go: Call ComputeFileChanges(nil) even when no active session to still detect deleted files (they don't depend on preState) - state_test.go: Replace weak stub test with comprehensive tests that create isolated git repos and verify both new and deleted file detection behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: f9e69d99c484
ComputeFileChanges uses strategy.OpenRepository() which calls GetWorktreePath() (git rev-parse --show-toplevel), not paths.RepoRoot(). The cache clearing was unnecessary and the comments were misleading. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: a09495d15235
Combine ComputeNewFiles and ComputeDeletedFiles into single ComputeFileChanges function that calls worktree.Status() once instead of twice. This reduces stop hook latency by ~40-80ms in repos with 400+ files. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: 6beaf3327136
Address PR review feedback from Copilot and Cursor Bugbot: - ComputeFileChanges now always computes deleted files regardless of preState (deleted files don't depend on pre-prompt state) - Only newFiles computation requires preState to compare against - Update test name and comments to accurately reflect behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Address PR review feedback: - debug.go: Call ComputeFileChanges(nil) even when no active session to still detect deleted files (they don't depend on preState) - state_test.go: Replace weak stub test with comprehensive tests that create isolated git repos and verify both new and deleted file detection behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: f9e69d99c484
ComputeFileChanges uses strategy.OpenRepository() which calls GetWorktreePath() (git rev-parse --show-toplevel), not paths.RepoRoot(). The cache clearing was unnecessary and the comments were misleading. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: a09495d15235
…kpoint Replace collectWorkingDirectoryFiles() with collectChangedFiles() to fix slow shadow branch creation when node_modules/ or .worktrees/ exist. The old implementation walked the entire filesystem, only skipping .git/ and .entire/ directories. This was extremely slow in repos with large ignored directories like node_modules/ (50k+ files). The new implementation uses worktree.Status() which: - Respects .gitignore (excludes node_modules/, build/, etc.) - Only returns changed files (modified tracked + untracked non-ignored) - Is much faster: O(changed files) vs O(all files) The base tree from HEAD already contains all unchanged tracked files, so we only need to capture files that have changed. Added tests to verify: - Modified tracked files are captured (user's uncommitted work) - Untracked non-ignored files are captured - Gitignored files are excluded - Both user and agent changes are captured together Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: 4dee03b57bf0
Address PR review feedback: replace slices.Contains (O(n)) with map lookup (O(1)) for deduplication, removing the slices import. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: 0faddab5d923
- Clarify comments about go-git's worktree.Status() respecting repo .gitignore but not global gitignore (core.excludesfile) - Make test assertion more specific: check for ErrFileNotFound/ErrEntryNotFound instead of just non-nil error Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: 2bd1d37452a2
Switch from go-git's worktree.Status() to native git CLI commands for collecting changed files during checkpoint creation. This ensures full gitignore compatibility including global gitignore (core.excludesfile). Key changes: - Use `git status --porcelain -uall` to get all changed files - Set cmd.Dir to repo root for correct execution in test environments - Rename inner `worktree` variable to `wtStatus` to avoid shadowing This aligns with the safety approach used in PR #129 for other git operations where go-git has known issues. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: b4b052c197de
Address PR feedback: - Switch to `git status --porcelain -z` for NUL-separated output (handles quoted filenames with spaces/special chars correctly) - Pass context from caller for cancellation/timeout support - Handle renamed files (R) and copied files (C) with proper path parsing - Capture user's pre-existing file deletions (D) in first checkpoint - Handle type changes (T) and unmerged files (U) for completeness - Update stale comment that mentioned worktree.Status() Add tests for deleted files, renamed files, and filenames with spaces. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move R/C handling before infrastructure path check to ensure old name entry is always skipped, even if new name is an infrastructure path - Use git mv in rename test to ensure R status (not D+A) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Entire-Checkpoint: e26858f99834
3070690 to
941f759
Compare
gtrrz-victor
approved these changes
Feb 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ComputeFileChangesfunctionCalculateTotalTokenUsagePerformance Improvement
Measured in repos with 400+ files. The main win comes from calling
worktree.Status()once instead of twice.Test plan
🤖 Generated with Claude Code
Note
Medium Risk
Touches checkpoint tree construction and file-change detection around session start, which can affect what gets committed (especially deletions/renames/ignored files); behavior is covered by added tests but relies on invoking the
gitCLI and parsing porcelain output.Overview
Reduces stop-hook overhead by consolidating new and deleted file detection into a single
ComputeFileChangescall, updating Claude Code and Gemini hook handlers (anddebug auto-commit) to use it while deprecating the old split helpers.Optimizes temporary checkpoint creation for the first checkpoint by replacing a full working-directory walk with a
git status --porcelain -z -uallbased collector that respects repo + global gitignore, and by merging user pre-existing deletions into the checkpoint tree; adds extensive tests covering first-checkpoint capture of modified tracked files, untracked files, gitignored exclusions, deletions, renames, and filenames with spaces.Avoids double transcript parsing in Claude Code token accounting by parsing once and reusing the parsed transcript for both token usage and spawned subagent detection (including handling empty transcript paths).
Written by Cursor Bugbot for commit 941f759. This will update automatically on new commits. Configure here.