From scrum-master
Manage Jira issues, sprints, and boards via the Jira REST API v3 and Agile API. Use this skill whenever the user wants to create, update, transition, search, or manage Jira issues — including epics, stories, tasks, bugs, and sub-tasks. Trigger on any mention of Jira issues, Jira URLs ({instance}/browse/PROJ-123), issue keys, JQL queries, sprints, boards, backlogs, or requests like "create a Jira ticket", "move this to In Progress", "search for bugs in PROJ", "add to sprint", "link these issues", or "bulk create stories".
How this skill is triggered — by the user, by Claude, or both
Slash command
/scrum-master:jiraThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are working with Jira Cloud via the **REST API v3** for issue management and the **Agile REST API 1.0** for board/sprint operations.
You are working with Jira Cloud via the REST API v3 for issue management and the Agile REST API 1.0 for board/sprint operations.
Credentials and instance URL are read from environment variables. Users must set these in their shell profile (see README for setup):
# Read credentials from environment (set in ~/.zshrc, ~/.bashrc, etc.)
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-yourcompany.atlassian.net}"
If the env vars are not available in the subprocess, fall back to reading from the shell profile:
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL:-$(grep 'export ATLASSIAN_EMAIL' ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN:-$(grep 'export ATLASSIAN_API_TOKEN' ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-$(grep 'export ATLASSIAN_INSTANCE' ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//' || echo 'yourcompany.atlassian.net')}"
https://${ATLASSIAN_INSTANCE}/rest/api/3https://${ATLASSIAN_INSTANCE}/rest/agile/1.0-u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN"When the user pastes a Jira URL, the issue key is the last path segment:
https://{instance}/browse/PROJ-123
^^^^^^^^
This is the issue key
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL:-$(grep 'export ATLASSIAN_EMAIL' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN:-$(grep 'export ATLASSIAN_API_TOKEN' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-$(grep 'export ATLASSIAN_INSTANCE' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//' || echo 'yourcompany.atlassian.net')}"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/project" \
| python3 -c "
import json, sys
projects = json.load(sys.stdin)
for p in projects:
print(f\"{p['key']}\t{p['name']}\t{p.get('projectTypeKey','')}\")
"
Before creating an issue, discover valid issue types for a project:
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/createmeta/$PROJECT_KEY/issuetypes" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for it in d.get('issueTypes', d.get('values', [])):
print(f\"{it['id']}\t{it['name']}\t{'subtask' if it.get('subtask') else 'standard'}\")
"
After selecting an issue type, discover required and available fields:
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/createmeta/$PROJECT_KEY/issuetypes/$ISSUE_TYPE_ID" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for f in d.get('fields', d.get('values', [])):
req = 'REQUIRED' if f.get('required') else 'optional'
print(f\"{f['fieldId']}\t{f['name']}\t{req}\t{f.get('schema',{}).get('type','')}\")
"
Use this to discover custom field IDs (e.g., customfield_10014 for Epic Link) before creating issues.
Supports: Epic, Story, Task, Bug, Sub-task.
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL:-$(grep 'export ATLASSIAN_EMAIL' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN:-$(grep 'export ATLASSIAN_API_TOKEN' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-$(grep 'export ATLASSIAN_INSTANCE' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//' || echo 'yourcompany.atlassian.net')}"
python3 -c "
import json
payload = {
'fields': {
'project': {'key': '<PROJECT_KEY>'},
'issuetype': {'name': '<Issue Type>'}, # Epic, Story, Task, Bug, Sub-task
'summary': '<Issue summary>',
'description': {
'type': 'doc',
'version': 1,
'content': [
{
'type': 'paragraph',
'content': [{'type': 'text', 'text': '<Description text>'}]
}
]
}
# Add optional/custom fields as needed:
# 'priority': {'name': 'High'},
# 'labels': ['backend', 'sprint-12'],
# 'assignee': {'accountId': '<account_id>'},
# 'parent': {'key': 'PROJ-100'}, # for Sub-task
# 'customfield_10014': 'PROJ-50', # Epic Link (field ID varies)
}
}
json.dump(payload, open('/tmp/jira_create.json', 'w'))
print('Payload written.')
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_create.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
if 'key' in d:
print(f\"Created: {d['key']}\")
print(f\"URL: https://$ATLASSIAN_INSTANCE/browse/{d['key']}\")
print(f\"ID: {d['id']}\")
else:
print('Error:', json.dumps(d, indent=2))
sys.exit(1)
"
rm -f /tmp/jira_create.json
ISSUE_KEY="PROJ-123"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
f = d['fields']
print(f\"Key: {d['key']}\")
print(f\"Summary: {f['summary']}\")
print(f\"Type: {f['issuetype']['name']}\")
print(f\"Status: {f['status']['name']}\")
print(f\"Priority: {f.get('priority',{}).get('name','None')}\")
print(f\"Assignee: {f.get('assignee',{}).get('displayName','Unassigned') if f.get('assignee') else 'Unassigned'}\")
print(f\"Reporter: {f.get('reporter',{}).get('displayName','Unknown')}\")
print(f\"Labels: {', '.join(f.get('labels',[])) or 'None'}\")
print(f\"Created: {f['created']}\")
print(f\"Updated: {f['updated']}\")
# Print description if present
if f.get('description'):
print(f\"Description: (ADF document - see raw JSON for full content)\")
"
python3 -c "
import json
payload = {
'fields': {
'summary': '<New summary>',
# 'priority': {'name': 'High'},
# 'labels': ['updated-label'],
# 'description': { ADF document },
}
}
json.dump(payload, open('/tmp/jira_update.json', 'w'))
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X PUT \
-H "Content-Type: application/json" \
-d @/tmp/jira_update.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY" \
-w "\nHTTP Status: %{http_code}\n"
rm -f /tmp/jira_update.json
A 204 No Content response means success.
First, get available transitions for the issue:
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY/transitions" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for t in d['transitions']:
print(f\"{t['id']}\t{t['name']}\t→ {t['to']['name']}\")
"
Then execute the transition:
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d "{\"transition\": {\"id\": \"$TRANSITION_ID\"}}" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY/transitions" \
-w "\nHTTP Status: %{http_code}\n"
A 204 No Content response means the transition was successful.
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL:-$(grep 'export ATLASSIAN_EMAIL' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN:-$(grep 'export ATLASSIAN_API_TOKEN' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-$(grep 'export ATLASSIAN_INSTANCE' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//' || echo 'yourcompany.atlassian.net')}"
python3 -c "
import json
payload = {
'jql': '<JQL query>',
'maxResults': 50,
'fields': ['summary', 'status', 'assignee', 'priority', 'issuetype', 'labels']
}
json.dump(payload, open('/tmp/jira_search.json', 'w'))
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_search.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/search" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f\"Total: {d['total']} results\")
for issue in d['issues']:
f = issue['fields']
assignee = f.get('assignee',{}).get('displayName','Unassigned') if f.get('assignee') else 'Unassigned'
print(f\"{issue['key']}\t{f['issuetype']['name']}\t{f['status']['name']}\t{f['summary']}\t{assignee}\")
"
rm -f /tmp/jira_search.json
Common JQL examples:
project = PROJ AND status = "In Progress" — all in-progress issuesproject = PROJ AND issuetype = Bug AND status != Done — open bugsassignee = currentUser() AND sprint in openSprints() — my sprint itemsproject = PROJ AND labels = "backend" ORDER BY priority DESC — by labelproject = PROJ AND created >= -7d — issues created in last 7 daysparent = PROJ-100 — sub-tasks of an epic/storypython3 -c "
import json
payload = {
'body': {
'type': 'doc',
'version': 1,
'content': [
{
'type': 'paragraph',
'content': [{'type': 'text', 'text': '<Comment text>'}]
}
]
}
}
json.dump(payload, open('/tmp/jira_comment.json', 'w'))
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_comment.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY/comment" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
if 'id' in d:
print(f\"Comment added (ID: {d['id']})\")
else:
print('Error:', json.dumps(d, indent=2))
"
rm -f /tmp/jira_comment.json
# First find the user's accountId
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/user/search?query=<email_or_name>" \
| python3 -c "
import json, sys
users = json.load(sys.stdin)
for u in users:
print(f\"{u['accountId']}\t{u['displayName']}\t{u.get('emailAddress','')}\")
"
# Then assign
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X PUT \
-H "Content-Type: application/json" \
-d "{\"accountId\": \"<accountId>\"}" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY/assignee" \
-w "\nHTTP Status: %{http_code}\n"
To unassign, use {"accountId": null}.
python3 -c "
import json
payload = {
'type': {'name': '<link_type>'},
'inwardIssue': {'key': '<PROJ-1>'},
'outwardIssue': {'key': '<PROJ-2>'}
}
json.dump(payload, open('/tmp/jira_link.json', 'w'))
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_link.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issueLink" \
-w "\nHTTP Status: %{http_code}\n"
rm -f /tmp/jira_link.json
Common link types: Blocks, is blocked by, Clones, Duplicate, Relates.
To discover available link types:
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issueLinkType" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for lt in d['issueLinkTypes']:
print(f\"{lt['name']}\t{lt['inward']}\t{lt['outward']}\")
"
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL:-$(grep 'export ATLASSIAN_EMAIL' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN:-$(grep 'export ATLASSIAN_API_TOKEN' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-$(grep 'export ATLASSIAN_INSTANCE' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//' || echo 'yourcompany.atlassian.net')}"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/agile/1.0/board" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for b in d.get('values', []):
print(f\"{b['id']}\t{b['name']}\t{b['type']}\")
"
To filter boards by project: append ?projectKeyOrId=PROJ to the URL.
List sprints for a board:
BOARD_ID="<board_id>"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/agile/1.0/board/$BOARD_ID/sprint" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for s in d.get('values', []):
print(f\"{s['id']}\t{s['name']}\t{s['state']}\t{s.get('startDate','N/A')}\t{s.get('endDate','N/A')}\")
"
Get sprint issues:
SPRINT_ID="<sprint_id>"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/agile/1.0/sprint/$SPRINT_ID/issue" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for issue in d.get('issues', []):
f = issue['fields']
assignee = f.get('assignee',{}).get('displayName','Unassigned') if f.get('assignee') else 'Unassigned'
print(f\"{issue['key']}\t{f['issuetype']['name']}\t{f['status']['name']}\t{f['summary']}\t{assignee}\")
"
Move issues to a sprint:
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d "{\"issues\": [\"PROJ-1\", \"PROJ-2\", \"PROJ-3\"]}" \
"https://$ATLASSIAN_INSTANCE/rest/agile/1.0/sprint/$SPRINT_ID/issue" \
-w "\nHTTP Status: %{http_code}\n"
BOARD_ID="<board_id>"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/agile/1.0/board/$BOARD_ID/backlog?maxResults=50" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for issue in d.get('issues', []):
f = issue['fields']
priority = f.get('priority',{}).get('name','None') if f.get('priority') else 'None'
print(f\"{issue['key']}\t{f['issuetype']['name']}\t{priority}\t{f['summary']}\")
"
python3 -c "
import json
payload = {
'issueUpdates': [
{
'fields': {
'project': {'key': '<PROJECT_KEY>'},
'issuetype': {'name': 'Story'},
'summary': 'Story 1 title',
'description': {
'type': 'doc', 'version': 1,
'content': [{'type': 'paragraph', 'content': [{'type': 'text', 'text': 'Description 1'}]}]
}
}
},
{
'fields': {
'project': {'key': '<PROJECT_KEY>'},
'issuetype': {'name': 'Story'},
'summary': 'Story 2 title',
'description': {
'type': 'doc', 'version': 1,
'content': [{'type': 'paragraph', 'content': [{'type': 'text', 'text': 'Description 2'}]}]
}
}
}
]
}
json.dump(payload, open('/tmp/jira_bulk.json', 'w'))
print(f'Payload: {len(payload[\"issueUpdates\"])} issues')
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_bulk.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/bulk" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
for issue in d.get('issues', []):
print(f\"Created: {issue['key']} — https://$ATLASSIAN_INSTANCE/browse/{issue['key']}\")
for err in d.get('errors', []):
print(f\"Error: {json.dumps(err)}\")
"
rm -f /tmp/jira_bulk.json
Jira v3 API uses ADF for rich-text fields (description, comment body). All ADF documents share this wrapper:
{
"type": "doc",
"version": 1,
"content": [ ... ]
}
Paragraph:
{"type": "paragraph", "content": [{"type": "text", "text": "Hello world"}]}
Heading (levels 1–6):
{"type": "heading", "attrs": {"level": 2}, "content": [{"type": "text", "text": "Section Title"}]}
Bold / Italic / Code:
{"type": "text", "text": "bold text", "marks": [{"type": "strong"}]}
{"type": "text", "text": "italic text", "marks": [{"type": "em"}]}
{"type": "text", "text": "inline code", "marks": [{"type": "code"}]}
Bullet List:
{
"type": "bulletList",
"content": [
{"type": "listItem", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Item 1"}]}]},
{"type": "listItem", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Item 2"}]}]}
]
}
Ordered List:
{
"type": "orderedList",
"content": [
{"type": "listItem", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Step 1"}]}]},
{"type": "listItem", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Step 2"}]}]}
]
}
Code Block:
{
"type": "codeBlock",
"attrs": {"language": "python"},
"content": [{"type": "text", "text": "print('hello')"}]
}
Table:
{
"type": "table",
"content": [
{
"type": "tableRow",
"content": [
{"type": "tableHeader", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Header 1"}]}]},
{"type": "tableHeader", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Header 2"}]}]}
]
},
{
"type": "tableRow",
"content": [
{"type": "tableCell", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Cell 1"}]}]},
{"type": "tableCell", "content": [{"type": "paragraph", "content": [{"type": "text", "text": "Cell 2"}]}]}
]
}
]
}
Link:
{"type": "text", "text": "Click here", "marks": [{"type": "link", "attrs": {"href": "https://example.com"}}]}
Mention (user):
{"type": "mention", "attrs": {"id": "<accountId>", "text": "@User Name"}}
Fetch all issues assigned to the current user in active sprints:
ATLASSIAN_EMAIL="${ATLASSIAN_EMAIL:-$(grep 'export ATLASSIAN_EMAIL' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_API_TOKEN="${ATLASSIAN_API_TOKEN:-$(grep 'export ATLASSIAN_API_TOKEN' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//')}"
ATLASSIAN_INSTANCE="${ATLASSIAN_INSTANCE:-$(grep 'export ATLASSIAN_INSTANCE' ~/.zshrc ~/.bashrc 2>/dev/null | head -1 | sed 's/.*=\"//;s/\".*//' || echo 'yourcompany.atlassian.net')}"
python3 -c "
import json
payload = {
'jql': 'assignee = currentUser() AND sprint in openSprints() ORDER BY status ASC, priority DESC',
'maxResults': 50,
'fields': ['summary', 'status', 'priority', 'issuetype', 'labels', 'parent', 'subtasks']
}
json.dump(payload, open('/tmp/jira_my.json', 'w'))
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_my.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/search" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f'My Issues ({d[\"total\"]} total):')
print()
for issue in d['issues']:
f = issue['fields']
prio = f.get('priority',{}).get('name','—') if f.get('priority') else '—'
parent = f.get('parent',{}).get('key','') if f.get('parent') else ''
parent_str = f' (under {parent})' if parent else ''
subtask_count = len(f.get('subtasks', []))
sub_str = f' [{subtask_count} sub-tasks]' if subtask_count else ''
print(f\"{issue['key']}\t{f['issuetype']['name']}\t{f['status']['name']}\t{prio}\t{f['summary']}{parent_str}{sub_str}\")
"
rm -f /tmp/jira_my.json
Variations:
assignee = currentUser() AND status != Doneassignee = currentUser() AND project = PROJ AND status != DoneFetch an issue's sub-tasks to see the full breakdown:
ISSUE_KEY="PROJ-123"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY?fields=summary,status,subtasks" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
f = d['fields']
print(f\"Parent: {d['key']} — {f['summary']} [{f['status']['name']}]\")
print()
subtasks = f.get('subtasks', [])
if subtasks:
print(f'Sub-tasks ({len(subtasks)}):')
for st in subtasks:
sf = st['fields']
print(f\" {st['key']}\t{sf['status']['name']}\t{sf['summary']}\")
else:
print('No sub-tasks.')
"
After viewing the parent, create sub-tasks referencing it:
python3 -c "
import json
payload = {
'fields': {
'project': {'key': '<PROJECT_KEY>'},
'parent': {'key': '<PARENT_KEY>'},
'issuetype': {'name': 'Sub-task'},
'summary': '<Sub-task summary>',
'description': {
'type': 'doc',
'version': 1,
'content': [
{
'type': 'heading', 'attrs': {'level': 3},
'content': [{'type': 'text', 'text': 'Acceptance Criteria'}]
},
{
'type': 'bulletList',
'content': [
{'type': 'listItem', 'content': [{'type': 'paragraph', 'content': [{'type': 'text', 'text': 'Criterion 1'}]}]},
{'type': 'listItem', 'content': [{'type': 'paragraph', 'content': [{'type': 'text', 'text': 'Criterion 2'}]}]}
]
}
]
}
}
}
json.dump(payload, open('/tmp/jira_subtask.json', 'w'))
print('Payload written.')
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_subtask.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
if 'key' in d:
print(f\"Created sub-task: {d['key']}\")
print(f\"URL: https://$ATLASSIAN_INSTANCE/browse/{d['key']}\")
else:
print('Error:', json.dumps(d, indent=2))
sys.exit(1)
"
rm -f /tmp/jira_subtask.json
Add a Confluence page URL (or any external link) as a web link on a Jira issue:
ISSUE_KEY="PROJ-123"
LINK_URL="https://yourcompany.atlassian.net/wiki/spaces/PROJ/pages/12345/Page-Title"
LINK_TITLE="Architecture Document"
python3 -c "
import json
payload = {
'object': {
'url': '$LINK_URL',
'title': $(python3 -c "import json; print(json.dumps('$LINK_TITLE'))"),
'icon': {
'url16x16': 'https://yourcompany.atlassian.net/favicon.ico'
}
}
}
json.dump(payload, open('/tmp/jira_remotelink.json', 'w'))
"
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
-X POST \
-H "Content-Type: application/json" \
-d @/tmp/jira_remotelink.json \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY/remotelink" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
if 'id' in d:
print(f\"Link added to {sys.argv[1]}: {sys.argv[2]}\")
else:
print('Error:', json.dumps(d, indent=2))
" "$ISSUE_KEY" "$LINK_TITLE"
rm -f /tmp/jira_remotelink.json
curl -s -u "$ATLASSIAN_EMAIL:$ATLASSIAN_API_TOKEN" \
"https://$ATLASSIAN_INSTANCE/rest/api/3/issue/$ISSUE_KEY/remotelink" \
| python3 -c "
import json, sys
links = json.load(sys.stdin)
if links:
for l in links:
obj = l.get('object', {})
print(f\"{l['id']}\t{obj.get('title','')}\t{obj.get('url','')}\")
else:
print('No remote links.')
"
Custom fields have instance-specific IDs (e.g., customfield_10014). Always discover before using:
customfield_XXXXX in create/update payloadsCommon custom fields (IDs vary by instance):
customfield_10014 (typically) — set to epic's issue keycustomfield_10016 (typically) — numeric valuecustomfield_10020 (typically) — sprint ID (number)Created: PROJ-123 — https://instance/browse/PROJ-123PROJ-123: transitioned to "In Progress"$ATLASSIAN_INSTANCE env var (defaults to yourcompany.atlassian.net)/rest/api/3) — uses ADF for rich text/rest/agile/1.0) — boards, sprints, backlog204 No Content = success for update, transition, assign operationsparent.key in the create payloadnpx claudepluginhub nexussema/omg-marketplace --plugin scrum-masterAutomates Jira tasks (issues, projects, sprints, boards, comments) via Rube MCP (Composio). Always search tools first for current schemas.
Epic creation and sprint management - create epics, manage sprints, view backlog, estimate with story points. TRIGGERS: 'create an epic', 'create epic', 'new epic', 'show the backlog', 'view backlog', 'add to sprint', 'move to sprint', 'set story points', 'sprint planning', 'epic for', 'link to epic', 'sprint list', 'active sprint', 'velocity', 'create subtask'. NOT FOR: bugs/tasks/stories without epic context (use jira-issue), field ID discovery (use jira-fields), searching issues by JQL (use jira-search), transitioning issues through workflow (use jira-lifecycle).
YouTrack issue tracker domain knowledge — data model, custom fields, query language, commands, linking, state machines, and tags. Invoke whenever task involves any interaction with YouTrack — creating issues, searching, updating fields, linking, querying, or understanding workflows.