- Update Makefile install target to extract and pass git commit/branch via ldflags
- Add -X main.Commit and -X main.Branch to all build configurations in .goreleaser.yml
- Create scripts/install.sh helper for explicit version control during installation
- Add comprehensive tests for commit/branch resolution and output formatting
Fixes github #503: 'bd version' now reports as-built commit hash and branch
information regardless of installation method (make install, go install, or
released binaries from goreleaser).
Implements the bd reset command for GitHub issue #479:
- CLI command with flags: --hard, --force, --backup, --dry-run, --skip-init, --verbose
- Impact summary showing issues/tombstones to be deleted
- Confirmation prompt (skippable with --force)
- Colored output for better UX
- Unit tests for reset.go and git.go
- Fix: use --force flag in git rm to handle staged files
Part of epic bd-aydr.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On macOS, downloaded binaries with ad-hoc signatures from other machines
trigger Gatekeeper malware checks on every invocation, causing slowness.
Re-signing with a local ad-hoc signature avoids this.
Fixes#466🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Clarify that `bd ready` shows issues ready to work on (#512)
- Update git hooks install command from deprecated script to `bd hooks install` (#513)
- Fix Claude Code plugin local install: use `./beads` not `.`, clarify shell vs CC commands (#514)
Fixes#512, #513, #514🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add mise (polyglot runtime manager) as an installation option using the
ubi backend to install from GitHub releases.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update `/bd-create` and `/bd-ready` to `/beads:create` and `/beads:ready`
in the claude-code-skill README. This was missed in PR #467.
Relates to #463🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Marketplace-installed plugins use the beads: prefix instead of the
old bd- prefix. Updated all documentation and examples to reflect the
current slash command naming.
Fixes#463
- Remove Force from core ResetOptions (CLI concern)
- Clarify file locations (internal/reset/)
- Add ImpactSummary struct with open/closed counts
- Note backup dirs should be gitignored
- Add --verbose flag to CLI spec
- Clarify TDD approach for unit tests
- Add bd-aydr.9 for gitignore template update
The Claude Code allowedTools pattern uses prefix matching with space separator,
not colon. "Bash(bd *)" matches "bd ready", "bd create", etc.
Follow-up to 3d48458.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users with workflows that don't need sync-branch (e.g., Graphite PR
stacking, single-clone setups) were seeing confusing warnings.
Changes:
- Remove checkSyncBranchQuick() - no longer warn on every bd command
- Change bd doctor from warning to OK status for missing sync-branch
- Update message to "Not configured (optional)"
Fixes#498🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add bd to Claude Code allowedTools when running `bd setup claude` so
that all bd commands (create, update, close, etc.) can run without
requiring per-command approval.
Changes:
- Add addAllowedTool() and removeAllowedTool() helper functions
- InstallClaude() now adds "Bash(bd:*)" to allowedTools
- RemoveClaude() cleans up the allowedTools entry
- Add tests for new functionality
Users who have already run `bd setup claude` can run it again to add
the missing allowedTools entry while keeping their existing hooks.
Fixes#511🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests were hardcoding '.git' paths directly, which fails in git worktrees
where .git is a file (not a directory) containing a pointer to the actual
git directory.
Changes:
- Replace hardcoded '.git' paths with getGitDir() calls in all hook tests
- Add os.MkdirAll() calls to ensure hooks directory exists before writing
Fixes test failures in:
- TestInstallHooks, TestInstallHooksBackup, TestInstallHooksForce,
TestUninstallHooks, TestInstallHooksShared (hooks_test.go)
- TestDetectExistingHooks, TestInstallGitHooks_NoExistingHooks,
TestInstallGitHooks_ExistingHookBackup (init_hooks_test.go)
Cherry-picked from PR #472
Co-Authored-By: matt wilkie <maphew@gmail.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix sync bug where newly created issues were incorrectly tombstoned during bd sync.
The root cause was git-history-backfill finding issues in local commits on the sync branch, then tombstoning them when they weren't in the merged JSONL. The fix protects issues from the left snapshot (local export) from git-history-backfill.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Enable daemon RPC support for delete operations to trigger auto-sync.
This PR adds delete operation support to the RPC daemon, ensuring that delete operations emit mutation events and trigger auto-sync like other mutating operations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The TestSyncBranchCommitAndPush_WithPreCommitHook test needed fixes
to run correctly in isolation:
1. Set global dbPath variable so findJSONLPath() can locate the JSONL
file during sync operations. Without this, the test failed with
"JSONL path not found".
2. Add dummy git remote so hasGitRemote() returns true. The
syncBranchCommitAndPush function skips sync branch operations
when no remote is configured (local-only mode support).
3. Relax commit count assertion to check for "multiple commits"
rather than exact count of 4, since sync branch initialization
may add an extra commit depending on timing.
These changes ensure the regression test properly validates that
--no-verify bypasses pre-commit hooks in worktree commits.
Test verified:
- FAILS without --no-verify fix (confirms bug detection)
- PASSES with --no-verify fix (confirms fix works)
The integration tests were failing to compile due to several issues
introduced by API changes that weren't reflected in the test files:
1. daemon_test.go:
- getPIDFilePath() signature changed: removed boolean parameter
- getLogFilePath() signature changed: removed boolean parameter
- Removed duplicate windowsOS constant (already in test_helpers_test.go)
2. daemon_parent_test.go:
- Removed duplicate runGitCmd() function (already in git_sync_test.go
with more functionality including date env vars)
- Removed unused os/exec import
These fixes allow `go test -tags integration ./cmd/bd` to compile
successfully. The test suite can now be run to verify daemon and
sync branch functionality.
No behavioral changes - only fixing test compilation issues.
When pre-commit hooks are installed (via "bd hooks install"), daemon auto-sync
to sync branches fails with "git commit failed in worktree: exit status 1".
Root cause:
- gitCommitInWorktree() was missing --no-verify flag
- Pre-commit hook runs "bd sync --flush-only" which fails in worktree context
- Worktree has .beads directory, triggering hook execution
- Hook fails because bd cannot find proper database in worktree path
The fix adds --no-verify to git commit in gitCommitInWorktree(), matching
the existing implementation in internal/syncbranch/worktree.go line 684.
This is correct because:
- Worktree commits are internal to bd sync operations
- Running pre-commit hooks in worktree context is semantically wrong
- The library function already skips hooks for this reason
Includes regression test that:
- Creates a repo with sync branch configured
- Installs a failing pre-commit hook (simulating bd hook behavior)
- Verifies commits succeed because --no-verify bypasses the hook
- Tests multiple consecutive commits to ensure consistent behavior
Tested manually by:
1. Creating issue with "bd create" (triggers mutation event)
2. Verifying daemon logs show successful commit to sync branch
3. Confirming push to remote sync branch completes
- Add bd migrate-tombstones command (bd-8f9) to convert legacy
deletions.jsonl entries to inline tombstones in issues.jsonl
- Supports --dry-run to preview changes
- Supports --verbose for detailed progress
- Archives deletions.jsonl with .migrated suffix after migration
- Update bd compact to prune expired tombstones (bd-okh)
- All compact modes now prune tombstones older than 30-day TTL
- Reports count of pruned tombstones in output
- Add resurrection merge test (bd-bob)
- Tests scenario where base is tombstone but both left/right resurrect
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed Priority from hardcoded 2 to 0 (unset) to distinguish legacy tombstones
from user-set values. IssueType remains TypeTask as empty fails validation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Track legacy deletions.jsonl entries converted to tombstones during import:
- Add Result.ConvertedToTombstone counter
- Add Result.ConvertedTombstoneIDs for the converted IDs
- Update test to verify the new counter
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TestIsExpiredTombstone with edge cases for merge package
- Add TestImportIssues_LegacyDeletionsConvertedToTombstones for importer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Verify that tombstones are properly included when exporting issues
to JSONL format, including all tombstone-specific fields like
DeletedAt, DeletedBy, DeleteReason, and OriginalType.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace fragile strings.Contains("no-db: true") with proper YAML parsing
to avoid false matches in comments or nested keys.
Changes:
- Add NoDb field to localConfig struct
- Add isNoDbModeConfigured() helper function
- Update main.go and doctor.go to use the helper
- Add 8 test cases for isNoDbModeConfigured
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Close bd-6y5 (getLocalSyncBranch tests)
- Include tombstone export logic fix (bd-81x6)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace fragile line-by-line parsing with yaml.Unmarshal to handle:
- Indented sync-branch values
- Comments containing sync-branch
- Multi-line values
- Values with special characters
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update import/export to handle tombstones for deletion sync propagation:
Exporter:
- Include tombstones in JSONL output by setting IncludeTombstones: true
- Both single-repo and multi-repo exports now include tombstones
Importer:
- Tombstones from JSONL are imported as-is (they're issues with status=tombstone)
- Legacy deletions.jsonl entries are converted to tombstones via convertDeletionToTombstone()
- Non-tombstone issues in deletions manifest are still skipped (backward compat)
- purgeDeletedIssues() now creates tombstones instead of hard-deleting
This is Phase 2 of the tombstone implementation (bd-dli design), enabling
inline soft-delete tracking for cross-clone deletion synchronization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive validation for config values in bd doctor:
YAML config (config.yaml) validations:
- actor: alphanumeric with dashes, underscores, dots, @
- db: valid database extension (.db, .sqlite, .sqlite3)
- Boolean flags: json, no-daemon, no-auto-flush, no-auto-import,
no-db, auto-start-daemon validate as true/false/yes/no/1/0/on/off
- sync.require_confirmation_on_mass_delete: boolean validation
- repos.primary: must be a directory if path exists
- repos.additional: paths must be directories if they exist
Database config validations:
- status.custom: validates custom status names are lowercase
alphanumeric with underscores, checks for conflicts with built-in
statuses (open, in_progress, blocked, closed)
- sync.branch (legacy): validates as git branch name
Includes tests for all new validation functions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add unit tests verifying the --output flag registration and
exportDiagnostics function for saving doctor results to JSON files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- bd-ki14: Preserve tombstones when other side implicitly deleted
In merge3WayWithTTL(), implicit deletion cases now check if the
remaining side is a tombstone and preserve it instead of dropping.
- bd-ig5: Remove duplicate constants from merge package
StatusTombstone, DefaultTombstoneTTL, and ClockSkewGrace now
reference the types package to avoid duplication.
- bd-6x5: Handle empty DeletedAt in mergeTombstones()
Added explicit handling for edge cases where one or both tombstones
have empty DeletedAt fields with deterministic behavior.
- bd-1sn: Copy tombstone fields in mergeIssue() safety fallback
When status becomes tombstone via mergeStatus safety fallback,
tombstone fields are now copied from the appropriate side.
Added comprehensive tests for all fixed edge cases.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both findBeadsDir() and findGitRoot() now use filepath.EvalSymlinks()
to resolve to canonical paths before comparison. This fixes the issue
where filepath.Rel() fails on macOS because /var is a symlink to
/private/var, causing path mismatches.
Fixes: TestDatabaseReinitialization failures on macOS
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 1 of tombstone migration: bd delete now creates tombstones instead
of hard-deleting issues.
Key changes:
- Add CreateTombstone() method to SQLiteStorage for soft-delete
- Modify executeDelete() to create tombstones instead of removing rows
- Add IsExpired() method with 30-day default TTL and clock skew grace
- Fix deleted_at schema from TEXT to DATETIME for proper time scanning
- Update delete.go to call CreateTombstone (single issue path)
- Still writes to deletions.jsonl for backward compatibility (dual-write)
- Dependencies are removed when creating tombstones
- Tombstones are excluded from normal searches (bd-1bu)
TTL constants:
- DefaultTombstoneTTL: 30 days
- MinTombstoneTTL: 7 days (safety floor)
- ClockSkewGrace: 1 hour
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add partial index on deleted_at for efficient TTL queries
- Exclude tombstones from SearchIssues by default (new IncludeTombstones filter)
- Report tombstone count separately in GetStatistics
- Display tombstone count in bd stats output
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add validation in ValidateWithCustomStatuses() requiring deleted_at for tombstones
- Add validation that non-tombstones cannot have deleted_at set
- Block direct status update to tombstone in validateStatusWithCustom()
- Users must use 'bd delete' instead of 'bd update --status=tombstone'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Small fixes from code review:
- Error message now shows actual gitRef instead of hardcoded HEAD
- Removed unused lineNum variable in readFirstIssueFromGit
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When sync-branch is configured in config.yaml, bd init now reads from
that branch (origin/<branch> first, then local <branch>) instead of
HEAD. This ensures fresh clones correctly import issues from the sync
branch.
Key changes:
- checkGitForIssues() now returns gitRef (third return value)
- New getLocalSyncBranch() reads sync-branch directly from config.yaml
(not cached global config) to handle test environments where CWD changes
- importFromGit() accepts gitRef parameter to read from correct branch
- Added readFirstIssueFromGit() for prefix auto-detection from git
- Fixed macOS symlink issue: filepath.EvalSymlinks() ensures /var and
/private/var paths are normalized before filepath.Rel()
Part of GitHub issue #464 (beads deletes issues in multi-clone environments)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When syncing JSONL to worktree, if the worktree has more issues than
local, merge them instead of blindly overwriting. This prevents fresh
clones from accidentally deleting remote issues when they sync with
fewer issues than the sync branch.
Root cause of GitHub #464: A fresh clone with sync-branch configured
would start with an empty database (since JSONL is on sync-branch, not
HEAD). When syncing, the local 1-issue JSONL would overwrite the
remotes 10-issue JSONL, and the subsequent 3-way merge would see this
as local deleted 9 issues causing deletion to win.
The fix compares issue counts and triggers a merge when local has fewer
issues than the worktree (remote). Uses 3-way merge with empty base to
combine both sets of issues.
Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>