A non-interactive CLI for Jira built for AI agents, CI, and automation. All output is machine-readable JSON by default, so it drops straight into jq pipelines, shell scripts, and LLM tool calling.
jira search jql "assignee = currentUser() AND status != Done" | jq -r '.[].key'- JSON by default, clean streams. Results go to
stdout, all diagnostics tostderr, and failures return a non-zero exit code.jq,rg, andwcwork without choking on error blobs, and scripts check$?instead of parsing strings. - Auth stays out of band. You log in once; the token lives in the OS keychain and is never written to disk or exposed to an agent. Execution inherits an already-authenticated environment.
- Inspectable, scoped state.
jira context set --project PROJpins defaults on disk so you don't repeat--projecton every call — and you can read back exactly what scope you're operating in. - Lean output. Project only the fields you need with repeatable
-F customfield_xflags, keeping responses (and token usage) small. - No daemon, no custom protocol. It's a plain binary. Every action is one replayable command line — debug by copying it from your shell history and running it again.
- Server-side audit.
jira me audit --date YYYY-MM-DDreconstructs what actually changed on a given day from Jira's own changelog, independent of local state.
See What makes this an agent CLI for the longer rationale.
This project is inspired by ankitpokhrel/jira-cli — a feature-rich interactive Jira command line with a full TUI (tables, keyboard navigation, prompts). Think of it as the k9s of Jira: visual and built for humans at a terminal.
This takes the opposite approach — the kubectl of Jira: non-interactive, scriptable, and built for agents and automation. No TUI, no prompts, just structured output machines can parse. Want a great interactive experience? Use that one. Want to wire Jira into an AI agent, CI pipeline, or shell script? Use this.
npm install -g @888aaen/jira-cliInstalls the jira binary for macOS (arm64, x64), Linux (x64, arm64), and Windows (x64).
Prerequisites: Go 1.25+
git clone https://github.com/AndersSpringborg/jira-agent-cli.git
cd jira-agent-cli
sudo make install # builds and installs to /usr/local/bin/jira
sudo make uninstall # to removeGive an agent Jira access in two commands:
npm install -g @888aaen/jira-cli
npx skills@latest add AndersSpringborg/jira-agent-cliThe first installs the jira binary. The second installs the jira-cli skill into ~/.claude/skills/jira-cli/, so any Claude Code agent on the machine learns to drive it — it checks for an existing session, guides you through login if needed, and picks the right command for each request.
Create an API token at https://id.atlassian.com/manage-profile/security/api-tokens, then:
jira auth login --server https://your-org.atlassian.net \
--email you@example.com --token YOUR_API_TOKENThe token is stored in the OS keychain — never written to disk.
Jira Cloud and Server/Data Center. Cloud profiles (
*.atlassian.net) use basic auth + REST v3 + ADF bodies. Server/Data Center profiles use PAT/bearer auth + REST v2 + wiki/plain text bodies.
jira ping # check connectivity
jira context set --project PROJ # default project for subsequent commands
jira context set --board-id 42
jira context set --display markdown # human-readable output everywhere (json is the default)A per-command --format flag always overrides the context default. See Output Formats.
jira issue list # issues in your project
jira issue view PROJ-123
jira issue create -p PROJ -s "Fix login bug" -t Bug
jira search jql "project = PROJ AND status = 'In Progress'"
jira issue list | jq '.[].key'| Flag | Description |
|---|---|
--format json |
Machine-readable JSON (default) |
--format markdown |
Structured markdown, fewer tokens for LLMs |
Set a persistent default with jira context set --display markdown; --format always overrides it.
Every mutation is a single command with explicit flags, so the transcript line is exactly what changed. Write commands print the result as JSON and exit non-zero on failure, so you can chain them with && and check the exit code.
# Create (prints structured JSON with .key; add --raw for the full Jira response)
jira issue create -p PROJ -s "Fix login bug" -t Bug -b "Steps to reproduce..."
# Edit fields, including custom fields by id (-F is repeatable)
jira issue edit PROJ-123 -s "New summary" -l backend -F customfield_10145="value"
# Transition through the workflow
jira issue move PROJ-123 "In Progress"
jira issue move PROJ-123 Done --resolution Fixed --comment "Shipped in v1.2"
# Assign and comment ('me' for yourself, 'x' to unassign; otherwise pass an account ID,
# username, or email resolvable by `jira user search`)
jira issue assign PROJ-123 me
jira issue comment add PROJ-123 "Investigated -- root cause was a stale cache."
# Link, clone, delete
jira issue link PROJ-123 PROJ-456 "blocks"
jira issue clone PROJ-123 -s "Follow-up: ..."
jira issue delete PROJ-123
# Sprint management
jira sprint add 42 PROJ-123 PROJ-456Capture a created key and act on it in the same script:
key=$(jira issue create -p PROJ -s "Automated task" -t Task | jq -r '.key')
jira issue move "$key" "In Progress" && jira issue assign "$key" meRun jira issue <verb> --help for the full flag set on any command.
| Command | Description |
|---|---|
jira auth |
Login, logout, status, whoami |
jira config |
Manage profiles (init, list, show, set, use, delete) |
jira context |
Set default filters (project, board, labels, etc.) |
jira issue |
Full issue lifecycle (list, view, create, edit, delete, assign, move, comment, link, clone) |
jira board |
List boards, view board issues |
jira sprint |
List, start, close sprints; add issues |
jira project |
List and view projects |
jira search |
JQL and full-text search |
jira user |
Search and get users |
jira me |
Show current user; me audit for daily activity |
jira mine |
List issues assigned to you |
jira open |
Open project or issue in browser |
jira ping |
Check connectivity to Jira |
Run jira <command> --help for details on any command.
Config lives at ~/.config/jira-cli/config.yml. You normally don't edit it by hand — use the jira config and jira context commands.
Manage multiple Jira instances:
jira config init --profile work --base-url https://work.atlassian.net
jira auth login --profile work --server https://work.atlassian.net \
--email you@work.com --token YOUR_TOKEN
jira config use work # switch default
jira issue list --profile work # use a profile for one commandThese override config file values and are useful in CI/automation:
| Variable | Description |
|---|---|
JIRA_BASE_URL |
Jira server URL |
JIRA_TOKEN |
API token (bypasses OS keychain) |
JIRA_EMAIL |
User email |
JIRA_AUTH_TYPE |
Auth type: basic or pat |
JIRABOT_PROFILE |
Profile name to use |
Authentication and execution are decoupled. A human or CI process runs jira auth login once; the token is stored in the OS keychain, never on disk. An agent never sees, requests, or routes the credential — it inherits an already-authenticated environment, and access is revoked system-side without any model trust.
jira context set --project PROJ --board-id 42 writes default parameters to disk, restricting the default operating scope without an agent having to append --project to every call. The context is explicit, inspectable state; breaking scope requires an explicit per-command override (e.g. --profile).
The CLI leans on standard tools (jq, rg, grep, wc) instead of a bespoke processing engine. stdout carries only the result; diagnostics, warnings, and errors go to stderr, so pipelines never choke on error output. Failed calls return a non-zero exit code, so success/failure is read from $? rather than parsed from text.
jira search jql "assignee = currentUser()" | jq -r '.[].key'
jira issue list | jq '[.[] | select(.status.name=="Done")] | length'
jira issue view CER-1 --raw | rg -o '"customfield_\d+"'Read paths (search jql, search text, issue list, mine, issue view) are non-mutating and return consistent, keyed JSON (key, fields.*). Custom-field projection keeps responses small so an agent retrieves only what it needs via repeatable --field/-F flags; issue view also keeps --fields as a comma-separated alias.
Rather than running a Model Context Protocol server, this is a standard binary: no long-lived daemon, no open socket, no persistent state between executions. Every action is the exact execution string, so debugging is just re-running the command from shell history, and agents reuse shell operators (&&, ||, |, >) instead of learning a custom RPC protocol.
Verification rests on hard records, not transcripts. Shell history is a replayable record of what was attempted — every input is a command-line argument, not hidden state. jira me audit --date YYYY-MM-DD queries Jira's server-side changelog to reconstruct what actually mutated on a given day for the authenticated user, independent of local terminal state.