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). Uses acli when no visibility restriction is needed. 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.