
# CLI

`acahti` 是 `<instance>/acahti/v1` 的轻量客户端，与 MCP 使用同一套目录。IDE Agent（Codex、Cursor、Claude Code）继续使用 [MCP](mcp.md)，不需要这个包。shell Agent、流水线任务和在终端操作的人使用 CLI。

## 安装

这个包以私有形式发布在 GitHub Packages（`npm.pkg.github.com`），不在 registry.npmjs.org 上。先让 npm 通过 `acahti-ai` 组织的认证，再安装：

```bash
echo '@acahti-ai:registry=https://npm.pkg.github.com' >> ~/.npmrc
# 具有 acahti-ai read:packages 权限的 GitHub PAT
echo '//npm.pkg.github.com/:_authToken=GITHUB_TOKEN' >> ~/.npmrc
npm install -g @acahti-ai/acahti
```

包内带有 macOS 和 Linux 在 amd64 与 arm64 上的二进制文件。`acahti version` 打印版本号。CLI 通过 `/acahti/v1` API 与任意实例通信，独立发版（`cli-vX.Y.Z` 标签）。每位成员使用自己的 GitHub 凭据；不要提交或共享令牌。

## 配置

只需两个值：实例地址和访问令牌。

```bash
export ACAHTI_ROOT_URL=https://acahti.example.com
export ACAHTI_TOKEN=…
acahti whoami
```

令牌由本人在头像菜单 →「访问令牌」中创建；只显示一次，删除即吊销。流水线任务中这两个变量已经存在。CLI 以 `Authorization: Bearer` 发送令牌，从不发送账号密码。

如果想保存下来而不是每次 export：

```bash
acahti auth login --hostname https://acahti.example.com --with-token < token.txt
```

该命令用 `GET /me` 校验令牌，并把两个值保存到 `~/.config/acahti/credentials.json`（权限 `0600`）。`acahti auth login https://acahti.example.com` 只打印配置指引，不打开浏览器。在 Benchyard 中，把 ACAHTI_ROOT_URL 和 ACAHTI_TOKEN 设为 Secrets 后直接使用；不要把注入的占位值复制到凭据文件里。`acahti auth status` 显示主机和登录状态；`acahti auth logout` 删除该文件。

环境变量优先于已保存的凭据。既没有 `ACAHTI_ROOT_URL` 也没有已保存的主机时，CLI 使用名为 `acahti` 的 git remote 的 HTTPS 地址。

## 命令

| 命令 | REST |
|---|---|
| `acahti whoami` | `GET /me` |
| `acahti user list [--q TEXT]` | `GET /users` |
| `acahti user create LOGIN [--author NAME] [--email EMAIL] [--admin]` | `POST /users` |
| `acahti user update LOGIN [--author NAME] [--email EMAIL]` | `PATCH /users/{login}` |
| `acahti user permissions LOGIN [--create-team true\|false --create-repo true\|false]` | `GET`/`PUT /users/{login}/permissions` |
| `acahti team create\|delete NAME` | `POST /teams`、`DELETE /teams/NAME` |
| `acahti repo create NAME [--team TEAM]` | `POST /repos` |
| `acahti repo delete OWNER/REPO` | `DELETE /repos/OWNER/REPO` |
| `acahti repo move OWNER/REPO TEAM` | `PUT /repos/OWNER/REPO/team/TEAM` |
| `acahti repo archive\|unarchive OWNER/REPO` | `PUT /repos/OWNER/REPO/archived` |
| `acahti pr list OWNER/REPO [--state open]` | `GET /repos/OWNER/REPO/pulls` |
| `acahti pr view OWNER/REPO N` | `GET …/pulls/N` |
| `acahti pr create OWNER/REPO --title T --head BRANCH [--base B] [--body B]` | `POST …/pulls` |
| `acahti pr merge OWNER/REPO N` | `POST …/pulls/N/merge` |
| `acahti pr close OWNER/REPO N` | `DELETE …/pulls/N` |
| `acahti checks OWNER/REPO SHA [--wait] [--timeout 30m]` | `GET …/checks/SHA`；`--wait` 轮询直到最新流水线结束，结果不是绿色时以 1 退出 |
| `acahti pipeline list OWNER/REPO [--sha S] [--branch B] [--status S]` | `GET …/pipelines` |
| `acahti pipeline view OWNER/REPO N` | `GET …/pipelines/N` |
| `acahti pipeline log OWNER/REPO N [--step N] [--tail N]` | `GET …/pipelines/N/log` |
| `acahti pipeline trigger OWNER/REPO [--ref REF]` | `POST …/pipelines`（不带 `--ref` 时使用默认分支） |
| `acahti pipeline rerun\|cancel\|approve OWNER/REPO N` | `POST …/pipelines/N/rerun`、`/cancel`、`/approve` |
| `acahti inbox [--section pipes\|prs]` | `GET /inbox` |
| `acahti search [--q TEXT] [--type …] [--assignee …] [--state …] [--scope …]` | `GET /work/search` |
| `acahti iteration list\|view\|create\|update\|close …` | `/work/iterations` |
| `acahti ticket\|todo\|document list\|view\|create\|update\|comment …` | `/work/tickets`、`/work/todos`、`/work/docs` |
| `acahti attachment view\|download\|upload …` | `/work/attachments` |
| `acahti delivery list\|draft\|view\|submit\|retry …` | `/work/deliveries` |
| `acahti track list [--view work\|tickets\|argos] [--project ID] [--owner LOGIN] [--pack P] [--group G] [--env E] [--stuck-days N]` | `GET /work/track` |
| `acahti track view KIND ID` | `GET /work/track/KIND/ID` |
| `acahti track activity [--login LOGIN] [--days N] [--until YYYY-MM-DD]` | `GET /work/track/activity` |
| `acahti track templates` | `GET /work/track-templates` |
| `acahti track template set\|reset TYPE …` | `PUT`/`DELETE /work/track-templates/TYPE` |
| `acahti track flow KIND ID [--template TYPE] [--add k,…] [--skip k,…] \| --clear` | `PUT /work/track/KIND/ID/flow` |
| `acahti aidy ask --question TEXT [--conversation ID]` | `POST /work/aidy/ask` |
| `acahti aidy confirm ID --call CALL_ID [--cancel]` | `POST /work/aidy/conversations/ID/confirm` |
| `acahti aidy list\|view ID\|rename ID --title T\|delete ID` | `/work/aidy/conversations` |
| `acahti aidy settings\|configure …` | `/work/aidy/settings` |
| `acahti mcp tools`、`acahti mcp call TOOL [JSON \| -]` | `/mcp` |

完整工具目录见 [MCP](mcp.md)；shell Agent 用上面的命令完成交付循环，用 `acahti mcp call` 调用其他工具，用 `acahti aidy` 把问题或指令交给 Aidy。

## 推送之后

```bash
sha=$(git rev-parse HEAD)
acahti checks acme/demo "$sha" --wait
acahti pipeline list acme/demo --sha "$sha"
acahti pipeline log acme/demo 12
acahti pr merge acme/demo 3
```

`pr merge` 要求 head SHA 上最新的流水线为绿色，与 MCP 工具的规则完全一致。

## Git

Git 始终走 HTTPS。用户名是 Acahti 登录名；密码是账号密码或访问令牌。SSH 密钥不能作为 Git、MCP 或 CLI 的凭据。

## 在 shell 中使用完整 MCP 目录

`acahti mcp tools` 列出实时的工具 schema。`acahti mcp call TOOL '{"argument":"value"}'` 使用同一个个人令牌，通过 Streamable HTTP 调用工具。用 `-` 代替 JSON 可从 stdin 读取参数。工具出错时以非零状态退出。请求有超时上限，不跟随重定向，也不会把已保存的凭据发往其他实例。

## Work：工单、待办、文档和文件

用于仓库的个人访问令牌或 OAuth 身份，无需网页会话也能使用 Work。所有 Work 对象在实例内共享；个人视图只筛选列表，不限制对象访问。

```bash
acahti ticket view 267
acahti ticket view https://your-instance.example/tickets/267
acahti ticket list --view assigned --page 2 --page-size 20
acahti ticket create --body-file ticket.json
acahti ticket update 267 --body-file changes.json
acahti ticket comment 267 --body-file comment.txt --file evidence.png
acahti ticket archive 267
acahti ticket unarchive 267
acahti todo create --body-file todo.json
acahti todo comment 12 --body 'Progress'
acahti todo link-ticket 12
acahti todo delete 12
acahti document create --body-file document.json
acahti document update 8 --body-file changes.json
acahti document folder-create --body-file folder.json
acahti document folder-update 3 --body-file folder.json
acahti document folder-delete 3
acahti document delete 8
acahti mcp call project_list '{}'
acahti mcp call project_context '{"id":"turbomesh"}'
acahti mcp call time_entry_save '{"todo_id":12,"work_date":"2026-10-30","minutes":120,"summary":"Completed task implementation"}'
acahti mcp call time_entry_list '{"work_date":"2026-10-30"}'
acahti attachment view 123
acahti attachment download 123 --output evidence.png
acahti attachment upload document 8 --file evidence.png
```

`ticket`、`todo` 和 `document` 共用 `list`、`view`、`create` 和 `update`。`--body-file -` 从 stdin 读取。创建和更新文件内容是一个 JSON 对象。评论文件是纯文本。命令返回 JSON；下载会以私有权限写入新文件，不会覆盖已有文件。页面 URL 必须属于所选实例。令牌从不放进 URL。

工单详情包含全部评论和附件元数据。二进制文件通过需认证的 multipart 上传和内容下载端点传输，不以 JSON 编码的文件载荷传输。PDF 和 Office 附件还提供需认证的预览 URL，返回 PDF（Office 文件会被转换）。工单评论最多接受九个 200 MiB 的文件；待办和文档上传一次接受一个 200 MiB 的文件。只有未归档的工单接受更新和评论。文档默认在实例内共享（`visibility=instance`）。受限文档按登录名授予查看或编辑权限；所有者、Work 管理员和组织管理员始终保有完全访问权限。

工单和待办接受来自 `project_list` 的 `project` ID（按仓库权限筛选）；传空字符串即清除。待办关联到工单时保留其原有项目。移动文档用 `document update --body-file`，内容为 `{"folder_id":3}`，移到根目录则用 `{"folder_id":null}`。空文件夹可以删除；非空文件夹会返回冲突，需先移走其中内容。

待办的列表和详情响应包含 `logged_minutes`，即所有人在所有日期为该任务记录的总时长。工时记录归属已认证的本人。每次保存新增一条记录，包含分钟数（1 到 960）和完成情况摘要。日期格式为 `YYYY-MM-DD`；界面默认使用 Asia/Shanghai 时区的当天日期。`time_entry_list` 显示当天合计，并以八小时作为参照；每日合计不设上限。`time_entry_delete` 接受记录 ID，只能删除自己的记录。有工时记录的任务需先删除这些记录才能删除。

```bash
acahti search --q TurboMesh
acahti search --type ticket --scope mine --state open
acahti iteration list --project turbomesh --state open
acahti iteration view 9
acahti aidy ask --question "Which of my tickets are still open?"
acahti aidy ask --question "创建一个 Star Map 新项目"
acahti aidy confirm 12 --call call_ab12
acahti aidy settings
```

`search`、`iteration` 和 `aidy` 是用于产品规划和基于来源作答的命名命令。`aidy ask` 在一个对话中运行 Aidy（除非 `--conversation` 指定已有对话，否则新建一个）：读取操作立即执行，写入操作会放在 `pending` 下返回，直到 `aidy confirm` 批准或 `--cancel` 放弃。自行调用这些操作的 Agent 应引用 URL，并且只在用户授权修改时才使用写入命令。主机模型运行期间，`aidy ask` 和 `aidy confirm` 可能需要几分钟。

没有命名命令的操作通过 `acahti mcp call` 执行，例如 `acahti mcp call ticket_get '{"id":267}'`；`acahti mcp tools` 列出实时目录。

### 交付命令

```bash
acahti delivery draft --iteration 12
acahti delivery submit --body-file delivery.json
acahti delivery list --iteration 12 --page 1
acahti delivery view 7
acahti delivery retry 7
```

交付清单和重试规则见[迭代](iterations.md)。

### 追踪命令

```bash
acahti track list                                   # 未完成的待办，以及没有任何待办承载的工单
acahti track list --view tickets --owner wei --stuck-days 3
acahti track list --view argos --pack nebula --env bj-test
acahti track view todo 123
acahti track view argos_case nebula.upload
```

`--project` 和 `--owner` 筛选 work 和 tickets 视图；`--pack`、`--group` 和 `--env` 筛选 argos 视图。`KIND` 取值为 `ticket`、`todo`、`iteration`、`delivery` 或 `argos_case`。阶段和视图的说明见[追踪](track.md)。`todo update` 要求在 JSON 正文中提供当前的 `revision`。批量规划和结构化的 Agent 步骤使用 `acahti mcp call planning_move_todos`、`planning_todo_links`、`planning_run_step` 以及对应的读取工具。
