Skip to content

Migrate forge_cli to clap derive + re-point cli-command drift rule (P5.2)#31

Merged
LayerDynamics merged 5 commits into
mainfrom
docs-pipeline-p52-clap-migration
Jun 8, 2026
Merged

Migrate forge_cli to clap derive + re-point cli-command drift rule (P5.2)#31
LayerDynamics merged 5 commits into
mainfrom
docs-pipeline-p52-clap-migration

Conversation

@LayerDynamics

Copy link
Copy Markdown
Owner

Summary

Phase P5.2 of the docs-autogeneration pipeline: migrate the forge CLI from a hand-rolled env::args() parser to clap derive, and update the documentation-drift gate to track the new command model.

CLI migration

  • forge_cli/src/main.rs now uses #[derive(Parser)] Cli + #[derive(Subcommand)] enum Commands, with dispatch in main().
  • Generated --help / --version, real clap error messages, and per-subcommand help.
  • Contract preserved exactly: dev/build/bundle/smelt/sign/icon/docs; -o/--out, --embed, -i/--identity, the artifact positional, icon create/validate. Hand-written usage extras carried over via clap after_help.
  • docs keeps trailing_var_arg pass-through so its sub-flags still forward to the docs generator unchanged.

Drift-rule follow-through

The migration deleted the forge <a|b|c> usage banner that the cli-command drift rule parsed. That rule is re-pointed at the clap Commands enum (the new single source of truth): it brace-matches the enum body, harvests top-level variant names, and applies clap's default PascalCase→kebab-case rename to recover the exact subcommand names. forge smelt-style "real subcommand but undocumented" detection keeps working.

Test plan

  • Documentation drift gate: in sync, 0 issues
  • forge-docs-check: 15/15 (incl. updated clap-enum fixture)
  • forge_cli: 15 unit + 10 characterization pass
  • cargo fmt --check + cargo clippy -D warnings clean on both crates

Notes

  • The characterization tests (P5.1) were intentionally rewritten: clap's edge-case behavior (no-args, unknown subcommand, bad flag) now exits non-zero with clap errors instead of the old hand-parser's exit-0 usage print. The preserved command/flag/alias/positional surface is asserted so a future change can't silently drop one.
  • Independent of the open branding PR feat: Forge brand icon as the default scaffolded app icon #30 (feat-forge-branded-icon); the two main.rs changesets touch disjoint regions.

🤖 Generated with Claude Code

…5.2)

Replace the hand-rolled `env::args()` parser in forge_cli with clap derive:
a `#[derive(Parser)] Cli` + `#[derive(Subcommand)] enum Commands` model.
This yields generated `--help`/`--version`, real error messages, and per-
subcommand help, while preserving the exact command/flag/positional contract
(dev/build/bundle/smelt/sign/icon/docs; `-o/--out`, `--embed`, `-i/--identity`,
the artifact positional, icon create/validate). The hand-written usage extras
are carried over via clap `after_help`. `docs` keeps trailing-var-arg
pass-through so its sub-flags still forward to the docs generator unchanged.

Because the clap migration removed the `forge <a|b|c>` usage banner that the
`cli-command` documentation-drift rule parsed, re-point that rule at the new
source of truth: the clap `Commands` enum. The rule now brace-matches the
`enum Commands { .. }` body, harvests top-level variant names, and applies
clap's default PascalCase->kebab-case rename to recover the exact subcommand
names the CLI exposes. This keeps `forge smelt`-style "real subcommand but
undocumented" detection working post-migration.

- forge_cli/src/main.rs: clap Parser/Subcommand model; dispatch in main().
- forge_cli/Cargo.toml: add clap with the derive feature.
- forge_cli/tests/cli_characterization.rs: rewrite P5.1 snapshots to clap's
  contract (edge cases now exit non-zero with clap errors) + assert the
  preserved command/flag/alias/positional surface.
- forge-docs-check/src/checks/cli_commands.rs: source subcommands from the
  clap enum instead of the deleted usage banner.
- forge-docs-check/tests/rules.rs: update the fixture to the clap-enum shape.

Verification: drift gate in sync; forge-docs-check 15/15; forge_cli 15+10
tests; fmt + clippy clean on both crates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @LayerDynamics, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request migrates the forge CLI from a hand-rolled argument parser to a clap-derive model, updating command-line parsing, help generation, and error handling across the application. It also updates the documentation-checking tool (forge-docs-check) to extract subcommands directly from the Commands enum in the source code rather than a hand-written usage banner. Feedback on the pull request highlights that the custom brace-matching parser used in the documentation checker could fail if braces are present within comments or string literals, and suggests stripping comments and literals before parsing to ensure robustness.

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.

Comment thread crates/forge-docs-check/src/checks/cli_commands.rs
LayerDynamics and others added 2 commits June 7, 2026 13:35
… output

`every_subcommand_has_help` panicked only on windows-latest: clap renders its
`Usage:` line from the real binary file name, which is `forge.exe` on Windows.
The assertion `contains("forge {cmd}")` then failed because the infix `.exe`
splits `forge` from the subcommand (`Usage: forge.exe dev ...`).

Normalize `forge.exe` -> `forge` once in the shared `combined()` helper so all
string assertions hold across platforms. No-op on Unix (the suffix never
appears). The CLI itself was always correct; this was a unix-only test
assumption.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Note that the PascalCase->kebab split matches clap/heck only for single-word
variants (all current forge subcommands); multi-word acronyms would diverge.
Documentation-only; no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LayerDynamics and others added 2 commits June 8, 2026 03:15
The `v8` crate's build script downloads a large prebuilt static lib from
GitHub releases (denoland/rusty_v8) into ~/.cargo/.rusty_v8 at build time.
That directory was not covered by any cache, so every cold build re-downloaded
it — and an intermittent `HTTP Error 504: Gateway Time-out` from the releases
endpoint panicked v8's build.rs (assertion failed: status.success()), failing
Clippy/Check/Test/cross-platform jobs even though the code was sound.

Cache ~/.cargo/.rusty_v8 explicitly:
- ci.yml (check, test matrix, clippy): a dedicated actions/cache step keyed on
  Cargo.lock with a `${{ runner.os }}-rusty_v8-` restore-keys prefix, so the
  binary is restored even when Cargo.lock changes (the v8 version rarely moves)
  and is downloaded at most once per OS.
- cross-platform.yml: add `cache-directories: ~/.cargo/.rusty_v8` to the
  Swatinem/rust-cache step (rust-cache doesn't cover it by default).

docs.yml is unchanged: forge-docs-check's dependency graph contains no
deno_core/v8 (verified via cargo tree), so that job never performs the download.

Validated with actionlint (exit 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LayerDynamics LayerDynamics merged commit fd0513a into main Jun 8, 2026
20 checks passed
LayerDynamics added a commit that referenced this pull request Jun 8, 2026
#32)

## 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. The published docs and the binary's
argument parser are now derived from the *same* model and cannot
diverge.

> **Stacked on #31 (P5.2).** This PR's base is the P5.2 branch, so the
diff shows only the single P5.3 commit. Once #31 merges to `main`,
retarget this PR to `main` (`gh pr edit --base main`).

## What changed

**`forge_cli` → lib + bin split**
- 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; command handlers stay in the
bin. 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 `icon`
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 the
region is stale. `make docs-cli` / `--write-cli` regenerates it. Mirrors
the `api-block`/`example-block` marker-hybrid pattern (and inherits its
CRLF-safe comparison).
- **`cli-command`** rule 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 reference` section + a `lib.rs`
entry in the 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.

## Test plan
- [x] Drift gate: **in sync, 0 issues** (new `cli-doc` rule passes)
- [x] `forge-docs-check`: 18 fixture rules + `docs_sync` ratchet + 3
`clidoc` unit tests + new `cli-doc` fixture (stale flagged / fresh
passes / un-opted skipped)
- [x] `forge_cli`: 15 unit + 10 characterization
- [x] `cargo fmt --all --check` + `clippy -D warnings` clean
- [ ] Windows CI green on the drift gate (the new surface) — watched on
this PR

## Notes
- `clidoc::render_block_body()` prefixes the literal `"forge"` (not the
binary name), so the `.exe` issue that bit P5.2's test cannot leak into
the generated docs.
- No dependency cycle: `forge-docs-check → forge_cli →
forge-etch/forge-smelt`, nothing back.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant