@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.
- package/dist/{accept-orchestration-XXANWJVZ.js → accept-orchestration-ZUWQUHSK.js} +6 -6
- package/dist/add-VSPZ6FM4.js +81 -0
- package/dist/{aggregate-XHQ6GI3Z.js → aggregate-SV3VGEIL.js} +2 -2
- package/dist/assess-UHBDYIK7.js +68 -0
- package/dist/{beacon-BTLQMYQL.js → beacon-3SJV4DAP.js} +2 -2
- package/dist/calibration-WWHK73WU.js +135 -0
- package/dist/{chunk-C5ZE6WEX.js → chunk-2SKXFXIT.js} +91 -1
- package/dist/{chunk-S5TDFT5Q.js → chunk-7COU5S2Z.js} +2 -2
- package/dist/{chunk-H4TVBJD4.js → chunk-AKIMFN6I.js} +3 -3
- package/dist/{chunk-3DYYXGDC.js → chunk-CDMAMDSG.js} +33 -0
- package/dist/chunk-F3BCHPYT.js +143 -0
- package/dist/{chunk-R2SGQ22F.js → chunk-FKJUBQU3.js} +461 -2
- package/dist/chunk-GT5QGC2H.js +253 -0
- package/dist/{chunk-UQNTJ5VB.js → chunk-HIKKOCXY.js} +1 -1
- package/dist/{chunk-J26YQVAK.js → chunk-J4E6K5MG.js} +1 -1
- package/dist/chunk-L27I3CPZ.js +357 -0
- package/dist/{chunk-WOONGZ3C.js → chunk-P7XSBJE3.js} +1 -1
- package/dist/{chunk-Z7W7HNRG.js → chunk-QDXI2DHR.js} +1 -1
- package/dist/{chunk-BRILIG7Z.js → chunk-QIOCFXDQ.js} +42 -0
- package/dist/{chunk-3BGSDKWD.js → chunk-QWA26UNO.js} +7 -7
- package/dist/{lore-server-ILPHKWLK.js → chunk-RAB5IKPR.js} +77 -112
- package/dist/chunk-SOBTKFSP.js +616 -0
- package/dist/{chunk-BKMNLROM.js → chunk-ZDHLG5VP.js} +461 -147
- package/dist/{chunk-CTF6RHKG.js → chunk-ZGUAAVMA.js} +17 -2
- package/dist/{chunk-PFLWLC6J.js → chunk-ZMQA6SCO.js} +855 -34
- package/dist/{chunk-3BAMPB6I.js → chunk-ZSYVKSY6.js} +2 -147
- package/dist/{commands-KPT2T2OZ.js → commands-5N4ILTPH.js} +465 -1
- package/dist/config-schema-3YNIFJCJ.js +152 -0
- package/dist/{constellation-LZ6XIKDT.js → constellation-FAGT45TU.js} +2 -2
- package/dist/{context-audit-RI4R2WRH.js → context-audit-557EO6PK.js} +138 -8
- package/dist/{cost-4SZM7OUS.js → cost-UD3WPEKZ.js} +1 -1
- package/dist/{delete-YTASL4SM.js → delete-RRK4RL6Y.js} +1 -1
- package/dist/{diff-T6YJSAAC.js → diff-IP5CIARP.js} +6 -6
- package/dist/{dist-AG5JNIZU-HW2FWNTZ.js → dist-5QE2BB2B-X6DYVSUL.js} +59 -5
- package/dist/{dist-IKBGY7FQ.js → dist-CM3MVWWW.js} +3 -1
- package/dist/{dist-OH4DBV2O.js → dist-OGTSAZ55.js} +16 -1
- package/dist/{dist-RMAIFRTW.js → dist-POMVY6WP.js} +5 -3
- package/dist/{dist-QSBAGCZT.js → dist-UXWV4OKX.js} +2 -2
- package/dist/{doctor-INBOLZC7.js → doctor-GKZJU7QG.js} +1 -1
- package/dist/{edit-S7NZD7H7.js → edit-4CLNN5JG.js} +1 -1
- package/dist/{graph-ERNQQQ7C.js → graph-YYUXI3F7.js} +1 -1
- package/dist/graph-server-ZPXRSGCW.js +116 -0
- package/dist/{habits-7BORPC2F.js → habits-RG5SVKXP.js} +2 -2
- package/dist/index.js +200 -86
- package/dist/integrity-MK2OP5TA.js +194 -0
- package/dist/integrity-checker-J7YXRTBT.js +11 -0
- package/dist/{lint-MTRZB5EC.js → lint-HYWGS3JJ.js} +1 -1
- package/dist/{list-QTFWN35D.js → list-BTLFHSRC.js} +1 -1
- package/dist/list-IUCYPGMK.js +57 -0
- package/dist/{lore-loader-S5BXMH27.js → lore-loader-VTEEZDX3.js} +3 -1
- package/dist/lore-server-NOOAHKJX.js +118 -0
- package/dist/mcp.js +2591 -112
- package/dist/{migrate-HRN5TUBQ.js → migrate-FQVGQNXZ.js} +21 -3
- package/dist/{migrate-assessments-FPR6C35Z.js → migrate-assessments-JP6Q5KME.js} +1 -1
- package/dist/{orchestrate-3SI6ON33.js → orchestrate-A226N6FC.js} +6 -6
- package/dist/platform-server-KHL6ZPPN.js +900 -0
- package/dist/{probe-ABMGCXQG.js → probe-7JK7IDNI.js} +4 -4
- package/dist/{providers-YW3FG6DA.js → providers-YNFSL6HK.js} +1 -1
- package/dist/quiz-I75NU2QQ.js +99 -0
- package/dist/{record-UGN75GTB.js → record-46CLR4OG.js} +11 -2
- package/dist/{reindex-YC7LD4MN.js → reindex-WIJMCJ4A.js} +3 -2
- package/dist/{remember-WR6ZVXLT.js → remember-4EUZKIIB.js} +1 -1
- package/dist/{retag-URLJLMSK.js → retag-KC4JVRLE.js} +1 -1
- package/dist/{review-725ZKA7U.js → review-Q7M4CRB5.js} +1 -1
- package/dist/{ripple-QTXKJCEI.js → ripple-RI3LOT6R.js} +2 -2
- package/dist/{sentinel-FUR3QKCJ.js → sentinel-UOIGJWHH.js} +1 -1
- package/dist/sentinel-bridge-APDXYAZS.js +109 -0
- package/dist/sentinel-mcp.js +13 -0
- package/dist/sentinel-ui/assets/{index-Zh1YM0C9.css → index-CJ1Wx083.css} +1 -1
- package/dist/sentinel-ui/assets/index-S1VJ67dT.js +62 -0
- package/dist/sentinel-ui/assets/index-S1VJ67dT.js.map +1 -0
- package/dist/sentinel-ui/index.html +2 -2
- package/dist/sentinel.js +6 -6
- package/dist/{serve-DIALBCTU.js → serve-22A4XOIG.js} +1 -1
- package/dist/{university-A66BMZ4Z.js → serve-2YJ6D2Y6.js} +9 -8
- package/dist/serve-JVXSRSUB.js +33 -0
- package/dist/{server-2VICPDUR.js → server-JV6UFGWZ.js} +25 -2
- package/dist/{server-OWBK2WFS.js → server-RDLQ3DK7.js} +49 -4
- package/dist/{setup-ASR6OMKV.js → setup-M2ZKLKNN.js} +2 -2
- package/dist/{shift-7XLSBLDW.js → shift-LNMKFYLR.js} +63 -14
- package/dist/{show-GEVVQWWG.js → show-P7GYO43X.js} +1 -1
- package/dist/show-PKZMYKRN.js +82 -0
- package/dist/{snapshot-QZFD7YBI.js → snapshot-Y3COXK4T.js} +2 -2
- package/dist/{spawn-DIY7T4QW.js → spawn-SSXZX45U.js} +2 -2
- package/dist/status-KLHALGW4.js +71 -0
- package/dist/{summary-R4CSYNNP.js → summary-5NQNOD3F.js} +2 -2
- package/dist/{sweep-5POCF2E4.js → sweep-EZU3GU6S.js} +1 -1
- package/dist/symphony-EYRGGVNE.js +470 -0
- package/dist/symphony-QWOEKZMC.js +308 -0
- package/dist/{team-VH3HYABB.js → team-HGLJXWQG.js} +7 -7
- package/dist/{timeline-RKXNRMKF.js → timeline-ANC7LVDL.js} +1 -1
- package/dist/{triage-GJ6GK647.js → triage-IZ4MDYNB.js} +2 -2
- package/dist/university-content/courses/.purpose +7 -1
- package/dist/university-content/courses/para-501.json +166 -0
- package/dist/university-content/plsat/.purpose +6 -0
- package/dist/university-content/plsat/v3.0.json +323 -1
- package/dist/university-content/reference.json +48 -0
- package/dist/university-ui/assets/{index-TcsCEBMo.js → index-tfi5xN4Q.js} +2 -2
- package/dist/university-ui/assets/{index-TcsCEBMo.js.map → index-tfi5xN4Q.js.map} +1 -1
- package/dist/university-ui/index.html +1 -1
- package/dist/validate-GD5XWILV.js +134 -0
- package/dist/{validate-OUHUBZPO.js → validate-ZVPNN4FL.js} +1 -1
- package/dist/{workspace-5RBSALXC.js → workspace-UIUTHZTD.js} +5 -5
- package/package.json +4 -2
- package/platform-ui/dist/assets/GitSection-BD3Ze06e.js +4 -0
- package/platform-ui/dist/assets/GitSection-C-GQWHcu.css +1 -0
- package/platform-ui/dist/assets/GraphSection-BlgXTl53.css +1 -0
- package/platform-ui/dist/assets/GraphSection-SglITfSs.js +8 -0
- package/platform-ui/dist/assets/LoreSection-C3EixkjW.css +1 -0
- package/platform-ui/dist/assets/LoreSection-bR5Km4Fd.js +1 -0
- package/platform-ui/dist/assets/SentinelSection-BI-aIYKL.css +1 -0
- package/platform-ui/dist/assets/SentinelSection-QSpAZArG.js +1 -0
- package/platform-ui/dist/assets/SymphonySection-CobYJgvg.js +1 -0
- package/platform-ui/dist/assets/SymphonySection-zY0C5tFl.css +1 -0
- package/platform-ui/dist/assets/index-CfpZFjea.css +1 -0
- package/platform-ui/dist/assets/index-DbxeSMkV.js +57 -0
- package/platform-ui/dist/index.html +14 -0
- package/dist/graph-server-BZ73HTAT.js +0 -251
- package/dist/sentinel-ui/assets/index-C_Wstm64.js +0 -62
- package/dist/sentinel-ui/assets/index-C_Wstm64.js.map +0 -1
- /package/dist/{chunk-VUSCJJ4A.js → chunk-EDOAWN7J.js} +0 -0
- /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-
|
|
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
|
|
9
|
-
import * as
|
|
10
|
-
import * as
|
|
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
|
|
2037
|
-
const firstPart =
|
|
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-
|
|
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,
|
|
4176
|
-
const parts =
|
|
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-
|
|
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 ||
|
|
4768
|
-
const paradigmDir =
|
|
4769
|
-
if (!
|
|
4770
|
-
|
|
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 =
|
|
4781
|
-
|
|
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 =
|
|
4785
|
-
|
|
5534
|
+
const navigatorPath = path10.join(paradigmDir, "navigator.yaml");
|
|
5535
|
+
fs9.writeFileSync(
|
|
4786
5536
|
navigatorPath,
|
|
4787
|
-
|
|
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 =
|
|
4795
|
-
|
|
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) =>
|
|
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) =>
|
|
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 =
|
|
4908
|
-
if (
|
|
5714
|
+
const gitignorePath = path10.join(rootDir, ".gitignore");
|
|
5715
|
+
if (fs9.existsSync(gitignorePath)) {
|
|
4909
5716
|
try {
|
|
4910
|
-
const content =
|
|
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 =
|
|
5766
|
+
const dir = path10.dirname(pf);
|
|
4960
5767
|
return dir.toLowerCase().includes(symbol.id.toLowerCase());
|
|
4961
5768
|
});
|
|
4962
5769
|
if (matchingPurpose) {
|
|
4963
|
-
symbolMap[symbolId] =
|
|
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 =
|
|
4975
|
-
const data =
|
|
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:
|
|
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:
|
|
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
|