@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 +1 -1
- package/package.json +1 -1
- package/skills/forge/SKILL.md +3 -2
- package/skills/forge/command/forge.md +29 -27
- package/skills/forge/reference/assay.md +10 -10
- package/skills/forge/reference/chain.md +3 -3
- package/skills/forge/reference/evolutionary-architecture.md +2 -2
- package/skills/forge/reference/forge.md +18 -10
- package/skills/forge/reference/inspect.md +3 -3
- package/skills/forge/reference/quench.md +3 -3
- package/skills/forge/reference/reforge.md +4 -4
- package/skills/forge/scripts/detect.mjs +63 -0
- package/skills/forge/scripts/forge-boot.mjs +3 -1
- package/skills/forge/scripts/forgeSentinel-lib.mjs +1 -1
- package/skills/forge/scripts/inspect.mjs +5 -4
- package/skills/forge/scripts/update.mjs +21 -2
- package/src/cli.js +21 -1
package/README.md
CHANGED
package/package.json
CHANGED
package/skills/forge/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
|
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` |
|
|
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
|
|
30
|
+
Inicializa el proyecto arquitectónicamente con configuración persistente.
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
node
|
|
34
|
-
|
|
35
|
-
node
|
|
36
|
-
node
|
|
37
|
-
|
|
38
|
-
node
|
|
39
|
-
node
|
|
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
|
|
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
|
|
65
|
+
node {{AGENT_PATH}}/scripts/graph.mjs
|
|
64
66
|
```
|
|
65
67
|
|
|
66
68
|
Para salida JSON:
|
|
67
69
|
|
|
68
70
|
```
|
|
69
|
-
node
|
|
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
|
|
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
|
|
93
|
+
node {{AGENT_PATH}}/scripts/inspect.mjs
|
|
92
94
|
```
|
|
93
95
|
|
|
94
96
|
Para salida JSON:
|
|
95
97
|
|
|
96
98
|
```
|
|
97
|
-
node
|
|
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
|
|
107
|
+
node {{AGENT_PATH}}/scripts/assay.mjs
|
|
106
108
|
```
|
|
107
109
|
|
|
108
110
|
Filtros:
|
|
109
111
|
|
|
110
112
|
```
|
|
111
|
-
node
|
|
112
|
-
node
|
|
113
|
-
node
|
|
114
|
-
node
|
|
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
|
|
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
|
|
132
|
+
node {{AGENT_PATH}}/scripts/chain.mjs
|
|
131
133
|
```
|
|
132
134
|
|
|
133
135
|
Para salida JSON:
|
|
134
136
|
|
|
135
137
|
```
|
|
136
|
-
node
|
|
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
|
|
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
|
|
26
|
+
node {{AGENT_PATH}}/scripts/assay.mjs
|
|
27
27
|
|
|
28
28
|
# Solo la opinión de una persona
|
|
29
|
-
node
|
|
30
|
-
node
|
|
31
|
-
node
|
|
32
|
-
node
|
|
33
|
-
node
|
|
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
|
|
36
|
+
node {{AGENT_PATH}}/scripts/assay.mjs --json
|
|
37
37
|
|
|
38
38
|
# Persistir ensayo en .forge/assay/
|
|
39
|
-
node
|
|
39
|
+
node {{AGENT_PATH}}/scripts/assay.mjs --save
|
|
40
40
|
|
|
41
41
|
# Ver historial de ensayos
|
|
42
|
-
node
|
|
42
|
+
node {{AGENT_PATH}}/scripts/assay.mjs history
|
|
43
43
|
|
|
44
44
|
# Leer un ensayo previo
|
|
45
|
-
node
|
|
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
|
|
57
|
+
node {{AGENT_PATH}}/scripts/chain.mjs
|
|
58
58
|
|
|
59
59
|
# Grafo arquitectónico completo (nodos, edges, violaciones)
|
|
60
|
-
node
|
|
60
|
+
node {{AGENT_PATH}}/scripts/graph.mjs
|
|
61
61
|
|
|
62
62
|
# ARCHITECTURE.md con grafo incluido
|
|
63
|
-
node
|
|
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
|
|
88
|
+
- run: node {{AGENT_PATH}}/scripts/detect.mjs
|
|
89
89
|
env:
|
|
90
90
|
FORGE_STRICT: "true"
|
|
91
|
-
- run: node
|
|
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`
|
|
15
|
-
2. Ejecutar `scripts/bootstrap.mjs`
|
|
16
|
-
3.
|
|
17
|
-
4. Ejecutar `
|
|
18
|
-
5. Ejecutar `
|
|
19
|
-
6.
|
|
20
|
-
7.
|
|
21
|
-
8.
|
|
22
|
-
9.
|
|
23
|
-
10.
|
|
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
|
|
56
|
-
node
|
|
57
|
-
node
|
|
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
|
|
67
|
+
node {{AGENT_PATH}}/scripts/detect.mjs
|
|
68
68
|
|
|
69
69
|
# Solo errores y críticos
|
|
70
|
-
node
|
|
70
|
+
node {{AGENT_PATH}}/scripts/detect.mjs --severity ERROR
|
|
71
71
|
|
|
72
72
|
# Solo un tipo específico
|
|
73
|
-
node
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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")
|
|
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 =
|
|
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}
|
|
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
|
-
|
|
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
|
}
|