@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  <img src="favicon.svg" alt="Forge Logo" width="100" height="100">
2
2
 
3
- > **v1.3.6** — Multi-Agent Path Resolution Fix
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.6",
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": {
@@ -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` | 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.
31
-
32
- ```
33
- node {{AGENT_PATH}}/scripts/context.mjs
34
- node {{AGENT_PATH}}/scripts/bootstrap.mjs
35
- node {{AGENT_PATH}}/scripts/profile.mjs
36
- node {{AGENT_PATH}}/scripts/armorer.mjs
37
- node {{AGENT_PATH}}/scripts/graph.mjs
38
- node {{AGENT_PATH}}/scripts/chain.mjs
39
- node {{AGENT_PATH}}/scripts/architecture.mjs
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` 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
@@ -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.6";
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 {