Files
gastown/internal/cmd/feed.go
Steve Yegge f8fc01ecb5 Add gt feed command for activity streaming (gt-3pm0f)
Wraps bd activity with Gas Town integration:
- Default follow mode for streaming
- --rig flag to target specific rig beads
- Passthrough of bd activity flags (--since, --mol, --type, --limit)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 09:46:37 -08:00

142 lines
3.8 KiB
Go

package cmd
import (
"fmt"
"os"
"os/exec"
"syscall"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/workspace"
)
var (
feedFollow bool
feedLimit int
feedSince string
feedMol string
feedType string
feedRig string
feedNoFollow bool
)
func init() {
rootCmd.AddCommand(feedCmd)
feedCmd.Flags().BoolVarP(&feedFollow, "follow", "f", false, "Stream events in real-time (default when no other flags)")
feedCmd.Flags().BoolVar(&feedNoFollow, "no-follow", false, "Show events once and exit")
feedCmd.Flags().IntVarP(&feedLimit, "limit", "n", 100, "Maximum number of events to show")
feedCmd.Flags().StringVar(&feedSince, "since", "", "Show events since duration (e.g., 5m, 1h, 30s)")
feedCmd.Flags().StringVar(&feedMol, "mol", "", "Filter by molecule/issue ID prefix")
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")
}
var feedCmd = &cobra.Command{
Use: "feed",
GroupID: GroupDiag,
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, streams in follow mode. Use --no-follow to show events once.
Event symbols:
+ created/bonded - New issue or molecule created
→ in_progress - Work started on an issue
✓ completed - Issue closed or step completed
✗ failed - Step or issue failed
⊘ deleted - Issue removed
Examples:
gt feed # Stream all events (default: --follow)
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,
}
func runFeed(cmd *cobra.Command, args []string) error {
// Find bd binary
bdPath, err := exec.LookPath("bd")
if err != nil {
return fmt.Errorf("bd not found in PATH: %w", err)
}
// Determine working directory
workDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("getting current directory: %w", err)
}
// If --rig specified, find that rig's beads directory
if feedRig != "" {
townRoot, err := workspace.FindFromCwdOrError()
if err != nil {
return fmt.Errorf("not in a Gas Town workspace: %w", err)
}
// Try common beads locations for the rig
candidates := []string{
fmt.Sprintf("%s/%s/mayor/rig", townRoot, feedRig),
fmt.Sprintf("%s/%s", townRoot, feedRig),
}
found := false
for _, candidate := range candidates {
if _, err := os.Stat(candidate + "/.beads"); err == nil {
workDir = candidate
found = true
break
}
}
if !found {
return fmt.Errorf("rig '%s' not found or has no .beads directory", feedRig)
}
}
// Build bd activity command args
bdArgs := []string{"bd", "activity"}
// Default to follow mode unless --no-follow or other display flags set
shouldFollow := !feedNoFollow
if feedFollow {
shouldFollow = true
}
if shouldFollow {
bdArgs = append(bdArgs, "--follow")
}
if feedLimit != 100 {
bdArgs = append(bdArgs, "--limit", fmt.Sprintf("%d", feedLimit))
}
if feedSince != "" {
bdArgs = append(bdArgs, "--since", feedSince)
}
if feedMol != "" {
bdArgs = append(bdArgs, "--mol", feedMol)
}
if feedType != "" {
bdArgs = append(bdArgs, "--type", feedType)
}
// Use exec to replace the current process with bd
// This gives clean signal handling and terminal control
env := os.Environ()
// Change to the target directory before exec
if err := os.Chdir(workDir); err != nil {
return fmt.Errorf("changing to directory %s: %w", workDir, err)
}
return syscall.Exec(bdPath, bdArgs, env)
}