refactor: Split large cmd/bd files to meet 800-line limit (bd-xtf5)
Split 6 files exceeding 800 lines by extracting cohesive function groups: - show.go (1592→578): extracted show_thread.go, close.go, edit.go, update.go - doctor.go (1295→690): extracted doctor_fix.go, doctor_health.go, doctor_pollution.go - sync.go (1201→749): extracted sync_git.go - compact.go (1199→775): extracted compact_tombstone.go, compact_rpc.go - linear.go (1190→641): extracted linear_sync.go, linear_conflict.go - main.go (1148→800): extracted main_help.go, main_errors.go, main_daemon.go All files now under 800-line acceptance criteria. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
119
cmd/bd/main_daemon.go
Normal file
119
cmd/bd/main_daemon.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DaemonStatus captures daemon connection state for the current command
|
||||
type DaemonStatus struct {
|
||||
Mode string `json:"mode"` // "daemon" or "direct"
|
||||
Connected bool `json:"connected"`
|
||||
Degraded bool `json:"degraded"`
|
||||
SocketPath string `json:"socket_path,omitempty"`
|
||||
AutoStartEnabled bool `json:"auto_start_enabled"`
|
||||
AutoStartAttempted bool `json:"auto_start_attempted"`
|
||||
AutoStartSucceeded bool `json:"auto_start_succeeded"`
|
||||
FallbackReason string `json:"fallback_reason,omitempty"` // "none","flag_no_daemon","connect_failed","health_failed","auto_start_disabled","auto_start_failed"
|
||||
Detail string `json:"detail,omitempty"` // short diagnostic
|
||||
Health string `json:"health,omitempty"` // "healthy","degraded","unhealthy"
|
||||
}
|
||||
|
||||
// Fallback reason constants
|
||||
const (
|
||||
FallbackNone = "none"
|
||||
FallbackFlagNoDaemon = "flag_no_daemon"
|
||||
FallbackConnectFailed = "connect_failed"
|
||||
FallbackHealthFailed = "health_failed"
|
||||
FallbackWorktreeSafety = "worktree_safety"
|
||||
cmdDaemon = "daemon"
|
||||
cmdImport = "import"
|
||||
statusHealthy = "healthy"
|
||||
FallbackAutoStartDisabled = "auto_start_disabled"
|
||||
FallbackAutoStartFailed = "auto_start_failed"
|
||||
FallbackDaemonUnsupported = "daemon_unsupported"
|
||||
FallbackWispOperation = "wisp_operation"
|
||||
)
|
||||
|
||||
// Command group IDs for help organization
|
||||
const (
|
||||
GroupMaintenance = "maintenance"
|
||||
GroupIntegrations = "integrations"
|
||||
)
|
||||
|
||||
// signalGasTownActivity writes an activity signal for Gas Town daemon.
|
||||
// This enables exponential backoff based on bd usage detection.
|
||||
// Best-effort: silent on any failure, never affects bd operation.
|
||||
func signalGasTownActivity() {
|
||||
// Determine town root
|
||||
// Priority: GT_ROOT env > detect from cwd path > skip
|
||||
townRoot := os.Getenv("GT_ROOT")
|
||||
if townRoot == "" {
|
||||
// Try to detect from cwd - if under ~/gt/, use that as town root
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
gtRoot := filepath.Join(home, "gt")
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(cwd, gtRoot+string(os.PathSeparator)) {
|
||||
townRoot = gtRoot
|
||||
}
|
||||
}
|
||||
|
||||
if townRoot == "" {
|
||||
return // Not in Gas Town, skip
|
||||
}
|
||||
|
||||
// Ensure daemon directory exists
|
||||
daemonDir := filepath.Join(townRoot, "daemon")
|
||||
if err := os.MkdirAll(daemonDir, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Build command line from os.Args
|
||||
cmdLine := strings.Join(os.Args, " ")
|
||||
|
||||
// Determine actor (use package-level var if set, else fall back to env)
|
||||
actorName := actor
|
||||
if actorName == "" {
|
||||
if bdActor := os.Getenv("BD_ACTOR"); bdActor != "" {
|
||||
actorName = bdActor
|
||||
} else if user := os.Getenv("USER"); user != "" {
|
||||
actorName = user
|
||||
} else {
|
||||
actorName = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Build activity signal
|
||||
activity := struct {
|
||||
LastCommand string `json:"last_command"`
|
||||
Actor string `json:"actor"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}{
|
||||
LastCommand: cmdLine,
|
||||
Actor: actorName,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
data, err := json.Marshal(activity)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Write atomically (write to temp, rename)
|
||||
activityPath := filepath.Join(daemonDir, "activity.json")
|
||||
tmpPath := activityPath + ".tmp"
|
||||
// nolint:gosec // G306: 0644 is appropriate for a status file
|
||||
if err := os.WriteFile(tmpPath, data, 0644); err != nil {
|
||||
return
|
||||
}
|
||||
_ = os.Rename(tmpPath, activityPath)
|
||||
}
|
||||
Reference in New Issue
Block a user