关于 cli-design
用于设计、讲解、审查或重设计命令行界面的 skill。它以统一流程组织从需求到演进的工作,并强调面向用户、脚本、CI 和 Agent 的可预测契约。
主要功能
- 01七阶段 CLI 交付框架
- 02命令树与动词约定
- 03参数、选项与输入规则
- 04输出、错误与退出码指南
- 05帮助布局与发现机制
- 06契约演进与审计实践
适用场景
- 01起草新的 CLI 命令模型
- 02审查现有 CLI 的不一致
- 03重设计命令并追踪重命名
- 04判断使用选项还是子命令
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yahyabedirhan/ skills --skill cli-design安装后在 Claude Code 中输入/cli-design确认已加载。
附带文件references/conventions.md
详细介绍
CLI 设计
命令行界面是面向所有调用者的契约:无论是终端用户、脚本、CI 任务还是 Agent。每个命令名称、标志、输出格式、退出代码和环境变量都是这份契约的一部分;命令发布后,就会有脚本依赖它。优秀的 CLI 应当可预测:学会一个命令的人就能猜出下一个命令,而且只看帮助信息就足以使用它。
无论是在设计、解释、审计还是重新设计,都要在每次会话中通过交付框架产出并呈现设计,让用户总能在相同位置找到相同内容。由用户决定会话的目的;框架则决定答案的组织方式。用户同意设计后就停止,因为实现工作会在之后进行。
反复出现的问题是偏离:每个命令各行其是,最终同一个动词有三种含义,同一个概念有三种名称,帮助信息、文档和代码各自列出的内容也不一致。要避免这种情况,就应一次性确定规则,包括名词、动词、标志名称以及输出和错误约定,并据此生成每个命令以及读者看到的每份列表。
若要设计命令背后的代码结构,包括模块、类和文件夹,请使用 /low-level-design。若要设计网络 API,请使用 /system-design。
步骤
- 用一句话重述本次会话的目的,根据请求及其上下文作出判断,避免因误解而多花一轮回复:设计新的 CLI 或新命令、解释或审查现有 CLI、重新设计它,还是只决定一个问题。
- **在包含 CLI 且没有使用说明的项目中调用:**解释并审查该 CLI。
- **目的不明确时:**询问用户是想设计新的 CLI、审查现有 CLI,还是修改它。自行确认 CLI 是否存在。
- **逐阶段完成下方的交付框架。**当某个阶段涉及标志、退出代码、环境变量、配置或帮助信息的布局时,阅读 conventions.md。
- 逐一呈现每个阶段,并与用户一起修改,直到用户认可。
- 将设计保留在用户指定的位置。
目的决定各阶段的推进方式。以下是常见情况,并非完整清单;一次会话可以混合多种情况,也可以从一种情况转向另一种。
- **设计新内容:**根据用户需求,完成涵盖所有阶段的完整初稿。
- **解释或审查现有 CLI:**按其现状逐阶段梳理。
- 根据证据构建每个阶段:运行顶层帮助和每个分组的帮助,阅读命令定义和分发代码,并阅读调用该 CLI 的文档、脚本和技能。每项判断都要注明依据的文件或命令。
- 将从行为中读出的要求标记为推断。将证据无法说明的内容(例如主要调用方是谁,或某个名称为何如此选择)标记为未知,并向用户询问,而不是自行填补。
- 量化审查结果:各分组的命令数、动词及其各自含义,以及手动列出相同内容的位置。数字比形容词更能体现偏差。
- 根据末尾的验证清单记录观察到的弱点,不提出改动建议,因为本次会话保持 CLI 原样。
- **重新设计现有 CLI:**先进行说明,并确认用户认可该说明。然后按相同阶段重新设计,只改动问题所需的部分。将每个重命名的命令以变更前/后的形式列出,并注明违反的规则;在演进阶段也要纳入每项重命名,因为现有调用方依赖旧名称。
- **决定一个问题,**例如“应该用标志还是子命令?”:结合受影响的设计部分、背后的要求、各种选项及其权衡作答,并给出建议。
如果正在设计的 CLI 与知名工具相似,在依赖记忆作答之前,先研究该工具的帮助信息和末尾引用的指南:沿用有助于调用方的模式,并说明哪些模式未采用及原因。
交付框架
Requirements → Command model → Inputs → Output and errors → Help → Implementation → Evolution
每个阶段都建立在前一阶段之上:命令模型源自需求,输入和输出描述模型中的每条命令,帮助信息由这些内容生成,实现将它们集中在一处,而演进阶段则用下一次变更检验整个设计。当前阶段满足其产品清单中的所有要求后,再进入下一阶段。如果后续阶段发现缺口,就回到较早的阶段。
要求
围绕以下五个主题展开:
- 调用方: 谁在运行 CLI:终端上的人员、脚本、CI、Agent。为每项任务注明主要调用方,因为面向人员的命令和面向脚本的命令需要做出不同权衡。
- 任务: 每位调用方要完成什么,用“调用方应能够……”的语句描述。控制范围内的任务列表要简短;每项任务对应一个或几个命令。
- 角色: 根据命令的影响类型分类:只读、修改数据、操作运行中的服务或应用、等待事件、管理。每种角色都有自己的规则,例如是否需要确认、锁或凭据。
- 运行时: 每条命令运行所需的条件:无需依赖、本地文件、正在运行的守护进程或应用、网络。说明哪些命令可离线运行,因为需要依赖被检查服务的状态检查,无法报告该服务已停止。
- 范围外: CLI 不会实现的内容,例如交互式界面、插件系统或 Windows 支持。明确排除项,避免命令树不断加入没人需要的命令。
用简短的编号列表列出这些内容。
命令模型
将任务转化为命令树。
- 名词: 用项目现有术语表、UI 和文档中的词语,表示任务所操作的对象。拥有自己命令的名词应成为分组;关于名词的信息应作为选项或输出字段,而不是分组。
- 层级: 将名词画成包含关系树,例如 account › project › environment,并让分组遵循该层级。采用
名词 动词顺序,因为这种顺序更常见,也能让帮助信息中的相关命令归为一组。 - 动词: 使用一套封闭词汇,每个词只有一种含义,并在所有支持该动词的名词上保持一致。每组近义词只选一个词,例如
create或new、get或show,并始终使用所选词。区分打印和显示:打印数据的动词始终表示打印;窗口或选择器应使用独立的动词。默认词汇见 conventions.md。 - 深度: 最多两层,即
tool <noun> <verb>。复合操作使用连字符连接的动词(send-text),而不是增加第三层。 - 顶层: 仅保留调用方每次会话都会运行的少数命令,以及
help。其余命令全部放入分组,因为过长的扁平列表会掩盖重要命令。 - 目标: 为命令指定一套统一规则,用于命名其操作的对象:必填词(
tool project get <id>)、命名选项(--project <id>),或当前默认值,例如配置的上下文或工作目录,且选项可以覆盖默认值。所有命令都遵循同一规则。
将模型列为命令树(每个分组占一行)、动词表,以及目标规则。
输入
完整说明每个命令的参数、标志和其他输入。
- **参数和标志:**参数是按自然顺序排列的必需输入,例如要复制的文件。其他内容都是命名标志,因为以后可以添加标志而不影响调用方,并且在脚本中一目了然。当命令需要两种不同类型的输入时,将第二项作为标志,以免调用方误将它们交换。
- **标志名称:**所有标志都使用 kebab-case 长名称,只有最常用的少数标志使用单字母短名称;对于标准含义,使用标准名称(
--help、--version、--json、--quiet、--force、--dry-run)。同一个标志在每个命令中的含义都相同。 - 全局标志(例如输出格式、详细程度和目标上下文)由所有命令接收,并在同一处解析。
- **其他来源:**当参数为
-时使用标准输入,也可使用环境变量和配置文件。按统一的优先级顺序应用这些来源,从标志到内置默认值依次递减。 - **交互:**仅当标准输入是终端时才显示提示,并为每个提示提供一个可用于回答它的标志,这样脚本和 Agent 就不会一直挂起。对破坏性操作进行确认;使用
--force或--yes跳过确认,并为难以撤销的操作提供--dry-run。 - 机密信息应来自文件、标准输入或环境变量,绝不能来自标志,因为标志会留在 shell 历史记录和进程列表中。
- 选项结束标记:
--表示选项结束,因此以连字符开头的文本仍会被视为参数。
为每个命令显示一行用法,其中包含参数、标志及其值占位符。
输出与错误
明确每个命令会打印什么,以及出错时如何处理。
- **流:**数据输出到标准输出,以便进行管道传递。进度、警告和错误输出到标准错误。
- **人类可读和 JSON 输出:**默认输出面向用户,简短易读。
--json会为每个输出数据的命令打印稳定且有文档说明的结构。JSON 结构属于契约的一部分,而普通输出可以自由改进。 - **终端:**仅当流连接到终端时才使用颜色、旋转指示器和进度条;遇到
NO_COLOR、TERM=dumb和--no-color时关闭颜色。约在 100 ms 内打印内容;对于耗时超过片刻的操作,显示进度,避免命令看起来像是挂起。 - **错误:**用一行说明出了什么问题以及接下来该怎么做,并指出可用于解决问题的命令或标志。将最重要的信息放在最后,也就是视线最终停留的位置。仅在用户请求时打印调试跟踪信息。
- **退出码:**成功时返回 0,失败时返回非零值,用法错误时返回独立的退出码。记录 CLI 使用的所有退出码;只有在调用方会以不同于失败的方式处理某种情况(例如超时或“未找到内容”)时,才添加对应的退出码。
- **健壮性:**在执行任何操作之前验证输入;为每次网络调用和每次等待设置超时;在可能的情况下确保重复运行安全;收到 Ctrl-C 时迅速退出。
为每个命令或命令组提供一张表,说明其输出内容、--json 结构、拒绝处理的情况及其退出码。
帮助
将帮助设计为学习命令行工具的主要入口,面向人和 Agent。
- 三个层级: 顶层帮助(
tool --help)控制在一屏内;组帮助(tool <group> --help,或直接输入组名)列出该组的每条命令及其完整用法行;命令帮助(tool <group> <verb> --help)提供用法、描述、所有标志和示例。布局见 conventions.md。 - 摘要: 每条命令一行,以动词开头,说明命令能实现什么,而不是代码如何实现。命令在任何地方显示时,都使用相同的摘要。
- 示例放在命令帮助的开头,因为大多数读者会复制一个示例并加以调整。
- 错误用法: 用法错误时,打印该命令的用法行,并提示查看帮助,而不是输出整本手册。命令拼写错误时,建议最接近的匹配项,并让调用方自行运行。
- 发现方式: 顶层帮助指出完整文档的位置。对于调用方为 Agent 的情况,提供机器可读的命令列表,例如
help --json或架构命令,并在顶层帮助中提示一次。 - 为常用 Shell 生成补全,且使用与命令相同的定义。
用模拟示例展示顶层帮助、一个组的帮助和一条命令的帮助。
实现
设计 CLI 如何在代码中维护这份契约。使用该语言生态已标准化的参数解析库,只自行实现其缺少的功能。
- 以数据形式维护一张命令表: 在同一处记录每条命令的名称、组、用法、摘要、描述、示例、标志、目标和角色。解析器、帮助、补全、文档和机器可读列表都从中读取。
- 从参数到退出码只有一条路径: 解析全局标志、查找命令、解析输入、运行命令、打印并退出。每条命令都经过这条路径,因此无需每条命令重复声明约定。
- 不重复手写列表: 读者能看到的每个列表,例如哪些命令接受某个标志,都来自命令表。在帮助或文档中手写的列表会逐渐与代码不一致。
- 每个组一个文件,文件以组名命名,这样修改某个组时只需打开一个文件。
- 契约测试确保设计得到遵守:帮助中的每个示例都能通过解析;错误消息、文档或脚本中提到的每条命令都存在;帮助与已存储的快照一致;每条命令的
--json输出都符合文档规定的结构。
展示文件夹树并说明每个文件的职责、命令表的字段、从参数到退出码的一条命令执行路径,以及一次拒绝操作。
演进
针对可能出现的后续变更测试设计,并决定契约应如何调整。
- 优先采用增量变更: 添加命令、标志或 JSON 字段,而不是修改现有内容。新增标志可以改变行为,同时不影响未传入该标志的调用方。
- 重命名时,将旧名称保留为隐藏别名,并在标准错误流中发出警告,指出新名称;保留期限需明确。按名称列出所有别名,并且只匹配完整名称:兜底命令会阻止任何包含其捕获词语的未来命令;前缀匹配会阻止任何与其共享前缀的未来命令。
- 破坏性变更在发布前通过 CLI 提前公告,并说明如何立即调整用法;发布时提升版本号,以便调用方识别。
- 版本管理:
--version打印版本号;文档、补全以及任何会调用 CLI 的 Agent 指令都与同一版本一同发布。
用“变更 → 修改内容”表格展示可能出现的变更。若变更只涉及命令表中的一个新增命令,则可直接纳入;若需要在多个文件中重复相同修改,则需要设置一个隔离层。
默认约定优先
每项选择都从默认约定开始,因为调用者会带着对其他 CLI 的预期,而每一处偏离都会让他们多查一次。只有在有明确需求时才偏离默认约定,并说明是哪项需求。
- 命令形式: 分两级,采用
名词 动词。 - 标志: 使用标准名称。
- 输出: 每个打印数据的命令都支持
--json;数据输出到标准输出,消息输出到标准错误。 - 退出码: 成功为 0,失败为 1,用法错误为 2。
- 提示: 不在终端中运行时不显示提示。
- 帮助: 根据一张命令表生成。
验证
根据以下每项检查已完成的设计,或审查现有 CLI:
- 仅凭帮助即可完成每项任务。
- 每个动词在整个命令树中含义一致,每个概念只有一个名称。
- 每个名词都是项目已在使用的词。
- 命令树最多两级,顶层只包含日常使用的命令。
- 同一标志在每个命令中的含义相同,标准含义使用标准名称。
- 每个打印数据的命令都支持
--json,数据和消息输出到不同的流。 - 每条错误信息都说明下一步该怎么做,每个退出码都有文档说明。
- 没有终端时,不会显示提示或卡住;每次等待都有超时。
- 帮助和文档中的每份列表都由命令表生成。
- 每个示例都经过测试,错误信息、文档或脚本中提及的每个命令都确实存在。
- 每次重命名都保留一个别名,每项破坏性变更都在发布前公告。
与用户协作
加载 /show-me,并以可视化形式呈现每个阶段:命令树用树形结构表示,动词和重命名用表格表示,帮助用示例呈现,跟踪的命令用调用树表示。只有解释选择背后的原因时才使用散文。
- 保留一份当前设计版本,并在其基础上修改,不要从头开始。
- 每次修订都先说明变更内容和原因,以变更前后
diff的形式展示受影响的树、表格或帮助,并在所有受影响阶段中体现这项变更。 - 用户已确定的事项保持不变,除非用户重新提出。
- 有多个合理选项时,将选项并列编号,分别说明代价,并优先给出推荐方案。
- 只询问代码、文档和用户先前的回答都无法确定的问题,每次只问一个。其余事项由你决定,并说明你的决定。
设计完善期间,将草案保存在项目的临时目录中,并遵循仓库惯例;如果仓库没有相关惯例,询问保存位置。解释可以留在对话中。用户同意并希望保留设计后,将其写入项目文档;更新现有设计文档,而不是另添一份。
参考资料
| 参考资料 | 涵盖内容 |
|---|---|
| conventions.md | 默认动词词汇、标准标志名称、退出码、环境变量、配置优先级,以及三个帮助级别的布局 |
来源:Command Line Interface Guidelines 和 POSIX Utility Conventions。