feat: add display guards for large molecules in bd mol current (bd-vln0)
For molecules with >100 steps, shows summary instead of full step list: - Counts children first using efficient GetMoleculeProgress query - Shows progress summary with pointer to bd mol progress - Add --limit flag to cap output (e.g., --limit 50) - Add --range flag for specific ranges (e.g., --range 1-50) Also closes epic bd-5nu1 (mega-molecule progress support). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
f3224278a8
commit
7c9b975436
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/beads/internal/rpc"
|
||||
@@ -15,6 +17,10 @@ import (
|
||||
"github.com/steveyegge/beads/internal/utils"
|
||||
)
|
||||
|
||||
// LargeMoleculeThreshold is the step count above which we show summary instead of full list.
|
||||
// This prevents overwhelming output and slow queries for mega-molecules.
|
||||
const LargeMoleculeThreshold = 100
|
||||
|
||||
// MoleculeProgress holds the progress information for a molecule
|
||||
type MoleculeProgress struct {
|
||||
MoleculeID string `json:"molecule_id"`
|
||||
@@ -47,11 +53,18 @@ The output shows all steps with status indicators:
|
||||
[current] - Step is in_progress (you are here)
|
||||
[ready] - Step is ready to start (unblocked)
|
||||
[blocked] - Step is blocked by dependencies
|
||||
[pending] - Step is waiting`,
|
||||
[pending] - Step is waiting
|
||||
|
||||
For large molecules (>100 steps), a summary is shown instead.
|
||||
Use --limit or --range to view specific steps:
|
||||
bd mol current <id> --limit 50 # Show first 50 steps
|
||||
bd mol current <id> --range 100-150 # Show steps 100-150`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx := rootCtx
|
||||
forAgent, _ := cmd.Flags().GetString("for")
|
||||
limit, _ := cmd.Flags().GetInt("limit")
|
||||
rangeStr, _ := cmd.Flags().GetString("range")
|
||||
|
||||
// Determine who we're looking for
|
||||
agent := forAgent
|
||||
@@ -70,6 +83,20 @@ The output shows all steps with status indicators:
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse range flag if provided
|
||||
var rangeStart, rangeEnd int
|
||||
if rangeStr != "" {
|
||||
var err error
|
||||
rangeStart, rangeEnd, err = parseRange(rangeStr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: invalid range '%s': %v\n", rangeStr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if user explicitly requested steps
|
||||
explicitSteps := limit > 0 || rangeStr != ""
|
||||
|
||||
var molecules []*MoleculeProgress
|
||||
|
||||
if len(args) == 1 {
|
||||
@@ -80,11 +107,32 @@ The output shows all steps with status indicators:
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Check child count first for large molecule detection
|
||||
stats, err := store.GetMoleculeProgress(ctx, moleculeID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading molecule: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// If large molecule and no explicit flags, show summary
|
||||
if stats.Total > LargeMoleculeThreshold && !explicitSteps && !jsonOutput {
|
||||
printLargeMoleculeSummary(stats)
|
||||
return
|
||||
}
|
||||
|
||||
progress, err := getMoleculeProgress(ctx, store, moleculeID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading molecule: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Apply limit or range filtering
|
||||
if rangeStr != "" {
|
||||
progress.Steps = filterStepsByRange(progress.Steps, rangeStart, rangeEnd)
|
||||
} else if limit > 0 && len(progress.Steps) > limit {
|
||||
progress.Steps = progress.Steps[:limit]
|
||||
}
|
||||
|
||||
molecules = append(molecules, progress)
|
||||
} else {
|
||||
// Infer from in_progress issues
|
||||
@@ -480,7 +528,76 @@ func PrintContinueResult(result *ContinueResult) {
|
||||
}
|
||||
}
|
||||
|
||||
// parseRange parses a range string like "1-50" or "100-150" into start and end indices.
|
||||
// Returns 1-based indices (start=1 means first step).
|
||||
func parseRange(rangeStr string) (start, end int, err error) {
|
||||
parts := strings.Split(rangeStr, "-")
|
||||
if len(parts) != 2 {
|
||||
return 0, 0, fmt.Errorf("expected format 'start-end' (e.g., '1-50')")
|
||||
}
|
||||
start, err = strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("invalid start: %w", err)
|
||||
}
|
||||
end, err = strconv.Atoi(strings.TrimSpace(parts[1]))
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("invalid end: %w", err)
|
||||
}
|
||||
if start < 1 {
|
||||
return 0, 0, fmt.Errorf("start must be >= 1")
|
||||
}
|
||||
if end < start {
|
||||
return 0, 0, fmt.Errorf("end must be >= start")
|
||||
}
|
||||
return start, end, nil
|
||||
}
|
||||
|
||||
// filterStepsByRange filters steps to a 1-based range [start, end].
|
||||
func filterStepsByRange(steps []*StepStatus, start, end int) []*StepStatus {
|
||||
// Convert to 0-based indices
|
||||
startIdx := start - 1
|
||||
endIdx := end
|
||||
|
||||
if startIdx >= len(steps) {
|
||||
return nil
|
||||
}
|
||||
if endIdx > len(steps) {
|
||||
endIdx = len(steps)
|
||||
}
|
||||
return steps[startIdx:endIdx]
|
||||
}
|
||||
|
||||
// printLargeMoleculeSummary prints a summary for molecules with many steps.
|
||||
func printLargeMoleculeSummary(stats *types.MoleculeProgressStats) {
|
||||
fmt.Printf("Molecule: %s\n", ui.RenderAccent(stats.MoleculeID))
|
||||
fmt.Printf(" %s\n", stats.MoleculeTitle)
|
||||
fmt.Println()
|
||||
|
||||
// Progress summary
|
||||
var percent float64
|
||||
if stats.Total > 0 {
|
||||
percent = float64(stats.Completed) * 100 / float64(stats.Total)
|
||||
}
|
||||
fmt.Printf("Progress: %d / %d steps (%.1f%%)\n", stats.Completed, stats.Total, percent)
|
||||
|
||||
if stats.CurrentStepID != "" {
|
||||
fmt.Printf("Current step: %s\n", stats.CurrentStepID)
|
||||
} else if stats.InProgress > 0 {
|
||||
fmt.Printf("In progress: %d step(s)\n", stats.InProgress)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("%s This molecule has %d steps (threshold: %d).\n",
|
||||
ui.RenderWarn("Note:"), stats.Total, LargeMoleculeThreshold)
|
||||
fmt.Println("To view steps, use one of:")
|
||||
fmt.Printf(" bd mol current %s --limit 50 # First 50 steps\n", stats.MoleculeID)
|
||||
fmt.Printf(" bd mol current %s --range 1-50 # Steps 1-50\n", stats.MoleculeID)
|
||||
fmt.Printf(" bd mol progress %s # Efficient progress summary\n", stats.MoleculeID)
|
||||
}
|
||||
|
||||
func init() {
|
||||
molCurrentCmd.Flags().String("for", "", "Show molecules for a specific agent/assignee")
|
||||
molCurrentCmd.Flags().Int("limit", 0, "Maximum number of steps to display (0 = auto, use 'all' threshold)")
|
||||
molCurrentCmd.Flags().String("range", "", "Display specific step range (e.g., '1-50', '100-150')")
|
||||
molCmd.AddCommand(molCurrentCmd)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user