@a-company/paradigm 3.28.0 → 3.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accept-orchestration-6EM5EHXA.js → accept-orchestration-ZUWQUHSK.js} +6 -6
- package/dist/add-VSPZ6FM4.js +81 -0
- package/dist/{aggregate-M5WMUI6B.js → aggregate-SV3VGEIL.js} +2 -2
- package/dist/assess-UHBDYIK7.js +68 -0
- package/dist/{beacon-XL2ALH5O.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-AK5M6KJB.js → chunk-36TKPM5Z.js} +20 -2
- package/dist/{chunk-W4VFKZVF.js → chunk-7COU5S2Z.js} +3 -3
- package/dist/{chunk-3BAMPB6I.js → chunk-7WEKMZ46.js} +2 -147
- package/dist/{chunk-SCC77UUP.js → chunk-AKIMFN6I.js} +3 -3
- package/dist/{chunk-3DYYXGDC.js → chunk-CDMAMDSG.js} +33 -0
- package/dist/{chunk-7IJ5JVKT.js → chunk-CZEIK3Y2.js} +913 -40
- package/dist/{chunk-MRENOFTR.js → chunk-EDOAWN7J.js} +6 -1
- package/dist/chunk-F3BCHPYT.js +143 -0
- package/dist/chunk-GT5QGC2H.js +253 -0
- package/dist/{chunk-N6RNYCZD.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-KWDTBXP2.js → chunk-LHLIAYQ3.js} +1 -1
- package/dist/{chunk-OXG5GVDJ.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-ZOH24ZPF.js → chunk-QWA26UNO.js} +7 -7
- package/dist/{lore-server-ILPHKWLK.js → chunk-RAB5IKPR.js} +77 -112
- package/dist/{chunk-BKMNLROM.js → chunk-RGFANZ4Q.js} +448 -147
- package/dist/{chunk-R2SGQ22F.js → chunk-YW5OCVKB.js} +448 -2
- package/dist/{chunk-6P4IFIK2.js → chunk-ZGUAAVMA.js} +53 -4
- package/dist/{commands-6ZVTD74M.js → commands-LEPFD7S5.js} +452 -1
- package/dist/config-schema-3YNIFJCJ.js +152 -0
- package/dist/{constellation-NXU6Q2HM.js → constellation-FAGT45TU.js} +2 -2
- package/dist/{context-audit-RI4R2WRH.js → context-audit-557EO6PK.js} +138 -8
- package/dist/{cost-CTGSLSOC.js → cost-UD3WPEKZ.js} +1 -1
- package/dist/{cursorrules-XBWFX66V.js → cursorrules-3TKZ4E4R.js} +2 -2
- package/dist/{delete-YTASL4SM.js → delete-RRK4RL6Y.js} +1 -1
- package/dist/{diff-AH7L4PRQ.js → diff-IP5CIARP.js} +6 -6
- package/dist/{dist-AG5JNIZU-HW2FWNTZ.js → dist-5QE2BB2B-X6DYVSUL.js} +59 -5
- package/dist/{dist-KY5HGDDL.js → dist-OGTSAZ55.js} +58 -4
- package/dist/{dist-IKBGY7FQ.js → dist-RVKYUCRU.js} +3 -1
- package/dist/{dist-7U64HDSC.js → dist-UXWV4OKX.js} +8 -2
- package/dist/{dist-RMAIFRTW.js → dist-Y7I3CFY5.js} +5 -3
- 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-O37HTUKE.js} +2 -2
- package/dist/index.js +207 -89
- package/dist/integrity-MK2OP5TA.js +194 -0
- package/dist/integrity-checker-J7YXRTBT.js +11 -0
- package/dist/{lint-53GPXKKI.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 +2616 -112
- package/dist/migrate-FQVGQNXZ.js +889 -0
- package/dist/{migrate-assessments-FPR6C35Z.js → migrate-assessments-JP6Q5KME.js} +1 -1
- package/dist/{orchestrate-HMSQ2CED.js → orchestrate-A226N6FC.js} +6 -6
- package/dist/platform-server-KK4OCRTV.js +891 -0
- package/dist/{probe-SN4BNXOC.js → probe-7JK7IDNI.js} +5 -5
- 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-YG3KIXAK.js → reindex-NZQRGKPN.js} +3 -2
- package/dist/{remember-IEBQHXHZ.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-DFMXLFWI.js → ripple-RI3LOT6R.js} +2 -2
- package/dist/{sentinel-FUR3QKCJ.js → sentinel-BKYTBT7M.js} +1 -1
- package/dist/sentinel-bridge-IZTXYS5M.js +109 -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-3V2WXLGM.js +33 -0
- package/dist/{server-2VICPDUR.js → server-OFEJ2HJP.js} +25 -2
- package/dist/{server-OWBK2WFS.js → server-RDLQ3DK7.js} +49 -4
- package/dist/{setup-HOI52TN3.js → setup-M2ZKLKNN.js} +4 -4
- package/dist/{shift-DRF5M3G6.js → shift-LNMKFYLR.js} +73 -14
- package/dist/{show-GEVVQWWG.js → show-P7GYO43X.js} +1 -1
- package/dist/show-PKZMYKRN.js +82 -0
- package/dist/{snapshot-XHINQBZS.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-NV7SBV5O.js → summary-5NQNOD3F.js} +2 -2
- package/dist/{sweep-5POCF2E4.js → sweep-EZU3GU6S.js} +1 -1
- package/dist/symphony-ROEKK7VD.js +999 -0
- package/dist/{team-YOGT2Q2X.js → team-HGLJXWQG.js} +7 -7
- package/dist/{timeline-RKXNRMKF.js → timeline-ANC7LVDL.js} +1 -1
- package/dist/{triage-GJ6GK647.js → triage-POXJ2TIX.js} +2 -2
- package/dist/university-content/courses/.purpose +7 -1
- package/dist/university-content/courses/para-101.json +53 -0
- 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 +400 -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/{upgrade-65QOQXRC.js → upgrade-ANX3LVSA.js} +1 -0
- package/dist/validate-GD5XWILV.js +134 -0
- package/dist/{validate-TKKRGJKC.js → validate-ZVPNN4FL.js} +1 -1
- package/dist/{workspace-L27RR5MF.js → workspace-UIUTHZTD.js} +6 -6
- package/package.json +4 -2
- package/platform-ui/dist/assets/GitSection-C-GQWHcu.css +1 -0
- package/platform-ui/dist/assets/GitSection-DvyJBF_-.js +4 -0
- package/platform-ui/dist/assets/GraphSection-BiQrXqfs.js +8 -0
- package/platform-ui/dist/assets/GraphSection-BlgXTl53.css +1 -0
- package/platform-ui/dist/assets/LoreSection-BaH1FaRb.js +1 -0
- package/platform-ui/dist/assets/LoreSection-C3EixkjW.css +1 -0
- package/platform-ui/dist/assets/SentinelSection-BI-aIYKL.css +1 -0
- package/platform-ui/dist/assets/SentinelSection-DemAznjI.js +1 -0
- package/platform-ui/dist/assets/index-CfpZFjea.css +1 -0
- package/platform-ui/dist/assets/index-DDKhCt-w.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-5SXMV4SP.js → chunk-FS3WTUHY.js} +0 -0
|
@@ -8,6 +8,12 @@ import * as path from "path";
|
|
|
8
8
|
import { glob } from "glob";
|
|
9
9
|
var PurposeItemSchema = z.object({
|
|
10
10
|
description: z.string(),
|
|
11
|
+
// Component type and hierarchy (v4)
|
|
12
|
+
type: z.string().optional(),
|
|
13
|
+
parent: z.string().optional(),
|
|
14
|
+
anchors: z.array(z.string()).optional(),
|
|
15
|
+
tags: z.array(z.string()).optional(),
|
|
16
|
+
// Standard fields
|
|
11
17
|
endpoints: z.array(z.string()).optional(),
|
|
12
18
|
tests: z.array(z.string()).optional(),
|
|
13
19
|
rules: z.record(z.unknown()).optional(),
|
|
@@ -19,7 +25,6 @@ var PurposeItemSchema = z.object({
|
|
|
19
25
|
states: z.array(z.string()).optional(),
|
|
20
26
|
components: z.array(z.string()).optional(),
|
|
21
27
|
// Extra fields preserved
|
|
22
|
-
tags: z.array(z.string()).optional(),
|
|
23
28
|
location: z.string().optional(),
|
|
24
29
|
locations: z.array(z.string()).optional(),
|
|
25
30
|
uses: z.array(z.string()).optional(),
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/graph-server/routes/symbols.ts
|
|
4
|
+
import { Router } from "express";
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
function createSymbolsRouter(projectDir) {
|
|
8
|
+
const router = Router();
|
|
9
|
+
router.get("/", (_req, res) => {
|
|
10
|
+
const indexPath = path.join(projectDir, ".paradigm", "scan-index.json");
|
|
11
|
+
if (!fs.existsSync(indexPath)) {
|
|
12
|
+
res.status(404).json({
|
|
13
|
+
error: "scan-index.json not found",
|
|
14
|
+
hint: "Run `paradigm scan` to generate the symbol index."
|
|
15
|
+
});
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const raw = fs.readFileSync(indexPath, "utf-8");
|
|
20
|
+
const index = JSON.parse(raw);
|
|
21
|
+
const categoryMap = {
|
|
22
|
+
components: "component",
|
|
23
|
+
features: "component",
|
|
24
|
+
flows: "flow",
|
|
25
|
+
state: "component",
|
|
26
|
+
gates: "gate",
|
|
27
|
+
signals: "signal",
|
|
28
|
+
aspects: "aspect",
|
|
29
|
+
screens: "component"
|
|
30
|
+
};
|
|
31
|
+
const prefixMap = {
|
|
32
|
+
component: "#",
|
|
33
|
+
flow: "$",
|
|
34
|
+
gate: "^",
|
|
35
|
+
signal: "!",
|
|
36
|
+
aspect: "~"
|
|
37
|
+
};
|
|
38
|
+
const symbols = [];
|
|
39
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40
|
+
for (const [sectionKey, category] of Object.entries(categoryMap)) {
|
|
41
|
+
const entries = index[sectionKey];
|
|
42
|
+
if (!entries || typeof entries !== "object") continue;
|
|
43
|
+
const items = Array.isArray(entries) ? entries : Object.values(entries);
|
|
44
|
+
for (const entry of items) {
|
|
45
|
+
const name = entry.name || entry.id || "";
|
|
46
|
+
if (!name || seen.has(name)) continue;
|
|
47
|
+
seen.add(name);
|
|
48
|
+
symbols.push({
|
|
49
|
+
id: name,
|
|
50
|
+
name,
|
|
51
|
+
category,
|
|
52
|
+
prefix: prefixMap[category] || "#",
|
|
53
|
+
description: entry.description || "",
|
|
54
|
+
path: entry.path || entry.file || "",
|
|
55
|
+
tags: entry.tags || [],
|
|
56
|
+
related: entry.related || []
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
res.json({
|
|
61
|
+
symbols,
|
|
62
|
+
meta: {
|
|
63
|
+
total: symbols.length,
|
|
64
|
+
projectDir,
|
|
65
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} catch (err) {
|
|
69
|
+
res.status(500).json({
|
|
70
|
+
error: "Failed to read scan-index.json",
|
|
71
|
+
details: err.message
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return router;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/graph-server/routes/graphs.ts
|
|
79
|
+
import { Router as Router2 } from "express";
|
|
80
|
+
import * as fs2 from "fs";
|
|
81
|
+
import * as path2 from "path";
|
|
82
|
+
var GRAPHS_DIR = ".paradigm/graphs";
|
|
83
|
+
function createGraphsRouter(projectDir) {
|
|
84
|
+
const router = Router2();
|
|
85
|
+
const graphsPath = path2.join(projectDir, GRAPHS_DIR);
|
|
86
|
+
router.get("/", (_req, res) => {
|
|
87
|
+
if (!fs2.existsSync(graphsPath)) {
|
|
88
|
+
res.json({ graphs: [] });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const files = fs2.readdirSync(graphsPath).filter((f) => f.endsWith(".graph.json"));
|
|
93
|
+
const graphs = files.map((file) => {
|
|
94
|
+
const filePath = path2.join(graphsPath, file);
|
|
95
|
+
const stat = fs2.statSync(filePath);
|
|
96
|
+
const slug = file.replace(/\.graph\.json$/, "");
|
|
97
|
+
let name = slug;
|
|
98
|
+
let nodeCount = 0;
|
|
99
|
+
let edgeCount = 0;
|
|
100
|
+
try {
|
|
101
|
+
const raw = JSON.parse(fs2.readFileSync(filePath, "utf8"));
|
|
102
|
+
name = raw.name || slug;
|
|
103
|
+
nodeCount = Array.isArray(raw.nodes) ? raw.nodes.length : 0;
|
|
104
|
+
edgeCount = Array.isArray(raw.edges) ? raw.edges.length : 0;
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
slug,
|
|
109
|
+
file,
|
|
110
|
+
name,
|
|
111
|
+
nodes: nodeCount,
|
|
112
|
+
edges: edgeCount,
|
|
113
|
+
size: stat.size,
|
|
114
|
+
modified: stat.mtime.toISOString()
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
graphs.sort((a, b) => b.modified.localeCompare(a.modified));
|
|
118
|
+
res.json({ graphs });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
res.status(500).json({ error: "Failed to list graphs", details: err.message });
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
router.get("/:slug", (req, res) => {
|
|
124
|
+
const slug = req.params.slug;
|
|
125
|
+
const filePath = path2.join(graphsPath, `${slug}.graph.json`);
|
|
126
|
+
if (!fs2.existsSync(filePath)) {
|
|
127
|
+
res.status(404).json({ error: `Graph "${slug}" not found` });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
132
|
+
res.type("application/json").send(raw);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
res.status(500).json({ error: "Failed to read graph", details: err.message });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return router;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
createSymbolsRouter,
|
|
142
|
+
createGraphsRouter
|
|
143
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/core/university/storage.ts
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import * as yaml from "js-yaml";
|
|
7
|
+
var UNIVERSITY_DIR = ".paradigm/university";
|
|
8
|
+
var CONTENT_DIR = "content";
|
|
9
|
+
function parseFrontmatter(content) {
|
|
10
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
11
|
+
if (!match) return null;
|
|
12
|
+
try {
|
|
13
|
+
const frontmatter = yaml.load(match[1]);
|
|
14
|
+
return { frontmatter, body: match[2].trim() };
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function serializeFrontmatter(frontmatter, body) {
|
|
20
|
+
const fm = yaml.dump(frontmatter, { lineWidth: -1, noRefs: true, sortKeys: false });
|
|
21
|
+
return `---
|
|
22
|
+
${fm}---
|
|
23
|
+
|
|
24
|
+
${body}
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
function loadUniversityIndex(rootDir) {
|
|
28
|
+
const indexPath = path.join(rootDir, UNIVERSITY_DIR, "index.yaml");
|
|
29
|
+
if (!fs.existsSync(indexPath)) return null;
|
|
30
|
+
try {
|
|
31
|
+
return yaml.load(fs.readFileSync(indexPath, "utf8"));
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function resolveFile(rootDir, id, ext) {
|
|
37
|
+
const base = path.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR);
|
|
38
|
+
for (const subdir of ["notes", "policies", "quizzes", "paths"]) {
|
|
39
|
+
const fp = path.join(base, subdir, `${id}${ext}`);
|
|
40
|
+
if (fs.existsSync(fp)) return fp;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function loadNote(rootDir, id) {
|
|
45
|
+
const fp = resolveFile(rootDir, id, ".md");
|
|
46
|
+
if (!fp) return null;
|
|
47
|
+
try {
|
|
48
|
+
const parsed = parseFrontmatter(fs.readFileSync(fp, "utf8"));
|
|
49
|
+
if (!parsed) return null;
|
|
50
|
+
const fm = parsed.frontmatter;
|
|
51
|
+
return {
|
|
52
|
+
frontmatter: {
|
|
53
|
+
...fm,
|
|
54
|
+
tags: fm.tags || [],
|
|
55
|
+
symbols: fm.symbols || [],
|
|
56
|
+
prerequisites: fm.prerequisites || []
|
|
57
|
+
},
|
|
58
|
+
body: parsed.body
|
|
59
|
+
};
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function loadQuiz(rootDir, id) {
|
|
65
|
+
const fp = resolveFile(rootDir, id, ".yaml");
|
|
66
|
+
if (!fp) return null;
|
|
67
|
+
try {
|
|
68
|
+
const data = yaml.load(fs.readFileSync(fp, "utf8"));
|
|
69
|
+
if (!data?.id) return null;
|
|
70
|
+
return { ...data, tags: data.tags || [], symbols: data.symbols || [], questions: data.questions || [] };
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function loadPath(rootDir, id) {
|
|
76
|
+
const fp = resolveFile(rootDir, id, ".yaml");
|
|
77
|
+
if (!fp) return null;
|
|
78
|
+
try {
|
|
79
|
+
const data = yaml.load(fs.readFileSync(fp, "utf8"));
|
|
80
|
+
if (!data?.id) return null;
|
|
81
|
+
return data;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function loadDiplomas(rootDir, filter) {
|
|
87
|
+
const dir = path.join(rootDir, UNIVERSITY_DIR, "diplomas");
|
|
88
|
+
if (!fs.existsSync(dir)) return [];
|
|
89
|
+
const results = [];
|
|
90
|
+
try {
|
|
91
|
+
for (const file of fs.readdirSync(dir).filter((f) => f.endsWith(".yaml"))) {
|
|
92
|
+
try {
|
|
93
|
+
const d = yaml.load(fs.readFileSync(path.join(dir, file), "utf8"));
|
|
94
|
+
if (!d?.id) continue;
|
|
95
|
+
if (filter?.student && d.student !== filter.student) continue;
|
|
96
|
+
if (filter?.type && d.type !== filter.type) continue;
|
|
97
|
+
results.push(d);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
}
|
|
103
|
+
return results.sort((a, b) => b.earnedAt.localeCompare(a.earnedAt));
|
|
104
|
+
}
|
|
105
|
+
function saveNote(rootDir, frontmatter, body) {
|
|
106
|
+
const subdir = frontmatter.type === "policy" ? "policies" : "notes";
|
|
107
|
+
const dir = path.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, subdir);
|
|
108
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
109
|
+
const fp = path.join(dir, `${frontmatter.id}.md`);
|
|
110
|
+
fs.writeFileSync(fp, serializeFrontmatter(frontmatter, body), "utf8");
|
|
111
|
+
return fp;
|
|
112
|
+
}
|
|
113
|
+
function saveQuiz(rootDir, quiz) {
|
|
114
|
+
const dir = path.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, "quizzes");
|
|
115
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
116
|
+
const fp = path.join(dir, `${quiz.id}.yaml`);
|
|
117
|
+
fs.writeFileSync(fp, yaml.dump(quiz, { lineWidth: -1, noRefs: true }), "utf8");
|
|
118
|
+
return fp;
|
|
119
|
+
}
|
|
120
|
+
function saveDiploma(rootDir, diploma) {
|
|
121
|
+
const dir = path.join(rootDir, UNIVERSITY_DIR, "diplomas");
|
|
122
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
123
|
+
const fp = path.join(dir, `${diploma.id}.yaml`);
|
|
124
|
+
fs.writeFileSync(fp, yaml.dump(diploma, { lineWidth: -1, noRefs: true }), "utf8");
|
|
125
|
+
return fp;
|
|
126
|
+
}
|
|
127
|
+
function rebuildUniversityIndex(rootDir) {
|
|
128
|
+
const uniDir = path.join(rootDir, UNIVERSITY_DIR);
|
|
129
|
+
const contentBase = path.join(uniDir, CONTENT_DIR);
|
|
130
|
+
const entries = [];
|
|
131
|
+
for (const subdir of ["notes", "policies"]) {
|
|
132
|
+
const dir = path.join(contentBase, subdir);
|
|
133
|
+
if (!fs.existsSync(dir)) continue;
|
|
134
|
+
try {
|
|
135
|
+
for (const file of fs.readdirSync(dir).filter((f) => f.endsWith(".md"))) {
|
|
136
|
+
try {
|
|
137
|
+
const raw = fs.readFileSync(path.join(dir, file), "utf8");
|
|
138
|
+
const parsed = parseFrontmatter(raw);
|
|
139
|
+
if (!parsed) continue;
|
|
140
|
+
const fm = parsed.frontmatter;
|
|
141
|
+
entries.push({
|
|
142
|
+
id: fm.id || file.replace(".md", ""),
|
|
143
|
+
title: fm.title || file,
|
|
144
|
+
type: fm.type || (subdir === "policies" ? "policy" : "note"),
|
|
145
|
+
author: fm.author || "unknown",
|
|
146
|
+
created: fm.created || "",
|
|
147
|
+
updated: fm.updated || "",
|
|
148
|
+
tags: Array.isArray(fm.tags) ? fm.tags : [],
|
|
149
|
+
symbols: Array.isArray(fm.symbols) ? fm.symbols : [],
|
|
150
|
+
difficulty: fm.difficulty || "beginner",
|
|
151
|
+
file: `${CONTENT_DIR}/${subdir}/${file}`
|
|
152
|
+
});
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const quizDir = path.join(contentBase, "quizzes");
|
|
160
|
+
if (fs.existsSync(quizDir)) {
|
|
161
|
+
try {
|
|
162
|
+
for (const file of fs.readdirSync(quizDir).filter((f) => f.endsWith(".yaml"))) {
|
|
163
|
+
try {
|
|
164
|
+
const data = yaml.load(fs.readFileSync(path.join(quizDir, file), "utf8"));
|
|
165
|
+
if (!data?.id) continue;
|
|
166
|
+
entries.push({
|
|
167
|
+
id: data.id,
|
|
168
|
+
title: data.title || file,
|
|
169
|
+
type: "quiz",
|
|
170
|
+
author: data.author || "unknown",
|
|
171
|
+
created: data.created || "",
|
|
172
|
+
updated: data.updated || "",
|
|
173
|
+
tags: data.tags || [],
|
|
174
|
+
symbols: data.symbols || [],
|
|
175
|
+
difficulty: data.difficulty || "beginner",
|
|
176
|
+
file: `${CONTENT_DIR}/quizzes/${file}`
|
|
177
|
+
});
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const pathDir = path.join(contentBase, "paths");
|
|
185
|
+
if (fs.existsSync(pathDir)) {
|
|
186
|
+
try {
|
|
187
|
+
for (const file of fs.readdirSync(pathDir).filter((f) => f.endsWith(".yaml"))) {
|
|
188
|
+
try {
|
|
189
|
+
const data = yaml.load(fs.readFileSync(path.join(pathDir, file), "utf8"));
|
|
190
|
+
if (!data?.id) continue;
|
|
191
|
+
entries.push({
|
|
192
|
+
id: data.id,
|
|
193
|
+
title: data.title || file,
|
|
194
|
+
type: "path",
|
|
195
|
+
author: data.author || "unknown",
|
|
196
|
+
created: data.created || "",
|
|
197
|
+
updated: data.updated || "",
|
|
198
|
+
tags: data.tags || [],
|
|
199
|
+
symbols: [],
|
|
200
|
+
file: `${CONTENT_DIR}/paths/${file}`
|
|
201
|
+
});
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
let diplomaCount = 0;
|
|
209
|
+
const diplomaDir = path.join(uniDir, "diplomas");
|
|
210
|
+
if (fs.existsSync(diplomaDir)) {
|
|
211
|
+
try {
|
|
212
|
+
diplomaCount = fs.readdirSync(diplomaDir).filter((f) => f.endsWith(".yaml")).length;
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const index = {
|
|
217
|
+
version: "1.0",
|
|
218
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
219
|
+
totalContent: entries.length,
|
|
220
|
+
entries,
|
|
221
|
+
diplomaCount
|
|
222
|
+
};
|
|
223
|
+
fs.mkdirSync(uniDir, { recursive: true });
|
|
224
|
+
fs.writeFileSync(path.join(uniDir, "index.yaml"), yaml.dump(index, { lineWidth: -1, noRefs: true }), "utf8");
|
|
225
|
+
return index;
|
|
226
|
+
}
|
|
227
|
+
function searchContent(rootDir, filter) {
|
|
228
|
+
const index = loadUniversityIndex(rootDir);
|
|
229
|
+
if (!index) return [];
|
|
230
|
+
let results = [...index.entries];
|
|
231
|
+
if (filter.type) results = results.filter((e) => e.type === filter.type);
|
|
232
|
+
if (filter.tag) results = results.filter((e) => e.tags.some((t) => t.startsWith(filter.tag)));
|
|
233
|
+
if (filter.difficulty) results = results.filter((e) => e.difficulty === filter.difficulty);
|
|
234
|
+
if (filter.symbol) results = results.filter((e) => e.symbols.includes(filter.symbol));
|
|
235
|
+
if (filter.query) {
|
|
236
|
+
const q = filter.query.toLowerCase();
|
|
237
|
+
results = results.filter((e) => e.title.toLowerCase().includes(q) || e.id.toLowerCase().includes(q));
|
|
238
|
+
}
|
|
239
|
+
return results.slice(0, filter.limit || 20);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export {
|
|
243
|
+
loadUniversityIndex,
|
|
244
|
+
loadNote,
|
|
245
|
+
loadQuiz,
|
|
246
|
+
loadPath,
|
|
247
|
+
loadDiplomas,
|
|
248
|
+
saveNote,
|
|
249
|
+
saveQuiz,
|
|
250
|
+
saveDiploma,
|
|
251
|
+
rebuildUniversityIndex,
|
|
252
|
+
searchContent
|
|
253
|
+
};
|