Initial commit
This commit is contained in:
677
skills/jira-resource-validator/SKILL.md
Normal file
677
skills/jira-resource-validator/SKILL.md
Normal file
@@ -0,0 +1,677 @@
|
||||
---
|
||||
name: jira-resource-validator
|
||||
description: Validates Jira projects and boards exist, creates missing resources automatically. Smart enough to prompt user to select existing or create new projects. For boards, accepts either IDs (validates existence) or names (creates boards and updates .env with IDs). NEW - Per-project configuration support - JIRA_BOARDS_{ProjectKey} for hierarchical board organization across multiple projects. Activates for jira setup, jira validation, jira configuration, missing jira project, missing jira boards, jira .env setup, per-project boards.
|
||||
allowed-tools: Read, Bash, Write, Edit
|
||||
---
|
||||
|
||||
# Jira Resource Validator Skill
|
||||
|
||||
**Purpose**: Validate and auto-create Jira projects and boards, ensuring .env configuration is correct.
|
||||
|
||||
**Auto-Activation**: Triggers when Jira setup or validation is needed.
|
||||
|
||||
## What This Skill Does
|
||||
|
||||
This skill ensures your Jira configuration in `.env` is valid and all resources exist. It's **smart enough** to:
|
||||
|
||||
1. **Validate Jira projects** - Check if `JIRA_PROJECT` exists
|
||||
2. **Prompt for action** - Select existing project or create new one
|
||||
3. **Validate Jira boards** - Check if boards exist (by ID or name)
|
||||
4. **Create missing boards** - If board names provided, create them automatically
|
||||
5. **Update .env with IDs** - Replace board names with actual board IDs after creation
|
||||
|
||||
## When This Skill Activates
|
||||
|
||||
✅ **Automatically activates when**:
|
||||
- You set up Jira integration for the first time
|
||||
- You run `/specweave-jira:sync` and resources are missing
|
||||
- Your `.env` has invalid Jira configuration
|
||||
- You mention "jira setup" or "jira validation"
|
||||
|
||||
## Jira Configuration Structure
|
||||
|
||||
### Required .env Variables
|
||||
|
||||
```bash
|
||||
JIRA_API_TOKEN=your_token_here
|
||||
JIRA_EMAIL=your_email@company.com
|
||||
JIRA_DOMAIN=yourcompany.atlassian.net
|
||||
JIRA_STRATEGY=board-based
|
||||
JIRA_PROJECT=PROJECTKEY
|
||||
JIRA_BOARDS=1,2,3 # IDs (if exist) OR names (if creating)
|
||||
```
|
||||
|
||||
### Smart Per-Board Detection (Mixed Mode Support!)
|
||||
|
||||
**The system is smart enough to handle ANY combination of IDs and names:**
|
||||
|
||||
**All IDs** (validate existing boards):
|
||||
```bash
|
||||
JIRA_BOARDS=1,2,3
|
||||
```
|
||||
→ Validates boards 1, 2, 3 exist
|
||||
|
||||
**All Names** (create new boards):
|
||||
```bash
|
||||
JIRA_BOARDS=Frontend,Backend,Mobile
|
||||
```
|
||||
→ Creates 3 boards, updates .env with IDs: `JIRA_BOARDS=101,102,103`
|
||||
|
||||
**Mixed IDs and Names** (smart handling!):
|
||||
```bash
|
||||
JIRA_BOARDS=101,102,QA,Dashboard
|
||||
```
|
||||
→ Validates 101, 102 exist
|
||||
→ Creates "QA" and "Dashboard" boards
|
||||
→ Updates .env: `JIRA_BOARDS=101,102,103,104` (all IDs!)
|
||||
|
||||
**How it works**: Each entry is checked individually:
|
||||
- Numeric (e.g., "123") → Validate ID exists
|
||||
- Non-numeric (e.g., "QA") → Create board with that name
|
||||
- After creation, .env is updated with ALL board IDs
|
||||
|
||||
### NEW: Per-Project Configuration (Advanced - Multiple Projects × Boards)
|
||||
|
||||
**Multiple JIRA projects with their own boards:**
|
||||
|
||||
```bash
|
||||
# Multiple projects with their own boards
|
||||
JIRA_STRATEGY=project-per-team
|
||||
JIRA_PROJECTS=BACKEND,FRONTEND,MOBILE
|
||||
|
||||
# Per-project boards (hierarchical naming)
|
||||
JIRA_BOARDS_BACKEND=123,456 # Sprint + Kanban (IDs)
|
||||
JIRA_BOARDS_FRONTEND=Sprint,Bug # Create these boards
|
||||
JIRA_BOARDS_MOBILE=789,012,345 # iOS + Android + Release (IDs)
|
||||
```
|
||||
→ Validates 3 projects exist: BACKEND, FRONTEND, MOBILE
|
||||
→ Validates/creates boards per project:
|
||||
- BACKEND: Validates boards 123, 456 exist
|
||||
- FRONTEND: Creates "Sprint" and "Bug" boards, updates .env with IDs
|
||||
- MOBILE: Validates boards 789, 012, 345 exist
|
||||
|
||||
**Naming Convention**: `{PROVIDER}_{RESOURCE_TYPE}_{PROJECT_KEY}`
|
||||
|
||||
**Mixed IDs and Names Per Project**:
|
||||
```bash
|
||||
JIRA_BOARDS_BACKEND=123,NewBoard,456
|
||||
```
|
||||
→ Validates 123, 456 exist
|
||||
→ Creates "NewBoard"
|
||||
→ Updates .env: `JIRA_BOARDS_BACKEND=123,789,456` (all IDs!)
|
||||
|
||||
## Validation Flow
|
||||
|
||||
### Step 1: Project Validation
|
||||
|
||||
**Check if project exists**:
|
||||
```bash
|
||||
# API call to Jira
|
||||
GET /rest/api/3/project/PROJECTKEY
|
||||
```
|
||||
|
||||
**If project exists**:
|
||||
```
|
||||
✅ Project "PROJECTKEY" exists
|
||||
ID: 10001
|
||||
Name: My Project
|
||||
```
|
||||
|
||||
**If project doesn't exist**:
|
||||
```
|
||||
⚠️ Project "PROJECTKEY" not found
|
||||
|
||||
What would you like to do?
|
||||
1. Select an existing project
|
||||
2. Create a new project
|
||||
3. Cancel
|
||||
|
||||
Your choice [1]:
|
||||
```
|
||||
|
||||
**Option 1: Select Existing**:
|
||||
```
|
||||
Available projects:
|
||||
1. PROJ1 - Project One
|
||||
2. PROJ2 - Project Two
|
||||
3. PROJ3 - Project Three
|
||||
|
||||
Select a project [1]:
|
||||
|
||||
✅ Updated .env: JIRA_PROJECT=PROJ1
|
||||
```
|
||||
|
||||
**Option 2: Create New**:
|
||||
```
|
||||
Enter project name: My New Project
|
||||
|
||||
📦 Creating Jira project: PROJECTKEY (My New Project)...
|
||||
✅ Project created: PROJECTKEY (ID: 10005)
|
||||
```
|
||||
|
||||
### Step 2: Board Validation (Per-Board Smart Detection)
|
||||
|
||||
**Scenario A: All Board IDs** (all numeric):
|
||||
```bash
|
||||
JIRA_BOARDS=1,2,3
|
||||
```
|
||||
|
||||
**Validation**:
|
||||
```
|
||||
Checking boards: 1,2,3...
|
||||
✅ Board 1: Frontend Board (exists)
|
||||
✅ Board 2: Backend Board (exists)
|
||||
⚠️ Board 3: Not found
|
||||
|
||||
⚠️ Issues found: 1 board(s)
|
||||
```
|
||||
|
||||
**Scenario B: All Board Names** (all non-numeric):
|
||||
```bash
|
||||
JIRA_BOARDS=Frontend,Backend,Mobile
|
||||
```
|
||||
|
||||
**Auto-creation**:
|
||||
```
|
||||
Checking boards: Frontend,Backend,Mobile...
|
||||
📦 Creating board: Frontend...
|
||||
✅ Created: Frontend (ID: 101)
|
||||
📦 Creating board: Backend...
|
||||
✅ Created: Backend (ID: 102)
|
||||
📦 Creating board: Mobile...
|
||||
✅ Created: Mobile (ID: 103)
|
||||
|
||||
📝 Updating .env with board IDs...
|
||||
✅ Updated JIRA_BOARDS: 101,102,103
|
||||
|
||||
✅ All boards validated/created successfully
|
||||
```
|
||||
|
||||
**Scenario C: Mixed IDs and Names** (SMART!):
|
||||
```bash
|
||||
JIRA_BOARDS=101,102,QA,Dashboard
|
||||
```
|
||||
|
||||
**Smart handling**:
|
||||
```
|
||||
Checking boards: 101,102,QA,Dashboard...
|
||||
✅ Board 101: Frontend Board (exists)
|
||||
✅ Board 102: Backend Board (exists)
|
||||
📦 Creating board: QA...
|
||||
✅ Created: QA (ID: 103)
|
||||
📦 Creating board: Dashboard...
|
||||
✅ Created: Dashboard (ID: 104)
|
||||
|
||||
📝 Updating .env with board IDs...
|
||||
✅ Updated JIRA_BOARDS: 101,102,103,104
|
||||
|
||||
✅ All boards validated/created successfully
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Fresh Jira Setup
|
||||
|
||||
**Scenario**: New project, no Jira resources exist yet
|
||||
|
||||
**Action**: Run `/specweave-jira:sync`
|
||||
|
||||
**What Happens**:
|
||||
```bash
|
||||
🔍 Validating Jira configuration...
|
||||
|
||||
Checking project: MINIDOOM...
|
||||
⚠️ Project "MINIDOOM" not found
|
||||
|
||||
What would you like to do?
|
||||
1. Select an existing project
|
||||
2. Create a new project
|
||||
3. Cancel
|
||||
|
||||
Your choice [2]: 2
|
||||
|
||||
Enter project name: Mini DOOM Tournament
|
||||
|
||||
📦 Creating Jira project: MINIDOOM (Mini DOOM Tournament)...
|
||||
✅ Project created: MINIDOOM (ID: 10005)
|
||||
|
||||
Checking boards: Frontend,Backend,Mobile...
|
||||
📦 Creating boards from names...
|
||||
|
||||
Creating board: Frontend in project MINIDOOM...
|
||||
✅ Board created: Frontend (ID: 101)
|
||||
|
||||
Creating board: Backend in project MINIDOOM...
|
||||
✅ Board created: Backend (ID: 102)
|
||||
|
||||
Creating board: Mobile in project MINIDOOM...
|
||||
✅ Board created: Mobile (ID: 103)
|
||||
|
||||
✅ Updated .env: JIRA_BOARDS=101,102,103
|
||||
|
||||
🎉 Jira configuration complete! All resources ready.
|
||||
```
|
||||
|
||||
**Result**: `.env` now has correct project and board IDs
|
||||
|
||||
### Example 2: Select Existing Project
|
||||
|
||||
**Scenario**: Project already exists in Jira
|
||||
|
||||
**Action**: Run validation
|
||||
|
||||
**What Happens**:
|
||||
```bash
|
||||
🔍 Validating Jira configuration...
|
||||
|
||||
Checking project: PROJ...
|
||||
⚠️ Project "PROJ" not found
|
||||
|
||||
What would you like to do?
|
||||
1. Select an existing project
|
||||
2. Create a new project
|
||||
3. Cancel
|
||||
|
||||
Your choice [1]: 1
|
||||
|
||||
Available projects:
|
||||
1. FRONTEND - Frontend Team
|
||||
2. BACKEND - Backend Team
|
||||
3. MOBILE - Mobile Team
|
||||
|
||||
Select a project [1]: 2
|
||||
|
||||
✅ Updated .env: JIRA_PROJECT=BACKEND
|
||||
✅ Project "BACKEND" exists
|
||||
|
||||
Checking boards: 45,46...
|
||||
✅ All boards exist
|
||||
```
|
||||
|
||||
### Example 3: Mixed Board IDs (Some Exist, Some Don't)
|
||||
|
||||
**Scenario**: Some board IDs are invalid
|
||||
|
||||
**Action**: Run validation
|
||||
|
||||
**What Happens**:
|
||||
```bash
|
||||
🔍 Validating Jira configuration...
|
||||
|
||||
Checking project: PROJECTKEY...
|
||||
✅ Project "PROJECTKEY" exists
|
||||
|
||||
Checking boards: 1,2,999...
|
||||
|
||||
Board 1: ✅ Exists (Frontend Board)
|
||||
Board 2: ✅ Exists (Backend Board)
|
||||
Board 999: ❌ Not found
|
||||
|
||||
⚠️ Boards not found: 999
|
||||
|
||||
Available boards in project PROJECTKEY:
|
||||
1. Frontend Board (ID: 1)
|
||||
2. Backend Board (ID: 2)
|
||||
3. QA Board (ID: 3)
|
||||
4. DevOps Board (ID: 4)
|
||||
|
||||
Would you like to:
|
||||
1. Remove invalid board (999) from configuration
|
||||
2. Replace with correct board ID
|
||||
3. Create new board
|
||||
|
||||
Your choice [2]: 2
|
||||
|
||||
Enter correct board ID or name: 3
|
||||
|
||||
✅ Updated .env: JIRA_BOARDS=1,2,3
|
||||
```
|
||||
|
||||
## CLI Command
|
||||
|
||||
**Manual validation**:
|
||||
```bash
|
||||
# From TypeScript
|
||||
npx tsx src/utils/external-resource-validator.ts
|
||||
|
||||
# Or via skill activation
|
||||
"Can you validate my Jira configuration?"
|
||||
```
|
||||
|
||||
**Validation output**:
|
||||
```typescript
|
||||
{
|
||||
valid: true,
|
||||
project: {
|
||||
exists: true,
|
||||
key: 'PROJECTKEY',
|
||||
id: '10001',
|
||||
name: 'My Project'
|
||||
},
|
||||
boards: {
|
||||
valid: true,
|
||||
existing: [1, 2, 3],
|
||||
missing: [],
|
||||
created: []
|
||||
},
|
||||
envUpdated: false
|
||||
}
|
||||
```
|
||||
|
||||
## Smart Board Creation Logic (Per-Board Detection)
|
||||
|
||||
### Detection Algorithm
|
||||
|
||||
```typescript
|
||||
// Parse JIRA_BOARDS from .env
|
||||
const boardsConfig = "101,102,QA,Dashboard"; // Mixed!
|
||||
const boardEntries = boardsConfig.split(',').map(b => b.trim());
|
||||
const finalBoardIds = [];
|
||||
|
||||
// Check EACH board individually
|
||||
for (const entry of boardEntries) {
|
||||
const isNumeric = /^\d+$/.test(entry);
|
||||
|
||||
if (isNumeric) {
|
||||
// Entry is a board ID - validate it exists
|
||||
const boardId = parseInt(entry);
|
||||
const board = await checkBoard(boardId);
|
||||
if (board) {
|
||||
console.log(`✅ Board ${boardId}: ${board.name} (exists)`);
|
||||
finalBoardIds.push(boardId);
|
||||
} else {
|
||||
console.error(`⚠️ Board ${boardId}: Not found`);
|
||||
}
|
||||
} else {
|
||||
// Entry is a board name - create it
|
||||
console.log(`📦 Creating board: ${entry}...`);
|
||||
const board = await createBoard(entry, projectKey);
|
||||
console.log(`✅ Created: ${entry} (ID: ${board.id})`);
|
||||
finalBoardIds.push(board.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Update .env if any boards were created
|
||||
if (createdBoardIds.length > 0) {
|
||||
updateEnv({ JIRA_BOARDS: finalBoardIds.join(',') });
|
||||
}
|
||||
```
|
||||
|
||||
**Key improvement**: Per-board detection instead of all-or-nothing!
|
||||
- `JIRA_BOARDS=1,2,3` → Validates all IDs
|
||||
- `JIRA_BOARDS=A,B,C` → Creates all boards
|
||||
- `JIRA_BOARDS=1,2,C` → Validates 1,2, creates C (mixed!)
|
||||
|
||||
### Board Creation API
|
||||
|
||||
**Jira REST API** (v3):
|
||||
```bash
|
||||
POST /rest/api/3/board
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Frontend Board",
|
||||
"type": "scrum",
|
||||
"filterId": 10000, # Filter for project issues
|
||||
"location": {
|
||||
"type": "project",
|
||||
"projectKeyOrId": "PROJECTKEY" # CRITICAL: Associates board with project
|
||||
}
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"id": 101,
|
||||
"name": "Frontend Board",
|
||||
"type": "scrum"
|
||||
}
|
||||
```
|
||||
|
||||
**IMPORTANT**: The `location` field is **MANDATORY** to associate the board with a project. Without it, Jira creates the board but leaves it detached, requiring manual connection via the UI.
|
||||
|
||||
**Filter creation** (required for board):
|
||||
```bash
|
||||
POST /rest/api/3/filter
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "PROJECTKEY Issues",
|
||||
"jql": "project = PROJECTKEY"
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"id": 10000
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Example 1: All Names (Create Boards)
|
||||
|
||||
**Before** (`.env`):
|
||||
```bash
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=Frontend,Backend,QA,DevOps
|
||||
```
|
||||
|
||||
**After validation**:
|
||||
```bash
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=101,102,103,104
|
||||
```
|
||||
|
||||
**What happened**:
|
||||
- Detected non-numeric values (names)
|
||||
- Created 4 boards in Jira
|
||||
- Updated .env with actual board IDs
|
||||
|
||||
### Example 2: All IDs (Validate Existing)
|
||||
|
||||
**Before** (`.env`):
|
||||
```bash
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=1,2,3
|
||||
```
|
||||
|
||||
**After validation**:
|
||||
```bash
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=1,2,3
|
||||
```
|
||||
|
||||
**What happened**:
|
||||
- Detected numeric values (IDs)
|
||||
- Validated all boards exist
|
||||
- No changes needed
|
||||
|
||||
### Example 3: Mixed IDs and Names (SMART!)
|
||||
|
||||
**Before** (`.env`):
|
||||
```bash
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=101,102,QA,Dashboard
|
||||
```
|
||||
|
||||
**After validation**:
|
||||
```bash
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=101,102,103,104
|
||||
```
|
||||
|
||||
**What happened**:
|
||||
- Validated boards 101, 102 exist
|
||||
- Created "QA" board (got ID 103)
|
||||
- Created "Dashboard" board (got ID 104)
|
||||
- Updated .env with ALL board IDs
|
||||
- **This is the key feature** - you can mix existing IDs with new board names!
|
||||
|
||||
### Example 4: Fix Invalid Project
|
||||
|
||||
**Before** (`.env`):
|
||||
```bash
|
||||
JIRA_PROJECT=NONEXISTENT
|
||||
JIRA_BOARDS=1,2
|
||||
```
|
||||
|
||||
**After validation** (user selected existing project):
|
||||
```bash
|
||||
JIRA_PROJECT=EXISTINGPROJ
|
||||
JIRA_BOARDS=1,2
|
||||
```
|
||||
|
||||
**What happened**:
|
||||
- Project NONEXISTENT not found
|
||||
- User selected EXISTINGPROJ from list
|
||||
- Updated .env with correct project key
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error 1: Invalid Credentials
|
||||
|
||||
**Symptom**: API calls fail with 401 Unauthorized
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
❌ Jira API authentication failed
|
||||
|
||||
Please check:
|
||||
1. JIRA_API_TOKEN is correct
|
||||
2. JIRA_EMAIL matches your Jira account
|
||||
3. JIRA_DOMAIN is correct (yourcompany.atlassian.net)
|
||||
|
||||
Generate new token at:
|
||||
https://id.atlassian.com/manage-profile/security/api-tokens
|
||||
```
|
||||
|
||||
### Error 2: Insufficient Permissions
|
||||
|
||||
**Symptom**: Cannot create projects/boards (403 Forbidden)
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
❌ Insufficient permissions to create resources
|
||||
|
||||
You need:
|
||||
- Project Creator permission (for projects)
|
||||
- Board Creator permission (for boards)
|
||||
|
||||
Contact your Jira administrator to request permissions.
|
||||
```
|
||||
|
||||
### Error 3: Project Key Already Taken
|
||||
|
||||
**Symptom**: Project creation fails (key exists)
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
❌ Project key "PROJ" already exists
|
||||
|
||||
Options:
|
||||
1. Use a different project key
|
||||
2. Select the existing project
|
||||
3. Cancel
|
||||
|
||||
Your choice [2]:
|
||||
```
|
||||
|
||||
### Error 4: Network/API Errors
|
||||
|
||||
**Symptom**: API calls timeout or fail
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
❌ Jira API error: Request timeout
|
||||
|
||||
Please check:
|
||||
1. Internet connection
|
||||
2. Jira domain is correct
|
||||
3. Jira is not down (check status.atlassian.com)
|
||||
|
||||
Retry? [Y/n]:
|
||||
```
|
||||
|
||||
## Integration with SpecWeave Workflow
|
||||
|
||||
### Automatic Validation
|
||||
|
||||
When using `/specweave-jira:sync`, validation runs automatically:
|
||||
|
||||
```bash
|
||||
/specweave-jira:sync 0014
|
||||
|
||||
# Internally calls:
|
||||
1. validateJiraResources()
|
||||
2. Fix missing project/boards
|
||||
3. Proceed with sync
|
||||
```
|
||||
|
||||
### Manual Validation
|
||||
|
||||
Run validation independently:
|
||||
|
||||
```bash
|
||||
# Via skill
|
||||
"Validate my Jira configuration"
|
||||
|
||||
# Via TypeScript
|
||||
npx tsx src/utils/external-resource-validator.ts
|
||||
|
||||
# Via CLI (future)
|
||||
specweave validate-jira
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
✅ **Use board names for initial setup**:
|
||||
```bash
|
||||
JIRA_BOARDS=Sprint-1,Sprint-2,Backlog
|
||||
```
|
||||
- System creates boards automatically
|
||||
- Updates .env with IDs
|
||||
- One-time setup, then use IDs
|
||||
|
||||
✅ **Use board IDs after creation**:
|
||||
```bash
|
||||
JIRA_BOARDS=101,102,103
|
||||
```
|
||||
- Faster validation (no creation needed)
|
||||
- More reliable (IDs don't change)
|
||||
|
||||
✅ **Keep .env in version control** (gitignored tokens):
|
||||
```bash
|
||||
# Commit project/board structure
|
||||
JIRA_PROJECT=PROJ
|
||||
JIRA_BOARDS=101,102,103
|
||||
|
||||
# Don't commit sensitive data
|
||||
JIRA_API_TOKEN=<redacted>
|
||||
JIRA_EMAIL=<redacted>
|
||||
```
|
||||
|
||||
✅ **Document board mapping** (in README):
|
||||
```markdown
|
||||
## Jira Boards
|
||||
|
||||
- Board 101: Frontend Team
|
||||
- Board 102: Backend Team
|
||||
- Board 103: QA Team
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
This skill ensures your Jira configuration is **always valid** by:
|
||||
|
||||
1. ✅ **Validating projects** - Check if project exists, prompt to select or create
|
||||
2. ✅ **Validating boards** - Check if boards exist (IDs) or create them (names)
|
||||
3. ✅ **Auto-updating .env** - Replace board names with IDs after creation
|
||||
4. ✅ **Clear error messages** - Actionable guidance for all failures
|
||||
5. ✅ **Non-blocking** - Graceful degradation with manual fallback
|
||||
|
||||
**Result**: Zero manual Jira setup - system handles everything!
|
||||
|
||||
---
|
||||
|
||||
**Skill Version**: 1.0.0
|
||||
**Introduced**: SpecWeave v0.9.5
|
||||
**Last Updated**: 2025-11-09
|
||||
3
skills/jira-sync/.gitignore
vendored
Normal file
3
skills/jira-sync/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
test-results/
|
||||
*.log
|
||||
.DS_Store
|
||||
291
skills/jira-sync/README.md
Normal file
291
skills/jira-sync/README.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# jira-sync Skill
|
||||
|
||||
**Status**: To be developed
|
||||
**Priority**: Medium
|
||||
|
||||
## Purpose
|
||||
|
||||
Bidirectional sync between SpecWeave increments and JIRA (Atlassian)
|
||||
|
||||
**Note**: This skill handles ONLY JIRA. For Azure DevOps, see `ado-sync` skill.
|
||||
|
||||
## Features
|
||||
|
||||
### Export to JIRA
|
||||
- Create JIRA issues from SpecWeave increments
|
||||
- Map spec.md user stories → JIRA Stories
|
||||
- Map tasks.md tasks → JIRA Sub-tasks
|
||||
- Create Epics if specified in spec.md
|
||||
- Set priorities, labels, components
|
||||
|
||||
### Import from JIRA
|
||||
- Sync JIRA updates back to SpecWeave
|
||||
- Import existing JIRA issues as increments
|
||||
- Update status, assignees, comments
|
||||
|
||||
### Bidirectional Sync
|
||||
- Keep status in sync (To Do, In Progress, Done)
|
||||
- Sync descriptions and acceptance criteria
|
||||
- Sync comments
|
||||
- Handle conflicts intelligently
|
||||
|
||||
## JIRA-Specific Concepts
|
||||
|
||||
### Mapping: SpecWeave → JIRA
|
||||
|
||||
| SpecWeave | JIRA |
|
||||
|-----------|------|
|
||||
| spec.md (with Epic) | Epic |
|
||||
| spec.md User Story | Story |
|
||||
| tasks.md Task | Sub-task |
|
||||
| Acceptance Tests (spec.md) | Acceptance Criteria (Story) |
|
||||
| Acceptance Criteria (tasks.md) | Sub-task checklist |
|
||||
| Status: planned | To Do |
|
||||
| Status: in-progress | In Progress |
|
||||
| Status: completed | Done |
|
||||
|
||||
### JIRA Structure Example
|
||||
|
||||
**spec.md with JIRA structure**:
|
||||
```markdown
|
||||
---
|
||||
increment: 002-payment-processing
|
||||
status: planned
|
||||
structure: jira
|
||||
jira_epic: PROJ-123
|
||||
---
|
||||
|
||||
## Epic: E-commerce Infrastructure
|
||||
**JIRA**: PROJ-123
|
||||
|
||||
### Story: Subscribe to Plan
|
||||
**JIRA**: PROJ-124
|
||||
**Priority**: P1
|
||||
**Labels**: payments, stripe
|
||||
**Components**: Backend, Frontend
|
||||
|
||||
**Description**:
|
||||
As a user, I want to subscribe to a monthly plan...
|
||||
|
||||
**Acceptance Criteria**:
|
||||
- User can select plan
|
||||
- Payment processed
|
||||
- Subscription activated
|
||||
```
|
||||
|
||||
**tasks.md creates Sub-tasks**:
|
||||
```markdown
|
||||
## Tasks for PROJ-124 (Subscribe to Plan)
|
||||
|
||||
### Task T001: Create StripeService
|
||||
**JIRA**: PROJ-125 (Sub-task of PROJ-124)
|
||||
**Agent**: nodejs-backend
|
||||
|
||||
**Description**: Create Stripe service class...
|
||||
|
||||
**Acceptance Criteria**:
|
||||
- [ ] StripeService class exists
|
||||
- [ ] Unit tests passing
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
**JIRA Cloud**:
|
||||
|
||||
|
||||
**JIRA Server/Data Center**:
|
||||
```yaml
|
||||
jira_sync:
|
||||
type: "server"
|
||||
url: "https://jira.your-company.com"
|
||||
username: "user"
|
||||
password: "${JIRA_PASSWORD}" # From environment variable
|
||||
project_key: "PROJ"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
|
||||
## Workflow
|
||||
|
||||
### Export Workflow (SpecWeave → JIRA)
|
||||
|
||||
```
|
||||
User: Creates increment in SpecWeave
|
||||
.specweave/increments/0002-payment/
|
||||
spec.md (with structure: jira)
|
||||
tasks.md
|
||||
|
||||
↓ jira-sync detects new increment
|
||||
|
||||
Creates in JIRA:
|
||||
Epic: PROJ-123 "E-commerce Infrastructure"
|
||||
Story: PROJ-124 "Subscribe to Plan"
|
||||
Sub-task: PROJ-125 "Create StripeService"
|
||||
Sub-task: PROJ-126 "Create API endpoints"
|
||||
|
||||
Links created:
|
||||
spec.md → PROJ-124
|
||||
tasks.md T001 → PROJ-125
|
||||
tasks.md T002 → PROJ-126
|
||||
```
|
||||
|
||||
### Import Workflow (JIRA → SpecWeave)
|
||||
|
||||
```
|
||||
User: Updates JIRA issue status to "In Progress"
|
||||
|
||||
↓ JIRA webhook triggers
|
||||
|
||||
jira-sync:
|
||||
Detects change to PROJ-124
|
||||
Finds linked increment: 002-payment
|
||||
Updates: .specweave/increments/0002-payment/spec.md
|
||||
status: planned → in-progress
|
||||
```
|
||||
|
||||
### Bidirectional Sync
|
||||
|
||||
```
|
||||
User: Checks off task in tasks.md
|
||||
- [x] T001: Create StripeService
|
||||
|
||||
↓ jira-sync detects change
|
||||
|
||||
Updates JIRA:
|
||||
PROJ-125 status → Done
|
||||
|
||||
User: Changes PROJ-124 to "Done" in JIRA
|
||||
|
||||
↓ JIRA webhook triggers
|
||||
|
||||
jira-sync updates SpecWeave:
|
||||
.specweave/increments/0002-payment/spec.md
|
||||
status: in-progress → completed
|
||||
```
|
||||
|
||||
## API Integration
|
||||
|
||||
### JIRA REST API Endpoints Used
|
||||
|
||||
```typescript
|
||||
// Create Epic
|
||||
POST /rest/api/3/issue
|
||||
{
|
||||
"fields": {
|
||||
"project": { "key": "PROJ" },
|
||||
"issuetype": { "name": "Epic" },
|
||||
"summary": "E-commerce Infrastructure",
|
||||
"customfield_10011": "epic-name" // Epic Name field
|
||||
}
|
||||
}
|
||||
|
||||
// Create Story (linked to Epic)
|
||||
POST /rest/api/3/issue
|
||||
{
|
||||
"fields": {
|
||||
"project": { "key": "PROJ" },
|
||||
"issuetype": { "name": "Story" },
|
||||
"summary": "Subscribe to Plan",
|
||||
"parent": { "key": "PROJ-123" } // Link to Epic
|
||||
}
|
||||
}
|
||||
|
||||
// Create Sub-task
|
||||
POST /rest/api/3/issue
|
||||
{
|
||||
"fields": {
|
||||
"project": { "key": "PROJ" },
|
||||
"issuetype": { "name": "Sub-task" },
|
||||
"parent": { "key": "PROJ-124" },
|
||||
"summary": "Create StripeService"
|
||||
}
|
||||
}
|
||||
|
||||
// Update status
|
||||
POST /rest/api/3/issue/{issueKey}/transitions
|
||||
{
|
||||
"transition": { "id": "31" } // "In Progress"
|
||||
}
|
||||
```
|
||||
|
||||
## Webhooks
|
||||
|
||||
### Setup JIRA Webhook
|
||||
|
||||
1. Go to JIRA Settings → System → Webhooks
|
||||
2. Create webhook:
|
||||
- URL: `https://your-app.com/api/webhooks/jira`
|
||||
- Events: Issue created, updated, deleted
|
||||
- Secret: Random string (store in JIRA_WEBHOOK_SECRET)
|
||||
|
||||
### Webhook Handler
|
||||
|
||||
```typescript
|
||||
// Receives JIRA webhook
|
||||
POST /api/webhooks/jira
|
||||
|
||||
// jira-sync processes:
|
||||
1. Verify webhook signature
|
||||
2. Extract issue data
|
||||
3. Find linked SpecWeave increment
|
||||
4. Update spec.md or tasks.md
|
||||
5. Commit changes (optional)
|
||||
```
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
**Scenario**: Both SpecWeave and JIRA updated simultaneously
|
||||
|
||||
**Strategy**:
|
||||
1. **Timestamp-based**: Latest change wins
|
||||
2. **User prompt**: Ask user which to keep
|
||||
3. **Merge**: Combine changes if possible
|
||||
|
||||
**Example**:
|
||||
```
|
||||
SpecWeave: status → in-progress (10:00 AM)
|
||||
JIRA: status → done (10:05 AM)
|
||||
|
||||
jira-sync:
|
||||
Latest is JIRA (10:05 AM)
|
||||
Update SpecWeave → done
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Common errors**:
|
||||
- JIRA API rate limits → Retry with exponential backoff
|
||||
- Authentication failed → Notify user, check credentials
|
||||
- Issue not found → Create if export, skip if import
|
||||
- Network errors → Queue for retry
|
||||
|
||||
## Testing
|
||||
|
||||
**Test scenarios**:
|
||||
1. Create increment → Creates JIRA issues
|
||||
2. Update JIRA → Updates SpecWeave
|
||||
3. Update SpecWeave → Updates JIRA
|
||||
4. Conflict resolution
|
||||
5. Webhook handling
|
||||
6. Error recovery
|
||||
|
||||
## Integration with Other Skills
|
||||
|
||||
- **task-builder**: Reads JIRA structure from spec.md
|
||||
- **increment-planner**: Can specify structure: jira
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Support for JIRA sprints/iterations
|
||||
- Sync custom fields
|
||||
- Attachment sync
|
||||
- Advanced filtering (which issues to sync)
|
||||
- Bulk import from JIRA
|
||||
|
||||
---
|
||||
|
||||
**To implement**: See task in .specweave/increments/
|
||||
|
||||
**See also**: `ado-sync` skill for Azure DevOps integration
|
||||
226
skills/jira-sync/SKILL.md
Normal file
226
skills/jira-sync/SKILL.md
Normal file
@@ -0,0 +1,226 @@
|
||||
---
|
||||
name: jira-sync
|
||||
description: Sync SpecWeave increments with JIRA epics/stories. Content flows SpecWeave→JIRA, status flows JIRA→SpecWeave. Activates ONLY when user asks questions about JIRA integration or needs help configuring JIRA sync. Does NOT activate for slash commands. For syncing, use /specweave-jira:sync command instead. Coordinates with specweave-jira-mapper agent.
|
||||
allowed-tools: Read, Write, Edit, Task, Bash
|
||||
---
|
||||
|
||||
# JIRA Sync Skill
|
||||
|
||||
Coordinates JIRA synchronization by delegating to `specweave-jira-mapper` agent.
|
||||
|
||||
**Sync Behavior**: Content (specs, tasks) syncs SpecWeave → JIRA. Status (open/closed) syncs JIRA → SpecWeave.
|
||||
|
||||
**⚠️ IMPORTANT**: This skill provides HELP and GUIDANCE about JIRA sync. For actual syncing, users should use the `/specweave-jira:sync` command directly. This skill should NOT auto-activate when the command is being invoked.
|
||||
|
||||
## When to Activate
|
||||
|
||||
✅ **Do activate when**:
|
||||
- User asks: "How do I set up JIRA sync?"
|
||||
- User asks: "What JIRA credentials do I need?"
|
||||
- User asks: "How does JIRA sync work?"
|
||||
- User needs help configuring JIRA integration
|
||||
|
||||
❌ **Do NOT activate when**:
|
||||
- User invokes `/specweave-jira:sync` command (command handles it)
|
||||
- Command is already running (avoid duplicate invocation)
|
||||
- Task completion hook is syncing (automatic process)
|
||||
|
||||
## Responsibilities
|
||||
|
||||
1. Answer questions about JIRA sync configuration
|
||||
2. Help validate prerequisites (JIRA credentials, increment structure)
|
||||
3. Explain sync directions: content (SpecWeave→JIRA), status (JIRA→SpecWeave)
|
||||
4. Provide troubleshooting guidance
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ CRITICAL: Secrets Required (MANDATORY CHECK)
|
||||
|
||||
**BEFORE attempting JIRA sync, CHECK for JIRA credentials.**
|
||||
|
||||
### Step 1: Check If Credentials Exist
|
||||
|
||||
```bash
|
||||
# Check .env file for both required credentials
|
||||
if [ -f .env ] && grep -q "JIRA_API_TOKEN" .env && grep -q "JIRA_EMAIL" .env; then
|
||||
echo "✅ JIRA credentials found"
|
||||
else
|
||||
# Credentials NOT found - STOP and prompt user
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 2: If Credentials Missing, STOP and Show This Message
|
||||
|
||||
```
|
||||
🔐 **JIRA API Token and Email Required**
|
||||
|
||||
I need your JIRA API token and email to sync with JIRA.
|
||||
|
||||
**How to get it**:
|
||||
1. Go to: https://id.atlassian.com/manage-profile/security/api-tokens
|
||||
2. Log in with your Atlassian account
|
||||
3. Click "Create API token"
|
||||
4. Give it a label (e.g., "specweave-sync")
|
||||
5. Click "Create"
|
||||
6. **Copy the token immediately** (you can't see it again!)
|
||||
|
||||
**Where I'll save it**:
|
||||
- File: `.env` (gitignored, secure)
|
||||
- Format:
|
||||
```
|
||||
JIRA_API_TOKEN=your-jira-api-token-here
|
||||
JIRA_EMAIL=your-email@example.com
|
||||
JIRA_DOMAIN=your-domain.atlassian.net
|
||||
```
|
||||
|
||||
**Security**:
|
||||
✅ .env is in .gitignore (never committed to git)
|
||||
✅ Token is random alphanumeric string (variable length)
|
||||
✅ Stored locally only (not in source code)
|
||||
|
||||
Please provide:
|
||||
1. Your JIRA API token:
|
||||
2. Your JIRA email:
|
||||
3. Your JIRA domain (e.g., company.atlassian.net):
|
||||
```
|
||||
|
||||
### Step 3: Validate Credentials Format
|
||||
|
||||
```bash
|
||||
# Validate email format
|
||||
if [[ ! "$JIRA_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||||
echo "⚠️ Warning: Email format unexpected"
|
||||
echo "Expected: valid email address"
|
||||
echo "Got: $JIRA_EMAIL"
|
||||
fi
|
||||
|
||||
# Validate domain format
|
||||
if [[ ! "$JIRA_DOMAIN" =~ \.atlassian\.net$ ]]; then
|
||||
echo "⚠️ Warning: Domain format unexpected"
|
||||
echo "Expected: *.atlassian.net"
|
||||
echo "Got: $JIRA_DOMAIN"
|
||||
echo "Note: Self-hosted JIRA may have different domain format"
|
||||
fi
|
||||
|
||||
# Token validation (just check it's not empty)
|
||||
if [ -z "$JIRA_API_TOKEN" ]; then
|
||||
echo "❌ Error: JIRA API token is empty"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 4: Save Credentials Securely
|
||||
|
||||
```bash
|
||||
# Save to .env
|
||||
cat >> .env << EOF
|
||||
JIRA_API_TOKEN=$JIRA_API_TOKEN
|
||||
JIRA_EMAIL=$JIRA_EMAIL
|
||||
JIRA_DOMAIN=$JIRA_DOMAIN
|
||||
EOF
|
||||
|
||||
# Ensure .env is gitignored
|
||||
if ! grep -q "^\\.env$" .gitignore; then
|
||||
echo ".env" >> .gitignore
|
||||
fi
|
||||
|
||||
# Create .env.example for team
|
||||
cat > .env.example << 'EOF'
|
||||
# JIRA API Token
|
||||
# Get from: https://id.atlassian.com/manage-profile/security/api-tokens
|
||||
JIRA_API_TOKEN=your-jira-api-token
|
||||
JIRA_EMAIL=your-email@example.com
|
||||
JIRA_DOMAIN=your-domain.atlassian.net
|
||||
EOF
|
||||
|
||||
echo "✅ Credentials saved to .env (gitignored)"
|
||||
echo "✅ Created .env.example for team (commit this)"
|
||||
```
|
||||
|
||||
### Step 5: Use Credentials in Sync
|
||||
|
||||
```bash
|
||||
# Export for JIRA API calls
|
||||
export JIRA_API_TOKEN=$(grep JIRA_API_TOKEN .env | cut -d '=' -f2)
|
||||
export JIRA_EMAIL=$(grep JIRA_EMAIL .env | cut -d '=' -f2)
|
||||
export JIRA_DOMAIN=$(grep JIRA_DOMAIN .env | cut -d '=' -f2)
|
||||
|
||||
# Create Basic Auth header (JIRA uses email:token)
|
||||
AUTH=$(echo -n "$JIRA_EMAIL:$JIRA_API_TOKEN" | base64)
|
||||
|
||||
# Use in JIRA API calls
|
||||
curl -H "Authorization: Basic $AUTH" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://$JIRA_DOMAIN/rest/api/3/issue/PROJ-123
|
||||
```
|
||||
|
||||
### Step 6: Never Log Secrets
|
||||
|
||||
```bash
|
||||
# ❌ WRONG - Logs secret
|
||||
echo "Using token: $JIRA_API_TOKEN"
|
||||
|
||||
# ✅ CORRECT - Masks secret
|
||||
echo "Using JIRA credentials (token present: ✅, email: $JIRA_EMAIL)"
|
||||
```
|
||||
|
||||
### Step 7: Error Handling
|
||||
|
||||
```bash
|
||||
# If API call fails with 401 Unauthorized
|
||||
if [ $? -eq 401 ]; then
|
||||
echo "❌ JIRA credentials invalid"
|
||||
echo ""
|
||||
echo "Possible causes:"
|
||||
echo "1. API token expired or revoked"
|
||||
echo "2. Email address incorrect"
|
||||
echo "3. Domain incorrect (check: $JIRA_DOMAIN)"
|
||||
echo "4. Account lacks permissions (need: project admin or issue create/edit)"
|
||||
echo ""
|
||||
echo "Please verify credentials:"
|
||||
echo "https://id.atlassian.com/manage-profile/security/api-tokens"
|
||||
fi
|
||||
|
||||
# If API call fails with 403 Forbidden
|
||||
if [ $? -eq 403 ]; then
|
||||
echo "❌ JIRA permission denied"
|
||||
echo ""
|
||||
echo "Your account lacks permissions for this operation."
|
||||
echo "Required permissions:"
|
||||
echo "- Browse projects"
|
||||
echo "- Create issues"
|
||||
echo "- Edit issues"
|
||||
echo "- Administer projects (for Epic creation)"
|
||||
echo ""
|
||||
echo "Contact your JIRA administrator."
|
||||
fi
|
||||
```
|
||||
|
||||
### Step 8: Production Recommendations
|
||||
|
||||
**For production deployments, use OAuth 2.0** instead of API tokens:
|
||||
|
||||
**Why OAuth 2.0?**
|
||||
- ✅ More secure (no long-lived credentials)
|
||||
- ✅ Fine-grained permissions (scopes)
|
||||
- ✅ Automatic token refresh
|
||||
- ✅ Audit trail in JIRA
|
||||
|
||||
**How to set up OAuth 2.0**:
|
||||
1. Go to: https://developer.atlassian.com/console/myapps/
|
||||
2. Create a new app
|
||||
3. Configure OAuth 2.0 credentials
|
||||
4. Add required scopes (read:jira-work, write:jira-work)
|
||||
5. Use OAuth flow instead of API token
|
||||
|
||||
**For self-hosted JIRA**: Use Personal Access Tokens (PAT) instead of API tokens.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
**Export**: `/sync-jira export 0001`
|
||||
**Import**: `/sync-jira import PROJ-123`
|
||||
**Sync**: `/sync-jira sync 0001`
|
||||
|
||||
All conversion logic is handled by the `specweave-jira-mapper` agent.
|
||||
500
skills/specweave-jira-mapper/SKILL.md
Normal file
500
skills/specweave-jira-mapper/SKILL.md
Normal file
@@ -0,0 +1,500 @@
|
||||
---
|
||||
name: specweave-jira-mapper
|
||||
description: Expert in mapping SpecWeave increments to JIRA epics/stories/subtasks. Content flows SpecWeave→JIRA, status flows JIRA→SpecWeave. Handles export (increment → JIRA), import (JIRA → increment). Activates for JIRA sync, issue creation, import from JIRA.
|
||||
tools: Read, Write, Edit, Bash
|
||||
model: claude-sonnet-4-5-20250929
|
||||
---
|
||||
|
||||
# Specweave Jira Mapper Skill
|
||||
|
||||
You are an expert in mapping SpecWeave concepts to JIRA and vice versa with precision and traceability.
|
||||
|
||||
## Core Responsibilities
|
||||
|
||||
1. **Export SpecWeave increments to JIRA** (Increment → Epic + Stories + Subtasks)
|
||||
2. **Import JIRA epics as SpecWeave increments** (Epic → Increment structure)
|
||||
3. **Sync**: Content flows SpecWeave→JIRA, status flows JIRA→SpecWeave
|
||||
4. **Maintain traceability** (store keys, URLs, timestamps)
|
||||
5. **Validate mapping accuracy** using test cases
|
||||
6. **Handle edge cases** (missing fields, invalid statuses, API errors)
|
||||
|
||||
---
|
||||
|
||||
## Concept Mappings
|
||||
|
||||
### SpecWeave → JIRA
|
||||
|
||||
| SpecWeave Concept | JIRA Concept | Mapping Rules |
|
||||
|-------------------|--------------|---------------|
|
||||
| **Increment** | Epic | Title: `[Increment ###] [Title]` |
|
||||
| **User Story** (from spec.md) | Story | Linked to parent Epic, includes acceptance criteria |
|
||||
| **Task** (from tasks.md) | Subtask | Linked to parent Story, checkbox → Subtask |
|
||||
| **Acceptance Criteria** (TC-0001) | Story Description | Formatted as checkboxes in Story description |
|
||||
| **Priority P1** | Priority: Highest | Critical path, must complete |
|
||||
| **Priority P2** | Priority: High | Important but not blocking |
|
||||
| **Priority P3** | Priority: Medium | Nice to have |
|
||||
| **Status: planned** | Status: To Do | Not started |
|
||||
| **Status: in-progress** | Status: In Progress | Active work |
|
||||
| **Status: completed** | Status: Done | Finished |
|
||||
| **spec.md** | Epic Description | Summary + link to spec (if GitHub repo) |
|
||||
| **context-manifest.yaml** | Custom Field: Context | Serialized YAML in custom field (optional) |
|
||||
|
||||
### JIRA → SpecWeave
|
||||
|
||||
| JIRA Concept | SpecWeave Concept | Import Rules |
|
||||
|--------------|-------------------|--------------|
|
||||
| **Epic** | Increment | Auto-number next available (e.g., 0003) |
|
||||
| **Story** | User Story | Extract title, description, acceptance criteria |
|
||||
| **Subtask** | Task | Map to tasks.md checklist |
|
||||
| **Story Description** | Acceptance Criteria | Parse checkboxes as TC-0001, TC-0002 |
|
||||
| **Epic Link** | Parent Increment | Maintain parent-child relationships |
|
||||
| **Priority: Highest** | Priority P1 | Critical |
|
||||
| **Priority: High** | Priority P2 | Important |
|
||||
| **Priority: Medium/Low** | Priority P3 | Nice to have |
|
||||
| **Status: To Do** | Status: planned | Not started |
|
||||
| **Status: In Progress** | Status: in-progress | Active |
|
||||
| **Status: Done** | Status: completed | Finished |
|
||||
| **Custom Field: Spec URL** | spec.md link | Cross-reference |
|
||||
|
||||
---
|
||||
|
||||
## Conversion Workflows
|
||||
|
||||
### 1. Export: Increment → JIRA Epic
|
||||
|
||||
**Input**: `.specweave/increments/0001-feature-name/`
|
||||
|
||||
**Prerequisites**:
|
||||
- Increment folder exists
|
||||
- `spec.md` exists with valid frontmatter
|
||||
- `tasks.md` exists
|
||||
- JIRA connection configured
|
||||
|
||||
**Process**:
|
||||
|
||||
1. **Read increment files**:
|
||||
```bash
|
||||
# Read spec.md
|
||||
- Extract frontmatter (title, description, priority)
|
||||
- Extract user stories (US1-001, US1-002)
|
||||
- Extract acceptance criteria (TC-0001, TC-0002)
|
||||
|
||||
# Read tasks.md
|
||||
- Extract task checklist
|
||||
- Group tasks by user story (if structured)
|
||||
```
|
||||
|
||||
2. **Create JIRA Epic**:
|
||||
```
|
||||
Title: [Increment 0001] Feature Name
|
||||
Description:
|
||||
{spec.md summary}
|
||||
|
||||
Specification: {link to spec.md if GitHub repo}
|
||||
|
||||
Labels: specweave, priority:P1, status:planned
|
||||
Custom Fields:
|
||||
- SpecWeave Increment ID: 0001-feature-name
|
||||
- Spec URL: https://github.com/user/repo/blob/main/.specweave/increments/0001-feature-name/spec.md
|
||||
```
|
||||
|
||||
3. **Create JIRA Stories** (one per user story):
|
||||
```
|
||||
Title: {User Story title}
|
||||
Description:
|
||||
**As a** {role}
|
||||
**I want to** {goal}
|
||||
**So that** {benefit}
|
||||
|
||||
**Acceptance Criteria**:
|
||||
- [ ] TC-0001: {criteria}
|
||||
- [ ] TC-0002: {criteria}
|
||||
|
||||
Epic Link: {Epic Key}
|
||||
Labels: specweave, user-story
|
||||
```
|
||||
|
||||
4. **Create JIRA Subtasks** (from tasks.md):
|
||||
```
|
||||
Title: {Task description}
|
||||
Parent: {Story Key}
|
||||
Labels: specweave, task
|
||||
```
|
||||
|
||||
5. **Update increment frontmatter**:
|
||||
```yaml
|
||||
jira:
|
||||
epic_key: "PROJ-123"
|
||||
epic_url: "https://jira.company.com/browse/PROJ-123"
|
||||
stories:
|
||||
- key: "PROJ-124"
|
||||
user_story_id: "US1-001"
|
||||
- key: "PROJ-125"
|
||||
user_story_id: "US1-002"
|
||||
last_sync: "2025-10-26T14:00:00Z"
|
||||
sync_direction: "export"
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```
|
||||
✅ Exported to JIRA!
|
||||
|
||||
Epic: PROJ-123
|
||||
URL: https://jira.company.com/browse/PROJ-123
|
||||
Stories: 5 created (PROJ-124 to PROJ-128)
|
||||
Subtasks: 12 created
|
||||
Last Sync: 2025-10-26T14:00:00Z
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Import: JIRA Epic → Increment
|
||||
|
||||
**Input**: JIRA Epic key (e.g., `PROJ-123`)
|
||||
|
||||
**Prerequisites**:
|
||||
- Valid JIRA Epic key
|
||||
- Epic exists and is accessible
|
||||
- JIRA connection configured
|
||||
|
||||
**Process**:
|
||||
|
||||
1. **Fetch Epic details** (via JIRA API/MCP):
|
||||
```
|
||||
- Epic title, description, labels
|
||||
- Epic custom fields (if SpecWeave ID exists)
|
||||
- Priority, status
|
||||
```
|
||||
|
||||
2. **Fetch linked Stories and Subtasks**:
|
||||
```
|
||||
- All Stories linked to Epic
|
||||
- All Subtasks linked to each Story
|
||||
- Story descriptions (acceptance criteria)
|
||||
```
|
||||
|
||||
3. **Auto-number next increment**:
|
||||
```bash
|
||||
# Scan .specweave/increments/ for highest number
|
||||
ls .specweave/increments/ | grep -E '^[0-9]{4}' | sort -n | tail -1
|
||||
# Increment by 1 → 0003
|
||||
```
|
||||
|
||||
4. **Create increment folder**:
|
||||
```
|
||||
.specweave/increments/0003-imported-feature/
|
||||
```
|
||||
|
||||
5. **Generate spec.md**:
|
||||
```yaml
|
||||
---
|
||||
increment_id: "0003"
|
||||
title: "{Epic title}"
|
||||
status: "{mapped from JIRA status}"
|
||||
priority: "{mapped from JIRA priority}"
|
||||
created_at: "{Epic created date}"
|
||||
jira:
|
||||
epic_key: "PROJ-123"
|
||||
epic_url: "https://jira.company.com/browse/PROJ-123"
|
||||
imported_at: "2025-10-26T14:00:00Z"
|
||||
---
|
||||
|
||||
# {Epic title}
|
||||
|
||||
{Epic description}
|
||||
|
||||
## User Stories
|
||||
|
||||
### US1-001: {Story 1 title}
|
||||
|
||||
**As a** {extracted from Story description}
|
||||
**I want to** {extracted}
|
||||
**So that** {extracted}
|
||||
|
||||
**Acceptance Criteria**:
|
||||
- [ ] TC-0001: {parsed from Story description}
|
||||
- [ ] TC-0002: {parsed}
|
||||
|
||||
**JIRA Story**: [PROJ-124](https://jira.company.com/browse/PROJ-124)
|
||||
```
|
||||
|
||||
6. **Generate tasks.md**:
|
||||
```markdown
|
||||
# Tasks: {Increment title}
|
||||
|
||||
## User Story: US1-001
|
||||
|
||||
- [ ] {Subtask 1 title} (JIRA: PROJ-130)
|
||||
- [ ] {Subtask 2 title} (JIRA: PROJ-131)
|
||||
|
||||
## User Story: US1-002
|
||||
|
||||
- [ ] {Subtask 3 title} (JIRA: PROJ-132)
|
||||
```
|
||||
|
||||
7. **Generate context-manifest.yaml** (default):
|
||||
```yaml
|
||||
---
|
||||
spec_sections: []
|
||||
documentation: []
|
||||
max_context_tokens: 10000
|
||||
priority: high
|
||||
auto_refresh: false
|
||||
---
|
||||
```
|
||||
|
||||
8. **Update JIRA Epic** (add custom field if available):
|
||||
```
|
||||
Custom Field: SpecWeave Increment ID = 0003-imported-feature
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```
|
||||
✅ Imported from JIRA!
|
||||
|
||||
Increment: 0003-imported-feature
|
||||
Location: .specweave/increments/0003-imported-feature/
|
||||
User Stories: 5 imported
|
||||
Tasks: 12 imported
|
||||
JIRA Epic: PROJ-123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Bidirectional Sync
|
||||
|
||||
**Trigger**: Manual (`/sync-jira`) or webhook
|
||||
|
||||
**Prerequisites**:
|
||||
- Increment has JIRA metadata in frontmatter
|
||||
- JIRA Epic/Stories exist
|
||||
- Last sync timestamp available
|
||||
|
||||
**Process**:
|
||||
|
||||
1. **Detect changes since last sync**:
|
||||
```
|
||||
SpecWeave changes:
|
||||
- spec.md modified after last_sync
|
||||
- tasks.md modified after last_sync
|
||||
- Task checkboxes changed
|
||||
|
||||
JIRA changes:
|
||||
- Epic/Story/Subtask updated after last_sync
|
||||
- Status changes
|
||||
- New comments
|
||||
```
|
||||
|
||||
2. **Compare and detect conflicts**:
|
||||
```
|
||||
Conflict types:
|
||||
- Title changed in both (SpecWeave + JIRA)
|
||||
- Task marked done in SpecWeave, but JIRA Subtask still "In Progress"
|
||||
- Priority changed in both
|
||||
```
|
||||
|
||||
3. **Present conflicts to user**:
|
||||
```
|
||||
⚠️ Sync Conflicts Detected:
|
||||
|
||||
1. Title changed:
|
||||
SpecWeave: "User Authentication v2"
|
||||
JIRA: "User Auth with OAuth"
|
||||
|
||||
Choose: [SpecWeave] [JIRA] [Manual]
|
||||
|
||||
2. Task status mismatch:
|
||||
Task: "Implement login endpoint"
|
||||
SpecWeave: ✅ completed
|
||||
JIRA Subtask: In Progress
|
||||
|
||||
Choose: [Mark JIRA Done] [Uncheck SpecWeave] [Manual]
|
||||
```
|
||||
|
||||
4. **Apply sync**:
|
||||
```
|
||||
SpecWeave → JIRA:
|
||||
- Update Epic/Story titles
|
||||
- Update Subtask statuses (checkbox → JIRA status)
|
||||
- Add comments for significant changes
|
||||
|
||||
JIRA → SpecWeave:
|
||||
- Update spec.md frontmatter (status, priority)
|
||||
- Update task checkboxes (JIRA Subtask status → checkbox)
|
||||
- Log JIRA comments to increment logs/
|
||||
```
|
||||
|
||||
5. **Update sync timestamps**:
|
||||
```yaml
|
||||
jira:
|
||||
last_sync: "2025-10-26T16:30:00Z"
|
||||
sync_direction: "two-way"
|
||||
conflicts_resolved: 2
|
||||
```
|
||||
|
||||
**Output**:
|
||||
```
|
||||
✅ Synced with JIRA!
|
||||
|
||||
Direction: Two-way
|
||||
Changes Applied:
|
||||
- SpecWeave → JIRA: 3 updates
|
||||
- JIRA → SpecWeave: 5 updates
|
||||
Conflicts Resolved: 2 (user decisions)
|
||||
Last Sync: 2025-10-26T16:30:00Z
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Edge Cases and Error Handling
|
||||
|
||||
### Missing Fields
|
||||
|
||||
**Problem**: Increment missing spec.md or JIRA Epic missing required fields
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
❌ Error: spec.md not found in increment 0001-feature-name
|
||||
|
||||
Expected: .specweave/increments/0001-feature-name/spec.md
|
||||
|
||||
Please create spec.md before exporting to JIRA.
|
||||
```
|
||||
|
||||
### JIRA API Errors
|
||||
|
||||
**Problem**: JIRA API rate limit, authentication failure, network error
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
❌ JIRA API Error: Rate limit exceeded (429)
|
||||
|
||||
Retry in: 60 seconds
|
||||
|
||||
Alternative: Export to JSON and manually import to JIRA later.
|
||||
```
|
||||
|
||||
### Invalid Status Mapping
|
||||
|
||||
**Problem**: JIRA uses custom workflow statuses not in standard mapping
|
||||
|
||||
**Solution**:
|
||||
```
|
||||
⚠️ Unknown JIRA status: "Awaiting Review"
|
||||
|
||||
Available mappings:
|
||||
- To Do → planned
|
||||
- In Progress → in-progress
|
||||
- Done → completed
|
||||
|
||||
Map "Awaiting Review" to: [planned] [in-progress] [completed] [Custom]
|
||||
```
|
||||
|
||||
### Conflict Resolution
|
||||
|
||||
**Problem**: Same field changed in both SpecWeave and JIRA
|
||||
|
||||
**Solution**:
|
||||
- Always ask user for resolution
|
||||
- Provide diff view
|
||||
- Offer merge options
|
||||
- Never auto-resolve conflicts silently
|
||||
|
||||
---
|
||||
|
||||
## Templates
|
||||
|
||||
Use templates in `templates/` folder for consistent outputs:
|
||||
|
||||
- `epic-from-increment.md` - Epic creation template
|
||||
- `story-from-user-story.md` - Story creation template
|
||||
- `increment-from-epic.md` - Increment import template
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
Consult references in `references/` folder:
|
||||
|
||||
- `jira-concepts.md` - JIRA terminology (Epic, Story, Subtask, Epic Link)
|
||||
- `specweave-concepts.md` - SpecWeave structure (Increment, User Story, Task)
|
||||
- `mapping-examples.md` - Real-world conversion examples
|
||||
|
||||
---
|
||||
|
||||
## Test Cases
|
||||
|
||||
Validate all conversions using test cases in `test-cases/`:
|
||||
|
||||
1. **test-1-increment-to-epic.yaml** - Basic export (Increment → Epic)
|
||||
2. **test-2-epic-to-increment.yaml** - Basic import (Epic → Increment)
|
||||
3. **test-3-bidirectional-sync.yaml** - Sync with conflict resolution
|
||||
|
||||
**Run tests**:
|
||||
```bash
|
||||
npm run test:agents:specweave-jira-mapper
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always validate before sync** - Check increment structure, JIRA connection
|
||||
2. **Preserve traceability** - Store JIRA keys in frontmatter, SpecWeave IDs in JIRA
|
||||
3. **Ask before overwriting** - Never auto-resolve conflicts
|
||||
4. **Log all operations** - Write sync logs to `.specweave/increments/{id}/logs/jira-sync.log`
|
||||
5. **Handle errors gracefully** - Provide actionable error messages
|
||||
6. **Test mappings** - Use test cases to validate accuracy
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Export to JIRA
|
||||
|
||||
```
|
||||
User: "Export increment 0001 to JIRA"
|
||||
|
||||
You:
|
||||
1. Read .specweave/increments/0001-*/spec.md and tasks.md
|
||||
2. Extract user stories and tasks
|
||||
3. Create JIRA Epic with title "[Increment 0001] {title}"
|
||||
4. Create Stories for each user story
|
||||
5. Create Subtasks for each task
|
||||
6. Update increment frontmatter with JIRA keys
|
||||
7. Present summary with Epic URL
|
||||
```
|
||||
|
||||
### Import from JIRA
|
||||
|
||||
```
|
||||
User: "Import JIRA epic PROJ-123"
|
||||
|
||||
You:
|
||||
1. Fetch Epic PROJ-123 via JIRA API
|
||||
2. Fetch linked Stories and Subtasks
|
||||
3. Auto-number next increment (e.g., 0003)
|
||||
4. Generate spec.md with user stories
|
||||
5. Generate tasks.md with subtasks
|
||||
6. Generate context-manifest.yaml (default)
|
||||
7. Present summary with increment location
|
||||
```
|
||||
|
||||
### Bidirectional Sync
|
||||
|
||||
```
|
||||
User: "Sync increment 0001 with JIRA"
|
||||
|
||||
You:
|
||||
1. Read increment frontmatter for JIRA keys
|
||||
2. Detect changes since last_sync
|
||||
3. Compare SpecWeave vs JIRA
|
||||
4. Present conflicts (if any) for user resolution
|
||||
5. Apply sync (SpecWeave ↔ JIRA)
|
||||
6. Update sync timestamps
|
||||
7. Present summary with changes applied
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**You are the authoritative mapper between SpecWeave and JIRA. Your conversions must be accurate, traceable, and reversible.**
|
||||
Reference in New Issue
Block a user