@a-company/paradigm 3.28.0 → 3.43.0

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 (122) hide show
  1. package/dist/{accept-orchestration-6EM5EHXA.js → accept-orchestration-ZUWQUHSK.js} +6 -6
  2. package/dist/add-VSPZ6FM4.js +81 -0
  3. package/dist/{aggregate-M5WMUI6B.js → aggregate-SV3VGEIL.js} +2 -2
  4. package/dist/assess-UHBDYIK7.js +68 -0
  5. package/dist/{beacon-XL2ALH5O.js → beacon-3SJV4DAP.js} +2 -2
  6. package/dist/calibration-WWHK73WU.js +135 -0
  7. package/dist/{chunk-C5ZE6WEX.js → chunk-2SKXFXIT.js} +91 -1
  8. package/dist/{chunk-AK5M6KJB.js → chunk-36TKPM5Z.js} +20 -2
  9. package/dist/{chunk-W4VFKZVF.js → chunk-7COU5S2Z.js} +3 -3
  10. package/dist/{chunk-3BAMPB6I.js → chunk-7WEKMZ46.js} +2 -147
  11. package/dist/{chunk-SCC77UUP.js → chunk-AKIMFN6I.js} +3 -3
  12. package/dist/{chunk-3DYYXGDC.js → chunk-CDMAMDSG.js} +33 -0
  13. package/dist/{chunk-7IJ5JVKT.js → chunk-CZEIK3Y2.js} +913 -40
  14. package/dist/{chunk-MRENOFTR.js → chunk-EDOAWN7J.js} +6 -1
  15. package/dist/chunk-F3BCHPYT.js +143 -0
  16. package/dist/chunk-GT5QGC2H.js +253 -0
  17. package/dist/{chunk-N6RNYCZD.js → chunk-HIKKOCXY.js} +1 -1
  18. package/dist/{chunk-J26YQVAK.js → chunk-J4E6K5MG.js} +1 -1
  19. package/dist/chunk-L27I3CPZ.js +357 -0
  20. package/dist/{chunk-KWDTBXP2.js → chunk-LHLIAYQ3.js} +1 -1
  21. package/dist/{chunk-OXG5GVDJ.js → chunk-P7XSBJE3.js} +1 -1
  22. package/dist/{chunk-Z7W7HNRG.js → chunk-QDXI2DHR.js} +1 -1
  23. package/dist/{chunk-BRILIG7Z.js → chunk-QIOCFXDQ.js} +42 -0
  24. package/dist/{chunk-ZOH24ZPF.js → chunk-QWA26UNO.js} +7 -7
  25. package/dist/{lore-server-ILPHKWLK.js → chunk-RAB5IKPR.js} +77 -112
  26. package/dist/{chunk-BKMNLROM.js → chunk-RGFANZ4Q.js} +448 -147
  27. package/dist/{chunk-R2SGQ22F.js → chunk-YW5OCVKB.js} +448 -2
  28. package/dist/{chunk-6P4IFIK2.js → chunk-ZGUAAVMA.js} +53 -4
  29. package/dist/{commands-6ZVTD74M.js → commands-LEPFD7S5.js} +452 -1
  30. package/dist/config-schema-3YNIFJCJ.js +152 -0
  31. package/dist/{constellation-NXU6Q2HM.js → constellation-FAGT45TU.js} +2 -2
  32. package/dist/{context-audit-RI4R2WRH.js → context-audit-557EO6PK.js} +138 -8
  33. package/dist/{cost-CTGSLSOC.js → cost-UD3WPEKZ.js} +1 -1
  34. package/dist/{cursorrules-XBWFX66V.js → cursorrules-3TKZ4E4R.js} +2 -2
  35. package/dist/{delete-YTASL4SM.js → delete-RRK4RL6Y.js} +1 -1
  36. package/dist/{diff-AH7L4PRQ.js → diff-IP5CIARP.js} +6 -6
  37. package/dist/{dist-AG5JNIZU-HW2FWNTZ.js → dist-5QE2BB2B-X6DYVSUL.js} +59 -5
  38. package/dist/{dist-KY5HGDDL.js → dist-OGTSAZ55.js} +58 -4
  39. package/dist/{dist-IKBGY7FQ.js → dist-RVKYUCRU.js} +3 -1
  40. package/dist/{dist-7U64HDSC.js → dist-UXWV4OKX.js} +8 -2
  41. package/dist/{dist-RMAIFRTW.js → dist-Y7I3CFY5.js} +5 -3
  42. package/dist/{doctor-INBOLZC7.js → doctor-GKZJU7QG.js} +1 -1
  43. package/dist/{edit-S7NZD7H7.js → edit-4CLNN5JG.js} +1 -1
  44. package/dist/{graph-ERNQQQ7C.js → graph-YYUXI3F7.js} +1 -1
  45. package/dist/graph-server-ZPXRSGCW.js +116 -0
  46. package/dist/{habits-7BORPC2F.js → habits-O37HTUKE.js} +2 -2
  47. package/dist/index.js +207 -89
  48. package/dist/integrity-MK2OP5TA.js +194 -0
  49. package/dist/integrity-checker-J7YXRTBT.js +11 -0
  50. package/dist/{lint-53GPXKKI.js → lint-HYWGS3JJ.js} +1 -1
  51. package/dist/{list-QTFWN35D.js → list-BTLFHSRC.js} +1 -1
  52. package/dist/list-IUCYPGMK.js +57 -0
  53. package/dist/{lore-loader-S5BXMH27.js → lore-loader-VTEEZDX3.js} +3 -1
  54. package/dist/lore-server-NOOAHKJX.js +118 -0
  55. package/dist/mcp.js +2616 -112
  56. package/dist/migrate-FQVGQNXZ.js +889 -0
  57. package/dist/{migrate-assessments-FPR6C35Z.js → migrate-assessments-JP6Q5KME.js} +1 -1
  58. package/dist/{orchestrate-HMSQ2CED.js → orchestrate-A226N6FC.js} +6 -6
  59. package/dist/platform-server-KK4OCRTV.js +891 -0
  60. package/dist/{probe-SN4BNXOC.js → probe-7JK7IDNI.js} +5 -5
  61. package/dist/{providers-YW3FG6DA.js → providers-YNFSL6HK.js} +1 -1
  62. package/dist/quiz-I75NU2QQ.js +99 -0
  63. package/dist/{record-UGN75GTB.js → record-46CLR4OG.js} +11 -2
  64. package/dist/{reindex-YG3KIXAK.js → reindex-NZQRGKPN.js} +3 -2
  65. package/dist/{remember-IEBQHXHZ.js → remember-4EUZKIIB.js} +1 -1
  66. package/dist/{retag-URLJLMSK.js → retag-KC4JVRLE.js} +1 -1
  67. package/dist/{review-725ZKA7U.js → review-Q7M4CRB5.js} +1 -1
  68. package/dist/{ripple-DFMXLFWI.js → ripple-RI3LOT6R.js} +2 -2
  69. package/dist/{sentinel-FUR3QKCJ.js → sentinel-BKYTBT7M.js} +1 -1
  70. package/dist/sentinel-bridge-IZTXYS5M.js +109 -0
  71. package/dist/sentinel-ui/assets/{index-Zh1YM0C9.css → index-CJ1Wx083.css} +1 -1
  72. package/dist/sentinel-ui/assets/index-S1VJ67dT.js +62 -0
  73. package/dist/sentinel-ui/assets/index-S1VJ67dT.js.map +1 -0
  74. package/dist/sentinel-ui/index.html +2 -2
  75. package/dist/sentinel.js +6 -6
  76. package/dist/{serve-DIALBCTU.js → serve-22A4XOIG.js} +1 -1
  77. package/dist/{university-A66BMZ4Z.js → serve-2YJ6D2Y6.js} +9 -8
  78. package/dist/serve-3V2WXLGM.js +33 -0
  79. package/dist/{server-2VICPDUR.js → server-OFEJ2HJP.js} +25 -2
  80. package/dist/{server-OWBK2WFS.js → server-RDLQ3DK7.js} +49 -4
  81. package/dist/{setup-HOI52TN3.js → setup-M2ZKLKNN.js} +4 -4
  82. package/dist/{shift-DRF5M3G6.js → shift-LNMKFYLR.js} +73 -14
  83. package/dist/{show-GEVVQWWG.js → show-P7GYO43X.js} +1 -1
  84. package/dist/show-PKZMYKRN.js +82 -0
  85. package/dist/{snapshot-XHINQBZS.js → snapshot-Y3COXK4T.js} +2 -2
  86. package/dist/{spawn-DIY7T4QW.js → spawn-SSXZX45U.js} +2 -2
  87. package/dist/status-KLHALGW4.js +71 -0
  88. package/dist/{summary-NV7SBV5O.js → summary-5NQNOD3F.js} +2 -2
  89. package/dist/{sweep-5POCF2E4.js → sweep-EZU3GU6S.js} +1 -1
  90. package/dist/symphony-ROEKK7VD.js +999 -0
  91. package/dist/{team-YOGT2Q2X.js → team-HGLJXWQG.js} +7 -7
  92. package/dist/{timeline-RKXNRMKF.js → timeline-ANC7LVDL.js} +1 -1
  93. package/dist/{triage-GJ6GK647.js → triage-POXJ2TIX.js} +2 -2
  94. package/dist/university-content/courses/.purpose +7 -1
  95. package/dist/university-content/courses/para-101.json +53 -0
  96. package/dist/university-content/courses/para-501.json +166 -0
  97. package/dist/university-content/plsat/.purpose +6 -0
  98. package/dist/university-content/plsat/v3.0.json +400 -1
  99. package/dist/university-content/reference.json +48 -0
  100. package/dist/university-ui/assets/{index-TcsCEBMo.js → index-tfi5xN4Q.js} +2 -2
  101. package/dist/university-ui/assets/{index-TcsCEBMo.js.map → index-tfi5xN4Q.js.map} +1 -1
  102. package/dist/university-ui/index.html +1 -1
  103. package/dist/{upgrade-65QOQXRC.js → upgrade-ANX3LVSA.js} +1 -0
  104. package/dist/validate-GD5XWILV.js +134 -0
  105. package/dist/{validate-TKKRGJKC.js → validate-ZVPNN4FL.js} +1 -1
  106. package/dist/{workspace-L27RR5MF.js → workspace-UIUTHZTD.js} +6 -6
  107. package/package.json +4 -2
  108. package/platform-ui/dist/assets/GitSection-C-GQWHcu.css +1 -0
  109. package/platform-ui/dist/assets/GitSection-DvyJBF_-.js +4 -0
  110. package/platform-ui/dist/assets/GraphSection-BiQrXqfs.js +8 -0
  111. package/platform-ui/dist/assets/GraphSection-BlgXTl53.css +1 -0
  112. package/platform-ui/dist/assets/LoreSection-BaH1FaRb.js +1 -0
  113. package/platform-ui/dist/assets/LoreSection-C3EixkjW.css +1 -0
  114. package/platform-ui/dist/assets/SentinelSection-BI-aIYKL.css +1 -0
  115. package/platform-ui/dist/assets/SentinelSection-DemAznjI.js +1 -0
  116. package/platform-ui/dist/assets/index-CfpZFjea.css +1 -0
  117. package/platform-ui/dist/assets/index-DDKhCt-w.js +57 -0
  118. package/platform-ui/dist/index.html +14 -0
  119. package/dist/graph-server-BZ73HTAT.js +0 -251
  120. package/dist/sentinel-ui/assets/index-C_Wstm64.js +0 -62
  121. package/dist/sentinel-ui/assets/index-C_Wstm64.js.map +0 -1
  122. /package/dist/{chunk-5SXMV4SP.js → chunk-FS3WTUHY.js} +0 -0
@@ -2,12 +2,17 @@
2
2
  import {
3
3
  loadLoreEntries,
4
4
  loadLoreEntry
5
- } from "./chunk-3DYYXGDC.js";
5
+ } from "./chunk-CDMAMDSG.js";
6
+ import {
7
+ checkComponentAnchors,
8
+ checkIntegrity,
9
+ checkPurposeHealth
10
+ } from "./chunk-L27I3CPZ.js";
6
11
 
7
12
  // ../paradigm-mcp/src/tools/reindex.ts
8
- import * as fs8 from "fs";
9
- import * as path9 from "path";
10
- import * as yaml7 from "js-yaml";
13
+ import * as fs9 from "fs";
14
+ import * as path10 from "path";
15
+ import * as yaml8 from "js-yaml";
11
16
 
12
17
  // ../premise/core/dist/index.js
13
18
  import * as yaml3 from "js-yaml";
@@ -22,6 +27,12 @@ import * as path from "path";
22
27
  import { glob } from "glob";
23
28
  var PurposeItemSchema = z.object({
24
29
  description: z.string(),
30
+ // Component type and hierarchy (v4)
31
+ type: z.string().optional(),
32
+ parent: z.string().optional(),
33
+ anchors: z.array(z.string()).optional(),
34
+ tags: z.array(z.string()).optional(),
35
+ // Standard fields
25
36
  endpoints: z.array(z.string()).optional(),
26
37
  tests: z.array(z.string()).optional(),
27
38
  rules: z.record(z.unknown()).optional(),
@@ -33,7 +44,6 @@ var PurposeItemSchema = z.object({
33
44
  states: z.array(z.string()).optional(),
34
45
  components: z.array(z.string()).optional(),
35
46
  // Extra fields preserved
36
- tags: z.array(z.string()).optional(),
37
47
  location: z.string().optional(),
38
48
  locations: z.array(z.string()).optional(),
39
49
  uses: z.array(z.string()).optional(),
@@ -620,6 +630,138 @@ function validatePurposeItem(id, item, itemType, prefix, issues) {
620
630
  }
621
631
  }
622
632
  }
633
+ function validateCrossFile(allFiles) {
634
+ const issues = [];
635
+ const definedSymbols = /* @__PURE__ */ new Set();
636
+ for (const { data } of allFiles) {
637
+ const components = normalizeToEntries(data.components);
638
+ for (const [id] of components) {
639
+ definedSymbols.add(`#${id}`);
640
+ definedSymbols.add(id);
641
+ }
642
+ const features = normalizeToEntries(data.features);
643
+ for (const [id] of features) {
644
+ definedSymbols.add(`#${id}`);
645
+ definedSymbols.add(id);
646
+ }
647
+ if (data.gates) {
648
+ for (const id of Object.keys(data.gates)) {
649
+ definedSymbols.add(`^${id}`);
650
+ definedSymbols.add(id);
651
+ }
652
+ }
653
+ if (data.signals) {
654
+ for (const id of Object.keys(data.signals)) {
655
+ definedSymbols.add(`!${id}`);
656
+ definedSymbols.add(id);
657
+ }
658
+ }
659
+ if (data.flows) {
660
+ if (Array.isArray(data.flows)) {
661
+ for (const flow of data.flows) {
662
+ if (flow?.name) {
663
+ definedSymbols.add(`$${flow.name}`);
664
+ definedSymbols.add(flow.name);
665
+ }
666
+ }
667
+ } else {
668
+ for (const id of Object.keys(data.flows)) {
669
+ definedSymbols.add(`$${id}`);
670
+ definedSymbols.add(id);
671
+ }
672
+ }
673
+ }
674
+ if (data.aspects) {
675
+ for (const id of Object.keys(data.aspects)) {
676
+ definedSymbols.add(`~${id}`);
677
+ definedSymbols.add(id);
678
+ }
679
+ }
680
+ if (data.states) {
681
+ for (const id of Object.keys(data.states)) {
682
+ definedSymbols.add(`#${id}`);
683
+ definedSymbols.add(id);
684
+ }
685
+ }
686
+ }
687
+ for (const { filePath, data } of allFiles) {
688
+ const prefix = filePath ? `${filePath}: ` : "";
689
+ const allEntries = [
690
+ ...normalizeToEntries(data.components),
691
+ ...normalizeToEntries(data.features)
692
+ ];
693
+ for (const [id, item] of allEntries) {
694
+ if (item.parent) {
695
+ const parentRef = item.parent.replace(/^["']|["']$/g, "");
696
+ const bareRef = parentRef.replace(/^[#$^!~@%?&]/, "");
697
+ if (!definedSymbols.has(parentRef) && !definedSymbols.has(bareRef)) {
698
+ issues.push({
699
+ type: "warning",
700
+ message: `${prefix}Component "${id}" references parent "${parentRef}" which is not defined in any .purpose file`,
701
+ path: `components.${id}.parent`
702
+ });
703
+ }
704
+ }
705
+ const refLists = [
706
+ { field: "gates", refs: item.gates },
707
+ { field: "signals", refs: item.signals },
708
+ { field: "flows", refs: item.flows },
709
+ { field: "components", refs: item.components },
710
+ { field: "aspects", refs: item.aspects }
711
+ ];
712
+ for (const { field, refs } of refLists) {
713
+ if (!refs) continue;
714
+ for (const ref of refs) {
715
+ const bareRef = ref.replace(/^[#$^!~@%?&]/, "");
716
+ if (!definedSymbols.has(ref) && !definedSymbols.has(bareRef)) {
717
+ issues.push({
718
+ type: "warning",
719
+ message: `${prefix}Symbol "${id}" references ${field} "${ref}" which is not defined`,
720
+ path: `components.${id}.${field}`
721
+ });
722
+ }
723
+ }
724
+ }
725
+ }
726
+ if (data.flows) {
727
+ if (Array.isArray(data.flows)) {
728
+ for (const flow of data.flows) {
729
+ if (!flow?.steps) continue;
730
+ for (const step of flow.steps) {
731
+ if (typeof step === "string" || !step?.component) continue;
732
+ const bareRef = step.component.replace(/^#/, "");
733
+ if (!definedSymbols.has(step.component) && !definedSymbols.has(bareRef)) {
734
+ issues.push({
735
+ type: "warning",
736
+ message: `${prefix}Flow "${flow.name}" step references "${step.component}" which is not defined`,
737
+ path: `flows.${flow.name}.steps`
738
+ });
739
+ }
740
+ }
741
+ }
742
+ } else {
743
+ for (const [flowId, flowDef] of Object.entries(data.flows)) {
744
+ if (!flowDef?.steps) continue;
745
+ for (const step of flowDef.steps) {
746
+ if (typeof step === "string" || !step?.component) continue;
747
+ const bareRef = step.component.replace(/^#/, "");
748
+ if (!definedSymbols.has(step.component) && !definedSymbols.has(bareRef)) {
749
+ issues.push({
750
+ type: "warning",
751
+ message: `${prefix}Flow "${flowId}" step references "${step.component}" which is not defined`,
752
+ path: `flows.${flowId}.steps`
753
+ });
754
+ }
755
+ }
756
+ }
757
+ }
758
+ }
759
+ }
760
+ return {
761
+ valid: issues.filter((i) => i.type === "error").length === 0,
762
+ issues
763
+ };
764
+ }
623
765
 
624
766
  // ../portal/core/dist/index.js
625
767
  import * as fs2 from "fs";
@@ -847,6 +989,7 @@ async function aggregateFromPremise(premiseFile, rootDir) {
847
989
  purposeFiles.push(...parsed.map((p) => p.filePath));
848
990
  const features = extractFeatures(parsed);
849
991
  for (const [id, { item, filePath }] of features) {
992
+ const featureTags = ["feature", ...item.tags || []];
850
993
  symbols.push(createSymbolEntry({
851
994
  id: `purpose-feature-${id}`,
852
995
  symbol: `#${id}`,
@@ -855,7 +998,10 @@ async function aggregateFromPremise(premiseFile, rootDir) {
855
998
  filePath,
856
999
  data: item,
857
1000
  description: item.description,
858
- tags: ["feature"]
1001
+ tags: featureTags,
1002
+ componentType: item.type,
1003
+ parentSymbol: item.parent,
1004
+ anchors: item.anchors?.map((a) => parseAnchorString(a))
859
1005
  }));
860
1006
  }
861
1007
  const components = extractComponents(parsed);
@@ -867,7 +1013,11 @@ async function aggregateFromPremise(premiseFile, rootDir) {
867
1013
  source: "purpose",
868
1014
  filePath,
869
1015
  data: item,
870
- description: item.description
1016
+ description: item.description,
1017
+ tags: item.tags,
1018
+ componentType: item.type,
1019
+ parentSymbol: item.parent,
1020
+ anchors: item.anchors?.map((a) => parseAnchorString(a))
871
1021
  }));
872
1022
  }
873
1023
  const gates = extractGates(parsed);
@@ -1033,12 +1183,27 @@ async function aggregateFromPremise(premiseFile, rootDir) {
1033
1183
  }));
1034
1184
  }
1035
1185
  resolveReferences(symbols);
1186
+ const symbolFileMap = /* @__PURE__ */ new Map();
1187
+ for (const sym of symbols) {
1188
+ const files = symbolFileMap.get(sym.symbol) || [];
1189
+ if (!files.includes(sym.filePath)) {
1190
+ files.push(sym.filePath);
1191
+ }
1192
+ symbolFileMap.set(sym.symbol, files);
1193
+ }
1194
+ const duplicateSymbols = [];
1195
+ for (const [symbol, files] of symbolFileMap) {
1196
+ if (files.length > 1) {
1197
+ duplicateSymbols.push({ symbol, files });
1198
+ }
1199
+ }
1036
1200
  return {
1037
1201
  symbols,
1038
1202
  purposeFiles,
1039
1203
  portalFiles,
1040
1204
  errors,
1041
- timestamp: Date.now()
1205
+ timestamp: Date.now(),
1206
+ ...duplicateSymbols.length > 0 ? { duplicateSymbols } : {}
1042
1207
  };
1043
1208
  }
1044
1209
  function createSymbolEntry(partial) {
@@ -1191,6 +1356,11 @@ function searchSymbols(index, query) {
1191
1356
  }
1192
1357
  if (entry.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery))) {
1193
1358
  results.push(entry);
1359
+ continue;
1360
+ }
1361
+ if (entry.componentType?.toLowerCase().includes(lowerQuery)) {
1362
+ results.push(entry);
1363
+ continue;
1194
1364
  }
1195
1365
  }
1196
1366
  return results;
@@ -1258,6 +1428,20 @@ function generateScanIndex(input, options) {
1258
1428
  }
1259
1429
  }
1260
1430
  }
1431
+ const typeCounts = {};
1432
+ for (const comp of Object.values(index.components)) {
1433
+ if (comp.componentType) {
1434
+ typeCounts[comp.componentType] = (typeCounts[comp.componentType] || 0) + 1;
1435
+ }
1436
+ }
1437
+ for (const feat of Object.values(index.features)) {
1438
+ if (feat.componentType) {
1439
+ typeCounts[feat.componentType] = (typeCounts[feat.componentType] || 0) + 1;
1440
+ }
1441
+ }
1442
+ if (Object.keys(typeCounts).length > 0) {
1443
+ index.$meta.componentTypes = typeCounts;
1444
+ }
1261
1445
  buildScreenReferences(index);
1262
1446
  return index;
1263
1447
  }
@@ -1313,7 +1497,9 @@ function addComponent(symbol, index, options) {
1313
1497
  path: symbol.filePath,
1314
1498
  description: symbol.description,
1315
1499
  visualTags,
1316
- related: symbol.references
1500
+ related: symbol.references,
1501
+ componentType: symbol.componentType,
1502
+ parent: symbol.parentSymbol
1317
1503
  };
1318
1504
  index.components[id] = element;
1319
1505
  index.symbolMap[symbol.symbol] = { category: "components", id };
@@ -1329,7 +1515,9 @@ function addFeature(symbol, index, options) {
1329
1515
  path: symbol.filePath,
1330
1516
  description: symbol.description,
1331
1517
  visualTags,
1332
- related: symbol.references
1518
+ related: symbol.references,
1519
+ componentType: symbol.componentType,
1520
+ parent: symbol.parentSymbol
1333
1521
  };
1334
1522
  index.features[id] = element;
1335
1523
  index.symbolMap[symbol.symbol] = { category: "features", id };
@@ -1997,8 +2185,8 @@ var SessionTracker = class {
1997
2185
  * Extract resource type from URI
1998
2186
  */
1999
2187
  extractResourceType(uri) {
2000
- const path10 = uri.replace("paradigm://", "");
2001
- const firstPart = path10.split("/")[0];
2188
+ const path11 = uri.replace("paradigm://", "");
2189
+ const firstPart = path11.split("/")[0];
2002
2190
  return firstPart || "unknown";
2003
2191
  }
2004
2192
  /**
@@ -2752,7 +2940,7 @@ async function buildRecoveryPreamble(rootDir) {
2752
2940
  } catch {
2753
2941
  }
2754
2942
  try {
2755
- const { loadLoreEntries: loadLoreEntries2 } = await import("./lore-loader-S5BXMH27.js");
2943
+ const { loadLoreEntries: loadLoreEntries2 } = await import("./lore-loader-VTEEZDX3.js");
2756
2944
  const arcEntries = await loadLoreEntries2(rootDir, { limit: 10 });
2757
2945
  const entriesWithArcs = arcEntries.filter((e) => e.tags?.some((t) => t.startsWith("arc:")));
2758
2946
  if (entriesWithArcs.length > 0) {
@@ -4136,8 +4324,8 @@ async function getAffectedPersonas(rootDir, symbol) {
4136
4324
  }
4137
4325
  return results;
4138
4326
  }
4139
- function deepGet(obj, path10) {
4140
- const parts = path10.split(/[.\[\]]+/).filter(Boolean);
4327
+ function deepGet(obj, path11) {
4328
+ const parts = path11.split(/[.\[\]]+/).filter(Boolean);
4141
4329
  let current = obj;
4142
4330
  for (const part of parts) {
4143
4331
  if (current == null || typeof current !== "object") return void 0;
@@ -4217,7 +4405,7 @@ function assertStep(step, event) {
4217
4405
  async function validateAgainstSentinel(persona, options = {}) {
4218
4406
  const steps = [];
4219
4407
  try {
4220
- const { SentinelStorage } = await import("./dist-IKBGY7FQ.js");
4408
+ const { SentinelStorage } = await import("./dist-RVKYUCRU.js");
4221
4409
  const storage = new SentinelStorage();
4222
4410
  const events = storage.queryEvents?.({
4223
4411
  schemaId: "paradigm-personas",
@@ -4640,6 +4828,604 @@ function slugify(name) {
4640
4828
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
4641
4829
  }
4642
4830
 
4831
+ // ../paradigm-mcp/src/utils/university-loader.ts
4832
+ import * as fs8 from "fs";
4833
+ import * as path9 from "path";
4834
+ import * as yaml7 from "js-yaml";
4835
+ var UNIVERSITY_DIR = ".paradigm/university";
4836
+ var CONTENT_DIR = "content";
4837
+ var NOTES_DIR = "notes";
4838
+ var POLICIES_DIR = "policies";
4839
+ var QUIZZES_DIR = "quizzes";
4840
+ var PATHS_DIR = "paths";
4841
+ var DIPLOMAS_DIR = "diplomas";
4842
+ var INDEX_FILE3 = "index.yaml";
4843
+ var CONFIG_FILE = "config.yaml";
4844
+ var DEFAULT_BRANDING = {
4845
+ name: "Project University",
4846
+ tagline: "Learn the codebase",
4847
+ institution: "Paradigm"
4848
+ };
4849
+ var DEFAULT_THEME = {
4850
+ primary: "#6366f1",
4851
+ secondary: "#8b5cf6",
4852
+ accent: "#f59e0b",
4853
+ background: "#0f172a",
4854
+ surface: "#1e293b",
4855
+ text: "#f8fafc",
4856
+ textMuted: "#94a3b8",
4857
+ success: "#22c55e",
4858
+ error: "#ef4444",
4859
+ font: "Inter, system-ui, sans-serif"
4860
+ };
4861
+ var DEFAULT_CONFIG = {
4862
+ branding: DEFAULT_BRANDING,
4863
+ theme: DEFAULT_THEME,
4864
+ content: {
4865
+ categories: [],
4866
+ defaultDifficulty: "beginner",
4867
+ requireApproval: false
4868
+ },
4869
+ diplomas: {
4870
+ includeGlobalPLSAT: true,
4871
+ customCertStyle: null
4872
+ }
4873
+ };
4874
+ function loadUniversityConfig(rootDir) {
4875
+ const configPath = path9.join(rootDir, UNIVERSITY_DIR, CONFIG_FILE);
4876
+ if (!fs8.existsSync(configPath)) {
4877
+ return { ...DEFAULT_CONFIG };
4878
+ }
4879
+ try {
4880
+ const raw = fs8.readFileSync(configPath, "utf8");
4881
+ const data = yaml7.load(raw);
4882
+ if (!data) return { ...DEFAULT_CONFIG };
4883
+ return {
4884
+ branding: { ...DEFAULT_BRANDING, ...data.branding || {} },
4885
+ theme: { ...DEFAULT_THEME, ...data.theme || {} },
4886
+ content: {
4887
+ categories: data.content?.categories || [],
4888
+ defaultDifficulty: data.content?.defaultDifficulty || "beginner",
4889
+ requireApproval: data.content?.requireApproval ?? false
4890
+ },
4891
+ diplomas: {
4892
+ includeGlobalPLSAT: data.diplomas?.includeGlobalPLSAT ?? true,
4893
+ customCertStyle: data.diplomas?.customCertStyle ?? null
4894
+ }
4895
+ };
4896
+ } catch {
4897
+ return { ...DEFAULT_CONFIG };
4898
+ }
4899
+ }
4900
+ function loadUniversityIndex(rootDir) {
4901
+ const indexPath = path9.join(rootDir, UNIVERSITY_DIR, INDEX_FILE3);
4902
+ if (!fs8.existsSync(indexPath)) return null;
4903
+ try {
4904
+ const raw = fs8.readFileSync(indexPath, "utf8");
4905
+ return yaml7.load(raw);
4906
+ } catch {
4907
+ return null;
4908
+ }
4909
+ }
4910
+ function parseFrontmatter(content) {
4911
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
4912
+ if (!match) return null;
4913
+ try {
4914
+ const frontmatter = yaml7.load(match[1]);
4915
+ return { frontmatter, body: match[2].trim() };
4916
+ } catch {
4917
+ return null;
4918
+ }
4919
+ }
4920
+ function serializeFrontmatter(frontmatter, body) {
4921
+ const fm = yaml7.dump(frontmatter, { lineWidth: -1, noRefs: true, sortKeys: false });
4922
+ return `---
4923
+ ${fm}---
4924
+
4925
+ ${body}
4926
+ `;
4927
+ }
4928
+ function loadNote(rootDir, id) {
4929
+ const filePath = resolveContentFile(rootDir, id, ".md");
4930
+ if (!filePath) return null;
4931
+ try {
4932
+ const raw = fs8.readFileSync(filePath, "utf8");
4933
+ const parsed = parseFrontmatter(raw);
4934
+ if (!parsed) return null;
4935
+ const fm = parsed.frontmatter;
4936
+ return { frontmatter: normalizeFrontmatter(fm), body: parsed.body };
4937
+ } catch {
4938
+ return null;
4939
+ }
4940
+ }
4941
+ function saveNote(rootDir, frontmatter, body) {
4942
+ const subdir = frontmatter.type === "policy" ? POLICIES_DIR : NOTES_DIR;
4943
+ const dir = path9.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, subdir);
4944
+ fs8.mkdirSync(dir, { recursive: true });
4945
+ const filePath = path9.join(dir, `${frontmatter.id}.md`);
4946
+ const content = serializeFrontmatter(frontmatter, body);
4947
+ fs8.writeFileSync(filePath, content, "utf8");
4948
+ return filePath;
4949
+ }
4950
+ function loadQuiz(rootDir, id) {
4951
+ const filePath = resolveContentFile(rootDir, id, ".yaml");
4952
+ if (!filePath) return null;
4953
+ try {
4954
+ const raw = fs8.readFileSync(filePath, "utf8");
4955
+ const data = yaml7.load(raw);
4956
+ if (!data || !data.id) return null;
4957
+ return normalizeQuiz(data);
4958
+ } catch {
4959
+ return null;
4960
+ }
4961
+ }
4962
+ function saveQuiz(rootDir, quiz) {
4963
+ const dir = path9.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, QUIZZES_DIR);
4964
+ fs8.mkdirSync(dir, { recursive: true });
4965
+ const filePath = path9.join(dir, `${quiz.id}.yaml`);
4966
+ fs8.writeFileSync(filePath, yaml7.dump(quiz, { lineWidth: -1, noRefs: true }), "utf8");
4967
+ return filePath;
4968
+ }
4969
+ function loadPath(rootDir, id) {
4970
+ const filePath = resolveContentFile(rootDir, id, ".yaml");
4971
+ if (!filePath) return null;
4972
+ try {
4973
+ const raw = fs8.readFileSync(filePath, "utf8");
4974
+ const data = yaml7.load(raw);
4975
+ if (!data || !data.id) return null;
4976
+ return data;
4977
+ } catch {
4978
+ return null;
4979
+ }
4980
+ }
4981
+ function savePath(rootDir, lp) {
4982
+ const dir = path9.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, PATHS_DIR);
4983
+ fs8.mkdirSync(dir, { recursive: true });
4984
+ const filePath = path9.join(dir, `${lp.id}.yaml`);
4985
+ fs8.writeFileSync(filePath, yaml7.dump(lp, { lineWidth: -1, noRefs: true }), "utf8");
4986
+ return filePath;
4987
+ }
4988
+ function loadDiplomas(rootDir, filter) {
4989
+ const dir = path9.join(rootDir, UNIVERSITY_DIR, DIPLOMAS_DIR);
4990
+ if (!fs8.existsSync(dir)) return [];
4991
+ const results = [];
4992
+ try {
4993
+ const files = fs8.readdirSync(dir).filter((f) => f.endsWith(".yaml"));
4994
+ for (const file of files) {
4995
+ try {
4996
+ const raw = fs8.readFileSync(path9.join(dir, file), "utf8");
4997
+ const diploma = yaml7.load(raw);
4998
+ if (!diploma || !diploma.id) continue;
4999
+ if (filter?.student && diploma.student !== filter.student) continue;
5000
+ if (filter?.type && diploma.type !== filter.type) continue;
5001
+ results.push(diploma);
5002
+ } catch {
5003
+ }
5004
+ }
5005
+ } catch {
5006
+ }
5007
+ return results.sort((a, b) => b.earnedAt.localeCompare(a.earnedAt));
5008
+ }
5009
+ function saveDiploma(rootDir, diploma) {
5010
+ const dir = path9.join(rootDir, UNIVERSITY_DIR, DIPLOMAS_DIR);
5011
+ fs8.mkdirSync(dir, { recursive: true });
5012
+ const filePath = path9.join(dir, `${diploma.id}.yaml`);
5013
+ fs8.writeFileSync(filePath, yaml7.dump(diploma, { lineWidth: -1, noRefs: true }), "utf8");
5014
+ return filePath;
5015
+ }
5016
+ function searchContent(rootDir, filter) {
5017
+ const index = loadUniversityIndex(rootDir);
5018
+ if (!index) return [];
5019
+ let results = [...index.entries];
5020
+ if (filter.type) {
5021
+ results = results.filter((e) => e.type === filter.type);
5022
+ }
5023
+ if (filter.tag) {
5024
+ results = results.filter((e) => e.tags.some((t) => t.startsWith(filter.tag)));
5025
+ }
5026
+ if (filter.difficulty) {
5027
+ results = results.filter((e) => e.difficulty === filter.difficulty);
5028
+ }
5029
+ if (filter.symbol) {
5030
+ results = results.filter((e) => e.symbols.some((s) => s === filter.symbol));
5031
+ }
5032
+ if (filter.author) {
5033
+ results = results.filter((e) => e.author === filter.author);
5034
+ }
5035
+ if (filter.query) {
5036
+ const q = filter.query.toLowerCase();
5037
+ results = results.filter(
5038
+ (e) => e.title.toLowerCase().includes(q) || e.id.toLowerCase().includes(q) || e.tags.some((t) => t.toLowerCase().includes(q))
5039
+ );
5040
+ }
5041
+ const limit = filter.limit || 20;
5042
+ return results.slice(0, limit);
5043
+ }
5044
+ function rebuildUniversityIndex(rootDir) {
5045
+ const uniDir = path9.join(rootDir, UNIVERSITY_DIR);
5046
+ const contentDir = path9.join(uniDir, CONTENT_DIR);
5047
+ const entries = [];
5048
+ for (const subdir of [NOTES_DIR, POLICIES_DIR]) {
5049
+ const dir = path9.join(contentDir, subdir);
5050
+ if (!fs8.existsSync(dir)) continue;
5051
+ try {
5052
+ for (const file of fs8.readdirSync(dir).filter((f) => f.endsWith(".md"))) {
5053
+ try {
5054
+ const raw = fs8.readFileSync(path9.join(dir, file), "utf8");
5055
+ const parsed = parseFrontmatter(raw);
5056
+ if (!parsed) continue;
5057
+ const fm = parsed.frontmatter;
5058
+ entries.push({
5059
+ id: fm.id || file.replace(".md", ""),
5060
+ title: fm.title || file,
5061
+ type: fm.type || (subdir === POLICIES_DIR ? "policy" : "note"),
5062
+ author: fm.author || "unknown",
5063
+ created: fm.created || "",
5064
+ updated: fm.updated || "",
5065
+ tags: Array.isArray(fm.tags) ? fm.tags : [],
5066
+ symbols: Array.isArray(fm.symbols) ? fm.symbols : [],
5067
+ difficulty: fm.difficulty || "beginner",
5068
+ file: `${CONTENT_DIR}/${subdir}/${file}`
5069
+ });
5070
+ } catch {
5071
+ }
5072
+ }
5073
+ } catch {
5074
+ }
5075
+ }
5076
+ const quizDir = path9.join(contentDir, QUIZZES_DIR);
5077
+ if (fs8.existsSync(quizDir)) {
5078
+ try {
5079
+ for (const file of fs8.readdirSync(quizDir).filter((f) => f.endsWith(".yaml"))) {
5080
+ try {
5081
+ const raw = fs8.readFileSync(path9.join(quizDir, file), "utf8");
5082
+ const quiz = yaml7.load(raw);
5083
+ if (!quiz || !quiz.id) continue;
5084
+ entries.push({
5085
+ id: quiz.id,
5086
+ title: quiz.title || file,
5087
+ type: "quiz",
5088
+ author: quiz.author || "unknown",
5089
+ created: quiz.created || "",
5090
+ updated: quiz.updated || "",
5091
+ tags: quiz.tags || [],
5092
+ symbols: quiz.symbols || [],
5093
+ difficulty: quiz.difficulty || "beginner",
5094
+ file: `${CONTENT_DIR}/${QUIZZES_DIR}/${file}`
5095
+ });
5096
+ } catch {
5097
+ }
5098
+ }
5099
+ } catch {
5100
+ }
5101
+ }
5102
+ const pathDir = path9.join(contentDir, PATHS_DIR);
5103
+ if (fs8.existsSync(pathDir)) {
5104
+ try {
5105
+ for (const file of fs8.readdirSync(pathDir).filter((f) => f.endsWith(".yaml"))) {
5106
+ try {
5107
+ const raw = fs8.readFileSync(path9.join(pathDir, file), "utf8");
5108
+ const lp = yaml7.load(raw);
5109
+ if (!lp || !lp.id) continue;
5110
+ entries.push({
5111
+ id: lp.id,
5112
+ title: lp.title || file,
5113
+ type: "path",
5114
+ author: lp.author || "unknown",
5115
+ created: lp.created || "",
5116
+ updated: lp.updated || "",
5117
+ tags: lp.tags || [],
5118
+ symbols: [],
5119
+ file: `${CONTENT_DIR}/${PATHS_DIR}/${file}`
5120
+ });
5121
+ } catch {
5122
+ }
5123
+ }
5124
+ } catch {
5125
+ }
5126
+ }
5127
+ let diplomaCount = 0;
5128
+ const diplomaDir = path9.join(uniDir, DIPLOMAS_DIR);
5129
+ if (fs8.existsSync(diplomaDir)) {
5130
+ try {
5131
+ diplomaCount = fs8.readdirSync(diplomaDir).filter((f) => f.endsWith(".yaml")).length;
5132
+ } catch {
5133
+ }
5134
+ }
5135
+ const index = {
5136
+ version: "1.0",
5137
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5138
+ totalContent: entries.length,
5139
+ entries,
5140
+ diplomaCount
5141
+ };
5142
+ fs8.mkdirSync(uniDir, { recursive: true });
5143
+ const indexPath = path9.join(uniDir, INDEX_FILE3);
5144
+ fs8.writeFileSync(indexPath, yaml7.dump(index, { lineWidth: -1, noRefs: true }), "utf8");
5145
+ return index;
5146
+ }
5147
+ function validateUniversityContent(rootDir, options) {
5148
+ const index = loadUniversityIndex(rootDir) || rebuildUniversityIndex(rootDir);
5149
+ const issues = [];
5150
+ let entriesToCheck = index.entries;
5151
+ if (options?.id) {
5152
+ entriesToCheck = entriesToCheck.filter((e) => e.id === options.id);
5153
+ }
5154
+ let knownSymbols = null;
5155
+ if (options?.deep) {
5156
+ knownSymbols = loadKnownSymbols(rootDir);
5157
+ }
5158
+ const allContentIds = new Set(index.entries.map((e) => e.id));
5159
+ for (const entry of entriesToCheck) {
5160
+ if (!entry.title) {
5161
+ issues.push({
5162
+ contentId: entry.id,
5163
+ severity: "error",
5164
+ check: "missing-title",
5165
+ message: "Content is missing a title",
5166
+ fix: "Add a title field to the content frontmatter"
5167
+ });
5168
+ }
5169
+ if (entry.type === "quiz") {
5170
+ validateQuizContent(rootDir, entry.id, issues);
5171
+ }
5172
+ if (entry.type === "path") {
5173
+ validatePathContent(rootDir, entry.id, allContentIds, issues);
5174
+ }
5175
+ if (knownSymbols && entry.symbols.length > 0) {
5176
+ for (const sym of entry.symbols) {
5177
+ if (!knownSymbols.has(sym)) {
5178
+ issues.push({
5179
+ contentId: entry.id,
5180
+ severity: "warning",
5181
+ check: "broken-symbol-ref",
5182
+ message: `Symbol "${sym}" not found in scan-index`,
5183
+ fix: `Remove or update the symbol reference in ${entry.id}`
5184
+ });
5185
+ }
5186
+ }
5187
+ }
5188
+ if (options?.deep && entry.symbols.length > 0 && entry.updated) {
5189
+ checkContentStaleness(rootDir, entry, issues);
5190
+ }
5191
+ }
5192
+ const diplomas = loadDiplomas(rootDir);
5193
+ for (const diploma of diplomas) {
5194
+ if (diploma.total > 0 && diploma.percentage !== Math.round(diploma.score / diploma.total * 1e4) / 100) {
5195
+ const expected = Math.round(diploma.score / diploma.total * 1e4) / 100;
5196
+ if (Math.abs(diploma.percentage - expected) > 0.1) {
5197
+ issues.push({
5198
+ contentId: diploma.id,
5199
+ severity: "warning",
5200
+ check: "diploma-score-mismatch",
5201
+ message: `Diploma percentage ${diploma.percentage} doesn't match score ${diploma.score}/${diploma.total} (expected ${expected})`
5202
+ });
5203
+ }
5204
+ }
5205
+ }
5206
+ const symbolCoverage = computeSymbolCoverage(rootDir, index);
5207
+ return {
5208
+ status: issues.some((i) => i.severity === "error") ? "errors" : issues.length > 0 ? "warnings" : "healthy",
5209
+ totalContent: index.totalContent,
5210
+ checked: entriesToCheck.length,
5211
+ issues,
5212
+ symbolCoverage
5213
+ };
5214
+ }
5215
+ function validateQuizContent(rootDir, id, issues) {
5216
+ const quiz = loadQuiz(rootDir, id);
5217
+ if (!quiz) {
5218
+ issues.push({
5219
+ contentId: id,
5220
+ severity: "error",
5221
+ check: "unreadable-quiz",
5222
+ message: "Quiz file could not be parsed"
5223
+ });
5224
+ return;
5225
+ }
5226
+ if (!quiz.passThreshold || quiz.passThreshold < 0 || quiz.passThreshold > 1) {
5227
+ issues.push({
5228
+ contentId: id,
5229
+ severity: "warning",
5230
+ check: "invalid-pass-threshold",
5231
+ message: `passThreshold should be between 0 and 1, got ${quiz.passThreshold}`,
5232
+ fix: "Set passThreshold to a value between 0.0 and 1.0"
5233
+ });
5234
+ }
5235
+ for (const q of quiz.questions) {
5236
+ if (!q.choices || typeof q.choices !== "object") {
5237
+ issues.push({
5238
+ contentId: id,
5239
+ severity: "error",
5240
+ check: "invalid-quiz-choices",
5241
+ message: `Question ${q.id} has no choices defined`
5242
+ });
5243
+ continue;
5244
+ }
5245
+ if (!q.correct || !(q.correct in q.choices)) {
5246
+ issues.push({
5247
+ contentId: id,
5248
+ severity: "error",
5249
+ check: "invalid-quiz-answer",
5250
+ message: `Question ${q.id}: correct answer "${q.correct}" not found in choices [${Object.keys(q.choices).join(", ")}]`,
5251
+ fix: `Set correct to one of: ${Object.keys(q.choices).join(", ")}`
5252
+ });
5253
+ }
5254
+ }
5255
+ }
5256
+ function validatePathContent(rootDir, id, allContentIds, issues) {
5257
+ const lp = loadPath(rootDir, id);
5258
+ if (!lp) {
5259
+ issues.push({
5260
+ contentId: id,
5261
+ severity: "error",
5262
+ check: "unreadable-path",
5263
+ message: "Learning path file could not be parsed"
5264
+ });
5265
+ return;
5266
+ }
5267
+ for (const step of lp.steps) {
5268
+ if (step.content.startsWith("plsat:")) continue;
5269
+ if (!allContentIds.has(step.content)) {
5270
+ issues.push({
5271
+ contentId: id,
5272
+ severity: "error",
5273
+ check: "broken-path-step",
5274
+ message: `Learning path step references "${step.content}" which doesn't exist`,
5275
+ fix: `Create content with id "${step.content}" or remove this step`
5276
+ });
5277
+ }
5278
+ }
5279
+ }
5280
+ function getAffectedUniversityContent(rootDir, symbol) {
5281
+ const index = loadUniversityIndex(rootDir);
5282
+ if (!index) return [];
5283
+ const affected = [];
5284
+ for (const entry of index.entries) {
5285
+ if (entry.symbols.includes(symbol)) {
5286
+ const stale = isContentStale(rootDir, entry, symbol);
5287
+ affected.push({
5288
+ id: entry.id,
5289
+ title: entry.title,
5290
+ type: entry.type,
5291
+ stale
5292
+ });
5293
+ }
5294
+ }
5295
+ return affected;
5296
+ }
5297
+ function getOnboardingSequence(rootDir, student) {
5298
+ const index = loadUniversityIndex(rootDir);
5299
+ if (!index) {
5300
+ return { paths: [], suggestedContent: [], diplomaCount: 0, totalContent: 0 };
5301
+ }
5302
+ const pathEntries = index.entries.filter((e) => e.type === "path");
5303
+ const diplomas = student ? loadDiplomas(rootDir, { student }) : [];
5304
+ const diplomaSourceIds = new Set(diplomas.map((d) => d.source));
5305
+ const paths = pathEntries.map((pe) => {
5306
+ const lp = loadPath(rootDir, pe.id);
5307
+ return {
5308
+ id: pe.id,
5309
+ title: pe.title,
5310
+ steps: lp?.steps.length || 0,
5311
+ completed: diplomaSourceIds.has(pe.id)
5312
+ };
5313
+ });
5314
+ const suggestedContent = index.entries.filter((e) => e.type !== "path" && (e.difficulty === "beginner" || e.tags.includes("onboarding"))).slice(0, 10);
5315
+ return {
5316
+ paths,
5317
+ suggestedContent,
5318
+ diplomaCount: diplomas.length,
5319
+ totalContent: index.totalContent
5320
+ };
5321
+ }
5322
+ function resolveContentFile(rootDir, id, ext) {
5323
+ const contentDir = path9.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR);
5324
+ for (const subdir of [NOTES_DIR, POLICIES_DIR, QUIZZES_DIR, PATHS_DIR]) {
5325
+ const filePath = path9.join(contentDir, subdir, `${id}${ext}`);
5326
+ if (fs8.existsSync(filePath)) return filePath;
5327
+ }
5328
+ return null;
5329
+ }
5330
+ function normalizeFrontmatter(fm) {
5331
+ return {
5332
+ id: fm.id || "",
5333
+ title: fm.title || "",
5334
+ type: fm.type || "note",
5335
+ author: fm.author || "unknown",
5336
+ created: fm.created || "",
5337
+ updated: fm.updated || "",
5338
+ tags: Array.isArray(fm.tags) ? fm.tags : [],
5339
+ symbols: Array.isArray(fm.symbols) ? fm.symbols : [],
5340
+ difficulty: fm.difficulty || "beginner",
5341
+ estimatedMinutes: fm.estimatedMinutes,
5342
+ prerequisites: Array.isArray(fm.prerequisites) ? fm.prerequisites : []
5343
+ };
5344
+ }
5345
+ function normalizeQuiz(quiz) {
5346
+ return {
5347
+ ...quiz,
5348
+ tags: quiz.tags || [],
5349
+ symbols: quiz.symbols || [],
5350
+ difficulty: quiz.difficulty || "beginner",
5351
+ passThreshold: quiz.passThreshold ?? 0.7,
5352
+ questions: quiz.questions || []
5353
+ };
5354
+ }
5355
+ function loadKnownSymbols(rootDir) {
5356
+ const symbols = /* @__PURE__ */ new Set();
5357
+ const scanIndexPath = path9.join(rootDir, ".paradigm", "scan-index.json");
5358
+ if (!fs8.existsSync(scanIndexPath)) return symbols;
5359
+ try {
5360
+ const raw = fs8.readFileSync(scanIndexPath, "utf8");
5361
+ const index = JSON.parse(raw);
5362
+ if (index.symbols && Array.isArray(index.symbols)) {
5363
+ for (const sym of index.symbols) {
5364
+ if (sym.symbol) symbols.add(sym.symbol);
5365
+ }
5366
+ }
5367
+ } catch {
5368
+ }
5369
+ return symbols;
5370
+ }
5371
+ function computeSymbolCoverage(rootDir, index) {
5372
+ const knownSymbols = loadKnownSymbols(rootDir);
5373
+ const coveredSymbols = /* @__PURE__ */ new Set();
5374
+ for (const entry of index.entries) {
5375
+ for (const sym of entry.symbols) {
5376
+ if (knownSymbols.has(sym)) {
5377
+ coveredSymbols.add(sym);
5378
+ }
5379
+ }
5380
+ }
5381
+ const total = knownSymbols.size;
5382
+ return {
5383
+ totalSymbols: total,
5384
+ coveredByContent: coveredSymbols.size,
5385
+ percentage: total > 0 ? Math.round(coveredSymbols.size / total * 100) : 0
5386
+ };
5387
+ }
5388
+ function isContentStale(rootDir, entry, _symbol) {
5389
+ if (!entry.updated) return false;
5390
+ const contentUpdated = new Date(entry.updated).getTime();
5391
+ if (isNaN(contentUpdated)) return false;
5392
+ const scanIndexPath = path9.join(rootDir, ".paradigm", "scan-index.json");
5393
+ if (!fs8.existsSync(scanIndexPath)) return false;
5394
+ try {
5395
+ const raw = fs8.readFileSync(scanIndexPath, "utf8");
5396
+ const index = JSON.parse(raw);
5397
+ if (index.symbols && Array.isArray(index.symbols)) {
5398
+ for (const sym of index.symbols) {
5399
+ if (sym.symbol === _symbol && sym.filePath) {
5400
+ const purposePath = path9.join(rootDir, sym.filePath);
5401
+ if (fs8.existsSync(purposePath)) {
5402
+ const stat = fs8.statSync(purposePath);
5403
+ if (stat.mtime.getTime() > contentUpdated) {
5404
+ return true;
5405
+ }
5406
+ }
5407
+ }
5408
+ }
5409
+ }
5410
+ } catch {
5411
+ }
5412
+ return false;
5413
+ }
5414
+ function checkContentStaleness(rootDir, entry, issues) {
5415
+ for (const sym of entry.symbols) {
5416
+ if (isContentStale(rootDir, entry, sym)) {
5417
+ issues.push({
5418
+ contentId: entry.id,
5419
+ severity: "warning",
5420
+ check: "stale-content",
5421
+ message: `Content may be stale: symbol "${sym}" was updated after content was last modified`,
5422
+ fix: `Review and update ${entry.id} to reflect changes to ${sym}`
5423
+ });
5424
+ break;
5425
+ }
5426
+ }
5427
+ }
5428
+
4643
5429
  // ../paradigm-mcp/src/tools/reindex.ts
4644
5430
  var SYMBOL_CATEGORIES = {
4645
5431
  "@": { category: "features", prefix: "@" },
@@ -4728,10 +5514,10 @@ async function rebuildStaticFiles(rootDir, ctx) {
4728
5514
  } else {
4729
5515
  aggregation = await aggregateFromDirectory(rootDir);
4730
5516
  }
4731
- const projectName = ctx?.projectName || path9.basename(rootDir);
4732
- const paradigmDir = path9.join(rootDir, ".paradigm");
4733
- if (!fs8.existsSync(paradigmDir)) {
4734
- fs8.mkdirSync(paradigmDir, { recursive: true });
5517
+ const projectName = ctx?.projectName || path10.basename(rootDir);
5518
+ const paradigmDir = path10.join(rootDir, ".paradigm");
5519
+ if (!fs9.existsSync(paradigmDir)) {
5520
+ fs9.mkdirSync(paradigmDir, { recursive: true });
4735
5521
  }
4736
5522
  const scanIndex = generateScanIndex(
4737
5523
  {
@@ -4741,22 +5527,22 @@ async function rebuildStaticFiles(rootDir, ctx) {
4741
5527
  },
4742
5528
  { projectName }
4743
5529
  );
4744
- const scanIndexPath = path9.join(paradigmDir, "scan-index.json");
4745
- fs8.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
5530
+ const scanIndexPath = path10.join(paradigmDir, "scan-index.json");
5531
+ fs9.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
4746
5532
  filesWritten.push(".paradigm/scan-index.json");
4747
5533
  const navigatorData = buildNavigatorData(rootDir, aggregation);
4748
- const navigatorPath = path9.join(paradigmDir, "navigator.yaml");
4749
- fs8.writeFileSync(
5534
+ const navigatorPath = path10.join(paradigmDir, "navigator.yaml");
5535
+ fs9.writeFileSync(
4750
5536
  navigatorPath,
4751
- yaml7.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
5537
+ yaml8.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
4752
5538
  "utf8"
4753
5539
  );
4754
5540
  filesWritten.push(".paradigm/navigator.yaml");
4755
5541
  const flowIndex = generateFlowIndex(rootDir, aggregation.purposeFiles);
4756
5542
  let flowCount = 0;
4757
5543
  if (flowIndex && Object.keys(flowIndex.flows).length > 0) {
4758
- const flowIndexPath = path9.join(paradigmDir, "flow-index.json");
4759
- fs8.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
5544
+ const flowIndexPath = path10.join(paradigmDir, "flow-index.json");
5545
+ fs9.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
4760
5546
  filesWritten.push(".paradigm/flow-index.json");
4761
5547
  flowCount = Object.keys(flowIndex.flows).length;
4762
5548
  }
@@ -4797,10 +5583,68 @@ async function rebuildStaticFiles(rootDir, ctx) {
4797
5583
  }
4798
5584
  } catch {
4799
5585
  }
5586
+ let universityStats;
5587
+ try {
5588
+ const uniDir = path10.join(rootDir, ".paradigm", "university");
5589
+ if (fs9.existsSync(uniDir)) {
5590
+ const uniIndex = rebuildUniversityIndex(rootDir);
5591
+ if (uniIndex.totalContent > 0 || uniIndex.diplomaCount > 0) {
5592
+ universityStats = {
5593
+ totalContent: uniIndex.totalContent,
5594
+ diplomaCount: uniIndex.diplomaCount
5595
+ };
5596
+ filesWritten.push(".paradigm/university/index.yaml");
5597
+ }
5598
+ }
5599
+ } catch {
5600
+ }
5601
+ let integrityReport;
5602
+ try {
5603
+ integrityReport = checkIntegrity(aggregation, rootDir);
5604
+ } catch {
5605
+ }
5606
+ let componentAnchorIssues;
5607
+ try {
5608
+ const anchorReport = checkComponentAnchors(aggregation.symbols, rootDir);
5609
+ const issues = anchorReport.missing + anchorReport.outOfBounds;
5610
+ if (issues > 0) {
5611
+ componentAnchorIssues = issues;
5612
+ }
5613
+ } catch {
5614
+ }
5615
+ let purposeHealth;
5616
+ try {
5617
+ purposeHealth = checkPurposeHealth(aggregation.purposeFiles, rootDir);
5618
+ } catch {
5619
+ }
5620
+ let crossFileIssues;
5621
+ try {
5622
+ const parsedFiles = [];
5623
+ for (const filePath of aggregation.purposeFiles) {
5624
+ const result = parsePurposeFile(filePath);
5625
+ if (result.data) {
5626
+ parsedFiles.push({ filePath, data: result.data });
5627
+ }
5628
+ }
5629
+ if (parsedFiles.length > 0) {
5630
+ const crossResult = validateCrossFile(parsedFiles);
5631
+ const warningCount = crossResult.issues.length;
5632
+ if (warningCount > 0) {
5633
+ crossFileIssues = warningCount;
5634
+ }
5635
+ }
5636
+ } catch {
5637
+ }
4800
5638
  const breakdown = {};
4801
5639
  for (const sym of aggregation.symbols) {
4802
5640
  breakdown[sym.type] = (breakdown[sym.type] || 0) + 1;
4803
5641
  }
5642
+ const componentTypeBreakdown = {};
5643
+ for (const sym of aggregation.symbols) {
5644
+ if (sym.type === "component" && sym.componentType) {
5645
+ componentTypeBreakdown[sym.componentType] = (componentTypeBreakdown[sym.componentType] || 0) + 1;
5646
+ }
5647
+ }
4804
5648
  return {
4805
5649
  action: "reindex",
4806
5650
  filesWritten,
@@ -4809,23 +5653,38 @@ async function rebuildStaticFiles(rootDir, ctx) {
4809
5653
  flowCount,
4810
5654
  aspectGraphStats,
4811
5655
  personaCount,
4812
- protocolHealth
5656
+ protocolHealth,
5657
+ ...Object.keys(componentTypeBreakdown).length > 0 ? { componentTypeBreakdown } : {},
5658
+ ...universityStats ? { universityStats } : {},
5659
+ ...integrityReport ? { integrityReport } : {},
5660
+ ...componentAnchorIssues !== void 0 ? { componentAnchorIssues } : {},
5661
+ ...purposeHealth ? { purposeHealth } : {},
5662
+ ...crossFileIssues !== void 0 ? { crossFileIssues } : {}
4813
5663
  };
4814
5664
  }
4815
5665
  function buildNavigatorData(rootDir, aggregation) {
5666
+ const symbolsByComponentType = {};
5667
+ for (const sym of aggregation.symbols) {
5668
+ if (sym.type === "component" && sym.componentType) {
5669
+ const ct = sym.componentType;
5670
+ if (!symbolsByComponentType[ct]) symbolsByComponentType[ct] = [];
5671
+ symbolsByComponentType[ct].push(sym.symbol);
5672
+ }
5673
+ }
4816
5674
  return {
4817
5675
  version: "1.0",
4818
5676
  generated: (/* @__PURE__ */ new Date()).toISOString(),
4819
5677
  structure: buildStructure(rootDir),
4820
5678
  key_files: buildKeyFiles(rootDir),
4821
5679
  skip_patterns: buildSkipPatterns(rootDir),
4822
- symbols: buildSymbolMap(aggregation.symbols, aggregation.purposeFiles, rootDir)
5680
+ symbols: buildSymbolMap(aggregation.symbols, aggregation.purposeFiles, rootDir),
5681
+ ...Object.keys(symbolsByComponentType).length > 0 ? { symbolsByComponentType } : {}
4823
5682
  };
4824
5683
  }
4825
5684
  function buildStructure(rootDir) {
4826
5685
  const structure = {};
4827
5686
  for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
4828
- const existingPaths = patterns.filter((p) => fs8.existsSync(path9.join(rootDir, p)));
5687
+ const existingPaths = patterns.filter((p) => fs9.existsSync(path10.join(rootDir, p)));
4829
5688
  if (existingPaths.length > 0) {
4830
5689
  const symbolInfo = Object.values(SYMBOL_CATEGORIES).find((s) => s.category === category);
4831
5690
  structure[category] = { paths: existingPaths, symbol: symbolInfo?.prefix || "@" };
@@ -4836,7 +5695,7 @@ function buildStructure(rootDir) {
4836
5695
  function buildKeyFiles(rootDir) {
4837
5696
  const keyFiles = {};
4838
5697
  for (const [category, patterns] of Object.entries(KEY_FILE_PATTERNS)) {
4839
- const existingPaths = patterns.filter((p) => fs8.existsSync(path9.join(rootDir, p)));
5698
+ const existingPaths = patterns.filter((p) => fs9.existsSync(path10.join(rootDir, p)));
4840
5699
  if (existingPaths.length > 0) {
4841
5700
  keyFiles[category] = existingPaths;
4842
5701
  }
@@ -4852,10 +5711,10 @@ function buildSkipPatterns(rootDir) {
4852
5711
  unless_testing: [...DEFAULT_SKIP_PATTERNS.unless_testing],
4853
5712
  unless_docs: [...DEFAULT_SKIP_PATTERNS.unless_docs]
4854
5713
  };
4855
- const gitignorePath = path9.join(rootDir, ".gitignore");
4856
- if (fs8.existsSync(gitignorePath)) {
5714
+ const gitignorePath = path10.join(rootDir, ".gitignore");
5715
+ if (fs9.existsSync(gitignorePath)) {
4857
5716
  try {
4858
- const content = fs8.readFileSync(gitignorePath, "utf8");
5717
+ const content = fs9.readFileSync(gitignorePath, "utf8");
4859
5718
  const gitignorePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).filter(
4860
5719
  (line) => line.endsWith("/") || line.includes("*") || ["node_modules", "dist", "build", ".cache"].some((p) => line.includes(p))
4861
5720
  ).slice(0, 20);
@@ -4904,11 +5763,11 @@ function buildSymbolMap(symbols, purposeFiles, _rootDir) {
4904
5763
  symbolMap[symbolId] = symbol.filePath;
4905
5764
  } else {
4906
5765
  const matchingPurpose = purposeFiles.find((pf) => {
4907
- const dir = path9.dirname(pf);
5766
+ const dir = path10.dirname(pf);
4908
5767
  return dir.toLowerCase().includes(symbol.id.toLowerCase());
4909
5768
  });
4910
5769
  if (matchingPurpose) {
4911
- symbolMap[symbolId] = path9.dirname(matchingPurpose) + "/";
5770
+ symbolMap[symbolId] = path10.dirname(matchingPurpose) + "/";
4912
5771
  }
4913
5772
  }
4914
5773
  }
@@ -4919,8 +5778,8 @@ function generateFlowIndex(rootDir, purposeFiles) {
4919
5778
  const symbolToFlows = {};
4920
5779
  for (const filePath of purposeFiles) {
4921
5780
  try {
4922
- const content = fs8.readFileSync(filePath, "utf8");
4923
- const data = yaml7.load(content);
5781
+ const content = fs9.readFileSync(filePath, "utf8");
5782
+ const data = yaml8.load(content);
4924
5783
  if (!data?.flows) continue;
4925
5784
  if (Array.isArray(data.flows)) {
4926
5785
  for (const flowItem of data.flows) {
@@ -4933,7 +5792,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
4933
5792
  id: flowId,
4934
5793
  description: flow.description || "",
4935
5794
  steps,
4936
- definedIn: path9.relative(rootDir, filePath)
5795
+ definedIn: path10.relative(rootDir, filePath)
4937
5796
  };
4938
5797
  indexFlowSymbols(flowId, steps, symbolToFlows);
4939
5798
  }
@@ -4949,7 +5808,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
4949
5808
  trigger: flowDef.trigger,
4950
5809
  steps,
4951
5810
  validation: flowDef.validation,
4952
- definedIn: path9.relative(rootDir, filePath)
5811
+ definedIn: path10.relative(rootDir, filePath)
4953
5812
  };
4954
5813
  indexFlowSymbols(flowId, steps, symbolToFlows);
4955
5814
  }
@@ -5059,6 +5918,20 @@ export {
5059
5918
  updateProtocol,
5060
5919
  validateProtocol,
5061
5920
  detectProtocolSuggestion,
5921
+ loadUniversityConfig,
5922
+ loadNote,
5923
+ saveNote,
5924
+ loadQuiz,
5925
+ saveQuiz,
5926
+ loadPath,
5927
+ savePath,
5928
+ loadDiplomas,
5929
+ saveDiploma,
5930
+ searchContent,
5931
+ rebuildUniversityIndex,
5932
+ validateUniversityContent,
5933
+ getAffectedUniversityContent,
5934
+ getOnboardingSequence,
5062
5935
  getReindexToolsList,
5063
5936
  handleReindexTool,
5064
5937
  rebuildStaticFiles