@aisdlc/core 1.3.0 → 1.3.1

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.cjs CHANGED
@@ -750,6 +750,12 @@ function walkMdFiles(dir, fileList = [], excludeDirs = [
750
750
  "node_modules",
751
751
  ".git",
752
752
  "dist",
753
+ "bin",
754
+ "obj",
755
+ "build",
756
+ "out",
757
+ "target",
758
+ "coverage",
753
759
  ".changeset",
754
760
  "scratch",
755
761
  "test-scaffold",
@@ -761,7 +767,9 @@ function walkMdFiles(dir, fileList = [], excludeDirs = [
761
767
  ".claude",
762
768
  ".cursor",
763
769
  ".github",
764
- ".vscode"
770
+ ".vscode",
771
+ ".vs",
772
+ ".idea"
765
773
  ]) {
766
774
  try {
767
775
  if (!fs6__namespace.existsSync(dir)) return fileList;
@@ -3312,10 +3320,18 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
3312
3320
  "node_modules",
3313
3321
  ".git",
3314
3322
  "dist",
3323
+ "bin",
3324
+ "obj",
3325
+ "build",
3326
+ "out",
3327
+ "target",
3328
+ "coverage",
3315
3329
  "scratch",
3316
3330
  "fixtures",
3317
3331
  "templates",
3318
- "examples"
3332
+ "examples",
3333
+ ".vs",
3334
+ ".idea"
3319
3335
  ]);
3320
3336
  function shouldSkipDir(name) {
3321
3337
  return SKIPPED_DIR_NAMES.has(name) || name.startsWith("temp-");
@@ -3418,7 +3434,12 @@ function verifyTraceability(options = {}) {
3418
3434
  }
3419
3435
  }
3420
3436
  if (relFile.includes("architecture/") || relFile.includes("06_runtime_view") || relFile.includes("08_security_concept") || relFile.includes("09_decisions")) {
3421
- architectureViews.push({ file: relFile, content });
3437
+ architectureViews.push({
3438
+ file: relFile,
3439
+ id: frontmatter.id ? String(frontmatter.id).trim() : void 0,
3440
+ type: typeof frontmatter.type === "string" ? frontmatter.type.trim() : void 0,
3441
+ content
3442
+ });
3422
3443
  }
3423
3444
  } catch {
3424
3445
  }
@@ -3521,8 +3542,9 @@ function verifyTraceability(options = {}) {
3521
3542
  }
3522
3543
  for (const view of architectureViews) {
3523
3544
  if (view.content.includes(reqId)) {
3524
- const viewName = path8__namespace.basename(view.file);
3525
- if (!archTraces.includes(viewName)) archTraces.push(viewName);
3545
+ const isEntityWithId = view.type === "component" || view.type === "adr" || view.type === "architecture-decision-record" || view.type === "security-enclave" || view.id !== void 0 && (view.id.startsWith("CMP-") || view.id.startsWith("ADR-") || view.id.startsWith("SEC-ENC-"));
3546
+ const viewIdentifier = isEntityWithId && view.id ? view.id : path8__namespace.basename(view.file);
3547
+ if (!archTraces.includes(viewIdentifier)) archTraces.push(viewIdentifier);
3526
3548
  }
3527
3549
  }
3528
3550
  const uniqueArchTraces = Array.from(new Set(archTraces));
@@ -3859,7 +3881,7 @@ function scanAllTestArtifacts(rootDir) {
3859
3881
  for (const e of entries) {
3860
3882
  const fullPath = path8__namespace.join(dir, e.name);
3861
3883
  if (e.isDirectory()) {
3862
- if (!["node_modules", ".git", "dist", ".changeset", "scratch", "fixtures", "templates", "examples"].includes(e.name)) {
3884
+ if (!["node_modules", ".git", "dist", "bin", "obj", "build", "out", "target", "coverage", ".changeset", "scratch", "fixtures", "templates", "examples", ".vs", ".idea"].includes(e.name)) {
3863
3885
  walk(fullPath);
3864
3886
  }
3865
3887
  } else {
@@ -7164,7 +7186,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7164
7186
  for (const n of pdac.drifts) {
7165
7187
  driftTargets.add(n.targetId);
7166
7188
  }
7189
+ const NON_GRAPH_TYPES = /* @__PURE__ */ new Set([
7190
+ "term",
7191
+ "handoff",
7192
+ "agent-handoff",
7193
+ "tasks",
7194
+ "task-plan",
7195
+ "spec-change-proposal",
7196
+ "proposal",
7197
+ "spec-design",
7198
+ "spec-change-design",
7199
+ "design",
7200
+ "delivery-spec",
7201
+ "spec",
7202
+ "pre-merge-audit-report",
7203
+ "user-design-feedback",
7204
+ "release-kpis",
7205
+ "session-telemetry",
7206
+ "license-manifest",
7207
+ "mcp-config",
7208
+ "user-manual",
7209
+ "production-manual",
7210
+ "architecture-glossary"
7211
+ ]);
7167
7212
  const scannedArtifactsMap = /* @__PURE__ */ new Map();
7213
+ const archViewByBasename = /* @__PURE__ */ new Map();
7168
7214
  const artifactDirs = ["product", "architecture", "specs", "security"];
7169
7215
  for (const dirName of artifactDirs) {
7170
7216
  const fullDirPath = path8__namespace.join(rootDir, dirName);
@@ -7174,6 +7220,7 @@ function buildGraphElements(rootDir = process.cwd()) {
7174
7220
  try {
7175
7221
  const fileContent = fs6__namespace.readFileSync(f, "utf-8");
7176
7222
  const relPath = path8__namespace.relative(rootDir, f).replace(/\\/g, "/");
7223
+ if (relPath.includes("/changes/") || relPath.startsWith("changes/")) continue;
7177
7224
  const match = fileContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
7178
7225
  if (!match) continue;
7179
7226
  let fm = {};
@@ -7185,6 +7232,9 @@ function buildGraphElements(rootDir = process.cwd()) {
7185
7232
  const id = typeof fm.id === "string" ? fm.id.trim() : void 0;
7186
7233
  if (!id) continue;
7187
7234
  let type = typeof fm.type === "string" ? fm.type.trim() : "";
7235
+ if (NON_GRAPH_TYPES.has(type) || id.startsWith("TERM-") || id.startsWith("HOF-") || id.startsWith("AHOF-") || id.startsWith("CHG-") || id.startsWith("DSG-") || id.startsWith("SPEC-") || id.startsWith("TSK-") || id.startsWith("UDF-") || id.startsWith("AUDIT-")) {
7236
+ continue;
7237
+ }
7188
7238
  let layer = "product";
7189
7239
  if (id.startsWith("JRN-") || type === "journey") {
7190
7240
  type = "journey";
@@ -7219,16 +7269,19 @@ function buildGraphElements(rootDir = process.cwd()) {
7219
7269
  } else if (id.startsWith("SEC-REQ-") || type === "security-requirement") {
7220
7270
  type = "security-requirement";
7221
7271
  layer = "requirement";
7222
- } else if (id.startsWith("FR-") || id.startsWith("QR-") || id.startsWith("CON-") || type === "requirement") {
7272
+ } else if (id.startsWith("FR-") || id.startsWith("QR-") || id.startsWith("CON-") || type === "requirement" || type === "functional-requirement" || type === "quality-requirement") {
7223
7273
  type = "requirement";
7224
7274
  layer = "requirement";
7275
+ } else if (id.startsWith("ARCH-") || relPath.includes("architecture/")) {
7276
+ type = "architecture";
7277
+ layer = "architecture";
7225
7278
  } else {
7226
7279
  type = type || "product";
7227
7280
  layer = "product";
7228
7281
  }
7229
7282
  const title = typeof fm.title === "string" ? fm.title.trim() : id;
7230
7283
  const content = fileContent.replace(/^---\r?\n[\s\S]*?\r?\n---/, "").trim();
7231
- scannedArtifactsMap.set(id, {
7284
+ const scannedArt = {
7232
7285
  id,
7233
7286
  type,
7234
7287
  title,
@@ -7236,7 +7289,12 @@ function buildGraphElements(rootDir = process.cwd()) {
7236
7289
  filePath: relPath,
7237
7290
  content,
7238
7291
  frontmatter: fm
7239
- });
7292
+ };
7293
+ if (layer === "architecture" && type === "architecture") {
7294
+ archViewByBasename.set(path8__namespace.basename(relPath), scannedArt);
7295
+ } else {
7296
+ scannedArtifactsMap.set(id, scannedArt);
7297
+ }
7240
7298
  } catch {
7241
7299
  }
7242
7300
  }
@@ -7245,7 +7303,7 @@ function buildGraphElements(rootDir = process.cwd()) {
7245
7303
  for (const r of trace.rows) {
7246
7304
  const isOk = (r.productStatus === "COMPLIANT" || r.productStatus === "CONFORME") && (r.archStatus === "COMPLIANT" || r.archStatus === "CONFORME") && (r.testStatus === "COMPLIANT" || r.testStatus === "CONFORME");
7247
7305
  const status = isOk ? "COMPLIANT" : "ORPHAN";
7248
- const reqType = r.id.startsWith("SEC-REQ-") ? "security-requirement" : r.type || "requirement";
7306
+ const reqType = r.id.startsWith("SEC-REQ-") ? "security-requirement" : "requirement";
7249
7307
  nodeMap.set(r.id, {
7250
7308
  id: r.id,
7251
7309
  label: r.id,
@@ -7305,13 +7363,16 @@ function buildGraphElements(rootDir = process.cwd()) {
7305
7363
  });
7306
7364
  if (!nodeMap.has(a)) {
7307
7365
  const aType = a.startsWith("CMP-") ? "component" : a.startsWith("ADR-") ? "adr" : a.startsWith("SEC-ENC-") ? "security-enclave" : "architecture";
7366
+ const viewArt = archViewByBasename.get(a);
7308
7367
  nodeMap.set(a, {
7309
7368
  id: a,
7310
7369
  label: a,
7311
- title: a,
7370
+ title: viewArt?.title || a,
7312
7371
  type: aType,
7313
7372
  layer: "architecture",
7314
- status: "COMPLIANT"
7373
+ status: "COMPLIANT",
7374
+ filePath: viewArt?.filePath,
7375
+ content: viewArt?.content
7315
7376
  });
7316
7377
  }
7317
7378
  }
@@ -7443,7 +7504,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7443
7504
  status: "COMPLIANT"
7444
7505
  });
7445
7506
  }
7446
- const governedBy = Array.isArray(fm["governed-by"]) ? fm["governed-by"] : [];
7507
+ const supportingActors = Array.isArray(fm["supporting-actors"]) ? fm["supporting-actors"] : Array.isArray(fm["secondary-actors"]) ? fm["secondary-actors"] : [];
7508
+ for (const actorId of supportingActors) {
7509
+ if (typeof actorId === "string" && actorId.trim()) {
7510
+ const a = actorId.trim();
7511
+ if (!nodeMap.has(a)) {
7512
+ nodeMap.set(a, {
7513
+ id: a,
7514
+ label: a,
7515
+ title: a,
7516
+ type: "actor",
7517
+ layer: "product",
7518
+ status: "COMPLIANT"
7519
+ });
7520
+ }
7521
+ addEdge({
7522
+ id: `edge-${a}-${art.id}`,
7523
+ source: a,
7524
+ target: art.id,
7525
+ label: "initiates",
7526
+ relation: "initiates",
7527
+ status: "COMPLIANT"
7528
+ });
7529
+ }
7530
+ }
7531
+ const governedBy = Array.isArray(fm["governed-by"]) ? fm["governed-by"] : Array.isArray(fm["business-rules"]) ? fm["business-rules"] : [];
7447
7532
  for (const ruleId of governedBy) {
7448
7533
  if (typeof ruleId === "string" && ruleId.trim()) {
7449
7534
  const r = ruleId.trim();
@@ -7504,7 +7589,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7504
7589
  status: "COMPLIANT"
7505
7590
  });
7506
7591
  }
7507
- const rawTargets = fm["targets-use-case"] ? [fm["targets-use-case"]] : Array.isArray(fm["targets-use-cases"]) ? fm["targets-use-cases"] : Array.isArray(fm["targets"]) ? fm["targets"] : [];
7592
+ const secondaryThreatActors = Array.isArray(fm["secondary-actors"]) ? fm["secondary-actors"] : Array.isArray(fm["supporting-actors"]) ? fm["supporting-actors"] : [];
7593
+ for (const secActor of secondaryThreatActors) {
7594
+ if (typeof secActor === "string" && secActor.trim()) {
7595
+ const sa = secActor.trim();
7596
+ if (!nodeMap.has(sa)) {
7597
+ nodeMap.set(sa, {
7598
+ id: sa,
7599
+ label: sa,
7600
+ title: sa,
7601
+ type: sa.startsWith("ACT-THREAT-") ? "threat-actor" : "actor",
7602
+ layer: "product",
7603
+ status: "COMPLIANT"
7604
+ });
7605
+ }
7606
+ addEdge({
7607
+ id: `edge-${sa}-${art.id}`,
7608
+ source: sa,
7609
+ target: art.id,
7610
+ label: "threatens",
7611
+ relation: "threatens",
7612
+ status: "COMPLIANT"
7613
+ });
7614
+ }
7615
+ }
7616
+ const rawTargets = Array.isArray(fm["targets-use-case"]) ? fm["targets-use-case"] : typeof fm["targets-use-case"] === "string" ? [fm["targets-use-case"]] : Array.isArray(fm["targets-use-cases"]) ? fm["targets-use-cases"] : Array.isArray(fm["targets"]) ? fm["targets"] : [];
7508
7617
  for (const tgt of rawTargets) {
7509
7618
  if (typeof tgt === "string" && tgt.trim()) {
7510
7619
  const t = tgt.trim();
@@ -7569,6 +7678,40 @@ function buildGraphElements(rootDir = process.cwd()) {
7569
7678
  });
7570
7679
  }
7571
7680
  }
7681
+ } else if (art.type === "adr") {
7682
+ const affectedComponents = Array.isArray(fm["affects-components"]) ? fm["affects-components"] : typeof fm["affects-components"] === "string" ? [fm["affects-components"]] : [];
7683
+ for (const cmpId of affectedComponents) {
7684
+ if (typeof cmpId === "string" && cmpId.trim()) {
7685
+ const c = cmpId.trim();
7686
+ if (nodeMap.has(c)) {
7687
+ addEdge({
7688
+ id: `edge-${art.id}-${c}`,
7689
+ source: art.id,
7690
+ target: c,
7691
+ label: "governs",
7692
+ relation: "governs",
7693
+ status: "COMPLIANT"
7694
+ });
7695
+ }
7696
+ }
7697
+ }
7698
+ } else if (art.type === "security-enclave") {
7699
+ const enforcedBy = Array.isArray(fm["enforced-by"]) ? fm["enforced-by"] : typeof fm["enforced-by"] === "string" ? [fm["enforced-by"]] : [];
7700
+ for (const secReq of enforcedBy) {
7701
+ if (typeof secReq === "string" && secReq.trim()) {
7702
+ const s = secReq.trim();
7703
+ if (nodeMap.has(s)) {
7704
+ addEdge({
7705
+ id: `edge-${s}-${art.id}`,
7706
+ source: s,
7707
+ target: art.id,
7708
+ label: "satisfies",
7709
+ relation: "satisfies",
7710
+ status: "COMPLIANT"
7711
+ });
7712
+ }
7713
+ }
7714
+ }
7572
7715
  }
7573
7716
  }
7574
7717
  for (const [id, node] of nodeMap.entries()) {
@@ -7718,6 +7861,9 @@ function collectDashboardMetrics(rootDir) {
7718
7861
  function escapeHtml(str) {
7719
7862
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
7720
7863
  }
7864
+ function safeJsonForScript(data) {
7865
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
7866
+ }
7721
7867
  function renderAutonomyModeSection(mode, statusIcon, count, roleDescription, allTasks = []) {
7722
7868
  const modeTasks = (allTasks || []).filter(
7723
7869
  (t) => (t.task?.autonomyMode || "").toUpperCase() === mode
@@ -7829,14 +7975,14 @@ function renderAutonomyModeSection(mode, statusIcon, count, roleDescription, all
7829
7975
  }
7830
7976
  function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard Interactivo de Trazabilidad y Calidad") {
7831
7977
  const cytoscapeScript = getCytoscapeScript();
7832
- const elementsJson = JSON.stringify(graphData.elements);
7978
+ const elementsJson = safeJsonForScript(graphData.elements);
7833
7979
  const now = (/* @__PURE__ */ new Date()).toISOString();
7834
7980
  return `<!DOCTYPE html>
7835
7981
  <html lang="es">
7836
7982
  <head>
7837
7983
  <meta charset="UTF-8">
7838
7984
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7839
- <title>${title}</title>
7985
+ <title>${escapeHtml(title)}</title>
7840
7986
  <style>
7841
7987
  :root {
7842
7988
  --bg-dark: #0f172a;
@@ -8211,12 +8357,12 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8211
8357
  ${metricsData.trace.rows.map((r) => {
8212
8358
  const isRowOk = (r.productStatus === "COMPLIANT" || r.productStatus === "CONFORME") && (r.archStatus === "COMPLIANT" || r.archStatus === "CONFORME") && (r.testStatus === "COMPLIANT" || r.testStatus === "CONFORME");
8213
8359
  return `
8214
- <tr onclick="focusNode('${r.id}')" onmouseenter="startRowTooltip(event, '${r.id}')" onmouseleave="cancelRowTooltip()" onmousemove="updateRowTooltipPos(event)" style="cursor: pointer;">
8215
- <td><strong><code>${r.id}</code></strong></td>
8216
- <td>${r.title}</td>
8217
- <td><code>${r.productTraces}</code></td>
8218
- <td><code>${r.archTraces}</code></td>
8219
- <td><code>${r.testTraces}</code></td>
8360
+ <tr onclick="focusNode('${escapeHtml(String(r.id || ""))}')" onmouseenter="startRowTooltip(event, '${escapeHtml(String(r.id || ""))}')" onmouseleave="cancelRowTooltip()" onmousemove="updateRowTooltipPos(event)" style="cursor: pointer;">
8361
+ <td><strong><code>${escapeHtml(String(r.id || ""))}</code></strong></td>
8362
+ <td>${escapeHtml(String(r.title || ""))}</td>
8363
+ <td><code>${escapeHtml(String(r.productTraces || ""))}</code></td>
8364
+ <td><code>${escapeHtml(String(r.archTraces || ""))}</code></td>
8365
+ <td><code>${escapeHtml(String(r.testTraces || ""))}</code></td>
8220
8366
  <td><span class="badge ${isRowOk ? "badge-green" : "badge-amber"}">${isRowOk ? "COMPLIANT" : "ORPHAN"}</span></td>
8221
8367
  </tr>
8222
8368
  `;
@@ -8429,7 +8575,7 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8429
8575
  </script>
8430
8576
  <script>
8431
8577
  var elements = ${elementsJson};
8432
- var govTasks = ${JSON.stringify(metricsData.gov.tasks || [])};
8578
+ var govTasks = ${safeJsonForScript(metricsData.gov.tasks || [])};
8433
8579
  var cy = null;
8434
8580
 
8435
8581
  var reqHoverTimer = null;
@@ -9062,9 +9208,10 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
9062
9208
 
9063
9209
  function switchTab(name) {
9064
9210
  hideTooltip();
9065
- document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active'); });
9211
+ document.querySelectorAll('.tab-btn').forEach(function(b) {
9212
+ b.classList.toggle('active', b.getAttribute('onclick') === "switchTab('" + name + "')");
9213
+ });
9066
9214
  document.querySelectorAll('.tab-panel').forEach(function(p) { p.classList.remove('active'); });
9067
- event.target?.classList?.add('active');
9068
9215
  var p = document.getElementById('tab-' + name);
9069
9216
  if (p) p.classList.add('active');
9070
9217
  if (name === 'graph' && cy) {