package cli import ( "context" "fmt" "os" "time" "email-mcp/mcpgen" frameworkcli "forge.lclr.dev/AI/mcp-framework/cli" frameworkconfig "forge.lclr.dev/AI/mcp-framework/config" ) func (a *App) runDoctor(ctx context.Context, args []string) error { profileFlag, err := parseProfileArgs("doctor", args) if err != nil { return err } if a.configStore == nil { return fmt.Errorf("config store is not configured") } if a.openSecretStore == nil { return fmt.Errorf("secret store is not configured") } if a.newMailService == nil { return fmt.Errorf("mail service is not configured") } report := frameworkcli.RunDoctor(ctx, frameworkcli.DoctorOptions{ ConfigCheck: frameworkcli.NewConfigCheck(frameworkconfig.NewStore[ProfileConfig](mcpgen.BinaryName)), SecretStoreCheck: frameworkcli.SecretStoreAvailabilityCheck(a.openSecretStore), ConnectivityCheck: a.doctorConnectivityCheck(profileFlag), ExtraChecks: []frameworkcli.DoctorCheck{ a.doctorRequiredProfileFieldsCheck(profileFlag), }, }) if err := frameworkcli.RenderDoctorReport(a.stdout, report); err != nil { return err } if report.HasFailures() { return fmt.Errorf("doctor checks failed") } return nil } func (a *App) doctorRequiredProfileFieldsCheck(profileFlag string) frameworkcli.DoctorCheck { var ( profileValues map[string]string loadErr error ) check := frameworkcli.RequiredResolvedFieldsCheck(frameworkcli.ResolveOptions{ Fields: profileFieldSpecs(a.resolveDoctorProfileName(profileFlag)), Lookup: frameworkcli.ResolveLookup(frameworkcli.ResolveLookupOptions{ Env: frameworkcli.EnvLookup(os.LookupEnv), Config: func(key string) (string, bool, error) { if loadErr != nil { return "", false, loadErr } if profileValues == nil { cfg, _, err := a.configStore.LoadDefault() if err != nil { loadErr = err return "", false, loadErr } profileName := a.resolveProfileName(profileFlag, cfg.CurrentProfile) profile := cfg.Profiles[profileName] profileValues = map[string]string{ "host": profile.Host, "username": profile.Username, } } return frameworkcli.MapLookup(profileValues)(key) }, }), }) return func(ctx context.Context) frameworkcli.DoctorResult { result := check(ctx) result.Name = "profile" return result } } func (a *App) doctorConnectivityCheck(profileFlag string) frameworkcli.DoctorCheck { return func(parent context.Context) frameworkcli.DoctorResult { ctx, cancel := context.WithTimeout(parent, 35*time.Second) defer cancel() cred, err := a.loadCredential(profileFlag) if err != nil { return frameworkcli.DoctorResult{ Name: "connectivity", Status: frameworkcli.DoctorStatusFail, Summary: "cannot load IMAP credentials", Detail: err.Error(), } } if _, err := a.newMailService().ListMailboxes(ctx, cred); err != nil { return frameworkcli.DoctorResult{ Name: "connectivity", Status: frameworkcli.DoctorStatusFail, Summary: "IMAP server is unreachable or rejected authentication", Detail: err.Error(), } } return frameworkcli.DoctorResult{ Name: "connectivity", Status: frameworkcli.DoctorStatusOK, Summary: "IMAP server is reachable", } } } func (a *App) resolveDoctorProfileName(profileFlag string) string { if a.configStore == nil { return a.resolveProfileName(profileFlag, "") } cfg, _, err := a.configStore.LoadDefault() if err != nil { return a.resolveProfileName(profileFlag, "") } return a.resolveProfileName(profileFlag, cfg.CurrentProfile) }