的完整实践手册)
Astryx CLI 贡献指南为 Agent 设计命令面cli-conventions的完整实践手册【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx导读本指南将docs/architecture/cli-surface.md中的 CLI 面架构落成可执行步骤面向需要修改packages/cli的贡献者如何判断该加命令还是加深命令、如何为命令添加 flag、如何用共享格式化函数输出、如何维护稳定的错误码以及开 PR 前必须走完的自检清单。Astryx 的 CLI 只有一个受众——在子进程中运行它的 AI Agent读者读完将掌握一套以数据优先、Agent 优先为内核的命令面维护方法论并能直接对照仓库源码验证每一条约定。定位这份文档在 Astryx 中的角色Astryx 是一个完全可定制、面向 Agent 就绪的开源设计系统见仓库根 README.md而 CLI 是 Agent 触达 Astryx 的唯一通道。docs/contributing/cli-conventions.md是packages/cli贡献者的操作规程它与架构文档 cli-surface.md 的关系是架构文档记录事实shipped surface本指南规定流程怎么改。文档开篇即明确当两者冲突时以架构记录为准——本指南不创造政策。仓库的 contributing/README.md 将贡献指南定位为把 Astryx owner 记录转化为可操作的贡献步骤与 pull-requests.mdPR 意图和 api-conventions.md组件 API 约定并列。阅读本指南时建议配合架构文档中的验证清单与verified_by契约测试列表一起使用。CLI 为谁而设计调用者是 Agent理解整份约定的前提只有一个事实调用 CLI 的是一段程序Agent不是人。Agent 在子进程中运行 CLI读取--json输出并据此行动全程无人旁观。终端前的人类读者是受支持的读者但绝不是设计服务的调用方。这一前提直接推导出两条铁律并在争论开始前就解决大部分分歧CLI 不得阻碍 Agent 的流程无提示、无确认、无提问。无法完成的命令返回带错误码的错误让 Agent 能据此分支branch on code并附上可执行的建议suggestion。输出是数据优先--json是真相来源source of truth文本输出只是同一批数值经格式化器投影出来的视图。架构文档将这一点固化为 INV1——CLI 从不提问无 TTY 检测、不为控制流读 stdin非 TTY 环境是唯一受支持的条件而非回退见 cli-surface.md 的 Boundaries and invariants。对应的契约测试是 interactive-guard.test.mjs如果某个命令竟然发起交互导致子进程挂起测试会以signal SIGTERM且status null的方式失败。CLI 的职责覆盖与深度而非命令数量CLI 存在的意义是给 Agent 一切有助于用 Astryx 构建的资源访问权——有哪些组件和模板、它们做什么、怎么用、当前代码有什么问题以及能修改代码的工具。衡量 CLI 的标准是这个面的覆盖度和深度不是命令的个数。文档明确给出判断最有价值的工作是让一个现有命令回答得更好Most valuable work makes an existing command answer better。这意味着在提出新命令之前第一反应应该是加深现有命令新命令是深思熟虑后的例外而不是默认路径。这也解释了后面Adding a command一节的四重门槛——先想清楚是否能用现有命令表达再谈新增。新增命令Adding a command前置条件代码 owner 审批在动手写任何新命令之前必须先获得packages/cli代码 owner 的批准。谁是 owner以.github/CODEOWNERS为准该文件在仓库中确实存在。文档要求先开提案理由非常具体一个命令是永久概念——它出现在帮助文本、manifest、README 和每个 Agent 的 cheat sheet 里移除一个命令是 breaking change。四重门槛命令凭什么占一个位置一个命令只有当以下四条全部成立时才值得存在它回答 Agent 在用 Astryx 构建时真实会问的问题——而不是CLI 恰好能暴露的某个函数。现有命令无法通过加深来回答它。加深deepening是默认选项只有在你说得出我会扩展哪个命令、为什么扩展它是错的之后才考虑新命令。它只做一件事。如果命令的 summary 里需要和and字那就是两个命令。它的结果值得以数据形式返回。如果有用的输出只是给人看的散文那它是文档主题docs topic不是命令。任何一条不满足的命令通常实际上是一个 flag、一个子命令subcommand或一个文档主题。从源码结构看命令布局遵循架构记录的 INV6——一命令一文件clients/cli/commands/name.mjs命令组则是name/index.mjs并配一个兄弟文档name.doc.mjs见 commands 目录 中discover.mjs、discover.doc.mjs的配对布局。布局由pnpm check:cli-structure强制校验。新增 flagAdding a flagflag 比命令宽容不需要提案。但宽容不等于免费——每一个 flag 都是 Agent 必须知道的分支、是某个人必须持续维护的组合。好 flag 的六条标准收窄或重定向命令已有的工作绝不给命令增加第二个任务。默认值在大多数时候就是正确答案。flag 存在的意义是逃离默认而不是为了触及有用行为。如果调用方必须传 flag 才能得到合理结果说明默认值错了。是布尔关或带值。默认值为 true 的布尔 flag 其实是命名错误的 opt-out——直接命名 opt-out 本身。改变命令产生的内容。如果 flag 只是改变同一结果的呈现方式它属于全局选项组--json、--detail、--lang不属于你的命令。不是 workaround。如果加 flag 是为了让调用方绕开缺陷修复缺陷。组合矩阵是封闭的见下节——这是人们最常跳过的检查。如果 flag 改变了命令是什么what the commandis它就是子命令。组合矩阵The composition matrix这是本指南最具操作性的部分。在让 flag 落地之前必须把它与命令上已有的每一个 flag 配对并为每个格子拍板。每个格子只有三种合法答案且每格必须有答案它们可组合compose——并且有测试证明。它们被拒绝同时出现refused together——带清晰的消息和错误码。它们无法共现cannot co-occur——因为另一条规则已经拒绝了会触达它们的组合。未决定的格子就是缺陷。它会以没人选择的行为形态上线而且 Agent 会先于人发现它。这正对应Common review smells中一个没人决定的组合格——flag PR 中最常见的缺陷。同名 flag名字是跨全 CLI 的承诺一个 flag 名是整个 CLI 的承诺两条规则同一个 flag 名在任何地方含义相同、拼写相同。其他任何东西不得占用这个 flag 名去表达别的想法。没有任何命令因为兄弟命令有某 flag 而被迫携带它。对齐的是含义不是存在性。后一条与 review smells 中的因为另一个命令有 flag 就加 flag直接呼应存在性不必对齐含义必须对齐。输出必须使用共享函数永远不要调用console.log。文档给出了一张完整的路径对照表这是命令输出的唯一合法词汇表你想做什么用什么机器可读的结果jsonOut({type, data, meta?})错误jsonError(message, suggestions, code)/AstryxError标题、散文、列表section()、text()、list()单条记录或多条记录record(obj, opts)、records(arr, opts)代码样例code(source)打印上述任意内容emit(...blocks)需被 JSON 隐藏的闲聊输出humanLog()、humanWarn()这套机制在源码中有坚实的实现支撑json.mjs 定义了API_VERSION 1、jsonOut、jsonError、toErrorEnvelope、humanLog、humanWarn和进程级setJsonMode/isJsonMode。humanLog/humanWarn在 JSON 模式下是 no-op——这就是闲聊永不触碰 JSON 模式的 stdout架构 INV12的实现方式也是把if (!json) console.log(...)这种易忘、易遗漏的模式收拢到一个基元的工程取舍。jsonOut只在序列化成功后才设置process.__xdsJsonHandled标记如果JSON.stringify因循环引用或 BigInt 抛错错误边界仍能补发 JSON 错误信封从而守住每次--json输出都是单个合法信封INV2。formatters/index.mjs 定义了一个不透明Block类私有字段#text保证外部无法伪造emit只接受 Block裸字符串在类型层面无法通过编译从而让游离字符串污染 stdout在编译期就被拒绝。record/records直接吃 JSON 原生值对象、对象数组并渲染为对齐的key: value行。输出约束是纯 ASCII无颜色、无 TTY 检测、无宽度折行打印与管道输出逐字节一致架构 INV4。toAscii会把 em/en 破折号、弯引号、省略号、不换行空格归一化为 ASCII 等价物而code()渲染器刻意跳过归一化保证astryx template X file.tsx这类管道输出逐字节保真。两条硬性规则文本字段名必须与 JSON 键完全一致文本输出是信封的视图不是独立设计架构 INV5文本输出绝不能用字符串拼接构建——它会在一个版本内就与 JSON 漂移。错误每一次失败都带错误码错误码契约是整个 CLI 与 Agent 协作的稳定性基石当现有错误码没有合适的向 error-codes.mjs 添加新码。命名规则是ERR_SUBJECT[_QUALIFIER]按主题分组component、hook、docs、template、theme……全大写蛇形一律ERR_前缀。append-only一旦发布错误码永不删除、永不重新定义含义。消息可以随时改写以帮助读者但绝不要求调用方匹配消息。对应架构 INV3代码是契约散文不是。在 Agent 有明显下一步的地方附加suggestions——比如近似的名字、能列出合法值的命令。源码里可以看到这套契约的完整面貌ERROR_CODES是一个Object.freeze的只读映射源码头注释error-codes.mjs解释了为什么消费者必须分支于code而不是error字符串——措辞随时可能被润色、补充细节或本地化而 code 永不变。toErrorEnvelope按优先级解析 code显式code参数 抛出的 Error/AstryxError 携带的code属性 ERR_UNKNOWN兜底保证信封上永远有可分支的 code。消费者侧工具parseResponse、isError、assertResponse通过astryxdesign/cli/json导出。契约由 error-codes.test.mjs 与 error-envelope-code.test.mjs 守护已发布的码消失、或信封携带未注册的码都会测试失败。每个命令都必须带文档CommandDoc命令没写完的标志就是缺name.doc.mjs。必须在CommandDoc中填齐summary、description、args、options每项都要有描述、examples、exitCodes、related。帮助文本、README 表格和 manifest 全部由它生成——未记录的 flag 就是不可见的 flag架构 INV7帮助文本、README 命令/错误码表、manifest 都是生成的没有一项是手写的。至少给一个 Agent 会真实运行的示例必须包含一个--json示例。以 discover.doc.mjs 为范例它声明type: command、name: discover、summary、description、可选的argsquery非必填、options--components、两个examples其中一个是astryx discover --json、exitCodes0 成功、1 为未知包/组件/畸形文档或空查询和relatedcomponent、search、template。这个 doc 文件同时是一个来源fn字段引用discover()API 函数args/flags 映射到该函数的参数转换器据此同时生成 Commander 配置和--help。标记进行中的工作Marking work in progress当前命令面并非全部完成而且没有任何机制标明这一点——调用方无法分辨已定型的命令和仍在塑造中的命令。因此约定当落地的命令/子命令还不宜被依赖时必须标记它并在 doc 里说明预期还会改变什么。未标记的命令视为稳定改变它的输出形状或 flags 就是 breaking change。这一条直接保护 Agent 侧集成标记让可依赖成为显式承诺避免半成品悄悄进入 Agent 的依赖面。开 PR 前的自检清单文档给出的完整清单逐项过一遍再提 PR新命令代码 owner 已批准提案。一命令一文件且带兄弟 doc 文件。--json返回单个信封type与 API 函数一致。每条失败路径都带错误码新码只追加、不改动。无console.log所有人类输出走格式化器。文本字段名与 JSON 键一致。有无--json时退出码一致架构 INV8由 cli-exit-codes.test.mjs 验证——同一条件无论是否带--json都以相同方式退出命令才能不解析 stdout 就充当 gate。组合矩阵封闭每对 flag 或组合有测试证明、或拒绝带消息、或无法共现。你写的任何路径都经过assertWithin架构 INV10对输出路径做 symlink 规范化、拒绝 NUL 字节越界即ERR_PATH_TRAVERSAL有界输入封顶而不是信任。doc 列出了新 flag、示例和退出码。若不适宜被依赖已标记为进行中。常见的 review 味道Common review smells文档列出的反面模式清单是 review 时的高频命中点只有维护者才会传的 flag它是调试便利品请留在表面之外。summary 里带and的新命令两个命令。因为另一个命令有 flag 就加 flag存在性不必对齐含义才要对齐。没人决定的组合格flag PR 里最常见的缺陷。在调用点现编的错误码码只存在于一张冻结的表里。用字符串拼接构建文本输出它会在一个版本内就与 JSON 漂移。--json输出缺少文本输出展示的内容文本是投影它不能比 JSON 更丰富。错误消息让 Agent检查你的配置说出文件名、键和期望值或给出 suggestion。默认值是无用答案的 flagAgent 不会发现它。与架构文档的衔接一次改动动到哪些面如果你的改动涉及以下任何一项必须重读 cli-surface.md并且当它移动了某个不变式invariant时在同一个 PR 里更新它Change coupling 一节增删改名命令/子命令增删错误码或改变既有码的含义改动稳定 JSON 信封或判别式响应字段走spec:AST-017/DEC-4新增格式化器或从emit/jsonOut以外的任何地方写 stdout改动clients/cli/commands下的文件布局改动集成写作者的收据、防覆盖/回滚行为或 package.json 变更策略改动integration pack --check执行的内容改动本地/配置/自动链接集成优先级改动集成主题目录或消费者拷贝契约。布局由pnpm check:cli-structure强制信封/退出码/错误码/非交互保证由架构记录的verified_by列出的契约测试强制如 json-contract.test.mjs、interactive-guard.test.mjs、cli-exit-codes.test.mjs、error-codes.test.mjs。掌握这份指南就等于掌握了以 Agent 为唯一调用方、以 JSON 信封为唯一事实来源的 Astryx CLI 演进纪律——新增一个 flag 或命令之前先让组合矩阵封闭让错误码进冻结表让 doc 先生成你提交的命令才真正算对 Agent 就绪。【免费下载链接】astryxAn open source design system thats fully customizable and agent ready项目地址: https://gitcode.com/GitHub_Trending/as/astryx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考