refactor(mol): remove molecule.go (superseded by mol.go)
The simple single-issue instantiation in molecule.go is superseded by the full DAG-based mol commands. Use 'bd mol bond' for all molecule instantiation - it handles both single-issue and multi-issue molecules. Closes: bd-8b0x Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,353 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/beads/internal/hooks"
|
||||
"github.com/steveyegge/beads/internal/rpc"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
"github.com/steveyegge/beads/internal/ui"
|
||||
)
|
||||
|
||||
var moleculeCmd = &cobra.Command{
|
||||
Use: "molecule",
|
||||
Short: "Manage template molecules",
|
||||
Long: `Manage template molecules for issue instantiation.
|
||||
|
||||
Molecules are template issues that can be instantiated to create work items.
|
||||
They are stored in molecules.jsonl and marked with is_template=true.
|
||||
|
||||
Examples:
|
||||
bd molecule list # List all available molecules
|
||||
bd molecule show mol-123 # Show details of a molecule
|
||||
`,
|
||||
}
|
||||
|
||||
var moleculeListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List available molecules",
|
||||
Long: `List all available template molecules.
|
||||
|
||||
Templates are read-only issues that can be instantiated to create work items.
|
||||
Use --all to include closed molecules.
|
||||
|
||||
Examples:
|
||||
bd molecule list # List open molecules
|
||||
bd molecule list --all # List all molecules including closed
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
showAll, _ := cmd.Flags().GetBool("all")
|
||||
|
||||
ctx := rootCtx
|
||||
if daemonClient == nil {
|
||||
if err := ensureDatabaseFresh(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Build filter for template molecules
|
||||
isTemplate := true
|
||||
filter := types.IssueFilter{
|
||||
IsTemplate: &isTemplate,
|
||||
}
|
||||
|
||||
if !showAll {
|
||||
// Default to non-closed
|
||||
status := types.StatusOpen
|
||||
filter.Status = &status
|
||||
}
|
||||
|
||||
// Direct mode only for now
|
||||
if store == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: database not available\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
issues, err := store.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
fmt.Println("No molecules found")
|
||||
return
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
outputJSON(issues)
|
||||
return
|
||||
}
|
||||
|
||||
// Print molecule list
|
||||
for _, issue := range issues {
|
||||
priorityStr := fmt.Sprintf("P%d", issue.Priority)
|
||||
fmt.Printf("%s [%s] %s\n", issue.ID, priorityStr, issue.Title)
|
||||
}
|
||||
fmt.Printf("\n%d molecule(s)\n", len(issues))
|
||||
},
|
||||
}
|
||||
|
||||
var moleculeShowCmd = &cobra.Command{
|
||||
Use: "show <molecule-id>",
|
||||
Short: "Show molecule details",
|
||||
Long: `Show detailed information about a template molecule.
|
||||
|
||||
Examples:
|
||||
bd molecule show mol-123
|
||||
`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
moleculeID := args[0]
|
||||
|
||||
ctx := rootCtx
|
||||
if daemonClient == nil {
|
||||
if err := ensureDatabaseFresh(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if store == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: database not available\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
issue, err := store.GetIssue(ctx, moleculeID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if issue == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: molecule %s not found\n", moleculeID)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !issue.IsTemplate {
|
||||
fmt.Fprintf(os.Stderr, "Warning: %s is not a template molecule\n", moleculeID)
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
outputJSON(issue)
|
||||
return
|
||||
}
|
||||
|
||||
// Print molecule details
|
||||
fmt.Printf("%s: %s\n", issue.ID, issue.Title)
|
||||
fmt.Printf("Type: %s\n", issue.IssueType)
|
||||
fmt.Printf("Priority: P%d\n", issue.Priority)
|
||||
fmt.Printf("Status: %s\n", issue.Status)
|
||||
fmt.Printf("Template: %v\n", issue.IsTemplate)
|
||||
|
||||
if issue.Description != "" {
|
||||
fmt.Printf("\nDescription:\n%s\n", issue.Description)
|
||||
}
|
||||
|
||||
if issue.Design != "" {
|
||||
fmt.Printf("\nDesign:\n%s\n", issue.Design)
|
||||
}
|
||||
|
||||
if issue.AcceptanceCriteria != "" {
|
||||
fmt.Printf("\nAcceptance Criteria:\n%s\n", issue.AcceptanceCriteria)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var moleculeInstantiateCmd = &cobra.Command{
|
||||
Use: "instantiate <molecule-id>",
|
||||
Short: "Create a work item from a template molecule",
|
||||
Long: `Create a new work item based on a template molecule.
|
||||
|
||||
The new issue will inherit the template's title, description, design,
|
||||
acceptance criteria, priority, and issue type. The new issue will have
|
||||
is_template=false and will be linked to the template via a discovered-from
|
||||
dependency.
|
||||
|
||||
Examples:
|
||||
bd molecule instantiate mol-123 # Create work item from template
|
||||
bd molecule instantiate mol-123 --title "Custom title" # Override title
|
||||
`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
CheckReadonly("instantiate")
|
||||
moleculeID := args[0]
|
||||
|
||||
// Get flag overrides
|
||||
titleOverride, _ := cmd.Flags().GetString("title")
|
||||
assignee, _ := cmd.Flags().GetString("assignee")
|
||||
|
||||
ctx := rootCtx
|
||||
if daemonClient == nil {
|
||||
if err := ensureDatabaseFresh(ctx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if store == nil && daemonClient == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: database not available\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the template molecule
|
||||
var template *types.Issue
|
||||
var err error
|
||||
|
||||
if daemonClient != nil {
|
||||
showArgs := &rpc.ShowArgs{ID: moleculeID}
|
||||
resp, showErr := daemonClient.Show(showArgs)
|
||||
if showErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", showErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
template = &types.Issue{}
|
||||
if jsonErr := json.Unmarshal(resp.Data, template); jsonErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing response: %v\n", jsonErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
template, err = store.GetIssue(ctx, moleculeID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if template == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: molecule %s not found\n", moleculeID)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !template.IsTemplate {
|
||||
fmt.Fprintf(os.Stderr, "Warning: %s is not a template molecule (is_template=false)\n", moleculeID)
|
||||
}
|
||||
|
||||
// Build title (use override or template title)
|
||||
title := template.Title
|
||||
if titleOverride != "" {
|
||||
title = titleOverride
|
||||
}
|
||||
|
||||
// Create the new work item
|
||||
if daemonClient != nil {
|
||||
// Daemon mode
|
||||
createArgs := &rpc.CreateArgs{
|
||||
Title: title,
|
||||
Description: template.Description,
|
||||
IssueType: string(template.IssueType),
|
||||
Priority: template.Priority,
|
||||
Design: template.Design,
|
||||
AcceptanceCriteria: template.AcceptanceCriteria,
|
||||
Assignee: assignee,
|
||||
Dependencies: []string{"discovered-from:" + moleculeID},
|
||||
}
|
||||
|
||||
resp, createErr := daemonClient.Create(createArgs)
|
||||
if createErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", createErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var issue types.Issue
|
||||
if jsonErr := json.Unmarshal(resp.Data, &issue); jsonErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing response: %v\n", jsonErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Run create hook
|
||||
if hookRunner != nil {
|
||||
hookRunner.Run(hooks.EventCreate, &issue)
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
outputJSON(&issue)
|
||||
} else {
|
||||
fmt.Printf("%s Created work item: %s (from template %s)\n", ui.RenderPass("✓"), issue.ID, moleculeID)
|
||||
fmt.Printf(" Title: %s\n", issue.Title)
|
||||
fmt.Printf(" Priority: P%d\n", issue.Priority)
|
||||
fmt.Printf(" Status: %s\n", issue.Status)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Direct mode
|
||||
now := time.Now()
|
||||
issue := &types.Issue{
|
||||
Title: title,
|
||||
Description: template.Description,
|
||||
Design: template.Design,
|
||||
AcceptanceCriteria: template.AcceptanceCriteria,
|
||||
Status: types.StatusOpen,
|
||||
Priority: template.Priority,
|
||||
IssueType: template.IssueType,
|
||||
Assignee: assignee,
|
||||
IsTemplate: false, // Work items are not templates
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err := store.CreateIssue(ctx, issue, actor); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating issue: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the created issue (ID is set by CreateIssue)
|
||||
createdIssue, err := store.GetIssue(ctx, issue.ID)
|
||||
if err != nil || createdIssue == nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting created issue: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Add discovered-from dependency to template
|
||||
dep := &types.Dependency{
|
||||
IssueID: createdIssue.ID,
|
||||
DependsOnID: moleculeID,
|
||||
Type: types.DepDiscoveredFrom,
|
||||
CreatedAt: now,
|
||||
CreatedBy: actor,
|
||||
}
|
||||
if err := store.AddDependency(ctx, dep, actor); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to add dependency to template: %v\n", err)
|
||||
}
|
||||
|
||||
// Run create hook
|
||||
if hookRunner != nil {
|
||||
hookRunner.Run(hooks.EventCreate, createdIssue)
|
||||
}
|
||||
|
||||
// Schedule auto-flush
|
||||
markDirtyAndScheduleFlush()
|
||||
|
||||
if jsonOutput {
|
||||
outputJSON(createdIssue)
|
||||
} else {
|
||||
fmt.Printf("%s Created work item: %s (from template %s)\n", ui.RenderPass("✓"), createdIssue.ID, moleculeID)
|
||||
fmt.Printf(" Title: %s\n", createdIssue.Title)
|
||||
fmt.Printf(" Priority: P%d\n", createdIssue.Priority)
|
||||
fmt.Printf(" Status: %s\n", createdIssue.Status)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Add subcommands
|
||||
moleculeCmd.AddCommand(moleculeListCmd)
|
||||
moleculeCmd.AddCommand(moleculeShowCmd)
|
||||
moleculeCmd.AddCommand(moleculeInstantiateCmd)
|
||||
|
||||
// Flags for list command
|
||||
moleculeListCmd.Flags().Bool("all", false, "Include closed molecules")
|
||||
|
||||
// Flags for instantiate command
|
||||
moleculeInstantiateCmd.Flags().String("title", "", "Override the template title")
|
||||
moleculeInstantiateCmd.Flags().StringP("assignee", "a", "", "Assign the new work item")
|
||||
|
||||
// Add molecule command to root
|
||||
rootCmd.AddCommand(moleculeCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user