helloworld CLI
一个最小但完整的示例:CLI 打成单文件二进制(内嵌 Node 运行时),
skill 与它同版本发布,两者靠 latest.json 一起升级。
本 skill 与 CLI 源码同仓库,改了 CLI 行为就在同一次变更里更新它。
构建、打包与分发的细节见仓库根目录的 README.md。
命令本身很简单,重点在周边那套分发机制 —— 见 分发链路。
快速开始
# 从仓库源码直接跑(开发时用,不需要先构建)
pnpm dev info
pnpm dev greet Claude -l en
# 或者构建出本机二进制再跑
pnpm package:local
./release/helloworld-macos-arm64 info
命令
helloworld info
自检:打印版本、内嵌的 Node 运行时版本、平台架构与本地路径。
装完之后第一个该跑的命令。
helloworld info
{
"name": "helloworld",
"version": "0.1.0",
"packaged": true,
"runtime": { "node": "20.x.x", "platform": "darwin", "arch": "arm64" },
"paths": { "home": "/Users/you/.helloworld", "execPath": "…/helloworld" }
}
packaged: true 表示正跑在单文件二进制里;runtime.node 是打进二进制的那个 Node,
与目标机器上装没装 Node 无关 —— 机器上完全没有 Node 时这条命令照样出结果。
helloworld greet [name]
| 选项 | 说明 | 默认 |
|---|---|---|
-l, --locale <locale> |
zh / en / ja |
zh |
-n, --repeat <times> |
重复次数,1~100 | 1 |
--ndjson |
逐行 NDJSON 输出(流式管道用) | — |
--plain |
只输出纯文本,不带 JSON 包装 | — |
helloworld greet # {"name":"World","locale":"zh","text":"你好,World!",…}
helloworld greet Claude -l en # Hello, Claude!
helloworld greet -n 3 --ndjson # 三行 NDJSON
helloworld greet --plain | wc -l # 纯文本进管道
输出契约
调用方主要是 agent,不是人,所以两条流严格分工:
- stdout 只放结构化数据(JSON / NDJSON),给管道和 agent 消费;
- stderr 放给人看的(进度、警告、错误);
- 失败时 stdout 不输出成功结构 —— 刻意与一部分同类 CLI 相反,
免得调用方「见到 JSON 就以为成功」。
| 退出码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 通用失败 |
| 2 | 用法错误(参数缺失、取值非法) |
| 3 | 环境不满足 |
helloworld greet -l fr; echo "exit=$?" # exit=2,且 stdout 一个字节都没有
分发链路
这才是本示例真正要演示的东西。
源码 ──esbuild──> dist/index.cjs ──pkg──> release/helloworld-<平台> (内嵌 Node)
│
.claude/skills/helloworld/ ──视角转换──> helloworld-cli-skill.zip
│
latest.json(版本发现)
│
install-helloworld-all.{sh,ps1}
四条关键约束:
- CLI 与 skill 必须同版本。 skill 是 CLI 的操作手册,版本漂移时文档会描述
二进制里根本不存在的命令,而 agent 读不出「CLI 旧了」这层意思。
所以有install-*-all这个脚本:版本只解析一次再分发给两半。 - 版本从
latest.json现取,不自己拼 URL。 旧版本目录不会被清理,
拼错版本段会稳定下载到旧包,还打印「更新完成」。 - 升级是整目录替换,不是
cp叠加。 叠加只加不删,上个版本删掉的文件会永远留下,
新旧两套文档同时被读到,而旧的那份不带任何「我已过期」标记。 - 不留备份,尤其别放进 skills 目录。 宿主按
<skills 目录>/*扫子目录加载 skill,
放个xxx.bak兄弟目录等于凭空多一份陈旧副本。
升级方式见 scripts/install.md。不要自己写更新脚本。
与仓库其它文档的关系
| 想知道 | 看 |
|---|---|
| 构建 / 打包 / 分发怎么跑 | 仓库根 README.md |
| 安装与升级(给 AI 助手读) | scripts/install.md |
| 输出契约与退出码的实现 | src/core/output.ts |
| 分发产物是怎么组装的 | scripts/dist.mjs |
命令细节与本文档冲突时,以 helloworld <子命令> --help 的实际输出为准 ——
文档会过期,二进制不会。