Skip to content

feat: add TUI diagnostics center - #197

Merged
gnanam1990 merged 1 commit into
mainfrom
feat/tui-diagnostics-center
Jun 14, 2026
Merged

feat: add TUI diagnostics center#197
gnanam1990 merged 1 commit into
mainfrom
feat/tui-diagnostics-center

Conversation

@Vasanthdev2004

@Vasanthdev2004 Vasanthdev2004 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add a TUI diagnostics renderer for /doctor with grouped provider/platform checks and actionable hints
  • add /health as a discoverable alias for /doctor
  • support async /doctor --connectivity in the TUI using the configured provider health probe
  • wire existing config paths and provider-health/user-agent context from CLI launch into TUI doctor
  • make doctor model health friendlier for dynamic OpenAI-compatible providers by warning/pass-through instead of failing unknown custom models

Tests

  • go test ./internal/tui -run Doctor|Health|Command -count=1
  • go test ./internal/doctor -run ProviderModel -count=1
  • go test ./internal/cli -run TestRunNoArgsLaunchesSetupTUIWithNilProviderWhenNoProviderConfigured|TestRunNoArgsLaunchesTUI -count=1
  • go test ./... -timeout 300s
  • go vet ./...
  • go build ./...
  • go run ./cmd/zero-release build
  • go run ./cmd/zero-release smoke

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced /doctor command with --connectivity flag to check provider connectivity and health.
    • Added /health as an alias for the /doctor command.
    • Improved diagnostic output with structured sections for Provider, Platform, and Backend information.
  • Bug Fixes

    • Provider model validation now properly fails when a required model is missing or empty.
  • Tests

    • Comprehensive test coverage added for doctor command functionality and provider validation scenarios.

@github-actions

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Tests: go test ./...
  • [pass] Build: go run ./cmd/zero-release build
  • [pass] Smoke build: go run ./cmd/zero-release smoke

Scope

Head: 511cc89dcffd
Changed files (13): internal/cli/app.go, internal/cli/app_test.go, internal/doctor/doctor.go, internal/doctor/doctor_test.go, internal/tui/command_center.go, internal/tui/commands.go, internal/tui/doctor_command_test.go, internal/tui/doctor_view.go, internal/tui/doctor_view_test.go, internal/tui/health_command_test.go, internal/tui/model.go, internal/tui/model_test.go, and 1 more

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR wires an async /doctor TUI command (aliased /health) with an optional --connectivity flag. It extends tui.Options and the model with config paths, a ProbeProviderHealth callback, and sequencing for async results. A new doctor_view.go converts doctor.Report to structured TUI output. The doctor core gains an early empty-model fail guard. CLI app resolution populates and forwards the new config paths.

Changes

TUI /doctor Command Flow

Layer / File(s) Summary
TUI Options and model struct extensions
internal/tui/options.go, internal/tui/model.go
tui.Options gains DoctorUserConfigPath, ProjectConfigPath, ProbeProviderHealth, and UserAgent fields. The model struct gains matching fields, doctorCommandSeq, and the doctorCommandResultMsg type.
Doctor provider model empty-check fix
internal/doctor/doctor.go, internal/doctor/doctor_test.go
providerModelCheck now returns StatusFail with "Provider model is required." immediately when profile.Model is blank, before any registry loading. Four targeted tests replace the prior generic OpenAI-compatible model warning test.
Doctor view rendering
internal/tui/doctor_view.go, internal/tui/doctor_view_test.go
New doctor_view.go implements doctorCommandOutput and helpers to convert doctor.Report plus optional BackendLifecycleSnapshot into a structured commandOutput with Provider/Platform/Other/Backend sections, status, and deduplicated hints.
model.go async dispatch and doctorOptions
internal/tui/model.go
newModel populates doctor/provider fields with config-path fallback. New doctorOptions(connectivity bool) conditionally calls ProbeProviderHealth. commandDoctor dispatch switches from static text to startDoctorCommand; doctorCommandResultMsg is handled in Update.
/doctor command handler in command center
internal/tui/command_center.go, internal/tui/commands.go
startDoctorCommand parses --connectivity/--help, manages running-state transcript messages, increments doctorCommandSeq, and returns an async tea.Cmd. doctorText(connectivity bool) routes through renderCommandOutput. /help is repositioned in commandDefinitions.
CLI config-path resolution
internal/cli/app.go, internal/cli/app_test.go
runInteractiveTUIWithSetup calls config.DefaultResolveOptions to derive DoctorUserConfigPath and ProjectConfigPath and passes them into tui.Options. Tests add OS-aware config-root setup and assert the new path fields.
Tests: command, view, alias, model
internal/tui/doctor_command_test.go, internal/tui/doctor_view_test.go, internal/tui/health_command_test.go, internal/tui/model_test.go
Covers doctorOptions config-path propagation, async probe invocation timing, /health alias canonicalization and autocomplete, doctor view status/grouping/hints/backend sections, and updated model transcript assertions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TUIModel as TUI model
    participant startDoctorCommand
    participant doctorOptions as doctorOptions(connectivity)
    participant ProbeProviderHealth
    participant DoctorRun as doctor.Run
    participant doctorCommandOutput

    User->>TUIModel: /doctor --connectivity
    TUIModel->>startDoctorCommand: command.text
    startDoctorCommand-->>TUIModel: tea.Cmd (async), transcript: "checking provider connectivity..."
    Note over startDoctorCommand: executes asynchronously
    startDoctorCommand->>doctorOptions: connectivity=true
    doctorOptions->>ProbeProviderHealth: {Connectivity, UserAgent, Profile}
    ProbeProviderHealth-->>doctorOptions: providerhealth.Result
    doctorOptions-->>startDoctorCommand: doctor.Options
    startDoctorCommand->>DoctorRun: Run(options)
    DoctorRun-->>startDoctorCommand: report
    startDoctorCommand->>doctorCommandOutput: report + BackendLifecycleSnapshot
    doctorCommandOutput-->>startDoctorCommand: commandOutput
    startDoctorCommand-->>TUIModel: doctorCommandResultMsg{id, text}
    TUIModel->>TUIModel: reduceTranscript with doctor output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Gitlawb/zero#151: Adds providerhealth.Probe and doctor.Options.ProviderHealth plumbing to surface connectivity checks — this PR consumes and extends that infrastructure in the TUI /doctor --connectivity flow.
  • Gitlawb/zero#57: Refactors the TUI command registry and adds a placeholder /doctor handler in commands.go/model.go — this PR replaces that placeholder with the full async implementation.
  • Gitlawb/zero#98: Introduces renderCommandOutput/commandOutput formatting infrastructure in command_center.go — this PR routes the new /doctor output through the same rendering pipeline.

Suggested reviewers

  • anandh8x
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add TUI diagnostics center' directly and clearly summarizes the main change—introducing a diagnostics center for the TUI with the /doctor command.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tui-diagnostics-center

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
internal/tui/command_center.go (1)

48-60: ⚡ Quick win

Consider adding test coverage for unknown flag handling.

parseDoctorCommandArgs returns an error for unknown flags, but there's no test verifying this behavior. Consider adding a test case like:

func TestParseDoctorCommandArgsRejectsUnknownFlags(t *testing.T) {
    _, _, err := parseDoctorCommandArgs("--invalid")
    if err == nil || !strings.Contains(err.Error(), "unknown doctor flag") {
        t.Fatalf("expected unknown flag error, got %v", err)
    }
}

This ensures the error path is exercised and prevents regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/command_center.go` around lines 48 - 60, The
parseDoctorCommandArgs function includes error handling for unknown flags, but
this error path is not covered by tests, leaving room for regressions. Add a
test function named TestParseDoctorCommandArgsRejectsUnknownFlags that calls
parseDoctorCommandArgs with an invalid flag argument (such as "--invalid"),
verifies that the function returns a non-nil error, and confirms that the error
message contains the text "unknown doctor flag". This will ensure the error
handling path is exercised and documented through tests.
internal/tui/model.go (1)

1002-1006: ⚡ Quick win

Clarify the id == 0 fallback condition.

The check msg.id == 0 || msg.id == m.doctorCommandSeq accepts messages with id 0, but looking at startDoctorCommand, messages are always created with a non-zero id (m.doctorCommandSeq after increment). When would a message have id 0? If this is defensive coding, a comment would help explain the intent. If it's unintentional, consider removing it or documenting the scenario.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/model.go` around lines 1002 - 1006, In the
doctorCommandResultMsg case handler where you check `msg.id == 0 || msg.id ==
m.doctorCommandSeq`, the `msg.id == 0` condition appears unnecessary since
messages created by startDoctorCommand always have non-zero ids (assigned from
m.doctorCommandSeq after increment). Either remove the `msg.id == 0` check if it
serves no purpose, or if this is intentional defensive coding, add a comment
explaining the scenario where a message could have id 0 and why it should be
accepted.
internal/tui/doctor_view.go (1)

146-150: 💤 Low value

Consider conditionally showing backend hints.

The backend hints (/mcp, /hooks, /plugins) are always added when a backend snapshot is present, regardless of check status. This might clutter the output when diagnostics are healthy.

Consider only adding these hints when there are non-pass checks or when explicitly requested (e.g., verbose mode).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/doctor_view.go` around lines 146 - 150, The backend hints for
`/mcp`, `/hooks`, and `/plugins` are being unconditionally added whenever
backend is not nil, which can clutter output when all diagnostics are healthy.
Modify the condition in the if backend != nil block to additionally check
whether there are any non-pass checks in the diagnostics or if verbose mode is
enabled. Only add these hint messages when there are actual issues to diagnose
or when verbose output is explicitly requested, rather than always showing them.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/tui/command_center.go`:
- Around line 48-60: The parseDoctorCommandArgs function includes error handling
for unknown flags, but this error path is not covered by tests, leaving room for
regressions. Add a test function named
TestParseDoctorCommandArgsRejectsUnknownFlags that calls parseDoctorCommandArgs
with an invalid flag argument (such as "--invalid"), verifies that the function
returns a non-nil error, and confirms that the error message contains the text
"unknown doctor flag". This will ensure the error handling path is exercised and
documented through tests.

In `@internal/tui/doctor_view.go`:
- Around line 146-150: The backend hints for `/mcp`, `/hooks`, and `/plugins`
are being unconditionally added whenever backend is not nil, which can clutter
output when all diagnostics are healthy. Modify the condition in the if backend
!= nil block to additionally check whether there are any non-pass checks in the
diagnostics or if verbose mode is enabled. Only add these hint messages when
there are actual issues to diagnose or when verbose output is explicitly
requested, rather than always showing them.

In `@internal/tui/model.go`:
- Around line 1002-1006: In the doctorCommandResultMsg case handler where you
check `msg.id == 0 || msg.id == m.doctorCommandSeq`, the `msg.id == 0` condition
appears unnecessary since messages created by startDoctorCommand always have
non-zero ids (assigned from m.doctorCommandSeq after increment). Either remove
the `msg.id == 0` check if it serves no purpose, or if this is intentional
defensive coding, add a comment explaining the scenario where a message could
have id 0 and why it should be accepted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 82da9a5c-566a-432d-a9e1-98bb2d8406b3

📥 Commits

Reviewing files that changed from the base of the PR and between 0444eb0 and 511cc89.

📒 Files selected for processing (13)
  • internal/cli/app.go
  • internal/cli/app_test.go
  • internal/doctor/doctor.go
  • internal/doctor/doctor_test.go
  • internal/tui/command_center.go
  • internal/tui/commands.go
  • internal/tui/doctor_command_test.go
  • internal/tui/doctor_view.go
  • internal/tui/doctor_view_test.go
  • internal/tui/health_command_test.go
  • internal/tui/model.go
  • internal/tui/model_test.go
  • internal/tui/options.go

@gnanam1990 gnanam1990 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve — well-built, no blockers. I verified the security/correctness-sensitive parts:

Async --connectivity — correct

  • Plain /doctor renders synchronously and offline (doctorOptions(false) never probes). Only --connectivity probes.
  • --connectivity returns a tea.Cmd, so the probe runs in bubbletea's goroutine — off the Update loop (no UI freeze). A doctorCommandSeq id on the producer + the case doctorCommandResultMsg guard (msg.id == m.doctorCommandSeq) correctly discards superseded probe results.
  • The probe is time-bounded: providerhealth.Probe wraps the ctx in a 5s context.WithTimeout and the HTTP client has its own timeout, so a hung endpoint can't leave the "running…" state stuck.

No secret leak in the new view

  • doctorCheckRow renders [status] id - message only — it does not render check.Details, where the raw baseURL lives. Combined with the existing credentialConfigured (presence-only, never the key), the TUI doctor surfaces no secret material.

Validation change is a strengthening, not a regression

  • provider.model now fails on an empty model (previously unhandled), and the unknown-custom-model path still warns + passes through with a helpful --connectivity hint. No weakening.

Tests / CI

  • Good coverage (doctor command/view, health alias, model wiring, provider-model validation); full CI green incl. the Security gate.

One optional, forward-looking note (non-blocking)

The TUI view intentionally omits check.Details (so baseURL isn't shown) — nice. If a future change ever renders Details in the TUI, strip userinfo from baseURL at that point (it can in rare cases embed user:token@host), to match the redaction model elsewhere.

Nice feature — clean async handling and good diagnostics ergonomics.

@gnanam1990
gnanam1990 merged commit 7153ec7 into main Jun 14, 2026
7 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 14, 2026
@Vasanthdev2004
Vasanthdev2004 deleted the feat/tui-diagnostics-center branch June 28, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants