feat(polecat): add repo path to worktrees for LLM ergonomics (GH#283)

Changes polecat worktree structure from:
  polecats/<name>/
to:
  polecats/<name>/<rigname>/

This gives Claude Code agents a recognizable directory name (e.g., tidepool/)
in their cwd instead of just the polecat name, preventing confusion about
which repo they are working in.

Key changes:
- Add clonePath() method to manager.go and session_manager.go for the actual
  git worktree path, keeping polecatDir() for existence checks
- Update Add(), RepairWorktree(), Remove() to use new structure
- Update daemon lifecycle and restart code for new paths
- Update witness handlers to detect both structures
- Update doctor checks (rig_check, branch_check, config_check,
  claude_settings_check) for backward compatibility
- All code includes fallback to old structure for existing polecats

Fixes #283

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gastown/crew/max
2026-01-08 23:15:51 -08:00
committed by Steve Yegge
parent c8c97fdf64
commit 9b2f4a7652
10 changed files with 189 additions and 52 deletions

View File

@@ -752,8 +752,14 @@ func (d *Daemon) restartPolecatSession(rigName, polecatName, sessionName string)
return fmt.Errorf("cannot restart polecat: %s", reason)
}
// Determine working directory
workDir := filepath.Join(d.config.TownRoot, rigName, "polecats", polecatName)
// Determine working directory (handle both new and old structures)
// New structure: polecats/<name>/<rigname>/
// Old structure: polecats/<name>/
workDir := filepath.Join(d.config.TownRoot, rigName, "polecats", polecatName, rigName)
if _, err := os.Stat(workDir); os.IsNotExist(err) {
// Fall back to old structure
workDir = filepath.Join(d.config.TownRoot, rigName, "polecats", polecatName)
}
// Verify the worktree exists
if _, err := os.Stat(workDir); os.IsNotExist(err) {

View File

@@ -3,6 +3,7 @@ package daemon
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
@@ -427,6 +428,12 @@ func (d *Daemon) getWorkDir(config *beads.RoleConfig, parsed *ParsedIdentity) st
case "crew":
return filepath.Join(d.config.TownRoot, parsed.RigName, "crew", parsed.AgentName)
case "polecat":
// New structure: polecats/<name>/<rigname>/ (for LLM ergonomics)
// Old structure: polecats/<name>/ (for backward compat)
newPath := filepath.Join(d.config.TownRoot, parsed.RigName, "polecats", parsed.AgentName, parsed.RigName)
if _, err := os.Stat(newPath); err == nil {
return newPath
}
return filepath.Join(d.config.TownRoot, parsed.RigName, "polecats", parsed.AgentName)
default:
return ""