From reading
Push a markdown file from the active project into iCloud Drive as a PDF so Apple Books on iPad picks it up for off-desk reading with Apple Pencil annotations. Push-only — no read-back, no annotation extraction, no Books auto-import. Push triggers (EN) "send to books", "read on ipad", "review on books"; push triggers (RU) "положи это в books", "положи это в книги", "почитаю на айпаде".
How this skill is triggered — by the user, by Claude, or both
Slash command
/reading:booksThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
User-level skill that converts a markdown file from the active project into a
User-level skill that converts a markdown file from the active project into a PDF, drops it into an iCloud Drive subfolder, and trusts iCloud + Apple Books to surface it on the iPad. The skill is push-only: Apple Pencil annotations stay on the iPad, with the human, not with Claude.
The skill performs MD → PDF conversion directly via scripts/md-to-pdf.py
(weasyprint + the markdown package), then mkdir -p + os.replace for an
atomic publish into iCloud Drive.
Default iCloud Drive root on the laptop:
~/Library/Mobile Documents/com~apple~CloudDocs/Reading
Override: set READING_ICLOUD_DIR in your shell profile (~/.zshrc or
~/.bashrc) to point at any directory under iCloud Drive (or elsewhere). The
skill reads it at every invocation; restart the shell after editing.
Every push step that touches iCloud resolves the root with:
ICLOUD_DIR="${READING_ICLOUD_DIR:-$HOME/Library/Mobile Documents/com~apple~CloudDocs/Reading}"
ICLOUD_DIR="${ICLOUD_DIR%/}" # strip trailing slash for consistency
Trigger phrases (any of):
Procedure:
.md — this skill ships
markdown only; PDF/EPUB inputs are out of scope.Path(source).stem (the filename without the .md
extension). On collision in the target directory, append
-<sha1(absolute_source_path)[:6]> — mirrors the offdesk slug-collision
pattern at plugins/obsidian/skills/offdesk/SKILL.md.git rev-parse --show-toplevel. If the
command fails (not a git repo), fall back to dirname(source). The
basename of the project root becomes the iCloud subfolder name.ICLOUD_DIR="${READING_ICLOUD_DIR:-$HOME/Library/Mobile Documents/com~apple~CloudDocs/Reading}"
TARGET_DIR="$ICLOUD_DIR/<project-basename>"
mkdir -p "$TARGET_DIR"
uv run plugins/reading/skills/books/scripts/md-to-pdf.py \
"<source.md>" \
"$TARGET_DIR/<slug>.pdf"
.<slug>.pdf.tmp), then os.replaces it to the
final <slug>.pdf so iCloud only sees complete files.references/styles.css produces the default layout:
'IBM Plex Serif', 'PT Serif', 'Apple Color Emoji', 'Times New Roman', serif,
11pt body, with font-variant-numeric: lining-nums so digits sit on the
baseline; 18/15/13 pt for H1/H2/H3.'IBM Plex Mono', Menlo, Consolas, monospace, 10pt, light-grey
background. No syntax highlighting in v0.1.#0050b3).max-width: 100%.h1, h2 { page-break-before: auto; break-inside: avoid; } to
prevent orphan headings.CSS lives in references/ so the user can override without touching the
script.
Heading anchors follow Obsidian's exact-text convention so a markdown doc that
navigates correctly in Obsidian also navigates correctly in the rendered PDF.
Both the heading id and the link fragment pass through a single
obsidian_slugify (in scripts/md-to-pdf.py):
-Authors write the link in either CommonMark spelling — bare or angle-bracketed for fragments containing whitespace:
[§3.2](#3.2 Camunda — соседняя ИС вне периметра ПФ)
[§3.2](<#3.2 Camunda — соседняя ИС вне периметра ПФ>)
Both normalize to the same id. No {#explicit-id} annotations on the
heading are required — the heading text alone is the anchor.
The skill needs both the markdown and weasyprint Python packages, plus
their native dependencies on macOS.
Python packages — installed via uv in the repo root:
uv add weasyprint markdown
macOS system prerequisites — install via Homebrew before the first push, or weasyprint will fail to render with a cryptic Cairo/Pango error:
brew install cairo pango gdk-pixbuf
brew install --cask font-ibm-plex
The cairo/pango/gdk-pixbuf trio are runtime dependencies for weasyprint's PDF
rendering pipeline. font-ibm-plex installs the IBM Plex Serif / Mono families
used by the default stylesheet; without them weasyprint falls back to PT Serif
or Times New Roman.
Two source files with identical basenames (e.g., ~/work/foo/notes.md and
~/play/bar/notes.md) would collide in $TARGET_DIR/notes.pdf. Resolution:
suffix the slug with a short hash of the absolute source path when a
collision is detected.
Example: notes and notes-a1b2c3 (where a1b2c3 is the first 6 hex chars
of sha1(absolute_source_path)). Use a stable hash so the same source
always maps to the same slug.
This mirrors the offdesk pattern at
plugins/obsidian/skills/offdesk/SKILL.md.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub dddpaul/claude-skills --plugin reading