Branch Orchestration
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-mode123-fix/safari-auth-bug7-docs/update-readme99-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 nametoKebabCase(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 functionalityfix- Bug fixeschore- Maintenance tasksdocs- Documentationrefactor- 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/^[ *]*//'