Skip to content

docs/cli.md: a flag-level CLI reference for an agent, GENERATED from every shipped tool's --help, with a hygiene test that fails when the committed file drifts from what the generator produces #97

Closed

Description

@brettheap

Lane: openreposhape-2 (openRepoShape-2)

What was asked

Brett Heap, in session, 2026-09-10/11: "do we have documentation designed for
an AI to understand what our CLI can do and how to use it?"

The answer was no. AGENTS.md is PROCEDURE-first — it tells an assistant which
act to perform, in what order, and what it must not do — and README.md is the
standard itself. Neither is a flag-by-flag reference, and the only place the
current flags exist is in each tool's own --help, which an agent can only read
by running eleven commands and remembering the output. The README's one
concession is a sentence pointing at openRepoShape --help and setup.sh --help; that covers two of the seventeen entry points.

Brett's ruling: "add the docs/cli.md".

What this adds

docs/cli.md: a flag-level CLI reference, for an agent, generated from
every shipped tool's own --help output. Not hand-written, because a
hand-written flag list is a second copy of the flags that starts drifting the
day after it is written — the same argument this repository already makes about
a hand-edited pin.

scripts/render-cli-reference.py

Python 3 standard library. Runs each tool's --help (and each subcommand's)
from the repository root, captures stdout and stderr, and renders the whole
document:

  • a header stating the file is GENERATED, by which script, and which test keeps
    it honest;
  • a one-paragraph orientation for an agent, and a table giving what each tool is
    FOR in one line — the only prose in the document, and it lives INSIDE the
    generator, not in the output;
  • one section per tool, the help verbatim in a fenced block, subcommands nested;
  • two groups: Run from a checkout of the standard and Shipped into every
    project (copied, digest-pinned)
    — the second group's files are placed at
    scripts/<name>.py in a scaffolded project or a family holder and are run
    from THERE, which the reference has to say or an agent will try to run them
    here.

Determinism, because a generated file compared byte-for-byte flaps
otherwise: the child environment pins COLUMNS, forces UTF-8, and clears the
locale and colour variables; every line is stripped of trailing whitespace;
CRLF is folded to LF; and the usage: block is re-wrapped by this script from
its own collapsed tokens rather than left as argparse wrapped it — Python 3.14's
argparse breaks family.py bump's usage line in a different place from 3.12's,
and nothing but the line breaks differs, so collapsing and re-wrapping is
lossless and version-proof. Rule 1 is asserted directly: no host-absolute path
may land in the file.

tests/test_cli_reference.py

  • regenerates into a temporary path and asserts byte-equality with the committed
    docs/cli.md, printing a unified diff and the exact regeneration command on
    failure;
  • asserts the header line, so the "do not edit" sentence cannot be lost;
  • asserts that every shipped tool has a section, with the list DERIVED from
    the repository rather than written down twice — so a tool added tomorrow and
    forgotten here fails a test rather than going unlisted.

Windows. setup.sh and openRepoShape are bash, and the windows job has no
bash it can use (tests/conftest.py::WINDOWS_SKIP says why). The byte-equality
test is WINDOWS_SKIPped and the ubuntu and macOS jobs — the two platforms that
have a bash — carry it. The other assertions, and a per-tool check that every
PYTHON tool's help is the text the committed file carries, run everywhere,
Windows included, so that job still fails on real drift in fifteen of the
seventeen entries.

Everything else

  • AGENTS.md: ONE short paragraph pointing at docs/cli.md as the flag-level
    reference and saying it is generated — never hand-edited.
  • README.md: docs/cli.md and the generator in the Layout block, and the
    sentence that already points a reader at two --help texts now points at the
    file that has all of them.
  • Both line caps raised, with a dated docstring entry naming this issue.
  • templates/ is untouched: git diff --stat origin/main -- templates/ is
    empty. Nothing here ships into a project.
  • No new README ## heading, so README_HEADING_TO_HANDBOOK_ID does not move;
    the handbook's README.md blob <hash> is re-cut because README.md changed.

Activity

  1. brettheap commented on Sep 11, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openreposhape-2 (openRepoShape-2), session 8fa66b30-4cf5-4b80-b40c-ac3640cf45ab, 2026-09-11T02:16Z, for the whole of this issue, branch docs/cli-reference.

    Worktree openRepoShape-worktrees/cli-reference off origin/main at 3d46ab5; the shared checkout is another live session's and is not touched.

    Sibling search before claiming: gh issue list --state open and gh pr list --state open both empty; --search "cli reference" across all states returns only #59 (the preflight), which is unrelated. Nothing to serialise behind.

    Reads before authoring, in full:

    • AGENTS.md (400 lines — AT its cap) and README.md (1324 — AT its cap), so both move with a dated docstring entry naming docs/cli.md: a flag-level CLI reference for an agent, GENERATED from every shipped tool's --help, with a hygiene test that fails when the committed file drifts from what the generator produces #97.
    • tests/test_repo_hygiene.py (1479), for the guards this change has to stay inside: test_no_committed_file_names_a_host_absolute_path (tree-wide, so the generated file is bound by it), test_the_handbook_follows_the_readmes_outline (no new README ## heading, and the footer's README.md blob <hash> re-cut because README.md changes), test_every_naming_families_count_matches_the_policy (tree-wide over .md/.html, so the generated file must not carry the phrase), test_the_windows_commands_name_files_that_exist (single-match unpacks of Invoke-WebRequest and py <file> in README.md and AGENTS.md — neither may gain a second), and test_the_materializers_account_for_every_template_file (nothing under templates/ moves).
    • tests/conftest.py (REPO, SCAFFOLD, WINDOWS_SKIP and the reason it exists) and .github/workflows/tests.yml (ubuntu / windows / macOS).
    • Every shipped tool's --help, and every subcommand's: setup.sh, openRepoShape, setup-project.py, scaffold-project.py, adopt-project.py + plan/check/execute, update-shape.py + check/apply, shape-doctor.py, bootstrap, scripts/bump-leg.py, scripts/family.py + init/add/bump/remove/siblings, scripts/validate-repository-naming.py, and the six project-side copies under templates/*/scripts/.

    Two facts found by reading that shape the design, both recorded here before any of it is written:

    1. The project-side copies cannot print their help from this checkout as they stand. Each does sys.path.insert(0, <its own directory>) and then from repo_shape import …; repo_shape.py is beside them only in a materialized project. The generator runs them with scripts/ on PYTHONPATH, which the inserted directory still shadows, so nothing about the files changes.
    2. argparse's usage wrapping is not stable across Python versions. COLUMNS=80 python3.12 and COLUMNS=80 python3.14 agree on all twenty-two help texts except family.py bump, where 3.14 keeps --to COMMIT together and 3.12 breaks it. CI pins python-version: '3.x', so a byte-equality test written naively is red the day the runner moves. The generator therefore re-wraps the usage: block itself from the collapsed tokens — the tokens are identical, only the breaks differ — and pins COLUMNS, UTF-8 and the locale in the child environment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions