Merge GH#532
This commit is contained in:
@@ -25,6 +25,12 @@ if [ ! -d .beads ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Skip if bd sync is already in progress (GH#532: prevents circular error)
|
||||
# bd sync sets this env var when pushing from worktree
|
||||
if [ -n "$BD_SYNC_IN_PROGRESS" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Optionally flush pending bd changes so they surface in JSONL
|
||||
# This prevents the race where a debounced flush lands after the check
|
||||
if command -v bd >/dev/null 2>&1; then
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
117
cmd/bd/init.go
117
cmd/bd/init.go
@@ -423,6 +423,15 @@ With --stealth: configures global git settings for invisible beads usage:
|
||||
}
|
||||
}
|
||||
|
||||
// Add "landing the plane" instructions to AGENTS.md and @AGENTS.md
|
||||
// Skip in stealth mode (user wants invisible setup) and quiet mode (suppress all output)
|
||||
if !stealth {
|
||||
if err := addLandingThePlaneInstructions(!quiet); err != nil && !quiet {
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
fmt.Fprintf(os.Stderr, "%s Failed to add landing-the-plane instructions: %v\n", yellow("⚠"), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Skip output if quiet mode
|
||||
if quiet {
|
||||
return
|
||||
@@ -1550,6 +1559,114 @@ Aborting.`, yellow("⚠"), dbPath, cyan("bd list"), prefix)
|
||||
}
|
||||
|
||||
|
||||
// landingThePlaneSection is the "landing the plane" instructions for AI agents
|
||||
// This gets appended to AGENTS.md and @AGENTS.md during bd init
|
||||
const landingThePlaneSection = `
|
||||
## Landing the Plane (Session Completion)
|
||||
|
||||
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until ` + "`git push`" + ` succeeds.
|
||||
|
||||
**MANDATORY WORKFLOW:**
|
||||
|
||||
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
||||
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
||||
3. **Update issue status** - Close finished work, update in-progress items
|
||||
4. **PUSH TO REMOTE** - This is MANDATORY:
|
||||
` + "```bash" + `
|
||||
git pull --rebase
|
||||
bd sync
|
||||
git push
|
||||
git status # MUST show "up to date with origin"
|
||||
` + "```" + `
|
||||
5. **Clean up** - Clear stashes, prune remote branches
|
||||
6. **Verify** - All changes committed AND pushed
|
||||
7. **Hand off** - Provide context for next session
|
||||
|
||||
**CRITICAL RULES:**
|
||||
- Work is NOT complete until ` + "`git push`" + ` succeeds
|
||||
- NEVER stop before pushing - that leaves work stranded locally
|
||||
- NEVER say "ready to push when you are" - YOU must push
|
||||
- If push fails, resolve and retry until it succeeds
|
||||
`
|
||||
|
||||
// addLandingThePlaneInstructions adds "landing the plane" instructions to AGENTS.md and @AGENTS.md
|
||||
func addLandingThePlaneInstructions(verbose bool) error {
|
||||
// Files to update (AGENTS.md and @AGENTS.md for web Claude)
|
||||
agentFiles := []string{"AGENTS.md", "@AGENTS.md"}
|
||||
|
||||
for _, filename := range agentFiles {
|
||||
if err := updateAgentFile(filename, verbose); err != nil {
|
||||
// Non-fatal - continue with other files
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to update %s: %v\n", filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateAgentFile creates or updates an agent instructions file with landing the plane section
|
||||
func updateAgentFile(filename string, verbose bool) error {
|
||||
// Check if file exists
|
||||
content, err := os.ReadFile(filename)
|
||||
if os.IsNotExist(err) {
|
||||
// File doesn't exist - create it with basic structure
|
||||
newContent := fmt.Sprintf(`# Agent Instructions
|
||||
|
||||
This project uses **bd** (beads) for issue tracking. Run ` + "`bd onboard`" + ` to get started.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
` + "```bash" + `
|
||||
bd ready # Find available work
|
||||
bd show <id> # View issue details
|
||||
bd update <id> --status in_progress # Claim work
|
||||
bd close <id> # Complete work
|
||||
bd sync # Sync with git
|
||||
` + "```" + `
|
||||
%s
|
||||
`, landingThePlaneSection)
|
||||
|
||||
// #nosec G306 - markdown needs to be readable
|
||||
if err := os.WriteFile(filename, []byte(newContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create %s: %w", filename, err)
|
||||
}
|
||||
if verbose {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf(" %s Created %s with landing-the-plane instructions\n", green("✓"), filename)
|
||||
}
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to read %s: %w", filename, err)
|
||||
}
|
||||
|
||||
// File exists - check if it already has landing the plane section
|
||||
if strings.Contains(string(content), "Landing the Plane") {
|
||||
if verbose {
|
||||
fmt.Printf(" %s already has landing-the-plane instructions\n", filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Append the landing the plane section
|
||||
newContent := string(content)
|
||||
if !strings.HasSuffix(newContent, "\n") {
|
||||
newContent += "\n"
|
||||
}
|
||||
newContent += landingThePlaneSection
|
||||
|
||||
// #nosec G306 - markdown needs to be readable
|
||||
if err := os.WriteFile(filename, []byte(newContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to update %s: %w", filename, err)
|
||||
}
|
||||
if verbose {
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf(" %s Added landing-the-plane instructions to %s\n", green("✓"), filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupClaudeSettings creates or updates .claude/settings.local.json with onboard instruction
|
||||
func setupClaudeSettings(verbose bool) error {
|
||||
claudeDir := ".claude"
|
||||
|
||||
@@ -23,20 +23,12 @@ if ! command -v bd >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if we're in a bd workspace with an actual database
|
||||
# Just having a .beads directory isn't enough - it must be properly initialized
|
||||
# Check if we're in a bd workspace
|
||||
if [ ! -d .beads ]; then
|
||||
# Not a bd workspace, nothing to do
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Verify beads is actually initialized (has database or config)
|
||||
# This handles the case where .beads was removed from git but directory lingers
|
||||
if [ ! -f .beads/beads.db ] && [ ! -f .beads/config.yaml ] && [ ! -f .beads/issues.jsonl ]; then
|
||||
# Directory exists but beads not initialized, nothing to do
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if sync-branch is configured in config.yaml or env var
|
||||
# If so, .beads changes go to a separate branch via worktree, not the current branch
|
||||
SYNC_BRANCH="${BEADS_SYNC_BRANCH:-}"
|
||||
@@ -54,36 +46,18 @@ fi
|
||||
# Flush pending changes to JSONL
|
||||
# Use --flush-only to skip git operations (we're already in a git hook)
|
||||
# Suppress output unless there's an error
|
||||
<<<<<<< HEAD
|
||||
# Note: We don't block commits on flush failure - beads issues shouldn't prevent code commits
|
||||
if ! bd sync --flush-only >/dev/null 2>&1; then
|
||||
echo "Warning: Failed to flush bd changes to JSONL" >&2
|
||||
echo "Run 'bd sync --flush-only' manually to diagnose" >&2
|
||||
# Continue with commit - don't block code changes due to beads issues
|
||||
=======
|
||||
# Note: We warn but don't fail - this allows commits to proceed even if
|
||||
# beads has issues (e.g., user removed .beads from their branch)
|
||||
if ! bd sync --flush-only >/dev/null 2>&1; then
|
||||
echo "Warning: Failed to flush bd changes to JSONL" >&2
|
||||
echo "Run 'bd sync --flush-only' manually to diagnose" >&2
|
||||
# Don't block the commit - user may have removed beads or have other issues
|
||||
>>>>>>> origin/bd-l0pg-slit
|
||||
fi
|
||||
|
||||
# Stage all tracked JSONL files (issues.jsonl is canonical, beads.jsonl for backward compat, deletions.jsonl for deletion propagation)
|
||||
# For worktrees, .beads is in the main repo's working tree, not the worktree,
|
||||
# so we can't use git add. Skip staging for worktrees.
|
||||
if [ "$(git rev-parse --git-dir)" = "$(git rev-parse --git-common-dir)" ]; then
|
||||
# Regular repo: files are in the working tree, safe to add
|
||||
# git add is harmless if file doesn't exist
|
||||
for f in .beads/beads.jsonl .beads/issues.jsonl .beads/deletions.jsonl; do
|
||||
[ -f "$f" ] && git add "$f" 2>/dev/null || true
|
||||
done
|
||||
else
|
||||
# Worktree: .beads is in the main repo's working tree, not this worktree
|
||||
# Git rejects adding files outside the worktree, so we skip it.
|
||||
# The main repo will see the changes on the next pull/sync.
|
||||
: # do nothing
|
||||
fi
|
||||
# Stage all tracked JSONL files (beads.jsonl, issues.jsonl for backward compat, deletions.jsonl for deletion propagation)
|
||||
# git add is harmless if file doesn't exist
|
||||
for f in .beads/beads.jsonl .beads/issues.jsonl .beads/deletions.jsonl; do
|
||||
[ -f "$f" ] && git add "$f" 2>/dev/null || true
|
||||
done
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -28,6 +28,12 @@ if [ ! -d .beads ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Skip if bd sync is already in progress (GH#532: prevents circular error)
|
||||
# bd sync sets this env var when pushing from worktree
|
||||
if [ -n "$BD_SYNC_IN_PROGRESS" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if sync-branch is configured in config.yaml or env var
|
||||
# If so, .beads changes go to a separate branch via worktree, not the current branch
|
||||
SYNC_BRANCH="${BEADS_SYNC_BRANCH:-}"
|
||||
|
||||
@@ -28,6 +28,12 @@ if [ ! -d .beads ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Skip if bd sync is already in progress (GH#532: prevents circular error)
|
||||
# bd sync sets this env var when pushing from worktree
|
||||
if [ -n "$BD_SYNC_IN_PROGRESS" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if sync-branch is configured in config.yaml or env var
|
||||
# If so, .beads changes go to a separate branch via worktree, not the current branch
|
||||
SYNC_BRANCH="${BEADS_SYNC_BRANCH:-}"
|
||||
|
||||
@@ -855,6 +855,9 @@ func pushFromWorktree(ctx context.Context, worktreePath, branch string) error {
|
||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||
// Push with explicit remote and branch, set upstream if not set
|
||||
cmd := exec.CommandContext(ctx, "git", "-C", worktreePath, "push", "--set-upstream", remote, branch)
|
||||
// Set BD_SYNC_IN_PROGRESS so pre-push hook knows to skip checks (GH#532)
|
||||
// This prevents circular error where hook suggests running bd sync
|
||||
cmd.Env = append(os.Environ(), "BD_SYNC_IN_PROGRESS=1")
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err == nil {
|
||||
|
||||
Reference in New Issue
Block a user