From jira
Manages Atlassian products (JIRA and Confluence) via REST API. Handles JIRA ticket creation, updates, epic linking, status changes. Also fetches and reads Confluence pages. Use when the user asks to "create a ticket", "update a ticket", "link to epic", "read this confluence page", or any JIRA/Confluence task.
How this skill is triggered — by the user, by Claude, or both
Slash command
/jira:jiraThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill handles JIRA and Confluence operations via the Atlassian REST APIs. It's parameterized to work with any Atlassian instance.
This skill handles JIRA and Confluence operations via the Atlassian REST APIs. It's parameterized to work with any Atlassian instance.
The wrapper script is located at ${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh.
Authentication (required):
security add-generic-password -s 'atlassian-auth' -a 'jwilson' -w '[email protected]:your-api-token'
Configuration (required):
security add-generic-password -s 'atlassian-config' -a 'jwilson' -w '{"domain":"yourcompany.atlassian.net","project":"PROJ","issueTypeId":"10002","focusFieldId":"customfield_10695","focusValueId":"10452","transitions":{"start":"21","done":"31","reopen":"11"}}'
| Field | Description | Example |
|---|---|---|
| domain | Atlassian instance domain | mycompany.atlassian.net |
| project | Default project key | PROJ |
| issueTypeId | Default issue type ID | 10002 (Task) |
| focusFieldId | Custom field ID for focus/team | customfield_10695 |
| focusValueId | Default focus value ID | 10452 |
| transitions | Map of transition name to ID | {"start":"21","done":"31"} |
Run the wrapper with no endpoint to see current config:
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh <METHOD> <endpoint> [json-body]
Examples:
# GET request
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET /rest/api/3/myself
# POST with JSON body
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh POST /rest/api/3/issue '{"fields":{...}}'
# PUT with JSON body
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh PUT /rest/api/3/issue/PROJ-123 '{"fields":{...}}'
When: User asks to create a new ticket, task, story, or issue
Available Issue Types:
| Type | ID | Use Case |
|---|---|---|
| Task | 10002 | Default for general work items |
| Bug | 10004 | Problems/defects |
| Spike | 10231 | Research/investigation work |
| Epic | 10000 | Large initiatives |
| Initiative | 10344 | Strategic work spanning multiple epics |
Note: Issue type IDs may vary by instance. Query available types with:
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET /rest/api/3/issuetype
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh POST /rest/api/3/issue '{
"fields": {
"project": {"key": "{PROJECT}"},
"summary": "{title}",
"description": {
"type": "doc",
"version": 1,
"content": [{"type": "paragraph", "content": [{"type": "text", "text": "{description}"}]}]
},
"issuetype": {"id": "{issueTypeId}"},
"customfield_XXXXX": {"id": "{focusValueId}"}
}
}'
After creation: Return the ticket key and link: https://{domain}/browse/{key}
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh POST /rest/api/3/issue '{
"fields": {
"project": {"key": "{PROJECT}"},
"summary": "{epic title}",
"description": {
"type": "doc",
"version": 1,
"content": [{"type": "paragraph", "content": [{"type": "text", "text": "{description}"}]}]
},
"issuetype": {"id": "10000"}
}
}'
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh PUT /rest/api/3/issue/{TICKET_KEY} '{"fields":{"parent":{"key":"{EPIC_KEY}"}}}'
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh PUT /rest/api/3/issue/{TICKET_KEY} '{
"fields": {
"summary": "{new title}",
"description": {
"type": "doc",
"version": 1,
"content": [{"type": "paragraph", "content": [{"type": "text", "text": "{new description}"}]}]
}
}
}'
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh POST /rest/api/3/issue/{TICKET_KEY}/comment '{
"body": {
"type": "doc",
"version": 1,
"content": [{"type": "paragraph", "content": [{"type": "text", "text": "{comment text}"}]}]
}
}'
Use transition IDs from config (stored in transitions field):
start - Begin work on ticketdone - Mark completereopen - Reopen closed ticketApply transition using config ID:
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh POST /rest/api/3/issue/{TICKET_KEY}/transitions '{"transition":{"id":"{transitions.done}"}}'
Only query transitions if config ID fails or is missing:
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET /rest/api/3/issue/{TICKET_KEY}/transitions
Then update the user's keychain config with the correct IDs for future use.
Do not verify status after transition. The API response confirms success (204) or failure (4xx). A separate GET request to check the new status is unnecessary.
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET /rest/api/3/issue/{TICKET_KEY}
Useful fields: fields.summary, fields.status.name, fields.assignee.displayName, fields.parent.key
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh POST /rest/api/3/search/jql '{"jql": "project={PROJECT} AND assignee=currentUser()", "maxResults": 10}'
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET '/rest/api/3/user/search?query={name}'
Extract page ID from URL: https://.../wiki/spaces/{space}/pages/{pageId}/{title} → {pageId}
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET '/wiki/api/v2/pages/{pageId}?body-format=storage'
Response fields: title, body.storage.value (HTML content)
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET '/wiki/rest/api/content/search?cql=text~"{searchTerm}"'
${CLAUDE_PLUGIN_ROOT}/scripts/atlassian-curl.sh GET /wiki/api/v2/spaces/{spaceId}/pages
The body.storage.value contains Atlassian Storage Format (XML/HTML). Common elements:
<h1>, <h2>, <h3> - Headings<p> - Paragraphs<table>, <tr>, <td> - Tables<ul>, <li> - Lists<ac:structured-macro> - Macros| Error | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Bad/expired credentials | Check keychain entry or regenerate API token |
| 404 Not Found | Issue/page doesn't exist | Verify ticket key or page ID |
| 400 Bad Request | Invalid field data | Check field names and formats |
To switch between Atlassian instances (e.g., Zylo vs IGG), update the config keychain entry:
# Delete existing
security delete-generic-password -s 'atlassian-config' -a 'jwilson'
# Add new config (include instance-specific transition IDs)
security add-generic-password -s 'atlassian-config' -a 'jwilson' -w '{"domain":"igg.atlassian.net","project":"IGG","issueTypeId":"10002","transitions":{"start":"21","done":"31","reopen":"11"}}'
You may also need to update the auth entry if credentials differ between instances.
Tip: To discover transition IDs for a new instance, query any ticket's transitions once and save the IDs to config.
body-format=storagehttps://{domain}/browse/{KEY}jq for parsingCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub jamiewilson1977/claude --plugin jira