Skip to content

rtb generate

Scaffold something from the embedded templates. Five kinds: project, command, flag, setting, docs.

Every kind except project requires an existing scaffolded tree — they read .rtb/manifest.yaml and fail without it.

rtb generate project [PATH]

Render a preset into a new directory.

Argument Type Default Notes
PATH (positional) path ./<name> Destination directory. Must not already exist.
-n, --name <NAME> string Tool name. Must match ^[a-z][a-z0-9-]{0,63}$ and not be a reserved name. Required under --non-interactive; otherwise the wizard asks.
-d, --description <TEXT> string empty One-line description written into the generated Cargo.toml and the tool's clap about. At most 500 bytes after NFC normalisation, no control characters except tab, and no {{ or }}.
--preset <NAME> string minimal Which bundled preset to render. See presets.
--non-interactive flag off Never prompt. Every required field must arrive as a flag.
--no-git flag off Skip git init and the initial commit.

What it writes

Six files, plus a git repository unless --no-git is given:

$ rtb generate project --name greeter --description "A greeting tool" --non-interactive
Created `greeter` (6 files written to greeter)
greeter/
├── .git/
├── .gitignore
├── .rtb/manifest.yaml
├── Cargo.toml
├── README.md
└── src/
    ├── commands/mod.rs
    └── main.rs

When it fails

  • The destination exists. generate never overwrites; it errors and suggests removing the directory or choosing another destination. This is what keeps the verb safe to re-run.
  • --non-interactive with a missing required field. One diagnostic lists every missing field at once, rather than failing on the first.
  • The name or description fails validation. See validation rules.
  • The preset is not bundled. Unknown preset names are a hard error; there is no silent fallback to minimal.

rtb generate command <COMMAND_PATH>

Add a subcommand to a scaffolded tool. Writes src/commands/<name>.rs and registers it between the // rtb:commands-begin / -end markers in src/commands/mod.rs.

Argument Type Default Notes
COMMAND_PATH (positional) string Command name. Required unless a protect / unprotect sub-verb is used.
--about <ABOUT> string Scaffolded subcommand Short help text for the command.
--short <CHAR> char One-character alias. Recorded in the manifest.
--aliases <A,B,C> comma list Alternative command names. Deduplicated; each validated as a command name.
--long <TEXT> string falls back to --about Long help shown in the command's own --help.
--args <name:type,…> comma list Scaffold arguments alongside the command. Equivalent to one generate flag per entry; the type defaults to string.
--provider <PROVIDER> string anthropic AI provider for --prompt / --script. See environment variables.
--model <MODEL> string provider default Model id override.
--prompt <TEXT-OR-PATH> string Natural-language description of the behaviour, or a path to a file holding one.
--script <PATH> path A bash / python / js script to port into the command body.
--agentless flag off One-shot generation; skip the verify-and-repair loop.
--max-repair-iterations <N> u32 5 Cap on repair attempts.

Sub-verbs

  • rtb generate command protect <COMMAND_PATH> — add the command's file to the manifest's protected: list. regenerate project then always skips it, and remove refuses it without --force.
  • rtb generate command unprotect <COMMAND_PATH> — reverse that.

When it fails

  • The name contains /. Nested command paths are rejected with "nested command paths are not yet supported", even though validate_command_path accepts up to four segments.
  • The command already exists in the manifest, or the target file exists on disk.
  • --prompt or --script on the cli preset. AI codegen targets the minimal preset only; on cli it errors before touching disk.

The AI call, when requested, happens before anything is written, so a provider failure leaves no half-scaffolded command behind and the command can simply be re-run.

rtb generate flag <FLAG_NAME>

Add a clap argument to an already-scaffolded command.

Argument Type Default Notes
FLAG_NAME (positional) string Long name without the leading --. Passing --name rather than name is rejected with a message saying so.
-c, --command <COMMAND_PATH> string Required. Target command, matching the file basename under src/commands/.
--type <TY> enum string string, bool, int, float, string_slice. Aliases below.
-d, --description <TEXT> string empty Rendered as .help("…").
-s, --shorthand <CHAR> char One-character short flag.
--required flag off Renders clap::Arg::required(true).
--persistent flag off Renders clap::Arg::global(true).
--default <VALUE> string Default value as a string literal. Ignored when --type bool.
--default-is-code flag off Not implemented — setting it is an error.
--provider <PROVIDER> / --model <MODEL> string Draft the help text with an AI when --description is omitted.

Accepted --type values

Canonical Also accepted Renders
string str plain value argument
bool boolean, flag .action(clap::ArgAction::SetTrue)
int i64, integer .value_parser(clap::value_parser!(i64))
float f64, number .value_parser(clap::value_parser!(f64))
string_slice stringslice, strings .action(clap::ArgAction::Append)

Anything else is rejected and the message lists the five canonical names.

rtb generate setting <FIELD_NAME>

Add a persistent setting — a field on the tool's AppConfig struct in src/main.rs, inserted between the // rtb:settings-begin / -end markers.

Argument Type Default Notes
FIELD_NAME (positional) string See the naming constraint below.
--type <TY> enum string Same names as generate flag; rendered as String, bool, i64, f64, Vec<String>.
--default <VALUE> string Written as a // default: <value> comment above the field. It does not change the runtime value.
-p, --path <PATH> path cwd Project root.

The name must be a single lowercase word

The help text calls FIELD_NAME a "snake_case Rust identifier", but the value is checked with the flag-name validator, which requires ^[a-z][a-z0-9-]{0,63}$. In practice:

  • greeting — accepted, renders pub greeting: String,.
  • default_namerejected: invalid setting name: flag_name: must match ^[a-z][a-z0-9-]{0,63}$.
  • default-name — accepted by the validator, but renders pub default-name: String,, which is not valid Rust and will not compile.

Until that is fixed, keep setting names to one lowercase word. See what RTB does not do.

--default is documentation only

The value is rendered as a comment; the field still takes its type's Default at runtime:

// rtb:settings-begin
// default: hello
pub greeting: String,
// rtb:settings-end

rtb generate docs

Write one Markdown page per scaffolded command, plus an index.

Argument Type Default Notes
-p, --path <PATH> path cwd Project root.
--provider <PROVIDER> string Run the AI prose pass over each page. Omit for the mechanical extraction only.
--model <MODEL> string provider default Model id override.
--page-by-page flag off Accepted but not implemented — it changes nothing.

Where the pages land

Into docs/components/ in the target project, hardcoded. The path is not configurable, and it does not match the Diátaxis layout this project now uses (reference/cli/ for command pages). Generated pages are recorded in the manifest's generated: map so regenerate manifest keeps them in sync.

The two passes

  1. Extraction (always). Reads each scaffolded command file, pulls the string literals out of fn name, fn about and the flag long names, and writes a structural page.
  2. AI prose (only with --provider). Sends each extracted page to the provider and replaces it with the returned prose. The index page is deliberately skipped — its content is mechanical.

The AI pass rewrites pages in place with no diff and no confirmation step, so run it on a clean working tree.