# Jira CLI

> 通过内置 Python CLI 直接调用 Jira Server/Data Center REST API v2，查询、创建、编辑、流转和删除 Issue、Epic、Sub-task、评论、附件、关联、Watcher、Vote 与 Worklog，并查询 Saved Filter、项目、字段、Board 和 Sprint。适用于需要可控地操作 Jira、保留 Jira wiki markup 并精确管理请求与输出的场景。

- Skill: `dcjanus/jira-cli` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add dcjanus/jira-cli`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dcjanus/jira-cli/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: DCjanus (https://skillmd.com/u/dcjanus)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/dcjanus/jira-cli

---


# Jira CLI

使用本 skill 自带的 `scripts/jira_cli.py`。当前契约面向 Jira Server/Data Center 8.20.6，不支持 Jira Cloud API v3 或 ADF。

## 首次配置

默认配置文件为 `~/.config/jira-cli/config.toml`。使用 `config set` 创建或修改配置：

```bash
./scripts/jira_cli.py config set --default-project SATOS
./scripts/jira_cli.py config set --server https://jira.example --prompt-token
./scripts/jira_cli.py config show
```

配置文件权限固定为 `0600`。已有配置只会在显式执行 `config set` 时更新。

环境变量和全局选项可以临时覆盖配置，但不会反写 TOML：

- `JIRA_SERVER`
- `JIRA_API_TOKEN`
- `JIRA_USERNAME`
- `JIRA_AUTH_TYPE`
- `JIRA_TIMEOUT`
- `JIRA_DANGEROUSLY_ALLOW_HTTP`
- `JIRA_DANGEROUSLY_DISABLE_TLS_VERIFICATION`

默认只允许 HTTPS 并校验 TLS。只有用户明确要求时才使用
`--dangerously-allow-http` 或 `--dangerously-disable-tls-verification`；前者会让凭据明文传输。
临时 token 只通过 `JIRA_API_TOKEN` 传入，不接受可能进入 shell history 和进程参数的
`--token`。

## 调用约定

全局选项放在子命令之前：

```bash
./scripts/jira_cli.py --json user me
./scripts/jira_cli.py --json issue get SATOS-261728
```

- `--json` 保留 Jira 原始响应结构，适合 Agent 和脚本处理。
- 普通模式提供 Rich 摘要或表格。
- 复杂正文优先写入临时文件，再用 `--description-file` 或 `--body-file` 传递。
- 如果正文文件由 Agent 为当前单次操作临时创建，且成功后不再复用，在同一次 shell
  调用中把最终写操作与 `rm -- <明确路径>` 用 `&&` 串行执行。写操作失败时保留文件，
  不要使用 `;` 无条件删除，也不要用变量、通配符或目录作为清理目标。用户提供的文件、
  附件和后续操作仍需复用的文件不得自动清理。
- 额外字段使用重复的 `--field KEY=JSON_OR_TEXT`。
- 涉及“我”、当前用户、负责人、报告人等身份语义时，先执行
  `./scripts/jira_cli.py --json user me` 获取当前 Jira 认证身份。不得根据会话称呼、
  记忆中的邮箱、本机用户名或其它系统身份猜测 Jira 账号；除非任务确有必要，
  回复和持久化内容中只使用完成操作所需的最少身份信息。

## Jira 正文格式

Issue Description、Comment 和 Worklog Comment 均按 Jira wiki markup 原样发送；CLI
不做正文格式转换。使用下面这组高频 Jira wiki markup：

```text
h3. 调查结论

*重要结论*、_需要强调_、{{inline_code}}

* 第一项
** 嵌套项

# 第一步
## 子步骤

||字段||值||
|状态|完成|

bq. 单段引用

{quote}
多段引用
{quote}

[RCA-SRE-22950|https://dems.example/task-centre/tasks/RCA-SRE-22950]
[^report.txt]
[~username]

{noformat}
保留原始格式，不解析 *强调* 等语法
{noformat}

{code:python}
print("hello")
{code}
```

链接使用 `[显示文本|URL]`。空行开始新段落，`\\` 强制换行，`----` 插入水平线；
使用反斜杠转义后续特殊字符。必要时可使用 `{panel:title=标题}...{panel}` 和
`!image.png|thumbnail!`。

Jira 管理员可以按字段配置 renderer，插件也可能增减宏。需要完整或实例特定语法时，
读取当前 Jira 的
`/secure/WikiRendererHelpAction.jspa?section=all`。

## 常用查询

```bash
./scripts/jira_cli.py server-info
./scripts/jira_cli.py user search user@example.com
./scripts/jira_cli.py project list
./scripts/jira_cli.py project versions SATOS
./scripts/jira_cli.py metadata fields
./scripts/jira_cli.py metadata issue-types
./scripts/jira_cli.py metadata create --project SATOS --type Task
./scripts/jira_cli.py filter favourites
./scripts/jira_cli.py filter get 157700
./scripts/jira_cli.py issue list --jql 'assignee = currentUser() ORDER BY updated DESC'
./scripts/jira_cli.py board list --project SATOS
./scripts/jira_cli.py sprint list 12345
```

状态名称依赖真实 Workflow。流转前先查合法 transition，不猜测：

```bash
./scripts/jira_cli.py issue transitions SATOS-261728
./scripts/jira_cli.py issue move SATOS-261728 'Mark as Done' \
  --field 'resolution={"name":"Done"}'
```

未封装的查询可使用只读逃生口；路径必须以 `rest/` 开头，不支持任意写请求：

```bash
./scripts/jira_cli.py api get rest/api/2/priority
./scripts/jira_cli.py api get rest/api/2/search --param 'jql=project = SATOS'
```

## Saved Filter

`filter favourites` 使用 Jira Server 8.20 的公开
`GET /rest/api/2/filter/favourite` 接口，只返回当前认证用户收藏且有权查看的 Filter；
结果不等于当前用户拥有的全部 Filter。当前版本没有列出 owned Filter 的公开 REST
接口；不得把 Favorite 结果按 owner 过滤后宣称为完整 owned 列表，也不要改用 Jira
内部接口或 HTML 抓取。

```bash
./scripts/jira_cli.py filter favourites
./scripts/jira_cli.py --json filter favourites --expand owner --expand sharePermissions
./scripts/jira_cli.py --json filter get 157700
./scripts/jira_cli.py issue list --jql 'filter = 157700'
```

`filter get` 返回当前用户有权查看的指定 Filter，包括其 JQL。要检索 Filter 对应的
Issue，继续使用 `issue list --jql 'filter = FILTER_ID'`，不在 CLI 中引入额外作用域
或层级展开语义。

## Issue 与 Epic

创建 Task 或 Sub-task：

```bash
./scripts/jira_cli.py issue create \
  --type Task \
  --summary '中文任务标题' \
  --description-file /tmp/description.txt \
  && rm -- /tmp/description.txt

./scripts/jira_cli.py issue create \
  --type Sub-task \
  --parent SATOS-261716 \
  --summary '中文子任务标题'
```

编辑、指派和 clone：

```bash
./scripts/jira_cli.py issue edit SATOS-261728 --summary '新的标题'
./scripts/jira_cli.py issue edit SATOS-261728 --set-label reviewed
./scripts/jira_cli.py issue assign SATOS-261728 'user@example.com'
./scripts/jira_cli.py issue clone SATOS-261728 --summary '复制后的标题' \
  --field customfield_10001='复制后的 Epic 名称'
```

`issue edit` 的 `--set-label`、`--set-component` 和 `--set-fix-version`
会替换对应字段的完整集合；追加或移除单项时应先读取 Issue，再显式提交完整目标集合。
clone 会依据 Jira create metadata 复制无默认值的必填字段；目标项目缺少可复用值时，
使用 `--field KEY=VALUE` 显式提供。

Epic 使用配置中的 `epic_name_field` 和 `epic_link_field`：

```bash
./scripts/jira_cli.py epic create --summary 'Epic 标题' --epic-name '简短名称'
./scripts/jira_cli.py epic add SATOS-100 SATOS-101 SATOS-102
./scripts/jira_cli.py epic remove SATOS-100 SATOS-101 SATOS-102
```

`epic add` 不会默认覆盖 Issue 已有的其他 Epic 归属；确认需要移动时显式传
`--allow-move`。`epic remove` 的第一个参数是预期 Epic，只有当前归属匹配时才会移除。

## 评论与链接

评论正文遵循上面的 Jira wiki markup 规则；外部资源使用带简洁显示文本的链接。

```bash
./scripts/jira_cli.py comment add SATOS-261728 --body-file /tmp/comment.txt \
  && rm -- /tmp/comment.txt
./scripts/jira_cli.py comment edit SATOS-261728 22384028 --body-file /tmp/comment.txt
./scripts/jira_cli.py comment list SATOS-261728
```

Issue link 和外部链接分别使用 `link`、`remote-link`：

```bash
./scripts/jira_cli.py link types
./scripts/jira_cli.py link add SATOS-1 SATOS-2 --type Relates
./scripts/jira_cli.py link delete 123 --inward-issue SATOS-1 \
  --outward-issue SATOS-2 --yes
./scripts/jira_cli.py remote-link add SATOS-1 --title 'MR !38' --url https://git.example/mr/38
./scripts/jira_cli.py remote-link upsert SATOS-1 --global-id mr-38 \
  --title 'MR !38' --url https://git.example/mr/38
```

## 附件、Watcher、Vote 与 Worklog

```bash
./scripts/jira_cli.py attachment add SATOS-1 /tmp/evidence.txt
./scripts/jira_cli.py attachment delete SATOS-1 123 --filename evidence.txt --yes
./scripts/jira_cli.py watcher list SATOS-1
./scripts/jira_cli.py watcher add SATOS-1 'user@example.com'
./scripts/jira_cli.py vote get SATOS-1
./scripts/jira_cli.py worklog add SATOS-1 --time-spent 30m --comment '排查问题'
```

Worklog 默认使用 `--adjust-estimate leave`，不会隐式修改 Remaining Estimate。
需要联动估时时可显式选择 `auto`、`new` 或 `manual`；`new` 搭配
`--new-estimate`，新增时的 `manual` 搭配 `--reduce-by`，删除时搭配
`--increase-by`。

使用各命令的 `--help` 查看完整参数。

## 删除与写操作安全

- 创建 Issue 前先搜索是否已有重复任务；除非用户明确要求，不自行创建 Jira 任务。
- 用户要求分配给自己或以自己身份填写字段时，必须通过 `user me` 解析当前 Jira
  认证身份后再写入，不得从其它上下文推断；用户明确指定其他 Jira 用户时，使用
  `user search` 核实目标账号。
- 修改、流转或删除前先回读目标，确认 Issue key 和当前状态。
- Issue、评论、附件、关联和 Worklog 的删除均要求显式 `--yes`。
- 删除存在 Sub-task 的 Issue 默认失败；只有明确接受级联时才加 `--delete-subtasks`。
- 测试写操作只使用本次新建且带唯一测试前缀的临时资源，结束后逆序删除并逐项回查 404。
- 不在命令、日志或回复中输出完整 token；`config show` 只展示遮蔽值。

