Jira¶
Client¶
client
¶
Jira client with acli-first delegation and REST API fallback.
Provides a JiraClient class that delegates to the Atlassian CLI
(acli) for operations it supports and falls back to the REST API
for gaps (changelog queries, visibility-restricted comments, custom
fields, attachment upload, ADF conversion).
When acli is not on PATH, all operations use the REST API.
Usage::
from agentic_ci.jira import JiraClient
client = JiraClient.from_env(url="https://myorg.atlassian.net")
ticket = client.get_issue("PROJ-123")
client.add_comment("PROJ-123", "Fixed in PR #42")
JiraError(message, status_code=None, response_text='')
¶
JiraClient(url, email, token, *, timeout=30)
¶
Jira client that delegates to acli where possible.
On init, checks for acli on PATH. If available, write
operations (create, edit, transition, assign, comment, link)
and search/view use acli subprocess calls. Read operations
that need ADF conversion, changelog queries, custom fields,
or visibility-restricted comments fall back to the REST API.
Source code in src/agentic_ci/jira/client.py
from_env(url=None)
classmethod
¶
Create a client from environment variables.
Reads JIRA_URL, JIRA_EMAIL (or JIRA_USER), and
JIRA_API_TOKEN.
Raises RuntimeError if required variables are missing.
Source code in src/agentic_ci/jira/client.py
get_issue(key)
¶
Fetch a single issue with comments. Returns a normalised dict.
The returned dict has keys: key, summary, description
(plain text), issue_type, labels, status, project,
components, reporter_name, reporter_email, comments.
Source code in src/agentic_ci/jira/client.py
search(jql, *, max_results=500)
¶
Search issues by JQL. Returns a list of normalised dicts.
Source code in src/agentic_ci/jira/client.py
get_label_author(key, label)
¶
Find who most recently added a label via the changelog.
Returns {"found": True, "email": ..., "displayName": ...}
or {"found": False}.
Falls back to the ticket reporter if the label was set at creation time (no changelog entry).
Source code in src/agentic_ci/jira/client.py
get_description_editors(key)
¶
Return email addresses of all users who edited the issue description.
Walks the full changelog looking for description field changes. Returns a list of unique email addresses (may be empty if the description was never edited after creation).
Source code in src/agentic_ci/jira/client.py
get_custom_field(key, *field_names)
¶
Read custom fields by name. Returns {name: value}.
Source code in src/agentic_ci/jira/client.py
search_parent_epics(jql)
¶
Find parent Epic keys for issues matching a child JQL query.
Source code in src/agentic_ci/jira/client.py
add_comment(key, body, *, visibility_group=None)
¶
Post a comment, optionally restricted to a visibility group.
The body is plain text (with optional markdown markup, converted
to ADF for the REST API). Always uses the REST API because acli
comment create does not support --body-adf. Returns True
on success, False on failure.
Source code in src/agentic_ci/jira/client.py
update_comment(key, comment_id, body, *, visibility_group=None)
¶
Update an existing comment by ID.
Same ADF conversion and visibility semantics as add_comment.
Returns True on success, False on failure.
Source code in src/agentic_ci/jira/client.py
edit_labels(key, *, add=None, remove=None)
¶
Add and/or remove labels on an issue (atomic update).
Delegates to acli for add-only operations. Falls back to
REST API for remove or mixed add+remove (acli edit --labels
replaces; the REST API supports atomic add/remove).
Source code in src/agentic_ci/jira/client.py
transition(key, status)
¶
Transition an issue to a new status by name.
Source code in src/agentic_ci/jira/client.py
assign(key, assignee)
¶
Assign an issue to a user by account ID or email.
Source code in src/agentic_ci/jira/client.py
create_issue(project, issue_type, summary, *, description='', parent_epic='', **extra_fields)
¶
Create a new issue. Returns the issue key.
Uses acli for simple creates (no extra_fields). Falls back to REST API when Epic Link or extra fields are needed.
Source code in src/agentic_ci/jira/client.py
link_issues(source, target, link_type)
¶
Link two issues with a named link type.
Source code in src/agentic_ci/jira/client.py
attach_file(key, filepath)
¶
Attach a file to an issue.
Source code in src/agentic_ci/jira/client.py
set_security_level(key, level_name)
¶
Set the security level on an issue by level name.
Fetches available security levels for the issue, matches level_name (case-insensitive), and sets it via the REST API.
Raises JiraError if the level name is not found.
Source code in src/agentic_ci/jira/client.py
set_custom_field(key, field_name, value)
¶
Set a custom field by name.
Consults the field schema to determine value format: text/string
fields get the raw string, option/select fields get {"value": v}.
If the value is valid JSON, it is sent as-is.
Source code in src/agentic_ci/jira/client.py
ADF Conversion¶
adf
¶
Atlassian Document Format (ADF) conversion utilities.
Converts between plain text (with markdown markup) and ADF, the JSON document format used by Jira Cloud REST API v3 for rich-text fields.
text_to_adf(text)
¶
Convert plain text with markdown markup to Atlassian Document Format.
Handles:
- lang ... fenced code blocks -> codeBlock nodes
- {expand:Title}...{expand} -> expand nodes (collapsible sections)
- ---- or --- on a line by itself -> rule nodes (horizontal dividers)
- # through ###### headings -> heading nodes
- - bullets -> bulletList nodes
- bold and italic inline markup
- URLs -> inlineCard nodes
- Double newlines split paragraphs, single newlines become hardBreak
Source code in src/agentic_ci/jira/adf.py
adf_to_text(adf)
¶
Extract plain text from an ADF document.
Source code in src/agentic_ci/jira/adf.py
acli Wrapper¶
acli
¶
Atlassian CLI (acli) wrapper for agentic-ci.
Downloads the acli binary if not already on PATH, handles authentication, and provides a subprocess runner for acli commands.
AcliError(message, returncode=1, stderr='')
¶
is_available()
¶
ensure_acli(dest='/usr/local/bin/acli')
¶
Download acli if not already on PATH. Returns absolute path to binary.
Source code in src/agentic_ci/jira/acli.py
setup_auth(site=DEFAULT_SITE)
¶
Authenticate acli using JIRA_EMAIL + JIRA_API_TOKEN env vars.
Source code in src/agentic_ci/jira/acli.py
run_acli(*args, json_output=False, check=True)
¶
Run an acli command.
If json_output is True, appends --json to the command.
If check is True, raises AcliError on non-zero exit.