Add sparse checkout to exclude source repo .claude/ directories
When cloning or creating worktrees from repos that have their own .claude/ directory, those settings would override Gas Town's agent settings. This adds sparse checkout configuration to automatically exclude .claude/ from all clones and worktrees. Changes: - Add ConfigureSparseCheckout() to git.go, called from all Clone/WorktreeAdd methods - Add IsSparseCheckoutConfigured() to detect if sparse checkout is properly set up - Add doctor check to verify sparse checkout config (checks config, not symptoms) - Doctor --fix will configure sparse checkout for repos missing it 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
119
internal/doctor/sparse_checkout_check.go
Normal file
119
internal/doctor/sparse_checkout_check.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/git"
|
||||
)
|
||||
|
||||
// SparseCheckoutCheck verifies that git clones/worktrees have sparse checkout configured
|
||||
// to exclude .claude/ from source repos. This ensures source repo settings don't override
|
||||
// Gas Town agent settings.
|
||||
type SparseCheckoutCheck struct {
|
||||
FixableCheck
|
||||
rigPath string
|
||||
affectedRepos []string // repos missing sparse checkout configuration
|
||||
}
|
||||
|
||||
// NewSparseCheckoutCheck creates a new sparse checkout check.
|
||||
func NewSparseCheckoutCheck() *SparseCheckoutCheck {
|
||||
return &SparseCheckoutCheck{
|
||||
FixableCheck: FixableCheck{
|
||||
BaseCheck: BaseCheck{
|
||||
CheckName: "sparse-checkout",
|
||||
CheckDescription: "Verify sparse checkout is configured to exclude .claude/",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run checks if sparse checkout is configured for all git repos in the rig.
|
||||
func (c *SparseCheckoutCheck) Run(ctx *CheckContext) *CheckResult {
|
||||
c.rigPath = ctx.RigPath()
|
||||
if c.rigPath == "" {
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusError,
|
||||
Message: "No rig specified",
|
||||
}
|
||||
}
|
||||
|
||||
c.affectedRepos = nil
|
||||
|
||||
// Check all git repo locations
|
||||
repoPaths := []string{
|
||||
filepath.Join(c.rigPath, "mayor", "rig"),
|
||||
filepath.Join(c.rigPath, "refinery", "rig"),
|
||||
}
|
||||
|
||||
// Add crew clones
|
||||
crewDir := filepath.Join(c.rigPath, "crew")
|
||||
if entries, err := os.ReadDir(crewDir); err == nil {
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && entry.Name() != "README.md" {
|
||||
repoPaths = append(repoPaths, filepath.Join(crewDir, entry.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add polecat worktrees
|
||||
polecatDir := filepath.Join(c.rigPath, "polecats")
|
||||
if entries, err := os.ReadDir(polecatDir); err == nil {
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
repoPaths = append(repoPaths, filepath.Join(polecatDir, entry.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, repoPath := range repoPaths {
|
||||
// Skip if not a git repo
|
||||
if _, err := os.Stat(filepath.Join(repoPath, ".git")); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if sparse checkout is configured (not just if .claude/ exists)
|
||||
if !git.IsSparseCheckoutConfigured(repoPath) {
|
||||
c.affectedRepos = append(c.affectedRepos, repoPath)
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.affectedRepos) == 0 {
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusOK,
|
||||
Message: "All repos have sparse checkout configured to exclude .claude/",
|
||||
}
|
||||
}
|
||||
|
||||
// Build details with relative paths
|
||||
var details []string
|
||||
for _, repoPath := range c.affectedRepos {
|
||||
relPath, _ := filepath.Rel(c.rigPath, repoPath)
|
||||
if relPath == "" {
|
||||
relPath = repoPath
|
||||
}
|
||||
details = append(details, relPath)
|
||||
}
|
||||
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusError,
|
||||
Message: fmt.Sprintf("%d repo(s) missing sparse checkout configuration", len(c.affectedRepos)),
|
||||
Details: details,
|
||||
FixHint: "Run 'gt doctor --fix' to configure sparse checkout",
|
||||
}
|
||||
}
|
||||
|
||||
// Fix configures sparse checkout for affected repos to exclude .claude/.
|
||||
func (c *SparseCheckoutCheck) Fix(ctx *CheckContext) error {
|
||||
for _, repoPath := range c.affectedRepos {
|
||||
if err := git.ConfigureSparseCheckout(repoPath); err != nil {
|
||||
relPath, _ := filepath.Rel(c.rigPath, repoPath)
|
||||
return fmt.Errorf("failed to configure sparse checkout for %s: %w", relPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user