openhands/extensions

jira-issue-to-pr

This skill should be used when the user asks to "set up a Jira automation to create pull requests", "poll Jira for create-pr issues", "automatically create GitHub PRs from Jira tickets", "deploy a Jira issue-to-PR automation", "create a Jira to GitHub PR workflow", or mentions automating GitHub PR creation from a Jira label. Deploys a cron-based OpenHands automation that watches a Jira Cloud project for issues labeled with a configurable label (default: "create-pr") and spawns an agent conversa…

First seen Jul 25, 2026

Installation

$ npx skills add openhands/extensions --skill jira-issue-to-pr

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from openhands/extensions · top by installs.

npx skills add openhands/extensions

Browse all from openhands/extensions

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 143
License LICENSE
Default branch main
Open issues 45
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,124 B
  • docs SUMMARY.md 703 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 9 installs

SKILL.md

Jira → GitHub PR Automation

Deploys a cron automation that polls a Jira Cloud instance for open issues carrying a configurable label and, for each new issue, starts an OpenHands agent conversation that clones the GitHub repository specified in the ticket body, creates a branch, implements or placeholders the requested change, and opens a pull request. Once the conversation starts, it also posts a comment on the Jira ticket: "I'm on it: <conversation URL>".

How It Works

  1. Poll - every N minutes, POST /rest/api/3/search/jql on the Jira Cloud instance

to find open issues with the configured label.

  1. Deduplicate - on the very first run the script records a firstrunat baseline

timestamp in the KV store; any issue whose updated timestamp predates that baseline is skipped (no backfill blast on first deploy). Using updated rather than created means an old issue that has its label added after the automation is deployed will still be picked up. Subsequent runs filter by both firstrunat and a KV-backed set of already-processed issue keys. A maxnewper_run cap (default 5) limits conversations started per cron firing as additional defense-in-depth.

  1. Dispatch - for each new issue, call POST /api/conversations on the agent server

to start an independent agent conversation with a PR-creation prompt. The prompt instructs the agent to extract the target GitHub repository (owner/repo) from the ticket body.

  1. Comment - immediately after the conversation is created, post a Jira comment on the

issue: I'm on it: <conversation URL>.

  1. Persist - record the processed issue key so re-runs never duplicate work.

The polling run is lightweight (stdlib only, no SDK install); LLM costs are incurred only when new issues are actually found.

Prerequisites

Before deploying, ensure the following are in place:

Requirement Details
Jira API token Stored as an OpenHands secret (see [Jira API token setup](#jira-api-token))
GitHub token Must be stored as an OpenHands secret with repo + workflow scope so the spawned conversation can push branches and open PRs
Jira label The label to watch for (default: create-pr) must exist in the Jira project
GitHub repo The target repository must exist and the GitHub token must have write access

Deploying the Automation

Step 1 - Collect parameters

Gather the following from the user before proceeding:

Parameter Example Notes
jirabaseurl https://acme.atlassian.net No trailing slash
jira_email [email protected] Atlassian account email for Basic auth
jiratokensecret JIRACLOUDKEY Name of the OpenHands secret holding the API token
jira_label create-pr Label to watch for (optional, defaults to create-pr)
maxnewper_run 5 Max conversations dispatched per cron firing (optional, defaults to 5)
cron_schedule /5 * Polling frequency in cron syntax

Note: The GitHub repository is not configured here. Each Jira ticket body must include
a reference to the target GitHub repo in owner/repo format (e.g. acme-org/backend).
The spawned agent extracts it from the ticket text.

Step 2 - Create config.json

Create config.json next to scripts/main.py when packaging:

{
  "jira_base_url":     "https://acme.atlassian.net",
  "jira_email":        "[email protected]",
  "jira_token_secret": "JIRA_CLOUD_KEY",
  "jira_label":        "create-pr",
  "max_new_per_run":   5
}

Step 3 - Package the tarball

Copy scripts/main.py from this skill and package it with the config.json:

WORK=$(mktemp -d)
cp <skill-dir>/scripts/main.py "$WORK/main.py"
# write config.json into $WORK/config.json (see Step 2)
tar -czf /tmp/jira-issue-to-pr.tar.gz -C "$WORK" .
python3 -m py_compile "$WORK/main.py"   # validate syntax before uploading

Step 4 - Upload the tarball

TARBALL_PATH=$(curl -s -X POST \
  "http://localhost:8000/api/automation/v1/uploads?name=jira-issue-to-pr" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
  -H "Content-Type: application/gzip" \
  --data-binary @/tmp/jira-issue-to-pr.tar.gz \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['tarball_path'])")

Step 5 - Create the automation

curl -s -X POST "http://localhost:8000/api/automation/v1" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"Jira issue-to-PR Poller\",
    \"trigger\": {
      \"type\":     \"cron\",
      \"schedule\": \"*/5 * * * *\",
      \"timezone\": \"UTC\"
    },
    \"tarball_path\": \"$TARBALL_PATH\",
    \"entrypoint\":   \"python3 main.py\",
    \"timeout\":      540
  }" | python3 -m json.tool

Save the returned id - use it for updates and monitoring.

Step 6 - Verify with a test dispatch

curl -s -X POST \
  "http://localhost:8000/api/automation/v1/<AUTOMATION_ID>/dispatch" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" | python3 -m json.tool

# After ~30 seconds, check the run status:
curl -s "http://localhost:8000/api/automation/v1/<AUTOMATION_ID>/runs?limit=1" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
  | python3 -c "import sys,json; r=json.load(sys.stdin)['runs'][0]; print(r['status'], r.get('error_detail'))"

Updating an Existing Deployment

To change configuration or update the script:

  1. Edit config.json with new values.
  2. Repackage and upload a new tarball (Steps 3-4 above).
  3. PATCH the existing automation with the new tarball_path:
curl -s -X PATCH \
  "http://localhost:8000/api/automation/v1/<AUTOMATION_ID>" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"tarball_path\": \"<NEW_TARBALL_PATH>\"}"

Resetting Processed State

To reprocess issues that were already handled (e.g., after testing), clear the KV store:

curl -s -X DELETE \
  "http://localhost:8000/api/automation/v1/<KV_BASE>/v1/kv/state" \
  -H "Authorization: Bearer $AUTOMATION_KV_TOKEN"

Or delete and recreate the automation to start with a clean state.

Script Reference

The automation script lives at scripts/main.py. Key behaviors:

  • No SDK dependencies - pure Python stdlib; no setup.sh or uv install needed.
  • Config file - reads all parameters from config.json co-located with the script.
  • First-run baseline - on the very first execution the script writes firstrunat (UTC timestamp) into the KV store and exits without dispatching; issues whose updated timestamp predates that baseline are skipped on all subsequent runs. Using updated (not created) means an old issue that has its label applied after deployment is correctly treated as new.
  • Per-run cap - maxnewper_run (default 5) limits how many conversations are started per cron firing; any remaining new issues are dispatched on the next run.
  • KV store - persists {"processedkeys": [...], "firstrunat": "..."} between runs; falls back to a local file in dev environments where AUTOMATIONKV_TOKEN is absent.
  • Jira API - uses POST /rest/api/3/search/jql (the current non-deprecated endpoint).
  • Conversation dispatch - calls POST /api/conversations on the agent server with the current user's LLM/agent settings forwarded to the new conversation.
  • Error transparency - captures Jira HTTP response bodies in error messages for fast diagnosis.

Known Limitations

Pre-existing issues updated after deployment

The deduplication filter compares each issue's fields.updated timestamp against firstrunat. updated is Jira's last-modified timestamp for the issue as a whole — it advances whenever any field changes (comments, priority, description, status, etc.), not only when the create-pr label is applied.

This means a pre-existing issue that already carried the label at deployment time can slip through the filter if it is later updated for an unrelated reason (e.g. someone adds a comment), because its updated timestamp will have advanced past firstrunat while its key is not yet in processed_keys.

Workaround: The only fully reliable way to detect exactly when a label was applied is the Jira changelog API (GET /rest/api/3/issue/{key}/changelog), which requires an extra HTTP call per issue. To avoid that overhead, keep the automation's scope narrow: use a label that is exclusively added as a PR-creation signal and is not already present on issues at the time of deployment.

Once an issue is successfully dispatched its key is written to processed_keys in the KV store and is permanently skipped on every future run — regardless of subsequent label changes, comments, or any other updates to the issue. The only way to re-trigger a previously processed issue is to manually clear the KV store or delete and recreate the automation. This means the risk window described above is finite: as soon as the automation processes a pre-existing issue (even accidentally), it will never dispatch that issue again.

Additional Resources

  • references/setup.md - Jira API token creation, GitHub token scopes, cron schedule reference, and troubleshooting guide.