feat: Add label operations to bd update command
Implements bd-au0.2, completing all P0 tasks in the command standardization epic. Changes: - Add --add-label, --remove-label, --set-labels flags to bd update - Support multiple labels via repeatable flags - Implement in both daemon and direct modes - Add comprehensive tests for all label operations The bd update command now supports: bd update <id> --add-label <label> # Add one or more labels bd update <id> --remove-label <label> # Remove one or more labels bd update <id> --set-labels <labels> # Replace all labels 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -74,16 +74,19 @@ type CreateArgs struct {
|
||||
|
||||
// UpdateArgs represents arguments for the update operation
|
||||
type UpdateArgs struct {
|
||||
ID string `json:"id"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
Design *string `json:"design,omitempty"`
|
||||
AcceptanceCriteria *string `json:"acceptance_criteria,omitempty"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
Assignee *string `json:"assignee,omitempty"`
|
||||
ExternalRef *string `json:"external_ref,omitempty"` // Link to external issue trackers
|
||||
ID string `json:"id"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Status *string `json:"status,omitempty"`
|
||||
Priority *int `json:"priority,omitempty"`
|
||||
Design *string `json:"design,omitempty"`
|
||||
AcceptanceCriteria *string `json:"acceptance_criteria,omitempty"`
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
Assignee *string `json:"assignee,omitempty"`
|
||||
ExternalRef *string `json:"external_ref,omitempty"` // Link to external issue trackers
|
||||
AddLabels []string `json:"add_labels,omitempty"`
|
||||
RemoveLabels []string `json:"remove_labels,omitempty"`
|
||||
SetLabels []string `json:"set_labels,omitempty"`
|
||||
}
|
||||
|
||||
// CloseArgs represents arguments for the close operation
|
||||
|
||||
@@ -279,19 +279,73 @@ func (s *Server) handleUpdate(req *Request) Response {
|
||||
|
||||
ctx := s.reqCtx(req)
|
||||
updates := updatesFromArgs(updateArgs)
|
||||
if len(updates) == 0 {
|
||||
return Response{Success: true}
|
||||
}
|
||||
actor := s.reqActor(req)
|
||||
|
||||
if err := store.UpdateIssue(ctx, updateArgs.ID, updates, s.reqActor(req)); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to update issue: %v", err),
|
||||
// Apply regular field updates if any
|
||||
if len(updates) > 0 {
|
||||
if err := store.UpdateIssue(ctx, updateArgs.ID, updates, actor); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to update issue: %v", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit mutation event for event-driven daemon
|
||||
s.emitMutation(MutationUpdate, updateArgs.ID)
|
||||
// Handle label operations
|
||||
// Set labels (replaces all existing labels)
|
||||
if len(updateArgs.SetLabels) > 0 {
|
||||
// Get current labels
|
||||
currentLabels, err := store.GetLabels(ctx, updateArgs.ID)
|
||||
if err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to get current labels: %v", err),
|
||||
}
|
||||
}
|
||||
// Remove all current labels
|
||||
for _, label := range currentLabels {
|
||||
if err := store.RemoveLabel(ctx, updateArgs.ID, label, actor); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to remove label %s: %v", label, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add new labels
|
||||
for _, label := range updateArgs.SetLabels {
|
||||
if err := store.AddLabel(ctx, updateArgs.ID, label, actor); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to set label %s: %v", label, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add labels
|
||||
for _, label := range updateArgs.AddLabels {
|
||||
if err := store.AddLabel(ctx, updateArgs.ID, label, actor); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to add label %s: %v", label, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove labels
|
||||
for _, label := range updateArgs.RemoveLabels {
|
||||
if err := store.RemoveLabel(ctx, updateArgs.ID, label, actor); err != nil {
|
||||
return Response{
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("failed to remove label %s: %v", label, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit mutation event for event-driven daemon (only if any updates or label operations were performed)
|
||||
if len(updates) > 0 || len(updateArgs.SetLabels) > 0 || len(updateArgs.AddLabels) > 0 || len(updateArgs.RemoveLabels) > 0 {
|
||||
s.emitMutation(MutationUpdate, updateArgs.ID)
|
||||
}
|
||||
|
||||
issue, err := store.GetIssue(ctx, updateArgs.ID)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user