cargo-generate: scaffold a project from a git template repo. Use when bootstrapping a Rust or Bevy project from a template, or authoring one.
user-invocable
false
allowed-tools
Bash, Read, Write, Edit, Grep, Glob, TodoWrite
cargo-generate — Project Scaffolding from Git Templates
cargo-generate expands a git repository of real files into a new project,
substituting Liquid expressions in file
contents and in path names. It is the Rust ecosystem's standard scaffolding
tool — Bevy, wasm-pack, and most framework starters ship templates for it.
When to Use This Skill
Use this skill when...
Use the alternative instead when...
Starting from a published template (bevy_new_2d, wasm-pack-template)
Starting a plain binary or library — cargo new is enough (see rust-development)
Authoring a template so a project shape can be reproduced
Applying standards to an existing repo — cargo-generate only generates once (see configure-plugin)
A scaffold needs prompts, variants, or derived values
The scaffold is a fixed file set with no substitution — copy it
It is a cargo subcommand (cargo generate) and also a standalone binary
(cargo-generate), which is what a test harness should invoke.
Generating a project
# From a GitHub template (gh:/gl:/bb:/sr: prefixes also work)
cargo generate --git https://github.com/TheBevyFlock/bevy_new_2d --name my_game
# From a local directory — the form to use while authoring a template
cargo generate --path ./templates/my-template --name my-project
# Pin to a branch, tag, or revision
cargo generate --git <url> --branch main --name my-project
# Expand into the current directory instead of creating a new one
cargo generate --git <url> --init
Nothing about the tool is Rust-specific: a template needs no Cargo.toml,
and generating a TypeScript, Python, or config-only project works the same way.
Non-interactive use
This is what makes cargo-generate usable from a skill or CI job — prompts would
otherwise block:
Case filters: kebab_case, snake_case, pascal_case, lower_camel_case,
upper_camel_case, title_case, shouty_snake_case, shouty_kebab_case.
Path names are rendered too, so styles/{{ module_id }}.css works.
Rhai hooks
Use a hook for values that should be derived, not asked for — anything a
prompt could get inconsistent with something else:
let name = variable::get("project-name");
if !name.starts_with("prefix-") {
abort("name must start with 'prefix-'");
}
variable::set("short_name", name.sub_string(7));
let now = system::date();
variable::set("year", "" + now.year); // strings only — see gotchas
Available: variable::get/set/is_set/prompt, file::exists/rename/delete/write/listdir,
system::date, system::command (needs --allow-commands), abort, and the
to_kebab_case()-style converters.
Gotchas
Liquid eats other {{ }} languages. GitHub Actions ${{ … }}, Handlebars,
Jinja, and Vue interpolations all render to the empty string if the file is
templated. Two fixes:
Situation
Fix
The file needs no substitution
[template] exclude it — copied verbatim, never rendered
The file needs both
Wrap the foreign expressions in {% raw %}…{% endraw %}, or assign the literal brace once: {%- assign hb = "{{" -%} then {{ hb }}localize "…"}}
Only an opening {{ needs escaping; a bare }} means nothing to Liquid.
variable::set takes strings only. Passing an integer — including
system::date().year — fails with
Function not found: variable::set (…, i64). Concatenate with "" first.
The project name is kebab-cased before hooks run.--name My_Project
becomes my-project silently. A hook cannot reject a malformed name because it
never sees one; if the exact spelling matters, validate before invoking.
.liquid is a magic suffix. A file named x.rs.liquid is rendered and
emitted as x.rs. To ship a literal .liquid file, double the extension.
foundryvtt-plugin/templates/foundryvtt-module/ is a worked example — three
variants via [conditional], a derived id via a Rhai pre-hook, both brace
collisions above, and a parity test against the generator it ports. See
foundryvtt-plugin/templates/README.md.