Ingest course content from markdown files into the database. Parses manifest + module files, validates, and upserts course_lessons, reflection_questions, discussion_prompts, and exercises.
Extract per-lesson: id (from heading), title, type (from **Type:** line), body content
Convert markdown body to HTML using the same approach as existing scripts
Handle special blocks:
Blockquotes with > ... followed by — [reference] → scripture blocks
Numbered lists → exercise steps
Reflection questions marked with **Question N:** or numbered items under reflection headings
Child Item Extraction
From lesson content, extract structured child items:
Reflection questions: Look for numbered questions in reflection-type sections. Create reflection_questions rows with question text, question_type, block_order.
Discussion prompts: Look for prompts in discussion/cohort sections. Map to prompt_type (conceptual, application, integration, general) based on E/E/E/J structure.
Exercises: Look for exercise blocks with title, instructions, purpose, estimated time. Create exercises rows.
Validation (Pre-Insert)
Before writing to the database, validate:
All manifest lesson IDs exist in the markdown files
No empty lesson bodies (flag but allow override)
No placeholder text ("[Content TBD]", "[TODO]", "Lorem ipsum")
All lesson types are valid SECTION_TYPES
8 weeks present (numbered 1-8, no Week 0)
Reflection sections have 6-8 questions each (warn if outside range)
Report validation results and ask for confirmation before proceeding.
Suggest running /course-validate [slug] for Charter compliance
Output Format
## Course Ingestion Report: [title] ([slug])
### Source: [path]
### Validation: PASS / FAIL (with details)
### Ingested:
- Course: [title] (id: [id])
- Weeks: 8
- Sections: N total
- Week 1: X sections
- Week 2: X sections
- ...
- Reflection questions: N
- Discussion prompts: N
- Exercises: N
### Warnings:
- [any non-blocking issues]
### Next Steps:
- Run `/course-validate [slug]` to check Charter compliance
- Review draft sections in the learn view at /content/courses/[slug]/learn
Rules
8 weeks, numbered 1-8. No Week 0.
Always upsert (idempotent) — running twice should not create duplicates
Set ingested content to status: "draft" unless the user says otherwise
Never delete existing data — only insert or update
Preserve existing content if a lesson already has non-empty content (warn and skip)
Use the project's existing Drizzle patterns — check src/lib/services/simplified/courses.service.ts for query conventions
1---2name: course-ingest-23description: Ingest course content from markdown files into the database. Parses manifest + module files, validates, and upserts course_lessons, reflection_questions, discussion_prompts, and exercises.4---56Ingest course content: $ARGUMENTS78$ARGUMENTS should be a course slug or path to the content directory. If empty, ask the user.910## Before Starting11121. Read any existing ingestion scripts in `scripts/` to understand patterns (e.g., `scripts/ingest-mdna-course.ts`)132. Read `src/lib/database/schema.ts` for table definitions (courses, courseWeeks, courseLessons, reflectionQuestions, discussionPrompts, exercises)143. Read `src/lib/schemas/course-learn.ts` for SECTION_TYPES154. Read `src/lib/config/lesson-types.ts` for type mapping1617## Locate Content Source1819Search for content in these locations (in order):20211. `content-library/courses/[slug]/` — project content library222. `/Users/joshuashepherd/Desktop/Dev/repos/mdna-course/docs/` — standalone course repo (for mdna)233. A path provided directly by the user2425Look for:26- `course-manifest.json` — lesson order, types, week structure27- `module-*.md` or `week-*.md` — lesson body content28- Any structured markdown with `## Lesson` or `## M.N.X` headings2930## Content Parsing3132### Manifest Format (expected)3334```json35{36 "courseTitle": "The mDNA",37 "weeks": [38 {39 "id": 1,40 "title": "Introduction & Orientation",41 "outcomes": ["..."],42 "lessons": [43 { "id": "lesson-01", "title": "Course Overview", "type": "welcome", "sidebarLabel": "Welcome" }44 ]45 }46 ]47}48```4950### Markdown Parsing5152For each `module-*.md` or `week-*.md` file:531. Split on `## Lesson` or `## M` headings542. Extract per-lesson: id (from heading), title, type (from `**Type:**` line), body content553. Convert markdown body to HTML using the same approach as existing scripts564. Handle special blocks:57 - Blockquotes with `> ...` followed by `— [reference]` → scripture blocks58 - Numbered lists → exercise steps59 - Reflection questions marked with `**Question N:**` or numbered items under reflection headings6061### Child Item Extraction6263From lesson content, extract structured child items:6465- **Reflection questions**: Look for numbered questions in reflection-type sections. Create `reflection_questions` rows with question text, question_type, block_order.66- **Discussion prompts**: Look for prompts in discussion/cohort sections. Map to prompt_type (conceptual, application, integration, general) based on E/E/E/J structure.67- **Exercises**: Look for exercise blocks with title, instructions, purpose, estimated time. Create `exercises` rows.6869## Validation (Pre-Insert)7071Before writing to the database, validate:72731. [ ] All manifest lesson IDs exist in the markdown files742. [ ] No empty lesson bodies (flag but allow override)753. [ ] No placeholder text ("[Content TBD]", "[TODO]", "Lorem ipsum")764. [ ] All lesson types are valid SECTION_TYPES775. [ ] 8 weeks present (numbered 1-8, no Week 0)786. [ ] Reflection sections have 6-8 questions each (warn if outside range)7980Report validation results and ask for confirmation before proceeding.8182## Database Operations8384### Create or update the ingestion script8586Create/update `scripts/ingest-[slug]-course.ts` that:87881. Connects via project Drizzle setup (`src/lib/database/`)892. Uses `getTenantOrgId()` for organization scoping903. Upserts in this order:91 - `courses` row (find by slug, update if exists, insert if not)92 - `courseWeeks` rows (find by course_id + week_number, upsert)93 - `courseLessons` rows (find by course_id + slug or section_order, upsert)94 - `reflectionQuestions` rows (find by lesson_id + block_order, upsert)95 - `discussionPrompts` rows (find by lesson_id + block_order, upsert)96 - `exercises` rows (find by lesson_id + block_order, upsert)974. Reports: rows created, rows updated, rows skipped9899### Run the script100101Execute with: `pnpm tsx scripts/ingest-[slug]-course.ts`102103## Post-Ingest Verification104105After ingestion:1061. Query the course to verify section count matches manifest1072. Verify child items (reflection questions, discussion prompts, exercises) are linked correctly1083. Suggest running `/course-validate [slug]` for Charter compliance109110## Output Format111112```113## Course Ingestion Report: [title] ([slug])114115### Source: [path]116### Validation: PASS / FAIL (with details)117118### Ingested:119- Course: [title] (id: [id])120- Weeks: 8121- Sections: N total122 - Week 1: X sections123 - Week 2: X sections124 - ...125- Reflection questions: N126- Discussion prompts: N127- Exercises: N128129### Warnings:130- [any non-blocking issues]131132### Next Steps:133- Run `/course-validate [slug]` to check Charter compliance134- Review draft sections in the learn view at /content/courses/[slug]/learn135```136137## Rules138139- 8 weeks, numbered 1-8. No Week 0.140- Always upsert (idempotent) — running twice should not create duplicates141- Set ingested content to status: "draft" unless the user says otherwise142- Never delete existing data — only insert or update143- Preserve existing content if a lesson already has non-empty content (warn and skip)144- Use the project's existing Drizzle patterns — check `src/lib/services/simplified/courses.service.ts` for query conventions
Run npx skillmds@latest add joshuashepherd/course-ingest-2 in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Ingest course content from markdown files into the database. Parses manifest + module files, validates, and upserts course_lessons, reflection_questions, discussion_prompts, and exercises. It is listed under Docs & Writing on SkillMD.
This skill has not completed SkillMD's automated safety review yet. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
JoshuaShepherd (@joshuashepherd) published this skill. Their other Agent Skills are listed on their SkillMD profile.