Files
beads/cmd/bd/undefer.go
Bob Cotton 025cdac962 feat(completion): add dynamic shell completions for issue IDs (#935)
Add intelligent shell completions that query the database to provide
issue ID suggestions with titles for commands that take IDs as arguments.

Changes:
- Add issueIDCompletion function that queries storage for all issues
- Register completion for show, update, close, edit, defer, undefer, reopen
- Add comprehensive test suite with 3 test cases
- Completions display ID with title as description (ID\tTitle format)

The completion function opens the database (read-only) and filters issues
based on the partially typed prefix, providing a better UX for commands
that require issue IDs.

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

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-06 18:59:25 -08:00

141 lines
3.6 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/steveyegge/beads/internal/rpc"
"github.com/steveyegge/beads/internal/types"
"github.com/steveyegge/beads/internal/ui"
"github.com/steveyegge/beads/internal/utils"
)
var undeferCmd = &cobra.Command{
Use: "undefer [id...]",
Short: "Undefer one or more issues (restore to open)",
Long: `Undefer issues to restore them to open status.
This brings issues back from the icebox so they can be worked on again.
Issues will appear in 'bd ready' if they have no blockers.
Examples:
bd undefer bd-abc # Undefer a single issue
bd undefer bd-abc bd-def # Undefer multiple issues`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
CheckReadonly("undefer")
ctx := rootCtx
// Resolve partial IDs first
var resolvedIDs []string
if daemonClient != nil {
for _, id := range args {
resolveArgs := &rpc.ResolveIDArgs{ID: id}
resp, err := daemonClient.ResolveID(resolveArgs)
if err != nil {
fmt.Fprintf(os.Stderr, "Error resolving ID %s: %v\n", id, err)
os.Exit(1)
}
var resolvedID string
if err := json.Unmarshal(resp.Data, &resolvedID); err != nil {
fmt.Fprintf(os.Stderr, "Error unmarshaling resolved ID: %v\n", err)
os.Exit(1)
}
resolvedIDs = append(resolvedIDs, resolvedID)
}
} else {
var err error
resolvedIDs, err = utils.ResolvePartialIDs(ctx, store, args)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
undeferredIssues := []*types.Issue{}
// If daemon is running, use RPC
if daemonClient != nil {
for _, id := range resolvedIDs {
status := string(types.StatusOpen)
emptyStr := "" // Clear defer_until by sending empty string (GH#820)
updateArgs := &rpc.UpdateArgs{
ID: id,
Status: &status,
DeferUntil: &emptyStr, // Clear defer_until timestamp
}
resp, err := daemonClient.Update(updateArgs)
if err != nil {
fmt.Fprintf(os.Stderr, "Error undeferring %s: %v\n", id, err)
continue
}
if jsonOutput {
var issue types.Issue
if err := json.Unmarshal(resp.Data, &issue); err == nil {
undeferredIssues = append(undeferredIssues, &issue)
}
} else {
fmt.Printf("%s Undeferred %s (now open)\n", ui.RenderPass("*"), id)
}
}
if jsonOutput && len(undeferredIssues) > 0 {
outputJSON(undeferredIssues)
}
return
}
// Fall back to direct storage access
if store == nil {
fmt.Fprintln(os.Stderr, "Error: database not initialized")
os.Exit(1)
}
for _, id := range args {
fullID, err := utils.ResolvePartialID(ctx, store, id)
if err != nil {
fmt.Fprintf(os.Stderr, "Error resolving %s: %v\n", id, err)
continue
}
updates := map[string]interface{}{
"status": string(types.StatusOpen),
"defer_until": nil, // Clear defer_until timestamp (GH#820)
}
if err := store.UpdateIssue(ctx, fullID, updates, actor); err != nil {
fmt.Fprintf(os.Stderr, "Error undeferring %s: %v\n", fullID, err)
continue
}
if jsonOutput {
issue, _ := store.GetIssue(ctx, fullID)
if issue != nil {
undeferredIssues = append(undeferredIssues, issue)
}
} else {
fmt.Printf("%s Undeferred %s (now open)\n", ui.RenderPass("*"), fullID)
}
}
// Schedule auto-flush if any issues were undeferred
if len(args) > 0 {
markDirtyAndScheduleFlush()
}
if jsonOutput && len(undeferredIssues) > 0 {
outputJSON(undeferredIssues)
}
},
}
func init() {
undeferCmd.ValidArgsFunction = issueIDCompletion
rootCmd.AddCommand(undeferCmd)
}