@ronaldjdevfs/forge 1.0.1

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.
Files changed (56) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +9 -0
  3. package/README.md +338 -0
  4. package/logo.png +0 -0
  5. package/package.json +47 -0
  6. package/skills/forge/SKILL.md +207 -0
  7. package/skills/forge/command/forge.md +110 -0
  8. package/skills/forge/profiles/express-mongodb.md +289 -0
  9. package/skills/forge/profiles/express-postgres.md +164 -0
  10. package/skills/forge/profiles/express-prisma.md +116 -0
  11. package/skills/forge/profiles/fastify-postgres.md +110 -0
  12. package/skills/forge/profiles/nestjs-prisma.md +160 -0
  13. package/skills/forge/reference/cast.md +77 -0
  14. package/skills/forge/reference/chain.md +73 -0
  15. package/skills/forge/reference/forge.md +44 -0
  16. package/skills/forge/reference/inscribe.md +129 -0
  17. package/skills/forge/reference/inspect.md +58 -0
  18. package/skills/forge/reference/patterns.md +108 -0
  19. package/skills/forge/reference/principles.md +29 -0
  20. package/skills/forge/reference/quench.md +74 -0
  21. package/skills/forge/reference/reforge.md +40 -0
  22. package/skills/forge/reference/relocate.md +38 -0
  23. package/skills/forge/reference/smelt.md +31 -0
  24. package/skills/forge/reference/temper.md +49 -0
  25. package/skills/forge/scripts/architecture.mjs +176 -0
  26. package/skills/forge/scripts/armorer.mjs +421 -0
  27. package/skills/forge/scripts/bootstrap.mjs +187 -0
  28. package/skills/forge/scripts/chain.mjs +258 -0
  29. package/skills/forge/scripts/context.mjs +237 -0
  30. package/skills/forge/scripts/detect.mjs +843 -0
  31. package/skills/forge/scripts/graph.mjs +594 -0
  32. package/skills/forge/scripts/inspect.mjs +193 -0
  33. package/skills/forge/scripts/profile.mjs +92 -0
  34. package/skills/forge/templates/feature/controller.ts.md +66 -0
  35. package/skills/forge/templates/feature/entity.ts.md +11 -0
  36. package/skills/forge/templates/feature/mapper.ts.md +18 -0
  37. package/skills/forge/templates/feature/repository-impl.ts.md +59 -0
  38. package/skills/forge/templates/feature/repository-interface.ts.md +12 -0
  39. package/skills/forge/templates/feature/routes.ts.md +17 -0
  40. package/skills/forge/templates/feature/schema.ts.md +16 -0
  41. package/skills/forge/templates/feature/use-case.ts.md +19 -0
  42. package/skills/forge/templates/infra/mail.ts.md +31 -0
  43. package/skills/forge/templates/infra/mongodb.ts.md +12 -0
  44. package/skills/forge/templates/infra/prisma.ts.md +21 -0
  45. package/skills/forge/templates/infra/redis.ts.md +30 -0
  46. package/skills/forge/templates/platform/config.ts.md +37 -0
  47. package/skills/forge/templates/platform/database.ts.md +40 -0
  48. package/skills/forge/templates/platform/di.ts.md +18 -0
  49. package/skills/forge/templates/platform/http.ts.md +29 -0
  50. package/skills/forge/templates/platform/logger.ts.md +38 -0
  51. package/skills/forge/templates/platform/server.ts.md +25 -0
  52. package/skills/forge/templates/shared/contract.ts.md +20 -0
  53. package/skills/forge/templates/shared/error.ts.md +27 -0
  54. package/skills/forge/templates/shared/type.ts.md +18 -0
  55. package/skills/forge/templates/shared/util.ts.md +23 -0
  56. package/src/cli.js +179 -0
@@ -0,0 +1,164 @@
1
+ # Profile: Express + PostgreSQL (raw)
2
+
3
+ ## Metadata
4
+
5
+ ```yaml
6
+ framework: Express
7
+ runtime: Node 20+
8
+ database: PostgreSQL
9
+ orm: node-postgres (pg)
10
+ di_strategy: manual
11
+ architecture: hexagonal-feature
12
+ ```
13
+
14
+ ## DI Strategy
15
+
16
+ Este perfil usa **DI manual** (sin tsyringe). Las dependencias se inyectan por constructor y se instancian manualmente en bootstrap.
17
+
18
+ ```typescript
19
+ // app.ts — wiring manual
20
+ import { CreditRepository } from "@/features/credit/adapters/out/persistence/CreditRepository.js";
21
+ import { AddCredit } from "@/features/credit/application/use-cases/Add.js";
22
+ import { CreditController } from "@/features/credit/adapters/in/http/CreditController.js";
23
+
24
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
25
+
26
+ const creditRepo = new CreditRepository(pool);
27
+ const addCredit = new AddCredit(creditRepo);
28
+ const creditController = new CreditController(addCredit);
29
+ ```
30
+
31
+ ## Project Structure
32
+
33
+ ```
34
+ ├── src/
35
+ │ ├── features/
36
+ │ │ └── <domain>/
37
+ │ │ ├── domain/
38
+ │ │ │ ├── <Domain>.entity.ts
39
+ │ │ │ └── I<Domain>Repository.ts
40
+ │ │ ├── application/
41
+ │ │ │ ├── use-cases/
42
+ │ │ │ └── dto/
43
+ │ │ └── adapters/
44
+ │ │ ├── in/http/
45
+ │ │ └── out/persistence/
46
+ │ ├── shared/
47
+ │ ├── infrastructure/
48
+ │ │ └── pool.ts # pg Pool singleton
49
+ │ └── server.ts
50
+ ```
51
+
52
+ ## Persistence (pg)
53
+
54
+ ### Pool singleton
55
+
56
+ ```typescript
57
+ // src/infrastructure/pool.ts
58
+ import { Pool } from "pg";
59
+
60
+ export const pool = new Pool({
61
+ connectionString: process.env.DATABASE_URL,
62
+ });
63
+ ```
64
+
65
+ ### Repository pattern
66
+
67
+ ```typescript
68
+ export class CreditRepository implements ICreditRepository {
69
+ constructor(private readonly db: Pool) {}
70
+
71
+ async create(data: Partial<Credit>): Promise<Credit> {
72
+ const { rows } = await this.db.query(
73
+ `INSERT INTO credits (amount, status) VALUES ($1, $2) RETURNING *`,
74
+ [data.amount, data.status]
75
+ );
76
+ return rows[0] as Credit;
77
+ }
78
+
79
+ async findById(id: string): Promise<Credit | null> {
80
+ const { rows } = await this.db.query(
81
+ `SELECT * FROM credits WHERE id = $1`,
82
+ [id]
83
+ );
84
+ return rows[0] ? (rows[0] as Credit) : null;
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### Transactions
90
+
91
+ ```typescript
92
+ export class CreateCredit {
93
+ constructor(
94
+ private readonly creditRepo: ICreditRepository,
95
+ private readonly pool: Pool
96
+ ) {}
97
+
98
+ async execute(data: CreditInput): Promise<Credit> {
99
+ const client = await this.pool.connect();
100
+ try {
101
+ await client.query("BEGIN");
102
+ const credit = await this.creditRepo.create(data, client);
103
+ await client.query("COMMIT");
104
+ return credit;
105
+ } catch (error) {
106
+ await client.query("ROLLBACK");
107
+ throw error;
108
+ } finally {
109
+ client.release();
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ## Controller pattern
116
+
117
+ ```typescript
118
+ export class CreditController {
119
+ constructor(private readonly addCredit: AddCredit) {}
120
+
121
+ add = async (req: Request, res: Response, next: NextFunction) => {
122
+ try {
123
+ const result = await this.addCredit.execute(req.body);
124
+ res.status(201).json({ data: result });
125
+ } catch (error) {
126
+ next(error);
127
+ }
128
+ };
129
+ }
130
+ ```
131
+
132
+ ## Routes pattern
133
+
134
+ ```typescript
135
+ import { Emitter } from "@adpt/adapters";
136
+
137
+ const router = Router();
138
+
139
+ export function setupCreditRoutes(controller: CreditController) {
140
+ router.post("/", controller.add);
141
+ router.get("/:id", controller.getById);
142
+ return router;
143
+ }
144
+ ```
145
+
146
+ ## Testing Strategy
147
+
148
+ ### Unit test
149
+
150
+ ```typescript
151
+ const mockRepo = { create: jest.fn() };
152
+ const useCase = new AddCredit(mockRepo);
153
+ ```
154
+
155
+ ### Integration test
156
+
157
+ ```typescript
158
+ import { Pool } from "pg";
159
+ const testPool = new Pool({ connectionString: process.env.TEST_DATABASE_URL });
160
+ ```
161
+
162
+ ## Naming Conventions
163
+
164
+ Tables in snake_case, columns in snake_case, JavaScript/TypeScript in camelCase.
@@ -0,0 +1,116 @@
1
+ # Profile: Express + Prisma
2
+
3
+ ## Metadata
4
+
5
+ ```yaml
6
+ framework: Express
7
+ runtime: Node 20+
8
+ database: Cualquier RDBMS (PostgreSQL, MySQL, SQLite)
9
+ orm: Prisma
10
+ di_strategy: tsyringe
11
+ architecture: hexagonal-feature
12
+ ```
13
+
14
+ ## Project Structure
15
+
16
+ ```
17
+ ├── prisma/
18
+ │ └── schema.prisma
19
+ ├── src/
20
+ │ ├── features/
21
+ │ │ └── <domain>/
22
+ │ │ ├── domain/
23
+ │ │ │ ├── <Domain>.entity.ts
24
+ │ │ │ └── I<Domain>Repository.ts
25
+ │ │ ├── application/
26
+ │ │ │ ├── use-cases/
27
+ │ │ │ ├── dto/
28
+ │ │ │ └── mappers/
29
+ │ │ └── adapters/
30
+ │ │ ├── in/http/
31
+ │ │ └── out/persistence/
32
+ │ ├── shared/
33
+ │ ├── infrastructure/
34
+ │ │ └── prisma.ts # PrismaClient singleton
35
+ │ └── server.ts
36
+ ```
37
+
38
+ ## DI Setup
39
+
40
+ Igual que `express-mongodb` (tsyringe). Las interfaces se registran como singletons.
41
+
42
+ ## Persistence (Prisma)
43
+
44
+ ### PrismaClient singleton
45
+
46
+ ```typescript
47
+ // src/infrastructure/prisma.ts
48
+ import { PrismaClient } from "@prisma/client";
49
+
50
+ export const prisma = new PrismaClient();
51
+ ```
52
+
53
+ ### Repository pattern
54
+
55
+ ```typescript
56
+ @injectable()
57
+ export class CreditRepository implements ICreditRepository {
58
+ async create(data: Partial<Credit>): Promise<Credit> {
59
+ const record = await prisma.credit.create({ data });
60
+ return CreditMapper.toDomain(record);
61
+ }
62
+
63
+ async findById(id: string): Promise<Credit | null> {
64
+ const record = await prisma.credit.findUnique({ where: { id } });
65
+ return record ? CreditMapper.toDomain(record) : null;
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Transactions
71
+
72
+ ```typescript
73
+ @injectable()
74
+ export class CreateCredit {
75
+ async execute(data: CreditInput): Promise<Credit> {
76
+ return prisma.$transaction(async (tx) => {
77
+ const credit = await tx.credit.create({ data });
78
+ await tx.log.create({ data: { action: "credit_created", creditId: credit.id } });
79
+ return credit;
80
+ });
81
+ }
82
+ }
83
+ ```
84
+
85
+ ## Testing Strategy
86
+
87
+ ### Unit test
88
+
89
+ Misma estrategia que express-mongodb: `container.registerInstance()` con mocks.
90
+
91
+ ### Integration test
92
+
93
+ ```typescript
94
+ import { prisma } from "@/infrastructure/prisma.js";
95
+ import { CreditRepository } from "./CreditRepository.js";
96
+
97
+ beforeAll(async () => {
98
+ // Prisma usa archivo de test o DB separada
99
+ // process.env.DATABASE_URL = "postgresql://test:test@localhost:5432/test";
100
+ });
101
+
102
+ afterAll(async () => {
103
+ await prisma.$disconnect();
104
+ });
105
+
106
+ it("persists a credit", async () => {
107
+ const repo = new CreditRepository();
108
+ const credit = await repo.create({ amount: 100 });
109
+ expect(credit.id).toBeDefined();
110
+ await prisma.credit.delete({ where: { id: credit.id } }); // cleanup
111
+ });
112
+ ```
113
+
114
+ ## Naming Conventions
115
+
116
+ Mismas que express-mongodb. Los nombres de tablas en Prisma siguen snake_case.
@@ -0,0 +1,110 @@
1
+ # Profile: Fastify + PostgreSQL + Prisma
2
+
3
+ ## Metadata
4
+
5
+ ```yaml
6
+ framework: Fastify
7
+ runtime: Node 20+
8
+ database: PostgreSQL
9
+ orm: Prisma
10
+ di_strategy: manual
11
+ architecture: hexagonal-feature
12
+ ```
13
+
14
+ ## DI Strategy
15
+
16
+ DI manual. Sin contenedor de DI externo. Las dependencias se inyectan por constructor y se registran explícitamente en bootstrap.
17
+
18
+ ```typescript
19
+ // app.ts
20
+ import Fastify from "fastify";
21
+ import { PrismaClient } from "@prisma/client";
22
+ import { creditRoutes } from "@/features/credit/adapters/in/http/credit.routes.js";
23
+
24
+ const app = Fastify({ logger: true });
25
+ const prisma = new PrismaClient();
26
+ const creditRepo = new CreditRepository(prisma);
27
+ const addCredit = new AddCredit(creditRepo);
28
+
29
+ app.register(creditRoutes(addCredit));
30
+
31
+ await app.listen({ port: 3000 });
32
+ ```
33
+
34
+ ## Project Structure
35
+
36
+ ```
37
+ ├── prisma/
38
+ │ └── schema.prisma
39
+ ├── src/
40
+ │ ├── features/
41
+ │ │ └── <domain>/
42
+ │ │ ├── domain/
43
+ │ │ ├── application/
44
+ │ │ └── adapters/
45
+ │ │ ├── in/http/
46
+ │ │ └── out/persistence/
47
+ │ ├── shared/
48
+ │ ├── infrastructure/
49
+ │ │ └── prisma.ts
50
+ │ ├── app.ts
51
+ │ └── server.ts
52
+ ```
53
+
54
+ ## Routes & Controllers
55
+
56
+ ### Fastify plugin pattern
57
+
58
+ ```typescript
59
+ // credit.routes.ts
60
+ import type { FastifyInstance } from "fastify";
61
+ import { CreditController } from "./CreditController.js";
62
+
63
+ export async function creditRoutes(app: FastifyInstance, controller: CreditController) {
64
+ app.post("/credits", controller.add);
65
+ app.get("/credits/:id", controller.getById);
66
+ app.get("/credits", controller.list);
67
+ }
68
+ ```
69
+
70
+ ### Controller
71
+
72
+ ```typescript
73
+ export class CreditController {
74
+ constructor(private readonly addCredit: AddCredit) {}
75
+
76
+ add = async (request: FastifyRequest, reply: FastifyReply) => {
77
+ const result = await this.addCredit.execute(request.body as CreditInput);
78
+ return reply.status(201).send({ data: result });
79
+ };
80
+ }
81
+ ```
82
+
83
+ ## Persistence
84
+
85
+ Prisma repository pattern, identical to `express-prisma` profile.
86
+
87
+ ## Testing Strategy
88
+
89
+ ```typescript
90
+ import Fastify from "fastify";
91
+
92
+ const app = Fastify();
93
+ const mockRepo = { create: jest.fn() };
94
+ const useCase = new AddCredit(mockRepo);
95
+ const controller = new CreditController(useCase);
96
+
97
+ app.register(creditRoutes(controller));
98
+
99
+ afterAll(() => app.close());
100
+
101
+ it("creates a credit", async () => {
102
+ mockRepo.create.mockResolvedValue({ id: "1" });
103
+ const res = await app.inject({ method: "POST", url: "/credits", payload: { amount: 100 } });
104
+ expect(res.statusCode).toBe(201);
105
+ });
106
+ ```
107
+
108
+ ## Naming Conventions
109
+
110
+ Mismas que express-profile profiles. Fastify usa camelCase para rutas.
@@ -0,0 +1,160 @@
1
+ # Profile: NestJS + Prisma
2
+
3
+ ## Metadata
4
+
5
+ ```yaml
6
+ framework: NestJS
7
+ runtime: Node 20+
8
+ database: Cualquier RDBMS
9
+ orm: Prisma
10
+ di_strategy: framework (NestJS DI)
11
+ architecture: hexagonal-feature
12
+ ```
13
+
14
+ ## DI Strategy
15
+
16
+ NestJS tiene su propio sistema de DI: `@Injectable()` decorator + módulos. No se necesita tsyringe.
17
+
18
+ ```typescript
19
+ import { Injectable } from "@nestjs/common";
20
+ import { InjectRepository } from "@nestjs/typeorm"; // o Prisma
21
+
22
+ @Injectable()
23
+ export class AddCredit {
24
+ constructor(private readonly creditRepo: ICreditRepository) {}
25
+ }
26
+ ```
27
+
28
+ ## Project Structure
29
+
30
+ ```
31
+ ├── prisma/
32
+ │ └── schema.prisma
33
+ ├── src/
34
+ │ ├── features/
35
+ │ │ └── <domain>/
36
+ │ │ ├── domain/
37
+ │ │ │ ├── <Domain>.entity.ts
38
+ │ │ │ └── I<Domain>Repository.ts
39
+ │ │ ├── application/
40
+ │ │ │ ├── use-cases/
41
+ │ │ │ └── dto/
42
+ │ │ └── adapters/
43
+ │ │ ├── in/http/
44
+ │ │ │ ├── <Domain>Controller.ts
45
+ │ │ │ └── <domain>.module.ts
46
+ │ │ └── out/persistence/
47
+ │ │ ├── <Domain>Repository.ts
48
+ │ │ └── <Domain>PrismaRepository.ts
49
+ │ └── app.module.ts
50
+ ```
51
+
52
+ ## Module setup
53
+
54
+ ### feature module
55
+
56
+ ```typescript
57
+ // credit.module.ts
58
+ import { Module } from "@nestjs/common";
59
+ import { CreditController } from "./CreditController.js";
60
+ import { AddCredit } from "../../application/use-cases/Add.js";
61
+ import { CreditRepository } from "../out/persistence/CreditPrismaRepository.js";
62
+
63
+ @Module({
64
+ controllers: [CreditController],
65
+ providers: [
66
+ AddCredit,
67
+ { provide: "ICreditRepository", useClass: CreditRepository },
68
+ ],
69
+ })
70
+ export class CreditModule {}
71
+ ```
72
+
73
+ ### Controller (NestJS style)
74
+
75
+ ```typescript
76
+ import { Controller, Post, Get, Body, Param } from "@nestjs/common";
77
+ import { AddCredit } from "../../application/use-cases/Add.js";
78
+
79
+ @Controller("credits")
80
+ export class CreditController {
81
+ constructor(private readonly addCredit: AddCredit) {}
82
+
83
+ @Post()
84
+ async create(@Body() data: CreditInput) {
85
+ return this.addCredit.execute(data);
86
+ }
87
+
88
+ @Get(":id")
89
+ async findById(@Param("id") id: string) {
90
+ return this.addCredit.execute(id);
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## Persistence (Prisma)
96
+
97
+ NestJS con Prisma: crear un `PrismaModule` global y `PrismaService` que extiende `PrismaClient`.
98
+
99
+ ```typescript
100
+ import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
101
+ import { PrismaClient } from "@prisma/client";
102
+
103
+ @Injectable()
104
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
105
+ async onModuleInit() {
106
+ await this.$connect();
107
+ }
108
+
109
+ async onModuleDestroy() {
110
+ await this.$disconnect();
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Repository
116
+
117
+ ```typescript
118
+ @Injectable()
119
+ export class CreditRepository implements ICreditRepository {
120
+ constructor(private readonly prisma: PrismaService) {}
121
+
122
+ async create(data: Partial<Credit>): Promise<Credit> {
123
+ const record = await this.prisma.credit.create({ data });
124
+ return CreditMapper.toDomain(record);
125
+ }
126
+ }
127
+ ```
128
+
129
+ ## Testing Strategy
130
+
131
+ ```typescript
132
+ import { Test, TestingModule } from "@nestjs/testing";
133
+
134
+ const mockRepo = { create: jest.fn() };
135
+
136
+ beforeEach(async () => {
137
+ const module: TestingModule = await Test.createTestingModule({
138
+ providers: [
139
+ AddCredit,
140
+ { provide: "ICreditRepository", useValue: mockRepo },
141
+ ],
142
+ }).compile();
143
+
144
+ useCase = module.get<AddCredit>(AddCredit);
145
+ });
146
+ ```
147
+
148
+ ## Naming Conventions
149
+
150
+ NestJS usa PascalCase para clases y camelCase para métodos/propiedades. Decoradores nativos `@Injectable()`, `@Controller()`, etc.
151
+
152
+ ## Cross-feature communication
153
+
154
+ ```typescript
155
+ // En NestJS, los módulos exportan providers para que otros módulos los usen
156
+ @Module({
157
+ exports: [AddCredit], // Disponible para otros módulos
158
+ })
159
+ export class CreditModule {}
160
+ ```
@@ -0,0 +1,77 @@
1
+ # Cast
2
+
3
+ Crea un nuevo feature desde cero siguiendo la arquitectura hexagonal basada en features.
4
+
5
+ ## Cuándo usarlo
6
+
7
+ - Agregar un nuevo dominio de negocio al proyecto
8
+ - El dominio no existe previamente en forma legacy
9
+
10
+ ## Pre-condiciones
11
+
12
+ Antes de crear un feature, verificar la existencia de los layers arquitectónicos:
13
+
14
+ 1. **Platform** — `src/platform/` (config, server, logger, di, etc.)
15
+ 2. **Shared** — `src/shared/` (errors, contracts, types, utils)
16
+ 3. **Infra** — `src/infra/` (prisma, redis, etc.)
17
+
18
+ Si alguno no existe, ejecutar `bootstrapPlatform()` automáticamente para crearlos.
19
+
20
+ ## Flujo
21
+
22
+ 1. Verificar que `src/platform/`, `src/shared/`, `src/infra/` existan (crearlos si no)
23
+ 2. Determinar el nombre del feature (formato: kebab-case)
24
+ 3. Crear estructura de directorios:
25
+ ```
26
+ src/features/<name>/
27
+ ├── domain/
28
+ ├── application/
29
+ │ ├── use-cases/
30
+ │ └── mappers/
31
+ └── adapters/
32
+ ├── in/http/
33
+ └── out/persistence/
34
+ ```
35
+ 4. Crear archivos del feature en este orden (ver `templates/feature/`):
36
+ - `<Name>.entity.ts` — interfaz de dominio
37
+ - `I<Name>Repository.ts` — puerto de repositorio
38
+ - `<Name>.mapper.ts` — mapper dominio ↔ persistencia
39
+ - `<Name>Schema.ts` — schema de BD (según perfil)
40
+ - `<Name>Repository.ts` — implementación del repositorio
41
+ - Use cases (`Create.ts`, `Get.ts`, `List.ts`, `Update.ts`, `Delete.ts`)
42
+ - `<Name>Controller.ts` — controlador HTTP
43
+ - `<name>.routes.ts` — rutas HTTP
44
+ 5. Registrar rutas en el enrutador principal
45
+ 6. Ejecutar `forge quench` para verificar el feature
46
+ 7. Actualizar `ARCHITECTURE.md`
47
+
48
+ ## Convenciones
49
+
50
+ Ver `reference/patterns.md` para el patrón completo.
51
+
52
+ | Elemento | Formato | Ejemplo |
53
+ |---|---|---|
54
+ | Feature directory | `kebab-case/` | `credit-card/` |
55
+ | Entity | `<Name>.entity.ts` | `CreditCard.entity.ts` |
56
+ | Repository interface | `I<Name>.repository.ts` | `ICreditCard.repository.ts` |
57
+ | Repository impl | `<Name>.repository.ts` | `CreditCard.repository.ts` |
58
+ | Use case | `<Action>.uc.ts` | `CreateCreditCard.uc.ts` |
59
+ | Mapper | `<Name>.mapper.ts` | `CreditCard.mapper.ts` |
60
+ | Controller | `<Name>.controller.ts` | `CreditCard.controller.ts` |
61
+ | Routes | `<Name>.routes.ts` | `CreditCard.routes.ts` |
62
+ | Schema | `<Name>.schema.ts` | `CreditCard.schema.ts` |
63
+
64
+ ## Con el perfil activo
65
+
66
+ Usar el perfil detectado para determinar:
67
+ - Estrategia de DI (tsyringe/manual/framework)
68
+ - Patrón de controlador (Express/Fastify/NestJS)
69
+ - Patrón de persistencia (Mongoose/Prisma/pg)
70
+ - Convenciones de imports (rutas relativas vs alias)
71
+ - Componentes de platform a usar (config, logger, http, database)
72
+
73
+ ## Post-creación
74
+
75
+ - `forge quench` — verificar que no hay violaciones
76
+ - `forge inspect` — confirmar puntuación
77
+ - `ARCHITECTURE.md` actualizado automáticamente
@@ -0,0 +1,73 @@
1
+ # Chain
2
+
3
+ Gestiona las cadenas de dependencias entre features.
4
+
5
+ ## Cuándo usarlo
6
+
7
+ - Antes de migrar un feature (para conocer sus dependencias)
8
+ - Cuando se detectan imports directos entre features
9
+ - Para determinar el orden topológico de migración
10
+ - Para diagnosticar acoplamiento excesivo
11
+
12
+ ## Reglas
13
+
14
+ - Nunca imports directos entre features
15
+ - La comunicación entre features siempre via interfaces inyectadas
16
+ - Un feature puede depender de otro, pero nunca al revés (sin ciclos)
17
+ - Las interfaces compartidas se declaran en el feature que las define
18
+ - Dependencias transitivas se inyectan, no se heredan
19
+
20
+ ## Orden topológico
21
+
22
+ Migrar features en orden de menor a mayor dependencia:
23
+
24
+ ```
25
+ 1. Features sin dependencias
26
+ 2. Features que dependen solo de shared/
27
+ 3. Features que dependen de features ya migrados
28
+ ```
29
+
30
+ ## Cómo inyectar dependencias entre features
31
+
32
+ ### Entre features del mismo proyecto
33
+
34
+ ```
35
+ Feature A (credit)
36
+ ├── domain/ICreditRepository.ts ← define la interfaz
37
+ └── adapters/out/CreditRepository.ts ← implementa
38
+
39
+ Feature B (payment) ← necesita CreditRepository
40
+ ├── application/PaymentUseCase.ts
41
+ └── @inject(ICreditRepository) creditRepo
42
+ ```
43
+
44
+ ### Registro en bootstrap
45
+
46
+ ```typescript
47
+ container.registerSingleton<ICreditRepository>(
48
+ ICreditRepository as symbol,
49
+ CreditRepository
50
+ );
51
+ ```
52
+
53
+ ## Detectar dependencias
54
+
55
+ ```bash
56
+ # Grafo de dependencias entre features (API legacy)
57
+ node .opencode/skills/forge/scripts/chain.mjs
58
+
59
+ # Grafo arquitectónico completo (nodos, edges, violaciones)
60
+ node .opencode/skills/forge/scripts/graph.mjs
61
+
62
+ # ARCHITECTURE.md con grafo incluido
63
+ node .opencode/skills/forge/scripts/architecture.mjs
64
+ ```
65
+
66
+ `chain.mjs` ahora es un wrapper sobre `graph.mjs`. Produce el mismo formato de salida para compatibilidad (nodos, edges, features, orden topológico). El nuevo `graph.mjs` amplía el análisis a todos los tipos de nodo (core, feature, domain, infra, adapter) y detecta violaciones de reglas arquitectónicas (R1-R6).
67
+
68
+ ## Buenas prácticas
69
+
70
+ - Mantener el grafo acíclico
71
+ - Si hay ciclo, extraer la interfaz común a shared/
72
+ - Documentar dependencias en ARCHITECTURE.md
73
+ - Revisar dependencias después de cada migración