Agent Integration
jr is designed from the ground up for AI agents and LLM-powered tools. Every command returns structured JSON on stdout, errors as JSON on stderr, and semantic exit codes — so agents can parse, branch, and retry reliably.
Why jr for Agents
| Property | How jr handles it |
|---|---|
| Output format | JSON on stdout, always. Errors on stderr, always. No mixed output. |
| Error handling | Semantic exit codes (0-7) — branch without parsing error messages |
| Token efficiency | --preset, --fields + --jq reduce 10,000-token responses to ~50 tokens |
| Workflow commands | jr workflow commands accept simple flags — no JSON body construction needed |
| Discovery | jr schema for runtime command discovery — no hardcoding needed |
| Batch operations | jr batch runs multiple commands in a single process |
| Security controls | Per-profile operation allowlist/denylist, batch limits, audit logging |
| 600+ commands | Auto-generated from Jira's OpenAPI spec, always up to date |
Runtime Discovery
Agents should use jr schema to discover commands dynamically rather than hardcoding command names:
# Get all resources (compact overview)
jr schema
# List just resource names
jr schema --list
# See all verbs for a resource
jr schema issue
# Get full flag reference for one operation
jr schema issue getTIP
Always use jr schema to discover the exact command name and flags before running an unfamiliar operation. Command names come from Jira's API and can be verbose (e.g., search-and-reconsile-issues-using-jql).
Discovery flow for agents
1. jr schema --list → pick the right resource
2. jr schema <resource> → pick the right verb
3. jr schema <resource> <verb> → see required flags
4. jr <resource> <verb> --flags... → executeWorkflow Commands
jr workflow provides high-level commands that accept simple flags instead of raw JSON bodies. These resolve human-friendly names to Jira IDs automatically — agents don't need to look up transition IDs, account IDs, sprint IDs, or link type IDs.
# Transition by status name
jr workflow transition --issue PROJ-123 --to "Done"
# Assign by name, email, or "me"/"none"
jr workflow assign --issue PROJ-123 --to "me"
# Transition + assign in one step
jr workflow move --issue PROJ-123 --to "In Progress" --assign me
# Add a comment (plain text, auto-converted to ADF)
jr workflow comment --issue PROJ-123 --text "Fixed in latest deploy"
# Create issue from flags (no raw JSON)
jr workflow create --project PROJ --type Bug --summary "Login broken" --priority High
# Link issues by type name
jr workflow link --from PROJ-1 --to PROJ-2 --type blocks
# Log work with human-friendly duration
jr workflow log-work --issue PROJ-123 --time "2h 30m" --comment "Debugging"
# Move issue to sprint by name
jr workflow sprint --issue PROJ-123 --to "Sprint 5"TIP
Workflow commands save significant tokens compared to constructing raw JSON bodies. For example, jr workflow create replaces a multi-line --body JSON payload with simple flags.
Watch for Changes
jr watch polls Jira on an interval and emits change events as NDJSON (one JSON object per line). This enables autonomous agents to monitor Jira without repeated manual searches.
# Poll a JQL query every 30 seconds
jr watch --jql "project = PROJ AND updated > -5m" --interval 30s
# Watch a single issue
jr watch --issue PROJ-123 --interval 10s
# Use a preset for output shaping
jr watch --jql "status changed" --interval 1m --preset triage
# Stop after N events
jr watch --jql "project = PROJ" --max-events 10Event types:
initial— emitted on first poll for all matching issuescreated— new issue appeared in resultsupdated— issue'supdatedtimestamp changedremoved— issue no longer matches the query
Each event is one JSON line: {"type":"updated","issue":{...}}
Use Ctrl-C (SIGINT) to stop gracefully. Auth errors (exit code 2) cause immediate termination; transient errors retry with backoff.
Token Efficiency
Jira responses can be enormous. A single issue response can consume ~10,000 tokens. Several mechanisms solve this:
Output presets
Use --preset to select a predefined set of fields without remembering --fields + --jq combos:
# Agent-friendly preset: key, summary, status, assignee, type, priority
jr issue get --issueIdOrKey PROJ-123 --preset agent
# Detailed preset: includes description, comments, subtasks, links
jr issue get --issueIdOrKey PROJ-123 --preset detail
# Triage preset: key, summary, status, priority, created, updated, reporter
jr issue get --issueIdOrKey PROJ-123 --preset triage
# Board preset: key, summary, status, assignee, sprint, story points, type
jr issue get --issueIdOrKey PROJ-123 --preset board
# List all available presets
jr preset listManual field filtering
For custom field combinations, use --fields and --jq:
# --fields: server-side filtering (tell Jira what to return)
# --jq: client-side filtering (reshape the JSON locally)
# Without filtering: ~10,000 tokens
jr issue get --issueIdOrKey PROJ-123
# With filtering: ~50 tokens
jr issue get --issueIdOrKey PROJ-123 \
--fields key,summary,status \
--jq '{key: .key, summary: .fields.summary, status: .fields.status.name}'WARNING
Always use --preset or --fields + --jq to minimize output. A single unfiltered issue can consume 10,000+ tokens of an agent's context window.
Caching repeated reads
Use --cache to avoid redundant API calls for stable data:
# Cache project list for 5 minutes
jr project search --cache 5m --jq '[.values[].key]'Batch Operations
When an agent needs multiple Jira calls, use jr batch to run them in a single process invocation:
echo '[
{"command": "issue get", "args": {"issueIdOrKey": "PROJ-1"}, "jq": ".key"},
{"command": "issue get", "args": {"issueIdOrKey": "PROJ-2"}, "jq": ".fields.summary"},
{"command": "project search", "args": {}, "jq": "[.values[].key]"}
]' | jr batchOutput is an array of results:
[
{"index": 0, "exit_code": 0, "data": "PROJ-1"},
{"index": 1, "exit_code": 0, "data": "Fix login timeout"},
{"index": 2, "exit_code": 0, "data": ["PROJ", "TEAM", "OPS"]}
]Each result includes index, exit_code, and either data (on success) or error (on failure). This avoids spawning N subprocesses.
Error Handling for Agents
Errors are structured JSON on stderr. Branch on exit codes, not error messages:
| Exit Code | Error Type | Meaning | Agent Action |
|---|---|---|---|
| 0 | — | Success | Parse stdout as JSON |
| 1 | — | General error | Log and report to user |
| 2 | auth_failed | Auth failed (401/403) | Check token/credentials |
| 3 | not_found | Not found (404/410) | Verify issue key/resource ID |
| 4 | validation_error | Bad request (400/422) | Fix the request payload |
| 5 | rate_limited | Rate limited (429) | Wait retry_after seconds, then retry |
| 6 | conflict | Conflict (409) | Fetch latest and retry |
| 7 | server_error | Server error (5xx) | Retry with backoff |
Example error response:
{
"error_type": "not_found",
"status": 404,
"message": "Issue Does Not Exist",
"hint": "",
"request": {"method": "GET", "path": "/rest/api/3/issue/BAD-999"}
}TIP
For exit code 5 (rate limited), the error JSON includes a retry_after field with the number of seconds to wait.
Skill Setup
jr ships with a skill file that teaches AI agents everything on this page automatically. The skill follows the Agent Skills open standard, supported by 30+ tools.
See the Skill Setup Guide for installation instructions for Claude Code, Cursor, VS Code Copilot, OpenAI Codex, Gemini CLI, Goose, Roo Code, and more.
Troubleshooting
"command not found"
jr is not installed. Install via:
brew install sofq/tap/jr # Homebrew
go install github.com/sofq/jira-cli@latest # GoExit code 2 (auth)
Token expired or misconfigured. Verify with:
jr raw GET /rest/api/3/myselfIf this fails, generate a new API token at https://id.atlassian.com/manage-profile/security/api-tokens.
Unknown command
Command names are auto-generated from Jira's API and can be verbose. Use jr schema to find the right name:
jr schema --list # find the resource
jr schema <resource> # find the verbOr use jr raw as an escape hatch for any API endpoint.
Large responses filling context
DANGER
Always use --preset or --fields + --jq to minimize output. A single unfiltered issue can consume 10,000+ tokens of an agent's context window.
Dry-run before mutations
Use --dry-run to preview what jr will send without making the API call:
jr issue edit --issueIdOrKey PROJ-123 \
--body '{"fields":{"summary":"New title"}}' \
--dry-run