From ca678ca5ba2614e692181aaea610b0e2ed3d8a61 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Sun, 21 Dec 2025 00:01:53 -0800 Subject: [PATCH] feat(mol): add bd mol commands, deprecate bd template Adds the mol command group for molecule workflows: - bd mol catalog - list available molecules - bd mol show - show molecule structure and variables - bd mol bond --var k=v - instantiate a molecule Molecules are templates with workflow semantics - they are the orchestration primitive for Gas Town. This unifies the template system under the mol command. The template commands are deprecated but still work: - bd template list -> use bd mol catalog - bd template show -> use bd mol show - bd template instantiate -> use bd mol bond Closes: bd-ffjt Co-Authored-By: Claude Opus 4.5 --- .beads/issues.jsonl | 6 +- cmd/bd/mol.go | 319 ++++++++++++++++++++++++++++++++++++++++++++ cmd/bd/template.go | 24 ++-- 3 files changed, 338 insertions(+), 11 deletions(-) create mode 100644 cmd/bd/mol.go diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index e212dacb..0661ac70 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -9,7 +9,7 @@ {"id":"bd-0w5","title":"Fix update-hooks verification in version-bump.yaml","description":"The update-hooks task verification command at version-bump.yaml:358 always succeeds due to '|| echo ...' fallback. Remove the fallback so verification actually fails when hooks aren't installed.","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-12-17T22:23:06.55467-08:00","updated_at":"2025-12-17T22:34:07.290409-08:00","closed_at":"2025-12-17T22:34:07.290409-08:00"} {"id":"bd-0zp7","title":"Add missing hook calls in mail reply and ack","description":"The mail commands are missing hook calls:\n\n1. runMailReply (mail.go:525-672) creates a message but doesn't call hookRunner.Run(hooks.EventMessage, ...) after creating the reply in direct mode (around line 640)\n\n2. runMailAck (mail.go:432-523) closes messages but doesn't call hookRunner.Run(hooks.EventClose, ...) after closing each message (around line 487 for daemon mode, 493 for direct mode)\n\nThis means GGT hooks won't fire for replies or message acknowledgments.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-16T20:52:53.069412-08:00","updated_at":"2025-12-17T23:13:40.532054-08:00","closed_at":"2025-12-17T17:22:59.368024-08:00"} {"id":"bd-14ie","title":"Work on beads-2vn: Add simple built-in beads viewer (GH#6...","description":"Work on beads-2vn: Add simple built-in beads viewer (GH#654). Add bd list --pretty with --watch flag, tree view with priority/status symbols. When done, submit MR (not PR) to integration branch for Refinery.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:56:47.305831-08:00","updated_at":"2025-12-19T23:28:32.429492-08:00","closed_at":"2025-12-19T23:23:13.928323-08:00","close_reason":"Implemented --pretty flag with tree view and symbols. Tests pass."} -{"id":"bd-1slh","title":"Investigate charmbracelet-based TUI for beads","description":"Now that we've merged the create-form command (PR #603) which uses charmbracelet/huh, investigate whether beads should have a more comprehensive TUI.\n\nConsiderations:\n- Should this be in core or a separate binary (bd-tui)?\n- What functionality would benefit from a TUI? (list view, issue details, search, bulk operations)\n- Plugin/extension architecture vs build tags vs separate binary\n- Dependency cost vs user experience tradeoff\n- Target audience: humans who want interactive workflows vs CLI/scripting users\n\nRelated: PR #603 added charmbracelet/huh dependency for create-form command.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-17T14:20:51.503563-08:00","updated_at":"2025-12-17T14:20:51.503563-08:00"} +{"id":"bd-1slh","title":"Investigate charmbracelet-based TUI for beads","description":"Now that we've merged the create-form command (PR #603) which uses charmbracelet/huh, investigate whether beads should have a more comprehensive TUI.\n\nConsiderations:\n- Should this be in core or a separate binary (bd-tui)?\n- What functionality would benefit from a TUI? (list view, issue details, search, bulk operations)\n- Plugin/extension architecture vs build tags vs separate binary\n- Dependency cost vs user experience tradeoff\n- Target audience: humans who want interactive workflows vs CLI/scripting users\n\nRelated: PR #603 added charmbracelet/huh dependency for create-form command.","notes":"Foundation is in place (lipgloss, huh), but not a priority right now","status":"deferred","priority":3,"issue_type":"feature","created_at":"2025-12-17T14:20:51.503563-08:00","updated_at":"2025-12-20T23:31:34.354023-08:00"} {"id":"bd-1tw","title":"Fix G104 errors unhandled in internal/storage/sqlite/queries.go:1186","description":"Linting issue: G104: Errors unhandled (gosec) at internal/storage/sqlite/queries.go:1186:2. Error: rows.Close()","status":"closed","issue_type":"bug","created_at":"2025-12-07T15:35:13.051671889-07:00","updated_at":"2025-12-17T23:13:40.53486-08:00","closed_at":"2025-12-17T16:46:11.0289-08:00"} {"id":"bd-20j","title":"sync branch not match config","description":"./bd sync\nโ†’ Exporting pending changes to JSONL...\nโ†’ No changes to commit\nโ†’ Pulling from sync branch 'gh-386'...\nError pulling from sync branch: failed to create worktree: failed to create worktree parent directory: mkdir /var/home/matt/dev/beads/worktree-db-fail/.git: not a directory\nmatt@blufin-framation ~/d/b/worktree-db-fail (worktree-db-fail) [1]\u003e bd config list\n\nConfiguration:\n auto_compact_enabled = false\n compact_batch_size = 50\n compact_model = claude-3-5-haiku-20241022\n compact_parallel_workers = 5\n compact_tier1_days = 30\n compact_tier1_dep_levels = 2\n compact_tier2_commits = 100\n compact_tier2_days = 90\n compact_tier2_dep_levels = 5\n compaction_enabled = false\n issue_prefix = worktree-db-fail\n sync.branch = worktree-db-fail","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-08T06:49:04.449094018-07:00","updated_at":"2025-12-08T06:49:04.449094018-07:00"} {"id":"bd-28db","title":"Add 'bd status' command for issue database overview","description":"Implement a bd status command that provides a quick snapshot of the issue database state, similar to how git status shows working tree state.\n\nExpected output: Show summary including counts by state (open, in-progress, blocked, closed), recent activity (last 7 days), and quick overview without needing multiple queries.\n\nExample output showing issue counts, recent activity stats, and pointer to bd list for details.\n\nProposed options: --all (show all issues), --assigned (show issues assigned to current user), --json (JSON format output)\n\nUse cases: Quick project health check, onboarding for new contributors, integration with shell prompts or CI/CD, daily standup reference","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-02T17:25:59.203549-08:00","updated_at":"2025-11-02T17:25:59.203549-08:00"} @@ -64,6 +64,7 @@ {"id":"bd-7tuu","title":"Commit and push release","description":"git add -A \u0026\u0026 git commit \u0026\u0026 git push to trigger CI","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:02.053382-08:00","updated_at":"2025-12-20T01:23:52.484043-08:00","closed_at":"2025-12-20T01:23:52.484043-08:00","close_reason":"Superseded by 0.30.7 release - already committed and pushed","dependencies":[{"issue_id":"bd-7tuu","depends_on_id":"bd-6s61","type":"parent-child","created_at":"2025-12-19T22:56:15.021087-08:00","created_by":"daemon"},{"issue_id":"bd-7tuu","depends_on_id":"bd-hw3w","type":"blocks","created_at":"2025-12-19T22:56:23.291591-08:00","created_by":"daemon"}]} {"id":"bd-7yg","title":"Git merge driver uses invalid placeholders (%L, %R instead of %A, %B)","description":"## Problem\n\nThe beads git merge driver is configured with invalid Git placeholders:\n\n```\ngit config merge.beads.driver \"bd merge %A %O %L %R\"\n```\n\nGit doesn't recognize `%L` or `%R` as valid merge driver placeholders. The valid placeholders are:\n- `%O` = base (common ancestor)\n- `%A` = current version (ours)\n- `%B` = other version (theirs)\n\n## Impact\n\n- Affects ALL users when they have `.beads/beads.jsonl` merge conflicts\n- Automatic JSONL merge fails with error: \"error reading left file: failed to open file: open 7: no such file or directory\"\n- Users must manually resolve conflicts instead of getting automatic merge\n\n## Root Cause\n\nThe `bd init` command (or wherever the merge driver is configured) is using non-standard placeholders. When Git encounters `%L` and `%R`, it either passes them literally or interprets them incorrectly.\n\n## Fix\n\nUpdate the merge driver configuration to:\n```\ngit config merge.beads.driver \"bd merge %A %O %A %B\"\n```\n\nWhere:\n- 1st `%A` = output file (current file, will be overwritten)\n- `%O` = base (common ancestor)\n- 2nd `%A` = left/current version\n- `%B` = right/other version\n\n## Action Items\n\n1. Fix `bd init` (or equivalent setup command) to use correct placeholders\n2. Add migration/warning for existing users with misconfigured merge driver\n3. Update documentation with correct merge driver setup\n4. Consider adding validation when `bd init` is run","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-21T19:51:55.747608-05:00","updated_at":"2025-12-17T23:13:40.532368-08:00","closed_at":"2025-12-17T17:24:52.678668-08:00"} {"id":"bd-89f89fc0","title":"Remove unreachable RPC methods","description":"Several RPC server and client methods are unreachable and should be removed:\n\nServer methods (internal/rpc/server.go):\n- `Server.GetLastImportTime` (line 2116)\n- `Server.SetLastImportTime` (line 2123)\n- `Server.findJSONLPath` (line 2255)\n\nClient methods (internal/rpc/client.go):\n- `Client.Import` (line 311) - RPC import not used (daemon uses autoimport)\n\nEvidence:\n```bash\ngo run golang.org/x/tools/cmd/deadcode@latest -test ./...\n```\n\nImpact: Removes ~80 LOC of unused RPC code","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.432202-07:00","updated_at":"2025-12-17T22:58:34.564401-08:00","closed_at":"2025-12-17T22:58:34.564401-08:00","close_reason":"Closed"} +{"id":"bd-8b0x","title":"Remove molecule.go (simple instantiation)","description":"molecule.go uses is_template field for simple single-issue cloning. This is too simple for what molecules should be - full DAG orchestration. The use case is covered by bd mol bond with a single-issue molecule. Delete molecule.go and its commands.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T23:52:15.041776-08:00","updated_at":"2025-12-20T23:52:15.041776-08:00","dependencies":[{"issue_id":"bd-8b0x","depends_on_id":"bd-ffjt","type":"blocks","created_at":"2025-12-20T23:52:25.807967-08:00","created_by":"daemon"}]} {"id":"bd-8e0q","title":"Merge: beads-ocs","description":"branch: polecat/valkyrie\ntarget: main\nsource_issue: beads-ocs\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-19T23:24:45.281478-08:00","updated_at":"2025-12-20T23:17:26.995706-08:00","closed_at":"2025-12-20T23:17:26.995706-08:00","close_reason":"Branches nuked, MRs obsolete"} {"id":"bd-8fgn","title":"test hash length","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T13:49:32.113843-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} {"id":"bd-8g8","title":"Fix G304 potential file inclusion in cmd/bd/tips.go:259","description":"Linting issue: G304: Potential file inclusion via variable (gosec) at cmd/bd/tips.go:259:18. Error: if data, err := os.ReadFile(settingsPath); err == nil {","status":"closed","issue_type":"bug","created_at":"2025-12-07T15:34:57.189730843-07:00","updated_at":"2025-12-17T23:13:40.534569-08:00","closed_at":"2025-12-17T16:46:11.029837-08:00"} @@ -82,6 +83,7 @@ {"id":"bd-abjw","title":"Consider consolidating config.yaml parsing into shared utility","description":"Multiple places parse config.yaml with custom structs:\n\n1. **autoimport.go:148** - `localConfig{SyncBranch}`\n2. **main.go:310** - strings.Contains for no-db (fragile, see bd-r6k2)\n3. **doctor.go:863** - strings.Contains for no-db (fragile, see bd-r6k2)\n4. **internal/config/config.go** - Uses viper (but caches at startup, problematic for tests)\n\nConsider creating a shared utility in `internal/configfile/` or extending the viper config:\n\n```go\n// internal/configfile/yaml.go\ntype YAMLConfig struct {\n SyncBranch string `yaml:\"sync-branch\"`\n NoDb bool `yaml:\"no-db\"`\n IssuePrefix string `yaml:\"issue-prefix\"`\n Author string `yaml:\"author\"`\n}\n\nfunc LoadYAML(beadsDir string) (*YAMLConfig, error) {\n // Parse config.yaml with proper YAML library\n}\n```\n\nBenefits:\n- Single source of truth for config.yaml structure\n- Proper YAML parsing everywhere\n- Easier to add new config fields\n\nTrade-off: May add complexity for simple one-off reads.","status":"open","priority":4,"issue_type":"task","created_at":"2025-12-07T02:03:26.067311-08:00","updated_at":"2025-12-07T02:03:26.067311-08:00"} {"id":"bd-adoe","title":"Add --hard flag to bd cleanup to permanently cull tombstones before cutoff date","description":"Currently tombstones persist for 30 days before cleanup prunes them. Need an official way to force-cull tombstones earlier than the default TTL, for scenarios like cleaning house after extended absence where resurrection from old clones is not a concern. Proposed: bd cleanup --hard --older-than N to bypass the 30-day tombstone TTL.","status":"tombstone","priority":2,"issue_type":"feature","created_at":"2025-12-16T01:17:31.064914-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"feature"} {"id":"bd-aec5439f","title":"Update LINTING.md with current baseline","description":"After cleanup, document the remaining acceptable baseline in LINTING.md so we can track regression.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-27T18:53:10.38679-07:00","updated_at":"2025-12-17T22:58:34.564854-08:00","closed_at":"2025-12-17T22:58:34.564854-08:00","close_reason":"Closed"} +{"id":"bd-akcq","title":"Design molecule step hooks","description":"Hooks that fire between molecule steps. When a bead in a molecule closes, trigger hook that can spawn agent attention to prompts/requests. This enables reactive orchestration - the molecule drives, hooks respond. Gas Town feature built on Beads data plane.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T23:52:18.63487-08:00","updated_at":"2025-12-20T23:52:18.63487-08:00","dependencies":[{"issue_id":"bd-akcq","depends_on_id":"bd-icnf","type":"blocks","created_at":"2025-12-20T23:52:25.935274-08:00","created_by":"daemon"}]} {"id":"bd-an4s","title":"Version Bump: 0.32.1","description":"Release checklist for version 0.32.1. Patch release with MCP output control params and pin field fix.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-20T21:53:01.315592-08:00","updated_at":"2025-12-20T21:57:13.909864-08:00","closed_at":"2025-12-20T21:57:13.909864-08:00","close_reason":"Version 0.32.1 released"} {"id":"bd-ao0s","title":"bd graph crashes with --no-daemon on closed issues","description":"The `bd graph` command panics with nil pointer dereference when using `--no-daemon` flag on an issue with closed children.\n\n**Reproduction:**\n```bash\nbd graph bd-qqc --no-daemon\n# panic: runtime error: invalid memory address or nil pointer dereference\n# in main.computeDependencyCounts\n```\n\n**Stack trace:**\n```\npanic: runtime error: invalid memory address or nil pointer dereference\n[signal SIGSEGV: segmentation violation code=0x2 addr=0x20 pc=0x1010bdfb0]\n\ngoroutine 1 [running]:\nmain.computeDependencyCounts(...)\n /Users/stevey/gt/beads/crew/emma/cmd/bd/graph.go:428\nmain.renderGraph(0x1400033bb80, 0x0)\n /Users/stevey/gt/beads/crew/emma/cmd/bd/graph.go:307 +0x300\n```\n\n**Location:** cmd/bd/graph.go:428 - computeDependencyCounts() not handling nil case","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-18T22:57:36.972585-08:00","updated_at":"2025-12-20T01:13:29.206821-08:00","closed_at":"2025-12-20T01:13:29.206821-08:00","close_reason":"Fixed: pass subgraph instead of nil to renderGraph"} {"id":"bd-au0","title":"Command Set Standardization \u0026 Flag Consistency","description":"Comprehensive improvements to bd command set based on 2025 audit findings.\n\n## Background\nSee docs/command-audit-2025.md for detailed analysis.\n\n## Goals\n1. Standardize flag naming and behavior across all commands\n2. Add missing flags for feature parity\n3. Fix naming confusion\n4. Improve consistency in JSON output\n\n## Success Criteria\n- All mutating commands support --dry-run (no --preview variants)\n- bd update supports label operations\n- bd search has filter parity with bd list\n- Priority flags accept both int and P0-P4 format everywhere\n- JSON output is consistent across all commands","status":"open","priority":2,"issue_type":"epic","created_at":"2025-11-21T21:05:55.672749-05:00","updated_at":"2025-11-21T21:05:55.672749-05:00"} @@ -146,6 +148,7 @@ {"id":"bd-f3ll","title":"Merge: bd-ot0w","description":"branch: polecat/dementus\ntarget: main\nsource_issue: bd-ot0w\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-19T23:20:33.495772-08:00","updated_at":"2025-12-20T23:17:27.000252-08:00","closed_at":"2025-12-20T23:17:27.000252-08:00","close_reason":"Branches nuked, MRs obsolete"} {"id":"bd-f5cc","title":"Thread Test","description":"Testing the thread feature","status":"tombstone","priority":2,"issue_type":"message","created_at":"2025-12-16T18:21:01.244501-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","dependencies":[{"issue_id":"bd-f5cc","depends_on_id":"bd-x36g","type":"supersedes","created_at":"2025-12-18T13:45:31.137191-08:00","created_by":"migration"}],"deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"message"} {"id":"bd-fa2h","title":"๐Ÿค HANDOFF: v0.31.0 released, molecules discussion","description":"Session completed 0.31.0 release and had important molecules discussion.\n\n## Completed\n- v0.31.0 released (deferred status, audit trail, directory labels, etc.)\n- Fixed lint issues, hook version markers, codesigning\n- All CI green, artifacts verified\n\n## Filed Issues\n- bd-usro: Rename template instantiate โ†’ bd mol bond\n- bd-y8bj: Auto-detect identity for bd mail (P1 bug)\n- gt-975: Molecule execution support for polecats/crew\n- gt-976: Crew lifecycle support in Deacon\n\n## Key Insight\nMolecules are the future - TodoWrite is ephemeral, molecules are persistent institutional memory on the world chain. I tried to use TodoWrite for version bump and missed steps (codesigning, MCP verification). Molecules would have caught this.\n\n## Next Steps\n- bd mol bond implementation is priority\n- Max has gt-976 for crew lifecycle (enables automated refresh mid-molecule)\n\nCheck bd ready and gt-975/976 status.","status":"open","priority":2,"issue_type":"message","assignee":"beads/crew/dave","created_at":"2025-12-20T17:23:09.889562-08:00","updated_at":"2025-12-20T17:23:09.889562-08:00","sender":"Steve Yegge","ephemeral":true} +{"id":"bd-ffjt","title":"Unify template.go and mol.go under bd mol","description":"Consolidate the two DAG-template systems into one under the mol command. mol.go (on rictus branch) has the right UX (catalog/show/bond), template.go has the mechanics. Merge them, deprecate bd template commands.","status":"in_progress","priority":1,"issue_type":"task","created_at":"2025-12-20T23:52:13.208972-08:00","updated_at":"2025-12-20T23:55:11.522661-08:00"} {"id":"bd-fgw3","title":"Update local installation","description":"Run install script or brew upgrade to get new version locally: curl -fsSL .../install.sh | bash","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:05.052016-08:00","updated_at":"2025-12-20T00:49:51.928221-08:00","closed_at":"2025-12-20T00:25:52.805029-08:00","dependencies":[{"issue_id":"bd-fgw3","depends_on_id":"bd-6s61","type":"parent-child","created_at":"2025-12-19T22:56:15.248427-08:00","created_by":"daemon"},{"issue_id":"bd-fgw3","depends_on_id":"bd-si4g","type":"blocks","created_at":"2025-12-19T22:56:23.497325-08:00","created_by":"daemon"}]} {"id":"bd-fi05","title":"bd sync fails with orphaned issues and duplicate ID conflict","description":"After fixing the deleted_at TEXT column scanning bug (commit 18b1eb2), bd sync still fails with two issues:\n\n1. Orphan Detection Warning: 12 orphaned child issues whose parents no longer exist (bd-cb64c226.* and bd-cbed9619.*)\n\n2. Import Failure: UNIQUE constraint failed for bd-360 - this tombstone exists in both DB and JSONL\n\nError: \"Import failed: error creating depth-0 issues: bulk insert issues: failed to insert issue bd-360: sqlite3: constraint failed: UNIQUE constraint failed: issues.id\"\n\nFix options:\n- Delete orphaned child issues with bd delete\n- Resolve bd-360 duplicate (in deletions.jsonl vs tombstone in DB)\n- Reset sync branch: git branch -f beads-sync main \u0026\u0026 git push --force-with-lease origin beads-sync","notes":"Fixed tombstone constraint violation bug. When deleting closed issues, the CHECK constraint (status = 'closed') = (closed_at IS NOT NULL) was violated because CreateTombstone didn't clear closed_at. Fix: set closed_at = NULL in tombstone creation SQL.\n\nThe sync data corruption (orphaned issues in beads-sync branch) requires manual cleanup: reset sync branch with 'git branch -f beads-sync main \u0026\u0026 git push --force-with-lease origin beads-sync'","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-13T07:14:33.831346-08:00","updated_at":"2025-12-13T10:50:48.545465-08:00","closed_at":"2025-12-13T07:30:33.843986-08:00"} {"id":"bd-fom","title":"Remove all deletions.jsonl code except migration","description":"There's deletions manifest code spread across the entire codebase that should have been removed after tombstone migration:\n\nFiles with deletions code (non-migration):\n- internal/deletions/ - entire package\n- cmd/bd/sync.go - 25+ references, auto-compact, sanitize\n- cmd/bd/delete.go - dual-writes to deletions.jsonl\n- internal/importer/importer.go - checks deletions manifest\n- internal/syncbranch/worktree.go - merges deletions.jsonl\n- cmd/bd/doctor/fix/sync.go - cleanupDeletionsManifest\n- cmd/bd/doctor/fix/deletions.go - HydrateDeletionsManifest\n- cmd/bd/integrity.go - checks deletions for data loss\n- cmd/bd/deleted.go - entire command\n- cmd/bd/compact.go - pruneDeletionsManifest\n- cmd/bd/doctor.go - checkDeletionsManifest\n- Plus many more\n\nAction: Aggressively remove all non-migration deletions code. Tombstones are the only deletion mechanism now.","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-16T13:29:04.960863-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} @@ -166,6 +169,7 @@ {"id":"bd-hy9p","title":"Add --body-file flag to bd create for reading descriptions from files","description":"## Problem\n\nCreating issues with long/complex descriptions via CLI requires shell escaping gymnastics:\n\n```bash\n# Current workaround - awkward heredoc quoting\nbd create --title=\"...\" --description=\"$(cat \u003c\u003c'EOF'\n...markdown...\nEOF\n)\"\n\n# Often fails with quote escaping errors in eval context\n# Agents resort to writing temp files then reading them\n```\n\n## Proposed Solution\n\nAdd `--body-file` and `--description-file` flags to read description from a file, matching `gh` CLI pattern.\n\n```bash\n# Natural pattern that aligns with training data\ncat \u003e /tmp/desc.md \u003c\u003c 'EOF'\n...markdown content...\nEOF\n\nbd create --title=\"...\" --body-file=/tmp/desc.md\n```\n\n## Implementation\n\n### 1. Add new flags to `bd create`\n\n```go\ncreateCmd.Flags().String(\"body-file\", \"\", \"Read description from file (use - for stdin)\")\ncreateCmd.Flags().String(\"description-file\", \"\", \"Alias for --body-file\")\n```\n\n### 2. Flag precedence\n\n- If `--body-file` or `--description-file` is provided, read from file\n- If value is `-`, read from stdin\n- Otherwise fall back to `--body` or `--description` flag\n- If neither provided, description is empty (current behavior)\n\n### 3. Error handling\n\n- File doesn't exist โ†’ clear error message\n- File not readable โ†’ clear error message\n- stdin specified but not available โ†’ clear error message\n\n## Benefits\n\nโœ… **Matches training data**: `gh issue create --body-file file.txt` is a common pattern\nโœ… **No shell escaping issues**: File content is read directly\nโœ… **Works with any content**: Markdown, special characters, quotes, etc.\nโœ… **Agent-friendly**: Agents already write complex content to temp files\nโœ… **User-friendly**: Easier for humans too when pasting long descriptions\n\n## Related Commands\n\nConsider adding similar support to:\n- `bd update --body-file` (for updating descriptions)\n- `bd comment --body-file` (if/when we add comments)\n\n## Examples\n\n```bash\n# From file\nbd create --title=\"Add new feature\" --body-file=feature.md\n\n# From stdin\necho \"Quick description\" | bd create --title=\"Bug fix\" --body-file=-\n\n# With other flags\nbd create \\\n --title=\"Security issue\" \\\n --type=bug \\\n --priority=0 \\\n --body-file=security-report.md \\\n --label=security\n```\n\n## Testing\n\n- Test with normal files\n- Test with stdin (`-`)\n- Test with non-existent files (error handling)\n- Test with binary files (should handle gracefully)\n- Test with empty files (valid - empty description)\n- Test that `--description-file` and `--body-file` are equivalent aliases","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-11-22T00:02:08.762684-08:00","updated_at":"2025-12-17T23:13:40.536024-08:00","closed_at":"2025-12-17T17:28:52.505239-08:00"} {"id":"bd-hzvz","title":"Update info.go versionChanges","description":"Add entry to versionChanges in cmd/bd/info.go with agent-actionable changes for 0.30.7","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T22:56:48.649359-08:00","updated_at":"2025-12-19T22:57:31.604229-08:00","closed_at":"2025-12-19T22:57:31.604229-08:00","dependencies":[{"issue_id":"bd-hzvz","depends_on_id":"bd-8pyn","type":"parent-child","created_at":"2025-12-19T22:56:48.652068-08:00","created_by":"stevey"},{"issue_id":"bd-hzvz","depends_on_id":"bd-2ep8","type":"blocks","created_at":"2025-12-19T22:56:48.652376-08:00","created_by":"stevey"}]} {"id":"bd-i0rx","title":"Merge: bd-ao0s","description":"branch: polecat/rictus\ntarget: main\nsource_issue: bd-ao0s\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-20T01:13:42.716658-08:00","updated_at":"2025-12-20T23:17:26.993744-08:00","closed_at":"2025-12-20T23:17:26.993744-08:00","close_reason":"Branches nuked, MRs obsolete"} +{"id":"bd-icnf","title":"Add bd mol run command (bond + assign + pin)","description":"bd mol run = bond + assign root to caller + pin to startup mail. This is the Gas Town integration point. When agent restarts, check startup mail, find pinned molecule root, query bd ready for next step. Makes molecules immortal.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-20T23:52:17.462882-08:00","updated_at":"2025-12-20T23:52:17.462882-08:00","dependencies":[{"issue_id":"bd-icnf","depends_on_id":"bd-ffjt","type":"blocks","created_at":"2025-12-20T23:52:25.871742-08:00","created_by":"daemon"}]} {"id":"bd-in7","title":"Test message","description":"Hello world","status":"closed","priority":2,"issue_type":"message","created_at":"2025-12-17T23:16:13.184946-08:00","updated_at":"2025-12-18T17:42:26.000073-08:00","closed_at":"2025-12-17T23:37:38.563369-08:00"} {"id":"bd-indn","title":"bd template commands fail with daemon mode","description":"The `bd template show` and `bd template instantiate` commands fail with 'Error loading template: no database connection' when daemon is running.\n\n**Reproduction:**\n```bash\nbd daemon --start\nbd template show bd-qqc # Error: no database connection\nbd template show bd-qqc --no-daemon # Works\n```\n\n**Expected:** Template commands should work with daemon like other commands.\n\n**Workaround:** Use `--no-daemon` flag.\n\n**Location:** Likely in cmd/bd/template.go - daemon RPC path not implemented for template operations.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-18T22:57:35.16596-08:00","updated_at":"2025-12-18T22:57:35.16596-08:00"} {"id":"bd-io8c","title":"Improve test coverage for internal/syncbranch (33.0% โ†’ 70%)","description":"The syncbranch package has only 33.0% test coverage. This package handles git sync operations and is critical for data integrity.\n\nCurrent coverage: 33.0%\nTarget coverage: 70%","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:43:02.079145-08:00","updated_at":"2025-12-13T21:01:14.972533-08:00"} diff --git a/cmd/bd/mol.go b/cmd/bd/mol.go new file mode 100644 index 00000000..e2945963 --- /dev/null +++ b/cmd/bd/mol.go @@ -0,0 +1,319 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + "github.com/steveyegge/beads/internal/rpc" + "github.com/steveyegge/beads/internal/storage" + "github.com/steveyegge/beads/internal/types" + "github.com/steveyegge/beads/internal/ui" + "github.com/steveyegge/beads/internal/utils" +) + +// Molecule commands - work templates for agent workflows +// +// Terminology: +// - Molecule: A template epic with child issues forming a DAG workflow +// - Bond: Instantiate a molecule, creating real issues from the template +// - Catalog: List available molecules +// +// Usage: +// bd mol catalog # List available molecules +// bd mol show # Show molecule structure +// bd mol bond --var key=value # Create issues from molecule + +// MoleculeLabel is the label used to identify molecules (templates) +// Molecules use the same label as templates - they ARE templates with workflow semantics +const MoleculeLabel = BeadsTemplateLabel + +// MoleculeSubgraph is an alias for TemplateSubgraph +// Molecules and templates share the same subgraph structure +type MoleculeSubgraph = TemplateSubgraph + +var molCmd = &cobra.Command{ + Use: "mol", + Short: "Molecule commands (work templates)", + Long: `Manage molecules - work templates for agent workflows. + +Molecules are epics with the "template" label. They define a DAG of work +that can be instantiated ("bonded") to create real issues. + +The molecule metaphor: + - A molecule is a template (reusable work pattern) + - Bonding creates new issues from the template + - Variables ({{key}}) are substituted during bonding + +Commands: + catalog List available molecules + show Show molecule structure and variables + bond Create issues from a molecule template`, +} + +var molCatalogCmd = &cobra.Command{ + Use: "catalog", + Aliases: []string{"list", "ls"}, + Short: "List available molecules", + Run: func(cmd *cobra.Command, args []string) { + ctx := rootCtx + var molecules []*types.Issue + + if daemonClient != nil { + resp, err := daemonClient.List(&rpc.ListArgs{}) + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading molecules: %v\n", err) + os.Exit(1) + } + var allIssues []*types.Issue + if err := json.Unmarshal(resp.Data, &allIssues); err == nil { + for _, issue := range allIssues { + for _, label := range issue.Labels { + if label == MoleculeLabel { + molecules = append(molecules, issue) + break + } + } + } + } + } else if store != nil { + var err error + molecules, err = store.GetIssuesByLabel(ctx, MoleculeLabel) + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading molecules: %v\n", err) + os.Exit(1) + } + } else { + fmt.Fprintf(os.Stderr, "Error: no database connection\n") + os.Exit(1) + } + + if jsonOutput { + outputJSON(molecules) + return + } + + if len(molecules) == 0 { + fmt.Println("No molecules available.") + fmt.Println("\nTo create a molecule:") + fmt.Println(" 1. Create an epic with child issues") + fmt.Println(" 2. Add the 'template' label: bd label add template") + fmt.Println(" 3. Use {{variable}} placeholders in titles/descriptions") + fmt.Println("\nTo bond (instantiate) a molecule:") + fmt.Println(" bd mol bond --var key=value") + return + } + + fmt.Printf("%s\n", ui.RenderPass("Molecules (for bd mol bond):")) + for _, mol := range molecules { + vars := extractVariables(mol.Title + " " + mol.Description) + varStr := "" + if len(vars) > 0 { + varStr = fmt.Sprintf(" (vars: %s)", strings.Join(vars, ", ")) + } + fmt.Printf(" %s: %s%s\n", ui.RenderAccent(mol.ID), mol.Title, varStr) + } + fmt.Println() + }, +} + +var molShowCmd = &cobra.Command{ + Use: "show ", + Short: "Show molecule details", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ctx := rootCtx + + // mol show requires direct store access for subgraph loading + if store == nil { + if daemonClient != nil { + fmt.Fprintf(os.Stderr, "Error: mol show requires direct database access\n") + fmt.Fprintf(os.Stderr, "Hint: use --no-daemon flag: bd --no-daemon mol show %s\n", args[0]) + } else { + fmt.Fprintf(os.Stderr, "Error: no database connection\n") + } + os.Exit(1) + } + + moleculeID, err := utils.ResolvePartialID(ctx, store, args[0]) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: molecule '%s' not found\n", args[0]) + os.Exit(1) + } + + subgraph, err := loadTemplateSubgraph(ctx, store, moleculeID) + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading molecule: %v\n", err) + os.Exit(1) + } + + showMolecule(subgraph) + }, +} + +func showMolecule(subgraph *MoleculeSubgraph) { + if jsonOutput { + outputJSON(map[string]interface{}{ + "root": subgraph.Root, + "issues": subgraph.Issues, + "dependencies": subgraph.Dependencies, + "variables": extractAllVariables(subgraph), + }) + return + } + + fmt.Printf("\n%s Molecule: %s\n", ui.RenderAccent("๐Ÿงช"), subgraph.Root.Title) + fmt.Printf(" ID: %s\n", subgraph.Root.ID) + fmt.Printf(" Steps: %d\n", len(subgraph.Issues)) + + vars := extractAllVariables(subgraph) + if len(vars) > 0 { + fmt.Printf("\n%s Variables:\n", ui.RenderWarn("๐Ÿ“")) + for _, v := range vars { + fmt.Printf(" {{%s}}\n", v) + } + } + + fmt.Printf("\n%s Structure:\n", ui.RenderPass("๐ŸŒฒ")) + printMoleculeTree(subgraph, subgraph.Root.ID, 0, true) + fmt.Println() +} + +var molBondCmd = &cobra.Command{ + Use: "bond ", + Short: "Create issues from a molecule template", + Long: `Bond (instantiate) a molecule by creating real issues from its template. + +Variables are specified with --var key=value flags. The molecule's {{key}} +placeholders will be replaced with the corresponding values. + +Example: + bd mol bond mol-code-review --var pr=123 --var repo=myproject + bd mol bond bd-abc123 --var version=1.2.0 --assignee=worker-1`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + CheckReadonly("mol bond") + + ctx := rootCtx + + // mol bond requires direct store access for subgraph loading and cloning + if store == nil { + if daemonClient != nil { + fmt.Fprintf(os.Stderr, "Error: mol bond requires direct database access\n") + fmt.Fprintf(os.Stderr, "Hint: use --no-daemon flag: bd --no-daemon mol bond %s ...\n", args[0]) + } else { + fmt.Fprintf(os.Stderr, "Error: no database connection\n") + } + os.Exit(1) + } + + dryRun, _ := cmd.Flags().GetBool("dry-run") + varFlags, _ := cmd.Flags().GetStringSlice("var") + assignee, _ := cmd.Flags().GetString("assignee") + + // Parse variables + vars := make(map[string]string) + for _, v := range varFlags { + parts := strings.SplitN(v, "=", 2) + if len(parts) != 2 { + fmt.Fprintf(os.Stderr, "Error: invalid variable format '%s', expected 'key=value'\n", v) + os.Exit(1) + } + vars[parts[0]] = parts[1] + } + + // Resolve molecule ID + moleculeID, err := utils.ResolvePartialID(ctx, store, args[0]) + if err != nil { + fmt.Fprintf(os.Stderr, "Error resolving molecule ID %s: %v\n", args[0], err) + os.Exit(1) + } + + // Load the molecule subgraph + subgraph, err := loadTemplateSubgraph(ctx, store, moleculeID) + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading molecule: %v\n", err) + os.Exit(1) + } + + // Check for missing variables + requiredVars := extractAllVariables(subgraph) + var missingVars []string + for _, v := range requiredVars { + if _, ok := vars[v]; !ok { + missingVars = append(missingVars, v) + } + } + if len(missingVars) > 0 { + fmt.Fprintf(os.Stderr, "Error: missing required variables: %s\n", strings.Join(missingVars, ", ")) + fmt.Fprintf(os.Stderr, "Provide them with: --var %s=\n", missingVars[0]) + os.Exit(1) + } + + if dryRun { + fmt.Printf("\nDry run: would create %d issues from molecule %s\n\n", len(subgraph.Issues), moleculeID) + for _, issue := range subgraph.Issues { + newTitle := substituteVariables(issue.Title, vars) + suffix := "" + if issue.ID == subgraph.Root.ID && assignee != "" { + suffix = fmt.Sprintf(" (assignee: %s)", assignee) + } + fmt.Printf(" - %s (from %s)%s\n", newTitle, issue.ID, suffix) + } + if len(vars) > 0 { + fmt.Printf("\nVariables:\n") + for k, v := range vars { + fmt.Printf(" {{%s}} = %s\n", k, v) + } + } + return + } + + // Clone the subgraph (bond the molecule) + result, err := bondMolecule(ctx, store, subgraph, vars, assignee, actor) + if err != nil { + fmt.Fprintf(os.Stderr, "Error bonding molecule: %v\n", err) + os.Exit(1) + } + + // Schedule auto-flush + markDirtyAndScheduleFlush() + + if jsonOutput { + outputJSON(result) + return + } + + fmt.Printf("%s Bonded molecule: created %d issues\n", ui.RenderPass("โœ“"), result.Created) + fmt.Printf(" Root issue: %s\n", result.NewEpicID) + }, +} + +func init() { + molBondCmd.Flags().StringSlice("var", []string{}, "Variable substitution (key=value)") + molBondCmd.Flags().Bool("dry-run", false, "Preview what would be created") + molBondCmd.Flags().String("assignee", "", "Assign the root issue to this agent/user") + + molCmd.AddCommand(molCatalogCmd) + molCmd.AddCommand(molShowCmd) + molCmd.AddCommand(molBondCmd) + rootCmd.AddCommand(molCmd) +} + +// ============================================================================= +// Molecule Helper Functions +// ============================================================================= + +// bondMolecule creates new issues from the molecule with variable substitution +// Wraps cloneSubgraph from template.go and returns BondResult +func bondMolecule(ctx context.Context, s storage.Storage, subgraph *MoleculeSubgraph, vars map[string]string, assignee string, actorName string) (*InstantiateResult, error) { + return cloneSubgraph(ctx, s, subgraph, vars, assignee, actorName) +} + +// printMoleculeTree prints the molecule structure as a tree +func printMoleculeTree(subgraph *MoleculeSubgraph, parentID string, depth int, isRoot bool) { + printTemplateTree(subgraph, parentID, depth, isRoot) +} diff --git a/cmd/bd/template.go b/cmd/bd/template.go index 6b196caa..9d7ecfef 100644 --- a/cmd/bd/template.go +++ b/cmd/bd/template.go @@ -39,9 +39,10 @@ type InstantiateResult struct { } var templateCmd = &cobra.Command{ - Use: "template", - GroupID: "setup", - Short: "Manage issue templates", + Use: "template", + GroupID: "setup", + Short: "Manage issue templates", + Deprecated: "use 'bd mol' instead (mol catalog, mol show, mol bond)", Long: `Manage Beads templates for creating issue hierarchies. Templates are epics with the "template" label. They can have child issues @@ -57,8 +58,9 @@ To use a template: } var templateListCmd = &cobra.Command{ - Use: "list", - Short: "List available templates", + Use: "list", + Short: "List available templates", + Deprecated: "use 'bd mol catalog' instead", Run: func(cmd *cobra.Command, args []string) { ctx := rootCtx var beadsTemplates []*types.Issue @@ -121,9 +123,10 @@ var templateListCmd = &cobra.Command{ } var templateShowCmd = &cobra.Command{ - Use: "show ", - Short: "Show template details", - Args: cobra.ExactArgs(1), + Use: "show ", + Short: "Show template details", + Deprecated: "use 'bd mol show' instead", + Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { ctx := rootCtx var templateID string @@ -193,8 +196,9 @@ func showBeadsTemplate(subgraph *TemplateSubgraph) { } var templateInstantiateCmd = &cobra.Command{ - Use: "instantiate ", - Short: "Create issues from a Beads template", + Use: "instantiate ", + Short: "Create issues from a Beads template", + Deprecated: "use 'bd mol bond' instead", Long: `Instantiate a Beads template by cloning its subgraph and substituting variables. Variables are specified with --var key=value flags. The template's {{key}}