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

book-to-skill normalizes and expands file paths through a three-stage pipeline in 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 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/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) 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:


# 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: 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: 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 (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. 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.

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 →