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:
9
beads.go
9
beads.go
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user