Source profileQuality 91/100Review permissions

drpwchen/textbook-to-note/skills/textbook-to-md/SKILL.md

textbook-to-md

Convert PDF/EPUB textbooks to searchable markdown files for an AI agent's own reference. Use this skill whenever: (1) the user asks to convert a textbook/PDF chapter to markdown, (2) you need to search textbook content and no markdown version exists yet, (3) batch-converting a set of reference books into a knowledge base. This is a 0-token local conversion — no vision model needed.

Source repository stars
99
Declared platforms
0
Static risk flags
1
Last source update
2026-08-26
Source checked
2026-08-28

Decision brief

What it does: where it fits

This skill calls scripts in your clone of the textbook-to-note repo. At install time, replace {REPO} below with the absolute path of the clone.

Best for

  • User explicitly asks to convert a textbook or chapter
  • You need to search textbook content and want to avoid per-query PDF
  • Building up a knowledge base from a personal library of reference books

Not for

  • Image-based tables (scanned/embedded as pictures): the table extractor
  • Merged cells: the table extractor sometimes splits or duplicates

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeNot declaredNo explicit evidencePortability before use
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/drpwchen/textbook-to-note --skill "skills/textbook-to-md"
Safe inspection promptEditorial

Inspect the Agent Skill "textbook-to-md" from https://github.com/drpwchen/textbook-to-note/blob/65e76902d6d68c0f467fbe1ab1412c4c4798baa4/skills/textbook-to-md/SKILL.md at commit 65e76902d6d68c0f467fbe1ab1412c4c4798baa4. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    Quality verification

    After conversion, check: - Total words 500K words → chapter detection failed, re-split needed - 0 chars extracted from sample pages → pure scan; batch-dir handles this automatically. Single-file mode does not auto-route — use batch-dir on a temp folder for OCR needs - Garbled gl…

    Total words < 500 for a book → likely scanned; batch-dir auto-routesSingle file 500K words → chapter detection failed, re-split needed0 chars extracted from sample pages → pure scan; batch-dir handles
  2. 02

    On-demand workflow — single entry point

    Call the figure-remap skill's public entrypoint (figureremap.py extract — not the internal gate script). The entrypoint runs deterministic geometric matching by default and returns a stable contract:

    Call the figure-remap skill's public entrypoint (figureremap.py extract — not the internal gate script). The entrypoint runs deterministic geometric matching by default and returns a stable contract:Contract: {status: pass|fail|escalate, matchquality: exact|uncertain|failed, hardfail, file, figid, reason, qcdegraded, qcskipped} — those eight keys exactly (figures/figureremap.py CONTRACTKEYS; the validator raises on…On pass (exit 0), embed the --out path (result.file) in the note:
  3. 03

    Purpose

    Convert PDF or EPUB textbooks into searchable markdown that the agent can grep/read directly, eliminating the need for PDF-library extraction at every query.

    Convert PDF or EPUB textbooks into searchable markdown that the agent can grep/read directly, eliminating the need for PDF-library extraction at every query.Output lives outside the note vault, at the path configured by OUTPUTDIR in shared/config.py (default ./output/), and is for the agent's consumption, not for the user's reading.Figures are on-demand, not pre-extracted. This skill produces markdown text only. Figures are extracted one at a time when a note needs them, via the figure-remap skill's entrypoint (QC-gated). Do not batch-extract a wh…
  4. 04

    When to use

    User explicitly asks to convert a textbook or chapter

    User explicitly asks to convert a textbook or chapterYou need to search textbook content and want to avoid per-query PDFBuilding up a knowledge base from a personal library of reference books
  5. 05

    Quick reference

    Review the “Quick reference” section in the pinned source before continuing.

    Review and apply the “Quick reference” source section.

Permission review

Static risk signals and limitations

Runs scripts

medium · line 37

The documentation asks the agent to run terminal commands or scripts.

python {REPO}/converter/convert.py "path/to/chapter.pdf"

Runs scripts

medium · line 40

The documentation asks the agent to run terminal commands or scripts.

python {REPO}/converter/convert.py "path/to/chapter.pdf" "output.md" --book-label "Author Title 2e — Ch32"

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score91/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars99SourceRepository attention, not individual Skill quality
Compatibility0 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
drpwchen/textbook-to-note
Skill path
skills/textbook-to-md/SKILL.md
Commit
65e76902d6d68c0f467fbe1ab1412c4c4798baa4
License
MIT
Collected
2026-08-28
Default branch
main
View the original SKILL.md

Textbook-to-Markdown Converter

This skill calls scripts in your clone of the textbook-to-note repo. At install time, replace {REPO} below with the absolute path of the clone.

Purpose

Convert PDF or EPUB textbooks into searchable markdown that the agent can grep/read directly, eliminating the need for PDF-library extraction at every query.

Output lives outside the note vault, at the path configured by OUTPUT_DIR in shared/config.py (default ./output/), and is for the agent's consumption, not for the user's reading.

Figures are on-demand, not pre-extracted. This skill produces markdown text only. Figures are extracted one at a time when a note needs them, via the figure-remap skill's entrypoint (QC-gated). Do not batch-extract a whole book's figures into a figures/ folder — that approach does not scale and is unnecessary since the on-demand path already handles it. A legacy figures/ folder may exist for books converted before this design; new conversions are markdown-only.

When to use

  • User explicitly asks to convert a textbook or chapter
  • You need to search textbook content and want to avoid per-query PDF re-parsing overhead
  • Building up a knowledge base from a personal library of reference books
  • Before starting work on a new topic, convert the relevant chapters first

Quick reference

# Single file
python {REPO}/converter/convert.py "path/to/chapter.pdf"

# Single file with custom output and label
python {REPO}/converter/convert.py "path/to/chapter.pdf" "output.md" --book-label "Author Title 2e — Ch32"

# Batch-dir: convert ALL PDFs in a directory tree (auto chapter split + PDF bookmarks)
python {REPO}/converter/convert.py --batch-dir "path/to/your/textbook/folder"

# Force re-convert (ignore existing md)
python {REPO}/converter/convert.py --batch-dir "path/to/your/textbook/folder" --force

# Force OCR for ALL PDFs in batch-dir (bypass the text-extraction path entirely).
# Use when the text layer "looks" healthy but quality is actually bad (OCR-overlay
# scans, some digitized reprints) — auto-detection won't trigger because the text
# layer passes the shallow check.
python {REPO}/converter/convert.py --batch-dir DIR --force --force-surya

# EPUB → markdown (the 2nd arg is a FOLDER, not a .md file)
python {REPO}/converter/convert.py "path/to/book.epub" "<OUTPUT_DIR>/Author_Title_2e_2022"

Batch mode skips files whose markdown already exists and is newer than the source PDF. Batch-dir mode saves progress to batch_progress.json — if interrupted, re-running resumes where it stopped. --batch-dir also picks up .epub files automatically.

EPUB support

  • Uses pandoc (epub → gfm). EPUBs are reflowable, so there are no <!-- page N --> markers; files carry a <!-- SOURCE: epub --> marker instead.
  • If pandoc emits proper heading markup, that drives the chapter split. If the EPUB is CSS-styled with no semantic headings (common), headings are rebuilt from the TOC link table + body anchors before splitting.
  • Output is a folder of chNN_*.md + full_text.md (same shape as a PDF book) — the single-file 2nd argument is the output directory, not a .md path.
  • Requires pandoc on PATH. Figures are not extracted from EPUB (the on-demand figure flow is PDF-only).

Batch-dir features

  • Recursively finds all PDFs, auto-outputs to <OUTPUT_DIR>/{PDF_stem}/ (OUTPUT_DIR from shared/config.py; default ./output)
  • Produces full_text.md (complete with <!-- page N --> markers) plus a chapter split
  • Chapter-splitting priority: PDF bookmarks → pattern detection → force-split (every 30 pages)
  • Writes PDF bookmarks to source PDFs if none exist
  • Auto-routes OCR-needed PDFs to the local OCR engine (GPU). Two triggers: (1) a page is scan-only (near-zero extractable characters); (2) fitz-silent-failure is detected (see docs/ocr-ladder.md). Falls back to skip-with-explanation if the OCR environment is missing. Output is full_text.md with page markers, and a chapter split is attempted on the OCR'd text too (numbered-heading patterns often survive OCR); it is best-effort and simply yields no chapters when heading detection fails

Output structure

<OUTPUT_DIR>/                       ← default ./output (shared/config.py)
├── Author_Title_Edition_Year/
│   ├── ch01_Chapter_Title.md      ← conversion produces md only
│   ├── ch02_....md
│   ├── full_text.md
│   └── figures/                   ← LEGACY ONLY — not produced by conversion
└── Another_Book/                  ← single-file conversions use the same <book>/full_text.md layout

Figure-registry generation (figure_registry.json) is an optional external hook, not a shipped script — set the FIGURE_REGISTRY_SCRIPT env var to a generator script if you have one; if unset, post_convert.py skips that step.

==Conversion writes .md files only.== A figures/ subdirectory is legacy — present only on books converted before the on-demand switch. New conversions never create one; figures are pulled on demand by figure-remap.

What the converter produces

Each markdown file contains:

  • Page markers: <!-- page X --> at every page boundary, to map content back to the source PDF
  • Cleaned text: control characters removed, broken/hyphenated words rejoined, grep-searchable
  • Tables: extracted as markdown tables (a low content-cell threshold is used to avoid discarding real tables)
  • Figure/table reference markers: when the text mentions a figure or table (Fig. 32.1, Table 31.2, etc.), an HTML comment <!-- REF: Fig. 32.1 → see PDF page X --> is inserted so the agent knows where to look in the original PDF

Searching converted textbooks

Two search methods:

1. Keyword search (grep) — exact match, 0 tokens

grep -r "your search term" "$OUTPUT_DIR"      # default ./output

2. Semantic search (optional) — concept match

If you've set up the optional semantic index (LanceDB + a local embedding model — see docs/architecture.md), a textbook_search tool becomes available for concept-level queries that don't depend on exact wording.

Search strategy

  • Known keyword → grep first (fastest, exact)
  • Concept/topic exploration → semantic search (finds related content even with different phrasing)
  • Both → grep for precision, semantic search for coverage

Post-conversion pipeline

After converting new books, run the post-pipeline:

# Full pipeline: verify quality + refresh index.md + refresh figure registry + semantic index
python {REPO}/converter/post_convert.py

# Or individual steps:
python {REPO}/converter/post_convert.py --verify   # check quality only
python {REPO}/converter/post_convert.py --index    # semantic index only
python {REPO}/converter/post_convert.py --audit    # report index coverage (no backfill)

The pipeline does not extract figures — it only verifies page markers, refreshes index.md + figure_registry.json from whatever figures already exist, and (optionally) builds the semantic index. Figure extraction is never a batch post-step.

Missing page markers do not block indexing by design — EPUBs (reflowable) and some vector-glyph PDFs legitimately lack <!-- page N -->; the indexer handles this gracefully (page metadata = 0). After indexing, a coverage audit compares the markdown corpus against the index and auto-backfills any book that has markdown but no index rows, so every converted book ends up searchable even if a step was skipped.

Figures — do NOT batch-extract

No pre-extraction step. A whole-book batch figure dump is retired and should not be run on new books. When a note needs a figure, figure-remap extracts that single figure on demand with QC. See "Using figures in notes" below.

Chapter splitting (batch mode)

Chapter detection patterns (in priority order)

  1. Chapter N / CHAPTER N — standard
  2. Part N / PART N — with roman numerals (Part III)
  3. Section N / SECTION N / Unit N
  4. Numbered headings: 1 Introduction, 23 Shoulder (digit + title-case text)

Running header deduplication

Many textbooks repeat Chapter N Title as a running header on every page. Only count the first occurrence of each unique chapter number — track a seen_chapters set and skip duplicates.

Force-split rules

  • No chapter breaks detected AND >200 pages → split every 30 pages (pages_0001-0030.md)
  • Single output file >500K words → re-split (likely missed chapter breaks)

Output naming

  • Folder name = PDF filename without .pdf (use an Author_Title_Edition_Year convention)
  • Chapter files: ch01_Chapter_Title.md, ch00_Front_Matter.md
  • Forced splits: pages_0001-0030.md

Quality verification

After conversion, check:

  • Total words < 500 for a book → likely scanned; batch-dir auto-routes to OCR. If it ran the text-extraction path anyway, the OCR-trigger heuristic missed and needs tuning
  • Single file > 500K words → chapter detection failed, re-split needed
  • 0 chars extracted from sample pages → pure scan; batch-dir handles this automatically. Single-file mode does not auto-route — use batch-dir on a temp folder for OCR needs
  • Garbled glyphs (high char count, mostly private-use-area codepoints) → CID-encoded font without a Unicode map; detected by the silent-failure check, auto-routes to OCR in batch-dir

Always cite with year when referencing converted content: (Author 2e, 2021), not just (Author).

Using figures in notes (on-demand)

When writing or supplementing a note that needs a figure (anatomy, classification, imaging, algorithm), extract that one figure on demand — never batch-dump a book. The markdown's <!-- REF: Fig. 5.1 → see PDF page 42 --> markers tell you which figure exists and what PDF page it's on; hand that to the gate.

On-demand workflow — single entry point

Call the figure-remap skill's public entrypoint (figure_remap.py extract — not the internal gate script). The entrypoint runs deterministic geometric matching by default and returns a stable contract:

python {REPO}/figures/figure_remap.py extract \
  --book "{Book}" \
  --fig-id "5-1" \
  --caption "<caption text from the REF marker / md>" \
  --out "path/to/your/vault/attachments/Fig_5-1_{BookShort}.jpeg" \
  --pdf "<source PDF path>" \
  --page {1-indexed PDF page from the REF marker}

Contract: {status: pass|fail|escalate, match_quality: exact|uncertain|failed, hard_fail, file, fig_id, reason, qc_degraded, qc_skipped} — those eight keys exactly (figures/figure_remap.py CONTRACT_KEYS; the validator raises on any extra or missing key, so branching on the engine's internal match_method is not just discouraged, it is impossible). status:fail (exit 1) is a deterministic miss — a correct refusal, not a wrong crop; fix --page, escalate to vision, or leave a <!-- TODO -->. Read the real caption to confirm the figure depicts what you intend.

On pass (exit 0), embed the --out path (result.file) in the note:

![[Fig_5-1_{BookShort}.jpeg|400]]
*Fig 5.1 — description (Author 2e, p.42)*

When writing a fresh note via the note-writing workflow (see workflows/note-writing.md), figure harvest is Phase 3.5 — don't call the gate manually there, the workflow does it. The manual call above is for ad-hoc figure needs outside that workflow. Full fallback ladder + per-book calibration: see the figure-remap skill.

When to include figures

  • Always: anatomy diagrams, classification systems, algorithm flowcharts, key reference images
  • Skip: decorative images, author photos, generic stock photos
  • Ask the user if unsure whether a figure adds value

Figure registry

figure_registry.json (produced by the post-conversion pipeline) records each book's figure status. A status of "not extracted" or "lazy-only" is the expected normal state — it does not mean a batch extraction has to run first; the on-demand gate handles extraction from the PDF regardless.

Naming convention for note attachments

{FigID}_{BookShort}.{ext} — e.g. Fig_5-1_AuthorName.jpeg, Fig_32-1_AuthorName.jpeg. Avoids filename collisions across books in your attachments folder.

Known limitations

  • Image-based tables (scanned/embedded as pictures): the table extractor can't read these. They show up as mostly-empty tables and get filtered out. Check the original PDF at the page number shown in <!-- page X -->.
  • Merged cells: the table extractor sometimes splits or duplicates merged cells. Still readable, but may have redundant columns.
  • CJK OCR: text extraction works well for CJK text in native (born- digital) PDFs. For scanned CJK PDFs, fall back to the sanctioned OCR ladder — see docs/ocr-ladder.md.
  • dump_all figures: books where caption detection failed get page-based filenames (page_0042.jpeg) without captions. Still usable, but you must identify content by reading the image.
  • Windows subprocess OCR encoding: any OCR engine run as a subprocess must read stdout in bytes mode and decode explicitly as UTF-8 with error replacement — do not rely on the platform's default text-mode decoding.

Conversion for other PDFs

The script accepts any PDF, not just your priority set. For ad-hoc conversions:

python {REPO}/converter/convert.py "path/to/any.pdf" "path/to/output.md" --book-label "Book Name — Chapter"

If no output path is specified, output goes to OUTPUT_DIR/<pdf name>/full_text.md — the same layout --batch-dir uses, so a later batch run skips it as already converted.

Frequently asked questions

What to verify before installation and use

What does the textbook-to-md source document cover?

This skill calls scripts in your clone of the textbook-to-note repo. At install time, replace {REPO} below with the absolute path of the clone.

How do I install textbook-to-md?

The source record exposes this install command: npx skills add https://github.com/drpwchen/textbook-to-note --skill "skills/textbook-to-md". Inspect the command and pinned source before running it.

Which permission-related actions were detected?

Static rules flagged exec-script in the source; the page lists the matching lines and excerpts.