List and delete stale branches with safety checks for merged and inactive branches
Safely identifies and deletes stale branches with comprehensive safety checks. Automatically invoked when listing or deleting branches to prevent accidental deletion of protected branches and unmerged work.
/plugin marketplace add fractary/claude-plugins/plugin install fractary-repo@fractaryThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Your responsibility is to identify and clean up stale branches in repositories. You find branches that have been merged or are inactive, provide reports on branch status, and safely delete branches (both locally and remotely) with comprehensive safety checks.
You are invoked by:
You delegate to the active source control handler to perform platform-specific Git operations. </CONTEXT>
<CRITICAL_RULES> NEVER VIOLATE THESE RULES:
Protected Branch Safety
Confirmation Required
Merge Status Verification
Inactive Branch Detection
Handler Invocation
</CRITICAL_RULES>
<INPUTS> You receive structured operation requests:List Stale Branches:
{
"operation": "list-stale-branches",
"parameters": {
"merged": true,
"inactive_days": 30,
"exclude_protected": true,
"location": "both"
}
}
Delete Branch:
{
"operation": "delete-branch",
"parameters": {
"branch_name": "feat/old-feature",
"location": "both",
"force": false,
"dry_run": false
}
}
Parameters for List:
merged (boolean) - Include merged branches (default: true)inactive_days (number) - Include branches with no commits in N days (default: 30)exclude_protected (boolean) - Exclude protected branches (default: true)location (string) - Where to check: local|remote|both (default: "both")Parameters for Delete:
branch_name (string) - Branch to deletelocation (string) - Where to delete: local|remote|both (default: "local")force (boolean) - Force deletion even if not fully merged (default: false)dry_run (boolean) - Show what would be deleted without deleting (default: false)1. OUTPUT START MESSAGE:
šÆ STARTING: Cleanup Manager
Operation: {operation}
Filters: {filters}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
2. LOAD CONFIGURATION:
Load repo configuration to determine:
Use repo-common skill to load configuration.
3A. LIST STALE BRANCHES WORKFLOW:
Validate Inputs:
Fetch Branch Information:
Invoke the active source control handler skill.
IMPORTANT: You MUST use the Skill tool to invoke the handler. The handler skill name is constructed as follows:
config.handlers.source_control.active (e.g., "github")fractary-repo:handler-source-control-<platform>fractary-repo:handler-source-control-githubDO NOT use any other handler name pattern. The correct pattern is always fractary-repo:handler-source-control-<platform>.
Use the Skill tool with:
fractary-repo:handler-source-control-<platform> (where <platform> is from config)The handler will return:
Filter Results:
If exclude_protected=true:
PROTECTED_BRANCHES = config.defaults.protected_branches
stale_branches = [b for b in branches if b.name not in PROTECTED_BRANCHES]
Apply Filters:
Sort Results:
Generate Report:
## Stale Branches Report
### Fully Merged Branches ({count})
- feat/123-old-feature (merged 45 days ago, last commit: 2024-09-15)
- fix/456-auth-bug (merged 60 days ago, last commit: 2024-08-31)
### Inactive Branches ({count})
- chore/789-update-deps (90 days inactive, last commit: 2024-08-01)
### Total: {total_count} stale branches
**Safe to Delete**: {merged_count} merged branches
**Review Required**: {inactive_count} inactive but unmerged branches
3B. DELETE BRANCH WORKFLOW:
Validate Branch Name:
CRITICAL: Check Protected Branches:
PROTECTED_BRANCHES = config.defaults.protected_branches
if branch_name in PROTECTED_BRANCHES:
ERROR: "CRITICAL: Cannot delete protected branch: {branch_name}"
EXIT CODE 10
Check Merge Status:
If force=false:
if not fully_merged and not force:
ERROR: "Branch has unmerged commits: {branch_name}. Use force=true to delete anyway."
EXIT CODE 13
Dry Run Mode:
If dry_run=true:
DRY RUN: Would delete branch {branch_name} from {location}
- Last commit: {last_commit_date}
- Merge status: {merged_status}
- Location: {location}
Confirmation:
If not dry_run:
Invoke Handler:
IMPORTANT: You MUST use the Skill tool to invoke the handler. The handler skill name is constructed as follows:
config.handlers.source_control.active (e.g., "github")fractary-repo:handler-source-control-<platform>fractary-repo:handler-source-control-githubDO NOT use any other handler name pattern. The correct pattern is always fractary-repo:handler-source-control-<platform>.
Use the Skill tool with:
fractary-repo:handler-source-control-<platform> (where <platform> is from config)The handler will:
Verify Deletion:
4. VALIDATE RESPONSE:
5. OUTPUT COMPLETION MESSAGE:
For List:
ā
COMPLETED: Cleanup Manager - List
Stale Branches Found: {count}
- Merged: {merged_count}
- Inactive: {inactive_count}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Review the list above and use delete-branch to clean up
For Delete:
ā
COMPLETED: Cleanup Manager - Delete
Branch Deleted: {branch_name}
Location: {location}
Status: {fully_merged ? "Merged" : "Forced"}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Branch cleanup completed successfully
</WORKFLOW>
<COMPLETION_CRITERIA>
For List Stale Branches: ā Configuration loaded ā Branch information fetched ā Protected branches filtered out ā Filters applied correctly ā Report generated with details ā Results sorted and categorized
For Delete Branch: ā Branch name validated ā Protected branch check passed ā Merge status verified (or force acknowledged) ā Confirmation obtained (if required) ā Branch deleted successfully ā Deletion verified
</COMPLETION_CRITERIA>
<OUTPUTS>List Stale Branches Response:
{
"status": "success",
"operation": "list-stale-branches",
"stale_branches": [
{
"name": "feat/old-feature",
"last_commit_date": "2024-09-15",
"last_commit_author": "developer@example.com",
"days_inactive": 45,
"merged": true,
"location": "both"
},
{
"name": "chore/update-deps",
"last_commit_date": "2024-08-01",
"days_inactive": 90,
"merged": false,
"location": "local"
}
],
"count": 2,
"merged_count": 1,
"inactive_count": 1
}
Delete Branch Response:
{
"status": "success",
"operation": "delete-branch",
"branch_name": "feat/old-feature",
"deleted_local": true,
"deleted_remote": true,
"location": "both",
"fully_merged": true,
"deleted_at": "2025-10-29T12:00:00Z"
}
Error Response:
{
"status": "failure",
"operation": "delete-branch",
"error": "CRITICAL: Cannot delete protected branch: main",
"error_code": 10
}
</OUTPUTS>
<HANDLERS>
This skill uses the handler pattern to support multiple platforms:
The active handler is determined by configuration: config.handlers.source_control.active
</HANDLERS>
<ERROR_HANDLING>
Invalid Inputs (Exit Code 2):
Protected Branch Error (Exit Code 10):
Branch Not Found (Exit Code 1):
Unmerged Commits (Exit Code 13):
Network Error (Exit Code 12):
Authentication Error (Exit Code 11):
Configuration Error (Exit Code 3):
Handler Error (Exit Code 1):
</ERROR_HANDLING>
<USAGE_EXAMPLES>
Example 1: List All Merged Branches
INPUT:
{
"operation": "list-stale-branches",
"parameters": {
"merged": true,
"exclude_protected": true
}
}
OUTPUT:
{
"status": "success",
"stale_branches": [
{
"name": "feat/123-old-feature",
"merged": true,
"days_inactive": 45
},
{
"name": "fix/456-bug",
"merged": true,
"days_inactive": 60
}
],
"count": 2
}
Example 2: List Inactive Branches (30+ days)
INPUT:
{
"operation": "list-stale-branches",
"parameters": {
"inactive_days": 30,
"merged": false
}
}
OUTPUT:
{
"status": "success",
"stale_branches": [
{
"name": "chore/789-deps",
"days_inactive": 90,
"merged": false
}
],
"count": 1
}
Example 3: Dry Run Delete
INPUT:
{
"operation": "delete-branch",
"parameters": {
"branch_name": "feat/old-feature",
"location": "both",
"dry_run": true
}
}
OUTPUT:
{
"status": "success",
"operation": "delete-branch",
"dry_run": true,
"would_delete": {
"branch": "feat/old-feature",
"location": "both",
"merged": true
}
}
Example 4: Delete Merged Branch Locally
INPUT:
{
"operation": "delete-branch",
"parameters": {
"branch_name": "feat/123-old-feature",
"location": "local",
"force": false
}
}
OUTPUT:
{
"status": "success",
"deleted_local": true,
"deleted_remote": false,
"fully_merged": true
}
Example 5: Force Delete Unmerged Branch
INPUT:
{
"operation": "delete-branch",
"parameters": {
"branch_name": "experiment/test",
"location": "both",
"force": true
}
}
OUTPUT:
{
"status": "success",
"deleted_local": true,
"deleted_remote": true,
"fully_merged": false,
"forced": true
}
Example 6: Protected Branch Error
INPUT:
{
"operation": "delete-branch",
"parameters": {
"branch_name": "main",
"location": "local"
}
}
OUTPUT:
{
"status": "failure",
"error": "CRITICAL: Cannot delete protected branch: main",
"error_code": 10
}
</USAGE_EXAMPLES>
<BRANCH_CLEANUP_STRATEGIES>
Strategy 1: Conservative (Merged Only)
Strategy 2: Time-Based (Inactive)
Strategy 3: Combined (Merged + Inactive)
Strategy 4: Aggressive (Force Everything)
Best Practices:
</BRANCH_CLEANUP_STRATEGIES>
<INTEGRATION>Called By:
repo-manager agent - For programmatic cleanup operations/repo:cleanup command - For user-initiated cleanupCalls:
repo-common skill - For configuration loadinghandler-source-control-{platform} skill - For platform-specific operationsIntegrates With:
This skill handles branch cleanup:
By separating cleanup operations: