Initial commit
This commit is contained in:
1038
commands/README.md
Normal file
1038
commands/README.md
Normal file
File diff suppressed because it is too large
Load Diff
87
commands/SAFETY_RULES.md
Normal file
87
commands/SAFETY_RULES.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# PM Commands Safety Rules
|
||||
|
||||
## 🚨 CRITICAL SAFETY CONSTRAINTS
|
||||
|
||||
### ⛔ ABSOLUTE PROHIBITION - External PM Systems
|
||||
|
||||
**NEVER submit, post, update, or modify ANYTHING to the following systems without EXPLICIT user confirmation:**
|
||||
|
||||
- ✖️ **Jira** (issues, comments, attachments, status changes)
|
||||
- ✖️ **Confluence** (pages, comments, edits)
|
||||
- ✖️ **BitBucket** (pull requests, comments, repository changes)
|
||||
- ✖️ **Slack** (messages, posts, reactions)
|
||||
|
||||
**This applies even in bypass permission mode.**
|
||||
|
||||
### ✅ Allowed Actions (Read-Only)
|
||||
|
||||
The following read-only operations are permitted without confirmation:
|
||||
|
||||
- ✅ **Fetching/Reading** Jira tickets
|
||||
- ✅ **Searching** Confluence documentation
|
||||
- ✅ **Viewing** BitBucket pull requests and commits
|
||||
- ✅ **Searching** Slack messages and conversations
|
||||
- ✅ **Browsing** with Playwright MCP (read-only)
|
||||
|
||||
### 📝 Linear Operations
|
||||
|
||||
Linear operations are permitted but should follow confirmation workflow:
|
||||
|
||||
- ✅ **Creating** Linear issues (confirm if creating multiple)
|
||||
- ✅ **Updating** Linear issues (confirm if significant changes)
|
||||
- ✅ **Adding** comments to Linear (always safe)
|
||||
- ✅ **Changing** status/labels in Linear (confirm if bulk changes)
|
||||
|
||||
### 🔒 Confirmation Workflow
|
||||
|
||||
Before ANY write operation to external PM systems:
|
||||
|
||||
1. **Display** what you intend to do
|
||||
2. **Show** the exact content to be posted/updated
|
||||
3. **Wait** for explicit user confirmation
|
||||
4. **Only proceed** after receiving "yes", "confirm", "go ahead", or similar
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
🚨 CONFIRMATION REQUIRED
|
||||
|
||||
I want to post the following comment to Jira ticket TRAIN-123:
|
||||
|
||||
---
|
||||
Implementation complete. Moving to QA.
|
||||
- All tests passing
|
||||
- Code review approved
|
||||
---
|
||||
|
||||
Do you want me to proceed? (yes/no)
|
||||
```
|
||||
|
||||
### ⚠️ Common Pitfalls to Avoid
|
||||
|
||||
**DO NOT:**
|
||||
|
||||
- ❌ Auto-post status updates to Jira after completing work
|
||||
- ❌ Auto-update Confluence with implementation notes
|
||||
- ❌ Auto-comment on BitBucket PRs with review feedback
|
||||
- ❌ Auto-send Slack notifications about task completion
|
||||
- ❌ Assume "go ahead and finish" means "post to Jira"
|
||||
|
||||
**DO:**
|
||||
|
||||
- ✅ Gather all information from external systems
|
||||
- ✅ Create comprehensive Linear issues with all context
|
||||
- ✅ Update Linear freely (internal tracking)
|
||||
- ✅ Ask before posting anything externally
|
||||
- ✅ Show exactly what will be posted before posting
|
||||
|
||||
### 📋 Remember
|
||||
|
||||
**The goal is to:**
|
||||
|
||||
- **Gather** intelligence from external PM systems
|
||||
- **Centralize** planning and tracking in Linear
|
||||
- **Never pollute** external systems without explicit approval
|
||||
- **Maintain** full transparency with the user
|
||||
|
||||
**When in doubt, ASK first.**
|
||||
470
commands/SPEC_MANAGEMENT_SUMMARY.md
Normal file
470
commands/SPEC_MANAGEMENT_SUMMARY.md
Normal file
@@ -0,0 +1,470 @@
|
||||
# PM Commands: Spec Management System - Implementation Summary
|
||||
|
||||
**Date**: 2025-11-10
|
||||
**Status**: ✅ COMPLETE
|
||||
**Version**: PM Commands 2.0 + Spec Management
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
Implemented a comprehensive **Spec Management System** for PM Commands, enabling spec-first development workflow with Linear Documents integration. This enhances the existing PM Commands 2.0 (Interactive Mode) with 6 new spec-focused commands plus a context-aware help system.
|
||||
|
||||
---
|
||||
|
||||
## 📊 What Was Built
|
||||
|
||||
### 6 New Spec Management Commands
|
||||
|
||||
#### 1. `/ccpm:spec:create <type> "<title>" [parent-id]`
|
||||
**Purpose**: Create Epic/Feature with Linear Document
|
||||
|
||||
**Features**:
|
||||
- Creates Linear Initiative (Epic) or Parent Issue (Feature)
|
||||
- Generates associated Linear Document for specs
|
||||
- Pre-populates with appropriate template (Epic Spec or Feature Design)
|
||||
- Links document to issue
|
||||
- Supports hierarchy (Features can belong to Epics)
|
||||
|
||||
**Templates Included**:
|
||||
- **Epic Spec Template**: Vision, User Research, Architecture, Features Breakdown, Timeline, Security
|
||||
- **Feature Design Template**: Requirements, UX, Technical Design, Testing, Implementation Plan, Risks
|
||||
|
||||
#### 2. `/ccpm:spec:write <doc-id> <section>`
|
||||
**Purpose**: AI-assisted spec writing with codebase analysis
|
||||
|
||||
**Sections**:
|
||||
- `requirements` - Functional, non-functional, acceptance criteria
|
||||
- `architecture` - System design, component breakdown, tech stack
|
||||
- `api-design` - RESTful endpoints, request/response, validation
|
||||
- `data-model` - Database schema, TypeScript types, migrations
|
||||
- `testing` - Unit, integration, E2E strategies
|
||||
- `security` - Auth, validation, rate limiting, audit logs
|
||||
- `user-flow` - User journeys, wireframes, error states
|
||||
- `timeline` - Task breakdown, estimates, milestones
|
||||
- `all` - Write all sections sequentially
|
||||
|
||||
**AI Capabilities**:
|
||||
- Analyzes existing codebase for patterns
|
||||
- Fetches library documentation via Context7 MCP
|
||||
- Follows project conventions
|
||||
- Generates specific, testable content
|
||||
|
||||
#### 3. `/ccpm:spec:review <doc-id>`
|
||||
**Purpose**: AI-powered spec validation
|
||||
|
||||
**Analysis**:
|
||||
- **Completeness Score** (0-100%) based on required vs optional sections
|
||||
- **Quality Assessment**: Specificity, testability, clarity, consistency
|
||||
- **Risk Identification**: Scope creep, technical risks, timeline issues
|
||||
- **Grading**: A (90-100%), B (75-89%), C (60-74%), D (50-59%), F (<50%)
|
||||
|
||||
**Output**:
|
||||
- Detailed review report
|
||||
- Actionable recommendations
|
||||
- Best practices checklist
|
||||
- Missing sections identified
|
||||
|
||||
#### 4. `/ccpm:spec:break-down <epic-or-feature-id>`
|
||||
**Purpose**: Generate implementation items from spec
|
||||
|
||||
**Breakdown Logic**:
|
||||
- **Epic → Features**: Parses "Features Breakdown" table, creates Parent Issues
|
||||
- **Feature → Tasks**: Parses "Task Breakdown" checklist, creates Sub-Issues
|
||||
|
||||
**Features**:
|
||||
- Auto-extracts from spec sections
|
||||
- AI suggests missing items
|
||||
- Detects and preserves dependencies
|
||||
- Maps priorities (P0=Urgent, P1=High, etc.)
|
||||
- Converts time estimates to Linear points
|
||||
- Shows preview before creation
|
||||
- Requires user confirmation
|
||||
|
||||
#### 5. `/ccpm:spec:migrate <project-path> [category]`
|
||||
**Purpose**: Migrate existing markdown specs to Linear
|
||||
|
||||
**Discovery**:
|
||||
- Scans `.claude/` directory: docs, plans, enhancements, tasks, research, analysis
|
||||
- Categorizes files: Epic, Feature, Task, Documentation
|
||||
- Auto-detects based on content patterns and file location
|
||||
|
||||
**Migration Process**:
|
||||
1. **Discover** all markdown files in project
|
||||
2. **Categorize** by type (Epic/Feature/Task/Doc)
|
||||
3. **Show detailed preview** for EVERY file (MANDATORY)
|
||||
4. **Ask confirmation** before ANY creation
|
||||
5. **Create** Linear Issues + Documents
|
||||
6. **Extract** checklists → create sub-tasks
|
||||
7. **Move** originals to `.claude/migrated/`
|
||||
8. **Create** breadcrumb files with Linear links
|
||||
|
||||
**Safety**:
|
||||
- ✅ **ALWAYS shows full preview** before migration
|
||||
- ✅ **Requires explicit confirmation**
|
||||
- ✅ Original files moved (NOT deleted)
|
||||
- ✅ Can rollback from `.claude/migrated/`
|
||||
- ✅ Preserves full content in Linear Documents
|
||||
|
||||
#### 6. `/ccpm:spec:sync <doc-id-or-issue-id>`
|
||||
**Purpose**: Sync spec with implementation reality
|
||||
|
||||
**Drift Detection**:
|
||||
- **Requirements Drift**: Missing, extra, or changed features
|
||||
- **API Drift**: Endpoint signatures, new/missing endpoints
|
||||
- **Data Model Drift**: Schema changes, field modifications
|
||||
- **Task Drift**: Status mismatches between spec checklist and Linear
|
||||
|
||||
**Sync Options**:
|
||||
1. **Update spec to match reality** (recommended)
|
||||
2. **Update implementation to match spec**
|
||||
3. **Hybrid approach** (choose per item)
|
||||
4. **Review only**
|
||||
|
||||
**Drift Score**: 0-100% (lower is better, 0% = perfect sync)
|
||||
|
||||
### 1 New Help Command
|
||||
|
||||
#### 7. `/ccpm:utils:help [issue-id]`
|
||||
**Purpose**: Context-aware help and command suggestions
|
||||
|
||||
**Features**:
|
||||
- **General Mode**: Shows all commands categorized
|
||||
- **Context-Aware Mode**: Analyzes issue status and suggests relevant commands
|
||||
- **Priority-Based Suggestions**: High/Medium/Low based on current state
|
||||
- **Interactive Actions**: Quick action menu for common workflows
|
||||
- **Workflow Guidance**: Spec-first vs Task-first flowcharts
|
||||
|
||||
**Smart Suggestions**:
|
||||
- Status "Planning" → Suggest: Create spec, Write spec, Break down
|
||||
- Status "In Progress" → Suggest: Next action, Sync spec, Quality checks
|
||||
- Status "Verification" → Suggest: Run verification
|
||||
- Status "Done" → Suggest: Finalize, Final spec sync
|
||||
- Label "blocked" → Suggest: Fix issues, Review status
|
||||
|
||||
---
|
||||
|
||||
## 📁 Files Created
|
||||
|
||||
### Spec Management Commands (6 files)
|
||||
```
|
||||
`$CCPM_COMMANDS_DIR/`
|
||||
├── create.md - Create Epic/Feature with spec doc (459 lines)
|
||||
├── write.md - AI-assisted spec writing (934 lines)
|
||||
├── review.md - Spec validation & grading (333 lines)
|
||||
├── break-down.md - Epic→Features, Feature→Tasks (413 lines)
|
||||
├── migrate.md - Migrate .claude/ specs to Linear (634 lines)
|
||||
└── sync.md - Sync spec with implementation (536 lines)
|
||||
```
|
||||
|
||||
### Help Command (1 file)
|
||||
```
|
||||
`$CCPM_COMMANDS_DIR/`
|
||||
└── help.md - Context-aware help (434 lines)
|
||||
```
|
||||
|
||||
### Documentation Updates (1 file)
|
||||
```
|
||||
`$CCPM_COMMANDS_DIR/`
|
||||
└── README.md - Updated with spec management section
|
||||
```
|
||||
|
||||
**Total**: 8 files created/modified, ~3,743 lines of documentation
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Integration with Existing PM Commands
|
||||
|
||||
**Spec Management enhances PM Commands 2.0** (Interactive Mode + 10 workflow commands):
|
||||
|
||||
### Combined Workflow Options
|
||||
|
||||
#### Option 1: Spec-First Workflow (Recommended for Personal Project)
|
||||
```
|
||||
1. /ccpm:spec:create epic "User Auth"
|
||||
2. /ccpm:spec:write DOC-123 all
|
||||
3. /ccpm:spec:review DOC-123
|
||||
4. /ccpm:spec:break-down WORK-100 ← Creates Features
|
||||
5. /ccpm:spec:write DOC-124 all ← For each feature
|
||||
6. /ccpm:spec:break-down WORK-101 ← Creates Tasks
|
||||
7. /ccpm:implementation:start WORK-201
|
||||
8. /ccpm:spec:sync WORK-101 ← Keep in sync
|
||||
9. /ccpm:verification:check WORK-201
|
||||
10. /ccpm:complete:finalize WORK-201
|
||||
```
|
||||
|
||||
#### Option 2: Task-First Workflow (Quick tasks)
|
||||
```
|
||||
1. /ccpm:planning:create "Add dark mode" personal-project
|
||||
2. /ccpm:implementation:start WORK-300
|
||||
3. /ccpm:verification:check WORK-300
|
||||
4. /ccpm:complete:finalize WORK-300
|
||||
```
|
||||
|
||||
#### Option 3: Migrate Existing → Spec Workflow
|
||||
```
|
||||
1. /ccpm:spec:migrate ~/personal/personal-project
|
||||
2. Review migrated items in Linear
|
||||
3. /ccpm:spec:sync DOC-XXX ← Sync with codebase
|
||||
4. Continue with spec-first workflow
|
||||
```
|
||||
|
||||
### All PM Commands (25 Total)
|
||||
|
||||
**Spec Management (6):**
|
||||
- `/ccpm:spec:create`
|
||||
- `/ccpm:spec:write`
|
||||
- `/ccpm:spec:review`
|
||||
- `/ccpm:spec:break-down`
|
||||
- `/ccpm:spec:migrate`
|
||||
- `/ccpm:spec:sync`
|
||||
|
||||
**Planning (3):**
|
||||
- `/ccpm:planning:create`
|
||||
- `/ccpm:planning:plan`
|
||||
- `/ccpm:planning:quick-plan`
|
||||
|
||||
**Implementation (3):**
|
||||
- `/ccpm:implementation:start`
|
||||
- `/ccpm:implementation:next`
|
||||
- `/ccpm:implementation:update`
|
||||
|
||||
**Verification (3):**
|
||||
- `/ccpm:verification:check`
|
||||
- `/ccpm:verification:verify`
|
||||
- `/ccpm:verification:fix`
|
||||
|
||||
**Completion (1):**
|
||||
- `/ccpm:complete:finalize`
|
||||
|
||||
**Utilities (9):**
|
||||
- `/ccpm:utils:status`
|
||||
- `/ccpm:utils:context`
|
||||
- `/ccpm:utils:report`
|
||||
- `/ccpm:utils:insights`
|
||||
- `/ccpm:utils:auto-assign`
|
||||
- `/ccpm:utils:sync-status`
|
||||
- `/ccpm:utils:rollback`
|
||||
- `/ccpm:utils:dependencies`
|
||||
- `/ccpm:utils:agents`
|
||||
- `/ccpm:utils:help` ← NEW
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Use Cases
|
||||
|
||||
### Use Case 1: New Feature Development (personal-project)
|
||||
|
||||
**Scenario**: Building "Task Comments System" feature
|
||||
|
||||
```bash
|
||||
# 1. Create Feature with Spec
|
||||
/ccpm:spec:create feature "Task Comments System"
|
||||
# Creates: WORK-100 + DOC-123
|
||||
|
||||
# 2. Write Comprehensive Spec
|
||||
/ccpm:spec:write DOC-123 requirements
|
||||
/ccpm:spec:write DOC-123 api-design
|
||||
/ccpm:spec:write DOC-123 data-model
|
||||
/ccpm:spec:write DOC-123 testing
|
||||
# AI generates detailed specs based on codebase
|
||||
|
||||
# 3. Review & Validate
|
||||
/ccpm:spec:review DOC-123
|
||||
# Output: Grade A (92%) - Ready for approval
|
||||
|
||||
# 4. Break Down into Tasks
|
||||
/ccpm:spec:break-down WORK-100
|
||||
# Creates: WORK-101, WORK-102, WORK-103 (subtasks)
|
||||
|
||||
# 5. Implement
|
||||
/ccpm:implementation:start WORK-101
|
||||
# Works on first subtask
|
||||
|
||||
# 6. Keep Spec in Sync
|
||||
/ccpm:spec:sync WORK-100
|
||||
# Detects: API added optional field not in spec
|
||||
# Updates spec to match reality
|
||||
```
|
||||
|
||||
### Use Case 2: Migrating Existing Specs
|
||||
|
||||
**Scenario**: You have 45 markdown files in `.claude/` to migrate
|
||||
|
||||
```bash
|
||||
# 1. Run Migration
|
||||
/ccpm:spec:migrate ~/personal/personal-project
|
||||
|
||||
# Output: Detailed preview for ALL 45 files
|
||||
# - 2 Epics → Linear Initiatives + Spec Docs
|
||||
# - 12 Features → Parent Issues + Design Docs
|
||||
# - 25 Tasks → Issues (some with subtasks)
|
||||
# - 6 Docs → Reference documents
|
||||
|
||||
# 2. Confirm after reviewing preview
|
||||
# → Creates all items in Linear
|
||||
|
||||
# 3. Review in Linear
|
||||
/ccpm:utils:report personal-project
|
||||
# Shows all migrated items organized
|
||||
|
||||
# 4. Continue with spec workflow
|
||||
/ccpm:spec:sync WORK-102
|
||||
/ccpm:spec:break-down WORK-103
|
||||
```
|
||||
|
||||
### Use Case 3: Daily Development
|
||||
|
||||
**Morning:**
|
||||
```bash
|
||||
/ccpm:utils:report personal-project
|
||||
# Shows: 5 active tasks, 2 blocked, 3 in verification
|
||||
|
||||
/ccpm:utils:help WORK-150
|
||||
# Suggests: Continue with /ccpm:implementation:next WORK-150
|
||||
```
|
||||
|
||||
**During Work:**
|
||||
```bash
|
||||
/ccpm:implementation:next WORK-150
|
||||
# AI: Next task is Task 3 (no dependencies, ready)
|
||||
|
||||
/ccpm:spec:sync WORK-150
|
||||
# Drift Score: 15% (minor - API signature changed)
|
||||
# Updates spec to match
|
||||
```
|
||||
|
||||
**End of Day:**
|
||||
```bash
|
||||
/ccpm:verification:check WORK-150
|
||||
# All checks pass
|
||||
|
||||
/ccpm:complete:finalize WORK-150
|
||||
# Creates PR, updates Jira (with confirmation)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Key Features
|
||||
|
||||
### 1. Safety First
|
||||
- **Migration**: ALWAYS shows full preview, requires confirmation
|
||||
- **External Systems**: Never writes to Jira/Confluence/Slack without explicit approval
|
||||
- **Rollback**: Original files preserved in `.claude/migrated/`
|
||||
|
||||
### 2. AI-Powered
|
||||
- **Codebase Analysis**: Searches existing code for patterns
|
||||
- **Library Docs**: Fetches latest via Context7 MCP
|
||||
- **Smart Suggestions**: Context-aware next actions
|
||||
- **Complexity Analysis**: Estimates and risk identification
|
||||
|
||||
### 3. Spec-First Development
|
||||
- **Linear Documents** as source of truth
|
||||
- **Epic → Feature → Task** hierarchy
|
||||
- **Sync Detection** for spec drift
|
||||
- **Automated Breakdown** from specs
|
||||
|
||||
### 4. Interactive & Guided
|
||||
- **Context-Aware Help**: Suggests commands based on status
|
||||
- **Interactive Mode**: Every command suggests next action
|
||||
- **Workflow Guidance**: Built-in flowcharts and examples
|
||||
|
||||
### 5. Migration-Friendly
|
||||
- **Discovers** all markdown specs automatically
|
||||
- **Categorizes** intelligently (Epic/Feature/Task)
|
||||
- **Preserves** full content and metadata
|
||||
- **Safe** with backup and rollback
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next Steps for User
|
||||
|
||||
### Getting Started with Spec Management
|
||||
|
||||
1. **Migrate Existing Specs** (if you have any):
|
||||
```bash
|
||||
/ccpm:spec:migrate ~/personal/personal-project
|
||||
```
|
||||
|
||||
2. **Create First Epic with Spec**:
|
||||
```bash
|
||||
/ccpm:spec:create epic "Your Epic Name"
|
||||
/ccpm:spec:write DOC-XXX all
|
||||
/ccpm:spec:review DOC-XXX
|
||||
```
|
||||
|
||||
3. **Break Down into Features**:
|
||||
```bash
|
||||
/ccpm:spec:break-down WORK-XXX
|
||||
```
|
||||
|
||||
4. **Start Implementation**:
|
||||
```bash
|
||||
/ccpm:implementation:start WORK-YYY
|
||||
```
|
||||
|
||||
5. **Keep Spec in Sync**:
|
||||
```bash
|
||||
/ccpm:spec:sync WORK-XXX
|
||||
```
|
||||
|
||||
### Daily Workflow
|
||||
|
||||
**Morning:**
|
||||
```bash
|
||||
/ccpm:utils:report personal-project
|
||||
/ccpm:utils:help WORK-XXX # Get context-aware suggestions
|
||||
```
|
||||
|
||||
**During Work:**
|
||||
```bash
|
||||
/ccpm:implementation:next WORK-XXX
|
||||
/ccpm:spec:sync WORK-XXX # Periodically
|
||||
```
|
||||
|
||||
**End of Day:**
|
||||
```bash
|
||||
/ccpm:verification:check WORK-XXX
|
||||
/ccpm:complete:finalize WORK-XXX
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
**Main README**:
|
||||
- ``$CCPM_COMMANDS_DIR/`README.md`
|
||||
|
||||
**Spec Command Docs**:
|
||||
- ``$CCPM_COMMANDS_DIR/`*.md`
|
||||
|
||||
**Help Command**:
|
||||
```bash
|
||||
/ccpm:utils:help # General help
|
||||
/ccpm:utils:help WORK-123 # Context-aware help
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Summary
|
||||
|
||||
**Implemented**:
|
||||
- ✅ 6 Spec Management Commands
|
||||
- ✅ 1 Context-Aware Help Command
|
||||
- ✅ Comprehensive documentation
|
||||
- ✅ Safety-first migration process
|
||||
- ✅ AI-powered spec writing
|
||||
- ✅ Spec-implementation sync detection
|
||||
- ✅ Integration with PM Commands 2.0
|
||||
|
||||
**Benefits**:
|
||||
- 📐 Spec-first development workflow
|
||||
- 🔄 Keep specs in sync with code
|
||||
- 📦 Migrate existing markdown specs
|
||||
- 🤖 AI-assisted spec writing
|
||||
- 🎯 Context-aware guidance
|
||||
- ✅ Linear Documents as source of truth
|
||||
|
||||
**Total Commands**: 25 (was 19 in PM Commands 2.0, now 25 with Spec Management)
|
||||
|
||||
**Ready to use!** 🚀
|
||||
305
commands/_feature-flag-evaluator.md
Normal file
305
commands/_feature-flag-evaluator.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# Feature Flag Evaluator
|
||||
|
||||
Evaluates feature flags for Phase 6 rollout with deterministic rollout control and variant assignment.
|
||||
|
||||
## Purpose
|
||||
|
||||
This shared utility handles:
|
||||
- Feature flag configuration loading
|
||||
- Deterministic rollout control (consistent user assignment)
|
||||
- Variant assignment for A/B testing
|
||||
- Stage transitions (beta → early access → GA)
|
||||
- User preference overrides
|
||||
|
||||
## Algorithm
|
||||
|
||||
```javascript
|
||||
function evaluateFlag(userId, flagName, config) {
|
||||
// Step 1: Check if flag is enabled globally
|
||||
const flagDef = config.flags[flagName];
|
||||
if (!flagDef || !flagDef.enabled) {
|
||||
return { enabled: false, variant: 'disabled', reason: 'flag_disabled' };
|
||||
}
|
||||
|
||||
// Step 2: Check minimum version requirement
|
||||
const currentVersion = getPluginVersion(); // e.g., "2.3.0-beta.1"
|
||||
if (!meetsMinVersion(currentVersion, flagDef.min_version)) {
|
||||
return {
|
||||
enabled: false,
|
||||
variant: 'old_version',
|
||||
reason: 'version_requirement_not_met',
|
||||
required_version: flagDef.min_version,
|
||||
current_version: currentVersion
|
||||
};
|
||||
}
|
||||
|
||||
// Step 3: Check user overrides (highest priority)
|
||||
const userConfig = getUserConfig();
|
||||
if (flagDef.user_override && userConfig.feature_flags?.[flagName]?.override !== null) {
|
||||
const override = userConfig.feature_flags[flagName].override;
|
||||
return {
|
||||
enabled: override,
|
||||
variant: override ? 'user_enabled' : 'user_disabled',
|
||||
reason: 'user_override'
|
||||
};
|
||||
}
|
||||
|
||||
// Step 4: Deterministic rollout assignment
|
||||
// Use hash(userId + flagName + salt) % 100 for consistent assignment
|
||||
const hash = deterministicHash(userId, flagName);
|
||||
const rolloutPercentage = flagDef.rollout_percentage || 0;
|
||||
|
||||
if (hash >= rolloutPercentage) {
|
||||
return {
|
||||
enabled: false,
|
||||
variant: 'not_in_rollout',
|
||||
reason: 'rollout_percentage_not_reached',
|
||||
rollout_percentage: rolloutPercentage,
|
||||
user_hash: hash
|
||||
};
|
||||
}
|
||||
|
||||
// Step 5: Assign variant based on rollout
|
||||
const variant = assignVariant(flagDef.variants, hash);
|
||||
return {
|
||||
enabled: true,
|
||||
variant: variant,
|
||||
reason: 'flag_enabled',
|
||||
rollout_percentage: rolloutPercentage
|
||||
};
|
||||
}
|
||||
|
||||
function assignVariant(variants, hash) {
|
||||
// Assign control or treatment variant
|
||||
if (!variants) return 'default';
|
||||
|
||||
let cumulativePercentage = 0;
|
||||
for (const [variantName, variantDef] of Object.entries(variants)) {
|
||||
const variantPercentage = variantDef.percentage || 50;
|
||||
if (hash < cumulativePercentage + variantPercentage) {
|
||||
return variantName;
|
||||
}
|
||||
cumulativePercentage += variantPercentage;
|
||||
}
|
||||
|
||||
return 'default';
|
||||
}
|
||||
|
||||
function deterministicHash(userId, flagName) {
|
||||
// Simple deterministic hash: consistent across calls
|
||||
const combined = `${userId}:${flagName}`;
|
||||
let hash = 0;
|
||||
for (let i = 0; i < combined.length; i++) {
|
||||
const char = combined.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash % 100);
|
||||
}
|
||||
|
||||
function meetsMinVersion(current, required) {
|
||||
// Semantic version comparison
|
||||
const currentParts = current.split(/[.-]/);
|
||||
const requiredParts = required.split(/[.-]/);
|
||||
|
||||
for (let i = 0; i < Math.max(currentParts.length, requiredParts.length); i++) {
|
||||
const curr = parseInt(currentParts[i]) || 0;
|
||||
const req = parseInt(requiredParts[i]) || 0;
|
||||
|
||||
if (curr > req) return true;
|
||||
if (curr < req) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## Feature Flag Caching
|
||||
|
||||
Feature flags are loaded and cached at startup with 60-second refresh interval:
|
||||
|
||||
```javascript
|
||||
class FeatureFlagCache {
|
||||
constructor() {
|
||||
this.cache = new Map();
|
||||
this.lastRefresh = 0;
|
||||
this.refreshInterval = 60000; // 60 seconds
|
||||
}
|
||||
|
||||
async getFlag(userId, flagName) {
|
||||
// Return cached result if fresh
|
||||
const cacheKey = `${userId}:${flagName}`;
|
||||
if (this.cache.has(cacheKey)) {
|
||||
const cached = this.cache.get(cacheKey);
|
||||
if (Date.now() - cached.timestamp < this.refreshInterval) {
|
||||
return cached.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Load and cache flag configuration
|
||||
const config = await loadFeatureFlagsConfig();
|
||||
const result = evaluateFlag(userId, flagName, config);
|
||||
|
||||
this.cache.set(cacheKey, {
|
||||
value: result,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
invalidate(userId, flagName) {
|
||||
const cacheKey = `${userId}:${flagName}`;
|
||||
this.cache.delete(cacheKey);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache.clear();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## User Configuration
|
||||
|
||||
Users can override feature flags in `~/.claude/ccpm-config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"feature_flags": {
|
||||
"optimized_workflow_commands": {
|
||||
"override": true,
|
||||
"enabled": true
|
||||
},
|
||||
"linear_subagent_enabled": {
|
||||
"override": false,
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Stage Transitions
|
||||
|
||||
Feature flags automatically transition through stages based on configuration:
|
||||
|
||||
```
|
||||
Beta (Dec 9) → 10% rollout
|
||||
↓
|
||||
Early Access (Dec 21) → 30% rollout
|
||||
↓
|
||||
General Availability (Jan 6) → 100% rollout
|
||||
```
|
||||
|
||||
Stages are managed automatically by checking current date against `rollout_schedule` in feature-flags.json.
|
||||
|
||||
## Integration with Commands
|
||||
|
||||
All commands should check feature flags before execution:
|
||||
|
||||
```markdown
|
||||
Task(feature-flag-evaluator): `
|
||||
userId: {{currentUserId}}
|
||||
flag: "optimized_workflow_commands"
|
||||
`
|
||||
|
||||
If result.enabled is true:
|
||||
- Use optimized implementation
|
||||
Else:
|
||||
- Use legacy implementation
|
||||
- Show migration hint after completion
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
Feature flag evaluator tracks:
|
||||
- Number of users in each rollout stage
|
||||
- Variant distribution
|
||||
- Override usage
|
||||
- Cache hit rates
|
||||
- Evaluation latency
|
||||
|
||||
Metrics are stored in `.ccpm/metrics.json` and used for dashboards.
|
||||
|
||||
## Admin Commands
|
||||
|
||||
Admins can manage feature flags via commands:
|
||||
|
||||
```bash
|
||||
# View current feature flag status
|
||||
/ccpm:admin:flags --show
|
||||
|
||||
# Manually set rollout percentage
|
||||
/ccpm:admin:flags --set optimized_workflow_commands --percentage 50
|
||||
|
||||
# Override flag for specific user
|
||||
/ccpm:admin:flags --set optimized_workflow_commands --user-override true
|
||||
|
||||
# Trigger automatic rollback
|
||||
/ccpm:admin:flags --rollback optimized_workflow_commands
|
||||
|
||||
# View metrics
|
||||
/ccpm:admin:flags --metrics
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Feature flags can be tested in isolated environments:
|
||||
|
||||
```bash
|
||||
# Test with specific rollout percentage
|
||||
CCPM_FEATURE_FLAG_OVERRIDE=optimized_workflow_commands:50 claude --code
|
||||
|
||||
# Test with specific variant
|
||||
CCPM_FEATURE_FLAG_VARIANT=optimized_workflow_commands:treatment claude --code
|
||||
|
||||
# Disable all new features
|
||||
CCPM_FEATURE_FLAGS_DISABLED=true claude --code
|
||||
```
|
||||
|
||||
## Rollback Procedures
|
||||
|
||||
### Automatic Rollback (Monitoring)
|
||||
|
||||
When critical error threshold reached (5%+ error rate):
|
||||
|
||||
1. Monitoring detects error rate spike
|
||||
2. Automatically disables flag via feature-flags.json update
|
||||
3. Routes traffic to control variant
|
||||
4. Sends alert to on-call engineer
|
||||
5. No user action required (automatic recovery)
|
||||
|
||||
### Manual Rollback (User-Initiated)
|
||||
|
||||
User can disable feature flag:
|
||||
|
||||
```bash
|
||||
/ccpm:config feature-flags optimized_workflow_commands false
|
||||
```
|
||||
|
||||
### Emergency Rollback (Team-Initiated)
|
||||
|
||||
Team can trigger full rollback:
|
||||
|
||||
```bash
|
||||
/ccpm:admin:flags --emergency-rollback optimized_workflow_commands
|
||||
```
|
||||
|
||||
This immediately sets rollout_percentage to 0 and notifies all affected users.
|
||||
|
||||
## Success Metrics
|
||||
|
||||
Feature flag evaluator success is measured by:
|
||||
|
||||
- ✅ Consistent user assignment (same user gets same variant every time)
|
||||
- ✅ Accurate rollout percentages (±2% deviation allowed)
|
||||
- ✅ Sub-100ms evaluation latency
|
||||
- ✅ >90% cache hit rate
|
||||
- ✅ Zero cache invalidation bugs
|
||||
- ✅ Deterministic, reproducible results
|
||||
|
||||
## References
|
||||
|
||||
- `.ccpm/feature-flags.json` - Feature flag configuration
|
||||
- `~/.claude/ccpm-config.json` - User overrides
|
||||
- `.ccpm/metrics.json` - Evaluation metrics
|
||||
- `docs/guides/feature-flag-configuration.md` - User guide
|
||||
- `docs/architecture/feature-flag-system.md` - Design document
|
||||
801
commands/_shared-checklist-helpers.md
Normal file
801
commands/_shared-checklist-helpers.md
Normal file
@@ -0,0 +1,801 @@
|
||||
<!-- cSpell:words ccpm CCPM -->
|
||||
|
||||
# Shared Checklist Utilities (Unified Parsing & Update Logic)
|
||||
|
||||
This file provides reusable utility functions for checklist management across CCPM commands. **These functions implement robust parsing, updating, and progress calculation for Implementation Checklists in Linear issue descriptions.**
|
||||
|
||||
## Overview
|
||||
|
||||
All CCPM commands that interact with checklists should use these utilities to ensure consistent behavior:
|
||||
|
||||
- `parseChecklist()` - Extracts checklist from description (marker comments or header-based)
|
||||
- `updateChecklistItems()` - Updates checkbox states and recalculates progress
|
||||
- `calculateProgress()` - Computes completion percentage
|
||||
- `formatProgressLine()` - Generates standardized progress line
|
||||
- `validateChecklistStructure()` - Checks checklist integrity
|
||||
|
||||
**Key Benefits**:
|
||||
|
||||
- **Consistent parsing** - Handles both marker comments and header-based formats
|
||||
- **Robust updates** - Atomic checkbox state changes with progress calculation
|
||||
- **Error resilience** - Graceful handling of malformed or missing checklists
|
||||
- **Maintainability** - Single source of truth for checklist logic
|
||||
|
||||
**Usage in commands:** Reference this file at the start of command execution:
|
||||
|
||||
```markdown
|
||||
READ: commands/_shared-checklist-helpers.md
|
||||
```
|
||||
|
||||
Then use the functions as described below.
|
||||
|
||||
---
|
||||
|
||||
## Functions
|
||||
|
||||
### 1. parseChecklist
|
||||
|
||||
Extracts checklist items from a Linear issue description, supporting both marker comment and header-based formats.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Parse checklist from Linear issue description
|
||||
* @param {string} description - Full issue description (markdown)
|
||||
* @returns {Object|null} Parsed checklist or null if not found
|
||||
* @returns {Array<Object>} items - Parsed checklist items
|
||||
* @returns {number} items[].index - 0-based index
|
||||
* @returns {boolean} items[].checked - Checkbox state
|
||||
* @returns {string} items[].content - Item text (without checkbox)
|
||||
* @returns {string} format - 'marker' or 'header'
|
||||
* @returns {number} startLine - Line number where checklist starts
|
||||
* @returns {number} endLine - Line number where checklist ends
|
||||
* @returns {string|null} progressLine - Existing progress line text
|
||||
* @returns {number|null} progressLineNumber - Line number of progress line
|
||||
*/
|
||||
function parseChecklist(description) {
|
||||
if (!description || typeof description !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lines = description.split('\n');
|
||||
let format = null;
|
||||
let startLine = -1;
|
||||
let endLine = -1;
|
||||
let progressLine = null;
|
||||
let progressLineNumber = null;
|
||||
|
||||
// Strategy 1: Try marker comment detection (preferred)
|
||||
const startMarkerIndex = lines.findIndex(
|
||||
line => line.trim() === '<!-- ccpm-checklist-start -->'
|
||||
);
|
||||
|
||||
if (startMarkerIndex !== -1) {
|
||||
// Found start marker, look for end marker
|
||||
const endMarkerIndex = lines.findIndex(
|
||||
(line, idx) => idx > startMarkerIndex && line.trim() === '<!-- ccpm-checklist-end -->'
|
||||
);
|
||||
|
||||
if (endMarkerIndex !== -1) {
|
||||
format = 'marker';
|
||||
startLine = startMarkerIndex + 1; // First line after marker
|
||||
endLine = endMarkerIndex - 1; // Last line before marker
|
||||
|
||||
// Look for progress line after end marker (within 3 lines)
|
||||
for (let i = endMarkerIndex + 1; i < Math.min(endMarkerIndex + 4, lines.length); i++) {
|
||||
if (lines[i].match(/^Progress: \d+% \(\d+\/\d+ completed\)/)) {
|
||||
progressLine = lines[i];
|
||||
progressLineNumber = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 2: Fallback to header-based detection
|
||||
if (format === null) {
|
||||
const headerPatterns = [
|
||||
/^## ✅ Implementation Checklist/,
|
||||
/^## Implementation Checklist/,
|
||||
/^## ✅ Checklist/,
|
||||
/^## Checklist/
|
||||
];
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (headerPatterns.some(pattern => pattern.test(line))) {
|
||||
format = 'header';
|
||||
startLine = i + 1;
|
||||
|
||||
// Find end: next ## header or end of description
|
||||
endLine = lines.length - 1;
|
||||
for (let j = i + 1; j < lines.length; j++) {
|
||||
if (lines[j].match(/^##\s+/)) {
|
||||
endLine = j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for progress line before next header
|
||||
for (let j = i + 1; j <= endLine; j++) {
|
||||
if (lines[j].match(/^Progress: \d+% \(\d+\/\d+ completed\)/)) {
|
||||
progressLine = lines[j];
|
||||
progressLineNumber = j;
|
||||
// Exclude progress line from checklist items
|
||||
if (endLine >= j) {
|
||||
endLine = j - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No checklist found
|
||||
if (format === null || startLine === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract checklist items
|
||||
const items = [];
|
||||
const checkboxPattern = /^- \[([ x])\] (.+)$/;
|
||||
|
||||
for (let i = startLine; i <= endLine; i++) {
|
||||
const line = lines[i].trim();
|
||||
const match = line.match(checkboxPattern);
|
||||
|
||||
if (match) {
|
||||
items.push({
|
||||
index: items.length, // 0-based index
|
||||
checked: match[1] === 'x',
|
||||
content: match[2].trim(),
|
||||
originalLine: i
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Return null if no items found
|
||||
if (items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
format,
|
||||
startLine,
|
||||
endLine,
|
||||
progressLine,
|
||||
progressLineNumber
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const description = `
|
||||
## ✅ Implementation Checklist
|
||||
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1: First task
|
||||
- [x] Task 2: Second task
|
||||
- [ ] Task 3: Third task
|
||||
<!-- ccpm-checklist-end -->
|
||||
|
||||
Progress: 33% (1/3 completed)
|
||||
Last updated: 2025-11-22 14:30 UTC
|
||||
`;
|
||||
|
||||
const checklist = parseChecklist(description);
|
||||
|
||||
if (!checklist) {
|
||||
console.log("No checklist found");
|
||||
} else {
|
||||
console.log(`Found ${checklist.items.length} items`);
|
||||
console.log(`Format: ${checklist.format}`);
|
||||
console.log(`Progress: ${checklist.progressLine}`);
|
||||
|
||||
checklist.items.forEach(item => {
|
||||
console.log(`[${item.checked ? 'x' : ' '}] ${item.content}`);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Edge Cases Handled:**
|
||||
|
||||
- **No checklist** → Returns `null`
|
||||
- **Marker comments only** → Prefers marker-based parsing
|
||||
- **Header only** → Falls back to header detection
|
||||
- **Empty checklist** → Returns `null` (no items)
|
||||
- **Malformed items** → Skips lines that don't match pattern
|
||||
- **Multiple checklists** → Uses first one found (marker takes precedence)
|
||||
- **Progress line** → Detected and excluded from items
|
||||
|
||||
---
|
||||
|
||||
### 2. updateChecklistItems
|
||||
|
||||
Updates checkbox states for specific items and recalculates progress.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Update checklist item states and recalculate progress
|
||||
* @param {string} description - Original issue description
|
||||
* @param {number[]} indices - Array of item indices to update (0-based)
|
||||
* @param {boolean} markComplete - true = check boxes, false = uncheck boxes
|
||||
* @param {Object} options - Optional configuration
|
||||
* @param {boolean} options.addTimestamp - Add/update timestamp (default: true)
|
||||
* @returns {Object} Update result
|
||||
* @returns {string} updatedDescription - Modified description
|
||||
* @returns {number} changedCount - Number of items actually changed
|
||||
* @returns {Object} progress - New progress metrics
|
||||
* @returns {Array<string>} changedItems - Text of changed items (for logging)
|
||||
*/
|
||||
function updateChecklistItems(description, indices, markComplete, options = {}) {
|
||||
const addTimestamp = options.addTimestamp !== false;
|
||||
|
||||
// Parse existing checklist
|
||||
const checklist = parseChecklist(description);
|
||||
|
||||
if (!checklist) {
|
||||
throw new Error('No checklist found in description');
|
||||
}
|
||||
|
||||
if (indices.length === 0) {
|
||||
throw new Error('No indices provided for update');
|
||||
}
|
||||
|
||||
// Validate indices
|
||||
const invalidIndices = indices.filter(idx => idx < 0 || idx >= checklist.items.length);
|
||||
if (invalidIndices.length > 0) {
|
||||
throw new Error(`Invalid indices: ${invalidIndices.join(', ')}. Valid range: 0-${checklist.items.length - 1}`);
|
||||
}
|
||||
|
||||
// Split description into lines for modification
|
||||
const lines = description.split('\n');
|
||||
|
||||
// Track changes
|
||||
const changedItems = [];
|
||||
let changedCount = 0;
|
||||
|
||||
// Update checkbox states
|
||||
indices.forEach(idx => {
|
||||
const item = checklist.items[idx];
|
||||
const targetState = markComplete;
|
||||
|
||||
// Skip if already in target state
|
||||
if (item.checked === targetState) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find and update the line
|
||||
const lineIdx = item.originalLine;
|
||||
const currentLine = lines[lineIdx];
|
||||
|
||||
const newLine = markComplete
|
||||
? currentLine.replace('- [ ]', '- [x]')
|
||||
: currentLine.replace('- [x]', '- [ ]');
|
||||
|
||||
if (newLine !== currentLine) {
|
||||
lines[lineIdx] = newLine;
|
||||
changedItems.push(item.content);
|
||||
changedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
// Recalculate progress
|
||||
const updatedChecklist = parseChecklist(lines.join('\n'));
|
||||
if (!updatedChecklist) {
|
||||
throw new Error('Failed to parse updated checklist');
|
||||
}
|
||||
|
||||
const progress = calculateProgress(updatedChecklist.items);
|
||||
|
||||
// Update or insert progress line
|
||||
const newProgressLine = formatProgressLine(progress.completed, progress.total, addTimestamp);
|
||||
|
||||
if (updatedChecklist.progressLineNumber !== null) {
|
||||
// Replace existing progress line
|
||||
lines[updatedChecklist.progressLineNumber] = newProgressLine;
|
||||
} else {
|
||||
// Insert after checklist end (or after end marker if using markers)
|
||||
const insertPosition = checklist.format === 'marker'
|
||||
? checklist.endLine + 2 // After <!-- ccpm-checklist-end -->
|
||||
: checklist.endLine + 1; // After last checklist item
|
||||
|
||||
// Ensure we have a blank line before progress
|
||||
if (lines[insertPosition] && lines[insertPosition].trim() !== '') {
|
||||
lines.splice(insertPosition, 0, '', newProgressLine);
|
||||
} else {
|
||||
lines.splice(insertPosition, 0, newProgressLine);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
updatedDescription: lines.join('\n'),
|
||||
changedCount,
|
||||
progress,
|
||||
changedItems
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const description = `
|
||||
## ✅ Implementation Checklist
|
||||
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1: First task
|
||||
- [ ] Task 2: Second task
|
||||
- [ ] Task 3: Third task
|
||||
<!-- ccpm-checklist-end -->
|
||||
|
||||
Progress: 0% (0/3 completed)
|
||||
`;
|
||||
|
||||
// Mark items 0 and 2 as complete
|
||||
const result = updateChecklistItems(description, [0, 2], true);
|
||||
|
||||
console.log(`Changed ${result.changedCount} items`);
|
||||
console.log(`New progress: ${result.progress.percentage}%`);
|
||||
console.log(`Changed items: ${result.changedItems.join(', ')}`);
|
||||
console.log(result.updatedDescription);
|
||||
|
||||
// Output:
|
||||
// Changed 2 items
|
||||
// New progress: 67%
|
||||
// Changed items: Task 1: First task, Task 3: Third task
|
||||
// [Updated description with items 0 and 2 checked]
|
||||
```
|
||||
|
||||
**Edge Cases Handled:**
|
||||
|
||||
- **Invalid indices** → Throws error with details
|
||||
- **Already in target state** → Skips update (idempotent)
|
||||
- **No progress line** → Inserts new one
|
||||
- **Existing progress line** → Updates in place
|
||||
- **Empty indices array** → Throws error
|
||||
|
||||
---
|
||||
|
||||
### 3. calculateProgress
|
||||
|
||||
Computes completion percentage and counts from checklist items.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Calculate completion progress from checklist items
|
||||
* @param {Array<Object>} items - Checklist items from parseChecklist()
|
||||
* @returns {Object} Progress metrics
|
||||
* @returns {number} completed - Number of checked items
|
||||
* @returns {number} total - Total number of items
|
||||
* @returns {number} percentage - Completion percentage (0-100, rounded)
|
||||
*/
|
||||
function calculateProgress(items) {
|
||||
if (!items || items.length === 0) {
|
||||
return {
|
||||
completed: 0,
|
||||
total: 0,
|
||||
percentage: 0
|
||||
};
|
||||
}
|
||||
|
||||
const total = items.length;
|
||||
const completed = items.filter(item => item.checked).length;
|
||||
const percentage = Math.round((completed / total) * 100);
|
||||
|
||||
return {
|
||||
completed,
|
||||
total,
|
||||
percentage
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const items = [
|
||||
{ index: 0, checked: true, content: 'Task 1' },
|
||||
{ index: 1, checked: false, content: 'Task 2' },
|
||||
{ index: 2, checked: true, content: 'Task 3' }
|
||||
];
|
||||
|
||||
const progress = calculateProgress(items);
|
||||
console.log(`${progress.percentage}% (${progress.completed}/${progress.total})`);
|
||||
// Output: 67% (2/3)
|
||||
```
|
||||
|
||||
**Edge Cases Handled:**
|
||||
|
||||
- **Empty array** → Returns 0% (0/0)
|
||||
- **Null items** → Returns 0% (0/0)
|
||||
- **All complete** → Returns 100%
|
||||
- **None complete** → Returns 0%
|
||||
- **Fractional percentages** → Rounds to nearest integer
|
||||
|
||||
---
|
||||
|
||||
### 4. formatProgressLine
|
||||
|
||||
Generates standardized progress line with optional timestamp.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Format progress line for checklist
|
||||
* @param {number} completed - Number of completed items
|
||||
* @param {number} total - Total number of items
|
||||
* @param {boolean} includeTimestamp - Add timestamp (default: true)
|
||||
* @returns {string} Formatted progress line
|
||||
*/
|
||||
function formatProgressLine(completed, total, includeTimestamp = true) {
|
||||
const percentage = total === 0 ? 0 : Math.round((completed / total) * 100);
|
||||
let line = `Progress: ${percentage}% (${completed}/${total} completed)`;
|
||||
|
||||
if (includeTimestamp) {
|
||||
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 16);
|
||||
line += `\nLast updated: ${timestamp} UTC`;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
// With timestamp
|
||||
const line1 = formatProgressLine(2, 5);
|
||||
console.log(line1);
|
||||
// Output:
|
||||
// Progress: 40% (2/5 completed)
|
||||
// Last updated: 2025-11-22 14:30 UTC
|
||||
|
||||
// Without timestamp
|
||||
const line2 = formatProgressLine(3, 3, false);
|
||||
console.log(line2);
|
||||
// Output:
|
||||
// Progress: 100% (3/3 completed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. validateChecklistStructure
|
||||
|
||||
Validates checklist structure and returns warnings for issues.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Validate checklist structure and return warnings
|
||||
* @param {string} description - Issue description to validate
|
||||
* @returns {Object} Validation result
|
||||
* @returns {boolean} valid - Overall validity
|
||||
* @returns {Array<string>} warnings - List of warnings
|
||||
* @returns {Array<string>} suggestions - Suggestions to fix issues
|
||||
* @returns {Object|null} checklist - Parsed checklist (if valid)
|
||||
*/
|
||||
function validateChecklistStructure(description) {
|
||||
const warnings = [];
|
||||
const suggestions = [];
|
||||
|
||||
// Try to parse
|
||||
const checklist = parseChecklist(description);
|
||||
|
||||
if (!checklist) {
|
||||
return {
|
||||
valid: false,
|
||||
warnings: ['No checklist found in description'],
|
||||
suggestions: [
|
||||
'Add a checklist using marker comments:',
|
||||
' <!-- ccpm-checklist-start -->',
|
||||
' - [ ] Task 1',
|
||||
' - [ ] Task 2',
|
||||
' <!-- ccpm-checklist-end -->',
|
||||
'',
|
||||
'Or use a header:',
|
||||
' ## ✅ Implementation Checklist',
|
||||
' - [ ] Task 1',
|
||||
' - [ ] Task 2'
|
||||
],
|
||||
checklist: null
|
||||
};
|
||||
}
|
||||
|
||||
// Check for marker comments (preferred)
|
||||
if (checklist.format === 'header') {
|
||||
warnings.push('Using header-based format (marker comments preferred)');
|
||||
suggestions.push(
|
||||
'Consider adding marker comments for more reliable parsing:',
|
||||
' <!-- ccpm-checklist-start -->',
|
||||
' ... existing checklist items ...',
|
||||
' <!-- ccpm-checklist-end -->'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for progress line
|
||||
if (!checklist.progressLine) {
|
||||
warnings.push('No progress line found');
|
||||
suggestions.push(
|
||||
'Add a progress line after the checklist:',
|
||||
' Progress: 0% (0/N completed)',
|
||||
' Last updated: YYYY-MM-DD HH:MM UTC'
|
||||
);
|
||||
}
|
||||
|
||||
// Check for empty checklist
|
||||
if (checklist.items.length === 0) {
|
||||
warnings.push('Checklist is empty (no items)');
|
||||
suggestions.push('Add checklist items using the format: - [ ] Task description');
|
||||
}
|
||||
|
||||
// Check for very long items (>200 chars)
|
||||
const longItems = checklist.items.filter(item => item.content.length > 200);
|
||||
if (longItems.length > 0) {
|
||||
warnings.push(`${longItems.length} item(s) exceed 200 characters`);
|
||||
suggestions.push('Consider breaking long items into smaller tasks');
|
||||
}
|
||||
|
||||
// Check for duplicate content
|
||||
const contentMap = new Map();
|
||||
checklist.items.forEach(item => {
|
||||
const normalized = item.content.toLowerCase().trim();
|
||||
if (contentMap.has(normalized)) {
|
||||
contentMap.set(normalized, contentMap.get(normalized) + 1);
|
||||
} else {
|
||||
contentMap.set(normalized, 1);
|
||||
}
|
||||
});
|
||||
|
||||
const duplicates = Array.from(contentMap.entries()).filter(([_, count]) => count > 1);
|
||||
if (duplicates.length > 0) {
|
||||
warnings.push(`${duplicates.length} duplicate item(s) found`);
|
||||
suggestions.push('Review checklist for duplicate tasks');
|
||||
}
|
||||
|
||||
return {
|
||||
valid: warnings.length === 0,
|
||||
warnings,
|
||||
suggestions,
|
||||
checklist
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```javascript
|
||||
const description = `
|
||||
## Implementation Checklist
|
||||
- [ ] Task 1
|
||||
- [ ] Task 1
|
||||
`;
|
||||
|
||||
const validation = validateChecklistStructure(description);
|
||||
|
||||
console.log(`Valid: ${validation.valid}`);
|
||||
console.log('Warnings:');
|
||||
validation.warnings.forEach(w => console.log(` - ${w}`));
|
||||
console.log('Suggestions:');
|
||||
validation.suggestions.forEach(s => console.log(` ${s}`));
|
||||
|
||||
// Output:
|
||||
// Valid: false
|
||||
// Warnings:
|
||||
// - Using header-based format (marker comments preferred)
|
||||
// - No progress line found
|
||||
// - 1 duplicate item(s) found
|
||||
// Suggestions:
|
||||
// Consider adding marker comments for more reliable parsing:
|
||||
// <!-- ccpm-checklist-start -->
|
||||
// ... existing checklist items ...
|
||||
// <!-- ccpm-checklist-end -->
|
||||
// Add a progress line after the checklist:
|
||||
// Progress: 0% (0/N completed)
|
||||
// Last updated: YYYY-MM-DD HH:MM UTC
|
||||
// Review checklist for duplicate tasks
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Patterns
|
||||
|
||||
### Pattern 1: Simple Checklist Update
|
||||
|
||||
Used by: `/ccpm:sync`, `/ccpm:verify`, `/ccpm:done`
|
||||
|
||||
```javascript
|
||||
// 1. Fetch issue
|
||||
const issue = await getIssue(issueId);
|
||||
|
||||
// 2. Parse checklist
|
||||
const checklist = parseChecklist(issue.description);
|
||||
if (!checklist) {
|
||||
console.log('No checklist to update');
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Determine which items to update (e.g., from user selection)
|
||||
const indicesToComplete = [0, 2, 5];
|
||||
|
||||
// 4. Update checklist
|
||||
const result = updateChecklistItems(
|
||||
issue.description,
|
||||
indicesToComplete,
|
||||
true // mark complete
|
||||
);
|
||||
|
||||
// 5. Update Linear
|
||||
await updateIssueDescription(issueId, result.updatedDescription);
|
||||
|
||||
// 6. Log progress
|
||||
console.log(`✅ Updated ${result.changedCount} items`);
|
||||
console.log(`Progress: ${result.progress.percentage}%`);
|
||||
```
|
||||
|
||||
### Pattern 2: Validation Before Update
|
||||
|
||||
Used by: `/ccpm:utils:update-checklist`, `/ccpm:planning:update`
|
||||
|
||||
```javascript
|
||||
// 1. Validate checklist structure
|
||||
const validation = validateChecklistStructure(description);
|
||||
|
||||
if (!validation.valid) {
|
||||
console.log('⚠️ Checklist has issues:');
|
||||
validation.warnings.forEach(w => console.log(` - ${w}`));
|
||||
console.log('\nSuggestions:');
|
||||
validation.suggestions.forEach(s => console.log(` ${s}`));
|
||||
|
||||
// Ask user if they want to continue
|
||||
const proceed = await askUserQuestion('Continue anyway?');
|
||||
if (!proceed) return;
|
||||
}
|
||||
|
||||
// 2. Proceed with update...
|
||||
```
|
||||
|
||||
### Pattern 3: AI-Powered Suggestion
|
||||
|
||||
Used by: `/ccpm:sync` (smart checklist analysis)
|
||||
|
||||
```javascript
|
||||
// 1. Parse checklist
|
||||
const checklist = parseChecklist(issue.description);
|
||||
if (!checklist) return;
|
||||
|
||||
// 2. Analyze git changes and score items
|
||||
const uncheckedItems = checklist.items.filter(item => !item.checked);
|
||||
const scoredItems = scoreChecklistItems(uncheckedItems, gitChanges);
|
||||
|
||||
// 3. Present suggestions to user
|
||||
const highConfidence = scoredItems.filter(item => item.score >= 50);
|
||||
console.log('🤖 AI Suggestions (high confidence):');
|
||||
highConfidence.forEach(item => {
|
||||
console.log(` [${item.index}] ${item.content}`);
|
||||
});
|
||||
|
||||
// 4. Get user confirmation via interactive selection
|
||||
const selectedIndices = await askUserQuestion(/* ... */);
|
||||
|
||||
// 5. Update selected items
|
||||
const result = updateChecklistItems(
|
||||
issue.description,
|
||||
selectedIndices,
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
All functions throw descriptive errors for invalid inputs:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const result = updateChecklistItems(description, [0, 1], true);
|
||||
console.log('Update successful');
|
||||
} catch (error) {
|
||||
if (error.message.includes('No checklist found')) {
|
||||
console.error('❌ No checklist to update');
|
||||
// Suggest adding a checklist
|
||||
} else if (error.message.includes('Invalid indices')) {
|
||||
console.error('❌ Invalid item indices:', error.message);
|
||||
// Show valid range
|
||||
} else {
|
||||
console.error('❌ Update failed:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always validate inputs** - Use `parseChecklist()` before updates
|
||||
2. **Handle null gracefully** - Check if checklist exists before operations
|
||||
3. **Preserve user data** - Never modify items outside checklist section
|
||||
4. **Update atomically** - Use `updateChecklistItems()` for batch updates
|
||||
5. **Track changes** - Log `changedItems` and `changedCount` for audit trail
|
||||
6. **Prefer marker comments** - Encourage migration to marker-based format
|
||||
7. **Validate structure** - Use `validateChecklistStructure()` for quality checks
|
||||
8. **Add timestamps** - Include update timestamps for transparency
|
||||
|
||||
---
|
||||
|
||||
## Testing Helpers
|
||||
|
||||
Test these functions in isolation:
|
||||
|
||||
```javascript
|
||||
// Test parsing
|
||||
const testDescription = `
|
||||
## ✅ Implementation Checklist
|
||||
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1
|
||||
- [x] Task 2
|
||||
- [ ] Task 3
|
||||
<!-- ccpm-checklist-end -->
|
||||
|
||||
Progress: 33% (1/3 completed)
|
||||
`;
|
||||
|
||||
const checklist = parseChecklist(testDescription);
|
||||
console.log('Parsed items:', checklist.items.length);
|
||||
|
||||
// Test update
|
||||
const result = updateChecklistItems(testDescription, [0, 2], true);
|
||||
console.log('Updated description:', result.updatedDescription);
|
||||
console.log('Progress:', result.progress);
|
||||
|
||||
// Test validation
|
||||
const validation = validateChecklistStructure(testDescription);
|
||||
console.log('Valid:', validation.valid);
|
||||
console.log('Warnings:', validation.warnings);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Maintenance
|
||||
|
||||
### When to Update This File
|
||||
|
||||
1. **New checklist format** - Add support for new parsing patterns
|
||||
2. **Progress calculation changes** - Modify `calculateProgress()` logic
|
||||
3. **Timestamp format changes** - Update `formatProgressLine()` format
|
||||
4. **Validation rules** - Add new checks to `validateChecklistStructure()`
|
||||
|
||||
### Finding Usages
|
||||
|
||||
To find all commands using these helpers:
|
||||
|
||||
```bash
|
||||
grep -r "parseChecklist\|updateChecklistItems\|calculateProgress" commands/ | grep -v "_shared-checklist"
|
||||
```
|
||||
|
||||
### Version History
|
||||
|
||||
- **v1.0.0** - Initial implementation (Phase 1 of PSN-37)
|
||||
- Core parsing functions
|
||||
- Update logic with progress calculation
|
||||
- Validation utilities
|
||||
- Support for both marker and header formats
|
||||
|
||||
---
|
||||
|
||||
## Related Files
|
||||
|
||||
- `commands/_shared-linear-helpers.md` - Linear API delegation utilities
|
||||
- `commands/utils:update-checklist.md` - Interactive checklist update command
|
||||
- `commands/sync.md` - Natural sync command with AI checklist analysis
|
||||
- `agents/linear-operations.md` - Linear operations subagent
|
||||
|
||||
---
|
||||
|
||||
**This file is part of CCPM's unified checklist management system (PSN-37).**
|
||||
918
commands/_shared-decision-helpers.md
Normal file
918
commands/_shared-decision-helpers.md
Normal file
@@ -0,0 +1,918 @@
|
||||
# Shared Decision Helper Functions (Always-Ask Policy Implementation)
|
||||
|
||||
This file provides reusable decision-making utilities for implementing the Always-Ask Policy across all CCPM commands. These helpers enable consistent, confidence-based decision making with automatic user interaction when confidence is below thresholds.
|
||||
|
||||
## Overview
|
||||
|
||||
The Always-Ask Policy states: **When confidence < 80%, explicitly ask the user rather than making assumptions.**
|
||||
|
||||
These helper functions provide:
|
||||
- **Confidence calculation** - Score decisions 0-100 based on context
|
||||
- **Ask/proceed logic** - Automatically ask user when confidence is low
|
||||
- **User question formatting** - Standardized question templates
|
||||
- **Fuzzy matching** - Intelligent string matching with thresholds
|
||||
- **Validation** - Pre/during/post command validation
|
||||
|
||||
**Usage in commands:** Reference this file at the start of command execution:
|
||||
```markdown
|
||||
READ: commands/_shared-decision-helpers.md
|
||||
```
|
||||
|
||||
Then use the functions as described below.
|
||||
|
||||
---
|
||||
|
||||
## Core Functions
|
||||
|
||||
### 1. calculateConfidence
|
||||
|
||||
Calculates confidence score (0-100) for a decision based on multiple signals.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Calculate confidence score for a decision
|
||||
* @param {Object} context - Decision context
|
||||
* @param {any} context.input - User input to evaluate
|
||||
* @param {any} context.expected - Expected value/pattern
|
||||
* @param {Object} context.signals - Additional confidence signals
|
||||
* @param {number} context.signals.patternMatch - Pattern match confidence (0-100)
|
||||
* @param {number} context.signals.contextMatch - Context match confidence (0-100)
|
||||
* @param {number} context.signals.historicalSuccess - Historical success rate (0-100)
|
||||
* @param {number} context.signals.userPreference - User preference strength (0-100)
|
||||
* @returns {Object} Confidence result with score and reasoning
|
||||
*/
|
||||
function calculateConfidence(context) {
|
||||
let confidence = 0;
|
||||
const reasoning = [];
|
||||
|
||||
// Signal 1: Pattern Match (weight: 50%)
|
||||
if (context.signals?.patternMatch !== undefined) {
|
||||
const patternScore = context.signals.patternMatch * 0.5;
|
||||
confidence += patternScore;
|
||||
reasoning.push(`Pattern match: ${context.signals.patternMatch}% (weighted: ${patternScore.toFixed(0)})`);
|
||||
}
|
||||
|
||||
// Signal 2: Context Match (weight: 30%)
|
||||
if (context.signals?.contextMatch !== undefined) {
|
||||
const contextScore = context.signals.contextMatch * 0.3;
|
||||
confidence += contextScore;
|
||||
reasoning.push(`Context match: ${context.signals.contextMatch}% (weighted: ${contextScore.toFixed(0)})`);
|
||||
}
|
||||
|
||||
// Signal 3: Historical Success (weight: 20%)
|
||||
if (context.signals?.historicalSuccess !== undefined) {
|
||||
const historyScore = context.signals.historicalSuccess * 0.2;
|
||||
confidence += historyScore;
|
||||
reasoning.push(`Historical success: ${context.signals.historicalSuccess}% (weighted: ${historyScore.toFixed(0)})`);
|
||||
}
|
||||
|
||||
// Signal 4: User Preference (bonus: +10)
|
||||
if (context.signals?.userPreference !== undefined && context.signals.userPreference > 70) {
|
||||
const preferenceBonus = 10;
|
||||
confidence += preferenceBonus;
|
||||
reasoning.push(`User preference bonus: +${preferenceBonus}`);
|
||||
}
|
||||
|
||||
// Cap at 100
|
||||
confidence = Math.min(confidence, 100);
|
||||
|
||||
return {
|
||||
confidence: Math.round(confidence),
|
||||
reasoning: reasoning.join(', '),
|
||||
shouldAsk: confidence < 80,
|
||||
level: getConfidenceLevel(confidence)
|
||||
};
|
||||
}
|
||||
|
||||
function getConfidenceLevel(confidence) {
|
||||
if (confidence >= 95) return 'CERTAIN';
|
||||
if (confidence >= 80) return 'HIGH';
|
||||
if (confidence >= 50) return 'MEDIUM';
|
||||
return 'LOW';
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Calculate confidence for issue ID pattern
|
||||
const result = calculateConfidence({
|
||||
input: "PSN-29",
|
||||
expected: /^[A-Z]+-\d+$/,
|
||||
signals: {
|
||||
patternMatch: 100, // Exact regex match
|
||||
contextMatch: 90, // Branch name contains PSN-29
|
||||
historicalSuccess: 95 // 95% past success rate
|
||||
}
|
||||
});
|
||||
|
||||
// Result:
|
||||
// {
|
||||
// confidence: 93,
|
||||
// reasoning: "Pattern match: 100% (weighted: 50), Context match: 90% (weighted: 27), Historical success: 95% (weighted: 19)",
|
||||
// shouldAsk: false,
|
||||
// level: "HIGH"
|
||||
// }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. shouldAsk
|
||||
|
||||
Determines if user should be asked based on confidence level and policy.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Determine if user should be asked for confirmation
|
||||
* @param {number} confidence - Confidence score (0-100)
|
||||
* @param {Object} options - Additional options
|
||||
* @param {boolean} options.alwaysAsk - Force asking regardless of confidence
|
||||
* @param {boolean} options.neverAsk - Never ask (for certain operations)
|
||||
* @param {number} options.threshold - Custom confidence threshold (default: 80)
|
||||
* @returns {Object} Decision result
|
||||
*/
|
||||
function shouldAsk(confidence, options = {}) {
|
||||
// Override: Always ask (for safety-critical operations)
|
||||
if (options.alwaysAsk) {
|
||||
return {
|
||||
shouldAsk: true,
|
||||
reason: 'Safety-critical operation requires confirmation',
|
||||
displayMode: 'CONFIRM_REQUIRED'
|
||||
};
|
||||
}
|
||||
|
||||
// Override: Never ask (for validation operations)
|
||||
if (options.neverAsk) {
|
||||
return {
|
||||
shouldAsk: false,
|
||||
reason: 'Validation operation, no user input needed',
|
||||
displayMode: 'AUTO_PROCEED'
|
||||
};
|
||||
}
|
||||
|
||||
// Default threshold: 80%
|
||||
const threshold = options.threshold || 80;
|
||||
|
||||
if (confidence >= 95) {
|
||||
return {
|
||||
shouldAsk: false,
|
||||
reason: 'Certain - proceeding automatically',
|
||||
displayMode: 'AUTO_PROCEED_SILENT'
|
||||
};
|
||||
} else if (confidence >= threshold) {
|
||||
return {
|
||||
shouldAsk: false,
|
||||
reason: `High confidence (${confidence}%) - proceeding with display`,
|
||||
displayMode: 'AUTO_PROCEED_WITH_DISPLAY'
|
||||
};
|
||||
} else if (confidence >= 50) {
|
||||
return {
|
||||
shouldAsk: true,
|
||||
reason: `Medium confidence (${confidence}%) - suggesting with confirmation`,
|
||||
displayMode: 'SUGGEST_AND_CONFIRM'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
shouldAsk: true,
|
||||
reason: `Low confidence (${confidence}%) - asking user`,
|
||||
displayMode: 'ASK_WITHOUT_SUGGESTION'
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Check if we should ask
|
||||
const decision = shouldAsk(65);
|
||||
// Result: { shouldAsk: true, reason: "Medium confidence (65%) - suggesting with confirmation", displayMode: "SUGGEST_AND_CONFIRM" }
|
||||
|
||||
// Force asking for external writes
|
||||
const externalWrite = shouldAsk(95, { alwaysAsk: true });
|
||||
// Result: { shouldAsk: true, reason: "Safety-critical operation requires confirmation", displayMode: "CONFIRM_REQUIRED" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. askUserForClarification
|
||||
|
||||
Wrapper around AskUserQuestion tool with standardized formatting.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Ask user for clarification using AskUserQuestion tool
|
||||
* @param {Object} questionConfig - Question configuration
|
||||
* @param {string} questionConfig.question - The question to ask
|
||||
* @param {string} questionConfig.header - Short header (max 12 chars)
|
||||
* @param {Array} questionConfig.options - Array of options
|
||||
* @param {any} questionConfig.options[].label - Option label
|
||||
* @param {string} questionConfig.options[].description - Option description
|
||||
* @param {any} questionConfig.options[].value - Option value (optional, defaults to label)
|
||||
* @param {boolean} questionConfig.multiSelect - Allow multiple selections (default: false)
|
||||
* @param {any} questionConfig.suggestion - Suggested value (for pre-selection)
|
||||
* @param {number} questionConfig.confidence - Confidence in suggestion (for display)
|
||||
* @returns {Promise<any>} User's selected value(s)
|
||||
*/
|
||||
async function askUserForClarification(questionConfig) {
|
||||
const {
|
||||
question,
|
||||
header,
|
||||
options,
|
||||
multiSelect = false,
|
||||
suggestion = null,
|
||||
confidence = null
|
||||
} = questionConfig;
|
||||
|
||||
// Format options with suggestion highlighting
|
||||
const formattedOptions = options.map(opt => {
|
||||
const isSuggestion = suggestion && (opt.value === suggestion || opt.label === suggestion);
|
||||
|
||||
return {
|
||||
label: isSuggestion ? `${opt.label} ⭐ (suggested)` : opt.label,
|
||||
description: isSuggestion && confidence
|
||||
? `${opt.description} (${confidence}% confidence)`
|
||||
: opt.description
|
||||
};
|
||||
});
|
||||
|
||||
// Display question context if medium/low confidence
|
||||
if (confidence !== null && confidence < 80) {
|
||||
console.log(`\n💡 AI Confidence: ${confidence}%`);
|
||||
if (suggestion) {
|
||||
console.log(` Suggested: ${suggestion}`);
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Ask user
|
||||
const answer = await AskUserQuestion({
|
||||
questions: [{
|
||||
question,
|
||||
header,
|
||||
multiSelect,
|
||||
options: formattedOptions
|
||||
}]
|
||||
});
|
||||
|
||||
// Extract answer (remove suggestion marker if present)
|
||||
const rawAnswer = answer[header];
|
||||
const cleanedAnswer = Array.isArray(rawAnswer)
|
||||
? rawAnswer.map(a => a.replace(' ⭐ (suggested)', ''))
|
||||
: rawAnswer.replace(' ⭐ (suggested)', '');
|
||||
|
||||
return cleanedAnswer;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Ask with suggestion (medium confidence)
|
||||
const mode = await askUserForClarification({
|
||||
question: "What would you like to do?",
|
||||
header: "Mode",
|
||||
options: [
|
||||
{ label: "Create new", description: "Start from scratch" },
|
||||
{ label: "Plan existing", description: "Add plan to existing issue" },
|
||||
{ label: "Update plan", description: "Modify existing plan" }
|
||||
],
|
||||
suggestion: "Plan existing",
|
||||
confidence: 65
|
||||
});
|
||||
|
||||
// Ask without suggestion (low confidence)
|
||||
const commitType = await askUserForClarification({
|
||||
question: "Select commit type:",
|
||||
header: "Type",
|
||||
options: [
|
||||
{ label: "feat", description: "New feature" },
|
||||
{ label: "fix", description: "Bug fix" },
|
||||
{ label: "docs", description: "Documentation" }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. displayOptionsAndConfirm
|
||||
|
||||
Display what will happen and ask for confirmation.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Display proposed action and ask for confirmation
|
||||
* @param {string} action - Action description
|
||||
* @param {Object} details - Action details to display
|
||||
* @param {Object} options - Display options
|
||||
* @param {string} options.title - Section title (default: "Proposed Action")
|
||||
* @param {string} options.emoji - Title emoji (default: "📋")
|
||||
* @param {boolean} options.requireExplicitYes - Require "yes" instead of any confirmation (default: false)
|
||||
* @returns {Promise<boolean>} True if confirmed, false otherwise
|
||||
*/
|
||||
async function displayOptionsAndConfirm(action, details, options = {}) {
|
||||
const {
|
||||
title = 'Proposed Action',
|
||||
emoji = '📋',
|
||||
requireExplicitYes = false
|
||||
} = options;
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(`${emoji} ${title}`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log(action);
|
||||
console.log('');
|
||||
|
||||
// Display details
|
||||
Object.entries(details).forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
console.log(`${key}:`);
|
||||
value.forEach(item => console.log(` • ${item}`));
|
||||
} else {
|
||||
console.log(`${key}: ${value}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
// Ask for confirmation
|
||||
const answer = await askUserForClarification({
|
||||
question: "Proceed with this action?",
|
||||
header: "Confirm",
|
||||
options: [
|
||||
{ label: "Yes, proceed", description: "Execute the action shown above" },
|
||||
{ label: "No, cancel", description: "Cancel and return" }
|
||||
]
|
||||
});
|
||||
|
||||
const confirmed = answer.toLowerCase().includes('yes');
|
||||
|
||||
if (!confirmed) {
|
||||
console.log('❌ Cancelled\n');
|
||||
}
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Display and confirm external write
|
||||
const confirmed = await displayOptionsAndConfirm(
|
||||
"Update Jira ticket TRAIN-456",
|
||||
{
|
||||
"Status": "In Progress → Done",
|
||||
"Comment": "Completed via /ccpm:done",
|
||||
"Labels": ["ccpm", "completed"]
|
||||
},
|
||||
{
|
||||
title: "External System Write",
|
||||
emoji: "🚨",
|
||||
requireExplicitYes: true
|
||||
}
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
// Proceed with write
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. fuzzyMatch
|
||||
|
||||
Intelligent fuzzy matching with confidence scoring.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Fuzzy match input against options with confidence scoring
|
||||
* @param {string} input - User input
|
||||
* @param {Array} options - Array of valid options (strings or objects with 'name' property)
|
||||
* @param {Object} config - Matching configuration
|
||||
* @param {number} config.threshold - Minimum similarity threshold (0-100, default: 60)
|
||||
* @param {boolean} config.caseSensitive - Case sensitive matching (default: false)
|
||||
* @param {Array} config.aliases - Map of aliases to canonical values
|
||||
* @returns {Object} Match result
|
||||
*/
|
||||
function fuzzyMatch(input, options, config = {}) {
|
||||
const {
|
||||
threshold = 60,
|
||||
caseSensitive = false,
|
||||
aliases = {}
|
||||
} = config;
|
||||
|
||||
// Normalize input
|
||||
const normalizedInput = caseSensitive ? input : input.toLowerCase().trim();
|
||||
|
||||
// Check aliases first
|
||||
if (aliases[normalizedInput]) {
|
||||
return {
|
||||
match: aliases[normalizedInput],
|
||||
confidence: 100,
|
||||
exactMatch: true,
|
||||
reason: 'Alias match'
|
||||
};
|
||||
}
|
||||
|
||||
// Extract option strings
|
||||
const optionStrings = options.map(opt =>
|
||||
typeof opt === 'string' ? opt : opt.name || opt.label || String(opt)
|
||||
);
|
||||
|
||||
// Normalize options
|
||||
const normalizedOptions = optionStrings.map(opt =>
|
||||
caseSensitive ? opt : opt.toLowerCase().trim()
|
||||
);
|
||||
|
||||
// Strategy 1: Exact match
|
||||
const exactIndex = normalizedOptions.findIndex(opt => opt === normalizedInput);
|
||||
if (exactIndex !== -1) {
|
||||
return {
|
||||
match: optionStrings[exactIndex],
|
||||
confidence: 100,
|
||||
exactMatch: true,
|
||||
reason: 'Exact match'
|
||||
};
|
||||
}
|
||||
|
||||
// Strategy 2: Starts with
|
||||
const startsWithIndex = normalizedOptions.findIndex(opt => opt.startsWith(normalizedInput));
|
||||
if (startsWithIndex !== -1) {
|
||||
return {
|
||||
match: optionStrings[startsWithIndex],
|
||||
confidence: 85,
|
||||
exactMatch: false,
|
||||
reason: 'Starts with match'
|
||||
};
|
||||
}
|
||||
|
||||
// Strategy 3: Contains
|
||||
const containsIndex = normalizedOptions.findIndex(opt => opt.includes(normalizedInput));
|
||||
if (containsIndex !== -1) {
|
||||
return {
|
||||
match: optionStrings[containsIndex],
|
||||
confidence: 70,
|
||||
exactMatch: false,
|
||||
reason: 'Contains match'
|
||||
};
|
||||
}
|
||||
|
||||
// Strategy 4: Levenshtein distance (simplified)
|
||||
const distances = normalizedOptions.map(opt => ({
|
||||
option: opt,
|
||||
distance: levenshteinDistance(normalizedInput, opt)
|
||||
}));
|
||||
|
||||
distances.sort((a, b) => a.distance - b.distance);
|
||||
const closest = distances[0];
|
||||
|
||||
// Calculate similarity percentage
|
||||
const maxLen = Math.max(normalizedInput.length, closest.option.length);
|
||||
const similarity = ((maxLen - closest.distance) / maxLen) * 100;
|
||||
|
||||
if (similarity >= threshold) {
|
||||
const matchIndex = normalizedOptions.indexOf(closest.option);
|
||||
return {
|
||||
match: optionStrings[matchIndex],
|
||||
confidence: Math.round(similarity),
|
||||
exactMatch: false,
|
||||
reason: `Fuzzy match (${closest.distance} edits)`
|
||||
};
|
||||
}
|
||||
|
||||
// No match found
|
||||
return {
|
||||
match: null,
|
||||
confidence: 0,
|
||||
exactMatch: false,
|
||||
reason: 'No match found',
|
||||
suggestions: optionStrings.slice(0, 3)
|
||||
};
|
||||
}
|
||||
|
||||
// Simplified Levenshtein distance
|
||||
function levenshteinDistance(str1, str2) {
|
||||
const len1 = str1.length;
|
||||
const len2 = str2.length;
|
||||
const matrix = Array(len1 + 1).fill(null).map(() => Array(len2 + 1).fill(0));
|
||||
|
||||
for (let i = 0; i <= len1; i++) matrix[i][0] = i;
|
||||
for (let j = 0; j <= len2; j++) matrix[0][j] = j;
|
||||
|
||||
for (let i = 1; i <= len1; i++) {
|
||||
for (let j = 1; j <= len2; j++) {
|
||||
const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
|
||||
matrix[i][j] = Math.min(
|
||||
matrix[i - 1][j] + 1, // deletion
|
||||
matrix[i][j - 1] + 1, // insertion
|
||||
matrix[i - 1][j - 1] + cost // substitution
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return matrix[len1][len2];
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Fuzzy match state name
|
||||
const result = fuzzyMatch("in prog", [
|
||||
"Backlog",
|
||||
"In Progress",
|
||||
"In Review",
|
||||
"Done"
|
||||
]);
|
||||
// Result: { match: "In Progress", confidence: 70, exactMatch: false, reason: "Contains match" }
|
||||
|
||||
// Fuzzy match with aliases
|
||||
const typeResult = fuzzyMatch("bugfix", ["feat", "fix", "docs"], {
|
||||
aliases: {
|
||||
"bugfix": "fix",
|
||||
"feature": "feat",
|
||||
"documentation": "docs"
|
||||
}
|
||||
});
|
||||
// Result: { match: "fix", confidence: 100, exactMatch: true, reason: "Alias match" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation Functions
|
||||
|
||||
### 6. validateTransition
|
||||
|
||||
Validate workflow state transitions.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Validate if state transition is allowed
|
||||
* @param {string} fromState - Current state
|
||||
* @param {string} toState - Target state
|
||||
* @param {Object} stateMachine - State machine definition
|
||||
* @returns {Object} Validation result
|
||||
*/
|
||||
function validateTransition(fromState, toState, stateMachine) {
|
||||
const currentStateConfig = stateMachine[fromState];
|
||||
|
||||
if (!currentStateConfig) {
|
||||
return {
|
||||
valid: false,
|
||||
confidence: 0,
|
||||
error: `Unknown state: ${fromState}`,
|
||||
suggestions: Object.keys(stateMachine)
|
||||
};
|
||||
}
|
||||
|
||||
const allowedNextStates = currentStateConfig.next_states || [];
|
||||
|
||||
if (!allowedNextStates.includes(toState)) {
|
||||
return {
|
||||
valid: false,
|
||||
confidence: 0,
|
||||
error: `Cannot transition from ${fromState} to ${toState}`,
|
||||
allowedStates: allowedNextStates,
|
||||
suggestions: allowedNextStates.map(s => `Try: ${s}`)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
confidence: currentStateConfig.confidence_to_transition || 90
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern Matching Functions
|
||||
|
||||
### 7. Common Patterns
|
||||
|
||||
```javascript
|
||||
// Issue ID pattern (PROJECT-NUMBER)
|
||||
const ISSUE_ID_PATTERN = /^[A-Z]+-\d+$/;
|
||||
|
||||
function isIssueId(input) {
|
||||
return ISSUE_ID_PATTERN.test(input);
|
||||
}
|
||||
|
||||
function detectIssueIdConfidence(input) {
|
||||
if (ISSUE_ID_PATTERN.test(input)) {
|
||||
return { confidence: 95, match: true };
|
||||
}
|
||||
|
||||
// Partial match (e.g., "PSN" or "123")
|
||||
if (/^[A-Z]+$/.test(input) || /^\d+$/.test(input)) {
|
||||
return { confidence: 30, match: false, suggestion: 'Provide full issue ID (e.g., PSN-29)' };
|
||||
}
|
||||
|
||||
return { confidence: 0, match: false };
|
||||
}
|
||||
|
||||
// Quoted string pattern (for titles)
|
||||
const QUOTED_STRING_PATTERN = /^["'].*["']$/;
|
||||
|
||||
function isQuotedString(input) {
|
||||
return QUOTED_STRING_PATTERN.test(input);
|
||||
}
|
||||
|
||||
// Detect change type from update text
|
||||
function detectChangeType(text) {
|
||||
const lower = text.toLowerCase();
|
||||
|
||||
const patterns = {
|
||||
scope_change: /(add|also|include|plus|additionally|extra)/i,
|
||||
approach_change: /(instead|different|change|use.*not|replace.*with)/i,
|
||||
simplification: /(remove|don't need|skip|simpler|drop|omit)/i,
|
||||
blocker: /(blocked|can't|cannot|doesn't work|issue|problem|error)/i,
|
||||
clarification: /.*/ // Default
|
||||
};
|
||||
|
||||
for (const [type, pattern] of Object.entries(patterns)) {
|
||||
if (pattern.test(lower) && type !== 'clarification') {
|
||||
return {
|
||||
type,
|
||||
confidence: 75,
|
||||
keywords: lower.match(pattern)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'clarification',
|
||||
confidence: 50,
|
||||
keywords: []
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Display Helpers
|
||||
|
||||
### 8. Confidence Display
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Display confidence level with appropriate emoji and color
|
||||
* @param {number} confidence - Confidence score (0-100)
|
||||
* @param {string} message - Message to display
|
||||
*/
|
||||
function displayWithConfidence(confidence, message) {
|
||||
let emoji, prefix;
|
||||
|
||||
if (confidence >= 95) {
|
||||
emoji = '✅';
|
||||
prefix = 'CERTAIN';
|
||||
} else if (confidence >= 80) {
|
||||
emoji = '✅';
|
||||
prefix = `HIGH (${confidence}%)`;
|
||||
} else if (confidence >= 50) {
|
||||
emoji = '⚠️';
|
||||
prefix = `MEDIUM (${confidence}%)`;
|
||||
} else {
|
||||
emoji = '❓';
|
||||
prefix = `LOW (${confidence}%)`;
|
||||
}
|
||||
|
||||
console.log(`${emoji} ${prefix}: ${message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display decision summary
|
||||
* @param {Object} decision - Decision result
|
||||
*/
|
||||
function displayDecision(decision) {
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('🎯 Decision Summary');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
displayWithConfidence(decision.confidence, decision.suggestion || decision.action);
|
||||
|
||||
if (decision.reasoning) {
|
||||
console.log(`\n📊 Reasoning: ${decision.reasoning}`);
|
||||
}
|
||||
|
||||
if (decision.shouldAsk) {
|
||||
console.log(`\n👤 User input required`);
|
||||
} else {
|
||||
console.log(`\n🤖 Proceeding automatically`);
|
||||
}
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### 9. Structured Error Messages
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Create structured error with suggestions
|
||||
* @param {string} message - Error message
|
||||
* @param {Object} details - Error details
|
||||
* @param {Array} details.suggestions - Actionable suggestions
|
||||
* @param {Array} details.availableOptions - Available options
|
||||
* @returns {Error} Enhanced error object
|
||||
*/
|
||||
function createStructuredError(message, details = {}) {
|
||||
const error = new Error(message);
|
||||
error.details = details;
|
||||
|
||||
// Format error message with suggestions
|
||||
let fullMessage = message;
|
||||
|
||||
if (details.availableOptions && details.availableOptions.length > 0) {
|
||||
fullMessage += '\n\nAvailable options:';
|
||||
details.availableOptions.forEach(opt => {
|
||||
fullMessage += `\n • ${opt}`;
|
||||
});
|
||||
}
|
||||
|
||||
if (details.suggestions && details.suggestions.length > 0) {
|
||||
fullMessage += '\n\nSuggestions:';
|
||||
details.suggestions.forEach(suggestion => {
|
||||
fullMessage += `\n • ${suggestion}`;
|
||||
});
|
||||
}
|
||||
|
||||
error.message = fullMessage;
|
||||
return error;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Example
|
||||
|
||||
Complete example showing all helpers working together:
|
||||
|
||||
```javascript
|
||||
// Example: Command routing with Always-Ask Policy
|
||||
async function executeSmartPlan(args) {
|
||||
const arg1 = args[0];
|
||||
const arg2 = args[1];
|
||||
|
||||
// Step 1: Detect mode with confidence
|
||||
const issueIdCheck = detectIssueIdConfidence(arg1);
|
||||
|
||||
let modeDecision;
|
||||
if (issueIdCheck.match) {
|
||||
// High confidence: Issue ID pattern
|
||||
if (arg2) {
|
||||
modeDecision = {
|
||||
mode: 'UPDATE',
|
||||
confidence: 95,
|
||||
reasoning: 'Issue ID with update text provided'
|
||||
};
|
||||
} else {
|
||||
modeDecision = {
|
||||
mode: 'PLAN',
|
||||
confidence: 95,
|
||||
reasoning: 'Issue ID without update text'
|
||||
};
|
||||
}
|
||||
} else if (isQuotedString(arg1)) {
|
||||
// High confidence: Quoted title
|
||||
modeDecision = {
|
||||
mode: 'CREATE',
|
||||
confidence: 90,
|
||||
reasoning: 'Quoted string indicates new task title'
|
||||
};
|
||||
} else {
|
||||
// Low confidence: Ambiguous
|
||||
modeDecision = {
|
||||
mode: null,
|
||||
confidence: 30,
|
||||
reasoning: 'Input format is ambiguous'
|
||||
};
|
||||
}
|
||||
|
||||
// Step 2: Check if we should ask
|
||||
const askDecision = shouldAsk(modeDecision.confidence);
|
||||
|
||||
// Step 3: Ask user if needed
|
||||
if (askDecision.shouldAsk) {
|
||||
displayDecision({
|
||||
...modeDecision,
|
||||
shouldAsk: true,
|
||||
action: `Detected input: "${arg1}"`
|
||||
});
|
||||
|
||||
const mode = await askUserForClarification({
|
||||
question: "What would you like to do?",
|
||||
header: "Mode",
|
||||
options: [
|
||||
{ label: "Create new", description: "Create new task with this title" },
|
||||
{ label: "Plan existing", description: "Plan existing issue" },
|
||||
{ label: "Update plan", description: "Update existing plan" }
|
||||
],
|
||||
suggestion: modeDecision.mode,
|
||||
confidence: modeDecision.confidence
|
||||
});
|
||||
|
||||
modeDecision.mode = mode.replace(' new', '').replace(' existing', '').replace(' plan', '');
|
||||
} else {
|
||||
// High confidence: Display and proceed
|
||||
displayWithConfidence(modeDecision.confidence, `Mode: ${modeDecision.mode}`);
|
||||
}
|
||||
|
||||
// Step 4: Execute mode
|
||||
switch (modeDecision.mode.toUpperCase()) {
|
||||
case 'CREATE':
|
||||
await executeCreate(arg1, arg2);
|
||||
break;
|
||||
case 'PLAN':
|
||||
await executePlan(arg1);
|
||||
break;
|
||||
case 'UPDATE':
|
||||
await executeUpdate(arg1, arg2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always calculate confidence** - Don't guess, use signals
|
||||
2. **Display confidence scores** - Be transparent with users
|
||||
3. **Provide reasoning** - Explain why confidence is at a level
|
||||
4. **Ask with suggestions** - Pre-select high confidence options
|
||||
5. **Validate early** - Catch errors before execution
|
||||
6. **Use fuzzy matching** - Be forgiving with user input
|
||||
7. **Log decisions** - Track accuracy over time
|
||||
8. **Fail gracefully** - Provide actionable error messages
|
||||
|
||||
---
|
||||
|
||||
## Testing Helpers
|
||||
|
||||
```javascript
|
||||
// Test confidence calculation
|
||||
const testConfidence = calculateConfidence({
|
||||
input: "PSN-29",
|
||||
signals: {
|
||||
patternMatch: 100,
|
||||
contextMatch: 90,
|
||||
historicalSuccess: 95
|
||||
}
|
||||
});
|
||||
console.log('Confidence test:', testConfidence);
|
||||
|
||||
// Test fuzzy matching
|
||||
const testMatch = fuzzyMatch("in prog", ["Backlog", "In Progress", "Done"]);
|
||||
console.log('Fuzzy match test:', testMatch);
|
||||
|
||||
// Test should ask
|
||||
const testAsk = shouldAsk(65);
|
||||
console.log('Should ask test:', testAsk);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
| Operation | Avg Time | Notes |
|
||||
|-----------|----------|-------|
|
||||
| calculateConfidence | <1ms | Synchronous calculation |
|
||||
| shouldAsk | <1ms | Simple threshold check |
|
||||
| askUserForClarification | Varies | Waits for user input |
|
||||
| fuzzyMatch | 1-5ms | Depends on options count |
|
||||
| displayOptionsAndConfirm | Varies | Waits for user input |
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
**Old Pattern (no confidence tracking)**:
|
||||
```javascript
|
||||
if (ISSUE_ID_PATTERN.test(arg1)) {
|
||||
mode = 'PLAN';
|
||||
} else {
|
||||
mode = 'CREATE';
|
||||
}
|
||||
```
|
||||
|
||||
**New Pattern (with Always-Ask Policy)**:
|
||||
```javascript
|
||||
const issueIdCheck = detectIssueIdConfidence(arg1);
|
||||
const decision = shouldAsk(issueIdCheck.confidence);
|
||||
|
||||
if (decision.shouldAsk) {
|
||||
mode = await askUserForClarification({ ... });
|
||||
} else {
|
||||
displayWithConfidence(issueIdCheck.confidence, `Mode: ${mode}`);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- [Decision Framework](../docs/architecture/decision-framework.md)
|
||||
- [Decision Trees](../docs/architecture/decision-trees-visual.md)
|
||||
- [Implementation Guide](../docs/guides/implementing-always-ask-policy.md)
|
||||
- [Workflow State Tracking](./_shared-workflow-state.md)
|
||||
271
commands/_shared-figma-detection.md
Normal file
271
commands/_shared-figma-detection.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# Shared: Figma Link Detection and Context Extraction
|
||||
|
||||
This shared module detects and processes Figma links from Linear issues, external PM systems, and related documentation.
|
||||
|
||||
## When to Use
|
||||
|
||||
Include this step in any command that needs to extract Figma design specifications:
|
||||
- `/ccpm:planning:plan` - Extract Figma links during planning phase
|
||||
- `/ccpm:planning:design-ui` - Replace ASCII wireframes with Figma designs
|
||||
- `/ccpm:implementation:start` - Load Figma context for implementation
|
||||
- `/ccpm:utils:context` - Include Figma designs in task context
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Detect Figma Links
|
||||
|
||||
Use `scripts/figma-utils.sh` to extract Figma URLs from text sources:
|
||||
|
||||
```bash
|
||||
# From Linear issue description/comments
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESCRIPTION")
|
||||
|
||||
# From plain text (Jira, Confluence, Slack)
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh detect "$EXTERNAL_PM_CONTENT")
|
||||
```
|
||||
|
||||
**Sources to search:**
|
||||
- Linear issue description and comments
|
||||
- Jira ticket description, comments, and attachments
|
||||
- Confluence pages linked to the task
|
||||
- Slack thread discussions
|
||||
- PRD and design documents
|
||||
|
||||
### 2. Select Appropriate MCP Server
|
||||
|
||||
Determine which Figma MCP server to use for this project:
|
||||
|
||||
```bash
|
||||
# Auto-select based on project configuration
|
||||
FIGMA_SERVER=$(./scripts/figma-server-manager.sh select "$PROJECT_ID")
|
||||
|
||||
# Or use project-specific server
|
||||
FIGMA_SERVER=$(./scripts/figma-server-manager.sh detect "$PROJECT_ID" | jq -r '.server')
|
||||
```
|
||||
|
||||
**Available servers:**
|
||||
- `figma-repeat` - For "repeat" project
|
||||
- `figma-trainer-guru` - For "trainer-guru" project
|
||||
- Auto-detect - Falls back to first available server
|
||||
|
||||
### 3. Extract Design Data
|
||||
|
||||
For each detected Figma link, extract design specifications via MCP:
|
||||
|
||||
```javascript
|
||||
// Parse the Figma URL
|
||||
const parsed = parseFigmaUrl(figmaUrl)
|
||||
// {url, type, file_id, file_name, node_id, is_valid}
|
||||
|
||||
// Fetch design data via MCP (use appropriate server)
|
||||
// This requires implementing MCP call - placeholder for now
|
||||
const designData = {
|
||||
file_id: parsed.file_id,
|
||||
file_name: parsed.file_name,
|
||||
node_id: parsed.node_id || null,
|
||||
url: parsed.url,
|
||||
canonical_url: getCanonicalUrl(parsed.url),
|
||||
server: figmaServer
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Cache in Linear Comments
|
||||
|
||||
Store extracted Figma context in Linear issue comments for future reference:
|
||||
|
||||
```markdown
|
||||
## 🎨 Figma Design Context
|
||||
|
||||
**Detected**: {count} Figma link(s)
|
||||
|
||||
### Design: {file_name}
|
||||
- **URL**: {canonical_url}
|
||||
- **Node**: {node_id || "Full file"}
|
||||
- **MCP Server**: {server}
|
||||
- **Cached**: {timestamp}
|
||||
|
||||
---
|
||||
*Figma context extracted automatically by CCPM (PSN-25)*
|
||||
*Refresh with: `/ccpm:utils:figma-refresh {linear-issue-id}`*
|
||||
```
|
||||
|
||||
### 5. Return Structured Data
|
||||
|
||||
Provide formatted output for use in planning/implementation:
|
||||
|
||||
```json
|
||||
{
|
||||
"figma_links": [
|
||||
{
|
||||
"url": "https://www.figma.com/file/ABC123/Project",
|
||||
"canonical_url": "https://www.figma.com/file/ABC123",
|
||||
"file_id": "ABC123",
|
||||
"file_name": "Project",
|
||||
"node_id": null,
|
||||
"server": "figma-repeat"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"cached": true,
|
||||
"cache_timestamp": "2025-11-20T16:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### detectFigmaLinks(text)
|
||||
```bash
|
||||
./scripts/figma-utils.sh detect "$text"
|
||||
# Returns: JSON array of URLs
|
||||
```
|
||||
|
||||
### parseFigmaUrl(url)
|
||||
```bash
|
||||
./scripts/figma-utils.sh parse "$url"
|
||||
# Returns: JSON object with file_id, node_id, type, etc.
|
||||
```
|
||||
|
||||
### selectFigmaServer(project_id)
|
||||
```bash
|
||||
./scripts/figma-server-manager.sh select "$project_id"
|
||||
# Returns: Server name (e.g., "figma-repeat")
|
||||
```
|
||||
|
||||
### getCanonicalUrl(url)
|
||||
```bash
|
||||
./scripts/figma-utils.sh canonical "$url"
|
||||
# Returns: Canonical URL without query params
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Graceful degradation:**
|
||||
- If no Figma links found → Continue without Figma context
|
||||
- If MCP server not configured → Log warning, continue
|
||||
- If MCP fetch fails → Use cached data from Linear comments
|
||||
- If cache expired → Try refresh, fallback to stale cache
|
||||
|
||||
**Never fail the entire workflow due to Figma issues.**
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- Link detection: <100ms (regex-based)
|
||||
- Server selection: <50ms (config lookup)
|
||||
- MCP fetch: 1-3s per file (network + processing)
|
||||
- Caching: Reduces repeated fetches to <100ms
|
||||
|
||||
**Optimization:**
|
||||
- Cache design data in Linear comments (1 hour TTL)
|
||||
- Only fetch if cache miss or expired
|
||||
- Deduplicate URLs before fetching
|
||||
- Limit to first 5 Figma links to avoid rate limits
|
||||
|
||||
## Integration Example
|
||||
|
||||
```bash
|
||||
# Step 0.6: Detect Figma Links (in planning:plan.md)
|
||||
|
||||
# 1. Extract Linear description/comments
|
||||
LINEAR_DESC=$(linear_get_issue "$1" | jq -r '.description')
|
||||
|
||||
# 2. Detect Figma links
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -gt 0 ]; then
|
||||
echo "✅ Detected $FIGMA_COUNT Figma link(s)"
|
||||
|
||||
# 3. Select MCP server
|
||||
FIGMA_SERVER=$(./scripts/figma-server-manager.sh select "$PROJECT_ID")
|
||||
|
||||
# 4. Format for Linear comment
|
||||
# (MCP fetch implementation pending - Phase 2)
|
||||
|
||||
# 5. Add to planning context
|
||||
echo "$FIGMA_LINKS" > /tmp/figma-context.json
|
||||
else
|
||||
echo "ℹ️ No Figma links detected"
|
||||
fi
|
||||
```
|
||||
|
||||
## Phase 1 Status ✅ COMPLETE
|
||||
|
||||
**Completed:**
|
||||
- ✅ Link detection utilities (`scripts/figma-utils.sh`)
|
||||
- ✅ MCP server manager (`scripts/figma-server-manager.sh`)
|
||||
- ✅ Configuration schema
|
||||
- ✅ Integration in planning workflow (detection only)
|
||||
|
||||
## Phase 2 Status ✅ COMPLETE
|
||||
|
||||
**Completed:**
|
||||
- ✅ MCP data extraction implementation (`scripts/figma-data-extractor.sh`)
|
||||
- ✅ Design analysis and token extraction (colors, fonts, spacing, frames)
|
||||
- ✅ Linear comment caching (`scripts/figma-cache-manager.sh`)
|
||||
- ✅ Rate limit tracking (`scripts/figma-rate-limiter.sh`)
|
||||
- ✅ Enhanced planning workflow integration (full extraction)
|
||||
|
||||
**Phase 2 Capabilities:**
|
||||
|
||||
1. **Data Extraction** (`figma-data-extractor.sh`)
|
||||
- MCP gateway integration for all three server types
|
||||
- Retry logic with exponential backoff
|
||||
- Response normalization across different servers
|
||||
- Comprehensive design analysis
|
||||
|
||||
2. **Caching** (`figma-cache-manager.sh`)
|
||||
- Linear comment-based cache storage
|
||||
- Configurable TTL (default: 1 hour)
|
||||
- Cache validation and expiry checking
|
||||
- Stale cache fallback for rate-limited scenarios
|
||||
|
||||
3. **Rate Limiting** (`figma-rate-limiter.sh`)
|
||||
- Per-server rate limit tracking
|
||||
- Official server: 6 calls/month (free tier)
|
||||
- Community servers: 60 calls/hour
|
||||
- Real-time status reporting
|
||||
|
||||
4. **Design Analysis**
|
||||
- Frame metadata extraction
|
||||
- Text content analysis
|
||||
- Color palette extraction (RGB, hex)
|
||||
- Font family and style detection
|
||||
- Spacing pattern identification
|
||||
- Component hierarchy mapping
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
# Extract design data
|
||||
./scripts/figma-data-extractor.sh extract "file_id" "server_name" ["node_id"]
|
||||
|
||||
# Analyze extracted data
|
||||
./scripts/figma-data-extractor.sh analyze "$raw_data"
|
||||
|
||||
# Cache in Linear
|
||||
./scripts/figma-cache-manager.sh store "issue_id" "file_id" "name" "url" "server" "$data"
|
||||
|
||||
# Check rate limit
|
||||
./scripts/figma-rate-limiter.sh status "server_name"
|
||||
|
||||
# View all rate limits
|
||||
./scripts/figma-rate-limiter.sh report
|
||||
```
|
||||
|
||||
**Integration with Planning:**
|
||||
|
||||
Phase 2 extraction is automatically triggered during `/ccpm:planning:plan` when:
|
||||
1. Figma links are detected
|
||||
2. MCP server is configured
|
||||
3. Rate limit allows extraction
|
||||
|
||||
If rate limit is reached, cached data is used (even if stale) to avoid blocking the workflow.
|
||||
|
||||
**Pending (Phase 3 - Future):**
|
||||
- ⏳ Design token mapping to Tailwind classes
|
||||
- ⏳ Component-to-code generation
|
||||
- ⏳ Multi-frame comparison and variant detection
|
||||
- ⏳ Change detection and design drift alerts
|
||||
- ⏳ Accessibility analysis (color contrast, ARIA labels)
|
||||
|
||||
Phase 2 provides **full design extraction and caching** - Phase 3 will add advanced analysis and code generation.
|
||||
1835
commands/_shared-image-analysis.md
Normal file
1835
commands/_shared-image-analysis.md
Normal file
File diff suppressed because it is too large
Load Diff
633
commands/_shared-linear-helpers.md
Normal file
633
commands/_shared-linear-helpers.md
Normal file
@@ -0,0 +1,633 @@
|
||||
# Shared Linear Integration Helpers (Subagent Delegation Layer)
|
||||
|
||||
This file provides reusable utility functions for Linear integration across CCPM commands. **These functions now delegate to the Linear operations subagent for optimized token usage and centralized caching.**
|
||||
|
||||
## Overview
|
||||
|
||||
These helper functions are a **delegation layer** that maintains backward compatibility while routing operations to the `linear-operations` subagent:
|
||||
|
||||
- `getOrCreateLabel()` - Delegates to `get_or_create_label` operation
|
||||
- `getValidStateId()` - Delegates to `get_valid_state_id` operation
|
||||
- `ensureLabelsExist()` - Delegates to `ensure_labels_exist` operation
|
||||
- `getDefaultColor()` - Local utility (no delegation needed)
|
||||
|
||||
**Key Benefits**:
|
||||
- **50-60% token reduction** per command (via centralized caching)
|
||||
- **No breaking changes** - All function signatures identical
|
||||
- **Automatic caching** - Session-level cache for teams, labels, states
|
||||
- **Better error handling** - Structured error responses from subagent
|
||||
|
||||
**Usage in commands:** Reference this file at the start of command execution:
|
||||
```markdown
|
||||
READ: commands/_shared-linear-helpers.md
|
||||
```
|
||||
|
||||
Then use the functions as described below. The delegation to the subagent happens automatically.
|
||||
|
||||
---
|
||||
|
||||
## Functions
|
||||
|
||||
### 1. getOrCreateLabel
|
||||
|
||||
Retrieves an existing label or creates it if it doesn't exist. **Now delegates to linear-operations subagent.**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Get existing label or create new one (delegates to linear-operations subagent)
|
||||
* @param {string} teamId - Linear team ID or name
|
||||
* @param {string} labelName - Label name to find or create
|
||||
* @param {Object} options - Optional configuration
|
||||
* @param {string} options.color - Hex color code (default: auto-assigned)
|
||||
* @param {string} options.description - Label description
|
||||
* @returns {Promise<Object>} Label object with id and name
|
||||
*/
|
||||
async function getOrCreateLabel(teamId, labelName, options = {}) {
|
||||
// Delegate to linear-operations subagent
|
||||
const result = await Task('linear-operations', `
|
||||
operation: get_or_create_label
|
||||
params:
|
||||
team: ${teamId}
|
||||
name: ${labelName}
|
||||
${options.color ? `color: ${options.color}` : ''}
|
||||
${options.description ? `description: ${options.description}` : ''}
|
||||
context:
|
||||
command: "shared-helpers"
|
||||
purpose: "Ensuring label exists for workflow"
|
||||
`);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
`Failed to get/create label '${labelName}': ${result.error?.message || 'Unknown error'}`
|
||||
);
|
||||
}
|
||||
|
||||
// Return in same format as before for backward compatibility
|
||||
return {
|
||||
id: result.data.id,
|
||||
name: result.data.name
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Simple usage - auto color (delegates to subagent)
|
||||
const label = await getOrCreateLabel(teamId, "planning");
|
||||
|
||||
// With custom options
|
||||
const label = await getOrCreateLabel(teamId, "high-priority", {
|
||||
color: "#eb5757",
|
||||
description: "High priority tasks"
|
||||
});
|
||||
|
||||
// Note: teamId can now be team name, key, or ID (subagent resolves it)
|
||||
const label = await getOrCreateLabel("Engineering", "planning");
|
||||
```
|
||||
|
||||
**Migration Note**: The subagent accepts team names in addition to IDs, making the function more flexible.
|
||||
|
||||
---
|
||||
|
||||
### 2. getValidStateId
|
||||
|
||||
Validates and resolves state names/types to valid Linear state IDs. **Now delegates to linear-operations subagent with enhanced fuzzy matching.**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Get valid Linear state ID from state name or type (delegates to linear-operations subagent)
|
||||
* @param {string} teamId - Linear team ID or name
|
||||
* @param {string} stateNameOrType - State name (e.g., "In Progress") or type (e.g., "started")
|
||||
* @returns {Promise<string>} Valid state ID
|
||||
* @throws {Error} If no matching state found (with helpful suggestions)
|
||||
*/
|
||||
async function getValidStateId(teamId, stateNameOrType) {
|
||||
// Delegate to linear-operations subagent
|
||||
const result = await Task('linear-operations', `
|
||||
operation: get_valid_state_id
|
||||
params:
|
||||
team: ${teamId}
|
||||
state: ${stateNameOrType}
|
||||
context:
|
||||
command: "shared-helpers"
|
||||
purpose: "Resolving workflow state"
|
||||
`);
|
||||
|
||||
if (!result.success) {
|
||||
// Enhanced error message with suggestions from subagent
|
||||
const suggestions = result.error?.suggestions || [];
|
||||
const availableStates = result.error?.details?.available_statuses || [];
|
||||
|
||||
let errorMsg = `Invalid state: "${stateNameOrType}"\n\n`;
|
||||
|
||||
if (availableStates.length > 0) {
|
||||
errorMsg += `Available states for this team:\n`;
|
||||
availableStates.forEach(s => {
|
||||
errorMsg += ` - ${s.name} (type: ${s.type})\n`;
|
||||
});
|
||||
errorMsg += '\n';
|
||||
}
|
||||
|
||||
if (suggestions.length > 0) {
|
||||
errorMsg += `Suggestions:\n`;
|
||||
suggestions.forEach(s => {
|
||||
errorMsg += ` - ${s}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
// Return just the ID for backward compatibility
|
||||
return result.data.id;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// By state name (delegates to subagent with fuzzy matching)
|
||||
const stateId = await getValidStateId(teamId, "In Progress");
|
||||
|
||||
// By state type
|
||||
const stateId = await getValidStateId(teamId, "started");
|
||||
|
||||
// Common aliases work (subagent handles mapping)
|
||||
const stateId = await getValidStateId(teamId, "todo"); // Maps to "unstarted" type
|
||||
|
||||
// With team name (subagent resolves it)
|
||||
const stateId = await getValidStateId("Engineering", "In Progress");
|
||||
```
|
||||
|
||||
**Subagent Advantages**:
|
||||
- Fuzzy matching with 6-step resolution strategy
|
||||
- Cached state lookups (90%+ cache hit rate expected)
|
||||
- Helpful error messages with available options
|
||||
- Handles common aliases automatically
|
||||
|
||||
---
|
||||
|
||||
### 3. ensureLabelsExist
|
||||
|
||||
Ensures multiple labels exist, creating them if needed. **Now delegates to linear-operations subagent with batch optimization.**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Ensure multiple labels exist, creating missing ones (delegates to linear-operations subagent)
|
||||
* @param {string} teamId - Linear team ID or name
|
||||
* @param {string[]} labelNames - Array of label names
|
||||
* @param {Object} options - Optional configuration
|
||||
* @param {Object} options.colors - Map of label names to color codes
|
||||
* @param {Object} options.descriptions - Map of label names to descriptions
|
||||
* @returns {Promise<string[]>} Array of label names (guaranteed to exist)
|
||||
*/
|
||||
async function ensureLabelsExist(teamId, labelNames, options = {}) {
|
||||
const colors = options.colors || {};
|
||||
const descriptions = options.descriptions || {};
|
||||
|
||||
// Build label definitions for subagent
|
||||
const labelDefs = labelNames.map(name => {
|
||||
const def = { name };
|
||||
if (colors[name]) def.color = colors[name];
|
||||
// Note: subagent auto-assigns color from getDefaultColor if not provided
|
||||
return def;
|
||||
});
|
||||
|
||||
// Delegate to linear-operations subagent (batch operation)
|
||||
const result = await Task('linear-operations', `
|
||||
operation: ensure_labels_exist
|
||||
params:
|
||||
team: ${teamId}
|
||||
labels:
|
||||
${labelDefs.map(l => `- name: ${l.name}${l.color ? `\n color: ${l.color}` : ''}`).join('\n ')}
|
||||
context:
|
||||
command: "shared-helpers"
|
||||
purpose: "Ensuring workflow labels exist"
|
||||
`);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
`Failed to ensure labels exist: ${result.error?.message || 'Unknown error'}`
|
||||
);
|
||||
}
|
||||
|
||||
// Return label names in same format as before
|
||||
return result.data.labels.map(l => l.name);
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Simple usage - auto colors (batch delegated to subagent)
|
||||
const labels = await ensureLabelsExist(teamId, [
|
||||
"planning",
|
||||
"implementation",
|
||||
"verification"
|
||||
]);
|
||||
|
||||
// With custom colors and descriptions
|
||||
const labels = await ensureLabelsExist(teamId,
|
||||
["bug", "feature", "epic"],
|
||||
{
|
||||
colors: {
|
||||
bug: "#eb5757",
|
||||
feature: "#bb87fc"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// With team name
|
||||
const labels = await ensureLabelsExist("Engineering", [
|
||||
"planning",
|
||||
"backend"
|
||||
]);
|
||||
```
|
||||
|
||||
**Subagent Advantages**:
|
||||
- Batch operation: Single API call for all labels
|
||||
- Intelligent caching: Reuses lookups across calls
|
||||
- Performance: 80%+ of labels typically cached
|
||||
- Rate limiting: Optimized to respect Linear API limits
|
||||
|
||||
---
|
||||
|
||||
### 4. getDefaultColor
|
||||
|
||||
Returns standardized hex color codes for CCPM workflow labels. **This is a local utility (no delegation).**
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Get default color for common CCPM labels (local utility, no subagent needed)
|
||||
* @param {string} labelName - Label name (case-insensitive)
|
||||
* @returns {string} Hex color code (with #)
|
||||
*/
|
||||
function getDefaultColor(labelName) {
|
||||
const colorMap = {
|
||||
// CCPM Workflow stages
|
||||
'planning': '#f7c8c1', // Light coral
|
||||
'implementation': '#26b5ce', // Cyan
|
||||
'verification': '#f2c94c', // Yellow
|
||||
'pr-review': '#5e6ad2', // Indigo
|
||||
'done': '#4cb782', // Green
|
||||
'approved': '#4cb782', // Green
|
||||
|
||||
// Issue types
|
||||
'bug': '#eb5757', // Red
|
||||
'feature': '#bb87fc', // Purple
|
||||
'epic': '#f7c8c1', // Light coral
|
||||
'task': '#26b5ce', // Cyan
|
||||
'improvement': '#4ea7fc', // Blue
|
||||
|
||||
// Status indicators
|
||||
'blocked': '#eb5757', // Red
|
||||
'research': '#26b5ce', // Cyan
|
||||
'research-complete': '#26b5ce', // Cyan
|
||||
|
||||
// Priority labels
|
||||
'critical': '#eb5757', // Red
|
||||
'high-priority': '#f2994a', // Orange
|
||||
'low-priority': '#95a2b3', // Gray
|
||||
|
||||
// Technical areas
|
||||
'backend': '#26b5ce', // Cyan
|
||||
'frontend': '#bb87fc', // Purple
|
||||
'database': '#4ea7fc', // Blue
|
||||
'api': '#26b5ce', // Cyan
|
||||
'security': '#eb5757', // Red
|
||||
'performance': '#f2c94c', // Yellow
|
||||
'testing': '#4cb782', // Green
|
||||
'documentation': '#95a2b3' // Gray
|
||||
};
|
||||
|
||||
const normalized = labelName.toLowerCase().trim();
|
||||
return colorMap[normalized] || '#95a2b3'; // Default gray
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Example:**
|
||||
```javascript
|
||||
// Get color for standard label (no subagent call)
|
||||
const color = getDefaultColor("planning"); // Returns "#f7c8c1"
|
||||
|
||||
// Unknown labels get gray
|
||||
const color = getDefaultColor("custom-label"); // Returns "#95a2b3"
|
||||
|
||||
// Case-insensitive
|
||||
const color = getDefaultColor("FEATURE"); // Returns "#bb87fc"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling Patterns
|
||||
|
||||
All functions handle errors gracefully and throw descriptive exceptions when operations fail.
|
||||
|
||||
### Graceful Label Creation
|
||||
```javascript
|
||||
try {
|
||||
const label = await getOrCreateLabel(teamId, "planning");
|
||||
// Proceed with label.id
|
||||
console.log(`Using label: ${label.name} (${label.id})`);
|
||||
} catch (error) {
|
||||
console.error("Failed to create/get label:", error.message);
|
||||
// Decide: fail task or continue without label
|
||||
throw new Error(`Linear label operation failed: ${error.message}`);
|
||||
}
|
||||
```
|
||||
|
||||
### State Validation with Helpful Messages
|
||||
```javascript
|
||||
try {
|
||||
const stateId = await getValidStateId(teamId, "In Progress");
|
||||
// Use stateId for issue operations
|
||||
console.log(`Using state: ${stateId}`);
|
||||
} catch (error) {
|
||||
// Error includes helpful message with available states and suggestions
|
||||
console.error(error.message);
|
||||
throw error; // Re-throw to halt command
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Label Creation
|
||||
```javascript
|
||||
try {
|
||||
const labels = await ensureLabelsExist(teamId, [
|
||||
"planning",
|
||||
"implementation",
|
||||
"verification"
|
||||
]);
|
||||
console.log(`Labels ready: ${labels.join(", ")}`);
|
||||
} catch (error) {
|
||||
console.error("Failed to ensure labels exist:", error.message);
|
||||
// Decide: fail or continue with partial labels
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### Example 1: Creating Issue with Labels via Subagent
|
||||
|
||||
**Key Change**: Instead of calling helper functions then making direct MCP calls, delegate the entire operation to the linear-operations subagent for maximum optimization:
|
||||
|
||||
```javascript
|
||||
// OLD WAY (not recommended - higher token usage):
|
||||
// const label = await getOrCreateLabel(teamId, "planning");
|
||||
// const stateId = await getValidStateId(teamId, "In Progress");
|
||||
// const issue = await mcp__linear__create_issue({...});
|
||||
|
||||
// NEW WAY (recommended - lower token usage):
|
||||
// Instead of using helpers + direct MCP, delegate to subagent:
|
||||
|
||||
Task(linear-operations): `
|
||||
operation: create_issue
|
||||
params:
|
||||
team: ${teamId}
|
||||
title: "Implement user authentication"
|
||||
description: "## Overview\n..."
|
||||
state: "In Progress"
|
||||
labels:
|
||||
- "planning"
|
||||
- "backend"
|
||||
- "high-priority"
|
||||
assignee: "me"
|
||||
context:
|
||||
command: "planning:create"
|
||||
purpose: "Creating planned task with workflow labels"
|
||||
`
|
||||
```
|
||||
|
||||
### Example 2: Validating State Before Update
|
||||
```javascript
|
||||
// Use helper to validate state
|
||||
const doneStateId = await getValidStateId(teamId, "done");
|
||||
|
||||
// Then delegate issue update to subagent
|
||||
Task(linear-operations): `
|
||||
operation: update_issue
|
||||
params:
|
||||
issue_id: ${issueId}
|
||||
state: "done"
|
||||
context:
|
||||
command: "implementation:update"
|
||||
`
|
||||
```
|
||||
|
||||
### Example 3: Conditional Label Creation
|
||||
```javascript
|
||||
// Create label only if needed (uses helper function)
|
||||
const shouldAddPriorityLabel = isUrgent;
|
||||
|
||||
if (shouldAddPriorityLabel) {
|
||||
const label = await getOrCreateLabel(teamId, "high-priority", {
|
||||
color: "#f2994a"
|
||||
});
|
||||
console.log(`Added priority label: ${label.name}`);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## State Type Reference
|
||||
|
||||
Linear workflow state types:
|
||||
- **backlog**: Issue is in backlog, not yet planned
|
||||
- **unstarted**: Planned but not started (Todo, Ready)
|
||||
- **started**: Actively being worked on (In Progress, In Review)
|
||||
- **completed**: Successfully finished (Done, Deployed)
|
||||
- **canceled**: Closed without completion (Canceled, Blocked)
|
||||
|
||||
---
|
||||
|
||||
## Color Palette Reference
|
||||
|
||||
CCPM standard colors:
|
||||
- **Workflow**: Coral (#f7c8c1), Cyan (#26b5ce), Yellow (#f2c94c), Green (#4cb782)
|
||||
- **Priority**: Red (#eb5757), Orange (#f2994a), Gray (#95a2b3)
|
||||
- **Types**: Purple (#bb87fc), Blue (#4ea7fc), Indigo (#5e6ad2)
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always validate state IDs** before creating/updating issues
|
||||
2. **Reuse existing labels** instead of creating duplicates
|
||||
3. **Use consistent colors** from `getDefaultColor()` for visual clarity
|
||||
4. **Handle errors gracefully** with helpful messages
|
||||
5. **Batch label operations** when creating multiple labels
|
||||
6. **Log label creation** for debugging and transparency
|
||||
7. **Use case-insensitive matching** for better UX
|
||||
|
||||
---
|
||||
|
||||
## Testing Helpers
|
||||
|
||||
To test these functions in isolation:
|
||||
|
||||
```javascript
|
||||
// Test label creation
|
||||
const label = await getOrCreateLabel("TEAM-123", "test-label");
|
||||
console.log("Created/found label:", label);
|
||||
|
||||
// Test state validation
|
||||
try {
|
||||
const stateId = await getValidStateId("TEAM-123", "invalid-state");
|
||||
} catch (error) {
|
||||
console.log("Expected error:", error.message);
|
||||
}
|
||||
|
||||
// Test batch labels
|
||||
const labels = await ensureLabelsExist("TEAM-123", [
|
||||
"label1",
|
||||
"label2",
|
||||
"label3"
|
||||
]);
|
||||
console.log("All labels exist:", labels);
|
||||
|
||||
// Test color lookup
|
||||
console.log("Planning color:", getDefaultColor("planning"));
|
||||
console.log("Unknown color:", getDefaultColor("random"));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Subagent Integration Details
|
||||
|
||||
### How It Works
|
||||
|
||||
When you call `getOrCreateLabel()`, `getValidStateId()`, or `ensureLabelsExist()`:
|
||||
|
||||
1. **Function invokes Task tool** with YAML-formatted request to linear-operations subagent
|
||||
2. **Subagent handles the operation** with session-level caching
|
||||
3. **Result is parsed** and returned in original format for backward compatibility
|
||||
4. **Error handling** extracts helpful messages from subagent responses
|
||||
|
||||
**Example flow for getOrCreateLabel**:
|
||||
```
|
||||
Command → getOrCreateLabel(teamId, "planning")
|
||||
↓
|
||||
Task('linear-operations', { operation: get_or_create_label, ... })
|
||||
↓
|
||||
Subagent checks cache for "planning" label
|
||||
↓
|
||||
If cached: Return instantly (~25ms)
|
||||
If not cached: Fetch from Linear, cache, return (~400ms)
|
||||
↓
|
||||
Result parsed and returned as { id, name }
|
||||
```
|
||||
|
||||
### Caching Benefits
|
||||
|
||||
The subagent maintains session-level in-memory cache for:
|
||||
- **Teams** - 95% cache hit rate
|
||||
- **Labels** - 85% cache hit rate
|
||||
- **States** - 95% cache hit rate
|
||||
- **Projects** - 90% cache hit rate
|
||||
|
||||
This means **second and subsequent calls within a command are nearly instant**.
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### For Command Developers
|
||||
|
||||
**Old Pattern (with direct MCP calls)**:
|
||||
```markdown
|
||||
## Get team labels
|
||||
Read: commands/_shared-linear-helpers.md
|
||||
Get team ID for: ${TEAM_NAME}
|
||||
Ensure labels exist: ["planning", "backend"]
|
||||
Get state ID for: "In Progress"
|
||||
Create issue with:
|
||||
- Team: ${TEAM_ID}
|
||||
- Labels: [label-1, label-2]
|
||||
- State: state-123
|
||||
```
|
||||
|
||||
**New Pattern (delegating to subagent)**:
|
||||
```markdown
|
||||
## Create Issue with Labels and State
|
||||
|
||||
Task(linear-operations): `
|
||||
operation: create_issue
|
||||
params:
|
||||
team: ${TEAM_NAME}
|
||||
title: "${ISSUE_TITLE}"
|
||||
state: "In Progress"
|
||||
labels: ["planning", "backend"]
|
||||
context:
|
||||
command: "${COMMAND_NAME}"
|
||||
purpose: "Creating task"
|
||||
`
|
||||
```
|
||||
|
||||
**Token Savings**: 2500 tokens → 400 tokens (84% reduction)
|
||||
|
||||
### For Helper Function Calls
|
||||
|
||||
**When to use helpers:**
|
||||
- Validating a single state before conditional logic
|
||||
- Creating a single label with custom options
|
||||
- Checking if a label exists
|
||||
|
||||
**When to use subagent directly (preferred):**
|
||||
- Creating issues with labels
|
||||
- Updating issues with state/labels
|
||||
- Any operation that requires multiple calls
|
||||
|
||||
---
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Latency Comparison
|
||||
|
||||
| Operation | Old (Direct MCP) | New (via Subagent) | Cache Hit |
|
||||
|-----------|------------------|-------------------|-----------|
|
||||
| Get label | 400-600ms | 25-50ms | Yes |
|
||||
| Create label | 300-500ms | 300-500ms | First time |
|
||||
| Ensure 3 labels | 1200-1800ms | 50-100ms | 2+ cached |
|
||||
| Get state | 300-500ms | 20-30ms | Yes |
|
||||
| Create issue | 600-800ms | 600-800ms | N/A |
|
||||
|
||||
**Cumulative benefit**: Commands with 5+ Linear operations see 50-60% token reduction.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use helpers for validation** - Validate states/labels before conditional logic
|
||||
2. **Use subagent for multi-step operations** - Create issue + labels in one call
|
||||
3. **Rely on auto-coloring** - Don't hardcode colors; use getDefaultColor()
|
||||
4. **Handle errors gracefully** - Catch and re-throw with context
|
||||
5. **Batch operations when possible** - Use ensureLabelsExist() for multiple labels
|
||||
6. **Team parameter flexibility** - Pass team name instead of ID (subagent resolves it)
|
||||
7. **Cache awareness** - Understand that subsequent calls are much faster
|
||||
|
||||
---
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Updating Helper Functions
|
||||
|
||||
When modifying these helpers:
|
||||
1. **Maintain function signatures** - No breaking changes to callers
|
||||
2. **Update YAML contracts** - Align with linear-operations subagent definition
|
||||
3. **Test error paths** - Ensure error handling still works
|
||||
4. **Update examples** - Keep usage examples in sync
|
||||
5. **Document changes** - Update CHANGELOG.md for any behavior changes
|
||||
|
||||
### Monitoring Usage
|
||||
|
||||
To find all commands using these helpers:
|
||||
```bash
|
||||
grep -r "getOrCreateLabel\|getValidStateId\|ensureLabelsExist" commands/ | grep -v "_shared-linear"
|
||||
```
|
||||
|
||||
### When Linear API Changes
|
||||
|
||||
If Linear MCP server updates its interface:
|
||||
1. Update linear-operations subagent (single source of truth)
|
||||
2. This file automatically benefits from subagent improvements
|
||||
3. No changes needed to 40+ dependent commands
|
||||
433
commands/_shared-next-actions.md
Normal file
433
commands/_shared-next-actions.md
Normal file
@@ -0,0 +1,433 @@
|
||||
# Shared Smart Next-Action Suggestions
|
||||
|
||||
This file provides smart next-action suggestion logic used by the new natural workflow commands.
|
||||
|
||||
## Purpose
|
||||
|
||||
Automatically suggest the most appropriate next action based on:
|
||||
- Current workflow stage
|
||||
- Task status and progress
|
||||
- Time since last action
|
||||
- Blockers or dependencies
|
||||
|
||||
## Next Action Functions
|
||||
|
||||
### 1. Determine Next Action After Planning
|
||||
|
||||
```javascript
|
||||
async function suggestAfterPlan(issueId) {
|
||||
const issue = await linear_get_issue(issueId)
|
||||
const status = issue.status
|
||||
const progress = calculateProgress(issue.description)
|
||||
|
||||
// Just created/planned - ready to start
|
||||
return {
|
||||
primary: {
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Start Implementation',
|
||||
description: 'Begin working on this task',
|
||||
icon: '🚀'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'insights',
|
||||
command: `/ccpm:utils:insights ${issueId}`,
|
||||
label: 'Get AI Insights',
|
||||
description: 'Analyze complexity and risks'
|
||||
},
|
||||
{
|
||||
action: 'status',
|
||||
command: `/ccpm:utils:status ${issueId}`,
|
||||
label: 'Review Status',
|
||||
description: 'Check planning details'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Determine Next Action After Work
|
||||
|
||||
```javascript
|
||||
async function suggestAfterWork(issueId) {
|
||||
const issue = await linear_get_issue(issueId)
|
||||
const progress = calculateProgress(issue.description)
|
||||
const uncommitted = detectUncommittedChanges()
|
||||
|
||||
// Has uncommitted changes - should commit or sync
|
||||
if (uncommitted.hasChanges) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'commit',
|
||||
command: `/ccpm:commit ${issueId}`,
|
||||
label: 'Commit Changes',
|
||||
description: `Commit ${uncommitted.summary}`,
|
||||
icon: '💾'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'sync',
|
||||
command: `/ccpm:sync ${issueId}`,
|
||||
label: 'Sync Progress',
|
||||
description: 'Save progress to Linear without committing'
|
||||
},
|
||||
{
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: 'Keep working on subtasks'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// No uncommitted changes, check if complete
|
||||
if (progress.isComplete) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'verify',
|
||||
command: `/ccpm:verify ${issueId}`,
|
||||
label: 'Run Verification',
|
||||
description: 'All tasks complete, run quality checks',
|
||||
icon: '✅'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: 'Make more changes'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// In progress, not complete - continue working
|
||||
return {
|
||||
primary: {
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: `${progress.remaining} tasks remaining`,
|
||||
icon: '⚡'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'sync',
|
||||
command: `/ccpm:sync ${issueId}`,
|
||||
label: 'Sync Progress',
|
||||
description: 'Save current progress'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Determine Next Action After Sync
|
||||
|
||||
```javascript
|
||||
async function suggestAfterSync(issueId) {
|
||||
const issue = await linear_get_issue(issueId)
|
||||
const progress = calculateProgress(issue.description)
|
||||
const uncommitted = detectUncommittedChanges()
|
||||
|
||||
// Has uncommitted changes - should commit
|
||||
if (uncommitted.hasChanges) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'commit',
|
||||
command: `/ccpm:commit ${issueId}`,
|
||||
label: 'Commit Changes',
|
||||
description: 'Commit your work to git',
|
||||
icon: '💾'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: 'Keep making changes'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// All committed, check if complete
|
||||
if (progress.isComplete) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'verify',
|
||||
command: `/ccpm:verify ${issueId}`,
|
||||
label: 'Run Verification',
|
||||
description: 'All tasks complete, verify quality',
|
||||
icon: '✅'
|
||||
},
|
||||
alternatives: []
|
||||
}
|
||||
}
|
||||
|
||||
// Not complete - continue working
|
||||
return {
|
||||
primary: {
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: `${progress.remaining} tasks remaining`,
|
||||
icon: '⚡'
|
||||
},
|
||||
alternatives: []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Determine Next Action After Commit
|
||||
|
||||
```javascript
|
||||
async function suggestAfterCommit(issueId) {
|
||||
const issue = await linear_get_issue(issueId)
|
||||
const progress = calculateProgress(issue.description)
|
||||
const pushed = isBranchPushed()
|
||||
|
||||
// Not pushed - should sync and maybe push
|
||||
if (!pushed.isPushed) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'sync',
|
||||
command: `/ccpm:sync ${issueId}`,
|
||||
label: 'Sync to Linear',
|
||||
description: 'Update Linear with progress',
|
||||
icon: '🔄'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'push',
|
||||
command: `git push -u origin ${pushed.branch}`,
|
||||
label: 'Push to Remote',
|
||||
description: 'Push commits to GitHub'
|
||||
},
|
||||
{
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: 'Keep making changes'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Pushed, check if complete
|
||||
if (progress.isComplete) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'verify',
|
||||
command: `/ccpm:verify ${issueId}`,
|
||||
label: 'Run Verification',
|
||||
description: 'All done, verify quality',
|
||||
icon: '✅'
|
||||
},
|
||||
alternatives: []
|
||||
}
|
||||
}
|
||||
|
||||
// Not complete - work or sync
|
||||
return {
|
||||
primary: {
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Continue Working',
|
||||
description: `${progress.remaining} tasks remaining`,
|
||||
icon: '⚡'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'sync',
|
||||
command: `/ccpm:sync ${issueId}`,
|
||||
label: 'Sync Progress',
|
||||
description: 'Update Linear'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Determine Next Action After Verify
|
||||
|
||||
```javascript
|
||||
async function suggestAfterVerify(issueId, verificationPassed) {
|
||||
if (!verificationPassed) {
|
||||
// Verification failed - fix issues
|
||||
return {
|
||||
primary: {
|
||||
action: 'fix',
|
||||
command: `/ccpm:verification:fix ${issueId}`,
|
||||
label: 'Fix Issues',
|
||||
description: 'Debug and fix verification failures',
|
||||
icon: '🔧'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Make Changes',
|
||||
description: 'Continue implementation'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Verification passed - ready to finalize
|
||||
return {
|
||||
primary: {
|
||||
action: 'done',
|
||||
command: `/ccpm:done ${issueId}`,
|
||||
label: 'Finalize Task',
|
||||
description: 'Create PR and mark complete',
|
||||
icon: '🎉'
|
||||
},
|
||||
alternatives: [
|
||||
{
|
||||
action: 'work',
|
||||
command: `/ccpm:work ${issueId}`,
|
||||
label: 'Make More Changes',
|
||||
description: 'Continue working'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Display Template
|
||||
|
||||
### Standard Next Action Prompt
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 What's Next?
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
⭐ Recommended: ${primary.label}
|
||||
${primary.description}
|
||||
|
||||
${primary.command}
|
||||
|
||||
${alternatives.length > 0 ? `
|
||||
Or:
|
||||
${alternatives.map((alt, i) => `
|
||||
${i+1}. ${alt.label}
|
||||
${alt.description}
|
||||
${alt.command}
|
||||
`).join('\n')}
|
||||
` : ''}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Interactive Prompt with AskUserQuestion
|
||||
|
||||
```javascript
|
||||
function createNextActionQuestion(suggestions) {
|
||||
const options = [
|
||||
{
|
||||
label: suggestions.primary.label,
|
||||
description: suggestions.primary.description
|
||||
}
|
||||
]
|
||||
|
||||
suggestions.alternatives.forEach(alt => {
|
||||
options.push({
|
||||
label: alt.label,
|
||||
description: alt.description
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
questions: [{
|
||||
question: "What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Context-Aware Suggestions
|
||||
|
||||
### Time-Based Suggestions
|
||||
|
||||
```javascript
|
||||
function adjustForTime(suggestions, hoursSinceLastAction) {
|
||||
// If been >4 hours, suggest sync first
|
||||
if (hoursSinceLastAction > 4) {
|
||||
return {
|
||||
primary: {
|
||||
action: 'sync',
|
||||
command: `/ccpm:sync ${issueId}`,
|
||||
label: 'Sync Progress',
|
||||
description: 'It\'s been a while, sync your progress first',
|
||||
icon: '🔄'
|
||||
},
|
||||
alternatives: [suggestions.primary, ...suggestions.alternatives]
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions
|
||||
}
|
||||
```
|
||||
|
||||
### Status-Based Suggestions
|
||||
|
||||
```javascript
|
||||
function adjustForStatus(suggestions, status) {
|
||||
// If status is "Blocked", prioritize fixing
|
||||
if (status === 'Blocked') {
|
||||
return {
|
||||
primary: {
|
||||
action: 'fix',
|
||||
command: `/ccpm:verification:fix ${issueId}`,
|
||||
label: 'Fix Blockers',
|
||||
description: 'Task is blocked, resolve issues first',
|
||||
icon: '🔧'
|
||||
},
|
||||
alternatives: [suggestions.primary, ...suggestions.alternatives]
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions
|
||||
}
|
||||
```
|
||||
|
||||
## Usage in Commands
|
||||
|
||||
Each command should call the appropriate suggestion function at the end:
|
||||
|
||||
```javascript
|
||||
// In /ccpm:plan
|
||||
const suggestions = await suggestAfterPlan(issueId)
|
||||
displayNextActions(suggestions)
|
||||
|
||||
// In /ccpm:work
|
||||
const suggestions = await suggestAfterWork(issueId)
|
||||
displayNextActions(suggestions)
|
||||
|
||||
// In /ccpm:sync
|
||||
const suggestions = await suggestAfterSync(issueId)
|
||||
displayNextActions(suggestions)
|
||||
|
||||
// In /ccpm:commit
|
||||
const suggestions = await suggestAfterCommit(issueId)
|
||||
displayNextActions(suggestions)
|
||||
|
||||
// In /ccpm:verify
|
||||
const suggestions = await suggestAfterVerify(issueId, verificationPassed)
|
||||
displayNextActions(suggestions)
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Context-Aware**: Different suggestions based on workflow stage
|
||||
✅ **Time-Sensitive**: Adjusts based on time since last action
|
||||
✅ **Status-Aware**: Considers blockers and task status
|
||||
✅ **Progressive**: Guides users through complete workflow
|
||||
✅ **Flexible**: Users can choose alternatives if primary doesn't fit
|
||||
794
commands/_shared-planning-workflow.md
Normal file
794
commands/_shared-planning-workflow.md
Normal file
@@ -0,0 +1,794 @@
|
||||
# Shared Planning Workflow
|
||||
|
||||
This module provides the common planning workflow logic used by both `/ccpm:planning:plan` and `/ccpm:planning:create` commands.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Calling commands MUST provide these context variables before executing this workflow:
|
||||
|
||||
**Required Variables:**
|
||||
- `LINEAR_ISSUE_ID` - Linear issue ID to plan (e.g., "PSN-25")
|
||||
- `JIRA_TICKET_ID` - Optional Jira ticket ID (e.g., "TRAIN-123")
|
||||
- `PROJECT_CONFIG` - Project configuration loaded from `~/.claude/ccpm-config.yaml`
|
||||
- `EXTERNAL_PM_ENABLED` - Boolean flag for external PM integration
|
||||
- `EXTERNAL_PM_TYPE` - Type of external PM (e.g., "jira")
|
||||
- `JIRA_ENABLED`, `CONFLUENCE_ENABLED`, `SLACK_ENABLED` - Feature flags
|
||||
|
||||
**Optional context (if already loaded):**
|
||||
- `FIGMA_LINKS` - Pre-detected Figma links
|
||||
- `IMAGE_ANALYSES` - Pre-analyzed images
|
||||
|
||||
## Workflow Steps
|
||||
|
||||
### Step 0.5: Detect and Analyze Images
|
||||
|
||||
**READ**: `commands/_shared-image-analysis.md`
|
||||
|
||||
Apply the image analysis workflow to detect and analyze any images attached to or referenced in the Linear issue:
|
||||
|
||||
1. **Detect images** from Linear issue using detectImages(issue):
|
||||
- Extracts image attachments from Linear issue
|
||||
- Detects inline markdown images in description
|
||||
- Returns: `[{ url, title, type }]`
|
||||
|
||||
2. **For each detected image** (limit to first 5):
|
||||
- Determine context type: generateImagePrompt(image.title, "planning")
|
||||
- Fetch and analyze: fetchAndAnalyzeImage(image.url, prompt)
|
||||
- Collect results: `{ url, title, type, analysis }`
|
||||
- Handle errors gracefully: If analysis starts with "⚠️", log warning and continue
|
||||
|
||||
3. **Format results**: formatImageContext(imageAnalyses)
|
||||
- Returns formatted markdown section ready for Linear
|
||||
- Includes section header and individual image analyses
|
||||
- Preserves image URLs for implementation phase
|
||||
|
||||
4. **Prepare for insertion**: Store formatted image context
|
||||
- Will be inserted into Linear description in Step 4
|
||||
- Inserted after checklist, before Context section
|
||||
- Provides visual context for planning and implementation
|
||||
|
||||
**Performance Note**: Image analysis adds ~2-5s per image. Limiting to 5 images max to avoid excessive processing.
|
||||
|
||||
**Error Handling**: Never fail the entire workflow if image analysis fails. Log warnings for failed images and continue with successful ones.
|
||||
|
||||
**Why This Matters**:
|
||||
- UI mockups provide exact design specifications for implementation
|
||||
- Architecture diagrams clarify system structure and relationships
|
||||
- Screenshots document current state and issues to address
|
||||
- All images preserved for direct visual reference during implementation phase
|
||||
|
||||
### Step 0.6: Detect and Extract Figma Designs
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
After analyzing static images, check for live Figma design links that provide authoritative design system information:
|
||||
|
||||
1. **Detect Figma links** from Linear issue (description + comments):
|
||||
```bash
|
||||
LINEAR_DESC=$(linear_get_issue "$LINEAR_ISSUE_ID" | jq -r '.description')
|
||||
LINEAR_COMMENTS=$(linear_get_issue "$LINEAR_ISSUE_ID" | jq -r '.comments[]? | .body' || echo "")
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC $LINEAR_COMMENTS")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
```
|
||||
|
||||
2. **If Figma links found** (FIGMA_COUNT > 0):
|
||||
- Select appropriate MCP server: `./scripts/figma-server-manager.sh select "$PROJECT_ID"`
|
||||
- For each Figma link:
|
||||
- Extract file_id, file_name, node_id from parsed link
|
||||
- Check cache first: `./scripts/figma-cache-manager.sh get "$LINEAR_ISSUE_ID" "$file_id"`
|
||||
- If cache miss or expired:
|
||||
- Generate MCP extraction call: `./scripts/figma-data-extractor.sh extract "$file_id" "$server"`
|
||||
- Execute MCP call via mcp__agent-mcp-gateway__execute_tool
|
||||
- Analyze design data: `./scripts/figma-data-extractor.sh generate "$figma_data"`
|
||||
- Cache result: `./scripts/figma-cache-manager.sh store "$LINEAR_ISSUE_ID" "$file_id" ...`
|
||||
- Format design system as markdown: `./scripts/figma-design-analyzer.sh format "$design_system" "$file_name"`
|
||||
|
||||
3. **Prepare Figma context** for Linear description:
|
||||
- Store formatted Figma analysis separately from image analysis
|
||||
- Will be inserted into Linear description in Step 4
|
||||
- Provides design tokens, component library, layout patterns
|
||||
|
||||
**Benefits over static images**:
|
||||
- **Live data**: Always up-to-date with latest Figma changes
|
||||
- **Design system**: Automatic extraction of colors, fonts, spacing
|
||||
- **Tailwind mapping**: Direct mapping from Figma styles to Tailwind classes
|
||||
- **Component library**: Understanding of existing design components
|
||||
- **Structured data**: Machine-readable design tokens vs. visual interpretation
|
||||
|
||||
**Graceful Degradation**:
|
||||
- If no MCP server configured → Log warning, use static images only
|
||||
- If MCP call fails → Use cached data (even if stale)
|
||||
- If cache miss and MCP fails → Continue without Figma context
|
||||
- Never fail planning workflow due to Figma extraction issues
|
||||
|
||||
**Performance**: Figma extraction adds ~2-5s per file (with caching) or ~10-20s (first time).
|
||||
|
||||
**Integration with Images**:
|
||||
- Figma designs = **authoritative** (design system, tokens, components)
|
||||
- Static images = **supplementary** (specific screens, states, flows)
|
||||
- Both included in Linear description, Figma prioritized for implementation
|
||||
|
||||
### Step 1: Gather All Context from External PM Systems
|
||||
|
||||
**Only if Jira ticket ID is available** (from parameter or Linear description):
|
||||
|
||||
1. **Use Atlassian MCP** to:
|
||||
- Fetch Jira ticket: $JIRA_TICKET_ID (or extracted from Linear)
|
||||
- Get all linked issues
|
||||
- Read issue description, comments, attachments
|
||||
|
||||
2. **Search Confluence** for:
|
||||
- Related documentation
|
||||
- PRD (Product Requirements Document)
|
||||
- Design documents
|
||||
- Architecture decisions
|
||||
- Technical specifications
|
||||
- **SAVE all page URLs** for linking in description
|
||||
|
||||
3. **Use Slack MCP** to:
|
||||
- Search relevant channels for discussions about this ticket
|
||||
- Find context from team conversations
|
||||
- Identify any blockers or decisions made
|
||||
- **SAVE thread URLs** for linking in description
|
||||
|
||||
4. **Use Playwright MCP** to:
|
||||
- Open BitBucket for related pull requests
|
||||
- Check commit history for related changes
|
||||
- Review code review comments if applicable
|
||||
- **SAVE PR URLs** for linking in description
|
||||
|
||||
5. **Extract and store all URLs**:
|
||||
- From Jira API responses (issue URLs, attachment URLs)
|
||||
- From Confluence API responses (page URLs)
|
||||
- From Slack API responses (thread/message URLs)
|
||||
- From BitBucket/browser (PR URLs, commit URLs)
|
||||
- From Linear API responses (related issue URLs)
|
||||
|
||||
6. **Use Context7 MCP** to:
|
||||
- Search for latest best practices related to this task
|
||||
- **IMPORTANT**: Do NOT trust knowledge cutoff - always search for current best practices
|
||||
- Find recommended approaches and patterns
|
||||
|
||||
### Step 2: Analyze Codebase
|
||||
|
||||
1. **Read relevant repository files**:
|
||||
- Identify files that need to be modified
|
||||
- Understand current implementation patterns
|
||||
- Find similar features for reference
|
||||
|
||||
2. **Identify patterns and conventions**:
|
||||
- Code structure and organization
|
||||
- Naming conventions
|
||||
- Testing patterns
|
||||
- API design patterns
|
||||
- Error handling approaches
|
||||
|
||||
3. **Map dependencies**:
|
||||
- Which repositories need changes
|
||||
- Dependencies between components
|
||||
- External service integrations
|
||||
- Database schema impacts
|
||||
|
||||
### Step 2.5: Invoke Engineer Agents for Deep Technical Analysis
|
||||
|
||||
**CRITICAL**: After gathering all context and analyzing the codebase, invoke specialized engineer agents to provide deeper insights and validate the approach.
|
||||
|
||||
**Determine which agents to invoke based on the task type:**
|
||||
|
||||
1. **For Backend/API tasks** → Invoke `backend-architect`:
|
||||
```
|
||||
Task(backend-architect): "Analyze the implementation approach for [task description].
|
||||
|
||||
Context:
|
||||
- Jira requirements: [key requirements from Step 1]
|
||||
- Current architecture: [findings from Step 2]
|
||||
- Best practices: [Context7 findings]
|
||||
|
||||
Please provide:
|
||||
1. Recommended architecture approach
|
||||
2. API design considerations
|
||||
3. Database schema changes (if any)
|
||||
4. Performance implications
|
||||
5. Security considerations
|
||||
6. Potential pitfalls to avoid
|
||||
7. Testing strategy recommendations"
|
||||
```
|
||||
|
||||
2. **For Frontend/UI tasks** → Invoke `frontend-developer`:
|
||||
```
|
||||
Task(frontend-developer): "Analyze the implementation approach for [task description].
|
||||
|
||||
Context:
|
||||
- Requirements: [key requirements from Step 1]
|
||||
- Current component structure: [findings from Step 2]
|
||||
- UI patterns used: [patterns found in codebase]
|
||||
|
||||
Please provide:
|
||||
1. Component architecture recommendations
|
||||
2. State management approach
|
||||
3. Reusable components to leverage
|
||||
4. Styling approach (Tailwind/NativeWind patterns)
|
||||
5. Accessibility considerations
|
||||
6. Performance optimizations
|
||||
7. Testing strategy for components"
|
||||
```
|
||||
|
||||
3. **For Mobile-specific tasks** → Invoke `mobile-developer`:
|
||||
```
|
||||
Task(mobile-developer): "Analyze the implementation approach for [task description].
|
||||
|
||||
Context:
|
||||
- Requirements: [key requirements from Step 1]
|
||||
- React Native version and constraints: [from package.json]
|
||||
- Current navigation patterns: [findings from Step 2]
|
||||
|
||||
Please provide:
|
||||
1. Platform-specific considerations (iOS/Android)
|
||||
2. Navigation flow recommendations
|
||||
3. Offline sync strategy (if applicable)
|
||||
4. Native module requirements (if any)
|
||||
5. Performance optimizations for mobile
|
||||
6. Cross-platform compatibility notes
|
||||
7. Testing on different devices"
|
||||
```
|
||||
|
||||
4. **For Full-Stack tasks** → Invoke both `backend-architect` AND `frontend-developer` **in parallel**:
|
||||
```
|
||||
# Use single message with multiple Task calls
|
||||
Task(backend-architect): "[backend-specific analysis as above]"
|
||||
Task(frontend-developer): "[frontend-specific analysis as above]"
|
||||
```
|
||||
|
||||
5. **For Database/Data modeling tasks** → Invoke `backend-architect` (data modeling skill):
|
||||
```
|
||||
Task(backend-architect): "Design the data model for [task description].
|
||||
|
||||
Context:
|
||||
- Current schema: [existing tables/models]
|
||||
- Requirements: [data requirements from Step 1]
|
||||
- Access patterns: [how data will be queried]
|
||||
|
||||
Please provide:
|
||||
1. Schema design recommendations
|
||||
2. Indexing strategy
|
||||
3. Migration approach
|
||||
4. Data validation rules
|
||||
5. Relationships and constraints
|
||||
6. Performance considerations
|
||||
7. Backward compatibility plan"
|
||||
```
|
||||
|
||||
6. **For Security-critical tasks** → Invoke `security-auditor` (in addition to primary agent):
|
||||
```
|
||||
Task(security-auditor): "Review the security implications of [task description].
|
||||
|
||||
Context:
|
||||
- Proposed approach: [from primary agent analysis]
|
||||
- Authentication/authorization requirements: [from Step 1]
|
||||
|
||||
Please provide:
|
||||
1. Security vulnerabilities to address
|
||||
2. OWASP compliance checklist
|
||||
3. Authentication/authorization recommendations
|
||||
4. Data protection requirements
|
||||
5. Input validation strategy
|
||||
6. Secure coding practices to follow"
|
||||
```
|
||||
|
||||
**Agent Invocation Strategy:**
|
||||
|
||||
- **Invoke agents sequentially** when one depends on another (e.g., backend design → security review)
|
||||
- **Invoke agents in parallel** when they analyze independent aspects (e.g., backend + frontend for full-stack tasks)
|
||||
- **ALWAYS wait for agent responses** before proceeding to Step 3
|
||||
- **Incorporate agent insights** into the Linear issue description
|
||||
|
||||
**Capture Agent Insights:**
|
||||
|
||||
After agents respond, extract and organize their insights:
|
||||
|
||||
1. **Architecture recommendations** → Use in "Implementation Plan" section
|
||||
2. **Technical considerations** → Add to "Technical Constraints" section
|
||||
3. **Security/Performance notes** → Include in "Best Practices" section
|
||||
4. **Testing strategy** → Inform checklist subtasks
|
||||
5. **Pitfalls to avoid** → Document in "Considerations" section
|
||||
|
||||
**Example Agent Output Integration:**
|
||||
|
||||
```markdown
|
||||
## 🤖 Engineer Agent Analysis
|
||||
|
||||
### Backend Architecture (backend-architect)
|
||||
- **Recommended Approach**: [Agent's recommendation]
|
||||
- **API Design**: [Agent's API design suggestions]
|
||||
- **Database Changes**: [Agent's schema recommendations]
|
||||
- **Performance**: [Agent's performance notes]
|
||||
- **Security**: [Agent's security considerations]
|
||||
|
||||
### Frontend Architecture (frontend-developer)
|
||||
- **Component Strategy**: [Agent's component recommendations]
|
||||
- **State Management**: [Agent's state management approach]
|
||||
- **Reusable Components**: [Components agent identified]
|
||||
- **Styling Approach**: [Agent's styling recommendations]
|
||||
|
||||
### Security Review (security-auditor)
|
||||
- **Vulnerabilities**: [Agent's identified risks]
|
||||
- **Mitigation Strategy**: [Agent's recommendations]
|
||||
- **Compliance**: [Agent's compliance notes]
|
||||
```
|
||||
|
||||
### Step 3: Update Linear Issue with Research
|
||||
|
||||
Use **Linear operations subagent** to update issue $LINEAR_ISSUE_ID with comprehensive research:
|
||||
|
||||
**Update Status**: Planning (if not already)
|
||||
**Add Labels**: planning, research-complete
|
||||
|
||||
**Subagent Invocation**:
|
||||
```markdown
|
||||
Task(linear-operations): `
|
||||
operation: update_issue
|
||||
params:
|
||||
issue_id: ${LINEAR_ISSUE_ID}
|
||||
state: "Planning"
|
||||
labels:
|
||||
- "planning"
|
||||
- "research-complete"
|
||||
context:
|
||||
command: "planning:plan"
|
||||
purpose: "Updating issue with planning phase research"
|
||||
`
|
||||
```
|
||||
|
||||
After the subagent updates the issue, proceed to update the description with research content (see below).
|
||||
|
||||
**IMPORTANT - Linking Format**:
|
||||
|
||||
When mentioning Jira tickets, Confluence pages, or related issues, create proper markdown links:
|
||||
|
||||
1. **Extract URLs from MCP responses** - When fetching from Atlassian MCP, Linear MCP, or Playwright:
|
||||
- Capture full URLs from API responses
|
||||
- Save them for linking in the description
|
||||
|
||||
2. **Link Format Examples**:
|
||||
- **Jira tickets**: `[TRAIN-123](https://jira.company.com/browse/TRAIN-123)`
|
||||
- **Confluence pages**: `[PRD: Authentication Flow](https://confluence.company.com/display/DOCS/Authentication)`
|
||||
- **Linear issues**: `[WORK-456](https://linear.app/workspace/issue/WORK-456)`
|
||||
- **BitBucket PRs**: `[PR #123: Add JWT auth](https://bitbucket.org/company/repo/pull-requests/123)`
|
||||
- **Slack threads**: `[Discussion about auth](https://company.slack.com/archives/C123/p456789)`
|
||||
|
||||
3. **Link Storage**:
|
||||
- Store all discovered URLs in a map/object as you research
|
||||
- Use them when writing the description
|
||||
- Example:
|
||||
```javascript
|
||||
const links = {
|
||||
jira: "https://jira.company.com/browse/TRAIN-123",
|
||||
confluence: {
|
||||
prd: "https://confluence.company.com/display/DOCS/PRD",
|
||||
design: "https://confluence.company.com/display/DOCS/Design"
|
||||
},
|
||||
relatedTickets: [
|
||||
{ id: "TRAIN-100", url: "https://jira.company.com/browse/TRAIN-100" },
|
||||
{ id: "TRAIN-101", url: "https://jira.company.com/browse/TRAIN-101" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. **Every mention MUST be a link**:
|
||||
- ✅ `See [TRAIN-123](https://jira.company.com/browse/TRAIN-123) for details`
|
||||
- ❌ `See TRAIN-123 for details` (no link)
|
||||
- ✅ `Based on [PRD](https://confluence.company.com/display/DOCS/PRD)`
|
||||
- ❌ `Based on PRD` (no link)
|
||||
|
||||
**Update Description with Subagent**:
|
||||
|
||||
After formatting the comprehensive research content below, use the linear-operations subagent to update the description:
|
||||
|
||||
```markdown
|
||||
Task(linear-operations): `
|
||||
operation: update_issue
|
||||
params:
|
||||
issue_id: ${LINEAR_ISSUE_ID}
|
||||
description: |
|
||||
${FORMATTED_RESEARCH_DESCRIPTION}
|
||||
context:
|
||||
command: "planning:plan"
|
||||
purpose: "Updating issue description with research findings"
|
||||
`
|
||||
```
|
||||
|
||||
**Description** structure (to be included in the subagent description update):
|
||||
|
||||
```markdown
|
||||
## ✅ Implementation Checklist
|
||||
|
||||
> **Status**: Planning
|
||||
> **Complexity**: [Low/Medium/High]
|
||||
|
||||
- [ ] **Subtask 1**: [Specific, actionable description]
|
||||
- [ ] **Subtask 2**: [Specific, actionable description]
|
||||
- [ ] **Subtask 3**: [Specific, actionable description]
|
||||
- [ ] **Subtask 4**: [Specific, actionable description]
|
||||
- [ ] **Subtask 5**: [Specific, actionable description]
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Visual Context Analysis
|
||||
|
||||
[If images were analyzed, insert formatted image context here]
|
||||
|
||||
[If Figma designs extracted, insert formatted Figma context here]
|
||||
|
||||
## 📋 Context
|
||||
|
||||
**Linear Issue**: [$LINEAR_ISSUE_ID](https://linear.app/workspace/issue/$LINEAR_ISSUE_ID)
|
||||
**Original Jira Ticket**: [$JIRA_TICKET_ID](https://jira.company.com/browse/$JIRA_TICKET_ID) (if available)
|
||||
**Summary**: [Brief description from Jira/Linear]
|
||||
|
||||
## 🔍 Research Findings
|
||||
|
||||
### Jira/Documentation Analysis
|
||||
|
||||
**Key Requirements**:
|
||||
|
||||
- [Key requirement 1 from Jira]
|
||||
- [Key requirement 2 from Jira]
|
||||
|
||||
**Related Tickets**:
|
||||
|
||||
- [TRAIN-XXX](link) - [Brief description and outcome]
|
||||
- [TRAIN-YYY](link) - [Brief description and outcome]
|
||||
|
||||
**Design Decisions** (from PRD/Confluence):
|
||||
|
||||
- [Decision 1 with link to [Confluence page](link)]
|
||||
- [Decision 2 with link to [Confluence page](link)]
|
||||
|
||||
### Codebase Analysis
|
||||
|
||||
**Current Architecture**:
|
||||
|
||||
- [How feature currently works]
|
||||
- [Relevant files and their purposes]
|
||||
|
||||
**Patterns Used**:
|
||||
|
||||
- [Code patterns found in similar features]
|
||||
- [Conventions to follow]
|
||||
|
||||
**Technical Constraints**:
|
||||
|
||||
- [Any limitations or considerations]
|
||||
|
||||
### Best Practices (from Context7)
|
||||
|
||||
- [Latest recommended approach 1]
|
||||
- [Latest recommended approach 2]
|
||||
- [Performance considerations]
|
||||
- [Security considerations]
|
||||
|
||||
## 🤖 Engineer Agent Analysis
|
||||
|
||||
### Backend Architecture (backend-architect)
|
||||
|
||||
**Recommended Approach**:
|
||||
- [Agent's recommended architecture approach]
|
||||
|
||||
**API Design Considerations**:
|
||||
- [Agent's API design suggestions]
|
||||
- [Endpoint structure recommendations]
|
||||
|
||||
**Database Changes**:
|
||||
- [Agent's schema recommendations]
|
||||
- [Migration strategy]
|
||||
|
||||
**Performance Implications**:
|
||||
- [Agent's performance analysis]
|
||||
- [Optimization opportunities]
|
||||
|
||||
**Security Considerations**:
|
||||
- [Agent's security recommendations]
|
||||
- [OWASP compliance notes]
|
||||
|
||||
**Potential Pitfalls**:
|
||||
- [Agent's warnings about common mistakes]
|
||||
- [Edge cases to handle]
|
||||
|
||||
**Testing Strategy**:
|
||||
- [Agent's recommended testing approach]
|
||||
- [Key test scenarios]
|
||||
|
||||
### Frontend Architecture (frontend-developer)
|
||||
|
||||
*(Include only if frontend work is involved)*
|
||||
|
||||
**Component Architecture**:
|
||||
- [Agent's component structure recommendations]
|
||||
- [Component breakdown]
|
||||
|
||||
**State Management**:
|
||||
- [Agent's state management approach]
|
||||
- [Data flow patterns]
|
||||
|
||||
**Reusable Components**:
|
||||
- [Existing components to leverage]
|
||||
- [New reusable components to create]
|
||||
|
||||
**Styling Approach**:
|
||||
- [Tailwind/NativeWind patterns to use]
|
||||
- [Design system integration]
|
||||
|
||||
**Accessibility**:
|
||||
- [A11y requirements]
|
||||
- [WCAG compliance notes]
|
||||
|
||||
**Performance**:
|
||||
- [Rendering optimizations]
|
||||
- [Memoization strategies]
|
||||
|
||||
### Mobile-Specific Considerations (mobile-developer)
|
||||
|
||||
*(Include only if React Native work is involved)*
|
||||
|
||||
**Platform Differences**:
|
||||
- **iOS**: [iOS-specific considerations]
|
||||
- **Android**: [Android-specific considerations]
|
||||
|
||||
**Navigation**:
|
||||
- [Navigation flow recommendations]
|
||||
- [Screen transition patterns]
|
||||
|
||||
**Offline Sync**:
|
||||
- [Offline data strategy if applicable]
|
||||
|
||||
**Native Modules**:
|
||||
- [Required native modules if any]
|
||||
|
||||
**Performance**:
|
||||
- [Mobile performance optimizations]
|
||||
|
||||
**Testing**:
|
||||
- [Device-specific testing requirements]
|
||||
|
||||
### Security Review (security-auditor)
|
||||
|
||||
*(Include only if security-critical)*
|
||||
|
||||
**Identified Risks**:
|
||||
- [Agent's security vulnerability analysis]
|
||||
|
||||
**Mitigation Strategy**:
|
||||
- [Recommended security controls]
|
||||
|
||||
**Compliance Requirements**:
|
||||
- [OWASP, GDPR, or other compliance notes]
|
||||
|
||||
**Secure Coding Practices**:
|
||||
- [Agent's secure coding recommendations]
|
||||
|
||||
### Cross-Repository Dependencies
|
||||
|
||||
[If applicable]:
|
||||
|
||||
- **Repository 1**: [What needs to change]
|
||||
- **Repository 2**: [What needs to change]
|
||||
- **Database**: [Schema changes if needed]
|
||||
|
||||
## 📝 Implementation Plan
|
||||
|
||||
**Approach**:
|
||||
[Detailed explanation of how to implement this]
|
||||
|
||||
**Considerations**:
|
||||
|
||||
- [Edge cases to handle]
|
||||
- [Backward compatibility]
|
||||
- [Testing strategy]
|
||||
- [Rollout plan if needed]
|
||||
|
||||
## 🔗 References
|
||||
|
||||
- **Linear Issue**: [$LINEAR_ISSUE_ID](https://linear.app/workspace/issue/$LINEAR_ISSUE_ID)
|
||||
- **Original Jira**: [$JIRA_TICKET_ID](https://jira.company.com/browse/$JIRA_TICKET_ID) (if available)
|
||||
- **Related PRD**: [Title](link to Confluence page) (if found)
|
||||
- **Design Doc**: [Title](link to Confluence page) (if found)
|
||||
- **Related PRs**: [PR #XXX](link to BitBucket) (if found)
|
||||
- **Similar Implementation**: [file.ts:123](link to code) (if found)
|
||||
```
|
||||
|
||||
### Step 4: Confirm Completion
|
||||
|
||||
After all Linear updates via subagent are complete:
|
||||
|
||||
1. **Fetch final issue state** using subagent:
|
||||
```markdown
|
||||
Task(linear-operations): `
|
||||
operation: get_issue
|
||||
params:
|
||||
issue_id: ${LINEAR_ISSUE_ID}
|
||||
context:
|
||||
command: "planning:plan"
|
||||
purpose: "Fetching updated issue for confirmation display"
|
||||
`
|
||||
```
|
||||
|
||||
2. Display the Linear issue ID and current status
|
||||
3. Show a summary of the research findings added
|
||||
4. Confirm checklist has been created/updated
|
||||
5. Provide the Linear issue URL
|
||||
6. Show confirmation that all Linear operations completed successfully
|
||||
|
||||
## Output Format
|
||||
|
||||
Provide a summary like:
|
||||
|
||||
```
|
||||
✅ Planning Complete!
|
||||
|
||||
📋 Linear Issue Updated: $LINEAR_ISSUE_ID
|
||||
🔗 URL: https://linear.app/workspace/issue/$LINEAR_ISSUE_ID
|
||||
📝 Jira Reference: $JIRA_TICKET_ID (if available)
|
||||
|
||||
📊 Research Summary Added:
|
||||
- Gathered context from [X] Jira tickets
|
||||
- Found [Y] relevant Confluence docs
|
||||
- Analyzed [Z] related Slack discussions
|
||||
- Identified [N] files to modify
|
||||
- Researched best practices from Context7
|
||||
|
||||
✅ Checklist: [X] subtasks created/updated
|
||||
|
||||
🚀 Ready for implementation! Run: /ccpm:implementation:start $LINEAR_ISSUE_ID
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### Checklist Positioning
|
||||
|
||||
- **ALWAYS place checklist at the TOP** of the description
|
||||
- This makes it immediately visible when opening the ticket
|
||||
- Use blockquote for status and complexity metadata
|
||||
- Separate checklist from detailed research with `---` horizontal rule
|
||||
|
||||
### Linking Best Practices
|
||||
|
||||
- **Every ticket/page mention MUST be a clickable link**
|
||||
- Extract URLs from MCP API responses, not manual construction
|
||||
- Store URLs as you research, use when writing description
|
||||
- Link text should be descriptive (not just ticket ID)
|
||||
- Example: `[TRAIN-123: Add JWT auth](url)` not just `[TRAIN-123](url)`
|
||||
|
||||
### Research Quality
|
||||
|
||||
- Be thorough in research - this is the foundation for successful implementation
|
||||
- Always search Context7 for latest best practices
|
||||
- Cross-reference multiple sources to validate approach
|
||||
- If information is missing, document what's unknown in the Linear issue
|
||||
- Create specific, actionable subtasks in the checklist
|
||||
- Include links to ALL referenced materials (Jira, Confluence, Slack, PRs)
|
||||
|
||||
## Linear Subagent Integration
|
||||
|
||||
This workflow uses the **linear-operations subagent** for all Linear API operations. This provides:
|
||||
|
||||
### Benefits
|
||||
|
||||
- **Token Reduction**: 50-60% fewer tokens per operation through caching and batching
|
||||
- **Performance**: <50ms for cached operations, <500ms for uncached
|
||||
- **Consistency**: Centralized Linear operation logic with standardized error handling
|
||||
- **Maintainability**: Single source of truth for Linear operations
|
||||
|
||||
### Subagent Invocations in This Workflow
|
||||
|
||||
**Step 3.1 - Update Issue Status & Labels**:
|
||||
- Operation: `update_issue`
|
||||
- Sets issue to "Planning" state
|
||||
- Adds labels: "planning", "research-complete"
|
||||
- Uses cached team/state/label lookups
|
||||
|
||||
**Step 3.2 - Update Issue Description**:
|
||||
- Operation: `update_issue`
|
||||
- Sets comprehensive description with research findings
|
||||
- Includes all linked resources (Jira, Confluence, Slack, etc.)
|
||||
- Preserves markdown formatting and structure
|
||||
|
||||
**Step 4.1 - Fetch Final Issue State**:
|
||||
- Operation: `get_issue`
|
||||
- Retrieves updated issue for confirmation display
|
||||
- Shows final state, labels, and status
|
||||
|
||||
### Caching Benefits
|
||||
|
||||
The subagent caches:
|
||||
- Team lookups (first request populates cache, subsequent requests <50ms)
|
||||
- Label existence checks (batch operation reduces MCP calls)
|
||||
- State/status validation (fuzzy matching cached per team)
|
||||
- Project lookups (if specified)
|
||||
|
||||
### Error Handling
|
||||
|
||||
If a subagent operation fails:
|
||||
|
||||
1. Check the error response for the `error.code` and `error.suggestions`
|
||||
2. Most errors include available values (e.g., valid states for a team)
|
||||
3. The workflow can continue partially if non-critical operations fail
|
||||
4. Always re-raise errors that prevent issue creation/update
|
||||
|
||||
### Example Subagent Responses
|
||||
|
||||
**Successful State/Label Update**:
|
||||
```yaml
|
||||
success: true
|
||||
data:
|
||||
id: "abc-123"
|
||||
identifier: "PSN-25"
|
||||
state:
|
||||
id: "state-planning"
|
||||
name: "Planning"
|
||||
labels:
|
||||
- id: "label-1"
|
||||
name: "planning"
|
||||
- id: "label-2"
|
||||
name: "research-complete"
|
||||
metadata:
|
||||
cached: true
|
||||
duration_ms: 95
|
||||
mcp_calls: 1
|
||||
```
|
||||
|
||||
**Error Response (State Not Found)**:
|
||||
```yaml
|
||||
success: false
|
||||
error:
|
||||
code: STATUS_NOT_FOUND
|
||||
message: "Status 'InvalidState' not found for team 'Engineering'"
|
||||
suggestions:
|
||||
- "Use exact status name: 'In Progress', 'Done', etc."
|
||||
- "Use status type: 'started', 'completed', etc."
|
||||
suggestions:
|
||||
- "Run /ccpm:utils:statuses to list available statuses"
|
||||
```
|
||||
|
||||
### Migration Notes
|
||||
|
||||
This refactoring replaces all direct Linear MCP calls with subagent invocations:
|
||||
|
||||
**Before**:
|
||||
```markdown
|
||||
Use Linear MCP to update issue:
|
||||
await mcp__linear__update_issue({
|
||||
id: issueId,
|
||||
state: "Planning",
|
||||
labels: ["planning", "research-complete"]
|
||||
});
|
||||
```
|
||||
|
||||
**After**:
|
||||
```markdown
|
||||
Task(linear-operations): `
|
||||
operation: update_issue
|
||||
params:
|
||||
issue_id: ${issueId}
|
||||
state: "Planning"
|
||||
labels: ["planning", "research-complete"]
|
||||
context:
|
||||
command: "planning:plan"
|
||||
`
|
||||
```
|
||||
|
||||
### Performance Impact
|
||||
|
||||
Expected token reduction for this workflow:
|
||||
- **Before**: ~15,000-20,000 tokens (heavy Linear MCP usage)
|
||||
- **After**: ~6,000-8,000 tokens (subagent with caching)
|
||||
- **Reduction**: ~55-60% fewer tokens
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- **Linear Subagent**: `agents/linear-operations.md` (comprehensive operation reference)
|
||||
- **Image Analysis**: `commands/_shared-image-analysis.md`
|
||||
- **Figma Detection**: `commands/_shared-figma-detection.md`
|
||||
- **Project Configuration**: `commands/_shared-project-config-loader.md`
|
||||
- **Planning Command**: `commands/planning:plan.md`
|
||||
- **Create & Plan Command**: `commands/planning:create.md`
|
||||
- **CCPM Architecture**: `docs/architecture/linear-subagent-architecture.md`
|
||||
193
commands/_shared-project-config-loader.md
Normal file
193
commands/_shared-project-config-loader.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Shared Project Configuration Loader
|
||||
|
||||
**Include this in commands that need project configuration.**
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your command file where you need to load project config:
|
||||
|
||||
```markdown
|
||||
**LOAD PROJECT CONFIG** (see `_shared-project-config-loader.md`)
|
||||
```
|
||||
|
||||
## Configuration Loader Code
|
||||
|
||||
```bash
|
||||
# ============================================================================
|
||||
# Project Configuration Loader
|
||||
# ============================================================================
|
||||
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
|
||||
# Check if config file exists
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "❌ Error: CCPM configuration not found"
|
||||
echo ""
|
||||
echo "Create configuration:"
|
||||
echo " /ccpm:project:add <project-id>"
|
||||
echo ""
|
||||
echo "Or copy example:"
|
||||
echo " cp ~/.claude/plugins/ccpm/ccpm-config.example.yaml ~/.claude/ccpm-config.yaml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get project ID from argument (adjust $INDEX based on your command)
|
||||
# For most commands: $2 is project ID
|
||||
# Adjust this based on your command's argument structure
|
||||
PROJECT_ID="${PROJECT_ARG}" # Set PROJECT_ARG before including this
|
||||
|
||||
# If no project specified, try active project
|
||||
if [[ -z "$PROJECT_ID" ]]; then
|
||||
PROJECT_ID=$(yq eval '.context.current_project' "$CONFIG_FILE" 2>/dev/null)
|
||||
|
||||
if [[ "$PROJECT_ID" == "null" ]] || [[ -z "$PROJECT_ID" ]]; then
|
||||
# No active project - list available and exit
|
||||
echo "⚠️ No project specified and no active project set"
|
||||
echo ""
|
||||
echo "Available projects:"
|
||||
yq eval '.projects | keys | .[]' "$CONFIG_FILE"
|
||||
echo ""
|
||||
echo "To use this command:"
|
||||
echo " 1. Set active project: /ccpm:project:set <project-id>"
|
||||
echo " 2. Or specify in command: <command> ... <project-id>"
|
||||
echo ""
|
||||
echo "To add a new project:"
|
||||
echo " /ccpm:project:add <project-id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ℹ️ Using active project: $PROJECT_ID"
|
||||
fi
|
||||
|
||||
# Validate project exists in configuration
|
||||
if ! yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
|
||||
echo "❌ Error: Project '$PROJECT_ID' not found"
|
||||
echo ""
|
||||
echo "Available projects:"
|
||||
yq eval '.projects | keys | .[]' "$CONFIG_FILE"
|
||||
echo ""
|
||||
echo "Add project:"
|
||||
echo " /ccpm:project:add $PROJECT_ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load all project settings into variables
|
||||
PROJECT_NAME=$(yq eval ".projects.$PROJECT_ID.name" "$CONFIG_FILE")
|
||||
PROJECT_DESCRIPTION=$(yq eval ".projects.$PROJECT_ID.description" "$CONFIG_FILE")
|
||||
|
||||
# Linear configuration
|
||||
LINEAR_TEAM=$(yq eval ".projects.$PROJECT_ID.linear.team" "$CONFIG_FILE")
|
||||
LINEAR_PROJECT=$(yq eval ".projects.$PROJECT_ID.linear.project" "$CONFIG_FILE")
|
||||
LINEAR_DEFAULT_LABELS=$(yq eval ".projects.$PROJECT_ID.linear.default_labels[]" "$CONFIG_FILE" | tr '\n' ',' | sed 's/,$//')
|
||||
|
||||
# External PM configuration
|
||||
EXTERNAL_PM_ENABLED=$(yq eval ".projects.$PROJECT_ID.external_pm.enabled" "$CONFIG_FILE")
|
||||
EXTERNAL_PM_TYPE=$(yq eval ".projects.$PROJECT_ID.external_pm.type" "$CONFIG_FILE")
|
||||
|
||||
# Jira configuration (if enabled)
|
||||
if [[ "$EXTERNAL_PM_ENABLED" == "true" ]] && [[ "$EXTERNAL_PM_TYPE" == "jira" ]]; then
|
||||
JIRA_ENABLED=$(yq eval ".projects.$PROJECT_ID.external_pm.jira.enabled" "$CONFIG_FILE")
|
||||
JIRA_BASE_URL=$(yq eval ".projects.$PROJECT_ID.external_pm.jira.base_url" "$CONFIG_FILE")
|
||||
JIRA_PROJECT_KEY=$(yq eval ".projects.$PROJECT_ID.external_pm.jira.project_key" "$CONFIG_FILE")
|
||||
|
||||
CONFLUENCE_ENABLED=$(yq eval ".projects.$PROJECT_ID.external_pm.confluence.enabled" "$CONFIG_FILE")
|
||||
CONFLUENCE_BASE_URL=$(yq eval ".projects.$PROJECT_ID.external_pm.confluence.base_url" "$CONFIG_FILE")
|
||||
CONFLUENCE_SPACE_KEY=$(yq eval ".projects.$PROJECT_ID.external_pm.confluence.space_key" "$CONFIG_FILE")
|
||||
|
||||
SLACK_ENABLED=$(yq eval ".projects.$PROJECT_ID.external_pm.slack.enabled" "$CONFIG_FILE")
|
||||
SLACK_WORKSPACE=$(yq eval ".projects.$PROJECT_ID.external_pm.slack.workspace" "$CONFIG_FILE")
|
||||
fi
|
||||
|
||||
# Repository configuration
|
||||
REPO_TYPE=$(yq eval ".projects.$PROJECT_ID.code_repository.type" "$CONFIG_FILE")
|
||||
|
||||
if [[ "$REPO_TYPE" == "github" ]]; then
|
||||
GITHUB_OWNER=$(yq eval ".projects.$PROJECT_ID.code_repository.github.owner" "$CONFIG_FILE")
|
||||
GITHUB_REPO=$(yq eval ".projects.$PROJECT_ID.code_repository.github.repo" "$CONFIG_FILE")
|
||||
elif [[ "$REPO_TYPE" == "bitbucket" ]]; then
|
||||
BITBUCKET_WORKSPACE=$(yq eval ".projects.$PROJECT_ID.code_repository.bitbucket.workspace" "$CONFIG_FILE")
|
||||
BITBUCKET_REPO=$(yq eval ".projects.$PROJECT_ID.code_repository.bitbucket.repo_slug" "$CONFIG_FILE")
|
||||
BITBUCKET_BASE_URL=$(yq eval ".projects.$PROJECT_ID.code_repository.bitbucket.base_url" "$CONFIG_FILE")
|
||||
fi
|
||||
|
||||
# Display loaded configuration (optional - for debugging)
|
||||
echo "✅ Loaded project configuration: $PROJECT_NAME"
|
||||
echo " Linear: $LINEAR_TEAM / $LINEAR_PROJECT"
|
||||
echo " External PM: $([ "$EXTERNAL_PM_ENABLED" = "true" ] && echo "$EXTERNAL_PM_TYPE" || echo "disabled")"
|
||||
echo ""
|
||||
|
||||
# ============================================================================
|
||||
# Available Variables After Loading:
|
||||
# ============================================================================
|
||||
# - PROJECT_ID: Project identifier
|
||||
# - PROJECT_NAME: Human-readable project name
|
||||
# - PROJECT_DESCRIPTION: Project description
|
||||
#
|
||||
# Linear:
|
||||
# - LINEAR_TEAM: Linear team name
|
||||
# - LINEAR_PROJECT: Linear project name
|
||||
# - LINEAR_DEFAULT_LABELS: Comma-separated labels
|
||||
#
|
||||
# External PM:
|
||||
# - EXTERNAL_PM_ENABLED: true/false
|
||||
# - EXTERNAL_PM_TYPE: jira/github/linear-only
|
||||
#
|
||||
# Jira (if enabled):
|
||||
# - JIRA_ENABLED: true/false
|
||||
# - JIRA_BASE_URL: https://jira.company.com
|
||||
# - JIRA_PROJECT_KEY: PROJ
|
||||
# - CONFLUENCE_ENABLED: true/false
|
||||
# - CONFLUENCE_BASE_URL: https://confluence.company.com
|
||||
# - CONFLUENCE_SPACE_KEY: PROJ
|
||||
# - SLACK_ENABLED: true/false
|
||||
# - SLACK_WORKSPACE: company-workspace
|
||||
#
|
||||
# Repository:
|
||||
# - REPO_TYPE: github/bitbucket/gitlab
|
||||
# - GITHUB_OWNER, GITHUB_REPO (if GitHub)
|
||||
# - BITBUCKET_WORKSPACE, BITBUCKET_REPO, BITBUCKET_BASE_URL (if BitBucket)
|
||||
# ============================================================================
|
||||
```
|
||||
|
||||
## Example Usage in Commands
|
||||
|
||||
### In planning:create.md
|
||||
|
||||
```markdown
|
||||
## Load Project Configuration
|
||||
|
||||
Set the project argument index:
|
||||
```bash
|
||||
PROJECT_ARG="$2" # $2 is the project ID in this command
|
||||
```
|
||||
|
||||
**LOAD PROJECT CONFIG** (see `_shared-project-config-loader.md`)
|
||||
|
||||
Now you can use all the loaded variables:
|
||||
- ${LINEAR_TEAM}
|
||||
- ${LINEAR_PROJECT}
|
||||
- ${JIRA_ENABLED}
|
||||
- etc.
|
||||
```
|
||||
|
||||
### In planning:plan.md
|
||||
|
||||
```markdown
|
||||
## Load Project Configuration
|
||||
|
||||
Set the project argument:
|
||||
```bash
|
||||
# Extract from Linear issue or use argument
|
||||
PROJECT_ARG=$(get_project_from_issue "$1") # Or $2, etc.
|
||||
```
|
||||
|
||||
**LOAD PROJECT CONFIG** (see `_shared-project-config-loader.md`)
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Commands must set `PROJECT_ARG` before loading
|
||||
- All variables are available after loading
|
||||
- Handles active project fallback automatically
|
||||
- Provides helpful error messages
|
||||
- Validates project exists before proceeding
|
||||
932
commands/_shared-state-machine.md
Normal file
932
commands/_shared-state-machine.md
Normal file
@@ -0,0 +1,932 @@
|
||||
# CCPM Workflow State Machine
|
||||
|
||||
This file implements the CCPM workflow state machine for tracking task progression through workflow phases. It provides state management, validation, transitions, and persistence via Linear custom fields.
|
||||
|
||||
## Overview
|
||||
|
||||
The state machine tracks 8 workflow phases:
|
||||
- **IDEA** - Initial concept
|
||||
- **PLANNED** - Implementation plan created
|
||||
- **IMPLEMENTING** - Active development
|
||||
- **BLOCKED** - Cannot proceed due to blocker
|
||||
- **VERIFYING** - Quality checks in progress
|
||||
- **VERIFIED** - Verified and ready to complete
|
||||
- **COMPLETE** - Task finalized
|
||||
- **CANCELLED** - Task cancelled/abandoned
|
||||
|
||||
**Integration**: This file delegates all Linear operations to the `linear-operations` subagent for optimal performance.
|
||||
|
||||
---
|
||||
|
||||
## State Machine Definition
|
||||
|
||||
```javascript
|
||||
const STATE_MACHINE = {
|
||||
IDEA: {
|
||||
description: "Initial concept, not yet planned",
|
||||
linear_status_mapping: ["Backlog"],
|
||||
phase: "ideation",
|
||||
next_states: ["PLANNED", "CANCELLED"],
|
||||
confidence_to_transition: {
|
||||
PLANNED: 95, // High confidence via /ccpm:plan
|
||||
CANCELLED: 50 // Requires confirmation
|
||||
},
|
||||
allowed_commands: [
|
||||
"/ccpm:plan",
|
||||
"/ccpm:utils:*"
|
||||
]
|
||||
},
|
||||
|
||||
PLANNED: {
|
||||
description: "Requirements gathered, implementation plan created",
|
||||
linear_status_mapping: ["Planned", "Todo", "Ready"],
|
||||
phase: "planning",
|
||||
next_states: ["IMPLEMENTING", "IDEA", "CANCELLED"],
|
||||
confidence_to_transition: {
|
||||
IMPLEMENTING: 95, // High confidence via /ccpm:work
|
||||
IDEA: 70, // Re-planning
|
||||
CANCELLED: 50 // Requires confirmation
|
||||
},
|
||||
allowed_commands: [
|
||||
"/ccpm:work",
|
||||
"/ccpm:plan", // Update plan
|
||||
"/ccpm:utils:*"
|
||||
]
|
||||
},
|
||||
|
||||
IMPLEMENTING: {
|
||||
description: "Active development in progress",
|
||||
linear_status_mapping: ["In Progress", "In Development", "Doing"],
|
||||
phase: "implementation",
|
||||
next_states: ["VERIFYING", "PLANNED", "BLOCKED"],
|
||||
confidence_to_transition: {
|
||||
VERIFYING: 70, // Medium - depends on checklist
|
||||
PLANNED: 60, // Re-planning
|
||||
BLOCKED: 90 // High if blocker detected
|
||||
},
|
||||
allowed_commands: [
|
||||
"/ccpm:sync",
|
||||
"/ccpm:commit",
|
||||
"/ccpm:verify",
|
||||
"/ccpm:implementation:*",
|
||||
"/ccpm:utils:*"
|
||||
]
|
||||
},
|
||||
|
||||
BLOCKED: {
|
||||
description: "Cannot proceed due to blocker",
|
||||
linear_status_mapping: ["Blocked"],
|
||||
phase: "implementation",
|
||||
next_states: ["IMPLEMENTING", "CANCELLED"],
|
||||
confidence_to_transition: {
|
||||
IMPLEMENTING: 85, // High when unblocked
|
||||
CANCELLED: 50 // Requires confirmation
|
||||
},
|
||||
allowed_commands: [
|
||||
"/ccpm:verification:fix",
|
||||
"/ccpm:utils:status",
|
||||
"/ccpm:sync"
|
||||
]
|
||||
},
|
||||
|
||||
VERIFYING: {
|
||||
description: "Quality checks and verification in progress",
|
||||
linear_status_mapping: ["In Review", "Testing", "Verification"],
|
||||
phase: "verification",
|
||||
next_states: ["VERIFIED", "IMPLEMENTING"],
|
||||
confidence_to_transition: {
|
||||
VERIFIED: 85, // High if all checks pass
|
||||
IMPLEMENTING: 100 // Certain if checks fail
|
||||
},
|
||||
allowed_commands: [
|
||||
"/ccpm:verify",
|
||||
"/ccpm:verification:*",
|
||||
"/ccpm:utils:*"
|
||||
]
|
||||
},
|
||||
|
||||
VERIFIED: {
|
||||
description: "Verified and ready to complete",
|
||||
linear_status_mapping: ["Verified", "Ready for Review", "Approved"],
|
||||
phase: "completion",
|
||||
next_states: ["COMPLETE", "IMPLEMENTING"],
|
||||
confidence_to_transition: {
|
||||
COMPLETE: 95, // High confidence via /ccpm:done
|
||||
IMPLEMENTING: 70 // If changes needed
|
||||
},
|
||||
allowed_commands: [
|
||||
"/ccpm:done",
|
||||
"/ccpm:complete:finalize",
|
||||
"/ccpm:utils:*"
|
||||
]
|
||||
},
|
||||
|
||||
COMPLETE: {
|
||||
description: "Task finalized and closed",
|
||||
linear_status_mapping: ["Done", "Completed", "Closed"],
|
||||
phase: "complete",
|
||||
next_states: [], // Terminal state
|
||||
confidence_to_transition: {},
|
||||
allowed_commands: [
|
||||
"/ccpm:utils:status",
|
||||
"/ccpm:utils:report"
|
||||
]
|
||||
},
|
||||
|
||||
CANCELLED: {
|
||||
description: "Task cancelled or abandoned",
|
||||
linear_status_mapping: ["Cancelled", "Archived"],
|
||||
phase: "cancelled",
|
||||
next_states: [], // Terminal state
|
||||
confidence_to_transition: {},
|
||||
allowed_commands: [
|
||||
"/ccpm:utils:status"
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Functions
|
||||
|
||||
### 1. loadWorkflowState
|
||||
|
||||
Load current workflow state from Linear custom fields.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Load workflow state from Linear issue
|
||||
* @param {string} issueId - Linear issue ID or identifier
|
||||
* @returns {Promise<Object>} Workflow state object
|
||||
*/
|
||||
async function loadWorkflowState(issueId) {
|
||||
// Delegate to Linear subagent
|
||||
const result = await Task('linear-operations', `
|
||||
operation: get_issue
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
include_custom_fields: true
|
||||
context:
|
||||
command: "state-machine:load"
|
||||
purpose: "Loading workflow state"
|
||||
`);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(`Failed to load workflow state: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
const issue = result.data;
|
||||
const customFields = issue.customFields || {};
|
||||
|
||||
// Extract CCPM state fields
|
||||
const phase = customFields.ccpmPhase || inferPhaseFromStatus(issue.state.name);
|
||||
const lastCommand = customFields.ccpmLastCommand || null;
|
||||
const lastUpdate = customFields.ccpmLastUpdate || issue.updatedAt;
|
||||
const autoTransitions = customFields.ccpmAutoTransitions !== false; // Default true
|
||||
const verificationGate = customFields.ccpmVerificationGate || 'STANDARD';
|
||||
const checklistRequired = customFields.ccpmChecklistRequired !== false; // Default true
|
||||
|
||||
return {
|
||||
// Current state
|
||||
phase,
|
||||
linearStatus: issue.state.name,
|
||||
lastCommand,
|
||||
lastUpdate,
|
||||
|
||||
// Configuration
|
||||
autoTransitions,
|
||||
verificationGate,
|
||||
checklistRequired,
|
||||
|
||||
// Metadata
|
||||
issueId: issue.id,
|
||||
issueIdentifier: issue.identifier,
|
||||
title: issue.title,
|
||||
teamId: issue.team.id,
|
||||
projectId: issue.project?.id || null,
|
||||
|
||||
// Calculated fields
|
||||
nextStates: STATE_MACHINE[phase]?.next_states || [],
|
||||
allowedCommands: STATE_MACHINE[phase]?.allowed_commands || []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer CCPM phase from Linear status name (fallback)
|
||||
* @param {string} statusName - Linear status name
|
||||
* @returns {string} CCPM phase
|
||||
*/
|
||||
function inferPhaseFromStatus(statusName) {
|
||||
const lower = statusName.toLowerCase();
|
||||
|
||||
if (lower.includes('backlog')) return 'IDEA';
|
||||
if (lower.includes('plan') || lower.includes('todo') || lower.includes('ready')) return 'PLANNED';
|
||||
if (lower.includes('progress') || lower.includes('doing') || lower.includes('development')) return 'IMPLEMENTING';
|
||||
if (lower.includes('blocked')) return 'BLOCKED';
|
||||
if (lower.includes('review') || lower.includes('verif') || lower.includes('testing')) return 'VERIFYING';
|
||||
if (lower.includes('approved')) return 'VERIFIED';
|
||||
if (lower.includes('done') || lower.includes('complete') || lower.includes('closed')) return 'COMPLETE';
|
||||
if (lower.includes('cancel') || lower.includes('archived')) return 'CANCELLED';
|
||||
|
||||
// Default fallback
|
||||
return 'IDEA';
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. saveWorkflowState
|
||||
|
||||
Persist workflow state to Linear custom fields.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Save workflow state to Linear
|
||||
* @param {string} issueId - Linear issue ID or identifier
|
||||
* @param {Object} stateUpdates - State fields to update
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function saveWorkflowState(issueId, stateUpdates) {
|
||||
// Build custom fields update
|
||||
const customFields = {};
|
||||
|
||||
if (stateUpdates.phase !== undefined) {
|
||||
customFields.ccpmPhase = stateUpdates.phase;
|
||||
}
|
||||
|
||||
if (stateUpdates.lastCommand !== undefined) {
|
||||
customFields.ccpmLastCommand = stateUpdates.lastCommand;
|
||||
}
|
||||
|
||||
// Always update timestamp
|
||||
customFields.ccpmLastUpdate = new Date().toISOString();
|
||||
|
||||
if (stateUpdates.autoTransitions !== undefined) {
|
||||
customFields.ccpmAutoTransitions = stateUpdates.autoTransitions;
|
||||
}
|
||||
|
||||
if (stateUpdates.verificationGate !== undefined) {
|
||||
customFields.ccpmVerificationGate = stateUpdates.verificationGate;
|
||||
}
|
||||
|
||||
if (stateUpdates.checklistRequired !== undefined) {
|
||||
customFields.ccpmChecklistRequired = stateUpdates.checklistRequired;
|
||||
}
|
||||
|
||||
// Delegate to Linear subagent
|
||||
const result = await Task('linear-operations', `
|
||||
operation: update_issue_custom_fields
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
custom_fields:
|
||||
${Object.entries(customFields).map(([key, value]) =>
|
||||
`${key}: ${typeof value === 'string' ? `"${value}"` : value}`
|
||||
).join('\n ')}
|
||||
context:
|
||||
command: "state-machine:save"
|
||||
purpose: "Persisting workflow state"
|
||||
`);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(`Failed to save workflow state: ${result.error?.message || 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. validateTransition
|
||||
|
||||
Validate if a state transition is allowed.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Validate if transition is allowed
|
||||
* @param {string} fromPhase - Current phase
|
||||
* @param {string} toPhase - Target phase
|
||||
* @param {Object} options - Validation options
|
||||
* @param {string} options.issueId - Issue ID for pre-condition checks
|
||||
* @returns {Promise<Object>} Validation result
|
||||
*/
|
||||
async function validateTransition(fromPhase, toPhase, options = {}) {
|
||||
const stateMachine = STATE_MACHINE;
|
||||
const currentStateConfig = stateMachine[fromPhase];
|
||||
|
||||
if (!currentStateConfig) {
|
||||
return {
|
||||
valid: false,
|
||||
confidence: 0,
|
||||
error: `Unknown phase: ${fromPhase}`,
|
||||
suggestions: Object.keys(stateMachine)
|
||||
};
|
||||
}
|
||||
|
||||
const allowedNextStates = currentStateConfig.next_states || [];
|
||||
|
||||
if (!allowedNextStates.includes(toPhase)) {
|
||||
return {
|
||||
valid: false,
|
||||
confidence: 0,
|
||||
error: `Cannot transition from ${fromPhase} to ${toPhase}`,
|
||||
allowedStates: allowedNextStates,
|
||||
suggestions: allowedNextStates.map(s => `Try transitioning to: ${s}`)
|
||||
};
|
||||
}
|
||||
|
||||
// Validate pre-conditions if issueId provided
|
||||
if (options.issueId) {
|
||||
const preConditions = await validatePreConditions(options.issueId, toPhase);
|
||||
|
||||
if (!preConditions.passed) {
|
||||
return {
|
||||
valid: false,
|
||||
confidence: 0,
|
||||
error: `Pre-conditions not met for ${toPhase}`,
|
||||
failures: preConditions.failures,
|
||||
suggestions: preConditions.failures.map(f => `Fix: ${f}`)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get confidence for this transition
|
||||
const confidence = currentStateConfig.confidence_to_transition[toPhase] || 90;
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
confidence
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate pre-conditions for target phase
|
||||
* @param {string} issueId - Issue ID
|
||||
* @param {string} toPhase - Target phase
|
||||
* @returns {Promise<Object>} Pre-condition validation result
|
||||
*/
|
||||
async function validatePreConditions(issueId, toPhase) {
|
||||
const failures = [];
|
||||
|
||||
// Load issue data
|
||||
const state = await loadWorkflowState(issueId);
|
||||
const issueResult = await Task('linear-operations', `
|
||||
operation: get_issue
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
context:
|
||||
command: "state-machine:validate-preconditions"
|
||||
`);
|
||||
|
||||
if (!issueResult.success) {
|
||||
failures.push('Failed to fetch issue data');
|
||||
return { passed: false, failures };
|
||||
}
|
||||
|
||||
const issue = issueResult.data;
|
||||
|
||||
switch (toPhase) {
|
||||
case 'PLANNED':
|
||||
// Must have implementation checklist
|
||||
if (!issue.description.includes('## Implementation Checklist')) {
|
||||
failures.push('Missing implementation checklist');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'IMPLEMENTING':
|
||||
// Must be planned
|
||||
if (!issue.description.includes('## Implementation Checklist')) {
|
||||
failures.push('Not planned yet - run /ccpm:plan first');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VERIFYING':
|
||||
// Check checklist completion
|
||||
const checklist = extractChecklist(issue.description);
|
||||
if (state.checklistRequired && checklist.completion < 100) {
|
||||
failures.push(`Checklist incomplete (${checklist.completion}%)`);
|
||||
}
|
||||
|
||||
// Check for uncommitted changes (local git check)
|
||||
const hasUncommitted = await hasUncommittedChanges();
|
||||
if (hasUncommitted) {
|
||||
failures.push('Uncommitted changes detected - commit first');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VERIFIED':
|
||||
// Must have passing verification
|
||||
// Note: This is typically checked by verification command
|
||||
break;
|
||||
|
||||
case 'COMPLETE':
|
||||
// Must be verified
|
||||
if (state.phase !== 'VERIFIED') {
|
||||
failures.push('Not verified yet - run /ccpm:verify first');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
passed: failures.length === 0,
|
||||
failures
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract checklist from description
|
||||
* @param {string} description - Issue description
|
||||
* @returns {Object} Checklist data
|
||||
*/
|
||||
function extractChecklist(description) {
|
||||
const checklistItems = description.match(/- \[([ x])\] .+/g) || [];
|
||||
const completed = checklistItems.filter(i => i.includes('[x]')).length;
|
||||
const total = checklistItems.length;
|
||||
|
||||
return {
|
||||
items: checklistItems,
|
||||
completed,
|
||||
total,
|
||||
completion: total > 0 ? Math.round((completed / total) * 100) : 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for uncommitted changes (local git)
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function hasUncommittedChanges() {
|
||||
try {
|
||||
const result = await Bash({
|
||||
command: 'git status --porcelain',
|
||||
description: 'Check for uncommitted changes'
|
||||
});
|
||||
return result.trim().length > 0;
|
||||
} catch (error) {
|
||||
return false; // Not a git repo or error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. transitionState
|
||||
|
||||
Execute a state transition with validation and persistence.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Execute state transition
|
||||
* @param {string} issueId - Issue ID
|
||||
* @param {string} toPhase - Target phase
|
||||
* @param {Object} options - Transition options
|
||||
* @param {string} options.reason - Reason for transition
|
||||
* @param {boolean} options.userConfirmed - User confirmed transition
|
||||
* @param {string} options.command - Command triggering transition
|
||||
* @returns {Promise<Object>} Transition result
|
||||
*/
|
||||
async function transitionState(issueId, toPhase, options = {}) {
|
||||
const {
|
||||
reason = 'State transition',
|
||||
userConfirmed = false,
|
||||
command = 'unknown'
|
||||
} = options;
|
||||
|
||||
// Load current state
|
||||
const currentState = await loadWorkflowState(issueId);
|
||||
|
||||
// Validate transition
|
||||
const validation = await validateTransition(
|
||||
currentState.phase,
|
||||
toPhase,
|
||||
{ issueId }
|
||||
);
|
||||
|
||||
if (!validation.valid) {
|
||||
return {
|
||||
success: false,
|
||||
error: validation.error,
|
||||
failures: validation.failures,
|
||||
suggestions: validation.suggestions
|
||||
};
|
||||
}
|
||||
|
||||
// Check if confirmation needed
|
||||
const requiresConfirmation = validation.confidence < 80;
|
||||
|
||||
if (requiresConfirmation && !userConfirmed) {
|
||||
return {
|
||||
success: false,
|
||||
requiresConfirmation: true,
|
||||
message: `Transition ${currentState.phase} → ${toPhase} requires confirmation`,
|
||||
confidence: validation.confidence
|
||||
};
|
||||
}
|
||||
|
||||
// Determine new Linear status
|
||||
const newLinearStatus = getLinearStatusForPhase(toPhase);
|
||||
|
||||
// Update Linear issue status
|
||||
const updateResult = await Task('linear-operations', `
|
||||
operation: update_issue
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
state: "${newLinearStatus}"
|
||||
context:
|
||||
command: "state-machine:transition"
|
||||
from_phase: "${currentState.phase}"
|
||||
to_phase: "${toPhase}"
|
||||
`);
|
||||
|
||||
if (!updateResult.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Failed to update Linear status: ${updateResult.error?.message}`
|
||||
};
|
||||
}
|
||||
|
||||
// Update workflow state
|
||||
await saveWorkflowState(issueId, {
|
||||
phase: toPhase,
|
||||
lastCommand: command
|
||||
});
|
||||
|
||||
// Add transition comment
|
||||
await addTransitionComment(issueId, currentState.phase, toPhase, reason);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
fromPhase: currentState.phase,
|
||||
toPhase,
|
||||
newStatus: newLinearStatus,
|
||||
confidence: validation.confidence
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Linear status for phase
|
||||
* @param {string} phase - CCPM phase
|
||||
* @returns {string} Linear status name
|
||||
*/
|
||||
function getLinearStatusForPhase(phase) {
|
||||
const statusMap = {
|
||||
IDEA: 'Backlog',
|
||||
PLANNED: 'Planned',
|
||||
IMPLEMENTING: 'In Progress',
|
||||
BLOCKED: 'Blocked',
|
||||
VERIFYING: 'In Review',
|
||||
VERIFIED: 'Verified',
|
||||
COMPLETE: 'Done',
|
||||
CANCELLED: 'Cancelled'
|
||||
};
|
||||
|
||||
return statusMap[phase] || 'Backlog';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add transition comment to Linear
|
||||
* @param {string} issueId - Issue ID
|
||||
* @param {string} fromPhase - Previous phase
|
||||
* @param {string} toPhase - New phase
|
||||
* @param {string} reason - Transition reason
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function addTransitionComment(issueId, fromPhase, toPhase, reason) {
|
||||
const emoji = getPhaseEmoji(toPhase);
|
||||
|
||||
const commentBody = `${emoji} **Workflow Phase: ${fromPhase} → ${toPhase}**
|
||||
|
||||
${reason}
|
||||
|
||||
---
|
||||
*Automated state transition*`;
|
||||
|
||||
await Task('linear-operations', `
|
||||
operation: create_comment
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
body: |
|
||||
${commentBody}
|
||||
context:
|
||||
command: "state-machine:transition-comment"
|
||||
`);
|
||||
}
|
||||
|
||||
function getPhaseEmoji(phase) {
|
||||
const emojis = {
|
||||
IDEA: '💡',
|
||||
PLANNED: '📋',
|
||||
IMPLEMENTING: '🚀',
|
||||
BLOCKED: '🚫',
|
||||
VERIFYING: '🔍',
|
||||
VERIFIED: '✅',
|
||||
COMPLETE: '🎉',
|
||||
CANCELLED: '❌'
|
||||
};
|
||||
|
||||
return emojis[phase] || '📌';
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. suggestNextAction
|
||||
|
||||
Suggest the next command based on current workflow state.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Suggest next action based on current state
|
||||
* @param {Object} state - Workflow state
|
||||
* @returns {Object} Action suggestion
|
||||
*/
|
||||
function suggestNextAction(state) {
|
||||
const phase = state.phase;
|
||||
|
||||
switch (phase) {
|
||||
case 'IDEA':
|
||||
return {
|
||||
command: '/ccpm:plan',
|
||||
description: 'Create implementation plan',
|
||||
confidence: 90,
|
||||
reasoning: 'Task needs planning before implementation'
|
||||
};
|
||||
|
||||
case 'PLANNED':
|
||||
return {
|
||||
command: '/ccpm:work',
|
||||
description: 'Start implementation',
|
||||
confidence: 90,
|
||||
reasoning: 'Plan is ready, begin development'
|
||||
};
|
||||
|
||||
case 'IMPLEMENTING':
|
||||
// Check progress
|
||||
if (state.checklistCompletion >= 100) {
|
||||
return {
|
||||
command: '/ccpm:verify',
|
||||
description: 'Run quality checks',
|
||||
confidence: 85,
|
||||
reasoning: 'Checklist complete, verify before completion'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
command: '/ccpm:sync',
|
||||
description: 'Save progress',
|
||||
confidence: 70,
|
||||
reasoning: 'Continue implementation and sync progress'
|
||||
};
|
||||
}
|
||||
|
||||
case 'BLOCKED':
|
||||
return {
|
||||
command: '/ccpm:verification:fix',
|
||||
description: 'Fix blocker',
|
||||
confidence: 80,
|
||||
reasoning: 'Address blocking issue to continue'
|
||||
};
|
||||
|
||||
case 'VERIFYING':
|
||||
return {
|
||||
command: '/ccpm:verify',
|
||||
description: 'Continue verification',
|
||||
confidence: 80,
|
||||
reasoning: 'Complete verification process'
|
||||
};
|
||||
|
||||
case 'VERIFIED':
|
||||
return {
|
||||
command: '/ccpm:done',
|
||||
description: 'Finalize and create PR',
|
||||
confidence: 95,
|
||||
reasoning: 'Verification passed, ready to complete'
|
||||
};
|
||||
|
||||
case 'COMPLETE':
|
||||
return null; // Terminal state
|
||||
|
||||
case 'CANCELLED':
|
||||
return null; // Terminal state
|
||||
|
||||
default:
|
||||
return {
|
||||
command: '/ccpm:utils:status',
|
||||
description: 'Check task status',
|
||||
confidence: 60,
|
||||
reasoning: 'Unknown state, check status'
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. isCommandAllowed
|
||||
|
||||
Check if command is allowed in current state.
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Check if command is allowed in current phase
|
||||
* @param {string} command - Command to check
|
||||
* @param {string} phase - Current phase
|
||||
* @returns {Object} Validation result
|
||||
*/
|
||||
function isCommandAllowed(command, phase) {
|
||||
const stateConfig = STATE_MACHINE[phase];
|
||||
|
||||
if (!stateConfig) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Unknown phase: ${phase}`
|
||||
};
|
||||
}
|
||||
|
||||
const allowedPatterns = stateConfig.allowed_commands || [];
|
||||
|
||||
// Check if command matches any pattern
|
||||
const isAllowed = allowedPatterns.some(pattern => {
|
||||
if (pattern.endsWith('*')) {
|
||||
// Wildcard pattern (e.g., "/ccpm:utils:*")
|
||||
const prefix = pattern.slice(0, -1);
|
||||
return command.startsWith(prefix);
|
||||
} else {
|
||||
// Exact match
|
||||
return command === pattern;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isAllowed) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Command ${command} not typically used in ${phase} phase`,
|
||||
suggestedCommands: allowedPatterns
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
allowed: true
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Example
|
||||
|
||||
```javascript
|
||||
// Example: Using state machine in /ccpm:work command
|
||||
|
||||
async function executeWork(issueId) {
|
||||
// Load current state
|
||||
const state = await loadWorkflowState(issueId);
|
||||
|
||||
console.log(`\n📊 Current Phase: ${state.phase}`);
|
||||
console.log(`📋 Status: ${state.linearStatus}\n`);
|
||||
|
||||
// Check if command is allowed
|
||||
const commandCheck = isCommandAllowed('/ccpm:work', state.phase);
|
||||
|
||||
if (!commandCheck.allowed) {
|
||||
console.log(`⚠️ ${commandCheck.reason}`);
|
||||
console.log(`\nSuggested commands for ${state.phase}:`);
|
||||
commandCheck.suggestedCommands.forEach(cmd => console.log(` • ${cmd}`));
|
||||
console.log('');
|
||||
|
||||
// Ask user if they want to continue anyway
|
||||
const answer = await askUserForClarification({
|
||||
question: "Continue anyway?",
|
||||
header: "Confirm",
|
||||
options: [
|
||||
{ label: "Yes, continue", description: "Proceed with command" },
|
||||
{ label: "No, cancel", description: "Cancel command" }
|
||||
]
|
||||
});
|
||||
|
||||
if (!answer.includes('Yes')) {
|
||||
console.log('❌ Cancelled');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine action based on current phase
|
||||
if (state.phase === 'PLANNED') {
|
||||
// START mode - transition to IMPLEMENTING
|
||||
console.log('🚀 Starting implementation...\n');
|
||||
|
||||
const transitionResult = await transitionState(issueId, 'IMPLEMENTING', {
|
||||
reason: 'Implementation started via /ccpm:work',
|
||||
command: '/ccpm:work'
|
||||
});
|
||||
|
||||
if (transitionResult.success) {
|
||||
console.log(`✅ Transitioned: ${transitionResult.fromPhase} → ${transitionResult.toPhase}`);
|
||||
// ... continue with implementation start
|
||||
} else {
|
||||
console.error(`❌ Transition failed: ${transitionResult.error}`);
|
||||
if (transitionResult.failures) {
|
||||
transitionResult.failures.forEach(f => console.log(` • ${f}`));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (state.phase === 'IMPLEMENTING') {
|
||||
// RESUME mode
|
||||
console.log('⏩ Resuming implementation...\n');
|
||||
// ... continue with resume logic
|
||||
} else {
|
||||
// Unexpected phase
|
||||
const suggestion = suggestNextAction(state);
|
||||
if (suggestion) {
|
||||
console.log(`💡 Suggested: ${suggestion.command}`);
|
||||
console.log(` ${suggestion.description}\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Display Helpers
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Display workflow state summary
|
||||
* @param {Object} state - Workflow state
|
||||
*/
|
||||
function displayStateSummary(state) {
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('🎯 Workflow State');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
const emoji = getPhaseEmoji(state.phase);
|
||||
console.log(`${emoji} Phase: ${state.phase}`);
|
||||
console.log(`📋 Status: ${state.linearStatus}`);
|
||||
|
||||
if (state.lastCommand) {
|
||||
console.log(`⚙️ Last Command: ${state.lastCommand}`);
|
||||
}
|
||||
|
||||
const lastUpdateDate = new Date(state.lastUpdate);
|
||||
console.log(`🕐 Last Update: ${lastUpdateDate.toLocaleString()}`);
|
||||
|
||||
console.log('\n📍 Next Actions:');
|
||||
state.nextStates.forEach(nextState => {
|
||||
console.log(` • Transition to ${nextState}`);
|
||||
});
|
||||
|
||||
const suggestion = suggestNextAction(state);
|
||||
if (suggestion) {
|
||||
console.log(`\n💡 Suggested: ${suggestion.command}`);
|
||||
console.log(` ${suggestion.description}`);
|
||||
console.log(` Confidence: ${suggestion.confidence}%`);
|
||||
}
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always load state** before command execution
|
||||
2. **Validate transitions** before attempting state changes
|
||||
3. **Check pre-conditions** for critical transitions
|
||||
4. **Display state** for user awareness
|
||||
5. **Persist transitions** with comments for audit trail
|
||||
6. **Suggest next actions** to guide workflow
|
||||
7. **Handle terminal states** (COMPLETE, CANCELLED) gracefully
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
```javascript
|
||||
// Test state loading
|
||||
const state = await loadWorkflowState('PSN-29');
|
||||
console.log('Loaded state:', state);
|
||||
|
||||
// Test transition validation
|
||||
const validation = await validateTransition('IMPLEMENTING', 'VERIFYING', {
|
||||
issueId: 'PSN-29'
|
||||
});
|
||||
console.log('Validation:', validation);
|
||||
|
||||
// Test state transition
|
||||
const result = await transitionState('PSN-29', 'VERIFYING', {
|
||||
reason: 'Checklist complete',
|
||||
command: '/ccpm:verify'
|
||||
});
|
||||
console.log('Transition result:', result);
|
||||
|
||||
// Test next action suggestion
|
||||
const suggestion = suggestNextAction(state);
|
||||
console.log('Suggested action:', suggestion);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documents
|
||||
|
||||
- [Decision Framework](../docs/architecture/decision-framework.md)
|
||||
- [Workflow State Tracking Design](../docs/architecture/workflow-state-tracking.md)
|
||||
- [Decision Helpers](./_shared-decision-helpers.md)
|
||||
424
commands/_shared-workflow-state.md
Normal file
424
commands/_shared-workflow-state.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# Shared Workflow State Detection
|
||||
|
||||
This file provides workflow state detection utilities used by the new natural workflow commands (`plan`, `work`, `sync`, `commit`, `verify`, `done`).
|
||||
|
||||
## Purpose
|
||||
|
||||
Detect potential workflow issues before executing commands:
|
||||
- Uncommitted changes before creating new task
|
||||
- Stale sync (>2h) before starting work
|
||||
- Incomplete tasks before finalizing
|
||||
- Wrong branch before operations
|
||||
|
||||
## Architecture
|
||||
|
||||
**Linear Operations**: This file delegates all Linear read operations to the `linear-operations` subagent for optimal token usage and caching.
|
||||
|
||||
**Git Operations**: All git-based state detection remains local in this file (no external dependencies).
|
||||
|
||||
**Function Classification**:
|
||||
- Linear read functions (use subagent): `detectStaleSync()`, `checkTaskCompletion()`
|
||||
- Pure git functions (local): `detectUncommittedChanges()`, `detectActiveWork()`, `isBranchPushed()`
|
||||
|
||||
## State Detection Functions
|
||||
|
||||
### 1. Detect Uncommitted Changes
|
||||
|
||||
```javascript
|
||||
function detectUncommittedChanges() {
|
||||
try {
|
||||
const status = execSync('git status --porcelain', {
|
||||
encoding: 'utf-8'
|
||||
}).trim()
|
||||
|
||||
if (status.length === 0) {
|
||||
return { hasChanges: false }
|
||||
}
|
||||
|
||||
// Parse changes
|
||||
const lines = status.split('\n')
|
||||
const changes = lines.map(line => {
|
||||
const status = line.substring(0, 2)
|
||||
const path = line.substring(3)
|
||||
return { status: status.trim(), path }
|
||||
})
|
||||
|
||||
return {
|
||||
hasChanges: true,
|
||||
count: changes.length,
|
||||
changes,
|
||||
summary: generateChangeSummary(changes)
|
||||
}
|
||||
} catch (error) {
|
||||
return { hasChanges: false, error: 'Not a git repository' }
|
||||
}
|
||||
}
|
||||
|
||||
function generateChangeSummary(changes) {
|
||||
const modified = changes.filter(c => c.status === 'M').length
|
||||
const added = changes.filter(c => c.status === 'A' || c.status === '??').length
|
||||
const deleted = changes.filter(c => c.status === 'D').length
|
||||
|
||||
const parts = []
|
||||
if (modified > 0) parts.push(`${modified} modified`)
|
||||
if (added > 0) parts.push(`${added} new`)
|
||||
if (deleted > 0) parts.push(`${deleted} deleted`)
|
||||
|
||||
return parts.join(', ')
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Detect Stale Sync
|
||||
|
||||
Uses Linear subagent to fetch issue comments, then compares with current time.
|
||||
|
||||
```javascript
|
||||
async function detectStaleSync(issueId) {
|
||||
try {
|
||||
// Step 1: Fetch issue with comments via Linear subagent
|
||||
const linearResult = await Task('linear-operations', `
|
||||
operation: get_issue
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
include_comments: true
|
||||
context:
|
||||
command: "workflow:detect-stale"
|
||||
purpose: "Checking if Linear comments are stale"
|
||||
`);
|
||||
|
||||
if (!linearResult.success) {
|
||||
return {
|
||||
isStale: false,
|
||||
error: linearResult.error?.message || 'Failed to fetch issue'
|
||||
}
|
||||
}
|
||||
|
||||
const issue = linearResult.data
|
||||
const comments = issue.comments || []
|
||||
|
||||
// Step 2: Find most recent sync comment (local logic)
|
||||
const syncComments = comments.filter(c =>
|
||||
c.body.includes('## 🔄 Progress Sync') ||
|
||||
c.body.includes('Progress Sync') ||
|
||||
c.body.includes('📝 Implementation Progress')
|
||||
)
|
||||
|
||||
if (syncComments.length === 0) {
|
||||
return { isStale: false, reason: 'No previous sync' }
|
||||
}
|
||||
|
||||
// Step 3: Compare timestamps (local logic)
|
||||
const lastSync = syncComments[syncComments.length - 1]
|
||||
const lastSyncTime = new Date(lastSync.createdAt)
|
||||
const now = new Date()
|
||||
const hoursSinceSync = (now - lastSyncTime) / (1000 * 60 * 60)
|
||||
|
||||
return {
|
||||
isStale: hoursSinceSync > 2,
|
||||
hoursSinceSync: Math.round(hoursSinceSync * 10) / 10,
|
||||
lastSyncTime: lastSyncTime.toISOString()
|
||||
}
|
||||
} catch (error) {
|
||||
return { isStale: false, error: error.message }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: The Linear subagent caches comments at session level, making subsequent calls very fast (<50ms).
|
||||
The parsing and comparison logic remains local for full control over stale detection thresholds.
|
||||
|
||||
### 3. Detect Active Work on Another Task
|
||||
|
||||
```javascript
|
||||
async function detectActiveWork(currentIssueId) {
|
||||
try {
|
||||
// Check git branch for different issue
|
||||
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
||||
encoding: 'utf-8'
|
||||
}).trim()
|
||||
|
||||
const branchMatch = branch.match(/([A-Z]+-\d+)/)
|
||||
if (branchMatch && branchMatch[1] !== currentIssueId) {
|
||||
return {
|
||||
hasActiveWork: true,
|
||||
activeIssueId: branchMatch[1],
|
||||
branch
|
||||
}
|
||||
}
|
||||
|
||||
// Check for uncommitted work
|
||||
const uncommitted = detectUncommittedChanges()
|
||||
if (uncommitted.hasChanges) {
|
||||
return {
|
||||
hasActiveWork: true,
|
||||
uncommittedChanges: uncommitted
|
||||
}
|
||||
}
|
||||
|
||||
return { hasActiveWork: false }
|
||||
} catch (error) {
|
||||
return { hasActiveWork: false, error: error.message }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Check If Branch is Pushed
|
||||
|
||||
```javascript
|
||||
function isBranchPushed() {
|
||||
try {
|
||||
execSync('git rev-parse @{u}', { stdio: 'ignore' })
|
||||
return { isPushed: true }
|
||||
} catch {
|
||||
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
||||
encoding: 'utf-8'
|
||||
}).trim()
|
||||
|
||||
return {
|
||||
isPushed: false,
|
||||
branch,
|
||||
command: `git push -u origin ${branch}`
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Check Task Completion Status
|
||||
|
||||
Uses Linear subagent to fetch issue description, then parses checklist locally.
|
||||
|
||||
```javascript
|
||||
async function checkTaskCompletion(issueId) {
|
||||
try {
|
||||
// Step 1: Fetch issue via Linear subagent
|
||||
const linearResult = await Task('linear-operations', `
|
||||
operation: get_issue
|
||||
params:
|
||||
issue_id: "${issueId}"
|
||||
include_comments: false
|
||||
include_attachments: false
|
||||
context:
|
||||
command: "workflow:check-completion"
|
||||
purpose: "Checking task completion status from checklist"
|
||||
`);
|
||||
|
||||
if (!linearResult.success) {
|
||||
return {
|
||||
hasChecklist: false,
|
||||
isComplete: false,
|
||||
error: linearResult.error?.message || 'Failed to fetch issue'
|
||||
}
|
||||
}
|
||||
|
||||
const issue = linearResult.data
|
||||
|
||||
// Step 2: Parse checklist from description (local logic)
|
||||
const description = issue.description || ''
|
||||
const checklistMatch = description.match(/- \[([ x])\]/g)
|
||||
|
||||
if (!checklistMatch) {
|
||||
return {
|
||||
hasChecklist: false,
|
||||
isComplete: true // No checklist = assume complete
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Calculate completion percentage (local logic)
|
||||
const total = checklistMatch.length
|
||||
const completed = checklistMatch.filter(m => m.includes('[x]')).length
|
||||
const percent = Math.round((completed / total) * 100)
|
||||
|
||||
return {
|
||||
hasChecklist: true,
|
||||
isComplete: completed === total,
|
||||
total,
|
||||
completed,
|
||||
percent,
|
||||
remaining: total - completed
|
||||
}
|
||||
} catch (error) {
|
||||
return { hasChecklist: false, isComplete: false, error: error.message }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: The Linear subagent caches issue descriptions at session level.
|
||||
The regex parsing and completion calculation remain local for full control over what constitutes "completion".
|
||||
|
||||
## Usage in Commands
|
||||
|
||||
### In `/ccpm:plan` (before creating new task)
|
||||
|
||||
```javascript
|
||||
// Check for active work before creating new task
|
||||
const activeWork = await detectActiveWork(null)
|
||||
|
||||
if (activeWork.hasActiveWork) {
|
||||
console.log("⚠️ You have active work in progress")
|
||||
console.log("")
|
||||
|
||||
if (activeWork.activeIssueId) {
|
||||
console.log(`Current branch: ${activeWork.branch}`)
|
||||
console.log(`Active issue: ${activeWork.activeIssueId}`)
|
||||
}
|
||||
|
||||
if (activeWork.uncommittedChanges) {
|
||||
console.log(`Uncommitted changes: ${activeWork.uncommittedChanges.summary}`)
|
||||
}
|
||||
|
||||
console.log("")
|
||||
console.log("Recommendation:")
|
||||
console.log(" 1. Commit current work: /ccpm:commit")
|
||||
console.log(" 2. Or sync progress: /ccpm:sync")
|
||||
console.log(" 3. Then create new task")
|
||||
console.log("")
|
||||
|
||||
// Ask user if they want to proceed anyway
|
||||
const answer = await askUser("Create new task anyway?")
|
||||
if (answer !== "Yes") {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### In `/ccpm:work` (before starting work)
|
||||
|
||||
```javascript
|
||||
// Check for stale sync
|
||||
const staleCheck = await detectStaleSync(issueId)
|
||||
|
||||
if (staleCheck.isStale) {
|
||||
console.log(`⚠️ Last sync was ${staleCheck.hoursSinceSync} hours ago`)
|
||||
console.log("")
|
||||
console.log("Recommendation: Sync progress first")
|
||||
console.log(` /ccpm:sync ${issueId}`)
|
||||
console.log("")
|
||||
|
||||
const answer = await askUser("Continue without syncing?")
|
||||
if (answer !== "Yes") {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### In `/ccpm:done` (before finalizing)
|
||||
|
||||
```javascript
|
||||
// Check task completion
|
||||
const completion = await checkTaskCompletion(issueId)
|
||||
|
||||
if (completion.hasChecklist && !completion.isComplete) {
|
||||
console.log(`⚠️ Task is only ${completion.percent}% complete`)
|
||||
console.log(` ${completion.remaining} checklist items remaining`)
|
||||
console.log("")
|
||||
console.log("Recommendation: Complete all tasks first")
|
||||
console.log(` /ccpm:work ${issueId}`)
|
||||
console.log("")
|
||||
|
||||
const answer = await askUser("Finalize incomplete task?")
|
||||
if (answer !== "Yes") {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if branch is pushed
|
||||
const pushCheck = isBranchPushed()
|
||||
|
||||
if (!pushCheck.isPushed) {
|
||||
console.error("❌ Branch not pushed to remote")
|
||||
console.log("")
|
||||
console.log(`Push first: ${pushCheck.command}`)
|
||||
process.exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
## Warning Display Template
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Workflow Warning
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
${warningMessage}
|
||||
|
||||
${recommendation}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Subagent Integration
|
||||
|
||||
### Linear Operations Subagent
|
||||
|
||||
Two functions in this file use the `linear-operations` subagent for optimized read operations:
|
||||
|
||||
1. **`detectStaleSync(issueId)`**
|
||||
- Uses: `linear-operations` with `get_issue` operation
|
||||
- Fetches: Issue with comments (`include_comments: true`)
|
||||
- Local logic: Filters sync comments, compares timestamps
|
||||
- Performance: <50ms for cached calls, ~400-500ms for uncached
|
||||
- Caching: Session-level cache automatically populated
|
||||
|
||||
2. **`checkTaskCompletion(issueId)`**
|
||||
- Uses: `linear-operations` with `get_issue` operation
|
||||
- Fetches: Issue description only (no comments/attachments)
|
||||
- Local logic: Regex parsing, completion calculation
|
||||
- Performance: <50ms for cached calls, ~400-500ms for uncached
|
||||
- Caching: Session-level cache automatically populated
|
||||
|
||||
### Why Use the Subagent?
|
||||
|
||||
- **Token Efficiency**: 60-70% fewer tokens vs direct Linear MCP calls
|
||||
- **Caching**: Session-level cache hits = massive performance boost
|
||||
- **Consistency**: Single source of truth for Linear API interactions
|
||||
- **Error Handling**: Standardized error responses with helpful suggestions
|
||||
- **Maintainability**: Linear API changes isolated to subagent
|
||||
|
||||
### Error Handling
|
||||
|
||||
Both functions gracefully handle subagent errors:
|
||||
|
||||
```javascript
|
||||
if (!linearResult.success) {
|
||||
return {
|
||||
isStale: false, // or appropriate default
|
||||
error: linearResult.error?.message || 'Fallback error message'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the subagent fails to fetch Linear data, the workflow continues with safe defaults rather than blocking.
|
||||
|
||||
### Subagent Task Format
|
||||
|
||||
Both functions use the standard CCPM subagent invocation pattern:
|
||||
|
||||
```javascript
|
||||
const result = await Task('linear-operations', `
|
||||
operation: <operation_name>
|
||||
params:
|
||||
<param_name>: <value>
|
||||
...
|
||||
context:
|
||||
command: "workflow:..."
|
||||
purpose: "..."
|
||||
`);
|
||||
```
|
||||
|
||||
Key fields:
|
||||
- `operation`: The subagent operation (e.g., `get_issue`)
|
||||
- `params`: Operation parameters with issue_id/team/etc
|
||||
- `context`: Metadata for logging and command tracking
|
||||
- `success`: Result boolean indicating success/failure
|
||||
- `data`: Operation response (issue object, etc)
|
||||
- `error`: Error details if success=false
|
||||
- `metadata`: Execution metrics (duration_ms, mcp_calls, cached flag)
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Prevents Common Mistakes**: Catches issues before they cause problems
|
||||
✅ **Actionable Recommendations**: Always suggests what to do next
|
||||
✅ **User Control**: Warnings, not errors - user can proceed if needed
|
||||
✅ **Context Aware**: Different checks for different workflow stages
|
||||
✅ **Optimized Linear Reads**: Uses subagent caching for 60-70% token reduction
|
||||
✅ **Pure Git Operations**: All git logic remains fast and local
|
||||
402
commands/commit.md
Normal file
402
commands/commit.md
Normal file
@@ -0,0 +1,402 @@
|
||||
---
|
||||
description: Smart git commit with Linear integration and conventional commits
|
||||
allowed-tools: [Bash, LinearMCP]
|
||||
argument-hint: "[issue-id] [message]"
|
||||
---
|
||||
|
||||
# Smart Commit Command
|
||||
|
||||
You are executing the **smart git commit command** that integrates with Linear and follows conventional commits format.
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
This command performs **git operations** which are local and safe. No external PM system writes.
|
||||
|
||||
## Conventional Commits Format
|
||||
|
||||
This command follows the [Conventional Commits](https://www.conventionalcommits.org/) specification:
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
```
|
||||
|
||||
**Types**:
|
||||
- `feat`: New feature
|
||||
- `fix`: Bug fix
|
||||
- `docs`: Documentation changes
|
||||
- `style`: Code style changes (formatting, etc.)
|
||||
- `refactor`: Code refactoring
|
||||
- `test`: Adding or updating tests
|
||||
- `chore`: Maintenance tasks
|
||||
|
||||
## Implementation
|
||||
|
||||
### Step 1: Determine Issue ID
|
||||
|
||||
```javascript
|
||||
const args = process.argv.slice(2)
|
||||
let issueId = args[0]
|
||||
let userMessage = args[1]
|
||||
|
||||
const ISSUE_ID_PATTERN = /^[A-Z]+-\d+$/
|
||||
|
||||
// If first arg doesn't look like issue ID, it might be the message
|
||||
if (args[0] && !ISSUE_ID_PATTERN.test(args[0])) {
|
||||
userMessage = args[0]
|
||||
issueId = null
|
||||
}
|
||||
|
||||
// Try to detect issue ID from git branch
|
||||
if (!issueId) {
|
||||
try {
|
||||
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
||||
encoding: 'utf-8'
|
||||
}).trim()
|
||||
|
||||
const branchMatch = branch.match(/([A-Z]+-\d+)/)
|
||||
if (branchMatch) {
|
||||
issueId = branchMatch[1]
|
||||
console.log(`🔍 Detected issue from branch: ${issueId}`)
|
||||
}
|
||||
} catch (error) {
|
||||
// Not in a git repo or branch detection failed
|
||||
console.log("ℹ️ Could not detect issue from branch")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Check for Uncommitted Changes
|
||||
|
||||
```bash
|
||||
# Get status
|
||||
git status --porcelain
|
||||
|
||||
# Check if there are changes to commit
|
||||
if [ -z "$(git status --porcelain)" ]; then
|
||||
echo "✅ No changes to commit (working tree clean)"
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 3: Show Changes Summary
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Smart Commit Command
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
${issueId ? `📋 Issue: ${issueId}` : ''}
|
||||
|
||||
📊 Changes to commit:
|
||||
────────────────────
|
||||
|
||||
${changedFiles.map((file, i) => ` ${i+1}. ${file.status} ${file.path}`).join('\n')}
|
||||
|
||||
📈 Total: ${changedFiles.length} file(s) changed
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Fetch Issue Context (if Issue ID available)
|
||||
|
||||
If issue ID is available, get context from Linear:
|
||||
|
||||
```javascript
|
||||
let issueTitle = null
|
||||
let issueType = null
|
||||
|
||||
if (issueId) {
|
||||
try {
|
||||
const issue = await linear_get_issue(issueId)
|
||||
issueTitle = issue.title
|
||||
issueType = detectIssueType(issue)
|
||||
|
||||
console.log(`📋 Issue: ${issueId} - ${issueTitle}`)
|
||||
console.log("")
|
||||
} catch (error) {
|
||||
console.log(`⚠️ Could not fetch issue ${issueId} from Linear`)
|
||||
console.log(" Proceeding without issue context")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Analyze Changes and Determine Commit Type
|
||||
|
||||
```javascript
|
||||
function analyzeChanges(changedFiles) {
|
||||
const analysis = {
|
||||
hasTests: false,
|
||||
hasSource: false,
|
||||
hasDocs: false,
|
||||
hasConfig: false,
|
||||
newFiles: 0,
|
||||
modifiedFiles: 0
|
||||
}
|
||||
|
||||
changedFiles.forEach(file => {
|
||||
if (file.status === 'A' || file.status === '??') {
|
||||
analysis.newFiles++
|
||||
} else if (file.status === 'M') {
|
||||
analysis.modifiedFiles++
|
||||
}
|
||||
|
||||
if (file.path.includes('test') || file.path.includes('spec')) {
|
||||
analysis.hasTests = true
|
||||
} else if (file.path.includes('src/') || file.path.includes('lib/')) {
|
||||
analysis.hasSource = true
|
||||
} else if (file.path.match(/\.(md|txt)$/)) {
|
||||
analysis.hasDocs = true
|
||||
} else if (file.path.match(/\.(config|json|yaml|yml)$/)) {
|
||||
analysis.hasConfig = true
|
||||
}
|
||||
})
|
||||
|
||||
return analysis
|
||||
}
|
||||
|
||||
function suggestCommitType(analysis, issueType) {
|
||||
// Priority order for determining type
|
||||
if (issueType === 'bug') return 'fix'
|
||||
if (issueType === 'feature') return 'feat'
|
||||
|
||||
// Infer from changes
|
||||
if (analysis.hasSource && analysis.newFiles > 0) return 'feat'
|
||||
if (analysis.hasSource && analysis.modifiedFiles > 0) {
|
||||
// Could be feat, fix, or refactor - let user choose
|
||||
return 'feat' // default to feat
|
||||
}
|
||||
if (analysis.hasTests && !analysis.hasSource) return 'test'
|
||||
if (analysis.hasDocs && !analysis.hasSource) return 'docs'
|
||||
if (analysis.hasConfig) return 'chore'
|
||||
|
||||
return 'feat' // default
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Generate or Collect Commit Message
|
||||
|
||||
```javascript
|
||||
let commitType, commitScope, commitDescription
|
||||
|
||||
if (userMessage) {
|
||||
// User provided message, parse or use as-is
|
||||
const conventionalMatch = userMessage.match(/^(\w+)(\([\w-]+\))?: (.+)$/)
|
||||
|
||||
if (conventionalMatch) {
|
||||
// Already in conventional format
|
||||
commitType = conventionalMatch[1]
|
||||
commitScope = conventionalMatch[2]?.slice(1, -1) // Remove parens
|
||||
commitDescription = conventionalMatch[3]
|
||||
} else {
|
||||
// Plain message, add conventional format
|
||||
commitType = suggestCommitType(analysis, issueType)
|
||||
commitScope = issueId ? issueId : null
|
||||
commitDescription = userMessage
|
||||
}
|
||||
} else {
|
||||
// Auto-generate from context
|
||||
commitType = suggestCommitType(analysis, issueType)
|
||||
commitScope = issueId ? issueId : null
|
||||
|
||||
// Generate description
|
||||
if (issueTitle) {
|
||||
commitDescription = issueTitle
|
||||
} else {
|
||||
// Generate from file changes
|
||||
commitDescription = generateDescriptionFromChanges(analysis, changedFiles)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: Display Proposed Commit Message
|
||||
|
||||
```markdown
|
||||
💬 Proposed Commit Message:
|
||||
───────────────────────────
|
||||
|
||||
${commitType}${commitScope ? `(${commitScope})` : ''}: ${commitDescription}
|
||||
|
||||
${issueId ? `
|
||||
Related to: ${issueId}
|
||||
${issueTitle ? `Issue: ${issueTitle}` : ''}
|
||||
` : ''}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Confirm and Commit
|
||||
|
||||
Use **AskUserQuestion** to confirm:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Proceed with this commit?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, commit",
|
||||
description: "Create commit with this message"
|
||||
},
|
||||
{
|
||||
label: "Edit message",
|
||||
description: "Let me modify the commit message"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Don't commit, go back"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**If "Yes, commit"**:
|
||||
|
||||
```bash
|
||||
# Stage all changes
|
||||
git add .
|
||||
|
||||
# Create commit with conventional format
|
||||
git commit -m "${commitType}${commitScope ? `(${commitScope})` : ''}: ${commitDescription}" \
|
||||
${issueId ? `-m "Related to: ${issueId}"` : ''} \
|
||||
${issueTitle ? `-m "${issueTitle}"` : ''}
|
||||
|
||||
echo "✅ Commit created successfully!"
|
||||
echo ""
|
||||
echo "Commit: $(git log -1 --oneline)"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " /ccpm:sync # Sync progress to Linear"
|
||||
echo " /ccpm:work # Continue working"
|
||||
echo " git push # Push to remote"
|
||||
```
|
||||
|
||||
**If "Edit message"**:
|
||||
|
||||
```markdown
|
||||
Please provide your commit message (conventional format preferred):
|
||||
|
||||
Format: <type>(<scope>): <description>
|
||||
Examples:
|
||||
- feat(auth): add JWT token validation
|
||||
- fix(PSN-27): resolve login button click handler
|
||||
- docs: update API documentation
|
||||
|
||||
Your message:
|
||||
> [User input]
|
||||
```
|
||||
|
||||
Then repeat confirmation.
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### Detect Issue Type
|
||||
|
||||
```javascript
|
||||
function detectIssueType(issue) {
|
||||
const title = issue.title.toLowerCase()
|
||||
const labels = issue.labels || []
|
||||
|
||||
// Check labels first
|
||||
if (labels.includes('bug') || labels.includes('fix')) return 'bug'
|
||||
if (labels.includes('feature') || labels.includes('enhancement')) return 'feature'
|
||||
|
||||
// Check title keywords
|
||||
if (title.includes('fix') || title.includes('bug')) return 'bug'
|
||||
if (title.includes('add') || title.includes('implement')) return 'feature'
|
||||
|
||||
return 'feature' // default
|
||||
}
|
||||
```
|
||||
|
||||
### Generate Description from Changes
|
||||
|
||||
```javascript
|
||||
function generateDescriptionFromChanges(analysis, changedFiles) {
|
||||
if (analysis.newFiles > 0 && analysis.hasSource) {
|
||||
const mainFile = changedFiles.find(f => f.status === 'A' && f.path.includes('src/'))
|
||||
if (mainFile) {
|
||||
const fileName = mainFile.path.split('/').pop().replace(/\.(ts|js|tsx|jsx)$/, '')
|
||||
return `add ${fileName} module`
|
||||
}
|
||||
return `add new feature components`
|
||||
}
|
||||
|
||||
if (analysis.modifiedFiles > 0 && analysis.hasSource) {
|
||||
return `update implementation`
|
||||
}
|
||||
|
||||
if (analysis.hasTests) {
|
||||
return `add tests`
|
||||
}
|
||||
|
||||
if (analysis.hasDocs) {
|
||||
return `update documentation`
|
||||
}
|
||||
|
||||
return `update files`
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Commit with Auto-Detection
|
||||
|
||||
```bash
|
||||
git checkout -b duongdev/PSN-27-add-auth
|
||||
# ... make changes ...
|
||||
/ccpm:commit
|
||||
```
|
||||
|
||||
**Detection**: PSN-27 from branch, fetches issue title from Linear
|
||||
**Generated**: `feat(PSN-27): Add user authentication`
|
||||
**Result**: Conventional commit created with Linear link
|
||||
|
||||
### Example 2: Commit with Custom Message
|
||||
|
||||
```bash
|
||||
/ccpm:commit PSN-27 "Completed JWT token validation"
|
||||
```
|
||||
|
||||
**Result**: `feat(PSN-27): Completed JWT token validation`
|
||||
|
||||
### Example 3: Commit with Full Conventional Format
|
||||
|
||||
```bash
|
||||
/ccpm:commit "fix(auth): resolve login button handler"
|
||||
```
|
||||
|
||||
**Result**: Uses provided conventional format as-is
|
||||
|
||||
### Example 4: Commit Without Issue ID
|
||||
|
||||
```bash
|
||||
/ccpm:commit "update documentation"
|
||||
```
|
||||
|
||||
**Result**: `docs: update documentation`
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Conventional Commits**: Automatic format following best practices
|
||||
✅ **Linear Integration**: Links commits to issues automatically
|
||||
✅ **Smart Detection**: Auto-detects commit type from changes
|
||||
✅ **Auto-Generation**: Creates meaningful messages from context
|
||||
✅ **Git Integration**: Built into workflow (no context switching)
|
||||
✅ **Change Summary**: Shows what's being committed before confirming
|
||||
|
||||
## Migration Hint
|
||||
|
||||
This is a NEW command that integrates git commits into CCPM workflow:
|
||||
- Replaces manual `git add . && git commit -m "message"`
|
||||
- Automatically follows conventional commits format
|
||||
- Links commits to Linear issues
|
||||
- Part of natural workflow (plan → work → commit → sync → verify → done)
|
||||
356
commands/complete:finalize.md
Normal file
356
commands/complete:finalize.md
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
description: Finalize completed task - sync with Jira, create PR, clean up
|
||||
allowed-tools: [Bash, LinearMCP, AtlassianMCP, SlackMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Finalizing Task: $1
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:done [issue-id]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Auto-detects issue from git branch if not provided
|
||||
- Includes pre-flight safety checks (uncommitted changes, branch pushed, etc.)
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
⛔ **WILL ASK FOR CONFIRMATION** before posting to Jira, Slack, or creating PR!
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Verify Task is Complete
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
**A) Check Status**
|
||||
|
||||
Verify status is "Done" or "Verification" (passed).
|
||||
|
||||
If status is "In Progress" or "Backlog":
|
||||
- Display: ⚠️ Task status is "$status". Run `/ccpm:verification:verify $1` first.
|
||||
- Exit
|
||||
|
||||
**B) Parse and Verify Checklist Completion**
|
||||
|
||||
Look for checklist in description using markers:
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1
|
||||
- [x] Task 2
|
||||
<!-- ccpm-checklist-end -->
|
||||
```
|
||||
|
||||
Or find "## ✅ Implementation Checklist" header.
|
||||
|
||||
**Calculate completion:**
|
||||
- Total items: Count all `- [ ]` and `- [x]` lines
|
||||
- Checked items: Count `- [x]` lines only
|
||||
- Percentage: (checked / total) × 100
|
||||
|
||||
**If completion < 100%:**
|
||||
|
||||
Display incomplete items:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⛔ Cannot Finalize: Checklist Incomplete
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Progress: X% (Y/Z completed)
|
||||
|
||||
❌ Remaining Items:
|
||||
- [ ] Task 3: Description
|
||||
- [ ] Task 5: Description
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔧 Actions Required
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. Complete remaining items
|
||||
2. Update checklist: /ccpm:utils:update-checklist $1
|
||||
3. Then run finalize again: /ccpm:complete:finalize $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**BLOCK finalization and exit.**
|
||||
|
||||
**If completion = 100%:**
|
||||
|
||||
Display:
|
||||
```
|
||||
✅ Checklist complete! (100% - Z/Z items)
|
||||
```
|
||||
|
||||
Continue to Step 2.
|
||||
|
||||
**C) Check for "blocked" label**
|
||||
|
||||
If "blocked" label exists:
|
||||
- Display: ⚠️ Task has "blocked" label. Resolve blockers before finalizing.
|
||||
- Exit
|
||||
|
||||
**If all verifications pass:**
|
||||
- Continue to Step 2
|
||||
|
||||
### Step 2: Generate Completion Summary
|
||||
|
||||
Create summary from Linear description and checklist:
|
||||
|
||||
```markdown
|
||||
## Implementation Summary for $1
|
||||
|
||||
### What Was Implemented
|
||||
[Extract from checklist items marked complete]
|
||||
|
||||
### Files Modified
|
||||
[Extract file paths mentioned in description/comments]
|
||||
|
||||
### Tests Added
|
||||
[Extract test information]
|
||||
|
||||
### Related Links
|
||||
- Linear: [link]
|
||||
- Jira: [link]
|
||||
- PRs: [links if exist]
|
||||
```
|
||||
|
||||
### Step 3: Interactive Finalization Choices
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Do you want to create a Pull Request?",
|
||||
header: "Create PR",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Yes, Create PR", description: "Generate PR with description"},
|
||||
{label: "No, Skip PR", description: "I'll create it manually later"}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Do you want to update Jira status?",
|
||||
header: "Sync Jira",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Yes, Update Jira", description: "Mark Jira ticket as Done"},
|
||||
{label: "No, Skip Jira", description: "I'll update manually"}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Do you want to notify team in Slack?",
|
||||
header: "Notify Team",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Yes, Notify Slack", description: "Post completion message"},
|
||||
{label: "No, Skip Slack", description: "No notification needed"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Execute Chosen Actions
|
||||
|
||||
**If Create PR chosen**:
|
||||
- Generate PR title from Linear title
|
||||
- Generate PR description from implementation summary
|
||||
- Suggest command: `gh pr create --title "..." --body "..."`
|
||||
- Show command for user approval
|
||||
|
||||
**If Update Jira chosen**:
|
||||
- **ASK FOR CONFIRMATION** with preview:
|
||||
```
|
||||
🚨 CONFIRMATION REQUIRED
|
||||
|
||||
I will update Jira ticket [JIRA-ID] to status "Done" with comment:
|
||||
---
|
||||
Completed in Linear: [WORK-123]
|
||||
[Implementation summary]
|
||||
---
|
||||
|
||||
Proceed? (yes/no)
|
||||
```
|
||||
- If yes → Use Atlassian MCP to update
|
||||
|
||||
**If Notify Slack chosen**:
|
||||
- **ASK FOR CONFIRMATION** with preview:
|
||||
```
|
||||
🚨 CONFIRMATION REQUIRED
|
||||
|
||||
I will post to #[channel]:
|
||||
---
|
||||
✅ [Linear title] is complete!
|
||||
[Brief summary]
|
||||
Linear: [link]
|
||||
---
|
||||
|
||||
Proceed? (yes/no)
|
||||
```
|
||||
- If yes → Use Slack MCP to post
|
||||
|
||||
### Step 5: Update Linear Status and Labels
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
Use **Linear MCP** to mark task as complete:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// Get team ID from issue
|
||||
const teamId = issue.team.id;
|
||||
|
||||
// Get valid "Done" state ID
|
||||
const doneStateId = await getValidStateId(teamId, "Done");
|
||||
|
||||
// Get or create "done" label
|
||||
const doneLabel = await getOrCreateLabel(teamId, "done", {
|
||||
color: "#4cb782",
|
||||
description: "CCPM: Task completed successfully"
|
||||
});
|
||||
|
||||
// Get current labels
|
||||
const currentLabels = issue.labels || [];
|
||||
const currentLabelIds = currentLabels.map(l => l.id);
|
||||
|
||||
// Find labels to remove
|
||||
const implementationLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "implementation"
|
||||
);
|
||||
const verificationLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "verification"
|
||||
);
|
||||
const blockedLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "blocked"
|
||||
);
|
||||
|
||||
// Build new label list: remove workflow labels, add done
|
||||
let newLabelIds = currentLabelIds.filter(id =>
|
||||
id !== implementationLabel?.id &&
|
||||
id !== verificationLabel?.id &&
|
||||
id !== blockedLabel?.id
|
||||
);
|
||||
|
||||
// Add done label if not already present
|
||||
if (!currentLabels.some(l => l.name.toLowerCase() === "done")) {
|
||||
newLabelIds.push(doneLabel.id);
|
||||
}
|
||||
|
||||
// Update issue with Done status and final labels
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "update_issue",
|
||||
args: {
|
||||
id: issue.id,
|
||||
stateId: doneStateId,
|
||||
labelIds: newLabelIds
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Linear issue finalized:");
|
||||
console.log(" Status: Done");
|
||||
console.log(" Labels: done (removed implementation, verification, blocked)");
|
||||
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to update Linear issue:", error.message);
|
||||
console.warn("⚠️ Task is complete but status may not be updated in Linear.");
|
||||
console.log(" You can manually update status to Done if needed.");
|
||||
}
|
||||
```
|
||||
|
||||
**Add completion timestamp comment**:
|
||||
|
||||
```javascript
|
||||
const finalComment = `## 🎉 Task Completed and Finalized
|
||||
|
||||
**Completion Time**: ${new Date().toISOString()}
|
||||
|
||||
### Actions Taken:
|
||||
${prCreated ? '✅ Pull Request created' : '⏭️ PR creation skipped'}
|
||||
${jiraUpdated ? '✅ Jira status updated to Done' : '⏭️ Jira update skipped'}
|
||||
${slackNotified ? '✅ Team notified in Slack' : '⏭️ Slack notification skipped'}
|
||||
|
||||
### Final Status:
|
||||
- Linear: Done ✅
|
||||
- All workflow labels cleaned up
|
||||
- Task marked as complete
|
||||
|
||||
---
|
||||
|
||||
**This task is now closed and archived.** 🎊
|
||||
`;
|
||||
|
||||
try {
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "create_comment",
|
||||
args: {
|
||||
issueId: issue.id,
|
||||
body: finalComment
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Completion comment added to Linear");
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to add comment:", error.message);
|
||||
// Not critical, continue
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Show Final Summary
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎉 Task Finalized: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ Linear: Updated to Done
|
||||
[✅/⏭️ ] Pull Request: [Created/Skipped]
|
||||
[✅/⏭️ ] Jira: [Updated/Skipped]
|
||||
[✅/⏭️ ] Slack: [Notified/Skipped]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 What's Next?
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
Use **AskUserQuestion** for next action:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Task complete! What would you like to do next?",
|
||||
header: "Next Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Create New Task", description: "Start a new task"},
|
||||
{label: "View Project Report", description: "See project progress"},
|
||||
{label: "Pick Another Task", description: "Work on existing task"},
|
||||
{label: "Done for Now", description: "Exit"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Always asks for confirmation before external writes
|
||||
- Generates helpful PR descriptions
|
||||
- Keeps team informed (if desired)
|
||||
- Suggests next productive action
|
||||
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
|
||||
267
commands/implementation:next.md
Normal file
267
commands/implementation:next.md
Normal file
@@ -0,0 +1,267 @@
|
||||
---
|
||||
description: Suggest smart next action based on task status, dependencies, and progress
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Next Action for: $1
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:work [issue-id]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Auto-detects issue from git branch if not provided
|
||||
- Auto-detects mode (start vs resume)
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
Analyzing task **$1** to suggest the optimal next action.
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
**NEVER** submit, post, or update anything to external PM systems without confirmation.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Task Details
|
||||
|
||||
Use **Linear MCP** to get:
|
||||
- Full issue details, status, labels
|
||||
- Complete checklist with all subtasks
|
||||
- Progress information
|
||||
- Any blockers or dependencies
|
||||
|
||||
### Step 2: Analyze Current State
|
||||
|
||||
```javascript
|
||||
const state = {
|
||||
status: issue.status,
|
||||
progress: {
|
||||
total: checklist.length,
|
||||
completed: checklist.filter(i => i.checked).length,
|
||||
inProgress: checklist.filter(i => i.status === 'in_progress').length,
|
||||
blocked: checklist.filter(i => i.status === 'blocked').length
|
||||
},
|
||||
isBlocked: issue.labels.includes('blocked'),
|
||||
timeInStatus: calculateDuration(issue.statusUpdatedAt, now)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Determine Next Action
|
||||
|
||||
**Logic**:
|
||||
|
||||
```javascript
|
||||
function determineNextAction(state) {
|
||||
// If blocked
|
||||
if (state.isBlocked) {
|
||||
return {
|
||||
action: 'fix-blockers',
|
||||
command: `/ccpm:verification:fix ${issueId}`,
|
||||
reason: 'Task is blocked. Fix issues before continuing.'
|
||||
}
|
||||
}
|
||||
|
||||
// If status is Planning
|
||||
if (state.status === 'Planning') {
|
||||
return {
|
||||
action: 'start-implementation',
|
||||
command: `/ccpm:implementation:start ${issueId}`,
|
||||
reason: 'Planning complete. Ready to start implementation.'
|
||||
}
|
||||
}
|
||||
|
||||
// If status is In Progress
|
||||
if (state.status === 'In Progress') {
|
||||
// All tasks complete
|
||||
if (state.progress.completed === state.progress.total) {
|
||||
return {
|
||||
action: 'quality-checks',
|
||||
command: `/ccpm:verification:check ${issueId}`,
|
||||
reason: 'All subtasks complete. Run quality checks.'
|
||||
}
|
||||
}
|
||||
|
||||
// Check for next ready task (respecting dependencies)
|
||||
const nextTask = findNextReadyTask(checklist)
|
||||
if (nextTask) {
|
||||
return {
|
||||
action: 'work-on-subtask',
|
||||
subtask: nextTask,
|
||||
command: `Work on: ${nextTask.description}`,
|
||||
reason: `Next ready subtask (${nextTask.index + 1}/${state.progress.total})`
|
||||
}
|
||||
}
|
||||
|
||||
// Has in-progress task
|
||||
if (state.progress.inProgress > 0) {
|
||||
return {
|
||||
action: 'continue-current',
|
||||
command: `/ccpm:utils:context ${issueId}`,
|
||||
reason: 'Continue working on in-progress subtask.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If status is Verification
|
||||
if (state.status === 'Verification') {
|
||||
return {
|
||||
action: 'run-verification',
|
||||
command: `/ccpm:verification:verify ${issueId}`,
|
||||
reason: 'Ready for final verification.'
|
||||
}
|
||||
}
|
||||
|
||||
// If status is Done
|
||||
if (state.status === 'Done') {
|
||||
return {
|
||||
action: 'finalize',
|
||||
command: `/ccpm:complete:finalize ${issueId}`,
|
||||
reason: 'Task complete. Finalize and sync.'
|
||||
}
|
||||
}
|
||||
|
||||
// Default
|
||||
return {
|
||||
action: 'check-status',
|
||||
command: `/ccpm:utils:status ${issueId}`,
|
||||
reason: 'Review current status to decide next step.'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Check Dependencies
|
||||
|
||||
For "In Progress" tasks, check dependencies:
|
||||
|
||||
```markdown
|
||||
Parse checklist items for dependency markers:
|
||||
- "depends on: X" or "(depends: X)" or "(after: X)"
|
||||
- Extract dependency index/description
|
||||
- Check if dependency is complete
|
||||
- Only suggest tasks with all dependencies met
|
||||
```
|
||||
|
||||
###Step 5: Display Analysis
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Next Action for: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📊 Current Status: [status]
|
||||
🎯 Progress: [X/Y] subtasks ([%]%)
|
||||
⏱️ Time in status: [duration]
|
||||
🏷️ Labels: [labels]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Recommended Next Action
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Action: [action type]
|
||||
Why: [reason]
|
||||
Command: [suggested command]
|
||||
|
||||
[If subtask work recommended:]
|
||||
📝 Next Subtask: [index]/[total]
|
||||
Description: [subtask description]
|
||||
Dependencies: [All met ✅ / Waiting on: X]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 6: Interactive Choice
|
||||
|
||||
**READ**: ``$CCPM_COMMANDS_DIR/_shared-linear-helpers.md``
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Ready to proceed with the recommended action?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, Proceed",
|
||||
description: suggestedAction.reason
|
||||
},
|
||||
{
|
||||
label: "Show All Options",
|
||||
description: "See all available actions for this task"
|
||||
},
|
||||
{
|
||||
label: "Load Context First",
|
||||
description: "Load full task context before deciding"
|
||||
},
|
||||
{
|
||||
label: "Just Status",
|
||||
description: "Just show current status, I'll decide"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute based on choice**:
|
||||
- "Yes, Proceed" → Execute suggested command
|
||||
- "Show All Options" → Display all possible next actions with pros/cons
|
||||
- "Load Context First" → Run `/ccpm:utils:context $1`
|
||||
- "Just Status" → Run `/ccpm:utils:status $1`
|
||||
- "Other" → Exit gracefully
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Status: /ccpm:utils:status $1
|
||||
Context: /ccpm:utils:context $1
|
||||
Update: /ccpm:implementation:update $1 <idx> <status> "msg"
|
||||
Report: /ccpm:utils:report [project]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### Smart Detection
|
||||
|
||||
- ✅ Respects task dependencies
|
||||
- ✅ Detects blockers automatically
|
||||
- ✅ Suggests quality checks when ready
|
||||
- ✅ Identifies next ready subtask
|
||||
- ✅ Considers time in status
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Quick decision helper
|
||||
/ccpm:implementation:next WORK-123
|
||||
|
||||
# After completing a subtask
|
||||
/ccpm:implementation:next WORK-123
|
||||
|
||||
# When resuming work
|
||||
/ccpm:implementation:next WORK-123
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- ⚡ **Fast** - Instant recommendation
|
||||
- 🎯 **Smart** - Considers all factors
|
||||
- 📋 **Clear** - Explains reasoning
|
||||
- 🤖 **Interactive** - One-click execution
|
||||
- 🔄 **Context-aware** - Understands workflow
|
||||
634
commands/implementation:start.md
Normal file
634
commands/implementation:start.md
Normal file
@@ -0,0 +1,634 @@
|
||||
---
|
||||
description: Start implementation - fetch task, list agents, assign subtasks, coordinate parallel work
|
||||
allowed-tools: [Bash, LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Starting Implementation: $1
|
||||
|
||||
You are beginning the **Implementation Phase** for Linear issue **$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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Implementation Workflow
|
||||
|
||||
### Step 1: Fetch Task Details from Linear
|
||||
|
||||
Use **Linear MCP** to:
|
||||
1. Get issue details for: $1
|
||||
2. Read the full description
|
||||
3. Extract the checklist
|
||||
4. Understand all requirements
|
||||
|
||||
Display the task summary:
|
||||
```
|
||||
📋 Task: [Title]
|
||||
Project: [Project name]
|
||||
Status: [Current status]
|
||||
|
||||
Checklist Items:
|
||||
- [ ] Item 1
|
||||
- [ ] Item 2
|
||||
...
|
||||
```
|
||||
|
||||
### Step 1.5: Prepare Visual Context for UI/Design Tasks
|
||||
|
||||
**READ**: `commands/_shared-image-analysis.md`
|
||||
|
||||
Detect UI/design subtasks and prepare visual references for pixel-perfect implementation:
|
||||
|
||||
```javascript
|
||||
// 1. Extract all subtasks from checklist
|
||||
const subtasks = extractChecklistItems(issue.description)
|
||||
|
||||
// 2. Detect UI/design work using keywords
|
||||
const uiKeywords = /\b(UI|design|mockup|screen|component|layout|interface|visual|frontend|styling|theme)\b/i
|
||||
|
||||
const uiTasks = []
|
||||
for (const [index, subtask] of subtasks.entries()) {
|
||||
if (uiKeywords.test(subtask.description)) {
|
||||
uiTasks.push({ index, description: subtask.description })
|
||||
}
|
||||
}
|
||||
|
||||
// 3. If UI tasks found, detect and prepare images
|
||||
if (uiTasks.length > 0) {
|
||||
console.log(`🎨 Detected ${uiTasks.length} UI/design subtask(s)`)
|
||||
|
||||
const images = detectImages(issue)
|
||||
if (images.length > 0) {
|
||||
console.log(`📎 Found ${images.length} image(s) for visual reference`)
|
||||
|
||||
// Map images to relevant subtasks
|
||||
const visualContext = {}
|
||||
for (const task of uiTasks) {
|
||||
// Match images to subtasks by keyword overlap
|
||||
const relevantImages = images.filter(img =>
|
||||
// Check if image title/description relates to subtask
|
||||
task.description.toLowerCase().includes(img.title.toLowerCase().split('.')[0]) ||
|
||||
img.title.toLowerCase().includes('mockup') ||
|
||||
img.title.toLowerCase().includes('design') ||
|
||||
img.title.toLowerCase().includes('wireframe')
|
||||
)
|
||||
|
||||
if (relevantImages.length > 0) {
|
||||
visualContext[task.index] = relevantImages
|
||||
} else {
|
||||
// If no specific match, use all UI-related images
|
||||
visualContext[task.index] = images.filter(img =>
|
||||
/(mockup|design|wireframe|ui|screen|interface)/i.test(img.title)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
console.log("✅ Visual context prepared for UI tasks")
|
||||
// Store visualContext for use in Step 5
|
||||
} else {
|
||||
console.log("⚠️ No images found - will implement from text descriptions")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Matters**:
|
||||
- **Pixel-perfect implementation**: Frontend/mobile agents see the exact design mockup
|
||||
- **No information loss**: Direct visual reference vs. lossy text translation
|
||||
- **Design fidelity**: ~95-100% accuracy (vs. ~70-80% from text descriptions)
|
||||
- **Faster implementation**: No interpretation needed, implement directly from mockup
|
||||
|
||||
**Note**: Images were preserved in planning phase (Subtask 4) specifically for this step.
|
||||
|
||||
### Step 1.6: Load Figma Design Context
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
For UI/design tasks, check if Figma design system was extracted during planning phase:
|
||||
|
||||
```javascript
|
||||
// 1. Check Linear description for Figma design system section
|
||||
const hasFigmaContext = issue.description.includes("## 🎨 Design System Analysis")
|
||||
|
||||
if (hasFigmaContext && uiTasks.length > 0) {
|
||||
console.log("🎨 Figma design system found in planning")
|
||||
|
||||
// 2. Extract design system data from Linear description
|
||||
// The design system includes:
|
||||
// - Color palette with Tailwind mappings
|
||||
// - Typography with font families
|
||||
// - Spacing scale with Tailwind utilities
|
||||
// - Component library
|
||||
// - Layout patterns
|
||||
|
||||
// 3. Also check for cached design data in Linear comments
|
||||
const designSystemComment = issue.comments?.find(c =>
|
||||
c.body.includes("🎨 Figma Design Context") ||
|
||||
c.body.includes("Design System Analysis")
|
||||
)
|
||||
|
||||
if (designSystemComment) {
|
||||
console.log(" ✓ Design tokens extracted")
|
||||
console.log(" ✓ Tailwind class mappings available")
|
||||
console.log(" ✓ Component patterns documented")
|
||||
console.log("")
|
||||
console.log("💡 Frontend agents will receive:")
|
||||
console.log(" • Exact color hex codes → Tailwind classes")
|
||||
console.log(" • Font family mappings")
|
||||
console.log(" • Spacing values → Tailwind scale")
|
||||
console.log(" • Layout patterns (flex, grid, auto-layout)")
|
||||
console.log(" • Component structure and hierarchy")
|
||||
}
|
||||
|
||||
// 4. Store for passing to frontend/mobile agents
|
||||
const figmaContext = {
|
||||
hasDesignSystem: true,
|
||||
designSystemMarkdown: extractFigmaSection(issue.description),
|
||||
cacheComment: designSystemComment?.body
|
||||
}
|
||||
|
||||
console.log("✅ Figma design context loaded for implementation")
|
||||
} else if (uiTasks.length > 0) {
|
||||
console.log("ℹ️ No Figma design system - using static images only")
|
||||
}
|
||||
```
|
||||
|
||||
**What agents receive**:
|
||||
- **Frontend Developer**: Figma design tokens + Tailwind mappings + component structure
|
||||
- **Mobile Developer**: Same design system adapted for React Native
|
||||
- **UI Designer**: Component library and pattern documentation
|
||||
|
||||
**Benefits**:
|
||||
- **Consistency**: Exact color/font/spacing values from design system
|
||||
- **Efficiency**: No manual color picking or spacing guessing
|
||||
- **Quality**: Design system compliance guaranteed
|
||||
- **Speed**: Pre-mapped Tailwind classes ready to use
|
||||
|
||||
**Fallback**: If no Figma context, agents use static images + text descriptions.
|
||||
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:work [issue-id]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Auto-detects issue from git branch if not provided
|
||||
- Auto-detects mode (start vs resume)
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
### Step 2: List Available Subagents
|
||||
|
||||
Read the **CLAUDE.md** file in the project root to get subagent definitions.
|
||||
|
||||
Display available agents:
|
||||
```
|
||||
🤖 Available Subagents:
|
||||
|
||||
1. frontend-agent
|
||||
- Capabilities: React/Vue, UI/UX, styling
|
||||
- Use for: UI components, frontend features
|
||||
|
||||
2. backend-agent
|
||||
- Capabilities: APIs, database, auth
|
||||
- Use for: Server logic, endpoints
|
||||
|
||||
3. mobile-agent
|
||||
- Capabilities: React Native, iOS/Android
|
||||
- Use for: Mobile development
|
||||
|
||||
4. integration-agent
|
||||
- Capabilities: API integration, third-party services
|
||||
- Use for: Connecting systems
|
||||
|
||||
5. verification-agent
|
||||
- Capabilities: Testing, QA, code review
|
||||
- Use for: Final verification
|
||||
|
||||
6. devops-agent
|
||||
- Capabilities: CI/CD, deployment
|
||||
- Use for: Infrastructure tasks
|
||||
|
||||
[Add more agents as defined in CLAUDE.md]
|
||||
```
|
||||
|
||||
### Step 3: Create Assignment Plan
|
||||
|
||||
For each checklist item, determine:
|
||||
1. **Which agent** is best suited for the task
|
||||
2. **Dependencies** between subtasks
|
||||
3. **Parallel execution** opportunities
|
||||
|
||||
Create an assignment map:
|
||||
```
|
||||
📝 Assignment Plan:
|
||||
|
||||
✅ Group 1 (Run First):
|
||||
- [ ] Subtask 1 → database-agent
|
||||
|
||||
✅ Group 2 (After Group 1):
|
||||
- [ ] Subtask 2 → backend-agent
|
||||
|
||||
✅ Group 3 (Parallel, after Group 2):
|
||||
- [ ] Subtask 3 → frontend-agent (parallel)
|
||||
- [ ] Subtask 4 → mobile-agent (parallel)
|
||||
|
||||
✅ Group 4 (After Group 3):
|
||||
- [ ] Subtask 5 → integration-agent
|
||||
|
||||
✅ Group 5 (Final):
|
||||
- [ ] Subtask 6 → verification-agent
|
||||
```
|
||||
|
||||
### Step 4: Update Linear
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
Use **Linear MCP** to update issue status and labels:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// Get team ID from issue
|
||||
const teamId = issue.team.id;
|
||||
|
||||
// Step 4a: Get valid "In Progress" state ID
|
||||
const inProgressStateId = await getValidStateId(teamId, "In Progress");
|
||||
|
||||
// Step 4b: Get or create "implementation" label
|
||||
const implementationLabel = await getOrCreateLabel(teamId, "implementation", {
|
||||
color: "#26b5ce",
|
||||
description: "CCPM: Task in implementation phase"
|
||||
});
|
||||
|
||||
// Step 4c: Get current labels and remove "planning" if present
|
||||
const currentLabels = issue.labels || [];
|
||||
const currentLabelIds = currentLabels.map(l => l.id);
|
||||
|
||||
// Find planning label ID to remove
|
||||
const planningLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "planning"
|
||||
);
|
||||
|
||||
// Build new label list: remove planning, add implementation
|
||||
let newLabelIds = currentLabelIds.filter(id =>
|
||||
id !== planningLabel?.id
|
||||
);
|
||||
|
||||
// Add implementation label if not already present
|
||||
if (!currentLabels.some(l => l.name.toLowerCase() === "implementation")) {
|
||||
newLabelIds.push(implementationLabel.id);
|
||||
}
|
||||
|
||||
// Step 4d: Update issue with new status and labels
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "update_issue",
|
||||
args: {
|
||||
id: issue.id,
|
||||
stateId: inProgressStateId,
|
||||
labelIds: newLabelIds
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Linear issue updated:");
|
||||
console.log(" Status: In Progress");
|
||||
console.log(" Labels: implementation (planning removed)");
|
||||
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to update Linear issue:", error.message);
|
||||
console.warn("⚠️ Continuing with implementation, but status/labels may not be updated.");
|
||||
console.log(" You can manually update status in Linear if needed.");
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4e: Add comment with assignment plan**:
|
||||
|
||||
```javascript
|
||||
const commentBody = `## 🚀 Implementation Started
|
||||
|
||||
### Agent Assignments:
|
||||
${assignmentPlan.map(group =>
|
||||
group.subtasks.map(st =>
|
||||
`- ${st.description} → ${st.agent}`
|
||||
).join('\n')
|
||||
).join('\n\n')}
|
||||
|
||||
### Execution Strategy:
|
||||
${assignmentPlan.map((group, idx) =>
|
||||
`- Group ${idx + 1}: ${group.parallel ? 'Parallel execution' : 'Sequential execution'}`
|
||||
).join('\n')}
|
||||
`;
|
||||
|
||||
try {
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "create_comment",
|
||||
args: {
|
||||
issueId: issue.id,
|
||||
body: commentBody
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Implementation plan added to Linear comments");
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to add comment:", error.message);
|
||||
// Not critical, continue
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Begin Execution
|
||||
|
||||
Now you're ready to invoke subagents!
|
||||
|
||||
**For each subtask**:
|
||||
1. Invoke the assigned agent with full context
|
||||
2. Provide clear success criteria
|
||||
3. After completion, use `/update` command
|
||||
|
||||
## Execution Guidelines
|
||||
|
||||
### Invoking Subagents
|
||||
|
||||
When invoking a subagent, always provide:
|
||||
|
||||
**Context**:
|
||||
- Full task description from Linear
|
||||
- Specific subtask requirements
|
||||
- Related code files to modify
|
||||
- Patterns to follow (from CLAUDE.md)
|
||||
|
||||
**Success Criteria**:
|
||||
- What "done" looks like
|
||||
- Testing requirements
|
||||
- Performance/security considerations
|
||||
|
||||
**Example invocation**:
|
||||
```
|
||||
Invoke backend-agent to implement authentication endpoints:
|
||||
|
||||
Context:
|
||||
- Linear issue: $1
|
||||
- Subtask: "Implement JWT authentication endpoints"
|
||||
- Files to modify: src/api/auth.ts, src/middleware/auth.ts
|
||||
|
||||
Requirements:
|
||||
- POST /api/auth/login - JWT authentication
|
||||
- POST /api/auth/logout - Token invalidation
|
||||
- POST /api/auth/refresh - Token refresh
|
||||
- Rate limiting: 5 requests/minute
|
||||
- Follow patterns in src/api/users.ts
|
||||
|
||||
Success Criteria:
|
||||
- All endpoints functional
|
||||
- Tests pass
|
||||
- No linting errors
|
||||
- Security best practices followed
|
||||
```
|
||||
|
||||
### Invoking Frontend/Mobile Agents with Visual References
|
||||
|
||||
**CRITICAL for UI/Design Tasks**: When invoking frontend-developer or mobile-developer agents for UI/design subtasks:
|
||||
|
||||
**If visual context exists** (from Step 1.5):
|
||||
```
|
||||
Invoke frontend-developer to implement [UI component]:
|
||||
|
||||
**Design Mockup** (view directly):
|
||||
- mockup-name.png: https://linear.app/attachments/[url]
|
||||
|
||||
Load the mockup using WebFetch:
|
||||
[Agent will automatically see the image via WebFetch tool]
|
||||
|
||||
**Implementation Requirements**:
|
||||
- Match EXACT layout and spacing from mockup above
|
||||
- Extract and use exact colors (hex values from mockup)
|
||||
- Match typography: font sizes, weights, line heights from mockup
|
||||
- Implement component hierarchy shown in mockup
|
||||
- Responsive breakpoints visible in mockup
|
||||
|
||||
**Available Components** (from codebase):
|
||||
[List reusable components found during planning]
|
||||
|
||||
**Success Criteria**:
|
||||
- Pixel-perfect match to mockup (~95-100% fidelity)
|
||||
- All colors extracted from mockup and used correctly
|
||||
- Spacing and layout matches mockup measurements
|
||||
- Component structure follows mockup hierarchy
|
||||
- Works on all target devices shown in mockup
|
||||
|
||||
**DO NOT** rely on text descriptions. Implement directly from the visual mockup loaded above.
|
||||
```
|
||||
|
||||
**Example invocation with mockup**:
|
||||
```
|
||||
Invoke frontend-developer to implement login screen UI:
|
||||
|
||||
Context:
|
||||
- Linear issue: WORK-123
|
||||
- Subtask: "Implement login screen UI component"
|
||||
- Files to create/modify: src/components/Auth/LoginScreen.tsx
|
||||
|
||||
**Design Mockup** (view directly):
|
||||
- login-mockup.png: https://linear.app/attachments/abc123/login-mockup.png
|
||||
|
||||
Use WebFetch to load and view the mockup above. Implement the login screen to match the mockup exactly.
|
||||
|
||||
Implementation Requirements:
|
||||
- Extract exact colors from mockup (primary blue, backgrounds, text colors)
|
||||
- Match spacing and padding shown in mockup
|
||||
- Implement form layout as shown (centered card, input fields, button)
|
||||
- Typography: Match font sizes and weights from mockup
|
||||
- Use available components: Card, Input, Button, Link
|
||||
|
||||
Success Criteria:
|
||||
- Pixel-perfect implementation matching mockup
|
||||
- All interactive elements functional
|
||||
- Responsive design matches mockup behavior
|
||||
- Accessibility: proper labels, keyboard navigation
|
||||
- Tests pass for all functionality
|
||||
|
||||
DO NOT interpret or guess the design. Implement directly from the visual mockup above.
|
||||
```
|
||||
|
||||
**Benefits of Direct Visual Reference**:
|
||||
- **Eliminates translation loss**: No text interpretation needed
|
||||
- **Exact design fidelity**: ~95-100% accuracy vs. ~70-80% from text
|
||||
- **Faster implementation**: No back-and-forth clarifications
|
||||
- **Pixel-perfect results**: Agents measure directly from mockup
|
||||
- **Color accuracy**: Extract exact hex values from image
|
||||
- **Layout precision**: Measure spacing and dimensions from mockup
|
||||
|
||||
**Fallback**: If no mockups available, proceed with text description as before.
|
||||
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
For subtasks that can run in parallel:
|
||||
1. Invoke all agents simultaneously
|
||||
2. Each works independently
|
||||
3. Wait for all to complete before moving to next group
|
||||
|
||||
### Status Updates
|
||||
|
||||
After EACH subtask completion:
|
||||
```
|
||||
/update $1 <subtask-index> completed "<summary of what was done>"
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
After displaying the assignment plan:
|
||||
|
||||
1. **Start with Group 1** - Invoke first agent(s)
|
||||
2. **Update after each subtask** - Use `/update` command
|
||||
3. **Move through groups sequentially** (except parallel groups)
|
||||
4. **After all subtasks done** - Run `/check $1`
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
✅ Implementation Started!
|
||||
|
||||
📋 Task: [Title]
|
||||
🔗 Linear: https://linear.app/workspace/issue/$1
|
||||
|
||||
🤖 Agent Assignments Created
|
||||
📝 Execution plan in Linear comments
|
||||
|
||||
⚡ Ready to Execute!
|
||||
|
||||
Next: Invoking [first-agent] for Subtask 1...
|
||||
[Then actually invoke the agent]
|
||||
```
|
||||
|
||||
## Remember
|
||||
|
||||
- Provide full context to each subagent
|
||||
- Update Linear after each subtask
|
||||
- Execute parallel tasks simultaneously when possible
|
||||
- Follow patterns defined in CLAUDE.md
|
||||
- Run quality checks before verification
|
||||
### Step 1.6: Load Figma Design Context
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
If Figma links were detected during planning, load them for implementation:
|
||||
|
||||
```bash
|
||||
# Check if Figma context exists from planning phase
|
||||
FIGMA_CONTEXT_FILE="/tmp/figma-context-${1}.json"
|
||||
|
||||
if [ -f "$FIGMA_CONTEXT_FILE" ]; then
|
||||
FIGMA_LINKS=$(cat "$FIGMA_CONTEXT_FILE")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -gt 0 ]; then
|
||||
echo "🎨 Loaded $FIGMA_COUNT Figma design(s) from planning phase"
|
||||
|
||||
# Display Figma context
|
||||
echo "$FIGMA_LINKS" | jq -r '.[] | " - \(.file_name): \(.canonical_url)"'
|
||||
|
||||
# Store for agent context
|
||||
FIGMA_AVAILABLE=true
|
||||
fi
|
||||
else
|
||||
# Try to detect from Linear issue directly
|
||||
echo "ℹ️ No cached Figma context - checking Linear issue..."
|
||||
LINEAR_DESC=$(linear_get_issue "$1" | jq -r '.description')
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -gt 0 ]; then
|
||||
echo "✅ Detected $FIGMA_COUNT Figma link(s) from Linear"
|
||||
FIGMA_AVAILABLE=true
|
||||
else
|
||||
echo "ℹ️ No Figma designs found"
|
||||
FIGMA_AVAILABLE=false
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
**Map Figma Designs to UI Subtasks**
|
||||
|
||||
For UI/design subtasks identified in Step 1.5, map relevant Figma links:
|
||||
|
||||
```javascript
|
||||
if (figmaAvailable && uiTasks.length > 0) {
|
||||
console.log("🎨 Mapping Figma designs to UI subtasks...")
|
||||
|
||||
const figmaContext = {}
|
||||
for (const task of uiTasks) {
|
||||
// Match Figma designs to subtasks by keyword overlap
|
||||
const relevantDesigns = figmaLinks.filter(design =>
|
||||
task.description.toLowerCase().includes(design.file_name.toLowerCase()) ||
|
||||
design.file_name.toLowerCase().includes('ui') ||
|
||||
design.file_name.toLowerCase().includes('design') ||
|
||||
design.file_name.toLowerCase().includes('mockup')
|
||||
)
|
||||
|
||||
if (relevantDesigns.length > 0) {
|
||||
figmaContext[task.index] = relevantDesigns
|
||||
console.log(` ✅ Subtask ${task.index + 1}: ${relevantDesigns.length} Figma design(s)`)
|
||||
} else {
|
||||
// Use all Figma links as fallback
|
||||
figmaContext[task.index] = figmaLinks
|
||||
console.log(` ℹ️ Subtask ${task.index + 1}: Using all Figma designs`)
|
||||
}
|
||||
}
|
||||
|
||||
console.log("✅ Figma context mapped for pixel-perfect implementation")
|
||||
// Store figmaContext for use in Step 5 (agent invocation)
|
||||
}
|
||||
```
|
||||
|
||||
**Agent Context Enhancement**
|
||||
|
||||
When invoking frontend/mobile agents for UI tasks:
|
||||
|
||||
```javascript
|
||||
// In Step 5: Assign Subtasks to Agents
|
||||
if (subtask.hasVisualContext) {
|
||||
const images = visualContext[subtask.index] || []
|
||||
const figma = figmaContext[subtask.index] || []
|
||||
|
||||
agentPrompt += `
|
||||
|
||||
**Visual References**:
|
||||
- Images: ${images.length} screenshot(s)/mockup(s) attached
|
||||
${images.map((img, i) => ` ${i+1}. ${img.title}: ${img.url}`).join('\n')}
|
||||
|
||||
- Figma Designs: ${figma.length} live design(s) available
|
||||
${figma.map((design, i) => ` ${i+1}. ${design.file_name}: ${design.canonical_url}`).join('\n')}
|
||||
|
||||
**Implementation Priority**:
|
||||
1. Use Figma as authoritative design source (live, up-to-date)
|
||||
2. Use images for quick visual reference
|
||||
3. Implement pixel-perfect from Figma specifications
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Matters**:
|
||||
- **Authoritative Source**: Figma is the live design specification
|
||||
- **Always Current**: Unlike static images, Figma shows latest design iterations
|
||||
- **Precise Specifications**: Access to exact measurements, colors, spacing
|
||||
- **Component Mapping**: Direct reference for component implementation
|
||||
- **Design Fidelity**: ~98-100% accuracy (vs. ~95% from images, ~70% from text)
|
||||
|
||||
**Performance**: Loading Figma context adds <100ms (file read). Phase 2 will enable on-demand MCP extraction (~1-3s per design).
|
||||
|
||||
**Error Handling**: If Figma links unavailable, fall back to images → text descriptions (graceful degradation).
|
||||
|
||||
777
commands/implementation:sync.md
Normal file
777
commands/implementation:sync.md
Normal file
@@ -0,0 +1,777 @@
|
||||
---
|
||||
description: Sync implementation progress, findings, and changes to Linear for full context
|
||||
allowed-tools: [Bash, LinearMCP, Read, Glob, Grep]
|
||||
argument-hint: <linear-issue-id> [optional-summary]
|
||||
---
|
||||
|
||||
# Syncing Implementation Progress: $1
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:sync [issue-id] [summary]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Auto-detects issue from git branch if not provided
|
||||
- Auto-generates summary from git changes
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
Syncing all implementation progress, code changes, technical findings, and blockers to Linear issue **$1** so you have full context to continue later.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Sync Workflow
|
||||
|
||||
### Step 1: Detect Changes Since Last Sync
|
||||
|
||||
Use **Bash** to gather git information:
|
||||
|
||||
```bash
|
||||
# Get current branch
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
|
||||
# Get changed files (staged + unstaged)
|
||||
git status --porcelain
|
||||
|
||||
# Get commit history since last sync (look for sync marker in Linear comments)
|
||||
git log --oneline -10
|
||||
|
||||
# Get detailed diff summary
|
||||
git diff --stat HEAD
|
||||
git diff --cached --stat
|
||||
```
|
||||
|
||||
**Parse output to identify:**
|
||||
- Modified files (M)
|
||||
- New files (A, ??)
|
||||
- Deleted files (D)
|
||||
- Renamed files (R)
|
||||
- Number of insertions/deletions per file
|
||||
|
||||
### Step 2: Fetch Linear Issue Context
|
||||
|
||||
Use **Linear MCP** to:
|
||||
|
||||
1. Get full issue details (title, description, status, labels)
|
||||
2. Get all comments to find last sync timestamp
|
||||
3. Check for existing "Implementation Notes" section in description
|
||||
|
||||
**Look for last sync:**
|
||||
```
|
||||
Search for comments matching pattern: "## 🔄 Progress Sync"
|
||||
Extract timestamp from most recent sync comment
|
||||
```
|
||||
|
||||
### Step 3: Analyze Code Changes
|
||||
|
||||
For each changed file:
|
||||
|
||||
1. **Read file** (if <500 lines, otherwise read relevant sections)
|
||||
2. **Get git diff** for the file
|
||||
3. **Categorize change**:
|
||||
- New feature code
|
||||
- Bug fix
|
||||
- Refactoring
|
||||
- Test file
|
||||
- Configuration
|
||||
- Documentation
|
||||
|
||||
4. **Extract key information**:
|
||||
- New functions/classes added
|
||||
- Modified APIs
|
||||
- Dependencies added/removed
|
||||
- TODO/FIXME comments
|
||||
|
||||
Use **Grep** to find:
|
||||
- TODO comments: `grep -r "TODO" --include="*.{js,ts,tsx,jsx}"`
|
||||
- FIXME comments: `grep -r "FIXME" --include="*.{js,ts,tsx,jsx}"`
|
||||
- New imports: Look for added import statements in diffs
|
||||
|
||||
### Step 4: Interactive Review & Add Notes
|
||||
|
||||
Display detected changes in a clear format:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Detected Changes
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📝 Modified Files (3):
|
||||
1. src/api/auth.ts (+45, -12)
|
||||
• Added JWT token validation
|
||||
• New function: validateToken()
|
||||
|
||||
2. src/components/Login.tsx (+23, -8)
|
||||
• Updated login form UI
|
||||
• Added error handling
|
||||
|
||||
3. src/tests/auth.test.ts (+67, -0)
|
||||
• New test file
|
||||
• 12 test cases added
|
||||
|
||||
➕ New Files (2):
|
||||
4. src/middleware/jwt.ts (+89, -0)
|
||||
• JWT middleware implementation
|
||||
|
||||
5. src/types/auth.d.ts (+23, -0)
|
||||
• Auth type definitions
|
||||
|
||||
📊 Summary:
|
||||
• 5 files changed
|
||||
• +247 insertions, -20 deletions
|
||||
• 2 new files created
|
||||
• Branch: feature/auth-implementation
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**Then use AskUserQuestion** to gather additional context:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Any technical decisions or findings to document?",
|
||||
header: "Tech Notes",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, let me add notes",
|
||||
description: "I'll provide technical insights and decisions made"
|
||||
},
|
||||
{
|
||||
label: "No, changes are self-explanatory",
|
||||
description: "Code changes speak for themselves"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Did you encounter any blockers or challenges?",
|
||||
header: "Blockers",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, had blockers (resolved)",
|
||||
description: "Encountered issues but managed to resolve them"
|
||||
},
|
||||
{
|
||||
label: "Yes, still blocked",
|
||||
description: "Currently blocked and need help"
|
||||
},
|
||||
{
|
||||
label: "No blockers",
|
||||
description: "Everything went smoothly"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Any test results or quality metrics to include?",
|
||||
header: "Quality",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, ran tests",
|
||||
description: "I have test results to share"
|
||||
},
|
||||
{
|
||||
label: "Tests pending",
|
||||
description: "Haven't run tests yet"
|
||||
},
|
||||
{
|
||||
label: "No tests needed",
|
||||
description: "Not applicable for this change"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**If user wants to add technical notes**, prompt for free-form text input:
|
||||
```
|
||||
Please provide your technical notes (press Enter twice when done):
|
||||
> [User types their notes]
|
||||
```
|
||||
|
||||
**If user has blockers**, prompt for details:
|
||||
```
|
||||
Please describe the blocker and what's needed to unblock:
|
||||
> [User types blocker details]
|
||||
```
|
||||
|
||||
**If user has test results**, prompt for summary:
|
||||
```
|
||||
Please share test results (coverage %, passing/failing tests, etc.):
|
||||
> [User types test results]
|
||||
```
|
||||
|
||||
### Step 5: Build Progress Report
|
||||
|
||||
Combine all information into a comprehensive progress report:
|
||||
|
||||
```markdown
|
||||
## 🔄 Progress Sync
|
||||
|
||||
**Timestamp**: [Current date/time]
|
||||
**Branch**: [branch-name]
|
||||
**Synced by**: @[user-name]
|
||||
|
||||
### 📝 Code Changes
|
||||
|
||||
**Summary**: [X] files changed (+[insertions], -[deletions])
|
||||
|
||||
**Modified Files**:
|
||||
- `src/api/auth.ts` (+45, -12)
|
||||
- Added JWT token validation
|
||||
- New function: `validateToken()`
|
||||
|
||||
- `src/components/Login.tsx` (+23, -8)
|
||||
- Updated login form UI
|
||||
- Added error handling
|
||||
|
||||
**New Files**:
|
||||
- `src/middleware/jwt.ts` (+89, -0)
|
||||
- JWT middleware implementation
|
||||
|
||||
- `src/types/auth.d.ts` (+23, -0)
|
||||
- Auth type definitions
|
||||
|
||||
### 🧠 Technical Decisions & Findings
|
||||
|
||||
[User-provided technical notes, or auto-detected insights:]
|
||||
- Chose jsonwebtoken library over jose for broader Node.js compatibility
|
||||
- Implemented token refresh strategy using sliding window approach
|
||||
- Added rate limiting to prevent brute force attacks (100 req/15min per IP)
|
||||
|
||||
### 🚧 Blockers & Challenges
|
||||
|
||||
[If any blockers reported:]
|
||||
|
||||
**Resolved**:
|
||||
- ✅ Issue with bcrypt on Node 20 → Upgraded to bcrypt@5.1.0
|
||||
- ✅ CORS errors in dev → Added proper CORS middleware configuration
|
||||
|
||||
**Active Blockers**:
|
||||
- 🚫 [Blocker description]
|
||||
- What's needed: [User-provided info]
|
||||
- Status: Needs attention
|
||||
|
||||
### ✅ Test Results & Quality
|
||||
|
||||
[If test results provided:]
|
||||
- ✅ All 12 auth tests passing
|
||||
- ✅ Code coverage: 87% (target: 80%)
|
||||
- ✅ ESLint: No errors
|
||||
- ⚠️ TypeScript: 2 warnings (non-critical)
|
||||
|
||||
### 📋 Checklist Update
|
||||
|
||||
[Auto-update based on completed work:]
|
||||
- [x] ✅ Implement JWT authentication → Completed
|
||||
- [x] ✅ Add login form validation → Completed
|
||||
- [ ] ⏳ Integrate with frontend → In Progress (60%)
|
||||
- [ ] Add refresh token rotation → Next
|
||||
|
||||
### 🎯 Next Steps
|
||||
|
||||
Based on progress, suggested next actions:
|
||||
1. Continue with frontend integration
|
||||
2. Implement refresh token rotation
|
||||
3. Run end-to-end tests
|
||||
4. Update API documentation
|
||||
|
||||
---
|
||||
|
||||
**Full Diff**: Available in git history (`git diff [previous-sync-commit]..HEAD`)
|
||||
```
|
||||
|
||||
### Step 6: Update Checklist Items
|
||||
|
||||
**BEFORE syncing progress**, update the Implementation Checklist in the description:
|
||||
|
||||
**A) Parse Current Checklist**
|
||||
|
||||
Look for checklist in description using markers:
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1: Description
|
||||
- [x] Task 2: Description
|
||||
<!-- ccpm-checklist-end -->
|
||||
```
|
||||
|
||||
Or find "## ✅ Implementation Checklist" header.
|
||||
|
||||
**B) Display Current Checklist State**
|
||||
|
||||
Show incomplete items with their indices:
|
||||
```
|
||||
📋 Current Checklist Progress: X% (Y/Z completed)
|
||||
|
||||
Incomplete Items:
|
||||
0. [ ] Task 1: Create parser functions
|
||||
3. [ ] Task 4: Modify verification command
|
||||
5. [ ] Task 6: Add tests
|
||||
```
|
||||
|
||||
**C) AI-Powered Suggestion Analysis**
|
||||
|
||||
**BEFORE showing the checklist**, analyze git changes to suggest which items were likely completed:
|
||||
|
||||
**1. Get git diff summary:**
|
||||
```bash
|
||||
git diff --stat HEAD
|
||||
git diff HEAD --name-only
|
||||
```
|
||||
|
||||
**2. Extract changed files and their paths:**
|
||||
- Parse file paths from git output
|
||||
- Categorize by type: implementation, tests, config, docs
|
||||
- Extract key terms from paths (e.g., "auth", "login", "parser", "sync")
|
||||
|
||||
**3. For each unchecked checklist item:**
|
||||
|
||||
Parse the item description to extract key terms:
|
||||
```
|
||||
"Create checklist parser functions"
|
||||
→ Keywords: ["create", "checklist", "parser", "functions"]
|
||||
```
|
||||
|
||||
**4. Semantic Matching Algorithm:**
|
||||
|
||||
For each unchecked item, calculate a match score with git changes:
|
||||
|
||||
```javascript
|
||||
score = 0
|
||||
|
||||
// File path matching
|
||||
for each changed file:
|
||||
if file path contains any checklist item keyword:
|
||||
score += 30
|
||||
|
||||
// Type matching (e.g., "parser" → "parser.ts")
|
||||
if file name matches item description pattern:
|
||||
score += 40
|
||||
|
||||
// Content size matching (larger changes = more likely complete)
|
||||
if file has > 50 lines changed:
|
||||
score += 10
|
||||
if file has > 100 lines changed:
|
||||
score += 20
|
||||
|
||||
// Related file matching
|
||||
if multiple related files changed (e.g., impl + tests):
|
||||
score += 20
|
||||
|
||||
// Keyword frequency
|
||||
count keyword matches in file paths and diffs:
|
||||
score += (keyword_matches * 5)
|
||||
|
||||
// Confidence thresholds:
|
||||
// score >= 50: High confidence (pre-select)
|
||||
// score 30-49: Medium confidence (suggest but don't pre-select)
|
||||
// score < 30: Low confidence (don't suggest)
|
||||
```
|
||||
|
||||
**5. Build suggestions map:**
|
||||
```javascript
|
||||
suggestions = {
|
||||
highConfidence: [0, 2], // Pre-select these
|
||||
mediumConfidence: [4], // Mention but don't pre-select
|
||||
lowConfidence: [5, 6] // Don't mention
|
||||
}
|
||||
```
|
||||
|
||||
**D) Interactive Checklist Update with AI Suggestions**
|
||||
|
||||
Use **AskUserQuestion** with multi-select, with AI suggestions:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Which checklist items did you complete in this session? (Select all that apply)\n\n🤖 AI Suggestions based on git changes are pre-selected. Adjust as needed.",
|
||||
header: "Completed",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{
|
||||
label: "0: Create checklist parser functions",
|
||||
description: "🤖 SUGGESTED - High confidence (files: utils:update-checklist.md)",
|
||||
// This item is PRE-SELECTED based on AI analysis
|
||||
},
|
||||
{
|
||||
label: "2: Modify /ccpm:implementation:sync",
|
||||
description: "🤖 SUGGESTED - High confidence (files: implementation:sync.md)",
|
||||
// This item is PRE-SELECTED
|
||||
},
|
||||
{
|
||||
label: "4: Add tests",
|
||||
description: "💡 Possible match - Medium confidence (files: test-utils.ts)",
|
||||
// This item is NOT pre-selected, but mentioned as possibility
|
||||
},
|
||||
{
|
||||
label: "3: Modify verification command",
|
||||
description: "Mark as complete"
|
||||
// No suggestion for this item (low confidence)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Update checklist before syncing progress?",
|
||||
header: "Update Now",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, update checklist",
|
||||
description: "Update description with selected items"
|
||||
},
|
||||
{
|
||||
label: "Skip for now",
|
||||
description: "I'll update manually later"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Display suggestion reasoning:**
|
||||
|
||||
Show user why items were suggested:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🤖 AI Suggestions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
High Confidence (pre-selected):
|
||||
✅ 0: Create checklist parser functions
|
||||
Reason: Modified utils:update-checklist.md (+247 lines)
|
||||
Keywords matched: "checklist", "parser", "functions"
|
||||
|
||||
✅ 2: Modify /ccpm:implementation:sync
|
||||
Reason: Modified implementation:sync.md (+125 lines)
|
||||
Keywords matched: "implementation", "sync"
|
||||
|
||||
Medium Confidence (review):
|
||||
💡 4: Add tests
|
||||
Reason: Modified test-utils.ts (+45 lines)
|
||||
Keywords matched: "tests"
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Review the pre-selected items and adjust before confirming.
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**D) Update Description if Confirmed**
|
||||
|
||||
If user confirms update:
|
||||
|
||||
1. Parse selected indices from user response
|
||||
2. For each selected index:
|
||||
- Change `- [ ]` to `- [x]` in description
|
||||
3. Calculate new completion percentage
|
||||
4. Update progress line: `Progress: X% (Y/Z completed)`
|
||||
5. Add timestamp: `Last updated: [ISO timestamp]`
|
||||
|
||||
**Updated Format:**
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [x] Task 1: Create parser functions ← UPDATED!
|
||||
- [x] Task 2: Description
|
||||
- [ ] Task 3: Modify sync command
|
||||
- [x] Task 4: Modify verification ← UPDATED!
|
||||
<!-- ccpm-checklist-end -->
|
||||
|
||||
Progress: 75% (3/4 completed) ← UPDATED!
|
||||
Last updated: 2025-01-20T14:30:00Z
|
||||
```
|
||||
|
||||
### Step 7: Update Linear Issue
|
||||
|
||||
**A) Add Progress Comment**
|
||||
|
||||
Use **Linear MCP** to create comment with the progress report from Step 5.
|
||||
|
||||
Include checklist changes in the comment:
|
||||
```markdown
|
||||
## 🔄 Progress Sync
|
||||
|
||||
...existing progress report...
|
||||
|
||||
### 📋 Checklist Updated
|
||||
|
||||
**Progress**: X% → Y% (+Z%)
|
||||
|
||||
**Completed This Session**:
|
||||
- ✅ Task 1: Create parser functions
|
||||
- ✅ Task 4: Modify verification
|
||||
|
||||
**AI Suggestions Used**: 2/2 high confidence suggestions confirmed
|
||||
|
||||
**Timestamp**: [current date/time]
|
||||
```
|
||||
|
||||
**B) Update Issue Description**
|
||||
|
||||
1. Update description with modified checklist (from Step 6)
|
||||
2. Look for "## 📊 Implementation Notes" section
|
||||
3. If exists, append new entry
|
||||
4. If not exists, create section before checklist
|
||||
|
||||
**Format for description section:**
|
||||
|
||||
```markdown
|
||||
## 📊 Implementation Notes
|
||||
|
||||
<details>
|
||||
<summary>🔄 Latest Sync - [Date/Time] - [% Complete]</summary>
|
||||
|
||||
**Files Changed**: [X] files (+[insertions], -[deletions])
|
||||
**Status**: [Status summary]
|
||||
**Key Progress**: [Brief summary of main achievements]
|
||||
|
||||
See latest comment for full details.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>🔄 Previous Sync - [Date/Time] - [% Complete]</summary>
|
||||
[Previous sync summary...]
|
||||
</details>
|
||||
```
|
||||
|
||||
This keeps description clean while maintaining history.
|
||||
|
||||
**C) Update Labels if Needed**
|
||||
|
||||
- Add "blocked" label if active blockers reported
|
||||
- Remove "blocked" label if all blockers resolved
|
||||
- Add "needs-review" label if work is ready for review
|
||||
|
||||
### Step 8: Display Confirmation
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Progress Synced to Linear!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: $1
|
||||
🔗 Link: [Linear issue URL]
|
||||
|
||||
📝 Synced Information:
|
||||
✅ Code changes (5 files)
|
||||
✅ Technical decisions (3 notes)
|
||||
✅ Blockers (1 resolved, 0 active)
|
||||
✅ Test results (12 passing)
|
||||
✅ Checklist updated (2 items marked complete)
|
||||
|
||||
💬 Comment added with full details
|
||||
📊 Description updated with sync summary
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. ⭐ Continue Next Subtask
|
||||
/ccpm:implementation:next $1
|
||||
|
||||
2. Run Quality Checks
|
||||
/ccpm:verification:check $1
|
||||
|
||||
3. View Updated Status
|
||||
/ccpm:utils:status $1
|
||||
|
||||
4. Just review what was synced
|
||||
[Show Linear issue link]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
What would you like to do?
|
||||
```
|
||||
|
||||
Use **AskUserQuestion** to let user choose next action.
|
||||
|
||||
## Alternative: Manual Summary Mode
|
||||
|
||||
If user provided summary as second argument: `$2`
|
||||
|
||||
Skip interactive questions and git detection, just:
|
||||
1. Use provided summary text as the progress note
|
||||
2. Still fetch Linear issue for context
|
||||
3. Create simpler sync comment with manual summary
|
||||
4. Update description with summary
|
||||
5. Skip automatic checklist updates
|
||||
|
||||
**Format:**
|
||||
```markdown
|
||||
## 🔄 Progress Sync (Manual)
|
||||
|
||||
**Timestamp**: [Current date/time]
|
||||
**Summary**: $2
|
||||
|
||||
[Rest of standard format...]
|
||||
```
|
||||
|
||||
## Command Variants
|
||||
|
||||
### Quick Sync (No Interaction)
|
||||
|
||||
```bash
|
||||
# Quick sync with just summary
|
||||
/ccpm:implementation:sync WORK-123 "Completed auth implementation, all tests passing"
|
||||
```
|
||||
|
||||
### Full Interactive Sync
|
||||
|
||||
```bash
|
||||
# Full interactive mode with git detection
|
||||
/ccpm:implementation:sync WORK-123
|
||||
```
|
||||
|
||||
### Sync with Blocker
|
||||
|
||||
```bash
|
||||
# Sync and mark as blocked
|
||||
/ccpm:implementation:sync WORK-123 "Blocked: Need backend API endpoint deployed"
|
||||
# → Automatically adds "blocked" label
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Mid-Implementation Sync
|
||||
|
||||
```bash
|
||||
/ccpm:implementation:sync TRAIN-456
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
Detected 3 file changes:
|
||||
• src/components/Dashboard.tsx (modified)
|
||||
• src/hooks/useAuth.ts (new)
|
||||
• src/tests/dashboard.test.ts (new)
|
||||
|
||||
[Interactive prompts for notes, blockers, tests...]
|
||||
|
||||
✅ Synced to Linear!
|
||||
📋 2 checklist items updated
|
||||
💬 Progress comment added
|
||||
```
|
||||
|
||||
### Example 2: Quick Manual Sync
|
||||
|
||||
```bash
|
||||
/ccpm:implementation:sync TRAIN-456 "Refactored auth logic to use hooks pattern. Tests updated and passing. Ready for code review."
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
✅ Quick sync complete!
|
||||
💬 Comment added to Linear
|
||||
📊 Description updated
|
||||
```
|
||||
|
||||
### Example 3: End-of-Day Sync
|
||||
|
||||
```bash
|
||||
/ccpm:implementation:sync TRAIN-456
|
||||
```
|
||||
|
||||
User adds comprehensive notes:
|
||||
- Technical decisions made today
|
||||
- Challenges encountered and resolved
|
||||
- Tomorrow's plan
|
||||
- Test coverage status
|
||||
|
||||
→ Creates complete progress snapshot for next day
|
||||
|
||||
## Benefits
|
||||
|
||||
### 🎯 Full Context Capture
|
||||
- Never lose track of what you did
|
||||
- Document technical decisions in real-time
|
||||
- Track evolution of implementation approach
|
||||
|
||||
### 🔄 Easy Resume
|
||||
- Use with `/ccpm:utils:context` to quickly resume work
|
||||
- All progress and decisions documented
|
||||
- Clear picture of what's done and what's next
|
||||
|
||||
### 📊 Progress Visibility
|
||||
- Team can see progress without asking
|
||||
- Stakeholders get regular updates
|
||||
- Project managers have real-time status
|
||||
|
||||
### 🧠 Knowledge Retention
|
||||
- Technical decisions documented
|
||||
- Blockers and solutions recorded
|
||||
- Learning captured for future reference
|
||||
|
||||
### ⚡ Automation
|
||||
- Auto-detects code changes from git
|
||||
- Auto-updates checklist based on work done
|
||||
- Auto-adds relevant labels
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
**Before starting work:**
|
||||
```bash
|
||||
/ccpm:utils:context WORK-123 # Load context
|
||||
/ccpm:implementation:next WORK-123 # Get next task
|
||||
```
|
||||
|
||||
**During work (multiple times per day):**
|
||||
```bash
|
||||
/ccpm:implementation:sync WORK-123 # Sync progress
|
||||
```
|
||||
|
||||
**After work session:**
|
||||
```bash
|
||||
/ccpm:implementation:sync WORK-123 # Final sync
|
||||
/ccpm:verification:check WORK-123 # Run quality checks
|
||||
```
|
||||
|
||||
**Next day:**
|
||||
```bash
|
||||
/ccpm:utils:context WORK-123 # Resume with full context
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### When to Sync
|
||||
|
||||
- ✅ **End of work session** - Capture progress before stopping
|
||||
- ✅ **After major milestone** - Document completion of big chunks
|
||||
- ✅ **When blocked** - Record blocker for team awareness
|
||||
- ✅ **Before switching tasks** - Save state before context switch
|
||||
- ✅ **Mid-day checkpoints** - Capture progress on long tasks
|
||||
|
||||
### What Gets Synced
|
||||
|
||||
- ✅ All git changes (files, diffs, stats)
|
||||
- ✅ Technical notes and decisions
|
||||
- ✅ Blockers and resolutions
|
||||
- ✅ Test results and quality metrics
|
||||
- ✅ Automatic checklist updates
|
||||
- ✅ Next steps suggestions
|
||||
|
||||
### Safety
|
||||
|
||||
- ✅ Only writes to Linear (safe)
|
||||
- ❌ Never writes to Jira/Confluence/Slack without confirmation
|
||||
- ✅ Creates immutable history in comments
|
||||
- ✅ Keeps description summary clean with collapsible sections
|
||||
190
commands/implementation:update.md
Normal file
190
commands/implementation:update.md
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
description: Update subtask status and add work summary to Linear
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <linear-issue-id> <subtask-index> <status> "<summary>"
|
||||
---
|
||||
|
||||
# Updating Subtask for: $1
|
||||
|
||||
Subtask Index: $2
|
||||
New Status: $3
|
||||
Summary: $4
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Update Workflow
|
||||
|
||||
### Step 0: Load Shared Helpers
|
||||
|
||||
READ: commands/_shared-linear-helpers.md
|
||||
|
||||
### Step 1: Fetch Current Issue
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
Read the current checklist and description.
|
||||
|
||||
### Step 2: Parse Checklist from Description
|
||||
|
||||
Look for the Implementation Checklist section in the description:
|
||||
|
||||
**Pattern 1: Marker Comments (Preferred)**
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1: Description
|
||||
- [x] Task 2: Description
|
||||
<!-- ccpm-checklist-end -->
|
||||
```
|
||||
|
||||
**Pattern 2: Header-Based (Fallback)**
|
||||
```markdown
|
||||
## ✅ Implementation Checklist
|
||||
- [ ] Task 1
|
||||
- [ ] Task 2
|
||||
```
|
||||
|
||||
Parse the checklist to find item at index **$2**.
|
||||
|
||||
### Step 3: Update Checklist Item in Description
|
||||
|
||||
Update checklist item at index **$2** (0-based indexing) **directly in the description**:
|
||||
|
||||
**If status is "completed"**:
|
||||
- Change `- [ ]` to `- [x]` at line index $2
|
||||
- Mark as checked in description
|
||||
|
||||
**If status is "in-progress"**:
|
||||
- Keep `- [ ]` (unchecked)
|
||||
- No change to checkbox, only update via comment
|
||||
|
||||
**If status is "blocked"**:
|
||||
- Keep `- [ ]` (unchecked)
|
||||
- No change to checkbox
|
||||
- **Also add "blocked" label** to the issue (ensure it exists first using `getOrCreateLabel()`)
|
||||
|
||||
**Generate Updated Description:**
|
||||
|
||||
1. Split description into lines
|
||||
2. Find checklist section (between markers or under header)
|
||||
3. Locate line at index $2 within checklist
|
||||
4. If status is "completed":
|
||||
- Replace `- [ ]` with `- [x]` on that line
|
||||
5. Calculate new completion percentage
|
||||
6. Update progress line: `Progress: X% (Y/Z completed)`
|
||||
7. Add/update timestamp: `Last updated: [ISO timestamp]`
|
||||
|
||||
**Example Update:**
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1: Description
|
||||
- [x] Task 2: Description ← UPDATED!
|
||||
- [ ] Task 3: Description
|
||||
<!-- ccpm-checklist-end -->
|
||||
|
||||
Progress: 33% (1/3 completed) ← UPDATED!
|
||||
Last updated: 2025-01-20T14:30:00Z
|
||||
```
|
||||
|
||||
### Step 4: Update Linear Issue
|
||||
|
||||
Use **Linear MCP** to:
|
||||
|
||||
**A) Update description** (with modified checklist from Step 3)
|
||||
|
||||
**B) Add comment** documenting the change:
|
||||
|
||||
```markdown
|
||||
## 📝 Subtask #$2 Update
|
||||
|
||||
**Status**: $3
|
||||
**Summary**: $4
|
||||
|
||||
**Checklist Progress**: X% → Y% (if status is "completed")
|
||||
|
||||
**Timestamp**: [current date/time]
|
||||
```
|
||||
|
||||
**C) Update labels if needed**:
|
||||
- If status is "blocked":
|
||||
1. Get team ID from the issue
|
||||
2. Use `getOrCreateLabel(teamId, "blocked", { color: "#eb5757", description: "CCPM: Task blocked, needs resolution" })`
|
||||
3. Add the label to the issue using the returned label ID
|
||||
- If status is "completed" and all items complete:
|
||||
1. Use `getOrCreateLabel(teamId, "ready-for-review", { color: "#5e6ad2", description: "CCPM: Ready for code review" })`
|
||||
2. Add the label to the issue
|
||||
|
||||
**Error Handling**:
|
||||
```javascript
|
||||
try {
|
||||
// Get or create label
|
||||
const blockedLabel = await getOrCreateLabel(teamId, "blocked", {
|
||||
color: "#eb5757",
|
||||
description: "CCPM: Task blocked, needs resolution"
|
||||
});
|
||||
|
||||
// Add label to issue
|
||||
await mcp__linear__update_issue({
|
||||
id: issueId,
|
||||
labelIds: [...existingLabelIds, blockedLabel.id]
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to add blocked label:", error);
|
||||
// Continue with update but warn user
|
||||
console.warn("⚠️ Could not add 'blocked' label. Please add manually if needed.");
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Display Confirmation
|
||||
|
||||
Display confirmation:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Subtask #$2 Updated!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: $1
|
||||
Status: $3
|
||||
Summary: $4
|
||||
|
||||
📊 Progress: X% → Y% (if completed)
|
||||
|
||||
✅ Updated in Linear:
|
||||
• Description checkbox updated
|
||||
• Progress % recalculated
|
||||
• Comment added
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Status Options
|
||||
|
||||
- **completed**: Task done, mark as checked
|
||||
- **in-progress**: Currently working on it
|
||||
- **blocked**: Cannot proceed, needs resolution
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Mark subtask complete
|
||||
/update TRAIN-123 0 completed "Implemented JWT authentication with rate limiting"
|
||||
|
||||
# Update progress
|
||||
/update TRAIN-123 1 in-progress "Working on frontend integration, 60% done"
|
||||
|
||||
# Mark as blocked
|
||||
/update TRAIN-123 2 blocked "Waiting for backend API endpoint to be deployed"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Use 0-based indexing for subtask index (first item is 0)
|
||||
- Always provide a clear summary of work done
|
||||
- If blocked, explain what's blocking and what's needed
|
||||
- Keep summaries concise but informative
|
||||
750
commands/plan.md
Normal file
750
commands/plan.md
Normal file
@@ -0,0 +1,750 @@
|
||||
---
|
||||
description: Smart planning command - create, plan, or update tasks (optimized)
|
||||
allowed-tools: [Bash, Task, AskUserQuestion]
|
||||
argument-hint: "[title]" OR <issue-id> OR <issue-id> "[changes]"
|
||||
---
|
||||
|
||||
# /ccpm:plan - Smart Planning Command
|
||||
|
||||
**Token Budget:** ~2,450 tokens (vs ~7,000 baseline) | **65% reduction**
|
||||
|
||||
Intelligent command that creates new tasks, plans existing tasks, or updates plans based on context.
|
||||
|
||||
## Mode Detection
|
||||
|
||||
The command has **3 modes** with clear, unambiguous detection:
|
||||
|
||||
- **CREATE**: `plan "title" [project] [jira-ticket]` → Creates new task and plans it
|
||||
- **PLAN**: `plan WORK-123` → Plans existing task
|
||||
- **UPDATE**: `plan WORK-123 "changes"` → Updates existing plan
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Mode 1: CREATE - New task
|
||||
/ccpm:plan "Add user authentication"
|
||||
/ccpm:plan "Add dark mode" my-app TRAIN-456
|
||||
|
||||
# Mode 2: PLAN - Plan existing
|
||||
/ccpm:plan PSN-27
|
||||
|
||||
# Mode 3: UPDATE - Update plan
|
||||
/ccpm:plan PSN-27 "Add email notifications too"
|
||||
/ccpm:plan PSN-27 "Use Redis instead of in-memory cache"
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Step 1: Parse Arguments & Detect Mode
|
||||
|
||||
```javascript
|
||||
const args = process.argv.slice(2);
|
||||
const arg1 = args[0];
|
||||
const arg2 = args[1];
|
||||
const arg3 = args[2];
|
||||
|
||||
// Issue ID pattern: PROJECT-NUMBER (e.g., PSN-27, WORK-123)
|
||||
const ISSUE_ID_PATTERN = /^[A-Z]+-\d+$/;
|
||||
|
||||
if (!arg1) {
|
||||
return error(`
|
||||
❌ Missing arguments
|
||||
|
||||
Usage:
|
||||
/ccpm:plan "Task title" [project] [jira] # Create new
|
||||
/ccpm:plan WORK-123 # Plan existing
|
||||
/ccpm:plan WORK-123 "changes" # Update plan
|
||||
`);
|
||||
}
|
||||
|
||||
// Detect mode
|
||||
let mode, issueId, title, project, jiraTicket, updateText;
|
||||
|
||||
if (ISSUE_ID_PATTERN.test(arg1)) {
|
||||
// Starts with issue ID
|
||||
issueId = arg1;
|
||||
if (arg2) {
|
||||
mode = 'update';
|
||||
updateText = arg2;
|
||||
} else {
|
||||
mode = 'plan';
|
||||
}
|
||||
} else {
|
||||
// First arg is not issue ID = CREATE mode
|
||||
mode = 'create';
|
||||
title = arg1;
|
||||
project = arg2 || null;
|
||||
jiraTicket = arg3 || null;
|
||||
}
|
||||
|
||||
console.log(`\n🎯 Mode: ${mode.toUpperCase()}`);
|
||||
```
|
||||
|
||||
### Step 2A: CREATE Mode - Create & Plan New Task
|
||||
|
||||
```yaml
|
||||
## CREATE: Create new task and plan it
|
||||
|
||||
1. Detect/load project configuration:
|
||||
|
||||
Task(project-context-manager): `
|
||||
${project ? `Get context for project: ${project}` : 'Get active project context'}
|
||||
Format: standard
|
||||
Include all sections: true
|
||||
`
|
||||
|
||||
Store: projectId, teamId, projectLinearId, defaultLabels, externalPM config
|
||||
|
||||
2. Create Linear issue via subagent:
|
||||
|
||||
**Use the Task tool to create a new Linear issue:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: create_issue
|
||||
params:
|
||||
team: "{team ID from step 1}"
|
||||
title: "{task title from arguments}"
|
||||
project: "{project Linear ID from step 1}"
|
||||
state: "Backlog"
|
||||
labels: {default labels from step 1}
|
||||
description: |
|
||||
## Task
|
||||
|
||||
{task title}
|
||||
|
||||
{if Jira ticket provided: **Jira Reference**: {jiraTicket}}
|
||||
---
|
||||
|
||||
_Planning in progress..._
|
||||
context:
|
||||
command: "plan"
|
||||
mode: "create"
|
||||
```
|
||||
|
||||
Store: issue.id, issue.identifier (e.g., PSN-30)
|
||||
|
||||
Display: "✅ Created issue: ${issue.identifier}"
|
||||
|
||||
3. Gather context (smart agent selection):
|
||||
|
||||
Task: `
|
||||
Plan implementation for: ${title}
|
||||
|
||||
${jiraTicket ? `Jira Ticket: ${jiraTicket}\n` : ''}
|
||||
|
||||
Your task:
|
||||
1. If Jira ticket provided, research it and related Confluence docs
|
||||
2. Analyze codebase to identify files to modify
|
||||
3. Research best practices using Context7 MCP
|
||||
4. Create detailed implementation checklist (5-10 items)
|
||||
5. Estimate complexity (low/medium/high)
|
||||
6. Identify potential risks or challenges
|
||||
|
||||
Provide structured plan with:
|
||||
- Implementation checklist (actionable subtasks)
|
||||
- Files to modify (with brief rationale)
|
||||
- Dependencies and prerequisites
|
||||
- Testing approach
|
||||
- Complexity estimate and reasoning
|
||||
`
|
||||
|
||||
Note: Smart-agent-selector automatically chooses optimal agent based on task type
|
||||
|
||||
4. Update Linear issue with plan:
|
||||
|
||||
**Use the Task tool to update the issue description with the plan:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue_description
|
||||
params:
|
||||
issueId: "{issue identifier from step 2}"
|
||||
description: |
|
||||
## Implementation Checklist
|
||||
|
||||
{checklist generated from planning result in step 3}
|
||||
|
||||
> **Complexity**: {complexity from step 3} | **Estimated**: {estimate from step 3}
|
||||
|
||||
---
|
||||
|
||||
## Task
|
||||
|
||||
{task title}
|
||||
|
||||
{if Jira ticket: **Jira**: [{jiraTicket}](url)}
|
||||
|
||||
## Files to Modify
|
||||
|
||||
{files list from planning result in step 3}
|
||||
|
||||
## Research & Context
|
||||
|
||||
{research from planning result in step 3}
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
{testing strategy from planning result in step 3}
|
||||
|
||||
---
|
||||
|
||||
*Planned via /ccpm:plan*
|
||||
context:
|
||||
command: "plan"
|
||||
```
|
||||
|
||||
5. Update issue status and labels:
|
||||
|
||||
**Use the Task tool to update the issue status:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue
|
||||
params:
|
||||
issueId: "{issue identifier from step 2}"
|
||||
state: "Planned"
|
||||
labels: ["planned", "ready"]
|
||||
context:
|
||||
command: "plan"
|
||||
```
|
||||
|
||||
6. Display completion:
|
||||
|
||||
console.log('\n═══════════════════════════════════════');
|
||||
console.log('✅ Task Created & Planned!');
|
||||
console.log('═══════════════════════════════════════\n');
|
||||
console.log(`📋 Issue: ${issue.identifier} - ${title}`);
|
||||
console.log(`🔗 ${issue.url}`);
|
||||
console.log(`\n📊 Plan Summary:`);
|
||||
console.log(` ✅ ${checklistCount} subtasks created`);
|
||||
console.log(` 📁 ${filesCount} files to modify`);
|
||||
console.log(` ⚡ Complexity: ${complexity}`);
|
||||
console.log(`\n💡 Next: /ccpm:work ${issue.identifier}`);
|
||||
```
|
||||
|
||||
### Step 2B: PLAN Mode - Plan Existing Task
|
||||
|
||||
```yaml
|
||||
## PLAN: Plan existing task
|
||||
|
||||
1. Fetch issue via subagent:
|
||||
|
||||
**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 arguments}"
|
||||
context:
|
||||
cache: true
|
||||
command: "plan"
|
||||
```
|
||||
|
||||
Store: issue.id, issue.title, issue.description, issue.state, issue.team
|
||||
|
||||
Display: "📋 Planning: ${issue.identifier} - ${issue.title}"
|
||||
|
||||
2. Check if already planned:
|
||||
|
||||
const hasChecklist = issue.description.includes('## Implementation Checklist');
|
||||
const isPlanned = issue.state.name === 'Planned' || issue.state.name === 'Ready';
|
||||
|
||||
if (hasChecklist && isPlanned) {
|
||||
console.log('\nℹ️ Task already has a plan. Use one of:');
|
||||
console.log(` • /ccpm:plan ${issueId} "changes" - Update the plan`);
|
||||
console.log(` • /ccpm:work ${issueId} - Start implementation`);
|
||||
return;
|
||||
}
|
||||
|
||||
3. Extract context from description:
|
||||
|
||||
// Check for Jira reference
|
||||
const jiraMatch = issue.description.match(/\*\*Jira.*?\*\*:\s*([A-Z]+-\d+)/);
|
||||
const jiraTicket = jiraMatch ? jiraMatch[1] : null;
|
||||
|
||||
4. Gather planning context (smart agent selection):
|
||||
|
||||
Task: `
|
||||
Create implementation plan for: ${issue.title}
|
||||
|
||||
Current description:
|
||||
${issue.description}
|
||||
|
||||
${jiraTicket ? `Jira ticket: ${jiraTicket}\n` : ''}
|
||||
|
||||
Your task:
|
||||
1. ${jiraTicket ? 'Research Jira ticket and related Confluence docs' : 'Use current description as requirements'}
|
||||
2. Analyze codebase to identify files to modify
|
||||
3. Research best practices using Context7 MCP
|
||||
4. Create detailed implementation checklist (5-10 items)
|
||||
5. Estimate complexity (low/medium/high)
|
||||
6. Identify potential risks
|
||||
|
||||
Provide structured plan with:
|
||||
- Implementation checklist (specific, actionable items)
|
||||
- Files to modify with rationale
|
||||
- Dependencies and prerequisites
|
||||
- Testing strategy
|
||||
- Complexity and estimate
|
||||
`
|
||||
|
||||
5. Update issue description with plan:
|
||||
|
||||
**Use the Task tool to update the issue description:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue_description
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
description: |
|
||||
## Implementation Checklist
|
||||
|
||||
{checklist generated from planning result in step 4}
|
||||
|
||||
> **Complexity**: {complexity from step 4} | **Estimated**: {estimate from step 4}
|
||||
|
||||
---
|
||||
|
||||
{original issue description from step 1}
|
||||
|
||||
## Files to Modify
|
||||
|
||||
{files list from planning result in step 4}
|
||||
|
||||
## Research & Context
|
||||
|
||||
{research from planning result in step 4}
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
{testing strategy from planning result in step 4}
|
||||
|
||||
---
|
||||
|
||||
*Planned via /ccpm:plan*
|
||||
context:
|
||||
command: "plan"
|
||||
```
|
||||
|
||||
6. Update status and labels:
|
||||
|
||||
**Use the Task tool to update the issue status:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
state: "Planned"
|
||||
labels: ["planned", "ready"]
|
||||
context:
|
||||
command: "plan"
|
||||
```
|
||||
|
||||
7. Display completion:
|
||||
|
||||
console.log('\n═══════════════════════════════════════');
|
||||
console.log('✅ Planning Complete!');
|
||||
console.log('═══════════════════════════════════════\n');
|
||||
console.log(`📋 Issue: ${issueId} - ${issue.title}`);
|
||||
console.log(`🔗 ${issue.url}`);
|
||||
console.log(`\n📊 Plan Added:`);
|
||||
console.log(` ✅ ${checklistCount} subtasks`);
|
||||
console.log(` 📁 ${filesCount} files to modify`);
|
||||
console.log(` ⚡ Complexity: ${complexity}`);
|
||||
console.log(`\n💡 Next: /ccpm:work ${issueId}`);
|
||||
```
|
||||
|
||||
### Step 2C: UPDATE Mode - Update Existing Plan
|
||||
|
||||
```yaml
|
||||
## UPDATE: Update existing plan
|
||||
|
||||
1. Fetch current plan:
|
||||
|
||||
**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 arguments}"
|
||||
context:
|
||||
cache: true
|
||||
command: "plan"
|
||||
```
|
||||
|
||||
Store: issue with full description, checklist, state
|
||||
|
||||
2. Display current plan summary:
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(`📋 Current Plan: ${issueId}`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log(`🏷️ Title: ${issue.title}`);
|
||||
console.log(`📊 Status: ${issue.state.name}`);
|
||||
|
||||
const checklist = issue.description.match(/- \[([ x])\] .+/g) || [];
|
||||
const completed = checklist.filter(i => i.includes('[x]')).length;
|
||||
console.log(`🎯 Progress: ${completed}/${checklist.length} items\n`);
|
||||
|
||||
if (checklist.length > 0) {
|
||||
console.log('Current Checklist:');
|
||||
checklist.slice(0, 5).forEach((item, idx) => {
|
||||
const icon = item.includes('[x]') ? '✅' : '⏳';
|
||||
const text = item.replace(/- \[([ x])\] /, '');
|
||||
console.log(` ${icon} ${idx + 1}. ${text}`);
|
||||
});
|
||||
if (checklist.length > 5) console.log(` ... and ${checklist.length - 5} more\n`);
|
||||
}
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('📝 Update Request');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log(updateText);
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
3. Analyze update request:
|
||||
|
||||
// Detect change type
|
||||
const changeType = detectChangeType(updateText);
|
||||
// Returns: 'scope_change', 'approach_change', 'simplification', 'blocker', 'clarification'
|
||||
|
||||
4. Interactive clarification (if needed):
|
||||
|
||||
if (requiresClarification(changeType, updateText)) {
|
||||
const questions = generateClarificationQuestions(changeType, updateText, issue);
|
||||
|
||||
AskUserQuestion({
|
||||
questions: questions // 1-4 targeted questions based on update
|
||||
});
|
||||
|
||||
// Use answers to refine update request
|
||||
}
|
||||
|
||||
5. Generate updated plan with smart agent:
|
||||
|
||||
Task: `
|
||||
Update implementation plan for: ${issue.title}
|
||||
|
||||
Update request: ${updateText}
|
||||
Change type: ${changeType}
|
||||
${clarification ? `Clarification: ${JSON.stringify(clarification)}` : ''}
|
||||
|
||||
Current plan:
|
||||
${issue.description}
|
||||
|
||||
Your task:
|
||||
1. Analyze the update request and current plan
|
||||
2. Determine what needs to change (keep/modify/add/remove)
|
||||
3. Research any new requirements using Context7 MCP
|
||||
4. Update implementation checklist accordingly
|
||||
5. Adjust complexity estimate if needed
|
||||
6. Document the changes made
|
||||
|
||||
Provide:
|
||||
- Updated checklist with changes highlighted
|
||||
- Change summary (what was kept/modified/added/removed)
|
||||
- Updated complexity if changed
|
||||
- Rationale for changes
|
||||
`
|
||||
|
||||
6. Display change preview:
|
||||
|
||||
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('📝 Proposed Changes');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
console.log('✅ Kept:');
|
||||
keptItems.forEach(i => console.log(` • ${i}`));
|
||||
console.log('\n✏️ Modified:');
|
||||
modifiedItems.forEach(i => console.log(` • ${i.old} → ${i.new}`));
|
||||
console.log('\n➕ Added:');
|
||||
addedItems.forEach(i => console.log(` • ${i}`));
|
||||
if (removedItems.length > 0) {
|
||||
console.log('\n❌ Removed:');
|
||||
removedItems.forEach(i => console.log(` • ${i}`));
|
||||
}
|
||||
|
||||
7. Confirm and update:
|
||||
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Apply these changes to the plan?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Yes, apply changes", description: "Update the plan with changes shown above" },
|
||||
{ label: "Needs adjustment", description: "Refine the changes first" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
// Use the Task tool to update the issue description
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue_description
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
description: {updated description from step 5}
|
||||
context:
|
||||
command: "plan"
|
||||
changeType: "{change type from step 3}"
|
||||
```
|
||||
|
||||
// Use the Task tool to add comment documenting the change
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: create_comment
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
body: |
|
||||
## 📝 Plan Updated
|
||||
|
||||
**Change Type**: {change type from step 3}
|
||||
**Request**: {update text from arguments}
|
||||
|
||||
### Changes Made
|
||||
|
||||
{change summary from step 5}
|
||||
|
||||
---
|
||||
*Updated via /ccpm:plan*
|
||||
context:
|
||||
command: "plan"
|
||||
```
|
||||
}
|
||||
|
||||
8. Display completion:
|
||||
|
||||
console.log('\n✅ Plan Updated!');
|
||||
console.log(`📋 Issue: ${issueId} - ${issue.title}`);
|
||||
console.log(`🔗 ${issue.url}`);
|
||||
console.log(`\n📊 Changes: ${changes.added} added, ${changes.modified} modified, ${changes.removed} removed`);
|
||||
console.log(`\n💡 Next: /ccpm:work ${issueId}`);
|
||||
```
|
||||
|
||||
### Helper Functions
|
||||
|
||||
```javascript
|
||||
// Detect change type from update request
|
||||
function detectChangeType(text) {
|
||||
const lower = text.toLowerCase();
|
||||
|
||||
if (/(add|also|include|plus|additionally)/i.test(lower)) return 'scope_change';
|
||||
if (/(instead|different|change|use.*not)/i.test(lower)) return 'approach_change';
|
||||
if (/(remove|don't need|skip|simpler)/i.test(lower)) return 'simplification';
|
||||
if (/(blocked|can't|doesn't work|issue|problem)/i.test(lower)) return 'blocker';
|
||||
return 'clarification';
|
||||
}
|
||||
|
||||
// Generate checklist from planning result
|
||||
function generateChecklist(plan) {
|
||||
return plan.subtasks.map(task => `- [ ] ${task}`).join('\n');
|
||||
}
|
||||
|
||||
// Format files list
|
||||
function formatFilesList(files) {
|
||||
return files.map(f => `- **${f.path}**: ${f.rationale}`).join('\n');
|
||||
}
|
||||
|
||||
// Generate clarification questions based on change type
|
||||
function generateClarificationQuestions(changeType, updateText, issue) {
|
||||
// Returns 1-4 AskUserQuestion-formatted questions
|
||||
// Based on change type and context
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Invalid Issue ID Format
|
||||
```
|
||||
❌ Invalid issue ID format: proj123
|
||||
Expected format: PROJ-123
|
||||
```
|
||||
|
||||
### Issue Not Found
|
||||
```
|
||||
❌ Error fetching issue: Issue not found
|
||||
|
||||
Suggestions:
|
||||
- Verify the issue ID is correct
|
||||
- Check you have access to this Linear team
|
||||
```
|
||||
|
||||
### Missing Title
|
||||
```
|
||||
❌ Missing arguments
|
||||
|
||||
Usage:
|
||||
/ccpm:plan "Task title" [project] [jira] # Create new
|
||||
/ccpm:plan WORK-123 # Plan existing
|
||||
/ccpm:plan WORK-123 "changes" # Update plan
|
||||
```
|
||||
|
||||
### Project Configuration Error
|
||||
```
|
||||
❌ Could not detect project configuration
|
||||
|
||||
Suggestions:
|
||||
- Specify project: /ccpm:plan "title" my-project
|
||||
- Configure project: /ccpm:project:add my-project
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: CREATE Mode
|
||||
|
||||
```bash
|
||||
/ccpm:plan "Add user authentication"
|
||||
|
||||
# Output:
|
||||
# 🎯 Mode: CREATE
|
||||
#
|
||||
# ✅ Created issue: PSN-30
|
||||
# 📋 Planning: PSN-30 - Add user authentication
|
||||
#
|
||||
# [Smart agent analyzes requirements...]
|
||||
#
|
||||
# ═══════════════════════════════════════
|
||||
# ✅ Task Created & Planned!
|
||||
# ═══════════════════════════════════════
|
||||
#
|
||||
# 📋 Issue: PSN-30 - Add user authentication
|
||||
# 🔗 https://linear.app/.../PSN-30
|
||||
#
|
||||
# 📊 Plan Summary:
|
||||
# ✅ 7 subtasks created
|
||||
# 📁 5 files to modify
|
||||
# ⚡ Complexity: Medium
|
||||
#
|
||||
# 💡 Next: /ccpm:work PSN-30
|
||||
```
|
||||
|
||||
### Example 2: PLAN Mode
|
||||
|
||||
```bash
|
||||
/ccpm:plan PSN-29
|
||||
|
||||
# Output:
|
||||
# 🎯 Mode: PLAN
|
||||
#
|
||||
# 📋 Planning: PSN-29 - Implement dark mode
|
||||
#
|
||||
# [Smart agent creates plan...]
|
||||
#
|
||||
# ═══════════════════════════════════════
|
||||
# ✅ Planning Complete!
|
||||
# ═══════════════════════════════════════
|
||||
#
|
||||
# 📋 Issue: PSN-29 - Implement dark mode
|
||||
# 🔗 https://linear.app/.../PSN-29
|
||||
#
|
||||
# 📊 Plan Added:
|
||||
# ✅ 6 subtasks
|
||||
# 📁 8 files to modify
|
||||
# ⚡ Complexity: Low
|
||||
#
|
||||
# 💡 Next: /ccpm:work PSN-29
|
||||
```
|
||||
|
||||
### Example 3: UPDATE Mode
|
||||
|
||||
```bash
|
||||
/ccpm:plan PSN-29 "Also add email notifications"
|
||||
|
||||
# Output:
|
||||
# 🎯 Mode: UPDATE
|
||||
#
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# 📋 Current Plan: PSN-29
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
#
|
||||
# 🏷️ Title: Implement dark mode
|
||||
# 📊 Status: Planned
|
||||
# 🎯 Progress: 0/6 items
|
||||
#
|
||||
# [Shows clarification questions...]
|
||||
#
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# 📝 Proposed Changes
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
#
|
||||
# ✅ Kept: 6 items
|
||||
# ➕ Added:
|
||||
# • Set up email service integration
|
||||
# • Add notification templates
|
||||
#
|
||||
# [Confirmation prompt...]
|
||||
#
|
||||
# ✅ Plan Updated!
|
||||
# 📊 Changes: 2 added, 0 modified, 0 removed
|
||||
```
|
||||
|
||||
## Token Budget Breakdown
|
||||
|
||||
| Section | Tokens | Notes |
|
||||
|---------|--------|-------|
|
||||
| Frontmatter & description | 100 | Minimal metadata |
|
||||
| Step 1: Parse & detect mode | 200 | Argument parsing |
|
||||
| Step 2A: CREATE mode | 600 | Create + plan workflow |
|
||||
| Step 2B: PLAN mode | 550 | Plan existing workflow |
|
||||
| Step 2C: UPDATE mode | 500 | Update workflow with clarification |
|
||||
| Helper functions | 150 | Reusable utilities |
|
||||
| Error handling | 100 | 4 error scenarios |
|
||||
| Examples | 250 | 3 concise examples |
|
||||
| **Total** | **~2,450** | **vs ~7,000 baseline (65% reduction)** |
|
||||
|
||||
## Key Optimizations
|
||||
|
||||
1. ✅ **No routing overhead** - All 3 modes implemented directly
|
||||
2. ✅ **Linear subagent** - All Linear ops cached (85-95% hit rate)
|
||||
3. ✅ **Smart agent selection** - Automatic optimal agent for planning
|
||||
4. ✅ **Batch operations** - Single update_issue call (state + labels + description)
|
||||
5. ✅ **Concise examples** - Only 3 essential examples
|
||||
6. ✅ **Focused scope** - Simplified planning workflow (no full external PM research by default)
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
- **After planning** → Use /ccpm:work to start implementation
|
||||
- **During work** → Use /ccpm:sync to save progress
|
||||
- **Before completion** → Use /ccpm:verify for quality checks
|
||||
- **Finalize** → Use /ccpm:done to create PR and complete
|
||||
|
||||
## Notes
|
||||
|
||||
- **Mode detection**: Clear, unambiguous patterns (issue ID vs quoted string)
|
||||
- **Smart agents**: Automatic selection based on task type (backend/frontend/mobile)
|
||||
- **Project detection**: Auto-detects or uses explicit project argument
|
||||
- **Caching**: Linear subagent caches all data for 85-95% faster operations
|
||||
- **Error recovery**: Structured error messages with actionable suggestions
|
||||
391
commands/planning:create.md
Normal file
391
commands/planning:create.md
Normal file
@@ -0,0 +1,391 @@
|
||||
---
|
||||
description: Create Linear issue and run full planning workflow in one step
|
||||
allowed-tools: [Bash, LinearMCP, AtlassianMCP, SlackMCP, PlaywrightMCP, Context7MCP]
|
||||
argument-hint: "<title>" <project> <jira-ticket-id>
|
||||
---
|
||||
|
||||
# Creating & Planning: $1 for Project: $2
|
||||
|
||||
You are **creating a new Linear issue** and running the **Planning Phase** in one step.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **READ-ONLY** operations are permitted (fetch, search, view)
|
||||
- ⛔ **WRITE operations** require user confirmation
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
|
||||
When in doubt, ASK before posting anything externally.
|
||||
|
||||
## Project Configuration
|
||||
|
||||
**IMPORTANT**: This command uses CCPM agents for efficient project detection and configuration loading.
|
||||
|
||||
### Auto-Activate Skills
|
||||
|
||||
```markdown
|
||||
Skill(project-detection): Auto-activates for project context
|
||||
Skill(project-operations): Provides workflow guidance
|
||||
```
|
||||
|
||||
### Load Project Configuration with Agents
|
||||
|
||||
Use project agents to detect and load configuration:
|
||||
|
||||
```javascript
|
||||
// Step 1: Detect or get project from argument
|
||||
const projectArg = "$2" // Optional project argument
|
||||
|
||||
let projectContext
|
||||
|
||||
if (projectArg && projectArg !== "") {
|
||||
// User specified project, load it
|
||||
projectContext = Task(project-context-manager): `
|
||||
Get context for project: ${projectArg}
|
||||
Format: standard
|
||||
Include all sections: true
|
||||
`
|
||||
} else {
|
||||
// Auto-detect from current directory
|
||||
projectContext = Task(project-context-manager): `
|
||||
Get active project context
|
||||
Format: standard
|
||||
Include all sections: true
|
||||
`
|
||||
}
|
||||
|
||||
if (projectContext.error) {
|
||||
console.error(`❌ ${projectContext.error.message}`)
|
||||
projectContext.error.suggestions?.forEach(s => console.log(` - ${s}`))
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Now we have all project configuration in structured format:
|
||||
const PROJECT_ID = projectContext.detected.project_id
|
||||
const PROJECT_NAME = projectContext.config.project_name
|
||||
const SUBPROJECT = projectContext.detected.subproject // NEW: for monorepos
|
||||
|
||||
const LINEAR_TEAM = projectContext.config.linear.team
|
||||
const LINEAR_PROJECT = projectContext.config.linear.project
|
||||
const LINEAR_DEFAULT_LABELS = projectContext.config.linear.default_labels
|
||||
|
||||
const EXTERNAL_PM_ENABLED = projectContext.config.external_pm.enabled
|
||||
const EXTERNAL_PM_TYPE = projectContext.config.external_pm.type
|
||||
const JIRA_CONFIG = projectContext.config.external_pm.jira
|
||||
const CONFLUENCE_CONFIG = projectContext.config.external_pm.confluence
|
||||
|
||||
const TECH_STACK = SUBPROJECT
|
||||
? projectContext.config.subproject?.tech_stack
|
||||
: projectContext.config.tech_stack
|
||||
|
||||
// Display context
|
||||
console.log(projectContext.display.title) // e.g., "My Monorepo › frontend"
|
||||
```
|
||||
|
||||
**Benefits of Agent-Based Approach**:
|
||||
- ~80% token reduction vs inline logic
|
||||
- Automatic subdirectory detection for monorepos
|
||||
- Consistent error handling
|
||||
- Structured, validated output
|
||||
- Tech stack awareness (overall or per-subproject)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Create Linear Issue
|
||||
|
||||
**IMPORTANT**: Use shared Linear helpers for resilient state and label handling:
|
||||
|
||||
```markdown
|
||||
READ: commands/_shared-linear-helpers.md
|
||||
```
|
||||
|
||||
Use **Linear MCP** to create a new issue using loaded configuration:
|
||||
|
||||
**Title**: $1
|
||||
**Team**: ${LINEAR_TEAM} (from config)
|
||||
**Project**: ${LINEAR_PROJECT} (from config)
|
||||
**Status**: Backlog
|
||||
**Labels**: ${LINEAR_DEFAULT_LABELS} + subproject label (if in monorepo)
|
||||
|
||||
```javascript
|
||||
// Build labels array
|
||||
const labels = [...LINEAR_DEFAULT_LABELS]
|
||||
|
||||
// Add subproject label for monorepo context
|
||||
if (SUBPROJECT) {
|
||||
labels.push(SUBPROJECT)
|
||||
console.log(`📁 Subproject context: ${SUBPROJECT}`)
|
||||
}
|
||||
|
||||
// Ensure all labels exist before creating issue
|
||||
try {
|
||||
const validLabels = await ensureLabelsExist(LINEAR_TEAM, labels, {
|
||||
descriptions: {
|
||||
'planning': 'Task is in planning phase',
|
||||
'research': 'Research and discovery required',
|
||||
'implementation': 'Task is being implemented',
|
||||
'verification': 'Task is being verified'
|
||||
}
|
||||
})
|
||||
|
||||
// Get valid state ID for "Backlog"
|
||||
const backlogStateId = await getValidStateId(LINEAR_TEAM, "Backlog")
|
||||
|
||||
// Create issue with validated state and labels
|
||||
const issue = linear_create_issue({
|
||||
title: "$1",
|
||||
team: LINEAR_TEAM,
|
||||
project: LINEAR_PROJECT,
|
||||
stateId: backlogStateId,
|
||||
labelIds: validLabels
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
if (error.message.includes('Could not find state') || error.message.includes('Invalid state')) {
|
||||
console.error(`❌ Linear State Error: ${error.message}`)
|
||||
console.log(`💡 Tip: Run '/ccpm:utils:status' to see available states for your team`)
|
||||
throw error
|
||||
}
|
||||
throw error
|
||||
}
|
||||
```
|
||||
|
||||
**Initial Description**:
|
||||
|
||||
```markdown
|
||||
## Task
|
||||
|
||||
$1
|
||||
|
||||
**Jira Reference**: $3 (if provided)
|
||||
|
||||
---
|
||||
|
||||
_Planning in progress..._
|
||||
```
|
||||
|
||||
Save the created Linear issue ID.
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
✅ Linear Issue Created!
|
||||
|
||||
📋 Issue: [WORK-123]
|
||||
🔗 URL: https://linear.app/workspace/issue/[WORK-123]
|
||||
📝 Title: $1
|
||||
🏢 Project: $2
|
||||
|
||||
⏳ Starting planning workflow...
|
||||
```
|
||||
|
||||
### Step 2: Execute Shared Planning Workflow
|
||||
|
||||
**READ**: `commands/_shared-planning-workflow.md`
|
||||
|
||||
Execute the shared planning workflow to complete planning for the newly created issue.
|
||||
|
||||
**Set required context variables:**
|
||||
- `LINEAR_ISSUE_ID` = [created issue ID from Step 1]
|
||||
- `JIRA_TICKET_ID` = $3 (optional Jira ticket ID)
|
||||
- `PROJECT_CONFIG` = [loaded in project configuration section]
|
||||
- `EXTERNAL_PM_ENABLED` = [from config]
|
||||
- `EXTERNAL_PM_TYPE` = [from config]
|
||||
- `JIRA_ENABLED`, `CONFLUENCE_ENABLED`, `SLACK_ENABLED` = [from config]
|
||||
|
||||
**Execute these steps from the shared workflow:**
|
||||
|
||||
1. **Step 0.5**: Detect and analyze images in the Linear issue
|
||||
- Uses `commands/_shared-image-analysis.md` logic
|
||||
- Finds UI mockups, diagrams, screenshots
|
||||
- Analyzes and formats for Linear description
|
||||
|
||||
2. **Step 0.6**: Detect and extract Figma designs
|
||||
- Uses `commands/_shared-figma-detection.md` logic
|
||||
- Identifies live Figma links
|
||||
- Extracts design tokens and specifications
|
||||
- Caches results in Linear comments
|
||||
|
||||
3. **Step 1**: Gather external PM context (Jira, Confluence, Slack)
|
||||
- Only if external PM is enabled
|
||||
- Fetches Jira ticket details and linked issues
|
||||
- Searches Confluence for related documentation
|
||||
- Finds Slack thread discussions
|
||||
- Checks BitBucket for related PRs
|
||||
|
||||
4. **Step 2**: Analyze codebase
|
||||
- Identifies relevant files to modify
|
||||
- Maps patterns and conventions
|
||||
- Determines dependencies
|
||||
|
||||
5. **Step 2.5**: Invoke engineer agents for technical analysis
|
||||
- Selects appropriate agents based on task type
|
||||
- Backend tasks → `backend-architect`
|
||||
- Frontend tasks → `frontend-developer`
|
||||
- Mobile tasks → `mobile-developer`
|
||||
- Full-stack → both backend and frontend in parallel
|
||||
- Security-critical → add `security-auditor`
|
||||
|
||||
6. **Step 3**: Update Linear description with comprehensive research
|
||||
- Creates implementation checklist
|
||||
- Inserts visual context (images + Figma designs)
|
||||
- Adds research findings
|
||||
- Includes agent analysis
|
||||
- Links all external resources
|
||||
|
||||
7. **Step 4**: Confirm completion
|
||||
- Display status summary
|
||||
- Show research added
|
||||
- Provide Linear issue URL
|
||||
|
||||
This ensures the new issue receives the same comprehensive planning as existing issues:
|
||||
- Image analysis
|
||||
- Figma design extraction
|
||||
- External PM research
|
||||
- Codebase analysis
|
||||
- Implementation checklist generation
|
||||
|
||||
### Step 3: Show Status & Interactive Next Actions
|
||||
|
||||
**READ**: ``$CCPM_COMMANDS_DIR/_shared-linear-helpers.md``
|
||||
|
||||
Use **Linear MCP** to get current status.
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Create & Planning Complete!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Linear Issue: [WORK-123]
|
||||
🔗 URL: https://linear.app/workspace/issue/[WORK-123]
|
||||
📝 Jira Reference: $3 (if provided)
|
||||
|
||||
📊 Current Status: Planning
|
||||
🎯 Progress: 0 of [N] subtasks complete (0%)
|
||||
🏷️ Labels: planning, research-complete
|
||||
⏱️ Time in status: Just created
|
||||
📈 Complexity: [Low/Medium/High]
|
||||
|
||||
📊 Research Summary:
|
||||
- Gathered context from [X] Jira tickets (if applicable)
|
||||
- Found [Y] relevant Confluence docs (if applicable)
|
||||
- Analyzed [Z] related Slack discussions (if applicable)
|
||||
- Identified [N] files to modify
|
||||
- Researched best practices from Context7
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
Use **AskUserQuestion** tool:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Planning complete! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Start Implementation",
|
||||
description: "Begin working with agent coordination (/ccpm:implementation:start)"
|
||||
},
|
||||
{
|
||||
label: "Get AI Insights",
|
||||
description: "Get AI analysis of complexity, risks, and timeline (/ccpm:utils:insights)"
|
||||
},
|
||||
{
|
||||
label: "Review Planning",
|
||||
description: "Review the planning details in Linear (/ccpm:utils:status)"
|
||||
},
|
||||
{
|
||||
label: "Auto-Assign Agents",
|
||||
description: "Let AI assign subtasks to optimal agents (/ccpm:utils:auto-assign)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute the chosen action**:
|
||||
|
||||
- If "Start Implementation" → Run `/ccpm:implementation:start [WORK-123]`
|
||||
- If "Get AI Insights" → Run `/ccpm:utils:insights [WORK-123]`
|
||||
- If "Review Planning" → Run `/ccpm:utils:status [WORK-123]`
|
||||
- If "Auto-Assign Agents" → Run `/ccpm:utils:auto-assign [WORK-123]`
|
||||
- If "Other" → Show quick commands and exit gracefully
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Status: /ccpm:utils:status [WORK-123]
|
||||
Start: /ccpm:implementation:start [WORK-123]
|
||||
Insights: /ccpm:utils:insights [WORK-123]
|
||||
Context: /ccpm:utils:context [WORK-123]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:plan "task title" [project] [jira-ticket]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Same functionality, simpler syntax
|
||||
- Part of the 6-command natural workflow
|
||||
- Auto-detects project if configured
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
### This Command Combines
|
||||
|
||||
1. **Issue Creation** - No manual Linear UI needed
|
||||
2. **Full Planning** - All research in one go
|
||||
3. **Interactive Mode** - Suggests next actions
|
||||
4. **Continuous Flow** - Can chain to next command
|
||||
|
||||
### Usage Patterns
|
||||
|
||||
**With Jira:**
|
||||
|
||||
```bash
|
||||
/ccpm:planning:create "Add JWT authentication" my-app PROJ-456
|
||||
```
|
||||
|
||||
**Without Jira (Linear-only):**
|
||||
|
||||
```bash
|
||||
/ccpm:planning:create "Add dark mode toggle" personal-project
|
||||
```
|
||||
|
||||
**With Active Project:**
|
||||
|
||||
```bash
|
||||
/ccpm:project:set my-app
|
||||
/ccpm:planning:create "Add JWT authentication" # Uses active project
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ One command instead of two
|
||||
- ✅ No context switching to Linear UI
|
||||
- ✅ Immediate planning after creation
|
||||
- ✅ Interactive next steps
|
||||
- ✅ Fast workflow start
|
||||
695
commands/planning:design-approve.md
Normal file
695
commands/planning:design-approve.md
Normal file
@@ -0,0 +1,695 @@
|
||||
---
|
||||
description: Approve final UI design and generate comprehensive developer specifications
|
||||
allowed-tools: [Task, LinearMCP, Context7MCP, Read, Grep, Glob, AskUserQuestion]
|
||||
argument-hint: <issue-id> <option-number>
|
||||
---
|
||||
|
||||
# Approving UI Design: $1 - Option $2
|
||||
|
||||
You are **approving the final UI design** and generating **comprehensive developer specifications** for Linear issue **$1**.
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
This command is **READ-ONLY** for external systems and **WRITE** to Linear (internal tracking).
|
||||
|
||||
## 📦 Shared Helper Functions
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
Use the following helper functions from the shared library:
|
||||
- `getValidStateId(teamId, stateNameOrType)` - Resolves state names to valid IDs
|
||||
- `ensureLabelsExist(teamId, labelNames, options)` - Creates labels if missing
|
||||
- `getOrCreateLabel(teamId, labelName, options)` - Gets or creates single label
|
||||
- `getDefaultColor(labelName)` - Standard CCPM color palette
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1**: Linear issue ID (e.g., WORK-123)
|
||||
- **$2**: Option number to approve (1, 2, 3, or "refined")
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Approved Design
|
||||
|
||||
Use **Linear MCP** to get issue details:
|
||||
|
||||
```javascript
|
||||
linear_get_issue({ id: "$1" })
|
||||
```
|
||||
|
||||
Extract:
|
||||
- Approved design option (Option $2)
|
||||
- Design wireframe and description
|
||||
- Original requirements
|
||||
- Any refinements made
|
||||
- User feedback history
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Approving Design Option $2 for $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: [Title from Linear]
|
||||
🎨 Approved Design: Option $2
|
||||
🔗 URL: https://linear.app/workspace/issue/$1
|
||||
|
||||
**Selected Design**:
|
||||
[Display the approved option's wireframe and description]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⏳ Generating comprehensive UI specifications...
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 2: Deep Dive into Design System
|
||||
|
||||
**Comprehensive codebase analysis** for specifications:
|
||||
|
||||
Use **Read** to extract full details:
|
||||
|
||||
```bash
|
||||
# Tailwind configuration
|
||||
tailwind.config.{js,ts}
|
||||
|
||||
# Theme configurations
|
||||
**/theme/**/*
|
||||
**/styles/**/*
|
||||
|
||||
# Component libraries
|
||||
components.json (shadcn-ui)
|
||||
components/ui/**/*.{tsx,ts}
|
||||
|
||||
# Global styles
|
||||
globals.css
|
||||
index.css
|
||||
```
|
||||
|
||||
**Extract Complete Design Tokens**:
|
||||
- **All colors** with hex values and Tailwind classes
|
||||
- **Typography** (font families, sizes, weights, line heights)
|
||||
- **Spacing scale** (complete px values for all Tailwind classes)
|
||||
- **Border styles** (widths, colors, radius values)
|
||||
- **Shadow definitions** (all shadow levels)
|
||||
- **Breakpoint values** (exact px widths)
|
||||
- **Animation/transition configs**
|
||||
- **Z-index scale** (layering system)
|
||||
|
||||
**Component Library Audit**:
|
||||
- List ALL available components with imports
|
||||
- Document component props and variants
|
||||
- Identify composition patterns
|
||||
- Note any custom components to create
|
||||
|
||||
### Step 3: Research Latest Implementation Patterns
|
||||
|
||||
**Use Context7 MCP for developer-focused documentation**:
|
||||
|
||||
```javascript
|
||||
// Get latest component implementation patterns
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/tailwindlabs/tailwindcss",
|
||||
topic: "component implementation patterns and best practices",
|
||||
tokens: 3000
|
||||
})
|
||||
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/shadcn-ui/ui",
|
||||
topic: "component composition and TypeScript patterns",
|
||||
tokens: 3000
|
||||
})
|
||||
|
||||
// If React Native
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/nativewind/nativewind",
|
||||
topic: "React Native styling patterns and platform differences",
|
||||
tokens: 2000
|
||||
})
|
||||
```
|
||||
|
||||
**Research**:
|
||||
- Latest TypeScript patterns for components
|
||||
- Accessibility implementation (ARIA attributes)
|
||||
- Responsive implementation strategies
|
||||
- Dark mode implementation patterns
|
||||
- Animation/transition best practices
|
||||
- Performance optimization techniques
|
||||
|
||||
### Step 4: Invoke pm:ui-designer Agent for Specifications
|
||||
|
||||
**CRITICAL: Invoke the specialist agent to generate complete specs**:
|
||||
|
||||
```javascript
|
||||
Task(pm:ui-designer): `
|
||||
Generate comprehensive UI specifications for approved design (Option $2) for Linear issue $1.
|
||||
|
||||
**Approved Design**:
|
||||
[Include the approved wireframe and full description]
|
||||
|
||||
**Complete Design System** (from codebase analysis):
|
||||
- Colors: [All colors with hex and Tailwind classes]
|
||||
- Typography: [All font configs with exact values]
|
||||
- Spacing: [Complete spacing scale]
|
||||
- Borders: [All border configs]
|
||||
- Shadows: [All shadow definitions]
|
||||
- Breakpoints: [Exact breakpoint values]
|
||||
- Animations: [Transition/animation configs]
|
||||
|
||||
**Frontend Architecture Context** (from design-ui analysis):
|
||||
- Component patterns used in project
|
||||
- Existing reusable components (with file paths)
|
||||
- State management approach
|
||||
- Styling conventions
|
||||
- Data flow patterns
|
||||
- Performance patterns
|
||||
- Technical constraints
|
||||
|
||||
**Available Components**:
|
||||
[List all existing components with import paths]
|
||||
|
||||
**Latest Implementation Patterns** (from Context7):
|
||||
[Summarize latest best practices from Context7]
|
||||
|
||||
**Original Requirements**:
|
||||
[User requirements and goals from initial planning]
|
||||
|
||||
**CRITICAL REQUIREMENTS**:
|
||||
- MAXIMIZE reuse of existing components
|
||||
- FOLLOW project conventions from frontend analysis
|
||||
- ENSURE technical feasibility based on architecture
|
||||
- ALIGN with established patterns
|
||||
|
||||
**REQUIREMENTS**:
|
||||
Generate a COMPREHENSIVE UI specification document that includes:
|
||||
|
||||
1. **Design System Reference**
|
||||
- All colors (name, hex, Tailwind class, usage)
|
||||
- All typography (size, weight, line height, usage)
|
||||
- Complete spacing scale
|
||||
- Border and shadow definitions
|
||||
- Breakpoint values
|
||||
|
||||
2. **Layout Structure**
|
||||
- Container specifications
|
||||
- Grid/Flex configurations
|
||||
- Responsive behavior at each breakpoint
|
||||
|
||||
3. **Component Breakdown**
|
||||
- Each component with:
|
||||
* Purpose and description
|
||||
* TypeScript interface for props
|
||||
* Complete JSX structure with Tailwind classes
|
||||
* All states (default, hover, active, disabled, focus)
|
||||
* All variants (primary, secondary, etc.)
|
||||
* Accessibility requirements (ARIA, semantic HTML)
|
||||
* Responsive behavior (mobile, tablet, desktop)
|
||||
|
||||
4. **Interactions & Animations**
|
||||
- Transition configurations
|
||||
- Hover effects with exact classes
|
||||
- Loading states
|
||||
- Micro-interactions
|
||||
|
||||
5. **Responsive Design**
|
||||
- Mobile-first implementation strategy
|
||||
- Breakpoint-specific styles
|
||||
- Examples with Tailwind responsive prefixes
|
||||
|
||||
6. **Dark Mode Support**
|
||||
- Color mappings for dark mode
|
||||
- Implementation strategy with dark: prefix
|
||||
- Examples
|
||||
|
||||
7. **Accessibility Checklist**
|
||||
- WCAG 2.1 AA requirements
|
||||
- Semantic HTML elements
|
||||
- ARIA attributes needed
|
||||
- Keyboard navigation requirements
|
||||
- Focus management
|
||||
|
||||
8. **Component Library Mapping**
|
||||
- Which existing components to reuse (with import paths)
|
||||
- Which new components to create (with file locations)
|
||||
- Component composition patterns
|
||||
|
||||
9. **Implementation Priority**
|
||||
- High/Medium/Low priority breakdown
|
||||
- Suggested implementation order
|
||||
|
||||
10. **Implementation Tips**
|
||||
- Step-by-step implementation guide
|
||||
- Common pitfalls to avoid
|
||||
- Performance considerations
|
||||
- Testing recommendations
|
||||
|
||||
**FORMAT**: Use the comprehensive markdown template with ALL sections filled in detail. Be extremely specific with Tailwind classes, TypeScript interfaces, and implementation examples.
|
||||
|
||||
**GOAL**: This specification should be so detailed that a developer can implement the UI accurately without needing to ask questions or make design decisions.
|
||||
`
|
||||
```
|
||||
|
||||
**Agent Output Expected**:
|
||||
- 10+ page comprehensive specification document
|
||||
- Complete design token reference
|
||||
- Detailed component breakdown with code
|
||||
- TypeScript interfaces for all components
|
||||
- Accessibility guidelines
|
||||
- Responsive implementation details
|
||||
- Dark mode implementation
|
||||
- Developer implementation guide
|
||||
|
||||
### Step 5: Create Linear Document for Specifications
|
||||
|
||||
Use **Linear MCP** to create a comprehensive document:
|
||||
|
||||
```javascript
|
||||
// Create Linear Document
|
||||
linear_create_document({
|
||||
title: "UI Specification: [Issue Title]",
|
||||
content: `
|
||||
[Agent's comprehensive specification output]
|
||||
`,
|
||||
projectId: "[Project ID from issue]"
|
||||
})
|
||||
|
||||
// Link document to issue
|
||||
linear_update_issue({
|
||||
id: "$1",
|
||||
description: "[Existing description]\n\n---\n\n## ✅ Approved UI Design\n\n**Selected**: Option $2\n**Specifications**: [Link to Linear Document]\n\n[Approved wireframe]\n\n**Status**: Ready for implementation"
|
||||
})
|
||||
```
|
||||
|
||||
### Step 6: Update Issue Status and Labels
|
||||
|
||||
Use **Linear MCP** with shared helpers to mark as ready for development:
|
||||
|
||||
```javascript
|
||||
// Extract team ID from issue
|
||||
const issue = await linear_get_issue({ id: "$1" });
|
||||
const teamId = issue.team.id;
|
||||
|
||||
// Get valid state ID using shared helper (supports fuzzy matching)
|
||||
const stateId = await getValidStateId(teamId, "todo"); // Maps to "unstarted" type
|
||||
|
||||
// Ensure all required labels exist before using them
|
||||
const labelNames = await ensureLabelsExist(teamId, [
|
||||
"design-approved",
|
||||
"spec-complete",
|
||||
"ready-for-dev"
|
||||
], {
|
||||
colors: {
|
||||
"design-approved": getDefaultColor("approved"),
|
||||
"spec-complete": getDefaultColor("documentation"),
|
||||
"ready-for-dev": getDefaultColor("planning")
|
||||
}
|
||||
});
|
||||
|
||||
// Update issue with validated state ID and labels
|
||||
await linear_update_issue({
|
||||
id: "$1",
|
||||
stateId: stateId,
|
||||
labelIds: labelNames
|
||||
});
|
||||
```
|
||||
|
||||
**Error Handling:**
|
||||
```javascript
|
||||
try {
|
||||
const stateId = await getValidStateId(teamId, "todo");
|
||||
// Proceed with update
|
||||
} catch (error) {
|
||||
console.error("Failed to resolve state:", error.message);
|
||||
// Error message includes available states for the team
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: Generate Implementation Tasks (Optional)
|
||||
|
||||
Use **AskUserQuestion** to offer task breakdown:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Would you like me to break this down into implementation tasks?",
|
||||
header: "Task Breakdown",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "⭐ Yes, create tasks",
|
||||
description: "Create sub-tasks for each component/section"
|
||||
},
|
||||
{
|
||||
label: "No, I'll implement as one task",
|
||||
description: "Keep as single issue"
|
||||
},
|
||||
{
|
||||
label: "Let me review specs first",
|
||||
description: "I'll decide after reviewing the specifications"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**If user wants task breakdown**:
|
||||
|
||||
Create sub-issues using **Linear MCP** with validated labels:
|
||||
|
||||
```javascript
|
||||
// Get team ID from parent issue
|
||||
const parentIssue = await linear_get_issue({ id: "$1" });
|
||||
const teamId = parentIssue.team.id;
|
||||
|
||||
// Ensure labels exist before creating sub-tasks
|
||||
const taskLabels = await ensureLabelsExist(teamId, [
|
||||
"frontend",
|
||||
"ui-implementation"
|
||||
], {
|
||||
colors: {
|
||||
"frontend": getDefaultColor("frontend"),
|
||||
"ui-implementation": getDefaultColor("implementation")
|
||||
}
|
||||
});
|
||||
|
||||
// Create sub-issues with validated labels
|
||||
await linear_create_issue({
|
||||
title: "Implement [Component 1 Name]",
|
||||
description: "See UI Specification: [Link]\n\nComponent: [Component 1]\nPriority: High",
|
||||
parentId: "$1",
|
||||
teamId: teamId,
|
||||
labelIds: taskLabels
|
||||
});
|
||||
|
||||
await linear_create_issue({
|
||||
title: "Implement [Component 2 Name]",
|
||||
description: "See UI Specification: [Link]\n\nComponent: [Component 2]\nPriority: High",
|
||||
parentId: "$1",
|
||||
teamId: teamId,
|
||||
labelIds: taskLabels
|
||||
});
|
||||
|
||||
// Continue for all major components
|
||||
```
|
||||
|
||||
### Step 8: Display Final Summary
|
||||
|
||||
Show comprehensive output:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ UI Design Approved & Specifications Generated
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: $1
|
||||
🎨 Approved: Option $2
|
||||
🔗 Linear: https://linear.app/workspace/issue/$1
|
||||
📄 Specifications: [Linear Document URL]
|
||||
🏷️ Status: Ready for Development
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Specification Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Design System**:
|
||||
- Colors: [X] defined with Tailwind classes
|
||||
- Typography: [Y] sizes documented
|
||||
- Spacing: Complete 4px grid scale
|
||||
- Components: [Z] to implement
|
||||
|
||||
**Components Breakdown**:
|
||||
1. [Component 1] - [Complexity: Simple/Moderate/Complex]
|
||||
2. [Component 2] - [Complexity]
|
||||
3. [Component 3] - [Complexity]
|
||||
[...]
|
||||
|
||||
**Accessibility**: WCAG 2.1 AA compliant
|
||||
**Responsive**: Mobile-first, 3 breakpoints
|
||||
**Dark Mode**: Full support with dark: variants
|
||||
**Animations**: [X] micro-interactions defined
|
||||
|
||||
**Implementation Priority**:
|
||||
- High: [X] components (core functionality)
|
||||
- Medium: [Y] components (enhancements)
|
||||
- Low: [Z] components (polish)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📦 Developer Resources
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**What's Included**:
|
||||
✅ Complete design token reference
|
||||
✅ TypeScript interfaces for all components
|
||||
✅ Tailwind class mappings for every element
|
||||
✅ Accessibility implementation guide (ARIA, semantic HTML)
|
||||
✅ Responsive behavior documentation (mobile/tablet/desktop)
|
||||
✅ Dark mode implementation guide
|
||||
✅ Animation and interaction specifications
|
||||
✅ Component library mapping (reuse vs create new)
|
||||
✅ Step-by-step implementation tips
|
||||
✅ Performance and testing recommendations
|
||||
|
||||
**Documentation Location**:
|
||||
📄 Linear Document: [URL]
|
||||
📋 Issue: https://linear.app/workspace/issue/$1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🚀 Implementation Checklist
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
For developers implementing this UI:
|
||||
|
||||
1. [ ] Read complete specification document
|
||||
2. [ ] Review design system and available components
|
||||
3. [ ] Set up component structure following specs
|
||||
4. [ ] Implement base layout and structure
|
||||
5. [ ] Add Tailwind styling per specifications
|
||||
6. [ ] Implement interactive states (hover, focus, active)
|
||||
7. [ ] Add responsive behavior for all breakpoints
|
||||
8. [ ] Implement dark mode variants
|
||||
9. [ ] Add accessibility features (ARIA, keyboard nav)
|
||||
10. [ ] Test on mobile, tablet, and desktop
|
||||
11. [ ] Test dark mode
|
||||
12. [ ] Test keyboard navigation
|
||||
13. [ ] Test with screen reader (if available)
|
||||
14. [ ] Optimize performance (lazy loading, code splitting)
|
||||
15. [ ] Review against original design wireframe
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
Use **AskUserQuestion** for next steps:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "⭐ Start Implementation",
|
||||
description: "Begin coding with agent coordination (/ccpm:implementation:start)"
|
||||
},
|
||||
{
|
||||
label: "Break into Sub-Tasks",
|
||||
description: "Create Linear sub-issues for each component"
|
||||
},
|
||||
{
|
||||
label: "Review Specifications",
|
||||
description: "View the Linear Document with full specs"
|
||||
},
|
||||
{
|
||||
label: "Assign to Developer",
|
||||
description: "Assign issue to a team member in Linear"
|
||||
},
|
||||
{
|
||||
label: "Continue Planning",
|
||||
description: "Return to planning workflow (/ccpm:planning:plan)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 9: Execute Selected Next Action
|
||||
|
||||
**If "Start Implementation"**:
|
||||
```bash
|
||||
/ccpm:implementation:start $1
|
||||
```
|
||||
|
||||
**If "Break into Sub-Tasks"**:
|
||||
- Create Linear sub-issues for each major component
|
||||
- Link all to parent issue
|
||||
- Add priorities based on spec
|
||||
- Display task breakdown
|
||||
|
||||
**If "Review Specifications"**:
|
||||
- Display Linear Document URL
|
||||
- Show table of contents from spec
|
||||
- Suggest key sections to review
|
||||
|
||||
**If "Assign to Developer"**:
|
||||
- Use Linear MCP to assign issue
|
||||
- Add comment notifying assignee
|
||||
- Link to specifications document
|
||||
|
||||
**If "Continue Planning"**:
|
||||
```bash
|
||||
/ccpm:planning:plan $1
|
||||
```
|
||||
|
||||
### Step 10: Final Summary
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎉 UI Design Complete!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Your UI design has been approved and comprehensive specifications
|
||||
have been generated. Developers now have everything they need to
|
||||
implement this design accurately and consistently.
|
||||
|
||||
📄 **Specification Document**: [Linear Document URL]
|
||||
📋 **Issue**: https://linear.app/workspace/issue/$1
|
||||
🏷️ **Status**: Ready for Development
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Start Implementation: /ccpm:implementation:start $1
|
||||
View Status: /ccpm:utils:status $1
|
||||
Create Sub-Tasks: /ccpm:spec:break-down $1
|
||||
Review Spec: [Linear Document URL]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Specification Quality Standards
|
||||
|
||||
The generated specifications MUST include:
|
||||
|
||||
### 1. Design System Reference (Complete)
|
||||
- ✅ ALL colors with name, hex, Tailwind class, and usage context
|
||||
- ✅ ALL typography styles with exact values
|
||||
- ✅ Complete spacing scale (1-96 in Tailwind units)
|
||||
- ✅ All border configurations
|
||||
- ✅ All shadow definitions
|
||||
- ✅ Exact breakpoint values
|
||||
|
||||
### 2. Component Specifications (Detailed)
|
||||
For EACH component:
|
||||
- ✅ Clear purpose statement
|
||||
- ✅ TypeScript interface with all props
|
||||
- ✅ Complete JSX structure with exact Tailwind classes
|
||||
- ✅ All states (default, hover, active, disabled, focus) with classes
|
||||
- ✅ All variants (primary, secondary, outline, etc.)
|
||||
- ✅ Accessibility requirements (semantic HTML, ARIA attributes)
|
||||
- ✅ Responsive behavior at each breakpoint
|
||||
- ✅ Dark mode variant classes
|
||||
|
||||
### 3. Implementation Guidance (Actionable)
|
||||
- ✅ Step-by-step implementation order
|
||||
- ✅ Code examples for complex patterns
|
||||
- ✅ Common pitfalls to avoid
|
||||
- ✅ Performance optimization tips
|
||||
- ✅ Testing recommendations
|
||||
|
||||
### 4. Accessibility (WCAG 2.1 AA)
|
||||
- ✅ Color contrast requirements met
|
||||
- ✅ Semantic HTML elements specified
|
||||
- ✅ ARIA attributes documented
|
||||
- ✅ Keyboard navigation flow defined
|
||||
- ✅ Focus management strategy
|
||||
- ✅ Screen reader considerations
|
||||
|
||||
### 5. Responsive Design (Mobile-First)
|
||||
- ✅ Mobile layout (default)
|
||||
- ✅ Tablet layout (sm: and md:)
|
||||
- ✅ Desktop layout (lg: and xl:)
|
||||
- ✅ Touch target sizes (44x44px minimum)
|
||||
- ✅ Responsive typography scale
|
||||
|
||||
## Notes
|
||||
|
||||
### This Command Does
|
||||
|
||||
1. ✅ Fetches approved design from Linear
|
||||
2. ✅ Deep analysis of design system (all tokens)
|
||||
3. ✅ Research latest implementation patterns (Context7)
|
||||
4. ✅ Invokes pm:ui-designer agent for comprehensive specs
|
||||
5. ✅ Creates Linear Document with full specifications
|
||||
6. ✅ Updates issue status to "Ready for Development"
|
||||
7. ✅ Offers task breakdown into sub-issues
|
||||
8. ✅ Provides developer handoff checklist
|
||||
9. ✅ Suggests next actions (implementation, review, assignment)
|
||||
|
||||
### Usage Examples
|
||||
|
||||
**Basic usage**:
|
||||
```bash
|
||||
/ccpm:planning:design-approve WORK-123 1
|
||||
```
|
||||
|
||||
**After refinement**:
|
||||
```bash
|
||||
# After running /ccpm:planning:design-refine WORK-123 2
|
||||
/ccpm:planning:design-approve WORK-123 2
|
||||
```
|
||||
|
||||
**Direct approval from design-ui**:
|
||||
```bash
|
||||
# After running /ccpm:planning:design-ui WORK-123
|
||||
# User selected "Approve Option 2"
|
||||
/ccpm:planning:design-approve WORK-123 2
|
||||
```
|
||||
|
||||
### What Gets Generated
|
||||
|
||||
A comprehensive UI specification document (typically 3000-5000 words) that includes:
|
||||
|
||||
- **Complete Design Token Reference**: Every color, font, spacing, shadow
|
||||
- **TypeScript Interfaces**: For every component with all props
|
||||
- **Implementation Code**: JSX structures with exact Tailwind classes
|
||||
- **State Management**: All states (hover, focus, active, disabled)
|
||||
- **Responsive Patterns**: Mobile, tablet, desktop implementations
|
||||
- **Accessibility Guide**: WCAG 2.1 AA compliance steps
|
||||
- **Dark Mode Strategy**: Complete dark: variant mappings
|
||||
- **Animation Specs**: Transitions and micro-interactions
|
||||
- **Component Mapping**: What to reuse vs create new
|
||||
- **Priority Breakdown**: High/Medium/Low implementation order
|
||||
- **Implementation Tips**: Step-by-step guide for developers
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ No ambiguity for developers
|
||||
- ✅ Consistent implementation across team
|
||||
- ✅ Accessibility built-in from start
|
||||
- ✅ Responsive behavior clearly defined
|
||||
- ✅ Dark mode fully specified
|
||||
- ✅ Performance considerations included
|
||||
- ✅ Reduces back-and-forth during development
|
||||
- ✅ Enables parallel development of components
|
||||
- ✅ Complete developer handoff documentation
|
||||
- ✅ Linear Document for permanent reference
|
||||
|
||||
### Quality Assurance
|
||||
|
||||
The pm:ui-designer agent ensures:
|
||||
- ✅ No missing information (if unclear, agent asks for clarification)
|
||||
- ✅ All Tailwind classes are valid and current
|
||||
- ✅ TypeScript interfaces are type-safe
|
||||
- ✅ Accessibility standards are met
|
||||
- ✅ Responsive breakpoints are logical
|
||||
- ✅ Dark mode colors have sufficient contrast
|
||||
- ✅ Component hierarchy makes sense
|
||||
- ✅ Implementation is feasible with existing design system
|
||||
495
commands/planning:design-refine.md
Normal file
495
commands/planning:design-refine.md
Normal file
@@ -0,0 +1,495 @@
|
||||
---
|
||||
description: Refine a UI design option based on user feedback
|
||||
allowed-tools: [Task, LinearMCP, Context7MCP, AskUserQuestion, Read]
|
||||
argument-hint: <issue-id> [option-number] [feedback]
|
||||
---
|
||||
|
||||
# Refining UI Design: $1 - Option $2
|
||||
|
||||
You are **refining a design option** for Linear issue **$1** based on user feedback.
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
This command is **READ-ONLY** for external systems and **WRITE** to Linear (internal tracking).
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1**: Linear issue ID (e.g., WORK-123)
|
||||
- **$2**: Option number to refine (1, 2, or 3) - OPTIONAL if feedback is clear
|
||||
- **$3+**: User feedback describing what to change - OPTIONAL (will prompt if missing)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Current Design Options
|
||||
|
||||
Use **Linear MCP** to get issue details:
|
||||
|
||||
```javascript
|
||||
linear_get_issue({ id: "$1" })
|
||||
```
|
||||
|
||||
Extract:
|
||||
- Previous design options from description
|
||||
- Which option user selected for refinement (if $2 not provided)
|
||||
- Any previous feedback or comments
|
||||
- Original requirements
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔄 Refining Design Option $2 for $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: [Title from Linear]
|
||||
🎨 Refining: Option $2
|
||||
🔗 URL: https://linear.app/workspace/issue/$1
|
||||
|
||||
**Current Design** (Option $2):
|
||||
[Display the selected option's wireframe and description]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 2: Collect Refinement Feedback
|
||||
|
||||
**If $3 (feedback) not provided**, use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "What would you like to change about Option $2?",
|
||||
header: "Changes",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{
|
||||
label: "Layout/Structure",
|
||||
description: "Change how elements are arranged"
|
||||
},
|
||||
{
|
||||
label: "Visual Style",
|
||||
description: "Adjust colors, spacing, shadows"
|
||||
},
|
||||
{
|
||||
label: "Component Choice",
|
||||
description: "Use different components or patterns"
|
||||
},
|
||||
{
|
||||
label: "Responsive Behavior",
|
||||
description: "Change mobile/tablet/desktop layouts"
|
||||
},
|
||||
{
|
||||
label: "Interactions",
|
||||
description: "Modify hover, click, animation effects"
|
||||
},
|
||||
{
|
||||
label: "Content Priority",
|
||||
description: "Emphasize different information"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Please describe the specific changes you want:",
|
||||
header: "Details",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Type custom feedback",
|
||||
description: "I'll describe exactly what I want"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Get detailed feedback**:
|
||||
- What user wants to change
|
||||
- Why the current design doesn't work
|
||||
- Any specific examples or references
|
||||
- Priority of changes (must-have vs nice-to-have)
|
||||
|
||||
**Example feedback types**:
|
||||
- "Make it more minimalist" → Reduce visual weight, increase whitespace
|
||||
- "Add more color" → Introduce color accents, vibrant palette
|
||||
- "Better hierarchy" → Adjust typography, spacing, contrast
|
||||
- "Improve mobile" → Larger touch targets, better stacking
|
||||
- "More modern" → Latest trends, animations, gradients
|
||||
- "Simpler" → Remove complexity, fewer elements
|
||||
- "More professional" → Conservative colors, clean lines
|
||||
|
||||
### Step 3: Analyze Feedback & Plan Changes
|
||||
|
||||
**Parse feedback to identify**:
|
||||
1. **What stays** (elements user likes)
|
||||
2. **What changes** (specific adjustments)
|
||||
3. **What's added** (new elements)
|
||||
4. **What's removed** (unnecessary elements)
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Change Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Feedback**: [User's feedback]
|
||||
|
||||
**Change Type**: [Layout / Visual Style / Component / Responsive / Interaction / Content]
|
||||
|
||||
**Interpretation**:
|
||||
[Explain what changes are needed and why]
|
||||
|
||||
**Planned Adjustments**:
|
||||
✅ Keep: [Elements that stay the same]
|
||||
🔄 Change: [Elements to modify]
|
||||
➕ Add: [New elements]
|
||||
➖ Remove: [Elements to remove]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Research Context (if needed)
|
||||
|
||||
**If feedback requires new patterns**, use **Context7 MCP**:
|
||||
|
||||
```javascript
|
||||
// Example: User wants "more modern card design"
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/shadcn-ui/ui",
|
||||
topic: "modern card component patterns and styles",
|
||||
tokens: 2000
|
||||
})
|
||||
|
||||
// Example: User wants "better mobile navigation"
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/tailwindlabs/tailwindcss",
|
||||
topic: "mobile navigation patterns and responsive menus",
|
||||
tokens: 2000
|
||||
})
|
||||
```
|
||||
|
||||
### Step 5: Invoke pm:ui-designer Agent for Refinement
|
||||
|
||||
**CRITICAL: Invoke the specialist agent**:
|
||||
|
||||
```javascript
|
||||
Task(pm:ui-designer): `
|
||||
Refine design Option $2 for Linear issue $1 based on user feedback.
|
||||
|
||||
**Original Design (Option $2)**:
|
||||
[Include the original wireframe and description]
|
||||
|
||||
**User Feedback**:
|
||||
[User's feedback and requested changes]
|
||||
|
||||
**Change Analysis**:
|
||||
- Keep: [What stays]
|
||||
- Change: [What modifies]
|
||||
- Add: [What's new]
|
||||
- Remove: [What goes]
|
||||
|
||||
**Requirements**:
|
||||
1. Generate refined design that addresses ALL feedback
|
||||
2. Include ASCII wireframe showing the changes
|
||||
3. Explain what changed and why
|
||||
4. Show before/after comparison for major changes
|
||||
5. Maintain consistency with design system
|
||||
6. Preserve what user liked about original
|
||||
7. Ensure changes improve the design, not just differ
|
||||
|
||||
**Additional Context**:
|
||||
- **Frontend Architecture**: [From original design-ui analysis - component patterns, existing components, conventions]
|
||||
- Design System: [From codebase]
|
||||
- Original Requirements: [From issue]
|
||||
- Latest Trends: [From Context7 if fetched]
|
||||
|
||||
**Constraints**:
|
||||
- Must still reuse existing components where possible
|
||||
- Must follow project conventions
|
||||
- Must remain technically feasible
|
||||
- Changes should not conflict with architecture
|
||||
|
||||
**Deliverable**:
|
||||
Present refined design in structured markdown format with:
|
||||
- Updated ASCII wireframe
|
||||
- Description of changes made
|
||||
- Explanation of how feedback was addressed
|
||||
- Before/after comparison (if significant changes)
|
||||
- Updated pros/cons
|
||||
- Technical considerations
|
||||
`
|
||||
```
|
||||
|
||||
**Agent Output Expected**:
|
||||
- Refined design wireframe
|
||||
- Clear explanation of what changed
|
||||
- Before/after comparison
|
||||
- How feedback was addressed
|
||||
- Updated pros/cons
|
||||
|
||||
### Step 6: Present Refined Design
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎨 Refined Design Option $2
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[Agent's refined design output]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Changes Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Addressed Feedback**:
|
||||
✅ [Feedback point 1]: [How it was addressed]
|
||||
✅ [Feedback point 2]: [How it was addressed]
|
||||
✅ [Feedback point 3]: [How it was addressed]
|
||||
|
||||
**What Changed**:
|
||||
- [Change 1 with explanation]
|
||||
- [Change 2 with explanation]
|
||||
- [Change 3 with explanation]
|
||||
|
||||
**What Stayed the Same**:
|
||||
- [Element 1 - kept because it worked]
|
||||
- [Element 2 - user liked it]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 7: Collect User Feedback on Refinement
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Does this refined design meet your needs?",
|
||||
header: "Approval",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "⭐ Approve this design",
|
||||
description: "Perfect! Generate full UI specifications"
|
||||
},
|
||||
{
|
||||
label: "Needs further refinement",
|
||||
description: "Close, but I want more adjustments"
|
||||
},
|
||||
{
|
||||
label: "Go back to original",
|
||||
description: "The original Option $2 was better"
|
||||
},
|
||||
{
|
||||
label: "Try different approach",
|
||||
description: "Let's explore a completely different direction"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 8: Handle User Response
|
||||
|
||||
**If user approves**:
|
||||
- Jump to `/ccpm:planning:design-approve $1 $2-refined`
|
||||
- Generate full UI specifications
|
||||
|
||||
**If user wants further refinement**:
|
||||
- Prompt: "What else would you like to adjust?"
|
||||
- Store additional feedback
|
||||
- Return to Step 5 with cumulative feedback
|
||||
|
||||
**If user wants original**:
|
||||
- Restore original Option $2 design
|
||||
- Ask: "What about the original did you prefer?"
|
||||
- Consider showing both side-by-side
|
||||
|
||||
**If user wants different approach**:
|
||||
- Jump back to `/ccpm:planning:design-ui $1`
|
||||
- Start fresh with new direction
|
||||
|
||||
### Step 9: Update Linear Issue
|
||||
|
||||
Use **Linear MCP** to update with refinement progress:
|
||||
|
||||
```javascript
|
||||
linear_update_issue({
|
||||
id: "$1",
|
||||
description: "[Existing description with design options]\n\n---\n\n## 🔄 Design Refinement (Option $2)\n\n**User Feedback**: [Feedback]\n\n**Refined Design**:\n[Refined wireframe and description]\n\n**Changes Made**: [List of changes]\n\n**Status**: [Awaiting approval / Approved / Needs more refinement]"
|
||||
})
|
||||
|
||||
// If approved, add label
|
||||
linear_update_issue({
|
||||
id: "$1",
|
||||
labels: ["design-approved"]
|
||||
})
|
||||
```
|
||||
|
||||
### Step 10: Show Status & Next Actions
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Design Refinement Complete
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: $1
|
||||
🎨 Refined: Option $2
|
||||
🔗 Linear: https://linear.app/workspace/issue/$1
|
||||
🏷️ Status: [design-approved / design-in-progress]
|
||||
|
||||
📝 Changes Made: [X] adjustments
|
||||
✅ Feedback Addressed: [Y] points
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[If approved]:
|
||||
Perfect! I'll now generate comprehensive UI specifications including:
|
||||
- Component breakdown with TypeScript interfaces
|
||||
- Tailwind class mappings
|
||||
- Responsive behavior documentation
|
||||
- Accessibility guidelines
|
||||
- Dark mode implementation
|
||||
- Animation and interaction specs
|
||||
|
||||
[If needs more refinement]:
|
||||
I can refine further. Just describe what else needs adjusting.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Approve: /ccpm:planning:design-approve $1 $2
|
||||
Refine more: /ccpm:planning:design-refine $1 $2 [new feedback]
|
||||
Status: /ccpm:utils:status $1
|
||||
Start over: /ccpm:planning:design-ui $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Refinement Patterns
|
||||
|
||||
### Common Feedback Types
|
||||
|
||||
**"Make it more minimalist"**:
|
||||
- Increase whitespace (gap-6 → gap-8)
|
||||
- Reduce visual elements (remove borders, shadows)
|
||||
- Simplify color palette (fewer accent colors)
|
||||
- Use more subtle typography (lighter weights)
|
||||
|
||||
**"Add more color/personality"**:
|
||||
- Introduce accent colors (primary, secondary)
|
||||
- Add gradient backgrounds
|
||||
- Use colorful icons or illustrations
|
||||
- Implement subtle animations
|
||||
|
||||
**"Improve information hierarchy"**:
|
||||
- Increase heading size/weight
|
||||
- Add visual separators (borders, backgrounds)
|
||||
- Use color to emphasize important elements
|
||||
- Adjust spacing to group related items
|
||||
|
||||
**"Better mobile experience"**:
|
||||
- Stack elements vertically
|
||||
- Increase touch target sizes (min 44x44px)
|
||||
- Simplify navigation (hamburger menu)
|
||||
- Reduce content density
|
||||
|
||||
**"More modern/trendy"**:
|
||||
- Use latest component patterns (from Context7)
|
||||
- Add micro-interactions (hover effects, transitions)
|
||||
- Implement glass-morphism or neumorphism
|
||||
- Use modern typography (variable fonts)
|
||||
|
||||
**"Simpler/Less complex"**:
|
||||
- Remove secondary features
|
||||
- Combine similar elements
|
||||
- Reduce the number of sections
|
||||
- Use progressive disclosure (hide details)
|
||||
|
||||
**"More professional/corporate"**:
|
||||
- Use conservative colors (blues, grays)
|
||||
- Formal typography (sans-serif, medium weights)
|
||||
- Grid-based layouts
|
||||
- Subtle effects only
|
||||
|
||||
### Iteration Strategy
|
||||
|
||||
**First Refinement** (this command):
|
||||
- Address primary feedback
|
||||
- Make targeted changes
|
||||
- Preserve what worked
|
||||
|
||||
**Second Refinement** (if needed):
|
||||
- Fine-tune details
|
||||
- Adjust based on cumulative feedback
|
||||
- Consider showing multiple micro-variants
|
||||
|
||||
**Third+ Refinement** (rarely needed):
|
||||
- If still not right, consider:
|
||||
- Going back to original options
|
||||
- Generating completely new options
|
||||
- Having a design discussion about requirements
|
||||
|
||||
## Notes
|
||||
|
||||
### This Command Does
|
||||
|
||||
1. ✅ Fetches current design options from Linear
|
||||
2. ✅ Collects specific refinement feedback
|
||||
3. ✅ Analyzes what needs to change vs what stays
|
||||
4. ✅ Researches new patterns if needed (Context7)
|
||||
5. ✅ Invokes pm:ui-designer agent for expert refinement
|
||||
6. ✅ Presents refined design with before/after comparison
|
||||
7. ✅ Explains how feedback was addressed
|
||||
8. ✅ Handles approval or further iteration
|
||||
9. ✅ Updates Linear with refinement progress
|
||||
|
||||
### Usage Examples
|
||||
|
||||
**Basic usage** (will prompt for feedback):
|
||||
```bash
|
||||
/ccpm:planning:design-refine WORK-123 2
|
||||
```
|
||||
|
||||
**With inline feedback**:
|
||||
```bash
|
||||
/ccpm:planning:design-refine WORK-123 2 "Make it more minimalist with larger spacing"
|
||||
```
|
||||
|
||||
**After design-ui command**:
|
||||
```bash
|
||||
# After running /ccpm:planning:design-ui WORK-123
|
||||
# User selected "Refine Option 2"
|
||||
/ccpm:planning:design-refine WORK-123 2
|
||||
```
|
||||
|
||||
**Multiple refinements**:
|
||||
```bash
|
||||
# First refinement
|
||||
/ccpm:planning:design-refine WORK-123 2 "Add more color"
|
||||
|
||||
# After reviewing, second refinement
|
||||
/ccpm:planning:design-refine WORK-123 2 "Good but reduce the amount of purple"
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ Targeted refinements without starting over
|
||||
- ✅ Preserves what user liked
|
||||
- ✅ Clear before/after comparison
|
||||
- ✅ Explains how feedback was addressed
|
||||
- ✅ Iterative improvement workflow
|
||||
- ✅ Context-aware changes using Context7
|
||||
- ✅ Expert refinement from pm:ui-designer agent
|
||||
- ✅ Linear tracking of all refinements
|
||||
768
commands/planning:design-ui.md
Normal file
768
commands/planning:design-ui.md
Normal file
@@ -0,0 +1,768 @@
|
||||
---
|
||||
description: Start UI design process with multiple design options and wireframes
|
||||
allowed-tools: [Task, LinearMCP, Context7MCP, AskUserQuestion, Read, Grep, Glob]
|
||||
argument-hint: <issue-id>
|
||||
---
|
||||
|
||||
# UI Design Planning: $1
|
||||
|
||||
You are initiating the **UI Design Planning Phase** for Linear issue **$1**.
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
This command is **READ-ONLY** for external systems and **WRITE** to Linear (internal tracking).
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Linear Issue Context
|
||||
|
||||
Use **Linear MCP** to get issue details:
|
||||
|
||||
```javascript
|
||||
linear_get_issue({ id: "$1" })
|
||||
```
|
||||
|
||||
Extract:
|
||||
- Issue title and description
|
||||
- Attachments (screenshots, references)
|
||||
- Comments with design feedback
|
||||
- Related issues
|
||||
- Project and team context
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎨 Starting UI Design for $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: [Title from Linear]
|
||||
📝 Description: [Brief summary]
|
||||
🔗 URL: https://linear.app/workspace/issue/$1
|
||||
📎 Attachments: [X] screenshot(s) found
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 1.5: Analyze Reference Mockups
|
||||
|
||||
**READ**: `commands/_shared-image-analysis.md`
|
||||
|
||||
If the user has attached reference mockups or screenshots, analyze them before generating design options:
|
||||
|
||||
```javascript
|
||||
const images = detectImages(issue)
|
||||
if (images.length > 0) {
|
||||
console.log("📎 Found " + images.length + " reference image(s)")
|
||||
|
||||
// Analyze each reference
|
||||
const references = []
|
||||
for (const img of images) {
|
||||
const prompt = generateImagePrompt(img.title, "design reference")
|
||||
const analysis = fetchAndAnalyzeImage(img.url, prompt)
|
||||
references.push({ ...img, analysis })
|
||||
}
|
||||
|
||||
// Use analysis to inform design options
|
||||
console.log("✅ Reference analysis complete")
|
||||
console.log("Using design patterns and elements from references...")
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Understand user's design preferences and expectations
|
||||
- Identify design patterns and elements to incorporate
|
||||
- Extract colors, typography, and layout preferences
|
||||
- Ensure design options align with provided references
|
||||
|
||||
**Note**: If no references found, proceed with Step 1.6 to check for Figma designs.
|
||||
|
||||
### Step 1.6: Detect and Analyze Figma Designs
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
Before asking user requirements, check if there are Figma design links in the Linear issue:
|
||||
|
||||
```bash
|
||||
# 1. Extract Linear description and comments
|
||||
LINEAR_DESC=$(linear_get_issue "$1" | jq -r '.description')
|
||||
LINEAR_COMMENTS=$(linear_get_issue "$1" | jq -r '.comments[]? | .body' | tr '\n' ' ' || echo "")
|
||||
|
||||
# 2. Detect Figma links
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC $LINEAR_COMMENTS")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -gt 0 ]; then
|
||||
echo "🎨 Found $FIGMA_COUNT Figma design(s) - analyzing..."
|
||||
|
||||
# 3. Select MCP server for project
|
||||
PROJECT_ID=$(linear_get_issue "$1" | jq -r '.projectId // ""')
|
||||
FIGMA_SERVER=$(./scripts/figma-server-manager.sh select "$PROJECT_ID")
|
||||
|
||||
if [ -n "$FIGMA_SERVER" ]; then
|
||||
# 4. Extract and analyze each Figma design
|
||||
echo "$FIGMA_LINKS" | jq -c '.[]' | while read -r link; do
|
||||
FILE_ID=$(echo "$link" | jq -r '.file_id')
|
||||
FILE_NAME=$(echo "$link" | jq -r '.file_name')
|
||||
|
||||
echo " 📐 Analyzing: $FILE_NAME"
|
||||
|
||||
# Generate MCP call for data extraction
|
||||
MCP_INSTRUCTION=$(./scripts/figma-data-extractor.sh extract "$FILE_ID" "$FIGMA_SERVER")
|
||||
|
||||
# Claude should execute the MCP call here
|
||||
# FIGMA_DATA=$(execute MCP call based on MCP_INSTRUCTION)
|
||||
|
||||
# Then analyze the design data
|
||||
# DESIGN_SYSTEM=$(echo "$FIGMA_DATA" | ./scripts/figma-design-analyzer.sh generate -)
|
||||
|
||||
# Cache in Linear for future use
|
||||
# ./scripts/figma-cache-manager.sh store "$1" "$FILE_ID" "$FILE_NAME" "$(echo "$link" | jq -r '.url')" "$FIGMA_SERVER" "$DESIGN_SYSTEM"
|
||||
done
|
||||
|
||||
echo " ✅ Figma design analysis complete"
|
||||
echo " 💡 Design system extracted - will inform UI design options"
|
||||
else
|
||||
echo " ⚠️ No Figma MCP server configured - skipping automated extraction"
|
||||
echo " ℹ️ You can still manually reference Figma links: $(echo "$FIGMA_LINKS" | jq -r '.[0].url')"
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
**What this enables:**
|
||||
- **Automatic Design System Detection**: Extract colors, fonts, spacing from Figma
|
||||
- **Component Analysis**: Understand existing component library
|
||||
- **Design Token Mapping**: Map Figma styles to Tailwind classes
|
||||
- **Consistency**: Ensure generated designs match existing design system
|
||||
|
||||
**Fallback**: If no Figma MCP server or extraction fails, proceed with Step 2 user questions.
|
||||
|
||||
**Note**: Figma design data is cached in Linear comments for reuse during implementation.
|
||||
|
||||
|
||||
### Step 2: Gather User Requirements
|
||||
|
||||
Use **AskUserQuestion** to collect design requirements:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "What is the primary goal of this UI?",
|
||||
header: "Goal",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Display information",
|
||||
description: "Show data to users (dashboard, profile, list)"
|
||||
},
|
||||
{
|
||||
label: "Collect input",
|
||||
description: "Forms, settings, creation flows"
|
||||
},
|
||||
{
|
||||
label: "Enable actions",
|
||||
description: "Buttons, controls, interactive elements"
|
||||
},
|
||||
{
|
||||
label: "Navigate content",
|
||||
description: "Menus, tabs, navigation bars"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What devices should this design prioritize?",
|
||||
header: "Devices",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{
|
||||
label: "Mobile (phones)",
|
||||
description: "320px-639px, touch-first"
|
||||
},
|
||||
{
|
||||
label: "Tablet",
|
||||
description: "640px-1023px, hybrid input"
|
||||
},
|
||||
{
|
||||
label: "Desktop",
|
||||
description: "1024px+, mouse/keyboard"
|
||||
},
|
||||
{
|
||||
label: "All equally",
|
||||
description: "Fully responsive across all devices"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What aesthetic are you aiming for?",
|
||||
header: "Aesthetic",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Minimal & Clean",
|
||||
description: "Generous whitespace, subtle colors, simple"
|
||||
},
|
||||
{
|
||||
label: "Bold & Vibrant",
|
||||
description: "Strong colors, high contrast, energetic"
|
||||
},
|
||||
{
|
||||
label: "Professional",
|
||||
description: "Corporate, trustworthy, conventional"
|
||||
},
|
||||
{
|
||||
label: "Modern & Trendy",
|
||||
description: "Latest design trends, cutting-edge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Any specific design constraints or preferences?",
|
||||
header: "Constraints",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{
|
||||
label: "Must match existing pages",
|
||||
description: "Consistency with current design system"
|
||||
},
|
||||
{
|
||||
label: "Accessibility is critical",
|
||||
description: "WCAG AAA compliance, screen reader support"
|
||||
},
|
||||
{
|
||||
label: "Performance matters",
|
||||
description: "Fast loading, minimal animations"
|
||||
},
|
||||
{
|
||||
label: "Dark mode required",
|
||||
description: "Must support dark mode from start"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Document Responses**:
|
||||
- Store all answers for design generation
|
||||
- Note any "Other" responses with custom text
|
||||
- Identify priority constraints
|
||||
|
||||
### Step 3: Frontend Architecture Analysis (CRITICAL)
|
||||
|
||||
**ALWAYS collaborate with frontend agent FIRST** to understand:
|
||||
|
||||
**Invoke Frontend Agent**:
|
||||
|
||||
```javascript
|
||||
// The smart-agent-selector will automatically choose the right frontend agent
|
||||
Task(frontend-mobile-development:frontend-developer): `
|
||||
Analyze the current frontend architecture and patterns to inform UI design for [feature name from Linear issue].
|
||||
|
||||
**Analysis Needed**:
|
||||
1. Component architecture patterns (atomic design, feature-based, etc.)
|
||||
2. State management approach (TanStack Query, Context, Redux, Zustand)
|
||||
3. Styling patterns (Tailwind classes, CSS-in-JS, etc.)
|
||||
4. Existing reusable components (list with file paths)
|
||||
5. Component composition patterns
|
||||
6. Data flow patterns (props, Context, state management)
|
||||
7. Routing/navigation conventions
|
||||
8. Performance patterns (lazy loading, memoization)
|
||||
9. Accessibility patterns (existing implementations)
|
||||
10. Technical constraints (platform limitations, performance budgets)
|
||||
|
||||
**Deliverable**:
|
||||
Comprehensive frontend context document that covers:
|
||||
- What component patterns are used
|
||||
- Which components can be reused
|
||||
- What conventions to follow
|
||||
- What technical constraints exist
|
||||
- What performance considerations apply
|
||||
`
|
||||
|
||||
// OR if React Native/Mobile project
|
||||
Task(frontend-mobile-development:mobile-developer): `
|
||||
[Same analysis adapted for React Native/mobile patterns]
|
||||
`
|
||||
```
|
||||
|
||||
**Expected Output**:
|
||||
- Component architecture documentation
|
||||
- List of reusable components with paths
|
||||
- Project conventions and patterns
|
||||
- Technical constraints
|
||||
- Performance considerations
|
||||
|
||||
Display summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🏗️ Frontend Architecture Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Component Patterns**: [Description from frontend agent]
|
||||
**State Management**: [Approach]
|
||||
**Styling**: [Method]
|
||||
**Existing Components**: [X] reusable components found
|
||||
- [Component 1] at [path]
|
||||
- [Component 2] at [path]
|
||||
- [...]
|
||||
|
||||
**Technical Constraints**:
|
||||
- [Constraint 1]
|
||||
- [Constraint 2]
|
||||
|
||||
**Conventions to Follow**:
|
||||
- [Convention 1]
|
||||
- [Convention 2]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**Why This Matters**:
|
||||
- Ensures designs fit existing technical patterns
|
||||
- Maximizes component reuse (saves development time)
|
||||
- Avoids proposing infeasible implementations
|
||||
- Maintains consistency with current architecture
|
||||
- Reduces back-and-forth during implementation
|
||||
|
||||
### Step 4: Analyze Existing Design System
|
||||
|
||||
**Scan codebase for design configurations**:
|
||||
|
||||
Use **Glob** and **Read** to find:
|
||||
|
||||
```bash
|
||||
# Tailwind configuration
|
||||
tailwind.config.{js,ts}
|
||||
|
||||
# NativeWind configuration
|
||||
nativewind.config.js
|
||||
|
||||
# shadcn-ui configuration
|
||||
components.json
|
||||
|
||||
# Global styles
|
||||
globals.css
|
||||
index.css
|
||||
app.css
|
||||
|
||||
# Theme files
|
||||
**/theme/**/*.{ts,js}
|
||||
**/styles/**/*.{ts,js}
|
||||
```
|
||||
|
||||
**Extract Design Tokens**:
|
||||
- **Colors**: Primary, secondary, accent, semantic colors
|
||||
- **Typography**: Font families, sizes, weights
|
||||
- **Spacing**: Padding/margin scale
|
||||
- **Borders**: Radius, widths
|
||||
- **Shadows**: Box shadow configurations
|
||||
- **Breakpoints**: Mobile, tablet, desktop
|
||||
|
||||
**Find Existing Components**:
|
||||
|
||||
```bash
|
||||
# Search for component directories
|
||||
components/ui/*.{tsx,ts}
|
||||
components/primitives/*.{tsx,ts}
|
||||
src/components/**/*.{tsx,ts}
|
||||
```
|
||||
|
||||
Document:
|
||||
- Available shadcn-ui components
|
||||
- Custom components
|
||||
- Reusable primitives
|
||||
- Component patterns used
|
||||
|
||||
Display summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Design System Detected
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Framework**: [Tailwind CSS / NativeWind]
|
||||
**Components**: [shadcn-ui / reactreusables / Custom]
|
||||
|
||||
**Colors**:
|
||||
- Primary: #2563eb (blue-600)
|
||||
- Secondary: #4b5563 (gray-600)
|
||||
- Accent: #a855f7 (purple-500)
|
||||
[...]
|
||||
|
||||
**Typography**:
|
||||
- Font: Inter
|
||||
- Base: 16px (text-base)
|
||||
- Scale: 12px, 14px, 16px, 20px, 24px, 30px, 36px
|
||||
|
||||
**Spacing**: 4px grid (Tailwind default)
|
||||
|
||||
**Components Found**: [X] components
|
||||
- Button, Card, Input, Dialog, [...]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 5: Research Current Design Trends
|
||||
|
||||
**CRITICAL: Use Context7 MCP for latest documentation**:
|
||||
|
||||
```javascript
|
||||
// Resolve library IDs first
|
||||
resolve-library-id({ libraryName: "tailwind" })
|
||||
resolve-library-id({ libraryName: "shadcn-ui" })
|
||||
resolve-library-id({ libraryName: "nativewind" })
|
||||
|
||||
// Fetch current best practices
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/tailwindlabs/tailwindcss",
|
||||
topic: "modern UI design patterns and components",
|
||||
tokens: 3000
|
||||
})
|
||||
|
||||
get-library-docs({
|
||||
context7CompatibleLibraryID: "/shadcn-ui/ui",
|
||||
topic: "component composition and design patterns",
|
||||
tokens: 2000
|
||||
})
|
||||
```
|
||||
|
||||
**Research Topics**:
|
||||
- Latest component patterns (cards, navigation, forms)
|
||||
- Modern color trends and dark mode strategies
|
||||
- Typography best practices
|
||||
- Accessibility guidelines (ARIA, semantic HTML)
|
||||
- Mobile-first design patterns
|
||||
- Animation and transition trends
|
||||
|
||||
**Summarize Findings**:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Current Design Trends (from Context7)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Layout Trends**:
|
||||
- Card-based layouts with subtle shadows
|
||||
- Generous whitespace (8px grid)
|
||||
- Bento-style grids (Pinterest-like)
|
||||
|
||||
**Color Trends**:
|
||||
- Neutral-based palettes with vibrant accents
|
||||
- Dark mode as default consideration
|
||||
- 60-30-10 rule (60% neutral, 30% secondary, 10% accent)
|
||||
|
||||
**Typography**:
|
||||
- Large, bold headings (36px+)
|
||||
- Readable body text (16px minimum)
|
||||
- Variable fonts for performance
|
||||
|
||||
**Interactions**:
|
||||
- Micro-animations (scale, fade)
|
||||
- Hover lift effects (translate-y)
|
||||
- Smooth transitions (200-300ms)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 6: Invoke pm:ui-designer Agent
|
||||
|
||||
**CRITICAL: Invoke the specialist agent to generate design options**:
|
||||
|
||||
```javascript
|
||||
Task(pm:ui-designer): `
|
||||
Generate UI design options for Linear issue $1.
|
||||
|
||||
**Context**:
|
||||
- Issue: [Title and description]
|
||||
- User Requirements: [From AskUserQuestion responses]
|
||||
- Design System: [Detected design tokens and components]
|
||||
- Design Trends: [From Context7 research]
|
||||
- Screenshot References: [Describe any attachments found]
|
||||
|
||||
**Requirements**:
|
||||
1. Generate 2-3 design options
|
||||
2. Include ASCII wireframes for each option
|
||||
3. Provide detailed descriptions with pros/cons
|
||||
4. Consider responsive behavior (mobile, tablet, desktop)
|
||||
5. Ensure accessibility (WCAG 2.1 AA minimum)
|
||||
6. Map to existing component library where possible
|
||||
7. Follow detected design system strictly
|
||||
|
||||
**Deliverable**:
|
||||
Present design options in structured markdown format with:
|
||||
- ASCII wireframes
|
||||
- Design descriptions
|
||||
- Pros and cons
|
||||
- Technical considerations
|
||||
- Component mapping
|
||||
`
|
||||
```
|
||||
|
||||
**Agent Output Expected**:
|
||||
- 2-3 design options with wireframes
|
||||
- Detailed pros/cons for each
|
||||
- Technical complexity assessment
|
||||
- Component breakdown (including reuse of existing components)
|
||||
- Accessibility considerations
|
||||
- Architecture alignment notes
|
||||
|
||||
### Step 7: Present Design Options to User
|
||||
|
||||
**Display agent output**:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎨 Design Options Generated
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[Agent's design options output here]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Collect User Feedback
|
||||
|
||||
Use **AskUserQuestion** for design selection:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Which design option do you prefer?",
|
||||
header: "Design Choice",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "⭐ Option 1: [Name]",
|
||||
description: "[Brief summary from agent output]"
|
||||
},
|
||||
{
|
||||
label: "Option 2: [Name]",
|
||||
description: "[Brief summary from agent output]"
|
||||
},
|
||||
{
|
||||
label: "Option 3: [Name]",
|
||||
description: "[Brief summary from agent output]"
|
||||
},
|
||||
{
|
||||
label: "Refine Option 1",
|
||||
description: "I like Option 1 but want to make changes"
|
||||
},
|
||||
{
|
||||
label: "Refine Option 2",
|
||||
description: "I like Option 2 but want to make changes"
|
||||
},
|
||||
{
|
||||
label: "Refine Option 3",
|
||||
description: "I like Option 3 but want to make changes"
|
||||
},
|
||||
{
|
||||
label: "Need different options",
|
||||
description: "Show me completely different approaches"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 9: Handle User Selection
|
||||
|
||||
**If user approves an option** (Option 1, 2, or 3):
|
||||
- Jump to `/ccpm:planning:design-approve $1 [option-number]`
|
||||
- This will generate full UI specifications
|
||||
|
||||
**If user wants refinement**:
|
||||
- Prompt for feedback: "What would you like to change about [Option X]?"
|
||||
- Store feedback
|
||||
- Jump to `/ccpm:planning:design-refine $1 [option-number] [feedback]`
|
||||
|
||||
**If user wants different options**:
|
||||
- Ask: "What kind of approach are you looking for?"
|
||||
- Restart from Step 6 with new direction
|
||||
|
||||
### Step 10: Update Linear Issue
|
||||
|
||||
Use **Linear MCP** to update issue with design progress:
|
||||
|
||||
```javascript
|
||||
linear_update_issue({
|
||||
id: "$1",
|
||||
labels: ["design-in-progress"], // Add label
|
||||
description: "[Existing description]\n\n---\n\n## 🎨 UI Design Options\n\n[Design options generated by agent]\n\n**User Feedback**: [Awaiting selection / Selected Option X / Requested refinement]"
|
||||
})
|
||||
```
|
||||
|
||||
### Step 11: Show Status & Next Actions
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ UI Design Options Presented
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: $1
|
||||
🎨 Design Options: [X] generated
|
||||
🔗 Linear: https://linear.app/workspace/issue/$1
|
||||
🏷️ Status: design-in-progress
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Once you've selected a design option above, I'll:
|
||||
1. Generate comprehensive UI specifications
|
||||
2. Create component breakdown with TypeScript interfaces
|
||||
3. Document Tailwind classes and responsive behavior
|
||||
4. Add accessibility and dark mode guidelines
|
||||
5. Prepare developer handoff documentation
|
||||
|
||||
Or you can:
|
||||
- Refine an option (select "Refine Option X" above)
|
||||
- Request different approaches (select "Need different options")
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Refine: /ccpm:planning:design-refine $1 [option-number]
|
||||
Approve: /ccpm:planning:design-approve $1 [option-number]
|
||||
Status: /ccpm:utils:status $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### This Command Does
|
||||
|
||||
1. ✅ Fetches Linear issue context and attachments
|
||||
2. ✅ Gathers user requirements interactively
|
||||
3. ✅ **Invokes frontend agent to analyze architecture and patterns** (CRITICAL)
|
||||
4. ✅ Analyzes existing design system automatically
|
||||
5. ✅ Researches latest design trends via Context7
|
||||
6. ✅ Invokes pm:ui-designer agent with frontend context for expert design options
|
||||
7. ✅ Ensures designs reuse existing components and follow project conventions
|
||||
8. ✅ Presents 2-3 design options with wireframes
|
||||
9. ✅ Collects user feedback interactively
|
||||
10. ✅ Updates Linear with design progress
|
||||
11. ✅ Suggests next actions
|
||||
|
||||
### Usage Examples
|
||||
|
||||
**Basic usage**:
|
||||
```bash
|
||||
/ccpm:planning:design-ui WORK-123
|
||||
```
|
||||
|
||||
**With existing Linear issue**:
|
||||
```bash
|
||||
# Issue already has description and requirements
|
||||
/ccpm:planning:design-ui WORK-456
|
||||
```
|
||||
|
||||
**After spec is written**:
|
||||
```bash
|
||||
# Use after /ccpm:spec:write to design the UI
|
||||
/ccpm:planning:design-ui WORK-789
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ Interactive requirements gathering
|
||||
- ✅ **Frontend architecture analysis ensures technical feasibility**
|
||||
- ✅ **Maximizes reuse of existing components**
|
||||
- ✅ **Designs follow project conventions automatically**
|
||||
- ✅ Automatic design system detection
|
||||
- ✅ Latest design trends via Context7
|
||||
- ✅ Expert design options from pm:ui-designer agent
|
||||
- ✅ Visual wireframes (ASCII art)
|
||||
- ✅ Comprehensive pros/cons analysis
|
||||
- ✅ Seamless flow to refinement or approval
|
||||
- ✅ Linear integration for tracking
|
||||
- ✅ **Reduces implementation time and back-and-forth**
|
||||
|
||||
### Step 1.6: Detect Figma Design Links
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
Check if the Linear issue or related documentation contains Figma design links:
|
||||
|
||||
```bash
|
||||
# Detect Figma links from Linear issue
|
||||
LINEAR_DESC=$(linear_get_issue "$1" | jq -r '.description')
|
||||
LINEAR_COMMENTS=$(linear_get_issue "$1" | jq -r '.comments[] | .body')
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC $LINEAR_COMMENTS")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -gt 0 ]; then
|
||||
echo "✅ Found $FIGMA_COUNT Figma design(s) - will use as authoritative source"
|
||||
FIGMA_AVAILABLE=true
|
||||
|
||||
# Display detected Figma links
|
||||
echo "🎨 Figma Designs:"
|
||||
echo "$FIGMA_LINKS" | jq -r '.[] | " - \(.file_name): \(.canonical_url)"'
|
||||
|
||||
# Determine workflow mode
|
||||
DESIGN_MODE="figma-based" # Use Figma as primary source
|
||||
else
|
||||
echo "ℹ️ No Figma links found - will generate design options from requirements"
|
||||
FIGMA_AVAILABLE=false
|
||||
DESIGN_MODE="generative" # Generate design options
|
||||
fi
|
||||
```
|
||||
|
||||
**Workflow Decision Tree**
|
||||
|
||||
Based on Figma availability, choose the appropriate workflow:
|
||||
|
||||
```javascript
|
||||
if (figmaAvailable) {
|
||||
// MODE: Figma-Based Design
|
||||
// Skip Step 2 (user requirements) - extract from Figma instead
|
||||
// Modify Step 6 to use Figma data instead of generating wireframes
|
||||
|
||||
console.log("📋 Workflow: Figma-Based Design")
|
||||
console.log(" ✓ Skip requirements gathering (extract from Figma)")
|
||||
console.log(" ✓ Load Figma designs via MCP (Phase 2)")
|
||||
console.log(" ✓ Generate specifications from Figma data")
|
||||
console.log(" ✓ Create developer-ready component breakdown")
|
||||
|
||||
} else {
|
||||
// MODE: Generative Design
|
||||
// Continue with Step 2 (gather requirements)
|
||||
// Continue with Step 6 (generate ASCII wireframes)
|
||||
|
||||
console.log("📋 Workflow: Generative Design")
|
||||
console.log(" ✓ Gather user requirements")
|
||||
console.log(" ✓ Analyze existing codebase patterns")
|
||||
console.log(" ✓ Generate multiple design options")
|
||||
console.log(" ✓ Create ASCII wireframes for approval")
|
||||
}
|
||||
```
|
||||
|
||||
**Modified Step 2 (Conditional)**
|
||||
|
||||
```javascript
|
||||
if (designMode === "figma-based") {
|
||||
console.log("⏭️ Skipping requirements gathering - using Figma as source")
|
||||
// Jump to Step 3: Analyze Frontend Architecture
|
||||
} else {
|
||||
// Execute original Step 2: Gather User Requirements
|
||||
// (AskUserQuestion for goal, devices, aesthetic, constraints)
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Matters**:
|
||||
- **Design Authority**: If Figma exists, it's the authoritative design specification
|
||||
- **Efficiency**: Skip manual requirements gathering when design already exists
|
||||
- **Accuracy**: Extract actual design data instead of making assumptions
|
||||
- **Consistency**: Ensure implementation matches approved designs
|
||||
|
||||
**Phase 1 vs Phase 2**:
|
||||
- **Phase 1 (Current)**: Detect Figma links, skip ASCII wireframes if Figma found
|
||||
- **Phase 2 (Future)**: Extract design data from Figma via MCP, generate component specs
|
||||
|
||||
187
commands/planning:plan.md
Normal file
187
commands/planning:plan.md
Normal file
@@ -0,0 +1,187 @@
|
||||
---
|
||||
description: Plan a task - gather context from Jira/Confluence/Slack, analyze codebase, update Linear issue with research and checklist
|
||||
allowed-tools: [Bash, LinearMCP, AtlassianMCP, SlackMCP, PlaywrightMCP, Context7MCP]
|
||||
argument-hint: <linear-issue-id> <jira-ticket-id>
|
||||
---
|
||||
|
||||
# Planning Task: Linear $1 (Jira: $2)
|
||||
|
||||
You are starting the **Planning Phase** for Linear issue **$1** based on Jira ticket **$2**.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **READ-ONLY** operations are permitted (fetch, search, view)
|
||||
- ⛔ **WRITE operations** require user confirmation
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
|
||||
When in doubt, ASK before posting anything externally.
|
||||
|
||||
## Project Configuration
|
||||
|
||||
**IMPORTANT**: This command uses dynamic project configuration from `~/.claude/ccpm-config.yaml`.
|
||||
|
||||
## Planning Workflow
|
||||
|
||||
### Step 0: Fetch Existing Linear Issue & Load Project Config
|
||||
|
||||
Use **Linear MCP** to:
|
||||
|
||||
1. Get issue details for: $1
|
||||
2. Read current title, description, and any existing context
|
||||
3. **Determine the project from the Linear issue** (team/project mapping)
|
||||
4. Extract any existing Jira ticket reference (if not provided as $2)
|
||||
|
||||
**Load Project Configuration:**
|
||||
|
||||
```bash
|
||||
# Get project ID from Linear issue's team/project
|
||||
# Map Linear team+project to project ID in config
|
||||
|
||||
# Example: If Linear shows "Work / My App"
|
||||
# Search config for matching linear.team="Work" and linear.project="My App"
|
||||
|
||||
# Load project config
|
||||
PROJECT_ARG=$(determine_project_from_linear_issue "$1")
|
||||
```
|
||||
|
||||
**LOAD PROJECT CONFIG**: Follow instructions in `commands/_shared-project-config-loader.md`
|
||||
|
||||
After loading, you'll have:
|
||||
- `${EXTERNAL_PM_ENABLED}` - Whether to query Jira/Confluence/Slack
|
||||
- `${EXTERNAL_PM_TYPE}` - Type of external PM
|
||||
- `${JIRA_ENABLED}`, `${CONFLUENCE_ENABLED}`, `${SLACK_ENABLED}`
|
||||
- All other project settings
|
||||
|
||||
If $2 (Jira ticket ID) is not provided:
|
||||
|
||||
- Check Linear description for Jira ticket reference
|
||||
- If no Jira ticket found, ask user for Jira ticket ID or proceed without external PM research
|
||||
|
||||
### Step 0.5+: Execute Shared Planning Workflow
|
||||
|
||||
**READ**: `commands/_shared-planning-workflow.md`
|
||||
|
||||
Execute the shared planning workflow to handle all planning steps systematically.
|
||||
|
||||
**Set required context variables:**
|
||||
- `LINEAR_ISSUE_ID` = $1 (the Linear issue to plan)
|
||||
- `JIRA_TICKET_ID` = $2 (optional, can be extracted from Linear issue)
|
||||
- `PROJECT_CONFIG` = [loaded from Step 0]
|
||||
- `EXTERNAL_PM_ENABLED` = [from config]
|
||||
- `EXTERNAL_PM_TYPE` = [from config]
|
||||
- `JIRA_ENABLED`, `CONFLUENCE_ENABLED`, `SLACK_ENABLED` = [from config]
|
||||
|
||||
**Execute these steps from the shared workflow:**
|
||||
|
||||
1. **Step 0.5**: Detect and analyze images in the Linear issue
|
||||
- Uses `commands/_shared-image-analysis.md` logic
|
||||
- Finds UI mockups, diagrams, screenshots
|
||||
- Analyzes and formats for Linear description
|
||||
|
||||
2. **Step 0.6**: Detect and extract Figma designs
|
||||
- Uses `commands/_shared-figma-detection.md` logic
|
||||
- Identifies live Figma links
|
||||
- Extracts design tokens and specifications
|
||||
- Caches results in Linear comments
|
||||
|
||||
3. **Step 1**: Gather external PM context (Jira, Confluence, Slack)
|
||||
- Only if external PM is enabled
|
||||
- Fetches Jira ticket details and linked issues
|
||||
- Searches Confluence for related documentation
|
||||
- Finds Slack thread discussions
|
||||
- Checks BitBucket for related PRs
|
||||
|
||||
4. **Step 2**: Analyze codebase
|
||||
- Identifies relevant files to modify
|
||||
- Maps patterns and conventions
|
||||
- Determines dependencies
|
||||
|
||||
5. **Step 2.5**: Invoke engineer agents for technical analysis
|
||||
- Selects appropriate agents based on task type
|
||||
- Backend tasks → `backend-architect`
|
||||
- Frontend tasks → `frontend-developer`
|
||||
- Mobile tasks → `mobile-developer`
|
||||
- Full-stack → both backend and frontend in parallel
|
||||
- Security-critical → add `security-auditor`
|
||||
|
||||
6. **Step 3**: Update Linear description with comprehensive research
|
||||
- Creates implementation checklist
|
||||
- Inserts visual context (images + Figma designs)
|
||||
- Adds research findings
|
||||
- Includes agent analysis
|
||||
- Links all external resources
|
||||
|
||||
7. **Step 4**: Confirm completion
|
||||
- Display status summary
|
||||
- Show research added
|
||||
- Provide Linear issue URL
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:plan WORK-123
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Same functionality, simpler syntax
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
## Output Format
|
||||
|
||||
Provide a summary like:
|
||||
|
||||
```
|
||||
✅ Planning Complete!
|
||||
|
||||
📋 Linear Issue Updated: $1
|
||||
🔗 URL: https://linear.app/workspace/issue/$1
|
||||
📝 Jira Reference: $2 (if available)
|
||||
|
||||
📊 Research Summary Added:
|
||||
- Gathered context from [X] Jira tickets
|
||||
- Found [Y] relevant Confluence docs
|
||||
- Analyzed [Z] related Slack discussions
|
||||
- Identified [N] files to modify
|
||||
- Researched best practices from Context7
|
||||
|
||||
✅ Checklist: [X] subtasks created/updated
|
||||
|
||||
🚀 Ready for implementation! Run: /ccpm:implementation:start $1
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### Checklist Positioning
|
||||
|
||||
- **ALWAYS place checklist at the TOP** of the description
|
||||
- This makes it immediately visible when opening the ticket
|
||||
- Use blockquote for status and complexity metadata
|
||||
- Separate checklist from detailed research with `---` horizontal rule
|
||||
|
||||
### Linking Best Practices
|
||||
|
||||
- **Every ticket/page mention MUST be a clickable link**
|
||||
- Extract URLs from MCP API responses, not manual construction
|
||||
- Store URLs as you research, use when writing description
|
||||
- Link text should be descriptive (not just ticket ID)
|
||||
- Example: `[TRAIN-123: Add JWT auth](url)` not just `[TRAIN-123](url)`
|
||||
|
||||
### Research Quality
|
||||
|
||||
- Be thorough in research - this is the foundation for successful implementation
|
||||
- Always search Context7 for latest best practices
|
||||
- Cross-reference multiple sources to validate approach
|
||||
- If information is missing, document what's unknown in the Linear issue
|
||||
- Create specific, actionable subtasks in the checklist
|
||||
- Include links to ALL referenced materials (Jira, Confluence, Slack, PRs)
|
||||
123
commands/planning:quick-plan.md
Normal file
123
commands/planning:quick-plan.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
description: Quick planning for tasks without external PM systems
|
||||
allowed-tools: [Bash, LinearMCP, Context7MCP]
|
||||
argument-hint: "<task-description>" <project>
|
||||
---
|
||||
|
||||
# Quick Planning: $ARGUMENTS
|
||||
|
||||
You are doing **Quick Planning** for: **$1** in project **$2**.
|
||||
|
||||
This command is for projects without external PM systems (no Jira/Confluence).
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
**NEVER** submit, post, or update anything to external PM systems without explicit user confirmation.
|
||||
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
- ⛔ **External systems** require user confirmation for write operations
|
||||
|
||||
## Quick Planning Workflow
|
||||
|
||||
### Step 1: Understand the Task
|
||||
|
||||
Task Description: $1
|
||||
Project: $2
|
||||
|
||||
### Step 2: Analyze Codebase
|
||||
|
||||
1. **Read relevant files**:
|
||||
- Identify what needs to be implemented
|
||||
- Find similar patterns in existing code
|
||||
- Understand current architecture
|
||||
|
||||
2. **Identify conventions**:
|
||||
- Code organization
|
||||
- Naming patterns
|
||||
- Testing approach
|
||||
|
||||
### Step 3: Search for Best Practices
|
||||
|
||||
**Use Context7 MCP** to:
|
||||
- Search for latest recommendations for this type of task
|
||||
- Find modern approaches and patterns
|
||||
- **CRITICAL**: Do NOT rely on knowledge cutoff - always search
|
||||
|
||||
### Step 4: Create Linear Issue
|
||||
|
||||
Use **Linear MCP** to create issue:
|
||||
|
||||
**Team & Project**:
|
||||
- Load from project configuration using `_shared-project-config-loader.md`
|
||||
|
||||
**Title**: $1
|
||||
**Status**: Planning
|
||||
**Labels**: $2, planning
|
||||
|
||||
**Description**:
|
||||
|
||||
```markdown
|
||||
## Task Description
|
||||
$1
|
||||
|
||||
## Codebase Analysis
|
||||
|
||||
**Current State**:
|
||||
- [How things work now]
|
||||
- [Relevant files]
|
||||
|
||||
**Patterns to Follow**:
|
||||
- [Code patterns found]
|
||||
- [Conventions used in project]
|
||||
|
||||
## Best Practices (from Context7)
|
||||
- [Latest recommended approach]
|
||||
- [Modern patterns to use]
|
||||
- [Performance/security considerations]
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
**Approach**:
|
||||
[How to implement this]
|
||||
|
||||
**Technical Details**:
|
||||
- [Specific implementation notes]
|
||||
- [Edge cases to handle]
|
||||
|
||||
**Testing Strategy**:
|
||||
- [How to test this]
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] **Subtask 1**: [Specific task]
|
||||
- [ ] **Subtask 2**: [Specific task]
|
||||
- [ ] **Subtask 3**: [Specific task]
|
||||
```
|
||||
|
||||
### Step 5: Confirm
|
||||
|
||||
Display:
|
||||
```
|
||||
✅ Quick Planning Complete!
|
||||
|
||||
📋 Linear Issue: [PROJECT-123]
|
||||
🔗 URL: https://linear.app/workspace/issue/[PROJECT-123]
|
||||
|
||||
📊 Summary:
|
||||
- Analyzed codebase patterns
|
||||
- Found [X] relevant files
|
||||
- Researched best practices
|
||||
|
||||
✅ Checklist: [X] subtasks created
|
||||
|
||||
🚀 Run: /start [PROJECT-123]
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Focus on practical, actionable subtasks
|
||||
- Keep descriptions concise but clear
|
||||
- Always check Context7 for latest approaches
|
||||
- Reference existing code patterns when possible
|
||||
759
commands/planning:update.md
Normal file
759
commands/planning:update.md
Normal file
@@ -0,0 +1,759 @@
|
||||
---
|
||||
description: Update existing plan with interactive clarification and smart analysis
|
||||
allowed-tools: [Bash, LinearMCP, AtlassianMCP, SlackMCP, PlaywrightMCP, Context7MCP, AskUserQuestion]
|
||||
argument-hint: <linear-issue-id> "<update-request>"
|
||||
---
|
||||
|
||||
# Updating Plan: $1
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:plan WORK-123 "your changes here"
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Same functionality, simpler syntax
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
You are updating the existing plan for Linear issue **$1** based on the update request: **$2**
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **READ-ONLY** operations are permitted (fetch, search, view)
|
||||
- ⛔ **WRITE operations** require user confirmation
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
|
||||
When in doubt, ASK before posting anything externally.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Current Plan
|
||||
|
||||
Use **Linear MCP** to:
|
||||
|
||||
1. Get issue details for: $1
|
||||
2. Read current description, title, status, labels
|
||||
3. Parse existing checklist and subtasks
|
||||
4. Identify project to determine PM systems
|
||||
5. Extract any Jira ticket reference from description
|
||||
|
||||
**Display Current Plan Summary:**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Current Plan: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🏷️ Title: [Current title]
|
||||
📊 Status: [Current status]
|
||||
🎯 Progress: [X/Y] subtasks ([percentage]%)
|
||||
⏱️ Complexity: [Low/Medium/High if available]
|
||||
|
||||
Current Subtasks:
|
||||
[x] 1. [Subtask 1] ✅
|
||||
[x] 2. [Subtask 2] ✅
|
||||
[ ] 3. [Subtask 3] ⏳
|
||||
[ ] 4. [Subtask 4]
|
||||
[ ] 5. [Subtask 5]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Update Request
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
$2
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 2: Intelligent Analysis
|
||||
|
||||
Analyze the update request and current plan to determine:
|
||||
|
||||
**Change Type Detection:**
|
||||
|
||||
1. **Scope Change**:
|
||||
- Keywords: "add", "also need", "include", "plus", "additionally"
|
||||
- Impact: New subtasks, modified requirements
|
||||
|
||||
2. **Approach Change**:
|
||||
- Keywords: "instead of", "different approach", "change how", "use X not Y"
|
||||
- Impact: Modified subtasks, architecture changes
|
||||
|
||||
3. **Clarification**:
|
||||
- Keywords: "unclear", "what about", "how to", "explain", "detail"
|
||||
- Impact: Need more context, no plan change yet
|
||||
|
||||
4. **Simplification**:
|
||||
- Keywords: "remove", "don't need", "simpler", "skip", "not necessary"
|
||||
- Impact: Remove subtasks, reduce complexity
|
||||
|
||||
5. **Blocker/Issue**:
|
||||
- Keywords: "blocked", "can't", "doesn't work", "problem with", "issue"
|
||||
- Impact: May need alternative approach
|
||||
|
||||
**Analyze Required Information:**
|
||||
|
||||
Identify what information is needed to make the update:
|
||||
|
||||
- Technical constraints or limitations?
|
||||
- Priority or urgency changes?
|
||||
- Dependencies on other work?
|
||||
- Resource or timeline constraints?
|
||||
- Specific implementation preferences?
|
||||
- Risk tolerance or security requirements?
|
||||
|
||||
### Step 3: Interactive Clarification
|
||||
|
||||
Use **AskUserQuestion** tool to gather necessary context.
|
||||
|
||||
**Ask 1-4 targeted questions** based on the analysis:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "[Specific clarifying question based on update request]",
|
||||
header: "Clarification",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "[Option 1]",
|
||||
description: "[What this means for the plan]"
|
||||
},
|
||||
{
|
||||
label: "[Option 2]",
|
||||
description: "[What this means for the plan]"
|
||||
},
|
||||
{
|
||||
label: "[Option 3]",
|
||||
description: "[What this means for the plan]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Clarification Scenarios:**
|
||||
|
||||
**Scenario 1: Scope Addition**
|
||||
```
|
||||
Update request: "Also add email notifications"
|
||||
|
||||
Question: "How should email notifications be implemented?"
|
||||
Options:
|
||||
- "Use existing email service" → Add 2 subtasks
|
||||
- "Integrate new service (SendGrid)" → Add 4 subtasks + config
|
||||
- "Just log for now, implement later" → Add 1 subtask (placeholder)
|
||||
```
|
||||
|
||||
**Scenario 2: Approach Change**
|
||||
```
|
||||
Update request: "Use REST instead of GraphQL"
|
||||
|
||||
Question: "What should we do with existing GraphQL work?"
|
||||
Options:
|
||||
- "Replace entirely" → Remove GraphQL subtasks, add REST subtasks
|
||||
- "Support both" → Keep GraphQL, add REST subtasks
|
||||
- "Migrate gradually" → Add migration subtasks
|
||||
```
|
||||
|
||||
**Scenario 3: Ambiguous Request**
|
||||
```
|
||||
Update request: "Make it faster"
|
||||
|
||||
Multiple questions:
|
||||
1. "What specifically needs to be faster?"
|
||||
- Database queries
|
||||
- API response time
|
||||
- UI rendering
|
||||
- All of the above
|
||||
|
||||
2. "What's the target performance?"
|
||||
- <100ms (aggressive)
|
||||
- <500ms (moderate)
|
||||
- <1s (baseline)
|
||||
- Not sure / measure first
|
||||
```
|
||||
|
||||
**Scenario 4: Blocker**
|
||||
```
|
||||
Update request: "Can't use library X, it's deprecated"
|
||||
|
||||
Question: "What should we use instead of library X?"
|
||||
Options:
|
||||
- "Library Y (recommended alternative)" → Update subtasks
|
||||
- "Build custom solution" → Add more subtasks
|
||||
- "Research alternatives first" → Add research subtask
|
||||
```
|
||||
|
||||
### Step 4: Gather Additional Context
|
||||
|
||||
Based on clarifications and change type, gather additional context:
|
||||
|
||||
**For Scope/Approach Changes:**
|
||||
|
||||
1. **Search Codebase**:
|
||||
- Find similar implementations
|
||||
- Identify affected files
|
||||
- Check for existing patterns
|
||||
|
||||
2. **Use Context7 MCP**:
|
||||
- Search for latest best practices for new approach
|
||||
- Find recommended libraries/frameworks
|
||||
- Get implementation examples
|
||||
|
||||
3. **Check External PM** (if applicable):
|
||||
- Search Jira for related tickets
|
||||
- Check Confluence for design docs
|
||||
- Search Slack for team discussions
|
||||
|
||||
**For Clarifications:**
|
||||
|
||||
1. **Analyze Current Code**:
|
||||
- Read relevant files
|
||||
- Understand current architecture
|
||||
- Identify constraints
|
||||
|
||||
2. **Check Documentation**:
|
||||
- Search Confluence for specs
|
||||
- Find related PRs in BitBucket
|
||||
- Review team decisions
|
||||
|
||||
### Step 4.5: Invoke Engineer Agents for Technical Validation
|
||||
|
||||
**CRITICAL**: Before generating the updated plan, invoke specialized engineer agents to validate the changes and provide technical insights.
|
||||
|
||||
**Determine which agents to invoke based on the change type:**
|
||||
|
||||
1. **For Scope/Approach Changes** → Invoke relevant technical agents:
|
||||
|
||||
**Backend changes** → `backend-architect`:
|
||||
```
|
||||
Task(backend-architect): "Review the proposed plan update for [task description].
|
||||
|
||||
Original Plan:
|
||||
[Current checklist and approach from Step 1]
|
||||
|
||||
Proposed Changes:
|
||||
[Update request: $2]
|
||||
[User clarifications from Step 3]
|
||||
|
||||
Please validate:
|
||||
1. Is the new approach technically sound?
|
||||
2. What are the implications of this change?
|
||||
3. Are there better alternatives?
|
||||
4. What new risks are introduced?
|
||||
5. What additional subtasks are needed?
|
||||
6. Updated complexity estimate"
|
||||
```
|
||||
|
||||
**Frontend changes** → `frontend-developer`:
|
||||
```
|
||||
Task(frontend-developer): "Review the proposed plan update for [task description].
|
||||
|
||||
Original Plan:
|
||||
[Current checklist and approach]
|
||||
|
||||
Proposed Changes:
|
||||
[Update request and clarifications]
|
||||
|
||||
Please validate:
|
||||
1. Component architecture impact
|
||||
2. State management changes needed
|
||||
3. UI/UX implications
|
||||
4. Performance considerations
|
||||
5. Testing strategy updates
|
||||
6. Accessibility impact"
|
||||
```
|
||||
|
||||
2. **For Architecture Changes** → Invoke `backend-architect` + `security-auditor` sequentially:
|
||||
```
|
||||
Step 1: Task(backend-architect): "Analyze architecture change..."
|
||||
|
||||
Step 2: Task(security-auditor): "Review security implications of [architect's recommendations]..."
|
||||
```
|
||||
|
||||
3. **For Technical Blockers** → Invoke `debugger` first, then relevant architect:
|
||||
```
|
||||
Task(debugger): "Analyze the reported blocker: [user's blocker description].
|
||||
|
||||
Current implementation: [from codebase analysis]
|
||||
Error/Issue: [blocker details]
|
||||
|
||||
Please provide:
|
||||
1. Root cause analysis
|
||||
2. Potential workarounds
|
||||
3. Recommended solutions
|
||||
4. Implementation complexity"
|
||||
|
||||
# Then based on debugger's findings:
|
||||
Task(backend-architect or frontend-developer): "Implement solution for [debugger's recommendation]..."
|
||||
```
|
||||
|
||||
4. **For Simplification Requests** → Quick architecture validation:
|
||||
```
|
||||
Task(backend-architect OR frontend-developer): "Validate simplification request.
|
||||
|
||||
Original scope: [current plan]
|
||||
Proposed removal: [what user wants to remove/simplify]
|
||||
|
||||
Please confirm:
|
||||
1. Can we safely remove this?
|
||||
2. Any dependencies that break?
|
||||
3. What testing is still needed?
|
||||
4. Reduced complexity estimate"
|
||||
```
|
||||
|
||||
**Agent Response Integration:**
|
||||
|
||||
After agents respond:
|
||||
|
||||
1. **Extract key insights**:
|
||||
- Technical validation (thumbs up/down)
|
||||
- New risks identified
|
||||
- Recommended additional subtasks
|
||||
- Updated complexity/timeline estimate
|
||||
|
||||
2. **Update plan sections**:
|
||||
- Modify checklist based on agent recommendations
|
||||
- Add agent insights to "Research Findings"
|
||||
- Update complexity estimate
|
||||
- Document new risks/considerations
|
||||
|
||||
**Example Agent Integration in Updated Plan:**
|
||||
|
||||
```markdown
|
||||
## 🔄 Plan Update Analysis
|
||||
|
||||
### Change Requested
|
||||
$2
|
||||
|
||||
### Engineer Agent Validation
|
||||
|
||||
**Backend Architect Review**:
|
||||
- ✅ Technically sound approach
|
||||
- ⚠️ Identified new dependency on [X]
|
||||
- 💡 Recommended adding subtask for [Y]
|
||||
- 📊 Complexity: Medium → High
|
||||
|
||||
**Security Auditor Review** (if applicable):
|
||||
- ⚠️ New security consideration: [Z]
|
||||
- 💡 Recommended mitigation: [approach]
|
||||
|
||||
### Updated Approach
|
||||
[Incorporate agent recommendations here]
|
||||
```
|
||||
|
||||
### Step 5: Generate Updated Plan
|
||||
|
||||
Based on gathered context and clarifications, generate updated plan:
|
||||
|
||||
**Update Checklist:**
|
||||
|
||||
```markdown
|
||||
## ✅ Implementation Checklist
|
||||
|
||||
> **Status**: [Keep current status or change to "Planning" if major changes]
|
||||
> **Complexity**: [Update if complexity changed]
|
||||
> **Last Updated**: [Current date/time] - [Update summary]
|
||||
|
||||
[Generate new checklist]:
|
||||
- [ ] **Subtask 1**: [Description]
|
||||
- [ ] **Subtask 2**: [Description]
|
||||
- [ ] **Subtask 3**: [Description]
|
||||
...
|
||||
|
||||
**Changes from previous plan:**
|
||||
- ✅ Kept: [List unchanged subtasks]
|
||||
- 🔄 Modified: [List modified subtasks with what changed]
|
||||
- ➕ Added: [List new subtasks]
|
||||
- ➖ Removed: [List removed subtasks with reason]
|
||||
```
|
||||
|
||||
**Update Research Findings:**
|
||||
|
||||
Add or modify relevant sections:
|
||||
|
||||
```markdown
|
||||
## 🔍 Research Findings
|
||||
|
||||
[Keep existing sections that are still relevant]
|
||||
|
||||
### Update History
|
||||
|
||||
**[Date/Time] - Plan Update**:
|
||||
- **Requested**: $2
|
||||
- **Clarifications**: [Summary of user answers]
|
||||
- **Changes**: [Summary of plan changes]
|
||||
- **Rationale**: [Why these changes were made]
|
||||
|
||||
[Add new sections if needed based on new research]:
|
||||
|
||||
### New Approach Analysis (if approach changed)
|
||||
|
||||
**Previous Approach**: [Old approach]
|
||||
**New Approach**: [New approach]
|
||||
**Reasoning**: [Why the change]
|
||||
**Trade-offs**:
|
||||
- ✅ Benefits: [List benefits]
|
||||
- ⚠️ Considerations: [List considerations]
|
||||
|
||||
### Additional Best Practices (if new research done)
|
||||
|
||||
[New best practices from Context7]
|
||||
|
||||
### Updated Dependencies (if scope changed)
|
||||
|
||||
[Updated dependency information]
|
||||
```
|
||||
|
||||
**Preserve Important Context:**
|
||||
|
||||
- Keep all URLs and references from original plan
|
||||
- Preserve completed subtask history
|
||||
- Maintain link to original Jira/external tickets
|
||||
- Keep research findings that are still relevant
|
||||
|
||||
### Step 6: Display Plan Comparison
|
||||
|
||||
Show side-by-side comparison of changes:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Plan Update Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🔄 Changes Overview:
|
||||
✅ Kept: [X] subtasks unchanged
|
||||
🔄 Modified: [Y] subtasks updated
|
||||
➕ Added: [Z] new subtasks
|
||||
➖ Removed: [W] subtasks
|
||||
|
||||
📈 Complexity Impact:
|
||||
Before: [Old complexity]
|
||||
After: [New complexity]
|
||||
Change: [Increased/Decreased/Unchanged]
|
||||
|
||||
⏱️ Timeline Impact:
|
||||
Estimated: [+/- X days/hours]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Detailed Changes
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ KEPT (Unchanged):
|
||||
1. [Subtask that stayed the same]
|
||||
2. [Another unchanged subtask]
|
||||
|
||||
🔄 MODIFIED:
|
||||
3. [Old: "Previous description"]
|
||||
[New: "Updated description"]
|
||||
[Why: Reason for change]
|
||||
|
||||
➕ ADDED:
|
||||
6. [New subtask 1]
|
||||
[Why: Reason for addition]
|
||||
7. [New subtask 2]
|
||||
[Why: Reason for addition]
|
||||
|
||||
➖ REMOVED:
|
||||
[Old subtask 4: "Removed description"]
|
||||
[Why: Reason for removal]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 7: Confirm and Update
|
||||
|
||||
Use **AskUserQuestion** to confirm changes:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Does this updated plan look correct?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Approve & Update",
|
||||
description: "Update Linear issue with new plan"
|
||||
},
|
||||
{
|
||||
label: "Needs Adjustment",
|
||||
description: "Make further changes before updating"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Keep current plan, don't update"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**If "Approve & Update":**
|
||||
|
||||
Use **Linear MCP** to update issue $1:
|
||||
- Update description with new plan
|
||||
- Update complexity if changed
|
||||
- Change status to "Planning" if major changes
|
||||
- Add comment documenting the update
|
||||
- Add label "plan-updated"
|
||||
|
||||
**If "Needs Adjustment":**
|
||||
|
||||
Ask follow-up question:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What needs to be adjusted?",
|
||||
header: "Adjustment",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Too many subtasks",
|
||||
description: "Consolidate or reduce scope"
|
||||
},
|
||||
{
|
||||
label: "Missing something",
|
||||
description: "Add missing requirements"
|
||||
},
|
||||
{
|
||||
label: "Wrong approach",
|
||||
description: "Reconsider technical approach"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Then refine and show comparison again (loop to Step 6).
|
||||
|
||||
**If "Cancel":**
|
||||
|
||||
Exit gracefully without changes.
|
||||
|
||||
### Step 8: Show Next Actions
|
||||
|
||||
After successful update, display interactive next actions:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Plan Updated Successfully!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Linear Issue: $1
|
||||
🔗 URL: https://linear.app/workspace/issue/$1
|
||||
|
||||
📊 Update Summary:
|
||||
🔄 Modified: [Y] subtasks
|
||||
➕ Added: [Z] subtasks
|
||||
➖ Removed: [W] subtasks
|
||||
📈 Complexity: [New complexity]
|
||||
|
||||
💬 Update logged in comments
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Review Updated Plan",
|
||||
description: "Review the updated plan details (/ccpm:utils:status)"
|
||||
},
|
||||
{
|
||||
label: "Get Fresh Insights",
|
||||
description: "Get AI analysis of updated plan (/ccpm:utils:insights)"
|
||||
},
|
||||
{
|
||||
label: "Start/Resume Work",
|
||||
description: "Begin or continue implementation (/ccpm:implementation:start or /ccpm:implementation:next)"
|
||||
},
|
||||
{
|
||||
label: "Sync External Systems",
|
||||
description: "Update Jira status if needed (/ccpm:utils:sync-status)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute chosen action:**
|
||||
|
||||
- If "Review Updated Plan" → Run `/ccpm:utils:status $1`
|
||||
- If "Get Fresh Insights" → Run `/ccpm:utils:insights $1`
|
||||
- If "Start/Resume Work" → Determine if should run `/ccpm:implementation:start $1` or `/ccpm:implementation:next $1` based on current status
|
||||
- If "Sync External Systems" → Run `/ccpm:utils:sync-status $1`
|
||||
- If "Other" → Show quick commands
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Status: /ccpm:utils:status $1
|
||||
Insights: /ccpm:utils:insights $1
|
||||
Start: /ccpm:implementation:start $1
|
||||
Next: /ccpm:implementation:next $1
|
||||
Update Again: /ccpm:planning:update $1 "<new request>"
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Smart Analysis Features
|
||||
|
||||
### Context-Aware Question Generation
|
||||
|
||||
The command should intelligently determine what to ask based on:
|
||||
|
||||
1. **Ambiguity Level**: More ambiguous requests need more clarification
|
||||
2. **Change Scope**: Larger changes need more questions
|
||||
3. **Technical Complexity**: Complex changes need architecture questions
|
||||
4. **Current Status**: In-progress tasks need migration questions
|
||||
5. **Project Type**: Different projects may have different constraints
|
||||
|
||||
### Change Impact Assessment
|
||||
|
||||
Automatically analyze:
|
||||
|
||||
- **Complexity Change**: Does this make the task simpler or more complex?
|
||||
- **Timeline Impact**: Does this add or reduce estimated time?
|
||||
- **Risk Impact**: Does this introduce new risks?
|
||||
- **Dependency Impact**: Does this affect other tasks?
|
||||
- **Resource Impact**: Does this require new skills/tools?
|
||||
|
||||
### Intelligent Subtask Generation
|
||||
|
||||
When generating new subtasks:
|
||||
|
||||
1. **Maintain Granularity**: Match existing subtask size
|
||||
2. **Logical Ordering**: Put subtasks in implementation order
|
||||
3. **Dependency Awareness**: Note dependencies between subtasks
|
||||
4. **Actionable Language**: Use clear, specific verbs
|
||||
5. **Testable Outcomes**: Each subtask should have clear completion criteria
|
||||
|
||||
### Progressive Refinement
|
||||
|
||||
If user selects "Needs Adjustment", continue refining:
|
||||
|
||||
- Ask follow-up questions
|
||||
- Adjust based on feedback
|
||||
- Show updated comparison
|
||||
- Repeat until approved
|
||||
|
||||
## Example Usage Scenarios
|
||||
|
||||
### Scenario 1: Adding Scope
|
||||
|
||||
```bash
|
||||
/ccpm:planning:update WORK-123 "Also add email notifications"
|
||||
|
||||
→ Analyzes current plan
|
||||
→ Asks: "Should we use existing email service or integrate new one?"
|
||||
→ User: "Use existing service"
|
||||
→ Asks: "What events should trigger emails?"
|
||||
→ User: "User signup and password reset"
|
||||
→ Generates: 2 new subtasks for email integration
|
||||
→ Shows: Comparison (added 2 subtasks, complexity +1)
|
||||
→ Confirms and updates
|
||||
```
|
||||
|
||||
### Scenario 2: Changing Approach
|
||||
|
||||
```bash
|
||||
/ccpm:planning:update WORK-456 "Use Redis caching instead of in-memory"
|
||||
|
||||
→ Analyzes: Architecture change
|
||||
→ Asks: "What's the reason for switching to Redis?"
|
||||
→ User: "Need persistence across restarts"
|
||||
→ Asks: "Redis already set up or need to add?"
|
||||
→ User: "Need to set up"
|
||||
→ Generates: Updated subtasks (remove in-memory, add Redis setup + integration)
|
||||
→ Shows: Comparison (modified 3, added 2 subtasks, complexity +2)
|
||||
→ Asks: "Need Redis for local dev too?"
|
||||
→ User: "Yes, add Docker setup"
|
||||
→ Refines: Adds Docker setup subtask
|
||||
→ Confirms and updates
|
||||
```
|
||||
|
||||
### Scenario 3: Simplification
|
||||
|
||||
```bash
|
||||
/ccpm:planning:update WORK-789 "Remove the admin dashboard, just add an API"
|
||||
|
||||
→ Analyzes: Scope reduction
|
||||
→ Asks: "Keep any of the admin dashboard work?"
|
||||
→ User: "No, remove it all"
|
||||
→ Generates: Removes 4 subtasks, keeps API subtasks
|
||||
→ Shows: Comparison (removed 4 subtasks, complexity -3)
|
||||
→ Confirms and updates
|
||||
```
|
||||
|
||||
### Scenario 4: Blocker Resolution
|
||||
|
||||
```bash
|
||||
/ccpm:planning:update WORK-321 "Library X doesn't support Node 20, need alternative"
|
||||
|
||||
→ Analyzes: Technical constraint
|
||||
→ Asks: "Researched alternatives yet?"
|
||||
→ User: "Not yet"
|
||||
→ Adds: Research subtask first
|
||||
→ Asks: "Want suggestions for alternatives?"
|
||||
→ User: "Yes"
|
||||
→ [Searches Context7 for alternatives]
|
||||
→ Suggests: Library Y and Library Z
|
||||
→ User: "Library Y looks good"
|
||||
→ Generates: Updated subtasks using Library Y
|
||||
→ Shows: Comparison (modified 2 subtasks)
|
||||
→ Confirms and updates
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### This Command Provides
|
||||
|
||||
1. **Interactive Clarification** - Asks smart questions before changing
|
||||
2. **Context Gathering** - Fetches additional info as needed
|
||||
3. **Impact Analysis** - Shows complexity/timeline changes
|
||||
4. **Change Tracking** - Documents update history
|
||||
5. **Safe Updates** - Confirms before modifying plan
|
||||
6. **Next Actions** - Suggests what to do after update
|
||||
|
||||
### Best Practices
|
||||
|
||||
- **Be Specific**: More specific update requests need fewer clarifications
|
||||
- **Progressive**: Can run multiple times to refine iteratively
|
||||
- **History**: All updates are logged in Linear comments
|
||||
- **Reversible**: Original plan is preserved in update history
|
||||
- **Transparent**: Shows exactly what will change before updating
|
||||
|
||||
### Integration with Other Commands
|
||||
|
||||
- **After `/ccpm:planning:plan`**: Update if initial plan needs refinement
|
||||
- **During `/ccpm:implementation:*`**: Update if scope/approach changes
|
||||
- **With `/ccpm:utils:insights`**: Get AI analysis after updating
|
||||
- **Before `/ccpm:verification:check`**: Ensure plan reflects actual work
|
||||
917
commands/pr:check-bitbucket.md
Normal file
917
commands/pr:check-bitbucket.md
Normal file
@@ -0,0 +1,917 @@
|
||||
---
|
||||
description: Check and analyze BitBucket PR for any project
|
||||
allowed-tools: [PlaywrightMCP, BrowserMCP, LinearMCP, Read, AskUserQuestion, Bash]
|
||||
argument-hint: <pr-number-or-url> [project-id]
|
||||
---
|
||||
|
||||
# Check BitBucket PR
|
||||
|
||||
**Works with any project configured with BitBucket in `~/.claude/ccpm-config.yaml`**
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
**NEVER** submit, post, comment, or modify anything on BitBucket or SonarQube without explicit user confirmation.
|
||||
|
||||
---
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - PR number or full BitBucket URL (required)
|
||||
- **$2** - Project ID (optional, uses active project if not specified)
|
||||
|
||||
## Shared Helpers
|
||||
|
||||
**Load shared Linear helpers for label and state management:**
|
||||
|
||||
```markdown
|
||||
READ: commands/_shared-linear-helpers.md
|
||||
```
|
||||
|
||||
This provides helper functions:
|
||||
- `getOrCreateLabel(teamId, labelName, options)` - Get or create labels
|
||||
- `getValidStateId(teamId, stateNameOrType)` - Resolve state names to IDs
|
||||
- `ensureLabelsExist(teamId, labelNames, options)` - Ensure multiple labels exist
|
||||
- `getDefaultColor(labelName)` - Get standard CCPM colors
|
||||
|
||||
## Project Configuration
|
||||
|
||||
**Load project configuration to get BitBucket settings:**
|
||||
|
||||
```bash
|
||||
# Set project argument
|
||||
PROJECT_ARG="$2" # Optional - will use active project if not provided
|
||||
```
|
||||
|
||||
**LOAD PROJECT CONFIG**: Follow instructions in `commands/_shared-project-config-loader.md`
|
||||
|
||||
After loading, you'll have:
|
||||
- `${REPO_TYPE}` - Should be "bitbucket"
|
||||
- `${BITBUCKET_WORKSPACE}`, `${BITBUCKET_REPO}`, `${BITBUCKET_BASE_URL}`
|
||||
- Custom command config (browser_mcp preference, etc.)
|
||||
|
||||
**Validate BitBucket is configured:**
|
||||
|
||||
```bash
|
||||
if [[ "$REPO_TYPE" != "bitbucket" ]]; then
|
||||
echo "❌ Error: Project '$PROJECT_ID' is not configured for BitBucket"
|
||||
echo " Current repository type: $REPO_TYPE"
|
||||
echo ""
|
||||
echo "To use this command, configure BitBucket in project settings:"
|
||||
echo " /ccpm:project:update $PROJECT_ID"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Select Browser MCP
|
||||
|
||||
Ask user which browser MCP to use:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Which browser automation tool would you like to use?",
|
||||
header: "Browser MCP",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Playwright MCP",
|
||||
description: "Recommended - More robust, better error handling (mcp__playwright__* tools)"
|
||||
},
|
||||
{
|
||||
label: "Browser MCP",
|
||||
description: "Alternative - Simpler interface (mcp__browsermcp__* tools)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Store the selected MCP type for use in subsequent steps.
|
||||
|
||||
### Step 2: Parse PR Identifier
|
||||
|
||||
Determine PR URL from input using loaded project configuration:
|
||||
|
||||
```javascript
|
||||
let prUrl
|
||||
|
||||
if ($1.startsWith('http')) {
|
||||
// Full URL provided
|
||||
prUrl = $1
|
||||
} else {
|
||||
// PR number provided - construct URL from project config
|
||||
// Use BITBUCKET_BASE_URL from loaded config
|
||||
prUrl = `${BITBUCKET_BASE_URL}/pull-requests/${$1}`
|
||||
|
||||
// Alternative if base URL not in config:
|
||||
// prUrl = `https://bitbucket.org/${BITBUCKET_WORKSPACE}/${BITBUCKET_REPO}/pull-requests/${$1}`
|
||||
}
|
||||
|
||||
console.log(`📎 PR URL: ${prUrl}`)
|
||||
```
|
||||
|
||||
### Step 3: Navigate to PR
|
||||
|
||||
**IMPORTANT**: Different tool names based on selected MCP:
|
||||
|
||||
#### If Playwright MCP selected:
|
||||
```javascript
|
||||
await mcp__playwright__browser_navigate({ url: prUrl })
|
||||
```
|
||||
|
||||
#### If Browser MCP selected:
|
||||
```javascript
|
||||
await mcp__browsermcp__browser_navigate({ url: prUrl })
|
||||
```
|
||||
|
||||
**Authentication Check**:
|
||||
- After navigation, check if redirected to login page
|
||||
- If authentication required:
|
||||
```
|
||||
🔐 Authentication Required
|
||||
|
||||
I've navigated to the PR, but BitBucket requires sign-in.
|
||||
|
||||
Please manually sign in to BitBucket in the browser, then reply with "continue" when ready.
|
||||
|
||||
⏸️ PAUSED - Waiting for authentication...
|
||||
```
|
||||
- Wait for user to reply "continue" before proceeding
|
||||
- After user confirms, take a snapshot to verify successful authentication
|
||||
|
||||
### Step 4: Capture Initial State
|
||||
|
||||
Take snapshot of the PR page:
|
||||
|
||||
#### If Playwright MCP:
|
||||
```javascript
|
||||
const snapshot = await mcp__playwright__browser_snapshot({})
|
||||
```
|
||||
|
||||
#### If Browser MCP:
|
||||
```javascript
|
||||
const snapshot = await mcp__browsermcp__browser_snapshot({})
|
||||
```
|
||||
|
||||
Extract and display PR information:
|
||||
- PR title
|
||||
- Author
|
||||
- Source/target branches
|
||||
- Current status (Open, Merged, Declined)
|
||||
- Number of reviewers and their status
|
||||
- Number of comments
|
||||
|
||||
### Step 5: Check Build Status
|
||||
|
||||
Look for build/CI status indicators in the snapshot.
|
||||
|
||||
**If build is failing:**
|
||||
|
||||
```
|
||||
⚠️ Build Status: FAILING
|
||||
|
||||
I can see the build is failing. Would you like me to:
|
||||
1. Analyze build logs and suggest fixes
|
||||
2. Skip build analysis for now
|
||||
|
||||
Please select an option (1 or 2):
|
||||
```
|
||||
|
||||
If user selects option 1:
|
||||
1. Click on build status link to view logs (using appropriate MCP click tool)
|
||||
2. **Authentication Check**: If redirected to CI/CD login, pause and ask user to authenticate
|
||||
3. Read and analyze build logs
|
||||
4. Identify failure causes
|
||||
5. Suggest specific fixes
|
||||
6. Display suggestions but **DO NOT** make any code changes without explicit approval
|
||||
|
||||
**If build is passing:**
|
||||
```
|
||||
✅ Build Status: PASSING
|
||||
|
||||
Proceeding to code review...
|
||||
```
|
||||
|
||||
### Step 6: Review SonarQube Issues
|
||||
|
||||
Navigate to Quality Gate section (if visible in PR):
|
||||
|
||||
#### If Playwright MCP:
|
||||
```javascript
|
||||
// Look for SonarQube link in snapshot
|
||||
// Click if found
|
||||
await mcp__playwright__browser_click({
|
||||
element: "SonarQube Quality Gate link",
|
||||
ref: "[ref from snapshot]"
|
||||
})
|
||||
```
|
||||
|
||||
#### If Browser MCP:
|
||||
```javascript
|
||||
await mcp__browsermcp__browser_click({
|
||||
element: "SonarQube Quality Gate link",
|
||||
ref: "[ref from snapshot]"
|
||||
})
|
||||
```
|
||||
|
||||
**Authentication Check**:
|
||||
- If SonarQube requires login:
|
||||
```
|
||||
🔐 SonarQube Authentication Required
|
||||
|
||||
Please sign in to SonarQube in the browser, then reply with "continue" when ready.
|
||||
|
||||
⏸️ PAUSED - Waiting for authentication...
|
||||
```
|
||||
- Wait for user confirmation before proceeding
|
||||
|
||||
**Analyze Issues**:
|
||||
|
||||
For each issue found, categorize and suggest improvements:
|
||||
|
||||
```
|
||||
📊 SonarQube Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🔴 Critical Issues (0)
|
||||
🟠 Major Issues (3)
|
||||
🟡 Minor Issues (12)
|
||||
📋 Code Smells (5)
|
||||
🎯 Test Coverage: 78% (target: 80%)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Major Issues:
|
||||
|
||||
1. [Bug] Potential null pointer dereference
|
||||
📁 File: src/components/TaskList.tsx:45
|
||||
💡 Suggestion: Add null check before accessing property
|
||||
```typescript
|
||||
// Current code
|
||||
const title = task.details.title
|
||||
|
||||
// Suggested fix
|
||||
const title = task.details?.title ?? 'Untitled'
|
||||
```
|
||||
|
||||
2. [Security] Hardcoded credentials detected
|
||||
📁 File: src/config/api.ts:12
|
||||
💡 Suggestion: Move to environment variables
|
||||
```typescript
|
||||
// Current code
|
||||
const API_KEY = 'sk_test_12345'
|
||||
|
||||
// Suggested fix
|
||||
const API_KEY = process.env.EXPO_PUBLIC_API_KEY
|
||||
```
|
||||
|
||||
3. [Performance] Inefficient array iteration
|
||||
📁 File: src/utils/helpers.ts:89
|
||||
💡 Suggestion: Use more efficient method
|
||||
```typescript
|
||||
// Current code
|
||||
items.forEach(item => {
|
||||
if (item.id === targetId) result = item
|
||||
})
|
||||
|
||||
// Suggested fix
|
||||
const result = items.find(item => item.id === targetId)
|
||||
```
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📈 Test Coverage Gaps:
|
||||
|
||||
Files below 80% coverage:
|
||||
- src/components/TaskList.tsx: 65%
|
||||
- src/hooks/useAuth.tsx: 72%
|
||||
- src/utils/validation.ts: 45%
|
||||
|
||||
💡 Recommendations:
|
||||
1. Add unit tests for edge cases in TaskList
|
||||
2. Test error handling in useAuth hook
|
||||
3. Add comprehensive tests for validation utilities
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**IMPORTANT**: Display suggestions only - DO NOT create files, commit changes, or modify code without explicit user approval.
|
||||
|
||||
### Step 7: Find and Sync Linear Ticket
|
||||
|
||||
Look for Linear issue reference in:
|
||||
|
||||
1. PR title (e.g., "RPT-123: Add feature")
|
||||
2. Branch name (format: `feature/RPT-XXXX-description`)
|
||||
3. PR description
|
||||
4. Extract Jira ticket ID if found (format: `RPT-\d+`)
|
||||
|
||||
#### Search for Linear Issue
|
||||
|
||||
```javascript
|
||||
// Extract ticket ID from PR
|
||||
const ticketMatch = branchName.match(/RPT-(\d+)/) ||
|
||||
prTitle.match(/RPT-(\d+)/) ||
|
||||
prDescription.match(/RPT-(\d+)/)
|
||||
|
||||
let linearIssue = null
|
||||
|
||||
if (ticketMatch) {
|
||||
const ticketId = `RPT-${ticketMatch[1]}`
|
||||
console.log(`🔍 Found Jira Ticket: ${ticketId}`)
|
||||
|
||||
// Search for Linear issue linked to this Jira ticket
|
||||
// Use Linear MCP to search by title or description containing ticket ID
|
||||
const searchResults = await mcp__linear__list_issues({
|
||||
team: ${LINEAR_TEAM}, // or appropriate team identifier
|
||||
query: ticketId,
|
||||
limit: 10
|
||||
})
|
||||
|
||||
// Find exact match
|
||||
linearIssue = searchResults.find(issue =>
|
||||
issue.title.includes(ticketId) ||
|
||||
issue.description?.includes(ticketId)
|
||||
)
|
||||
|
||||
if (linearIssue) {
|
||||
console.log(`✅ Found Linear Issue: ${linearIssue.identifier} - ${linearIssue.title}`)
|
||||
} else {
|
||||
console.log(`⚠️ No Linear issue found for Jira ticket ${ticketId}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Display Current Status
|
||||
|
||||
If Linear issue found:
|
||||
|
||||
```plaintext
|
||||
📋 Linear Issue Status
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Issue: ${linearIssue.identifier}
|
||||
Title: ${linearIssue.title}
|
||||
Status: ${linearIssue.state.name}
|
||||
Assignee: ${linearIssue.assignee?.name || 'Unassigned'}
|
||||
Priority: ${linearIssue.priority || 'None'}
|
||||
Labels: ${linearIssue.labels.map(l => l.name).join(', ')}
|
||||
|
||||
🔗 Jira Ticket: ${ticketId}
|
||||
🔗 PR: #${prNumber}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
#### Sync PR Status to Linear
|
||||
|
||||
Ask user if they want to update Linear:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Would you like to sync PR review findings to Linear?",
|
||||
header: "Linear Sync",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Add Comment",
|
||||
description: "Add PR review summary as Linear comment"
|
||||
},
|
||||
{
|
||||
label: "Update Status",
|
||||
description: "Update Linear issue status based on PR state"
|
||||
},
|
||||
{
|
||||
label: "Both",
|
||||
description: "Add comment AND update status"
|
||||
},
|
||||
{
|
||||
label: "Skip",
|
||||
description: "Don't sync to Linear"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Option: Add Comment**
|
||||
|
||||
Draft Linear comment with PR review findings:
|
||||
|
||||
```markdown
|
||||
## PR Review - #${prNumber}
|
||||
|
||||
### Status: ${prStatus} (${buildStatus})
|
||||
|
||||
### Build & Quality
|
||||
- Build: ${buildPassing ? '✅ Passing' : '❌ Failing'}
|
||||
- Tests: ${testsPassing ? '✅ All passing' : '⚠️ Some failing'}
|
||||
- Coverage: ${testCoverage}%
|
||||
- SonarQube: ${criticalIssues} critical, ${majorIssues} major, ${minorIssues} minor
|
||||
|
||||
### Issues Found
|
||||
${issuesList}
|
||||
|
||||
### Recommended Actions
|
||||
${recommendedActions}
|
||||
|
||||
🔗 [View PR](${prUrl})
|
||||
```
|
||||
|
||||
Show preview and ask for confirmation:
|
||||
|
||||
```plaintext
|
||||
🚨 CONFIRMATION REQUIRED
|
||||
|
||||
I'll add the following comment to Linear issue ${linearIssue.identifier}:
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
[Show comment preview above]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Reply "yes" to post, or "no" to cancel.
|
||||
```
|
||||
|
||||
If user confirms:
|
||||
|
||||
```javascript
|
||||
await mcp__linear__create_comment({
|
||||
issueId: linearIssue.id,
|
||||
body: commentMarkdown
|
||||
})
|
||||
|
||||
console.log('✅ Comment added to Linear issue')
|
||||
```
|
||||
|
||||
**Option: Update Status**
|
||||
|
||||
Suggest status update based on PR state:
|
||||
|
||||
```javascript
|
||||
// Determine suggested status
|
||||
let suggestedStatus = linearIssue.state.name // Keep current by default
|
||||
|
||||
if (prStatus === 'MERGED') {
|
||||
suggestedStatus = 'Done'
|
||||
} else if (prStatus === 'OPEN' && buildPassing && noBlockingIssues) {
|
||||
suggestedStatus = 'In Review'
|
||||
} else if (prStatus === 'OPEN' && (!buildPassing || hasBlockingIssues)) {
|
||||
suggestedStatus = 'In Progress' // Needs fixes
|
||||
} else if (prStatus === 'DECLINED') {
|
||||
suggestedStatus = 'Canceled'
|
||||
}
|
||||
|
||||
// Show current and suggested status
|
||||
console.log(`
|
||||
📊 Status Update Suggestion
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Current Status: ${linearIssue.state.name}
|
||||
Suggested Status: ${suggestedStatus}
|
||||
|
||||
Reason: ${getStatusReason()}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
// Ask for confirmation
|
||||
{
|
||||
questions: [{
|
||||
question: `Update Linear status from "${linearIssue.state.name}" to "${suggestedStatus}"?`,
|
||||
header: "Status Update",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Yes", description: "Update status as suggested" },
|
||||
{ label: "Choose Different", description: "Select a different status" },
|
||||
{ label: "No", description: "Keep current status" }
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If user chooses "Choose Different":
|
||||
|
||||
```javascript
|
||||
// Get available statuses for the team
|
||||
const statuses = await mcp__linear__list_issue_statuses({
|
||||
team: linearIssue.team.key
|
||||
})
|
||||
|
||||
// Ask user to select
|
||||
{
|
||||
questions: [{
|
||||
question: "Which status would you like?",
|
||||
header: "Select Status",
|
||||
multiSelect: false,
|
||||
options: statuses.map(s => ({
|
||||
label: s.name,
|
||||
description: s.description || s.type
|
||||
}))
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If user confirms status update:
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// Get team ID from Linear issue
|
||||
const teamId = linearIssue.team.id;
|
||||
|
||||
// Get valid state ID using helper
|
||||
const stateId = await getValidStateId(teamId, selectedStatus);
|
||||
|
||||
// Update issue with proper state ID
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "update_issue",
|
||||
args: {
|
||||
id: linearIssue.id,
|
||||
stateId: stateId // Use stateId, not state
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ Linear issue updated to "${selectedStatus}"`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`⚠️ Failed to update Linear status: ${error.message}`);
|
||||
console.log(` Could not update status to "${selectedStatus}"`);
|
||||
console.log(` Please update manually in Linear if needed`);
|
||||
}
|
||||
```
|
||||
|
||||
**Option: Both**
|
||||
|
||||
Execute both comment addition and status update in sequence with confirmations.
|
||||
|
||||
#### If No Linear Issue Found
|
||||
|
||||
Offer to create one:
|
||||
|
||||
```javascript
|
||||
if (!linearIssue && ticketId) {
|
||||
{
|
||||
questions: [{
|
||||
question: "No Linear issue found. Would you like to create one?",
|
||||
header: "Create Linear Issue",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes",
|
||||
description: `Create Linear issue for ${ticketId}`
|
||||
},
|
||||
{
|
||||
label: "No",
|
||||
description: "Skip Linear tracking"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
if (userWantsCreate) {
|
||||
// Ensure pr-review label exists
|
||||
await ensureLabelsExist(${LINEAR_TEAM}, ["pr-review"], {
|
||||
colors: {
|
||||
"pr-review": "#5e6ad2"
|
||||
},
|
||||
descriptions: {
|
||||
"pr-review": "Pull request under review"
|
||||
}
|
||||
})
|
||||
|
||||
// Get valid state ID for "In Review"
|
||||
let inReviewStateId
|
||||
try {
|
||||
inReviewStateId = await getValidStateId(${LINEAR_TEAM}, "In Review")
|
||||
} catch (error) {
|
||||
console.error(`⚠️ Could not find "In Review" state: ${error.message}`)
|
||||
console.log(`Using fallback: "started" state type`)
|
||||
inReviewStateId = await getValidStateId(${LINEAR_TEAM}, "started")
|
||||
}
|
||||
|
||||
// Create Linear issue with PR context
|
||||
const newIssue = await mcp__linear__create_issue({
|
||||
team: ${LINEAR_TEAM},
|
||||
title: `[${ticketId}] ${prTitle}`,
|
||||
description: `
|
||||
# Jira Ticket: ${ticketId}
|
||||
# PR: #${prNumber}
|
||||
|
||||
${prDescription}
|
||||
|
||||
## PR Review Findings
|
||||
${reviewSummary}
|
||||
`,
|
||||
stateId: inReviewStateId,
|
||||
labelIds: ['pr-review'],
|
||||
// Add PR link
|
||||
links: [{
|
||||
url: prUrl,
|
||||
title: `PR #${prNumber}`
|
||||
}]
|
||||
})
|
||||
|
||||
console.log(`✅ Created Linear issue: ${newIssue.identifier}`)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 8: Quality Verification Checklist
|
||||
|
||||
Display comprehensive quality checklist:
|
||||
|
||||
```
|
||||
✅ Quality Verification Checklist
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Build & Tests:
|
||||
✅ Build passing
|
||||
✅ All tests passing
|
||||
⚠️ Test coverage: 78% (below 80% target)
|
||||
|
||||
Code Quality:
|
||||
⚠️ 3 major SonarQube issues
|
||||
⚠️ 12 minor issues
|
||||
✅ No critical/blocking issues
|
||||
|
||||
Best Practices:
|
||||
✅ Proper error handling
|
||||
⚠️ Hardcoded credentials detected
|
||||
✅ TypeScript types properly defined
|
||||
|
||||
Security:
|
||||
⚠️ 1 security vulnerability (hardcoded credentials)
|
||||
✅ No SQL injection risks
|
||||
✅ No XSS vulnerabilities
|
||||
|
||||
Performance:
|
||||
⚠️ 1 inefficient iteration pattern
|
||||
✅ No memory leaks detected
|
||||
|
||||
Documentation:
|
||||
✅ PR description clear
|
||||
✅ Code comments present
|
||||
⚠️ Missing JSDoc for public APIs
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Overall Assessment: NEEDS IMPROVEMENTS ⚠️
|
||||
|
||||
Recommended Actions:
|
||||
1. Fix hardcoded credentials (security issue)
|
||||
2. Improve test coverage to 80%+
|
||||
3. Address major SonarQube issues
|
||||
4. Optimize array iteration pattern
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 9: Interactive Next Actions
|
||||
|
||||
Ask user what they want to do:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do next?",
|
||||
header: "Next Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Sync to Linear",
|
||||
description: "Update Linear issue with PR review findings (if Linear issue found)"
|
||||
},
|
||||
{
|
||||
label: "Fix Issues Locally",
|
||||
description: "I'll help you fix the identified issues in your local codebase"
|
||||
},
|
||||
{
|
||||
label: "Review Code Changes",
|
||||
description: "Let me show you the specific code changes in this PR"
|
||||
},
|
||||
{
|
||||
label: "Generate PR Comment",
|
||||
description: "Draft a review comment (I'll show it to you before posting)"
|
||||
},
|
||||
{
|
||||
label: "Export Report",
|
||||
description: "Save this analysis to a markdown file"
|
||||
},
|
||||
{
|
||||
label: "Done",
|
||||
description: "Just review the findings above"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Handle each option:
|
||||
|
||||
#### Option 1: Sync to Linear
|
||||
|
||||
If Linear issue was found in Step 7, re-run the Linear sync workflow:
|
||||
|
||||
```javascript
|
||||
if (linearIssue) {
|
||||
// Go back to Step 7: Sync PR Status to Linear
|
||||
// Show the same options: Add Comment, Update Status, Both, Skip
|
||||
// Execute based on user selection with confirmations
|
||||
} else {
|
||||
console.log('⚠️ No Linear issue found. Run Step 7 to search or create one.')
|
||||
}
|
||||
```
|
||||
|
||||
#### Option 2: Fix Issues Locally
|
||||
```
|
||||
I can help you fix the issues. Would you like me to:
|
||||
|
||||
1. Fix security issues (hardcoded credentials)
|
||||
2. Improve test coverage
|
||||
3. Refactor inefficient code
|
||||
4. All of the above
|
||||
|
||||
Please select or reply with specific instructions.
|
||||
```
|
||||
|
||||
After user confirms what to fix, make the changes and show a summary before committing.
|
||||
|
||||
#### Option 3: Review Code Changes
|
||||
|
||||
Navigate to "Files changed" tab and analyze specific modifications.
|
||||
|
||||
#### Option 4: Generate PR Comment
|
||||
Draft a professional review comment:
|
||||
|
||||
```markdown
|
||||
## Code Review Summary
|
||||
|
||||
### ✅ Strengths
|
||||
- Clean code structure
|
||||
- Good test coverage in core modules
|
||||
- Proper TypeScript usage
|
||||
|
||||
### ⚠️ Issues to Address
|
||||
|
||||
**High Priority:**
|
||||
1. **Security**: Remove hardcoded credentials in `src/config/api.ts`
|
||||
2. **Test Coverage**: Increase coverage to 80%+ (currently 78%)
|
||||
|
||||
**Medium Priority:**
|
||||
3. **Performance**: Optimize array iteration in `src/utils/helpers.ts:89`
|
||||
4. **Code Quality**: Address 3 major SonarQube issues
|
||||
|
||||
### 📝 Detailed Recommendations
|
||||
|
||||
[... detailed suggestions from Step 6 ...]
|
||||
|
||||
### 🎯 Next Steps
|
||||
1. Address security vulnerability
|
||||
2. Add missing test cases
|
||||
3. Re-run SonarQube analysis
|
||||
4. Request re-review when ready
|
||||
```
|
||||
|
||||
Show this to user:
|
||||
```
|
||||
🚨 CONFIRMATION REQUIRED
|
||||
|
||||
I've drafted the following PR review comment.
|
||||
|
||||
Would you like me to post this to BitBucket?
|
||||
|
||||
⚠️ This will add a comment to PR #${prNumber}
|
||||
|
||||
Reply "yes" to post, "edit" to modify, or "no" to cancel.
|
||||
```
|
||||
|
||||
**DO NOT POST** without explicit "yes" confirmation.
|
||||
|
||||
#### Option 5: Export Report
|
||||
```javascript
|
||||
const reportPath = `./pr-${prNumber}-review-${Date.now()}.md`
|
||||
|
||||
// Generate comprehensive markdown report
|
||||
const report = `# PR #${prNumber} Review Report
|
||||
|
||||
Generated: ${new Date().toISOString()}
|
||||
|
||||
... (include all analysis from above)
|
||||
...
|
||||
`
|
||||
|
||||
// Save to file
|
||||
fs.writeFileSync(reportPath, report)
|
||||
|
||||
console.log(`✅ Report saved to: ${reportPath}`)
|
||||
```
|
||||
|
||||
### Step 10: Close Browser (Optional)
|
||||
|
||||
Ask user if they want to close the browser:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Close the browser?",
|
||||
header: "Cleanup",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Yes", description: "Close browser session" },
|
||||
{ label: "No", description: "Keep browser open for manual review" }
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
#### If Playwright MCP:
|
||||
```javascript
|
||||
if (userWantsClose) {
|
||||
await mcp__playwright__browser_close({})
|
||||
}
|
||||
```
|
||||
|
||||
#### If Browser MCP:
|
||||
```javascript
|
||||
if (userWantsClose) {
|
||||
await mcp__browsermcp__browser_close({})
|
||||
}
|
||||
```
|
||||
|
||||
## Safety Reminders
|
||||
|
||||
Throughout the entire workflow:
|
||||
|
||||
1. **✅ READ OPERATIONS** - Freely read from BitBucket, SonarQube, Jira
|
||||
2. **⛔ WRITE OPERATIONS** - ALWAYS require explicit confirmation:
|
||||
- Posting PR comments
|
||||
- Updating Jira tickets
|
||||
- Committing code changes
|
||||
- Modifying any external system
|
||||
|
||||
3. **🔐 AUTHENTICATION** - Pause and wait for user to sign in manually:
|
||||
- BitBucket login
|
||||
- SonarQube login
|
||||
- CI/CD system login
|
||||
|
||||
4. **📝 TRANSPARENCY** - Always show what you plan to do before doing it:
|
||||
- Show comment drafts before posting
|
||||
- Show code changes before committing
|
||||
- Show ticket updates before sending
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Check PR by number
|
||||
|
||||
```bash
|
||||
/ccpm:pr:check-bitbucket 123
|
||||
|
||||
# Workflow:
|
||||
# 1. Ask which MCP to use
|
||||
# 2. Navigate to PR #123
|
||||
# 3. Authenticate if needed
|
||||
# 4. Analyze build, SonarQube, code
|
||||
# 5. Show findings
|
||||
# 6. Ask for next action
|
||||
```
|
||||
|
||||
### Example 2: Check PR by URL
|
||||
|
||||
```bash
|
||||
/ccpm:pr:check-bitbucket https://bitbucket.org/my-workspace/my-repo/pull-requests/456
|
||||
|
||||
# Workflow:
|
||||
# Same as above but uses provided URL directly
|
||||
```
|
||||
|
||||
### Example 3: Full workflow with fixes
|
||||
|
||||
```bash
|
||||
/ccpm:pr:check-bitbucket 789
|
||||
|
||||
# After review:
|
||||
# User selects: "Fix Issues Locally"
|
||||
# User confirms: "Fix all issues"
|
||||
# → Makes changes
|
||||
# → Shows diff
|
||||
# → Asks: "Commit these changes?"
|
||||
# → User confirms: "yes"
|
||||
# → Commits with message: "fix: address PR review findings"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Browser MCP selection allows flexibility between Playwright and Browser MCPs
|
||||
- All authentication is manual - we pause and wait for user
|
||||
- Zero mutations without explicit approval
|
||||
- Comprehensive analysis with actionable suggestions
|
||||
- Interactive workflow for maximum control
|
||||
- Export capability for offline review
|
||||
- Respects PM Commands safety rules throughout
|
||||
455
commands/project:add.md
Normal file
455
commands/project:add.md
Normal file
@@ -0,0 +1,455 @@
|
||||
---
|
||||
description: Add a new project to CCPM configuration
|
||||
allowed-tools: [Bash, Read, Write, Edit, AskUserQuestion]
|
||||
argument-hint: <project-id> [--template TEMPLATE]
|
||||
---
|
||||
|
||||
# Add New Project to CCPM
|
||||
|
||||
Add a new project configuration to `~/.claude/ccpm-config.yaml`.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required, e.g., "my-app", "acme-platform")
|
||||
- **--template** - Use a template (optional: "fullstack-with-jira", "simple-linear", "open-source")
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate Project ID
|
||||
|
||||
```javascript
|
||||
const projectId = $1
|
||||
|
||||
if (!projectId) {
|
||||
console.log("❌ Error: Project ID required")
|
||||
console.log("Usage: /ccpm:project:add <project-id> [--template TEMPLATE]")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Validate format (lowercase, hyphens only)
|
||||
if (!/^[a-z0-9-]+$/.test(projectId)) {
|
||||
console.log("❌ Error: Invalid project ID format")
|
||||
console.log("Project ID must be lowercase with hyphens only (e.g., 'my-app')")
|
||||
exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Check if Configuration Exists
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "📝 No CCPM configuration found. Creating new one..."
|
||||
|
||||
# Create from example
|
||||
cp "$HOME/.claude/plugins/ccpm/ccpm-config.example.yaml" "$CONFIG_FILE"
|
||||
|
||||
echo "✅ Created $CONFIG_FILE"
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 3: Check if Project Already Exists
|
||||
|
||||
```bash
|
||||
# Use yq to check if project exists
|
||||
if yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
|
||||
EXISTING=$(yq eval ".projects.$PROJECT_ID.name" "$CONFIG_FILE")
|
||||
|
||||
if [[ "$EXISTING" != "null" ]]; then
|
||||
echo "⚠️ Project '$PROJECT_ID' already exists: $EXISTING"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " 1. Update existing project: /ccpm:project:update $PROJECT_ID"
|
||||
echo " 2. Delete and recreate: /ccpm:project:delete $PROJECT_ID"
|
||||
echo " 3. Choose different ID: /ccpm:project:add <different-id>"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 4: Gather Project Information
|
||||
|
||||
Use **AskUserQuestion** to gather project details:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "What type of project is this?",
|
||||
header: "Project Type",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Full-stack with Jira",
|
||||
description: "Jira, Confluence, Slack integration (template: fullstack-with-jira)"
|
||||
},
|
||||
{
|
||||
label: "Simple Linear-only",
|
||||
description: "Linear tracking only, no external PM (template: simple-linear)"
|
||||
},
|
||||
{
|
||||
label: "Open Source",
|
||||
description: "GitHub-based open source project (template: open-source)"
|
||||
},
|
||||
{
|
||||
label: "Custom",
|
||||
description: "Configure from scratch"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What's the project name (human-readable)?",
|
||||
header: "Project Name",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "Type the project name"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "Which Linear team should this project use?",
|
||||
header: "Linear Team",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Work",
|
||||
description: "Work-related projects"
|
||||
},
|
||||
{
|
||||
label: "Personal",
|
||||
description: "Personal projects"
|
||||
},
|
||||
{
|
||||
label: "Other",
|
||||
description: "Specify custom team"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What's the code repository type?",
|
||||
header: "Repository",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "GitHub",
|
||||
description: "GitHub repository"
|
||||
},
|
||||
{
|
||||
label: "BitBucket",
|
||||
description: "BitBucket repository"
|
||||
},
|
||||
{
|
||||
label: "GitLab",
|
||||
description: "GitLab repository"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Store answers:
|
||||
- `projectType` → template to use
|
||||
- `projectName` → human-readable name
|
||||
- `linearTeam` → Linear team
|
||||
- `repoType` → repository type
|
||||
|
||||
### Step 5: Gather Additional Details Based on Type
|
||||
|
||||
#### If "Full-stack with Jira" selected:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "What's your Jira project key? (e.g., PROJ)",
|
||||
header: "Jira Key",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "Type the Jira project key"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What's your Confluence space key?",
|
||||
header: "Confluence",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Same as Jira",
|
||||
description: "Use same key as Jira project"
|
||||
},
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "Type the Confluence space key"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What's your primary Slack channel?",
|
||||
header: "Slack Channel",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "e.g., #project-dev"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### If GitHub/BitBucket/GitLab selected:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: `What's your ${repoType} repository? (format: owner/repo)`,
|
||||
header: "Repository",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "e.g., company/project-name"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Build Project Configuration
|
||||
|
||||
```javascript
|
||||
// Start with template or empty config
|
||||
let projectConfig = {}
|
||||
|
||||
if (projectType !== "Custom") {
|
||||
// Load template from global config
|
||||
const template = await yq(".templates.${templateName}", CONFIG_FILE)
|
||||
projectConfig = { ...template }
|
||||
}
|
||||
|
||||
// Set basic fields
|
||||
projectConfig.name = projectName
|
||||
projectConfig.description = projectDescription || `${projectName} project`
|
||||
projectConfig.owner = projectOwner || "Engineering Team"
|
||||
|
||||
// Repository
|
||||
projectConfig.repository = {
|
||||
url: repositoryUrl,
|
||||
default_branch: "main"
|
||||
}
|
||||
|
||||
// Linear configuration
|
||||
projectConfig.linear = {
|
||||
team: linearTeam,
|
||||
project: projectName,
|
||||
default_labels: [projectId, "planning"]
|
||||
}
|
||||
|
||||
// External PM (if applicable)
|
||||
if (jiraEnabled) {
|
||||
projectConfig.external_pm = {
|
||||
enabled: true,
|
||||
type: "jira",
|
||||
jira: {
|
||||
enabled: true,
|
||||
base_url: jiraBaseUrl || "https://jira.company.com",
|
||||
project_key: jiraProjectKey
|
||||
},
|
||||
confluence: {
|
||||
enabled: confluenceEnabled,
|
||||
base_url: confluenceBaseUrl || "https://confluence.company.com",
|
||||
space_key: confluenceSpaceKey || jiraProjectKey
|
||||
},
|
||||
slack: {
|
||||
enabled: slackEnabled,
|
||||
workspace: slackWorkspace || "company-workspace",
|
||||
channels: [
|
||||
{
|
||||
name: slackChannel,
|
||||
id: slackChannelId || "C0XXXXXXX"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
projectConfig.external_pm = {
|
||||
enabled: false,
|
||||
type: "linear-only"
|
||||
}
|
||||
}
|
||||
|
||||
// Code repository
|
||||
if (repoType === "github") {
|
||||
const [owner, repo] = repoUrl.split("/")
|
||||
projectConfig.code_repository = {
|
||||
type: "github",
|
||||
github: {
|
||||
enabled: true,
|
||||
owner: owner,
|
||||
repo: repo
|
||||
}
|
||||
}
|
||||
} else if (repoType === "bitbucket") {
|
||||
const [workspace, repoSlug] = repoUrl.split("/")
|
||||
projectConfig.code_repository = {
|
||||
type: "bitbucket",
|
||||
bitbucket: {
|
||||
enabled: true,
|
||||
workspace: workspace,
|
||||
repo_slug: repoSlug,
|
||||
base_url: `https://bitbucket.org/${workspace}/${repoSlug}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tech stack (ask for details)
|
||||
projectConfig.tech_stack = {
|
||||
languages: techLanguages || ["typescript"],
|
||||
frameworks: {
|
||||
frontend: frontendFrameworks || [],
|
||||
backend: backendFrameworks || []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: Show Configuration Preview
|
||||
|
||||
```yaml
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 New Project Configuration Preview
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project ID: ${projectId}
|
||||
Name: ${projectName}
|
||||
Description: ${projectDescription}
|
||||
|
||||
Linear:
|
||||
Team: ${linearTeam}
|
||||
Project: ${projectName}
|
||||
Labels: [${projectLabels.join(", ")}]
|
||||
|
||||
${jiraEnabled ? `
|
||||
External PM:
|
||||
Jira: ${jiraProjectKey}
|
||||
Confluence: ${confluenceSpaceKey}
|
||||
Slack: ${slackChannel}
|
||||
` : `
|
||||
External PM: Linear-only (no external integration)
|
||||
`}
|
||||
|
||||
Repository:
|
||||
Type: ${repoType}
|
||||
${repoType === "github" ? `Owner/Repo: ${owner}/${repo}` : `Workspace/Repo: ${workspace}/${repoSlug}`}
|
||||
|
||||
Tech Stack:
|
||||
Languages: ${languages.join(", ")}
|
||||
Frontend: ${frontendFrameworks.join(", ") || "N/A"}
|
||||
Backend: ${backendFrameworks.join(", ") || "N/A"}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Confirm and Save
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Add this project to CCPM configuration?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, add it",
|
||||
description: "Save configuration to ~/.claude/ccpm-config.yaml"
|
||||
},
|
||||
{
|
||||
label: "Edit details",
|
||||
description: "Go back and modify configuration"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Don't add project"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If confirmed:
|
||||
|
||||
```bash
|
||||
# Add project to configuration using yq
|
||||
yq eval -i ".projects.$PROJECT_ID = $PROJECT_CONFIG_JSON" "$CONFIG_FILE"
|
||||
|
||||
echo ""
|
||||
echo "✅ Project added successfully!"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Next Steps"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "1. View configuration:"
|
||||
echo " /ccpm:project:list"
|
||||
echo ""
|
||||
echo "2. Set as active project (if in project directory):"
|
||||
echo " /ccpm:project:set $PROJECT_ID"
|
||||
echo ""
|
||||
echo "3. Create your first task:"
|
||||
echo " /ccpm:planning:create \"Task title\" $PROJECT_ID"
|
||||
echo ""
|
||||
echo "4. Edit configuration anytime:"
|
||||
echo " /ccpm:project:update $PROJECT_ID"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Add with template
|
||||
|
||||
```bash
|
||||
/ccpm:project:add my-app --template simple-linear
|
||||
|
||||
# Will prompt for:
|
||||
# - Project name
|
||||
# - Linear team
|
||||
# - Repository details
|
||||
```
|
||||
|
||||
### Example 2: Add full-stack project
|
||||
|
||||
```bash
|
||||
/ccpm:project:add acme-platform
|
||||
|
||||
# Interactive prompts will guide you through:
|
||||
# - Project type selection (choose "Full-stack with Jira")
|
||||
# - Jira/Confluence/Slack configuration
|
||||
# - Repository setup
|
||||
# - Tech stack details
|
||||
```
|
||||
|
||||
### Example 3: Add personal project
|
||||
|
||||
```bash
|
||||
/ccpm:project:add my-side-project --template open-source
|
||||
|
||||
# Quick setup for personal/open-source projects
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Configuration is stored in `~/.claude/ccpm-config.yaml`
|
||||
- You can manually edit this file later
|
||||
- Templates provide quick starting points
|
||||
- All fields can be customized after creation
|
||||
- Use `/ccpm:project:update` to modify existing projects
|
||||
332
commands/project:delete.md
Normal file
332
commands/project:delete.md
Normal file
@@ -0,0 +1,332 @@
|
||||
---
|
||||
description: Delete a project from CCPM configuration
|
||||
allowed-tools: [Bash, Read, Edit, AskUserQuestion]
|
||||
argument-hint: <project-id> [--force]
|
||||
---
|
||||
|
||||
# Delete Project from CCPM
|
||||
|
||||
Remove a project configuration from `~/.claude/ccpm-config.yaml`.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
- **--force** - Skip confirmation (optional, dangerous)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Interactive delete with confirmation
|
||||
/ccpm:project:delete my-app
|
||||
|
||||
# Force delete without confirmation
|
||||
/ccpm:project:delete my-app --force
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate Project Exists
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
PROJECT_ID=$1
|
||||
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "❌ Error: No CCPM configuration found"
|
||||
exit(1)
|
||||
fi
|
||||
|
||||
if ! yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
|
||||
echo "❌ Error: Project '$PROJECT_ID' not found"
|
||||
echo ""
|
||||
echo "Available projects:"
|
||||
yq eval '.projects | keys | .[]' "$CONFIG_FILE"
|
||||
exit(1)
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 2: Load and Display Project Info
|
||||
|
||||
```javascript
|
||||
const projectConfig = await yq(`.projects.${projectId}`, CONFIG_FILE)
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Delete Project: ${projectId}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project will be removed:
|
||||
Name: ${projectConfig.name}
|
||||
Description: ${projectConfig.description || "N/A"}
|
||||
Linear: ${projectConfig.linear.team} / ${projectConfig.linear.project}
|
||||
Repository: ${projectConfig.repository?.url || "N/A"}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ WARNING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
This will remove the project configuration from CCPM.
|
||||
|
||||
What will happen:
|
||||
✓ Project removed from ~/.claude/ccpm-config.yaml
|
||||
✓ CCPM commands will no longer recognize this project
|
||||
✓ You can re-add the project later if needed
|
||||
|
||||
What will NOT happen:
|
||||
✗ No data in Linear will be deleted
|
||||
✗ No data in Jira/Confluence will be deleted
|
||||
✗ No code repositories will be affected
|
||||
✗ No files in your project will be deleted
|
||||
|
||||
This ONLY removes the CCPM configuration for this project.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 3: Confirm Deletion
|
||||
|
||||
If `--force` flag is NOT provided:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: `Are you sure you want to delete project '${projectId}'?`,
|
||||
header: "Confirm Delete",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, delete it",
|
||||
description: "Remove project from CCPM configuration"
|
||||
},
|
||||
{
|
||||
label: "Show details first",
|
||||
description: "View full project configuration before deleting"
|
||||
},
|
||||
{
|
||||
label: "No, cancel",
|
||||
description: "Keep the project"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If user selects "Show details first":
|
||||
```bash
|
||||
# Run /ccpm:project:show internally
|
||||
/ccpm:project:show $PROJECT_ID
|
||||
|
||||
# Then ask again
|
||||
{
|
||||
questions: [{
|
||||
question: "After reviewing, do you want to delete this project?",
|
||||
header: "Confirm Delete",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, delete it",
|
||||
description: "Remove project from CCPM"
|
||||
},
|
||||
{
|
||||
label: "No, keep it",
|
||||
description: "Cancel deletion"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Check if Project is Active
|
||||
|
||||
```javascript
|
||||
const isActive = await isActiveProject(projectId)
|
||||
|
||||
if (isActive) {
|
||||
console.log(`
|
||||
⚠️ Additional Warning: Active Project
|
||||
|
||||
This project is currently active (auto-detected from your working directory).
|
||||
|
||||
If you delete it, you'll need to:
|
||||
1. Use /ccpm:project:set <other-project> to switch, OR
|
||||
2. Navigate to a different project directory, OR
|
||||
3. CCPM commands will prompt you to select a project
|
||||
|
||||
`)
|
||||
|
||||
// Ask for additional confirmation
|
||||
{
|
||||
questions: [{
|
||||
question: "This is your active project. Still delete?",
|
||||
header: "Active Project",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, delete anyway",
|
||||
description: "I understand this is active"
|
||||
},
|
||||
{
|
||||
label: "No, cancel",
|
||||
description: "Keep the active project"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Perform Deletion
|
||||
|
||||
```bash
|
||||
# Create backup first
|
||||
BACKUP_FILE="$HOME/.claude/ccpm-config.backup.$(date +%Y%m%d_%H%M%S).yaml"
|
||||
cp "$CONFIG_FILE" "$BACKUP_FILE"
|
||||
|
||||
echo "📦 Backup created: $BACKUP_FILE"
|
||||
echo ""
|
||||
|
||||
# Delete the project using yq
|
||||
yq eval -i "del(.projects.$PROJECT_ID)" "$CONFIG_FILE"
|
||||
|
||||
echo "✅ Project deleted successfully!"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Summary"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Deleted: $PROJECT_ID"
|
||||
echo "Backup: $BACKUP_FILE"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Next Steps"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "View remaining projects: /ccpm:project:list"
|
||||
echo "Add new project: /ccpm:project:add <project-id>"
|
||||
echo ""
|
||||
echo "To restore (if needed):"
|
||||
echo " cp $BACKUP_FILE $CONFIG_FILE"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
```
|
||||
|
||||
### Step 6: Cleanup Active Project Context
|
||||
|
||||
If the deleted project was active:
|
||||
|
||||
```bash
|
||||
# Clear active project context
|
||||
yq eval -i '.context.current_project = null' "$CONFIG_FILE"
|
||||
|
||||
echo ""
|
||||
echo "ℹ️ Active project context cleared"
|
||||
echo ""
|
||||
echo "Next time you run a CCPM command, you'll be prompted to select a project."
|
||||
echo "Or set a default: /ccpm:project:set <project-id>"
|
||||
```
|
||||
|
||||
## Safety Features
|
||||
|
||||
### 1. Always Creates Backup
|
||||
|
||||
Before deletion, a timestamped backup is created:
|
||||
```
|
||||
~/.claude/ccpm-config.backup.20250120_143022.yaml
|
||||
```
|
||||
|
||||
### 2. Confirmation Required
|
||||
|
||||
Unless `--force` is used, user must confirm:
|
||||
- Once for regular projects
|
||||
- Twice for active projects
|
||||
|
||||
### 3. Clear Communication
|
||||
|
||||
The command clearly states what WILL and WILL NOT be deleted.
|
||||
|
||||
### 4. Easy Restoration
|
||||
|
||||
Backup file path is provided for easy restoration if needed.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Delete with confirmation
|
||||
|
||||
```bash
|
||||
/ccpm:project:delete old-project
|
||||
|
||||
# Shows project details
|
||||
# Asks for confirmation
|
||||
# Creates backup
|
||||
# Deletes configuration
|
||||
# ✅ Done
|
||||
```
|
||||
|
||||
### Example 2: Delete active project
|
||||
|
||||
```bash
|
||||
/ccpm:project:delete my-current-app
|
||||
|
||||
# Shows project details
|
||||
# ⚠️ Warns it's the active project
|
||||
# Asks for confirmation twice
|
||||
# Creates backup
|
||||
# Deletes configuration
|
||||
# Clears active project context
|
||||
# ✅ Done
|
||||
```
|
||||
|
||||
### Example 3: Force delete (no confirmation)
|
||||
|
||||
```bash
|
||||
/ccpm:project:delete temp-project --force
|
||||
|
||||
# ⚠️ DANGEROUS: Skips all confirmations
|
||||
# Creates backup
|
||||
# Deletes immediately
|
||||
# ✅ Done
|
||||
|
||||
# Use with caution!
|
||||
```
|
||||
|
||||
### Example 4: Restore from backup
|
||||
|
||||
```bash
|
||||
# If you deleted by mistake:
|
||||
cp ~/.claude/ccpm-config.backup.20250120_143022.yaml ~/.claude/ccpm-config.yaml
|
||||
|
||||
# Or run:
|
||||
/ccpm:project:list # Shows the backup file path
|
||||
|
||||
# Then manually restore
|
||||
```
|
||||
|
||||
## What Gets Deleted
|
||||
|
||||
### Deleted ✓
|
||||
|
||||
- Project configuration in `~/.claude/ccpm-config.yaml`
|
||||
- Project entry from CCPM's project list
|
||||
- Active project context (if applicable)
|
||||
|
||||
### NOT Deleted ✗
|
||||
|
||||
- Linear issues and data
|
||||
- Jira tickets and data
|
||||
- Confluence pages
|
||||
- Slack messages
|
||||
- Git repositories
|
||||
- Local project files
|
||||
- Any actual code or data
|
||||
|
||||
**This command ONLY removes the CCPM configuration, not any actual project data.**
|
||||
|
||||
## Notes
|
||||
|
||||
- Always creates a timestamped backup before deletion
|
||||
- Can be safely restored from backup
|
||||
- Does not affect any external systems
|
||||
- Use `--force` carefully (skips all confirmations)
|
||||
- Active projects require extra confirmation
|
||||
- Configuration file: `~/.claude/ccpm-config.yaml`
|
||||
300
commands/project:list.md
Normal file
300
commands/project:list.md
Normal file
@@ -0,0 +1,300 @@
|
||||
---
|
||||
description: List all configured CCPM projects
|
||||
---
|
||||
|
||||
# List CCPM Projects
|
||||
|
||||
Display all projects configured in `~/.claude/ccpm-config.yaml` with active project detection including subdirectory/subproject context.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/ccpm:project:list
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Auto-Activate Skills
|
||||
|
||||
```markdown
|
||||
# Skills auto-activate for guidance
|
||||
Skill(project-detection): Provides detection workflow
|
||||
Skill(project-operations): Provides display format guidance
|
||||
```
|
||||
|
||||
### Step 2: Get Active Project Context
|
||||
|
||||
Use the project-context-manager agent to detect the active project:
|
||||
|
||||
```javascript
|
||||
const activeContext = Task(project-context-manager): `
|
||||
Get active project context
|
||||
Include detection method
|
||||
Format: compact
|
||||
`
|
||||
|
||||
// activeContext contains:
|
||||
// - project_id (or null if not detected)
|
||||
// - subproject (or null if not in subdirectory)
|
||||
// - detection_method (git_remote, subdirectory, local_path, etc.)
|
||||
```
|
||||
|
||||
### Step 3: List All Projects
|
||||
|
||||
Use the project-config-loader agent to get all projects:
|
||||
|
||||
```javascript
|
||||
const allProjects = Task(project-config-loader): `
|
||||
Load all project configurations
|
||||
Return summary list with names and descriptions
|
||||
Validate: false
|
||||
`
|
||||
|
||||
if (allProjects.error?.code === 'CONFIG_NOT_FOUND') {
|
||||
console.log("⚠️ No CCPM configuration found")
|
||||
console.log("")
|
||||
console.log("Create configuration:")
|
||||
console.log(" /ccpm:project:add <project-id>")
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if (Object.keys(allProjects.projects).length === 0) {
|
||||
console.log("📋 No projects configured yet")
|
||||
console.log("")
|
||||
console.log("Add your first project:")
|
||||
console.log(" /ccpm:project:add <project-id>")
|
||||
exit(0)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Display Project List
|
||||
|
||||
Sort projects with active first, then alphabetically:
|
||||
|
||||
```javascript
|
||||
const projects = allProjects.projects
|
||||
const projectIds = Object.keys(projects)
|
||||
|
||||
// Sort: active first, then alphabetically
|
||||
projectIds.sort((a, b) => {
|
||||
if (activeContext?.project_id === a) return -1
|
||||
if (activeContext?.project_id === b) return 1
|
||||
return a.localeCompare(b)
|
||||
})
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 CCPM Projects (${projectIds.length})
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
for (const projectId of projectIds) {
|
||||
const config = projects[projectId]
|
||||
const isActive = activeContext?.project_id === projectId
|
||||
const activeIndicator = isActive ? "⭐" : " "
|
||||
|
||||
// Build project title with subproject if applicable
|
||||
let projectTitle = projectId
|
||||
if (isActive && activeContext.subproject) {
|
||||
projectTitle = `${projectId} › ${activeContext.subproject}`
|
||||
}
|
||||
|
||||
console.log(`
|
||||
${activeIndicator} ${projectTitle}
|
||||
Name: ${config.name}
|
||||
Description: ${config.description || "N/A"}
|
||||
Linear: ${config.linear.team} / ${config.linear.project}
|
||||
Repo Type: ${config.code_repository?.type || "N/A"}
|
||||
External PM: ${config.external_pm?.enabled ? config.external_pm.type : "disabled"}`)
|
||||
|
||||
// Show subproject info for active project
|
||||
if (isActive && activeContext.subproject) {
|
||||
const subprojectConfig = config.code_repository?.subprojects?.find(
|
||||
s => s.name === activeContext.subproject
|
||||
)
|
||||
if (subprojectConfig) {
|
||||
console.log(` Subproject: 📁 ${subprojectConfig.path}`)
|
||||
const techStack = subprojectConfig.tech_stack
|
||||
if (techStack) {
|
||||
const langs = techStack.languages?.join(", ") || ""
|
||||
const frameworks = Object.values(techStack.frameworks || {}).flat().join(", ")
|
||||
console.log(` Tech Stack: ${[langs, frameworks].filter(Boolean).join(", ")}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
const detectionMethod = activeContext.detection_method || "unknown"
|
||||
const methodDisplay = {
|
||||
'manual': 'Manual setting',
|
||||
'git_remote': 'Git remote match',
|
||||
'subdirectory': 'Subdirectory match',
|
||||
'local_path': 'Local path match',
|
||||
'pattern': 'Custom pattern match'
|
||||
}[detectionMethod] || detectionMethod
|
||||
console.log(` Status: 🟢 Active (${methodDisplay})`)
|
||||
}
|
||||
|
||||
console.log("")
|
||||
console.log(" Commands:")
|
||||
console.log(` View details: /ccpm:project:show ${projectId}`)
|
||||
console.log(` Update config: /ccpm:project:update ${projectId}`)
|
||||
console.log(` Delete: /ccpm:project:delete ${projectId}`)
|
||||
console.log(` Set active: /ccpm:project:set ${projectId}`)
|
||||
console.log("")
|
||||
}
|
||||
|
||||
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
```
|
||||
|
||||
### Step 4: Show Quick Actions
|
||||
|
||||
```plaintext
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🚀 Quick Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Add new project: /ccpm:project:add <project-id>
|
||||
Show project info: /ccpm:project:show <project-id>
|
||||
Update project: /ccpm:project:update <project-id>
|
||||
Delete project: /ccpm:project:delete <project-id>
|
||||
|
||||
Configuration file: ~/.claude/ccpm-config.yaml
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
### Compact View (default)
|
||||
|
||||
**Example 1: Simple Project (no subdirectories)**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 CCPM Projects (3)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
⭐ my-app
|
||||
Name: My App
|
||||
Description: Example application with full PM integration
|
||||
Linear: Work / My App
|
||||
Repo Type: github
|
||||
External PM: jira
|
||||
Status: 🟢 Active (Git remote match)
|
||||
|
||||
Commands:
|
||||
View details: /ccpm:project:show my-app
|
||||
Update config: /ccpm:project:update my-app
|
||||
Delete: /ccpm:project:delete my-app
|
||||
Set active: /ccpm:project:set my-app
|
||||
|
||||
my-project
|
||||
Name: My Project
|
||||
Description: Another example project
|
||||
Linear: Work / My Project
|
||||
Repo Type: bitbucket
|
||||
External PM: jira
|
||||
|
||||
Commands:
|
||||
View details: /ccpm:project:show my-project
|
||||
Update config: /ccpm:project:update my-project
|
||||
Delete: /ccpm:project:delete my-project
|
||||
Set active: /ccpm:project:set my-project
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**Example 2: Monorepo with Active Subproject**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 CCPM Projects (2)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
⭐ repeat › jarvis
|
||||
Name: Repeat
|
||||
Description: Repeat.gg gaming platform - multi-project repository
|
||||
Linear: Work / Repeat
|
||||
Repo Type: bitbucket
|
||||
External PM: jira
|
||||
Subproject: 📁 jarvis
|
||||
Tech Stack: typescript, nextjs, react, nestjs
|
||||
Status: 🟢 Active (Subdirectory match)
|
||||
|
||||
Commands:
|
||||
View details: /ccpm:project:show repeat
|
||||
Update config: /ccpm:project:update repeat
|
||||
Delete: /ccpm:project:delete repeat
|
||||
Set active: /ccpm:project:set repeat
|
||||
|
||||
nv-internal
|
||||
Name: NV Internal
|
||||
Description: Task management application
|
||||
Linear: Personal / NV Internal
|
||||
Repo Type: github
|
||||
External PM: disabled
|
||||
|
||||
Commands:
|
||||
View details: /ccpm:project:show nv-internal
|
||||
Update config: /ccpm:project:update nv-internal
|
||||
Delete: /ccpm:project:delete nv-internal
|
||||
Set active: /ccpm:project:set nv-internal
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Detailed View (with --detailed flag)
|
||||
|
||||
Shows complete configuration for each project including:
|
||||
- All Linear settings
|
||||
- External PM details (Jira, Confluence, Slack)
|
||||
- Code repository configuration
|
||||
- Tech stack
|
||||
- Custom commands
|
||||
- Quality gates
|
||||
|
||||
## Active Project Detection
|
||||
|
||||
The command uses the `project-context-manager` agent to automatically detect the active project by:
|
||||
|
||||
**Detection Priority Order**:
|
||||
1. **Manual override** - User-set active project (highest priority)
|
||||
2. **Git remote URL** - Matches repository URL in config
|
||||
3. **Subdirectory patterns** - Matches working directory against configured patterns (NEW)
|
||||
4. **Local path** - Matches current directory path
|
||||
5. **Custom patterns** - User-defined glob patterns
|
||||
|
||||
**Subdirectory Detection** (NEW):
|
||||
- For monorepos, detects which subproject you're currently in
|
||||
- Displays as: `project-name › subproject-name`
|
||||
- Shows subproject path and tech stack
|
||||
- Detection method shown as "Subdirectory match"
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
# Working in /Users/dev/repeat/jarvis/src
|
||||
# Detects: project="repeat", subproject="jarvis"
|
||||
# Displays: ⭐ repeat › jarvis
|
||||
```
|
||||
|
||||
The active project is marked with ⭐ and always appears first in the list.
|
||||
|
||||
## Agent Integration
|
||||
|
||||
This command uses CCPM agents for efficient operation:
|
||||
|
||||
- **project-context-manager**: Detects active project and subproject
|
||||
- **project-config-loader**: Loads all project configurations
|
||||
- **project-detection skill**: Auto-activates for detection guidance
|
||||
- **project-operations skill**: Provides display format guidance
|
||||
|
||||
Token usage: ~200 tokens (vs ~2000 with inline logic)
|
||||
|
||||
## Notes
|
||||
|
||||
- Projects are listed with active first, then alphabetically
|
||||
- Active project shows detection method (git remote, subdirectory, etc.)
|
||||
- Subproject information shown only for active monorepo projects
|
||||
- Use `/ccpm:project:show <id>` for full details including all subprojects
|
||||
- Configuration file: `~/.claude/ccpm-config.yaml`
|
||||
324
commands/project:set.md
Normal file
324
commands/project:set.md
Normal file
@@ -0,0 +1,324 @@
|
||||
---
|
||||
description: Set the active project for CCPM commands
|
||||
allowed-tools: [Bash, Read, Edit, AskUserQuestion]
|
||||
argument-hint: <project-id>
|
||||
---
|
||||
|
||||
# Set Active CCPM Project
|
||||
|
||||
Set or change the currently active project for CCPM commands.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required, or "auto" for auto-detection)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set specific project as active
|
||||
/ccpm:project:set my-app
|
||||
|
||||
# Enable auto-detection
|
||||
/ccpm:project:set auto
|
||||
|
||||
# Clear active project
|
||||
/ccpm:project:set none
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate Configuration
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "❌ Error: No CCPM configuration found"
|
||||
echo ""
|
||||
echo "Create configuration:"
|
||||
echo " /ccpm:project:add <project-id>"
|
||||
exit(1)
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 2: Handle Special Values
|
||||
|
||||
#### If `$1 == "auto"`:
|
||||
|
||||
```bash
|
||||
# Enable auto-detection
|
||||
yq eval -i '.context.current_project = null' "$CONFIG_FILE"
|
||||
yq eval -i '.context.detection.by_git_remote = true' "$CONFIG_FILE"
|
||||
yq eval -i '.context.detection.by_cwd = true' "$CONFIG_FILE"
|
||||
|
||||
echo "✅ Auto-detection enabled"
|
||||
echo ""
|
||||
echo "CCPM will automatically detect your project based on:"
|
||||
echo " • Git remote URL"
|
||||
echo " • Current working directory"
|
||||
echo " • Custom detection patterns"
|
||||
echo ""
|
||||
echo "Test auto-detection:"
|
||||
echo " /ccpm:project:list # Active project will be marked with ⭐"
|
||||
exit(0)
|
||||
```
|
||||
|
||||
#### If `$1 == "none"` or `$1 == "clear"`:
|
||||
|
||||
```bash
|
||||
# Clear active project
|
||||
yq eval -i '.context.current_project = null' "$CONFIG_FILE"
|
||||
yq eval -i '.context.detection.by_git_remote = false' "$CONFIG_FILE"
|
||||
yq eval -i '.context.detection.by_cwd = false' "$CONFIG_FILE"
|
||||
|
||||
echo "✅ Active project cleared"
|
||||
echo ""
|
||||
echo "CCPM commands will now prompt you to select a project."
|
||||
echo ""
|
||||
echo "To set an active project:"
|
||||
echo " /ccpm:project:set <project-id>"
|
||||
exit(0)
|
||||
```
|
||||
|
||||
### Step 3: Validate Project Exists
|
||||
|
||||
```bash
|
||||
PROJECT_ID=$1
|
||||
|
||||
# Check if project exists in configuration
|
||||
if ! yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
|
||||
echo "❌ Error: Project '$PROJECT_ID' not found"
|
||||
echo ""
|
||||
echo "Available projects:"
|
||||
yq eval '.projects | keys | .[]' "$CONFIG_FILE"
|
||||
echo ""
|
||||
echo "Add new project:"
|
||||
echo " /ccpm:project:add $PROJECT_ID"
|
||||
exit(1)
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 4: Show Project Info
|
||||
|
||||
```javascript
|
||||
const projectConfig = await yq(`.projects.${projectId}`, CONFIG_FILE)
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Set Active Project: ${projectId}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project Details:
|
||||
Name: ${projectConfig.name}
|
||||
Description: ${projectConfig.description || "N/A"}
|
||||
Linear: ${projectConfig.linear.team} / ${projectConfig.linear.project}
|
||||
Repository: ${projectConfig.repository?.url || "N/A"}
|
||||
|
||||
This project will be used by default for all CCPM commands.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 5: Confirm (optional)
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: `Set '${projectId}' as your active project?`,
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, set as active",
|
||||
description: "Use this project by default"
|
||||
},
|
||||
{
|
||||
label: "No, cancel",
|
||||
description: "Don't change active project"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Set Active Project
|
||||
|
||||
```bash
|
||||
# Set the active project
|
||||
yq eval -i ".context.current_project = \"$PROJECT_ID\"" "$CONFIG_FILE"
|
||||
|
||||
# Optionally disable auto-detection to enforce this choice
|
||||
yq eval -i '.context.detection.by_git_remote = false' "$CONFIG_FILE"
|
||||
yq eval -i '.context.detection.by_cwd = false' "$CONFIG_FILE"
|
||||
|
||||
echo ""
|
||||
echo "✅ Active project set to: $PROJECT_ID"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 This project will be used for:"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo " • All CCPM planning commands"
|
||||
echo " • Implementation and verification commands"
|
||||
echo " • Project-specific custom commands"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🚀 Quick Start"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "Create a task:"
|
||||
echo " /ccpm:planning:create \"Task title\""
|
||||
echo " (no project ID needed - uses active project)"
|
||||
echo ""
|
||||
echo "View project status:"
|
||||
echo " /ccpm:project:show $PROJECT_ID"
|
||||
echo ""
|
||||
echo "Change active project:"
|
||||
echo " /ccpm:project:set <different-project-id>"
|
||||
echo ""
|
||||
echo "Enable auto-detection:"
|
||||
echo " /ccpm:project:set auto"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
```
|
||||
|
||||
## Auto-Detection vs Manual Setting
|
||||
|
||||
### Manual Setting
|
||||
|
||||
When you explicitly set a project with `/ccpm:project:set <project-id>`:
|
||||
- Project is used **everywhere**, regardless of current directory
|
||||
- Auto-detection is **disabled**
|
||||
- Consistent project across all terminal sessions
|
||||
- Good for focused work on one project
|
||||
|
||||
### Auto-Detection
|
||||
|
||||
When you enable auto-detection with `/ccpm:project:set auto`:
|
||||
- Project is detected based on:
|
||||
1. Git remote URL matching
|
||||
2. Current working directory path matching
|
||||
3. Custom detection patterns
|
||||
- Different directories can have different active projects
|
||||
- More flexible for multi-project work
|
||||
- Project changes as you `cd` between directories
|
||||
|
||||
## Detection Methods
|
||||
|
||||
### 1. Git Remote URL Matching
|
||||
|
||||
```yaml
|
||||
# In ccpm-config.yaml
|
||||
projects:
|
||||
my-app:
|
||||
repository:
|
||||
url: "https://github.com/company/my-app"
|
||||
```
|
||||
|
||||
When `by_git_remote: true`:
|
||||
```bash
|
||||
cd ~/code/my-app
|
||||
git remote get-url origin
|
||||
# → https://github.com/company/my-app
|
||||
|
||||
# CCPM detects: "my-app" is active
|
||||
```
|
||||
|
||||
### 2. Current Working Directory
|
||||
|
||||
```yaml
|
||||
# In ccpm-config.yaml
|
||||
context:
|
||||
detection:
|
||||
patterns:
|
||||
- pattern: "*/my-app*"
|
||||
project: my-app
|
||||
- pattern: "*/frontend/*"
|
||||
project: my-fullstack-app
|
||||
```
|
||||
|
||||
When `by_cwd: true`:
|
||||
```bash
|
||||
cd ~/code/my-app/src
|
||||
# Path matches "*/my-app*"
|
||||
# CCPM detects: "my-app" is active
|
||||
|
||||
cd ~/code/frontend/dashboard
|
||||
# Path matches "*/frontend/*"
|
||||
# CCPM detects: "my-fullstack-app" is active
|
||||
```
|
||||
|
||||
### 3. Priority Order
|
||||
|
||||
If multiple detection methods match:
|
||||
1. **Manual setting** (highest priority)
|
||||
2. Git remote URL match
|
||||
3. Current working directory match
|
||||
4. Custom patterns
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Set active project
|
||||
|
||||
```bash
|
||||
/ccpm:project:set my-app
|
||||
|
||||
# ✅ Active project set to: my-app
|
||||
# All CCPM commands now default to this project
|
||||
```
|
||||
|
||||
### Example 2: Enable auto-detection
|
||||
|
||||
```bash
|
||||
/ccpm:project:set auto
|
||||
|
||||
# ✅ Auto-detection enabled
|
||||
# Project will be detected based on:
|
||||
# • Git remote URL
|
||||
# • Current directory
|
||||
```
|
||||
|
||||
### Example 3: Clear active project
|
||||
|
||||
```bash
|
||||
/ccpm:project:set none
|
||||
|
||||
# ✅ Active project cleared
|
||||
# CCPM will prompt for project selection
|
||||
```
|
||||
|
||||
### Example 4: Switch between projects
|
||||
|
||||
```bash
|
||||
# Working on project A
|
||||
/ccpm:project:set project-a
|
||||
/ccpm:planning:create "Task for A"
|
||||
|
||||
# Switch to project B
|
||||
/ccpm:project:set project-b
|
||||
/ccpm:planning:create "Task for B"
|
||||
|
||||
# Back to auto-detection
|
||||
/ccpm:project:set auto
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
After setting active project, verify with:
|
||||
|
||||
```bash
|
||||
# See active project marked with ⭐
|
||||
/ccpm:project:list
|
||||
|
||||
# Or view project details
|
||||
/ccpm:project:show <project-id>
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Active project setting is global (affects all terminal sessions)
|
||||
- Manual setting overrides auto-detection
|
||||
- Auto-detection is more flexible for multi-project work
|
||||
- Can switch projects anytime with `/ccpm:project:set`
|
||||
- Configuration file: `~/.claude/ccpm-config.yaml`
|
||||
396
commands/project:show.md
Normal file
396
commands/project:show.md
Normal file
@@ -0,0 +1,396 @@
|
||||
---
|
||||
description: Show detailed configuration for a specific project
|
||||
argument-hint: <project-id>
|
||||
---
|
||||
|
||||
# Show Project Details
|
||||
|
||||
Display complete configuration for a specific CCPM project, including subdirectory/subproject information for monorepos.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/ccpm:project:show my-app
|
||||
/ccpm:project:show repeat # Shows all subprojects in monorepo
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Auto-Activate Skills
|
||||
|
||||
```markdown
|
||||
Skill(project-operations): Provides display format guidance
|
||||
Skill(project-detection): Helps with detection context
|
||||
```
|
||||
|
||||
### Step 2: Validate Arguments
|
||||
|
||||
```javascript
|
||||
const projectId = $1
|
||||
|
||||
if (!projectId) {
|
||||
console.log("❌ Error: Project ID required")
|
||||
console.log("Usage: /ccpm:project:show <project-id>")
|
||||
console.log("")
|
||||
console.log("Available projects:")
|
||||
console.log(" /ccpm:project:list")
|
||||
exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Load Project Configuration
|
||||
|
||||
Use project-config-loader agent:
|
||||
|
||||
```javascript
|
||||
const projectConfig = Task(project-config-loader): `
|
||||
Load configuration for project: ${projectId}
|
||||
Include all sections: true
|
||||
Validate: true
|
||||
`
|
||||
|
||||
if (projectConfig.error) {
|
||||
if (projectConfig.error.code === 'PROJECT_NOT_FOUND') {
|
||||
console.log(`❌ Error: Project '${projectId}' not found`)
|
||||
console.log("")
|
||||
console.log("Available projects:")
|
||||
projectConfig.error.available_projects.forEach(p => console.log(` - ${p}`))
|
||||
console.log("")
|
||||
console.log("View all: /ccpm:project:list")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
console.error(`❌ Error: ${projectConfig.error.message}`)
|
||||
exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Check if Active
|
||||
|
||||
Use project-context-manager to check if this is the active project:
|
||||
|
||||
```javascript
|
||||
const activeContext = Task(project-context-manager): `
|
||||
Get active project context
|
||||
Format: compact
|
||||
`
|
||||
|
||||
const isActive = activeContext?.project_id === projectId
|
||||
const activeSubproject = isActive ? activeContext.subproject : null
|
||||
```
|
||||
|
||||
### Step 5: Display Complete Configuration
|
||||
|
||||
```javascript
|
||||
const config = projectConfig
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Project: ${projectId} ${isActive ? "⭐ (Active)" : ""}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## Project Information
|
||||
|
||||
Name: ${config.project_name}
|
||||
Description: ${config.description || "N/A"}
|
||||
Owner: ${config.owner || "N/A"}
|
||||
|
||||
Repository:
|
||||
URL: ${config.repository.url || "N/A"}
|
||||
Branch: ${config.repository.default_branch}
|
||||
Local Path: ${config.repository.local_path || "N/A"}
|
||||
|
||||
${isActive ? `
|
||||
Detection:
|
||||
Method: ${activeContext.detection_method}
|
||||
${activeSubproject ? `Subproject: ${activeSubproject}` : ""}
|
||||
` : ""}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Linear Configuration
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Team: ${config.linear.team}
|
||||
Project: ${config.linear.project}
|
||||
Labels: ${config.linear.default_labels?.join(", ") || "N/A"}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## External PM Integration
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Status: ${config.external_pm.enabled ? "✅ Enabled" : "❌ Disabled"}
|
||||
Type: ${config.external_pm.type}
|
||||
`)
|
||||
|
||||
if (config.external_pm.enabled && config.external_pm.jira?.enabled) {
|
||||
console.log(`
|
||||
### Jira
|
||||
Enabled: ✅
|
||||
Base URL: ${config.external_pm.jira.base_url}
|
||||
Project Key: ${config.external_pm.jira.project_key}
|
||||
`)
|
||||
}
|
||||
|
||||
if (config.external_pm.enabled && config.external_pm.confluence?.enabled) {
|
||||
console.log(`
|
||||
### Confluence
|
||||
Enabled: ✅
|
||||
Base URL: ${config.external_pm.confluence.base_url}
|
||||
Space Key: ${config.external_pm.confluence.space_key}
|
||||
`)
|
||||
}
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Code Repository
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Type: ${config.code_repository.type}
|
||||
`)
|
||||
|
||||
if (config.code_repository.type === "github" && config.code_repository.github?.enabled) {
|
||||
console.log(`
|
||||
### GitHub
|
||||
Owner: ${config.code_repository.github.owner}
|
||||
Repository: ${config.code_repository.github.repo}
|
||||
URL: https://github.com/${config.code_repository.github.owner}/${config.code_repository.github.repo}
|
||||
`)
|
||||
}
|
||||
|
||||
if (config.code_repository.type === "bitbucket" && config.code_repository.bitbucket?.enabled) {
|
||||
console.log(`
|
||||
### BitBucket
|
||||
Workspace: ${config.code_repository.bitbucket.workspace}
|
||||
URL: ${config.code_repository.bitbucket.base_url}
|
||||
`)
|
||||
}
|
||||
|
||||
// NEW: Display subprojects if configured
|
||||
if (config.code_repository.subprojects && config.code_repository.subprojects.length > 0) {
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Subprojects (Monorepo) ${activeSubproject ? `⭐ Active: ${activeSubproject}` : ""}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
config.code_repository.subprojects.forEach(subproject => {
|
||||
const isActiveSub = activeSubproject === subproject.name
|
||||
const indicator = isActiveSub ? "⭐" : " "
|
||||
|
||||
console.log(`
|
||||
${indicator} ${subproject.name}
|
||||
Description: ${subproject.description || "N/A"}
|
||||
Path: 📁 ${subproject.path}`)
|
||||
|
||||
if (subproject.tech_stack) {
|
||||
const langs = subproject.tech_stack.languages?.join(", ") || ""
|
||||
if (langs) console.log(` Languages: ${langs}`)
|
||||
|
||||
if (subproject.tech_stack.frameworks) {
|
||||
const frameworks = Object.entries(subproject.tech_stack.frameworks)
|
||||
.map(([type, fws]) => `${type}: ${fws.join(", ")}`)
|
||||
.join(", ")
|
||||
if (frameworks) console.log(` Frameworks: ${frameworks}`)
|
||||
}
|
||||
|
||||
const dbs = subproject.tech_stack.database?.join(", ") || ""
|
||||
if (dbs) console.log(` Database: ${dbs}`)
|
||||
}
|
||||
|
||||
console.log("")
|
||||
})
|
||||
|
||||
console.log(`
|
||||
Subdirectory Detection:
|
||||
Configured: ${config.context?.detection?.subdirectories ? "✅ Yes" : "❌ No"}
|
||||
`)
|
||||
|
||||
if (config.context?.detection?.subdirectories) {
|
||||
console.log(" Patterns:")
|
||||
config.context.detection.subdirectories.forEach(pattern => {
|
||||
console.log(` - ${pattern.match_pattern} → ${pattern.subproject} (priority: ${pattern.priority || 0})`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Tech Stack (Overall)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
if (config.tech_stack.languages) {
|
||||
console.log(`Languages: ${config.tech_stack.languages.join(", ")}`)
|
||||
}
|
||||
|
||||
if (config.tech_stack.frameworks) {
|
||||
if (config.tech_stack.frameworks.frontend) {
|
||||
console.log(`Frontend: ${config.tech_stack.frameworks.frontend.join(", ")}`)
|
||||
}
|
||||
if (config.tech_stack.frameworks.backend) {
|
||||
console.log(`Backend: ${config.tech_stack.frameworks.backend.join(", ")}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.tech_stack.database) {
|
||||
console.log(`Database: ${config.tech_stack.database.join(", ")}`)
|
||||
}
|
||||
|
||||
if (config.tech_stack.infrastructure) {
|
||||
console.log(`Infra: ${config.tech_stack.infrastructure.join(", ")}`)
|
||||
}
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🚀 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Set as active: /ccpm:project:set ${projectId}
|
||||
Update config: /ccpm:project:update ${projectId}
|
||||
Delete project: /ccpm:project:delete ${projectId}
|
||||
List all: /ccpm:project:list
|
||||
|
||||
${config.code_repository.subprojects && config.code_repository.subprojects.length > 0 ? `
|
||||
For subdirectory detection to work:
|
||||
1. Navigate to a subproject directory
|
||||
2. CCPM will auto-detect the active subproject
|
||||
3. All commands will use that context
|
||||
` : ""}
|
||||
|
||||
Configuration file: ~/.claude/ccpm-config.yaml
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
## Agent Integration
|
||||
|
||||
This command uses CCPM agents:
|
||||
|
||||
- **project-config-loader**: Loads and validates project configuration
|
||||
- **project-context-manager**: Checks if project is currently active
|
||||
- **project-operations skill**: Provides display format guidance
|
||||
- **project-detection skill**: Auto-activates for context awareness
|
||||
|
||||
Token usage: ~200 tokens (vs ~2000 with inline logic)
|
||||
|
||||
## Example Output
|
||||
|
||||
### Simple Project
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Project: my-app ⭐ (Active)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## Project Information
|
||||
|
||||
Name: My App
|
||||
Description: Example application
|
||||
Owner: john.doe
|
||||
|
||||
Repository:
|
||||
URL: https://github.com/org/my-app
|
||||
Branch: main
|
||||
Local Path: /Users/dev/my-app
|
||||
|
||||
Detection:
|
||||
Method: git_remote
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Linear Configuration
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Team: Engineering
|
||||
Project: My App
|
||||
Labels: my-app, planning
|
||||
|
||||
[... rest of configuration ...]
|
||||
```
|
||||
|
||||
### Monorepo with Subprojects
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Project: repeat ⭐ (Active)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## Project Information
|
||||
|
||||
Name: Repeat
|
||||
Description: Repeat.gg gaming platform - multi-project repository
|
||||
Owner: duongdev
|
||||
|
||||
Repository:
|
||||
URL: https://bitbucket.org/repeatgg/repeat
|
||||
Branch: develop
|
||||
Local Path: /Users/duongdev/repeat
|
||||
|
||||
Detection:
|
||||
Method: subdirectory
|
||||
Subproject: jarvis
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Subprojects (Monorepo) ⭐ Active: jarvis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
xygaming_symfony
|
||||
Description: Legacy Symfony 4.4 PHP web application
|
||||
Path: 📁 xygaming_symfony
|
||||
Languages: php
|
||||
Frameworks: backend: symfony
|
||||
Database: mysql, redis
|
||||
|
||||
⭐ jarvis
|
||||
Description: Modern admin web application (TurboRepo)
|
||||
Path: 📁 jarvis
|
||||
Languages: typescript
|
||||
Frameworks: frontend: nextjs, react, backend: nestjs
|
||||
Database: mysql, prisma
|
||||
|
||||
repeat-mobile-app
|
||||
Description: React Native mobile application
|
||||
Path: 📁 repeat-mobile-app
|
||||
Languages: typescript, javascript
|
||||
Frameworks: frontend: react-native, expo
|
||||
|
||||
messaging
|
||||
Description: Node.js microservice
|
||||
Path: 📁 messaging
|
||||
Languages: javascript, typescript
|
||||
Frameworks: backend: nodejs
|
||||
|
||||
Subdirectory Detection:
|
||||
Configured: ✅ Yes
|
||||
Patterns:
|
||||
- */xygaming_symfony/* → xygaming_symfony (priority: 10)
|
||||
- */jarvis/* → jarvis (priority: 10)
|
||||
- */repeat-mobile-app/* → repeat-mobile-app (priority: 10)
|
||||
- */messaging/* → messaging (priority: 10)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
## Tech Stack (Overall)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Languages: php, typescript, javascript
|
||||
Frontend: nextjs, react, react-native
|
||||
Backend: symfony, nestjs, nodejs
|
||||
Database: mysql, redis
|
||||
Infra: aws-ecs, aws-s3, aws-ssm, firebase, kafka
|
||||
|
||||
[... rest of configuration ...]
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **NEW**: Displays all subprojects in monorepo configuration
|
||||
- **NEW**: Shows active subproject with ⭐ marker
|
||||
- **NEW**: Displays subdirectory detection patterns
|
||||
- **NEW**: Shows tech stack per subproject
|
||||
- Uses agents for efficient configuration loading
|
||||
- Validates project exists before displaying
|
||||
- Provides actionable quick commands
|
||||
357
commands/project:subdir:add.md
Normal file
357
commands/project:subdir:add.md
Normal file
@@ -0,0 +1,357 @@
|
||||
---
|
||||
description: Add a subdirectory/subproject to a project for monorepo support
|
||||
argument-hint: <project-id> <subproject-name> <path> [--pattern <pattern>] [--priority <priority>]
|
||||
---
|
||||
|
||||
# Add Subdirectory to Project
|
||||
|
||||
Add subdirectory configuration to a project for automatic detection in monorepos.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
- **$2** - Subproject name (required)
|
||||
- **$3** - Subproject path (required, relative to repo root)
|
||||
- **--pattern** - Match pattern (optional, default: `*/${path}/*`)
|
||||
- **--priority** - Detection priority (optional, default: 10)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Add with defaults
|
||||
/ccpm:project:subdir:add repeat jarvis jarvis
|
||||
|
||||
# Add with custom pattern and priority
|
||||
/ccpm:project:subdir:add repeat jarvis jarvis --pattern "*/jarvis/**" --priority 15
|
||||
|
||||
# Interactive mode (prompts for details)
|
||||
/ccpm:project:subdir:add repeat
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Auto-Activate Skills
|
||||
|
||||
```markdown
|
||||
Skill(project-operations): Provides configuration guidance
|
||||
```
|
||||
|
||||
### Step 2: Parse Arguments
|
||||
|
||||
```javascript
|
||||
const projectId = "$1"
|
||||
const subprojectName = "$2"
|
||||
const subprojectPath = "$3"
|
||||
|
||||
if (!projectId) {
|
||||
console.log("❌ Error: Project ID required")
|
||||
console.log("Usage: /ccpm:project:subdir:add <project-id> <name> <path>")
|
||||
console.log("")
|
||||
console.log("Available projects:")
|
||||
console.log(" /ccpm:project:list")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Check if interactive mode (only project ID provided)
|
||||
const interactiveMode = !subprojectName
|
||||
```
|
||||
|
||||
### Step 3: Load Project Configuration
|
||||
|
||||
```javascript
|
||||
const projectConfig = Task(project-config-loader): `
|
||||
Load configuration for project: ${projectId}
|
||||
Validate: true
|
||||
`
|
||||
|
||||
if (projectConfig.error) {
|
||||
console.error(`❌ ${projectConfig.error.message}`)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
console.log(`📋 Project: ${projectConfig.project_name}`)
|
||||
console.log(`📁 Repository: ${projectConfig.repository.local_path || "Not configured"}`)
|
||||
console.log("")
|
||||
```
|
||||
|
||||
### Step 4: Interactive Input (if needed)
|
||||
|
||||
If in interactive mode, use AskUserQuestion to gather details:
|
||||
|
||||
```javascript
|
||||
if (interactiveMode) {
|
||||
const answers = AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "What is the subproject name? (e.g., 'frontend', 'mobile-app')",
|
||||
header: "Name",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter custom name",
|
||||
description: "Provide a unique name for this subproject"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "What is the relative path to this subproject? (e.g., 'apps/frontend', 'packages/mobile')",
|
||||
header: "Path",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "apps/*",
|
||||
description: "Subproject in apps directory"
|
||||
},
|
||||
{
|
||||
label: "packages/*",
|
||||
description: "Subproject in packages directory"
|
||||
},
|
||||
{
|
||||
label: "services/*",
|
||||
description: "Subproject in services directory"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
subprojectName = answers["What is the subproject name?"]
|
||||
subprojectPath = answers["What is the relative path to this subproject?"]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Build Configuration
|
||||
|
||||
```javascript
|
||||
// Parse optional flags
|
||||
const customPattern = getFlag("--pattern")
|
||||
const customPriority = getFlag("--priority")
|
||||
|
||||
// Build subdirectory detection entry
|
||||
const detectionEntry = {
|
||||
subproject: subprojectName,
|
||||
match_pattern: customPattern || `*/${subprojectPath}/*`,
|
||||
priority: customPriority ? parseInt(customPriority) : 10
|
||||
}
|
||||
|
||||
// Build subproject metadata entry
|
||||
const metadataEntry = {
|
||||
name: subprojectName,
|
||||
path: subprojectPath,
|
||||
description: "", // Will prompt user
|
||||
tech_stack: {} // Can be filled later
|
||||
}
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📁 New Subdirectory Configuration
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Subproject Name: ${detectionEntry.subproject}
|
||||
Path: ${metadataEntry.path}
|
||||
Match Pattern: ${detectionEntry.match_pattern}
|
||||
Priority: ${detectionEntry.priority}
|
||||
|
||||
This will be added to project: ${projectId}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 6: Confirm and Update Configuration
|
||||
|
||||
```javascript
|
||||
const confirmation = AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Add this subdirectory configuration?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, add subdirectory",
|
||||
description: "Update ccpm-config.yaml with this configuration"
|
||||
},
|
||||
{
|
||||
label: "No, cancel",
|
||||
description: "Don't make any changes"
|
||||
}
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
if (confirmation["Add this subdirectory configuration?"] === "No, cancel") {
|
||||
console.log("❌ Cancelled")
|
||||
exit(0)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: Update Configuration File
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
|
||||
# Read current configuration
|
||||
current_config=$(cat "$CONFIG_FILE")
|
||||
|
||||
# Add to context.detection.subdirectories
|
||||
# (This is pseudocode - actual implementation would use yq or python)
|
||||
yq eval -i ".projects.${projectId}.context.detection.subdirectories += [{
|
||||
\"subproject\": \"${subprojectName}\",
|
||||
\"match_pattern\": \"${matchPattern}\",
|
||||
\"priority\": ${priority}
|
||||
}]" "$CONFIG_FILE"
|
||||
|
||||
# Add to code_repository.subprojects
|
||||
yq eval -i ".projects.${projectId}.code_repository.subprojects += [{
|
||||
\"name\": \"${subprojectName}\",
|
||||
\"path\": \"${subprojectPath}\",
|
||||
\"description\": \"\",
|
||||
\"tech_stack\": {}
|
||||
}]" "$CONFIG_FILE"
|
||||
|
||||
echo "✅ Subdirectory configuration added!"
|
||||
```
|
||||
|
||||
### Step 8: Validate and Display Results
|
||||
|
||||
```javascript
|
||||
// Reload configuration to validate
|
||||
const updatedConfig = Task(project-config-loader): `
|
||||
Load configuration for project: ${projectId}
|
||||
Validate: true
|
||||
`
|
||||
|
||||
if (updatedConfig.error) {
|
||||
console.error("⚠️ Warning: Configuration validation failed")
|
||||
console.error(updatedConfig.error.message)
|
||||
console.log("")
|
||||
console.log("Please check the configuration file:")
|
||||
console.log(` ~/.claude/ccpm-config.yaml`)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Subdirectory Added Successfully
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectId}
|
||||
Subproject: ${subprojectName}
|
||||
Path: ${subprojectPath}
|
||||
Match Pattern: ${detectionEntry.match_pattern}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🧪 Test Detection
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
To test automatic detection:
|
||||
cd ${projectConfig.repository.local_path}/${subprojectPath}
|
||||
/ccpm:project:list
|
||||
|
||||
Expected result:
|
||||
⭐ ${projectId} › ${subprojectName}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Update tech stack:
|
||||
/ccpm:project:subdir:update ${projectId} ${subprojectName} --tech-stack
|
||||
|
||||
Add more subdirectories:
|
||||
/ccpm:project:subdir:add ${projectId}
|
||||
|
||||
View all subdirectories:
|
||||
/ccpm:project:subdir:list ${projectId}
|
||||
|
||||
View project details:
|
||||
/ccpm:project:show ${projectId}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Add Frontend Subproject
|
||||
|
||||
```bash
|
||||
/ccpm:project:subdir:add my-monorepo frontend apps/frontend
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
📋 Project: My Monorepo
|
||||
📁 Repository: /Users/dev/my-monorepo
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📁 New Subdirectory Configuration
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Subproject Name: frontend
|
||||
Path: apps/frontend
|
||||
Match Pattern: */apps/frontend/*
|
||||
Priority: 10
|
||||
|
||||
✅ Subdirectory Added Successfully
|
||||
```
|
||||
|
||||
### Example 2: Add with Custom Pattern
|
||||
|
||||
```bash
|
||||
/ccpm:project:subdir:add my-monorepo backend services/api --pattern "**/services/api/**" --priority 15
|
||||
```
|
||||
|
||||
### Example 3: Interactive Mode
|
||||
|
||||
```bash
|
||||
/ccpm:project:subdir:add my-monorepo
|
||||
```
|
||||
|
||||
Then answer prompts for name, path, tech stack, etc.
|
||||
|
||||
## Pattern Matching
|
||||
|
||||
**Glob Pattern Format**:
|
||||
- `*/path/*` - Matches any nesting level with path in between
|
||||
- `**/path/**` - Matches path at any depth
|
||||
- `apps/*/src` - Matches specific structure
|
||||
|
||||
**Priority Guidelines**:
|
||||
- **10** (default) - Standard subprojects
|
||||
- **15-20** - More specific patterns (nested paths)
|
||||
- **5** - Less specific, fallback patterns
|
||||
|
||||
**Examples**:
|
||||
```yaml
|
||||
# Specific nested path (high priority)
|
||||
- subproject: admin-panel
|
||||
match_pattern: "*/apps/web/admin/*"
|
||||
priority: 20
|
||||
|
||||
# General web app (standard priority)
|
||||
- subproject: web-app
|
||||
match_pattern: "*/apps/web/*"
|
||||
priority: 10
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Subdirectory detection requires `repository.local_path` to be configured
|
||||
- Patterns are matched against the current working directory
|
||||
- Higher priority patterns are matched first
|
||||
- Use `/ccpm:project:show <project-id>` to see all configured subdirectories
|
||||
- Tech stack can be added later with `/ccpm:project:subdir:update`
|
||||
|
||||
## Agent Integration
|
||||
|
||||
Uses these agents:
|
||||
- **project-config-loader**: Loads and validates project configuration
|
||||
- **project-operations skill**: Provides configuration guidance
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/ccpm:project:subdir:list` - List all subdirectories
|
||||
- `/ccpm:project:subdir:update` - Update subdirectory details
|
||||
- `/ccpm:project:subdir:remove` - Remove subdirectory
|
||||
- `/ccpm:project:show` - View complete project configuration
|
||||
301
commands/project:subdir:list.md
Normal file
301
commands/project:subdir:list.md
Normal file
@@ -0,0 +1,301 @@
|
||||
---
|
||||
description: List all subdirectories/subprojects configured for a project
|
||||
argument-hint: <project-id>
|
||||
---
|
||||
|
||||
# List Project Subdirectories
|
||||
|
||||
Display all subdirectories configured for a monorepo project with their detection patterns and metadata.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/ccpm:project:subdir:list repeat
|
||||
/ccpm:project:subdir:list my-monorepo
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Auto-Activate Skills
|
||||
|
||||
```markdown
|
||||
Skill(project-operations): Provides display guidance
|
||||
Skill(project-detection): Provides detection context
|
||||
```
|
||||
|
||||
### Step 2: Load Project Configuration
|
||||
|
||||
```javascript
|
||||
const projectId = "$1"
|
||||
|
||||
if (!projectId) {
|
||||
console.log("❌ Error: Project ID required")
|
||||
console.log("Usage: /ccpm:project:subdir:list <project-id>")
|
||||
console.log("")
|
||||
console.log("Available projects:")
|
||||
console.log(" /ccpm:project:list")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
const projectConfig = Task(project-config-loader): `
|
||||
Load configuration for project: ${projectId}
|
||||
Include all sections: true
|
||||
Validate: false
|
||||
`
|
||||
|
||||
if (projectConfig.error) {
|
||||
console.error(`❌ ${projectConfig.error.message}`)
|
||||
exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Check for Subdirectories
|
||||
|
||||
```javascript
|
||||
const hasDetection = projectConfig.context?.detection?.subdirectories?.length > 0
|
||||
const hasMetadata = projectConfig.code_repository?.subprojects?.length > 0
|
||||
|
||||
if (!hasDetection && !hasMetadata) {
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📁 No Subdirectories Configured
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectConfig.project_name} (${projectId})
|
||||
|
||||
This project doesn't have subdirectory detection configured.
|
||||
|
||||
Add subdirectories for monorepo support:
|
||||
/ccpm:project:subdir:add ${projectId}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
exit(0)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Get Active Subproject
|
||||
|
||||
```javascript
|
||||
const activeContext = Task(project-context-manager): `
|
||||
Get active project context
|
||||
Format: compact
|
||||
`
|
||||
|
||||
const isThisProjectActive = activeContext?.project_id === projectId
|
||||
const activeSubproject = isThisProjectActive ? activeContext.subproject : null
|
||||
```
|
||||
|
||||
### Step 5: Display Subdirectories
|
||||
|
||||
```javascript
|
||||
const detectionConfig = projectConfig.context?.detection?.subdirectories || []
|
||||
const metadataConfig = projectConfig.code_repository?.subprojects || []
|
||||
|
||||
// Combine both sources
|
||||
const allSubprojects = new Map()
|
||||
|
||||
// Add from metadata
|
||||
metadataConfig.forEach(meta => {
|
||||
allSubprojects.set(meta.name, {
|
||||
name: meta.name,
|
||||
path: meta.path,
|
||||
description: meta.description,
|
||||
tech_stack: meta.tech_stack,
|
||||
detection: null
|
||||
})
|
||||
})
|
||||
|
||||
// Add detection info
|
||||
detectionConfig.forEach(det => {
|
||||
if (allSubprojects.has(det.subproject)) {
|
||||
allSubprojects.get(det.subproject).detection = det
|
||||
} else {
|
||||
allSubprojects.set(det.subproject, {
|
||||
name: det.subproject,
|
||||
path: null,
|
||||
description: null,
|
||||
tech_stack: null,
|
||||
detection: det
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📁 Subdirectories for ${projectConfig.project_name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project ID: ${projectId}
|
||||
Repository: ${projectConfig.repository.local_path || "Not configured"}
|
||||
Total Subprojects: ${allSubprojects.size}
|
||||
${isThisProjectActive && activeSubproject ? `Active: ⭐ ${activeSubproject}` : ""}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
allSubprojects.forEach((subproject, name) => {
|
||||
const isActive = activeSubproject === name
|
||||
const indicator = isActive ? "⭐" : " "
|
||||
|
||||
console.log(`
|
||||
${indicator} ${name}
|
||||
Path: 📁 ${subproject.path || "Not configured"}
|
||||
Description: ${subproject.description || "N/A"}`)
|
||||
|
||||
if (subproject.detection) {
|
||||
console.log(` Match Pattern: ${subproject.detection.match_pattern}`)
|
||||
console.log(` Priority: ${subproject.detection.priority || 0}`)
|
||||
} else {
|
||||
console.log(` Detection: ⚠️ Not configured`)
|
||||
}
|
||||
|
||||
if (subproject.tech_stack) {
|
||||
const langs = subproject.tech_stack.languages?.join(", ") || ""
|
||||
if (langs) console.log(` Languages: ${langs}`)
|
||||
|
||||
if (subproject.tech_stack.frameworks) {
|
||||
const frameworks = Object.entries(subproject.tech_stack.frameworks)
|
||||
.map(([type, fws]) => `${type}: ${fws.join(", ")}`)
|
||||
.join(", ")
|
||||
if (frameworks) console.log(` Frameworks: ${frameworks}`)
|
||||
}
|
||||
|
||||
const dbs = subproject.tech_stack.database?.join(", ") || ""
|
||||
if (dbs) console.log(` Database: ${dbs}`)
|
||||
}
|
||||
|
||||
console.log("")
|
||||
console.log(` Commands:`)
|
||||
console.log(` Update: /ccpm:project:subdir:update ${projectId} ${name}`)
|
||||
console.log(` Remove: /ccpm:project:subdir:remove ${projectId} ${name}`)
|
||||
console.log("")
|
||||
})
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🚀 Quick Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Add new subdirectory:
|
||||
/ccpm:project:subdir:add ${projectId}
|
||||
|
||||
View complete project details:
|
||||
/ccpm:project:show ${projectId}
|
||||
|
||||
Test detection:
|
||||
cd ${projectConfig.repository.local_path}/<subproject-path>
|
||||
/ccpm:project:list
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📁 Subdirectories for Repeat
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project ID: repeat
|
||||
Repository: /Users/duongdev/repeat
|
||||
Total Subprojects: 4
|
||||
Active: ⭐ jarvis
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
xygaming_symfony
|
||||
Path: 📁 xygaming_symfony
|
||||
Description: Legacy Symfony 4.4 PHP web application
|
||||
Match Pattern: */xygaming_symfony/*
|
||||
Priority: 10
|
||||
Languages: php
|
||||
Frameworks: backend: symfony
|
||||
Database: mysql, redis
|
||||
|
||||
Commands:
|
||||
Update: /ccpm:project:subdir:update repeat xygaming_symfony
|
||||
Remove: /ccpm:project:subdir:remove repeat xygaming_symfony
|
||||
|
||||
⭐ jarvis
|
||||
Path: 📁 jarvis
|
||||
Description: Modern admin web application (TurboRepo)
|
||||
Match Pattern: */jarvis/*
|
||||
Priority: 10
|
||||
Languages: typescript
|
||||
Frameworks: frontend: nextjs, react, backend: nestjs
|
||||
Database: mysql, prisma
|
||||
|
||||
Commands:
|
||||
Update: /ccpm:project:subdir:update repeat jarvis
|
||||
Remove: /ccpm:project:subdir:remove repeat jarvis
|
||||
|
||||
repeat-mobile-app
|
||||
Path: 📁 repeat-mobile-app
|
||||
Description: React Native mobile application
|
||||
Match Pattern: */repeat-mobile-app/*
|
||||
Priority: 10
|
||||
Languages: typescript, javascript
|
||||
Frameworks: frontend: react-native, expo
|
||||
|
||||
Commands:
|
||||
Update: /ccpm:project:subdir:update repeat repeat-mobile-app
|
||||
Remove: /ccpm:project:subdir:remove repeat repeat-mobile-app
|
||||
|
||||
messaging
|
||||
Path: 📁 messaging
|
||||
Description: Node.js microservice
|
||||
Match Pattern: */messaging/*
|
||||
Priority: 10
|
||||
Languages: javascript, typescript
|
||||
Frameworks: backend: nodejs
|
||||
|
||||
Commands:
|
||||
Update: /ccpm:project:subdir:update repeat messaging
|
||||
Remove: /ccpm:project:subdir:remove repeat messaging
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🚀 Quick Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Add new subdirectory:
|
||||
/ccpm:project:subdir:add repeat
|
||||
|
||||
View complete project details:
|
||||
/ccpm:project:show repeat
|
||||
|
||||
Test detection:
|
||||
cd /Users/duongdev/repeat/<subproject-path>
|
||||
/ccpm:project:list
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Active subproject (if this project is active) is marked with ⭐
|
||||
- Shows both detection configuration and metadata
|
||||
- Warns if detection is not configured for a subproject
|
||||
- Provides commands to update or remove each subdirectory
|
||||
- Use `/ccpm:project:show` for complete project view
|
||||
|
||||
## Agent Integration
|
||||
|
||||
Uses these agents:
|
||||
- **project-config-loader**: Loads project configuration
|
||||
- **project-context-manager**: Checks active subproject
|
||||
- **project-operations skill**: Provides display guidance
|
||||
- **project-detection skill**: Provides detection context
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/ccpm:project:subdir:add` - Add new subdirectory
|
||||
- `/ccpm:project:subdir:update` - Update subdirectory details
|
||||
- `/ccpm:project:subdir:remove` - Remove subdirectory
|
||||
- `/ccpm:project:show` - View complete project configuration
|
||||
- `/ccpm:project:list` - List all projects with active detection
|
||||
103
commands/project:subdir:remove.md
Normal file
103
commands/project:subdir:remove.md
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
description: Remove a subdirectory/subproject from a project
|
||||
argument-hint: <project-id> <subproject-name>
|
||||
---
|
||||
|
||||
# Remove Project Subdirectory
|
||||
|
||||
Remove subdirectory configuration from a monorepo project.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
- **$2** - Subproject name (required)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/ccpm:project:subdir:remove repeat jarvis
|
||||
/ccpm:project:subdir:remove my-monorepo old-service
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
```javascript
|
||||
const projectId = "$1"
|
||||
const subprojectName = "$2"
|
||||
|
||||
// Load project
|
||||
const projectConfig = Task(project-config-loader): `
|
||||
Load configuration for project: ${projectId}
|
||||
`
|
||||
|
||||
// Find subproject
|
||||
const detectionEntry = projectConfig.context?.detection?.subdirectories?.find(
|
||||
s => s.subproject === subprojectName
|
||||
)
|
||||
const metadataEntry = projectConfig.code_repository?.subprojects?.find(
|
||||
s => s.name === subprojectName
|
||||
)
|
||||
|
||||
if (!detectionEntry && !metadataEntry) {
|
||||
console.error(`❌ Subproject '${subprojectName}' not found in project '${projectId}'`)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Show what will be removed
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Remove Subdirectory
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectId}
|
||||
Subproject: ${subprojectName}
|
||||
Path: ${metadataEntry?.path || "N/A"}
|
||||
Match Pattern: ${detectionEntry?.match_pattern || "N/A"}
|
||||
|
||||
This will remove all configuration for this subdirectory.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
// Confirm
|
||||
const confirmation = AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Remove this subdirectory configuration?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Yes, remove it", description: "Delete all configuration" },
|
||||
{ label: "No, keep it", description: "Cancel operation" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
if (confirmation !== "Yes, remove it") {
|
||||
console.log("❌ Cancelled")
|
||||
exit(0)
|
||||
}
|
||||
|
||||
// Remove from config (pseudocode)
|
||||
// yq eval -i "del(.projects.${projectId}.context.detection.subdirectories[] | select(.subproject == \"${subprojectName}\"))" ~/.claude/ccpm-config.yaml
|
||||
// yq eval -i "del(.projects.${projectId}.code_repository.subprojects[] | select(.name == \"${subprojectName}\"))" ~/.claude/ccpm-config.yaml
|
||||
|
||||
console.log(`
|
||||
✅ Subdirectory '${subprojectName}' removed from project '${projectId}'
|
||||
|
||||
View remaining subdirectories:
|
||||
/ccpm:project:subdir:list ${projectId}
|
||||
`)
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Removes both detection configuration and metadata
|
||||
- Requires confirmation before removing
|
||||
- Cannot be undone (backup config file first if needed)
|
||||
- Use `/ccpm:project:subdir:list` to see all subdirectories
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/ccpm:project:subdir:list` - List all subdirectories
|
||||
- `/ccpm:project:subdir:add` - Add new subdirectory
|
||||
- `/ccpm:project:subdir:update` - Update subdirectory
|
||||
174
commands/project:subdir:update.md
Normal file
174
commands/project:subdir:update.md
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
description: Update subdirectory/subproject configuration
|
||||
argument-hint: <project-id> <subproject-name> [--field <field>]
|
||||
---
|
||||
|
||||
# Update Project Subdirectory
|
||||
|
||||
Update subdirectory configuration including tech stack, description, pattern, and priority.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
- **$2** - Subproject name (required)
|
||||
- **--field** - Specific field to update (optional)
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Interactive update (all fields)
|
||||
/ccpm:project:subdir:update repeat jarvis
|
||||
|
||||
# Update specific field
|
||||
/ccpm:project:subdir:update repeat jarvis --field tech_stack
|
||||
/ccpm:project:subdir:update repeat jarvis --field description
|
||||
/ccpm:project:subdir:update repeat jarvis --field pattern
|
||||
/ccpm:project:subdir:update repeat jarvis --field priority
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
```javascript
|
||||
const projectId = "$1"
|
||||
const subprojectName = "$2"
|
||||
const specificField = getFlag("--field")
|
||||
|
||||
// Load configuration
|
||||
const projectConfig = Task(project-config-loader): `
|
||||
Load configuration for project: ${projectId}
|
||||
`
|
||||
|
||||
// Find subproject
|
||||
const detectionEntry = projectConfig.context?.detection?.subdirectories?.find(
|
||||
s => s.subproject === subprojectName
|
||||
)
|
||||
const metadataEntry = projectConfig.code_repository?.subprojects?.find(
|
||||
s => s.name === subprojectName
|
||||
)
|
||||
|
||||
if (!detectionEntry && !metadataEntry) {
|
||||
console.error(`❌ Subproject '${subprojectName}' not found`)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Display current configuration
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Update Subdirectory: ${subprojectName}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Current Configuration:
|
||||
Path: ${metadataEntry?.path || "Not set"}
|
||||
Description: ${metadataEntry?.description || "Not set"}
|
||||
Match Pattern: ${detectionEntry?.match_pattern || "Not set"}
|
||||
Priority: ${detectionEntry?.priority || "Not set"}
|
||||
Tech Stack: ${metadataEntry?.tech_stack ? "Configured" : "Not set"}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
// Interactive field selection or update specific field
|
||||
const fieldsToUpdate = specificField
|
||||
? [specificField]
|
||||
: await selectFieldsToUpdate()
|
||||
|
||||
// Update each selected field
|
||||
for (const field of fieldsToUpdate) {
|
||||
const newValue = await promptForFieldValue(field, currentValue)
|
||||
// Update in config file
|
||||
// yq eval -i ".projects.${projectId}...${field} = \"${newValue}\"" ~/.claude/ccpm-config.yaml
|
||||
}
|
||||
|
||||
console.log(`
|
||||
✅ Subdirectory '${subprojectName}' updated successfully
|
||||
|
||||
View changes:
|
||||
/ccpm:project:subdir:list ${projectId}
|
||||
/ccpm:project:show ${projectId}
|
||||
`)
|
||||
```
|
||||
|
||||
## Updatable Fields
|
||||
|
||||
### Description
|
||||
Update the human-readable description:
|
||||
```bash
|
||||
/ccpm:project:subdir:update repeat jarvis --field description
|
||||
# Prompts: "Enter description for jarvis"
|
||||
```
|
||||
|
||||
### Tech Stack
|
||||
Update languages, frameworks, databases:
|
||||
```bash
|
||||
/ccpm:project:subdir:update repeat jarvis --field tech_stack
|
||||
# Interactive prompts for:
|
||||
# - Languages (typescript, python, etc.)
|
||||
# - Frontend frameworks (react, vue, etc.)
|
||||
# - Backend frameworks (nestjs, express, etc.)
|
||||
# - Databases (postgresql, mongodb, etc.)
|
||||
```
|
||||
|
||||
### Match Pattern
|
||||
Update the glob pattern for detection:
|
||||
```bash
|
||||
/ccpm:project:subdir:update repeat jarvis --field pattern
|
||||
# Prompts: "Enter new match pattern"
|
||||
# Example: "*/jarvis/**" or "**/services/jarvis/*"
|
||||
```
|
||||
|
||||
### Priority
|
||||
Update detection priority (higher = more specific):
|
||||
```bash
|
||||
/ccpm:project:subdir:update repeat jarvis --field priority
|
||||
# Prompts: "Enter priority (0-100)"
|
||||
# Default: 10, Specific paths: 15-20
|
||||
```
|
||||
|
||||
## Example: Update Tech Stack
|
||||
|
||||
```bash
|
||||
/ccpm:project:subdir:update repeat jarvis --field tech_stack
|
||||
```
|
||||
|
||||
Interactive prompts:
|
||||
```
|
||||
Languages (comma-separated): typescript, javascript
|
||||
Frontend frameworks: react, nextjs, tailwindcss
|
||||
Backend frameworks: nestjs
|
||||
Databases: postgresql, redis
|
||||
|
||||
✅ Tech stack updated for 'jarvis'
|
||||
```
|
||||
|
||||
Result in config:
|
||||
```yaml
|
||||
subprojects:
|
||||
- name: jarvis
|
||||
path: jarvis
|
||||
tech_stack:
|
||||
languages: [typescript, javascript]
|
||||
frameworks:
|
||||
frontend: [react, nextjs, tailwindcss]
|
||||
backend: [nestjs]
|
||||
database: [postgresql, redis]
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Changes are saved to `~/.claude/ccpm-config.yaml`
|
||||
- Validates configuration after updates
|
||||
- Use `--field` to update specific field quickly
|
||||
- Without `--field`, enters interactive mode for all fields
|
||||
|
||||
## Agent Integration
|
||||
|
||||
Uses:
|
||||
- **project-config-loader**: Loads current configuration
|
||||
- **project-operations skill**: Provides update guidance
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/ccpm:project:subdir:list` - View all subdirectories
|
||||
- `/ccpm:project:subdir:add` - Add new subdirectory
|
||||
- `/ccpm:project:subdir:remove` - Remove subdirectory
|
||||
- `/ccpm:project:show` - View complete project config
|
||||
417
commands/project:update.md
Normal file
417
commands/project:update.md
Normal file
@@ -0,0 +1,417 @@
|
||||
---
|
||||
description: Update an existing project configuration
|
||||
allowed-tools: [Bash, Read, Edit, AskUserQuestion]
|
||||
argument-hint: <project-id> [--field FIELD_PATH]
|
||||
---
|
||||
|
||||
# Update Project Configuration
|
||||
|
||||
Update an existing project in `~/.claude/ccpm-config.yaml`.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
- **--field** - Specific field to update (optional, e.g., "linear.team", "external_pm.jira.project_key")
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Interactive update (all fields)
|
||||
/ccpm:project:update my-app
|
||||
|
||||
# Update specific field
|
||||
/ccpm:project:update my-app --field linear.team
|
||||
/ccpm:project:update my-app --field external_pm.jira.project_key
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate and Load Project
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
PROJECT_ID=$1
|
||||
|
||||
# Check configuration exists
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "❌ Error: No CCPM configuration found"
|
||||
echo ""
|
||||
echo "Create configuration:"
|
||||
echo " /ccpm:project:add <project-id>"
|
||||
exit(1)
|
||||
fi
|
||||
|
||||
# Check project exists
|
||||
if ! yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
|
||||
echo "❌ Error: Project '$PROJECT_ID' not found"
|
||||
echo ""
|
||||
echo "Available projects:"
|
||||
yq eval '.projects | keys | .[]' "$CONFIG_FILE"
|
||||
exit(1)
|
||||
fi
|
||||
|
||||
# Load current configuration
|
||||
CURRENT_CONFIG=$(yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" -o=json)
|
||||
```
|
||||
|
||||
### Step 2: Determine Update Mode
|
||||
|
||||
If `--field` is provided → **Targeted field update**
|
||||
Otherwise → **Interactive full update**
|
||||
|
||||
### Mode A: Targeted Field Update
|
||||
|
||||
```javascript
|
||||
const fieldPath = $2 // e.g., "linear.team"
|
||||
const currentValue = await yq(`.projects.${projectId}.${fieldPath}`, CONFIG_FILE)
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Update Field: ${fieldPath}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectId}
|
||||
Field: ${fieldPath}
|
||||
Current: ${currentValue}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
// Ask for new value
|
||||
{
|
||||
questions: [{
|
||||
question: `What should the new value be for ${fieldPath}?`,
|
||||
header: "New Value",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "Type the new value"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
// Validate new value (type-specific validation)
|
||||
const newValue = userInput
|
||||
|
||||
// Update configuration
|
||||
await yq(`-i '.projects.${projectId}.${fieldPath} = "${newValue}"'`, CONFIG_FILE)
|
||||
|
||||
console.log(`
|
||||
✅ Updated successfully!
|
||||
|
||||
Field: ${fieldPath}
|
||||
Old: ${currentValue}
|
||||
New: ${newValue}
|
||||
`)
|
||||
```
|
||||
|
||||
### Mode B: Interactive Full Update
|
||||
|
||||
Ask user which category to update:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to update?",
|
||||
header: "Update Category",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Project Info",
|
||||
description: "Name, description, owner"
|
||||
},
|
||||
{
|
||||
label: "Linear Settings",
|
||||
description: "Team, project, labels, workflow states"
|
||||
},
|
||||
{
|
||||
label: "External PM",
|
||||
description: "Jira, Confluence, Slack configuration"
|
||||
},
|
||||
{
|
||||
label: "Code Repository",
|
||||
description: "Repository type and settings"
|
||||
},
|
||||
{
|
||||
label: "Quality Gates",
|
||||
description: "SonarQube, code review settings"
|
||||
},
|
||||
{
|
||||
label: "Tech Stack",
|
||||
description: "Languages, frameworks, databases"
|
||||
},
|
||||
{
|
||||
label: "Custom Commands",
|
||||
description: "Project-specific commands"
|
||||
},
|
||||
{
|
||||
label: "All Settings",
|
||||
description: "Review and update all categories"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
#### Update: Project Info
|
||||
|
||||
```javascript
|
||||
const current = config
|
||||
|
||||
// Show current values
|
||||
console.log(`
|
||||
Current Project Info:
|
||||
Name: ${current.name}
|
||||
Description: ${current.description}
|
||||
Owner: ${current.owner}
|
||||
`)
|
||||
|
||||
// Ask for updates
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "New project name? (or keep current)",
|
||||
header: "Name",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Keep current", description: current.name },
|
||||
{ label: "Change", description: "Enter new name" }
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "New description? (or keep current)",
|
||||
header: "Description",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Keep current", description: current.description },
|
||||
{ label: "Change", description: "Enter new description" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
if (nameChanged) {
|
||||
await yq(`-i '.projects.${projectId}.name = "${newName}"'`, CONFIG_FILE)
|
||||
}
|
||||
if (descriptionChanged) {
|
||||
await yq(`-i '.projects.${projectId}.description = "${newDescription}"'`, CONFIG_FILE)
|
||||
}
|
||||
```
|
||||
|
||||
#### Update: Linear Settings
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Which Linear setting to update?",
|
||||
header: "Linear",
|
||||
multiSelect: true, // Allow multiple selections
|
||||
options: [
|
||||
{ label: "Team", description: `Current: ${config.linear.team}` },
|
||||
{ label: "Project", description: `Current: ${config.linear.project}` },
|
||||
{ label: "Labels", description: `Current: ${config.linear.default_labels.join(", ")}` },
|
||||
{ label: "Workflow States", description: "Backlog, Planning, etc." }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// For each selected setting, prompt for new value
|
||||
// Then update configuration
|
||||
```
|
||||
|
||||
#### Update: External PM
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Update external PM integration?",
|
||||
header: "External PM",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enable/Disable",
|
||||
description: `Currently: ${config.external_pm.enabled ? "Enabled" : "Disabled"}`
|
||||
},
|
||||
{
|
||||
label: "Jira Settings",
|
||||
description: "Base URL, project key"
|
||||
},
|
||||
{
|
||||
label: "Confluence Settings",
|
||||
description: "Base URL, space key"
|
||||
},
|
||||
{
|
||||
label: "Slack Settings",
|
||||
description: "Workspace, channels"
|
||||
},
|
||||
{
|
||||
label: "Disable All External PM",
|
||||
description: "Switch to Linear-only mode"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
#### Update: Code Repository
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Update repository settings?",
|
||||
header: "Repository",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Change Type",
|
||||
description: `Currently: ${config.code_repository.type}`
|
||||
},
|
||||
{
|
||||
label: "Update GitHub Settings",
|
||||
description: "Owner, repo name"
|
||||
},
|
||||
{
|
||||
label: "Update BitBucket Settings",
|
||||
description: "Workspace, repo slug"
|
||||
},
|
||||
{
|
||||
label: "Update Repository URL",
|
||||
description: "Change repository URL"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Show Changes Summary
|
||||
|
||||
After collecting all updates:
|
||||
|
||||
```javascript
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Changes Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectId}
|
||||
|
||||
${changes.map(change => `
|
||||
${change.field}
|
||||
Old: ${change.oldValue}
|
||||
New: ${change.newValue}
|
||||
`).join("\n")}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 4: Confirm and Apply
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Apply these changes?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, apply",
|
||||
description: "Save changes to configuration"
|
||||
},
|
||||
{
|
||||
label: "Review again",
|
||||
description: "Go back and modify"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Discard all changes"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If confirmed:
|
||||
|
||||
```bash
|
||||
# Apply all changes using yq
|
||||
for change in "${CHANGES[@]}"; do
|
||||
yq eval -i ".projects.$PROJECT_ID.${change.field} = ${change.value}" "$CONFIG_FILE"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ Project configuration updated!"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Next Steps"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "View updated config: /ccpm:project:show $PROJECT_ID"
|
||||
echo "List all projects: /ccpm:project:list"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Update Linear team
|
||||
|
||||
```bash
|
||||
/ccpm:project:update my-app --field linear.team
|
||||
|
||||
# Prompts:
|
||||
# Current: Work
|
||||
# New value: Personal
|
||||
|
||||
# Result:
|
||||
# ✅ Updated linear.team from "Work" to "Personal"
|
||||
```
|
||||
|
||||
### Example 2: Interactive update
|
||||
|
||||
```bash
|
||||
/ccpm:project:update my-app
|
||||
|
||||
# Shows menu:
|
||||
# 1. Project Info
|
||||
# 2. Linear Settings
|
||||
# 3. External PM
|
||||
# ...
|
||||
|
||||
# User selects "Linear Settings"
|
||||
# Shows checkboxes for Team, Project, Labels, Workflow States
|
||||
# Updates selected fields
|
||||
```
|
||||
|
||||
### Example 3: Enable Jira integration
|
||||
|
||||
```bash
|
||||
/ccpm:project:update my-side-project
|
||||
|
||||
# Select: External PM
|
||||
# Choose: Enable Jira
|
||||
# Enter: Base URL, project key
|
||||
# Result: external_pm.enabled = true, jira configured
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
The command validates:
|
||||
- Project ID exists
|
||||
- Field paths are valid
|
||||
- Values match expected types
|
||||
- Required fields are not left empty
|
||||
- URLs are properly formatted
|
||||
|
||||
## Notes
|
||||
|
||||
- All changes are applied atomically
|
||||
- Invalid changes are rejected before saving
|
||||
- Can cancel at any point before confirmation
|
||||
- Use `/ccpm:project:show` to verify changes
|
||||
- Configuration file: `~/.claude/ccpm-config.yaml`
|
||||
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
|
||||
543
commands/spec:create.md
Normal file
543
commands/spec:create.md
Normal file
@@ -0,0 +1,543 @@
|
||||
---
|
||||
description: Create Epic/Feature/Initiative with Linear Document
|
||||
allowed-tools: [LinearMCP, Read, AskUserQuestion]
|
||||
argument-hint: <type> "<title>" [parent-id]
|
||||
---
|
||||
|
||||
# Create Spec: $2
|
||||
|
||||
## 🚨 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 Utilities
|
||||
|
||||
**READ**: commands/_shared-linear-helpers.md
|
||||
|
||||
This provides helper functions for Linear integration:
|
||||
- `ensureLabelsExist()` - Ensures labels exist, creates if missing
|
||||
- `getValidStateId()` - Validates and resolves state IDs
|
||||
- `getOrCreateLabel()` - Get or create single label
|
||||
- `getDefaultColor()` - Standard CCPM colors
|
||||
|
||||
---
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Type: `epic`, `feature`, or `initiative`
|
||||
- **$2** - Title: The name of the epic/feature/initiative
|
||||
- **$3** - (Optional) Parent ID: For feature (parent epic ID) or task (parent feature ID)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate Type and Hierarchy
|
||||
|
||||
**Type Mapping:**
|
||||
```javascript
|
||||
const typeMapping = {
|
||||
'epic': {
|
||||
linearType: 'initiative', // Epics are Initiatives in Linear
|
||||
labelPrefix: 'epic',
|
||||
docTemplate: 'epic-spec',
|
||||
canHaveParent: false
|
||||
},
|
||||
'feature': {
|
||||
linearType: 'issue', // Features are Parent Issues
|
||||
labelPrefix: 'feature',
|
||||
docTemplate: 'feature-design',
|
||||
canHaveParent: true, // Can belong to Epic
|
||||
requiresProject: true
|
||||
},
|
||||
'initiative': {
|
||||
linearType: 'initiative', // Same as epic (alias)
|
||||
labelPrefix: 'initiative',
|
||||
docTemplate: 'epic-spec',
|
||||
canHaveParent: false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- If type is `feature` and no parent provided → Ask user to select parent epic
|
||||
- If type is `epic` or `initiative` → No parent needed
|
||||
|
||||
### Step 2: Load Project Configuration
|
||||
|
||||
**IMPORTANT**: Uses dynamic project configuration from `~/.claude/ccpm-config.yaml`.
|
||||
|
||||
```bash
|
||||
# Try to use active project or auto-detect
|
||||
PROJECT_ARG="" # Will be auto-detected or prompted
|
||||
```
|
||||
|
||||
**LOAD PROJECT CONFIG**: Follow instructions in `commands/_shared-project-config-loader.md`
|
||||
|
||||
This will:
|
||||
1. Try to use active project from config
|
||||
2. Try auto-detection (git remote, directory patterns)
|
||||
3. If neither works, list available projects and prompt user
|
||||
|
||||
After loading, you'll have:
|
||||
- `${PROJECT_ID}` - Selected project
|
||||
- `${LINEAR_TEAM}`, `${LINEAR_PROJECT}` - For creating Linear entities
|
||||
- All other project settings
|
||||
|
||||
### Step 3: Create Linear Entity
|
||||
|
||||
#### For Epic/Initiative:
|
||||
|
||||
**Step 1: Ensure labels exist**
|
||||
|
||||
```javascript
|
||||
// Ensure required labels exist before creating entity
|
||||
const labelNames = await ensureLabelsExist(
|
||||
projectMapping[project].team,
|
||||
["epic", "spec:draft"],
|
||||
{
|
||||
descriptions: {
|
||||
"epic": "CCPM: Epic-level work item",
|
||||
"spec:draft": "CCPM: Specification in draft status"
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**Step 2: Validate state (optional)**
|
||||
|
||||
If you need to set a specific initial state:
|
||||
|
||||
```javascript
|
||||
// Optional: Validate state if setting non-default state
|
||||
// const stateId = await getValidStateId(projectMapping[project].team, "planned");
|
||||
```
|
||||
|
||||
**Step 3: Create initiative**
|
||||
|
||||
Use **Linear MCP** `create_project` or initiative creation:
|
||||
|
||||
```javascript
|
||||
{
|
||||
name: $2,
|
||||
team: projectMapping[project].team,
|
||||
description: "Spec document: [DOC-XXX](link) (will be added after doc creation)",
|
||||
// status: "planned", // Optional: Use default state or validated stateId
|
||||
labels: labelNames // Use validated label names
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: State is optional for initiatives. If you need a specific state, use `getValidStateId()` to validate it first.
|
||||
|
||||
#### For Feature (Parent Issue):
|
||||
|
||||
**Step 1: Ensure labels exist**
|
||||
|
||||
```javascript
|
||||
// Ensure required labels exist before creating issue
|
||||
const labelNames = await ensureLabelsExist(
|
||||
projectMapping[project].team,
|
||||
["feature", "spec:draft"],
|
||||
{
|
||||
descriptions: {
|
||||
"feature": "CCPM: Feature-level work item",
|
||||
"spec:draft": "CCPM: Specification in draft status"
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**Step 2: Create issue**
|
||||
|
||||
Use **Linear MCP** `create_issue`:
|
||||
|
||||
```javascript
|
||||
{
|
||||
title: $2,
|
||||
team: projectMapping[project].team,
|
||||
project: projectMapping[project].project,
|
||||
description: "Design doc: [DOC-XXX](link) (will be added after doc creation)",
|
||||
labels: labelNames, // Use validated label names
|
||||
parent: $3, // if provided (epic/initiative ID)
|
||||
priority: 0 // No priority unless specified
|
||||
}
|
||||
```
|
||||
|
||||
**Save the created ID** (e.g., `WORK-123` for feature, initiative ID for epic)
|
||||
|
||||
### Step 4: Create Linear Document
|
||||
|
||||
Use **Linear MCP** `create_document`:
|
||||
|
||||
**Document Title Format:**
|
||||
- Epic: `Epic Spec: $2`
|
||||
- Feature: `Feature Design: $2`
|
||||
|
||||
**Document Content:**
|
||||
|
||||
Use template based on type (see templates below).
|
||||
|
||||
**Initial Content:**
|
||||
- Epic → Use Epic Spec Template
|
||||
- Feature → Use Feature Design Template
|
||||
|
||||
**Save Document ID** (e.g., `DOC-456`)
|
||||
|
||||
### Step 5: Link Document to Linear Entity
|
||||
|
||||
Update the created Linear issue/initiative description with link to document:
|
||||
|
||||
```markdown
|
||||
## 📄 Specification
|
||||
|
||||
**Spec Document**: [Epic Spec: $2](https://linear.app/workspace/document/DOC-456) ← Full specification
|
||||
|
||||
---
|
||||
|
||||
[Rest of description...]
|
||||
```
|
||||
|
||||
### Step 6: Display Summary
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Epic/Feature Created!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Type: [Epic/Feature]
|
||||
🎯 Title: [$2]
|
||||
🔗 Linear: [WORK-123](https://linear.app/workspace/issue/WORK-123)
|
||||
📄 Spec Doc: [DOC-456](https://linear.app/workspace/document/DOC-456)
|
||||
🏷️ Labels: spec:draft, [epic/feature]
|
||||
📁 Project: [project name]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 7: Interactive Next Actions
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Spec created! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Write Spec Content",
|
||||
description: "Start writing the spec document with AI assistance (/ccpm:spec:write)"
|
||||
},
|
||||
{
|
||||
label: "View in Linear",
|
||||
description: "Open Linear to see the created epic/feature"
|
||||
},
|
||||
{
|
||||
label: "Create Another",
|
||||
description: "Create another epic/feature"
|
||||
},
|
||||
{
|
||||
label: "Done for Now",
|
||||
description: "I'll work on the spec later"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute based on choice:**
|
||||
- Write Spec Content → Run `/ccpm:spec:write [doc-id] requirements`
|
||||
- View in Linear → Show URL and exit
|
||||
- Create Another → Ask for details and repeat
|
||||
- Done → Show quick commands and exit
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Label Creation Failures
|
||||
|
||||
If label creation fails, show helpful error message:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const labelNames = await ensureLabelsExist(teamId, ["epic", "spec:draft"], {...});
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to create/verify labels:", error.message);
|
||||
throw new Error(
|
||||
`Unable to create spec labels. This may indicate:\n` +
|
||||
` - Insufficient Linear permissions\n` +
|
||||
` - Network connectivity issues\n` +
|
||||
` - Invalid team ID\n\n` +
|
||||
`Original error: ${error.message}`
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### State Validation Failures
|
||||
|
||||
If state validation is used (optional for this command):
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const stateId = await getValidStateId(teamId, "planned");
|
||||
} catch (error) {
|
||||
// Error already includes helpful message with available states
|
||||
console.error("❌ Invalid state:", error.message);
|
||||
throw error; // Re-throw to halt command
|
||||
}
|
||||
```
|
||||
|
||||
### Recovery Strategy
|
||||
|
||||
If label/state operations fail:
|
||||
1. Display clear error message with context
|
||||
2. Show what was attempted (label names, state name)
|
||||
3. Suggest fixes (check permissions, verify team ID)
|
||||
4. DO NOT proceed with entity creation if validation fails
|
||||
|
||||
---
|
||||
|
||||
## Templates
|
||||
|
||||
### Epic Spec Template
|
||||
|
||||
```markdown
|
||||
# Epic: [$2]
|
||||
|
||||
**Status**: 🟡 Draft
|
||||
**Owner**: [Auto-detect from Linear user]
|
||||
**Created**: [Current date]
|
||||
**Last Updated**: [Current date]
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vision & Goals
|
||||
|
||||
### Problem Statement
|
||||
What problem are we solving? Who has this problem?
|
||||
|
||||
[AI: Analyze the epic title and suggest problem statement based on common patterns]
|
||||
|
||||
### Success Metrics
|
||||
How will we measure success?
|
||||
- Metric 1: [Target]
|
||||
- Metric 2: [Target]
|
||||
|
||||
### Out of Scope
|
||||
What are we explicitly NOT doing?
|
||||
|
||||
---
|
||||
|
||||
## 🔍 User Research
|
||||
|
||||
### User Personas
|
||||
- **Persona 1**: [Description]
|
||||
|
||||
### User Stories
|
||||
- As a [role], I want [feature] so that [benefit]
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ High-Level Architecture
|
||||
|
||||
### System Components
|
||||
- Component 1: [Purpose]
|
||||
|
||||
### Integration Points
|
||||
- External System 1: [How we integrate]
|
||||
|
||||
### Technology Choices
|
||||
- Frontend: [Tech + Rationale]
|
||||
- Backend: [Tech + Rationale]
|
||||
- Database: [Tech + Rationale]
|
||||
|
||||
---
|
||||
|
||||
## 📊 Features Breakdown
|
||||
|
||||
| Feature | Priority | Complexity | Est. Timeline |
|
||||
|---------|----------|------------|---------------|
|
||||
| Feature 1 | P0 | High | 2 weeks |
|
||||
|
||||
**💡 Tip**: Use `/ccpm:spec:break-down [epic-id]` to auto-generate features from this spec.
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security & Compliance
|
||||
|
||||
### Security Considerations
|
||||
- Authentication: [Approach]
|
||||
- Authorization: [Approach]
|
||||
- Data Protection: [Approach]
|
||||
|
||||
---
|
||||
|
||||
## 📅 Timeline & Milestones
|
||||
|
||||
| Milestone | Date | Status |
|
||||
|-----------|------|--------|
|
||||
| Spec Complete | [Date] | ⏳ |
|
||||
| Feature 1 Complete | [Date] | 📅 |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 References
|
||||
|
||||
**Linear Epic**: [WORK-XXX](https://linear.app/workspace/issue/WORK-XXX)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Change Log
|
||||
|
||||
| Date | Change | Author |
|
||||
|------|--------|--------|
|
||||
| [Date] | Initial draft | [Name] |
|
||||
```
|
||||
|
||||
### Feature Design Template
|
||||
|
||||
```markdown
|
||||
# Feature: [$2]
|
||||
|
||||
**Status**: 🟡 Draft
|
||||
**Epic**: [Link to parent Epic if exists]
|
||||
**Owner**: [Auto-detect from Linear user]
|
||||
**Created**: [Current date]
|
||||
|
||||
---
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
### Functional Requirements
|
||||
- FR1: System shall...
|
||||
- FR2: System shall...
|
||||
|
||||
### Non-Functional Requirements
|
||||
- NFR1: Performance...
|
||||
- NFR2: Scalability...
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Criterion 1
|
||||
- [ ] Criterion 2
|
||||
|
||||
---
|
||||
|
||||
## 🎨 User Experience
|
||||
|
||||
### User Flows
|
||||
1. Flow 1: [Step by step]
|
||||
|
||||
### Wireframes / Designs
|
||||
[Link to Figma/designs if available]
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Technical Design
|
||||
|
||||
### Architecture
|
||||
[Diagram or description]
|
||||
|
||||
### API Design
|
||||
```
|
||||
POST /api/endpoint
|
||||
Request: {...}
|
||||
Response: {...}
|
||||
```
|
||||
|
||||
### Data Model
|
||||
```typescript
|
||||
interface Model {
|
||||
field1: type
|
||||
field2: type
|
||||
}
|
||||
```
|
||||
|
||||
### Component Structure (Frontend)
|
||||
```
|
||||
- ComponentName/
|
||||
- index.tsx
|
||||
- types.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Test case 1
|
||||
|
||||
### Integration Tests
|
||||
- Test scenario 1
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Implementation Plan
|
||||
|
||||
### Task Breakdown
|
||||
- [ ] **Task 1**: Description (Est: 2h)
|
||||
- [ ] **Task 2**: Description (Est: 4h)
|
||||
|
||||
**Total Estimate**: 6 hours (~1 day)
|
||||
|
||||
**💡 Tip**: Use `/ccpm:spec:break-down [feature-id]` to create Linear tasks from this plan.
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security Considerations
|
||||
|
||||
- Input validation: [Approach]
|
||||
- Authentication: [Required?]
|
||||
|
||||
---
|
||||
|
||||
## 📊 Risks & Mitigations
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|-------------|--------|------------|
|
||||
| Risk 1 | Medium | High | [Strategy] |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 References
|
||||
|
||||
**Linear Feature**: [WORK-XXX](https://linear.app/workspace/issue/WORK-XXX)
|
||||
**Parent Epic Spec**: [Link if exists]
|
||||
|
||||
---
|
||||
|
||||
## 📝 Decision Log
|
||||
|
||||
| Date | Decision | Rationale |
|
||||
|------|----------|-----------|
|
||||
| [Date] | Initial design | [Why] |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Commands Footer
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Write Spec: /ccpm:spec:write [doc-id] [section]
|
||||
Review Spec: /ccpm:spec:review [doc-id]
|
||||
Break Down: /ccpm:spec:break-down [issue-id]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Epic/Initiative are interchangeable (same Linear entity type)
|
||||
- Features are Parent Issues that can have Tasks as sub-issues
|
||||
- All specs start with `spec:draft` label
|
||||
- Use `/ccpm:spec:write` to populate sections with AI assistance
|
||||
- Use `/ccpm:spec:review` to validate completeness before approval
|
||||
808
commands/spec:migrate.md
Normal file
808
commands/spec:migrate.md
Normal file
@@ -0,0 +1,808 @@
|
||||
---
|
||||
description: Migrate existing markdown specs from .claude/ to Linear Documents
|
||||
allowed-tools: [LinearMCP, Read, Glob, Bash, AskUserQuestion]
|
||||
argument-hint: <project-path> [category]
|
||||
---
|
||||
|
||||
# Migrate Specs to Linear: $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.
|
||||
|
||||
---
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project path:
|
||||
- `.` - Use current directory
|
||||
- Absolute path (e.g., `~/projects/my-app`)
|
||||
- Relative path (e.g., `../other-project`)
|
||||
- **$2** - (Optional) Category to migrate: `docs`, `plans`, `enhancements`, `tasks`, `all`
|
||||
|
||||
Default category: `all`
|
||||
|
||||
**Note**: Using `.` will scan current working directory for `.claude/` folder.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Resolve Project Path
|
||||
|
||||
```javascript
|
||||
// Resolve project path
|
||||
let projectPath = $1
|
||||
|
||||
if (projectPath === '.') {
|
||||
// Use current working directory
|
||||
projectPath = process.cwd()
|
||||
}
|
||||
|
||||
// Resolve to absolute path
|
||||
projectPath = path.resolve(projectPath)
|
||||
|
||||
// Check if .claude/ exists
|
||||
const claudePath = path.join(projectPath, '.claude')
|
||||
if (!fs.existsSync(claudePath)) {
|
||||
// Error: No .claude/ directory found
|
||||
// Suggest: Check path or create .claude/ first
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Discover Existing Specs
|
||||
|
||||
Scan project `.claude/` directory for markdown files:
|
||||
|
||||
```javascript
|
||||
const categories = {
|
||||
docs: '.claude/docs/',
|
||||
plans: '.claude/plans/',
|
||||
enhancements: '.claude/enhancements/',
|
||||
tasks: '.claude/tasks/',
|
||||
research: '.claude/research/',
|
||||
analysis: '.claude/analysis/',
|
||||
qa: '.claude/qa/',
|
||||
security: '.claude/security/'
|
||||
}
|
||||
|
||||
// Use Glob to find all .md files in each category
|
||||
const files = {}
|
||||
|
||||
for (const [category, path] of Object.entries(categories)) {
|
||||
const pattern = `${projectPath}/${path}**/*.md`
|
||||
files[category] = await glob(pattern)
|
||||
}
|
||||
```
|
||||
|
||||
**Filter by category** if `$2` provided.
|
||||
|
||||
### Step 2: Categorize Files by Type
|
||||
|
||||
**Analyze each file to determine type:**
|
||||
|
||||
```javascript
|
||||
function categorizeFile(filePath, content) {
|
||||
// Check filename patterns
|
||||
const fileName = path.basename(filePath)
|
||||
|
||||
// Plans with checklists → Features
|
||||
if (fileName.includes('plan') && hasChecklist(content)) {
|
||||
return { type: 'feature', confidence: 'high' }
|
||||
}
|
||||
|
||||
// Enhancements → Features
|
||||
if (filePath.includes('/enhancements/')) {
|
||||
return { type: 'feature', confidence: 'high' }
|
||||
}
|
||||
|
||||
// Tasks → Tasks
|
||||
if (filePath.includes('/tasks/') && hasImplementationDetails(content)) {
|
||||
return { type: 'task', confidence: 'high' }
|
||||
}
|
||||
|
||||
// Docs/guides → Documentation (not migrated, or linked)
|
||||
if (filePath.includes('/docs/') || fileName.includes('guide')) {
|
||||
return { type: 'documentation', confidence: 'high' }
|
||||
}
|
||||
|
||||
// Research → Link as reference
|
||||
if (filePath.includes('/research/')) {
|
||||
return { type: 'reference', confidence: 'high' }
|
||||
}
|
||||
|
||||
// Large multi-section files → Epics
|
||||
if (hasSections(content) > 5 && hasFeatureBreakdown(content)) {
|
||||
return { type: 'epic', confidence: 'medium' }
|
||||
}
|
||||
|
||||
// Default: Feature
|
||||
return { type: 'feature', confidence: 'low' }
|
||||
}
|
||||
|
||||
function hasChecklist(content) {
|
||||
return /- \[ \]/.test(content)
|
||||
}
|
||||
|
||||
function hasImplementationDetails(content) {
|
||||
return content.includes('## What Was Implemented') ||
|
||||
content.includes('## Implementation') ||
|
||||
content.includes('Status:** ✅')
|
||||
}
|
||||
|
||||
function hasFeatureBreakdown(content) {
|
||||
return /## Features/i.test(content) ||
|
||||
/## Phases/i.test(content)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Ask What to Migrate
|
||||
|
||||
**FIRST: Let user select which categories to migrate.**
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Which categories would you like to migrate?",
|
||||
header: "Select Categories",
|
||||
multiSelect: true, // Allow multiple selections
|
||||
options: [
|
||||
{
|
||||
label: "Epics (2 files)",
|
||||
description: `${epics.length} epic specs found in plans/`
|
||||
},
|
||||
{
|
||||
label: "Features (12 files)",
|
||||
description: `${features.length} features found in enhancements/`
|
||||
},
|
||||
{
|
||||
label: "Tasks (25 files)",
|
||||
description: `${tasks.length} tasks found in tasks/`
|
||||
},
|
||||
{
|
||||
label: "Documentation (6 files)",
|
||||
description: `${docs.length} docs/guides found in docs/`
|
||||
},
|
||||
{
|
||||
label: "Research (8 files)",
|
||||
description: `${research.length} research docs found in research/`
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**User can select**:
|
||||
- Single category (e.g., only "Features")
|
||||
- Multiple categories (e.g., "Epics" + "Features")
|
||||
- All categories (select all)
|
||||
|
||||
**After selection**, filter files to only selected categories.
|
||||
|
||||
### Step 4: Show Migration Preview for Selected Categories
|
||||
|
||||
Display categorized files to user:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📦 Migration Preview: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📂 Category: ${$2 || 'all'}
|
||||
📁 Project Path: $1/.claude/
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Discovered Files
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📚 EPICS (Will create Initiatives + Spec Docs):
|
||||
1. production-deployment-plan.md → Epic: "Production Deployment Plan"
|
||||
2. [...]
|
||||
|
||||
🎨 FEATURES (Will create Features + Design Docs):
|
||||
1. 20251031-posthog-observability-implementation.md → Feature: "PostHog Observability"
|
||||
2. 20251024-120100-search-and-filter-system.md → Feature: "Search & Filter System"
|
||||
3. [...]
|
||||
|
||||
✅ TASKS (Will create Tasks):
|
||||
1. 20251030-130330-implement-task-comments-phase2.md → Task: "Task Comments Phase 2"
|
||||
2. 20251107-095033-fix-posthog-provider-crash.md → Task: "Fix PostHog Provider Crash"
|
||||
3. [...]
|
||||
|
||||
📖 DOCUMENTATION (Will link as references):
|
||||
1. feature-flags-guide.md
|
||||
2. ota-updates-guide.md
|
||||
3. [...]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📈 Migration Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Total Files: 45
|
||||
- Epics: 2
|
||||
- Features: 12
|
||||
- Tasks: 25
|
||||
- Documentation: 6
|
||||
|
||||
Estimated Time: ~15 minutes
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Migration Rules
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ Original .md files will NOT be deleted (safe migration)
|
||||
✅ Files will be moved to .claude/migrated/ after successful migration
|
||||
✅ Linear issues will include link to original file
|
||||
✅ You can review and edit in Linear after migration
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Show Detailed Preview (MANDATORY)
|
||||
|
||||
**CRITICAL: ALWAYS show full preview before ANY migration.**
|
||||
|
||||
For EACH file, display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📄 File #1: production-deployment-plan.md
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📁 Original Path: .claude/plans/production-deployment-plan.md
|
||||
📊 Type: Epic (detected)
|
||||
📝 Size: 57KB
|
||||
|
||||
Will Create in Linear:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. Epic/Initiative:
|
||||
- Title: "Production Deployment Plan"
|
||||
- Team: Personal
|
||||
- Project: Personal Project
|
||||
- Labels: ["epic", "migrated", "spec:draft"]
|
||||
- Status: Planned
|
||||
|
||||
2. Linear Document:
|
||||
- Title: "Epic Spec: Production Deployment Plan"
|
||||
- Content: Full markdown (57KB)
|
||||
- Linked to Epic above
|
||||
|
||||
3. Extracted Metadata:
|
||||
- Created: 2025-11-01
|
||||
- Status: Planning
|
||||
- Version: v1.0.0
|
||||
|
||||
4. Post-Migration:
|
||||
- Original file moved to: .claude/migrated/plans/
|
||||
- Breadcrumb created: .claude/plans/production-deployment-plan.md.migrated.txt
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📄 File #2: 20251031-posthog-observability-implementation.md
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📁 Original Path: .claude/enhancements/20251031-posthog-observability-implementation.md
|
||||
📊 Type: Feature (detected)
|
||||
📝 Size: 35KB
|
||||
📋 Checklist Found: 3 subtasks detected
|
||||
|
||||
Will Create in Linear:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. Feature (Parent Issue):
|
||||
- Title: "PostHog Observability Implementation"
|
||||
- Team: Personal
|
||||
- Project: Personal Project
|
||||
- Labels: ["feature", "migrated", "spec:draft"]
|
||||
- Priority: High (detected)
|
||||
|
||||
2. Linear Document:
|
||||
- Title: "Feature Design: PostHog Observability Implementation"
|
||||
- Content: Full markdown (35KB)
|
||||
- Linked to Feature above
|
||||
|
||||
3. Sub-Tasks (from checklist):
|
||||
- Task 1: "Setup PostHog Integration" (Est: 2h)
|
||||
- Task 2: "Configure Event Tracking" (Est: 4h)
|
||||
- Task 3: "Add Custom Properties" (Est: 3h)
|
||||
|
||||
4. Post-Migration:
|
||||
- Original file moved to: .claude/migrated/enhancements/
|
||||
- Breadcrumb created with Linear links
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
[... show ALL files ...]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 5: Confirm Migration (REQUIRED)
|
||||
|
||||
**NEVER migrate without explicit confirmation.**
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "⚠️ REVIEW COMPLETE. Proceed with creating these items in Linear?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "✅ Yes, Migrate All",
|
||||
description: "Create all items in Linear as shown above"
|
||||
},
|
||||
{
|
||||
label: "🔍 Select Specific Files",
|
||||
description: "I want to choose which files to migrate"
|
||||
},
|
||||
{
|
||||
label: "❌ Cancel",
|
||||
description: "Don't migrate anything"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**If "Select Specific Files":**
|
||||
|
||||
- Show numbered list of all files
|
||||
- Ask user to specify indices (e.g., "1,3,5-8,12")
|
||||
- Show preview AGAIN for selected files only
|
||||
- Ask confirmation AGAIN before migrating
|
||||
|
||||
### Step 5: Migrate Each File
|
||||
|
||||
For each file:
|
||||
|
||||
#### Step 5.1: Read and Parse File
|
||||
|
||||
```javascript
|
||||
const content = await readFile(filePath)
|
||||
|
||||
const metadata = extractMetadata(content)
|
||||
// Extracts:
|
||||
// - Title (from # heading or filename)
|
||||
// - Created date (from file or "Created:" field)
|
||||
// - Status (from "Status:" field)
|
||||
// - Related docs (from "Related:" or links)
|
||||
```
|
||||
|
||||
#### Step 5.2: Transform Content for Linear
|
||||
|
||||
**Keep most content as-is, but:**
|
||||
|
||||
1. **Remove file-specific headers:**
|
||||
```markdown
|
||||
# Task Comments Phase 2: Photo Attachments Implementation
|
||||
|
||||
**Date:** 2025-10-30 - 2025-10-31
|
||||
**Status:** ✅ COMPLETED
|
||||
**Related Task:** `.claude/tasks/20251030-130330-implement-task-comments.md`
|
||||
```
|
||||
|
||||
Becomes Linear issue fields:
|
||||
- Title: "Task Comments Phase 2: Photo Attachments"
|
||||
- Status: "Done"
|
||||
- Description: (rest of content)
|
||||
|
||||
2. **Convert local file links to references:**
|
||||
```markdown
|
||||
**Related Task:** `.claude/tasks/20251030-130330-implement-task-comments.md`
|
||||
```
|
||||
|
||||
Becomes:
|
||||
```markdown
|
||||
**Related Task:** [Will be migrated as WORK-XXX] or [Original file: `.claude/tasks/...`]
|
||||
```
|
||||
|
||||
3. **Preserve all other content:**
|
||||
- Code blocks → Keep as-is
|
||||
- Tables → Keep as-is
|
||||
- Images → Keep as-is (if hosted)
|
||||
- Checklists → Keep as-is
|
||||
- Headers, formatting → Keep as-is
|
||||
|
||||
#### Step 5.3: Create Linear Entities
|
||||
|
||||
**For Epic:**
|
||||
|
||||
```javascript
|
||||
// 1. Create Initiative in Linear
|
||||
const epic = await createLinearInitiative({
|
||||
name: metadata.title,
|
||||
description: `Original file: \`${relativePath}\`\n\nMigrated from: ${filePath}`,
|
||||
team: detectTeam(projectPath),
|
||||
targetDate: metadata.targetDate
|
||||
})
|
||||
|
||||
// 2. Create Linear Document for Epic Spec
|
||||
const doc = await createLinearDocument({
|
||||
title: `Epic Spec: ${metadata.title}`,
|
||||
content: transformedContent, // Full markdown content
|
||||
projectId: epic.id
|
||||
})
|
||||
|
||||
// 3. Update Initiative description to link to spec doc
|
||||
await updateLinearInitiative(epic.id, {
|
||||
description: `
|
||||
## 📄 Specification
|
||||
|
||||
**Spec Document**: [Epic Spec: ${metadata.title}](${doc.url})
|
||||
|
||||
**Original File**: \`${relativePath}\`
|
||||
**Migrated**: ${new Date().toISOString().split('T')[0]}
|
||||
|
||||
---
|
||||
|
||||
${epic.description}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
**For Feature:**
|
||||
|
||||
```javascript
|
||||
// 1. Create Feature (Parent Issue)
|
||||
const feature = await createLinearIssue({
|
||||
title: metadata.title,
|
||||
team: detectTeam(projectPath),
|
||||
project: detectProject(projectPath),
|
||||
labels: ['feature', 'migrated', metadata.status ? `status:${metadata.status}` : 'spec:draft'],
|
||||
priority: metadata.priority || 0,
|
||||
description: `Original file: \`${relativePath}\`\n\nMigrated from: ${filePath}`
|
||||
})
|
||||
|
||||
// 2. Create Linear Document for Feature Design
|
||||
const doc = await createLinearDocument({
|
||||
title: `Feature Design: ${metadata.title}`,
|
||||
content: transformedContent,
|
||||
projectId: feature.projectId
|
||||
})
|
||||
|
||||
// 3. Link doc to feature
|
||||
await updateLinearIssue(feature.id, {
|
||||
description: `
|
||||
## 📄 Specification
|
||||
|
||||
**Design Doc**: [Feature Design: ${metadata.title}](${doc.url})
|
||||
|
||||
**Original File**: \`${relativePath}\`
|
||||
**Migrated**: ${new Date().toISOString().split('T')[0]}
|
||||
**Original Status**: ${metadata.status || 'N/A'}
|
||||
|
||||
---
|
||||
|
||||
${feature.description}
|
||||
`
|
||||
})
|
||||
|
||||
// 4. If file has checklist, extract and add as sub-tasks
|
||||
if (hasChecklist(content)) {
|
||||
const tasks = extractChecklist(content)
|
||||
for (const task of tasks) {
|
||||
await createLinearIssue({
|
||||
title: task.title,
|
||||
team: feature.team,
|
||||
project: feature.project,
|
||||
parent: feature.id, // Sub-issue
|
||||
labels: ['task', 'migrated'],
|
||||
description: task.description || ''
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**For Task:**
|
||||
|
||||
```javascript
|
||||
// Create Task (regular issue, may or may not be sub-issue)
|
||||
const task = await createLinearIssue({
|
||||
title: metadata.title,
|
||||
team: detectTeam(projectPath),
|
||||
project: detectProject(projectPath),
|
||||
labels: ['task', 'migrated', metadata.status === '✅ COMPLETED' ? 'status:done' : 'planning'],
|
||||
status: metadata.status === '✅ COMPLETED' ? 'Done' : 'Planning',
|
||||
description: `
|
||||
## 📄 Original Implementation Notes
|
||||
|
||||
**Original File**: \`${relativePath}\`
|
||||
**Migrated**: ${new Date().toISOString().split('T')[0]}
|
||||
**Original Status**: ${metadata.status || 'N/A'}
|
||||
|
||||
---
|
||||
|
||||
${transformedContent}
|
||||
`
|
||||
})
|
||||
```
|
||||
|
||||
**For Documentation:**
|
||||
|
||||
```javascript
|
||||
// Don't create Linear issue, just create reference document
|
||||
const doc = await createLinearDocument({
|
||||
title: `Reference: ${metadata.title}`,
|
||||
content: `
|
||||
# ${metadata.title}
|
||||
|
||||
**Source**: \`${relativePath}\`
|
||||
**Type**: Documentation/Guide
|
||||
|
||||
---
|
||||
|
||||
${content}
|
||||
`
|
||||
})
|
||||
|
||||
// Optionally create "Documentation" parent issue to group all docs
|
||||
```
|
||||
|
||||
#### Step 5.4: Move Original File
|
||||
|
||||
After successful migration:
|
||||
|
||||
```bash
|
||||
# Create migrated directory
|
||||
mkdir -p ${projectPath}/.claude/migrated/${category}
|
||||
|
||||
# Move original file
|
||||
mv ${filePath} ${projectPath}/.claude/migrated/${category}/
|
||||
|
||||
# Create breadcrumb file
|
||||
echo "Migrated to Linear: [WORK-123](url)" > ${filePath}.migrated.txt
|
||||
```
|
||||
|
||||
### Step 6: Track Progress
|
||||
|
||||
Show progress during migration:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔄 Migration in Progress...
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[████████████░░░░░░░░] 12/45 (26%)
|
||||
|
||||
Current: 20251031-posthog-observability-implementation.md
|
||||
Status: Creating feature... ✅
|
||||
Creating design doc... ✅
|
||||
Extracting subtasks... ✅ (3 tasks created)
|
||||
Moving to migrated/... ✅
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 7: Generate Migration Summary
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Migration Complete!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📊 Migration Results
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Total Files Migrated: 45
|
||||
- Epics Created: 2
|
||||
- Features Created: 12
|
||||
- Tasks Created: 25
|
||||
- Documentation: 6
|
||||
|
||||
Total Linear Issues Created: 39
|
||||
Total Linear Documents Created: 14
|
||||
Total Sub-Tasks Created: 18
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Created Items Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Epics:
|
||||
✅ WORK-100: Production Deployment Plan
|
||||
✅ WORK-101: Feature Roadmap V1
|
||||
|
||||
Features:
|
||||
✅ WORK-102: PostHog Observability (+ 3 subtasks)
|
||||
✅ WORK-103: Search & Filter System (+ 4 subtasks)
|
||||
✅ WORK-104: Task Comment Enhancements (+ 2 subtasks)
|
||||
... [show first 10, then "and X more"]
|
||||
|
||||
Tasks:
|
||||
✅ WORK-120: Fix PostHog Provider Crash
|
||||
✅ WORK-121: Implement Additional Task Flags
|
||||
... [show first 10, then "and X more"]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📁 Original Files
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Original files moved to:
|
||||
${projectPath}/.claude/migrated/
|
||||
|
||||
Breadcrumb files created with Linear links.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. Review migrated items in Linear
|
||||
2. Update status labels if needed
|
||||
3. Link related features/tasks
|
||||
4. Run /ccpm:utils:report to see project overview
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Create Migration Log
|
||||
|
||||
Create a migration log file:
|
||||
|
||||
```markdown
|
||||
# Migration Log - ${new Date().toISOString().split('T')[0]}
|
||||
|
||||
## Summary
|
||||
|
||||
- Total Files: 45
|
||||
- Epics: 2
|
||||
- Features: 12
|
||||
- Tasks: 25
|
||||
- Documentation: 6
|
||||
|
||||
## Migrated Files
|
||||
|
||||
| Original File | Type | Linear ID | Linear URL | Status |
|
||||
|---------------|------|-----------|------------|--------|
|
||||
| production-deployment-plan.md | Epic | WORK-100 | [Link](url) | ✅ |
|
||||
| 20251031-posthog-observability-implementation.md | Feature | WORK-102 | [Link](url) | ✅ |
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
## Errors
|
||||
|
||||
[None / List any files that failed to migrate]
|
||||
|
||||
## Notes
|
||||
|
||||
- Original files moved to `.claude/migrated/`
|
||||
- All Linear issues labeled with `migrated`
|
||||
- Spec documents created for Epics and Features
|
||||
- Subtasks extracted from checklists
|
||||
```
|
||||
|
||||
Save to: `${projectPath}/.claude/migration-log-${timestamp}.md`
|
||||
|
||||
### Step 9: Interactive Next Actions
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Migration complete! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "View Project Report",
|
||||
description: "See all migrated items organized (/ccpm:utils:report)"
|
||||
},
|
||||
{
|
||||
label: "Review in Linear",
|
||||
description: "Open Linear to review migrated items"
|
||||
},
|
||||
{
|
||||
label: "View Migration Log",
|
||||
description: "Open detailed migration log file"
|
||||
},
|
||||
{
|
||||
label: "Done",
|
||||
description: "Finish migration"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
```javascript
|
||||
function detectTeamAndProject(projectPath) {
|
||||
// Load CCPM configuration
|
||||
const config = loadCCPMConfig() // from ~/.claude/ccpm-config.yaml
|
||||
|
||||
// Try auto-detection from config patterns
|
||||
for (const [projectId, projectConfig] of Object.entries(config.projects)) {
|
||||
const patterns = config.context.detection.patterns || []
|
||||
|
||||
// Check if path matches any detection pattern
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.project === projectId) {
|
||||
const regex = new RegExp(pattern.pattern.replace('*', '.*'))
|
||||
if (regex.test(projectPath)) {
|
||||
return {
|
||||
team: projectConfig.linear.team,
|
||||
project: projectConfig.linear.project,
|
||||
projectId: projectId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also check repository URL match
|
||||
if (projectConfig.repository?.url && projectPath.includes(projectConfig.repository.url)) {
|
||||
return {
|
||||
team: projectConfig.linear.team,
|
||||
project: projectConfig.linear.project,
|
||||
projectId: projectId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no match, use active project or prompt
|
||||
const activeProject = config.context.current_project
|
||||
if (activeProject && config.projects[activeProject]) {
|
||||
return {
|
||||
team: config.projects[activeProject].linear.team,
|
||||
project: config.projects[activeProject].linear.project,
|
||||
projectId: activeProject
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort: prompt user to select from available projects
|
||||
return promptForProject(config.projects)
|
||||
}
|
||||
|
||||
function loadCCPMConfig() {
|
||||
// Load from ~/.claude/ccpm-config.yaml
|
||||
const configPath = path.join(process.env.HOME, '.claude', 'ccpm-config.yaml')
|
||||
return yaml.parse(fs.readFileSync(configPath, 'utf8'))
|
||||
}
|
||||
|
||||
function extractMetadata(content) {
|
||||
const metadata = {}
|
||||
|
||||
// Extract title (first # heading)
|
||||
const titleMatch = content.match(/^#\s+(.+)$/m)
|
||||
metadata.title = titleMatch ? titleMatch[1] : 'Untitled'
|
||||
|
||||
// Extract created date
|
||||
const createdMatch = content.match(/\*\*Created[:\s]+\*\*\s*(\d{4}-\d{2}-\d{2})/i)
|
||||
metadata.created = createdMatch ? createdMatch[1] : null
|
||||
|
||||
// Extract status
|
||||
const statusMatch = content.match(/\*\*Status[:\s]+\*\*\s*(.+?)(?:\n|$)/i)
|
||||
metadata.status = statusMatch ? statusMatch[1].trim() : null
|
||||
|
||||
// Extract priority
|
||||
const priorityMatch = content.match(/\*\*Priority[:\s]+\*\*\s*(.+?)(?:\n|$)/i)
|
||||
metadata.priority = priorityMatch ? mapPriority(priorityMatch[1].trim()) : null
|
||||
|
||||
return metadata
|
||||
}
|
||||
|
||||
function extractChecklist(content) {
|
||||
const tasks = []
|
||||
const checklistRegex = /- \[ \] \*\*(.+?)\*\*[:\s]+(.+?)(?:\(Est: (.+?)\))?$/gm
|
||||
|
||||
let match
|
||||
while ((match = checklistRegex.exec(content)) !== null) {
|
||||
tasks.push({
|
||||
title: match[1].trim(),
|
||||
description: match[2].trim(),
|
||||
estimate: match[3] ? match[3].trim() : null
|
||||
})
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **Safe Migration**: Original files are moved, not deleted
|
||||
- **Preserves History**: Original file path saved in Linear description
|
||||
- **Full Content**: Entire markdown content migrated to Linear Documents
|
||||
- **Relationships**: Checklists → Sub-tasks automatically
|
||||
- **Status Mapping**: Original status preserved as label
|
||||
- **Breadcrumbs**: .migrated.txt files created with Linear links
|
||||
- **Rollback**: Can restore from `.claude/migrated/` if needed
|
||||
441
commands/spec:review.md
Normal file
441
commands/spec:review.md
Normal file
@@ -0,0 +1,441 @@
|
||||
---
|
||||
description: AI-powered spec review for completeness and quality
|
||||
allowed-tools: [LinearMCP, AskUserQuestion]
|
||||
argument-hint: <doc-id>
|
||||
---
|
||||
|
||||
# Review Spec: $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.
|
||||
|
||||
---
|
||||
|
||||
## Argument
|
||||
|
||||
- **$1** - Document ID (e.g., `DOC-456` or document title/slug)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Document
|
||||
|
||||
Use **Linear MCP** `get_document` with ID `$1`:
|
||||
|
||||
- Get full document content
|
||||
- Identify document type (Epic Spec vs Feature Design)
|
||||
- Parse all sections
|
||||
|
||||
### Step 2: Analyze Completeness
|
||||
|
||||
**For Epic Spec, check for:**
|
||||
|
||||
```javascript
|
||||
const epicRequiredSections = {
|
||||
vision: {
|
||||
required: ['Problem Statement', 'Success Metrics'],
|
||||
optional: ['Out of Scope']
|
||||
},
|
||||
userResearch: {
|
||||
required: ['User Personas', 'User Stories'],
|
||||
optional: []
|
||||
},
|
||||
architecture: {
|
||||
required: ['System Components', 'Technology Choices'],
|
||||
optional: ['Integration Points']
|
||||
},
|
||||
features: {
|
||||
required: ['Features Breakdown'],
|
||||
optional: []
|
||||
},
|
||||
timeline: {
|
||||
required: ['Timeline & Milestones'],
|
||||
optional: []
|
||||
},
|
||||
security: {
|
||||
required: ['Security Considerations'],
|
||||
optional: ['Compliance Requirements']
|
||||
}
|
||||
}
|
||||
|
||||
const score = calculateCompletenessScore(doc, epicRequiredSections)
|
||||
// Score: 0-100 based on required sections filled
|
||||
```
|
||||
|
||||
**For Feature Design, check for:**
|
||||
|
||||
```javascript
|
||||
const featureRequiredSections = {
|
||||
requirements: {
|
||||
required: ['Functional Requirements', 'Acceptance Criteria'],
|
||||
optional: ['Non-Functional Requirements', 'User Acceptance Testing']
|
||||
},
|
||||
ux: {
|
||||
required: ['User Flows'],
|
||||
optional: ['Wireframes']
|
||||
},
|
||||
technical: {
|
||||
required: ['Architecture', 'API Design', 'Data Model'],
|
||||
optional: ['Component Structure']
|
||||
},
|
||||
testing: {
|
||||
required: ['Testing Strategy'],
|
||||
optional: ['Test Data']
|
||||
},
|
||||
implementation: {
|
||||
required: ['Implementation Plan', 'Task Breakdown'],
|
||||
optional: ['Dependencies']
|
||||
},
|
||||
security: {
|
||||
required: ['Security Considerations'],
|
||||
optional: []
|
||||
},
|
||||
risks: {
|
||||
required: ['Risks & Mitigations'],
|
||||
optional: []
|
||||
}
|
||||
}
|
||||
|
||||
const score = calculateCompletenessScore(doc, featureRequiredSections)
|
||||
```
|
||||
|
||||
### Step 3: Quality Analysis
|
||||
|
||||
**Content Quality Checks:**
|
||||
|
||||
1. **Specificity**:
|
||||
- ❌ "The system should be fast"
|
||||
- ✅ "API response time < 200ms for 95th percentile"
|
||||
|
||||
2. **Testability**:
|
||||
- ❌ "Feature should work well"
|
||||
- ✅ "When user clicks button, modal appears within 100ms"
|
||||
|
||||
3. **Clarity**:
|
||||
- Avoid vague terms: "probably", "might", "should be good"
|
||||
- Use precise language: "must", "will", "shall"
|
||||
|
||||
4. **Consistency**:
|
||||
- API endpoints follow same naming pattern
|
||||
- Data models use consistent field naming
|
||||
- Code examples use same style
|
||||
|
||||
5. **Technical Feasibility**:
|
||||
- Technology choices are realistic
|
||||
- Timeline is achievable
|
||||
- Dependencies are clear
|
||||
|
||||
### Step 4: Risk Assessment
|
||||
|
||||
**Identify Potential Issues:**
|
||||
|
||||
```javascript
|
||||
const risks = {
|
||||
scope: detectScopeCreep(doc),
|
||||
technical: detectTechnicalRisks(doc),
|
||||
timeline: detectTimelineIssues(doc),
|
||||
dependencies: detectMissingDependencies(doc),
|
||||
security: detectSecurityGaps(doc)
|
||||
}
|
||||
|
||||
function detectScopeCreep(doc) {
|
||||
// Check if feature count is too high
|
||||
// Check if requirements are too broad
|
||||
// Look for "also", "and", "additionally" in requirements
|
||||
return issues
|
||||
}
|
||||
|
||||
function detectTechnicalRisks(doc) {
|
||||
// Check for unproven technologies
|
||||
// Check for complex integrations
|
||||
// Analyze technology choices
|
||||
return issues
|
||||
}
|
||||
|
||||
function detectTimelineIssues(doc) {
|
||||
// Compare task count to estimated time
|
||||
// Check if estimates are too optimistic
|
||||
// Verify buffer exists
|
||||
return issues
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Best Practices Check
|
||||
|
||||
**Epic Spec Best Practices:**
|
||||
|
||||
- [ ] Success metrics are measurable (not "improve UX", but "reduce task completion time by 30%")
|
||||
- [ ] User personas are specific (not "users", but "Project Manager with 5+ years experience")
|
||||
- [ ] Features are prioritized (P0, P1, P2)
|
||||
- [ ] Technology choices include rationale
|
||||
- [ ] Timeline has milestones
|
||||
- [ ] Security considerations are addressed
|
||||
- [ ] Out of scope is defined
|
||||
|
||||
**Feature Design Best Practices:**
|
||||
|
||||
- [ ] Requirements are numbered (FR1, FR2, NFR1)
|
||||
- [ ] Acceptance criteria are testable
|
||||
- [ ] API design follows REST principles
|
||||
- [ ] Data model includes indexes
|
||||
- [ ] Testing strategy covers unit + integration
|
||||
- [ ] Security considerations include auth + validation
|
||||
- [ ] Risks have mitigations
|
||||
- [ ] Implementation tasks have estimates
|
||||
- [ ] Dependencies are explicitly stated
|
||||
|
||||
### Step 6: Generate Review Report
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Spec Review: [Document Title]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📄 Document: [DOC-456](https://linear.app/workspace/document/DOC-456)
|
||||
📊 Type: [Epic Spec / Feature Design]
|
||||
📈 Completeness: [XX]% ([Grade])
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Completeness Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ Complete Sections ([X]/[Y]):
|
||||
- ✓ Requirements (Functional + Acceptance Criteria)
|
||||
- ✓ Architecture (System Components + Tech Stack)
|
||||
- ✓ Testing Strategy
|
||||
|
||||
⚠️ Incomplete Sections ([X]/[Y]):
|
||||
- ⚠️ API Design (Missing error codes)
|
||||
- ⚠️ Security (No rate limiting mentioned)
|
||||
- ⚠️ Timeline (Missing milestones)
|
||||
|
||||
❌ Missing Sections ([X]/[Y]):
|
||||
- ✗ User Flows (Required)
|
||||
- ✗ Risks & Mitigations (Required)
|
||||
|
||||
**Overall Grade**: [A/B/C/D/F]
|
||||
- A (90-100%): Ready for approval
|
||||
- B (75-89%): Minor fixes needed
|
||||
- C (60-74%): Needs work
|
||||
- D (50-59%): Major gaps
|
||||
- F (<50%): Incomplete
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Quality Assessment
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ Strengths:
|
||||
1. Requirements are specific and testable
|
||||
2. Technology choices are well-justified
|
||||
3. Data model includes proper indexes
|
||||
|
||||
⚠️ Issues Found:
|
||||
1. **Vague Success Metrics** (Medium Priority)
|
||||
- Current: "Improve user experience"
|
||||
- Should be: "Reduce task completion time by 30%"
|
||||
- Section: Vision & Goals
|
||||
|
||||
2. **Missing Error Handling** (High Priority)
|
||||
- API design lacks error codes
|
||||
- Should include: Error code taxonomy
|
||||
- Section: API Design
|
||||
|
||||
3. **Timeline Too Optimistic** (Medium Priority)
|
||||
- 20 hours for 6 tasks seems tight
|
||||
- Consider: Adding 30% buffer
|
||||
- Section: Timeline
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🚨 Risks Identified
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**High Priority Risks:**
|
||||
1. **Scope Creep** (Probability: High, Impact: High)
|
||||
- Issue: Feature includes 8+ distinct capabilities
|
||||
- Mitigation: Split into 2 features (Core + Advanced)
|
||||
- Recommended: Review feature breakdown
|
||||
|
||||
2. **Missing Dependencies** (Probability: Medium, Impact: High)
|
||||
- Issue: No mention of auth system setup
|
||||
- Mitigation: Add auth as prerequisite
|
||||
- Recommended: Update dependencies section
|
||||
|
||||
**Medium Priority Risks:**
|
||||
1. **Technical Complexity** (Probability: Medium, Impact: Medium)
|
||||
- Issue: Multiple external API integrations
|
||||
- Mitigation: Add integration testing plan
|
||||
- Recommended: Expand testing section
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✨ Recommendations
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Immediate Actions (Before Approval):**
|
||||
1. Add User Flows section (Required)
|
||||
2. Complete Risks & Mitigations (Required)
|
||||
3. Specify measurable success metrics
|
||||
4. Add API error codes
|
||||
|
||||
**Nice to Have (Can Do Later):**
|
||||
1. Add wireframes/mockups
|
||||
2. Include performance benchmarks
|
||||
3. Add monitoring plan
|
||||
|
||||
**Estimated Time to Address**: 2-3 hours
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Best Practices Checklist
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Legend: ✅ Pass | ⚠️ Partial | ❌ Fail
|
||||
|
||||
✅ Requirements are numbered and testable
|
||||
⚠️ Acceptance criteria exist but lack "Given/When/Then"
|
||||
✅ API follows REST conventions
|
||||
✅ Data model includes indexes
|
||||
⚠️ Testing strategy covers unit tests only (missing E2E)
|
||||
❌ Security: No input validation mentioned
|
||||
✅ Implementation tasks have estimates
|
||||
⚠️ Dependencies mentioned but not in dependency graph
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎬 Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**If Grade A-B:**
|
||||
→ Ready for approval! Update status to `spec:approved`
|
||||
|
||||
**If Grade C-D:**
|
||||
→ Address issues above, then run review again
|
||||
|
||||
**If Grade F:**
|
||||
→ Use `/ccpm:spec:write [doc-id] [section]` to complete missing sections
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 7: Update Linear Issue Label
|
||||
|
||||
**Based on grade:**
|
||||
|
||||
- **A (90-100%)**: Suggest changing label to `spec:approved`
|
||||
- **B (75-89%)**: Keep as `spec:review`
|
||||
- **C-D-F (<75%)**: Keep as `spec:draft`
|
||||
|
||||
**Use AskUserQuestion if grade is A:**
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Spec review complete with Grade A! Should I update the status to 'spec:approved'?",
|
||||
header: "Approval",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, Approve",
|
||||
description: "Update label to spec:approved (ready for implementation)"
|
||||
},
|
||||
{
|
||||
label: "Not Yet",
|
||||
description: "I'll review manually first"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If approved, update Linear issue/initiative labels via **Linear MCP**.
|
||||
|
||||
### Step 8: Interactive Next Actions
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Review complete! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Fix Issues",
|
||||
description: "Use /ccpm:spec:write to address identified issues"
|
||||
},
|
||||
{
|
||||
label: "Break Down into Tasks",
|
||||
description: "Create implementation tasks (/ccpm:spec:break-down)"
|
||||
},
|
||||
{
|
||||
label: "View in Linear",
|
||||
description: "Open document to review in Linear"
|
||||
},
|
||||
{
|
||||
label: "Done",
|
||||
description: "Finish for now"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Scoring Algorithm
|
||||
|
||||
```javascript
|
||||
function calculateCompletenessScore(doc, requiredSections) {
|
||||
let totalRequired = 0
|
||||
let completedRequired = 0
|
||||
let totalOptional = 0
|
||||
let completedOptional = 0
|
||||
|
||||
for (const [section, items] of Object.entries(requiredSections)) {
|
||||
totalRequired += items.required.length
|
||||
totalOptional += items.optional.length
|
||||
|
||||
for (const item of items.required) {
|
||||
if (sectionExists(doc, item) && hasContent(doc, item)) {
|
||||
completedRequired++
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of items.optional) {
|
||||
if (sectionExists(doc, item) && hasContent(doc, item)) {
|
||||
completedOptional++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Required sections: 80% weight
|
||||
// Optional sections: 20% weight
|
||||
const requiredScore = (completedRequired / totalRequired) * 80
|
||||
const optionalScore = (completedOptional / totalOptional) * 20
|
||||
|
||||
return Math.round(requiredScore + optionalScore)
|
||||
}
|
||||
|
||||
function sectionExists(doc, sectionName) {
|
||||
// Check if section heading exists in document
|
||||
return doc.content.includes(`## ${sectionName}`) ||
|
||||
doc.content.includes(`### ${sectionName}`)
|
||||
}
|
||||
|
||||
function hasContent(doc, sectionName) {
|
||||
// Extract section content and check if it has meaningful content
|
||||
// (not just placeholders like "[TODO]" or "[Description]")
|
||||
const sectionContent = extractSection(doc, sectionName)
|
||||
|
||||
const placeholders = ['[TODO]', '[Description]', '[TBD]', '[...]']
|
||||
const hasPlaceholder = placeholders.some(p => sectionContent.includes(p))
|
||||
|
||||
const wordCount = sectionContent.split(/\s+/).length
|
||||
|
||||
return wordCount > 10 && !hasPlaceholder
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Review is non-destructive (read-only analysis)
|
||||
- Provides actionable feedback
|
||||
- Highlights both strengths and issues
|
||||
- Grades based on completeness and quality
|
||||
- Suggests specific improvements
|
||||
665
commands/spec:sync.md
Normal file
665
commands/spec:sync.md
Normal file
@@ -0,0 +1,665 @@
|
||||
---
|
||||
description: Sync spec document with implementation reality
|
||||
allowed-tools: [LinearMCP, Read, Glob, Grep, AskUserQuestion]
|
||||
argument-hint: <doc-id-or-issue-id>
|
||||
---
|
||||
|
||||
# Sync Spec: $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.
|
||||
|
||||
---
|
||||
|
||||
## Argument
|
||||
|
||||
- **$1** - Document ID or Issue ID (will find linked spec doc)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Spec and Related Issues
|
||||
|
||||
If `$1` is an issue ID:
|
||||
|
||||
```javascript
|
||||
// 1. Get issue
|
||||
const issue = await getLinearIssue($1)
|
||||
|
||||
// 2. Extract spec doc link from description
|
||||
const docLinkPattern = /\[(?:Epic Spec|Feature Design): .+?\]\((.+?)\)/
|
||||
const match = issue.description.match(docLinkPattern)
|
||||
|
||||
if (match) {
|
||||
const docUrl = match[1]
|
||||
const docId = extractDocId(docUrl)
|
||||
}
|
||||
|
||||
// 3. Get all sub-issues (tasks)
|
||||
const tasks = await getLinearSubIssues(issue.id)
|
||||
```
|
||||
|
||||
If `$1` is a doc ID:
|
||||
|
||||
```javascript
|
||||
// 1. Get document
|
||||
const doc = await getLinearDocument($1)
|
||||
|
||||
// 2. Find linked issue from document content or metadata
|
||||
// Look for "Linear Epic:" or "Linear Feature:" links
|
||||
const issueLinkPattern = /\[WORK-\d+\]\((.+?)\)/
|
||||
const match = doc.content.match(issueLinkPattern)
|
||||
|
||||
if (match) {
|
||||
const issueUrl = match[1]
|
||||
const issueId = extractIssueId(issueUrl)
|
||||
const issue = await getLinearIssue(issueId)
|
||||
const tasks = await getLinearSubIssues(issue.id)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Analyze Spec vs Reality
|
||||
|
||||
**Compare spec sections with actual implementation:**
|
||||
|
||||
#### 2.1: Requirements Drift
|
||||
|
||||
```javascript
|
||||
function checkRequirementsDrift(specDoc, tasks) {
|
||||
const specRequirements = extractRequirements(specDoc)
|
||||
const implementedFeatures = extractImplementedFeatures(tasks)
|
||||
|
||||
const drift = {
|
||||
missing: [], // In spec but not implemented
|
||||
extra: [], // Implemented but not in spec
|
||||
changed: [] // Different from spec
|
||||
}
|
||||
|
||||
// Compare
|
||||
for (const req of specRequirements) {
|
||||
const implemented = implementedFeatures.find(f => matches(f, req))
|
||||
|
||||
if (!implemented) {
|
||||
drift.missing.push(req)
|
||||
} else if (!exactMatch(implemented, req)) {
|
||||
drift.changed.push({ spec: req, actual: implemented })
|
||||
}
|
||||
}
|
||||
|
||||
for (const feature of implementedFeatures) {
|
||||
if (!specRequirements.find(r => matches(feature, r))) {
|
||||
drift.extra.push(feature)
|
||||
}
|
||||
}
|
||||
|
||||
return drift
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2: Implementation Tasks Drift
|
||||
|
||||
```javascript
|
||||
function checkTasksDrift(specDoc, linearTasks) {
|
||||
const specTasks = extractTasksFromSpec(specDoc)
|
||||
// From "## Implementation Plan" or "## Task Breakdown"
|
||||
|
||||
const drift = {
|
||||
inSpecNotLinear: [], // Tasks in spec but no Linear issue
|
||||
inLinearNotSpec: [], // Linear tasks not documented in spec
|
||||
statusMismatch: [] // Different completion status
|
||||
}
|
||||
|
||||
// Compare task lists
|
||||
for (const specTask of specTasks) {
|
||||
const linearTask = linearTasks.find(lt => matches(lt, specTask))
|
||||
|
||||
if (!linearTask) {
|
||||
drift.inSpecNotLinear.push(specTask)
|
||||
} else {
|
||||
// Check if status matches
|
||||
const specCompleted = specTask.checked
|
||||
const linearCompleted = linearTask.status === 'Done'
|
||||
|
||||
if (specCompleted !== linearCompleted) {
|
||||
drift.statusMismatch.push({
|
||||
task: specTask,
|
||||
specStatus: specCompleted ? 'Done' : 'Pending',
|
||||
linearStatus: linearTask.status
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const linearTask of linearTasks) {
|
||||
if (!specTasks.find(st => matches(linearTask, st))) {
|
||||
drift.inLinearNotSpec.push(linearTask)
|
||||
}
|
||||
}
|
||||
|
||||
return drift
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3: API Design Drift
|
||||
|
||||
```javascript
|
||||
function checkApiDrift(specDoc, codebase) {
|
||||
const specApis = extractApiDesign(specDoc)
|
||||
const implementedApis = searchCodebaseForApis(codebase)
|
||||
|
||||
const drift = {
|
||||
endpointsMissing: [],
|
||||
endpointsExtra: [],
|
||||
signatureChanged: []
|
||||
}
|
||||
|
||||
// Compare endpoints
|
||||
for (const specApi of specApis) {
|
||||
const impl = implementedApis.find(api => api.path === specApi.path)
|
||||
|
||||
if (!impl) {
|
||||
drift.endpointsMissing.push(specApi)
|
||||
} else if (!signaturesMatch(impl, specApi)) {
|
||||
drift.signatureChanged.push({
|
||||
spec: specApi,
|
||||
actual: impl,
|
||||
differences: compareSignatures(specApi, impl)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return drift
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.4: Data Model Drift
|
||||
|
||||
```javascript
|
||||
function checkDataModelDrift(specDoc, codebase) {
|
||||
const specModels = extractDataModel(specDoc)
|
||||
const implementedModels = searchCodebaseForModels(codebase)
|
||||
|
||||
const drift = {
|
||||
tablesMissing: [],
|
||||
tablesExtra: [],
|
||||
fieldsMissing: [],
|
||||
fieldsChanged: []
|
||||
}
|
||||
|
||||
// Compare schemas
|
||||
for (const specModel of specModels) {
|
||||
const impl = implementedModels.find(m => m.name === specModel.name)
|
||||
|
||||
if (!impl) {
|
||||
drift.tablesMissing.push(specModel)
|
||||
} else {
|
||||
// Compare fields
|
||||
for (const specField of specModel.fields) {
|
||||
const implField = impl.fields.find(f => f.name === specField.name)
|
||||
|
||||
if (!implField) {
|
||||
drift.fieldsMissing.push({
|
||||
table: specModel.name,
|
||||
field: specField
|
||||
})
|
||||
} else if (specField.type !== implField.type) {
|
||||
drift.fieldsChanged.push({
|
||||
table: specModel.name,
|
||||
field: specField.name,
|
||||
specType: specField.type,
|
||||
actualType: implField.type
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return drift
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Search Codebase for Implementation
|
||||
|
||||
Use **Glob** and **Grep** to find implemented code:
|
||||
|
||||
```javascript
|
||||
async function searchCodebaseForApis(projectPath) {
|
||||
// Search for API route files
|
||||
const apiFiles = await glob(`${projectPath}/**/api/**/*.{ts,js}`)
|
||||
|
||||
const apis = []
|
||||
|
||||
for (const file of apiFiles) {
|
||||
const content = await read(file)
|
||||
|
||||
// Extract API endpoints
|
||||
// Look for: app.post('/api/endpoint', ...) or export async function POST(...)
|
||||
const endpointPattern = /(?:app\.(get|post|put|delete|patch)|export async function (GET|POST|PUT|DELETE|PATCH))\s*\(\s*['"`](.+?)['"`]/g
|
||||
|
||||
let match
|
||||
while ((match = endpointPattern.exec(content)) !== null) {
|
||||
const method = match[1] || match[2]
|
||||
const path = match[3]
|
||||
|
||||
apis.push({
|
||||
method: method.toUpperCase(),
|
||||
path: path,
|
||||
file: file,
|
||||
// Could extract request/response types if TypeScript
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return apis
|
||||
}
|
||||
|
||||
async function searchCodebaseForModels(projectPath) {
|
||||
// Search for database schema files
|
||||
const schemaPattern = `${projectPath}/**/schema/**/*.{ts,js,sql}`
|
||||
const schemaFiles = await glob(schemaPattern)
|
||||
|
||||
const models = []
|
||||
|
||||
for (const file of schemaFiles) {
|
||||
const content = await read(file)
|
||||
|
||||
// Extract table definitions
|
||||
// Drizzle: pgTable('table_name', { ... })
|
||||
// Prisma: model TableName { ... }
|
||||
// SQL: CREATE TABLE table_name ( ... )
|
||||
|
||||
// Parse and extract models
|
||||
// This is simplified - actual implementation would parse properly
|
||||
models.push(...parseSchema(content))
|
||||
}
|
||||
|
||||
return models
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Generate Sync Report
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔄 Spec Sync Report: [Document Title]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📄 Spec Doc: [DOC-456](link)
|
||||
🎯 Issue: [WORK-123 - Feature Title](link)
|
||||
📅 Last Synced: [Never / Date]
|
||||
📊 Drift Score: [XX]% ([Grade])
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Overall Drift Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Drift Score**: 85% ← Lower is better (0% = perfect sync)
|
||||
|
||||
Legend: ✅ In Sync | ⚠️ Minor Drift | ❌ Major Drift
|
||||
|
||||
✅ Requirements: 2 missing, 1 extra (10% drift)
|
||||
⚠️ Implementation Tasks: 3 status mismatches (25% drift)
|
||||
❌ API Design: 2 endpoints differ (40% drift)
|
||||
✅ Data Model: All tables match (0% drift)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Requirements Drift
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**In Spec, Not Implemented:**
|
||||
1. FR3: Password reset via email
|
||||
- Spec says: "Users can reset password via email link"
|
||||
- Reality: No implementation found
|
||||
- Action: Implement or remove from spec
|
||||
|
||||
2. NFR2: Response time < 200ms
|
||||
- Spec says: "95th percentile response time under 200ms"
|
||||
- Reality: Not measured/verified
|
||||
- Action: Add performance monitoring
|
||||
|
||||
**Implemented, Not in Spec:**
|
||||
1. Social Login (Google OAuth)
|
||||
- Found: POST /api/auth/google endpoint
|
||||
- Not documented in spec
|
||||
- Action: Add to spec or mark as scope creep
|
||||
|
||||
**Changed from Spec:**
|
||||
1. Login Rate Limiting
|
||||
- Spec: "10 attempts per hour"
|
||||
- Actual: 5 attempts per 15 minutes (stricter)
|
||||
- Action: Update spec to reflect current implementation
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Implementation Tasks Drift
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Status Mismatches:**
|
||||
1. Task 2: API endpoints
|
||||
- Spec Checklist: ✅ Marked complete
|
||||
- Linear Status: In Progress
|
||||
- Action: Update spec checklist or complete Linear task
|
||||
|
||||
2. Task 4: Testing
|
||||
- Spec Checklist: ⏳ Not checked
|
||||
- Linear Status: Done
|
||||
- Action: Check off in spec
|
||||
|
||||
3. Task 5: Documentation
|
||||
- Spec Checklist: ⏳ Not checked
|
||||
- Linear Status: Done
|
||||
- Action: Check off in spec
|
||||
|
||||
**In Spec, No Linear Task:**
|
||||
1. Task 6: Performance optimization
|
||||
- Missing Linear task
|
||||
- Action: Create Linear task or remove from spec
|
||||
|
||||
**In Linear, Not in Spec:**
|
||||
1. WORK-125: Fix login bug (subtask)
|
||||
- Unplanned task added during implementation
|
||||
- Action: Add to spec as "Bug Fixes" section
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔌 API Design Drift
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Signature Changed:**
|
||||
1. POST /api/auth/login
|
||||
- Spec Request:
|
||||
```typescript
|
||||
{ email: string, password: string }
|
||||
```
|
||||
- Actual Request:
|
||||
```typescript
|
||||
{ email: string, password: string, rememberMe?: boolean }
|
||||
```
|
||||
- Change: Added optional `rememberMe` field
|
||||
- Action: Update spec with new signature
|
||||
|
||||
2. POST /api/auth/refresh
|
||||
- Spec Response:
|
||||
```typescript
|
||||
{ token: string }
|
||||
```
|
||||
- Actual Response:
|
||||
```typescript
|
||||
{ accessToken: string, refreshToken: string, expiresIn: number }
|
||||
```
|
||||
- Change: More detailed response
|
||||
- Action: Update spec to match implementation
|
||||
|
||||
**Endpoints Missing:**
|
||||
- None ✅
|
||||
|
||||
**Extra Endpoints (Not in Spec):**
|
||||
1. GET /api/auth/session
|
||||
- Found in code but not documented
|
||||
- Action: Add to spec
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🗄️ Data Model Drift
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ All tables and fields match spec! No drift detected.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Recommended Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Critical (Fix Immediately):**
|
||||
1. Update API signatures in spec (2 endpoints changed)
|
||||
2. Implement missing requirements or remove from spec (2 items)
|
||||
|
||||
**Important (Fix Soon):**
|
||||
1. Sync task statuses between spec checklist and Linear (3 mismatches)
|
||||
2. Document unplanned features added during implementation (1 endpoint)
|
||||
|
||||
**Nice to Have:**
|
||||
1. Add performance monitoring for NFR validation
|
||||
2. Create missing Linear tasks for spec items
|
||||
|
||||
**Estimated Time to Sync**: 2-3 hours
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 5: Ask User How to Resolve Drift
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Drift detected! How would you like to resolve it?",
|
||||
header: "Sync Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Update Spec to Match Reality",
|
||||
description: "Modify spec doc to reflect current implementation (recommended)"
|
||||
},
|
||||
{
|
||||
label: "Update Implementation to Match Spec",
|
||||
description: "Modify code to match original spec design"
|
||||
},
|
||||
{
|
||||
label: "Hybrid Approach",
|
||||
description: "Update spec for some items, code for others (I'll choose)"
|
||||
},
|
||||
{
|
||||
label: "Review Only",
|
||||
description: "Just show the report, I'll fix manually"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Apply Sync Changes
|
||||
|
||||
#### If "Update Spec to Match Reality":
|
||||
|
||||
```javascript
|
||||
// Update spec document with actual implementation
|
||||
|
||||
// 1. Update Requirements section
|
||||
updateSpecSection(doc, 'requirements', {
|
||||
add: drift.requirements.extra,
|
||||
remove: drift.requirements.missing,
|
||||
modify: drift.requirements.changed
|
||||
})
|
||||
|
||||
// 2. Update API Design section
|
||||
updateSpecSection(doc, 'api-design', {
|
||||
updateSignatures: drift.api.signatureChanged,
|
||||
addEndpoints: drift.api.endpointsExtra
|
||||
})
|
||||
|
||||
// 3. Update Task Checklist
|
||||
updateSpecChecklist(doc, {
|
||||
check: drift.tasks.statusMismatch.filter(t => t.linearStatus === 'Done'),
|
||||
uncheck: drift.tasks.statusMismatch.filter(t => t.linearStatus !== 'Done'),
|
||||
add: drift.tasks.inLinearNotSpec
|
||||
})
|
||||
|
||||
// 4. Add "Change Log" entry
|
||||
appendToChangeLog(doc, {
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
change: 'Synced spec with implementation reality',
|
||||
details: `Updated ${changesCount} sections to match current implementation`,
|
||||
author: currentUser
|
||||
})
|
||||
|
||||
// 5. Update "Last Synced" timestamp in spec
|
||||
updateMetadata(doc, {
|
||||
lastSynced: new Date().toISOString()
|
||||
})
|
||||
```
|
||||
|
||||
#### If "Update Implementation to Match Spec":
|
||||
|
||||
```javascript
|
||||
// Show what needs to be implemented to match spec
|
||||
|
||||
const implementationPlan = {
|
||||
missingRequirements: drift.requirements.missing,
|
||||
missingEndpoints: drift.api.endpointsMissing,
|
||||
changedSignatures: drift.api.signatureChanged,
|
||||
missingTasks: drift.tasks.inSpecNotLinear
|
||||
}
|
||||
|
||||
// Show plan and ask if user wants to create Linear tasks for fixes
|
||||
const tasks = generateFixTasks(implementationPlan)
|
||||
|
||||
// Offer to create tasks via /ccpm:spec:break-down or manually
|
||||
```
|
||||
|
||||
#### If "Hybrid Approach":
|
||||
|
||||
```javascript
|
||||
// Show each drift item and ask user decision
|
||||
|
||||
for (const item of allDriftItems) {
|
||||
const choice = await askUserQuestion({
|
||||
question: `Drift: ${item.description}. How to resolve?`,
|
||||
options: [
|
||||
"Update Spec",
|
||||
"Update Code",
|
||||
"Keep As Is"
|
||||
]
|
||||
})
|
||||
|
||||
if (choice === "Update Spec") {
|
||||
updateSpec(item)
|
||||
} else if (choice === "Update Code") {
|
||||
addToImplementationBacklog(item)
|
||||
}
|
||||
// else: skip
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: Display Sync Results
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Spec Synced Successfully!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📄 Document: [DOC-456](link)
|
||||
🔄 Sync Method: [Update Spec to Match Reality]
|
||||
📊 Changes Applied: 12
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Changes Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Requirements Section:**
|
||||
- ✅ Added: 1 new requirement (Social Login)
|
||||
- ✅ Removed: 2 unimplemented requirements
|
||||
- ✅ Updated: 1 changed requirement (Rate Limiting)
|
||||
|
||||
**API Design Section:**
|
||||
- ✅ Updated: 2 endpoint signatures
|
||||
- ✅ Added: 1 undocumented endpoint (GET /api/auth/session)
|
||||
|
||||
**Task Checklist:**
|
||||
- ✅ Checked: 2 completed tasks
|
||||
- ✅ Added: 1 new task (Bug Fixes)
|
||||
|
||||
**Metadata:**
|
||||
- ✅ Last Synced: ${new Date().toISOString().split('T')[0]}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 New Drift Score: 5% (was 85%)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Interactive Next Actions
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Sync complete! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Review Updated Spec",
|
||||
description: "Open spec document in Linear to review changes"
|
||||
},
|
||||
{
|
||||
label: "Review Spec",
|
||||
description: "Run /ccpm:spec:review to validate updated spec"
|
||||
},
|
||||
{
|
||||
label: "View Project Status",
|
||||
description: "Check overall project status (/ccpm:utils:status)"
|
||||
},
|
||||
{
|
||||
label: "Done",
|
||||
description: "Finish for now"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Drift Score Calculation
|
||||
|
||||
```javascript
|
||||
function calculateDriftScore(drift) {
|
||||
let totalItems = 0
|
||||
let driftItems = 0
|
||||
|
||||
// Requirements
|
||||
totalItems += drift.requirements.total
|
||||
driftItems += drift.requirements.missing.length +
|
||||
drift.requirements.extra.length +
|
||||
drift.requirements.changed.length
|
||||
|
||||
// Tasks
|
||||
totalItems += drift.tasks.total
|
||||
driftItems += drift.tasks.statusMismatch.length +
|
||||
drift.tasks.inSpecNotLinear.length +
|
||||
drift.tasks.inLinearNotSpec.length
|
||||
|
||||
// API
|
||||
totalItems += drift.api.total
|
||||
driftItems += drift.api.endpointsMissing.length +
|
||||
drift.api.endpointsExtra.length +
|
||||
drift.api.signatureChanged.length
|
||||
|
||||
// Data Model
|
||||
totalItems += drift.dataModel.total
|
||||
driftItems += drift.dataModel.tablesMissing.length +
|
||||
drift.dataModel.tablesExtra.length +
|
||||
drift.dataModel.fieldsMissing.length +
|
||||
drift.dataModel.fieldsChanged.length
|
||||
|
||||
// Calculate percentage
|
||||
if (totalItems === 0) return 0
|
||||
|
||||
const driftPercentage = Math.round((driftItems / totalItems) * 100)
|
||||
|
||||
return driftPercentage
|
||||
}
|
||||
|
||||
function getDriftGrade(score) {
|
||||
if (score <= 10) return { grade: 'A', label: 'Excellent Sync' }
|
||||
if (score <= 25) return { grade: 'B', label: 'Minor Drift' }
|
||||
if (score <= 50) return { grade: 'C', label: 'Moderate Drift' }
|
||||
if (score <= 75) return { grade: 'D', label: 'Major Drift' }
|
||||
return { grade: 'F', label: 'Significant Drift' }
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Non-destructive: Always creates backups before updating
|
||||
- Bidirectional: Can sync spec → code or code → spec
|
||||
- Smart Detection: Uses codebase analysis to find actual implementation
|
||||
- Preserves Intent: Asks user before resolving ambiguous drift
|
||||
- Change Log: Tracks all sync operations in spec document
|
||||
- Drift Score: Quantifies how much spec diverged from reality
|
||||
741
commands/spec:write.md
Normal file
741
commands/spec:write.md
Normal file
@@ -0,0 +1,741 @@
|
||||
---
|
||||
description: AI-assisted spec document writing
|
||||
allowed-tools: [LinearMCP, Read, Glob, Grep, Context7MCP, AskUserQuestion]
|
||||
argument-hint: <doc-id> <section>
|
||||
---
|
||||
|
||||
# Write Spec Section: $1 → $2
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
---
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Document ID (e.g., `DOC-456` or document title/slug)
|
||||
- **$2** - Section to write: `requirements`, `architecture`, `api-design`, `data-model`, `testing`, `security`, `user-flow`, `timeline`, `all`
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Document
|
||||
|
||||
Use **Linear MCP** `get_document` with ID `$1`:
|
||||
|
||||
- Get current document content
|
||||
- Parse document structure
|
||||
- Identify document type (Epic Spec vs Feature Design)
|
||||
- Extract existing sections
|
||||
|
||||
### Step 2: Analyze Context
|
||||
|
||||
**For ALL sections**, gather context:
|
||||
|
||||
1. **Read related Linear issue/initiative** (extract from doc or ask)
|
||||
2. **Search codebase** for similar features:
|
||||
- Use **Glob** to find related files
|
||||
- Use **Grep** to search for patterns
|
||||
- Read existing implementations
|
||||
|
||||
3. **Check for documentation** in project:
|
||||
- Look in `.claude/docs/`
|
||||
- Look in `.claude/plans/`
|
||||
- Look in `.claude/enhancements/`
|
||||
- Look in project README, docs folders
|
||||
|
||||
4. **Fetch library docs** if technical section:
|
||||
- Use **Context7 MCP** for frameworks/libraries
|
||||
- Get latest API documentation
|
||||
|
||||
### Step 3: Write Section Content
|
||||
|
||||
Based on `$2` parameter:
|
||||
|
||||
#### `requirements` Section
|
||||
|
||||
**For Features:**
|
||||
```markdown
|
||||
## 📋 Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
[AI analyzes feature title and existing context to generate:]
|
||||
|
||||
- **FR1**: [Specific, testable requirement]
|
||||
- Input: [What user provides]
|
||||
- Output: [What system produces]
|
||||
- Business Rule: [Logic/validation]
|
||||
|
||||
- **FR2**: [Next requirement]
|
||||
- ...
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
- **NFR1: Performance**: [Specific metric, e.g., "API response < 200ms for 95th percentile"]
|
||||
- **NFR2: Scalability**: [Capacity requirement, e.g., "Handle 1000 concurrent users"]
|
||||
- **NFR3: Availability**: [Uptime requirement, e.g., "99.9% uptime"]
|
||||
- **NFR4: Security**: [Security requirement, e.g., "All data encrypted at rest"]
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
**Must Have:**
|
||||
- [ ] [Testable criterion 1]
|
||||
- [ ] [Testable criterion 2]
|
||||
|
||||
**Nice to Have:**
|
||||
- [ ] [Optional criterion 1]
|
||||
|
||||
### User Acceptance Testing
|
||||
|
||||
**Test Scenarios:**
|
||||
1. **Scenario**: [Description]
|
||||
- **Given**: [Initial state]
|
||||
- **When**: [Action]
|
||||
- **Then**: [Expected outcome]
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Analyze feature title and description
|
||||
- Search codebase for similar features
|
||||
- Generate specific, testable requirements
|
||||
- Include edge cases and error handling
|
||||
- Make acceptance criteria SMART (Specific, Measurable, Achievable, Relevant, Time-bound)
|
||||
|
||||
#### `architecture` Section
|
||||
|
||||
```markdown
|
||||
## 🏗️ Architecture
|
||||
|
||||
### System Overview
|
||||
|
||||
[AI generates based on feature scope:]
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Mobile App │
|
||||
│ (React Native) │
|
||||
└────────┬────────┘
|
||||
│ HTTPS
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ API Gateway │
|
||||
│ (Next.js API) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Database │
|
||||
│ (Postgres) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Component Breakdown
|
||||
|
||||
**Frontend Components:**
|
||||
- **Component1**: [Purpose, props, state]
|
||||
- **Component2**: [Purpose, props, state]
|
||||
|
||||
**Backend Services:**
|
||||
- **Service1**: [Responsibility, endpoints]
|
||||
- **Service2**: [Responsibility, endpoints]
|
||||
|
||||
**Data Layer:**
|
||||
- **Model1**: [Fields, relationships]
|
||||
|
||||
### Technology Stack
|
||||
|
||||
| Layer | Technology | Rationale |
|
||||
|-------|-----------|-----------|
|
||||
| Frontend | [e.g., React Native] | [Why this choice] |
|
||||
| Backend | [e.g., Next.js API Routes] | [Why this choice] |
|
||||
| Database | [e.g., Postgres] | [Why this choice] |
|
||||
| State Management | [e.g., TanStack Query] | [Why this choice] |
|
||||
|
||||
### Design Patterns
|
||||
|
||||
- **Pattern 1**: [e.g., Repository Pattern for data access]
|
||||
- **Pattern 2**: [e.g., Factory Pattern for object creation]
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Detect tech stack from codebase (read package.json, check imports)
|
||||
- Use **Context7 MCP** to get latest docs for detected libraries
|
||||
- Generate architecture diagram in ASCII
|
||||
- Recommend patterns based on feature complexity
|
||||
- Ensure consistency with existing codebase architecture
|
||||
|
||||
#### `api-design` Section
|
||||
|
||||
```markdown
|
||||
## 🔌 API Design
|
||||
|
||||
### Endpoints
|
||||
|
||||
#### [Method] [Path]
|
||||
|
||||
**Description**: [What this endpoint does]
|
||||
|
||||
**Request:**
|
||||
```typescript
|
||||
// Headers
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
|
||||
// Body (if POST/PUT/PATCH)
|
||||
{
|
||||
field1: type, // Description
|
||||
field2: type // Description
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```typescript
|
||||
// Success (200)
|
||||
{
|
||||
data: {
|
||||
id: string,
|
||||
field1: type,
|
||||
field2: type
|
||||
}
|
||||
}
|
||||
|
||||
// Error (400/500)
|
||||
{
|
||||
error: {
|
||||
code: string,
|
||||
message: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Validation:**
|
||||
- `field1`: Required, must be [constraint]
|
||||
- `field2`: Optional, defaults to [value]
|
||||
|
||||
**Business Logic:**
|
||||
1. Validate input
|
||||
2. Check permissions
|
||||
3. Process request
|
||||
4. Return response
|
||||
|
||||
**Error Codes:**
|
||||
- `INVALID_INPUT`: Input validation failed
|
||||
- `UNAUTHORIZED`: User not authenticated
|
||||
- `FORBIDDEN`: User lacks permission
|
||||
|
||||
---
|
||||
|
||||
### API Contract
|
||||
|
||||
**Base URL**: `[e.g., /api/v1]`
|
||||
|
||||
**Authentication**: Bearer token (Clerk JWT)
|
||||
|
||||
**Rate Limiting**: [e.g., 100 requests/minute per user]
|
||||
|
||||
**Pagination**:
|
||||
```typescript
|
||||
{
|
||||
data: T[],
|
||||
pagination: {
|
||||
page: number,
|
||||
limit: number,
|
||||
total: number,
|
||||
hasMore: boolean
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Analyze feature requirements
|
||||
- Generate RESTful endpoints following project conventions
|
||||
- Search codebase for existing API patterns (use Grep)
|
||||
- Include proper TypeScript types
|
||||
- Add validation rules
|
||||
- Document error handling
|
||||
|
||||
#### `data-model` Section
|
||||
|
||||
```markdown
|
||||
## 🗄️ Data Model
|
||||
|
||||
### Database Schema
|
||||
|
||||
#### Table: [table_name]
|
||||
|
||||
```sql
|
||||
CREATE TABLE [table_name] (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
field1 VARCHAR(255) NOT NULL,
|
||||
field2 INTEGER,
|
||||
field3 TIMESTAMP,
|
||||
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
|
||||
FOREIGN KEY (field2) REFERENCES other_table(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_[table_name]_field1 ON [table_name](field1);
|
||||
```
|
||||
|
||||
**Fields:**
|
||||
- `id`: Primary key (UUID)
|
||||
- `field1`: [Description, constraints, purpose]
|
||||
- `field2`: [Description, constraints, purpose]
|
||||
|
||||
**Indexes:**
|
||||
- `idx_[table_name]_field1`: For fast lookup by field1
|
||||
|
||||
**Relationships:**
|
||||
- Belongs to: `other_table` (many-to-one)
|
||||
- Has many: `child_table` (one-to-many)
|
||||
|
||||
### TypeScript Types
|
||||
|
||||
```typescript
|
||||
// Database model
|
||||
interface [ModelName] {
|
||||
id: string
|
||||
field1: string
|
||||
field2: number | null
|
||||
field3: Date | null
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
// API DTO (Data Transfer Object)
|
||||
interface [ModelName]DTO {
|
||||
id: string
|
||||
field1: string
|
||||
field2?: number
|
||||
// Omit internal fields like createdAt if not needed in API
|
||||
}
|
||||
|
||||
// Create input
|
||||
interface Create[ModelName]Input {
|
||||
field1: string
|
||||
field2?: number
|
||||
}
|
||||
|
||||
// Update input
|
||||
interface Update[ModelName]Input {
|
||||
field1?: string
|
||||
field2?: number
|
||||
}
|
||||
```
|
||||
|
||||
### Migrations
|
||||
|
||||
**Migration File**: `YYYYMMDD-HHmmss-add-[table_name].sql`
|
||||
|
||||
```sql
|
||||
-- Up
|
||||
CREATE TABLE [table_name] (...);
|
||||
|
||||
-- Down
|
||||
DROP TABLE [table_name];
|
||||
```
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Search codebase for existing schema files (Drizzle, Prisma, raw SQL)
|
||||
- Follow existing naming conventions
|
||||
- Generate indexes for foreign keys and frequently queried fields
|
||||
- Include soft delete if project uses it
|
||||
- Add TypeScript types matching database schema
|
||||
|
||||
#### `testing` Section
|
||||
|
||||
```markdown
|
||||
## 🧪 Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
|
||||
**Backend API Tests:**
|
||||
|
||||
```typescript
|
||||
describe('[Feature Name] API', () => {
|
||||
describe('POST /api/endpoint', () => {
|
||||
it('should create [entity] successfully', async () => {
|
||||
// Arrange
|
||||
const input = { ... }
|
||||
|
||||
// Act
|
||||
const response = await request(app)
|
||||
.post('/api/endpoint')
|
||||
.send(input)
|
||||
|
||||
// Assert
|
||||
expect(response.status).toBe(201)
|
||||
expect(response.body.data.field1).toBe(input.field1)
|
||||
})
|
||||
|
||||
it('should return 400 for invalid input', async () => {
|
||||
// Test validation
|
||||
})
|
||||
|
||||
it('should return 401 for unauthenticated request', async () => {
|
||||
// Test auth
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
**Frontend Component Tests:**
|
||||
|
||||
```typescript
|
||||
describe('[ComponentName]', () => {
|
||||
it('should render correctly', () => {
|
||||
render(<ComponentName />)
|
||||
expect(screen.getByText('Expected Text')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle user interaction', async () => {
|
||||
const mockFn = jest.fn()
|
||||
render(<ComponentName onAction={mockFn} />)
|
||||
|
||||
fireEvent.press(screen.getByTestId('action-button'))
|
||||
await waitFor(() => expect(mockFn).toHaveBeenCalled())
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
**Test Scenarios:**
|
||||
|
||||
1. **End-to-End Flow**:
|
||||
- User performs action A
|
||||
- System processes B
|
||||
- User sees result C
|
||||
|
||||
2. **Error Handling**:
|
||||
- Network failure during request
|
||||
- Invalid server response
|
||||
- User sees error message
|
||||
|
||||
### Manual Testing Checklist
|
||||
|
||||
- [ ] Happy path works
|
||||
- [ ] Error states handled
|
||||
- [ ] Loading states shown
|
||||
- [ ] Accessibility (screen reader)
|
||||
- [ ] Performance (no lag)
|
||||
|
||||
### Test Data
|
||||
|
||||
**Fixtures:**
|
||||
```typescript
|
||||
const mockData = {
|
||||
valid: { ... },
|
||||
invalid: { ... }
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Generate tests for all requirements and acceptance criteria
|
||||
- Use project's testing framework (detect from package.json)
|
||||
- Follow existing test patterns (search with Grep)
|
||||
- Include edge cases
|
||||
|
||||
#### `security` Section
|
||||
|
||||
```markdown
|
||||
## 🔒 Security Considerations
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
**Authentication:**
|
||||
- [e.g., Clerk JWT tokens]
|
||||
- Token expiration: [e.g., 1 hour]
|
||||
- Refresh token flow: [e.g., sliding window]
|
||||
|
||||
**Authorization:**
|
||||
- **Role-Based Access Control (RBAC)**:
|
||||
- Admin: Full access
|
||||
- User: Limited access
|
||||
|
||||
**Permission Checks:**
|
||||
```typescript
|
||||
if (!user.hasPermission('feature.action')) {
|
||||
throw new ForbiddenError('Insufficient permissions')
|
||||
}
|
||||
```
|
||||
|
||||
### Input Validation
|
||||
|
||||
**Server-Side Validation:**
|
||||
- All user inputs validated with [e.g., Zod]
|
||||
- SQL injection prevention: Parameterized queries
|
||||
- XSS prevention: HTML escaping on output
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
const schema = z.object({
|
||||
field1: z.string().min(1).max(255),
|
||||
field2: z.number().positive()
|
||||
})
|
||||
|
||||
const validated = schema.parse(input) // Throws if invalid
|
||||
```
|
||||
|
||||
### Data Protection
|
||||
|
||||
**Encryption:**
|
||||
- At rest: [e.g., Database-level encryption]
|
||||
- In transit: HTTPS only
|
||||
|
||||
**Sensitive Data:**
|
||||
- Passwords: Hashed with bcrypt (12 rounds)
|
||||
- API keys: Stored in environment variables
|
||||
- PII: [e.g., Encrypted at field level]
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
**API Rate Limits:**
|
||||
- 100 requests/minute per user
|
||||
- 1000 requests/hour per IP
|
||||
|
||||
**Implementation:**
|
||||
```typescript
|
||||
const limiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 100
|
||||
})
|
||||
```
|
||||
|
||||
### Security Headers
|
||||
|
||||
```typescript
|
||||
// Content Security Policy
|
||||
'Content-Security-Policy': "default-src 'self'"
|
||||
|
||||
// Prevent clickjacking
|
||||
'X-Frame-Options': 'DENY'
|
||||
|
||||
// XSS Protection
|
||||
'X-Content-Type-Options': 'nosniff'
|
||||
```
|
||||
|
||||
### Audit Logging
|
||||
|
||||
**Log Security Events:**
|
||||
- Failed login attempts
|
||||
- Permission changes
|
||||
- Data access (for sensitive data)
|
||||
|
||||
**Log Format:**
|
||||
```typescript
|
||||
{
|
||||
timestamp: '2025-01-01T00:00:00Z',
|
||||
event: 'AUTH_FAILED',
|
||||
userId: 'user_123',
|
||||
ip: '1.2.3.4',
|
||||
metadata: { reason: 'invalid_password' }
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Detect auth system from codebase (Clerk, NextAuth, etc.)
|
||||
- Use **Context7 MCP** for security best practices
|
||||
- Check for existing security middleware
|
||||
- Reference OWASP Top 10
|
||||
|
||||
#### `user-flow` Section
|
||||
|
||||
```markdown
|
||||
## 🎨 User Experience & Flows
|
||||
|
||||
### Primary User Flow
|
||||
|
||||
```
|
||||
1. User lands on [screen]
|
||||
↓
|
||||
2. User taps [action button]
|
||||
↓
|
||||
3. System shows [loading state]
|
||||
↓
|
||||
4. System validates [input]
|
||||
↓
|
||||
5. System processes [action]
|
||||
↓
|
||||
6. User sees [success state]
|
||||
```
|
||||
|
||||
### Wireframes / UI Mockups
|
||||
|
||||
**Screen 1: [Screen Name]**
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ ◀ [Screen Title] ⋮ │
|
||||
├─────────────────────────┤
|
||||
│ │
|
||||
│ [Main Content Area] │
|
||||
│ │
|
||||
│ [Interactive Element] │
|
||||
│ │
|
||||
│ [Action Button] │
|
||||
│ │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
### Edge Cases & Error States
|
||||
|
||||
**Error Case 1: Network Failure**
|
||||
- Show: "Unable to connect. Please try again."
|
||||
- Action: Retry button
|
||||
|
||||
**Error Case 2: Invalid Input**
|
||||
- Show: Inline validation error
|
||||
- Action: Highlight field, show help text
|
||||
|
||||
### Loading States
|
||||
|
||||
- Initial load: Skeleton screen
|
||||
- Pull-to-refresh: Pull indicator
|
||||
- Button action: Button shows spinner
|
||||
|
||||
### Accessibility
|
||||
|
||||
- Screen reader labels for all interactive elements
|
||||
- Minimum touch target size: 44x44 points
|
||||
- Color contrast ratio: 4.5:1 minimum
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Create step-by-step flows for all user journeys
|
||||
- Draw ASCII wireframes for key screens
|
||||
- Include error handling UX
|
||||
- Reference accessibility guidelines (WCAG)
|
||||
|
||||
#### `timeline` Section
|
||||
|
||||
```markdown
|
||||
## 📅 Timeline & Estimation
|
||||
|
||||
### Task Breakdown with Estimates
|
||||
|
||||
| Task | Description | Est. Time | Dependencies |
|
||||
|------|-------------|-----------|--------------|
|
||||
| 1. Database Schema | Create tables and migrations | 2h | None |
|
||||
| 2. API Endpoints | Implement backend logic | 4h | Task 1 |
|
||||
| 3. Frontend UI | Build components | 6h | Task 2 |
|
||||
| 4. Integration | Connect frontend to backend | 2h | Task 3 |
|
||||
| 5. Testing | Write and run tests | 4h | Task 4 |
|
||||
| 6. Review & Polish | Code review, bug fixes | 2h | Task 5 |
|
||||
|
||||
**Total Estimate**: 20 hours (~2-3 days)
|
||||
|
||||
### Milestones
|
||||
|
||||
| Milestone | Date | Deliverable |
|
||||
|-----------|------|-------------|
|
||||
| Spec Complete | [+1 day] | This document approved |
|
||||
| Development Start | [+2 days] | Tasks 1-2 complete |
|
||||
| Alpha Ready | [+4 days] | Tasks 1-4 complete |
|
||||
| Testing Complete | [+6 days] | All tasks done |
|
||||
| Production Release | [+7 days] | Feature live |
|
||||
|
||||
### Risk Buffer
|
||||
|
||||
- Add 20% buffer for unforeseen issues: **+4 hours**
|
||||
- **Total with buffer**: 24 hours (~3 days)
|
||||
|
||||
### Critical Path
|
||||
|
||||
```
|
||||
Database → API → Frontend → Integration → Testing
|
||||
```
|
||||
|
||||
Tasks 1-2-3-4 are sequential (critical path).
|
||||
Task 5 (Testing) can partially overlap with Task 4.
|
||||
```
|
||||
|
||||
**AI Instructions:**
|
||||
- Break down requirements into tasks
|
||||
- Estimate based on similar features (search codebase)
|
||||
- Identify dependencies
|
||||
- Add realistic buffer
|
||||
- Create Gantt-style timeline
|
||||
|
||||
#### `all` Section
|
||||
|
||||
**If user requests `all`:**
|
||||
|
||||
1. Show progress indicator
|
||||
2. Write each section sequentially:
|
||||
- requirements → architecture → api-design → data-model → testing → security → user-flow → timeline
|
||||
3. After each section, update document
|
||||
4. Show final summary
|
||||
|
||||
### Step 4: Update Linear Document
|
||||
|
||||
Use **Linear MCP** `update_document`:
|
||||
|
||||
- Append or replace section in document
|
||||
- Update "Last Updated" timestamp
|
||||
- Preserve existing content in other sections
|
||||
|
||||
### Step 5: Display Result
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Spec Section Written: $2
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📄 Document: [DOC-456](https://linear.app/workspace/document/DOC-456)
|
||||
✏️ Section: [$2]
|
||||
📝 Content: [X] lines added
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 6: Interactive Next Actions
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Section written! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Write Another Section",
|
||||
description: "Continue writing other sections"
|
||||
},
|
||||
{
|
||||
label: "Review Spec",
|
||||
description: "Run AI review for completeness (/ccpm:spec:review)"
|
||||
},
|
||||
{
|
||||
label: "View in Linear",
|
||||
description: "Open document in Linear"
|
||||
},
|
||||
{
|
||||
label: "Done",
|
||||
description: "Finish for now"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- AI generates content based on codebase analysis
|
||||
- Uses **Context7 MCP** for library documentation
|
||||
- Searches existing files for patterns and conventions
|
||||
- All generated content follows project style
|
||||
- User can edit in Linear after generation
|
||||
468
commands/sync.md
Normal file
468
commands/sync.md
Normal file
@@ -0,0 +1,468 @@
|
||||
---
|
||||
description: Smart sync command - save progress to Linear (auto-detect task)
|
||||
allowed-tools: [Bash, Task, AskUserQuestion]
|
||||
argument-hint: "[issue-id] [summary]"
|
||||
---
|
||||
|
||||
# /ccpm:sync - Smart Progress Sync
|
||||
|
||||
**Token Budget:** ~2,100 tokens (vs ~6,000 baseline) | **65% reduction**
|
||||
|
||||
Auto-detects issue from git branch and syncs progress to Linear with smart checklist updates.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Auto-detect issue from git branch
|
||||
/ccpm:sync
|
||||
|
||||
# Explicit issue ID
|
||||
/ccpm:sync PSN-29
|
||||
|
||||
# With custom summary
|
||||
/ccpm:sync PSN-29 "Completed auth implementation"
|
||||
|
||||
# Auto-detect with summary
|
||||
/ccpm:sync "Finished UI components"
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Step 1: Parse Arguments & Detect Issue
|
||||
|
||||
```javascript
|
||||
const args = process.argv.slice(2);
|
||||
let issueId = args[0];
|
||||
let summary = args[1];
|
||||
|
||||
// Pattern for issue ID validation
|
||||
const ISSUE_ID_PATTERN = /^[A-Z]+-\d+$/;
|
||||
|
||||
// If first arg looks like summary (not issue ID), treat as summary
|
||||
if (args[0] && !ISSUE_ID_PATTERN.test(args[0])) {
|
||||
summary = args[0];
|
||||
issueId = null;
|
||||
}
|
||||
|
||||
// Auto-detect from git branch if no issue ID
|
||||
if (!issueId) {
|
||||
console.log("🔍 Auto-detecting issue from git branch...");
|
||||
|
||||
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 branch name
|
||||
|
||||
Current branch: ${branch}
|
||||
|
||||
Usage: /ccpm:sync [ISSUE-ID] [summary]
|
||||
|
||||
Examples:
|
||||
/ccpm:sync PSN-29
|
||||
/ccpm:sync PSN-29 "Completed feature X"
|
||||
/ccpm:sync "Made progress on auth"
|
||||
`);
|
||||
}
|
||||
|
||||
issueId = match[1];
|
||||
console.log(`✅ Detected issue: ${issueId}\n`);
|
||||
}
|
||||
|
||||
// Validate issue ID format
|
||||
if (!ISSUE_ID_PATTERN.test(issueId)) {
|
||||
return error(`Invalid issue ID: ${issueId}. Expected format: PROJ-123`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Detect Git Changes
|
||||
|
||||
Run in parallel using Bash:
|
||||
|
||||
```bash
|
||||
# Get all git information in one call
|
||||
git status --porcelain && echo "---" && \
|
||||
git diff --stat HEAD && echo "---" && \
|
||||
git diff --cached --stat && echo "---" && \
|
||||
git rev-parse --abbrev-ref HEAD
|
||||
```
|
||||
|
||||
Parse output to extract:
|
||||
- Changed files (M, A, D, R, ??)
|
||||
- Insertions/deletions per file
|
||||
- Staged vs unstaged changes
|
||||
- Current branch name
|
||||
|
||||
```javascript
|
||||
const changes = {
|
||||
modified: [],
|
||||
added: [],
|
||||
deleted: [],
|
||||
renamed: [],
|
||||
insertions: 0,
|
||||
deletions: 0
|
||||
};
|
||||
|
||||
// Parse git status output
|
||||
// M = modified, A = added, D = deleted, R = renamed, ?? = untracked
|
||||
lines.forEach(line => {
|
||||
const [status, file] = line.trim().split(/\s+/);
|
||||
if (status === 'M') changes.modified.push(file);
|
||||
else if (status === 'A' || status === '??') changes.added.push(file);
|
||||
else if (status === 'D') changes.deleted.push(file);
|
||||
else if (status === 'R') changes.renamed.push(file);
|
||||
});
|
||||
```
|
||||
|
||||
### Step 3: Fetch Issue via Linear Subagent
|
||||
|
||||
**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: "sync"
|
||||
```
|
||||
|
||||
Store response containing:
|
||||
- issue.id, issue.identifier, issue.title
|
||||
- issue.description (with checklist)
|
||||
- issue.state, issue.labels
|
||||
- issue.comments (for last sync timestamp)
|
||||
|
||||
### Step 4: Auto-Generate Summary (if not provided)
|
||||
|
||||
If no summary provided, generate from git changes:
|
||||
|
||||
```javascript
|
||||
if (!summary && changes.modified.length + changes.added.length > 0) {
|
||||
const parts = [];
|
||||
|
||||
if (changes.modified.length > 0) {
|
||||
parts.push(`Updated ${changes.modified.length} file(s)`);
|
||||
}
|
||||
if (changes.added.length > 0) {
|
||||
parts.push(`Added ${changes.added.length} new file(s)`);
|
||||
}
|
||||
if (changes.deleted.length > 0) {
|
||||
parts.push(`Deleted ${changes.deleted.length} file(s)`);
|
||||
}
|
||||
|
||||
summary = parts.join(', ') || 'Work in progress';
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Smart Checklist Analysis (AI-Powered)
|
||||
|
||||
Extract unchecked items from issue description:
|
||||
|
||||
```javascript
|
||||
const checklistItems = issue.description.match(/- \[ \] (.+)/g) || [];
|
||||
const uncheckedItems = checklistItems.map((item, idx) => ({
|
||||
index: idx,
|
||||
text: item.replace('- [ ] ', ''),
|
||||
score: 0
|
||||
}));
|
||||
```
|
||||
|
||||
**Score each item based on git changes:**
|
||||
|
||||
```javascript
|
||||
uncheckedItems.forEach(item => {
|
||||
const keywords = extractKeywords(item.text);
|
||||
|
||||
// File path matching (30 points)
|
||||
changes.modified.concat(changes.added).forEach(file => {
|
||||
if (keywords.some(kw => file.toLowerCase().includes(kw))) {
|
||||
item.score += 30;
|
||||
}
|
||||
});
|
||||
|
||||
// File name exact match (40 points)
|
||||
if (changes.modified.some(f => matchesPattern(f, item.text))) {
|
||||
item.score += 40;
|
||||
}
|
||||
|
||||
// Large changes (10-20 points)
|
||||
const totalLines = changes.insertions + changes.deletions;
|
||||
if (totalLines > 50) item.score += 10;
|
||||
if (totalLines > 100) item.score += 20;
|
||||
});
|
||||
|
||||
// Categorize by confidence
|
||||
const highConfidence = uncheckedItems.filter(i => i.score >= 50);
|
||||
const mediumConfidence = uncheckedItems.filter(i => i.score >= 30 && i.score < 50);
|
||||
```
|
||||
|
||||
### Step 6: Interactive Checklist Update
|
||||
|
||||
Use AskUserQuestion to confirm suggested items:
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "Which checklist items did you complete? (AI suggestions pre-selected)",
|
||||
header: "Completed",
|
||||
multiSelect: true,
|
||||
options: uncheckedItems.map(item => ({
|
||||
label: `${item.index}: ${item.text}`,
|
||||
description: item.score >= 50
|
||||
? "🤖 SUGGESTED - High confidence"
|
||||
: item.score >= 30
|
||||
? "💡 Possible match"
|
||||
: "Mark as complete"
|
||||
}))
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Step 7: Build Progress Report
|
||||
|
||||
```markdown
|
||||
## 🔄 Progress Sync
|
||||
|
||||
**Timestamp**: ${new Date().toISOString()}
|
||||
**Branch**: ${branchName}
|
||||
|
||||
### 📝 Summary
|
||||
${summary}
|
||||
|
||||
### 📊 Code Changes
|
||||
**Files Changed**: ${totalFiles} (+${changes.insertions}, -${changes.deletions})
|
||||
|
||||
**Modified**:
|
||||
${changes.modified.slice(0, 5).map(f => `- ${f}`).join('\n')}
|
||||
${changes.modified.length > 5 ? `\n... and ${changes.modified.length - 5} more` : ''}
|
||||
|
||||
**New Files**:
|
||||
${changes.added.slice(0, 3).map(f => `- ${f}`).join('\n')}
|
||||
|
||||
### 📋 Checklist Updated
|
||||
${completedItems.length > 0 ? `
|
||||
**Completed This Session**:
|
||||
${completedItems.map(i => `- ✅ ${i.text}`).join('\n')}
|
||||
` : 'No checklist updates'}
|
||||
|
||||
---
|
||||
*Synced via /ccpm:sync*
|
||||
```
|
||||
|
||||
### Step 8: Update Linear Issue
|
||||
|
||||
**A) Update checklist in description:**
|
||||
|
||||
**Use the Task tool to update the checklist:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_checklist_items
|
||||
params:
|
||||
issue_id: "{issue ID from step 1}"
|
||||
indices: [{list of completed item indices from step 6}]
|
||||
mark_complete: true
|
||||
add_comment: false # We'll add the full progress report separately
|
||||
update_timestamp: true
|
||||
context:
|
||||
command: "sync"
|
||||
purpose: "Marking completed checklist items based on git changes"
|
||||
```
|
||||
|
||||
**Note**: This operation uses the shared checklist helpers (`_shared-checklist-helpers.md`) for consistent parsing and updating. It will:
|
||||
- Parse the checklist using marker comments or header detection
|
||||
- Update the specified indices (mark as complete)
|
||||
- Recalculate progress percentage
|
||||
- Update the progress line with timestamp
|
||||
- Return structured result with before/after progress
|
||||
|
||||
**B) Add progress comment:**
|
||||
|
||||
**Use the Task tool to add a progress 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: |
|
||||
{the progress report from step 7}
|
||||
context:
|
||||
command: "sync"
|
||||
```
|
||||
|
||||
### Step 9: Display Confirmation & Next Actions
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Progress Synced to Linear!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: ${issueId} - ${issue.title}
|
||||
🔗 ${issue.url}
|
||||
|
||||
📝 Synced:
|
||||
✅ ${totalFiles} files changed
|
||||
✅ ${completedItems.length} checklist items updated
|
||||
💬 Progress comment added
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. ⭐ Continue work
|
||||
2. 📝 Commit changes /ccpm:commit
|
||||
3. ✅ Run verification /ccpm:verify
|
||||
4. 🔍 View status /ccpm:utils:status ${issueId}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Quick Sync Mode (Manual Summary)
|
||||
|
||||
If user provides summary argument, skip interactive mode:
|
||||
|
||||
1. Skip checklist AI analysis
|
||||
2. Skip AskUserQuestion
|
||||
3. Use provided summary directly
|
||||
4. Create simple progress comment
|
||||
5. No automatic checklist updates
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
/ccpm:sync PSN-29 "Completed auth implementation, all tests passing"
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
✅ Quick sync complete!
|
||||
💬 Comment added to Linear
|
||||
📊 Summary: "Completed auth implementation, all tests passing"
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Invalid Issue ID
|
||||
```
|
||||
❌ Invalid issue ID format: proj123
|
||||
Expected format: PROJ-123
|
||||
```
|
||||
|
||||
### No Git Changes
|
||||
```
|
||||
ℹ️ No uncommitted changes detected
|
||||
|
||||
You can still sync progress with a manual summary:
|
||||
/ccpm:sync PSN-29 "Updated documentation"
|
||||
```
|
||||
|
||||
### Branch Detection Failed
|
||||
```
|
||||
❌ Could not detect issue ID from branch
|
||||
|
||||
Current branch: main
|
||||
|
||||
Usage: /ccpm:sync [ISSUE-ID]
|
||||
Example: /ccpm:sync PSN-29
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Auto-detect with changes
|
||||
|
||||
```bash
|
||||
# Branch: feature/PSN-29-add-auth
|
||||
/ccpm:sync
|
||||
|
||||
# Output:
|
||||
# 🔍 Auto-detecting issue from git branch...
|
||||
# ✅ Detected issue: PSN-29
|
||||
#
|
||||
# 📊 Detected Changes:
|
||||
# Modified: 3 files (+127, -45)
|
||||
#
|
||||
# 🤖 AI Suggestions:
|
||||
# ✅ 0: Implement JWT authentication (High confidence)
|
||||
# ✅ 2: Add login form (High confidence)
|
||||
#
|
||||
# [Interactive checklist update...]
|
||||
#
|
||||
# ✅ Progress Synced to Linear!
|
||||
```
|
||||
|
||||
### Example 2: Quick sync with summary
|
||||
|
||||
```bash
|
||||
/ccpm:sync PSN-29 "Finished refactoring auth module"
|
||||
|
||||
# Output:
|
||||
# ✅ Quick sync complete!
|
||||
# 💬 Comment added to Linear
|
||||
```
|
||||
|
||||
### Example 3: Summary-only (auto-detect issue)
|
||||
|
||||
```bash
|
||||
# Branch: feature/PSN-29-add-auth
|
||||
/ccpm:sync "Completed UI components, tests passing"
|
||||
|
||||
# Output:
|
||||
# ✅ Detected issue: PSN-29
|
||||
# ✅ Quick sync complete!
|
||||
```
|
||||
|
||||
## Token Budget Breakdown
|
||||
|
||||
| Section | Tokens | Notes |
|
||||
|---------|--------|-------|
|
||||
| Frontmatter & description | 80 | Minimal metadata |
|
||||
| Step 1: Argument parsing | 250 | Git detection + validation |
|
||||
| Step 2: Git changes | 200 | Parallel bash execution |
|
||||
| Step 3: Fetch issue | 150 | Linear subagent (cached) |
|
||||
| Step 4: Auto-summary | 100 | Simple generation logic |
|
||||
| Step 5: AI checklist analysis | 300 | Scoring algorithm |
|
||||
| Step 6: Interactive update | 200 | AskUserQuestion |
|
||||
| Step 7: Build report | 200 | Markdown generation |
|
||||
| Step 8: Update Linear | 200 | Subagent batch operations |
|
||||
| Step 9: Confirmation | 150 | Next actions menu |
|
||||
| Quick sync mode | 100 | Manual summary path |
|
||||
| Error handling | 100 | 4 scenarios |
|
||||
| Examples | 270 | 3 concise examples |
|
||||
| **Total** | **~2,100** | **vs ~6,000 baseline (65% reduction)** |
|
||||
|
||||
## Key Optimizations
|
||||
|
||||
1. ✅ **Linear subagent** - All Linear ops cached (85-95% hit rate)
|
||||
2. ✅ **Parallel git operations** - Single bash call for all git info
|
||||
3. ✅ **No routing overhead** - Direct implementation (no /ccpm:implementation:sync call)
|
||||
4. ✅ **Smart defaults** - Auto-generates summary from changes
|
||||
5. ✅ **Quick sync mode** - Skip interactions when summary provided
|
||||
6. ✅ **Batch updates** - Single subagent call for description + comment
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
- **During work** → Use `/ccpm:sync` to save progress
|
||||
- **After sync** → Use `/ccpm:commit` for git commits
|
||||
- **Before completion** → Use `/ccpm:verify` for quality checks
|
||||
- **Resume work** → Use `/ccpm:work` to continue
|
||||
|
||||
## Notes
|
||||
|
||||
- **Git detection**: Extracts issue ID from branch names like `feature/PSN-29-add-auth`
|
||||
- **AI suggestions**: Analyzes git changes to pre-select completed checklist items
|
||||
- **Caching**: Linear subagent caches issue data for faster operations
|
||||
- **Flexible**: Works with or without arguments, adapts to context
|
||||
266
commands/utils:agents.md
Normal file
266
commands/utils:agents.md
Normal file
@@ -0,0 +1,266 @@
|
||||
---
|
||||
description: List available subagents and their capabilities from CLAUDE.md
|
||||
allowed-tools: []
|
||||
---
|
||||
|
||||
# Available Subagents
|
||||
|
||||
Reading subagent definitions from **CLAUDE.md** in project root...
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **READ-ONLY** operations are permitted
|
||||
- ⛔ **WRITE operations** require user confirmation
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
|
||||
## Subagents Overview
|
||||
|
||||
Display all subagents defined in CLAUDE.md with their:
|
||||
- Name
|
||||
- Role/Purpose
|
||||
- Capabilities
|
||||
- Best use cases
|
||||
- Example invocation patterns
|
||||
|
||||
## Expected Format
|
||||
|
||||
```
|
||||
🤖 Available Subagents
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 1. frontend-agent
|
||||
|
||||
**Role**: Frontend development specialist
|
||||
|
||||
**Capabilities**:
|
||||
- React/Vue/Angular component development
|
||||
- UI/UX implementation
|
||||
- CSS/Tailwind/styled-components styling
|
||||
- State management
|
||||
- Component architecture
|
||||
- Frontend testing
|
||||
|
||||
**Use For**:
|
||||
- UI components and features
|
||||
- Styling and layout tasks
|
||||
- Client-side logic
|
||||
- Form handling
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke frontend-agent to implement the login form with email/password inputs, validation, and error handling. Follow patterns in /src/components."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 2. backend-agent
|
||||
|
||||
**Role**: Backend development specialist
|
||||
|
||||
**Capabilities**:
|
||||
- RESTful/GraphQL API development
|
||||
- Database operations
|
||||
- Authentication/Authorization
|
||||
- Server-side business logic
|
||||
- API integrations
|
||||
- Backend testing
|
||||
|
||||
**Use For**:
|
||||
- API endpoints
|
||||
- Database logic
|
||||
- Authentication
|
||||
- Server middleware
|
||||
- Background jobs
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke backend-agent to implement JWT authentication endpoints: POST /api/auth/login, /logout, /refresh. Include rate limiting and follow patterns in /src/api."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 3. mobile-agent
|
||||
|
||||
**Role**: Mobile development specialist
|
||||
|
||||
**Capabilities**:
|
||||
- React Native development
|
||||
- iOS-specific features
|
||||
- Android-specific features
|
||||
- Mobile UI patterns
|
||||
- Device-specific functionality
|
||||
- Mobile testing
|
||||
|
||||
**Use For**:
|
||||
- React Native components
|
||||
- Platform-specific code
|
||||
- Native module integration
|
||||
- Mobile app configuration
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke mobile-agent to implement push notifications with Firebase Cloud Messaging. Handle permissions for both iOS and Android."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 4. integration-agent
|
||||
|
||||
**Role**: System integration specialist
|
||||
|
||||
**Capabilities**:
|
||||
- Third-party API integration
|
||||
- Webhook implementation
|
||||
- Data synchronization
|
||||
- API client implementation
|
||||
- OAuth flows
|
||||
- Integration testing
|
||||
|
||||
**Use For**:
|
||||
- External service connections
|
||||
- API clients
|
||||
- Data sync logic
|
||||
- Webhook handlers
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke integration-agent to integrate Stripe payments. Implement checkout flow, webhook handlers for payment events, and refund functionality."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 5. verification-agent
|
||||
|
||||
**Role**: Quality assurance specialist
|
||||
|
||||
**Capabilities**:
|
||||
- Code review
|
||||
- Comprehensive testing
|
||||
- Regression testing
|
||||
- Requirements validation
|
||||
- Security audit
|
||||
- Performance testing
|
||||
|
||||
**Use For**:
|
||||
- Final verification
|
||||
- Requirements validation
|
||||
- Regression checking
|
||||
- Quality gates
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke verification-agent to verify authentication implementation. Review against requirements, run all tests, check for security issues and regressions."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 6. devops-agent
|
||||
|
||||
**Role**: DevOps specialist
|
||||
|
||||
**Capabilities**:
|
||||
- CI/CD configuration
|
||||
- Deployment automation
|
||||
- Docker containerization
|
||||
- Environment configuration
|
||||
- Build optimization
|
||||
- Infrastructure as Code
|
||||
|
||||
**Use For**:
|
||||
- CI/CD tasks
|
||||
- Deployment scripts
|
||||
- Environment setup
|
||||
- Infrastructure changes
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke devops-agent to set up staging deployment with Docker compose, environment variables, and CI/CD pipeline for auto-deploy."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
### 7. database-agent
|
||||
|
||||
**Role**: Database specialist
|
||||
|
||||
**Capabilities**:
|
||||
- Database schema design
|
||||
- Query optimization
|
||||
- Migration management
|
||||
- Index optimization
|
||||
- Database performance tuning
|
||||
- Data modeling
|
||||
|
||||
**Use For**:
|
||||
- Schema changes
|
||||
- Complex queries
|
||||
- Migration scripts
|
||||
- Performance optimization
|
||||
|
||||
**Example Invocation**:
|
||||
"Invoke database-agent to optimize user queries. Analyze slow queries, add indexes, rewrite N+1 queries, and create migration."
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Agent definitions should be in **CLAUDE.md** in project root
|
||||
- Customize agents based on your tech stack
|
||||
- Add more specialized agents as needed (e.g., ml-agent, data-agent)
|
||||
- Keep CLAUDE.md updated with project patterns
|
||||
|
||||
## If CLAUDE.md Doesn't Exist
|
||||
|
||||
If CLAUDE.md is not found, display:
|
||||
|
||||
```
|
||||
⚠️ CLAUDE.md not found in project root!
|
||||
|
||||
Create a CLAUDE.md file to define your subagents.
|
||||
|
||||
Example structure:
|
||||
|
||||
# CLAUDE.md
|
||||
|
||||
## Subagent Definitions
|
||||
|
||||
### frontend-agent
|
||||
**Role**: Frontend development
|
||||
**Capabilities**: React, UI/UX, styling
|
||||
**Use for**: UI components, frontend features
|
||||
|
||||
### backend-agent
|
||||
**Role**: Backend development
|
||||
**Capabilities**: APIs, database, auth
|
||||
**Use for**: Server logic, endpoints
|
||||
|
||||
[Add more agents as needed]
|
||||
```
|
||||
|
||||
## Using Agents
|
||||
|
||||
When you know which agent you need:
|
||||
|
||||
1. **Invoke with full context**:
|
||||
- Task description
|
||||
- Specific requirements
|
||||
- Files to modify
|
||||
- Patterns to follow
|
||||
|
||||
2. **Provide clear success criteria**:
|
||||
- What "done" looks like
|
||||
- Testing requirements
|
||||
- Quality standards
|
||||
|
||||
3. **Update after completion**:
|
||||
- Use `/update` command
|
||||
- Add summary of work
|
||||
|
||||
## Agent Selection Tips
|
||||
|
||||
**Question yourself**:
|
||||
- What type of work needs to be done?
|
||||
- Which domain does it fall under?
|
||||
- Are there dependencies between tasks?
|
||||
- Can tasks run in parallel?
|
||||
|
||||
**Match task to agent**:
|
||||
- UI work → frontend-agent
|
||||
- API work → backend-agent
|
||||
- Integration → integration-agent
|
||||
- Testing → verification-agent
|
||||
- Deployment → devops-agent
|
||||
- Database → database-agent
|
||||
74
commands/utils:auto-assign.md
Normal file
74
commands/utils:auto-assign.md
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
description: AI-powered agent assignment based on subtask analysis
|
||||
allowed-tools: [LinearMCP, Read]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Auto-Assigning Agents for: $1
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Checklist
|
||||
- Get all subtasks from Linear
|
||||
|
||||
### Step 2: Analyze Each Subtask
|
||||
For each subtask:
|
||||
```javascript
|
||||
const analysis = {
|
||||
keywords: extractKeywords(subtask.description),
|
||||
type: detectType(keywords), // backend, frontend, database, etc.
|
||||
suggestedAgent: mapTypeToAgent(type),
|
||||
dependencies: extractDependencies(subtask.description),
|
||||
canRunParallel: checkParallelPossibility(subtask, allSubtasks)
|
||||
}
|
||||
```
|
||||
|
||||
Agent mapping:
|
||||
- API/endpoint keywords → backend-agent
|
||||
- UI/component keywords → frontend-agent
|
||||
- Database/schema keywords → database-agent
|
||||
- Integration/3rd-party → integration-agent
|
||||
- Testing keywords → verification-agent
|
||||
- CI/CD/deploy keywords → devops-agent
|
||||
|
||||
### Step 3: Create Execution Plan
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🤖 AI Agent Assignment Plan
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Sequential Groups:
|
||||
|
||||
Group 1 (Run First):
|
||||
- Subtask 1: [desc] → database-agent
|
||||
Reason: Database schema must be created first
|
||||
|
||||
Group 2 (After Group 1):
|
||||
- Subtask 2: [desc] → backend-agent
|
||||
Reason: Depends on database schema
|
||||
|
||||
Group 3 (Parallel, after Group 2):
|
||||
- Subtask 3: [desc] → frontend-agent (parallel)
|
||||
- Subtask 4: [desc] → mobile-agent (parallel)
|
||||
Reason: Both depend on API, can run simultaneously
|
||||
|
||||
Group 4 (After Group 3):
|
||||
- Subtask 5: [desc] → verification-agent
|
||||
Reason: Final testing after all implementation
|
||||
|
||||
Estimated Time: [X] hours (sequential) vs [Y] hours (with parallelization)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Confirm Plan
|
||||
Use **AskUserQuestion** to approve or modify plan
|
||||
|
||||
### Step 5: Add to Linear
|
||||
- Add comment with assignment plan
|
||||
- Update labels if needed
|
||||
- Show next action (/ccpm:implementation:start)
|
||||
|
||||
## Notes
|
||||
- Detects parallelization opportunities
|
||||
- Respects dependencies automatically
|
||||
- Suggests optimal execution order
|
||||
570
commands/utils:cheatsheet.md
Normal file
570
commands/utils:cheatsheet.md
Normal file
@@ -0,0 +1,570 @@
|
||||
---
|
||||
description: Visual workflow cheatsheet - Quick reference for all CCPM commands
|
||||
allowed-tools: []
|
||||
---
|
||||
|
||||
# CCPM Workflow Cheatsheet
|
||||
|
||||
**Quick visual reference for CCPM commands and workflows.**
|
||||
|
||||
```assistant
|
||||
I'll show you a comprehensive CCPM workflow cheatsheet with visual diagrams.
|
||||
|
||||
---
|
||||
|
||||
# 🗺️ CCPM Workflow Cheatsheet
|
||||
|
||||
## 📊 Quick Command Finder
|
||||
|
||||
**What do you want to do?**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Starting a new project/feature? │
|
||||
│ → Spec-First Workflow (Section 1) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Working on a small task? │
|
||||
│ → Task-First Workflow (Section 2) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Have existing markdown specs? │
|
||||
│ → /ccpm:spec:migrate │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Resuming work on a task? │
|
||||
│ → /ccpm:utils:context <issue-id> │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Need project overview? │
|
||||
│ → /ccpm:utils:report <project> │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Stuck or need help? │
|
||||
│ → /ccpm:utils:help [issue-id] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ Spec-First Workflow (Recommended for Features)
|
||||
|
||||
**Use when:** Building new features, major projects, or anything requiring detailed planning
|
||||
|
||||
```
|
||||
START
|
||||
│
|
||||
├─► 📋 Create Epic/Feature
|
||||
│ /ccpm:spec:create epic "User Authentication"
|
||||
│ /ccpm:spec:create feature "JWT Auth" EPIC-123
|
||||
│
|
||||
├─► ✍️ Write Comprehensive Spec
|
||||
│ /ccpm:spec:write DOC-123 all
|
||||
│ OR write section by section:
|
||||
│ /ccpm:spec:write DOC-123 requirements
|
||||
│ /ccpm:spec:write DOC-123 architecture
|
||||
│ /ccpm:spec:write DOC-123 api-design
|
||||
│ /ccpm:spec:write DOC-123 data-model
|
||||
│ /ccpm:spec:write DOC-123 testing
|
||||
│ /ccpm:spec:write DOC-123 security
|
||||
│
|
||||
├─► 🔍 Review & Validate Spec
|
||||
│ /ccpm:spec:review DOC-123
|
||||
│ (AI grades A-F, suggests improvements)
|
||||
│
|
||||
├─► 📦 Break Down into Tasks
|
||||
│ /ccpm:spec:break-down WORK-100
|
||||
│ (Creates Features from Epic, or Tasks from Feature)
|
||||
│
|
||||
├─► 🚀 Implementation Phase
|
||||
│ [Continue with Task-First Workflow below]
|
||||
│
|
||||
└─► 🔄 Keep Spec in Sync
|
||||
/ccpm:spec:sync WORK-100
|
||||
(Run periodically during implementation)
|
||||
```
|
||||
|
||||
**Spec Sections Available:**
|
||||
- `requirements` - Functional, non-functional, acceptance criteria
|
||||
- `architecture` - System design, component breakdown
|
||||
- `api-design` - Endpoints, request/response schemas
|
||||
- `data-model` - Database schema, TypeScript types
|
||||
- `testing` - Test strategies (unit, integration, E2E)
|
||||
- `security` - Auth, validation, rate limiting
|
||||
- `user-flow` - User journeys, wireframes
|
||||
- `timeline` - Task breakdown, estimates
|
||||
- `all` - Write all sections sequentially
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ Task-First Workflow (Quick Implementation)
|
||||
|
||||
**Use when:** Small tasks, bug fixes, or quick features
|
||||
|
||||
```
|
||||
START
|
||||
│
|
||||
├─► 📝 Create Task + Plan
|
||||
│ /ccpm:planning:create "Add dark mode" my-project JIRA-123
|
||||
│ OR (without external PM):
|
||||
│ /ccpm:planning:quick-plan "Add dark mode" my-project
|
||||
│
|
||||
├─► 🎯 Get AI Insights (Optional)
|
||||
│ /ccpm:utils:insights WORK-200
|
||||
│ (Complexity scoring, risk analysis)
|
||||
│
|
||||
├─► 🤖 Auto-Assign Agents (Optional)
|
||||
│ /ccpm:utils:auto-assign WORK-200
|
||||
│ (AI suggests optimal agent for each subtask)
|
||||
│
|
||||
├─► 🚀 Start Implementation
|
||||
│ /ccpm:implementation:start WORK-200
|
||||
│ (Agent coordination begins)
|
||||
│
|
||||
├─► 🔄 Work on Subtasks
|
||||
│ /ccpm:implementation:next WORK-200
|
||||
│ (AI suggests optimal next subtask)
|
||||
│
|
||||
│ Update progress:
|
||||
│ /ccpm:implementation:update WORK-200 0 completed "Done"
|
||||
│
|
||||
│ Save progress & findings:
|
||||
│ /ccpm:implementation:sync WORK-200
|
||||
│
|
||||
├─► ✅ Quality Checks
|
||||
│ /ccpm:verification:check WORK-200
|
||||
│ (Lint, tests, IDE warnings)
|
||||
│
|
||||
├─► 🔍 Final Verification
|
||||
│ /ccpm:verification:verify WORK-200
|
||||
│ (Comprehensive review by verification-agent)
|
||||
│
|
||||
│ If failures:
|
||||
│ /ccpm:verification:fix WORK-200
|
||||
│
|
||||
└─► 🎉 Finalize & Complete
|
||||
/ccpm:complete:finalize WORK-200
|
||||
(PR creation, Jira sync, Slack notification)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ Planning Phase Commands
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Create new task + plan in one step │
|
||||
│ /ccpm:planning:create "<title>" <project> [jira-id] │
|
||||
│ │
|
||||
│ Plan existing Linear issue │
|
||||
│ /ccpm:planning:plan <issue-id> [jira-id] │
|
||||
│ │
|
||||
│ Quick planning (no external PM) │
|
||||
│ /ccpm:planning:quick-plan "<desc>" <project> │
|
||||
│ │
|
||||
│ Update existing plan (interactive clarification) │
|
||||
│ /ccpm:planning:update <issue-id> "<update-request>" │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Update Plan Examples:**
|
||||
```bash
|
||||
/ccpm:planning:update WORK-123 "Also add email notifications"
|
||||
/ccpm:planning:update WORK-456 "Use Redis instead of cache"
|
||||
/ccpm:planning:update WORK-789 "Remove admin panel"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ Implementation Phase Commands
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Start implementation with agent coordination │
|
||||
│ /ccpm:implementation:start <issue-id> │
|
||||
│ │
|
||||
│ Smart next action detection │
|
||||
│ /ccpm:implementation:next <issue-id> │
|
||||
│ │
|
||||
│ Update subtask progress │
|
||||
│ /ccpm:implementation:update <id> <idx> <status> "<msg>" │
|
||||
│ │
|
||||
│ Sync progress & findings to Linear │
|
||||
│ /ccpm:implementation:sync <issue-id> [summary] │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Status values:** `pending` | `in_progress` | `completed` | `blocked`
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ Verification Phase Commands
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Run quality checks (lint, tests, IDE warnings) │
|
||||
│ /ccpm:verification:check <issue-id> │
|
||||
│ │
|
||||
│ Final comprehensive verification │
|
||||
│ /ccpm:verification:verify <issue-id> │
|
||||
│ │
|
||||
│ Fix verification failures │
|
||||
│ /ccpm:verification:fix <issue-id> │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ Completion Phase Commands
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Post-completion workflow (PR, Jira sync, Slack) │
|
||||
│ /ccpm:complete:finalize <issue-id> │
|
||||
│ │
|
||||
│ Final spec sync (recommended) │
|
||||
│ /ccpm:spec:sync <issue-id> │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7️⃣ Utility Commands (Available Anytime)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 📊 Status & Context │
|
||||
│ /ccpm:utils:status <issue-id> - Detailed task status │
|
||||
│ /ccpm:utils:context <issue-id> - Load full context │
|
||||
│ /ccpm:utils:report <project> - Project-wide report │
|
||||
│ │
|
||||
│ 🤖 AI-Powered Analysis │
|
||||
│ /ccpm:utils:insights <issue-id> - Complexity & risk │
|
||||
│ /ccpm:utils:auto-assign <id> - Optimal agent assign │
|
||||
│ │
|
||||
│ 🔗 Dependencies & Structure │
|
||||
│ /ccpm:utils:dependencies <id> - Visualize dependencies │
|
||||
│ /ccpm:utils:agents - List available agents │
|
||||
│ │
|
||||
│ 🔄 Sync & History │
|
||||
│ /ccpm:utils:sync-status <id> - Sync to Jira │
|
||||
│ /ccpm:utils:rollback <issue-id> - Rollback to previous │
|
||||
│ │
|
||||
│ ❓ Help │
|
||||
│ /ccpm:utils:help - General help │
|
||||
│ /ccpm:utils:help <issue-id> - Context-aware help │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8️⃣ Spec Management Commands
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 📋 Create & Structure │
|
||||
│ /ccpm:spec:create epic "<title>" │
|
||||
│ /ccpm:spec:create feature "<title>" [parent-id] │
|
||||
│ │
|
||||
│ ✍️ Write Spec Content │
|
||||
│ /ccpm:spec:write <doc-id> <section> │
|
||||
│ Sections: requirements, architecture, api-design, │
|
||||
│ data-model, testing, security, user-flow, │
|
||||
│ timeline, all │
|
||||
│ │
|
||||
│ 🔍 Review & Break Down │
|
||||
│ /ccpm:spec:review <doc-id> - AI validation │
|
||||
│ /ccpm:spec:break-down <id> - Generate tasks │
|
||||
│ │
|
||||
│ 🔄 Sync & Migration │
|
||||
│ /ccpm:spec:sync <id> - Sync with code │
|
||||
│ /ccpm:spec:migrate <path> - Migrate from markdown │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI Design Workflow (New)
|
||||
|
||||
**Use when:** Designing user interfaces
|
||||
|
||||
```
|
||||
START
|
||||
│
|
||||
├─► 🎨 Design UI
|
||||
│ /ccpm:planning:design-ui WORK-300
|
||||
│ (AI generates comprehensive UI design)
|
||||
│
|
||||
├─► 🔄 Refine Design (if needed)
|
||||
│ /ccpm:planning:design-refine WORK-300 "<refinement>"
|
||||
│
|
||||
├─► ✅ Approve Design
|
||||
│ /ccpm:planning:design-approve WORK-300
|
||||
│ (Marks design as approved, ready for implementation)
|
||||
│
|
||||
└─► 🚀 Continue with Implementation
|
||||
/ccpm:implementation:start WORK-300
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Common Workflow Patterns
|
||||
|
||||
### Pattern A: Feature Development (Complete)
|
||||
```bash
|
||||
# 1. Create epic with spec
|
||||
/ccpm:spec:create epic "User Authentication System"
|
||||
|
||||
# 2. Write comprehensive spec
|
||||
/ccpm:spec:write DOC-100 all
|
||||
|
||||
# 3. Review spec
|
||||
/ccpm:spec:review DOC-100
|
||||
|
||||
# 4. Break down into features
|
||||
/ccpm:spec:break-down WORK-100
|
||||
|
||||
# 5. For each feature, write detailed spec
|
||||
/ccpm:spec:write DOC-101 all
|
||||
|
||||
# 6. Break feature into tasks
|
||||
/ccpm:spec:break-down WORK-101
|
||||
|
||||
# 7. Implement each task
|
||||
/ccpm:implementation:start WORK-201
|
||||
/ccpm:verification:check WORK-201
|
||||
/ccpm:verification:verify WORK-201
|
||||
/ccpm:complete:finalize WORK-201
|
||||
|
||||
# 8. Keep spec in sync throughout
|
||||
/ccpm:spec:sync WORK-101
|
||||
```
|
||||
|
||||
### Pattern B: Quick Task (Express)
|
||||
```bash
|
||||
# 1. Create + plan
|
||||
/ccpm:planning:create "Add dark mode" my-project
|
||||
|
||||
# 2. Implement
|
||||
/ccpm:implementation:start WORK-300
|
||||
|
||||
# 3. Verify & complete
|
||||
/ccpm:verification:check WORK-300
|
||||
/ccpm:verification:verify WORK-300
|
||||
/ccpm:complete:finalize WORK-300
|
||||
```
|
||||
|
||||
### Pattern C: Resume Work
|
||||
```bash
|
||||
# Morning: Check project status
|
||||
/ccpm:utils:report my-project
|
||||
|
||||
# Load task context
|
||||
/ccpm:utils:context WORK-150
|
||||
|
||||
# Continue where you left off
|
||||
/ccpm:implementation:next WORK-150
|
||||
|
||||
# End of day: Save progress
|
||||
/ccpm:implementation:sync WORK-150
|
||||
```
|
||||
|
||||
### Pattern D: Migration + Spec Workflow
|
||||
```bash
|
||||
# 1. Migrate existing specs
|
||||
/ccpm:spec:migrate ~/personal/my-project
|
||||
|
||||
# 2. Review migrated items
|
||||
/ccpm:utils:report my-project
|
||||
|
||||
# 3. Sync with codebase
|
||||
/ccpm:spec:sync WORK-102
|
||||
|
||||
# 4. Continue with spec workflow
|
||||
/ccpm:spec:break-down WORK-102
|
||||
/ccpm:implementation:start WORK-201
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Decision Trees
|
||||
|
||||
### "I'm starting something new..."
|
||||
|
||||
```
|
||||
Are you building a major feature or project?
|
||||
├─ YES → Use Spec-First Workflow
|
||||
│ 1. /ccpm:spec:create
|
||||
│ 2. /ccpm:spec:write
|
||||
│ 3. /ccpm:spec:review
|
||||
│ 4. /ccpm:spec:break-down
|
||||
│
|
||||
└─ NO → Use Task-First Workflow
|
||||
1. /ccpm:planning:create
|
||||
2. /ccpm:implementation:start
|
||||
```
|
||||
|
||||
### "I have existing markdown specs..."
|
||||
|
||||
```
|
||||
Do you have markdown files in .claude/?
|
||||
└─ YES → Migrate first
|
||||
1. /ccpm:spec:migrate <path>
|
||||
2. /ccpm:utils:report <project>
|
||||
3. Continue with spec workflow
|
||||
```
|
||||
|
||||
### "I'm stuck on a task..."
|
||||
|
||||
```
|
||||
What's the issue?
|
||||
├─ Don't know what to do next
|
||||
│ → /ccpm:implementation:next <issue-id>
|
||||
│
|
||||
├─ Task seems too complex
|
||||
│ → /ccpm:utils:insights <issue-id>
|
||||
│
|
||||
├─ Not sure which agent to use
|
||||
│ → /ccpm:utils:auto-assign <issue-id>
|
||||
│
|
||||
├─ Need to understand dependencies
|
||||
│ → /ccpm:utils:dependencies <issue-id>
|
||||
│
|
||||
└─ Need context refresh
|
||||
→ /ccpm:utils:context <issue-id>
|
||||
```
|
||||
|
||||
### "I'm ready to finish..."
|
||||
|
||||
```
|
||||
Are all subtasks complete?
|
||||
├─ YES → Run quality checks
|
||||
│ 1. /ccpm:verification:check <issue-id>
|
||||
│ 2. /ccpm:verification:verify <issue-id>
|
||||
│ 3. /ccpm:complete:finalize <issue-id>
|
||||
│
|
||||
└─ NO → Find next action
|
||||
/ccpm:implementation:next <issue-id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Command Syntax Quick Reference
|
||||
|
||||
### Spec Management
|
||||
```bash
|
||||
/ccpm:spec:create epic|feature "<title>" [parent-id]
|
||||
/ccpm:spec:write <doc-id> requirements|architecture|api-design|data-model|testing|security|user-flow|timeline|all
|
||||
/ccpm:spec:review <doc-id>
|
||||
/ccpm:spec:break-down <epic-or-feature-id>
|
||||
/ccpm:spec:migrate <project-path> [category]
|
||||
/ccpm:spec:sync <doc-id-or-issue-id>
|
||||
```
|
||||
|
||||
### Planning
|
||||
```bash
|
||||
/ccpm:planning:create "<title>" <project> [jira-id]
|
||||
/ccpm:planning:plan <issue-id> [jira-id]
|
||||
/ccpm:planning:quick-plan "<description>" <project>
|
||||
/ccpm:planning:update <issue-id> "<update-request>"
|
||||
/ccpm:planning:design-ui <issue-id>
|
||||
/ccpm:planning:design-refine <issue-id> "<refinement>"
|
||||
/ccpm:planning:design-approve <issue-id>
|
||||
```
|
||||
|
||||
### Implementation
|
||||
```bash
|
||||
/ccpm:implementation:start <issue-id>
|
||||
/ccpm:implementation:next <issue-id>
|
||||
/ccpm:implementation:update <issue-id> <idx> <status> "<message>"
|
||||
/ccpm:implementation:sync <issue-id> [summary]
|
||||
```
|
||||
|
||||
### Verification
|
||||
```bash
|
||||
/ccpm:verification:check <issue-id>
|
||||
/ccpm:verification:verify <issue-id>
|
||||
/ccpm:verification:fix <issue-id>
|
||||
```
|
||||
|
||||
### Completion
|
||||
```bash
|
||||
/ccpm:complete:finalize <issue-id>
|
||||
```
|
||||
|
||||
### Utilities
|
||||
```bash
|
||||
/ccpm:utils:status <issue-id>
|
||||
/ccpm:utils:context <issue-id>
|
||||
/ccpm:utils:report <project>
|
||||
/ccpm:utils:insights <issue-id>
|
||||
/ccpm:utils:auto-assign <issue-id>
|
||||
/ccpm:utils:sync-status <issue-id>
|
||||
/ccpm:utils:rollback <issue-id>
|
||||
/ccpm:utils:dependencies <issue-id>
|
||||
/ccpm:utils:agents
|
||||
/ccpm:utils:help [issue-id]
|
||||
/ccpm:utils:cheatsheet
|
||||
```
|
||||
|
||||
### Project-Specific
|
||||
```bash
|
||||
/ccpm:my-project:check-pr <pr-number-or-url>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Pro Tips
|
||||
|
||||
### Workflow Tips
|
||||
- **Interactive Mode**: Every command suggests next actions - trust the flow!
|
||||
- **Context Loading**: Use `/ccpm:utils:context` when resuming work
|
||||
- **Daily Standup**: Start with `/ccpm:utils:report <project>`
|
||||
- **Spec Sync**: Run `/ccpm:spec:sync` periodically during implementation
|
||||
- **Get Help**: Use `/ccpm:utils:help <issue-id>` for context-aware suggestions
|
||||
|
||||
### Efficiency Tips
|
||||
- **Parallel Work**: Use `/ccpm:utils:auto-assign` to identify parallel tasks
|
||||
- **Dependencies**: Check `/ccpm:utils:dependencies` before starting
|
||||
- **Insights Early**: Run `/ccpm:utils:insights` for complex tasks upfront
|
||||
- **Rollback**: Made a mistake? Use `/ccpm:utils:rollback`
|
||||
|
||||
### Quality Tips
|
||||
- **Always check before verify**: `/ccpm:verification:check` catches issues early
|
||||
- **Spec-First for features**: Better estimates, fewer surprises
|
||||
- **Review specs**: `/ccpm:spec:review` ensures completeness before coding
|
||||
- **Keep specs synced**: Drift reports help maintain documentation
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Safety Reminders
|
||||
|
||||
⛔ **Never writes to external systems without confirmation:**
|
||||
- Jira (issues, comments, status changes)
|
||||
- Confluence (pages, edits)
|
||||
- BitBucket (pull requests, comments)
|
||||
- Slack (messages, posts)
|
||||
|
||||
✅ **Always allowed:**
|
||||
- Read operations from external systems
|
||||
- Linear operations (internal tracking)
|
||||
- Local file operations
|
||||
|
||||
📖 Full details: See `SAFETY_RULES.md`
|
||||
|
||||
---
|
||||
|
||||
## 📚 More Help
|
||||
|
||||
- **Comprehensive Guide**: See `README.md` in commands directory
|
||||
- **Context-Aware Help**: `/ccpm:utils:help <issue-id>`
|
||||
- **General Help**: `/ccpm:utils:help`
|
||||
- **Available Agents**: `/ccpm:utils:agents`
|
||||
|
||||
---
|
||||
|
||||
💡 **What would you like to do next?**
|
||||
|
||||
1. ⭐ Start with Pattern A, B, C, or D above
|
||||
2. 🔍 Get help for specific task → `/ccpm:utils:help <issue-id>`
|
||||
3. 📊 Check project status → `/ccpm:utils:report <project>`
|
||||
4. 📋 Load task context → `/ccpm:utils:context <issue-id>`
|
||||
5. 🎯 Find next action → `/ccpm:implementation:next <issue-id>`
|
||||
6. ❓ Ask me anything about CCPM workflow
|
||||
|
||||
```
|
||||
412
commands/utils:context.md
Normal file
412
commands/utils:context.md
Normal file
@@ -0,0 +1,412 @@
|
||||
---
|
||||
description: Load task context quickly - fetch issue, related files, and set up environment
|
||||
allowed-tools: [Bash, LinearMCP, Read, Glob]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Loading Context for: $1
|
||||
|
||||
Quickly loading all context for Linear issue **$1** to help you resume work.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Linear Issue Details
|
||||
|
||||
Use **Linear MCP** to get full issue details:
|
||||
|
||||
1. Title, description, status, labels
|
||||
2. Full checklist with progress
|
||||
3. All comments and activity
|
||||
4. Related issues (parent, sub-issues)
|
||||
5. Assignee, dates, project info
|
||||
|
||||
### Step 1.5: Display Attached Images
|
||||
|
||||
**READ**: `commands/_shared-image-analysis.md`
|
||||
|
||||
If the issue has attached images, display them:
|
||||
|
||||
```javascript
|
||||
const images = detectImages(issue)
|
||||
if (images.length > 0) {
|
||||
console.log("📎 Attached Images (" + images.length + "):")
|
||||
images.forEach((img, i) => {
|
||||
console.log(` ${i+1}. ${img.title} (${img.type.toUpperCase()}) - ${img.url}`)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: Images may contain UI mockups, architecture diagrams, or screenshots that provide visual context for the task.
|
||||
|
||||
|
||||
### Step 2: Extract Context from Description
|
||||
|
||||
Parse the description to extract:
|
||||
|
||||
**Files Mentioned**:
|
||||
|
||||
- Look for code file paths (e.g., `src/api/auth.ts`, `components/Login.tsx`)
|
||||
- Look for file patterns (e.g., `*.test.ts`, `api/**/*.js`)
|
||||
- Extract all file references from implementation plan
|
||||
|
||||
**Related Links**:
|
||||
|
||||
- Jira tickets (extract URLs)
|
||||
- Confluence pages (extract URLs)
|
||||
- Slack threads (extract URLs)
|
||||
- BitBucket PRs (extract URLs)
|
||||
- Linear issues (extract issue IDs)
|
||||
|
||||
**Key Sections**:
|
||||
|
||||
- Current architecture
|
||||
- Implementation approach
|
||||
- Technical constraints
|
||||
- Best practices
|
||||
- Cross-repository dependencies
|
||||
|
||||
### Step 3: Load Relevant Files
|
||||
|
||||
Use **Glob** and **Read** tools to:
|
||||
|
||||
1. Find all mentioned files in the codebase
|
||||
2. Read key files (limited to first 100 lines each)
|
||||
3. Display file summaries
|
||||
|
||||
```
|
||||
📂 Relevant Files Found:
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. src/api/auth.ts (145 lines)
|
||||
Purpose: [Inferred from description or filename]
|
||||
Status: [To be modified/new file/reference only]
|
||||
|
||||
2. src/components/Login.tsx (89 lines)
|
||||
Purpose: [Inferred from description]
|
||||
Status: [To be modified]
|
||||
|
||||
3. src/middleware/jwt.ts (67 lines)
|
||||
Purpose: [Inferred from description]
|
||||
Status: [To be created]
|
||||
|
||||
[... up to 10 most relevant files ...]
|
||||
```
|
||||
|
||||
### Step 4: Analyze Current Progress
|
||||
|
||||
Calculate and display progress:
|
||||
|
||||
```javascript
|
||||
const progress = {
|
||||
total: checklistItems.length,
|
||||
completed: checklistItems.filter(i => i.checked).length,
|
||||
inProgress: checklistItems.filter(i => i.status === 'in_progress').length,
|
||||
blocked: checklistItems.filter(i => i.status === 'blocked').length,
|
||||
remaining: checklistItems.filter(i => !i.checked).length,
|
||||
percentage: Math.round((completed / total) * 100)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Display Complete Context
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Context Loaded: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🏷️ Title: [Full title]
|
||||
📊 Status: [Current status]
|
||||
🎯 Progress: [X/Y] subtasks ([%]%)
|
||||
⏱️ Time in status: [Duration]
|
||||
🏷️ Labels: [Comma-separated labels]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[First paragraph from Context section of description]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Checklist Progress
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Completed ([X]):
|
||||
✅ Subtask 1: [Description]
|
||||
✅ Subtask 2: [Description]
|
||||
|
||||
In Progress ([Y]):
|
||||
⏳ Subtask 3: [Description]
|
||||
|
||||
Remaining ([Z]):
|
||||
⬜ Subtask 4: [Description]
|
||||
⬜ Subtask 5: [Description]
|
||||
|
||||
Blocked ([N]):
|
||||
🚫 Subtask 6: [Description] - [Blocker reason]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📂 Files to Work On
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[List from Step 3 above]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔗 Related Links
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Jira: [TRAIN-123](link)
|
||||
Confluence: [PRD](link), [Design Doc](link)
|
||||
Slack: [Discussion](link)
|
||||
PRs: [PR #789](link)
|
||||
Related Issues: [WORK-456](link)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Implementation Approach
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[Extracted from Implementation Plan section]
|
||||
|
||||
Key Points:
|
||||
- [Point 1]
|
||||
- [Point 2]
|
||||
- [Point 3]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Important Considerations
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Technical Constraints:
|
||||
- [Constraint 1]
|
||||
- [Constraint 2]
|
||||
|
||||
Best Practices:
|
||||
- [Practice 1]
|
||||
- [Practice 2]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💬 Recent Activity
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[Last 3 comments with timestamps and authors]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 6: Interactive Next Actions
|
||||
|
||||
**READ**: ``$CCPM_COMMANDS_DIR/_shared-linear-helpers.md``
|
||||
|
||||
Determine next action based on status and progress:
|
||||
|
||||
```javascript
|
||||
// If status is Planning
|
||||
if (status === 'Planning') {
|
||||
suggestOptions = [
|
||||
"Start Implementation",
|
||||
"Get AI Insights",
|
||||
"Auto-Assign Agents",
|
||||
"Just Review"
|
||||
]
|
||||
}
|
||||
|
||||
// If status is In Progress
|
||||
if (status === 'In Progress') {
|
||||
if (hasIncompleteTask) {
|
||||
suggestOptions = [
|
||||
"Continue Next Task",
|
||||
"Update Progress",
|
||||
"Check Quality (if ready)",
|
||||
"Just Review"
|
||||
]
|
||||
} else {
|
||||
suggestOptions = [
|
||||
"Run Quality Checks",
|
||||
"Update Last Task",
|
||||
"Just Review"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// If status is Verification
|
||||
if (status === 'Verification') {
|
||||
suggestOptions = [
|
||||
"Run Verification",
|
||||
"Check Quality First",
|
||||
"Just Review"
|
||||
]
|
||||
}
|
||||
|
||||
// If blocked
|
||||
if (labels.includes('blocked')) {
|
||||
suggestOptions = [
|
||||
"Fix Issues",
|
||||
"View Status",
|
||||
"Rollback Changes",
|
||||
"Just Review"
|
||||
]
|
||||
}
|
||||
|
||||
// If done
|
||||
if (status === 'Done') {
|
||||
suggestOptions = [
|
||||
"Finalize Task",
|
||||
"Create New Task",
|
||||
"Just Review"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use **AskUserQuestion** tool with detected options.
|
||||
|
||||
**Execute based on choice** or show quick commands and exit.
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Status: /ccpm:utils:status $1
|
||||
Next: /ccpm:implementation:next $1
|
||||
Start: /ccpm:implementation:start $1
|
||||
Update: /ccpm:implementation:update $1 <idx> <status> "msg"
|
||||
Check: /ccpm:verification:check $1
|
||||
Verify: /ccpm:verification:verify $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### When to Use
|
||||
|
||||
- **Starting your work day** - Quick recap of what you're working on
|
||||
- **Switching between tasks** - Fast context switch
|
||||
- **After a break** - Remember where you left off
|
||||
- **Code review** - Understand the full context quickly
|
||||
- **Onboarding** - Get up to speed on a task
|
||||
|
||||
### What It Does
|
||||
|
||||
✅ Fetches full Linear issue
|
||||
✅ Extracts all relevant files
|
||||
✅ Shows progress at a glance
|
||||
✅ Provides related links
|
||||
✅ Displays key implementation points
|
||||
✅ Shows recent activity
|
||||
✅ Suggests next actions
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Load context for any task
|
||||
/ccpm:utils:context WORK-123
|
||||
|
||||
# Quick resume after break
|
||||
/ccpm:utils:context WORK-123
|
||||
|
||||
# Switch to different task
|
||||
/ccpm:utils:context WORK-456
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- ⚡ **Fast** - No manual searching
|
||||
- 🎯 **Focused** - Only relevant information
|
||||
- 🔄 **Resumable** - Easy to pick up where you left off
|
||||
- 📋 **Complete** - All context in one view
|
||||
- 🤖 **Interactive** - Suggests what to do next
|
||||
|
||||
### Step 1.6: Display Figma Design Links
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
If the issue contains Figma design links, display them for easy access:
|
||||
|
||||
```bash
|
||||
# Detect Figma links from Linear issue
|
||||
LINEAR_DESC=$(linear_get_issue "$1" | jq -r '.description')
|
||||
LINEAR_COMMENTS=$(linear_get_issue "$1" | jq -r '.comments[] | .body' || echo "")
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC $LINEAR_COMMENTS")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "🎨 Figma Designs ($FIGMA_COUNT):"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Display each Figma design with details
|
||||
echo "$FIGMA_LINKS" | jq -r '.[] | "\n 📐 \(.file_name)\n 🔗 \(.canonical_url)\n 📍 Node: \(.node_id // "Full file")\n 🎯 Type: \(.type)"'
|
||||
|
||||
# Show quick access command
|
||||
echo ""
|
||||
echo "💡 Quick Access:"
|
||||
echo " • Open in Figma: Click URLs above"
|
||||
echo " • Refresh cache: /ccpm:utils:figma-refresh $1 (Phase 2)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
else
|
||||
echo ""
|
||||
echo "ℹ️ No Figma designs found in this issue"
|
||||
fi
|
||||
```
|
||||
|
||||
**Figma Context Display Format**
|
||||
|
||||
```
|
||||
🎨 Figma Designs (2):
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📐 Login Screen Design
|
||||
🔗 https://www.figma.com/file/ABC123
|
||||
📍 Node: 1-2
|
||||
🎯 Type: file
|
||||
|
||||
📐 Dashboard Mockup
|
||||
🔗 https://www.figma.com/design/XYZ789
|
||||
📍 Node: Full file
|
||||
🎯 Type: design
|
||||
|
||||
💡 Quick Access:
|
||||
• Open in Figma: Click URLs above
|
||||
• Refresh cache: /ccpm:utils:figma-refresh PSN-25 (Phase 2)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**Integration with Image Context**
|
||||
|
||||
Display both images and Figma designs together:
|
||||
|
||||
```javascript
|
||||
// After Step 1.5 (Display Attached Images)
|
||||
// Add Step 1.6 (Display Figma Designs)
|
||||
|
||||
const visualResources = {
|
||||
images: images.length,
|
||||
figma: figmaLinks.length,
|
||||
total: images.length + figmaLinks.length
|
||||
}
|
||||
|
||||
if (visualResources.total > 0) {
|
||||
console.log(`\n📊 Visual Resources Summary: ${visualResources.total} total`)
|
||||
console.log(` • Static Images: ${visualResources.images} (snapshots, mockups)`)
|
||||
console.log(` • Figma Designs: ${visualResources.figma} (live, authoritative)`)
|
||||
console.log(`\n💡 Use Figma as primary source, images for quick reference`)
|
||||
}
|
||||
```
|
||||
|
||||
**Why This Matters**:
|
||||
- **Quick Access**: All design resources visible immediately when loading context
|
||||
- **Context Awareness**: Understand what visual resources are available
|
||||
- **Design Priority**: Figma = authoritative, images = supplementary
|
||||
- **Efficiency**: No need to search through Linear comments for design links
|
||||
|
||||
**Performance**: Figma link detection adds <100ms to context loading.
|
||||
|
||||
63
commands/utils:dependencies.md
Normal file
63
commands/utils:dependencies.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
description: Visualize subtask dependencies and execution order
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Dependencies for: $1
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Parse Dependencies
|
||||
Extract from checklist items:
|
||||
- "depends on: X"
|
||||
- "(after: X)"
|
||||
- "(requires: X)"
|
||||
|
||||
### Step 2: Build Dependency Graph
|
||||
```javascript
|
||||
const graph = checklist.map((task, idx) => ({
|
||||
index: idx,
|
||||
description: task.description,
|
||||
dependencies: extractDependencies(task.description),
|
||||
dependents: findDependents(idx, checklist),
|
||||
status: task.checked ? 'complete' : 'pending',
|
||||
canStart: allDependenciesMet(idx, checklist)
|
||||
}))
|
||||
```
|
||||
|
||||
### Step 3: Visualize
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Dependency Graph for: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Legend: ✅ Complete | ⏳ Ready | ⏸️ Blocked | 📍 Current
|
||||
|
||||
[1] ✅ Database schema
|
||||
↓
|
||||
[2] ✅ API endpoints (depends on: 1)
|
||||
↓
|
||||
├→ [3] ⏳ Frontend (depends on: 2) [READY TO START]
|
||||
└→ [4] ⏳ Mobile (depends on: 2) [READY TO START]
|
||||
↓
|
||||
[5] ⏸️ Tests (depends on: 3, 4) [BLOCKED]
|
||||
|
||||
Execution Order:
|
||||
1. Task 1 (no dependencies)
|
||||
2. Task 2 (after task 1)
|
||||
3. Tasks 3 & 4 in parallel (after task 2)
|
||||
4. Task 5 (after tasks 3 & 4)
|
||||
|
||||
Ready to Start: Tasks 3, 4
|
||||
Blocked: Task 5 (waiting on: 3, 4)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Suggest Next Action
|
||||
Show which task(s) are ready to start
|
||||
|
||||
## Notes
|
||||
- ASCII graph visualization
|
||||
- Shows ready vs blocked
|
||||
- Identifies parallel opportunities
|
||||
206
commands/utils:figma-refresh.md
Executable file
206
commands/utils:figma-refresh.md
Executable file
@@ -0,0 +1,206 @@
|
||||
---
|
||||
description: Force refresh Figma design cache and update Linear with latest data
|
||||
allowed-tools: [Bash, LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Refresh Figma Cache: $1
|
||||
|
||||
Force refresh cached Figma design data for Linear issue **$1**.
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**READ FIRST**: ``$CCPM_COMMANDS_DIR/SAFETY_RULES.md``
|
||||
|
||||
This command is **READ-ONLY** for external systems and **WRITE** to Linear (internal tracking).
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Linear Issue
|
||||
|
||||
Use **Linear MCP** to get issue details for $1:
|
||||
|
||||
```javascript
|
||||
linear_get_issue({ id: "$1" })
|
||||
```
|
||||
|
||||
Extract:
|
||||
- Issue description
|
||||
- Comments (check for cached Figma data)
|
||||
- Project ID
|
||||
|
||||
### Step 2: Detect Figma Links
|
||||
|
||||
**READ**: `commands/_shared-figma-detection.md`
|
||||
|
||||
Extract Figma links from Linear issue:
|
||||
|
||||
```bash
|
||||
LINEAR_DESC=$(linear_get_issue "$1" | jq -r '.description')
|
||||
LINEAR_COMMENTS=$(linear_get_issue "$1" | jq -r '.comments[]? | .body' || echo "")
|
||||
|
||||
FIGMA_LINKS=$(./scripts/figma-utils.sh extract-markdown "$LINEAR_DESC $LINEAR_COMMENTS")
|
||||
FIGMA_COUNT=$(echo "$FIGMA_LINKS" | jq 'length')
|
||||
|
||||
if [ "$FIGMA_COUNT" -eq 0 ]; then
|
||||
echo "❌ No Figma links found in issue $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔍 Found $FIGMA_COUNT Figma design(s)"
|
||||
```
|
||||
|
||||
### Step 3: Check Existing Cache
|
||||
|
||||
For each Figma link, check if cached data exists:
|
||||
|
||||
```bash
|
||||
echo "$FIGMA_LINKS" | jq -c '.[]' | while read -r link; do
|
||||
FILE_ID=$(echo "$link" | jq -r '.file_id')
|
||||
FILE_NAME=$(echo "$link" | jq -r '.file_name')
|
||||
|
||||
CACHE_STATUS=$(./scripts/figma-cache-manager.sh status "$1" "$FILE_ID")
|
||||
|
||||
if [ -n "$CACHE_STATUS" ]; then
|
||||
echo " 📦 Found cache for: $FILE_NAME"
|
||||
CACHE_AGE=$(echo "$CACHE_STATUS" | jq -r '.age_hours')
|
||||
echo " Age: ${CACHE_AGE}h"
|
||||
else
|
||||
echo " ⚠️ No cache for: $FILE_NAME"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### Step 4: Force Refresh from Figma
|
||||
|
||||
For each Figma link:
|
||||
|
||||
1. Select MCP server
|
||||
2. Extract fresh data via MCP
|
||||
3. Analyze design system
|
||||
4. Update cache
|
||||
5. Detect changes
|
||||
|
||||
```bash
|
||||
PROJECT_ID=$(linear_get_issue "$1" | jq -r '.projectId')
|
||||
FIGMA_SERVER=$(./scripts/figma-server-manager.sh select "$PROJECT_ID")
|
||||
|
||||
if [ -z "$FIGMA_SERVER" ]; then
|
||||
echo "❌ No Figma MCP server configured for project"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔄 Refreshing Figma data..."
|
||||
|
||||
echo "$FIGMA_LINKS" | jq -c '.[]' | while read -r link; do
|
||||
FILE_ID=$(echo "$link" | jq -r '.file_id')
|
||||
FILE_NAME=$(echo "$link" | jq -r '.file_name')
|
||||
FILE_URL=$(echo "$link" | jq -r '.url')
|
||||
|
||||
echo ""
|
||||
echo " 📐 Refreshing: $FILE_NAME"
|
||||
|
||||
# Get old cache for comparison
|
||||
OLD_CACHE=$(./scripts/figma-cache-manager.sh get "$1" "$FILE_ID" 2>/dev/null || echo "{}")
|
||||
|
||||
# Generate MCP call
|
||||
MCP_INSTRUCTION=$(./scripts/figma-data-extractor.sh extract "$FILE_ID" "$FIGMA_SERVER")
|
||||
|
||||
# Execute MCP call (Claude should do this)
|
||||
# FIGMA_DATA=$(execute MCP based on MCP_INSTRUCTION)
|
||||
|
||||
# Analyze design system
|
||||
# DESIGN_SYSTEM=$(echo "$FIGMA_DATA" | ./scripts/figma-design-analyzer.sh generate -)
|
||||
|
||||
# Update cache
|
||||
# ./scripts/figma-cache-manager.sh store "$1" "$FILE_ID" "$FILE_NAME" "$FILE_URL" "$FIGMA_SERVER" "$DESIGN_SYSTEM"
|
||||
|
||||
echo " ✅ Cache updated"
|
||||
|
||||
# Detect changes
|
||||
if [ -n "$OLD_CACHE" ] && [ "$OLD_CACHE" != "{}" ]; then
|
||||
# Compare OLD_CACHE with new DESIGN_SYSTEM
|
||||
# Detect color changes, component changes, etc.
|
||||
echo " 🔍 Checking for design changes..."
|
||||
# TODO: Implement change detection
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### Step 5: Update Linear Description
|
||||
|
||||
Update the "🎨 Design System Analysis" section in Linear description with refreshed data:
|
||||
|
||||
```javascript
|
||||
// 1. Format new design system
|
||||
const formattedDesignSystem = formatDesignSystemMarkdown(designSystem, fileName)
|
||||
|
||||
// 2. Update Linear description
|
||||
const updatedDescription = issue.description.replace(
|
||||
/## 🎨 Design System Analysis:.*?(?=##|$)/s,
|
||||
formattedDesignSystem
|
||||
)
|
||||
|
||||
// 3. Save to Linear
|
||||
linear_update_issue({
|
||||
id: "$1",
|
||||
description: updatedDescription
|
||||
})
|
||||
```
|
||||
|
||||
### Step 6: Add Linear Comment
|
||||
|
||||
Add a comment documenting the refresh:
|
||||
|
||||
```markdown
|
||||
## 🔄 Figma Design Refresh
|
||||
|
||||
**Refreshed**: [timestamp]
|
||||
**Files updated**: [count]
|
||||
|
||||
### Changes Detected
|
||||
|
||||
- [File 1]: [change summary]
|
||||
- [File 2]: No changes detected
|
||||
|
||||
### Updated Data
|
||||
|
||||
- Design tokens: ✅ Refreshed
|
||||
- Component library: ✅ Refreshed
|
||||
- Tailwind mappings: ✅ Refreshed
|
||||
|
||||
Cache will expire in 1 hour. Run `/ccpm:utils:figma-refresh $1` to refresh again.
|
||||
```
|
||||
|
||||
Display final summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Figma Cache Refreshed: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Refreshed [X] Figma design(s)
|
||||
Design System: Updated
|
||||
Linear Description: Updated
|
||||
Cache expires: [timestamp]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Refresh Figma cache for a task
|
||||
/ccpm:utils:figma-refresh WORK-123
|
||||
|
||||
# After designer updates Figma
|
||||
/ccpm:utils:figma-refresh WORK-456
|
||||
```
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Fresh data**: Get latest design system updates
|
||||
- **Change detection**: Know what changed since last cache
|
||||
- **Implementation sync**: Keep implementation aligned with latest designs
|
||||
- **Manual control**: Force refresh when needed (vs automatic 1hr expiry)
|
||||
546
commands/utils:help.md
Normal file
546
commands/utils:help.md
Normal file
@@ -0,0 +1,546 @@
|
||||
---
|
||||
description: Context-aware PM commands help and suggestions
|
||||
allowed-tools: [LinearMCP, Read, AskUserQuestion]
|
||||
argument-hint: [issue-id]
|
||||
---
|
||||
|
||||
# PM Commands Help
|
||||
|
||||
## 🚨 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 mode.
|
||||
|
||||
---
|
||||
|
||||
## Argument
|
||||
|
||||
- **$1** - (Optional) Linear issue ID for context-aware suggestions
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Detect Context
|
||||
|
||||
If `$1` (issue ID) is provided:
|
||||
|
||||
```javascript
|
||||
// Get issue details
|
||||
const issue = await getLinearIssue($1)
|
||||
|
||||
// Detect context
|
||||
const context = {
|
||||
type: detectIssueType(issue), // epic, feature, task
|
||||
status: issue.status, // Planning, In Progress, Verification, Done, Blocked
|
||||
hasSpec: hasLinkedSpecDoc(issue),
|
||||
hasSubtasks: issue.children && issue.children.length > 0,
|
||||
progress: calculateProgress(issue),
|
||||
labels: issue.labels
|
||||
}
|
||||
```
|
||||
|
||||
If no issue ID:
|
||||
|
||||
```javascript
|
||||
// Show general help
|
||||
const context = { type: 'general' }
|
||||
```
|
||||
|
||||
### Step 2: Show Command Reference
|
||||
|
||||
Display categorized commands based on context:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📚 PM Commands Help ${$1 ? `- ${issue.title}` : ''}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
${$1 ? `
|
||||
📊 Current Context
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🎯 Issue: ${issue.identifier} - ${issue.title}
|
||||
📋 Type: ${context.type}
|
||||
📈 Status: ${context.status}
|
||||
${context.hasSpec ? '📄 Spec Doc: Linked' : '⚠️ No spec document'}
|
||||
${context.hasSubtasks ? `✅ Subtasks: ${context.progress.completed}/${context.progress.total} (${context.progress.percentage}%)` : ''}
|
||||
🏷️ Labels: ${context.labels.join(', ')}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
` : ''}
|
||||
|
||||
📋 Available Commands by Category
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## 📐 Spec Management
|
||||
|
||||
/ccpm:spec:create <type> "<title>" [parent-id]
|
||||
Create Epic/Feature with Linear Document
|
||||
Types: epic, feature
|
||||
Example: /ccpm:spec:create epic "User Auth System"
|
||||
|
||||
/ccpm:spec:write <doc-id> <section>
|
||||
AI-assisted spec writing
|
||||
Sections: requirements, architecture, api-design, data-model, testing, security, user-flow, timeline, all
|
||||
Example: /ccpm:spec:write DOC-123 requirements
|
||||
|
||||
/ccpm:spec:review <doc-id>
|
||||
Validate spec completeness & quality (A-F grade)
|
||||
Example: /ccpm:spec:review DOC-123
|
||||
|
||||
/ccpm:spec:break-down <epic-or-feature-id>
|
||||
Epic → Features or Feature → Tasks
|
||||
Example: /ccpm:spec:break-down WORK-100
|
||||
|
||||
/ccpm:spec:migrate <project-path> [category]
|
||||
Migrate .claude/ markdown specs to Linear
|
||||
Categories: docs, plans, enhancements, tasks, all
|
||||
Example: /ccpm:spec:migrate ~/personal/personal-project
|
||||
|
||||
/ccpm:spec:sync <doc-id-or-issue-id>
|
||||
Sync spec with implementation (detect drift)
|
||||
Example: /ccpm:spec:sync WORK-123
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## 📝 Planning
|
||||
|
||||
/ccpm:planning:create "<title>" <project> [jira-id]
|
||||
Create + plan Linear issue in one step
|
||||
Projects: my-app, my-project, personal-project
|
||||
Example: /ccpm:planning:create "Add JWT auth" personal-project
|
||||
|
||||
/ccpm:planning:plan <linear-issue-id> [jira-id]
|
||||
Populate existing issue with research
|
||||
Example: /ccpm:planning:plan WORK-123 TRAIN-456
|
||||
|
||||
/ccpm:planning:quick-plan "<description>" <project>
|
||||
Quick planning (no Jira)
|
||||
Example: /ccpm:planning:quick-plan "Add dark mode" personal-project
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## 🔨 Implementation
|
||||
|
||||
/ccpm:implementation:start <linear-issue-id>
|
||||
Start with agent coordination
|
||||
Example: /ccpm:implementation:start WORK-123
|
||||
|
||||
/ccpm:implementation:next <linear-issue-id>
|
||||
Smart next action detection
|
||||
Example: /ccpm:implementation:next WORK-123
|
||||
|
||||
/ccpm:implementation:update <id> <idx> <status> "<msg>"
|
||||
Update subtask status
|
||||
Statuses: completed, in-progress, blocked
|
||||
Example: /ccpm:implementation:update WORK-123 0 completed "Done"
|
||||
|
||||
/ccpm:implementation:sync <linear-issue-id> [summary]
|
||||
Sync progress, findings & code changes to Linear
|
||||
Auto-detects git changes, prompts for notes
|
||||
Example: /ccpm:implementation:sync WORK-123
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## ✅ Verification
|
||||
|
||||
/ccpm:verification:check <linear-issue-id>
|
||||
Run quality checks (IDE, linting, tests)
|
||||
Example: /ccpm:verification:check WORK-123
|
||||
|
||||
/ccpm:verification:verify <linear-issue-id>
|
||||
Final verification with verification-agent
|
||||
Example: /ccpm:verification:verify WORK-123
|
||||
|
||||
/ccpm:verification:fix <linear-issue-id>
|
||||
Fix verification failures
|
||||
Example: /ccpm:verification:fix WORK-123
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## 🎉 Completion
|
||||
|
||||
/ccpm:complete:finalize <linear-issue-id>
|
||||
Post-completion (PR + Jira sync + Slack)
|
||||
Example: /ccpm:complete:finalize WORK-123
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
## 🛠️ Utilities
|
||||
|
||||
/ccpm:utils:status <linear-issue-id>
|
||||
Show detailed task status
|
||||
Example: /ccpm:utils:status WORK-123
|
||||
|
||||
/ccpm:utils:context <linear-issue-id>
|
||||
Fast task context loading
|
||||
Example: /ccpm:utils:context WORK-123
|
||||
|
||||
/ccpm:utils:report <project>
|
||||
Project-wide progress report
|
||||
Example: /ccpm:utils:report personal-project
|
||||
|
||||
/ccpm:utils:insights <linear-issue-id>
|
||||
AI complexity & risk analysis
|
||||
Example: /ccpm:utils:insights WORK-123
|
||||
|
||||
/ccpm:utils:auto-assign <linear-issue-id>
|
||||
AI-powered agent assignment
|
||||
Example: /ccpm:utils:auto-assign WORK-123
|
||||
|
||||
/ccpm:utils:sync-status <linear-issue-id>
|
||||
Sync Linear → Jira (with confirmation)
|
||||
Example: /ccpm:utils:sync-status WORK-123
|
||||
|
||||
/ccpm:utils:rollback <linear-issue-id>
|
||||
Rollback planning changes
|
||||
Example: /ccpm:utils:rollback WORK-123
|
||||
|
||||
/ccpm:utils:dependencies <linear-issue-id>
|
||||
Visualize task dependencies
|
||||
Example: /ccpm:utils:dependencies WORK-123
|
||||
|
||||
/ccpm:utils:agents
|
||||
List available subagents
|
||||
Example: /ccpm:utils:agents
|
||||
|
||||
/ccpm:utils:help [issue-id]
|
||||
This help (context-aware)
|
||||
Example: /ccpm:utils:help WORK-123
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 3: Context-Aware Suggestions
|
||||
|
||||
If issue ID provided, suggest relevant commands based on status:
|
||||
|
||||
```javascript
|
||||
function suggestCommands(context) {
|
||||
const suggestions = []
|
||||
|
||||
// Status-based suggestions
|
||||
if (context.status === 'Planning') {
|
||||
if (!context.hasSpec && context.type === 'feature') {
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:create feature "${context.title}" [epic-id]`,
|
||||
reason: 'Create spec document for better planning',
|
||||
priority: 'high'
|
||||
})
|
||||
}
|
||||
|
||||
if (context.hasSpec) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:write ${context.specDocId} all`,
|
||||
reason: 'Write comprehensive spec sections',
|
||||
priority: 'high'
|
||||
})
|
||||
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:review ${context.specDocId}`,
|
||||
reason: 'Validate spec before implementation',
|
||||
priority: 'medium'
|
||||
})
|
||||
}
|
||||
|
||||
if (context.type === 'epic' && context.hasSpec) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:break-down ${context.issueId}`,
|
||||
reason: 'Break epic into features',
|
||||
priority: 'high'
|
||||
})
|
||||
}
|
||||
|
||||
if (context.type === 'feature' && context.hasSpec) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:break-down ${context.issueId}`,
|
||||
reason: 'Break feature into tasks',
|
||||
priority: 'high'
|
||||
})
|
||||
}
|
||||
|
||||
if (!context.hasSpec) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:implementation:start ${context.issueId}`,
|
||||
reason: 'Start implementation (task-first approach)',
|
||||
priority: 'medium'
|
||||
})
|
||||
|
||||
suggestions.push({
|
||||
command: `/ccpm:utils:insights ${context.issueId}`,
|
||||
reason: 'Get AI analysis before starting',
|
||||
priority: 'low'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (context.status === 'In Progress') {
|
||||
suggestions.push({
|
||||
command: `/ccpm:implementation:next ${context.issueId}`,
|
||||
reason: 'Find optimal next action',
|
||||
priority: 'high'
|
||||
})
|
||||
|
||||
suggestions.push({
|
||||
command: `/ccpm:implementation:sync ${context.issueId}`,
|
||||
reason: 'Save current progress and findings',
|
||||
priority: 'medium'
|
||||
})
|
||||
|
||||
if (context.progress.percentage >= 100) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:verification:check ${context.issueId}`,
|
||||
reason: 'All subtasks complete - run quality checks',
|
||||
priority: 'high'
|
||||
})
|
||||
}
|
||||
|
||||
if (context.hasSpec) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:sync ${context.issueId}`,
|
||||
reason: 'Check if implementation matches spec',
|
||||
priority: 'medium'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (context.status === 'Verification') {
|
||||
suggestions.push({
|
||||
command: `/ccpm:verification:verify ${context.issueId}`,
|
||||
reason: 'Run final verification',
|
||||
priority: 'high'
|
||||
})
|
||||
}
|
||||
|
||||
if (context.labels.includes('blocked')) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:verification:fix ${context.issueId}`,
|
||||
reason: 'Fix blocking issues',
|
||||
priority: 'high'
|
||||
})
|
||||
|
||||
suggestions.push({
|
||||
command: `/ccpm:utils:status ${context.issueId}`,
|
||||
reason: 'Review current status and blockers',
|
||||
priority: 'high'
|
||||
})
|
||||
}
|
||||
|
||||
if (context.status === 'Done') {
|
||||
suggestions.push({
|
||||
command: `/ccpm:complete:finalize ${context.issueId}`,
|
||||
reason: 'Finalize with PR creation and notifications',
|
||||
priority: 'high'
|
||||
})
|
||||
|
||||
if (context.hasSpec) {
|
||||
suggestions.push({
|
||||
command: `/ccpm:spec:sync ${context.issueId}`,
|
||||
reason: 'Final spec sync to document changes',
|
||||
priority: 'medium'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions.sort((a, b) => {
|
||||
const priorityOrder = { high: 0, medium: 1, low: 2 }
|
||||
return priorityOrder[a.priority] - priorityOrder[b.priority]
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Display suggestions:
|
||||
|
||||
```
|
||||
${$1 ? `
|
||||
💡 Suggested Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Based on current status (${context.status}):
|
||||
|
||||
🔥 High Priority:
|
||||
1. ${suggestions[0].command}
|
||||
→ ${suggestions[0].reason}
|
||||
|
||||
2. ${suggestions[1].command}
|
||||
→ ${suggestions[1].reason}
|
||||
|
||||
💡 Consider Also:
|
||||
3. ${suggestions[2].command}
|
||||
→ ${suggestions[2].reason}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
` : ''}
|
||||
```
|
||||
|
||||
### Step 4: Workflow Quick Reference
|
||||
|
||||
```
|
||||
📊 Workflow Quick Reference
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Spec-First Workflow (Recommended):**
|
||||
1. /ccpm:spec:create → Create Epic/Feature
|
||||
2. /ccpm:spec:write → Write spec sections
|
||||
3. /ccpm:spec:review → Validate spec
|
||||
4. /ccpm:spec:break-down → Generate tasks
|
||||
5. /ccpm:implementation:start → Begin work
|
||||
6. /ccpm:spec:sync → Keep in sync
|
||||
|
||||
**Task-First Workflow (Quick):**
|
||||
1. /ccpm:planning:create → Create + plan
|
||||
2. /ccpm:implementation:start → Begin work
|
||||
3. /ccpm:verification:check → Quality checks
|
||||
4. /ccpm:verification:verify → Final review
|
||||
5. /ccpm:complete:finalize → Wrap up
|
||||
|
||||
**Daily Commands:**
|
||||
- /ccpm:utils:report <project> - Morning overview
|
||||
- /ccpm:utils:context <id> - Resume work
|
||||
- /ccpm:implementation:next <id> - What's next?
|
||||
- /ccpm:implementation:sync <id> - Save progress
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 5: Interactive Actions
|
||||
|
||||
If issue ID provided, offer quick actions:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do?",
|
||||
header: "Quick Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
// Dynamically show top 3-4 suggestions
|
||||
{
|
||||
label: suggestions[0].command.split(' ')[0].replace('/ccpm:', ''),
|
||||
description: suggestions[0].reason
|
||||
},
|
||||
{
|
||||
label: "View Full Status",
|
||||
description: "See detailed status (/ccpm:utils:status)"
|
||||
},
|
||||
{
|
||||
label: "Load Context",
|
||||
description: "Load full task context (/ccpm:utils:context)"
|
||||
},
|
||||
{
|
||||
label: "Just Show Help",
|
||||
description: "I just wanted the command reference"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If no issue ID:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do?",
|
||||
header: "Getting Started",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Create New Epic/Feature",
|
||||
description: "Start with spec-first approach (/ccpm:spec:create)"
|
||||
},
|
||||
{
|
||||
label: "Create New Task",
|
||||
description: "Quick task-first approach (/ccpm:planning:create)"
|
||||
},
|
||||
{
|
||||
label: "Migrate Existing Specs",
|
||||
description: "Import markdown specs to Linear (/ccpm:spec:migrate)"
|
||||
},
|
||||
{
|
||||
label: "View Project Report",
|
||||
description: "See project overview (/ccpm:utils:report)"
|
||||
},
|
||||
{
|
||||
label: "List Agents",
|
||||
description: "See available subagents (/ccpm:utils:agents)"
|
||||
},
|
||||
{
|
||||
label: "Just Show Help",
|
||||
description: "I just wanted the command reference"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Show Additional Resources
|
||||
|
||||
```
|
||||
📚 Additional Resources
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📖 Full Documentation:
|
||||
~`$CCPM_COMMANDS_DIR/`README.md
|
||||
|
||||
⚠️ Safety Rules:
|
||||
~`$CCPM_COMMANDS_DIR/SAFETY_RULES.md`
|
||||
|
||||
🔍 Command Details:
|
||||
Each command has detailed docs in:
|
||||
~/.claude/commands/pm/<category>/<command>.md
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: General Help
|
||||
|
||||
```bash
|
||||
/ccpm:utils:help
|
||||
```
|
||||
|
||||
Shows:
|
||||
- All commands categorized
|
||||
- Workflow quick reference
|
||||
- Getting started options
|
||||
|
||||
### Example 2: Context-Aware Help
|
||||
|
||||
```bash
|
||||
/ccpm:utils:help WORK-123
|
||||
```
|
||||
|
||||
If WORK-123 is in "Planning" status with no spec:
|
||||
|
||||
Shows:
|
||||
- Current status summary
|
||||
- **Suggested**: Create spec document
|
||||
- **Suggested**: Or start implementation directly
|
||||
- **Suggested**: Get AI insights
|
||||
- All commands (for reference)
|
||||
- Quick action menu
|
||||
|
||||
### Example 3: Help During Implementation
|
||||
|
||||
```bash
|
||||
/ccpm:utils:help WORK-123
|
||||
```
|
||||
|
||||
If WORK-123 is "In Progress" (3/5 subtasks done):
|
||||
|
||||
Shows:
|
||||
- Progress: 60% complete
|
||||
- **Suggested**: Continue with next task (/ccpm:implementation:next)
|
||||
- **Suggested**: Sync spec if exists
|
||||
- All commands
|
||||
- Quick action menu
|
||||
|
||||
## Notes
|
||||
|
||||
- Context-aware suggestions based on issue status
|
||||
- Interactive quick actions for common workflows
|
||||
- Categorized command reference
|
||||
- Workflow guidance for new users
|
||||
- Always accessible via `/ccpm:utils:help` or `/ccpm:help`
|
||||
122
commands/utils:insights.md
Normal file
122
commands/utils:insights.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
description: AI-powered insights on complexity, risks, and timeline
|
||||
allowed-tools: [LinearMCP, Read, Glob, Context7MCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# AI Insights for: $1
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Gather Data
|
||||
- Fetch Linear issue details
|
||||
- Read mentioned files in description
|
||||
- Analyze checklist complexity
|
||||
- Check codebase for similar features
|
||||
|
||||
### Step 2: Analyze Complexity
|
||||
```javascript
|
||||
const complexity = {
|
||||
filesImpacted: countFiles(description),
|
||||
linesEstimate: estimateLines(checklist),
|
||||
techStackComplexity: analyzeStack(files),
|
||||
integrationPoints: countIntegrations(description),
|
||||
score: calculateComplexityScore(), // 1-10
|
||||
level: score < 4 ? 'Low' : score < 7 ? 'Medium' : 'High'
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Identify Risks
|
||||
```javascript
|
||||
const risks = [
|
||||
checkBreakingChanges(description),
|
||||
checkSecurityConcerns(description),
|
||||
checkPerformanceImpact(files),
|
||||
checkDependencyRisks(checklist),
|
||||
checkTestingGaps(checklist)
|
||||
].filter(r => r.severity > 0)
|
||||
```
|
||||
|
||||
### Step 4: Estimate Timeline
|
||||
```javascript
|
||||
const timeline = {
|
||||
optimistic: estimateMin(checklist, complexity),
|
||||
realistic: estimateRealistic(checklist, complexity, risks),
|
||||
pessimistic: estimateMax(checklist, complexity, risks),
|
||||
confidence: calculateConfidence(similarFeatures)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Display Insights
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 AI Insights for: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📊 Complexity Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Overall: [High/Medium/Low] (Score: [X]/10)
|
||||
|
||||
Factors:
|
||||
- Files Impacted: [N] files across [M] directories
|
||||
- Estimated Lines: ~[X] lines of code
|
||||
- Tech Stack: [complexity assessment]
|
||||
- Integration Points: [N] external systems
|
||||
- Similar Features: [Found X similar implementations]
|
||||
|
||||
🚨 Identified Risks
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[High Priority]
|
||||
⚠️ Breaking Changes: [description]
|
||||
Mitigation: [suggestion]
|
||||
|
||||
⚠️ Security Concern: [description]
|
||||
Mitigation: [suggestion]
|
||||
|
||||
[Medium Priority]
|
||||
⚡ Performance Impact: [description]
|
||||
Mitigation: [suggestion]
|
||||
|
||||
[Low Priority]
|
||||
📋 Testing Gap: [description]
|
||||
Mitigation: [suggestion]
|
||||
|
||||
⏰ Timeline Estimate
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Optimistic: [X] hours ([Y] days)
|
||||
Realistic: [X] hours ([Y] days) ⭐ Recommended
|
||||
Pessimistic: [X] hours ([Y] days)
|
||||
|
||||
Confidence: [High/Medium/Low] based on [N] similar features
|
||||
|
||||
💡 Recommendations
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. [Recommendation 1 based on complexity]
|
||||
2. [Recommendation 2 based on risks]
|
||||
3. [Recommendation 3 based on timeline]
|
||||
|
||||
✨ Optimization Opportunities
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
- [Opportunity 1: parallel execution, etc.]
|
||||
- [Opportunity 2: reuse existing code]
|
||||
- [Opportunity 3: leverage patterns]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 6: Interactive Next Actions
|
||||
Suggest actions based on insights:
|
||||
- If High complexity → Suggest breaking into smaller tasks
|
||||
- If High risk → Suggest additional planning/review
|
||||
- If Timeline long → Suggest parallelization opportunities
|
||||
|
||||
## Notes
|
||||
- AI-powered analysis
|
||||
- Based on codebase patterns
|
||||
- Provides actionable recommendations
|
||||
- Helps with planning accuracy
|
||||
742
commands/utils:organize-docs.md
Normal file
742
commands/utils:organize-docs.md
Normal file
@@ -0,0 +1,742 @@
|
||||
---
|
||||
description: Organize repository documentation following CCPM pattern
|
||||
allowed-tools: [Bash, Read, Write, Edit, Glob, AskUserQuestion]
|
||||
argument-hint: [repo-path] [--dry-run] [--global]
|
||||
---
|
||||
|
||||
# Organize Documentation
|
||||
|
||||
Reorganizes repository documentation following the CCPM documentation pattern for clean, navigable, and scalable documentation structure.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - (Optional) Repository path (default: current directory)
|
||||
- **$2** - (Optional) `--dry-run` to preview changes without applying
|
||||
- **$3** - (Optional) `--global` to install pattern globally in `~/.claude/templates/`
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Analyze Current Structure
|
||||
|
||||
```javascript
|
||||
const repoPath = $1 || process.cwd()
|
||||
const dryRun = args.includes('--dry-run')
|
||||
const installGlobal = args.includes('--global')
|
||||
|
||||
// Analyze current documentation
|
||||
const analysis = {
|
||||
rootMarkdownFiles: findMarkdownFiles(repoPath, { maxDepth: 1 }),
|
||||
existingDocsDir: dirExists(`${repoPath}/docs`),
|
||||
categories: {
|
||||
guides: [],
|
||||
reference: [],
|
||||
architecture: [],
|
||||
research: []
|
||||
}
|
||||
}
|
||||
|
||||
// Categorize files
|
||||
analysis.rootMarkdownFiles.forEach(file => {
|
||||
const category = categorizeFi le(file)
|
||||
if (category) {
|
||||
analysis.categories[category].push(file)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Display analysis:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Documentation Analysis
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Repository: ${basename(repoPath)}
|
||||
Path: ${repoPath}
|
||||
|
||||
📄 Found ${analysis.rootMarkdownFiles.length} markdown files in root
|
||||
|
||||
${analysis.rootMarkdownFiles.length > 5 ? '⚠️ Too many files in root (>5)' : '✅ Root is clean (≤5 files)'}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 2: Categorize Files
|
||||
|
||||
Categorization rules:
|
||||
|
||||
```javascript
|
||||
function categorizeFile(filename) {
|
||||
// Keep in root
|
||||
const keepInRoot = [
|
||||
'README.md',
|
||||
'CHANGELOG.md',
|
||||
'CONTRIBUTING.md',
|
||||
'LICENSE.md',
|
||||
'LICENSE',
|
||||
'CLAUDE.md',
|
||||
'MIGRATION.md'
|
||||
]
|
||||
|
||||
if (keepInRoot.includes(filename)) {
|
||||
return 'root'
|
||||
}
|
||||
|
||||
// Guides (user-facing how-to)
|
||||
if (filename.match(/GUIDE|INSTALL|SETUP|WORKFLOW|TUTORIAL/i)) {
|
||||
return 'guides'
|
||||
}
|
||||
|
||||
// Reference (API, catalog, reference)
|
||||
if (filename.match(/CATALOG|REFERENCE|API|COMMANDS/i)) {
|
||||
return 'reference'
|
||||
}
|
||||
|
||||
// Architecture
|
||||
if (filename.match(/ARCHITECTURE|DESIGN/i)) {
|
||||
return 'architecture'
|
||||
}
|
||||
|
||||
// Research (historical planning)
|
||||
if (filename.match(/RESEARCH|PLAN|PROPOSAL|STATUS|SUMMARY|COMPARISON|MATRIX|QUICK.?REFERENCE/i)) {
|
||||
return 'research'
|
||||
}
|
||||
|
||||
return null // Unknown, ask user
|
||||
}
|
||||
```
|
||||
|
||||
Display categorization:
|
||||
|
||||
```
|
||||
📦 Proposed File Organization
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
✅ Keep in Root (${analysis.categories.root?.length || 0}):
|
||||
${analysis.categories.root?.map(f => ` - ${f}`).join('\n') || ' (none)'}
|
||||
|
||||
📘 Move to docs/guides/ (${analysis.categories.guides.length}):
|
||||
${analysis.categories.guides.map(f => ` - ${f} → docs/guides/${kebabCase(f)}`).join('\n') || ' (none)'}
|
||||
|
||||
📖 Move to docs/reference/ (${analysis.categories.reference.length}):
|
||||
${analysis.categories.reference.map(f => ` - ${f} → docs/reference/${kebabCase(f)}`).join('\n') || ' (none)'}
|
||||
|
||||
🏗️ Move to docs/architecture/ (${analysis.categories.architecture.length}):
|
||||
${analysis.categories.architecture.map(f => ` - ${f} → docs/architecture/${kebabCase(f)}`).join('\n') || ' (none)'}
|
||||
|
||||
📚 Move to docs/research/ (${analysis.categories.research.length}):
|
||||
${analysis.categories.research.map(f => ` - ${f} → docs/research/${categorizeTopic(f)}/${kebabCase(f)}`).join('\n') || ' (none)'}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 3: Ask for Confirmation
|
||||
|
||||
Use AskUserQuestion for files with unclear categorization:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Some files need categorization. Where should these go?",
|
||||
header: "Categorize",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Apply Auto-Categorization",
|
||||
description: "Use CCPM pattern rules for all files"
|
||||
},
|
||||
{
|
||||
label: "Review Each File",
|
||||
description: "I'll categorize unclear files manually"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Don't reorganize"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If "Review Each File" selected, ask for each unclear file:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: `Where should ${filename} go?`,
|
||||
header: "Categorize File",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Keep in Root", description: "Important user-facing file" },
|
||||
{ label: "docs/guides/", description: "User how-to guide" },
|
||||
{ label: "docs/reference/", description: "API or feature reference" },
|
||||
{ label: "docs/architecture/", description: "Design decision" },
|
||||
{ label: "docs/research/", description: "Historical planning (archive)" },
|
||||
{ label: "Skip", description: "Don't move this file" }
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Apply Changes
|
||||
|
||||
If `--dry-run`:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 DRY RUN - No changes will be made
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Would perform these operations:
|
||||
|
||||
📁 Create directories:
|
||||
✓ docs/guides/
|
||||
✓ docs/reference/
|
||||
✓ docs/architecture/decisions/
|
||||
✓ docs/development/
|
||||
✓ docs/research/
|
||||
|
||||
📦 Move files (${totalMoves}):
|
||||
${moves.map(m => ` ${m.from} → ${m.to}`).join('\n')}
|
||||
|
||||
📄 Create index files (6):
|
||||
✓ docs/README.md
|
||||
✓ docs/guides/README.md
|
||||
✓ docs/reference/README.md
|
||||
✓ docs/architecture/README.md
|
||||
✓ docs/development/README.md
|
||||
✓ docs/research/README.md
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Run without --dry-run to apply changes.
|
||||
```
|
||||
|
||||
If not dry-run, execute:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd "${repoPath}"
|
||||
|
||||
# Phase 1: Create directory structure
|
||||
echo "📁 Creating directory structure..."
|
||||
mkdir -p docs/{guides,reference,architecture/decisions,development,research}
|
||||
|
||||
# Phase 2: Move files
|
||||
echo "📦 Moving files..."
|
||||
${moves.map(m => `
|
||||
if [ -f "${m.from}" ]; then
|
||||
mv "${m.from}" "${m.to}"
|
||||
echo " ✓ ${m.from} → ${m.to}"
|
||||
fi`).join('\n')}
|
||||
|
||||
# Phase 3: Create index files
|
||||
echo "📄 Creating index files..."
|
||||
# [Generate index file content using templates]
|
||||
|
||||
echo "✅ Documentation reorganization complete!"
|
||||
```
|
||||
|
||||
### Step 5: Create Index Files
|
||||
|
||||
Use CCPM templates for all index files:
|
||||
|
||||
**docs/README.md**:
|
||||
```markdown
|
||||
# [Project] Documentation
|
||||
|
||||
Welcome to the [Project] documentation.
|
||||
|
||||
## Quick Links
|
||||
|
||||
- **[Quick Start](guides/quick-start.md)** - Get started in 5 minutes
|
||||
- **[Installation](guides/installation.md)** - Detailed setup
|
||||
- **[Reference](reference/)** - Complete documentation
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
### 📘 [Guides](guides/) - How-to guides
|
||||
- [Quick Start](guides/quick-start.md)
|
||||
- [Installation](guides/installation.md)
|
||||
|
||||
### 📖 [Reference](reference/) - API & feature reference
|
||||
- [API](reference/api.md)
|
||||
- [Configuration](reference/config.md)
|
||||
|
||||
### 🏗️ [Architecture](architecture/) - Design decisions
|
||||
- [Overview](architecture/overview.md)
|
||||
- [Decisions](architecture/decisions/)
|
||||
|
||||
### 🔧 [Development](development/) - For contributors
|
||||
- [Setup](development/setup.md)
|
||||
- [Testing](development/testing.md)
|
||||
|
||||
### 📚 [Research](research/) - Historical context
|
||||
Archived research and planning documents.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||
```
|
||||
|
||||
**docs/guides/README.md**:
|
||||
```markdown
|
||||
# User Guides
|
||||
|
||||
How-to guides for using [Project].
|
||||
|
||||
## Getting Started
|
||||
|
||||
- [Quick Start](quick-start.md) - 5-minute introduction
|
||||
- [Installation](installation.md) - Detailed installation
|
||||
|
||||
## Features
|
||||
|
||||
[Auto-generated list of guides]
|
||||
|
||||
## Need Help?
|
||||
|
||||
See the main [Documentation Index](../README.md).
|
||||
```
|
||||
|
||||
**docs/research/README.md**:
|
||||
```markdown
|
||||
# Research & Planning Documents
|
||||
|
||||
**Archived historical documents** - For current docs, see main [Documentation](../README.md).
|
||||
|
||||
## Purpose
|
||||
|
||||
These documents explain why decisions were made and how features were researched.
|
||||
|
||||
**Note**: May be outdated - refer to main docs for current state.
|
||||
|
||||
## Contents
|
||||
|
||||
[Auto-generated list of research topics]
|
||||
```
|
||||
|
||||
### Step 6: Update Links
|
||||
|
||||
Scan all moved files for internal links and update them:
|
||||
|
||||
```javascript
|
||||
// Find all markdown links
|
||||
const linkPattern = /\[([^\]]+)\]\(([^)]+)\)/g
|
||||
|
||||
movedFiles.forEach(file => {
|
||||
let content = readFile(file.newPath)
|
||||
let updated = false
|
||||
|
||||
content = content.replace(linkPattern, (match, text, url) => {
|
||||
if (url.startsWith('http')) return match // External link
|
||||
|
||||
// Calculate new relative path
|
||||
const oldPath = resolvePath(file.oldPath, url)
|
||||
const newPath = calculateRelativePath(file.newPath, oldPath)
|
||||
|
||||
if (newPath !== url) {
|
||||
updated = true
|
||||
return `[${text}](${newPath})`
|
||||
}
|
||||
|
||||
return match
|
||||
})
|
||||
|
||||
if (updated) {
|
||||
writeFile(file.newPath, content)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Display link updates:
|
||||
|
||||
```
|
||||
🔗 Updating Internal Links
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Updated links in ${updatedFiles.length} files:
|
||||
${updatedFiles.map(f => ` ✓ ${f.path} (${f.linksUpdated} links)`).join('\n')}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 7: Update CLAUDE.md
|
||||
|
||||
If CLAUDE.md exists, add documentation pattern section:
|
||||
|
||||
```javascript
|
||||
const claudeMdPath = `${repoPath}/CLAUDE.md`
|
||||
|
||||
if (fileExists(claudeMdPath)) {
|
||||
const claudeMd = readFile(claudeMdPath)
|
||||
|
||||
// Check if documentation section already exists
|
||||
if (!claudeMd.includes('## Documentation Structure') && !claudeMd.includes('## Documentation Pattern')) {
|
||||
const documentationSection = generateDocumentationSection(analysis)
|
||||
|
||||
// Append to CLAUDE.md
|
||||
appendToFile(claudeMdPath, `\n\n${documentationSection}`)
|
||||
} else {
|
||||
// Update existing section
|
||||
updateDocumentationSection(claudeMdPath, analysis)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Documentation section template:
|
||||
|
||||
```markdown
|
||||
## Documentation Structure
|
||||
|
||||
This repository follows the CCPM documentation pattern for clean, navigable, and scalable documentation.
|
||||
|
||||
### Pattern Overview
|
||||
|
||||
```
|
||||
docs/
|
||||
├── README.md # Documentation navigation hub
|
||||
├── guides/ # 📘 User how-to guides
|
||||
├── reference/ # 📖 API & feature reference
|
||||
├── architecture/ # 🏗️ Design decisions & ADRs
|
||||
├── development/ # 🔧 Contributor documentation
|
||||
└── research/ # 📚 Historical context (archived)
|
||||
```
|
||||
|
||||
### Documentation Guidelines
|
||||
|
||||
**When creating new documentation:**
|
||||
|
||||
1. **User guides** → `docs/guides/`
|
||||
- Installation, setup, configuration
|
||||
- Feature walkthroughs and tutorials
|
||||
- Troubleshooting guides
|
||||
- Use descriptive filenames: `installation.md`, `quick-start.md`
|
||||
|
||||
2. **Reference documentation** → `docs/reference/`
|
||||
- API documentation
|
||||
- Command/feature catalogs
|
||||
- Configuration references
|
||||
- Technical specifications
|
||||
|
||||
3. **Architecture documentation** → `docs/architecture/`
|
||||
- System architecture overviews
|
||||
- Component designs
|
||||
- Architecture Decision Records (ADRs) in `decisions/`
|
||||
- Use ADR template for decisions
|
||||
|
||||
4. **Development documentation** → `docs/development/`
|
||||
- Development environment setup
|
||||
- Testing guides
|
||||
- Release processes
|
||||
- Contribution workflows
|
||||
|
||||
5. **Research/Planning documents** → `docs/research/`
|
||||
- Historical planning documents
|
||||
- Research findings
|
||||
- Implementation journeys
|
||||
- **Note**: These are archived - current docs go elsewhere
|
||||
|
||||
### Root Directory Rules
|
||||
|
||||
**Keep ONLY these files in root:**
|
||||
- `README.md` - Main entry point
|
||||
- `CHANGELOG.md` - Version history
|
||||
- `CONTRIBUTING.md` - Contribution guide
|
||||
- `LICENSE` - License file
|
||||
- `CLAUDE.md` - This file
|
||||
- `MIGRATION.md` - Migration guide (if applicable)
|
||||
|
||||
**All other documentation goes in `docs/`**
|
||||
|
||||
### Index Files
|
||||
|
||||
Each documentation directory has a `README.md` that:
|
||||
- Explains what the directory contains
|
||||
- Links to all documents in that directory
|
||||
- Provides navigation back to main docs
|
||||
|
||||
### Maintaining Documentation
|
||||
|
||||
**When you create or move documentation:**
|
||||
|
||||
1. Place it in the appropriate `docs/` subdirectory
|
||||
2. Update the relevant index `README.md`
|
||||
3. Update internal links to use correct relative paths
|
||||
4. Keep root directory clean (≤5 markdown files)
|
||||
|
||||
**When you reference documentation:**
|
||||
|
||||
1. Use relative links from current location
|
||||
2. Link to `docs/README.md` for main navigation
|
||||
3. Link to specific guides/references as needed
|
||||
|
||||
### Auto-Organization
|
||||
|
||||
To reorganize documentation automatically:
|
||||
|
||||
```bash
|
||||
/ccpm:utils:organize-docs [repo-path] [--dry-run] [--global]
|
||||
```
|
||||
|
||||
This command:
|
||||
- Analyzes current documentation structure
|
||||
- Categorizes files using CCPM pattern rules
|
||||
- Moves files to appropriate locations
|
||||
- Creates index files
|
||||
- Updates internal links
|
||||
- Can be installed globally for use in any repository
|
||||
|
||||
### Navigation
|
||||
|
||||
All documentation is accessible from `docs/README.md`:
|
||||
- **Quick Start**: `docs/guides/quick-start.md`
|
||||
- **Full Documentation**: Browse by category in `docs/`
|
||||
- **Contributing**: `CONTRIBUTING.md`
|
||||
|
||||
### Pattern Benefits
|
||||
|
||||
- ✅ Clean root directory
|
||||
- ✅ Clear separation of concerns
|
||||
- ✅ Easy to find documentation
|
||||
- ✅ Scales with project growth
|
||||
- ✅ Historical context preserved
|
||||
- ✅ AI assistant friendly
|
||||
- ✅ Consistent across projects
|
||||
```
|
||||
|
||||
Display update confirmation:
|
||||
|
||||
```
|
||||
📝 Updating CLAUDE.md
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
${exists ? '✓ Updated documentation structure section' : '✓ Added documentation structure section'}
|
||||
|
||||
CLAUDE.md now includes:
|
||||
- Documentation pattern overview
|
||||
- Guidelines for new documentation
|
||||
- Root directory rules
|
||||
- Index file conventions
|
||||
- Auto-organization instructions
|
||||
- Navigation guidelines
|
||||
|
||||
This ensures AI assistants always follow the pattern.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 8: Global Installation (if --global)
|
||||
|
||||
If `--global` flag is set:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🌍 Installing CCPM docs pattern globally..."
|
||||
|
||||
# Create global template directory
|
||||
mkdir -p ~/.claude/templates/ccpm-docs-pattern
|
||||
mkdir -p ~/.claude/scripts
|
||||
|
||||
# Copy pattern documentation
|
||||
cp GLOBAL_DOCS_PATTERN.md ~/.claude/templates/ccpm-docs-pattern/
|
||||
|
||||
# Copy organize script
|
||||
cp scripts/organize-docs.sh ~/.claude/templates/ccpm-docs-pattern/
|
||||
|
||||
# Create global auto-organize script
|
||||
cat > ~/.claude/scripts/organize-docs << 'SCRIPT'
|
||||
#!/bin/bash
|
||||
# Auto-organize documentation for any repository
|
||||
|
||||
REPO_PATH="${1:-.}"
|
||||
cd "$REPO_PATH" || exit 1
|
||||
|
||||
# Use CCPM organize command
|
||||
claude /ccpm:utils:organize-docs "$REPO_PATH"
|
||||
SCRIPT
|
||||
|
||||
chmod +x ~/.claude/scripts/organize-docs
|
||||
|
||||
# Add to PATH if not already there
|
||||
if [[ ":$PATH:" != *":$HOME/.claude/scripts:"* ]]; then
|
||||
echo ""
|
||||
echo "Add to your shell profile (~/.zshrc or ~/.bashrc):"
|
||||
echo " export PATH=\"\$HOME/.claude/scripts:\$PATH\""
|
||||
fi
|
||||
|
||||
echo "✅ Global installation complete!"
|
||||
echo ""
|
||||
echo "Usage in any repository:"
|
||||
echo " organize-docs"
|
||||
echo " organize-docs /path/to/repo"
|
||||
echo " organize-docs --dry-run"
|
||||
```
|
||||
|
||||
### Step 9: Summary
|
||||
|
||||
Display completion summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎉 Documentation Reorganization Complete!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📊 Summary:
|
||||
✓ Root files: ${before} → ${after} (-${reduction}%)
|
||||
✓ Files moved: ${movedCount}
|
||||
✓ Index files created: 6
|
||||
✓ Links updated: ${linksUpdated}
|
||||
|
||||
📁 New Structure:
|
||||
docs/
|
||||
├── guides/ (${guidesCount} files)
|
||||
├── reference/ (${referenceCount} files)
|
||||
├── architecture/ (${architectureCount} files)
|
||||
├── development/ (${developmentCount} files)
|
||||
└── research/ (${researchCount} files, archived)
|
||||
|
||||
📝 Next Steps:
|
||||
1. Review changes: git status
|
||||
2. Test documentation links
|
||||
3. Update README.md with new structure
|
||||
${hasClaude ? '4. ✅ CLAUDE.md updated with documentation pattern' : ''}
|
||||
5. Commit changes: git add . && git commit -m "docs: reorganize documentation"
|
||||
|
||||
${installGlobal ? `
|
||||
🌍 Global Pattern Installed:
|
||||
Pattern available at: ~/.claude/templates/ccpm-docs-pattern/
|
||||
Command available: organize-docs
|
||||
|
||||
Use in any repository:
|
||||
cd ~/projects/any-repo
|
||||
organize-docs
|
||||
` : `
|
||||
💡 Install Globally:
|
||||
Run: /ccpm:utils:organize-docs . --global
|
||||
Then use 'organize-docs' in any repository
|
||||
`}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Analyze Current Repository
|
||||
|
||||
```bash
|
||||
/ccpm:utils:organize-docs --dry-run
|
||||
```
|
||||
|
||||
Output:
|
||||
- Analysis of current structure
|
||||
- Proposed changes
|
||||
- No files moved
|
||||
|
||||
### Example 2: Reorganize Current Repository
|
||||
|
||||
```bash
|
||||
/ccpm:utils:organize-docs
|
||||
```
|
||||
|
||||
Performs:
|
||||
1. Analyzes documentation
|
||||
2. Shows categorization
|
||||
3. Asks for confirmation
|
||||
4. Creates docs/ structure
|
||||
5. Moves files
|
||||
6. Creates index files
|
||||
7. Updates links
|
||||
8. Shows summary
|
||||
|
||||
### Example 3: Reorganize Different Repository
|
||||
|
||||
```bash
|
||||
/ccpm:utils:organize-docs ~/projects/my-app
|
||||
```
|
||||
|
||||
Same as Example 2 but for different repository.
|
||||
|
||||
### Example 4: Install Global Pattern
|
||||
|
||||
```bash
|
||||
/ccpm:utils:organize-docs . --global
|
||||
```
|
||||
|
||||
Performs:
|
||||
1. Reorganizes current repository
|
||||
2. Installs pattern to ~/.claude/templates/
|
||||
3. Creates global organize-docs script
|
||||
4. Adds to PATH
|
||||
|
||||
Then can use in any repo:
|
||||
```bash
|
||||
cd ~/projects/any-repo
|
||||
organize-docs
|
||||
```
|
||||
|
||||
## File Categorization Rules
|
||||
|
||||
### Keep in Root
|
||||
- README.md - Entry point
|
||||
- CHANGELOG.md - Version history
|
||||
- CONTRIBUTING.md - Contribution guide
|
||||
- LICENSE/LICENSE.md - License
|
||||
- CLAUDE.md - AI assistant instructions
|
||||
- MIGRATION.md - Migration guide
|
||||
|
||||
### docs/guides/
|
||||
Files matching: `*GUIDE*`, `*INSTALL*`, `*SETUP*`, `*WORKFLOW*`, `*TUTORIAL*`
|
||||
|
||||
Examples:
|
||||
- INSTALL_HOOKS.md → docs/guides/hooks.md
|
||||
- MCP_INTEGRATION_GUIDE.md → docs/guides/mcp-integration.md
|
||||
- UI_WORKFLOW.md → docs/guides/ui-workflow.md
|
||||
|
||||
### docs/reference/
|
||||
Files matching: `*CATALOG*`, `*REFERENCE*`, `*API*`, `*COMMANDS*`
|
||||
|
||||
Examples:
|
||||
- SKILLS_CATALOG.md → docs/reference/skills.md
|
||||
- API_REFERENCE.md → docs/reference/api.md
|
||||
|
||||
### docs/architecture/
|
||||
Files matching: `*ARCHITECTURE*`, `*DESIGN*`
|
||||
|
||||
Examples:
|
||||
- SKILLS_ARCHITECTURE.md → docs/architecture/skills-system.md
|
||||
- SYSTEM_DESIGN.md → docs/architecture/overview.md
|
||||
|
||||
### docs/research/
|
||||
Files matching: `*RESEARCH*`, `*PLAN*`, `*PROPOSAL*`, `*STATUS*`, `*SUMMARY*`, `*COMPARISON*`, `*MATRIX*`
|
||||
|
||||
Examples:
|
||||
- SKILLS_INTEGRATION_PLAN.md → docs/research/skills/integration-plan.md
|
||||
- HOOKS_RESEARCH_SUMMARY.md → docs/research/hooks/research-summary.md
|
||||
|
||||
## Notes
|
||||
|
||||
- Always creates backup before moving files (git makes this easy)
|
||||
- Preserves git history for moved files
|
||||
- Updates internal markdown links automatically
|
||||
- Creates index files for easy navigation
|
||||
- **Updates CLAUDE.md** with documentation pattern instructions
|
||||
- Ensures AI assistants always follow the pattern
|
||||
- Follows CCPM documentation pattern globally
|
||||
- Can be used on any repository, not just CCPM
|
||||
- Dry-run mode for safe preview
|
||||
- Global installation for reuse across all projects
|
||||
|
||||
## Success Metrics
|
||||
|
||||
After running this command:
|
||||
- ✅ Root directory has ≤5 markdown files
|
||||
- ✅ All docs reachable within 2 clicks from docs/README.md
|
||||
- ✅ Clear separation: guides/reference/architecture/research
|
||||
- ✅ Index files guide navigation
|
||||
- ✅ Historical context preserved in research/
|
||||
- ✅ Pattern reusable across projects
|
||||
259
commands/utils:report.md
Normal file
259
commands/utils:report.md
Normal file
@@ -0,0 +1,259 @@
|
||||
---
|
||||
description: Show progress report across all tasks in a project
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <project>
|
||||
---
|
||||
|
||||
# Progress Report for Project: $1
|
||||
|
||||
Generating comprehensive progress report for **$1** project.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Project Context
|
||||
|
||||
**Project Mapping**:
|
||||
|
||||
- **my-app** → Linear Team: "Work", Project: "My App"
|
||||
- **my-project** → Linear Team: "Work", Project: "My Project"
|
||||
- **personal-project** → Linear Team: "Personal", Project: "Personal Project"
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch All Issues for Project
|
||||
|
||||
Use **Linear MCP** to:
|
||||
|
||||
1. List all issues for the project ($1)
|
||||
2. Include: Backlog, Planning, In Progress, Verification, Done (recent)
|
||||
3. Exclude: Canceled, archived (unless requested)
|
||||
4. Get full details: status, labels, assignee, checklist progress, dates
|
||||
|
||||
### Step 2: Categorize and Analyze
|
||||
|
||||
Group issues by status:
|
||||
|
||||
```javascript
|
||||
const categories = {
|
||||
backlog: issues.filter(i => i.status === 'Backlog'),
|
||||
planning: issues.filter(i => i.status === 'Planning'),
|
||||
inProgress: issues.filter(i => i.status === 'In Progress'),
|
||||
verification: issues.filter(i => i.status === 'Verification'),
|
||||
blocked: issues.filter(i => i.labels.includes('blocked')),
|
||||
done: issues.filter(i => i.status === 'Done' && withinLast7Days(i))
|
||||
}
|
||||
```
|
||||
|
||||
Calculate statistics:
|
||||
|
||||
```javascript
|
||||
const stats = {
|
||||
total: issues.length,
|
||||
byStatus: Object.keys(categories).map(k => ({
|
||||
status: k,
|
||||
count: categories[k].length,
|
||||
percentage: (categories[k].length / issues.length * 100).toFixed(1)
|
||||
})),
|
||||
avgCompletionTime: calculateAvgTime(categories.done),
|
||||
totalSubtasks: sumAllSubtasks(issues),
|
||||
completedSubtasks: sumCompletedSubtasks(issues),
|
||||
blockedCount: categories.blocked.length
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Display Report
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📊 Progress Report: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📅 Report Date: [Current Date]
|
||||
🏢 Project: $1
|
||||
📈 Total Issues: [N]
|
||||
✅ Overall Progress: [X]% ([Y] of [Z] subtasks complete)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Status Breakdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📦 Backlog: [N] issues ([%]%)
|
||||
📝 Planning: [N] issues ([%]%)
|
||||
⏳ In Progress: [N] issues ([%]%)
|
||||
🔍 Verification: [N] issues ([%]%)
|
||||
🚫 Blocked: [N] issues ([%]%) ⚠️
|
||||
✅ Done (7d): [N] issues ([%]%)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚠️ Blocked Issues (Needs Attention!)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[If blocked issues exist, list them:]
|
||||
|
||||
1. [WORK-123]: [Title]
|
||||
Status: [Status]
|
||||
Blocked: [Duration]
|
||||
Action: /ccpm:verification:fix WORK-123
|
||||
|
||||
2. [WORK-124]: [Title]
|
||||
Status: [Status]
|
||||
Blocked: [Duration]
|
||||
Action: /ccpm:verification:fix WORK-124
|
||||
|
||||
[Or if none:]
|
||||
✅ No blocked issues!
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🏃 In Progress Issues
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[For each in-progress issue:]
|
||||
|
||||
1. [WORK-125]: [Title]
|
||||
Progress: [X/Y] subtasks ([%]%)
|
||||
Time in progress: [Duration]
|
||||
Next: /ccpm:implementation:next WORK-125
|
||||
|
||||
2. [WORK-126]: [Title]
|
||||
Progress: [X/Y] subtasks ([%]%)
|
||||
Time in progress: [Duration]
|
||||
Next: /ccpm:implementation:next WORK-126
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Planning Issues (Ready to Start)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[For each planning issue:]
|
||||
|
||||
1. [WORK-127]: [Title]
|
||||
Checklist: [N] subtasks
|
||||
Ready: /ccpm:implementation:start WORK-127
|
||||
|
||||
2. [WORK-128]: [Title]
|
||||
Checklist: [N] subtasks
|
||||
Ready: /ccpm:implementation:start WORK-128
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Verification Issues (Almost Done!)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[For each verification issue:]
|
||||
|
||||
1. [WORK-129]: [Title]
|
||||
Time in verification: [Duration]
|
||||
Next: /ccpm:verification:verify WORK-129
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Recently Completed (Last 7 Days)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[For each done issue:]
|
||||
|
||||
1. [WORK-130]: [Title]
|
||||
Completed: [Date]
|
||||
Time to complete: [Duration]
|
||||
|
||||
2. [WORK-131]: [Title]
|
||||
Completed: [Date]
|
||||
Time to complete: [Duration]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📈 Insights
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
⚡ Velocity: [N] issues completed in last 7 days
|
||||
⏱️ Avg Completion Time: [X] days
|
||||
🎯 Focus: [Most common status - where work is concentrated]
|
||||
🚨 Attention Needed: [Number] blocked, [Number] in verification too long
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Interactive Next Actions
|
||||
|
||||
**READ**: ``$CCPM_COMMANDS_DIR/_shared-linear-helpers.md``
|
||||
|
||||
Use **AskUserQuestion** tool:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do next?",
|
||||
header: "Next Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Work on Blocked Issues",
|
||||
description: `Fix ${blockedCount} blocked issues`
|
||||
},
|
||||
{
|
||||
label: "Continue In-Progress",
|
||||
description: `Work on ${inProgressCount} active tasks`
|
||||
},
|
||||
{
|
||||
label: "Start New Task",
|
||||
description: "Start one of the planning tasks"
|
||||
},
|
||||
{
|
||||
label: "Create New Issue",
|
||||
description: "Create and plan a new task (/ccpm:planning:create)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute based on choice**:
|
||||
|
||||
- If "Work on Blocked Issues" → Show blocked issues and ask which to fix
|
||||
- If "Continue In-Progress" → Show in-progress issues and ask which to work on
|
||||
- If "Start New Task" → Show planning issues and ask which to start
|
||||
- If "Create New Issue" → Prompt for title and run `/ccpm:planning:create`
|
||||
- If "Other" → Show quick commands and exit
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Create Task: /ccpm:planning:create "<title>" $1
|
||||
View Task: /ccpm:utils:status <issue-id>
|
||||
Context: /ccpm:utils:context <issue-id>
|
||||
Refresh: /ccpm:utils:report $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### Report Scope
|
||||
|
||||
- Shows all active work in the project
|
||||
- Highlights blockers and issues needing attention
|
||||
- Provides quick actions for each category
|
||||
- Calculates velocity and insights
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# For external PM projects
|
||||
/ccpm:utils:report my-app
|
||||
/ccpm:utils:report my-project
|
||||
|
||||
# For internal projects
|
||||
/ccpm:utils:report personal-project
|
||||
```
|
||||
|
||||
### Refresh Frequency
|
||||
|
||||
- Run anytime to see current project state
|
||||
- Especially useful at start of day
|
||||
- Or when planning next work
|
||||
- Or in team standups
|
||||
51
commands/utils:rollback.md
Normal file
51
commands/utils:rollback.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
description: Rollback planning changes to previous version
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Rollback for: $1
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch History
|
||||
- Get Linear issue history/activity
|
||||
- Show last 10 description changes with timestamps
|
||||
|
||||
### Step 2: Show Versions
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📜 Description History for: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Current (v5): [timestamp] by [user]
|
||||
Preview: [first 100 chars...]
|
||||
|
||||
v4: [timestamp] by [user]
|
||||
Preview: [first 100 chars...]
|
||||
|
||||
v3: [timestamp] by [user]
|
||||
Preview: [first 100 chars...]
|
||||
|
||||
[... up to 10 versions ...]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 3: Ask Which Version
|
||||
Use **AskUserQuestion** to select version to rollback to
|
||||
|
||||
### Step 4: Preview Rollback
|
||||
Show full description of selected version for review
|
||||
|
||||
### Step 5: Confirm Rollback
|
||||
Ask final confirmation before updating Linear
|
||||
|
||||
### Step 6: Execute
|
||||
- Update Linear description to selected version
|
||||
- Add comment: "Rolled back to version from [timestamp]"
|
||||
- Show success
|
||||
|
||||
## Notes
|
||||
- Safer than manual editing
|
||||
- Shows full history
|
||||
- Requires confirmation
|
||||
250
commands/utils:search.md
Normal file
250
commands/utils:search.md
Normal file
@@ -0,0 +1,250 @@
|
||||
---
|
||||
description: Search and list tasks from a project by text query
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <project> <search-query>
|
||||
---
|
||||
|
||||
# Search Tasks in Project: $1
|
||||
|
||||
Searching for tasks matching: **$2**
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Project Context
|
||||
|
||||
**Project Mapping**:
|
||||
|
||||
- **my-app** → Linear Team: "Work", Project: "My App"
|
||||
- **my-project** → Linear Team: "Work", Project: "My Project"
|
||||
- **personal-project** → Linear Team: "Personal", Project: "Personal Project"
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Parse Arguments
|
||||
|
||||
Extract:
|
||||
- `$1` = Project identifier (my-app, my-project, or personal-project)
|
||||
- `$2+` = Search query (all remaining arguments joined with spaces)
|
||||
|
||||
### Step 2: Search Issues
|
||||
|
||||
Use **Linear MCP** `list_issues` with:
|
||||
|
||||
```javascript
|
||||
{
|
||||
query: "$2", // Search in title and description
|
||||
project: "$1", // Filter by project
|
||||
includeArchived: false, // Exclude archived by default
|
||||
limit: 50, // Return up to 50 results
|
||||
orderBy: "updatedAt" // Most recently updated first
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Display Results
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Search Results: "$2"
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📦 Project: $1
|
||||
🔎 Query: $2
|
||||
📊 Found: [N] issue(s)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Results
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[For each issue, display:]
|
||||
|
||||
1. [WORK-123] 📌 [Status Emoji] [Title]
|
||||
Status: [Current Status] | Progress: [X/Y] subtasks ([%]%)
|
||||
Labels: [label1, label2, ...]
|
||||
Updated: [Relative time - e.g., "2 hours ago", "3 days ago"]
|
||||
|
||||
📝 Description preview:
|
||||
[First 2 lines of description...]
|
||||
|
||||
🔗 Actions:
|
||||
- View: /ccpm:utils:status WORK-123
|
||||
- Context: /ccpm:utils:context WORK-123
|
||||
- Start: /ccpm:implementation:start WORK-123
|
||||
|
||||
─────────────────────────────────────────────────────
|
||||
|
||||
2. [WORK-124] 📌 [Status Emoji] [Title]
|
||||
[... same format ...]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**Status Emojis**:
|
||||
- 📦 = Backlog
|
||||
- 📝 = Planning
|
||||
- ⏳ = In Progress
|
||||
- 🔍 = Verification
|
||||
- 🚫 = Blocked (has "blocked" label)
|
||||
- ✅ = Done
|
||||
|
||||
### Step 4: Handle Empty Results
|
||||
|
||||
If no issues found:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Search Results: "$2"
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📦 Project: $1
|
||||
🔎 Query: $2
|
||||
📊 Found: 0 issues
|
||||
|
||||
❌ No issues found matching "$2"
|
||||
|
||||
💡 Tips:
|
||||
- Try broader search terms
|
||||
- Check spelling
|
||||
- Try searching in all projects (omit project parameter)
|
||||
- View all project tasks: /ccpm:utils:report $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 5: Interactive Next Actions
|
||||
|
||||
**READ**: ``$CCPM_COMMANDS_DIR/_shared-linear-helpers.md``
|
||||
|
||||
Use **AskUserQuestion** tool:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to do next?",
|
||||
header: "Next Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "View Task Details",
|
||||
description: "View full details of a specific task from results"
|
||||
},
|
||||
{
|
||||
label: "Load Task Context",
|
||||
description: "Load task context to start working on it"
|
||||
},
|
||||
{
|
||||
label: "Refine Search",
|
||||
description: "Search again with different query"
|
||||
},
|
||||
{
|
||||
label: "View Project Report",
|
||||
description: "See full project report (/ccpm:utils:report)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Execute based on choice**:
|
||||
|
||||
- If "View Task Details" → Ask which issue ID and run `/ccpm:utils:status <id>`
|
||||
- If "Load Task Context" → Ask which issue ID and run `/ccpm:utils:context <id>`
|
||||
- If "Refine Search" → Ask for new search query and re-run search
|
||||
- If "View Project Report" → Run `/ccpm:utils:report $1`
|
||||
- If "Other" → Show quick commands and exit
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Quick Commands
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
View Task: /ccpm:utils:status <issue-id>
|
||||
Load Context: /ccpm:utils:context <issue-id>
|
||||
Start Work: /ccpm:implementation:start <issue-id>
|
||||
Project Report: /ccpm:utils:report $1
|
||||
New Search: /ccpm:utils:search $1 "<new-query>"
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Advanced Search Options
|
||||
|
||||
### Search by Status
|
||||
|
||||
Combine with status filters:
|
||||
|
||||
```bash
|
||||
# Search for "auth" tasks in "In Progress" status
|
||||
/ccpm:utils:search my-app "auth"
|
||||
# Then manually filter by status in results
|
||||
```
|
||||
|
||||
### Search Across All Projects
|
||||
|
||||
Omit project parameter to search all projects:
|
||||
|
||||
```bash
|
||||
# Search all projects
|
||||
/ccpm:utils:search "" "authentication"
|
||||
# Note: Empty string for project searches all
|
||||
```
|
||||
|
||||
### Search by Keywords
|
||||
|
||||
Common search patterns:
|
||||
|
||||
- Feature name: `/ccpm:utils:search my-project "user profile"`
|
||||
- Bug description: `/ccpm:utils:search my-app "crash"`
|
||||
- Technical term: `/ccpm:utils:search personal-project "API"`
|
||||
- Label/tag: `/ccpm:utils:search my-project "backend"`
|
||||
|
||||
## Notes
|
||||
|
||||
### Search Behavior
|
||||
|
||||
- Searches both title AND description
|
||||
- Case-insensitive
|
||||
- Partial word matching
|
||||
- Ordered by most recently updated
|
||||
- Excludes archived issues by default
|
||||
|
||||
### Result Limit
|
||||
|
||||
- Returns up to 50 results maximum
|
||||
- If more exist, shows "50+ issues found"
|
||||
- Refine search query for better targeting
|
||||
|
||||
### Usage Examples
|
||||
|
||||
```bash
|
||||
# Search for authentication tasks in My App
|
||||
/ccpm:utils:search my-app "authentication"
|
||||
|
||||
# Search for UI-related tasks in My Project
|
||||
/ccpm:utils:search my-project "UI component"
|
||||
|
||||
# Search for bug fixes in Personal Project
|
||||
/ccpm:utils:search personal-project "bug fix"
|
||||
|
||||
# Search all projects for "Redis"
|
||||
/ccpm:utils:search "" "Redis"
|
||||
```
|
||||
|
||||
### Performance
|
||||
|
||||
- Fast search via Linear API
|
||||
- Results appear immediately
|
||||
- No local caching needed
|
||||
- Always shows latest data
|
||||
|
||||
### Complementary Commands
|
||||
|
||||
- `/ccpm:utils:report <project>` - See all project tasks by status
|
||||
- `/ccpm:utils:status <id>` - View full task details
|
||||
- `/ccpm:utils:context <id>` - Load task context for work
|
||||
170
commands/utils:status.md
Normal file
170
commands/utils:status.md
Normal file
@@ -0,0 +1,170 @@
|
||||
---
|
||||
description: Show current status of a task from Linear with formatted display
|
||||
allowed-tools: [LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Status for: $1
|
||||
|
||||
Fetching current status from Linear...
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Status Display
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
Display formatted status:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Task Status: $1
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
🏷️ **Title**: [Issue title]
|
||||
🏢 **Team**: [Team name - Work or Personal]
|
||||
📦 **Project**: [Project name - My App, My Project, or Personal Project]
|
||||
📊 **Status**: [Current status]
|
||||
🏷️ **Labels**: [Comma-separated labels]
|
||||
👤 **Assignee**: [Assignee name if any]
|
||||
📅 **Created**: [Creation date]
|
||||
📅 **Updated**: [Last update date]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Description (Preview)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[First 5 lines of description...]
|
||||
|
||||
[If longer: "... (see full description in Linear)"]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Progress: [X of Y] subtasks completed ([percentage]%)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Checklist**:
|
||||
[x] Subtask 1 ✅ [Summary if available]
|
||||
[x] Subtask 2 ✅ [Summary if available]
|
||||
[ ] Subtask 3 ⏳ [Summary if available]
|
||||
[ ] Subtask 4 🚫 [Summary if available]
|
||||
[ ] Subtask 5
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💬 Recent Activity (Last 3 comments)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**[Date/Time]** - [Author]
|
||||
[Comment preview - first 2 lines...]
|
||||
|
||||
**[Date/Time]** - [Author]
|
||||
[Comment preview - first 2 lines...]
|
||||
|
||||
**[Date/Time]** - [Author]
|
||||
[Comment preview - first 2 lines...]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔗 Links & References
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
**Linear Issue**: https://linear.app/[workspace]/issue/$1
|
||||
[If found in description]:
|
||||
**Original Ticket**: [Jira/other link]
|
||||
**Related Docs**: [Documentation links]
|
||||
**Pull Request**: [PR link if available]
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Current Phase & Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[Based on current status, show appropriate next step]:
|
||||
|
||||
If status is "Planning":
|
||||
📋 Next: Begin implementation with /start $1
|
||||
|
||||
If status is "In Progress" and no blockers:
|
||||
⏳ In Progress: Continue working on subtasks
|
||||
📝 Update progress: /update $1 <index> <status> "<summary>"
|
||||
|
||||
If status is "In Progress" with blockers:
|
||||
🚫 Blocked: Address blocking issues
|
||||
🔧 Fix issues: /fix $1
|
||||
|
||||
If status is "Verification":
|
||||
🔍 Next: Run quality checks with /check $1
|
||||
✅ Then verify: /verify $1
|
||||
|
||||
If status is "Done":
|
||||
✅ Task Complete! No further action needed.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Status Indicators
|
||||
|
||||
**Checklist Items**:
|
||||
- `- [x] ✅` = Completed
|
||||
- `- [ ] ⏳` = In Progress
|
||||
- `- [ ] 🚫` = Blocked
|
||||
- `- [ ]` = Not Started
|
||||
|
||||
**Status Meanings**:
|
||||
- **Backlog**: Not started yet
|
||||
- **Planning**: Research and planning phase
|
||||
- **Ready**: Ready to implement
|
||||
- **In Progress**: Active development
|
||||
- **Verification**: Quality checks and verification
|
||||
- **Done**: Completed and verified
|
||||
- **Blocked**: Cannot proceed (with "blocked" label)
|
||||
|
||||
## Quick Actions Based on Status
|
||||
|
||||
Display relevant quick actions:
|
||||
|
||||
```
|
||||
💡 Quick Actions:
|
||||
|
||||
[If Planning]:
|
||||
/start $1 - Begin implementation
|
||||
|
||||
[If In Progress]:
|
||||
/update $1 <idx> <status> "msg" - Update a subtask
|
||||
/status $1 - Refresh status
|
||||
/check $1 - Run quality checks (when ready)
|
||||
|
||||
[If Verification]:
|
||||
/check $1 - Run quality checks
|
||||
/verify $1 - Verify task completion
|
||||
|
||||
[If Blocked]:
|
||||
/fix $1 - Start fixing issues
|
||||
/status $1 - Check current state
|
||||
```
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
Also display high-level stats:
|
||||
|
||||
```
|
||||
📊 Statistics:
|
||||
- Total subtasks: [N]
|
||||
- Completed: [X] ([percentage]%)
|
||||
- In progress: [Y]
|
||||
- Blocked: [Z]
|
||||
- Comments: [M]
|
||||
- Days in current status: [D]
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Status updates in real-time from Linear
|
||||
- Shows most recent activity
|
||||
- Highlights any blocking issues
|
||||
- Provides contextual next steps
|
||||
- Use this command frequently to stay updated
|
||||
65
commands/utils:sync-status.md
Normal file
65
commands/utils:sync-status.md
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
description: Sync Linear status to Jira with confirmation
|
||||
allowed-tools: [LinearMCP, AtlassianMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Syncing Status: $1
|
||||
|
||||
## 🚨 CRITICAL: Safety Rules
|
||||
|
||||
**WILL ASK FOR CONFIRMATION** before updating Jira!
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Fetch Linear Status
|
||||
- Get current Linear status, progress, completion summary
|
||||
|
||||
### Step 2: Determine Jira Status
|
||||
Map Linear → Jira:
|
||||
- Planning → "In Progress" or "To Do"
|
||||
- In Progress → "In Progress"
|
||||
- Verification → "In Review"
|
||||
- Done → "Done"
|
||||
|
||||
### Step 3: Preview Changes
|
||||
```
|
||||
🔄 Proposed Jira Update
|
||||
|
||||
Jira Ticket: [JIRA-ID]
|
||||
Current Status: [Current]
|
||||
New Status: [Proposed]
|
||||
|
||||
Comment to add:
|
||||
---
|
||||
Updated from Linear [WORK-123]
|
||||
Status: [status]
|
||||
Progress: [X/Y] subtasks ([%]%)
|
||||
[Brief summary if done]
|
||||
---
|
||||
```
|
||||
|
||||
### Step 4: Ask Confirmation
|
||||
Use **AskUserQuestion**:
|
||||
```javascript
|
||||
{questions: [{
|
||||
question: "Update Jira with this status?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{label: "Yes, Update Jira", description: "Proceed with update"},
|
||||
{label: "Edit Comment", description: "Let me edit the comment first"},
|
||||
{label: "Cancel", description: "Don't update Jira"}
|
||||
]
|
||||
}]}
|
||||
```
|
||||
|
||||
### Step 5: Execute if Confirmed
|
||||
- Use Atlassian MCP to update Jira
|
||||
- Add comment with Linear link
|
||||
- Show confirmation
|
||||
|
||||
## Notes
|
||||
- Read-only until user confirms
|
||||
- Always shows preview first
|
||||
- Includes link back to Linear
|
||||
441
commands/utils:update-checklist.md
Normal file
441
commands/utils:update-checklist.md
Normal file
@@ -0,0 +1,441 @@
|
||||
---
|
||||
description: Update Implementation Checklist items in Linear issue description
|
||||
allowed-tools: [LinearMCP, AskUserQuestion, Bash]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Update Checklist: $1
|
||||
|
||||
Updating Implementation Checklist directly in the Linear issue description.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (our internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Checklist Update Workflow
|
||||
|
||||
### Step 1: Fetch Linear Issue
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
Extract:
|
||||
- Full description (markdown)
|
||||
- Current status
|
||||
- Title
|
||||
|
||||
### Step 2: Parse Checklist from Description
|
||||
|
||||
Look for the Implementation Checklist section in the description using these markers:
|
||||
|
||||
**Pattern 1: Marker Comments (Preferred)**
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1: Description
|
||||
- [x] Task 2: Description (completed)
|
||||
- [ ] Task 3: Description
|
||||
<!-- ccpm-checklist-end -->
|
||||
```
|
||||
|
||||
**Pattern 2: Header-Based (Fallback)**
|
||||
```markdown
|
||||
## ✅ Implementation Checklist
|
||||
- [ ] Task 1
|
||||
- [ ] Task 2
|
||||
...
|
||||
(until next ## header or end of content)
|
||||
```
|
||||
|
||||
**Parsing Logic:**
|
||||
1. Search for `<!-- ccpm-checklist-start -->` marker
|
||||
2. If found, extract all lines between start and end markers
|
||||
3. If not found, look for "## ✅ Implementation Checklist" or similar headers
|
||||
4. Extract all checklist items (lines starting with `- [ ]` or `- [x]`)
|
||||
5. Parse each item to extract:
|
||||
- Index (0-based position)
|
||||
- Status (checked or unchecked)
|
||||
- Content (the text after the checkbox)
|
||||
|
||||
**Edge Cases:**
|
||||
- No checklist found → Display warning, offer to skip
|
||||
- Corrupted format → Attempt recovery, show warning
|
||||
- Multiple checklists → Use marker comments to identify the right one
|
||||
- Empty checklist → Display error
|
||||
|
||||
### Step 3: Display Current Checklist
|
||||
|
||||
Show current state with indices:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Current Implementation Checklist ($1)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Progress: X% (Y/Z completed)
|
||||
|
||||
0. [ ] Task 1: Description
|
||||
1. [x] Task 2: Description ✅
|
||||
2. [ ] Task 3: Description
|
||||
3. [ ] Task 4: Description
|
||||
4. [x] Task 5: Description ✅
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Choose Update Mode
|
||||
|
||||
First, ask user if they want to mark items complete or rollback (uncheck) items:
|
||||
|
||||
Use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "What would you like to do with the checklist?",
|
||||
header: "Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Mark items complete",
|
||||
description: "Check off completed tasks (default)"
|
||||
},
|
||||
{
|
||||
label: "Rollback items",
|
||||
description: "Uncheck incorrectly marked items"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Interactive Selection
|
||||
|
||||
**If "Mark items complete" selected:**
|
||||
|
||||
Use **AskUserQuestion** with multi-select to let user choose which items to check:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Which checklist items did you complete? (Select all that apply)",
|
||||
header: "Completed",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{
|
||||
label: "0: Task 1: Description",
|
||||
description: "Mark this task as complete"
|
||||
},
|
||||
{
|
||||
label: "2: Task 3: Description",
|
||||
description: "Mark this task as complete"
|
||||
},
|
||||
{
|
||||
label: "3: Task 4: Description",
|
||||
description: "Mark this task as complete"
|
||||
}
|
||||
// Only show UNCHECKED items
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**If "Rollback items" selected:**
|
||||
|
||||
Use **AskUserQuestion** with multi-select to let user choose which items to uncheck:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Which items were incorrectly marked complete? (Select all to rollback)",
|
||||
header: "Rollback",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{
|
||||
label: "1: Task 2: Description",
|
||||
description: "Uncheck this item (mark incomplete)"
|
||||
},
|
||||
{
|
||||
label: "4: Task 5: Description",
|
||||
description: "Uncheck this item (mark incomplete)"
|
||||
}
|
||||
// Only show CHECKED items
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Parse user selections:**
|
||||
- Extract indices from selected options (first number before ":")
|
||||
- Store as array of indices
|
||||
- Store mode: "complete" or "rollback"
|
||||
|
||||
### Step 6: Update Checklist via Linear Operations Subagent
|
||||
|
||||
**Use the Task tool to update the checklist:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_checklist_items
|
||||
params:
|
||||
issue_id: "{issue ID from step 1}"
|
||||
indices: [{selected indices from step 5}]
|
||||
mark_complete: {true if mode is "complete", false if mode is "rollback"}
|
||||
add_comment: true # Document the change with a comment
|
||||
update_timestamp: true
|
||||
context:
|
||||
command: "utils:update-checklist"
|
||||
purpose: "Manual checklist update by user"
|
||||
```
|
||||
|
||||
**This operation will:**
|
||||
1. Use shared checklist helpers (`_shared-checklist-helpers.md`) for parsing
|
||||
2. Update the specified checkbox states (✓ or uncheck)
|
||||
3. Recalculate progress percentage automatically
|
||||
4. Update the progress line with current timestamp
|
||||
5. Add a comment documenting the changes
|
||||
6. Return structured result with before/after progress
|
||||
|
||||
**Example response:**
|
||||
```yaml
|
||||
success: true
|
||||
data:
|
||||
checklist_summary:
|
||||
items_updated: 2
|
||||
previous_progress: 20
|
||||
new_progress: 60
|
||||
completed: 3
|
||||
total: 5
|
||||
changed_items:
|
||||
- index: 1
|
||||
content: "Task 2: Description"
|
||||
previous_state: unchecked
|
||||
new_state: checked
|
||||
metadata:
|
||||
duration_ms: 320
|
||||
used_shared_helpers: true
|
||||
```
|
||||
|
||||
### Step 7: Display Confirmation
|
||||
|
||||
Show success message with details
|
||||
|
||||
**Timestamp**: [current date/time]
|
||||
```
|
||||
|
||||
**If mode is "rollback":**
|
||||
```markdown
|
||||
## 🔄 Checklist Rollback
|
||||
|
||||
**Progress**: X% → Y% (-Z%)
|
||||
|
||||
**Unmarked Items** (marked incomplete):
|
||||
- ⏪ Task 2: Description
|
||||
- ⏪ Task 3: Description
|
||||
|
||||
**Reason**: Incorrectly marked complete, rolling back for accuracy
|
||||
|
||||
**Timestamp**: [current date/time]
|
||||
```
|
||||
|
||||
3. **Track version history** in comment:
|
||||
- Include previous state
|
||||
- Include new state
|
||||
- Record who made the change and why
|
||||
|
||||
### Step 8: Display Confirmation
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ Checklist Updated Successfully!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: $1
|
||||
🔗 Linear: [issue URL]
|
||||
|
||||
📊 Progress: X% → Y% (+Z%)
|
||||
|
||||
✅ Marked Complete (N items):
|
||||
• Task 2: Description
|
||||
• Task 3: Description
|
||||
|
||||
📝 Updated in Linear:
|
||||
✅ Issue description updated
|
||||
✅ Progress comment added
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🎯 Next Actions
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. ⭐ Continue Implementation
|
||||
/ccpm:implementation:next $1
|
||||
|
||||
2. Sync Progress
|
||||
/ccpm:implementation:sync $1
|
||||
|
||||
3. Run Quality Checks
|
||||
/ccpm:verification:check $1
|
||||
|
||||
4. View Updated Status
|
||||
/ccpm:utils:status $1
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Helper Functions (Inline Logic)
|
||||
|
||||
### Parse Checklist
|
||||
|
||||
**Pseudocode:**
|
||||
```
|
||||
1. Get description text
|
||||
2. Find start marker: <!-- ccpm-checklist-start -->
|
||||
3. Find end marker: <!-- ccpm-checklist-end -->
|
||||
4. If markers found:
|
||||
- Extract lines between markers
|
||||
5. Else:
|
||||
- Find "## ✅ Implementation Checklist" or "## Implementation Checklist"
|
||||
- Extract lines until next ## header
|
||||
6. Filter lines that match: /^- \[([ x])\] (.+)$/
|
||||
7. Parse each line:
|
||||
- Extract checkbox state: [ ] or [x]
|
||||
- Extract content after checkbox
|
||||
- Store index, state, content
|
||||
8. Return array of checklist items
|
||||
```
|
||||
|
||||
### Calculate Completion
|
||||
|
||||
**Pseudocode:**
|
||||
```
|
||||
1. Count total items
|
||||
2. Count checked items (- [x])
|
||||
3. Calculate percentage: (checked / total) * 100
|
||||
4. Round to nearest integer
|
||||
5. Return percentage and counts
|
||||
```
|
||||
|
||||
### Update Checklist Items
|
||||
|
||||
**Pseudocode:**
|
||||
```
|
||||
1. Get current description
|
||||
2. Parse checklist (get start/end positions)
|
||||
3. For each index in indices_to_complete:
|
||||
- Find line in checklist section
|
||||
- Replace "- [ ]" with "- [x]"
|
||||
4. Calculate new completion %
|
||||
5. Find or create progress line
|
||||
6. Update progress line with new %
|
||||
7. Add/update timestamp
|
||||
8. Return modified description
|
||||
```
|
||||
|
||||
## Rollback Capability (Built-in)
|
||||
|
||||
The rollback feature is now fully integrated into the main workflow (Step 4):
|
||||
|
||||
**Features:**
|
||||
- ✅ Two-mode operation: Complete or Rollback
|
||||
- ✅ Rollback shows only checked items
|
||||
- ✅ Complete shows only unchecked items
|
||||
- ✅ Version history tracked in comments
|
||||
- ✅ Clear reasoning documented
|
||||
|
||||
**Use Cases:**
|
||||
- Accidentally marked wrong item complete
|
||||
- Item thought complete but needs more work
|
||||
- Task requirements changed, no longer complete
|
||||
- Quality issues discovered after marking complete
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Mark Multiple Items Complete
|
||||
|
||||
```bash
|
||||
/ccpm:utils:update-checklist PSN-26
|
||||
```
|
||||
|
||||
**Interactive Flow:**
|
||||
```
|
||||
Current Progress: 20% (1/5 completed)
|
||||
|
||||
Which items did you complete?
|
||||
[x] 0: Create checklist parser functions
|
||||
[ ] 2: Modify /ccpm:implementation:sync
|
||||
[ ] 3: Modify /ccpm:implementation:update
|
||||
|
||||
→ User selects items 0 and 2
|
||||
|
||||
✅ Updated! Progress: 20% → 60% (+40%)
|
||||
```
|
||||
|
||||
### Example 2: No Changes Needed
|
||||
|
||||
```bash
|
||||
/ccpm:utils:update-checklist PSN-26
|
||||
```
|
||||
|
||||
**Interactive Flow:**
|
||||
```
|
||||
Current Progress: 100% (5/5 completed)
|
||||
|
||||
All items complete! ✅
|
||||
|
||||
No changes needed.
|
||||
```
|
||||
|
||||
### Example 3: Rollback Mistake
|
||||
|
||||
```bash
|
||||
/ccpm:utils:update-checklist PSN-26
|
||||
```
|
||||
|
||||
**Interactive Flow:**
|
||||
```
|
||||
What would you like to do?
|
||||
( ) Mark items complete
|
||||
(●) Rollback items
|
||||
|
||||
Which items were incorrectly marked complete?
|
||||
[x] 3: Modify /ccpm:implementation:update
|
||||
[x] 4: Modify /ccpm:verification:check
|
||||
|
||||
→ User selects items 3 and 4
|
||||
|
||||
✅ Rolled back! Progress: 80% → 40% (-40%)
|
||||
|
||||
Comment added:
|
||||
"🔄 Checklist Rollback
|
||||
Unmarked: Task 3, Task 4
|
||||
Reason: Incorrectly marked complete, rolling back for accuracy"
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **Idempotent**: Running multiple times is safe
|
||||
- **Atomic**: Either all updates succeed or none
|
||||
- **Tracked**: Every change creates a comment for history
|
||||
- **Visible**: Progress visible in description, not just comments
|
||||
- **Flexible**: Works with or without marker comments
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
This command provides the core checklist update logic that other commands can reference:
|
||||
|
||||
- `/ccpm:implementation:sync` - Auto-suggest completed items based on git diff
|
||||
- `/ccpm:implementation:update` - Update specific item by index
|
||||
- `/ccpm:verification:check` - Check completion % before verification
|
||||
- `/ccpm:complete:finalize` - Require 100% before finalization
|
||||
328
commands/verification:check.md
Normal file
328
commands/verification:check.md
Normal file
@@ -0,0 +1,328 @@
|
||||
---
|
||||
description: Run quality checks - resolve IDE warnings, run linting, execute tests
|
||||
allowed-tools: [Bash, LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Quality Check for: $1
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:verify [issue-id]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Auto-detects issue from git branch if not provided
|
||||
- Runs both quality checks AND final verification in sequence
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
Running comprehensive quality checks before verification.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Quality Check Workflow
|
||||
|
||||
### Step 0: Check Implementation Checklist Completion
|
||||
|
||||
**BEFORE running quality checks**, verify the Implementation Checklist is complete:
|
||||
|
||||
**A) Fetch Linear Issue**
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
**B) Parse Checklist from Description**
|
||||
|
||||
Look for checklist using markers:
|
||||
```markdown
|
||||
<!-- ccpm-checklist-start -->
|
||||
- [ ] Task 1
|
||||
- [x] Task 2
|
||||
<!-- ccpm-checklist-end -->
|
||||
```
|
||||
|
||||
Or find "## ✅ Implementation Checklist" header.
|
||||
|
||||
**C) Calculate Completion Percentage**
|
||||
|
||||
Count total items vs. checked items:
|
||||
- Total items: Count all `- [ ]` and `- [x]` lines
|
||||
- Checked items: Count `- [x]` lines only
|
||||
- Percentage: (checked / total) × 100
|
||||
|
||||
**D) Display Checklist Status**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 Implementation Checklist Status
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Progress: X% (Y/Z completed)
|
||||
|
||||
[If < 100%]
|
||||
⚠️ Checklist is not complete!
|
||||
|
||||
Remaining Items:
|
||||
- [ ] Task 3: Description
|
||||
- [ ] Task 5: Description
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**E) Prompt User if Incomplete**
|
||||
|
||||
If completion < 100%, use **AskUserQuestion**:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Checklist shows X% complete. What would you like to do?",
|
||||
header: "Checklist",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Update checklist first",
|
||||
description: "Mark completed items before verification"
|
||||
},
|
||||
{
|
||||
label: "Continue anyway",
|
||||
description: "Run quality checks with incomplete checklist (warning will be logged)"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Go back and complete remaining items"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**If "Update checklist first" selected:**
|
||||
- Display unchecked items
|
||||
- Use **AskUserQuestion** multi-select to let user mark items complete
|
||||
- Update description with changes
|
||||
- Then proceed to quality checks
|
||||
|
||||
**If "Continue anyway" selected:**
|
||||
- Log warning in Linear comment:
|
||||
```markdown
|
||||
⚠️ Quality checks run with incomplete checklist (X% complete)
|
||||
```
|
||||
- Add "incomplete-checklist" label
|
||||
- Proceed to quality checks
|
||||
|
||||
**If "Cancel" selected:**
|
||||
- Display message: "Complete remaining checklist items, then run /ccpm:verification:check $1 again"
|
||||
- Exit without running checks
|
||||
|
||||
**If 100% complete:**
|
||||
- Display: ✅ Checklist complete! Proceeding with quality checks...
|
||||
- Continue to Step 1
|
||||
|
||||
### Step 1: IDE Warnings & Errors
|
||||
|
||||
Check and resolve:
|
||||
- ✅ All IDE warnings
|
||||
- ✅ All compilation errors
|
||||
- ✅ Unused imports and variables
|
||||
- ✅ Type consistency
|
||||
- ✅ Missing dependencies
|
||||
|
||||
Use your IDE or LSP to identify issues, then fix them.
|
||||
|
||||
Display results:
|
||||
```
|
||||
🔧 IDE Checks:
|
||||
[List any warnings/errors found and fixed]
|
||||
```
|
||||
|
||||
### Step 2: Linting
|
||||
|
||||
Run project linter based on project type:
|
||||
|
||||
**For JavaScript/TypeScript projects**:
|
||||
```bash
|
||||
!npm run lint
|
||||
# or
|
||||
!yarn lint
|
||||
# or
|
||||
!pnpm lint
|
||||
```
|
||||
|
||||
**For Python projects**:
|
||||
```bash
|
||||
!pylint src/
|
||||
# or
|
||||
!flake8 src/
|
||||
# or
|
||||
!ruff check .
|
||||
```
|
||||
|
||||
**Auto-fix if available**:
|
||||
```bash
|
||||
!npm run lint:fix
|
||||
# or
|
||||
!yarn lint --fix
|
||||
```
|
||||
|
||||
Display results:
|
||||
```
|
||||
✨ Linting:
|
||||
[Show linting results]
|
||||
```
|
||||
|
||||
If linting fails, fix all issues before proceeding.
|
||||
|
||||
### Step 3: Run Tests
|
||||
|
||||
Execute all test suites:
|
||||
|
||||
**For JavaScript/TypeScript**:
|
||||
```bash
|
||||
!npm test
|
||||
# or
|
||||
!yarn test
|
||||
# or
|
||||
!pnpm test
|
||||
```
|
||||
|
||||
**For Python**:
|
||||
```bash
|
||||
!pytest
|
||||
# or
|
||||
!python -m unittest
|
||||
```
|
||||
|
||||
**Optional - Check coverage**:
|
||||
```bash
|
||||
!npm run test:coverage
|
||||
```
|
||||
|
||||
Display results:
|
||||
```
|
||||
🧪 Tests:
|
||||
[X/Y tests passed]
|
||||
[Coverage: Z%]
|
||||
```
|
||||
|
||||
If any tests fail, fix them before proceeding.
|
||||
|
||||
### Step 4: Project-Specific Checks
|
||||
|
||||
Run any additional project-specific checks:
|
||||
- Build verification
|
||||
- Type checking (if separate from linting)
|
||||
- Security scans
|
||||
- Integration tests
|
||||
|
||||
### Step 5: Update Linear
|
||||
|
||||
Use **Linear MCP**:
|
||||
|
||||
**If ALL checks passed**:
|
||||
1. Update status to: **Verification**
|
||||
2. Remove label: **implementation**
|
||||
3. Add label: **verification**
|
||||
4. Add comment:
|
||||
|
||||
```markdown
|
||||
## ✅ Quality Checks Passed
|
||||
|
||||
### Results:
|
||||
- ✅ IDE checks: PASS
|
||||
- ✅ Linting: PASS
|
||||
- ✅ Tests: PASS ([X]/[Y] tests)
|
||||
- ✅ Coverage: [Z]%
|
||||
|
||||
Ready for verification!
|
||||
```
|
||||
|
||||
**If ANY checks failed**:
|
||||
1. Keep status: **In Progress**
|
||||
2. Add label: **blocked**
|
||||
3. Add comment:
|
||||
|
||||
```markdown
|
||||
## ❌ Quality Checks Failed
|
||||
|
||||
### Issues Found:
|
||||
- [ ] IDE warnings: [count and description]
|
||||
- [ ] Linting errors: [count and description]
|
||||
- [ ] Test failures: [count and description]
|
||||
|
||||
### Action Required:
|
||||
Fix the issues above before proceeding to verification.
|
||||
```
|
||||
|
||||
### Step 6: Display Summary
|
||||
|
||||
```
|
||||
📊 Quality Check Results for $1
|
||||
|
||||
✅ IDE Checks: [PASS/FAIL]
|
||||
✅ Linting: [PASS/FAIL]
|
||||
✅ Tests: [PASS/FAIL - X/Y tests]
|
||||
|
||||
[If all passed]
|
||||
🎉 All checks passed!
|
||||
Next step: /verify $1
|
||||
|
||||
[If any failed]
|
||||
❌ Some checks failed - please fix issues and run /check again
|
||||
```
|
||||
|
||||
## Common Commands by Project Type
|
||||
|
||||
### React/Next.js
|
||||
```bash
|
||||
npm run lint
|
||||
npm run type-check
|
||||
npm test
|
||||
npm run build
|
||||
```
|
||||
|
||||
### React Native
|
||||
```bash
|
||||
npm run lint
|
||||
npm test
|
||||
npm run ios # or android - build check
|
||||
```
|
||||
|
||||
### Node.js/Express
|
||||
```bash
|
||||
npm run lint
|
||||
npm run type-check
|
||||
npm test
|
||||
```
|
||||
|
||||
### Python/Django
|
||||
```bash
|
||||
flake8 .
|
||||
mypy .
|
||||
pytest
|
||||
python manage.py check
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Fix all issues before moving to verification
|
||||
- If build fails, that's a blocking issue
|
||||
- Test failures must be resolved, not skipped
|
||||
- Linting rules should not be disabled to pass checks
|
||||
- Use `--fix` flags when available for auto-fixes
|
||||
286
commands/verification:fix.md
Normal file
286
commands/verification:fix.md
Normal file
@@ -0,0 +1,286 @@
|
||||
---
|
||||
description: Fix verification failures by identifying issues and invoking relevant agents
|
||||
allowed-tools: [Bash, LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Fixing Verification Failures: $1
|
||||
|
||||
Addressing issues found during verification.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Fix Workflow
|
||||
|
||||
### Step 1: Fetch Failure Details
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
Find the verification failure comment and extract:
|
||||
|
||||
- ❌ Critical issues
|
||||
- ⚠️ Non-critical issues
|
||||
- 📋 Required actions
|
||||
- 💡 Recommendations
|
||||
|
||||
Display:
|
||||
|
||||
```
|
||||
📋 Issues to Fix:
|
||||
|
||||
Critical:
|
||||
1. [Issue 1]
|
||||
2. [Issue 2]
|
||||
|
||||
Non-Critical:
|
||||
3. [Issue 3]
|
||||
4. [Issue 4]
|
||||
```
|
||||
|
||||
### Step 2: Analyze and Map to Agents
|
||||
|
||||
For each issue, determine:
|
||||
|
||||
1. **What** is the problem
|
||||
2. **Where** in the code it exists
|
||||
3. **Which agent** should fix it
|
||||
4. **Priority** (critical first)
|
||||
|
||||
Create fix plan:
|
||||
|
||||
```
|
||||
🔧 Fix Plan:
|
||||
|
||||
Priority 1 (Critical):
|
||||
- Issue 1 → backend-agent
|
||||
Files: src/api/auth.ts
|
||||
Problem: JWT validation not handling expired tokens
|
||||
|
||||
- Issue 2 → frontend-agent
|
||||
Files: src/components/Login.tsx
|
||||
Problem: Missing error state handling
|
||||
|
||||
Priority 2 (Non-Critical):
|
||||
- Issue 3 → integration-agent
|
||||
Files: src/services/payment.ts
|
||||
Problem: Retry logic could be improved
|
||||
|
||||
- Issue 4 → devops-agent
|
||||
Files: .github/workflows/deploy.yml
|
||||
Problem: Missing environment validation
|
||||
```
|
||||
|
||||
### Step 3: Invoke Agents to Fix Issues
|
||||
|
||||
For each issue (in priority order):
|
||||
|
||||
**Invoke the assigned agent** with:
|
||||
|
||||
**Context**:
|
||||
|
||||
```
|
||||
Task: $1
|
||||
Issue to fix: [specific issue description]
|
||||
Files affected: [list of files]
|
||||
Current behavior: [what's wrong]
|
||||
Expected behavior: [what should happen]
|
||||
Related code: [relevant code context]
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
|
||||
- Fix the specific issue
|
||||
- Ensure no new issues introduced
|
||||
- Follow project patterns
|
||||
- Add/update tests if needed
|
||||
- Verify fix works
|
||||
|
||||
**Example invocation**:
|
||||
|
||||
```
|
||||
Invoke backend-agent to fix JWT validation issue:
|
||||
|
||||
Context:
|
||||
- Task: $1
|
||||
- Issue: JWT validation not handling expired tokens properly
|
||||
- File: src/api/auth.ts
|
||||
- Problem: Expired tokens returning 500 instead of 401
|
||||
- Expected: Return 401 Unauthorized for expired tokens
|
||||
|
||||
Requirements:
|
||||
- Add proper expiration checking
|
||||
- Return correct HTTP status
|
||||
- Add error message
|
||||
- Update tests to cover this case
|
||||
- Ensure no security issues
|
||||
|
||||
Success Criteria:
|
||||
- Expired tokens return 401
|
||||
- Error message is clear
|
||||
- Tests pass
|
||||
- No regressions
|
||||
```
|
||||
|
||||
### Step 4: Update Progress
|
||||
|
||||
After each issue is fixed, use:
|
||||
|
||||
```
|
||||
/update $1 <subtask-index> completed "Fixed: [issue description]"
|
||||
```
|
||||
|
||||
Or add comments to Linear manually:
|
||||
|
||||
```markdown
|
||||
## 🔧 Fix Progress
|
||||
|
||||
### Completed:
|
||||
|
||||
- ✅ Issue 1: Fixed JWT validation - now returns 401 for expired tokens
|
||||
- ✅ Issue 2: Added error state handling in Login component
|
||||
|
||||
### In Progress:
|
||||
|
||||
- ⏳ Issue 3: Improving retry logic in payment service
|
||||
|
||||
### Todo:
|
||||
|
||||
- [ ] Issue 4: Add environment validation to deployment
|
||||
```
|
||||
|
||||
### Step 5: Update Linear Status
|
||||
|
||||
Use **Linear MCP** to:
|
||||
|
||||
1. Remove label: **blocked**
|
||||
2. Keep status: **In Progress**
|
||||
3. Add comment:
|
||||
|
||||
```markdown
|
||||
## 🔧 Fixing Verification Issues
|
||||
|
||||
### Issues Being Addressed
|
||||
|
||||
1. [Issue 1] → Assigned to: backend-agent ✅ FIXED
|
||||
2. [Issue 2] → Assigned to: frontend-agent ✅ FIXED
|
||||
3. [Issue 3] → Assigned to: integration-agent ⏳ IN PROGRESS
|
||||
4. [Issue 4] → Assigned to: devops-agent
|
||||
|
||||
### Progress
|
||||
|
||||
- ✅ Critical issues: [X/Y] fixed
|
||||
- ⏳ Non-critical issues: [M/N] fixed
|
||||
|
||||
---
|
||||
|
||||
Will run quality checks and re-verify once all fixes complete.
|
||||
```
|
||||
|
||||
### Step 6: Coordinate Parallel Fixes
|
||||
|
||||
If multiple agents can work in parallel:
|
||||
|
||||
- Invoke them simultaneously
|
||||
- Track progress for each
|
||||
- Wait for all to complete before proceeding
|
||||
|
||||
### Step 7: After All Fixes Complete
|
||||
|
||||
Display summary:
|
||||
|
||||
```
|
||||
✅ All Issues Fixed!
|
||||
|
||||
Critical Issues Resolved:
|
||||
- ✅ Issue 1: [brief description]
|
||||
- ✅ Issue 2: [brief description]
|
||||
|
||||
Non-Critical Issues Resolved:
|
||||
- ✅ Issue 3: [brief description]
|
||||
- ✅ Issue 4: [brief description]
|
||||
|
||||
🔍 Next Steps:
|
||||
1. Run: /check $1 (quality checks)
|
||||
2. Then: /verify $1 (re-verification)
|
||||
```
|
||||
|
||||
## Agent Selection Guide
|
||||
|
||||
**backend-agent**:
|
||||
|
||||
- API issues
|
||||
- Database problems
|
||||
- Authentication/authorization bugs
|
||||
- Server-side logic errors
|
||||
|
||||
**frontend-agent**:
|
||||
|
||||
- UI bugs
|
||||
- Component issues
|
||||
- State management problems
|
||||
- Client-side logic errors
|
||||
|
||||
**mobile-agent**:
|
||||
|
||||
- React Native issues
|
||||
- Platform-specific bugs
|
||||
- Mobile UI problems
|
||||
|
||||
**integration-agent**:
|
||||
|
||||
- API integration issues
|
||||
- Third-party service problems
|
||||
- Data sync errors
|
||||
|
||||
**devops-agent**:
|
||||
|
||||
- CI/CD issues
|
||||
- Deployment problems
|
||||
- Infrastructure errors
|
||||
- Environment configuration
|
||||
|
||||
**database-agent**:
|
||||
|
||||
- Schema issues
|
||||
- Query problems
|
||||
- Migration errors
|
||||
- Performance issues
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Fix critical issues first** - They block completion
|
||||
2. **One agent per issue** - Don't mix responsibilities
|
||||
3. **Provide full context** - Help agents understand the problem
|
||||
4. **Test after each fix** - Ensure fix works
|
||||
5. **Update Linear frequently** - Keep progress visible
|
||||
6. **Parallel when possible** - Speed up fixes
|
||||
7. **Re-verify after all fixes** - Ensure everything works
|
||||
|
||||
## After Fixing
|
||||
|
||||
Once all issues are fixed:
|
||||
|
||||
```bash
|
||||
# Run quality checks
|
||||
/check $1
|
||||
|
||||
# If checks pass, re-verify
|
||||
/verify $1
|
||||
|
||||
# Should pass this time! 🎉
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Don't skip non-critical issues - fix them all
|
||||
- Each fix should have tests
|
||||
- Verify no regressions introduced
|
||||
- Update documentation if needed
|
||||
- Keep fixes focused and minimal
|
||||
363
commands/verification:verify.md
Normal file
363
commands/verification:verify.md
Normal file
@@ -0,0 +1,363 @@
|
||||
---
|
||||
description: Verify completed work with verification agent - final review before completion
|
||||
allowed-tools: [Bash, LinearMCP]
|
||||
argument-hint: <linear-issue-id>
|
||||
---
|
||||
|
||||
# Verifying Task: $1
|
||||
|
||||
## 💡 Hint: Try the New Natural Command
|
||||
|
||||
For a simpler workflow, consider using:
|
||||
|
||||
```bash
|
||||
/ccpm:verify [issue-id]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Auto-detects issue from git branch if not provided
|
||||
- Runs both quality checks AND final verification in sequence
|
||||
- Part of the 6-command natural workflow
|
||||
- See: [Quick Start Guide](./README.md#quick-start)
|
||||
|
||||
This command still works perfectly! The hint is just a suggestion.
|
||||
|
||||
---
|
||||
|
||||
Running final verification before marking task as complete.
|
||||
|
||||
## 🚨 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.
|
||||
|
||||
- ✅ **Linear** operations are permitted (internal tracking)
|
||||
- ⛔ **External PM systems** require user confirmation for write operations
|
||||
|
||||
## Verification Workflow
|
||||
|
||||
### Step 1: Fetch Task Requirements
|
||||
|
||||
Use **Linear MCP** to get issue: $1
|
||||
|
||||
Review:
|
||||
- Original requirements
|
||||
- All checklist items
|
||||
- Expected outcomes
|
||||
- Success criteria
|
||||
|
||||
### Step 2: Invoke Verification Agent
|
||||
|
||||
From **CLAUDE.md**, invoke the **verification-agent** with:
|
||||
|
||||
**Context**:
|
||||
```
|
||||
Task: $1
|
||||
Requirements: [from Linear description]
|
||||
All changes made: [list of modified files]
|
||||
All completed subtasks: [from checklist]
|
||||
```
|
||||
|
||||
**Verification Checklist**:
|
||||
- [ ] All requirements from original ticket met
|
||||
- [ ] All checklist items completed
|
||||
- [ ] Code follows project patterns and conventions
|
||||
- [ ] No regressions in existing functionality
|
||||
- [ ] All tests passing
|
||||
- [ ] No security vulnerabilities introduced
|
||||
- [ ] Performance meets expectations
|
||||
- [ ] Error handling is comprehensive
|
||||
- [ ] Documentation updated if needed
|
||||
- [ ] Code is production-ready
|
||||
|
||||
**Ask verification-agent to**:
|
||||
1. Review all changes against requirements
|
||||
2. Run comprehensive test suite
|
||||
3. Check for regressions
|
||||
4. Validate against original ticket
|
||||
5. Verify code quality standards
|
||||
6. Check security best practices
|
||||
7. Assess performance impact
|
||||
|
||||
### Step 3: Collect Verification Results
|
||||
|
||||
The verification-agent should provide:
|
||||
- ✅ What passed verification
|
||||
- ❌ What failed verification (if any)
|
||||
- 🔍 Any concerns or recommendations
|
||||
- 📊 Test results
|
||||
- 🚨 Critical issues found (if any)
|
||||
|
||||
### Step 4a: If Verification PASSES
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
Use **Linear MCP** to update issue status and labels:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// Get team ID from issue
|
||||
const teamId = issue.team.id;
|
||||
|
||||
// Get valid "Done" state ID
|
||||
const doneStateId = await getValidStateId(teamId, "Done");
|
||||
|
||||
// Get current labels
|
||||
const currentLabels = issue.labels || [];
|
||||
const currentLabelIds = currentLabels.map(l => l.id);
|
||||
|
||||
// Find labels to remove
|
||||
const verificationLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "verification"
|
||||
);
|
||||
const blockedLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "blocked"
|
||||
);
|
||||
|
||||
// Build new label list: remove verification and blocked
|
||||
let newLabelIds = currentLabelIds.filter(id =>
|
||||
id !== verificationLabel?.id && id !== blockedLabel?.id
|
||||
);
|
||||
|
||||
// Update issue with Done status and cleaned labels
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "update_issue",
|
||||
args: {
|
||||
id: issue.id,
|
||||
stateId: doneStateId,
|
||||
labelIds: newLabelIds
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Linear issue updated:");
|
||||
console.log(" Status: Done");
|
||||
console.log(" Labels: verification and blocked removed");
|
||||
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to update Linear issue:", error.message);
|
||||
console.warn("⚠️ Task is verified but status may not be updated in Linear.");
|
||||
console.log(" You can manually update status to Done if needed.");
|
||||
}
|
||||
```
|
||||
|
||||
**Add completion comment**:
|
||||
|
||||
```javascript
|
||||
const commentBody = `## ✅ Verification Complete - Task Done!
|
||||
|
||||
### Verification Results
|
||||
✅ All requirements met
|
||||
✅ All tests passing (${testResults.passed}/${testResults.total} tests)
|
||||
✅ No regressions detected
|
||||
✅ Code quality standards met
|
||||
✅ Security best practices followed
|
||||
✅ Performance acceptable
|
||||
|
||||
### Implementation Summary
|
||||
${verificationReport.summary}
|
||||
|
||||
### Changes Made
|
||||
**Files Modified**:
|
||||
${verificationReport.filesModified.map(f => `- ${f.path} - ${f.description}`).join('\n')}
|
||||
|
||||
**Key Features Implemented**:
|
||||
${verificationReport.features.map(f => `- ${f}`).join('\n')}
|
||||
|
||||
### Test Coverage
|
||||
- Unit tests: ${testResults.unit} added/updated
|
||||
- Integration tests: ${testResults.integration} added/updated
|
||||
- All tests passing: ✅
|
||||
|
||||
### Related Links
|
||||
- Linear: ${issue.url}
|
||||
${jiraLink ? `- Jira: ${jiraLink}` : ''}
|
||||
${prLink ? `- Pull request: ${prLink}` : ''}
|
||||
|
||||
---
|
||||
|
||||
**Task completed successfully!** 🎉
|
||||
`;
|
||||
|
||||
try {
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "create_comment",
|
||||
args: {
|
||||
issueId: issue.id,
|
||||
body: commentBody
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Verification results added to Linear comments");
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to add comment:", error.message);
|
||||
// Not critical, continue
|
||||
}
|
||||
```
|
||||
|
||||
Display success message:
|
||||
```
|
||||
🎉 Verification Passed!
|
||||
|
||||
✅ Task $1 is complete and verified
|
||||
✅ Status updated to Done
|
||||
✅ All requirements met
|
||||
|
||||
Summary: [brief summary of work]
|
||||
```
|
||||
|
||||
### Step 4b: If Verification FAILS
|
||||
|
||||
**READ**: `commands/_shared-linear-helpers.md`
|
||||
|
||||
Use **Linear MCP** to update issue and add blocker:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
// Get team ID from issue
|
||||
const teamId = issue.team.id;
|
||||
|
||||
// Get or create "blocked" label
|
||||
const blockedLabel = await getOrCreateLabel(teamId, "blocked", {
|
||||
color: "#eb5757",
|
||||
description: "CCPM: Task blocked, needs resolution"
|
||||
});
|
||||
|
||||
// Get current labels
|
||||
const currentLabels = issue.labels || [];
|
||||
const currentLabelIds = currentLabels.map(l => l.id);
|
||||
|
||||
// Find verification label to remove
|
||||
const verificationLabel = currentLabels.find(l =>
|
||||
l.name.toLowerCase() === "verification"
|
||||
);
|
||||
|
||||
// Build new label list: remove verification, add blocked
|
||||
let newLabelIds = currentLabelIds.filter(id =>
|
||||
id !== verificationLabel?.id
|
||||
);
|
||||
|
||||
// Add blocked label if not already present
|
||||
if (!currentLabels.some(l => l.name.toLowerCase() === "blocked")) {
|
||||
newLabelIds.push(blockedLabel.id);
|
||||
}
|
||||
|
||||
// Keep status as "In Progress" - don't change it
|
||||
// Just update labels
|
||||
await mcp__agent-mcp-gateway__execute_tool({
|
||||
server: "linear",
|
||||
tool: "update_issue",
|
||||
args: {
|
||||
id: issue.id,
|
||||
labelIds: newLabelIds
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ Linear issue updated:");
|
||||
console.log(" Status: In Progress (unchanged)");
|
||||
console.log(" Labels: blocked added, verification removed");
|
||||
|
||||
} catch (error) {
|
||||
console.error("⚠️ Failed to update Linear issue:", error.message);
|
||||
console.warn("⚠️ Verification failed but status may not be updated.");
|
||||
console.log(" Please manually add 'blocked' label if needed.");
|
||||
}
|
||||
```
|
||||
|
||||
**Add failure comment**:
|
||||
4. Add failure comment:
|
||||
|
||||
```markdown
|
||||
## ❌ Verification Failed
|
||||
|
||||
### Issues Found
|
||||
|
||||
**Critical Issues**:
|
||||
1. [Issue 1 description]
|
||||
2. [Issue 2 description]
|
||||
|
||||
**Non-Critical Issues**:
|
||||
1. [Issue 3 description]
|
||||
2. [Issue 4 description]
|
||||
|
||||
### Required Actions
|
||||
|
||||
- [ ] **Action 1**: [What needs to be fixed]
|
||||
- [ ] **Action 2**: [What needs to be fixed]
|
||||
- [ ] **Action 3**: [What needs to be fixed]
|
||||
|
||||
### Recommendations
|
||||
- [Recommendation 1]
|
||||
- [Recommendation 2]
|
||||
|
||||
---
|
||||
|
||||
**Next Steps**: Fix the issues above, then run quality checks and verification again.
|
||||
```
|
||||
|
||||
Display failure message:
|
||||
```
|
||||
❌ Verification Failed for $1
|
||||
|
||||
Issues found:
|
||||
[List critical issues]
|
||||
|
||||
Next steps:
|
||||
1. Fix the issues listed above
|
||||
2. Run: /fix $1
|
||||
3. After fixes: /check $1
|
||||
4. Then: /verify $1 again
|
||||
```
|
||||
|
||||
## Post-Verification
|
||||
|
||||
### If Passed
|
||||
- Task is complete ✅
|
||||
- Can move to next task
|
||||
- Consider creating PR if not done
|
||||
|
||||
### If Failed
|
||||
- Run `/fix $1` to start fixing issues
|
||||
- Address all critical issues first
|
||||
- Re-run quality checks after fixes
|
||||
- Re-run verification
|
||||
|
||||
## Verification Standards
|
||||
|
||||
The verification-agent should check:
|
||||
|
||||
**Functionality**:
|
||||
- All acceptance criteria met
|
||||
- Edge cases handled
|
||||
- Error scenarios covered
|
||||
|
||||
**Code Quality**:
|
||||
- Follows project patterns
|
||||
- Readable and maintainable
|
||||
- Properly documented
|
||||
- No code smells
|
||||
|
||||
**Testing**:
|
||||
- Comprehensive test coverage
|
||||
- All tests passing
|
||||
- No flaky tests
|
||||
|
||||
**Security**:
|
||||
- No vulnerabilities introduced
|
||||
- Input validation present
|
||||
- Authorization checks in place
|
||||
|
||||
**Performance**:
|
||||
- No performance regressions
|
||||
- Efficient algorithms used
|
||||
- Database queries optimized
|
||||
|
||||
## Notes
|
||||
|
||||
- Verification is the final gate before completion
|
||||
- All issues must be addressed
|
||||
- Critical issues block completion
|
||||
- Non-critical issues can be documented as follow-ups
|
||||
- Be thorough - this ensures production-ready code
|
||||
752
commands/verify.md
Normal file
752
commands/verify.md
Normal file
@@ -0,0 +1,752 @@
|
||||
---
|
||||
description: Smart verification command - run quality checks and final verification
|
||||
allowed-tools: [Bash, Task, AskUserQuestion]
|
||||
argument-hint: "[issue-id]"
|
||||
---
|
||||
|
||||
# /ccpm:verify - Smart Verification
|
||||
|
||||
**Token Budget:** ~2,800 tokens (vs ~8,000 baseline) | **65% reduction**
|
||||
|
||||
Intelligent verification command that runs quality checks followed by final verification in sequence.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Auto-detect issue from git branch
|
||||
/ccpm:verify
|
||||
|
||||
# Explicit issue ID
|
||||
/ccpm:verify PSN-29
|
||||
|
||||
# Examples
|
||||
/ccpm:verify PROJ-123 # Verify PROJ-123
|
||||
/ccpm:verify # Auto-detect from branch name "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 || !/^[A-Z]+-\d+$/.test(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 branch name
|
||||
|
||||
Current branch: ${branch}
|
||||
|
||||
Usage: /ccpm:verify [ISSUE-ID]
|
||||
|
||||
Examples:
|
||||
/ccpm:verify PSN-29
|
||||
/ccpm:verify # Auto-detect from branch
|
||||
`);
|
||||
}
|
||||
|
||||
issueId = match[1];
|
||||
console.log(`📌 Detected issue from branch: ${issueId}\n`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Fetch Issue via Linear Subagent
|
||||
|
||||
**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: "verify"
|
||||
```
|
||||
|
||||
**Store response as `issue` object** containing:
|
||||
- `issue.id`, `issue.identifier`, `issue.title`
|
||||
- `issue.description` (with checklist)
|
||||
- `issue.state.name`, `issue.state.id`
|
||||
- `issue.labels`, `issue.team.id`
|
||||
|
||||
**Error handling:**
|
||||
```javascript
|
||||
if (subagentResponse.error) {
|
||||
console.log(`❌ Error fetching issue: ${subagentResponse.error.message}`);
|
||||
console.log('\nSuggestions:');
|
||||
subagentResponse.error.suggestions.forEach(s => console.log(` - ${s}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const issue = subagentResponse.issue;
|
||||
```
|
||||
|
||||
### Step 3: Display Verification Flow
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔍 Smart Verify Command
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: ${issueId} - ${issue.title}
|
||||
📊 Status: ${issue.state.name}
|
||||
|
||||
Verification Flow:
|
||||
──────────────────
|
||||
1. Quality Checks (linting, tests, build)
|
||||
2. Final Verification (code review, security)
|
||||
|
||||
Starting verification...
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Step 4: Check Implementation Checklist
|
||||
|
||||
Parse checklist from issue description:
|
||||
|
||||
```javascript
|
||||
const description = issue.description || '';
|
||||
|
||||
// Extract checklist items
|
||||
const checklistItems = description.match(/- \[([ x])\] .+/g) || [];
|
||||
const totalItems = checklistItems.length;
|
||||
const completedItems = checklistItems.filter(item => item.includes('[x]')).length;
|
||||
const progress = totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 100;
|
||||
|
||||
console.log(`📋 Checklist: ${progress}% (${completedItems}/${totalItems} items)\n`);
|
||||
```
|
||||
|
||||
**If checklist incomplete (< 100%), prompt user:**
|
||||
|
||||
```javascript
|
||||
if (progress < 100) {
|
||||
const incompleteItems = checklistItems.filter(item => item.includes('[ ]'));
|
||||
|
||||
console.log('⚠️ Checklist incomplete!\n');
|
||||
console.log('Remaining items:');
|
||||
incompleteItems.forEach(item => {
|
||||
console.log(` ${item.replace('- [ ] ', '⏳ ')}`);
|
||||
});
|
||||
console.log('');
|
||||
|
||||
// Ask user what to do
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Checklist is ${progress}% complete. What would you like to do?`,
|
||||
header: "Checklist",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Continue anyway",
|
||||
description: "Run checks despite incomplete checklist (warning will be logged)"
|
||||
},
|
||||
{
|
||||
label: "Update checklist",
|
||||
description: "Mark completed items first, then continue"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Go back and complete remaining items"
|
||||
}
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (response.answers[0] === "Cancel") {
|
||||
console.log('\n📝 Complete remaining checklist items, then run /ccpm:verify again\n');
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.answers[0] === "Update checklist") {
|
||||
// Interactive checklist update
|
||||
const updateResponse = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Which items have you completed?",
|
||||
header: "Completed",
|
||||
multiSelect: true,
|
||||
options: incompleteItems.map((item, idx) => ({
|
||||
label: item.replace('- [ ] ', ''),
|
||||
description: `Mark item ${idx + 1} as complete`
|
||||
}))
|
||||
}]
|
||||
});
|
||||
|
||||
// Update checklist in description using linear-operations subagent
|
||||
if (updateResponse.answers && updateResponse.answers.length > 0) {
|
||||
const selectedIndices = updateResponse.answers.map(answer => {
|
||||
// Extract index from the label (assuming format "Item text")
|
||||
const itemIndex = incompleteItems.findIndex(item =>
|
||||
item.replace('- [ ] ', '') === answer
|
||||
);
|
||||
return itemIndex;
|
||||
}).filter(idx => idx >= 0);
|
||||
|
||||
if (selectedIndices.length > 0) {
|
||||
// Use Task tool to update checklist via subagent
|
||||
await Task('linear-operations', `
|
||||
operation: update_checklist_items
|
||||
params:
|
||||
issue_id: ${issueId}
|
||||
indices: [${selectedIndices.join(', ')}]
|
||||
mark_complete: true
|
||||
add_comment: true
|
||||
update_timestamp: true
|
||||
context:
|
||||
command: "verify"
|
||||
purpose: "Updating checklist items during verification"
|
||||
`);
|
||||
|
||||
console.log(`\n✅ Updated ${selectedIndices.length} checklist item(s)\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.answers[0] === "Continue anyway") {
|
||||
console.log('⚠️ Continuing with incomplete checklist\n');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Run Quality Checks
|
||||
|
||||
```markdown
|
||||
═══════════════════════════════════════
|
||||
Step 1/2: Running Quality Checks
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
**A) Detect project type and commands:**
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const hasPackageJson = fs.existsSync('./package.json');
|
||||
const hasPyProject = fs.existsSync('./pyproject.toml');
|
||||
|
||||
let lintCommand, testCommand, buildCommand;
|
||||
|
||||
if (hasPackageJson) {
|
||||
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||
lintCommand = pkg.scripts?.lint ? 'npm run lint' : null;
|
||||
testCommand = pkg.scripts?.test ? 'npm test' : null;
|
||||
buildCommand = pkg.scripts?.build ? 'npm run build' : null;
|
||||
} else if (hasPyProject) {
|
||||
lintCommand = 'ruff check . || flake8 .';
|
||||
testCommand = 'pytest';
|
||||
buildCommand = null;
|
||||
}
|
||||
```
|
||||
|
||||
**B) Run checks sequentially:**
|
||||
|
||||
```bash
|
||||
# Linting
|
||||
echo "🔍 Running linting..."
|
||||
${lintCommand}
|
||||
LINT_EXIT=$?
|
||||
|
||||
# Tests
|
||||
echo "🧪 Running tests..."
|
||||
${testCommand}
|
||||
TEST_EXIT=$?
|
||||
|
||||
# Build (optional)
|
||||
if [ -n "${buildCommand}" ]; then
|
||||
echo "🏗️ Running build..."
|
||||
${buildCommand}
|
||||
BUILD_EXIT=$?
|
||||
fi
|
||||
```
|
||||
|
||||
**C) Evaluate results:**
|
||||
|
||||
```javascript
|
||||
const results = {
|
||||
lint: LINT_EXIT === 0,
|
||||
test: TEST_EXIT === 0,
|
||||
build: buildCommand ? BUILD_EXIT === 0 : true
|
||||
};
|
||||
|
||||
const allPassed = results.lint && results.test && results.build;
|
||||
|
||||
// Display results
|
||||
console.log('\n📊 Quality Check Results:');
|
||||
console.log(` ${results.lint ? '✅' : '❌'} Linting`);
|
||||
console.log(` ${results.test ? '✅' : '❌'} Tests`);
|
||||
if (buildCommand) {
|
||||
console.log(` ${results.build ? '✅' : '❌'} Build`);
|
||||
}
|
||||
console.log('');
|
||||
```
|
||||
|
||||
**D) Handle failure:**
|
||||
|
||||
```javascript
|
||||
if (!allPassed) {
|
||||
console.log('❌ Quality Checks Failed\n');
|
||||
console.log('To debug and fix issues:');
|
||||
console.log(` /ccpm:verification:fix ${issueId}\n`);
|
||||
console.log('Then run verification again:');
|
||||
console.log(` /ccpm:verify ${issueId}\n`);
|
||||
|
||||
// Update Linear with failure
|
||||
// Use the Task tool to add failure comment
|
||||
await Task({
|
||||
subagent_type: 'ccpm:linear-operations',
|
||||
description: 'Add quality check failure comment',
|
||||
prompt: `
|
||||
operation: create_comment
|
||||
params:
|
||||
issueId: "${issueId}"
|
||||
body: |
|
||||
## ❌ Quality Checks Failed
|
||||
|
||||
**Results:**
|
||||
- ${results.lint ? '✅' : '❌'} Linting
|
||||
- ${results.test ? '✅' : '❌'} Tests
|
||||
${buildCommand ? `- ${results.build ? '✅' : '❌'} Build` : ''}
|
||||
|
||||
**Action Required:**
|
||||
Fix the issues above, then run \`/ccpm:verify\` again.
|
||||
|
||||
---
|
||||
*Via /ccpm:verify*
|
||||
context:
|
||||
command: "verify"
|
||||
`
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Run Final Verification (if checks passed)
|
||||
|
||||
```markdown
|
||||
═══════════════════════════════════════
|
||||
Step 2/2: Running Final Verification
|
||||
═══════════════════════════════════════
|
||||
```
|
||||
|
||||
**A) Invoke code-reviewer agent with smart agent selection:**
|
||||
|
||||
```yaml
|
||||
Task: `
|
||||
Review all code changes for issue ${issueId}: ${issue.title}
|
||||
|
||||
Context:
|
||||
- Issue description:
|
||||
${issue.description}
|
||||
|
||||
- All checklist items marked complete
|
||||
|
||||
Your task:
|
||||
1. Review all changes against requirements
|
||||
2. Check for code quality and best practices
|
||||
3. Verify security considerations
|
||||
4. Check for potential regressions
|
||||
5. Validate error handling
|
||||
6. Assess performance impact
|
||||
|
||||
Provide:
|
||||
- ✅ What passed review
|
||||
- ❌ Critical issues (if any)
|
||||
- 🔍 Recommendations (if any)
|
||||
- 📊 Overall assessment (PASS/FAIL)
|
||||
`
|
||||
|
||||
Note: Smart agent selector will automatically choose the best agent
|
||||
(code-reviewer, security-auditor, or specialized reviewer)
|
||||
```
|
||||
|
||||
**B) Parse verification results:**
|
||||
|
||||
```javascript
|
||||
// Look for PASS/FAIL in agent response
|
||||
const verificationPassed = !response.includes('❌ FAIL') &&
|
||||
!response.includes('Critical issues') &&
|
||||
(response.includes('✅ PASS') || response.includes('All checks passed'));
|
||||
```
|
||||
|
||||
### Step 7: Update Linear Based on Results
|
||||
|
||||
**If verification PASSED:**
|
||||
|
||||
**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: ["verified"]
|
||||
context:
|
||||
command: "verify"
|
||||
```
|
||||
|
||||
**Use the Task tool to add success 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: |
|
||||
## ✅ Verification Complete
|
||||
|
||||
**Quality Checks:**
|
||||
- ✅ Linting: PASS
|
||||
- ✅ Tests: PASS
|
||||
- ✅ Build: PASS
|
||||
|
||||
**Final Verification:**
|
||||
- ✅ Code review: PASS
|
||||
- ✅ Requirements met
|
||||
- ✅ Security validated
|
||||
- ✅ Performance acceptable
|
||||
|
||||
**Task completed successfully!** 🎉
|
||||
|
||||
---
|
||||
*Via /ccpm:verify*
|
||||
context:
|
||||
command: "verify"
|
||||
```
|
||||
|
||||
**If verification FAILED:**
|
||||
|
||||
**Use the Task tool to add failure labels:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
labels: ["blocked", "needs-revision"]
|
||||
context:
|
||||
command: "verify"
|
||||
```
|
||||
|
||||
**Use the Task tool to add failure 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: |
|
||||
## ❌ Verification Failed
|
||||
|
||||
**Quality Checks:** ✅ PASS
|
||||
|
||||
**Final Verification:** ❌ FAIL
|
||||
|
||||
**Issues Found:**
|
||||
{verification issues from step 6}
|
||||
|
||||
**Action Required:**
|
||||
Fix the issues above, then run \`/ccpm:verify\` again.
|
||||
|
||||
---
|
||||
*Via /ccpm:verify*
|
||||
context:
|
||||
command: "verify"
|
||||
```
|
||||
|
||||
### Step 8: Display Results & Next Actions
|
||||
|
||||
**If all passed:**
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
✅ All Verification Complete!
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: ${issueId} - ${issue.title}
|
||||
📊 Status: Done
|
||||
|
||||
✅ Quality Checks: PASS
|
||||
✅ Final Verification: PASS
|
||||
|
||||
All verifications passed! Ready to finalize.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 What's Next?
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
⭐ Recommended: Finalize task
|
||||
/ccpm:done ${issueId}
|
||||
|
||||
This will:
|
||||
• Create pull request
|
||||
• Sync status to Jira (if configured)
|
||||
• Send notifications (if configured)
|
||||
• Mark task as complete
|
||||
|
||||
Or continue making changes:
|
||||
/ccpm:work ${issueId}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
**Interactive menu:**
|
||||
|
||||
```javascript
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: "Verification complete! What would you like to do next?",
|
||||
header: "Next Step",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Finalize Task",
|
||||
description: "Create PR and mark as complete (/ccpm:done)"
|
||||
},
|
||||
{
|
||||
label: "Continue Working",
|
||||
description: "Make more changes (/ccpm:work)"
|
||||
},
|
||||
{
|
||||
label: "View Status",
|
||||
description: "Check current status (/ccpm:utils:status)"
|
||||
}
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
// Execute chosen action
|
||||
if (response.answers[0] === "Finalize Task") {
|
||||
await SlashCommand(`/ccpm:done ${issueId}`);
|
||||
} else if (response.answers[0] === "Continue Working") {
|
||||
await SlashCommand(`/ccpm:work ${issueId}`);
|
||||
} else {
|
||||
await SlashCommand(`/ccpm:utils:status ${issueId}`);
|
||||
}
|
||||
```
|
||||
|
||||
**If failed:**
|
||||
|
||||
```markdown
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
❌ Verification Failed
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📋 Issue: ${issueId} - ${issue.title}
|
||||
|
||||
${failureType === 'checks' ? '❌ Quality Checks: FAIL' : '✅ Quality Checks: PASS'}
|
||||
${failureType === 'verification' ? '❌ Final Verification: FAIL' : ''}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
💡 Next Steps
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
1. Fix the issues (see details above)
|
||||
2. Run: /ccpm:verification:fix ${issueId}
|
||||
3. Then verify again: /ccpm:verify ${issueId}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Invalid Issue ID Format
|
||||
```
|
||||
❌ Invalid issue ID format: proj123
|
||||
Expected format: PROJ-123 (uppercase letters, hyphen, numbers)
|
||||
```
|
||||
|
||||
### Issue Not Found
|
||||
```
|
||||
❌ Error fetching issue: Issue not found
|
||||
|
||||
Suggestions:
|
||||
- Verify the issue ID is correct
|
||||
- Check you have access to this Linear team
|
||||
- Ensure the issue hasn't been deleted
|
||||
```
|
||||
|
||||
### Git Branch Detection Failed
|
||||
```
|
||||
❌ Could not detect issue ID from git branch
|
||||
|
||||
Current branch: main
|
||||
|
||||
Usage: /ccpm:verify [ISSUE-ID]
|
||||
|
||||
Example: /ccpm:verify PSN-29
|
||||
```
|
||||
|
||||
### Project Commands Not Found
|
||||
```
|
||||
⚠️ No lint/test commands found in package.json
|
||||
|
||||
Verification requires:
|
||||
- "lint" script for linting
|
||||
- "test" script for testing
|
||||
|
||||
Add these to package.json and try again.
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Verify with auto-detection (all passed)
|
||||
|
||||
```bash
|
||||
# Current branch: feature/PSN-29-add-auth
|
||||
/ccpm:verify
|
||||
|
||||
# Output:
|
||||
# 📌 Detected issue from branch: PSN-29
|
||||
#
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# 🔍 Smart Verify Command
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
#
|
||||
# 📋 Issue: PSN-29 - Add user authentication
|
||||
# 📊 Status: In Progress
|
||||
# 📋 Checklist: 100% (5/5 items)
|
||||
#
|
||||
# ═══════════════════════════════════════
|
||||
# Step 1/2: Running Quality Checks
|
||||
# ═══════════════════════════════════════
|
||||
#
|
||||
# 🔍 Running linting...
|
||||
# ✅ All files pass linting
|
||||
#
|
||||
# 🧪 Running tests...
|
||||
# ✅ All tests passed (28/28)
|
||||
#
|
||||
# 🏗️ Running build...
|
||||
# ✅ Build successful
|
||||
#
|
||||
# 📊 Quality Check Results:
|
||||
# ✅ Linting
|
||||
# ✅ Tests
|
||||
# ✅ Build
|
||||
#
|
||||
# ═══════════════════════════════════════
|
||||
# Step 2/2: Running Final Verification
|
||||
# ═══════════════════════════════════════
|
||||
#
|
||||
# [Code reviewer agent analyzes changes...]
|
||||
#
|
||||
# ✅ All requirements met
|
||||
# ✅ Code quality standards met
|
||||
# ✅ Security best practices followed
|
||||
#
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# ✅ All Verification Complete!
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Example 2: Verify explicit issue (checks failed)
|
||||
|
||||
```bash
|
||||
/ccpm:verify PSN-29
|
||||
|
||||
# Output:
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# 🔍 Smart Verify Command
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
#
|
||||
# [... quality checks ...]
|
||||
#
|
||||
# 📊 Quality Check Results:
|
||||
# ✅ Linting
|
||||
# ❌ Tests
|
||||
# ✅ Build
|
||||
#
|
||||
# ❌ Quality Checks Failed
|
||||
#
|
||||
# To debug and fix issues:
|
||||
# /ccpm:verification:fix PSN-29
|
||||
#
|
||||
# Then run verification again:
|
||||
# /ccpm:verify PSN-29
|
||||
```
|
||||
|
||||
### Example 3: Incomplete checklist prompt
|
||||
|
||||
```bash
|
||||
/ccpm:verify PSN-29
|
||||
|
||||
# Output:
|
||||
# 📋 Checklist: 80% (4/5 items)
|
||||
#
|
||||
# ⚠️ Checklist incomplete!
|
||||
#
|
||||
# Remaining items:
|
||||
# ⏳ Write integration tests
|
||||
#
|
||||
# [Interactive prompt appears:]
|
||||
# Checklist is 80% complete. What would you like to do?
|
||||
# • Continue anyway
|
||||
# • Update checklist
|
||||
# • Cancel
|
||||
```
|
||||
|
||||
## Token Budget Breakdown
|
||||
|
||||
| Section | Tokens | Notes |
|
||||
|---------|--------|-------|
|
||||
| Frontmatter & description | 80 | Minimal metadata |
|
||||
| Step 1: Argument parsing | 180 | Git detection + validation |
|
||||
| Step 2: Fetch issue | 120 | Linear subagent (cached) |
|
||||
| Step 3: Display flow | 80 | Header + flow diagram |
|
||||
| Step 4: Checklist check | 250 | Parsing + interactive prompt |
|
||||
| Step 5: Quality checks | 500 | Commands + execution + results |
|
||||
| Step 6: Final verification | 300 | Agent invocation + parsing |
|
||||
| Step 7: Update Linear | 200 | Batch update + comment |
|
||||
| Step 8: Results display | 250 | Success/failure + menu |
|
||||
| Error handling | 200 | 4 scenarios |
|
||||
| Examples | 340 | 3 concise examples |
|
||||
| **Total** | **~2,500** | **vs ~8,000 baseline (69% reduction)** |
|
||||
|
||||
## Key Optimizations
|
||||
|
||||
1. ✅ **No routing overhead** - Direct implementation (no /ccpm:verification:check or :verify calls)
|
||||
2. ✅ **Linear subagent** - All Linear ops cached (85-95% hit rate)
|
||||
3. ✅ **Smart agent selection** - Automatic optimal agent choice for verification
|
||||
4. ✅ **Sequential execution** - Checks → verification (fail fast)
|
||||
5. ✅ **Auto-detection** - Issue ID from git branch
|
||||
6. ✅ **Batch operations** - Single update_issue call (state + labels)
|
||||
7. ✅ **Concise examples** - Only 3 essential examples
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
- **After /ccpm:sync** → Use /ccpm:verify to check quality
|
||||
- **After /ccpm:work** → Complete work then /ccpm:verify
|
||||
- **Before /ccpm:done** → Always verify before finalizing
|
||||
- **Failed checks** → Use /ccpm:verification:fix to debug
|
||||
|
||||
## Notes
|
||||
|
||||
- **Git branch detection**: Extracts issue ID from branch names like `feature/PSN-29-add-auth`
|
||||
- **Smart agent selection**: Automatically invokes optimal verification agent
|
||||
- **Fail fast**: Stops at quality checks if they fail (no wasted verification)
|
||||
- **Checklist validation**: Prompts user if checklist incomplete
|
||||
- **Caching**: Linear subagent caches issue data for faster operations
|
||||
- **Error recovery**: Provides actionable suggestions for all error scenarios
|
||||
503
commands/work.md
Normal file
503
commands/work.md
Normal file
@@ -0,0 +1,503 @@
|
||||
---
|
||||
description: Smart work command - start or resume work (optimized)
|
||||
allowed-tools: [Bash, Task]
|
||||
argument-hint: "[issue-id]"
|
||||
---
|
||||
|
||||
# /ccpm:work - Start or Resume Work
|
||||
|
||||
**Token Budget:** ~5,000 tokens (vs ~15,000 baseline) | **67% reduction**
|
||||
|
||||
Intelligent command that detects whether to start new work or resume in-progress tasks.
|
||||
|
||||
## Mode Detection
|
||||
|
||||
- **START**: Issue status is Planning/Backlog/Todo/Planned → Initialize implementation
|
||||
- **RESUME**: Issue status is In Progress/In Development/Doing → Show progress and next action
|
||||
- **ERROR**: Issue status is Done/Completed/Cancelled → Cannot work on completed tasks
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Auto-detect issue from git branch
|
||||
/ccpm:work
|
||||
|
||||
# Explicit issue ID
|
||||
/ccpm:work PSN-29
|
||||
|
||||
# Examples
|
||||
/ccpm:work PROJ-123 # Start or resume PROJ-123
|
||||
/ccpm:work # Auto-detect from branch name "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. Usage: /ccpm:work [ISSUE-ID]');
|
||||
}
|
||||
|
||||
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 format: PROJ-123`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Fetch Issue via Linear Subagent
|
||||
|
||||
**Use the Task tool to fetch the issue from Linear:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent with these parameters:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**: Use this exact format:
|
||||
```
|
||||
operation: get_issue
|
||||
params:
|
||||
issueId: "{the issue ID from step 1}"
|
||||
context:
|
||||
cache: true
|
||||
command: "work"
|
||||
```
|
||||
|
||||
**Store response as `issue` object** containing:
|
||||
- `issue.id` - Internal Linear ID
|
||||
- `issue.identifier` - Human-readable ID (e.g., PSN-29)
|
||||
- `issue.title` - Issue title
|
||||
- `issue.description` - Full description with checklist
|
||||
- `issue.state.name` - Current status name
|
||||
- `issue.state.id` - Status ID
|
||||
- `issue.labels` - Array of label objects
|
||||
- `issue.team.id` - Team ID
|
||||
|
||||
**Error handling:**
|
||||
```javascript
|
||||
if (subagentResponse.error) {
|
||||
console.log(`❌ Error fetching issue: ${subagentResponse.error.message}`);
|
||||
console.log('\nSuggestions:');
|
||||
subagentResponse.error.suggestions.forEach(s => console.log(` - ${s}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const issue = subagentResponse.issue;
|
||||
```
|
||||
|
||||
### Step 3: Detect Mode
|
||||
|
||||
```javascript
|
||||
const status = issue.state.name;
|
||||
|
||||
const startStatuses = ['Planning', 'Backlog', 'Todo', 'Planned', 'Not Started'];
|
||||
const resumeStatuses = ['In Progress', 'In Development', 'Doing', 'Started'];
|
||||
const completeStatuses = ['Done', 'Completed', 'Closed', 'Cancelled'];
|
||||
|
||||
let mode;
|
||||
if (startStatuses.includes(status)) {
|
||||
mode = 'START';
|
||||
} else if (resumeStatuses.includes(status)) {
|
||||
mode = 'RESUME';
|
||||
} else if (completeStatuses.includes(status)) {
|
||||
console.log(`❌ Cannot work on completed task: ${issueId}`);
|
||||
console.log(`Status: ${status}`);
|
||||
console.log('\nThis task is already complete. Did you mean to start a different task?');
|
||||
return;
|
||||
} else {
|
||||
// Unknown status - default to RESUME
|
||||
mode = 'RESUME';
|
||||
}
|
||||
|
||||
console.log(`\n🎯 Mode: ${mode}`);
|
||||
console.log(`📋 Issue: ${issue.identifier} - ${issue.title}`);
|
||||
console.log(`📊 Status: ${status}\n`);
|
||||
```
|
||||
|
||||
### Step 4A: START Mode Implementation
|
||||
|
||||
```yaml
|
||||
## START Mode: Initialize Implementation
|
||||
|
||||
1. Update issue status and labels (batch operation):
|
||||
|
||||
**Use the Task tool to update the Linear issue:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: update_issue
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
state: "In Progress"
|
||||
labels: ["implementation"]
|
||||
context:
|
||||
cache: true
|
||||
command: "work"
|
||||
```
|
||||
|
||||
Display: "✅ Updated status: Planning → In Progress"
|
||||
|
||||
2. Analyze codebase with smart agent selection:
|
||||
|
||||
Task: `
|
||||
Analyze the codebase to create an implementation plan for: ${issue.title}
|
||||
|
||||
Context:
|
||||
- Issue: ${issueId}
|
||||
- Description:
|
||||
${issue.description}
|
||||
|
||||
Your task:
|
||||
1. Identify files that need to be modified
|
||||
2. List dependencies and imports needed
|
||||
3. Outline testing strategy
|
||||
4. Note potential challenges or risks
|
||||
5. Estimate complexity (low/medium/high)
|
||||
|
||||
Provide a structured implementation plan with specific file paths and line numbers where possible.
|
||||
`
|
||||
|
||||
Note: The smart-agent-selector hook will automatically choose the optimal agent:
|
||||
- backend-architect for API/backend tasks
|
||||
- frontend-developer for UI/React tasks
|
||||
- mobile-developer for mobile tasks
|
||||
- etc.
|
||||
|
||||
3. Store the plan and add comment via Linear subagent:
|
||||
|
||||
**Use the Task tool to add a comment to Linear:**
|
||||
|
||||
Invoke the `ccpm:linear-operations` subagent:
|
||||
- **Tool**: Task
|
||||
- **Subagent**: ccpm:linear-operations
|
||||
- **Prompt**:
|
||||
```
|
||||
operation: create_comment
|
||||
params:
|
||||
issueId: "{issue ID from step 1}"
|
||||
body: |
|
||||
## 🚀 Implementation Started
|
||||
|
||||
**Status:** Planning → In Progress
|
||||
|
||||
### Implementation Plan
|
||||
|
||||
{paste the analysis result from step 2 here}
|
||||
|
||||
---
|
||||
*Started via /ccpm:work*
|
||||
context:
|
||||
command: "work"
|
||||
```
|
||||
|
||||
Display: "✅ Added implementation plan to Linear"
|
||||
|
||||
4. Display next actions:
|
||||
|
||||
console.log('\n═══════════════════════════════════════');
|
||||
console.log('🎯 Implementation Started');
|
||||
console.log('═══════════════════════════════════════\n');
|
||||
console.log('📝 Plan added to Linear issue');
|
||||
console.log('\n💡 Next Steps:');
|
||||
console.log(' 1. Review the implementation plan above');
|
||||
console.log(' 2. Start coding');
|
||||
console.log(' 3. Use /ccpm:sync to save progress');
|
||||
console.log(' 4. Use /ccpm:verify when ready for review');
|
||||
console.log('\n📌 Quick Commands:');
|
||||
console.log(` /ccpm:sync "${issueId}" "progress update"`);
|
||||
console.log(` /ccpm:commit "${issueId}"`);
|
||||
console.log(` /ccpm:verify "${issueId}"`);
|
||||
```
|
||||
|
||||
### Step 4B: RESUME Mode Implementation
|
||||
|
||||
```yaml
|
||||
## RESUME Mode: Show Progress and Next Action
|
||||
|
||||
1. Calculate progress from checklist:
|
||||
|
||||
const description = issue.description || '';
|
||||
const checklistItems = description.match(/- \[([ x])\] .+/g) || [];
|
||||
const totalItems = checklistItems.length;
|
||||
const completedItems = checklistItems.filter(item => item.includes('[x]')).length;
|
||||
const progress = totalItems > 0 ? Math.round((completedItems / totalItems) * 100) : 0;
|
||||
|
||||
2. Determine next action:
|
||||
|
||||
let nextAction = null;
|
||||
let suggestion = null;
|
||||
|
||||
if (progress === 100) {
|
||||
suggestion = 'All checklist items complete! Ready for verification.';
|
||||
nextAction = '/ccpm:verify';
|
||||
} else {
|
||||
// Find first incomplete checklist item
|
||||
const incompleteItem = checklistItems.find(item => item.includes('[ ]'));
|
||||
if (incompleteItem) {
|
||||
const itemText = incompleteItem.replace(/- \[ \] /, '');
|
||||
nextAction = `Continue work on: ${itemText}`;
|
||||
} else {
|
||||
suggestion = 'No checklist found. Continue implementation.';
|
||||
}
|
||||
}
|
||||
|
||||
3. Display progress and suggestion:
|
||||
|
||||
console.log('\n═══════════════════════════════════════');
|
||||
console.log('📊 Work in Progress');
|
||||
console.log('═══════════════════════════════════════\n');
|
||||
console.log(`📋 Issue: ${issue.identifier} - ${issue.title}`);
|
||||
console.log(`📊 Status: ${issue.state.name}`);
|
||||
console.log(`✅ Progress: ${progress}% (${completedItems}/${totalItems} items)\n`);
|
||||
|
||||
if (checklistItems.length > 0) {
|
||||
console.log('📝 Checklist:\n');
|
||||
checklistItems.forEach(item => {
|
||||
const isComplete = item.includes('[x]');
|
||||
const icon = isComplete ? '✅' : '⏳';
|
||||
const text = item.replace(/- \[([ x])\] /, '');
|
||||
console.log(` ${icon} ${text}`);
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (suggestion) {
|
||||
console.log(`💡 Suggestion: ${suggestion}\n`);
|
||||
}
|
||||
|
||||
if (nextAction) {
|
||||
console.log(`🎯 Next Action: ${nextAction}\n`);
|
||||
}
|
||||
|
||||
4. Interactive menu:
|
||||
|
||||
console.log('Available Actions:');
|
||||
console.log(' 1. ⭐ Sync progress - /ccpm:sync');
|
||||
console.log(' 2. 📝 Git commit - /ccpm:commit');
|
||||
console.log(' 3. ✅ Run verification - /ccpm:verify');
|
||||
console.log(' 4. 🔍 View issue details - /ccpm:utils:status ' + issueId);
|
||||
console.log(' 5. 🛠️ Fix issues - /ccpm:verification:fix ' + issueId);
|
||||
console.log('\n📌 Quick Commands:');
|
||||
console.log(` /ccpm:sync "completed ${itemText}"`);
|
||||
console.log(` /ccpm:commit "feat: ${issue.title.toLowerCase()}"`);
|
||||
|
||||
if (progress === 100) {
|
||||
console.log('\n⭐ Recommended: /ccpm:verify (checklist complete)');
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Interactive Menu
|
||||
|
||||
Display menu based on mode:
|
||||
|
||||
**START mode menu:**
|
||||
```
|
||||
Available Actions:
|
||||
1. ⭐ Start coding - Begin implementation
|
||||
2. 📝 Sync progress - /ccpm:sync
|
||||
3. 🔍 View issue details - /ccpm:utils:status PSN-29
|
||||
|
||||
Quick Commands:
|
||||
/ccpm:sync "implemented X feature"
|
||||
/ccpm:commit "feat: add user authentication"
|
||||
```
|
||||
|
||||
**RESUME mode menu:**
|
||||
```
|
||||
Available Actions:
|
||||
1. ⭐ Sync progress - /ccpm:sync
|
||||
2. 📝 Git commit - /ccpm:commit
|
||||
3. ✅ Run verification - /ccpm:verify
|
||||
4. 🔍 View issue details - /ccpm:utils:status PSN-29
|
||||
5. 🛠️ Fix issues - /ccpm:verification:fix PSN-29
|
||||
|
||||
Quick Commands:
|
||||
/ccpm:sync "progress update"
|
||||
/ccpm:commit
|
||||
/ccpm:verify
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Invalid Issue ID Format
|
||||
```
|
||||
❌ Invalid issue ID format: proj123
|
||||
Expected format: PROJ-123 (uppercase letters, hyphen, numbers)
|
||||
```
|
||||
|
||||
### Issue Not Found
|
||||
```
|
||||
❌ Error fetching issue: Issue not found
|
||||
|
||||
Suggestions:
|
||||
- Verify the issue ID is correct
|
||||
- Check you have access to this Linear team
|
||||
- Ensure the issue hasn't been deleted
|
||||
```
|
||||
|
||||
### Git Branch Detection Failed
|
||||
```
|
||||
❌ Could not detect issue ID from git branch
|
||||
|
||||
Current branch: main
|
||||
|
||||
Usage: /ccpm:work [ISSUE-ID]
|
||||
|
||||
Example: /ccpm:work PSN-29
|
||||
```
|
||||
|
||||
### Completed Task
|
||||
```
|
||||
❌ Cannot work on completed task: PSN-29
|
||||
Status: Done
|
||||
|
||||
This task is already complete. Did you mean to start a different task?
|
||||
```
|
||||
|
||||
### Network Errors
|
||||
```
|
||||
❌ Error fetching issue: Network request failed
|
||||
|
||||
Suggestions:
|
||||
- Check your internet connection
|
||||
- Verify Linear MCP server is running
|
||||
- Try again in a moment
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Start work (auto-detect from branch)
|
||||
|
||||
```bash
|
||||
# Current branch: feature/PSN-29-add-auth
|
||||
/ccpm:work
|
||||
|
||||
# Output:
|
||||
# 📌 Detected issue from branch: PSN-29
|
||||
#
|
||||
# 🎯 Mode: START
|
||||
# 📋 Issue: PSN-29 - Add user authentication
|
||||
# 📊 Status: Planning
|
||||
#
|
||||
# ✅ Updated status: Planning → In Progress
|
||||
#
|
||||
# [Smart agent analyzes codebase...]
|
||||
#
|
||||
# ✅ Added implementation plan to Linear
|
||||
#
|
||||
# ═══════════════════════════════════════
|
||||
# 🎯 Implementation Started
|
||||
# ═══════════════════════════════════════
|
||||
#
|
||||
# 📝 Plan added to Linear issue
|
||||
#
|
||||
# 💡 Next Steps:
|
||||
# 1. Review the implementation plan above
|
||||
# 2. Start coding
|
||||
# 3. Use /ccpm:sync to save progress
|
||||
# 4. Use /ccpm:verify when ready for review
|
||||
```
|
||||
|
||||
### Example 2: Resume work (explicit issue ID)
|
||||
|
||||
```bash
|
||||
/ccpm:work PSN-29
|
||||
|
||||
# Output:
|
||||
# 🎯 Mode: RESUME
|
||||
# 📋 Issue: PSN-29 - Add user authentication
|
||||
# 📊 Status: In Progress
|
||||
#
|
||||
# ═══════════════════════════════════════
|
||||
# 📊 Work in Progress
|
||||
# ═══════════════════════════════════════
|
||||
#
|
||||
# 📋 Issue: PSN-29 - Add user authentication
|
||||
# 📊 Status: In Progress
|
||||
# ✅ Progress: 60% (3/5 items)
|
||||
#
|
||||
# 📝 Checklist:
|
||||
#
|
||||
# ✅ Create auth endpoints
|
||||
# ✅ Add JWT validation
|
||||
# ✅ Implement login flow
|
||||
# ⏳ Add password reset
|
||||
# ⏳ Write tests
|
||||
#
|
||||
# 🎯 Next Action: Continue work on: Add password reset
|
||||
#
|
||||
# Available Actions:
|
||||
# 1. ⭐ Sync progress - /ccpm:sync
|
||||
# 2. 📝 Git commit - /ccpm:commit
|
||||
# 3. ✅ Run verification - /ccpm:verify
|
||||
```
|
||||
|
||||
### Example 3: Resume completed work (error)
|
||||
|
||||
```bash
|
||||
/ccpm:work PSN-28
|
||||
|
||||
# Output:
|
||||
# 🎯 Mode: ERROR
|
||||
# 📋 Issue: PSN-28 - Fix navigation bug
|
||||
# 📊 Status: Done
|
||||
#
|
||||
# ❌ Cannot work on completed task: PSN-28
|
||||
# Status: Done
|
||||
#
|
||||
# This task is already complete. Did you mean to start a different task?
|
||||
```
|
||||
|
||||
## Token Budget Breakdown
|
||||
|
||||
| Section | Tokens | Notes |
|
||||
|---------|--------|-------|
|
||||
| Frontmatter & description | 100 | Minimal metadata |
|
||||
| Step 1: Argument parsing | 300 | Git detection + validation |
|
||||
| Step 2: Fetch issue | 400 | Linear subagent + error handling |
|
||||
| Step 3: Mode detection | 200 | Status checks + display |
|
||||
| Step 4A: START mode | 1,500 | Update + analysis + comment |
|
||||
| Step 4B: RESUME mode | 1,000 | Progress + next action + menu |
|
||||
| Step 5: Interactive menu | 600 | Mode-specific menus |
|
||||
| Examples | 400 | 3 concise examples |
|
||||
| Error handling | 500 | 5 error scenarios |
|
||||
| **Total** | **~5,000** | **vs ~15,000 baseline (67% reduction)** |
|
||||
|
||||
## Key Optimizations
|
||||
|
||||
1. ✅ **No routing overhead** - Direct implementation of both modes
|
||||
2. ✅ **Linear subagent** - All Linear ops cached (85-95% hit rate)
|
||||
3. ✅ **Smart agent selection** - Automatic optimal agent choice for analysis
|
||||
4. ✅ **Batch operations** - Single update_issue call (state + labels)
|
||||
5. ✅ **Concise examples** - Only 3 essential examples
|
||||
6. ✅ **Focused scope** - START mode simplified (no full agent discovery)
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
- **After /ccpm:plan** → Use /ccpm:work to start implementation
|
||||
- **During work** → Use /ccpm:sync to save progress
|
||||
- **Git commits** → Use /ccpm:commit for conventional commits
|
||||
- **Before completion** → Use /ccpm:verify for quality checks
|
||||
- **Finalize** → Use /ccpm:done to create PR and complete
|
||||
|
||||
## Notes
|
||||
|
||||
- **Git branch detection**: Extracts issue ID from branch names like `feature/PSN-29-add-auth`
|
||||
- **Smart agent selection**: Automatically invokes optimal agent based on task type
|
||||
- **Progress tracking**: Calculates from checklist items in issue description
|
||||
- **Caching**: Linear subagent caches issue data for 85-95% faster subsequent operations
|
||||
- **Error recovery**: Provides actionable suggestions for all error scenarios
|
||||
Reference in New Issue
Block a user