PythonUtils_Scaffolder
Section titled “PythonUtils_Scaffolder”[AIStudio.google.com chat](https://drive.google.com/file/d/10tkXz8SdrKN5dWkaHBze6uihW2uzvNMf/view?usp=sharing, https://drive.google.com/file/d/14YImc_fu4Zq1uxOWIgxPBPdUYmJsfnxN/view?usp=sharing, https://drive.google.com/file/d/14nYNfPyobNo1jYyWWv5E2RHKtpZWPmHe/view?usp=sharing, https://aistudio.google.com/app/prompts?state=%7B%22ids%22:%5B%221HPhQ2V2n55KQbp5c0BYsLchpmx3RQEq3%22%5D,%22action%22:%22open%22,%22userId%22:%22110125519267764925776%22,%22resourceKeys%22:%7B%7D%7D&usp=sharing, https://drive.google.com/file/d/1mAduP71SBZqSFvW-zzgfJ9M4G4t5DRJB/view?usp=sharing, https://drive.google.com/file/d/1tx_mGt4Ila3gRibMPFqLUCJl_v6E6H2I/view?usp=sharing, https://drive.google.com/file/d/1umE3dyb8kPr1ES2sErtEqn5lIY5qW4jF/view?usp=sharing)
### ⏸ status: PAUSED (Jan 23, 2026)**Context:** Environment cleaned. `uv` adopted. Scaffolder plan defined but code NOT written.
**Next Actions:**1. [ ] Create the `_scaffolder/templates` folder.2. [ ] Save the YAML/TOML templates (from the chat log below) into that folder.3. [ ] Write the `scaffold.py` script to tie it all together.==> config.yaml (IMPORTANT) - “.git/lfs” # INCLUDE .git repos, but EXCLUDE large files, in .git/lfs ==> change from my_util.yaml to config.yaml
==> ALWAYS use notify_manager to report errors/issues
d:\FSS\Software\Utils\PythonUtils\notify_manager\
==> define AI SKILLS to convert config.yaml to CLI interface (Typer), and Q&A UX (multiple choice with defaults for efficiency) - all utils should have a CLI UX
==> upgrade debugging, to use Loguru, Icecream - per Tech Stack??
Here are the adjustments and the decision on the Web projects.
1. Decisions & Adjustments
Section titled “1. Decisions & Adjustments”A. File Naming (main.py vs. my_util.py)
We will standardize on my_util.py (matching the project name).
- Why: When you have 5 tabs open in VS Code, seeing
main.py,main.py, andmain.pyis a nightmare. Seeingbackup.py,deploy.py, andmonitor.pyis clear. - The
uvconnection: We will configurepyproject.tomlsouvknows thatmy_util.pyis the entry point.
B. Shell I will strictly use PowerShell syntax in examples going forward.
C. Scope: Web Projects? Recommendation: Keep them separate.
- Why: Astro and Modern Web Apps have their own powerful CLIs (
npm create astro@latest,npx, etc.) that are updated frequently. Wrapping them in a Python script adds unnecessary maintenance. - Separation of Concerns: Your
PythonUtilsare typically “System/Data Tools” (Windows automation, backups). Your Web Projects are “Products” (Apps). They usually belong in different root folders (e.g.,D:\FSS\Software\WebApps\). - Verdict: Let
_scaffolderbe the master of your Python domain only.
2. Next Steps: Building _scaffolder
Section titled “2. Next Steps: Building _scaffolder”Let’s build this utility now. We will start with the Blueprint (Step 1) and the Templates (Step 2).
Action:
Create the folder: D:\FSS\Software\Utils\PythonUtils\_scaffolder\
Create a subfolder: D:\FSS\Software\Utils\PythonUtils\_scaffolder\templates\
Step 1: The Blueprint (scaffold.yaml)
Section titled “Step 1: The Blueprint (scaffold.yaml)”Save this file in _scaffolder\scaffold.yaml.
This controls what gets created.
# The Blueprint for FSS Python Utilities
defaults: description: "FSS Utility created via _scaffolder" version: "0.1.0" python_version: ">=3.12"
# Files to generatestructure: # 1. The Entry Point (e.g., my_util/my_util.py) - path: "{name}/{name}.py" template: "util_script.py.j2" update_behavior: "skip" # Never overwrite code logic
# 2. Configuration (e.g., my_util/config.yaml) - path: "{name}/config.yaml" template: "config.yaml.j2" update_behavior: "skip" # Preserve user settings
# 3. Dependency Management (pyproject.toml) - path: "{name}/pyproject.toml" template: "pyproject.toml.j2" update_behavior: "merge" # Advanced: We might want to inject ruff config later
# 4. Documentation - path: "{name}/README.md" template: "README.md.j2" update_behavior: "skip"
# 5. Secrets (Template only) - path: "{name}/.env.example" template: ".env.example.j2" update_behavior: "overwrite"
# 6. Yamllint Config (for pre-commit) - path: "{name}/.yamllint" template: ".yamllint.j2" update_behavior: "overwrite"
# Folders to ignore in Git (Standardized)gitignore: - "__pycache__/" - "*.pyc" - ".env" - ".venv/" - ".idea/" - ".vscode/" - "dist/" - "build/"Step 2: The Critical Templates
Section titled “Step 2: The Critical Templates”Save these files in _scaffolder\templates\.
A. pyproject.toml.j2 (The Modern uv setup)
Notice the [project.scripts] section—this allows you to run your tool via uv run {name} easily.
[project]name = "{name}"version = "0.1.0"description = "FSS Utility: {name}"readme = "README.md"requires-python = ">=3.12"dependencies = [ "pyyaml", "python-dotenv", "rich",]
[project.scripts]# Allows running "uv run {name}" from anywhere{name} = "{name}:main"
[build-system]requires = ["hatchling"]build-backend = "hatchling.build"
# --- RUFF CONFIGURATION (Standardized) ---[tool.ruff]line-length = 100target-version = "py312"
[tool.ruff.lint]select = ["E", "F", "I", "B", "SIM"]ignore = []
[tool.ruff.format]quote-style = "double"B. util_script.py.j2 (Your Standard Python Boilerplate)
Standardizes logging, config loading, and structure.
"""Utility: {name}"""import sysimport loggingimport yamlfrom pathlib import Pathfrom dotenv import load_dotenvfrom rich.logging import RichHandler
# 1. Setup Logging (Rich)logging.basicConfig( level="INFO", format="%(message)s", datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=True)])log = logging.getLogger("rich")
# 2. Config Loaderdef load_config(): """Safe loads config.yaml relative to this script.""" config_path = Path(__file__).parent / "config.yaml" if not config_path.exists(): log.warning("⚠ config.yaml not found. Using defaults.") return {}
with open(config_path, "r", encoding="utf-8") as f: try: return yaml.safe_load(f) except yaml.YAMLError as e: log.error(f"Failed to parse config.yaml: {e}") sys.exit(1)
def main(): # Load Environment & Config load_dotenv() config = load_config()
app_name = config.get("app_name", "{name}") log.info(f"🚀 Starting {{app_name}}...")
# --- YOUR CODE HERE ---
log.info("Done.")
if __name__ == "__main__": main()C. config.yaml.j2 (The Safe YAML Standard)
# Configuration for {name}# NOTE: Use explicit double quotes for strings to avoid parsing errors.
app_name: "{name}"version: "0.1.0"
# Example Settings (Remove or Modify)# input_path: "D:/Data/Input"# dry_run: "true" # Use string "true"/"false" if parsing manually, or unquoted true/false for booleansD. .yamllint.j2 (The Quality Control)
extends: default
rules: line-length: disable truthy: level: warning check-keys: falseWhat to do now
Section titled “What to do now”- Create the files listed above in your
_scaffolderfolder. - Confirm once they are created.
- We will then write the final piece:
scaffold.py, which ties it all together usinguvto install the dependencies automatically.
LF/CRLF Issue
Section titled “LF/CRLF Issue”To solve this permanently for all future projects, we must add .gitattributes to your _scaffolder blueprints.
How to update the Scaffolder Plan
Section titled “How to update the Scaffolder Plan”Add this to your D:\FSS\KB\Business\_WorkingOn\Later\PythonUtils_Scaffolder.md note:
1. Add to scaffold.yaml (The Blueprint)
Section titled “1. Add to scaffold.yaml (The Blueprint)”In the structure: section, add this entry. We set update_behavior: overwrite because this file rarely needs customization and should always enforce the standard.
# 7. Git Line Ending Normalization (Crucial for Windows/uv) - path: "{name}/.gitattributes" template: ".gitattributes.j2" update_behavior: "overwrite"2. Add the Template
Section titled “2. Add the Template”Create _scaffolder/templates/.gitattributes.j2 with the content I gave you above:
# Enforce LF for code (Standard for Python/uv/Prettier)* text=auto*.py text eol=lf*.toml text eol=lf*.yaml text eol=lf*.json text eol=lf*.md text eol=lfuv.lock text eol=lf.gitignore text eol=lf
# Enforce CRLF for Windows Scripts*.bat text eol=crlf*.cmd text eol=crlfWhy this is the “Professional Solution”
Section titled “Why this is the “Professional Solution””By baking this into the scaffolder:
- Zero Friction: You never see that warning again.
- Consistency: If you ever open these projects in WSL (Linux) or share them with a collaborator (or an AI agent running on Linux), the line endings won’t spontaneously change and show 1,000 modified lines in Git.
- Tool Compatibility:
uv,ruff, andprettierall prefer LF endings. This forces Windows to play nice with modern tools.
🧩 Future Feature: Scaffolder “Advanced Mode”
Section titled “🧩 Future Feature: Scaffolder “Advanced Mode””The current plan supports simple single-file scripts. We need a --type package flag for larger utilities.
Similar to this …
- D:\FSS\Software\Utils\PythonUtils\my_backup\
- │
- ├── config.yaml # Stays at root
- ├── pyproject.toml # Stays at root
- ├── README.md
- │
- └── src\ # NEW FOLDER
- └── my_backup\ # NEW PACKAGE FOLDER
- ├── init.py # Empty file (marks it as a package)
- ├── main.py # The Conductor (imports others)
- ├── config.py # Config loading & Env var expansion
- ├── utils.py # Logging, run_command, JSON parsing
- └── tasks.py # The heavy logic (Kopia, Robocopy, Pre-hooks)
- └── my_backup\ # NEW PACKAGE FOLDER
How …
Requirements:
- CLI Flag:
python scaffold.py my_tool --type package - Structure Generation:
Instead of
my_tool/my_tool.py, it generates:src/my_tool/__init__.pysrc/my_tool/main.pysrc/my_tool/config.pysrc/my_tool/utils.py
- TOML Update:
Automatically sets
[project.scripts]tomy_tool = "my_tool.main:main"instead ofmy_tool:main.