Refactor: Introduce CommandContext to consolidate global variables (bd-qobn)
This addresses the code smell of 20+ global variables in main.go by: 1. Creating CommandContext struct in context.go that groups all runtime state: - Configuration (DBPath, Actor, JSONOutput, etc.) - Runtime state (Store, DaemonClient, HookRunner, etc.) - Auto-flush/import state - Version tracking - Profiling handles 2. Adding accessor functions (getStore, getActor, getDaemonClient, etc.) that provide backward-compatible access to the state while allowing gradual migration to CommandContext. 3. Updating direct_mode.go to demonstrate the migration pattern using accessor functions instead of direct global access. 4. Adding test isolation helpers (ensureCleanGlobalState, enableTestModeGlobals) to prevent test interference when multiple tests manipulate global state. Benefits: - Reduces global count from 20+ to 1 (cmdCtx) - Better testability (can inject mock contexts) - Clearer state ownership (all state in one place) - Thread safety (mutexes grouped with the data they protect) Note: Two pre-existing test failures (TestTrackBdVersion_*) are unrelated to this change and fail both with and without these modifications. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
||||
// ensureDirectMode makes sure the CLI is operating in direct-storage mode.
|
||||
// If the daemon is active, it is cleanly disconnected and the shared store is opened.
|
||||
func ensureDirectMode(reason string) error {
|
||||
if daemonClient != nil {
|
||||
if getDaemonClient() != nil {
|
||||
if err := fallbackToDirectMode(reason); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,20 +30,22 @@ func fallbackToDirectMode(reason string) error {
|
||||
|
||||
// disableDaemonForFallback closes the daemon client and updates status metadata.
|
||||
func disableDaemonForFallback(reason string) {
|
||||
if daemonClient != nil {
|
||||
_ = daemonClient.Close()
|
||||
daemonClient = nil
|
||||
if client := getDaemonClient(); client != nil {
|
||||
_ = client.Close()
|
||||
setDaemonClient(nil)
|
||||
}
|
||||
|
||||
daemonStatus.Mode = "direct"
|
||||
daemonStatus.Connected = false
|
||||
daemonStatus.Degraded = true
|
||||
ds := getDaemonStatus()
|
||||
ds.Mode = "direct"
|
||||
ds.Connected = false
|
||||
ds.Degraded = true
|
||||
if reason != "" {
|
||||
daemonStatus.Detail = reason
|
||||
ds.Detail = reason
|
||||
}
|
||||
if daemonStatus.FallbackReason == FallbackNone {
|
||||
daemonStatus.FallbackReason = FallbackDaemonUnsupported
|
||||
if ds.FallbackReason == FallbackNone {
|
||||
ds.FallbackReason = FallbackDaemonUnsupported
|
||||
}
|
||||
setDaemonStatus(ds)
|
||||
|
||||
if reason != "" {
|
||||
debug.Logf("Debug: %s\n", reason)
|
||||
@@ -52,16 +54,18 @@ func disableDaemonForFallback(reason string) {
|
||||
|
||||
// ensureStoreActive guarantees that a local SQLite store is initialized and tracked.
|
||||
func ensureStoreActive() error {
|
||||
storeMutex.Lock()
|
||||
active := storeActive && store != nil
|
||||
storeMutex.Unlock()
|
||||
lockStore()
|
||||
active := isStoreActive() && getStore() != nil
|
||||
unlockStore()
|
||||
if active {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dbPath == "" {
|
||||
path := getDBPath()
|
||||
if path == "" {
|
||||
if found := beads.FindDatabasePath(); found != "" {
|
||||
dbPath = found
|
||||
setDBPath(found)
|
||||
path = found
|
||||
} else {
|
||||
// Check if this is a JSONL-only project
|
||||
beadsDir := beads.FindBeadsDir()
|
||||
@@ -85,23 +89,23 @@ func ensureStoreActive() error {
|
||||
}
|
||||
}
|
||||
|
||||
sqlStore, err := sqlite.New(rootCtx, dbPath)
|
||||
sqlStore, err := sqlite.New(getRootContext(), path)
|
||||
if err != nil {
|
||||
// Check for fresh clone scenario
|
||||
if isFreshCloneError(err) {
|
||||
beadsDir := filepath.Dir(dbPath)
|
||||
beadsDir := filepath.Dir(path)
|
||||
handleFreshCloneError(err, beadsDir)
|
||||
return fmt.Errorf("database not initialized")
|
||||
}
|
||||
return fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
|
||||
storeMutex.Lock()
|
||||
store = sqlStore
|
||||
storeActive = true
|
||||
storeMutex.Unlock()
|
||||
lockStore()
|
||||
setStore(sqlStore)
|
||||
setStoreActive(true)
|
||||
unlockStore()
|
||||
|
||||
if autoImportEnabled {
|
||||
if isAutoImportEnabled() {
|
||||
autoImportIfNewer()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user