Fix gt crew at to detect existing Claude sessions (gt-l90dq)

Before: gt crew at only looked for tmux sessions with the specific naming
convention gt-<rig>-crew-<name>. If the user started Claude manually or
via a different mechanism, it would create a duplicate session.

After: Before creating a new session, check if any existing tmux session
has Claude running in the crews directory. If found, attach to that
session instead of creating a new one.

Changes:
- Add FindSessionByWorkDir() to internal/tmux/tmux.go to search sessions
  by working directory, optionally filtering for Claude (node) running
- Update runCrewAt() to check for existing sessions before creating new

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-29 21:57:15 -08:00
parent 81b250ee32
commit 6da21f6a3e
2 changed files with 67 additions and 0 deletions

View File

@@ -83,6 +83,37 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
return fmt.Errorf("checking session: %w", err) return fmt.Errorf("checking session: %w", err)
} }
// Before creating a new session, check if there's already a Claude session
// running in this crew's directory (might have been started manually or via
// a different mechanism)
if !hasSession {
existingSessions, err := t.FindSessionByWorkDir(worker.ClonePath, true)
if err == nil && len(existingSessions) > 0 {
// Found an existing session with Claude running in this directory
existingSession := existingSessions[0]
fmt.Printf("%s Found existing Claude session '%s' in crew directory\n",
style.Warning.Render("⚠"),
existingSession)
fmt.Printf(" Attaching to existing session instead of creating a new one\n")
// If inside tmux (but different session), inform user
if tmux.IsInsideTmux() {
fmt.Printf("Use C-b s to switch to '%s'\n", existingSession)
return nil
}
// Outside tmux: attach unless --detached flag is set
if crewDetached {
fmt.Printf("Existing session: '%s'. Run 'tmux attach -t %s' to attach.\n",
existingSession, existingSession)
return nil
}
// Attach to existing session
return attachToTmuxSession(existingSession)
}
}
if !hasSession { if !hasSession {
// Create new session // Create new session
if err := t.NewSession(sessionID, worker.ClonePath); err != nil { if err := t.NewSession(sessionID, worker.ClonePath); err != nil {

View File

@@ -292,6 +292,42 @@ func (t *Tmux) GetPaneWorkDir(session string) (string, error) {
return strings.TrimSpace(out), nil return strings.TrimSpace(out), nil
} }
// FindSessionByWorkDir finds tmux sessions where the pane's current working directory
// matches or is under the target directory. Returns session names that match.
// If checkClaude is true, only returns sessions that have Claude (node) running.
func (t *Tmux) FindSessionByWorkDir(targetDir string, checkClaude bool) ([]string, error) {
sessions, err := t.ListSessions()
if err != nil {
return nil, err
}
var matches []string
for _, session := range sessions {
if session == "" {
continue
}
workDir, err := t.GetPaneWorkDir(session)
if err != nil {
continue // Skip sessions we can't query
}
// Check if workdir matches target (exact match or subdir)
if workDir == targetDir || strings.HasPrefix(workDir, targetDir+"/") {
if checkClaude {
// Only include if Claude is running
if t.IsClaudeRunning(session) {
matches = append(matches, session)
}
} else {
matches = append(matches, session)
}
}
}
return matches, nil
}
// CapturePane captures the visible content of a pane. // CapturePane captures the visible content of a pane.
func (t *Tmux) CapturePane(session string, lines int) (string, error) { func (t *Tmux) CapturePane(session string, lines int) (string, error) {
return t.run("capture-pane", "-p", "-t", session, "-S", fmt.Sprintf("-%d", lines)) return t.run("capture-pane", "-p", "-t", session, "-S", fmt.Sprintf("-%d", lines))