feat: add --estimate flag to bd create/update commands (GH #443)
The estimated_minutes field existed in the Issue schema but wasn't exposed via CLI. This adds: - --estimate / -e flag to bd create (e.g., bd create "Task" --estimate 120) - --estimate / -e flag to bd update (e.g., bd update bd-xyz --estimate 60) - EstimatedMinutes field to RPC CreateArgs and UpdateArgs - Server-side handling in handleCreate and updatesFromArgs - Validation for non-negative values The value is specified in minutes and is useful for planning and prioritization. The vscode-beads extension already has an Estimate column that can now be populated. Fixes #443 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,16 @@ var createCmd = &cobra.Command{
|
||||
deps, _ := cmd.Flags().GetStringSlice("deps")
|
||||
forceCreate, _ := cmd.Flags().GetBool("force")
|
||||
repoOverride, _ := cmd.Flags().GetString("repo")
|
||||
|
||||
// Get estimate if provided
|
||||
var estimatedMinutes *int
|
||||
if cmd.Flags().Changed("estimate") {
|
||||
est, _ := cmd.Flags().GetInt("estimate")
|
||||
if est < 0 {
|
||||
FatalError("estimate must be a non-negative number of minutes")
|
||||
}
|
||||
estimatedMinutes = &est
|
||||
}
|
||||
// Use global jsonOutput set by PersistentPreRun
|
||||
|
||||
// Determine target repository using routing logic
|
||||
@@ -227,6 +237,7 @@ var createCmd = &cobra.Command{
|
||||
AcceptanceCriteria: acceptance,
|
||||
Assignee: assignee,
|
||||
ExternalRef: externalRef,
|
||||
EstimatedMinutes: estimatedMinutes,
|
||||
Labels: labels,
|
||||
Dependencies: deps,
|
||||
}
|
||||
@@ -264,6 +275,7 @@ var createCmd = &cobra.Command{
|
||||
IssueType: types.IssueType(issueType),
|
||||
Assignee: assignee,
|
||||
ExternalRef: externalRefPtr,
|
||||
EstimatedMinutes: estimatedMinutes,
|
||||
}
|
||||
|
||||
ctx := rootCtx
|
||||
@@ -402,6 +414,7 @@ func init() {
|
||||
createCmd.Flags().StringSlice("deps", []string{}, "Dependencies in format 'type:id' or 'id' (e.g., 'discovered-from:bd-20,blocks:bd-15' or 'bd-20')")
|
||||
createCmd.Flags().Bool("force", false, "Force creation even if prefix doesn't match database prefix")
|
||||
createCmd.Flags().String("repo", "", "Target repository for issue (overrides auto-routing)")
|
||||
createCmd.Flags().IntP("estimate", "e", 0, "Time estimate in minutes (e.g., 60 for 1 hour)")
|
||||
// Note: --json flag is defined as a persistent flag in main.go, not here
|
||||
rootCmd.AddCommand(createCmd)
|
||||
}
|
||||
|
||||
@@ -506,6 +506,14 @@ var updateCmd = &cobra.Command{
|
||||
externalRef, _ := cmd.Flags().GetString("external-ref")
|
||||
updates["external_ref"] = externalRef
|
||||
}
|
||||
if cmd.Flags().Changed("estimate") {
|
||||
estimate, _ := cmd.Flags().GetInt("estimate")
|
||||
if estimate < 0 {
|
||||
fmt.Fprintf(os.Stderr, "Error: estimate must be a non-negative number of minutes\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
updates["estimated_minutes"] = estimate
|
||||
}
|
||||
if cmd.Flags().Changed("add-label") {
|
||||
addLabels, _ := cmd.Flags().GetStringSlice("add-label")
|
||||
updates["add_labels"] = addLabels
|
||||
@@ -583,9 +591,12 @@ var updateCmd = &cobra.Command{
|
||||
if acceptanceCriteria, ok := updates["acceptance_criteria"].(string); ok {
|
||||
updateArgs.AcceptanceCriteria = &acceptanceCriteria
|
||||
}
|
||||
if externalRef, ok := updates["external_ref"].(string); ok { // NEW: Map external_ref
|
||||
if externalRef, ok := updates["external_ref"].(string); ok {
|
||||
updateArgs.ExternalRef = &externalRef
|
||||
}
|
||||
if estimate, ok := updates["estimated_minutes"].(int); ok {
|
||||
updateArgs.EstimatedMinutes = &estimate
|
||||
}
|
||||
if addLabels, ok := updates["add_labels"].([]string); ok {
|
||||
updateArgs.AddLabels = addLabels
|
||||
}
|
||||
@@ -1012,6 +1023,7 @@ func init() {
|
||||
updateCmd.Flags().String("notes", "", "Additional notes")
|
||||
updateCmd.Flags().String("acceptance-criteria", "", "DEPRECATED: use --acceptance")
|
||||
_ = updateCmd.Flags().MarkHidden("acceptance-criteria")
|
||||
updateCmd.Flags().IntP("estimate", "e", 0, "Time estimate in minutes (e.g., 60 for 1 hour)")
|
||||
updateCmd.Flags().StringSlice("add-label", nil, "Add labels (repeatable)")
|
||||
updateCmd.Flags().StringSlice("remove-label", nil, "Remove labels (repeatable)")
|
||||
updateCmd.Flags().StringSlice("set-labels", nil, "Set labels, replacing all existing (repeatable)")
|
||||
|
||||
@@ -68,6 +68,7 @@ type CreateArgs struct {
|
||||
AcceptanceCriteria string `json:"acceptance_criteria,omitempty"`
|
||||
Assignee string `json:"assignee,omitempty"`
|
||||
ExternalRef string `json:"external_ref,omitempty"` // Link to external issue trackers
|
||||
EstimatedMinutes *int `json:"estimated_minutes,omitempty"` // Time estimate in minutes
|
||||
Labels []string `json:"labels,omitempty"`
|
||||
Dependencies []string `json:"dependencies,omitempty"`
|
||||
}
|
||||
@@ -84,6 +85,7 @@ type UpdateArgs struct {
|
||||
Notes *string `json:"notes,omitempty"`
|
||||
Assignee *string `json:"assignee,omitempty"`
|
||||
ExternalRef *string `json:"external_ref,omitempty"` // Link to external issue trackers
|
||||
EstimatedMinutes *int `json:"estimated_minutes,omitempty"` // Time estimate in minutes
|
||||
AddLabels []string `json:"add_labels,omitempty"`
|
||||
RemoveLabels []string `json:"remove_labels,omitempty"`
|
||||
SetLabels []string `json:"set_labels,omitempty"`
|
||||
|
||||
@@ -70,6 +70,9 @@ func updatesFromArgs(a UpdateArgs) map[string]interface{} {
|
||||
if a.ExternalRef != nil {
|
||||
u["external_ref"] = *a.ExternalRef
|
||||
}
|
||||
if a.EstimatedMinutes != nil {
|
||||
u["estimated_minutes"] = *a.EstimatedMinutes
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
@@ -142,6 +145,7 @@ func (s *Server) handleCreate(req *Request) Response {
|
||||
AcceptanceCriteria: strValue(acceptance),
|
||||
Assignee: strValue(assignee),
|
||||
ExternalRef: externalRef,
|
||||
EstimatedMinutes: createArgs.EstimatedMinutes,
|
||||
Status: types.StatusOpen,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user