Generate CLI reference from the clap model + cli-doc drift gate (P5.3)#32
Conversation
Generate the `forge` CLI reference in crates/forge.md directly from the clap command model, and gate it against drift — the published docs and the binary's argument parser are now the same model and cannot diverge. forge_cli: split into lib + bin. The clap model (`Cli`/`Commands`/`IconCommand` + `AFTER_HELP`) moves to `src/lib.rs`, which exposes `pub fn cli() -> clap::Command`. The binary imports it and dispatches as before; the command handlers stay in the bin. This is the smallest lib surface that lets tooling introspect the real CLI. forge-docs-check: - New `clidoc` module: introspects `forge_cli::cli()` and renders an authoritative reference (synopsis, arguments, options, nested subcommands) into the `<!-- forge:cli -->` region of crates/forge.md. Authored prose in the narrative `## Commands` section sits outside the markers and is never touched. - New `cli-doc` rule (wired into run_all_checks) fails when that region is stale; `make docs-cli` / `--write-cli` regenerates it. Mirrors the api-block/example-block marker-hybrid pattern. - `cli-command` rule refactored from regex source-parsing to clap introspection (`forge_cli::cli()`), deleting ~120 lines of brace/heck-mangling parsing and the source-move fragility — the clap model is now the literal source of truth. Docs/config: forge.md gains the generated `## CLI reference` section and a `lib.rs` entry in its file-structure tree; DOCUMENTATION.md documents the `cli-doc` (and previously-undocumented `ext-index`) rules and `make docs-cli`; Makefile adds the `docs-cli` target. Tests: clidoc unit tests (every subcommand, flags/positionals, nested icon, help filtered out) + a fixture drift test (stale region flagged, fresh region passes, un-opted page skipped). The cli-command fixture test updated for introspection. Verification: drift gate in sync; forge-docs-check 18 rules + docs_sync + clidoc; forge_cli 15 + 10; fmt --all + clippy -D warnings clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @LayerDynamics, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
There was a problem hiding this comment.
Code Review
This pull request refactors the forge CLI command model by moving it from the binary entry point to a library crate (forge_cli), allowing external tooling to introspect the CLI surface. It introduces a new cli-doc rule in forge-docs-check to automatically generate and verify the CLI reference in crates/forge.md directly from the live clap model, eliminating manual source-text parsing. Feedback on these changes suggests normalizing CRLF line endings to LF when reading and writing the CLI reference to prevent cross-platform test failures on Windows, as well as simplifying a string join operation in the value_name helper to avoid redundant allocations.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| match markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) { | ||
| Some((_, _, body)) if body == expected => Vec::new(), |
There was a problem hiding this comment.
To prevent test failures on Windows platforms where files might be checked out with CRLF line endings, normalize the line endings of the extracted region to LF (\n) before comparing it with the expected generated body.
| match markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) { | |
| Some((_, _, body)) if body == expected => Vec::new(), | |
| match markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) { | |
| Some((_, _, body)) if body.replace("\r\n", "\n") == expected => Vec::new(), |
| if let Some((_, _, body)) = markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) { | ||
| if body == expected { | ||
| return Ok(Vec::new()); | ||
| } |
There was a problem hiding this comment.
Similarly, normalize CRLF line endings to LF in write_all to avoid rewriting the file unnecessarily when the only difference is the line endings (CRLF vs LF).
| if let Some((_, _, body)) = markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) { | |
| if body == expected { | |
| return Ok(Vec::new()); | |
| } | |
| if let Some((_, _, body)) = markers::find_region(&page, BLOCK_OPEN, BLOCK_CLOSE) { | |
| if body.replace("\r\n", "\n") == expected { | |
| return Ok(Vec::new()); | |
| } |
| if let Some(names) = arg.get_value_names() { | ||
| if !names.is_empty() { | ||
| return names | ||
| .iter() | ||
| .map(|n| n.to_string()) | ||
| .collect::<Vec<_>>() | ||
| .join(" "); | ||
| } | ||
| } |
There was a problem hiding this comment.
Since names is a slice of String (or &str), you can directly call names.join(" ") instead of mapping and collecting into a temporary Vec. This is more concise, readable, and avoids unnecessary allocations.
if let Some(names) = arg.get_value_names() {
if !names.is_empty() {
return names.join(" ");
}
}b76e3d4
into
docs-pipeline-p52-clap-migration
#33) ## Summary Phase **P5.3** (final phase) of the docs-autogeneration pipeline: generate the `forge` CLI reference **directly from the clap command model** and gate it against drift. > **Re-opened against `main`.** The original #32 was stacked on the P5.2 branch and got merged into *that* branch instead of `main`, so P5.3 never landed on `main` (`clidoc.rs` / `forge_cli/src/lib.rs` were missing). This PR targets `main` directly. `main` already contains the P5.2 fork point, so the diff is just the single P5.3 commit (plus the merge of current `main`). ## What changed **`forge_cli` → lib + bin split** - The clap model (`Cli`/`Commands`/`IconCommand` + `AFTER_HELP`) moves to `src/lib.rs`, exposing `pub fn cli() -> clap::Command`. The binary imports it and dispatches as before; handlers stay in the bin. **`forge-docs-check`** - New `clidoc` module: introspects `forge_cli::cli()` and renders an authoritative reference (synopsis, arguments, options, nested `icon` subcommands) into the `<!-- forge:cli -->` region of `crates/forge.md`. Authored prose stays outside the markers. - New **`cli-doc`** rule fails when that region is stale; `make docs-cli` / `--write-cli` regenerates it. - **`cli-command`** rule refactored from regex source-parsing to clap introspection — deletes ~120 lines of brace/heck-mangling. **Docs/config:** `forge.md` generated `## CLI reference` + `lib.rs` in the file tree; `DOCUMENTATION.md` documents the `cli-doc`/`ext-index` rules and `make docs-cli`; `Makefile` adds `docs-cli`. ## Test plan - [x] Merged with current `main` (branding + rusty_v8 CI cache fix); zero conflicts - [x] Drift gate **in sync**; forge-docs-check 18 rules + docs_sync + clidoc + cli-doc fixture - [x] `forge_cli` 15 + 10; build, `fmt --all --check`, `clippy -D warnings` all clean - [x] CI now inherits the `~/.cargo/.rusty_v8` cache from `main`, so the V8-download 504s can't gate it 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
Phase P5.3 (final phase) of the docs-autogeneration pipeline: generate the
forgeCLI reference directly from the clap command model and gate it against drift. The published docs and the binary's argument parser are now derived from the same model and cannot diverge.What changed
forge_cli→ lib + bin splitCli/Commands/IconCommand+AFTER_HELP) moves tosrc/lib.rs, which exposespub fn cli() -> clap::Command. The binary imports it and dispatches as before; command handlers stay in the bin. Smallest lib surface that lets tooling introspect the real CLI.forge-docs-checkclidocmodule: introspectsforge_cli::cli()and renders an authoritative reference (synopsis, arguments, options, nestediconsubcommands) into the<!-- forge:cli -->region ofcrates/forge.md. Authored prose in the narrative## Commandssection sits outside the markers and is never touched.cli-docrule (wired intorun_all_checks): fails when the region is stale.make docs-cli/--write-cliregenerates it. Mirrors theapi-block/example-blockmarker-hybrid pattern (and inherits its CRLF-safe comparison).cli-commandrule refactored from regex source-parsing to clap introspection — deletes ~120 lines of brace/heck-mangling and the source-move fragility. The clap model is now the literal source of truth.Docs/config
forge.md: new generated## CLI referencesection + alib.rsentry in the file-structure tree.DOCUMENTATION.md: documents thecli-doc(and previously-undocumentedext-index) rules andmake docs-cli.Makefile: adds thedocs-clitarget.Test plan
cli-docrule passes)forge-docs-check: 18 fixture rules +docs_syncratchet + 3clidocunit tests + newcli-docfixture (stale flagged / fresh passes / un-opted skipped)forge_cli: 15 unit + 10 characterizationcargo fmt --all --check+clippy -D warningscleanNotes
clidoc::render_block_body()prefixes the literal"forge"(not the binary name), so the.exeissue that bit P5.2's test cannot leak into the generated docs.forge-docs-check → forge_cli → forge-etch/forge-smelt, nothing back.🤖 Generated with Claude Code