关于 better-cli
用于设计和审查面向终端用户及 AI Agent 的命令式 CLI 技能,强调可预测的结构化输出、可操作的错误信息和可组合命令,不涵盖全屏终端应用。
主要功能
- 01将数据输出与诊断信息分开
- 02统一提供结构化 JSON 输出
- 03提供可操作的成功与错误信息
- 04支持非交互式自动化
- 05根据 TTY 和管道调整输出
适用场景
- 01设计新的命令式 CLI
- 02改造现有 CLI 以便 Agent 使用
- 03审查命令的易用性和可组合性
- 04添加便于自动化的输出与错误处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yogin16/ better-cli --skill better-cli安装后在 Claude Code 中输入/better-cli确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdAGENTS.mdLICENSEPUBLISHING.mdpackage.jsonclaw.json.claude-plugin/plugin.jsonmarketplace.json.clinerules/better-cli.md.cursor/rules/better-cli.mdc.github/copilot-instructions.md.windsurf/rules/better-cli.mdreferences/composability.mddiscoverability.mdagent-patterns.mderror-handling.mdinteractivity.mdsecurity.mdoutput-design.mdtesting.md详细介绍
更好的 CLI:为人类和 AI Agent 构建 CLI
构建命令行工具,让人类在终端中使用时乐在其中,同时让 AI Agent 在自动化流水线中可靠运行。这两个目标相辅相成,并不冲突。
本指南面向基于命令的 CLI(例如 git、docker、gh、kubectl)——这类工具包含子命令、标志和结构化输出。本指南不涉及全屏 TUI 应用或交互式仪表板。
适用场景
- 使用任意语言构建新的 CLI 工具
- 改进或重构现有 CLI,使其更方便人类和 Agent 使用
- 为现有 CLI 添加命令、标志或输出格式
- 检查 CLI 代码的易用性、可组合性或对 Agent 的就绪程度
不适用于:TUI/全屏终端应用、GUI 应用、REST/GraphQL API 设计、Shell 脚本
核心理念
人类体验(DX)注重易于发现和容错。Agent 体验(DX)注重可预测性和纵深防御。优秀的 CLI 兼顾两者。
黄金法则:输出应告诉你下一步该做什么。 每条命令的输出——无论成功还是失败——都应提供足够的上下文,让用户(人类或 AI Agent)知道下一步该做什么。读取 CLI 输出的 Agent 应能继续其工作流,而无需猜测。
flowchart LR
H[Human at Terminal] --> TTY{stdout is TTY?}
A[AI Agent] --> TTY
TTY -->|Yes| RICH[Rich: colors, tables,\nprogress bars, prompts]
TTY -->|No| CLEAN[Clean: JSON, no ANSI,\nno prompts, parseable]
RICH --> CORE[Same data, same exit codes,\nsame semantics]
CLEAN --> CORE
规则(按优先级排序)
P0:关键——每个 CLI 都必须做到
1. 数据输出到 stdout,其他内容输出到 stderr
stdout -> primary output (results, JSON, file contents) — for piping
stderr -> errors, warnings, progress, diagnostics, prompts — for humans
这是最重要的一条规则。它支持管道、重定向和 Agent 解析。切勿混用。
2. 成功时退出码为 0,失败时为非零
使用语义化退出码,以便脚本和 Agent 根据失败类型进行分支处理:
| 代码 | 含义 | 使用场景 |
|---|---|---|
| 0 | 成功 | 操作已完成 |
| 1 | 一般失败 | 通用错误 |
| 2 | 用法错误 | 参数错误、未知标志 |
| 3 | 未找到资源 | 文件、用户或服务不存在 |
| 4 | 权限被拒绝 | 身份验证失败、权限不足 |
| 5 | 冲突 | 资源已存在 |
| 75 | 临时失败 | 网络超时——重试可能有帮助 |
| 78 | 配置错误 | 配置缺失或无效 |
请记录退出码。区分 transient(75 与其他代码)至关重要——Agent 会据此决定是否重试。
3. 支持 --json 输出结构化内容
每条会产生输出的命令都必须支持 --json(或 --output json)。使用统一的封装格式:
{
"status": "ok",
"data": { "id": "abc-123", "name": "my-resource" },
"warnings": []
}
所有命令使用相同的结构。Agent 只需解析一次,即可在任何地方使用。应将其视为有版本管理的 API 契约——添加可选字段是安全的;移除或重命名字段则属于破坏性变更。
4. 输出必须指引下一步操作
这是让 CLI 适用于 AI Agent 的关键。每条命令的输出都应易于理解且便于操作——读者无需猜测就能知道下一步该做什么。
成功时——确认已完成的操作并建议后续步骤:
Created deployment 'web-app' (ID: deploy-xyz)
Next steps:
View logs: mycli logs deploy-xyz
Check status: mycli status deploy-xyz
失败时——说明出了什么问题以及如何修复:
Error: Cannot deploy — 2 failing health checks.
Fix: mycli health check web-app --verbose
mycli deploy --env staging --force (skip health checks)
结果不完整时——指出缺少什么以及如何获取更多内容:
Showing 20 of 142 results. Next page: mycli list --cursor eyJpZCI6IDIwfQ==
这种模式能让 Agent 自主运行——它读取输出,看到建议的命令,然后执行该命令。缺少这种模式时,Agent 会停滞不前或臆测下一步操作。
5. 支持 --help 和 --version
--help 必须包含:描述、用法格式、带类型和默认值的标志列表,以及 2–3 个符合实际的示例。示例是阅读最多的部分——请将它们放在前面。
EXAMPLES
$ mycli deploy --env staging Deploy to staging
$ mycli deploy --env prod --dry-run Preview production deployment
$ mycli deploy --env prod --yes Deploy without confirmation
6. 绝不要求交互式输入
每个交互式提示都必须有对应的标志。如果 stdin 不是 TTY,绝不显示提示——应给出清晰的错误信息或使用默认值。
| 提示类型 | 对应标志 |
|---|---|
| 确认 | --yes / --force |
| 选择 | --type=value |
| 文本输入 | --name=value |
| 密码 | --password-file=path 或通过 stdin 管道传入 |
Agent 无法在提示中输入“y”。如果 CLI 挂起并等待输入,Agent 的工作流就会中断。
P1:高优先级——优秀与卓越的区别
7. 检测 TTY 并进行适配
当 stdout 是 TTY 时:使用颜色、表格、进度条和交互式提示。 当 stdout 不是 TTY 时:输出纯文本,不使用 ANSI 代码、加载动画或提示。
遵循以下环境信号:
| 信号 | 含义 |
|---|---|
NO_COLOR(任意非空值) | 禁用 ANSI 颜色 |
TERM=dumb | 最简终端——不使用格式化 |
CI=true | 正在 CI 中运行——不启用交互功能 |
MYCLI_NO_INPUT=1 | 应用专属的非交互模式覆盖设置 |
8. 错误必须提供可执行的解决方案
每个错误都需要包含以下四个部分:
- 问题——发生的错误
- 上下文——涉及哪个资源、执行了什么操作、输入是什么
- 修复方法——用于解决问题的确切命令或操作
- 参考资料——文档 URL 或
mycli help <topic>
不好的示例:Error: EACCES
好的示例:Error: Cannot write to /etc/config — permission denied. Try: sudo mycli config set key=value
启用 --json 时,错误必须采用结构化格式:
{
"status": "error",
"error": {
"code": "AUTH_EXPIRED",
"message": "API token has expired",
"fix": "Run: mycli auth login --refresh",
"transient": false
}
}
code 字段可供机器读取(Agent 会根据它进行分支处理)。transient 字段告知 Agent 是否应该重试。
9. 配置优先级
始终遵循:标志 > 环境变量 > 项目配置 > 用户配置 > 默认值
| 层级 | 示例 | 用途 |
|---|---|---|
| 标志 | --port 8080 | 一次性覆盖 |
| 环境变量 | MYCLI_PORT=8080 | CI/CD、容器 |
| 项目配置 | 仓库根目录中的 .mycli.yaml | 团队共享设置 |
| 用户配置 | ~/.config/mycli/config.yaml | 个人默认值 |
| 默认值 | 硬编码 | 开箱即用的合理设置 |
绝不要通过标志接收密钥——它们会泄露到 ps 输出和 shell 历史记录中。请使用环境变量、配置文件或 --password-file。
10. 为管道和组合式使用而设计
- 支持
--fields name,status,id,以限制输出列(提升 Agent 的令牌使用效率) - 支持从标准输入读取:
cat ids.txt | mycli get --stdin - 对流式或分页数据使用 NDJSON(每行一个 JSON 对象)
- 创建资源时,输出其标识符,以便后续命令串联使用
# This pipeline must work
mycli create --json | jq -r '.data.id' | xargs mycli deploy --id
11. 绝不破坏现有契约
改进现有 CLI 时,应将其当前行为视为契约。用户和脚本都依赖这些行为。
破坏性更改(没有主版本升级时绝不可进行):
- 移除或重命名标志、子命令或环境变量
- 更改现有故障场景的退出代码
- 移除或重命名
--json输出中的字段 - 更改现有命令的默认行为
- 更改位置参数的顺序或含义
安全的增量更改(始终可以进行):
- 添加默认值能保持旧行为的新标志
- 在 JSON 输出中添加新的可选字段
- 添加新的子命令
- 在原先不支持的地方添加
--json支持 - 为原先返回 1 的场景添加新的退出代码
- 添加
--fields、--quiet、--dry-run等新标志
如有疑问,只添加,不修改。移除旧行为前,先通过 stderr 中的警告将其弃用。
12. 优先使用标志,而非位置参数
标志可自我说明、顺序无关且面向未来。
不好的示例:mycli deploy production v2.1.0
好的示例:mycli deploy --env production --version v2.1.0
一个位置参数是可以接受的(“主要对象”——文件路径、资源名称)。两个就值得警惕。三个则是错误做法。
P2:中等——打磨细节,建立信任
13. 长时间操作时在 stderr 中显示进度
对于 TTY:显示加载动画、进度条和预计剩余时间。 对于管道:完全不显示,或向 stderr 输出结构化进度信息。 绝不要将进度指示发送到 stdout——这会破坏数据流。
14. 为会修改数据的命令支持 --dry-run
准确显示将发生的更改,但不实际执行。使用 --json 时,以结构化格式输出计划中的更改。这是 AI Agent 必不可少的安全保障——它们可以在执行前进行验证。
15. 使操作具备幂等性
mycli config set key=value 必须支持安全地运行两次。创建命令应支持 --if-not-exists。Agent 会在遇到暂时性故障时重试——设计时应考虑这一点。
16. 提供 Shell 补全
通过 mycli completions <shell> 支持 bash、zsh 和 fish。大多数框架会自动生成补全(Cobra、Click、oclif、clap)。
17. 统一命令语法
选择一种模式,并在所有命令中保持一致:
- 名词-动词:
mycli pod list、mycli pod delete(kubectl 风格) - 动词-名词:
mycli list pods、mycli delete pod(docker 风格)
统一使用常见动词:list、get/show、create、update、delete。
Agent 就绪度决策树
评审或构建 CLI 时,按以下步骤进行:
flowchart TD
START[New CLI command] --> OUT{Produces output?}
OUT -->|Yes| JSON[Add --json with consistent envelope]
OUT -->|No| EXIT[Ensure meaningful exit code]
JSON --> MUT{Mutates state?}
EXIT --> MUT
MUT -->|Yes| DRY[Add --dry-run]
MUT -->|No| PIPE{Usable in pipes?}
DRY --> CONFIRM{Needs confirmation?}
CONFIRM -->|Yes| FORCE[Add --yes/--force bypass]
CONFIRM -->|No| IDEM[Make idempotent if possible]
FORCE --> IDEM
IDEM --> PIPE
PIPE --> FIELDS[Add --fields for column selection]
FIELDS --> ERR[Structured errors with code + fix + transient]
ERR --> DONE[Agent-ready]
反模式
将数据和诊断信息混在 stdout 中
新手:“把所有内容都打印到 stdout,用户会看到。”
专家:如果警告也输出到 stdout,mycli list | jq . 就会出错。数据输出到 stdout,其他所有内容输出到 stderr。没有例外。
时间线:自 1977 年以来就是 Unix 惯例。至今仍是新 CLI 中最常见的错误。
在管道输出中使用颜色和 ANSI 转义码
新手:“始终显示颜色,带来更好的 UX。”
专家:LLM 会将 ANSI 转义序列(\x1b[38;2;...m)作为文本进行分词,浪费上下文窗口并破坏解析。输出任何 ANSI 代码前,先检查 isatty(stdout) 和 NO_COLOR。
检测:将输出通过管道传给 cat -v——如果看到 ^[[ 序列,就说明存在问题。
交互式提示没有跳过方式
新手:“为了安全,始终确认破坏性操作。”
专家:Agent 无法输入“y”。每个提示都必须提供 --yes/--force 跳过方式。如果 stdin 不是 TTY,且未提供跳过标志,则应显示明确错误并退出——绝不要挂起。
检测:在 CI 中运行命令,或将 stdin 通过管道连接到 /dev/null 时,命令会挂起。
成功时不打印任何内容
新手:“Unix 的传统是静默表示成功。”
专家:对人类来说,静默会让人觉得出了问题——应显示简短确认信息(“已创建资源 abc123”)。对 Agent 来说,静默含义不明——应以退出码 0 退出,并支持 --json 返回结果。脚本明确要求静默时,使用 -q/--quiet。
只为人类或机器设计,而非兼顾两者
新手:“我们的用户都是开发者,他们会解析文本。”或“始终只输出 JSON。”
专家:检测上下文(TTY 与管道),并自动调整。终端中默认输出便于人类阅读的内容;通过管道输出或传入 --json 时,则输出机器可读内容。同一条命令即可服务两类用户。
输出没有说明接下来该做什么
新手:“只打印结果,然后退出。” 专家:每条输出都应指引下一步。成功时,建议合理的下一条命令;失败时,建议修复命令;结果不完整时,说明如何获取更多信息。Agent 依赖这些提示自主继续操作——没有它们,Agent 就会停滞、盲目重试或臆造命令。 检测:成功运行一条命令后,问自己:“输出是否告诉我接下来该做什么?”如果没有,就添加“后续步骤”部分。
以“改进”为名破坏现有 CLI 契约
新手:“这个标志名不好,我来改一下。”或“我来调整 JSON 输出结构,让它更简洁。” 专家:现有用户和脚本依赖当前契约——标志名、退出码、输出结构和默认行为。添加新功能时应保留旧功能。移除功能前,先弃用并通过 stderr 发出警告。重命名标志或移除 JSON 字段,可能会破坏数千个下游脚本和 Agent 工作流。 检测:任何 PR 若在未提升主版本号的情况下,移除、重命名或更改现有标志、退出码或输出字段的类型,都应引起警惕。
默认输出冗长,浪费 Agent 上下文
新手:“信息越多越好。”
专家:一次 vitest 运行会生成 419KB 的输出。Agent 只需要 5 个数字。应支持 --fields 选择列、--quiet 输出最少内容,以及 --json 输出结构化数据。stdout 中每个不必要的字节都会消耗 Agent 的 token。
Agent 就绪度检查清单
快速审查任意 CLI:
[ ] Every command supports --json with consistent envelope
[ ] stdout has ONLY data; stderr has everything else
[ ] Exit codes are semantic (not just 0/1)
[ ] Every prompt has a --yes / --force / --flag bypass
[ ] Errors include: code, message, fix suggestion, transient flag
[ ] --dry-run available for all mutating commands
[ ] Progress/spinners go to stderr, never stdout
[ ] NO_COLOR and TTY detection implemented
[ ] --fields flag limits output to requested columns
[ ] Create commands output the created resource identifier
[ ] Mutating commands are idempotent or have --if-not-exists
[ ] --help includes examples, not just flag descriptions
[ ] Secrets never accepted via flags (use env vars or files)
[ ] Piped output contains zero ANSI escape codes
[ ] Success output includes "next steps" with exact follow-up commands
[ ] Error output includes the exact command to fix or diagnose the issue
[ ] No debug traces or stack traces in default output (use --debug)
[ ] Existing flags, exit codes, and output fields are never removed or renamed (additive only)
参考资料
如需深入了解,请查阅以下资料——默认情况下不会加载:
| 文件 | 查阅时机 |
|---|---|
references/output-design.md | 设计 JSON 信封、NDJSON 流式传输、字段选择、输出版本管理和令牌高效格式 |
references/agent-patterns.md | 了解 AI Agent 如何使用 CLI、MCP 封装、架构自省和动态工具发现 |
references/error-handling.md | 查看完整的退出代码表(sysexits.h)、结构化错误格式规范和错误分类设计 |
references/interactivity.md | 查看 TTY 检测代码示例(Node/Python/Go/Rust)、NO_COLOR 实现、进度条模式和提示词设计 |
references/composability.md | 查看适合管道的模式、stdin 约定、跨命令链接和 NDJSON 流式传输 |
references/discoverability.md | 查看帮助文本结构、Shell 补全生成、man 手册,以及用于 Agent 自省的 --help-json |
references/security.md | 查看机密信息处理、Agent 上下文的输入验证(路径遍历、注入)和权限范围控制 |
references/testing.md | 查看输出契约测试、CLI 集成测试模式、快照测试和 CI 中的架构验证 |