feat: show redirect info in bd prime output (bd-kblo)

When beads is redirected (e.g., in Gas Town crew clones), bd prime now
shows a notice about the redirect. This helps agents understand why
they share issues with other clones.

CLI mode shows:
> ⚠️ **Redirected**: Local .beads → /path/to/target/.beads
> You share issues with other clones using this redirect.

MCP mode shows:
**Note**: Beads redirected to /path/to/target/.beads (shared with other clones)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-27 21:33:01 -08:00
parent aa3c4cb3eb
commit ba1c9d6b17
3 changed files with 106 additions and 2 deletions

View File

@@ -50,6 +50,15 @@ func FindAllDatabases() []DatabaseInfo {
return beads.FindAllDatabases()
}
// RedirectInfo contains information about a beads directory redirect
type RedirectInfo = beads.RedirectInfo
// GetRedirectInfo checks if the current beads directory is redirected.
// Returns RedirectInfo with IsRedirected=true if a redirect is active.
func GetRedirectInfo() RedirectInfo {
return beads.GetRedirectInfo()
}
// Core types from internal/types
type (
Issue = types.Issue

View File

@@ -126,6 +126,22 @@ var isEphemeralBranch = func() bool {
return err != nil
}
// getRedirectNotice returns a notice string if beads is redirected
func getRedirectNotice(verbose bool) string {
redirectInfo := beads.GetRedirectInfo()
if !redirectInfo.IsRedirected {
return ""
}
if verbose {
return fmt.Sprintf(`> ⚠️ **Redirected**: Local .beads → %s
> You share issues with other clones using this redirect.
`, redirectInfo.TargetDir)
}
return fmt.Sprintf("**Note**: Beads redirected to %s (shared with other clones)\n\n", redirectInfo.TargetDir)
}
// outputPrimeContext outputs workflow context in markdown format
func outputPrimeContext(w io.Writer, mcpMode bool, stealthMode bool) error {
if mcpMode {
@@ -151,9 +167,11 @@ func outputMCPContext(w io.Writer, stealthMode bool) error {
closeProtocol = "Before saying \"done\": git status → git add → bd sync → git commit → bd sync → git push"
}
redirectNotice := getRedirectNotice(false)
context := `# Beads Issue Tracker Active
# 🚨 SESSION CLOSE PROTOCOL 🚨
` + redirectNotice + `# 🚨 SESSION CLOSE PROTOCOL 🚨
` + closeProtocol + `
@@ -238,12 +256,14 @@ bd sync # Push to remote
` + "```"
}
redirectNotice := getRedirectNotice(true)
context := `# Beads Workflow Context
> **Context Recovery**: Run ` + "`bd prime`" + ` after compaction, clear, or new session
> Hooks auto-call this in Claude Code when .beads/ detected
# 🚨 SESSION CLOSE PROTOCOL 🚨
` + redirectNotice + `# 🚨 SESSION CLOSE PROTOCOL 🚨
**CRITICAL**: Before saying "done" or "complete", you MUST run this checklist:

View File

@@ -93,6 +93,81 @@ func followRedirect(beadsDir string) string {
return target
}
// RedirectInfo contains information about a beads directory redirect.
type RedirectInfo struct {
// IsRedirected is true if the local .beads has a redirect file
IsRedirected bool
// LocalDir is the local .beads directory (the one with the redirect file)
LocalDir string
// TargetDir is the actual .beads directory being used (after following redirect)
TargetDir string
}
// GetRedirectInfo checks if the current beads directory is redirected.
// It searches for the local .beads/ directory and checks if it contains a redirect file.
// Returns RedirectInfo with IsRedirected=true if a redirect is active.
func GetRedirectInfo() RedirectInfo {
info := RedirectInfo{}
// Find the local .beads directory without following redirects
localBeadsDir := findLocalBeadsDir()
if localBeadsDir == "" {
return info
}
info.LocalDir = localBeadsDir
// Check if this directory has a redirect file
redirectFile := filepath.Join(localBeadsDir, RedirectFileName)
if _, err := os.Stat(redirectFile); err != nil {
// No redirect file
return info
}
// There's a redirect - find the target
targetDir := followRedirect(localBeadsDir)
if targetDir == localBeadsDir {
// Redirect file exists but failed to resolve (invalid target)
return info
}
info.IsRedirected = true
info.TargetDir = targetDir
return info
}
// findLocalBeadsDir finds the local .beads directory without following redirects.
// This is used to detect if a redirect is configured.
func findLocalBeadsDir() string {
// Check BEADS_DIR environment variable first
if beadsDir := os.Getenv("BEADS_DIR"); beadsDir != "" {
return utils.CanonicalizePath(beadsDir)
}
// Check for worktree - use main repo's .beads
mainRepoRoot, err := git.GetMainRepoRoot()
if err == nil && mainRepoRoot != "" {
beadsDir := filepath.Join(mainRepoRoot, ".beads")
if info, err := os.Stat(beadsDir); err == nil && info.IsDir() {
return beadsDir
}
}
// Walk up directory tree
cwd, err := os.Getwd()
if err != nil {
return ""
}
for dir := cwd; dir != "/" && dir != "."; dir = filepath.Dir(dir) {
beadsDir := filepath.Join(dir, ".beads")
if info, err := os.Stat(beadsDir); err == nil && info.IsDir() {
return beadsDir
}
}
return ""
}
// findDatabaseInBeadsDir searches for a database file within a .beads directory.
// It implements the standard search order:
// 1. Check config.json first (single source of truth)