specatlas 0.1.36 → 0.1.38

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 (2) hide show
  1. package/dist/bin.js +452 -161
  2. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -5810,7 +5810,7 @@ var require_pako = __commonJS({
5810
5810
  });
5811
5811
 
5812
5812
  // src/cli.ts
5813
- import path61 from "path";
5813
+ import path62 from "path";
5814
5814
 
5815
5815
  // ../core/dist/index.js
5816
5816
  import path from "path";
@@ -23473,14 +23473,14 @@ var parameters = /* @__PURE__ */ new Map([
23473
23473
  ["Z", 0],
23474
23474
  ["z", 0]
23475
23475
  ]);
23476
- var parse = function(path62) {
23476
+ var parse = function(path63) {
23477
23477
  var cmd;
23478
23478
  var ret = [];
23479
23479
  var args = [];
23480
23480
  var curArg = "";
23481
23481
  var foundDecimal = false;
23482
23482
  var params = 0;
23483
- for (var _i = 0, path_1 = path62; _i < path_1.length; _i++) {
23483
+ for (var _i = 0, path_1 = path63; _i < path_1.length; _i++) {
23484
23484
  var c = path_1[_i];
23485
23485
  if (parameters.has(c)) {
23486
23486
  params = parameters.get(c);
@@ -23794,8 +23794,8 @@ var segmentToBezier = function(cx1, cy1, th0, th1, rx, ry, sinTh, cosTh) {
23794
23794
  ];
23795
23795
  return result;
23796
23796
  };
23797
- var svgPathToOperators = function(path62) {
23798
- return apply(parse(path62));
23797
+ var svgPathToOperators = function(path63) {
23798
+ return apply(parse(path63));
23799
23799
  };
23800
23800
 
23801
23801
  // ../../node_modules/.pnpm/pdf-lib@1.17.1/node_modules/pdf-lib/es/api/operations.js
@@ -23952,7 +23952,7 @@ var drawEllipse = function(options) {
23952
23952
  popGraphicsState()
23953
23953
  ]).filter(Boolean);
23954
23954
  };
23955
- var drawSvgPath = function(path62, options) {
23955
+ var drawSvgPath = function(path63, options) {
23956
23956
  var _a, _b, _c;
23957
23957
  return __spreadArrays([
23958
23958
  pushGraphicsState(),
@@ -23966,7 +23966,7 @@ var drawSvgPath = function(path62, options) {
23966
23966
  options.borderWidth && setLineWidth(options.borderWidth),
23967
23967
  options.borderLineCap && setLineCap(options.borderLineCap),
23968
23968
  setDashPattern((_b = options.borderDashArray) !== null && _b !== void 0 ? _b : [], (_c = options.borderDashPhase) !== null && _c !== void 0 ? _c : 0)
23969
- ], svgPathToOperators(path62), [
23969
+ ], svgPathToOperators(path63), [
23970
23970
  // prettier-ignore
23971
23971
  options.color && options.borderWidth ? fillAndStroke() : options.color ? fill() : options.borderColor ? stroke() : closePath(),
23972
23972
  popGraphicsState()
@@ -27948,12 +27948,12 @@ var PDFPage = (
27948
27948
  graphicsState: graphicsStateKey
27949
27949
  }));
27950
27950
  };
27951
- PDFPage2.prototype.drawSvgPath = function(path62, options) {
27951
+ PDFPage2.prototype.drawSvgPath = function(path63, options) {
27952
27952
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
27953
27953
  if (options === void 0) {
27954
27954
  options = {};
27955
27955
  }
27956
- assertIs(path62, "path", ["string"]);
27956
+ assertIs(path63, "path", ["string"]);
27957
27957
  assertOrUndefined(options.x, "options.x", ["number"]);
27958
27958
  assertOrUndefined(options.y, "options.y", ["number"]);
27959
27959
  assertOrUndefined(options.scale, "options.scale", ["number"]);
@@ -27982,7 +27982,7 @@ var PDFPage = (
27982
27982
  options.borderColor = rgb(0, 0, 0);
27983
27983
  }
27984
27984
  var contentStream = this.getContentStream();
27985
- contentStream.push.apply(contentStream, drawSvgPath(path62, {
27985
+ contentStream.push.apply(contentStream, drawSvgPath(path63, {
27986
27986
  x: (_a = options.x) !== null && _a !== void 0 ? _a : this.x,
27987
27987
  y: (_b = options.y) !== null && _b !== void 0 ? _b : this.y,
27988
27988
  scale: options.scale,
@@ -29375,10 +29375,12 @@ import path23 from "path";
29375
29375
  import { promises as fs2 } from "fs";
29376
29376
  import path24 from "path";
29377
29377
  import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
29378
+ import os from "os";
29378
29379
  import path25 from "path";
29379
29380
  import path26 from "path";
29380
29381
  import path27 from "path";
29381
29382
  import path28 from "path";
29383
+ import path29 from "path";
29382
29384
  var CORE_VERSION = "0.0.1";
29383
29385
  var DIAGNOSTIC_FAMILIES = [
29384
29386
  { prefix: "LINT-STR", title: "Estructura", description: "La forma del documento: ids, encabezados y bloques donde corresponde." },
@@ -30710,13 +30712,13 @@ var FIELD_LABEL = {
30710
30712
  when: "un CUANDO",
30711
30713
  then: "un ENTONCES"
30712
30714
  };
30713
- function lintPlaceholders(req, path292) {
30715
+ function lintPlaceholders(req, path302) {
30714
30716
  const hits = requirementPlaceholders(req);
30715
30717
  if (hits.length === 0) return [];
30716
30718
  if (isSkeletonRequirement(req)) {
30717
30719
  return [
30718
30720
  diag("LINT-BIZ-003", "error", `El requisito ${req.id} sigue siendo la plantilla de \`satlas new\`: no especifica nada todav\xEDa`, {
30719
- path: path292,
30721
+ path: path302,
30720
30722
  line: req.line,
30721
30723
  suggestion: "Escribe la necesidad de negocio, sus reglas y el CUANDO/ENTONCES de cada escenario"
30722
30724
  })
@@ -30725,7 +30727,7 @@ function lintPlaceholders(req, path292) {
30725
30727
  return hits.map((hit) => {
30726
30728
  const where = hit.scenario ? `${hit.scenario}` : req.id;
30727
30729
  return diag("LINT-BIZ-003", "error", `Texto de plantilla sin completar en ${where}: ${FIELD_LABEL[hit.field]} sigue siendo el hueco de \`satlas new\``, {
30728
- path: path292,
30730
+ path: path302,
30729
30731
  line: hit.line,
30730
30732
  suggestion: "Escribe el comportamiento real de negocio en lugar del texto entre par\xE9ntesis de la plantilla"
30731
30733
  });
@@ -30755,14 +30757,14 @@ function wordBoundary(text, term) {
30755
30757
  const re = new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, "iu");
30756
30758
  return re.test(text);
30757
30759
  }
30758
- function lintText(text, code, terms, label, path292, line) {
30760
+ function lintText(text, code, terms, label, path302, line) {
30759
30761
  const out = [];
30760
30762
  const lower = text.toLowerCase();
30761
30763
  for (const term of terms) {
30762
30764
  if (wordBoundary(lower, term)) {
30763
30765
  out.push(
30764
30766
  diag(code, "error", `${label}: "${term}"`, {
30765
- path: path292,
30767
+ path: path302,
30766
30768
  line,
30767
30769
  suggestion: "La especificaci\xF3n es funcional y de negocio: describe comportamiento, no tecnolog\xEDa ni adjetivos vagos"
30768
30770
  })
@@ -30771,7 +30773,7 @@ function lintText(text, code, terms, label, path292, line) {
30771
30773
  }
30772
30774
  return out;
30773
30775
  }
30774
- function lintRequirement(req, path292, opts = {}) {
30776
+ function lintRequirement(req, path302, opts = {}) {
30775
30777
  const out = [];
30776
30778
  const vague = opts.language === "en" ? VAGUE_EN : VAGUE_ES;
30777
30779
  const tech = opts.language === "en" ? TECH_EN : TECH_ES;
@@ -30782,33 +30784,33 @@ function lintRequirement(req, path292, opts = {}) {
30782
30784
  ...req.scenarios.flatMap((s) => [...s.when.map((w) => ({ text: w, line: s.line })), ...s.then.map((t) => ({ text: t, line: s.line }))])
30783
30785
  ];
30784
30786
  for (const part of parts) {
30785
- out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path292, part.line));
30787
+ out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path302, part.line));
30786
30788
  if (opts.businessOnly !== false) {
30787
- out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path292, part.line));
30789
+ out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path302, part.line));
30788
30790
  }
30789
30791
  }
30790
- out.push(...lintPlaceholders(req, path292));
30792
+ out.push(...lintPlaceholders(req, path302));
30791
30793
  if (req.scenarios.length === 0) {
30792
- out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path292, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
30794
+ out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path302, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
30793
30795
  }
30794
30796
  return out;
30795
30797
  }
30796
- function lintDelta(delta, livingRequirements, path292, opts = {}) {
30798
+ function lintDelta(delta, livingRequirements, path302, opts = {}) {
30797
30799
  const out = [...delta.diagnostics];
30798
30800
  for (const req of [...delta.added, ...delta.modified]) {
30799
- out.push(...lintRequirement(req, path292, opts));
30801
+ out.push(...lintRequirement(req, path302, opts));
30800
30802
  }
30801
30803
  for (const req of delta.modified) {
30802
30804
  const living = livingRequirements.get(req.id);
30803
30805
  if (!living) {
30804
- out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path292, line: req.line }));
30806
+ out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path302, line: req.line }));
30805
30807
  continue;
30806
30808
  }
30807
30809
  for (const existing of living.scenarios) {
30808
30810
  if (!req.scenarios.some((s) => s.id === existing.id)) {
30809
30811
  out.push(
30810
30812
  diag("TRACE-007", "error", `MODIFIED ${req.id} pierde el escenario ${existing.id}: copia el bloque completo`, {
30811
- path: path292,
30813
+ path: path302,
30812
30814
  line: req.line,
30813
30815
  suggestion: "Copia el bloque completo de la spec viva y ed\xEDtalo; para quitarlo, decl\xE1ralo en REMOVED"
30814
30816
  })
@@ -30819,15 +30821,15 @@ function lintDelta(delta, livingRequirements, path292, opts = {}) {
30819
30821
  for (const req of delta.removed) {
30820
30822
  const living = livingRequirements.get(req.id);
30821
30823
  if (!living) {
30822
- out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path292, line: req.line }));
30824
+ out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path302, line: req.line }));
30823
30825
  }
30824
30826
  }
30825
30827
  for (const rename2 of delta.renamed) {
30826
30828
  if (rename2.from.id !== rename2.to.id) {
30827
- out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path292, line: rename2.line }));
30829
+ out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path302, line: rename2.line }));
30828
30830
  }
30829
30831
  if (!livingRequirements.has(rename2.from.id)) {
30830
- out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path292, line: rename2.line }));
30832
+ out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path302, line: rename2.line }));
30831
30833
  }
30832
30834
  }
30833
30835
  return out;
@@ -30851,7 +30853,7 @@ var MERMAID_KEYWORDS = [
30851
30853
  "architecture-beta"
30852
30854
  ];
30853
30855
  var MERMAID_BLOCKS = /^\s*(alt|loop|opt|par|rect|critical|break|subgraph)\b/;
30854
- function lintPlan(planText, path292) {
30856
+ function lintPlan(planText, path302) {
30855
30857
  const out = [];
30856
30858
  const blocks = [...planText.matchAll(/```mermaid\r?\n([\s\S]*?)```/g)];
30857
30859
  for (const [index, block] of blocks.entries()) {
@@ -30861,7 +30863,7 @@ function lintPlan(planText, path292) {
30861
30863
  if (!MERMAID_KEYWORDS.some((keyword) => first.startsWith(keyword))) {
30862
30864
  out.push(
30863
30865
  diag("LINT-PLN-002", "error", `Diagrama mermaid ${index + 1}: la primera l\xEDnea debe declarar el tipo (${MERMAID_KEYWORDS.slice(0, 5).join(", ")}\u2026) y empieza por "${first.slice(0, 30)}"`, {
30864
- path: path292,
30866
+ path: path302,
30865
30867
  suggestion: "Corrige el tipo del diagrama o elimina el bloque"
30866
30868
  })
30867
30869
  );
@@ -30875,7 +30877,7 @@ function lintPlan(planText, path292) {
30875
30877
  if (open !== 0) {
30876
30878
  out.push(
30877
30879
  diag("LINT-PLN-002", "error", `Diagrama mermaid ${index + 1}: faltan ${Math.abs(open)} \`end\` (bloques alt/loop/subgraph sin cerrar)`, {
30878
- path: path292,
30880
+ path: path302,
30879
30881
  suggestion: "Cierra cada bloque alt/loop/opt/par/rect/subgraph con `end`"
30880
30882
  })
30881
30883
  );
@@ -30887,7 +30889,7 @@ function lintPlan(planText, path292) {
30887
30889
  if (message.includes(";")) {
30888
30890
  out.push(
30889
30891
  diag("LINT-PLN-003", "error", `Diagrama mermaid ${index + 1}: el mensaje "${message.trim().slice(0, 40)}\u2026" usa \`;\` y mermaid lo interpreta como fin de sentencia`, {
30890
- path: path292,
30892
+ path: path302,
30891
30893
  suggestion: "Sustituye `;` por `\xB7` o `,` en los mensajes de sequenceDiagram"
30892
30894
  })
30893
30895
  );
@@ -34463,6 +34465,11 @@ function labelsFor(language) {
34463
34465
  signatureName: "Approver name",
34464
34466
  signatureDate: "Signature date",
34465
34467
  signatureNote: "The signature is recorded with the specification hash and the date.",
34468
+ signedBy: "Approved by",
34469
+ signedAt: "Date",
34470
+ signedHash: "Signed hash",
34471
+ signedChannel: "Channel",
34472
+ signHere: "Sign here to approve this proposal.",
34466
34473
  openMockup: "Open mockup",
34467
34474
  mockupHint: "Each screen opens full size in its own tab. In a browser they are also previewed inline.",
34468
34475
  illustrates: "Illustrates"
@@ -34494,6 +34501,11 @@ function labelsFor(language) {
34494
34501
  missingMockups: "Mockups declarados que faltan",
34495
34502
  screenshots: "Capturas",
34496
34503
  print: "Imprimir / Guardar PDF",
34504
+ signedBy: "Aprobada por",
34505
+ signedAt: "Fecha",
34506
+ signedHash: "Huella firmada",
34507
+ signedChannel: "Canal",
34508
+ signHere: "Firma aqu\xED para aprobar esta propuesta.",
34497
34509
  signatureName: "Nombre de quien aprueba",
34498
34510
  signatureDate: "Fecha de la firma",
34499
34511
  signatureNote: "La firma se registra con la huella de la especificaci\xF3n y la fecha.",
@@ -34576,6 +34588,8 @@ th { background:var(--elev); font-size:11px; text-transform:uppercase; letter-sp
34576
34588
  .grid-shots { display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:12px; margin-top:12px; }
34577
34589
  .grid-shots img { width:100%; border:1px solid var(--line); border-radius:10px; }
34578
34590
  .signature { display:grid; grid-template-columns:1.2fr .8fr; gap:18px; }
34591
+ .sign-line.signed { border-style:solid; border-color:var(--ok); background:color-mix(in srgb, var(--ok) 8%, transparent); }
34592
+ .sign-line .mono { font-family:ui-monospace, Consolas, monospace; font-size:11.5px; word-break:break-all; }
34579
34593
  .sign-line { border:1px dashed var(--accent); border-radius:8px; padding:16px; background:var(--accent-soft); }
34580
34594
  .sign-line .sl-row { display:flex; justify-content:space-between; gap:12px; border-bottom:1px solid var(--line); padding:0 0 6px; margin-bottom:18px; color:var(--muted); font-size:12.5px; }
34581
34595
  .sign-line .sl-row:last-child { border-bottom:0; margin-bottom:0; padding-top:14px; }
@@ -34622,6 +34636,15 @@ function page(input) {
34622
34636
  return `<tr><td><code>${esc(s.id)}</code></td><td>${esc(s.title)}</td><td><span class="pill ${cls}">${esc(label)}</span></td></tr>`;
34623
34637
  }).join("");
34624
34638
  const signatureState = input.approvalStatus === "valid" && input.approval ? `<div class="sign-state ok"><span class="ss-label">${esc(l.approve)}</span><b>${esc(l.approveDone.replace("{by}", input.approval.by).replace("{at}", input.approval.at))}</b></div>` : input.approvalStatus === "stale" && input.approval ? `<div class="sign-state stale"><span class="ss-label">${esc(l.approve)}</span><b>${esc(l.approveStale)}</b><p>${esc(l.approveDone.replace("{by}", input.approval.by).replace("{at}", input.approval.at))}</p></div>` : `<div class="sign-state"><span class="ss-label">${esc(l.approve)}</span><b>${esc(l.approvePending)}</b><p>${esc(l.approveText)}</p></div>`;
34639
+ const signatureBlock = input.approvalStatus === "valid" && input.approval ? `<div class="sign-line signed">
34640
+ <div class="sl-row"><span>${esc(l.signedBy)}</span><b>${esc(input.approval.by)}</b></div>
34641
+ <div class="sl-row"><span>${esc(l.signedAt)}</span><b>${esc(input.approval.at)}</b></div>
34642
+ <div class="sl-row"><span>${esc(l.signedHash)}</span><span class="mono">${esc(input.hash)}</span></div>
34643
+ </div>` : `<div class="sign-line">
34644
+ <p class="sign-help" style="margin:0 0 12px">${esc(l.signHere)}</p>
34645
+ <div class="sl-row"><span>${esc(l.signatureName)}</span><span style="font-family:ui-monospace,Consolas,monospace">____________________</span></div>
34646
+ <div class="sl-row"><span>${esc(l.signatureDate)}</span><span style="font-family:ui-monospace,Consolas,monospace">____ / ____ / ________</span></div>
34647
+ </div>`;
34625
34648
  const mockupHtml = input.mockups.length === 0 ? `<div class="callout">${esc(l.noMockups)}</div>` : `<p class="sign-help" style="margin:0 0 12px">${esc(l.mockupHint)}</p><div class="mockups">${input.mockups.map(
34626
34649
  (m) => `<div class="mockup ${m.platform === "mobile" ? "phone" : ""}" data-mockup="${esc(m.file)}" data-title="${esc(m.title)}"><header><span>${esc(m.title)}</span><a class="btn" href="${esc(m.file)}" target="_blank" rel="noopener">${esc(l.openMockup)}</a></header><div class="mockup-body"><span class="mockup-file">${esc(m.file)}</span>${m.illustrates.length > 0 ? `<span class="mockup-scn">${esc(l.illustrates)}: ${m.illustrates.map((id) => esc(id)).join(", ")}</span>` : ""}</div></div>`
34627
34650
  ).join("")}</div>`;
@@ -34716,14 +34739,11 @@ function page(input) {
34716
34739
  <div class="card-head"><span class="num">05</span><h2>${esc(l.approve)}</h2><span class="sub">acto humano \xB7 queda auditado</span></div>
34717
34740
  <div class="card-body">
34718
34741
  <div class="signature">
34719
- <div class="sign-line">
34720
- <div class="sl-row"><span>${esc(l.signatureName)}</span><span style="font-family:ui-monospace,Consolas,monospace">____________________</span></div>
34721
- <div class="sl-row"><span>${esc(l.signatureDate)}</span><span style="font-family:ui-monospace,Consolas,monospace">____ / ____ / ________</span></div>
34722
- </div>
34742
+ ${signatureBlock}
34723
34743
  <div>
34724
34744
  <p class="sign-help">${esc(l.signatureNote)}</p>
34725
- <p class="sign-help" style="margin-top:8px"><code>${esc(input.approveHint)}</code></p>
34726
- <p class="sign-help" style="margin-top:10px">${esc(l.approveStale)}</p>
34745
+ ${input.approvalStatus === "valid" ? "" : `<p class="sign-help" style="margin-top:8px"><code>${esc(input.approveHint)}</code></p>`}
34746
+ ${input.approvalStatus === "stale" ? `<p class="sign-help" style="margin-top:10px">${esc(l.approveStale)}</p>` : ""}
34727
34747
  </div>
34728
34748
  </div>
34729
34749
  </div>
@@ -35712,6 +35732,140 @@ async function upgradeAdvisory(root) {
35712
35732
  }
35713
35733
  return { plan, diagnostics };
35714
35734
  }
35735
+ var PACKAGE_NAME = "specatlas";
35736
+ var REGISTRY = "https://registry.npmjs.org";
35737
+ var TTL_MS = 24 * 60 * 60 * 1e3;
35738
+ var TIMEOUT_MS = 2e3;
35739
+ function detectarInstalacion(rutaEjecutable2, plataforma = process.platform) {
35740
+ const ruta = toPosix(rutaEjecutable2).toLowerCase();
35741
+ if (/[/](_npx|\.npm[/]_npx)[/]/.test(ruta) || ruta.includes("/.pnpm-store/") || /[/]dlx-[^/]*[/]/.test(ruta)) {
35742
+ return {
35743
+ gestor: "npx",
35744
+ motivo: "la herramienta se est\xE1 ejecutando sin instalar: cada ejecuci\xF3n ya usa la versi\xF3n publicada"
35745
+ };
35746
+ }
35747
+ if (ruta.includes("/pnpm/global/") || ruta.includes("/.pnpm/global/") || /[/]pnpm[/][^/]*[/]global[/]/.test(ruta)) {
35748
+ return { gestor: "pnpm", comando: `pnpm add -g ${PACKAGE_NAME}@latest` };
35749
+ }
35750
+ if (ruta.includes("/.bun/")) {
35751
+ return { gestor: "bun", comando: `bun add -g ${PACKAGE_NAME}@latest` };
35752
+ }
35753
+ if (ruta.includes("/.yarn/") || ruta.includes("/yarn/global/")) {
35754
+ return { gestor: "yarn", comando: `yarn global add ${PACKAGE_NAME}@latest` };
35755
+ }
35756
+ if (ruta.includes("/npm/node_modules/") || ruta.includes("/roaming/npm/") || ruta.includes("/lib/node_modules/") || ruta.includes("/npm-global/")) {
35757
+ return { gestor: "npm", comando: `npm install -g ${PACKAGE_NAME}@latest` };
35758
+ }
35759
+ return { gestor: "desconocido", comando: plataforma === "win32" ? `npm install -g ${PACKAGE_NAME}@latest` : `npm install -g ${PACKAGE_NAME}@latest` };
35760
+ }
35761
+ function compararVersiones(a, b) {
35762
+ const partes = (v) => v.trim().replace(/^v/, "").split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
35763
+ const ap = partes(a);
35764
+ const bp = partes(b);
35765
+ for (let i = 0; i < 3; i += 1) {
35766
+ const diff = (ap[i] ?? 0) - (bp[i] ?? 0);
35767
+ if (diff !== 0) return diff > 0 ? 1 : -1;
35768
+ }
35769
+ return 0;
35770
+ }
35771
+ function hayVersionNueva(instalada, publicada) {
35772
+ if (!publicada) return false;
35773
+ return compararVersiones(publicada, instalada) > 0;
35774
+ }
35775
+ var PREFERENCIAS_POR_DEFECTO = { comprobar: true, automatica: false };
35776
+ function directorioPersonal(home = os.homedir()) {
35777
+ return path25.join(home, ".specatlas");
35778
+ }
35779
+ function rutaPreferencias(home) {
35780
+ return path25.join(directorioPersonal(home), "config.json");
35781
+ }
35782
+ function rutaEstado(home) {
35783
+ return path25.join(directorioPersonal(home), "update-check.json");
35784
+ }
35785
+ async function leerPreferencias(home) {
35786
+ const raw = await readTextIfExists(rutaPreferencias(home));
35787
+ if (raw === void 0) return { ...PREFERENCIAS_POR_DEFECTO };
35788
+ try {
35789
+ const data = JSON.parse(raw);
35790
+ return {
35791
+ comprobar: data.update?.comprobar ?? PREFERENCIAS_POR_DEFECTO.comprobar,
35792
+ automatica: data.update?.automatica ?? PREFERENCIAS_POR_DEFECTO.automatica
35793
+ };
35794
+ } catch {
35795
+ return { ...PREFERENCIAS_POR_DEFECTO };
35796
+ }
35797
+ }
35798
+ async function escribirPreferencias(preferencias, home) {
35799
+ const file = rutaPreferencias(home);
35800
+ await ensureDir(path25.dirname(file));
35801
+ await writeText(file, `${JSON.stringify({ update: preferencias }, null, 2)}
35802
+ `);
35803
+ return file;
35804
+ }
35805
+ async function leerEstado(home) {
35806
+ const raw = await readTextIfExists(rutaEstado(home));
35807
+ if (raw === void 0) return {};
35808
+ try {
35809
+ return JSON.parse(raw);
35810
+ } catch {
35811
+ return {};
35812
+ }
35813
+ }
35814
+ async function escribirEstado(estado, home) {
35815
+ const file = rutaEstado(home);
35816
+ await ensureDir(path25.dirname(file));
35817
+ await writeText(file, `${JSON.stringify(estado, null, 2)}
35818
+ `);
35819
+ }
35820
+ function tocaConsultar(estado, ahora = /* @__PURE__ */ new Date(), ttlMs = TTL_MS) {
35821
+ if (!estado.ultimaConsulta) return true;
35822
+ const ultima = Date.parse(estado.ultimaConsulta);
35823
+ if (Number.isNaN(ultima)) return true;
35824
+ return ahora.getTime() - ultima >= ttlMs;
35825
+ }
35826
+ function esIntegracionContinua(env = process.env) {
35827
+ return Boolean(env["CI"] || env["CONTINUOUS_INTEGRATION"] || env["GITHUB_ACTIONS"] || env["GITLAB_CI"] || env["BUILDKITE"] || env["TEAMCITY_VERSION"]);
35828
+ }
35829
+ function comprobacionDesactivada(env = process.env) {
35830
+ const flag3 = env["SPECATLAS_NO_UPDATE_CHECK"];
35831
+ return flag3 === "1" || flag3 === "true";
35832
+ }
35833
+ async function consultarVersionPublicada(timeoutMs = TIMEOUT_MS, fetchImpl = fetch) {
35834
+ const control = new AbortController();
35835
+ const timer = setTimeout(() => control.abort(), timeoutMs);
35836
+ try {
35837
+ const respuesta = await fetchImpl(`${REGISTRY}/-/package/${PACKAGE_NAME}/dist-tags`, { signal: control.signal });
35838
+ if (!respuesta.ok) return void 0;
35839
+ const data = await respuesta.json();
35840
+ return typeof data.latest === "string" ? data.latest : void 0;
35841
+ } catch {
35842
+ return void 0;
35843
+ } finally {
35844
+ clearTimeout(timer);
35845
+ }
35846
+ }
35847
+ async function comprobarActualizacion(ctx) {
35848
+ const env = ctx.env ?? process.env;
35849
+ if (ctx.json || esIntegracionContinua(env) || comprobacionDesactivada(env)) return void 0;
35850
+ const preferencias = await leerPreferencias(ctx.home);
35851
+ if (!preferencias.comprobar) return void 0;
35852
+ const ahora = ctx.ahora ?? /* @__PURE__ */ new Date();
35853
+ const estado = await leerEstado(ctx.home);
35854
+ let publicada = estado.ultimaVersion;
35855
+ if (tocaConsultar(estado, ahora)) {
35856
+ publicada = await consultarVersionPublicada(TIMEOUT_MS, ctx.fetchImpl ?? fetch);
35857
+ if (publicada === void 0) return void 0;
35858
+ await escribirEstado({ ultimaConsulta: ahora.toISOString(), ultimaVersion: publicada }, ctx.home);
35859
+ }
35860
+ if (!hayVersionNueva(ctx.instalada, publicada)) return void 0;
35861
+ const instalacion = detectarInstalacion(ctx.rutaEjecutable);
35862
+ return {
35863
+ instalada: ctx.instalada,
35864
+ publicada,
35865
+ comando: instalacion.comando ?? `npm install -g ${PACKAGE_NAME}@latest`,
35866
+ gestor: instalacion.gestor
35867
+ };
35868
+ }
35715
35869
  var DOCS_MARKER_START = "<!-- specatlas:generado:inicio -->";
35716
35870
  var DOCS_MARKER_END = "<!-- specatlas:generado:fin -->";
35717
35871
  var ES_WORDING = {
@@ -36197,7 +36351,7 @@ ${notes}
36197
36351
  ${existing}`;
36198
36352
  }
36199
36353
  async function generateDocs(opts) {
36200
- const root = path25.resolve(opts.root);
36354
+ const root = path26.resolve(opts.root);
36201
36355
  const { config } = await loadWorkspace(root);
36202
36356
  const change = await loadChange(root, opts.slug);
36203
36357
  if (!change.meta) {
@@ -36213,10 +36367,10 @@ async function generateDocs(opts) {
36213
36367
  const tipos = tipo === "all" ? ["tecnica", "manual"] : [tipo];
36214
36368
  const templates = templatesFor(language);
36215
36369
  const [proposalRaw, planRaw, glossaryRaw, analyzeRaw, mockupManifest] = await Promise.all([
36216
- readTextIfExists(path25.join(change.dir, "proposal.md")),
36217
- readTextIfExists(path25.join(change.dir, "plan.md")),
36218
- readTextIfExists(path25.join(root, ".sdd", "glossary.md")),
36219
- readTextIfExists(path25.join(change.dir, "analyze.md")),
36370
+ readTextIfExists(path26.join(change.dir, "proposal.md")),
36371
+ readTextIfExists(path26.join(change.dir, "plan.md")),
36372
+ readTextIfExists(path26.join(root, ".sdd", "glossary.md")),
36373
+ readTextIfExists(path26.join(change.dir, "analyze.md")),
36220
36374
  readMockupManifest(root, change.slug)
36221
36375
  ]);
36222
36376
  const mockups = (mockupManifest.manifest?.screens ?? []).map((screen) => ({
@@ -36230,7 +36384,7 @@ async function generateDocs(opts) {
36230
36384
  const files = [];
36231
36385
  const diagnostics = [];
36232
36386
  for (const current of tipos) {
36233
- const file = path25.join(change.dir, "docs", `${current}.md`);
36387
+ const file = path26.join(change.dir, "docs", `${current}.md`);
36234
36388
  const block = renderTemplate(current === "tecnica" ? templates.docTecnica : templates.docManual, data);
36235
36389
  const existing = await readTextIfExists(file);
36236
36390
  const content = mergeManaged(existing, block, language);
@@ -36239,7 +36393,7 @@ async function generateDocs(opts) {
36239
36393
  const label = current === "tecnica" ? "t\xE9cnico" : "manual";
36240
36394
  const title = `${current === "tecnica" ? language === "en" ? "Technical document" : "Documento t\xE9cnico" : language === "en" ? "User manual" : "Manual de usuario"} \u2014 ${change.meta.title ?? change.slug}`;
36241
36395
  try {
36242
- const htmlPath = path25.join(change.dir, "docs", `${current}.html`);
36396
+ const htmlPath = path26.join(change.dir, "docs", `${current}.html`);
36243
36397
  await writeText(htmlPath, renderDocument(content, { theme: "auto", title }));
36244
36398
  doc.htmlPath = htmlPath;
36245
36399
  } catch (error2) {
@@ -36251,7 +36405,7 @@ async function generateDocs(opts) {
36251
36405
  );
36252
36406
  }
36253
36407
  try {
36254
- const pdfPath = path25.join(change.dir, "docs", `${current}.pdf`);
36408
+ const pdfPath = path26.join(change.dir, "docs", `${current}.pdf`);
36255
36409
  const pdf = await renderPdf(content, { title, project: config.project.name, generatedAt: localStamp(ahora) });
36256
36410
  await writeBytes(pdfPath, pdf.bytes);
36257
36411
  doc.pdfPath = pdfPath;
@@ -36304,7 +36458,7 @@ function resultOf(diagnostic, root) {
36304
36458
  message: { text: diagnostic.message }
36305
36459
  };
36306
36460
  if (diagnostic.path) {
36307
- const rel = path26.relative(root, diagnostic.path);
36461
+ const rel = path27.relative(root, diagnostic.path);
36308
36462
  if (rel !== "") {
36309
36463
  const physicalLocation = {
36310
36464
  artifactLocation: { uri: toPosix(rel) },
@@ -36359,7 +36513,7 @@ async function runDoctor(root) {
36359
36513
  const approvals = await loadApprovals(workspace.sddDir);
36360
36514
  findings.push(...approvals.diagnostics);
36361
36515
  for (const change of workspace.changes) {
36362
- const deltaPath = path27.join(change.dir, "spec.md");
36516
+ const deltaPath = path28.join(change.dir, "spec.md");
36363
36517
  const deltaContent = await readTextIfExists(deltaPath);
36364
36518
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent ?? void 0);
36365
36519
  if ((change.planPath || change.tasks) && (approval.status === "missing" || approval.status === "stale")) {
@@ -36381,7 +36535,7 @@ async function runDoctor(root) {
36381
36535
  }
36382
36536
  for (const override of change.meta?.overrides ?? []) {
36383
36537
  if (!override.reason.trim() || !override.by.trim()) {
36384
- findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path27.join(change.dir, "meta.yaml") }));
36538
+ findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path28.join(change.dir, "meta.yaml") }));
36385
36539
  }
36386
36540
  }
36387
36541
  }
@@ -36413,7 +36567,7 @@ function livingRequirementsMap(specs) {
36413
36567
  return map;
36414
36568
  }
36415
36569
  async function runCiGate(opts) {
36416
- const root = path28.resolve(opts.root);
36570
+ const root = path29.resolve(opts.root);
36417
36571
  const { workspace, config } = await loadWorkspace(root);
36418
36572
  const diagnostics = [];
36419
36573
  const checks = [];
@@ -36424,7 +36578,7 @@ async function runCiGate(opts) {
36424
36578
  let changesErrors = 0;
36425
36579
  let changesWarnings = 0;
36426
36580
  for (const change of workspace.changes) {
36427
- const lintFindings = change.delta ? lintDelta(change.delta, living, path28.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
36581
+ const lintFindings = change.delta ? lintDelta(change.delta, living, path29.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
36428
36582
  const trace = checkTrace({
36429
36583
  specs: workspace.specs,
36430
36584
  change,
@@ -36565,6 +36719,7 @@ var CATALOG = [
36565
36719
  { name: "run", description: "Persiste runs y eventos de ejecuci\xF3n (auditor\xEDa y reanudaci\xF3n)", flags: ["phase", "inputs", "data", "slug", "status", "json"], usage: "satlas run start|event|status|show|list" },
36566
36720
  { name: "hash", description: "Calcula el sha256 de un archivo o texto (para evidencia)", flags: ["text", "json"], usage: 'satlas hash <archivo> | satlas hash --text "salida"' },
36567
36721
  { name: "explain", description: "Explica un c\xF3digo de diagn\xF3stico: qu\xE9 significa, por qu\xE9 importa y c\xF3mo se cierra", flags: ["json"], usage: "satlas explain [<c\xF3digo>]" },
36722
+ { name: "self-update", description: "Actualiza la herramienta a la versi\xF3n publicada (o solo comprueba si la hay)", flags: ["check", "auto", "json"], usage: "satlas self-update [--check] [--auto on|off]" },
36568
36723
  { name: "version", description: "Muestra la versi\xF3n", flags: ["json"], usage: "satlas version" },
36569
36724
  { name: "help", description: "Muestra la ayuda", flags: ["json"], usage: "satlas help [comando]" }
36570
36725
  ];
@@ -36682,26 +36837,26 @@ function cliVersion() {
36682
36837
  }
36683
36838
 
36684
36839
  // src/commands/adapters.ts
36685
- import path31 from "path";
36840
+ import path32 from "path";
36686
36841
 
36687
36842
  // ../adapters/dist/index.js
36688
- import path29 from "path";
36843
+ import path30 from "path";
36689
36844
  import { stringify as stringifyYaml9 } from "yaml";
36690
36845
  import path210 from "path";
36691
36846
  async function loadWorkflow(workflowDir) {
36692
- const phasesDir = path29.join(workflowDir, "phases");
36693
- const snippetsDir = path29.join(workflowDir, "snippets");
36847
+ const phasesDir = path30.join(workflowDir, "phases");
36848
+ const snippetsDir = path30.join(workflowDir, "snippets");
36694
36849
  const phases = [];
36695
36850
  const snippets = /* @__PURE__ */ new Map();
36696
36851
  const hashParts = [];
36697
36852
  for (const entry of (await listDir(phasesDir)).sort()) {
36698
36853
  if (!entry.endsWith(".md")) continue;
36699
- const filePath = path29.join(phasesDir, entry);
36854
+ const filePath = path30.join(phasesDir, entry);
36700
36855
  const raw = await readText(filePath);
36701
36856
  hashParts.push(raw);
36702
36857
  const fm = parseFrontmatter(raw, filePath);
36703
36858
  const data = fm.data;
36704
- const id = typeof data["id"] === "string" ? data["id"] : path29.basename(entry, ".md");
36859
+ const id = typeof data["id"] === "string" ? data["id"] : path30.basename(entry, ".md");
36705
36860
  phases.push({
36706
36861
  id,
36707
36862
  title: typeof data["title"] === "string" ? data["title"] : id,
@@ -36715,9 +36870,9 @@ async function loadWorkflow(workflowDir) {
36715
36870
  }
36716
36871
  for (const entry of (await listDir(snippetsDir)).sort()) {
36717
36872
  if (!entry.endsWith(".md")) continue;
36718
- const raw = await readTextIfExists(path29.join(snippetsDir, entry)) ?? "";
36873
+ const raw = await readTextIfExists(path30.join(snippetsDir, entry)) ?? "";
36719
36874
  hashParts.push(raw);
36720
- snippets.set(path29.basename(entry, ".md"), raw.trim());
36875
+ snippets.set(path30.basename(entry, ".md"), raw.trim());
36721
36876
  }
36722
36877
  return { phases: phases.sort((a, b) => a.id.localeCompare(b.id)), snippets, sourceHash: sha256(hashParts.join("\n---\n")) };
36723
36878
  }
@@ -37032,11 +37187,11 @@ async function checkAdapters(opts) {
37032
37187
  }
37033
37188
 
37034
37189
  // src/paths.ts
37035
- import path30 from "path";
37190
+ import path31 from "path";
37036
37191
  import { fileURLToPath as fileURLToPath2 } from "url";
37037
37192
  async function isSourceRepoRoot(dir) {
37038
- if (!await exists(path30.join(dir, "pnpm-workspace.yaml"))) return false;
37039
- const raw = await readTextIfExists(path30.join(dir, "packages", "cli", "package.json"));
37193
+ if (!await exists(path31.join(dir, "pnpm-workspace.yaml"))) return false;
37194
+ const raw = await readTextIfExists(path31.join(dir, "packages", "cli", "package.json"));
37040
37195
  if (raw === void 0) return false;
37041
37196
  try {
37042
37197
  return JSON.parse(raw).name === "specatlas";
@@ -37048,17 +37203,17 @@ async function sourceRepoRoot(startDir) {
37048
37203
  let dir = startDir;
37049
37204
  for (let i = 0; i < 5; i += 1) {
37050
37205
  if (await isSourceRepoRoot(dir)) return dir;
37051
- const parent = path30.dirname(dir);
37206
+ const parent = path31.dirname(dir);
37052
37207
  if (parent === dir) break;
37053
37208
  dir = parent;
37054
37209
  }
37055
37210
  return void 0;
37056
37211
  }
37057
37212
  async function resolveFromSource(relative) {
37058
- const start = path30.dirname(fileURLToPath2(import.meta.url));
37213
+ const start = path31.dirname(fileURLToPath2(import.meta.url));
37059
37214
  const root = await sourceRepoRoot(start);
37060
37215
  if (!root) return void 0;
37061
- const candidate = path30.join(root, relative);
37216
+ const candidate = path31.join(root, relative);
37062
37217
  return await exists(candidate) ? candidate : void 0;
37063
37218
  }
37064
37219
  async function resolveProfilesDir() {
@@ -37069,16 +37224,16 @@ async function resolveProfilesDir() {
37069
37224
  async function resolveWorkflowDir() {
37070
37225
  const env = process.env["SPECATLAS_WORKFLOW_DIR"];
37071
37226
  if (env && await exists(env)) return env;
37072
- return await resolveFromSource("workflow") ?? walkUpFor(path30.join("workflow", "phases"));
37227
+ return await resolveFromSource("workflow") ?? walkUpFor(path31.join("workflow", "phases"));
37073
37228
  }
37074
37229
  async function walkUpFor(relative) {
37075
- let dir = path30.dirname(fileURLToPath2(import.meta.url));
37230
+ let dir = path31.dirname(fileURLToPath2(import.meta.url));
37076
37231
  for (let i = 0; i < 8; i += 1) {
37077
- const candidate = path30.join(dir, relative);
37232
+ const candidate = path31.join(dir, relative);
37078
37233
  if (await exists(candidate)) {
37079
- return relative.includes(path30.sep) ? path30.dirname(candidate) : candidate;
37234
+ return relative.includes(path31.sep) ? path31.dirname(candidate) : candidate;
37080
37235
  }
37081
- const parent = path30.dirname(dir);
37236
+ const parent = path31.dirname(dir);
37082
37237
  if (parent === dir) break;
37083
37238
  dir = parent;
37084
37239
  }
@@ -37156,7 +37311,7 @@ async function runAdapters(ctx) {
37156
37311
  for (const [target, count2] of byTarget) lines.push(` ${target}: ${count2} archivo(s)`);
37157
37312
  if (report.written.length > 0) {
37158
37313
  lines.push("");
37159
- for (const p of report.written) lines.push(` + ${path31.relative(ctx.cwd, path31.join(root, p))}`);
37314
+ for (const p of report.written) lines.push(` + ${path32.relative(ctx.cwd, path32.join(root, p))}`);
37160
37315
  } else {
37161
37316
  lines.push("");
37162
37317
  lines.push("Sin cambios: los adaptadores ya estaban al d\xEDa.");
@@ -37167,7 +37322,7 @@ async function runAdapters(ctx) {
37167
37322
  }
37168
37323
 
37169
37324
  // src/commands/adopt.ts
37170
- import path32 from "path";
37325
+ import path33 from "path";
37171
37326
  async function runAdopt(ctx) {
37172
37327
  const { root } = await requireWorkspace(ctx);
37173
37328
  const domainsFlag = flagString(ctx.flags, "domains") ?? flagString(ctx.flags, "domain");
@@ -37187,11 +37342,11 @@ async function runAdopt(ctx) {
37187
37342
  }
37188
37343
  if (result.createdSpecs.length > 0) {
37189
37344
  lines.push("");
37190
- for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path32.relative(ctx.cwd, spec)}`);
37345
+ for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path33.relative(ctx.cwd, spec)}`);
37191
37346
  }
37192
37347
  if (result.reportPath && !result.dryRun) {
37193
37348
  lines.push("");
37194
- lines.push(` Informe: ${path32.relative(ctx.cwd, result.reportPath)}`);
37349
+ lines.push(` Informe: ${path33.relative(ctx.cwd, result.reportPath)}`);
37195
37350
  }
37196
37351
  lines.push("");
37197
37352
  lines.push("Siguiente: fase /satlas-adopt por dominio (requisitos AS-IS) y luego:");
@@ -37204,8 +37359,8 @@ async function runAdopt(ctx) {
37204
37359
  diagnostics: result.diagnostics,
37205
37360
  data: {
37206
37361
  domains: result.domains.map((d) => ({ name: d.name, files: d.files, existingSpec: d.existingSpec })),
37207
- createdSpecs: result.createdSpecs.map((s) => path32.relative(ctx.cwd, s)),
37208
- reportPath: result.reportPath ? path32.relative(ctx.cwd, result.reportPath) : void 0,
37362
+ createdSpecs: result.createdSpecs.map((s) => path33.relative(ctx.cwd, s)),
37363
+ reportPath: result.reportPath ? path33.relative(ctx.cwd, result.reportPath) : void 0,
37209
37364
  stack: result.stack,
37210
37365
  dryRun: result.dryRun
37211
37366
  },
@@ -37254,7 +37409,7 @@ async function runAmend(ctx) {
37254
37409
  }
37255
37410
 
37256
37411
  // src/commands/analyze.ts
37257
- import path33 from "path";
37412
+ import path34 from "path";
37258
37413
  async function runAnalyzeCommand(ctx) {
37259
37414
  const { root } = await requireWorkspace(ctx);
37260
37415
  const slug = ctx.positionals[0];
@@ -37265,23 +37420,23 @@ async function runAnalyzeCommand(ctx) {
37265
37420
  const lines = [`An\xE1lisis \u2014 ${slug}`, "", ` estado: ${result.status}`, ` hallazgos: ${result.summary.errors} errores, ${result.summary.warnings} avisos`, ` evidencia: ${result.evidence.done}/${result.evidence.total} escenarios`];
37266
37421
  if (result.waves) lines.push(` olas: ${result.waves.blocks} bloque(s), ${result.waves.waves} ola(s), ${result.waves.tasks} tarea(s)`);
37267
37422
  if (result.mockups) lines.push(` mockups: ${result.mockups.screens} pantalla(s)${result.mockups.stale ? " (desactualizados)" : ""}`);
37268
- if (result.path) lines.push("", ` informe: ${path33.relative(ctx.cwd, result.path)}`);
37423
+ if (result.path) lines.push("", ` informe: ${path34.relative(ctx.cwd, result.path)}`);
37269
37424
  for (const finding of result.findings) {
37270
37425
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}`);
37271
37426
  }
37272
37427
  return {
37273
37428
  exitCode: result.status === "blocked" ? 1 : 0,
37274
37429
  diagnostics: result.findings,
37275
- data: result.path ? { ...result, path: path33.relative(ctx.cwd, result.path) } : result,
37430
+ data: result.path ? { ...result, path: path34.relative(ctx.cwd, result.path) } : result,
37276
37431
  text: lines
37277
37432
  };
37278
37433
  }
37279
37434
 
37280
37435
  // src/commands/approve.ts
37281
- import path35 from "path";
37436
+ import path36 from "path";
37282
37437
 
37283
37438
  // src/commands/issue.ts
37284
- import path34 from "path";
37439
+ import path35 from "path";
37285
37440
  async function runIssue(ctx) {
37286
37441
  const action = ctx.positionals[0] ?? "status";
37287
37442
  const slug = ctx.positionals[1];
@@ -37309,7 +37464,7 @@ async function runIssue(ctx) {
37309
37464
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-ISSUE-000", severity: "error", message: "Falta el slug: satlas issue status <slug>" }] };
37310
37465
  }
37311
37466
  const change = await loadChange(root, slug);
37312
- const { config } = await loadConfig(path34.join(root, ".sdd"));
37467
+ const { config } = await loadConfig(path35.join(root, ".sdd"));
37313
37468
  const tracker = change.meta?.tracker;
37314
37469
  const lines = [`Issue de ${slug}`, ""];
37315
37470
  if (!tracker || tracker.provider !== "github") {
@@ -37380,7 +37535,7 @@ async function runApprove(ctx) {
37380
37535
  };
37381
37536
  }
37382
37537
  const { root, workspace, config } = await requireWorkspace(ctx);
37383
- const artifact = target.includes("/") || target.includes("\\") ? target : path35.join("changes", target, "spec.md");
37538
+ const artifact = target.includes("/") || target.includes("\\") ? target : path36.join("changes", target, "spec.md");
37384
37539
  const channelFlag = flagString(ctx.flags, "channel");
37385
37540
  const channel = channelFlag === "presentation" || channelFlag === "editor" || channelFlag === "pr" || channelFlag === "tracker" ? channelFlag : "cli";
37386
37541
  const change = workspace.changes.find(
@@ -37416,18 +37571,30 @@ async function runApprove(ctx) {
37416
37571
  lines.push(` fecha: ${result.approval.approvedAt}`);
37417
37572
  lines.push("");
37418
37573
  lines.push("La firma desbloquea el plan. Si el artefacto cambia, la firma queda obsoleta autom\xE1ticamente.");
37574
+ const slug = change?.slug;
37575
+ if (slug && !flagBool(ctx.flags, "dry-run")) {
37576
+ const presentation = path36.join(root, ".sdd", "changes", slug, "presentation", "index.html");
37577
+ if (await exists(presentation)) {
37578
+ try {
37579
+ await generatePresentation({ root, slug });
37580
+ lines.push(`Propuesta actualizada con la firma: ${path36.relative(ctx.cwd, presentation)}`);
37581
+ } catch (error2) {
37582
+ lines.push(`La propuesta no se pudo rehacer (${error2.message}); vuelve a generarla con \`satlas present ${slug}\`.`);
37583
+ }
37584
+ }
37585
+ }
37419
37586
  }
37420
37587
  const hasErrors2 = result.diagnostics.some((d) => d.severity === "error");
37421
37588
  return {
37422
37589
  exitCode: hasErrors2 ? 1 : 0,
37423
37590
  diagnostics: result.diagnostics,
37424
- data: result.approval ? { ...result.approval, file: path35.relative(ctx.cwd, result.file) } : void 0,
37591
+ data: result.approval ? { ...result.approval, file: path36.relative(ctx.cwd, result.file) } : void 0,
37425
37592
  text: lines
37426
37593
  };
37427
37594
  }
37428
37595
 
37429
37596
  // src/commands/archive.ts
37430
- import path36 from "path";
37597
+ import path37 from "path";
37431
37598
  async function runArchive(ctx) {
37432
37599
  const slug = ctx.positionals[0];
37433
37600
  if (!slug) {
@@ -37464,7 +37631,7 @@ async function runArchive(ctx) {
37464
37631
  } else {
37465
37632
  lines.push("El delta no contiene operaciones (ADDED/MODIFIED/REMOVED/RENAMED).");
37466
37633
  }
37467
- if (result.archivedTo) lines.push(` archivado en: ${path36.relative(ctx.cwd, result.archivedTo)}`);
37634
+ if (result.archivedTo) lines.push(` archivado en: ${path37.relative(ctx.cwd, result.archivedTo)}`);
37468
37635
  const hasErrors2 = result.diagnostics.some((d) => d.severity === "error");
37469
37636
  return {
37470
37637
  exitCode: hasErrors2 ? 1 : 0,
@@ -37475,7 +37642,7 @@ async function runArchive(ctx) {
37475
37642
  }
37476
37643
 
37477
37644
  // src/commands/ci.ts
37478
- import path37 from "path";
37645
+ import path38 from "path";
37479
37646
  async function runCi(ctx) {
37480
37647
  const { root, config } = await requireWorkspace(ctx);
37481
37648
  const strict = flagBool(ctx.flags, "strict");
@@ -37501,10 +37668,10 @@ async function runCi(ctx) {
37501
37668
  const lines = ["CI de SpecAtlas", ""];
37502
37669
  let sarifWritten;
37503
37670
  if (sarifPath) {
37504
- const target = path37.resolve(ctx.cwd, sarifPath);
37671
+ const target = path38.resolve(ctx.cwd, sarifPath);
37505
37672
  try {
37506
37673
  await writeText(target, toSarifText({ diagnostics: gate.diagnostics, root, version: cliVersion(), failed: gate.failed }));
37507
- sarifWritten = path37.relative(ctx.cwd, target);
37674
+ sarifWritten = path38.relative(ctx.cwd, target);
37508
37675
  } catch (err) {
37509
37676
  diagnostics.push({
37510
37677
  code: "ATLAS-CI-SARIF-001",
@@ -37581,7 +37748,7 @@ async function runClarify(ctx) {
37581
37748
  }
37582
37749
 
37583
37750
  // src/commands/contracts.ts
37584
- import path38 from "path";
37751
+ import path39 from "path";
37585
37752
  async function runContracts(ctx) {
37586
37753
  const slug = ctx.positionals[0];
37587
37754
  if (!slug) {
@@ -37601,7 +37768,7 @@ async function runContracts(ctx) {
37601
37768
  const coverage = contractCoverage(change, config.gates.contracts.mode);
37602
37769
  const findings = [...state.findings, ...coverage];
37603
37770
  for (const file of state.files) {
37604
- lines.push(` ${path38.relative(ctx.cwd, file.path)} (${file.format}) \u2014 ${file.operations.length} operaci\xF3n(es)`);
37771
+ lines.push(` ${path39.relative(ctx.cwd, file.path)} (${file.format}) \u2014 ${file.operations.length} operaci\xF3n(es)`);
37605
37772
  for (const operation of file.operations) lines.push(` ${operation.id}`);
37606
37773
  }
37607
37774
  lines.push("");
@@ -37617,7 +37784,7 @@ async function runContracts(ctx) {
37617
37784
  data: {
37618
37785
  slug,
37619
37786
  mode: config.gates.contracts.mode,
37620
- contracts: state.files.map((file) => ({ path: path38.relative(ctx.cwd, file.path), format: file.format, operations: file.operations.map((operation) => operation.id) })),
37787
+ contracts: state.files.map((file) => ({ path: path39.relative(ctx.cwd, file.path), format: file.format, operations: file.operations.map((operation) => operation.id) })),
37621
37788
  findings
37622
37789
  },
37623
37790
  text: lines
@@ -37625,7 +37792,7 @@ async function runContracts(ctx) {
37625
37792
  }
37626
37793
 
37627
37794
  // src/commands/docs.ts
37628
- import path39 from "path";
37795
+ import path40 from "path";
37629
37796
  async function runDocs(ctx) {
37630
37797
  const slug = ctx.positionals[0];
37631
37798
  if (!slug) {
@@ -37639,9 +37806,9 @@ async function runDocs(ctx) {
37639
37806
  const warnings = result.diagnostics.filter((d) => d.severity === "warning");
37640
37807
  const lines = [msg("docs.title", ctx.language), ""];
37641
37808
  for (const file of result.files) {
37642
- lines.push(` ${file.created ? msg("docs.created", ctx.language) : msg("docs.updated", ctx.language)}: ${path39.relative(ctx.cwd, file.path)}`);
37643
- if (file.htmlPath) lines.push(` html: ${path39.relative(ctx.cwd, file.htmlPath)}`);
37644
- if (file.pdfPath) lines.push(` pdf: ${path39.relative(ctx.cwd, file.pdfPath)}`);
37809
+ lines.push(` ${file.created ? msg("docs.created", ctx.language) : msg("docs.updated", ctx.language)}: ${path40.relative(ctx.cwd, file.path)}`);
37810
+ if (file.htmlPath) lines.push(` html: ${path40.relative(ctx.cwd, file.htmlPath)}`);
37811
+ if (file.pdfPath) lines.push(` pdf: ${path40.relative(ctx.cwd, file.pdfPath)}`);
37645
37812
  }
37646
37813
  for (const warning of warnings) lines.push(` AVISO ${warning.code} \u2014 ${warning.message}`);
37647
37814
  for (const error2 of errors) lines.push(` ERROR ${error2.code} \u2014 ${error2.message}`);
@@ -37653,10 +37820,10 @@ async function runDocs(ctx) {
37653
37820
  tipo,
37654
37821
  files: result.files.map((file) => ({
37655
37822
  tipo: file.tipo,
37656
- path: path39.relative(ctx.cwd, file.path),
37823
+ path: path40.relative(ctx.cwd, file.path),
37657
37824
  created: file.created,
37658
- html: file.htmlPath ? path39.relative(ctx.cwd, file.htmlPath) : void 0,
37659
- pdf: file.pdfPath ? path39.relative(ctx.cwd, file.pdfPath) : void 0
37825
+ html: file.htmlPath ? path40.relative(ctx.cwd, file.htmlPath) : void 0,
37826
+ pdf: file.pdfPath ? path40.relative(ctx.cwd, file.pdfPath) : void 0
37660
37827
  }))
37661
37828
  },
37662
37829
  text: lines
@@ -37664,7 +37831,7 @@ async function runDocs(ctx) {
37664
37831
  }
37665
37832
 
37666
37833
  // src/commands/drift.ts
37667
- import path40 from "path";
37834
+ import path41 from "path";
37668
37835
  async function runDrift(ctx) {
37669
37836
  const { root, config } = await requireWorkspace(ctx);
37670
37837
  const anchors = await loadAllAnchors(root);
@@ -37708,7 +37875,7 @@ async function runDrift(ctx) {
37708
37875
  mode: report.mode,
37709
37876
  domains: report.domains,
37710
37877
  checked: report.checked,
37711
- drifted: report.drifted.map((item) => ({ ...item, specPath: path40.relative(ctx.cwd, item.specPath) }))
37878
+ drifted: report.drifted.map((item) => ({ ...item, specPath: path41.relative(ctx.cwd, item.specPath) }))
37712
37879
  },
37713
37880
  text: lines
37714
37881
  };
@@ -37759,7 +37926,7 @@ async function runImpact(ctx) {
37759
37926
  }
37760
37927
 
37761
37928
  // src/commands/link.ts
37762
- import path41 from "path";
37929
+ import path42 from "path";
37763
37930
  async function runLink(ctx) {
37764
37931
  const { root } = await requireWorkspace(ctx);
37765
37932
  const action = ctx.positionals[0] ?? "list";
@@ -37771,7 +37938,7 @@ async function runLink(ctx) {
37771
37938
  } else {
37772
37939
  for (const entry of state.entries) {
37773
37940
  const status = entry.available ? `${entry.requirements} requisito(s) \xB7 ${entry.domains.join(", ") || "sin dominios"}` : "NO DISPONIBLE";
37774
- lines.push(` ${entry.name} ${path41.relative(ctx.cwd, entry.path)} ${status}`);
37941
+ lines.push(` ${entry.name} ${path42.relative(ctx.cwd, entry.path)} ${status}`);
37775
37942
  }
37776
37943
  }
37777
37944
  return {
@@ -37780,7 +37947,7 @@ async function runLink(ctx) {
37780
37947
  data: {
37781
37948
  links: state.entries.map((entry) => ({
37782
37949
  name: entry.name,
37783
- path: path41.relative(ctx.cwd, entry.path),
37950
+ path: path42.relative(ctx.cwd, entry.path),
37784
37951
  available: entry.available,
37785
37952
  requirements: entry.requirements,
37786
37953
  domains: entry.domains
@@ -37825,12 +37992,12 @@ async function runLink(ctx) {
37825
37992
  }
37826
37993
 
37827
37994
  // src/commands/profile.ts
37828
- import path42 from "path";
37995
+ import path43 from "path";
37829
37996
  async function runProfile(ctx) {
37830
37997
  const action = ctx.positionals[0] ?? "detect";
37831
37998
  const { root, workspace } = await requireWorkspace(ctx);
37832
37999
  const officialDir = await resolveProfilesDir();
37833
- const customDir = path42.join(workspace.sddDir, "profiles", "custom");
38000
+ const customDir = path43.join(workspace.sddDir, "profiles", "custom");
37834
38001
  const profiles = [...officialDir ? await loadProfilesFromDir(officialDir) : [], ...await loadProfilesFromDir(customDir)];
37835
38002
  if (action === "detect") {
37836
38003
  const result = await detectProfiles(root, profiles);
@@ -37859,7 +38026,7 @@ async function runProfile(ctx) {
37859
38026
  if (!name) {
37860
38027
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-001", severity: "error", message: "Falta el nombre del perfil: satlas profile create <nombre>" }] };
37861
38028
  }
37862
- const file = path42.join(customDir, `${name}.yaml`);
38029
+ const file = path43.join(customDir, `${name}.yaml`);
37863
38030
  if (await exists(file)) {
37864
38031
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-002", severity: "error", message: `El perfil ya existe: ${file}` }] };
37865
38032
  }
@@ -37868,7 +38035,7 @@ async function runProfile(ctx) {
37868
38035
  exitCode: 0,
37869
38036
  diagnostics: [],
37870
38037
  data: { file },
37871
- text: [`Perfil creado: ${path42.relative(ctx.cwd, file)}`, "", "Ed\xEDtalo con los comandos reales de tu stack (build/lint/test) y tus anti-patrones."]
38038
+ text: [`Perfil creado: ${path43.relative(ctx.cwd, file)}`, "", "Ed\xEDtalo con los comandos reales de tu stack (build/lint/test) y tus anti-patrones."]
37872
38039
  };
37873
38040
  }
37874
38041
  return {
@@ -37877,7 +38044,7 @@ async function runProfile(ctx) {
37877
38044
  };
37878
38045
  }
37879
38046
  async function isInside(dir, name) {
37880
- return exists(path42.join(dir, `${name}.yaml`));
38047
+ return exists(path43.join(dir, `${name}.yaml`));
37881
38048
  }
37882
38049
  function profileTemplate(name) {
37883
38050
  return `name: ${name}
@@ -37905,7 +38072,7 @@ assumptions: []
37905
38072
  }
37906
38073
 
37907
38074
  // src/commands/doctor.ts
37908
- import path43 from "path";
38075
+ import path44 from "path";
37909
38076
  async function runDoctorCommand(ctx) {
37910
38077
  const { root, config } = await requireWorkspace(ctx);
37911
38078
  const report = await runDoctor(root);
@@ -37930,7 +38097,7 @@ async function runDoctorCommand(ctx) {
37930
38097
  lines.push("Sin hallazgos. El workspace est\xE1 sano.");
37931
38098
  } else {
37932
38099
  for (const finding of report.findings) {
37933
- const location = finding.path ? ` ${path43.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
38100
+ const location = finding.path ? ` ${path44.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
37934
38101
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${location} \u2014 ${finding.message}`);
37935
38102
  if (finding.suggestion) lines.push(` \u21B3 ${finding.suggestion}`);
37936
38103
  }
@@ -38041,7 +38208,7 @@ async function runHelp(ctx, commandName) {
38041
38208
  }
38042
38209
 
38043
38210
  // src/commands/init.ts
38044
- import path44 from "path";
38211
+ import path45 from "path";
38045
38212
  async function runInit(ctx) {
38046
38213
  const languageFlag = flagString(ctx.flags, "language");
38047
38214
  const language = languageFlag === "en" ? "en" : languageFlag === "es" ? "es" : void 0;
@@ -38091,11 +38258,11 @@ async function runInit(ctx) {
38091
38258
  const lines = [msg("init.title", ctx.language), ""];
38092
38259
  if (result.created.length > 0) {
38093
38260
  lines.push(msg("init.done", ctx.language));
38094
- for (const file of result.created) lines.push(` + ${path44.relative(ctx.cwd, file)}`);
38261
+ for (const file of result.created) lines.push(` + ${path45.relative(ctx.cwd, file)}`);
38095
38262
  if (compiled.length > 0) {
38096
38263
  lines.push("");
38097
38264
  lines.push("Adaptadores compilados:");
38098
- for (const file of compiled) lines.push(` + ${path44.relative(ctx.cwd, path44.join(ctx.cwd, file))}`);
38265
+ for (const file of compiled) lines.push(` + ${path45.relative(ctx.cwd, path45.join(ctx.cwd, file))}`);
38099
38266
  }
38100
38267
  lines.push("");
38101
38268
  lines.push("Siguiente: crea un cambio con `satlas new <slug>` y escribe la spec funcional.");
@@ -38105,8 +38272,8 @@ async function runInit(ctx) {
38105
38272
  exitCode: hasErrors2 ? 1 : 0,
38106
38273
  diagnostics: result.diagnostics,
38107
38274
  data: {
38108
- sddDir: path44.relative(ctx.cwd, result.sddDir),
38109
- created: result.created.map((f) => path44.relative(ctx.cwd, f)),
38275
+ sddDir: path45.relative(ctx.cwd, result.sddDir),
38276
+ created: result.created.map((f) => path45.relative(ctx.cwd, f)),
38110
38277
  detected: result.detected?.matches.map((m) => ({ name: m.name, score: m.score })),
38111
38278
  best: result.detected?.best?.name
38112
38279
  },
@@ -38147,7 +38314,7 @@ async function runMetrics(ctx) {
38147
38314
  }
38148
38315
 
38149
38316
  // src/commands/mockup.ts
38150
- import path45 from "path";
38317
+ import path46 from "path";
38151
38318
  async function runMockup(ctx) {
38152
38319
  const { root, workspace, config } = await requireWorkspace(ctx);
38153
38320
  const slug = ctx.positionals[0];
@@ -38197,17 +38364,17 @@ async function runMockup(ctx) {
38197
38364
  ` pantallas: ${plan.screens.length}`,
38198
38365
  ...plan.screens.map((s) => ` - ${s.id}: ${s.title} (${s.illustrates.length} escenario(s))`),
38199
38366
  "",
38200
- ` plan: ${path45.relative(ctx.cwd, planFile)}`,
38201
- ` manifiesto: ${path45.relative(ctx.cwd, manifestFile)}`,
38367
+ ` plan: ${path46.relative(ctx.cwd, planFile)}`,
38368
+ ` manifiesto: ${path46.relative(ctx.cwd, manifestFile)}`,
38202
38369
  "",
38203
38370
  `Siguiente: genera el HTML de cada pantalla (fase ${agentCommand("mockup", slug, config)}) y valida con \`satlas mockup ${slug} --check\`.`
38204
38371
  ];
38205
- return { exitCode: 0, diagnostics: [], data: { plan, planFile: path45.relative(ctx.cwd, planFile), manifestFile: path45.relative(ctx.cwd, manifestFile) }, text: lines };
38372
+ return { exitCode: 0, diagnostics: [], data: { plan, planFile: path46.relative(ctx.cwd, planFile), manifestFile: path46.relative(ctx.cwd, manifestFile) }, text: lines };
38206
38373
  }
38207
38374
 
38208
38375
  // src/mcp/server.ts
38209
38376
  import { promises as fs3 } from "fs";
38210
- import path50 from "path";
38377
+ import path51 from "path";
38211
38378
  import { createInterface } from "readline/promises";
38212
38379
 
38213
38380
  // src/mcp/protocol.ts
@@ -38269,7 +38436,7 @@ function stringArg(args, key) {
38269
38436
  }
38270
38437
 
38271
38438
  // src/mcp/tools/contracts.ts
38272
- import path46 from "path";
38439
+ import path47 from "path";
38273
38440
  async function runAtlasContracts(args, host) {
38274
38441
  const resolved = await host.getWorkspace();
38275
38442
  if (!resolved) return noWorkspaceResult();
@@ -38289,7 +38456,7 @@ async function runAtlasContracts(args, host) {
38289
38456
  slug,
38290
38457
  mode,
38291
38458
  contracts: state.files.map((file) => ({
38292
- path: path46.relative(resolved.root, file.path),
38459
+ path: path47.relative(resolved.root, file.path),
38293
38460
  format: file.format,
38294
38461
  operations: file.operations.map((operation) => operation.id)
38295
38462
  })),
@@ -38325,12 +38492,12 @@ async function runAtlasFixes(_args, host) {
38325
38492
  }
38326
38493
 
38327
38494
  // src/mcp/tools/glossary.ts
38328
- import path47 from "path";
38495
+ import path48 from "path";
38329
38496
  async function runAtlasGlossary(_args, host) {
38330
38497
  const resolved = await host.getWorkspace();
38331
38498
  if (!resolved) return noWorkspaceResult();
38332
38499
  const { root, config } = resolved;
38333
- const glossaryPath = path47.resolve(root, config.spec.glossary);
38500
+ const glossaryPath = path48.resolve(root, config.spec.glossary);
38334
38501
  const raw = await readTextIfExists(glossaryPath);
38335
38502
  if (raw === void 0) {
38336
38503
  return jsonResult({
@@ -38351,7 +38518,7 @@ async function runAtlasGlossary(_args, host) {
38351
38518
  }
38352
38519
 
38353
38520
  // src/mcp/tools/links.ts
38354
- import path48 from "path";
38521
+ import path49 from "path";
38355
38522
  async function runAtlasLinks(_args, host) {
38356
38523
  const resolved = await host.getWorkspace();
38357
38524
  if (!resolved) return noWorkspaceResult();
@@ -38362,7 +38529,7 @@ async function runAtlasLinks(_args, host) {
38362
38529
  return jsonResult({
38363
38530
  links: state.entries.map((entry) => ({
38364
38531
  name: entry.name,
38365
- path: path48.relative(resolved.root, entry.path),
38532
+ path: path49.relative(resolved.root, entry.path),
38366
38533
  available: entry.available,
38367
38534
  requirements: entry.requirements,
38368
38535
  domains: entry.domains
@@ -38399,7 +38566,7 @@ async function runAtlasImpact(args, host) {
38399
38566
  }
38400
38567
 
38401
38568
  // src/evaluate.ts
38402
- import path49 from "path";
38569
+ import path50 from "path";
38403
38570
  function livingRequirementsMap2(specs) {
38404
38571
  const map = /* @__PURE__ */ new Map();
38405
38572
  for (const spec of specs) {
@@ -38408,7 +38575,7 @@ function livingRequirementsMap2(specs) {
38408
38575
  return map;
38409
38576
  }
38410
38577
  async function evaluateChange(workspace, config, change, approvals) {
38411
- const deltaPath = path49.join(change.dir, "spec.md");
38578
+ const deltaPath = path50.join(change.dir, "spec.md");
38412
38579
  const deltaContent = await readTextIfExists(deltaPath);
38413
38580
  const approval = verifyApproval(change, approvals, config, deltaContent ?? void 0);
38414
38581
  const living = livingRequirementsMap2(workspace.specs);
@@ -38704,8 +38871,8 @@ var WorkspaceCache = class {
38704
38871
  }
38705
38872
  };
38706
38873
  async function workspaceStamp(root) {
38707
- const sddDir = path50.join(root, ".sdd");
38708
- const markers = [sddDir, path50.join(sddDir, "config.yaml"), path50.join(sddDir, "changes"), path50.join(sddDir, "specs"), path50.join(sddDir, "approvals.yaml")];
38874
+ const sddDir = path51.join(root, ".sdd");
38875
+ const markers = [sddDir, path51.join(sddDir, "config.yaml"), path51.join(sddDir, "changes"), path51.join(sddDir, "specs"), path51.join(sddDir, "approvals.yaml")];
38709
38876
  let stamp2 = 0;
38710
38877
  for (const marker of markers) {
38711
38878
  try {
@@ -38723,7 +38890,7 @@ async function resolveMcpWorkspace(cwd, cache) {
38723
38890
  const cached2 = cache.get(root);
38724
38891
  if (cached2 && cached2.mtimeMs === stamp2) return cached2.value;
38725
38892
  const { workspace, config } = await loadWorkspace(root);
38726
- const approvals = await loadApprovals(path50.join(root, ".sdd"));
38893
+ const approvals = await loadApprovals(path51.join(root, ".sdd"));
38727
38894
  const value = { root, workspace, config, approvals: approvals.byArtifact };
38728
38895
  cache.set(root, stamp2, value);
38729
38896
  return value;
@@ -38815,7 +38982,7 @@ async function runMcp(ctx) {
38815
38982
  }
38816
38983
 
38817
38984
  // src/commands/packs.ts
38818
- import path51 from "path";
38985
+ import path52 from "path";
38819
38986
  async function runPacks(ctx) {
38820
38987
  const { root, config, workspace } = await requireWorkspace(ctx);
38821
38988
  const slug = flagString(ctx.flags, "check");
@@ -38873,14 +39040,14 @@ async function runPacks(ctx) {
38873
39040
  data: {
38874
39041
  slug,
38875
39042
  packs: evaluations.map((evaluation) => ({ id: evaluation.pack.id, status: evaluation.status, passed: evaluation.passed, failed: evaluation.failed, results: evaluation.results })),
38876
- analyzePath: path51.posix.join(".sdd", "changes", slug, "analyze.md")
39043
+ analyzePath: path52.posix.join(".sdd", "changes", slug, "analyze.md")
38877
39044
  },
38878
39045
  text: lines
38879
39046
  };
38880
39047
  }
38881
39048
 
38882
39049
  // src/commands/pause.ts
38883
- import path52 from "path";
39050
+ import path53 from "path";
38884
39051
  async function runPause(ctx) {
38885
39052
  const slug = ctx.positionals[0];
38886
39053
  if (!slug) {
@@ -38910,7 +39077,7 @@ async function runPause(ctx) {
38910
39077
  ` motivo: ${result.paused?.reason ?? ""}`,
38911
39078
  ` por: ${result.paused?.by ?? ""}`,
38912
39079
  ` fecha: ${result.paused?.at ?? ""}`,
38913
- ` estado: ${path52.relative(ctx.cwd, result.path)}`,
39080
+ ` estado: ${path53.relative(ctx.cwd, result.path)}`,
38914
39081
  "",
38915
39082
  `Ret\xF3malo cuando quieras con \`satlas resume ${slug}\`.`
38916
39083
  ];
@@ -38953,7 +39120,7 @@ async function runResume(ctx) {
38953
39120
  }
38954
39121
 
38955
39122
  // src/commands/new.ts
38956
- import path53 from "path";
39123
+ import path54 from "path";
38957
39124
  async function runNew(ctx) {
38958
39125
  const slug = ctx.positionals[0];
38959
39126
  if (!slug) {
@@ -38979,14 +39146,14 @@ async function runNew(ctx) {
38979
39146
  msg("new.title", ctx.language),
38980
39147
  "",
38981
39148
  msg("new.done", ctx.language),
38982
- ...result.files.map((f) => ` + ${path53.relative(ctx.cwd, f)}`),
39149
+ ...result.files.map((f) => ` + ${path54.relative(ctx.cwd, f)}`),
38983
39150
  "",
38984
39151
  `Siguiente: ${agentCommand("specify", result.slug, config)} \u2014 escribe la especificaci\xF3n 100% funcional y de negocio.`
38985
39152
  ];
38986
39153
  return {
38987
39154
  exitCode: hasErrors2 ? 1 : 0,
38988
39155
  diagnostics: result.diagnostics,
38989
- data: { slug: result.slug, files: result.files.map((f) => path53.relative(ctx.cwd, f)) },
39156
+ data: { slug: result.slug, files: result.files.map((f) => path54.relative(ctx.cwd, f)) },
38990
39157
  text: lines
38991
39158
  };
38992
39159
  }
@@ -39119,7 +39286,7 @@ async function runNext(ctx) {
39119
39286
  }
39120
39287
 
39121
39288
  // src/commands/present.ts
39122
- import path54 from "path";
39289
+ import path55 from "path";
39123
39290
  async function runPresentCommand(ctx) {
39124
39291
  const { root } = await requireWorkspace(ctx);
39125
39292
  const slug = ctx.positionals[0];
@@ -39131,7 +39298,7 @@ async function runPresentCommand(ctx) {
39131
39298
  if (result.path) {
39132
39299
  lines.push("Propuesta generada");
39133
39300
  lines.push("");
39134
- lines.push(` archivo: ${path54.relative(ctx.cwd, result.path)}`);
39301
+ lines.push(` archivo: ${path55.relative(ctx.cwd, result.path)}`);
39135
39302
  if (result.hash) lines.push(` hash de la spec: ${shortHash(result.hash)}`);
39136
39303
  lines.push("");
39137
39304
  lines.push("\xC1brela en el navegador y comp\xE1rtela con el stakeholder.");
@@ -39141,13 +39308,13 @@ async function runPresentCommand(ctx) {
39141
39308
  return {
39142
39309
  exitCode: hasErrors2 ? 1 : 0,
39143
39310
  diagnostics: result.diagnostics,
39144
- data: result.path ? { path: path54.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
39311
+ data: result.path ? { path: path55.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
39145
39312
  text: lines
39146
39313
  };
39147
39314
  }
39148
39315
 
39149
39316
  // src/commands/review.ts
39150
- import path55 from "path";
39317
+ import path56 from "path";
39151
39318
  async function runReview(ctx) {
39152
39319
  const slug = ctx.positionals[0];
39153
39320
  if (!slug) {
@@ -39159,7 +39326,7 @@ async function runReview(ctx) {
39159
39326
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-REVIEW-000", severity: "error", message: `No existe el cambio "${slug}"` }] };
39160
39327
  }
39161
39328
  const mode = config.gates.review.mode;
39162
- const reviewFile = path55.join(change.dir, "review.md");
39329
+ const reviewFile = path56.join(change.dir, "review.md");
39163
39330
  if (!await exists(reviewFile)) {
39164
39331
  const templates = templatesFor(config.project.language);
39165
39332
  await writeText(reviewFile, renderTemplate(templates.review, { TITLE: change.meta?.title ?? slug, SLUG: slug }));
@@ -39167,7 +39334,7 @@ async function runReview(ctx) {
39167
39334
  "Revisi\xF3n del cambio",
39168
39335
  "",
39169
39336
  ` modo: ${mode}`,
39170
- ` creado: ${path55.relative(ctx.cwd, reviewFile)}`,
39337
+ ` creado: ${path56.relative(ctx.cwd, reviewFile)}`,
39171
39338
  "",
39172
39339
  `Revisa el c\xF3digo con la fase del agente: ${agentCommand("review", slug, config)}`,
39173
39340
  "Al cerrar, deja `- resultado: pass` en el veredicto y marca los hallazgos resueltos."
@@ -39180,7 +39347,7 @@ async function runReview(ctx) {
39180
39347
  const passed = reviewPassed(review);
39181
39348
  const lines = ["Revisi\xF3n del cambio", ""];
39182
39349
  lines.push(` modo: ${mode}`);
39183
- lines.push(` archivo: ${path55.relative(ctx.cwd, reviewFile)}`);
39350
+ lines.push(` archivo: ${path56.relative(ctx.cwd, reviewFile)}`);
39184
39351
  lines.push(` resultado: ${review?.verdict ?? "pendiente"}${review?.by ? ` \xB7 por ${review.by}` : ""}${review?.date ? ` \xB7 ${review.date}` : ""}`);
39185
39352
  lines.push(` hallazgos: ${review?.findings.length ?? 0} (${open.length} sin resolver, ${blocking.length} bloqueante(s))`);
39186
39353
  if (open.length > 0) {
@@ -39282,6 +39449,124 @@ function usage(usageText) {
39282
39449
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-RUN-000", severity: "error", message: `Uso: ${usageText}` }] };
39283
39450
  }
39284
39451
 
39452
+ // src/commands/self-update.ts
39453
+ function rutaEjecutable() {
39454
+ return process.argv[1] ?? process.execPath;
39455
+ }
39456
+ async function instalarUltima(instalacion, cwd) {
39457
+ if (!instalacion.comando) return { ok: false, salida: instalacion.motivo ?? "no se puede actualizar esta instalaci\xF3n" };
39458
+ const resultado = await runProcess(instalacion.comando, { cwd, timeoutMs: 18e4 });
39459
+ return { ok: resultado.ok, salida: `${resultado.stdout}${resultado.stderr}`.trim() };
39460
+ }
39461
+ async function runSelfUpdate(ctx) {
39462
+ const instalada = cliVersion();
39463
+ const soloConsultar = flagBool(ctx.flags, "check");
39464
+ const auto = flagString(ctx.flags, "auto");
39465
+ if (auto !== void 0) {
39466
+ const valor = auto.toLowerCase();
39467
+ if (valor !== "on" && valor !== "off") {
39468
+ return {
39469
+ exitCode: 2,
39470
+ diagnostics: [{ code: "ATLAS-SELFUPDATE-000", severity: "error", message: "Valor inv\xE1lido para --auto", suggestion: "satlas self-update --auto on | off" }]
39471
+ };
39472
+ }
39473
+ const preferencias2 = await leerPreferencias();
39474
+ const nuevas = { ...preferencias2, automatica: valor === "on" };
39475
+ const file = await escribirPreferencias(nuevas);
39476
+ return {
39477
+ exitCode: 0,
39478
+ diagnostics: [],
39479
+ data: { automatica: nuevas.automatica, archivo: file },
39480
+ text: [
39481
+ "Actualizaci\xF3n desatendida",
39482
+ "",
39483
+ ` ${nuevas.automatica ? "activada: la herramienta se actualizar\xE1 sola al detectar una versi\xF3n nueva" : "desactivada: solo se avisa cuando hay versi\xF3n nueva"}`,
39484
+ ` preferencia: ${file}`
39485
+ ]
39486
+ };
39487
+ }
39488
+ const publicada = await consultarVersionPublicada();
39489
+ const instalacion = detectarInstalacion(rutaEjecutable());
39490
+ const preferencias = await leerPreferencias();
39491
+ if (publicada === void 0) {
39492
+ return {
39493
+ exitCode: 1,
39494
+ diagnostics: [
39495
+ {
39496
+ code: "ATLAS-SELFUPDATE-001",
39497
+ severity: "error",
39498
+ message: "No se pudo consultar la versi\xF3n publicada",
39499
+ suggestion: `Comprueba tu conexi\xF3n o instala a mano: ${instalacion.comando ?? "npm install -g specatlas@latest"}`
39500
+ }
39501
+ ]
39502
+ };
39503
+ }
39504
+ const base = [
39505
+ "Actualizaci\xF3n de SpecAtlas",
39506
+ "",
39507
+ ` instalada: ${instalada}`,
39508
+ ` publicada: ${publicada}`,
39509
+ ` instalada con: ${instalacion.gestor}`,
39510
+ ` desatendida: ${preferencias.automatica ? "activada" : "desactivada"}`
39511
+ ];
39512
+ if (!hayVersionNueva(instalada, publicada)) {
39513
+ return { exitCode: 0, diagnostics: [], data: { instalada, publicada, actualizada: false, alDia: true }, text: [...base, "", " Ya est\xE1s en la \xFAltima versi\xF3n."] };
39514
+ }
39515
+ if (soloConsultar) {
39516
+ return {
39517
+ exitCode: 0,
39518
+ diagnostics: [],
39519
+ data: { instalada, publicada, actualizada: false, alDia: false, comando: instalacion.comando },
39520
+ text: [...base, "", ` Hay una versi\xF3n nueva. Actualiza con \`satlas self-update\`${instalacion.comando ? ` (o \`${instalacion.comando}\`)` : ""}.`]
39521
+ };
39522
+ }
39523
+ if (!instalacion.comando) {
39524
+ return {
39525
+ exitCode: 0,
39526
+ diagnostics: [{ code: "ATLAS-SELFUPDATE-002", severity: "info", message: instalacion.motivo ?? "esta instalaci\xF3n no se actualiza sola" }],
39527
+ data: { instalada, publicada, actualizada: false, motivo: instalacion.motivo },
39528
+ text: [...base, "", ` ${instalacion.motivo}`]
39529
+ };
39530
+ }
39531
+ const resultado = await instalarUltima(instalacion, ctx.cwd);
39532
+ if (!resultado.ok) {
39533
+ return {
39534
+ exitCode: 1,
39535
+ diagnostics: [
39536
+ {
39537
+ code: "ATLAS-SELFUPDATE-003",
39538
+ severity: "error",
39539
+ message: `No se pudo instalar ${publicada}: ${resultado.salida.split("\n")[0] ?? "el gestor fall\xF3"}`,
39540
+ suggestion: `Ejec\xFAtalo a mano: ${instalacion.comando}`
39541
+ }
39542
+ ],
39543
+ data: { instalada, publicada, actualizada: false, comando: instalacion.comando },
39544
+ text: [...base, "", ` La instalaci\xF3n fall\xF3; sigues con ${instalada}.`]
39545
+ };
39546
+ }
39547
+ return {
39548
+ exitCode: 0,
39549
+ diagnostics: [],
39550
+ data: { instalada, publicada, actualizada: true, comando: instalacion.comando },
39551
+ text: [...base, "", ` Actualizada: ${instalada} \u2192 ${publicada}.`, " Abre una terminal nueva si el comando sigue apuntando a la versi\xF3n anterior."]
39552
+ };
39553
+ }
39554
+ async function avisoDeActualizacion(json2, cwd) {
39555
+ try {
39556
+ const aviso = await comprobarActualizacion({ instalada: cliVersion(), rutaEjecutable: rutaEjecutable(), json: json2 });
39557
+ if (!aviso) return [];
39558
+ const preferencias = await leerPreferencias();
39559
+ if (!preferencias.automatica) {
39560
+ return ["", `Hay una versi\xF3n nueva de SpecAtlas: ${aviso.instalada} \u2192 ${aviso.publicada}. Actualiza con \`satlas self-update\`.`];
39561
+ }
39562
+ const instalacion = detectarInstalacion(rutaEjecutable());
39563
+ const resultado = await instalarUltima(instalacion, cwd);
39564
+ return resultado.ok ? ["", `SpecAtlas se actualiz\xF3 solo: ${aviso.instalada} \u2192 ${aviso.publicada}.`] : ["", `Hay una versi\xF3n nueva (${aviso.publicada}) y la actualizaci\xF3n autom\xE1tica fall\xF3. Ejecuta \`${aviso.comando}\`.`];
39565
+ } catch {
39566
+ return [];
39567
+ }
39568
+ }
39569
+
39285
39570
  // src/commands/status.ts
39286
39571
  async function runStatus(ctx) {
39287
39572
  const { root, workspace, config, approvals } = await requireWorkspace(ctx);
@@ -39356,7 +39641,7 @@ async function runStatus(ctx) {
39356
39641
  }
39357
39642
 
39358
39643
  // src/commands/trace.ts
39359
- import path56 from "path";
39644
+ import path57 from "path";
39360
39645
  async function runTrace(ctx) {
39361
39646
  const { workspace, config } = await requireWorkspace(ctx);
39362
39647
  const slug = flagString(ctx.flags, "change");
@@ -39376,7 +39661,7 @@ async function runTrace(ctx) {
39376
39661
  data.push({ slug: change.slug, nodes: result.graph.nodes.length, edges: result.graph.edges.length, summary: result.summary, findings: result.findings });
39377
39662
  lines.push(` ${change.slug}: ${result.graph.nodes.length} nodos, ${result.graph.edges.length} aristas, ${errors} errores, ${result.summary.warnings} avisos`);
39378
39663
  for (const finding of result.findings) {
39379
- const location = finding.path ? ` (${path56.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
39664
+ const location = finding.path ? ` (${path57.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
39380
39665
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}${location}`);
39381
39666
  }
39382
39667
  }
@@ -39386,7 +39671,7 @@ async function runTrace(ctx) {
39386
39671
  }
39387
39672
 
39388
39673
  // src/commands/upgrade.ts
39389
- import path57 from "path";
39674
+ import path58 from "path";
39390
39675
  async function runUpgrade(ctx) {
39391
39676
  const { root } = await requireWorkspace(ctx);
39392
39677
  const apply2 = flagBool(ctx.flags, "apply");
@@ -39440,7 +39725,7 @@ function renderPreview(plan, root, ctx) {
39440
39725
  }
39441
39726
  for (const item of plan.unreadable) {
39442
39727
  lines.push(` Ilegible: "${item.artifact}" \u2014 ${item.reason}`);
39443
- lines.push(` \u21B3 ${path57.relative(ctx.cwd, item.path)}`);
39728
+ lines.push(` \u21B3 ${path58.relative(ctx.cwd, item.path)}`);
39444
39729
  }
39445
39730
  return { exitCode: 0, diagnostics: [], data: summarizable(plan), text: lines };
39446
39731
  }
@@ -39503,7 +39788,7 @@ function renderRollback(report, ctx) {
39503
39788
  }
39504
39789
 
39505
39790
  // src/commands/validate.ts
39506
- import path58 from "path";
39791
+ import path59 from "path";
39507
39792
  async function runValidate(ctx) {
39508
39793
  const { root, workspace, config, approvals } = await requireWorkspace(ctx);
39509
39794
  const slug = flagString(ctx.flags, "change");
@@ -39530,7 +39815,7 @@ async function runValidate(ctx) {
39530
39815
  for (const finding of evaluation.lintFindings) {
39531
39816
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${finding.line ? `:${finding.line}` : ""} \u2014 ${finding.message}`);
39532
39817
  }
39533
- lines.push(` archivo: ${path58.relative(ctx.cwd, path58.join(change.dir, "spec.md"))}`);
39818
+ lines.push(` archivo: ${path59.relative(ctx.cwd, path59.join(change.dir, "spec.md"))}`);
39534
39819
  }
39535
39820
  if (targets.length === 0) lines.push("Sin cambios activos.");
39536
39821
  if (advisory && advisory.plan.pending.length > 0) {
@@ -39558,7 +39843,7 @@ async function runValidate(ctx) {
39558
39843
  }
39559
39844
 
39560
39845
  // src/commands/verify.ts
39561
- import path59 from "path";
39846
+ import path60 from "path";
39562
39847
  async function runVerify(ctx) {
39563
39848
  const { root, workspace, config } = await requireWorkspace(ctx);
39564
39849
  const slug = ctx.positionals[0];
@@ -39601,7 +39886,7 @@ async function runVerify(ctx) {
39601
39886
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-VERIFY-001", severity: "error", message: "Falta --by <nombre> (o define meta.owner)" }] };
39602
39887
  }
39603
39888
  const profilesDir = await resolveProfilesDir();
39604
- const profile = await loadActiveProfile(path59.join(root, ".sdd"), [profilesDir ?? "", path59.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
39889
+ const profile = await loadActiveProfile(path60.join(root, ".sdd"), [profilesDir ?? "", path60.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
39605
39890
  const record = await recordEvidence({
39606
39891
  root,
39607
39892
  slug,
@@ -39623,7 +39908,7 @@ async function runVerify(ctx) {
39623
39908
  if (record.evidence.command) lines.push(` comando: ${record.evidence.command}`);
39624
39909
  lines.push(` resultado: ${record.evidence.result}`);
39625
39910
  if (record.evidence.outputHash) lines.push(` hash: ${record.evidence.outputHash}`);
39626
- lines.push(` archivo: ${path59.relative(ctx.cwd, record.path)}`);
39911
+ lines.push(` archivo: ${path60.relative(ctx.cwd, record.path)}`);
39627
39912
  if (record.stdout) {
39628
39913
  lines.push("");
39629
39914
  lines.push(" salida (\xFAltimas l\xEDneas):");
@@ -39651,14 +39936,14 @@ async function runVerify(ctx) {
39651
39936
  return {
39652
39937
  exitCode: record.exitCode === 0 ? 0 : 1,
39653
39938
  diagnostics: [...record.diagnostics, ...mockupDiagnostics],
39654
- data: { evidence: record.evidence, path: path59.relative(ctx.cwd, record.path), exitCode: record.exitCode },
39939
+ data: { evidence: record.evidence, path: path60.relative(ctx.cwd, record.path), exitCode: record.exitCode },
39655
39940
  text: lines
39656
39941
  };
39657
39942
  }
39658
39943
 
39659
39944
  // src/commands/watch.ts
39660
39945
  import { watch } from "fs";
39661
- import path60 from "path";
39946
+ import path61 from "path";
39662
39947
  var DEBOUNCE_MS = 250;
39663
39948
  function stamp(now) {
39664
39949
  return now.toTimeString().slice(0, 8);
@@ -39672,7 +39957,7 @@ async function runWatch(ctx) {
39672
39957
  diagnostics: [{ code: "ATLAS-WATCH-001", severity: "error", message: "`satlas watch` es interactivo y no admite --json", suggestion: "Usa `satlas ci --json` en una tuber\xEDa" }]
39673
39958
  };
39674
39959
  }
39675
- const sddDir = path60.join(root, ".sdd");
39960
+ const sddDir = path61.join(root, ".sdd");
39676
39961
  const write = (line) => {
39677
39962
  process.stdout.write(`${line}
39678
39963
  `);
@@ -39690,7 +39975,7 @@ async function runWatch(ctx) {
39690
39975
  } else {
39691
39976
  write(` ${counts.errors} errores, ${counts.warnings} avisos`);
39692
39977
  for (const finding of findings.slice(0, 8)) {
39693
- const where = finding.path ? ` ${path60.relative(root, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
39978
+ const where = finding.path ? ` ${path61.relative(root, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
39694
39979
  write(` ${finding.severity === "error" ? "ERROR" : "AVISO"} ${finding.code}${where} \u2014 ${finding.message}`);
39695
39980
  }
39696
39981
  if (findings.length > 8) write(` \u2026 y ${findings.length - 8} m\xE1s (satlas validate)`);
@@ -39701,7 +39986,7 @@ async function runWatch(ctx) {
39701
39986
  }
39702
39987
  if (changes.length === 0) write(" sin cambios activos");
39703
39988
  };
39704
- write(`Vigilando ${path60.relative(ctx.cwd, sddDir) || ".sdd"} \u2014 Ctrl+C para salir`);
39989
+ write(`Vigilando ${path61.relative(ctx.cwd, sddDir) || ".sdd"} \u2014 Ctrl+C para salir`);
39705
39990
  await pass();
39706
39991
  await new Promise((resolve) => {
39707
39992
  let timer;
@@ -39805,6 +40090,7 @@ var HANDLERS2 = {
39805
40090
  pause: runPause,
39806
40091
  resume: runResume,
39807
40092
  archive: runArchive,
40093
+ "self-update": runSelfUpdate,
39808
40094
  version: async () => ({ exitCode: 0, data: { version: cliVersion() }, diagnostics: [], text: [`specatlas ${cliVersion()}`] }),
39809
40095
  help: (ctx) => runHelp(ctx)
39810
40096
  };
@@ -39834,7 +40120,7 @@ async function requireWorkspace(ctx) {
39834
40120
  throw new CliError("ATLAS-WS-001", msg("cli.noWorkspace", ctx.language), 2);
39835
40121
  }
39836
40122
  const { workspace, config } = await loadWorkspace(root);
39837
- const approvals = await loadApprovals(path61.join(root, ".sdd"));
40123
+ const approvals = await loadApprovals(path62.join(root, ".sdd"));
39838
40124
  return { root, workspace, config, approvals: approvals.byArtifact };
39839
40125
  }
39840
40126
  var CliError = class extends Error {
@@ -39885,6 +40171,11 @@ async function main(argv) {
39885
40171
  try {
39886
40172
  const result = await command.handler(ctx);
39887
40173
  printResult(parsed.command, result, json2, ctx.language);
40174
+ if (parsed.command !== "self-update" && parsed.command !== "mcp") {
40175
+ const aviso = await avisoDeActualizacion(json2, ctx.cwd);
40176
+ if (aviso.length > 0) process.stdout.write(`${aviso.join("\n")}
40177
+ `);
40178
+ }
39888
40179
  return result.exitCode;
39889
40180
  } catch (err) {
39890
40181
  if (err instanceof CliError) {
@@ -39903,7 +40194,7 @@ function printResult(command, result, json2, language) {
39903
40194
  }
39904
40195
  const lines = [...result.text ?? []];
39905
40196
  for (const d of result.diagnostics) {
39906
- const location = d.path ? ` ${path61.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
40197
+ const location = d.path ? ` ${path62.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
39907
40198
  lines.push(`${d.severity === "error" ? "ERROR" : d.severity === "warning" ? "AVISO" : "NOTA"} ${d.code}${location} \u2014 ${d.message}`);
39908
40199
  if (d.suggestion) lines.push(` \u21B3 ${d.suggestion}`);
39909
40200
  }