Create Pull Request
Open GitHub PR with Beads integration (<10 seconds total).
Workflow
1. Extract and Validate Feature Key
CURRENT_BRANCH=$(git branch --show-current)
# Validate branch naming convention
# Allow dots for hierarchical IDs (e.g., feature-bd-xyz.1.2)
if [[ ! "$CURRENT_BRANCH" =~ ^feature-bd-[a-zA-Z0-9.-]+$ ]]; then
echo "❌ Branch name doesn't follow convention"
echo ""
echo "Current branch: $CURRENT_BRANCH"
echo "Expected format: feature-bd-<ID> (e.g., feature-bd-xyz or feature-bd-xyz.1.2)"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Recovery options:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Option 1: Rename branch to match Beads issue"
echo " 1. Find your Beads issue:"
echo " bd list --status open"
echo ""
echo " 2. Rename branch:"
echo " git branch -m feature-bd-<ID>"
echo ""
echo " 3. If pushed to remote:"
echo " git push origin :$CURRENT_BRANCH # Delete old"
echo " git push -u origin feature-bd-<ID> # Push new"
echo ""
echo "Option 2: Create Beads issue for current work"
echo " 1. Extract descriptive name:"
DESCRIPTIVE_NAME=$(echo "$CURRENT_BRANCH" | sed 's/^feature-//')
echo " Title: $DESCRIPTIVE_NAME"
echo ""
echo " 2. Create issue:"
echo " bd create '$DESCRIPTIVE_NAME' --type feature --priority 2"
echo " # Returns: bd-xyz"
echo ""
echo " 3. Rename branch to match:"
echo " git branch -m feature-bd-xyz"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Why this matters:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "- Enables automatic PR-to-Beads linking"
echo "- Provides commit history tracking via Feature-Key"
echo "- Supports multi-developer coordination"
echo "- Enforces Issue-First workflow"
echo ""
exit 1
fi
# Extract FEATURE_KEY from feature-bd-<ID> pattern
FEATURE_KEY=$(echo "$CURRENT_BRANCH" | sed 's/^feature-//')
Why validate:
- Enforces convention (feature-bd-xyz)
- Prevents silent failures downstream
- Provides clear recovery steps
- Educates on Issue-First workflow
2. Get Beads Context
Use MCP tool:
mcp__plugin_beads_beads__set_context(workspace_root="/path/to/project")
issue = mcp__plugin_beads_beads__show(issue_id=<FEATURE_KEY>)
If not found, create proactively:
mcp__plugin_beads_beads__create(
title=<FEATURE_KEY>,
issue_type="feature",
id=<FEATURE_KEY>,
priority=2
)
Detect epic vs feature:
- If issue is epic → PR closes all child tasks, not epic itself
- If issue is feature → PR closes the feature directly
- Check:
issue.dependentsto find child tasks
2.3. Structural Verification (If Needed)
If layout.tsx, middleware.ts, or global config changed:
echo "ℹ️ Structural changes detected. Running build check..."
make build
if fails: exit 1
2.4. Sync with Master (Prevent JSONL Conflicts)
Before creating PR, merge master to prevent conflicts:
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔄 SYNCING WITH MASTER"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Fetch latest master
git fetch origin master
# Check if .beads/issues.jsonl exists and has diverged
if [ -f .beads/issues.jsonl ]; then
JSONL_DIVERGED=$(git diff origin/master...HEAD -- .beads/issues.jsonl | wc -l)
if [ "$JSONL_DIVERGED" -gt 0 ]; then
echo "⚠️ Beads JSONL has diverged from master ($JSONL_DIVERGED lines)"
echo " Merging master now to prevent PR conflicts..."
echo ""
# Merge master (union merge auto-applies for JSONL)
if git merge origin/master --no-edit; then
echo "✅ Merged master successfully (clean merge)"
else
# Check if only JSONL has conflicts
CONFLICT_FILES=$(git diff --name-only --diff-filter=U)
if [ "$CONFLICT_FILES" = ".beads/issues.jsonl" ]; then
echo "✅ Auto-resolving JSONL conflict with union merge..."
# Union merge: keep all lines from both sides
git checkout --union .beads/issues.jsonl
git add .beads/issues.jsonl
git commit --no-edit -m "chore: Auto-merge JSONL (union strategy)
Feature-Key: ${FEATURE_KEY}
Agent: claude-code
Role: create-pull-request-skill"
echo "✅ JSONL conflict resolved automatically"
else
echo "❌ Multiple files have conflicts, aborting auto-merge"
echo " Conflict files:"
echo "$CONFLICT_FILES"
echo ""
echo "Please resolve conflicts manually, then run this skill again"
git merge --abort
exit 1
fi
fi
# Run bd sync to import merged JSONL
if command -v bd >/dev/null 2>&1; then
echo "Running bd sync to import merged JSONL..."
bd sync --import-only
fi
# Push merged changes
echo "Pushing merged changes..."
git push
echo ""
echo "✅ Synced with master, JSONL conflicts prevented"
echo ""
else
echo "✅ JSONL already in sync with master"
echo ""
fi
else
echo "ℹ️ No Beads JSONL file, skipping sync check"
echo ""
fi
Why this is critical:
- Prevents PR conflicts: Merges master BEFORE creating PR
- Union merge strategy: Auto-resolves JSONL conflicts (keeps all lines)
- Multi-agent safety: Works across VMs (each agent syncs independently)
- Complements GitHub Action: Proactive prevention (skill) + reactive fallback (action)
What happens:
- Check if JSONL diverged from master
- If yes: Merge master automatically
- If conflict: Auto-resolve with union merge (JSONL only)
- Run
bd sync --import-onlyto update local database - Push merged changes
- Continue with PR creation
2.5. Ask if Work is Complete (CRITICAL)
Decision point: Is work complete and ready to merge?
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔍 WORK COMPLETION CHECK"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Is this work complete and ready to merge?"
echo ""
echo "✅ YES → Close Beads issue, create PR for merge"
echo "⏸️ NO → Leave open, create draft PR for feedback"
echo ""
read -p "Complete? (y/n): " COMPLETE
echo ""
If YES (work complete):
# Close Beads issue BEFORE creating PR
echo "Closing Beads issue ${FEATURE_KEY}..."
bd close ${FEATURE_KEY} --reason "Work complete, ready for review in PR"
# Export and commit JSONL to feature branch
echo "Syncing Beads state to feature branch..."
bd sync
# Push to include JSONL in PR
echo "Pushing branch with JSONL..."
git push
echo ""
echo "✅ Issue closed and JSONL committed to feature branch"
echo " → JSONL will merge atomically with code"
echo ""
If NO (work in progress):
# Leave issue as in_progress
echo "ℹ️ Keeping ${FEATURE_KEY} as in_progress"
echo " → PR will be marked as draft"
echo ""
DRAFT_FLAG="--draft"
Why this is critical:
- Atomic merge: JSONL closes issue on feature branch → merges with code
- No post-merge mutations: Never need to modify JSONL on master
- Prevents hook conflicts: All Beads state changes happen on feature branch
- Clear workflow: Close issue = "ready to ship", open issue = "work in progress"
2.6. Validate Docs (Epics/Features Only)
Check if docs exist and are linked:
DOC_DIR="docs/${FEATURE_KEY}"
if [ "$issue.type" = "epic" ] || [ "$issue.type" = "feature" ]; then
# Check if docs directory exists
if [ -d "$DOC_DIR" ]; then
# Check if linked to Beads
CURRENT_REF=$(bd show ${FEATURE_KEY} --json | jq -r '.external_ref // ""')
if [ -z "$CURRENT_REF" ] || [ "$CURRENT_REF" = "null" ]; then
echo "ℹ️ Docs exist but not linked to Beads"
echo " Auto-linking: ${FEATURE_KEY} ↔ ${DOC_DIR}/"
bd update ${FEATURE_KEY} --external-ref "docs:${DOC_DIR}/"
fi
# Check if docs updated recently (within last 7 days)
LAST_COMMIT=$(git log -1 --format=%at -- "$DOC_DIR")
NOW=$(date +%s)
DAYS_AGO=$(( ($NOW - $LAST_COMMIT) / 86400 ))
if [ $DAYS_AGO -gt 7 ]; then
echo ""
echo "⚠️ Docs may be stale (last updated ${DAYS_AGO} days ago)"
echo " Consider reviewing: $DOC_DIR/"
echo ""
else
echo "✅ Docs updated recently ($DAYS_AGO days ago)"
fi
else
# No docs directory - informational only
echo "ℹ️ No docs/ directory for ${FEATURE_KEY}"
echo " (OK for small features, recommended for epics)"
fi
fi
Why informational:
- Non-blocking (doesn't prevent PR creation)
- Epics/features benefit from docs, but not required
- Tasks/bugs don't need separate docs
- User controls doc creation timing
What this checks:
- ✅ Docs exist? → Auto-link to Beads if not linked
- ✅ Docs recent? → Warn if stale (>7 days)
- ℹ️ No docs? → Informational (not error)
3. Push Branch
# Check if branch exists on remote
if ! git ls-remote --heads origin <branch>:
git push -u origin <branch>
4. Create PR with gh CLI
Prepare doc link:
DOC_DIR="docs/${FEATURE_KEY}"
if [ -d "$DOC_DIR" ]; then
DOC_LINK="📄 Design docs: [\`$DOC_DIR/\`]($DOC_DIR/)"
else
DOC_LINK="📄 No docs/ directory (tracked in Beads only)"
fi
For Features:
gh pr create \
${DRAFT_FLAG} \
--title "{FEATURE_KEY}: Feature implementation" \
--body "
## Feature
{FEATURE_KEY}
## Beads Issue
$(if [ -z "$DRAFT_FLAG" ]; then echo "Closes: bd-{ID}"; else echo "Related: bd-{ID} (in progress)"; fi)
See: `.beads/issues.jsonl` (line with id: bd-{ID})
## Documentation
${DOC_LINK}
## Next Steps
- Dev environment: Auto-deploying
- CI checks: Running in background
- Review: $(if [ -z "$DRAFT_FLAG" ]; then echo "Ready once CI passes"; else echo "Ready when work complete"; fi)
---
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
" \
--base master
For Epics (closes child tasks, not epic):
gh pr create \
--title "{FEATURE_KEY}: Epic implementation" \
--body "
## Epic
{FEATURE_KEY}
## Beads Issues
**This PR completes the following tasks:**
- Closes: bd-{ID}.1 (Research)
- Closes: bd-{ID}.2 (Spec)
- Closes: bd-{ID}.3 (Implementation)
- Closes: bd-{ID}.4 (Testing)
**Epic:** bd-{ID} (remains open for future work)
See: `.beads/issues.jsonl`
## Documentation
${DOC_LINK}
## Next Steps
- Dev environment: Auto-deploying
- CI checks: Running in background
- Review: Ready once CI passes
---
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
" \
--base master
5. Link PR to Beads
PR_NUMBER=$(gh pr view --json number -q .number)
# Use helper script if available
scripts/bd-link-pr $PR_NUMBER
# Or update via MCP
mcp__plugin_beads_beads__update(
issue_id=<FEATURE_KEY>,
status="in_progress",
external_ref="PR#{PR_NUMBER}"
)
6. Confirm to User
✅ PR#{PR_NUMBER} created
✅ CI running in background
✅ Dev environment deploying
Check status: gh pr view {PR_NUMBER}
Test in dev: dev.yourapp.com/pr-{PR_NUMBER}
Best Practices
- Create Beads issue if missing - Never block on missing metadata
- Use gh CLI - More reliable than API calls
- Link PR bidirectionally - Beads → PR and PR → Beads
- Trust async validation - CI runs in background
What Happens Next (Automatic)
- ✅ CI runs full test suite
- ✅ Dev environment auto-deploys PR
- ✅ GitHub posts check results
- ✅ Danger bot comments with guidance
User can test immediately in dev environment while CI runs.
What This DOESN'T Do
- ❌ Wait for CI (runs async)
- ❌ Run tests locally (CI handles it)
- ❌ Validate deployment (environments handle it)
Philosophy: Fast PR creation + Trust the pipeline