explico 0.1.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.
Files changed (113) hide show
  1. package/README.md +108 -0
  2. package/dist/analytics/AnalyticsEmitter.d.ts +22 -0
  3. package/dist/analytics/AnalyticsEmitter.js +5 -0
  4. package/dist/compiler/cli.d.ts +189 -0
  5. package/dist/compiler/cli.js +552 -0
  6. package/dist/compiler/collections.d.ts +70 -0
  7. package/dist/compiler/collections.js +383 -0
  8. package/dist/compiler/course-config.d.ts +31 -0
  9. package/dist/compiler/course-config.js +0 -0
  10. package/dist/compiler/lesson-derivation.d.ts +89 -0
  11. package/dist/compiler/lesson-derivation.js +91 -0
  12. package/dist/compiler/manifest.d.ts +178 -0
  13. package/dist/compiler/manifest.js +345 -0
  14. package/dist/compiler/resolution-report.d.ts +45 -0
  15. package/dist/compiler/resolution-report.js +97 -0
  16. package/dist/curriculum/validation.d.ts +76 -0
  17. package/dist/curriculum/validation.js +320 -0
  18. package/dist/progress/ProgressRepository.d.ts +53 -0
  19. package/dist/progress/ProgressRepository.js +12 -0
  20. package/dist/reference/base-library.mjs +27 -0
  21. package/dist/reference/citation-format.mjs +133 -0
  22. package/dist/reference/consistency.d.ts +64 -0
  23. package/dist/reference/consistency.js +234 -0
  24. package/dist/reference/curriculum-alignment.d.ts +7 -0
  25. package/dist/reference/curriculum-alignment.js +166 -0
  26. package/dist/reference/equations-validate.d.ts +50 -0
  27. package/dist/reference/equations-validate.js +90 -0
  28. package/dist/reference/equations.mjs +144 -0
  29. package/dist/reference/gate-math.d.ts +41 -0
  30. package/dist/reference/gate-math.js +151 -0
  31. package/dist/reference/katex-options.mjs +91 -0
  32. package/dist/reference/lab-math-scope.d.ts +21 -0
  33. package/dist/reference/lab-math-scope.js +28 -0
  34. package/dist/reference/label.d.ts +11 -0
  35. package/dist/reference/label.js +38 -0
  36. package/dist/reference/lint-ignore.d.ts +32 -0
  37. package/dist/reference/lint-ignore.js +39 -0
  38. package/dist/reference/math-glyphs.mjs +830 -0
  39. package/dist/reference/prose.d.ts +15 -0
  40. package/dist/reference/prose.js +12 -0
  41. package/dist/reference/references.d.ts +17 -0
  42. package/dist/reference/references.js +42 -0
  43. package/dist/reference/registry.d.ts +6 -0
  44. package/dist/reference/registry.js +548 -0
  45. package/dist/reference/rehype-fail-katex-errors.mjs +55 -0
  46. package/dist/reference/remark-citation.mjs +0 -0
  47. package/dist/reference/remark-notation.mjs +753 -0
  48. package/dist/reference/render-lab-math.d.ts +21 -0
  49. package/dist/reference/render-lab-math.js +58 -0
  50. package/dist/reference/types.d.ts +151 -0
  51. package/dist/reference/types.js +0 -0
  52. package/dist/reference/units-vocab.d.ts +26 -0
  53. package/dist/reference/units-vocab.js +119 -0
  54. package/dist/session/PreferenceStore.d.ts +28 -0
  55. package/dist/session/PreferenceStore.js +30 -0
  56. package/dist/session/user-context.d.ts +7 -0
  57. package/dist/session/user-context.js +10 -0
  58. package/dist/session/user.d.ts +21 -0
  59. package/dist/session/user.js +8 -0
  60. package/package.json +53 -0
  61. package/src/analytics/AnalyticsEmitter.ts +23 -0
  62. package/src/compiler/cli.ts +866 -0
  63. package/src/compiler/collections.ts +554 -0
  64. package/src/compiler/course-config.ts +32 -0
  65. package/src/compiler/lesson-derivation.ts +186 -0
  66. package/src/compiler/manifest.ts +601 -0
  67. package/src/compiler/resolution-report.ts +150 -0
  68. package/src/components/CurriculumMap.astro +208 -0
  69. package/src/components/HoverPanel.astro +67 -0
  70. package/src/components/assessments/AssessmentRunner.tsx +320 -0
  71. package/src/components/assessments/AssessmentSet.astro +54 -0
  72. package/src/components/citation/CitationLayer.astro +173 -0
  73. package/src/components/examples/CompactExample.astro +34 -0
  74. package/src/components/examples/CompactExamples.astro +253 -0
  75. package/src/components/hover-panel.ts +273 -0
  76. package/src/components/notation/NotationGlossary.astro +412 -0
  77. package/src/components/notation/NotationLayer.astro +594 -0
  78. package/src/components/notation/NotationMath.astro +24 -0
  79. package/src/components/notation/SafeMathNode.astro +23 -0
  80. package/src/components/notation/render-notation-math.ts +223 -0
  81. package/src/components/starlight/EquationEnhancer.astro +72 -0
  82. package/src/components/starlight/LayoutHeader.astro +102 -0
  83. package/src/components/starlight/LayoutPageSidebar.astro +33 -0
  84. package/src/components/starlight/LayoutSidebar.astro +29 -0
  85. package/src/components/starlight/LessonFooter.astro +58 -0
  86. package/src/content-config.ts +259 -0
  87. package/src/curriculum/validation.ts +481 -0
  88. package/src/progress/ProgressRepository.ts +64 -0
  89. package/src/reference/base-library.mjs +27 -0
  90. package/src/reference/citation-format.mjs +133 -0
  91. package/src/reference/consistency.ts +375 -0
  92. package/src/reference/curriculum-alignment.ts +228 -0
  93. package/src/reference/equations-validate.ts +167 -0
  94. package/src/reference/equations.mjs +144 -0
  95. package/src/reference/gate-math.ts +250 -0
  96. package/src/reference/katex-options.mjs +91 -0
  97. package/src/reference/lab-math-scope.ts +54 -0
  98. package/src/reference/label.ts +47 -0
  99. package/src/reference/lint-ignore.ts +75 -0
  100. package/src/reference/math-glyphs.mjs +830 -0
  101. package/src/reference/prose.ts +21 -0
  102. package/src/reference/references.ts +82 -0
  103. package/src/reference/registry.ts +723 -0
  104. package/src/reference/rehype-fail-katex-errors.mjs +55 -0
  105. package/src/reference/remark-citation.mjs +0 -0
  106. package/src/reference/remark-notation.mjs +753 -0
  107. package/src/reference/render-lab-math.ts +99 -0
  108. package/src/reference/types.ts +182 -0
  109. package/src/reference/units-vocab.ts +143 -0
  110. package/src/session/PreferenceStore.ts +54 -0
  111. package/src/session/user-context.ts +18 -0
  112. package/src/session/user.ts +24 -0
  113. package/src/styles/global.css +763 -0
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # explico
2
+
3
+ A build-time engine for **prerequisite-aware, notation-rigorous course
4
+ content**. It turns a folder of MDX lessons and JSON collections into a
5
+ validated static course whose every rendered symbol is explained and whose
6
+ curriculum graph is checked.
7
+
8
+ What it gives a course:
9
+
10
+ - **One validated content model** — Zod schemas for lessons, competencies,
11
+ assessments, tracks, sources, and notation; a single loader from files to
12
+ typed data.
13
+ - **A semantic notation system** — `[[key]]` binds a glyph to a define-once
14
+ meaning; a **completeness gate** fails the build if any identifier in any
15
+ rendered-math context (lesson body, `notation.formula`, `checks.yml`,
16
+ assessment prompts, component slots) does not resolve to a key. One meaning
17
+ per glyph per page, enforced corpus-wide.
18
+ - **Inline citations** — `[@source-id; locator]` → numbered marker + generated
19
+ reference list, resolved against a schema-checked source collection.
20
+ - **Equation identity** — `\label{eq:key}` gives a display equation a stable
21
+ key, an appearance-order number, an anchor, and cross-lesson `[[eq-key]]`
22
+ references.
23
+ - **A KaTeX trust boundary** — build-time HTML + MathML; the trust callback
24
+ accepts exactly one validated `data-notation-key`. No runtime math, no CDN.
25
+ - **A deterministic JSON manifest** — the MCP/LMS contract: lessons, notation,
26
+ competencies, sources, cross-refs, diagnostics, content hashes. Two compiles
27
+ of one content tree are byte-identical.
28
+ - **Shared Astro/Starlight UI** — a hover-panel primitive shared by the
29
+ notation and citation layers, a glossary, a curriculum map, compact examples,
30
+ layout overrides.
31
+ - **LMS / analytics / identity seams** — framework-free interfaces shipped as
32
+ no-ops; a host supplies real implementations without touching a component.
33
+ - **A `content` CLI** — `status`, `context <lesson>`, `check <lesson>` (compiles
34
+ MDX → remark → KaTeX and fails loudly), `new lesson|term`.
35
+
36
+ The prerequisite graph itself is authored by the lecturer or an AI; the engine
37
+ validates it (DAG, ordering, coverage, orphans) rather than inferring it.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pnpm add -D explico
43
+ ```
44
+
45
+ Peer dependencies (the course provides them, pinned): `astro`,
46
+ `@astrojs/starlight`, `@astrojs/react`, `@astrojs/markdown-remark`, `react`,
47
+ `react-dom`, `katex`, `rehype-katex`, `remark-math`.
48
+
49
+ ## Use
50
+
51
+ A course provides `content/` (the six collections + `content/domain/` +
52
+ `content/course.config.ts`) and wires the engine in three places:
53
+
54
+ ```ts
55
+ // src/content.config.ts — Astro's content entry
56
+ export { collections } from 'explico/content-config';
57
+ ```
58
+
59
+ ```js
60
+ // astro.config.mjs
61
+ import { loadManifest } from 'explico/compiler/manifest.js';
62
+ import remarkNotation from 'explico/reference/remark-notation.mjs';
63
+ import remarkCitation from 'explico/reference/remark-citation.mjs';
64
+ import { createNotationKatexOptions } from 'explico/reference/katex-options.mjs';
65
+ import rehypeFailKatexErrors from 'explico/reference/rehype-fail-katex-errors.mjs';
66
+ import { courseConfig } from './content/course.config.ts';
67
+
68
+ const manifest = await loadManifest(courseConfig);
69
+ // … starlight({ customCss: ['explico/styles/global.css'],
70
+ // components: { Footer: 'explico/components/starlight/LessonFooter.astro', … } })
71
+ // … markdown.remarkPlugins / rehypePlugins from the manifest
72
+ ```
73
+
74
+ ```jsonc
75
+ // package.json — the read-side CLI
76
+ "scripts": { "validate:content": "tsx scripts/compile-manifest.ts" }
77
+ ```
78
+
79
+ The reference course is
80
+ [**credit-and-derivatives**](https://github.com/danieltuzes/credit-and-derivatives):
81
+ discounting → bonds → credit/CDS → derivatives and options.
82
+
83
+ ## Layout
84
+
85
+ | Path | What |
86
+ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
87
+ | `src/compiler/` | the one manifest compiler + the `content` CLI |
88
+ | `src/reference/` | notation registry, page-glyph resolver, remark/rehype adapters, the completeness gate, equation identity, consistency checks, KaTeX options |
89
+ | `src/curriculum/` | the curriculum-graph semantic validator |
90
+ | `src/components/` | shared Astro/React UI (notation layer, citations, glossary, hover panel, examples, layout) |
91
+ | `src/progress/`, `src/session/`, `src/analytics/` | the LMS / identity / analytics seams (no-op impls) |
92
+ | `src/content-config.ts` | the Zod collection schemas (Astro re-exports them) |
93
+ | `src/styles/` | `global.css` (notation panel, keyed-equation flash, KaTeX) |
94
+
95
+ ## Build
96
+
97
+ ```bash
98
+ pnpm build # tsup → dist/ (ESM + .d.ts) for the .ts core; .astro / .tsx / .css
99
+ # and content-config.ts ship from src/; .mjs adapters are copied.
100
+ ```
101
+
102
+ Published `exports` resolve to `dist/` for the typed core and to `src/` for the
103
+ Astro components and styles (see `publishConfig` in `package.json`).
104
+
105
+ ## Status
106
+
107
+ `0.1.x` — extracted from the `equations` project. API not yet stable; the
108
+ license is undecided (`UNLICENSED` until chosen).
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The analytics seam (Phase H1).
3
+ *
4
+ * `noopAnalyticsEmitter` is the only sink the static site ships — `emit` does
5
+ * nothing. Stateful components call it alongside the {@link ProgressRepository}
6
+ * so a future host can route interaction events somewhere real without touching
7
+ * a component. Framework-free: no React, Astro, content, or browser API, and no
8
+ * network call.
9
+ */
10
+ interface AnalyticsEvent {
11
+ readonly name: string;
12
+ /** Epoch milliseconds, supplied by the caller so this stays side-effect-free. */
13
+ readonly at: number;
14
+ readonly props?: Readonly<Record<string, string | number | boolean | null>>;
15
+ }
16
+ interface AnalyticsEmitter {
17
+ emit(event: AnalyticsEvent): void;
18
+ }
19
+ /** The only sink in the static build. */
20
+ declare const noopAnalyticsEmitter: AnalyticsEmitter;
21
+
22
+ export { type AnalyticsEmitter, type AnalyticsEvent, noopAnalyticsEmitter };
@@ -0,0 +1,5 @@
1
+ const noopAnalyticsEmitter = { emit: () => {
2
+ } };
3
+ export {
4
+ noopAnalyticsEmitter
5
+ };
@@ -0,0 +1,189 @@
1
+ import { Manifest, ManifestLesson } from './manifest.js';
2
+ import '../curriculum/validation.js';
3
+ import '../reference/consistency.js';
4
+ import './course-config.js';
5
+ import './collections.js';
6
+ import '../reference/types.js';
7
+ import '../reference/lint-ignore.js';
8
+ import '../reference/equations-validate.js';
9
+ import '../reference/gate-math.js';
10
+ import './lesson-derivation.js';
11
+
12
+ /**
13
+ * The `content` CLI core (Phase G1).
14
+ *
15
+ * `scripts/content.ts` is a thin argv wrapper; every command's logic lives here
16
+ * as a pure-ish function that takes a compiled {@link Manifest} and returns a
17
+ * plain data object. The router is **read-only** — only `content new` touches
18
+ * the filesystem, and even then `scaffoldLesson` / `scaffoldTerm` only return
19
+ * the path + contents for the wrapper to write, and never overwrite.
20
+ *
21
+ * Design points the G1 gate pins:
22
+ * - Every diagnostic carries a **stable code** (`<source>:<kind>`), never a
23
+ * bare string, so an AI caller can branch on it.
24
+ * - `--json` output is data only: no stack traces. The wrapper converts any
25
+ * thrown {@link ContentCliError} (and any unexpected error) into
26
+ * `{ code, message }`.
27
+ * - `content context <lesson>` emits that lesson plus only what a drafter
28
+ * needs — direct-prerequisite outcomes, the resolved notation bundle, cited
29
+ * source metadata + locators, the section's domain signatures, and the
30
+ * lesson's current diagnostics — and reports token counts against the doc
31
+ * set + corpus so the size win is visible.
32
+ * - `content check` compiles the lesson MDX → remark → KaTeX → HTML and fails
33
+ * on any renderability error (unparseable MDX, KaTeX error, unresolved
34
+ * notation, unknown `[[key]]`, unknown citation), on top of the manifest's
35
+ * schema / ref / math / equation diagnostics for that lesson.
36
+ */
37
+
38
+ type ContentCliCode = 'unknown-command' | 'usage' | 'unknown-lesson' | 'unknown-kind' | 'invalid-id' | 'exists' | 'not-a-lesson' | 'no-git';
39
+ /** A user-facing failure with a stable code and no stack in `--json` output. */
40
+ declare class ContentCliError extends Error {
41
+ readonly code: ContentCliCode;
42
+ constructor(code: ContentCliCode, message: string);
43
+ }
44
+ /** One normalized diagnostic. `code` is `<source>:<kind>` and always stable. */
45
+ interface CliDiagnostic {
46
+ readonly code: string;
47
+ readonly severity: 'error' | 'warning';
48
+ readonly message: string;
49
+ readonly file?: string;
50
+ readonly line?: number;
51
+ }
52
+ /**
53
+ * Every manifest diagnostic that names this lesson (by id or file), normalized
54
+ * to {@link CliDiagnostic}. Corpus-wide checks (curriculum) are included only
55
+ * when their message mentions the lesson; per-file checks (notation, math,
56
+ * equations, consistency) are filtered by `file`.
57
+ */
58
+ declare function lessonDiagnostics(manifest: Manifest, lesson: ManifestLesson): CliDiagnostic[];
59
+ interface RenderResult {
60
+ readonly rendered: boolean;
61
+ readonly diagnostics: readonly CliDiagnostic[];
62
+ }
63
+ /**
64
+ * Compile one lesson body through the real remark → KaTeX → HTML pipeline and
65
+ * report the first failure as a coded diagnostic. Exported so a fixture body
66
+ * can be checked directly, without a manifest entry.
67
+ */
68
+ declare function renderLesson(manifest: Manifest, input: {
69
+ body: string;
70
+ frontmatter: Record<string, unknown>;
71
+ path: string;
72
+ }): Promise<RenderResult>;
73
+ /** Rough token estimate — ~4 chars/token, the usual GPT-family heuristic. */
74
+ declare function estimateTokens(text: string): number;
75
+ interface TokenCounts {
76
+ /** Tokens in the serialized `content context` payload. */
77
+ readonly context: number;
78
+ /** `README.md` + `AGENTS.md` + `docs/architecture.md`. */
79
+ readonly docSet: number;
80
+ /** Every file under `content/`. */
81
+ readonly corpus: number;
82
+ /** `context / (docSet + corpus)`, rounded to 4 dp. */
83
+ readonly ratio: number;
84
+ }
85
+ /** Normalize `foundations/discount-factors` or the dotted id to the dotted id. */
86
+ declare function normalizeLessonId(raw: string): string;
87
+ declare function findLesson(manifest: Manifest, rawId: string): ManifestLesson;
88
+ interface DomainFileSignatures {
89
+ readonly file: string;
90
+ readonly signatures: readonly string[];
91
+ }
92
+ interface StatusReport {
93
+ readonly counts: {
94
+ readonly lessons: number;
95
+ readonly competencies: number;
96
+ readonly assessments: number;
97
+ readonly sources: number;
98
+ readonly tracks: number;
99
+ readonly notationDefinitions: number;
100
+ readonly keyedEquations: number;
101
+ };
102
+ readonly draftDebt: {
103
+ readonly lessons: readonly string[];
104
+ readonly notation: readonly string[];
105
+ };
106
+ readonly orphans: readonly CliDiagnostic[];
107
+ readonly diagnostics: {
108
+ readonly errors: number;
109
+ readonly warnings: number;
110
+ };
111
+ }
112
+ declare function contentStatus(manifest: Manifest): StatusReport;
113
+ interface ContextReport {
114
+ readonly lesson: {
115
+ readonly id: string;
116
+ readonly slug: string;
117
+ readonly file: string;
118
+ readonly title: string;
119
+ readonly description: string;
120
+ readonly status: string;
121
+ readonly teaches: readonly string[];
122
+ readonly requires: readonly string[];
123
+ readonly assumptions: readonly string[];
124
+ readonly notationLocal: unknown;
125
+ readonly body: string;
126
+ };
127
+ readonly taughtOutcomes: readonly {
128
+ id: string;
129
+ outcome: string;
130
+ }[];
131
+ readonly prerequisiteOutcomes: readonly {
132
+ id: string;
133
+ outcome: string;
134
+ }[];
135
+ readonly notation: readonly {
136
+ key: string;
137
+ latex: string;
138
+ meaning: string;
139
+ scope: string;
140
+ units?: string;
141
+ formula?: string;
142
+ }[];
143
+ readonly equations: readonly {
144
+ key: string;
145
+ number: string;
146
+ }[];
147
+ readonly sources: readonly {
148
+ id: string;
149
+ title: string;
150
+ type: string;
151
+ recordLocator?: string;
152
+ citedLocators: readonly string[];
153
+ }[];
154
+ readonly domainSignatures: readonly DomainFileSignatures[];
155
+ readonly diagnostics: readonly CliDiagnostic[];
156
+ readonly tokens: TokenCounts;
157
+ }
158
+ declare function contentContext(manifest: Manifest, rawId: string): ContextReport;
159
+ interface CheckLessonResult {
160
+ readonly lesson: string;
161
+ readonly file: string;
162
+ readonly ok: boolean;
163
+ readonly rendered: boolean;
164
+ readonly diagnostics: readonly CliDiagnostic[];
165
+ }
166
+ interface CheckReport {
167
+ readonly ok: boolean;
168
+ readonly lessons: readonly CheckLessonResult[];
169
+ }
170
+ /** `.mdx` / `.checks.yml` paths that differ from `HEAD`, tracked or not. */
171
+ declare function changedLessonIds(): string[];
172
+ declare function contentCheck(manifest: Manifest, options: {
173
+ lesson?: string;
174
+ changed?: boolean;
175
+ }): Promise<CheckReport>;
176
+ interface Scaffold {
177
+ /** Repo-relative path the wrapper writes. */
178
+ readonly path: string;
179
+ readonly contents: string;
180
+ /** Companion files (e.g. a lesson's `checks.yml`), also non-overwriting. */
181
+ readonly companions: readonly {
182
+ path: string;
183
+ contents: string;
184
+ }[];
185
+ }
186
+ declare function scaffoldLesson(rawId: string): Scaffold;
187
+ declare function scaffoldTerm(rawKey: string): Scaffold;
188
+
189
+ export { type CheckLessonResult, type CheckReport, type CliDiagnostic, type ContentCliCode, ContentCliError, type ContextReport, type DomainFileSignatures, type RenderResult, type Scaffold, type StatusReport, type TokenCounts, changedLessonIds, contentCheck, contentContext, contentStatus, estimateTokens, findLesson, lessonDiagnostics, normalizeLessonId, renderLesson, scaffoldLesson, scaffoldTerm };