diff --git a/.claude/commands/learn.md b/.claude/commands/learn.md index fbb9fef..90b948c 100644 --- a/.claude/commands/learn.md +++ b/.claude/commands/learn.md @@ -66,6 +66,7 @@ Create `~/skill-tutor-tutorials/learner_profile.md` with their answers. |-----------|--------| | $ARGUMENTS = lesson number (e.g. `3.2`) | Read `.claude/commands/learn/teaching.md` | | $ARGUMENTS = "status" | Read `.claude/commands/learn/status.md` | +| $ARGUMENTS = "slides" or "slides [lesson]" | Read `.claude/commands/learn/slides.md` | | $ARGUMENTS empty | Read `.claude/commands/learn/resume.md` | | "quiz me" trigger | Read `.claude/commands/learn/quiz.md` | | "stop" trigger | Read `.claude/commands/learn/progress.md` | @@ -86,3 +87,7 @@ Create `~/skill-tutor-tutorials/learner_profile.md` with their answers. | stop | Read `.claude/commands/learn/progress.md`, then summarize | | read aloud | Use TTS helper | | settings | Show current settings | +| detail 1 | Switch to detail level 1 (very brief summaries) | +| detail 2 | Switch to detail level 2 (slightly compressed — default) | +| detail 3 | Switch to detail level 3 (full depth, may add bullet points) | +| slides | Read `.claude/commands/learn/slides.md` — verbatim slide reading mode | diff --git a/.claude/commands/learn/slides.md b/.claude/commands/learn/slides.md new file mode 100644 index 0000000..daf9438 --- /dev/null +++ b/.claude/commands/learn/slides.md @@ -0,0 +1,89 @@ +# Slides Module + +*Loaded when the learner types `/learn slides` or "slides" during a session. Switches to verbatim slide reading mode.* + +Respond in `session.language` throughout. + +--- + +## Overview + +Slides Mode reads the course script verbatim — no Journey Format, no reformulation. The learner hears the slides exactly as written, translated to `session.language` if needed. Exercises are still written by the tutor (not taken from the exercises file verbatim). + +--- + +## Step 1 — Resolve Lesson + +If a lesson number was specified (e.g. `/learn slides 3.1`), load that lesson's script file. + +If no lesson number was given, use the current lesson already loaded in the session. If no lesson is active, ask the learner which lesson they want. + +Resolve the script path using the same priority as `teaching.md`: +1. Check `./lessons/` at project root +2. Fallback to `course.path` from settings + +Split the script by `[מעבר שקף]`. + +--- + +## Step 2 — Announce Mode + +Tell the learner: +> "Switching to Slides Mode. I'll read each slide verbatim. Say **next** to advance, **stop slides** to return to teaching mode, or **exercises** to get the exercises for this lesson." + +--- + +## Step 3 — Launch Viewer + +On entry to Slides Mode, start the slide server and generate the HTML viewer: + +1. **Start the server** (if not already running on port 7823): + ```powershell + Start-Process powershell -ArgumentList "-NoProfile -File `"C:\Users\yuval\Tov-learn\.claude\scripts\slide-server.ps1`"" -WindowStyle Normal + Start-Sleep -Seconds 2 + ``` + +2. **Resolve the lesson's absolute path** on disk: + - Slide images are at: `C:\Users\yuval\ai-track\courses\ai-engineer\lessons\[module]\[lesson]\digital-course-screenshots\` + - TTS scripts are at: `C:\Users\yuval\ai-track\courses\ai-engineer\lessons\[module]\[lesson]\digital-course-tts-scripts\` + +3. **Generate and open the viewer**: + ```powershell + & "C:\Users\yuval\Tov-learn\.claude\scripts\generate-slideshow.ps1" -LessonPath "ABSOLUTE_LESSON_PATH" + Start-Process "$env:TEMP\tov_slideshow.html" + ``` + +--- + +## Step 4 — Per-Slide Loop + +Repeat this sequence for every slide, starting at slide 1: + +**a. Display text** — show the verbatim slide content (translated to `session.language`). + +**b. Fire TTS — MANDATORY, do not skip.** Immediately after displaying the text, run: +```powershell +Invoke-RestMethod -Uri "http://localhost:7823/" -Method POST -Body "SLIDE_NUMBER" | Out-Null +``` +Replace `SLIDE_NUMBER` with the current slide number (integer). This tells the server to speak the slide and sync the viewer. TTS fires automatically on every slide — the learner should never have to ask for it. + +**c. Wait** for the learner to say **next** before advancing. + +**Do not** apply Journey Format. **Do not** ask thinking questions between slides. + +--- + +## Step 4 — Exercises + +When the learner says **exercises**: +- Do NOT read from the exercises file verbatim +- Write fresh, interactive exercises based on the slide content covered so far +- Follow the exercise design rule: most exercises completable through Claude directly; external tool exercises only when the lesson topic is that specific tool + +--- + +## Step 5 — Exit Slides Mode + +When the learner says **stop slides** or **teaching mode**: +- Return to the teaching module (`teaching.md`) at the slide where they left off +- Resume in the previously set `session.detail_level` diff --git a/.claude/commands/learn/teaching.md b/.claude/commands/learn/teaching.md index febf65f..14d7c1d 100644 --- a/.claude/commands/learn/teaching.md +++ b/.claude/commands/learn/teaching.md @@ -33,29 +33,53 @@ Greet the learner. State the lesson topic and number of sections. **If a progress file exists for this lesson:** Tell the learner their previous score and ask if they want to restart or jump to a quiz on what was already covered. -**Otherwise:** Ask what they already know about the topic, if anything. +**Otherwise:** Ask what they already know about the topic, if anything. Also ask which detail level they prefer: +- **detail 1** — very brief, one or two sentences per slide +- **detail 2** — slightly compressed (default) +- **detail 3** — full depth, may add extra bullet points -Use their answer plus `learner_profile.md` to calibrate depth. Offer to skip sections they clearly already know. +Store the chosen level as `session.detail_level` (default: 2). + +Use their answers plus `learner_profile.md` to calibrate depth. Offer to skip sections they clearly already know. *(Speak greeting if TTS enabled)* --- -## Step 4 — Teach Each Section +## Step 4 — Cover Every Slide + +**IMPORTANT: Every slide (section split by `[מעבר שקף]`) MUST be represented in the lesson output.** Never silently skip a slide. At minimum, include one sentence summarizing it. + +For every slide, apply the **Journey Format** scaled by `session.detail_level`: + +### Detail Level 1 — Very Brief +- 1–2 sentences max per slide +- State the core idea only +- No question, no context example +- Good for fast review passes -For every section, use the **Journey Format**: +### Detail Level 2 — Standard (default) +Use the full Journey Format: +1. **The problem** — one sentence +2. **The insight** — 2–3 sentences in your own words, not copied from the script +3. **In your context** — one concrete connection to the learner's project or a real example +4. **Question** — one thinking question; wait for an answer before continuing -1. **The problem** — What problem does this section solve? Why is it hard without it? (One sentence) -2. **The insight** — What do experts understand that beginners don't? (2–3 sentences, in your own words — not copied from the script) -3. **In your context** — How does this connect to the learner's project from their profile, or a relevant real-world example -4. **Question** — Ask one thinking question. Not trivia. Wait for an answer before continuing. +Each block: max 5 sentences total. + +### Detail Level 3 — Full Depth +Use the full Journey Format plus: +- Expand the insight to 4–5 sentences +- Add bullet points for key facts, numbers, or comparisons from the slide +- May include one extra "did you know" point not in the script if genuinely relevant +- Still ask a question and wait for an answer + +**The learner can switch levels at any time** by saying "detail 1", "detail 2", or "detail 3". **Responding to answers:** -- Correct → brief acknowledgment + "Shall we continue?" +- Correct → brief acknowledgment + continue - Partial/wrong → one hint → let them try again → then explain -Each teaching block: max 5 sentences. The learner should write more than you. - *(Speak each teaching block if TTS enabled — strip markdown before speaking)* --- diff --git a/.claude/scripts/auto-save-progress.ps1 b/.claude/scripts/auto-save-progress.ps1 new file mode 100644 index 0000000..0de533a --- /dev/null +++ b/.claude/scripts/auto-save-progress.ps1 @@ -0,0 +1,43 @@ +# Auto-save progress when Claude session ends. +# Reads lesson/slide from TTS temp files and writes to skill-tutor-tutorials/progress/. +# Runs silently — no output on success. + +$lessonFile = "$env:TEMP\tov_current_lesson.txt" +$slideFile = "$env:TEMP\tov_current_slide.txt" +$progressDir = "$env:USERPROFILE\skill-tutor-tutorials\progress" + +if (!(Test-Path $lessonFile)) { exit 0 } +$lessonPath = (Get-Content $lessonFile -Raw -Encoding UTF8).Trim() +if (!$lessonPath) { exit 0 } + +$lessonFolder = Split-Path $lessonPath -Leaf +$lessonNum = if ($lessonFolder -match '^(\d+\.\d+)') { $Matches[1] } else { $lessonFolder } + +$slide = if (Test-Path $slideFile) { (Get-Content $slideFile -Raw).Trim() } else { "1" } +$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm" + +if (!(Test-Path $progressDir)) { New-Item -ItemType Directory -Path $progressDir -Force | Out-Null } + +$progressFile = "$progressDir\lesson-$lessonNum.md" + +# Read existing file to preserve quiz scores and notes +$existing = if (Test-Path $progressFile) { Get-Content $progressFile -Raw -Encoding UTF8 } else { "" } + +# Replace or prepend the auto-save block +$autoSaveBlock = @" + +**שיעור:** $lessonNum +**שקף אחרון:** $slide +**זמן:** $timestamp +**נתיב:** $lessonPath + + +"@ + +if ($existing -match '(?s).*?') { + $updated = $existing -replace '(?s).*?\r?\n?', $autoSaveBlock +} else { + $updated = $autoSaveBlock + $existing +} + +[System.IO.File]::WriteAllText($progressFile, $updated, (New-Object System.Text.UTF8Encoding($true))) diff --git a/.claude/scripts/generate-slideshow.ps1 b/.claude/scripts/generate-slideshow.ps1 new file mode 100644 index 0000000..fbcf974 --- /dev/null +++ b/.claude/scripts/generate-slideshow.ps1 @@ -0,0 +1,207 @@ +# Generates tov_slideshow.html for a given lesson +# Usage: .\generate-slideshow.ps1 -LessonPath "ABSOLUTE\PATH\TO\LESSON" +# The LessonPath must contain digital-course-screenshots\ and digital-course-tts-scripts\ + +param( + [string]$LessonPath +) + +$slidesDir = Join-Path $LessonPath "digital-course-screenshots" +$slides = Get-ChildItem "$slidesDir\slide-*.png" | Sort-Object Name + +if ($slides.Count -eq 0) { + Write-Error "No slides found in $slidesDir" + exit 1 +} + +# Build slide tags +$slideImgTags = "" +$i = 1 +foreach ($slide in $slides) { + $path = $slide.FullName -replace '\\', '/' + $display = if ($i -eq 1) { "block" } else { "none" } + $slideImgTags += " `n" + $i++ +} + +$total = $slides.Count + +# Write lesson path for the TTS server to preload +[System.IO.File]::WriteAllText("$env:TEMP\tov_current_lesson.txt", $LessonPath, [System.Text.Encoding]::UTF8) +[System.IO.File]::WriteAllText("$env:TEMP\tov_current_slide.txt", "1", [System.Text.Encoding]::UTF8) + +$html = @" + + + + +Tov-learn + + + + + +
+ +$slideImgTags +
+
+ +
+ +
+
+ +
+
+ + + + +"@ + +$outPath = "$env:TEMP\tov_slideshow.html" +[System.IO.File]::WriteAllText($outPath, $html, (New-Object System.Text.UTF8Encoding($true))) +Write-Host $outPath diff --git a/.claude/scripts/slide-server.ps1 b/.claude/scripts/slide-server.ps1 new file mode 100644 index 0000000..f51799a --- /dev/null +++ b/.claude/scripts/slide-server.ps1 @@ -0,0 +1,125 @@ +# Tov-learn Slide Server — localhost:7823 +# GET / → returns current slide number +# POST / {num} → advance to slide N + speak TTS +# POST / pause → pause current TTS in place +# POST / resume → resume from exact pause point +# POST / stop → stop current TTS + +param([int]$Port = 7823) + +$slideFile = "$env:TEMP\tov_current_slide.txt" +$lessonFile = "$env:TEMP\tov_current_lesson.txt" +$ttsTextFile = "$env:TEMP\tov_tts_text.txt" +$ttsRunFile = "$env:TEMP\tov_tts_run.ps1" +$controlFile = "$env:TEMP\tov_tts_control.txt" + +if (!(Test-Path $slideFile)) { "1" | Set-Content $slideFile } + +$script:ttsProcess = $null +$script:loadedLesson = "" +$script:ttsScripts = @{} + +function Load-Lesson { + param([string]$lessonPath) + if ($lessonPath -eq $script:loadedLesson) { return } + $script:ttsScripts = @{} + $ttsDir = Join-Path $lessonPath "digital-course-tts-scripts" + if (Test-Path $ttsDir) { + Get-ChildItem "$ttsDir\slide-*.txt" | ForEach-Object { + $num = [int]($_.BaseName -replace 'slide-', '') + $script:ttsScripts[$num] = (Get-Content $_.FullName -Raw -Encoding UTF8).Trim() + } + Write-Host "Preloaded $($script:ttsScripts.Count) TTS scripts from $lessonPath" + } + $script:loadedLesson = $lessonPath +} + +function Kill-TTS { + [System.IO.File]::WriteAllText($controlFile, "stop") + Start-Sleep -Milliseconds 200 + if ($script:ttsProcess -and !$script:ttsProcess.HasExited) { + try { $script:ttsProcess.Kill() } catch {} + $script:ttsProcess = $null + } +} + +function Speak-Slide { + param([int]$num) + Kill-TTS + + if (Test-Path $lessonFile) { + $lesson = (Get-Content $lessonFile -Raw -Encoding UTF8).Trim() + Load-Lesson $lesson + } + + $text = $script:ttsScripts[$num] + if (!$text) { return } + + [System.IO.File]::WriteAllText($ttsTextFile, $text, (New-Object System.Text.UTF8Encoding($true))) + [System.IO.File]::WriteAllText($controlFile, "play") + + $runner = @" +Add-Type -AssemblyName System.Speech +`$s = New-Object System.Speech.Synthesis.SpeechSynthesizer +`$hv = `$s.GetInstalledVoices() | Where-Object { `$_.VoiceInfo.Culture.Name -like 'he-*' } | Select-Object -First 1 +if (`$hv) { `$s.SelectVoice(`$hv.VoiceInfo.Name) } +`$t = [System.IO.File]::ReadAllText('$ttsTextFile', [System.Text.Encoding]::UTF8) +`$controlFile = '$controlFile' +`$chunks = [System.Text.RegularExpressions.Regex]::Split(`$t, '(?<=[.!?])\s+|\r?\n') | Where-Object { `$_.Trim() -ne '' } +foreach (`$chunk in `$chunks) { + while (`$true) { + `$ctrl = ([System.IO.File]::ReadAllText(`$controlFile)).Trim() + if (`$ctrl -eq 'stop') { exit } + if (`$ctrl -ne 'pause') { break } + Start-Sleep -Milliseconds 200 + } + `$s.Speak(`$chunk) +} +"@ + [System.IO.File]::WriteAllText($ttsRunFile, $runner, (New-Object System.Text.UTF8Encoding($true))) + $script:ttsProcess = Start-Process powershell -ArgumentList "-NoProfile -WindowStyle Hidden -File `"$ttsRunFile`"" -PassThru +} + +if (Test-Path $lessonFile) { + $lesson = (Get-Content $lessonFile -Raw -Encoding UTF8).Trim() + Load-Lesson $lesson +} + +$listener = New-Object System.Net.HttpListener +$listener.Prefixes.Add("http://localhost:$Port/") +$listener.Start() +Write-Host "Slide server running on http://localhost:$Port" + +while ($listener.IsListening) { + try { + $ctx = $listener.GetContext() + $res = $ctx.Response + $res.Headers.Add("Access-Control-Allow-Origin", "*") + $res.Headers.Add("Cache-Control", "no-cache") + + if ($ctx.Request.HttpMethod -eq "POST") { + $reader = New-Object System.IO.StreamReader($ctx.Request.InputStream) + $body = $reader.ReadToEnd().Trim() + + if ($body -eq "stop") { + Kill-TTS + } elseif ($body -eq "pause") { + [System.IO.File]::WriteAllText($controlFile, "pause") + } elseif ($body -eq "resume") { + [System.IO.File]::WriteAllText($controlFile, "resume") + } else { + $num = [int]$body + "$num" | Set-Content $slideFile + Speak-Slide $num + } + $buf = [System.Text.Encoding]::UTF8.GetBytes("ok") + } else { + $cur = if (Test-Path $slideFile) { (Get-Content $slideFile).Trim() } else { "1" } + $buf = [System.Text.Encoding]::UTF8.GetBytes($cur) + } + + $res.ContentLength64 = $buf.Length + $res.OutputStream.Write($buf, 0, $buf.Length) + $res.OutputStream.Close() + } catch { } +} diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..9120472 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "powershell -NoProfile -WindowStyle Hidden -File \"C:\\Users\\yuval\\Tov-learn\\.claude\\scripts\\auto-save-progress.ps1\"" + } + ] + } + ] + } +} diff --git a/CLAUDE.md b/CLAUDE.md index b67ae13..b672392 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,6 +18,9 @@ | Progress | `.claude/commands/learn/progress.md` | שמירת tutorials, knowledge map | | Status | `.claude/commands/learn/status.md` | HTML dashboard — all lessons, scores, due reviews | | Project Analysis | `.claude/commands/learn/project-analysis.md` | סריקת קוד, ראיון ארכיטקט, מפת HTML | +| Slides | `.claude/commands/learn/slides.md` | קריאה מילה במילה מסקריפטים, viewer אינטראקטיבי, TTS אוטומטי | + +סקריפטים תומכים נמצאים ב-`.claude/scripts/` (PowerShell — slide server, HTML generator). **כלל פיתוח:** כל מודול עצמאי — לא מניחים שמודול אחר כבר נטען. מידע שמודול צריך — הוא קורא בעצמו. TTS helper מוגדר ב-`learn.md` ונטען לפני כל מודול. diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..4b6528d --- /dev/null +++ b/FAQ.md @@ -0,0 +1,70 @@ +# שאלות נפוצות — Tov-learn + +--- + +## קול ו-TTS + +### איך מוסיפים קול בעברית למערכת? + +מערכת ה-TTS של Tov-learn משתמשת בקולות Windows SAPI. ברירת המחדל היא לחפש קול עברי אוטומטית — אם אין קול עברי מותקן, הדיקלום לא יעבוד. + +**שלב 1 — התקנת חבילת שפה עברית:** + +1. פתחו **הגדרות** ← **זמן ושפה** ← **שפה ואזור** +2. לחצו **הוסף שפה** וחפשו **עברית** +3. לאחר ההתקנה, לחצו על **עברית** ← **אפשרויות שפה** +4. מצאו את **המרת טקסט לדיבור** ולחצו **הורד** + +**שלב 2 — חיבור הקול ל-SAPI (נדרש פעם אחת):** + +Windows מתקין קולות חדשים תחת "OneCore" שאינו נגיש ישירות ל-SAPI. יש להריץ את הפקודה הבאה ב-PowerShell **כמנהל** (Admin): + +```powershell +$src = "HKLM:\SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens\MSTTS_V110_heIL_Asaf" +$dst = "HKLM:\SOFTWARE\Microsoft\Speech\Voices\Tokens\MSTTS_V110_heIL_Asaf" +function Copy-RegKey($srcPath, $dstPath) { + $srcKey = Get-Item $srcPath + New-Item -Path $dstPath -Force | Out-Null + foreach ($val in $srcKey.GetValueNames()) { + $data = $srcKey.GetValue($val, $null, "DoNotExpandEnvironmentNames") + $kind = $srcKey.GetValueKind($val) + Set-ItemProperty -Path $dstPath -Name $val -Value $data -Type $kind + } + foreach ($sub in $srcKey.GetSubKeyNames()) { + Copy-RegKey "$srcPath\$sub" "$dstPath\$sub" + } +} +Copy-RegKey $src $dst +Write-Host "הקול אסף חובר בהצלחה" +``` + +**שלב 3 — בדיקה:** + +```powershell +Add-Type -AssemblyName System.Speech +$s = New-Object System.Speech.Synthesis.SpeechSynthesizer +$s.GetInstalledVoices() | ForEach-Object { $_.VoiceInfo.Name + " | " + $_.VoiceInfo.Culture.Name } +``` + +אם מופיע `Microsoft Asaf | he-IL` — הכל מוכן. + +--- + +### אילו קולות נתמכים? + +| קול | שפה | סטטוס | +|-----|-----|--------| +| Microsoft Asaf | עברית (ישראל) | מומלץ | +| Microsoft David | אנגלית (ארה"ב) | ברירת מחדל אם אין עברית | + +המערכת תמיד מנסה לבחור קול עברי ראשון. אם לא נמצא — תדובר השפה הזמינה (ייתכן שלא יקרא עברית נכון). + +--- + +### ה-TTS מדלג על הטקסט העברי ומדבר רק מילים באנגלית — מה קורה? + +זה קורה כשאין קול עברי מותקן. הקול האנגלי קורא רק את האותיות הלטיניות ומדלג על כל האות העברי. + +**פתרון:** התקינו קול עברי לפי השלבים למעלה. + +--- diff --git a/changes.md b/changes.md index 4943121..978487b 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,82 @@ +# Changes — yuval_ver branch + +## Overview + +Three additions to the `/learn` skill system, focused on giving the learner more control over how they experience course content. + +--- + +## 1. Detail Levels (`detail 1 / 2 / 3`) + +**What changed:** `teaching.md`, `learn.md` + +The learner can now type `detail 1`, `detail 2`, or `detail 3` at any point during a session to change how deeply Claude explains each slide. + +| Level | Behavior | +|-------|----------| +| `detail 1` | 1–2 sentences per slide — just the core idea, no follow-up question | +| `detail 2` | Full Journey Format, max 5 sentences — the default | +| `detail 3` | Journey Format + extended insight + bullet points with extra context | + +Claude now asks for the preferred detail level at the start of each session so learners don't have to discover this themselves. + +**Why:** Some learners are already familiar with parts of the material and want to move faster. Others want a deeper dive. This makes the tutor adapt to the learner rather than forcing everyone through the same pace. + +--- + +## 2. Cover Every Slide Rule + +**What changed:** `teaching.md` + +Claude is now required to cover every slide in the lesson — no skipping, no merging slides silently. If a slide is short or repetitive, it may summarize briefly (according to the active detail level), but it must represent every slide. + +**Why:** Previously Claude could skip slides it deemed redundant. Learners were missing content without knowing it. + +--- + +## 3. Slides Mode (`/learn slides` or `slides` command) + +**What changed:** `learn.md` (routing), new file `slides.md`, new scripts `slide-server.ps1` + `generate-slideshow.ps1` + +A new mode the learner can switch into at any time. Instead of Claude's teaching format, the learner sees and hears the actual course slides verbatim. + +**How it works:** +- Claude opens an interactive browser window showing the real slide images from the course +- The existing הבא / הקודם buttons in the slide images are made clickable via transparent overlays +- Extra controls are added: ⏸ השהה (pause TTS) and ⛶ מסך מלא (fullscreen) +- A local server (`localhost:7823`) keeps the browser viewer and Claude in sync — if Claude advances, the browser follows, and vice versa +- TTS reads the official course script for each slide automatically — no manual trigger needed +- Exercises in this mode are still written fresh by Claude (not pulled verbatim from the exercises file) + +**Learner commands in this mode:** + +| Command | Action | +|---------|--------| +| `next` | Advance to next slide | +| `stop slides` | Return to teaching mode at the current slide | +| `exercises` | Get fresh exercises based on slides covered so far | + +**Supporting scripts** (in `.claude/scripts/`): +- `slide-server.ps1` — preloads all TTS scripts at startup, speaks via Windows TTS on every slide change, handles pause/resume +- `generate-slideshow.ps1` — generates the HTML viewer for any lesson, positions click overlays over the existing nav buttons in the slide images + +**Why:** Some learners want to experience the slides the way they were designed — visually, with the recorded voiceover — rather than Claude's reformulation. This mode gives them that while keeping Claude available for exercises and Q&A. + +--- + +## File Map + +| File | Status | Purpose | +|------|--------|---------| +| `.claude/commands/learn.md` | Modified | Added slides routing + detail 1/2/3 and slides to learner commands | +| `.claude/commands/learn/teaching.md` | Modified | Cover-every-slide rule, detail levels, ask preference at session start | +| `.claude/commands/learn/slides.md` | New | Slides Mode — verbatim reading, viewer launch, TTS loop | +| `.claude/scripts/generate-slideshow.ps1` | New | Generates interactive HTML slide viewer | +| `.claude/scripts/slide-server.ps1` | New | Local TTS + slide state server on localhost:7823 | +| `CLAUDE.md` | Modified | Added Slides to modules table, noted scripts folder | + +--- + # Changes — sean_changes branch Modifications made while testing the `/learn` user flow on the AI Dev course (lesson 1.1) and streamlining the repo. @@ -44,4 +123,4 @@ Modifications made while testing the `/learn` user flow on the AI Dev course (le - A normal `quiz me` can mark a lesson as "Mastered" (score 8+) even if only a few sections were covered. Consider requiring full coverage — or `quiz me full` — before granting mastery in the knowledge map. - still need to add a FAQ and maybe a walkthrough to help with onboarding. -- also to teach about adding RTL support for claude code https://marketplace.visualstudio.com/items?itemName=yechielby.claude-code-rtl \ No newline at end of file +- also to teach about adding RTL support for claude code https://marketplace.visualstudio.com/items?itemName=yechielby.claude-code-rtl