Documenting a command-line interface
To document a command-line tool so an agent can run it correctly, describe it in a cmdspec file and package that. `docspack build --cmdspec` writes one chunk per command, each saying how to run it and what running it does.
To document a command-line tool so an agent can run it correctly, describe it in a file — the way
an HTTP API is described in OpenAPI — and package that. docspack build --cmdspec reads the
description and writes one chunk per command, each saying how to run that command and what
running it does:
npx docspack build --cmdspec ./cmdspec.yaml --name @acme/docspack --pkg-version 1.4.0Combine it with --from, and with --openapi, to publish prose, an API and a CLI in one package,
or put it in the docspack key of the package’s package.json:
{
"docspack": {
"from": "./docs",
"cmdspec": "./cmdspec.yaml"
}
}The file is cmdspec, an open format for describing a command line. docspack reads two others as well, converting them: a Usage spec (KDL), which many Rust, Go and Python CLIs can export, and an OpenCLI document.
A Node CLI built with commander, citty or yargs need not be described by hand.
@docspack/cmdspec/emit reads the program and writes the document — fromCommander(program),
await fromCitty(command), await fromYargs(() => buildProgram(), { version }) — with every
command, option and argument the program parses. What a framework does not know, add to the
result: effects, exit statuses, outputs, examples.
What one chunk contains
Everything needed to run the command, in the shape of a help page, with what help pages leave
out spelled in words: the types and defaults of its inputs, where a value can come from, what the
command changes, what it prints and what its exit statuses mean. The command’s own prose comes
first, where a question is matched against it. Shortened, the chunk for docspack ask:
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)
also accepts: --store <path>, --cwd <dir>, --json, -q, --quiet
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
$ docspack ask "how do I verify a webhook signature"effects is the line an agent most needs and a help page never has: whether running the command
reads, writes, deletes, talks to the network or runs other programs. A command that does not state
its effects says effects: not stated, never nothing, so an agent cannot mistake silence for
safety.
The package also gets an overview chunk: the program’s prose, the options every command accepts with what they do, and one line per command — or the top-level commands only, for a CLI too large to list in one answer.
Asking for one command
A command path is a key, not a phrase. Ranked as prose, git remote add matches every chunk that
mentions remotes; named in a question, it pins its chunk ahead of anything the ranker found:
docspack ask "git remote add origin https://example.com/repo.git"
docspack ask "what does git remote rm do" # an alias reaches its commandThe longest path the question names wins, so the first question gets git remote add, not
git remote as well. Paths match exactly, as typed: command names are case-sensitive.
A CLI you depend on can describe itself
docspack describes its own this way — docspack --cmdspec prints the document its parser and its
--help are generated from, and its reference is rendered from the
same file — and any package can do the same.
A package that ships a CLI can name its cmdspec document in its own package.json:
{
"name": "acme",
"version": "4.2.0",
"bin": { "acme": "./bin/acme.js" },
"cmdspec": "./cmdspec.json"
}docspack sync then indexes that CLI for exactly the version installed, beside its type
declarations, and docspack ask answers from it — ranked like documentation, and pinned when a
question names a command. A description of a different version than the one installed is
reported and skipped, never indexed: it would answer with a command line the installed program
does not accept.
What build checks
An invalid description stops the build, listing every problem. One that is valid but answers
worse is built, with a warning for each gap: commands without a summary, without an example,
without stated effects, or a CLI that says nothing about its exit statuses. docspack doctor runs
the same checks against the file docspack.cmdspec names, so CI catches a description that drifts
after the payload was built.
npx @docspack/cmdspec validate ./cmdspec.yaml checks a description on its own, and
npx @docspack/cmdspec convert turns a Usage or OpenCLI document into cmdspec.