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"Sprout reads the template's questions and exposes them as CLI flags. Provide answers as flags to skip prompts; those questions won't appear in the TUI.
usage: sprout [-h] [--force] [--<question-flag> <value> ...] 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 --<question-flag> answer a template question and skip the prompt 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]: ...
Questions can be conditionally shown with when. The condition can be a bool or a callable that receives previously collected answers and returns True/False.
from sprout import Question questions = [ Question( key="include_ci", prompt="Set up CI?", choices=[("yes", "Yes"), ("no", "No")], default="yes", ), Question( key="ci_provider", prompt="Which CI provider should we use?", choices=[("github", "GitHub Actions"), ("gitlab", "GitLab CI")], when=lambda answers: answers.get("include_ci") == "yes", ), ]Notes:
- Conditions are evaluated in question order, so dependencies should come earlier in the list.
- If a question is skipped by
when, its key is omitted fromanswers. - Explicit CLI flags still win; if
--ci-provideris passed, the value is used even whenwhenwould be false.
Use Question.yes_no(...) to avoid manually wiring choices and a bool parser.
from sprout import Question questions = [ Question.yes_no( key="create_github_repo", prompt="Create GitHub repository now?", default=False, ), ]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