--- description: Smart finalize command - create PR, sync status, complete task (optimized) allowed-tools: [Bash, Task, AskUserQuestion] argument-hint: "[issue-id]" --- # /ccpm:done - Finalize Task **Token Budget:** ~2,100 tokens (vs ~6,000 baseline) | **65% reduction** Finalize a completed task: creates GitHub PR, updates Linear status, and optionally syncs with external PM systems. ## Safety Rules **READ FIRST**: `commands/SAFETY_RULES.md` - āœ… **Linear** operations are automatic (internal tracking) - āœ… **GitHub** PR creation is automatic (code hosting) - ā›” **Jira/Confluence/Slack** writes require user confirmation ## Usage ```bash # Auto-detect issue from git branch /ccpm:done # Explicit issue ID /ccpm:done PSN-29 # Examples /ccpm:done PROJ-123 # Finalize PROJ-123 /ccpm:done # Auto-detect from branch "feature/PSN-29-add-auth" ``` ## Implementation ### Step 1: Parse Arguments & Detect Context ```javascript // Parse issue ID from arguments or git branch let issueId = args[0]; if (!issueId) { // Attempt to extract from git branch name const branch = await Bash('git rev-parse --abbrev-ref HEAD'); const match = branch.match(/([A-Z]+-\d+)/); if (!match) { return error('Could not detect issue ID from git branch.\n\nUsage: /ccpm:done [ISSUE-ID]\nExample: /ccpm:done PSN-29'); } issueId = match[1]; console.log(`šŸ“Œ Detected issue from branch: ${issueId}`); } // Validate format if (!/^[A-Z]+-\d+$/.test(issueId)) { return error(`Invalid issue ID format: ${issueId}. Expected: PROJ-123`); } ``` ### Step 2: Pre-Flight Safety Checks ```javascript // 1. Check if on main/master branch const currentBranch = await Bash('git rev-parse --abbrev-ref HEAD'); if (currentBranch === 'main' || currentBranch === 'master') { console.log('āŒ Error: You are on the main/master branch\n'); console.log('Please checkout a feature branch first:'); console.log(` git checkout -b your-name/${issueId}-feature-name\n`); return; } // 2. Check for uncommitted changes const hasUncommitted = (await Bash('git status --porcelain')).trim().length > 0; if (hasUncommitted) { const status = await Bash('git status --short'); console.log('āš ļø You have uncommitted changes\n'); console.log(status); console.log('\nCommit your changes first:'); console.log(' /ccpm:commit\n'); console.log('Then run /ccpm:done again'); return; } // 3. Check if branch is pushed to remote try { await Bash('git rev-parse @{u}', { stdio: 'ignore' }); } catch { console.log('āš ļø Branch not pushed to remote\n'); console.log('Push your branch first:'); console.log(` git push -u origin ${currentBranch}\n`); console.log('Then run /ccpm:done again'); return; } console.log('āœ… All pre-flight checks passed!\n'); ``` ### Step 3: Fetch Issue & Verify Completion **Use the Task tool to fetch the issue from Linear:** Invoke the `ccpm:linear-operations` subagent: - **Tool**: Task - **Subagent**: ccpm:linear-operations - **Prompt**: ``` operation: get_issue params: issueId: "{issue ID from step 1}" context: cache: true command: "done" ``` **Error handling:** ```javascript if (subagentResponse.error) { console.log(`āŒ Error: ${subagentResponse.error.message}\n`); subagentResponse.error.suggestions.forEach(s => console.log(` - ${s}`)); return; } const issue = subagentResponse.issue; ``` **Parse checklist and verify completion:** ```javascript const description = issue.description || ''; // Find checklist section (between markers or under header) const checklistMatch = description.match( /([\s\S]*?)/ ) || description.match(/## āœ… Implementation Checklist([\s\S]*?)(?=\n## |$)/); if (checklistMatch) { const checklistContent = checklistMatch[1]; const items = checklistContent.match(/- \[([ x])\] .+/g) || []; const total = items.length; const completed = items.filter(item => item.includes('[x]')).length; const progress = total > 0 ? Math.round((completed / total) * 100) : 100; if (progress < 100) { const incomplete = items.filter(item => item.includes('[ ]')); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('ā›” Cannot Finalize: Checklist Incomplete'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); console.log(`Progress: ${progress}% (${completed}/${total} completed)\n`); console.log('āŒ Remaining Items:'); incomplete.forEach(item => console.log(` ${item}`)); console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('šŸ”§ Actions Required'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); console.log('1. Complete remaining checklist items'); console.log(`2. Update checklist: /ccpm:utils:update-checklist ${issueId}`); console.log(`3. Then run: /ccpm:done ${issueId}\n`); return; } console.log(`āœ… Checklist complete: ${progress}% (${completed}/${total} items)\n`); } // Check for blocked label const isBlocked = (issue.labels || []).some(l => l.name.toLowerCase() === 'blocked' ); if (isBlocked) { console.log('āš ļø Task has "blocked" label\n'); console.log('Resolve blockers before finalizing'); return; } ``` ### Step 4: Create GitHub Pull Request ```javascript // Generate PR title and description const prTitle = issue.title; const prBody = `## Summary Closes: ${issue.identifier} ${issue.description || ''} ## Checklist ${checklistContent || '- [x] Implementation complete'} --- Linear: ${issue.url} `; console.log('šŸ“ Creating GitHub Pull Request...\n'); // Create PR using gh CLI (delegates to smart agent selector) Task: ` Create a GitHub pull request with the following details: Title: ${prTitle} Body: ${prBody} Use: gh pr create --title "${prTitle}" --body-file <(echo "${prBody}") After creating the PR: 1. Extract the PR URL from output 2. Return the PR URL ` console.log('āœ… Pull Request created\n'); ``` ### Step 5: Prompt for External System Updates Use AskUserQuestion for Jira/Slack confirmation: ```javascript const answers = await AskUserQuestion({ questions: [ { question: "Do you want to update Jira status to Done?", header: "Sync Jira", multiSelect: false, options: [ {label: "Yes, Update Jira", description: "Mark Jira ticket as Done"}, {label: "No, Skip", description: "I'll update manually"} ] }, { question: "Do you want to notify team in Slack?", header: "Notify Team", multiSelect: false, options: [ {label: "Yes, Send Notification", description: "Post completion message"}, {label: "No, Skip", description: "No notification needed"} ] } ] }); const updateJira = answers['Sync Jira'] === 'Yes, Update Jira'; const notifySlack = answers['Notify Team'] === 'Yes, Send Notification'; ``` **If Jira update requested:** ```javascript if (updateJira) { console.log('\n🚨 CONFIRMATION REQUIRED\n'); console.log(`I will update Jira ticket to status "Done" with comment:\n`); console.log('---'); console.log(`Completed in Linear: ${issueId}`); console.log(`PR: ${prUrl}`); console.log('---\n'); console.log('Proceed? (Type "yes" to confirm)'); // Wait for confirmation (handled by external-system-safety skill) // Then delegate to smart agent selector for Jira update Task: `Update Jira ticket to Done status and add completion comment with PR link`; } ``` **If Slack notification requested:** ```javascript if (notifySlack) { console.log('\n🚨 CONFIRMATION REQUIRED\n'); console.log('I will post to Slack:\n'); console.log('---'); console.log(`āœ… ${issue.title} is complete!`); console.log(`Linear: ${issue.url}`); console.log(`PR: ${prUrl}`); console.log('---\n'); console.log('Proceed? (Type "yes" to confirm)'); // Wait for confirmation (handled by external-system-safety skill) // Then delegate to smart agent selector for Slack notification Task: `Post completion message to Slack with PR and Linear links`; } ``` ### Step 6: Update Linear Status (Automatic) **Use the Task tool to update Linear issue to Done:** Invoke the `ccpm:linear-operations` subagent: - **Tool**: Task - **Subagent**: ccpm:linear-operations - **Prompt**: ``` operation: update_issue params: issueId: "{issue ID from step 1}" state: "Done" labels: ["done"] context: cache: true command: "done" purpose: "Marking task as complete" ``` **Use the Task tool to add completion comment:** Invoke the `ccpm:linear-operations` subagent: - **Tool**: Task - **Subagent**: ccpm:linear-operations - **Prompt**: ``` operation: create_comment params: issueId: "{issue ID from step 1}" body: | ## šŸŽ‰ Task Completed and Finalized **Completion Time:** {current timestamp} ### Actions Taken: āœ… Pull Request created: {PR URL from step 4} {if Jira updated: āœ… Jira status updated to Done, else: ā­ļø Jira update skipped} {if Slack notified: āœ… Team notified in Slack, else: ā­ļø Slack notification skipped} ### Final Status: - Linear: Done āœ… - Workflow labels cleaned up - Task marked as complete --- **This task is now closed.** šŸŽŠ context: command: "done" ``` **Display:** ```javascript console.log('āœ… Linear issue updated to Done\n'); ``` ### Step 7: Show Final Summary ```javascript console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log(`šŸŽ‰ Task Finalized: ${issueId}`); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); console.log('āœ… Linear: Updated to Done'); console.log(`āœ… Pull Request: ${prUrl}`); console.log(`${updateJira ? 'āœ…' : 'ā­ļø '} Jira: ${updateJira ? 'Updated' : 'Skipped'}`); console.log(`${notifySlack ? 'āœ…' : 'ā­ļø '} Slack: ${notifySlack ? 'Notified' : 'Skipped'}`); console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log('šŸ’” What\'s Next?'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); console.log('Available Actions:'); console.log(' 1. /ccpm:plan "title" - Create new task'); console.log(' 2. /ccpm:utils:report - View project progress'); console.log(' 3. /ccpm:utils:search "query" - Find task to work on'); console.log('\nšŸŽŠ Great work! Task complete.'); ``` ## Error Handling ### Invalid Issue ID Format ``` āŒ Invalid issue ID format: proj123 Expected format: PROJ-123 (uppercase letters, hyphen, numbers) ``` ### Git Branch Detection Failed ``` āŒ Could not detect issue ID from git branch Current branch: main Usage: /ccpm:done [ISSUE-ID] Example: /ccpm:done PSN-29 ``` ### Uncommitted Changes ``` āš ļø You have uncommitted changes M src/api/auth.ts ?? src/tests/new-test.ts Commit your changes first: /ccpm:commit Then run /ccpm:done again ``` ### On Main Branch ``` āŒ Error: You are on the main/master branch Please checkout a feature branch first: git checkout -b your-name/PSN-29-feature-name ``` ### Branch Not Pushed ``` āš ļø Branch not pushed to remote Push your branch first: git push -u origin feature/PSN-29-add-auth Then run /ccpm:done again ``` ### Checklist Incomplete ``` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ā›” Cannot Finalize: Checklist Incomplete ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Progress: 80% (4/5 completed) āŒ Remaining Items: - [ ] Write tests for password reset ``` ### Task Blocked ``` āš ļø Task has "blocked" label Resolve blockers before finalizing ``` ## Examples ### Example 1: Done with Auto-Detection ```bash # Current branch: feature/PSN-29-add-auth /ccpm:done # Output: # šŸ“Œ Detected issue from branch: PSN-29 # # āœ… All pre-flight checks passed! # # āœ… Checklist complete: 100% (5/5 items) # # šŸ“ Creating GitHub Pull Request... # āœ… Pull Request created # # [AskUserQuestion for Jira/Slack] # # āœ… Linear issue updated to Done # # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # šŸŽ‰ Task Finalized: PSN-29 # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # # āœ… Linear: Updated to Done # āœ… Pull Request: https://github.com/... # ā­ļø Jira: Skipped # ā­ļø Slack: Skipped ``` ### Example 2: Done with Explicit Issue ID ```bash /ccpm:done PSN-29 # Same flow as Example 1 ``` ### Example 3: Done with Uncommitted Changes (Error) ```bash /ccpm:done PSN-29 # Output: # āš ļø You have uncommitted changes # # M src/api/auth.ts # ?? src/tests/new-test.ts # # Commit your changes first: # /ccpm:commit # # Then run /ccpm:done again ``` ## Token Budget Breakdown | Section | Tokens | Notes | |---------|--------|-------| | Frontmatter & description | 80 | Minimal metadata | | Step 1: Argument parsing | 150 | Git detection + validation | | Step 2: Pre-flight checks | 300 | Branch/commit/push checks | | Step 3: Fetch & verify | 350 | Linear subagent + checklist parsing | | Step 4: Create PR | 250 | Smart agent delegation | | Step 5: External confirmations | 200 | AskUserQuestion + safety | | Step 6: Update Linear | 250 | Batch update + comment | | Step 7: Final summary | 150 | Display results | | Error handling | 220 | 6 error scenarios (concise) | | Examples | 150 | 3 essential examples | | **Total** | **~2,100** | **vs ~6,000 baseline (65% reduction)** | ## Key Optimizations 1. āœ… **No routing overhead** - Direct implementation (no call to complete:finalize) 2. āœ… **Linear subagent** - All Linear ops with session-level caching 3. āœ… **Smart agent delegation** - PR creation and external syncs use smart-agent-selector 4. āœ… **Pre-flight checks** - Prevent common mistakes before processing 5. āœ… **Batch operations** - Single update for state + labels 6. āœ… **Safety confirmation** - Built into workflow for Jira/Slack 7. āœ… **Concise examples** - Only 3 essential examples ## Integration with Other Commands - **After /ccpm:verify** → Use /ccpm:done to finalize - **Auto-detection** → Works with /ccpm:work branch-based workflow - **Git integration** → Follows /ccpm:commit for clean commits - **Safety rules** → Enforces confirmation for external systems ## Notes - **Git branch detection**: Extracts issue ID from branch names like `feature/PSN-29-add-auth` - **Pre-flight checks**: Validates all prerequisites before finalization - **Smart agent selection**: Automatically chooses optimal agents for PR and external syncs - **Safety first**: Jira/Slack updates require explicit confirmation - **Linear automatic**: Internal tracking updates happen automatically - **Caching**: Linear subagent provides 85-95% cache hit rate for faster operations