keating 0.3.6
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/README.md +334 -0
- package/SYSTEM.md +33 -0
- package/bin/keating.js +31 -0
- package/dist/src/cli/main.js +357 -0
- package/dist/src/cli/setup.js +197 -0
- package/dist/src/cli/web.js +84 -0
- package/dist/src/core/animation.js +304 -0
- package/dist/src/core/ax-optimizer.js +81 -0
- package/dist/src/core/ax-prompt-learner.js +59 -0
- package/dist/src/core/ax-trial.js +181 -0
- package/dist/src/core/benchmark.js +253 -0
- package/dist/src/core/commands.js +57 -0
- package/dist/src/core/config.js +120 -0
- package/dist/src/core/engagement.js +235 -0
- package/dist/src/core/env.js +9 -0
- package/dist/src/core/evolution.js +242 -0
- package/dist/src/core/flashcards.js +133 -0
- package/dist/src/core/learner-state.js +108 -0
- package/dist/src/core/lesson-plan.js +155 -0
- package/dist/src/core/map-elites.js +228 -0
- package/dist/src/core/map.js +89 -0
- package/dist/src/core/mastery.js +207 -0
- package/dist/src/core/paths.js +100 -0
- package/dist/src/core/pi-agent.js +82 -0
- package/dist/src/core/policy.js +79 -0
- package/dist/src/core/project.js +337 -0
- package/dist/src/core/projects.js +281 -0
- package/dist/src/core/prompt-evolution.js +344 -0
- package/dist/src/core/quiz.js +194 -0
- package/dist/src/core/random.js +19 -0
- package/dist/src/core/self-improve.js +425 -0
- package/dist/src/core/speech.js +54 -0
- package/dist/src/core/terminal.js +117 -0
- package/dist/src/core/theme.js +101 -0
- package/dist/src/core/topics.js +620 -0
- package/dist/src/core/types.js +1 -0
- package/dist/src/core/util.js +30 -0
- package/dist/src/core/verification.js +162 -0
- package/dist/src/pi/hyperteacher-extension.js +573 -0
- package/dist/src/runtime/pi.js +343 -0
- package/package.json +78 -0
- package/pi/prompts/bridge.md +14 -0
- package/pi/prompts/diagnose.md +15 -0
- package/pi/prompts/improve.md +39 -0
- package/pi/prompts/learn.md +21 -0
- package/pi/prompts/quiz.md +14 -0
- package/pi/skills/adaptive-teaching/SKILL.md +33 -0
- package/scripts/install/install.sh +308 -0
- package/web/dist/.well-known/llms.txt +44 -0
- package/web/dist/apple-touch-icon.svg +10 -0
- package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/web/dist/assets/_baseFor-B_cjfMB6.js +1 -0
- package/web/dist/assets/anthropic-BT6Vfzb1.js +36 -0
- package/web/dist/assets/arc-x2nTilpc.js +1 -0
- package/web/dist/assets/architecture-YZFGNWBL-B1hlUWjX.js +1 -0
- package/web/dist/assets/architectureDiagram-Q4EWVU46-CMApWFyw.js +36 -0
- package/web/dist/assets/array-B9UHiPd-.js +1 -0
- package/web/dist/assets/azure-openai-responses-CommX3YJ.js +1 -0
- package/web/dist/assets/blockDiagram-DXYQGD6D-DOQbsNRY.js +132 -0
- package/web/dist/assets/c4Diagram-AHTNJAMY-VFfRZWWA.js +10 -0
- package/web/dist/assets/channel-KY2Tg8Ba.js +1 -0
- package/web/dist/assets/chunk-2KRD3SAO-B-AqvS0u.js +1 -0
- package/web/dist/assets/chunk-336JU56O-DlYgPyl6.js +2 -0
- package/web/dist/assets/chunk-426QAEUC-CsVoBkfR.js +1 -0
- package/web/dist/assets/chunk-4BX2VUAB-0Z13aFAn.js +1 -0
- package/web/dist/assets/chunk-4TB4RGXK-DqC0Zwm7.js +206 -0
- package/web/dist/assets/chunk-55IACEB6-CWE_u-IY.js +1 -0
- package/web/dist/assets/chunk-5FUZZQ4R-CApli0xX.js +62 -0
- package/web/dist/assets/chunk-5PVQY5BW-Cbzhfhln.js +2 -0
- package/web/dist/assets/chunk-67CJDMHE-Cx7uJS4d.js +1 -0
- package/web/dist/assets/chunk-7N4EOEYR-CYPNsFus.js +1 -0
- package/web/dist/assets/chunk-AA7GKIK3-rU0uhR_u.js +1 -0
- package/web/dist/assets/chunk-BSJP7CBP-5VmcfR4-.js +1 -0
- package/web/dist/assets/chunk-Bj-mKKzh.js +1 -0
- package/web/dist/assets/chunk-CIAEETIT-CHJ-L8H1.js +1 -0
- package/web/dist/assets/chunk-EDXVE4YY-DZHAJjMI.js +1 -0
- package/web/dist/assets/chunk-ENJZ2VHE-DbUDFa7w.js +10 -0
- package/web/dist/assets/chunk-FMBD7UC4-BsYE5e_h.js +15 -0
- package/web/dist/assets/chunk-FOC6F5B3-Cm6aoTv7.js +1 -0
- package/web/dist/assets/chunk-ICPOFSXX-C5eNZ4L6.js +123 -0
- package/web/dist/assets/chunk-K5T4RW27-R7dAJ4rq.js +94 -0
- package/web/dist/assets/chunk-KGLVRYIC-MO99YZXL.js +1 -0
- package/web/dist/assets/chunk-LIHQZDEY-DUJ656sT.js +1 -0
- package/web/dist/assets/chunk-ORNJ4GCN-DXuuEC1n.js +1 -0
- package/web/dist/assets/chunk-OYMX7WX6-pJlEprWq.js +231 -0
- package/web/dist/assets/chunk-QZHKN3VN-_pQxbbiW.js +1 -0
- package/web/dist/assets/chunk-U2HBQHQK-Mh_l9PLe.js +70 -0
- package/web/dist/assets/chunk-X2U36JSP-BOeiJW0w.js +1 -0
- package/web/dist/assets/chunk-XPW4576I-fQ9SDvr_.js +32 -0
- package/web/dist/assets/chunk-YZCP3GAM-eboO4P5S.js +1 -0
- package/web/dist/assets/chunk-ZZ45TVLE-Cky0eqlr.js +1 -0
- package/web/dist/assets/classDiagram-6PBFFD2Q-DEPsZSU3.js +1 -0
- package/web/dist/assets/classDiagram-v2-HSJHXN6E-DhmIOEpX.js +1 -0
- package/web/dist/assets/clone-DeTzYqo8.js +1 -0
- package/web/dist/assets/cose-bilkent-S5V4N54A-N4zWUJ7C.js +1 -0
- package/web/dist/assets/cytoscape.esm-BBMd0vGm.js +321 -0
- package/web/dist/assets/dagre-IpK1aoMm.js +1 -0
- package/web/dist/assets/dagre-KV5264BT-DCytJuju.js +4 -0
- package/web/dist/assets/defaultLocale-5eAKkKJC.js +1 -0
- package/web/dist/assets/diagram-5BDNPKRD-Cv4miBae.js +10 -0
- package/web/dist/assets/diagram-G4DWMVQ6-CtICKUFi.js +24 -0
- package/web/dist/assets/diagram-MMDJMWI5-Cn7aGorh.js +43 -0
- package/web/dist/assets/diagram-TYMM5635-CCUWDPsC.js +24 -0
- package/web/dist/assets/dist-Dm98VvTW.js +1 -0
- package/web/dist/assets/env-api-keys-BNlMKqxw.js +1 -0
- package/web/dist/assets/erDiagram-SMLLAGMA-uT88sBlT.js +85 -0
- package/web/dist/assets/event-stream-D33K9rpL.js +1 -0
- package/web/dist/assets/flatten-C-u5nd5-.js +1 -0
- package/web/dist/assets/flowDiagram-DWJPFMVM-Bl3O7S1m.js +162 -0
- package/web/dist/assets/ganttDiagram-T4ZO3ILL-B1FhwV45.js +292 -0
- package/web/dist/assets/gitGraph-7Q5UKJZL-Bc_7vzer.js +1 -0
- package/web/dist/assets/gitGraphDiagram-UUTBAWPF-DfW6svMS.js +106 -0
- package/web/dist/assets/github-copilot-headers-L39QqneT.js +1 -0
- package/web/dist/assets/google-BdYNeCP_.js +1 -0
- package/web/dist/assets/google-gemini-cli-DpxAL3K4.js +2 -0
- package/web/dist/assets/google-shared-DyQdgtsI.js +2 -0
- package/web/dist/assets/google-vertex-CKRybaXj.js +1 -0
- package/web/dist/assets/graphlib-CMTVFyOZ.js +1 -0
- package/web/dist/assets/hash-kZ2KD_no.js +1 -0
- package/web/dist/assets/index-Bdb7P7gx.css +2 -0
- package/web/dist/assets/index-DNxepp8B.js +2891 -0
- package/web/dist/assets/info-OMHHGYJF-BGcxeaZt.js +1 -0
- package/web/dist/assets/infoDiagram-42DDH7IO-BbES7X_c.js +2 -0
- package/web/dist/assets/init-DlZdxViB.js +1 -0
- package/web/dist/assets/isEmpty-DssUW35f.js +1 -0
- package/web/dist/assets/ishikawaDiagram-UXIWVN3A-DxQ28rho.js +70 -0
- package/web/dist/assets/journeyDiagram-VCZTEJTY-D0X8qQ0P.js +139 -0
- package/web/dist/assets/json-parse-C6tSeIxX.js +2 -0
- package/web/dist/assets/kanban-definition-6JOO6SKY-DWYfSlpl.js +89 -0
- package/web/dist/assets/katex-CyM-5LlM.js +265 -0
- package/web/dist/assets/line-CuHce5JG.js +1 -0
- package/web/dist/assets/linear-Ca0Vkwuj.js +1 -0
- package/web/dist/assets/mermaid-parser.core-Cy4iY_Dy.js +4 -0
- package/web/dist/assets/mermaid.core-6PGkQdYc.js +11 -0
- package/web/dist/assets/mindmap-definition-QFDTVHPH-BBnKdtQh.js +96 -0
- package/web/dist/assets/mistral-BWaUMIgd.js +7 -0
- package/web/dist/assets/openai-D4NSaQIs.js +16 -0
- package/web/dist/assets/openai-codex-responses-CHBgKhmb.js +7 -0
- package/web/dist/assets/openai-completions-kcXmmaHI.js +5 -0
- package/web/dist/assets/openai-responses-Cqq3H3p3.js +1 -0
- package/web/dist/assets/openai-responses-shared-CTNuo9ci.js +10 -0
- package/web/dist/assets/ordinal-_K3x1fkz.js +1 -0
- package/web/dist/assets/ort-wasm-simd-threaded.jsep-B0T3yYHD.wasm +0 -0
- package/web/dist/assets/packet-4T2RLAQJ-D35ZLSBH.js +1 -0
- package/web/dist/assets/path-6uRLdFF7.js +1 -0
- package/web/dist/assets/pdf.worker.min-Cpi8b8z3.mjs +28 -0
- package/web/dist/assets/pie-ZZUOXDRM-DRoETpJX.js +1 -0
- package/web/dist/assets/pieDiagram-DEJITSTG-DfMjfTQz.js +30 -0
- package/web/dist/assets/preload-helper-DSXbuxSR.js +1 -0
- package/web/dist/assets/quadrantDiagram-34T5L4WZ-DfBSEept.js +7 -0
- package/web/dist/assets/radar-PYXPWWZC-DLKxRJ0V.js +1 -0
- package/web/dist/assets/reduce-836A2NiQ.js +1 -0
- package/web/dist/assets/requirementDiagram-MS252O5E-BPkxJQkz.js +84 -0
- package/web/dist/assets/rough.esm-Djo4Abte.js +1 -0
- package/web/dist/assets/sankeyDiagram-XADWPNL6-He3x9tNT.js +10 -0
- package/web/dist/assets/sequenceDiagram-FGHM5R23-DfCDpvrT.js +157 -0
- package/web/dist/assets/src-DdOdIreR.js +1 -0
- package/web/dist/assets/stateDiagram-FHFEXIEX-fuww6347.js +1 -0
- package/web/dist/assets/stateDiagram-v2-QKLJ7IA2-U6voafO3.js +1 -0
- package/web/dist/assets/timeline-definition-GMOUNBTQ-BWunHgBC.js +120 -0
- package/web/dist/assets/transform-messages-CqKEdRVp.js +1 -0
- package/web/dist/assets/transformers.web-DKUtmSAi.js +2818 -0
- package/web/dist/assets/treeView-SZITEDCU-BCx0xSAm.js +1 -0
- package/web/dist/assets/treemap-W4RFUUIX-2CvghWJK.js +1 -0
- package/web/dist/assets/vennDiagram-DHZGUBPP-CBXRutSP.js +34 -0
- package/web/dist/assets/wardley-RL74JXVD-BkPL_mhd.js +1 -0
- package/web/dist/assets/wardleyDiagram-NUSXRM2D-DTcVscPH.js +20 -0
- package/web/dist/assets/web-CMKYLKbT.js +10 -0
- package/web/dist/assets/xychartDiagram-5P7HB3ND-CZLgX9Fe.js +7 -0
- package/web/dist/favicon.svg +10 -0
- package/web/dist/index.html +104 -0
- package/web/dist/keating-metaharness.pdf +10557 -3
- package/web/dist/llms.txt +44 -0
- package/web/dist/logo.png +0 -0
- package/web/dist/manifest.webmanifest +1 -0
- package/web/dist/og-image.png +0 -0
- package/web/dist/pwa-192x192.svg +10 -0
- package/web/dist/pwa-512x512.svg +10 -0
- package/web/dist/registerSW.js +1 -0
- package/web/dist/robots.txt +8 -0
- package/web/dist/sitemap.xml +39 -0
- package/web/dist/sw.js +1 -0
- package/web/dist/tapes/doctor.mp4 +0 -0
- package/web/dist/tapes/feedback-flow.mp4 +0 -0
- package/web/dist/tapes/improve-flow.mp4 +0 -0
- package/web/dist/tapes/intro.mp4 +0 -0
- package/web/dist/tapes/learning-flow.mp4 +0 -0
- package/web/dist/tapes/session-flow.mp4 +0 -0
- package/web/dist/tapes/teacher-flow.mp4 +0 -0
- package/web/dist/tapes/tests.mp4 +0 -0
- package/web/dist/workbox-66610c77.js +1 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { Prng } from "./random.js";
|
|
3
|
+
import { clamp } from "./util.js";
|
|
4
|
+
import { DEFAULT_POLICY, DEFAULT_WEIGHTS, clampPolicy, clampWeights } from "./policy.js";
|
|
5
|
+
import { benchmarkToMarkdown, runBenchmarkSuite } from "./benchmark.js";
|
|
6
|
+
export const DEFAULT_DESCRIPTORS = ["formalism", "socraticRatio"];
|
|
7
|
+
export const DEFAULT_RESOLUTION = 10;
|
|
8
|
+
function mutateScalar(prng, value, amplitude = 0.18) {
|
|
9
|
+
return clamp(value + (prng.next() * 2 - 1) * amplitude);
|
|
10
|
+
}
|
|
11
|
+
function mutatePolicy(parent, prng, iteration) {
|
|
12
|
+
return clampPolicy({
|
|
13
|
+
...parent,
|
|
14
|
+
name: `me-candidate-${iteration}`,
|
|
15
|
+
analogyDensity: mutateScalar(prng, parent.analogyDensity),
|
|
16
|
+
socraticRatio: mutateScalar(prng, parent.socraticRatio),
|
|
17
|
+
formalism: mutateScalar(prng, parent.formalism),
|
|
18
|
+
retrievalPractice: mutateScalar(prng, parent.retrievalPractice),
|
|
19
|
+
exerciseCount: parent.exerciseCount + prng.int(-1, 1),
|
|
20
|
+
diagramBias: mutateScalar(prng, parent.diagramBias),
|
|
21
|
+
reflectionBias: mutateScalar(prng, parent.reflectionBias),
|
|
22
|
+
interdisciplinaryBias: mutateScalar(prng, parent.interdisciplinaryBias),
|
|
23
|
+
challengeRate: mutateScalar(prng, parent.challengeRate)
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function mutateWeights(parent, prng, amplitude = 0.12) {
|
|
27
|
+
return clampWeights({
|
|
28
|
+
masteryGain: mutateScalar(prng, parent.masteryGain, amplitude),
|
|
29
|
+
retention: mutateScalar(prng, parent.retention, amplitude),
|
|
30
|
+
engagement: mutateScalar(prng, parent.engagement, amplitude),
|
|
31
|
+
transfer: mutateScalar(prng, parent.transfer, amplitude),
|
|
32
|
+
confusion: mutateScalar(prng, parent.confusion, amplitude)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function getDescriptorValues(policy, descriptors) {
|
|
36
|
+
return descriptors.map((d) => {
|
|
37
|
+
const val = policy[d];
|
|
38
|
+
return typeof val === "number" ? val : 0;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function cellKey(descriptors, resolution) {
|
|
42
|
+
return descriptors
|
|
43
|
+
.map((d) => Math.min(Math.floor(d * resolution), resolution - 1))
|
|
44
|
+
.join(",");
|
|
45
|
+
}
|
|
46
|
+
export function createGrid(descriptors, resolution) {
|
|
47
|
+
return {
|
|
48
|
+
descriptors,
|
|
49
|
+
resolution,
|
|
50
|
+
cells: new Map()
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function placeInGrid(grid, policy, weights, score, benchmark, iteration) {
|
|
54
|
+
const descVals = getDescriptorValues(policy, grid.descriptors);
|
|
55
|
+
const key = cellKey(descVals, grid.resolution);
|
|
56
|
+
const existing = grid.cells.get(key);
|
|
57
|
+
if (!existing || score > existing.score) {
|
|
58
|
+
grid.cells.set(key, { policy, weights, score, benchmark, iteration });
|
|
59
|
+
return !existing;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
function selectParent(grid, prng) {
|
|
64
|
+
const filled = Array.from(grid.cells.values()).filter((c) => c !== null);
|
|
65
|
+
if (filled.length === 0) {
|
|
66
|
+
return { policy: DEFAULT_POLICY, weights: DEFAULT_WEIGHTS };
|
|
67
|
+
}
|
|
68
|
+
const idx = Math.floor(prng.next() * filled.length);
|
|
69
|
+
return { policy: filled[idx].policy, weights: filled[idx].weights };
|
|
70
|
+
}
|
|
71
|
+
function randomPolicy(prng, iteration) {
|
|
72
|
+
return clampPolicy({
|
|
73
|
+
name: `me-random-${iteration}`,
|
|
74
|
+
analogyDensity: prng.next(),
|
|
75
|
+
socraticRatio: prng.next(),
|
|
76
|
+
formalism: prng.next(),
|
|
77
|
+
retrievalPractice: prng.next(),
|
|
78
|
+
exerciseCount: Math.round(1 + prng.next() * 4),
|
|
79
|
+
diagramBias: prng.next(),
|
|
80
|
+
reflectionBias: prng.next(),
|
|
81
|
+
interdisciplinaryBias: prng.next(),
|
|
82
|
+
challengeRate: prng.next()
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function randomWeights(prng) {
|
|
86
|
+
return clampWeights({
|
|
87
|
+
masteryGain: 0.1 + prng.next() * 0.9,
|
|
88
|
+
retention: 0.1 + prng.next() * 0.9,
|
|
89
|
+
engagement: 0.1 + prng.next() * 0.9,
|
|
90
|
+
transfer: 0.1 + prng.next() * 0.9,
|
|
91
|
+
confusion: 0.1 + prng.next() * 0.9
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export async function mapElitesEvolve(cwd, basePolicy, options = {}) {
|
|
95
|
+
const { iterations = 48, seed = 20260401, descriptors = DEFAULT_DESCRIPTORS, resolution = DEFAULT_RESOLUTION, focusTopic, initRandom = Math.floor(iterations * 0.25) } = options;
|
|
96
|
+
const prng = new Prng(seed);
|
|
97
|
+
const grid = createGrid(descriptors, resolution);
|
|
98
|
+
const totalCells = resolution ** descriptors.length;
|
|
99
|
+
const baseline = await runBenchmarkSuite(cwd, basePolicy, focusTopic, seed, 3, DEFAULT_WEIGHTS);
|
|
100
|
+
placeInGrid(grid, basePolicy, DEFAULT_WEIGHTS, baseline.overallScore, baseline, 0);
|
|
101
|
+
const exploredCandidates = [];
|
|
102
|
+
for (let i = 1; i <= iterations; i++) {
|
|
103
|
+
let candidatePolicy;
|
|
104
|
+
let candidateWeights;
|
|
105
|
+
if (i <= initRandom) {
|
|
106
|
+
candidatePolicy = randomPolicy(prng, i);
|
|
107
|
+
candidateWeights = randomWeights(prng);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
const parent = selectParent(grid, prng);
|
|
111
|
+
candidatePolicy = mutatePolicy(parent.policy, prng, i);
|
|
112
|
+
candidateWeights = mutateWeights(parent.weights, prng);
|
|
113
|
+
}
|
|
114
|
+
const candidateBenchmark = await runBenchmarkSuite(cwd, candidatePolicy, focusTopic, seed + i * 11, 3, candidateWeights);
|
|
115
|
+
const isNewCell = placeInGrid(grid, candidatePolicy, candidateWeights, candidateBenchmark.overallScore, candidateBenchmark, i);
|
|
116
|
+
exploredCandidates.push({
|
|
117
|
+
policy: candidatePolicy,
|
|
118
|
+
benchmark: candidateBenchmark,
|
|
119
|
+
parentName: null,
|
|
120
|
+
iteration: i,
|
|
121
|
+
novelty: isNewCell ? 1 : 0,
|
|
122
|
+
accepted: isNewCell,
|
|
123
|
+
decision: {
|
|
124
|
+
improves: isNewCell,
|
|
125
|
+
safe: true,
|
|
126
|
+
novelEnough: isNewCell,
|
|
127
|
+
scoreDelta: 0,
|
|
128
|
+
weakestTopicDelta: 0,
|
|
129
|
+
reasons: isNewCell
|
|
130
|
+
? [`placed in new cell or improved existing cell (score ${candidateBenchmark.overallScore.toFixed(2)})`]
|
|
131
|
+
: [`discarded — cell already held a better elite`]
|
|
132
|
+
},
|
|
133
|
+
parameterDelta: []
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
let best = baseline;
|
|
137
|
+
for (const cell of grid.cells.values()) {
|
|
138
|
+
if (cell && cell.benchmark.overallScore > best.overallScore) {
|
|
139
|
+
best = cell.benchmark;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
baseline,
|
|
144
|
+
best,
|
|
145
|
+
grid,
|
|
146
|
+
filledCellCount: grid.cells.size,
|
|
147
|
+
totalCells,
|
|
148
|
+
exploredCandidates
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export function mapElitesToMarkdown(run) {
|
|
152
|
+
const lines = [
|
|
153
|
+
"# MAP-Elites Evolution Report",
|
|
154
|
+
"",
|
|
155
|
+
`- Descriptors: ${run.grid.descriptors.join(" × ")}`,
|
|
156
|
+
`- Grid: ${run.grid.resolution}^${run.grid.descriptors.length} = ${run.totalCells} cells`,
|
|
157
|
+
`- Filled cells: ${run.filledCellCount} / ${run.totalCells} (${((run.filledCellCount / run.totalCells) * 100).toFixed(1)}%)`,
|
|
158
|
+
`- Baseline score: ${run.baseline.overallScore.toFixed(2)}`,
|
|
159
|
+
`- Best score: ${run.best.overallScore.toFixed(2)}`,
|
|
160
|
+
`- Explored candidates: ${run.exploredCandidates.length}`,
|
|
161
|
+
""
|
|
162
|
+
];
|
|
163
|
+
lines.push("## Elite Archive");
|
|
164
|
+
lines.push("");
|
|
165
|
+
const sorted = Array.from(run.grid.cells.entries())
|
|
166
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
167
|
+
const header = run.grid.descriptors.map((d, i) => `${d}[${i}]`).join(" | ");
|
|
168
|
+
lines.push(`| ${header} | Policy | Score | Weights (m/r/e/t/c) |`);
|
|
169
|
+
lines.push(`| ${run.grid.descriptors.map(() => "---").join(" | ")} | --- | ---: | --- |`);
|
|
170
|
+
for (const [key, cell] of sorted) {
|
|
171
|
+
if (!cell)
|
|
172
|
+
continue;
|
|
173
|
+
const indices = key.split(",").map(Number);
|
|
174
|
+
const labels = indices.map((idx, i) => {
|
|
175
|
+
const lo = (idx / run.grid.resolution).toFixed(2);
|
|
176
|
+
const hi = ((idx + 1) / run.grid.resolution).toFixed(2);
|
|
177
|
+
return `${lo}–${hi}`;
|
|
178
|
+
}).join(" | ");
|
|
179
|
+
const w = cell.weights;
|
|
180
|
+
lines.push(`| ${labels} | ${cell.policy.name} | ${cell.score.toFixed(2)} | ${w.masteryGain.toFixed(2)}/${w.retention.toFixed(2)}/${w.engagement.toFixed(2)}/${w.transfer.toFixed(2)}/${w.confusion.toFixed(2)} |`);
|
|
181
|
+
}
|
|
182
|
+
lines.push("");
|
|
183
|
+
lines.push("## Best Benchmark Snapshot");
|
|
184
|
+
lines.push("");
|
|
185
|
+
lines.push(benchmarkToMarkdown(run.best).trim());
|
|
186
|
+
lines.push("");
|
|
187
|
+
return `${lines.join("\n")}\n`;
|
|
188
|
+
}
|
|
189
|
+
export async function loadMapElitesGrid(filePath, descriptors, resolution) {
|
|
190
|
+
try {
|
|
191
|
+
const content = await readFile(filePath, "utf8");
|
|
192
|
+
const data = JSON.parse(content);
|
|
193
|
+
const grid = createGrid(descriptors, resolution);
|
|
194
|
+
for (const [key, cell] of Object.entries(data.cells ?? {})) {
|
|
195
|
+
grid.cells.set(key, cell);
|
|
196
|
+
}
|
|
197
|
+
return grid;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return createGrid(descriptors, resolution);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export async function saveMapElitesGrid(filePath, grid) {
|
|
204
|
+
const serializable = {};
|
|
205
|
+
for (const [key, cell] of grid.cells.entries()) {
|
|
206
|
+
serializable[key] = cell;
|
|
207
|
+
}
|
|
208
|
+
await writeFile(filePath, `${JSON.stringify({ descriptors: grid.descriptors, resolution: grid.resolution, cells: serializable }, null, 2)}\n`, "utf8");
|
|
209
|
+
}
|
|
210
|
+
export function mapElitesToEvolutionRun(run) {
|
|
211
|
+
return {
|
|
212
|
+
baseline: run.baseline,
|
|
213
|
+
best: run.best,
|
|
214
|
+
acceptedCandidates: run.exploredCandidates.filter((c) => c.accepted),
|
|
215
|
+
exploredCandidates: run.exploredCandidates,
|
|
216
|
+
archive: {
|
|
217
|
+
currentPolicy: run.best.policy,
|
|
218
|
+
bestScore: run.best.overallScore,
|
|
219
|
+
candidates: run.exploredCandidates.map((c) => ({
|
|
220
|
+
policy: c.policy,
|
|
221
|
+
score: c.benchmark.overallScore,
|
|
222
|
+
novelty: c.novelty,
|
|
223
|
+
accepted: c.accepted,
|
|
224
|
+
iteration: c.iteration
|
|
225
|
+
}))
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { buildLessonPlan } from "./lesson-plan.js";
|
|
5
|
+
import { mapsDir } from "./paths.js";
|
|
6
|
+
import { slugify } from "./util.js";
|
|
7
|
+
function mermaidLabel(text) {
|
|
8
|
+
return text.replaceAll('"', "'").replaceAll("\n", " ");
|
|
9
|
+
}
|
|
10
|
+
export function lessonPlanToMermaid(topicName, policy) {
|
|
11
|
+
const plan = buildLessonPlan(topicName, policy);
|
|
12
|
+
const topicId = slugify(plan.topic.title);
|
|
13
|
+
const lines = [
|
|
14
|
+
"graph TD",
|
|
15
|
+
" classDef phase fill:#12263f,stroke:#8db5ff,color:#f8f5ec,stroke-width:1px;",
|
|
16
|
+
" classDef concept fill:#173122,stroke:#8fd7b6,color:#f8f5ec,stroke-width:1px;",
|
|
17
|
+
" classDef friction fill:#3a2317,stroke:#ff9b6b,color:#f8f5ec,stroke-width:1px;",
|
|
18
|
+
" classDef transfer fill:#2f2140,stroke:#d3a6ff,color:#f8f5ec,stroke-width:1px;",
|
|
19
|
+
` learner(("Learner state"))`,
|
|
20
|
+
` thesis["${mermaidLabel(plan.topic.title)}: ${mermaidLabel(plan.topic.summary)}"]`
|
|
21
|
+
];
|
|
22
|
+
lines.push(' subgraph pedagogy["Teaching Loop"]');
|
|
23
|
+
for (const phase of plan.phases) {
|
|
24
|
+
lines.push(` ${phase.id}["${mermaidLabel(phase.title)}"]`);
|
|
25
|
+
}
|
|
26
|
+
for (let index = 0; index < plan.phases.length - 1; index += 1) {
|
|
27
|
+
lines.push(` ${plan.phases[index].id} --> ${plan.phases[index + 1].id}`);
|
|
28
|
+
}
|
|
29
|
+
lines.push(" end");
|
|
30
|
+
lines.push(' subgraph meaning["Meaning Map"]');
|
|
31
|
+
lines.push(` ${topicId}_core["${mermaidLabel(plan.topic.title)}"]`);
|
|
32
|
+
for (const [index, node] of plan.topic.diagramNodes.entries()) {
|
|
33
|
+
lines.push(` ${topicId}_concept_${index}["${mermaidLabel(node)}"]`);
|
|
34
|
+
lines.push(` ${topicId}_core --> ${topicId}_concept_${index}`);
|
|
35
|
+
}
|
|
36
|
+
for (const [index, prerequisite] of plan.topic.prerequisites.slice(0, 3).entries()) {
|
|
37
|
+
lines.push(` ${topicId}_prereq_${index}["Prereq: ${mermaidLabel(prerequisite)}"]`);
|
|
38
|
+
lines.push(` ${topicId}_prereq_${index} --> ${topicId}_core`);
|
|
39
|
+
}
|
|
40
|
+
lines.push(" end");
|
|
41
|
+
lines.push(' subgraph friction["Misconceptions And Practice"]');
|
|
42
|
+
for (const [index, misconception] of plan.topic.misconceptions.slice(0, 2).entries()) {
|
|
43
|
+
lines.push(` ${topicId}_mis_${index}["${mermaidLabel(misconception)}"]`);
|
|
44
|
+
lines.push(` ${topicId}_core --> ${topicId}_mis_${index}`);
|
|
45
|
+
}
|
|
46
|
+
for (const [index, exercise] of plan.topic.exercises.slice(0, 2).entries()) {
|
|
47
|
+
lines.push(` ${topicId}_exercise_${index}["Practice: ${mermaidLabel(exercise)}"]`);
|
|
48
|
+
lines.push(` ${topicId}_mis_${Math.min(index, Math.max(plan.topic.misconceptions.length - 1, 0))} --> ${topicId}_exercise_${index}`);
|
|
49
|
+
}
|
|
50
|
+
lines.push(" end");
|
|
51
|
+
lines.push(' subgraph transfer["Transfer Hooks"]');
|
|
52
|
+
for (const [index, hook] of plan.topic.interdisciplinaryHooks.entries()) {
|
|
53
|
+
lines.push(` ${topicId}_hook_${index}["${mermaidLabel(hook)}"]`);
|
|
54
|
+
lines.push(` ${topicId}_core --> ${topicId}_hook_${index}`);
|
|
55
|
+
}
|
|
56
|
+
lines.push(" end");
|
|
57
|
+
lines.push(" learner --> orient");
|
|
58
|
+
lines.push(" thesis --> orient");
|
|
59
|
+
lines.push(` ${plan.phases.at(-1).id} --> ${topicId}_hook_0`);
|
|
60
|
+
lines.push(` ${plan.phases.find((phase) => phase.id === "diagram")?.id ?? "examples"} --> ${topicId}_core`);
|
|
61
|
+
lines.push(` class ${plan.phases.map((phase) => phase.id).join(",")} phase;`);
|
|
62
|
+
lines.push(` class ${topicId}_core,${plan.topic.diagramNodes.map((_, index) => `${topicId}_concept_${index}`).join(",")},${plan.topic.prerequisites.slice(0, 3).map((_, index) => `${topicId}_prereq_${index}`).join(",")} concept;`);
|
|
63
|
+
const frictionNodes = [
|
|
64
|
+
...plan.topic.misconceptions.slice(0, 2).map((_, index) => `${topicId}_mis_${index}`),
|
|
65
|
+
...plan.topic.exercises.slice(0, 2).map((_, index) => `${topicId}_exercise_${index}`)
|
|
66
|
+
];
|
|
67
|
+
if (frictionNodes.length > 0) {
|
|
68
|
+
lines.push(` class ${frictionNodes.join(",")} friction;`);
|
|
69
|
+
}
|
|
70
|
+
const transferNodes = plan.topic.interdisciplinaryHooks.map((_, index) => `${topicId}_hook_${index}`);
|
|
71
|
+
if (transferNodes.length > 0) {
|
|
72
|
+
lines.push(` class ${transferNodes.join(",")} transfer;`);
|
|
73
|
+
}
|
|
74
|
+
return `${lines.join("\n")}\n`;
|
|
75
|
+
}
|
|
76
|
+
export async function writeLessonMap(cwd, topicName, policy) {
|
|
77
|
+
const slug = slugify(topicName);
|
|
78
|
+
const mmdPath = join(mapsDir(cwd), `${slug}.mmd`);
|
|
79
|
+
const svgPath = join(mapsDir(cwd), `${slug}.svg`);
|
|
80
|
+
await writeFile(mmdPath, lessonPlanToMermaid(topicName, policy), "utf8");
|
|
81
|
+
const render = spawnSync("oxdraw", ["--input", mmdPath, "--output", svgPath], {
|
|
82
|
+
cwd,
|
|
83
|
+
encoding: "utf8"
|
|
84
|
+
});
|
|
85
|
+
if (render.status === 0) {
|
|
86
|
+
return { mmdPath, svgPath };
|
|
87
|
+
}
|
|
88
|
+
return { mmdPath, svgPath: null };
|
|
89
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastery Assessment Engine
|
|
3
|
+
*
|
|
4
|
+
* Generates multi-level diagnostic questions, scores learner responses,
|
|
5
|
+
* and produces a mastery report with per-dimension breakdown.
|
|
6
|
+
*/
|
|
7
|
+
import { resolveTopic } from "./topics.js";
|
|
8
|
+
import { clamp } from "./util.js";
|
|
9
|
+
export function generateDiagnosticQuestions(topic) {
|
|
10
|
+
const questions = [];
|
|
11
|
+
// Recall level (Blooms taxonomy L1)
|
|
12
|
+
questions.push({
|
|
13
|
+
id: `${topic.slug}-q1`,
|
|
14
|
+
level: "recall",
|
|
15
|
+
question: `State the definition of "${topic.title}" in your own words.`,
|
|
16
|
+
rubric: `1pt: attempts definition. 2pts: core elements present. 3pts: precise, recognizes nuance. Max ${3} points.`,
|
|
17
|
+
maxPoints: 3
|
|
18
|
+
});
|
|
19
|
+
// Recall from formal core
|
|
20
|
+
questions.push({
|
|
21
|
+
id: `${topic.slug}-q2`,
|
|
22
|
+
level: "recall",
|
|
23
|
+
question: `What are the key prerequisites needed before a learner can grasp ${topic.title}?`,
|
|
24
|
+
rubric: `1pt: mentions prerequisites. 2pts: explains connection. 3pts: correct full list. Max ${3} points.`,
|
|
25
|
+
maxPoints: 3
|
|
26
|
+
});
|
|
27
|
+
// Comprehension (Bloom L2)
|
|
28
|
+
const intuitionHook = topic.intuition[0] ?? "the core idea";
|
|
29
|
+
questions.push({
|
|
30
|
+
id: `${topic.slug}-q3`,
|
|
31
|
+
level: "comprehension",
|
|
32
|
+
question: `Explain why "${intuitionHook}" is a useful way to think about ${topic.title}.`,
|
|
33
|
+
rubric: `1pt: repeats hook. 2pts: connects to structure. 3pts: sees limitations and when it breaks. Max ${3} points.`,
|
|
34
|
+
maxPoints: 3
|
|
35
|
+
});
|
|
36
|
+
// Common misconception
|
|
37
|
+
const misconception = topic.misconceptions[0] ?? "the common error";
|
|
38
|
+
questions.push({
|
|
39
|
+
id: `${topic.slug}-q4`,
|
|
40
|
+
level: "comprehension",
|
|
41
|
+
question: `Many learners think: "${misconception}". Explain why this is incorrect.`,
|
|
42
|
+
rubric: `1pt: says it is wrong. 2pts: identifies the subtle issue. 3pts: gives a counterexample. Max ${3} points.`,
|
|
43
|
+
maxPoints: 3
|
|
44
|
+
});
|
|
45
|
+
// Application (Bloom L3)
|
|
46
|
+
const example = topic.examples[0] ?? "a basic scenario";
|
|
47
|
+
questions.push({
|
|
48
|
+
id: `${topic.slug}-q5`,
|
|
49
|
+
level: "application",
|
|
50
|
+
question: `Apply ${topic.title} to a new situation: ${example}`,
|
|
51
|
+
rubric: `1pt: attempts application. 2pts: correct mechanics, minor gaps. 3pts: fully correct with explanation. Max ${3} points.`,
|
|
52
|
+
maxPoints: 3
|
|
53
|
+
});
|
|
54
|
+
// Transfer (Bloom L5)
|
|
55
|
+
const hook = topic.interdisciplinaryHooks[0] ?? "another field";
|
|
56
|
+
questions.push({
|
|
57
|
+
id: `${topic.slug}-q6`,
|
|
58
|
+
level: "transfer",
|
|
59
|
+
question: `How might ${topic.title} be relevant in ${hook}? Construct an analogy.`,
|
|
60
|
+
rubric: `1pt: mentions connection. 2pts: coherent analogy. 3pts: analogy holds under scrutiny. Max ${3} points.`,
|
|
61
|
+
maxPoints: 3
|
|
62
|
+
});
|
|
63
|
+
return questions;
|
|
64
|
+
}
|
|
65
|
+
function classifyLevel(score, max) {
|
|
66
|
+
const ratio = score / max;
|
|
67
|
+
if (ratio >= 0.9)
|
|
68
|
+
return "expert";
|
|
69
|
+
if (ratio >= 0.7)
|
|
70
|
+
return "proficient";
|
|
71
|
+
if (ratio >= 0.5)
|
|
72
|
+
return "competent";
|
|
73
|
+
if (ratio >= 0.3)
|
|
74
|
+
return "beginner";
|
|
75
|
+
return "novice";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Score an answer heuristically (0–maxPoints).
|
|
79
|
+
* In practice an LLM should do this, but this provides deterministic defaults.
|
|
80
|
+
*/
|
|
81
|
+
export function scoreAnswer(question, answer) {
|
|
82
|
+
const trimmed = answer.trim();
|
|
83
|
+
if (trimmed.length === 0)
|
|
84
|
+
return 0;
|
|
85
|
+
if (trimmed.length < 20)
|
|
86
|
+
return 1; // Very short
|
|
87
|
+
// Medium-fidelity heuristic: longer + key topic words = higher
|
|
88
|
+
const topicWords = question.question.toLowerCase().split(/[^a-z]+/).filter(w => w.length > 3);
|
|
89
|
+
const matches = topicWords.filter(w => trimmed.toLowerCase().includes(w)).length;
|
|
90
|
+
const ratio = matches / Math.max(1, topicWords.length);
|
|
91
|
+
if (ratio > 0.5 && trimmed.length > 100)
|
|
92
|
+
return question.maxPoints;
|
|
93
|
+
if (ratio > 0.3 && trimmed.length > 50)
|
|
94
|
+
return Math.min(question.maxPoints, 2);
|
|
95
|
+
return Math.min(question.maxPoints, 2);
|
|
96
|
+
}
|
|
97
|
+
export function computeMasteryAssessment(topicName, answers) {
|
|
98
|
+
const topic = resolveTopic(topicName);
|
|
99
|
+
const questions = generateDiagnosticQuestions(topic);
|
|
100
|
+
const dimensions = [];
|
|
101
|
+
const gaps = [];
|
|
102
|
+
const strengths = [];
|
|
103
|
+
let totalScore = 0;
|
|
104
|
+
let totalMax = 0;
|
|
105
|
+
// Group by level
|
|
106
|
+
const levelGroups = {
|
|
107
|
+
recall: "Factual Recall",
|
|
108
|
+
comprehension: "Conceptual Understanding",
|
|
109
|
+
application: "Application",
|
|
110
|
+
analysis: "Analysis",
|
|
111
|
+
transfer: "Transfer"
|
|
112
|
+
};
|
|
113
|
+
for (const [level, dimName] of Object.entries(levelGroups)) {
|
|
114
|
+
const levelQs = questions.filter(q => q.level === level);
|
|
115
|
+
if (levelQs.length === 0)
|
|
116
|
+
continue;
|
|
117
|
+
let dimScore = 0;
|
|
118
|
+
let dimMax = 0;
|
|
119
|
+
const dimQuestions = [];
|
|
120
|
+
for (const q of levelQs) {
|
|
121
|
+
const answer = answers[q.id] ?? "";
|
|
122
|
+
const score = scoreAnswer(q, answer);
|
|
123
|
+
dimScore += score;
|
|
124
|
+
dimMax += q.maxPoints;
|
|
125
|
+
dimQuestions.push(q);
|
|
126
|
+
}
|
|
127
|
+
totalScore += dimScore;
|
|
128
|
+
totalMax += dimMax;
|
|
129
|
+
const recs = [];
|
|
130
|
+
const ratio = dimMax > 0 ? dimScore / dimMax : 0;
|
|
131
|
+
if (ratio < 0.5) {
|
|
132
|
+
recs.push(`Strengthen ${dimName.toLowerCase()} through targeted practice.`);
|
|
133
|
+
gaps.push(dimName);
|
|
134
|
+
}
|
|
135
|
+
else if (ratio >= 0.8) {
|
|
136
|
+
strengths.push(dimName);
|
|
137
|
+
}
|
|
138
|
+
dimensions.push({
|
|
139
|
+
name: dimName,
|
|
140
|
+
score: dimScore,
|
|
141
|
+
maxScore: dimMax,
|
|
142
|
+
questions: dimQuestions,
|
|
143
|
+
recommendations: recs
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const overall = clamp(totalScore / Math.max(1, totalMax), 0, 1);
|
|
147
|
+
const nextSteps = [];
|
|
148
|
+
if (gaps.length > 0) {
|
|
149
|
+
nextSteps.push(`Focus on: ${gaps.join(", ")}.`);
|
|
150
|
+
}
|
|
151
|
+
if (strengths.length > 0) {
|
|
152
|
+
nextSteps.push(`Your strengths (${strengths.join(", ")}) are solid — leverage them for harder transfer problems.`);
|
|
153
|
+
}
|
|
154
|
+
if (gaps.length === 0 && strengths.length === dimensions.length) {
|
|
155
|
+
nextSteps.push("You score highly across all dimensions. Try synthesis-level questions or teach the topic to someone else.");
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
topic: topic.title,
|
|
159
|
+
overallScore: totalScore,
|
|
160
|
+
maxScore: totalMax,
|
|
161
|
+
level: classifyLevel(totalScore, totalMax),
|
|
162
|
+
dimensions,
|
|
163
|
+
gaps,
|
|
164
|
+
strengths,
|
|
165
|
+
nextSteps
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export function masteryAssessmentToMarkdown(ass) {
|
|
169
|
+
const lines = [
|
|
170
|
+
`# Mastery Assessment: ${ass.topic}`,
|
|
171
|
+
"",
|
|
172
|
+
`**Level**: ${ass.level.toUpperCase()} (${((ass.overallScore / ass.maxScore) * 100).toFixed(1)}%)`,
|
|
173
|
+
`**Score**: ${ass.overallScore} / ${ass.maxScore}`,
|
|
174
|
+
""
|
|
175
|
+
];
|
|
176
|
+
lines.push("## Dimension Breakdown");
|
|
177
|
+
lines.push("");
|
|
178
|
+
for (const d of ass.dimensions) {
|
|
179
|
+
const pct = ((d.score / d.maxScore) * 100).toFixed(0);
|
|
180
|
+
lines.push(`### ${d.name}: ${d.score}/${d.maxScore} (${pct}%)`);
|
|
181
|
+
for (const q of d.questions) {
|
|
182
|
+
lines.push(`- **${q.id}** (${q.level}): ${q.question}`);
|
|
183
|
+
lines.push(` - Rubric: ${q.rubric}`);
|
|
184
|
+
}
|
|
185
|
+
if (d.recommendations.length > 0) {
|
|
186
|
+
lines.push(` - *Recommendation:* ${d.recommendations[0]}`);
|
|
187
|
+
}
|
|
188
|
+
lines.push("");
|
|
189
|
+
}
|
|
190
|
+
if (ass.gaps.length > 0) {
|
|
191
|
+
lines.push("## Gaps Identified");
|
|
192
|
+
for (const g of ass.gaps)
|
|
193
|
+
lines.push(`- ${g}`);
|
|
194
|
+
lines.push("");
|
|
195
|
+
}
|
|
196
|
+
if (ass.strengths.length > 0) {
|
|
197
|
+
lines.push("## Strengths");
|
|
198
|
+
for (const s of ass.strengths)
|
|
199
|
+
lines.push(`- ${s}`);
|
|
200
|
+
lines.push("");
|
|
201
|
+
}
|
|
202
|
+
lines.push("## Next Steps");
|
|
203
|
+
for (const ns of ass.nextSteps)
|
|
204
|
+
lines.push(`- ${ns}`);
|
|
205
|
+
lines.push("");
|
|
206
|
+
return lines.join("\n");
|
|
207
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function keatingRoot(cwd) {
|
|
4
|
+
return join(cwd, ".keating");
|
|
5
|
+
}
|
|
6
|
+
export function stateDir(cwd) {
|
|
7
|
+
return join(keatingRoot(cwd), "state");
|
|
8
|
+
}
|
|
9
|
+
export function outputsDir(cwd) {
|
|
10
|
+
return join(keatingRoot(cwd), "outputs");
|
|
11
|
+
}
|
|
12
|
+
export function plansDir(cwd) {
|
|
13
|
+
return join(outputsDir(cwd), "plans");
|
|
14
|
+
}
|
|
15
|
+
export function mapsDir(cwd) {
|
|
16
|
+
return join(outputsDir(cwd), "maps");
|
|
17
|
+
}
|
|
18
|
+
export function animationsDir(cwd) {
|
|
19
|
+
return join(outputsDir(cwd), "animations");
|
|
20
|
+
}
|
|
21
|
+
export function benchmarksDir(cwd) {
|
|
22
|
+
return join(outputsDir(cwd), "benchmarks");
|
|
23
|
+
}
|
|
24
|
+
export function evolutionDir(cwd) {
|
|
25
|
+
return join(outputsDir(cwd), "evolution");
|
|
26
|
+
}
|
|
27
|
+
export function promptEvolutionDir(cwd) {
|
|
28
|
+
return join(outputsDir(cwd), "prompt-evolution");
|
|
29
|
+
}
|
|
30
|
+
export function tracesDir(cwd) {
|
|
31
|
+
return join(outputsDir(cwd), "traces");
|
|
32
|
+
}
|
|
33
|
+
export function sessionsDir(cwd) {
|
|
34
|
+
return join(keatingRoot(cwd), "sessions");
|
|
35
|
+
}
|
|
36
|
+
export function configDir(cwd) {
|
|
37
|
+
return join(keatingRoot(cwd), "pi-config");
|
|
38
|
+
}
|
|
39
|
+
export function currentPolicyPath(cwd) {
|
|
40
|
+
return join(stateDir(cwd), "current-policy.json");
|
|
41
|
+
}
|
|
42
|
+
export function policyArchivePath(cwd) {
|
|
43
|
+
return join(stateDir(cwd), "policy-archive.json");
|
|
44
|
+
}
|
|
45
|
+
export function promptEvolutionArchivePath(cwd) {
|
|
46
|
+
return join(stateDir(cwd), "prompt-evolution-archive.json");
|
|
47
|
+
}
|
|
48
|
+
export function learnerStatePath(cwd) {
|
|
49
|
+
return join(stateDir(cwd), "learner.json");
|
|
50
|
+
}
|
|
51
|
+
export function verificationsDir(cwd) {
|
|
52
|
+
return join(outputsDir(cwd), "verifications");
|
|
53
|
+
}
|
|
54
|
+
export function verificationCachePath(cwd) {
|
|
55
|
+
return join(stateDir(cwd), "verification-cache.json");
|
|
56
|
+
}
|
|
57
|
+
export function engagementPolicyPath(cwd) {
|
|
58
|
+
return join(stateDir(cwd), "engagement-policy.json");
|
|
59
|
+
}
|
|
60
|
+
export function timelineDir(cwd) {
|
|
61
|
+
return join(outputsDir(cwd), "timelines");
|
|
62
|
+
}
|
|
63
|
+
export function quizDir(cwd) {
|
|
64
|
+
return join(outputsDir(cwd), "quizzes");
|
|
65
|
+
}
|
|
66
|
+
export function flashcardsDir(cwd) {
|
|
67
|
+
return join(outputsDir(cwd), "flashcards");
|
|
68
|
+
}
|
|
69
|
+
export function projectsDir(cwd) {
|
|
70
|
+
return join(outputsDir(cwd), "projects");
|
|
71
|
+
}
|
|
72
|
+
export function masteryDir(cwd) {
|
|
73
|
+
return join(outputsDir(cwd), "mastery");
|
|
74
|
+
}
|
|
75
|
+
export function workbooksDir(cwd) {
|
|
76
|
+
return join(outputsDir(cwd), "workbooks");
|
|
77
|
+
}
|
|
78
|
+
export async function ensureKeatingDirs(cwd) {
|
|
79
|
+
await Promise.all([
|
|
80
|
+
mkdir(stateDir(cwd), { recursive: true }),
|
|
81
|
+
mkdir(join(stateDir(cwd), "snapshots"), { recursive: true }),
|
|
82
|
+
mkdir(plansDir(cwd), { recursive: true }),
|
|
83
|
+
mkdir(mapsDir(cwd), { recursive: true }),
|
|
84
|
+
mkdir(animationsDir(cwd), { recursive: true }),
|
|
85
|
+
mkdir(benchmarksDir(cwd), { recursive: true }),
|
|
86
|
+
mkdir(evolutionDir(cwd), { recursive: true }),
|
|
87
|
+
mkdir(promptEvolutionDir(cwd), { recursive: true }),
|
|
88
|
+
mkdir(tracesDir(cwd), { recursive: true }),
|
|
89
|
+
mkdir(verificationsDir(cwd), { recursive: true }),
|
|
90
|
+
mkdir(join(outputsDir(cwd), "improvements"), { recursive: true }),
|
|
91
|
+
mkdir(sessionsDir(cwd), { recursive: true }),
|
|
92
|
+
mkdir(timelineDir(cwd), { recursive: true }),
|
|
93
|
+
mkdir(quizDir(cwd), { recursive: true }),
|
|
94
|
+
mkdir(flashcardsDir(cwd), { recursive: true }),
|
|
95
|
+
mkdir(projectsDir(cwd), { recursive: true }),
|
|
96
|
+
mkdir(masteryDir(cwd), { recursive: true }),
|
|
97
|
+
mkdir(workbooksDir(cwd), { recursive: true }),
|
|
98
|
+
mkdir(configDir(cwd), { recursive: true })
|
|
99
|
+
]);
|
|
100
|
+
}
|