fix: Capture stderr instead of suppressing in command execution
Several files were setting cmd.Stderr = nil, which hides potentially critical error messages: - prime.go: bd prime, gt mail check, and bd show commands now log stderr on failure for debugging - orphans.go: git fsck now includes stderr in error messages - patrol_helpers.go: bd list/show/catalog commands now log stderr Daemon launch cases (up.go, daemon.go, daemon_check.go) correctly use nil for I/O detachment but now have clarifying comments. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -124,14 +124,19 @@ func findOrphanCommits(repoPath string) ([]OrphanCommit, error) {
|
||||
fsckCmd := exec.Command("git", "fsck", "--unreachable", "--no-reflogs")
|
||||
fsckCmd.Dir = repoPath
|
||||
|
||||
var fsckOut bytes.Buffer
|
||||
var fsckOut, fsckErr bytes.Buffer
|
||||
fsckCmd.Stdout = &fsckOut
|
||||
fsckCmd.Stderr = nil // Ignore warnings
|
||||
fsckCmd.Stderr = &fsckErr
|
||||
|
||||
if err := fsckCmd.Run(); err != nil {
|
||||
// git fsck returns non-zero if there are issues, but we still get output
|
||||
// Only fail if we got no output at all
|
||||
if fsckOut.Len() == 0 {
|
||||
// Include stderr in error message for debugging
|
||||
errMsg := strings.TrimSpace(fsckErr.String())
|
||||
if errMsg != "" {
|
||||
return nil, fmt.Errorf("git fsck failed: %w (%s)", err, errMsg)
|
||||
}
|
||||
return nil, fmt.Errorf("git fsck failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
@@ -28,11 +29,15 @@ func findActivePatrol(cfg PatrolConfig) (patrolID, patrolLine string, found bool
|
||||
if cfg.CheckInProgress {
|
||||
cmdList := exec.Command("bd", "--no-daemon", "list", "--status=in_progress", "--type=epic")
|
||||
cmdList.Dir = cfg.BeadsDir
|
||||
var stdoutList bytes.Buffer
|
||||
var stdoutList, stderrList bytes.Buffer
|
||||
cmdList.Stdout = &stdoutList
|
||||
cmdList.Stderr = nil
|
||||
cmdList.Stderr = &stderrList
|
||||
|
||||
if cmdList.Run() == nil {
|
||||
if err := cmdList.Run(); err != nil {
|
||||
if errMsg := strings.TrimSpace(stderrList.String()); errMsg != "" {
|
||||
fmt.Fprintf(os.Stderr, "bd list: %s\n", errMsg)
|
||||
}
|
||||
} else {
|
||||
lines := strings.Split(stdoutList.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, cfg.PatrolMolName) && !strings.Contains(line, "[template]") {
|
||||
@@ -48,11 +53,15 @@ func findActivePatrol(cfg PatrolConfig) (patrolID, patrolLine string, found bool
|
||||
// Check for open patrols with open children (active wisp)
|
||||
cmdOpen := exec.Command("bd", "--no-daemon", "list", "--status=open", "--type=epic")
|
||||
cmdOpen.Dir = cfg.BeadsDir
|
||||
var stdoutOpen bytes.Buffer
|
||||
var stdoutOpen, stderrOpen bytes.Buffer
|
||||
cmdOpen.Stdout = &stdoutOpen
|
||||
cmdOpen.Stderr = nil
|
||||
cmdOpen.Stderr = &stderrOpen
|
||||
|
||||
if cmdOpen.Run() == nil {
|
||||
if err := cmdOpen.Run(); err != nil {
|
||||
if errMsg := strings.TrimSpace(stderrOpen.String()); errMsg != "" {
|
||||
fmt.Fprintf(os.Stderr, "bd list: %s\n", errMsg)
|
||||
}
|
||||
} else {
|
||||
lines := strings.Split(stdoutOpen.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, cfg.PatrolMolName) && !strings.Contains(line, "[template]") {
|
||||
@@ -62,10 +71,14 @@ func findActivePatrol(cfg PatrolConfig) (patrolID, patrolLine string, found bool
|
||||
// Check if this molecule has open children
|
||||
cmdShow := exec.Command("bd", "--no-daemon", "show", molID)
|
||||
cmdShow.Dir = cfg.BeadsDir
|
||||
var stdoutShow bytes.Buffer
|
||||
var stdoutShow, stderrShow bytes.Buffer
|
||||
cmdShow.Stdout = &stdoutShow
|
||||
cmdShow.Stderr = nil
|
||||
if cmdShow.Run() == nil {
|
||||
cmdShow.Stderr = &stderrShow
|
||||
if err := cmdShow.Run(); err != nil {
|
||||
if errMsg := strings.TrimSpace(stderrShow.String()); errMsg != "" {
|
||||
fmt.Fprintf(os.Stderr, "bd show: %s\n", errMsg)
|
||||
}
|
||||
} else {
|
||||
showOutput := stdoutShow.String()
|
||||
// Deacon only checks "- open]", witness/refinery also check "- in_progress]"
|
||||
hasOpenChildren := strings.Contains(showOutput, "- open]")
|
||||
@@ -90,12 +103,16 @@ func autoSpawnPatrol(cfg PatrolConfig) (string, error) {
|
||||
// Find the proto ID for the patrol molecule
|
||||
cmdCatalog := exec.Command("bd", "--no-daemon", "mol", "catalog")
|
||||
cmdCatalog.Dir = cfg.BeadsDir
|
||||
var stdoutCatalog bytes.Buffer
|
||||
var stdoutCatalog, stderrCatalog bytes.Buffer
|
||||
cmdCatalog.Stdout = &stdoutCatalog
|
||||
cmdCatalog.Stderr = nil
|
||||
cmdCatalog.Stderr = &stderrCatalog
|
||||
|
||||
if err := cmdCatalog.Run(); err != nil {
|
||||
return "", fmt.Errorf("failed to list molecule catalog")
|
||||
errMsg := strings.TrimSpace(stderrCatalog.String())
|
||||
if errMsg != "" {
|
||||
return "", fmt.Errorf("failed to list molecule catalog: %s", errMsg)
|
||||
}
|
||||
return "", fmt.Errorf("failed to list molecule catalog: %w", err)
|
||||
}
|
||||
|
||||
// Find patrol molecule in catalog
|
||||
|
||||
@@ -429,12 +429,16 @@ func runBdPrime(workDir string) {
|
||||
cmd := exec.Command("bd", "prime")
|
||||
cmd.Dir = workDir
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = nil // Ignore stderr
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// Silently skip if bd prime fails (beads might not be available)
|
||||
// Skip if bd prime fails (beads might not be available)
|
||||
// But log stderr if present for debugging
|
||||
if errMsg := strings.TrimSpace(stderr.String()); errMsg != "" {
|
||||
fmt.Fprintf(os.Stderr, "bd prime: %s\n", errMsg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -521,12 +525,15 @@ func runMailCheckInject(workDir string) {
|
||||
cmd := exec.Command("gt", "mail", "check", "--inject")
|
||||
cmd.Dir = workDir
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = nil // Ignore stderr
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// Silently skip if mail check fails
|
||||
// Skip if mail check fails, but log stderr for debugging
|
||||
if errMsg := strings.TrimSpace(stderr.String()); errMsg != "" {
|
||||
fmt.Fprintf(os.Stderr, "gt mail check: %s\n", errMsg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -947,10 +954,16 @@ func checkSlungWork(ctx RoleContext) bool {
|
||||
// Show bead preview using bd show
|
||||
fmt.Println("**Bead details:**")
|
||||
cmd := exec.Command("bd", "show", hookedBead.ID)
|
||||
var stdout bytes.Buffer
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = nil
|
||||
if cmd.Run() == nil {
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
if errMsg := strings.TrimSpace(stderr.String()); errMsg != "" {
|
||||
fmt.Fprintf(os.Stderr, " bd show %s: %s\n", hookedBead.ID, errMsg)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, " bd show %s: %v\n", hookedBead.ID, err)
|
||||
}
|
||||
} else {
|
||||
lines := strings.Split(stdout.String(), "\n")
|
||||
maxLines := 15
|
||||
if len(lines) > maxLines {
|
||||
|
||||
@@ -196,6 +196,7 @@ func ensureDaemon(townRoot string) error {
|
||||
|
||||
cmd := exec.Command(gtPath, "daemon", "run")
|
||||
cmd.Dir = townRoot
|
||||
// Detach from parent I/O for background daemon (uses its own logging)
|
||||
cmd.Stdin = nil
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
|
||||
@@ -73,7 +73,7 @@ func (c *DaemonCheck) Fix(ctx *CheckContext) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start daemon in background
|
||||
// Start daemon in background (detach from parent I/O - daemon uses its own logging)
|
||||
cmd := exec.Command(gtPath, "daemon", "run")
|
||||
cmd.Dir = ctx.TownRoot
|
||||
cmd.Stdin = nil
|
||||
|
||||
Reference in New Issue
Block a user