@docspack/cmdspec

中文文档
0.2.0发布于 2026/10/03MITTypeScriptES Module近 30 天下载 2.4万

cmdspec: a machine-readable description of a command-line interface. Validate it, convert it to and from Usage and OpenCLI, and render a per-command digest for an agent.

原始使用文档
文档目录9 节
  1. The CLI
  2. The digest — what an agent reads
  3. Converting
  4. As a library
  5. A command line from a reader's choices
  6. From a commander, citty or yargs program
  7. Keeping what a framework cannot know
  8. Finding a program's document
  9. Licence

@docspack/cmdspec

cmdspec: a machine-readable description of a command-line interface.

OpenAPI says what an HTTP API takes and returns. Nothing has said the same about a CLI with that standing: OpenCLI is a 0.1 draft with no value types, and Usage is rich but has no schema for its own documents. cmdspec is a format that contains both — anything either says converts into it without loss — and adds what neither says: whether a command is safe to run, what its exit statuses mean, what it prints, and which options apply to which operand.

cmdspec: "0.1"
info: { name: greet, version: 1.0.0 }
arguments:
  - { name: who, required: true }
options:
  - { name: --shout, aliases: [-s], summary: Print in capitals }
effects: []
examples:
  - run: greet world --shout

The CLI

npx @docspack/cmdspec validate cmdspec.yaml
npx @docspack/cmdspec convert mise.usage.kdl --to cmdspec --program-version 2026.10.0
npx @docspack/cmdspec convert cmdspec.yaml --to opencli -o opencli.json
npx @docspack/cmdspec digest cmdspec.yaml remote add
npx @docspack/cmdspec map cmdspec.yaml

validate reports every problem, not the first, and names the command and option each one is in. The CLI is itself defined by a cmdspec document — cmdspec.yaml: its parser, its --help and cmdspec --cmdspec all come from that one file.

The digest — what an agent reads

One command, everything needed to run it, in the shape of a help page:

docspack ask <question>... [options]
  Answer from the local index — the command to give an agent
  <question>...  words are joined with spaces
  -p, --package <s>  only packages whose name contains this text
  --limit <n>  maximum chunks to return (integer; at least 1; default 3)
  --max-tokens <n>  token ceiling for the result set (integer; at least 1; default 3000)
  --all  search the whole store, not just this project
  also accepts: --store <path>, --cwd <dir>, --json, -q, --quiet, -h, --help
  effects: read; idempotent
  stdout: text/markdown, the answer; application/json, the chunks and their scores with --json
  exit: 0 success; 1 the command failed; 2 the command was used wrongly; 3 nothing matched, and a docs package is installed but not indexed; 4 nothing matched, and everything installed is already indexed
  $ docspack ask "how do I verify a webhook signature"
  $ docspack ask "webhook signature" --package stripe --limit 5  # Look in one package only, and return more

It is not a notation: every annotation is a word, because the help page is the format a model has seen most, and the design doc measured that words cost 3.2% more than markers it would have to be taught. What it adds to a help page is what help pages leave out — types, defaults, effects, outputs and exit statuses. The largest digest across the four examples is 544 tokens.

Converting

From → toLoses
Usage → cmdspecnothing: unmapped properties and nodes are kept in x-usage and written back
OpenCLI → cmdspecnothing: unmapped fields are kept as x-opencli-*
cmdspec → OpenCLInothing: what OpenCLI cannot say travels in its metadata
cmdspec → Usagewhat Usage cannot say — listed, never dropped quietly

Tested on real documents rather than on promises: mise's own Usage spec (290 commands) survives Usage → cmdspec → Usage → cmdspec unchanged and converts to a valid document, and dotnet's published OpenCLI document (813 KB) comes back from OpenCLI → cmdspec → OpenCLI as the same JSON.

As a library

import {
  commandDigest,
  fromOpenCli,
  mergeOverlay,
  parseArgsConfig,
  resolveDocument,
  toOpenCli,
} from "@docspack/cmdspec";
import { validate, validateOverlay } from "@docspack/cmdspec/validate";
import { readFile } from "@docspack/cmdspec/read";
import { fromUsage, toUsage } from "@docspack/cmdspec/usage";
import { fromCitty, fromCommander, fromYargs } from "@docspack/cmdspec/emit";
EntryContainsDependency
@docspack/cmdspectypes, resolveDocument, the digest and map, OpenCLI conversion, parseArgsConfig, the rules, mergeOverlaynone
@docspack/cmdspec/validatevalidate — the schema, then the rules; validateOverlayajv
@docspack/cmdspec/readreadFile, readText — JSON or YAML, validatedyaml
@docspack/cmdspec/usagefromUsage, toUsage@bgotink/kdl
@docspack/cmdspec/emitfromCommander, fromCitty, fromYargsnone
@docspack/cmdspec/buildermountCommandBuilders — the command builder, in a browsernone

parseArgsConfig(command) gives node:util's parseArgs its options from a command's document, so a Node CLI can parse its command line from the same file it is documented by.

A command line from a reader's choices

commandLine(builderTarget(command), values) writes the command line a set of values makes — attaching values the way the document's syntax says, quoting for a POSIX shell, putting -- before arguments that come after it — and lists the required inputs still missing. @docspack/sheaf-react's CliBuilder renders that as a form, and @docspack/cmdspec/builder runs it in the browser as the reader types.

From a commander, citty or yargs program

A program already built with one of these frameworks has its command line in memory. The emitters read it and write the document, so describing it starts from what it actually parses:

import { fromCommander } from "@docspack/cmdspec/emit";
import { program } from "./cli.js"; // the commander program, not yet parsed

const document = fromCommander(program);

fromCitty(command) resolves every lazy subcommand, so it is async. fromYargs(build) takes the function that builds the program, because yargs builds a command's options only while running it, on the program itself; it is called once per command, and the version comes from fromYargs(build, { version }). No framework is imported: each emitter reads the few fields it needs, and the entry has no dependencies.

What a framework does not know, the document does not say: effects, exit statuses, outputs and examples. Keep them in an overlay, so the next generation does not lose them; docspack build warns for each one still missing.

Keeping what a framework cannot know

An overlay is a file beside the program — schema/overlay-v0.1.json — merged into every document generated from it:

cmdspecOverlay: "0.1"
exits:
  "0": { meaning: success }
commands:
  serve:
    effects: [network]
    options:
      --port: { value: { minimum: 1024 } }
  db drop:
    effects: [destructive]
import { mergeOverlay } from "@docspack/cmdspec";

const result = mergeOverlay(fromCommander(program), overlay);
if (!result.merged) throw new Error(result.problems.map((p) => `${p.at}: ${p.message}`).join("\n"));

Commands are keyed by their path below the program, options by spelling and arguments by name. Every entry must name something the program has, so renaming db drop makes the overlay a problem rather than a patch that silently stops applying. The overlay fills what the document leaves out; where the program states a value, the overlay must agree or list the field in override. validateOverlay checks an overlay against its schema, and mergeOverlay reports every problem, not the first. Why it works this way is docs/design/cmdspec-adapters.md.

Finding a program's document

A package names its document in package.json — "cmdspec": "./dist/cmdspec.json", as this one does — and a program may print it when run with --cmdspec. Both are in SPEC §13.

Licence

The code, the schema and the examples are MIT. The specification text, SPEC.md, is CC BY 4.0. The fixtures are MIT-licensed documents from mise and OpenCLI; see tests/fixtures/README.md.

版本与发布记录

@docspack/cmdspec 共发布 3 个版本,首次发布于 2026/10/03;过去 12 个月发布了 3 个版本。

版本发布日期距今
0.2.0latest2026/10/031 个月前
0.1.02026/10/031 个月前
0.0.02026/10/031 个月前

关于 @docspack/cmdspec 的常见问题

@docspack/cmdspec 是什么?

cmdspec: a machine-readable description of a command-line interface. Validate it, convert it to and from Usage and OpenCLI, and render a per-command digest for an agent.

如何安装 @docspack/cmdspec?

在项目目录运行 npm install @docspack/cmdspec,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 cmdspec,可以用 npx cmdspec 直接运行。

@docspack/cmdspec 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@docspack/cmdspec 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@docspack/cmdspec 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.5.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@docspack/cmdspec 可以商用吗?

@docspack/cmdspec 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@docspack/cmdspec 的下载量有多少?

根据 npm 官方下载统计,@docspack/cmdspec 在 2026/10/13 – 2026/11/11 这 30 天内被下载 24,365 次,日均约 812 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@docspack/cmdspec 还在维护吗?

最新版本 0.2.0 发布于 2026/10/03(1 个月前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/10/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。