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 "[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:
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 "[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:
📋 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="[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
# 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.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?