A Jinja2 project generator with Python-based configuration
uv tool install "git+https://github.com/zigai/sprout.git"pip install "git+https://github.com/zigai/sprout.git"usage: sprout [-h] [--force] template destination
generate a project from a sprout manifest (questions with optional apply)
positional arguments:
template path or git repository containing a sprout.py manifest
destination target directory for the generated project
options:
-h, --help show this help message and exit
--force overwrite files in the destination directory if they already exist
The template repository or directory must contain a file named sprout.py at its root.
Define these module-level names:
Define one of:
-
A sequence of
sprout.Question:questions: Sequence[sprout.Question]
-
A callable that returns a sequence of
Question:from jinja2 import Environment from pathlib import Path from typing import Sequence def questions(env: Environment, destination: Path) -> Sequence[Question]: ...
Path to the templates directory. Relative paths resolve from the repository root. Default: template.
Function to skip rendering or copying specific files. relative_path is relative to template_dir.
def should_skip_file(relative_path: str, answers: dict[str, Any]) -> bool: ...A sprout.style.Style instance to customize prompt appearance.
A sequence of Jinja2 Extension subclasses to add to the environment.
A string to print before prompting, or a callable that returns a string.
Custom generation logic.
- If omitted: sprout renders all files from
template_dirto the destination, with Jinja rendering enabled for files ending in.jinjaand for relative paths. - If provided: the function may request any of these parameters by name:
env,template_dir,template_root,destination,answers,style,console,render_templates.
from sprout import Question
questions = [
Question(key="project_name", prompt="Project name"),
]
template_dir = "template"
def should_skip_file(relative_path: str, answers: dict[str, Any]) -> bool:
if relative_path == "LICENSE.jinja" and answers.get("copyright_license") == "None":
return True
return False