An agent skill (/tps) that puts what the lecturer said under each slide it was said on.
Give it slide PDFs and lecture-recording transcripts. For each lecture it writes one Markdown study pack:
- each slide's image,
- under it, that slide's stretch of the recording, rewritten into clean notes in the lecturer's own voice,
- under that, a tutoring block where the agent's answers to your questions about the slide are kept, if you want them kept, with figures drawn when words are not enough.
Anything that affects your marks or what you have to do (deadlines, mark weights, required actions) gets a ★ and a list at the top, so it doesn't get lost in a two-hour recording.
Students who missed the lecture and don't want to choose between reading bare slides and sitting through the whole recording. With a pack, you can:
- read three slides on the bus and stop,
- spend an hour on the one slide you don't get, without the lecture running on,
- jump around and follow whatever you're curious about. In a live lecture, that is how you fall behind.
See a complete example pack (a short demo lecture written for this repo, including a tutoring note with a figure).
The skill is this whole folder, in the Agent Skills
format (SKILL.md plus scripts/ and references/).
The skill is called tps, so the folder must be named tps.
Claude Code, as a plugin (easiest to keep up to date). In a Claude Code session:
/plugin marketplace add RuiquanQiao/transcript-per-slide
/plugin install tps@transcript-per-slide
Plugin skills carry the plugin's name as a prefix, so the command shows up as
/tps:tps. You can also just ask ("pack my new lecture").
To update: run /plugin marketplace update transcript-per-slide, or turn on
Enable auto-update for this marketplace under Marketplaces in
/plugin. Every new commit counts as an update. Your packs and notes live in
your course folder, so updates never touch them.
Claude Code, as a plain skill, for one course:
git clone https://github.com/RuiquanQiao/transcript-per-slide <course>/.claude/skills/tpsOr for all your courses, clone it into ~/.claude/skills/tps. To update a
cloned copy, run git pull in that folder.
Cursor: clone into <course>/.cursor/skills/tps.
Other agents that read SKILL.md: put the folder, named tps, wherever they load skills from.
You also need Python 3.11+ and PyMuPDF:
python -m pip install -r requirements.txtPut your files in a course folder:
my-course/
slides/ lecture PDFs, named however your course names them
transcripts/ .txt (Echo360 "SPEAKER N" or plain), .vtt, .srt, or Whisper .json
Those folder names are only a suggestion. If your slides are in ppt/ or
Week 3/, or everything sits loose in the course folder, the agent looks
through the folder, works out from the files' contents which ones are slides
and which are transcripts, and records that in the config. If something is
missing (no transcripts, or .pptx that needs exporting to PDF, or only a
video), it tells you what to add before packing.
Then, in that folder, run /tps. On the first run it sets things up; after
that, it packs every lecture that is new.
Everything else is just /tps plus whatever you want, in your own words and
any language. There is no syntax to remember. For example:
| You type | What happens |
|---|---|
/tps |
Pack every new lecture (first run: set up) |
/tps 12 why is this O(n log n)? |
Explain slide 12 of the lecture you're on, and write it under the slide |
/tps the bit about the midpoint overflow |
Find that slide, then explain it |
/tps draw slide 5, I can't picture it |
Draw a figure, check it renders, put it under slide 5 |
/tps keep that |
Put the explanation you just got in chat under its slide |
/tps lec3.1 slides were updated |
Re-pack that lecture (asks first; your tutoring stays) |
/tps what's left? |
What is packed, half-done, or not paired yet |
Without /tps, questions are answered in chat. The agent then offers to save
the answer under the slide, so you decide what goes on the page.
Packs land in study/<lecture>.md, one file per lecture and nothing else
in that folder. Slide images, figures and working files go to build/; the
packs link to them, so images still show. Open packs in any Markdown viewer
(VS Code, Obsidian, GitHub). A course packed by an older version (with
study/<lecture>/pack.md) is moved over by migrate.py; /tps offers it.
The work splits along one line: the agent does everything that needs
understanding; the scripts in scripts/ do everything that doesn't.
| Step | Who | Why |
|---|---|---|
| Find every folder holding slides, transcripts, decks or recordings | locate.py |
mechanical |
| Decide which folders are slides and which are transcripts | agent, from the files' content | folder names lie too (ppt/, Week 3/, everything in one place) |
| List PDFs and transcripts with previews | inventory.py |
mechanical |
| Decide which recording belongs to which deck | agent, by reading title slides and opening lines | filenames lie (lec1.1 vs Lecture 1-1 vs Echo360_0812) |
| Render slide PNGs, cut the transcript into numbered segments | extract.py |
mechanical; caption files are also cut at pauses, where slide changes usually happen |
| Assign segments to slides, name the lecture's parts, write notes, flag notices | agent | needs to follow the lecture, not match keywords |
| Check every slide is covered, segment order, missing notes, cut-off recordings | validate.py |
catches agent mistakes before they reach the page |
Write study/<lecture>.md, keeping tutoring blocks |
render.py |
re-rendering never loses your notes |
| Explain stuck slides, draw figures | agent | needs to know what you misunderstood |
| Rasterize figures so the agent can look at them | figure.py |
headless Chrome/Edge (what Markdown viewers show), PyMuPDF fallback; a parser accepting an SVG proves nothing |
Lectures that already have a pack are never touched again unless you ask for a redo, so a new week's files don't disturb old notes.
Which deck goes with which recording is kept in build/pairs.json, along with
a fingerprint of each file's content. Rename or move your files whenever you
like. The next /tps finds them by content and updates the record, and the
lecture keeps its pack and your notes. If the lecturer replaces a deck or
recording under the same name, /tps notices and asks whether to redo that
pack, and keeps asking until you decide.
When the scripts can't cope, the agent takes over. Real course folders
are messy: .pptx instead of PDF, Teams transcripts as .docx, a .txt
full of timestamps, a PDF saved without .pdf, a login page saved as
slides.pdf, a GBK-encoded file, a scanned deck. The scripts read files by
what's inside rather than what they're called, and anything they can't handle
they don't crash on or skip. They print a NEEDS AGENT [code] block and exit
with status 3. The skill tells the agent to act on it straight away, following
references/handoffs.md: fix the config, write a
readable copy into build/_converted/ (your own files are never touched),
read titles off a scanned deck, or ask you for exactly the one thing it
can't do itself.
Every script also works on its own; run any of them with --help. Also there:
init.py (config + a README for the study folder), locate.py (where the
files are) and status.py. Tests: python -m unittest discover tests.
transcript-per-slide.toml in the course folder (created by init.py; every key
is optional). See the example.
Highlights:
slides.footer_patterns: regexes for footers and page counters, so slide titles come out cleantranscript.asr_fixes: words your captioning keeps getting wrong (cash→cache)transcript.skip_speakers: e.g. the speaker that carries a copyright noticepaths.*: where slides, transcripts and packs live (the agent fills these in on the first run if your folders aren'tslides/andtranscripts/)
Lecture slides and recordings usually belong to your university. Keep packs for
your own study, and don't commit course materials to a public repository. The
demo lecture in examples/ was written for this repo.
MIT