@thanh01.pmt/curriculum-kit 1.4.43 → 1.4.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
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');
@@ -9,18 +9,18 @@ var zod = require('zod');
9
9
  var ai = require('ai');
10
10
  var pRetry = require('p-retry');
11
11
  var jsonrepair = require('jsonrepair');
12
+ var url = require('url');
12
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 + "=")) {
@@ -3944,467 +3944,105 @@ var RoadmapInputSchema = zod.z.object({
3944
3944
 
3945
3945
  // src/ai/index.ts
3946
3946
  init_provider_factory();
3947
-
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();
3947
+ function stripTemplateFrontmatter(raw) {
3948
+ let body = raw;
3949
+ if (body.startsWith("---")) {
3950
+ const end = body.indexOf("\n---", 3);
3951
+ if (end !== -1) body = body.slice(end + 4);
3952
+ }
3953
+ return body.split("\n").filter((l) => l.trim() !== "<!-- INJECT_BODY -->").join("\n").replace(/^\s*\n/, "").trimEnd();
3954
+ }
3955
+ function extractRequiredSections(raw) {
3956
+ const lines = raw.split("\n");
3957
+ const start = lines.findIndex((l) => /^template_required_sections:\s*$/.test(l.trim()));
3958
+ if (start === -1) return null;
3959
+ const items = [];
3960
+ for (let i = start + 1; i < lines.length; i++) {
3961
+ const line = lines[i] ?? "";
3962
+ if (line.trim() === "") continue;
3963
+ if (!/^\s+-\s/.test(line)) break;
3964
+ items.push(line.trim().replace(/^-\s+/, "").replace(/^["']|["']$/g, ""));
3965
+ }
3966
+ return items.length > 0 ? items : null;
3967
+ }
3968
+ function normalizeHeading(h) {
3969
+ return h.replace(/\[(REQUIRED|OPTIONAL|CONDITIONAL)\]\s*/gi, "").replace(/\s+/g, " ").trim();
3970
+ }
3971
+ function extractBodySections(raw) {
3972
+ const body = stripTemplateFrontmatter(raw);
3973
+ return body.split("\n").filter((l) => l.startsWith("## ")).map((l) => normalizeHeading(l.slice(3).trim())).filter((s) => s.length > 0);
3974
+ }
3975
+ var KIT_TEMPLATE_FALLBACK_DIRS = [
3976
+ // Packaged with the npm bundle (dist/templates) — resolves from node_modules.
3977
+ "templates",
3978
+ // Repo checkout layouts.
3979
+ "docs/refs/templates"
3980
+ ];
3981
+ var _moduleDir = (() => {
3982
+ try {
3983
+ 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") {
3984
+ 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))));
3985
+ }
3986
+ } catch {
3987
+ }
3988
+ return typeof __dirname !== "undefined" ? __dirname : process.cwd();
3989
+ })();
3990
+ function candidateDirs(explicitDir) {
3991
+ const dirs = [];
3992
+ if (explicitDir) dirs.push(explicitDir);
3993
+ if (process.env.CURRICULUM_TEMPLATES_DIR) dirs.push(process.env.CURRICULUM_TEMPLATES_DIR);
3994
+ dirs.push(path4__default.default.resolve(process.cwd(), "_templates"));
3995
+ for (const rel of KIT_TEMPLATE_FALLBACK_DIRS) {
3996
+ dirs.push(path4__default.default.resolve(_moduleDir, rel));
3997
+ dirs.push(path4__default.default.resolve(process.cwd(), rel));
3998
+ dirs.push(path4__default.default.resolve(process.cwd(), "packages/curriculum-kit", rel));
3999
+ }
4000
+ return dirs;
4001
+ }
4002
+ function findTemplatePath(templateName, explicitDir) {
4003
+ for (const dir of candidateDirs(explicitDir)) {
4004
+ const p = path4__default.default.join(dir, templateName);
4005
+ if (!fs3__default.default.existsSync(p)) continue;
4006
+ try {
4007
+ fs3__default.default.accessSync(p, fs3__default.default.constants.R_OK);
4008
+ return p;
4009
+ } catch {
4010
+ }
4011
+ }
4012
+ return null;
4013
+ }
4014
+ function resolveArtifactTemplate(templateName, opts) {
4015
+ const p = findTemplatePath(templateName, opts?.templatesDir);
4016
+ if (!p) return null;
4017
+ try {
4018
+ const raw = fs3__default.default.readFileSync(p, "utf-8");
4019
+ return {
4020
+ content: stripTemplateFrontmatter(raw),
4021
+ sections: extractRequiredSections(raw) ?? extractBodySections(raw),
4022
+ sourcePath: p
4023
+ };
4024
+ } catch {
4025
+ return null;
4026
+ }
4027
+ }
4028
+ function loadCurriculumTemplate(templateName, templatesDir) {
4029
+ const p = findTemplatePath(templateName, templatesDir);
4030
+ if (!p) return "";
4031
+ try {
4032
+ return fs3__default.default.readFileSync(p, "utf-8");
4033
+ } catch {
4034
+ return "";
4035
+ }
4036
+ }
4037
+ function loadAuthoringTemplate(templateName, templatesDir) {
4038
+ const p = findTemplatePath(templateName, templatesDir);
4039
+ if (!p) return "";
4040
+ try {
4041
+ return stripTemplateFrontmatter(fs3__default.default.readFileSync(p, "utf-8"));
4042
+ } catch {
4043
+ return "";
4044
+ }
4045
+ }
4408
4046
 
4409
4047
  // src/ai/prompts/lessonMasterPrompt.ts
4410
4048
  function buildLessonMasterPrompt(input) {
@@ -4458,7 +4096,7 @@ Build a deep, rigorous, and highly pedagogical Master Lesson Plan (LESSON) that
4458
4096
  # MANDATORY TEMPLATE STRUCTURE
4459
4097
  You MUST generate structured data that will render directly into this exact Markdown template:
4460
4098
  ---
4461
- ${LESSON_PLAN_TEMPLATE}
4099
+ ${loadAuthoringTemplate("LESSON_5E_template.md") ?? "[TEMPLATE FILE NOT FOUND: LESSON_5E_template.md]"}
4462
4100
  ---
4463
4101
 
4464
4102
  # INPUT PARAMETERS
@@ -4536,7 +4174,7 @@ Transform the exact pedagogical flow and activities from the Master Lesson Plan
4536
4174
  # MANDATORY TEMPLATE STRUCTURE
4537
4175
  You MUST generate data aligned with this Markdown template:
4538
4176
  ---
4539
- ${ACT_TEMPLATE}
4177
+ ${loadAuthoringTemplate("ACT_template.md") ?? "[TEMPLATE FILE NOT FOUND: ACT_template.md]"}
4540
4178
  ---
4541
4179
 
4542
4180
  # CANONICAL MASTER LESSON CONTEXT (SOURCE OF TRUTH)
@@ -4631,7 +4269,7 @@ Generate complete, verified source code that satisfies the lesson objectives and
4631
4269
  # MANDATORY TEMPLATE STRUCTURE
4632
4270
  You MUST generate data aligned with this Markdown template:
4633
4271
  ---
4634
- ${CODE_LAB_TEMPLATE}
4272
+ ${loadAuthoringTemplate("LAB_TEMPLATE.md") ?? "[TEMPLATE FILE NOT FOUND: LAB_TEMPLATE.md]"}
4635
4273
  ---
4636
4274
 
4637
4275
  # CANONICAL LESSON CONTEXT (SOURCE OF TRUTH)
@@ -5326,7 +4964,7 @@ Build a complete, engaging Self-Paced Hands-On Lab (SELF_LAB) for autonomous lea
5326
4964
  # MANDATORY TEMPLATE STRUCTURE
5327
4965
  You MUST generate data aligned with this Markdown template:
5328
4966
  ---
5329
- ${SELF_LAB_TEMPLATE}
4967
+ ${loadAuthoringTemplate("SELF_LAB_template.md") ?? "[TEMPLATE FILE NOT FOUND: SELF_LAB_template.md]"}
5330
4968
  ---
5331
4969
 
5332
4970
  # INPUT PARAMETERS
@@ -5463,7 +5101,7 @@ Use this mapping to determine which Bloom level each question truly targets. Mat
5463
5101
  # MANDATORY TEMPLATE STRUCTURE
5464
5102
  You MUST generate data aligned with this Markdown template:
5465
5103
  ---
5466
- ${QUIZ_TEMPLATE}
5104
+ ${loadAuthoringTemplate("QUIZ_v4.6_template.md") ?? "[TEMPLATE FILE NOT FOUND: QUIZ_v4.6_template.md]"}
5467
5105
  ---
5468
5106
 
5469
5107
  # INPUT PARAMETERS
@@ -7760,13 +7398,13 @@ async function generateSelfPacedBundle(input) {
7760
7398
  if (input.outputDirectory) {
7761
7399
  await fsPromises__default.default.mkdir(input.outputDirectory, { recursive: true });
7762
7400
  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")
7401
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "INSTRUCTION.md"), instructionMd, "utf-8"),
7402
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "SELF_LAB.md"), selfLabMd, "utf-8"),
7403
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "QUIZ.md"), quizMd, "utf-8"),
7404
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "QUIZ.json"), JSON.stringify(quiz, null, 2), "utf-8"),
7405
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "CHEAT_SHEET.md"), cheatSheetMd, "utf-8"),
7406
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "LAB.md"), codeLabMd, "utf-8"),
7407
+ fsPromises__default.default.writeFile(path4__default.default.join(input.outputDirectory, "bundle.json"), JSON.stringify(bundle, null, 2), "utf-8")
7770
7408
  ]);
7771
7409
  }
7772
7410
  return {
@@ -8167,15 +7805,15 @@ var STANDARD_SOT_FILES = [
8167
7805
  "ALIGNMENT_MATRIX.md"
8168
7806
  ];
8169
7807
  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`);
7808
+ const dir = path4__default.default.dirname(targetPath);
7809
+ if (!fs3__default.default.existsSync(dir)) fs3__default.default.mkdirSync(dir, { recursive: true });
7810
+ 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
7811
  try {
8174
- fs2__default.default.writeFileSync(tmpPath, content, "utf-8");
8175
- fs2__default.default.renameSync(tmpPath, targetPath);
7812
+ fs3__default.default.writeFileSync(tmpPath, content, "utf-8");
7813
+ fs3__default.default.renameSync(tmpPath, targetPath);
8176
7814
  } catch (err) {
8177
7815
  try {
8178
- if (fs2__default.default.existsSync(tmpPath)) fs2__default.default.unlinkSync(tmpPath);
7816
+ if (fs3__default.default.existsSync(tmpPath)) fs3__default.default.unlinkSync(tmpPath);
8179
7817
  } catch {
8180
7818
  }
8181
7819
  throw err;
@@ -8184,51 +7822,51 @@ function atomicWriteFileSync(targetPath, content) {
8184
7822
  var FileSystemCurriculumAdapter = class {
8185
7823
  baseDir;
8186
7824
  constructor(options = {}) {
8187
- this.baseDir = options.baseDir || path3__default.default.resolve(process.cwd(), "output", "projects");
7825
+ this.baseDir = options.baseDir || path4__default.default.resolve(process.cwd(), "output", "projects");
8188
7826
  }
8189
7827
  getProjectDir(projectId) {
8190
7828
  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;
7829
+ const directPath = path4__default.default.join(this.baseDir, raw);
7830
+ if (fs3__default.default.existsSync(directPath)) return directPath;
7831
+ const fullIdPath = path4__default.default.join(this.baseDir, projectId);
7832
+ if (fs3__default.default.existsSync(fullIdPath)) return fullIdPath;
8195
7833
  return directPath;
8196
7834
  }
8197
7835
  /** Ensures the resolved path stays within the project directory (prevents path traversal). */
8198
7836
  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) {
7837
+ const resolvedProject = path4__default.default.resolve(projectDir);
7838
+ const resolvedTarget = path4__default.default.resolve(targetPath);
7839
+ if (!resolvedTarget.startsWith(resolvedProject + path4__default.default.sep) && resolvedTarget !== resolvedProject) {
8202
7840
  throw new Error(`[FileSystemCurriculumAdapter] Path traversal blocked: ${targetPath} is outside project directory ${projectDir}`);
8203
7841
  }
8204
7842
  }
8205
7843
  async projectExists(projectId) {
8206
- return fs2__default.default.existsSync(this.getProjectDir(projectId));
7844
+ return fs3__default.default.existsSync(this.getProjectDir(projectId));
8207
7845
  }
8208
7846
  async readSotDocument(projectId, filename) {
8209
7847
  const projectDir = this.getProjectDir(projectId);
8210
7848
  const possiblePaths = [
8211
- path3__default.default.join(projectDir, "_sot", filename),
8212
- path3__default.default.join(projectDir, filename)
7849
+ path4__default.default.join(projectDir, "_sot", filename),
7850
+ path4__default.default.join(projectDir, filename)
8213
7851
  ];
8214
7852
  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");
7853
+ if (fs3__default.default.existsSync(p) && fs3__default.default.statSync(p).isFile()) {
7854
+ return fs3__default.default.readFileSync(p, "utf-8");
8217
7855
  }
8218
7856
  }
8219
7857
  return null;
8220
7858
  }
8221
7859
  async listSotDocuments(projectId) {
8222
7860
  const projectDir = this.getProjectDir(projectId);
8223
- const sotDir = path3__default.default.join(projectDir, "_sot");
7861
+ const sotDir = path4__default.default.join(projectDir, "_sot");
8224
7862
  const discoveredFiles = new Set(STANDARD_SOT_FILES);
8225
- if (fs2__default.default.existsSync(sotDir)) {
7863
+ if (fs3__default.default.existsSync(sotDir)) {
8226
7864
  try {
8227
- const diskFiles = fs2__default.default.readdirSync(sotDir);
7865
+ const diskFiles = fs3__default.default.readdirSync(sotDir);
8228
7866
  for (const f of diskFiles) {
8229
7867
  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()) {
7868
+ const fullPath = path4__default.default.join(sotDir, f);
7869
+ if (fs3__default.default.statSync(fullPath).isFile()) {
8232
7870
  discoveredFiles.add(f);
8233
7871
  }
8234
7872
  }
@@ -8236,9 +7874,9 @@ var FileSystemCurriculumAdapter = class {
8236
7874
  }
8237
7875
  }
8238
7876
  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);
7877
+ 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;
7878
+ if (p && fs3__default.default.existsSync(p)) {
7879
+ const stats = fs3__default.default.statSync(p);
8242
7880
  return {
8243
7881
  name: filename.replace(/\.(md|json)$/i, "").replace(/_/g, " "),
8244
7882
  filename,
@@ -8256,18 +7894,18 @@ var FileSystemCurriculumAdapter = class {
8256
7894
  }
8257
7895
  async readArtifact(projectId, relPathOrIdentifier) {
8258
7896
  const projectDir = this.getProjectDir(projectId);
8259
- const directCandidate = path3__default.default.join(projectDir, relPathOrIdentifier);
7897
+ const directCandidate = path4__default.default.join(projectDir, relPathOrIdentifier);
8260
7898
  this.assertInsideProject(projectDir, directCandidate);
8261
7899
  const possiblePaths = [
8262
7900
  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))
7901
+ path4__default.default.join(projectDir, "_sot", relPathOrIdentifier),
7902
+ path4__default.default.join(projectDir, "lessons", relPathOrIdentifier),
7903
+ path4__default.default.join(projectDir, "_sot", path4__default.default.basename(relPathOrIdentifier)),
7904
+ path4__default.default.join(projectDir, "lessons", path4__default.default.basename(relPathOrIdentifier))
8267
7905
  ];
8268
7906
  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");
7907
+ if (fs3__default.default.existsSync(p) && fs3__default.default.statSync(p).isFile()) {
7908
+ return fs3__default.default.readFileSync(p, "utf-8");
8271
7909
  }
8272
7910
  }
8273
7911
  if (relPathOrIdentifier.includes(".md") || relPathOrIdentifier.includes("/")) {
@@ -8276,8 +7914,8 @@ var FileSystemCurriculumAdapter = class {
8276
7914
  const lessonMatch = relPathOrIdentifier.match(/U\d+_M\d+_L\d+/i);
8277
7915
  if (lessonMatch) {
8278
7916
  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()) {
7917
+ const lessonDir = path4__default.default.join(projectDir, "lessons", lessonId);
7918
+ if (fs3__default.default.existsSync(lessonDir) && fs3__default.default.statSync(lessonDir).isDirectory()) {
8281
7919
  const lower = relPathOrIdentifier.toLowerCase();
8282
7920
  let artifactPrefix = "LESSON";
8283
7921
  if (lower.includes("act") || lower.includes("lab") || lower.includes("th\u1EF1c h\xE0nh")) {
@@ -8297,9 +7935,9 @@ var FileSystemCurriculumAdapter = class {
8297
7935
  } else if (lower.includes("ext") || lower.includes("extension")) {
8298
7936
  artifactPrefix = "EXT";
8299
7937
  }
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");
7938
+ const candidateFile = path4__default.default.join(lessonDir, `${artifactPrefix}_${lessonId}.md`);
7939
+ if (fs3__default.default.existsSync(candidateFile)) {
7940
+ return fs3__default.default.readFileSync(candidateFile, "utf-8");
8303
7941
  }
8304
7942
  }
8305
7943
  }
@@ -8307,22 +7945,22 @@ var FileSystemCurriculumAdapter = class {
8307
7945
  }
8308
7946
  async saveArtifact(projectId, relPath, content) {
8309
7947
  const projectDir = this.getProjectDir(projectId);
8310
- const targetPath = path3__default.default.join(projectDir, relPath);
7948
+ const targetPath = path4__default.default.join(projectDir, relPath);
8311
7949
  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 });
7950
+ const targetDir = path4__default.default.dirname(targetPath);
7951
+ if (!fs3__default.default.existsSync(targetDir)) {
7952
+ fs3__default.default.mkdirSync(targetDir, { recursive: true });
8315
7953
  }
8316
- if (fs2__default.default.existsSync(targetPath)) {
7954
+ if (fs3__default.default.existsSync(targetPath)) {
8317
7955
  try {
8318
- const oldContent = fs2__default.default.readFileSync(targetPath, "utf-8");
7956
+ const oldContent = fs3__default.default.readFileSync(targetPath, "utf-8");
8319
7957
  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();
7958
+ const historyDir = path4__default.default.join(projectDir, ".history", relPath);
7959
+ fs3__default.default.mkdirSync(historyDir, { recursive: true });
7960
+ atomicWriteFileSync(path4__default.default.join(historyDir, `${Date.now()}.md`), oldContent);
7961
+ const versions = fs3__default.default.readdirSync(historyDir).filter((f) => f.endsWith(".md")).sort();
8324
7962
  while (versions.length > 10) {
8325
- fs2__default.default.unlinkSync(path3__default.default.join(historyDir, versions.shift()));
7963
+ fs3__default.default.unlinkSync(path4__default.default.join(historyDir, versions.shift()));
8326
7964
  }
8327
7965
  }
8328
7966
  } catch (histErr) {
@@ -8330,24 +7968,24 @@ var FileSystemCurriculumAdapter = class {
8330
7968
  }
8331
7969
  }
8332
7970
  atomicWriteFileSync(targetPath, content);
8333
- const filename = path3__default.default.basename(relPath);
7971
+ const filename = path4__default.default.basename(relPath);
8334
7972
  const match = filename.match(/(LESSON|ACT|QUIZ|SLIDE|GUIDE|HANDOUT|WKS|EXT)_(U\d+_M\d+_L\d+)\.md/i);
8335
7973
  if (match) {
8336
7974
  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);
7975
+ atomicWriteFileSync(path4__default.default.join(projectDir, "lessons", lessonId, filename), content);
7976
+ const legacyContentDir = path4__default.default.join(projectDir, "_content", lessonId);
7977
+ if (fs3__default.default.existsSync(legacyContentDir)) {
7978
+ atomicWriteFileSync(path4__default.default.join(legacyContentDir, filename), content);
8341
7979
  }
8342
7980
  }
8343
7981
  }
8344
7982
  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("."));
7983
+ const lessonsDir = path4__default.default.join(this.getProjectDir(projectId), "lessons");
7984
+ if (!fs3__default.default.existsSync(lessonsDir)) return [];
7985
+ const lessonFolders = fs3__default.default.readdirSync(lessonsDir).filter((f) => !f.startsWith("."));
8348
7986
  return lessonFolders.map((folder) => {
8349
- const lDir = path3__default.default.join(lessonsDir, folder);
8350
- const isDir = fs2__default.default.statSync(lDir).isDirectory();
7987
+ const lDir = path4__default.default.join(lessonsDir, folder);
7988
+ const isDir = fs3__default.default.statSync(lDir).isDirectory();
8351
7989
  if (!isDir) {
8352
7990
  return {
8353
7991
  lessonId: folder,
@@ -8356,7 +7994,7 @@ var FileSystemCurriculumAdapter = class {
8356
7994
  artifacts: []
8357
7995
  };
8358
7996
  }
8359
- const artifacts = fs2__default.default.readdirSync(lDir).filter((f) => !f.startsWith("."));
7997
+ const artifacts = fs3__default.default.readdirSync(lDir).filter((f) => !f.startsWith("."));
8360
7998
  return {
8361
7999
  lessonId: folder,
8362
8000
  artifactsCount: artifacts.length,
@@ -8402,10 +8040,10 @@ ${lessonTable}
8402
8040
  }
8403
8041
  async getPipelineState(projectId) {
8404
8042
  const projectDir = this.getProjectDir(projectId);
8405
- const stateFile = path3__default.default.join(projectDir, "_pipeline", "state.json");
8406
- if (fs2__default.default.existsSync(stateFile)) {
8043
+ const stateFile = path4__default.default.join(projectDir, "_pipeline", "state.json");
8044
+ if (fs3__default.default.existsSync(stateFile)) {
8407
8045
  try {
8408
- const raw = fs2__default.default.readFileSync(stateFile, "utf-8");
8046
+ const raw = fs3__default.default.readFileSync(stateFile, "utf-8");
8409
8047
  const parsed = JSON.parse(raw);
8410
8048
  if (parsed && typeof parsed === "object" && parsed.tasks) {
8411
8049
  return parsed;
@@ -8422,11 +8060,11 @@ ${lessonTable}
8422
8060
  }
8423
8061
  async savePipelineState(projectId, state) {
8424
8062
  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 });
8063
+ const pipelineDir = path4__default.default.join(projectDir, "_pipeline");
8064
+ if (!fs3__default.default.existsSync(pipelineDir)) {
8065
+ fs3__default.default.mkdirSync(pipelineDir, { recursive: true });
8428
8066
  }
8429
- const stateFile = path3__default.default.join(pipelineDir, "state.json");
8067
+ const stateFile = path4__default.default.join(pipelineDir, "state.json");
8430
8068
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
8431
8069
  atomicWriteFileSync(stateFile, JSON.stringify(state, null, 2));
8432
8070
  }
@@ -8449,9 +8087,9 @@ ${lessonTable}
8449
8087
  async deleteArtifact(projectId, relPath) {
8450
8088
  try {
8451
8089
  const projectDir = this.getProjectDir(projectId);
8452
- const targetPath = path3__default.default.join(projectDir, relPath);
8090
+ const targetPath = path4__default.default.join(projectDir, relPath);
8453
8091
  this.assertInsideProject(projectDir, targetPath);
8454
- if (fs2__default.default.existsSync(targetPath)) fs2__default.default.rmSync(targetPath, { force: true });
8092
+ if (fs3__default.default.existsSync(targetPath)) fs3__default.default.rmSync(targetPath, { force: true });
8455
8093
  } catch {
8456
8094
  }
8457
8095
  }
@@ -9201,19 +8839,19 @@ function buildStandardStackMarkdown(opts) {
9201
8839
 
9202
8840
  // src/services/sotGeneratorService.ts
9203
8841
  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();
8842
+ var __dirname2 = __filename$1 ? path4__default.default.dirname(__filename$1) : process.cwd();
9205
8843
  function loadSotTemplate(templateFileName) {
9206
8844
  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)
8845
+ path4__default.default.resolve(__dirname2, "../../docs/refs/templates", templateFileName),
8846
+ path4__default.default.resolve(process.cwd(), "packages/curriculum-kit/docs/refs/templates", templateFileName),
8847
+ path4__default.default.resolve(process.cwd(), "../learnwell-platform/packages/curriculum-kit/docs/refs/templates", templateFileName),
8848
+ path4__default.default.resolve("/Users/tonypham/MEGA/WebApp/WIP/learnwell-platform/packages/curriculum-kit/docs/refs/templates", templateFileName),
8849
+ path4__default.default.resolve("/Users/tonypham/MEGA/ai-platform/learning-artifact-gen-ui/_templates", templateFileName)
9212
8850
  ];
9213
8851
  for (const p of possiblePaths) {
9214
- if (fs2__default.default.existsSync(p)) {
8852
+ if (fs3__default.default.existsSync(p)) {
9215
8853
  try {
9216
- return fs2__default.default.readFileSync(p, "utf-8");
8854
+ return fs3__default.default.readFileSync(p, "utf-8");
9217
8855
  } catch {
9218
8856
  }
9219
8857
  }
@@ -10228,7 +9866,7 @@ function buildSectionAwareExcerpt(markdown, opts = {}) {
10228
9866
  if (s.canonicalKey && !byCanonical.has(s.canonicalKey)) {
10229
9867
  byCanonical.set(s.canonicalKey, s);
10230
9868
  }
10231
- const norm2 = normalizeHeading(s.cleanTitle);
9869
+ const norm2 = normalizeHeading2(s.cleanTitle);
10232
9870
  if (!byCleanTitle.has(norm2)) {
10233
9871
  byCleanTitle.set(norm2, s);
10234
9872
  }
@@ -10278,7 +9916,7 @@ ${truncated}`;
10278
9916
  blocks.push(metaBlock);
10279
9917
  let sectionAware = false;
10280
9918
  for (const key of priorities) {
10281
- const sec = byCanonical.get(key) || byCleanTitle.get(normalizeHeading(key));
9919
+ const sec = byCanonical.get(key) || byCleanTitle.get(normalizeHeading2(key));
10282
9920
  if (!sec) continue;
10283
9921
  sectionAware = true;
10284
9922
  if (!tryAdd(sec.cleanTitle, sec.body, key)) break;
@@ -10360,15 +9998,15 @@ function parseMarkdownSections(markdown, slcMap, artifactType) {
10360
9998
  const maps = artifactType && slcMap[artifactType] ? [slcMap[artifactType]] : Object.values(slcMap);
10361
9999
  for (const m of maps) {
10362
10000
  for (const [canonical, localized] of Object.entries(m)) {
10363
- reverseMap.set(normalizeHeading(localized), canonical);
10364
- reverseMap.set(normalizeHeading(canonical), canonical);
10001
+ reverseMap.set(normalizeHeading2(localized), canonical);
10002
+ reverseMap.set(normalizeHeading2(canonical), canonical);
10365
10003
  }
10366
10004
  }
10367
10005
  }
10368
10006
  const flush = () => {
10369
10007
  if (!currentHeading) return;
10370
10008
  const cleanTitle = currentHeading.replace(/^#{1,4}\s+/, "").trim();
10371
- const norm2 = normalizeHeading(cleanTitle);
10009
+ const norm2 = normalizeHeading2(cleanTitle);
10372
10010
  const canonicalKey = reverseMap.get(norm2) || findCanonicalFuzzy(norm2);
10373
10011
  sections.push({
10374
10012
  rawHeading: currentHeading,
@@ -10392,7 +10030,7 @@ function parseMarkdownSections(markdown, slcMap, artifactType) {
10392
10030
  flush();
10393
10031
  return sections;
10394
10032
  }
10395
- function normalizeHeading(str) {
10033
+ function normalizeHeading2(str) {
10396
10034
  return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
10397
10035
  }
10398
10036
  function findCanonicalFuzzy(norm2) {
@@ -25461,96 +25099,6 @@ function evaluateActivityAlignment(canonicalExcerpt, generatedContent, now = /*
25461
25099
  const passed = matchedTokens.length > 0;
25462
25100
  return { checked: true, passed, matchedTokens: matchedTokens.slice(0, 10), missingTokens: missingTokens.slice(0, 10), mode };
25463
25101
  }
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
25102
 
25555
25103
  // src/services/lessonProductionService.ts
25556
25104
  var LESSON_TEMPLATE_BY_PEDAGOGY = {
@@ -29492,10 +29040,10 @@ init_slideProductionWorkflow();
29492
29040
  var LocalWorkspaceManager = class {
29493
29041
  baseDir;
29494
29042
  constructor(baseDir) {
29495
- this.baseDir = baseDir || path3__default.default.resolve(process.cwd(), "output", "workspace-jobs");
29043
+ this.baseDir = baseDir || path4__default.default.resolve(process.cwd(), "output", "workspace-jobs");
29496
29044
  }
29497
29045
  getJobDirectory(jobId) {
29498
- return path3__default.default.join(this.baseDir, jobId);
29046
+ return path4__default.default.join(this.baseDir, jobId);
29499
29047
  }
29500
29048
  async initJobWorkspace(jobId, initialInfo) {
29501
29049
  const jobDir = this.getJobDirectory(jobId);
@@ -29518,9 +29066,9 @@ var LocalWorkspaceManager = class {
29518
29066
  }
29519
29067
  async saveArtifact(jobId, milestoneSlug, filename, content, artifactType) {
29520
29068
  const jobDir = this.getJobDirectory(jobId);
29521
- const targetDir = path3__default.default.join(jobDir, milestoneSlug);
29069
+ const targetDir = path4__default.default.join(jobDir, milestoneSlug);
29522
29070
  await fsPromises__default.default.mkdir(targetDir, { recursive: true });
29523
- const filePath = path3__default.default.join(targetDir, filename);
29071
+ const filePath = path4__default.default.join(targetDir, filename);
29524
29072
  await fsPromises__default.default.writeFile(filePath, content, "utf-8");
29525
29073
  const stat = await fsPromises__default.default.stat(filePath);
29526
29074
  const manifest = await this.getManifest(jobId);
@@ -29528,7 +29076,7 @@ var LocalWorkspaceManager = class {
29528
29076
  manifest.artifacts.push({
29529
29077
  milestoneId: milestoneSlug,
29530
29078
  artifactType,
29531
- filePath: path3__default.default.relative(jobDir, filePath),
29079
+ filePath: path4__default.default.relative(jobDir, filePath),
29532
29080
  sizeBytes: stat.size,
29533
29081
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
29534
29082
  });
@@ -29538,7 +29086,7 @@ var LocalWorkspaceManager = class {
29538
29086
  return filePath;
29539
29087
  }
29540
29088
  async getManifest(jobId) {
29541
- const manifestPath = path3__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29089
+ const manifestPath = path4__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29542
29090
  try {
29543
29091
  const data = await fsPromises__default.default.readFile(manifestPath, "utf-8");
29544
29092
  return JSON.parse(data);
@@ -29547,7 +29095,7 @@ var LocalWorkspaceManager = class {
29547
29095
  }
29548
29096
  }
29549
29097
  async saveManifest(jobId, manifest) {
29550
- const manifestPath = path3__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29098
+ const manifestPath = path4__default.default.join(this.getJobDirectory(jobId), "manifest.json");
29551
29099
  manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
29552
29100
  await fsPromises__default.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
29553
29101
  }
@@ -29588,12 +29136,12 @@ async function publishToGitHub(options) {
29588
29136
  async function scanDir(currentDir) {
29589
29137
  const entries = await fsPromises__default.default.readdir(currentDir, { withFileTypes: true });
29590
29138
  for (const entry of entries) {
29591
- const fullPath = path3__default.default.join(currentDir, entry.name);
29139
+ const fullPath = path4__default.default.join(currentDir, entry.name);
29592
29140
  if (entry.isDirectory()) {
29593
29141
  await scanDir(fullPath);
29594
29142
  } else if (entry.isFile() && !entry.name.startsWith(".")) {
29595
29143
  const content = await fsPromises__default.default.readFile(fullPath, "utf-8");
29596
- const relPath = path3__default.default.relative(options.localJobDir, fullPath);
29144
+ const relPath = path4__default.default.relative(options.localJobDir, fullPath);
29597
29145
  filesToUpload.push({ path: relPath, content });
29598
29146
  }
29599
29147
  }
@@ -31699,13 +31247,13 @@ var MediaLedger = class {
31699
31247
  filePath;
31700
31248
  entries = /* @__PURE__ */ new Map();
31701
31249
  constructor(projectDir, lessonId) {
31702
- this.filePath = path3__default.default.join(projectDir, "_media", `ledger-${lessonId.toUpperCase()}.jsonl`);
31250
+ this.filePath = path4__default.default.join(projectDir, "_media", `ledger-${lessonId.toUpperCase()}.jsonl`);
31703
31251
  this.load();
31704
31252
  }
31705
31253
  load() {
31706
31254
  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");
31255
+ if (!fs3__default.default.existsSync(this.filePath)) return;
31256
+ const lines = fs3__default.default.readFileSync(this.filePath, "utf-8").split("\n");
31709
31257
  for (const line of lines) {
31710
31258
  const trimmed = line.trim();
31711
31259
  if (!trimmed) continue;
@@ -31717,8 +31265,8 @@ var MediaLedger = class {
31717
31265
  }
31718
31266
  }
31719
31267
  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");
31268
+ fs3__default.default.mkdirSync(path4__default.default.dirname(this.filePath), { recursive: true });
31269
+ fs3__default.default.writeFileSync(this.filePath, Array.from(this.entries.values()).map((e) => JSON.stringify(e)).join("\n") + "\n", "utf-8");
31722
31270
  }
31723
31271
  /** Agent tạo artifact ghi entry mới (hoặc cập nhật nếu slug đã có và chưa done) */
31724
31272
  upsert(input) {
@@ -31900,8 +31448,8 @@ async function fulfillMediaLedger(options) {
31900
31448
  const maxRetries = options.maxRetries ?? 2;
31901
31449
  const approved = ledger.listByStatus("approved");
31902
31450
  const result = { done: [], failed: [], skipped: [] };
31903
- const mediaDir = path3__default.default.join(projectDir, "_media");
31904
- fs2__default.default.mkdirSync(mediaDir, { recursive: true });
31451
+ const mediaDir = path4__default.default.join(projectDir, "_media");
31452
+ fs3__default.default.mkdirSync(mediaDir, { recursive: true });
31905
31453
  const mode = policy.mode ?? "auto";
31906
31454
  const canSearch = mode === "search_only" || mode === "auto";
31907
31455
  const canGen = mode === "gen_only" || mode === "auto";
@@ -31983,7 +31531,7 @@ async function fulfillMediaLedger(options) {
31983
31531
  });
31984
31532
  const ext = image.mimeType === "image/jpeg" ? "jpg" : "png";
31985
31533
  const filename = `${entry.slug}-${Date.now()}.${ext}`;
31986
- fs2__default.default.writeFileSync(path3__default.default.join(mediaDir, filename), Buffer.from(image.base64, "base64"));
31534
+ fs3__default.default.writeFileSync(path4__default.default.join(mediaDir, filename), Buffer.from(image.base64, "base64"));
31987
31535
  ledger.setStatus(entry.slug, "done", { relPath: `_media/${filename}`, provider: image.provider });
31988
31536
  result.done.push(entry.slug);
31989
31537
  options.onProgress?.(entry.slug, "done", `source: ${image.provider}`);
@@ -32014,7 +31562,6 @@ function renderMediaPlaceholder(entry) {
32014
31562
  return `> \u{1F5BC}\uFE0F [\u1EA2nh \u0111ang ch\u1EDD: ${entry.name} \u2014 s\u1EBD \u0111\u01B0\u1EE3c t\u1EA1o sau khi duy\u1EC7t media]`;
32015
31563
  }
32016
31564
 
32017
- exports.ACT_TEMPLATE = ACT_TEMPLATE;
32018
31565
  exports.ARTIFACT_DEPENDENCY_ROUTING = ARTIFACT_DEPENDENCY_ROUTING;
32019
31566
  exports.ARTIFACT_EXECUTION_ORDER = ARTIFACT_EXECUTION_ORDER;
32020
31567
  exports.AcademicAuditor = AcademicAuditor;
@@ -32036,7 +31583,6 @@ exports.BloomLevelSchema = BloomLevelSchema;
32036
31583
  exports.BloomTaxonomyEvaluator = BloomTaxonomyEvaluator;
32037
31584
  exports.BronzeTierSchema = BronzeTierSchema;
32038
31585
  exports.CERConclusionSchema = CERConclusionSchema;
32039
- exports.CODE_LAB_TEMPLATE = CODE_LAB_TEMPLATE;
32040
31586
  exports.CURRICULUM_SLASH_COMMANDS = CURRICULUM_SLASH_COMMANDS;
32041
31587
  exports.CardTierEnum = CardTierEnum;
32042
31588
  exports.ChoiceBoardRubricRowSchema = ChoiceBoardRubricRowSchema;
@@ -32111,7 +31657,6 @@ exports.JUDGE_ESCALATE_THRESHOLD = JUDGE_ESCALATE_THRESHOLD;
32111
31657
  exports.JudgeCriterionSchema = JudgeCriterionSchema;
32112
31658
  exports.LAYER_IDLE_BUDGET_MS = LAYER_IDLE_BUDGET_MS;
32113
31659
  exports.LAYER_TOTAL_BUDGET_MS = LAYER_TOTAL_BUDGET_MS;
32114
- exports.LESSON_PLAN_TEMPLATE = LESSON_PLAN_TEMPLATE;
32115
31660
  exports.LLMJudgeEngine = LLMJudgeEngine;
32116
31661
  exports.LabTierTaskSchema = LabTierTaskSchema;
32117
31662
  exports.LearningObjectiveInputSchema = LearningObjectiveInputSchema;
@@ -32153,7 +31698,6 @@ exports.ProjectGraphAuditSchema = ProjectGraphAuditSchema;
32153
31698
  exports.ProjectGraphSchema = ProjectGraphSchema;
32154
31699
  exports.ProjectInstructionSchema = ProjectInstructionSchema;
32155
31700
  exports.ProjectProfileSchema = ProjectProfileSchema;
32156
- exports.QUIZ_TEMPLATE = QUIZ_TEMPLATE;
32157
31701
  exports.QualityAuditVerdictSchema = QualityAuditVerdictSchema;
32158
31702
  exports.QuizOptionSchema = QuizOptionSchema;
32159
31703
  exports.RecordProvenanceSchema = RecordProvenanceSchema;
@@ -32166,7 +31710,6 @@ exports.RotationStationSchema = RotationStationSchema;
32166
31710
  exports.RubricCriteriaSchema = RubricCriteriaSchema;
32167
31711
  exports.RubricSchema = RubricSchema;
32168
31712
  exports.SATELLITE_LESSON_PRIORITIES = SATELLITE_LESSON_PRIORITIES;
32169
- exports.SELF_LAB_TEMPLATE = SELF_LAB_TEMPLATE;
32170
31713
  exports.SLIDE_CANONICAL_METHODOLOGY = SLIDE_CANONICAL_METHODOLOGY;
32171
31714
  exports.STANDARD_REF_REGEX = STANDARD_REF_REGEX;
32172
31715
  exports.STANDARD_SOT_FILES = STANDARD_SOT_FILES;
@@ -32342,6 +31885,7 @@ exports.isTranslationDue = isTranslationDue;
32342
31885
  exports.lintAndSanitizeArtifact = lintAndSanitizeArtifact;
32343
31886
  exports.lintCurriculumFramework = lintCurriculumFramework;
32344
31887
  exports.lintFrameworkPack = lintFrameworkPack;
31888
+ exports.loadAuthoringTemplate = loadAuthoringTemplate;
32345
31889
  exports.loadCurriculumTemplate = loadCurriculumTemplate;
32346
31890
  exports.loadSotTemplate = loadSotTemplate;
32347
31891
  exports.normalizePlanningGraph = normalizePlanningGraph;