feat(create): add --dry-run flag to preview issue creation (bd-0hi7)

Adds --dry-run flag that shows what would be created without actually
creating the issue. Useful for scripting and validation.

- Shows preview in human-readable format by default
- Outputs full issue JSON with --json flag (empty ID, not placeholder string)
- Skips all side effects (hooks, labels, dependencies)
- Works with --rig/--prefix flags (shows target rig)
- Errors gracefully with --file flag (not supported)
- Includes event fields when --type=event
This commit is contained in:
beads/crew/emma
2026-01-07 01:04:58 -08:00
committed by Steve Yegge
parent 81a2af9598
commit 744d2a7d22

View File

@@ -36,6 +36,11 @@ var createCmd = &cobra.Command{
if len(args) > 0 {
FatalError("cannot specify both title and --file flag")
}
// --dry-run not supported with --file (would need to parse and preview multiple issues)
dryRun, _ := cmd.Flags().GetBool("dry-run")
if dryRun {
FatalError("--dry-run is not supported with --file flag")
}
createIssuesFromMarkdown(cmd, file)
return
}
@@ -173,6 +178,81 @@ var createCmd = &cobra.Command{
deferUntil = &t
}
// Handle --dry-run flag (before --rig to ensure it works with cross-rig creation)
dryRun, _ := cmd.Flags().GetBool("dry-run")
if dryRun {
// Build preview issue
var externalRefPtr *string
if externalRef != "" {
externalRefPtr = &externalRef
}
previewIssue := &types.Issue{
Title: title,
Description: description,
Design: design,
AcceptanceCriteria: acceptance,
Notes: notes,
Status: types.StatusOpen,
Priority: priority,
IssueType: types.IssueType(issueType),
Assignee: assignee,
ExternalRef: externalRefPtr,
Ephemeral: wisp,
CreatedBy: getActorWithGit(),
MolType: molType,
RoleType: roleType,
Rig: agentRig,
DueAt: dueAt,
DeferUntil: deferUntil,
// Event fields
EventKind: eventCategory,
Actor: eventActor,
Target: eventTarget,
Payload: eventPayload,
}
if explicitID != "" {
previewIssue.ID = explicitID
}
if jsonOutput {
outputJSON(previewIssue)
} else {
idDisplay := previewIssue.ID
if idDisplay == "" {
idDisplay = "(will be generated)"
}
fmt.Printf("%s [DRY RUN] Would create issue:\n", ui.RenderWarn("⚠"))
fmt.Printf(" ID: %s\n", idDisplay)
fmt.Printf(" Title: %s\n", previewIssue.Title)
fmt.Printf(" Type: %s\n", previewIssue.IssueType)
fmt.Printf(" Priority: P%d\n", previewIssue.Priority)
fmt.Printf(" Status: %s\n", previewIssue.Status)
if previewIssue.Assignee != "" {
fmt.Printf(" Assignee: %s\n", previewIssue.Assignee)
}
if previewIssue.Description != "" {
fmt.Printf(" Description: %s\n", previewIssue.Description)
}
if len(labels) > 0 {
fmt.Printf(" Labels: %s\n", strings.Join(labels, ", "))
}
if len(deps) > 0 {
fmt.Printf(" Dependencies: %s\n", strings.Join(deps, ", "))
}
if rigOverride != "" || prefixOverride != "" {
rig := rigOverride
if rig == "" {
rig = prefixOverride
}
fmt.Printf(" Target rig: %s\n", rig)
}
if eventCategory != "" {
fmt.Printf(" Event category: %s\n", eventCategory)
}
}
return
}
// Handle --rig or --prefix flag: create issue in a different rig
// Both flags use the same forgiving lookup (accepts rig names or prefixes)
targetRig := rigOverride
@@ -602,6 +682,7 @@ func init() {
createCmd.Flags().StringP("file", "f", "", "Create multiple issues from markdown file")
createCmd.Flags().String("title", "", "Issue title (alternative to positional argument)")
createCmd.Flags().Bool("silent", false, "Output only the issue ID (for scripting)")
createCmd.Flags().Bool("dry-run", false, "Preview what would be created without actually creating")
registerPriorityFlag(createCmd, "2")
createCmd.Flags().StringP("type", "t", "task", "Issue type (bug|feature|task|epic|chore|merge-request|molecule|gate|agent|role|rig|convoy|event)")
registerCommonIssueFlags(createCmd)