jira-workflow

Orchestrate Jira workflows end-to-end. Use when building stories with approvals, transitioning items through lifecycle states, or syncing task completion with Jira.

25 stars

Best use case

jira-workflow is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Orchestrate Jira workflows end-to-end. Use when building stories with approvals, transitioning items through lifecycle states, or syncing task completion with Jira.

Teams using jira-workflow should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/jira-workflow/SKILL.md --create-dirs "https://raw.githubusercontent.com/ComeOnOliver/skillshub/main/skills/aiskillstore/marketplace/01000001-01001110/jira-safe/jira-workflow/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/jira-workflow/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How jira-workflow Compares

Feature / Agentjira-workflowStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Orchestrate Jira workflows end-to-end. Use when building stories with approvals, transitioning items through lifecycle states, or syncing task completion with Jira.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Jira Workflow Orchestration Skill

> Complete workflow management for Jira: building stories (SAFe), getting approvals, and transitioning items through the development lifecycle (To Do → Progressing → Done).

**IMPORTANT**: This project uses Next-Gen (Team-managed) Jira with custom workflow states. The actual states are:
- `To Do` (backlog)
- `In Review`
- `Progressing` (active work)
- `Out Review`
- `Done`

Always query available transitions first: `GET /rest/api/3/issue/{key}/transitions`

## When to Use

- Creating new user stories, epics, or tasks for the project
- Getting user approval before creating Jira items
- Moving stories through workflow states as work progresses
- Syncing Claude Code task completion with Jira status
- Managing sprint planning and backlog refinement
- Tracking development progress in real-time

## Prerequisites

**Environment Variables:**
```bash
JIRA_EMAIL=your.email@domain.com
JIRA_API_TOKEN=your_api_token
JIRA_BASE_URL=https://your-org.atlassian.net
JIRA_PROJECT_KEY=SCRUM
JIRA_BOARD_ID=1
```

**Project Configuration:**
- Must know if project is Next-Gen (Team-managed) or Classic (Company-managed)
- Next-Gen: Use `parent` field for Epic links
- Classic: Use `customfield_10014` for Epic links

---

## Core Workflow Pattern

### The Approval-Create-Track Loop

```
1. PLAN: Analyze task requirements
   ↓
2. PROPOSE: Present story to user for approval
   ↓
3. APPROVE: User confirms or modifies
   ↓
4. CREATE: Issue created in Jira backlog
   ↓
5. START: Transition to "Progressing" when work begins
   ↓
6. COMPLETE: Transition to "Done" when work verified
   ↓
7. SYNC: Update Jira with implementation details
```

---

## Phase 1: Story Building (SAFe Format)

### Building a Story Proposal

When user requests work, build a SAFe-compliant story proposal:

```javascript
function buildStoryProposal(task) {
  return {
    summary: `As a ${task.persona}, I want ${task.goal}, so that ${task.benefit}`,
    description: {
      userStory: `As a **${task.persona}**, I want **${task.goal}**, so that **${task.benefit}**.`,
      acceptanceCriteria: task.scenarios.map(s => ({
        name: s.name,
        given: s.given,
        when: s.when,
        then: s.then
      })),
      definitionOfDone: [
        'Code reviewed and approved',
        'Unit tests written and passing',
        'Integration tests passing',
        'Documentation updated',
        'Deployed to staging',
        'Validated in production'
      ],
      technicalNotes: task.technicalNotes || []
    },
    category: task.category, // authentication, ui, api, database, etc.
    estimatedComplexity: task.complexity || 'medium', // small, medium, large
    subtasks: task.subtasks || []
  };
}
```

### Presenting for Approval

**CRITICAL: Always get user approval before creating Jira items.**

Use this prompt pattern:

```markdown
## Proposed Jira Story

**Summary:** As a [persona], I want [goal], so that [benefit]

**Category:** [category]
**Complexity:** [small/medium/large]

### Acceptance Criteria

**Scenario 1: [Name]**
- **GIVEN** [precondition]
- **WHEN** [action]
- **THEN** [expected result]

### Subtasks (if any)
1. [Subtask 1]
2. [Subtask 2]
3. [Subtask 3]

---

**Do you want me to create this in Jira?**

Options:
1. **Yes, create as-is** - I'll create the story now
2. **Modify** - Tell me what to change
3. **Skip** - Don't create in Jira, just do the work
```

---

## Phase 2: Issue Creation

### Create Story in Jira

```javascript
const JIRA_EMAIL = process.env.JIRA_EMAIL;
const JIRA_API_TOKEN = process.env.JIRA_API_TOKEN;
const JIRA_BASE_URL = process.env.JIRA_BASE_URL;
const PROJECT_KEY = process.env.JIRA_PROJECT_KEY;

const auth = Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString('base64');
const headers = {
  'Authorization': `Basic ${auth}`,
  'Content-Type': 'application/json',
  'Accept': 'application/json'
};

async function createStory(proposal, epicKey = null) {
  const body = {
    fields: {
      project: { key: PROJECT_KEY },
      issuetype: { name: 'Story' },
      summary: proposal.summary,
      description: buildADF(proposal.description),
      labels: [proposal.category.toLowerCase().replace(/\s+/g, '-')]
    }
  };

  // Link to Epic (Next-Gen project)
  if (epicKey) {
    body.fields.parent = { key: epicKey };
  }

  const response = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue`, {
    method: 'POST',
    headers,
    body: JSON.stringify(body)
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Failed to create story: ${error}`);
  }

  const issue = await response.json();
  console.log(`Created: ${issue.key} - ${proposal.summary}`);

  // Create subtasks if any
  if (proposal.subtasks?.length > 0) {
    for (const subtask of proposal.subtasks) {
      await createSubtask(issue.key, subtask);
      await delay(100); // Rate limiting
    }
  }

  return issue;
}

async function createSubtask(parentKey, summary) {
  const body = {
    fields: {
      project: { key: PROJECT_KEY },
      issuetype: { name: 'Subtask' }, // Note: 'Subtask' for Next-Gen
      parent: { key: parentKey },
      summary: summary
    }
  };

  const response = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue`, {
    method: 'POST',
    headers,
    body: JSON.stringify(body)
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Failed to create subtask: ${error}`);
  }

  return response.json();
}

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
```

### Build Atlassian Document Format (ADF)

```javascript
function buildADF(content) {
  const sections = [];

  // User Story Section
  sections.push({
    type: 'heading',
    attrs: { level: 2 },
    content: [{ type: 'text', text: 'User Story' }]
  });
  sections.push({
    type: 'paragraph',
    content: [{ type: 'text', text: content.userStory }]
  });

  // Acceptance Criteria Section
  sections.push({
    type: 'heading',
    attrs: { level: 2 },
    content: [{ type: 'text', text: 'Acceptance Criteria' }]
  });

  for (const scenario of content.acceptanceCriteria) {
    sections.push({
      type: 'heading',
      attrs: { level: 3 },
      content: [{ type: 'text', text: `Scenario: ${scenario.name}` }]
    });
    sections.push({
      type: 'bulletList',
      content: [
        { type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: `GIVEN ${scenario.given}`, marks: [{ type: 'strong' }] }] }] },
        { type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: `WHEN ${scenario.when}`, marks: [{ type: 'strong' }] }] }] },
        { type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: `THEN ${scenario.then}`, marks: [{ type: 'strong' }] }] }] }
      ]
    });
  }

  // Definition of Done Section
  sections.push({
    type: 'heading',
    attrs: { level: 2 },
    content: [{ type: 'text', text: 'Definition of Done' }]
  });
  sections.push({
    type: 'bulletList',
    content: content.definitionOfDone.map(item => ({
      type: 'listItem',
      content: [{ type: 'paragraph', content: [{ type: 'text', text: `[ ] ${item}` }] }]
    }))
  });

  // Technical Notes (if any)
  if (content.technicalNotes?.length > 0) {
    sections.push({
      type: 'heading',
      attrs: { level: 2 },
      content: [{ type: 'text', text: 'Technical Notes' }]
    });
    sections.push({
      type: 'bulletList',
      content: content.technicalNotes.map(note => ({
        type: 'listItem',
        content: [{ type: 'paragraph', content: [{ type: 'text', text: note }] }]
      }))
    });
  }

  return { type: 'doc', version: 1, content: sections };
}
```

---

## Phase 3: Workflow Transitions

### Get Available Transitions

```javascript
async function getTransitions(issueKey) {
  const response = await fetch(
    `${JIRA_BASE_URL}/rest/api/3/issue/${issueKey}/transitions`,
    { headers }
  );

  if (!response.ok) {
    throw new Error(`Failed to get transitions: ${response.status}`);
  }

  const data = await response.json();
  return data.transitions;
}
```

### Transition Issue to State

```javascript
async function transitionTo(issueKey, targetState) {
  // Get available transitions
  const transitions = await getTransitions(issueKey);

  // Find the transition to target state
  const transition = transitions.find(t =>
    t.to.name.toLowerCase() === targetState.toLowerCase() ||
    t.name.toLowerCase() === targetState.toLowerCase()
  );

  if (!transition) {
    console.log(`Available transitions for ${issueKey}:`);
    transitions.forEach(t => console.log(`  - ${t.name} → ${t.to.name}`));
    throw new Error(`No transition to "${targetState}" found`);
  }

  // Execute the transition
  const response = await fetch(
    `${JIRA_BASE_URL}/rest/api/3/issue/${issueKey}/transitions`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify({ transition: { id: transition.id } })
    }
  );

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Failed to transition: ${error}`);
  }

  console.log(`${issueKey} transitioned to ${targetState}`);
  return true;
}
```

### Common Workflow Operations

```javascript
// Start work on a story (To Do → Progressing)
async function startWork(issueKey) {
  await transitionTo(issueKey, 'Progressing');
  console.log(`Started: ${issueKey}`);
}

// Complete a story (Progressing → Done)
async function completeWork(issueKey) {
  await transitionTo(issueKey, 'Done');
  console.log(`Completed: ${issueKey}`);
}

// Move back to backlog (any state → To Do)
async function moveToBacklog(issueKey) {
  await transitionTo(issueKey, 'To Do');
  console.log(`Moved to backlog: ${issueKey}`);
}

// Reopen a completed issue (Done → To Do)
async function reopenWork(issueKey) {
  await transitionTo(issueKey, 'To Do');
  console.log(`Reopened: ${issueKey}`);
}
```

---

## Phase 4: Add Comments and Updates

### Add Work Log Comment

```javascript
async function addComment(issueKey, comment) {
  const body = {
    body: {
      type: 'doc',
      version: 1,
      content: [
        {
          type: 'paragraph',
          content: [{ type: 'text', text: comment }]
        }
      ]
    }
  };

  const response = await fetch(
    `${JIRA_BASE_URL}/rest/api/3/issue/${issueKey}/comment`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify(body)
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to add comment: ${response.status}`);
  }

  console.log(`Comment added to ${issueKey}`);
  return response.json();
}
```

### Add Implementation Details Comment

```javascript
async function addImplementationDetails(issueKey, details) {
  const content = [
    { type: 'heading', attrs: { level: 3 }, content: [{ type: 'text', text: 'Implementation Details' }] },
    { type: 'paragraph', content: [{ type: 'text', text: `Completed: ${new Date().toISOString()}` }] }
  ];

  if (details.files?.length > 0) {
    content.push(
      { type: 'heading', attrs: { level: 4 }, content: [{ type: 'text', text: 'Files Modified' }] },
      {
        type: 'bulletList',
        content: details.files.map(f => ({
          type: 'listItem',
          content: [{ type: 'paragraph', content: [{ type: 'text', text: f }] }]
        }))
      }
    );
  }

  if (details.commits?.length > 0) {
    content.push(
      { type: 'heading', attrs: { level: 4 }, content: [{ type: 'text', text: 'Commits' }] },
      {
        type: 'bulletList',
        content: details.commits.map(c => ({
          type: 'listItem',
          content: [{ type: 'paragraph', content: [{ type: 'text', text: c }] }]
        }))
      }
    );
  }

  if (details.notes) {
    content.push(
      { type: 'heading', attrs: { level: 4 }, content: [{ type: 'text', text: 'Notes' }] },
      { type: 'paragraph', content: [{ type: 'text', text: details.notes }] }
    );
  }

  const body = { body: { type: 'doc', version: 1, content } };

  const response = await fetch(
    `${JIRA_BASE_URL}/rest/api/3/issue/${issueKey}/comment`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify(body)
    }
  );

  return response.json();
}
```

---

## Complete Workflow Example

### Full Cycle: Propose → Approve → Create → Work → Complete

```javascript
async function fullWorkflowCycle(task) {
  // 1. Build proposal
  const proposal = buildStoryProposal(task);

  // 2. Present for approval (use AskUserQuestion tool)
  const approved = await presentForApproval(proposal);

  if (!approved) {
    console.log('Story creation skipped by user');
    return null;
  }

  // 3. Create in Jira
  const issue = await createStory(proposal, task.epicKey);
  console.log(`Created: ${issue.key}`);

  // 4. Start work (transition to In Progress)
  await startWork(issue.key);

  // 5. Do the actual work (your implementation here)
  const result = await doTheWork(task);

  // 6. Add implementation details
  await addImplementationDetails(issue.key, {
    files: result.modifiedFiles,
    commits: result.commits,
    notes: result.notes
  });

  // 7. Complete the work
  await completeWork(issue.key);

  return issue;
}
```

---

## Integration with Claude Code Orchestration

### Sync with TodoWrite

When working on Jira stories, sync with TodoWrite:

```markdown
TodoWrite todos:
[
  { "content": "SCRUM-55: Create signup API", "status": "in_progress", "activeForm": "Working on SCRUM-55" },
  { "content": "SCRUM-56: Create login API", "status": "pending", "activeForm": "Waiting for SCRUM-55" },
  { "content": "SCRUM-57: Create logout API", "status": "pending", "activeForm": "Waiting for SCRUM-56" }
]

As each task completes:
1. Mark TodoWrite item as completed
2. Transition Jira issue to Done
3. Add implementation comment to Jira
4. Move to next task
```

### Auto-Transition Pattern

```javascript
// When starting a task
async function startTask(issueKey) {
  // 1. Transition Jira to Progressing
  await startWork(issueKey);

  // 2. Update TodoWrite (in Claude Code)
  // TodoWrite: Mark as in_progress

  return issueKey;
}

// When completing a task
async function completeTask(issueKey, details) {
  // 1. Add implementation comment
  await addImplementationDetails(issueKey, details);

  // 2. Transition Jira to Done
  await completeWork(issueKey);

  // 3. Update TodoWrite (in Claude Code)
  // TodoWrite: Mark as completed

  return issueKey;
}
```

---

## Quick Reference

### Status Transitions (SCRUM Project - Next-Gen)

| From | To | Transition Name | Typical Use |
|------|-----|-----------------|-------------|
| To Do | Progressing | "Progressing" | Starting work |
| To Do | In Review | "In Review" | Needs review first |
| Progressing | Done | "Done" | Work complete |
| Progressing | To Do | "To Do" | Blocked/deprioritized |
| Done | To Do | "To Do" | Reopening |

**Available States:** To Do, In Review, Progressing, Out Review, Done

**Note:** Always query transitions first - they vary by issue type and current state.

### API Endpoints

| Action | Method | Endpoint |
|--------|--------|----------|
| Create Issue | POST | `/rest/api/3/issue` |
| Get Issue | GET | `/rest/api/3/issue/{key}` |
| Update Issue | PUT | `/rest/api/3/issue/{key}` |
| Delete Issue | DELETE | `/rest/api/3/issue/{key}` |
| Get Transitions | GET | `/rest/api/3/issue/{key}/transitions` |
| Do Transition | POST | `/rest/api/3/issue/{key}/transitions` |
| Add Comment | POST | `/rest/api/3/issue/{key}/comment` |
| Search | GET | `/rest/api/3/search/jql?jql=...` |

### Rate Limiting

- Max 10 requests/second
- Add 100ms delay between bulk operations
- Batch operations where possible

---

## Error Handling

```javascript
async function safeJiraOperation(operation, issueKey) {
  try {
    return await operation();
  } catch (error) {
    console.error(`Jira operation failed for ${issueKey}: ${error.message}`);

    // Common error patterns
    if (error.message.includes('404')) {
      console.log('Issue not found - may have been deleted');
    }
    if (error.message.includes('401')) {
      console.log('Authentication failed - check API token');
    }
    if (error.message.includes('403')) {
      console.log('Permission denied - check project access');
    }
    if (error.message.includes('400')) {
      console.log('Bad request - check field names and values');
    }

    throw error;
  }
}
```

---

## Executable Scripts

Ready-to-run scripts are available in both Node.js and Python:

### Using the Cross-Platform Runner

```bash
# From the .claude/skills/jira directory
node scripts/run.js workflow demo SCRUM-100  # Demo full workflow
node scripts/run.js test                      # Test authentication

# Force specific runtime
node scripts/run.js --python workflow demo SCRUM-100
node scripts/run.js --node workflow demo SCRUM-100
```

### Direct Script Execution

```bash
# Node.js
node scripts/jira-workflow-demo.mjs demo SCRUM-100
node scripts/jira-workflow-demo.mjs start SCRUM-100
node scripts/jira-workflow-demo.mjs complete SCRUM-100
node scripts/jira-workflow-demo.mjs reopen SCRUM-100
node scripts/jira-workflow-demo.mjs status SCRUM-100

# Python (recommended on Windows)
python scripts/jira-workflow-demo.py demo SCRUM-100
python scripts/jira-workflow-demo.py start SCRUM-100
python scripts/jira-workflow-demo.py complete SCRUM-100
python scripts/jira-workflow-demo.py reopen SCRUM-100
python scripts/jira-workflow-demo.py status SCRUM-100
```

### Available Scripts

| Script | Node.js | Python | Purpose |
|--------|---------|--------|---------|
| Workflow Demo | `jira-workflow-demo.mjs` | `jira-workflow-demo.py` | Full To Do → Progressing → Done demo |
| Add Subtasks | `jira-add-subtasks.mjs` | `jira-add-subtasks.py` | Create subtasks under a story |
| Create Story | `jira-create-one.mjs` | `jira-create-one.py` | Create single story |
| Bulk Create | `jira-bulk-create.mjs` | `jira-bulk-create.py` | Create from git commits |

---

## References

- [Jira REST API v3](https://developer.atlassian.com/cloud/jira/platform/rest/v3/)
- [Atlassian Document Format](https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/)
- [SAFe Framework](https://scaledagileframework.com/)
- [SAFe Story Format](https://scaledagileframework.com/story/)

Related Skills

coreweave-core-workflow-b

25
from ComeOnOliver/skillshub

Run distributed GPU training jobs on CoreWeave with multi-node PyTorch. Use when training models across multiple GPUs, setting up distributed training, or running fine-tuning jobs on CoreWeave H100 clusters. Trigger with phrases like "coreweave training", "coreweave multi-gpu", "distributed training coreweave", "fine-tune on coreweave".

coreweave-core-workflow-a

25
from ComeOnOliver/skillshub

Deploy KServe InferenceService on CoreWeave with autoscaling and GPU scheduling. Use when serving ML models with KServe, configuring scale-to-zero, or deploying production inference endpoints on CoreWeave. Trigger with phrases like "coreweave inference service", "coreweave kserve", "coreweave model serving", "deploy model on coreweave".

cohere-core-workflow-b

25
from ComeOnOliver/skillshub

Build tool-use agents and function calling with Cohere API v2. Use when implementing multi-step agents, function calling, or building autonomous tool-using workflows with Cohere. Trigger with phrases like "cohere tool use", "cohere agents", "cohere function calling", "cohere multi-step".

cohere-core-workflow-a

25
from ComeOnOliver/skillshub

Build a complete RAG pipeline with Cohere Chat, Embed, and Rerank. Use when implementing retrieval-augmented generation, building grounded Q&A systems, or combining search with LLM generation. Trigger with phrases like "cohere RAG", "cohere retrieval", "cohere grounded generation", "cohere search and answer".

coderabbit-core-workflow-b

25
from ComeOnOliver/skillshub

Tune CodeRabbit review configuration: learnings, code guidelines, and noise reduction. Use when fine-tuning review quality, training CodeRabbit with team preferences, adding code guidelines, or reducing false positives. Trigger with phrases like "coderabbit tune reviews", "coderabbit learnings", "coderabbit guidelines", "reduce coderabbit noise", "coderabbit false positives".

coderabbit-core-workflow-a

25
from ComeOnOliver/skillshub

Execute CodeRabbit primary workflow: automated PR code review with configuration. Use when setting up automated code reviews on pull requests, configuring review behavior, or establishing the core CodeRabbit review loop. Trigger with phrases like "coderabbit review workflow", "coderabbit PR review", "coderabbit auto review", "configure coderabbit reviews".

clickup-core-workflow-b

25
from ComeOnOliver/skillshub

Manage ClickUp workspaces, spaces, folders, lists, and views via API v2. Use when creating project structures, organizing spaces and lists, or managing the ClickUp hierarchy programmatically. Trigger: "clickup space", "clickup folder", "clickup list", "clickup views", "create clickup space", "organize clickup workspace", "clickup hierarchy".

clickup-core-workflow-a

25
from ComeOnOliver/skillshub

Manage ClickUp tasks via API v2: create, read, update, delete tasks with assignees, priorities, due dates, subtasks, and statuses. Trigger: "clickup task", "create clickup task", "update task status", "manage clickup tasks", "clickup CRUD", "clickup task management".

clickhouse-core-workflow-b

25
from ComeOnOliver/skillshub

Insert, query, and aggregate data in ClickHouse with real SQL patterns. Use when writing analytical queries, inserting data at scale, building dashboards, or implementing materialized views. Trigger: "clickhouse query", "clickhouse insert", "clickhouse aggregate", "clickhouse materialized view", "clickhouse SQL".

clickhouse-core-workflow-a

25
from ComeOnOliver/skillshub

Design ClickHouse schemas with MergeTree engines, ORDER BY keys, and partitioning. Use when creating new tables, choosing engines, designing sort keys, or modeling data for analytical workloads. Trigger: "clickhouse schema design", "clickhouse table design", "clickhouse ORDER BY", "clickhouse partitioning", "MergeTree table".

clerk-core-workflow-b

25
from ComeOnOliver/skillshub

Implement session management and middleware with Clerk. Use when managing user sessions, configuring route protection, or implementing token refresh and custom JWT templates. Trigger with phrases like "clerk session", "clerk middleware", "clerk route protection", "clerk token", "clerk JWT".

clerk-core-workflow-a

25
from ComeOnOliver/skillshub

Implement user sign-up and sign-in flows with Clerk. Use when building authentication UI, customizing sign-in experience, or implementing OAuth social login. Trigger with phrases like "clerk sign-in", "clerk sign-up", "clerk login flow", "clerk OAuth", "clerk social login".