Create, update, fetch, delete, diff, and validate Jira tickets from structured markdown or JSON sources. Use when the user asks to create Jira tickets, update existing issues, bulk-create stories and subtasks from a spec, compare local definitions against live Jira state, validate that sub-ticket estimates sum to parent estimates, or rewrite markdown links to git browse URLs in ticket descriptions. Handles epics, stories, subtasks, bugs, tasks, custom fields, story points, and link rewriting.
Shorthand used below: $PY = <skill_dir>/.venv/bin/python, $S = <skill_dir>/scripts
Run the preflight script before any other operation:
python3 <skill_dir>/scripts/jira_preflight.py
It validates the entire environment in a single pass:
markdown, markdownify).jira.json) is found and has required fields (jira_url, project_key)field_mappings or issue_types are emptyIf the venv or dependencies are missing, the preflight tells you to run:
python3 <skill_dir>/scripts/jira_setup_env.py
To skip the connectivity check (e.g. on repeated calls):
python3 <skill_dir>/scripts/jira_preflight.py --skip-connectivity
# Single ticket
$PY $S/fetch_tickets.py --key PROJ-101 --format detail
# Multiple tickets by key (--key is repeatable, or use comma-separated --keys)
$PY $S/fetch_tickets.py --key PROJ-101 --key PROJ-102 --key PROJ-103 --format table
$PY $S/fetch_tickets.py --keys PROJ-101,PROJ-102,PROJ-103 --format table
# JQL search
$PY $S/fetch_tickets.py --jql "project=PROJ AND type=Story" --format table
# Filter (builds JQL automatically — quote values with parens in shell)
$PY $S/fetch_tickets.py --filter 'assignee=currentUser()' 'status=In Progress'
# Children of epic or story
$PY $S/fetch_tickets.py --children-of PROJ-100
# Board (active sprint only by default; add --board-all for everything)
$PY $S/fetch_tickets.py --board-id 123
$PY $S/fetch_tickets.py --board-id 123 --filter 'assignee=currentUser()'
# List boards
$PY $S/fetch_tickets.py --boards
Formats: table (default — shows key, type, SP, priority, status, sprint, summary), detail, json.
Options: --max-results N (default 50), --no-convert (skip markup conversion), --include-remote-links (fetch remote issue links for detail/json output).
Filter values that are JQL functions (e.g. currentUser(), now(), startOfDay()) are passed through unquoted. Plain values are auto-quoted.
Shell quoting: Always single-quote filter values containing parentheses (e.g. 'assignee=currentUser()') to prevent shell expansion.
# Story under an epic
$PY $S/create_ticket.py --type story --summary "Title" --description "Desc" \
--epic PROJ-100 --story-points 2 --priority High
# Subtask under a story
$PY $S/create_ticket.py --type sub-task --summary "Subtask title" --parent PROJ-101
# With assignee (use display name — automatically resolved to accountId on Jira Cloud)
$PY $S/create_ticket.py --type story --summary "Title" --assignee "Jane Smith" --parent PROJ-100
# With extra required fields (use --fields for raw JSON when named flags aren't enough)
$PY $S/create_ticket.py --type bug --summary "Bug title" \
--fields '{"customfield_29823": {"value": "Dev"}, "customfield_29843": [{"value": "Production"}]}'
Create flags: --type, --summary, --description, --epic, --parent, --priority, --assignee, --component (repeatable), --fix-version (repeatable), --sprint, --labels, --story-points, --attachment (repeatable), --fields (raw JSON for custom fields), --copy-fields-from ISSUE-KEY (copy custom fields like QBR/team from an existing issue).
--copy-fields-from: Fetches all custom fields from the source issue and applies them to the new issue. Only copies customfield_* fields that aren't already set by other flags. Automatically skips non-copyable fields (e.g. GreenHopper rank fields) by checking field schema metadata. Useful when your Jira project has required custom fields (e.g. QBR, QBR Theme) that vary per team/project — just point at a sibling issue instead of hunting for field IDs.
Create order: epics → stories → subtasks. On failure, stop — do not continue with dependents.
If create fails with a 400 error, the script auto-diagnoses the missing field. Always follow the auto-diagnosis output:
Fix: line verbatim.$PY $S/discover_fields.py --fields-for-type <type> manually to explore available fields.$PY $S/bulk_create.py --source tickets.md --epic PROJ-100 --dry-run
Remove --dry-run after review. See SOURCE_FORMAT.md for format.
# Common fields
$PY $S/update_ticket.py --key PROJ-101 --summary "New Title" --story-points 3 --priority High
# Status transition
$PY $S/update_ticket.py --key PROJ-101 --status "In Progress"
# Assign by display name (auto-resolved to accountId on Jira Cloud)
$PY $S/update_ticket.py --key PROJ-101 --assignee "Jane Smith"
# Unassign (empty string = unassign)
$PY $S/update_ticket.py --key PROJ-101 --assignee ""
# Re-parent an issue (set parent to another issue)
$PY $S/update_ticket.py --key PROJ-101 --parent PROJ-200
# Any field by name (updates only — resolves via field_catalog)
$PY $S/update_ticket.py --key PROJ-101 --set "components=Backend"
# Comment, link, attachment (all combinable in one call)
$PY $S/update_ticket.py --key PROJ-101 --comment "Done." \
--link "Blocks:PROJ-200" --attachment report.pdf
# Log work (time tracking)
$PY $S/update_ticket.py --key PROJ-101 --worklog "2h" --worklog-comment "Code review"
$PY $S/update_ticket.py --key PROJ-101 --worklog "1d"
Update flags: --summary, --description, --status, --priority, --assignee, --parent, --component, --fix-version, --sprint, --labels, --story-points, --set "field=value" (repeatable), --fields (raw JSON), --comment, --link "Type:KEY" (repeatable), --attachment (repeatable), --worklog "TIME" (e.g. 2h, 1d, 30m), --worklog-comment, --dry-run.
--parent: Sets the parent issue (e.g. --parent PROJ-200). Auto-wraps as {"key": "..."} for the API. If the update fails with a hierarchy error, the script auto-diagnoses the issue: shows both issue types' hierarchy levels and suggests what intermediate type to create. Also works via --set "parent=PROJ-200".
$PY $S/bulk_update.py --tickets "PROJ-1,PROJ-2" --status Done --confirm
$PY $S/bulk_update.py --board-id 123 --sprint "Sprint 5" --confirm
$PY $S/bulk_update.py --jql "project=PROJ AND priority=High" --priority Medium --confirm
Defaults to dry-run preview without --confirm.
$PY $S/delete_ticket.py --key PROJ-110 --dry-run # preview
$PY $S/delete_ticket.py --key PROJ-110 --confirm # execute
Never delete without explicit user approval.
$PY $S/issue_comments.py list --key PROJ-101
$PY $S/issue_comments.py add --key PROJ-101 --body "Thanks for the update."
$PY $S/issue_comments.py edit --key PROJ-101 --comment-id 12345 --body "Revised note."
$PY $S/issue_comments.py delete --key PROJ-101 --comment-id 12345
$PY $S/list_projects.py
$PY $S/discover_fields.py --all --apply # full discovery + save to config
$PY $S/discover_fields.py --search "QBR" # find a field by name
$PY $S/discover_fields.py --fields-for-type epic # list all fields + values for a type
$PY $S/discover_fields.py --transitions PROJ-101 # list available workflow transitions
$PY $S/diff_tickets.py --manifest # diff local vs Jira
$PY $S/validate_estimates.py --epic PROJ-100 # check sub-ticket estimate sums
--apply). Do NOT abandon scripts and write custom code.--set works on both create and update — resolves field names via field_catalog. For fields that --set can't resolve, use --fields '{"customfield_123": "val"}' (raw JSON).--no-convert to skip. --rewrite-links rewrites relative markdown links to git browse URLs.) are detected, the paths are rewritten to basenames for Jira wiki markup (!image.png!), and the files are uploaded as attachments after issue creation/update. HTTP/HTTPS URLs are left as-is. For --description-file, paths resolve relative to the file's directory first, then fall back to CWD if not found; for --description, relative to CWD. Works in create_ticket, update_ticket, and bulk_create.```mermaid code blocks) are automatically rendered to PNG and attached. Requires mmdc (npm i -g @mermaid-js/mermaid-cli) or npx. If neither is available the blocks are left as code with a warning.| Error | Fix |
|---|---|
| 401 | Check env vars and token permissions |
| 404 | Check issue key and project_key |
| Missing required fields | Script auto-suggests the fix. Or use --copy-fields-from SIBLING-KEY to inherit fields. Or run discover_fields.py --fields-for-type <type> manually |
| No transition found | Some statuses need intermediate steps |
| ModuleNotFoundError | Run jira_setup_env.py |
| Sprint not setting | Run discover_fields.py --all --apply |
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer