--- name: jira-cli description: Drive Jira (Cloud or Data Center) from the terminal via the `jira` CLI (ankitpokhrel/jira-cli) — search issues with JQL, view/create/edit/transition/comment/assign/worklog issues, and list epics/sprints/boards/projects. Use whenever a task needs to read or change Jira issue state and the `jira` CLI is installed and authenticated. Every mutating command is shown in full and confirmed before it runs; credentials come only from environment variables, never handled or printed directly. --- # Jira via `jira-cli` This skill wraps the `jira` binary from [`ankitpokhrel/jira-cli`](https://github.com/ankitpokhrel/jira-cli). It's the pick over Atlassian's own official `acli` because `acli` is **Jira Cloud only**, and this skill also has to work against Jira Data Center/Server — `jira-cli` covers both, with non-interactive env-var-only auth that fits a headless agent far better than `acli`'s stdin-piped-token-or-browser-OAuth flow. Known gaps: no dedicated "list transitions available on this issue" command short of running `move` interactively, no attachment upload/download, no dashboard/filter management (all of which `acli` does have — if your Jira turns out to be Cloud-only, `acli` is worth a second look for those). Other tools install a binary that is also called `jira` (go-jira, for one), and its commands differ. Everything below is for `ankitpokhrel/jira-cli`. If a command here isn't recognised, check which tool is on `PATH` before assuming the instance is broken. Output from your organisation's Jira can be sensitive. Keep it, and every credential, out of version control: never commit either into a repository. [story-enrich](../story-enrich/SKILL.md) and [spec-to-stories](../spec-to-stories/SKILL.md) use this skill when they reach Jira, and keep their own approval step on top of the confirmations below. ## 0. Setup / auth check — run this before anything else 1. Confirm the binary exists and check its version: ```powershell jira --version ``` If this fails with "command not found," stop and report that `jira-cli` isn't installed — do not install it yourself unless explicitly asked. (Install methods, if asked: on Windows, `scoop bucket add extras` and then `scoop install jira-cli`; also `brew tap ankitpokhrel/jira-cli` and `brew install jira-cli`, `go install github.com/ankitpokhrel/jira-cli/cmd/jira@latest`, or a binary from the [releases page](https://github.com/ankitpokhrel/jira-cli/releases).) 2. Confirm required environment variables are set — **check presence, never print the value**: ```powershell if ($env:JIRA_BASE_URL -and $env:JIRA_API_TOKEN) { "env present" } else { "env MISSING" } ``` (bash equivalent, if ever running under Git Bash/WSL instead of PowerShell: `[ -n "$JIRA_BASE_URL" ] && [ -n "$JIRA_API_TOKEN" ] && echo "env present" || echo "env MISSING"`) Required variables (placeholders below — never substitute a real value into a command or file): - `JIRA_BASE_URL` — the site's base URL. `jira-cli` actually stores this inside the config file generated by `jira init`, not as a live env var read on every call, but this skill still requires it be set in the shell so a fresh `jira init` (or a config-file regeneration) never has to ask interactively mid-run. - `JIRA_API_TOKEN` — Cloud: an Atlassian API token. Data Center: either the account's login password (`basic` auth, the default) or a Personal Access Token (only when `JIRA_AUTH_TYPE=bearer` is also set). - `JIRA_EMAIL` — Cloud only, the account email tied to the API token. - `JIRA_AUTH_TYPE` — unset/`basic` (default) or `bearer` (PAT, Data Center) or `mtls` (client certs, Data Center). - `JIRA_CONFIG_FILE` — optional, only needed when more than one Jira instance/project is configured on this machine; points at the specific generated config YAML to use for this run. 3. Confirm the CLI is actually authenticated against the configured instance with a cheap read: ```powershell jira me ``` Success prints the authenticated account's display name. Anything else (see § 5 Common failure signatures) means auth is broken — stop and report before attempting any other command. 4. If no config exists yet (`jira init` was never run on this machine), that's a **setup step, not a query** — say it's needed and which installation type to pick (`Cloud` or `Local` for Data Center/Server) rather than running `jira init` unattended, since it's interactive by design. ## 1. Read commands (safe to run freely, no confirmation needed) Every placeholder is quoted, so a command can be pasted into PowerShell as it stands: a bare `` as its own argument is a parse error there. | Purpose | Command | |---|---| | JQL search, JSON output | `jira issue list --jql "project = AND status = 'In Progress'" --raw` | | My open issues | `jira issue list -a$(jira me) --raw` | | View one issue | `jira issue view ""` | | View with recent comments | `jira issue view "" --comments 5` | | List projects I can see | `jira project list` | | List boards in a project | `jira board list` | | List sprints (explorer) | `jira sprint list --table` | | Current active sprint's issues | `jira sprint list --current --raw` | | List epics | `jira epic list --table` | | Issues inside one epic | `jira epic list "" --raw` | | Release/version list | `jira release list --project ""` | | Who am I | `jira me` | `-a$(jira me)` uses PowerShell's `$(...)` subexpression operator, which works the same way here as bash's command substitution — no rewrite needed. Notes: - `--raw` is this CLI's JSON flag (not `--json`/`-o json` — confirm you're using `--raw`, the other spellings silently fall through to the default interactive/table view on this CLI). - `--plain` gives a script-friendly flat table (no JSON) — use it only when a human is going to read the terminal output directly; prefer `--raw` for anything this skill itself will parse. - Every list command defaults to an interactive TUI when stdout is a terminal. Always pass `--raw` or `--plain` when running non-interactively (which this skill always is) so the process doesn't hang waiting for keyboard input. ## 2. Pagination `jira-cli`'s list commands do not take an explicit `--page`/`--cursor` flag; they page internally and return what matches the filters up to their own defaults. To bound a large result set instead of paging through it: - Narrow with JQL (`--jql`) — add date ranges (`created`, `updated`), status, or project filters server-side rather than pulling everything and filtering client-side. - `sprint list`, `epic list`, etc. are already scoped to a specific sprint/epic ID, which keeps result sets small by construction. - If a query is still too large, split it by an additional JQL predicate (date window, assignee, status) rather than assuming a page-size flag exists — there isn't one. ## 3. Mutating commands — show the command and get a yes, every time **Before running anything in this section, state in plain language exactly what it will change** (issue key, field, old → new value) **and get an explicit go-ahead in the same turn.** This CLI has no built-in `--dry-run` flag, so that confirmation step is on this skill, not the tool: state the intended command verbatim, get the go-ahead, then run it — never run first and ask forgiveness. Ask it with the harness's question tool ([why](../../docs/ask-with-the-question-tool.md)): two options, "Run it" (describe the exact effect) and "Don't run it" (nothing changes), neither preselected. Only a picked "Run it" runs it; a dismissed picker, no answer or a run with no one to ask means don't. With no question tool, ask a numbered list that accepts the number or any clear wording, never an exact phrase. This is in addition to, not instead of, Copilot's own shell-command approval prompt — the command itself will still need to clear that separately. | Purpose | Command | Confirm before running | |---|---|---| | Create issue | `jira issue create -tBug -s"" -yHigh -lbug -b"" --no-input` | Project, type, summary, and the fact that it creates a new issue | | Edit fields | `jira issue edit "" -s"" --no-input` | Which fields change and to what | | Transition | `jira issue move "" ""` | Source status (if known) → target status; that the workflow allows it is not guaranteed until it runs | | Transition + comment/assignee/resolution | `jira issue move "" Done -RFixed -a$(jira me)` | Every side-effect the one command bundles, not just the status change | | Assign | `jira issue assign "" ""` | The issue and the new assignee (or `x` to unassign, `default` for project default) | | Add comment | `jira issue comment add "" ""` | The exact text, and whether `--internal` should be set | | Add worklog | `jira issue worklog add "" "" --comment "" --no-input` | Duration and issue — worklogs are rarely correctable after the fact | | Link two issues | `jira issue link "" "" ""` | Both keys and the link type/direction | | Unlink | `jira issue unlink "" ""` | That this removes an existing link, not just views it | | Clone | `jira issue clone "" -s""` | That this creates a NEW issue as a copy | | Delete | `jira issue delete ""` (add `--cascade` for subtasks) | This is destructive and usually not reversible via the CLI — confirm the key twice if `--cascade` is set | | Add issues to sprint | `jira sprint add "" "" ""` | Sprint ID (get it from `jira sprint list --table` first) and every issue key | | Add/remove issues on an epic | `jira epic add "" ""` / `jira epic remove ""` | Epic key and issue keys | Always pass `--no-input` on create/edit/worklog commands once the values are confirmed — without it the CLI drops into an interactive prompt that will hang a non-interactive session. ## 4. Output parsing - Prefer `--raw` on every read command that feeds back into this skill's logic; treat its output as JSON. In PowerShell, pipe it through `ConvertFrom-Json` to get objects (field names mirror Jira's REST API — `key`, `fields.summary`, `fields.status.name`, etc.): ```powershell jira issue list --jql "project = ABC" --raw | ConvertFrom-Json ``` - `jira issue view` has no `--raw` mode documented — treat its output as human-readable Markdown-ish text, not structured data, if it needs to be consumed programmatically. - `--plain --no-headers --columns ` is the right shape when a human-readable script needs specific columns without JSON (see the CLI's own README "Scripts" section for worked examples), but for anything this skill parses programmatically, `--raw` + `ConvertFrom-Json` is still preferred. ## 5. Common failure signatures | Symptom | Likely cause | What to do | |---|---|---| | `jira me` errors with 401/"unauthorized" | Token expired or wrong `JIRA_AUTH_TYPE` for the instance (e.g. using `basic` against a PAT-only Data Center setup) | Report it; do not attempt to regenerate a token yourself | | 403 on a specific project/issue | Account lacks permission on that project, or wrong site (`JIRA_BASE_URL` config points elsewhere) | Confirm the project key and the configured base URL before assuming it's a bug | | 404 on an issue/project key | Wrong project key, issue deleted, or key belongs to a different project the current config isn't scoped to | Re-check the key casing/prefix; list the project first | | 429 / "rate limit" | Too many requests too fast (bulk scripted loops) | Back off and retry with delay; don't tighten a retry loop against it | | `move` silently offers no matching transition | Target status name doesn't match a transition available from the issue's current status | Run `jira issue move ""` interactively once (human-supervised) to see the real transition list, or view the issue's current status first | | CLI drops into an interactive prompt and the process hangs | Missing `--no-input` (create/edit/worklog) or missing `--raw`/`--plain` on a list command | Add the missing flag; never leave a headless call able to wait on stdin | | "config not found" / `jira init` never run | No config file generated yet on this machine | Report as a setup gap, don't auto-run `jira init` (it's interactive) | | Values look stale after a known change | `JIRA_CONFIG_FILE` pointing at the wrong instance/project when more than one is configured | Check which config is active before assuming Jira itself is wrong | ## 6. Secrets rule - `JIRA_API_TOKEN`, and any password/PAT that stands in for it, come **from the environment only**. Never paste a token literally into a command line, a config file this skill writes, a log line, or a chat transcript. - Never print an env var's value to prove it's set — use a presence check (`if ($env:VAR) { ... }`), never `$env:VAR` alone or `Write-Output $env:VAR`. - `jira-cli` also supports `.netrc` and the OS keychain as token sources — if either is already how this machine is set up, don't override it with an inline env var; just confirm it resolves via `jira me`. - If a command ever needs a "paste the token here" flag as opposed to reading it from the environment or config, that's a sign the wrong invocation was picked — every credential this CLI needs has an env-var or config-file path; use that path, never a literal on the command line. ## Acceptance This checklist is for reviewing the skill itself and needs no live Jira instance. - [ ] `jira --version` is documented above as the install-check command, and its exact expected failure text ("command not found") is named. - [ ] An auth-check command (`jira me`) is documented, distinct from the install check, and its success/failure shapes are both described. - [ ] Every placeholder env var (`JIRA_BASE_URL`, `JIRA_API_TOKEN`, `JIRA_EMAIL`, `JIRA_AUTH_TYPE`, `JIRA_CONFIG_FILE`) is a placeholder — nothing here looks like a real hostname or token. - [ ] Every command in § 3 (mutating commands) has a "Confirm before running" note naming what to confirm — none run unconfirmed, and Copilot's own approval prompt is named as a second, separate layer rather than a substitute. - [ ] JSON output uses this CLI's actual flag (`--raw`), not an invented `--json`/`-o json` that this CLI doesn't support. - [ ] Pagination is addressed even though this CLI has no page/cursor flag — the narrowing-by-JQL strategy is stated, not silently skipped. - [ ] The failure-signature table covers at minimum: auth/401, 403, 404, 429, and a hung-process case specific to this CLI (missing `--no-input`/`--raw`). - [ ] Every command block is PowerShell-valid (no `[ -n ... ]`, no bare `echo $VAR`, every placeholder quoted); the one bash variant given is explicitly labeled as such. - [ ] Nothing here points outside the kit except the CLI's own public links; the only cross-links are relative links to sibling skills.