Date: 2026-05-26 Inputs reviewed:
docs/jira-cli-prd.md- Sibling Rust CLIs in
..:xero-cli,posthog-cli,slack-cli,keito-cli,reddit-cli,semrush-cli,otterai-cli,ms-teams-cli,todoist-agent-cli,wordpress-cli - Current Atlassian Jira Cloud REST v3 and GitHub Actions documentation
Build jira-cli as a Rust 2021 single-crate application with a public library target, matching the xero-cli shape (src/api, src/auth, src/cli, src/config, src/output, src/rate_limit, src/models). Keep the module boundaries strict enough that a future workspace split is easy, but avoid a multi-crate workspace until the ADF codec, MCP server, or generated API layer justify it.
The first shippable milestone should be agent-safe issue reads and comments, not full Jira coverage. That means: profile config, API token auth, secure token storage, typed client, /rest/api/3/search/jql cursor pagination, issue view, issue list, issue body, comment add/list, stable JSON/JSONL output, deterministic errors, and no hidden stdin reads. OAuth, full ADF round-tripping, bulk operations, Agile APIs, cache, and MCP should follow in that order.
The PRD is directionally strong. Its core differentiation remains valid: Jira Cloud v3-native, ADF-native, cursor pagination, profile-aware, and agent-friendly. The implementation should preserve those points, but adjust a few details before coding.
-
OAuth 3LO base URLs need explicit handling. For OAuth access tokens, Atlassian requires calls through
https://api.atlassian.com/ex/jira/{cloudid}/..., after resolving accessible resources. Direct site URLs such ashttps://example.atlassian.net/rest/api/3/...remain suitable for API-token/basic auth. Model this as anApiBaseenum, not string concatenation scattered through endpoint modules. -
Treat PKCE as an auth spike, not an assumption. The PRD says OAuth 3LO PKCE, but the current Atlassian 3LO docs still describe authorization-code exchange with a client secret. Confirm whether Atlassian supports public/native PKCE for this app type before committing the UX. If not, support either a product-owned distributable 3LO app or user-provided
client_id/client_secret. -
Build the scope catalogue from endpoint metadata. The PRD mixes broad classic scopes (
read:jira-work,write:jira-work) with current granular scopes. Store both, but makeauth doctorendpoint-aware and able to explain the exact missing granular scope when Jira returns 401/403. -
Keep ADF lossless as a goal, but implement it in tiers. Start with a robust CommonMark/GFM subset plus raw ADF preservation for unknown nodes. Full bidirectional fidelity across Jira panels, statuses, mentions, media, tables, and custom macros should be gated by snapshot corpus quality.
-
Defer a hand-written full JQL parser. Start with macro expansion (
@me,@open,@current-sprint) plus server-side validation and helpful error mapping. Add a parser once the field catalogue/cache exists, otherwise the validator will drift. -
MCP should be a post-core feature. It should wrap proven CLI/API operations rather than drive the initial architecture. Design output schemas now so MCP can reuse them later.
xero-cli is the best architectural base: single binary plus lib, clap derive, reqwest with rustls, keyring, miette/thiserror, comfy-table, SQLite cache, rate-limit modules, and a simple dispatch layer. Reuse that shape, but improve the parts that matter for agents.
posthog-cli and slack-cli have better agent-output patterns: JSON/JSONL output, TTY-based auto format, response envelopes (ok, data, meta), field projection, deterministic JSON errors, and pagination helpers. Jira should standardize on an envelope for all machine-readable output.
keito-cli has the strongest CI/release posture: workflow concurrency, minimal permissions, pinned action SHAs, release smoke tests, generated manpage verification, a release gate before asset builds, semver tag validation, and explicit Homebrew/Scoop publishing. Use this as the new workflow baseline.
posthog-cli and reddit-cli show the cargo-dist route. Because the PRD requires multi-platform releases, checksums, installers, and Homebrew automation, use cargo-dist for release artifact planning, but wrap it with Keito-style gates and smoke tests.
Older sibling workflows using actions/checkout@v4, actions/cache@v4, actions/upload-artifact@v4, or taiki-e/upload-rust-binary-action are acceptable historical context, not the recommended baseline for a new repo in 2026.
jira-cli/
|-- Cargo.toml
|-- Cargo.lock
|-- build.rs
|-- LICENSE
|-- README.md
|-- CHANGELOG.md
|-- SECURITY.md
|-- .github/
| |-- dependabot.yml
| `-- workflows/
| |-- ci.yml
| |-- release.yml
| `-- release-version-guard.yml
|-- docs/
| |-- jira-cli-prd.md
| |-- jira-cli-implementation-plan.md
| |-- auth.md
| |-- adf-spec.md
| |-- agent-integration.md
| |-- jql-cookbook.md
| |-- recipes.md
| `-- schema/
|-- src/
| |-- main.rs
| |-- lib.rs
| |-- error.rs
| |-- cli/
| |-- api/
| |-- auth/
| |-- config/
| |-- output/
| |-- adf/
| |-- jql/
| |-- cache/
| |-- rate_limit/
| |-- models/
| `-- mcp/
`-- tests/
|-- cli_smoke.rs
|-- auth_flow.rs
|-- issue_search.rs
|-- pagination.rs
|-- adf_roundtrip.rs
`-- fixtures/
Use clap derive with a thin main.rs:
- parse CLI
- initialize logging based on global flags/env
- convert top-level args into
GlobalArgs - dispatch to command modules
- map
JiraCliErrorto exit codes and JSON/text diagnostics
Global flags should include --profile, --site, --output, --fields, --no-input, --no-color, --quiet, --verbose, --dry-run, --debug-curl, --config, --log, --log-format, --page-size, --max, and --all.
Use aliases sparingly but intentionally:
jira searchas alias forjira issue list --jqljira issue body KEYfor markdown/plain body extractionjira me issuesfor common agent use
Config files:
- user config: platform-specific config dir, using
dirs - local override:
.jira.tomlor.jira/config.toml, discovered upward fromcwd - env overrides:
JIRA_PROFILE,JIRA_SITE,JIRA_OUTPUT,JIRA_CONFIG,JIRA_TOKEN,JIRA_EMAIL
Profile model:
pub struct Profile {
pub name: String,
pub site: String,
pub cloud_id: Option<String>,
pub auth_method: AuthMethod,
pub email: Option<String>,
pub default_project: Option<String>,
pub default_board: Option<u64>,
pub default_fields: Option<Vec<String>>,
pub scopes: Vec<String>,
}Selection precedence:
- command flags
- environment
- local repo config
- user config active profile
- first configured profile
Implement auth in this order:
- Environment token override, never persisted.
- Scoped API token with email, suitable for CI and initial agent workflows.
- Keyring-backed token storage per profile.
- Encrypted file fallback, if keyring is unavailable.
- OAuth 3LO after a spike confirms PKCE/client-secret constraints.
- Legacy unscoped token/basic auth with deprecation warnings.
Token storage must never place bearer tokens or API tokens in plain TOML. Config may reference token source metadata only.
OAuth profile data must include cloud_id, resolved through accessible-resources, and the API client must use the Atlassian gateway base URL for OAuth calls.
Centralize HTTP behavior in api::client::JiraClient:
reqwest::Clientwithrustls-tls, timeouts, gzip, multipart- auth injection by
AuthContext - API base construction by auth mode
- request redaction before logging
--debug-curlrendering with secrets redactedRetry-Afterparsing as seconds or HTTP date- 429 and 5xx retry with jitter
- Jira error body parsing into typed errors
- response metadata capture for rate-limit headers
Endpoint modules should expose typed methods and models for known fields, but keep a serde_json::Value escape hatch for custom fields.
Search must use GET/POST /rest/api/3/search/jql, not deprecated /rest/api/3/search. For CLI consistency, prefer POST internally and support nextPageToken, isLast, maxResults, fields, expand, and reconcileIssues.
Make machine-readable output stable from day one:
{
"ok": true,
"data": {},
"meta": {
"schema_version": "1",
"profile": "work",
"site": "example.atlassian.net",
"request_id": "...",
"rate_limit": {}
}
}Formats:
auto: table on TTY, JSON on non-TTYjson: pretty unless--compactjsonl: one envelope or data object per line for streaming liststablecsvtsvyamlplaintemplate=<expr>
Errors in JSON mode:
{
"ok": false,
"error": {
"code": "auth_scope_missing",
"message": "...",
"hint": "Add scope read:issue-details:jira"
}
}Initial ADF scope:
- strongly typed
AdfDocument,AdfNode,AdfMark - parse/render paragraphs, headings, lists, links, code spans, code blocks, blockquotes, rules, tables, status, mentions
- preserve unknown nodes in a loss-tolerant representation
adf::to_plainforjira issue body --plainadf::to_markdownfor display/editor flowsadf::from_markdownfor create/edit/comment input
Testing must drive this module. Add fixtures before broad endpoint work.
Phase 1:
- macro expansion
- quote-safe builder for internally generated queries
- server-side validation endpoint/error mapping
- unbounded query warnings
Phase 2:
- field-aware lexer/parser
- typo suggestions from field cache
- miette spans
Use SQLite once core issue commands are stable:
- fields and custom field schema
- projects/components/versions
- users
- transition graphs
- board/sprint metadata
Use cache for dynamic completions and for custom field name resolution. Provide jira cache info and jira cache clear.
Tasks:
- Add
Cargo.toml,Cargo.lock,build.rs,src/main.rs,src/lib.rs. - Add modules with empty boundaries and smoke tests.
- Add MIT
LICENSE,README.md,CHANGELOG.md,SECURITY.md. - Add lints: forbid unsafe, warn clippy all, release profile with strip/LTO/codegen-units.
- Add
jira --version, help text, and completions.
Acceptance:
cargo fmt --all -- --checkcargo clippy --all-targets --all-features -- -D warningscargo test --all-targets --all-featuresjira --help,jira --version, and completions work.
Tasks:
- Add
.github/workflows/ci.ymlwith format, clippy, test matrix, audit, release-smoke. - Add concurrency and least-privilege permissions.
- Pin actions by full commit SHA with version comments.
- Add Dependabot or Renovate for Cargo and GitHub Actions updates.
- Add release workflow skeleton with semver tag validation and pre-release gate.
Acceptance:
- CI passes on Ubuntu, macOS, and Windows.
- Release smoke builds the binary, runs
--version, runs--help, and verifies generated manpages. - No release job has write permissions unless it publishes.
Tasks:
- Implement
JiraCliErrorwith PRD exit codes. - Implement JSON error output and text/miette diagnostics.
- Implement
OutputFormat, TTY detection, JSON envelope, JSONL, table, CSV, TSV, YAML, plain. - Implement
--fieldsprojection with dotted paths. - Implement config load/merge precedence and profile commands.
- Implement no-input behavior and tests that prove no stdin read occurs unless explicitly requested.
Acceptance:
- Non-TTY defaults to JSON and no color.
- TTY defaults to table.
--no-inputis honored globally.- Every error type maps to the expected exit code.
Tasks:
- Implement
auth setup-token,auth status,auth list,auth logout,auth migrate. - Store tokens by profile in keyring.
- Add encrypted file fallback if keyring fails.
- Support
JIRA_TOKEN,JIRA_TOKEN_<PROFILE>,JIRA_EMAIL, and--token-file. - Reject plain token fields in config with a migration hint.
Acceptance:
- Token setup/status/logout work with temp config dirs.
- Env tokens override stored tokens and are not persisted.
- File fallback is permission-restricted and encrypted.
- Secret redaction is covered by tests.
Tasks:
- Implement
JiraClientwith request methods and typed error mapping. - Implement rate limiter and retry middleware.
- Implement
api::pagination::SearchPagerovernextPageTokenandisLast. - Add
--debug-curlwith redacted auth. - Add wiremock fixtures for 200, 400, 401, 403, 404, 429, 5xx, and multi-page search.
Acceptance:
- No code path calls deprecated
/rest/api/3/search. - Multi-page search follows all
nextPageTokenvalues untilisLast. - 429 honors
Retry-After. - Failed Jira responses become structured CLI errors.
Tasks:
- Implement
issue view KEY. - Implement
issue list --jql,search,--all,--max,--page-size. - Implement
issue body KEYand--description. - Implement field projection and stable issue normalization.
- Implement macros:
@me,@open,@mine,@updated-today.
Acceptance:
- Agents can fetch ticket context with one JSON command.
--all -o jsonlstreams without loading all issues into memory.issue bodyreturns Markdown/plain without table chrome.- Snapshot tests cover table, JSON, JSONL, CSV, TSV, YAML, and plain output.
Tasks:
- Add ADF data model.
- Implement Markdown to ADF for common blocks and marks.
- Implement ADF to Markdown/plain for descriptions and comments.
- Add corpus fixtures from real Jira ADF samples.
- Add editor temp-file flow with frontmatter.
Acceptance:
- 100 fixture round-trips are snapshotted.
- Unknown nodes are preserved or surfaced without data loss.
- Editor flow aborts safely on parse errors and leaves recovery files.
Tasks:
- Implement
issue create,edit,delete. - Implement
comment add/list/view/update/delete. - Implement comment visibility flags.
- Implement transition listing and
issue transition. - Implement assignment and issue links.
Acceptance:
- All mutation commands support
--dry-run. - Every command has JSON output and structured partial error details.
- Wiremock tests cover validation errors and permission failures.
Tasks:
- Implement worklog CRUD.
- Implement attachment upload/download/list/delete, including
X-Atlassian-Token: no-check. - Implement project, version, component, user, group, and field reads.
- Add custom-field schema helpers.
Acceptance:
- Multipart attachment upload is tested.
- Field cache can map human names to
customfield_*IDs. - Project defaults are used by
issue create.
Tasks:
- Implement bulk transition/edit/assign/comment/link/delete.
- Add concurrency limits and JSONL progress events.
- Add dry-run affected-set preview.
- Add partial failure summaries.
Acceptance:
- Bulk commands continue on per-issue failures.
- Exit code reflects max severity.
- Non-TTY progress is machine-readable JSONL.
Tasks:
- Implement board, sprint, and epic commands.
- Implement
@current-sprintresolution through board/profile context. - Add SQLite cache for fields, projects, users, transitions, boards, sprints.
- Add dynamic completions backed by cache.
Acceptance:
- Sprint resolution is deterministic; if board is ambiguous, it errors with candidates.
- Cache invalidation and clear/info commands are tested.
Tasks:
- Complete PKCE/client-secret spike.
- Implement OAuth login, refresh, logout, status, and site selection.
- Resolve and store
cloud_idfrom accessible resources. - Use Atlassian gateway URLs for OAuth profiles.
- Add
auth doctorfor scope/site mismatch.
Acceptance:
- OAuth flow is wiremock-tested.
- Refresh token rotation is handled.
- Profile switching between token-auth and OAuth-auth sites works.
Tasks:
- Implement
jira mcp serve --stdio. - Expose curated tools: search, get issue, create issue, transition, comment, assign, worklog, link, current sprint.
- Generate JSON schemas from shared request types.
- Add MCP fixtures and protocol tests.
Acceptance:
- MCP tools return the same normalized issue/comment objects as CLI JSON.
- Failures use machine-readable error codes.
Tasks:
- Add cargo-dist metadata and release workflow.
- Publish GitHub Releases with checksums.
- Publish Homebrew formula to
osodevops/homebrew-tap. - Publish to crates.io if package naming is available.
- Add container image for CI usage.
- Add docs: auth, ADF, recipes, JQL cookbook, agent integration, migration guide.
Acceptance:
- Release artifacts exist for macOS arm64/x86_64, Linux gnu/musl where feasible, Windows x86_64.
brew install osodevops/tap/jira-cliworks after release.- Docs include copy-pasteable Codex/Claude/Cursor integration examples.
Use current local Keito-style security and current GitHub guidance:
permissions: contents: readfor CI.permissions: contents: writeonly in release jobs that create releases or tags.permissions: packages: writeonly for GHCR publishing.concurrency.group: ${{ github.workflow }}-${{ github.ref }}andcancel-in-progress: truefor CI.cancel-in-progress: falsefor release.- Pin actions by SHA with version comments.
- Keep third-party action count low.
- Never use
pull_request_target. - Do not expose secrets to PR workflows.
- Use unique artifact names per matrix target.
CI jobs:
fmt:cargo fmt --all -- --checkclippy:cargo clippy --all-targets --all-features -- -D warningstest: matrixubuntu-latest,macos-latest,windows-latestrelease-smoke:cargo build --release,jira --version,jira --help, generated manpage checksaudit:cargo auditci-complete: verifies required jobs succeeded
Release jobs:
pre-release-gate: semver tag validation, fmt, clippy, tests, audit, smoke.plan: cargo-dist plan.build-assets: target matrix, checksums, unique artifact names.create-release: create/update GitHub release.publish-crates: optional, after dry-run.publish-homebrew: update tap.publish-container: optional GHCR image.release-complete: verifies required release jobs.
Unit tests:
- config precedence
- output projection
- error code mapping
- JQL macro expansion
- ADF node conversion
- token redaction
- retry delay parsing
Wiremock integration tests:
- auth token setup/status
- OAuth code exchange and refresh
- accessible resources
- issue search pagination
- issue view/create/edit/delete
- comments/worklogs/attachments
- 401/403/404/429/5xx mapping
CLI tests:
- help/version/completions
- no-args help
- non-TTY output defaults
--no-inputno-hang behavior- JSON and JSONL schemas
- dry-run output
Snapshot tests:
- table formatting
- normalized issue JSON
- error JSON
- ADF Markdown output
Property/fuzz tests:
- Markdown to ADF to Markdown stability
- JQL lexer/parser once implemented
- custom field value builders
Optional live tests:
- behind
JIRA_LIVE_TESTS=1 - use a dedicated test Jira site/project
- never run in PR CI
ADF round-trip complexity is the highest product risk. Mitigate with a real corpus, explicit supported-node matrix, and raw unknown-node preservation.
OAuth 3LO details may not match the PRD's PKCE assumption. Mitigate with an early spike and by shipping API-token auth first for CI/agent workflows.
Jira scopes drift over time. Mitigate with an endpoint scope catalogue and tests that compare known command requirements against the current pinned endpoint metadata.
Custom fields are unpredictable. Mitigate by caching field schema, supporting raw JSON escape hatches, and adding focused builders only for common types.
Release target complexity can slow v1. Mitigate by using cargo-dist for artifact planning and keeping manual Homebrew/Scoop code small and tested.
Agent safety is easy to regress. Mitigate with CI tests that run commands with closed stdin/non-TTY stdout and fail on prompts, colors, or spinners.
Week 1:
- Phase 0 and Phase 1
- basic config/output/error foundations
Week 2:
- API-token auth
- API client
- search pagination fixtures
Week 3:
issue view,issue list,issue body- JSON/JSONL/table/csv/tsv/yaml/plain output
Week 4:
- ADF v1
- comments
- issue create/edit
Week 5:
- transitions, assignment, links, worklogs, attachments
- project/field metadata
Week 6:
- bulk operations
- cache
- Agile API
Week 7:
- OAuth 3LO
- auth doctor
- release automation
Week 8:
- MCP
- docs
- hardening, live test pass, beta release
If time tightens, cut MCP, bulk, and Agile from v1.0 and keep the first public release focused on reliable issue/search/comment workflows.
- Bootstrap the Rust repo and CI.
- Implement config/output/error/no-input behavior before any Jira API calls.
- Implement API-token auth and token storage.
- Implement Jira client and
/search/jqlpagination with wiremock fixtures. - Ship the first vertical slice:
jira issue view,jira issue list,jira issue body, andjira comment add/listwith stable JSON.
- Atlassian Jira Cloud REST API v3 issue search:
https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/ - Atlassian Jira Cloud REST API v3 issues and ADF fields:
https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/ - Atlassian Jira Cloud REST API v3 intro and ADF support:
https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/ - Atlassian OAuth 2.0 3LO apps:
https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/ - GitHub Actions Rust guide:
https://docs.github.com/en/actions/tutorials/build-and-test-code/rust - GitHub Actions secure use reference:
https://docs.github.com/en/actions/reference/security/secure-use - GitHub Actions workflow syntax:
https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax - GitHub workflow artifacts docs:
https://docs.github.com/en/actions/tutorials/store-and-share-data