explico 0.13.1 → 0.15.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 (256) hide show
  1. package/CHANGELOG.md +990 -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 +100 -0
  6. package/dist/compiler/cli-main.js +433 -0
  7. package/dist/compiler/cli-reference.d.ts +50 -0
  8. package/dist/compiler/cli-reference.js +213 -0
  9. package/dist/compiler/cli-verbs.d.ts +45 -0
  10. package/dist/compiler/cli-verbs.js +224 -0
  11. package/dist/compiler/cli-verify.d.ts +51 -0
  12. package/dist/compiler/cli-verify.js +151 -0
  13. package/dist/compiler/cli.d.ts +408 -24
  14. package/dist/compiler/cli.js +1328 -8
  15. package/dist/compiler/collections.d.ts +88 -1
  16. package/dist/compiler/collections.js +170 -25
  17. package/dist/compiler/content-release.d.ts +49 -0
  18. package/dist/compiler/content-release.js +63 -0
  19. package/dist/compiler/content-schemas.d.ts +2 -2
  20. package/dist/compiler/course-config.d.ts +1 -1
  21. package/dist/compiler/course-config.js +105 -1
  22. package/dist/compiler/decks.d.ts +161 -0
  23. package/dist/compiler/decks.js +235 -0
  24. package/dist/compiler/exports/combined-html.d.ts +5 -1
  25. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  26. package/dist/compiler/exports/deck-fit.js +90 -0
  27. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  28. package/dist/compiler/exports/export-chapters.js +64 -5
  29. package/dist/compiler/exports/export-pages.d.ts +30 -3
  30. package/dist/compiler/exports/export-pages.js +24 -1
  31. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  32. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  33. package/dist/compiler/exports/latex/cache.js +129 -0
  34. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  35. package/dist/compiler/exports/latex/capture.js +3 -1
  36. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  37. package/dist/compiler/exports/latex/compile.js +12 -1
  38. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  39. package/dist/compiler/exports/latex/deck.js +349 -0
  40. package/dist/compiler/exports/latex/document.d.ts +51 -6
  41. package/dist/compiler/exports/latex/document.js +71 -12
  42. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  43. package/dist/compiler/exports/latex/explico.cls +13 -183
  44. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  45. package/dist/compiler/exports/latex/serialize.js +4 -2
  46. package/dist/compiler/exports/latex-export.d.ts +24 -2
  47. package/dist/compiler/exports/latex-export.js +107 -26
  48. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  49. package/dist/compiler/exports/markdown/serialize.js +85 -8
  50. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  51. package/dist/compiler/exports/markdown-export.js +50 -11
  52. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  53. package/dist/compiler/exports/standalone-html.js +3 -3
  54. package/dist/compiler/exports/static-tree.d.ts +38 -1
  55. package/dist/compiler/exports/static-tree.js +120 -4
  56. package/dist/compiler/knowledge-version.d.ts +26 -2
  57. package/dist/compiler/knowledge-version.js +27 -17
  58. package/dist/compiler/lesson-derivation.d.ts +143 -6
  59. package/dist/compiler/lesson-derivation.js +145 -13
  60. package/dist/compiler/manifest.d.ts +133 -4
  61. package/dist/compiler/manifest.js +364 -58
  62. package/dist/compiler/page-versions.d.ts +184 -0
  63. package/dist/compiler/page-versions.js +217 -0
  64. package/dist/compiler/render-diagnostics.d.ts +5 -1
  65. package/dist/compiler/resolution-report.d.ts +24 -6
  66. package/dist/compiler/resolution-report.js +22 -4
  67. package/dist/compiler/validate-content.d.ts +7 -0
  68. package/dist/compiler/validate-content.js +160 -0
  69. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  70. package/dist/curriculum/competency-graph.d.ts +54 -0
  71. package/dist/curriculum/competency-graph.js +41 -0
  72. package/dist/reference/base-library.d.mts +8 -0
  73. package/dist/reference/base-library.mjs +19 -0
  74. package/dist/reference/consistency.d.ts +8 -4
  75. package/dist/reference/consistency.js +82 -26
  76. package/dist/reference/deck-remark.d.mts +123 -0
  77. package/dist/reference/deck-remark.mjs +600 -0
  78. package/dist/reference/deck-source.d.ts +107 -0
  79. package/dist/reference/deck-source.js +140 -0
  80. package/dist/reference/decks-validate.d.ts +133 -0
  81. package/dist/reference/decks-validate.js +359 -0
  82. package/dist/reference/derived-entries.d.ts +5 -1
  83. package/dist/reference/diagram-fit.d.ts +6 -2
  84. package/dist/reference/diagram-fit.js +5 -1
  85. package/dist/reference/figure-symbols.d.ts +23 -6
  86. package/dist/reference/figure-symbols.js +51 -15
  87. package/dist/reference/float-name.d.ts +43 -0
  88. package/dist/reference/float-name.js +58 -0
  89. package/dist/reference/folds-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-indices.js +5 -4
  92. package/dist/reference/gate-math.d.ts +29 -2
  93. package/dist/reference/gate-math.js +99 -18
  94. package/dist/reference/gate-part-pages.d.ts +1 -1
  95. package/dist/reference/lab-katex-validate.d.ts +10 -6
  96. package/dist/reference/lab-katex-validate.js +17 -9
  97. package/dist/reference/lab-math-scope.js +29 -4
  98. package/dist/reference/math-glyphs.d.mts +25 -0
  99. package/dist/reference/math-glyphs.mjs +189 -25
  100. package/dist/reference/page-notation.d.ts +22 -3
  101. package/dist/reference/page-notation.js +32 -10
  102. package/dist/reference/references.d.ts +14 -1
  103. package/dist/reference/references.js +13 -0
  104. package/dist/reference/registry-scope.d.mts +33 -0
  105. package/dist/reference/registry-scope.mjs +34 -0
  106. package/dist/reference/registry.js +29 -10
  107. package/dist/reference/rehype-deck.d.mts +6 -0
  108. package/dist/reference/rehype-deck.mjs +286 -0
  109. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  110. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  111. package/dist/reference/remark-notation.mjs +249 -40
  112. package/dist/reference/source-lines.d.mts +12 -0
  113. package/dist/reference/source-lines.mjs +65 -0
  114. package/dist/reference/types.d.ts +28 -0
  115. package/dist/rules/catalog.d.ts +21 -4
  116. package/dist/rules/catalog.js +237 -2
  117. package/dist/rules/course-rules.d.ts +5 -1
  118. package/dist/rules/course-rules.js +17 -3
  119. package/dist/rules/course-rules.schema.json +12 -0
  120. package/dist/rules/in-effect.d.ts +13 -3
  121. package/dist/rules/in-effect.js +34 -10
  122. package/dist/rules/rules.schema.json +280 -1
  123. package/dist/rules/settings.d.ts +1 -1
  124. package/dist/session/PreferenceStore.d.ts +6 -0
  125. package/dist/session/PreferenceStore.js +18 -1
  126. package/dist/session/deck-memory.d.ts +116 -0
  127. package/dist/session/deck-memory.js +143 -0
  128. package/dist/session/reading-memory.d.ts +96 -0
  129. package/dist/session/reading-memory.js +133 -0
  130. package/docs/VERSIONING.md +21 -18
  131. package/docs/authoring/README.md +31 -9
  132. package/docs/authoring/agents.md +72 -18
  133. package/docs/authoring/cli-reference.json +2172 -0
  134. package/docs/authoring/cli.md +887 -0
  135. package/docs/authoring/components.md +15 -0
  136. package/docs/authoring/deck.md +612 -0
  137. package/docs/authoring/migrating.md +1195 -16
  138. package/docs/authoring/notation.md +49 -6
  139. package/docs/authoring/structure.md +80 -1
  140. package/docs/authoring/versioning.md +56 -3
  141. package/docs/authoring/visualizations.md +7 -2
  142. package/docs/course-agent/AGENTS.md +40 -0
  143. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  144. package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
  145. package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
  146. package/docs/course-agent/skills/release-content/SKILL.md +26 -0
  147. package/docs/diagram-scaling.md +148 -0
  148. package/docs/privacy.md +59 -0
  149. package/package.json +8 -2
  150. package/src/compiler/astro.ts +60 -21
  151. package/src/compiler/cli-main.ts +557 -0
  152. package/src/compiler/cli-reference.ts +285 -0
  153. package/src/compiler/cli-verbs.ts +270 -0
  154. package/src/compiler/cli-verify.ts +241 -0
  155. package/src/compiler/cli.ts +2119 -170
  156. package/src/compiler/collections.ts +284 -25
  157. package/src/compiler/content-release.ts +91 -0
  158. package/src/compiler/course-config.ts +267 -0
  159. package/src/compiler/decks.ts +455 -0
  160. package/src/compiler/exports/deck-fit.ts +127 -0
  161. package/src/compiler/exports/export-chapters.ts +105 -6
  162. package/src/compiler/exports/export-pages.ts +46 -2
  163. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  164. package/src/compiler/exports/latex/cache.ts +183 -0
  165. package/src/compiler/exports/latex/capture.ts +5 -2
  166. package/src/compiler/exports/latex/compile.ts +18 -0
  167. package/src/compiler/exports/latex/deck.ts +419 -0
  168. package/src/compiler/exports/latex/document.ts +117 -21
  169. package/src/compiler/exports/latex/explico-common.sty +198 -0
  170. package/src/compiler/exports/latex/explico.cls +13 -183
  171. package/src/compiler/exports/latex/serialize.ts +25 -21
  172. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  173. package/src/compiler/exports/latex-export.ts +187 -45
  174. package/src/compiler/exports/markdown/serialize.ts +118 -9
  175. package/src/compiler/exports/markdown-export.ts +56 -11
  176. package/src/compiler/exports/standalone-html.ts +8 -7
  177. package/src/compiler/exports/static-tree.ts +175 -3
  178. package/src/compiler/knowledge-version.ts +56 -21
  179. package/src/compiler/lesson-derivation.ts +282 -16
  180. package/src/compiler/manifest.ts +568 -63
  181. package/src/compiler/page-versions.ts +401 -0
  182. package/src/compiler/resolution-report.ts +38 -7
  183. package/src/compiler/validate-content.ts +215 -0
  184. package/src/components/CourseContents.astro +124 -6
  185. package/src/components/CurriculumMap.astro +49 -1
  186. package/src/components/FloatIndex.astro +1 -1
  187. package/src/components/PartOverview.astro +51 -5
  188. package/src/components/assessments/AssessmentSet.astro +41 -15
  189. package/src/components/block-heading.ts +15 -6
  190. package/src/components/card-carry.ts +7 -4
  191. package/src/components/card-manager.ts +14 -2
  192. package/src/components/card-memory.ts +13 -9
  193. package/src/components/deck/DeckEnhancer.astro +13 -0
  194. package/src/components/deck/deck-context.ts +104 -0
  195. package/src/components/deck/deck-controller.ts +1287 -0
  196. package/src/components/deck/deck-map.ts +91 -0
  197. package/src/components/deck-lane.ts +163 -0
  198. package/src/components/diagram/Diagram.astro +131 -86
  199. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  200. package/src/components/figure/Figure.astro +7 -2
  201. package/src/components/figure/FloatCaption.astro +5 -3
  202. package/src/components/fold/Fold.astro +2 -2
  203. package/src/components/notation/NotationFormula.astro +81 -11
  204. package/src/components/notation/NotationGlossary.astro +132 -1
  205. package/src/components/notation/NotationLayer.astro +15 -7
  206. package/src/components/starlight/AboutPanel.astro +186 -3
  207. package/src/components/starlight/CollapsibleSections.astro +24 -3
  208. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  209. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  210. package/src/components/starlight/LayoutHeader.astro +17 -0
  211. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  212. package/src/components/starlight/LayoutSidebar.astro +807 -21
  213. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  214. package/src/components/starlight/LessonExportActions.astro +27 -9
  215. package/src/components/starlight/LessonFooter.astro +59 -0
  216. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  217. package/src/components/starlight/TextSizeControl.astro +24 -13
  218. package/src/components/starlight/page-marks.ts +139 -0
  219. package/src/components/starlight/section-summary.ts +1 -1
  220. package/src/components/starlight/sidebar-editions.ts +245 -0
  221. package/src/components/table/Table.astro +15 -7
  222. package/src/content-config.ts +87 -1
  223. package/src/curriculum/competency-graph.ts +95 -0
  224. package/src/reference/base-library.mjs +19 -0
  225. package/src/reference/consistency.ts +137 -39
  226. package/src/reference/deck-remark.mjs +600 -0
  227. package/src/reference/deck-source.ts +273 -0
  228. package/src/reference/decks-validate.ts +604 -0
  229. package/src/reference/diagram-fit.ts +18 -4
  230. package/src/reference/diagram-layout.ts +3 -1
  231. package/src/reference/figure-symbols.ts +108 -31
  232. package/src/reference/folds-validate.ts +1 -1
  233. package/src/reference/gate-indices.ts +5 -3
  234. package/src/reference/gate-math.ts +169 -25
  235. package/src/reference/lab-katex-validate.ts +30 -15
  236. package/src/reference/lab-math-scope.ts +34 -3
  237. package/src/reference/math-glyphs.mjs +189 -25
  238. package/src/reference/page-notation.ts +69 -15
  239. package/src/reference/references.ts +28 -0
  240. package/src/reference/registry-scope.mjs +34 -0
  241. package/src/reference/registry.ts +41 -11
  242. package/src/reference/rehype-deck.mjs +286 -0
  243. package/src/reference/rehype-heading-anchors.mjs +48 -6
  244. package/src/reference/remark-notation.mjs +249 -40
  245. package/src/reference/source-lines.mjs +65 -0
  246. package/src/reference/types.ts +28 -0
  247. package/src/rules/catalog.ts +274 -4
  248. package/src/rules/course-rules.ts +21 -1
  249. package/src/rules/in-effect.ts +58 -11
  250. package/src/session/PreferenceStore.ts +23 -0
  251. package/src/session/deck-memory.ts +263 -0
  252. package/src/session/reading-memory.ts +243 -0
  253. package/src/styles/deck.css +1150 -0
  254. package/src/styles/global.css +958 -27
  255. package/src/styles/zones.css +277 -0
  256. /package/src/{components → reference}/float-name.ts +0 -0
@@ -0,0 +1,433 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import {
4
+ ContentCliError,
5
+ applyEditPlan,
6
+ changedLessonIds,
7
+ contentBindings,
8
+ contentCheck,
9
+ contentContext,
10
+ contentDiagnostics,
11
+ contentEquation,
12
+ contentMigrate,
13
+ contentRule,
14
+ contentRules,
15
+ contentStatus,
16
+ contentSymbol,
17
+ contentWarnings,
18
+ filterDiagnostics,
19
+ findLesson,
20
+ formatBindingsReport,
21
+ formatCliDiagnostic,
22
+ formatDiagnosticsSummary,
23
+ formatEquationReport,
24
+ formatMigrateReport,
25
+ formatRuleReport,
26
+ formatSymbolReport,
27
+ ownerTag,
28
+ parseRulesFilter,
29
+ planFixMany,
30
+ planInitAgent,
31
+ planMove,
32
+ planRename,
33
+ renderEditPlan,
34
+ scaffoldDeck,
35
+ scaffoldLesson,
36
+ scaffoldTerm,
37
+ summarizeDiagnostics
38
+ } from "./cli.js";
39
+ import { formatVerbHelp, loadCliReference, verbHelp } from "./cli-reference.js";
40
+ import { CONTENT_USAGE } from "./cli-verbs.js";
41
+ import { planContentRelease } from "./content-release.js";
42
+ import { formatVerifyReport, formatVerifyStep, runVerify } from "./cli-verify.js";
43
+ import { compileManifest } from "./manifest.js";
44
+ const VALUED_FLAGS = ["element", "enforcement", "edition", "code", "lesson", "from", "to"];
45
+ function parseContentArgs(argv) {
46
+ const positionals = [];
47
+ const values = {};
48
+ let json = false;
49
+ let changed = false;
50
+ let all = false;
51
+ let write = false;
52
+ let e2e = false;
53
+ let force = false;
54
+ const valued = new RegExp(`^--(${VALUED_FLAGS.join("|")})(?:=(.*))?$`);
55
+ for (let i = 0; i < argv.length; i += 1) {
56
+ const arg = argv[i];
57
+ const match = valued.exec(arg);
58
+ if (arg === "--json") json = true;
59
+ else if (arg === "--changed") changed = true;
60
+ else if (arg === "--all") all = true;
61
+ else if (arg === "--write") write = true;
62
+ else if (arg === "--e2e") e2e = true;
63
+ else if (arg === "--force") force = true;
64
+ else if (match) values[match[1]] = match[2] ?? argv[i += 1] ?? "";
65
+ else positionals.push(arg);
66
+ }
67
+ return {
68
+ command: positionals[0] ?? "",
69
+ positionals: positionals.slice(1),
70
+ json,
71
+ changed,
72
+ all,
73
+ write,
74
+ e2e,
75
+ force,
76
+ ...values
77
+ };
78
+ }
79
+ function diagnosticsWhere(diagnostics, file) {
80
+ const own = diagnostics.filter((diagnostic) => diagnostic.file === file).length;
81
+ return own === diagnostics.length ? "" : ` (${own} in ${file}, ${diagnostics.length - own} in the shared cards it shows)`;
82
+ }
83
+ const PROCESS_IO = {
84
+ out: (text) => process.stdout.write(text),
85
+ err: (text) => process.stderr.write(text)
86
+ };
87
+ class Usage extends Error {
88
+ constructor(code, message) {
89
+ super(message);
90
+ this.code = code;
91
+ }
92
+ code;
93
+ }
94
+ function usage(code, message) {
95
+ throw new Usage(code, message);
96
+ }
97
+ function writeScaffold(scaffold) {
98
+ const written = [];
99
+ for (const file of [{ path: scaffold.path, contents: scaffold.contents }, ...scaffold.companions]) {
100
+ const absolute = join(process.cwd(), file.path);
101
+ mkdirSync(dirname(absolute), { recursive: true });
102
+ writeFileSync(absolute, file.contents, { flag: "wx" });
103
+ written.push(file.path);
104
+ }
105
+ return written;
106
+ }
107
+ async function runContentCli(courseConfig, argv = process.argv.slice(2), io = PROCESS_IO, compiled) {
108
+ const args = parseContentArgs(argv);
109
+ const json = (value) => io.out(`${JSON.stringify(value, null, 2)}
110
+ `);
111
+ try {
112
+ return await dispatch(courseConfig, args, io, json, compiled);
113
+ } catch (error) {
114
+ const code = error instanceof Usage || error instanceof ContentCliError ? error.code : "internal";
115
+ const message = error instanceof Error ? error.message : String(error);
116
+ if (args.json) json({ ok: false, error: { code, message } });
117
+ else io.err(`Error [${code}]: ${message}
118
+ `);
119
+ return 1;
120
+ }
121
+ }
122
+ async function dispatch(courseConfig, args, io, json, compiled) {
123
+ const out = io.out;
124
+ if (args.command === "" || args.command === "--help" || args.command === "help" && args.positionals.length === 0 && !args.json) {
125
+ out(`${CONTENT_USAGE}
126
+ `);
127
+ return 0;
128
+ }
129
+ if (args.command === "help") {
130
+ if (args.positionals.length > 1) usage("usage", `content help takes one verb; got ${args.positionals.join(" ")}.`);
131
+ const reference = loadCliReference();
132
+ const [word] = args.positionals;
133
+ if (word === void 0) {
134
+ json(reference);
135
+ return 0;
136
+ }
137
+ const help = verbHelp(reference, word);
138
+ if (args.json) json(help);
139
+ else out(formatVerbHelp(reference, help));
140
+ return 0;
141
+ }
142
+ if (args.command === "verify") {
143
+ if (args.positionals.length > 0) usage("usage", `content verify takes no argument; got ${args.positionals.join(" ")}.`);
144
+ if (!args.json) out(`verify (${args.e2e ? "with" : "without"} e2e; whole output under node_modules/.cache/explico/verify/)
145
+ `);
146
+ const report = runVerify({
147
+ e2e: args.e2e,
148
+ force: args.force,
149
+ ...args.json ? {} : { onStep: (event) => event.phase === "end" && out(formatVerifyStep(event.step)) }
150
+ });
151
+ if (args.json) json(report);
152
+ else out(report.cached ? formatVerifyReport(report) : `
153
+ ${formatVerifyReport(report)}`);
154
+ return report.ok ? 0 : 1;
155
+ }
156
+ if (args.command === "new") {
157
+ const [kind, id] = args.positionals;
158
+ if (kind !== "lesson" && kind !== "deck" && kind !== "term") {
159
+ usage("unknown-kind", 'content new expects "lesson", "deck" or "term".');
160
+ }
161
+ if (!id) usage("usage", `content new ${kind} needs an id.`);
162
+ if (args.positionals.length > 2) {
163
+ usage("usage", `content new ${kind} takes one id; got also ${args.positionals.slice(2).join(" ")}.`);
164
+ }
165
+ const scaffold = kind === "lesson" ? scaffoldLesson(id) : kind === "deck" ? scaffoldDeck(id) : scaffoldTerm(id);
166
+ const written = writeScaffold(scaffold);
167
+ if (args.json) json({ ok: true, created: written });
168
+ else out(`Created:
169
+ ${written.map((p) => ` ${p}`).join("\n")}
170
+ `);
171
+ return 0;
172
+ }
173
+ if (["status", "rules", "warnings"].includes(args.command) && args.positionals.length > 0) {
174
+ usage("usage", `content ${args.command} takes no <lesson>; got ${args.positionals.join(" ")}.`);
175
+ }
176
+ if (args.command === "init-agent" || args.command === "release") {
177
+ let plan;
178
+ if (args.command === "init-agent") {
179
+ if (args.positionals.length > 0) usage("usage", `content init-agent takes no argument; got ${args.positionals.join(" ")}.`);
180
+ plan = planInitAgent();
181
+ } else {
182
+ if (args.positionals.length > 1) usage("usage", `content release takes one of major, minor, patch; got ${args.positionals.join(" ")}.`);
183
+ plan = planContentRelease(args.positionals[0]);
184
+ }
185
+ if (args.json) json({ ...plan, written: args.write });
186
+ else out(renderEditPlan(plan));
187
+ if (plan.edits.length === 0) return 0;
188
+ const next = plan.next.length > 0 ? plan.next.join(" \xB7 ") : "";
189
+ if (args.write) {
190
+ const touched = applyEditPlan(plan);
191
+ if (!args.json) out(`
192
+ written: ${touched.join(", ")}${next === "" ? "" : `
193
+ next: ${next}`}
194
+ `);
195
+ } else if (!args.json) out(`
196
+ dry run: nothing written. Add --write to apply${next === "" ? "" : `; then ${next}`}.
197
+ `);
198
+ return 0;
199
+ }
200
+ if (args.command === "migrate") {
201
+ if (args.positionals.length > 0) {
202
+ usage("usage", `content migrate takes no positional argument; got ${args.positionals.join(" ")}. Use --from and --to.`);
203
+ }
204
+ const report = contentMigrate({
205
+ ...args.from === void 0 ? {} : { from: args.from },
206
+ ...args.to === void 0 ? {} : { to: args.to }
207
+ });
208
+ if (args.json) json(report);
209
+ else out(formatMigrateReport(report));
210
+ return 0;
211
+ }
212
+ const known = [
213
+ "status",
214
+ "move",
215
+ "rename",
216
+ "fix",
217
+ "rule",
218
+ "rules",
219
+ "warnings",
220
+ "diagnostics",
221
+ "context",
222
+ "equation",
223
+ "bindings",
224
+ "symbol",
225
+ "check"
226
+ ];
227
+ if (!known.includes(args.command)) {
228
+ usage("unknown-command", `Unknown command "${args.command}".
229
+ ${CONTENT_USAGE}`);
230
+ }
231
+ const manifest = compiled ?? await compileManifest(courseConfig);
232
+ switch (args.command) {
233
+ case "status": {
234
+ const report = contentStatus(manifest);
235
+ if (args.json) {
236
+ json(report);
237
+ return 0;
238
+ }
239
+ const c = report.counts;
240
+ const unreleased = report.pageVersions.unreleased;
241
+ const UNRELEASED_SHOWN = 6;
242
+ const unreleasedList = unreleased.length <= UNRELEASED_SHOWN ? unreleased.join(", ") : `${unreleased.slice(0, UNRELEASED_SHOWN).join(", ")}, \u2026 ${unreleased.length - UNRELEASED_SHOWN} more in --json`;
243
+ out(
244
+ `Knowledge version ${report.knowledgeVersion}
245
+ Page versions: ${report.pageVersions.tracked} page(s) tracked, ${unreleased.length} with changes not yet released${unreleased.length > 0 ? ` (${unreleasedList})` : ""}
246
+ Lessons ${c.lessons} \xB7 competencies ${c.competencies} \xB7 assessments ${c.assessments} \xB7 sources ${c.sources} \xB7 tracks ${c.tracks} \xB7 notation ${c.notationDefinitions} \xB7 keyed equations ${c.keyedEquations} \xB7 tables ${c.keyedTables} \xB7 figures ${c.keyedFigures} \xB7 diagrams ${c.keyedDiagrams} \xB7 example sets ${c.keyedExamples}
247
+ Diagnostics: ${report.diagnostics.errors} error(s), ${report.diagnostics.warnings} warning(s), ${report.diagnostics.infos} info
248
+ Orphans: ${report.orphans.length}
249
+ Agent surfaces (estimated tokens): ${report.agentSurfaces.map((s) => `${s.name} ${(s.tokens / 1e3).toFixed(1)}k`).join(" \xB7 ")}
250
+ Agent guide: ${report.agentGuide.agentsFile === null ? "no AGENTS.md \u2014 `pnpm content init-agent --write` installs the baseline" : report.agentGuide.linksGuide ? `${report.agentGuide.agentsFile} points at the engine's guide` : `${report.agentGuide.agentsFile} does not point at node_modules/explico/docs/authoring/agents.md \u2014 add that line`}; procedures ${report.agentGuide.skillsInstalled ? "installed" : "not installed (`pnpm content init-agent --write`)"}
251
+ `
252
+ );
253
+ return 0;
254
+ }
255
+ case "move":
256
+ case "rename":
257
+ case "fix": {
258
+ let plan;
259
+ if (args.command === "move") {
260
+ const [target, ...extra] = args.positionals;
261
+ const [lesson, key] = (target ?? "").split(":");
262
+ if (!lesson || !key || extra.length > 0) usage("usage", "content move needs <lesson>:<key> and optionally --to <key>.");
263
+ plan = planMove(manifest, lesson, key, args.to === void 0 ? {} : { to: args.to });
264
+ } else if (args.command === "rename") {
265
+ const [oldKey, newKey, ...extra] = args.positionals;
266
+ if (!oldKey || !newKey || extra.length > 0) usage("usage", "content rename needs <old> <new>.");
267
+ plan = planRename(manifest, oldKey, newKey);
268
+ } else {
269
+ if (args.all === args.positionals.length > 0) usage("usage", "content fix needs <lesson> ids, or --all for every lesson.");
270
+ plan = planFixMany(manifest, args.all ? manifest.lessons.map((lesson) => lesson.id) : args.positionals);
271
+ }
272
+ if (args.json) json({ ...plan, written: args.write });
273
+ else out(renderEditPlan(plan));
274
+ if (plan.edits.length === 0) return 0;
275
+ if (args.write) {
276
+ const touched = applyEditPlan(plan);
277
+ if (!args.json) out(`
278
+ written: ${touched.join(", ")}
279
+ next: ${plan.next.join(" \xB7 ")}
280
+ `);
281
+ } else if (!args.json) {
282
+ out(`
283
+ dry run: nothing written. Add --write to apply; then ${plan.next.join(" \xB7 ")}.
284
+ `);
285
+ }
286
+ return 0;
287
+ }
288
+ case "rule": {
289
+ const [id, ...extra] = args.positionals;
290
+ if (!id) usage("usage", "content rule needs a rule ID: N4, V8, X1, or an invariant such as keyed-floats.");
291
+ if (extra.length > 0) usage("usage", `content rule takes one ID; got also ${extra.join(" ")}.`);
292
+ const report = contentRule(manifest, id);
293
+ if (args.json) json(report);
294
+ else out(formatRuleReport(report));
295
+ return report.status === "unknown" ? 1 : 0;
296
+ }
297
+ case "rules": {
298
+ const filter = parseRulesFilter({
299
+ ...args.element === void 0 ? {} : { element: args.element },
300
+ ...args.enforcement === void 0 ? {} : { enforcement: args.enforcement },
301
+ ...args.edition === void 0 ? {} : { edition: args.edition }
302
+ });
303
+ const report = contentRules(manifest, filter);
304
+ if (args.json) json({ filter: report.filter, count: report.count, rules: report.rules });
305
+ else out(report.markdown);
306
+ return 0;
307
+ }
308
+ case "warnings": {
309
+ const report = contentWarnings(manifest);
310
+ if (args.json) json(report);
311
+ else {
312
+ for (const warning of report.warnings) out(`${formatCliDiagnostic(warning)}
313
+ `);
314
+ out(`${report.count} warning(s)
315
+ `);
316
+ }
317
+ return 0;
318
+ }
319
+ case "diagnostics": {
320
+ const [requested = "info", ...extra] = args.positionals;
321
+ if (extra.length > 0 || requested !== "info" && requested !== "warning" && requested !== "error") {
322
+ usage("usage", "content diagnostics expects one of: info, warning, error.");
323
+ }
324
+ let report = contentDiagnostics(manifest, requested);
325
+ let narrowed = false;
326
+ if (args.code !== void 0) {
327
+ report = filterDiagnostics(report, { code: args.code });
328
+ narrowed = true;
329
+ }
330
+ if (args.lesson !== void 0) {
331
+ report = filterDiagnostics(report, { files: [findLesson(manifest, args.lesson).file] });
332
+ narrowed = true;
333
+ }
334
+ if (args.changed) {
335
+ const ids = new Set(changedLessonIds());
336
+ const files = manifest.lessons.filter((l) => ids.has(l.id)).map((l) => l.file);
337
+ report = filterDiagnostics(report, { files });
338
+ narrowed = true;
339
+ }
340
+ if (args.json) json(report);
341
+ else if (args.all || narrowed) {
342
+ for (const diagnostic of report.diagnostics) out(`${formatCliDiagnostic(diagnostic)}
343
+ `);
344
+ out(`${report.count} diagnostic(s) at ${report.minimumSeverity} or higher
345
+ `);
346
+ } else out(formatDiagnosticsSummary(summarizeDiagnostics(report)));
347
+ return 0;
348
+ }
349
+ case "context": {
350
+ const [lesson, ...extra] = args.positionals;
351
+ if (!lesson) usage("usage", "content context needs a <lesson> id.");
352
+ if (extra.length > 0) usage("usage", `content context takes one <lesson> id; got also ${extra.join(" ")}.`);
353
+ const report = contentContext(manifest, lesson);
354
+ if (args.json) {
355
+ json(report);
356
+ return 0;
357
+ }
358
+ const t = report.tokens;
359
+ out(
360
+ `${report.lesson.id} \u2014 ${report.lesson.title}
361
+ teaches: ${report.lesson.teaches.join(", ") || "(none)"}
362
+ requires: ${report.lesson.requires.join(", ") || "(none)"}
363
+ notation: ${report.notation.map((n) => n.key).join(", ") || "(none)"}
364
+ sources: ${report.sources.map((s) => s.id).join(", ") || "(none)"}
365
+ diagnostics: ${report.diagnostics.length}${diagnosticsWhere(report.diagnostics, report.lesson.file)}
366
+ tokens: context ${t.context} vs doc set ${t.docSet} + corpus ${t.corpus} (${(t.ratio * 100).toFixed(2)}% of the baseline)
367
+ `
368
+ );
369
+ return 0;
370
+ }
371
+ case "equation": {
372
+ const [latex, ...extra] = args.positionals;
373
+ if (!latex) usage("usage", "content equation needs one quoted LaTeX string: content equation 'v_0 \\sin(\\theta)' --lesson <id>.");
374
+ if (extra.length > 0) usage("usage", `content equation takes one string; got also ${extra.join(" ")}. Quote the whole expression.`);
375
+ const report = contentEquation(manifest, latex, args.lesson === void 0 ? {} : { lesson: args.lesson });
376
+ if (args.json) json(report);
377
+ else out(formatEquationReport(report));
378
+ return report.ok ? 0 : 1;
379
+ }
380
+ case "bindings": {
381
+ const [lesson, ...extra] = args.positionals;
382
+ if (!lesson) usage("usage", "content bindings needs a <lesson> id.");
383
+ if (extra.length > 0) usage("usage", `content bindings takes one <lesson> id; got also ${extra.join(" ")}.`);
384
+ const report = contentBindings(manifest, lesson);
385
+ if (args.json) json(report);
386
+ else out(formatBindingsReport(report));
387
+ return 0;
388
+ }
389
+ case "symbol": {
390
+ const [query, ...extra] = args.positionals;
391
+ if (!query) usage("usage", "content symbol needs a <latex | key>; quote a backslash: '\\theta'.");
392
+ if (extra.length > 0) {
393
+ usage("usage", `content symbol takes one <latex | key>; got also ${extra.join(" ")}. Quote a symbol with spaces: 'v(t)'.`);
394
+ }
395
+ const report = contentSymbol(manifest, query);
396
+ if (args.json) json(report);
397
+ else out(formatSymbolReport(report));
398
+ return 0;
399
+ }
400
+ case "check": {
401
+ const report = await contentCheck(manifest, { lessons: args.positionals, changed: args.changed });
402
+ if (args.json) json(report);
403
+ else {
404
+ for (const entry of report.lessons) {
405
+ out(`${entry.ok ? "ok " : "FAIL"} ${entry.lesson}${entry.rendered ? "" : " (render failed)"}
406
+ `);
407
+ const infos = /* @__PURE__ */ new Map();
408
+ for (const diagnostic of entry.diagnostics) {
409
+ if (diagnostic.severity === "info") infos.set(diagnostic.code, (infos.get(diagnostic.code) ?? 0) + 1);
410
+ else out(` [${diagnostic.severity}] ${diagnostic.code}${ownerTag(diagnostic)}: ${diagnostic.message}
411
+ `);
412
+ }
413
+ if (infos.size > 0) {
414
+ const total = [...infos.values()].reduce((sum, n) => sum + n, 0);
415
+ const byCode = [...infos].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([code, n]) => `${code} ${n}`).join(", ");
416
+ out(` + ${total} info (${byCode}); content diagnostics --lesson ${entry.lesson} lists them
417
+ `);
418
+ }
419
+ }
420
+ if (report.lessons.length === 0) out("No changed lessons.\n");
421
+ }
422
+ return report.ok ? 0 : 1;
423
+ }
424
+ default:
425
+ return usage("unknown-command", `Unknown command "${args.command}".
426
+ ${CONTENT_USAGE}`);
427
+ }
428
+ }
429
+ export {
430
+ CONTENT_USAGE,
431
+ parseContentArgs,
432
+ runContentCli
433
+ };
@@ -0,0 +1,50 @@
1
+ import { ContentVerb } from './cli-verbs.js';
2
+
3
+ /** A JSON Schema (draft 2020-12), the subset the generator writes. */
4
+ type JsonSchema = Readonly<Record<string, unknown>>;
5
+ /** `docs/authoring/cli-reference.json`, whole: what `content help --json` prints. */
6
+ interface CliReference {
7
+ /** Where it came from, and that it is generated. */
8
+ readonly generatedBy: string;
9
+ readonly verbs: readonly ContentVerb[];
10
+ /** The type every verb's `--json` prints when the run fails: a stable code and a message, never a stack. */
11
+ readonly error: string;
12
+ /** A JSON Schema of every type a verb's `--json` prints, and of every named type inside one, by name. */
13
+ readonly $defs: Readonly<Record<string, JsonSchema>>;
14
+ }
15
+ /** One verb's row, its usage line, and a self-contained JSON Schema of its `--json`. */
16
+ interface VerbHelp extends ContentVerb {
17
+ readonly usage: string;
18
+ readonly schema: JsonSchema;
19
+ }
20
+ /** What `content help <verb> --json` prints: every verb the word names (`new` names three). */
21
+ interface VerbHelpJson {
22
+ readonly verbs: readonly VerbHelp[];
23
+ }
24
+ /** `content help --json` prints the whole reference; `content help <verb> --json` that verb's rows. */
25
+ type HelpJson = CliReference | VerbHelpJson;
26
+ declare const CLI_REFERENCE_FILE = "cli-reference.json";
27
+ /** The reference beside the engine (`node_modules/explico/docs/authoring/`, or this repository's). */
28
+ declare function loadCliReference(): CliReference;
29
+ /** The verbs a word names: `symbol` one, `new` the three `new …` verbs. */
30
+ declare function verbsNamed(word: string): readonly ContentVerb[];
31
+ /** A self-contained schema of one named type: a `$ref` and the `$defs` it reaches. */
32
+ declare function schemaOf(reference: CliReference, name: string): JsonSchema;
33
+ declare function verbHelp(reference: CliReference, word: string): VerbHelpJson;
34
+ /** One line per property, `name?: type — description`, nested objects indented under their property. */
35
+ declare function shapeLines(schema: JsonSchema, indent?: string): string[];
36
+ /** A named type as a block: its name and description, then its shape. */
37
+ declare function defLines(name: string, schema: JsonSchema): string[];
38
+ /** `content help <verb>` as text: the usage, what it is for, its flags, and the shape of its `--json`. */
39
+ declare function formatVerbHelp(reference: CliReference, help: VerbHelpJson): string;
40
+ /** `docs/authoring/cli.md`, generated: one section per verb, then every `--json` type. */
41
+ declare function cliReferenceMarkdown(reference: CliReference): string;
42
+ /**
43
+ * Where `value` departs from `schema`, one message per place (`$.hits[0].tier:
44
+ * expected one of …`); empty when it conforms. The subset the generator
45
+ * writes: `$ref`, `type`, `const`, `enum`, `anyOf`, `properties`, `required`,
46
+ * `additionalProperties`, `items`.
47
+ */
48
+ declare function jsonShapeErrors(schema: JsonSchema, value: unknown, defs?: CliReference["$defs"], path?: string): string[];
49
+
50
+ export { CLI_REFERENCE_FILE, type CliReference, type HelpJson, type JsonSchema, type VerbHelp, type VerbHelpJson, cliReferenceMarkdown, defLines, formatVerbHelp, jsonShapeErrors, loadCliReference, schemaOf, shapeLines, verbHelp, verbsNamed };