# How book-to-skill Resolves Input File Paths: Tilde Expansion and Glob Pattern Support

> Learn how book-to-skill resolves input file paths with tilde expansion and glob pattern support. Discover its three-stage pipeline for deterministic document lists.

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

---

**book-to-skill normalizes and expands file paths through a three-stage pipeline in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) that handles tilde expansion, recursive glob matching, and directory traversal to produce a deterministic, deduplicated list of supported documents.**

The open-source document processing tool [virgiliojr94/book-to-skill](https://github.com/virgiliojr94/book-to-skill) converts books and documents into structured skill extractions using a sophisticated path resolution system. Understanding how it resolves input file paths—including globs and tildes—ensures reliable scripting across different shells and operating systems.

## The `resolve_input_files` Function Architecture

All path resolution logic resides in the **`resolve_input_files`** function within [[`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py)](https://github.com/virgiliojr94/book-to-skill/blob/master/book_to_skill/utils.py) (lines 698‑744). This function processes raw command-line arguments through a sequential pipeline designed to handle edge cases like quoted tildes and non-existent explicit paths.

### Stage 1: Tilde Expansion with `os.path.expanduser`

Every raw path first undergoes **tilde expansion** via `os.path.expanduser` (line 715). This conversion handles the `~` character even when the shell does not expand it, such as when arguments are quoted (e.g., `"~/books/*.epub"`). Without this step, glob patterns containing tildes would be interpreted literally and fail to match files.

### Stage 2: Glob Pattern Resolution and Filtering

If the expanded path does not exist as a literal file and contains glob characters (`*`, `?`, `[`), the function invokes **`glob.glob(..., recursive=True)`** (lines 720‑723). The resulting matches undergo two processing steps:

- **Extension filtering**: Only files with suffixes listed in `SUPPORTED_EXTENSIONS` (defined in [`book_to_skill/config.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/config.py)) are retained.
- **Deterministic sorting**: Matches are sorted using `key=lambda x: str(x).lower()` (lines 724‑728) to guarantee reproducible ordering regardless of underlying filesystem behavior.

### Stage 3: Directory Recursion

When the expanded path resolves to an existing directory (line 730), the function walks the directory tree using **`os.walk`**. It collects all files matching `SUPPORTED_EXTENSIONS` and sorts them deterministically (lines 734‑739), ensuring consistent processing order for batch operations.

## Error Handling and Deduplication

The resolution pipeline includes robust handling for edge cases:

- **Non-existent files**: Explicit file arguments that do not exist are still appended to the result list (line 742), allowing downstream error handling to report clear "file not found" messages rather than silently dropping inputs.
- **Deduplication**: The final stage deduplicates `Path` objects while preserving the original user-specified order for explicit arguments (lines 745‑753). This prevents duplicate processing when glob patterns overlap with explicit file listings.

## Code Examples

The `resolve_input_files` function accepts a list of string paths and returns a list of `pathlib.Path` objects:

```python

# Single explicit file

>>> from book_to_skill import utils
>>> utils.resolve_input_files(["/path/to/book.pdf"])
[PosixPath('/path/to/book.pdf')]

# Quoted home-directory glob (shell-safe)

>>> utils.resolve_input_files(['"~/mybooks/*.epub"'])
[PosixPath('/home/you/mybooks/intro.epub'), PosixPath('/home/you/mybooks/advanced.epub')]

# Directory traversal

>>> utils.resolve_input_files(['/data/books'])
[PosixPath('/data/books/chapter1.pdf'), PosixPath('/data/books/appendix.md')]

# Mixed inputs: explicit, glob, and directory

>>> utils.resolve_input_files([
...     '/tmp/manual.docx',
...     '~/shared/*.txt',
...     '/var/docs'
... ])
[
    PosixPath('/tmp/manual.docx'),               # explicit file (preserved order)

    PosixPath('/home/you/shared/notes.txt'),    # glob results (sorted)

    PosixPath('/var/docs/guide.pdf'),           # directory results (sorted)

]

```

## CLI Integration Points

The path resolution system integrates with the command-line interface through two key components:

- **[`book_to_skill/cli.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/cli.py)**: The argument parser calls `resolve_input_files` immediately after parsing CLI arguments, converting user input strings into resolved `Path` objects before the extraction pipeline begins.
- **[`book_to_skill/config.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/config.py)**: The `SUPPORTED_EXTENSIONS` constant drives the filtering logic in both glob and directory traversal stages, ensuring only extractable document types (PDF, EPUB, DOCX, etc.) enter the processing queue.

## Summary

- **`resolve_input_files`** in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) (lines 698‑744) serves as the centralized path resolution engine.
- **Tilde expansion** occurs via `os.path.expanduser` (line 715) to handle `~` characters safely regardless of shell quoting.
- **Glob patterns** expand recursively and filter against `SUPPORTED_EXTENSIONS`, with deterministic lowercase sorting (lines 720‑728).
- **Directory inputs** trigger recursive `os.walk` traversal with extension filtering (lines 730‑739).
- **Deduplication** preserves explicit argument order while removing duplicates (lines 745‑753).

## Frequently Asked Questions

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

The tool explicitly expands tildes using `os.path.expanduser` at line 715 in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py). This ensures `~/documents` resolves correctly even when the argument is quoted and the shell has not performed expansion, preventing literal tilde characters from causing resolution failures.

### What glob pattern syntax does book-to-skill support?

The implementation uses Python's standard `glob.glob` with `recursive=True` (lines 720‑723), supporting standard wildcards including `*` (any characters), `?` (single character), and `[seq]` (character ranges). These patterns work across nested directory structures when the `**` recursive syntax is included.

### Why are resolved files sorted deterministically?

Files returned from glob and directory operations are sorted via `key=lambda x: str(x).lower()` (lines 724‑728 and 734‑739) to ensure reproducible execution order across different filesystems. This deterministic behavior is critical for consistent batch processing and debugging.

### What happens if I provide a path that doesn't exist?

Non-existent explicit paths are appended to the result list at line 742 rather than filtered out. This design allows the main extraction loop to detect missing files and report specific "file not found" errors, providing clearer debugging information than silent omission would allow.