feat: add interactive TUI dashboard command#372
feat: add interactive TUI dashboard command#372yasunogithub wants to merge 2 commits intoentireio:mainfrom
Conversation
Add `entire dashboard` with four tabs (Sessions, Checkpoints, Active, Settings) using bubbletea/lipgloss. Includes accessible text-mode fallback, filter search, checkpoint rewind with PreviewRewind safety check, and rune-safe input handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Entire-Checkpoint: 1e466c84838a
There was a problem hiding this comment.
Pull request overview
This PR adds an interactive TUI dashboard command (entire dashboard) built with the bubbletea/lipgloss framework. The dashboard provides a consolidated view of sessions, checkpoints, active sessions, and settings across four navigable tabs. It includes accessibility support via a text-mode fallback when ACCESSIBLE=1 is set, and implements checkpoint rewind functionality with safety warnings for untracked file deletions.
Changes:
- Adds new
entire dashboardcommand with full-screen TUI and accessible text-mode fallback - Implements four dashboard tabs: Sessions, Checkpoints, Active Sessions, and Settings with filtering and detail views
- Integrates checkpoint rewind workflow with
PreviewRewindsafety checks for file deletion warnings - Promotes bubbletea and lipgloss from indirect to direct dependencies in go.mod
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| go.mod | Promotes bubbletea and lipgloss to direct dependencies |
| cmd/entire/cli/strategy/registry.go | Removes nolint:ireturn comments (will cause linter failures) |
| cmd/entire/cli/strategy/manual_commit.go | Removes nolint:ireturn comments (will cause linter failures) |
| cmd/entire/cli/strategy/auto_commit.go | Removes nolint:ireturn comments (will cause linter failures) |
| cmd/entire/cli/root.go | Registers new dashboard command |
| cmd/entire/cli/dashboard_cmd.go | Command entry point with accessibility check and rewind flow |
| cmd/entire/cli/dashboard/styles.go | Dracula theme colors for consistent TUI styling |
| cmd/entire/cli/dashboard/settings_tab.go | Settings display tab with configuration and agent status |
| cmd/entire/cli/dashboard/sessions_tab.go | Sessions tab with rune-safe filtering and detail views |
| cmd/entire/cli/dashboard/data.go | Async data loading commands and utility functions |
| cmd/entire/cli/dashboard/dashboard.go | Main bubbletea model with tab navigation and lifecycle |
| cmd/entire/cli/dashboard/checkpoints_tab.go | Checkpoints tab with filtering and rewind confirmation |
| cmd/entire/cli/dashboard/active_tab.go | Active sessions tab with header-pinning scroll |
| cmd/entire/cli/dashboard/accessible.go | Text-based accessible mode fallback |
| @@ -61,7 +61,7 @@ const DefaultStrategyName = StrategyNameManualCommit | |||
|
|
|||
| // Default returns the default strategy. | |||
| // Falls back to returning nil if no strategies are registered. | |||
There was a problem hiding this comment.
The nolint:ireturn comment should not be removed here. This function returns the Strategy interface, which is not in the ireturn allow list in .golangci.yaml. The linter will fail without this comment. The original comment correctly explained this is intentional for the registry pattern.
| // Falls back to returning nil if no strategies are registered. | |
| // Falls back to returning nil if no strategies are registered. | |
| //nolint:ireturn // Strategy is returned by design for the registry pattern; not in ireturn allow list |
| // | ||
|
|
||
| func NewManualCommitStrategy() Strategy { //nolint:ireturn // factory returns interface by design | ||
| func NewManualCommitStrategy() Strategy { |
There was a problem hiding this comment.
The nolint:ireturn comment should not be removed here. This function returns the Strategy interface, which is not in the ireturn allow list in .golangci.yaml. The linter will fail without this comment.
| // NewShadowStrategy creates a new manual-commit strategy instance. | ||
| // This legacy constructor delegates to NewManualCommitStrategy. | ||
| // | ||
|
|
There was a problem hiding this comment.
The nolint:ireturn comment should not be removed here. This function returns the Strategy interface, which is not in the ireturn allow list in .golangci.yaml. The linter will fail without this comment.
| //nolint:ireturn // Strategy is not in the ireturn allow list; constructors return the interface |
| @@ -84,7 +84,7 @@ func (s *AutoCommitStrategy) getCheckpointStore() (*checkpoint.GitStore, error) | |||
| // NewAutoCommitStrategy creates a new AutoCommitStrategy instance | |||
| // | |||
|
|
|||
There was a problem hiding this comment.
The nolint:ireturn comment should not be removed here. This function returns the Strategy interface, which is not in the ireturn allow list in .golangci.yaml. The linter will fail without this comment.
| //nolint:ireturn // factory intentionally returns Strategy interface (not in ireturn allow list) |
| func newDashboardCmd() *cobra.Command { | ||
| return &cobra.Command{ | ||
| Use: "dashboard", | ||
| Short: "Open interactive session dashboard", | ||
| Long: `Interactive TUI dashboard for browsing sessions, checkpoints, | ||
| active sessions, and settings. | ||
|
|
||
| Navigate with Tab/Shift+Tab between tabs, j/k or arrow keys to move, | ||
| Enter to view details, and q to quit. | ||
|
|
||
| In the Checkpoints tab, press r to rewind to a selected checkpoint.`, | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| // Check if we're in a git repository | ||
| if _, err := paths.RepoRoot(); err != nil { | ||
| cmd.SilenceUsage = true | ||
| fmt.Fprintln(cmd.ErrOrStderr(), "Not a git repository. Please run from within a git repository.") | ||
| return NewSilentError(errors.New("not a git repository")) | ||
| } | ||
|
|
||
| // Check if Entire is disabled | ||
| if checkDisabledGuard(cmd.OutOrStdout()) { | ||
| return nil | ||
| } | ||
|
|
||
| // Accessible mode uses text-based menu | ||
| if IsAccessibleMode() { | ||
| return dashboard.RunAccessible(cmd.OutOrStdout()) | ||
| } | ||
|
|
||
| // Run TUI dashboard | ||
| rewindReq, err := dashboard.Run() | ||
| if err != nil { | ||
| return fmt.Errorf("dashboard error: %w", err) | ||
| } | ||
|
|
||
| // Handle rewind request from dashboard | ||
| if rewindReq != nil { | ||
| return performRewindFromDashboard(cmd, rewindReq.PointID) | ||
| } | ||
|
|
||
| return nil | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // performRewindFromDashboard executes a rewind after the dashboard TUI exits. | ||
| func performRewindFromDashboard(cmd *cobra.Command, pointID string) error { | ||
| strat := GetStrategy() | ||
|
|
||
| // Check if rewind is possible | ||
| canRewind, reason, err := strat.CanRewind() | ||
| if err != nil { | ||
| return fmt.Errorf("failed to check rewind status: %w", err) | ||
| } | ||
| if !canRewind { | ||
| fmt.Fprintln(cmd.OutOrStdout(), reason) | ||
| return nil | ||
| } | ||
|
|
||
| // Find the matching rewind point | ||
| points, err := strat.GetRewindPoints(100) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to get rewind points: %w", err) | ||
| } | ||
|
|
||
| var target *strategy.RewindPoint | ||
| for i := range points { | ||
| if points[i].ID == pointID { | ||
| target = &points[i] | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if target == nil { | ||
| return fmt.Errorf("rewind point %s not found", pointID) | ||
| } | ||
|
|
||
| // Preview rewind to show warnings about files that will be deleted | ||
| preview, previewErr := strat.PreviewRewind(*target) | ||
| if previewErr == nil && preview != nil && len(preview.FilesToDelete) > 0 { | ||
| fmt.Fprintf(cmd.ErrOrStderr(), "\nWarning: The following untracked files will be DELETED:\n") | ||
| for _, f := range preview.FilesToDelete { | ||
| fmt.Fprintf(cmd.ErrOrStderr(), " - %s\n", f) | ||
| } | ||
| fmt.Fprintf(cmd.ErrOrStderr(), "\n") | ||
| } | ||
|
|
||
| // Perform the rewind | ||
| if err := strat.Rewind(*target); err != nil { | ||
| return fmt.Errorf("rewind failed: %w", err) | ||
| } | ||
|
|
||
| fmt.Fprintln(cmd.OutOrStdout(), "Successfully rewound to checkpoint.") | ||
|
|
||
| // Print resume command if agent info available | ||
| if target.Agent != "" { | ||
| resumeCmd := formatResumeCommand(target.Agent, target.SessionID) | ||
| if resumeCmd != "" { | ||
| fmt.Fprintf(cmd.OutOrStdout(), "\nTo resume: %s\n", resumeCmd) | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // formatResumeCommand generates the resume command for an agent. | ||
| func formatResumeCommand(agentType agent.AgentType, sessionID string) string { | ||
| ag, err := agent.GetByAgentType(agentType) | ||
| if err != nil { | ||
| return "" | ||
| } | ||
| return ag.FormatResumeCommand(sessionID) | ||
| } |
There was a problem hiding this comment.
Missing test coverage for the dashboard command and package. The repository has comprehensive test coverage for other commands (e.g., status_test.go, clean_test.go, setup_test.go). The dashboard command should have similar test coverage for:
- Command registration and basic execution
- Error handling (not in git repo, disabled guard)
- Accessible mode vs TUI mode
- Rewind flow from dashboard
- Data loading functions
- Filter and navigation logic in tab models
At minimum, add tests for the command entry point and error paths in dashboard_cmd.go.
| @@ -24,7 +24,7 @@ func Register(name string, factory Factory) { | |||
|
|
|||
| // Get retrieves a strategy by name. | |||
| // Returns an error if the strategy is not registered. | |||
There was a problem hiding this comment.
The nolint:ireturn comment should not be removed here. This function returns the Strategy interface, which is not in the ireturn allow list in .golangci.yaml (lines 98-110). The linter will fail without this comment. The original comment correctly explained this is intentional for the registry pattern.
| // Returns an error if the strategy is not registered. | |
| // Returns an error if the strategy is not registered. | |
| //nolint:ireturn // returning Strategy interface is intentional for the registry-based strategy pattern |
Add strategy.Strategy to the ireturn allow list in .golangci.yaml, replacing scattered //nolint:ireturn comments on factory functions. Add comprehensive test coverage for the dashboard package and command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Entire-Checkpoint: f3278aa07808
Summary
entire dashboardcommand with four tabs: Sessions, Checkpoints, Active Sessions, and SettingsACCESSIBLE=1)PreviewRewindsafety check for untracked file deletion warningsTest plan
mise run fmt && mise run lint && mise run test:ci— all passentire dashboardand navigate all four tabsACCESSIBLE=1 entire dashboardfor accessible text mode🤖 Generated with Claude Code