@a-company/paradigm 3.46.0 → 5.4.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-ZUWQUHSK.js → accept-orchestration-GX2YRWM4.js} +5 -5
- package/dist/{add-VSPZ6FM4.js → add-FZRKEGH4.js} +1 -1
- package/dist/agent-WERIO2XV.js +523 -0
- package/dist/agent-loader-SJPJJS33.js +36 -0
- package/dist/{agents-suggest-65SER5IS.js → agents-suggest-DNSYJ6IA.js} +1 -1
- package/dist/{aggregate-SV3VGEIL.js → aggregate-H57K7PNV.js} +1 -1
- package/dist/{assess-UHBDYIK7.js → assess-4WVXZLZQ.js} +2 -2
- package/dist/{auto-24ICVUH4.js → auto-QFS5NHQU.js} +1 -1
- package/dist/{beacon-3SJV4DAP.js → beacon-KXZXYQHX.js} +1 -1
- package/dist/{calibration-WWHK73WU.js → calibration-V46G7JTY.js} +2 -2
- package/dist/{check-OLI6AUS6.js → check-OWAIWV23.js} +1 -1
- package/dist/{chunk-RP6TZYGE.js → chunk-2IO7JAG2.js} +1 -1
- package/dist/chunk-2T6BTYBN.js +712 -0
- package/dist/{chunk-CDMAMDSG.js → chunk-5VKJBNJL.js} +13 -5
- package/dist/{chunk-KB4XJWE3.js → chunk-6N3JTACN.js} +98 -437
- package/dist/{chunk-ZMQA6SCO.js → chunk-7HRBT23N.js} +631 -231
- package/dist/chunk-7N7GSU6K.js +34 -0
- package/dist/chunk-A2L4TSLZ.js +526 -0
- package/dist/{chunk-P7XSBJE3.js → chunk-ABVQGRF7.js} +1 -1
- package/dist/{chunk-HIKKOCXY.js → chunk-EI32ZBE6.js} +1 -1
- package/dist/{chunk-QIOCFXDQ.js → chunk-EKGMAM62.js} +1 -1
- package/dist/chunk-EZ3GOCYC.js +132 -0
- package/dist/{chunk-DS5QY37M.js → chunk-GTR2TBIJ.js} +247 -15
- package/dist/chunk-ICSLIPUS.js +1128 -0
- package/dist/{chunk-QDXI2DHR.js → chunk-J2JEQRT3.js} +1 -1
- package/dist/{chunk-AKIMFN6I.js → chunk-JASGXLK3.js} +2 -2
- package/dist/{chunk-J4E6K5MG.js → chunk-LSRABQIY.js} +25 -1
- package/dist/chunk-MCMOGQMU.js +145 -0
- package/dist/{chunk-ZXMDA7VB.js → chunk-PDX44BCA.js} +1 -6
- package/dist/{chunk-2SKXFXIT.js → chunk-S3ORKP3V.js} +10 -15
- package/dist/chunk-TAIJOFOE.js +124 -0
- package/dist/{chunk-FS3WTUHY.js → chunk-TXESEO7Y.js} +6 -6
- package/dist/{chunk-7COU5S2Z.js → chunk-VL67H5IC.js} +1 -1
- package/dist/{chunk-QWA26UNO.js → chunk-WQITYKHM.js} +7 -7
- package/dist/{chunk-MW5DMGBB.js → chunk-YMDLDELF.js} +114 -55
- package/dist/{claude-63ISJAZK.js → claude-FRRWJSTJ.js} +1 -1
- package/dist/{claude-cli-ABML5RHX.js → claude-cli-XJLK2X4L.js} +1 -1
- package/dist/{claude-code-JRLMRPTO.js → claude-code-HTBA4XRB.js} +1 -1
- package/dist/{claude-code-teams-CAJBEFIZ.js → claude-code-teams-T4SP24MD.js} +1 -1
- package/dist/{conductor-HLWYWUVH.js → conductor-PGPDVIVE.js} +1 -1
- package/dist/{config-schema-3YNIFJCJ.js → config-schema-EA4XALGG.js} +4 -2
- package/dist/{constellation-FAGT45TU.js → constellation-A26CCGQS.js} +1 -1
- package/dist/{context-audit-557EO6PK.js → context-audit-RLO3ETRP.js} +8 -5
- package/dist/{cost-XEBADYFT.js → cost-BGM32XJU.js} +1 -1
- package/dist/{cost-UD3WPEKZ.js → cost-VI46A4XL.js} +1 -1
- package/dist/{cursor-cli-QUOOF2N4.js → cursor-cli-JVEZGHWQ.js} +1 -1
- package/dist/{cursorrules-3TKZ4E4R.js → cursorrules-HLIKJJZT.js} +1 -1
- package/dist/decision-loader-WWCLIQPJ.js +20 -0
- package/dist/{delete-RRK4RL6Y.js → delete-KBRPQLPC.js} +2 -2
- package/dist/{diff-IP5CIARP.js → diff-RQLLNAFI.js} +5 -5
- package/dist/{discipline-5F5OVTXB.js → discipline-FA4OZXIS.js} +1 -1
- package/dist/{dist-UXWV4OKX.js → dist-34NA5RS5.js} +1 -1
- package/dist/{dist-5QE2BB2B-X6DYVSUL.js → dist-5QE2BB2B-5S3T6Y3T.js} +1 -1
- package/dist/{dist-CM3MVWWW.js → dist-77JDTVAY.js} +1 -0
- package/dist/{dist-POMVY6WP.js → dist-QK4SQAK7.js} +1 -1
- package/dist/{dist-3RVKEJRT.js → dist-TA6LSC2Q.js} +1 -1
- package/dist/docs-J2BTKRVU.js +155 -0
- package/dist/docs-PBZB7LYP.js +89 -0
- package/dist/{doctor-GKZJU7QG.js → doctor-ULBOHEIC.js} +3 -3
- package/dist/{drift-YGT4LJ7Q.js → drift-R5NRKFHI.js} +1 -1
- package/dist/{echo-A6HD5UP7.js → echo-O2LY7CC2.js} +1 -1
- package/dist/{edit-4CLNN5JG.js → edit-R2HNLMOG.js} +2 -2
- package/dist/event-25OJKDCE.js +31 -0
- package/dist/{export-T7CMMJIB.js → export-IWVL7XLF.js} +1 -1
- package/dist/{flow-UFMPVOEM.js → flow-CRRVV3O3.js} +2 -2
- package/dist/{global-HHUJSBG5.js → global-3NG5JXUB.js} +1 -1
- package/dist/graduate-USAWGBJM.js +160 -0
- package/dist/{graph-YYUXI3F7.js → graph-VHUMAAS6.js} +2 -2
- package/dist/{graph-server-ZPXRSGCW.js → graph-server-YL22VBBN.js} +1 -1
- package/dist/{habits-RG5SVKXP.js → habits-OL5NGPXO.js} +3 -3
- package/dist/{history-CETCSUCP.js → history-WOWC573W.js} +1 -1
- package/dist/{hooks-TCUHQMPF.js → hooks-HFWSCGPV.js} +2 -2
- package/dist/index.js +302 -188
- package/dist/{integrity-MK2OP5TA.js → integrity-IHO4FZTS.js} +1 -1
- package/dist/{integrity-checker-J7YXRTBT.js → integrity-checker-PSKJA5SB.js} +1 -0
- package/dist/journal-loader-5EYSBFFY.js +18 -0
- package/dist/{lint-HYWGS3JJ.js → lint-K6CJGGPH.js} +1 -1
- package/dist/{list-IUCYPGMK.js → list-4YK7QKFF.js} +1 -1
- package/dist/{list-BTLFHSRC.js → list-ENR7Q4CR.js} +2 -2
- package/dist/{lore-loader-VTEEZDX3.js → lore-loader-7NO6N6FT.js} +4 -1
- package/dist/{lore-server-NOOAHKJX.js → lore-server-UNJY5KC3.js} +1 -1
- package/dist/{manual-AFJ2J2V3.js → manual-G6FISID5.js} +1 -1
- package/dist/mcp.js +3917 -356
- package/dist/{migrate-FQVGQNXZ.js → migrate-LS45DNEV.js} +2 -2
- package/dist/{migrate-assessments-JP6Q5KME.js → migrate-assessments-RGH4O6IX.js} +2 -2
- package/dist/nomination-engine-HDWMN4IO.js +42 -0
- package/dist/notebook-YWIYGEHV.js +155 -0
- package/dist/{orchestrate-A226N6FC.js → orchestrate-XZA33TJC.js} +5 -5
- package/dist/{peers-RFQCWVLV.js → peers-DEOUIZM6.js} +1 -1
- package/dist/persona-UHAHIVST.js +390 -0
- package/dist/{pipeline-3G2FRAKM.js → pipeline-L4HCSBGN.js} +1 -1
- package/dist/{platform-server-H7Y6Q7O4.js → platform-server-2D6S6YTK.js} +412 -18
- package/dist/{plugin-update-checker-HMRPGY5Z.js → plugin-update-checker-ELOEEQYS.js} +1 -0
- package/dist/{portal-check-FF5EKZE5.js → portal-check-NPYGII2D.js} +2 -2
- package/dist/{portal-compliance-VU4NIFEN.js → portal-compliance-J7DGAPFX.js} +2 -2
- package/dist/{probe-7JK7IDNI.js → probe-MHL5HQZ2.js} +3 -3
- package/dist/{promote-XO63XMAN.js → promote-F6ZYZZAL.js} +2 -2
- package/dist/{providers-YNFSL6HK.js → providers-GK7PB2OL.js} +2 -2
- package/dist/{quiz-I75NU2QQ.js → quiz-M66SC7F7.js} +1 -1
- package/dist/{record-46CLR4OG.js → record-RA4WR2BO.js} +2 -2
- package/dist/{reindex-WIJMCJ4A.js → reindex-65H4WULU.js} +3 -2
- package/dist/{remember-4EUZKIIB.js → remember-HBWJ655S.js} +1 -1
- package/dist/{retag-KC4JVRLE.js → retag-3OLCVDEQ.js} +2 -2
- package/dist/{review-Q7M4CRB5.js → review-27ATYTD2.js} +2 -2
- package/dist/review-57QMURZV.js +334 -0
- package/dist/{ripple-RI3LOT6R.js → ripple-JPBXP5I3.js} +1 -1
- package/dist/{sentinel-UOIGJWHH.js → sentinel-4XIG4STA.js} +2 -2
- package/dist/{sentinel-bridge-APDXYAZS.js → sentinel-bridge-MDUXTQRL.js} +2 -2
- package/dist/{serve-KKEHE44G.js → serve-EFVRS4GA.js} +2 -2
- package/dist/{serve-22A4XOIG.js → serve-INL7SNBK.js} +2 -2
- package/dist/{serve-2YJ6D2Y6.js → serve-KBSE36PL.js} +4 -4
- package/dist/{server-JV6UFGWZ.js → server-54SKYFFY.js} +2 -2
- package/dist/{server-RDLQ3DK7.js → server-XUOIO7E6.js} +1 -1
- package/dist/{setup-YNZJQLW7.js → setup-EDS27WUR.js} +1 -1
- package/dist/{setup-M2ZKLKNN.js → setup-KO5AFC4K.js} +2 -2
- package/dist/{shift-LNMKFYLR.js → shift-VFG23DLA.js} +16 -16
- package/dist/{show-P7GYO43X.js → show-5PV5KFJE.js} +2 -2
- package/dist/{show-PKZMYKRN.js → show-NQKYX6WQ.js} +1 -1
- package/dist/{snapshot-Y3COXK4T.js → snapshot-BK4RBPCG.js} +1 -1
- package/dist/{spawn-SSXZX45U.js → spawn-AW6GDECS.js} +3 -3
- package/dist/{status-KLHALGW4.js → status-WGIAQODY.js} +1 -1
- package/dist/{summary-5NQNOD3F.js → summary-NIRABMF5.js} +2 -2
- package/dist/{sweep-EZU3GU6S.js → sweep-QMHNSIY5.js} +2 -2
- package/dist/{switch-WYUMVNA5.js → switch-6EJPZDIA.js} +1 -1
- package/dist/{symphony-6K3HD7AW.js → symphony-4OCY36AI.js} +5 -5
- package/dist/{symphony-YCHBYN3E.js → symphony-B75X2MME.js} +2 -2
- package/dist/{symphony-peers-HSY3RI3S.js → symphony-peers-2ZQYLRNI.js} +1 -1
- package/dist/{symphony-peers-APOGJPF4.js → symphony-peers-OL7F6M5S.js} +1 -0
- package/dist/{symphony-relay-GTAJRCVF.js → symphony-relay-UJYUXN65.js} +28 -1
- package/dist/{sync-ZM4Q3R4U.js → sync-VEHUH4OA.js} +3 -3
- package/dist/{sync-llms-JIPP3XX4.js → sync-llms-YHCFIE6X.js} +2 -2
- package/dist/{task-loader-7M2FCBX6.js → task-loader-LDYWQSLM.js} +1 -0
- package/dist/{team-HGLJXWQG.js → team-7HG7XK5C.js} +6 -6
- package/dist/{test-WTR5Q33E.js → test-566CP5KC.js} +1 -1
- package/dist/{thread-3WM7KKID.js → thread-N754I4D5.js} +1 -1
- package/dist/{timeline-ANC7LVDL.js → timeline-M3CICQFE.js} +2 -2
- package/dist/{triage-IZ4MDYNB.js → triage-HHYGT3HY.js} +1 -1
- package/dist/{tutorial-GC6QL4US.js → tutorial-KD22SUNO.js} +1 -1
- package/dist/university-content/courses/.purpose +66 -0
- package/dist/university-content/courses/para-401.json +146 -0
- package/dist/university-content/courses/para-501.json +67 -0
- package/dist/university-content/courses/para-601.json +690 -0
- package/dist/university-content/plsat/.purpose +6 -0
- package/dist/university-content/plsat/v2.0.json +2 -2
- package/dist/university-content/plsat/v3.0.json +563 -3
- package/dist/university-content/reference.json +91 -0
- package/dist/university-ui/assets/{index-tfi5xN4Q.js → index-C6bH_6xu.js} +2 -2
- package/dist/university-ui/assets/{index-tfi5xN4Q.js.map → index-C6bH_6xu.js.map} +1 -1
- package/dist/university-ui/index.html +1 -1
- package/dist/{upgrade-ANX3LVSA.js → upgrade-H5PF32BW.js} +2 -2
- package/dist/{validate-GD5XWILV.js → validate-CNKEKO6A.js} +1 -1
- package/dist/{validate-ITC5D6QG.js → validate-MB5ULIHS.js} +1 -1
- package/dist/{validate-ZVPNN4FL.js → validate-QH3LADM6.js} +1 -1
- package/dist/{watch-X64UK7K4.js → watch-2TKP5PVL.js} +3 -3
- package/dist/{watch-ERBEJUJW.js → watch-ZF4ML6CD.js} +2 -2
- package/dist/{wisdom-L2WC7J62.js → wisdom-AATMGNFA.js} +1 -1
- package/dist/work-log-loader-5L45XNYZ.js +14 -0
- package/dist/{workspace-UIUTHZTD.js → workspace-6E6OSRNU.js} +4 -4
- package/package.json +1 -1
- package/platform-ui/dist/assets/DocsSection-ByAgPzWV.js +1 -0
- package/platform-ui/dist/assets/DocsSection-CjdO6R-u.css +1 -0
- package/platform-ui/dist/assets/{GitSection-BD3Ze06e.js → GitSection-BLovj9yT.js} +1 -1
- package/platform-ui/dist/assets/{GraphSection-SglITfSs.js → GraphSection-C5PCPUFl.js} +1 -1
- package/platform-ui/dist/assets/{LoreSection-bR5Km4Fd.js → LoreSection-BftejTla.js} +1 -1
- package/platform-ui/dist/assets/{SentinelSection-QSpAZArG.js → SentinelSection-CnYcasN7.js} +1 -1
- package/platform-ui/dist/assets/{SymphonySection-CobYJgvg.js → SymphonySection-BpmqCHeK.js} +1 -1
- package/platform-ui/dist/assets/{index-DbxeSMkV.js → index-G9JnWEs_.js} +10 -10
- package/platform-ui/dist/index.html +1 -1
- package/dist/dist-PSF5CP4I.js +0 -7294
|
@@ -1,17 +1,266 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
loadLoreEntries,
|
|
4
|
-
loadLoreEntry
|
|
5
|
-
} from "./chunk-CDMAMDSG.js";
|
|
6
2
|
import {
|
|
7
3
|
checkComponentAnchors,
|
|
8
4
|
checkIntegrity,
|
|
9
5
|
checkPurposeHealth
|
|
10
6
|
} from "./chunk-L27I3CPZ.js";
|
|
7
|
+
import {
|
|
8
|
+
init_lore_loader,
|
|
9
|
+
loadLoreEntries,
|
|
10
|
+
loadLoreEntry
|
|
11
|
+
} from "./chunk-5VKJBNJL.js";
|
|
12
|
+
import {
|
|
13
|
+
__esm,
|
|
14
|
+
__export,
|
|
15
|
+
__toCommonJS
|
|
16
|
+
} from "./chunk-7N7GSU6K.js";
|
|
17
|
+
|
|
18
|
+
// ../paradigm-mcp/src/utils/aspect-fingerprint.ts
|
|
19
|
+
var aspect_fingerprint_exports = {};
|
|
20
|
+
__export(aspect_fingerprint_exports, {
|
|
21
|
+
contentSearch: () => contentSearch,
|
|
22
|
+
detectFileRename: () => detectFileRename,
|
|
23
|
+
generateFingerprint: () => generateFingerprint,
|
|
24
|
+
levenshteinDistance: () => levenshteinDistance,
|
|
25
|
+
levenshteinSimilarity: () => levenshteinSimilarity,
|
|
26
|
+
searchSiblingFiles: () => searchSiblingFiles,
|
|
27
|
+
slidingWindowSearch: () => slidingWindowSearch
|
|
28
|
+
});
|
|
29
|
+
import * as fs5 from "fs";
|
|
30
|
+
import * as path6 from "path";
|
|
31
|
+
import * as crypto2 from "crypto";
|
|
32
|
+
import { execSync } from "child_process";
|
|
33
|
+
function generateFingerprint(content) {
|
|
34
|
+
const lines = content.split("\n").filter((l) => l.trim() !== "");
|
|
35
|
+
return {
|
|
36
|
+
firstLine: normalizeLine(lines[0] || ""),
|
|
37
|
+
lastLine: normalizeLine(lines[lines.length - 1] || ""),
|
|
38
|
+
lineCount: lines.length,
|
|
39
|
+
structuralHash: extractStructuralHash(lines)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function extractStructuralHash(lines) {
|
|
43
|
+
const structural = lines.map((l) => l.trim()).filter((l) => STRUCTURAL_TOKENS.test(l)).map((l) => {
|
|
44
|
+
const match = l.match(STRUCTURAL_TOKENS);
|
|
45
|
+
return match ? match[1].trim() : "";
|
|
46
|
+
}).join("|");
|
|
47
|
+
return crypto2.createHash("sha256").update(structural).digest("hex").slice(0, 16);
|
|
48
|
+
}
|
|
49
|
+
function normalizeLine(line) {
|
|
50
|
+
return line.trim().replace(/\s+/g, " ").toLowerCase();
|
|
51
|
+
}
|
|
52
|
+
function levenshteinDistance(a, b) {
|
|
53
|
+
if (a === b) return 0;
|
|
54
|
+
if (a.length === 0) return b.length;
|
|
55
|
+
if (b.length === 0) return a.length;
|
|
56
|
+
if (a.length > b.length) [a, b] = [b, a];
|
|
57
|
+
const aLen = a.length;
|
|
58
|
+
const bLen = b.length;
|
|
59
|
+
if (aLen > 5e3 || bLen > 5e3) {
|
|
60
|
+
return Math.abs(aLen - bLen);
|
|
61
|
+
}
|
|
62
|
+
let prev = new Array(aLen + 1);
|
|
63
|
+
let curr = new Array(aLen + 1);
|
|
64
|
+
for (let i = 0; i <= aLen; i++) prev[i] = i;
|
|
65
|
+
for (let j = 1; j <= bLen; j++) {
|
|
66
|
+
curr[0] = j;
|
|
67
|
+
for (let i = 1; i <= aLen; i++) {
|
|
68
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
69
|
+
curr[i] = Math.min(
|
|
70
|
+
prev[i] + 1,
|
|
71
|
+
// deletion
|
|
72
|
+
curr[i - 1] + 1,
|
|
73
|
+
// insertion
|
|
74
|
+
prev[i - 1] + cost
|
|
75
|
+
// substitution
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
[prev, curr] = [curr, prev];
|
|
79
|
+
}
|
|
80
|
+
return prev[aLen];
|
|
81
|
+
}
|
|
82
|
+
function levenshteinSimilarity(a, b) {
|
|
83
|
+
if (a.length === 0 && b.length === 0) return 1;
|
|
84
|
+
const maxLen = Math.max(a.length, b.length);
|
|
85
|
+
const distance = levenshteinDistance(a, b);
|
|
86
|
+
return 1 - distance / maxLen;
|
|
87
|
+
}
|
|
88
|
+
function slidingWindowSearch(fileLines, fingerprint, originalContent, maxResults = 3) {
|
|
89
|
+
const { lineCount } = fingerprint;
|
|
90
|
+
const minWindow = Math.max(1, Math.floor(lineCount * 0.8));
|
|
91
|
+
const maxWindow = Math.ceil(lineCount * 1.2);
|
|
92
|
+
const results = [];
|
|
93
|
+
const normalizedOriginal = normalizeBlock(originalContent);
|
|
94
|
+
for (const windowSize of [lineCount, minWindow, maxWindow]) {
|
|
95
|
+
if (windowSize > fileLines.length) continue;
|
|
96
|
+
for (let start = 0; start <= fileLines.length - windowSize; start++) {
|
|
97
|
+
const windowLines = fileLines.slice(start, start + windowSize);
|
|
98
|
+
const score = scoreWindow(windowLines, fingerprint, normalizedOriginal);
|
|
99
|
+
if (score >= 0.5) {
|
|
100
|
+
const windowContent = windowLines.join("\n");
|
|
101
|
+
results.push({
|
|
102
|
+
windowStart: start + 1,
|
|
103
|
+
// 1-indexed
|
|
104
|
+
windowEnd: start + windowSize,
|
|
105
|
+
similarity: levenshteinSimilarity(normalizeBlock(windowContent), normalizedOriginal),
|
|
106
|
+
score
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const byStart = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const r of results) {
|
|
113
|
+
const existing = byStart.get(r.windowStart);
|
|
114
|
+
if (!existing || r.score > existing.score) {
|
|
115
|
+
byStart.set(r.windowStart, r);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return Array.from(byStart.values()).sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
119
|
+
}
|
|
120
|
+
function scoreWindow(windowLines, fingerprint, normalizedOriginal) {
|
|
121
|
+
const nonEmpty = windowLines.filter((l) => l.trim() !== "");
|
|
122
|
+
if (nonEmpty.length === 0) return 0;
|
|
123
|
+
let score = 0;
|
|
124
|
+
const firstLine = normalizeLine(nonEmpty[0]);
|
|
125
|
+
const lastLine = normalizeLine(nonEmpty[nonEmpty.length - 1]);
|
|
126
|
+
let firstLastScore = 0;
|
|
127
|
+
if (firstLine === fingerprint.firstLine) firstLastScore += 0.5;
|
|
128
|
+
if (lastLine === fingerprint.lastLine) firstLastScore += 0.5;
|
|
129
|
+
score += firstLastScore * W_FIRST_LAST;
|
|
130
|
+
const windowStructural = extractStructuralHash(nonEmpty);
|
|
131
|
+
if (windowStructural === fingerprint.structuralHash) {
|
|
132
|
+
score += W_STRUCTURAL;
|
|
133
|
+
}
|
|
134
|
+
const windowContent = nonEmpty.join("\n");
|
|
135
|
+
const similarity = levenshteinSimilarity(normalizeBlock(windowContent), normalizedOriginal);
|
|
136
|
+
if (similarity >= 0.8) {
|
|
137
|
+
score += (similarity - 0.8) / 0.2 * W_LEVENSHTEIN;
|
|
138
|
+
}
|
|
139
|
+
const countRatio = nonEmpty.length / fingerprint.lineCount;
|
|
140
|
+
if (countRatio >= 0.8 && countRatio <= 1.2) {
|
|
141
|
+
const countScore = 1 - Math.abs(1 - countRatio) / 0.2;
|
|
142
|
+
score += countScore * W_LINE_COUNT;
|
|
143
|
+
}
|
|
144
|
+
return score;
|
|
145
|
+
}
|
|
146
|
+
function normalizeBlock(content) {
|
|
147
|
+
return content.split("\n").map((l) => l.trim()).filter((l) => l !== "").join("\n");
|
|
148
|
+
}
|
|
149
|
+
function detectFileRename(rootDir, oldPath) {
|
|
150
|
+
try {
|
|
151
|
+
const result = execSync(
|
|
152
|
+
`git log --follow --diff-filter=R --name-status --format="" -- "${oldPath}"`,
|
|
153
|
+
{ cwd: rootDir, encoding: "utf8", timeout: 5e3 }
|
|
154
|
+
).trim();
|
|
155
|
+
if (!result) return null;
|
|
156
|
+
const lines = result.split("\n");
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const parts = line.split(" ");
|
|
159
|
+
if (parts.length >= 3 && parts[0].startsWith("R")) {
|
|
160
|
+
return parts[2];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function searchSiblingFiles(rootDir, dirPath, fingerprint, originalContent, maxFiles = 10) {
|
|
169
|
+
const absoluteDir = path6.isAbsolute(dirPath) ? dirPath : path6.join(rootDir, dirPath);
|
|
170
|
+
if (!fs5.existsSync(absoluteDir)) return [];
|
|
171
|
+
const results = [];
|
|
172
|
+
try {
|
|
173
|
+
const files = fs5.readdirSync(absoluteDir).filter((f) => !f.startsWith(".") && fs5.statSync(path6.join(absoluteDir, f)).isFile()).slice(0, maxFiles);
|
|
174
|
+
for (const file of files) {
|
|
175
|
+
try {
|
|
176
|
+
const content = fs5.readFileSync(path6.join(absoluteDir, file), "utf8");
|
|
177
|
+
const lines = content.split("\n");
|
|
178
|
+
const matches = slidingWindowSearch(lines, fingerprint, originalContent, 1);
|
|
179
|
+
if (matches.length > 0 && matches[0].score >= 0.7) {
|
|
180
|
+
const relPath = path6.relative(rootDir, path6.join(absoluteDir, file));
|
|
181
|
+
results.push({
|
|
182
|
+
file: relPath,
|
|
183
|
+
score: matches[0].score,
|
|
184
|
+
start: matches[0].windowStart,
|
|
185
|
+
end: matches[0].windowEnd
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
return results.sort((a, b) => b.score - a.score);
|
|
195
|
+
}
|
|
196
|
+
function contentSearch(rootDir, filePath, originalContent, autoHeal = true) {
|
|
197
|
+
const fingerprint = generateFingerprint(originalContent);
|
|
198
|
+
const absolutePath = path6.isAbsolute(filePath) ? filePath : path6.join(rootDir, filePath);
|
|
199
|
+
if (fs5.existsSync(absolutePath)) {
|
|
200
|
+
const fileContent = fs5.readFileSync(absolutePath, "utf8");
|
|
201
|
+
const fileLines = fileContent.split("\n");
|
|
202
|
+
const matches = slidingWindowSearch(fileLines, fingerprint, originalContent);
|
|
203
|
+
if (matches.length > 0) {
|
|
204
|
+
const best = matches[0];
|
|
205
|
+
return {
|
|
206
|
+
found: best.score >= 0.7,
|
|
207
|
+
score: best.score,
|
|
208
|
+
suggestedStart: best.windowStart,
|
|
209
|
+
suggestedEnd: best.windowEnd,
|
|
210
|
+
similarity: best.similarity
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const renamedTo = detectFileRename(rootDir, filePath);
|
|
215
|
+
if (renamedTo) {
|
|
216
|
+
const renamedPath = path6.join(rootDir, renamedTo);
|
|
217
|
+
if (fs5.existsSync(renamedPath)) {
|
|
218
|
+
const renamedContent = fs5.readFileSync(renamedPath, "utf8");
|
|
219
|
+
const renamedLines = renamedContent.split("\n");
|
|
220
|
+
const matches = slidingWindowSearch(renamedLines, fingerprint, originalContent);
|
|
221
|
+
if (matches.length > 0 && matches[0].score >= 0.7) {
|
|
222
|
+
return {
|
|
223
|
+
found: true,
|
|
224
|
+
score: matches[0].score,
|
|
225
|
+
suggestedStart: matches[0].windowStart,
|
|
226
|
+
suggestedEnd: matches[0].windowEnd,
|
|
227
|
+
suggestedPath: renamedTo,
|
|
228
|
+
similarity: matches[0].similarity
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const dirPath = path6.dirname(filePath);
|
|
234
|
+
const siblingResults = searchSiblingFiles(rootDir, dirPath, fingerprint, originalContent);
|
|
235
|
+
if (siblingResults.length > 0 && siblingResults[0].score >= 0.7) {
|
|
236
|
+
const best = siblingResults[0];
|
|
237
|
+
return {
|
|
238
|
+
found: true,
|
|
239
|
+
score: best.score,
|
|
240
|
+
suggestedStart: best.start,
|
|
241
|
+
suggestedEnd: best.end,
|
|
242
|
+
suggestedPath: best.file !== filePath ? best.file : void 0,
|
|
243
|
+
similarity: best.score
|
|
244
|
+
// approximate
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return { found: false, score: 0 };
|
|
248
|
+
}
|
|
249
|
+
var STRUCTURAL_TOKENS, W_FIRST_LAST, W_STRUCTURAL, W_LEVENSHTEIN, W_LINE_COUNT;
|
|
250
|
+
var init_aspect_fingerprint = __esm({
|
|
251
|
+
"../paradigm-mcp/src/utils/aspect-fingerprint.ts"() {
|
|
252
|
+
"use strict";
|
|
253
|
+
STRUCTURAL_TOKENS = /^\s*(function |class |if |else |for |while |switch |case |return |export |import |const |let |var |async |await |try |catch |throw |struct |enum |protocol |guard |def |fn )/;
|
|
254
|
+
W_FIRST_LAST = 0.4;
|
|
255
|
+
W_STRUCTURAL = 0.3;
|
|
256
|
+
W_LEVENSHTEIN = 0.2;
|
|
257
|
+
W_LINE_COUNT = 0.1;
|
|
258
|
+
}
|
|
259
|
+
});
|
|
11
260
|
|
|
12
261
|
// ../paradigm-mcp/src/tools/reindex.ts
|
|
13
|
-
import * as
|
|
14
|
-
import * as
|
|
262
|
+
import * as fs10 from "fs";
|
|
263
|
+
import * as path11 from "path";
|
|
15
264
|
import * as yaml8 from "js-yaml";
|
|
16
265
|
|
|
17
266
|
// ../premise/core/dist/index.js
|
|
@@ -1979,6 +2228,7 @@ var SessionTracker = class {
|
|
|
1979
2228
|
context: data.context,
|
|
1980
2229
|
timestamp: Date.now(),
|
|
1981
2230
|
sessionId: this.session.sessionId,
|
|
2231
|
+
externalId: data.externalId,
|
|
1982
2232
|
plan: data.plan,
|
|
1983
2233
|
modifiedFiles: data.modifiedFiles,
|
|
1984
2234
|
symbolsTouched: data.symbolsTouched,
|
|
@@ -2185,8 +2435,8 @@ var SessionTracker = class {
|
|
|
2185
2435
|
* Extract resource type from URI
|
|
2186
2436
|
*/
|
|
2187
2437
|
extractResourceType(uri) {
|
|
2188
|
-
const
|
|
2189
|
-
const firstPart =
|
|
2438
|
+
const path12 = uri.replace("paradigm://", "");
|
|
2439
|
+
const firstPart = path12.split("/")[0];
|
|
2190
2440
|
return firstPart || "unknown";
|
|
2191
2441
|
}
|
|
2192
2442
|
/**
|
|
@@ -2531,6 +2781,10 @@ function getContextToolsList() {
|
|
|
2531
2781
|
type: "string",
|
|
2532
2782
|
description: "What's top-of-mind right now (1-3 sentences)"
|
|
2533
2783
|
},
|
|
2784
|
+
externalId: {
|
|
2785
|
+
type: "string",
|
|
2786
|
+
description: 'Optional: deterministic ID from external source for automatic session recovery (e.g. "linear:PROJ-123", "github:owner/repo#42")'
|
|
2787
|
+
},
|
|
2534
2788
|
plan: {
|
|
2535
2789
|
type: "string",
|
|
2536
2790
|
description: "Optional: the current plan or approach"
|
|
@@ -2573,6 +2827,10 @@ function getContextToolsList() {
|
|
|
2573
2827
|
type: "string",
|
|
2574
2828
|
description: "What's top-of-mind right now (1-3 sentences)"
|
|
2575
2829
|
},
|
|
2830
|
+
externalId: {
|
|
2831
|
+
type: "string",
|
|
2832
|
+
description: 'Optional: deterministic ID from external source for automatic session recovery (e.g. "linear:PROJ-123", "github:owner/repo#42")'
|
|
2833
|
+
},
|
|
2576
2834
|
plan: {
|
|
2577
2835
|
type: "string",
|
|
2578
2836
|
description: "Optional: the current plan or approach"
|
|
@@ -2852,6 +3110,7 @@ ${nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "(none specified
|
|
|
2852
3110
|
tracker2.setRootDir(_ctx.rootDir);
|
|
2853
3111
|
const phase = args.phase;
|
|
2854
3112
|
const context = args.context;
|
|
3113
|
+
const externalId = args.externalId;
|
|
2855
3114
|
const plan = args.plan;
|
|
2856
3115
|
const modifiedFiles = args.modifiedFiles;
|
|
2857
3116
|
const symbolsTouched = args.symbolsTouched;
|
|
@@ -2859,6 +3118,7 @@ ${nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "(none specified
|
|
|
2859
3118
|
const { checkpoint, persisted } = tracker2.saveCheckpoint({
|
|
2860
3119
|
phase,
|
|
2861
3120
|
context,
|
|
3121
|
+
externalId,
|
|
2862
3122
|
plan,
|
|
2863
3123
|
modifiedFiles,
|
|
2864
3124
|
symbolsTouched,
|
|
@@ -2874,6 +3134,7 @@ ${nextSteps.map((step, i) => `${i + 1}. ${step}`).join("\n") || "(none specified
|
|
|
2874
3134
|
phase: checkpoint.phase,
|
|
2875
3135
|
context: checkpoint.context,
|
|
2876
3136
|
sessionId: checkpoint.sessionId,
|
|
3137
|
+
...checkpoint.externalId ? { externalId: checkpoint.externalId } : {},
|
|
2877
3138
|
timestamp: new Date(checkpoint.timestamp).toISOString(),
|
|
2878
3139
|
modifiedFiles: checkpoint.modifiedFiles?.length || 0,
|
|
2879
3140
|
symbolsTouched: checkpoint.symbolsTouched?.length || 0,
|
|
@@ -2927,7 +3188,7 @@ async function buildRecoveryPreamble(rootDir) {
|
|
|
2927
3188
|
}
|
|
2928
3189
|
}
|
|
2929
3190
|
try {
|
|
2930
|
-
const { loadTasks } = await import("./task-loader-
|
|
3191
|
+
const { loadTasks } = await import("./task-loader-LDYWQSLM.js");
|
|
2931
3192
|
const openTasks = await loadTasks(rootDir, { status: "open", limit: 5 });
|
|
2932
3193
|
if (openTasks.length > 0) {
|
|
2933
3194
|
lines.push("");
|
|
@@ -2940,7 +3201,7 @@ async function buildRecoveryPreamble(rootDir) {
|
|
|
2940
3201
|
} catch {
|
|
2941
3202
|
}
|
|
2942
3203
|
try {
|
|
2943
|
-
const { loadLoreEntries: loadLoreEntries2 } = await import("./lore-loader-
|
|
3204
|
+
const { loadLoreEntries: loadLoreEntries2 } = await import("./lore-loader-7NO6N6FT.js");
|
|
2944
3205
|
const arcEntries = await loadLoreEntries2(rootDir, { limit: 10 });
|
|
2945
3206
|
const entriesWithArcs = arcEntries.filter((e) => e.tags?.some((t) => t.startsWith("arc:")));
|
|
2946
3207
|
if (entriesWithArcs.length > 0) {
|
|
@@ -2961,6 +3222,21 @@ async function buildRecoveryPreamble(rootDir) {
|
|
|
2961
3222
|
}
|
|
2962
3223
|
} catch {
|
|
2963
3224
|
}
|
|
3225
|
+
try {
|
|
3226
|
+
const { loadNominations } = await import("./nomination-engine-HDWMN4IO.js");
|
|
3227
|
+
const urgent = loadNominations(rootDir, { pending_only: true }).filter((n) => n.urgency === "critical" || n.urgency === "high");
|
|
3228
|
+
if (urgent.length > 0) {
|
|
3229
|
+
lines.push("");
|
|
3230
|
+
lines.push("Ambient nominations (urgent):");
|
|
3231
|
+
for (const n of urgent.slice(0, 5)) {
|
|
3232
|
+
lines.push(` [${n.urgency}] ${n.brief}`);
|
|
3233
|
+
}
|
|
3234
|
+
if (urgent.length > 5) {
|
|
3235
|
+
lines.push(` ... and ${urgent.length - 5} more. Use paradigm_ambient_nominations to see all.`);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
} catch {
|
|
3239
|
+
}
|
|
2964
3240
|
lines.push("");
|
|
2965
3241
|
lines.push("IMPORTANT: Present a brief summary of this recovery data to the user, then ask what they would like to do: (1) Continue \u2014 pick up where the last session left off, (2) Discard \u2014 ignore the previous session and start fresh, or (3) let them describe what they want to work on instead. Do NOT automatically continue without asking.");
|
|
2966
3242
|
lines.push("---");
|
|
@@ -3018,10 +3294,10 @@ var ToolCache = class {
|
|
|
3018
3294
|
var toolCache = new ToolCache(3e4);
|
|
3019
3295
|
|
|
3020
3296
|
// ../paradigm-mcp/src/utils/aspect-graph.ts
|
|
3021
|
-
import * as
|
|
3022
|
-
import * as
|
|
3023
|
-
import * as
|
|
3024
|
-
import { execSync } from "child_process";
|
|
3297
|
+
import * as fs6 from "fs";
|
|
3298
|
+
import * as path7 from "path";
|
|
3299
|
+
import * as crypto3 from "crypto";
|
|
3300
|
+
import { execSync as execSync2 } from "child_process";
|
|
3025
3301
|
import initSqlJs from "sql.js";
|
|
3026
3302
|
var cachedSQL = null;
|
|
3027
3303
|
async function getSqlJs() {
|
|
@@ -3097,6 +3373,23 @@ var SCHEMA_STATEMENTS = [
|
|
|
3097
3373
|
PRIMARY KEY (aspect_id, access_type)
|
|
3098
3374
|
)`
|
|
3099
3375
|
];
|
|
3376
|
+
var MIGRATIONS = [
|
|
3377
|
+
`ALTER TABLE anchors ADD COLUMN original_content TEXT`,
|
|
3378
|
+
`CREATE TABLE IF NOT EXISTS anchor_history (
|
|
3379
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
3380
|
+
anchor_id INTEGER NOT NULL,
|
|
3381
|
+
action TEXT NOT NULL,
|
|
3382
|
+
old_start INTEGER,
|
|
3383
|
+
old_end INTEGER,
|
|
3384
|
+
new_start INTEGER,
|
|
3385
|
+
new_end INTEGER,
|
|
3386
|
+
old_path TEXT,
|
|
3387
|
+
new_path TEXT,
|
|
3388
|
+
confidence REAL,
|
|
3389
|
+
commit_hash TEXT,
|
|
3390
|
+
healed_at TEXT NOT NULL
|
|
3391
|
+
)`
|
|
3392
|
+
];
|
|
3100
3393
|
var FTS_SQL = `CREATE VIRTUAL TABLE IF NOT EXISTS aspects_fts USING fts5(id, description, enforcement, tags)`;
|
|
3101
3394
|
function queryRows(db, sql, params) {
|
|
3102
3395
|
const stmt = db.prepare(sql);
|
|
@@ -3124,15 +3417,15 @@ function queryOne(db, sql, params) {
|
|
|
3124
3417
|
}
|
|
3125
3418
|
async function openAspectGraph(rootDir) {
|
|
3126
3419
|
const SQL = await getSqlJs();
|
|
3127
|
-
const dbDir =
|
|
3128
|
-
const dbPath =
|
|
3420
|
+
const dbDir = path7.join(rootDir, ".paradigm");
|
|
3421
|
+
const dbPath = path7.join(dbDir, "aspect-graph.db");
|
|
3129
3422
|
let db;
|
|
3130
|
-
if (
|
|
3131
|
-
const buffer =
|
|
3423
|
+
if (fs6.existsSync(dbPath)) {
|
|
3424
|
+
const buffer = fs6.readFileSync(dbPath);
|
|
3132
3425
|
db = new SQL.Database(buffer);
|
|
3133
3426
|
} else {
|
|
3134
|
-
if (!
|
|
3135
|
-
|
|
3427
|
+
if (!fs6.existsSync(dbDir)) {
|
|
3428
|
+
fs6.mkdirSync(dbDir, { recursive: true });
|
|
3136
3429
|
}
|
|
3137
3430
|
db = new SQL.Database();
|
|
3138
3431
|
}
|
|
@@ -3143,17 +3436,23 @@ async function openAspectGraph(rootDir) {
|
|
|
3143
3436
|
db.run(FTS_SQL);
|
|
3144
3437
|
} catch {
|
|
3145
3438
|
}
|
|
3439
|
+
for (const migration of MIGRATIONS) {
|
|
3440
|
+
try {
|
|
3441
|
+
db.run(migration);
|
|
3442
|
+
} catch {
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3146
3445
|
return db;
|
|
3147
3446
|
}
|
|
3148
3447
|
function closeAspectGraph(db, rootDir) {
|
|
3149
3448
|
if (rootDir) {
|
|
3150
|
-
const dbDir =
|
|
3151
|
-
if (!
|
|
3152
|
-
|
|
3449
|
+
const dbDir = path7.join(rootDir, ".paradigm");
|
|
3450
|
+
if (!fs6.existsSync(dbDir)) {
|
|
3451
|
+
fs6.mkdirSync(dbDir, { recursive: true });
|
|
3153
3452
|
}
|
|
3154
|
-
const dbPath =
|
|
3453
|
+
const dbPath = path7.join(dbDir, "aspect-graph.db");
|
|
3155
3454
|
const data = db.export();
|
|
3156
|
-
|
|
3455
|
+
fs6.writeFileSync(dbPath, Buffer.from(data));
|
|
3157
3456
|
}
|
|
3158
3457
|
db.close();
|
|
3159
3458
|
}
|
|
@@ -3162,7 +3461,7 @@ function materializeAspects(db, symbols, rootDir) {
|
|
|
3162
3461
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3163
3462
|
let headCommit = null;
|
|
3164
3463
|
try {
|
|
3165
|
-
headCommit =
|
|
3464
|
+
headCommit = execSync2("git rev-parse HEAD", { cwd: rootDir, encoding: "utf8" }).trim();
|
|
3166
3465
|
} catch {
|
|
3167
3466
|
}
|
|
3168
3467
|
db.run("DELETE FROM anchors");
|
|
@@ -3200,9 +3499,9 @@ function materializeAspects(db, symbols, rootDir) {
|
|
|
3200
3499
|
const { startLine, endLine } = resolveAnchorLines(anchor);
|
|
3201
3500
|
const hashes = computeAnchorHash(anchor, null);
|
|
3202
3501
|
db.run(
|
|
3203
|
-
`INSERT INTO anchors (aspect_id, file_path, start_line, end_line, content_hash, normalized_hash, materialized_at_commit, last_verified)
|
|
3204
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3205
|
-
[entry.symbol, anchor.path, startLine, endLine, hashes.exact, hashes.normalized, headCommit, now]
|
|
3502
|
+
`INSERT INTO anchors (aspect_id, file_path, start_line, end_line, content_hash, normalized_hash, materialized_at_commit, last_verified, original_content)
|
|
3503
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
3504
|
+
[entry.symbol, anchor.path, startLine, endLine, hashes.exact, hashes.normalized, headCommit, now, hashes.normalizedContent]
|
|
3206
3505
|
);
|
|
3207
3506
|
}
|
|
3208
3507
|
}
|
|
@@ -3289,8 +3588,8 @@ function checkDrift(db, rootDir, aspectId, autoHeal = true) {
|
|
|
3289
3588
|
const anchorRows = aspectId ? queryRows(db, "SELECT * FROM anchors WHERE aspect_id = ?", [aspectId]) : queryRows(db, "SELECT * FROM anchors");
|
|
3290
3589
|
const results = [];
|
|
3291
3590
|
for (const anchor of anchorRows) {
|
|
3292
|
-
const absolutePath =
|
|
3293
|
-
if (!
|
|
3591
|
+
const absolutePath = path7.isAbsolute(anchor.file_path) ? anchor.file_path : path7.join(rootDir, anchor.file_path);
|
|
3592
|
+
if (!fs6.existsSync(absolutePath)) {
|
|
3294
3593
|
results.push({
|
|
3295
3594
|
aspectId: anchor.aspect_id,
|
|
3296
3595
|
path: anchor.file_path,
|
|
@@ -3304,12 +3603,12 @@ function checkDrift(db, rootDir, aspectId, autoHeal = true) {
|
|
|
3304
3603
|
continue;
|
|
3305
3604
|
}
|
|
3306
3605
|
try {
|
|
3307
|
-
const fileContent =
|
|
3606
|
+
const fileContent = fs6.readFileSync(absolutePath, "utf8");
|
|
3308
3607
|
const lines = fileContent.split("\n");
|
|
3309
3608
|
const startIdx = Math.max(0, anchor.start_line - 1);
|
|
3310
3609
|
const endIdx = Math.min(lines.length, anchor.end_line);
|
|
3311
3610
|
const sliceContent = lines.slice(startIdx, endIdx).join("\n");
|
|
3312
|
-
const currentExactHash =
|
|
3611
|
+
const currentExactHash = crypto3.createHash("sha256").update(sliceContent).digest("hex");
|
|
3313
3612
|
if (anchor.content_hash != null && currentExactHash === anchor.content_hash) {
|
|
3314
3613
|
results.push({
|
|
3315
3614
|
aspectId: anchor.aspect_id,
|
|
@@ -3326,7 +3625,7 @@ function checkDrift(db, rootDir, aspectId, autoHeal = true) {
|
|
|
3326
3625
|
}
|
|
3327
3626
|
continue;
|
|
3328
3627
|
}
|
|
3329
|
-
const currentNormalizedHash =
|
|
3628
|
+
const currentNormalizedHash = crypto3.createHash("sha256").update(normalizeForHash(sliceContent)).digest("hex");
|
|
3330
3629
|
if (anchor.normalized_hash != null && currentNormalizedHash === anchor.normalized_hash) {
|
|
3331
3630
|
db.run("UPDATE anchors SET content_hash = ?, drifted = 0 WHERE id = ?", [currentExactHash, anchor.id]);
|
|
3332
3631
|
results.push({
|
|
@@ -3371,7 +3670,7 @@ function checkDrift(db, rootDir, aspectId, autoHeal = true) {
|
|
|
3371
3670
|
const shiftedStartIdx = Math.max(0, mapping.currentStart - 1);
|
|
3372
3671
|
const shiftedEndIdx = Math.min(lines.length, mapping.currentEnd);
|
|
3373
3672
|
const shiftedContent = lines.slice(shiftedStartIdx, shiftedEndIdx).join("\n");
|
|
3374
|
-
const shiftedExactHash =
|
|
3673
|
+
const shiftedExactHash = crypto3.createHash("sha256").update(shiftedContent).digest("hex");
|
|
3375
3674
|
if (anchor.content_hash != null && shiftedExactHash === anchor.content_hash) {
|
|
3376
3675
|
const healed = autoHeal;
|
|
3377
3676
|
if (healed) {
|
|
@@ -3411,10 +3710,10 @@ function checkDrift(db, rootDir, aspectId, autoHeal = true) {
|
|
|
3411
3710
|
});
|
|
3412
3711
|
resolvedByGit = true;
|
|
3413
3712
|
} else {
|
|
3414
|
-
const shiftedNormalized =
|
|
3713
|
+
const shiftedNormalized = crypto3.createHash("sha256").update(normalizeForHash(shiftedContent)).digest("hex");
|
|
3415
3714
|
if (anchor.normalized_hash != null && shiftedNormalized === anchor.normalized_hash) {
|
|
3416
3715
|
if (autoHeal) {
|
|
3417
|
-
const shiftedNewHash =
|
|
3716
|
+
const shiftedNewHash = crypto3.createHash("sha256").update(shiftedContent).digest("hex");
|
|
3418
3717
|
db.run(
|
|
3419
3718
|
"UPDATE anchors SET start_line = ?, end_line = ?, content_hash = ?, drifted = 0 WHERE id = ?",
|
|
3420
3719
|
[mapping.currentStart, mapping.currentEnd, shiftedNewHash, anchor.id]
|
|
@@ -3455,6 +3754,73 @@ function checkDrift(db, rootDir, aspectId, autoHeal = true) {
|
|
|
3455
3754
|
}
|
|
3456
3755
|
}
|
|
3457
3756
|
if (resolvedByGit) continue;
|
|
3757
|
+
if (anchor.original_content) {
|
|
3758
|
+
const { contentSearch: contentSearch2 } = (init_aspect_fingerprint(), __toCommonJS(aspect_fingerprint_exports));
|
|
3759
|
+
const searchResult = contentSearch2(rootDir, anchor.file_path, anchor.original_content, autoHeal);
|
|
3760
|
+
if (searchResult.found && searchResult.score >= 0.7) {
|
|
3761
|
+
const isAutoHeal = autoHeal && searchResult.score >= 0.85 && !searchResult.suggestedPath;
|
|
3762
|
+
if (isAutoHeal && searchResult.suggestedStart && searchResult.suggestedEnd) {
|
|
3763
|
+
db.run(
|
|
3764
|
+
"UPDATE anchors SET start_line = ?, end_line = ?, drifted = 0 WHERE id = ?",
|
|
3765
|
+
[searchResult.suggestedStart, searchResult.suggestedEnd, anchor.id]
|
|
3766
|
+
);
|
|
3767
|
+
try {
|
|
3768
|
+
db.run(
|
|
3769
|
+
`INSERT INTO anchor_history (anchor_id, action, old_start, old_end, new_start, new_end, confidence, healed_at)
|
|
3770
|
+
VALUES (?, 'relocated', ?, ?, ?, ?, ?, ?)`,
|
|
3771
|
+
[anchor.id, anchor.start_line, anchor.end_line, searchResult.suggestedStart, searchResult.suggestedEnd, searchResult.score, (/* @__PURE__ */ new Date()).toISOString()]
|
|
3772
|
+
);
|
|
3773
|
+
} catch {
|
|
3774
|
+
}
|
|
3775
|
+
const aspectRow = queryRows(
|
|
3776
|
+
db,
|
|
3777
|
+
"SELECT defined_in FROM aspects WHERE id = ?",
|
|
3778
|
+
[anchor.aspect_id]
|
|
3779
|
+
);
|
|
3780
|
+
if (aspectRow.length > 0) {
|
|
3781
|
+
healAnchorInPurposeFile(
|
|
3782
|
+
rootDir,
|
|
3783
|
+
aspectRow[0].defined_in,
|
|
3784
|
+
anchor.file_path,
|
|
3785
|
+
anchor.start_line,
|
|
3786
|
+
anchor.end_line,
|
|
3787
|
+
searchResult.suggestedStart,
|
|
3788
|
+
searchResult.suggestedEnd
|
|
3789
|
+
);
|
|
3790
|
+
}
|
|
3791
|
+
results.push({
|
|
3792
|
+
aspectId: anchor.aspect_id,
|
|
3793
|
+
path: anchor.file_path,
|
|
3794
|
+
startLine: searchResult.suggestedStart,
|
|
3795
|
+
endLine: searchResult.suggestedEnd,
|
|
3796
|
+
status: "relocated",
|
|
3797
|
+
resolvedBy: "content-search",
|
|
3798
|
+
exists: true,
|
|
3799
|
+
similarity: searchResult.similarity,
|
|
3800
|
+
suggestedStart: searchResult.suggestedStart,
|
|
3801
|
+
suggestedEnd: searchResult.suggestedEnd,
|
|
3802
|
+
autoHealed: true,
|
|
3803
|
+
drifted: false
|
|
3804
|
+
});
|
|
3805
|
+
continue;
|
|
3806
|
+
}
|
|
3807
|
+
results.push({
|
|
3808
|
+
aspectId: anchor.aspect_id,
|
|
3809
|
+
path: searchResult.suggestedPath || anchor.file_path,
|
|
3810
|
+
startLine: anchor.start_line,
|
|
3811
|
+
endLine: anchor.end_line,
|
|
3812
|
+
status: "relocated",
|
|
3813
|
+
resolvedBy: "content-search",
|
|
3814
|
+
exists: true,
|
|
3815
|
+
similarity: searchResult.similarity,
|
|
3816
|
+
suggestedStart: searchResult.suggestedStart,
|
|
3817
|
+
suggestedEnd: searchResult.suggestedEnd,
|
|
3818
|
+
autoHealed: false,
|
|
3819
|
+
drifted: true
|
|
3820
|
+
});
|
|
3821
|
+
continue;
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3458
3824
|
db.run("UPDATE anchors SET drifted = 1 WHERE id = ?", [anchor.id]);
|
|
3459
3825
|
results.push({
|
|
3460
3826
|
aspectId: anchor.aspect_id,
|
|
@@ -3515,7 +3881,7 @@ function parseUnifiedDiffHunks(diffOutput) {
|
|
|
3515
3881
|
function computeLineShift(rootDir, filePath, fromCommit, originalStart, originalEnd) {
|
|
3516
3882
|
let diff;
|
|
3517
3883
|
try {
|
|
3518
|
-
diff =
|
|
3884
|
+
diff = execSync2(
|
|
3519
3885
|
`git diff ${fromCommit}..HEAD --unified=0 -- "${filePath}"`,
|
|
3520
3886
|
{ cwd: rootDir, encoding: "utf8", timeout: 5e3 }
|
|
3521
3887
|
);
|
|
@@ -3547,15 +3913,15 @@ function computeLineShift(rootDir, filePath, fromCommit, originalStart, original
|
|
|
3547
3913
|
};
|
|
3548
3914
|
}
|
|
3549
3915
|
function healAnchorInPurposeFile(rootDir, purposeFilePath, anchorFilePath, oldStart, oldEnd, newStart, newEnd) {
|
|
3550
|
-
const absolutePurpose =
|
|
3551
|
-
if (!
|
|
3916
|
+
const absolutePurpose = path7.isAbsolute(purposeFilePath) ? purposeFilePath : path7.join(rootDir, purposeFilePath);
|
|
3917
|
+
if (!fs6.existsSync(absolutePurpose)) return false;
|
|
3552
3918
|
try {
|
|
3553
|
-
const content =
|
|
3919
|
+
const content = fs6.readFileSync(absolutePurpose, "utf8");
|
|
3554
3920
|
const oldAnchor = oldStart === oldEnd ? `${anchorFilePath}:${oldStart}` : `${anchorFilePath}:${oldStart}-${oldEnd}`;
|
|
3555
3921
|
const newAnchor = newStart === newEnd ? `${anchorFilePath}:${newStart}` : `${anchorFilePath}:${newStart}-${newEnd}`;
|
|
3556
3922
|
if (!content.includes(oldAnchor)) return false;
|
|
3557
3923
|
const updated = content.replace(oldAnchor, newAnchor);
|
|
3558
|
-
|
|
3924
|
+
fs6.writeFileSync(absolutePurpose, updated, "utf8");
|
|
3559
3925
|
return true;
|
|
3560
3926
|
} catch {
|
|
3561
3927
|
return false;
|
|
@@ -3565,21 +3931,22 @@ function normalizeForHash(content) {
|
|
|
3565
3931
|
return content.split("\n").map((line) => line.trimEnd()).filter((line) => line.trim() !== "").map((line) => line.replace(/\s+/g, " ")).join("\n");
|
|
3566
3932
|
}
|
|
3567
3933
|
function computeAnchorHash(anchor, rootDir) {
|
|
3568
|
-
if (!rootDir) return { exact: null, normalized: null };
|
|
3569
|
-
const absolutePath =
|
|
3570
|
-
if (!
|
|
3934
|
+
if (!rootDir) return { exact: null, normalized: null, normalizedContent: null };
|
|
3935
|
+
const absolutePath = path7.isAbsolute(anchor.path) ? anchor.path : path7.join(rootDir, anchor.path);
|
|
3936
|
+
if (!fs6.existsSync(absolutePath)) return { exact: null, normalized: null, normalizedContent: null };
|
|
3571
3937
|
try {
|
|
3572
|
-
const fileContent =
|
|
3938
|
+
const fileContent = fs6.readFileSync(absolutePath, "utf8");
|
|
3573
3939
|
const lines = fileContent.split("\n");
|
|
3574
3940
|
const { startLine, endLine } = resolveAnchorLines(anchor);
|
|
3575
3941
|
const startIdx = Math.max(0, startLine - 1);
|
|
3576
3942
|
const endIdx = Math.min(lines.length, endLine);
|
|
3577
3943
|
const sliceContent = lines.slice(startIdx, endIdx).join("\n");
|
|
3578
|
-
const
|
|
3579
|
-
const
|
|
3580
|
-
|
|
3944
|
+
const normalizedContent = normalizeForHash(sliceContent);
|
|
3945
|
+
const exact = crypto3.createHash("sha256").update(sliceContent).digest("hex");
|
|
3946
|
+
const normalized = crypto3.createHash("sha256").update(normalizedContent).digest("hex");
|
|
3947
|
+
return { exact, normalized, normalizedContent };
|
|
3581
3948
|
} catch {
|
|
3582
|
-
return { exact: null, normalized: null };
|
|
3949
|
+
return { exact: null, normalized: null, normalizedContent: null };
|
|
3583
3950
|
}
|
|
3584
3951
|
}
|
|
3585
3952
|
function inferCategory(data, entry) {
|
|
@@ -3600,6 +3967,7 @@ function inferSeverity(data, entry) {
|
|
|
3600
3967
|
}
|
|
3601
3968
|
|
|
3602
3969
|
// ../paradigm-mcp/src/utils/aspect-lore-bridge.ts
|
|
3970
|
+
init_lore_loader();
|
|
3603
3971
|
var LORE_ID_PATTERN = /L-\d{4}-\d{2}-\d{2}-\d{3}/g;
|
|
3604
3972
|
async function materializeLoreLinks(db, rootDir) {
|
|
3605
3973
|
db.run("DELETE FROM lore_links");
|
|
@@ -3795,19 +4163,19 @@ function toLoreSummary(entry) {
|
|
|
3795
4163
|
}
|
|
3796
4164
|
|
|
3797
4165
|
// ../paradigm-mcp/src/utils/personas-loader.ts
|
|
3798
|
-
import * as
|
|
3799
|
-
import * as
|
|
4166
|
+
import * as fs7 from "fs";
|
|
4167
|
+
import * as path8 from "path";
|
|
3800
4168
|
import * as yaml5 from "js-yaml";
|
|
3801
4169
|
var PERSONAS_ROOT = ".paradigm/personas";
|
|
3802
4170
|
var INDEX_FILE = "index.yaml";
|
|
3803
4171
|
async function loadPersonas(rootDir, filter) {
|
|
3804
|
-
const personasDir =
|
|
3805
|
-
if (!
|
|
3806
|
-
const files =
|
|
4172
|
+
const personasDir = path8.join(rootDir, PERSONAS_ROOT);
|
|
4173
|
+
if (!fs7.existsSync(personasDir)) return [];
|
|
4174
|
+
const files = fs7.readdirSync(personasDir).filter((f) => f.endsWith(".persona"));
|
|
3807
4175
|
const personas = [];
|
|
3808
4176
|
for (const file of files) {
|
|
3809
4177
|
try {
|
|
3810
|
-
const content =
|
|
4178
|
+
const content = fs7.readFileSync(path8.join(personasDir, file), "utf8");
|
|
3811
4179
|
const persona = yaml5.load(content);
|
|
3812
4180
|
if (persona && persona.id) {
|
|
3813
4181
|
personas.push(persona);
|
|
@@ -3818,10 +4186,10 @@ async function loadPersonas(rootDir, filter) {
|
|
|
3818
4186
|
return applyFilter(personas, filter);
|
|
3819
4187
|
}
|
|
3820
4188
|
async function loadPersona(rootDir, id) {
|
|
3821
|
-
const filePath =
|
|
3822
|
-
if (!
|
|
4189
|
+
const filePath = path8.join(rootDir, PERSONAS_ROOT, `${id}.persona`);
|
|
4190
|
+
if (!fs7.existsSync(filePath)) return null;
|
|
3823
4191
|
try {
|
|
3824
|
-
return yaml5.load(
|
|
4192
|
+
return yaml5.load(fs7.readFileSync(filePath, "utf8"));
|
|
3825
4193
|
} catch {
|
|
3826
4194
|
return null;
|
|
3827
4195
|
}
|
|
@@ -3851,10 +4219,10 @@ function applyFilter(personas, filter) {
|
|
|
3851
4219
|
return result;
|
|
3852
4220
|
}
|
|
3853
4221
|
async function createPersona(rootDir, data) {
|
|
3854
|
-
const personasDir =
|
|
3855
|
-
|
|
3856
|
-
const filePath =
|
|
3857
|
-
if (
|
|
4222
|
+
const personasDir = path8.join(rootDir, PERSONAS_ROOT);
|
|
4223
|
+
fs7.mkdirSync(personasDir, { recursive: true });
|
|
4224
|
+
const filePath = path8.join(personasDir, `${data.id}.persona`);
|
|
4225
|
+
if (fs7.existsSync(filePath)) {
|
|
3858
4226
|
throw new Error(`Persona ${data.id} already exists`);
|
|
3859
4227
|
}
|
|
3860
4228
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3871,14 +4239,14 @@ async function createPersona(rootDir, data) {
|
|
|
3871
4239
|
created: now,
|
|
3872
4240
|
updated: now
|
|
3873
4241
|
};
|
|
3874
|
-
|
|
4242
|
+
fs7.writeFileSync(filePath, yaml5.dump(persona, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }));
|
|
3875
4243
|
await rebuildPersonaIndex(rootDir);
|
|
3876
4244
|
return data.id;
|
|
3877
4245
|
}
|
|
3878
4246
|
async function updatePersona(rootDir, id, partial) {
|
|
3879
4247
|
const persona = await loadPersona(rootDir, id);
|
|
3880
4248
|
if (!persona) return false;
|
|
3881
|
-
const filePath =
|
|
4249
|
+
const filePath = path8.join(rootDir, PERSONAS_ROOT, `${id}.persona`);
|
|
3882
4250
|
const updated = {
|
|
3883
4251
|
...persona,
|
|
3884
4252
|
...partial,
|
|
@@ -3890,13 +4258,13 @@ async function updatePersona(rootDir, id, partial) {
|
|
|
3890
4258
|
// immutable
|
|
3891
4259
|
updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
3892
4260
|
};
|
|
3893
|
-
|
|
4261
|
+
fs7.writeFileSync(filePath, yaml5.dump(updated, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }));
|
|
3894
4262
|
await rebuildPersonaIndex(rootDir);
|
|
3895
4263
|
return true;
|
|
3896
4264
|
}
|
|
3897
4265
|
async function deletePersona(rootDir, id) {
|
|
3898
|
-
const filePath =
|
|
3899
|
-
if (!
|
|
4266
|
+
const filePath = path8.join(rootDir, PERSONAS_ROOT, `${id}.persona`);
|
|
4267
|
+
if (!fs7.existsSync(filePath)) {
|
|
3900
4268
|
return { deleted: false, warnings: [] };
|
|
3901
4269
|
}
|
|
3902
4270
|
const warnings = [];
|
|
@@ -3912,7 +4280,7 @@ async function deletePersona(rootDir, id) {
|
|
|
3912
4280
|
}
|
|
3913
4281
|
}
|
|
3914
4282
|
}
|
|
3915
|
-
|
|
4283
|
+
fs7.unlinkSync(filePath);
|
|
3916
4284
|
await rebuildPersonaIndex(rootDir);
|
|
3917
4285
|
return { deleted: true, warnings };
|
|
3918
4286
|
}
|
|
@@ -4014,12 +4382,12 @@ async function validatePersona(rootDir, persona, deep = false) {
|
|
|
4014
4382
|
}
|
|
4015
4383
|
}
|
|
4016
4384
|
if (deep) {
|
|
4017
|
-
const portalPath =
|
|
4385
|
+
const portalPath = path8.join(rootDir, "portal.yaml");
|
|
4018
4386
|
let portalGates = [];
|
|
4019
4387
|
let portalRoutes = [];
|
|
4020
|
-
if (
|
|
4388
|
+
if (fs7.existsSync(portalPath)) {
|
|
4021
4389
|
try {
|
|
4022
|
-
const portal = yaml5.load(
|
|
4390
|
+
const portal = yaml5.load(fs7.readFileSync(portalPath, "utf8"));
|
|
4023
4391
|
if (portal.gates && typeof portal.gates === "object") {
|
|
4024
4392
|
portalGates = Object.keys(portal.gates);
|
|
4025
4393
|
}
|
|
@@ -4070,10 +4438,10 @@ async function validatePersona(rootDir, persona, deep = false) {
|
|
|
4070
4438
|
}
|
|
4071
4439
|
}
|
|
4072
4440
|
let allFlows = [];
|
|
4073
|
-
const flowIndexPath =
|
|
4074
|
-
if (
|
|
4441
|
+
const flowIndexPath = path8.join(rootDir, ".paradigm", "flow-index.json");
|
|
4442
|
+
if (fs7.existsSync(flowIndexPath)) {
|
|
4075
4443
|
try {
|
|
4076
|
-
const flowIndex = JSON.parse(
|
|
4444
|
+
const flowIndex = JSON.parse(fs7.readFileSync(flowIndexPath, "utf8"));
|
|
4077
4445
|
allFlows = Object.keys(flowIndex.flows || {});
|
|
4078
4446
|
} catch {
|
|
4079
4447
|
}
|
|
@@ -4150,8 +4518,8 @@ async function detectSpawnCycle(rootDir, startId) {
|
|
|
4150
4518
|
return visit(startId);
|
|
4151
4519
|
}
|
|
4152
4520
|
async function rebuildPersonaIndex(rootDir) {
|
|
4153
|
-
const personasDir =
|
|
4154
|
-
|
|
4521
|
+
const personasDir = path8.join(rootDir, PERSONAS_ROOT);
|
|
4522
|
+
fs7.mkdirSync(personasDir, { recursive: true });
|
|
4155
4523
|
const personas = await loadPersonas(rootDir);
|
|
4156
4524
|
const entries = {};
|
|
4157
4525
|
const gateCoverage = {};
|
|
@@ -4190,10 +4558,10 @@ async function rebuildPersonaIndex(rootDir) {
|
|
|
4190
4558
|
}
|
|
4191
4559
|
}
|
|
4192
4560
|
let uncoveredRoutes = [];
|
|
4193
|
-
const portalPath =
|
|
4194
|
-
if (
|
|
4561
|
+
const portalPath = path8.join(rootDir, "portal.yaml");
|
|
4562
|
+
if (fs7.existsSync(portalPath)) {
|
|
4195
4563
|
try {
|
|
4196
|
-
const portal = yaml5.load(
|
|
4564
|
+
const portal = yaml5.load(fs7.readFileSync(portalPath, "utf8"));
|
|
4197
4565
|
if (portal.routes && typeof portal.routes === "object") {
|
|
4198
4566
|
const portalRoutes = Object.keys(portal.routes);
|
|
4199
4567
|
uncoveredRoutes = portalRoutes.filter((pr) => {
|
|
@@ -4204,12 +4572,12 @@ async function rebuildPersonaIndex(rootDir) {
|
|
|
4204
4572
|
}
|
|
4205
4573
|
}
|
|
4206
4574
|
const chains = {};
|
|
4207
|
-
const chainsDir =
|
|
4208
|
-
if (
|
|
4209
|
-
const chainFiles =
|
|
4575
|
+
const chainsDir = path8.join(personasDir, "chains");
|
|
4576
|
+
if (fs7.existsSync(chainsDir)) {
|
|
4577
|
+
const chainFiles = fs7.readdirSync(chainsDir).filter((f) => f.endsWith(".yaml"));
|
|
4210
4578
|
for (const file of chainFiles) {
|
|
4211
4579
|
try {
|
|
4212
|
-
const content =
|
|
4580
|
+
const content = fs7.readFileSync(path8.join(chainsDir, file), "utf8");
|
|
4213
4581
|
const chain = yaml5.load(content);
|
|
4214
4582
|
if (chain && chain.id) {
|
|
4215
4583
|
const orderIds = chain.order.map((o) => o.persona);
|
|
@@ -4242,8 +4610,8 @@ async function rebuildPersonaIndex(rootDir) {
|
|
|
4242
4610
|
route_coverage: routeCoverage,
|
|
4243
4611
|
uncovered_routes: uncoveredRoutes
|
|
4244
4612
|
};
|
|
4245
|
-
|
|
4246
|
-
|
|
4613
|
+
fs7.writeFileSync(
|
|
4614
|
+
path8.join(personasDir, INDEX_FILE),
|
|
4247
4615
|
yaml5.dump(index, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false })
|
|
4248
4616
|
);
|
|
4249
4617
|
return index;
|
|
@@ -4262,20 +4630,20 @@ async function getPersonaCoverage(rootDir) {
|
|
|
4262
4630
|
}
|
|
4263
4631
|
let portalGates = [];
|
|
4264
4632
|
let portalRoutes = [];
|
|
4265
|
-
const portalPath =
|
|
4266
|
-
if (
|
|
4633
|
+
const portalPath = path8.join(rootDir, "portal.yaml");
|
|
4634
|
+
if (fs7.existsSync(portalPath)) {
|
|
4267
4635
|
try {
|
|
4268
|
-
const portal = yaml5.load(
|
|
4636
|
+
const portal = yaml5.load(fs7.readFileSync(portalPath, "utf8"));
|
|
4269
4637
|
if (portal.gates && typeof portal.gates === "object") portalGates = Object.keys(portal.gates);
|
|
4270
4638
|
if (portal.routes && typeof portal.routes === "object") portalRoutes = Object.keys(portal.routes);
|
|
4271
4639
|
} catch {
|
|
4272
4640
|
}
|
|
4273
4641
|
}
|
|
4274
4642
|
let allFlows = [];
|
|
4275
|
-
const flowIndexPath =
|
|
4276
|
-
if (
|
|
4643
|
+
const flowIndexPath = path8.join(rootDir, ".paradigm", "flow-index.json");
|
|
4644
|
+
if (fs7.existsSync(flowIndexPath)) {
|
|
4277
4645
|
try {
|
|
4278
|
-
const flowIndex = JSON.parse(
|
|
4646
|
+
const flowIndex = JSON.parse(fs7.readFileSync(flowIndexPath, "utf8"));
|
|
4279
4647
|
allFlows = Object.keys(flowIndex.flows || {});
|
|
4280
4648
|
} catch {
|
|
4281
4649
|
}
|
|
@@ -4324,8 +4692,8 @@ async function getAffectedPersonas(rootDir, symbol) {
|
|
|
4324
4692
|
}
|
|
4325
4693
|
return results;
|
|
4326
4694
|
}
|
|
4327
|
-
function deepGet(obj,
|
|
4328
|
-
const parts =
|
|
4695
|
+
function deepGet(obj, path12) {
|
|
4696
|
+
const parts = path12.split(/[.\[\]]+/).filter(Boolean);
|
|
4329
4697
|
let current = obj;
|
|
4330
4698
|
for (const part of parts) {
|
|
4331
4699
|
if (current == null || typeof current !== "object") return void 0;
|
|
@@ -4405,7 +4773,7 @@ function assertStep(step, event) {
|
|
|
4405
4773
|
async function validateAgainstSentinel(persona, options = {}) {
|
|
4406
4774
|
const steps = [];
|
|
4407
4775
|
try {
|
|
4408
|
-
const { SentinelStorage } = await import("./dist-
|
|
4776
|
+
const { SentinelStorage } = await import("./dist-77JDTVAY.js");
|
|
4409
4777
|
const storage = new SentinelStorage();
|
|
4410
4778
|
const events = storage.queryEvents?.({
|
|
4411
4779
|
schemaId: "paradigm-personas",
|
|
@@ -4494,21 +4862,21 @@ async function validateAgainstSentinel(persona, options = {}) {
|
|
|
4494
4862
|
}
|
|
4495
4863
|
|
|
4496
4864
|
// ../paradigm-mcp/src/utils/protocol-loader.ts
|
|
4497
|
-
import * as
|
|
4498
|
-
import * as
|
|
4865
|
+
import * as fs8 from "fs";
|
|
4866
|
+
import * as path9 from "path";
|
|
4499
4867
|
import * as yaml6 from "js-yaml";
|
|
4500
4868
|
var PROTOCOLS_DIR = ".paradigm/protocols";
|
|
4501
4869
|
var INDEX_FILE2 = "index.yaml";
|
|
4502
4870
|
async function loadProtocols(rootDir) {
|
|
4503
|
-
const protocolsDir =
|
|
4504
|
-
if (!
|
|
4871
|
+
const protocolsDir = path9.join(rootDir, PROTOCOLS_DIR);
|
|
4872
|
+
if (!fs8.existsSync(protocolsDir)) {
|
|
4505
4873
|
return [];
|
|
4506
4874
|
}
|
|
4507
|
-
const files =
|
|
4875
|
+
const files = fs8.readdirSync(protocolsDir).filter((f) => f.endsWith(".protocol")).sort();
|
|
4508
4876
|
const protocols = [];
|
|
4509
4877
|
for (const file of files) {
|
|
4510
4878
|
try {
|
|
4511
|
-
const content =
|
|
4879
|
+
const content = fs8.readFileSync(path9.join(protocolsDir, file), "utf8");
|
|
4512
4880
|
const protocol = yaml6.load(content);
|
|
4513
4881
|
if (protocol?.id && protocol?.name) {
|
|
4514
4882
|
protocols.push(protocol);
|
|
@@ -4520,10 +4888,10 @@ async function loadProtocols(rootDir) {
|
|
|
4520
4888
|
}
|
|
4521
4889
|
async function loadProtocol(rootDir, id) {
|
|
4522
4890
|
const slug = id.replace(/^P-/, "");
|
|
4523
|
-
const filePath =
|
|
4524
|
-
if (
|
|
4891
|
+
const filePath = path9.join(rootDir, PROTOCOLS_DIR, `${slug}.protocol`);
|
|
4892
|
+
if (fs8.existsSync(filePath)) {
|
|
4525
4893
|
try {
|
|
4526
|
-
const content =
|
|
4894
|
+
const content = fs8.readFileSync(filePath, "utf8");
|
|
4527
4895
|
return yaml6.load(content);
|
|
4528
4896
|
} catch {
|
|
4529
4897
|
return null;
|
|
@@ -4533,12 +4901,12 @@ async function loadProtocol(rootDir, id) {
|
|
|
4533
4901
|
return protocols.find((p) => p.id === id) || null;
|
|
4534
4902
|
}
|
|
4535
4903
|
async function loadProtocolIndex(rootDir) {
|
|
4536
|
-
const indexPath =
|
|
4537
|
-
if (!
|
|
4904
|
+
const indexPath = path9.join(rootDir, PROTOCOLS_DIR, INDEX_FILE2);
|
|
4905
|
+
if (!fs8.existsSync(indexPath)) {
|
|
4538
4906
|
return null;
|
|
4539
4907
|
}
|
|
4540
4908
|
try {
|
|
4541
|
-
const content =
|
|
4909
|
+
const content = fs8.readFileSync(indexPath, "utf8");
|
|
4542
4910
|
return yaml6.load(content);
|
|
4543
4911
|
} catch {
|
|
4544
4912
|
return null;
|
|
@@ -4590,9 +4958,9 @@ async function searchProtocols(rootDir, task, limit = 3) {
|
|
|
4590
4958
|
return scored.slice(0, limit);
|
|
4591
4959
|
}
|
|
4592
4960
|
async function recordProtocol(rootDir, protocol) {
|
|
4593
|
-
const protocolsDir =
|
|
4594
|
-
if (!
|
|
4595
|
-
|
|
4961
|
+
const protocolsDir = path9.join(rootDir, PROTOCOLS_DIR);
|
|
4962
|
+
if (!fs8.existsSync(protocolsDir)) {
|
|
4963
|
+
fs8.mkdirSync(protocolsDir, { recursive: true });
|
|
4596
4964
|
}
|
|
4597
4965
|
const slug = slugify(protocol.name);
|
|
4598
4966
|
const id = `P-${slug}`;
|
|
@@ -4612,8 +4980,8 @@ async function recordProtocol(rootDir, protocol) {
|
|
|
4612
4980
|
verified_by: protocol.verified_by || "claude-opus-4-6",
|
|
4613
4981
|
status: "current"
|
|
4614
4982
|
};
|
|
4615
|
-
const filePath =
|
|
4616
|
-
|
|
4983
|
+
const filePath = path9.join(protocolsDir, `${slug}.protocol`);
|
|
4984
|
+
fs8.writeFileSync(filePath, yaml6.dump(full, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4617
4985
|
return id;
|
|
4618
4986
|
}
|
|
4619
4987
|
async function updateProtocol(rootDir, id, partial, refresh = false) {
|
|
@@ -4633,20 +5001,20 @@ async function updateProtocol(rootDir, id, partial, refresh = false) {
|
|
|
4633
5001
|
protocol.verified_by = partial.verified_by || "claude-opus-4-6";
|
|
4634
5002
|
}
|
|
4635
5003
|
const slug = id.replace(/^P-/, "");
|
|
4636
|
-
const filePath =
|
|
4637
|
-
|
|
5004
|
+
const filePath = path9.join(rootDir, PROTOCOLS_DIR, `${slug}.protocol`);
|
|
5005
|
+
fs8.writeFileSync(filePath, yaml6.dump(protocol, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4638
5006
|
return true;
|
|
4639
5007
|
}
|
|
4640
5008
|
function validateProtocol(rootDir, protocol) {
|
|
4641
5009
|
const issues = [];
|
|
4642
5010
|
let status = "current";
|
|
4643
5011
|
if (protocol.exemplar) {
|
|
4644
|
-
const exemplarPath =
|
|
4645
|
-
if (!
|
|
5012
|
+
const exemplarPath = path9.join(rootDir, protocol.exemplar);
|
|
5013
|
+
if (!fs8.existsSync(exemplarPath)) {
|
|
4646
5014
|
issues.push(`Exemplar missing: ${protocol.exemplar}`);
|
|
4647
5015
|
status = "broken";
|
|
4648
5016
|
} else {
|
|
4649
|
-
const stat =
|
|
5017
|
+
const stat = fs8.statSync(exemplarPath);
|
|
4650
5018
|
if (stat.mtime.toISOString() > protocol.last_verified) {
|
|
4651
5019
|
issues.push(`Exemplar modified since last verified: ${protocol.exemplar}`);
|
|
4652
5020
|
if (status !== "broken") status = "stale";
|
|
@@ -4655,15 +5023,15 @@ function validateProtocol(rootDir, protocol) {
|
|
|
4655
5023
|
}
|
|
4656
5024
|
for (const step of protocol.steps) {
|
|
4657
5025
|
if (step.template_from) {
|
|
4658
|
-
const templatePath =
|
|
4659
|
-
if (!
|
|
5026
|
+
const templatePath = path9.join(rootDir, step.template_from);
|
|
5027
|
+
if (!fs8.existsSync(templatePath)) {
|
|
4660
5028
|
issues.push(`Template file missing: ${step.template_from}`);
|
|
4661
5029
|
status = "broken";
|
|
4662
5030
|
}
|
|
4663
5031
|
}
|
|
4664
5032
|
if (step.action === "modify" && step.target) {
|
|
4665
|
-
const targetPath =
|
|
4666
|
-
if (!step.target.includes("{") && !
|
|
5033
|
+
const targetPath = path9.join(rootDir, step.target);
|
|
5034
|
+
if (!step.target.includes("{") && !fs8.existsSync(targetPath)) {
|
|
4667
5035
|
issues.push(`Modify target missing: ${step.target}`);
|
|
4668
5036
|
status = "broken";
|
|
4669
5037
|
}
|
|
@@ -4682,9 +5050,9 @@ async function rebuildProtocolIndex(rootDir) {
|
|
|
4682
5050
|
if (protocol.status !== validation.status) {
|
|
4683
5051
|
protocol.status = validation.status;
|
|
4684
5052
|
const slug = protocol.id.replace(/^P-/, "");
|
|
4685
|
-
const filePath =
|
|
4686
|
-
if (
|
|
4687
|
-
|
|
5053
|
+
const filePath = path9.join(rootDir, PROTOCOLS_DIR, `${slug}.protocol`);
|
|
5054
|
+
if (fs8.existsSync(filePath)) {
|
|
5055
|
+
fs8.writeFileSync(filePath, yaml6.dump(protocol, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4688
5056
|
}
|
|
4689
5057
|
}
|
|
4690
5058
|
switch (validation.status) {
|
|
@@ -4718,13 +5086,13 @@ async function rebuildProtocolIndex(rootDir) {
|
|
|
4718
5086
|
broken
|
|
4719
5087
|
}
|
|
4720
5088
|
};
|
|
4721
|
-
const protocolsDir =
|
|
5089
|
+
const protocolsDir = path9.join(rootDir, PROTOCOLS_DIR);
|
|
4722
5090
|
if (protocols.length > 0) {
|
|
4723
|
-
if (!
|
|
4724
|
-
|
|
5091
|
+
if (!fs8.existsSync(protocolsDir)) {
|
|
5092
|
+
fs8.mkdirSync(protocolsDir, { recursive: true });
|
|
4725
5093
|
}
|
|
4726
|
-
const indexPath =
|
|
4727
|
-
|
|
5094
|
+
const indexPath = path9.join(protocolsDir, INDEX_FILE2);
|
|
5095
|
+
fs8.writeFileSync(indexPath, yaml6.dump(index, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4728
5096
|
}
|
|
4729
5097
|
return index;
|
|
4730
5098
|
}
|
|
@@ -4732,21 +5100,21 @@ function detectProtocolSuggestion(rootDir, filesCreated, filesModified) {
|
|
|
4732
5100
|
if (!filesCreated || filesCreated.length < 2) return null;
|
|
4733
5101
|
const dirGroups = {};
|
|
4734
5102
|
for (const file of filesCreated) {
|
|
4735
|
-
const dir =
|
|
5103
|
+
const dir = path9.dirname(file);
|
|
4736
5104
|
if (!dirGroups[dir]) dirGroups[dir] = [];
|
|
4737
5105
|
dirGroups[dir].push(file);
|
|
4738
5106
|
}
|
|
4739
5107
|
for (const [dir, created] of Object.entries(dirGroups)) {
|
|
4740
5108
|
if (created.length < 2) continue;
|
|
4741
|
-
const absDir =
|
|
4742
|
-
if (!
|
|
4743
|
-
const existing =
|
|
4744
|
-
const ext =
|
|
5109
|
+
const absDir = path9.join(rootDir, dir);
|
|
5110
|
+
if (!fs8.existsSync(absDir)) continue;
|
|
5111
|
+
const existing = fs8.readdirSync(absDir).filter((f) => {
|
|
5112
|
+
const ext = path9.extname(f);
|
|
4745
5113
|
return [".ts", ".tsx", ".js", ".jsx", ".rs", ".py"].includes(ext);
|
|
4746
5114
|
});
|
|
4747
|
-
const preExisting = existing.filter((f) => !created.some((c) =>
|
|
5115
|
+
const preExisting = existing.filter((f) => !created.some((c) => path9.basename(c) === f));
|
|
4748
5116
|
if (preExisting.length > 0) {
|
|
4749
|
-
const exemplar =
|
|
5117
|
+
const exemplar = path9.join(dir, preExisting[0]);
|
|
4750
5118
|
const steps = [
|
|
4751
5119
|
...created.map((f) => ({ action: "create", target: f })),
|
|
4752
5120
|
...filesModified.map((f) => ({ action: "modify", target: f }))
|
|
@@ -4754,7 +5122,7 @@ function detectProtocolSuggestion(rootDir, filesCreated, filesModified) {
|
|
|
4754
5122
|
return {
|
|
4755
5123
|
hint: `This session created ${created.length} new files in ${dir}/ following existing patterns. Consider recording a protocol.`,
|
|
4756
5124
|
draft: {
|
|
4757
|
-
name: `Add a ${
|
|
5125
|
+
name: `Add a ${path9.basename(dir).replace(/s$/, "")}`,
|
|
4758
5126
|
exemplar,
|
|
4759
5127
|
steps
|
|
4760
5128
|
}
|
|
@@ -4829,8 +5197,8 @@ function slugify(name) {
|
|
|
4829
5197
|
}
|
|
4830
5198
|
|
|
4831
5199
|
// ../paradigm-mcp/src/utils/university-loader.ts
|
|
4832
|
-
import * as
|
|
4833
|
-
import * as
|
|
5200
|
+
import * as fs9 from "fs";
|
|
5201
|
+
import * as path10 from "path";
|
|
4834
5202
|
import * as yaml7 from "js-yaml";
|
|
4835
5203
|
var UNIVERSITY_DIR = ".paradigm/university";
|
|
4836
5204
|
var CONTENT_DIR = "content";
|
|
@@ -4872,12 +5240,12 @@ var DEFAULT_CONFIG = {
|
|
|
4872
5240
|
}
|
|
4873
5241
|
};
|
|
4874
5242
|
function loadUniversityConfig(rootDir) {
|
|
4875
|
-
const configPath =
|
|
4876
|
-
if (!
|
|
5243
|
+
const configPath = path10.join(rootDir, UNIVERSITY_DIR, CONFIG_FILE);
|
|
5244
|
+
if (!fs9.existsSync(configPath)) {
|
|
4877
5245
|
return { ...DEFAULT_CONFIG };
|
|
4878
5246
|
}
|
|
4879
5247
|
try {
|
|
4880
|
-
const raw =
|
|
5248
|
+
const raw = fs9.readFileSync(configPath, "utf8");
|
|
4881
5249
|
const data = yaml7.load(raw);
|
|
4882
5250
|
if (!data) return { ...DEFAULT_CONFIG };
|
|
4883
5251
|
return {
|
|
@@ -4886,7 +5254,8 @@ function loadUniversityConfig(rootDir) {
|
|
|
4886
5254
|
content: {
|
|
4887
5255
|
categories: data.content?.categories || [],
|
|
4888
5256
|
defaultDifficulty: data.content?.defaultDifficulty || "beginner",
|
|
4889
|
-
requireApproval: data.content?.requireApproval ?? false
|
|
5257
|
+
requireApproval: data.content?.requireApproval ?? false,
|
|
5258
|
+
defaultCategory: data.content?.defaultCategory
|
|
4890
5259
|
},
|
|
4891
5260
|
diplomas: {
|
|
4892
5261
|
includeGlobalPLSAT: data.diplomas?.includeGlobalPLSAT ?? true,
|
|
@@ -4898,10 +5267,10 @@ function loadUniversityConfig(rootDir) {
|
|
|
4898
5267
|
}
|
|
4899
5268
|
}
|
|
4900
5269
|
function loadUniversityIndex(rootDir) {
|
|
4901
|
-
const indexPath =
|
|
4902
|
-
if (!
|
|
5270
|
+
const indexPath = path10.join(rootDir, UNIVERSITY_DIR, INDEX_FILE3);
|
|
5271
|
+
if (!fs9.existsSync(indexPath)) return null;
|
|
4903
5272
|
try {
|
|
4904
|
-
const raw =
|
|
5273
|
+
const raw = fs9.readFileSync(indexPath, "utf8");
|
|
4905
5274
|
return yaml7.load(raw);
|
|
4906
5275
|
} catch {
|
|
4907
5276
|
return null;
|
|
@@ -4929,7 +5298,7 @@ function loadNote(rootDir, id) {
|
|
|
4929
5298
|
const filePath = resolveContentFile(rootDir, id, ".md");
|
|
4930
5299
|
if (!filePath) return null;
|
|
4931
5300
|
try {
|
|
4932
|
-
const raw =
|
|
5301
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
4933
5302
|
const parsed = parseFrontmatter(raw);
|
|
4934
5303
|
if (!parsed) return null;
|
|
4935
5304
|
const fm = parsed.frontmatter;
|
|
@@ -4940,18 +5309,18 @@ function loadNote(rootDir, id) {
|
|
|
4940
5309
|
}
|
|
4941
5310
|
function saveNote(rootDir, frontmatter, body) {
|
|
4942
5311
|
const subdir = frontmatter.type === "policy" ? POLICIES_DIR : NOTES_DIR;
|
|
4943
|
-
const dir =
|
|
4944
|
-
|
|
4945
|
-
const filePath =
|
|
5312
|
+
const dir = path10.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, subdir);
|
|
5313
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
5314
|
+
const filePath = path10.join(dir, `${frontmatter.id}.md`);
|
|
4946
5315
|
const content = serializeFrontmatter(frontmatter, body);
|
|
4947
|
-
|
|
5316
|
+
fs9.writeFileSync(filePath, content, "utf8");
|
|
4948
5317
|
return filePath;
|
|
4949
5318
|
}
|
|
4950
5319
|
function loadQuiz(rootDir, id) {
|
|
4951
5320
|
const filePath = resolveContentFile(rootDir, id, ".yaml");
|
|
4952
5321
|
if (!filePath) return null;
|
|
4953
5322
|
try {
|
|
4954
|
-
const raw =
|
|
5323
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
4955
5324
|
const data = yaml7.load(raw);
|
|
4956
5325
|
if (!data || !data.id) return null;
|
|
4957
5326
|
return normalizeQuiz(data);
|
|
@@ -4960,17 +5329,17 @@ function loadQuiz(rootDir, id) {
|
|
|
4960
5329
|
}
|
|
4961
5330
|
}
|
|
4962
5331
|
function saveQuiz(rootDir, quiz) {
|
|
4963
|
-
const dir =
|
|
4964
|
-
|
|
4965
|
-
const filePath =
|
|
4966
|
-
|
|
5332
|
+
const dir = path10.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, QUIZZES_DIR);
|
|
5333
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
5334
|
+
const filePath = path10.join(dir, `${quiz.id}.yaml`);
|
|
5335
|
+
fs9.writeFileSync(filePath, yaml7.dump(quiz, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4967
5336
|
return filePath;
|
|
4968
5337
|
}
|
|
4969
5338
|
function loadPath(rootDir, id) {
|
|
4970
5339
|
const filePath = resolveContentFile(rootDir, id, ".yaml");
|
|
4971
5340
|
if (!filePath) return null;
|
|
4972
5341
|
try {
|
|
4973
|
-
const raw =
|
|
5342
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
4974
5343
|
const data = yaml7.load(raw);
|
|
4975
5344
|
if (!data || !data.id) return null;
|
|
4976
5345
|
return data;
|
|
@@ -4979,21 +5348,21 @@ function loadPath(rootDir, id) {
|
|
|
4979
5348
|
}
|
|
4980
5349
|
}
|
|
4981
5350
|
function savePath(rootDir, lp) {
|
|
4982
|
-
const dir =
|
|
4983
|
-
|
|
4984
|
-
const filePath =
|
|
4985
|
-
|
|
5351
|
+
const dir = path10.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR, PATHS_DIR);
|
|
5352
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
5353
|
+
const filePath = path10.join(dir, `${lp.id}.yaml`);
|
|
5354
|
+
fs9.writeFileSync(filePath, yaml7.dump(lp, { lineWidth: -1, noRefs: true }), "utf8");
|
|
4986
5355
|
return filePath;
|
|
4987
5356
|
}
|
|
4988
5357
|
function loadDiplomas(rootDir, filter) {
|
|
4989
|
-
const dir =
|
|
4990
|
-
if (!
|
|
5358
|
+
const dir = path10.join(rootDir, UNIVERSITY_DIR, DIPLOMAS_DIR);
|
|
5359
|
+
if (!fs9.existsSync(dir)) return [];
|
|
4991
5360
|
const results = [];
|
|
4992
5361
|
try {
|
|
4993
|
-
const files =
|
|
5362
|
+
const files = fs9.readdirSync(dir).filter((f) => f.endsWith(".yaml"));
|
|
4994
5363
|
for (const file of files) {
|
|
4995
5364
|
try {
|
|
4996
|
-
const raw =
|
|
5365
|
+
const raw = fs9.readFileSync(path10.join(dir, file), "utf8");
|
|
4997
5366
|
const diploma = yaml7.load(raw);
|
|
4998
5367
|
if (!diploma || !diploma.id) continue;
|
|
4999
5368
|
if (filter?.student && diploma.student !== filter.student) continue;
|
|
@@ -5007,10 +5376,10 @@ function loadDiplomas(rootDir, filter) {
|
|
|
5007
5376
|
return results.sort((a, b) => b.earnedAt.localeCompare(a.earnedAt));
|
|
5008
5377
|
}
|
|
5009
5378
|
function saveDiploma(rootDir, diploma) {
|
|
5010
|
-
const dir =
|
|
5011
|
-
|
|
5012
|
-
const filePath =
|
|
5013
|
-
|
|
5379
|
+
const dir = path10.join(rootDir, UNIVERSITY_DIR, DIPLOMAS_DIR);
|
|
5380
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
5381
|
+
const filePath = path10.join(dir, `${diploma.id}.yaml`);
|
|
5382
|
+
fs9.writeFileSync(filePath, yaml7.dump(diploma, { lineWidth: -1, noRefs: true }), "utf8");
|
|
5014
5383
|
return filePath;
|
|
5015
5384
|
}
|
|
5016
5385
|
function searchContent(rootDir, filter) {
|
|
@@ -5038,20 +5407,34 @@ function searchContent(rootDir, filter) {
|
|
|
5038
5407
|
(e) => e.title.toLowerCase().includes(q) || e.id.toLowerCase().includes(q) || e.tags.some((t) => t.toLowerCase().includes(q))
|
|
5039
5408
|
);
|
|
5040
5409
|
}
|
|
5410
|
+
if (filter.category) {
|
|
5411
|
+
results = results.filter((e) => e.category === filter.category);
|
|
5412
|
+
}
|
|
5413
|
+
if (filter.track) {
|
|
5414
|
+
const config = loadUniversityConfig(rootDir);
|
|
5415
|
+
const categoryTrackMap = /* @__PURE__ */ new Map();
|
|
5416
|
+
for (const cat of config.content.categories) {
|
|
5417
|
+
categoryTrackMap.set(cat.id, cat.track || "core");
|
|
5418
|
+
}
|
|
5419
|
+
results = results.filter((e) => {
|
|
5420
|
+
const entryTrack = e.category ? categoryTrackMap.get(e.category) || "core" : "core";
|
|
5421
|
+
return entryTrack === filter.track;
|
|
5422
|
+
});
|
|
5423
|
+
}
|
|
5041
5424
|
const limit = filter.limit || 20;
|
|
5042
5425
|
return results.slice(0, limit);
|
|
5043
5426
|
}
|
|
5044
5427
|
function rebuildUniversityIndex(rootDir) {
|
|
5045
|
-
const uniDir =
|
|
5046
|
-
const contentDir =
|
|
5428
|
+
const uniDir = path10.join(rootDir, UNIVERSITY_DIR);
|
|
5429
|
+
const contentDir = path10.join(uniDir, CONTENT_DIR);
|
|
5047
5430
|
const entries = [];
|
|
5048
5431
|
for (const subdir of [NOTES_DIR, POLICIES_DIR]) {
|
|
5049
|
-
const dir =
|
|
5050
|
-
if (!
|
|
5432
|
+
const dir = path10.join(contentDir, subdir);
|
|
5433
|
+
if (!fs9.existsSync(dir)) continue;
|
|
5051
5434
|
try {
|
|
5052
|
-
for (const file of
|
|
5435
|
+
for (const file of fs9.readdirSync(dir).filter((f) => f.endsWith(".md"))) {
|
|
5053
5436
|
try {
|
|
5054
|
-
const raw =
|
|
5437
|
+
const raw = fs9.readFileSync(path10.join(dir, file), "utf8");
|
|
5055
5438
|
const parsed = parseFrontmatter(raw);
|
|
5056
5439
|
if (!parsed) continue;
|
|
5057
5440
|
const fm = parsed.frontmatter;
|
|
@@ -5065,7 +5448,8 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5065
5448
|
tags: Array.isArray(fm.tags) ? fm.tags : [],
|
|
5066
5449
|
symbols: Array.isArray(fm.symbols) ? fm.symbols : [],
|
|
5067
5450
|
difficulty: fm.difficulty || "beginner",
|
|
5068
|
-
file: `${CONTENT_DIR}/${subdir}/${file}
|
|
5451
|
+
file: `${CONTENT_DIR}/${subdir}/${file}`,
|
|
5452
|
+
...fm.category ? { category: fm.category } : {}
|
|
5069
5453
|
});
|
|
5070
5454
|
} catch {
|
|
5071
5455
|
}
|
|
@@ -5073,12 +5457,12 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5073
5457
|
} catch {
|
|
5074
5458
|
}
|
|
5075
5459
|
}
|
|
5076
|
-
const quizDir =
|
|
5077
|
-
if (
|
|
5460
|
+
const quizDir = path10.join(contentDir, QUIZZES_DIR);
|
|
5461
|
+
if (fs9.existsSync(quizDir)) {
|
|
5078
5462
|
try {
|
|
5079
|
-
for (const file of
|
|
5463
|
+
for (const file of fs9.readdirSync(quizDir).filter((f) => f.endsWith(".yaml"))) {
|
|
5080
5464
|
try {
|
|
5081
|
-
const raw =
|
|
5465
|
+
const raw = fs9.readFileSync(path10.join(quizDir, file), "utf8");
|
|
5082
5466
|
const quiz = yaml7.load(raw);
|
|
5083
5467
|
if (!quiz || !quiz.id) continue;
|
|
5084
5468
|
entries.push({
|
|
@@ -5091,7 +5475,8 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5091
5475
|
tags: quiz.tags || [],
|
|
5092
5476
|
symbols: quiz.symbols || [],
|
|
5093
5477
|
difficulty: quiz.difficulty || "beginner",
|
|
5094
|
-
file: `${CONTENT_DIR}/${QUIZZES_DIR}/${file}
|
|
5478
|
+
file: `${CONTENT_DIR}/${QUIZZES_DIR}/${file}`,
|
|
5479
|
+
...quiz.category ? { category: quiz.category } : {}
|
|
5095
5480
|
});
|
|
5096
5481
|
} catch {
|
|
5097
5482
|
}
|
|
@@ -5099,12 +5484,12 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5099
5484
|
} catch {
|
|
5100
5485
|
}
|
|
5101
5486
|
}
|
|
5102
|
-
const pathDir =
|
|
5103
|
-
if (
|
|
5487
|
+
const pathDir = path10.join(contentDir, PATHS_DIR);
|
|
5488
|
+
if (fs9.existsSync(pathDir)) {
|
|
5104
5489
|
try {
|
|
5105
|
-
for (const file of
|
|
5490
|
+
for (const file of fs9.readdirSync(pathDir).filter((f) => f.endsWith(".yaml"))) {
|
|
5106
5491
|
try {
|
|
5107
|
-
const raw =
|
|
5492
|
+
const raw = fs9.readFileSync(path10.join(pathDir, file), "utf8");
|
|
5108
5493
|
const lp = yaml7.load(raw);
|
|
5109
5494
|
if (!lp || !lp.id) continue;
|
|
5110
5495
|
entries.push({
|
|
@@ -5116,7 +5501,8 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5116
5501
|
updated: lp.updated || "",
|
|
5117
5502
|
tags: lp.tags || [],
|
|
5118
5503
|
symbols: [],
|
|
5119
|
-
file: `${CONTENT_DIR}/${PATHS_DIR}/${file}
|
|
5504
|
+
file: `${CONTENT_DIR}/${PATHS_DIR}/${file}`,
|
|
5505
|
+
...lp.category ? { category: lp.category } : {}
|
|
5120
5506
|
});
|
|
5121
5507
|
} catch {
|
|
5122
5508
|
}
|
|
@@ -5125,10 +5511,10 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5125
5511
|
}
|
|
5126
5512
|
}
|
|
5127
5513
|
let diplomaCount = 0;
|
|
5128
|
-
const diplomaDir =
|
|
5129
|
-
if (
|
|
5514
|
+
const diplomaDir = path10.join(uniDir, DIPLOMAS_DIR);
|
|
5515
|
+
if (fs9.existsSync(diplomaDir)) {
|
|
5130
5516
|
try {
|
|
5131
|
-
diplomaCount =
|
|
5517
|
+
diplomaCount = fs9.readdirSync(diplomaDir).filter((f) => f.endsWith(".yaml")).length;
|
|
5132
5518
|
} catch {
|
|
5133
5519
|
}
|
|
5134
5520
|
}
|
|
@@ -5139,9 +5525,9 @@ function rebuildUniversityIndex(rootDir) {
|
|
|
5139
5525
|
entries,
|
|
5140
5526
|
diplomaCount
|
|
5141
5527
|
};
|
|
5142
|
-
|
|
5143
|
-
const indexPath =
|
|
5144
|
-
|
|
5528
|
+
fs9.mkdirSync(uniDir, { recursive: true });
|
|
5529
|
+
const indexPath = path10.join(uniDir, INDEX_FILE3);
|
|
5530
|
+
fs9.writeFileSync(indexPath, yaml7.dump(index, { lineWidth: -1, noRefs: true }), "utf8");
|
|
5145
5531
|
return index;
|
|
5146
5532
|
}
|
|
5147
5533
|
function validateUniversityContent(rootDir, options) {
|
|
@@ -5173,11 +5559,14 @@ function validateUniversityContent(rootDir, options) {
|
|
|
5173
5559
|
validatePathContent(rootDir, entry.id, allContentIds, issues);
|
|
5174
5560
|
}
|
|
5175
5561
|
if (knownSymbols && entry.symbols.length > 0) {
|
|
5562
|
+
const config = loadUniversityConfig(rootDir);
|
|
5563
|
+
const entryCat = config.content.categories.find((c) => c.id === entry.category);
|
|
5564
|
+
const isRelaxed = entryCat?.validationStrictness === "relaxed";
|
|
5176
5565
|
for (const sym of entry.symbols) {
|
|
5177
5566
|
if (!knownSymbols.has(sym)) {
|
|
5178
5567
|
issues.push({
|
|
5179
5568
|
contentId: entry.id,
|
|
5180
|
-
severity: "warning",
|
|
5569
|
+
severity: isRelaxed ? "warning" : "warning",
|
|
5181
5570
|
check: "broken-symbol-ref",
|
|
5182
5571
|
message: `Symbol "${sym}" not found in scan-index`,
|
|
5183
5572
|
fix: `Remove or update the symbol reference in ${entry.id}`
|
|
@@ -5297,9 +5686,18 @@ function getAffectedUniversityContent(rootDir, symbol) {
|
|
|
5297
5686
|
function getOnboardingSequence(rootDir, student) {
|
|
5298
5687
|
const index = loadUniversityIndex(rootDir);
|
|
5299
5688
|
if (!index) {
|
|
5300
|
-
return { paths: [], suggestedContent: [], diplomaCount: 0, totalContent: 0 };
|
|
5689
|
+
return { paths: [], suggestedContent: [], extracurricular: [], diplomaCount: 0, totalContent: 0 };
|
|
5690
|
+
}
|
|
5691
|
+
const config = loadUniversityConfig(rootDir);
|
|
5692
|
+
const excludedCategories = /* @__PURE__ */ new Set();
|
|
5693
|
+
for (const cat of config.content.categories) {
|
|
5694
|
+
if (cat.excludeFromOnboarding) {
|
|
5695
|
+
excludedCategories.add(cat.id);
|
|
5696
|
+
}
|
|
5301
5697
|
}
|
|
5302
|
-
const
|
|
5698
|
+
const coreEntries = index.entries.filter((e) => !e.category || !excludedCategories.has(e.category));
|
|
5699
|
+
const extracurricularEntries = index.entries.filter((e) => e.category && excludedCategories.has(e.category));
|
|
5700
|
+
const pathEntries = coreEntries.filter((e) => e.type === "path");
|
|
5303
5701
|
const diplomas = student ? loadDiplomas(rootDir, { student }) : [];
|
|
5304
5702
|
const diplomaSourceIds = new Set(diplomas.map((d) => d.source));
|
|
5305
5703
|
const paths = pathEntries.map((pe) => {
|
|
@@ -5311,19 +5709,20 @@ function getOnboardingSequence(rootDir, student) {
|
|
|
5311
5709
|
completed: diplomaSourceIds.has(pe.id)
|
|
5312
5710
|
};
|
|
5313
5711
|
});
|
|
5314
|
-
const suggestedContent =
|
|
5712
|
+
const suggestedContent = coreEntries.filter((e) => e.type !== "path" && (e.difficulty === "beginner" || e.tags.includes("onboarding"))).slice(0, 10);
|
|
5315
5713
|
return {
|
|
5316
5714
|
paths,
|
|
5317
5715
|
suggestedContent,
|
|
5716
|
+
extracurricular: extracurricularEntries,
|
|
5318
5717
|
diplomaCount: diplomas.length,
|
|
5319
5718
|
totalContent: index.totalContent
|
|
5320
5719
|
};
|
|
5321
5720
|
}
|
|
5322
5721
|
function resolveContentFile(rootDir, id, ext) {
|
|
5323
|
-
const contentDir =
|
|
5722
|
+
const contentDir = path10.join(rootDir, UNIVERSITY_DIR, CONTENT_DIR);
|
|
5324
5723
|
for (const subdir of [NOTES_DIR, POLICIES_DIR, QUIZZES_DIR, PATHS_DIR]) {
|
|
5325
|
-
const filePath =
|
|
5326
|
-
if (
|
|
5724
|
+
const filePath = path10.join(contentDir, subdir, `${id}${ext}`);
|
|
5725
|
+
if (fs9.existsSync(filePath)) return filePath;
|
|
5327
5726
|
}
|
|
5328
5727
|
return null;
|
|
5329
5728
|
}
|
|
@@ -5339,7 +5738,8 @@ function normalizeFrontmatter(fm) {
|
|
|
5339
5738
|
symbols: Array.isArray(fm.symbols) ? fm.symbols : [],
|
|
5340
5739
|
difficulty: fm.difficulty || "beginner",
|
|
5341
5740
|
estimatedMinutes: fm.estimatedMinutes,
|
|
5342
|
-
prerequisites: Array.isArray(fm.prerequisites) ? fm.prerequisites : []
|
|
5741
|
+
prerequisites: Array.isArray(fm.prerequisites) ? fm.prerequisites : [],
|
|
5742
|
+
...fm.category ? { category: fm.category } : {}
|
|
5343
5743
|
};
|
|
5344
5744
|
}
|
|
5345
5745
|
function normalizeQuiz(quiz) {
|
|
@@ -5354,10 +5754,10 @@ function normalizeQuiz(quiz) {
|
|
|
5354
5754
|
}
|
|
5355
5755
|
function loadKnownSymbols(rootDir) {
|
|
5356
5756
|
const symbols = /* @__PURE__ */ new Set();
|
|
5357
|
-
const scanIndexPath =
|
|
5358
|
-
if (!
|
|
5757
|
+
const scanIndexPath = path10.join(rootDir, ".paradigm", "scan-index.json");
|
|
5758
|
+
if (!fs9.existsSync(scanIndexPath)) return symbols;
|
|
5359
5759
|
try {
|
|
5360
|
-
const raw =
|
|
5760
|
+
const raw = fs9.readFileSync(scanIndexPath, "utf8");
|
|
5361
5761
|
const index = JSON.parse(raw);
|
|
5362
5762
|
if (index.symbols && Array.isArray(index.symbols)) {
|
|
5363
5763
|
for (const sym of index.symbols) {
|
|
@@ -5389,17 +5789,17 @@ function isContentStale(rootDir, entry, _symbol) {
|
|
|
5389
5789
|
if (!entry.updated) return false;
|
|
5390
5790
|
const contentUpdated = new Date(entry.updated).getTime();
|
|
5391
5791
|
if (isNaN(contentUpdated)) return false;
|
|
5392
|
-
const scanIndexPath =
|
|
5393
|
-
if (!
|
|
5792
|
+
const scanIndexPath = path10.join(rootDir, ".paradigm", "scan-index.json");
|
|
5793
|
+
if (!fs9.existsSync(scanIndexPath)) return false;
|
|
5394
5794
|
try {
|
|
5395
|
-
const raw =
|
|
5795
|
+
const raw = fs9.readFileSync(scanIndexPath, "utf8");
|
|
5396
5796
|
const index = JSON.parse(raw);
|
|
5397
5797
|
if (index.symbols && Array.isArray(index.symbols)) {
|
|
5398
5798
|
for (const sym of index.symbols) {
|
|
5399
5799
|
if (sym.symbol === _symbol && sym.filePath) {
|
|
5400
|
-
const purposePath =
|
|
5401
|
-
if (
|
|
5402
|
-
const stat =
|
|
5800
|
+
const purposePath = path10.join(rootDir, sym.filePath);
|
|
5801
|
+
if (fs9.existsSync(purposePath)) {
|
|
5802
|
+
const stat = fs9.statSync(purposePath);
|
|
5403
5803
|
if (stat.mtime.getTime() > contentUpdated) {
|
|
5404
5804
|
return true;
|
|
5405
5805
|
}
|
|
@@ -5514,10 +5914,10 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
5514
5914
|
} else {
|
|
5515
5915
|
aggregation = await aggregateFromDirectory(rootDir);
|
|
5516
5916
|
}
|
|
5517
|
-
const projectName = ctx?.projectName ||
|
|
5518
|
-
const paradigmDir =
|
|
5519
|
-
if (!
|
|
5520
|
-
|
|
5917
|
+
const projectName = ctx?.projectName || path11.basename(rootDir);
|
|
5918
|
+
const paradigmDir = path11.join(rootDir, ".paradigm");
|
|
5919
|
+
if (!fs10.existsSync(paradigmDir)) {
|
|
5920
|
+
fs10.mkdirSync(paradigmDir, { recursive: true });
|
|
5521
5921
|
}
|
|
5522
5922
|
const scanIndex = generateScanIndex(
|
|
5523
5923
|
{
|
|
@@ -5527,12 +5927,12 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
5527
5927
|
},
|
|
5528
5928
|
{ projectName }
|
|
5529
5929
|
);
|
|
5530
|
-
const scanIndexPath =
|
|
5531
|
-
|
|
5930
|
+
const scanIndexPath = path11.join(paradigmDir, "scan-index.json");
|
|
5931
|
+
fs10.writeFileSync(scanIndexPath, serializeScanIndex(scanIndex), "utf8");
|
|
5532
5932
|
filesWritten.push(".paradigm/scan-index.json");
|
|
5533
5933
|
const navigatorData = buildNavigatorData(rootDir, aggregation);
|
|
5534
|
-
const navigatorPath =
|
|
5535
|
-
|
|
5934
|
+
const navigatorPath = path11.join(paradigmDir, "navigator.yaml");
|
|
5935
|
+
fs10.writeFileSync(
|
|
5536
5936
|
navigatorPath,
|
|
5537
5937
|
yaml8.dump(navigatorData, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false }),
|
|
5538
5938
|
"utf8"
|
|
@@ -5541,8 +5941,8 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
5541
5941
|
const flowIndex = generateFlowIndex(rootDir, aggregation.purposeFiles);
|
|
5542
5942
|
let flowCount = 0;
|
|
5543
5943
|
if (flowIndex && Object.keys(flowIndex.flows).length > 0) {
|
|
5544
|
-
const flowIndexPath =
|
|
5545
|
-
|
|
5944
|
+
const flowIndexPath = path11.join(paradigmDir, "flow-index.json");
|
|
5945
|
+
fs10.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
|
|
5546
5946
|
filesWritten.push(".paradigm/flow-index.json");
|
|
5547
5947
|
flowCount = Object.keys(flowIndex.flows).length;
|
|
5548
5948
|
}
|
|
@@ -5585,8 +5985,8 @@ async function rebuildStaticFiles(rootDir, ctx) {
|
|
|
5585
5985
|
}
|
|
5586
5986
|
let universityStats;
|
|
5587
5987
|
try {
|
|
5588
|
-
const uniDir =
|
|
5589
|
-
if (
|
|
5988
|
+
const uniDir = path11.join(rootDir, ".paradigm", "university");
|
|
5989
|
+
if (fs10.existsSync(uniDir)) {
|
|
5590
5990
|
const uniIndex = rebuildUniversityIndex(rootDir);
|
|
5591
5991
|
if (uniIndex.totalContent > 0 || uniIndex.diplomaCount > 0) {
|
|
5592
5992
|
universityStats = {
|
|
@@ -5684,7 +6084,7 @@ function buildNavigatorData(rootDir, aggregation) {
|
|
|
5684
6084
|
function buildStructure(rootDir) {
|
|
5685
6085
|
const structure = {};
|
|
5686
6086
|
for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
|
|
5687
|
-
const existingPaths = patterns.filter((p) =>
|
|
6087
|
+
const existingPaths = patterns.filter((p) => fs10.existsSync(path11.join(rootDir, p)));
|
|
5688
6088
|
if (existingPaths.length > 0) {
|
|
5689
6089
|
const symbolInfo = Object.values(SYMBOL_CATEGORIES).find((s) => s.category === category);
|
|
5690
6090
|
structure[category] = { paths: existingPaths, symbol: symbolInfo?.prefix || "@" };
|
|
@@ -5695,7 +6095,7 @@ function buildStructure(rootDir) {
|
|
|
5695
6095
|
function buildKeyFiles(rootDir) {
|
|
5696
6096
|
const keyFiles = {};
|
|
5697
6097
|
for (const [category, patterns] of Object.entries(KEY_FILE_PATTERNS)) {
|
|
5698
|
-
const existingPaths = patterns.filter((p) =>
|
|
6098
|
+
const existingPaths = patterns.filter((p) => fs10.existsSync(path11.join(rootDir, p)));
|
|
5699
6099
|
if (existingPaths.length > 0) {
|
|
5700
6100
|
keyFiles[category] = existingPaths;
|
|
5701
6101
|
}
|
|
@@ -5711,10 +6111,10 @@ function buildSkipPatterns(rootDir) {
|
|
|
5711
6111
|
unless_testing: [...DEFAULT_SKIP_PATTERNS.unless_testing],
|
|
5712
6112
|
unless_docs: [...DEFAULT_SKIP_PATTERNS.unless_docs]
|
|
5713
6113
|
};
|
|
5714
|
-
const gitignorePath =
|
|
5715
|
-
if (
|
|
6114
|
+
const gitignorePath = path11.join(rootDir, ".gitignore");
|
|
6115
|
+
if (fs10.existsSync(gitignorePath)) {
|
|
5716
6116
|
try {
|
|
5717
|
-
const content =
|
|
6117
|
+
const content = fs10.readFileSync(gitignorePath, "utf8");
|
|
5718
6118
|
const gitignorePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).filter(
|
|
5719
6119
|
(line) => line.endsWith("/") || line.includes("*") || ["node_modules", "dist", "build", ".cache"].some((p) => line.includes(p))
|
|
5720
6120
|
).slice(0, 20);
|
|
@@ -5763,11 +6163,11 @@ function buildSymbolMap(symbols, purposeFiles, _rootDir) {
|
|
|
5763
6163
|
symbolMap[symbolId] = symbol.filePath;
|
|
5764
6164
|
} else {
|
|
5765
6165
|
const matchingPurpose = purposeFiles.find((pf) => {
|
|
5766
|
-
const dir =
|
|
6166
|
+
const dir = path11.dirname(pf);
|
|
5767
6167
|
return dir.toLowerCase().includes(symbol.id.toLowerCase());
|
|
5768
6168
|
});
|
|
5769
6169
|
if (matchingPurpose) {
|
|
5770
|
-
symbolMap[symbolId] =
|
|
6170
|
+
symbolMap[symbolId] = path11.dirname(matchingPurpose) + "/";
|
|
5771
6171
|
}
|
|
5772
6172
|
}
|
|
5773
6173
|
}
|
|
@@ -5778,7 +6178,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
|
|
|
5778
6178
|
const symbolToFlows = {};
|
|
5779
6179
|
for (const filePath of purposeFiles) {
|
|
5780
6180
|
try {
|
|
5781
|
-
const content =
|
|
6181
|
+
const content = fs10.readFileSync(filePath, "utf8");
|
|
5782
6182
|
const data = yaml8.load(content);
|
|
5783
6183
|
if (!data?.flows) continue;
|
|
5784
6184
|
if (Array.isArray(data.flows)) {
|
|
@@ -5792,7 +6192,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
|
|
|
5792
6192
|
id: flowId,
|
|
5793
6193
|
description: flow.description || "",
|
|
5794
6194
|
steps,
|
|
5795
|
-
definedIn:
|
|
6195
|
+
definedIn: path11.relative(rootDir, filePath)
|
|
5796
6196
|
};
|
|
5797
6197
|
indexFlowSymbols(flowId, steps, symbolToFlows);
|
|
5798
6198
|
}
|
|
@@ -5808,7 +6208,7 @@ function generateFlowIndex(rootDir, purposeFiles) {
|
|
|
5808
6208
|
trigger: flowDef.trigger,
|
|
5809
6209
|
steps,
|
|
5810
6210
|
validation: flowDef.validation,
|
|
5811
|
-
definedIn:
|
|
6211
|
+
definedIn: path11.relative(rootDir, filePath)
|
|
5812
6212
|
};
|
|
5813
6213
|
indexFlowSymbols(flowId, steps, symbolToFlows);
|
|
5814
6214
|
}
|