jira-atlassian-cli

star 2

Load before any Jira interaction or mcp__atlassian__* call. Holds required field values (team ID, sprint IDs, custom field mappings) unavailable elsewhere; skipping causes missing fields on created tickets. Triggers: Jira URLs, ticket keys (e.g. ABC-123), MCP tools mcp__atlassian__*, acli, or keywords create/read/edit/search/transition, ticket, Jira, issue, sprint, backlog, refinement, work item.

carlos-algms By carlos-algms schedule Updated 6/3/2026

name: jira-atlassian-cli description: | Load before any Jira interaction or mcp__atlassian__* call. Holds required field values (team ID, sprint IDs, custom field mappings) unavailable elsewhere; skipping causes missing fields on created tickets. Triggers: Jira URLs, ticket keys (e.g. ABC-123), MCP tools mcp__atlassian__*, acli, or keywords create/read/edit/search/transition, ticket, Jira, issue, sprint, backlog, refinement, work item.

Jira Tools

Team Context (load before any Jira call)

Private team values live in ~/OneDrive/work/contexts/jira-data.yaml.

  • Read the ENTIRE file once per session. No partial reads; risks missing my_team (pointer to default team key) or mixing cross-team values.
  • Resolve team: user-named team, else top-level my_team field.
  • Substitute placeholders from resolved team: {cloud_id}, {project_key}, {team_field}, {team_id}, {sprint_field}, {refinement_sprint_id}, {active_sprint_board_id}.
  • File missing: ask user. Do not guess. End turn.

Active (current) sprint is NOT constant

  • {refinement_sprint_id} is stable; the active sprint id rotates every sprint.

  • Never hardcode the active sprint id. Resolve it at runtime.

  • {active_sprint_board_id} is the constant agile board; sprints are its children. Resolve the active sprint from the board in one call (verified):

    acli jira board list-sprints --id {active_sprint_board_id} --state active --json
    

    Read id from the single active sprint. Flag is --id (the board id), not --board. The command is board list-sprints; there is no acli jira sprint list.

  • Do NOT use the old search+view path (customfield_10020 is rejected in a search --fields list and stripped from search results). The board endpoint replaces it.

Tool Priority

Prefer MCP (mcp__atlassian__*) for everything. Supports markdown (contentFormat: "markdown") and full ADF (contentFormat: "adf"). Pass cloudId: "{cloud_id}" on all MCP calls.

Use acli only when MCP cannot do the job, or not available (bulk ops, advanced JQL export, edge cases).

MCP Create/Edit Notes

  • contentFormat: "adf" required when description contains acceptance criteria or any checklist. Jira markdown renders - [ ] as literal text in bullets, not checkboxes. Only taskList/taskItem ADF nodes produce real checkboxes.

  • contentFormat: "markdown" for simple descriptions (headings, bold, lists, links, no checklists).

  • contentFormat: "adf" for full control (checkboxes, panels, complex layouts). Pass ADF JSON as the description string.

  • Always set team and sprint via additional_fields:

    "additional_fields": {
      "{team_field}": "{team_id}",
      "{sprint_field}": {refinement_sprint_id}
    }
    
  • Epic link: "parent": "{project_key}-NNN".

  • API response body is unreliable for verifying rendering. Create/edit/get responses echo description as markdown even when sent or requested as ADF (including responseContentFormat: "adf"); ADF features (checkboxes, expand, panels) appear flat in the echo but render correctly in Jira. Verify by opening the issue URL.

  • expand IS valid in issue ADF. editJiraIssue INVALID_INPUT almost always means a structural error in a sibling node, not expand. Most common: paragraph-wrapped taskItem.content (see ADF Reference).

ADF Reference (for contentFormat: "adf")

Pass ADF JSON as the description value.

Canonical docs: https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/. Per-node spec at .../document/nodes/<nodeName>/.

Debugging a node

  1. Fetch the node spec for allowed content, attrs, marks:

    defuddle parse \
      "https://developer.atlassian.com/cloud/jira/platform/apis/document/nodes/<nodeName>/" \
      --md
    
  2. Still rejected: isolate by replacing the suspect subtree with a minimal valid node (e.g. taskList -> bulletList). Success means the suspect is the cause. MCP errors usually name the offending node.

  3. MCP-stricter-than-spec traps:

    • taskItem.content: bare inline nodes, NOT paragraph-wrapped. Same rule inside expand. See atlassian/atlassian-mcp-server#25 (also confirms markdown - [ ] does NOT auto-convert to taskList; send ADF).
    • tableCell / tableHeader / panel / expand: block-node children only; wrap raw text in paragraph.

ADF Node Types

Use inside description.content:

  • Paragraph: { "type": "paragraph", "content": [{ "type": "text", "text": "..." }] }

  • Heading: { "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "..." }] }

  • Code block: { "type": "codeBlock", "attrs": { "language": "tsx" }, "content": [{ "type": "text", "text": "..." }] }

  • Bullet list: { "type": "bulletList", "content": [{ "type": "listItem", "content": [{ "type": "paragraph", "content": [...] }] }] }

  • Ordered list: { "type": "orderedList", "content": [/* same as bulletList */] }

  • Bold text: { "type": "text", "text": "...", "marks": [{ "type": "strong" }] }

  • Inline code: { "type": "text", "text": "...", "marks": [{ "type": "code" }] }

  • Task list (checkboxes): { "type": "taskList", "attrs": { "localId": "ac-1" }, "content": [{ "type": "taskItem", "attrs": { "localId": "ac-1-1", "state": "TODO" }, "content": [{ "type": "text", "text": "..." }] }] }

    Acceptance criteria MUST use taskList/taskItem, not bulletList. taskItem.content MUST be bare text nodes; paragraph-wrapping is rejected with INVALID_INPUT (see Debugging trap above).

  • Expand (collapsible section): { "type": "expand", "attrs": { "title": "..." }, "content": [<block nodes>] } Children: block nodes (paragraph, bulletList, heading, etc.). Use to hide long context (findings, traces) below the fold.

  • Inline smart link (renders as Jira card / Notion preview / any URL): { "type": "inlineCard", "attrs": { "url": "https://{cloud_id}/browse/{project_key}-NNN" } } Use inside a paragraph content array. Works for any URL.

  • Panel (info/warning/error/success callout):

    {
      "type": "panel",
      "attrs": { "panelType": "info" },
      "content": [
        {
          "type": "paragraph",
          "content": [{ "type": "text", "text": "Note text here" }]
        }
      ]
    }
    

    panelType: info, note, warning, error, success.

  • Table:

    {
      "type": "table",
      "attrs": { "isNumberColumnEnabled": false, "layout": "default" },
      "content": [
        {
          "type": "tableRow",
          "content": [
            {
              "type": "tableHeader",
              "content": [
                {
                  "type": "paragraph",
                  "content": [{ "type": "text", "text": "Header" }]
                }
              ]
            }
          ]
        },
        {
          "type": "tableRow",
          "content": [
            {
              "type": "tableCell",
              "content": [
                {
                  "type": "paragraph",
                  "content": [{ "type": "text", "text": "Cell" }]
                }
              ]
            }
          ]
        }
      ]
    }
    

    Header row: tableHeader. Data rows: tableCell. Cell content: block nodes (wrap text in paragraph).

  • Horizontal rule: { "type": "rule" }

  • Link mark: { "type": "text", "text": "click here", "marks": [{ "type": "link", "attrs": { "href": "https://..." } }] }

Issue Linking (Blocks, Relates, etc.)

Use MCP mcp__atlassian__createIssueLink. Available link types:

Name Inward (passive) Outward (active)
Blocks is blocked by blocks
Relates relates to relates to
Duplicate is duplicated by duplicates
Fixes is fixed by fixes
Improves is improved by improves

Directionality for "Blocks": inwardIssue = blocker, outwardIssue = blocked. A blocks B:

inwardIssue: "{project_key}-A", outwardIssue: "{project_key}-B", type: "Blocks"

Subtasks

MCP create with issueTypeName: "Sub-task" and parent: "{project_key}-NNN". Same parent field as epic linking; issue type determines subtask vs. epic child.

Sprint Field

{sprint_field} takes a plain integer, not an object:

"{sprint_field}": {refinement_sprint_id}

NOT { "id": ... } (errors with "Number value expected as the Sprint id").

Active (current) sprint vs refinement

jira-data.yaml holds refinement_sprint_id and active_sprint_board_id. The current/active sprint id is NOT in the yaml and changes every sprint. To target the active sprint (user says "current sprint", "this sprint", "not refinement"), resolve it live from the board in one call:

acli jira board list-sprints --id {active_sprint_board_id} --state active --json then read id from the single active sprint. Use that integer as {sprint_field} in the create JSON.

Parent / Epic Linking

MCP create: "parent": "{project_key}-NNN". MCP edit (mcp__atlassian__editJiraIssue): fields: {"parent": {"key": "{project_key}-NNN"}}.

acli Fallback Reference

Use acli jira workitem subcommands only when MCP cannot do the job.

Quick Reference

Substitute {project_key} before running.

Operation Command
View acli jira workitem view {project_key}-N
View (all) acli jira workitem view {project_key}-N --fields '*all' --json
Search acli jira workitem search --jql "project = {project_key} AND ..." --limit 20
Create acli jira workitem create --project {project_key} --type Task --summary "..."
Create ADF acli jira workitem create --from-json file.json
Edit acli jira workitem edit --key {project_key}-N --summary "New title"
Transition acli jira workitem transition --key {project_key}-N --status "In Progress"
Comment acli jira workitem comment create --key {project_key}-N --body "..."

acli pitfalls (verified)

  • acli jira workitem edit prompts y/N; pass --yes for non-interactive runs.
  • No acli jira me. Get your accountId via JQL: acli jira workitem search --jql "assignee = currentUser()" --limit 1 --json, read fields.assignee.accountId.
  • Assignee on create: nest inside additionalAttributes as "assignee": { "accountId": "..." }.
  • Active sprint id: resolve at runtime (see "Active (current) sprint" above); never reuse {refinement_sprint_id} for it.

acli JSON Template (for --from-json)

Assignee on create: add assignee.accountId inside additionalAttributes. There is NO acli jira me. Get your own accountId via: acli jira workitem search --jql "assignee = currentUser()" --limit 1 --json then read fields.assignee.accountId.

{
  "additionalAttributes": {
    "{team_field}": "{team_id}",
    "{sprint_field}": {refinement_sprint_id},
    "assignee": { "accountId": "712020:..." }
  },
  "parentIssueId": "{project_key}-NNN",
  "projectKey": "{project_key}",
  "summary": "Ticket title",
  "type": "Task",
  "description": {
    "type": "doc",
    "version": 1,
    "content": [
      {
        "type": "paragraph",
        "content": [{ "type": "text", "text": "Description here." }]
      }
    ]
  }
}

acli Edit JSON Format

Uses "issues" (array of keys) instead of "projectKey":

{
  "issues": ["{project_key}-N"],
  "description": {
    "type": "doc",
    "version": 1,
    "content": [...]
  }
}

Ticket framing (summary = deliverable, not motivation)

The summary and description must describe the WORK to be done, not the event that prompted it. A PR, bug sighting, or incident is the MOTIVATION; it belongs in the description as context, never as the headline.

  • Wrong: "Remove duplicate handler type in chat-embedded (PR #2100)" — that frames a one-off PR fix when the actual deliverable was a review-rule enhancement.
  • Right: "Enhance AI-review duplication rule: same-app scope + near-dup detection" — names the durable change; the PR is one motivating line in the body.

Before creating: ask "what is the lasting deliverable?" If the summary names a PR/ticket/incident as its subject, rewrite it to name the change instead.

Common Workflows

Read a ticket from a URL

Extract key from URL (e.g. {project_key}-NNN from https://{cloud_id}/browse/{project_key}-NNN), then MCP mcp__atlassian__getJiraIssue with issueIdOrKey: "{project_key}-NNN".

Search team tickets

MCP mcp__atlassian__searchJiraIssuesUsingJql, or fallback:

acli jira workitem search --jql "project = {project_key} AND status = 'New'" --limit 20
acli jira workitem search --jql "project = {project_key} AND sprint = {refinement_sprint_id}" --limit 50
Install via CLI
npx skills add https://github.com/carlos-algms/dotfiles --skill jira-atlassian-cli
Repository Details
star Stars 2
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
carlos-algms
carlos-algms Explore all skills →