bd create: support --description=- for stdin input
Allows descriptions with apostrophes and other shell-problematic characters by reading from stdin. Works with both --description=- and --body=- (the GitHub CLI-style alias). Example: echo "It's working" | bd create --title "Test" --description=- Also fixes pre-existing duplicate pinned column/variable declarations. Fixes beads-iwi 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@ func registerCommonIssueFlags(cmd *cobra.Command) {
|
|||||||
|
|
||||||
// getDescriptionFlag retrieves the description value, checking --body-file, --description-file,
|
// getDescriptionFlag retrieves the description value, checking --body-file, --description-file,
|
||||||
// --description, and --body (in that order of precedence).
|
// --description, and --body (in that order of precedence).
|
||||||
|
// Supports reading from stdin via --description=- or --body=- (useful when description
|
||||||
|
// contains apostrophes or other characters that are hard to escape in shell).
|
||||||
// Returns the value and whether any flag was explicitly changed.
|
// Returns the value and whether any flag was explicitly changed.
|
||||||
func getDescriptionFlag(cmd *cobra.Command) (string, bool) {
|
func getDescriptionFlag(cmd *cobra.Command) (string, bool) {
|
||||||
bodyFileChanged := cmd.Flags().Changed("body-file")
|
bodyFileChanged := cmd.Flags().Changed("body-file")
|
||||||
@@ -64,10 +66,29 @@ func getDescriptionFlag(cmd *cobra.Command) (string, bool) {
|
|||||||
return content, true
|
return content, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if description or body is "-" (read from stdin)
|
||||||
|
// This provides a convenient shorthand: --description=- instead of --body-file=-
|
||||||
|
desc, _ := cmd.Flags().GetString("description")
|
||||||
|
body, _ := cmd.Flags().GetString("body")
|
||||||
|
|
||||||
|
if desc == "-" || body == "-" {
|
||||||
|
// Error if both are set to different values
|
||||||
|
if descChanged && bodyChanged && desc != body {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: cannot specify both --description and --body with different values\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " --description: %q\n", desc)
|
||||||
|
fmt.Fprintf(os.Stderr, " --body: %q\n", body)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
content, err := readBodyFile("-")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error reading from stdin: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return content, true
|
||||||
|
}
|
||||||
|
|
||||||
// Error if both description and body are specified with different values
|
// Error if both description and body are specified with different values
|
||||||
if descChanged && bodyChanged {
|
if descChanged && bodyChanged {
|
||||||
desc, _ := cmd.Flags().GetString("description")
|
|
||||||
body, _ := cmd.Flags().GetString("body")
|
|
||||||
if desc != body {
|
if desc != body {
|
||||||
fmt.Fprintf(os.Stderr, "Error: cannot specify both --description and --body with different values\n")
|
fmt.Fprintf(os.Stderr, "Error: cannot specify both --description and --body with different values\n")
|
||||||
fmt.Fprintf(os.Stderr, " --description: %q\n", desc)
|
fmt.Fprintf(os.Stderr, " --description: %q\n", desc)
|
||||||
@@ -78,11 +99,9 @@ func getDescriptionFlag(cmd *cobra.Command) (string, bool) {
|
|||||||
|
|
||||||
// Return whichever was set (or description's value if neither)
|
// Return whichever was set (or description's value if neither)
|
||||||
if bodyChanged {
|
if bodyChanged {
|
||||||
body, _ := cmd.Flags().GetString("body")
|
|
||||||
return body, true
|
return body, true
|
||||||
}
|
}
|
||||||
|
|
||||||
desc, _ := cmd.Flags().GetString("description")
|
|
||||||
return desc, descChanged
|
return desc, descChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,11 +36,6 @@ func insertIssue(ctx context.Context, conn *sql.Conn, issue *types.Issue) error
|
|||||||
pinned = 1
|
pinned = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pinned := 0
|
|
||||||
if issue.Pinned {
|
|
||||||
pinned = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := conn.ExecContext(ctx, `
|
_, err := conn.ExecContext(ctx, `
|
||||||
INSERT OR IGNORE INTO issues (
|
INSERT OR IGNORE INTO issues (
|
||||||
id, content_hash, title, description, design, acceptance_criteria, notes,
|
id, content_hash, title, description, design, acceptance_criteria, notes,
|
||||||
@@ -100,11 +95,6 @@ func insertIssues(ctx context.Context, conn *sql.Conn, issues []*types.Issue) er
|
|||||||
pinned = 1
|
pinned = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pinned := 0
|
|
||||||
if issue.Pinned {
|
|
||||||
pinned = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.ExecContext(ctx,
|
_, err = stmt.ExecContext(ctx,
|
||||||
issue.ID, issue.ContentHash, issue.Title, issue.Description, issue.Design,
|
issue.ID, issue.ContentHash, issue.Title, issue.Description, issue.Design,
|
||||||
issue.AcceptanceCriteria, issue.Notes, issue.Status,
|
issue.AcceptanceCriteria, issue.Notes, issue.Status,
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ CREATE TABLE IF NOT EXISTS issues (
|
|||||||
pinned INTEGER DEFAULT 0,
|
pinned INTEGER DEFAULT 0,
|
||||||
-- NOTE: replies_to, relates_to, duplicate_of, superseded_by removed per Decision 004
|
-- NOTE: replies_to, relates_to, duplicate_of, superseded_by removed per Decision 004
|
||||||
-- These relationships are now stored in the dependencies table
|
-- These relationships are now stored in the dependencies table
|
||||||
-- Workflow fields
|
|
||||||
pinned INTEGER DEFAULT 0,
|
|
||||||
CHECK ((status = 'closed') = (closed_at IS NOT NULL))
|
CHECK ((status = 'closed') = (closed_at IS NOT NULL))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user