start-vibing-stacks 2.5.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/detector.js CHANGED
@@ -114,10 +114,8 @@ export function detectNodeFramework(projectDir) {
114
114
  return null;
115
115
  }
116
116
  export function detectPythonFramework(projectDir) {
117
- // Check manage.py (Django)
118
117
  if (existsSync(join(projectDir, 'manage.py')))
119
118
  return 'django';
120
- // Check for FastAPI in requirements or pyproject
121
119
  for (const reqFile of ['requirements.txt', 'pyproject.toml', 'Pipfile']) {
122
120
  const filePath = join(projectDir, reqFile);
123
121
  if (existsSync(filePath)) {
@@ -128,5 +126,10 @@ export function detectPythonFramework(projectDir) {
128
126
  return 'flask';
129
127
  }
130
128
  }
129
+ // If Python project detected but no web framework, suggest scripts
130
+ if (existsSync(join(projectDir, 'main.py')) &&
131
+ !existsSync(join(projectDir, 'app', 'main.py'))) {
132
+ return 'scripts';
133
+ }
131
134
  return null;
132
135
  }
package/dist/scanner.js CHANGED
@@ -322,6 +322,69 @@ function scanEslintConfig(projectDir) {
322
322
  ],
323
323
  };
324
324
  }
325
+ // ─── Python Scanners ───────────────────────────────────────────────────────
326
+ const PYTHON_PACKAGES = {
327
+ 'fastapi': { category: 'framework', name: 'FastAPI framework' },
328
+ 'django': { category: 'framework', name: 'Django framework' },
329
+ 'flask': { category: 'framework', name: 'Flask framework' },
330
+ 'uvicorn': { category: 'server', name: 'Uvicorn ASGI server' },
331
+ 'gunicorn': { category: 'server', name: 'Gunicorn WSGI server' },
332
+ 'sqlalchemy': { category: 'database', name: 'SQLAlchemy ORM' },
333
+ 'alembic': { category: 'database', name: 'Alembic migrations' },
334
+ 'asyncpg': { category: 'database', name: 'asyncpg (PostgreSQL async)' },
335
+ 'psycopg': { category: 'database', name: 'psycopg (PostgreSQL)' },
336
+ 'mariadb': { category: 'database', name: 'MariaDB connector' },
337
+ 'pymongo': { category: 'database', name: 'PyMongo (MongoDB)' },
338
+ 'beanie': { category: 'database', name: 'Beanie ODM (MongoDB)' },
339
+ 'motor': { category: 'database', name: 'Motor (MongoDB async)' },
340
+ 'pydantic': { category: 'validation', name: 'Pydantic v2 validation' },
341
+ 'pydantic-settings': { category: 'config', name: 'Pydantic Settings' },
342
+ 'httpx': { category: 'http', name: 'httpx HTTP client' },
343
+ 'tenacity': { category: 'reliability', name: 'tenacity retry logic' },
344
+ 'celery': { category: 'queue', name: 'Celery task queue' },
345
+ 'arq': { category: 'queue', name: 'ARQ async task queue' },
346
+ 'redis': { category: 'cache', name: 'Redis client' },
347
+ 'pytest': { category: 'testing', name: 'pytest testing' },
348
+ 'pytest-asyncio': { category: 'testing', name: 'pytest async support' },
349
+ 'mypy': { category: 'quality', name: 'mypy type checker' },
350
+ 'ruff': { category: 'quality', name: 'ruff linter + formatter' },
351
+ 'rich': { category: 'ui', name: 'rich CLI output' },
352
+ 'typer': { category: 'cli', name: 'Typer CLI framework' },
353
+ 'click': { category: 'cli', name: 'Click CLI framework' },
354
+ 'stripe': { category: 'billing', name: 'Stripe payments' },
355
+ 'google-ads': { category: 'ads', name: 'Google Ads API' },
356
+ 'facebook-business': { category: 'ads', name: 'Facebook/Meta Ads API' },
357
+ 'python-wordpress-xmlrpc': { category: 'cms', name: 'WordPress XML-RPC client' },
358
+ };
359
+ function scanPyprojectToml(projectDir) {
360
+ const content = readFileIfExists(join(projectDir, 'pyproject.toml')) ||
361
+ readFileIfExists(join(projectDir, 'requirements.txt'));
362
+ if (!content)
363
+ return null;
364
+ const patterns = [];
365
+ const lowerContent = content.toLowerCase();
366
+ for (const [pkg, meta] of Object.entries(PYTHON_PACKAGES)) {
367
+ if (lowerContent.includes(pkg.toLowerCase())) {
368
+ patterns.push({
369
+ category: meta.category,
370
+ name: meta.name,
371
+ confidence: 95,
372
+ detail: `Found: ${pkg}`,
373
+ });
374
+ }
375
+ }
376
+ const pythonVersionMatch = content.match(/requires-python\s*=\s*"([^"]+)"/i);
377
+ if (pythonVersionMatch) {
378
+ patterns.push({
379
+ category: 'runtime',
380
+ name: `Python version constraint: ${pythonVersionMatch[1]}`,
381
+ confidence: 100,
382
+ });
383
+ }
384
+ if (patterns.length === 0)
385
+ return null;
386
+ return { source: 'pyproject.toml', patterns };
387
+ }
325
388
  // ─── Shared Scanners ───────────────────────────────────────────────────────
326
389
  function scanProjectFiles(projectDir) {
327
390
  const patterns = [];
@@ -399,6 +462,33 @@ function scanProjectFiles(projectDir) {
399
462
  else if (existsSync(join(projectDir, 'yarn.lock'))) {
400
463
  patterns.push({ category: 'runtime', name: 'Yarn package manager in use', confidence: 100 });
401
464
  }
465
+ // ── Python: Project configs ──
466
+ if (existsSync(join(projectDir, 'pyproject.toml'))) {
467
+ patterns.push({ category: 'runtime', name: 'pyproject.toml present (modern Python)', confidence: 100 });
468
+ }
469
+ if (existsSync(join(projectDir, 'uv.lock'))) {
470
+ patterns.push({ category: 'runtime', name: 'uv package manager in use', confidence: 100 });
471
+ }
472
+ else if (existsSync(join(projectDir, 'Pipfile.lock'))) {
473
+ patterns.push({ category: 'runtime', name: 'Pipenv package manager in use', confidence: 100 });
474
+ }
475
+ if (existsSync(join(projectDir, 'mypy.ini')) || existsSync(join(projectDir, '.mypy.ini'))) {
476
+ patterns.push({ category: 'quality', name: 'mypy config present', confidence: 100 });
477
+ }
478
+ if (existsSync(join(projectDir, 'ruff.toml')) || existsSync(join(projectDir, '.ruff.toml'))) {
479
+ patterns.push({ category: 'quality', name: 'ruff config present', confidence: 100 });
480
+ }
481
+ if (existsSync(join(projectDir, 'alembic.ini'))) {
482
+ patterns.push({ category: 'database', name: 'Alembic migrations present', confidence: 100 });
483
+ }
484
+ if (existsSync(join(projectDir, 'pytest.ini')) || existsSync(join(projectDir, 'pyproject.toml'))) {
485
+ if (existsSync(join(projectDir, 'tests'))) {
486
+ patterns.push({ category: 'testing', name: 'pytest test directory present', confidence: 90 });
487
+ }
488
+ }
489
+ if (existsSync(join(projectDir, '.pre-commit-config.yaml'))) {
490
+ patterns.push({ category: 'quality', name: 'pre-commit hooks configured', confidence: 100 });
491
+ }
402
492
  // ── Deploy targets ──
403
493
  if (existsSync(join(projectDir, 'vercel.json'))) {
404
494
  patterns.push({ category: 'deploy', name: 'Vercel deployment config', confidence: 100 });
@@ -485,6 +575,7 @@ export function scanProjectStandards(projectDir) {
485
575
  scanPackageJson,
486
576
  scanTsConfig,
487
577
  scanEslintConfig,
578
+ scanPyprojectToml,
488
579
  scanProjectFiles,
489
580
  ];
490
581
  const results = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-vibing-stacks",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "AI-powered multi-stack dev workflow for Claude Code. Supports PHP, Node.js, Python and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,11 +6,13 @@
6
6
 
7
7
  ```
8
8
  What are you building?
9
- ├── API / Microservices → FastAPI (async, Pydantic, fast)
10
- ├── Full-stack / CMS / Admin → Django (batteries-included)
11
- ├── Simple / Script → Flask (minimal)
12
- ├── AI/ML API serving → FastAPI (Pydantic, uvicorn)
13
- └── Background workers Celery + any framework
9
+ ├── API / Microservices → FastAPI (async, Pydantic, fast)
10
+ ├── Full-stack / CMS / Admin → Django (batteries-included)
11
+ ├── Lightweight web app → Flask (minimal)
12
+ ├── AI/ML API serving → FastAPI (Pydantic, uvicorn)
13
+ ├── Local scripts / automation Scripts (httpx, argparse, rich)
14
+ ├── WordPress / Ads / ETL → Scripts (no framework needed)
15
+ └── Background workers → Celery + any framework
14
16
  ```
15
17
 
16
18
  ## Async vs Sync
@@ -74,6 +76,20 @@ myproject/
74
76
  └── tests/
75
77
  ```
76
78
 
79
+ ### Local Scripts / Automation
80
+ ```
81
+ project/
82
+ ├── main.py # CLI entry (argparse + match/case)
83
+ ├── scripts/ # One module per task
84
+ ├── lib/
85
+ │ ├── config.py # Pydantic Settings (.env)
86
+ │ ├── http_client.py # httpx + tenacity retry
87
+ │ └── logger.py # rich logging
88
+ ├── data/ # Input/output files
89
+ ├── logs/
90
+ └── tests/
91
+ ```
92
+
77
93
  ## Error Handling
78
94
 
79
95
  ```python
@@ -0,0 +1,255 @@
1
+ # Local Scripts & Automation — Python 3.12+
2
+
3
+ **ALWAYS invoke when building local scripts, CLI tools, or automation tasks.**
4
+
5
+ ## When to Use
6
+
7
+ - WordPress API automation (create/update posts, manage media)
8
+ - Ad campaign management (Google Ads, Facebook Ads, TikTok Ads API)
9
+ - Data pipelines (CSV/Excel processing, database sync)
10
+ - Web scraping and data extraction
11
+ - File system operations and batch processing
12
+ - Scheduled tasks and cron-like automation
13
+ - API integrations without a web framework
14
+
15
+ ## Project Structure
16
+
17
+ ```
18
+ project/
19
+ ├── pyproject.toml # Dependencies + project metadata
20
+ ├── .env # API keys, credentials (NEVER commit)
21
+ ├── .env.example # Template without real values
22
+ ├── scripts/
23
+ │ ├── __init__.py
24
+ │ ├── wordpress.py # WordPress automation
25
+ │ ├── ads_manager.py # Ad campaigns
26
+ │ └── data_sync.py # Database sync
27
+ ├── lib/
28
+ │ ├── __init__.py
29
+ │ ├── http_client.py # Reusable httpx client
30
+ │ ├── config.py # Pydantic Settings
31
+ │ ├── logger.py # Structured logging
32
+ │ └── retry.py # Retry with backoff
33
+ ├── data/ # Input/output data files
34
+ ├── logs/ # Log files
35
+ ├── tests/
36
+ │ └── test_scripts.py
37
+ └── main.py # Entry point / CLI
38
+ ```
39
+
40
+ ## Configuration (Pydantic Settings)
41
+
42
+ ```python
43
+ from pydantic_settings import BaseSettings
44
+
45
+ class Settings(BaseSettings):
46
+ WP_URL: str
47
+ WP_USER: str
48
+ WP_APP_PASSWORD: str
49
+
50
+ GOOGLE_ADS_DEVELOPER_TOKEN: str = ""
51
+ FACEBOOK_ACCESS_TOKEN: str = ""
52
+ TIKTOK_ACCESS_TOKEN: str = ""
53
+
54
+ DB_HOST: str = "localhost"
55
+ DB_PORT: int = 3306
56
+ DB_NAME: str
57
+ DB_USER: str
58
+ DB_PASSWORD: str
59
+
60
+ LOG_LEVEL: str = "INFO"
61
+ DRY_RUN: bool = False
62
+
63
+ model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
64
+
65
+ settings = Settings()
66
+ ```
67
+
68
+ ## HTTP Client (reusable)
69
+
70
+ ```python
71
+ import httpx
72
+ from tenacity import retry, stop_after_attempt, wait_exponential
73
+
74
+ class ApiClient:
75
+ def __init__(self, base_url: str, auth: tuple[str, str] | None = None):
76
+ self.client = httpx.Client(
77
+ base_url=base_url,
78
+ auth=auth,
79
+ timeout=30.0,
80
+ headers={"User-Agent": "AutomationScript/1.0"},
81
+ )
82
+
83
+ @retry(stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=10))
84
+ def get(self, path: str, **kwargs) -> dict:
85
+ r = self.client.get(path, **kwargs)
86
+ r.raise_for_status()
87
+ return r.json()
88
+
89
+ @retry(stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=10))
90
+ def post(self, path: str, **kwargs) -> dict:
91
+ r = self.client.post(path, **kwargs)
92
+ r.raise_for_status()
93
+ return r.json()
94
+
95
+ def close(self):
96
+ self.client.close()
97
+ ```
98
+
99
+ ## WordPress REST API Pattern
100
+
101
+ ```python
102
+ from lib.http_client import ApiClient
103
+ from lib.config import settings
104
+
105
+ wp = ApiClient(
106
+ base_url=f"{settings.WP_URL}/wp-json/wp/v2",
107
+ auth=(settings.WP_USER, settings.WP_APP_PASSWORD),
108
+ )
109
+
110
+ def create_post(title: str, content: str, status: str = "draft") -> dict:
111
+ return wp.post("/posts", json={
112
+ "title": title,
113
+ "content": content,
114
+ "status": status,
115
+ })
116
+
117
+ def update_post(post_id: int, **fields) -> dict:
118
+ return wp.post(f"/posts/{post_id}", json=fields)
119
+
120
+ def bulk_update_posts(posts: list[dict]) -> list[dict]:
121
+ results = []
122
+ for post in posts:
123
+ pid = post.pop("id")
124
+ result = update_post(pid, **post)
125
+ results.append(result)
126
+ logger.info(f"Updated post {pid}: {result['title']['rendered']}")
127
+ return results
128
+ ```
129
+
130
+ ## Database Access (direct, no ORM)
131
+
132
+ ```python
133
+ import mariadb
134
+ from contextlib import contextmanager
135
+ from lib.config import settings
136
+
137
+ @contextmanager
138
+ def get_connection():
139
+ conn = mariadb.connect(
140
+ host=settings.DB_HOST,
141
+ port=settings.DB_PORT,
142
+ user=settings.DB_USER,
143
+ password=settings.DB_PASSWORD,
144
+ database=settings.DB_NAME,
145
+ )
146
+ try:
147
+ yield conn
148
+ finally:
149
+ conn.close()
150
+
151
+ def fetch_all(query: str, params: tuple = ()) -> list[dict]:
152
+ with get_connection() as conn:
153
+ cursor = conn.cursor(dictionary=True)
154
+ cursor.execute(query, params)
155
+ return cursor.fetchall()
156
+
157
+ def execute(query: str, params: tuple = ()) -> int:
158
+ with get_connection() as conn:
159
+ cursor = conn.cursor()
160
+ cursor.execute(query, params)
161
+ conn.commit()
162
+ return cursor.rowcount
163
+ ```
164
+
165
+ ## CLI Entry Point
166
+
167
+ ```python
168
+ import argparse
169
+ import logging
170
+ from lib.config import settings
171
+
172
+ logging.basicConfig(
173
+ level=getattr(logging, settings.LOG_LEVEL),
174
+ format="%(asctime)s [%(levelname)s] %(message)s",
175
+ handlers=[
176
+ logging.StreamHandler(),
177
+ logging.FileHandler("logs/script.log"),
178
+ ],
179
+ )
180
+ logger = logging.getLogger(__name__)
181
+
182
+ def main():
183
+ parser = argparse.ArgumentParser(description="Automation Scripts")
184
+ sub = parser.add_subparsers(dest="command")
185
+
186
+ sub.add_parser("wp-sync", help="Sync WordPress posts")
187
+ sub.add_parser("ads-report", help="Generate ads performance report")
188
+ sub.add_parser("db-migrate", help="Run data migration")
189
+
190
+ args = parser.parse_args()
191
+
192
+ if settings.DRY_RUN:
193
+ logger.warning("DRY RUN mode — no changes will be saved")
194
+
195
+ match args.command:
196
+ case "wp-sync":
197
+ from scripts.wordpress import sync_posts
198
+ sync_posts()
199
+ case "ads-report":
200
+ from scripts.ads_manager import generate_report
201
+ generate_report()
202
+ case "db-migrate":
203
+ from scripts.data_sync import run_migration
204
+ run_migration()
205
+ case _:
206
+ parser.print_help()
207
+
208
+ if __name__ == "__main__":
209
+ main()
210
+ ```
211
+
212
+ ## Essential Libraries
213
+
214
+ ```toml
215
+ # pyproject.toml
216
+ [project]
217
+ dependencies = [
218
+ "httpx>=0.27",
219
+ "pydantic-settings>=2.0",
220
+ "tenacity>=8.0",
221
+ "python-dotenv>=1.0",
222
+ "mariadb>=1.1",
223
+ "rich>=13.0",
224
+ ]
225
+
226
+ [project.optional-dependencies]
227
+ dev = ["pytest>=8.0", "mypy>=1.8", "ruff>=0.3"]
228
+ ads = ["google-ads>=24.0", "facebook-business>=19.0"]
229
+ ```
230
+
231
+ ## Logging (structured)
232
+
233
+ ```python
234
+ from rich.console import Console
235
+ from rich.logging import RichHandler
236
+ import logging
237
+
238
+ console = Console()
239
+ logging.basicConfig(
240
+ level="INFO",
241
+ format="%(message)s",
242
+ handlers=[RichHandler(console=console, rich_tracebacks=True)],
243
+ )
244
+ ```
245
+
246
+ ## FORBIDDEN
247
+
248
+ 1. **Hardcoded credentials** — use `.env` + Pydantic Settings
249
+ 2. **No error handling on API calls** — always try/except + retry
250
+ 3. **No logging** — every script must log actions and errors
251
+ 4. **`requests` library** — use `httpx` (modern, sync+async)
252
+ 5. **Print statements for output** — use `logging` or `rich`
253
+ 6. **No `--dry-run` flag** — destructive scripts must support dry run
254
+ 7. **SQL without parameterization** — always use `?` placeholders
255
+ 8. **No `.env.example`** — always provide template for credentials
@@ -2,7 +2,75 @@
2
2
  "id": "python",
3
3
  "name": "Python 3.12+",
4
4
  "icon": "🐍",
5
- "runtime": "python",
5
+ "runtime": "Python 3.12+",
6
+ "minVersion": "3.12.0",
7
+ "packageManager": "uv|pip",
8
+ "extensions": [".py", ".pyi"],
9
+ "testExtensions": ["test_*.py", "*_test.py"],
10
+ "detectFiles": ["pyproject.toml", "requirements.txt", "Pipfile", "setup.py", "manage.py"],
11
+ "commands": {
12
+ "test": "pytest --tb=short",
13
+ "lint": "ruff check .",
14
+ "format": "ruff format .",
15
+ "serve": "uvicorn app.main:app --reload",
16
+ "typecheck": "mypy ."
17
+ },
18
+ "qualityGates": [
19
+ { "name": "mypy", "command": "mypy .", "required": true, "order": 1 },
20
+ { "name": "ruff", "command": "ruff check .", "required": true, "order": 2 },
21
+ { "name": "pytest", "command": "pytest --tb=short", "required": true, "order": 3 }
22
+ ],
23
+ "frameworks": [
24
+ {
25
+ "id": "fastapi",
26
+ "name": "FastAPI + Uvicorn",
27
+ "icon": "⚡",
28
+ "detectFiles": ["app/main.py"],
29
+ "default": true,
30
+ "skills": ["fastapi-patterns", "async-patterns"]
31
+ },
32
+ {
33
+ "id": "django",
34
+ "name": "Django 5+",
35
+ "icon": "🎸",
36
+ "detectFiles": ["manage.py"],
37
+ "skills": ["django-patterns"]
38
+ },
39
+ {
40
+ "id": "flask",
41
+ "name": "Flask",
42
+ "icon": "🧪",
43
+ "detectFiles": ["app.py", "wsgi.py"],
44
+ "skills": []
45
+ },
46
+ {
47
+ "id": "scripts",
48
+ "name": "Local Scripts / Automation",
49
+ "icon": "🤖",
50
+ "skills": ["scripting-automation"]
51
+ }
52
+ ],
53
+ "databases": [
54
+ { "id": "mysql", "name": "MySQL / MariaDB", "icon": "🐬", "default": true },
55
+ { "id": "postgresql", "name": "PostgreSQL", "icon": "🐘" },
56
+ { "id": "sqlite", "name": "SQLite (local)", "icon": "📦" },
57
+ { "id": "mongodb", "name": "MongoDB", "icon": "🍃" },
58
+ { "id": "none", "name": "No database", "icon": "⬜" }
59
+ ],
60
+ "frontendOptions": [
61
+ { "id": "react", "name": "React (SPA)", "icon": "⚛️", "frameworks": ["fastapi", "flask", "django"] },
62
+ { "id": "htmx", "name": "HTMX + Jinja2", "icon": "🔄", "frameworks": ["fastapi", "flask", "django"] },
63
+ { "id": "none", "name": "API only / CLI only", "icon": "🔌", "default": true }
64
+ ],
65
+ "deployTargets": [
66
+ { "id": "github", "name": "GitHub (git push)", "icon": "🐙" }
67
+ ],
68
+ "skills": [
69
+ "python-patterns",
70
+ "pydantic-validation",
71
+ "pytest-testing",
72
+ "python-performance"
73
+ ],
6
74
  "requirements": [
7
75
  {
8
76
  "name": "Python",
@@ -26,39 +94,5 @@
26
94
  },
27
95
  "versionRegex": "pip\\s+(\\d+\\.\\d+\\.?\\d*)"
28
96
  }
29
- ],
30
- "frameworks": [
31
- { "id": "fastapi", "name": "FastAPI + Uvicorn", "icon": "⚡" },
32
- { "id": "django", "name": "Django 5+", "icon": "🎸" },
33
- { "id": "flask", "name": "Flask", "icon": "🧪" },
34
- { "id": "vanilla", "name": "Vanilla Python", "icon": "🐍" }
35
- ],
36
- "databases": [
37
- { "id": "postgresql", "name": "PostgreSQL", "icon": "🐘" },
38
- { "id": "mysql", "name": "MySQL / MariaDB", "icon": "🐬" },
39
- { "id": "mongodb", "name": "MongoDB", "icon": "🍃" },
40
- { "id": "sqlite", "name": "SQLite", "icon": "📦" }
41
- ],
42
- "frontendOptions": [
43
- { "id": "react", "name": "React (SPA)", "icon": "⚛️" },
44
- { "id": "htmx", "name": "HTMX + Jinja2", "icon": "🔄" },
45
- { "id": "none", "name": "API only (no frontend)", "icon": "🔌" }
46
- ],
47
- "deployTargets": [
48
- { "id": "github", "name": "GitHub (git push)", "icon": "🐙" }
49
- ],
50
- "skills": [
51
- "python-patterns",
52
- "fastapi-patterns",
53
- "django-patterns",
54
- "pydantic-validation",
55
- "pytest-testing",
56
- "async-patterns",
57
- "python-performance"
58
- ],
59
- "qualityGates": [
60
- { "name": "mypy", "command": "mypy .", "description": "Type checking" },
61
- { "name": "ruff", "command": "ruff check .", "description": "Linting" },
62
- { "name": "pytest", "command": "pytest --tb=short", "description": "Tests" }
63
97
  ]
64
98
  }
@@ -0,0 +1,315 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ > **CHARACTER LIMIT**: Max 40,000 chars. Validate with `wc -m CLAUDE.md` before commit.
4
+
5
+ ## Last Change
6
+
7
+ **Branch:** main
8
+ **Date:** {{DATE}}
9
+ **Summary:** Initial project setup with start-vibing-stacks (Python)
10
+
11
+ ## 30 Seconds Overview
12
+
13
+ {{PROJECT_NAME}} is a Python 3.12+ project using {{FRAMEWORK}}.
14
+
15
+ ## Stack
16
+
17
+ | Component | Technology |
18
+ |-----------|------------|
19
+ | Language | Python >= 3.12 |
20
+ | Framework | {{FRAMEWORK}} |
21
+ | Database | {{DATABASE}} |
22
+ | Type Checking | mypy (strict) |
23
+ | Linting | ruff |
24
+ | Testing | pytest + pytest-asyncio |
25
+ | Validation | Pydantic v2 |
26
+ | HTTP Client | httpx |
27
+ | Package Manager | uv / pip |
28
+
29
+ ## Architecture
30
+
31
+ ### FastAPI / Flask Projects
32
+
33
+ ```
34
+ project/
35
+ ├── CLAUDE.md # This file (40k char max)
36
+ ├── pyproject.toml # Dependencies + project config
37
+ ├── .env # Secrets (NEVER commit)
38
+ ├── .env.example # Template without values
39
+ ├── .claude/
40
+ │ ├── agents/ # 6 active subagents
41
+ │ ├── skills/ # Skill systems (auto-injected)
42
+ │ ├── hooks/ # Validation hooks
43
+ │ ├── config/ # Project configuration
44
+ │ └── commands/ # Slash commands
45
+ ├── app/
46
+ │ ├── main.py # App entrypoint + startup
47
+ │ ├── api/v1/
48
+ │ │ ├── routes/ # Endpoint modules
49
+ │ │ └── deps.py # Dependencies (auth, db)
50
+ │ ├── models/ # SQLAlchemy / Beanie models
51
+ │ ├── schemas/ # Pydantic schemas
52
+ │ ├── services/ # Business logic layer
53
+ │ ├── core/
54
+ │ │ ├── config.py # Pydantic Settings (env)
55
+ │ │ └── security.py # Auth, hashing
56
+ │ └── utils/ # Helpers
57
+ ├── scripts/ # CLI scripts / automation
58
+ ├── tests/
59
+ │ ├── conftest.py # Shared fixtures
60
+ │ ├── unit/
61
+ │ └── integration/
62
+ └── alembic/ # DB migrations (if SQL)
63
+ ```
64
+
65
+ ### Local Scripts / Automation Projects
66
+
67
+ ```
68
+ project/
69
+ ├── CLAUDE.md
70
+ ├── pyproject.toml
71
+ ├── .env
72
+ ├── .env.example
73
+ ├── .claude/
74
+ ├── main.py # CLI entry point (argparse)
75
+ ├── scripts/
76
+ │ ├── __init__.py
77
+ │ ├── wordpress.py # WordPress API automation
78
+ │ ├── ads_manager.py # Google/Facebook/TikTok Ads
79
+ │ └── data_sync.py # Database sync / ETL
80
+ ├── lib/
81
+ │ ├── __init__.py
82
+ │ ├── config.py # Pydantic Settings
83
+ │ ├── http_client.py # Reusable httpx client
84
+ │ ├── logger.py # Structured logging (rich)
85
+ │ └── retry.py # tenacity retry logic
86
+ ├── data/ # Input/output data files
87
+ ├── logs/ # Log files
88
+ └── tests/
89
+ ```
90
+
91
+ ### Django Projects
92
+
93
+ ```
94
+ project/
95
+ ├── CLAUDE.md
96
+ ├── pyproject.toml
97
+ ├── manage.py
98
+ ├── config/ # Settings, URLs, ASGI
99
+ ├── apps/
100
+ │ ├── users/ # Per-app: models, views, serializers, tests
101
+ │ └── products/
102
+ └── tests/
103
+ ```
104
+
105
+ ## Critical Rules
106
+
107
+ ### Python 3.12+ (MANDATORY)
108
+
109
+ - **Type hints on ALL public functions** — parameters, returns, class attributes
110
+ - **Pydantic v2 for all data boundaries** — API schemas, config, external data
111
+ - **`match/case`** for complex branching (3.10+)
112
+ - **`type` keyword** for simple type aliases (3.12+)
113
+ - **f-strings** everywhere, never `%` or `.format()`
114
+ - **Structural pattern matching** over nested if/elif chains
115
+
116
+ ### Code Organization
117
+
118
+ - **Thin routes, fat services** — business logic in `services/`, not in routes/views
119
+ - **Repository pattern** for database access — isolate queries from business logic
120
+ - **Dependency injection** — FastAPI `Depends()`, Django class-based views
121
+ - **One concern per module** — a file should do one thing well
122
+
123
+ ### Environment Variables & Secrets (MANDATORY)
124
+
125
+ ```python
126
+ # CORRECT: Pydantic Settings loads from .env
127
+ from pydantic_settings import BaseSettings
128
+
129
+ class Settings(BaseSettings):
130
+ DATABASE_URL: str
131
+ SECRET_KEY: str
132
+ API_TOKEN: str
133
+ model_config = {"env_file": ".env"}
134
+
135
+ settings = Settings()
136
+ ```
137
+
138
+ | Rule | Reason |
139
+ |------|--------|
140
+ | All secrets in `.env` | Never hardcode credentials |
141
+ | `.env` in `.gitignore` | Never commit secrets |
142
+ | `.env.example` always present | Document required variables |
143
+ | Pydantic Settings for loading | Typed, validated, auto-parsed |
144
+ | `--dry-run` flag for destructive scripts | Prevent accidental data loss |
145
+
146
+ ### Async vs Sync
147
+
148
+ | Workload | Pattern |
149
+ |----------|---------|
150
+ | I/O-bound (HTTP, DB, files) | `async def` + `httpx` / `asyncpg` |
151
+ | CPU-bound (parsing, computation) | `def` + `multiprocessing` / `concurrent.futures` |
152
+ | Local scripts (simple) | Sync is fine unless hitting APIs in bulk |
153
+ | Bulk API calls | `async def` + `asyncio.gather` + semaphore |
154
+
155
+ ### Database Safety
156
+
157
+ - **Parameterized queries ALWAYS** — `cursor.execute(query, params)`
158
+ - **Connection context managers** — auto-close on exit
159
+ - **Migrations** — Alembic (FastAPI) or Django `makemigrations`
160
+ - **Pool connections** in production — `pool_size` + `max_overflow`
161
+
162
+ ## Quality Gates
163
+
164
+ ```bash
165
+ mypy . # Type checking (MUST pass)
166
+ ruff check . # Linting (MUST pass)
167
+ ruff format . --check # Format check
168
+ pytest --tb=short # Tests (MUST pass)
169
+ ```
170
+
171
+ ## FORBIDDEN
172
+
173
+ ### Security (CRITICAL)
174
+
175
+ | Action | Reason |
176
+ |--------|--------|
177
+ | Hardcoded API keys/passwords | Use `.env` + Pydantic Settings |
178
+ | Commit `.env` files | Secrets leak to repository |
179
+ | SQL without parameterization | SQL injection |
180
+ | Deserializing untrusted binary data | Remote code execution risk |
181
+ | `yaml.load()` without SafeLoader | Code injection |
182
+ | No input validation on external data | Use Pydantic models |
183
+ | Storing plaintext passwords | Use `passlib` / `bcrypt` |
184
+
185
+ ### Code Quality
186
+
187
+ | Action | Reason |
188
+ |--------|--------|
189
+ | `import *` | Explicit imports only |
190
+ | `requests` library | Use `httpx` (modern, sync+async) |
191
+ | `print()` for logging | Use `logging` module or `rich` |
192
+ | No type hints on public APIs | mypy must pass |
193
+ | Business logic in routes/views | Use services layer |
194
+ | Bare `except Exception` | Catch specific exceptions |
195
+ | `time.sleep()` in async code | Use `await asyncio.sleep()` |
196
+ | Sync HTTP in async context | Blocks the event loop |
197
+ | Global mutable state | Use dependency injection |
198
+ | Files > 400 lines | Split into modules |
199
+
200
+ ### Workflow
201
+
202
+ | Action | Reason |
203
+ |--------|--------|
204
+ | Skip tests | Quality gate blocks commit |
205
+ | Skip type checking | mypy catches runtime errors |
206
+ | No `.env.example` | Others can't configure project |
207
+ | No `--dry-run` on destructive scripts | Risk of accidental data loss |
208
+ | Commit directly to main | Use feature branches |
209
+
210
+ ## CLAUDE.md Update Rules
211
+
212
+ ### When to Update
213
+
214
+ | Change Type | What to Update |
215
+ |-------------|----------------|
216
+ | Any file change | Last Change section |
217
+ | New feature | 30s Overview, Architecture if needed |
218
+ | New pattern | Add to relevant section |
219
+ | Gotcha discovered | Add to FORBIDDEN or NRY |
220
+ | New dependency | Update Stack table |
221
+
222
+ ### Last Change Format (MANDATORY)
223
+
224
+ ```markdown
225
+ ## Last Change
226
+
227
+ **Branch:** feature/example
228
+ **Date:** YYYY-MM-DD
229
+ **Summary:** 1-2 sentences describing WHAT and WHY.
230
+ ```
231
+
232
+ ## Agent System
233
+
234
+ | Agent | Purpose |
235
+ |-------|---------|
236
+ | research-web | Researches best practices (MANDATORY for new features) |
237
+ | documenter | Maps files to domains, tracks what exists |
238
+ | domain-updater | Records problems, solutions, learnings |
239
+ | commit-manager | Manages commits and merges |
240
+ | tester | Creates tests with pytest |
241
+ | claude-md-compactor | Compacts CLAUDE.md when over 40k chars |
242
+
243
+ ### Skills
244
+
245
+ | Category | Skills |
246
+ |----------|--------|
247
+ | **Core** | python-patterns, pydantic-validation, pytest-testing, python-performance |
248
+ | **Framework** | fastapi-patterns, django-patterns, async-patterns, scripting-automation |
249
+ | **UI** | ui-ux-pro-max (auto-installed for frontend projects) |
250
+
251
+ ## Domain Documentation
252
+
253
+ ### Location
254
+
255
+ ```
256
+ .claude/skills/codebase-knowledge/domains/
257
+ ├── api.md
258
+ ├── database.md
259
+ ├── scripts.md
260
+ └── ...
261
+ ```
262
+
263
+ ### Documentation Agents
264
+
265
+ | Agent | Role | When |
266
+ |-------|------|------|
267
+ | **documenter** | Maps files to domains | AFTER implementation |
268
+ | **domain-updater** | Records problems + solutions | BEFORE commit |
269
+
270
+ ## Workflow
271
+
272
+ ```
273
+ 1. TODO LIST → Create detailed task list
274
+ 2. RESEARCH → Run research agent for new features
275
+ 3. BRANCH → Create feature/ | fix/ | refactor/ | test/
276
+ 4. IMPLEMENT → Follow skills + type everything
277
+ 5. QUALITY → mypy → ruff → pytest (all must pass)
278
+ 6. DOCUMENT → Update domains + CLAUDE.md
279
+ 7. COMMIT → Conventional commit format
280
+ ```
281
+
282
+ ## Commit Format
283
+
284
+ ```
285
+ [type]: [description]
286
+
287
+ - Detail 1
288
+ - Detail 2
289
+
290
+ Generated with Claude Code
291
+ Co-Authored-By: Claude <noreply@anthropic.com>
292
+ ```
293
+
294
+ Types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`
295
+
296
+ ## NRY (Never Repeat Yourself)
297
+
298
+ - Always check `.claude/skills/` before implementing patterns from scratch
299
+ - Use `tenacity` for retry logic, not hand-rolled loops
300
+ - Use `pydantic_settings` for env config, not `os.getenv()` manually
301
+ - Use `httpx` for HTTP, never `urllib` or `requests`
302
+ - Use `rich` for CLI output, not bare `print()`
303
+
304
+ ## Configuration
305
+
306
+ Project-specific settings in `.claude/config/`:
307
+
308
+ - `active-project.json` — Stack, framework, database, skills
309
+ - `security-rules.json` — Security audit rules
310
+ - `standards-review.json` — Imported project standards (if adapted)
311
+
312
+ ## Setup by start-vibing
313
+
314
+ This project was set up with `npx start-vibing-stacks`.
315
+ For updates: `npx start-vibing-stacks --force`