Fix GH#367: bd import defaulting to stdin is confusing

Changes:
1. Add TTY detection to bd import - prevents silent hang when run
   interactively without arguments. Shows helpful usage message instead.
2. Fix misleading error messages - change "Run 'bd import'" to
   "Run 'bd sync --import-only'" or explicit file path throughout.

Technical details:
- Added golang.org/x/term dependency for IsTerminal()
- When stdin is a TTY and no -i flag: show usage and exit
- When stdin is piped: works as before (supports script pipelines)
- Preserved all legitimate stdin uses:
  * python gh2jsonl.py --repo owner/repo | bd import
  * python md2jsonl.py feature.md | bd import
  * git show HEAD:.beads/beads.jsonl | bd import -i /dev/stdin

Updated error messages in:
- cmd/bd/staleness.go - main "out of sync" error
- cmd/bd/sync.go - merge completion suggestions
- internal/rpc/server_export_import_auto.go - daemon warnings

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-23 11:15:34 -08:00
parent 5b77ddb215
commit 012bed1068
6 changed files with 22 additions and 6 deletions

View File

@@ -16,6 +16,7 @@ import (
"github.com/steveyegge/beads/internal/debug"
"github.com/steveyegge/beads/internal/storage/sqlite"
"github.com/steveyegge/beads/internal/types"
"golang.org/x/term"
)
var importCmd = &cobra.Command{
@@ -71,6 +72,18 @@ NOTE: Import requires direct database access and does not work with daemon mode.
orphanHandling, _ := cmd.Flags().GetString("orphan-handling")
force, _ := cmd.Flags().GetBool("force")
// Check if stdin is being used interactively (not piped)
if input == "" && term.IsTerminal(int(os.Stdin.Fd())) {
fmt.Fprintf(os.Stderr, "Error: No input specified.\n\n")
fmt.Fprintf(os.Stderr, "Usage:\n")
fmt.Fprintf(os.Stderr, " bd import -i .beads/beads.jsonl # Import from file\n")
fmt.Fprintf(os.Stderr, " bd import -i .beads/beads.jsonl --dry-run # Preview changes\n")
fmt.Fprintf(os.Stderr, " cat data.jsonl | bd import # Import from pipe\n")
fmt.Fprintf(os.Stderr, " bd sync --import-only # Import latest JSONL\n\n")
fmt.Fprintf(os.Stderr, "For more information, run: bd import --help\n")
os.Exit(1)
}
// Open input
in := os.Stdin
if input != "" {

View File

@@ -44,14 +44,14 @@ func ensureDatabaseFresh(ctx context.Context) error {
// Database is stale - refuse to operate
return fmt.Errorf(
"Database out of sync with JSONL. Run 'bd import' first.\n\n"+
"Database out of sync with JSONL. Run 'bd sync --import-only' to fix.\n\n"+
"The JSONL file has been updated (e.g., after 'git pull') but the database\n"+
"hasn't been imported yet. This would cause you to see stale/incomplete data.\n\n"+
"To fix:\n"+
" bd import -i .beads/beads.jsonl # Import JSONL updates to database\n\n"+
" bd sync --import-only # Import JSONL updates to database\n"+
" bd import -i .beads/beads.jsonl # Alternative: specify file explicitly\n\n"+
"If in a sandboxed environment (e.g., Codex) where daemon can't be stopped:\n"+
" bd --sandbox ready # Use direct mode (no daemon)\n"+
" bd import --force # Force metadata update\n"+
" bd ready --allow-stale # Skip staleness check (use with caution)\n\n"+
"Or use daemon mode (auto-imports on every operation):\n"+
" bd daemon start\n"+

View File

@@ -862,7 +862,7 @@ func mergeSyncBranch(ctx context.Context, dryRun bool) error {
// Suggest next steps
fmt.Println("\nNext steps:")
fmt.Println("1. Review the merged changes")
fmt.Println("2. Run 'bd import' to sync the database with merged JSONL")
fmt.Println("2. Run 'bd sync --import-only' to sync the database with merged JSONL")
fmt.Println("3. Run 'bd sync' to push changes to remote")
return nil