@ronaldjdevfs/forge 1.0.2 → 1.1.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/README.md +130 -8
- package/package.json +2 -2
- package/skills/forge/SKILL.md +86 -15
- package/skills/forge/profiles/express-drizzle.md +107 -0
- package/skills/forge/profiles/fastify-mongodb.md +103 -0
- package/skills/forge/profiles/fastify-prisma.md +81 -0
- package/skills/forge/profiles/nestjs-mongodb.md +92 -0
- package/skills/forge/profiles/nestjs-postgres.md +98 -0
- package/skills/forge/reference/api-design.md +62 -0
- package/skills/forge/reference/assay.md +82 -0
- package/skills/forge/reference/cast.md +81 -7
- package/skills/forge/reference/data-patterns.md +86 -0
- package/skills/forge/reference/di-strategies.md +50 -0
- package/skills/forge/reference/errors.md +65 -0
- package/skills/forge/reference/events.md +95 -0
- package/skills/forge/reference/help.md +40 -0
- package/skills/forge/reference/hooks.md +62 -0
- package/skills/forge/reference/observability.md +66 -0
- package/skills/forge/reference/patterns.md +52 -0
- package/skills/forge/reference/principles.md +6 -0
- package/skills/forge/reference/reforge.md +69 -5
- package/skills/forge/reference/relocate.md +15 -2
- package/skills/forge/reference/security-patterns.md +87 -0
- package/skills/forge/reference/testing-patterns.md +69 -0
- package/skills/forge/scripts/assay.mjs +481 -0
- package/skills/forge/scripts/context.mjs +147 -43
- package/skills/forge/scripts/detect.mjs +371 -22
- package/skills/forge/scripts/forge-api.mjs +373 -0
- package/skills/forge/scripts/forge-config.mjs +268 -0
- package/skills/forge/scripts/forge-signals.mjs +131 -0
- package/skills/forge/scripts/forge-state.mjs +97 -0
- package/skills/forge/scripts/formatter.mjs +133 -0
- package/skills/forge/scripts/graph.mjs +5 -21
- package/skills/forge/scripts/hook.mjs +250 -0
- package/skills/forge/scripts/inspect.mjs +171 -22
- package/skills/forge/scripts/parse-imports.mjs +249 -0
- package/skills/forge/scripts/pin.mjs +151 -0
- package/skills/forge/scripts/posttool.mjs +224 -0
- package/skills/forge/scripts/profile.mjs +124 -20
- package/skills/forge/scripts/registry/rules.mjs +344 -0
- package/skills/forge/scripts/rename.mjs +669 -0
- package/skills/forge/scripts/rollback.mjs +213 -0
- package/skills/forge/scripts/update.mjs +114 -0
- package/skills/forge/templates/feature/domain-error.ts.md +9 -0
- package/skills/forge/templates/feature/domain-event.ts.md +9 -0
- package/skills/forge/templates/feature/event-handler.ts.md +10 -0
- package/skills/forge/templates/feature/use-case.ts.md +10 -2
- package/skills/forge/tests/core.test.mjs +403 -0
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Los proyectos backend degeneran en código acoplado porque la infraestructura t
|
|
|
46
46
|
Detecta el stack tecnológico, ejecuta bootstrap de platform/shared/infra si no existen, determina el perfil activo, analiza ownership y prepara el proyecto. Crea `ARCHITECTURE.md` si no existe.
|
|
47
47
|
|
|
48
48
|
```
|
|
49
|
-
Boot sequence: context →
|
|
49
|
+
Boot sequence: context → armorer → profile → graph → chain → inspect → architecture → execute → architecture
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
### `cast` — Crear feature
|
|
@@ -143,6 +143,99 @@ src/shared/
|
|
|
143
143
|
└── utils/ # <util>.ts (formatDate, pagination)
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
### `assay` — Ensayo multi-persona
|
|
147
|
+
|
|
148
|
+
Evalúa la arquitectura desde 5 perspectivas distintas:
|
|
149
|
+
|
|
150
|
+
| Persona | Enfoque |
|
|
151
|
+
|---------|---------|
|
|
152
|
+
| **Jeff Bezos** | Acoplamiento, escalabilidad, equipos autónomos (API mandate) |
|
|
153
|
+
| **Martin Fowler** | Refactoring, deuda técnica, microservicios vs monolitos |
|
|
154
|
+
| **Hacker** | Seguridad, performance, edge cases, vulnerabilidades |
|
|
155
|
+
| **Alex (PM)** | Tiempo de entrega, complejidad, ROI técnico |
|
|
156
|
+
| **Dra. Carter** | Dependencias cíclicas, violaciones de capas, salud estructural |
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
forge assay # Ensayo completo (5 personas)
|
|
160
|
+
forge assay --persona=bezos # Solo Bezos
|
|
161
|
+
forge assay --persona=fowler # Solo Fowler
|
|
162
|
+
forge assay --save # Persiste en .forge/assay/
|
|
163
|
+
forge assay --json # Salida JSON
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `nail / unnail` — Shortcuts de navegación
|
|
167
|
+
|
|
168
|
+
Fija rutas del proyecto como atajos para acceso rápido:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
nail src/features/auth # Crea atajo "auth"
|
|
172
|
+
nail src/platform/config # Crea atajo "config"
|
|
173
|
+
nail --list # Lista atajos
|
|
174
|
+
unnail auth # Elimina atajo
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### `forge state` — Estado persistente
|
|
178
|
+
|
|
179
|
+
Muestra y guarda el estado post-auditoría:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
forge state # Último score, grade, violaciones
|
|
183
|
+
forge state --show # Estado detallado
|
|
184
|
+
forge state --json # Salida JSON
|
|
185
|
+
forge state --history # Histórico de auditorías
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `forge hook` — Git pre-commit hook
|
|
189
|
+
|
|
190
|
+
Instala un hook pre-commit que valida la arquitectura en cada commit:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
forge hook install # Instalar hook
|
|
194
|
+
forge hook status # Ver estado
|
|
195
|
+
forge hook check # Validar archivos staged
|
|
196
|
+
forge hook uninstall # Eliminar hook
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### `forge rollback` — Restauración
|
|
200
|
+
|
|
201
|
+
Restaura el proyecto a un punto anterior:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
forge rollback # Último checkpoint
|
|
205
|
+
forge rollback --list # Lista checkpoints
|
|
206
|
+
forge rollback --id <checkpoint> # Restaurar específico
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### `forge update` — Actualización
|
|
210
|
+
|
|
211
|
+
Verifica si hay una nueva versión de Forge disponible.
|
|
212
|
+
|
|
213
|
+
### Inline Ignores
|
|
214
|
+
|
|
215
|
+
Forge soporta excepciones línea por línea para reglas arquitectónicas:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// forge-ignore-next-line
|
|
219
|
+
import { something } from "../infra/prisma"; // ← no se reporta
|
|
220
|
+
|
|
221
|
+
// forge-ignore: R1
|
|
222
|
+
import { PrismaClient } from "../../infra/prisma/client"; // ← solo R1 ignorada
|
|
223
|
+
|
|
224
|
+
// forge-ignore: R1, R8
|
|
225
|
+
import { crossFeature } from "../other-feature/domain/Entity"; // ← R1 y R8 ignoradas
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Usar `quench --show-ignores` para listar todos los ignores en el código.
|
|
229
|
+
|
|
230
|
+
### Flags adicionales
|
|
231
|
+
|
|
232
|
+
| Flag | Comando | Descripción |
|
|
233
|
+
|------|---------|-------------|
|
|
234
|
+
| `--fix` | `quench` | Auto-corrige violaciones WARNING/INFO (missing @injectable(), tsconfig, naming, container.resolve) |
|
|
235
|
+
| `--show-ignores` | `quench` | Muestra los inline ignores encontrados en el código |
|
|
236
|
+
| `--persona=<id>` | `assay` | Filtra ensayo por una persona (bezos, fowler, hacker, pm, senior) |
|
|
237
|
+
| `--save` | `assay` | Persiste ensayo en `.forge/assay/` |
|
|
238
|
+
|
|
146
239
|
---
|
|
147
240
|
|
|
148
241
|
## Modelo arquitectónico
|
|
@@ -224,7 +317,12 @@ Ver `reference/patterns.md` para el detalle completo.
|
|
|
224
317
|
| `express-mongodb` | Express | MongoDB | Mongoose | tsyringe |
|
|
225
318
|
| `express-postgres` | Express | PostgreSQL | raw pg | Manual |
|
|
226
319
|
| `express-prisma` | Express | PostgreSQL | Prisma | tsyringe |
|
|
320
|
+
| `express-drizzle` | Express | PostgreSQL | Drizzle | tsyringe |
|
|
321
|
+
| `fastify-mongodb` | Fastify | MongoDB | Mongoose | Manual |
|
|
227
322
|
| `fastify-postgres` | Fastify | PostgreSQL | Prisma | Manual |
|
|
323
|
+
| `fastify-prisma` | Fastify | PostgreSQL | Prisma | Manual |
|
|
324
|
+
| `nestjs-mongodb` | NestJS | MongoDB | Mongoose | NestJS DI |
|
|
325
|
+
| `nestjs-postgres` | NestJS | PostgreSQL | Prisma | NestJS DI |
|
|
228
326
|
| `nestjs-prisma` | NestJS | PostgreSQL | Prisma | NestJS DI |
|
|
229
327
|
|
|
230
328
|
Cada perfil define estructura de directorios, setup de DI, routing, persistencia, testing y naming conventions.
|
|
@@ -251,17 +349,33 @@ Donde vive toda la inteligencia arquitectónica:
|
|
|
251
349
|
| `scripts/profile.mjs` | Matchea stack contra perfiles conocidos o sintetiza uno genérico |
|
|
252
350
|
| `scripts/graph.mjs` | Grafo completo: 6 tipos de nodo, 4 capas, 9 reglas (R1-R9), risk score, dependency health |
|
|
253
351
|
| `scripts/chain.mjs` | Grafo multi-capa (platform, features, shared, infra) con orden topológico |
|
|
254
|
-
| `scripts/detect.mjs` |
|
|
352
|
+
| `scripts/detect.mjs` | Validación de reglas R1-R9 con inline ignores y `--fix` |
|
|
255
353
|
| `scripts/inspect.mjs` | Orquesta auditoría completa con reporte coloreado |
|
|
256
354
|
| `scripts/architecture.mjs` | Genera/actualiza `ARCHITECTURE.md` vivo |
|
|
257
355
|
| `scripts/bootstrap.mjs` | Inicializa platform/shared/infra según perfil (interno) |
|
|
356
|
+
| `scripts/formatter.mjs` | Output unificado: colores, JSON, scoreBar, formatCheck |
|
|
357
|
+
| `scripts/registry/rules.mjs` | Registry de reglas R1-R9 + custom rules desacoplado |
|
|
358
|
+
| `scripts/assay.mjs` | Ensayo multi-persona (Bezos, Fowler, Hacker, PM, Arquitecta) |
|
|
359
|
+
| `scripts/posttool.mjs` | PostToolUse hook con `--reminder` y `--strict` |
|
|
360
|
+
| `scripts/forge-config.mjs` | Persistencia de config, estado e histórico |
|
|
361
|
+
| `scripts/forge-state.mjs` | CLI wrapper de estado post-auditoría |
|
|
362
|
+
| `scripts/forge-signals.mjs` | Manejo de señales (SIGINT, SIGTERM) |
|
|
363
|
+
| `scripts/forge-api.mjs` | Validación de contratos API |
|
|
364
|
+
| `scripts/hook.mjs` | Gestión de git pre-commit hook |
|
|
365
|
+
| `scripts/pin.mjs` | Shortcuts de navegación (`nail`/`unnail`) |
|
|
366
|
+
| `scripts/rollback.mjs` | Restauración de puntos de guardado |
|
|
367
|
+
| `scripts/rename.mjs` | Renombrado bulk de componentes |
|
|
368
|
+
| `scripts/parse-imports.mjs` | Parsing de imports ESM |
|
|
369
|
+
| `scripts/update.mjs` | Verificador de actualizaciones |
|
|
258
370
|
| `reference/` | Documentación detallada de cada comando y principios |
|
|
259
371
|
| `reference/patterns.md` | Convenciones de nomenclatura globales |
|
|
260
|
-
| `
|
|
261
|
-
| `
|
|
262
|
-
| `
|
|
263
|
-
| `templates/
|
|
264
|
-
| `templates/
|
|
372
|
+
| `reference/assay.md` | Documentación del comando assay |
|
|
373
|
+
| `reference/hooks.md` | Documentación del sistema de hooks |
|
|
374
|
+
| `profiles/` | 10 perfiles tecnológicos detallados |
|
|
375
|
+
| `templates/feature/` | 11 templates TypeScript para features |
|
|
376
|
+
| `templates/platform/` | 6 templates para componentes de platform |
|
|
377
|
+
| `templates/shared/` | 4 templates para shared (errors, contracts, types, utils) |
|
|
378
|
+
| `templates/infra/` | 4 templates para infra (prisma, mongodb, redis, mail) |
|
|
265
379
|
|
|
266
380
|
---
|
|
267
381
|
|
|
@@ -297,7 +411,7 @@ forge install -g # global
|
|
|
297
411
|
|
|
298
412
|
## Uso
|
|
299
413
|
|
|
300
|
-
Una vez instalada, OpenCode carga automáticamente la skill `forge` al trabajar en el proyecto. Los comandos se invocan por lenguaje natural:
|
|
414
|
+
Una vez instalada, OpenCode carga automáticamente la skill `forge` al trabajar en el proyecto. Usa `forge --help` para ver la lista completa de comandos. Los comandos también se invocan por lenguaje natural:
|
|
301
415
|
|
|
302
416
|
| Lenguaje natural | Comando |
|
|
303
417
|
|---|---|
|
|
@@ -311,6 +425,14 @@ Una vez instalada, OpenCode carga automáticamente la skill `forge` al trabajar
|
|
|
311
425
|
| "cadena", "grafo", "acoplamiento" | `chain` |
|
|
312
426
|
| "inscribir", "grabar", "ARCHITECTURE.md" | `inscribe` |
|
|
313
427
|
| "fundir", "compartir", "mover a shared" | `smelt` |
|
|
428
|
+
| "examinar", "calidad", "opinión", "critique" | `assay` |
|
|
429
|
+
| "fijar", "pinar", "shortcut" | `nail` |
|
|
430
|
+
| "desfijar", "despinar" | `unnail` |
|
|
431
|
+
| "estado", "state", "último audit" | `forge state --show` |
|
|
432
|
+
| "hook", "pre-commit", "githook" | `forge hook` |
|
|
433
|
+
| "api", "contrato", "openapi", "swagger" | `forge api` |
|
|
434
|
+
| "rollback", "restaurar", "deshacer" | `forge rollback` |
|
|
435
|
+
| "actualizar", "update", "nueva versión" | `forge update` |
|
|
314
436
|
|
|
315
437
|
---
|
|
316
438
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ronaldjdevfs/forge",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Forge — Architecture Operating System for backend systems. Arquitectura Hexagonal, DDD pragmático y vertical slices.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"forge": "
|
|
7
|
+
"forge": "src/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"src/",
|
package/skills/forge/SKILL.md
CHANGED
|
@@ -100,6 +100,7 @@ inspect=$(node .opencode/skills/forge/scripts/inspect.mjs --json 2>/dev/null)
|
|
|
100
100
|
|
|
101
101
|
| Lenguaje natural | Comando | Archivo |
|
|
102
102
|
|---|---|---|
|
|
103
|
+
| "ayuda", "help", "comandos", "lista", "--help" | `forge --help` | `reference/help.md` |
|
|
103
104
|
| "inicializar", "setup", "empezar" | `forge` | `reference/forge.md` |
|
|
104
105
|
| "crear feature", "nuevo dominio" | `cast` | `reference/cast.md` |
|
|
105
106
|
| "inspeccionar", "diagnóstico", "evaluar" | `inspect` | `reference/inspect.md` |
|
|
@@ -112,6 +113,13 @@ inspect=$(node .opencode/skills/forge/scripts/inspect.mjs --json 2>/dev/null)
|
|
|
112
113
|
| "grafo", "graph", "nodo", "violaciones", "risk score" | `graph` | `scripts/graph.mjs` |
|
|
113
114
|
| "fundir", "compartir", "mover a shared" | `smelt` | `reference/smelt.md` |
|
|
114
115
|
| "ownership", "huérfanos", "armorer" | `inspect` | (incluido en auditoría) |
|
|
116
|
+
| "fijar", "pinar", "atajo", "shortcut" | `nail` | `scripts/pin.mjs` |
|
|
117
|
+
| "desfijar", "despinar", "remover atajo" | `unnail` | `scripts/pin.mjs` |
|
|
118
|
+
| "hook", "pre-commit", "githook", "validar commit" | `forge hook` | `reference/hooks.md` |
|
|
119
|
+
| "api", "contrato", "openapi", "swagger", "graphql" | `forge api` | `scripts/forge-api.mjs` |
|
|
120
|
+
| "rollback", "restaurar", "deshacer", "backup" | `forge rollback` | `scripts/rollback.mjs` |
|
|
121
|
+
| "estado", "state", "último audit" | `forge state --show` | `scripts/forge-state.mjs` |
|
|
122
|
+
| "examinar","calidad", "assay", "opinión", "personas", "critique", "evaluación cualitativa" | `assay` | `reference/assay.md` |
|
|
115
123
|
|
|
116
124
|
---
|
|
117
125
|
|
|
@@ -140,6 +148,23 @@ Para cada comando, Forge sigue este flujo:
|
|
|
140
148
|
- Si `ARCHITECTURE.md` está desactualizado (fecha de auditoría > 7 días), sugerir `forge inscribe`.
|
|
141
149
|
- Todos los resultados se muestran con severidades: `[CRITICAL]`, `[ERROR]`, `[WARNING]`, `[INFO]`, `[SUGGESTION]`.
|
|
142
150
|
|
|
151
|
+
### Inline Ignores
|
|
152
|
+
|
|
153
|
+
Forge soporta comentarios inline para excepcionar violaciones línea por línea:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// forge-ignore-next-line
|
|
157
|
+
import { something } from "../infra/prisma"; // ← esta línea no se reporta
|
|
158
|
+
|
|
159
|
+
// forge-ignore: R1
|
|
160
|
+
import { PrismaClient } from "../../infra/prisma/client"; // ← solo R1 ignorada
|
|
161
|
+
|
|
162
|
+
// forge-ignore: R1, R8
|
|
163
|
+
import { crossFeature } from "../other-feature/domain/Entity"; // ← R1 y R8 ignoradas
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
143
168
|
## ARCHITECTURE.md
|
|
144
169
|
|
|
145
170
|
Forge mantiene un archivo `ARCHITECTURE.md` en la raíz del proyecto con el contexto persistente. Contiene:
|
|
@@ -147,15 +172,15 @@ Forge mantiene un archivo `ARCHITECTURE.md` en la raíz del proyecto con el cont
|
|
|
147
172
|
```md
|
|
148
173
|
# Architecture State
|
|
149
174
|
|
|
150
|
-
Project Name: <name>
|
|
151
|
-
Framework: <detectado>
|
|
152
|
-
Runtime: <detectado>
|
|
153
|
-
Database: <detectado>
|
|
154
|
-
ORM: <detectado>
|
|
155
|
-
DI Strategy: <detectado>
|
|
156
|
-
Profile: <detectado>
|
|
157
|
-
Architecture: hexagonal-feature (Platform + Features + Shared + Infra)
|
|
158
|
-
Last Audit: <fecha> (score: <puntaje>)
|
|
175
|
+
- Project Name: <name>
|
|
176
|
+
- Framework: <detectado>
|
|
177
|
+
- Runtime: <detectado>
|
|
178
|
+
- Database: <detectado>
|
|
179
|
+
- ORM: <detectado>
|
|
180
|
+
- DI Strategy: <detectado>
|
|
181
|
+
- Profile: <detectado>
|
|
182
|
+
- Architecture: hexagonal-feature (Platform + Features + Shared + Infra)
|
|
183
|
+
- Last Audit: <fecha> (score: <puntaje>)
|
|
159
184
|
|
|
160
185
|
## Platform
|
|
161
186
|
- platform/config/
|
|
@@ -175,11 +200,11 @@ Last Audit: <fecha> (score: <puntaje>)
|
|
|
175
200
|
...
|
|
176
201
|
|
|
177
202
|
## Ownership
|
|
178
|
-
Health: healthy | degraded | critical
|
|
179
|
-
Score: 0-100
|
|
180
|
-
Orphans: 0
|
|
181
|
-
Duplicates: 0
|
|
182
|
-
Misplaced: 0
|
|
203
|
+
- Health: healthy | degraded | critical
|
|
204
|
+
- Score: 0-100
|
|
205
|
+
- Orphans: 0
|
|
206
|
+
- Duplicates: 0
|
|
207
|
+
- Misplaced: 0
|
|
183
208
|
|
|
184
209
|
## Architecture Graph
|
|
185
210
|
...
|
|
@@ -198,10 +223,56 @@ El agente DEBE leer este archivo al inicio de cada interacción y actualizarlo a
|
|
|
198
223
|
|---|---|
|
|
199
224
|
| `reference/principles.md` | Manifiesto y 12 principios inquebrantables |
|
|
200
225
|
| `reference/patterns.md` | Convenciones de nomenclatura globales (PascalCase.artifact, kebab dirs, etc.) |
|
|
226
|
+
| `reference/errors.md` | Manejo de errores tipados en dominio y aplicación |
|
|
227
|
+
| `reference/di-strategies.md` | Estrategias de inyección de dependencias según tamaño |
|
|
228
|
+
| `reference/testing-patterns.md` | Pirámide de tests, unit mocks, integration tests |
|
|
229
|
+
| `reference/api-design.md` | REST / GraphQL, paginación, validación, contratos |
|
|
230
|
+
| `reference/observability.md` | Logging, tracing, métricas, health checks |
|
|
231
|
+
| `reference/data-patterns.md` | Repository, Unit of Work, CQRS, Event Sourcing |
|
|
232
|
+
| `reference/security-patterns.md` | AuthN, AuthZ, RBAC, rate limiting, validación |
|
|
233
|
+
| `reference/events.md` | Eventos de dominio, outbox pattern, sagas |
|
|
234
|
+
| `reference/hooks.md` | Git pre-commit hook para validación arquitectónica |
|
|
235
|
+
| `reference/help.md` | Lista completa de comandos y flags de Forge |
|
|
236
|
+
| `reference/assay.md` | Ensayo arquitectónico multi-persona — interpretación cualitativa del audit |
|
|
201
237
|
| `profiles/` | Perfiles tecnológicos detallados (Express, Fastify, NestJS, etc.) |
|
|
202
|
-
| `scripts/` | Scripts
|
|
238
|
+
| `scripts/` | Scripts: context, detect, inspect, chain, profile, graph, architecture, armorer, bootstrap, forge-config, forge-signals, forge-state, forge-api, pin, update, rollback, hook, posttool, formatter, assay, registry/rules |
|
|
239
|
+
| `scripts/registry/rules.mjs` | Anti-pattern rule registry (R1-R9 + custom rules desacopladas de detect.mjs) |
|
|
240
|
+
| `scripts/formatter.mjs` | Output formatter unificado (JSON, tabla, severidad coloreada, scoreBar, formatCheck, formatViolation) |
|
|
241
|
+
| `scripts/posttool.mjs` | PostToolUse hook — analiza archivos modificados tras escritura y reporta violaciones |
|
|
242
|
+
| `scripts/assay.mjs` | Motor de ensayo arquitectónico multi-persona (Bezos, Fowler, Hacker, PM, Arquitecta Senior) |
|
|
203
243
|
| `templates/feature/` | Templates de feature (entity, repository, uc, controller, routes, schema, mapper) |
|
|
204
244
|
| `templates/platform/` | Templates de platform (config, server, database, logger, http, di) |
|
|
205
245
|
| `templates/shared/` | Templates de shared (errors, contracts, types, utils) |
|
|
206
246
|
| `templates/infra/` | Templates de infra (prisma, mongodb, redis, mail) |
|
|
207
247
|
| `command/forge.md` | Definición del comando `/forge` para opencode |
|
|
248
|
+
|
|
249
|
+
### Tests
|
|
250
|
+
|
|
251
|
+
Forge incluye tests unitarios con `node:test` (sin dependencias externas).
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
node --test .opencode/skills/forge/tests/core.test.mjs
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
| Módulo | Tests | Descripción |
|
|
258
|
+
|--------|-------|-------------|
|
|
259
|
+
| `profile.mjs` | 8 | Detección de perfiles |
|
|
260
|
+
| `graph.mjs` | 1 | Grafo vacío |
|
|
261
|
+
| `armorer.mjs` | 1 | Ownership vacío |
|
|
262
|
+
| `forge-config.mjs` | 2 | Load/save state |
|
|
263
|
+
| `chain.mjs` | 1 | Grafo de dependencias vacío |
|
|
264
|
+
| `formatter.mjs` | 4 | Output format, colores, JSON |
|
|
265
|
+
| `registry/rules.mjs` | 4 | R1-R9, evaluación, custom rules |
|
|
266
|
+
| `detect.mjs` (inline ignores) | 5 | parseInlineIgnores, isIgnored |
|
|
267
|
+
| `posttool.mjs` | 1 | PostToolUse hook |
|
|
268
|
+
| `assay.mjs` | 4 | Personas, generateAssay, opiniones |
|
|
269
|
+
|
|
270
|
+
### Flags adicionales
|
|
271
|
+
|
|
272
|
+
| Flag | Comando | Descripción |
|
|
273
|
+
|------|---------|-------------|
|
|
274
|
+
| `--fix` | `quench` | Auto-corrige violaciones WARNING/INFO (missing @injectable(), tsconfig, naming, container.resolve) |
|
|
275
|
+
| `--show-ignores` | `quench` | Muestra los inline ignores encontrados en el código |
|
|
276
|
+
| `--persona=<id>` | `assay` | Filtra ensayo por una persona (bezos, fowler, hacker, pm, senior) |
|
|
277
|
+
| `--save` | `assay` | Persiste ensayo en `.forge/assay/` |
|
|
278
|
+
| `--json` | `assay` | Salida JSON |
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Profile: Express + Drizzle ORM
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
framework: Express
|
|
7
|
+
runtime: Node 20+
|
|
8
|
+
database: PostgreSQL / MySQL / SQLite
|
|
9
|
+
orm: Drizzle
|
|
10
|
+
di_strategy: manual
|
|
11
|
+
architecture: hexagonal-feature
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## DI Strategy
|
|
15
|
+
|
|
16
|
+
DI manual. Sin contenedor externo. Las dependencias se inyectan por constructor y se cablean en `app.ts`.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// app.ts
|
|
20
|
+
import express from "express";
|
|
21
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
22
|
+
import { Pool } from "pg";
|
|
23
|
+
|
|
24
|
+
const app = express();
|
|
25
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
26
|
+
const db = drizzle(pool);
|
|
27
|
+
|
|
28
|
+
const creditRepo = new CreditRepository(db);
|
|
29
|
+
const addCredit = new AddCredit(creditRepo);
|
|
30
|
+
const creditController = new CreditController(addCredit);
|
|
31
|
+
|
|
32
|
+
app.use("/credits", creditRoutes(creditController));
|
|
33
|
+
app.listen(3000);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Project Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
├── src/
|
|
40
|
+
│ ├── db/
|
|
41
|
+
│ │ └── schema.ts # Drizzle schema definitions
|
|
42
|
+
│ ├── features/
|
|
43
|
+
│ │ └── <domain>/
|
|
44
|
+
│ │ ├── domain/
|
|
45
|
+
│ │ ├── application/
|
|
46
|
+
│ │ └── adapters/
|
|
47
|
+
│ │ ├── in/http/
|
|
48
|
+
│ │ └── out/persistence/
|
|
49
|
+
│ ├── shared/
|
|
50
|
+
│ ├── app.ts
|
|
51
|
+
│ └── server.ts
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Persistence (Drizzle)
|
|
55
|
+
|
|
56
|
+
### Schema definition
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// src/db/schema.ts
|
|
60
|
+
import { pgTable, serial, text, timestamp, integer } from "drizzle-orm/pg-core";
|
|
61
|
+
|
|
62
|
+
export const credits = pgTable("credits", {
|
|
63
|
+
id: serial("id").primaryKey(),
|
|
64
|
+
amount: integer("amount").notNull(),
|
|
65
|
+
userId: text("user_id").notNull(),
|
|
66
|
+
createdAt: timestamp("created_at").defaultNow(),
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Repository
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { eq } from "drizzle-orm";
|
|
74
|
+
import { credits } from "@/db/schema.js";
|
|
75
|
+
|
|
76
|
+
export class CreditRepository implements ICreditRepository {
|
|
77
|
+
constructor(private readonly db: DrizzleClient) {}
|
|
78
|
+
|
|
79
|
+
async create(data: CreditInput): Promise<Credit> {
|
|
80
|
+
const [record] = await this.db.insert(credits).values(data).returning();
|
|
81
|
+
return CreditMapper.toDomain(record);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async findById(id: number): Promise<Credit | null> {
|
|
85
|
+
const [record] = await this.db.select().from(credits).where(eq(credits.id, id));
|
|
86
|
+
return record ? CreditMapper.toDomain(record) : null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Migrations
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Generate
|
|
95
|
+
npx drizzle-kit generate
|
|
96
|
+
|
|
97
|
+
# Apply
|
|
98
|
+
npx drizzle-kit migrate
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Testing Strategy
|
|
102
|
+
|
|
103
|
+
Unit: mocks de DrizzleClient. Integration: base de datos de test independiente.
|
|
104
|
+
|
|
105
|
+
## Naming Conventions
|
|
106
|
+
|
|
107
|
+
Las tablas en Drizzle siguen camelCase en el esquema, snake_case en PostgreSQL.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Profile: Fastify + MongoDB + Mongoose
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
framework: Fastify
|
|
7
|
+
runtime: Node 20+
|
|
8
|
+
database: MongoDB
|
|
9
|
+
orm: Mongoose
|
|
10
|
+
di_strategy: manual
|
|
11
|
+
architecture: hexagonal-feature
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## DI Strategy
|
|
15
|
+
|
|
16
|
+
DI manual. Sin contenedor. Cableado en `app.ts`.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// app.ts
|
|
20
|
+
import Fastify from "fastify";
|
|
21
|
+
import mongoose from "mongoose";
|
|
22
|
+
|
|
23
|
+
await mongoose.connect(process.env.MONGO_URI);
|
|
24
|
+
|
|
25
|
+
const app = Fastify({ logger: true });
|
|
26
|
+
const creditRepo = new CreditRepository(mongoose.connection);
|
|
27
|
+
const addCredit = new AddCredit(creditRepo);
|
|
28
|
+
const creditController = new CreditController(addCredit);
|
|
29
|
+
|
|
30
|
+
app.register(creditRoutes(creditController));
|
|
31
|
+
await app.listen({ port: 3000 });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Project Structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
├── src/
|
|
38
|
+
│ ├── features/
|
|
39
|
+
│ │ └── <domain>/
|
|
40
|
+
│ │ ├── domain/
|
|
41
|
+
│ │ ├── application/
|
|
42
|
+
│ │ └── adapters/
|
|
43
|
+
│ │ ├── in/http/
|
|
44
|
+
│ │ └── out/persistence/
|
|
45
|
+
│ ├── shared/
|
|
46
|
+
│ ├── infrastructure/
|
|
47
|
+
│ │ ├── mongoose.ts # Mongoose connection
|
|
48
|
+
│ │ └── models/ # Mongoose schemas
|
|
49
|
+
│ ├── app.ts
|
|
50
|
+
│ └── server.ts
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Persistence (Mongoose)
|
|
54
|
+
|
|
55
|
+
### Model schema
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// infrastructure/models/credit.model.ts
|
|
59
|
+
import { Schema, model } from "mongoose";
|
|
60
|
+
|
|
61
|
+
const creditSchema = new Schema({
|
|
62
|
+
amount: { type: Number, required: true },
|
|
63
|
+
userId: { type: String, required: true },
|
|
64
|
+
}, { timestamps: true });
|
|
65
|
+
|
|
66
|
+
export const CreditModel = model("Credit", creditSchema);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Repository
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
export class CreditRepository implements ICreditRepository {
|
|
73
|
+
async create(data: CreditInput): Promise<Credit> {
|
|
74
|
+
const doc = await CreditModel.create(data);
|
|
75
|
+
return CreditMapper.toDomain(doc.toObject());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Routes & Controllers
|
|
81
|
+
|
|
82
|
+
Usar patrón Fastify plugin (ver `fastify-postgres`).
|
|
83
|
+
|
|
84
|
+
## Testing Strategy
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { MongoMemoryServer } from "mongodb-memory-server";
|
|
88
|
+
|
|
89
|
+
let mongod: MongoMemoryServer;
|
|
90
|
+
|
|
91
|
+
beforeAll(async () => {
|
|
92
|
+
mongod = await MongoMemoryServer.create();
|
|
93
|
+
await mongoose.connect(mongod.getUri());
|
|
94
|
+
});
|
|
95
|
+
afterAll(async () => {
|
|
96
|
+
await mongoose.disconnect();
|
|
97
|
+
await mongod.stop();
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Naming Conventions
|
|
102
|
+
|
|
103
|
+
Mismas que `express-mongodb`. Colecciones en snake_case, campos en camelCase.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Profile: Fastify + Prisma
|
|
2
|
+
|
|
3
|
+
## Metadata
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
framework: Fastify
|
|
7
|
+
runtime: Node 20+
|
|
8
|
+
database: Cualquier RDBMS (PostgreSQL, MySQL, SQLite)
|
|
9
|
+
orm: Prisma
|
|
10
|
+
di_strategy: manual
|
|
11
|
+
architecture: hexagonal-feature
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## DI Strategy
|
|
15
|
+
|
|
16
|
+
DI manual. Sin contenedor externo. Cableado explícito en `app.ts`.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// app.ts
|
|
20
|
+
import Fastify from "fastify";
|
|
21
|
+
import { PrismaClient } from "@prisma/client";
|
|
22
|
+
|
|
23
|
+
const app = Fastify({ logger: true });
|
|
24
|
+
const prisma = new PrismaClient();
|
|
25
|
+
const creditRepo = new CreditRepository(prisma);
|
|
26
|
+
const addCredit = new AddCredit(creditRepo);
|
|
27
|
+
const creditController = new CreditController(addCredit);
|
|
28
|
+
|
|
29
|
+
app.register(creditRoutes(creditController));
|
|
30
|
+
await app.listen({ port: 3000 });
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Project Structure
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
├── prisma/
|
|
37
|
+
│ └── schema.prisma
|
|
38
|
+
├── src/
|
|
39
|
+
│ ├── features/
|
|
40
|
+
│ │ └── <domain>/
|
|
41
|
+
│ │ ├── domain/
|
|
42
|
+
│ │ ├── application/
|
|
43
|
+
│ │ └── adapters/
|
|
44
|
+
│ │ ├── in/http/
|
|
45
|
+
│ │ └── out/persistence/
|
|
46
|
+
│ ├── shared/
|
|
47
|
+
│ ├── infrastructure/
|
|
48
|
+
│ │ └── prisma.ts
|
|
49
|
+
│ ├── app.ts
|
|
50
|
+
│ └── server.ts
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Persistence
|
|
54
|
+
|
|
55
|
+
Idéntico al perfil `express-prisma`: repositorios Prisma con `PrismaClient` singleton, transacciones con `$transaction()`.
|
|
56
|
+
|
|
57
|
+
## Routes & Controllers
|
|
58
|
+
|
|
59
|
+
Usar el patrón Fastify plugin como en `fastify-postgres`. Cada feature expone un plugin que recibe el controlador por parámetro.
|
|
60
|
+
|
|
61
|
+
## Testing Strategy
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import Fastify from "fastify";
|
|
65
|
+
|
|
66
|
+
const app = Fastify();
|
|
67
|
+
const mockRepo = { create: vi.fn() };
|
|
68
|
+
const useCase = new AddCredit(mockRepo);
|
|
69
|
+
|
|
70
|
+
app.register(creditRoutes(new CreditController(useCase)));
|
|
71
|
+
|
|
72
|
+
it("creates a credit", async () => {
|
|
73
|
+
mockRepo.create.mockResolvedValue({ id: "1" });
|
|
74
|
+
const res = await app.inject({ method: "POST", url: "/credits", payload: { amount: 100 } });
|
|
75
|
+
expect(res.statusCode).toBe(201);
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Naming Conventions
|
|
80
|
+
|
|
81
|
+
Mismas que `fastify-postgres`. Rutas en camelCase, servicios en PascalCase.
|