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.
- source_to_skill-0.7.0/CHANGELOG.md +379 -0
- source_to_skill-0.7.0/LICENSE +21 -0
- source_to_skill-0.7.0/MANIFEST.in +4 -0
- source_to_skill-0.7.0/PKG-INFO +435 -0
- source_to_skill-0.7.0/README.md +384 -0
- source_to_skill-0.7.0/pyproject.toml +131 -0
- source_to_skill-0.7.0/setup.cfg +4 -0
- source_to_skill-0.7.0/skills/skill-eval/SKILL.md +77 -0
- source_to_skill-0.7.0/skills/skill-library/SKILL.md +49 -0
- source_to_skill-0.7.0/skills/skill-library/scripts/library.py +160 -0
- source_to_skill-0.7.0/skills/skill-study/SKILL.md +85 -0
- source_to_skill-0.7.0/skills/source-to-skill/SKILL.md +388 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extract.py +188 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/__init__.py +10 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/config.py +205 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/dependencies.py +114 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/media.py +38 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/__init__.py +0 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/article.py +668 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/audio.py +87 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/book.py +493 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/docs.py +505 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/document.py +712 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/notes.py +365 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/paper.py +623 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/playlist.py +186 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/repo.py +858 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/thread.py +450 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/transcript.py +104 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/parsers/youtube.py +226 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/transcribe.py +132 -0
- source_to_skill-0.7.0/skills/source-to-skill/scripts/extractor/utils.py +245 -0
- source_to_skill-0.7.0/skills/source-to-skill/tools/diff_source.py +63 -0
- source_to_skill-0.7.0/skills/source-to-skill/tools/validate_skill.py +107 -0
- source_to_skill-0.7.0/src/source_to_skill/__init__.py +3 -0
- source_to_skill-0.7.0/src/source_to_skill/__main__.py +5 -0
- source_to_skill-0.7.0/src/source_to_skill/cli.py +237 -0
- source_to_skill-0.7.0/src/source_to_skill.egg-info/PKG-INFO +435 -0
- source_to_skill-0.7.0/src/source_to_skill.egg-info/SOURCES.txt +69 -0
- source_to_skill-0.7.0/src/source_to_skill.egg-info/dependency_links.txt +1 -0
- source_to_skill-0.7.0/src/source_to_skill.egg-info/entry_points.txt +2 -0
- source_to_skill-0.7.0/src/source_to_skill.egg-info/requires.txt +32 -0
- source_to_skill-0.7.0/src/source_to_skill.egg-info/top_level.txt +1 -0
- source_to_skill-0.7.0/tests/conftest.py +27 -0
- source_to_skill-0.7.0/tests/fixtures/arxiv_1706.03762.xml +47 -0
- source_to_skill-0.7.0/tests/fixtures/sample.vtt +18 -0
- source_to_skill-0.7.0/tests/test_article.py +506 -0
- source_to_skill-0.7.0/tests/test_audio.py +543 -0
- source_to_skill-0.7.0/tests/test_book.py +470 -0
- source_to_skill-0.7.0/tests/test_cli_package.py +282 -0
- source_to_skill-0.7.0/tests/test_dependencies.py +60 -0
- source_to_skill-0.7.0/tests/test_diff_source.py +53 -0
- source_to_skill-0.7.0/tests/test_docs.py +418 -0
- source_to_skill-0.7.0/tests/test_document.py +515 -0
- source_to_skill-0.7.0/tests/test_extract_cli.py +225 -0
- source_to_skill-0.7.0/tests/test_library.py +118 -0
- source_to_skill-0.7.0/tests/test_notes.py +216 -0
- source_to_skill-0.7.0/tests/test_paper.py +122 -0
- source_to_skill-0.7.0/tests/test_paper_arxiv.py +237 -0
- source_to_skill-0.7.0/tests/test_paper_sections.py +280 -0
- source_to_skill-0.7.0/tests/test_playlist.py +283 -0
- source_to_skill-0.7.0/tests/test_redirects.py +42 -0
- source_to_skill-0.7.0/tests/test_repo.py +726 -0
- source_to_skill-0.7.0/tests/test_segments.py +73 -0
- source_to_skill-0.7.0/tests/test_thread.py +383 -0
- source_to_skill-0.7.0/tests/test_transcript.py +168 -0
- source_to_skill-0.7.0/tests/test_utils.py +264 -0
- source_to_skill-0.7.0/tests/test_validate_skill.py +102 -0
- source_to_skill-0.7.0/tests/test_versions.py +41 -0
- source_to_skill-0.7.0/tests/test_vtt.py +59 -0
- 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.
|