How book-to-skill Detects Chapters with Digit-Led Titles: A Deep Dive into the Structural Analysis Algorithm

book-to-skill uses a two-stage validation process in book_to_skill/utils.py that examines both heading frequency and body text length to distinguish true chapter headings from numbered lists.

The open-source book-to-skill repository by virgiliojr94 provides intelligent chapter detection for Markdown and AsciiDoc books. When processing digit-led titles like ## 5 Setup or 1 Introduction, the system must avoid false positives from tutorial step numbers or simple list items. This article explains how the structural scanning algorithm validates these headings before accepting them as genuine chapters.

The Problem: Digit-Led Titles vs. Numbered Lists

Books frequently use numbers in chapter titles—Chapter 5, 5. The Journey Begins, or simply 5 Setup. However, technical documentation also contains numbered lists (1. Install Python) and tutorial steps that must not be mistaken for chapter boundaries. The detect_structure function in book_to_skill/utils.py (lines [221-272]) orchestrates multiple detection strategies to resolve this ambiguity.

Stage 1: Structural Scanning with Temporary Hold

The core validation lives in _structural_chapter_count (lines [85-94] of utils.py). During the initial pass through the source document, the scanner:

  1. Collects all ATX headings (# Heading) and setext headings (===/---)

  2. Temporarily sets aside digit-led titles in a numbered dictionary

  3. Requires further validation before merging them into the final levels map

This cautious approach prevents premature classification of suspect headings.

The Validation Helper: _numbered_titles_are_structural

Digit-led titles undergo rigorous scrutiny through _numbered_titles_are_structural (lines [82-101]):

def _numbered_titles_are_structural(
    entries: list[tuple[str, int]], heading_lines: list[int], lines: list[str]
) -> bool:

Two conditions must both pass:

Criterion Threshold Purpose
Minimum distinct numbered headings _MIN_NUMBERED_TITLES = 3 Ensures systematic chapter numbering, not isolated instances
Median body text length _MIN_NUMBERED_BODY_CHARS = 200 Rejects short "step" headings typical of tutorials

The median-body test is particularly clever. Short numbered headings with minimal intervening text—common in how-to guides—produce a low median and fail validation. Substantial chapters with hundreds of lines between headings pass cleanly.

Passing headings merge into levels at lines [71-73], becoming genuine chapter markers.

Stage 2: Plain Numbered Heading Detection

For simpler "Chapter N" formats without explicit text like "Chapter", _match_chapter_number provides a fast path (lines [441-449]):

plain = re.match(r"^([1-9]\d{0,2})\s{2,}\S", s)
if plain:
    return int(plain.group(1))

The regex pattern ^([1-9]\d{0,2})\s{2,}\S encodes critical safeguards:

  • ^ — Anchors to start of line
  • [1-9] — No leading zeros permitted
  • \d{0,2} — Allows 1-3 digit numbers (1-999)
  • \s{2,}Requires at least two spaces after the number
  • \S — Demands non-whitespace content follows

The two-space requirement specifically excludes list items formatted as 1. Item or 1) Step, which typically use one space or punctuation immediately after the digit.

Integration: The detect_structure Entry Point

The public API detect_structure (lines [221-272]) orchestrates detection with intelligent fallback:

  1. First attempts explicit "Chapter N" pattern matching
  2. If fewer than two numeric headings found, falls back to structural scanning
  3. Returns final count as chapters_detected and method used as chapters_method

Practical Code Examples

Example 1: Detecting Digit-Led Chapters in Markdown

from book_to_skill.utils import detect_structure

md = """# My Book

## 1  Introduction

Some intro text well over two hundred characters long to ensure the median body length test passes with substantial content here...

## 2  Getting Started

Similarly lengthy content for chapter two that demonstrates real chapter structure rather than tutorial steps...

## 3  Advanced Topics

More substantial content that validates this as a genuine three-chapter book structure...
"""

info = detect_structure(md)
print(info['chapters_detected'])   # → 3

print(info['chapters_method'])     # → structural

The three numbered headings with substantial intervening text pass both validation criteria.

Example 2: Plain Numbered Heading with Two Spaces

md = "1  Chapter One\n\nText of chapter one.\n"
info = detect_structure(md)
print(info['chapters_detected'])   # → 1 (numeric branch)

The double space after 1 satisfies the regex, routing through the numeric detection branch.

Example 3: List-Style Numbering Rejected

md = "1. Item one\n2. Item two\n"
info = detect_structure(md)
print(info['chapters_detected'])   # → 0

Single space after number plus period causes regex match failure—correctly ignored as list items.

Key Source Files and Test Coverage

File Responsibility
book_to_skill/utils.py Core detection logic: _structural_chapter_count, _numbered_titles_are_structural, _match_chapter_number, detect_structure
book_to_skill/parsers/text.py Plain-text extraction feeding detection
tests/test_numbered_headings.py Unit tests for digit-led title scenarios
tests/test_structural_chapter_count.py Structural detection validation

Direct source links:

Summary

  • book-to-skill detects digit-led chapter titles through a two-stage validation in book_to_skill/utils.py that combines structural scanning with body-length analysis
  • Temporary hold pattern: Digit-led headings isolate in numbered dict until _numbered_titles_are_structural validates minimum count (3+) and median body length (200+ chars)
  • Regex safeguards: The plain pattern in _match_chapter_number requires double spaces after digits, filtering list-item false positives
  • Intelligent fallback: detect_structure tries explicit patterns first, then structural scanning, returning method provenance for transparency
  • Well-tested: Dedicated test files verify both numeric and structural detection paths

Frequently Asked Questions

How does book-to-skill avoid mistaking tutorial steps for chapters?

The _numbered_titles_are_structural function applies a median body length threshold of 200 characters (_MIN_NUMBERED_BODY_CHARS). Tutorial steps typically have short explanatory text between headings, producing a low median that fails validation. Substantial chapters with hundreds of lines pass this test.

Why does the plain numbered regex require two spaces after the number?

The pattern r"^([1-9]\d{0,2})\s{2,}\S" uses \s{2,} to distinguish chapter titles like 1 Introduction from list items formatted as 1. Item or 1) Step. Single-space or punctuation-immediate formats fail matching, preventing false positives.

What happens if a book has only one or two numbered headings?

Fewer than _MIN_NUMBERED_TITLES = 3 distinct numbered headings causes _numbered_titles_are_structural to return False. These headings remain excluded from the levels map. However, detect_structure may still identify chapters through explicit "Chapter N" pattern matching before falling back to structural methods.

Can leading zeros appear in detected chapter numbers?

No. The regex [1-9]\d{0,2} explicitly requires the first digit to be 1-9, excluding formats like 01 or 005. This matches conventional chapter numbering while rejecting zero-padded list indices.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →