@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/LICENSE +21 -0
- package/dist/index.cjs +59 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +59 -19
- package/dist/index.mjs.map +1 -1
- package/dist/storage/index.cjs +37 -8
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.mjs +37 -8
- package/dist/storage/index.mjs.map +1 -1
- package/package.json +21 -22
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tony Pham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -8417,13 +8417,16 @@ function computeContentHash(content) {
|
|
|
8417
8417
|
return "sha256:" + crypto__default.default.createHash("sha256").update(content.trim(), "utf-8").digest("hex");
|
|
8418
8418
|
}
|
|
8419
8419
|
var STANDARD_SOT_FILES = [
|
|
8420
|
-
"PROJECT_BRIEF.md",
|
|
8421
8420
|
"LEARNER_PROFILE.md",
|
|
8421
|
+
"PROJECT_BRIEF.md",
|
|
8422
|
+
"REFERENCE_PACK.md",
|
|
8423
|
+
"PROJECT_GRAPH.json",
|
|
8424
|
+
"HYBRID_GRAPH.json",
|
|
8425
|
+
"SECTION_LANGUAGE_CONTRACT.md",
|
|
8422
8426
|
"CURRICULUM_FRAMEWORK.md",
|
|
8423
|
-
"PROJECT_STATUS.md",
|
|
8424
8427
|
"CONTENT_STYLE_GUIDE.md",
|
|
8425
8428
|
"ART_DIRECTION.md",
|
|
8426
|
-
"
|
|
8429
|
+
"ALIGNMENT_MATRIX.md"
|
|
8427
8430
|
];
|
|
8428
8431
|
var FileSystemCurriculumAdapter = class {
|
|
8429
8432
|
baseDir;
|
|
@@ -8465,19 +8468,33 @@ var FileSystemCurriculumAdapter = class {
|
|
|
8465
8468
|
async listSotDocuments(projectId) {
|
|
8466
8469
|
const projectDir = this.getProjectDir(projectId);
|
|
8467
8470
|
const sotDir = path3__default.default.join(projectDir, "_sot");
|
|
8468
|
-
|
|
8471
|
+
const discoveredFiles = new Set(STANDARD_SOT_FILES);
|
|
8472
|
+
if (fs2__default.default.existsSync(sotDir)) {
|
|
8473
|
+
try {
|
|
8474
|
+
const diskFiles = fs2__default.default.readdirSync(sotDir);
|
|
8475
|
+
for (const f of diskFiles) {
|
|
8476
|
+
if (f.startsWith(".") || f.endsWith(".review.json")) continue;
|
|
8477
|
+
const fullPath = path3__default.default.join(sotDir, f);
|
|
8478
|
+
if (fs2__default.default.statSync(fullPath).isFile()) {
|
|
8479
|
+
discoveredFiles.add(f);
|
|
8480
|
+
}
|
|
8481
|
+
}
|
|
8482
|
+
} catch {
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
return Array.from(discoveredFiles).map((filename) => {
|
|
8469
8486
|
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;
|
|
8470
8487
|
if (p && fs2__default.default.existsSync(p)) {
|
|
8471
8488
|
const stats = fs2__default.default.statSync(p);
|
|
8472
8489
|
return {
|
|
8473
|
-
name: filename.replace(
|
|
8490
|
+
name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
|
|
8474
8491
|
filename,
|
|
8475
8492
|
exists: true,
|
|
8476
8493
|
sizeBytes: stats.size
|
|
8477
8494
|
};
|
|
8478
8495
|
}
|
|
8479
8496
|
return {
|
|
8480
|
-
name: filename.replace(
|
|
8497
|
+
name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
|
|
8481
8498
|
filename,
|
|
8482
8499
|
exists: false,
|
|
8483
8500
|
sizeBytes: 0
|
|
@@ -8723,11 +8740,23 @@ var SupabaseCurriculumAdapter = class {
|
|
|
8723
8740
|
return null;
|
|
8724
8741
|
}
|
|
8725
8742
|
async listSotDocuments(projectId) {
|
|
8743
|
+
const discoveredFiles = new Set(STANDARD_SOT_FILES);
|
|
8744
|
+
try {
|
|
8745
|
+
const { data, error } = await this.client.storage.from(this.bucketName).list(`${projectId}/_sot`);
|
|
8746
|
+
if (!error && data) {
|
|
8747
|
+
for (const item of data) {
|
|
8748
|
+
if (item.name && !item.name.startsWith(".") && !item.name.endsWith(".review.json")) {
|
|
8749
|
+
discoveredFiles.add(item.name);
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
}
|
|
8753
|
+
} catch {
|
|
8754
|
+
}
|
|
8726
8755
|
const results = [];
|
|
8727
|
-
for (const filename of
|
|
8756
|
+
for (const filename of discoveredFiles) {
|
|
8728
8757
|
const content = await this.readSotDocument(projectId, filename);
|
|
8729
8758
|
results.push({
|
|
8730
|
-
name: filename.replace(
|
|
8759
|
+
name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
|
|
8731
8760
|
filename,
|
|
8732
8761
|
exists: !!content,
|
|
8733
8762
|
content: content || void 0,
|
|
@@ -9104,10 +9133,12 @@ async function auditQualityReport(storage, projectId) {
|
|
|
9104
9133
|
computedScore += Math.round(bloomPassCount / total * 20);
|
|
9105
9134
|
computedScore += Math.round(Math.min(syntaxExecutableCount, total) / total * 20);
|
|
9106
9135
|
computedScore += Math.round(scopeCompletenessCount / total * 20);
|
|
9136
|
+
const sotCompletedCount = status.sotReadiness.filter((s) => s.exists).length;
|
|
9137
|
+
const sotTotalCount = status.sotReadiness.length;
|
|
9107
9138
|
if (sotReady) {
|
|
9108
|
-
details.push(
|
|
9139
|
+
details.push(`\u2705 N\u1EC1n t\u1EA3ng SOT (${sotCompletedCount}/${sotTotalCount} t\xE0i li\u1EC7u) s\u1EB5n s\xE0ng.`);
|
|
9109
9140
|
} else {
|
|
9110
|
-
details.push(
|
|
9141
|
+
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}).`);
|
|
9111
9142
|
}
|
|
9112
9143
|
details.push(`\u{1F3AF} Ph\u1EA1m vi h\u1ECDc li\u1EC7u theo SOT (Artifact Scope): [${artifactScope.join(", ")}].`);
|
|
9113
9144
|
if (pedagogy5EPass) {
|
|
@@ -9123,7 +9154,7 @@ async function auditQualityReport(storage, projectId) {
|
|
|
9123
9154
|
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.`);
|
|
9124
9155
|
const rawMarkdownReport = `## \u{1F6E1}\uFE0F B\xC1O C\xC1O KI\u1EC2M \u0110\u1ECANH CH\u1EA4T L\u01AF\u1EE2NG (QUALITY AUDIT): \`${projectId.toUpperCase()}\`
|
|
9125
9156
|
- **\u0110i\u1EC3m th\u1EA9m \u0111\u1ECBnh ch\u1EA5t l\u01B0\u1EE3ng:** **${computedScore}/100**
|
|
9126
|
-
- **Tr\u1EA1ng th\xE1i SOT:** ${sotReady ?
|
|
9157
|
+
- **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})`}.
|
|
9127
9158
|
- **Ph\u1EA1m vi h\u1ECDc li\u1EC7u (SOT Artifact Scope):** \`${artifactScope.join(", ")}\`
|
|
9128
9159
|
- **C\u1EA5u tr\xFAc S\u01B0 ph\u1EA1m (5E / EDP):** ${pedagogy5ECount}/${total} b\xE0i h\u1ECDc \u0111\u1EA1t chu\u1EA9n ph\xE2n pha.
|
|
9129
9160
|
- **Thang \u0111o nh\u1EADn th\u1EE9c Bloom:** ${bloomPassCount}/${total} b\xE0i h\u1ECDc \u0111\u1EA1t ma tr\u1EADn m\u1EE5c ti\xEAu.
|
|
@@ -9225,7 +9256,9 @@ function extractScopeSequenceRows(framework) {
|
|
|
9225
9256
|
const trimmed = line.trim();
|
|
9226
9257
|
if (!trimmed.startsWith("|")) continue;
|
|
9227
9258
|
const lower = trimmed.toLowerCase();
|
|
9228
|
-
|
|
9259
|
+
const hasCodeCol = lower.includes("lesson code") || lower.includes("m\xE3 b\xE0i") || lower.includes("m\xE3 b\xE0i h\u1ECDc");
|
|
9260
|
+
const hasObjCol = lower.includes("learning objective") || lower.includes("m\u1EE5c ti\xEAu") || lower.includes("m\u1EE5c ti\xEAu h\u1ECDc t\u1EADp");
|
|
9261
|
+
if (hasCodeCol && hasObjCol) {
|
|
9229
9262
|
headerFound = true;
|
|
9230
9263
|
continue;
|
|
9231
9264
|
}
|
|
@@ -27870,6 +27903,7 @@ var pad22 = (n) => String(n).padStart(2, "0");
|
|
|
27870
27903
|
var esc = (s) => s.replace(/\|/g, "/").replace(/\n/g, " ").trim();
|
|
27871
27904
|
var BLOOM_OF_DEPTH = { ulo: "Understand", cio: "Apply", sio: "Create" };
|
|
27872
27905
|
function renderFrameworkFromPlan(plan, meta) {
|
|
27906
|
+
const isVi = meta.targetLanguage === "vi";
|
|
27873
27907
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
27874
27908
|
const totalSessions = plan.sessions.length;
|
|
27875
27909
|
const totalMinutes = plan.sessions.reduce(
|
|
@@ -27887,10 +27921,16 @@ function renderFrameworkFromPlan(plan, meta) {
|
|
|
27887
27921
|
const depthTops = [...new Set(s.depth_assignments.map((d) => d.depth))].map((d) => BLOOM_OF_DEPTH[d] ?? "Apply");
|
|
27888
27922
|
const bloom = depthTops[depthTops.length - 1] ?? "Apply";
|
|
27889
27923
|
const keyConcepts = s.new_keywords.length > 0 ? s.new_keywords.join(", ") : s.depth_assignments.map((d) => d.node_id).join(", ");
|
|
27890
|
-
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";
|
|
27924
|
+
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";
|
|
27891
27925
|
const duration = s.knowledge_minutes + s.practice_minutes + s.overhead_minutes;
|
|
27892
27926
|
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 + " |";
|
|
27893
27927
|
}).join("\n");
|
|
27928
|
+
const courseOverviewHeader = isVi ? "## [REQUIRED] T\u1ED5ng quan Kh\xF3a h\u1ECDc" : "## [REQUIRED] Course Overview";
|
|
27929
|
+
const globalObjectivesHeader = isVi ? "## [REQUIRED] M\u1EE5c ti\xEAu H\u1ECDc t\u1EADp To\xE0n di\u1EC7n" : "## [REQUIRED] Global Learning Objectives";
|
|
27930
|
+
const structuralHierarchyHeader = isVi ? "## [REQUIRED] C\u1EA5u tr\xFAc Kh\xF3a h\u1ECDc (Unit & Module)" : "## [REQUIRED] Structural Hierarchy (Unit & Module)";
|
|
27931
|
+
const scopeSequenceHeader = isVi ? "## [REQUIRED] Ph\u1EA1m vi & Tr\xECnh t\u1EF1 Chi ti\u1EBFt (Scope & Sequence)" : "## [REQUIRED] Scope & Sequence (Detailed Roadmap)";
|
|
27932
|
+
const standardFormatHeader = isVi ? "## [REQUIRED] C\u1EA5u tr\xFAc B\xE0i h\u1ECDc Chu\u1EA9n" : "## [REQUIRED] Standard Lesson Format";
|
|
27933
|
+
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) |";
|
|
27894
27934
|
return [
|
|
27895
27935
|
"---",
|
|
27896
27936
|
'id: "CURRICULUM-FRAMEWORK"',
|
|
@@ -27912,7 +27952,7 @@ function renderFrameworkFromPlan(plan, meta) {
|
|
|
27912
27952
|
"",
|
|
27913
27953
|
"_Projection of CURRICULUM_PLAN (hash " + plan.plan_hash + "). Approving this framework approves the per-session SCOPE recorded in the plan._",
|
|
27914
27954
|
"",
|
|
27915
|
-
|
|
27955
|
+
courseOverviewHeader,
|
|
27916
27956
|
"- **Official Course Name:** " + esc(meta.courseName),
|
|
27917
27957
|
meta.shortDescription ? "- **Short Description:** " + esc(meta.shortDescription) : "",
|
|
27918
27958
|
"- **Total Units:** " + plan.units.length,
|
|
@@ -27920,20 +27960,20 @@ function renderFrameworkFromPlan(plan, meta) {
|
|
|
27920
27960
|
"- **Total Duration:** ~" + totalHours + " hours (" + totalSessions + " sessions x " + plan.constraints.session_duration_minutes + " min).",
|
|
27921
27961
|
"- **Entry Level:** " + plan.constraints.entry_level + " (age band " + plan.constraints.age_band[0] + "-" + plan.constraints.age_band[1] + ").",
|
|
27922
27962
|
"",
|
|
27923
|
-
|
|
27963
|
+
globalObjectivesHeader,
|
|
27924
27964
|
...plan.course.objectives.map((o, i) => i + 1 + ". **[" + o.bloom + "]:** " + esc(o.statement)),
|
|
27925
27965
|
"",
|
|
27926
|
-
|
|
27966
|
+
structuralHierarchyHeader,
|
|
27927
27967
|
"| Unit | Module | Lessons | Module Objective | Lesson Codes |",
|
|
27928
27968
|
"|---|---|:---:|---|---|",
|
|
27929
27969
|
hierarchyRows || "| (none) | | | | |",
|
|
27930
27970
|
"",
|
|
27931
|
-
|
|
27932
|
-
|
|
27971
|
+
scopeSequenceHeader,
|
|
27972
|
+
scopeTableHeader,
|
|
27933
27973
|
"|:---:|:---:|---|---|---|---|---|---|---|:---:|:---:|",
|
|
27934
27974
|
scopeRows || "| 01 | U01_M01_L01 | (empty plan) | U01 | U01_M01 | - | - | - | None | Understand | " + plan.constraints.session_duration_minutes + " |",
|
|
27935
27975
|
"",
|
|
27936
|
-
|
|
27976
|
+
standardFormatHeader,
|
|
27937
27977
|
"- **Session duration:** " + plan.constraints.session_duration_minutes + " minutes (overhead " + String(plan.sessions[0]?.overhead_minutes ?? 0) + " min).",
|
|
27938
27978
|
"- **Knowledge/practice split per session:** see plan JSON (knowledge_minutes / practice_minutes).",
|
|
27939
27979
|
"- **Pedagogical flow:** 5E (Engage / Explore / Explain / Elaborate / Evaluate) unless policy overrides.",
|