refactor: Export FollowRedirect and consolidate duplicate implementations

- Rename followRedirect to FollowRedirect in internal/beads (export it)
- Update doctor/maintenance.go to use beads.FollowRedirect
- Update doctor/fix/common.go to use beads.FollowRedirect
- Remove 66 lines of duplicated code across 3 implementations

This ensures consistent redirect handling with path canonicalization,
chain prevention, and proper error warnings.

🤖 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-29 13:22:54 -08:00
parent ef40856f2f
commit 5560a4243e
4 changed files with 17 additions and 83 deletions

View File

@@ -113,40 +113,7 @@ func isWithinWorkspace(root, candidate string) bool {
// resolveBeadsDir follows .beads/redirect files to find the actual beads directory.
// If no redirect exists, returns the original path unchanged.
// This is a wrapper around beads.FollowRedirect for use within the fix package.
func resolveBeadsDir(beadsDir string) string {
redirectFile := filepath.Join(beadsDir, beads.RedirectFileName)
data, err := os.ReadFile(redirectFile) //nolint:gosec // redirect file path is constructed from known beadsDir
if err != nil {
// No redirect file - use original path
return beadsDir
}
// Parse the redirect target
target := strings.TrimSpace(string(data))
if target == "" {
return beadsDir
}
// Skip comments
lines := strings.Split(target, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "#") {
target = line
break
}
}
// Resolve relative paths from the parent of the .beads directory
if !filepath.IsAbs(target) {
projectRoot := filepath.Dir(beadsDir)
target = filepath.Join(projectRoot, target)
}
// Verify the target exists
if info, err := os.Stat(target); err != nil || !info.IsDir() {
return beadsDir
}
return target
return beads.FollowRedirect(beadsDir)
}

View File

@@ -313,42 +313,9 @@ func CheckCompactionCandidates(path string) DoctorCheck {
// resolveBeadsDir follows a redirect file if present in the beads directory.
// This handles Gas Town's redirect mechanism where .beads/redirect points to
// the actual beads directory location.
// This is a wrapper around beads.FollowRedirect for use within the doctor package.
func resolveBeadsDir(beadsDir string) string {
redirectFile := filepath.Join(beadsDir, "redirect")
data, err := os.ReadFile(redirectFile) //nolint:gosec // redirect file path is constructed from known beadsDir
if err != nil {
// No redirect file - use original path
return beadsDir
}
// Parse the redirect target
target := strings.TrimSpace(string(data))
if target == "" {
return beadsDir
}
// Skip comments
lines := strings.Split(target, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" && !strings.HasPrefix(line, "#") {
target = line
break
}
}
// Resolve relative paths from the parent of the .beads directory
if !filepath.IsAbs(target) {
projectRoot := filepath.Dir(beadsDir)
target = filepath.Join(projectRoot, target)
}
// Verify the target exists
if info, err := os.Stat(target); err != nil || !info.IsDir() {
return beadsDir
}
return target
return beads.FollowRedirect(beadsDir)
}
// CheckPersistentMolIssues detects mol- prefixed issues that should have been ephemeral.