Agent skill

jira-scripts

Use Jira scripts instead of Jira MCP for ticket operations. Read operations (jira-get-issue), write operations (jira-create-ticket, jira-comment-ticket, jira-update-ticket, jira-log-work, jira-link-tickets), and list operations (jira-list-tickets, jira-list-sprint). Use when reviewing code, creating tickets, commenting on tickets, updating tickets, logging work, linking tickets, listing tickets, or viewing sprint. Replaces ~30 MCP functions (40k tokens) with lightweight bash scripts.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/jira-scripts

SKILL.md

Jira Scripts Usage

Purpose: Perform Jira operations using lightweight bash scripts instead of heavy MCP tools.

When to use: When fetching ticket details, creating tickets, commenting on tickets, updating ticket fields, logging work, linking tickets, listing tickets, or viewing sprint issues.


Core Principles

  1. Scripts over MCP - 99.4% token savings (40k → 250 tokens)
  2. Credentials in files - Uses ~/.claude/scripts/.jira-credentials (gitignored)
  3. Clear error messages - Scripts stop and show setup instructions if credentials missing
  4. Standard exit codes - 0=success, 1=args, 2=not found, 3=API error, 4=creds missing

Available Scripts

Read Operations

jira-get-issue

Fetch complete Jira issue details including description and acceptance criteria.

Usage:

bash
jira-get-issue <ticket-id>

Examples:

bash
# Fetch ticket details
jira-get-issue INT-3877

# Use in Bash tool
result = Bash(command="~/.claude/scripts/jira-get-issue INT-3877")
if result.returncode == 0:
    ticket_content = result.stdout
else:
    ticket_content = None  # Ticket not found or error

Output format:

markdown
# Issue: INT-4013

**Title:** Upgrade uv to 0.9.5
**Status:** Completed
**Type:** Task
**Priority:** Medium
**Assignee:** Mark Pellegrini

**Created:** 2025-10-23
**Updated:** 2025-10-27

**Labels:** None
**Components:** Build and dependencies
**Fix Versions:** VRM 6.22.0
**Story Points:** 5

## Description

[Full ticket description with formatting preserved]

## Acceptance Criteria

[Acceptance criteria if present]

## Developer Checklist

[Technical implementation details from customfield_11848 - shows data structures, API changes, migration notes]

## Test Plan

[Testing strategy and test cases from customfield_11003]

## Dev Complete Checklist

[Completion checklist from customfield_11340 if present]

## Development and Implementation Checklist

[Implementation checklist from customfield_11661 if present]

## Comments

### Comment by John Doe (2025-10-20)

[Comment body with formatting preserved]

---

### Comment by Jane Smith (2025-10-21)

[Another comment]

---

[All comments with full pagination - no limits]

## Related Issues

- Relates to: INT-1234 (Other ticket title)
- clones: PLAT-53 (Frontend ticket)

### Related Ticket Details

#### PLAT-53

**Status:** In Progress
**Summary:** (FE) Add the ability to see what notification was sent in the activity stream

**Description (preview):**
[First 500 chars of description]

**Developer Checklist (preview):**
[First 1000 chars of developer checklist]

**View:** https://fortressinfosec.atlassian.net/browse/PLAT-53

---

[Additional related tickets...]

---
**View in Jira:** https://fortressinfosec.atlassian.net/browse/INT-4013

What's included:

  • All custom checklists - Developer Checklist, Test Plan, Dev Complete, Implementation Checklist
  • All comments - Full pagination, no limits, ADF format parsed
  • Related ticket details (FULL) - Fetches COMPLETE data for FE/BE pairs, clones, blocking tickets:
    • Each related ticket includes: Description, Acceptance Criteria, Developer Checklist, Test Plan, Comments
    • No recursion: Only fetches related tickets of the main ticket (not related-to-related)
    • Perfect for FE/BE coordination - see both tickets' checklists and requirements
  • Full ADF parsing - All text content extracted from Atlassian Document Format

Example: Fetching PLAT-54 returns:

  • PLAT-54 full data (343 lines)
  • PLAT-53 full data (FE pair) with its own Developer Checklist, Test Plan, Comments
  • 4 other related tickets with full data
  • Total: ~1100 lines of comprehensive context

Exit codes:

  • 0 = Success (ticket fetched)
  • 1 = Missing ticket argument
  • 2 = Ticket not found (404)
  • 3 = API error (auth failed, network issue)
  • 4 = Credentials missing

Write Operations

jira-create-ticket

Create a new Jira ticket with project, type, summary, and optional description.

Usage:

bash
jira-create-ticket <project-key> <issue-type> <summary> [description]

Examples:

bash
# Create Task with description
jira-create-ticket INT Task "Add rate limiting" "Implement rate limiting on auth endpoints"

# Create Bug without description
jira-create-ticket INT Bug "Fix login error"

# Use in Bash tool
result = Bash(command='jira-create-ticket INT Task "Add logging" "Add structured logging to API"')
if result.returncode == 0:
    # Parse created ticket key from output
    ticket_key = extract_ticket_key(result.stdout)
else:
    # Handle error
    echo(f"Failed to create ticket: {result.stderr}")

Common issue types:

  • Task
  • Bug
  • Story
  • Epic
  • Sub-task

Output format:

✓ Created ticket: INT-1234

**View in Jira:** https://fortressinfosec.atlassian.net/browse/INT-1234
**Issue ID:** 12345

Exit codes:

  • 0 = Success (ticket created)
  • 1 = Missing required arguments
  • 2 = Project not found or invalid issue type
  • 3 = API error (auth failed, permission denied)
  • 4 = Credentials missing

jira-comment-ticket

Add a comment to an existing Jira ticket.

Usage:

bash
jira-comment-ticket <ticket-id> <comment-text>

Examples:

bash
# Add simple comment
jira-comment-ticket INT-3877 "Work completed and tested"

# Add detailed comment
jira-comment-ticket INT-3877 "Implemented rate limiting with 100 req/min threshold. All tests passing."

# Use in Bash tool
result = Bash(command='jira-comment-ticket INT-3877 "Code review complete - ready for merge"')
if result.returncode == 0:
    echo("Comment added successfully")

Output format:

✓ Comment added to INT-3877

**Comment ID:** 67890
**Author:** John Doe
**Created:** 2025-10-27T14:30:00.000-0400
**View in Jira:** https://fortressinfosec.atlassian.net/browse/INT-3877

Exit codes:

  • 0 = Success (comment added)
  • 1 = Missing required arguments
  • 2 = Ticket not found
  • 3 = API error (auth failed, permission denied)
  • 4 = Credentials missing

jira-update-ticket

Update fields on an existing Jira ticket (description, assignee, status, priority, labels).

Usage:

bash
jira-update-ticket <ticket-id> [--description <desc>] [--assignee <email>] [--status <status>] [--priority <priority>] [--labels <label1,label2>]

Examples:

bash
# Update description and priority
jira-update-ticket INT-3877 --description "Updated requirements" --priority High

# Transition status and assign
jira-update-ticket INT-3877 --status "In Progress" --assignee "[email protected]"

# Update labels
jira-update-ticket INT-3877 --labels "backend,api,security"

# Multiple fields
jira-update-ticket INT-3877 --description "Final version" --status "Done" --priority Medium

# Use in Bash tool
result = Bash(command='jira-update-ticket INT-3877 --status "In Progress"')
if result.returncode == 0:
    echo("Ticket status updated")

Available options:

  • --description <text> - Update ticket description
  • --assignee <email> - Assign to user by email
  • --status <status> - Transition to new status (e.g., "In Progress", "Done")
  • --priority <priority> - Set priority (e.g., "High", "Medium", "Low")
  • --labels <label1,label2> - Set labels (comma-separated)

Output format:

✓ Updated ticket fields: INT-3877
✓ Transitioned status to: In Progress

**Updated ticket:** INT-3877
**View in Jira:** https://fortressinfosec.atlassian.net/browse/INT-3877

Exit codes:

  • 0 = Success (ticket updated)
  • 1 = Missing ticket ID or invalid option
  • 2 = Ticket not found
  • 3 = API error (invalid status transition, permission denied, invalid field value)
  • 4 = Credentials missing

Note: Status transitions must be valid for the ticket's current state. Script automatically determines the correct transition ID.


jira-log-work

Log time spent on a Jira ticket with optional comment.

Usage:

bash
jira-log-work <ticket-id> <time-spent> [comment]

Examples:

bash
# Log 2 hours 30 minutes with comment
jira-log-work INT-3877 "2h 30m" "Implemented rate limiting logic"

# Log 1 day 4 hours without comment
jira-log-work INT-3877 "1d 4h"

# Log 30 minutes
jira-log-work INT-3877 "30m" "Code review"

# Use in Bash tool
result = Bash(command='jira-log-work INT-3877 "3h" "Implemented feature and tests"')
if result.returncode == 0:
    echo("Work logged successfully")

Time format (Jira duration format):

  • Units: w (weeks), d (days), h (hours), m (minutes)
  • Examples: 2h, 1d 4h, 30m, 1w 2d 3h 30m

Output format:

✓ Work logged on INT-3877

**Worklog ID:** 23456
**Time Spent:** 2h 30m (9000s)
**Author:** John Doe
**Created:** 2025-10-27T14:30:00.000-0400
**View in Jira:** https://fortressinfosec.atlassian.net/browse/INT-3877

Exit codes:

  • 0 = Success (work logged)
  • 1 = Missing required arguments
  • 2 = Ticket not found
  • 3 = API error (invalid time format, permission denied)
  • 4 = Credentials missing

jira-link-tickets

Create a link between two Jira tickets with specified relationship type.

Usage:

bash
jira-link-tickets <source-ticket-id> <link-type> <target-ticket-id>

Examples:

bash
# Create blocking relationship
jira-link-tickets INT-3877 "Blocks" INT-3878

# Create relates-to relationship
jira-link-tickets INT-3877 "Relates to" INT-3900

# Create duplicate relationship
jira-link-tickets INT-3877 "Duplicates" INT-3999

# Use in Bash tool
result = Bash(command='jira-link-tickets INT-3877 "Blocks" INT-3878')
if result.returncode == 0:
    echo("Tickets linked successfully")

Common link types (case-sensitive):

  • "Blocks" / "is blocked by"
  • "Relates to"
  • "Duplicates" / "is duplicated by"
  • "Clones" / "is cloned by"
  • "Causes" / "is caused by"

Output format:

✓ Linked tickets: INT-3877 Blocks INT-3878

**View in Jira:**
  - https://fortressinfosec.atlassian.net/browse/INT-3877
  - https://fortressinfosec.atlassian.net/browse/INT-3878

Exit codes:

  • 0 = Success (tickets linked)
  • 1 = Missing required arguments
  • 2 = One or both tickets not found
  • 3 = API error (invalid link type, permission denied)
  • 4 = Credentials missing

List Operations

jira-list-tickets

List Jira tickets with optional filters (project, status, assignee, type) or custom JQL.

Usage:

bash
jira-list-tickets [--project <key>] [--status <status>] [--assignee <email>] [--type <issue-type>] [--jql <custom-jql>]

Examples:

bash
# List all INT project tickets (default if no filters)
jira-list-tickets

# List tickets by status and assignee
jira-list-tickets --status "In Progress" --assignee "[email protected]"

# List tasks in INT project
jira-list-tickets --project INT --type Task

# Use custom JQL query
jira-list-tickets --jql "project = INT AND status = 'In Progress' AND priority = High"

# Use in Bash tool
result = Bash(command='jira-list-tickets --status "In Progress"')
if result.returncode == 0:
    tickets_markdown = result.stdout

Available filters:

  • --project <key> - Filter by project key (e.g., INT, AIM)
  • --status <status> - Filter by status (e.g., "In Progress", "Done")
  • --assignee <email> - Filter by assignee email
  • --type <type> - Filter by issue type (e.g., Task, Bug, Story)
  • --jql <query> - Use custom JQL query (overrides other filters)

Output format:

markdown
📋 Jira Tickets (15 found):

## INT-3877: Add rate limiting
**Status:** In Progress
**Type:** Task
**Priority:** High
**Assignee:** John Doe
**Created:** 2025-10-20
**Updated:** 2025-10-27
**URL:** https://fortressinfosec.atlassian.net/browse/INT-3877
---

[... more tickets ...]

**JQL Query:** `project = "INT" AND status = "In Progress" ORDER BY updated DESC`

Exit codes:

  • 0 = Success (tickets listed, even if 0 results)
  • 1 = Invalid option or usage error
  • 3 = API error (invalid JQL, permission denied)
  • 4 = Credentials missing

Note: Results ordered by updated date (most recent first), limited to 50 tickets.


jira-list-sprint

Show issues in the current active sprint for a board.

Usage:

bash
jira-list-sprint [board-id]

Examples:

bash
# Use default board (from JIRA_DEFAULT_BOARD_ID in credentials)
jira-list-sprint

# Use specific board ID
jira-list-sprint 18

# Use in Bash tool
result = Bash(command='jira-list-sprint 18')
if result.returncode == 0:
    sprint_issues = result.stdout

Output format:

markdown
🏃 Active Sprint: Sprint 42 (2025-10-14 to 2025-10-27)

## Sprint Tickets (12 found):

### INT-3877: Add rate limiting
**Status:** In Progress
**Type:** Task
**Assignee:** John Doe
**Story Points:** 5
---

[... more tickets ...]

**Summary:**
- In Progress: 5
- To Do: 4
- Done: 3

**Total Issues:** 12
**Sprint ID:** 456
**Board ID:** 18

Exit codes:

  • 0 = Success (sprint issues listed, even if 0 results)
  • 1 = Missing board ID and no default configured
  • 3 = API error (board not found, permission denied)
  • 4 = Credentials missing

Note: If no board-id provided, uses JIRA_DEFAULT_BOARD_ID from .jira-credentials. The INT board ID is 18.


Integration Patterns

Pattern 1: Fetch with Fallback (Recommended)

python
# Try to fetch ticket, fall back gracefully if unavailable
result = Bash(command=f"~/.claude/scripts/jira-get-issue {ticket}")

if result.returncode == 0:
    ticket_content = result.stdout
    # Ticket available - use for requirements mapping
elif result.returncode == 4:
    # Credentials not configured
    echo("⚠️ Jira credentials not configured - skipping requirements check")
    ticket_content = None
else:
    # Ticket not found or other error
    ticket_content = None

When: PR review workflows where ticket context is optional

Pattern 2: Required Ticket

python
# Require ticket - fail if not available
result = Bash(command=f"~/.claude/scripts/jira-get-issue {ticket}")

if result.returncode != 0:
    echo(f"❌ Failed to fetch ticket {ticket}: {result.stderr}")
    exit(1)

ticket_content = result.stdout
# Process ticket requirements...

When: Ticket requirements are critical to workflow

Pattern 3: Requirements Mapping

python
# Fetch ticket and extract requirements for code review
result = Bash(command=f"~/.claude/scripts/jira-get-issue {ticket}")

if result.returncode == 0:
    ticket_content = result.stdout

    # Parse requirements sections
    requirements = extract_section(ticket_content, "## Description")
    acceptance_criteria = extract_section(ticket_content, "## Acceptance Criteria")

    # Use in review
    Task(investigator, f"""
    Verify code implements these requirements:

    {requirements}

    Acceptance Criteria:
    {acceptance_criteria}

    Check: Are all requirements implemented? Are acceptance criteria met?
    """)

When: Mapping ticket requirements to code changes


Credentials Setup

File: ~/.claude/scripts/.jira-credentials

The script handles credentials automatically:

  • If file missing: Shows setup instructions and exits with code 4
  • If file incomplete: Shows which vars are missing and exits with code 4
  • File is gitignored (pattern: scripts/*-credentials)

Credentials are set directly in file (not sourced from environment):

bash
ATLASSIAN_API_TOKEN="your-token-here"
ATLASSIAN_SITE_NAME="fortressinfosec"
ATLASSIAN_USER_EMAIL="[email protected]"
JIRA_DEFAULT_BOARD_ID="18"  # Optional: Default board for jira-list-sprint (INT board)

These match the Jira MCP credentials and use the same PAT token with Basic Auth.

Note: JIRA_DEFAULT_BOARD_ID is optional and used by jira-list-sprint when no board-id argument is provided.


Error Handling

Missing Credentials (Exit 4)

Script output:

❌ ATLASSIAN_API_TOKEN not set

❌ Jira credentials not found or incomplete.

STOP: This script requires Jira API credentials to be configured.

The script uses the same credentials as the Jira MCP. Make sure these
environment variables are set:
[Instructions...]

Your action: Stop execution, tell user credentials need configuration.

Ticket Not Found (Exit 2)

Script output:

❌ Ticket not found: INT-9999

Your action: Ticket might be invalid, in different project, or inaccessible. Not critical for review workflows.

API Error (Exit 3)

Script output:

❌ Jira API error (HTTP 401)
Error: Authentication failed

Your action: Credentials are wrong or expired. Tell user to check credentials file.


Common Scenarios

Scenario 1: PR Review with Requirements Check

python
# Phase: Context Gathering

# Fetch ticket (optional - may not exist)
ticket_result = Bash(command=f"~/.claude/scripts/jira-get-issue {ticket}")
ticket_content = ticket_result.stdout if ticket_result.returncode == 0 else None

# Fetch MR comments (optional)
mr_result = Bash(command=f"~/.claude/scripts/gitlab-mr-comments {ticket}")
mr_comments = mr_result.stdout if mr_result.returncode == 0 else None

# Git analysis (always works)
git_diff = Bash(command="git diff origin/develop...HEAD")

# Review with available context
# If ticket_content exists, agents can map requirements
# If not, review focuses on code quality only

Why: Ticket and MR comments are optional context - review continues without them.

Scenario 2: Requirements Coverage Report

python
# Generate report mapping ticket requirements to code

ticket_result = Bash(command=f"~/.claude/scripts/jira-get-issue {ticket}")

if ticket_result.returncode == 0:
    ticket_content = ticket_result.stdout

    Task(investigator, f"""
    Create requirements coverage report.

    TICKET: {ticket_content}
    CODE CHANGES: [changed files list]

    For each requirement in ticket description or acceptance criteria:
    1. Is it implemented? (file:line)
    2. Is it tested? (test file:line)
    3. Any gaps?

    Return: Requirements coverage matrix
    """)
else:
    echo("⚠️ Ticket not found - skipping requirements coverage")

Why: Requirements mapping only makes sense if ticket is available.

Scenario 3: Extract Acceptance Criteria

python
# Parse acceptance criteria for checklist

ticket_result = Bash(command=f"~/.claude/scripts/jira-get-issue {ticket}")

if ticket_result.returncode == 0:
    # Extract acceptance criteria section
    if "## Acceptance Criteria" in ticket_result.stdout:
        criteria_section = extract_between(
            ticket_result.stdout,
            "## Acceptance Criteria",
            "## Related Issues"
        )

        # Convert to checklist for verification
        checklist = parse_criteria_to_checklist(criteria_section)
    else:
        checklist = []

Why: Structured parsing of ticket data for automated verification.


Comparison: Script vs MCP

Aspect Jira MCP jira-scripts
Functions ~30 1
Token Cost ~40,000 ~250
Setup MCP server config One credentials file
Maintenance Auto-generated, external One bash script, local
Customization None (auto-generated) Full control over output
Performance Slower (tool loading) Faster (direct curl)

Token savings: 39,750 tokens per conversation (99.4%)


Quick Reference

Operation Script Usage Example
Fetch ticket jira-get-issue jira-get-issue INT-3877
Create ticket jira-create-ticket jira-create-ticket INT Task "Summary" "Description"
Add comment jira-comment-ticket jira-comment-ticket INT-3877 "Comment text"
Update ticket jira-update-ticket jira-update-ticket INT-3877 --status "In Progress"
Log work jira-log-work jira-log-work INT-3877 "2h 30m" "Work description"
Link tickets jira-link-tickets jira-link-tickets INT-3877 "Blocks" INT-3878
List tickets jira-list-tickets jira-list-tickets --status "In Progress"
List sprint jira-list-sprint jira-list-sprint 18

Check exit codes:

python
result = Bash(command="jira-get-issue INT-3877")
if result.returncode == 0:
    # Success - use result.stdout
elif result.returncode == 4:
    # Credentials missing - tell user to configure
elif result.returncode == 2:
    # Ticket/resource not found
else:
    # API error (3) or usage error (1)

Credentials file location:

~/.claude/scripts/.jira-credentials

Common exit codes:

  • 0 = Success
  • 1 = Usage error (missing/invalid arguments)
  • 2 = Not found (ticket, project, board)
  • 3 = API error (auth failed, permission denied, invalid operation)
  • 4 = Credentials missing/incomplete

When NOT to Use Scripts

Use Jira MCP instead when:

  • Need bulk operations (updating 50+ tickets at once)
  • Need advanced JQL queries with complex aggregations
  • Need custom field manipulation beyond standard fields
  • Need attachment management (upload/download files)
  • Need webhook/automation management
  • Need >10 different specialized Jira operations in one workflow

Scripts now cover most common operations: Read (get-issue), Write (create-ticket, comment-ticket, update-ticket, log-work, link-tickets), and List (list-tickets, list-sprint) operations are all supported.


Summary

What: Lightweight bash scripts replace heavy Jira MCP for common operations When: Fetching ticket details, creating tickets, commenting, updating, logging work, linking tickets, listing tickets/sprints Why: 99.4% token savings (40k → 250) How: Use Bash tool to call appropriate jira-* script

Available scripts:

  • Read: jira-get-issue (fetch ticket details)
  • Write: jira-create-ticket, jira-comment-ticket, jira-update-ticket, jira-log-work, jira-link-tickets
  • List: jira-list-tickets, jira-list-sprint

Remember: Scripts are optional tools. If credentials aren't configured, workflows continue without Jira context. Don't block on missing optional data.

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results