@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.js CHANGED
@@ -722,6 +722,12 @@ function walkMdFiles(dir, fileList = [], excludeDirs = [
722
722
  "node_modules",
723
723
  ".git",
724
724
  "dist",
725
+ "bin",
726
+ "obj",
727
+ "build",
728
+ "out",
729
+ "target",
730
+ "coverage",
725
731
  ".changeset",
726
732
  "scratch",
727
733
  "test-scaffold",
@@ -733,7 +739,9 @@ function walkMdFiles(dir, fileList = [], excludeDirs = [
733
739
  ".claude",
734
740
  ".cursor",
735
741
  ".github",
736
- ".vscode"
742
+ ".vscode",
743
+ ".vs",
744
+ ".idea"
737
745
  ]) {
738
746
  try {
739
747
  if (!fs6.existsSync(dir)) return fileList;
@@ -3284,10 +3292,18 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
3284
3292
  "node_modules",
3285
3293
  ".git",
3286
3294
  "dist",
3295
+ "bin",
3296
+ "obj",
3297
+ "build",
3298
+ "out",
3299
+ "target",
3300
+ "coverage",
3287
3301
  "scratch",
3288
3302
  "fixtures",
3289
3303
  "templates",
3290
- "examples"
3304
+ "examples",
3305
+ ".vs",
3306
+ ".idea"
3291
3307
  ]);
3292
3308
  function shouldSkipDir(name) {
3293
3309
  return SKIPPED_DIR_NAMES.has(name) || name.startsWith("temp-");
@@ -3390,7 +3406,12 @@ function verifyTraceability(options = {}) {
3390
3406
  }
3391
3407
  }
3392
3408
  if (relFile.includes("architecture/") || relFile.includes("06_runtime_view") || relFile.includes("08_security_concept") || relFile.includes("09_decisions")) {
3393
- architectureViews.push({ file: relFile, content });
3409
+ architectureViews.push({
3410
+ file: relFile,
3411
+ id: frontmatter.id ? String(frontmatter.id).trim() : void 0,
3412
+ type: typeof frontmatter.type === "string" ? frontmatter.type.trim() : void 0,
3413
+ content
3414
+ });
3394
3415
  }
3395
3416
  } catch {
3396
3417
  }
@@ -3493,8 +3514,9 @@ function verifyTraceability(options = {}) {
3493
3514
  }
3494
3515
  for (const view of architectureViews) {
3495
3516
  if (view.content.includes(reqId)) {
3496
- const viewName = path8.basename(view.file);
3497
- if (!archTraces.includes(viewName)) archTraces.push(viewName);
3517
+ 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-"));
3518
+ const viewIdentifier = isEntityWithId && view.id ? view.id : path8.basename(view.file);
3519
+ if (!archTraces.includes(viewIdentifier)) archTraces.push(viewIdentifier);
3498
3520
  }
3499
3521
  }
3500
3522
  const uniqueArchTraces = Array.from(new Set(archTraces));
@@ -3831,7 +3853,7 @@ function scanAllTestArtifacts(rootDir) {
3831
3853
  for (const e of entries) {
3832
3854
  const fullPath = path8.join(dir, e.name);
3833
3855
  if (e.isDirectory()) {
3834
- if (!["node_modules", ".git", "dist", ".changeset", "scratch", "fixtures", "templates", "examples"].includes(e.name)) {
3856
+ if (!["node_modules", ".git", "dist", "bin", "obj", "build", "out", "target", "coverage", ".changeset", "scratch", "fixtures", "templates", "examples", ".vs", ".idea"].includes(e.name)) {
3835
3857
  walk(fullPath);
3836
3858
  }
3837
3859
  } else {
@@ -7136,7 +7158,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7136
7158
  for (const n of pdac.drifts) {
7137
7159
  driftTargets.add(n.targetId);
7138
7160
  }
7161
+ const NON_GRAPH_TYPES = /* @__PURE__ */ new Set([
7162
+ "term",
7163
+ "handoff",
7164
+ "agent-handoff",
7165
+ "tasks",
7166
+ "task-plan",
7167
+ "spec-change-proposal",
7168
+ "proposal",
7169
+ "spec-design",
7170
+ "spec-change-design",
7171
+ "design",
7172
+ "delivery-spec",
7173
+ "spec",
7174
+ "pre-merge-audit-report",
7175
+ "user-design-feedback",
7176
+ "release-kpis",
7177
+ "session-telemetry",
7178
+ "license-manifest",
7179
+ "mcp-config",
7180
+ "user-manual",
7181
+ "production-manual",
7182
+ "architecture-glossary"
7183
+ ]);
7139
7184
  const scannedArtifactsMap = /* @__PURE__ */ new Map();
7185
+ const archViewByBasename = /* @__PURE__ */ new Map();
7140
7186
  const artifactDirs = ["product", "architecture", "specs", "security"];
7141
7187
  for (const dirName of artifactDirs) {
7142
7188
  const fullDirPath = path8.join(rootDir, dirName);
@@ -7146,6 +7192,7 @@ function buildGraphElements(rootDir = process.cwd()) {
7146
7192
  try {
7147
7193
  const fileContent = fs6.readFileSync(f, "utf-8");
7148
7194
  const relPath = path8.relative(rootDir, f).replace(/\\/g, "/");
7195
+ if (relPath.includes("/changes/") || relPath.startsWith("changes/")) continue;
7149
7196
  const match = fileContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
7150
7197
  if (!match) continue;
7151
7198
  let fm = {};
@@ -7157,6 +7204,9 @@ function buildGraphElements(rootDir = process.cwd()) {
7157
7204
  const id = typeof fm.id === "string" ? fm.id.trim() : void 0;
7158
7205
  if (!id) continue;
7159
7206
  let type = typeof fm.type === "string" ? fm.type.trim() : "";
7207
+ 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-")) {
7208
+ continue;
7209
+ }
7160
7210
  let layer = "product";
7161
7211
  if (id.startsWith("JRN-") || type === "journey") {
7162
7212
  type = "journey";
@@ -7191,16 +7241,19 @@ function buildGraphElements(rootDir = process.cwd()) {
7191
7241
  } else if (id.startsWith("SEC-REQ-") || type === "security-requirement") {
7192
7242
  type = "security-requirement";
7193
7243
  layer = "requirement";
7194
- } else if (id.startsWith("FR-") || id.startsWith("QR-") || id.startsWith("CON-") || type === "requirement") {
7244
+ } else if (id.startsWith("FR-") || id.startsWith("QR-") || id.startsWith("CON-") || type === "requirement" || type === "functional-requirement" || type === "quality-requirement") {
7195
7245
  type = "requirement";
7196
7246
  layer = "requirement";
7247
+ } else if (id.startsWith("ARCH-") || relPath.includes("architecture/")) {
7248
+ type = "architecture";
7249
+ layer = "architecture";
7197
7250
  } else {
7198
7251
  type = type || "product";
7199
7252
  layer = "product";
7200
7253
  }
7201
7254
  const title = typeof fm.title === "string" ? fm.title.trim() : id;
7202
7255
  const content = fileContent.replace(/^---\r?\n[\s\S]*?\r?\n---/, "").trim();
7203
- scannedArtifactsMap.set(id, {
7256
+ const scannedArt = {
7204
7257
  id,
7205
7258
  type,
7206
7259
  title,
@@ -7208,7 +7261,12 @@ function buildGraphElements(rootDir = process.cwd()) {
7208
7261
  filePath: relPath,
7209
7262
  content,
7210
7263
  frontmatter: fm
7211
- });
7264
+ };
7265
+ if (layer === "architecture" && type === "architecture") {
7266
+ archViewByBasename.set(path8.basename(relPath), scannedArt);
7267
+ } else {
7268
+ scannedArtifactsMap.set(id, scannedArt);
7269
+ }
7212
7270
  } catch {
7213
7271
  }
7214
7272
  }
@@ -7217,7 +7275,7 @@ function buildGraphElements(rootDir = process.cwd()) {
7217
7275
  for (const r of trace.rows) {
7218
7276
  const isOk = (r.productStatus === "COMPLIANT" || r.productStatus === "CONFORME") && (r.archStatus === "COMPLIANT" || r.archStatus === "CONFORME") && (r.testStatus === "COMPLIANT" || r.testStatus === "CONFORME");
7219
7277
  const status = isOk ? "COMPLIANT" : "ORPHAN";
7220
- const reqType = r.id.startsWith("SEC-REQ-") ? "security-requirement" : r.type || "requirement";
7278
+ const reqType = r.id.startsWith("SEC-REQ-") ? "security-requirement" : "requirement";
7221
7279
  nodeMap.set(r.id, {
7222
7280
  id: r.id,
7223
7281
  label: r.id,
@@ -7277,13 +7335,16 @@ function buildGraphElements(rootDir = process.cwd()) {
7277
7335
  });
7278
7336
  if (!nodeMap.has(a)) {
7279
7337
  const aType = a.startsWith("CMP-") ? "component" : a.startsWith("ADR-") ? "adr" : a.startsWith("SEC-ENC-") ? "security-enclave" : "architecture";
7338
+ const viewArt = archViewByBasename.get(a);
7280
7339
  nodeMap.set(a, {
7281
7340
  id: a,
7282
7341
  label: a,
7283
- title: a,
7342
+ title: viewArt?.title || a,
7284
7343
  type: aType,
7285
7344
  layer: "architecture",
7286
- status: "COMPLIANT"
7345
+ status: "COMPLIANT",
7346
+ filePath: viewArt?.filePath,
7347
+ content: viewArt?.content
7287
7348
  });
7288
7349
  }
7289
7350
  }
@@ -7415,7 +7476,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7415
7476
  status: "COMPLIANT"
7416
7477
  });
7417
7478
  }
7418
- const governedBy = Array.isArray(fm["governed-by"]) ? fm["governed-by"] : [];
7479
+ const supportingActors = Array.isArray(fm["supporting-actors"]) ? fm["supporting-actors"] : Array.isArray(fm["secondary-actors"]) ? fm["secondary-actors"] : [];
7480
+ for (const actorId of supportingActors) {
7481
+ if (typeof actorId === "string" && actorId.trim()) {
7482
+ const a = actorId.trim();
7483
+ if (!nodeMap.has(a)) {
7484
+ nodeMap.set(a, {
7485
+ id: a,
7486
+ label: a,
7487
+ title: a,
7488
+ type: "actor",
7489
+ layer: "product",
7490
+ status: "COMPLIANT"
7491
+ });
7492
+ }
7493
+ addEdge({
7494
+ id: `edge-${a}-${art.id}`,
7495
+ source: a,
7496
+ target: art.id,
7497
+ label: "initiates",
7498
+ relation: "initiates",
7499
+ status: "COMPLIANT"
7500
+ });
7501
+ }
7502
+ }
7503
+ const governedBy = Array.isArray(fm["governed-by"]) ? fm["governed-by"] : Array.isArray(fm["business-rules"]) ? fm["business-rules"] : [];
7419
7504
  for (const ruleId of governedBy) {
7420
7505
  if (typeof ruleId === "string" && ruleId.trim()) {
7421
7506
  const r = ruleId.trim();
@@ -7476,7 +7561,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7476
7561
  status: "COMPLIANT"
7477
7562
  });
7478
7563
  }
7479
- 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"] : [];
7564
+ const secondaryThreatActors = Array.isArray(fm["secondary-actors"]) ? fm["secondary-actors"] : Array.isArray(fm["supporting-actors"]) ? fm["supporting-actors"] : [];
7565
+ for (const secActor of secondaryThreatActors) {
7566
+ if (typeof secActor === "string" && secActor.trim()) {
7567
+ const sa = secActor.trim();
7568
+ if (!nodeMap.has(sa)) {
7569
+ nodeMap.set(sa, {
7570
+ id: sa,
7571
+ label: sa,
7572
+ title: sa,
7573
+ type: sa.startsWith("ACT-THREAT-") ? "threat-actor" : "actor",
7574
+ layer: "product",
7575
+ status: "COMPLIANT"
7576
+ });
7577
+ }
7578
+ addEdge({
7579
+ id: `edge-${sa}-${art.id}`,
7580
+ source: sa,
7581
+ target: art.id,
7582
+ label: "threatens",
7583
+ relation: "threatens",
7584
+ status: "COMPLIANT"
7585
+ });
7586
+ }
7587
+ }
7588
+ 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"] : [];
7480
7589
  for (const tgt of rawTargets) {
7481
7590
  if (typeof tgt === "string" && tgt.trim()) {
7482
7591
  const t = tgt.trim();
@@ -7541,6 +7650,40 @@ function buildGraphElements(rootDir = process.cwd()) {
7541
7650
  });
7542
7651
  }
7543
7652
  }
7653
+ } else if (art.type === "adr") {
7654
+ const affectedComponents = Array.isArray(fm["affects-components"]) ? fm["affects-components"] : typeof fm["affects-components"] === "string" ? [fm["affects-components"]] : [];
7655
+ for (const cmpId of affectedComponents) {
7656
+ if (typeof cmpId === "string" && cmpId.trim()) {
7657
+ const c = cmpId.trim();
7658
+ if (nodeMap.has(c)) {
7659
+ addEdge({
7660
+ id: `edge-${art.id}-${c}`,
7661
+ source: art.id,
7662
+ target: c,
7663
+ label: "governs",
7664
+ relation: "governs",
7665
+ status: "COMPLIANT"
7666
+ });
7667
+ }
7668
+ }
7669
+ }
7670
+ } else if (art.type === "security-enclave") {
7671
+ const enforcedBy = Array.isArray(fm["enforced-by"]) ? fm["enforced-by"] : typeof fm["enforced-by"] === "string" ? [fm["enforced-by"]] : [];
7672
+ for (const secReq of enforcedBy) {
7673
+ if (typeof secReq === "string" && secReq.trim()) {
7674
+ const s = secReq.trim();
7675
+ if (nodeMap.has(s)) {
7676
+ addEdge({
7677
+ id: `edge-${s}-${art.id}`,
7678
+ source: s,
7679
+ target: art.id,
7680
+ label: "satisfies",
7681
+ relation: "satisfies",
7682
+ status: "COMPLIANT"
7683
+ });
7684
+ }
7685
+ }
7686
+ }
7544
7687
  }
7545
7688
  }
7546
7689
  for (const [id, node] of nodeMap.entries()) {
@@ -7690,6 +7833,9 @@ function collectDashboardMetrics(rootDir) {
7690
7833
  function escapeHtml(str) {
7691
7834
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
7692
7835
  }
7836
+ function safeJsonForScript(data) {
7837
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
7838
+ }
7693
7839
  function renderAutonomyModeSection(mode, statusIcon, count, roleDescription, allTasks = []) {
7694
7840
  const modeTasks = (allTasks || []).filter(
7695
7841
  (t) => (t.task?.autonomyMode || "").toUpperCase() === mode
@@ -7801,14 +7947,14 @@ function renderAutonomyModeSection(mode, statusIcon, count, roleDescription, all
7801
7947
  }
7802
7948
  function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard Interactivo de Trazabilidad y Calidad") {
7803
7949
  const cytoscapeScript = getCytoscapeScript();
7804
- const elementsJson = JSON.stringify(graphData.elements);
7950
+ const elementsJson = safeJsonForScript(graphData.elements);
7805
7951
  const now = (/* @__PURE__ */ new Date()).toISOString();
7806
7952
  return `<!DOCTYPE html>
7807
7953
  <html lang="es">
7808
7954
  <head>
7809
7955
  <meta charset="UTF-8">
7810
7956
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7811
- <title>${title}</title>
7957
+ <title>${escapeHtml(title)}</title>
7812
7958
  <style>
7813
7959
  :root {
7814
7960
  --bg-dark: #0f172a;
@@ -8183,12 +8329,12 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8183
8329
  ${metricsData.trace.rows.map((r) => {
8184
8330
  const isRowOk = (r.productStatus === "COMPLIANT" || r.productStatus === "CONFORME") && (r.archStatus === "COMPLIANT" || r.archStatus === "CONFORME") && (r.testStatus === "COMPLIANT" || r.testStatus === "CONFORME");
8185
8331
  return `
8186
- <tr onclick="focusNode('${r.id}')" onmouseenter="startRowTooltip(event, '${r.id}')" onmouseleave="cancelRowTooltip()" onmousemove="updateRowTooltipPos(event)" style="cursor: pointer;">
8187
- <td><strong><code>${r.id}</code></strong></td>
8188
- <td>${r.title}</td>
8189
- <td><code>${r.productTraces}</code></td>
8190
- <td><code>${r.archTraces}</code></td>
8191
- <td><code>${r.testTraces}</code></td>
8332
+ <tr onclick="focusNode('${escapeHtml(String(r.id || ""))}')" onmouseenter="startRowTooltip(event, '${escapeHtml(String(r.id || ""))}')" onmouseleave="cancelRowTooltip()" onmousemove="updateRowTooltipPos(event)" style="cursor: pointer;">
8333
+ <td><strong><code>${escapeHtml(String(r.id || ""))}</code></strong></td>
8334
+ <td>${escapeHtml(String(r.title || ""))}</td>
8335
+ <td><code>${escapeHtml(String(r.productTraces || ""))}</code></td>
8336
+ <td><code>${escapeHtml(String(r.archTraces || ""))}</code></td>
8337
+ <td><code>${escapeHtml(String(r.testTraces || ""))}</code></td>
8192
8338
  <td><span class="badge ${isRowOk ? "badge-green" : "badge-amber"}">${isRowOk ? "COMPLIANT" : "ORPHAN"}</span></td>
8193
8339
  </tr>
8194
8340
  `;
@@ -8401,7 +8547,7 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8401
8547
  </script>
8402
8548
  <script>
8403
8549
  var elements = ${elementsJson};
8404
- var govTasks = ${JSON.stringify(metricsData.gov.tasks || [])};
8550
+ var govTasks = ${safeJsonForScript(metricsData.gov.tasks || [])};
8405
8551
  var cy = null;
8406
8552
 
8407
8553
  var reqHoverTimer = null;
@@ -9034,9 +9180,10 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
9034
9180
 
9035
9181
  function switchTab(name) {
9036
9182
  hideTooltip();
9037
- document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active'); });
9183
+ document.querySelectorAll('.tab-btn').forEach(function(b) {
9184
+ b.classList.toggle('active', b.getAttribute('onclick') === "switchTab('" + name + "')");
9185
+ });
9038
9186
  document.querySelectorAll('.tab-panel').forEach(function(p) { p.classList.remove('active'); });
9039
- event.target?.classList?.add('active');
9040
9187
  var p = document.getElementById('tab-' + name);
9041
9188
  if (p) p.classList.add('active');
9042
9189
  if (name === 'graph' && cy) {