mcpField
C
cli-design

cli-design

github.com/yahyabedirhan开发247 次安装6 天前更新
代码仓库

关于 cli-design

用于设计、讲解、审查或重设计命令行界面的 skill。它以统一流程组织从需求到演进的工作,并强调面向用户、脚本、CI 和 Agent 的可预测契约。

主要功能

  1. 01七阶段 CLI 交付框架
  2. 02命令树与动词约定
  3. 03参数、选项与输入规则
  4. 04输出、错误与退出码指南
  5. 05帮助布局与发现机制
  6. 06契约演进与审计实践

适用场景

  1. 01起草新的 CLI 命令模型
  2. 02审查现有 CLI 的不一致
  3. 03重设计命令并追踪重命名
  4. 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。

步骤

  1. 用一句话重述本次会话的目的,根据请求及其上下文作出判断,避免因误解而多花一轮回复:设计新的 CLI 或新命令、解释或审查现有 CLI、重新设计它,还是只决定一个问题。
    • **在包含 CLI 且没有使用说明的项目中调用:**解释并审查该 CLI。
    • **目的不明确时:**询问用户是想设计新的 CLI、审查现有 CLI,还是修改它。自行确认 CLI 是否存在。
  2. **逐阶段完成下方的交付框架。**当某个阶段涉及标志、退出代码、环境变量、配置或帮助信息的布局时,阅读 conventions.md。
  3. 逐一呈现每个阶段,并与用户一起修改,直到用户认可。
  4. 将设计保留在用户指定的位置。

目的决定各阶段的推进方式。以下是常见情况,并非完整清单;一次会话可以混合多种情况,也可以从一种情况转向另一种。

  • **设计新内容:**根据用户需求,完成涵盖所有阶段的完整初稿。
  • **解释或审查现有 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:

  1. 仅凭帮助即可完成每项任务。
  2. 每个动词在整个命令树中含义一致,每个概念只有一个名称。
  3. 每个名词都是项目已在使用的词。
  4. 命令树最多两级,顶层只包含日常使用的命令。
  5. 同一标志在每个命令中的含义相同,标准含义使用标准名称。
  6. 每个打印数据的命令都支持 --json,数据和消息输出到不同的流。
  7. 每条错误信息都说明下一步该怎么做,每个退出码都有文档说明。
  8. 没有终端时,不会显示提示或卡住;每次等待都有超时。
  9. 帮助和文档中的每份列表都由命令表生成。
  10. 每个示例都经过测试,错误信息、文档或脚本中提及的每个命令都确实存在。
  11. 每次重命名都保留一个别名,每项破坏性变更都在发布前公告。

与用户协作

加载 /show-me,并以可视化形式呈现每个阶段:命令树用树形结构表示,动词和重命名用表格表示,帮助用示例呈现,跟踪的命令用调用树表示。只有解释选择背后的原因时才使用散文。

  • 保留一份当前设计版本,并在其基础上修改,不要从头开始。
  • 每次修订都先说明变更内容和原因,以变更前后 diff 的形式展示受影响的树、表格或帮助,并在所有受影响阶段中体现这项变更。
  • 用户已确定的事项保持不变,除非用户重新提出。
  • 有多个合理选项时,将选项并列编号,分别说明代价,并优先给出推荐方案。
  • 只询问代码、文档和用户先前的回答都无法确定的问题,每次只问一个。其余事项由你决定,并说明你的决定。

设计完善期间,将草案保存在项目的临时目录中,并遵循仓库惯例;如果仓库没有相关惯例,询问保存位置。解释可以留在对话中。用户同意并希望保留设计后,将其写入项目文档;更新现有设计文档,而不是另添一份。

参考资料

参考资料涵盖内容
conventions.md默认动词词汇、标准标志名称、退出码、环境变量、配置优先级,以及三个帮助级别的布局

来源:Command Line Interface Guidelines 和 POSIX Utility Conventions。

评论

支持 Markdown