Initial commit
This commit is contained in:
528
commands/done.md
Normal file
528
commands/done.md
Normal file
@@ -0,0 +1,528 @@
|
||||
---
|
||||
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(
|
||||
/<!-- ccpm-checklist-start -->([\s\S]*?)<!-- ccpm-checklist-end -->/
|
||||
) || 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 <project> - View project progress');
|
||||
console.log(' 3. /ccpm:utils:search <project> "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
|
||||
Reference in New Issue
Block a user