specatlas 0.1.5 → 0.1.7
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 +184 -144
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -1581,7 +1581,7 @@ var require_core = __commonJS({
|
|
|
1581
1581
|
});
|
|
1582
1582
|
|
|
1583
1583
|
// src/cli.ts
|
|
1584
|
-
import
|
|
1584
|
+
import path39 from "path";
|
|
1585
1585
|
|
|
1586
1586
|
// ../core/dist/index.js
|
|
1587
1587
|
import path from "path";
|
|
@@ -8938,6 +8938,7 @@ import path15 from "path";
|
|
|
8938
8938
|
import { promises as fs } from "fs";
|
|
8939
8939
|
import path16 from "path";
|
|
8940
8940
|
import path17 from "path";
|
|
8941
|
+
import path18 from "path";
|
|
8941
8942
|
var CORE_VERSION = "0.0.1";
|
|
8942
8943
|
var REQ_ID_RE = /^REQ-[A-Z0-9]+(?:-[A-Z0-9]+)*-\d{3}$/;
|
|
8943
8944
|
var SCENARIO_ID_RE = /^REQ-[A-Z0-9]+(?:-[A-Z0-9]+)*-\d{3}-S\d+$/;
|
|
@@ -9513,14 +9514,14 @@ function wordBoundary(text, term) {
|
|
|
9513
9514
|
const re = new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, "iu");
|
|
9514
9515
|
return re.test(text);
|
|
9515
9516
|
}
|
|
9516
|
-
function lintText(text, code, terms, label,
|
|
9517
|
+
function lintText(text, code, terms, label, path192, line) {
|
|
9517
9518
|
const out = [];
|
|
9518
9519
|
const lower = text.toLowerCase();
|
|
9519
9520
|
for (const term of terms) {
|
|
9520
9521
|
if (wordBoundary(lower, term)) {
|
|
9521
9522
|
out.push(
|
|
9522
9523
|
diag(code, "error", `${label}: "${term}"`, {
|
|
9523
|
-
path:
|
|
9524
|
+
path: path192,
|
|
9524
9525
|
line,
|
|
9525
9526
|
suggestion: "La especificaci\xF3n es funcional y de negocio: describe comportamiento, no tecnolog\xEDa ni adjetivos vagos"
|
|
9526
9527
|
})
|
|
@@ -9529,7 +9530,7 @@ function lintText(text, code, terms, label, path182, line) {
|
|
|
9529
9530
|
}
|
|
9530
9531
|
return out;
|
|
9531
9532
|
}
|
|
9532
|
-
function lintRequirement(req,
|
|
9533
|
+
function lintRequirement(req, path192, opts = {}) {
|
|
9533
9534
|
const out = [];
|
|
9534
9535
|
const vague = opts.language === "en" ? VAGUE_EN : VAGUE_ES;
|
|
9535
9536
|
const tech = opts.language === "en" ? TECH_EN : TECH_ES;
|
|
@@ -9540,32 +9541,32 @@ function lintRequirement(req, path182, opts = {}) {
|
|
|
9540
9541
|
...req.scenarios.flatMap((s) => [...s.when.map((w) => ({ text: w, line: s.line })), ...s.then.map((t) => ({ text: t, line: s.line }))])
|
|
9541
9542
|
];
|
|
9542
9543
|
for (const part of parts) {
|
|
9543
|
-
out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n",
|
|
9544
|
+
out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path192, part.line));
|
|
9544
9545
|
if (opts.businessOnly !== false) {
|
|
9545
|
-
out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio",
|
|
9546
|
+
out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path192, part.line));
|
|
9546
9547
|
}
|
|
9547
9548
|
}
|
|
9548
9549
|
if (req.scenarios.length === 0) {
|
|
9549
|
-
out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path:
|
|
9550
|
+
out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path192, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
|
|
9550
9551
|
}
|
|
9551
9552
|
return out;
|
|
9552
9553
|
}
|
|
9553
|
-
function lintDelta(delta, livingRequirements,
|
|
9554
|
+
function lintDelta(delta, livingRequirements, path192, opts = {}) {
|
|
9554
9555
|
const out = [...delta.diagnostics];
|
|
9555
9556
|
for (const req of [...delta.added, ...delta.modified]) {
|
|
9556
|
-
out.push(...lintRequirement(req,
|
|
9557
|
+
out.push(...lintRequirement(req, path192, opts));
|
|
9557
9558
|
}
|
|
9558
9559
|
for (const req of delta.modified) {
|
|
9559
9560
|
const living = livingRequirements.get(req.id);
|
|
9560
9561
|
if (!living) {
|
|
9561
|
-
out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path:
|
|
9562
|
+
out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path192, line: req.line }));
|
|
9562
9563
|
continue;
|
|
9563
9564
|
}
|
|
9564
9565
|
for (const existing of living.scenarios) {
|
|
9565
9566
|
if (!req.scenarios.some((s) => s.id === existing.id)) {
|
|
9566
9567
|
out.push(
|
|
9567
9568
|
diag("TRACE-007", "error", `MODIFIED ${req.id} pierde el escenario ${existing.id}: copia el bloque completo`, {
|
|
9568
|
-
path:
|
|
9569
|
+
path: path192,
|
|
9569
9570
|
line: req.line,
|
|
9570
9571
|
suggestion: "Copia el bloque completo de la spec viva y ed\xEDtalo; para quitarlo, decl\xE1ralo en REMOVED"
|
|
9571
9572
|
})
|
|
@@ -9576,15 +9577,15 @@ function lintDelta(delta, livingRequirements, path182, opts = {}) {
|
|
|
9576
9577
|
for (const req of delta.removed) {
|
|
9577
9578
|
const living = livingRequirements.get(req.id);
|
|
9578
9579
|
if (!living) {
|
|
9579
|
-
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path:
|
|
9580
|
+
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path192, line: req.line }));
|
|
9580
9581
|
}
|
|
9581
9582
|
}
|
|
9582
9583
|
for (const rename of delta.renamed) {
|
|
9583
9584
|
if (rename.from.id !== rename.to.id) {
|
|
9584
|
-
out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename.from.id} \u2192 ${rename.to.id}); los ids son inmutables`, { path:
|
|
9585
|
+
out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename.from.id} \u2192 ${rename.to.id}); los ids son inmutables`, { path: path192, line: rename.line }));
|
|
9585
9586
|
}
|
|
9586
9587
|
if (!livingRequirements.has(rename.from.id)) {
|
|
9587
|
-
out.push(diag("TRACE-007", "error", `RENAMED ${rename.from.id} no existe en la spec viva`, { path:
|
|
9588
|
+
out.push(diag("TRACE-007", "error", `RENAMED ${rename.from.id} no existe en la spec viva`, { path: path192, line: rename.line }));
|
|
9588
9589
|
}
|
|
9589
9590
|
}
|
|
9590
9591
|
return out;
|
|
@@ -9914,7 +9915,7 @@ function deriveState(input) {
|
|
|
9914
9915
|
blockedBy.push(`evidencia ${scenariosEvidenced}/${deltaScenarios.length}`);
|
|
9915
9916
|
return { state: "built", blockedBy, nextAction: next(`satlas verify ${change.slug}`, "Registrar evidencia por escenario"), progress };
|
|
9916
9917
|
}
|
|
9917
|
-
if (lane === "full" && cfg.gates.review.mode === "blocking" && !change.
|
|
9918
|
+
if (lane === "full" && cfg.gates.review.mode === "blocking" && !change.reviewPath) {
|
|
9918
9919
|
blockedBy.push("review pendiente");
|
|
9919
9920
|
return { state: "verified", blockedBy, nextAction: next(`/satlas.review ${change.slug}`, "Revisi\xF3n de c\xF3digo", true), progress };
|
|
9920
9921
|
}
|
|
@@ -10102,8 +10103,8 @@ async function loadSpecs(sddDir) {
|
|
|
10102
10103
|
}
|
|
10103
10104
|
return out;
|
|
10104
10105
|
}
|
|
10105
|
-
async function loadChange(root, slug) {
|
|
10106
|
-
const dir = path4.join(root, SDD_DIR, "changes", slug);
|
|
10106
|
+
async function loadChange(root, slug, relDir) {
|
|
10107
|
+
const dir = path4.join(root, SDD_DIR, "changes", relDir ?? slug);
|
|
10107
10108
|
const diagnostics = [];
|
|
10108
10109
|
const change = { slug, dir, diagnostics };
|
|
10109
10110
|
const metaFile = path4.join(dir, "meta.yaml");
|
|
@@ -10125,6 +10126,8 @@ async function loadChange(root, slug) {
|
|
|
10125
10126
|
}
|
|
10126
10127
|
const planFile = path4.join(dir, "plan.md");
|
|
10127
10128
|
if (await exists(planFile)) change.planPath = planFile;
|
|
10129
|
+
const reviewFile = path4.join(dir, "review.md");
|
|
10130
|
+
if (await exists(reviewFile)) change.reviewPath = reviewFile;
|
|
10128
10131
|
const tasksFile = path4.join(dir, "tasks.md");
|
|
10129
10132
|
const tasksRaw = await readTextIfExists(tasksFile);
|
|
10130
10133
|
if (tasksRaw !== void 0) {
|
|
@@ -10165,7 +10168,14 @@ async function loadWorkspace(root) {
|
|
|
10165
10168
|
changes.push(change);
|
|
10166
10169
|
diagnostics.push(...change.diagnostics);
|
|
10167
10170
|
}
|
|
10168
|
-
|
|
10171
|
+
const archived = [];
|
|
10172
|
+
const archivedDir = path4.join(sddDir, "changes", "archive");
|
|
10173
|
+
if (await isDirectory(archivedDir)) {
|
|
10174
|
+
for (const entry of await listDirs(archivedDir)) {
|
|
10175
|
+
archived.push(await loadChange(root, entry.replace(/^\d{4}-\d{2}-/, ""), path4.join("archive", entry)));
|
|
10176
|
+
}
|
|
10177
|
+
}
|
|
10178
|
+
return { workspace: { root, sddDir, specs, changes, archived, diagnostics }, config };
|
|
10169
10179
|
}
|
|
10170
10180
|
async function ensureSddDirs(sddDir) {
|
|
10171
10181
|
const created = [];
|
|
@@ -12606,6 +12616,72 @@ async function runDoctor(root) {
|
|
|
12606
12616
|
}
|
|
12607
12617
|
return { findings, summary: countBySeverity(findings) };
|
|
12608
12618
|
}
|
|
12619
|
+
var UI_DOMAINS2 = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
|
|
12620
|
+
function count(name, diagnostics) {
|
|
12621
|
+
return {
|
|
12622
|
+
name,
|
|
12623
|
+
errors: diagnostics.filter((d) => d.severity === "error").length,
|
|
12624
|
+
warnings: diagnostics.filter((d) => d.severity === "warning").length
|
|
12625
|
+
};
|
|
12626
|
+
}
|
|
12627
|
+
function livingRequirementsMap(specs) {
|
|
12628
|
+
const map = /* @__PURE__ */ new Map();
|
|
12629
|
+
for (const spec of specs) {
|
|
12630
|
+
for (const requirement of spec.spec.requirements) map.set(requirement.id, requirement);
|
|
12631
|
+
}
|
|
12632
|
+
return map;
|
|
12633
|
+
}
|
|
12634
|
+
async function runCiGate(opts) {
|
|
12635
|
+
const root = path18.resolve(opts.root);
|
|
12636
|
+
const { workspace, config } = await loadWorkspace(root);
|
|
12637
|
+
const diagnostics = [];
|
|
12638
|
+
const checks = [];
|
|
12639
|
+
const specFindings = workspace.specs.flatMap((spec) => lintSpec(spec.spec, { language: config.spec.language, businessOnly: config.spec.business_only }));
|
|
12640
|
+
diagnostics.push(...specFindings);
|
|
12641
|
+
checks.push(count("specs vivas", specFindings));
|
|
12642
|
+
const living = livingRequirementsMap(workspace.specs);
|
|
12643
|
+
let changesErrors = 0;
|
|
12644
|
+
let changesWarnings = 0;
|
|
12645
|
+
for (const change of workspace.changes) {
|
|
12646
|
+
const lintFindings = change.delta ? lintDelta(change.delta, living, path18.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
|
|
12647
|
+
const trace = checkTrace({
|
|
12648
|
+
specs: workspace.specs,
|
|
12649
|
+
change,
|
|
12650
|
+
requireEvidence: config.gates.verify.mode !== "off" && config.gates.verify.require_evidence
|
|
12651
|
+
});
|
|
12652
|
+
const changeDiags = [...lintFindings, ...trace.findings];
|
|
12653
|
+
if (change.tasks && change.tasks.counts.total > 0) {
|
|
12654
|
+
const plan = planWaves(change.tasks, { maxParallel: config.waves.max_parallel });
|
|
12655
|
+
changeDiags.push(...plan.blocks.flatMap((block) => block.diagnostics));
|
|
12656
|
+
}
|
|
12657
|
+
if (UI_DOMAINS2.has(change.meta?.domain ?? "")) {
|
|
12658
|
+
const mockups = await checkMockups(root, change.slug, change);
|
|
12659
|
+
changeDiags.push(...mockups.findings);
|
|
12660
|
+
}
|
|
12661
|
+
if (config.packs.length > 0) {
|
|
12662
|
+
const resolved = await resolvePacks(workspace.sddDir, config);
|
|
12663
|
+
const evaluations = evaluatePacks(resolved.packs, change, config);
|
|
12664
|
+
changeDiags.push(...packFindings(evaluations, change));
|
|
12665
|
+
}
|
|
12666
|
+
diagnostics.push(...changeDiags);
|
|
12667
|
+
const entry = count(change.slug, changeDiags);
|
|
12668
|
+
changesErrors += entry.errors;
|
|
12669
|
+
changesWarnings += entry.warnings;
|
|
12670
|
+
}
|
|
12671
|
+
checks.push({ name: "cambios (lint + trace + waves + mockups)", errors: changesErrors, warnings: changesWarnings });
|
|
12672
|
+
const doctor = await runDoctor(root);
|
|
12673
|
+
const doctorErrors = doctor.findings.filter((d) => d.severity === "error");
|
|
12674
|
+
diagnostics.push(...doctorErrors);
|
|
12675
|
+
checks.push({ name: "doctor", errors: doctorErrors.length, warnings: 0 });
|
|
12676
|
+
for (const extra of opts.extra ?? []) {
|
|
12677
|
+
diagnostics.push(...extra.diagnostics);
|
|
12678
|
+
checks.push(count(extra.name, extra.diagnostics));
|
|
12679
|
+
}
|
|
12680
|
+
const errors = diagnostics.filter((d) => d.severity === "error").length;
|
|
12681
|
+
const warnings = diagnostics.filter((d) => d.severity === "warning").length;
|
|
12682
|
+
const failed = errors > 0 || (opts.strict ?? false) && warnings > 0;
|
|
12683
|
+
return { checks, diagnostics, errors, warnings, failed };
|
|
12684
|
+
}
|
|
12609
12685
|
|
|
12610
12686
|
// src/args.ts
|
|
12611
12687
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "local", "strict", "yes", "dry-run", "require-evidence", "help", "version"]);
|
|
@@ -12777,26 +12853,26 @@ function cliVersion() {
|
|
|
12777
12853
|
}
|
|
12778
12854
|
|
|
12779
12855
|
// src/commands/adapters.ts
|
|
12780
|
-
import
|
|
12856
|
+
import path21 from "path";
|
|
12781
12857
|
|
|
12782
12858
|
// ../adapters/dist/index.js
|
|
12783
|
-
import
|
|
12859
|
+
import path19 from "path";
|
|
12784
12860
|
import { stringify as stringifyYaml5 } from "yaml";
|
|
12785
12861
|
import path22 from "path";
|
|
12786
12862
|
async function loadWorkflow(workflowDir) {
|
|
12787
|
-
const phasesDir =
|
|
12788
|
-
const snippetsDir =
|
|
12863
|
+
const phasesDir = path19.join(workflowDir, "phases");
|
|
12864
|
+
const snippetsDir = path19.join(workflowDir, "snippets");
|
|
12789
12865
|
const phases = [];
|
|
12790
12866
|
const snippets = /* @__PURE__ */ new Map();
|
|
12791
12867
|
const hashParts = [];
|
|
12792
12868
|
for (const entry of (await listDir(phasesDir)).sort()) {
|
|
12793
12869
|
if (!entry.endsWith(".md")) continue;
|
|
12794
|
-
const filePath =
|
|
12870
|
+
const filePath = path19.join(phasesDir, entry);
|
|
12795
12871
|
const raw = await readText(filePath);
|
|
12796
12872
|
hashParts.push(raw);
|
|
12797
12873
|
const fm = parseFrontmatter(raw, filePath);
|
|
12798
12874
|
const data = fm.data;
|
|
12799
|
-
const id = typeof data["id"] === "string" ? data["id"] :
|
|
12875
|
+
const id = typeof data["id"] === "string" ? data["id"] : path19.basename(entry, ".md");
|
|
12800
12876
|
phases.push({
|
|
12801
12877
|
id,
|
|
12802
12878
|
title: typeof data["title"] === "string" ? data["title"] : id,
|
|
@@ -12810,9 +12886,9 @@ async function loadWorkflow(workflowDir) {
|
|
|
12810
12886
|
}
|
|
12811
12887
|
for (const entry of (await listDir(snippetsDir)).sort()) {
|
|
12812
12888
|
if (!entry.endsWith(".md")) continue;
|
|
12813
|
-
const raw = await readTextIfExists(
|
|
12889
|
+
const raw = await readTextIfExists(path19.join(snippetsDir, entry)) ?? "";
|
|
12814
12890
|
hashParts.push(raw);
|
|
12815
|
-
snippets.set(
|
|
12891
|
+
snippets.set(path19.basename(entry, ".md"), raw.trim());
|
|
12816
12892
|
}
|
|
12817
12893
|
return { phases: phases.sort((a, b) => a.id.localeCompare(b.id)), snippets, sourceHash: sha256(hashParts.join("\n---\n")) };
|
|
12818
12894
|
}
|
|
@@ -13116,7 +13192,7 @@ async function checkAdapters(opts) {
|
|
|
13116
13192
|
}
|
|
13117
13193
|
|
|
13118
13194
|
// src/paths.ts
|
|
13119
|
-
import
|
|
13195
|
+
import path20 from "path";
|
|
13120
13196
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13121
13197
|
async function resolveProfilesDir() {
|
|
13122
13198
|
const env = process.env["SPECATLAS_PROFILES_DIR"];
|
|
@@ -13126,16 +13202,16 @@ async function resolveProfilesDir() {
|
|
|
13126
13202
|
async function resolveWorkflowDir() {
|
|
13127
13203
|
const env = process.env["SPECATLAS_WORKFLOW_DIR"];
|
|
13128
13204
|
if (env && await exists(env)) return env;
|
|
13129
|
-
return walkUpFor(
|
|
13205
|
+
return walkUpFor(path20.join("workflow", "phases"));
|
|
13130
13206
|
}
|
|
13131
13207
|
async function walkUpFor(relative) {
|
|
13132
|
-
let dir =
|
|
13208
|
+
let dir = path20.dirname(fileURLToPath2(import.meta.url));
|
|
13133
13209
|
for (let i = 0; i < 8; i += 1) {
|
|
13134
|
-
const candidate =
|
|
13210
|
+
const candidate = path20.join(dir, relative);
|
|
13135
13211
|
if (await exists(candidate)) {
|
|
13136
|
-
return relative.includes(
|
|
13212
|
+
return relative.includes(path20.sep) ? path20.dirname(candidate) : candidate;
|
|
13137
13213
|
}
|
|
13138
|
-
const parent =
|
|
13214
|
+
const parent = path20.dirname(dir);
|
|
13139
13215
|
if (parent === dir) break;
|
|
13140
13216
|
dir = parent;
|
|
13141
13217
|
}
|
|
@@ -13213,7 +13289,7 @@ async function runAdapters(ctx) {
|
|
|
13213
13289
|
for (const [target, count2] of byTarget) lines.push(` ${target}: ${count2} archivo(s)`);
|
|
13214
13290
|
if (report.written.length > 0) {
|
|
13215
13291
|
lines.push("");
|
|
13216
|
-
for (const p of report.written) lines.push(` + ${
|
|
13292
|
+
for (const p of report.written) lines.push(` + ${path21.relative(ctx.cwd, path21.join(root, p))}`);
|
|
13217
13293
|
} else {
|
|
13218
13294
|
lines.push("");
|
|
13219
13295
|
lines.push("Sin cambios: los adaptadores ya estaban al d\xEDa.");
|
|
@@ -13224,7 +13300,7 @@ async function runAdapters(ctx) {
|
|
|
13224
13300
|
}
|
|
13225
13301
|
|
|
13226
13302
|
// src/commands/adopt.ts
|
|
13227
|
-
import
|
|
13303
|
+
import path23 from "path";
|
|
13228
13304
|
async function runAdopt(ctx) {
|
|
13229
13305
|
const { root } = await requireWorkspace(ctx);
|
|
13230
13306
|
const domainsFlag = flagString(ctx.flags, "domains") ?? flagString(ctx.flags, "domain");
|
|
@@ -13244,11 +13320,11 @@ async function runAdopt(ctx) {
|
|
|
13244
13320
|
}
|
|
13245
13321
|
if (result.createdSpecs.length > 0) {
|
|
13246
13322
|
lines.push("");
|
|
13247
|
-
for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${
|
|
13323
|
+
for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path23.relative(ctx.cwd, spec)}`);
|
|
13248
13324
|
}
|
|
13249
13325
|
if (result.reportPath && !result.dryRun) {
|
|
13250
13326
|
lines.push("");
|
|
13251
|
-
lines.push(` Informe: ${
|
|
13327
|
+
lines.push(` Informe: ${path23.relative(ctx.cwd, result.reportPath)}`);
|
|
13252
13328
|
}
|
|
13253
13329
|
lines.push("");
|
|
13254
13330
|
lines.push("Siguiente: fase /satlas-adopt por dominio (requisitos AS-IS) y luego:");
|
|
@@ -13261,8 +13337,8 @@ async function runAdopt(ctx) {
|
|
|
13261
13337
|
diagnostics: result.diagnostics,
|
|
13262
13338
|
data: {
|
|
13263
13339
|
domains: result.domains.map((d) => ({ name: d.name, files: d.files, existingSpec: d.existingSpec })),
|
|
13264
|
-
createdSpecs: result.createdSpecs.map((s) =>
|
|
13265
|
-
reportPath: result.reportPath ?
|
|
13340
|
+
createdSpecs: result.createdSpecs.map((s) => path23.relative(ctx.cwd, s)),
|
|
13341
|
+
reportPath: result.reportPath ? path23.relative(ctx.cwd, result.reportPath) : void 0,
|
|
13266
13342
|
stack: result.stack,
|
|
13267
13343
|
dryRun: result.dryRun
|
|
13268
13344
|
},
|
|
@@ -13271,7 +13347,7 @@ async function runAdopt(ctx) {
|
|
|
13271
13347
|
}
|
|
13272
13348
|
|
|
13273
13349
|
// src/commands/analyze.ts
|
|
13274
|
-
import
|
|
13350
|
+
import path24 from "path";
|
|
13275
13351
|
async function runAnalyzeCommand(ctx) {
|
|
13276
13352
|
const { root } = await requireWorkspace(ctx);
|
|
13277
13353
|
const slug = ctx.positionals[0];
|
|
@@ -13282,23 +13358,23 @@ async function runAnalyzeCommand(ctx) {
|
|
|
13282
13358
|
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`];
|
|
13283
13359
|
if (result.waves) lines.push(` olas: ${result.waves.blocks} bloque(s), ${result.waves.waves} ola(s), ${result.waves.tasks} tarea(s)`);
|
|
13284
13360
|
if (result.mockups) lines.push(` mockups: ${result.mockups.screens} pantalla(s)${result.mockups.stale ? " (desactualizados)" : ""}`);
|
|
13285
|
-
if (result.path) lines.push("", ` informe: ${
|
|
13361
|
+
if (result.path) lines.push("", ` informe: ${path24.relative(ctx.cwd, result.path)}`);
|
|
13286
13362
|
for (const finding of result.findings) {
|
|
13287
13363
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}`);
|
|
13288
13364
|
}
|
|
13289
13365
|
return {
|
|
13290
13366
|
exitCode: result.status === "blocked" ? 1 : 0,
|
|
13291
13367
|
diagnostics: result.findings,
|
|
13292
|
-
data: result.path ? { ...result, path:
|
|
13368
|
+
data: result.path ? { ...result, path: path24.relative(ctx.cwd, result.path) } : result,
|
|
13293
13369
|
text: lines
|
|
13294
13370
|
};
|
|
13295
13371
|
}
|
|
13296
13372
|
|
|
13297
13373
|
// src/commands/approve.ts
|
|
13298
|
-
import
|
|
13374
|
+
import path26 from "path";
|
|
13299
13375
|
|
|
13300
13376
|
// src/commands/issue.ts
|
|
13301
|
-
import
|
|
13377
|
+
import path25 from "path";
|
|
13302
13378
|
async function runIssue(ctx) {
|
|
13303
13379
|
const action = ctx.positionals[0] ?? "status";
|
|
13304
13380
|
const slug = ctx.positionals[1];
|
|
@@ -13326,7 +13402,7 @@ async function runIssue(ctx) {
|
|
|
13326
13402
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-ISSUE-000", severity: "error", message: "Falta el slug: satlas issue status <slug>" }] };
|
|
13327
13403
|
}
|
|
13328
13404
|
const change = await loadChange(root, slug);
|
|
13329
|
-
const { config } = await loadConfig(
|
|
13405
|
+
const { config } = await loadConfig(path25.join(root, ".sdd"));
|
|
13330
13406
|
const tracker = change.meta?.tracker;
|
|
13331
13407
|
const lines = [`Issue de ${slug}`, ""];
|
|
13332
13408
|
if (!tracker || tracker.provider !== "github") {
|
|
@@ -13397,7 +13473,7 @@ async function runApprove(ctx) {
|
|
|
13397
13473
|
};
|
|
13398
13474
|
}
|
|
13399
13475
|
const { root } = await requireWorkspace(ctx);
|
|
13400
|
-
const artifact = target.includes("/") || target.includes("\\") ? target :
|
|
13476
|
+
const artifact = target.includes("/") || target.includes("\\") ? target : path26.join("changes", target, "spec.md");
|
|
13401
13477
|
const channelFlag = flagString(ctx.flags, "channel");
|
|
13402
13478
|
const channel = channelFlag === "presentation" || channelFlag === "editor" || channelFlag === "pr" || channelFlag === "tracker" ? channelFlag : "cli";
|
|
13403
13479
|
const result = await signApproval({
|
|
@@ -13422,13 +13498,13 @@ async function runApprove(ctx) {
|
|
|
13422
13498
|
return {
|
|
13423
13499
|
exitCode: hasErrors2 ? 1 : 0,
|
|
13424
13500
|
diagnostics: result.diagnostics,
|
|
13425
|
-
data: result.approval ? { ...result.approval, file:
|
|
13501
|
+
data: result.approval ? { ...result.approval, file: path26.relative(ctx.cwd, result.file) } : void 0,
|
|
13426
13502
|
text: lines
|
|
13427
13503
|
};
|
|
13428
13504
|
}
|
|
13429
13505
|
|
|
13430
13506
|
// src/commands/archive.ts
|
|
13431
|
-
import
|
|
13507
|
+
import path27 from "path";
|
|
13432
13508
|
async function runArchive(ctx) {
|
|
13433
13509
|
const slug = ctx.positionals[0];
|
|
13434
13510
|
if (!slug) {
|
|
@@ -13458,7 +13534,7 @@ async function runArchive(ctx) {
|
|
|
13458
13534
|
lines.push(` modificados: ${result.fold.applied.modified.join(", ") || "\u2014"}`);
|
|
13459
13535
|
lines.push(` eliminados: ${result.fold.applied.removed.join(", ") || "\u2014"}`);
|
|
13460
13536
|
lines.push(` renombrados: ${result.fold.applied.renamed.join(", ") || "\u2014"}`);
|
|
13461
|
-
if (result.archivedTo) lines.push(` archivado en: ${
|
|
13537
|
+
if (result.archivedTo) lines.push(` archivado en: ${path27.relative(ctx.cwd, result.archivedTo)}`);
|
|
13462
13538
|
} else {
|
|
13463
13539
|
lines.push("El delta no contiene operaciones (ADDED/MODIFIED/REMOVED/RENAMED).");
|
|
13464
13540
|
}
|
|
@@ -13471,100 +13547,39 @@ async function runArchive(ctx) {
|
|
|
13471
13547
|
};
|
|
13472
13548
|
}
|
|
13473
13549
|
|
|
13474
|
-
// src/evaluate.ts
|
|
13475
|
-
import path27 from "path";
|
|
13476
|
-
function livingRequirementsMap(specs) {
|
|
13477
|
-
const map = /* @__PURE__ */ new Map();
|
|
13478
|
-
for (const spec of specs) {
|
|
13479
|
-
for (const req of spec.spec.requirements) map.set(req.id, req);
|
|
13480
|
-
}
|
|
13481
|
-
return map;
|
|
13482
|
-
}
|
|
13483
|
-
async function evaluateChange(workspace, config, change, approvals) {
|
|
13484
|
-
const deltaPath = path27.join(change.dir, "spec.md");
|
|
13485
|
-
const deltaContent = await readTextIfExists(deltaPath);
|
|
13486
|
-
const approval = verifyApproval(change, approvals, config, deltaContent ?? void 0);
|
|
13487
|
-
const living = livingRequirementsMap(workspace.specs);
|
|
13488
|
-
const lintFindings = change.delta ? lintDelta(change.delta, living, deltaPath, { language: config.spec.language }) : [];
|
|
13489
|
-
const trace = checkTrace({
|
|
13490
|
-
specs: workspace.specs,
|
|
13491
|
-
change,
|
|
13492
|
-
requireEvidence: config.gates.verify.mode !== "off" && config.gates.verify.require_evidence
|
|
13493
|
-
});
|
|
13494
|
-
const blocking = lintFindings.filter((d) => d.severity === "error").length + trace.findings.filter((d) => d.severity === "error").length;
|
|
13495
|
-
const state = deriveState({ change, cfg: config, approval, blockingFindings: change.delta ? blocking : 0 });
|
|
13496
|
-
return { change, approval, lintFindings, trace, blocking, state };
|
|
13497
|
-
}
|
|
13498
|
-
|
|
13499
13550
|
// src/commands/ci.ts
|
|
13500
|
-
var UI_DOMAINS2 = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
|
|
13501
13551
|
async function runCi(ctx) {
|
|
13502
|
-
const { root,
|
|
13552
|
+
const { root, config } = await requireWorkspace(ctx);
|
|
13503
13553
|
const strict = flagBool(ctx.flags, "strict");
|
|
13504
|
-
const
|
|
13505
|
-
const checks = [];
|
|
13506
|
-
const specFindings = workspace.specs.flatMap((spec) => lintSpec(spec.spec, { language: config.spec.language, businessOnly: config.spec.business_only }));
|
|
13507
|
-
diagnostics.push(...specFindings);
|
|
13508
|
-
checks.push(count("specs vivas", specFindings));
|
|
13509
|
-
let changesErrors = 0;
|
|
13510
|
-
let changesWarnings = 0;
|
|
13511
|
-
for (const change of workspace.changes) {
|
|
13512
|
-
const evaluation = await evaluateChange(workspace, config, change, approvals);
|
|
13513
|
-
const changeDiags = [...evaluation.lintFindings, ...evaluation.trace.findings];
|
|
13514
|
-
if (change.tasks && change.tasks.counts.total > 0) {
|
|
13515
|
-
const plan = planWaves(change.tasks, { maxParallel: config.waves.max_parallel });
|
|
13516
|
-
changeDiags.push(...plan.blocks.flatMap((b) => b.diagnostics));
|
|
13517
|
-
}
|
|
13518
|
-
if (UI_DOMAINS2.has(change.meta?.domain ?? "")) {
|
|
13519
|
-
const mockups = await checkMockups(root, change.slug, change);
|
|
13520
|
-
changeDiags.push(...mockups.findings);
|
|
13521
|
-
}
|
|
13522
|
-
if (config.packs.length > 0) {
|
|
13523
|
-
const resolved = await resolvePacks(workspace.sddDir, config);
|
|
13524
|
-
const evaluations = evaluatePacks(resolved.packs, change, config);
|
|
13525
|
-
changeDiags.push(...packFindings(evaluations, change));
|
|
13526
|
-
}
|
|
13527
|
-
diagnostics.push(...changeDiags);
|
|
13528
|
-
const entry = count(change.slug, changeDiags);
|
|
13529
|
-
changesErrors += entry.errors;
|
|
13530
|
-
changesWarnings += entry.warnings;
|
|
13531
|
-
}
|
|
13532
|
-
checks.push({ name: "cambios (lint + trace + waves + mockups)", errors: changesErrors, warnings: changesWarnings });
|
|
13533
|
-
const doctor = await runDoctor(root);
|
|
13534
|
-
const doctorErrors = doctor.findings.filter((d) => d.severity === "error");
|
|
13535
|
-
diagnostics.push(...doctorErrors);
|
|
13536
|
-
checks.push({ name: "doctor", errors: doctorErrors.length, warnings: 0 });
|
|
13554
|
+
const extra = [];
|
|
13537
13555
|
const workflowDir = await resolveWorkflowDir();
|
|
13538
13556
|
if (workflowDir) {
|
|
13539
13557
|
const health = await checkAdapters({ root, workflowDir, targets: config.adapters.targets, language: config.project.language });
|
|
13540
|
-
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
13548
|
-
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
}
|
|
13558
|
+
extra.push({
|
|
13559
|
+
name: "adaptadores",
|
|
13560
|
+
diagnostics: health.manifest && !health.ok ? [
|
|
13561
|
+
{
|
|
13562
|
+
code: "ATLAS-ADAPTERS-001",
|
|
13563
|
+
severity: "warning",
|
|
13564
|
+
message: "Los adaptadores de agente est\xE1n desactualizados respecto a workflow/",
|
|
13565
|
+
suggestion: "Ejecuta `satlas adapters` y commitea el resultado"
|
|
13566
|
+
}
|
|
13567
|
+
] : []
|
|
13568
|
+
});
|
|
13552
13569
|
}
|
|
13553
|
-
const
|
|
13554
|
-
const warnings = diagnostics.filter((d) => d.severity === "warning").length;
|
|
13555
|
-
const failed = errors > 0 || strict && warnings > 0;
|
|
13570
|
+
const gate = await runCiGate({ root, strict, extra });
|
|
13556
13571
|
const lines = ["CI de SpecAtlas", ""];
|
|
13557
|
-
for (const check of checks)
|
|
13572
|
+
for (const check of gate.checks) {
|
|
13573
|
+
lines.push(` ${check.errors === 0 && check.warnings === 0 ? "OK " : "FALLA"} ${check.name}: ${check.errors} errores, ${check.warnings} avisos`);
|
|
13574
|
+
}
|
|
13558
13575
|
lines.push("");
|
|
13559
|
-
lines.push(failed ? "Resultado: BLOQUEADO" : "Resultado: OK");
|
|
13576
|
+
lines.push(gate.failed ? "Resultado: BLOQUEADO" : "Resultado: OK");
|
|
13560
13577
|
if (strict) lines.push("(modo estricto: los avisos tambi\xE9n bloquean)");
|
|
13561
|
-
return { exitCode: failed ? 1 : 0, diagnostics, data: { checks, errors, warnings, strict, failed }, text: lines };
|
|
13562
|
-
}
|
|
13563
|
-
function count(name, diags) {
|
|
13564
13578
|
return {
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
13579
|
+
exitCode: gate.failed ? 1 : 0,
|
|
13580
|
+
diagnostics: gate.diagnostics,
|
|
13581
|
+
data: { checks: gate.checks, errors: gate.errors, warnings: gate.warnings, strict, failed: gate.failed },
|
|
13582
|
+
text: lines
|
|
13568
13583
|
};
|
|
13569
13584
|
}
|
|
13570
13585
|
|
|
@@ -14006,6 +14021,31 @@ async function runNew(ctx) {
|
|
|
14006
14021
|
};
|
|
14007
14022
|
}
|
|
14008
14023
|
|
|
14024
|
+
// src/evaluate.ts
|
|
14025
|
+
import path34 from "path";
|
|
14026
|
+
function livingRequirementsMap2(specs) {
|
|
14027
|
+
const map = /* @__PURE__ */ new Map();
|
|
14028
|
+
for (const spec of specs) {
|
|
14029
|
+
for (const req of spec.spec.requirements) map.set(req.id, req);
|
|
14030
|
+
}
|
|
14031
|
+
return map;
|
|
14032
|
+
}
|
|
14033
|
+
async function evaluateChange(workspace, config, change, approvals) {
|
|
14034
|
+
const deltaPath = path34.join(change.dir, "spec.md");
|
|
14035
|
+
const deltaContent = await readTextIfExists(deltaPath);
|
|
14036
|
+
const approval = verifyApproval(change, approvals, config, deltaContent ?? void 0);
|
|
14037
|
+
const living = livingRequirementsMap2(workspace.specs);
|
|
14038
|
+
const lintFindings = change.delta ? lintDelta(change.delta, living, deltaPath, { language: config.spec.language }) : [];
|
|
14039
|
+
const trace = checkTrace({
|
|
14040
|
+
specs: workspace.specs,
|
|
14041
|
+
change,
|
|
14042
|
+
requireEvidence: config.gates.verify.mode !== "off" && config.gates.verify.require_evidence
|
|
14043
|
+
});
|
|
14044
|
+
const blocking = lintFindings.filter((d) => d.severity === "error").length + trace.findings.filter((d) => d.severity === "error").length;
|
|
14045
|
+
const state = deriveState({ change, cfg: config, approval, blockingFindings: change.delta ? blocking : 0 });
|
|
14046
|
+
return { change, approval, lintFindings, trace, blocking, state };
|
|
14047
|
+
}
|
|
14048
|
+
|
|
14009
14049
|
// src/commands/next.ts
|
|
14010
14050
|
async function runNext(ctx) {
|
|
14011
14051
|
const { workspace, config, approvals } = await requireWorkspace(ctx);
|
|
@@ -14036,7 +14076,7 @@ async function runNext(ctx) {
|
|
|
14036
14076
|
}
|
|
14037
14077
|
|
|
14038
14078
|
// src/commands/present.ts
|
|
14039
|
-
import
|
|
14079
|
+
import path35 from "path";
|
|
14040
14080
|
async function runPresentCommand(ctx) {
|
|
14041
14081
|
const { root } = await requireWorkspace(ctx);
|
|
14042
14082
|
const slug = ctx.positionals[0];
|
|
@@ -14048,7 +14088,7 @@ async function runPresentCommand(ctx) {
|
|
|
14048
14088
|
if (result.path) {
|
|
14049
14089
|
lines.push("Propuesta generada");
|
|
14050
14090
|
lines.push("");
|
|
14051
|
-
lines.push(` archivo: ${
|
|
14091
|
+
lines.push(` archivo: ${path35.relative(ctx.cwd, result.path)}`);
|
|
14052
14092
|
if (result.hash) lines.push(` hash de la spec: ${shortHash(result.hash)}`);
|
|
14053
14093
|
lines.push("");
|
|
14054
14094
|
lines.push("\xC1brela en el navegador y comp\xE1rtela con el stakeholder.");
|
|
@@ -14058,7 +14098,7 @@ async function runPresentCommand(ctx) {
|
|
|
14058
14098
|
return {
|
|
14059
14099
|
exitCode: hasErrors2 ? 1 : 0,
|
|
14060
14100
|
diagnostics: result.diagnostics,
|
|
14061
|
-
data: result.path ? { path:
|
|
14101
|
+
data: result.path ? { path: path35.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
|
|
14062
14102
|
text: lines
|
|
14063
14103
|
};
|
|
14064
14104
|
}
|
|
@@ -14176,7 +14216,7 @@ async function runStatus(ctx) {
|
|
|
14176
14216
|
}
|
|
14177
14217
|
|
|
14178
14218
|
// src/commands/trace.ts
|
|
14179
|
-
import
|
|
14219
|
+
import path36 from "path";
|
|
14180
14220
|
async function runTrace(ctx) {
|
|
14181
14221
|
const { workspace, config } = await requireWorkspace(ctx);
|
|
14182
14222
|
const slug = flagString(ctx.flags, "change");
|
|
@@ -14196,7 +14236,7 @@ async function runTrace(ctx) {
|
|
|
14196
14236
|
data.push({ slug: change.slug, nodes: result.graph.nodes.length, edges: result.graph.edges.length, summary: result.summary, findings: result.findings });
|
|
14197
14237
|
lines.push(` ${change.slug}: ${result.graph.nodes.length} nodos, ${result.graph.edges.length} aristas, ${errors} errores, ${result.summary.warnings} avisos`);
|
|
14198
14238
|
for (const finding of result.findings) {
|
|
14199
|
-
const location = finding.path ? ` (${
|
|
14239
|
+
const location = finding.path ? ` (${path36.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
|
|
14200
14240
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}${location}`);
|
|
14201
14241
|
}
|
|
14202
14242
|
}
|
|
@@ -14206,7 +14246,7 @@ async function runTrace(ctx) {
|
|
|
14206
14246
|
}
|
|
14207
14247
|
|
|
14208
14248
|
// src/commands/validate.ts
|
|
14209
|
-
import
|
|
14249
|
+
import path37 from "path";
|
|
14210
14250
|
async function runValidate(ctx) {
|
|
14211
14251
|
const { workspace, config, approvals } = await requireWorkspace(ctx);
|
|
14212
14252
|
const slug = flagString(ctx.flags, "change");
|
|
@@ -14232,7 +14272,7 @@ async function runValidate(ctx) {
|
|
|
14232
14272
|
for (const finding of evaluation.lintFindings) {
|
|
14233
14273
|
lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${finding.line ? `:${finding.line}` : ""} \u2014 ${finding.message}`);
|
|
14234
14274
|
}
|
|
14235
|
-
lines.push(` archivo: ${
|
|
14275
|
+
lines.push(` archivo: ${path37.relative(ctx.cwd, path37.join(change.dir, "spec.md"))}`);
|
|
14236
14276
|
}
|
|
14237
14277
|
if (targets.length === 0) lines.push("Sin cambios activos.");
|
|
14238
14278
|
const errors = diagnostics.filter((d) => d.severity === "error").length;
|
|
@@ -14247,7 +14287,7 @@ async function runValidate(ctx) {
|
|
|
14247
14287
|
}
|
|
14248
14288
|
|
|
14249
14289
|
// src/commands/verify.ts
|
|
14250
|
-
import
|
|
14290
|
+
import path38 from "path";
|
|
14251
14291
|
async function runVerify(ctx) {
|
|
14252
14292
|
const { root, workspace } = await requireWorkspace(ctx);
|
|
14253
14293
|
const slug = ctx.positionals[0];
|
|
@@ -14290,7 +14330,7 @@ async function runVerify(ctx) {
|
|
|
14290
14330
|
return { exitCode: 2, diagnostics: [{ code: "ATLAS-VERIFY-001", severity: "error", message: "Falta --by <nombre> (o define meta.owner)" }] };
|
|
14291
14331
|
}
|
|
14292
14332
|
const profilesDir = await resolveProfilesDir();
|
|
14293
|
-
const profile = await loadActiveProfile(
|
|
14333
|
+
const profile = await loadActiveProfile(path38.join(root, ".sdd"), [profilesDir ?? "", path38.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
|
|
14294
14334
|
const record = await recordEvidence({
|
|
14295
14335
|
root,
|
|
14296
14336
|
slug,
|
|
@@ -14312,7 +14352,7 @@ async function runVerify(ctx) {
|
|
|
14312
14352
|
if (record.evidence.command) lines.push(` comando: ${record.evidence.command}`);
|
|
14313
14353
|
lines.push(` resultado: ${record.evidence.result}`);
|
|
14314
14354
|
if (record.evidence.outputHash) lines.push(` hash: ${record.evidence.outputHash}`);
|
|
14315
|
-
lines.push(` archivo: ${
|
|
14355
|
+
lines.push(` archivo: ${path38.relative(ctx.cwd, record.path)}`);
|
|
14316
14356
|
if (record.stdout) {
|
|
14317
14357
|
lines.push("");
|
|
14318
14358
|
lines.push(" salida (\xFAltimas l\xEDneas):");
|
|
@@ -14327,7 +14367,7 @@ async function runVerify(ctx) {
|
|
|
14327
14367
|
return {
|
|
14328
14368
|
exitCode: record.exitCode === 0 ? 0 : 1,
|
|
14329
14369
|
diagnostics: record.diagnostics,
|
|
14330
|
-
data: { evidence: record.evidence, path:
|
|
14370
|
+
data: { evidence: record.evidence, path: path38.relative(ctx.cwd, record.path), exitCode: record.exitCode },
|
|
14331
14371
|
text: lines
|
|
14332
14372
|
};
|
|
14333
14373
|
}
|
|
@@ -14425,7 +14465,7 @@ async function requireWorkspace(ctx) {
|
|
|
14425
14465
|
throw new CliError("ATLAS-WS-001", msg("cli.noWorkspace", ctx.language), 2);
|
|
14426
14466
|
}
|
|
14427
14467
|
const { workspace, config } = await loadWorkspace(root);
|
|
14428
|
-
const approvals = await loadApprovals(
|
|
14468
|
+
const approvals = await loadApprovals(path39.join(root, ".sdd"));
|
|
14429
14469
|
return { root, workspace, config, approvals: approvals.byArtifact };
|
|
14430
14470
|
}
|
|
14431
14471
|
var CliError = class extends Error {
|
|
@@ -14492,7 +14532,7 @@ function printResult(command, result, json2, language) {
|
|
|
14492
14532
|
}
|
|
14493
14533
|
const lines = [...result.text ?? []];
|
|
14494
14534
|
for (const d of result.diagnostics) {
|
|
14495
|
-
const location = d.path ? ` ${
|
|
14535
|
+
const location = d.path ? ` ${path39.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
|
|
14496
14536
|
lines.push(`${d.severity === "error" ? "ERROR" : d.severity === "warning" ? "AVISO" : "NOTA"} ${d.code}${location} \u2014 ${d.message}`);
|
|
14497
14537
|
if (d.suggestion) lines.push(` \u21B3 ${d.suggestion}`);
|
|
14498
14538
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specatlas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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.1",
|
|
43
|
-
"@specatlas/core": "0.1.
|
|
43
|
+
"@specatlas/core": "0.1.5"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"typecheck": "tsc --noEmit",
|