diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ab3f0dac..ab676669 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ "name": "beads", "source": "./", "description": "AI-supervised issue tracker for coding workflows", - "version": "0.33.2" + "version": "0.34.0" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 1258f682..ccc2cdd8 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "beads", "description": "AI-supervised issue tracker for coding workflows. Manage tasks, discover work, and maintain context with simple CLI commands.", - "version": "0.33.2", + "version": "0.34.0", "author": { "name": "Steve Yegge", "url": "https://github.com/steveyegge" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0294cfff..e5c61e41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.34.0] - 2025-12-22 + +### Added + +- **Wisp commands** (bd-kwjh) - Full ephemeral molecule management + - `bd wisp create ` - Instantiate proto as ephemeral wisp (solid→vapor) + - `bd wisp list` - List all wisps with stale detection + - `bd wisp gc` - Garbage collect orphaned wisps + - Wisps live in `.beads-wisp/` (gitignored), never sync to remote + +- **Chemistry UX commands** - Phase-aware molecule operations + - `bd pour ` - Instantiate proto as persistent mol (solid→liquid) + - `bd mol bond --wisp` - Force spawn as vapor when attaching to mol + - `bd mol bond --pour` - Force spawn as liquid when attaching to wisp + - Cross-store squash: condense wisp to digest in main storage + +- **Cross-project dependencies** (bd-66w1, bd-om4a) - Reference issues across repos + - `external::` dependency syntax + - `bd ship --to ` - Ship issues to other beads repos + - `bd ready` filters by external dependency satisfaction + - Configure additional repos in `.beads/config.yaml` + +- **Orphan detection in bd doctor** (bd-5hrq) - Find issues with missing parents + - Detects parent-child relationships pointing to deleted issues + - Suggests fix commands for orphaned issues + +### Changed + +- **Multi-repo config uses YAML** (GH#683) - `bd repo add/remove` now writes to `.beads/config.yaml` + - Fixes disconnect where CLI wrote to DB but hydration read from YAML + - `bd repo remove` now cleans up hydrated issues from removed repo + - Breaking: `bd repo add` no longer accepts optional alias argument + +### Fixed + +- **Wisp storage initialization** - NewWispStorage now copies issue_prefix from main db +- **Prefix validation in multi-repo mode** (GH#686) - Skip validation for external repos +- **Empty config values** (GH#680, GH#684) - Handle gracefully in getRepoConfig() +- **Doctor UX improvements** (GH#687) - Better diagnostics and daemon integration +- **Orphaned test file** - Removed repo_test.go with undefined functions + ## [0.33.2] - 2025-12-21 ## [0.33.1] - 2025-12-21 diff --git a/cmd/bd/doctor_test.go b/cmd/bd/doctor_test.go index f3ea14e7..fb485036 100644 --- a/cmd/bd/doctor_test.go +++ b/cmd/bd/doctor_test.go @@ -895,7 +895,8 @@ func TestCheckMetadataVersionTracking(t *testing.T) { { name: "slightly outdated version", setupVersion: func(beadsDir string) error { - return os.WriteFile(filepath.Join(beadsDir, ".local_version"), []byte("0.24.0\n"), 0644) + // Use a version that's less than 10 minor versions behind current + return os.WriteFile(filepath.Join(beadsDir, ".local_version"), []byte("0.30.0\n"), 0644) }, expectedStatus: doctor.StatusOK, expectWarning: false, @@ -903,7 +904,8 @@ func TestCheckMetadataVersionTracking(t *testing.T) { { name: "very old version", setupVersion: func(beadsDir string) error { - return os.WriteFile(filepath.Join(beadsDir, ".local_version"), []byte("0.14.0\n"), 0644) + // Use a version that's 10+ minor versions behind current (triggers warning) + return os.WriteFile(filepath.Join(beadsDir, ".local_version"), []byte("0.24.0\n"), 0644) }, expectedStatus: doctor.StatusWarning, expectWarning: true, diff --git a/cmd/bd/info.go b/cmd/bd/info.go index beeba717..8b1f0da9 100644 --- a/cmd/bd/info.go +++ b/cmd/bd/info.go @@ -288,6 +288,17 @@ type VersionChange struct { // versionChanges contains agent-actionable changes for recent versions var versionChanges = []VersionChange{ + { + Version: "0.34.0", + Date: "2025-12-22", + Changes: []string{ + "NEW: Wisp commands - bd wisp create/list/gc for ephemeral molecule management", + "NEW: Chemistry UX - bd pour, bd mol bond --wisp/--pour for phase control", + "NEW: Cross-project deps - external:: syntax, bd ship command", + "BREAKING: bd repo add/remove now writes to .beads/config.yaml (not DB)", + "FIX: Wisp storage auto-copies issue_prefix from main database", + }, + }, { Version: "0.33.2", Date: "2025-12-21", diff --git a/cmd/bd/repo_test.go b/cmd/bd/repo_test.go deleted file mode 100644 index 524b4e8c..00000000 --- a/cmd/bd/repo_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package main - -import ( - "context" - "testing" -) - -func TestGetRepoConfig_EmptyValue(t *testing.T) { - ctx := context.Background() - store, cleanup := setupTestDB(t) - defer cleanup() - - // Test 1: No config set at all - should return empty map - repos, err := getRepoConfig(ctx, store) - if err != nil { - t.Fatalf("getRepoConfig with no config failed: %v", err) - } - if len(repos) != 0 { - t.Errorf("Expected empty map, got %d entries", len(repos)) - } - - // Test 2: Empty string value - should return empty map (this was the bug) - // This simulates GetConfig returning ("", nil) which caused "unexpected end of JSON input" - err = store.SetConfig(ctx, "repos.additional", "") - if err != nil { - t.Fatalf("SetConfig failed: %v", err) - } - repos, err = getRepoConfig(ctx, store) - if err != nil { - t.Fatalf("getRepoConfig with empty value failed: %v", err) - } - if len(repos) != 0 { - t.Errorf("Expected empty map for empty value, got %d entries", len(repos)) - } - - // Test 3: Valid JSON value - should parse correctly - err = store.SetConfig(ctx, "repos.additional", `{"alias1":"/path/to/repo1","alias2":"/path/to/repo2"}`) - if err != nil { - t.Fatalf("SetConfig with JSON failed: %v", err) - } - repos, err = getRepoConfig(ctx, store) - if err != nil { - t.Fatalf("getRepoConfig with valid JSON failed: %v", err) - } - if len(repos) != 2 { - t.Errorf("Expected 2 repos, got %d", len(repos)) - } - if repos["alias1"] != "/path/to/repo1" { - t.Errorf("Expected '/path/to/repo1', got '%s'", repos["alias1"]) - } - if repos["alias2"] != "/path/to/repo2" { - t.Errorf("Expected '/path/to/repo2', got '%s'", repos["alias2"]) - } -} - -func TestSetRepoConfig(t *testing.T) { - ctx := context.Background() - store, cleanup := setupTestDB(t) - defer cleanup() - - // Set repos and verify round-trip - repos := map[string]string{ - "planning": "/home/user/planning-repo", - "shared": "/home/user/shared-repo", - } - - err := setRepoConfig(ctx, store, repos) - if err != nil { - t.Fatalf("setRepoConfig failed: %v", err) - } - - // Read back - result, err := getRepoConfig(ctx, store) - if err != nil { - t.Fatalf("getRepoConfig after set failed: %v", err) - } - - if len(result) != 2 { - t.Errorf("Expected 2 repos, got %d", len(result)) - } - if result["planning"] != "/home/user/planning-repo" { - t.Errorf("Expected planning repo path, got '%s'", result["planning"]) - } - if result["shared"] != "/home/user/shared-repo" { - t.Errorf("Expected shared repo path, got '%s'", result["shared"]) - } -} - -func TestRepoConfigEmptyMap(t *testing.T) { - ctx := context.Background() - store, cleanup := setupTestDB(t) - defer cleanup() - - // Set empty map - repos := make(map[string]string) - err := setRepoConfig(ctx, store, repos) - if err != nil { - t.Fatalf("setRepoConfig with empty map failed: %v", err) - } - - // Read back - should work and return empty map - result, err := getRepoConfig(ctx, store) - if err != nil { - t.Fatalf("getRepoConfig after empty set failed: %v", err) - } - if len(result) != 0 { - t.Errorf("Expected empty map, got %d entries", len(result)) - } -} diff --git a/cmd/bd/templates/hooks/post-checkout b/cmd/bd/templates/hooks/post-checkout index 7adf8771..bd4b2f52 100755 --- a/cmd/bd/templates/hooks/post-checkout +++ b/cmd/bd/templates/hooks/post-checkout @@ -1,6 +1,6 @@ #!/bin/sh # bd-shim v1 -# bd-hooks-version: 0.33.2 +# bd-hooks-version: 0.34.0 # # bd (beads) post-checkout hook - thin shim # diff --git a/cmd/bd/templates/hooks/post-merge b/cmd/bd/templates/hooks/post-merge index 8f1ff4d3..f61c0133 100755 --- a/cmd/bd/templates/hooks/post-merge +++ b/cmd/bd/templates/hooks/post-merge @@ -1,6 +1,6 @@ #!/bin/sh # bd-shim v1 -# bd-hooks-version: 0.33.2 +# bd-hooks-version: 0.34.0 # # bd (beads) post-merge hook - thin shim # diff --git a/cmd/bd/templates/hooks/pre-commit b/cmd/bd/templates/hooks/pre-commit index 5fcaf270..4aa6a0b5 100755 --- a/cmd/bd/templates/hooks/pre-commit +++ b/cmd/bd/templates/hooks/pre-commit @@ -1,6 +1,6 @@ #!/bin/sh # bd-shim v1 -# bd-hooks-version: 0.33.2 +# bd-hooks-version: 0.34.0 # # bd (beads) pre-commit hook - thin shim # diff --git a/cmd/bd/templates/hooks/pre-push b/cmd/bd/templates/hooks/pre-push index b162292d..97311f03 100755 --- a/cmd/bd/templates/hooks/pre-push +++ b/cmd/bd/templates/hooks/pre-push @@ -1,6 +1,6 @@ #!/bin/sh # bd-shim v1 -# bd-hooks-version: 0.33.2 +# bd-hooks-version: 0.34.0 # # bd (beads) pre-push hook - thin shim # diff --git a/cmd/bd/version.go b/cmd/bd/version.go index 5c73467a..6de83333 100644 --- a/cmd/bd/version.go +++ b/cmd/bd/version.go @@ -14,7 +14,7 @@ import ( var ( // Version is the current version of bd (overridden by ldflags at build time) - Version = "0.33.2" + Version = "0.34.0" // Build can be set via ldflags at compile time Build = "dev" // Commit and branch the git revision the binary was built from (optional ldflag) diff --git a/integrations/beads-mcp/pyproject.toml b/integrations/beads-mcp/pyproject.toml index bc83428a..f2144018 100644 --- a/integrations/beads-mcp/pyproject.toml +++ b/integrations/beads-mcp/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "beads-mcp" -version = "0.33.2" +version = "0.34.0" description = "MCP server for beads issue tracker." readme = "README.md" requires-python = ">=3.10" diff --git a/integrations/beads-mcp/src/beads_mcp/__init__.py b/integrations/beads-mcp/src/beads_mcp/__init__.py index 3acc94a6..4e6aa51b 100644 --- a/integrations/beads-mcp/src/beads_mcp/__init__.py +++ b/integrations/beads-mcp/src/beads_mcp/__init__.py @@ -4,4 +4,4 @@ This package provides an MCP (Model Context Protocol) server that exposes beads (bd) issue tracker functionality to MCP Clients. """ -__version__ = "0.33.2" +__version__ = "0.34.0" diff --git a/internal/beads/beads.go b/internal/beads/beads.go index 96e00b09..b5f22856 100644 --- a/internal/beads/beads.go +++ b/internal/beads/beads.go @@ -664,13 +664,39 @@ func FindWispDatabasePath() (string, error) { // NewWispStorage opens the wisp database for ephemeral molecule storage. // Creates the database and directory if they don't exist. // The wisp database uses the same schema as the main database. +// Automatically copies issue_prefix from the main beads config if not set. func NewWispStorage(ctx context.Context) (Storage, error) { dbPath, err := FindWispDatabasePath() if err != nil { return nil, err } - return sqlite.New(ctx, dbPath) + wispStore, err := sqlite.New(ctx, dbPath) + if err != nil { + return nil, err + } + + // Check if wisp db has issue_prefix configured + prefix, err := wispStore.GetConfig(ctx, "issue_prefix") + if err != nil || prefix == "" { + // Copy issue_prefix from main beads database + mainDBPath := FindDatabasePath() + if mainDBPath != "" { + mainStore, mainErr := sqlite.New(ctx, mainDBPath) + if mainErr == nil { + defer mainStore.Close() + mainPrefix, _ := mainStore.GetConfig(ctx, "issue_prefix") + if mainPrefix != "" { + if setErr := wispStore.SetConfig(ctx, "issue_prefix", mainPrefix); setErr != nil { + wispStore.Close() + return nil, fmt.Errorf("setting wisp issue_prefix: %w", setErr) + } + } + } + } + } + + return wispStore, nil } // EnsureWispGitignore ensures the wisp directory is gitignored. diff --git a/npm-package/package.json b/npm-package/package.json index 913568ed..b70dc4da 100644 --- a/npm-package/package.json +++ b/npm-package/package.json @@ -1,6 +1,6 @@ { "name": "@beads/bd", - "version": "0.33.2", + "version": "0.34.0", "description": "Beads issue tracker - lightweight memory system for coding agents with native binary support", "main": "bin/bd.js", "bin": {