@thanh01.pmt/curriculum-kit 1.4.8 → 1.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -540,6 +540,7 @@ declare function buildSessionSliceContext(plan: CurriculumPlan, lessonCode: stri
540
540
  interface ProjectionMeta {
541
541
  courseName: string;
542
542
  shortDescription?: string;
543
+ targetLanguage?: string;
543
544
  }
544
545
  declare function renderFrameworkFromPlan(plan: CurriculumPlan, meta: ProjectionMeta): string;
545
546
  /** Extract the glossary scope (terms per session) the EXPOSITION service consumes. */
package/dist/index.d.ts CHANGED
@@ -540,6 +540,7 @@ declare function buildSessionSliceContext(plan: CurriculumPlan, lessonCode: stri
540
540
  interface ProjectionMeta {
541
541
  courseName: string;
542
542
  shortDescription?: string;
543
+ targetLanguage?: string;
543
544
  }
544
545
  declare function renderFrameworkFromPlan(plan: CurriculumPlan, meta: ProjectionMeta): string;
545
546
  /** Extract the glossary scope (terms per session) the EXPOSITION service consumes. */
package/dist/index.mjs CHANGED
@@ -8406,13 +8406,16 @@ function computeContentHash(content) {
8406
8406
  return "sha256:" + crypto.createHash("sha256").update(content.trim(), "utf-8").digest("hex");
8407
8407
  }
8408
8408
  var STANDARD_SOT_FILES = [
8409
- "PROJECT_BRIEF.md",
8410
8409
  "LEARNER_PROFILE.md",
8410
+ "PROJECT_BRIEF.md",
8411
+ "REFERENCE_PACK.md",
8412
+ "PROJECT_GRAPH.json",
8413
+ "HYBRID_GRAPH.json",
8414
+ "SECTION_LANGUAGE_CONTRACT.md",
8411
8415
  "CURRICULUM_FRAMEWORK.md",
8412
- "PROJECT_STATUS.md",
8413
8416
  "CONTENT_STYLE_GUIDE.md",
8414
8417
  "ART_DIRECTION.md",
8415
- "REFERENCE_PACK.md"
8418
+ "ALIGNMENT_MATRIX.md"
8416
8419
  ];
8417
8420
  var FileSystemCurriculumAdapter = class {
8418
8421
  baseDir;
@@ -8454,19 +8457,33 @@ var FileSystemCurriculumAdapter = class {
8454
8457
  async listSotDocuments(projectId) {
8455
8458
  const projectDir = this.getProjectDir(projectId);
8456
8459
  const sotDir = path3.join(projectDir, "_sot");
8457
- return STANDARD_SOT_FILES.map((filename) => {
8460
+ const discoveredFiles = new Set(STANDARD_SOT_FILES);
8461
+ if (fs2.existsSync(sotDir)) {
8462
+ try {
8463
+ const diskFiles = fs2.readdirSync(sotDir);
8464
+ for (const f of diskFiles) {
8465
+ if (f.startsWith(".") || f.endsWith(".review.json")) continue;
8466
+ const fullPath = path3.join(sotDir, f);
8467
+ if (fs2.statSync(fullPath).isFile()) {
8468
+ discoveredFiles.add(f);
8469
+ }
8470
+ }
8471
+ } catch {
8472
+ }
8473
+ }
8474
+ return Array.from(discoveredFiles).map((filename) => {
8458
8475
  const p = fs2.existsSync(path3.join(sotDir, filename)) ? path3.join(sotDir, filename) : fs2.existsSync(path3.join(projectDir, filename)) ? path3.join(projectDir, filename) : null;
8459
8476
  if (p && fs2.existsSync(p)) {
8460
8477
  const stats = fs2.statSync(p);
8461
8478
  return {
8462
- name: filename.replace(".md", "").replace(/_/g, " "),
8479
+ name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
8463
8480
  filename,
8464
8481
  exists: true,
8465
8482
  sizeBytes: stats.size
8466
8483
  };
8467
8484
  }
8468
8485
  return {
8469
- name: filename.replace(".md", "").replace(/_/g, " "),
8486
+ name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
8470
8487
  filename,
8471
8488
  exists: false,
8472
8489
  sizeBytes: 0
@@ -8712,11 +8729,23 @@ var SupabaseCurriculumAdapter = class {
8712
8729
  return null;
8713
8730
  }
8714
8731
  async listSotDocuments(projectId) {
8732
+ const discoveredFiles = new Set(STANDARD_SOT_FILES);
8733
+ try {
8734
+ const { data, error } = await this.client.storage.from(this.bucketName).list(`${projectId}/_sot`);
8735
+ if (!error && data) {
8736
+ for (const item of data) {
8737
+ if (item.name && !item.name.startsWith(".") && !item.name.endsWith(".review.json")) {
8738
+ discoveredFiles.add(item.name);
8739
+ }
8740
+ }
8741
+ }
8742
+ } catch {
8743
+ }
8715
8744
  const results = [];
8716
- for (const filename of STANDARD_SOT_FILES) {
8745
+ for (const filename of discoveredFiles) {
8717
8746
  const content = await this.readSotDocument(projectId, filename);
8718
8747
  results.push({
8719
- name: filename.replace(".md", "").replace(/_/g, " "),
8748
+ name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
8720
8749
  filename,
8721
8750
  exists: !!content,
8722
8751
  content: content || void 0,
@@ -9093,10 +9122,12 @@ async function auditQualityReport(storage, projectId) {
9093
9122
  computedScore += Math.round(bloomPassCount / total * 20);
9094
9123
  computedScore += Math.round(Math.min(syntaxExecutableCount, total) / total * 20);
9095
9124
  computedScore += Math.round(scopeCompletenessCount / total * 20);
9125
+ const sotCompletedCount = status.sotReadiness.filter((s) => s.exists).length;
9126
+ const sotTotalCount = status.sotReadiness.length;
9096
9127
  if (sotReady) {
9097
- details.push("\u2705 N\u1EC1n t\u1EA3ng SOT (7/7 t\xE0i li\u1EC7u) s\u1EB5n s\xE0ng.");
9128
+ details.push(`\u2705 N\u1EC1n t\u1EA3ng SOT (${sotCompletedCount}/${sotTotalCount} t\xE0i li\u1EC7u) s\u1EB5n s\xE0ng.`);
9098
9129
  } else {
9099
- details.push("\u26A0\uFE0F C\u1EA7n ho\xE0n thi\u1EC7n \u0111\u1EA7y \u0111\u1EE7 t\xE0i li\u1EC7u SOT c\u01A1 s\u1EDF.");
9130
+ details.push(`\u26A0\uFE0F C\u1EA7n ho\xE0n thi\u1EC7n \u0111\u1EA7y \u0111\u1EE7 t\xE0i li\u1EC7u SOT c\u01A1 s\u1EDF (${sotCompletedCount}/${sotTotalCount}).`);
9100
9131
  }
9101
9132
  details.push(`\u{1F3AF} Ph\u1EA1m vi h\u1ECDc li\u1EC7u theo SOT (Artifact Scope): [${artifactScope.join(", ")}].`);
9102
9133
  if (pedagogy5EPass) {
@@ -9112,7 +9143,7 @@ async function auditQualityReport(storage, projectId) {
9112
9143
  details.push(`\u2705 \u0110\u1ED9 ph\u1EE7 \u0111\u1EA7y \u0111\u1EE7 c\xE1c h\u1ECDc li\u1EC7u trong Artifact Scope: ${scopeCompletenessCount}/${total} b\xE0i h\u1ECDc.`);
9113
9144
  const rawMarkdownReport = `## \u{1F6E1}\uFE0F B\xC1O C\xC1O KI\u1EC2M \u0110\u1ECANH CH\u1EA4T L\u01AF\u1EE2NG (QUALITY AUDIT): \`${projectId.toUpperCase()}\`
9114
9145
  - **\u0110i\u1EC3m th\u1EA9m \u0111\u1ECBnh ch\u1EA5t l\u01B0\u1EE3ng:** **${computedScore}/100**
9115
- - **Tr\u1EA1ng th\xE1i SOT:** ${sotReady ? "\u2705 7/7 File SOT ho\xE0n t\u1EA5t" : "\u26A0\uFE0F C\u1EA7n b\u1ED5 sung t\xE0i li\u1EC7u SOT thi\u1EBFu"}.
9146
+ - **Tr\u1EA1ng th\xE1i SOT:** ${sotReady ? `\u2705 ${sotCompletedCount}/${sotTotalCount} File SOT ho\xE0n t\u1EA5t` : `\u26A0\uFE0F C\u1EA7n b\u1ED5 sung t\xE0i li\u1EC7u SOT thi\u1EBFu (${sotCompletedCount}/${sotTotalCount})`}.
9116
9147
  - **Ph\u1EA1m vi h\u1ECDc li\u1EC7u (SOT Artifact Scope):** \`${artifactScope.join(", ")}\`
9117
9148
  - **C\u1EA5u tr\xFAc S\u01B0 ph\u1EA1m (5E / EDP):** ${pedagogy5ECount}/${total} b\xE0i h\u1ECDc \u0111\u1EA1t chu\u1EA9n ph\xE2n pha.
9118
9149
  - **Thang \u0111o nh\u1EADn th\u1EE9c Bloom:** ${bloomPassCount}/${total} b\xE0i h\u1ECDc \u0111\u1EA1t ma tr\u1EADn m\u1EE5c ti\xEAu.
@@ -9214,7 +9245,9 @@ function extractScopeSequenceRows(framework) {
9214
9245
  const trimmed = line.trim();
9215
9246
  if (!trimmed.startsWith("|")) continue;
9216
9247
  const lower = trimmed.toLowerCase();
9217
- if (lower.includes("lesson code") && lower.includes("learning objective")) {
9248
+ const hasCodeCol = lower.includes("lesson code") || lower.includes("m\xE3 b\xE0i") || lower.includes("m\xE3 b\xE0i h\u1ECDc");
9249
+ const hasObjCol = lower.includes("learning objective") || lower.includes("m\u1EE5c ti\xEAu") || lower.includes("m\u1EE5c ti\xEAu h\u1ECDc t\u1EADp");
9250
+ if (hasCodeCol && hasObjCol) {
9218
9251
  headerFound = true;
9219
9252
  continue;
9220
9253
  }
@@ -27859,6 +27892,7 @@ var pad22 = (n) => String(n).padStart(2, "0");
27859
27892
  var esc = (s) => s.replace(/\|/g, "/").replace(/\n/g, " ").trim();
27860
27893
  var BLOOM_OF_DEPTH = { ulo: "Understand", cio: "Apply", sio: "Create" };
27861
27894
  function renderFrameworkFromPlan(plan, meta) {
27895
+ const isVi = meta.targetLanguage === "vi";
27862
27896
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27863
27897
  const totalSessions = plan.sessions.length;
27864
27898
  const totalMinutes = plan.sessions.reduce(
@@ -27876,10 +27910,16 @@ function renderFrameworkFromPlan(plan, meta) {
27876
27910
  const depthTops = [...new Set(s.depth_assignments.map((d) => d.depth))].map((d) => BLOOM_OF_DEPTH[d] ?? "Apply");
27877
27911
  const bloom = depthTops[depthTops.length - 1] ?? "Apply";
27878
27912
  const keyConcepts = s.new_keywords.length > 0 ? s.new_keywords.join(", ") : s.depth_assignments.map((d) => d.node_id).join(", ");
27879
- const prereq = s.prerequisite_decisions.length > 0 ? s.prerequisite_decisions.filter((p) => p.decision === "taught_in_prior_lesson").length + " prior-taught, " + s.prerequisite_decisions.filter((p) => p.decision === "recap_in_lesson").length + " recap" : "None";
27913
+ const prereq = s.prerequisite_decisions.length > 0 ? s.prerequisite_decisions.filter((p) => p.decision === "taught_in_prior_lesson").length + " prior-taught, " + s.prerequisite_decisions.filter((p) => p.decision === "recap_in_lesson").length + " recap" : isVi ? "Kh\xF4ng" : "None";
27880
27914
  const duration = s.knowledge_minutes + s.practice_minutes + s.overhead_minutes;
27881
27915
  return "| " + pad22(i + 1) + " | " + s.id + " | " + esc(s.title) + " | " + s.unit_id + " | " + s.unit_id + "_M01 | " + esc(s.prose_objective) + " | " + esc(keyConcepts) + " | " + esc(s.exit_evidence[0] ?? "") + " | " + esc(prereq) + " | " + bloom + " | " + duration + " |";
27882
27916
  }).join("\n");
27917
+ const courseOverviewHeader = isVi ? "## [REQUIRED] T\u1ED5ng quan Kh\xF3a h\u1ECDc" : "## [REQUIRED] Course Overview";
27918
+ const globalObjectivesHeader = isVi ? "## [REQUIRED] M\u1EE5c ti\xEAu H\u1ECDc t\u1EADp To\xE0n di\u1EC7n" : "## [REQUIRED] Global Learning Objectives";
27919
+ const structuralHierarchyHeader = isVi ? "## [REQUIRED] C\u1EA5u tr\xFAc Kh\xF3a h\u1ECDc (Unit & Module)" : "## [REQUIRED] Structural Hierarchy (Unit & Module)";
27920
+ const scopeSequenceHeader = isVi ? "## [REQUIRED] Ph\u1EA1m vi & Tr\xECnh t\u1EF1 Chi ti\u1EBFt (Scope & Sequence)" : "## [REQUIRED] Scope & Sequence (Detailed Roadmap)";
27921
+ const standardFormatHeader = isVi ? "## [REQUIRED] C\u1EA5u tr\xFAc B\xE0i h\u1ECDc Chu\u1EA9n" : "## [REQUIRED] Standard Lesson Format";
27922
+ const scopeTableHeader = isVi ? "| # | Lesson Code | Title | Unit | Module | Learning Objective (H\u1ECDc sinh l\xE0m \u0111\u01B0\u1EE3c g\xEC...) | Key Concepts (H\u1ECDc g\xEC) | Hands-on Deliverable (S\u1EA3n ph\u1EA9m bu\u1ED5i h\u1ECDc) | Prerequisites | Bloom | Duration (mins) |" : "| # | Lesson Code | Title | Unit | Module | Learning Objective (Students will be able to...) | Key Concepts (Hoc gi) | Hands-on Deliverable (Lam duoc gi) | Prerequisites | Bloom | Duration (mins) |";
27883
27923
  return [
27884
27924
  "---",
27885
27925
  'id: "CURRICULUM-FRAMEWORK"',
@@ -27901,7 +27941,7 @@ function renderFrameworkFromPlan(plan, meta) {
27901
27941
  "",
27902
27942
  "_Projection of CURRICULUM_PLAN (hash " + plan.plan_hash + "). Approving this framework approves the per-session SCOPE recorded in the plan._",
27903
27943
  "",
27904
- "## [REQUIRED] Course Overview",
27944
+ courseOverviewHeader,
27905
27945
  "- **Official Course Name:** " + esc(meta.courseName),
27906
27946
  meta.shortDescription ? "- **Short Description:** " + esc(meta.shortDescription) : "",
27907
27947
  "- **Total Units:** " + plan.units.length,
@@ -27909,20 +27949,20 @@ function renderFrameworkFromPlan(plan, meta) {
27909
27949
  "- **Total Duration:** ~" + totalHours + " hours (" + totalSessions + " sessions x " + plan.constraints.session_duration_minutes + " min).",
27910
27950
  "- **Entry Level:** " + plan.constraints.entry_level + " (age band " + plan.constraints.age_band[0] + "-" + plan.constraints.age_band[1] + ").",
27911
27951
  "",
27912
- "## [REQUIRED] Global Learning Objectives",
27952
+ globalObjectivesHeader,
27913
27953
  ...plan.course.objectives.map((o, i) => i + 1 + ". **[" + o.bloom + "]:** " + esc(o.statement)),
27914
27954
  "",
27915
- "## [REQUIRED] Structural Hierarchy (Unit & Module)",
27955
+ structuralHierarchyHeader,
27916
27956
  "| Unit | Module | Lessons | Module Objective | Lesson Codes |",
27917
27957
  "|---|---|:---:|---|---|",
27918
27958
  hierarchyRows || "| (none) | | | | |",
27919
27959
  "",
27920
- "## [REQUIRED] Scope & Sequence (Detailed Roadmap)",
27921
- "| # | Lesson Code | Title | Unit | Module | Learning Objective (Students will be able to...) | Key Concepts (Hoc gi) | Hands-on Deliverable (Lam duoc gi) | Prerequisites | Bloom | Duration (mins) |",
27960
+ scopeSequenceHeader,
27961
+ scopeTableHeader,
27922
27962
  "|:---:|:---:|---|---|---|---|---|---|---|:---:|:---:|",
27923
27963
  scopeRows || "| 01 | U01_M01_L01 | (empty plan) | U01 | U01_M01 | - | - | - | None | Understand | " + plan.constraints.session_duration_minutes + " |",
27924
27964
  "",
27925
- "## [REQUIRED] Standard Lesson Format",
27965
+ standardFormatHeader,
27926
27966
  "- **Session duration:** " + plan.constraints.session_duration_minutes + " minutes (overhead " + String(plan.sessions[0]?.overhead_minutes ?? 0) + " min).",
27927
27967
  "- **Knowledge/practice split per session:** see plan JSON (knowledge_minutes / practice_minutes).",
27928
27968
  "- **Pedagogical flow:** 5E (Engage / Explore / Explain / Elaborate / Evaluate) unless policy overrides.",