feat: add silent quick-capture mode (bd q)

Add a truly silent quick-capture command that outputs only the issue ID,
enabling seamless script integration and reducing noise in AI conversations.

Changes:
- Add --silent flag to 'bd create' command
- Create new 'bd q' alias for quick capture
- Suppress warnings in silent/quiet mode

Usage:
  bd q "Fix login bug"           # Outputs only: bd-a1b2
  ISSUE=$(bd q "New feature")    # Capture ID in variable
  bd create "Task" --silent       # Same as bd q

Closes #540

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-16 01:08:40 -08:00
parent 7675c1dc31
commit cd8320fe3e
4 changed files with 325 additions and 212 deletions

2
.beads/.gitignore vendored
View File

@@ -30,3 +30,5 @@ beads.right.meta.json
!issues.jsonl
!metadata.json
!config.json
deletions.jsonl
deletions.jsonl.migrated

File diff suppressed because one or more lines are too long

View File

@@ -54,8 +54,11 @@ var createCmd = &cobra.Command{
FatalError("title required (or use --file to create from markdown)")
}
// Warn if creating a test issue in production database
if strings.HasPrefix(strings.ToLower(title), "test") {
// Get silent flag
silent, _ := cmd.Flags().GetBool("silent")
// Warn if creating a test issue in production database (unless silent mode)
if strings.HasPrefix(strings.ToLower(title), "test") && !silent && !debug.IsQuiet() {
yellow := color.New(color.FgYellow).SprintFunc()
fmt.Fprintf(os.Stderr, "%s Creating issue with 'Test' prefix in production database.\n", yellow("⚠"))
fmt.Fprintf(os.Stderr, " For testing, consider using: BEADS_DB=/tmp/test.db ./bd create \"Test issue\"\n")
@@ -77,8 +80,8 @@ var createCmd = &cobra.Command{
description = tmpl.Description
}
// Warn if creating an issue without a description (unless it's a test issue)
if description == "" && !strings.Contains(strings.ToLower(title), "test") {
// Warn if creating an issue without a description (unless it's a test issue or silent mode)
if description == "" && !strings.Contains(strings.ToLower(title), "test") && !silent && !debug.IsQuiet() {
yellow := color.New(color.FgYellow).SprintFunc()
fmt.Fprintf(os.Stderr, "%s Creating issue without description.\n", yellow("⚠"))
fmt.Fprintf(os.Stderr, " Issues without descriptions lack context for future work.\n")
@@ -249,6 +252,12 @@ var createCmd = &cobra.Command{
if jsonOutput {
fmt.Println(string(resp.Data))
} else if silent {
var issue types.Issue
if err := json.Unmarshal(resp.Data, &issue); err != nil {
FatalError("parsing response: %v", err)
}
fmt.Println(issue.ID)
} else {
var issue types.Issue
if err := json.Unmarshal(resp.Data, &issue); err != nil {
@@ -386,6 +395,8 @@ var createCmd = &cobra.Command{
if jsonOutput {
outputJSON(issue)
} else if silent {
fmt.Println(issue.ID)
} else {
green := color.New(color.FgGreen).SprintFunc()
fmt.Printf("%s Created issue: %s\n", green("✓"), issue.ID)
@@ -403,6 +414,7 @@ func init() {
createCmd.Flags().StringP("file", "f", "", "Create multiple issues from markdown file")
createCmd.Flags().String("from-template", "", "Create issue from template (e.g., 'epic', 'bug', 'feature')")
createCmd.Flags().String("title", "", "Issue title (alternative to positional argument)")
createCmd.Flags().Bool("silent", false, "Output only the issue ID (for scripting)")
registerPriorityFlag(createCmd, "2")
createCmd.Flags().StringP("type", "t", "task", "Issue type (bug|feature|task|epic|chore)")
registerCommonIssueFlags(createCmd)

94
cmd/bd/quick.go Normal file
View File

@@ -0,0 +1,94 @@
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/steveyegge/beads/internal/rpc"
"github.com/steveyegge/beads/internal/types"
"github.com/steveyegge/beads/internal/validation"
)
var quickCmd = &cobra.Command{
Use: "q [title]",
Short: "Quick capture: create issue and output only ID",
Long: `Quick capture creates an issue and outputs only the issue ID.
Designed for scripting and AI agent integration.
Example:
bd q "Fix login bug" # Outputs: bd-a1b2
ISSUE=$(bd q "New feature") # Capture ID in variable
bd q "Task" | xargs bd show # Pipe to other commands`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
CheckReadonly("create")
title := strings.Join(args, " ")
// Get optional flags
priorityStr, _ := cmd.Flags().GetString("priority")
issueType, _ := cmd.Flags().GetString("type")
labels, _ := cmd.Flags().GetStringSlice("labels")
// Parse priority
priority, err := validation.ValidatePriority(priorityStr)
if err != nil {
FatalError("%v", err)
}
// If daemon is running, use RPC
if daemonClient != nil {
createArgs := &rpc.CreateArgs{
Title: title,
Priority: priority,
IssueType: issueType,
Labels: labels,
}
resp, err := daemonClient.Create(createArgs)
if err != nil {
FatalError("%v", err)
}
var issue types.Issue
if err := json.Unmarshal(resp.Data, &issue); err != nil {
FatalError("parsing response: %v", err)
}
fmt.Println(issue.ID)
return
}
// Direct mode
issue := &types.Issue{
Title: title,
Status: types.StatusOpen,
Priority: priority,
IssueType: types.IssueType(issueType),
}
ctx := rootCtx
if err := store.CreateIssue(ctx, issue, actor); err != nil {
FatalError("%v", err)
}
// Add labels if specified (silently ignore failures)
for _, label := range labels {
_ = store.AddLabel(ctx, issue.ID, label, actor)
}
// Schedule auto-flush
markDirtyAndScheduleFlush()
// Output only the ID
fmt.Println(issue.ID)
},
}
func init() {
quickCmd.Flags().StringP("priority", "p", "2", "Priority (0-4 or P0-P4)")
quickCmd.Flags().StringP("type", "t", "task", "Issue type")
quickCmd.Flags().StringSliceP("labels", "l", []string{}, "Labels")
rootCmd.AddCommand(quickCmd)
}