fix(doctor): detect plugin and hooks in project-level settings (#1091)

* fix(doctor): detect beads plugin in project-level settings

isBeadsPluginInstalled() now checks project-level settings files
(.claude/settings.json and .claude/settings.local.json) in addition
to user-level settings (~/.claude/settings.json).

This fixes the contradictory bd doctor output where the plugin check
passes but the integration check warns "Not configured" when the
plugin is enabled at project scope.

Fixes #1090

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(doctor): detect Claude hooks in project-level settings.json

hasClaudeHooks() was missing .claude/settings.json - it only checked
.claude/settings.local.json for project-level hooks.

Now checks all three locations:
- ~/.claude/settings.json (user-level)
- .claude/settings.json (project-level)
- .claude/settings.local.json (project-level, gitignored)

Also uses filepath.Join consistently for cross-platform compatibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
James MacAulay
2026-01-14 23:51:16 -05:00
committed by GitHub
parent 31239495f1
commit 12e0caf90e
2 changed files with 158 additions and 6 deletions
+31 -6
View File
@@ -81,15 +81,39 @@ func CheckClaude() DoctorCheck {
}
}
// isBeadsPluginInstalled checks if beads plugin is enabled in Claude Code
// isBeadsPluginInstalled checks if beads plugin is enabled in Claude Code.
// It checks user-level (~/.claude/settings.json) and project-level settings
// (.claude/settings.json and .claude/settings.local.json).
func isBeadsPluginInstalled() bool {
home, err := os.UserHomeDir()
if err != nil {
return false
}
settingsPath := filepath.Join(home, ".claude/settings.json")
data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from user home dir, not user input
// Check user-level settings
userSettings := filepath.Join(home, ".claude", "settings.json")
if checkPluginInSettings(userSettings) {
return true
}
// Check project-level settings
projectSettings := filepath.Join(".claude", "settings.json")
if checkPluginInSettings(projectSettings) {
return true
}
// Check project-level local settings (gitignored)
projectLocalSettings := filepath.Join(".claude", "settings.local.json")
if checkPluginInSettings(projectLocalSettings) {
return true
}
return false
}
// checkPluginInSettings checks if beads plugin is enabled in a settings file
func checkPluginInSettings(settingsPath string) bool {
data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from known safe locations, not user input
if err != nil {
return false
}
@@ -159,10 +183,11 @@ func hasClaudeHooks() bool {
return false
}
globalSettings := filepath.Join(home, ".claude/settings.json")
projectSettings := ".claude/settings.local.json"
globalSettings := filepath.Join(home, ".claude", "settings.json")
projectSettings := filepath.Join(".claude", "settings.json")
projectLocalSettings := filepath.Join(".claude", "settings.local.json")
return hasBeadsHooks(globalSettings) || hasBeadsHooks(projectSettings)
return hasBeadsHooks(globalSettings) || hasBeadsHooks(projectSettings) || hasBeadsHooks(projectLocalSettings)
}
// hasBeadsHooks checks if a settings file has bd prime hooks