@ronaldjdevfs/forge 1.3.6 → 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 +1 -0
- package/skills/forge/command/forge.md +15 -13
- package/skills/forge/reference/forge.md +18 -10
- 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/README.md
CHANGED
package/package.json
CHANGED
package/skills/forge/SKILL.md
CHANGED
|
@@ -217,6 +217,7 @@ node --test {{AGENT_PATH}}/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
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
node {{AGENT_PATH}}/scripts/
|
|
34
|
-
|
|
35
|
-
node {{AGENT_PATH}}/scripts/
|
|
36
|
-
node {{AGENT_PATH}}/scripts/
|
|
37
|
-
|
|
38
|
-
node {{AGENT_PATH}}/scripts/
|
|
39
|
-
node {{AGENT_PATH}}/scripts/
|
|
40
|
-
|
|
30
|
+
Inicializa el proyecto arquitectónicamente con configuración persistente.
|
|
31
|
+
|
|
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
|
|
|
@@ -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
|
|
@@ -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 {
|