11package worktree
22
33import (
4- "crypto/sha256 "
4+ "hash/maphash "
55 "strings"
66 "sync"
77 "time"
@@ -11,15 +11,15 @@ import (
1111type ViewMode int
1212
1313const (
14- ViewModeList ViewMode = iota // List view (default)
15- ViewModeKanban // Kanban board view
16- ViewModeCreate // New worktree modal
17- ViewModeTaskLink // Task link modal (for existing worktrees)
18- ViewModeMerge // Merge workflow modal
19- ViewModeAgentChoice // Agent action choice modal (attach/restart)
20- ViewModeConfirmDelete // Delete confirmation modal
21- ViewModeCommitForMerge // Commit modal before merge workflow
22- ViewModePromptPicker // Prompt template picker modal
14+ ViewModeList ViewMode = iota // List view (default)
15+ ViewModeKanban // Kanban board view
16+ ViewModeCreate // New worktree modal
17+ ViewModeTaskLink // Task link modal (for existing worktrees)
18+ ViewModeMerge // Merge workflow modal
19+ ViewModeAgentChoice // Agent action choice modal (attach/restart)
20+ ViewModeConfirmDelete // Delete confirmation modal
21+ ViewModeCommitForMerge // Commit modal before merge workflow
22+ ViewModePromptPicker // Prompt template picker modal
2323)
2424
2525// FocusPane represents which pane is active in the split view.
@@ -179,10 +179,10 @@ type Worktree struct {
179179
180180// Agent represents an AI coding agent process.
181181type Agent struct {
182- Type AgentType // claude, codex, aider, gemini
183- TmuxSession string // tmux session name
184- TmuxPane string // Pane identifier
185- PID int // Process ID (if available)
182+ Type AgentType // claude, codex, aider, gemini
183+ TmuxSession string // tmux session name
184+ TmuxPane string // Pane identifier
185+ PID int // Process ID (if available)
186186 StartedAt time.Time
187187 LastOutput time.Time // Last time output was detected
188188 OutputBuf * OutputBuffer // Last N lines of output
@@ -224,14 +224,17 @@ type OutputBuffer struct {
224224 mu sync.Mutex
225225 lines []string
226226 cap int
227- lastHash [32 ]byte // SHA256 of last content for change detection
227+ lastHash uint64 // Hash of last content for change detection
228+ lastLen int // Length of last content (collision guard)
229+ hashSeed maphash.Seed // Seed for stable hashing
228230}
229231
230232// NewOutputBuffer creates a new output buffer with the given capacity.
231233func NewOutputBuffer (capacity int ) * OutputBuffer {
232234 return & OutputBuffer {
233- lines : make ([]string , 0 , capacity ),
234- cap : capacity ,
235+ lines : make ([]string , 0 , capacity ),
236+ cap : capacity ,
237+ hashSeed : maphash .MakeSeed (),
235238 }
236239}
237240
@@ -242,13 +245,14 @@ func (b *OutputBuffer) Update(content string) bool {
242245 defer b .mu .Unlock ()
243246
244247 // Compute hash of new content
245- hash := sha256 . Sum256 ([] byte ( content ) )
246- if hash == b .lastHash {
248+ hash := maphash . String ( b . hashSeed , content )
249+ if hash == b .lastHash && len ( content ) == b . lastLen {
247250 return false // Content unchanged
248251 }
249252
250253 // Content changed - update hash and replace lines
251254 b .lastHash = hash
255+ b .lastLen = len (content )
252256 b .lines = strings .Split (content , "\n " )
253257
254258 // Trim to capacity (keep most recent lines)
@@ -319,7 +323,8 @@ func (b *OutputBuffer) Clear() {
319323 b .mu .Lock ()
320324 defer b .mu .Unlock ()
321325 b .lines = b .lines [:0 ]
322- b .lastHash = [32 ]byte {} // Reset hash
326+ b .lastHash = 0
327+ b .lastLen = 0
323328}
324329
325330// Len returns the number of lines in the buffer.
0 commit comments