@specatlas/core 0.1.12 → 0.1.13

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { rename, cp, rm } from 'node:fs/promises';
2
3
  export { defaultTokens, renderDocument, renderStyles } from '@specatlas/render';
3
4
 
4
5
  declare const CORE_VERSION = "0.0.1";
@@ -1643,6 +1644,14 @@ interface FoldOutcome {
1643
1644
  diagnostics: Diagnostic[];
1644
1645
  }
1645
1646
  declare function foldDelta(livingBody: string, delta: Delta, language?: Language): FoldOutcome;
1647
+ interface MoveOps {
1648
+ rename?: typeof rename;
1649
+ cp?: typeof cp;
1650
+ rm?: typeof rm;
1651
+ }
1652
+ declare function moveDirectory(from: string, to: string, ops?: MoveOps): Promise<{
1653
+ strategy: 'rename' | 'copy';
1654
+ }>;
1646
1655
  interface ArchiveOptions {
1647
1656
  root: string;
1648
1657
  slug: string;
@@ -1700,4 +1709,4 @@ declare function livingRequirementsMap(specs: Array<{
1700
1709
  }>): Map<string, Requirement>;
1701
1710
  declare function runCiGate(opts: CiOptions): Promise<CiResult>;
1702
1711
 
1703
- export { type AdoptDomain, type AdoptOptions, type AdoptResult, type AgingBucket, type AnalyzeOptions, type AnalyzePackSummary, type AnalyzeResult, type Approval, type ApprovalStatus, type ApprovalsFile, type ApprovalsIndex, type ApproveFromGithubOptions, type ApproveFromGithubResult, type ArchiveOptions, type ArchiveResult, type AtlasConfig, type AttentionItem, BLOCK_HEAD_RE, BUILTIN_PACKS, type BlockWavePlan, CONFIG_FILE, CORE_VERSION, type CaptureResult, type Change, type ChangeMeta, type ChangeMetrics, type ChangeState, type CiCheck, type CiExtraCheck, type CiOptions, type CiResult, type Delta, type DeltaOp, type DeriveInput, type DerivedState, type DetectionResult, type Diagnostic, type DoctorReport, type Domain, type Evidence, type EvidenceMethod, type EvidenceResult, type ExecOptions, type ExecResult, type FoldOutcome, type FrontmatterResult, type GhRunner, type GitHubRepo, type InitOptions, type InitResult, type IssueBodyInput, type Lane, type Language, type LinkedIssue, type LintOptions, type LoadedConfig, type MockupCheckResult, type MockupManifest, type MockupPlan, type MockupPlanScreen, type MockupScreen, type NewChangeOptions, type NewChangeResult, type NextAction, type Override, type Pack, type PackCheck, type PackCheckResult, type PackCheckType, type PackEvaluation, type PresentOptions, type PresentResult, type ProfileMatch, REQ_HEAD_RE, REQ_ID_RE, RULE_ID_RE, RULE_RE, RUN_EVENT_TYPES, type RecordEvidenceOptions, type RecordEvidenceResult, type Rename, type Requirement, type RiskLevel, type Rule, type RunEvent, type RunEventType, type RunRecord, type RunState, type RunStatus, SCENARIO_HEAD_RE, SCENARIO_ID_RE, SDD_DIR, SLUG_RE, type Scenario, type Severity, type SignApprovalOptions, type SignApprovalResult, type SpecFile, type SpecRef, type StackProfile, type SyncGithubOptions, type SyncGithubResult, TASK_ID_RE, TASK_LINE_RE, type Task, type TaskBlock, type TasksFile, type TemplateSet, type TraceEdge, type TraceFinding, type TraceGraph, type TraceInput, type TraceNode, type TraceResult, type VerifyFile, type WalkEntry, type WavePlan, type Workspace, type WorkspaceMetrics, adoptWorkspace, appendRunEvent, approvalsSchema, approveFromGithub, archiveChange, artifactHash, atlasConfigSchema, buildTraceGraph, canonicalizeMarkdown, captureMockups, changeMarker, changeMetaSchema, changeMetaYaml, checkMockups, checkTrace, collectMetrics, commentIssue, compareTaskIds, computeInputsHash, configToYaml, copyFile, countBySeverity, createChange, createIssue, createRun, defaultConfig, deriveState, detectProfiles, detectRepo, detectionToYaml, diag, editIssue, emitFrontmatter, ensureDir, ensureSddDirs, esc, evaluatePacks, evidenceSummary, exists, findLinkedIssue, findWorkspaceRoot, firstToken, foldDelta, generatePresentation, generateRunId, getNumber, getString, ghAuthStatus, ghAvailable, hasErrors, hasShellMetacharacters, indexMarkdown, initWorkspace, inline, isCommandAllowed, isDirectory, issueBody, issueLabels, laneOrDefault, lintDelta, lintMockupHtml, lintMockupManifest, lintRequirement, lintSpec, lintTasks, lintVerify, listChangeSlugs, listDir, listDirs, listRuns, livingRequirementsMap, loadActiveProfile, loadApprovals, loadChange, loadConfig, loadDetectedBest, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, localCompact, localDate, localMonth, localOffset, localStamp, matchGlob, mockupManifestSchema, mockupsDir, mockupsReady, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFrontmatter, parseGitHubRemote, parseIssueUrl, parseRequirementBlocks, parseSpecFile, parseTasksFile, parseVerifyFile, patchChangeMeta, planBlock, planMockups, planWaves, profileSchema, readMockupManifest, readRun, readText, readTextIfExists, recordEvidence, regenerateIndex, renderMarkdown, renderRequirement, renderTemplate, requiresMockups, resolvePacks, runAnalyze, runCiGate, runDoctor, runProcess, setMockupRequirement, sha256, shortHash, signApproval, specHashOf, splitCommand, stateLabel, syncGithubIssue, templatesFor, toPosix, tokensFile, updateMockupScreenshots, updateRunStatus, verifyApproval, walkFiles, writeConfig, writeMockupManifest, writeMockupPlan, writeText };
1712
+ export { type AdoptDomain, type AdoptOptions, type AdoptResult, type AgingBucket, type AnalyzeOptions, type AnalyzePackSummary, type AnalyzeResult, type Approval, type ApprovalStatus, type ApprovalsFile, type ApprovalsIndex, type ApproveFromGithubOptions, type ApproveFromGithubResult, type ArchiveOptions, type ArchiveResult, type AtlasConfig, type AttentionItem, BLOCK_HEAD_RE, BUILTIN_PACKS, type BlockWavePlan, CONFIG_FILE, CORE_VERSION, type CaptureResult, type Change, type ChangeMeta, type ChangeMetrics, type ChangeState, type CiCheck, type CiExtraCheck, type CiOptions, type CiResult, type Delta, type DeltaOp, type DeriveInput, type DerivedState, type DetectionResult, type Diagnostic, type DoctorReport, type Domain, type Evidence, type EvidenceMethod, type EvidenceResult, type ExecOptions, type ExecResult, type FoldOutcome, type FrontmatterResult, type GhRunner, type GitHubRepo, type InitOptions, type InitResult, type IssueBodyInput, type Lane, type Language, type LinkedIssue, type LintOptions, type LoadedConfig, type MockupCheckResult, type MockupManifest, type MockupPlan, type MockupPlanScreen, type MockupScreen, type MoveOps, type NewChangeOptions, type NewChangeResult, type NextAction, type Override, type Pack, type PackCheck, type PackCheckResult, type PackCheckType, type PackEvaluation, type PresentOptions, type PresentResult, type ProfileMatch, REQ_HEAD_RE, REQ_ID_RE, RULE_ID_RE, RULE_RE, RUN_EVENT_TYPES, type RecordEvidenceOptions, type RecordEvidenceResult, type Rename, type Requirement, type RiskLevel, type Rule, type RunEvent, type RunEventType, type RunRecord, type RunState, type RunStatus, SCENARIO_HEAD_RE, SCENARIO_ID_RE, SDD_DIR, SLUG_RE, type Scenario, type Severity, type SignApprovalOptions, type SignApprovalResult, type SpecFile, type SpecRef, type StackProfile, type SyncGithubOptions, type SyncGithubResult, TASK_ID_RE, TASK_LINE_RE, type Task, type TaskBlock, type TasksFile, type TemplateSet, type TraceEdge, type TraceFinding, type TraceGraph, type TraceInput, type TraceNode, type TraceResult, type VerifyFile, type WalkEntry, type WavePlan, type Workspace, type WorkspaceMetrics, adoptWorkspace, appendRunEvent, approvalsSchema, approveFromGithub, archiveChange, artifactHash, atlasConfigSchema, buildTraceGraph, canonicalizeMarkdown, captureMockups, changeMarker, changeMetaSchema, changeMetaYaml, checkMockups, checkTrace, collectMetrics, commentIssue, compareTaskIds, computeInputsHash, configToYaml, copyFile, countBySeverity, createChange, createIssue, createRun, defaultConfig, deriveState, detectProfiles, detectRepo, detectionToYaml, diag, editIssue, emitFrontmatter, ensureDir, ensureSddDirs, esc, evaluatePacks, evidenceSummary, exists, findLinkedIssue, findWorkspaceRoot, firstToken, foldDelta, generatePresentation, generateRunId, getNumber, getString, ghAuthStatus, ghAvailable, hasErrors, hasShellMetacharacters, indexMarkdown, initWorkspace, inline, isCommandAllowed, isDirectory, issueBody, issueLabels, laneOrDefault, lintDelta, lintMockupHtml, lintMockupManifest, lintRequirement, lintSpec, lintTasks, lintVerify, listChangeSlugs, listDir, listDirs, listRuns, livingRequirementsMap, loadActiveProfile, loadApprovals, loadChange, loadConfig, loadDetectedBest, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, localCompact, localDate, localMonth, localOffset, localStamp, matchGlob, mockupManifestSchema, mockupsDir, mockupsReady, moveDirectory, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFrontmatter, parseGitHubRemote, parseIssueUrl, parseRequirementBlocks, parseSpecFile, parseTasksFile, parseVerifyFile, patchChangeMeta, planBlock, planMockups, planWaves, profileSchema, readMockupManifest, readRun, readText, readTextIfExists, recordEvidence, regenerateIndex, renderMarkdown, renderRequirement, renderTemplate, requiresMockups, resolvePacks, runAnalyze, runCiGate, runDoctor, runProcess, setMockupRequirement, sha256, shortHash, signApproval, specHashOf, splitCommand, stateLabel, syncGithubIssue, templatesFor, toPosix, tokensFile, updateMockupScreenshots, updateRunStatus, verifyApproval, walkFiles, writeConfig, writeMockupManifest, writeMockupPlan, writeText };
package/dist/index.js CHANGED
@@ -716,12 +716,12 @@ function lintDelta(delta, livingRequirements, path19, opts = {}) {
716
716
  out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path19, line: req.line }));
717
717
  }
718
718
  }
719
- for (const rename of delta.renamed) {
720
- if (rename.from.id !== rename.to.id) {
721
- out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename.from.id} \u2192 ${rename.to.id}); los ids son inmutables`, { path: path19, line: rename.line }));
719
+ for (const rename2 of delta.renamed) {
720
+ if (rename2.from.id !== rename2.to.id) {
721
+ out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path19, line: rename2.line }));
722
722
  }
723
- if (!livingRequirements.has(rename.from.id)) {
724
- out.push(diag("TRACE-007", "error", `RENAMED ${rename.from.id} no existe en la spec viva`, { path: path19, line: rename.line }));
723
+ if (!livingRequirements.has(rename2.from.id)) {
724
+ out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path19, line: rename2.line }));
725
725
  }
726
726
  }
727
727
  return out;
@@ -3700,6 +3700,7 @@ async function createChange(opts) {
3700
3700
 
3701
3701
  // src/archive.ts
3702
3702
  import { promises as fs } from "fs";
3703
+ import { cp, rename, rm } from "fs/promises";
3703
3704
  import path16 from "path";
3704
3705
  var REQ_HEADER_RE = /^###\s+(?:Requisito|Requirement):\s+(REQ-[A-Z0-9-]+)\s*(?:—|-|–)\s*(.*)$/;
3705
3706
  function foldDelta(livingBody, delta, language = "es") {
@@ -3744,16 +3745,16 @@ function foldDelta(livingBody, delta, language = "es") {
3744
3745
  lines = [...lines.slice(0, range.start), ...lines.slice(range.end)];
3745
3746
  applied.removed.push(req.id);
3746
3747
  }
3747
- for (const rename of delta.renamed) {
3748
- const range = findRange(rename.from.id);
3748
+ for (const rename2 of delta.renamed) {
3749
+ const range = findRange(rename2.from.id);
3749
3750
  if (!range) {
3750
- 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 }));
3751
+ 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 }));
3751
3752
  continue;
3752
3753
  }
3753
3754
  const header = lines[range.start] ?? "";
3754
3755
  const label = /Requirement/i.test(header) ? "Requirement" : "Requisito";
3755
- lines[range.start] = header.replace(REQ_HEADER_RE, (_match, id) => `### ${label}: ${id} \u2014 ${rename.to.title}`);
3756
- applied.renamed.push(rename.from.id);
3756
+ lines[range.start] = header.replace(REQ_HEADER_RE, (_match, id) => `### ${label}: ${id} \u2014 ${rename2.to.title}`);
3757
+ applied.renamed.push(rename2.from.id);
3757
3758
  }
3758
3759
  for (const req of delta.added) {
3759
3760
  const rendered = renderRequirement(req, language);
@@ -3768,6 +3769,46 @@ function trimTrailingBlanks(lines) {
3768
3769
  while (out.length > 0 && (out[out.length - 1] ?? "").trim() === "") out.pop();
3769
3770
  return out;
3770
3771
  }
3772
+ var TRANSIENT_MOVE_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "ENOTEMPTY", "EACCES"]);
3773
+ function delay(ms) {
3774
+ return new Promise((resolve) => setTimeout(resolve, ms));
3775
+ }
3776
+ async function moveDirectory(from, to, ops = {}) {
3777
+ const doRename = ops.rename ?? rename;
3778
+ const doCopy = ops.cp ?? cp;
3779
+ const doRemove = ops.rm ?? rm;
3780
+ let lastError;
3781
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
3782
+ try {
3783
+ await doRename(from, to);
3784
+ return { strategy: "rename" };
3785
+ } catch (error) {
3786
+ lastError = error;
3787
+ const code = error.code ?? "";
3788
+ if (!TRANSIENT_MOVE_CODES.has(code)) throw error;
3789
+ await delay(250 * attempt);
3790
+ }
3791
+ }
3792
+ await doCopy(from, to, { recursive: true, force: true });
3793
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
3794
+ try {
3795
+ await doRemove(from, { recursive: true, force: true, maxRetries: 2 });
3796
+ return { strategy: "copy" };
3797
+ } catch (error) {
3798
+ lastError = error;
3799
+ await delay(350 * attempt);
3800
+ }
3801
+ }
3802
+ await doRemove(to, { recursive: true, force: true }).catch(() => void 0);
3803
+ throw lastError;
3804
+ }
3805
+ async function restoreFile(file, previous) {
3806
+ try {
3807
+ if (previous === void 0) await fs.rm(file, { force: true });
3808
+ else await writeText(file, previous);
3809
+ } catch {
3810
+ }
3811
+ }
3771
3812
  async function archiveChange(opts) {
3772
3813
  const root = path16.resolve(opts.root);
3773
3814
  const diagnostics = [];
@@ -3792,7 +3833,17 @@ async function archiveChange(opts) {
3792
3833
  }
3793
3834
  if (!opts.dryRun) {
3794
3835
  await ensureDir(path16.dirname(targetFix));
3795
- await fs.rename(change.dir, targetFix);
3836
+ try {
3837
+ await moveDirectory(change.dir, targetFix);
3838
+ } catch (error) {
3839
+ diagnostics.push(
3840
+ diag("ATLAS-ARCH-004", "error", `No se pudo mover el cambio al hist\xF3rico: ${error.message}`, {
3841
+ path: change.dir,
3842
+ suggestion: "Cierra las pesta\xF1as con archivos de este cambio (y espera unos segundos si OneDrive est\xE1 sincronizando) y vuelve a intentar"
3843
+ })
3844
+ );
3845
+ return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: false };
3846
+ }
3796
3847
  await regenerateIndex(root, config);
3797
3848
  }
3798
3849
  return { slug: opts.slug, archivedTo: targetFix, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
@@ -3845,7 +3896,18 @@ ${body}`;
3845
3896
  if (!opts.dryRun) {
3846
3897
  await writeText(specFile, nextContent);
3847
3898
  await ensureDir(archiveDir);
3848
- await fs.rename(change.dir, target);
3899
+ try {
3900
+ await moveDirectory(change.dir, target);
3901
+ } catch (error) {
3902
+ await restoreFile(specFile, existing);
3903
+ diagnostics.push(
3904
+ diag("ATLAS-ARCH-004", "error", `No se pudo mover el cambio al hist\xF3rico: ${error.message}`, {
3905
+ path: change.dir,
3906
+ 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"
3907
+ })
3908
+ );
3909
+ return { slug: opts.slug, domain, fold, diagnostics, dryRun: false };
3910
+ }
3849
3911
  await regenerateIndex(root, config, now);
3850
3912
  }
3851
3913
  return { slug: opts.slug, domain, archivedTo: target, fold, diagnostics, dryRun: opts.dryRun ?? false };
@@ -4093,6 +4155,7 @@ export {
4093
4155
  mockupManifestSchema,
4094
4156
  mockupsDir,
4095
4157
  mockupsReady,
4158
+ moveDirectory,
4096
4159
  packFindings,
4097
4160
  parseApprovals,
4098
4161
  parseChangeMeta,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specatlas/core",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Kernel determinista de SpecAtlas: parsing, validación, trazabilidad, olas y ciclo de vida",
5
5
  "license": "MIT",
6
6
  "type": "module",