@thanh01.pmt/curriculum-kit 1.2.1 → 1.4.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.
@@ -6258,6 +6258,7 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
6258
6258
  }
6259
6259
  const slcMap = normalizeSlcContract(opts.sectionLanguageContract);
6260
6260
  const sections = parseMarkdownSections(source, slcMap, opts.artifactType);
6261
+ const metaBlock = buildMetaBlock(source);
6261
6262
  if (sections.length === 0) {
6262
6263
  issues.push("parse:no-sections");
6263
6264
  const excerpt2 = truncateByBudget(source, budget);
@@ -6291,12 +6292,13 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
6291
6292
  const block = `## ${heading}
6292
6293
 
6293
6294
  ${trimmedBody}`;
6294
- if (used + block.length > budget && blocks.length > 0) return false;
6295
+ if (used + block.length > budget && used > 0) return false;
6295
6296
  blocks.push(block);
6296
6297
  used += block.length;
6297
6298
  includedSections.push(key);
6298
6299
  return true;
6299
6300
  };
6301
+ blocks.push(metaBlock);
6300
6302
  let sectionAware = false;
6301
6303
  for (const key of priorities) {
6302
6304
  const sec = byCanonical.get(key);
@@ -6313,6 +6315,9 @@ ${trimmedBody}`;
6313
6315
  if (used >= budget) break;
6314
6316
  if (!tryAdd(sec.cleanTitle, sec.body, sec.cleanTitle)) break;
6315
6317
  }
6318
+ if (includedSections.length === 0 && unmatched.length > 0) {
6319
+ issues.push("parse:no-canonical-resolution");
6320
+ }
6316
6321
  if (priorities.length > 0 && !priorities.some((k) => includedSections.includes(k))) {
6317
6322
  issues.push("parse:no-priority-resolution");
6318
6323
  }
@@ -6321,14 +6326,14 @@ ${trimmedBody}`;
6321
6326
  excerpt = truncateByBudget(excerpt, budget);
6322
6327
  }
6323
6328
  const effectiveMinChars = Math.min(minContentChars, source.length);
6324
- if (excerpt.length < effectiveMinChars) {
6329
+ if (excerpt.length - metaBlock.length < effectiveMinChars) {
6325
6330
  issues.push("content:insufficient");
6326
6331
  }
6327
- const verified = !issues.includes("parse:no-priority-resolution") && excerpt.length >= effectiveMinChars;
6332
+ const verified = !issues.includes("parse:no-priority-resolution") && !issues.includes("parse:no-canonical-resolution") && excerpt.length - metaBlock.length >= effectiveMinChars;
6328
6333
  return {
6329
6334
  excerpt,
6330
6335
  verified,
6331
- sectionAware: sectionAware || priorities.length === 0 && sections.length > 0,
6336
+ sectionAware: sectionAware || unmatched.length === 0,
6332
6337
  includedSections,
6333
6338
  issues,
6334
6339
  tokenEstimate: Math.round(excerpt.length / 4)
@@ -6444,6 +6449,18 @@ function truncateByBudget(source, budget) {
6444
6449
  if (fenceCount % 2 === 1) cut += "\n```";
6445
6450
  return cut;
6446
6451
  }
6452
+ function buildMetaBlock(source) {
6453
+ const fm = source.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
6454
+ const pick = (key) => {
6455
+ if (!fm) return "";
6456
+ const m = fm[1].match(new RegExp(`^${key}:\\s*"?([^"\\n]+)"?`, "m"));
6457
+ return (m?.[1] || "").trim();
6458
+ };
6459
+ const id = pick("id");
6460
+ const title = pick("title");
6461
+ const type = pick("type");
6462
+ return `<!-- SOURCE: ${type || "ARTIFACT"} | ${id || "unknown"}${title ? ` \u2014 ${title}` : ""} (section-aware excerpt; canonical knowledge, do not contradict) -->`;
6463
+ }
6447
6464
  init_errors();
6448
6465
 
6449
6466
  // src/services/languageDirective.ts
@@ -7025,6 +7042,24 @@ function getArtifactSpecializedInvariants(type, req) {
7025
7042
  const studentCount = req.studentCount || "25";
7026
7043
  switch (type) {
7027
7044
  case "slide": {
7045
+ if (req.slidesEngine === "html") {
7046
+ return `
7047
+ ### \u{1F3AC} INTERACTIVE HTML SLIDE ENGINE CONTRACT:
7048
+ You generate structured slide decks for the LearnWell HTML Slide Presentation Engine.
7049
+ Output a single valid JSON object matching: { "title": "...", "theme": "ocean", "slides": [{ "layoutId": "hero-cover", "slots": { ... }, "notes": "..." }] }
7050
+
7051
+ Layout Presets available:
7052
+ - hero-cover (title, subtitle, presenter, date, tag)
7053
+ - two-column-split (col1Title, col1Items[], col2Title, col2Items[])
7054
+ - code-explainer (filename, language, code, highlightLines, annotations[])
7055
+ - metrics-3-card (card1Title, card1Val, card1Desc, ...)
7056
+ - process-flow (steps: [{ label, desc, status }])
7057
+ - quiz-checkpoint (question, options[], answerIdx, explanation)
7058
+ - tiered-practice-3cards (foundation, standard, challenge cards)
7059
+ - takeaways-summary (takeaways: bullet points)
7060
+
7061
+ Mandatory Presenter Notes on every slide with Talk Track, Cold-Call, and Scaffolding Tip.`;
7062
+ }
7028
7063
  const slideCount = cfg.slideCount || 10;
7029
7064
  const presentationStyle = cfg.presentationStyle || "modern";
7030
7065
  const presentationDuration = cfg.presentationDuration || 45;