Add a new doctor check that verifies the bd (beads) daemon is running and healthy. When the daemon fails to start, the check: - Surfaces specific error messages (legacy database, repo mismatch) - Provides one-liner fix commands - Auto-fixes by running bd migrate --update-repo-id when appropriate This addresses GH #25: gt status slow when bd daemon not running. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
166 lines
5.3 KiB
Go
166 lines
5.3 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
|
|
)
|
|
|
|
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
|
|
- mayor-state-valid Check mayor/state.json is valid (fixable)
|
|
|
|
Infrastructure checks:
|
|
- daemon Check if daemon is running (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
|
|
|
|
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
|
|
|
|
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")
|
|
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,
|
|
}
|
|
|
|
// Create doctor and register checks
|
|
d := doctor.NewDoctor()
|
|
|
|
// Register workspace-level checks first (fundamental)
|
|
d.RegisterAll(doctor.WorkspaceChecks()...)
|
|
|
|
// Register built-in checks
|
|
d.Register(doctor.NewTownGitCheck())
|
|
d.Register(doctor.NewDaemonCheck())
|
|
d.Register(doctor.NewBootHealthCheck())
|
|
d.Register(doctor.NewBeadsDatabaseCheck())
|
|
d.Register(doctor.NewBdDaemonCheck())
|
|
d.Register(doctor.NewPrefixConflictCheck())
|
|
d.Register(doctor.NewRoutesCheck())
|
|
d.Register(doctor.NewOrphanSessionCheck())
|
|
d.Register(doctor.NewOrphanProcessCheck())
|
|
d.Register(doctor.NewWispGCCheck())
|
|
d.Register(doctor.NewBranchCheck())
|
|
d.Register(doctor.NewBeadsSyncOrphanCheck())
|
|
d.Register(doctor.NewCloneDivergenceCheck())
|
|
d.Register(doctor.NewIdentityCollisionCheck())
|
|
d.Register(doctor.NewLinkedPaneCheck())
|
|
d.Register(doctor.NewThemeCheck())
|
|
|
|
// 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())
|
|
|
|
// NOTE: StaleAttachmentsCheck removed - staleness detection belongs in Deacon molecule
|
|
|
|
// Config architecture checks
|
|
d.Register(doctor.NewSettingsCheck())
|
|
d.Register(doctor.NewRuntimeGitignoreCheck())
|
|
d.Register(doctor.NewLegacyGastownCheck())
|
|
|
|
// Crew workspace checks
|
|
d.Register(doctor.NewCrewStateCheck())
|
|
|
|
// 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
|
|
}
|