AI News HubLIVE
In-site rewrite6 min read

A few thoughts on building a terminal ePub reader with AI

The author used AI coding agents to port the Python e-book reader epy to Rust, creating repy. The project took months instead of hours and garnered little attention, prompting reflections on the devaluation of software in the age of AI and the meaning of creation.

SourceHacker News AIAuthor: dawdler-purge

Notifications You must be signed in to change notification settings

Fork 3

Star 8

BranchesTags

Open more actions menu

Folders and files

NameName

Last commit message

Last commit date

Latest commit

History

307 Commits

307 Commits

.claude/skills/verify

.claude/skills/verify

.github/workflows

.github/workflows

screenshots

screenshots

specs

specs

src

src

tests

tests

.gitignore

.gitignore

.gitmodules

.gitmodules

AGENTS.md

AGENTS.md

CLAUDE.md

CLAUDE.md

Cargo.lock

Cargo.lock

Cargo.toml

Cargo.toml

GEMINI.md

GEMINI.md

README.md

README.md

ROADMAP.md

ROADMAP.md

to-do.md

to-do.md

Repository files navigation

Reflective notes

I like to do things in the terminal. It has little to do with efficiency. It is more my obsession with fiddling with software, so I don't have to think about all the depressing things happening in the world.

One terminal program I tried is epy, an e-book reader. I quite liked its speed. But like many other open source projects, it stopped being maintained.

So last November (2025), when coding agents like Codex CLI and Claude Code got better, I started to port epy from Python to Rust. I thought it was a task for an afternoon. After all, how hard can it be to display text in a terminal?

It turns out that rendering EPUB correctly in a terminal is a messy business. The format was not designed to be displayed in a terminal at all. And for a reasonable reading experience, you have to solve the problem of navigating the book correctly, which the AI struggled with quite a bit. Instead of a few hours, the agents worked for days, adding more and more features, until I was comfortable sharing the project in February this year (2026).

It got five up-votes and eight stars on GitHub. Perhaps there is not enough interest in terminal-based EPUB readers. Perhaps there is a general dislike of AI-built software. But had this project been published a few years earlier, I doubt the interest would have been so little.

With the recent release of Claude Fable 5 and GPT Sol 5.6, I added quite a few more features. Yet I doubt it would get even five up-votes this time.

With the abundance of software comes the devaluation of software. "Why would I use this, when I can ask my agent to spit out something similar, but closer to my own likes and dislikes?" A thing anyone can have made at will is a thing nobody treasures.

Books may be an analogue, even before authors began publishing hundreds of AI-generated novels. I used to read a lot, sometimes two dozen books a year. I kept an eye on authors I liked and tracked what they were publishing. Then I realized there are already far more good books than I can read in this lifetime. So new books, even great ones, mean nothing to me anymore. It is not that the books got worse. It is that my time was always the scarce thing, and now I can see it.

If we are really seeing the dawn of AGI, perhaps humans have to reckon with this: when there are far more intellectual artifacts --- games, software, books, music, films --- than anyone could consume in a lifetime, what is the meaning of creating more?

But maybe the question is older than it looks. Seneca complained two thousand years ago that the abundance of books distracts. There was already more than one life could hold; only the speed is new. And nobody ever wrote to finish writing, or read to finish reading. I did not port epy because the world lacked an e-book reader. I ported it to have something to fiddle with --- an afternoon that became months. The worth of making was never in being consumed. If it keeps my mind off the depressing things happening in the world, that is meaning enough, five up-votes or none.

⚠️ MASSIVE WARNING ⚠️

This is 100% AI-generated code. Every single line was written by Codex CLI, Gemini CLI, and Claude Code --- the human has not written a single line of Rust. That said, it works well for daily use. No guarantee it won't eat your epub, delete your database, or crash your terminal. You're on your own. PRs welcome.

Rust reimplementation of the excellent CLI ebook reader epy.

repy keeps the keyboard-first reading experience familiar while adding fast chapter rendering, inline terminal graphics, persistent annotations, and a self-contained SQLite library.

EPUB artwork rendered directly in the reading flow.

Jump through the table of contents, then select, copy, annotate, or look up text without leaving the reader.

Status

Functional for daily use! Core reading features are complete: TUI navigation, search, bookmarks, library management, two-phase cursor/selection modes, image viewing, link/footnote handling, dictionary lookup, Wikipedia lookup, persistent highlights/comments, highlight export, and TTS (text-to-speech) all work. Text is intelligently wrapped and hyphenated. Reading state and preferences are persisted per-book.

Supported formats: EPUB, FictionBook (.fb2 and .fb2.zip), MOBI6 (.mobi), plain text (.txt), Markdown (.md), and comic book archives (.cbz --- set "inline_images": "shown" and use a graphics-capable terminal such as kitty to see the pages). AZW/AZW3 files are accepted on a best-effort basis; KF8-only content may not be readable by the MOBI6 parser.

See to-do.md for detailed feature status and roadmap.

Installation

Download Binaries

You can download pre-built binaries for Linux, Windows, and macOS from the GitHub Releases page.

Linux: Download repy-linux-x86_64 (compatible with most modern distributions).

Windows: Download repy-windows-x86_64.exe.

macOS: Download repy-macos-universal (works natively on both Intel and Apple Silicon Macs).

After downloading, rename the file to repy (or repy.exe on Windows) and make it executable:

Linux/macOS

chmod +x repy-*-* mv repy-*-* /usr/local/bin/repy

Build from source

If you prefer to build it yourself, you need Rust and Cargo installed.

Clone this repository

git clone https://github.com/newptcai/repy.git cd repy

Build and install

cargo install --path .

The bundled rusqlite feature is enabled, so no system-wide libsqlite3 installation is required; SQLite is compiled and linked as part of the build.

Usage

Opening a book

To open any EPUB, plain-text, or Markdown file (doesn't need to be in your library):

repy /path/to/book.epub repy /path/to/notes.md

Starting without arguments

repy

If there is a reading history, repy reopens the last-read book at the last saved position. Otherwise, it starts in the reader UI without a book loaded.

Opening books from the reading history

The EBOOK argument can be a file path, a reading-history number, or a pattern matched case-insensitively against the title, author, and path of history entries (the most recently read match wins):

repy -r # Print the reading history with numbers and progress repy 3 # Open the 3rd book in the reading history repy dorian # Open the most recent history entry matching "dorian"

Other options

repy -d BOOK # Dump the parsed text of an ebook to stdout (pipe to less/grep) repy -c FILE # Use a specific configuration file repy -v # Increase verbosity (for debugging) repy --debug # Enable debug output repy --export-highlights /path/to/book.epub

--export-highlights writes all persisted highlights/comments for that EPUB to stdout. The default format is JSON (including the book identity); pass --format md for Markdown grouped by chapter, with quotes, notes, and dates:

repy --export-highlights book.epub --format md > notes.md

Search

Search functionality supports regular expressions.

Start Search: Press / to open the search input.

Incremental: Matches update live as you type, and the view previews the first match at or after your current position. Esc while typing cancels and restores the original position. Invalid partial regexes simply show no matches.

History: Up / Down while typing recall previous queries (persisted across sessions, most recent first, capped at 100). Down past the newest entry restores the query you were typing.

Navigation:

Enter: Confirm the query (recorded in history). Then j/k or Up/Down browse results, and a second Enter jumps and closes the window.

n: Jump to the next search hit.

p / N: Jump to the previous search hit.

Clear Highlights: There is no dedicated key to clear highlights. A workaround is to press / to start a new search (which clears existing highlights) and then Esc to cancel.

Current Hit: All matching text is highlighted in yellow; the line containing the current hit is highlighted in orange. A match N/M counter is shown in the top bar and status messages while navigating with n, p, or N.

Keybindings

Press ? in the TUI to see the help window at any time (Help (?)).

Navigation

k / Up --- Line Up

j / Down --- Line Down

h / Left --- Page Up

l / Right --- Page Down

Space --- Page Down

Ctrl+u --- Half Page Up

Ctrl+d --- Half Page Down

L --- Next Chapter

H --- Previous Chapter

g --- Chapter Start

G --- Chapter End

Home --- Book Start

End --- Book End

Jump History

Ctrl+o --- Jump Back

Ctrl+i / Tab --- Jump Forward

Display

+ / - --- Increase/Decrease Width

= --- Reset Width

T --- Toggle Top Bar

c --- Cycle Color Theme

Annotations

A --- Highlights list

Enter in highlights list --- Jump to selected highlight

e in highlights list --- Edit comment

d in highlights list --- Delete highlight

d in cursor mode --- Delete highlight under cursor

Windows & Tools

/ --- Search

! --- Text-to-Speech (Toggle)

v --- Cursor Mode

t --- Table of Contents

m --- Set a persistent mark (a-z, A-Z, 0-9)

` --- Jump to a persistent mark

B --- Bookmarks (a to add, d to delete, Enter to jump)

u --- Links on Page (Enter previews internal links; Enter again jumps)

o --- Images on Page

Enter shows the selected image in the terminal (kitty, iTerm2, or sixel graphics when the terminal supports them, halfblocks otherwise); Esc/q returns to the list

o opens it with the external viewer instead (default_viewer setting, then feh, then xdg-open); SVG images always use the external viewer

With "inline_images": "shown" (also toggleable in Settings), images render directly in the reading flow: space is reserved under each placeholder and the image appears once its block is fully on screen

i --- Metadata

s --- Settings, including typography controls:

paragraph style cycles through spaced, compact, and indented; indented paragraphs use a two-column first-line indent

line spacing cycles through 1.0, 1.5, and 2.0

justification expands eligible prose lines while leaving final lines, headings, lists, code, centered text, and CJK-only lines unchanged

r --- Library (reading history merged with books found on disk)

j/k to select an entry

Enter to open the selected book

c to show or hide the selected book's details and cover (off by default)

f to cycle among available formats for a Calibre book

R to refresh configured library directories

d to delete the selected history entry

s to cycle the sort order: recent / title / author / series / progress

Books found in library_directories but never opened show as new/unread; history entries whose file has disappeared are marked [missing]

When enabled with c, a responsive details panel shows metadata and all available formats; supported graphics terminals also show the cover (Calibre-style cover.jpg files are used directly, otherwise the cover is read from the ebook)

R --- Reading Statistics

s --- Settings

Enter: Activate (toggle boolean, input for dictionary client)

r: Reset to default

Dictionary command templates use %q as the query placeholder

q --- Quit / Close Window

In the Table of Contents, Bookmarks, Highlights, and Library windows, press / to fuzzy-filter the list. Matches narrow as you type, best match first. Enter acts on the selected entry directly, or confirms the filter so j/k can navigate the narrowed list; Esc clears the filter (a second Esc clo

[truncated for AI cost control]