Initial commit
This commit is contained in:
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.
|
||||
Reference in New Issue
Block a user