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.
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
- Scripts over MCP - 99.4% token savings (40k → 250 tokens)
- Credentials in files - Uses
~/.claude/scripts/.jira-credentials(gitignored) - Clear error messages - Scripts stop and show setup instructions if credentials missing
- 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:
jira-get-issue <ticket-id>
Examples:
# 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:
# 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:
jira-create-ticket <project-key> <issue-type> <summary> [description]
Examples:
# 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:
jira-comment-ticket <ticket-id> <comment-text>
Examples:
# 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:
jira-update-ticket <ticket-id> [--description <desc>] [--assignee <email>] [--status <status>] [--priority <priority>] [--labels <label1,label2>]
Examples:
# 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 "user@fortressinfosec.com"
# 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:
jira-log-work <ticket-id> <time-spent> [comment]
Examples:
# 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:
jira-link-tickets <source-ticket-id> <link-type> <target-ticket-id>
Examples:
# 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:
jira-list-tickets [--project <key>] [--status <status>] [--assignee <email>] [--type <issue-type>] [--jql <custom-jql>]
Examples:
# 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 "rmurphy@fortressinfosec.com"
# 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:
📋 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:
jira-list-sprint [board-id]
Examples:
# 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:
🏃 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)
# 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
# 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
# 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):
ATLASSIAN_API_TOKEN="your-token-here"
ATLASSIAN_SITE_NAME="fortressinfosec"
ATLASSIAN_USER_EMAIL="your.email@fortressinfosec.com"
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
# 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
# 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
# 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:
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.
Didn't find tool you were looking for?