@qubiit/lmagent 2.5.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/.editorconfig +18 -0
- package/AGENTS.md +169 -0
- package/CLAUDE.md +122 -0
- package/CONTRIBUTING.md +90 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/config/commands.yaml +194 -0
- package/config/levels.yaml +135 -0
- package/config/models.yaml +192 -0
- package/config/settings.yaml +405 -0
- package/config/tools-extended.yaml +534 -0
- package/config/tools.yaml +437 -0
- package/docs/assets/logo.png +0 -0
- package/docs/commands.md +132 -0
- package/docs/customization-guide.md +445 -0
- package/docs/getting-started.md +154 -0
- package/docs/how-to-start.md +242 -0
- package/docs/navigation-index.md +227 -0
- package/docs/usage-guide.md +113 -0
- package/install.js +1044 -0
- package/package.json +35 -0
- package/pyproject.toml +182 -0
- package/rules/_bootstrap.md +138 -0
- package/rules/agents-ia.md +607 -0
- package/rules/api-design.md +337 -0
- package/rules/automations-n8n.md +646 -0
- package/rules/code-style.md +570 -0
- package/rules/documentation.md +98 -0
- package/rules/security.md +316 -0
- package/rules/stack.md +395 -0
- package/rules/testing.md +326 -0
- package/rules/workflow.md +353 -0
- package/scripts/create_skill.js +300 -0
- package/scripts/validate_skills.js +283 -0
- package/skills/ai-agent-engineer/SKILL.md +394 -0
- package/skills/ai-agent-engineer/references/agent-patterns.md +149 -0
- package/skills/api-designer/SKILL.md +429 -0
- package/skills/api-designer/references/api-standards.md +13 -0
- package/skills/architect/SKILL.md +285 -0
- package/skills/architect/references/c4-model.md +133 -0
- package/skills/automation-engineer/SKILL.md +352 -0
- package/skills/automation-engineer/references/n8n-patterns.md +127 -0
- package/skills/backend-engineer/SKILL.md +261 -0
- package/skills/backend-engineer/assets/fastapi-project-structure.yaml +74 -0
- package/skills/backend-engineer/references/debugging-guide.md +174 -0
- package/skills/backend-engineer/references/design-patterns.md +208 -0
- package/skills/backend-engineer/scripts/scaffold_backend.py +313 -0
- package/skills/bmad-methodology/SKILL.md +202 -0
- package/skills/bmad-methodology/references/scale-adaptive-levels.md +141 -0
- package/skills/browser-agent/SKILL.md +502 -0
- package/skills/browser-agent/scripts/playwright_setup.ts +16 -0
- package/skills/code-reviewer/SKILL.md +306 -0
- package/skills/code-reviewer/references/code-review-checklist.md +16 -0
- package/skills/data-engineer/SKILL.md +474 -0
- package/skills/data-engineer/assets/pg-monitoring-queries.sql +154 -0
- package/skills/data-engineer/references/index-strategy.md +128 -0
- package/skills/data-engineer/scripts/backup_postgres.py +221 -0
- package/skills/devops-engineer/SKILL.md +547 -0
- package/skills/devops-engineer/references/ci-cd-patterns.md +265 -0
- package/skills/devops-engineer/scripts/docker_healthcheck.py +125 -0
- package/skills/document-generator/SKILL.md +746 -0
- package/skills/document-generator/references/pdf-generation.md +22 -0
- package/skills/frontend-engineer/SKILL.md +532 -0
- package/skills/frontend-engineer/references/accessibility-guide.md +146 -0
- package/skills/frontend-engineer/scripts/audit_bundle.py +144 -0
- package/skills/git-workflow/SKILL.md +374 -0
- package/skills/git-workflow/references/git-flow.md +25 -0
- package/skills/mcp-builder/SKILL.md +471 -0
- package/skills/mcp-builder/references/mcp-server-guide.md +23 -0
- package/skills/mobile-engineer/SKILL.md +502 -0
- package/skills/mobile-engineer/references/platform-guidelines.md +160 -0
- package/skills/orchestrator/SKILL.md +246 -0
- package/skills/orchestrator/references/methodology-routing.md +117 -0
- package/skills/orchestrator/references/persona-mapping.md +85 -0
- package/skills/orchestrator/references/routing-logic.md +110 -0
- package/skills/performance-engineer/SKILL.md +549 -0
- package/skills/performance-engineer/references/caching-patterns.md +181 -0
- package/skills/performance-engineer/scripts/profile_endpoint.py +170 -0
- package/skills/product-manager/SKILL.md +488 -0
- package/skills/product-manager/references/prioritization-frameworks.md +126 -0
- package/skills/prompt-engineer/SKILL.md +433 -0
- package/skills/prompt-engineer/references/prompt-patterns.md +158 -0
- package/skills/qa-engineer/SKILL.md +441 -0
- package/skills/qa-engineer/references/testing-strategy.md +166 -0
- package/skills/qa-engineer/scripts/run_coverage.py +147 -0
- package/skills/scrum-master/SKILL.md +225 -0
- package/skills/scrum-master/references/sprint-ceremonies.md +159 -0
- package/skills/security-analyst/SKILL.md +390 -0
- package/skills/security-analyst/references/owasp-top10.md +188 -0
- package/skills/security-analyst/scripts/audit_security.py +242 -0
- package/skills/seo-auditor/SKILL.md +523 -0
- package/skills/seo-auditor/references/seo-checklist.md +17 -0
- package/skills/spec-driven-dev/SKILL.md +342 -0
- package/skills/spec-driven-dev/references/phase-gates.md +107 -0
- package/skills/supabase-expert/SKILL.md +602 -0
- package/skills/supabase-expert/references/supabase-patterns.md +19 -0
- package/skills/swe-agent/SKILL.md +311 -0
- package/skills/swe-agent/references/trajectory-format.md +134 -0
- package/skills/systematic-debugger/SKILL.md +512 -0
- package/skills/systematic-debugger/references/debugging-guide.md +12 -0
- package/skills/tech-lead/SKILL.md +409 -0
- package/skills/tech-lead/references/code-review-checklist.md +111 -0
- package/skills/technical-writer/SKILL.md +631 -0
- package/skills/technical-writer/references/doc-templates.md +218 -0
- package/skills/testing-strategist/SKILL.md +476 -0
- package/skills/testing-strategist/references/testing-pyramid.md +16 -0
- package/skills/ux-ui-designer/SKILL.md +419 -0
- package/skills/ux-ui-designer/references/design-system-foundation.md +168 -0
- package/skills_overview.txt +94 -0
- package/templates/PROJECT_KICKOFF.md +284 -0
- package/templates/SKILL_TEMPLATE.md +131 -0
- package/templates/USAGE.md +95 -0
- package/templates/agent-python/README.md +71 -0
- package/templates/agent-python/agent.py +272 -0
- package/templates/agent-python/config.yaml +76 -0
- package/templates/agent-python/prompts/system.md +109 -0
- package/templates/agent-python/requirements.txt +7 -0
- package/templates/automation-n8n/README.md +14 -0
- package/templates/automation-n8n/webhook-handler.json +57 -0
- package/templates/backend-node/Dockerfile +12 -0
- package/templates/backend-node/README.md +15 -0
- package/templates/backend-node/package.json +30 -0
- package/templates/backend-node/src/index.ts +19 -0
- package/templates/backend-node/src/routes.ts +7 -0
- package/templates/backend-node/tsconfig.json +22 -0
- package/templates/backend-python/Dockerfile +11 -0
- package/templates/backend-python/README.md +78 -0
- package/templates/backend-python/app/core/config.py +12 -0
- package/templates/backend-python/app/core/database.py +12 -0
- package/templates/backend-python/app/main.py +17 -0
- package/templates/backend-python/app/routers/__init__.py +1 -0
- package/templates/backend-python/app/routers/health.py +7 -0
- package/templates/backend-python/requirements-dev.txt +6 -0
- package/templates/backend-python/requirements.txt +4 -0
- package/templates/backend-python/tests/test_health.py +9 -0
- package/templates/checkpoint.yaml +117 -0
- package/templates/database/README.md +474 -0
- package/templates/frontend-react/README.md +446 -0
- package/templates/plan.yaml +320 -0
- package/templates/session.yaml +125 -0
- package/templates/spec.yaml +229 -0
- package/templates/tasks.yaml +330 -0
- package/workflows/bugfix-backend.md +380 -0
- package/workflows/documentation.md +232 -0
- package/workflows/generate-prd.md +320 -0
- package/workflows/ideation.md +396 -0
- package/workflows/new-agent-ia.md +497 -0
- package/workflows/new-automation.md +374 -0
- package/workflows/new-feature.md +290 -0
- package/workflows/optimize-performance.md +373 -0
- package/workflows/resolve-github-issue.md +524 -0
- package/workflows/security-review.md +291 -0
- package/workflows/spec-driven.md +476 -0
- package/workflows/testing-strategy.md +296 -0
- package/workflows/third-party-integration.md +277 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Design Patterns Reference — Backend Engineer
|
|
2
|
+
|
|
3
|
+
> Patrones de diseño más usados en desarrollo backend con FastAPI y NestJS.
|
|
4
|
+
|
|
5
|
+
## Patrones Estructurales
|
|
6
|
+
|
|
7
|
+
### Repository Pattern
|
|
8
|
+
|
|
9
|
+
Separa la lógica de acceso a datos de la lógica de negocio.
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
# app/repositories/base.py
|
|
13
|
+
from typing import TypeVar, Generic, Type, Optional
|
|
14
|
+
from sqlmodel import Session, SQLModel, select
|
|
15
|
+
|
|
16
|
+
T = TypeVar("T", bound=SQLModel)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BaseRepository(Generic[T]):
|
|
20
|
+
"""Repositorio genérico con operaciones CRUD."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, model: Type[T], session: Session):
|
|
23
|
+
self.model = model
|
|
24
|
+
self.session = session
|
|
25
|
+
|
|
26
|
+
def get_by_id(self, id: str) -> Optional[T]:
|
|
27
|
+
return self.session.get(self.model, id)
|
|
28
|
+
|
|
29
|
+
def get_all(self, skip: int = 0, limit: int = 100) -> list[T]:
|
|
30
|
+
statement = select(self.model).offset(skip).limit(limit)
|
|
31
|
+
return self.session.exec(statement).all()
|
|
32
|
+
|
|
33
|
+
def create(self, obj: T) -> T:
|
|
34
|
+
self.session.add(obj)
|
|
35
|
+
self.session.commit()
|
|
36
|
+
self.session.refresh(obj)
|
|
37
|
+
return obj
|
|
38
|
+
|
|
39
|
+
def update(self, obj: T) -> T:
|
|
40
|
+
self.session.add(obj)
|
|
41
|
+
self.session.commit()
|
|
42
|
+
self.session.refresh(obj)
|
|
43
|
+
return obj
|
|
44
|
+
|
|
45
|
+
def delete(self, id: str) -> bool:
|
|
46
|
+
obj = self.get_by_id(id)
|
|
47
|
+
if obj:
|
|
48
|
+
self.session.delete(obj)
|
|
49
|
+
self.session.commit()
|
|
50
|
+
return True
|
|
51
|
+
return False
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Service Layer Pattern
|
|
55
|
+
|
|
56
|
+
Encapsula lógica de negocio entre controllers y repositories.
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# app/services/user_service.py
|
|
60
|
+
from app.repositories.user_repository import UserRepository
|
|
61
|
+
from app.schemas.user import UserCreate, UserUpdate
|
|
62
|
+
from app.models.user import User
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class UserService:
|
|
66
|
+
"""Lógica de negocio para usuarios."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, repo: UserRepository):
|
|
69
|
+
self.repo = repo
|
|
70
|
+
|
|
71
|
+
async def create_user(self, data: UserCreate) -> User:
|
|
72
|
+
# Validación de negocio
|
|
73
|
+
existing = self.repo.get_by_email(data.email)
|
|
74
|
+
if existing:
|
|
75
|
+
raise ValueError(f"Email {data.email} ya registrado")
|
|
76
|
+
|
|
77
|
+
user = User(**data.model_dump())
|
|
78
|
+
return self.repo.create(user)
|
|
79
|
+
|
|
80
|
+
async def get_user(self, user_id: str) -> User:
|
|
81
|
+
user = self.repo.get_by_id(user_id)
|
|
82
|
+
if not user:
|
|
83
|
+
raise ValueError(f"Usuario {user_id} no encontrado")
|
|
84
|
+
return user
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Dependency Injection (FastAPI)
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# app/api/deps.py
|
|
91
|
+
from typing import Annotated
|
|
92
|
+
from fastapi import Depends
|
|
93
|
+
from sqlmodel import Session
|
|
94
|
+
from app.core.database import get_session
|
|
95
|
+
from app.repositories.user_repository import UserRepository
|
|
96
|
+
from app.services.user_service import UserService
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_user_repository(
|
|
100
|
+
session: Annotated[Session, Depends(get_session)]
|
|
101
|
+
) -> UserRepository:
|
|
102
|
+
return UserRepository(session)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_user_service(
|
|
106
|
+
repo: Annotated[UserRepository, Depends(get_user_repository)]
|
|
107
|
+
) -> UserService:
|
|
108
|
+
return UserService(repo)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Patrones de Resiliencia
|
|
112
|
+
|
|
113
|
+
### Circuit Breaker
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import time
|
|
117
|
+
from enum import Enum
|
|
118
|
+
from typing import Callable, Any
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class CircuitState(Enum):
|
|
122
|
+
CLOSED = "closed" # Funcionando normal
|
|
123
|
+
OPEN = "open" # Cortado, rechaza requests
|
|
124
|
+
HALF_OPEN = "half_open" # Probando si se recuperó
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class CircuitBreaker:
|
|
128
|
+
"""Implementación simple de Circuit Breaker."""
|
|
129
|
+
|
|
130
|
+
def __init__(
|
|
131
|
+
self,
|
|
132
|
+
failure_threshold: int = 5,
|
|
133
|
+
recovery_timeout: int = 30,
|
|
134
|
+
):
|
|
135
|
+
self.failure_threshold = failure_threshold
|
|
136
|
+
self.recovery_timeout = recovery_timeout
|
|
137
|
+
self.failure_count = 0
|
|
138
|
+
self.state = CircuitState.CLOSED
|
|
139
|
+
self.last_failure_time = 0
|
|
140
|
+
|
|
141
|
+
async def call(self, func: Callable, *args, **kwargs) -> Any:
|
|
142
|
+
if self.state == CircuitState.OPEN:
|
|
143
|
+
if time.time() - self.last_failure_time > self.recovery_timeout:
|
|
144
|
+
self.state = CircuitState.HALF_OPEN
|
|
145
|
+
else:
|
|
146
|
+
raise Exception("Circuit breaker is OPEN")
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
result = await func(*args, **kwargs)
|
|
150
|
+
self._on_success()
|
|
151
|
+
return result
|
|
152
|
+
except Exception as e:
|
|
153
|
+
self._on_failure()
|
|
154
|
+
raise e
|
|
155
|
+
|
|
156
|
+
def _on_success(self):
|
|
157
|
+
self.failure_count = 0
|
|
158
|
+
self.state = CircuitState.CLOSED
|
|
159
|
+
|
|
160
|
+
def _on_failure(self):
|
|
161
|
+
self.failure_count += 1
|
|
162
|
+
self.last_failure_time = time.time()
|
|
163
|
+
if self.failure_count >= self.failure_threshold:
|
|
164
|
+
self.state = CircuitState.OPEN
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Retry with Backoff
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
import asyncio
|
|
171
|
+
import random
|
|
172
|
+
from functools import wraps
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def retry_with_backoff(
|
|
176
|
+
max_retries: int = 3,
|
|
177
|
+
base_delay: float = 1.0,
|
|
178
|
+
max_delay: float = 60.0,
|
|
179
|
+
):
|
|
180
|
+
"""Decorator para retry con exponential backoff + jitter."""
|
|
181
|
+
def decorator(func):
|
|
182
|
+
@wraps(func)
|
|
183
|
+
async def wrapper(*args, **kwargs):
|
|
184
|
+
for attempt in range(max_retries + 1):
|
|
185
|
+
try:
|
|
186
|
+
return await func(*args, **kwargs)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
if attempt == max_retries:
|
|
189
|
+
raise e
|
|
190
|
+
delay = min(base_delay * (2 ** attempt), max_delay)
|
|
191
|
+
jitter = random.uniform(0, delay * 0.1)
|
|
192
|
+
await asyncio.sleep(delay + jitter)
|
|
193
|
+
return wrapper
|
|
194
|
+
return decorator
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Anti-Patterns a Evitar
|
|
198
|
+
|
|
199
|
+
| ❌ Anti-Pattern | ✅ Alternativa |
|
|
200
|
+
|----------------|---------------|
|
|
201
|
+
| Fat Controller (lógica en endpoint) | Service Layer Pattern |
|
|
202
|
+
| God Object (clase que hace todo) | Single Responsibility |
|
|
203
|
+
| N+1 Queries | Eager loading / JOINs |
|
|
204
|
+
| Hardcoded configs | Environment variables |
|
|
205
|
+
| String SQL | ORM / Prepared statements |
|
|
206
|
+
| Catch-all exceptions | Specific exception handling |
|
|
207
|
+
| Synchronous blocking in async | `asyncio.to_thread()` |
|
|
208
|
+
| Mutable default arguments | `field(default_factory=list)` |
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
LMAgent - Backend Project Scaffolding Script
|
|
4
|
+
Genera la estructura base de un proyecto backend con FastAPI o NestJS.
|
|
5
|
+
|
|
6
|
+
Uso:
|
|
7
|
+
python scaffold_backend.py --framework fastapi --name mi-proyecto
|
|
8
|
+
python scaffold_backend.py --framework nestjs --name mi-proyecto
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import argparse
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_fastapi_structure(project_name: str, base_path: Path):
|
|
18
|
+
"""Crea estructura de proyecto FastAPI estandarizada."""
|
|
19
|
+
dirs = [
|
|
20
|
+
f"{project_name}/app",
|
|
21
|
+
f"{project_name}/app/api",
|
|
22
|
+
f"{project_name}/app/api/v1",
|
|
23
|
+
f"{project_name}/app/api/v1/endpoints",
|
|
24
|
+
f"{project_name}/app/core",
|
|
25
|
+
f"{project_name}/app/models",
|
|
26
|
+
f"{project_name}/app/schemas",
|
|
27
|
+
f"{project_name}/app/services",
|
|
28
|
+
f"{project_name}/app/repositories",
|
|
29
|
+
f"{project_name}/app/middleware",
|
|
30
|
+
f"{project_name}/app/utils",
|
|
31
|
+
f"{project_name}/tests",
|
|
32
|
+
f"{project_name}/tests/unit",
|
|
33
|
+
f"{project_name}/tests/integration",
|
|
34
|
+
f"{project_name}/migrations",
|
|
35
|
+
f"{project_name}/migrations/versions",
|
|
36
|
+
f"{project_name}/docs",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
files = {
|
|
40
|
+
f"{project_name}/app/__init__.py": "",
|
|
41
|
+
f"{project_name}/app/main.py": '''"""FastAPI Application Entry Point."""
|
|
42
|
+
from fastapi import FastAPI
|
|
43
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
44
|
+
from app.core.config import settings
|
|
45
|
+
|
|
46
|
+
app = FastAPI(
|
|
47
|
+
title=settings.PROJECT_NAME,
|
|
48
|
+
version=settings.VERSION,
|
|
49
|
+
docs_url="/docs",
|
|
50
|
+
redoc_url="/redoc",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
app.add_middleware(
|
|
54
|
+
CORSMiddleware,
|
|
55
|
+
allow_origins=settings.ALLOWED_ORIGINS,
|
|
56
|
+
allow_credentials=True,
|
|
57
|
+
allow_methods=["*"],
|
|
58
|
+
allow_headers=["*"],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@app.get("/health")
|
|
63
|
+
async def health_check():
|
|
64
|
+
return {"status": "healthy", "version": settings.VERSION}
|
|
65
|
+
''',
|
|
66
|
+
f"{project_name}/app/core/__init__.py": "",
|
|
67
|
+
f"{project_name}/app/core/config.py": '''"""Application Configuration."""
|
|
68
|
+
import os
|
|
69
|
+
from pydantic_settings import BaseSettings
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Settings(BaseSettings):
|
|
73
|
+
PROJECT_NAME: str = os.getenv("PROJECT_NAME", "LMAgent Backend")
|
|
74
|
+
VERSION: str = os.getenv("VERSION", "0.1.0")
|
|
75
|
+
DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true"
|
|
76
|
+
|
|
77
|
+
# Database
|
|
78
|
+
DATABASE_URL: str = os.getenv("DATABASE_URL", "postgresql://user:pass@localhost:5432/db")
|
|
79
|
+
|
|
80
|
+
# Redis
|
|
81
|
+
REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
|
82
|
+
|
|
83
|
+
# Security
|
|
84
|
+
SECRET_KEY: str = os.getenv("SECRET_KEY", "change-me-in-production")
|
|
85
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: int = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
|
|
86
|
+
|
|
87
|
+
# CORS
|
|
88
|
+
ALLOWED_ORIGINS: list[str] = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000").split(",")
|
|
89
|
+
|
|
90
|
+
class Config:
|
|
91
|
+
env_file = ".env"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
settings = Settings()
|
|
95
|
+
''',
|
|
96
|
+
f"{project_name}/app/api/__init__.py": "",
|
|
97
|
+
f"{project_name}/app/api/v1/__init__.py": "",
|
|
98
|
+
f"{project_name}/app/api/v1/endpoints/__init__.py": "",
|
|
99
|
+
f"{project_name}/app/models/__init__.py": "",
|
|
100
|
+
f"{project_name}/app/schemas/__init__.py": "",
|
|
101
|
+
f"{project_name}/app/services/__init__.py": "",
|
|
102
|
+
f"{project_name}/app/repositories/__init__.py": "",
|
|
103
|
+
f"{project_name}/app/middleware/__init__.py": "",
|
|
104
|
+
f"{project_name}/app/utils/__init__.py": "",
|
|
105
|
+
f"{project_name}/tests/__init__.py": "",
|
|
106
|
+
f"{project_name}/tests/conftest.py": '''"""Test Configuration and Fixtures."""
|
|
107
|
+
import pytest
|
|
108
|
+
from httpx import AsyncClient, ASGITransport
|
|
109
|
+
from app.main import app
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.fixture
|
|
113
|
+
async def client():
|
|
114
|
+
"""Async test client fixture."""
|
|
115
|
+
async with AsyncClient(
|
|
116
|
+
transport=ASGITransport(app=app),
|
|
117
|
+
base_url="http://test"
|
|
118
|
+
) as ac:
|
|
119
|
+
yield ac
|
|
120
|
+
''',
|
|
121
|
+
f"{project_name}/requirements.txt": """fastapi>=0.115.0
|
|
122
|
+
uvicorn[standard]>=0.30.0
|
|
123
|
+
pydantic>=2.0.0
|
|
124
|
+
pydantic-settings>=2.0.0
|
|
125
|
+
sqlmodel>=0.0.22
|
|
126
|
+
alembic>=1.14.0
|
|
127
|
+
asyncpg>=0.30.0
|
|
128
|
+
httpx>=0.27.0
|
|
129
|
+
redis>=5.0.0
|
|
130
|
+
python-jose[cryptography]>=3.3.0
|
|
131
|
+
passlib[bcrypt]>=1.7.4
|
|
132
|
+
python-multipart>=0.0.9
|
|
133
|
+
""",
|
|
134
|
+
f"{project_name}/requirements-dev.txt": """pytest>=8.0.0
|
|
135
|
+
pytest-asyncio>=0.23.0
|
|
136
|
+
pytest-cov>=5.0.0
|
|
137
|
+
httpx>=0.27.0
|
|
138
|
+
ruff>=0.8.0
|
|
139
|
+
mypy>=1.13.0
|
|
140
|
+
faker>=30.0.0
|
|
141
|
+
""",
|
|
142
|
+
f"{project_name}/.env.example": """# Application
|
|
143
|
+
PROJECT_NAME=mi-proyecto
|
|
144
|
+
VERSION=0.1.0
|
|
145
|
+
DEBUG=true
|
|
146
|
+
|
|
147
|
+
# Database
|
|
148
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
|
|
149
|
+
|
|
150
|
+
# Redis
|
|
151
|
+
REDIS_URL=redis://localhost:6379/0
|
|
152
|
+
|
|
153
|
+
# Security
|
|
154
|
+
SECRET_KEY=change-me-in-production
|
|
155
|
+
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
|
156
|
+
|
|
157
|
+
# CORS
|
|
158
|
+
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
|
|
159
|
+
""",
|
|
160
|
+
f"{project_name}/Dockerfile": """# Multi-stage build
|
|
161
|
+
FROM python:3.12-slim AS builder
|
|
162
|
+
WORKDIR /app
|
|
163
|
+
COPY requirements.txt .
|
|
164
|
+
RUN pip install --no-cache-dir --user -r requirements.txt
|
|
165
|
+
|
|
166
|
+
FROM python:3.12-slim
|
|
167
|
+
WORKDIR /app
|
|
168
|
+
COPY --from=builder /root/.local /root/.local
|
|
169
|
+
COPY . .
|
|
170
|
+
ENV PATH=/root/.local/bin:$PATH
|
|
171
|
+
EXPOSE 8000
|
|
172
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
173
|
+
""",
|
|
174
|
+
f"{project_name}/pyproject.toml": f"""[project]
|
|
175
|
+
name = "{project_name}"
|
|
176
|
+
version = "0.1.0"
|
|
177
|
+
requires-python = ">=3.12"
|
|
178
|
+
|
|
179
|
+
[tool.ruff]
|
|
180
|
+
target-version = "py312"
|
|
181
|
+
line-length = 100
|
|
182
|
+
|
|
183
|
+
[tool.ruff.lint]
|
|
184
|
+
select = ["E", "W", "F", "I", "N", "UP", "S", "B", "A", "C4"]
|
|
185
|
+
|
|
186
|
+
[tool.pytest.ini_options]
|
|
187
|
+
asyncio_mode = "auto"
|
|
188
|
+
testpaths = ["tests"]
|
|
189
|
+
|
|
190
|
+
[tool.mypy]
|
|
191
|
+
python_version = "3.12"
|
|
192
|
+
strict = true
|
|
193
|
+
""",
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for d in dirs:
|
|
197
|
+
(base_path / d).mkdir(parents=True, exist_ok=True)
|
|
198
|
+
|
|
199
|
+
for filepath, content in files.items():
|
|
200
|
+
(base_path / filepath).write_text(content, encoding="utf-8")
|
|
201
|
+
|
|
202
|
+
print(f"✅ Proyecto FastAPI '{project_name}' creado exitosamente.")
|
|
203
|
+
print(f" 📁 Ubicación: {base_path / project_name}")
|
|
204
|
+
print(f"\n Siguientes pasos:")
|
|
205
|
+
print(f" 1. cd {project_name}")
|
|
206
|
+
print(f" 2. cp .env.example .env")
|
|
207
|
+
print(f" 3. pip install -r requirements.txt")
|
|
208
|
+
print(f" 4. uvicorn app.main:app --reload")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def create_nestjs_structure(project_name: str, base_path: Path):
|
|
212
|
+
"""Crea estructura de proyecto NestJS estandarizada."""
|
|
213
|
+
dirs = [
|
|
214
|
+
f"{project_name}/src",
|
|
215
|
+
f"{project_name}/src/common",
|
|
216
|
+
f"{project_name}/src/common/decorators",
|
|
217
|
+
f"{project_name}/src/common/filters",
|
|
218
|
+
f"{project_name}/src/common/guards",
|
|
219
|
+
f"{project_name}/src/common/interceptors",
|
|
220
|
+
f"{project_name}/src/common/pipes",
|
|
221
|
+
f"{project_name}/src/config",
|
|
222
|
+
f"{project_name}/src/modules",
|
|
223
|
+
f"{project_name}/src/modules/auth",
|
|
224
|
+
f"{project_name}/src/modules/users",
|
|
225
|
+
f"{project_name}/test",
|
|
226
|
+
f"{project_name}/prisma",
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
files = {
|
|
230
|
+
f"{project_name}/src/main.ts": '''import { NestFactory } from "@nestjs/core";
|
|
231
|
+
import { ValidationPipe } from "@nestjs/common";
|
|
232
|
+
import { AppModule } from "./app.module";
|
|
233
|
+
|
|
234
|
+
async function bootstrap() {
|
|
235
|
+
const app = await NestFactory.create(AppModule);
|
|
236
|
+
|
|
237
|
+
app.enableCors({
|
|
238
|
+
origin: process.env.ALLOWED_ORIGINS?.split(",") || ["http://localhost:3000"],
|
|
239
|
+
credentials: true,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
|
|
243
|
+
app.setGlobalPrefix("api/v1");
|
|
244
|
+
|
|
245
|
+
const port = process.env.PORT || 3001;
|
|
246
|
+
await app.listen(port);
|
|
247
|
+
console.log(`🚀 Server running on http://localhost:${port}`);
|
|
248
|
+
}
|
|
249
|
+
bootstrap();
|
|
250
|
+
''',
|
|
251
|
+
f"{project_name}/.env.example": """# Application
|
|
252
|
+
PORT=3001
|
|
253
|
+
NODE_ENV=development
|
|
254
|
+
|
|
255
|
+
# Database
|
|
256
|
+
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
|
|
257
|
+
|
|
258
|
+
# JWT
|
|
259
|
+
JWT_SECRET=change-me-in-production
|
|
260
|
+
JWT_EXPIRES_IN=30m
|
|
261
|
+
|
|
262
|
+
# CORS
|
|
263
|
+
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
|
|
264
|
+
""",
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
for d in dirs:
|
|
268
|
+
(base_path / d).mkdir(parents=True, exist_ok=True)
|
|
269
|
+
|
|
270
|
+
for filepath, content in files.items():
|
|
271
|
+
(base_path / filepath).write_text(content, encoding="utf-8")
|
|
272
|
+
|
|
273
|
+
print(f"✅ Proyecto NestJS '{project_name}' creado exitosamente.")
|
|
274
|
+
print(f" 📁 Ubicación: {base_path / project_name}")
|
|
275
|
+
print(f"\n Siguientes pasos:")
|
|
276
|
+
print(f" 1. cd {project_name}")
|
|
277
|
+
print(f" 2. cp .env.example .env")
|
|
278
|
+
print(f" 3. npm install")
|
|
279
|
+
print(f" 4. npm run start:dev")
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def main():
|
|
283
|
+
parser = argparse.ArgumentParser(
|
|
284
|
+
description="LMAgent Backend Scaffolding — Genera estructura de proyecto backend"
|
|
285
|
+
)
|
|
286
|
+
parser.add_argument(
|
|
287
|
+
"--framework", "-f",
|
|
288
|
+
choices=["fastapi", "nestjs"],
|
|
289
|
+
required=True,
|
|
290
|
+
help="Framework a usar (fastapi o nestjs)"
|
|
291
|
+
)
|
|
292
|
+
parser.add_argument(
|
|
293
|
+
"--name", "-n",
|
|
294
|
+
required=True,
|
|
295
|
+
help="Nombre del proyecto"
|
|
296
|
+
)
|
|
297
|
+
parser.add_argument(
|
|
298
|
+
"--path", "-p",
|
|
299
|
+
default=".",
|
|
300
|
+
help="Ruta base donde crear el proyecto (default: directorio actual)"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
args = parser.parse_args()
|
|
304
|
+
base_path = Path(args.path).resolve()
|
|
305
|
+
|
|
306
|
+
if args.framework == "fastapi":
|
|
307
|
+
create_fastapi_structure(args.name, base_path)
|
|
308
|
+
elif args.framework == "nestjs":
|
|
309
|
+
create_nestjs_structure(args.name, base_path)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
if __name__ == "__main__":
|
|
313
|
+
main()
|