specatlas 0.1.38 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin.js +347 -67
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -36820,6 +36820,161 @@ function msg(key, language) {
36820
36820
  return dict[key] ?? key;
36821
36821
  }
36822
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
+ var MARCA = [
36920
+ [20, 184, 166],
36921
+ [37, 99, 235],
36922
+ [124, 58, 237]
36923
+ ];
36924
+ function soportaDegradado(env = process.env) {
36925
+ const colorterm = env["COLORTERM"] ?? "";
36926
+ return colorterm.includes("truecolor") || colorterm.includes("24bit") || env["TERM_PROGRAM"] === "vscode" || Boolean(env["WT_SESSION"]);
36927
+ }
36928
+ function pintaRGB(tema, [r, g, b], texto) {
36929
+ if (!tema.color || texto === "") return texto;
36930
+ return `\x1B[38;2;${r};${g};${b}m${texto}\x1B[39m`;
36931
+ }
36932
+ function mezcla(a, b, t) {
36933
+ return [Math.round(a[0] + (b[0] - a[0]) * t), Math.round(a[1] + (b[1] - a[1]) * t), Math.round(a[2] + (b[2] - a[2]) * t)];
36934
+ }
36935
+ function degradado(tema, texto, paradas = MARCA, env = process.env) {
36936
+ if (!tema.color) return texto;
36937
+ if (!soportaDegradado(env)) return pinta(tema, "cian", texto);
36938
+ const visibles = [...texto];
36939
+ const ultimo = Math.max(1, visibles.length - 1);
36940
+ return visibles.map((caracter, indice) => {
36941
+ if (caracter.trim() === "") return caracter;
36942
+ const posicion = indice / ultimo * (paradas.length - 1);
36943
+ const tramo = Math.min(paradas.length - 2, Math.floor(posicion));
36944
+ return pintaRGB(tema, mezcla(paradas[tramo], paradas[tramo + 1], posicion - tramo), caracter);
36945
+ }).join("");
36946
+ }
36947
+ var LETRAS = {
36948
+ S: ["\u2588\u2588\u2588\u2588\u2588", "\u2588 ", "\u2588\u2588\u2588\u2588\u2588", " \u2588", "\u2588\u2588\u2588\u2588\u2588"],
36949
+ P: ["\u2588\u2588\u2588\u2588\u2588", "\u2588 \u2588", "\u2588\u2588\u2588\u2588\u2588", "\u2588 ", "\u2588 "],
36950
+ E: ["\u2588\u2588\u2588\u2588\u2588", "\u2588 ", "\u2588\u2588\u2588\u2588 ", "\u2588 ", "\u2588\u2588\u2588\u2588\u2588"],
36951
+ C: ["\u2588\u2588\u2588\u2588\u2588", "\u2588 ", "\u2588 ", "\u2588 ", "\u2588\u2588\u2588\u2588\u2588"],
36952
+ A: ["\u2588\u2588\u2588\u2588\u2588", "\u2588 \u2588", "\u2588\u2588\u2588\u2588\u2588", "\u2588 \u2588", "\u2588 \u2588"],
36953
+ T: ["\u2588\u2588\u2588\u2588\u2588", " \u2588 ", " \u2588 ", " \u2588 ", " \u2588 "],
36954
+ L: ["\u2588 ", "\u2588 ", "\u2588 ", "\u2588 ", "\u2588\u2588\u2588\u2588\u2588"]
36955
+ };
36956
+ var ALTO_LETRERO = 5;
36957
+ var NOMBRE = "SPECATLAS";
36958
+ var ANCHO_LETRERO = NOMBRE.length * 6 - 1;
36959
+ function letrero(tema) {
36960
+ if (!tema.unicode || tema.ancho < ANCHO_LETRERO + 4) return void 0;
36961
+ const letras = [...NOMBRE].map((letra) => LETRAS[letra]);
36962
+ if (letras.some((letra) => letra === void 0)) return void 0;
36963
+ return Array.from({ length: ALTO_LETRERO }, (_, fila) => letras.map((letra) => letra[fila]).join(" "));
36964
+ }
36965
+ function banner(tema, version, lema = "el proceso se verifica, no se conf\xEDa") {
36966
+ const pie = pinta(tema, "gris", `${version} ${simbolos(tema).separador} ${lema}`);
36967
+ const letras = letrero(tema);
36968
+ if (!letras) return ["", ` ${degradado(tema, "SpecAtlas")}`, ` ${pie}`, ""];
36969
+ return ["", ...letras.map((fila) => ` ${degradado(tema, fila)}`), ` ${pie}`, ""];
36970
+ }
36971
+ function truncar(texto, maximo) {
36972
+ if (maximo <= 1 || anchoVisible(texto) <= maximo) return texto;
36973
+ const corte = texto.slice(0, maximo - 1);
36974
+ const espacio = corte.lastIndexOf(" ");
36975
+ return `${(espacio > maximo * 0.6 ? corte.slice(0, espacio) : corte).trimEnd()}\u2026`;
36976
+ }
36977
+
36823
36978
  // src/version.ts
36824
36979
  import { readFileSync } from "fs";
36825
36980
  import { fileURLToPath } from "url";
@@ -37665,7 +37820,7 @@ async function runCi(ctx) {
37665
37820
  }
37666
37821
  const gate = await runCiGate({ root, strict, extra });
37667
37822
  const diagnostics = [...gate.diagnostics];
37668
- const lines = ["CI de SpecAtlas", ""];
37823
+ const lines = encabezado(detectarTema(), "Comprobaci\xF3n continua", strict ? "modo estricto" : void 0);
37669
37824
  let sarifWritten;
37670
37825
  if (sarifPath) {
37671
37826
  const target = path38.resolve(ctx.cwd, sarifPath);
@@ -37681,13 +37836,18 @@ async function runCi(ctx) {
37681
37836
  });
37682
37837
  }
37683
37838
  }
37839
+ const tema = detectarTema();
37840
+ const s = simbolos(tema);
37684
37841
  for (const check of gate.checks) {
37685
- const mark = check.errors > 0 ? "FALLA" : check.warnings > 0 ? "AVISO" : "OK ";
37686
- lines.push(` ${mark} ${check.name}: ${check.errors} errores, ${check.warnings} avisos`);
37842
+ const marca = check.errors > 0 ? pinta(tema, "rojo", s.error) : check.warnings > 0 ? pinta(tema, "amarillo", s.aviso) : pinta(tema, "verde", s.ok);
37843
+ 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} `));
37844
+ lines.push(filaAlineada([{ texto: ` ${marca} ${check.name}`, ancho: 46 }, { texto: detalle }]));
37687
37845
  }
37688
37846
  lines.push("");
37689
- lines.push(gate.failed ? "Resultado: BLOQUEADO" : "Resultado: OK");
37690
- if (strict) lines.push("(modo estricto: los avisos tambi\xE9n bloquean)");
37847
+ lines.push(
37848
+ 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`)}`
37849
+ );
37850
+ if (strict) lines.push(pinta(tema, "gris", " (modo estricto: los avisos tambi\xE9n bloquean)"));
37691
37851
  if (sarifPath) {
37692
37852
  lines.push(sarifWritten ? `Informe SARIF: ${sarifWritten} (${gate.diagnostics.length} hallazgo(s))` : `Informe SARIF: no se pudo escribir en "${sarifPath}"`);
37693
37853
  }
@@ -38072,7 +38232,7 @@ assumptions: []
38072
38232
  }
38073
38233
 
38074
38234
  // src/commands/doctor.ts
38075
- import path44 from "path";
38235
+ import "path";
38076
38236
  async function runDoctorCommand(ctx) {
38077
38237
  const { root, config } = await requireWorkspace(ctx);
38078
38238
  const report = await runDoctor(root);
@@ -38092,17 +38252,10 @@ async function runDoctorCommand(ctx) {
38092
38252
  const advisory = await upgradeAdvisory(root);
38093
38253
  report.findings.push(...advisory.diagnostics);
38094
38254
  report.summary = countBySeverity(report.findings);
38095
- const lines = [msg("doctor.title", ctx.language), ""];
38255
+ const tema = detectarTema();
38256
+ const lines = encabezado(tema, msg("doctor.title", ctx.language));
38096
38257
  if (report.findings.length === 0) {
38097
- lines.push("Sin hallazgos. El workspace est\xE1 sano.");
38098
- } else {
38099
- for (const finding of report.findings) {
38100
- const location = finding.path ? ` ${path44.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
38101
- lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${location} \u2014 ${finding.message}`);
38102
- if (finding.suggestion) lines.push(` \u21B3 ${finding.suggestion}`);
38103
- }
38104
- lines.push("");
38105
- lines.push(`${report.summary.errors} errores, ${report.summary.warnings} avisos, ${report.summary.infos} notas`);
38258
+ lines.push(exito(tema, "El workspace est\xE1 sano."));
38106
38259
  }
38107
38260
  return {
38108
38261
  exitCode: report.summary.errors > 0 ? 1 : 0,
@@ -38183,27 +38336,72 @@ async function runHash(ctx) {
38183
38336
  }
38184
38337
 
38185
38338
  // src/commands/help.ts
38339
+ var GRUPOS = [
38340
+ { titulo: "Empezar", detalle: "poner el flujo en marcha", comandos: ["init", "adopt", "new"] },
38341
+ { titulo: "El ciclo", detalle: "de lo acordado a la evidencia", comandos: ["approve", "amend", "verify", "review", "archive", "pause", "resume"] },
38342
+ { titulo: "Saber d\xF3nde est\xE1s", detalle: "qu\xE9 toca y c\xF3mo va", comandos: ["status", "next", "watch", "metrics", "impact", "explain"] },
38343
+ { titulo: "Comprobar", detalle: "que encaja y que funciona", comandos: ["validate", "trace", "waves", "analyze", "ci", "doctor", "drift", "packs", "contracts", "clarify"] },
38344
+ { titulo: "Compartir", detalle: "lo que otros van a leer", comandos: ["present", "docs", "mockup", "issue"] },
38345
+ { titulo: "El proyecto", detalle: "configuraci\xF3n y herramientas", comandos: ["adapters", "profile", "link", "upgrade", "self-update", "hash", "run", "mcp", "version", "help"] }
38346
+ ];
38347
+ var TONOS = ["verde", "cian", "azul", "amarillo", "morado", "gris"];
38348
+ function detalleDeComando(tema, nombre) {
38349
+ const spec = CATALOG.find((item) => item.name === nombre);
38350
+ if (!spec) return [];
38351
+ const s = simbolos(tema);
38352
+ return [
38353
+ "",
38354
+ ` ${pinta(tema, ["negrita"], `satlas ${spec.name}`)} ${pinta(tema, "gris", spec.description)}`,
38355
+ "",
38356
+ ` ${pinta(tema, "cian", s.flecha)} ${pinta(tema, "cian", spec.usage ?? `satlas ${spec.name}`)}`,
38357
+ ...spec.flags.length > 0 ? ["", ` ${pinta(tema, "gris", "opciones:")} ${spec.flags.map((flag3) => pinta(tema, "gris", `--${flag3}`)).join(pinta(tema, "gris", " \xB7 "))}`] : [],
38358
+ ""
38359
+ ];
38360
+ }
38186
38361
  async function runHelp(ctx, commandName) {
38187
38362
  const language = ctx.language;
38188
- const lines = [];
38189
- lines.push(`${msg("cli.title", language)} ${cliVersion()} \u2014 kernel determinista de Spec-Driven Development`);
38190
- lines.push("");
38363
+ const tema = detectarTema();
38364
+ const s = simbolos(tema);
38191
38365
  if (commandName) {
38192
- const spec = CATALOG.find((c) => c.name === commandName);
38366
+ const spec = CATALOG.find((item) => item.name === commandName);
38193
38367
  if (!spec) {
38194
- return { exitCode: 2, diagnostics: [{ code: "ATLAS-CLI-001", severity: "error", message: `${msg("cli.unknownCommand", language)}: ${commandName}` }], text: lines };
38368
+ return {
38369
+ exitCode: 2,
38370
+ diagnostics: [{ code: "ATLAS-CLI-001", severity: "error", message: `${msg("cli.unknownCommand", language)}: ${commandName}`, suggestion: "Ejecuta `satlas help` para ver los comandos por grupo" }]
38371
+ };
38195
38372
  }
38196
- lines.push(`satlas ${spec.name} \u2014 ${spec.description}`);
38197
- lines.push(` ${spec.usage ?? `satlas ${spec.name}`}`);
38198
- return { exitCode: 0, diagnostics: [], text: lines };
38373
+ return { exitCode: 0, diagnostics: [], text: detalleDeComando(tema, commandName) };
38199
38374
  }
38200
- lines.push(msg("cli.usage", language));
38375
+ const lines = banner(tema, cliVersion());
38376
+ lines.push(filaAlineada([{ texto: ` ${pinta(tema, "gris", "uso")}`, ancho: 8 }, { texto: pinta(tema, "negrita", "satlas <comando> [opciones]") }]));
38377
+ lines.push(filaAlineada([{ texto: ` ${pinta(tema, "gris", "ayuda")}`, ancho: 8 }, { texto: pinta(tema, "gris", "satlas help <comando> \xB7 todos aceptan --json") }]));
38201
38378
  lines.push("");
38202
- for (const spec of CATALOG) {
38203
- lines.push(` ${spec.name.padEnd(9)} ${spec.description}`);
38379
+ lines.push(` ${pinta(tema, "cian", s.flecha)} ${pinta(tema, ["cian", "negrita"], "satlas next")} ${pinta(tema, "gris", "qu\xE9 toca hacer ahora mismo en este proyecto")}`);
38380
+ lines.push("");
38381
+ const ancho = Math.max(...CATALOG.map((spec) => spec.name.length)) + 2;
38382
+ const anchoDescripcion = Math.max(24, tema.ancho - ancho - 8);
38383
+ GRUPOS.forEach((grupo, indice) => {
38384
+ const disponibles = grupo.comandos.filter((nombre) => CATALOG.some((spec) => spec.name === nombre));
38385
+ if (disponibles.length === 0) return;
38386
+ lines.push(` ${pinta(tema, [TONOS[indice % TONOS.length], "negrita"], grupo.titulo.toUpperCase())} ${pinta(tema, "gris", grupo.detalle)}`);
38387
+ for (const nombre of disponibles) {
38388
+ const spec = CATALOG.find((item) => item.name === nombre);
38389
+ lines.push(filaAlineada([{ texto: ` ${pinta(tema, "negrita", spec.name)}`, ancho: ancho + 4 }, { texto: pinta(tema, "gris", truncar(spec.description, anchoDescripcion)) }]));
38390
+ }
38391
+ lines.push("");
38392
+ });
38393
+ const agrupados = new Set(GRUPOS.flatMap((grupo) => grupo.comandos));
38394
+ const sueltos = CATALOG.filter((spec) => !agrupados.has(spec.name));
38395
+ if (sueltos.length > 0) {
38396
+ lines.push(` ${pinta(tema, ["gris", "negrita"], "OTROS")}`);
38397
+ for (const spec of sueltos) {
38398
+ lines.push(filaAlineada([{ texto: ` ${pinta(tema, "negrita", spec.name)}`, ancho: ancho + 4 }, { texto: pinta(tema, "gris", truncar(spec.description, anchoDescripcion)) }]));
38399
+ }
38400
+ lines.push("");
38204
38401
  }
38402
+ lines.push(` ${pinta(tema, "gris", `salida: 0 ${s.separador} 1 hallazgos ${s.separador} 2 uso ${s.separador} 3 E/S`)}`);
38403
+ lines.push(` ${pinta(tema, "gris", "documentaci\xF3n: ")}${degradado(tema, "github.com/AlonsoAM/specatlas")}`);
38205
38404
  lines.push("");
38206
- lines.push("Documentaci\xF3n: ARQUITECTURA.md y PROPUESTA.md en el repositorio.");
38207
38405
  return { exitCode: 0, diagnostics: [], text: lines };
38208
38406
  }
38209
38407
 
@@ -39267,21 +39465,28 @@ async function runNext(ctx) {
39267
39465
  text: [`\u25B6 ${action.command}`, "", ...outcome.result.text ?? []]
39268
39466
  };
39269
39467
  }
39270
- const lines = [msg("next.title", ctx.language), ""];
39468
+ const tema = detectarTema();
39469
+ const s = simbolos(tema);
39470
+ const lines = encabezado(tema, msg("next.title", ctx.language));
39271
39471
  const data = [];
39272
39472
  let hasErrors2 = false;
39273
39473
  for (const change of targets) {
39274
39474
  const evaluation = await evaluateChange(workspace, config, change, approvals);
39275
39475
  if (evaluation.blocking > 0) hasErrors2 = true;
39276
39476
  const { state, nextAction, blockedBy } = evaluation.state;
39277
- lines.push(` ${change.slug} \u2014 ${stateLabel(state)}`);
39278
- lines.push(` ${msg("status.next", ctx.language)} ${nextAction.command}`);
39279
- lines.push(` ${nextAction.description}${nextAction.requiresAgent ? " (requiere agente)" : ""}`);
39280
- for (const block of blockedBy) lines.push(` bloqueado por: ${block}`);
39477
+ 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");
39478
+ lines.push(
39479
+ filaAlineada([
39480
+ { texto: ` ${pinta(tema, "negrita", change.slug)}`, ancho: 30 },
39481
+ { texto: pinta(tema, "gris", stateLabel(state)) }
39482
+ ])
39483
+ );
39484
+ lines.push(...siguienteAccion(tema, nextAction.command, `${nextAction.description} ${s.separador} ${actor}`));
39485
+ for (const block of blockedBy) lines.push(` ${pinta(tema, "amarillo", s.bloqueo)} ${pinta(tema, "gris", block)}`);
39281
39486
  lines.push("");
39282
39487
  data.push({ slug: change.slug, state, next: nextAction.command, blockedBy, requiresAgent: nextAction.requiresAgent });
39283
39488
  }
39284
- if (targets.length === 0) lines.push("Sin cambios activos.");
39489
+ if (targets.length === 0) lines.push(pinta(tema, "gris", " Sin cambios activos."));
39285
39490
  return { exitCode: hasErrors2 ? 1 : 0, diagnostics: [], data: { changes: data }, text: lines };
39286
39491
  }
39287
39492
 
@@ -39568,20 +39773,52 @@ async function avisoDeActualizacion(json2, cwd) {
39568
39773
  }
39569
39774
 
39570
39775
  // src/commands/status.ts
39776
+ var TONO_ESTADO = {
39777
+ draft: "gris",
39778
+ spec_draft: "amarillo",
39779
+ paused: "amarillo",
39780
+ awaiting_mockups: "morado",
39781
+ awaiting_approval: "morado",
39782
+ approved: "azul",
39783
+ planned: "azul",
39784
+ building: "azul",
39785
+ built: "amarillo",
39786
+ verified: "verde",
39787
+ reviewed: "verde",
39788
+ ready: "verde",
39789
+ archived: "gris"
39790
+ };
39791
+ function marcaDeEstado(tema, estado, bloqueado) {
39792
+ const s = simbolos(tema);
39793
+ if (bloqueado) return pinta(tema, "rojo", s.bloqueo);
39794
+ if (estado === "paused") return pinta(tema, "amarillo", s.pausa);
39795
+ if (estado === "ready") return pinta(tema, "verde", s.ok);
39796
+ return pinta(tema, TONO_ESTADO[estado] ?? "azul", s.punto);
39797
+ }
39571
39798
  async function runStatus(ctx) {
39572
39799
  const { root, workspace, config, approvals } = await requireWorkspace(ctx);
39573
39800
  const advisory = await upgradeAdvisory(root);
39574
- const lines = [msg("status.title", ctx.language), ""];
39801
+ const tema = detectarTema();
39802
+ const s = simbolos(tema);
39803
+ const fixes = await loadLivingFixes(root);
39804
+ const contexto = [
39805
+ `${workspace.specs.length} ${workspace.specs.length === 1 ? "spec viva" : "specs vivas"}`,
39806
+ `${workspace.changes.length} ${workspace.changes.length === 1 ? "cambio" : "cambios"}`,
39807
+ fixes.length > 0 ? `${fixes.length} ${fixes.length === 1 ? "fix" : "fixes"}` : ""
39808
+ ].filter((parte) => parte !== "").join(` ${s.separador} `);
39809
+ const lines = encabezado(tema, msg("status.title", ctx.language), `${config.project.name} ${s.separador} ${contexto}`);
39575
39810
  const changes = [];
39576
39811
  if (workspace.changes.length === 0) {
39577
- lines.push("Sin cambios activos. Crea uno con `satlas new <slug>`.");
39812
+ lines.push(pinta(tema, "gris", ` Sin cambios activos. Crea uno con \`satlas new <slug>\`.`));
39578
39813
  }
39579
39814
  for (const change of workspace.changes) {
39580
39815
  const evaluation = await evaluateChange(workspace, config, change, approvals);
39581
- const { state, progress, nextAction } = evaluation.state;
39816
+ const { state, progress, nextAction, blockedBy } = evaluation.state;
39817
+ const lane = change.meta?.lane ?? config.lanes.default;
39818
+ const bloqueado = blockedBy.length > 0 || evaluation.blocking > 0;
39582
39819
  changes.push({
39583
39820
  slug: change.slug,
39584
- lane: change.meta?.lane ?? config.lanes.default,
39821
+ lane,
39585
39822
  domain: change.meta?.domain,
39586
39823
  state,
39587
39824
  label: stateLabel(state),
@@ -39590,27 +39827,48 @@ async function runStatus(ctx) {
39590
39827
  blocking: evaluation.blocking
39591
39828
  });
39592
39829
  lines.push(
39593
- ` ${change.slug} ${(change.meta?.domain ?? "-").padEnd(10)} ${(change.meta?.lane ?? config.lanes.default).padEnd(9)} ${stateLabel(state)}`
39830
+ filaAlineada([
39831
+ { texto: ` ${marcaDeEstado(tema, state, bloqueado)} ${pinta(tema, "negrita", change.slug)}`, ancho: 30 },
39832
+ { texto: pinta(tema, TONO_ESTADO[state] ?? "azul", stateLabel(state)), ancho: 22 },
39833
+ { texto: pinta(tema, "gris", `${change.meta?.domain ?? "\u2014"} ${s.separador} ${lane}`) }
39834
+ ])
39594
39835
  );
39595
- lines.push(
39596
- ` ${msg("status.tasks", ctx.language)} ${progress.tasksDone}/${progress.tasksTotal} \xB7 ${msg("status.evidence", ctx.language)} ${progress.scenariosEvidenced}/${progress.scenariosTotal}`
39597
- );
39598
- lines.push(` ${msg("status.next", ctx.language)} ${nextAction.command} \u2014 ${nextAction.description}`);
39836
+ const medidas = [
39837
+ `${pinta(tema, "gris", "tareas")} ${barraProgreso(tema, progress.tasksDone, progress.tasksTotal)}`,
39838
+ `${pinta(tema, "gris", "evidencia")} ${barraProgreso(tema, progress.scenariosEvidenced, progress.scenariosTotal)}`
39839
+ ];
39840
+ lines.push(` ${medidas.join(pinta(tema, "gris", ` ${s.separador} `))}`);
39841
+ for (const bloqueo of blockedBy) lines.push(` ${pinta(tema, "amarillo", s.bloqueo)} ${pinta(tema, "gris", bloqueo)}`);
39842
+ lines.push(...siguienteAccion(tema, nextAction.command, nextAction.description));
39843
+ lines.push("");
39599
39844
  }
39600
- lines.push("");
39601
- lines.push(`${msg("status.specs", ctx.language)}: ${workspace.specs.length}`);
39602
- for (const spec of workspace.specs) {
39603
- lines.push(` ${spec.domain} \u2014 ${spec.spec.requirements.length} requisitos`);
39845
+ if (workspace.specs.length > 0) {
39846
+ lines.push(seccion(tema, msg("status.specs", ctx.language), `${workspace.specs.length}`));
39847
+ const ancho = Math.max(...workspace.specs.map((spec) => spec.domain.length), 8);
39848
+ for (const spec of workspace.specs) {
39849
+ lines.push(
39850
+ filaAlineada([
39851
+ { texto: ` ${pinta(tema, "morado", s.punto)} ${spec.domain}`, ancho: ancho + 6 },
39852
+ { texto: pinta(tema, "gris", `${spec.spec.requirements.length} ${spec.spec.requirements.length === 1 ? "requisito" : "requisitos"}`) }
39853
+ ])
39854
+ );
39855
+ }
39856
+ lines.push("");
39604
39857
  }
39605
- const fixes = await loadLivingFixes(root);
39606
- lines.push("");
39607
- lines.push(`${msg("status.fixes", ctx.language)}: ${fixes.length}`);
39858
+ lines.push(seccion(tema, msg("status.fixes", ctx.language), `${fixes.length}`));
39608
39859
  if (fixes.length === 0) {
39609
- lines.push(" (se llenan al archivar un fix)");
39860
+ lines.push(pinta(tema, "gris", " (se llenan al archivar un fix)"));
39610
39861
  } else {
39611
39862
  for (const fix of fixes) {
39612
- const origin = fix.source === "archive" ? " (hist\xF3rico)" : "";
39613
- lines.push(` ${fix.date || "\u2014"} ${(fix.domain ?? "\u2014").padEnd(10)} ${fix.slug} \u2014 ${fix.result}${origin}`);
39863
+ const resultado = fix.result === "pass" ? pinta(tema, "verde", fix.result) : pinta(tema, "rojo", fix.result);
39864
+ lines.push(
39865
+ filaAlineada([
39866
+ { texto: ` ${pinta(tema, "gris", fix.date || "\u2014")}`, ancho: 14 },
39867
+ { texto: pinta(tema, "gris", fix.domain ?? "\u2014"), ancho: 14 },
39868
+ { texto: fix.slug, ancho: 28 },
39869
+ { texto: `${resultado}${fix.source === "archive" ? pinta(tema, "gris", " (hist\xF3rico)") : ""}` }
39870
+ ])
39871
+ );
39614
39872
  }
39615
39873
  }
39616
39874
  const diagnostics = [...workspace.diagnostics, ...advisory.diagnostics];
@@ -39620,7 +39878,7 @@ async function runStatus(ctx) {
39620
39878
  diagnostics,
39621
39879
  data: {
39622
39880
  changes,
39623
- specs: workspace.specs.map((s) => ({ domain: s.domain, requirements: s.spec.requirements.length })),
39881
+ specs: workspace.specs.map((spec) => ({ domain: spec.domain, requirements: spec.spec.requirements.length })),
39624
39882
  fixes: fixes.map((fix) => ({
39625
39883
  slug: fix.slug,
39626
39884
  date: fix.date,
@@ -39794,8 +40052,10 @@ async function runValidate(ctx) {
39794
40052
  const slug = flagString(ctx.flags, "change");
39795
40053
  const strict = flagBool(ctx.flags, "strict");
39796
40054
  const advisory = slug ? void 0 : await upgradeAdvisory(root);
40055
+ const tema = detectarTema();
40056
+ const s = simbolos(tema);
39797
40057
  const diagnostics = [];
39798
- const lines = [msg("validate.title", ctx.language), ""];
40058
+ const lines = encabezado(tema, msg("validate.title", ctx.language), slug ? slug : void 0);
39799
40059
  for (const spec of workspace.specs) {
39800
40060
  diagnostics.push(...lintSpec(spec.spec, { language: config.spec.language, businessOnly: config.spec.business_only }));
39801
40061
  }
@@ -39811,11 +40071,15 @@ async function runValidate(ctx) {
39811
40071
  const evaluation = await evaluateChange(workspace, config, change, approvals);
39812
40072
  diagnostics.push(...evaluation.lintFindings);
39813
40073
  changes.push({ slug: change.slug, blocking: evaluation.blocking, findings: evaluation.lintFindings.length });
39814
- lines.push(` ${change.slug}: ${evaluation.lintFindings.length} ${msg("summary.findings", ctx.language)}`);
39815
- for (const finding of evaluation.lintFindings) {
39816
- lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${finding.line ? `:${finding.line}` : ""} \u2014 ${finding.message}`);
39817
- }
39818
- lines.push(` archivo: ${path59.relative(ctx.cwd, path59.join(change.dir, "spec.md"))}`);
40074
+ const errores = evaluation.lintFindings.filter((finding) => finding.severity === "error").length;
40075
+ const marca = errores > 0 ? pinta(tema, "rojo", s.error) : evaluation.lintFindings.length > 0 ? pinta(tema, "amarillo", s.aviso) : pinta(tema, "verde", s.ok);
40076
+ lines.push(
40077
+ filaAlineada([
40078
+ { texto: ` ${marca} ${pinta(tema, "negrita", change.slug)}`, ancho: 30 },
40079
+ { texto: evaluation.lintFindings.length === 0 ? pinta(tema, "gris", "sin hallazgos") : `${evaluation.lintFindings.length} ${msg("summary.findings", ctx.language)}`, ancho: 20 },
40080
+ { texto: pinta(tema, "gris", path59.relative(ctx.cwd, path59.join(change.dir, "spec.md"))) }
40081
+ ])
40082
+ );
39819
40083
  }
39820
40084
  if (targets.length === 0) lines.push("Sin cambios activos.");
39821
40085
  if (advisory && advisory.plan.pending.length > 0) {
@@ -39823,7 +40087,10 @@ async function runValidate(ctx) {
39823
40087
  }
39824
40088
  const errors = diagnostics.filter((d) => d.severity === "error").length;
39825
40089
  const warnings = diagnostics.filter((d) => d.severity === "warning").length;
39826
- if (errors === 0 && warnings === 0) lines.push(msg("validate.ok", ctx.language));
40090
+ if (errors === 0 && warnings === 0) {
40091
+ lines.push("");
40092
+ lines.push(exito(tema, msg("validate.ok", ctx.language)));
40093
+ }
39827
40094
  return {
39828
40095
  exitCode: errors > 0 || strict && warnings > 0 ? 1 : 0,
39829
40096
  diagnostics: advisory ? [...diagnostics, ...advisory.diagnostics] : diagnostics,
@@ -40092,7 +40359,7 @@ var HANDLERS2 = {
40092
40359
  archive: runArchive,
40093
40360
  "self-update": runSelfUpdate,
40094
40361
  version: async () => ({ exitCode: 0, data: { version: cliVersion() }, diagnostics: [], text: [`specatlas ${cliVersion()}`] }),
40095
- help: (ctx) => runHelp(ctx)
40362
+ help: (ctx) => runHelp(ctx, ctx.positionals[0])
40096
40363
  };
40097
40364
  var COMMANDS = Object.fromEntries(
40098
40365
  CATALOG.map((spec) => [spec.name, { description: spec.description, flags: spec.flags, handler: HANDLERS2[spec.name] }])
@@ -40192,15 +40459,28 @@ function printResult(command, result, json2, language) {
40192
40459
  `);
40193
40460
  return;
40194
40461
  }
40462
+ const tema = detectarTema();
40195
40463
  const lines = [...result.text ?? []];
40464
+ if (result.diagnostics.length > 0) lines.push("");
40196
40465
  for (const d of result.diagnostics) {
40197
- const location = d.path ? ` ${path62.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
40198
- lines.push(`${d.severity === "error" ? "ERROR" : d.severity === "warning" ? "AVISO" : "NOTA"} ${d.code}${location} \u2014 ${d.message}`);
40199
- if (d.suggestion) lines.push(` \u21B3 ${d.suggestion}`);
40466
+ const ruta = d.path ? path62.relative(process.cwd(), d.path) : void 0;
40467
+ lines.push(
40468
+ ...formatearDiagnostico(
40469
+ tema,
40470
+ {
40471
+ code: d.code,
40472
+ severity: d.severity,
40473
+ message: d.message,
40474
+ ...d.line !== void 0 ? { line: d.line } : {},
40475
+ ...d.suggestion !== void 0 ? { suggestion: d.suggestion } : {}
40476
+ },
40477
+ ruta
40478
+ )
40479
+ );
40200
40480
  }
40201
- const counts = countBySeverity(result.diagnostics);
40202
40481
  if (result.diagnostics.length > 0) {
40203
- lines.push(`${counts.errors} ${msg("label.errors", language)}, ${counts.warnings} ${msg("label.warnings", language)}, ${counts.infos} ${msg("label.infos", language)}`);
40482
+ lines.push("");
40483
+ lines.push(resumenHallazgos(tema, countBySeverity(result.diagnostics)));
40204
40484
  }
40205
40485
  if (lines.length > 0) process.stdout.write(`${lines.join("\n")}
40206
40486
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specatlas",
3
- "version": "0.1.38",
3
+ "version": "0.1.40",
4
4
  "description": "SpecAtlas: kernel determinista de Spec-Driven Development (CLI)",
5
5
  "license": "MIT",
6
6
  "type": "module",