@thanh01.pmt/curriculum-kit 1.4.24 → 1.4.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +71 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.mjs +71 -25
- package/dist/index.mjs.map +1 -1
- package/dist/workflow/index.cjs.map +1 -1
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1336,6 +1336,10 @@ function parseLessonFlow(lessonMarkdown) {
|
|
|
1336
1336
|
}
|
|
1337
1337
|
const durMatch = lessonMarkdown.match(/Estimated Duration:\s*([^\n]+)/i);
|
|
1338
1338
|
if (durMatch) result.estimatedDuration = durMatch[1].trim();
|
|
1339
|
+
if (!result.lessonTitle) {
|
|
1340
|
+
const h1 = lessonMarkdown.match(/^#\s+(?:LESSON[^:]*:\s*)?(.+)$/m);
|
|
1341
|
+
if (h1 && h1[1]) result.lessonTitle = h1[1].replace(/\*+/g, "").trim();
|
|
1342
|
+
}
|
|
1339
1343
|
const contractMatch = lessonMarkdown.match(/SLIDE must visualize:?\s*([^\n]+)/i);
|
|
1340
1344
|
if (contractMatch) result.slideContract = contractMatch[1].trim();
|
|
1341
1345
|
const actSeqSectionMatch = lessonMarkdown.match(/###\s*(?:\d+\.\s*)?Activity Sequence[\s\S]*?(?=(?:###|\n##\s+|$))/i);
|
|
@@ -1417,7 +1421,7 @@ var init_lessonFlowParser = __esm({
|
|
|
1417
1421
|
|
|
1418
1422
|
// src/ai/prompts/slideBlueprintPrompt.ts
|
|
1419
1423
|
function buildSlideBlueprintPrompt(params) {
|
|
1420
|
-
const { lessonFlow, targetSlideCount, stylePresetName = "Blue Professional" } = params;
|
|
1424
|
+
const { lessonFlow, targetSlideCount, stylePresetName = "Blue Professional", productSpecBlock } = params;
|
|
1421
1425
|
const activitiesSummary = lessonFlow.activities.map(
|
|
1422
1426
|
(a) => `Seq ${a.seq} [${a.phase}]: ${a.purpose} (Teacher: ${a.teacherMove} | Student: ${a.studentAction} | Time: ${a.time})`
|
|
1423
1427
|
).join("\n");
|
|
@@ -1439,7 +1443,9 @@ ${countGuidance}
|
|
|
1439
1443
|
- Target Duration: ${lessonFlow.estimatedDuration}
|
|
1440
1444
|
- Active Visual Style: "${stylePresetName}"
|
|
1441
1445
|
${lessonFlow.slideContract ? `- Slide Artifact Contract: "${lessonFlow.slideContract}"` : ""}
|
|
1442
|
-
|
|
1446
|
+
${productSpecBlock ? `
|
|
1447
|
+
${productSpecBlock}
|
|
1448
|
+
` : ""}
|
|
1443
1449
|
### \u{1F5FA}\uFE0F CANONICAL ACTIVITY SEQUENCE:
|
|
1444
1450
|
${activitiesSummary || "Standard phased sequence"}
|
|
1445
1451
|
|
|
@@ -1513,7 +1519,7 @@ function buildSlideBatchPrompt(params) {
|
|
|
1513
1519
|
skillPrompt,
|
|
1514
1520
|
language = "Vietnamese",
|
|
1515
1521
|
languageDirective = "",
|
|
1516
|
-
|
|
1522
|
+
// deprecated — see interface doc
|
|
1517
1523
|
groundContext = ""
|
|
1518
1524
|
} = params;
|
|
1519
1525
|
const slidesSpec = clusterSlides.map((s) => `
|
|
@@ -1534,7 +1540,6 @@ ${groundContext}
|
|
|
1534
1540
|
---` : ""}
|
|
1535
1541
|
### OPERATIONAL GROUND RULES FOR CHUNK GENERATION:
|
|
1536
1542
|
${languageDirective}
|
|
1537
|
-
${headingDirective}
|
|
1538
1543
|
1. **FOCUS ON CLUSTER ${clusterId}: "${clusterTitle}"**:
|
|
1539
1544
|
Generate EXACTLY the ${clusterSlides.length} slides requested in the user prompt.
|
|
1540
1545
|
2. **ZERO ABBREVIATION / NO "// TODO"**:
|
|
@@ -1725,7 +1730,7 @@ async function inferStructured(schema, systemPrompt, userPrompt, options, label)
|
|
|
1725
1730
|
const messages = [
|
|
1726
1731
|
{ role: "user", content: [systemPrompt, userPrompt].filter(Boolean).join("\n\n") }
|
|
1727
1732
|
];
|
|
1728
|
-
const raw = await runCurriculumAIInference(messages,
|
|
1733
|
+
const raw = await runCurriculumAIInference(messages, "", options.runnerOptions, (token, type) => {
|
|
1729
1734
|
if (type === "usage") options.onProgress?.("@illustrator", token, { type: "usage" });
|
|
1730
1735
|
});
|
|
1731
1736
|
const fenceMatch = raw.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
@@ -1911,7 +1916,8 @@ async function executeSlideProductionWorkflow(options) {
|
|
|
1911
1916
|
const blueprintPrompt = buildSlideBlueprintPrompt({
|
|
1912
1917
|
lessonFlow,
|
|
1913
1918
|
targetSlideCount,
|
|
1914
|
-
stylePresetName: stylePreset?.name || "Blue Professional"
|
|
1919
|
+
stylePresetName: stylePreset?.name || "Blue Professional",
|
|
1920
|
+
productSpecBlock: options.productSpecBlock
|
|
1915
1921
|
});
|
|
1916
1922
|
let blueprintItems;
|
|
1917
1923
|
let allGeneratedSlides;
|
|
@@ -1994,7 +2000,6 @@ ${p.content}`).join("\n\n") : lessonFlow.rawContent.slice(0, 3500);
|
|
|
1994
2000
|
skillPrompt,
|
|
1995
2001
|
language,
|
|
1996
2002
|
languageDirective,
|
|
1997
|
-
headingDirective,
|
|
1998
2003
|
groundContext: options.groundContext
|
|
1999
2004
|
});
|
|
2000
2005
|
try {
|
|
@@ -11719,19 +11724,30 @@ function extractToolchainFacts(refPack) {
|
|
|
11719
11724
|
const factLines = [];
|
|
11720
11725
|
for (const rawLine of excerpt.split("\n")) {
|
|
11721
11726
|
const line = rawLine.trim();
|
|
11722
|
-
if (!line || line.startsWith("#")) continue;
|
|
11723
|
-
if (
|
|
11724
|
-
|
|
11725
|
-
}
|
|
11727
|
+
if (!line || line.startsWith("#") || line.startsWith("<!--")) continue;
|
|
11728
|
+
if (!/\d{1,2}(?:\.\d{1,2})?/.test(line) && !/Xcode|Swift|macOS|iOS|SDK|Node|Python|JDK|\.NET/i.test(line)) continue;
|
|
11729
|
+
factLines.push(line.replace(/^[*\-•]\s*/, "- "));
|
|
11726
11730
|
if (factLines.length >= 8) break;
|
|
11727
11731
|
}
|
|
11728
11732
|
return factLines;
|
|
11729
11733
|
}
|
|
11734
|
+
function derivePrimaryTechnology(facts, briefing) {
|
|
11735
|
+
const explicit = (briefing?.coreTechnology || briefing?.techStack || "").trim();
|
|
11736
|
+
if (explicit && explicit.length <= 80) return explicit;
|
|
11737
|
+
const found = [];
|
|
11738
|
+
const CANON = ["Swift", "SwiftUI", "macOS", "iOS", "Xcode", "SwiftData", "Node.js", "TypeScript", "Python", "React"];
|
|
11739
|
+
for (const name of CANON) {
|
|
11740
|
+
if (facts.some((f) => f.includes(name)) && !found.includes(name)) found.push(name);
|
|
11741
|
+
}
|
|
11742
|
+
if (found.length > 0) return found.slice(0, 4).join(" + ");
|
|
11743
|
+
const topic = (briefing?.topic || "").trim();
|
|
11744
|
+
return topic && topic.length <= 60 ? topic : "";
|
|
11745
|
+
}
|
|
11730
11746
|
function buildProductSpecBlock(input) {
|
|
11731
11747
|
const b = input.briefing || {};
|
|
11732
|
-
const platform = (input.overrides?.primaryLanguage || b.coreTechnology || b.techStack || b.topic || "").trim();
|
|
11733
|
-
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11734
11748
|
const toolchainFacts = input.refPack ? extractToolchainFacts(input.refPack) : [];
|
|
11749
|
+
const platform = (input.overrides?.primaryLanguage || derivePrimaryTechnology(toolchainFacts, b) || "").trim();
|
|
11750
|
+
const hardware = (b.hardwarePlatform || b.studentEquipment || b.classDynamic || "").trim();
|
|
11735
11751
|
const lines = [];
|
|
11736
11752
|
lines.push("[PRODUCT SPEC \u2014 CANONICAL, BINDING FOR EVERY ARTIFACT OF THIS PROJECT]:");
|
|
11737
11753
|
lines.push("All decisions below are PROJECT-WIDE. Every artifact (LESSON, satellites, KX)");
|
|
@@ -11747,7 +11763,7 @@ function buildProductSpecBlock(input) {
|
|
|
11747
11763
|
if (input.overrides?.deliverableTemplate) {
|
|
11748
11764
|
lines.push(`- Deliverable template: ${input.overrides.deliverableTemplate}`);
|
|
11749
11765
|
}
|
|
11750
|
-
if (toolchainFacts.length > 0) {
|
|
11766
|
+
if (toolchainFacts.length > 0 && !input.skipToolchainList) {
|
|
11751
11767
|
lines.push("- Toolchain & versions (ground truth):");
|
|
11752
11768
|
for (const f of toolchainFacts) lines.push(` ${f}`);
|
|
11753
11769
|
}
|
|
@@ -11875,7 +11891,9 @@ async function ensureKnowledgeExposition(options) {
|
|
|
11875
11891
|
const productSpecBlock = buildProductSpecBlock({
|
|
11876
11892
|
refPack,
|
|
11877
11893
|
briefing: techStack || hardwarePlatform ? { techStack, hardwarePlatform } : void 0,
|
|
11878
|
-
overrides: options.productSpec
|
|
11894
|
+
overrides: options.productSpec,
|
|
11895
|
+
skipToolchainList: true
|
|
11896
|
+
// same RefPack excerpt already in [GROUND TRUTH]
|
|
11879
11897
|
});
|
|
11880
11898
|
const originalUserPrompt = buildUserPrompt(session, plan, glossary, targetLanguage, options.slcMarkdown, refPack) + (productSpecBlock ? "\n\n" + productSpecBlock : "");
|
|
11881
11899
|
const content = (await llmFn(
|
|
@@ -11975,17 +11993,28 @@ async function ensureExpositionForLesson(storage, projectId, lessonCode, options
|
|
|
11975
11993
|
const lpRaw = await storage.readSotDocument(projectId, "LEARNER_PROFILE.md");
|
|
11976
11994
|
if (lpRaw) {
|
|
11977
11995
|
const hwPatterns = [
|
|
11996
|
+
// "Cơ sở phần cứng (Hardware Ecosystem): Học sinh thực hành trên máy..."
|
|
11997
|
+
// — the audit: the old pattern captured the parenthetical label
|
|
11998
|
+
// "(Hardware Ecosystem):" itself. Capture AFTER the closing paren + colon.
|
|
11999
|
+
/\([^)]*Ecosystem[^)]*\)\s*:\s*([^\n\r]+)/i,
|
|
11978
12000
|
/(?:Student Equipment|Hardware|Platform|Thiết bị|Thiết bị học tập)\s*(?:\(|:|\*)*\s*([^\n\r]+?)(?:\s*\*\*|[.).]?\s*$)/i,
|
|
11979
12001
|
/(?:máy|device|machine)\s+([^,.;\n]{4,60}(?:M\d|Intel|PC|computer|mini)[^,.;\n]{0,30})/i
|
|
11980
12002
|
];
|
|
11981
12003
|
for (const p of hwPatterns) {
|
|
11982
12004
|
const m = lpRaw.match(p);
|
|
11983
|
-
if (m && !hw) {
|
|
11984
|
-
hw = m[1].trim();
|
|
12005
|
+
if (m && m[1] && !hw) {
|
|
12006
|
+
hw = m[1].trim().replace(/\*+/g, "").slice(0, 300);
|
|
11985
12007
|
break;
|
|
11986
12008
|
}
|
|
11987
12009
|
}
|
|
11988
12010
|
}
|
|
12011
|
+
if (!hw) {
|
|
12012
|
+
const rpRaw = await storage.readSotDocument(projectId, "REFERENCE_PACK.md");
|
|
12013
|
+
if (rpRaw) {
|
|
12014
|
+
const rp = rpRaw.match(/(?:School Lab|Thiết bị lớp học)[^\n]*\*\*([^*]+(?:M\d|Mac)[^*]*)\*\*/i);
|
|
12015
|
+
if (rp && rp[1]) hw = rp[1].trim();
|
|
12016
|
+
}
|
|
12017
|
+
}
|
|
11989
12018
|
} catch {
|
|
11990
12019
|
}
|
|
11991
12020
|
}
|
|
@@ -12722,11 +12751,11 @@ function buildSessionSliceContext(plan, lessonCode) {
|
|
|
12722
12751
|
lines.push("- Time split: knowledge " + s.knowledge_minutes + "m / practice " + s.practice_minutes + "m / overhead " + s.overhead_minutes + "m");
|
|
12723
12752
|
lines.push("- Depth assignments: " + s.depth_assignments.map((d) => d.node_id + "=" + d.depth.toUpperCase() + "(" + d.source + ")").join("; "));
|
|
12724
12753
|
if (s.prerequisite_decisions.length > 0) {
|
|
12725
|
-
lines.push("- Prerequisite decisions [
|
|
12754
|
+
lines.push("- Prerequisite decisions [" + s.prerequisite_decisions.length + "]:");
|
|
12726
12755
|
for (const d of s.prerequisite_decisions) lines.push(" * " + d.node_id + ": " + d.decision + " \u2014 " + d.reason);
|
|
12727
12756
|
}
|
|
12728
12757
|
if (s.scaffold_decisions.length > 0) {
|
|
12729
|
-
lines.push("- Scaffold decisions [
|
|
12758
|
+
lines.push("- Scaffold decisions [" + s.scaffold_decisions.length + "]:");
|
|
12730
12759
|
for (const d of s.scaffold_decisions) lines.push(" * " + d.item + ": " + d.decision + (d.minutes_saved > 0 ? " (saves " + d.minutes_saved + "m)" : "") + " \u2014 " + d.reason);
|
|
12731
12760
|
}
|
|
12732
12761
|
lines.push("- Exit evidence: " + s.exit_evidence.join("; "));
|
|
@@ -25961,8 +25990,11 @@ function buildFrameworkExcerptForLesson(framework, lessonId) {
|
|
|
25961
25990
|
});
|
|
25962
25991
|
const rowIdx = scopeRows[0] ?? -1;
|
|
25963
25992
|
const currentRow = rowIdx >= 0 ? lines[rowIdx] : "";
|
|
25964
|
-
const
|
|
25965
|
-
const
|
|
25993
|
+
const isSeparatorRow = (l) => !!l && /^\s*\|?[\s:|-]+\|?\s*$/.test(l) && l.includes("-");
|
|
25994
|
+
const prevRaw = rowIdx > 0 ? lines[rowIdx - 1] : "";
|
|
25995
|
+
const prevRow = prevRaw.trim().startsWith("|") && !isSeparatorRow(prevRaw) ? prevRaw : "";
|
|
25996
|
+
const nextRaw = lines[rowIdx + 1] || "";
|
|
25997
|
+
const nextRow = nextRaw.trim().startsWith("|") && !isSeparatorRow(nextRaw) ? nextRaw : "";
|
|
25966
25998
|
const hierStart = lines.findIndex((l) => /Structural Hierarchy/i.test(l));
|
|
25967
25999
|
let hierarchyBlock = "";
|
|
25968
26000
|
if (hierStart >= 0) {
|
|
@@ -26368,6 +26400,7 @@ ${renderHorizonPromptBlock(horizon)}`;
|
|
|
26368
26400
|
} catch (hErr) {
|
|
26369
26401
|
console.warn(`[produceSingleLesson] Notice: Curriculum Horizon extraction:`, hErr?.message || hErr);
|
|
26370
26402
|
}
|
|
26403
|
+
let productSpecBlock = "";
|
|
26371
26404
|
const buildGroundTruthBlock = () => {
|
|
26372
26405
|
const parts = [];
|
|
26373
26406
|
if (expositionContext) {
|
|
@@ -26378,9 +26411,9 @@ ${expositionContext}`);
|
|
|
26378
26411
|
parts.push(`### REFERENCE PACK GROUND TRUTH
|
|
26379
26412
|
${effectiveRefPack}`);
|
|
26380
26413
|
}
|
|
26381
|
-
|
|
26382
|
-
if (
|
|
26383
|
-
parts.push(
|
|
26414
|
+
productSpecBlock = buildProductSpecBlock({ refPack, briefing: options.briefing, skipToolchainList: true });
|
|
26415
|
+
if (productSpecBlock) {
|
|
26416
|
+
parts.push(productSpecBlock);
|
|
26384
26417
|
}
|
|
26385
26418
|
return parts.length > 0 ? `
|
|
26386
26419
|
|
|
@@ -26397,10 +26430,17 @@ ${standardsContext}` : "";
|
|
|
26397
26430
|
const sessionSliceBlock = sessionSliceContext ? `
|
|
26398
26431
|
|
|
26399
26432
|
${sessionSliceContext}` : "";
|
|
26433
|
+
const platformGuardrail = buildDomainLexiconGuardrail(
|
|
26434
|
+
options.briefing?.techStack || options.briefing?.coreTechnology,
|
|
26435
|
+
options.briefing?.hardwarePlatform
|
|
26436
|
+
);
|
|
26437
|
+
const guardrailBlock = `
|
|
26438
|
+
|
|
26439
|
+
${platformGuardrail}`;
|
|
26400
26440
|
const assembleCommonContext = (sg) => `${baseContextPrefix}
|
|
26401
26441
|
|
|
26402
26442
|
[CONTENT STYLE GUIDE EXCERPT]:
|
|
26403
|
-
${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBlock}${horizonBlock}`;
|
|
26443
|
+
${sg}${standardsBlock}${glossaryBlock}${buildGroundTruthBlock()}${sessionSliceBlock}${horizonBlock}${guardrailBlock}`;
|
|
26404
26444
|
let commonContext = assembleCommonContext(effectiveStyleGuide);
|
|
26405
26445
|
const MAX_COMPOSITE_PROMPT_CHARS = 3e4;
|
|
26406
26446
|
const runnerOptions = options.customInference ? { customInference: options.customInference } : {};
|
|
@@ -26926,6 +26966,11 @@ ${buildHeadingDirective("ACT", slcMarkdown, targetLang)}`;
|
|
|
26926
26966
|
const quizPrompt = `You are @assessor (Lead Psychometrician & Competency Assessment Specialist).
|
|
26927
26967
|
Based on Master Lesson Plan \`LESSON_${lessonCode}.md\`, author the scenario-based diagnostic question bank: \`QUIZ_${lessonCode}.md\`.
|
|
26928
26968
|
|
|
26969
|
+
SCOPE & PACING (prompt audit S-6):
|
|
26970
|
+
- This is a FORMATIVE CHECKPOINT for ONE 90-minute session, NOT an exam bank.
|
|
26971
|
+
- Size the bank to fit the session's Wrap-up (~10 minutes of answering): the MCQ bank, code-tracing question, and rubric together must be completable by a student in that time.
|
|
26972
|
+
- Question ONLY concepts inside this session's scope (see SESSION SLICE + ALLOWED DESIGN SPACE in the context).
|
|
26973
|
+
|
|
26929
26974
|
Mandatory Format Requirements:
|
|
26930
26975
|
1. EXACT YAML Frontmatter \u2014 copy verbatim, then fill in the blanks:
|
|
26931
26976
|
---
|
|
@@ -27010,6 +27055,7 @@ ${buildHeadingDirective("QUIZ", slcMarkdown, targetLang)}`;
|
|
|
27010
27055
|
languageDirective,
|
|
27011
27056
|
headingDirective: buildHeadingDirective("SLIDE", slcMarkdown, targetLang),
|
|
27012
27057
|
groundContext: slideGroundContext,
|
|
27058
|
+
productSpecBlock: productSpecBlock || void 0,
|
|
27013
27059
|
satelliteContext,
|
|
27014
27060
|
runnerOptions,
|
|
27015
27061
|
onProgress: (agent, msg, meta) => {
|