feat: add context optimization features for AI agents (#297)
* feat: add bd prime and setup commands for AI agent integration This commit consolidates context optimization features for AI agents: ## New Commands **bd prime** - AI-optimized workflow context injection - Outputs ~1-2k tokens of workflow context - Context-aware: adapts to MCP vs CLI mode - MCP mode: minimal reminders (~500 tokens) - CLI mode: full command reference (~1-2k tokens) - Warns against TodoWrite tool and markdown TODOs - Designed for SessionStart/PreCompact hooks **bd setup claude** - Claude Code integration installer - Installs hooks via JSON configuration (not file scripts) - Supports --project for project-only installation - Supports --check to verify installation - Supports --remove to uninstall hooks - Idempotent (safe to run multiple times) - Merges with existing settings **bd setup cursor** - Cursor IDE integration installer - Creates .cursor/rules/beads.mdc with workflow rules - Simplified implementation (just overwrites file) ## bd doctor Enhancements - New: CheckClaude() verifies Claude Code integration - Detects plugin, MCP server, and hooks installation - Provides actionable fix suggestions - Extracted legacy pattern detection to doctor/legacy.go - Detects JSONL-only mode and warns about legacy issues.jsonl ## Core Improvements - FindBeadsDir() utility for cross-platform .beads/ discovery - Works in JSONL-only mode (no database required) - Sorted noDbCommands alphabetically (one per line for easy diffs) ## Testing - Unit tests for setup command hook manipulation - Tests for idempotency, adding/removing hooks - All tests passing ## Documentation - cmd/bd/doctor/claude.md - Documents why beads doesn't use Claude Skills - commands/prime.md - Slash command for bd prime - Fixed G304 gosec warnings with nosec comments ## Token Efficiency The bd prime approach reduces AI context usage dramatically: - MCP mode: ~500 tokens (vs ~10.5k for full MCP tool scan) - CLI mode: ~1-2k tokens - 80-99% reduction in standing context overhead * fix: resolve linting errors in setup utils and remove obsolete test - Add error check for tmpFile.Close() in setup/utils.go to fix golangci-lint G104 - Remove TestCheckMultipleJSONLFiles test that referenced deleted checkMultipleJSONLFiles function Fixes golangci-lint errcheck violations introduced in the bd prime/setup feature.
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/beads/cmd/bd/doctor"
|
||||
"github.com/steveyegge/beads/internal/beads"
|
||||
"github.com/steveyegge/beads/internal/configfile"
|
||||
"github.com/steveyegge/beads/internal/daemon"
|
||||
@@ -156,10 +157,10 @@ func runDiagnostics(path string) doctorResult {
|
||||
result.OverallOK = false
|
||||
}
|
||||
|
||||
// Check 6: Multiple JSONL files
|
||||
multiJSONLCheck := checkMultipleJSONLFiles(path)
|
||||
result.Checks = append(result.Checks, multiJSONLCheck)
|
||||
if multiJSONLCheck.Status == statusWarning || multiJSONLCheck.Status == statusError {
|
||||
// Check 6: Legacy JSONL filename (issues.jsonl vs beads.jsonl)
|
||||
jsonlCheck := convertDoctorCheck(doctor.CheckLegacyJSONLFilename(path))
|
||||
result.Checks = append(result.Checks, jsonlCheck)
|
||||
if jsonlCheck.Status == statusWarning || jsonlCheck.Status == statusError {
|
||||
result.OverallOK = false
|
||||
}
|
||||
|
||||
@@ -191,9 +192,30 @@ func runDiagnostics(path string) doctorResult {
|
||||
result.OverallOK = false
|
||||
}
|
||||
|
||||
// Check 11: Claude integration
|
||||
claudeCheck := convertDoctorCheck(doctor.CheckClaude())
|
||||
result.Checks = append(result.Checks, claudeCheck)
|
||||
// Don't fail overall check for missing Claude integration, just warn
|
||||
|
||||
// Check 12: Legacy beads slash commands in documentation
|
||||
legacyDocsCheck := convertDoctorCheck(doctor.CheckLegacyBeadsSlashCommands(path))
|
||||
result.Checks = append(result.Checks, legacyDocsCheck)
|
||||
// Don't fail overall check for legacy docs, just warn
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// convertDoctorCheck converts doctor package check to main package check
|
||||
func convertDoctorCheck(dc doctor.DoctorCheck) doctorCheck {
|
||||
return doctorCheck{
|
||||
Name: dc.Name,
|
||||
Status: dc.Status,
|
||||
Message: dc.Message,
|
||||
Detail: dc.Detail,
|
||||
Fix: dc.Fix,
|
||||
}
|
||||
}
|
||||
|
||||
func checkInstallation(path string) doctorCheck {
|
||||
beadsDir := filepath.Join(path, ".beads")
|
||||
if _, err := os.Stat(beadsDir); os.IsNotExist(err) {
|
||||
@@ -231,8 +253,20 @@ func checkDatabaseVersion(path string) doctorCheck {
|
||||
// Check if database file exists
|
||||
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
|
||||
// Check if JSONL exists (--no-db mode)
|
||||
jsonlPath := filepath.Join(beadsDir, "issues.jsonl")
|
||||
if _, err := os.Stat(jsonlPath); err == nil {
|
||||
// Check both canonical (beads.jsonl) and legacy (issues.jsonl) names
|
||||
beadsJSONL := filepath.Join(beadsDir, "beads.jsonl")
|
||||
issuesJSONL := filepath.Join(beadsDir, "issues.jsonl")
|
||||
|
||||
if _, err := os.Stat(beadsJSONL); err == nil {
|
||||
return doctorCheck{
|
||||
Name: "Database",
|
||||
Status: statusOK,
|
||||
Message: "JSONL-only mode",
|
||||
Detail: "Using beads.jsonl (no SQLite database)",
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(issuesJSONL); err == nil {
|
||||
return doctorCheck{
|
||||
Name: "Database",
|
||||
Status: statusOK,
|
||||
@@ -626,42 +660,6 @@ func checkMultipleDatabases(path string) doctorCheck {
|
||||
}
|
||||
}
|
||||
|
||||
func checkMultipleJSONLFiles(path string) doctorCheck {
|
||||
beadsDir := filepath.Join(path, ".beads")
|
||||
|
||||
var jsonlFiles []string
|
||||
for _, name := range []string{"issues.jsonl", "beads.jsonl"} {
|
||||
jsonlPath := filepath.Join(beadsDir, name)
|
||||
if _, err := os.Stat(jsonlPath); err == nil {
|
||||
jsonlFiles = append(jsonlFiles, name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(jsonlFiles) == 0 {
|
||||
return doctorCheck{
|
||||
Name: "JSONL Files",
|
||||
Status: statusOK,
|
||||
Message: "No JSONL files found (database-only mode)",
|
||||
}
|
||||
}
|
||||
|
||||
if len(jsonlFiles) == 1 {
|
||||
return doctorCheck{
|
||||
Name: "JSONL Files",
|
||||
Status: statusOK,
|
||||
Message: fmt.Sprintf("Using %s", jsonlFiles[0]),
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple JSONL files found
|
||||
return doctorCheck{
|
||||
Name: "JSONL Files",
|
||||
Status: statusWarning,
|
||||
Message: fmt.Sprintf("Multiple JSONL files found: %s", strings.Join(jsonlFiles, ", ")),
|
||||
Fix: "Standardize on one JSONL file (issues.jsonl recommended). Delete or rename the other.",
|
||||
}
|
||||
}
|
||||
|
||||
func checkDaemonStatus(path string) doctorCheck {
|
||||
// Normalize path for reliable comparison (handles symlinks)
|
||||
wsNorm, err := filepath.EvalSymlinks(path)
|
||||
|
||||
Reference in New Issue
Block a user