Your AI agent's interface to Jira Cloud. JSON in, JSON out.
Project description
jira-genie ๐ง
Your AI agent's interface to Jira Cloud. JSON in, JSON out.
Give your agents the ability to search, create, edit, and manage Jira issues
through a simple CLI. Schema-aware field resolution means agents write
story_points โ not customfield_10036. Templates mean repetitive ticket
creation is a one-liner.
Works with any AI agent framework (LangChain, CrewAI, Claude Code, raw subprocess) and as a standalone CLI for humans and scripts.
Why
- JSON by default โ every command outputs structured JSON that agents parse directly
- Friendly field names โ write
story_pointsinstead ofcustomfield_10036, resolved automatically via your instance's schema - Templates โ define issue defaults once, reuse across agents and workflows
- Shell completion โ field names and enum values from your live Jira schema
- Zero-config auth โ
jira auth loginhandles OAuth, token refresh is transparent - Agent-ready โ designed as a tool agents call, not a TUI humans click through
Install
# Homebrew
brew tap henriquebastos/tap
brew install jira-genie
# Or with uv
uv tool install jira-genie
# Or from source
git clone https://github.com/henriquebastos/jira-genie.git
cd jira-genie
uv tool install -e .
Quick Start
# 1. Login (opens browser for Atlassian OAuth)
jira auth login
# 2. Sync your instance's field schema
jira fields sync
jira fields sync --project DEV # sync type schemas for a specific project
# 3. Get an issue
jira issue get DEV-123
# {"key": "DEV-123", "summary": "Fix auth", "status": "To Do", "assignee": null, "priority": "P2: Medium", "type": "Task"}
# 4. Search with JQL
jira search "project = DEV AND sprint in openSprints() AND status != Done"
# [{"key": "DEV-124", "summary": "...", ...}, ...]
# 5. Create an issue
jira issue create --json '{"project": "DEV", "issuetype": "Task", "summary": "New task", "parent": "DEV-100"}'
# {"id": "12345", "key": "DEV-125", ...}
Agent Integration
Every command returns JSON to stdout. Agents can call jira as a subprocess
and parse the output directly. Common agent workflows:
# Read an issue and decide what to do
jira issue get DEV-123
# Search for unfinished work
jira search "assignee = currentUser() AND status != Done"
# Create a task from agent analysis
jira issue create --template backend --summary "Fix race condition in payment handler"
# Update status after completing work
jira issue transition DEV-123 "Done"
jira issue comment DEV-123 "Fixed in commit abc1234"
# Bulk update parent epic
jira bulk edit DEV-1 DEV-2 DEV-3 --set parent=DEV-100
For raw API access (no field resolution), use --raw on reads and
--raw-payload on writes.
Authentication
jira auth login # opens browser, handles OAuth 2.0 (3LO) + PKCE
jira auth status # show login state
jira auth logout # remove stored tokens
Token refresh is automatic and transparent โ you never deal with tokens.
Using your own OAuth app
The CLI ships with a default OAuth app. To use your own, register one at developer.atlassian.com:
- Create an OAuth 2.0 (3LO) app
- Callback URL:
http://localhost:8888/callback - Jira API scopes:
read:jira-work,write:jira-work,read:jira-user - Enable sharing under Distribution
# Via flags
jira auth login --client-id YOUR_ID --client-secret YOUR_SECRET
# Or via environment variables
export JIRA_CLIENT_ID=YOUR_ID
export JIRA_CLIENT_SECRET=YOUR_SECRET
jira auth login
Resolution order: flags โ env vars โ built-in defaults. Credentials are saved on login and used for all subsequent token refreshes.
Multi-Instance
jira --instance mycompany issue get DEV-123
# or
export JIRA_INSTANCE=mycompany
Matches against site name (mycompany โ mycompany.atlassian.net).
Single instance is auto-detected.
Commands
Issues
jira issue get DEV-123 # formatted JSON
jira issue get DEV-123 --fields summary,status # specific fields
jira issue get DEV-123 --raw # raw API response
jira issue create --json '{"project": "DEV", "issuetype": "Task", "summary": "Title"}'
jira issue create --template my-template --summary "Title"
jira issue create --raw-payload '{"fields": {...}}' # bypass resolution
jira issue edit DEV-123 --set priority="P1: High" --set story_points=5
jira issue edit DEV-123 --json '{"team": "Backend"}'
jira issue edit DEV-123 --raw-payload '{"fields": {...}}' # bypass resolution
jira issue transition DEV-123 "In Progress"
jira issue assign DEV-123 alice@example.com
jira issue comment DEV-123 "Deployed to staging"
jira issue link DEV-123 DEV-456 --type blocks
Search
jira search "project = DEV AND sprint in openSprints()"
jira search "parent = DEV-100" --fields summary,status,assignee
Bulk
jira bulk edit DEV-1 DEV-2 DEV-3 --set parent=DEV-100
Sprints and Boards
jira sprint current --board 42
jira sprint list --board 42 --state active,future
jira sprint issues 123 --fields summary,status
jira board list --project DEV
jira board backlog 42
Users
jira user me
jira user search "alice"
Field Resolution
The CLI maps friendly field names to Jira's internal field IDs using
the synced schema. You write story_points, the API receives customfield_10036.
Values are also expanded based on field type:
| You write | API receives |
|---|---|
"project": "DEV" |
"project": {"key": "DEV"} |
"issuetype": "Task" |
"issuetype": {"name": "Task"} |
"parent": "DEV-100" |
"parent": {"key": "DEV-100"} |
"priority": "P1: High" |
"priority": {"name": "P1: High"} |
"team": "Backend" |
"customfield_10001": {"value": "Backend"} |
"components": ["API"] |
"components": [{"name": "API"}] |
"story_points": 5 |
"customfield_10036": 5 |
"labels": ["urgent"] |
"labels": ["urgent"] |
Already-structured values (dicts) pass through without double-wrapping.
Use --raw-payload to bypass all resolution.
Schema
jira fields sync # sync fields + discover available projects
jira fields sync --project DEV # also sync type schemas (required fields, allowed values)
jira fields list # all fields
jira fields list --filter story # search by name
jira fields schema --project DEV --type Task # full type schema for agents
Run after login, and again if your Jira admin changes field configuration.
You can sync multiple projects incrementally โ each --project adds to the
existing schema without overwriting previously synced projects.
Stored at ~/.config/jira-genie/{cloud_id}/schema.json.
Templates
Partial JSON with friendly field names. Define defaults for repetitive issue creation.
# Create
jira template create backend --json '{
"project": "DEV",
"issuetype": "Task",
"parent": "DEV-100",
"components": ["API"]
}'
# Use
jira issue create --template backend --summary "Fix the thing"
# Manage
jira template list
jira template show backend
jira template delete backend
jira template default backend # set as default
jira template default # show current default
jira template default --clear # remove default
Composition order
Fields merge in layers โ later layers override earlier ones (shallow, not deep):
Template โ --json โ --set/--summary โ resolve_fields() โ API
Or bypass everything with --raw-payload.
Shell Completion
jira completion install # prints setup for your shell
Requires argcomplete on PATH: uv tool install argcomplete
Completes commands, template names, field names, and enum values from your live schema:
jira issue create --template <TAB> โ backend, bug, ...
jira issue edit DEV-123 --set <TAB> โ story_points=, priority=, team=, ...
jira issue edit DEV-123 --set priority=<TAB> โ P0: Critical, P1: High, ...
Configuration
~/.config/jira-genie/
โโโ config.json # default instance
โโโ {cloud_id}/
โโโ config.json # client_id, cloud_id, site
โโโ refresh.json # OAuth refresh token
โโโ schema.json # field registry + type schemas
โโโ templates/
โโโ backend.json
Development
git clone https://github.com/henriquebastos/jira-genie.git
cd jira-genie
uv sync
uv run pytest # 148 tests
uv run ruff check src/ tests/
uv run jira --help
Architecture
src/jira/
โโโ auth.py # OAuth 2.0 (3LO) โ login, token refresh, PKCE
โโโ cache.py # FileCache โ file-backed key-value store with expiry
โโโ cli.py # argparse CLI โ parse/cli split, all dispatch
โโโ client.py # JiraClient + sub-clients (Issue, Search, Sprint, Board, User)
โโโ completers.py # Shell completion for argcomplete
โโโ config.py # Instance discovery and resolution
โโโ formatters.py # Pure response transformers (raw API โ clean JSON)
โโโ schema.py # Field registry, type schemas, field resolution
โโโ templates.py # Template CRUD, merge logic, build_issue_fields
See conventions.md for code style and workflow guidelines.
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jira_genie-0.2.1.tar.gz.
File metadata
- Download URL: jira_genie-0.2.1.tar.gz
- Upload date:
- Size: 7.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5f623289217f50e04fc234aeb718039c4bfeec8379247b9800cf0c002c017b5
|
|
| MD5 |
04421fe5c0efb3f64479994606e85a58
|
|
| BLAKE2b-256 |
e9018a4b9c89afee56024f485ece0225d532a0417f031c09b1df750ab1a21c4f
|
Provenance
The following attestation bundles were made for jira_genie-0.2.1.tar.gz:
Publisher:
release.yml on henriquebastos/jira-genie
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jira_genie-0.2.1.tar.gz -
Subject digest:
e5f623289217f50e04fc234aeb718039c4bfeec8379247b9800cf0c002c017b5 - Sigstore transparency entry: 1188771466
- Sigstore integration time:
-
Permalink:
henriquebastos/jira-genie@abe1c34423ee682d66625d5e91912576cd5a293e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/henriquebastos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@abe1c34423ee682d66625d5e91912576cd5a293e -
Trigger Event:
release
-
Statement type:
File details
Details for the file jira_genie-0.2.1-py3-none-any.whl.
File metadata
- Download URL: jira_genie-0.2.1-py3-none-any.whl
- Upload date:
- Size: 21.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
359d489a79bb106110b8a84c7ea3ef49dbf49e94f703fda3126a8e4453fb55fa
|
|
| MD5 |
4190dadc613b070aa9765a010e89e90f
|
|
| BLAKE2b-256 |
78687637d92bdf625c5b7fe2721fdc1db54445fe1572ec8097b53513a80c3e91
|
Provenance
The following attestation bundles were made for jira_genie-0.2.1-py3-none-any.whl:
Publisher:
release.yml on henriquebastos/jira-genie
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jira_genie-0.2.1-py3-none-any.whl -
Subject digest:
359d489a79bb106110b8a84c7ea3ef49dbf49e94f703fda3126a8e4453fb55fa - Sigstore transparency entry: 1188771479
- Sigstore integration time:
-
Permalink:
henriquebastos/jira-genie@abe1c34423ee682d66625d5e91912576cd5a293e -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/henriquebastos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@abe1c34423ee682d66625d5e91912576cd5a293e -
Trigger Event:
release
-
Statement type: