feat: Add charmbracelet TUI for gt feed (gt-u7dxq)
- Add bubbletea and bubbles dependencies - Create internal/tui/feed package with: - model.go: Main bubbletea model with agent tree and event stream - view.go: Rendering logic with lipgloss styling - keys.go: Vim-style key bindings (j/k, tab, /, q) - styles.go: Color palette and component styles - events.go: Event source from bd activity - Update gt feed to use TUI by default (--plain for text mode) - TUI features: agent tree by role, event stream, keyboard nav Closes gt-be0as, gt-lexye, gt-1uhmj 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,9 +7,12 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
"github.com/steveyegge/gastown/internal/tui/feed"
|
||||
"github.com/steveyegge/gastown/internal/workspace"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -21,6 +24,7 @@ var (
|
||||
feedRig string
|
||||
feedNoFollow bool
|
||||
feedWindow bool
|
||||
feedPlain bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -34,6 +38,7 @@ func init() {
|
||||
feedCmd.Flags().StringVar(&feedType, "type", "", "Filter by event type (create, update, delete, comment)")
|
||||
feedCmd.Flags().StringVar(&feedRig, "rig", "", "Run from specific rig's beads directory")
|
||||
feedCmd.Flags().BoolVarP(&feedWindow, "window", "w", false, "Open in dedicated tmux window (creates 'feed' window)")
|
||||
feedCmd.Flags().BoolVar(&feedPlain, "plain", false, "Use plain text output (bd activity) instead of TUI")
|
||||
}
|
||||
|
||||
var feedCmd = &cobra.Command{
|
||||
@@ -42,15 +47,16 @@ var feedCmd = &cobra.Command{
|
||||
Short: "Show real-time activity feed from beads",
|
||||
Long: `Display a real-time feed of issue and molecule state changes.
|
||||
|
||||
This command wraps 'bd activity' to show mutations as they happen,
|
||||
providing visibility into workflow progress across Gas Town.
|
||||
By default, launches an interactive TUI dashboard with:
|
||||
- Agent tree (top): Shows all agents organized by role with latest activity
|
||||
- Event stream (bottom): Chronological feed you can scroll through
|
||||
- Vim-style navigation: j/k to scroll, tab to switch panels, q to quit
|
||||
|
||||
By default, streams in follow mode. Use --no-follow to show events once.
|
||||
Use --plain for simple text output (wraps bd activity).
|
||||
|
||||
Tmux Integration:
|
||||
Use --window to open the feed in a dedicated tmux window named 'feed'.
|
||||
This creates a persistent window you can cycle to with C-b n/p.
|
||||
If the window already exists, switches to it.
|
||||
|
||||
Event symbols:
|
||||
+ created/bonded - New issue or molecule created
|
||||
@@ -60,12 +66,10 @@ Event symbols:
|
||||
⊘ deleted - Issue removed
|
||||
|
||||
Examples:
|
||||
gt feed # Stream all events (default: --follow)
|
||||
gt feed # Launch TUI dashboard
|
||||
gt feed --plain # Plain text output (bd activity)
|
||||
gt feed --window # Open in dedicated tmux window
|
||||
gt feed -w # Short form of --window
|
||||
gt feed --no-follow # Show last 100 events and exit
|
||||
gt feed --since 1h # Events from last hour
|
||||
gt feed --mol gt-xyz # Filter by issue prefix
|
||||
gt feed --rig gastown # Use gastown rig's beads`,
|
||||
RunE: runFeed,
|
||||
}
|
||||
@@ -112,7 +116,14 @@ func runFeed(cmd *cobra.Command, args []string) error {
|
||||
return runFeedInWindow(workDir, bdArgs)
|
||||
}
|
||||
|
||||
// Standard mode: exec bd activity directly
|
||||
// Use TUI by default if running in a terminal and not --plain
|
||||
useTUI := !feedPlain && term.IsTerminal(int(os.Stdout.Fd()))
|
||||
|
||||
if useTUI {
|
||||
return runFeedTUI(workDir)
|
||||
}
|
||||
|
||||
// Plain mode: exec bd activity directly
|
||||
return runFeedDirect(workDir, bdArgs)
|
||||
}
|
||||
|
||||
@@ -167,6 +178,28 @@ func runFeedDirect(workDir string, bdArgs []string) error {
|
||||
return syscall.Exec(bdPath, fullArgs, os.Environ())
|
||||
}
|
||||
|
||||
// runFeedTUI runs the interactive TUI feed.
|
||||
func runFeedTUI(workDir string) error {
|
||||
// Create event source from bd activity
|
||||
source, err := feed.NewBdActivitySource(workDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating event source: %w", err)
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
// Create model and connect event source
|
||||
m := feed.NewModel()
|
||||
m.SetEventChannel(source.Events())
|
||||
|
||||
// Run the TUI
|
||||
p := tea.NewProgram(m, tea.WithAltScreen())
|
||||
if _, err := p.Run(); err != nil {
|
||||
return fmt.Errorf("running TUI: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// runFeedInWindow opens the feed in a dedicated tmux window.
|
||||
func runFeedInWindow(workDir string, bdArgs []string) error {
|
||||
// Check if we're in tmux
|
||||
|
||||
Reference in New Issue
Block a user