Template Authoring Guide
How to build your own fledge templates.
Overview
A template is a directory with a template.toml manifest and whatever files you want. Files get rendered through Tera (Jinja2-style) before being written to the output.
Directory Structure
my-template/
├── template.toml # manifest (required)
├── src/
│ └── main.rs # template files, Tera syntax works here
├── README.md
├── Cargo.toml
└── .github/
└── workflows/
└── ci.yml
template.toml Reference
This is where you define metadata, prompts, file rules, and hooks.
Basic Structure
[template]
name = "my-template"
description = "A short description"
min_fledge_version = "1.0.0" # optional
[prompts]
description = { message = "Project description", default = "A new project" }
port = { message = "Default port", default = "3000" }
# Defaults can reference earlier variables:
repo_url = { message = "Repository URL", default = "https://github.com/{{ github_org }}/{{ project_name }}" }
[files]
render = ["**/*.rs", "**/*.toml", "**/*.md", "**/*.yml"]
copy = ["**/*.png", "**/*.ico"]
ignore = ["template.toml"]
[hooks]
post_create = ["cargo fmt", "npm install"]
[template] section
| Key | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | What you pass to --template |
description | string | Yes | Shows up in fledge templates list |
version | string | No | Template version (informational, shown in init summary) |
min_fledge_version | string | No | Minimum fledge version needed (checked before rendering) |
requires | string[] | No | Tools that must be on PATH (e.g. ["node", "npm"]) |
[prompts] section
Each key becomes a template variable that gets prompted to the user.
[prompts]
# With a default
description = { message = "Project description", default = "A new project" }
# No default, user has to answer
main_author = { message = "Primary author" }
# Default can reference other variables
repo_url = { message = "Repository URL", default = "https://github.com/{{ github_org }}/{{ project_name }}" }
[files] section
Controls which files get rendered, copied, or skipped.
render- process through Teracopy- copy as-is, even if arenderglob would otherwise match (use this for binary assets, images, fonts, anything you don’t want Tera to touch)ignore- skip entirely (file is not written to the project)
Files ending in .tera are always rendered (and the extension stripped) regardless of the globs. That is the explicit “render this” signal.
Precedence. First rule that matches wins:
ignoreglob → skip.teraextension → render, strip extensioncopyglob → copy bytes verbatimrenderglob → render through Tera- Default (nothing matched) → copy bytes
So a broad render = ["**/*"] is safe as long as binary assets are listed under copy. They bypass Tera even though the render glob would catch them.
[files]
render = ["**/*.rs", "**/*.toml", "**/*.md", "**/*.yml"]
copy = ["**/*.png", "**/*.ico", "assets/**"]
ignore = ["template.toml", "node_modules/**"]
[hooks] section
Commands that run after scaffolding, inside the new project directory.
[hooks]
post_create = ["cargo fmt", "cargo test", "git add -A && git commit -m 'Initial commit'"]
Built-in templates run hooks automatically. Remote templates show the commands and ask for confirmation (unless you pass --yes).
Built-in Variables
These are always available. You don’t need to define them in [prompts]:
| Variable | What it is | Example |
|---|---|---|
project_name | Name as the user typed it | my-cool-app |
project_name_snake | Snake case | my_cool_app |
project_name_pascal | PascalCase | MyCoolApp |
author | From config, git, or prompted | Leif |
github_org | From config or prompted | CorvidLabs |
license | From config, defaults to MIT | MIT |
year | Current year | 2026 |
date | Current date | 2026-04-18 |
Tera Syntax
Templates use Tera syntax. Here’s the stuff you’ll actually use:
Variables
# {{ project_name }}
{{ description }}
Conditionals
{% if license == "MIT" %}
This project is MIT licensed.
{% endif %}
Loops
{% for dep in dependencies %}
- {{ dep }}
{% endfor %}
Filters
Project slug: {{ project_name | slugify }}
Uppercase: {{ author | upper }}
Putting it together
# {{ project_name }}
{{ description }}
## Author
Created by {{ author }} ({{ github_org }}) in {{ year }}.
{% if license == "MIT" %}
This project is MIT licensed.
{% endif %}
## Quick Start
```
cd {{ project_name_snake }}
cargo build
```
Quick Start with fledge templates create
The fastest way to start a new template:
fledge templates create my-template
fledge templates create my-template --hooks --prompts --yes
This scaffolds a ready-to-edit template directory with template.toml and example files. See the CLI reference for all options.
Building a Template from Scratch
1. Make the directory
mkdir python-api && cd python-api
2. Write template.toml
[template]
name = "python-api"
description = "Python FastAPI project with Docker"
[prompts]
description = { message = "Project description", default = "A FastAPI application" }
python_version = { message = "Python version", default = "3.12" }
[files]
render = ["**/*.py", "**/*.toml", "**/*.md", "**/*.yml", "Dockerfile"]
ignore = ["template.toml"]
[hooks]
post_create = ["python -m venv .venv"]
3. Add your files
# app/main.py
"""{{ description }}"""
from fastapi import FastAPI
app = FastAPI(title="{{ project_name_pascal }}")
@app.get("/")
def root():
return {"name": "{{ project_name }}"}
# Dockerfile
FROM python:{{ python_version }}-slim
WORKDIR /app
COPY . .
RUN pip install -e .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
4. Test it
fledge templates init test-api --template ./python-api --dry-run
fledge templates init test-api --template ./python-api
Testing
Add your template directory to config:
# ~/.config/fledge/config.toml
[templates]
paths = ["~/dev/my-templates"]
Or point at it directly:
fledge templates init test-project --template ./my-template
The loop is: edit files → fledge templates init test-output --template my-template → check the output → delete test output → repeat.
Sharing
GitHub
Push your template to a GitHub repo. Anyone can use it with:
fledge templates init my-app --template user/my-template
Template Repos
Users can register your repo so it shows up in fledge templates list:
[templates]
repos = ["CorvidLabs/fledge-templates", "myorg/templates"]
Tips
- Name it clearly.
python-apibeatstemplate-1. - Write a README. Explain what the template does and what variables it uses.
- Default everything you can. Only prompt for things that actually vary.
- Test your hooks. Make sure
post_createcommands handle missing tools gracefully. - Use
min_fledge_versionif you depend on newer features.