explico 0.13.0 → 0.14.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 (239) hide show
  1. package/CHANGELOG.md +894 -1
  2. package/README.md +200 -30
  3. package/dist/compiler/astro.d.ts +15 -2
  4. package/dist/compiler/astro.js +40 -11
  5. package/dist/compiler/cli-main.d.ts +43 -0
  6. package/dist/compiler/cli-main.js +392 -0
  7. package/dist/compiler/cli.d.ts +424 -9
  8. package/dist/compiler/cli.js +1289 -8
  9. package/dist/compiler/collections.d.ts +88 -1
  10. package/dist/compiler/collections.js +170 -25
  11. package/dist/compiler/course-config.d.ts +1 -1
  12. package/dist/compiler/course-config.js +105 -1
  13. package/dist/compiler/decks.d.ts +161 -0
  14. package/dist/compiler/decks.js +235 -0
  15. package/dist/compiler/exports/combined-html.d.ts +5 -1
  16. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  17. package/dist/compiler/exports/deck-fit.js +90 -0
  18. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  19. package/dist/compiler/exports/export-chapters.js +64 -5
  20. package/dist/compiler/exports/export-pages.d.ts +30 -3
  21. package/dist/compiler/exports/export-pages.js +24 -1
  22. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  23. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  24. package/dist/compiler/exports/latex/cache.js +129 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  26. package/dist/compiler/exports/latex/capture.js +3 -1
  27. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  28. package/dist/compiler/exports/latex/compile.js +12 -1
  29. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  30. package/dist/compiler/exports/latex/deck.js +349 -0
  31. package/dist/compiler/exports/latex/document.d.ts +51 -6
  32. package/dist/compiler/exports/latex/document.js +71 -12
  33. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  34. package/dist/compiler/exports/latex/explico.cls +13 -183
  35. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  36. package/dist/compiler/exports/latex/serialize.js +4 -2
  37. package/dist/compiler/exports/latex-export.d.ts +24 -2
  38. package/dist/compiler/exports/latex-export.js +107 -26
  39. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  40. package/dist/compiler/exports/markdown/serialize.js +85 -8
  41. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  42. package/dist/compiler/exports/markdown-export.js +50 -11
  43. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  44. package/dist/compiler/exports/standalone-html.js +3 -3
  45. package/dist/compiler/exports/static-tree.d.ts +38 -1
  46. package/dist/compiler/exports/static-tree.js +120 -4
  47. package/dist/compiler/knowledge-version.d.ts +26 -2
  48. package/dist/compiler/knowledge-version.js +27 -17
  49. package/dist/compiler/lesson-derivation.d.ts +143 -6
  50. package/dist/compiler/lesson-derivation.js +145 -13
  51. package/dist/compiler/manifest.d.ts +133 -4
  52. package/dist/compiler/manifest.js +359 -58
  53. package/dist/compiler/page-versions.d.ts +160 -0
  54. package/dist/compiler/page-versions.js +198 -0
  55. package/dist/compiler/render-diagnostics.d.ts +5 -1
  56. package/dist/compiler/resolution-report.d.ts +24 -6
  57. package/dist/compiler/resolution-report.js +22 -4
  58. package/dist/compiler/validate-content.d.ts +7 -0
  59. package/dist/compiler/validate-content.js +160 -0
  60. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  61. package/dist/curriculum/competency-graph.d.ts +54 -0
  62. package/dist/curriculum/competency-graph.js +41 -0
  63. package/dist/reference/base-library.d.mts +8 -0
  64. package/dist/reference/base-library.mjs +19 -0
  65. package/dist/reference/consistency.d.ts +8 -4
  66. package/dist/reference/consistency.js +76 -26
  67. package/dist/reference/deck-remark.d.mts +123 -0
  68. package/dist/reference/deck-remark.mjs +600 -0
  69. package/dist/reference/deck-source.d.ts +107 -0
  70. package/dist/reference/deck-source.js +140 -0
  71. package/dist/reference/decks-validate.d.ts +133 -0
  72. package/dist/reference/decks-validate.js +359 -0
  73. package/dist/reference/derived-entries.d.ts +5 -1
  74. package/dist/reference/diagram-fit.d.ts +1 -1
  75. package/dist/reference/figure-symbols.d.ts +23 -6
  76. package/dist/reference/figure-symbols.js +51 -15
  77. package/dist/reference/float-name.d.ts +43 -0
  78. package/dist/reference/float-name.js +58 -0
  79. package/dist/reference/folds-validate.d.ts +1 -1
  80. package/dist/reference/gate-abbreviations.d.ts +1 -1
  81. package/dist/reference/gate-indices.js +5 -4
  82. package/dist/reference/gate-math.d.ts +29 -2
  83. package/dist/reference/gate-math.js +99 -18
  84. package/dist/reference/gate-part-pages.d.ts +1 -1
  85. package/dist/reference/lab-katex-validate.d.ts +10 -6
  86. package/dist/reference/lab-katex-validate.js +17 -9
  87. package/dist/reference/lab-math-scope.js +29 -4
  88. package/dist/reference/math-glyphs.d.mts +25 -0
  89. package/dist/reference/math-glyphs.mjs +189 -25
  90. package/dist/reference/page-notation.d.ts +22 -3
  91. package/dist/reference/page-notation.js +32 -10
  92. package/dist/reference/references.d.ts +14 -1
  93. package/dist/reference/references.js +13 -0
  94. package/dist/reference/registry-scope.d.mts +33 -0
  95. package/dist/reference/registry-scope.mjs +34 -0
  96. package/dist/reference/registry.js +29 -10
  97. package/dist/reference/rehype-deck.d.mts +6 -0
  98. package/dist/reference/rehype-deck.mjs +286 -0
  99. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  100. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  101. package/dist/reference/remark-notation.mjs +249 -40
  102. package/dist/reference/source-lines.d.mts +12 -0
  103. package/dist/reference/source-lines.mjs +65 -0
  104. package/dist/reference/types.d.ts +28 -0
  105. package/dist/rules/catalog.d.ts +21 -4
  106. package/dist/rules/catalog.js +221 -2
  107. package/dist/rules/course-rules.d.ts +5 -1
  108. package/dist/rules/course-rules.js +17 -3
  109. package/dist/rules/course-rules.schema.json +12 -0
  110. package/dist/rules/in-effect.d.ts +13 -3
  111. package/dist/rules/in-effect.js +30 -7
  112. package/dist/rules/rules.schema.json +259 -1
  113. package/dist/rules/settings.d.ts +1 -1
  114. package/dist/session/PreferenceStore.d.ts +6 -0
  115. package/dist/session/PreferenceStore.js +18 -1
  116. package/dist/session/deck-memory.d.ts +116 -0
  117. package/dist/session/deck-memory.js +143 -0
  118. package/dist/session/reading-memory.d.ts +96 -0
  119. package/dist/session/reading-memory.js +133 -0
  120. package/docs/VERSIONING.md +21 -18
  121. package/docs/authoring/README.md +29 -9
  122. package/docs/authoring/agents.md +63 -16
  123. package/docs/authoring/components.md +15 -0
  124. package/docs/authoring/deck.md +588 -0
  125. package/docs/authoring/migrating.md +1087 -14
  126. package/docs/authoring/notation.md +49 -6
  127. package/docs/authoring/structure.md +80 -1
  128. package/docs/authoring/versioning.md +54 -2
  129. package/docs/authoring/visualizations.md +7 -2
  130. package/docs/course-agent/AGENTS.md +37 -0
  131. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  132. package/docs/course-agent/skills/draft-lesson/SKILL.md +37 -0
  133. package/docs/course-agent/skills/migrate-course/SKILL.md +31 -0
  134. package/docs/course-agent/skills/release-content/SKILL.md +24 -0
  135. package/docs/diagram-scaling.md +148 -0
  136. package/docs/privacy.md +59 -0
  137. package/package.json +8 -2
  138. package/src/compiler/astro.ts +60 -21
  139. package/src/compiler/cli-main.ts +469 -0
  140. package/src/compiler/cli.ts +1905 -16
  141. package/src/compiler/collections.ts +284 -25
  142. package/src/compiler/course-config.ts +267 -0
  143. package/src/compiler/decks.ts +455 -0
  144. package/src/compiler/exports/deck-fit.ts +127 -0
  145. package/src/compiler/exports/export-chapters.ts +105 -6
  146. package/src/compiler/exports/export-pages.ts +46 -2
  147. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  148. package/src/compiler/exports/latex/cache.ts +183 -0
  149. package/src/compiler/exports/latex/capture.ts +5 -2
  150. package/src/compiler/exports/latex/compile.ts +18 -0
  151. package/src/compiler/exports/latex/deck.ts +419 -0
  152. package/src/compiler/exports/latex/document.ts +117 -21
  153. package/src/compiler/exports/latex/explico-common.sty +198 -0
  154. package/src/compiler/exports/latex/explico.cls +13 -183
  155. package/src/compiler/exports/latex/serialize.ts +25 -21
  156. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  157. package/src/compiler/exports/latex-export.ts +187 -45
  158. package/src/compiler/exports/markdown/serialize.ts +118 -9
  159. package/src/compiler/exports/markdown-export.ts +56 -11
  160. package/src/compiler/exports/standalone-html.ts +8 -7
  161. package/src/compiler/exports/static-tree.ts +175 -3
  162. package/src/compiler/knowledge-version.ts +56 -21
  163. package/src/compiler/lesson-derivation.ts +282 -16
  164. package/src/compiler/manifest.ts +558 -63
  165. package/src/compiler/page-versions.ts +359 -0
  166. package/src/compiler/resolution-report.ts +38 -7
  167. package/src/compiler/validate-content.ts +212 -0
  168. package/src/components/CourseContents.astro +124 -6
  169. package/src/components/CurriculumMap.astro +49 -1
  170. package/src/components/FloatIndex.astro +1 -1
  171. package/src/components/PartOverview.astro +51 -5
  172. package/src/components/assessments/AssessmentSet.astro +41 -15
  173. package/src/components/block-heading.ts +15 -6
  174. package/src/components/card-carry.ts +7 -4
  175. package/src/components/card-manager.ts +14 -2
  176. package/src/components/card-memory.ts +13 -9
  177. package/src/components/deck/DeckEnhancer.astro +13 -0
  178. package/src/components/deck/deck-context.ts +104 -0
  179. package/src/components/deck/deck-controller.ts +1287 -0
  180. package/src/components/deck/deck-map.ts +91 -0
  181. package/src/components/deck-lane.ts +163 -0
  182. package/src/components/diagram/Diagram.astro +131 -86
  183. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  184. package/src/components/figure/Figure.astro +7 -2
  185. package/src/components/figure/FloatCaption.astro +5 -3
  186. package/src/components/fold/Fold.astro +2 -2
  187. package/src/components/notation/NotationFormula.astro +81 -11
  188. package/src/components/notation/NotationGlossary.astro +132 -1
  189. package/src/components/notation/NotationLayer.astro +29 -0
  190. package/src/components/starlight/AboutPanel.astro +186 -3
  191. package/src/components/starlight/CollapsibleSections.astro +24 -3
  192. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  193. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  194. package/src/components/starlight/LayoutHeader.astro +17 -0
  195. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  196. package/src/components/starlight/LayoutSidebar.astro +807 -21
  197. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  198. package/src/components/starlight/LessonExportActions.astro +27 -9
  199. package/src/components/starlight/LessonFooter.astro +59 -0
  200. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  201. package/src/components/starlight/TextSizeControl.astro +24 -13
  202. package/src/components/starlight/page-marks.ts +139 -0
  203. package/src/components/starlight/section-summary.ts +1 -1
  204. package/src/components/starlight/sidebar-editions.ts +245 -0
  205. package/src/components/table/Table.astro +15 -7
  206. package/src/content-config.ts +87 -1
  207. package/src/curriculum/competency-graph.ts +95 -0
  208. package/src/reference/base-library.mjs +19 -0
  209. package/src/reference/consistency.ts +126 -39
  210. package/src/reference/deck-remark.mjs +600 -0
  211. package/src/reference/deck-source.ts +273 -0
  212. package/src/reference/decks-validate.ts +604 -0
  213. package/src/reference/diagram-layout.ts +3 -1
  214. package/src/reference/figure-symbols.ts +108 -31
  215. package/src/reference/folds-validate.ts +1 -1
  216. package/src/reference/gate-indices.ts +5 -3
  217. package/src/reference/gate-math.ts +169 -25
  218. package/src/reference/lab-katex-validate.ts +30 -15
  219. package/src/reference/lab-math-scope.ts +34 -3
  220. package/src/reference/math-glyphs.mjs +189 -25
  221. package/src/reference/page-notation.ts +69 -15
  222. package/src/reference/references.ts +28 -0
  223. package/src/reference/registry-scope.mjs +34 -0
  224. package/src/reference/registry.ts +41 -11
  225. package/src/reference/rehype-deck.mjs +286 -0
  226. package/src/reference/rehype-heading-anchors.mjs +48 -6
  227. package/src/reference/remark-notation.mjs +249 -40
  228. package/src/reference/source-lines.mjs +65 -0
  229. package/src/reference/types.ts +28 -0
  230. package/src/rules/catalog.ts +257 -4
  231. package/src/rules/course-rules.ts +21 -1
  232. package/src/rules/in-effect.ts +54 -8
  233. package/src/session/PreferenceStore.ts +23 -0
  234. package/src/session/deck-memory.ts +263 -0
  235. package/src/session/reading-memory.ts +243 -0
  236. package/src/styles/deck.css +1150 -0
  237. package/src/styles/global.css +958 -27
  238. package/src/styles/zones.css +277 -0
  239. /package/src/{components → reference}/float-name.ts +0 -0
@@ -1,12 +1,12 @@
1
1
  import { DiagnosticSeverity } from '../reference/types.js';
2
- import { EquationLink } from '../reference/gate-math.js';
3
- import { Manifest, ManifestLesson } from './manifest.js';
2
+ import { EquationLink, ScopeEntry } from '../reference/gate-math.js';
3
+ import { ManifestLesson, Manifest } from './manifest.js';
4
4
  import { RenderDiagnostic } from './render-diagnostics.js';
5
- import { RulesFilter, RuleInEffect } from '../rules/in-effect.js';
5
+ import { RetiredRule } from '../rules/catalog.js';
6
+ import { RuleInEffect, RulesFilter } from '../rules/in-effect.js';
6
7
  import '../curriculum/validation.js';
7
8
  import '../reference/consistency.js';
8
- import '../course-config-rRtME5UV.js';
9
- import '../rules/catalog.js';
9
+ import '../course-config-BLtIdkei.js';
10
10
  import '../rules/course-rules.js';
11
11
  import './collections.js';
12
12
  import '../reference/gate-retired-keys.js';
@@ -25,12 +25,17 @@ import '../reference/mixed-content.js';
25
25
  import '../reference/gloss.js';
26
26
  import '../rules/waivers.js';
27
27
  import './knowledge-version.js';
28
+ import './page-versions.js';
29
+ import './decks.js';
30
+ import '../reference/decks-validate.js';
31
+ import '../reference/deck-source.js';
28
32
  import './lesson-derivation.js';
29
33
 
30
34
  /**
31
35
  * The `content` CLI core (Phase G1).
32
36
  *
33
- * `scripts/content.ts` is a thin argv wrapper; every command's logic lives here
37
+ * `./cli-main` is the router (`runContentCli`), a course's `scripts/content.ts`
38
+ * a three-line shim over it; every command's logic lives here
34
39
  * as a pure-ish function that takes a compiled {@link Manifest} and returns a
35
40
  * plain data object. The router is **read-only** — only `content new` touches
36
41
  * the filesystem, and even then `scaffoldLesson` / `scaffoldTerm` only return
@@ -59,7 +64,7 @@ import './lesson-derivation.js';
59
64
  * under the `render:` source.
60
65
  */
61
66
 
62
- type ContentCliCode = 'unknown-command' | 'usage' | 'unknown-lesson' | 'unknown-kind' | 'invalid-id' | 'exists' | 'not-a-lesson' | 'no-git';
67
+ type ContentCliCode = 'unknown-command' | 'usage' | 'unknown-lesson' | 'unknown-kind' | 'invalid-id' | 'exists' | 'not-a-lesson' | 'no-git' | 'unknown-version' | 'migration-gap' | 'unknown-key';
63
68
  /** A user-facing failure with a stable code and no stack in `--json` output. */
64
69
  declare class ContentCliError extends Error {
65
70
  readonly code: ContentCliCode;
@@ -74,6 +79,8 @@ interface CliDiagnostic {
74
79
  * which `tests/rules/catalog.test.ts` treats as a bug.
75
80
  */
76
81
  readonly rule?: string;
82
+ /** The owner's page and anchor under `docs/authoring/`, `notation.md#n4`. */
83
+ readonly doc?: string;
77
84
  readonly severity: DiagnosticSeverity;
78
85
  readonly message: string;
79
86
  readonly file?: string;
@@ -87,13 +94,23 @@ interface CliDiagnostic {
87
94
  */
88
95
  readonly equation?: EquationLink;
89
96
  }
97
+ /** ` [N4 · notation.md#n4]` for a diagnostic the catalog owns; empty otherwise. */
98
+ declare function ownerTag(diagnostic: Pick<CliDiagnostic, 'rule' | 'doc'>): string;
99
+ /**
100
+ * One text line for a diagnostic, the shape every verb prints:
101
+ * `file:line:column: severity code [rule · doc]: message`. The location is
102
+ * left out when the diagnostic has no file, and reads `1:1` when the file is
103
+ * known but the position is not, so an editor opens the file. The rule and its page are on the
104
+ * line so a reader needs no second lookup to find what the code answers to.
105
+ */
106
+ declare function formatCliDiagnostic(diagnostic: CliDiagnostic): string;
90
107
  /**
91
108
  * Every manifest diagnostic that names this lesson (by id or file), normalized
92
109
  * to {@link CliDiagnostic}. Corpus-wide checks (curriculum) are included only
93
110
  * when their message mentions the lesson; per-file checks (notation, math,
94
111
  * equations, consistency) are filtered by `file`.
95
112
  */
96
- declare function lessonDiagnostics(manifest: Manifest, lesson: ManifestLesson, rendered?: readonly RenderDiagnostic[]): CliDiagnostic[];
113
+ declare function lessonDiagnostics(manifest: Manifest, lesson: CheckTarget, rendered?: readonly RenderDiagnostic[]): CliDiagnostic[];
97
114
  interface RenderResult {
98
115
  readonly rendered: boolean;
99
116
  readonly diagnostics: readonly CliDiagnostic[];
@@ -139,6 +156,34 @@ interface DomainFileSignatures {
139
156
  interface StatusReport {
140
157
  /** The course's published knowledge version (`about.version`). */
141
158
  readonly knowledgeVersion: string;
159
+ /**
160
+ * What an agent is told to read before working, in estimated tokens: the
161
+ * course's `AGENTS.md`, the repository's one level up when the course is a
162
+ * workspace package, and the rules-in-effect page. A budget to watch; the
163
+ * estimate is bytes over four.
164
+ */
165
+ readonly agentSurfaces: readonly {
166
+ readonly name: string;
167
+ readonly tokens: number;
168
+ }[];
169
+ /**
170
+ * Whether the course's own `AGENTS.md` points an agent at the engine's
171
+ * guide (`docs/authoring/agents.md`), and whether the procedures
172
+ * `content init-agent` installs are in `.claude/`.
173
+ */
174
+ readonly agentGuide: {
175
+ readonly agentsFile: string | null;
176
+ readonly linksGuide: boolean;
177
+ readonly skillsInstalled: boolean;
178
+ };
179
+ /**
180
+ * Rule C5: how many chapters and part pages carry a version of their own,
181
+ * and the slugs of those with a change `## [Unreleased]` has not released.
182
+ */
183
+ readonly pageVersions: {
184
+ readonly tracked: number;
185
+ readonly unreleased: readonly string[];
186
+ };
142
187
  readonly counts: {
143
188
  readonly lessons: number;
144
189
  readonly competencies: number;
@@ -166,6 +211,46 @@ interface DiagnosticsReport {
166
211
  readonly count: number;
167
212
  readonly diagnostics: readonly CliDiagnostic[];
168
213
  }
214
+ /**
215
+ * The diagnostics list folded to what an agent decides from: how many, of
216
+ * what, and where. The default text output, since a course can have hundreds
217
+ * of lines of two codes and the list is what `--all` is for.
218
+ */
219
+ interface DiagnosticsSummary {
220
+ readonly minimumSeverity: DiagnosticThreshold;
221
+ readonly count: number;
222
+ readonly bySeverity: {
223
+ readonly errors: number;
224
+ readonly warnings: number;
225
+ readonly infos: number;
226
+ };
227
+ /** One row per code and severity, most frequent first. */
228
+ readonly byCode: readonly {
229
+ readonly code: string;
230
+ readonly rule?: string;
231
+ readonly doc?: string;
232
+ readonly severity: DiagnosticSeverity;
233
+ readonly count: number;
234
+ }[];
235
+ /** One row per file, most diagnostics first; `(no file)` for the rest. */
236
+ readonly byFile: readonly {
237
+ readonly file: string;
238
+ readonly count: number;
239
+ readonly errors: number;
240
+ readonly warnings: number;
241
+ readonly infos: number;
242
+ }[];
243
+ }
244
+ declare function summarizeDiagnostics(report: DiagnosticsReport): DiagnosticsSummary;
245
+ /** The diagnostics of one code (with or without its `source:` prefix) or of some files. */
246
+ declare function filterDiagnostics(report: DiagnosticsReport, filter: {
247
+ readonly code?: string;
248
+ readonly files?: readonly string[];
249
+ }): DiagnosticsReport;
250
+ /** The summary as text: a severity line, then counts by code and by file, the file list capped. */
251
+ declare function formatDiagnosticsSummary(summary: DiagnosticsSummary, options?: {
252
+ readonly fileCap?: number;
253
+ }): string;
169
254
  interface WarningsReport {
170
255
  readonly count: number;
171
256
  readonly warnings: readonly CliDiagnostic[];
@@ -191,6 +276,8 @@ interface RulesReport {
191
276
  declare function parseRulesFilter(input: {
192
277
  readonly element?: string;
193
278
  readonly enforcement?: string;
279
+ /** `chapter` (the default page) or `deck` (`<lesson>.deck.mdx`). */
280
+ readonly edition?: string;
194
281
  }): RulesFilter;
195
282
  /** The rules this course has in effect, optionally narrowed. */
196
283
  declare function contentRules(manifest: Manifest, filter?: RulesFilter): RulesReport;
@@ -204,6 +291,8 @@ interface ContextReport {
204
291
  readonly teaches: readonly string[];
205
292
  readonly requires: readonly string[];
206
293
  readonly assumptions: readonly string[];
294
+ /** The version the page's content last changed in; `null` for a change not yet released (rule C5). */
295
+ readonly since: string | null;
207
296
  readonly notationLocal: unknown;
208
297
  readonly body: string;
209
298
  };
@@ -268,7 +357,317 @@ interface ContextReport {
268
357
  readonly diagnostics: readonly CliDiagnostic[];
269
358
  readonly tokens: TokenCounts;
270
359
  }
360
+ /**
361
+ * The migration guide, cut to the sections an upgrade needs: from the
362
+ * version a course pins to the version it has installed, oldest first. The
363
+ * guide is one newest-first file of every release; an upgrade reads one or
364
+ * two sections of it, and the heading grammar `## <from> → <to>` (a `0.4.x`
365
+ * range on the left, `Unreleased` allowed on the right) says which.
366
+ */
367
+ interface MigrationSection {
368
+ readonly from: string;
369
+ readonly to: string;
370
+ readonly heading: string;
371
+ readonly markdown: string;
372
+ readonly tokens: number;
373
+ }
374
+ interface MigrateReport {
375
+ readonly from: string;
376
+ readonly to: string;
377
+ readonly file: string;
378
+ readonly sections: readonly MigrationSection[];
379
+ readonly tokens: number;
380
+ }
381
+ /** Every `## <from> → <to>` section of the guide, in file order (newest first). */
382
+ declare function parseMigrationSections(markdown: string): MigrationSection[];
383
+ /**
384
+ * The sections that take a course from `from` to `to`, oldest first: each
385
+ * section's left side matches where the previous one ended. A hole in the
386
+ * chain is an error naming the version no section continues from, since a
387
+ * skipped section is a skipped migration.
388
+ */
389
+ declare function selectMigrationPath(sections: readonly MigrationSection[], from: string, to: string): MigrationSection[];
390
+ /** The engine version the course has installed: the package beside it, or this one. */
391
+ declare function installedEngineVersion(): string | undefined;
392
+ /**
393
+ * The engine version `package.json` pins at HEAD — what the course last
394
+ * migrated to, before the working tree bumped it. `workspace:*` or a range
395
+ * is no version, so the caller asks for `--from`.
396
+ */
397
+ declare function pinnedEngineVersion(): string | undefined;
398
+ declare function contentMigrate(options?: {
399
+ readonly from?: string;
400
+ readonly to?: string;
401
+ }): MigrateReport;
402
+ /** The sections, oldest first, under one line that says how much is coming. */
403
+ declare function formatMigrateReport(report: MigrateReport): string;
404
+ /**
405
+ * What a lesson's math binds, span by span, before a build says so: each
406
+ * glyph and the card it resolves to, how (`canonical`, `family`, `derived`,
407
+ * `base`, `operator`, or an explicit `\explain` wrap), the wraps the bare
408
+ * symbol would have resolved without, and every unresolved glyph with where
409
+ * `content symbol` finds it defined. The same resolver and scope the
410
+ * completeness gate uses, so what it prints is what the build will do.
411
+ */
412
+ interface SpanBinding {
413
+ readonly token: string;
414
+ readonly key: string;
415
+ /** The resolver's match kind; `explicit` for an `\explain` wrap. */
416
+ readonly how: string;
417
+ }
418
+ interface ExplicitWrap {
419
+ readonly key: string;
420
+ readonly inner: string;
421
+ /** The bare `inner` resolves to exactly this card: the wrap costs tokens and binds nothing new. */
422
+ readonly redundant: boolean;
423
+ }
424
+ interface UnresolvedGlyph {
425
+ readonly token: string;
426
+ readonly suspended?: string;
427
+ /** Rule N23: indexed entries whose base this bare letter is; none lends it a card. */
428
+ readonly lenders?: readonly string[];
429
+ /** Where `content symbol` finds the token defined: exact hits, then near misses. */
430
+ readonly defined: readonly {
431
+ readonly tier: SymbolTier;
432
+ readonly key: string;
433
+ readonly where: string;
434
+ }[];
435
+ readonly near: readonly {
436
+ readonly latex: string;
437
+ readonly key: string;
438
+ }[];
439
+ }
440
+ interface SpanBindings {
441
+ readonly bindings: readonly SpanBinding[];
442
+ readonly wraps: readonly ExplicitWrap[];
443
+ readonly unresolved: readonly Omit<UnresolvedGlyph, 'defined' | 'near'>[];
444
+ /** The resolver rejected the span (a KaTeX parse error or a rule it enforces, N13 say); its message. */
445
+ readonly error?: string;
446
+ }
447
+ interface BindingsSpan extends Omit<SpanBindings, 'unresolved'> {
448
+ readonly line: number;
449
+ readonly column: number;
450
+ readonly display: boolean;
451
+ readonly latex: string;
452
+ readonly unresolved: readonly UnresolvedGlyph[];
453
+ }
454
+ interface BindingsReport {
455
+ readonly lesson: string;
456
+ readonly file: string;
457
+ readonly scope: readonly {
458
+ readonly key: string;
459
+ readonly notation: string;
460
+ readonly scope: 'shared' | 'local';
461
+ }[];
462
+ readonly spans: readonly BindingsSpan[];
463
+ readonly counts: {
464
+ readonly spans: number;
465
+ readonly bindings: number;
466
+ readonly unresolved: number;
467
+ readonly wraps: number;
468
+ readonly redundantWraps: number;
469
+ readonly errors: number;
470
+ };
471
+ }
472
+ /**
473
+ * One span against a scope. Exported for tests and for a caller with a span
474
+ * of its own (`content equation`).
475
+ */
476
+ declare function spanBindings(latex: string, scope: readonly ScopeEntry[], options?: {
477
+ readonly suspended?: readonly string[];
478
+ readonly derive?: boolean;
479
+ }): SpanBindings;
480
+ declare function contentBindings(manifest: Manifest, rawId: string): BindingsReport;
481
+ /** The text form: a counts line, the scope, then one line per span. */
482
+ declare function formatBindingsReport(report: BindingsReport): string;
483
+ /**
484
+ * One LaTeX string, resolved the way the gate will: against a lesson's
485
+ * scope, or against every shared card when no lesson is named (the table a
486
+ * notation entry's body and formula use). The inner loop while drafting:
487
+ * a few lines out, non-zero exit when the build would refuse the span.
488
+ */
489
+ interface EquationReport extends Omit<SpanBindings, 'unresolved'> {
490
+ readonly latex: string;
491
+ readonly against: {
492
+ readonly kind: 'lesson';
493
+ readonly lesson: string;
494
+ readonly cards: number;
495
+ } | {
496
+ readonly kind: 'shared';
497
+ readonly cards: number;
498
+ };
499
+ readonly unresolved: readonly UnresolvedGlyph[];
500
+ readonly ok: boolean;
501
+ }
502
+ declare function contentEquation(manifest: Manifest, latex: string, options?: {
503
+ readonly lesson?: string;
504
+ }): EquationReport;
505
+ declare function formatEquationReport(report: EquationReport): string;
506
+ /**
507
+ * The refactors a model gets wrong most and reads most for, as deterministic
508
+ * edits: every one is planned first (`FileEdit`s with the whole text before
509
+ * and after), shown as a unified diff, and written only on `--write`.
510
+ */
511
+ interface FileEdit {
512
+ /** Course-relative path. */
513
+ readonly file: string;
514
+ /** `null` when the file is created. */
515
+ readonly before: string | null;
516
+ /** `null` when the file is deleted. */
517
+ readonly after: string | null;
518
+ }
519
+ interface EditPlan {
520
+ readonly summary: readonly string[];
521
+ readonly edits: readonly FileEdit[];
522
+ /** What to run after writing. */
523
+ readonly next: readonly string[];
524
+ }
525
+ /** `[[old]]` and `\explain{old}` become `new`; nothing else is touched. Returns the count. */
526
+ declare function rewriteKeyReferences(text: string, oldKey: string, newKey: string): {
527
+ text: string;
528
+ count: number;
529
+ };
530
+ /**
531
+ * Remove one `notation.local` item from a lesson file's text, by key, keeping
532
+ * the rest of the frontmatter byte for byte. An item runs from its `- key:`
533
+ * line to the next line indented no deeper than the dash; an emptied list
534
+ * becomes `local: []`.
535
+ */
536
+ declare function removeLocalItem(text: string, key: string): {
537
+ text: string;
538
+ removed: boolean;
539
+ };
540
+ /** A shared card's file from a page-local entry's fields, nothing invented. */
541
+ declare function sharedCardFromLocal(local: Record<string, unknown>, options: {
542
+ readonly key: string;
543
+ readonly fromLesson: string;
544
+ }): string;
545
+ /**
546
+ * `content move <lesson>:<key> [--to <key>]`: a page-local entry becomes a
547
+ * shared card. Every lesson that declares the key loses its copy (the card
548
+ * serves them all; copies that differ are reported, the named lesson's text
549
+ * wins), and with `--to` every `[[key]]` and `\explain{key}` follows.
550
+ */
551
+ declare function planMove(manifest: Manifest, lessonId: string, key: string, options?: {
552
+ readonly to?: string;
553
+ }): EditPlan;
554
+ /** `content rename <old> <new>`: a shared card's key, everywhere it is named. */
555
+ declare function planRename(manifest: Manifest, oldKey: string, newKey: string): EditPlan;
556
+ /**
557
+ * `content fix <lesson>`: the diagnostics with one mechanical repair, applied.
558
+ * Today: every `\explain{key}{inner}` the gate reports as `explain-redundant`
559
+ * becomes `inner`.
560
+ */
561
+ declare function planFix(manifest: Manifest, lessonId: string): EditPlan;
562
+ /** The plan as a unified diff (git's), file by file, under its summary lines. */
563
+ declare function renderEditPlan(plan: EditPlan): string;
564
+ /** Write a plan's edits. Returns the files touched. */
565
+ declare function applyEditPlan(plan: EditPlan): string[];
566
+ /**
567
+ * The engine's course-facing agent procedures (`docs/course-agent/`): skills,
568
+ * the `rules-reviewer` agent and the `AGENTS.md` baseline, beside the
569
+ * installed package or above this module.
570
+ */
571
+ declare function courseAgentDir(): string | undefined;
572
+ /**
573
+ * `content init-agent [--write]`: install or refresh the engine's agent
574
+ * procedures in this course — every skill under `.claude/skills/`, the
575
+ * reviewer under `.claude/agents/` — and, only when the course has no
576
+ * `AGENTS.md`, the baseline one. An existing `AGENTS.md` is never edited;
577
+ * when it does not point at the engine's guide, the plan says what to add.
578
+ */
579
+ declare function planInitAgent(): EditPlan;
580
+ /**
581
+ * One rule, whole: the catalog entry as this course has it (enforcement,
582
+ * codes, settings in effect, whether it is on) and the rule page's own
583
+ * section, example included. About half a thousand tokens where opening the
584
+ * page costs twenty thousand.
585
+ */
586
+ interface RuleReport {
587
+ readonly id: string;
588
+ readonly status: 'in-effect' | 'retired' | 'unknown';
589
+ readonly rule?: RuleInEffect;
590
+ readonly retired?: RetiredRule;
591
+ /** The rule page's section for this rule, when the page is at hand. */
592
+ readonly section?: {
593
+ readonly file: string;
594
+ readonly markdown: string;
595
+ readonly tokens: number;
596
+ };
597
+ /** For `unknown`: the IDs this course has, so the caller can pick one. */
598
+ readonly known: readonly string[];
599
+ }
600
+ /**
601
+ * The engine's `docs/authoring/`: beside the installed package in a course
602
+ * (`node_modules/explico/docs/authoring`, the path the rules-in-effect page
603
+ * links), or above this module in the repository and in the package itself.
604
+ */
605
+ declare function authoringDocsDir(): string | undefined;
606
+ /**
607
+ * The section of a rule page that `anchor` names: from the heading whose
608
+ * slug is the anchor (`## N4` → `n4`, `## Reference systems` →
609
+ * `reference-systems`) to the next heading of the same or a higher level,
610
+ * trailing rules and blank lines dropped.
611
+ */
612
+ declare function ruleSection(page: string, anchor: string): string | undefined;
613
+ declare function contentRule(manifest: Manifest, rawId: string): RuleReport;
614
+ /** The text form of a rule report: the entry's facts, then the page section. */
615
+ declare function formatRuleReport(report: RuleReport): string;
271
616
  declare function contentContext(manifest: Manifest, rawId: string): ContextReport;
617
+ /**
618
+ * Where a symbol is already defined, asked before an author declares it
619
+ * again. Reads the registry the manifest holds: shared cards, page-local
620
+ * entries (`notation.local`) and glosses (a card's `glosses:`), matched on
621
+ * the latex as written, a shared card's `aliases`, or the key. A `contains`
622
+ * list follows for a letter that sits inside a longer notation (`v` in
623
+ * `v_0`), so a near miss is visible too. Read-only; every list is sorted,
624
+ * so two runs print the same report.
625
+ */
626
+ type SymbolTier = 'shared' | 'local' | 'gloss';
627
+ interface SymbolHit {
628
+ readonly tier: SymbolTier;
629
+ readonly key: string;
630
+ readonly latex: string;
631
+ readonly label: string;
632
+ readonly meaning?: string;
633
+ readonly units?: string;
634
+ /** How the query matched: the latex as written, an alias, or the key. */
635
+ readonly matchedBy: 'latex' | 'alias' | 'key';
636
+ /** shared: the entry file; local: the lesson id; gloss: the owning card. */
637
+ readonly where: string;
638
+ /** shared only: the pages (lessons and overview pages) whose bundle binds the card. */
639
+ readonly boundIn?: readonly string[];
640
+ }
641
+ interface SymbolReport {
642
+ readonly query: string;
643
+ readonly shared: readonly SymbolHit[];
644
+ readonly local: readonly SymbolHit[];
645
+ readonly glosses: readonly SymbolHit[];
646
+ /** Entries whose latex contains the query as a glyph, not matched above. */
647
+ readonly contains: readonly SymbolHit[];
648
+ /** How many `contains` candidates there were before the list was capped. */
649
+ readonly containsTotal: number;
650
+ }
651
+ declare function contentSymbol(manifest: Manifest, rawQuery: string): SymbolReport;
652
+ /**
653
+ * The text form of a symbol report: one block per tier, then the near
654
+ * misses, then one line that counts what was found, worded for the next
655
+ * step an author takes.
656
+ */
657
+ declare function formatSymbolReport(report: SymbolReport): string;
658
+ /**
659
+ * A page `content check` checks: a lesson's (its chapter, or a slides-first
660
+ * lesson's deck), or a deck beside its chapter or a part's, by its page id.
661
+ */
662
+ type CheckTarget = Pick<ManifestLesson, 'id' | 'file' | 'assessments'> & {
663
+ readonly notation: Pick<ManifestLesson['notation'], 'definitionIds'>;
664
+ };
665
+ /**
666
+ * The lesson `rawId` names, or the deck whose page it names: a deck beside
667
+ * its chapter by its route (`motion/range/slides`), a part's deck by its own
668
+ * (`motion/slides`).
669
+ */
670
+ declare function findCheckTarget(manifest: Manifest, rawId: string): CheckTarget;
272
671
  interface CheckLessonResult {
273
672
  readonly lesson: string;
274
673
  readonly file: string;
@@ -299,6 +698,22 @@ interface Scaffold {
299
698
  }[];
300
699
  }
301
700
  declare function scaffoldLesson(rawId: string): Scaffold;
701
+ /**
702
+ * `content new deck <lesson>`: the slide edition of a lesson, as a draft to
703
+ * rewrite (`docs/authoring/deck.md`). Beside an existing chapter it starts
704
+ * from the chapter: a `##` section per chapter section, a `###` frame per
705
+ * subsection (or one for a section without any), a `<Recall>` for each
706
+ * numbered equation, figure, table and diagram there, and the section's first
707
+ * sentence in the frame's notes. Without a chapter it is a slides-first
708
+ * lesson, with the lesson's frontmatter and a skeleton frame.
709
+ *
710
+ * `content new deck <part>`, for a part's directory with its page
711
+ * (`index.mdx`), drafts the part's deck (`<part>/index.deck.mdx`): a
712
+ * `## <chapter title> [chapter: <name>]` section per chapter, in the order
713
+ * the tracks read them, and a frame per section of the chapter, with a
714
+ * `<Recall>` for each float it numbers and its first sentence in the notes.
715
+ */
716
+ declare function scaffoldDeck(rawId: string): Scaffold;
302
717
  declare function scaffoldTerm(rawKey: string): Scaffold;
303
718
 
304
- export { type CheckLessonResult, type CheckReport, type CliDiagnostic, type ContentCliCode, ContentCliError, type ContextReport, type DiagnosticThreshold, type DiagnosticsReport, type DomainFileSignatures, type RenderResult, type RulesReport, type Scaffold, type StatusReport, type TokenCounts, type WarningsReport, changedLessonIds, contentCheck, contentContext, contentDiagnostics, contentRules, contentStatus, contentWarnings, estimateTokens, findLesson, lessonDiagnostics, lessonSource, normalizeLessonId, parseRulesFilter, renderLesson, scaffoldLesson, scaffoldTerm };
719
+ export { type BindingsReport, type BindingsSpan, type CheckLessonResult, type CheckReport, type CheckTarget, type CliDiagnostic, type ContentCliCode, ContentCliError, type ContextReport, type DiagnosticThreshold, type DiagnosticsReport, type DiagnosticsSummary, type DomainFileSignatures, type EditPlan, type EquationReport, type ExplicitWrap, type FileEdit, type MigrateReport, type MigrationSection, type RenderResult, type RuleReport, type RulesReport, type Scaffold, type SpanBinding, type SpanBindings, type StatusReport, type SymbolHit, type SymbolReport, type SymbolTier, type TokenCounts, type UnresolvedGlyph, type WarningsReport, applyEditPlan, authoringDocsDir, changedLessonIds, contentBindings, contentCheck, contentContext, contentDiagnostics, contentEquation, contentMigrate, contentRule, contentRules, contentStatus, contentSymbol, contentWarnings, courseAgentDir, estimateTokens, filterDiagnostics, findCheckTarget, findLesson, formatBindingsReport, formatCliDiagnostic, formatDiagnosticsSummary, formatEquationReport, formatMigrateReport, formatRuleReport, formatSymbolReport, installedEngineVersion, lessonDiagnostics, lessonSource, normalizeLessonId, ownerTag, parseMigrationSections, parseRulesFilter, pinnedEngineVersion, planFix, planInitAgent, planMove, planRename, removeLocalItem, renderEditPlan, renderLesson, rewriteKeyReferences, ruleSection, scaffoldDeck, scaffoldLesson, scaffoldTerm, selectMigrationPath, sharedCardFromLocal, spanBindings, summarizeDiagnostics };