Files
2026-07-13 12:09:03 +08:00

138 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 2 ]]; then
cat <<'USAGE' >&2
Usage: scripts/scaffold-lesson.sh <phase-dir> <lesson-slug> [title]
Examples:
scripts/scaffold-lesson.sh 05-nlp-foundations-to-advanced 03-tokenizers
scripts/scaffold-lesson.sh 05-nlp-foundations-to-advanced 03-tokenizers "Tokenizers from Scratch"
Creates phases/<phase-dir>/<lesson-slug>/ with code/, notebook/, docs/, outputs/
and a docs/en.md skeleton prefilled from LESSON_TEMPLATE.md.
USAGE
exit 2
fi
PHASE="$1"
LESSON="$2"
TITLE="${3:-}"
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
if [[ -z "$REPO_ROOT" ]]; then
echo "error: run this from inside the ai-engineering-from-scratch git repo" >&2
exit 1
fi
PHASE_DIR="$REPO_ROOT/phases/$PHASE"
LESSON_DIR="$PHASE_DIR/$LESSON"
if [[ ! -d "$PHASE_DIR" ]]; then
echo "error: phase dir not found: phases/$PHASE" >&2
echo " run: ls phases/ to see valid phases" >&2
exit 1
fi
if [[ -e "$LESSON_DIR" ]]; then
echo "error: lesson already exists: phases/$PHASE/$LESSON" >&2
exit 1
fi
if [[ ! "$LESSON" =~ ^[0-9]{2}-[a-z0-9-]+$ ]]; then
echo "error: lesson slug must match NN-kebab-case (e.g. 03-tokenizers)" >&2
exit 1
fi
mkdir -p "$LESSON_DIR/code" "$LESSON_DIR/notebook" "$LESSON_DIR/docs" "$LESSON_DIR/outputs"
PRETTY_TITLE="$TITLE"
if [[ -z "$PRETTY_TITLE" ]]; then
PRETTY_TITLE="$(echo "${LESSON#[0-9][0-9]-}" | tr '-' ' ' | awk '{for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) substr($i,2);}1')"
fi
PHASE_NUM="${PHASE%%-*}"
LESSON_NUM="${LESSON%%-*}"
cat >"$LESSON_DIR/docs/en.md" <<EOF
# $PRETTY_TITLE
> [One-line motto. The core idea that sticks.]
**Type:** Build
**Languages:** Python
**Prerequisites:** [prior lessons]
**Time:** ~75 minutes
## The Problem
[2-3 paragraphs. What can't a learner do without this? Make it concrete.]
## The Concept
[Intuition first. Diagrams, tables, mental models. No code yet.]
## Build It
### Step 1: [name]
[explanation]
\`\`\`python
# code here
\`\`\`
### Step 2: [name]
[explanation]
\`\`\`python
# code here
\`\`\`
## Use It
[How a real framework solves the same thing. Compare your version.]
## Ship It
[The reusable artifact this lesson produces. Save in outputs/.]
## Exercises
1. [Easy — reinforce core concept]
2. [Medium — apply to a different problem]
3. [Hard — extend or combine with prior lessons]
## Key Terms
| Term | What people say | What it actually means |
|------|----------------|----------------------|
| | | |
## Further Reading
- []() — []
EOF
cat >"$LESSON_DIR/code/main.py" <<'EOF'
def main():
raise NotImplementedError("implement the lesson")
if __name__ == "__main__":
main()
EOF
touch "$LESSON_DIR/notebook/.gitkeep"
touch "$LESSON_DIR/outputs/.gitkeep"
echo "created phases/$PHASE/$LESSON/"
echo ""
echo "next:"
echo " 1. edit phases/$PHASE/$LESSON/docs/en.md"
echo " 2. write phases/$PHASE/$LESSON/code/main.py"
echo " 3. add a markdown-link row to ROADMAP.md under Phase $PHASE_NUM:"
echo " | $LESSON_NUM | [$PRETTY_TITLE](phases/$PHASE/$LESSON) | ✅ | ~75 min |"
echo " 4. atomic commit: git add phases/$PHASE/$LESSON ROADMAP.md && git commit -m \"feat(phase-$PHASE_NUM/$LESSON_NUM): $PRETTY_TITLE\""