Python Project Scaffold
This skill creates a multi-repo workspace skeleton for a new Python project. It sets up the directory structure and a root CLAUDE.md that describes each sub-project's purpose, how they connect, and which existing skills to run next. No application code is generated — actual code generation is deferred to existing skills.
When to Use This Skill
Use this skill when:
- Starting a brand-new Python project from scratch
- You want the standard multi-repo workspace layout (models lib, core lib, Flask backend)
- You want a root CLAUDE.md that guides future development with existing skills
What This Skill Creates
- Directory skeleton — empty sub-project directories under
{project}/ {project}/CLAUDE.md— root development guide describing each sub-project and which skills to use- A
.gitignorein each sub-project — Python/Node ignore patterns, written where they actually take effect (the workspace root is deliberately NOT a git repo — see Step 4) - Next-steps documentation — tells the user which skills to run in each sub-project
Step 1: Gather Project Information
IMPORTANT: Before creating anything, ask the user these questions using AskUserQuestion:
-
"What is your project name?" (e.g., "arcana", "trading-bot", "my-app")
- Derive naming variants:
{project}— kebab-case (e.g.,arcana,trading-bot){project_name}— snake_case (e.g.,arcana,trading_bot){ProjectName}— PascalCase (e.g.,Arcana,TradingBot){PROJECT_NAME}— UPPER_SNAKE (e.g.,ARCANA,TRADING_BOT)
- Derive naming variants:
-
"Brief project description?" (one or two sentences for the CLAUDE.md header)
-
"What is the GitHub org or owner?" (e.g.,
jmazzahacks) -
"Which optional sub-projects do you need?" (multi-select)
python-scripts— standalone utility scripts{project}-api-python— Python API client library{project}-api-ts— TypeScript API client library{project}-frontend— Next.js frontend
-
"Does the backend need Celery + Redis for background tasks?" (yes/no)
-
"Which license?"
- Proprietary
- MIT
- O'Saasy (https://osaasy.dev/)
Step 2: Create Directory Structure
Create empty directories under {project}/ with mkdir -p.
Do not add .gitkeep files. Each sub-project is its own git repo (Step 5) and
the workspace root is not a repo at all, so there is nothing tracking these
directories as empty — and every one of them gets populated by the setup skill
run inside it.
Always created:
{project}/
├── {project}-models/
├── {project}-core/
└── {project}-backend/
Conditionally created based on Step 1 answers:
├── python-scripts/ # if "python-scripts" selected
├── {project}-api-python/ # if Python API client selected
├── {project}-api-ts/ # if TypeScript API client selected
└── {project}-frontend/ # if Next.js frontend selected
Step 3: Create Root CLAUDE.md
Create {project}/CLAUDE.md with the following structure. Replace all {project}, {project_name}, {ProjectName}, and {PROJECT_NAME} placeholders with actual values.
# {ProjectName} — Development Guide
{description}
## Project Structure
This is a multi-repo workspace. Each sub-directory is an independent project with its own virtual environment, git history, and dependencies.
| Directory | Purpose | Type |
|-----------|---------|------|
| `{project}-models/` | Shared data models and schemas | pip package (library) |
| `{project}-core/` | Business logic and service layer | pip package (library) |
| `{project}-backend/` | Flask REST API server | Docker service |
{# Include rows for optional sub-projects only if selected: }
{# | `python-scripts/` | Standalone utility scripts | Scripts | }
{# | `{project}-api-python/` | Python API client library | pip package (library) | }
{# | `{project}-api-ts/` | TypeScript API client library | npm package | }
{# | `{project}-frontend/` | Next.js frontend application | Docker service | }
## Dependency Chain
{project}-models → {project}-core → {project}-backend
- **{project}-models** has no internal dependencies. It defines shared data models.
- **{project}-core** depends on `{project}-models`. It contains business logic.
- **{project}-backend** depends on both `{project}-models` and `{project}-core`.
## Setting Up Each Sub-Project
### {project}-models (shared models library)
Use the `python-lib-setup` skill to initialize this as a pip package:
cd {project}-models
Invoke python-lib-setup skill
### {project}-core (business logic library)
Use the `python-lib-setup` skill to initialize this as a pip package:
cd {project}-core
Invoke python-lib-setup skill
Add `{project}-models` as a GitHub dependency in `pyproject.toml` — **TOKEN-FREE
URL regardless of public/private.** Never embed `{env:CR_PAT}` (or any auth) in
`pyproject.toml`: hatchling expands it at wheel build time and bakes the live
PAT into the wheel's `.dist-info/METADATA` `Requires-Dist`, so every published
image carries a valid credential invisible to `docker inspect`. Auth for
private repos is supplied at INSTALL time (a `${CR_PAT}` line in the consuming
app's `requirements.txt`, or git `url.insteadOf` config — see the
`uv-supply-chain-hardening` skill for the full pattern).
```toml
dependencies = [
# Public OR private — always token-free here. Install-time auth handles
# private access; see uv-supply-chain-hardening Step 1 for the callout.
"{project}-models @ git+https://github.com/{github_org}/{project}-models.git",
]
{project}-backend (Flask API server)
Set up in this order:
flask-smorest-api— Flask app factory, blueprints, dataclass models, pytest smoke testpostgres-setup— Database schema and setup script. Run its Step 7 to scaffold the resilientDatabasedriver atsrc/{project_name}/database.py— required for the Flask process to survive Postgres restarts; skipping it leads to a wedged pool on the next upstream PG restart.flask-docker-deployment— Dockerfile, build script, versioningbyteforge-loki-logging— Structured logging to Grafana Lokiuv-supply-chain-hardening— Lock the dependency install: pin every third-party dep with hashes, gate on release age, pin the uv binary by digest, and route the private-dep token through a build-time secret so it never enterspyproject.tomlor the running image. Run this AFTER 1–4 — it hardens what's already there rather than emitting new app code. Not optional: every scaffold that skipped this ended up in a downstream ticket for a leakedCR_PATin the wheelRequires-Distmetadata.
Add model and core libraries as GitHub dependencies in requirements.txt:
# Public repos:
{project}-models @ git+https://github.com/{github_org}/{project}-models.git
{project}-core @ git+https://github.com/{github_org}/{project}-core.git
# Private repos (requires CR_PAT environment variable):
# {project}-models @ git+https://${CR_PAT}@github.com/{github_org}/{project}-models.git
# {project}-core @ git+https://${CR_PAT}@github.com/{github_org}/{project}-core.git
{# Include this section only if Celery + Redis was selected: }
Celery + Redis
This backend uses Celery for background task processing with Redis as the broker. Environment variables:
{PROJECT_NAME}_REDIS_URL— Redis connection URL (e.g.,redis://localhost:6379/0)
{# Include this section only if {project}-frontend was selected: }
{project}-frontend (Next.js frontend)
Use the aegis-nextjs-frontend skill to scaffold the frontend:
cd {project}-frontend
# Invoke aegis-nextjs-frontend skill
{# Include this section only if python-scripts was selected: }
python-scripts (utility scripts)
Standalone scripts for development, data migration, or maintenance tasks. Each script should:
- Have its own
#!/usr/bin/env pythonshebang - Use
python-dotenvto load.env - Import from
{project}-modelsand{project}-coreas needed
{# Include this section only if {project}-api-python was selected: }
{project}-api-python (Python API client)
Use the python-lib-setup skill to initialize this as a pip package:
cd {project}-api-python
# Invoke python-lib-setup skill
{# Include this section only if {project}-api-ts was selected: }
{project}-api-ts (TypeScript API client)
Initialize as a TypeScript npm package. Publish to GitHub Packages or npm.
Development Commands
Each sub-project with Python uses its own virtual environment, created at the
sub-project root (not in a bin/ or .venv/ subdirectory) so that activate
lands at bin/activate:
cd {project}-models/
python3 -m venv .
source bin/activate
pip install --upgrade -r requirements.txt
Note (Python 3.13+):
python3 -m venv .writes a.gitignorecontaining just*into the venv root — which is the sub-project root under this convention — so re-creating the venv overwrites that sub-project's.gitignore. Create the venv first, then write.gitignore; if you re-create the venv later, restore the tracked file withgit checkout .gitignore.
Run tests:
source bin/activate && pytest
Start the backend locally:
cd {project}-backend/
source bin/activate && python {project_name}.py
Conventions
- Unix timestamps only — All date/time fields use
BIGINT(epoch seconds), neverTIMESTAMPorDATETIME - UUID primary keys — Use
gen_random_uuid()in PostgreSQL - RealDictCursor — Always use
psycopg2.extras.RealDictCursorfor queries - Environment variables — Project-specific prefix:
{PROJECT_NAME}_(e.g.,{PROJECT_NAME}_DB_HOST) - Type hints — All function parameters and return types must have type annotations
- No lambdas — Use named functions or loops instead
- Virtual environments — Always
source bin/activatebefore running Python - No local path dependencies — NEVER use
pip install -e ../sibling-projectorfile:references. Cross-repo dependencies MUST use GitHub URLs.pyproject.toml— always TOKEN-FREE, public OR private:git+https://github.com/{github_org}/pkg.git. Never embed{env:CR_PAT}here — hatchling expands it at wheel build time and bakes the live PAT intoRequires-Distmetadata, leaking a credential into every published image.requirements.txt— token inline is safe (requirements files are never packaged):git+https://${CR_PAT}@github.com/{github_org}/pkg.git.- Auth for private deps declared in a
pyproject.tomlis supplied at INSTALL time — via a${CR_PAT}line in the consuming app'srequirements.txt, or giturl.insteadOfconfig. Theuv-supply-chain-hardeningskill wires this end-to-end for the backend.
**IMPORTANT**: When generating the actual CLAUDE.md file:
- Remove all `{# ... }` comment lines
- Only include sections for sub-projects that were selected in Step 1
- Replace all placeholders with actual values
- Do NOT wrap the entire file in a code fence — write it as a real markdown file
## Step 4: Create a .gitignore in Each Sub-Project
**The workspace root is NOT a git repo** — each sub-project is (see Step 5). So a
`.gitignore` at `{project}/` governs nothing. Write one into **each sub-project
directory** instead, where it actually takes effect. Write them now — they are not
the last word, for the reason below.
**CRITICAL — these get clobbered later, and must be re-written.** Since Python
3.13, `python3 -m venv <path>` writes a `.gitignore` containing just `*` into the
venv root. The convention here (and in `python-lib-setup`) puts the venv at the
sub-project root, so the file you write now is overwritten with `*` the moment the
Step 5 setup skill creates that sub-project's venv — and a subsequent
`git init && git add .` then silently ignores the entire sub-project. That is why
Step 5 item 3 exists: re-write these files after the setup skills have run, and
verify each one is the content below rather than a bare `*`. (`python-lib-setup`
re-writes its own; `{project}-backend/` and any sub-project whose setup skill does
not is on you.) Once a good version has been **committed**, `git checkout
.gitignore` recovers from a later clobber.
Contents for each Python sub-project (`{project}-models/`, `{project}-core/`,
`{project}-backend/`, `python-scripts/`, `{project}-api-python/`) — the whole
block below, minus the Node section. For `{project}-frontend/` and
`{project}-api-ts/`, use the Node, Environment, IDE, OS, and Docker sections and
drop the Python and virtual-environment ones.
```gitignore
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
*.egg-info/
*.egg
dist/
build/
*.whl
# Virtual environments
bin/
lib/
# No trailing slash: Linux venvs can make lib64 a symlink to lib.
lib64
# uv writes this transient lock into the venv root (= project root here).
/.lock
pyvenv.cfg
include/
share/
# Environment
.env
*.env.local
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Docker
VERSION
# Node (frontend / TypeScript client only)
node_modules/
.next/
out/
Step 5: Document Next Steps
After creating all files, tell the user:
- Initialize git in each SUB-PROJECT —
cd {project}-models/ && git init, and likewise for every other sub-project. Do NOTgit initthe workspace root. This is a multi-repo workspace:{project}/is a plain directory that holds independent repos. A root repo would swallow every sub-project as untracked content, which is the opposite of the intended layout. - Run skills in order for each sub-project:
cd {project}-models/→ runpython-lib-setupcd {project}-core/→ runpython-lib-setupcd {project}-backend/→ runflask-smorest-api, thenpostgres-setup, thenflask-docker-deployment, thenbyteforge-loki-logging, thenuv-supply-chain-hardening(locks + hashes the dep install, gates release-age, and routes the private-dep token as a build secret so it never lands inpyproject.tomlor the image — the default for new Python projects)- (If frontend selected)
cd {project}-frontend/→ runaegis-nextjs-frontend - (If Python API client selected)
cd {project}-api-python/→ runpython-lib-setup
- Re-write the per-sub-project
.gitignorefiles (Step 4) — creating each venv overwrote them with a bare*. Check every sub-project and restore the Step 4 content before the firstgit add ., or git will silently ignore the whole package. - Create GitHub repos for each sub-project under
{github_org}/ - Set up
.envfiles with required environment variables
Reporting problems with this skill
If you find a bug or stale instruction while using this skill:
- If HiveMake is connected and
byteforge-skills-maintainer-agentis available, file a ticket with that agent. Usediscover_agentsto find its project ID. - Otherwise, open an issue at GitHub Issues. If you cannot submit a report, give the human the details and this link.
Include the skill name, plugin version if known, the instruction followed, expected and actual results, relevant errors/runtime versions, and any fix or workaround that worked. Omit credentials and private data.
Address the user's immediate problem first; report the skill defect afterward.