Initial commit
This commit is contained in:
584
commands/spec:break-down.md
Normal file
584
commands/spec:break-down.md
Normal file
@@ -0,0 +1,584 @@
|
||||
---
|
||||
description: Break down Epic/Feature into Features/Tasks based on spec
|
||||
allowed-tools: [LinearMCP, AskUserQuestion]
|
||||
argument-hint: <epic-or-feature-id>
|
||||
---
|
||||
|
||||
# Break Down: $1
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
**NEVER** submit, post, or update anything to Jira, Confluence, BitBucket, or Slack without explicit user confirmation, even in bypass permission mode.
|
||||
|
||||
---
|
||||
|
||||
## Shared Helpers
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
This command uses the following helper functions:
|
||||
- `ensureLabelsExist()` - Ensure labels exist before using them
|
||||
- `getOrCreateLabel()` - Create or retrieve individual labels
|
||||
|
||||
---
|
||||
|
||||
## Argument
|
||||
|
||||
- **$1** - Epic ID (to break into Features) or Feature ID (to break into Tasks)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Issue and Determine Type
|
||||
|
||||
Use **Linear MCP** `get_issue` with ID `$1`:
|
||||
|
||||
- Get issue details
|
||||
- Check if it's an Epic/Initiative or Feature (parent issue)
|
||||
- Find linked spec document
|
||||
- Get project/team information
|
||||
|
||||
```javascript
|
||||
const issueType = determineType(issue)
|
||||
|
||||
function determineType(issue) {
|
||||
// Check if issue is an Initiative (Epic)
|
||||
if (issue.type === 'initiative' || issue.project?.type === 'initiative') {
|
||||
return 'epic'
|
||||
}
|
||||
|
||||
// Check if issue has sub-issues (Feature)
|
||||
if (issue.children && issue.children.length > 0) {
|
||||
return 'feature'
|
||||
}
|
||||
|
||||
// Check labels
|
||||
if (issue.labels.includes('epic')) return 'epic'
|
||||
if (issue.labels.includes('feature')) return 'feature'
|
||||
|
||||
// Default: treat as feature
|
||||
return 'feature'
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Fetch Spec Document
|
||||
|
||||
Extract spec document link from issue description:
|
||||
|
||||
```javascript
|
||||
// Look for pattern: [Epic Spec: Title](url) or [Feature Design: Title](url)
|
||||
const docLinkPattern = /\[(?:Epic Spec|Feature Design): .+?\]\((.+?)\)/
|
||||
|
||||
const match = issue.description.match(docLinkPattern)
|
||||
if (match) {
|
||||
const docUrl = match[1]
|
||||
// Extract doc ID from URL
|
||||
const docId = extractDocId(docUrl)
|
||||
}
|
||||
```
|
||||
|
||||
Use **Linear MCP** `get_document` to fetch spec content.
|
||||
|
||||
### Step 3: Analyze Spec and Generate Breakdown
|
||||
|
||||
#### If Breaking Down EPIC → Features
|
||||
|
||||
**Parse Epic Spec:**
|
||||
|
||||
Look for "Features Breakdown" section in spec document.
|
||||
|
||||
```markdown
|
||||
## 📊 Features Breakdown
|
||||
|
||||
| Feature | Priority | Complexity | Est. Timeline |
|
||||
|---------|----------|------------|---------------|
|
||||
| JWT Auth | P0 | High | 2 weeks |
|
||||
| OAuth Integration | P1 | Medium | 1 week |
|
||||
| MFA Support | P2 | Low | 3 days |
|
||||
```
|
||||
|
||||
**AI Analysis:**
|
||||
|
||||
```javascript
|
||||
const features = []
|
||||
|
||||
// Parse table
|
||||
for (const row of featureTable) {
|
||||
const feature = {
|
||||
title: row.feature,
|
||||
priority: row.priority, // P0, P1, P2
|
||||
complexity: row.complexity, // High, Medium, Low
|
||||
estimate: row.estimate,
|
||||
description: generateDescription(row.feature, epicContext)
|
||||
}
|
||||
features.push(feature)
|
||||
}
|
||||
|
||||
// Generate additional features if not in table
|
||||
// Analyze epic requirements and suggest missing features
|
||||
const suggestedFeatures = analyzeRequirements(epicSpec)
|
||||
features.push(...suggestedFeatures)
|
||||
```
|
||||
|
||||
**Create Feature Issues:**
|
||||
|
||||
For each feature:
|
||||
|
||||
```javascript
|
||||
// Ensure required labels exist before creating issues
|
||||
const featureLabels = await ensureLabelsExist(epic.team.id,
|
||||
['feature', 'spec:draft'],
|
||||
{
|
||||
descriptions: {
|
||||
'feature': 'Feature-level work item',
|
||||
'spec:draft': 'Specification in draft state'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
title: feature.title,
|
||||
team: epic.team,
|
||||
project: epic.project,
|
||||
parent: epic.id, // Link to epic
|
||||
labelIds: featureLabels, // Use validated label IDs
|
||||
priority: mapPriority(feature.priority), // P0=1, P1=2, P2=3, P3=4
|
||||
description: `
|
||||
## 📄 Specification
|
||||
|
||||
**Feature Design Doc**: [Will be created] ← Use /ccpm:spec:write to populate
|
||||
|
||||
**Parent Epic**: [${epic.title}](${epic.url})
|
||||
**Epic Spec**: [Link to epic spec doc]
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
${feature.description}
|
||||
|
||||
## 📋 Initial Requirements
|
||||
|
||||
[AI generates based on epic context and feature title]
|
||||
|
||||
## ⏱️ Estimate
|
||||
|
||||
**Complexity**: ${feature.complexity}
|
||||
**Timeline**: ${feature.estimate}
|
||||
|
||||
---
|
||||
|
||||
**Next Steps:**
|
||||
1. Run /ccpm:spec:write to create detailed feature design
|
||||
2. Run /ccpm:spec:review to validate completeness
|
||||
3. Run /ccpm:spec:break-down to create implementation tasks
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
#### If Breaking Down FEATURE → Tasks
|
||||
|
||||
**Parse Feature Design:**
|
||||
|
||||
Look for "Implementation Plan" or "Task Breakdown" section:
|
||||
|
||||
```markdown
|
||||
## 🚀 Implementation Plan
|
||||
|
||||
### Task Breakdown
|
||||
- [ ] **Task 1: Database schema**: Create user_auth table with migrations (Est: 2h)
|
||||
- [ ] **Task 2: API endpoints**: POST /login, /logout, /refresh (Est: 4h)
|
||||
- [ ] **Task 3: Frontend integration**: Login screen + auth context (Est: 6h)
|
||||
- [ ] **Task 4: Testing**: Unit + integration tests (Est: 4h)
|
||||
```
|
||||
|
||||
**AI Analysis:**
|
||||
|
||||
```javascript
|
||||
const tasks = []
|
||||
|
||||
// Parse task list
|
||||
for (const item of taskList) {
|
||||
const task = {
|
||||
title: extractTitle(item), // "Database schema"
|
||||
description: extractDescription(item), // "Create user_auth table..."
|
||||
estimate: extractEstimate(item), // "2h"
|
||||
dependencies: extractDependencies(item) // If mentioned
|
||||
}
|
||||
tasks.push(task)
|
||||
}
|
||||
|
||||
// Analyze dependencies
|
||||
const dependencyGraph = buildDependencyGraph(tasks)
|
||||
|
||||
// Suggest additional tasks if missing
|
||||
const suggestedTasks = analyzeMissingTasks(featureSpec, tasks)
|
||||
// Example: Missing documentation task, missing E2E test task
|
||||
tasks.push(...suggestedTasks)
|
||||
```
|
||||
|
||||
**Create Task Issues (as Sub-Issues):**
|
||||
|
||||
For each task:
|
||||
|
||||
```javascript
|
||||
// Ensure required labels exist before creating tasks
|
||||
const taskLabels = await ensureLabelsExist(feature.team.id,
|
||||
['task', 'planning'],
|
||||
{
|
||||
descriptions: {
|
||||
'task': 'Implementation task',
|
||||
'planning': 'Task in planning phase'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
{
|
||||
title: task.title,
|
||||
team: feature.team,
|
||||
project: feature.project,
|
||||
parent: feature.id, // Link to feature as sub-issue
|
||||
labelIds: taskLabels, // Use validated label IDs
|
||||
estimate: convertToPoints(task.estimate), // 2h → 1 point, 4h → 2 points
|
||||
description: `
|
||||
## 📋 Task Description
|
||||
|
||||
${task.description}
|
||||
|
||||
---
|
||||
|
||||
## 📄 Related Spec
|
||||
|
||||
**Feature**: [${feature.title}](${feature.url})
|
||||
**Feature Design**: [Link to design doc]
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Acceptance Criteria
|
||||
|
||||
[AI generates based on task title and feature requirements]
|
||||
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
|
||||
---
|
||||
|
||||
## ⏱️ Estimate
|
||||
|
||||
**Time**: ${task.estimate}
|
||||
**Complexity**: [Low/Medium/High based on estimate]
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Dependencies
|
||||
|
||||
${task.dependencies ? `Depends on: ${task.dependencies.map(d => `[Task ${d}]`).join(', ')}` : 'No dependencies'}
|
||||
|
||||
---
|
||||
|
||||
**Next Steps:**
|
||||
1. Run /ccpm:planning:plan to gather implementation research
|
||||
2. Run /ccpm:implementation:start when ready to begin work
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Show Preview
|
||||
|
||||
Before creating, show preview to user:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Breakdown Preview: [$1]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🎯 Type: [Epic → Features / Feature → Tasks]
|
||||
📄 Spec: [DOC-XXX]
|
||||
🔢 Items to Create: [N]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Breakdown Items
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[If Epic → Features:]
|
||||
|
||||
1. **JWT Auth** (P0, High Complexity)
|
||||
- Timeline: 2 weeks
|
||||
- Description: Implement JWT-based authentication...
|
||||
|
||||
2. **OAuth Integration** (P1, Medium Complexity)
|
||||
- Timeline: 1 week
|
||||
- Description: Add OAuth 2.0 support for Google, GitHub...
|
||||
|
||||
3. **MFA Support** (P2, Low Complexity)
|
||||
- Timeline: 3 days
|
||||
- Description: Two-factor authentication with TOTP...
|
||||
|
||||
[If Feature → Tasks:]
|
||||
|
||||
1. **Database schema** (Est: 2h)
|
||||
- Description: Create user_auth table with migrations
|
||||
- Dependencies: None
|
||||
|
||||
2. **API endpoints** (Est: 4h)
|
||||
- Description: POST /login, /logout, /refresh
|
||||
- Dependencies: Task 1
|
||||
|
||||
3. **Frontend integration** (Est: 6h)
|
||||
- Description: Login screen + auth context
|
||||
- Dependencies: Task 2
|
||||
|
||||
4. **Testing** (Est: 4h)
|
||||
- Description: Unit + integration tests
|
||||
- Dependencies: Task 3
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Total Items: [N]
|
||||
Total Estimate: [X hours / Y days]
|
||||
Critical Path: [Task sequence]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 5: Confirm Creation
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Create these [N] [features/tasks] in Linear?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, Create All",
|
||||
description: "Create all [N] items as shown above"
|
||||
},
|
||||
{
|
||||
label: "Let Me Edit First",
|
||||
description: "I want to modify the breakdown in the spec doc first"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Don't create anything"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Create Issues in Linear
|
||||
|
||||
If user confirms:
|
||||
|
||||
```javascript
|
||||
const createdIssues = []
|
||||
|
||||
try {
|
||||
// Ensure labels exist once before creating all issues
|
||||
// (Labels are reused across all items in the breakdown)
|
||||
const labels = isEpicBreakdown
|
||||
? await ensureLabelsExist(parentIssue.team.id,
|
||||
['feature', 'spec:draft'],
|
||||
{
|
||||
descriptions: {
|
||||
'feature': 'Feature-level work item',
|
||||
'spec:draft': 'Specification in draft state'
|
||||
}
|
||||
}
|
||||
)
|
||||
: await ensureLabelsExist(parentIssue.team.id,
|
||||
['task', 'planning'],
|
||||
{
|
||||
descriptions: {
|
||||
'task': 'Implementation task',
|
||||
'planning': 'Task in planning phase'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for (const item of breakdownItems) {
|
||||
const issue = await createLinearIssue({
|
||||
...item,
|
||||
labelIds: labels // Use pre-validated labels
|
||||
})
|
||||
createdIssues.push(issue)
|
||||
|
||||
// Brief pause to avoid rate limits
|
||||
await sleep(500)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to create issues: ${error.message}`);
|
||||
|
||||
if (error.message.includes('label')) {
|
||||
throw new Error(
|
||||
`Label operation failed. Please check that you have permission to create labels in this team.\n` +
|
||||
`Error: ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
**Update parent issue description** to include links to created children:
|
||||
|
||||
For Epic:
|
||||
```markdown
|
||||
## 🎨 Features
|
||||
|
||||
Created from spec breakdown:
|
||||
|
||||
- [Feature 1: JWT Auth](https://linear.app/workspace/issue/WORK-101)
|
||||
- [Feature 2: OAuth Integration](https://linear.app/workspace/issue/WORK-102)
|
||||
- [Feature 3: MFA Support](https://linear.app/workspace/issue/WORK-103)
|
||||
```
|
||||
|
||||
For Feature:
|
||||
```markdown
|
||||
## ✅ Implementation Tasks
|
||||
|
||||
Created from spec breakdown:
|
||||
|
||||
- [Task 1: Database schema](https://linear.app/workspace/issue/WORK-201)
|
||||
- [Task 2: API endpoints](https://linear.app/workspace/issue/WORK-202)
|
||||
- [Task 3: Frontend integration](https://linear.app/workspace/issue/WORK-203)
|
||||
- [Task 4: Testing](https://linear.app/workspace/issue/WORK-204)
|
||||
|
||||
**💡 Tip**: Use /ccpm:utils:dependencies to visualize task order
|
||||
```
|
||||
|
||||
### Step 7: Display Results
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Breakdown Complete!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🎯 Parent: [$1 - Title]
|
||||
🔢 Created: [N] [Features/Tasks]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Created Items
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ WORK-101: JWT Auth (Feature, P0)
|
||||
✅ WORK-102: OAuth Integration (Feature, P1)
|
||||
✅ WORK-103: MFA Support (Feature, P2)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Interactive Next Actions
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Breakdown complete! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
// If broke down Epic → Features
|
||||
epicMode ? {
|
||||
label: "Write Feature Specs",
|
||||
description: "Start writing detailed design for first feature"
|
||||
} : {
|
||||
label: "Start Implementation",
|
||||
description: "Begin working on first task (/ccpm:implementation:start)"
|
||||
},
|
||||
{
|
||||
label: "View Dependencies",
|
||||
description: "Visualize task dependencies (/ccpm:utils:dependencies)"
|
||||
},
|
||||
{
|
||||
label: "Auto-Assign Agents",
|
||||
description: "AI-powered agent assignment (/ccpm:utils:auto-assign)"
|
||||
},
|
||||
{
|
||||
label: "View in Linear",
|
||||
description: "Open parent issue in Linear"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Estimation Conversion
|
||||
|
||||
```javascript
|
||||
function convertToPoints(timeEstimate) {
|
||||
// Convert time estimates to Linear points
|
||||
const hours = parseHours(timeEstimate) // "2h" → 2, "1 day" → 8
|
||||
|
||||
if (hours <= 2) return 1 // 1 point = 1-2 hours
|
||||
if (hours <= 4) return 2 // 2 points = 2-4 hours
|
||||
if (hours <= 8) return 3 // 3 points = 4-8 hours (1 day)
|
||||
if (hours <= 16) return 5 // 5 points = 1-2 days
|
||||
if (hours <= 40) return 8 // 8 points = 1 week
|
||||
return 13 // 13 points = 2+ weeks
|
||||
}
|
||||
|
||||
function mapPriority(priority) {
|
||||
// Linear priority: 1 = Urgent, 2 = High, 3 = Medium, 4 = Low, 0 = No priority
|
||||
const mapping = {
|
||||
'P0': 1, // Urgent
|
||||
'P1': 2, // High
|
||||
'P2': 3, // Medium
|
||||
'P3': 4 // Low
|
||||
}
|
||||
return mapping[priority] || 0
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Detection
|
||||
|
||||
```javascript
|
||||
function extractDependencies(taskDescription) {
|
||||
// Look for patterns:
|
||||
// - "depends on Task 1"
|
||||
// - "after Task 2"
|
||||
// - "requires Task 3"
|
||||
// - "(depends: 1, 2)"
|
||||
|
||||
const patterns = [
|
||||
/depends on (?:Task )?(\d+)/gi,
|
||||
/after (?:Task )?(\d+)/gi,
|
||||
/requires (?:Task )?(\d+)/gi,
|
||||
/\(depends: ([\d, ]+)\)/gi
|
||||
]
|
||||
|
||||
const dependencies = []
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const matches = taskDescription.matchAll(pattern)
|
||||
for (const match of matches) {
|
||||
const taskNum = match[1]
|
||||
dependencies.push(parseInt(taskNum))
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(dependencies)] // Remove duplicates
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Epic → Features creates Parent Issues
|
||||
- Feature → Tasks creates Sub-Issues
|
||||
- All created items link back to spec
|
||||
- Dependencies are parsed and preserved
|
||||
- Timeline estimates are converted to Linear points
|
||||
- Priority mapping follows P0=Urgent, P1=High, etc.
|
||||
- **Label Handling**: Uses shared Linear helpers from `_shared-linear-helpers.md`
|
||||
- Labels are validated/created once before batch issue creation
|
||||
- Features get labels: `['feature', 'spec:draft']`
|
||||
- Tasks get labels: `['task', 'planning']`
|
||||
- Graceful error handling if label creation fails
|
||||
- Automatic label reuse prevents duplicates
|
||||
Reference in New Issue
Block a user