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.json synced
-
Use work type detection - Automatically categorize with detectWorkType()
-
Sync with remote - Always use -u flag 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/^[ ]//'