fix: Resolve 3 P1 bugs and add --body-file flag to bd create
- bd-06px: Add --no-git-history flag to import command for sync subprocess compatibility - bd-0zp7: Add missing hook calls (EventMessage in mail reply, EventClose in mail ack) - bd-hy9p: Implement --body-file and --description-file flags for reading descriptions from files Also closed stale issues that were already fixed: - bd-0d5p: macOS test timeout (already fixed with SysProcAttr) - bd-7yg: Merge driver placeholders (already using correct %A %O %A %B) - bd-4ri: Test deadlock (test passes in 0.04s now) - bd-b3og: TestImportBugIntegration (test no longer exists) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -13,18 +14,57 @@ func registerCommonIssueFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringP("description", "d", "", "Issue description")
|
||||
cmd.Flags().String("body", "", "Alias for --description (GitHub CLI convention)")
|
||||
_ = cmd.Flags().MarkHidden("body") // Hidden alias for agent/CLI ergonomics
|
||||
cmd.Flags().String("body-file", "", "Read description from file (use - for stdin)")
|
||||
cmd.Flags().String("description-file", "", "Alias for --body-file")
|
||||
_ = cmd.Flags().MarkHidden("description-file") // Hidden alias
|
||||
cmd.Flags().String("design", "", "Design notes")
|
||||
cmd.Flags().String("acceptance", "", "Acceptance criteria")
|
||||
cmd.Flags().String("external-ref", "", "External reference (e.g., 'gh-9', 'jira-ABC')")
|
||||
}
|
||||
|
||||
// getDescriptionFlag retrieves the description value, checking both --description and --body.
|
||||
// Returns the value and whether either flag was explicitly changed.
|
||||
// getDescriptionFlag retrieves the description value, checking --body-file, --description-file,
|
||||
// --description, and --body (in that order of precedence).
|
||||
// Returns the value and whether any flag was explicitly changed.
|
||||
func getDescriptionFlag(cmd *cobra.Command) (string, bool) {
|
||||
bodyFileChanged := cmd.Flags().Changed("body-file")
|
||||
descFileChanged := cmd.Flags().Changed("description-file")
|
||||
descChanged := cmd.Flags().Changed("description")
|
||||
bodyChanged := cmd.Flags().Changed("body")
|
||||
|
||||
// Error if both are specified with different values
|
||||
// Check for conflicting file flags
|
||||
if bodyFileChanged && descFileChanged {
|
||||
bodyFile, _ := cmd.Flags().GetString("body-file")
|
||||
descFile, _ := cmd.Flags().GetString("description-file")
|
||||
if bodyFile != descFile {
|
||||
fmt.Fprintf(os.Stderr, "Error: cannot specify both --body-file and --description-file with different values\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// File flags take precedence over string flags
|
||||
if bodyFileChanged || descFileChanged {
|
||||
var filePath string
|
||||
if bodyFileChanged {
|
||||
filePath, _ = cmd.Flags().GetString("body-file")
|
||||
} else {
|
||||
filePath, _ = cmd.Flags().GetString("description-file")
|
||||
}
|
||||
|
||||
// Error if both file and string flags are specified
|
||||
if descChanged || bodyChanged {
|
||||
fmt.Fprintf(os.Stderr, "Error: cannot specify both --body-file and --description/--body\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
content, err := readBodyFile(filePath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error reading body file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return content, true
|
||||
}
|
||||
|
||||
// Error if both description and body are specified with different values
|
||||
if descChanged && bodyChanged {
|
||||
desc, _ := cmd.Flags().GetString("description")
|
||||
body, _ := cmd.Flags().GetString("body")
|
||||
@@ -46,6 +86,30 @@ func getDescriptionFlag(cmd *cobra.Command) (string, bool) {
|
||||
return desc, descChanged
|
||||
}
|
||||
|
||||
// readBodyFile reads the description content from a file.
|
||||
// If filePath is "-", reads from stdin.
|
||||
func readBodyFile(filePath string) (string, error) {
|
||||
var reader io.Reader
|
||||
|
||||
if filePath == "-" {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
reader = file
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// registerPriorityFlag registers the priority flag with a specific default value.
|
||||
func registerPriorityFlag(cmd *cobra.Command, defaultVal string) {
|
||||
cmd.Flags().StringP("priority", "p", defaultVal, "Priority (0-4 or P0-P4, 0=highest)")
|
||||
|
||||
@@ -97,6 +97,8 @@ NOTE: Import requires direct database access and does not work with daemon mode.
|
||||
orphanHandling, _ := cmd.Flags().GetString("orphan-handling")
|
||||
force, _ := cmd.Flags().GetBool("force")
|
||||
protectLeftSnapshot, _ := cmd.Flags().GetBool("protect-left-snapshot")
|
||||
noGitHistory, _ := cmd.Flags().GetBool("no-git-history")
|
||||
_ = noGitHistory // Accepted for compatibility with bd sync subprocess calls
|
||||
|
||||
// Check if stdin is being used interactively (not piped)
|
||||
if input == "" && term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
@@ -777,6 +779,7 @@ func init() {
|
||||
importCmd.Flags().String("orphan-handling", "", "How to handle missing parent issues: strict/resurrect/skip/allow (default: use config or 'allow')")
|
||||
importCmd.Flags().Bool("force", false, "Force metadata update even when database is already in sync with JSONL")
|
||||
importCmd.Flags().Bool("protect-left-snapshot", false, "Protect issues in left snapshot from git-history-backfill (bd-sync-deletion fix)")
|
||||
importCmd.Flags().Bool("no-git-history", false, "Skip git history backfill for deletions (passed by bd sync)")
|
||||
importCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output import statistics in JSON format")
|
||||
rootCmd.AddCommand(importCmd)
|
||||
}
|
||||
|
||||
@@ -482,12 +482,20 @@ func runMailAck(cmd *cobra.Command, args []string) error {
|
||||
errors = append(errors, fmt.Sprintf("%s: %v", messageID, err))
|
||||
continue
|
||||
}
|
||||
// Fire close hook for GGT notifications (daemon mode)
|
||||
if hookRunner != nil {
|
||||
hookRunner.Run(hooks.EventClose, issue)
|
||||
}
|
||||
} else {
|
||||
// Direct mode - use CloseIssue for proper close handling
|
||||
if err := store.CloseIssue(rootCtx, messageID, "acknowledged", actor); err != nil {
|
||||
errors = append(errors, fmt.Sprintf("%s: %v", messageID, err))
|
||||
continue
|
||||
}
|
||||
// Fire close hook for GGT notifications (direct mode)
|
||||
if hookRunner != nil {
|
||||
hookRunner.Run(hooks.EventClose, issue)
|
||||
}
|
||||
}
|
||||
|
||||
acked = append(acked, messageID)
|
||||
@@ -646,6 +654,11 @@ func runMailReply(cmd *cobra.Command, args []string) error {
|
||||
flushManager.MarkDirty(false)
|
||||
}
|
||||
|
||||
// Fire message hook for GGT notifications
|
||||
if hookRunner != nil {
|
||||
hookRunner.Run(hooks.EventMessage, reply)
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
result := map[string]interface{}{
|
||||
"id": reply.ID,
|
||||
|
||||
Reference in New Issue
Block a user