npm install
npm run build
node dist/index.jsnpm run build # Compile TypeScript
npm run dev # Watch mode
npm test # Run tests (vitest)
node dist/index.js # Run the app
node dist/index.js --reset # Reset progress
node dist/index.js --lesson 3.1 # Jump to lesson
npx tsx scripts/validate-course.ts # Validate all 195 lessons across 3 locales- Ink v5 with React 18 — all UI is React components rendered to terminal
- ESM only — all imports use
.jsextensions (TypeScript convention for ESM) - i18n — 3 locales (es/en/fr). UI strings in
src/i18n/, lesson content in each module'sindex.tsvia factory functionsgetModuleXLessons(locale) - Progress stored in
~/.tuilder/progress.json. UsemutateProgress(fn)for atomic read-mutate-write - Lessons are data-driven: each lesson is a
Lessonobject with content fields + avalidate()function - Interactive types:
command(single input),practiceSteps(multi-step challenges),quizQuestions,agent-simulation,controls-tutorial
ui()returns current locale's UI strings.fmt(template, vars)for interpolationcolorize(line, theme)renders terminal-like output (diffs, prompts, folders)playSound(name)plays macOS system sounds, no-op elsewhereaddXp(),completeLesson()viamutateProgress()— never callsaveProgress()directlyCommandStep.acceptPattern— optional regex for flexible validation- Lessons per module are in
src/lessons/XX-name/index.ts, exported asgetModuleXLessons(locale: Locale): Lesson[]
- Add content to the module's
index.tsin ALL 3 locales (es, en, fr) - Add
msgentries for validation messages in all 3 locales - Wire the lesson in the factory function (
getModuleXLessons) - Ensure
idfollows patternX.Y(module.lesson) - Run
npx tsx scripts/validate-course.tsto confirm cross-locale consistency
- Create
src/lessons/XX-name/index.ts - Export
getModuleXLessons(locale: Locale): Lesson[] - Import and register in
src/lessons/index.ts - Update total lesson counts in README if needed
- Add the key to
src/i18n/types.ts(UIStrings interface) - Add translations in
src/i18n/es.ts,src/i18n/en.ts,src/i18n/fr.ts - Use via
ui().keyNamein components
When writing or editing lessons:
-
conceptexplains WHAT in plain language (no jargon without explanation) -
whyadds MOTIVATION (not a repeat of concept) -
analogyuses real-world comparison (optional but recommended) -
hint1gives a gentle nudge without the answer -
hint2gives the exact command/answer - Quiz answers distributed across positions 0-3 (not all
correct: 0) - Same
correctindex across all 3 locales for each quiz question - Practice steps flow logically — each challenge builds on the previous output
- Commands used in practice were taught in an earlier lesson
-
acceptPatternused for natural language inputs andgit commit -mcommands - No
.tsx, "branch", "diff", "staging", "PR", "boilerplate" without prior explanation - Simulated output is realistic for the command
- Target audience: non-tech (designers, POs, CEOs). NO jargon without explanation
- Every concept needs
concept(what),why(motivation), and optionallyanalogy whymust NOT repeatconcept— it should add new information- Quiz answers must vary positions (not all
correct: 0) - Practice step challenges should guide logically from previous step's output
hint2must ALWAYS give the exact answer to unblock the usergit commit -msteps should useacceptPattern: 'git commit -m .+'(accept any message)- All 3 locales must have same lesson count, same IDs, same
correctindices, sameinteractivetypes
- Don't hardcode strings in components — use
ui()from i18n - Don't use
saveProgress()directly — usemutateProgress(fn)to prevent race conditions - Don't add commands to practice steps that weren't taught in a previous lesson
- Don't assume the user knows what
.tsx, "branch", "diff", "staging", or "PR" means without prior explanation
npm run build # Must compile
npx tsx scripts/validate-course.ts # Must pass (0 errors, 0 warnings)
node dist/index.js --reset && node dist/index.js # Manual walkthrough
node dist/index.js --lesson X.Y # Test specific lesson- TypeScript ESM with
.jsextensions in imports - Components in
src/components/, one component per file - Lesson modules in
src/lessons/XX-name/index.ts - Factory function pattern:
getModuleXLessons(locale): Lesson[] - Locale content as
Record<Locale, Record<string, LessonText>>objects - Semantic color tokens from
theme.ts— never use raw hex in components