@specatlas/core 0.1.11 → 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.
|
@@ -17,12 +17,17 @@ function localDate(date = /* @__PURE__ */ new Date()) {
|
|
|
17
17
|
function localMonth(date = /* @__PURE__ */ new Date()) {
|
|
18
18
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}`;
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function localOffset(date = /* @__PURE__ */ new Date()) {
|
|
21
21
|
const offset = -date.getTimezoneOffset();
|
|
22
22
|
const sign = offset >= 0 ? "+" : "-";
|
|
23
23
|
const absolute = Math.abs(offset);
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
return `${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`;
|
|
25
|
+
}
|
|
26
|
+
function localStamp(date = /* @__PURE__ */ new Date()) {
|
|
27
|
+
return `${localDate(date)} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())} ${localOffset(date)}`;
|
|
28
|
+
}
|
|
29
|
+
function localCompact(date = /* @__PURE__ */ new Date()) {
|
|
30
|
+
return `${localDate(date).replace(/-/g, "")}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
// src/approvals.ts
|
|
@@ -219,7 +224,7 @@ async function signApproval(opts) {
|
|
|
219
224
|
artifact: rel,
|
|
220
225
|
artifactHash: artifactHash(content),
|
|
221
226
|
approvedBy: opts.by.trim(),
|
|
222
|
-
approvedAt:
|
|
227
|
+
approvedAt: localStamp(now),
|
|
223
228
|
channel: opts.channel ?? "cli"
|
|
224
229
|
};
|
|
225
230
|
if (opts.note) approval.note = opts.note;
|
|
@@ -255,7 +260,9 @@ export {
|
|
|
255
260
|
shortHash,
|
|
256
261
|
localDate,
|
|
257
262
|
localMonth,
|
|
258
|
-
|
|
263
|
+
localOffset,
|
|
264
|
+
localStamp,
|
|
265
|
+
localCompact,
|
|
259
266
|
changeMetaSchema,
|
|
260
267
|
parseChangeMeta,
|
|
261
268
|
approvalsSchema,
|
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";
|
|
@@ -590,7 +591,9 @@ declare function shortHash(hash: string): string;
|
|
|
590
591
|
|
|
591
592
|
declare function localDate(date?: Date): string;
|
|
592
593
|
declare function localMonth(date?: Date): string;
|
|
593
|
-
declare function
|
|
594
|
+
declare function localOffset(date?: Date): string;
|
|
595
|
+
declare function localStamp(date?: Date): string;
|
|
596
|
+
declare function localCompact(date?: Date): string;
|
|
594
597
|
|
|
595
598
|
declare const REQ_HEAD_RE: RegExp;
|
|
596
599
|
declare const SCENARIO_HEAD_RE: RegExp;
|
|
@@ -1641,6 +1644,14 @@ interface FoldOutcome {
|
|
|
1641
1644
|
diagnostics: Diagnostic[];
|
|
1642
1645
|
}
|
|
1643
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
|
+
}>;
|
|
1644
1655
|
interface ArchiveOptions {
|
|
1645
1656
|
root: string;
|
|
1646
1657
|
slug: string;
|
|
@@ -1698,4 +1709,4 @@ declare function livingRequirementsMap(specs: Array<{
|
|
|
1698
1709
|
}>): Map<string, Requirement>;
|
|
1699
1710
|
declare function runCiGate(opts: CiOptions): Promise<CiResult>;
|
|
1700
1711
|
|
|
1701
|
-
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,
|
|
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
|
@@ -6,15 +6,17 @@ import {
|
|
|
6
6
|
countBySeverity,
|
|
7
7
|
diag,
|
|
8
8
|
hasErrors,
|
|
9
|
+
localCompact,
|
|
9
10
|
localDate,
|
|
10
|
-
localIso,
|
|
11
11
|
localMonth,
|
|
12
|
+
localOffset,
|
|
13
|
+
localStamp,
|
|
12
14
|
parseApprovals,
|
|
13
15
|
parseChangeMeta,
|
|
14
16
|
sha256,
|
|
15
17
|
shortHash,
|
|
16
18
|
signApproval
|
|
17
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WF6H5ITI.js";
|
|
18
20
|
import {
|
|
19
21
|
copyFile,
|
|
20
22
|
ensureDir,
|
|
@@ -714,12 +716,12 @@ function lintDelta(delta, livingRequirements, path19, opts = {}) {
|
|
|
714
716
|
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path19, line: req.line }));
|
|
715
717
|
}
|
|
716
718
|
}
|
|
717
|
-
for (const
|
|
718
|
-
if (
|
|
719
|
-
out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${
|
|
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 }));
|
|
720
722
|
}
|
|
721
|
-
if (!livingRequirements.has(
|
|
722
|
-
out.push(diag("TRACE-007", "error", `RENAMED ${
|
|
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 }));
|
|
723
725
|
}
|
|
724
726
|
}
|
|
725
727
|
return out;
|
|
@@ -1972,7 +1974,7 @@ async function approveFromGithub(opts) {
|
|
|
1972
1974
|
if (!by) {
|
|
1973
1975
|
return { approved: false, label, issueNumber: number, diagnostics: [diag("ATLAS-GH-011", "error", "No se pudo determinar qui\xE9n aprueba (--by ni `gh api user`)")] };
|
|
1974
1976
|
}
|
|
1975
|
-
const { signApproval: signApproval2 } = await import("./approvals-
|
|
1977
|
+
const { signApproval: signApproval2 } = await import("./approvals-2PO6223I.js");
|
|
1976
1978
|
const signed = await signApproval2({
|
|
1977
1979
|
root: opts.root,
|
|
1978
1980
|
artifact: path5.posix.join("changes", opts.slug, "spec.md"),
|
|
@@ -2125,7 +2127,7 @@ ${exec.stderr}`);
|
|
|
2125
2127
|
scenario,
|
|
2126
2128
|
method,
|
|
2127
2129
|
result,
|
|
2128
|
-
date:
|
|
2130
|
+
date: localStamp(opts.now),
|
|
2129
2131
|
by: opts.by.trim(),
|
|
2130
2132
|
line: 0
|
|
2131
2133
|
};
|
|
@@ -2195,7 +2197,7 @@ var RUN_EVENT_TYPES = [
|
|
|
2195
2197
|
"run_completed"
|
|
2196
2198
|
];
|
|
2197
2199
|
function generateRunId(now = /* @__PURE__ */ new Date()) {
|
|
2198
|
-
const iso =
|
|
2200
|
+
const iso = localCompact(now);
|
|
2199
2201
|
return `${iso.slice(0, 8)}-${iso.slice(8, 14)}-${randomBytes(3).toString("hex")}`;
|
|
2200
2202
|
}
|
|
2201
2203
|
function runsDir(root) {
|
|
@@ -2208,8 +2210,8 @@ async function createRun(root, slug, phase, inputs, now = /* @__PURE__ */ new Da
|
|
|
2208
2210
|
slug,
|
|
2209
2211
|
phase,
|
|
2210
2212
|
status: "active",
|
|
2211
|
-
startedAt:
|
|
2212
|
-
updatedAt:
|
|
2213
|
+
startedAt: localStamp(now),
|
|
2214
|
+
updatedAt: localStamp(now)
|
|
2213
2215
|
};
|
|
2214
2216
|
if (inputs) state.inputs = inputs;
|
|
2215
2217
|
const dir = path7.join(runsDir(root), runId);
|
|
@@ -2221,7 +2223,7 @@ async function createRun(root, slug, phase, inputs, now = /* @__PURE__ */ new Da
|
|
|
2221
2223
|
return state;
|
|
2222
2224
|
}
|
|
2223
2225
|
async function appendRunEvent(root, runId, type, data, now = /* @__PURE__ */ new Date()) {
|
|
2224
|
-
const event = { eventId: randomBytes(4).toString("hex"), at:
|
|
2226
|
+
const event = { eventId: randomBytes(4).toString("hex"), at: localStamp(now), type };
|
|
2225
2227
|
if (data) event.data = data;
|
|
2226
2228
|
const file = path7.join(runsDir(root), runId, "events.jsonl");
|
|
2227
2229
|
const previous = await readTextIfExists(file) ?? "";
|
|
@@ -2235,7 +2237,7 @@ async function updateRunStatus(root, runId, status, now = /* @__PURE__ */ new Da
|
|
|
2235
2237
|
if (raw === void 0) return;
|
|
2236
2238
|
const state = JSON.parse(raw);
|
|
2237
2239
|
state.status = status;
|
|
2238
|
-
state.updatedAt =
|
|
2240
|
+
state.updatedAt = localStamp(now);
|
|
2239
2241
|
await writeText(file, `${JSON.stringify(state, null, 2)}
|
|
2240
2242
|
`);
|
|
2241
2243
|
}
|
|
@@ -2468,7 +2470,7 @@ async function writeMockupPlan(root, slug, plan, now = /* @__PURE__ */ new Date(
|
|
|
2468
2470
|
if (await exists(file)) return file;
|
|
2469
2471
|
const doc = {
|
|
2470
2472
|
schema_version: 1,
|
|
2471
|
-
generated_at:
|
|
2473
|
+
generated_at: localStamp(now),
|
|
2472
2474
|
platform: plan.platform,
|
|
2473
2475
|
screens: plan.screens.map((s) => ({ id: s.id, title: s.title, file: s.file, illustrates: s.illustrates, states: s.states, breakpoints: s.breakpoints }))
|
|
2474
2476
|
};
|
|
@@ -2484,7 +2486,7 @@ async function writeMockupManifest(root, slug, plan, inputsHash, now = /* @__PUR
|
|
|
2484
2486
|
level: "hifi",
|
|
2485
2487
|
platform: plan.platform,
|
|
2486
2488
|
inputs_hash: inputsHash,
|
|
2487
|
-
generated_at:
|
|
2489
|
+
generated_at: localStamp(now),
|
|
2488
2490
|
screens: plan.screens.map((s) => ({ id: s.id, title: s.title, file: s.file, illustrates: s.illustrates, states: s.states, breakpoints: s.breakpoints })),
|
|
2489
2491
|
screenshots: []
|
|
2490
2492
|
};
|
|
@@ -2933,7 +2935,7 @@ async function runAnalyze(opts) {
|
|
|
2933
2935
|
if (packs) result.packs = packs;
|
|
2934
2936
|
if (opts.write !== false) {
|
|
2935
2937
|
const file = path10.join(change.dir, "analyze.md");
|
|
2936
|
-
await writeText(file, renderAnalyze(change, result,
|
|
2938
|
+
await writeText(file, renderAnalyze(change, result, localStamp(opts.now)));
|
|
2937
2939
|
result.path = file;
|
|
2938
2940
|
}
|
|
2939
2941
|
return result;
|
|
@@ -3075,7 +3077,7 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
|
|
|
3075
3077
|
archived: archivedEntries.length
|
|
3076
3078
|
};
|
|
3077
3079
|
return {
|
|
3078
|
-
generatedAt:
|
|
3080
|
+
generatedAt: localStamp(now),
|
|
3079
3081
|
project: config.project.name,
|
|
3080
3082
|
root: resolved,
|
|
3081
3083
|
changes,
|
|
@@ -3135,7 +3137,7 @@ async function generatePresentation(opts) {
|
|
|
3135
3137
|
project: config.project.name,
|
|
3136
3138
|
version: change.delta.sectionsFound.length,
|
|
3137
3139
|
hash,
|
|
3138
|
-
generatedAt:
|
|
3140
|
+
generatedAt: localStamp(now),
|
|
3139
3141
|
proposalHtml: proposalBody ? renderMarkdown(proposalBody) : `<p><em>${labels.noProposal}</em></p>`,
|
|
3140
3142
|
requirements: change.delta ? [...change.delta.added.map((r) => ({ req: r, kind: "added" })), ...change.delta.modified.map((r) => ({ req: r, kind: "modified" }))] : [],
|
|
3141
3143
|
evidence: new Map((change.verify?.evidence ?? []).map((e) => [e.scenario, e.result])),
|
|
@@ -3538,7 +3540,7 @@ function adoptReport(input) {
|
|
|
3538
3540
|
lines.push(es ? "# Adopci\xF3n (brownfield)" : "# Adoption (brownfield)");
|
|
3539
3541
|
lines.push("");
|
|
3540
3542
|
lines.push(es ? `- Proyecto: ${input.config.project.name}` : `- Project: ${input.config.project.name}`);
|
|
3541
|
-
lines.push(es ? `- Generado: ${
|
|
3543
|
+
lines.push(es ? `- Generado: ${localStamp(input.now)}` : `- Generated: ${localStamp(input.now)}`);
|
|
3542
3544
|
lines.push(es ? `- Archivos de c\xF3digo inventariados: ${input.files} (de ${input.inventoried} archivos totales)` : `- Inventoried source files: ${input.files} (of ${input.inventoried} total files)`);
|
|
3543
3545
|
if (input.detection) lines.push(es ? `- Stack detectado: ${input.detection.name} (${input.detection.score} puntos)` : `- Detected stack: ${input.detection.name} (${input.detection.score})`);
|
|
3544
3546
|
lines.push("");
|
|
@@ -3593,7 +3595,7 @@ async function initWorkspace(opts) {
|
|
|
3593
3595
|
created.push(path14.join(sddDir, "glossary.md"));
|
|
3594
3596
|
const detected = await detectIfPossible(root, opts.profilesDir);
|
|
3595
3597
|
if (detected) {
|
|
3596
|
-
const yaml = detectionToYaml(detected,
|
|
3598
|
+
const yaml = detectionToYaml(detected, localStamp(opts.now));
|
|
3597
3599
|
await writeText(path14.join(sddDir, "profiles", "detected.yaml"), yaml);
|
|
3598
3600
|
created.push(path14.join(sddDir, "profiles", "detected.yaml"));
|
|
3599
3601
|
if (detected.best) {
|
|
@@ -3698,6 +3700,7 @@ async function createChange(opts) {
|
|
|
3698
3700
|
|
|
3699
3701
|
// src/archive.ts
|
|
3700
3702
|
import { promises as fs } from "fs";
|
|
3703
|
+
import { cp, rename, rm } from "fs/promises";
|
|
3701
3704
|
import path16 from "path";
|
|
3702
3705
|
var REQ_HEADER_RE = /^###\s+(?:Requisito|Requirement):\s+(REQ-[A-Z0-9-]+)\s*(?:—|-|–)\s*(.*)$/;
|
|
3703
3706
|
function foldDelta(livingBody, delta, language = "es") {
|
|
@@ -3742,16 +3745,16 @@ function foldDelta(livingBody, delta, language = "es") {
|
|
|
3742
3745
|
lines = [...lines.slice(0, range.start), ...lines.slice(range.end)];
|
|
3743
3746
|
applied.removed.push(req.id);
|
|
3744
3747
|
}
|
|
3745
|
-
for (const
|
|
3746
|
-
const range = findRange(
|
|
3748
|
+
for (const rename2 of delta.renamed) {
|
|
3749
|
+
const range = findRange(rename2.from.id);
|
|
3747
3750
|
if (!range) {
|
|
3748
|
-
diagnostics.push(diag("TRACE-007", "error", `No se puede renombrar ${
|
|
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 }));
|
|
3749
3752
|
continue;
|
|
3750
3753
|
}
|
|
3751
3754
|
const header = lines[range.start] ?? "";
|
|
3752
3755
|
const label = /Requirement/i.test(header) ? "Requirement" : "Requisito";
|
|
3753
|
-
lines[range.start] = header.replace(REQ_HEADER_RE, (_match, id) => `### ${label}: ${id} \u2014 ${
|
|
3754
|
-
applied.renamed.push(
|
|
3756
|
+
lines[range.start] = header.replace(REQ_HEADER_RE, (_match, id) => `### ${label}: ${id} \u2014 ${rename2.to.title}`);
|
|
3757
|
+
applied.renamed.push(rename2.from.id);
|
|
3755
3758
|
}
|
|
3756
3759
|
for (const req of delta.added) {
|
|
3757
3760
|
const rendered = renderRequirement(req, language);
|
|
@@ -3766,6 +3769,46 @@ function trimTrailingBlanks(lines) {
|
|
|
3766
3769
|
while (out.length > 0 && (out[out.length - 1] ?? "").trim() === "") out.pop();
|
|
3767
3770
|
return out;
|
|
3768
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
|
+
}
|
|
3769
3812
|
async function archiveChange(opts) {
|
|
3770
3813
|
const root = path16.resolve(opts.root);
|
|
3771
3814
|
const diagnostics = [];
|
|
@@ -3790,7 +3833,17 @@ async function archiveChange(opts) {
|
|
|
3790
3833
|
}
|
|
3791
3834
|
if (!opts.dryRun) {
|
|
3792
3835
|
await ensureDir(path16.dirname(targetFix));
|
|
3793
|
-
|
|
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
|
+
}
|
|
3794
3847
|
await regenerateIndex(root, config);
|
|
3795
3848
|
}
|
|
3796
3849
|
return { slug: opts.slug, archivedTo: targetFix, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
|
|
@@ -3843,7 +3896,18 @@ ${body}`;
|
|
|
3843
3896
|
if (!opts.dryRun) {
|
|
3844
3897
|
await writeText(specFile, nextContent);
|
|
3845
3898
|
await ensureDir(archiveDir);
|
|
3846
|
-
|
|
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
|
+
}
|
|
3847
3911
|
await regenerateIndex(root, config, now);
|
|
3848
3912
|
}
|
|
3849
3913
|
return { slug: opts.slug, domain, archivedTo: target, fold, diagnostics, dryRun: opts.dryRun ?? false };
|
|
@@ -4082,13 +4146,16 @@ export {
|
|
|
4082
4146
|
loadProjectPacks,
|
|
4083
4147
|
loadSpecs,
|
|
4084
4148
|
loadWorkspace,
|
|
4149
|
+
localCompact,
|
|
4085
4150
|
localDate,
|
|
4086
|
-
localIso,
|
|
4087
4151
|
localMonth,
|
|
4152
|
+
localOffset,
|
|
4153
|
+
localStamp,
|
|
4088
4154
|
matchGlob,
|
|
4089
4155
|
mockupManifestSchema,
|
|
4090
4156
|
mockupsDir,
|
|
4091
4157
|
mockupsReady,
|
|
4158
|
+
moveDirectory,
|
|
4092
4159
|
packFindings,
|
|
4093
4160
|
parseApprovals,
|
|
4094
4161
|
parseChangeMeta,
|