// Command dashboard provides an interactive TUI for managing bot agents. // // Usage: // // dashboard # launch the interactive TUI // go run ./cmd/dashboard package main import ( "fmt" "os" tea "github.com/charmbracelet/bubbletea" puretui "github.com/enmanuel/agents/pkg/tui" "github.com/enmanuel/agents/shell/process" shelltui "github.com/enmanuel/agents/shell/tui" ) const ( runDir = "run" agentsGlob = "agents/*/config.yaml" ) func main() { _ = os.MkdirAll(runDir, 0o755) mgr := process.NewManager(runDir, agentsGlob, "") adapter := shelltui.NewAdapter(mgr) p := tea.NewProgram(newBridge(adapter), tea.WithAltScreen()) if _, err := p.Run(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } // bridge implements tea.Model and connects the pure Update with the impure Adapter. type bridge struct { model puretui.Model adapter *shelltui.Adapter } func newBridge(adapter *shelltui.Adapter) bridge { return bridge{ model: puretui.InitialModel(), adapter: adapter, } } func (b bridge) Init() tea.Cmd { return b.adapter.RunIntent(puretui.Intent{Kind: puretui.IntentLoadAgents}) } func (b bridge) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Convert tea messages to pure messages. var pureMsg interface{} switch m := msg.(type) { case tea.KeyMsg: pureMsg = puretui.KeyMsg{Str: m.String()} case tea.WindowSizeMsg: pureMsg = puretui.WindowSizeMsg{Width: m.Width, Height: m.Height} default: // MsgAgentsLoaded, MsgActionDone, MsgLogsLoaded, MsgTick pass through. pureMsg = msg } // Pure update: no side effects. newModel, intents := puretui.Update(b.model, pureMsg) b.model = newModel // Convert pure intents to impure tea.Cmds. cmds := make([]tea.Cmd, 0, len(intents)) for _, intent := range intents { if cmd := b.adapter.RunIntent(intent); cmd != nil { cmds = append(cmds, cmd) } } return b, tea.Batch(cmds...) } func (b bridge) View() string { return puretui.View(b.model) }