feat: Improve bd mol distill command
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -15,21 +15,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var molDistillCmd = &cobra.Command{
|
var molDistillCmd = &cobra.Command{
|
||||||
Use: "distill <epic-id> [formula-name]",
|
Use: "distill <id> [formula-name]",
|
||||||
Short: "Extract a formula from an existing epic",
|
Short: "Extract a formula from a mol, wisp, or epic",
|
||||||
Long: `Distill a molecule by extracting a reusable formula from an existing 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:
|
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
|
2. Converts the structure to a .formula.json file
|
||||||
3. Replaces concrete values with {{variable}} placeholders (via --var flags)
|
3. Replaces concrete values with {{variable}} placeholders (via --var flags)
|
||||||
|
|
||||||
Use cases:
|
Use cases:
|
||||||
- Team develops good workflow organically, wants to reuse it
|
- Emergent patterns: structured work manually, want to templatize
|
||||||
- Capture tribal knowledge as executable templates
|
- Modified execution: poured formula, added steps, want to capture
|
||||||
- Create starting point for similar future work
|
- Learning from success: extract what made a workflow succeed
|
||||||
|
|
||||||
Variable syntax (both work - we detect which side is the concrete value):
|
Variable syntax (both work - we detect which side is the concrete value):
|
||||||
--var branch=feature-auth Spawn-style: variable=value (recommended)
|
--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)
|
2. ~/.beads/formulas/ (user-level, if project not writable)
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
bd mol distill bd-o5xe my-workflow
|
bd mol distill bd-mol-xyz my-workflow
|
||||||
bd mol distill bd-abc release-workflow --var feature_name=auth-refactor`,
|
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),
|
Args: cobra.RangeArgs(1, 2),
|
||||||
Run: runMolDistill,
|
Run: runMolDistill,
|
||||||
}
|
}
|
||||||
@@ -102,14 +105,9 @@ func parseDistillVar(varFlag, searchableText string) (string, string, error) {
|
|||||||
func runMolDistill(cmd *cobra.Command, args []string) {
|
func runMolDistill(cmd *cobra.Command, args []string) {
|
||||||
ctx := rootCtx
|
ctx := rootCtx
|
||||||
|
|
||||||
// mol distill requires direct store access for reading the epic
|
// Check we have some database access
|
||||||
if store == nil {
|
if store == nil && daemonClient == nil {
|
||||||
if daemonClient != nil {
|
fmt.Fprintf(os.Stderr, "Error: no database connection\n")
|
||||||
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")
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,17 +115,23 @@ func runMolDistill(cmd *cobra.Command, args []string) {
|
|||||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||||
outputDir, _ := cmd.Flags().GetString("output")
|
outputDir, _ := cmd.Flags().GetString("output")
|
||||||
|
|
||||||
// Resolve epic ID
|
// Load the subgraph (works with daemon or direct)
|
||||||
epicID, err := utils.ResolvePartialID(ctx, store, args[0])
|
// Show/GetIssue handle partial ID resolution
|
||||||
if err != nil {
|
var subgraph *TemplateSubgraph
|
||||||
fmt.Fprintf(os.Stderr, "Error: '%s' not found\n", args[0])
|
var err error
|
||||||
os.Exit(1)
|
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 {
|
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +176,7 @@ func runMolDistill(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if dryRun {
|
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("Formula: %s\n", formulaName)
|
||||||
fmt.Printf("Output: %s\n", outputPath)
|
fmt.Printf("Output: %s\n", outputPath)
|
||||||
if len(replacements) > 0 {
|
if len(replacements) > 0 {
|
||||||
@@ -365,7 +369,7 @@ func subgraphToFormula(subgraph *TemplateSubgraph, name string, replacements map
|
|||||||
func init() {
|
func init() {
|
||||||
molDistillCmd.Flags().StringSlice("var", []string{}, "Replace value with {{variable}} placeholder (variable=value)")
|
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().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)
|
molCmd.AddCommand(molDistillCmd)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user