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,573 @@
|
|
|
1
|
+
import { relative } from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { animateTopicArtifact, autoImproveArtifact, benchPolicyArtifact, promptEvalArtifact, currentPolicySummary, dueTopicsArtifact, ensureProjectScaffold, evolvePolicyArtifact, evolvePromptArtifact, improveArtifact, improveHistory, listArtifacts, mapTopicArtifact, planTopicArtifact, timelineArtifact, verifyTopicArtifact } from "../core/project.js";
|
|
4
|
+
import { learnerStatePath } from "../core/paths.js";
|
|
5
|
+
import { loadLearnerState, recordFeedback, recordSessionStart, saveLearnerState } from "../core/learner-state.js";
|
|
6
|
+
import { loadKeatingConfig } from "../core/config.js";
|
|
7
|
+
import { shellCommandSections } from "../core/commands.js";
|
|
8
|
+
import { KEATING_VOICE_TOOL_NAME, VOICE_TAGS, normalizeVoiceUtterance, speechStrategySummary, voiceTagLine } from "../core/speech.js";
|
|
9
|
+
import { KEATING_ASCII_LOGO, KEATING_SUBTITLE_LINES } from "../core/terminal.js";
|
|
10
|
+
const KEATING_VERSION = "0.3.6";
|
|
11
|
+
const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]/g;
|
|
12
|
+
function visibleWidth(text) {
|
|
13
|
+
return text.replace(ANSI_RE, "").length;
|
|
14
|
+
}
|
|
15
|
+
function padVisible(text, width) {
|
|
16
|
+
const vw = visibleWidth(text);
|
|
17
|
+
return vw >= width ? text : text + " ".repeat(width - vw);
|
|
18
|
+
}
|
|
19
|
+
function truncatePlain(text, width) {
|
|
20
|
+
if (text.length <= width)
|
|
21
|
+
return text;
|
|
22
|
+
if (width <= 1)
|
|
23
|
+
return text.slice(0, Math.max(0, width));
|
|
24
|
+
return `${text.slice(0, width - 1)}…`;
|
|
25
|
+
}
|
|
26
|
+
function centerPlain(text, width) {
|
|
27
|
+
const truncated = truncatePlain(text, width);
|
|
28
|
+
const gap = Math.max(0, width - truncated.length);
|
|
29
|
+
const left = Math.floor(gap / 2);
|
|
30
|
+
return `${" ".repeat(left)}${truncated}${" ".repeat(gap - left)}`;
|
|
31
|
+
}
|
|
32
|
+
function wrapWords(text, maxWidth) {
|
|
33
|
+
const width = Math.max(1, maxWidth);
|
|
34
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
35
|
+
const lines = [];
|
|
36
|
+
let current = "";
|
|
37
|
+
for (const raw of words) {
|
|
38
|
+
const word = truncatePlain(raw, width);
|
|
39
|
+
const next = current ? `${current} ${word}` : word;
|
|
40
|
+
if (current && next.length > width) {
|
|
41
|
+
lines.push(current);
|
|
42
|
+
current = word;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
current = next;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (current)
|
|
49
|
+
lines.push(current);
|
|
50
|
+
return lines.length > 0 ? lines : [""];
|
|
51
|
+
}
|
|
52
|
+
function formatHeaderPath(path) {
|
|
53
|
+
const home = homedir();
|
|
54
|
+
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
55
|
+
}
|
|
56
|
+
function getCurrentModelLabel(ctx) {
|
|
57
|
+
if (typeof ctx?.model === "string" && ctx.model.trim())
|
|
58
|
+
return ctx.model.trim();
|
|
59
|
+
if (ctx?.model?.provider && ctx?.model?.id)
|
|
60
|
+
return `${ctx.model.provider}/${ctx.model.id}`;
|
|
61
|
+
const branch = ctx?.sessionManager?.getBranch?.();
|
|
62
|
+
if (Array.isArray(branch)) {
|
|
63
|
+
for (let index = branch.length - 1; index >= 0; index -= 1) {
|
|
64
|
+
const entry = branch[index];
|
|
65
|
+
if (entry?.type === "model_change" && entry.provider && entry.modelId) {
|
|
66
|
+
return `${entry.provider}/${entry.modelId}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return "not set";
|
|
71
|
+
}
|
|
72
|
+
function getSessionLabel(ctx) {
|
|
73
|
+
const manager = ctx?.sessionManager;
|
|
74
|
+
return manager?.getSessionName?.()?.trim() || manager?.getSessionId?.() || "new session";
|
|
75
|
+
}
|
|
76
|
+
function summarizeLastActivity(ctx) {
|
|
77
|
+
const branch = ctx?.sessionManager?.getBranch?.();
|
|
78
|
+
if (!Array.isArray(branch))
|
|
79
|
+
return "";
|
|
80
|
+
for (let index = branch.length - 1; index >= 0; index -= 1) {
|
|
81
|
+
const entry = branch[index];
|
|
82
|
+
if (entry?.type !== "message")
|
|
83
|
+
continue;
|
|
84
|
+
const message = entry.message;
|
|
85
|
+
const role = message?.role === "assistant" ? "agent" : message?.role === "user" ? "you" : message?.role;
|
|
86
|
+
const content = message?.content;
|
|
87
|
+
const text = typeof content === "string"
|
|
88
|
+
? content
|
|
89
|
+
: Array.isArray(content)
|
|
90
|
+
? content.map((item) => item?.text ?? (item?.name ? `[${item.name}]` : "")).filter(Boolean).join(" ")
|
|
91
|
+
: "";
|
|
92
|
+
const compact = text.replace(/\s+/g, " ").trim();
|
|
93
|
+
if (compact)
|
|
94
|
+
return `${role ?? "message"}: ${compact}`;
|
|
95
|
+
}
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
function topicFromArgs(args) {
|
|
99
|
+
return (Array.isArray(args) ? args.join(" ") : String(args ?? "")).trim();
|
|
100
|
+
}
|
|
101
|
+
function info(ctx, message) {
|
|
102
|
+
ctx.ui.notify(message, "info");
|
|
103
|
+
}
|
|
104
|
+
function createKeatingHeaderComponent(pi, ctx) {
|
|
105
|
+
const sections = shellCommandSections();
|
|
106
|
+
const commandCount = sections.reduce((sum, section) => sum + section.commands.length, 0);
|
|
107
|
+
return (_tui, theme) => {
|
|
108
|
+
const t = theme.fg.bind(theme);
|
|
109
|
+
const b = theme.bold.bind(theme);
|
|
110
|
+
const border = (text) => t("borderMuted", text);
|
|
111
|
+
const dim = (text) => t("dim", text);
|
|
112
|
+
const accent = (text) => t("accent", text);
|
|
113
|
+
const heading = (text) => b(t("mdHeading", text));
|
|
114
|
+
const text = (value) => t("text", value);
|
|
115
|
+
return {
|
|
116
|
+
render(width) {
|
|
117
|
+
const maxWidth = Math.max(width - 2, 1);
|
|
118
|
+
const cardWidth = Math.min(maxWidth, 120);
|
|
119
|
+
const innerWidth = Math.max(cardWidth - 2, 1);
|
|
120
|
+
const contentWidth = Math.max(innerWidth - 2, 1);
|
|
121
|
+
const outerPad = " ".repeat(Math.max(0, Math.floor((width - cardWidth) / 2)));
|
|
122
|
+
const lines = [];
|
|
123
|
+
const push = (line) => lines.push(`${outerPad}${line}`);
|
|
124
|
+
const row = (content) => `${border("│")} ${padVisible(content, contentWidth)} ${border("│")}`;
|
|
125
|
+
const emptyRow = () => `${border("│")}${" ".repeat(innerWidth)}${border("│")}`;
|
|
126
|
+
const separator = () => `${border("├")}${border("─".repeat(innerWidth))}${border("┤")}`;
|
|
127
|
+
const useWideLayout = contentWidth >= 72;
|
|
128
|
+
push("");
|
|
129
|
+
if (cardWidth >= 72) {
|
|
130
|
+
const logoWidth = Math.max(...KEATING_ASCII_LOGO.map(line => line.length));
|
|
131
|
+
const logoPad = " ".repeat(Math.max(0, Math.floor((cardWidth - logoWidth) / 2)));
|
|
132
|
+
const palette = ["accent", "accent", "mdHeading", "mdHeading", "text", "text"];
|
|
133
|
+
for (let index = 0; index < KEATING_ASCII_LOGO.length; index += 1) {
|
|
134
|
+
push(b(t(palette[index] ?? "text", `${logoPad}${KEATING_ASCII_LOGO[index]}`)));
|
|
135
|
+
}
|
|
136
|
+
push("");
|
|
137
|
+
}
|
|
138
|
+
const versionTag = ` v${KEATING_VERSION} `;
|
|
139
|
+
const versionGap = Math.max(0, innerWidth - versionTag.length);
|
|
140
|
+
const versionLeft = Math.floor(versionGap / 2);
|
|
141
|
+
push(border(`╭${"─".repeat(versionLeft)}`) +
|
|
142
|
+
dim(versionTag) +
|
|
143
|
+
border(`${"─".repeat(versionGap - versionLeft)}╮`));
|
|
144
|
+
if (useWideLayout) {
|
|
145
|
+
const leftWidth = Math.min(38, Math.floor(contentWidth * 0.35));
|
|
146
|
+
const dividerWidth = 3;
|
|
147
|
+
const rightWidth = contentWidth - leftWidth - dividerWidth;
|
|
148
|
+
const leftValueWidth = Math.max(1, leftWidth - 11);
|
|
149
|
+
const commandNameWidth = 18;
|
|
150
|
+
const commandDescWidth = Math.max(12, rightWidth - commandNameWidth - 2);
|
|
151
|
+
const leftLines = [""];
|
|
152
|
+
const rightLines = ["", heading("Teaching Workflows")];
|
|
153
|
+
const leftLabel = (label, value, color) => {
|
|
154
|
+
const wrapped = wrapWords(value, leftValueWidth);
|
|
155
|
+
leftLines.push(`${dim(label.padEnd(10))} ${color === "text" ? text(wrapped[0]) : dim(wrapped[0])}`);
|
|
156
|
+
for (const line of wrapped.slice(1)) {
|
|
157
|
+
leftLines.push(`${" ".repeat(11)}${color === "text" ? text(line) : dim(line)}`);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const listBlock = (label, value) => {
|
|
161
|
+
if (!value)
|
|
162
|
+
return;
|
|
163
|
+
leftLines.push("");
|
|
164
|
+
leftLines.push(accent(b(label)));
|
|
165
|
+
for (const line of wrapWords(value, leftWidth)) {
|
|
166
|
+
leftLines.push(dim(line));
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
leftLabel("model", getCurrentModelLabel(ctx), "text");
|
|
170
|
+
leftLabel("directory", formatHeaderPath(ctx.cwd), "text");
|
|
171
|
+
leftLabel("session", getSessionLabel(ctx), "dim");
|
|
172
|
+
leftLines.push("");
|
|
173
|
+
leftLines.push(dim(`${pi.getAllTools?.().length ?? 0} tools · ${commandCount} commands`));
|
|
174
|
+
listBlock("Purpose", KEATING_SUBTITLE_LINES[0] ?? "The Hyperteacher");
|
|
175
|
+
listBlock("Last Activity", truncatePlain(summarizeLastActivity(ctx), leftWidth * 2));
|
|
176
|
+
for (const section of sections) {
|
|
177
|
+
rightLines.push("");
|
|
178
|
+
rightLines.push(accent(b(section.title)));
|
|
179
|
+
for (const command of section.commands) {
|
|
180
|
+
const wrapped = wrapWords(command.description, commandDescWidth);
|
|
181
|
+
rightLines.push(`${accent(command.usage.padEnd(commandNameWidth))}${dim(wrapped[0])}`);
|
|
182
|
+
for (const line of wrapped.slice(1)) {
|
|
183
|
+
rightLines.push(`${" ".repeat(commandNameWidth)}${dim(line)}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const maxRows = Math.max(leftLines.length, rightLines.length);
|
|
188
|
+
for (let index = 0; index < maxRows; index += 1) {
|
|
189
|
+
push(row(`${padVisible(leftLines[index] ?? "", leftWidth)}` +
|
|
190
|
+
`${border(" │ ")}` +
|
|
191
|
+
`${padVisible(rightLines[index] ?? "", rightWidth)}`));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
push(emptyRow());
|
|
196
|
+
push(row(heading(centerPlain(KEATING_SUBTITLE_LINES[0] ?? "Keating", contentWidth))));
|
|
197
|
+
push(row(dim(centerPlain(KEATING_SUBTITLE_LINES[1] ?? "The Hyperteacher", contentWidth))));
|
|
198
|
+
push(row(dim(centerPlain(`${pi.getAllTools?.().length ?? 0} tools · ${commandCount} commands`, contentWidth))));
|
|
199
|
+
push(emptyRow());
|
|
200
|
+
push(separator());
|
|
201
|
+
for (const section of sections) {
|
|
202
|
+
push(row(accent(b(section.title))));
|
|
203
|
+
for (const command of section.commands) {
|
|
204
|
+
const descWidth = Math.max(1, contentWidth - 18);
|
|
205
|
+
push(row(`${accent(command.usage.padEnd(17))}${dim(truncatePlain(command.description, descWidth))}`));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
210
|
+
push("");
|
|
211
|
+
return lines;
|
|
212
|
+
},
|
|
213
|
+
invalidate() { },
|
|
214
|
+
dispose() { },
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
let greetingShown = false;
|
|
219
|
+
const speechToolRegistrations = new WeakSet();
|
|
220
|
+
function voiceToolParameters() {
|
|
221
|
+
return {
|
|
222
|
+
type: "object",
|
|
223
|
+
additionalProperties: false,
|
|
224
|
+
required: ["text"],
|
|
225
|
+
properties: {
|
|
226
|
+
text: {
|
|
227
|
+
type: "string",
|
|
228
|
+
description: "Short learner-facing sentence or question to speak. Keep it conversational and concise."
|
|
229
|
+
},
|
|
230
|
+
voice: {
|
|
231
|
+
type: "string",
|
|
232
|
+
description: "Optional voice identity. Defaults to Keating's configured speech.defaultVoice."
|
|
233
|
+
},
|
|
234
|
+
tags: {
|
|
235
|
+
type: "array",
|
|
236
|
+
description: "Voice tags that describe the teaching move.",
|
|
237
|
+
items: {
|
|
238
|
+
type: "string",
|
|
239
|
+
enum: VOICE_TAGS
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
pace: {
|
|
243
|
+
type: "string",
|
|
244
|
+
enum: ["slow", "normal", "quick"],
|
|
245
|
+
description: "Delivery pace."
|
|
246
|
+
},
|
|
247
|
+
affect: {
|
|
248
|
+
type: "string",
|
|
249
|
+
enum: ["warm", "curious", "firm", "celebratory"],
|
|
250
|
+
description: "Conversational affect."
|
|
251
|
+
},
|
|
252
|
+
listenFor: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "What the supervising reasoning loop should listen for or verify after this utterance."
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function registerSpeechTool(pi, config) {
|
|
260
|
+
if (!config.speech.enabled || typeof pi.registerTool !== "function")
|
|
261
|
+
return;
|
|
262
|
+
if (typeof pi === "object" && pi !== null && speechToolRegistrations.has(pi))
|
|
263
|
+
return;
|
|
264
|
+
pi.registerTool({
|
|
265
|
+
name: KEATING_VOICE_TOOL_NAME,
|
|
266
|
+
label: "Keating Voice",
|
|
267
|
+
description: "Emit a concise voice-tagged teaching utterance for an optional conversational speech layer.",
|
|
268
|
+
promptSnippet: "Speak brief learner-facing utterances with voice tags while the normal model continues reasoning, questioning, and verification.",
|
|
269
|
+
promptGuidelines: [
|
|
270
|
+
"Use keating_voice only when speech is useful for a learner-facing sentence, question, redirect, recap, or encouragement.",
|
|
271
|
+
"Use keating_voice for short conversational delivery; keep deeper reasoning, verification, and tool-backed correction in normal text and normal tools.",
|
|
272
|
+
"Use keating_voice tags to mark the teaching move, especially question, verify, redirect, encourage, pause, recap, and explain.",
|
|
273
|
+
"Do not use keating_voice for citations, long derivations, file paths, code blocks, or private reasoning."
|
|
274
|
+
],
|
|
275
|
+
parameters: voiceToolParameters(),
|
|
276
|
+
async execute(_toolCallId, params) {
|
|
277
|
+
const utterance = normalizeVoiceUtterance(params, config.speech);
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: "text", text: voiceTagLine(utterance) }],
|
|
280
|
+
details: {
|
|
281
|
+
provider: "tags-only",
|
|
282
|
+
fastModel: config.speech.fastModel,
|
|
283
|
+
steeringModel: config.speech.steeringModel,
|
|
284
|
+
utterance
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
if (typeof pi === "object" && pi !== null) {
|
|
290
|
+
speechToolRegistrations.add(pi);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
export default function hyperteacher(pi) {
|
|
294
|
+
pi.registerCommand("plan", {
|
|
295
|
+
description: "Generate a deterministic lesson plan artifact for a topic.",
|
|
296
|
+
handler: async (args, ctx) => {
|
|
297
|
+
const topic = topicFromArgs(args);
|
|
298
|
+
if (!topic) {
|
|
299
|
+
info(ctx, "Usage: /plan <topic>");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const artifact = await planTopicArtifact(ctx.cwd, topic);
|
|
303
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, artifact.planPath)}`);
|
|
304
|
+
info(ctx, `Wrote ${relative(ctx.cwd, artifact.planPath)}`);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
pi.registerCommand("map", {
|
|
308
|
+
description: "Generate a Mermaid lesson map and render it with oxdraw when available.",
|
|
309
|
+
handler: async (args, ctx) => {
|
|
310
|
+
const topic = topicFromArgs(args);
|
|
311
|
+
if (!topic) {
|
|
312
|
+
info(ctx, "Usage: /map <topic>");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const artifact = await mapTopicArtifact(ctx.cwd, topic);
|
|
316
|
+
const outputs = [relative(ctx.cwd, artifact.mmdPath)];
|
|
317
|
+
if (artifact.svgPath)
|
|
318
|
+
outputs.push(relative(ctx.cwd, artifact.svgPath));
|
|
319
|
+
ctx.ui.setEditorText(`read ${outputs[0]}`);
|
|
320
|
+
info(ctx, `Generated ${outputs.join(" and ")}`);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
pi.registerCommand("animate", {
|
|
324
|
+
description: "Generate a manim-web animation bundle for a topic.",
|
|
325
|
+
handler: async (args, ctx) => {
|
|
326
|
+
const topic = topicFromArgs(args);
|
|
327
|
+
if (!topic) {
|
|
328
|
+
info(ctx, "Usage: /animate <topic>");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const artifact = await animateTopicArtifact(ctx.cwd, topic);
|
|
332
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, artifact.storyboardPath)}`);
|
|
333
|
+
info(ctx, `Generated ${relative(ctx.cwd, artifact.playerPath)}, ${relative(ctx.cwd, artifact.scenePath)}, and ${relative(ctx.cwd, artifact.manifestPath)}`);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
pi.registerCommand("bench", {
|
|
337
|
+
description: "Run the synthetic learner benchmark suite against the current teaching policy.",
|
|
338
|
+
handler: async (args, ctx) => {
|
|
339
|
+
const topic = topicFromArgs(args) || undefined;
|
|
340
|
+
const artifact = await benchPolicyArtifact(ctx.cwd, topic);
|
|
341
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, artifact.reportPath)}`);
|
|
342
|
+
info(ctx, `Benchmark score ${artifact.overallScore.toFixed(2)} saved to ${relative(ctx.cwd, artifact.reportPath)}${artifact.tracePath ? ` with trace ${relative(ctx.cwd, artifact.tracePath)}` : ""}`);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
pi.registerCommand("evolve", {
|
|
346
|
+
description: "Mutate and benchmark teaching policies, then keep the strongest safe candidate.",
|
|
347
|
+
handler: async (args, ctx) => {
|
|
348
|
+
const topic = topicFromArgs(args) || undefined;
|
|
349
|
+
const artifact = await evolvePolicyArtifact(ctx.cwd, topic);
|
|
350
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, artifact.reportPath)}`);
|
|
351
|
+
info(ctx, `Policy evolved to ${artifact.bestScore.toFixed(2)} and saved to ${relative(ctx.cwd, artifact.policyPath)}${artifact.tracePath ? ` with trace ${relative(ctx.cwd, artifact.tracePath)}` : ""}`);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
pi.registerCommand("prompt-evolve", {
|
|
355
|
+
description: "Evolve a prompt template using prompt-learning feedback and PROSPER-style selection.",
|
|
356
|
+
handler: async (args, ctx) => {
|
|
357
|
+
const promptName = topicFromArgs(args) || "learn";
|
|
358
|
+
const artifact = await evolvePromptArtifact(ctx.cwd, promptName);
|
|
359
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, artifact.reportPath)}`);
|
|
360
|
+
info(ctx, `Prompt ${promptName} evolved to ${artifact.bestScore.toFixed(2)} and saved to ${relative(ctx.cwd, artifact.evolvedPromptPath)}`);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
pi.registerCommand("prompt-eval", {
|
|
364
|
+
description: "Evaluate a prompt template for teaching effectiveness in a single pass.",
|
|
365
|
+
handler: async (args, ctx) => {
|
|
366
|
+
const promptContent = topicFromArgs(args);
|
|
367
|
+
if (!promptContent) {
|
|
368
|
+
info(ctx, "Usage: /prompt-eval <prompt text>");
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const result = await promptEvalArtifact(ctx.cwd, promptContent);
|
|
372
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, result.reportPath)}`);
|
|
373
|
+
info(ctx, `Prompt scored ${result.score.toFixed(2)}/100`);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
pi.registerCommand("policy", {
|
|
377
|
+
description: "Show the active hyperteacher policy.",
|
|
378
|
+
handler: async (_args, ctx) => {
|
|
379
|
+
ctx.ui.setEditorText(await currentPolicySummary(ctx.cwd));
|
|
380
|
+
info(ctx, "Loaded current policy into the editor.");
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
pi.registerCommand("speech", {
|
|
384
|
+
description: "Show optional voice-tool status.",
|
|
385
|
+
handler: async (_args, ctx) => {
|
|
386
|
+
const config = await loadKeatingConfig(ctx.cwd);
|
|
387
|
+
ctx.ui.setEditorText(speechStrategySummary(config.speech));
|
|
388
|
+
if (config.speech.enabled) {
|
|
389
|
+
info(ctx, `Speech is enabled. The model can call ${KEATING_VOICE_TOOL_NAME}.`);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
info(ctx, "Speech is disabled. Set speech.enabled=true in keating.config.json to expose the voice tool.");
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
pi.registerCommand("outputs", {
|
|
397
|
+
description: "Browse Keating plans, maps, benchmark reports, and evolution logs.",
|
|
398
|
+
handler: async (_args, ctx) => {
|
|
399
|
+
const artifacts = await listArtifacts(ctx.cwd);
|
|
400
|
+
if (artifacts.length === 0) {
|
|
401
|
+
info(ctx, "No artifacts yet. Use /plan, /map, /bench, or /evolve first.");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const selected = await ctx.ui.select("Keating Outputs", artifacts.map((artifact) => artifact.label));
|
|
405
|
+
const artifact = artifacts.find((entry) => entry.label === selected);
|
|
406
|
+
if (artifact) {
|
|
407
|
+
ctx.ui.setEditorText(`read ${artifact.path}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
pi.registerCommand("verify", {
|
|
412
|
+
description: "Generate a fact-checking checklist for a topic before teaching it.",
|
|
413
|
+
handler: async (args, ctx) => {
|
|
414
|
+
const topic = topicFromArgs(args);
|
|
415
|
+
if (!topic) {
|
|
416
|
+
info(ctx, "Usage: /verify <topic>");
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const result = await verifyTopicArtifact(ctx.cwd, topic);
|
|
420
|
+
if (result.alreadyVerified) {
|
|
421
|
+
info(ctx, `Already verified: ${relative(ctx.cwd, result.checklistPath)}`);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, result.checklistPath)}`);
|
|
425
|
+
info(ctx, `Verification checklist generated. Complete it before teaching this topic.`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
pi.registerCommand("feedback", {
|
|
430
|
+
description: "Record feedback on the current teaching session (up, down, confused) with an optional comment.",
|
|
431
|
+
handler: async (args, ctx) => {
|
|
432
|
+
const parts = Array.isArray(args) ? args : String(args ?? "").trim().split(/\s+/);
|
|
433
|
+
const signalMap = {
|
|
434
|
+
up: "thumbs-up",
|
|
435
|
+
down: "thumbs-down",
|
|
436
|
+
confused: "confused"
|
|
437
|
+
};
|
|
438
|
+
const signal = signalMap[parts[0]?.toLowerCase() ?? ""];
|
|
439
|
+
if (!signal) {
|
|
440
|
+
info(ctx, "Usage: /feedback <up|down|confused> [topic] [--comment=message]");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
let comment;
|
|
444
|
+
const filtered = parts.filter((arg) => {
|
|
445
|
+
if (arg.startsWith("--comment=")) {
|
|
446
|
+
comment = arg.slice("--comment=".length);
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
return true;
|
|
450
|
+
});
|
|
451
|
+
const topic = filtered.slice(1).join(" ") || "general";
|
|
452
|
+
const statePath = learnerStatePath(ctx.cwd);
|
|
453
|
+
const state = await loadLearnerState(statePath);
|
|
454
|
+
recordFeedback(state, topic, signal, comment);
|
|
455
|
+
await saveLearnerState(statePath, state);
|
|
456
|
+
const commentHint = comment ? ` with comment` : "";
|
|
457
|
+
info(ctx, `Recorded ${signal} feedback for "${topic}".${commentHint}`);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
pi.registerCommand("improve", {
|
|
461
|
+
description: "Generate a self-improvement proposal by diagnosing benchmark weaknesses.",
|
|
462
|
+
handler: async (args, ctx) => {
|
|
463
|
+
const sub = topicFromArgs(args).toLowerCase();
|
|
464
|
+
if (sub === "history") {
|
|
465
|
+
const md = await improveHistory(ctx.cwd);
|
|
466
|
+
ctx.ui.setEditorText(md);
|
|
467
|
+
info(ctx, "Loaded improvement history into the editor.");
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
info(ctx, "Running benchmark and diagnosing weaknesses...");
|
|
471
|
+
const artifact = await improveArtifact(ctx.cwd);
|
|
472
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, artifact.proposalPath)}`);
|
|
473
|
+
info(ctx, `Improvement proposal ${artifact.proposal.id} targets ${artifact.proposal.targets.map(t => t.file).join(", ")}`);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
pi.registerCommand("auto-improve", {
|
|
477
|
+
description: "Run the full self-improvement loop: benchmark → evolve policy → evolve prompt → benchmark again.",
|
|
478
|
+
handler: async (args, ctx) => {
|
|
479
|
+
const topic = topicFromArgs(args) || undefined;
|
|
480
|
+
info(ctx, "Running auto-improve loop (bench → evolve → prompt-evolve → bench)...");
|
|
481
|
+
const result = await autoImproveArtifact(ctx.cwd, topic);
|
|
482
|
+
const verdict = result.delta > 0 ? "IMPROVED" : result.delta < -0.5 ? "REGRESSED" : "NO SIGNIFICANT CHANGE";
|
|
483
|
+
ctx.ui.setEditorText(`read ${relative(ctx.cwd, result.reportPath)}`);
|
|
484
|
+
info(ctx, `Auto-improve: ${result.baselineScore.toFixed(2)} → ${result.afterScore.toFixed(2)} (${verdict}, Δ${result.delta >= 0 ? "+" : ""}${result.delta.toFixed(2)})`);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
pi.registerCommand("trace", {
|
|
488
|
+
description: "Browse persisted benchmark and evolution traces.",
|
|
489
|
+
handler: async (args, ctx) => {
|
|
490
|
+
const query = topicFromArgs(args).toLowerCase();
|
|
491
|
+
const artifacts = (await listArtifacts(ctx.cwd)).filter((artifact) => !query ? true : artifact.path.toLowerCase().includes(query) || artifact.label.toLowerCase().includes(query));
|
|
492
|
+
if (artifacts.length === 0) {
|
|
493
|
+
info(ctx, "No matching trace artifacts. Use /bench or /evolve first.");
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const selected = await ctx.ui.select("Keating Traces", artifacts.map((artifact) => artifact.label));
|
|
497
|
+
const artifact = artifacts.find((entry) => entry.label === selected);
|
|
498
|
+
if (artifact) {
|
|
499
|
+
ctx.ui.setEditorText(`read ${artifact.path}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
pi.registerCommand("learner-state", {
|
|
504
|
+
description: "Show the learner's profile and session history.",
|
|
505
|
+
handler: async (_args, ctx) => {
|
|
506
|
+
const statePath = learnerStatePath(ctx.cwd);
|
|
507
|
+
const state = await loadLearnerState(statePath);
|
|
508
|
+
const upCount = state.feedback.filter((f) => f.signal === "thumbs-up").length;
|
|
509
|
+
const downCount = state.feedback.filter((f) => f.signal === "thumbs-down").length;
|
|
510
|
+
const confusedCount = state.feedback.filter((f) => f.signal === "confused").length;
|
|
511
|
+
const lines = [
|
|
512
|
+
`Sessions: ${state.sessions?.length ?? 0}`,
|
|
513
|
+
`Topics covered: ${state.coveredTopics.length}`,
|
|
514
|
+
...state.coveredTopics.slice(-10).map((t) => ` - ${t.slug} (${t.domain})`),
|
|
515
|
+
`Feedback: 👍${upCount} 👎${downCount} 🤔${confusedCount}`,
|
|
516
|
+
`Misconceptions identified: ${state.identifiedMisconceptions.length}`,
|
|
517
|
+
];
|
|
518
|
+
ctx.ui.setEditorText(lines.join("\n"));
|
|
519
|
+
info(ctx, "Learner profile loaded.");
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
pi.registerCommand("timeline", {
|
|
523
|
+
description: "Show the engagement timeline for all covered topics, sorted by review urgency.",
|
|
524
|
+
handler: async (_args, ctx) => {
|
|
525
|
+
const artifact = await timelineArtifact(ctx.cwd);
|
|
526
|
+
ctx.ui.setEditorText(artifact.markdown);
|
|
527
|
+
info(ctx, `Engagement timeline saved to ${relative(ctx.cwd, artifact.reportPath)}`);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
pi.registerCommand("due", {
|
|
531
|
+
description: "Show topics that are due for review based on spaced repetition.",
|
|
532
|
+
handler: async (_args, ctx) => {
|
|
533
|
+
const artifact = await dueTopicsArtifact(ctx.cwd);
|
|
534
|
+
ctx.ui.setEditorText(artifact.markdown);
|
|
535
|
+
if (artifact.count === 0) {
|
|
536
|
+
info(ctx, "All topics are up to date! No reviews needed.");
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
info(ctx, `${artifact.count} topic${artifact.count === 1 ? "" : "s"} due for review.`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
544
|
+
await ensureProjectScaffold(ctx.cwd);
|
|
545
|
+
// Record session start in learner state
|
|
546
|
+
const statePath = learnerStatePath(ctx.cwd);
|
|
547
|
+
const state = await loadLearnerState(statePath);
|
|
548
|
+
recordSessionStart(state);
|
|
549
|
+
await saveLearnerState(statePath, state);
|
|
550
|
+
const config = await loadKeatingConfig(ctx.cwd);
|
|
551
|
+
registerSpeechTool(pi, config);
|
|
552
|
+
// ─── Branded greeting on first session in this process ───────────────
|
|
553
|
+
if (!greetingShown) {
|
|
554
|
+
greetingShown = true;
|
|
555
|
+
if (ctx.hasUI !== false && typeof ctx.ui.setHeader === "function") {
|
|
556
|
+
ctx.ui.setHeader(createKeatingHeaderComponent(pi, ctx));
|
|
557
|
+
}
|
|
558
|
+
else if (typeof ctx.ui.setWidget === "function") {
|
|
559
|
+
ctx.ui.setWidget("keating-greeting", createKeatingHeaderComponent(pi, ctx));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
// Check for due topics and notify
|
|
563
|
+
const dueArtifact = await dueTopicsArtifact(ctx.cwd);
|
|
564
|
+
if (config.debug.consoleSummary) {
|
|
565
|
+
if (dueArtifact.count > 0) {
|
|
566
|
+
info(ctx, `Keating loaded. ${dueArtifact.count} topic${dueArtifact.count === 1 ? " is" : "s are"} due for review. Use /due to see them.`);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
info(ctx, `Keating loaded — ready to teach. Type a topic or a command.`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|