Files
gastown/internal/cmd/doctor.go
george b178d056f6 refactor: remove bd daemon code from gastown
Remove all code that calls bd daemon commands, as bd daemon functionality
has been removed from beads:

- Delete internal/beads/daemon.go (CheckBdDaemonHealth, StopAllBdProcesses, etc.)
- Delete internal/beads/daemon_test.go
- Delete internal/doctor/bd_daemon_check.go (BdDaemonCheck health check)
- Remove bd daemon health check from gt status
- Remove bd daemon stopping from gt down
- Remove bd daemon cleanup from gt install
- Remove BdDaemonCheck registration from gt doctor

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:33:02 -08:00

209 lines
7.2 KiB
Go

package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/doctor"
"github.com/steveyegge/gastown/internal/workspace"
)
var (
doctorFix bool
doctorVerbose bool
doctorRig string
doctorRestartSessions bool
)
var doctorCmd = &cobra.Command{
Use: "doctor",
GroupID: GroupDiag,
Short: "Run health checks on the workspace",
Long: `Run diagnostic checks on the Gas Town workspace.
Doctor checks for common configuration issues, missing files,
and other problems that could affect workspace operation.
Workspace checks:
- town-config-exists Check mayor/town.json exists
- town-config-valid Check mayor/town.json is valid
- rigs-registry-exists Check mayor/rigs.json exists (fixable)
- rigs-registry-valid Check registered rigs exist (fixable)
- mayor-exists Check mayor/ directory structure
Town root protection:
- town-git Verify town root is under version control
- town-root-branch Verify town root is on main branch (fixable)
- pre-checkout-hook Verify pre-checkout hook prevents branch switches (fixable)
Infrastructure checks:
- stale-binary Check if gt binary is up to date with repo
- daemon Check if daemon is running (fixable)
- repo-fingerprint Check database has valid repo fingerprint (fixable)
- boot-health Check Boot watchdog health (vet mode)
Cleanup checks (fixable):
- orphan-sessions Detect orphaned tmux sessions
- orphan-processes Detect orphaned Claude processes
- wisp-gc Detect and clean abandoned wisps (>1h)
Clone divergence checks:
- persistent-role-branches Detect crew/witness/refinery not on main
- clone-divergence Detect clones significantly behind origin/main
Crew workspace checks:
- crew-state Validate crew worker state.json files (fixable)
- crew-worktrees Detect stale cross-rig worktrees (fixable)
Rig checks (with --rig flag):
- rig-is-git-repo Verify rig is a valid git repository
- git-exclude-configured Check .git/info/exclude has Gas Town dirs (fixable)
- witness-exists Verify witness/ structure exists (fixable)
- refinery-exists Verify refinery/ structure exists (fixable)
- mayor-clone-exists Verify mayor/rig/ clone exists (fixable)
- polecat-clones-valid Verify polecat directories are valid clones
- beads-config-valid Verify beads configuration (fixable)
Routing checks (fixable):
- routes-config Check beads routing configuration
- prefix-mismatch Detect rigs.json vs routes.jsonl prefix mismatches (fixable)
Session hook checks:
- session-hooks Check settings.json use session-start.sh
- claude-settings Check Claude settings.json match templates (fixable)
Patrol checks:
- patrol-molecules-exist Verify patrol molecules exist
- patrol-hooks-wired Verify daemon triggers patrols
- patrol-not-stuck Detect stale wisps (>1h)
- patrol-plugins-accessible Verify plugin directories
- patrol-roles-have-prompts Verify role prompts exist
Use --fix to attempt automatic fixes for issues that support it.
Use --rig to check a specific rig instead of the entire workspace.`,
RunE: runDoctor,
}
func init() {
doctorCmd.Flags().BoolVar(&doctorFix, "fix", false, "Attempt to automatically fix issues")
doctorCmd.Flags().BoolVarP(&doctorVerbose, "verbose", "v", false, "Show detailed output")
doctorCmd.Flags().StringVar(&doctorRig, "rig", "", "Check specific rig only")
doctorCmd.Flags().BoolVar(&doctorRestartSessions, "restart-sessions", false, "Restart patrol sessions when fixing stale settings (use with --fix)")
rootCmd.AddCommand(doctorCmd)
}
func runDoctor(cmd *cobra.Command, args []string) error {
// Find town root
townRoot, err := workspace.FindFromCwdOrError()
if err != nil {
return fmt.Errorf("not in a Gas Town workspace: %w", err)
}
// Create check context
ctx := &doctor.CheckContext{
TownRoot: townRoot,
RigName: doctorRig,
Verbose: doctorVerbose,
RestartSessions: doctorRestartSessions,
}
// Create doctor and register checks
d := doctor.NewDoctor()
// Register workspace-level checks first (fundamental)
d.RegisterAll(doctor.WorkspaceChecks()...)
d.Register(doctor.NewGlobalStateCheck())
// Register built-in checks
d.Register(doctor.NewStaleBinaryCheck())
d.Register(doctor.NewSqlite3Check())
d.Register(doctor.NewTownGitCheck())
d.Register(doctor.NewTownRootBranchCheck())
d.Register(doctor.NewPreCheckoutHookCheck())
d.Register(doctor.NewDaemonCheck())
d.Register(doctor.NewRepoFingerprintCheck())
d.Register(doctor.NewBootHealthCheck())
d.Register(doctor.NewBeadsDatabaseCheck())
d.Register(doctor.NewCustomTypesCheck())
d.Register(doctor.NewRoleLabelCheck())
d.Register(doctor.NewFormulaCheck())
d.Register(doctor.NewPrefixConflictCheck())
d.Register(doctor.NewPrefixMismatchCheck())
d.Register(doctor.NewRoutesCheck())
d.Register(doctor.NewRigRoutesJSONLCheck())
d.Register(doctor.NewRoutingModeCheck())
d.Register(doctor.NewOrphanSessionCheck())
d.Register(doctor.NewZombieSessionCheck())
d.Register(doctor.NewOrphanProcessCheck())
d.Register(doctor.NewWispGCCheck())
d.Register(doctor.NewCheckMisclassifiedWisps())
d.Register(doctor.NewBranchCheck())
d.Register(doctor.NewBeadsSyncOrphanCheck())
d.Register(doctor.NewCloneDivergenceCheck())
d.Register(doctor.NewIdentityCollisionCheck())
d.Register(doctor.NewLinkedPaneCheck())
d.Register(doctor.NewThemeCheck())
d.Register(doctor.NewCrashReportCheck())
d.Register(doctor.NewEnvVarsCheck())
// Patrol system checks
d.Register(doctor.NewPatrolMoleculesExistCheck())
d.Register(doctor.NewPatrolHooksWiredCheck())
d.Register(doctor.NewPatrolNotStuckCheck())
d.Register(doctor.NewPatrolPluginsAccessibleCheck())
d.Register(doctor.NewPatrolRolesHavePromptsCheck())
d.Register(doctor.NewAgentBeadsCheck())
d.Register(doctor.NewRigBeadsCheck())
d.Register(doctor.NewRoleBeadsCheck())
// NOTE: StaleAttachmentsCheck removed - staleness detection belongs in Deacon molecule
// Config architecture checks
d.Register(doctor.NewSettingsCheck())
d.Register(doctor.NewSessionHookCheck())
d.Register(doctor.NewRuntimeGitignoreCheck())
d.Register(doctor.NewLegacyGastownCheck())
d.Register(doctor.NewClaudeSettingsCheck())
// Priming subsystem check
d.Register(doctor.NewPrimingCheck())
// Crew workspace checks
d.Register(doctor.NewCrewStateCheck())
d.Register(doctor.NewCrewWorktreeCheck())
d.Register(doctor.NewCommandsCheck())
// Lifecycle hygiene checks
d.Register(doctor.NewLifecycleHygieneCheck())
// Hook attachment checks
d.Register(doctor.NewHookAttachmentValidCheck())
d.Register(doctor.NewHookSingletonCheck())
d.Register(doctor.NewOrphanedAttachmentsCheck())
// Rig-specific checks (only when --rig is specified)
if doctorRig != "" {
d.RegisterAll(doctor.RigChecks()...)
}
// Run checks
var report *doctor.Report
if doctorFix {
report = d.Fix(ctx)
} else {
report = d.Run(ctx)
}
// Print report
report.Print(os.Stdout, doctorVerbose)
// Exit with error code if there are errors
if report.HasErrors() {
return fmt.Errorf("doctor found %d error(s)", report.Summary.Errors)
}
return nil
}