specatlas 0.1.15 → 0.1.16

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 +74 -12
  2. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -8976,6 +8976,7 @@ import path13 from "path";
8976
8976
  import path14 from "path";
8977
8977
  import path15 from "path";
8978
8978
  import { promises as fs } from "fs";
8979
+ import { cp, rename, rm } from "fs/promises";
8979
8980
  import path16 from "path";
8980
8981
  import path17 from "path";
8981
8982
  import path18 from "path";
@@ -9620,12 +9621,12 @@ function lintDelta(delta, livingRequirements, path192, opts = {}) {
9620
9621
  out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path192, line: req.line }));
9621
9622
  }
9622
9623
  }
9623
- for (const rename of delta.renamed) {
9624
- if (rename.from.id !== rename.to.id) {
9625
- 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 }));
9624
+ for (const rename2 of delta.renamed) {
9625
+ if (rename2.from.id !== rename2.to.id) {
9626
+ out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path192, line: rename2.line }));
9626
9627
  }
9627
- if (!livingRequirements.has(rename.from.id)) {
9628
- out.push(diag("TRACE-007", "error", `RENAMED ${rename.from.id} no existe en la spec viva`, { path: path192, line: rename.line }));
9628
+ if (!livingRequirements.has(rename2.from.id)) {
9629
+ out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path192, line: rename2.line }));
9629
9630
  }
9630
9631
  }
9631
9632
  return out;
@@ -12539,16 +12540,16 @@ function foldDelta(livingBody, delta, language = "es") {
12539
12540
  lines = [...lines.slice(0, range.start), ...lines.slice(range.end)];
12540
12541
  applied.removed.push(req.id);
12541
12542
  }
12542
- for (const rename of delta.renamed) {
12543
- const range = findRange(rename.from.id);
12543
+ for (const rename2 of delta.renamed) {
12544
+ const range = findRange(rename2.from.id);
12544
12545
  if (!range) {
12545
- diagnostics.push(diag("TRACE-007", "error", `No se puede renombrar ${rename.from.id}: no existe en la spec viva`, { path: delta.path, line: rename.line }));
12546
+ diagnostics.push(diag("TRACE-007", "error", `No se puede renombrar ${rename2.from.id}: no existe en la spec viva`, { path: delta.path, line: rename2.line }));
12546
12547
  continue;
12547
12548
  }
12548
12549
  const header = lines[range.start] ?? "";
12549
12550
  const label = /Requirement/i.test(header) ? "Requirement" : "Requisito";
12550
- lines[range.start] = header.replace(REQ_HEADER_RE, (_match, id) => `### ${label}: ${id} \u2014 ${rename.to.title}`);
12551
- applied.renamed.push(rename.from.id);
12551
+ lines[range.start] = header.replace(REQ_HEADER_RE, (_match, id) => `### ${label}: ${id} \u2014 ${rename2.to.title}`);
12552
+ applied.renamed.push(rename2.from.id);
12552
12553
  }
12553
12554
  for (const req of delta.added) {
12554
12555
  const rendered = renderRequirement(req, language);
@@ -12563,6 +12564,46 @@ function trimTrailingBlanks(lines) {
12563
12564
  while (out.length > 0 && (out[out.length - 1] ?? "").trim() === "") out.pop();
12564
12565
  return out;
12565
12566
  }
12567
+ var TRANSIENT_MOVE_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "ENOTEMPTY", "EACCES"]);
12568
+ function delay(ms) {
12569
+ return new Promise((resolve) => setTimeout(resolve, ms));
12570
+ }
12571
+ async function moveDirectory(from, to, ops = {}) {
12572
+ const doRename = ops.rename ?? rename;
12573
+ const doCopy = ops.cp ?? cp;
12574
+ const doRemove = ops.rm ?? rm;
12575
+ let lastError;
12576
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
12577
+ try {
12578
+ await doRename(from, to);
12579
+ return { strategy: "rename" };
12580
+ } catch (error) {
12581
+ lastError = error;
12582
+ const code = error.code ?? "";
12583
+ if (!TRANSIENT_MOVE_CODES.has(code)) throw error;
12584
+ await delay(250 * attempt);
12585
+ }
12586
+ }
12587
+ await doCopy(from, to, { recursive: true, force: true });
12588
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
12589
+ try {
12590
+ await doRemove(from, { recursive: true, force: true, maxRetries: 2 });
12591
+ return { strategy: "copy" };
12592
+ } catch (error) {
12593
+ lastError = error;
12594
+ await delay(350 * attempt);
12595
+ }
12596
+ }
12597
+ await doRemove(to, { recursive: true, force: true }).catch(() => void 0);
12598
+ throw lastError;
12599
+ }
12600
+ async function restoreFile(file, previous) {
12601
+ try {
12602
+ if (previous === void 0) await fs.rm(file, { force: true });
12603
+ else await writeText(file, previous);
12604
+ } catch {
12605
+ }
12606
+ }
12566
12607
  async function archiveChange(opts) {
12567
12608
  const root = path16.resolve(opts.root);
12568
12609
  const diagnostics = [];
@@ -12587,7 +12628,17 @@ async function archiveChange(opts) {
12587
12628
  }
12588
12629
  if (!opts.dryRun) {
12589
12630
  await ensureDir(path16.dirname(targetFix));
12590
- await fs.rename(change.dir, targetFix);
12631
+ try {
12632
+ await moveDirectory(change.dir, targetFix);
12633
+ } catch (error) {
12634
+ diagnostics.push(
12635
+ diag("ATLAS-ARCH-004", "error", `No se pudo mover el cambio al hist\xF3rico: ${error.message}`, {
12636
+ path: change.dir,
12637
+ suggestion: "Cierra las pesta\xF1as con archivos de este cambio (y espera unos segundos si OneDrive est\xE1 sincronizando) y vuelve a intentar"
12638
+ })
12639
+ );
12640
+ return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: false };
12641
+ }
12591
12642
  await regenerateIndex(root, config);
12592
12643
  }
12593
12644
  return { slug: opts.slug, archivedTo: targetFix, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
@@ -12640,7 +12691,18 @@ ${body}`;
12640
12691
  if (!opts.dryRun) {
12641
12692
  await writeText(specFile, nextContent);
12642
12693
  await ensureDir(archiveDir);
12643
- await fs.rename(change.dir, target);
12694
+ try {
12695
+ await moveDirectory(change.dir, target);
12696
+ } catch (error) {
12697
+ await restoreFile(specFile, existing);
12698
+ diagnostics.push(
12699
+ diag("ATLAS-ARCH-004", "error", `No se pudo mover el cambio al hist\xF3rico: ${error.message}`, {
12700
+ path: change.dir,
12701
+ suggestion: "Cierra las pesta\xF1as con archivos de este cambio (y espera unos segundos si OneDrive est\xE1 sincronizando) y vuelve a intentar; el cambio y la spec viva quedaron como estaban"
12702
+ })
12703
+ );
12704
+ return { slug: opts.slug, domain, fold, diagnostics, dryRun: false };
12705
+ }
12644
12706
  await regenerateIndex(root, config, now);
12645
12707
  }
12646
12708
  return { slug: opts.slug, domain, archivedTo: target, fold, diagnostics, dryRun: opts.dryRun ?? false };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specatlas",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
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.12"
43
+ "@specatlas/core": "0.1.13"
44
44
  },
45
45
  "scripts": {
46
46
  "typecheck": "tsc --noEmit",