@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,570 @@
|
|
|
1
|
+
# Guía de Estilo de Código - LMAgent
|
|
2
|
+
|
|
3
|
+
> **Tipo**: `rule` | **Versión**: 2.1 | **Actualización**: 2026-01
|
|
4
|
+
|
|
5
|
+
## 📌 Quick Reference
|
|
6
|
+
|
|
7
|
+
| Regla | Python | TypeScript |
|
|
8
|
+
|-------|--------|------------|
|
|
9
|
+
| **Naming** | `snake_case` funciones/vars, `PascalCase` clases | `camelCase` funciones/vars, `PascalCase` clases |
|
|
10
|
+
| **Indentación** | 4 espacios | 2 espacios |
|
|
11
|
+
| **Línea máx** | 88 chars | 100 chars |
|
|
12
|
+
| **Types** | OBLIGATORIO (type hints) | OBLIGATORIO (strict TS) |
|
|
13
|
+
| **Lint** | `ruff check .` + `ruff format .` | `eslint .` + `prettier` |
|
|
14
|
+
| **Imports** | Stdlib → Third-party → Local | Built-in → External → Internal |
|
|
15
|
+
|
|
16
|
+
### 👥 Roles que usan esta regla
|
|
17
|
+
`backend-engineer`, `frontend-engineer`, `mobile-engineer`, `qa-engineer`
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
Este documento define las convenciones de estilo para Python y TypeScript.
|
|
22
|
+
|
|
23
|
+
## Python
|
|
24
|
+
|
|
25
|
+
### Herramientas
|
|
26
|
+
```bash
|
|
27
|
+
# Linting y formatting
|
|
28
|
+
ruff check . # Linting
|
|
29
|
+
ruff format . # Formatting (reemplaza black)
|
|
30
|
+
|
|
31
|
+
# Type checking (opcional pero recomendado)
|
|
32
|
+
mypy app/
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Configuración (pyproject.toml)
|
|
36
|
+
```toml
|
|
37
|
+
[tool.ruff]
|
|
38
|
+
target-version = "py311"
|
|
39
|
+
line-length = 88
|
|
40
|
+
select = [
|
|
41
|
+
"E", # pycodestyle errors
|
|
42
|
+
"W", # pycodestyle warnings
|
|
43
|
+
"F", # Pyflakes
|
|
44
|
+
"I", # isort
|
|
45
|
+
"B", # flake8-bugbear
|
|
46
|
+
"C4", # flake8-comprehensions
|
|
47
|
+
"UP", # pyupgrade
|
|
48
|
+
]
|
|
49
|
+
ignore = [
|
|
50
|
+
"E501", # line too long (handled by formatter)
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.isort]
|
|
54
|
+
known-first-party = ["app"]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Imports
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# ✅ Correcto - Agrupados y ordenados
|
|
61
|
+
from __future__ import annotations
|
|
62
|
+
|
|
63
|
+
# Standard library
|
|
64
|
+
import json
|
|
65
|
+
from datetime import datetime
|
|
66
|
+
from typing import Optional, List
|
|
67
|
+
|
|
68
|
+
# Third party
|
|
69
|
+
from fastapi import APIRouter, Depends, HTTPException
|
|
70
|
+
from pydantic import BaseModel
|
|
71
|
+
from sqlmodel import Session, select
|
|
72
|
+
|
|
73
|
+
# Local
|
|
74
|
+
from app.config import settings
|
|
75
|
+
from app.core.database import get_session
|
|
76
|
+
from app.models.user import User
|
|
77
|
+
|
|
78
|
+
# ❌ Incorrecto
|
|
79
|
+
from app.models.user import * # No usar import *
|
|
80
|
+
import json, os, sys # No múltiples en una línea
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Naming Conventions
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# Módulos y archivos: snake_case
|
|
87
|
+
user_service.py
|
|
88
|
+
database_utils.py
|
|
89
|
+
|
|
90
|
+
# Clases: PascalCase
|
|
91
|
+
class UserService:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
class HTTPRequestHandler: # Acronyms en mayúsculas
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
# Funciones y métodos: snake_case
|
|
98
|
+
def get_user_by_email(email: str) -> User:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
async def fetch_external_data():
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# Variables: snake_case
|
|
105
|
+
user_count = 0
|
|
106
|
+
is_active = True
|
|
107
|
+
api_response = {}
|
|
108
|
+
|
|
109
|
+
# Constantes: UPPER_SNAKE_CASE
|
|
110
|
+
MAX_RETRY_COUNT = 3
|
|
111
|
+
DEFAULT_TIMEOUT = 30
|
|
112
|
+
API_VERSION = "v1"
|
|
113
|
+
|
|
114
|
+
# Privados: prefijo _
|
|
115
|
+
def _internal_helper():
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
class MyClass:
|
|
119
|
+
def __init__(self):
|
|
120
|
+
self._private_attr = None
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Type Hints (Obligatorios)
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# ✅ Correcto - Type hints en todo
|
|
127
|
+
from typing import Optional, List, Dict, Any
|
|
128
|
+
|
|
129
|
+
def process_user(
|
|
130
|
+
user_id: int,
|
|
131
|
+
options: Optional[Dict[str, Any]] = None
|
|
132
|
+
) -> User:
|
|
133
|
+
"""Procesa un usuario por ID."""
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
async def fetch_users(
|
|
137
|
+
limit: int = 10,
|
|
138
|
+
offset: int = 0
|
|
139
|
+
) -> List[User]:
|
|
140
|
+
"""Obtiene lista de usuarios paginada."""
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
# Variables con tipos cuando no es obvio
|
|
144
|
+
users: List[User] = []
|
|
145
|
+
config: Dict[str, Any] = load_config()
|
|
146
|
+
|
|
147
|
+
# ❌ Incorrecto - Sin types
|
|
148
|
+
def process_user(user_id, options=None):
|
|
149
|
+
pass
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Docstrings
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
def calculate_discount(
|
|
156
|
+
price: float,
|
|
157
|
+
discount_percent: float,
|
|
158
|
+
max_discount: Optional[float] = None
|
|
159
|
+
) -> float:
|
|
160
|
+
"""
|
|
161
|
+
Calcula el precio con descuento aplicado.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
price: Precio original del producto.
|
|
165
|
+
discount_percent: Porcentaje de descuento (0-100).
|
|
166
|
+
max_discount: Descuento máximo permitido en valor absoluto.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Precio final después de aplicar el descuento.
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ValueError: Si discount_percent está fuera de rango (0-100).
|
|
173
|
+
|
|
174
|
+
Example:
|
|
175
|
+
>>> calculate_discount(100.0, 20.0)
|
|
176
|
+
80.0
|
|
177
|
+
>>> calculate_discount(100.0, 50.0, max_discount=30.0)
|
|
178
|
+
70.0
|
|
179
|
+
"""
|
|
180
|
+
if not 0 <= discount_percent <= 100:
|
|
181
|
+
raise ValueError("discount_percent must be between 0 and 100")
|
|
182
|
+
|
|
183
|
+
discount = price * (discount_percent / 100)
|
|
184
|
+
if max_discount is not None:
|
|
185
|
+
discount = min(discount, max_discount)
|
|
186
|
+
|
|
187
|
+
return price - discount
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class UserService:
|
|
191
|
+
"""
|
|
192
|
+
Servicio para gestión de usuarios.
|
|
193
|
+
|
|
194
|
+
Maneja la lógica de negocio relacionada con usuarios,
|
|
195
|
+
incluyendo creación, actualización y validaciones.
|
|
196
|
+
|
|
197
|
+
Attributes:
|
|
198
|
+
repository: Repositorio para acceso a datos de usuarios.
|
|
199
|
+
cache: Cliente de cache para optimización.
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
>>> service = UserService(repository, cache)
|
|
203
|
+
>>> user = await service.create({"email": "test@example.com"})
|
|
204
|
+
"""
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Clases y Métodos
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from abc import ABC, abstractmethod
|
|
211
|
+
from typing import Optional
|
|
212
|
+
|
|
213
|
+
class BaseService(ABC):
|
|
214
|
+
"""Clase base para servicios."""
|
|
215
|
+
|
|
216
|
+
def __init__(self, repository: BaseRepository):
|
|
217
|
+
self.repository = repository
|
|
218
|
+
self._cache: Dict[str, Any] = {}
|
|
219
|
+
|
|
220
|
+
@abstractmethod
|
|
221
|
+
async def get_by_id(self, id: int) -> Optional[Model]:
|
|
222
|
+
"""Obtiene entidad por ID."""
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
async def get_all(self, limit: int = 100) -> List[Model]:
|
|
226
|
+
"""Obtiene todas las entidades."""
|
|
227
|
+
return await self.repository.find_all(limit=limit)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class UserService(BaseService):
|
|
231
|
+
"""Servicio de usuarios."""
|
|
232
|
+
|
|
233
|
+
async def get_by_id(self, id: int) -> Optional[User]:
|
|
234
|
+
"""Obtiene usuario por ID."""
|
|
235
|
+
# Intentar cache primero
|
|
236
|
+
cache_key = f"user:{id}"
|
|
237
|
+
if cache_key in self._cache:
|
|
238
|
+
return self._cache[cache_key]
|
|
239
|
+
|
|
240
|
+
user = await self.repository.find_by_id(id)
|
|
241
|
+
if user:
|
|
242
|
+
self._cache[cache_key] = user
|
|
243
|
+
|
|
244
|
+
return user
|
|
245
|
+
|
|
246
|
+
async def create(self, data: UserCreate) -> User:
|
|
247
|
+
"""Crea un nuevo usuario."""
|
|
248
|
+
# Validar
|
|
249
|
+
self._validate_email(data.email)
|
|
250
|
+
|
|
251
|
+
# Crear
|
|
252
|
+
user = await self.repository.create(data)
|
|
253
|
+
|
|
254
|
+
# Invalidar cache
|
|
255
|
+
self._cache.clear()
|
|
256
|
+
|
|
257
|
+
return user
|
|
258
|
+
|
|
259
|
+
def _validate_email(self, email: str) -> None:
|
|
260
|
+
"""Valida formato de email."""
|
|
261
|
+
if "@" not in email:
|
|
262
|
+
raise ValueError("Invalid email format")
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Async/Await
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
# ✅ Correcto - Consistente uso de async
|
|
269
|
+
async def fetch_user_data(user_id: int) -> UserData:
|
|
270
|
+
"""Obtiene datos completos del usuario."""
|
|
271
|
+
# Ejecutar en paralelo cuando sea posible
|
|
272
|
+
user, orders, preferences = await asyncio.gather(
|
|
273
|
+
get_user(user_id),
|
|
274
|
+
get_orders(user_id),
|
|
275
|
+
get_preferences(user_id)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return UserData(user=user, orders=orders, preferences=preferences)
|
|
279
|
+
|
|
280
|
+
# ❌ Incorrecto - Blocking en código async
|
|
281
|
+
async def bad_fetch():
|
|
282
|
+
import requests # Blocking!
|
|
283
|
+
response = requests.get(url) # Bloquea el event loop
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Manejo de Errores
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
# Definir excepciones específicas
|
|
290
|
+
class ServiceError(Exception):
|
|
291
|
+
"""Error base para servicios."""
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
class NotFoundError(ServiceError):
|
|
295
|
+
"""Recurso no encontrado."""
|
|
296
|
+
pass
|
|
297
|
+
|
|
298
|
+
class ValidationError(ServiceError):
|
|
299
|
+
"""Error de validación."""
|
|
300
|
+
def __init__(self, field: str, message: str):
|
|
301
|
+
self.field = field
|
|
302
|
+
self.message = message
|
|
303
|
+
super().__init__(f"{field}: {message}")
|
|
304
|
+
|
|
305
|
+
# Uso
|
|
306
|
+
async def get_user(user_id: int) -> User:
|
|
307
|
+
user = await repository.find_by_id(user_id)
|
|
308
|
+
if not user:
|
|
309
|
+
raise NotFoundError(f"User {user_id} not found")
|
|
310
|
+
return user
|
|
311
|
+
|
|
312
|
+
# En routers, convertir a HTTPException
|
|
313
|
+
@router.get("/users/{user_id}")
|
|
314
|
+
async def get_user_endpoint(user_id: int):
|
|
315
|
+
try:
|
|
316
|
+
return await service.get_user(user_id)
|
|
317
|
+
except NotFoundError as e:
|
|
318
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
319
|
+
except ValidationError as e:
|
|
320
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## TypeScript
|
|
326
|
+
|
|
327
|
+
### Herramientas
|
|
328
|
+
```bash
|
|
329
|
+
# Linting y formatting
|
|
330
|
+
eslint .
|
|
331
|
+
prettier --write .
|
|
332
|
+
|
|
333
|
+
# Type checking
|
|
334
|
+
tsc --noEmit
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Configuración (tsconfig.json)
|
|
338
|
+
```json
|
|
339
|
+
{
|
|
340
|
+
"compilerOptions": {
|
|
341
|
+
"target": "ES2022",
|
|
342
|
+
"module": "NodeNext",
|
|
343
|
+
"moduleResolution": "NodeNext",
|
|
344
|
+
"strict": true,
|
|
345
|
+
"esModuleInterop": true,
|
|
346
|
+
"skipLibCheck": true,
|
|
347
|
+
"forceConsistentCasingInFileNames": true,
|
|
348
|
+
"declaration": true,
|
|
349
|
+
"outDir": "./dist"
|
|
350
|
+
},
|
|
351
|
+
"include": ["src/**/*"],
|
|
352
|
+
"exclude": ["node_modules", "dist"]
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Naming Conventions
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
// Archivos: kebab-case
|
|
360
|
+
user-service.ts
|
|
361
|
+
database-utils.ts
|
|
362
|
+
|
|
363
|
+
// Interfaces: PascalCase con prefijo I (opcional)
|
|
364
|
+
interface User {
|
|
365
|
+
id: number;
|
|
366
|
+
email: string;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
interface IUserService { // Prefijo I para interfaces de servicio
|
|
370
|
+
getUser(id: number): Promise<User>;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Types: PascalCase
|
|
374
|
+
type UserId = number;
|
|
375
|
+
type UserRole = 'admin' | 'user' | 'guest';
|
|
376
|
+
|
|
377
|
+
// Clases: PascalCase
|
|
378
|
+
class UserService implements IUserService {
|
|
379
|
+
// ...
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Funciones y métodos: camelCase
|
|
383
|
+
function getUserById(id: number): Promise<User> {
|
|
384
|
+
// ...
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Variables: camelCase
|
|
388
|
+
const userCount = 0;
|
|
389
|
+
const isActive = true;
|
|
390
|
+
|
|
391
|
+
// Constantes: UPPER_SNAKE_CASE o camelCase
|
|
392
|
+
const MAX_RETRY_COUNT = 3;
|
|
393
|
+
const defaultTimeout = 30;
|
|
394
|
+
|
|
395
|
+
// Enums: PascalCase para nombre y valores
|
|
396
|
+
enum UserRole {
|
|
397
|
+
Admin = 'admin',
|
|
398
|
+
User = 'user',
|
|
399
|
+
Guest = 'guest',
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Types (Obligatorios)
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// ✅ Correcto - Types explícitos
|
|
407
|
+
interface CreateUserDto {
|
|
408
|
+
email: string;
|
|
409
|
+
name: string;
|
|
410
|
+
role?: UserRole;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
interface UserResponse {
|
|
414
|
+
id: number;
|
|
415
|
+
email: string;
|
|
416
|
+
name: string;
|
|
417
|
+
createdAt: Date;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function createUser(data: CreateUserDto): Promise<UserResponse> {
|
|
421
|
+
// ...
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Usar generics cuando sea apropiado
|
|
425
|
+
interface Repository<T> {
|
|
426
|
+
findById(id: number): Promise<T | null>;
|
|
427
|
+
create(data: Partial<T>): Promise<T>;
|
|
428
|
+
update(id: number, data: Partial<T>): Promise<T>;
|
|
429
|
+
delete(id: number): Promise<void>;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
class UserRepository implements Repository<User> {
|
|
433
|
+
// ...
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ❌ Incorrecto - Usar any
|
|
437
|
+
function processData(data: any): any {
|
|
438
|
+
// Evitar any, usar unknown si es necesario
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Imports
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// ✅ Correcto - Organizados
|
|
446
|
+
// Node built-ins
|
|
447
|
+
import { readFile } from 'fs/promises';
|
|
448
|
+
import path from 'path';
|
|
449
|
+
|
|
450
|
+
// External packages
|
|
451
|
+
import express, { Request, Response } from 'express';
|
|
452
|
+
import { PrismaClient } from '@prisma/client';
|
|
453
|
+
|
|
454
|
+
// Internal - absolute imports
|
|
455
|
+
import { UserService } from '@/services/user-service';
|
|
456
|
+
import { User } from '@/models/user';
|
|
457
|
+
|
|
458
|
+
// Internal - relative imports
|
|
459
|
+
import { validateEmail } from './utils';
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Error Handling
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// Custom errors
|
|
466
|
+
class AppError extends Error {
|
|
467
|
+
constructor(
|
|
468
|
+
public statusCode: number,
|
|
469
|
+
message: string,
|
|
470
|
+
public isOperational = true
|
|
471
|
+
) {
|
|
472
|
+
super(message);
|
|
473
|
+
Object.setPrototypeOf(this, AppError.prototype);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
class NotFoundError extends AppError {
|
|
478
|
+
constructor(resource: string) {
|
|
479
|
+
super(404, `${resource} not found`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
class ValidationError extends AppError {
|
|
484
|
+
constructor(
|
|
485
|
+
public field: string,
|
|
486
|
+
message: string
|
|
487
|
+
) {
|
|
488
|
+
super(400, `${field}: ${message}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Uso
|
|
493
|
+
async function getUser(id: number): Promise<User> {
|
|
494
|
+
const user = await prisma.user.findUnique({ where: { id } });
|
|
495
|
+
if (!user) {
|
|
496
|
+
throw new NotFoundError('User');
|
|
497
|
+
}
|
|
498
|
+
return user;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Error handler middleware
|
|
502
|
+
function errorHandler(
|
|
503
|
+
err: Error,
|
|
504
|
+
req: Request,
|
|
505
|
+
res: Response,
|
|
506
|
+
next: NextFunction
|
|
507
|
+
): void {
|
|
508
|
+
if (err instanceof AppError) {
|
|
509
|
+
res.status(err.statusCode).json({
|
|
510
|
+
error: err.message,
|
|
511
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
|
|
512
|
+
});
|
|
513
|
+
} else {
|
|
514
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Reglas Comunes
|
|
522
|
+
|
|
523
|
+
### Comentarios
|
|
524
|
+
|
|
525
|
+
```python
|
|
526
|
+
# ✅ Comentarios que explican POR QUÉ, no QUÉ
|
|
527
|
+
# Usamos cache de 5 minutos porque los datos de usuario
|
|
528
|
+
# cambian con poca frecuencia pero se consultan mucho
|
|
529
|
+
CACHE_TTL = 300
|
|
530
|
+
|
|
531
|
+
# ❌ Comentarios innecesarios
|
|
532
|
+
# Incrementar contador
|
|
533
|
+
counter += 1 # Increment counter
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### TODO y FIXME
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
# TODO: Implementar paginación cuando tengamos más de 1000 usuarios
|
|
540
|
+
# FIXME: Este workaround es necesario por bug en librería X, remover en v2.0
|
|
541
|
+
# HACK: Solución temporal hasta que se resuelva issue #123
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Longitud de Línea
|
|
545
|
+
- Máximo: 88 caracteres (Python), 100 caracteres (TypeScript)
|
|
546
|
+
- Preferir líneas más cortas cuando sea posible
|
|
547
|
+
|
|
548
|
+
### Espaciado
|
|
549
|
+
- 4 espacios para indentación (Python)
|
|
550
|
+
- 2 espacios para indentación (TypeScript)
|
|
551
|
+
- Una línea en blanco entre funciones/métodos
|
|
552
|
+
- Dos líneas en blanco entre clases
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## ✅ Checklist de Validación (Antes de PR)
|
|
557
|
+
|
|
558
|
+
### Python
|
|
559
|
+
- [ ] `ruff check .` pasa sin errores
|
|
560
|
+
- [ ] `ruff format --check .` pasa
|
|
561
|
+
- [ ] Type hints en todas las funciones públicas
|
|
562
|
+
- [ ] Docstrings en funciones públicas
|
|
563
|
+
- [ ] Imports ordenados (stdlib → 3rd party → local)
|
|
564
|
+
|
|
565
|
+
### TypeScript
|
|
566
|
+
- [ ] `eslint .` pasa sin errores
|
|
567
|
+
- [ ] `prettier --check .` pasa
|
|
568
|
+
- [ ] `tsc --noEmit` sin errores de tipos
|
|
569
|
+
- [ ] Sin uso de `any`
|
|
570
|
+
- [ ] Imports ordenados
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Documentación Continua
|
|
2
|
+
|
|
3
|
+
> **Tipo**: `rule` | **Versión**: 2.1 | **Actualización**: 2026-01
|
|
4
|
+
|
|
5
|
+
## 📌 Quick Reference
|
|
6
|
+
|
|
7
|
+
| ¿Qué documentar? | ¿Dónde? |
|
|
8
|
+
|------------------|--------|
|
|
9
|
+
| Decisiones de stack | `rules/stack.md` |
|
|
10
|
+
| Patrones de código | `rules/code-style.md` |
|
|
11
|
+
| Flujos de trabajo | `rules/workflow.md` |
|
|
12
|
+
| Comandos del proyecto | `config/commands.yaml` |
|
|
13
|
+
| Cambios de versión | `CHANGELOG.md` |
|
|
14
|
+
|
|
15
|
+
### Regla de Oro
|
|
16
|
+
> "⚡ Si pensaste más de 5 minutos en algo, probablemente vale la pena documentarlo."
|
|
17
|
+
|
|
18
|
+
### 👥 Roles que usan esta regla
|
|
19
|
+
`technical-writer`, `backend-engineer`, `frontend-engineer`, `architect`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
> ⚠️ **REGLA CRÍTICA**: Esta regla debe aplicarse SIEMPRE, en cada sesión de trabajo.
|
|
24
|
+
|
|
25
|
+
## Cuándo Documentar
|
|
26
|
+
|
|
27
|
+
### Después de CADA implementación significativa:
|
|
28
|
+
|
|
29
|
+
1. **Nuevas funcionalidades** → Actualizar README o docs relevantes
|
|
30
|
+
2. **Cambios de arquitectura** → Actualizar `rules/stack.md` o crear diagrama
|
|
31
|
+
3. **Nuevos patrones** → Documentar en reglas aplicables
|
|
32
|
+
4. **Bugs resueltos** → Agregar a CHANGELOG o notas
|
|
33
|
+
5. **Decisiones técnicas** → Documentar el "por qué"
|
|
34
|
+
|
|
35
|
+
### Al usar el framework:
|
|
36
|
+
|
|
37
|
+
1. **Nuevas reglas aprendidas** → Agregar a `rules/`
|
|
38
|
+
2. **Workflows útiles** → Crear en `workflows/`
|
|
39
|
+
3. **Comandos frecuentes** → Agregar a `config/commands.yaml`
|
|
40
|
+
4. **Personas personalizadas** → Crear en `personas/`
|
|
41
|
+
|
|
42
|
+
## Qué Documentar
|
|
43
|
+
|
|
44
|
+
| Tipo | Dónde | Ejemplo |
|
|
45
|
+
|------|-------|---------|
|
|
46
|
+
| Decisiones de stack | `rules/stack.md` | "Usamos PostgreSQL porque..." |
|
|
47
|
+
| Patrones de código | `rules/code-style.md` | "Nombres de funciones en snake_case" |
|
|
48
|
+
| Flujos de trabajo | `rules/workflow.md` | "PRs requieren 2 reviewers" |
|
|
49
|
+
| Comandos del proyecto | `config/commands.yaml` | Alias personalizados |
|
|
50
|
+
| Changelog | `CHANGELOG.md` | Versiones y cambios |
|
|
51
|
+
|
|
52
|
+
## Instrucciones para el Agente
|
|
53
|
+
|
|
54
|
+
> **SIEMPRE al finalizar una tarea**:
|
|
55
|
+
|
|
56
|
+
```markdown
|
|
57
|
+
## Checklist de Cierre
|
|
58
|
+
|
|
59
|
+
- [ ] ¿Creé algo nuevo que otros deberían saber?
|
|
60
|
+
- [ ] ¿Cambié algún patrón o convención?
|
|
61
|
+
- [ ] ¿Resolví un problema que podría repetirse?
|
|
62
|
+
- [ ] ¿Las reglas actuales siguen siendo válidas?
|
|
63
|
+
|
|
64
|
+
Si respondí SÍ a cualquiera → ACTUALIZAR DOCUMENTACIÓN
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Formato Recomendado
|
|
68
|
+
|
|
69
|
+
### Para reglas nuevas:
|
|
70
|
+
|
|
71
|
+
```markdown
|
|
72
|
+
## [Nombre de la Regla]
|
|
73
|
+
|
|
74
|
+
**Contexto**: Por qué existe esta regla
|
|
75
|
+
**Regla**: Qué hacer específicamente
|
|
76
|
+
**Ejemplo**: Código o configuración de ejemplo
|
|
77
|
+
**Excepciones**: Cuándo no aplica (si hay)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Para cambios:
|
|
81
|
+
|
|
82
|
+
```markdown
|
|
83
|
+
## [Fecha] - [Título del Cambio]
|
|
84
|
+
|
|
85
|
+
**Antes**: Cómo era
|
|
86
|
+
**Después**: Cómo es ahora
|
|
87
|
+
**Razón**: Por qué se cambió
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Recordatorio Automático
|
|
91
|
+
|
|
92
|
+
Si el agente no documenta cambios significativos, recordarle:
|
|
93
|
+
|
|
94
|
+
> "¿No deberías documentar este cambio en [archivo relevante]?"
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
**⚡ Regla de oro**: Si tuviste que pensar más de 5 minutos en algo, probablemente vale la pena documentarlo.
|