jcli
A command-line client for Jira, Jira Service Management (JSM), and Confluence.
Note: This is AI slop but it is well tested and ready for daily use.
Requires Go 1.27.1 or newer. Install with Go, or run makepkg -si from this
checkout on Arch Linux:
go install codefloe.com/Honken/jcli@latest
jcli init
init prompts for credentials and writes jcli/config.json in the user config
directory: normally ~/.config/jcli on Linux, respecting XDG_CONFIG_HOME.
Alternatively, create the file yourself:
{
"site": "mysite.atlassian.net",
"email": "user@example.com",
"token_file": "~/.config/jcli/token",
"defaults": {
"board": "751"
}
}
Create an Atlassian API token,
put it in the token file, and restrict that file to your user (chmod 600).
An inline token is also supported. For a scoped token, add "scoped": true
at the top level or set JIRA_SCOPED=true; jcli cannot detect token scope.
OAuth client credentials are not supported.
Environment variables override configuration:
| Variable |
Config field |
JIRA_SITE |
site (hostname only) |
JIRA_EMAIL |
email |
JIRA_CLOUD_ID |
cloud_id (discovered if omitted) |
JIRA_SCOPED |
scoped |
JIRA_TOKEN |
token |
JIRA_TOKEN_FILE |
token_file |
Credential precedence is JIRA_TOKEN, JIRA_TOKEN_FILE, configured token,
then configured token_file. Only the selected file is read; an empty or
unreadable file fails rather than falling back. Changing JIRA_SITE clears
the configured cloud ID unless JIRA_CLOUD_ID is supplied. Discovered IDs
are saved only for the site in the config file.
Boards default to statusCategory != Done. Set defaults.board_filter to
customize this. If an older jcli init saved status NOT IN (Done, Canceled),
remove that entry to use the current default; explicit overrides are preserved.
New config files do not pin the board-filter default.
Service-desk features
Add these instance-specific values under defaults for JSM forms:
{
"sd_projects": ["SD"],
"checklist_field": "customfield_10038",
"forms_count_field": "customfield_10062"
}
sd_projects selects the service-desk comment API (internal by default).
Checklist commands need only checklist_field and the HeroCoders Checklist
plugin; forms commands require all three settings. Other commands work without them.
Commands
Use jcli <command> --help for flags and details. Global flags go before
the command: --json, --debug, --version/-v, --help/-h.
Command flags can appear before or after positional arguments; -- ends flag parsing.
--debug logs requests and their bodies to stderr, including field contents.
jcli KEY Shorthand for view KEY --full
jcli view KEY [--full] [--meta] Issue details and optional related data
jcli search [flags] [TEXT] Search issues
jcli count [flags] [TEXT] Approximate issue count
jcli recent [--days N] [--all] Recent activity; default: assigned to you, last day
jcli create -p PROJECT -s SUMMARY Create an issue (default type: Task)
jcli edit KEY [flags] Edit fields
jcli transition KEY STATUS [flags] Change status; accepts transition name, ID, or destination
jcli transitions KEY Available transitions and required fields
jcli comment KEY [TEXT | -] Add a comment; --public for customer-visible SD replies
jcli comments KEY [--full] List comments
jcli checklist KEY [ACTION TARGETS...] List or update checklist items
jcli children KEY [--limit N] List child issues
jcli link KEY TYPE KEY [--if-absent] Link issues
jcli unlink KEY TYPE KEY [--if-present] Remove an issue link
jcli links KEY List issue and remote links
jcli attach KEY FILE Upload an attachment
jcli attachments KEY List attachments
jcli download KEY FILE [-o OUTPUT] Download by exact filename
jcli history KEY [--field NAME] Field-change history
jcli watch KEY Add yourself as a watcher
jcli unwatch KEY Remove yourself as a watcher
jcli watchers KEY List watchers
jcli url KEY Print the browser URL
jcli board [--filter JQL] [ID] Board issues; ID defaults to config
jcli sprints [--state STATE] [ID] Board sprints; state defaults to active
jcli projects List visible projects
jcli project PROJECT [--type TYPE] Project details and create-field hints
jcli components PROJECT List components
jcli users QUERY Search by name or email
jcli forms KEY Shorthand for forms view KEY
jcli forms list KEY List attached forms
jcli forms view KEY Read form answers
jcli forms raw KEY [FORM-ID] Upstream form JSON in an envelope; default: first form
jcli init Configure credentials
jcli schema Command tree and help as JSON
Confluence
Page references accept numeric IDs or Confluence URLs. Hierarchy commands
also accept folders.
jcli wiki REF Shorthand for wiki page REF
jcli wiki search [-s SPACE] QUERY Search pages
jcli wiki page REF [--html] Read a page; --html shows storage XHTML
jcli wiki pages SPACE List pages, newest modification first
jcli wiki spaces List spaces
jcli wiki children REF [--depth N] Descendant tree (default depth: 2)
jcli wiki parents REF Ancestors, nearest parent first
jcli wiki siblings REF Other pages and folders with the same parent
jcli wiki create --space S --title T Create a page; optional --parent REF
jcli wiki edit REF [flags] Replace body, rename, or set version message
jcli wiki link KEY REF Link a Jira issue to a page
jcli wiki unlink KEY [LINK-ID] Remove a remote link; ID optional if only one exists
jcli wiki labels REF List labels
jcli wiki label REF LABEL Add a label
jcli wiki unlabel REF LABEL Remove a label
jcli wiki attachments REF List attachments
jcli wiki download REF FILE Download by exact filename; supports -o and --overwrite
jcli wiki upload REF FILE Upload an attachment
Common tasks
Search and count
jcli search -a @me -s '!Done'
jcli search -p ACME,AJAX --components Platform,Backend
jcli search --priority '!Low,Lowest' --limit 10
jcli search -p ACME --page-token TOKEN
jcli count --jql 'project = ACME AND priority = Highest'
Filters: -a assignee, -p project, -s status, -t type, -l labels,
--priority, and --components. Except for assignee, filters accept
comma-separated values (OR) and a leading ! (exclude). The special case
-s '!Done' excludes every status in the Done category, regardless of its name
(case-insensitive shorthand). This also excludes statuses such as "In production"
if they belong to that category. -s Done and -s '!Done,Blocked' remain literal
status-name filters. Raw --jql overrides both filters and text.
Search returns one page (default 25); reuse the same query with the returned
cursor to continue. --keys prints one key per line, or a JSON array under
data. Count makes one request and is approximate: text output starts with
~, and JSON includes meta.approximate: true.
Edit and validate
jcli project ACME --type Task
jcli view ACME-1044 --meta
jcli edit ACME-1044 --summary 'New title' --due 2026-10-01
jcli edit ACME-1044 --assignee @me --priority High
jcli edit ACME-1044 --labels infra,monitoring --add-component Platform
jcli edit ACME-1044 --set-json 'customfield_10030={"value":"Foo"}'
jcli transition ACME-1044 Resolve --set-json 'resolution={"name":"Done"}'
Use --assignee @none to unassign. --labels and --components replace
whole lists; --add-label, --remove-label, --add-component, and
--remove-component change individual entries. --type and --parent
change the issue type and parent.
--set FIELD=VALUE always supplies a string; --set-json FIELD=JSON
preserves JSON types. Both are repeatable. Generated examples use POSIX-shell
quoting and avoid CSV flags when they would change a value.
Create checks required fields; edit checks field IDs and allowed values;
transition checks required screen fields. Metadata lookup failures can skip
create/edit checks with a warning. Type changes skip edit validation because
the current type's fields may not apply. Jira still validates the submitted request.
Use project --full for every create field, project --type TYPE for one
issue type, or view --meta for editable fields and transitions.
Text input
Comments and wiki create/edit accept text arguments, --body-file FILE, or
stdin (- or a pipe). Do not combine a body file with text arguments.
Descriptions use create -d or edit --description.
| Input |
Jira comments/descriptions |
SD comments |
Wiki bodies |
| Plain text |
ADF paragraph |
Unchanged |
Storage XHTML |
| Markdown |
ADF |
Plain text |
Storage XHTML |
| ADF JSON |
ADF |
Plain text |
Storage XHTML |
| Storage XHTML |
Not a supported markup input |
Not a supported markup input |
Passed through |
The markdown subset includes headings, emphasis, links, code, lists, task
markers, blockquotes, tables, and hard breaks. Conversion and fallback notices
appear on stderr in text mode or in JSON warnings; unchanged plain text
needs no notice. Descriptions can force --description-format text|markdown|adf
instead of the default auto.
Wiki input starting with < is treated as storage XHTML. Unsupported markdown
images and autolinks return conversion errors; use storage XHTML for those.
Inspect an existing body with jcli wiki page REF --html before replacing it.
Checklists, links, and downloads
jcli checklist ACME-1019 done 2 5-8 11
jcli checklist ACME-1019 skip 'headset'
jcli checklist ACME-1019 open 3
jcli link ACME-1 blocks ACME-2 --if-absent
jcli download ACME-1019 report.pdf -o ./report.pdf
Checklist actions are done, open, progress, skip, and wontdo.
Targets can mix numbers, ranges, and case-insensitive text matches; text
selects the first matching item.
A blocks B means A blocks B; A "is blocked by" B reverses it. Type names
use their outward label. Unlink and duplicate checks use the same direction;
symmetric links match either orientation. Older versions reversed link creation;
existing links are not corrected automatically.
comment --if-absent skips matching text by you from the last minute, ignoring
whitespace differences. Hidden author emails prevent a confirmed match.
Skipped mutations succeed and report JSON action: "skipped".
Both Jira and wiki downloads publish complete files atomically. Without
--overwrite, existing paths—including symlinks or files created during the
download—are never replaced. This requires filesystem hard-link support.
JSON output
jcli --json view ACME-1 | jq '.data.fields.summary'
jcli --json search -p ACME | jq '.data[].key'
jcli --json count -p ACME | jq '.data'
Command results use a versioned envelope. Success goes to stdout; errors go
to stderr with a nonzero exit status. Help, setup prompts, and errors parsing
global flags are not JSON results. schema and forms raw always emit JSON
on success; use --json for their errors too.
{
"version": "2",
"command": "search",
"data": [],
"pagination": {"has_more": false}
}
error identifies failure. Optional fields are action, key, pagination,
meta, and warnings. data is omitted for errors and mutations without a
payload—not set to null. Lists are arrays, including empty lists.
| Command |
data |
view, edit, create |
Raw Jira issue |
view --meta |
{issue, edit_meta, transitions} |
view --full, bare KEY |
{issue, links, checklist, forms, comments, children}; --meta adds metadata |
project |
{project, statuses, create_meta}; --type omits statuses |
wiki page, wiki edit, wiki create |
Raw page (--html affects text output only) |
forms raw |
Raw form; absent when no forms are attached |
search |
Issue array, or key strings with --keys |
count, url |
Number, URL string |
sprints |
{sprints, issues}; issues populated only for one sprint with --state=active |
wiki children/parents/siblings |
Rows with depth, id, title, type, and optional spaceId, lastModified |
| Other list commands |
Arrays of command-specific records |
| Other mutations |
Omitted; use action, key, and optional meta |
schema |
{version, commands}; this inner version is the binary version |
Hierarchy depth measures distance from the queried node: downward for
children, upward for parents, zero for siblings.
Errors include kind, retryable, message, and an optional hint.
Kinds: not_found, auth, rate_limited, server, client, network,
validation, cancelled. Treat unknown kinds as non-retryable client errors.
Field validation can add meta.missing_fields, meta.unknown_fields, or
meta.invalid_values. Records identify the field and may include its type,
allowed values, a set_example, and the invalid supplied value.
Warnings include conversions, skipped checks, failed optional fetches, and
detected truncation. recent retains issues with failed history fetches if
others succeeded, adding meta.partial: true and meta.failed_issue_keys.
Cancellation or failure of every history fetch is an error. sprints reads
all matching pages and fails on pagination errors.
Automatic retries apply only to reads, including POST search/count. Writes
are sent once. An uncertain write failure sets retryable: false and may
mean the change succeeded; inspect Jira before repeating it. API read redirects
must preserve method and origin; writes are not redirected. Downloads may
follow HTTPS redirects, permanently stripping credentials after leaving the
original origin.
Development
make build
make test
make vet
The Makefile derives the version from Git; override with make build VERSION=x.y.z.
Go's encoding/json/v2 support is required; no GOEXPERIMENT setting is needed
with the declared toolchain. make lint also runs staticcheck when installed.
Tests use local fixtures and HTTP test servers, not a live Atlassian account.