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>
This commit is contained in:
141
internal/cmd/feed.go
Normal file
141
internal/cmd/feed.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user