fix(update): add prefix routing like bd show (bd-618f) (#905)
bd update now uses resolveAndGetIssueWithRouting in direct mode, matching bd show's prefix routing behavior. This enables cross-rig issue updates from any directory using prefix-based routing. Changes: - Use resolveAndGetIssueWithRouting for ID resolution with routing - Iterate over original args instead of pre-resolved IDs - Use routed store (issueStore) and resolved ID throughout - Remove early ID resolution that was blocking routing in direct mode - Add proper result.Close() calls for resource cleanup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/steveyegge/beads/internal/timeparsing"
|
"github.com/steveyegge/beads/internal/timeparsing"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"github.com/steveyegge/beads/internal/types"
|
||||||
"github.com/steveyegge/beads/internal/ui"
|
"github.com/steveyegge/beads/internal/ui"
|
||||||
"github.com/steveyegge/beads/internal/utils"
|
|
||||||
"github.com/steveyegge/beads/internal/validation"
|
"github.com/steveyegge/beads/internal/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -205,13 +204,8 @@ create, update, show, or close operation).`,
|
|||||||
}
|
}
|
||||||
resolvedIDs = append(resolvedIDs, resolvedID)
|
resolvedIDs = append(resolvedIDs, resolvedID)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
resolvedIDs, err = utils.ResolvePartialIDs(ctx, store, args)
|
|
||||||
if err != nil {
|
|
||||||
FatalErrorRespectJSON("%v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Note: Direct mode (no daemon) uses resolveAndGetIssueWithRouting in the loop below
|
||||||
|
|
||||||
// If daemon is running, use RPC
|
// If daemon is running, use RPC
|
||||||
if daemonClient != nil {
|
if daemonClient != nil {
|
||||||
@@ -429,18 +423,32 @@ create, update, show, or close operation).`,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direct mode
|
// Direct mode - use routed resolution for cross-repo lookups
|
||||||
updatedIssues := []*types.Issue{}
|
updatedIssues := []*types.Issue{}
|
||||||
var firstUpdatedID string // Track first successful update for last-touched
|
var firstUpdatedID string // Track first successful update for last-touched
|
||||||
for _, id := range resolvedIDs {
|
for _, id := range args {
|
||||||
// Check if issue is a template: templates are read-only
|
// Resolve and get issue with routing (e.g., gt-xyz routes to gastown)
|
||||||
issue, err := store.GetIssue(ctx, id)
|
result, err := resolveAndGetIssueWithRouting(ctx, store, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error getting %s: %v\n", id, err)
|
if result != nil {
|
||||||
|
result.Close()
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Error resolving %s: %v\n", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if result == nil || result.Issue == nil {
|
||||||
|
if result != nil {
|
||||||
|
result.Close()
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Issue %s not found\n", id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
issue := result.Issue
|
||||||
|
issueStore := result.Store
|
||||||
|
|
||||||
if err := validateIssueUpdatable(id, issue); err != nil {
|
if err := validateIssueUpdatable(id, issue); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,6 +457,7 @@ create, update, show, or close operation).`,
|
|||||||
// Check if already claimed (has non-empty assignee)
|
// Check if already claimed (has non-empty assignee)
|
||||||
if issue.Assignee != "" {
|
if issue.Assignee != "" {
|
||||||
fmt.Fprintf(os.Stderr, "Error claiming %s: already claimed by %s\n", id, issue.Assignee)
|
fmt.Fprintf(os.Stderr, "Error claiming %s: already claimed by %s\n", id, issue.Assignee)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Atomically set assignee and status
|
// Atomically set assignee and status
|
||||||
@@ -456,8 +465,9 @@ create, update, show, or close operation).`,
|
|||||||
"assignee": actor,
|
"assignee": actor,
|
||||||
"status": "in_progress",
|
"status": "in_progress",
|
||||||
}
|
}
|
||||||
if err := store.UpdateIssue(ctx, id, claimUpdates, actor); err != nil {
|
if err := issueStore.UpdateIssue(ctx, result.ResolvedID, claimUpdates, actor); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error claiming %s: %v\n", id, err)
|
fmt.Fprintf(os.Stderr, "Error claiming %s: %v\n", id, err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -470,8 +480,9 @@ create, update, show, or close operation).`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(regularUpdates) > 0 {
|
if len(regularUpdates) > 0 {
|
||||||
if err := store.UpdateIssue(ctx, id, regularUpdates, actor); err != nil {
|
if err := issueStore.UpdateIssue(ctx, result.ResolvedID, regularUpdates, actor); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error updating %s: %v\n", id, err)
|
fmt.Fprintf(os.Stderr, "Error updating %s: %v\n", id, err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,8 +499,9 @@ create, update, show, or close operation).`,
|
|||||||
removeLabels = v
|
removeLabels = v
|
||||||
}
|
}
|
||||||
if len(setLabels) > 0 || len(addLabels) > 0 || len(removeLabels) > 0 {
|
if len(setLabels) > 0 || len(addLabels) > 0 || len(removeLabels) > 0 {
|
||||||
if err := applyLabelUpdates(ctx, store, id, actor, setLabels, addLabels, removeLabels); err != nil {
|
if err := applyLabelUpdates(ctx, issueStore, result.ResolvedID, actor, setLabels, addLabels, removeLabels); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error updating labels for %s: %v\n", id, err)
|
fmt.Fprintf(os.Stderr, "Error updating labels for %s: %v\n", id, err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,26 +510,29 @@ create, update, show, or close operation).`,
|
|||||||
if newParent, ok := updates["parent"].(string); ok {
|
if newParent, ok := updates["parent"].(string); ok {
|
||||||
// Validate new parent exists (unless empty string to remove parent)
|
// Validate new parent exists (unless empty string to remove parent)
|
||||||
if newParent != "" {
|
if newParent != "" {
|
||||||
parentIssue, err := store.GetIssue(ctx, newParent)
|
parentIssue, err := issueStore.GetIssue(ctx, newParent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error getting parent %s: %v\n", newParent, err)
|
fmt.Fprintf(os.Stderr, "Error getting parent %s: %v\n", newParent, err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if parentIssue == nil {
|
if parentIssue == nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error: parent issue %s not found\n", newParent)
|
fmt.Fprintf(os.Stderr, "Error: parent issue %s not found\n", newParent)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and remove existing parent-child dependency
|
// Find and remove existing parent-child dependency
|
||||||
deps, err := store.GetDependencyRecords(ctx, id)
|
deps, err := issueStore.GetDependencyRecords(ctx, result.ResolvedID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error getting dependencies for %s: %v\n", id, err)
|
fmt.Fprintf(os.Stderr, "Error getting dependencies for %s: %v\n", id, err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, dep := range deps {
|
for _, dep := range deps {
|
||||||
if dep.Type == types.DepParentChild {
|
if dep.Type == types.DepParentChild {
|
||||||
if err := store.RemoveDependency(ctx, id, dep.DependsOnID, actor); err != nil {
|
if err := issueStore.RemoveDependency(ctx, result.ResolvedID, dep.DependsOnID, actor); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error removing old parent dependency: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error removing old parent dependency: %v\n", err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -527,19 +542,20 @@ create, update, show, or close operation).`,
|
|||||||
// Add new parent-child dependency (if not removing parent)
|
// Add new parent-child dependency (if not removing parent)
|
||||||
if newParent != "" {
|
if newParent != "" {
|
||||||
newDep := &types.Dependency{
|
newDep := &types.Dependency{
|
||||||
IssueID: id,
|
IssueID: result.ResolvedID,
|
||||||
DependsOnID: newParent,
|
DependsOnID: newParent,
|
||||||
Type: types.DepParentChild,
|
Type: types.DepParentChild,
|
||||||
}
|
}
|
||||||
if err := store.AddDependency(ctx, newDep, actor); err != nil {
|
if err := issueStore.AddDependency(ctx, newDep, actor); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error adding parent dependency: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error adding parent dependency: %v\n", err)
|
||||||
|
result.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run update hook
|
// Run update hook
|
||||||
updatedIssue, _ := store.GetIssue(ctx, id)
|
updatedIssue, _ := issueStore.GetIssue(ctx, result.ResolvedID)
|
||||||
if updatedIssue != nil && hookRunner != nil {
|
if updatedIssue != nil && hookRunner != nil {
|
||||||
hookRunner.Run(hooks.EventUpdate, updatedIssue)
|
hookRunner.Run(hooks.EventUpdate, updatedIssue)
|
||||||
}
|
}
|
||||||
@@ -549,13 +565,14 @@ create, update, show, or close operation).`,
|
|||||||
updatedIssues = append(updatedIssues, updatedIssue)
|
updatedIssues = append(updatedIssues, updatedIssue)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s Updated issue: %s\n", ui.RenderPass("✓"), id)
|
fmt.Printf("%s Updated issue: %s\n", ui.RenderPass("✓"), result.ResolvedID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track first successful update for last-touched
|
// Track first successful update for last-touched
|
||||||
if firstUpdatedID == "" {
|
if firstUpdatedID == "" {
|
||||||
firstUpdatedID = id
|
firstUpdatedID = result.ResolvedID
|
||||||
}
|
}
|
||||||
|
result.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set last touched after all updates complete
|
// Set last touched after all updates complete
|
||||||
|
|||||||
Reference in New Issue
Block a user