# How book-to-skill Resolves Input Files and Handles Glob Patterns

> Discover how book-to-skill resolves input files. Learn about its efficient handling of glob patterns, extension filtering, and deterministic deduplication for seamless project management.

- Repository: [Virgilio Junior/book-to-skill](https://github.com/virgiliojr94/book-to-skill)
- Tags: how-to-guide
- Published: 2026-08-26

---

**book-to-skill resolves input files by expanding tildes, detecting glob patterns, filtering by supported extensions, and deduplicating results while preserving deterministic order.**

The `book-to-skill` repository provides a robust document extraction pipeline that accepts diverse input specifications—from single files to complex glob expressions. Understanding how it resolves these inputs ensures you can efficiently batch-process PDFs, EPUBs, and other supported formats without manual file enumeration.

## The Core Resolution Pipeline

The heart of input resolution resides in **[`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py)**, specifically within the `resolve_input_files()` function. This utility implements an eight-step pipeline that transforms raw command-line arguments into a validated, ordered list of absolute `Path` objects.

### Step 1: Tilde Expansion

Before any pattern matching occurs, the function expands user home directories. Each raw path passes through `os.path.expanduser()`, converting leading tildes (`~`) into absolute home directory paths.

```python
path_str = os.path.expanduser(raw_path)

```

This operation respects the `HOME` environment variable on POSIX systems and `USERPROFILE` on Windows, ensuring cross-platform consistency whether you specify `~/books/*.epub` or `~user/document.pdf`.

### Step 2: Glob Pattern Detection

The resolver distinguishes between literal paths and glob patterns by checking two conditions: the path must not exist as a file or directory, and it must contain wildcard characters (`*`, `?`, or `[`).

```python
if not Path(path_str).exists() and any(char in path_str for char in '*?['):
    # Treat as glob pattern

```

This heuristic prevents accidental glob expansion of legitimate filenames while capturing intentional patterns like `**/*.md` or `documents/lecture?.pdf`.

### Step 3: Pattern Matching and Extension Filtering

When a glob pattern is detected, `resolve_input_files()` invokes `glob.glob(path_str, recursive=True)` to collect matches. Each result undergoes validation against `SUPPORTED_EXTENSIONS`—a constant defined in the configuration layer that includes PDF, EPUB, DOCX, RTF, TXT, HTML, and Calibre e-book formats.

Only files matching these extensions proceed; directories and unsupported binary files are silently discarded during this phase.

### Step 4: Deterministic Sorting

To guarantee reproducible execution across runs, matched files undergo case-insensitive lexical sorting:

```python
expanded.sort(key=lambda x: str(x).lower())

```

This deterministic ordering proves essential for downstream agents that may cache results or rely on consistent file sequencing.

### Step 5: Directory Recursion

When the input resolves to an existing directory, the function switches to `os.walk()` traversal. The walker recursively descends the directory tree, collecting all supported files (filtered by the same `SUPPORTED_EXTENSIONS` criteria) and applying identical case-insensitive sorting logic.

### Step 6: Plain File Preservation

Arguments that are neither globs nor directories are appended verbatim to the results list, even if they do not exist on the filesystem. This deferral strategy allows callers to handle validation errors later with specific context, rather than failing silently during the resolution phase.

### Step 7: Deduplication with Order Preservation

After processing all arguments, the function eliminates duplicates using a set-based tracking mechanism while maintaining insertion order. This ensures that explicitly named files retain priority over glob-discovered duplicates, and the final list contains only unique absolute paths.

## Supported File Extensions

The resolution logic strictly filters inputs against `SUPPORTED_EXTENSIONS`, which typically includes:

- **Document formats**: `.pdf`, `.docx`, `.rtf`
- **E-book formats**: `.epub`, `.mobi`, `.azw3`
- **Markup and text**: `.html`, `.htm`, `.txt`, `.md`

This filtering occurs in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) lines 75-81 and prevents the extractor from attempting to parse binary assets or unsupported file types.

## Practical Usage Examples

### Command-Line Interface

The CLI transparently passes arguments to `resolve_input_files()`, supporting mixed input types:

```bash

# Single file with tilde expansion

book-to-skill ~/documents/thesis.pdf

# Recursive glob for all EPUBs in home directory

book-to-skill "~/books/**/*.epub"

# Mixed inputs: explicit file, glob pattern, and directory

book-to-skill chapter1.pdf "~/downloads/*.docx" ./reference_materials/

```

### Python API

Import `resolve_input_files` directly for programmatic control:

```python
from book_to_skill.utils import resolve_input_files

raw_inputs = [
    "~/ebooks/*.epub",
    "./technical_docs/",
    "specific_article.pdf"
]

resolved_paths = resolve_input_files(raw_inputs)

for path in resolved_paths:
    print(f"Processing: {path}")
    # Each path is an absolute Path object ready for extraction

```

The function returns a `list[Path]` where each element is an absolute, deduplicated, and extension-filtered file location.

## Error Handling and Edge Cases

The resolver exhibits specific behaviors for problematic inputs:

- **Unresolvable tildes**: Paths like `~nonexistentuser/file.txt` remain unchanged (tilde unexpanded) so downstream validators can raise clear `ExtractionError` messages
- **Empty glob matches**: Patterns matching zero files result in no additions to the results list, not runtime exceptions
- **Permission errors**: During directory walks, permission-denied directories are skipped rather than crashing the traversal

These behaviors are validated by the test suite in [`tests/test_tilde_expansion.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tests/test_tilde_expansion.py) and [`tests/test_book_to_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tests/test_book_to_skill.py), which verify correct handling of `HOME`/`USERPROFILE` overrides, glob filtering accuracy, and duplicate removal semantics.

## Summary

- **`resolve_input_files()`** in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) orchestrates all input resolution through an eight-step pipeline
- **Tilde expansion** occurs first via `os.path.expanduser()`, supporting cross-platform home directory resolution
- **Glob detection** relies on wildcard character presence combined with path non-existence checks
- **Extension filtering** enforces the `SUPPORTED_EXTENSIONS` whitelist during both glob and directory traversal phases
- **Deterministic sorting** uses case-insensitive string comparison to ensure reproducible file ordering
- **Deduplication** preserves insertion order while eliminating redundant paths across mixed input types

## Frequently Asked Questions

### How does book-to-skill handle the tilde (~) character in file paths?

book-to-skill expands tildes using `os.path.expanduser()` before pattern matching, converting `~/documents/file.pdf` into an absolute path based on the `HOME` or `USERPROFILE` environment variable. If the username portion is invalid (e.g., `~nonexistentuser`), the path remains unexpanded for later error reporting.

### What glob syntax does book-to-shelf support?

The tool uses Python's standard `glob.glob()` with `recursive=True`, supporting `*` for wildcards, `?` for single characters, `[seq]` for character classes, and `**/` for recursive directory traversal. Patterns like `~/books/**/*.epub` correctly match EPUB files at any depth within the books directory.

### Why are my unsupported files being ignored during resolution?

`resolve_input_files()` filters all candidates against `SUPPORTED_EXTENSIONS`, which includes only document and e-book formats like PDF, EPUB, DOCX, and HTML. Files with extensions such as `.jpg`, `.zip`, or `.exe` are silently excluded during both glob expansion and directory walking to prevent extractor failures.

### What happens if I provide a path that does not exist?

Non-existent paths that do not contain glob wildcards are appended to the results list verbatim as absolute paths. The function does not raise errors during resolution; instead, it allows downstream components like `extract_single_file()` to detect the missing file and raise an appropriate `ExtractionError` with specific context.