Branch Orchestration
Intelligent Git branch management with automated naming, issue linking, and lifecycle operations.
Purpose
Branch Orchestration provides systematic branch naming and management following the convention {issueNum}-{workType}/{kebab-name}. Automatically links branches to GitHub issues, detects work types, and manages branch lifecycle including creation, renaming, remote sync, and cleanup.
When to Use
- •Creating branches with smart naming from issue context
- •Renaming branches to follow project conventions
- •Linking branches to GitHub issues
- •Cleaning up merged or stale branches
- •When hooks don't provide enough control over naming
Core Capabilities
Branch Naming Convention
Format: {issueNumber}-{workType}/{kebab-case-title}
Examples:
- •
42-feature/add-dark-mode - •
123-fix/safari-auth-bug - •
7-docs/update-readme - •
99-refactor/simplify-api
Branch Creation
Create branches with intelligent naming:
# Manual creation with naming utilities ISSUE_NUM=42 WORK_TYPE="feature" TITLE="Add dark mode support" # Generate branch name BRANCH_NAME=$(generateBranchName $ISSUE_NUM "$WORK_TYPE" "$TITLE") # Result: "42-feature/add-dark-mode-support" # Create and checkout branch git checkout -b "$BRANCH_NAME" # Push to remote with tracking git push -u origin "$BRANCH_NAME"
Utilities:
- •
generateBranchName(issueNumber, workType, title)- Generate formatted name - •
toKebabCase(title)- Convert title to kebab-case (max 40 chars) - •
validateBranchName(branchName)- Check naming conventions
Branch Renaming
Rename branches with remote sync:
# Get current branch OLD_BRANCH=$(git rev-parse --abbrev-ref HEAD) # Generate new name NEW_BRANCH="42-feature/add-dark-mode" # Rename local branch git branch -m "$OLD_BRANCH" "$NEW_BRANCH" # Check if old branch exists on remote if git ls-remote --heads origin "$OLD_BRANCH" | grep -q "$OLD_BRANCH"; then # Push new branch and delete old remote git push -u origin "$NEW_BRANCH" git push origin --delete "$OLD_BRANCH" else # Just set upstream for new branch git push -u origin "$NEW_BRANCH" fi
Utilities:
- •
parseBranchName(branchName)- Extract components (issue#, work type, title) - •
extractIssueNumber(branchName)- Get issue number from branch name
Branch Linking
Link branches to GitHub issues:
# Add branch reference to issue body
ISSUE_NUM=42
BRANCH_NAME="42-feature/add-dark-mode"
CURRENT_BODY=$(gh issue view $ISSUE_NUM --json body -q .body)
UPDATED_BODY="$CURRENT_BODY
---
**Branch:** \`$BRANCH_NAME\`"
echo "$UPDATED_BODY" | gh issue edit $ISSUE_NUM --body-file -
# Save to state file
STATE_FILE=".claude/logs/branch-issues.json"
jq --arg branch "$BRANCH_NAME" --arg issue "$ISSUE_NUM" \
'.[$branch] = {issueNumber: ($issue | tonumber)}' \
"$STATE_FILE" > tmp.json && mv tmp.json "$STATE_FILE"
State File:
- •
.claude/logs/branch-issues.json- Maps branch names to issue numbers
Branch Cleanup
Clean up merged or stale branches:
# Delete merged local branches
git branch --merged main | grep -v "^\*\|main\|master" | xargs -n 1 git branch -d
# Delete merged remote branches
gh pr list --state merged --json headRefName -q '.[].headRefName' | \
xargs -I {} git push origin --delete {}
# Delete stale branches (no commits in 30 days)
git for-each-ref --sort=-committerdate refs/heads/ \
--format='%(refname:short) %(committerdate:relative)' | \
awk '$2 ~ /months/ && $3 >= 1 {print $1}' | \
xargs -n 1 git branch -D
Work Type Detection
Automatically detect work type from context:
import { detectWorkType } from '../shared/hooks/utils/work-type-detector.js';
// From prompt
const workType = detectWorkType('fix the authentication bug');
// Returns: 'fix'
// From issue labels
const workType = detectWorkType(
'Update authentication system',
['bug', 'priority:high']
);
// Returns: 'fix' (detected from 'bug' label)
Work Types:
- •
feature- New functionality - •
fix- Bug fixes - •
chore- Maintenance tasks - •
docs- Documentation - •
refactor- Code improvements
Integration with Hooks
This skill complements the automatic hooks:
| Hook | Automatic Behavior | When to Use Skill |
|---|---|---|
| create-issue-on-prompt | Renames branch after issue creation | Rename before issue creation, custom naming |
| add-github-context | Discovers issue from branch name | Manual branch-issue linking |
State Files:
- •
.claude/logs/branch-issues.json- Branch → Issue mapping
Naming Utilities
generateBranchName
generateBranchName(issueNumber: number, workType: WorkType, title: string): string
Generates formatted branch name.
Example:
generateBranchName(42, 'feature', 'Add Dark Mode') // Returns: "42-feature/add-dark-mode"
parseBranchName
parseBranchName(branchName: string): ParsedBranchName
Parses branch name into components.
Example:
parseBranchName('42-feature/add-dark-mode')
// Returns: { issueNumber: 42, workType: 'feature', title: 'add-dark-mode' }
parseBranchName('123-fix-auth-bug')
// Returns: { issueNumber: 123, title: 'fix-auth-bug' }
validateBranchName
validateBranchName(branchName: string): BranchValidation
Validates branch name against conventions.
Example:
validateBranchName('42-feature/add-dark-mode')
// Returns: { valid: true }
validateBranchName('invalid name with spaces')
// Returns: { valid: false, reason: 'Branch name cannot contain spaces' }
extractIssueNumber
extractIssueNumber(branchName: string): number | null
Extracts issue number from branch name.
Example:
extractIssueNumber('42-feature/add-dark-mode')
// Returns: 42
extractIssueNumber('main')
// Returns: null
Examples
Example 1: Create Branch for Issue
# Get issue details ISSUE_NUM=42 ISSUE_DATA=$(gh issue view $ISSUE_NUM --json title,labels) TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') LABELS=$(echo "$ISSUE_DATA" | jq -r '.labels[].name' | tr '\n' ',') # Detect work type WORK_TYPE=$(detectWorkType "$TITLE" "$LABELS") # Returns: 'feature' or 'fix' or 'chore', etc. # Generate branch name BRANCH_NAME=$(generateBranchName $ISSUE_NUM "$WORK_TYPE" "$TITLE") # Returns: "42-feature/add-dark-mode-support" # Create and checkout git checkout -b "$BRANCH_NAME" git push -u origin "$BRANCH_NAME" # Link to issue echo "Branch \`$BRANCH_NAME\` created" | gh issue comment $ISSUE_NUM --body-file -
Example 2: Rename Branch to Convention
# Current branch doesn't follow convention CURRENT=$(git rev-parse --abbrev-ref HEAD) # e.g., "claude-agile-narwhal-x7h3k" # Get linked issue (if exists) ISSUE_NUM=$(parseIssueFromBranch "$CURRENT") if [ -z "$ISSUE_NUM" ]; then echo "No issue linked to this branch" exit 1 fi # Get issue details ISSUE_DATA=$(gh issue view $ISSUE_NUM --json title,labels) TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') LABELS=$(echo "$ISSUE_DATA" | jq -r '.labels[].name' | tr '\n' ',') # Generate new name WORK_TYPE=$(detectWorkType "$TITLE" "$LABELS") NEW_BRANCH=$(generateBranchName $ISSUE_NUM "$WORK_TYPE" "$TITLE") # Rename with remote sync git branch -m "$CURRENT" "$NEW_BRANCH" git push -u origin "$NEW_BRANCH" git push origin --delete "$CURRENT" 2>/dev/null || true
Example 3: Validate Branch Names in CI
# Get current branch
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Skip validation for protected branches
if [[ "$BRANCH" =~ ^(main|master|develop)$ ]]; then
exit 0
fi
# Validate format
if ! validateBranchName "$BRANCH"; then
echo "❌ Branch name '$BRANCH' doesn't follow convention: {issueNum}-{workType}/{kebab-name}"
echo "Examples: 42-feature/add-dark-mode, 123-fix/safari-bug"
exit 1
fi
# Check if issue exists
ISSUE_NUM=$(extractIssueNumber "$BRANCH")
if [ -n "$ISSUE_NUM" ]; then
if ! gh issue view $ISSUE_NUM &>/dev/null; then
echo "⚠️ Issue #$ISSUE_NUM referenced in branch name doesn't exist"
fi
fi
Example 4: Bulk Branch Cleanup
# Get all merged branches
MERGED=$(git branch --merged main | grep -v "^\*\|main\|master")
for branch in $MERGED; do
# Parse branch name
PARSED=$(parseBranchName "$branch")
ISSUE_NUM=$(echo "$PARSED" | jq -r '.issueNumber // empty')
# Check if issue is closed
if [ -n "$ISSUE_NUM" ]; then
STATE=$(gh issue view $ISSUE_NUM --json state -q .state 2>/dev/null || echo "")
if [ "$STATE" = "CLOSED" ]; then
echo "Deleting merged branch: $branch (issue #$ISSUE_NUM closed)"
git branch -d "$branch"
git push origin --delete "$branch" 2>/dev/null || true
fi
else
echo "Deleting merged branch: $branch (no linked issue)"
git branch -d "$branch"
fi
done
Best Practices
- •Follow naming convention - Always use
{issueNum}-{workType}/{kebab-name} - •Link to issues - Create branches from issue numbers when possible
- •Validate before push - Use
validateBranchName()to check format - •Clean up regularly - Delete merged and stale branches
- •Update state files - Keep
.claude/logs/branch-issues.jsonsynced - •Use work type detection - Automatically categorize with
detectWorkType() - •Sync with remote - Always use
-uflag when pushing new branches
Common Patterns
Pattern: Create Branch from Current Issue
# Auto-detect issue from current context ISSUE_NUM=$(cat .claude/logs/branch-issues.json | jq -r '.[] | .issueNumber' | head -1) if [ -n "$ISSUE_NUM" ]; then ISSUE_DATA=$(gh issue view $ISSUE_NUM --json title,labels) TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') WORK_TYPE=$(detectWorkType "$TITLE") BRANCH_NAME=$(generateBranchName $ISSUE_NUM "$WORK_TYPE" "$TITLE") git checkout -b "$BRANCH_NAME" git push -u origin "$BRANCH_NAME" fi
Pattern: Switch Branch by Issue Number
# Switch to branch for issue #42 ISSUE_NUM=42 BRANCH=$(git branch -a | grep -E "^[ *]*$ISSUE_NUM-" | sed 's/^[ *]*//' | head -1) if [ -n "$BRANCH" ]; then git checkout "$BRANCH" else echo "No branch found for issue #$ISSUE_NUM" fi
Pattern: List Branches by Work Type
# List all feature branches git branch | grep -E "feature/" | sed 's/^[ *]*//' # List all fix branches git branch | grep -E "fix/" | sed 's/^[ *]*//'