From b21b318588bf7ae195a190681380778f9a6b5dba Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Thu, 25 Dec 2025 18:54:33 -0800 Subject: [PATCH] feat: Improve bd mol distill command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update help text to clarify mol/wisp/epic support (not just epics) - Add daemon support (no longer requires --no-daemon) - Add -o shorthand for --output flag - Update use cases to match new architecture Part of bd-1dez.1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- cmd/bd/mol_distill.go | 62 +++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/cmd/bd/mol_distill.go b/cmd/bd/mol_distill.go index 49e5f834..5d07ccdf 100644 --- a/cmd/bd/mol_distill.go +++ b/cmd/bd/mol_distill.go @@ -15,21 +15,22 @@ import ( ) var molDistillCmd = &cobra.Command{ - Use: "distill [formula-name]", - Short: "Extract a formula from an existing epic", - Long: `Distill a molecule by extracting a reusable formula from an existing epic. + Use: "distill [formula-name]", + Short: "Extract a formula from a mol, wisp, or epic", + Long: `Extract a reusable formula from completed work. -This is the reverse of pour: instead of formula → molecule, it's molecule → formula. +This is the reverse of pour: instead of formula → mol, it's mol → formula. +Works with any hierarchical work: mols, wisps, or plain epics. The distill command: - 1. Loads the existing epic and all its children + 1. Loads the work item and all its children 2. Converts the structure to a .formula.json file 3. Replaces concrete values with {{variable}} placeholders (via --var flags) Use cases: - - Team develops good workflow organically, wants to reuse it - - Capture tribal knowledge as executable templates - - Create starting point for similar future work + - Emergent patterns: structured work manually, want to templatize + - Modified execution: poured formula, added steps, want to capture + - Learning from success: extract what made a workflow succeed Variable syntax (both work - we detect which side is the concrete value): --var branch=feature-auth Spawn-style: variable=value (recommended) @@ -40,8 +41,10 @@ Output locations (first writable wins): 2. ~/.beads/formulas/ (user-level, if project not writable) Examples: - bd mol distill bd-o5xe my-workflow - bd mol distill bd-abc release-workflow --var feature_name=auth-refactor`, + bd mol distill bd-mol-xyz my-workflow + bd mol distill bd-wisp-abc patrol-template + bd mol distill bd-epic-123 release-workflow --var version=1.2.3 + bd mol distill bd-xyz workflow -o ./formulas/`, Args: cobra.RangeArgs(1, 2), Run: runMolDistill, } @@ -102,14 +105,9 @@ func parseDistillVar(varFlag, searchableText string) (string, string, error) { func runMolDistill(cmd *cobra.Command, args []string) { ctx := rootCtx - // mol distill requires direct store access for reading the epic - if store == nil { - if daemonClient != nil { - fmt.Fprintf(os.Stderr, "Error: mol distill requires direct database access\n") - fmt.Fprintf(os.Stderr, "Hint: use --no-daemon flag: bd --no-daemon mol distill %s ...\n", args[0]) - } else { - fmt.Fprintf(os.Stderr, "Error: no database connection\n") - } + // Check we have some database access + if store == nil && daemonClient == nil { + fmt.Fprintf(os.Stderr, "Error: no database connection\n") os.Exit(1) } @@ -117,17 +115,23 @@ func runMolDistill(cmd *cobra.Command, args []string) { dryRun, _ := cmd.Flags().GetBool("dry-run") outputDir, _ := cmd.Flags().GetString("output") - // Resolve epic ID - epicID, err := utils.ResolvePartialID(ctx, store, args[0]) - if err != nil { - fmt.Fprintf(os.Stderr, "Error: '%s' not found\n", args[0]) - os.Exit(1) + // Load the subgraph (works with daemon or direct) + // Show/GetIssue handle partial ID resolution + var subgraph *TemplateSubgraph + var err error + if daemonClient != nil { + subgraph, err = loadTemplateSubgraphViaDaemon(daemonClient, args[0]) + } else { + // Resolve ID for direct access + issueID, resolveErr := utils.ResolvePartialID(ctx, store, args[0]) + if resolveErr != nil { + fmt.Fprintf(os.Stderr, "Error: '%s' not found\n", args[0]) + os.Exit(1) + } + subgraph, err = loadTemplateSubgraph(ctx, store, issueID) } - - // Load the epic subgraph - subgraph, err := loadTemplateSubgraph(ctx, store, epicID) if err != nil { - fmt.Fprintf(os.Stderr, "Error loading epic: %v\n", err) + fmt.Fprintf(os.Stderr, "Error loading issue: %v\n", err) os.Exit(1) } @@ -172,7 +176,7 @@ func runMolDistill(cmd *cobra.Command, args []string) { } if dryRun { - fmt.Printf("\nDry run: would distill %d steps from %s into formula\n\n", countSteps(f.Steps), epicID) + fmt.Printf("\nDry run: would distill %d steps from %s into formula\n\n", countSteps(f.Steps), subgraph.Root.ID) fmt.Printf("Formula: %s\n", formulaName) fmt.Printf("Output: %s\n", outputPath) if len(replacements) > 0 { @@ -365,7 +369,7 @@ func subgraphToFormula(subgraph *TemplateSubgraph, name string, replacements map func init() { molDistillCmd.Flags().StringSlice("var", []string{}, "Replace value with {{variable}} placeholder (variable=value)") molDistillCmd.Flags().Bool("dry-run", false, "Preview what would be created") - molDistillCmd.Flags().String("output", "", "Output directory for formula file") + molDistillCmd.Flags().StringP("output", "o", "", "Output directory for formula file") molCmd.AddCommand(molDistillCmd) }