Self-Describing Help

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page was written against the Agent Skills format as documented by Claude Code, Asciidoctor’s manpage backend, Python 3.12 and the docsctl running example of this sub-section, as checked on 2026-09-30. The docsctl help text and outputs are illustrative; the fictional tool exists only on these pages.

An agent meeting an unfamiliar CLI does what a person would: it runs --help and reads. Nothing else about the tool is in the model’s training data, so the help output is the documentation the model works from. The more it explains, and the less it makes the agent guess, the fewer failed calls it costs. Discoverability has four layers, from the one every tool has to the one that needs the most effort: help text, a machine-readable description of the whole tool, completion and man pages for the shell and the system, and an agent skill that tells a host when to use the tool at all.

--help written for LLMs

The GNU standards ask for --help on standard output with a zero exit code, and clig.dev asks for examples in it. Agents make both more valuable. A help text that works for a model is short enough to read cheaply, and concrete enough to copy.

  • Lead with a one-line purpose and the synopsis, then examples, then details. A model (and a person) usually needs the example, not the prose.

  • Put examples first and make them runnable, covering the common path and the machine path (--json, --fields, a paging call). Use realistic values, not <foo>.

  • Explain every flag in one line: what it does, its default, valid values, and any environment variable or config key that sets it. Group global flags separately so subcommand help stays short.

  • Name the exit codes and the output shape the command can produce, or point to the command that prints them.

  • State side effects and safety plainly: "Read-only" or "Destructive: overwrites the published site; run with --dry-run first".

  • Make subcommand help work everywhere: docsctl publish --help, docsctl help publish, and errors that end with "see `docsctl publish --help`".

  • No colour, no paging, no prompts in help when output is not a terminal, and no network access to print it.

  • Keep wording stable: flag names and defaults change with care, since agents (and skills) copy them.

docsctl publish --help, written for people and models alike
Publish a documentation component to the live site.

Usage: docsctl publish --component NAME [options]

Examples:
  docsctl publish --component irurueta-algebra --dry-run --json
      Show what would change, as a JSON plan with a planId and the confirm command.
  docsctl publish --component irurueta-algebra --confirm-token pln_7c41e9 --json
      Apply the plan you reviewed.
  docsctl publish --component irurueta-algebra --idempotency-key "$KEY" --confirm-token pln_7c41e9
      Safe to repeat: the same key returns the earlier result.

Options:
  --component NAME         Component to publish (required). List them: docsctl list-components.
  --dry-run                Print the plan and exit 0. Writes nothing.
  --confirm-token TOKEN    Confirm the plan with this id (from --dry-run). Without it, exit 5.
  --idempotency-key KEY    Label this attempt so retries do not publish twice.
  --force                  Skip the plan-binding check. Dangerous; never use without a person's decision.

Global options (see docsctl --help): --json, --format, --no-input, --yes, --timeout, ...

Safety: destructive (overwrites the published site), idempotent with the same key.
Exit codes: 0 ok, 2 usage, 3 validation, 4 not found, 5 confirmation required, 6 retryable, 7 auth.

Producing text like this is a matter of using your framework’s help features properly: Typer and Click take help text from docstrings and help= arguments and an epilog for examples; picocli takes it from annotation attributes; Cobra has an Example field per command. The framework pages show each: Python, Java, Node.js, Go & Rust and Swift & .NET. Whatever the framework, test the help like any other output: a test that fails when an option is added without a description keeps the documentation from rotting.

A machine-readable command tree

Help text is prose. An agent (or a wrapper that generates tool definitions) is better served by the same information as data, which it can filter and validate without parsing usage lines. docsctl schema --json prints the whole tree: global flags, each command with its arguments and flags and their types, the output schema, the exit codes it can return, and the safety metadata.

docsctl schema --json (shortened to one command)
{
  "schemaVersion": 1,
  "name": "docsctl",
  "version": "0.1.0",
  "globalFlags": [
    { "name": "--format", "type": "enum", "values": ["json", "jsonl", "text"], "default": "text", "description": "Output format." }
  ],
  "commands": [
    {
      "name": "search",
      "summary": "Full-text search over the documentation.",
      "args": [ { "name": "query", "required": true, "type": "string" } ],
      "flags": [ { "name": "--component", "type": "string", "required": false, "description": "Restrict to one component." } ],
      "output": { "schema": "https://example.invalid/docsctl/schemas/search.v1.json" },
      "exitCodes": [0, 2, 6, 7],
      "safety": { "readOnly": true, "destructive": false, "idempotent": true, "openWorld": true, "requiresConfirmation": false }
    }
  ]
}

What the tree buys you:

  • Progressive discovery: docsctl schema lists the commands; docsctl schema publish --json prints one, so an agent pays only for the command it needs.

  • One source of truth. Generate the help text, the tree, the completion scripts and the man page from the same command definitions, so they cannot disagree.

  • Direct mapping to MCP. A command becomes a tool, its flags an inputSchema, its output.schema an outputSchema, and the safety block the tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint), which is the subject of Wrapping a CLI as an MCP Server. Hosts treat annotations as hints from the server, not as guarantees, so an untrusted server’s annotations should not be relied on for security.

  • Generic tooling: a validator that checks every command has a summary, every flag a description and every command an output schema.

Some frameworks generate a tree for you (introspecting the command objects), and many can dump their command model; if yours cannot, keep the tree in one declarative file and build the CLI from it. An alternative some tools use is --help --json; the information is the same, so pick one spelling and keep it everywhere.

Shell completion

Completion is discoverability for the shell: press Tab and see the subcommands, flags and (with effort) the valid values. Agents rarely press Tab, but the same metadata that drives completion drives everything else on this page, and people who share the terminal with an agent benefit.

  • Ship a completion subcommand that prints the script for bash, zsh, fish and PowerShell (docsctl completion zsh), so installation is one line in the user’s profile and the tool never edits dotfiles on its own.

  • Complete dynamic values where it is cheap: --component completing from list-components. Keep completion fast and offline-safe, and let it fail silently.

  • Generate, do not hand-write. Typer and Click can produce completion scripts, Cobra provides a completion command out of the box, and clap and picocli have companion generators; the framework pages (Python, Java, Node.js, Go & Rust and Swift & .NET) give the details.

Loading and installing completion
# Try it in the current shell session
source <(docsctl completion bash)

# Persist it (zsh, with a completion directory on $fpath)
docsctl completion zsh > "${fpath[1]}/_docsctl"

Man pages

A man page is the traditional home of full reference documentation: man docsctl works offline, without the network or the tool’s own runtime, and is what packagers install. For an agent it is one more source it can read (man docsctl | col -b) that is longer and more complete than --help.

  • Generate from the same definitions as the help, when the framework can; otherwise write the page in AsciiDoc and convert it with the Asciidoctor manpage backend, or generate a draft from --help with a tool like GNU help2man, then edit.

  • Follow the section layout: NAME, SYNOPSIS, DESCRIPTION, OPTIONS, EXIT STATUS, ENVIRONMENT, FILES, EXAMPLES, SEE ALSO. EXIT STATUS and ENVIRONMENT are where the exit-code table and the DOCSCTL_* variables belong.

  • One page per subcommand for large tools (docsctl-publish(1), following git-commit(1)), plus one for the whole tool.

  • Ship them with the package, and keep the page’s version in step with the binary’s. Generating them from the command tree, together with the reference docs and the MCP schemas, is covered on Documenting CLIs.

Building a man page from AsciiDoc
asciidoctor -b manpage docsctl.1.adoc     # writes docsctl.1
man ./docsctl.1

Windows has no man; there the equivalents are --help and the published documentation site.

Shipping an agent skill with the CLI

Help text tells an agent how to use a command once it has decided to run it. It does not tell the host that the tool exists, when it is the right choice, or which habits to follow. An agent skill fills that gap. A skill is a folder with a SKILL.md file: front matter with a name and a description that hosts load at start-up, and a body with instructions that loads only when a task matches (the progressive disclosure described on Skills). Shipping one alongside the CLI, as part of the same repository and release, is the most effective way to teach agents a tool that is not in their training data.

What a good docsctl skill contains, following Writing Effective Skills:

  • A description written for triggering: what the tool does and when to use it, in the words a user would use ("search the Irurueta docs", "publish a docs component").

  • The workflow, as checkable steps: search with concise fields, then show the best hit; publish only via dry-run, review, confirm.

  • The rules the CLI cannot enforce by itself: always pass --json and --no-input; never pass --force; never invent a confirm token; stop and ask the user on exit code 5.

  • A pointer to docsctl schema --json for the full command tree, instead of duplicating it, so the skill stays short and never goes stale.

A minimal SKILL.md shipped with docsctl (for example at skills/docsctl/SKILL.md in its repository)
---
name: docsctl
description: Search and read the Irurueta documentation and publish a docs component with the docsctl CLI. Use when the user asks to look something up in these docs or to publish a component.
---

# Using docsctl

- Always pass `--json --no-input`. Never parse the text format.
- To find pages: `docsctl search QUERY --fields id,title,score --limit 5`, then
   `docsctl show ID --max-chars 2000` for the best hit.
- To publish: run `docsctl publish --component NAME --dry-run --json`, show the plan to the user,
   and run the `confirm.command` from the plan only after they agree.
- Never pass `--force`. Never invent a confirm token.
- Exit codes: 2 fix the call, 5 ask the user, 6 retry with the same command, 7 run `docsctl login`.
- Unsure about a flag? Run `docsctl schema --json`.

Practical points:

  • Install location: Claude Code loads project skills from .claude/skills/<name>/SKILL.md and personal ones from the user’s skills directory; GitHub Copilot and other hosts that adopted the open format read equivalents in their own directories (see Skills for the current list).

  • Distribution: put the skill in the CLI’s repository and have docsctl offer to install or update it, or publish it in a plugin (see Plugins & Marketplaces).

  • Test it as you would any skill (see Testing & Evaluating Skills): does an agent with only the skill find the right command, and stop where it should?

  • A skill is a supplement, not a substitute: the --help text and the command tree must still be self-sufficient for a host with no skill installed.