@ronaldjdevfs/forge 1.3.5 → 1.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  <img src="favicon.svg" alt="Forge Logo" width="100" height="100">
2
2
 
3
- > **v1.3.5** — R13 Platform Domain Guard & Legacy Cleanup
3
+ > **v1.4.0** — Forge Init, Auto-Fix Iterativo & Inspect Full Default
4
4
 
5
5
  ## Forge — Backend Architecture Operating System
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ronaldjdevfs/forge",
3
- "version": "1.3.5",
3
+ "version": "1.4.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": {
@@ -75,7 +75,7 @@ En esencia:
75
75
  ANTES de cualquier acción, Forge DEBE ejecutar `forge-boot.mjs` con la profundidad adecuada al comando:
76
76
 
77
77
  ```bash
78
- boot=$(node .opencode/skills/forge/scripts/forge-boot.mjs --depth <depth> --json 2>/dev/null)
78
+ boot=$(node {{AGENT_PATH}}/scripts/forge-boot.mjs --depth <depth> --json 2>/dev/null)
79
79
  ```
80
80
 
81
81
  La profundidad (`--depth`) depende del comando (ver Execution Flow):
@@ -193,7 +193,7 @@ El agente DEBE leer este archivo al inicio de cada interacción y actualizarlo a
193
193
  Forge incluye tests unitarios con `node:test` (sin dependencias externas).
194
194
 
195
195
  ```bash
196
- node --test .opencode/skills/forge/tests/core.test.mjs
196
+ node --test {{AGENT_PATH}}/tests/core.test.mjs
197
197
  ```
198
198
 
199
199
  | Módulo | Tests | Descripción |
@@ -217,6 +217,7 @@ node --test .opencode/skills/forge/tests/core.test.mjs
217
217
  | Flag | Comando | Descripción |
218
218
  |------|---------|-------------|
219
219
  | `--fix` | `quench` | Auto-corrige violaciones WARNING/INFO (missing @injectable(), tsconfig, naming, container.resolve) |
220
+ | `--auto` | `quench` | Itera fix → re-detect → fix hasta estabilizar violaciones auto-corregibles |
220
221
  | `--show-ignores` | `quench` | Muestra los inline ignores encontrados en el código |
221
222
  | `--persona=<id>` | `assay` | Filtra ensayo por una persona (bezos, fowler, hacker, pm, senior) |
222
223
  | `--save` | `assay` | Persiste ensayo en `.forge/assay/` |
@@ -9,11 +9,11 @@ Si el subcomando NO tiene flags en $ARGUMENTS y tiene flags disponibles (ver tab
9
9
 
10
10
  | Comando | Flags disponibles |
11
11
  |---------|------------------|
12
- | `forge` | Sin flags |
12
+ | `forge` | `--force` |
13
13
  | `cast` | Sin flags (pide nombre del feature interactivamente) |
14
14
  | `inspect` | `--json`, `--diff`, `--full`, `--summary`, `--severity=<nivel>`, `--force` |
15
15
  | `assay` | `--persona=<id>`, `--json`, `--save`, `history` |
16
- | `quench` | `--fix`, `--show-ignores`, `--severity=<nivel>`, `--json` |
16
+ | `quench` | `--fix`, `--auto`, `--show-ignores`, `--severity=<nivel>`, `--json` |
17
17
  | `chain` | `--json` |
18
18
  | `graph` | `--json` |
19
19
  | `armorer` | Sin flags |
@@ -27,17 +27,19 @@ Si el subcomando NO tiene flags en $ARGUMENTS y tiene flags disponibles (ver tab
27
27
 
28
28
  ### forge
29
29
 
30
- Inicializa el proyecto arquitectónicamente. Ejecuta context + bootstrap + profile + armorer + graph + chain + inscribe.
30
+ Inicializa el proyecto arquitectónicamente con configuración persistente.
31
31
 
32
- ```
33
- node .opencode/skills/forge/scripts/context.mjs
34
- node .opencode/skills/forge/scripts/bootstrap.mjs
35
- node .opencode/skills/forge/scripts/profile.mjs
36
- node .opencode/skills/forge/scripts/armorer.mjs
37
- node .opencode/skills/forge/scripts/graph.mjs
38
- node .opencode/skills/forge/scripts/chain.mjs
39
- node .opencode/skills/forge/scripts/architecture.mjs
40
- ```
32
+ 1. `node {{AGENT_PATH}}/scripts/context.mjs` — detectar stack actual
33
+ 2. `node {{AGENT_PATH}}/scripts/bootstrap.mjs` — crear platform/, shared/, infra/
34
+ 3. Crear `src/features/` — si no existe
35
+ 4. `node {{AGENT_PATH}}/scripts/forge-config.mjs --init` — crear `.forge/config.json` + `.forge/state.json`
36
+ 5. `node {{AGENT_PATH}}/scripts/forge-config.mjs --update` — detectar y persistir perfil
37
+ 6. Verificar `tsconfig.json` — agregar `experimentalDecorators` y `emitDecoratorMetadata` si falta
38
+ 7. `node {{AGENT_PATH}}/scripts/armorer.mjs` — ownership
39
+ 8. `node {{AGENT_PATH}}/scripts/graph.mjs` — grafo arquitectónico
40
+ 9. `node {{AGENT_PATH}}/scripts/chain.mjs` — dependencias multi-capa
41
+ 10. `node {{AGENT_PATH}}/scripts/detect.mjs --summary` — auditoría base
42
+ 11. `node {{AGENT_PATH}}/scripts/architecture.mjs` — generar `ARCHITECTURE.md`
41
43
 
42
44
  ### cast
43
45
 
@@ -52,7 +54,7 @@ Migra un feature existente. Puede targetizar platform/, shared/, infra/ o featur
52
54
  Genera ARCHITECTURE.md con grafo arquitectónico, ownership y platform.
53
55
 
54
56
  ```
55
- node .opencode/skills/forge/scripts/architecture.mjs
57
+ node {{AGENT_PATH}}/scripts/architecture.mjs
56
58
  ```
57
59
 
58
60
  ### graph
@@ -60,13 +62,13 @@ node .opencode/skills/forge/scripts/architecture.mjs
60
62
  Construye el grafo arquitectónico del proyecto (4 capas: platform, feature, shared, infra) con reglas R1-R9.
61
63
 
62
64
  ```
63
- node .opencode/skills/forge/scripts/graph.mjs
65
+ node {{AGENT_PATH}}/scripts/graph.mjs
64
66
  ```
65
67
 
66
68
  Para salida JSON:
67
69
 
68
70
  ```
69
- node .opencode/skills/forge/scripts/graph.mjs --json
71
+ node {{AGENT_PATH}}/scripts/graph.mjs --json
70
72
  ```
71
73
 
72
74
  ### smelt
@@ -78,7 +80,7 @@ Extrae código reutilizable a shared/ (solo código puro, sin dependencias infra
78
80
  Inicializa platform, shared e infra layers (uso interno, se ejecuta automáticamente).
79
81
 
80
82
  ```
81
- node .opencode/skills/forge/scripts/bootstrap.mjs
83
+ node {{AGENT_PATH}}/scripts/bootstrap.mjs
82
84
  ```
83
85
 
84
86
  ## Evaluate
@@ -88,13 +90,13 @@ node .opencode/skills/forge/scripts/bootstrap.mjs
88
90
  Audita la conformidad arquitectónica completa. 6 categorías: structure(20), layers(20), ownership(20), platform(15), dependencies(15), graph(20).
89
91
 
90
92
  ```
91
- node .opencode/skills/forge/scripts/inspect.mjs
93
+ node {{AGENT_PATH}}/scripts/inspect.mjs
92
94
  ```
93
95
 
94
96
  Para salida JSON:
95
97
 
96
98
  ```
97
- node .opencode/skills/forge/scripts/inspect.mjs --json
99
+ node {{AGENT_PATH}}/scripts/inspect.mjs --json
98
100
  ```
99
101
 
100
102
  ### assay
@@ -102,16 +104,16 @@ node .opencode/skills/forge/scripts/inspect.mjs --json
102
104
  Ensayo arquitectónico multi-persona. Interpretación cualitativa del audit desde 5 perspectivas (Bezos, Fowler, Hacker, PM, Arquitecta Senior).
103
105
 
104
106
  ```
105
- node .opencode/skills/forge/scripts/assay.mjs
107
+ node {{AGENT_PATH}}/scripts/assay.mjs
106
108
  ```
107
109
 
108
110
  Filtros:
109
111
 
110
112
  ```
111
- node .opencode/skills/forge/scripts/assay.mjs --persona=bezos
112
- node .opencode/skills/forge/scripts/assay.mjs --json
113
- node .opencode/skills/forge/scripts/assay.mjs --save
114
- node .opencode/skills/forge/scripts/assay.mjs history
113
+ node {{AGENT_PATH}}/scripts/assay.mjs --persona=bezos
114
+ node {{AGENT_PATH}}/scripts/assay.mjs --json
115
+ node {{AGENT_PATH}}/scripts/assay.mjs --save
116
+ node {{AGENT_PATH}}/scripts/assay.mjs history
115
117
  ```
116
118
 
117
119
  ### quench
@@ -119,7 +121,7 @@ node .opencode/skills/forge/scripts/assay.mjs history
119
121
  Valida reglas arquitectónicas R1-R9.
120
122
 
121
123
  ```
122
- node .opencode/skills/forge/scripts/detect.mjs
124
+ node {{AGENT_PATH}}/scripts/detect.mjs
123
125
  ```
124
126
 
125
127
  ### chain
@@ -127,13 +129,13 @@ node .opencode/skills/forge/scripts/detect.mjs
127
129
  Orden topológico de dependencias multi-capa (platform, features, shared, infra).
128
130
 
129
131
  ```
130
- node .opencode/skills/forge/scripts/chain.mjs
132
+ node {{AGENT_PATH}}/scripts/chain.mjs
131
133
  ```
132
134
 
133
135
  Para salida JSON:
134
136
 
135
137
  ```
136
- node .opencode/skills/forge/scripts/chain.mjs --json
138
+ node {{AGENT_PATH}}/scripts/chain.mjs --json
137
139
  ```
138
140
 
139
141
  ### armorer
@@ -141,7 +143,7 @@ node .opencode/skills/forge/scripts/chain.mjs --json
141
143
  Reporte de ownership: huérfanos, duplicados, componentes mal ubicados.
142
144
 
143
145
  ```
144
- node .opencode/skills/forge/scripts/armorer.mjs
146
+ node {{AGENT_PATH}}/scripts/armorer.mjs
145
147
  ```
146
148
 
147
149
  ## Refine
@@ -23,26 +23,26 @@ Genera un **ensayo arquitectónico multi-persona** que evalúa el estado actual
23
23
 
24
24
  ```bash
25
25
  # Ensayo completo con todas las personas
26
- node .opencode/skills/forge/scripts/assay.mjs
26
+ node {{AGENT_PATH}}/scripts/assay.mjs
27
27
 
28
28
  # Solo la opinión de una persona
29
- node .opencode/skills/forge/scripts/assay.mjs --persona=bezos
30
- node .opencode/skills/forge/scripts/assay.mjs --persona=fowler
31
- node .opencode/skills/forge/scripts/assay.mjs --persona=hacker
32
- node .opencode/skills/forge/scripts/assay.mjs --persona=pm
33
- node .opencode/skills/forge/scripts/assay.mjs --persona=senior
29
+ node {{AGENT_PATH}}/scripts/assay.mjs --persona=bezos
30
+ node {{AGENT_PATH}}/scripts/assay.mjs --persona=fowler
31
+ node {{AGENT_PATH}}/scripts/assay.mjs --persona=hacker
32
+ node {{AGENT_PATH}}/scripts/assay.mjs --persona=pm
33
+ node {{AGENT_PATH}}/scripts/assay.mjs --persona=senior
34
34
 
35
35
  # Salida JSON (para consumo por herramientas)
36
- node .opencode/skills/forge/scripts/assay.mjs --json
36
+ node {{AGENT_PATH}}/scripts/assay.mjs --json
37
37
 
38
38
  # Persistir ensayo en .forge/assay/
39
- node .opencode/skills/forge/scripts/assay.mjs --save
39
+ node {{AGENT_PATH}}/scripts/assay.mjs --save
40
40
 
41
41
  # Ver historial de ensayos
42
- node .opencode/skills/forge/scripts/assay.mjs history
42
+ node {{AGENT_PATH}}/scripts/assay.mjs history
43
43
 
44
44
  # Leer un ensayo previo
45
- node .opencode/skills/forge/scripts/assay.mjs read assay-2026-06-25T00-00-00.md
45
+ node {{AGENT_PATH}}/scripts/assay.mjs read assay-2026-06-25T00-00-00.md
46
46
  ```
47
47
 
48
48
  ## Formato de salida
@@ -54,13 +54,13 @@ container.registerSingleton<ICreditRepository>(
54
54
 
55
55
  ```bash
56
56
  # Grafo de dependencias entre features (API legacy)
57
- node .opencode/skills/forge/scripts/chain.mjs
57
+ node {{AGENT_PATH}}/scripts/chain.mjs
58
58
 
59
59
  # Grafo arquitectónico completo (nodos, edges, violaciones)
60
- node .opencode/skills/forge/scripts/graph.mjs
60
+ node {{AGENT_PATH}}/scripts/graph.mjs
61
61
 
62
62
  # ARCHITECTURE.md con grafo incluido
63
- node .opencode/skills/forge/scripts/architecture.mjs
63
+ node {{AGENT_PATH}}/scripts/architecture.mjs
64
64
  ```
65
65
 
66
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).
@@ -85,10 +85,10 @@ jobs:
85
85
  runs-on: ubuntu-latest
86
86
  steps:
87
87
  - uses: actions/checkout@v4
88
- - run: node .opencode/skills/forge/scripts/detect.mjs
88
+ - run: node {{AGENT_PATH}}/scripts/detect.mjs
89
89
  env:
90
90
  FORGE_STRICT: "true"
91
- - run: node .opencode/skills/forge/scripts/chain.mjs --json
91
+ - run: node {{AGENT_PATH}}/scripts/chain.mjs --json
92
92
  ```
93
93
 
94
94
  ---
@@ -11,20 +11,28 @@ Inicializa un proyecto para trabajar con Forge como Backend Architecture Operati
11
11
 
12
12
  ## Flujo
13
13
 
14
- 1. Ejecutar `scripts/context.mjs` para detectar stack actual (incluye platform, features, shared, infra)
15
- 2. Ejecutar `scripts/bootstrap.mjs` para crear layers faltantes (platform, shared, infra)
16
- 3. Ejecutar `scripts/profile.mjs` para determinar perfil tecnológico
17
- 4. Ejecutar `scripts/armorer.mjs` para detectar ownership y huérfanos
18
- 5. Ejecutar `scripts/graph.mjs` para construir grafo arquitectónico global
19
- 6. Ejecutar `scripts/chain.mjs` para analizar dependencias multi-capa
20
- 7. Si `ARCHITECTURE.md` no existe, crearlo con `forge inscribe`
21
- 8. Si faltan dependencias clave (según perfil), listarlas
22
- 9. Si el proyecto tiene código legacy, sugerir `forge relocate`
23
- 10. Si el proyecto está listo, sugerir `forge cast`
14
+ 1. Ejecutar `scripts/context.mjs` detectar stack actual (incluye platform, features, shared, infra)
15
+ 2. Ejecutar `scripts/bootstrap.mjs` crear layers faltantes (platform, shared, infra)
16
+ 3. Crear `src/features/` directorio de features si no existe
17
+ 4. Ejecutar `forge-config.mjs --init` crear `.forge/config.json` + `.forge/state.json`
18
+ 5. Ejecutar `forge-config.mjs --update` detectar y persistir perfil tecnológico
19
+ 6. Verificar `tsconfig.json` agregar `experimentalDecorators` y `emitDecoratorMetadata` si falta
20
+ 7. Ejecutar `scripts/armorer.mjs` detectar ownership y huérfanos
21
+ 8. Ejecutar `scripts/graph.mjs` construir grafo arquitectónico global
22
+ 9. Ejecutar `scripts/chain.mjs` analizar dependencias multi-capa
23
+ 10. Ejecutar `detect.mjs --summary` auditoría base
24
+ 11. Ejecutar `architecture.mjs` — generar `ARCHITECTURE.md`
25
+ 12. Si faltan dependencias clave (según perfil), listarlas
26
+ 13. Si el proyecto tiene código legacy, sugerir `forge relocate`
27
+ 14. Si el proyecto está listo, sugerir `forge cast`
24
28
 
25
29
  ## Output esperado
26
30
 
27
31
  - `ARCHITECTURE.md` creado en la raíz del proyecto
32
+ - `.forge/config.json` con perfil tecnológico persistido
33
+ - `.forge/state.json` con estado inicial
34
+ - `src/features/` creado si no existía
35
+ - `tsconfig.json` con `experimentalDecorators` y `emitDecoratorMetadata` habilitados
28
36
  - Layers Platform, Shared e Infrastructure creados si no existían
29
37
  - Perfil tecnológico detectado y registrado
30
38
  - Ownership analizado
@@ -52,9 +52,9 @@ Inspecciona la conformidad arquitectónica del proyecto.
52
52
  ## Ejecución
53
53
 
54
54
  ```bash
55
- node .opencode/skills/forge/scripts/inspect.mjs
56
- node .opencode/skills/forge/scripts/inspect.mjs --json
57
- node .opencode/skills/forge/scripts/detect.mjs --severity ERROR
55
+ node {{AGENT_PATH}}/scripts/inspect.mjs
56
+ node {{AGENT_PATH}}/scripts/inspect.mjs --json
57
+ node {{AGENT_PATH}}/scripts/detect.mjs --severity ERROR
58
58
 
59
59
  ## Ver también
60
60
 
@@ -64,13 +64,13 @@ El grafo se construye automáticamente con `scripts/graph.mjs` y las violaciones
64
64
 
65
65
  ```bash
66
66
  # Validación completa
67
- node .opencode/skills/forge/scripts/detect.mjs
67
+ node {{AGENT_PATH}}/scripts/detect.mjs
68
68
 
69
69
  # Solo errores y críticos
70
- node .opencode/skills/forge/scripts/detect.mjs --severity ERROR
70
+ node {{AGENT_PATH}}/scripts/detect.mjs --severity ERROR
71
71
 
72
72
  # Solo un tipo específico
73
- node .opencode/skills/forge/scripts/detect.mjs --type layers
73
+ node {{AGENT_PATH}}/scripts/detect.mjs --type layers
74
74
 
75
75
  ## Ver también
76
76
 
@@ -23,10 +23,10 @@ Renombra un único archivo según las convenciones de `reference/patterns.md` y
23
23
  ### Flujo
24
24
 
25
25
  1. Identificar la ruta del archivo (relativa a la raíz del proyecto)
26
- 2. Ejecutar `node .opencode/skills/forge/scripts/rename.mjs --detect --file <path>` para detectar violación
26
+ 2. Ejecutar `node {{AGENT_PATH}}/scripts/rename.mjs --detect --file <path>` para detectar violación
27
27
  3. Si el archivo ya cumple naming, informar y terminar
28
28
  4. Mostrar preview del cambio al usuario
29
- 5. Ejecutar `node .opencode/skills/forge/scripts/rename.mjs --file <path>` que:
29
+ 5. Ejecutar `node {{AGENT_PATH}}/scripts/rename.mjs --file <path>` que:
30
30
  - Renombra físicamente el archivo
31
31
  - Escanea todos los `.ts`/`.js` del proyecto
32
32
  - Actualiza imports que referencian el path antiguo (relativos y absolutos)
@@ -57,9 +57,9 @@ $ forge reforge src/features/users/domain/userEntity.ts
57
57
  1. Ejecutar `forge inspect` para obtener estado actual
58
58
  2. Identificar violaciones en el grafo arquitectónico
59
59
  3. Identificar ownership problemático (huérfanos, duplicados, mal ubicados)
60
- 4. **Detectar naming violations**: Ejecutar `node .opencode/skills/forge/scripts/rename.mjs --detect --json`
60
+ 4. **Detectar naming violations**: Ejecutar `node {{AGENT_PATH}}/scripts/rename.mjs --detect --json`
61
61
  5. Si hay naming violations, preguntar al usuario: "¿Corregir naming conventions automáticamente?"
62
- - Si acepta, ejecutar `node .opencode/skills/forge/scripts/rename.mjs --all`
62
+ - Si acepta, ejecutar `node {{AGENT_PATH}}/scripts/rename.mjs --all`
63
63
  6. Decidir las acciones correctivas en orden:
64
64
  - Violaciones CRITICAL primero (R1, R2, R5, R6)
65
65
  - Violaciones ERROR después (R3, R4, R8, R9)
@@ -1308,6 +1308,45 @@ export function applyFixes(checks, projectRoot = ROOT) {
1308
1308
  return { fixed, skipped, details };
1309
1309
  }
1310
1310
 
1311
+ /**
1312
+ * autoFixLoop — Iterative auto-fix: fix → re-detect → fix → ... until stable.
1313
+ * Returns { totalFixed, iterations, remaining } where remaining is the filtered
1314
+ * violations list from the last iteration.
1315
+ */
1316
+ async function autoFixLoop(initialFiltered, maxIterations = 10) {
1317
+ let totalFixed = 0;
1318
+ let filtered = initialFiltered;
1319
+ let iteration = 0;
1320
+
1321
+ for (; iteration < maxIterations; iteration++) {
1322
+ const result = applyFixes(filtered);
1323
+ if (result.fixed === 0) break;
1324
+
1325
+ totalFixed += result.fixed;
1326
+
1327
+ // Re-detect after fixes
1328
+ const { buildContext } = await import("./context.mjs");
1329
+ const ctx = await buildContext();
1330
+ const features = detectFeaturesOnSrc();
1331
+ const graph = ctx.graph || getGraph();
1332
+ const results = allChecks(features, graph, ctx);
1333
+
1334
+ let all = [];
1335
+ for (const [, cat] of Object.entries(results)) {
1336
+ all = all.concat(cat.checks);
1337
+ }
1338
+
1339
+ const allIgnores = loadAllInlineIgnores(join(ROOT, "src"));
1340
+ filtered = all.filter(c => {
1341
+ if (c.pass) return true;
1342
+ if (isIgnored(c, allIgnores)) return false;
1343
+ return true;
1344
+ });
1345
+ }
1346
+
1347
+ return { totalFixed, iterations: iteration, remaining: filtered };
1348
+ }
1349
+
1311
1350
  /* ── CLI ── */
1312
1351
  async function main() {
1313
1352
  const args = process.argv.slice(2);
@@ -1315,6 +1354,7 @@ async function main() {
1315
1354
  const filterSeverity = args.includes("--severity") ? args[args.indexOf("--severity") + 1] : null;
1316
1355
  const format = args.includes("--json") ? "json" : "text";
1317
1356
  const doFix = args.includes("--fix");
1357
+ const doAuto = args.includes("--auto");
1318
1358
  const showIgnores = args.includes("--show-ignores");
1319
1359
 
1320
1360
  const { buildContext } = await import("./context.mjs");
@@ -1356,6 +1396,29 @@ async function main() {
1356
1396
  return;
1357
1397
  }
1358
1398
 
1399
+ if (doAuto) {
1400
+ const result = await autoFixLoop(filtered);
1401
+ console.log(`\n${BOLD}Auto-Fix Iterativo${RESET}`);
1402
+ console.log(` ${GREEN}✔${RESET} ${result.totalFixed} violación(es) corregidas en ${result.iterations} iteración(es)`);
1403
+ console.log();
1404
+
1405
+ const remaining = result.remaining.filter(c => !c.pass);
1406
+ if (remaining.length === 0) {
1407
+ console.log(` ${GREEN}✔${RESET} No quedan violaciones.`);
1408
+ return;
1409
+ }
1410
+
1411
+ const critical = remaining.filter(c => c.severity === "CRITICAL").length;
1412
+ const errors = remaining.filter(c => c.severity === "ERROR").length;
1413
+ const warnings = remaining.filter(c => c.severity === "WARNING").length;
1414
+ console.log(` ${YELLOW}⚠${RESET} Quedan ${remaining.length} violación(es) sin corregir (${critical} CRITICAL, ${errors} ERROR, ${warnings} WARNING):\n`);
1415
+ for (const c of remaining) {
1416
+ console.log(formatCheck(c));
1417
+ }
1418
+ console.log();
1419
+ return;
1420
+ }
1421
+
1359
1422
  // Show inline ignores summary
1360
1423
  let totalIgnores = 0;
1361
1424
  for (const [, fileIgnores] of allIgnores) {
@@ -105,4 +105,6 @@ async function main() {
105
105
  console.log(`Boot [${depth}]: ${result.profile.profile} | features: ${ctx.features.total} | graph: ${result.graph.stats.totalNodes}n/${result.graph.stats.totalEdges}e | violations: ${result.inspect.totalChecks} [CRIT:${result.inspect.critical} ERR:${result.inspect.errors} WARN:${result.inspect.warnings}]`);
106
106
  }
107
107
 
108
- main().catch(console.error);
108
+ if (process.argv[1] && (process.argv[1].endsWith("forge-boot.mjs") || process.argv[1].endsWith("forge-boot.js"))) {
109
+ main().catch(console.error);
110
+ }
@@ -10,7 +10,7 @@ import { evaluateRules } from "./registry/rules.mjs";
10
10
  const ROOT = process.cwd();
11
11
 
12
12
  export async function runSentinelCheck(files, opts = {}) {
13
- const { strict = false, reminder = false } = opts;
13
+ const { reminder = false } = opts;
14
14
 
15
15
  if (!files || files.length === 0) {
16
16
  return { violations: [], graphViolations: [], total: 0, hasCritical: false, hasErrors: false, filesChecked: 0, summary: "Sin archivos fuente modificados" };
@@ -5,7 +5,7 @@ import { execFileSync } from "child_process";
5
5
  import { buildContext } from "./context.mjs";
6
6
  import { detectProfile, detectProfileExtended } from "./profile.mjs";
7
7
  import { buildDependencyGraph } from "./chain.mjs";
8
- import { allChecks, checkStructure, checkLayers, checkDecorators } from "./detect.mjs";
8
+ import { allChecks, checkStructure, checkLayers, checkDecorators, detectFeaturesOnSrc } from "./detect.mjs";
9
9
  import { saveHistory, updateStateFromAudit } from "./forge-config.mjs";
10
10
  import { buildPipeline, printPipeline } from "./recommendation-engine.mjs";
11
11
 
@@ -217,7 +217,7 @@ function getChangedFeatures(changedFiles, allFeatures) {
217
217
  async function main() {
218
218
  const args = process.argv.slice(2);
219
219
  const isJson = args.includes("--json");
220
- const isDiff = args.includes("--diff") || !args.includes("--full");
220
+ const isDiff = args.includes("--diff");
221
221
  const isSummary = args.includes("--summary");
222
222
  const force = args.includes("--force");
223
223
  const filterSeverity = args.includes("--severity") ? args[args.indexOf("--severity") + 1] : null;
@@ -227,7 +227,7 @@ async function main() {
227
227
  const profile = profileExtended.profile;
228
228
  const archGraph = ctx.graph;
229
229
  const chainGraph = buildDependencyGraph(process.cwd(), archGraph);
230
- const features = ctx.features.migrated;
230
+ const features = detectFeaturesOnSrc();
231
231
 
232
232
  if (isSummary) {
233
233
  const result = allChecks(features, archGraph, ctx);
@@ -267,7 +267,8 @@ async function main() {
267
267
  if (changedFeatures.length === 0) {
268
268
  if (!isJson) {
269
269
  console.log(`\n${YELLOW}⚠ No hay features afectados por los cambios.${RESET}`);
270
- console.log(`${DIM}Los cambios están fuera de src/features/ o no hay features migrados.${RESET}\n`);
270
+ console.log(`${DIM}Los cambios están fuera de src/features/ o no hay features migrados.${RESET}`);
271
+ console.log(`${DIM}Usá 'inspect --full' para un análisis completo de todo el proyecto.${RESET}\n`);
271
272
  } else {
272
273
  console.log(JSON.stringify({ diff: { changedFiles: changedFiles.length, changedFeatures: [], affectedFeatures: false } }));
273
274
  }
@@ -11,13 +11,32 @@
11
11
  */
12
12
 
13
13
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
14
- import { join } from "path";
14
+ import { join, dirname } from "path";
15
+ import { fileURLToPath } from "url";
15
16
 
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
18
  const ROOT = process.cwd();
17
19
  const CACHE_PATH = join(ROOT, ".forge", "version-cache.json");
18
20
  const CACHE_TTL = 86400000; // 24h
19
21
  const VERSION_URL = "https://forge.dev/latest";
20
- const CURRENT_VERSION = "1.3.5";
22
+
23
+ function readVersion() {
24
+ const candidates = [
25
+ join(__dirname, "..", "package.json"),
26
+ join(__dirname, "..", "..", "package.json"),
27
+ join(__dirname, "..", "..", "..", "package.json"),
28
+ join(__dirname, "..", "..", "..", "..", "package.json"),
29
+ join(__dirname, "..", "..", "..", "..", "..", "package.json"),
30
+ ];
31
+ for (const p of candidates) {
32
+ try {
33
+ const pkg = JSON.parse(readFileSync(p, "utf-8"));
34
+ if (pkg.version && pkg.name === "@ronaldjdevfs/forge") return pkg.version;
35
+ } catch {}
36
+ }
37
+ return "0.0.0";
38
+ }
39
+ const CURRENT_VERSION = readVersion();
21
40
 
22
41
  function readJson(path) {
23
42
  try {
package/src/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { copyFileSync, mkdirSync, existsSync, writeFileSync, readFileSync, cpSync, readdirSync } from "fs";
3
+ import { copyFileSync, mkdirSync, existsSync, writeFileSync, readFileSync, cpSync, readdirSync, statSync } from "fs";
4
4
  import { join, dirname, relative } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { execSync } from "child_process";
@@ -43,6 +43,20 @@ function copyRecursive(src, dest) {
43
43
  cpSync(src, dest, { recursive: true, force: true });
44
44
  }
45
45
 
46
+ function renderSkillPaths(skillDest, skillPath) {
47
+ for (const entry of readdirSync(skillDest)) {
48
+ const fullPath = join(skillDest, entry);
49
+ if (statSync(fullPath).isDirectory()) {
50
+ renderSkillPaths(fullPath, skillPath);
51
+ } else if (entry.endsWith(".md")) {
52
+ const content = readFileSync(fullPath, "utf-8");
53
+ if (content.includes("{{AGENT_PATH}}")) {
54
+ writeFileSync(fullPath, content.replace(/\{\{AGENT_PATH\}\}/g, skillPath), "utf-8");
55
+ }
56
+ }
57
+ }
58
+ }
59
+
46
60
  function detectPM(configDir) {
47
61
  if (existsSync(join(configDir, "pnpm-lock.yaml")) || existsSync(join(process.cwd(), "pnpm-lock.yaml"))) return "pnpm";
48
62
  if (existsSync(join(configDir, "bun.lock")) || existsSync(join(configDir, "bun.lockb"))) return "bun";
@@ -143,8 +157,11 @@ async function installOpenCode(isGlobal = false) {
143
157
 
144
158
  const s = spinner();
145
159
 
160
+ const skillPath = isGlobal ? join("~/.config/opencode", "skills", "forge") : join(".opencode", "skills", "forge");
161
+
146
162
  s.start("Copiando skill a " + rel);
147
163
  copyRecursive(SKILL_SRC, target);
164
+ renderSkillPaths(target, skillPath);
148
165
  s.stop("Skill copiada a " + rel);
149
166
 
150
167
  s.start("Generando comandos /forge-*");
@@ -192,6 +209,9 @@ async function installAgentTemplates(agentDir, agentName) {
192
209
  writeFileSync(join(skillDest, "SKILL.md"), rendered, "utf-8");
193
210
  }
194
211
 
212
+ // Render {{AGENT_PATH}} in remaining .md files (command/forge.md, reference/*.md)
213
+ renderSkillPaths(skillDest, config.skillPath);
214
+
195
215
  s.stop(`${agentName} configurado en ${agentDir}/`);
196
216
  log.success(`${agentName} listo (skill + ${config.postInstall || "templates"})`);
197
217
  }