specatlas 0.1.35 → 0.1.37
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/dist/bin.js +416 -155
- 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
|
|
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(
|
|
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 =
|
|
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(
|
|
23798
|
-
return apply(parse(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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:
|
|
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:
|
|
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,
|
|
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:
|
|
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,
|
|
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",
|
|
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",
|
|
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,
|
|
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:
|
|
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,
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
30892
|
+
path: path302,
|
|
30891
30893
|
suggestion: "Sustituye `;` por `\xB7` o `,` en los mensajes de sequenceDiagram"
|
|
30892
30894
|
})
|
|
30893
30895
|
);
|
|
@@ -35712,6 +35714,140 @@ async function upgradeAdvisory(root) {
|
|
|
35712
35714
|
}
|
|
35713
35715
|
return { plan, diagnostics };
|
|
35714
35716
|
}
|
|
35717
|
+
var PACKAGE_NAME = "specatlas";
|
|
35718
|
+
var REGISTRY = "https://registry.npmjs.org";
|
|
35719
|
+
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
35720
|
+
var TIMEOUT_MS = 2e3;
|
|
35721
|
+
function detectarInstalacion(rutaEjecutable2, plataforma = process.platform) {
|
|
35722
|
+
const ruta = toPosix(rutaEjecutable2).toLowerCase();
|
|
35723
|
+
if (/[/](_npx|\.npm[/]_npx)[/]/.test(ruta) || ruta.includes("/.pnpm-store/") || /[/]dlx-[^/]*[/]/.test(ruta)) {
|
|
35724
|
+
return {
|
|
35725
|
+
gestor: "npx",
|
|
35726
|
+
motivo: "la herramienta se est\xE1 ejecutando sin instalar: cada ejecuci\xF3n ya usa la versi\xF3n publicada"
|
|
35727
|
+
};
|
|
35728
|
+
}
|
|
35729
|
+
if (ruta.includes("/pnpm/global/") || ruta.includes("/.pnpm/global/") || /[/]pnpm[/][^/]*[/]global[/]/.test(ruta)) {
|
|
35730
|
+
return { gestor: "pnpm", comando: `pnpm add -g ${PACKAGE_NAME}@latest` };
|
|
35731
|
+
}
|
|
35732
|
+
if (ruta.includes("/.bun/")) {
|
|
35733
|
+
return { gestor: "bun", comando: `bun add -g ${PACKAGE_NAME}@latest` };
|
|
35734
|
+
}
|
|
35735
|
+
if (ruta.includes("/.yarn/") || ruta.includes("/yarn/global/")) {
|
|
35736
|
+
return { gestor: "yarn", comando: `yarn global add ${PACKAGE_NAME}@latest` };
|
|
35737
|
+
}
|
|
35738
|
+
if (ruta.includes("/npm/node_modules/") || ruta.includes("/roaming/npm/") || ruta.includes("/lib/node_modules/") || ruta.includes("/npm-global/")) {
|
|
35739
|
+
return { gestor: "npm", comando: `npm install -g ${PACKAGE_NAME}@latest` };
|
|
35740
|
+
}
|
|
35741
|
+
return { gestor: "desconocido", comando: plataforma === "win32" ? `npm install -g ${PACKAGE_NAME}@latest` : `npm install -g ${PACKAGE_NAME}@latest` };
|
|
35742
|
+
}
|
|
35743
|
+
function compararVersiones(a, b) {
|
|
35744
|
+
const partes = (v) => v.trim().replace(/^v/, "").split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
35745
|
+
const ap = partes(a);
|
|
35746
|
+
const bp = partes(b);
|
|
35747
|
+
for (let i = 0; i < 3; i += 1) {
|
|
35748
|
+
const diff = (ap[i] ?? 0) - (bp[i] ?? 0);
|
|
35749
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
35750
|
+
}
|
|
35751
|
+
return 0;
|
|
35752
|
+
}
|
|
35753
|
+
function hayVersionNueva(instalada, publicada) {
|
|
35754
|
+
if (!publicada) return false;
|
|
35755
|
+
return compararVersiones(publicada, instalada) > 0;
|
|
35756
|
+
}
|
|
35757
|
+
var PREFERENCIAS_POR_DEFECTO = { comprobar: true, automatica: false };
|
|
35758
|
+
function directorioPersonal(home = os.homedir()) {
|
|
35759
|
+
return path25.join(home, ".specatlas");
|
|
35760
|
+
}
|
|
35761
|
+
function rutaPreferencias(home) {
|
|
35762
|
+
return path25.join(directorioPersonal(home), "config.json");
|
|
35763
|
+
}
|
|
35764
|
+
function rutaEstado(home) {
|
|
35765
|
+
return path25.join(directorioPersonal(home), "update-check.json");
|
|
35766
|
+
}
|
|
35767
|
+
async function leerPreferencias(home) {
|
|
35768
|
+
const raw = await readTextIfExists(rutaPreferencias(home));
|
|
35769
|
+
if (raw === void 0) return { ...PREFERENCIAS_POR_DEFECTO };
|
|
35770
|
+
try {
|
|
35771
|
+
const data = JSON.parse(raw);
|
|
35772
|
+
return {
|
|
35773
|
+
comprobar: data.update?.comprobar ?? PREFERENCIAS_POR_DEFECTO.comprobar,
|
|
35774
|
+
automatica: data.update?.automatica ?? PREFERENCIAS_POR_DEFECTO.automatica
|
|
35775
|
+
};
|
|
35776
|
+
} catch {
|
|
35777
|
+
return { ...PREFERENCIAS_POR_DEFECTO };
|
|
35778
|
+
}
|
|
35779
|
+
}
|
|
35780
|
+
async function escribirPreferencias(preferencias, home) {
|
|
35781
|
+
const file = rutaPreferencias(home);
|
|
35782
|
+
await ensureDir(path25.dirname(file));
|
|
35783
|
+
await writeText(file, `${JSON.stringify({ update: preferencias }, null, 2)}
|
|
35784
|
+
`);
|
|
35785
|
+
return file;
|
|
35786
|
+
}
|
|
35787
|
+
async function leerEstado(home) {
|
|
35788
|
+
const raw = await readTextIfExists(rutaEstado(home));
|
|
35789
|
+
if (raw === void 0) return {};
|
|
35790
|
+
try {
|
|
35791
|
+
return JSON.parse(raw);
|
|
35792
|
+
} catch {
|
|
35793
|
+
return {};
|
|
35794
|
+
}
|
|
35795
|
+
}
|
|
35796
|
+
async function escribirEstado(estado, home) {
|
|
35797
|
+
const file = rutaEstado(home);
|
|
35798
|
+
await ensureDir(path25.dirname(file));
|
|
35799
|
+
await writeText(file, `${JSON.stringify(estado, null, 2)}
|
|
35800
|
+
`);
|
|
35801
|
+
}
|
|
35802
|
+
function tocaConsultar(estado, ahora = /* @__PURE__ */ new Date(), ttlMs = TTL_MS) {
|
|
35803
|
+
if (!estado.ultimaConsulta) return true;
|
|
35804
|
+
const ultima = Date.parse(estado.ultimaConsulta);
|
|
35805
|
+
if (Number.isNaN(ultima)) return true;
|
|
35806
|
+
return ahora.getTime() - ultima >= ttlMs;
|
|
35807
|
+
}
|
|
35808
|
+
function esIntegracionContinua(env = process.env) {
|
|
35809
|
+
return Boolean(env["CI"] || env["CONTINUOUS_INTEGRATION"] || env["GITHUB_ACTIONS"] || env["GITLAB_CI"] || env["BUILDKITE"] || env["TEAMCITY_VERSION"]);
|
|
35810
|
+
}
|
|
35811
|
+
function comprobacionDesactivada(env = process.env) {
|
|
35812
|
+
const flag3 = env["SPECATLAS_NO_UPDATE_CHECK"];
|
|
35813
|
+
return flag3 === "1" || flag3 === "true";
|
|
35814
|
+
}
|
|
35815
|
+
async function consultarVersionPublicada(timeoutMs = TIMEOUT_MS, fetchImpl = fetch) {
|
|
35816
|
+
const control = new AbortController();
|
|
35817
|
+
const timer = setTimeout(() => control.abort(), timeoutMs);
|
|
35818
|
+
try {
|
|
35819
|
+
const respuesta = await fetchImpl(`${REGISTRY}/-/package/${PACKAGE_NAME}/dist-tags`, { signal: control.signal });
|
|
35820
|
+
if (!respuesta.ok) return void 0;
|
|
35821
|
+
const data = await respuesta.json();
|
|
35822
|
+
return typeof data.latest === "string" ? data.latest : void 0;
|
|
35823
|
+
} catch {
|
|
35824
|
+
return void 0;
|
|
35825
|
+
} finally {
|
|
35826
|
+
clearTimeout(timer);
|
|
35827
|
+
}
|
|
35828
|
+
}
|
|
35829
|
+
async function comprobarActualizacion(ctx) {
|
|
35830
|
+
const env = ctx.env ?? process.env;
|
|
35831
|
+
if (ctx.json || esIntegracionContinua(env) || comprobacionDesactivada(env)) return void 0;
|
|
35832
|
+
const preferencias = await leerPreferencias(ctx.home);
|
|
35833
|
+
if (!preferencias.comprobar) return void 0;
|
|
35834
|
+
const ahora = ctx.ahora ?? /* @__PURE__ */ new Date();
|
|
35835
|
+
const estado = await leerEstado(ctx.home);
|
|
35836
|
+
let publicada = estado.ultimaVersion;
|
|
35837
|
+
if (tocaConsultar(estado, ahora)) {
|
|
35838
|
+
publicada = await consultarVersionPublicada(TIMEOUT_MS, ctx.fetchImpl ?? fetch);
|
|
35839
|
+
if (publicada === void 0) return void 0;
|
|
35840
|
+
await escribirEstado({ ultimaConsulta: ahora.toISOString(), ultimaVersion: publicada }, ctx.home);
|
|
35841
|
+
}
|
|
35842
|
+
if (!hayVersionNueva(ctx.instalada, publicada)) return void 0;
|
|
35843
|
+
const instalacion = detectarInstalacion(ctx.rutaEjecutable);
|
|
35844
|
+
return {
|
|
35845
|
+
instalada: ctx.instalada,
|
|
35846
|
+
publicada,
|
|
35847
|
+
comando: instalacion.comando ?? `npm install -g ${PACKAGE_NAME}@latest`,
|
|
35848
|
+
gestor: instalacion.gestor
|
|
35849
|
+
};
|
|
35850
|
+
}
|
|
35715
35851
|
var DOCS_MARKER_START = "<!-- specatlas:generado:inicio -->";
|
|
35716
35852
|
var DOCS_MARKER_END = "<!-- specatlas:generado:fin -->";
|
|
35717
35853
|
var ES_WORDING = {
|
|
@@ -36197,7 +36333,7 @@ ${notes}
|
|
|
36197
36333
|
${existing}`;
|
|
36198
36334
|
}
|
|
36199
36335
|
async function generateDocs(opts) {
|
|
36200
|
-
const root =
|
|
36336
|
+
const root = path26.resolve(opts.root);
|
|
36201
36337
|
const { config } = await loadWorkspace(root);
|
|
36202
36338
|
const change = await loadChange(root, opts.slug);
|
|
36203
36339
|
if (!change.meta) {
|
|
@@ -36213,10 +36349,10 @@ async function generateDocs(opts) {
|
|
|
36213
36349
|
const tipos = tipo === "all" ? ["tecnica", "manual"] : [tipo];
|
|
36214
36350
|
const templates = templatesFor(language);
|
|
36215
36351
|
const [proposalRaw, planRaw, glossaryRaw, analyzeRaw, mockupManifest] = await Promise.all([
|
|
36216
|
-
readTextIfExists(
|
|
36217
|
-
readTextIfExists(
|
|
36218
|
-
readTextIfExists(
|
|
36219
|
-
readTextIfExists(
|
|
36352
|
+
readTextIfExists(path26.join(change.dir, "proposal.md")),
|
|
36353
|
+
readTextIfExists(path26.join(change.dir, "plan.md")),
|
|
36354
|
+
readTextIfExists(path26.join(root, ".sdd", "glossary.md")),
|
|
36355
|
+
readTextIfExists(path26.join(change.dir, "analyze.md")),
|
|
36220
36356
|
readMockupManifest(root, change.slug)
|
|
36221
36357
|
]);
|
|
36222
36358
|
const mockups = (mockupManifest.manifest?.screens ?? []).map((screen) => ({
|
|
@@ -36230,7 +36366,7 @@ async function generateDocs(opts) {
|
|
|
36230
36366
|
const files = [];
|
|
36231
36367
|
const diagnostics = [];
|
|
36232
36368
|
for (const current of tipos) {
|
|
36233
|
-
const file =
|
|
36369
|
+
const file = path26.join(change.dir, "docs", `${current}.md`);
|
|
36234
36370
|
const block = renderTemplate(current === "tecnica" ? templates.docTecnica : templates.docManual, data);
|
|
36235
36371
|
const existing = await readTextIfExists(file);
|
|
36236
36372
|
const content = mergeManaged(existing, block, language);
|
|
@@ -36239,7 +36375,7 @@ async function generateDocs(opts) {
|
|
|
36239
36375
|
const label = current === "tecnica" ? "t\xE9cnico" : "manual";
|
|
36240
36376
|
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
36377
|
try {
|
|
36242
|
-
const htmlPath =
|
|
36378
|
+
const htmlPath = path26.join(change.dir, "docs", `${current}.html`);
|
|
36243
36379
|
await writeText(htmlPath, renderDocument(content, { theme: "auto", title }));
|
|
36244
36380
|
doc.htmlPath = htmlPath;
|
|
36245
36381
|
} catch (error2) {
|
|
@@ -36251,7 +36387,7 @@ async function generateDocs(opts) {
|
|
|
36251
36387
|
);
|
|
36252
36388
|
}
|
|
36253
36389
|
try {
|
|
36254
|
-
const pdfPath =
|
|
36390
|
+
const pdfPath = path26.join(change.dir, "docs", `${current}.pdf`);
|
|
36255
36391
|
const pdf = await renderPdf(content, { title, project: config.project.name, generatedAt: localStamp(ahora) });
|
|
36256
36392
|
await writeBytes(pdfPath, pdf.bytes);
|
|
36257
36393
|
doc.pdfPath = pdfPath;
|
|
@@ -36304,7 +36440,7 @@ function resultOf(diagnostic, root) {
|
|
|
36304
36440
|
message: { text: diagnostic.message }
|
|
36305
36441
|
};
|
|
36306
36442
|
if (diagnostic.path) {
|
|
36307
|
-
const rel =
|
|
36443
|
+
const rel = path27.relative(root, diagnostic.path);
|
|
36308
36444
|
if (rel !== "") {
|
|
36309
36445
|
const physicalLocation = {
|
|
36310
36446
|
artifactLocation: { uri: toPosix(rel) },
|
|
@@ -36359,7 +36495,7 @@ async function runDoctor(root) {
|
|
|
36359
36495
|
const approvals = await loadApprovals(workspace.sddDir);
|
|
36360
36496
|
findings.push(...approvals.diagnostics);
|
|
36361
36497
|
for (const change of workspace.changes) {
|
|
36362
|
-
const deltaPath =
|
|
36498
|
+
const deltaPath = path28.join(change.dir, "spec.md");
|
|
36363
36499
|
const deltaContent = await readTextIfExists(deltaPath);
|
|
36364
36500
|
const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent ?? void 0);
|
|
36365
36501
|
if ((change.planPath || change.tasks) && (approval.status === "missing" || approval.status === "stale")) {
|
|
@@ -36381,7 +36517,7 @@ async function runDoctor(root) {
|
|
|
36381
36517
|
}
|
|
36382
36518
|
for (const override of change.meta?.overrides ?? []) {
|
|
36383
36519
|
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:
|
|
36520
|
+
findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path28.join(change.dir, "meta.yaml") }));
|
|
36385
36521
|
}
|
|
36386
36522
|
}
|
|
36387
36523
|
}
|
|
@@ -36413,7 +36549,7 @@ function livingRequirementsMap(specs) {
|
|
|
36413
36549
|
return map;
|
|
36414
36550
|
}
|
|
36415
36551
|
async function runCiGate(opts) {
|
|
36416
|
-
const root =
|
|
36552
|
+
const root = path29.resolve(opts.root);
|
|
36417
36553
|
const { workspace, config } = await loadWorkspace(root);
|
|
36418
36554
|
const diagnostics = [];
|
|
36419
36555
|
const checks = [];
|
|
@@ -36424,7 +36560,7 @@ async function runCiGate(opts) {
|
|
|
36424
36560
|
let changesErrors = 0;
|
|
36425
36561
|
let changesWarnings = 0;
|
|
36426
36562
|
for (const change of workspace.changes) {
|
|
36427
|
-
const lintFindings = change.delta ? lintDelta(change.delta, living,
|
|
36563
|
+
const lintFindings = change.delta ? lintDelta(change.delta, living, path29.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
|
|
36428
36564
|
const trace = checkTrace({
|
|
36429
36565
|
specs: workspace.specs,
|
|
36430
36566
|
change,
|
|
@@ -36565,6 +36701,7 @@ var CATALOG = [
|
|
|
36565
36701
|
{ 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
36702
|
{ 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
36703
|
{ 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>]" },
|
|
36704
|
+
{ 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
36705
|
{ name: "version", description: "Muestra la versi\xF3n", flags: ["json"], usage: "satlas version" },
|
|
36569
36706
|
{ name: "help", description: "Muestra la ayuda", flags: ["json"], usage: "satlas help [comando]" }
|
|
36570
36707
|
];
|
|
@@ -36682,26 +36819,26 @@ function cliVersion() {
|
|
|
36682
36819
|
}
|
|
36683
36820
|
|
|
36684
36821
|
// src/commands/adapters.ts
|
|
36685
|
-
import
|
|
36822
|
+
import path32 from "path";
|
|
36686
36823
|
|
|
36687
36824
|
// ../adapters/dist/index.js
|
|
36688
|
-
import
|
|
36825
|
+
import path30 from "path";
|
|
36689
36826
|
import { stringify as stringifyYaml9 } from "yaml";
|
|
36690
36827
|
import path210 from "path";
|
|
36691
36828
|
async function loadWorkflow(workflowDir) {
|
|
36692
|
-
const phasesDir =
|
|
36693
|
-
const snippetsDir =
|
|
36829
|
+
const phasesDir = path30.join(workflowDir, "phases");
|
|
36830
|
+
const snippetsDir = path30.join(workflowDir, "snippets");
|
|
36694
36831
|
const phases = [];
|
|
36695
36832
|
const snippets = /* @__PURE__ */ new Map();
|
|
36696
36833
|
const hashParts = [];
|
|
36697
36834
|
for (const entry of (await listDir(phasesDir)).sort()) {
|
|
36698
36835
|
if (!entry.endsWith(".md")) continue;
|
|
36699
|
-
const filePath =
|
|
36836
|
+
const filePath = path30.join(phasesDir, entry);
|
|
36700
36837
|
const raw = await readText(filePath);
|
|
36701
36838
|
hashParts.push(raw);
|
|
36702
36839
|
const fm = parseFrontmatter(raw, filePath);
|
|
36703
36840
|
const data = fm.data;
|
|
36704
|
-
const id = typeof data["id"] === "string" ? data["id"] :
|
|
36841
|
+
const id = typeof data["id"] === "string" ? data["id"] : path30.basename(entry, ".md");
|
|
36705
36842
|
phases.push({
|
|
36706
36843
|
id,
|
|
36707
36844
|
title: typeof data["title"] === "string" ? data["title"] : id,
|
|
@@ -36715,9 +36852,9 @@ async function loadWorkflow(workflowDir) {
|
|
|
36715
36852
|
}
|
|
36716
36853
|
for (const entry of (await listDir(snippetsDir)).sort()) {
|
|
36717
36854
|
if (!entry.endsWith(".md")) continue;
|
|
36718
|
-
const raw = await readTextIfExists(
|
|
36855
|
+
const raw = await readTextIfExists(path30.join(snippetsDir, entry)) ?? "";
|
|
36719
36856
|
hashParts.push(raw);
|
|
36720
|
-
snippets.set(
|
|
36857
|
+
snippets.set(path30.basename(entry, ".md"), raw.trim());
|
|
36721
36858
|
}
|
|
36722
36859
|
return { phases: phases.sort((a, b) => a.id.localeCompare(b.id)), snippets, sourceHash: sha256(hashParts.join("\n---\n")) };
|
|
36723
36860
|
}
|
|
@@ -37032,11 +37169,11 @@ async function checkAdapters(opts) {
|
|
|
37032
37169
|
}
|
|
37033
37170
|
|
|
37034
37171
|
// src/paths.ts
|
|
37035
|
-
import
|
|
37172
|
+
import path31 from "path";
|
|
37036
37173
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
37037
37174
|
async function isSourceRepoRoot(dir) {
|
|
37038
|
-
if (!await exists(
|
|
37039
|
-
const raw = await readTextIfExists(
|
|
37175
|
+
if (!await exists(path31.join(dir, "pnpm-workspace.yaml"))) return false;
|
|
37176
|
+
const raw = await readTextIfExists(path31.join(dir, "packages", "cli", "package.json"));
|
|
37040
37177
|
if (raw === void 0) return false;
|
|
37041
37178
|
try {
|
|
37042
37179
|
return JSON.parse(raw).name === "specatlas";
|
|
@@ -37048,17 +37185,17 @@ async function sourceRepoRoot(startDir) {
|
|
|
37048
37185
|
let dir = startDir;
|
|
37049
37186
|
for (let i = 0; i < 5; i += 1) {
|
|
37050
37187
|
if (await isSourceRepoRoot(dir)) return dir;
|
|
37051
|
-
const parent =
|
|
37188
|
+
const parent = path31.dirname(dir);
|
|
37052
37189
|
if (parent === dir) break;
|
|
37053
37190
|
dir = parent;
|
|
37054
37191
|
}
|
|
37055
37192
|
return void 0;
|
|
37056
37193
|
}
|
|
37057
37194
|
async function resolveFromSource(relative) {
|
|
37058
|
-
const start =
|
|
37195
|
+
const start = path31.dirname(fileURLToPath2(import.meta.url));
|
|
37059
37196
|
const root = await sourceRepoRoot(start);
|
|
37060
37197
|
if (!root) return void 0;
|
|
37061
|
-
const candidate =
|
|
37198
|
+
const candidate = path31.join(root, relative);
|
|
37062
37199
|
return await exists(candidate) ? candidate : void 0;
|
|
37063
37200
|
}
|
|
37064
37201
|
async function resolveProfilesDir() {
|
|
@@ -37069,16 +37206,16 @@ async function resolveProfilesDir() {
|
|
|
37069
37206
|
async function resolveWorkflowDir() {
|
|
37070
37207
|
const env = process.env["SPECATLAS_WORKFLOW_DIR"];
|
|
37071
37208
|
if (env && await exists(env)) return env;
|
|
37072
|
-
return await resolveFromSource("workflow") ?? walkUpFor(
|
|
37209
|
+
return await resolveFromSource("workflow") ?? walkUpFor(path31.join("workflow", "phases"));
|
|
37073
37210
|
}
|
|
37074
37211
|
async function walkUpFor(relative) {
|
|
37075
|
-
let dir =
|
|
37212
|
+
let dir = path31.dirname(fileURLToPath2(import.meta.url));
|
|
37076
37213
|
for (let i = 0; i < 8; i += 1) {
|
|
37077
|
-
const candidate =
|
|
37214
|
+
const candidate = path31.join(dir, relative);
|
|
37078
37215
|
if (await exists(candidate)) {
|
|
37079
|
-
return relative.includes(
|
|
37216
|
+
return relative.includes(path31.sep) ? path31.dirname(candidate) : candidate;
|
|
37080
37217
|
}
|
|
37081
|
-
const parent =
|
|
37218
|
+
const parent = path31.dirname(dir);
|
|
37082
37219
|
if (parent === dir) break;
|
|
37083
37220
|
dir = parent;
|
|
37084
37221
|
}
|
|
@@ -37156,7 +37293,7 @@ async function runAdapters(ctx) {
|
|
|
37156
37293
|
for (const [target, count2] of byTarget) lines.push(` ${target}: ${count2} archivo(s)`);
|
|
37157
37294
|
if (report.written.length > 0) {
|
|
37158
37295
|
lines.push("");
|
|
37159
|
-
for (const p of report.written) lines.push(` + ${
|
|
37296
|
+
for (const p of report.written) lines.push(` + ${path32.relative(ctx.cwd, path32.join(root, p))}`);
|
|
37160
37297
|
} else {
|
|
37161
37298
|
lines.push("");
|
|
37162
37299
|
lines.push("Sin cambios: los adaptadores ya estaban al d\xEDa.");
|
|
@@ -37167,7 +37304,7 @@ async function runAdapters(ctx) {
|
|
|
37167
37304
|
}
|
|
37168
37305
|
|
|
37169
37306
|
// src/commands/adopt.ts
|
|
37170
|
-
import
|
|
37307
|
+
import path33 from "path";
|
|
37171
37308
|
async function runAdopt(ctx) {
|
|
37172
37309
|
const { root } = await requireWorkspace(ctx);
|
|
37173
37310
|
const domainsFlag = flagString(ctx.flags, "domains") ?? flagString(ctx.flags, "domain");
|
|
@@ -37187,11 +37324,11 @@ async function runAdopt(ctx) {
|
|
|
37187
37324
|
}
|
|
37188
37325
|
if (result.createdSpecs.length > 0) {
|
|
37189
37326
|
lines.push("");
|
|
37190
|
-
for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${
|
|
37327
|
+
for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path33.relative(ctx.cwd, spec)}`);
|
|
37191
37328
|
}
|
|
37192
37329
|
if (result.reportPath && !result.dryRun) {
|
|
37193
37330
|
lines.push("");
|
|
37194
|
-
lines.push(` Informe: ${
|
|
37331
|
+
lines.push(` Informe: ${path33.relative(ctx.cwd, result.reportPath)}`);
|
|
37195
37332
|
}
|
|
37196
37333
|
lines.push("");
|
|
37197
37334
|
lines.push("Siguiente: fase /satlas-adopt por dominio (requisitos AS-IS) y luego:");
|
|
@@ -37204,8 +37341,8 @@ async function runAdopt(ctx) {
|
|
|
37204
37341
|
diagnostics: result.diagnostics,
|
|
37205
37342
|
data: {
|
|
37206
37343
|
domains: result.domains.map((d) => ({ name: d.name, files: d.files, existingSpec: d.existingSpec })),
|
|
37207
|
-
createdSpecs: result.createdSpecs.map((s) =>
|
|
37208
|
-
reportPath: result.reportPath ?
|
|
37344
|
+
createdSpecs: result.createdSpecs.map((s) => path33.relative(ctx.cwd, s)),
|
|
37345
|
+
reportPath: result.reportPath ? path33.relative(ctx.cwd, result.reportPath) : void 0,
|
|
37209
37346
|
stack: result.stack,
|
|
37210
37347
|
dryRun: result.dryRun
|
|
37211
37348
|
},
|
|
@@ -37254,7 +37391,7 @@ async function runAmend(ctx) {
|
|
|
37254
37391
|
}
|
|
37255
37392
|
|
|
37256
37393
|
// src/commands/analyze.ts
|
|
37257
|
-
import
|
|
37394
|
+
import path34 from "path";
|
|
37258
37395
|
async function runAnalyzeCommand(ctx) {
|
|
37259
37396
|
const { root } = await requireWorkspace(ctx);
|
|
37260
37397
|
const slug = ctx.positionals[0];
|
|
@@ -37265,23 +37402,23 @@ async function runAnalyzeCommand(ctx) {
|
|
|
37265
37402
|
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
37403
|
if (result.waves) lines.push(` olas: ${result.waves.blocks} bloque(s), ${result.waves.waves} ola(s), ${result.waves.tasks} tarea(s)`);
|
|
37267
37404
|
if (result.mockups) lines.push(` mockups: ${result.mockups.screens} pantalla(s)${result.mockups.stale ? " (desactualizados)" : ""}`);
|
|
37268
|
-
if (result.path) lines.push("", ` informe: ${
|
|
37405
|
+
if (result.path) lines.push("", ` informe: ${path34.relative(ctx.cwd, result.path)}`);
|
|
37269
37406
|
for (const finding of result.findings) {
|
|
37270
37407
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}`);
|
|
37271
37408
|
}
|
|
37272
37409
|
return {
|
|
37273
37410
|
exitCode: result.status === "blocked" ? 1 : 0,
|
|
37274
37411
|
diagnostics: result.findings,
|
|
37275
|
-
data: result.path ? { ...result, path:
|
|
37412
|
+
data: result.path ? { ...result, path: path34.relative(ctx.cwd, result.path) } : result,
|
|
37276
37413
|
text: lines
|
|
37277
37414
|
};
|
|
37278
37415
|
}
|
|
37279
37416
|
|
|
37280
37417
|
// src/commands/approve.ts
|
|
37281
|
-
import
|
|
37418
|
+
import path36 from "path";
|
|
37282
37419
|
|
|
37283
37420
|
// src/commands/issue.ts
|
|
37284
|
-
import
|
|
37421
|
+
import path35 from "path";
|
|
37285
37422
|
async function runIssue(ctx) {
|
|
37286
37423
|
const action = ctx.positionals[0] ?? "status";
|
|
37287
37424
|
const slug = ctx.positionals[1];
|
|
@@ -37309,7 +37446,7 @@ async function runIssue(ctx) {
|
|
|
37309
37446
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-ISSUE-000", severity: "error", message: "Falta el slug: satlas issue status <slug>" }] };
|
|
37310
37447
|
}
|
|
37311
37448
|
const change = await loadChange(root, slug);
|
|
37312
|
-
const { config } = await loadConfig(
|
|
37449
|
+
const { config } = await loadConfig(path35.join(root, ".sdd"));
|
|
37313
37450
|
const tracker = change.meta?.tracker;
|
|
37314
37451
|
const lines = [`Issue de ${slug}`, ""];
|
|
37315
37452
|
if (!tracker || tracker.provider !== "github") {
|
|
@@ -37380,7 +37517,7 @@ async function runApprove(ctx) {
|
|
|
37380
37517
|
};
|
|
37381
37518
|
}
|
|
37382
37519
|
const { root, workspace, config } = await requireWorkspace(ctx);
|
|
37383
|
-
const artifact = target.includes("/") || target.includes("\\") ? target :
|
|
37520
|
+
const artifact = target.includes("/") || target.includes("\\") ? target : path36.join("changes", target, "spec.md");
|
|
37384
37521
|
const channelFlag = flagString(ctx.flags, "channel");
|
|
37385
37522
|
const channel = channelFlag === "presentation" || channelFlag === "editor" || channelFlag === "pr" || channelFlag === "tracker" ? channelFlag : "cli";
|
|
37386
37523
|
const change = workspace.changes.find(
|
|
@@ -37421,13 +37558,13 @@ async function runApprove(ctx) {
|
|
|
37421
37558
|
return {
|
|
37422
37559
|
exitCode: hasErrors2 ? 1 : 0,
|
|
37423
37560
|
diagnostics: result.diagnostics,
|
|
37424
|
-
data: result.approval ? { ...result.approval, file:
|
|
37561
|
+
data: result.approval ? { ...result.approval, file: path36.relative(ctx.cwd, result.file) } : void 0,
|
|
37425
37562
|
text: lines
|
|
37426
37563
|
};
|
|
37427
37564
|
}
|
|
37428
37565
|
|
|
37429
37566
|
// src/commands/archive.ts
|
|
37430
|
-
import
|
|
37567
|
+
import path37 from "path";
|
|
37431
37568
|
async function runArchive(ctx) {
|
|
37432
37569
|
const slug = ctx.positionals[0];
|
|
37433
37570
|
if (!slug) {
|
|
@@ -37464,7 +37601,7 @@ async function runArchive(ctx) {
|
|
|
37464
37601
|
} else {
|
|
37465
37602
|
lines.push("El delta no contiene operaciones (ADDED/MODIFIED/REMOVED/RENAMED).");
|
|
37466
37603
|
}
|
|
37467
|
-
if (result.archivedTo) lines.push(` archivado en: ${
|
|
37604
|
+
if (result.archivedTo) lines.push(` archivado en: ${path37.relative(ctx.cwd, result.archivedTo)}`);
|
|
37468
37605
|
const hasErrors2 = result.diagnostics.some((d) => d.severity === "error");
|
|
37469
37606
|
return {
|
|
37470
37607
|
exitCode: hasErrors2 ? 1 : 0,
|
|
@@ -37475,7 +37612,7 @@ async function runArchive(ctx) {
|
|
|
37475
37612
|
}
|
|
37476
37613
|
|
|
37477
37614
|
// src/commands/ci.ts
|
|
37478
|
-
import
|
|
37615
|
+
import path38 from "path";
|
|
37479
37616
|
async function runCi(ctx) {
|
|
37480
37617
|
const { root, config } = await requireWorkspace(ctx);
|
|
37481
37618
|
const strict = flagBool(ctx.flags, "strict");
|
|
@@ -37501,10 +37638,10 @@ async function runCi(ctx) {
|
|
|
37501
37638
|
const lines = ["CI de SpecAtlas", ""];
|
|
37502
37639
|
let sarifWritten;
|
|
37503
37640
|
if (sarifPath) {
|
|
37504
|
-
const target =
|
|
37641
|
+
const target = path38.resolve(ctx.cwd, sarifPath);
|
|
37505
37642
|
try {
|
|
37506
37643
|
await writeText(target, toSarifText({ diagnostics: gate.diagnostics, root, version: cliVersion(), failed: gate.failed }));
|
|
37507
|
-
sarifWritten =
|
|
37644
|
+
sarifWritten = path38.relative(ctx.cwd, target);
|
|
37508
37645
|
} catch (err) {
|
|
37509
37646
|
diagnostics.push({
|
|
37510
37647
|
code: "ATLAS-CI-SARIF-001",
|
|
@@ -37581,7 +37718,7 @@ async function runClarify(ctx) {
|
|
|
37581
37718
|
}
|
|
37582
37719
|
|
|
37583
37720
|
// src/commands/contracts.ts
|
|
37584
|
-
import
|
|
37721
|
+
import path39 from "path";
|
|
37585
37722
|
async function runContracts(ctx) {
|
|
37586
37723
|
const slug = ctx.positionals[0];
|
|
37587
37724
|
if (!slug) {
|
|
@@ -37601,7 +37738,7 @@ async function runContracts(ctx) {
|
|
|
37601
37738
|
const coverage = contractCoverage(change, config.gates.contracts.mode);
|
|
37602
37739
|
const findings = [...state.findings, ...coverage];
|
|
37603
37740
|
for (const file of state.files) {
|
|
37604
|
-
lines.push(` ${
|
|
37741
|
+
lines.push(` ${path39.relative(ctx.cwd, file.path)} (${file.format}) \u2014 ${file.operations.length} operaci\xF3n(es)`);
|
|
37605
37742
|
for (const operation of file.operations) lines.push(` ${operation.id}`);
|
|
37606
37743
|
}
|
|
37607
37744
|
lines.push("");
|
|
@@ -37617,7 +37754,7 @@ async function runContracts(ctx) {
|
|
|
37617
37754
|
data: {
|
|
37618
37755
|
slug,
|
|
37619
37756
|
mode: config.gates.contracts.mode,
|
|
37620
|
-
contracts: state.files.map((file) => ({ path:
|
|
37757
|
+
contracts: state.files.map((file) => ({ path: path39.relative(ctx.cwd, file.path), format: file.format, operations: file.operations.map((operation) => operation.id) })),
|
|
37621
37758
|
findings
|
|
37622
37759
|
},
|
|
37623
37760
|
text: lines
|
|
@@ -37625,7 +37762,7 @@ async function runContracts(ctx) {
|
|
|
37625
37762
|
}
|
|
37626
37763
|
|
|
37627
37764
|
// src/commands/docs.ts
|
|
37628
|
-
import
|
|
37765
|
+
import path40 from "path";
|
|
37629
37766
|
async function runDocs(ctx) {
|
|
37630
37767
|
const slug = ctx.positionals[0];
|
|
37631
37768
|
if (!slug) {
|
|
@@ -37639,9 +37776,9 @@ async function runDocs(ctx) {
|
|
|
37639
37776
|
const warnings = result.diagnostics.filter((d) => d.severity === "warning");
|
|
37640
37777
|
const lines = [msg("docs.title", ctx.language), ""];
|
|
37641
37778
|
for (const file of result.files) {
|
|
37642
|
-
lines.push(` ${file.created ? msg("docs.created", ctx.language) : msg("docs.updated", ctx.language)}: ${
|
|
37643
|
-
if (file.htmlPath) lines.push(` html: ${
|
|
37644
|
-
if (file.pdfPath) lines.push(` pdf: ${
|
|
37779
|
+
lines.push(` ${file.created ? msg("docs.created", ctx.language) : msg("docs.updated", ctx.language)}: ${path40.relative(ctx.cwd, file.path)}`);
|
|
37780
|
+
if (file.htmlPath) lines.push(` html: ${path40.relative(ctx.cwd, file.htmlPath)}`);
|
|
37781
|
+
if (file.pdfPath) lines.push(` pdf: ${path40.relative(ctx.cwd, file.pdfPath)}`);
|
|
37645
37782
|
}
|
|
37646
37783
|
for (const warning of warnings) lines.push(` AVISO ${warning.code} \u2014 ${warning.message}`);
|
|
37647
37784
|
for (const error2 of errors) lines.push(` ERROR ${error2.code} \u2014 ${error2.message}`);
|
|
@@ -37653,10 +37790,10 @@ async function runDocs(ctx) {
|
|
|
37653
37790
|
tipo,
|
|
37654
37791
|
files: result.files.map((file) => ({
|
|
37655
37792
|
tipo: file.tipo,
|
|
37656
|
-
path:
|
|
37793
|
+
path: path40.relative(ctx.cwd, file.path),
|
|
37657
37794
|
created: file.created,
|
|
37658
|
-
html: file.htmlPath ?
|
|
37659
|
-
pdf: file.pdfPath ?
|
|
37795
|
+
html: file.htmlPath ? path40.relative(ctx.cwd, file.htmlPath) : void 0,
|
|
37796
|
+
pdf: file.pdfPath ? path40.relative(ctx.cwd, file.pdfPath) : void 0
|
|
37660
37797
|
}))
|
|
37661
37798
|
},
|
|
37662
37799
|
text: lines
|
|
@@ -37664,7 +37801,7 @@ async function runDocs(ctx) {
|
|
|
37664
37801
|
}
|
|
37665
37802
|
|
|
37666
37803
|
// src/commands/drift.ts
|
|
37667
|
-
import
|
|
37804
|
+
import path41 from "path";
|
|
37668
37805
|
async function runDrift(ctx) {
|
|
37669
37806
|
const { root, config } = await requireWorkspace(ctx);
|
|
37670
37807
|
const anchors = await loadAllAnchors(root);
|
|
@@ -37708,7 +37845,7 @@ async function runDrift(ctx) {
|
|
|
37708
37845
|
mode: report.mode,
|
|
37709
37846
|
domains: report.domains,
|
|
37710
37847
|
checked: report.checked,
|
|
37711
|
-
drifted: report.drifted.map((item) => ({ ...item, specPath:
|
|
37848
|
+
drifted: report.drifted.map((item) => ({ ...item, specPath: path41.relative(ctx.cwd, item.specPath) }))
|
|
37712
37849
|
},
|
|
37713
37850
|
text: lines
|
|
37714
37851
|
};
|
|
@@ -37759,7 +37896,7 @@ async function runImpact(ctx) {
|
|
|
37759
37896
|
}
|
|
37760
37897
|
|
|
37761
37898
|
// src/commands/link.ts
|
|
37762
|
-
import
|
|
37899
|
+
import path42 from "path";
|
|
37763
37900
|
async function runLink(ctx) {
|
|
37764
37901
|
const { root } = await requireWorkspace(ctx);
|
|
37765
37902
|
const action = ctx.positionals[0] ?? "list";
|
|
@@ -37771,7 +37908,7 @@ async function runLink(ctx) {
|
|
|
37771
37908
|
} else {
|
|
37772
37909
|
for (const entry of state.entries) {
|
|
37773
37910
|
const status = entry.available ? `${entry.requirements} requisito(s) \xB7 ${entry.domains.join(", ") || "sin dominios"}` : "NO DISPONIBLE";
|
|
37774
|
-
lines.push(` ${entry.name} ${
|
|
37911
|
+
lines.push(` ${entry.name} ${path42.relative(ctx.cwd, entry.path)} ${status}`);
|
|
37775
37912
|
}
|
|
37776
37913
|
}
|
|
37777
37914
|
return {
|
|
@@ -37780,7 +37917,7 @@ async function runLink(ctx) {
|
|
|
37780
37917
|
data: {
|
|
37781
37918
|
links: state.entries.map((entry) => ({
|
|
37782
37919
|
name: entry.name,
|
|
37783
|
-
path:
|
|
37920
|
+
path: path42.relative(ctx.cwd, entry.path),
|
|
37784
37921
|
available: entry.available,
|
|
37785
37922
|
requirements: entry.requirements,
|
|
37786
37923
|
domains: entry.domains
|
|
@@ -37825,12 +37962,12 @@ async function runLink(ctx) {
|
|
|
37825
37962
|
}
|
|
37826
37963
|
|
|
37827
37964
|
// src/commands/profile.ts
|
|
37828
|
-
import
|
|
37965
|
+
import path43 from "path";
|
|
37829
37966
|
async function runProfile(ctx) {
|
|
37830
37967
|
const action = ctx.positionals[0] ?? "detect";
|
|
37831
37968
|
const { root, workspace } = await requireWorkspace(ctx);
|
|
37832
37969
|
const officialDir = await resolveProfilesDir();
|
|
37833
|
-
const customDir =
|
|
37970
|
+
const customDir = path43.join(workspace.sddDir, "profiles", "custom");
|
|
37834
37971
|
const profiles = [...officialDir ? await loadProfilesFromDir(officialDir) : [], ...await loadProfilesFromDir(customDir)];
|
|
37835
37972
|
if (action === "detect") {
|
|
37836
37973
|
const result = await detectProfiles(root, profiles);
|
|
@@ -37859,7 +37996,7 @@ async function runProfile(ctx) {
|
|
|
37859
37996
|
if (!name) {
|
|
37860
37997
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-001", severity: "error", message: "Falta el nombre del perfil: satlas profile create <nombre>" }] };
|
|
37861
37998
|
}
|
|
37862
|
-
const file =
|
|
37999
|
+
const file = path43.join(customDir, `${name}.yaml`);
|
|
37863
38000
|
if (await exists(file)) {
|
|
37864
38001
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-002", severity: "error", message: `El perfil ya existe: ${file}` }] };
|
|
37865
38002
|
}
|
|
@@ -37868,7 +38005,7 @@ async function runProfile(ctx) {
|
|
|
37868
38005
|
exitCode: 0,
|
|
37869
38006
|
diagnostics: [],
|
|
37870
38007
|
data: { file },
|
|
37871
|
-
text: [`Perfil creado: ${
|
|
38008
|
+
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
38009
|
};
|
|
37873
38010
|
}
|
|
37874
38011
|
return {
|
|
@@ -37877,7 +38014,7 @@ async function runProfile(ctx) {
|
|
|
37877
38014
|
};
|
|
37878
38015
|
}
|
|
37879
38016
|
async function isInside(dir, name) {
|
|
37880
|
-
return exists(
|
|
38017
|
+
return exists(path43.join(dir, `${name}.yaml`));
|
|
37881
38018
|
}
|
|
37882
38019
|
function profileTemplate(name) {
|
|
37883
38020
|
return `name: ${name}
|
|
@@ -37905,7 +38042,7 @@ assumptions: []
|
|
|
37905
38042
|
}
|
|
37906
38043
|
|
|
37907
38044
|
// src/commands/doctor.ts
|
|
37908
|
-
import
|
|
38045
|
+
import path44 from "path";
|
|
37909
38046
|
async function runDoctorCommand(ctx) {
|
|
37910
38047
|
const { root, config } = await requireWorkspace(ctx);
|
|
37911
38048
|
const report = await runDoctor(root);
|
|
@@ -37930,7 +38067,7 @@ async function runDoctorCommand(ctx) {
|
|
|
37930
38067
|
lines.push("Sin hallazgos. El workspace est\xE1 sano.");
|
|
37931
38068
|
} else {
|
|
37932
38069
|
for (const finding of report.findings) {
|
|
37933
|
-
const location = finding.path ? ` ${
|
|
38070
|
+
const location = finding.path ? ` ${path44.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
|
|
37934
38071
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${location} \u2014 ${finding.message}`);
|
|
37935
38072
|
if (finding.suggestion) lines.push(` \u21B3 ${finding.suggestion}`);
|
|
37936
38073
|
}
|
|
@@ -38041,7 +38178,7 @@ async function runHelp(ctx, commandName) {
|
|
|
38041
38178
|
}
|
|
38042
38179
|
|
|
38043
38180
|
// src/commands/init.ts
|
|
38044
|
-
import
|
|
38181
|
+
import path45 from "path";
|
|
38045
38182
|
async function runInit(ctx) {
|
|
38046
38183
|
const languageFlag = flagString(ctx.flags, "language");
|
|
38047
38184
|
const language = languageFlag === "en" ? "en" : languageFlag === "es" ? "es" : void 0;
|
|
@@ -38091,11 +38228,11 @@ async function runInit(ctx) {
|
|
|
38091
38228
|
const lines = [msg("init.title", ctx.language), ""];
|
|
38092
38229
|
if (result.created.length > 0) {
|
|
38093
38230
|
lines.push(msg("init.done", ctx.language));
|
|
38094
|
-
for (const file of result.created) lines.push(` + ${
|
|
38231
|
+
for (const file of result.created) lines.push(` + ${path45.relative(ctx.cwd, file)}`);
|
|
38095
38232
|
if (compiled.length > 0) {
|
|
38096
38233
|
lines.push("");
|
|
38097
38234
|
lines.push("Adaptadores compilados:");
|
|
38098
|
-
for (const file of compiled) lines.push(` + ${
|
|
38235
|
+
for (const file of compiled) lines.push(` + ${path45.relative(ctx.cwd, path45.join(ctx.cwd, file))}`);
|
|
38099
38236
|
}
|
|
38100
38237
|
lines.push("");
|
|
38101
38238
|
lines.push("Siguiente: crea un cambio con `satlas new <slug>` y escribe la spec funcional.");
|
|
@@ -38105,8 +38242,8 @@ async function runInit(ctx) {
|
|
|
38105
38242
|
exitCode: hasErrors2 ? 1 : 0,
|
|
38106
38243
|
diagnostics: result.diagnostics,
|
|
38107
38244
|
data: {
|
|
38108
|
-
sddDir:
|
|
38109
|
-
created: result.created.map((f) =>
|
|
38245
|
+
sddDir: path45.relative(ctx.cwd, result.sddDir),
|
|
38246
|
+
created: result.created.map((f) => path45.relative(ctx.cwd, f)),
|
|
38110
38247
|
detected: result.detected?.matches.map((m) => ({ name: m.name, score: m.score })),
|
|
38111
38248
|
best: result.detected?.best?.name
|
|
38112
38249
|
},
|
|
@@ -38147,7 +38284,7 @@ async function runMetrics(ctx) {
|
|
|
38147
38284
|
}
|
|
38148
38285
|
|
|
38149
38286
|
// src/commands/mockup.ts
|
|
38150
|
-
import
|
|
38287
|
+
import path46 from "path";
|
|
38151
38288
|
async function runMockup(ctx) {
|
|
38152
38289
|
const { root, workspace, config } = await requireWorkspace(ctx);
|
|
38153
38290
|
const slug = ctx.positionals[0];
|
|
@@ -38197,17 +38334,17 @@ async function runMockup(ctx) {
|
|
|
38197
38334
|
` pantallas: ${plan.screens.length}`,
|
|
38198
38335
|
...plan.screens.map((s) => ` - ${s.id}: ${s.title} (${s.illustrates.length} escenario(s))`),
|
|
38199
38336
|
"",
|
|
38200
|
-
` plan: ${
|
|
38201
|
-
` manifiesto: ${
|
|
38337
|
+
` plan: ${path46.relative(ctx.cwd, planFile)}`,
|
|
38338
|
+
` manifiesto: ${path46.relative(ctx.cwd, manifestFile)}`,
|
|
38202
38339
|
"",
|
|
38203
38340
|
`Siguiente: genera el HTML de cada pantalla (fase ${agentCommand("mockup", slug, config)}) y valida con \`satlas mockup ${slug} --check\`.`
|
|
38204
38341
|
];
|
|
38205
|
-
return { exitCode: 0, diagnostics: [], data: { plan, planFile:
|
|
38342
|
+
return { exitCode: 0, diagnostics: [], data: { plan, planFile: path46.relative(ctx.cwd, planFile), manifestFile: path46.relative(ctx.cwd, manifestFile) }, text: lines };
|
|
38206
38343
|
}
|
|
38207
38344
|
|
|
38208
38345
|
// src/mcp/server.ts
|
|
38209
38346
|
import { promises as fs3 } from "fs";
|
|
38210
|
-
import
|
|
38347
|
+
import path51 from "path";
|
|
38211
38348
|
import { createInterface } from "readline/promises";
|
|
38212
38349
|
|
|
38213
38350
|
// src/mcp/protocol.ts
|
|
@@ -38269,7 +38406,7 @@ function stringArg(args, key) {
|
|
|
38269
38406
|
}
|
|
38270
38407
|
|
|
38271
38408
|
// src/mcp/tools/contracts.ts
|
|
38272
|
-
import
|
|
38409
|
+
import path47 from "path";
|
|
38273
38410
|
async function runAtlasContracts(args, host) {
|
|
38274
38411
|
const resolved = await host.getWorkspace();
|
|
38275
38412
|
if (!resolved) return noWorkspaceResult();
|
|
@@ -38289,7 +38426,7 @@ async function runAtlasContracts(args, host) {
|
|
|
38289
38426
|
slug,
|
|
38290
38427
|
mode,
|
|
38291
38428
|
contracts: state.files.map((file) => ({
|
|
38292
|
-
path:
|
|
38429
|
+
path: path47.relative(resolved.root, file.path),
|
|
38293
38430
|
format: file.format,
|
|
38294
38431
|
operations: file.operations.map((operation) => operation.id)
|
|
38295
38432
|
})),
|
|
@@ -38325,12 +38462,12 @@ async function runAtlasFixes(_args, host) {
|
|
|
38325
38462
|
}
|
|
38326
38463
|
|
|
38327
38464
|
// src/mcp/tools/glossary.ts
|
|
38328
|
-
import
|
|
38465
|
+
import path48 from "path";
|
|
38329
38466
|
async function runAtlasGlossary(_args, host) {
|
|
38330
38467
|
const resolved = await host.getWorkspace();
|
|
38331
38468
|
if (!resolved) return noWorkspaceResult();
|
|
38332
38469
|
const { root, config } = resolved;
|
|
38333
|
-
const glossaryPath =
|
|
38470
|
+
const glossaryPath = path48.resolve(root, config.spec.glossary);
|
|
38334
38471
|
const raw = await readTextIfExists(glossaryPath);
|
|
38335
38472
|
if (raw === void 0) {
|
|
38336
38473
|
return jsonResult({
|
|
@@ -38351,7 +38488,7 @@ async function runAtlasGlossary(_args, host) {
|
|
|
38351
38488
|
}
|
|
38352
38489
|
|
|
38353
38490
|
// src/mcp/tools/links.ts
|
|
38354
|
-
import
|
|
38491
|
+
import path49 from "path";
|
|
38355
38492
|
async function runAtlasLinks(_args, host) {
|
|
38356
38493
|
const resolved = await host.getWorkspace();
|
|
38357
38494
|
if (!resolved) return noWorkspaceResult();
|
|
@@ -38362,7 +38499,7 @@ async function runAtlasLinks(_args, host) {
|
|
|
38362
38499
|
return jsonResult({
|
|
38363
38500
|
links: state.entries.map((entry) => ({
|
|
38364
38501
|
name: entry.name,
|
|
38365
|
-
path:
|
|
38502
|
+
path: path49.relative(resolved.root, entry.path),
|
|
38366
38503
|
available: entry.available,
|
|
38367
38504
|
requirements: entry.requirements,
|
|
38368
38505
|
domains: entry.domains
|
|
@@ -38399,7 +38536,7 @@ async function runAtlasImpact(args, host) {
|
|
|
38399
38536
|
}
|
|
38400
38537
|
|
|
38401
38538
|
// src/evaluate.ts
|
|
38402
|
-
import
|
|
38539
|
+
import path50 from "path";
|
|
38403
38540
|
function livingRequirementsMap2(specs) {
|
|
38404
38541
|
const map = /* @__PURE__ */ new Map();
|
|
38405
38542
|
for (const spec of specs) {
|
|
@@ -38408,7 +38545,7 @@ function livingRequirementsMap2(specs) {
|
|
|
38408
38545
|
return map;
|
|
38409
38546
|
}
|
|
38410
38547
|
async function evaluateChange(workspace, config, change, approvals) {
|
|
38411
|
-
const deltaPath =
|
|
38548
|
+
const deltaPath = path50.join(change.dir, "spec.md");
|
|
38412
38549
|
const deltaContent = await readTextIfExists(deltaPath);
|
|
38413
38550
|
const approval = verifyApproval(change, approvals, config, deltaContent ?? void 0);
|
|
38414
38551
|
const living = livingRequirementsMap2(workspace.specs);
|
|
@@ -38704,8 +38841,8 @@ var WorkspaceCache = class {
|
|
|
38704
38841
|
}
|
|
38705
38842
|
};
|
|
38706
38843
|
async function workspaceStamp(root) {
|
|
38707
|
-
const sddDir =
|
|
38708
|
-
const markers = [sddDir,
|
|
38844
|
+
const sddDir = path51.join(root, ".sdd");
|
|
38845
|
+
const markers = [sddDir, path51.join(sddDir, "config.yaml"), path51.join(sddDir, "changes"), path51.join(sddDir, "specs"), path51.join(sddDir, "approvals.yaml")];
|
|
38709
38846
|
let stamp2 = 0;
|
|
38710
38847
|
for (const marker of markers) {
|
|
38711
38848
|
try {
|
|
@@ -38723,7 +38860,7 @@ async function resolveMcpWorkspace(cwd, cache) {
|
|
|
38723
38860
|
const cached2 = cache.get(root);
|
|
38724
38861
|
if (cached2 && cached2.mtimeMs === stamp2) return cached2.value;
|
|
38725
38862
|
const { workspace, config } = await loadWorkspace(root);
|
|
38726
|
-
const approvals = await loadApprovals(
|
|
38863
|
+
const approvals = await loadApprovals(path51.join(root, ".sdd"));
|
|
38727
38864
|
const value = { root, workspace, config, approvals: approvals.byArtifact };
|
|
38728
38865
|
cache.set(root, stamp2, value);
|
|
38729
38866
|
return value;
|
|
@@ -38815,7 +38952,7 @@ async function runMcp(ctx) {
|
|
|
38815
38952
|
}
|
|
38816
38953
|
|
|
38817
38954
|
// src/commands/packs.ts
|
|
38818
|
-
import
|
|
38955
|
+
import path52 from "path";
|
|
38819
38956
|
async function runPacks(ctx) {
|
|
38820
38957
|
const { root, config, workspace } = await requireWorkspace(ctx);
|
|
38821
38958
|
const slug = flagString(ctx.flags, "check");
|
|
@@ -38873,14 +39010,14 @@ async function runPacks(ctx) {
|
|
|
38873
39010
|
data: {
|
|
38874
39011
|
slug,
|
|
38875
39012
|
packs: evaluations.map((evaluation) => ({ id: evaluation.pack.id, status: evaluation.status, passed: evaluation.passed, failed: evaluation.failed, results: evaluation.results })),
|
|
38876
|
-
analyzePath:
|
|
39013
|
+
analyzePath: path52.posix.join(".sdd", "changes", slug, "analyze.md")
|
|
38877
39014
|
},
|
|
38878
39015
|
text: lines
|
|
38879
39016
|
};
|
|
38880
39017
|
}
|
|
38881
39018
|
|
|
38882
39019
|
// src/commands/pause.ts
|
|
38883
|
-
import
|
|
39020
|
+
import path53 from "path";
|
|
38884
39021
|
async function runPause(ctx) {
|
|
38885
39022
|
const slug = ctx.positionals[0];
|
|
38886
39023
|
if (!slug) {
|
|
@@ -38910,7 +39047,7 @@ async function runPause(ctx) {
|
|
|
38910
39047
|
` motivo: ${result.paused?.reason ?? ""}`,
|
|
38911
39048
|
` por: ${result.paused?.by ?? ""}`,
|
|
38912
39049
|
` fecha: ${result.paused?.at ?? ""}`,
|
|
38913
|
-
` estado: ${
|
|
39050
|
+
` estado: ${path53.relative(ctx.cwd, result.path)}`,
|
|
38914
39051
|
"",
|
|
38915
39052
|
`Ret\xF3malo cuando quieras con \`satlas resume ${slug}\`.`
|
|
38916
39053
|
];
|
|
@@ -38953,7 +39090,7 @@ async function runResume(ctx) {
|
|
|
38953
39090
|
}
|
|
38954
39091
|
|
|
38955
39092
|
// src/commands/new.ts
|
|
38956
|
-
import
|
|
39093
|
+
import path54 from "path";
|
|
38957
39094
|
async function runNew(ctx) {
|
|
38958
39095
|
const slug = ctx.positionals[0];
|
|
38959
39096
|
if (!slug) {
|
|
@@ -38979,14 +39116,14 @@ async function runNew(ctx) {
|
|
|
38979
39116
|
msg("new.title", ctx.language),
|
|
38980
39117
|
"",
|
|
38981
39118
|
msg("new.done", ctx.language),
|
|
38982
|
-
...result.files.map((f) => ` + ${
|
|
39119
|
+
...result.files.map((f) => ` + ${path54.relative(ctx.cwd, f)}`),
|
|
38983
39120
|
"",
|
|
38984
39121
|
`Siguiente: ${agentCommand("specify", result.slug, config)} \u2014 escribe la especificaci\xF3n 100% funcional y de negocio.`
|
|
38985
39122
|
];
|
|
38986
39123
|
return {
|
|
38987
39124
|
exitCode: hasErrors2 ? 1 : 0,
|
|
38988
39125
|
diagnostics: result.diagnostics,
|
|
38989
|
-
data: { slug: result.slug, files: result.files.map((f) =>
|
|
39126
|
+
data: { slug: result.slug, files: result.files.map((f) => path54.relative(ctx.cwd, f)) },
|
|
38990
39127
|
text: lines
|
|
38991
39128
|
};
|
|
38992
39129
|
}
|
|
@@ -39119,7 +39256,7 @@ async function runNext(ctx) {
|
|
|
39119
39256
|
}
|
|
39120
39257
|
|
|
39121
39258
|
// src/commands/present.ts
|
|
39122
|
-
import
|
|
39259
|
+
import path55 from "path";
|
|
39123
39260
|
async function runPresentCommand(ctx) {
|
|
39124
39261
|
const { root } = await requireWorkspace(ctx);
|
|
39125
39262
|
const slug = ctx.positionals[0];
|
|
@@ -39131,7 +39268,7 @@ async function runPresentCommand(ctx) {
|
|
|
39131
39268
|
if (result.path) {
|
|
39132
39269
|
lines.push("Propuesta generada");
|
|
39133
39270
|
lines.push("");
|
|
39134
|
-
lines.push(` archivo: ${
|
|
39271
|
+
lines.push(` archivo: ${path55.relative(ctx.cwd, result.path)}`);
|
|
39135
39272
|
if (result.hash) lines.push(` hash de la spec: ${shortHash(result.hash)}`);
|
|
39136
39273
|
lines.push("");
|
|
39137
39274
|
lines.push("\xC1brela en el navegador y comp\xE1rtela con el stakeholder.");
|
|
@@ -39141,13 +39278,13 @@ async function runPresentCommand(ctx) {
|
|
|
39141
39278
|
return {
|
|
39142
39279
|
exitCode: hasErrors2 ? 1 : 0,
|
|
39143
39280
|
diagnostics: result.diagnostics,
|
|
39144
|
-
data: result.path ? { path:
|
|
39281
|
+
data: result.path ? { path: path55.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
|
|
39145
39282
|
text: lines
|
|
39146
39283
|
};
|
|
39147
39284
|
}
|
|
39148
39285
|
|
|
39149
39286
|
// src/commands/review.ts
|
|
39150
|
-
import
|
|
39287
|
+
import path56 from "path";
|
|
39151
39288
|
async function runReview(ctx) {
|
|
39152
39289
|
const slug = ctx.positionals[0];
|
|
39153
39290
|
if (!slug) {
|
|
@@ -39159,7 +39296,7 @@ async function runReview(ctx) {
|
|
|
39159
39296
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-REVIEW-000", severity: "error", message: `No existe el cambio "${slug}"` }] };
|
|
39160
39297
|
}
|
|
39161
39298
|
const mode = config.gates.review.mode;
|
|
39162
|
-
const reviewFile =
|
|
39299
|
+
const reviewFile = path56.join(change.dir, "review.md");
|
|
39163
39300
|
if (!await exists(reviewFile)) {
|
|
39164
39301
|
const templates = templatesFor(config.project.language);
|
|
39165
39302
|
await writeText(reviewFile, renderTemplate(templates.review, { TITLE: change.meta?.title ?? slug, SLUG: slug }));
|
|
@@ -39167,7 +39304,7 @@ async function runReview(ctx) {
|
|
|
39167
39304
|
"Revisi\xF3n del cambio",
|
|
39168
39305
|
"",
|
|
39169
39306
|
` modo: ${mode}`,
|
|
39170
|
-
` creado: ${
|
|
39307
|
+
` creado: ${path56.relative(ctx.cwd, reviewFile)}`,
|
|
39171
39308
|
"",
|
|
39172
39309
|
`Revisa el c\xF3digo con la fase del agente: ${agentCommand("review", slug, config)}`,
|
|
39173
39310
|
"Al cerrar, deja `- resultado: pass` en el veredicto y marca los hallazgos resueltos."
|
|
@@ -39180,7 +39317,7 @@ async function runReview(ctx) {
|
|
|
39180
39317
|
const passed = reviewPassed(review);
|
|
39181
39318
|
const lines = ["Revisi\xF3n del cambio", ""];
|
|
39182
39319
|
lines.push(` modo: ${mode}`);
|
|
39183
|
-
lines.push(` archivo: ${
|
|
39320
|
+
lines.push(` archivo: ${path56.relative(ctx.cwd, reviewFile)}`);
|
|
39184
39321
|
lines.push(` resultado: ${review?.verdict ?? "pendiente"}${review?.by ? ` \xB7 por ${review.by}` : ""}${review?.date ? ` \xB7 ${review.date}` : ""}`);
|
|
39185
39322
|
lines.push(` hallazgos: ${review?.findings.length ?? 0} (${open.length} sin resolver, ${blocking.length} bloqueante(s))`);
|
|
39186
39323
|
if (open.length > 0) {
|
|
@@ -39282,6 +39419,124 @@ function usage(usageText) {
|
|
|
39282
39419
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-RUN-000", severity: "error", message: `Uso: ${usageText}` }] };
|
|
39283
39420
|
}
|
|
39284
39421
|
|
|
39422
|
+
// src/commands/self-update.ts
|
|
39423
|
+
function rutaEjecutable() {
|
|
39424
|
+
return process.argv[1] ?? process.execPath;
|
|
39425
|
+
}
|
|
39426
|
+
async function instalarUltima(instalacion, cwd) {
|
|
39427
|
+
if (!instalacion.comando) return { ok: false, salida: instalacion.motivo ?? "no se puede actualizar esta instalaci\xF3n" };
|
|
39428
|
+
const resultado = await runProcess(instalacion.comando, { cwd, timeoutMs: 18e4 });
|
|
39429
|
+
return { ok: resultado.ok, salida: `${resultado.stdout}${resultado.stderr}`.trim() };
|
|
39430
|
+
}
|
|
39431
|
+
async function runSelfUpdate(ctx) {
|
|
39432
|
+
const instalada = cliVersion();
|
|
39433
|
+
const soloConsultar = flagBool(ctx.flags, "check");
|
|
39434
|
+
const auto = flagString(ctx.flags, "auto");
|
|
39435
|
+
if (auto !== void 0) {
|
|
39436
|
+
const valor = auto.toLowerCase();
|
|
39437
|
+
if (valor !== "on" && valor !== "off") {
|
|
39438
|
+
return {
|
|
39439
|
+
exitCode: 2,
|
|
39440
|
+
diagnostics: [{ code: "ATLAS-SELFUPDATE-000", severity: "error", message: "Valor inv\xE1lido para --auto", suggestion: "satlas self-update --auto on | off" }]
|
|
39441
|
+
};
|
|
39442
|
+
}
|
|
39443
|
+
const preferencias2 = await leerPreferencias();
|
|
39444
|
+
const nuevas = { ...preferencias2, automatica: valor === "on" };
|
|
39445
|
+
const file = await escribirPreferencias(nuevas);
|
|
39446
|
+
return {
|
|
39447
|
+
exitCode: 0,
|
|
39448
|
+
diagnostics: [],
|
|
39449
|
+
data: { automatica: nuevas.automatica, archivo: file },
|
|
39450
|
+
text: [
|
|
39451
|
+
"Actualizaci\xF3n desatendida",
|
|
39452
|
+
"",
|
|
39453
|
+
` ${nuevas.automatica ? "activada: la herramienta se actualizar\xE1 sola al detectar una versi\xF3n nueva" : "desactivada: solo se avisa cuando hay versi\xF3n nueva"}`,
|
|
39454
|
+
` preferencia: ${file}`
|
|
39455
|
+
]
|
|
39456
|
+
};
|
|
39457
|
+
}
|
|
39458
|
+
const publicada = await consultarVersionPublicada();
|
|
39459
|
+
const instalacion = detectarInstalacion(rutaEjecutable());
|
|
39460
|
+
const preferencias = await leerPreferencias();
|
|
39461
|
+
if (publicada === void 0) {
|
|
39462
|
+
return {
|
|
39463
|
+
exitCode: 1,
|
|
39464
|
+
diagnostics: [
|
|
39465
|
+
{
|
|
39466
|
+
code: "ATLAS-SELFUPDATE-001",
|
|
39467
|
+
severity: "error",
|
|
39468
|
+
message: "No se pudo consultar la versi\xF3n publicada",
|
|
39469
|
+
suggestion: `Comprueba tu conexi\xF3n o instala a mano: ${instalacion.comando ?? "npm install -g specatlas@latest"}`
|
|
39470
|
+
}
|
|
39471
|
+
]
|
|
39472
|
+
};
|
|
39473
|
+
}
|
|
39474
|
+
const base = [
|
|
39475
|
+
"Actualizaci\xF3n de SpecAtlas",
|
|
39476
|
+
"",
|
|
39477
|
+
` instalada: ${instalada}`,
|
|
39478
|
+
` publicada: ${publicada}`,
|
|
39479
|
+
` instalada con: ${instalacion.gestor}`,
|
|
39480
|
+
` desatendida: ${preferencias.automatica ? "activada" : "desactivada"}`
|
|
39481
|
+
];
|
|
39482
|
+
if (!hayVersionNueva(instalada, publicada)) {
|
|
39483
|
+
return { exitCode: 0, diagnostics: [], data: { instalada, publicada, actualizada: false, alDia: true }, text: [...base, "", " Ya est\xE1s en la \xFAltima versi\xF3n."] };
|
|
39484
|
+
}
|
|
39485
|
+
if (soloConsultar) {
|
|
39486
|
+
return {
|
|
39487
|
+
exitCode: 0,
|
|
39488
|
+
diagnostics: [],
|
|
39489
|
+
data: { instalada, publicada, actualizada: false, alDia: false, comando: instalacion.comando },
|
|
39490
|
+
text: [...base, "", ` Hay una versi\xF3n nueva. Actualiza con \`satlas self-update\`${instalacion.comando ? ` (o \`${instalacion.comando}\`)` : ""}.`]
|
|
39491
|
+
};
|
|
39492
|
+
}
|
|
39493
|
+
if (!instalacion.comando) {
|
|
39494
|
+
return {
|
|
39495
|
+
exitCode: 0,
|
|
39496
|
+
diagnostics: [{ code: "ATLAS-SELFUPDATE-002", severity: "info", message: instalacion.motivo ?? "esta instalaci\xF3n no se actualiza sola" }],
|
|
39497
|
+
data: { instalada, publicada, actualizada: false, motivo: instalacion.motivo },
|
|
39498
|
+
text: [...base, "", ` ${instalacion.motivo}`]
|
|
39499
|
+
};
|
|
39500
|
+
}
|
|
39501
|
+
const resultado = await instalarUltima(instalacion, ctx.cwd);
|
|
39502
|
+
if (!resultado.ok) {
|
|
39503
|
+
return {
|
|
39504
|
+
exitCode: 1,
|
|
39505
|
+
diagnostics: [
|
|
39506
|
+
{
|
|
39507
|
+
code: "ATLAS-SELFUPDATE-003",
|
|
39508
|
+
severity: "error",
|
|
39509
|
+
message: `No se pudo instalar ${publicada}: ${resultado.salida.split("\n")[0] ?? "el gestor fall\xF3"}`,
|
|
39510
|
+
suggestion: `Ejec\xFAtalo a mano: ${instalacion.comando}`
|
|
39511
|
+
}
|
|
39512
|
+
],
|
|
39513
|
+
data: { instalada, publicada, actualizada: false, comando: instalacion.comando },
|
|
39514
|
+
text: [...base, "", ` La instalaci\xF3n fall\xF3; sigues con ${instalada}.`]
|
|
39515
|
+
};
|
|
39516
|
+
}
|
|
39517
|
+
return {
|
|
39518
|
+
exitCode: 0,
|
|
39519
|
+
diagnostics: [],
|
|
39520
|
+
data: { instalada, publicada, actualizada: true, comando: instalacion.comando },
|
|
39521
|
+
text: [...base, "", ` Actualizada: ${instalada} \u2192 ${publicada}.`, " Abre una terminal nueva si el comando sigue apuntando a la versi\xF3n anterior."]
|
|
39522
|
+
};
|
|
39523
|
+
}
|
|
39524
|
+
async function avisoDeActualizacion(json2, cwd) {
|
|
39525
|
+
try {
|
|
39526
|
+
const aviso = await comprobarActualizacion({ instalada: cliVersion(), rutaEjecutable: rutaEjecutable(), json: json2 });
|
|
39527
|
+
if (!aviso) return [];
|
|
39528
|
+
const preferencias = await leerPreferencias();
|
|
39529
|
+
if (!preferencias.automatica) {
|
|
39530
|
+
return ["", `Hay una versi\xF3n nueva de SpecAtlas: ${aviso.instalada} \u2192 ${aviso.publicada}. Actualiza con \`satlas self-update\`.`];
|
|
39531
|
+
}
|
|
39532
|
+
const instalacion = detectarInstalacion(rutaEjecutable());
|
|
39533
|
+
const resultado = await instalarUltima(instalacion, cwd);
|
|
39534
|
+
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}\`.`];
|
|
39535
|
+
} catch {
|
|
39536
|
+
return [];
|
|
39537
|
+
}
|
|
39538
|
+
}
|
|
39539
|
+
|
|
39285
39540
|
// src/commands/status.ts
|
|
39286
39541
|
async function runStatus(ctx) {
|
|
39287
39542
|
const { root, workspace, config, approvals } = await requireWorkspace(ctx);
|
|
@@ -39356,7 +39611,7 @@ async function runStatus(ctx) {
|
|
|
39356
39611
|
}
|
|
39357
39612
|
|
|
39358
39613
|
// src/commands/trace.ts
|
|
39359
|
-
import
|
|
39614
|
+
import path57 from "path";
|
|
39360
39615
|
async function runTrace(ctx) {
|
|
39361
39616
|
const { workspace, config } = await requireWorkspace(ctx);
|
|
39362
39617
|
const slug = flagString(ctx.flags, "change");
|
|
@@ -39376,7 +39631,7 @@ async function runTrace(ctx) {
|
|
|
39376
39631
|
data.push({ slug: change.slug, nodes: result.graph.nodes.length, edges: result.graph.edges.length, summary: result.summary, findings: result.findings });
|
|
39377
39632
|
lines.push(` ${change.slug}: ${result.graph.nodes.length} nodos, ${result.graph.edges.length} aristas, ${errors} errores, ${result.summary.warnings} avisos`);
|
|
39378
39633
|
for (const finding of result.findings) {
|
|
39379
|
-
const location = finding.path ? ` (${
|
|
39634
|
+
const location = finding.path ? ` (${path57.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
|
|
39380
39635
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}${location}`);
|
|
39381
39636
|
}
|
|
39382
39637
|
}
|
|
@@ -39386,7 +39641,7 @@ async function runTrace(ctx) {
|
|
|
39386
39641
|
}
|
|
39387
39642
|
|
|
39388
39643
|
// src/commands/upgrade.ts
|
|
39389
|
-
import
|
|
39644
|
+
import path58 from "path";
|
|
39390
39645
|
async function runUpgrade(ctx) {
|
|
39391
39646
|
const { root } = await requireWorkspace(ctx);
|
|
39392
39647
|
const apply2 = flagBool(ctx.flags, "apply");
|
|
@@ -39440,7 +39695,7 @@ function renderPreview(plan, root, ctx) {
|
|
|
39440
39695
|
}
|
|
39441
39696
|
for (const item of plan.unreadable) {
|
|
39442
39697
|
lines.push(` Ilegible: "${item.artifact}" \u2014 ${item.reason}`);
|
|
39443
|
-
lines.push(` \u21B3 ${
|
|
39698
|
+
lines.push(` \u21B3 ${path58.relative(ctx.cwd, item.path)}`);
|
|
39444
39699
|
}
|
|
39445
39700
|
return { exitCode: 0, diagnostics: [], data: summarizable(plan), text: lines };
|
|
39446
39701
|
}
|
|
@@ -39503,7 +39758,7 @@ function renderRollback(report, ctx) {
|
|
|
39503
39758
|
}
|
|
39504
39759
|
|
|
39505
39760
|
// src/commands/validate.ts
|
|
39506
|
-
import
|
|
39761
|
+
import path59 from "path";
|
|
39507
39762
|
async function runValidate(ctx) {
|
|
39508
39763
|
const { root, workspace, config, approvals } = await requireWorkspace(ctx);
|
|
39509
39764
|
const slug = flagString(ctx.flags, "change");
|
|
@@ -39530,7 +39785,7 @@ async function runValidate(ctx) {
|
|
|
39530
39785
|
for (const finding of evaluation.lintFindings) {
|
|
39531
39786
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${finding.line ? `:${finding.line}` : ""} \u2014 ${finding.message}`);
|
|
39532
39787
|
}
|
|
39533
|
-
lines.push(` archivo: ${
|
|
39788
|
+
lines.push(` archivo: ${path59.relative(ctx.cwd, path59.join(change.dir, "spec.md"))}`);
|
|
39534
39789
|
}
|
|
39535
39790
|
if (targets.length === 0) lines.push("Sin cambios activos.");
|
|
39536
39791
|
if (advisory && advisory.plan.pending.length > 0) {
|
|
@@ -39558,7 +39813,7 @@ async function runValidate(ctx) {
|
|
|
39558
39813
|
}
|
|
39559
39814
|
|
|
39560
39815
|
// src/commands/verify.ts
|
|
39561
|
-
import
|
|
39816
|
+
import path60 from "path";
|
|
39562
39817
|
async function runVerify(ctx) {
|
|
39563
39818
|
const { root, workspace, config } = await requireWorkspace(ctx);
|
|
39564
39819
|
const slug = ctx.positionals[0];
|
|
@@ -39601,7 +39856,7 @@ async function runVerify(ctx) {
|
|
|
39601
39856
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-VERIFY-001", severity: "error", message: "Falta --by <nombre> (o define meta.owner)" }] };
|
|
39602
39857
|
}
|
|
39603
39858
|
const profilesDir = await resolveProfilesDir();
|
|
39604
|
-
const profile = await loadActiveProfile(
|
|
39859
|
+
const profile = await loadActiveProfile(path60.join(root, ".sdd"), [profilesDir ?? "", path60.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
|
|
39605
39860
|
const record = await recordEvidence({
|
|
39606
39861
|
root,
|
|
39607
39862
|
slug,
|
|
@@ -39623,7 +39878,7 @@ async function runVerify(ctx) {
|
|
|
39623
39878
|
if (record.evidence.command) lines.push(` comando: ${record.evidence.command}`);
|
|
39624
39879
|
lines.push(` resultado: ${record.evidence.result}`);
|
|
39625
39880
|
if (record.evidence.outputHash) lines.push(` hash: ${record.evidence.outputHash}`);
|
|
39626
|
-
lines.push(` archivo: ${
|
|
39881
|
+
lines.push(` archivo: ${path60.relative(ctx.cwd, record.path)}`);
|
|
39627
39882
|
if (record.stdout) {
|
|
39628
39883
|
lines.push("");
|
|
39629
39884
|
lines.push(" salida (\xFAltimas l\xEDneas):");
|
|
@@ -39651,14 +39906,14 @@ async function runVerify(ctx) {
|
|
|
39651
39906
|
return {
|
|
39652
39907
|
exitCode: record.exitCode === 0 ? 0 : 1,
|
|
39653
39908
|
diagnostics: [...record.diagnostics, ...mockupDiagnostics],
|
|
39654
|
-
data: { evidence: record.evidence, path:
|
|
39909
|
+
data: { evidence: record.evidence, path: path60.relative(ctx.cwd, record.path), exitCode: record.exitCode },
|
|
39655
39910
|
text: lines
|
|
39656
39911
|
};
|
|
39657
39912
|
}
|
|
39658
39913
|
|
|
39659
39914
|
// src/commands/watch.ts
|
|
39660
39915
|
import { watch } from "fs";
|
|
39661
|
-
import
|
|
39916
|
+
import path61 from "path";
|
|
39662
39917
|
var DEBOUNCE_MS = 250;
|
|
39663
39918
|
function stamp(now) {
|
|
39664
39919
|
return now.toTimeString().slice(0, 8);
|
|
@@ -39672,7 +39927,7 @@ async function runWatch(ctx) {
|
|
|
39672
39927
|
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
39928
|
};
|
|
39674
39929
|
}
|
|
39675
|
-
const sddDir =
|
|
39930
|
+
const sddDir = path61.join(root, ".sdd");
|
|
39676
39931
|
const write = (line) => {
|
|
39677
39932
|
process.stdout.write(`${line}
|
|
39678
39933
|
`);
|
|
@@ -39690,7 +39945,7 @@ async function runWatch(ctx) {
|
|
|
39690
39945
|
} else {
|
|
39691
39946
|
write(` ${counts.errors} errores, ${counts.warnings} avisos`);
|
|
39692
39947
|
for (const finding of findings.slice(0, 8)) {
|
|
39693
|
-
const where = finding.path ? ` ${
|
|
39948
|
+
const where = finding.path ? ` ${path61.relative(root, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
|
|
39694
39949
|
write(` ${finding.severity === "error" ? "ERROR" : "AVISO"} ${finding.code}${where} \u2014 ${finding.message}`);
|
|
39695
39950
|
}
|
|
39696
39951
|
if (findings.length > 8) write(` \u2026 y ${findings.length - 8} m\xE1s (satlas validate)`);
|
|
@@ -39701,7 +39956,7 @@ async function runWatch(ctx) {
|
|
|
39701
39956
|
}
|
|
39702
39957
|
if (changes.length === 0) write(" sin cambios activos");
|
|
39703
39958
|
};
|
|
39704
|
-
write(`Vigilando ${
|
|
39959
|
+
write(`Vigilando ${path61.relative(ctx.cwd, sddDir) || ".sdd"} \u2014 Ctrl+C para salir`);
|
|
39705
39960
|
await pass();
|
|
39706
39961
|
await new Promise((resolve) => {
|
|
39707
39962
|
let timer;
|
|
@@ -39805,6 +40060,7 @@ var HANDLERS2 = {
|
|
|
39805
40060
|
pause: runPause,
|
|
39806
40061
|
resume: runResume,
|
|
39807
40062
|
archive: runArchive,
|
|
40063
|
+
"self-update": runSelfUpdate,
|
|
39808
40064
|
version: async () => ({ exitCode: 0, data: { version: cliVersion() }, diagnostics: [], text: [`specatlas ${cliVersion()}`] }),
|
|
39809
40065
|
help: (ctx) => runHelp(ctx)
|
|
39810
40066
|
};
|
|
@@ -39834,7 +40090,7 @@ async function requireWorkspace(ctx) {
|
|
|
39834
40090
|
throw new CliError("ATLAS-WS-001", msg("cli.noWorkspace", ctx.language), 2);
|
|
39835
40091
|
}
|
|
39836
40092
|
const { workspace, config } = await loadWorkspace(root);
|
|
39837
|
-
const approvals = await loadApprovals(
|
|
40093
|
+
const approvals = await loadApprovals(path62.join(root, ".sdd"));
|
|
39838
40094
|
return { root, workspace, config, approvals: approvals.byArtifact };
|
|
39839
40095
|
}
|
|
39840
40096
|
var CliError = class extends Error {
|
|
@@ -39885,6 +40141,11 @@ async function main(argv) {
|
|
|
39885
40141
|
try {
|
|
39886
40142
|
const result = await command.handler(ctx);
|
|
39887
40143
|
printResult(parsed.command, result, json2, ctx.language);
|
|
40144
|
+
if (parsed.command !== "self-update" && parsed.command !== "mcp") {
|
|
40145
|
+
const aviso = await avisoDeActualizacion(json2, ctx.cwd);
|
|
40146
|
+
if (aviso.length > 0) process.stdout.write(`${aviso.join("\n")}
|
|
40147
|
+
`);
|
|
40148
|
+
}
|
|
39888
40149
|
return result.exitCode;
|
|
39889
40150
|
} catch (err) {
|
|
39890
40151
|
if (err instanceof CliError) {
|
|
@@ -39903,7 +40164,7 @@ function printResult(command, result, json2, language) {
|
|
|
39903
40164
|
}
|
|
39904
40165
|
const lines = [...result.text ?? []];
|
|
39905
40166
|
for (const d of result.diagnostics) {
|
|
39906
|
-
const location = d.path ? ` ${
|
|
40167
|
+
const location = d.path ? ` ${path62.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
|
|
39907
40168
|
lines.push(`${d.severity === "error" ? "ERROR" : d.severity === "warning" ? "AVISO" : "NOTA"} ${d.code}${location} \u2014 ${d.message}`);
|
|
39908
40169
|
if (d.suggestion) lines.push(` \u21B3 ${d.suggestion}`);
|
|
39909
40170
|
}
|