Excludes all Claude Code context files to prevent source repo instructions from interfering with Gas Town agent configuration: - .claude/ : settings, rules, agents, commands - CLAUDE.md : primary context file - CLAUDE.local.md: personal context file - .mcp.json : MCP server configuration Legacy configurations (only excluding .claude/) are detected and upgraded by gt doctor --fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
131 lines
3.9 KiB
Go
131 lines
3.9 KiB
Go
package doctor
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/steveyegge/gastown/internal/git"
|
|
)
|
|
|
|
// SparseCheckoutCheck verifies that git clones/worktrees have sparse checkout configured
|
|
// to exclude Claude Code context files from source repos. This ensures source repo settings
|
|
// and instructions don't override Gas Town agent configuration.
|
|
// Excluded files: .claude/, CLAUDE.md, CLAUDE.local.md, .mcp.json
|
|
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 excludes Claude context files (.claude/, CLAUDE.md, etc.)",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// 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 context files",
|
|
}
|
|
}
|
|
|
|
// 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 context files.
|
|
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)
|
|
}
|
|
|
|
// Check if any excluded files remain (untracked or modified files won't be removed by git read-tree)
|
|
if remaining := git.CheckExcludedFilesExist(repoPath); len(remaining) > 0 {
|
|
relPath, _ := filepath.Rel(c.rigPath, repoPath)
|
|
return fmt.Errorf("sparse checkout configured for %s but these files still exist: %s\n"+
|
|
"These files are untracked or modified and were not removed by git.\n"+
|
|
"Please manually remove or revert these files in %s",
|
|
relPath, strings.Join(remaining, ", "), repoPath)
|
|
}
|
|
}
|
|
return nil
|
|
}
|