GitHub 的 gh 应该是 vibe coding 里非常高频的命令了，而且它 CLI 本身也设计得很 agent-friendly，很适合拿来学习研究。

我觉得 gh 最值得学的，不是某几个具体 command，而是它处理了两个很关键的问题：

一个是 GitHub 能力面太大，CLI command 很容易爆炸。

另一个是 agent 的 context 很贵，命令输出不能把无关信息全塞进来。

先看 command 爆炸的问题。

GitHub 的能力面非常大。如果每个能力都做成一个 command，很快会变成：

```bash
gh issue list
gh issue create
gh issue comment delete
gh repo deploy-key add
gh project item archive
...
```

command 越来越多，整个 --help 也会变得难以维护。agent 也永远要猜：这个操作到底有没有对应的 command？

gh api 用很取巧的方式解决了这个问题（当然这也建立在 GitHub 本身非常完善的 RESTful API 基础上）：

```bash
gh api repos/epiral/bb-viewer/issues/comments/4517246421 -X DELETE
```

它不是简单地"可以直接调 API"，而是把 RESTful API 里的 resource model 自然迁移到了 CLI 里：路径定位资源，HTTP method 表达动作，认证和输出格式由 gh 统一处理。

所以文档里看到：

```text
DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}
```

几乎不用翻译，就能写成：

```bash
gh api repos/epiral/bb-viewer/issues/comments/4517246421 -X DELETE
```

这对 agent 特别重要。API 文档本身就可以变成 CLI 使用说明。agent 不需要学一套和 API 文档完全不同的 DSL，也不需要等 CLI 作者给每个长尾能力都包一层 command。

比如用 REST 路径直接查一个 PR：

```bash
gh api repos/cli/cli/pulls/13492 --jq '{number: .number, title: .title, state: .state}'
```

输出：

```json
{"number":13492,"state":"open","title":"Replace SITE_DEPLOY_PAT with gh-cli-site-deployer App"}
```

路径就是资源定位，--jq 做字段裁剪，整个过程不需要记任何专用 command。

这背后其实是一层 resource interface。resource 层解决的是覆盖面问题：能力很多，但语法可以统一。

但 resource 不是万能的。RESTful 一直以来的问题就是，有些用户意图很难自然 resource 化。

比如 login。它不是对某个资源做 CRUD。

比如 clone。它既涉及远程 repo，也涉及本地文件系统和 git 状态。

比如 checkout。它不是更新一个远程资源，而是在本地切换工作区状态。

比如 merge。它经常包含多个底层动作，但用户表达的不是"修改这个字段，再删除那个分支"，而是"把这个改动合进去"。

所以 CLI 里还需要 command 层。command 不是"多步骤编排"的同义词。多步骤编排只是 command 的常见来源之一。command 的本质是承接那些无法自然 resource 化的用户意图。

如果从 gh 再抽象一步，可以把 resource 和 command 在语法上显式分开。

一个可能的方案是用 / 前缀表示 resource：

```bash
# command，没有 / 前缀，表达动作
cli login
cli clone epiral/bb-viewer
cli checkout 353
cli merge 353 --squash
cli status

# resource，用 / 前缀，表达对象路径
cli /issues list
cli /issues/42 get
cli /issues/42 update state=closed
cli /issues/42 delete
cli /issues/42/comments create body="LGTM"
cli /projects/4/items list
```

/ 的好处是它不占用任何单词。

如果用 api，会让人以为这是传统 API wrapper；如果用 resource，太啰嗦；如果直接写 issues list，又会有歧义：issues 到底是 command 还是 resource？

/issues 就很清楚：这是一个资源路径。

resource 层的动词可以收敛到一个很小的集合：

```text
list
get
create
update
delete
```

这样 agent 学会一个资源，就基本学会了所有资源。

```bash
cli /issues list state=open author=me
cli /issues create title="Fix login" body="..."
cli /issues/42 update state=closed
cli /issues/42/comments create body="LGTM"
```

参数也可以分清楚：key=value 是资源参数，--flag 是 CLI 行为控制。

```bash
cli /issues list state=open --json --jq '.[].title'
```

resource 层负责覆盖长尾能力，command 层负责表达高层意图。两者不是替代关系，而是互补关系。

再看第二个问题：输出污染。

在 agent workflow 里，命令输出不是越多越好。无关字段进入 context，不仅浪费 token，还会污染语义空间，干扰后续推理。

gh 的 --json / --jq 很值得学。

比如不做裁剪，agent 拿到的可能是这种输出：

```bash
gh pr list -R cli/cli --limit 1 --json number,title,author,labels,state,reviewDecision,updatedAt
```

```json
[
  {
    "author": {
      "id": "MDQ6VXNlcjE2MTE1MTA=",
      "is_bot": false,
      "login": "williammartin",
      "name": "William Martin"
    },
    "labels": [],
    "number": 13492,
    "reviewDecision": "REVIEW_REQUIRED",
    "state": "OPEN",
    "title": "Replace SITE_DEPLOY_PAT with gh-cli-site-deployer App",
    "updatedAt": "2026-05-22T16:54:37Z"
  }
]
```

但如果下一步只是要知道 PR 标题，真正需要进入 context 的只有一行：

```bash
gh pr list -R cli/cli --limit 1 --json title --jq '.[0].title'
```

```text
Replace SITE_DEPLOY_PAT with gh-cli-site-deployer App
```

\--jq 的价值不是"省一个管道"，而是把信息裁剪发生在进入 LLM context 之前。先减少 token 浪费，再减少无关字段对后续推理的干扰。

还有一种情况：默认输出本身就是语义化的。

```bash
gh pr view 13492 -R cli/cli
```

```text
title:    Replace SITE_DEPLOY_PAT with gh-cli-site-deployer App
state:    OPEN
author:   williammartin
reviewers: copilot-pull-request-reviewer (Commented), BagToad (Requested)
number:   13492
url:      https://github.com/cli/cli/pull/13492
additions: 26
deletions: 2
--
## Summary

Replaces the personally-held SITE_DEPLOY_PAT used by the release workflow...
```

这类输出比一整坨 JSON 更适合 LLM 直接理解。

所以 JSON 的定位应该是串联和精确抽取，不是默认认知界面。默认输出应该尽量语义化。这个点前一篇展开过，这里只 callback 一下：自然语言是模型更擅长的表征。

如果设计自己的 resource 风格 CLI，也可以沿用这个思路：

```bash
cli /issues/42 get
```

默认输出：

```text
Issue #42: Fix login bug
State:   open
Author:  epiral
Labels:  bug, auth
Updated: 2h ago

Login fails when session expires.
```

需要串联时：

```bash
cli /issues/42 get --json --jq '.title'
```

```text
Fix login bug
```

默认语义化，需要时结构化。

最后还有一些执行层面的设计也值得学。

gh 的 flags 很一致：--repo、--assignee、--label、--json、--jq、--web 在不同 command 里复用。对人是降低学习成本，对 agent 是提高泛化能力。

\--web 是一个自然的 fallback：

```bash
gh pr view --web
```

CLI 不需要假装覆盖所有交互。有些事情就是 Web 更合适。

还有非交互模式：

```bash
GH_PROMPT_DISABLED=1 gh pr create --title "fix bug" --body "..."
```

\--yes 跳确认，--dry-run 做预览，token 走环境变量。这些都是 agent 能稳定使用 CLI 的基础设施。

所以 agent-friendly CLI 不只是 machine-readable CLI。

machine-readable 解决的是程序串联；agent-friendly 还要解决语义理解。

结构上要稳定可组合：resource path、统一动词、--json、--jq。

语义上要适合 LLM：默认自然语言输出、清晰错误信息、少 token 噪音。

从 gh 里可以学到的核心就是这个方向：用 resource 层避免 command 爆炸，用 command 层承接无法 resource 化的动作，再用输出裁剪避免污染 agent 的 context。
