Replace tool-specific setup commands with a generic recipe-based system.
New tools become config entries, not code changes.
Changes:
- Add internal/recipes/ package with Recipe type and built-in recipes
- Add --list flag to show available recipes
- Add --print flag to output template to stdout
- Add -o flag to write template to arbitrary path
- Add --add flag to save custom recipes to .beads/recipes.toml
- Add built-in recipes: windsurf, cody, kilocode (new)
- Legacy recipes (cursor, claude, gemini, aider, factory) continue to work
The recipe system enables:
- Adding new tool support without code changes
- User-defined recipes in .beads/recipes.toml
- Shared template across all file-based integrations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
The external_ref field was stored correctly and visible in --json
output, but missing from the human-readable text display.
Added External Ref line after other metadata fields in both daemon
and direct mode paths. Added tests for external_ref display.
The RPC server's handleReady() was explicitly setting Status to
StatusOpen, which overrode the intended behavior where an empty Status
field matches both 'open' and 'in_progress' issues.
Removed the Status field assignment so it remains empty (zero value),
allowing the SQLite storage layer to correctly return both statuses
as documented in the help text.
Fixes #5aml
The --blocks flag handler duplicated cycle detection warning logic from
depAddCmd. Extract to a shared helper function.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The bug: initializeNoDbMode() was setting the legacy global storeActive
but not cmdCtx.StoreActive. When ensureStoreActive() checked
isStoreActive(), it used cmdCtx.StoreActive (which was false), causing
the JSONL-only mode error even when --no-db was passed.
The fix: Use accessor functions (lockStore, setStore, setStoreActive,
unlockStore) which set both the legacy globals and cmdCtx fields.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add alreadyExported flag to skip redundant export. When
gitHasUncommittedBeadsChanges() detects uncommitted changes, we export
at line 175. The flag prevents the normal flow from exporting again
at line 293.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a --blocks (-b) shorthand flag to bd dep command for natural dependency syntax:
bd dep bd-xyz --blocks bd-abc # bd-xyz blocks bd-abc
Equivalent to: bd dep add bd-abc bd-xyz
- Full daemon and direct mode support
- Cycle detection and child-parent anti-pattern checks
- JSON output support
Contributed by: kraitsura
The daemon code path was returning early after adding the dependency,
skipping the cycle detection that runs for direct mode. Restructure
so both paths share the cycle detection and output code.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Agents naturally try to use 'bd dep <blocker> --blocks <blocked>' when
establishing blocking relationships - a desire path revealing intuitive
mental model for how dependencies should work.
When AI agents set up dependency chains, they consistently attempt:
bd dep conduit-abc --blocks conduit-xyz
This reveals a desire path - the syntax users naturally reach for before
reading documentation. Instead of fighting this intuition, we embrace it.
- Add --blocks (-b) flag to the bd dep command
- Support syntax: bd dep <blocker-id> --blocks <blocked-id>
- Equivalent to: bd dep add <blocked-id> <blocker-id>
- Full daemon and direct mode support
- Cycle detection and child-parent anti-pattern checks
- JSON output support for programmatic use
This is purely additive. The existing command structure remains:
- 'bd dep add' subcommand works exactly as before
- All other dep subcommands (remove, list, tree, cycles) unchanged
- No breaking changes to existing workflows
bd dep bd-xyz --blocks bd-abc # bd-xyz blocks bd-abc
bd dep bd-xyz -b bd-abc # Same, using shorthand
bd dep add bd-abc bd-xyz # Original syntax still works
- Added TestDepBlocksFlag for flag initialization
- Added TestDepBlocksFlagFunctionality for semantic correctness
- All existing tests pass
In performExport, if git commit succeeded but push failed, the
finalizeExportMetadata() was never called because we returned early.
This meant metadata would not reflect the successful export+commit.
Now finalize is called:
- Right after syncBranchCommitAndPush succeeds
- Right after gitCommit succeeds (before push attempt)
- When no git changes exist (export still happened)
Push failure still returns early, but metadata is already updated.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
On macOS with case-insensitive filesystem, path casing differences between
the daemon socket path and git worktree registry caused sync failures.
Changed isValidWorktree() to use utils.PathsEqual() which handles
case-insensitivity on macOS/Windows, matching the fix already applied
to daemon registry in GH#869.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When running bd init in a subdirectory of a hub (e.g., ~/Repos/project
where ~/Repos/.beads exists), the new database was incorrectly inheriting
issues from the parent hub.
Root cause: checkGitForIssues() computed the relative path from gitRoot
to beadsDir but did not validate that beadsDir was actually inside the
git repository. When beadsDir was outside (e.g., ../.beads), it would
still attempt to import, causing contamination.
Fix: Add a guard to reject beadsDir paths that start with .. (outside
the git repository boundary).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When setting a slot to a bead from a different beads database
(e.g., setting an hq-* role bead on a gt-* agent bead), the command
now uses prefix-based routing via routes.jsonl to resolve the bead
in the correct database.
Previously, bd slot set only looked in the local database, failing
to find cross-db references like hq-polecat-role from rig beads.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This fixes the atomicity gap where exportToJSONL would update SQLite
metadata (clear dirty flags, update content hash, last_import_time)
BEFORE the git commit. If git commit failed, SQLite would incorrectly
indicate the sync succeeded.
Changes:
- Add ExportResult struct to capture export metadata for deferred finalization
- Add exportToJSONLDeferred() that exports without updating metadata
- Add finalizeExport() to update metadata after git commit succeeds
- Update daemon_sync.go sync flows to defer metadata updates
Now the sync flow is truly atomic: metadata only updates after git commit.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
Add CheckSyncDivergence doctor check that detects:
- JSONL on disk differs from git HEAD version
- SQLite last_import_time does not match JSONL mtime
- Uncommitted .beads/ changes exist
Each issue includes auto-fix suggestions (bd sync, bd export, git commit).
Multiple divergence issues result in error status.
Part of GH#885 recovery mechanism.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add pre-flight safety check to detect when a previous sync exported
but failed before commit, leaving JSONL in an inconsistent state.
- Add gitHasUncommittedBeadsChanges() helper in sync_git.go
- Call in sync pre-flight checks after merge/rebase check
- If uncommitted changes detected, force re-export to reconcile state
This catches the failure mode early before it compounds across worktrees.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
bd sync fails with exit status 128 when the daemon is started from a
terminal with different path casing than what git has stored. This
happens on macOS case-insensitive filesystem when directory names
are renamed (e.g., MyProject to myproject) but terminal sessions
retain the old casing.
The fix uses realpath(1) on macOS to get the true filesystem case
when canonicalizing paths:
- CanonicalizePath() now calls realpath on macOS
- git.GetRepoRoot() canonicalizes repoRoot via canonicalizeCase()
- syncbranch.GetRepoRoot() uses utils.CanonicalizePath()
This ensures git worktree paths match exactly, preventing the
exit status 128 errors from git operations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
Use git sparse-checkout command instead of manually setting
core.sparseCheckout config. The sparse-checkout command properly
scopes the setting to the worktree via extensions.worktreeConfig,
avoiding the confusing sparse checkout message in git status.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On macOS (HFS+/APFS), `bd sync` would fail with exit status 128 when
the daemon was started from a terminal session with different path
casing than what git had stored for the worktree (e.g., /Users/.../
MyProject vs /Users/.../myproject).
Fixed by normalizing workspace paths using `filepath.EvalSymlinks()`
before storing in the registry and comparing during lookups:
- registry.Register(): Canonicalizes workspace path before storing
- registry.Unregister(): Canonicalizes paths before comparison
- FindDaemonByWorkspace(): Canonicalizes paths before lookup
This ensures consistent path matching across case-insensitive
filesystems since EvalSymlinks returns the actual filesystem casing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When bd sync fails mid-operation, the local JSONL can become stale while
the SQLite database has the correct state. Previously, bd doctor only
checked count and timestamp differences, missing cases where counts match
but issue statuses differ.
This adds content-level comparison to CheckDatabaseJSONLSync that:
- Compares issue statuses between DB and JSONL
- Samples up to 500 issues for performance on large databases
- Reports detailed mismatches (shows up to 3 examples)
- Suggests 'bd export' to fix the stale JSONL
Example detection:
Status mismatch: 1 issue(s) have different status in DB vs JSONL
Status mismatches detected:
test-1: DB=closed, JSONL=open
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add flag-based alternatives to the positional argument for `bd dep add`:
- `--blocked-by <id>`: Specify the blocking issue via flag
- `--depends-on <id>`: Alias for --blocked-by
This reduces token waste when Claude guesses flag-based syntax, which
is a common pattern. Previously, Claude would attempt commands like:
bd dep add issue-123 --blocked-by issue-456
This would fail with "unknown flag" and require retry. Now both:
bd dep add issue-123 issue-456
bd dep add issue-123 --blocked-by issue-456
work identically.
Closes GH#888
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>