@a-company/paradigm 3.34.0 → 3.44.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-XXANWJVZ.js → accept-orchestration-ZUWQUHSK.js} +6 -6
  2. package/dist/add-VSPZ6FM4.js +81 -0
  3. package/dist/{aggregate-XHQ6GI3Z.js → aggregate-SV3VGEIL.js} +2 -2
  4. package/dist/assess-UHBDYIK7.js +68 -0
  5. package/dist/{beacon-BTLQMYQL.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-S5TDFT5Q.js → chunk-7COU5S2Z.js} +2 -2
  9. package/dist/{chunk-H4TVBJD4.js → chunk-AKIMFN6I.js} +3 -3
  10. package/dist/{chunk-3DYYXGDC.js → chunk-CDMAMDSG.js} +33 -0
  11. package/dist/chunk-F3BCHPYT.js +143 -0
  12. package/dist/{chunk-R2SGQ22F.js → chunk-FKJUBQU3.js} +461 -2
  13. package/dist/chunk-GT5QGC2H.js +253 -0
  14. package/dist/{chunk-UQNTJ5VB.js → chunk-HIKKOCXY.js} +1 -1
  15. package/dist/{chunk-J26YQVAK.js → chunk-J4E6K5MG.js} +1 -1
  16. package/dist/chunk-L27I3CPZ.js +357 -0
  17. package/dist/{chunk-WOONGZ3C.js → chunk-P7XSBJE3.js} +1 -1
  18. package/dist/{chunk-Z7W7HNRG.js → chunk-QDXI2DHR.js} +1 -1
  19. package/dist/{chunk-BRILIG7Z.js → chunk-QIOCFXDQ.js} +42 -0
  20. package/dist/{chunk-3BGSDKWD.js → chunk-QWA26UNO.js} +7 -7
  21. package/dist/{lore-server-ILPHKWLK.js → chunk-RAB5IKPR.js} +77 -112
  22. package/dist/chunk-SOBTKFSP.js +616 -0
  23. package/dist/{chunk-BKMNLROM.js → chunk-ZDHLG5VP.js} +461 -147
  24. package/dist/{chunk-CTF6RHKG.js → chunk-ZGUAAVMA.js} +17 -2
  25. package/dist/{chunk-PFLWLC6J.js → chunk-ZMQA6SCO.js} +855 -34
  26. package/dist/{chunk-3BAMPB6I.js → chunk-ZSYVKSY6.js} +2 -147
  27. package/dist/{commands-KPT2T2OZ.js → commands-5N4ILTPH.js} +465 -1
  28. package/dist/config-schema-3YNIFJCJ.js +152 -0
  29. package/dist/{constellation-LZ6XIKDT.js → constellation-FAGT45TU.js} +2 -2
  30. package/dist/{context-audit-RI4R2WRH.js → context-audit-557EO6PK.js} +138 -8
  31. package/dist/{cost-4SZM7OUS.js → cost-UD3WPEKZ.js} +1 -1
  32. package/dist/{delete-YTASL4SM.js → delete-RRK4RL6Y.js} +1 -1
  33. package/dist/{diff-T6YJSAAC.js → diff-IP5CIARP.js} +6 -6
  34. package/dist/{dist-AG5JNIZU-HW2FWNTZ.js → dist-5QE2BB2B-X6DYVSUL.js} +59 -5
  35. package/dist/{dist-IKBGY7FQ.js → dist-CM3MVWWW.js} +3 -1
  36. package/dist/{dist-OH4DBV2O.js → dist-OGTSAZ55.js} +16 -1
  37. package/dist/{dist-RMAIFRTW.js → dist-POMVY6WP.js} +5 -3
  38. package/dist/{dist-QSBAGCZT.js → dist-UXWV4OKX.js} +2 -2
  39. package/dist/{doctor-INBOLZC7.js → doctor-GKZJU7QG.js} +1 -1
  40. package/dist/{edit-S7NZD7H7.js → edit-4CLNN5JG.js} +1 -1
  41. package/dist/{graph-ERNQQQ7C.js → graph-YYUXI3F7.js} +1 -1
  42. package/dist/graph-server-ZPXRSGCW.js +116 -0
  43. package/dist/{habits-7BORPC2F.js → habits-RG5SVKXP.js} +2 -2
  44. package/dist/index.js +200 -86
  45. package/dist/integrity-MK2OP5TA.js +194 -0
  46. package/dist/integrity-checker-J7YXRTBT.js +11 -0
  47. package/dist/{lint-MTRZB5EC.js → lint-HYWGS3JJ.js} +1 -1
  48. package/dist/{list-QTFWN35D.js → list-BTLFHSRC.js} +1 -1
  49. package/dist/list-IUCYPGMK.js +57 -0
  50. package/dist/{lore-loader-S5BXMH27.js → lore-loader-VTEEZDX3.js} +3 -1
  51. package/dist/lore-server-NOOAHKJX.js +118 -0
  52. package/dist/mcp.js +2591 -112
  53. package/dist/{migrate-HRN5TUBQ.js → migrate-FQVGQNXZ.js} +21 -3
  54. package/dist/{migrate-assessments-FPR6C35Z.js → migrate-assessments-JP6Q5KME.js} +1 -1
  55. package/dist/{orchestrate-3SI6ON33.js → orchestrate-A226N6FC.js} +6 -6
  56. package/dist/platform-server-KHL6ZPPN.js +900 -0
  57. package/dist/{probe-ABMGCXQG.js → probe-7JK7IDNI.js} +4 -4
  58. package/dist/{providers-YW3FG6DA.js → providers-YNFSL6HK.js} +1 -1
  59. package/dist/quiz-I75NU2QQ.js +99 -0
  60. package/dist/{record-UGN75GTB.js → record-46CLR4OG.js} +11 -2
  61. package/dist/{reindex-YC7LD4MN.js → reindex-WIJMCJ4A.js} +3 -2
  62. package/dist/{remember-WR6ZVXLT.js → remember-4EUZKIIB.js} +1 -1
  63. package/dist/{retag-URLJLMSK.js → retag-KC4JVRLE.js} +1 -1
  64. package/dist/{review-725ZKA7U.js → review-Q7M4CRB5.js} +1 -1
  65. package/dist/{ripple-QTXKJCEI.js → ripple-RI3LOT6R.js} +2 -2
  66. package/dist/{sentinel-FUR3QKCJ.js → sentinel-UOIGJWHH.js} +1 -1
  67. package/dist/sentinel-bridge-APDXYAZS.js +109 -0
  68. package/dist/sentinel-mcp.js +13 -0
  69. package/dist/sentinel-ui/assets/{index-Zh1YM0C9.css → index-CJ1Wx083.css} +1 -1
  70. package/dist/sentinel-ui/assets/index-S1VJ67dT.js +62 -0
  71. package/dist/sentinel-ui/assets/index-S1VJ67dT.js.map +1 -0
  72. package/dist/sentinel-ui/index.html +2 -2
  73. package/dist/sentinel.js +6 -6
  74. package/dist/{serve-DIALBCTU.js → serve-22A4XOIG.js} +1 -1
  75. package/dist/{university-A66BMZ4Z.js → serve-2YJ6D2Y6.js} +9 -8
  76. package/dist/serve-JVXSRSUB.js +33 -0
  77. package/dist/{server-2VICPDUR.js → server-JV6UFGWZ.js} +25 -2
  78. package/dist/{server-OWBK2WFS.js → server-RDLQ3DK7.js} +49 -4
  79. package/dist/{setup-ASR6OMKV.js → setup-M2ZKLKNN.js} +2 -2
  80. package/dist/{shift-7XLSBLDW.js → shift-LNMKFYLR.js} +63 -14
  81. package/dist/{show-GEVVQWWG.js → show-P7GYO43X.js} +1 -1
  82. package/dist/show-PKZMYKRN.js +82 -0
  83. package/dist/{snapshot-QZFD7YBI.js → snapshot-Y3COXK4T.js} +2 -2
  84. package/dist/{spawn-DIY7T4QW.js → spawn-SSXZX45U.js} +2 -2
  85. package/dist/status-KLHALGW4.js +71 -0
  86. package/dist/{summary-R4CSYNNP.js → summary-5NQNOD3F.js} +2 -2
  87. package/dist/{sweep-5POCF2E4.js → sweep-EZU3GU6S.js} +1 -1
  88. package/dist/symphony-EYRGGVNE.js +470 -0
  89. package/dist/symphony-QWOEKZMC.js +308 -0
  90. package/dist/{team-VH3HYABB.js → team-HGLJXWQG.js} +7 -7
  91. package/dist/{timeline-RKXNRMKF.js → timeline-ANC7LVDL.js} +1 -1
  92. package/dist/{triage-GJ6GK647.js → triage-IZ4MDYNB.js} +2 -2
  93. package/dist/university-content/courses/.purpose +7 -1
  94. package/dist/university-content/courses/para-501.json +166 -0
  95. package/dist/university-content/plsat/.purpose +6 -0
  96. package/dist/university-content/plsat/v3.0.json +323 -1
  97. package/dist/university-content/reference.json +48 -0
  98. package/dist/university-ui/assets/{index-TcsCEBMo.js → index-tfi5xN4Q.js} +2 -2
  99. package/dist/university-ui/assets/{index-TcsCEBMo.js.map → index-tfi5xN4Q.js.map} +1 -1
  100. package/dist/university-ui/index.html +1 -1
  101. package/dist/validate-GD5XWILV.js +134 -0
  102. package/dist/{validate-OUHUBZPO.js → validate-ZVPNN4FL.js} +1 -1
  103. package/dist/{workspace-5RBSALXC.js → workspace-UIUTHZTD.js} +5 -5
  104. package/package.json +4 -2
  105. package/platform-ui/dist/assets/GitSection-BD3Ze06e.js +4 -0
  106. package/platform-ui/dist/assets/GitSection-C-GQWHcu.css +1 -0
  107. package/platform-ui/dist/assets/GraphSection-BlgXTl53.css +1 -0
  108. package/platform-ui/dist/assets/GraphSection-SglITfSs.js +8 -0
  109. package/platform-ui/dist/assets/LoreSection-C3EixkjW.css +1 -0
  110. package/platform-ui/dist/assets/LoreSection-bR5Km4Fd.js +1 -0
  111. package/platform-ui/dist/assets/SentinelSection-BI-aIYKL.css +1 -0
  112. package/platform-ui/dist/assets/SentinelSection-QSpAZArG.js +1 -0
  113. package/platform-ui/dist/assets/SymphonySection-CobYJgvg.js +1 -0
  114. package/platform-ui/dist/assets/SymphonySection-zY0C5tFl.css +1 -0
  115. package/platform-ui/dist/assets/index-CfpZFjea.css +1 -0
  116. package/platform-ui/dist/assets/index-DbxeSMkV.js +57 -0
  117. package/platform-ui/dist/index.html +14 -0
  118. package/dist/graph-server-BZ73HTAT.js +0 -251
  119. package/dist/sentinel-ui/assets/index-C_Wstm64.js +0 -62
  120. package/dist/sentinel-ui/assets/index-C_Wstm64.js.map +0 -1
  121. /package/dist/{chunk-VUSCJJ4A.js → chunk-EDOAWN7J.js} +0 -0
  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";
@@ -625,6 +630,138 @@ function validatePurposeItem(id, item, itemType, prefix, issues) {
625
630
  }
626
631
  }
627
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
+ }
628
765
 
629
766
  // ../portal/core/dist/index.js
630
767
  import * as fs2 from "fs";
@@ -1046,12 +1183,27 @@ async function aggregateFromPremise(premiseFile, rootDir) {
1046
1183
  }));
1047
1184
  }
1048
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
+ }
1049
1200
  return {
1050
1201
  symbols,
1051
1202
  purposeFiles,
1052
1203
  portalFiles,
1053
1204
  errors,
1054
- timestamp: Date.now()
1205
+ timestamp: Date.now(),
1206
+ ...duplicateSymbols.length > 0 ? { duplicateSymbols } : {}
1055
1207
  };
1056
1208
  }
1057
1209
  function createSymbolEntry(partial) {
@@ -2033,8 +2185,8 @@ var SessionTracker = class {
2033
2185
  * Extract resource type from URI
2034
2186
  */
2035
2187
  extractResourceType(uri) {
2036
- const path10 = uri.replace("paradigm://", "");
2037
- const firstPart = path10.split("/")[0];
2188
+ const path11 = uri.replace("paradigm://", "");
2189
+ const firstPart = path11.split("/")[0];
2038
2190
  return firstPart || "unknown";
2039
2191
  }
2040
2192
  /**
@@ -2788,7 +2940,7 @@ async function buildRecoveryPreamble(rootDir) {
2788
2940
  } catch {
2789
2941
  }
2790
2942
  try {
2791
- const { loadLoreEntries: loadLoreEntries2 } = await import("./lore-loader-S5BXMH27.js");
2943
+ const { loadLoreEntries: loadLoreEntries2 } = await import("./lore-loader-VTEEZDX3.js");
2792
2944
  const arcEntries = await loadLoreEntries2(rootDir, { limit: 10 });
2793
2945
  const entriesWithArcs = arcEntries.filter((e) => e.tags?.some((t) => t.startsWith("arc:")));
2794
2946
  if (entriesWithArcs.length > 0) {
@@ -4172,8 +4324,8 @@ async function getAffectedPersonas(rootDir, symbol) {
4172
4324
  }
4173
4325
  return results;
4174
4326
  }
4175
- function deepGet(obj, path10) {
4176
- const parts = path10.split(/[.\[\]]+/).filter(Boolean);
4327
+ function deepGet(obj, path11) {
4328
+ const parts = path11.split(/[.\[\]]+/).filter(Boolean);
4177
4329
  let current = obj;
4178
4330
  for (const part of parts) {
4179
4331
  if (current == null || typeof current !== "object") return void 0;
@@ -4253,7 +4405,7 @@ function assertStep(step, event) {
4253
4405
  async function validateAgainstSentinel(persona, options = {}) {
4254
4406
  const steps = [];
4255
4407
  try {
4256
- const { SentinelStorage } = await import("./dist-IKBGY7FQ.js");
4408
+ const { SentinelStorage } = await import("./dist-CM3MVWWW.js");
4257
4409
  const storage = new SentinelStorage();
4258
4410
  const events = storage.queryEvents?.({
4259
4411
  schemaId: "paradigm-personas",
@@ -4676,6 +4828,604 @@ function slugify(name) {
4676
4828
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
4677
4829
  }
4678
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
+
4679
5429
  // ../paradigm-mcp/src/tools/reindex.ts
4680
5430
  var SYMBOL_CATEGORIES = {
4681
5431
  "@": { category: "features", prefix: "@" },
@@ -4764,10 +5514,10 @@ async function rebuildStaticFiles(rootDir, ctx) {
4764
5514
  } else {
4765
5515
  aggregation = await aggregateFromDirectory(rootDir);
4766
5516
  }
4767
- const projectName = ctx?.projectName || path9.basename(rootDir);
4768
- const paradigmDir = path9.join(rootDir, ".paradigm");
4769
- if (!fs8.existsSync(paradigmDir)) {
4770
- 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 });
4771
5521
  }
4772
5522
  const scanIndex = generateScanIndex(
4773
5523
  {
@@ -4777,22 +5527,22 @@ async function rebuildStaticFiles(rootDir, ctx) {
4777
5527
  },
4778
5528
  { projectName }
4779
5529
  );
4780
- const scanIndexPath = path9.join(paradigmDir, "scan-index.json");
4781
- fs8.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
5530
+ const scanIndexPath = path10.join(paradigmDir, "scan-index.json");
5531
+ fs9.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
4782
5532
  filesWritten.push(".paradigm/scan-index.json");
4783
5533
  const navigatorData = buildNavigatorData(rootDir, aggregation);
4784
- const navigatorPath = path9.join(paradigmDir, "navigator.yaml");
4785
- fs8.writeFileSync(
5534
+ const navigatorPath = path10.join(paradigmDir, "navigator.yaml");
5535
+ fs9.writeFileSync(
4786
5536
  navigatorPath,
4787
- yaml7.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
5537
+ yaml8.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
4788
5538
  "utf8"
4789
5539
  );
4790
5540
  filesWritten.push(".paradigm/navigator.yaml");
4791
5541
  const flowIndex = generateFlowIndex(rootDir, aggregation.purposeFiles);
4792
5542
  let flowCount = 0;
4793
5543
  if (flowIndex && Object.keys(flowIndex.flows).length > 0) {
4794
- const flowIndexPath = path9.join(paradigmDir, "flow-index.json");
4795
- 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");
4796
5546
  filesWritten.push(".paradigm/flow-index.json");
4797
5547
  flowCount = Object.keys(flowIndex.flows).length;
4798
5548
  }
@@ -4833,6 +5583,58 @@ async function rebuildStaticFiles(rootDir, ctx) {
4833
5583
  }
4834
5584
  } catch {
4835
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
+ }
4836
5638
  const breakdown = {};
4837
5639
  for (const sym of aggregation.symbols) {
4838
5640
  breakdown[sym.type] = (breakdown[sym.type] || 0) + 1;
@@ -4852,7 +5654,12 @@ async function rebuildStaticFiles(rootDir, ctx) {
4852
5654
  aspectGraphStats,
4853
5655
  personaCount,
4854
5656
  protocolHealth,
4855
- ...Object.keys(componentTypeBreakdown).length > 0 ? { componentTypeBreakdown } : {}
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 } : {}
4856
5663
  };
4857
5664
  }
4858
5665
  function buildNavigatorData(rootDir, aggregation) {
@@ -4877,7 +5684,7 @@ function buildNavigatorData(rootDir, aggregation) {
4877
5684
  function buildStructure(rootDir) {
4878
5685
  const structure = {};
4879
5686
  for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
4880
- const existingPaths = patterns.filter((p) => fs8.existsSync(path9.join(rootDir, p)));
5687
+ const existingPaths = patterns.filter((p) => fs9.existsSync(path10.join(rootDir, p)));
4881
5688
  if (existingPaths.length > 0) {
4882
5689
  const symbolInfo = Object.values(SYMBOL_CATEGORIES).find((s) => s.category === category);
4883
5690
  structure[category] = { paths: existingPaths, symbol: symbolInfo?.prefix || "@" };
@@ -4888,7 +5695,7 @@ function buildStructure(rootDir) {
4888
5695
  function buildKeyFiles(rootDir) {
4889
5696
  const keyFiles = {};
4890
5697
  for (const [category, patterns] of Object.entries(KEY_FILE_PATTERNS)) {
4891
- const existingPaths = patterns.filter((p) => fs8.existsSync(path9.join(rootDir, p)));
5698
+ const existingPaths = patterns.filter((p) => fs9.existsSync(path10.join(rootDir, p)));
4892
5699
  if (existingPaths.length > 0) {
4893
5700
  keyFiles[category] = existingPaths;
4894
5701
  }
@@ -4904,10 +5711,10 @@ function buildSkipPatterns(rootDir) {
4904
5711
  unless_testing: [...DEFAULT_SKIP_PATTERNS.unless_testing],
4905
5712
  unless_docs: [...DEFAULT_SKIP_PATTERNS.unless_docs]
4906
5713
  };
4907
- const gitignorePath = path9.join(rootDir, ".gitignore");
4908
- if (fs8.existsSync(gitignorePath)) {
5714
+ const gitignorePath = path10.join(rootDir, ".gitignore");
5715
+ if (fs9.existsSync(gitignorePath)) {
4909
5716
  try {
4910
- const content = fs8.readFileSync(gitignorePath, "utf8");
5717
+ const content = fs9.readFileSync(gitignorePath, "utf8");
4911
5718
  const gitignorePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).filter(
4912
5719
  (line) => line.endsWith("/") || line.includes("*") || ["node_modules", "dist", "build", ".cache"].some((p) => line.includes(p))
4913
5720
  ).slice(0, 20);
@@ -4956,11 +5763,11 @@ function buildSymbolMap(symbols, purposeFiles, _rootDir) {
4956
5763
  symbolMap[symbolId] = symbol.filePath;
4957
5764
  } else {
4958
5765
  const matchingPurpose = purposeFiles.find((pf) => {
4959
- const dir = path9.dirname(pf);
5766
+ const dir = path10.dirname(pf);
4960
5767
  return dir.toLowerCase().includes(symbol.id.toLowerCase());
4961
5768
  });
4962
5769
  if (matchingPurpose) {
4963
- symbolMap[symbolId] = path9.dirname(matchingPurpose) + "/";
5770
+ symbolMap[symbolId] = path10.dirname(matchingPurpose) + "/";
4964
5771
  }
4965
5772
  }
4966
5773
  }
@@ -4971,8 +5778,8 @@ function generateFlowIndex(rootDir, purposeFiles) {
4971
5778
  const symbolToFlows = {};
4972
5779
  for (const filePath of purposeFiles) {
4973
5780
  try {
4974
- const content = fs8.readFileSync(filePath, "utf8");
4975
- const data = yaml7.load(content);
5781
+ const content = fs9.readFileSync(filePath, "utf8");
5782
+ const data = yaml8.load(content);
4976
5783
  if (!data?.flows) continue;
4977
5784
  if (Array.isArray(data.flows)) {
4978
5785
  for (const flowItem of data.flows) {
@@ -4985,7 +5792,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
4985
5792
  id: flowId,
4986
5793
  description: flow.description || "",
4987
5794
  steps,
4988
- definedIn: path9.relative(rootDir, filePath)
5795
+ definedIn: path10.relative(rootDir, filePath)
4989
5796
  };
4990
5797
  indexFlowSymbols(flowId, steps, symbolToFlows);
4991
5798
  }
@@ -5001,7 +5808,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
5001
5808
  trigger: flowDef.trigger,
5002
5809
  steps,
5003
5810
  validation: flowDef.validation,
5004
- definedIn: path9.relative(rootDir, filePath)
5811
+ definedIn: path10.relative(rootDir, filePath)
5005
5812
  };
5006
5813
  indexFlowSymbols(flowId, steps, symbolToFlows);
5007
5814
  }
@@ -5111,6 +5918,20 @@ export {
5111
5918
  updateProtocol,
5112
5919
  validateProtocol,
5113
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,
5114
5935
  getReindexToolsList,
5115
5936
  handleReindexTool,
5116
5937
  rebuildStaticFiles