Merges complete npm package implementation for @beads/bd.
Features:
- npm package wrapping native bd binaries
- Automatic platform-specific binary download
- Claude Code for Web integration via SessionStart hooks
- Comprehensive integration test suite (5 tests, all passing)
- Complete documentation (6 guides)
- Release process documentation
Published to npm: https://www.npmjs.com/package/@beads/bd
Benefits over WASM:
- Full SQLite support (native vs custom VFS)
- Better performance
- Simpler implementation and maintenance
- 100% feature parity with standalone bd
Closes bd-febc
This change improves information density by using Base36 (0-9, a-z) instead
of hex (0-9, a-f) for hash-based issue IDs. Key benefits:
- Shorter IDs: Can now use 3-char IDs (was 4-char minimum)
- Better scaling: 3 chars good for ~160 issues, 4 chars for ~980 issues
- Case-insensitive: Maintains excellent CLI usability
- Backward compatible: Old hex IDs continue to work
Changes:
- Implemented Base36 encoding with proper truncation (keep LSB)
- Updated adaptive length thresholds (3-8 chars instead of 4-8)
- Fixed collision probability math to match encoding (was calculating
for base36 but encoding in hex - now both use base36)
- Fixed ID parser bug (use prefixWithHyphen for substring matching)
- Updated all tests and test data patterns
Fixes#213🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Multiple CLI commands had a systematic bug where ResolveID responses were
incorrectly converted using string(resp.Data) instead of json.Unmarshal.
Since resp.Data is json.RawMessage (already JSON-encoded), this preserved
the JSON quotes, causing IDs to become "bd-1048" instead of bd-1048.
When re-marshaled for subsequent RPC calls, these became double-quoted
("\"bd-1048\""), causing database lookups to fail.
Bugs fixed:
1. Nil pointer dereference in handleShow - added nil check after GetIssue
2. Double JSON encoding in 12 locations across 4 commands:
- bd show (3 instances in show.go)
- bd dep add/remove/tree (5 instances in dep.go)
- bd label add/remove/list (3 instances in label.go)
- bd reopen (1 instance in reopen.go)
All instances replaced string(resp.Data) with proper json.Unmarshal.
Removed debug logging added during investigation.
Tested: All affected commands now work correctly with daemon mode.
- Switched from modernc.org/sqlite to ncruces/go-sqlite3 for WASM support
- Added WASM-specific stubs for daemon process management
- Created wasm/ directory with build.sh and Node.js runner
- WASM build succeeds (32MB bd.wasm)
- Node.js can load and execute the WASM module
- Next: Need to bridge Go file I/O to Node.js fs module
Related: bd-44d0, bd-8534, bd-c7eb
Updated tests to match the new branchExists() signature that returns
bool instead of (bool, error).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add #nosec directives with explanations for all gosec warnings in worktree operations
- Tighten directory permissions from 0755 to 0750 for better security
- Fix misspellings: archaeological -> archeological, cancelled -> canceled
- Remove unused jsonlPath parameter from syncBranchCommitAndPush
- Change branchExists to return bool instead of (bool, error) - error was never used
All changes maintain backward compatibility and improve code quality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Extracts duplicated path canonicalization logic (filepath.Abs + EvalSymlinks)
into a reusable helper function utils.CanonicalizePath() in internal/utils/path.go.
Changes:
- Add internal/utils/path.go with CanonicalizePath() function
- Add comprehensive tests in internal/utils/path_test.go
- Replace inline canonicalization in beads.go:131-140
- Replace inline canonicalization in cmd/bd/main.go:446-454
- Replace inline canonicalization in cmd/bd/nodb.go:25-33
The new helper maintains identical behavior:
1. Converts path to absolute form via filepath.Abs
2. Resolves symlinks via filepath.EvalSymlinks
3. Falls back gracefully on errors (returns absPath if EvalSymlinks fails,
returns original path if Abs fails)
Fixes bd-efe8
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL BUG: The previous fix had a race condition where the
importInProgress flag could be released twice, allowing two goroutines
to think they both hold the lock.
Bug scenario:
1. Goroutine A: acquires lock (CAS true)
2. Goroutine A: manually releases at line 208 for git dirty skip
3. Goroutine B: CAS succeeds, acquires lock
4. Goroutine A: defer runs, releases flag AGAIN (clears B lock)
5. Goroutine C: CAS succeeds - now TWO goroutines have lock
Root cause: Using both manual Store(false) AND defer Store(false)
created a window where the flag could be cleared twice.
Fix: Use a shouldDeferRelease flag to disable the deferred release
when we manually release early. This ensures exactly one release
per acquisition.
Testing: All auto-import tests still passing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL FIX: The daemon could enter a corrupt state when auto-import
was triggered while uncommitted changes existed in .beads/ files. This
caused mysterious EOF errors requiring daemon restarts.
Root Causes Fixed:
1. No git dirty check before auto-import
2. Missing timeout protection (could hang indefinitely)
3. Race condition in importInProgress flag management
4. Improper git status parsing (false positives)
5. Context leak in export goroutine (accessing closed DB)
6. Data loss in triggerExport (missing deps/labels/comments)
Changes:
- Add hasUncommittedBeadsFiles() to check git status before import
- Properly parses git porcelain format ("XY filename")
- Ignores untracked files, only checks tracked .jsonl changes
- 5-second timeout on git command to prevent hangs
- Add 30-second timeout to import operations
- Prevents daemon from hanging on stuck imports
- Returns clear error message on timeout
- Fix race condition in importInProgress flag
- Explicitly release before early returns
- Prevents other requests seeing incorrect "import in progress"
- Fix context leak in onChanged export goroutine
- Check daemon shutdown state before export
- Suppress expected "database closed" errors during shutdown
- Pass storage/path as parameters to avoid closure issues
- Fix data loss in triggerExport()
- Populate dependencies, labels, comments (mirrors handleExport)
- Use atomic file write (temp + rename)
- Sort issues for consistent output
Impact: Prevents daemon corruption in collaborative workflows where
users frequently pull updates while having local uncommitted changes.
Testing: All auto-import tests passing
- TestDaemonAutoImportAfterGitPull ✓
- TestDaemonAutoImportDataCorruption ✓
- internal/autoimport tests ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add migration for UNIQUE index on external_ref column (bd-897a)
- Add validation for duplicate external_ref in batch imports (bd-7315)
- Add query planner test to verify index usage (bd-f9a1)
- Add concurrent import tests for external_ref (bd-3f6a)
The migration detects existing duplicates and fails gracefully.
Batch imports now reject duplicates with clear error messages.
Tests verify the index is actually used by SQLite query planner.
Amp-Thread-ID: https://ampcode.com/threads/T-45ca66ed-3912-46c4-963c-caa7724a9a2f
Co-authored-by: Amp <amp@ampcode.com>
- Created internal/syncbranch package with validation and env var support
- Added --branch flag to bd init command
- Enhanced bd config get/set to validate sync.branch
- Added BEADS_SYNC_BRANCH environment variable support
- Comprehensive tests for branch name validation
- Supports precedence: env var > database config > empty (current branch)
- Add GetIssueByExternalRef() query function to storage interface and implementations
- Update DetectCollisions() to prioritize external_ref matching over ID matching
- Modify upsertIssues() to handle external_ref matches in import logic
- Add index on external_ref column for performance
- Add comprehensive tests for external_ref matching in both collision detection and import
- Enables re-syncing from external systems (Jira, GitHub, Linear) without duplicates
- Preserves local issues (no external_ref) from being overwritten
- Created ids.go with ValidateIssueIDPrefix, GenerateIssueID, EnsureIDs
- Created issues.go with insertIssue/insertIssues helpers
- Created events_helpers.go with recordCreatedEvent/recordCreatedEvents
- Created dirty_helpers.go with markDirty/markDirtyBatch
- Refactored sqlite.go and batch_ops.go to use new helpers
- Removed duplicate code from hash_ids.go
Amp-Thread-ID: https://ampcode.com/threads/T-b1ab5a16-96de-4e4d-b255-3617055a89eb
Co-authored-by: Amp <amp@ampcode.com>
- Renamed config.json to metadata.json to clarify purpose (database metadata)
- Fixed config.yaml/config.json conflict by making Viper explicitly load only config.yaml
- Added automatic migration from config.json to metadata.json on first read
- Fixed jsonOutput variable shadowing across 22 command files
- Updated bd init to create both metadata.json and config.yaml template
- Fixed 5 failing JSON output tests
- All tests passing
Resolves config file confusion and makes config.yaml work correctly.
Closes#178 (global flags), addresses config issues from #193
Amp-Thread-ID: https://ampcode.com/threads/T-e6ac8192-e18f-4ed7-83bc-4a5986718bb7
Co-authored-by: Amp <amp@ampcode.com>
Simplify uptime calculation to always enforce minimum of 1 second,
even if time.Since returns exactly 0 (can happen on Windows with
coarse timing). This makes the test deterministic.
- Always recompute content_hash in importer to avoid stale hashes from JSONL
- Add .gitignore to test repos to prevent database files from being tracked
- Fix daemon auto-import test to use correct RPC operation ('show' not 'get_issue')
- Set last_import_time metadata in test helper to enable staleness check
- Add filesystem settle delay after git pull in tests
Root cause: CloseIssue updates status but not content_hash, so importer
thought issues were unchanged. Always recomputing content_hash fixes this.
Amp-Thread-ID: https://ampcode.com/threads/T-63ef3a7d-8efe-472d-97ed-6ac95bd8318b
Co-authored-by: Amp <amp@ampcode.com>
The JSON output from bd show now includes the dependency_type field
for both dependencies and dependents, enabling programmatic
differentiation between dependency types (blocks, related,
parent-child, discovered-from).
Implementation approach:
- Added IssueWithDependencyMetadata type with embedded Issue and
DependencyType field
- Extended GetDependenciesWithMetadata and GetDependentsWithMetadata
to include dependency type from SQL JOIN
- Made GetDependencies and GetDependents wrap the WithMetadata
methods for backward compatibility
- Added scanIssuesWithDependencyType helper to handle scanning with
dependency type field
- Updated bd show --json to use WithMetadata methods
Tests added:
- TestGetDependenciesWithMetadata - basic functionality
- TestGetDependentsWithMetadata - dependent retrieval
- TestGetDependenciesWithMetadataEmpty - edge case handling
- TestGetDependenciesWithMetadataMultipleTypes - multiple types
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
- Fix Windows binary path issues (bd.exe vs bd)
- Skip scripttest on Windows (requires Unix shell)
- Skip file lock tests on Windows (platform locking differences)
- Fix registry tests to use USERPROFILE on Windows
- Fix 8 unparam lint warnings by marking unused params with _
All changes are platform-aware and maintain functionality.
Amp-Thread-ID: https://ampcode.com/threads/T-bc27021a-65db-4b64-a3f3-4e8d7bc8aa0d
Co-authored-by: Amp <amp@ampcode.com>
- Add nolint:gosec comments for safe file operations
- G304: File reads from validated/secure paths
- G306/G302: JSONL/error files need 0644 for sharing/debugging
- G204: Subprocess launches with validated arguments
- G104: Deferred file close errors are non-critical
- G115: Safe integer conversions in backoff
- G201: SQL placeholders for IN clause expansion
All warnings are for intentional behavior that is safe in context.
Amp-Thread-ID: https://ampcode.com/threads/T-d78f2780-4709-497f-97b0-035ca8c809e1
Co-authored-by: Amp <amp@ampcode.com>
- Test AddIssueComment basic functionality
- Test GetIssueComments retrieval and ordering
- Test edge cases (empty, nonexistent issues)
- Test dirty flag marking
- Test comment isolation across issues
Improves sqlite package coverage: 69.1% → 70.6%
When using `bd list --json`, each issue now includes:
- `dependency_count`: Number of issues this issue depends on
- `dependent_count`: Number of issues that depend on this issue
This provides quick access to dependency relationship counts without
needing to fetch full dependency lists or run multiple bd show commands.
Performance:
- Uses single bulk query (GetDependencyCounts) instead of N individual queries
- Overhead: ~26% for 500 issues (24ms vs 19ms baseline)
- Avoids N+1 query problem that would have caused 2.2x slowdown
Implementation:
- Added GetDependencyCounts() to Storage interface for bulk counting
- Efficient SQLite query using UNION ALL + GROUP BY
- Memory storage implementation for testing
- Moved IssueWithCounts to types package to avoid duplication
- Both RPC and direct modes use optimized bulk query
Tests:
- Added comprehensive tests for GetDependencyCounts
- Tests cover: normal operation, empty list, nonexistent IDs
- All existing tests continue to pass
Backwards compatible: JSON structure is additive, all original fields preserved.
Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
- update prefix/number parsing to use the last hyphen across utils and nodb paths
- add regression tests covering multi-hyphen prefixes in both packages
- Created config.go with Config struct
- Created daemon.go with Daemon struct and Start/Stop methods
- Created logger.go for logging setup
- Created process.go for lock/PID management
- Created fingerprint.go for database validation
- Created flock_unix.go/flock_windows.go for platform-specific locking
- Created git.go for git operations
Still TODO:
- Implement runGlobalDaemon, startRPCServer, runSyncLoop
- Create sync.go, rpc.go, jsonl.go, validation.go
- Update cmd/bd/daemon.go to use daemonrunner
Part of bd-5f26
When daemon detects multiple .db files (after filtering .backup and vc.db),
it now writes detailed error to .beads/daemon-error file before exiting.
The error file is checked and displayed when:
- Daemon discovery fails to connect
- Auto-start fails to yield a running daemon
- User runs 'bd daemons list'
This makes the error immediately visible without requiring users to check
daemon logs.
Changes:
- cmd/bd/daemon.go: Write daemon-error file on multiple .db detection
- internal/daemon/discovery.go: Read and surface daemon-error in DaemonInfo.Error
- cmd/bd/main.go: Display daemon-error when auto-start fails
Amp-Thread-ID: https://ampcode.com/threads/T-1005a8d1-7a5a-4844-ad2d-2b8a6145825f
Co-authored-by: Amp <amp@ampcode.com>