source-to-skill 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. source_to_skill-0.7.0/CHANGELOG.md +379 -0
  2. source_to_skill-0.7.0/LICENSE +21 -0
  3. source_to_skill-0.7.0/MANIFEST.in +4 -0
  4. source_to_skill-0.7.0/PKG-INFO +435 -0
  5. source_to_skill-0.7.0/README.md +384 -0
  6. source_to_skill-0.7.0/pyproject.toml +131 -0
  7. source_to_skill-0.7.0/setup.cfg +4 -0
  8. source_to_skill-0.7.0/skills/skill-eval/SKILL.md +77 -0
  9. source_to_skill-0.7.0/skills/skill-library/SKILL.md +49 -0
  10. source_to_skill-0.7.0/skills/skill-library/scripts/library.py +160 -0
  11. source_to_skill-0.7.0/skills/skill-study/SKILL.md +85 -0
  12. source_to_skill-0.7.0/skills/source-to-skill/SKILL.md +388 -0
  13. source_to_skill-0.7.0/skills/source-to-skill/scripts/extract.py +188 -0
  14. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/__init__.py +10 -0
  15. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/config.py +205 -0
  16. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/dependencies.py +114 -0
  17. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/media.py +38 -0
  18. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/__init__.py +0 -0
  19. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/article.py +668 -0
  20. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/audio.py +87 -0
  21. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/book.py +493 -0
  22. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/docs.py +505 -0
  23. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/document.py +712 -0
  24. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/notes.py +365 -0
  25. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/paper.py +623 -0
  26. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/playlist.py +186 -0
  27. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/repo.py +858 -0
  28. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/thread.py +450 -0
  29. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/transcript.py +104 -0
  30. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/youtube.py +226 -0
  31. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/transcribe.py +132 -0
  32. source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/utils.py +245 -0
  33. source_to_skill-0.7.0/skills/source-to-skill/tools/diff_source.py +63 -0
  34. source_to_skill-0.7.0/skills/source-to-skill/tools/validate_skill.py +107 -0
  35. source_to_skill-0.7.0/src/source_to_skill/__init__.py +3 -0
  36. source_to_skill-0.7.0/src/source_to_skill/__main__.py +5 -0
  37. source_to_skill-0.7.0/src/source_to_skill/cli.py +237 -0
  38. source_to_skill-0.7.0/src/source_to_skill.egg-info/PKG-INFO +435 -0
  39. source_to_skill-0.7.0/src/source_to_skill.egg-info/SOURCES.txt +69 -0
  40. source_to_skill-0.7.0/src/source_to_skill.egg-info/dependency_links.txt +1 -0
  41. source_to_skill-0.7.0/src/source_to_skill.egg-info/entry_points.txt +2 -0
  42. source_to_skill-0.7.0/src/source_to_skill.egg-info/requires.txt +32 -0
  43. source_to_skill-0.7.0/src/source_to_skill.egg-info/top_level.txt +1 -0
  44. source_to_skill-0.7.0/tests/conftest.py +27 -0
  45. source_to_skill-0.7.0/tests/fixtures/arxiv_1706.03762.xml +47 -0
  46. source_to_skill-0.7.0/tests/fixtures/sample.vtt +18 -0
  47. source_to_skill-0.7.0/tests/test_article.py +506 -0
  48. source_to_skill-0.7.0/tests/test_audio.py +543 -0
  49. source_to_skill-0.7.0/tests/test_book.py +470 -0
  50. source_to_skill-0.7.0/tests/test_cli_package.py +282 -0
  51. source_to_skill-0.7.0/tests/test_dependencies.py +60 -0
  52. source_to_skill-0.7.0/tests/test_diff_source.py +53 -0
  53. source_to_skill-0.7.0/tests/test_docs.py +418 -0
  54. source_to_skill-0.7.0/tests/test_document.py +515 -0
  55. source_to_skill-0.7.0/tests/test_extract_cli.py +225 -0
  56. source_to_skill-0.7.0/tests/test_library.py +118 -0
  57. source_to_skill-0.7.0/tests/test_notes.py +216 -0
  58. source_to_skill-0.7.0/tests/test_paper.py +122 -0
  59. source_to_skill-0.7.0/tests/test_paper_arxiv.py +237 -0
  60. source_to_skill-0.7.0/tests/test_paper_sections.py +280 -0
  61. source_to_skill-0.7.0/tests/test_playlist.py +283 -0
  62. source_to_skill-0.7.0/tests/test_redirects.py +42 -0
  63. source_to_skill-0.7.0/tests/test_repo.py +726 -0
  64. source_to_skill-0.7.0/tests/test_segments.py +73 -0
  65. source_to_skill-0.7.0/tests/test_thread.py +383 -0
  66. source_to_skill-0.7.0/tests/test_transcript.py +168 -0
  67. source_to_skill-0.7.0/tests/test_utils.py +264 -0
  68. source_to_skill-0.7.0/tests/test_validate_skill.py +102 -0
  69. source_to_skill-0.7.0/tests/test_versions.py +41 -0
  70. source_to_skill-0.7.0/tests/test_vtt.py +59 -0
  71. source_to_skill-0.7.0/tests/test_youtube.py +150 -0
@@ -0,0 +1,379 @@
1
+ # Changelog
2
+
3
+ All notable changes to **source-to-skill** are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.7.0] - 2026-10-26
9
+
10
+ Five new kinds of source, three companion skills, measured quality, and
11
+ every major coding agent.
12
+
13
+ ### Added
14
+
15
+ - **Documentation sites via llms.txt** — a `/llms.txt` or `/llms-full.txt`
16
+ URL, or any docs URL with `--type docs`, becomes a docs skill: the full
17
+ file split on its headings, or each linked page fetched in order
18
+ (Markdown preferred, HTML through the article extractor), with guides
19
+ that cite the live page.
20
+ - **Notes folders** — a local directory (Obsidian vault, Markdown or
21
+ Logseq folder) becomes a skill of your own frameworks, hub notes and
22
+ recorded decisions: frontmatter tags kept, `[[wikilinks]]` and embeds
23
+ resolved, hubs ranked by inbound links, hidden folders skipped, and a
24
+ privacy rule in the template.
25
+ - **Word, PowerPoint and OpenDocument** — `.docx .pptx .odt .odp` through
26
+ the standard library: heading or slide segments, tables, and speaker
27
+ notes kept with their slide. `.doc`, `.ppt` and `.rtf` get a re-save
28
+ hint.
29
+ - **Hacker News and Reddit threads** — the post plus one segment per
30
+ top-level comment subtree in the site's own ranking; the template keeps
31
+ consensus, attributed tips and dissent apart.
32
+ - **Meeting transcripts** — `.srt` and `.vtt` exports from Zoom, Teams or
33
+ Meet, with speaker labels; the template records decisions, action items
34
+ with owners (never invented), and who said what.
35
+ - **Companion skills** in the plugin and the installer:
36
+ `skill-study` (one-question quizzes with Leitner spaced repetition,
37
+ explain-it-back checks, study plans, Anki export with deep links),
38
+ `skill-eval` (blind, source-grounded scoring of a generated skill) and
39
+ `skill-library` (inventory, search and refresh of every skill made).
40
+ - **Grounded evals in every skill** — generation writes
41
+ `evals/evals.json` (skill-creator format) from the source while it is in
42
+ context; `validate_skill.py` checks it. Every gallery skill now carries
43
+ one plus a blind run: 127 of 128 expectations pass.
44
+ - **Gallery: a podcast and a docs site** — `karpathy-agi-decade-away`
45
+ (Dwarkesh Patel with Andrej Karpathy, 2h25m, transcribed locally) and
46
+ `uv` (Astral's docs via llms.txt, 244k source tokens to a 1.7k
47
+ `SKILL.md`).
48
+ - **Plugin eval suite** — `evals/` holds 12 `claude plugin eval` cases
49
+ that check the right skill loads for real requests (and stays out of
50
+ unrelated ones); a manual `Plugin evals` workflow runs them in CI.
51
+ - **More agents** — `source-to-skill install --agent` now takes `codex`,
52
+ `gemini`, `cursor`, `opencode`, `kiro`, `windsurf` and `factory`, and
53
+ installs the companion skills next to `source-to-skill` (`--only` skips
54
+ them; `--dest PATH` installs only `source-to-skill`, into exactly that
55
+ folder). The README documents `npx skills add`.
56
+ - `docs/llms.txt` for the project site.
57
+
58
+ ### Fixed
59
+
60
+ - A direct podcast `.mp3` link that redirects to a signed CDN URL kept
61
+ the signed URL as `origin` and deep-link base, so links expired within
62
+ a day; the URL you passed is kept instead.
63
+ - Articles behind an HTTP 308 redirect failed on Python 3.10, whose
64
+ urllib does not follow 308 (Anthropic's engineering posts moved that
65
+ way).
66
+ - `--check` reported a Whisper backend that is installed but fails to
67
+ import (a native wheel built for another OS release) as missing, and
68
+ suggested installing it again; it now says `BROKEN` with the error.
69
+ - Extraction no longer loses content silently: docs pages and notes left
70
+ out by the page, size or text caps are listed in the metadata
71
+ (`dropped`), a docs page too big to keep whole is skipped instead of
72
+ cut mid-text, and threads report `truncated`.
73
+ - A note opening with a `---` horizontal rule lost everything up to the
74
+ next `---`; only YAML-looking blocks are treated as frontmatter.
75
+ - A Reddit comment whose body is only whitespace entities crashed thread
76
+ extraction.
77
+ - Transcript files kept only one of two identical consecutive lines
78
+ ("Yes." / "Yes."), and decoded non-UTF-8 files as Latin-1 instead of
79
+ Windows-1252.
80
+ - A local media file with `#` or `?` in its name (`Meeting #4.mp4`) was
81
+ not recognised.
82
+ - Repeated headings or slide titles in documents and llms-full.txt
83
+ collapsed into one segment in refresh diffs.
84
+ - `install --force` could leave no skill behind if the final move
85
+ failed; the old copy is now restored.
86
+ - `validate_skill.py` accepts no empty `evals` list, and the skill
87
+ library reads folded (`>`) and literal (`|`) descriptions.
88
+
89
+ ## [0.6.0] - 2026-10-05
90
+
91
+ Whole podcast shows, real tables, and repo docs in reading order.
92
+
93
+ ### Added
94
+
95
+ - **Podcast shows from RSS** — a feed URL (`feeds.*` hosts, `.rss`,
96
+ `.xml`, `/feed`, `/rss`) becomes a course skill of the latest 5
97
+ episodes (`--limit N`), each transcribed locally with Whisper and
98
+ titled from the feed (`"kind": "feed"`).
99
+ - **Per-lesson deep links** — playlist segments carry a
100
+ `deep_link_template` (`&t=` for YouTube, `#t=` for media files), so
101
+ lessons from any source link moments, not just videos.
102
+ - **Tables and images in books and articles** — HTML tables become
103
+ `| a | b |` rows with a header separator (layout-only tables stay plain
104
+ text); images with alt text become `[image: ...]` and captions
105
+ `[figure] ...` instead of stray lines.
106
+ - **Article bylines** — author and date fall back to JSON-LD,
107
+ `twitter:creator`, and `rel="author"` links.
108
+ - **Repo docs in reading order** — Sphinx `toctree` (from
109
+ `docs/index.rst`) or MkDocs `nav:` sets the segment order; `include` /
110
+ `literalinclude` (and MyST `{include}`) are resolved inside the repo, so
111
+ Flask's changelog and license are no longer empty stubs; reST roles
112
+ reduce to readable text, version and admonition directives to short
113
+ lines, autodoc to an "API reference:" line; segments flag `orphan` and
114
+ `stub`.
115
+ - **Plugin validation in CI** — `claude plugin validate --strict` for the
116
+ marketplace and plugin manifests (the marketplace gained a
117
+ description).
118
+
119
+ ### Changed
120
+
121
+ - Inline `[t=Ns]` markers every 30 s instead of 60 s: deep links land
122
+ within half a minute of a claim for about 3% more tokens.
123
+
124
+ ### Fixed
125
+
126
+ - **Whisper fallback broke with yt-dlp plugins installed** — yt-dlp's
127
+ plugin loader can rebind `sys.modules["extractor"]` to its own package;
128
+ deferred imports then failed with "No module named
129
+ 'extractor.media'". All extractor imports are now module-level.
130
+ - **Podcast downloads printed a progress bar** into the extractor output;
131
+ downloads are now silent.
132
+
133
+ ## [0.5.0] - 2026-10-04
134
+
135
+ Listen, not just watch and read: podcasts and audio become skills through
136
+ local Whisper, YouTube channels become course skills, several sources merge
137
+ into one topic skill, and skills can be refreshed. Plus a PyPI CLI, an
138
+ examples gallery, CI, and a landing page.
139
+
140
+ ### Added
141
+
142
+ - **Podcasts and audio** (`scripts/extractor/parsers/audio.py`) — Apple
143
+ Podcasts links, direct media URLs (`.mp3`, `.m4a`, `.wav`, `.mp4`, ...),
144
+ local audio/video files, and any page yt-dlp can download (Vimeo,
145
+ SoundCloud, TED, ... via `--type audio`) are transcribed locally with
146
+ Whisper. Segments follow the episode's chapters or 10-minute windows and
147
+ keep their start second; `deep_link_template` links back to the exact
148
+ moment where the origin is seekable (YouTube, Vimeo, direct media files).
149
+ Downloaded media is deleted after transcription.
150
+ - **Local Whisper transcription** (`scripts/extractor/transcribe.py`) —
151
+ mlx-whisper (Apple Silicon, `whisper-large-v3-turbo`), faster-whisper,
152
+ or openai-whisper, first one installed wins; model override via
153
+ `SOURCE_TO_SKILL_WHISPER_MODEL`. No API keys.
154
+ - **Captionless YouTube videos** — transcribed with Whisper instead of
155
+ failing when a backend is installed, in single videos and playlists;
156
+ `--transcribe` forces Whisper even when captions exist. Metadata
157
+ `captions` gains the value `whisper`.
158
+ - **YouTube channels** — `youtube.com/@handle`, `/channel/...`, `/c/...`,
159
+ `/user/...` extract the latest 20 uploads as a course skill
160
+ (`"kind": "channel"`); `--limit N` changes that and caps long playlists.
161
+ - **Topic skills from several sources** — `/source-to-skill <a> <b> <c>`
162
+ extracts each source into its own `--work-dir` and generates one skill
163
+ with source-tagged ideas, one file per source, and `disagreements.md`
164
+ recording where sources contradict instead of averaging them.
165
+ - **Skill refresh** — every extraction writes `source.json` (origin,
166
+ options, segment keys), copied into the generated skill;
167
+ `/source-to-skill update <skill-dir>` re-extracts and
168
+ `tools/diff_source.py` reports what is new, so only new lessons are
169
+ generated.
170
+ - **`--work-dir PATH`** — parallel or multi-source runs no longer share
171
+ one work directory.
172
+ - **PyPI package and CLI** — `uvx source-to-skill install` (or `pipx`)
173
+ installs the skill for Claude Code, cross-agent, or Copilot CLI
174
+ (`--agent`, `--project`, `--dest`, `--force`); `extract`, `check`, and
175
+ `validate` wrap the bundled scripts. Extras: `youtube`, `paper`,
176
+ `audio`, `all`.
177
+ - **Repo docs in reStructuredText and MDX** — `docs/` and `doc/` are read
178
+ for `*.md`, `*.mdx`, and `*.rst` (Sphinx projects such as Flask and
179
+ Django now extract their full docs); reST files are titled by their
180
+ first section title.
181
+ - **Timestamps inside long chapters and lessons** — video, audio and
182
+ playlist transcripts carry an inline `[t=Ns]` marker about once a
183
+ minute, so skills can link a claim to its own second, not just the
184
+ chapter start.
185
+ - **Article date fallback** — `<time datetime>` when no meta tag names
186
+ the date.
187
+ - **`--check`** reports the Whisper backend and model, a missing ffmpeg,
188
+ and warns when the installed yt-dlp is over 60 days old.
189
+ - **Examples gallery** (`examples/`) — skills generated by the tool itself
190
+ from a talk, a course playlist, a paper, a book, an article, a repo, and
191
+ a three-source topic; CI validates all of them.
192
+ - **Project infrastructure** — CI (tests on Python 3.10–3.14 and macOS,
193
+ wheel install smoke test, example validation), a weekly live-source
194
+ smoke workflow, release workflow with PyPI trusted publishing, issue
195
+ forms, PR template, `SECURITY.md`, `CODE_OF_CONDUCT.md`,
196
+ `CITATION.cff`, Dependabot, and a GitHub Pages landing page.
197
+
198
+ ### Fixed
199
+
200
+ - **Paper metadata and sections** — arXiv papers take title, authors, year
201
+ and DOI from the arXiv API (falling back to the PDF when it is
202
+ unreachable); local PDFs get their title from the largest page-1 font
203
+ instead of the file name. Years prefer the arXiv id or venue stamp over
204
+ dataset names like "WMT 2014". Numbered headings ("3 Model
205
+ Architecture", number on its own line), lettered appendices, and
206
+ tables of contents are recognized, so a paper no longer collapses into
207
+ one "Background" segment. References split into one entry per
208
+ reference (`[n]`, `[ADG+16]`, or numbered), with wrapped lines joined.
209
+ Metadata adds `arxiv_id` and `abstract_url`; `authors` is a list.
210
+ - **JSON summary corrupted by library output** — stdout now carries only
211
+ the JSON summary; anything a library prints while parsing goes to
212
+ stderr, and PyMuPDF is imported as `pymupdf` (its legacy `fitz` name
213
+ prints a deprecation notice on new versions).
214
+ - **Rate limits reported as missing captions** — a playlist whose videos
215
+ all hit YouTube's HTTP 429 now fails with "rate-limiting, wait and
216
+ retry" instead of suggesting Whisper, and stops after 3 rate-limited
217
+ videos in a row instead of requesting the rest. Single sources get the
218
+ same hint, and a YouTube bot check suggests updating yt-dlp.
219
+ - **EPUB code lost its indentation** — `<pre>` blocks keep leading
220
+ whitespace (`git status -s` columns, YAML, Python), superscripts keep a
221
+ `^` (`2^80`, not `280`), and zero-width spaces are removed. Front-matter
222
+ segments (contents, contributors, dedications) are flagged
223
+ `front_matter` so generators skip them.
224
+ - **No absolute home paths in `source.json`** — local sources are stored
225
+ as `~/...`.
226
+ - **Repo segments name their file** — each segment has a `path`, so
227
+ same-titled pages (two "Templates") stay distinguishable.
228
+ - **Caption notices in transcripts** — bracket-only cues (`[Music]`,
229
+ `[Submit subtitle corrections ...]`) are dropped.
230
+ - **Article text glued at nested blocks** — a list nested right after an
231
+ inline label (`<li><b>Sectioning</b>:<ul>...`) no longer runs the label
232
+ into the next line's text.
233
+
234
+ ### Changed
235
+
236
+ - The generator spec asks for an article byline and skips support files
237
+ that would only repeat a small source.
238
+
239
+ ## [0.4.0] - 2026-07-27
240
+
241
+ The repo is now a Claude Code plugin marketplace, and the skill lives in a
242
+ self-contained folder.
243
+
244
+ ### Added
245
+
246
+ - **Plugin install** — `.claude-plugin/marketplace.json` +
247
+ `.claude-plugin/plugin.json`: install with
248
+ `/plugin marketplace add michalstrnadel/source-to-skill` and
249
+ `/plugin install source-to-skill@source-to-skill`.
250
+
251
+ ### Changed
252
+
253
+ - **Breaking (install layout):** the skill moved from the repo root to the
254
+ self-contained `skills/source-to-skill/` folder (`SKILL.md`, `scripts/`,
255
+ `tools/` together). Existing installs that cloned the whole repo into
256
+ `~/.claude/skills/source-to-skill` must re-install: clone anywhere and
257
+ symlink/copy `skills/source-to-skill/` into the agent's skills folder
258
+ (see README Quick start).
259
+
260
+ ## [0.3.0] - 2026-07-27
261
+
262
+ Three new source types: YouTube playlists become course skills, web
263
+ articles become claims-and-highlights skills, GitHub repositories become
264
+ library skills — articles and repos with zero new dependencies.
265
+
266
+ ### Added
267
+
268
+ - **Playlist parser** (`scripts/extractor/parsers/playlist.py`) — YouTube
269
+ playlists via a yt-dlp flat listing plus per-video caption extraction
270
+ reusing the video parser. One segment per video with an extra `url` field;
271
+ videos without captions are skipped with a stderr warning and recorded in
272
+ `metadata["skipped"]` — one bad video never fails the playlist, which
273
+ errors only when no video has usable captions. Playlists over 30 videos
274
+ warn on stderr before proceeding. Metadata adds `channel`, `video_count`,
275
+ and `skipped` under `source_type: "playlist"`. A `watch?v=...&list=...`
276
+ URL stays a single video unless forced with `--type playlist`.
277
+ - **Article parser** (`scripts/extractor/parsers/article.py`) — any other
278
+ `http(s)` URL becomes a web article, parsed with the standard library
279
+ alone: urllib fetch with a browser User-Agent, charset from the HTTP
280
+ header or meta tag, and an `html.parser` extractor that drops
281
+ script/style/nav/header/footer/aside/form subtrees, prefers `<article>`
282
+ over `<main>` over `<body>`, and splits segments at h1–h3 headings.
283
+ Title from `og:title` or `<title>`, best-effort `author` and `date`.
284
+ Pages yielding under 100 words fail with "Couldn't extract a readable
285
+ article" and a next step.
286
+ - **Repo parser** (`scripts/extractor/parsers/repo.py`) — bare
287
+ `github.com/<owner>/<repo>` URLs download the codeload tarball (no git
288
+ clone) with path-traversal-safe extraction, then read the root README*,
289
+ top-level `*.md` (excluding LICENSE*), and `docs/**/*.md` in that order,
290
+ one segment per file titled by its first H1. Total text is capped at
291
+ ~2 MB with a warning listing dropped files. Missing or private repos
292
+ (404), other HTTP errors, and repos without any README or docs all fail
293
+ with clear errors. Metadata adds `owner`, `repo`, and `file_count` under
294
+ `source_type: "repo"`.
295
+ - **Detection order** (`scripts/extractor/utils.py`) — YouTube video →
296
+ playlist → arXiv → GitHub repo → generic http(s) article → `.pdf` →
297
+ `.epub`; `--type` validation extended (`playlist` valid on
298
+ `watch?list=` URLs, `article` on any http URL, `repo` only on GitHub
299
+ repo URLs).
300
+ - **Skill templates** (repo-root `SKILL.md`, Step 5) — playlist/course
301
+ skills get a course overview, a lesson index table, per-video
302
+ `lessons/NN-<slug>.md` files opening with the video URL, and a
303
+ course-wide cheatsheet; article skills get thesis plus key claims with a
304
+ link to the original and `highlights.md` quotes; repo skills get what
305
+ the project does, install, core usage patterns, a doc index,
306
+ `guides/*.md` per doc area, and a commands-and-snippets cheatsheet.
307
+ - **Docs** — README gains a Supported sources table, quick-start examples,
308
+ features, and troubleshooting entries for the three new types;
309
+ architecture docs cover the new parsers and detection order.
310
+
311
+ ## [0.2.0] - 2026-07-27
312
+
313
+ Books join videos and papers: EPUBs with zero new dependencies, PDF books
314
+ via a type override.
315
+
316
+ ### Added
317
+
318
+ - **Book parser** (`scripts/extractor/parsers/book.py`) — EPUB books parsed
319
+ with the Python standard library alone (zip → `META-INF/container.xml` →
320
+ OPF spine; chapter titles from `toc.ncx` or the EPUB3 `nav.xhtml`, falling
321
+ back to the first `<h1>`, the document `<title>`, then "Chapter N"; tags
322
+ stripped and entities decoded via `html.parser`). PDF books reuse PyMuPDF:
323
+ chapters come from the top-level PDF outline, falling back to section
324
+ detection. Metadata uses `source_type: "book"` and adds `author` (EPUB
325
+ `dc:creator` / PDF metadata). Malformed EPUBs (bad zip, missing
326
+ container.xml, unreadable OPF, empty spine) fail with clear errors; a
327
+ malformed `toc.ncx` falls back to nav/heading titles instead of aborting,
328
+ spine items missing from the archive are skipped with a stderr warning,
329
+ and unresolved PDF outline destinations are ignored.
330
+ - **`--type` override** — `extract.py <source> [--type youtube|paper|book]`
331
+ forces the parser when auto-detection is not enough (a PDF book vs. a PDF
332
+ paper); `.epub` auto-detects as book, unknown types and impossible
333
+ source/type combinations are rejected with clear errors.
334
+ - **Book skill template** (repo-root `SKILL.md`, Step 5) — core mental
335
+ models plus a chapter index table in the generated SKILL.md, on-demand
336
+ `chapters/NN-<slug>.md` files, `glossary.md` with chapter references, and
337
+ `cheatsheet.md` with decision rules and anti-patterns.
338
+
339
+ ## [0.1.0] - 2026-07-27
340
+
341
+ First public release: YouTube videos and academic papers in, installable
342
+ agent skills out.
343
+
344
+ ### Added
345
+
346
+ - **YouTube parser** (`scripts/extractor/parsers/youtube.py`) — captions via
347
+ yt-dlp (manual subtitles preferred, auto-generated as fallback; English
348
+ variants preferred), VTT parsing with auto-caption de-duplication, and
349
+ segmentation by the video's own chapters with fixed 10-minute windows as
350
+ fallback. Every segment records its start timestamp so generated skills
351
+ can deep-link back into the video with `&t=`.
352
+ - **Paper parser** (`scripts/extractor/parsers/paper.py`) — text extraction
353
+ via PyMuPDF from local PDFs or modern arXiv URLs (downloaded
354
+ automatically), heuristic section detection (Abstract through References,
355
+ with a safe full-text fallback), DOI/year detection, per-section page
356
+ numbers, and a raw reference list. Scanned PDFs without a text layer fail
357
+ with a clear "OCR not supported yet" error.
358
+ - **Extractor CLI** (`scripts/extract.py`) — detects the source type,
359
+ dispatches to the right parser, and writes `full_text.txt` +
360
+ `metadata.json` (the shared segment contract: `title`, `start_s`, `pages`,
361
+ `offset`) to the work dir. `--check` reports each optional dependency
362
+ (yt-dlp, PyMuPDF) with an exact install hint; extraction errors state what
363
+ failed and the next step.
364
+ - **Generator spec** (repo-root `SKILL.md`) — the six-step agent workflow:
365
+ extract, confirm token cost, analyze structure (segment-by-segment reads
366
+ for large sources), choose an install target, generate, verify. Video
367
+ skills get a timestamped segment index, per-segment files opening with a
368
+ deep link, and a cheatsheet; paper skills get a TL;DR plus
369
+ `methods.md`, `findings.md`, `limitations.md`, `glossary.md`, and
370
+ `citations.md`.
371
+ - **Skill validator** (`tools/validate_skill.py`) — checks generated skills
372
+ for valid frontmatter (name matches the directory, description within
373
+ limits), resolvable markdown links, and no empty files; prints
374
+ `OK: skill is valid` on success.
375
+ - **Offline test suite** — pytest coverage for source detection, VTT
376
+ parsing, segmentation, paper section detection, dependency probing, the
377
+ CLI, and the validator; yt-dlp is mocked and the PDF fixture is generated
378
+ locally, so no test touches the network.
379
+ - MIT license; Python ≥ 3.10.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michal Strnadel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ # sdist only (the wheel's contents come from [tool.setuptools] in pyproject.toml)
2
+ include CHANGELOG.md
3
+ graft tests
4
+ global-exclude *.py[cod] .DS_Store