release: v0.34.0
## Added - Wisp commands - bd wisp create/list/gc for ephemeral molecule management - Chemistry UX - bd pour, bd mol bond --wisp/--pour for phase control - Cross-project deps - external:<repo>:<id> syntax, bd ship command - Orphan detection in bd doctor ## Changed - Multi-repo config uses YAML - bd repo add/remove writes to .beads/config.yaml ## Fixed - Wisp storage auto-copies issue_prefix from main database - Prefix validation in multi-repo mode - Remove orphaned repo_test.go - Update version tests for 0.34.0 thresholds 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
"name": "beads",
|
||||
"source": "./",
|
||||
"description": "AI-supervised issue tracker for coding workflows",
|
||||
"version": "0.33.2"
|
||||
"version": "0.34.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
41
CHANGELOG.md
41
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 <proto>` - 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 <proto>` - 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:<repo>:<id>` dependency syntax
|
||||
- `bd ship <id> --to <repo>` - 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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:<repo>:<id> 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",
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user