specatlas 0.1.37 → 0.1.39
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 +267 -60
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -34465,6 +34465,11 @@ function labelsFor(language) {
|
|
|
34465
34465
|
signatureName: "Approver name",
|
|
34466
34466
|
signatureDate: "Signature date",
|
|
34467
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.",
|
|
34468
34473
|
openMockup: "Open mockup",
|
|
34469
34474
|
mockupHint: "Each screen opens full size in its own tab. In a browser they are also previewed inline.",
|
|
34470
34475
|
illustrates: "Illustrates"
|
|
@@ -34496,6 +34501,11 @@ function labelsFor(language) {
|
|
|
34496
34501
|
missingMockups: "Mockups declarados que faltan",
|
|
34497
34502
|
screenshots: "Capturas",
|
|
34498
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.",
|
|
34499
34509
|
signatureName: "Nombre de quien aprueba",
|
|
34500
34510
|
signatureDate: "Fecha de la firma",
|
|
34501
34511
|
signatureNote: "La firma se registra con la huella de la especificaci\xF3n y la fecha.",
|
|
@@ -34578,6 +34588,8 @@ th { background:var(--elev); font-size:11px; text-transform:uppercase; letter-sp
|
|
|
34578
34588
|
.grid-shots { display:grid; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:12px; margin-top:12px; }
|
|
34579
34589
|
.grid-shots img { width:100%; border:1px solid var(--line); border-radius:10px; }
|
|
34580
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; }
|
|
34581
34593
|
.sign-line { border:1px dashed var(--accent); border-radius:8px; padding:16px; background:var(--accent-soft); }
|
|
34582
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; }
|
|
34583
34595
|
.sign-line .sl-row:last-child { border-bottom:0; margin-bottom:0; padding-top:14px; }
|
|
@@ -34624,6 +34636,15 @@ function page(input) {
|
|
|
34624
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>`;
|
|
34625
34637
|
}).join("");
|
|
34626
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>`;
|
|
34627
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(
|
|
34628
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>`
|
|
34629
34650
|
).join("")}</div>`;
|
|
@@ -34718,14 +34739,11 @@ function page(input) {
|
|
|
34718
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>
|
|
34719
34740
|
<div class="card-body">
|
|
34720
34741
|
<div class="signature">
|
|
34721
|
-
|
|
34722
|
-
<div class="sl-row"><span>${esc(l.signatureName)}</span><span style="font-family:ui-monospace,Consolas,monospace">____________________</span></div>
|
|
34723
|
-
<div class="sl-row"><span>${esc(l.signatureDate)}</span><span style="font-family:ui-monospace,Consolas,monospace">____ / ____ / ________</span></div>
|
|
34724
|
-
</div>
|
|
34742
|
+
${signatureBlock}
|
|
34725
34743
|
<div>
|
|
34726
34744
|
<p class="sign-help">${esc(l.signatureNote)}</p>
|
|
34727
|
-
|
|
34728
|
-
|
|
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>` : ""}
|
|
34729
34747
|
</div>
|
|
34730
34748
|
</div>
|
|
34731
34749
|
</div>
|
|
@@ -36802,6 +36820,103 @@ function msg(key, language) {
|
|
|
36802
36820
|
return dict[key] ?? key;
|
|
36803
36821
|
}
|
|
36804
36822
|
|
|
36823
|
+
// src/ui.ts
|
|
36824
|
+
var CODIGOS = {
|
|
36825
|
+
rojo: [31, 39],
|
|
36826
|
+
verde: [32, 39],
|
|
36827
|
+
amarillo: [33, 39],
|
|
36828
|
+
azul: [34, 39],
|
|
36829
|
+
morado: [35, 39],
|
|
36830
|
+
cian: [36, 39],
|
|
36831
|
+
gris: [90, 39],
|
|
36832
|
+
negrita: [1, 22],
|
|
36833
|
+
atenuado: [2, 22],
|
|
36834
|
+
subrayado: [4, 24],
|
|
36835
|
+
inverso: [7, 27]
|
|
36836
|
+
};
|
|
36837
|
+
function detectarTema(env = process.env, salida = process.stdout) {
|
|
36838
|
+
const forzado = env["FORCE_COLOR"];
|
|
36839
|
+
const color = forzado !== void 0 && forzado !== "0" ? true : env["NO_COLOR"] !== void 0 || env["TERM"] === "dumb" || salida.isTTY !== true ? false : true;
|
|
36840
|
+
const unicode = env["SPECATLAS_ASCII"] === "1" ? false : process.platform !== "win32" || Boolean(env["WT_SESSION"] || env["TERM_PROGRAM"] || env["ConEmuANSI"] || env["TERM"]);
|
|
36841
|
+
const ancho = Math.max(40, Math.min(salida.columns ?? 100, 120));
|
|
36842
|
+
return { color, unicode, ancho };
|
|
36843
|
+
}
|
|
36844
|
+
function pinta(tema, estilo, texto) {
|
|
36845
|
+
if (!tema.color || texto === "") return texto;
|
|
36846
|
+
const estilos = Array.isArray(estilo) ? estilo : [estilo];
|
|
36847
|
+
return estilos.reduce((acc, uno) => {
|
|
36848
|
+
const [abre, cierra] = CODIGOS[uno];
|
|
36849
|
+
return `\x1B[${abre}m${acc}\x1B[${cierra}m`;
|
|
36850
|
+
}, texto);
|
|
36851
|
+
}
|
|
36852
|
+
var UNICODE = { ok: "\u2713", error: "\u2716", aviso: "\u25B2", info: "\u2139", flecha: "\u2192", punto: "\u25CF", bloqueo: "\u2298", pausa: "\u23F8", lleno: "\u25B0", vacio: "\u25B1", separador: "\xB7" };
|
|
36853
|
+
var ASCII = { ok: "v", error: "x", aviso: "!", info: "i", flecha: "->", punto: "*", bloqueo: "o", pausa: "||", lleno: "#", vacio: "-", separador: "-" };
|
|
36854
|
+
function simbolos(tema) {
|
|
36855
|
+
return tema.unicode ? UNICODE : ASCII;
|
|
36856
|
+
}
|
|
36857
|
+
function anchoVisible(texto) {
|
|
36858
|
+
return texto.replace(/\u001b\[[0-9;]*m/g, "").length;
|
|
36859
|
+
}
|
|
36860
|
+
function rellenar(texto, ancho) {
|
|
36861
|
+
const falta = ancho - anchoVisible(texto);
|
|
36862
|
+
return falta > 0 ? texto + " ".repeat(falta) : texto;
|
|
36863
|
+
}
|
|
36864
|
+
function encabezado(tema, titulo, contexto) {
|
|
36865
|
+
const izquierda = pinta(tema, "negrita", titulo);
|
|
36866
|
+
if (!contexto) return [izquierda, ""];
|
|
36867
|
+
const hueco = tema.ancho - anchoVisible(izquierda) - anchoVisible(contexto);
|
|
36868
|
+
if (hueco < 2) return [izquierda, pinta(tema, "gris", contexto), ""];
|
|
36869
|
+
return [`${izquierda}${" ".repeat(hueco)}${pinta(tema, "gris", contexto)}`, ""];
|
|
36870
|
+
}
|
|
36871
|
+
function barraProgreso(tema, hecho, total, ancho = 8) {
|
|
36872
|
+
const s = simbolos(tema);
|
|
36873
|
+
if (total <= 0) return pinta(tema, "gris", `${s.vacio.repeat(ancho)} 0/0`);
|
|
36874
|
+
const llenos = Math.max(0, Math.min(ancho, Math.round(hecho / total * ancho)));
|
|
36875
|
+
const completo = hecho >= total;
|
|
36876
|
+
const barra = `${s.lleno.repeat(llenos)}${s.vacio.repeat(ancho - llenos)}`;
|
|
36877
|
+
return `${pinta(tema, completo ? "verde" : "azul", barra)} ${hecho}/${total}`;
|
|
36878
|
+
}
|
|
36879
|
+
function marcaSeveridad(tema, severidad) {
|
|
36880
|
+
const s = simbolos(tema);
|
|
36881
|
+
if (severidad === "error") return pinta(tema, "rojo", s.error);
|
|
36882
|
+
if (severidad === "warning") return pinta(tema, "amarillo", s.aviso);
|
|
36883
|
+
return pinta(tema, "azul", s.info);
|
|
36884
|
+
}
|
|
36885
|
+
function formatearDiagnostico(tema, d, rutaRelativa) {
|
|
36886
|
+
const s = simbolos(tema);
|
|
36887
|
+
const ubicacion = rutaRelativa ? pinta(tema, "gris", `${rutaRelativa}${d.line ? `:${d.line}` : ""}`) : "";
|
|
36888
|
+
const codigo = pinta(tema, d.severity === "error" ? ["rojo", "negrita"] : d.severity === "warning" ? ["amarillo", "negrita"] : ["azul", "negrita"], d.code);
|
|
36889
|
+
const cabeza = [` ${marcaSeveridad(tema, d.severity)} ${codigo}`, ubicacion].filter((parte) => parte !== "").join(" ");
|
|
36890
|
+
const lineas = [cabeza, ` ${d.message}`];
|
|
36891
|
+
if (d.suggestion) lineas.push(pinta(tema, "gris", ` ${s.flecha} ${d.suggestion}`));
|
|
36892
|
+
return lineas;
|
|
36893
|
+
}
|
|
36894
|
+
function resumenHallazgos(tema, counts) {
|
|
36895
|
+
const s = simbolos(tema);
|
|
36896
|
+
if (counts.errors === 0 && counts.warnings === 0 && counts.infos === 0) return "";
|
|
36897
|
+
const partes = [];
|
|
36898
|
+
if (counts.errors > 0) partes.push(pinta(tema, "rojo", `${s.error} ${counts.errors} ${counts.errors === 1 ? "error" : "errores"}`));
|
|
36899
|
+
if (counts.warnings > 0) partes.push(pinta(tema, "amarillo", `${s.aviso} ${counts.warnings} ${counts.warnings === 1 ? "aviso" : "avisos"}`));
|
|
36900
|
+
if (counts.infos > 0) partes.push(pinta(tema, "azul", `${s.info} ${counts.infos} ${counts.infos === 1 ? "nota" : "notas"}`));
|
|
36901
|
+
return ` ${partes.join(pinta(tema, "gris", ` ${s.separador} `))}`;
|
|
36902
|
+
}
|
|
36903
|
+
function exito(tema, texto) {
|
|
36904
|
+
return ` ${pinta(tema, "verde", simbolos(tema).ok)} ${texto}`;
|
|
36905
|
+
}
|
|
36906
|
+
function siguienteAccion(tema, comando, descripcion) {
|
|
36907
|
+
const s = simbolos(tema);
|
|
36908
|
+
const lineas = [` ${pinta(tema, "cian", s.flecha)} ${pinta(tema, ["cian", "negrita"], comando)}`];
|
|
36909
|
+
if (descripcion) lineas.push(pinta(tema, "gris", ` ${descripcion}`));
|
|
36910
|
+
return lineas;
|
|
36911
|
+
}
|
|
36912
|
+
function filaAlineada(columnas, separacion = 2) {
|
|
36913
|
+
return columnas.map((columna, indice) => indice === columnas.length - 1 || columna.ancho === void 0 ? columna.texto : rellenar(columna.texto, columna.ancho)).join(" ".repeat(separacion));
|
|
36914
|
+
}
|
|
36915
|
+
function seccion(tema, titulo, detalle) {
|
|
36916
|
+
const cabeza = pinta(tema, ["negrita"], titulo);
|
|
36917
|
+
return detalle ? `${cabeza} ${pinta(tema, "gris", detalle)}` : cabeza;
|
|
36918
|
+
}
|
|
36919
|
+
|
|
36805
36920
|
// src/version.ts
|
|
36806
36921
|
import { readFileSync } from "fs";
|
|
36807
36922
|
import { fileURLToPath } from "url";
|
|
@@ -37553,6 +37668,18 @@ async function runApprove(ctx) {
|
|
|
37553
37668
|
lines.push(` fecha: ${result.approval.approvedAt}`);
|
|
37554
37669
|
lines.push("");
|
|
37555
37670
|
lines.push("La firma desbloquea el plan. Si el artefacto cambia, la firma queda obsoleta autom\xE1ticamente.");
|
|
37671
|
+
const slug = change?.slug;
|
|
37672
|
+
if (slug && !flagBool(ctx.flags, "dry-run")) {
|
|
37673
|
+
const presentation = path36.join(root, ".sdd", "changes", slug, "presentation", "index.html");
|
|
37674
|
+
if (await exists(presentation)) {
|
|
37675
|
+
try {
|
|
37676
|
+
await generatePresentation({ root, slug });
|
|
37677
|
+
lines.push(`Propuesta actualizada con la firma: ${path36.relative(ctx.cwd, presentation)}`);
|
|
37678
|
+
} catch (error2) {
|
|
37679
|
+
lines.push(`La propuesta no se pudo rehacer (${error2.message}); vuelve a generarla con \`satlas present ${slug}\`.`);
|
|
37680
|
+
}
|
|
37681
|
+
}
|
|
37682
|
+
}
|
|
37556
37683
|
}
|
|
37557
37684
|
const hasErrors2 = result.diagnostics.some((d) => d.severity === "error");
|
|
37558
37685
|
return {
|
|
@@ -37635,7 +37762,7 @@ async function runCi(ctx) {
|
|
|
37635
37762
|
}
|
|
37636
37763
|
const gate = await runCiGate({ root, strict, extra });
|
|
37637
37764
|
const diagnostics = [...gate.diagnostics];
|
|
37638
|
-
const lines =
|
|
37765
|
+
const lines = encabezado(detectarTema(), "Comprobaci\xF3n continua", strict ? "modo estricto" : void 0);
|
|
37639
37766
|
let sarifWritten;
|
|
37640
37767
|
if (sarifPath) {
|
|
37641
37768
|
const target = path38.resolve(ctx.cwd, sarifPath);
|
|
@@ -37651,13 +37778,18 @@ async function runCi(ctx) {
|
|
|
37651
37778
|
});
|
|
37652
37779
|
}
|
|
37653
37780
|
}
|
|
37781
|
+
const tema = detectarTema();
|
|
37782
|
+
const s = simbolos(tema);
|
|
37654
37783
|
for (const check of gate.checks) {
|
|
37655
|
-
const
|
|
37656
|
-
|
|
37784
|
+
const marca = check.errors > 0 ? pinta(tema, "rojo", s.error) : check.warnings > 0 ? pinta(tema, "amarillo", s.aviso) : pinta(tema, "verde", s.ok);
|
|
37785
|
+
const detalle = check.errors === 0 && check.warnings === 0 ? pinta(tema, "gris", "sin hallazgos") : [check.errors > 0 ? pinta(tema, "rojo", `${check.errors} ${check.errors === 1 ? "error" : "errores"}`) : "", check.warnings > 0 ? pinta(tema, "amarillo", `${check.warnings} ${check.warnings === 1 ? "aviso" : "avisos"}`) : ""].filter((parte) => parte !== "").join(pinta(tema, "gris", ` ${s.separador} `));
|
|
37786
|
+
lines.push(filaAlineada([{ texto: ` ${marca} ${check.name}`, ancho: 46 }, { texto: detalle }]));
|
|
37657
37787
|
}
|
|
37658
37788
|
lines.push("");
|
|
37659
|
-
lines.push(
|
|
37660
|
-
|
|
37789
|
+
lines.push(
|
|
37790
|
+
gate.failed ? ` ${pinta(tema, ["rojo", "negrita"], `${s.error} BLOQUEADO`)}${pinta(tema, "gris", " \u2014 resuelve los hallazgos antes de continuar")}` : ` ${pinta(tema, ["verde", "negrita"], `${s.ok} TODO EN ORDEN`)}`
|
|
37791
|
+
);
|
|
37792
|
+
if (strict) lines.push(pinta(tema, "gris", " (modo estricto: los avisos tambi\xE9n bloquean)"));
|
|
37661
37793
|
if (sarifPath) {
|
|
37662
37794
|
lines.push(sarifWritten ? `Informe SARIF: ${sarifWritten} (${gate.diagnostics.length} hallazgo(s))` : `Informe SARIF: no se pudo escribir en "${sarifPath}"`);
|
|
37663
37795
|
}
|
|
@@ -38042,7 +38174,7 @@ assumptions: []
|
|
|
38042
38174
|
}
|
|
38043
38175
|
|
|
38044
38176
|
// src/commands/doctor.ts
|
|
38045
|
-
import
|
|
38177
|
+
import "path";
|
|
38046
38178
|
async function runDoctorCommand(ctx) {
|
|
38047
38179
|
const { root, config } = await requireWorkspace(ctx);
|
|
38048
38180
|
const report = await runDoctor(root);
|
|
@@ -38062,17 +38194,10 @@ async function runDoctorCommand(ctx) {
|
|
|
38062
38194
|
const advisory = await upgradeAdvisory(root);
|
|
38063
38195
|
report.findings.push(...advisory.diagnostics);
|
|
38064
38196
|
report.summary = countBySeverity(report.findings);
|
|
38065
|
-
const
|
|
38197
|
+
const tema = detectarTema();
|
|
38198
|
+
const lines = encabezado(tema, msg("doctor.title", ctx.language));
|
|
38066
38199
|
if (report.findings.length === 0) {
|
|
38067
|
-
lines.push("
|
|
38068
|
-
} else {
|
|
38069
|
-
for (const finding of report.findings) {
|
|
38070
|
-
const location = finding.path ? ` ${path44.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
|
|
38071
|
-
lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${location} \u2014 ${finding.message}`);
|
|
38072
|
-
if (finding.suggestion) lines.push(` \u21B3 ${finding.suggestion}`);
|
|
38073
|
-
}
|
|
38074
|
-
lines.push("");
|
|
38075
|
-
lines.push(`${report.summary.errors} errores, ${report.summary.warnings} avisos, ${report.summary.infos} notas`);
|
|
38200
|
+
lines.push(exito(tema, "El workspace est\xE1 sano."));
|
|
38076
38201
|
}
|
|
38077
38202
|
return {
|
|
38078
38203
|
exitCode: report.summary.errors > 0 ? 1 : 0,
|
|
@@ -39237,21 +39362,28 @@ async function runNext(ctx) {
|
|
|
39237
39362
|
text: [`\u25B6 ${action.command}`, "", ...outcome.result.text ?? []]
|
|
39238
39363
|
};
|
|
39239
39364
|
}
|
|
39240
|
-
const
|
|
39365
|
+
const tema = detectarTema();
|
|
39366
|
+
const s = simbolos(tema);
|
|
39367
|
+
const lines = encabezado(tema, msg("next.title", ctx.language));
|
|
39241
39368
|
const data = [];
|
|
39242
39369
|
let hasErrors2 = false;
|
|
39243
39370
|
for (const change of targets) {
|
|
39244
39371
|
const evaluation = await evaluateChange(workspace, config, change, approvals);
|
|
39245
39372
|
if (evaluation.blocking > 0) hasErrors2 = true;
|
|
39246
39373
|
const { state, nextAction, blockedBy } = evaluation.state;
|
|
39247
|
-
|
|
39248
|
-
lines.push(
|
|
39249
|
-
|
|
39250
|
-
|
|
39374
|
+
const actor = nextAction.requiresAgent ? pinta(tema, "azul", "con agente") : /^satlas (approve|archive|amend|pause|resume)/.test(nextAction.command) ? pinta(tema, "morado", "humana") : pinta(tema, "verde", "local");
|
|
39375
|
+
lines.push(
|
|
39376
|
+
filaAlineada([
|
|
39377
|
+
{ texto: ` ${pinta(tema, "negrita", change.slug)}`, ancho: 30 },
|
|
39378
|
+
{ texto: pinta(tema, "gris", stateLabel(state)) }
|
|
39379
|
+
])
|
|
39380
|
+
);
|
|
39381
|
+
lines.push(...siguienteAccion(tema, nextAction.command, `${nextAction.description} ${s.separador} ${actor}`));
|
|
39382
|
+
for (const block of blockedBy) lines.push(` ${pinta(tema, "amarillo", s.bloqueo)} ${pinta(tema, "gris", block)}`);
|
|
39251
39383
|
lines.push("");
|
|
39252
39384
|
data.push({ slug: change.slug, state, next: nextAction.command, blockedBy, requiresAgent: nextAction.requiresAgent });
|
|
39253
39385
|
}
|
|
39254
|
-
if (targets.length === 0) lines.push("Sin cambios activos.");
|
|
39386
|
+
if (targets.length === 0) lines.push(pinta(tema, "gris", " Sin cambios activos."));
|
|
39255
39387
|
return { exitCode: hasErrors2 ? 1 : 0, diagnostics: [], data: { changes: data }, text: lines };
|
|
39256
39388
|
}
|
|
39257
39389
|
|
|
@@ -39538,20 +39670,52 @@ async function avisoDeActualizacion(json2, cwd) {
|
|
|
39538
39670
|
}
|
|
39539
39671
|
|
|
39540
39672
|
// src/commands/status.ts
|
|
39673
|
+
var TONO_ESTADO = {
|
|
39674
|
+
draft: "gris",
|
|
39675
|
+
spec_draft: "amarillo",
|
|
39676
|
+
paused: "amarillo",
|
|
39677
|
+
awaiting_mockups: "morado",
|
|
39678
|
+
awaiting_approval: "morado",
|
|
39679
|
+
approved: "azul",
|
|
39680
|
+
planned: "azul",
|
|
39681
|
+
building: "azul",
|
|
39682
|
+
built: "amarillo",
|
|
39683
|
+
verified: "verde",
|
|
39684
|
+
reviewed: "verde",
|
|
39685
|
+
ready: "verde",
|
|
39686
|
+
archived: "gris"
|
|
39687
|
+
};
|
|
39688
|
+
function marcaDeEstado(tema, estado, bloqueado) {
|
|
39689
|
+
const s = simbolos(tema);
|
|
39690
|
+
if (bloqueado) return pinta(tema, "rojo", s.bloqueo);
|
|
39691
|
+
if (estado === "paused") return pinta(tema, "amarillo", s.pausa);
|
|
39692
|
+
if (estado === "ready") return pinta(tema, "verde", s.ok);
|
|
39693
|
+
return pinta(tema, TONO_ESTADO[estado] ?? "azul", s.punto);
|
|
39694
|
+
}
|
|
39541
39695
|
async function runStatus(ctx) {
|
|
39542
39696
|
const { root, workspace, config, approvals } = await requireWorkspace(ctx);
|
|
39543
39697
|
const advisory = await upgradeAdvisory(root);
|
|
39544
|
-
const
|
|
39698
|
+
const tema = detectarTema();
|
|
39699
|
+
const s = simbolos(tema);
|
|
39700
|
+
const fixes = await loadLivingFixes(root);
|
|
39701
|
+
const contexto = [
|
|
39702
|
+
`${workspace.specs.length} ${workspace.specs.length === 1 ? "spec viva" : "specs vivas"}`,
|
|
39703
|
+
`${workspace.changes.length} ${workspace.changes.length === 1 ? "cambio" : "cambios"}`,
|
|
39704
|
+
fixes.length > 0 ? `${fixes.length} ${fixes.length === 1 ? "fix" : "fixes"}` : ""
|
|
39705
|
+
].filter((parte) => parte !== "").join(` ${s.separador} `);
|
|
39706
|
+
const lines = encabezado(tema, msg("status.title", ctx.language), `${config.project.name} ${s.separador} ${contexto}`);
|
|
39545
39707
|
const changes = [];
|
|
39546
39708
|
if (workspace.changes.length === 0) {
|
|
39547
|
-
lines.push("Sin cambios activos. Crea uno con
|
|
39709
|
+
lines.push(pinta(tema, "gris", ` Sin cambios activos. Crea uno con \`satlas new <slug>\`.`));
|
|
39548
39710
|
}
|
|
39549
39711
|
for (const change of workspace.changes) {
|
|
39550
39712
|
const evaluation = await evaluateChange(workspace, config, change, approvals);
|
|
39551
|
-
const { state, progress, nextAction } = evaluation.state;
|
|
39713
|
+
const { state, progress, nextAction, blockedBy } = evaluation.state;
|
|
39714
|
+
const lane = change.meta?.lane ?? config.lanes.default;
|
|
39715
|
+
const bloqueado = blockedBy.length > 0 || evaluation.blocking > 0;
|
|
39552
39716
|
changes.push({
|
|
39553
39717
|
slug: change.slug,
|
|
39554
|
-
lane
|
|
39718
|
+
lane,
|
|
39555
39719
|
domain: change.meta?.domain,
|
|
39556
39720
|
state,
|
|
39557
39721
|
label: stateLabel(state),
|
|
@@ -39560,27 +39724,48 @@ async function runStatus(ctx) {
|
|
|
39560
39724
|
blocking: evaluation.blocking
|
|
39561
39725
|
});
|
|
39562
39726
|
lines.push(
|
|
39563
|
-
|
|
39564
|
-
|
|
39565
|
-
|
|
39566
|
-
|
|
39727
|
+
filaAlineada([
|
|
39728
|
+
{ texto: ` ${marcaDeEstado(tema, state, bloqueado)} ${pinta(tema, "negrita", change.slug)}`, ancho: 30 },
|
|
39729
|
+
{ texto: pinta(tema, TONO_ESTADO[state] ?? "azul", stateLabel(state)), ancho: 22 },
|
|
39730
|
+
{ texto: pinta(tema, "gris", `${change.meta?.domain ?? "\u2014"} ${s.separador} ${lane}`) }
|
|
39731
|
+
])
|
|
39567
39732
|
);
|
|
39568
|
-
|
|
39733
|
+
const medidas = [
|
|
39734
|
+
`${pinta(tema, "gris", "tareas")} ${barraProgreso(tema, progress.tasksDone, progress.tasksTotal)}`,
|
|
39735
|
+
`${pinta(tema, "gris", "evidencia")} ${barraProgreso(tema, progress.scenariosEvidenced, progress.scenariosTotal)}`
|
|
39736
|
+
];
|
|
39737
|
+
lines.push(` ${medidas.join(pinta(tema, "gris", ` ${s.separador} `))}`);
|
|
39738
|
+
for (const bloqueo of blockedBy) lines.push(` ${pinta(tema, "amarillo", s.bloqueo)} ${pinta(tema, "gris", bloqueo)}`);
|
|
39739
|
+
lines.push(...siguienteAccion(tema, nextAction.command, nextAction.description));
|
|
39740
|
+
lines.push("");
|
|
39569
39741
|
}
|
|
39570
|
-
|
|
39571
|
-
|
|
39572
|
-
|
|
39573
|
-
|
|
39742
|
+
if (workspace.specs.length > 0) {
|
|
39743
|
+
lines.push(seccion(tema, msg("status.specs", ctx.language), `${workspace.specs.length}`));
|
|
39744
|
+
const ancho = Math.max(...workspace.specs.map((spec) => spec.domain.length), 8);
|
|
39745
|
+
for (const spec of workspace.specs) {
|
|
39746
|
+
lines.push(
|
|
39747
|
+
filaAlineada([
|
|
39748
|
+
{ texto: ` ${pinta(tema, "morado", s.punto)} ${spec.domain}`, ancho: ancho + 6 },
|
|
39749
|
+
{ texto: pinta(tema, "gris", `${spec.spec.requirements.length} ${spec.spec.requirements.length === 1 ? "requisito" : "requisitos"}`) }
|
|
39750
|
+
])
|
|
39751
|
+
);
|
|
39752
|
+
}
|
|
39753
|
+
lines.push("");
|
|
39574
39754
|
}
|
|
39575
|
-
|
|
39576
|
-
lines.push("");
|
|
39577
|
-
lines.push(`${msg("status.fixes", ctx.language)}: ${fixes.length}`);
|
|
39755
|
+
lines.push(seccion(tema, msg("status.fixes", ctx.language), `${fixes.length}`));
|
|
39578
39756
|
if (fixes.length === 0) {
|
|
39579
|
-
lines.push(" (se llenan al archivar un fix)");
|
|
39757
|
+
lines.push(pinta(tema, "gris", " (se llenan al archivar un fix)"));
|
|
39580
39758
|
} else {
|
|
39581
39759
|
for (const fix of fixes) {
|
|
39582
|
-
const
|
|
39583
|
-
lines.push(
|
|
39760
|
+
const resultado = fix.result === "pass" ? pinta(tema, "verde", fix.result) : pinta(tema, "rojo", fix.result);
|
|
39761
|
+
lines.push(
|
|
39762
|
+
filaAlineada([
|
|
39763
|
+
{ texto: ` ${pinta(tema, "gris", fix.date || "\u2014")}`, ancho: 14 },
|
|
39764
|
+
{ texto: pinta(tema, "gris", fix.domain ?? "\u2014"), ancho: 14 },
|
|
39765
|
+
{ texto: fix.slug, ancho: 28 },
|
|
39766
|
+
{ texto: `${resultado}${fix.source === "archive" ? pinta(tema, "gris", " (hist\xF3rico)") : ""}` }
|
|
39767
|
+
])
|
|
39768
|
+
);
|
|
39584
39769
|
}
|
|
39585
39770
|
}
|
|
39586
39771
|
const diagnostics = [...workspace.diagnostics, ...advisory.diagnostics];
|
|
@@ -39590,7 +39775,7 @@ async function runStatus(ctx) {
|
|
|
39590
39775
|
diagnostics,
|
|
39591
39776
|
data: {
|
|
39592
39777
|
changes,
|
|
39593
|
-
specs: workspace.specs.map((
|
|
39778
|
+
specs: workspace.specs.map((spec) => ({ domain: spec.domain, requirements: spec.spec.requirements.length })),
|
|
39594
39779
|
fixes: fixes.map((fix) => ({
|
|
39595
39780
|
slug: fix.slug,
|
|
39596
39781
|
date: fix.date,
|
|
@@ -39764,8 +39949,10 @@ async function runValidate(ctx) {
|
|
|
39764
39949
|
const slug = flagString(ctx.flags, "change");
|
|
39765
39950
|
const strict = flagBool(ctx.flags, "strict");
|
|
39766
39951
|
const advisory = slug ? void 0 : await upgradeAdvisory(root);
|
|
39952
|
+
const tema = detectarTema();
|
|
39953
|
+
const s = simbolos(tema);
|
|
39767
39954
|
const diagnostics = [];
|
|
39768
|
-
const lines =
|
|
39955
|
+
const lines = encabezado(tema, msg("validate.title", ctx.language), slug ? slug : void 0);
|
|
39769
39956
|
for (const spec of workspace.specs) {
|
|
39770
39957
|
diagnostics.push(...lintSpec(spec.spec, { language: config.spec.language, businessOnly: config.spec.business_only }));
|
|
39771
39958
|
}
|
|
@@ -39781,11 +39968,15 @@ async function runValidate(ctx) {
|
|
|
39781
39968
|
const evaluation = await evaluateChange(workspace, config, change, approvals);
|
|
39782
39969
|
diagnostics.push(...evaluation.lintFindings);
|
|
39783
39970
|
changes.push({ slug: change.slug, blocking: evaluation.blocking, findings: evaluation.lintFindings.length });
|
|
39784
|
-
|
|
39785
|
-
|
|
39786
|
-
|
|
39787
|
-
|
|
39788
|
-
|
|
39971
|
+
const errores = evaluation.lintFindings.filter((finding) => finding.severity === "error").length;
|
|
39972
|
+
const marca = errores > 0 ? pinta(tema, "rojo", s.error) : evaluation.lintFindings.length > 0 ? pinta(tema, "amarillo", s.aviso) : pinta(tema, "verde", s.ok);
|
|
39973
|
+
lines.push(
|
|
39974
|
+
filaAlineada([
|
|
39975
|
+
{ texto: ` ${marca} ${pinta(tema, "negrita", change.slug)}`, ancho: 30 },
|
|
39976
|
+
{ texto: evaluation.lintFindings.length === 0 ? pinta(tema, "gris", "sin hallazgos") : `${evaluation.lintFindings.length} ${msg("summary.findings", ctx.language)}`, ancho: 20 },
|
|
39977
|
+
{ texto: pinta(tema, "gris", path59.relative(ctx.cwd, path59.join(change.dir, "spec.md"))) }
|
|
39978
|
+
])
|
|
39979
|
+
);
|
|
39789
39980
|
}
|
|
39790
39981
|
if (targets.length === 0) lines.push("Sin cambios activos.");
|
|
39791
39982
|
if (advisory && advisory.plan.pending.length > 0) {
|
|
@@ -39793,7 +39984,10 @@ async function runValidate(ctx) {
|
|
|
39793
39984
|
}
|
|
39794
39985
|
const errors = diagnostics.filter((d) => d.severity === "error").length;
|
|
39795
39986
|
const warnings = diagnostics.filter((d) => d.severity === "warning").length;
|
|
39796
|
-
if (errors === 0 && warnings === 0)
|
|
39987
|
+
if (errors === 0 && warnings === 0) {
|
|
39988
|
+
lines.push("");
|
|
39989
|
+
lines.push(exito(tema, msg("validate.ok", ctx.language)));
|
|
39990
|
+
}
|
|
39797
39991
|
return {
|
|
39798
39992
|
exitCode: errors > 0 || strict && warnings > 0 ? 1 : 0,
|
|
39799
39993
|
diagnostics: advisory ? [...diagnostics, ...advisory.diagnostics] : diagnostics,
|
|
@@ -40162,15 +40356,28 @@ function printResult(command, result, json2, language) {
|
|
|
40162
40356
|
`);
|
|
40163
40357
|
return;
|
|
40164
40358
|
}
|
|
40359
|
+
const tema = detectarTema();
|
|
40165
40360
|
const lines = [...result.text ?? []];
|
|
40361
|
+
if (result.diagnostics.length > 0) lines.push("");
|
|
40166
40362
|
for (const d of result.diagnostics) {
|
|
40167
|
-
const
|
|
40168
|
-
lines.push(
|
|
40169
|
-
|
|
40363
|
+
const ruta = d.path ? path62.relative(process.cwd(), d.path) : void 0;
|
|
40364
|
+
lines.push(
|
|
40365
|
+
...formatearDiagnostico(
|
|
40366
|
+
tema,
|
|
40367
|
+
{
|
|
40368
|
+
code: d.code,
|
|
40369
|
+
severity: d.severity,
|
|
40370
|
+
message: d.message,
|
|
40371
|
+
...d.line !== void 0 ? { line: d.line } : {},
|
|
40372
|
+
...d.suggestion !== void 0 ? { suggestion: d.suggestion } : {}
|
|
40373
|
+
},
|
|
40374
|
+
ruta
|
|
40375
|
+
)
|
|
40376
|
+
);
|
|
40170
40377
|
}
|
|
40171
|
-
const counts = countBySeverity(result.diagnostics);
|
|
40172
40378
|
if (result.diagnostics.length > 0) {
|
|
40173
|
-
lines.push(
|
|
40379
|
+
lines.push("");
|
|
40380
|
+
lines.push(resumenHallazgos(tema, countBySeverity(result.diagnostics)));
|
|
40174
40381
|
}
|
|
40175
40382
|
if (lines.length > 0) process.stdout.write(`${lines.join("\n")}
|
|
40176
40383
|
`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specatlas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"description": "SpecAtlas: kernel determinista de Spec-Driven Development (CLI)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@specatlas/adapters": "0.1.2",
|
|
43
|
-
"@specatlas/core": "0.1.
|
|
43
|
+
"@specatlas/core": "0.1.32"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"typecheck": "tsc --noEmit",
|