@thanh01.pmt/curriculum-kit 1.4.43 → 1.4.45

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 (121) hide show
  1. package/dist/ai/index.cjs +66 -470
  2. package/dist/ai/index.cjs.map +1 -1
  3. package/dist/ai/index.d.cts +77 -77
  4. package/dist/ai/index.d.ts +77 -77
  5. package/dist/ai/index.mjs +64 -469
  6. package/dist/ai/index.mjs.map +1 -1
  7. package/dist/index.cjs +478 -837
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +124 -58
  10. package/dist/index.d.ts +124 -58
  11. package/dist/index.mjs +470 -829
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/pipeline/index.cjs +99 -484
  14. package/dist/pipeline/index.cjs.map +1 -1
  15. package/dist/pipeline/index.mjs +97 -483
  16. package/dist/pipeline/index.mjs.map +1 -1
  17. package/dist/schemas/index.d.cts +1064 -1064
  18. package/dist/schemas/index.d.ts +1064 -1064
  19. package/dist/standards/index.d.cts +2 -2
  20. package/dist/standards/index.d.ts +2 -2
  21. package/dist/{standardsCoverageGate-49pJq5t8.d.cts → standardsCoverageGate-CLnq0d0s.d.cts} +12 -12
  22. package/dist/{standardsCoverageGate-49pJq5t8.d.ts → standardsCoverageGate-CLnq0d0s.d.ts} +12 -12
  23. package/dist/templates/ACT_template.md +18 -9
  24. package/dist/templates/ALIGNMENT_MATRIX_template.md +5 -0
  25. package/dist/templates/ART_DIRECTION_template.md +7 -0
  26. package/dist/templates/ASSET_MANIFEST_template.md +5 -0
  27. package/dist/templates/BASELINE_STANDARD.md +5 -0
  28. package/dist/templates/BELL_RINGER_template.md +13 -13
  29. package/dist/templates/CHANGELOG_template.md +3 -0
  30. package/dist/templates/CHOICE_BOARD_template.md +8 -5
  31. package/dist/templates/CODE_template.md +5 -6
  32. package/dist/templates/CONTENT_CONTINUITY_template.md +6 -0
  33. package/dist/templates/CONTENT_STYLE_GUIDE_template.md +4 -5
  34. package/dist/templates/CURRICULUM_FRAMEWORK_template.md +5 -6
  35. package/dist/templates/DEPENDENCY_MAP_template.md +3 -4
  36. package/dist/templates/EXIT_TICKET_template.md +8 -8
  37. package/dist/templates/EXT_template.md +3 -0
  38. package/dist/templates/GLOSSARY_template.md +1 -2
  39. package/dist/templates/GRAPHIC_ORGANIZER_template.md +8 -8
  40. package/dist/templates/GUIDE_template.md +5 -6
  41. package/dist/templates/HANDOUT_template.md +5 -6
  42. package/dist/templates/HANDOVER_DOCUMENT_template.md +8 -0
  43. package/dist/templates/KNOWLEDGE_EXPOSITION_template.md +16 -6
  44. package/dist/templates/LAB_TEMPLATE.md +78 -0
  45. package/dist/templates/LEARNER_PROFILE_template.md +4 -5
  46. package/dist/templates/LESSONS_LEARNED_template.md +8 -0
  47. package/dist/templates/LESSON_5E_template.md +24 -9
  48. package/dist/templates/LESSON_EDP_template.md +26 -10
  49. package/dist/templates/LESSON_GENERAL_template.md +22 -8
  50. package/dist/templates/OUTPUT_CONSTRAINTS_template.md +7 -0
  51. package/dist/templates/PACKAGING_SPEC_template.md +8 -0
  52. package/dist/templates/PILOT_FEEDBACK_template.md +6 -0
  53. package/dist/templates/PROJECT_BRIEF_template.md +8 -0
  54. package/dist/templates/PROJECT_STATUS_template.md +9 -0
  55. package/dist/templates/QUIZ_template.md +7 -0
  56. package/dist/templates/QUIZ_v4.6_template.md +4 -5
  57. package/dist/templates/REFERENCE_PACK_template.md +4 -0
  58. package/dist/templates/REVIEW_GAME_template.md +11 -8
  59. package/dist/templates/RUBRIC_template.md +2 -3
  60. package/dist/templates/SCIENCE_LAB_template.md +11 -5
  61. package/dist/templates/SECTION_LANGUAGE_CONTRACT_template.md +44 -0
  62. package/dist/templates/SELF_LAB_template.md +174 -0
  63. package/dist/templates/SLIDE_template.md +23 -0
  64. package/dist/templates/STATION_ROTATION_template.md +11 -11
  65. package/dist/templates/TASK_CARDS_template.md +8 -8
  66. package/dist/templates/TIERED_PRACTICE_template.md +14 -10
  67. package/dist/templates/UPDATE_ROADMAP_template.md +7 -0
  68. package/dist/templates/WKS_template.md +5 -6
  69. package/dist/workflow/index.cjs +250 -672
  70. package/dist/workflow/index.cjs.map +1 -1
  71. package/dist/workflow/index.d.cts +1 -1
  72. package/dist/workflow/index.d.ts +1 -1
  73. package/dist/workflow/index.mjs +248 -670
  74. package/dist/workflow/index.mjs.map +1 -1
  75. package/docs/refs/templates/ACT_template.md +18 -9
  76. package/docs/refs/templates/ALIGNMENT_MATRIX_template.md +5 -0
  77. package/docs/refs/templates/ART_DIRECTION_template.md +7 -0
  78. package/docs/refs/templates/ASSET_MANIFEST_template.md +5 -0
  79. package/docs/refs/templates/BASELINE_STANDARD.md +5 -0
  80. package/docs/refs/templates/BELL_RINGER_template.md +13 -13
  81. package/docs/refs/templates/CHANGELOG_template.md +3 -0
  82. package/docs/refs/templates/CHOICE_BOARD_template.md +8 -5
  83. package/docs/refs/templates/CODE_template.md +5 -6
  84. package/docs/refs/templates/CONTENT_CONTINUITY_template.md +6 -0
  85. package/docs/refs/templates/CONTENT_STYLE_GUIDE_template.md +4 -5
  86. package/docs/refs/templates/CURRICULUM_FRAMEWORK_template.md +5 -6
  87. package/docs/refs/templates/DEPENDENCY_MAP_template.md +3 -4
  88. package/docs/refs/templates/EXIT_TICKET_template.md +8 -8
  89. package/docs/refs/templates/EXT_template.md +3 -0
  90. package/docs/refs/templates/GLOSSARY_template.md +1 -2
  91. package/docs/refs/templates/GRAPHIC_ORGANIZER_template.md +8 -8
  92. package/docs/refs/templates/GUIDE_template.md +5 -6
  93. package/docs/refs/templates/HANDOUT_template.md +5 -6
  94. package/docs/refs/templates/HANDOVER_DOCUMENT_template.md +8 -0
  95. package/docs/refs/templates/KNOWLEDGE_EXPOSITION_template.md +16 -6
  96. package/docs/refs/templates/LAB_TEMPLATE.md +78 -0
  97. package/docs/refs/templates/LEARNER_PROFILE_template.md +4 -5
  98. package/docs/refs/templates/LESSONS_LEARNED_template.md +8 -0
  99. package/docs/refs/templates/LESSON_5E_template.md +24 -9
  100. package/docs/refs/templates/LESSON_EDP_template.md +26 -10
  101. package/docs/refs/templates/LESSON_GENERAL_template.md +22 -8
  102. package/docs/refs/templates/OUTPUT_CONSTRAINTS_template.md +7 -0
  103. package/docs/refs/templates/PACKAGING_SPEC_template.md +8 -0
  104. package/docs/refs/templates/PILOT_FEEDBACK_template.md +6 -0
  105. package/docs/refs/templates/PROJECT_BRIEF_template.md +8 -0
  106. package/docs/refs/templates/PROJECT_STATUS_template.md +9 -0
  107. package/docs/refs/templates/QUIZ_template.md +7 -0
  108. package/docs/refs/templates/QUIZ_v4.6_template.md +4 -5
  109. package/docs/refs/templates/REFERENCE_PACK_template.md +4 -0
  110. package/docs/refs/templates/REVIEW_GAME_template.md +11 -8
  111. package/docs/refs/templates/RUBRIC_template.md +2 -3
  112. package/docs/refs/templates/SCIENCE_LAB_template.md +11 -5
  113. package/docs/refs/templates/SECTION_LANGUAGE_CONTRACT_template.md +44 -0
  114. package/docs/refs/templates/SELF_LAB_template.md +174 -0
  115. package/docs/refs/templates/SLIDE_template.md +23 -0
  116. package/docs/refs/templates/STATION_ROTATION_template.md +11 -11
  117. package/docs/refs/templates/TASK_CARDS_template.md +8 -8
  118. package/docs/refs/templates/TIERED_PRACTICE_template.md +14 -10
  119. package/docs/refs/templates/UPDATE_ROADMAP_template.md +7 -0
  120. package/docs/refs/templates/WKS_template.md +5 -6
  121. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var fs2 = require('fs');
4
- var path3 = require('path');
3
+ var fs3 = require('fs');
4
+ var path4 = require('path');
5
5
  var google = require('@ai-sdk/google');
6
6
  var openai = require('@ai-sdk/openai');
7
7
  var deepseek = require('@ai-sdk/deepseek');
8
+ var url = require('url');
8
9
  var zod = require('zod');
9
10
  var ai = require('ai');
10
11
  var pRetry = require('p-retry');
@@ -13,14 +14,13 @@ var pLimit = require('p-limit');
13
14
  var fsPromises = require('fs/promises');
14
15
  var crypto = require('crypto');
15
16
  var supabaseJs = require('@supabase/supabase-js');
16
- var url = require('url');
17
17
  var rest = require('@octokit/rest');
18
18
 
19
19
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
20
20
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
 
22
- var fs2__default = /*#__PURE__*/_interopDefault(fs2);
23
- var path3__default = /*#__PURE__*/_interopDefault(path3);
22
+ var fs3__default = /*#__PURE__*/_interopDefault(fs3);
23
+ var path4__default = /*#__PURE__*/_interopDefault(path4);
24
24
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
25
25
  var pLimit__default = /*#__PURE__*/_interopDefault(pLimit);
26
26
  var fsPromises__default = /*#__PURE__*/_interopDefault(fsPromises);
@@ -177,18 +177,18 @@ function resolveApiKey(keyName, explicitKey) {
177
177
  try {
178
178
  const currentDir = process.cwd();
179
179
  const envPaths = [
180
- path3__default.default.resolve(currentDir, ".env.local"),
181
- path3__default.default.resolve(currentDir, ".env"),
182
- path3__default.default.resolve(currentDir, "apps/web/.env.local"),
183
- path3__default.default.resolve(currentDir, "packages/learnwell-platform/.env"),
184
- path3__default.default.resolve(currentDir, "..", ".env.local"),
185
- path3__default.default.resolve(currentDir, "..", ".env"),
186
- path3__default.default.resolve(currentDir, "../..", ".env.local"),
187
- path3__default.default.resolve(currentDir, "../..", ".env")
180
+ path4__default.default.resolve(currentDir, ".env.local"),
181
+ path4__default.default.resolve(currentDir, ".env"),
182
+ path4__default.default.resolve(currentDir, "apps/web/.env.local"),
183
+ path4__default.default.resolve(currentDir, "packages/learnwell-platform/.env"),
184
+ path4__default.default.resolve(currentDir, "..", ".env.local"),
185
+ path4__default.default.resolve(currentDir, "..", ".env"),
186
+ path4__default.default.resolve(currentDir, "../..", ".env.local"),
187
+ path4__default.default.resolve(currentDir, "../..", ".env")
188
188
  ];
189
189
  for (const envPath of envPaths) {
190
- if (fs2__default.default.existsSync(envPath)) {
191
- const content = fs2__default.default.readFileSync(envPath, "utf-8");
190
+ if (fs3__default.default.existsSync(envPath)) {
191
+ const content = fs3__default.default.readFileSync(envPath, "utf-8");
192
192
  for (const line of content.split("\n")) {
193
193
  const trimmed = line.trim();
194
194
  if (trimmed && !trimmed.startsWith("#") && trimmed.startsWith(keyName + "=")) {
@@ -1178,6 +1178,229 @@ var init_provider_factory = __esm({
1178
1178
  };
1179
1179
  }
1180
1180
  });
1181
+ function stripTemplateFrontmatter(raw) {
1182
+ let body = raw;
1183
+ if (body.startsWith("---")) {
1184
+ const end = body.indexOf("\n---", 3);
1185
+ if (end !== -1) body = body.slice(end + 4);
1186
+ }
1187
+ return body.split("\n").filter((l) => l.trim() !== "<!-- INJECT_BODY -->").join("\n").replace(/^\s*\n/, "").trimEnd();
1188
+ }
1189
+ function extractRequiredSections(raw) {
1190
+ const lines = raw.split("\n");
1191
+ const start = lines.findIndex((l) => /^template_required_sections:\s*$/.test(l.trim()));
1192
+ if (start === -1) return null;
1193
+ const items = [];
1194
+ for (let i = start + 1; i < lines.length; i++) {
1195
+ const line = lines[i] ?? "";
1196
+ if (line.trim() === "") continue;
1197
+ if (!/^\s+-\s/.test(line)) break;
1198
+ items.push(line.trim().replace(/^-\s+/, "").replace(/^["']|["']$/g, ""));
1199
+ }
1200
+ return items.length > 0 ? items : null;
1201
+ }
1202
+ function stripHeadingKey(h) {
1203
+ return h.replace(/\[(REQUIRED|OPTIONAL|CONDITIONAL)\]\s*/gi, "").replace(/^\s*\d+[.)]\s+/, "").replace(/\s+/g, " ").trim();
1204
+ }
1205
+ function isVariableHeading(text) {
1206
+ return /\{\{[^}]*\}\}/.test(text) || /\[[^\]]+\]/.test(text);
1207
+ }
1208
+ function collectHeadings(body) {
1209
+ const out = [];
1210
+ let inFence = false;
1211
+ for (const line of body.split("\n")) {
1212
+ if (/^(```|~~~)/.test(line.trim())) {
1213
+ inFence = !inFence;
1214
+ continue;
1215
+ }
1216
+ if (inFence) continue;
1217
+ const m = line.match(/^(#{2,3})\s+(.*)$/);
1218
+ if (!m) continue;
1219
+ const text = m[2].trim();
1220
+ const optional = /^\[OPTIONAL\]/i.test(text);
1221
+ const name = stripHeadingKey(text);
1222
+ if (!name || isVariableHeading(name)) continue;
1223
+ out.push({ level: m[1].length, name, optional });
1224
+ }
1225
+ return out;
1226
+ }
1227
+ function extractEchoSections(raw) {
1228
+ const hs = collectHeadings(stripTemplateFrontmatter(raw));
1229
+ return {
1230
+ required: hs.filter((h) => !h.optional).map((h) => h.name),
1231
+ optional: hs.filter((h) => h.optional).map((h) => h.name)
1232
+ };
1233
+ }
1234
+ function extractConditionalSections(raw) {
1235
+ const body = stripTemplateFrontmatter(raw);
1236
+ return body.split("\n").filter((l) => /^#{2,3} .*\[CONDITIONAL\]/i.test(l)).map((l) => stripHeadingKey(l.replace(/^#{2,3}\s*/, "").trim())).filter((s) => s.length > 0 && !isVariableHeading(s));
1237
+ }
1238
+ function extractDeclaredLanguage(raw) {
1239
+ const m = raw.match(/^template_language:\s*["']?([a-zA-Z-]+)["']?\s*$/m);
1240
+ return m?.[1]?.toLowerCase() || "en";
1241
+ }
1242
+ function candidateDirs(explicitDir) {
1243
+ const dirs = [];
1244
+ if (explicitDir) dirs.push(explicitDir);
1245
+ if (process.env.CURRICULUM_TEMPLATES_DIR) dirs.push(process.env.CURRICULUM_TEMPLATES_DIR);
1246
+ dirs.push(path4__default.default.resolve(process.cwd(), "_templates"));
1247
+ for (const rel of KIT_TEMPLATE_FALLBACK_DIRS) {
1248
+ dirs.push(path4__default.default.resolve(_moduleDir, rel));
1249
+ dirs.push(path4__default.default.resolve(process.cwd(), rel));
1250
+ dirs.push(path4__default.default.resolve(process.cwd(), "packages/curriculum-kit", rel));
1251
+ }
1252
+ return dirs;
1253
+ }
1254
+ function findTemplatePath(templateName, explicitDir) {
1255
+ for (const dir of candidateDirs(explicitDir)) {
1256
+ const p = path4__default.default.join(dir, templateName);
1257
+ if (!fs3__default.default.existsSync(p)) continue;
1258
+ try {
1259
+ fs3__default.default.accessSync(p, fs3__default.default.constants.R_OK);
1260
+ return p;
1261
+ } catch {
1262
+ }
1263
+ }
1264
+ return null;
1265
+ }
1266
+ function resolveArtifactTemplate(templateName, opts) {
1267
+ const p = findTemplatePath(templateName, opts?.templatesDir);
1268
+ if (!p) return null;
1269
+ try {
1270
+ const raw = fs3__default.default.readFileSync(p, "utf-8");
1271
+ const echo = extractEchoSections(raw);
1272
+ return {
1273
+ content: stripTemplateFrontmatter(raw),
1274
+ sections: echo.required,
1275
+ optionalSections: echo.optional,
1276
+ sourcePath: p,
1277
+ conditionals: extractConditionalSections(raw),
1278
+ declaredLang: extractDeclaredLanguage(raw)
1279
+ };
1280
+ } catch {
1281
+ return null;
1282
+ }
1283
+ }
1284
+ function assembleTranslatedTemplate(templateName, translatedBody, targetLanguage, opts) {
1285
+ const raw = loadCurriculumTemplate(templateName, opts?.templatesDir);
1286
+ if (!raw || !translatedBody?.trim()) return null;
1287
+ const base = collectHeadings(stripTemplateFrontmatter(raw));
1288
+ const out = collectHeadings(translatedBody);
1289
+ if (base.length !== out.length) return null;
1290
+ for (let i = 0; i < base.length; i++) {
1291
+ if (base[i].level !== out[i].level || base[i].optional !== out[i].optional) return null;
1292
+ }
1293
+ const srcBody = stripTemplateFrontmatter(raw);
1294
+ for (const ph of new Set(srcBody.match(/\{\{[^}]+\}\}/g) ?? [])) {
1295
+ if (!translatedBody.includes(ph)) return null;
1296
+ }
1297
+ for (const mk of new Set(srcBody.match(/<!--\s*\[\/?[^\]]+\]\s*-->/g) ?? [])) {
1298
+ if (!translatedBody.includes(mk)) return null;
1299
+ }
1300
+ const lang = targetLanguage.trim().toLowerCase() || "en";
1301
+ const req = out.filter((h) => !h.optional).map((h) => h.name);
1302
+ let fmRaw = "";
1303
+ if (raw.startsWith("---")) {
1304
+ const end = raw.indexOf("\n---", 3);
1305
+ if (end !== -1) fmRaw = raw.slice(0, end + 4);
1306
+ }
1307
+ const fm = stampEchoOnFrontmatter(fmRaw || "---\n---", lang, req);
1308
+ return { raw: `${fm}
1309
+ ${translatedBody.trimStart()}`, sections: req, templateLanguage: lang };
1310
+ }
1311
+ function stampEchoOnFrontmatter(rawDoc, lang, sections) {
1312
+ const echoLines = ['template_language: "' + lang + '"', "template_required_sections:", ...sections.map((n) => ` - "${n.replace(/"/g, '\\"')}"`)];
1313
+ if (rawDoc.startsWith("---")) {
1314
+ const lines = rawDoc.split("\n");
1315
+ const closeIdx = lines.findIndex((l, i) => i > 0 && l.trim() === "---");
1316
+ if (closeIdx === -1) {
1317
+ return ["---", ...echoLines, "---", "", stripTemplateFrontmatter(rawDoc)].join("\n");
1318
+ }
1319
+ const kept = [];
1320
+ let skippingList = false;
1321
+ for (const l of lines.slice(0, closeIdx)) {
1322
+ const t = l.trim();
1323
+ if (/^template_required_sections:$/.test(t)) {
1324
+ skippingList = true;
1325
+ continue;
1326
+ }
1327
+ if (/^template_language:/.test(t)) continue;
1328
+ if (skippingList) {
1329
+ if (/^\s+-\s/.test(l)) continue;
1330
+ skippingList = false;
1331
+ }
1332
+ kept.push(l);
1333
+ }
1334
+ return [...kept, ...echoLines, "---", ...lines.slice(closeIdx + 1)].join("\n");
1335
+ }
1336
+ return ["---", ...echoLines, "---", "", rawDoc].join("\n");
1337
+ }
1338
+ async function translateTemplate(templateName, targetLanguage, runner, opts) {
1339
+ const raw = loadCurriculumTemplate(templateName, opts?.templatesDir);
1340
+ if (!raw) return null;
1341
+ const body = stripTemplateFrontmatter(raw);
1342
+ const placeholders = Array.from(new Set(body.match(/\{\{[^}]+\}\}/g) ?? []));
1343
+ const markers = Array.from(new Set(body.match(/<!--\s*\[\/?[^\]]+\]\s*-->/g) ?? []));
1344
+ const prompt = `Translate the following Markdown artifact TEMPLATE body into ${targetLanguage}.
1345
+ STRICT PRESERVATION RULES (violating any of these invalidates the output):
1346
+ 1. Keep the document structure EXACTLY: same number of headings, same order, same heading levels (## / ###).
1347
+ 2. Translate heading TEXT naturally, but a heading must remain a single static line: move nothing into or out of it. Headings never contain durations or placeholders \u2014 if a source heading carries "[X mins]"-style timing, the timing belongs on the line below the heading, not inside it.
1348
+ 3. Keep every placeholder token VERBATIM (do not translate or alter), including {{ XX }}-style duration tokens: ${placeholders.join(" ") || "(none)"}
1349
+ 4. Keep every HTML comment marker VERBATIM: ${markers.join(" ") || "(none)"}
1350
+ 5. Keep code blocks, file paths, technical identifiers (API names, protocols, component models) untranslated.
1351
+ 6. Translate ONLY natural-language prose, headings, and table headers into natural ${targetLanguage}.
1352
+ 7. Output ONLY the translated markdown \u2014 no explanations, no code fences around the whole document.
1353
+
1354
+ TEMPLATE BODY:
1355
+ ${body}`;
1356
+ let translatedBody;
1357
+ try {
1358
+ translatedBody = await runner(prompt);
1359
+ } catch {
1360
+ return null;
1361
+ }
1362
+ if (!translatedBody || translatedBody.length < body.length * 0.4) return null;
1363
+ translatedBody = translatedBody.trim().replace(/^```(?:markdown)?\n/, "").replace(/\n```$/, "");
1364
+ return assembleTranslatedTemplate(templateName, translatedBody, targetLanguage, opts);
1365
+ }
1366
+ function loadCurriculumTemplate(templateName, templatesDir) {
1367
+ const p = findTemplatePath(templateName, templatesDir);
1368
+ if (!p) return "";
1369
+ try {
1370
+ return fs3__default.default.readFileSync(p, "utf-8");
1371
+ } catch {
1372
+ return "";
1373
+ }
1374
+ }
1375
+ function loadAuthoringTemplate(templateName, templatesDir) {
1376
+ const p = findTemplatePath(templateName, templatesDir);
1377
+ if (!p) return "";
1378
+ try {
1379
+ return stripTemplateFrontmatter(fs3__default.default.readFileSync(p, "utf-8"));
1380
+ } catch {
1381
+ return "";
1382
+ }
1383
+ }
1384
+ var KIT_TEMPLATE_FALLBACK_DIRS, _moduleDir;
1385
+ var init_templateLoader = __esm({
1386
+ "src/services/templateLoader.ts"() {
1387
+ KIT_TEMPLATE_FALLBACK_DIRS = [
1388
+ // Packaged with the npm bundle (dist/templates) — resolves from node_modules.
1389
+ "templates",
1390
+ // Repo checkout layouts.
1391
+ "docs/refs/templates"
1392
+ ];
1393
+ _moduleDir = (() => {
1394
+ try {
1395
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && typeof (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) === "string") {
1396
+ return path4__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
1397
+ }
1398
+ } catch {
1399
+ }
1400
+ return typeof __dirname !== "undefined" ? __dirname : process.cwd();
1401
+ })();
1402
+ }
1403
+ });
1181
1404
 
1182
1405
  // src/pipeline/artifactLinter.ts
1183
1406
  var artifactLinter_exports = {};
@@ -1544,14 +1767,26 @@ function parseLessonFlow(lessonMarkdown) {
1544
1767
  }
1545
1768
  }
1546
1769
  }
1547
- const flowMatch = lessonMarkdown.match(/##\s*\[?REQUIRED\]?\s*B\.\s*Lesson Flow([\s\S]*?)$/i);
1770
+ const flowMatch = lessonMarkdown.match(/##\s*(?:\[?REQUIRED\]?\s*)?B\.\s*Lesson Flow([\s\S]*?)$/i);
1548
1771
  const flowSection = flowMatch ? flowMatch[1] : lessonMarkdown;
1549
- const phaseRegex = /###\s*(?:(\d+)\.\s*)?([^\n—\-]+)(?:[—\-]\s*\[?([^\]\n]+)\]?)?\s*\n([\s\S]*?)(?=(?:###\s*(?:\d+\.)?|$))/gi;
1772
+ const phaseRegex = /###\s*(?:(\d+)\.\s*)?([^\n]+)\n([\s\S]*?)(?=(?:###\s*(?:\d+\.)?|$))/gi;
1550
1773
  let pMatch;
1551
1774
  while ((pMatch = phaseRegex.exec(flowSection)) !== null) {
1552
- const rawPhaseName = pMatch[2].trim();
1553
- const timing = pMatch[3]?.trim();
1554
- const content = pMatch[4].trim();
1775
+ const headingText = pMatch[2].trim();
1776
+ const tailMatch = headingText.match(/[—–-]\s*\[?([^\]—–]+)\]?\s*$/);
1777
+ const tail = tailMatch?.[1]?.trim() ?? "";
1778
+ const rawPhaseName = stripHeadingKey(
1779
+ tailMatch && /mins?/i.test(tail) ? headingText.slice(0, tailMatch.index).trim() : headingText
1780
+ );
1781
+ let content = pMatch[3].trim();
1782
+ let timing;
1783
+ const durMatch2 = content.match(/^>?\s*\**Duration:?\**\s*([~0-9][^\n]*)/i);
1784
+ if (durMatch2) {
1785
+ timing = durMatch2[1].trim();
1786
+ content = content.replace(/^>?\s*\**Duration:?\**\s*[^\n]*\n?/i, "").trim();
1787
+ } else if (tail && /mins?/i.test(tail)) {
1788
+ timing = tail;
1789
+ }
1555
1790
  const codeSnippets = [];
1556
1791
  const codeRegex = /```(?:[a-zA-Z0-9_\-]+)?\s*\n([\s\S]*?)```/g;
1557
1792
  let cMatch;
@@ -1579,6 +1814,7 @@ function parseLessonFlow(lessonMarkdown) {
1579
1814
  }
1580
1815
  var init_lessonFlowParser = __esm({
1581
1816
  "src/parsers/lessonFlowParser.ts"() {
1817
+ init_templateLoader();
1582
1818
  }
1583
1819
  });
1584
1820
 
@@ -3945,468 +4181,8 @@ var RoadmapInputSchema = zod.z.object({
3945
4181
  // src/ai/index.ts
3946
4182
  init_provider_factory();
3947
4183
 
3948
- // src/templates/lessonTemplate.ts
3949
- var LESSON_PLAN_TEMPLATE = `
3950
- ---
3951
- id: "{{ID}}"
3952
- title: "{{TITLE}}"
3953
- type: "LESSON_5E"
3954
- created_by: "Curriculum OS Builder"
3955
- phase: "{{PHASE}}"
3956
- deliverable: "P3-T1"
3957
- version: "{{VERSION}}"
3958
- date: "{{DATE}}"
3959
- prerequisite: []
3960
- template_contract: "lesson-plan-v2"
3961
- template_required_sections:
3962
- - "A. Lesson Design Plan"
3963
- - "Learning Objectives & Evidence"
3964
- - "Activity Sequence"
3965
- - "Resource Map"
3966
- - "Assessment Map"
3967
- - "Artifact Contract"
3968
- - "B. Lesson Flow"
3969
- ---
3970
- <!-- INJECT_BODY -->
3971
-
3972
- # LESSON [{{LESSON_ID}}]: {{TITLE}} (5E Model)
3973
-
3974
- ## [REQUIRED] A. Lesson Design Plan
3975
-
3976
- ### 1. Learning Objectives & Evidence
3977
- - **Estimated Duration:** {{DURATION}}
3978
- - **Materials & Equipment:** {{MATERIALS_SUMMARY}}
3979
- - **Keywords / Core Concepts:** {{KEYWORDS}}
3980
-
3981
- | LO | Objective (Bloom's) | Student Evidence | Success Criteria |
3982
- |---|---|---|---|
3983
- {{LEARNING_OBJECTIVES_TABLE_ROWS}}
3984
-
3985
- ### 2. Activity Sequence
3986
- > This is the canonical activity contract. Satellite artifacts must expand this sequence and must not invent a different one.
3987
-
3988
- | Seq | 5E Phase | Activity Type | Actor | Purpose | Student Action | Teacher Move | Output/Evidence | LO | Time | Artifact Contract |
3989
- |---|---|---|---|---|---|---|---|---|---|---|
3990
- {{ACTIVITY_SEQUENCE_TABLE_ROWS}}
3991
-
3992
- ### 3. Resource Map
3993
- | Resource | Used In Activity | Purpose | Owner Artifact |
3994
- |---|---|---|---|
3995
- {{RESOURCE_MAP_TABLE_ROWS}}
3996
-
3997
- ### 4. Assessment Map
3998
- | Evidence | Assessed By | Criteria | Follow-up If Missing |
3999
- |---|---|---|---|
4000
- {{ASSESSMENT_MAP_TABLE_ROWS}}
4001
-
4002
- ### 5. Artifact Contract
4003
- - **ACT must expand:** {{ACT_CONTRACT}}
4004
- - **GUIDE must support:** {{GUIDE_CONTRACT}}
4005
- - **SLIDE must visualize:** {{SLIDE_CONTRACT}}
4006
- - **HANDOUT must provide:** {{HANDOUT_CONTRACT}}
4007
- - **WKS must practice:** {{WKS_CONTRACT}}
4008
- - **QUIZ must assess:** {{QUIZ_CONTRACT}}
4009
- - **EXT must extend:** {{EXT_CONTRACT}}
4010
-
4011
- ---
4012
-
4013
- ## [REQUIRED] B. Lesson Flow
4014
-
4015
- ### 1. ENGAGE \u2014 {{ENGAGE_DURATION}} mins
4016
- - **Hook Scenario:** {{HOOK_SCENARIO}}
4017
- - **Open Question:** {{HOOK_OPEN_QUESTION}}
4018
- - **Transition:** {{HOOK_TRANSITION}}
4019
-
4020
- ### 2. EXPLORE \u2014 {{EXPLORE_DURATION}} mins
4021
- - **Activity Reference:** {{EXPLORE_ACTIVITY_REF}}
4022
- - **Student Discovery:** {{EXPLORE_STUDENT_DISCOVERY}}
4023
-
4024
- ### 3. EXPLAIN \u2014 {{EXPLAIN_DURATION}} mins
4025
- - **Definitions:**
4026
- {{EXPLAIN_DEFINITIONS}}
4027
- - **Visual Illustration (REQUIRED):**
4028
- {{EXPLAIN_VISUAL_ILLUSTRATION}}
4029
-
4030
- ### 4. ELABORATE \u2014 {{ELABORATE_DURATION}} mins
4031
- - **Tiered Scaffolding:**
4032
- - \u{1F949} **Bronze:** {{DIFFERENTIATION_BRONZE}}
4033
- - \u{1F948} **Silver:** {{DIFFERENTIATION_SILVER}}
4034
- - \u{1F947} **Gold:** {{DIFFERENTIATION_GOLD}}
4035
-
4036
- > **ACT Contract**: Detailed operational instructions must expand the Activity Sequence above and must not change the planned evidence target.
4037
-
4038
- ### 5. EVALUATE \u2014 {{EVALUATE_DURATION}} mins
4039
- - **Exit Ticket:** {{EXIT_TICKET_CONTENT}}
4040
- - **Closing:** {{CLOSING_REMARKS}}
4041
-
4042
- > **QUIZ Contract**: Detailed assessment criteria and rubrics must assess the evidence and success criteria defined in the Lesson Design Plan.
4043
-
4044
- ---
4045
-
4046
- ## [OPTIONAL] Teacher Notes
4047
- - **Common Pitfalls:**
4048
- {{COMMON_PITFALLS}}
4049
- - **Troubleshooting:**
4050
- {{TROUBLESHOOTING_ROWS}}
4051
-
4052
- ---
4053
-
4054
- ## [REQUIRED] Artifact Linkage (Master Index)
4055
- > List all satellite files used in this lesson.
4056
-
4057
- | Artifact File | Role in the Classroom | When to Use |
4058
- |---|---|---|
4059
- | \`GUIDE_{{LESSON_ID}}.md\` | Detailed Teaching Script | Teacher preparation |
4060
- | \`SLIDE_{{LESSON_ID}}.md\` | Presentation Content | Throughout the session |
4061
- | \`HANDOUT_{{LESSON_ID}}.md\` | Theory Summary for Students | During EXPLAIN phase |
4062
- | \`ACT_{{LESSON_ID}}.md\` | Detailed Learning Activity Operations | During EXPLORE / ELABORATE / checkpoint phases |
4063
- | \`WKS_{{LESSON_ID}}.md\` | Individual Worksheet | Wrap-up or Homework |
4064
- | \`QUIZ_{{LESSON_ID}}.md\` | Assessment Rubric | During EVALUATE phase |
4065
- | \`EXT_{{LESSON_ID}}.md\` | Enrichment Materials | For "Gold" students |
4066
- `.trim();
4067
-
4068
- // src/templates/actTemplate.ts
4069
- var ACT_TEMPLATE = `
4070
- ---
4071
- id: "{{ID}}"
4072
- title: "{{TITLE}}"
4073
- type: "ACT"
4074
- created_by: "Curriculum OS Builder"
4075
- phase: "{{PHASE}}"
4076
- deliverable: "P3-T2"
4077
- version: "{{VERSION}}"
4078
- date: "{{DATE}}"
4079
- template_contract: "artifact-template-v1"
4080
- template_required_sections:
4081
- - "Computational Thinking Focus"
4082
- - "Materials & Setup"
4083
- - "Constraints & Safety"
4084
- - "Active Learning Workflow"
4085
- - "3-Tier Differentiation"
4086
- - "Reflection"
4087
- ---
4088
- <!-- INJECT_BODY -->
4089
-
4090
- # INQUIRY-BASED ACTIVITY: {{TITLE}}
4091
- _Milestone: {{MILESTONE_ID}} | Module: {{MODULE_NAME}} | Lesson: {{LESSON_ID}}_
4092
-
4093
- ## [REQUIRED] Computational Thinking Focus
4094
- - **Problem Statement (Real-world Scenario):** {{PROBLEM_STATEMENT}}
4095
- - **Decomposition:**
4096
- {{DECOMPOSITION_STEPS}}
4097
- - **Algorithm Design:** {{ALGORITHM_DESIGN}}
4098
-
4099
- ## [REQUIRED] Materials & Setup
4100
- - **Reference Material:** Students should use the **HANDOUT** for this lesson as their primary technical reference (lifeline).
4101
- - **Hardware/Software:**
4102
- <!-- [HARDWARE_BOM] -->
4103
- {{HARDWARE_ITEMS}}
4104
- <!-- [/HARDWARE_BOM] -->
4105
- - **Software:** {{SOFTWARE_ITEMS}}
4106
- - **Consumables:** {{CONSUMABLES}}
4107
- - **Other Resources:** {{OTHER_RESOURCES}}
4108
-
4109
- ## [REQUIRED] Constraints & Safety
4110
- - \u26A0\uFE0F **Technical/Safety:** {{SAFETY_WARNING}}
4111
- - \u{1F4A1} **Design Constraint:** {{DESIGN_CONSTRAINT}}
4112
-
4113
- ## [REQUIRED] Active Learning Workflow
4114
-
4115
- ### Phase 1: Planning & Design \u2014 {{PHASE1_DURATION}} mins
4116
- - **Student Task:** Hand-draw (or draft in words) the algorithm flowchart or connection architecture before touching the computer/devices.
4117
- - **Teacher Action:** Walk around to check the logic of each group. Ask inquiry questions:
4118
- {{PHASE1_INQUIRY_QUESTIONS}}
4119
-
4120
- ### Phase 2: Implementation & Discovery \u2014 {{PHASE2_DURATION}} mins
4121
- - **Student Task:** Apply the knowledge from the **HANDOUT** to realize the plan designed in Phase 1. Encourage self-inquiry and documentation lookup.
4122
- - **Teacher Action:** Avoid "hand-holding." Only intervene if a group has been stuck for more than 5 minutes.
4123
- {{PHASE2_GUIDANCE_BLOCK}}
4124
-
4125
- ### Phase 3: Debugging & Testing \u2014 {{PHASE3_DURATION}} mins
4126
- - **Testing:** Students run their system.
4127
- - **Error Analysis:** Ask students to record any error codes or unexpected behaviors and compare them with the Troubleshooting table in the Handout.
4128
- {{PHASE3_TESTING_STEPS}}
4129
-
4130
- ## [REQUIRED] 3-Tier Differentiation
4131
- > MUST design tasks in 3 tiers to support diverse learner groups. Bronze is the minimum requirement for all students.
4132
- - \u{1F949} **Bronze (Foundational):** {{DIFFERENTIATION_BRONZE}}
4133
- - \u{1F948} **Silver (Application):** {{DIFFERENTIATION_SILVER}}
4134
- - \u{1F947} **Gold (Enrichment/Creative):** {{DIFFERENTIATION_GOLD}}
4135
-
4136
- ## [REQUIRED] Reflection
4137
- - **Discussion Question:** {{REFLECTION_QUESTION}}
4138
- `.trim();
4139
-
4140
- // src/templates/codeLabTemplate.ts
4141
- var CODE_LAB_TEMPLATE = `
4142
- ---
4143
- id: "{{ID}}"
4144
- title: "{{TITLE}}"
4145
- type: "LAB"
4146
- created_by: "Curriculum OS Builder"
4147
- phase: "{{PHASE}}"
4148
- deliverable: "P3-T2"
4149
- version: "{{VERSION}}"
4150
- date: "{{DATE}}"
4151
- ---
4152
- <!-- INJECT_BODY -->
4153
-
4154
- # \u{1F468}\u200D\u{1F4BB} CODE LAB: {{TITLE}}
4155
- _Milestone: {{MILESTONE_ID}} | Tech Stack: {{TECH_STACK}}_
4156
-
4157
- ## \u{1F3AF} Overview
4158
- This lab focuses on the practical implementation of core technical concepts, bridging theory into working, robust, and testable code.
4159
-
4160
- ## \u{1F6E0}\uFE0F Environment & Setup
4161
- - **Language / Framework:** {{LANGUAGE_FRAMEWORK}}
4162
- - **IDE / Editor:** {{IDE_EDITOR}}
4163
- - **Required Libraries:** {{REQUIRED_LIBRARIES}}
4164
- - **Hardware / Connection Setup:** {{HARDWARE_ENV_CONNECTION}}
4165
-
4166
- ## \u{1F4DD} Lab Tasks (3-Tier Differentiation)
4167
-
4168
- ### \u{1F949} Bronze (Foundational) \u2014 {{BRONZE_MINS}} mins
4169
- **Objective:** {{BRONZE_OBJECTIVE}}
4170
- - **Task:** {{BRONZE_TASK}}
4171
- - **Requirement:** {{BRONZE_REQUIREMENT}}
4172
-
4173
- ### \u{1F948} Silver (Application) \u2014 {{SILVER_MINS}} mins
4174
- **Objective:** {{SILVER_OBJECTIVE}}
4175
- - **Task:** {{SILVER_TASK}}
4176
- - **Requirement:** {{SILVER_REQUIREMENT}}
4177
-
4178
- ### \u{1F947} Gold (Enrichment/Creative) \u2014 {{GOLD_MINS}} mins
4179
- **Objective:** {{GOLD_OBJECTIVE}}
4180
- - **Task:** {{GOLD_TASK}}
4181
- - **Requirement:** {{GOLD_REQUIREMENT}}
4182
-
4183
- ## \u{1F4BB} Technical Reference ([SME_MANDATE])
4184
- <!-- [SME_MANDATE] -->
4185
- > Any code provided in this section is verified for syntax accuracy and runtime execution.
4186
-
4187
- ### Starter Code Template
4188
- \`\`\`{{CODE_LANGUAGE}}
4189
- {{STARTER_CODE}}
4190
- \`\`\`
4191
-
4192
- ### Solution Code
4193
- \`\`\`{{CODE_LANGUAGE}}
4194
- {{SOLUTION_CODE}}
4195
- \`\`\`
4196
- <!-- [/SME_MANDATE] -->
4197
-
4198
- ## \u{1F50D} Troubleshooting & Debugging Matrix
4199
- | Issue | Potential Cause | Solution |
4200
- | :--- | :--- | :--- |
4201
- {{TROUBLESHOOTING_ROWS}}
4202
-
4203
- ## \u{1F3C1} Submission & Verification Checklist
4204
- {{CHECKLIST_ITEMS}}
4205
- `.trim();
4206
-
4207
- // src/templates/quizTemplate.ts
4208
- var QUIZ_TEMPLATE = `
4209
- ---
4210
- id: "{{ID}}"
4211
- title: "{{TITLE}}"
4212
- type: "QUIZ"
4213
- language: "{{LANGUAGE}}"
4214
- total_questions: {{TOTAL_QUESTIONS}}
4215
- passing_score_pct: {{PASSING_SCORE_PCT}}
4216
- ---
4217
-
4218
- # \u{1F9E0} Diagnostic & Active Recall Quiz: {{TITLE}}
4219
-
4220
- > \u23F1\uFE0F **Recommended Time:** ~10-15 mins
4221
- > \u{1F3AF} **Passing Score:** {{PASSING_SCORE_PCT}}%
4222
- > \u{1F4A1} **Purpose:** Test your conceptual understanding, catch hidden misconceptions, and evaluate architectural trade-offs.
4223
-
4224
- ---
4225
-
4226
- {{QUESTIONS_SECTION}}
4227
-
4228
- ---
4229
-
4230
- ## \u{1F511} Comprehensive Answer Key & Deep Explanation (Expand after completing)
4231
- <details>
4232
- <summary><b>\u{1F449} Click to View Full Explanations & Common Pitfall Analysis</b></summary>
4233
-
4234
- {{ANSWER_KEY_SECTION}}
4235
- </details>
4236
- `.trim();
4237
-
4238
- // src/templates/selfLabTemplate.ts
4239
- var SELF_LAB_TEMPLATE = `
4240
- ---
4241
- id: "{{ID}}"
4242
- title: "{{TITLE}}"
4243
- type: "SELF_LAB"
4244
- language: "{{LANGUAGE}}"
4245
- difficulty: "{{DIFFICULTY}}"
4246
- estimated_minutes: {{ESTIMATED_MINUTES}}
4247
- tech_stack: {{TECH_STACK}}
4248
- ---
4249
-
4250
- # \u{1F680} Self-Paced Hands-On Lab: {{TITLE}}
4251
-
4252
- > \u23F1\uFE0F **Estimated Duration:** ~{{ESTIMATED_MINUTES}} mins
4253
- > \u{1F3AF} **Difficulty Level:** {{DIFFICULTY_UPPER}}
4254
- > \u{1F9F0} **Tech Stack:** {{TECH_STACK_STR}}
4255
- > \u{1F4A1} **Self-Learner Notice:** You are in full control of this lab. Follow the progressive challenges below. If you get stuck at any point, use the **Progressive Hints System** to get unstuck without ruining the learning experience!
4256
-
4257
- ---
4258
-
4259
- ## \u{1F4CB} Mission Briefing & Environment Setup
4260
- {{MISSION_BRIEFING}}
4261
-
4262
- ### \u{1F6EB} Preflight Checklist
4263
- {{PREFLIGHT_CHECKLIST}}
4264
-
4265
- ---
4266
-
4267
- ## \u{1F3AF} Progressive Challenges (3-Tier Differentiation)
4268
-
4269
- ### \u{1F949} Challenge 1: Foundational Core (Bronze) \u2014 ~{{BRONZE_MINS}} mins
4270
- **Objective:** {{BRONZE_OBJECTIVE}}
4271
-
4272
- #### Tasks & Implementation Details:
4273
- {{BRONZE_TASKS}}
4274
-
4275
- #### \u{1F9EA} Self-Verification Command:
4276
- \`\`\`bash
4277
- {{BRONZE_VERIFY_COMMAND}}
4278
- \`\`\`
4279
- *Expected Terminal Output:*
4280
- \`\`\`text
4281
- {{BRONZE_EXPECTED_OUTPUT}}
4282
- \`\`\`
4283
-
4284
- #### \u{1F4A1} Progressive Hints (Expand only if stuck):
4285
- <details>
4286
- <summary><b>\u{1F4A1} Hint Level 1: Conceptual Clue (H\u01B0\u1EDBng t\u01B0 duy)</b></summary>
4287
-
4288
- {{BRONZE_HINT_1}}
4289
- </details>
4290
-
4291
- <details>
4292
- <summary><b>\u{1F50D} Hint Level 2: Structural Skeleton (Khung code m\u1EABu)</b></summary>
4293
-
4294
- \`\`\`{{CODE_LANGUAGE}}
4295
- {{BRONZE_HINT_2}}
4296
- \`\`\`
4297
- </details>
4298
-
4299
- <details>
4300
- <summary><b>\u{1F6A8} Hint Level 3: Full Solution & Explanation (L\u1EDDi gi\u1EA3i tr\u1ECDn v\u1EB9n)</b></summary>
4301
-
4302
- \`\`\`{{CODE_LANGUAGE}}
4303
- {{BRONZE_HINT_3_CODE}}
4304
- \`\`\`
4305
-
4306
- **Why this solution works:**
4307
- {{BRONZE_HINT_3_EXPLANATION}}
4308
- </details>
4309
-
4310
- ---
4311
-
4312
- ### \u{1F948} Challenge 2: Logic & Error Handling (Silver) \u2014 ~{{SILVER_MINS}} mins
4313
- **Objective:** {{SILVER_OBJECTIVE}}
4314
-
4315
- #### Tasks & Implementation Details:
4316
- {{SILVER_TASKS}}
4317
-
4318
- #### \u{1F9EA} Self-Verification Command:
4319
- \`\`\`bash
4320
- {{SILVER_VERIFY_COMMAND}}
4321
- \`\`\`
4322
- *Expected Terminal Output:*
4323
- \`\`\`text
4324
- {{SILVER_EXPECTED_OUTPUT}}
4325
- \`\`\`
4326
-
4327
- #### \u{1F4A1} Progressive Hints (Expand only if stuck):
4328
- <details>
4329
- <summary><b>\u{1F4A1} Hint Level 1: Conceptual Clue (H\u01B0\u1EDBng t\u01B0 duy)</b></summary>
4330
-
4331
- {{SILVER_HINT_1}}
4332
- </details>
4333
-
4334
- <details>
4335
- <summary><b>\u{1F50D} Hint Level 2: Structural Skeleton (Khung code m\u1EABu)</b></summary>
4336
-
4337
- \`\`\`{{CODE_LANGUAGE}}
4338
- {{SILVER_HINT_2}}
4339
- \`\`\`
4340
- </details>
4341
-
4342
- <details>
4343
- <summary><b>\u{1F6A8} Hint Level 3: Full Solution & Explanation (L\u1EDDi gi\u1EA3i tr\u1ECDn v\u1EB9n)</b></summary>
4344
-
4345
- \`\`\`{{CODE_LANGUAGE}}
4346
- {{SILVER_HINT_3_CODE}}
4347
- \`\`\`
4348
-
4349
- **Why this solution works:**
4350
- {{SILVER_HINT_3_EXPLANATION}}
4351
- </details>
4352
-
4353
- ---
4354
-
4355
- ### \u{1F947} Challenge 3: Edge Cases & Optimization (Gold) \u2014 ~{{GOLD_MINS}} mins
4356
- **Objective:** {{GOLD_OBJECTIVE}}
4357
-
4358
- #### Tasks & Implementation Details:
4359
- {{GOLD_TASKS}}
4360
-
4361
- #### \u{1F9EA} Self-Verification Command:
4362
- \`\`\`bash
4363
- {{GOLD_VERIFY_COMMAND}}
4364
- \`\`\`
4365
- *Expected Terminal Output:*
4366
- \`\`\`text
4367
- {{GOLD_EXPECTED_OUTPUT}}
4368
- \`\`\`
4369
-
4370
- #### \u{1F4A1} Progressive Hints (Expand only if stuck):
4371
- <details>
4372
- <summary><b>\u{1F4A1} Hint Level 1: Conceptual Clue (H\u01B0\u1EDBng t\u01B0 duy)</b></summary>
4373
-
4374
- {{GOLD_HINT_1}}
4375
- </details>
4376
-
4377
- <details>
4378
- <summary><b>\u{1F50D} Hint Level 2: Structural Skeleton (Khung code m\u1EABu)</b></summary>
4379
-
4380
- \`\`\`{{CODE_LANGUAGE}}
4381
- {{GOLD_HINT_2}}
4382
- \`\`\`
4383
- </details>
4384
-
4385
- <details>
4386
- <summary><b>\u{1F6A8} Hint Level 3: Full Solution & Explanation (L\u1EDDi gi\u1EA3i tr\u1ECDn v\u1EB9n)</b></summary>
4387
-
4388
- \`\`\`{{CODE_LANGUAGE}}
4389
- {{GOLD_HINT_3_CODE}}
4390
- \`\`\`
4391
-
4392
- **Why this solution works:**
4393
- {{GOLD_HINT_3_EXPLANATION}}
4394
- </details>
4395
-
4396
- ---
4397
-
4398
- ## \u{1F50D} Self-Learner Troubleshooting Matrix
4399
- | Symptom / Error Message | Root Cause | Exact Fix |
4400
- | :--- | :--- | :--- |
4401
- {{TROUBLESHOOTING_ROWS}}
4402
-
4403
- ---
4404
-
4405
- ## \u{1F3C1} Final Lab Submission & Mastery Checklist
4406
- {{FINAL_CHECKLIST}}
4407
- `.trim();
4408
-
4409
4184
  // src/ai/prompts/lessonMasterPrompt.ts
4185
+ init_templateLoader();
4410
4186
  function buildLessonMasterPrompt(input) {
4411
4187
  const { milestone, language, topic, targetAudience, contextContinuity, standardsContext } = input;
4412
4188
  const learningObjectives = milestone.learning_objectives || [];
@@ -4458,7 +4234,7 @@ Build a deep, rigorous, and highly pedagogical Master Lesson Plan (LESSON) that
4458
4234
  # MANDATORY TEMPLATE STRUCTURE
4459
4235
  You MUST generate structured data that will render directly into this exact Markdown template:
4460
4236
  ---
4461
- ${LESSON_PLAN_TEMPLATE}
4237
+ ${loadAuthoringTemplate("LESSON_5E_template.md") ?? "[TEMPLATE FILE NOT FOUND: LESSON_5E_template.md]"}
4462
4238
  ---
4463
4239
 
4464
4240
  # INPUT PARAMETERS
@@ -4508,6 +4284,7 @@ Return a single valid JSON object matching the LessonPlanSchema with these exact
4508
4284
  }
4509
4285
 
4510
4286
  // src/ai/prompts/activityPrompt.ts
4287
+ init_templateLoader();
4511
4288
  function buildActivityPrompt(input) {
4512
4289
  const { lesson, language, hardwareBOM, softwareTools } = input;
4513
4290
  const loList = lesson.learningObjectives.map((lo) => `- [${lo.bloomLevel}] ${lo.code}: ${lo.name || ""} \u2014 ${lo.description}${lo.successCriteria ? ` (Evidence: ${lo.successCriteria})` : ""}`).join("\n");
@@ -4536,7 +4313,7 @@ Transform the exact pedagogical flow and activities from the Master Lesson Plan
4536
4313
  # MANDATORY TEMPLATE STRUCTURE
4537
4314
  You MUST generate data aligned with this Markdown template:
4538
4315
  ---
4539
- ${ACT_TEMPLATE}
4316
+ ${loadAuthoringTemplate("ACT_template.md") ?? "[TEMPLATE FILE NOT FOUND: ACT_template.md]"}
4540
4317
  ---
4541
4318
 
4542
4319
  # CANONICAL MASTER LESSON CONTEXT (SOURCE OF TRUTH)
@@ -4607,6 +4384,7 @@ Return a single JSON object matching ActivityLabSchema with these exact keys:
4607
4384
  }
4608
4385
 
4609
4386
  // src/ai/prompts/codeLabPrompt.ts
4387
+ init_templateLoader();
4610
4388
  function buildCodeLabPrompt(input) {
4611
4389
  const { lesson, activity, language, targetTechStack } = input;
4612
4390
  const techStackList = targetTechStack && targetTechStack.length > 0 ? targetTechStack : ["General Programming & Engineering"];
@@ -4631,7 +4409,7 @@ Generate complete, verified source code that satisfies the lesson objectives and
4631
4409
  # MANDATORY TEMPLATE STRUCTURE
4632
4410
  You MUST generate data aligned with this Markdown template:
4633
4411
  ---
4634
- ${CODE_LAB_TEMPLATE}
4412
+ ${loadAuthoringTemplate("LAB_TEMPLATE.md") ?? "[TEMPLATE FILE NOT FOUND: LAB_TEMPLATE.md]"}
4635
4413
  ---
4636
4414
 
4637
4415
  # CANONICAL LESSON CONTEXT (SOURCE OF TRUTH)
@@ -5296,6 +5074,7 @@ Please return a single JSON object matching these exact keys:
5296
5074
  }
5297
5075
 
5298
5076
  // src/ai/prompts/selfLabPrompt.ts
5077
+ init_templateLoader();
5299
5078
  function buildSelfLabPrompt(input) {
5300
5079
  const { milestone, projectGraph, language, targetTechStack, contextContinuity } = input;
5301
5080
  const techStackList = targetTechStack && targetTechStack.length > 0 ? targetTechStack : milestone.tech_keywords && milestone.tech_keywords.length > 0 ? milestone.tech_keywords : ["General Engineering"];
@@ -5326,7 +5105,7 @@ Build a complete, engaging Self-Paced Hands-On Lab (SELF_LAB) for autonomous lea
5326
5105
  # MANDATORY TEMPLATE STRUCTURE
5327
5106
  You MUST generate data aligned with this Markdown template:
5328
5107
  ---
5329
- ${SELF_LAB_TEMPLATE}
5108
+ ${loadAuthoringTemplate("SELF_LAB_template.md") ?? "[TEMPLATE FILE NOT FOUND: SELF_LAB_template.md]"}
5330
5109
  ---
5331
5110
 
5332
5111
  # INPUT PARAMETERS
@@ -5404,6 +5183,7 @@ Return a single valid JSON object matching SelfLabSchema with these exact keys:
5404
5183
  }
5405
5184
 
5406
5185
  // src/ai/prompts/diagnosticQuizPrompt.ts
5186
+ init_templateLoader();
5407
5187
  function buildDiagnosticQuizPrompt(input) {
5408
5188
  const { milestone, language, questionCount = 4, passingScorePct = 80 } = input;
5409
5189
  const learningObjectives = milestone.learning_objectives || [];
@@ -5463,7 +5243,7 @@ Use this mapping to determine which Bloom level each question truly targets. Mat
5463
5243
  # MANDATORY TEMPLATE STRUCTURE
5464
5244
  You MUST generate data aligned with this Markdown template:
5465
5245
  ---
5466
- ${QUIZ_TEMPLATE}
5246
+ ${loadAuthoringTemplate("QUIZ_v4.6_template.md") ?? "[TEMPLATE FILE NOT FOUND: QUIZ_v4.6_template.md]"}
5467
5247
  ---
5468
5248
 
5469
5249
  # INPUT PARAMETERS
@@ -6868,7 +6648,7 @@ ${cc.technicalDepth}`;
6868
6648
  const dataRows = cc.comparisonTable.rows.map((r) => `| ${r.join(" | ")} |`).join("\n");
6869
6649
  str += `
6870
6650
 
6871
- #### Comparison & Trade-off Analysis
6651
+ **Comparison & Trade-off Analysis**
6872
6652
  ${headerRow}
6873
6653
  ${dividerRow}
6874
6654
  ${dataRows}`;
@@ -6912,7 +6692,8 @@ ${learningObjectivesStr}
6912
6692
 
6913
6693
  ---
6914
6694
 
6915
- ## 1. ENGAGE (Hook & High-Stakes Scenario) \u2014 10-15 mins
6695
+ ## 1. ENGAGE (Hook & High-Stakes Scenario)
6696
+ > **Duration:** 10-15 mins
6916
6697
 
6917
6698
  ### Real-World Context: "${lesson.hookTitle || "The Incident"}"
6918
6699
  ${lesson.hookScenario}
@@ -6921,13 +6702,15 @@ ${lesson.hookQuestions && lesson.hookQuestions.length > 0 ? lesson.hookQuestions
6921
6702
 
6922
6703
  ---
6923
6704
 
6924
- ## 2. EXPLORE & EXPLAIN (Core Technical Concepts & Principles) \u2014 30-40 mins
6705
+ ## 2. EXPLORE & EXPLAIN (Core Technical Concepts & Principles)
6706
+ > **Duration:** 30-40 mins
6925
6707
 
6926
6708
  ${coreConceptsStr}
6927
6709
 
6928
6710
  ---
6929
6711
 
6930
- ## 3. GUIDED PRACTICE (Visual Architecture & Annotated Code) \u2014 20-25 mins
6712
+ ## 3. GUIDED PRACTICE (Visual Architecture & Annotated Code)
6713
+ > **Duration:** 20-25 mins
6931
6714
 
6932
6715
  ### System Architecture & Data Flow (Mermaid Diagram)
6933
6716
  ${mermaidBlock}
@@ -6941,7 +6724,8 @@ ${guidedPractice.codeSnippet}
6941
6724
 
6942
6725
  ---
6943
6726
 
6944
- ## 4. ELABORATE & INDEPENDENT PRACTICE (Technical Challenge & Scaffolding) \u2014 15-20 mins
6727
+ ## 4. ELABORATE & INDEPENDENT PRACTICE (Technical Challenge & Scaffolding)
6728
+ > **Duration:** 15-20 mins
6945
6729
 
6946
6730
  ### Engineering Challenge: "${lesson.independentPractice?.title || "System Optimization Challenge"}"
6947
6731
  ${lesson.independentPractice?.description || "Implement the core logic and test against simulated edge cases."}
@@ -6952,7 +6736,8 @@ ${lesson.independentPractice?.description || "Implement the core logic and test
6952
6736
 
6953
6737
  ---
6954
6738
 
6955
- ## 5. EVALUATE (Summary, Assessment & Metacognition) \u2014 5-10 mins
6739
+ ## 5. EVALUATE (Summary, Assessment & Metacognition)
6740
+ > **Duration:** 5-10 mins
6956
6741
 
6957
6742
  ### Exit Ticket (Bloom: Apply / Analyze)
6958
6743
  > **Scenario Question:** ${lesson.exitTicket.questionStem}
@@ -7045,12 +6830,16 @@ ${hardwareItems}
7045
6830
 
7046
6831
  ## [REQUIRED] Active Learning Workflow
7047
6832
 
7048
- ### Phase 1: Planning & Design \u2014 ${phase1.durationMinutes} mins
6833
+ ### Phase 1: Planning & Design
6834
+ > **Duration:** ${phase1.durationMinutes} mins
6835
+
7049
6836
  - **Student Task:** ${phase1.studentTask}
7050
6837
  - **Teacher Action:** ${phase1.teacherAction}
7051
6838
  ${phase1.inquiryQuestions.map((q) => ` - *Inquiry Question:* ${q}`).join("\n")}
7052
6839
 
7053
- ### Phase 2: Implementation & Discovery \u2014 ${phase2.durationMinutes} mins
6840
+ ### Phase 2: Implementation & Discovery
6841
+ > **Duration:** ${phase2.durationMinutes} mins
6842
+
7054
6843
  - **Student Task:** ${phase2.studentTask}
7055
6844
  - **Teacher Action:** ${phase2.teacherAction}
7056
6845
 
@@ -7058,7 +6847,9 @@ ${phase2.codeOrCommands.map((c) => `\`\`\`text
7058
6847
  ${c}
7059
6848
  \`\`\``).join("\n\n")}
7060
6849
 
7061
- ### Phase 3: Debugging & Testing \u2014 ${phase3.durationMinutes} mins
6850
+ ### Phase 3: Debugging & Testing
6851
+ > **Duration:** ${phase3.durationMinutes} mins
6852
+
7062
6853
  - **Testing:** ${phase3.testingSteps}
7063
6854
  - **Error Analysis:** ${phase3.errorAnalysis}
7064
6855
 
@@ -7113,22 +6904,28 @@ This lab focuses on the practical implementation of core technical concepts, bri
7113
6904
 
7114
6905
  ## \u{1F4DD} Lab Tasks (3-Tier Differentiation)
7115
6906
 
7116
- ### \u{1F949} Bronze (Foundational) \u2014 ${tasks.bronze.mins} mins
6907
+ ### \u{1F949} Bronze (Foundational)
6908
+ > **Duration:** ${tasks.bronze.mins} mins
6909
+
7117
6910
  **Objective:** ${tasks.bronze.objective}
7118
6911
  - **Task:** ${tasks.bronze.task}
7119
6912
  - **Requirement:** ${tasks.bronze.requirement}
7120
6913
 
7121
- ### \u{1F948} Silver (Application) \u2014 ${tasks.silver.mins} mins
6914
+ ### \u{1F948} Silver (Application)
6915
+ > **Duration:** ${tasks.silver.mins} mins
6916
+
7122
6917
  **Objective:** ${tasks.silver.objective}
7123
6918
  - **Task:** ${tasks.silver.task}
7124
6919
  - **Requirement:** ${tasks.silver.requirement}
7125
6920
 
7126
- ### \u{1F947} Gold (Enrichment/Creative) \u2014 ${tasks.gold.mins} mins
6921
+ ### \u{1F947} Gold (Enrichment/Creative)
6922
+ > **Duration:** ${tasks.gold.mins} mins
6923
+
7127
6924
  **Objective:** ${tasks.gold.objective}
7128
6925
  - **Task:** ${tasks.gold.task}
7129
6926
  - **Requirement:** ${tasks.gold.requirement}
7130
6927
 
7131
- ## \u{1F4BB} Technical Reference ([SME_MANDATE])
6928
+ ## \u{1F4BB} Technical Reference
7132
6929
  <!-- [SME_MANDATE] -->
7133
6930
  > Any code provided in this section is verified for syntax accuracy and runtime execution.
7134
6931
 
@@ -7266,7 +7063,9 @@ ${instruction.verificationChecklist.map((c) => `- [ ] ${c}`).join("\n")}
7266
7063
  function serializeSelfLabToMarkdown(lab) {
7267
7064
  const renderChallenge = (c, label) => {
7268
7065
  const tasksList = c.tasks.map((t) => `- ${t}`).join("\n");
7269
- return `### ${label}: ${c.title} \u2014 ~${c.estimatedMinutes} mins
7066
+ return `### ${label}: ${c.title}
7067
+ > **Duration:** ~${c.estimatedMinutes} mins
7068
+
7270
7069
  **Objective:** ${c.objective}
7271
7070
 
7272
7071
  #### Tasks & Implementation Details:
@@ -7760,13 +7559,13 @@ async function generateSelfPacedBundle(input) {
7760
7559
  if (input.outputDirectory) {
7761
7560
  await fsPromises__default.default.mkdir(input.outputDirectory, { recursive: true });
7762
7561
  await Promise.all([
7763
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "INSTRUCTION.md"), instructionMd, "utf-8"),
7764
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "SELF_LAB.md"), selfLabMd, "utf-8"),
7765
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "QUIZ.md"), quizMd, "utf-8"),
7766
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "QUIZ.json"), JSON.stringify(quiz, null, 2), "utf-8"),
7767
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "CHEAT_SHEET.md"), cheatSheetMd, "utf-8"),
7768
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "LAB.md"), codeLabMd, "utf-8"),
7769
- fsPromises__default.default.writeFile(path3__default.default.join(input.outputDirectory, "bundle.json"), JSON.stringify(bundle, null, 2), "utf-8")
7562
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "INSTRUCTION.md"), instructionMd, "utf-8"),
7563
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "SELF_LAB.md"), selfLabMd, "utf-8"),
7564
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "QUIZ.md"), quizMd, "utf-8"),
7565
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "QUIZ.json"), JSON.stringify(quiz, null, 2), "utf-8"),
7566
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "CHEAT_SHEET.md"), cheatSheetMd, "utf-8"),
7567
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "LAB.md"), codeLabMd, "utf-8"),
7568
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "bundle.json"), JSON.stringify(bundle, null, 2), "utf-8")
7770
7569
  ]);
7771
7570
  }
7772
7571
  return {
@@ -8167,15 +7966,15 @@ var STANDARD_SOT_FILES = [
8167
7966
  "ALIGNMENT_MATRIX.md"
8168
7967
  ];
8169
7968
  function atomicWriteFileSync(targetPath, content) {
8170
- const dir = path3__default.default.dirname(targetPath);
8171
- if (!fs2__default.default.existsSync(dir)) fs2__default.default.mkdirSync(dir, { recursive: true });
8172
- const tmpPath = path3__default.default.join(dir, `.${path3__default.default.basename(targetPath)}.${process.pid}.${Date.now()}.${crypto__default.default.randomBytes(4).toString("hex")}.tmp`);
7969
+ const dir = path4__default.default.dirname(targetPath);
7970
+ if (!fs3__default.default.existsSync(dir)) fs3__default.default.mkdirSync(dir, { recursive: true });
7971
+ const tmpPath = path4__default.default.join(dir, `.${path4__default.default.basename(targetPath)}.${process.pid}.${Date.now()}.${crypto__default.default.randomBytes(4).toString("hex")}.tmp`);
8173
7972
  try {
8174
- fs2__default.default.writeFileSync(tmpPath, content, "utf-8");
8175
- fs2__default.default.renameSync(tmpPath, targetPath);
7973
+ fs3__default.default.writeFileSync(tmpPath, content, "utf-8");
7974
+ fs3__default.default.renameSync(tmpPath, targetPath);
8176
7975
  } catch (err) {
8177
7976
  try {
8178
- if (fs2__default.default.existsSync(tmpPath)) fs2__default.default.unlinkSync(tmpPath);
7977
+ if (fs3__default.default.existsSync(tmpPath)) fs3__default.default.unlinkSync(tmpPath);
8179
7978
  } catch {
8180
7979
  }
8181
7980
  throw err;
@@ -8184,51 +7983,51 @@ function atomicWriteFileSync(targetPath, content) {
8184
7983
  var FileSystemCurriculumAdapter = class {
8185
7984
  baseDir;
8186
7985
  constructor(options = {}) {
8187
- this.baseDir = options.baseDir || path3__default.default.resolve(process.cwd(), "output", "projects");
7986
+ this.baseDir = options.baseDir || path4__default.default.resolve(process.cwd(), "output", "projects");
8188
7987
  }
8189
7988
  getProjectDir(projectId) {
8190
7989
  const raw = (projectId || "").replace("proj_", "");
8191
- const directPath = path3__default.default.join(this.baseDir, raw);
8192
- if (fs2__default.default.existsSync(directPath)) return directPath;
8193
- const fullIdPath = path3__default.default.join(this.baseDir, projectId);
8194
- if (fs2__default.default.existsSync(fullIdPath)) return fullIdPath;
7990
+ const directPath = path4__default.default.join(this.baseDir, raw);
7991
+ if (fs3__default.default.existsSync(directPath)) return directPath;
7992
+ const fullIdPath = path4__default.default.join(this.baseDir, projectId);
7993
+ if (fs3__default.default.existsSync(fullIdPath)) return fullIdPath;
8195
7994
  return directPath;
8196
7995
  }
8197
7996
  /** Ensures the resolved path stays within the project directory (prevents path traversal). */
8198
7997
  assertInsideProject(projectDir, targetPath) {
8199
- const resolvedProject = path3__default.default.resolve(projectDir);
8200
- const resolvedTarget = path3__default.default.resolve(targetPath);
8201
- if (!resolvedTarget.startsWith(resolvedProject + path3__default.default.sep) && resolvedTarget !== resolvedProject) {
7998
+ const resolvedProject = path4__default.default.resolve(projectDir);
7999
+ const resolvedTarget = path4__default.default.resolve(targetPath);
8000
+ if (!resolvedTarget.startsWith(resolvedProject + path4__default.default.sep) && resolvedTarget !== resolvedProject) {
8202
8001
  throw new Error(`[FileSystemCurriculumAdapter] Path traversal blocked: ${targetPath} is outside project directory ${projectDir}`);
8203
8002
  }
8204
8003
  }
8205
8004
  async projectExists(projectId) {
8206
- return fs2__default.default.existsSync(this.getProjectDir(projectId));
8005
+ return fs3__default.default.existsSync(this.getProjectDir(projectId));
8207
8006
  }
8208
8007
  async readSotDocument(projectId, filename) {
8209
8008
  const projectDir = this.getProjectDir(projectId);
8210
8009
  const possiblePaths = [
8211
- path3__default.default.join(projectDir, "_sot", filename),
8212
- path3__default.default.join(projectDir, filename)
8010
+ path4__default.default.join(projectDir, "_sot", filename),
8011
+ path4__default.default.join(projectDir, filename)
8213
8012
  ];
8214
8013
  for (const p of possiblePaths) {
8215
- if (fs2__default.default.existsSync(p) && fs2__default.default.statSync(p).isFile()) {
8216
- return fs2__default.default.readFileSync(p, "utf-8");
8014
+ if (fs3__default.default.existsSync(p) && fs3__default.default.statSync(p).isFile()) {
8015
+ return fs3__default.default.readFileSync(p, "utf-8");
8217
8016
  }
8218
8017
  }
8219
8018
  return null;
8220
8019
  }
8221
8020
  async listSotDocuments(projectId) {
8222
8021
  const projectDir = this.getProjectDir(projectId);
8223
- const sotDir = path3__default.default.join(projectDir, "_sot");
8022
+ const sotDir = path4__default.default.join(projectDir, "_sot");
8224
8023
  const discoveredFiles = new Set(STANDARD_SOT_FILES);
8225
- if (fs2__default.default.existsSync(sotDir)) {
8024
+ if (fs3__default.default.existsSync(sotDir)) {
8226
8025
  try {
8227
- const diskFiles = fs2__default.default.readdirSync(sotDir);
8026
+ const diskFiles = fs3__default.default.readdirSync(sotDir);
8228
8027
  for (const f of diskFiles) {
8229
8028
  if (f.startsWith(".") || f.endsWith(".review.json")) continue;
8230
- const fullPath = path3__default.default.join(sotDir, f);
8231
- if (fs2__default.default.statSync(fullPath).isFile()) {
8029
+ const fullPath = path4__default.default.join(sotDir, f);
8030
+ if (fs3__default.default.statSync(fullPath).isFile()) {
8232
8031
  discoveredFiles.add(f);
8233
8032
  }
8234
8033
  }
@@ -8236,9 +8035,9 @@ var FileSystemCurriculumAdapter = class {
8236
8035
  }
8237
8036
  }
8238
8037
  return Array.from(discoveredFiles).map((filename) => {
8239
- const p = fs2__default.default.existsSync(path3__default.default.join(sotDir, filename)) ? path3__default.default.join(sotDir, filename) : fs2__default.default.existsSync(path3__default.default.join(projectDir, filename)) ? path3__default.default.join(projectDir, filename) : null;
8240
- if (p && fs2__default.default.existsSync(p)) {
8241
- const stats = fs2__default.default.statSync(p);
8038
+ const p = fs3__default.default.existsSync(path4__default.default.join(sotDir, filename)) ? path4__default.default.join(sotDir, filename) : fs3__default.default.existsSync(path4__default.default.join(projectDir, filename)) ? path4__default.default.join(projectDir, filename) : null;
8039
+ if (p && fs3__default.default.existsSync(p)) {
8040
+ const stats = fs3__default.default.statSync(p);
8242
8041
  return {
8243
8042
  name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
8244
8043
  filename,
@@ -8256,18 +8055,18 @@ var FileSystemCurriculumAdapter = class {
8256
8055
  }
8257
8056
  async readArtifact(projectId, relPathOrIdentifier) {
8258
8057
  const projectDir = this.getProjectDir(projectId);
8259
- const directCandidate = path3__default.default.join(projectDir, relPathOrIdentifier);
8058
+ const directCandidate = path4__default.default.join(projectDir, relPathOrIdentifier);
8260
8059
  this.assertInsideProject(projectDir, directCandidate);
8261
8060
  const possiblePaths = [
8262
8061
  directCandidate,
8263
- path3__default.default.join(projectDir, "_sot", relPathOrIdentifier),
8264
- path3__default.default.join(projectDir, "lessons", relPathOrIdentifier),
8265
- path3__default.default.join(projectDir, "_sot", path3__default.default.basename(relPathOrIdentifier)),
8266
- path3__default.default.join(projectDir, "lessons", path3__default.default.basename(relPathOrIdentifier))
8062
+ path4__default.default.join(projectDir, "_sot", relPathOrIdentifier),
8063
+ path4__default.default.join(projectDir, "lessons", relPathOrIdentifier),
8064
+ path4__default.default.join(projectDir, "_sot", path4__default.default.basename(relPathOrIdentifier)),
8065
+ path4__default.default.join(projectDir, "lessons", path4__default.default.basename(relPathOrIdentifier))
8267
8066
  ];
8268
8067
  for (const p of possiblePaths) {
8269
- if (fs2__default.default.existsSync(p) && fs2__default.default.statSync(p).isFile()) {
8270
- return fs2__default.default.readFileSync(p, "utf-8");
8068
+ if (fs3__default.default.existsSync(p) && fs3__default.default.statSync(p).isFile()) {
8069
+ return fs3__default.default.readFileSync(p, "utf-8");
8271
8070
  }
8272
8071
  }
8273
8072
  if (relPathOrIdentifier.includes(".md") || relPathOrIdentifier.includes("/")) {
@@ -8276,8 +8075,8 @@ var FileSystemCurriculumAdapter = class {
8276
8075
  const lessonMatch = relPathOrIdentifier.match(/U\d+_M\d+_L\d+/i);
8277
8076
  if (lessonMatch) {
8278
8077
  const lessonId = lessonMatch[0].toUpperCase();
8279
- const lessonDir = path3__default.default.join(projectDir, "lessons", lessonId);
8280
- if (fs2__default.default.existsSync(lessonDir) && fs2__default.default.statSync(lessonDir).isDirectory()) {
8078
+ const lessonDir = path4__default.default.join(projectDir, "lessons", lessonId);
8079
+ if (fs3__default.default.existsSync(lessonDir) && fs3__default.default.statSync(lessonDir).isDirectory()) {
8281
8080
  const lower = relPathOrIdentifier.toLowerCase();
8282
8081
  let artifactPrefix = "LESSON";
8283
8082
  if (lower.includes("act") || lower.includes("lab") || lower.includes("th\u1EF1c h\xE0nh")) {
@@ -8297,9 +8096,9 @@ var FileSystemCurriculumAdapter = class {
8297
8096
  } else if (lower.includes("ext") || lower.includes("extension")) {
8298
8097
  artifactPrefix = "EXT";
8299
8098
  }
8300
- const candidateFile = path3__default.default.join(lessonDir, `${artifactPrefix}_${lessonId}.md`);
8301
- if (fs2__default.default.existsSync(candidateFile)) {
8302
- return fs2__default.default.readFileSync(candidateFile, "utf-8");
8099
+ const candidateFile = path4__default.default.join(lessonDir, `${artifactPrefix}_${lessonId}.md`);
8100
+ if (fs3__default.default.existsSync(candidateFile)) {
8101
+ return fs3__default.default.readFileSync(candidateFile, "utf-8");
8303
8102
  }
8304
8103
  }
8305
8104
  }
@@ -8307,22 +8106,22 @@ var FileSystemCurriculumAdapter = class {
8307
8106
  }
8308
8107
  async saveArtifact(projectId, relPath, content) {
8309
8108
  const projectDir = this.getProjectDir(projectId);
8310
- const targetPath = path3__default.default.join(projectDir, relPath);
8109
+ const targetPath = path4__default.default.join(projectDir, relPath);
8311
8110
  this.assertInsideProject(projectDir, targetPath);
8312
- const targetDir = path3__default.default.dirname(targetPath);
8313
- if (!fs2__default.default.existsSync(targetDir)) {
8314
- fs2__default.default.mkdirSync(targetDir, { recursive: true });
8111
+ const targetDir = path4__default.default.dirname(targetPath);
8112
+ if (!fs3__default.default.existsSync(targetDir)) {
8113
+ fs3__default.default.mkdirSync(targetDir, { recursive: true });
8315
8114
  }
8316
- if (fs2__default.default.existsSync(targetPath)) {
8115
+ if (fs3__default.default.existsSync(targetPath)) {
8317
8116
  try {
8318
- const oldContent = fs2__default.default.readFileSync(targetPath, "utf-8");
8117
+ const oldContent = fs3__default.default.readFileSync(targetPath, "utf-8");
8319
8118
  if (oldContent !== content) {
8320
- const historyDir = path3__default.default.join(projectDir, ".history", relPath);
8321
- fs2__default.default.mkdirSync(historyDir, { recursive: true });
8322
- atomicWriteFileSync(path3__default.default.join(historyDir, `${Date.now()}.md`), oldContent);
8323
- const versions = fs2__default.default.readdirSync(historyDir).filter((f) => f.endsWith(".md")).sort();
8119
+ const historyDir = path4__default.default.join(projectDir, ".history", relPath);
8120
+ fs3__default.default.mkdirSync(historyDir, { recursive: true });
8121
+ atomicWriteFileSync(path4__default.default.join(historyDir, `${Date.now()}.md`), oldContent);
8122
+ const versions = fs3__default.default.readdirSync(historyDir).filter((f) => f.endsWith(".md")).sort();
8324
8123
  while (versions.length > 10) {
8325
- fs2__default.default.unlinkSync(path3__default.default.join(historyDir, versions.shift()));
8124
+ fs3__default.default.unlinkSync(path4__default.default.join(historyDir, versions.shift()));
8326
8125
  }
8327
8126
  }
8328
8127
  } catch (histErr) {
@@ -8330,24 +8129,24 @@ var FileSystemCurriculumAdapter = class {
8330
8129
  }
8331
8130
  }
8332
8131
  atomicWriteFileSync(targetPath, content);
8333
- const filename = path3__default.default.basename(relPath);
8132
+ const filename = path4__default.default.basename(relPath);
8334
8133
  const match = filename.match(/(LESSON|ACT|QUIZ|SLIDE|GUIDE|HANDOUT|WKS|EXT)_(U\d+_M\d+_L\d+)\.md/i);
8335
8134
  if (match) {
8336
8135
  const lessonId = match[2].toUpperCase();
8337
- atomicWriteFileSync(path3__default.default.join(projectDir, "lessons", lessonId, filename), content);
8338
- const legacyContentDir = path3__default.default.join(projectDir, "_content", lessonId);
8339
- if (fs2__default.default.existsSync(legacyContentDir)) {
8340
- atomicWriteFileSync(path3__default.default.join(legacyContentDir, filename), content);
8136
+ atomicWriteFileSync(path4__default.default.join(projectDir, "lessons", lessonId, filename), content);
8137
+ const legacyContentDir = path4__default.default.join(projectDir, "_content", lessonId);
8138
+ if (fs3__default.default.existsSync(legacyContentDir)) {
8139
+ atomicWriteFileSync(path4__default.default.join(legacyContentDir, filename), content);
8341
8140
  }
8342
8141
  }
8343
8142
  }
8344
8143
  async listLessons(projectId) {
8345
- const lessonsDir = path3__default.default.join(this.getProjectDir(projectId), "lessons");
8346
- if (!fs2__default.default.existsSync(lessonsDir)) return [];
8347
- const lessonFolders = fs2__default.default.readdirSync(lessonsDir).filter((f) => !f.startsWith("."));
8144
+ const lessonsDir = path4__default.default.join(this.getProjectDir(projectId), "lessons");
8145
+ if (!fs3__default.default.existsSync(lessonsDir)) return [];
8146
+ const lessonFolders = fs3__default.default.readdirSync(lessonsDir).filter((f) => !f.startsWith("."));
8348
8147
  return lessonFolders.map((folder) => {
8349
- const lDir = path3__default.default.join(lessonsDir, folder);
8350
- const isDir = fs2__default.default.statSync(lDir).isDirectory();
8148
+ const lDir = path4__default.default.join(lessonsDir, folder);
8149
+ const isDir = fs3__default.default.statSync(lDir).isDirectory();
8351
8150
  if (!isDir) {
8352
8151
  return {
8353
8152
  lessonId: folder,
@@ -8356,7 +8155,7 @@ var FileSystemCurriculumAdapter = class {
8356
8155
  artifacts: []
8357
8156
  };
8358
8157
  }
8359
- const artifacts = fs2__default.default.readdirSync(lDir).filter((f) => !f.startsWith("."));
8158
+ const artifacts = fs3__default.default.readdirSync(lDir).filter((f) => !f.startsWith("."));
8360
8159
  return {
8361
8160
  lessonId: folder,
8362
8161
  artifactsCount: artifacts.length,
@@ -8402,10 +8201,10 @@ ${lessonTable}
8402
8201
  }
8403
8202
  async getPipelineState(projectId) {
8404
8203
  const projectDir = this.getProjectDir(projectId);
8405
- const stateFile = path3__default.default.join(projectDir, "_pipeline", "state.json");
8406
- if (fs2__default.default.existsSync(stateFile)) {
8204
+ const stateFile = path4__default.default.join(projectDir, "_pipeline", "state.json");
8205
+ if (fs3__default.default.existsSync(stateFile)) {
8407
8206
  try {
8408
- const raw = fs2__default.default.readFileSync(stateFile, "utf-8");
8207
+ const raw = fs3__default.default.readFileSync(stateFile, "utf-8");
8409
8208
  const parsed = JSON.parse(raw);
8410
8209
  if (parsed && typeof parsed === "object" && parsed.tasks) {
8411
8210
  return parsed;
@@ -8422,11 +8221,11 @@ ${lessonTable}
8422
8221
  }
8423
8222
  async savePipelineState(projectId, state) {
8424
8223
  const projectDir = this.getProjectDir(projectId);
8425
- const pipelineDir = path3__default.default.join(projectDir, "_pipeline");
8426
- if (!fs2__default.default.existsSync(pipelineDir)) {
8427
- fs2__default.default.mkdirSync(pipelineDir, { recursive: true });
8224
+ const pipelineDir = path4__default.default.join(projectDir, "_pipeline");
8225
+ if (!fs3__default.default.existsSync(pipelineDir)) {
8226
+ fs3__default.default.mkdirSync(pipelineDir, { recursive: true });
8428
8227
  }
8429
- const stateFile = path3__default.default.join(pipelineDir, "state.json");
8228
+ const stateFile = path4__default.default.join(pipelineDir, "state.json");
8430
8229
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
8431
8230
  atomicWriteFileSync(stateFile, JSON.stringify(state, null, 2));
8432
8231
  }
@@ -8449,9 +8248,9 @@ ${lessonTable}
8449
8248
  async deleteArtifact(projectId, relPath) {
8450
8249
  try {
8451
8250
  const projectDir = this.getProjectDir(projectId);
8452
- const targetPath = path3__default.default.join(projectDir, relPath);
8251
+ const targetPath = path4__default.default.join(projectDir, relPath);
8453
8252
  this.assertInsideProject(projectDir, targetPath);
8454
- if (fs2__default.default.existsSync(targetPath)) fs2__default.default.rmSync(targetPath, { force: true });
8253
+ if (fs3__default.default.existsSync(targetPath)) fs3__default.default.rmSync(targetPath, { force: true });
8455
8254
  } catch {
8456
8255
  }
8457
8256
  }
@@ -9201,19 +9000,19 @@ function buildStandardStackMarkdown(opts) {
9201
9000
 
9202
9001
  // src/services/sotGeneratorService.ts
9203
9002
  var __filename$1 = typeof (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) === "string" && typeof url.fileURLToPath === "function" ? url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))) : "";
9204
- var __dirname2 = __filename$1 ? path3__default.default.dirname(__filename$1) : process.cwd();
9003
+ var __dirname2 = __filename$1 ? path4__default.default.dirname(__filename$1) : process.cwd();
9205
9004
  function loadSotTemplate(templateFileName) {
9206
9005
  const possiblePaths = [
9207
- path3__default.default.resolve(__dirname2, "../../docs/refs/templates", templateFileName),
9208
- path3__default.default.resolve(process.cwd(), "packages/curriculum-kit/docs/refs/templates", templateFileName),
9209
- path3__default.default.resolve(process.cwd(), "../learnwell-platform/packages/curriculum-kit/docs/refs/templates", templateFileName),
9210
- path3__default.default.resolve("/Users/tonypham/MEGA/WebApp/WIP/learnwell-platform/packages/curriculum-kit/docs/refs/templates", templateFileName),
9211
- path3__default.default.resolve("/Users/tonypham/MEGA/ai-platform/learning-artifact-gen-ui/_templates", templateFileName)
9006
+ path4__default.default.resolve(__dirname2, "../../docs/refs/templates", templateFileName),
9007
+ path4__default.default.resolve(process.cwd(), "packages/curriculum-kit/docs/refs/templates", templateFileName),
9008
+ path4__default.default.resolve(process.cwd(), "../learnwell-platform/packages/curriculum-kit/docs/refs/templates", templateFileName),
9009
+ path4__default.default.resolve("/Users/tonypham/MEGA/WebApp/WIP/learnwell-platform/packages/curriculum-kit/docs/refs/templates", templateFileName),
9010
+ path4__default.default.resolve("/Users/tonypham/MEGA/ai-platform/learning-artifact-gen-ui/_templates", templateFileName)
9212
9011
  ];
9213
9012
  for (const p of possiblePaths) {
9214
- if (fs2__default.default.existsSync(p)) {
9013
+ if (fs3__default.default.existsSync(p)) {
9215
9014
  try {
9216
- return fs2__default.default.readFileSync(p, "utf-8");
9015
+ return fs3__default.default.readFileSync(p, "utf-8");
9217
9016
  } catch {
9218
9017
  }
9219
9018
  }
@@ -9629,6 +9428,7 @@ function gateModeFor(resolved, type) {
9629
9428
  }
9630
9429
 
9631
9430
  // src/services/languageDirective.ts
9431
+ init_templateLoader();
9632
9432
  function resolveTargetLanguageCode(language) {
9633
9433
  const raw = String(language || "vi").trim().toLowerCase();
9634
9434
  if (raw.startsWith("vi")) return "vi";
@@ -9669,137 +9469,58 @@ function matchesArtifactType(candidate, target) {
9669
9469
  if ((c === "EXT" || c === "EXTENSION") && (t === "EXT" || t === "EXTENSION")) return true;
9670
9470
  return false;
9671
9471
  }
9672
- var DEFAULT_VIETNAMESE_SECTION_HEADINGS = {
9673
- LEARNER_PROFILE: {
9674
- "Foundational Info": "Th\xF4ng tin N\u1EC1n t\u1EA3ng",
9675
- "Entry Level": "Tr\xECnh \u0111\u1ED9 \u0110\u1EA7u v\xE0o",
9676
- "Learning Context": "B\u1ED1i c\u1EA3nh H\u1ECDc t\u1EADp",
9677
- "Client Objectives": "M\u1EE5c ti\xEAu Kh\xE1ch h\xE0ng & Chu\u1EA9n \u0111\u1EA7u ra"
9678
- },
9679
- PROJECT_BRIEF: {
9680
- "Project Overview": "T\u1ED5ng quan D\u1EF1 \xE1n",
9681
- "Audience & Duration": "\u0110\u1ED1i t\u01B0\u1EE3ng & Th\u1EDDi l\u01B0\u1EE3ng",
9682
- "Learning Roadmap": "L\u1ED9 tr\xECnh H\u1ECDc t\u1EADp & C\u1ED9t m\u1ED1c",
9683
- "Artifact Scope": "Ph\u1EA1m vi S\u1EA3n ph\u1EA9m H\u1ECDc t\u1EADp"
9684
- },
9685
- CURRICULUM_FRAMEWORK: {
9686
- "Course Overview": "T\u1ED5ng quan Kh\xF3a h\u1ECDc",
9687
- "Global Learning Objectives": "M\u1EE5c ti\xEAu H\u1ECDc t\u1EADp To\xE0n di\u1EC7n",
9688
- "Structural Hierarchy (Unit & Module)": "C\u1EA5u tr\xFAc Kh\xF3a h\u1ECDc (Unit & Module)",
9689
- "Scope & Sequence (Detailed Roadmap)": "Ph\u1EA1m vi & Tr\xECnh t\u1EF1 Chi ti\u1EBFt"
9690
- },
9691
- CONTENT_STYLE_GUIDE: {
9692
- "Voice & Tone": "Gi\u1ECDng v\u0103n & Phong c\xE1ch",
9693
- "Standard Terminology (Glossary)": "Thu\u1EADt ng\u1EEF Chu\u1EA9n (B\u1EA3ng thu\u1EADt ng\u1EEF)",
9694
- "Examples & Context Rules": "Quy t\u1EAFc V\xED d\u1EE5 & Ng\u1EEF c\u1EA3nh",
9695
- "Lesson Content Standards": "Ti\xEAu chu\u1EA9n N\u1ED9i dung B\xE0i h\u1ECDc"
9696
- },
9697
- SECTION_LANGUAGE_CONTRACT: {
9698
- "Metadata & Scope": "Th\xF4ng tin & Ph\u1EA1m vi H\u1EE3p \u0111\u1ED3ng",
9699
- "Section Translation Matrix": "Ma tr\u1EADn Ti\xEAu \u0111\u1EC1 \u0110a ng\xF4n ng\u1EEF",
9700
- "Validation Rules": "Quy t\u1EAFc X\xE1c th\u1EF1c & Kh\xF3a Chu\u1EA9n"
9701
- },
9702
- KNOWLEDGE_EXPOSITION: {
9703
- "Session Scope": "Ph\u1EA1m vi Bu\u1ED5i h\u1ECDc",
9704
- "Key Terms": "Thu\u1EADt ng\u1EEF Then ch\u1ED1t",
9705
- "Concept Narratives": "Di\u1EC5n gi\u1EA3i Kh\xE1i ni\u1EC7m",
9706
- "Worked Micro-Examples": "V\xED d\u1EE5 M\u1EABu Chi ti\u1EBFt",
9707
- "Common Mistakes": "L\u1ED7i Th\u01B0\u1EDDng g\u1EB7p & Kh\u1EAFc ph\u1EE5c",
9708
- "Self-Check Questions": "C\xE2u h\u1ECFi T\u1EF1 ki\u1EC3m tra"
9709
- },
9710
- LESSON: {
9711
- "A. Lesson Design Plan": "A. K\u1EBF ho\u1EA1ch Thi\u1EBFt k\u1EBF B\xE0i h\u1ECDc",
9712
- "Learning Objectives & Evidence": "M\u1EE5c ti\xEAu H\u1ECDc t\u1EADp & B\u1EB1ng ch\u1EE9ng N\u0103ng l\u1EF1c",
9713
- "Activity Sequence": "Chu\u1ED7i Ho\u1EA1t \u0111\u1ED9ng D\u1EA1y & H\u1ECDc",
9714
- "Resource Map": "B\u1EA3n \u0111\u1ED3 T\xE0i nguy\xEAn & H\u1ECDc li\u1EC7u",
9715
- "Assessment Map": "B\u1EA3n \u0111\u1ED3 \u0110\xE1nh gi\xE1 N\u0103ng l\u1EF1c",
9716
- "Artifact Contract": "H\u1EE3p \u0111\u1ED3ng S\u1EA3n ph\u1EA9m \u0110\u1EA7u ra",
9717
- "B. Lesson Flow": "B. Ti\u1EBFn tr\xECnh Gi\u1EA3ng d\u1EA1y Chi ti\u1EBFt"
9718
- },
9719
- ACT: {
9720
- "Computational Thinking Focus": "Tr\u1ECDng t\xE2m T\u01B0 duy M\xE1y t\xEDnh",
9721
- "Materials & Setup": "V\u1EADt li\u1EC7u & Chu\u1EA9n b\u1ECB Thi\u1EBFt b\u1ECB",
9722
- "Constraints & Safety": "R\xE0ng bu\u1ED9c & An to\xE0n Ph\xF2ng th\u1EF1c h\xE0nh",
9723
- "Active Learning Workflow": "Quy tr\xECnh Ho\u1EA1t \u0111\u1ED9ng Tr\u1EA3i nghi\u1EC7m",
9724
- "3-Tier Differentiation": "Ph\xE2n h\xF3a H\u1ECDc t\u1EADp 3 T\u1EA7ng",
9725
- "Reflection": "T\u1ED5ng k\u1EBFt & \u0110\xFAc k\u1EBFt Tr\u1EA3i nghi\u1EC7m"
9726
- },
9727
- CODE: {
9728
- "Technical Overview & Architecture Blueprint": "T\u1ED5ng quan K\u1EF9 thu\u1EADt & B\u1EA3n thi\u1EBFt k\u1EBF Ki\u1EBFn tr\xFAc",
9729
- "Hardware Pinout & Wiring Configuration Matrix": "S\u01A1 \u0111\u1ED3 Ch\xE2n & Ma tr\u1EADn N\u1ED1i d\xE2y Ph\u1EA7n c\u1EE9ng",
9730
- "Starter Code Sandbox": "M\xE3 ngu\u1ED3n Kh\u1EDFi \u0111\u1EA7u (Starter Code)",
9731
- "Verified Reference Solution Code": "M\xE3 ngu\u1ED3n L\u1EDDi gi\u1EA3i Chu\u1EA9n (Reference Solution)",
9732
- "Automated Test / Verification Script": "K\u1ECBch b\u1EA3n Ki\u1EC3m th\u1EED T\u1EF1 \u0111\u1ED9ng",
9733
- "Common Syntax & Runtime Pitfalls Matrix": "Ma tr\u1EADn L\u1ED7i C\xFA ph\xE1p & Th\u1EDDi gian ch\u1EA1y Th\u01B0\u1EDDng g\u1EB7p"
9734
- },
9735
- CODE_LAB: {
9736
- "Technical Overview & Architecture Blueprint": "T\u1ED5ng quan K\u1EF9 thu\u1EADt & B\u1EA3n thi\u1EBFt k\u1EBF Ki\u1EBFn tr\xFAc",
9737
- "Hardware Pinout & Wiring Configuration Matrix": "S\u01A1 \u0111\u1ED3 Ch\xE2n & Ma tr\u1EADn N\u1ED1i d\xE2y Ph\u1EA7n c\u1EE9ng",
9738
- "Starter Code Sandbox": "M\xE3 ngu\u1ED3n Kh\u1EDFi \u0111\u1EA7u (Starter Code)",
9739
- "Verified Reference Solution Code": "M\xE3 ngu\u1ED3n L\u1EDDi gi\u1EA3i Chu\u1EA9n (Reference Solution)",
9740
- "Automated Test / Verification Script": "K\u1ECBch b\u1EA3n Ki\u1EC3m th\u1EED T\u1EF1 \u0111\u1ED9ng",
9741
- "Common Syntax & Runtime Pitfalls Matrix": "Ma tr\u1EADn L\u1ED7i C\xFA ph\xE1p & Th\u1EDDi gian ch\u1EA1y Th\u01B0\u1EDDng g\u1EB7p"
9742
- },
9743
- QUIZ: {
9744
- "Assessment Objectives": "M\u1EE5c ti\xEAu \u0110\xE1nh gi\xE1",
9745
- "Multiple-Choice Question Bank": "Ng\xE2n h\xE0ng C\xE2u h\u1ECFi Tr\u1EAFc nghi\u1EC7m",
9746
- "Code Analysis / Debugging Question": "C\xE2u h\u1ECFi Ph\xE2n t\xEDch M\xE3 ngu\u1ED3n & S\u1EEDa l\u1ED7i",
9747
- "Competency Rubric": "Thang \u0111o \u0110\xE1nh gi\xE1 N\u0103ng l\u1EF1c (Rubric)"
9748
- },
9749
- GUIDE: {
9750
- "Objectives & Preparation": "M\u1EE5c ti\xEAu & Chu\u1EA9n b\u1ECB Gi\u1EA3ng d\u1EA1y",
9751
- "Preparation Checklist & Workstation Setup": "Danh m\u1EE5c Ki\u1EC3m tra Chu\u1EA9n b\u1ECB & Tr\u1EA1m Th\u1EF1c h\xE0nh",
9752
- "Facilitation Script & Timeline": "K\u1ECBch b\u1EA3n \u0110i\u1EC1u ph\u1ED1i & Khung Th\u1EDDi gian",
9753
- "Common Misconceptions & Diagnostic Remediation Matrix": "Quan ni\u1EC7m Sai l\u1EA7m Th\u01B0\u1EDDng g\u1EB7p & Ma tr\u1EADn Kh\u1EAFc ph\u1EE5c",
9754
- "Differentiated Support Strategies": "Chi\u1EBFn l\u01B0\u1EE3c H\u1ED7 tr\u1EE3 Ph\xE2n h\xF3a"
9755
- },
9756
- HANDOUT: {
9757
- "Core Concepts": "Kh\xE1i ni\u1EC7m C\u1ED1t l\xF5i",
9758
- "Core Syntax & Mechanism Cheat Sheet": "B\u1EA3ng Tra c\u1EE9u C\xFA ph\xE1p & C\u01A1 ch\u1EBF Tr\u1ECDng t\xE2m",
9759
- "Visual Mental Model / Architecture Diagram": "M\xF4 h\xECnh T\u01B0 duy Tr\u1EF1c quan / S\u01A1 \u0111\u1ED3 Ki\u1EBFn tr\xFAc",
9760
- "Step-by-Step Practical Quick-Start Guide": "H\u01B0\u1EDBng d\u1EABn Th\u1EF1c h\xE0nh T\u1EEBng b\u01B0\u1EDBc",
9761
- "Self-Check Diagnostic Checklist": "B\u1EA3ng Ki\u1EC3m tra T\u1EF1 ch\u1EA9n \u0111o\xE1n"
9762
- },
9763
- WKS: {
9764
- "Part 1: Knowledge Check": "Ph\u1EA7n 1: Ki\u1EC3m tra Ki\u1EBFn th\u1EE9c",
9765
- "Part 2: Concept Tracing & Diagram Fill-in": "Ph\u1EA7n 2: L\u1EA7n v\u1EBFt Kh\xE1i ni\u1EC7m & \u0110i\u1EC1n S\u01A1 \u0111\u1ED3",
9766
- "Part 3: Code Analysis & Bug Hunting Challenge": "Ph\u1EA7n 3: Ph\xE2n t\xEDch M\xE3 & Th\u1EED th\xE1ch S\u0103n l\u1ED7i",
9767
- "Part 4: Synthesis & Problem-Solving Application": "Ph\u1EA7n 4: T\u1ED5ng h\u1EE3p & \u1EE8ng d\u1EE5ng Gi\u1EA3i quy\u1EBFt V\u1EA5n \u0111\u1EC1",
9768
- "Part 5: Self-Reflection & Learning Log": "Ph\u1EA7n 5: T\u1EF1 suy ng\u1EABm & Nh\u1EADt k\xFD H\u1ECDc t\u1EADp"
9769
- },
9770
- EXT: {
9771
- "For: Advanced Students / Extra Time": "D\xE0nh cho: H\u1ECDc sinh N\xE2ng cao / Ho\xE0n th\xE0nh S\u1EDBm",
9772
- "Challenge 1: Mission Briefing & Real-World High-Stakes Scenario": "Th\u1EED th\xE1ch 1: B\u1ED1i c\u1EA3nh Nhi\u1EC7m v\u1EE5 Th\u1EF1c t\u1EBF & K\u1ECBch b\u1EA3n \u1EE8ng d\u1EE5ng",
9773
- "Challenge 2: Advanced Architectural Constraints & Edge Cases": "Th\u1EED th\xE1ch 2: R\xE0ng bu\u1ED9c Ki\u1EBFn tr\xFAc N\xE2ng cao & Tr\u01B0\u1EDDng h\u1EE3p Bi\xEAn",
9774
- "Challenge 3: Extension Milestones": "Th\u1EED th\xE1ch 3: C\xE1c M\u1ED1c Nhi\u1EC7m v\u1EE5 N\xE2ng cao",
9775
- "Metacognitive Deep Dive & Engineering Trade-offs": "Suy ng\u1EABm Si\xEAu nh\u1EADn th\u1EE9c & \u0110\xE1nh \u0111\u1ED5i K\u1EF9 thu\u1EADt"
9776
- },
9777
- SLIDE: {
9778
- "Title & Agenda": "Ti\xEAu \u0111\u1EC1 & M\u1EE5c l\u1EE5c",
9779
- "Core Concepts": "Kh\xE1i ni\u1EC7m C\u1ED1t l\xF5i",
9780
- "Guided Demonstration": "H\u01B0\u1EDBng d\u1EABn Th\u1EF1c h\xE0nh & Th\u1ECB ph\u1EA1m",
9781
- "Hands-on Mission": "Nhi\u1EC7m v\u1EE5 Th\u1EF1c h\xE0nh Tr\u1EA3i nghi\u1EC7m",
9782
- "Summary & Next Steps": "T\u1ED5ng k\u1EBFt & \u0110\u1ECBnh h\u01B0\u1EDBng Ti\u1EBFp theo"
9783
- }
9472
+ var TEMPLATE_BY_TYPE = {
9473
+ LESSON: "LESSON_5E_template.md",
9474
+ LESSON_5E: "LESSON_5E_template.md",
9475
+ LESSON_EDP: "LESSON_EDP_template.md",
9476
+ LESSON_GENERAL: "LESSON_GENERAL_template.md",
9477
+ ACT: "ACT_template.md",
9478
+ CODE: "CODE_template.md",
9479
+ // JSON-first code lab shares the CODE label set (the serializer renders its
9480
+ // own headings; this lookup only feeds the SLC/directive fallback path).
9481
+ CODE_LAB: "CODE_template.md",
9482
+ QUIZ: "QUIZ_v4.6_template.md",
9483
+ GUIDE: "GUIDE_template.md",
9484
+ HANDOUT: "HANDOUT_template.md",
9485
+ WKS: "WKS_template.md",
9486
+ EXT: "EXT_template.md",
9487
+ SLIDE: "SLIDE_template.md",
9488
+ KNOWLEDGE_EXPOSITION: "KNOWLEDGE_EXPOSITION_template.md",
9489
+ LEARNER_PROFILE: "LEARNER_PROFILE_template.md",
9490
+ PROJECT_BRIEF: "PROJECT_BRIEF_template.md",
9491
+ CURRICULUM_FRAMEWORK: "CURRICULUM_FRAMEWORK_template.md",
9492
+ CONTENT_STYLE_GUIDE: "CONTENT_STYLE_GUIDE_template.md",
9493
+ SECTION_LANGUAGE_CONTRACT: "SECTION_LANGUAGE_CONTRACT_template.md"
9784
9494
  };
9495
+ function keepContractEntries(artifactType, mapping) {
9496
+ const templateFile = TEMPLATE_BY_TYPE[artifactType.trim().toUpperCase()];
9497
+ if (!templateFile) return mapping;
9498
+ const resolved = resolveArtifactTemplate(templateFile);
9499
+ if (!resolved) return mapping;
9500
+ const contract = new Set(resolved.sections.map(stripHeadingKey));
9501
+ const kept = {};
9502
+ for (const [k, v] of Object.entries(mapping)) {
9503
+ if (contract.has(stripHeadingKey(k))) kept[k] = v;
9504
+ }
9505
+ return kept;
9506
+ }
9785
9507
  function extractSectionHeadingsFromSLC(slcInput, artifactType, targetLanguage = "vi") {
9786
9508
  const upperType = artifactType.trim().toUpperCase();
9787
9509
  const getFallback = () => {
9788
- if (resolveTargetLanguageCode(targetLanguage) === "vi") {
9789
- for (const [key, mapping] of Object.entries(DEFAULT_VIETNAMESE_SECTION_HEADINGS)) {
9790
- if (matchesArtifactType(key, upperType)) {
9791
- return mapping;
9792
- }
9793
- }
9794
- }
9795
- return {};
9510
+ const templateFile = TEMPLATE_BY_TYPE[upperType];
9511
+ if (!templateFile) return {};
9512
+ const resolved = resolveArtifactTemplate(templateFile);
9513
+ if (!resolved) return {};
9514
+ return Object.fromEntries(resolved.sections.map((s) => [s, s]));
9796
9515
  };
9797
9516
  if (!slcInput) return getFallback();
9798
9517
  if (typeof slcInput === "object") {
9799
- if (slcInput[upperType] && Object.keys(slcInput[upperType]).length > 0) return slcInput[upperType];
9518
+ if (slcInput[upperType] && Object.keys(slcInput[upperType]).length > 0) {
9519
+ return keepContractEntries(upperType, slcInput[upperType]);
9520
+ }
9800
9521
  for (const [key, mapping] of Object.entries(slcInput)) {
9801
9522
  if (matchesArtifactType(key, upperType) && Object.keys(mapping).length > 0) {
9802
- return mapping;
9523
+ return keepContractEntries(upperType, mapping);
9803
9524
  }
9804
9525
  }
9805
9526
  return getFallback();
@@ -9827,24 +9548,33 @@ function extractSectionHeadingsFromSLC(slcInput, artifactType, targetLanguage =
9827
9548
  }
9828
9549
  }
9829
9550
  }
9830
- if (Object.keys(mapping).length > 0) return mapping;
9551
+ if (Object.keys(mapping).length > 0) return keepContractEntries(upperType, mapping);
9831
9552
  }
9832
9553
  }
9833
9554
  return getFallback();
9834
9555
  }
9835
- function buildHeadingDirective(artifactType, slcMarkdown, targetLanguage = "vi") {
9836
- const headings = extractSectionHeadingsFromSLC(slcMarkdown, artifactType, targetLanguage);
9556
+ function buildHeadingDirective(artifactType, slcMarkdown, targetLanguage = "vi", opts) {
9557
+ const upperType = artifactType.trim().toUpperCase();
9558
+ const templateFile = TEMPLATE_BY_TYPE[upperType];
9559
+ const resolved = templateFile ? resolveArtifactTemplate(templateFile, opts) : null;
9560
+ const baked = !!resolved && resolved.declaredLang !== "en";
9561
+ const headings = resolved ? Object.fromEntries(
9562
+ resolved.sections.map((s) => [
9563
+ s,
9564
+ baked ? s : extractSectionHeadingsFromSLC(slcMarkdown, upperType, targetLanguage)[s] || s
9565
+ ])
9566
+ ) : extractSectionHeadingsFromSLC(baked ? null : slcMarkdown, upperType, targetLanguage);
9837
9567
  if (Object.keys(headings).length === 0) {
9838
9568
  return "";
9839
9569
  }
9840
9570
  const lines = Object.entries(headings).map(
9841
- ([canonical, localized]) => ` - "${canonical}" -> Use Display Heading: "${localized}"`
9571
+ ([heading, display]) => heading === display ? ` - "${display}"` : ` - "${heading}" -> Use Display Heading: "${display}"`
9842
9572
  );
9843
- return `MANDATORY SECTION HEADINGS FROM SECTION_LANGUAGE_CONTRACT:
9844
- - The YAML frontmatter \`template_required_sections\` MUST preserve the exact English canonical keys.
9845
- - In the markdown body, section headers MUST use the EXACT Localized Display Labels below (do NOT translate or paraphrase):
9573
+ return `MANDATORY SECTION HEADINGS (SOURCE: RESOLVED ARTIFACT TEMPLATE):
9574
+ - Copy the template's YAML frontmatter \`template_required_sections\` list VERBATIM into the artifact frontmatter \u2014 never translate, reorder, or invent entries.
9575
+ - In the markdown body, section headers MUST be exactly the Display Headings below (do NOT translate, paraphrase, or re-marker them):
9846
9576
  ${lines.join("\n")}
9847
- - For example: if canonical section is "Computational Thinking Focus", your heading must be "## [REQUIRED] ${headings["Computational Thinking Focus"] || "Computational Thinking Focus"}".`;
9577
+ - Example: if the resolved template's heading is "${Object.values(headings)[0]}", your body heading must be exactly "## ${Object.values(headings)[0]}".`;
9848
9578
  }
9849
9579
 
9850
9580
  // src/services/curriculumHorizon.ts
@@ -10698,14 +10428,13 @@ function buildSystemPrompt(targetLanguage = "vi", techStack, hardwarePlatform) {
10698
10428
  function buildUserPrompt(session, plan, glossary, targetLanguage = "vi", slcMarkdown, refPack) {
10699
10429
  const depthLines = session.depth_assignments.map((d) => "- " + d.node_id + " [" + d.depth.toUpperCase() + "]: " + DEPTH_RULES[d.depth]).join("\n");
10700
10430
  const termLines = glossary.map((g) => "- " + g.term + (g.definition ? " \u2014 " + g.definition : "") + (g.example ? " (example: " + g.example + ")" : "")).join("\n");
10701
- const langCode = resolveTargetLanguageCode(targetLanguage);
10702
- const headings = extractSectionHeadingsFromSLC(slcMarkdown, "KNOWLEDGE_EXPOSITION");
10703
- const hScope = headings["Session Scope"] || (langCode === "vi" ? "Ph\u1EA1m vi Bu\u1ED5i h\u1ECDc" : "Session Scope");
10704
- const hTerms = headings["Key Terms"] || (langCode === "vi" ? "Thu\u1EADt ng\u1EEF Then ch\u1ED1t" : "Key Terms");
10705
- const hNarratives = headings["Concept Narratives"] || (langCode === "vi" ? "Di\u1EC5n gi\u1EA3i Kh\xE1i ni\u1EC7m" : "Concept Narratives");
10706
- const hExamples = headings["Worked Micro-Examples"] || (langCode === "vi" ? "V\xED d\u1EE5 M\u1EABu Chi ti\u1EBFt" : "Worked Micro-Examples");
10707
- const hMistakes = headings["Common Mistakes"] || (langCode === "vi" ? "L\u1ED7i Th\u01B0\u1EDDng g\u1EB7p & Kh\u1EAFc ph\u1EE5c" : "Common Mistakes");
10708
- const hQuestions = headings["Self-Check Questions"] || (langCode === "vi" ? "C\xE2u h\u1ECFi T\u1EF1 ki\u1EC3m tra" : "Self-Check Questions");
10431
+ const headings = extractSectionHeadingsFromSLC(slcMarkdown, "KNOWLEDGE_EXPOSITION", targetLanguage);
10432
+ const hScope = headings["Session Scope"] || "Session Scope";
10433
+ const hTerms = headings["Key Terms"] || "Key Terms";
10434
+ const hNarratives = headings["Concept Narratives"] || "Concept Narratives";
10435
+ const hExamples = headings["Worked Micro-Examples"] || "Worked Micro-Examples";
10436
+ const hMistakes = headings["Common Mistakes"] || "Common Mistakes";
10437
+ const hQuestions = headings["Self-Check Questions"] || "Self-Check Questions";
10709
10438
  const skeleton = `## ${hScope} / ## ${hTerms} / ## ${hNarratives} / ## ${hExamples} / ## ${hMistakes} / ## ${hQuestions}`;
10710
10439
  const headingDirective = buildHeadingDirective("KNOWLEDGE_EXPOSITION", slcMarkdown, targetLanguage);
10711
10440
  let refPackBlock = "";
@@ -25461,98 +25190,9 @@ function evaluateActivityAlignment(canonicalExcerpt, generatedContent, now = /*
25461
25190
  const passed = matchedTokens.length > 0;
25462
25191
  return { checked: true, passed, matchedTokens: matchedTokens.slice(0, 10), missingTokens: missingTokens.slice(0, 10), mode };
25463
25192
  }
25464
- function stripTemplateFrontmatter(raw) {
25465
- let body = raw;
25466
- if (body.startsWith("---")) {
25467
- const end = body.indexOf("\n---", 3);
25468
- if (end !== -1) body = body.slice(end + 4);
25469
- }
25470
- return body.split("\n").filter((l) => l.trim() !== "<!-- INJECT_BODY -->").join("\n").replace(/^\s*\n/, "").trimEnd();
25471
- }
25472
- function extractRequiredSections(raw) {
25473
- const lines = raw.split("\n");
25474
- const start = lines.findIndex((l) => /^template_required_sections:\s*$/.test(l.trim()));
25475
- if (start === -1) return null;
25476
- const items = [];
25477
- for (let i = start + 1; i < lines.length; i++) {
25478
- const line = lines[i] ?? "";
25479
- if (line.trim() === "") continue;
25480
- if (!/^\s+-\s/.test(line)) break;
25481
- items.push(line.trim().replace(/^-\s+/, "").replace(/^["']|["']$/g, ""));
25482
- }
25483
- return items.length > 0 ? items : null;
25484
- }
25485
- function normalizeHeading2(h) {
25486
- return h.replace(/\[(REQUIRED|OPTIONAL|CONDITIONAL)\]\s*/gi, "").replace(/\s+/g, " ").trim();
25487
- }
25488
- function extractBodySections(raw) {
25489
- const body = stripTemplateFrontmatter(raw);
25490
- return body.split("\n").filter((l) => l.startsWith("## ")).map((l) => normalizeHeading2(l.slice(3).trim())).filter((s) => s.length > 0);
25491
- }
25492
- var KIT_TEMPLATE_FALLBACK_DIRS = [
25493
- // Packaged with the npm bundle (dist/templates) — resolves from node_modules.
25494
- "templates",
25495
- // Repo checkout layouts.
25496
- "docs/refs/templates"
25497
- ];
25498
- var _moduleDir = (() => {
25499
- try {
25500
- if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && typeof (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) === "string") {
25501
- return path3__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
25502
- }
25503
- } catch {
25504
- }
25505
- return typeof __dirname !== "undefined" ? __dirname : process.cwd();
25506
- })();
25507
- function candidateDirs(explicitDir) {
25508
- const dirs = [];
25509
- if (explicitDir) dirs.push(explicitDir);
25510
- if (process.env.CURRICULUM_TEMPLATES_DIR) dirs.push(process.env.CURRICULUM_TEMPLATES_DIR);
25511
- dirs.push(path3__default.default.resolve(process.cwd(), "_templates"));
25512
- for (const rel of KIT_TEMPLATE_FALLBACK_DIRS) {
25513
- dirs.push(path3__default.default.resolve(_moduleDir, rel));
25514
- dirs.push(path3__default.default.resolve(process.cwd(), rel));
25515
- dirs.push(path3__default.default.resolve(process.cwd(), "packages/curriculum-kit", rel));
25516
- }
25517
- return dirs;
25518
- }
25519
- function findTemplatePath(templateName, explicitDir) {
25520
- for (const dir of candidateDirs(explicitDir)) {
25521
- const p = path3__default.default.join(dir, templateName);
25522
- if (!fs2__default.default.existsSync(p)) continue;
25523
- try {
25524
- fs2__default.default.accessSync(p, fs2__default.default.constants.R_OK);
25525
- return p;
25526
- } catch {
25527
- }
25528
- }
25529
- return null;
25530
- }
25531
- function resolveArtifactTemplate(templateName, opts) {
25532
- const p = findTemplatePath(templateName, opts?.templatesDir);
25533
- if (!p) return null;
25534
- try {
25535
- const raw = fs2__default.default.readFileSync(p, "utf-8");
25536
- return {
25537
- content: stripTemplateFrontmatter(raw),
25538
- sections: extractRequiredSections(raw) ?? extractBodySections(raw),
25539
- sourcePath: p
25540
- };
25541
- } catch {
25542
- return null;
25543
- }
25544
- }
25545
- function loadCurriculumTemplate(templateName, templatesDir) {
25546
- const p = findTemplatePath(templateName, templatesDir);
25547
- if (!p) return "";
25548
- try {
25549
- return fs2__default.default.readFileSync(p, "utf-8");
25550
- } catch {
25551
- return "";
25552
- }
25553
- }
25554
25193
 
25555
25194
  // src/services/lessonProductionService.ts
25195
+ init_templateLoader();
25556
25196
  var LESSON_TEMPLATE_BY_PEDAGOGY = {
25557
25197
  edp: "LESSON_EDP_template.md",
25558
25198
  general: "LESSON_GENERAL_template.md",
@@ -29488,14 +29128,15 @@ function isTranslationDue(target, context) {
29488
29128
  }
29489
29129
 
29490
29130
  // src/services/index.ts
29131
+ init_templateLoader();
29491
29132
  init_slideProductionWorkflow();
29492
29133
  var LocalWorkspaceManager = class {
29493
29134
  baseDir;
29494
29135
  constructor(baseDir) {
29495
- this.baseDir = baseDir || path3__default.default.resolve(process.cwd(), "output", "workspace-jobs");
29136
+ this.baseDir = baseDir || path4__default.default.resolve(process.cwd(), "output", "workspace-jobs");
29496
29137
  }
29497
29138
  getJobDirectory(jobId) {
29498
- return path3__default.default.join(this.baseDir, jobId);
29139
+ return path4__default.default.join(this.baseDir, jobId);
29499
29140
  }
29500
29141
  async initJobWorkspace(jobId, initialInfo) {
29501
29142
  const jobDir = this.getJobDirectory(jobId);
@@ -29518,9 +29159,9 @@ var LocalWorkspaceManager = class {
29518
29159
  }
29519
29160
  async saveArtifact(jobId, milestoneSlug, filename, content, artifactType) {
29520
29161
  const jobDir = this.getJobDirectory(jobId);
29521
- const targetDir = path3__default.default.join(jobDir, milestoneSlug);
29162
+ const targetDir = path4__default.default.join(jobDir, milestoneSlug);
29522
29163
  await fsPromises__default.default.mkdir(targetDir, { recursive: true });
29523
- const filePath = path3__default.default.join(targetDir, filename);
29164
+ const filePath = path4__default.default.join(targetDir, filename);
29524
29165
  await fsPromises__default.default.writeFile(filePath, content, "utf-8");
29525
29166
  const stat = await fsPromises__default.default.stat(filePath);
29526
29167
  const manifest = await this.getManifest(jobId);
@@ -29528,7 +29169,7 @@ var LocalWorkspaceManager = class {
29528
29169
  manifest.artifacts.push({
29529
29170
  milestoneId: milestoneSlug,
29530
29171
  artifactType,
29531
- filePath: path3__default.default.relative(jobDir, filePath),
29172
+ filePath: path4__default.default.relative(jobDir, filePath),
29532
29173
  sizeBytes: stat.size,
29533
29174
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
29534
29175
  });
@@ -29538,7 +29179,7 @@ var LocalWorkspaceManager = class {
29538
29179
  return filePath;
29539
29180
  }
29540
29181
  async getManifest(jobId) {
29541
- const manifestPath = path3__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29182
+ const manifestPath = path4__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29542
29183
  try {
29543
29184
  const data = await fsPromises__default.default.readFile(manifestPath, "utf-8");
29544
29185
  return JSON.parse(data);
@@ -29547,7 +29188,7 @@ var LocalWorkspaceManager = class {
29547
29188
  }
29548
29189
  }
29549
29190
  async saveManifest(jobId, manifest) {
29550
- const manifestPath = path3__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29191
+ const manifestPath = path4__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29551
29192
  manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
29552
29193
  await fsPromises__default.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
29553
29194
  }
@@ -29588,12 +29229,12 @@ async function publishToGitHub(options) {
29588
29229
  async function scanDir(currentDir) {
29589
29230
  const entries = await fsPromises__default.default.readdir(currentDir, { withFileTypes: true });
29590
29231
  for (const entry of entries) {
29591
- const fullPath = path3__default.default.join(currentDir, entry.name);
29232
+ const fullPath = path4__default.default.join(currentDir, entry.name);
29592
29233
  if (entry.isDirectory()) {
29593
29234
  await scanDir(fullPath);
29594
29235
  } else if (entry.isFile() && !entry.name.startsWith(".")) {
29595
29236
  const content = await fsPromises__default.default.readFile(fullPath, "utf-8");
29596
- const relPath = path3__default.default.relative(options.localJobDir, fullPath);
29237
+ const relPath = path4__default.default.relative(options.localJobDir, fullPath);
29597
29238
  filesToUpload.push({ path: relPath, content });
29598
29239
  }
29599
29240
  }
@@ -31699,13 +31340,13 @@ var MediaLedger = class {
31699
31340
  filePath;
31700
31341
  entries = /* @__PURE__ */ new Map();
31701
31342
  constructor(projectDir, lessonId) {
31702
- this.filePath = path3__default.default.join(projectDir, "_media", `ledger-${lessonId.toUpperCase()}.jsonl`);
31343
+ this.filePath = path4__default.default.join(projectDir, "_media", `ledger-${lessonId.toUpperCase()}.jsonl`);
31703
31344
  this.load();
31704
31345
  }
31705
31346
  load() {
31706
31347
  this.entries.clear();
31707
- if (!fs2__default.default.existsSync(this.filePath)) return;
31708
- const lines = fs2__default.default.readFileSync(this.filePath, "utf-8").split("\n");
31348
+ if (!fs3__default.default.existsSync(this.filePath)) return;
31349
+ const lines = fs3__default.default.readFileSync(this.filePath, "utf-8").split("\n");
31709
31350
  for (const line of lines) {
31710
31351
  const trimmed = line.trim();
31711
31352
  if (!trimmed) continue;
@@ -31717,8 +31358,8 @@ var MediaLedger = class {
31717
31358
  }
31718
31359
  }
31719
31360
  persist() {
31720
- fs2__default.default.mkdirSync(path3__default.default.dirname(this.filePath), { recursive: true });
31721
- fs2__default.default.writeFileSync(this.filePath, Array.from(this.entries.values()).map((e) => JSON.stringify(e)).join("\n") + "\n", "utf-8");
31361
+ fs3__default.default.mkdirSync(path4__default.default.dirname(this.filePath), { recursive: true });
31362
+ fs3__default.default.writeFileSync(this.filePath, Array.from(this.entries.values()).map((e) => JSON.stringify(e)).join("\n") + "\n", "utf-8");
31722
31363
  }
31723
31364
  /** Agent tạo artifact ghi entry mới (hoặc cập nhật nếu slug đã có và chưa done) */
31724
31365
  upsert(input) {
@@ -31900,8 +31541,8 @@ async function fulfillMediaLedger(options) {
31900
31541
  const maxRetries = options.maxRetries ?? 2;
31901
31542
  const approved = ledger.listByStatus("approved");
31902
31543
  const result = { done: [], failed: [], skipped: [] };
31903
- const mediaDir = path3__default.default.join(projectDir, "_media");
31904
- fs2__default.default.mkdirSync(mediaDir, { recursive: true });
31544
+ const mediaDir = path4__default.default.join(projectDir, "_media");
31545
+ fs3__default.default.mkdirSync(mediaDir, { recursive: true });
31905
31546
  const mode = policy.mode ?? "auto";
31906
31547
  const canSearch = mode === "search_only" || mode === "auto";
31907
31548
  const canGen = mode === "gen_only" || mode === "auto";
@@ -31983,7 +31624,7 @@ async function fulfillMediaLedger(options) {
31983
31624
  });
31984
31625
  const ext = image.mimeType === "image/jpeg" ? "jpg" : "png";
31985
31626
  const filename = `${entry.slug}-${Date.now()}.${ext}`;
31986
- fs2__default.default.writeFileSync(path3__default.default.join(mediaDir, filename), Buffer.from(image.base64, "base64"));
31627
+ fs3__default.default.writeFileSync(path4__default.default.join(mediaDir, filename), Buffer.from(image.base64, "base64"));
31987
31628
  ledger.setStatus(entry.slug, "done", { relPath: `_media/${filename}`, provider: image.provider });
31988
31629
  result.done.push(entry.slug);
31989
31630
  options.onProgress?.(entry.slug, "done", `source: ${image.provider}`);
@@ -32014,7 +31655,6 @@ function renderMediaPlaceholder(entry) {
32014
31655
  return `> \u{1F5BC}\uFE0F [\u1EA2nh \u0111ang ch\u1EDD: ${entry.name} \u2014 s\u1EBD \u0111\u01B0\u1EE3c t\u1EA1o sau khi duy\u1EC7t media]`;
32015
31656
  }
32016
31657
 
32017
- exports.ACT_TEMPLATE = ACT_TEMPLATE;
32018
31658
  exports.ARTIFACT_DEPENDENCY_ROUTING = ARTIFACT_DEPENDENCY_ROUTING;
32019
31659
  exports.ARTIFACT_EXECUTION_ORDER = ARTIFACT_EXECUTION_ORDER;
32020
31660
  exports.AcademicAuditor = AcademicAuditor;
@@ -32036,7 +31676,6 @@ exports.BloomLevelSchema = BloomLevelSchema;
32036
31676
  exports.BloomTaxonomyEvaluator = BloomTaxonomyEvaluator;
32037
31677
  exports.BronzeTierSchema = BronzeTierSchema;
32038
31678
  exports.CERConclusionSchema = CERConclusionSchema;
32039
- exports.CODE_LAB_TEMPLATE = CODE_LAB_TEMPLATE;
32040
31679
  exports.CURRICULUM_SLASH_COMMANDS = CURRICULUM_SLASH_COMMANDS;
32041
31680
  exports.CardTierEnum = CardTierEnum;
32042
31681
  exports.ChoiceBoardRubricRowSchema = ChoiceBoardRubricRowSchema;
@@ -32066,7 +31705,6 @@ exports.DEFAULT_LESSON_PRIORITIES = DEFAULT_LESSON_PRIORITIES;
32066
31705
  exports.DEFAULT_OVERHEAD_RATIO = DEFAULT_OVERHEAD_RATIO;
32067
31706
  exports.DEFAULT_STREAM_IDLE_MS = DEFAULT_STREAM_IDLE_MS;
32068
31707
  exports.DEFAULT_STREAM_TOTAL_MS = DEFAULT_STREAM_TOTAL_MS;
32069
- exports.DEFAULT_VIETNAMESE_SECTION_HEADINGS = DEFAULT_VIETNAMESE_SECTION_HEADINGS;
32070
31708
  exports.DependencyEdgeSchema = DependencyEdgeSchema;
32071
31709
  exports.DepthAssignmentSchema = DepthAssignmentSchema;
32072
31710
  exports.DepthLevelSchema = DepthLevelSchema;
@@ -32111,7 +31749,6 @@ exports.JUDGE_ESCALATE_THRESHOLD = JUDGE_ESCALATE_THRESHOLD;
32111
31749
  exports.JudgeCriterionSchema = JudgeCriterionSchema;
32112
31750
  exports.LAYER_IDLE_BUDGET_MS = LAYER_IDLE_BUDGET_MS;
32113
31751
  exports.LAYER_TOTAL_BUDGET_MS = LAYER_TOTAL_BUDGET_MS;
32114
- exports.LESSON_PLAN_TEMPLATE = LESSON_PLAN_TEMPLATE;
32115
31752
  exports.LLMJudgeEngine = LLMJudgeEngine;
32116
31753
  exports.LabTierTaskSchema = LabTierTaskSchema;
32117
31754
  exports.LearningObjectiveInputSchema = LearningObjectiveInputSchema;
@@ -32153,7 +31790,6 @@ exports.ProjectGraphAuditSchema = ProjectGraphAuditSchema;
32153
31790
  exports.ProjectGraphSchema = ProjectGraphSchema;
32154
31791
  exports.ProjectInstructionSchema = ProjectInstructionSchema;
32155
31792
  exports.ProjectProfileSchema = ProjectProfileSchema;
32156
- exports.QUIZ_TEMPLATE = QUIZ_TEMPLATE;
32157
31793
  exports.QualityAuditVerdictSchema = QualityAuditVerdictSchema;
32158
31794
  exports.QuizOptionSchema = QuizOptionSchema;
32159
31795
  exports.RecordProvenanceSchema = RecordProvenanceSchema;
@@ -32166,7 +31802,6 @@ exports.RotationStationSchema = RotationStationSchema;
32166
31802
  exports.RubricCriteriaSchema = RubricCriteriaSchema;
32167
31803
  exports.RubricSchema = RubricSchema;
32168
31804
  exports.SATELLITE_LESSON_PRIORITIES = SATELLITE_LESSON_PRIORITIES;
32169
- exports.SELF_LAB_TEMPLATE = SELF_LAB_TEMPLATE;
32170
31805
  exports.SLIDE_CANONICAL_METHODOLOGY = SLIDE_CANONICAL_METHODOLOGY;
32171
31806
  exports.STANDARD_REF_REGEX = STANDARD_REF_REGEX;
32172
31807
  exports.STANDARD_SOT_FILES = STANDARD_SOT_FILES;
@@ -32209,6 +31844,7 @@ exports.ZpdVerdictSchema = ZpdVerdictSchema;
32209
31844
  exports.activityTools = activityTools;
32210
31845
  exports.analystTools = analystTools;
32211
31846
  exports.analyzeProjectCreationIntent = analyzeProjectCreationIntent;
31847
+ exports.assembleTranslatedTemplate = assembleTranslatedTemplate;
32212
31848
  exports.assertAcyclic = assertAcyclic;
32213
31849
  exports.assessorTools = assessorTools;
32214
31850
  exports.assignDepths = assignDepths;
@@ -32285,8 +31921,10 @@ exports.exportAllProjectQuizzes = exportAllProjectQuizzes;
32285
31921
  exports.expositionCacheKey = expositionCacheKey;
32286
31922
  exports.extractActivityContractRows = extractActivityContractRows;
32287
31923
  exports.extractAssessmentMap = extractAssessmentMap;
32288
- exports.extractBodySections = extractBodySections;
31924
+ exports.extractConditionalSections = extractConditionalSections;
32289
31925
  exports.extractCurriculumHorizon = extractCurriculumHorizon;
31926
+ exports.extractDeclaredLanguage = extractDeclaredLanguage;
31927
+ exports.extractEchoSections = extractEchoSections;
32290
31928
  exports.extractJsonArray = extractJsonArray;
32291
31929
  exports.extractRequiredSections = extractRequiredSections;
32292
31930
  exports.extractScopeSequenceRows = extractScopeSequenceRows;
@@ -32342,6 +31980,7 @@ exports.isTranslationDue = isTranslationDue;
32342
31980
  exports.lintAndSanitizeArtifact = lintAndSanitizeArtifact;
32343
31981
  exports.lintCurriculumFramework = lintCurriculumFramework;
32344
31982
  exports.lintFrameworkPack = lintFrameworkPack;
31983
+ exports.loadAuthoringTemplate = loadAuthoringTemplate;
32345
31984
  exports.loadCurriculumTemplate = loadCurriculumTemplate;
32346
31985
  exports.loadSotTemplate = loadSotTemplate;
32347
31986
  exports.normalizePlanningGraph = normalizePlanningGraph;
@@ -32390,11 +32029,13 @@ exports.streamLLMWithFallback = streamLLMWithFallback;
32390
32029
  exports.streamLayerPrefillWithFallback = streamLayerPrefillWithFallback;
32391
32030
  exports.streamLessonMasterFlow = streamLessonMasterFlow;
32392
32031
  exports.streamSelfLabFlow = streamSelfLabFlow;
32032
+ exports.stripHeadingKey = stripHeadingKey;
32393
32033
  exports.stripLlmJsonWrappers = stripLlmJsonWrappers;
32394
32034
  exports.stripTemplateFrontmatter = stripTemplateFrontmatter;
32395
32035
  exports.targetLanguageDisplayName = targetLanguageDisplayName;
32396
32036
  exports.techSmeTools = techSmeTools;
32397
32037
  exports.topoSort = topoSort;
32038
+ exports.translateTemplate = translateTemplate;
32398
32039
  exports.uploadAssetToBucket = uploadAssetToBucket;
32399
32040
  exports.validateArtifactDependencies = validateArtifactDependencies;
32400
32041
  exports.validateCurriculumPlan = validateCurriculumPlan;