feat(daemon): Add --global flag for multi-repo support

Implements bd-121: Global daemon with system-wide socket

Changes:
- Add --global flag to daemon command
- Use ~/.beads/bd.sock when --global is set
- Skip git repo validation for global daemon
- Update daemon discovery to check ~/.beads/ as fallback
- Both Go CLI and Python MCP client check global socket
- Update all tests to pass global parameter

Benefits:
- Single daemon serves all repos on system
- No per-repo daemon management needed
- Better resource usage for users with many repos
- Automatic fallback when local daemon not running

Usage:
  bd daemon --global         # Start global daemon
  bd daemon --status --global # Check global status
  bd daemon --stop --global   # Stop global daemon

Related: bd-73 (multi-repo epic)
Amp-Thread-ID: https://ampcode.com/threads/T-ea606216-b886-4af0-bba8-56d000362d01
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Steve Yegge
2025-10-17 22:45:33 -07:00
parent ad8f52eb87
commit 958bbc0853
5 changed files with 126 additions and 35 deletions

View File

@@ -640,7 +640,7 @@ def create_bd_client(
from pathlib import Path
# Check if daemon socket exists before creating client
# Walk up from working_dir to find .beads/bd.sock
# Walk up from working_dir to find .beads/bd.sock, then check global
search_dir = Path(working_dir) if working_dir else Path.cwd()
socket_found = False
@@ -652,16 +652,22 @@ def create_bd_client(
if sock_path.exists():
socket_found = True
break
# Found .beads but no socket - daemon not running
# Found .beads but no socket - check global before giving up
break
# Move up one directory
parent = current.parent
if parent == current:
# Reached filesystem root
# Reached filesystem root - check global
break
current = parent
# If no local socket, check global daemon socket at ~/.beads/bd.sock
if not socket_found:
global_sock_path = Path.home() / ".beads" / "bd.sock"
if global_sock_path.exists():
socket_found = True
if socket_found:
# Daemon is running, use it
client = BdDaemonClient(

View File

@@ -72,6 +72,8 @@ class BdDaemonClient(BdClientBase):
async def _find_socket_path(self) -> str:
"""Find daemon socket path by searching for .beads directory.
Checks local .beads/bd.sock first, then falls back to global ~/.beads/bd.sock.
Returns:
Path to bd.sock file
@@ -90,19 +92,26 @@ class BdDaemonClient(BdClientBase):
sock_path = beads_dir / "bd.sock"
if sock_path.exists():
return str(sock_path)
# Found .beads but no socket - daemon not running
raise DaemonNotRunningError(
f"Daemon socket not found at {sock_path}. Is the daemon running? Try: bd daemon"
)
# Found .beads but no socket - check global before failing
break
# Move up one directory
parent = current.parent
if parent == current:
# Reached filesystem root
raise DaemonNotRunningError(
"No .beads directory found. Initialize with: bd init"
)
# Reached filesystem root - check global
break
current = parent
# Check for global daemon socket at ~/.beads/bd.sock
home = Path.home()
global_sock_path = home / ".beads" / "bd.sock"
if global_sock_path.exists():
return str(global_sock_path)
# No socket found anywhere
raise DaemonNotRunningError(
"Daemon socket not found. Is the daemon running? Try: bd daemon (local) or bd daemon --global"
)
async def _send_request(self, operation: str, args: Dict[str, Any]) -> Dict[str, Any]:
"""Send RPC request to daemon and get response.