Skip to content
Back to skills

Course Ingest

ASecurity

Ingest course content from markdown files into the database. Parses manifest + module files, validates, and upserts course_lessons, reflection_questions, discussion_prompts, and exercises.

  • 2 stars
  • 0 votes
  • 1 copy
  • 4 views
  • Added October 26, 2026
databasesbashdatabase

Security analysis

A100/100

Scanned October 26, 2026

npx -y skills add JoshuaShepherd/my-skills --skill course-ingest --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Course Ingest?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Course Ingest
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/joshuashepherd-course-ingest-my-skills/badge)](https://www.skillsdirectory.com/skills/joshuashepherd-course-ingest-my-skills)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: course-ingest
description: Ingest course content from markdown files into the database. Parses manifest + module files, validates, and upserts course_lessons, reflection_questions, discussion_prompts, and exercises.
user-invocable: true
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
---

Ingest course content: $ARGUMENTS

$ARGUMENTS should be a course slug or path to the content directory. If empty, ask the user.

## Before Starting

1. Read any existing ingestion scripts in `scripts/` to understand patterns (e.g., `scripts/ingest-mdna-course.ts`)
2. Read `src/lib/database/schema.ts` for table definitions (courses, courseWeeks, courseLessons, reflectionQuestions, discussionPrompts, exercises)
3. Read `src/lib/schemas/course-learn.ts` for SECTION_TYPES
4. Read `src/lib/config/lesson-types.ts` for type mapping

## Locate Content Source

Search for content in these locations (in order):

1. `content-library/courses/[slug]/` — project content library
2. `/Users/joshuashepherd/Desktop/Dev/repos/mdna-course/docs/` — standalone course repo (for mdna)
3. A path provided directly by the user

Look for:
- `course-manifest.json` — lesson order, types, week structure
- `module-*.md` or `week-*.md` — lesson body content
- Any structured markdown with `## Lesson` or `## M.N.X` headings

## Content Parsing

### Manifest Format (expected)

```json
{
  "courseTitle": "The mDNA",
  "weeks": [
    {
      "id": 1,
      "title": "Introduction & Orientation",
      "outcomes": ["..."],
      "lessons": [
        { "id": "lesson-01", "title": "Course Overview", "type": "welcome", "sidebarLabel": "Welcome" }
      ]
    }
  ]
}
```

### Markdown Parsing

For each `module-*.md` or `week-*.md` file:
1. Split on `## Lesson` or `## M` headings
2. Extract per-lesson: id (from heading), title, type (from `**Type:**` line), body content
3. Convert markdown body to HTML using the same approach as existing scripts
4. 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:

1. [ ] All manifest lesson IDs exist in the markdown files
2. [ ] No empty lesson bodies (flag but allow override)
3. [ ] No placeholder text ("[Content TBD]", "[TODO]", "Lorem ipsum")
4. [ ] All lesson types are valid SECTION_TYPES
5. [ ] 8 weeks present (numbered 1-8, no Week 0)
6. [ ] Reflection sections have 6-8 questions each (warn if outside range)

Report validation results and ask for confirmation before proceeding.

## Database Operations

### Create or update the ingestion script

Create/update `scripts/ingest-[slug]-course.ts` that:

1. Connects via project Drizzle setup (`src/lib/database/`)
2. Uses `getTenantOrgId()` for organization scoping
3. Upserts in this order:
   - `courses` row (find by slug, update if exists, insert if not)
   - `courseWeeks` rows (find by course_id + week_number, upsert)
   - `courseLessons` rows (find by course_id + slug or section_order, upsert)
   - `reflectionQuestions` rows (find by lesson_id + block_order, upsert)
   - `discussionPrompts` rows (find by lesson_id + block_order, upsert)
   - `exercises` rows (find by lesson_id + block_order, upsert)
4. Reports: rows created, rows updated, rows skipped

### Run the script

Execute with: `pnpm tsx scripts/ingest-[slug]-course.ts`

## Post-Ingest Verification

After ingestion:
1. Query the course to verify section count matches manifest
2. Verify child items (reflection questions, discussion prompts, exercises) are linked correctly
3. 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.