eBook Manage Build Manual

Build from zero · Agent-facing playbook

從零搭起 Kobo 閱讀與 Book Companion 系統

這不是假設有一個現成 Skill 可以裝。這份文件是給 agent 的施工圖:建立工具、資料夾、指令檔、Kobo authentication、EPUB parser、閱讀進度,以及可跟使用者一起設計的 interpret 工作流。

目標是讓同事把這頁交給自己的本機 agent,agent 就能一步步把同等能力建立起來,並在過程中訪談他的閱讀偏好與公司知識需求。

Reference System Map local files + CLI tools + agent behavior
1 Agent Layer

本機 instructions、觸發詞、閱讀流程與安全邊界。

2 Kobo Adapter

`kobodl` 負責登入、書庫匯出與 EPUB 取得。

3 Book Store

固定本機資料夾保存 EPUB,方便搜尋、續讀、備份。

4 EPUB Parser

用 script 輸出 `info`、`toc`、`chapter` JSON。

5 Companion

深度解讀、章節 Q&A、下一章、跳章、目錄。

6 Context Lens

可選擇接入 Vault、strategy docs、project docs。

7 Progress

local JSON、agent memory 或使用者選的筆記系統。

1. 先把前提說清楚

Agent 不應該把這套東西當成一個已經存在的包。它要照這份 manual 建出一個本機系統,然後和使用者一起決定哪些行為要變成自己的閱讀習慣。

Agent contract: 先建立可運作的最小系統,再訪談使用者的 interpret 風格、書籍操作偏好、context 使用方式與 progress 儲存策略。
  1. 確認使用者的 agent workspace、書籍資料夾、Kobo 帳號狀態。
  2. 安裝 `kobodl`、`jq`、Python parser environment。
  3. 建立本機資料夾、instruction file、EPUB reader script。
  4. 完成 Kobo browser auth,匯出書庫 JSON。
  5. 下載或指定一本 EPUB,跑 `info`、`toc`、`chapter`。
  6. 訪談 Book Companion 的語氣、深度、context、進度儲存。
  7. 跑第一章解讀,確認這就是使用者想要的閱讀體驗。

2. 參考資料夾結構

Marcus 的本機版本長這樣。其他人不需要照抄路徑,但要保留同樣的分層:instructions、scripts、references、books、state。

reference-structure.txt
~/clawd/
  .claude/skills/ebook-manage/
    SKILL.md
    scripts/
      epub_reader.py
    references/
      kobodl-reference.md
      reading-companion-guide.md
      reading-companion-style.md
  state/
    reading-companion/
      progress.json

~/Books/kobo/
  Author - Book Title abc12345.epub
Instruction Layer

放 agent 讀得到的工作流說明、觸發詞、路徑與輸出規格。

Runtime Layer

放 `epub_reader.py` 和 Python venv,讓章節抽取可重複驗證。

State Layer

放 progress JSON 或連接到使用者選的 memory/note system。

3. 工具與環境安裝

預設 macOS + Homebrew;如果使用者是其他 OS,agent 應該換 package manager,但保留同樣的 Python venv 與資料夾設計。

建立資料夾

setup-folders.sh
mkdir -p "$HOME/clawd/.claude/skills/ebook-manage/scripts"
mkdir -p "$HOME/clawd/.claude/skills/ebook-manage/references"
mkdir -p "$HOME/clawd/state/reading-companion"
mkdir -p "$HOME/Books/kobo"

安裝 CLI 與 Python deps

install-tools.sh
brew install pipx jq
pipx ensurepath
pipx install kobodl

python3 -m venv "$HOME/clawd/.venvs/ebook-manage"
"$HOME/clawd/.venvs/ebook-manage/bin/python" -m pip install --upgrade pip
"$HOME/clawd/.venvs/ebook-manage/bin/python" -m pip install ebooklib beautifulsoup4 lxml

驗證工具

validate-tools.sh
kobodl --help >/dev/null
"$HOME/clawd/.venvs/ebook-manage/bin/python" - <<'PY'
import ebooklib
import bs4
import lxml
print("epub parser deps ok")
PY

4. Kobo Authentication

Kobo 由 `kobodl` 做 browser auth。這套系統不要求使用者把 Kobo 密碼、cookie 或 raw API key 貼進聊天。

kobo-auth.sh
kobodl user add
kobodl user list

kobodl book list
kobodl book list --read
kobodl book list --export-library /tmp/kobo-library.json
Agent should ask

你的 Kobo 帳號能不能用 email login?如果只有 Google/Facebook SSO,要先在 Kobo 補一個 email login 方法。

Agent should verify

`kobodl user list` 有帳號、`book list --read` 看得到書、JSON export 可以被 `jq` 讀取。

5. 書庫操作

Agent 要把 Kobo JSON 轉成好選的書單,而不是把原始輸出丟給使用者。

library-ops.sh
kobodl book list --read --export-library /tmp/kobo-library.json
jq 'length' /tmp/kobo-library.json

# After the user picks a book:
kobodl book get "$REVISION_ID" --output-dir "$HOME/Books/kobo"
ls -lh "$HOME/Books/kobo"/*.epub
JSON fieldPurposeAgent behavior
`BookEntitlement.Created`購買或加入日期預設由新到舊排序。
`BookEntitlement.RevisionId`下載識別碼使用者選書後拿它下載。
`ReadingState.StatusInfo.Status`閱讀狀態支援 unread / reading / finished filter。
`CurrentBookmark.ProgressPercent`Kobo 端進度顯示但不要當成本機進度唯一來源。
`BookMetadata.Title`書名支援模糊搜尋。
`ContributorRoles[].Name`作者顯示在選書表格。

6. EPUB Reader Script

Interpret 的內容來源必須是 parser 抽出的章節文字。不要只靠 TOC、metadata 或記憶猜。

epub_reader.py
#!/usr/bin/env python3
"""Deterministic EPUB metadata, TOC, and chapter extraction."""

import json
import re
import sys
from pathlib import Path

import ebooklib
from bs4 import BeautifulSoup
from ebooklib import epub


def strip_html(content: bytes) -> str:
    soup = BeautifulSoup(content, "html.parser")
    for tag in soup(["script", "style"]):
        tag.decompose()
    text = soup.get_text(separator="\n")
    return re.sub(r"\n{3,}", "\n\n", text).strip()


def resolve_href(href: str) -> str:
    return href.split("#")[0]


def flatten_toc(toc, depth=0):
    entries = []
    for item in toc:
        if isinstance(item, tuple):
            section, children = item
            entries.append({
                "title": section.title,
                "href": resolve_href(section.href) if hasattr(section, "href") else None,
                "depth": depth,
                "is_section": True,
            })
            entries.extend(flatten_toc(children, depth + 1))
        else:
            entries.append({
                "title": item.title,
                "href": resolve_href(item.href),
                "depth": depth,
                "is_section": False,
            })
    for index, entry in enumerate(entries):
        entry["index"] = index
    return entries


def chapter_content(book, href: str) -> str:
    for item in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
        if item.get_name() == href or item.get_name().endswith(href):
            return strip_html(item.get_body_content())
    return ""


def load_book(epub_path: str):
    path = Path(epub_path).expanduser()
    if not path.exists():
        print(json.dumps({"error": f"File not found: {path}"}, ensure_ascii=False))
        sys.exit(1)
    return epub.read_epub(str(path)), path


def cmd_info(epub_path: str):
    book, path = load_book(epub_path)
    title = book.get_metadata("DC", "title")
    author = book.get_metadata("DC", "creator")
    language = book.get_metadata("DC", "language")
    print(json.dumps({
        "title": title[0][0] if title else "Unknown",
        "author": author[0][0] if author else "Unknown",
        "language": language[0][0] if language else "Unknown",
        "chapters": len(flatten_toc(book.toc)),
        "file": str(path.resolve()),
    }, ensure_ascii=False, indent=2))


def cmd_toc(epub_path: str):
    book, _ = load_book(epub_path)
    toc_flat = flatten_toc(book.toc)
    for entry in toc_flat:
        href = entry.get("href")
        entry["chars"] = len(chapter_content(book, href)) if href else 0
    print(json.dumps(toc_flat, ensure_ascii=False, indent=2))


def cmd_chapter(epub_path: str, index: int):
    book, _ = load_book(epub_path)
    toc_flat = flatten_toc(book.toc)
    if index < 0 or index >= len(toc_flat):
        print(json.dumps({"error": f"Index {index} out of range"}, ensure_ascii=False))
        sys.exit(1)
    entry = toc_flat[index]
    content = chapter_content(book, entry.get("href") or "")
    print(json.dumps({
        "index": index,
        "title": entry["title"],
        "chars": len(content),
        "total_chapters": len(toc_flat),
        "content": content,
    }, ensure_ascii=False, indent=2))


def main():
    if len(sys.argv) < 3:
        print("Usage: epub_reader.py info|toc|chapter <epub_path> [index]")
        sys.exit(1)
    command = sys.argv[1]
    epub_path = sys.argv[2]
    if command == "info":
        cmd_info(epub_path)
    elif command == "toc":
        cmd_toc(epub_path)
    elif command == "chapter":
        if len(sys.argv) < 4:
            print(json.dumps({"error": "Missing chapter index"}, ensure_ascii=False))
            sys.exit(1)
        cmd_chapter(epub_path, int(sys.argv[3]))
    else:
        print(json.dumps({"error": f"Unknown command: {command}"}, ensure_ascii=False))
        sys.exit(1)


if __name__ == "__main__":
    main()
reader-commands.sh
chmod +x "$HOME/clawd/.claude/skills/ebook-manage/scripts/epub_reader.py"

EPUB="$HOME/Books/kobo/example.epub"
PY="$HOME/clawd/.venvs/ebook-manage/bin/python"
READER="$HOME/clawd/.claude/skills/ebook-manage/scripts/epub_reader.py"

"$PY" "$READER" info "$EPUB"
"$PY" "$READER" toc "$EPUB"
"$PY" "$READER" chapter "$EPUB" 0

7. Book Companion

這層不是摘要機器,而是閱讀夥伴。它要保留作者的推理、案例、概念轉折,並在必要時接回使用者自己的工作脈絡。

SKILL.md starter
---
name: ebook-manage
description: Kobo eBook library management and Book Companion reading workflow.
---

# eBook Manage

Use this workflow when the user asks to browse Kobo books, read an EPUB,
continue a book, interpret a chapter, or move to the next chapter.

## Default Paths

- Workspace: ~/clawd
- Books: ~/Books/kobo
- Reader script: ~/clawd/.claude/skills/ebook-manage/scripts/epub_reader.py
- Parser Python: ~/clawd/.venvs/ebook-manage/bin/python
- Progress: ~/clawd/state/reading-companion/progress.json

## Reading Flow

1. Select a book from Kobo library or direct EPUB path.
2. Download the selected Kobo EPUB if needed.
3. Run epub_reader.py info.
4. Run epub_reader.py toc.
5. Ask where to start unless resuming.
6. Run epub_reader.py chapter.
7. Interpret the extracted chapter.
8. Save progress.
9. Offer next chapter, previous chapter, jump, TOC, or Q&A.

## Interpretation Contract

- Explain what problem the chapter is solving.
- Reconstruct the author's reasoning chain.
- Preserve important examples, definitions, and metaphors.
- Add analysis, but separate it from the author's claim.
- Use user/company context only when relevant.
- End with navigation options.
Suggested triggers

讀書、讀這本、幫我讀、繼續讀、下一章、上一章、目錄、跳到第 X 章、book companion。

Output shape

章節問題、核心論點、作者敘事順序、框架圖、對使用者工作的意義、takeaways、下一步導航。

8. 使用者知識脈絡

如果使用者希望解讀能理解自己的公司、策略或 Obsidian Vault,agent 要先建立一個 context layer,而不是把私有資料硬塞進每段文字。

可接來源

Obsidian Vault、project docs、strategy docs、company knowledge base、meeting notes、manual context file。

使用原則

每章只抓 3-7 個真正相關的 context,說清楚為什麼相關,不要喧賓奪主。

Fallback

沒有搜尋系統時,用 `rg` 先做資料夾內關鍵字搜尋,再請使用者確認範圍。

context-fallback.sh
VAULT_PATH="$HOME/Obsidian"
rg -n "strategy|roadmap|growth|principle|decision|product" "$VAULT_PATH"

9. 客製化訪談

核心建起來後,agent 要帶使用者做一輪 product discovery。這套系統最值錢的地方,是它可以變成使用者自己的閱讀 operating system。

AreaDefaultQuestion
Book sourceKobo + local EPUB只讀 Kobo,還是也接本機 EPUB?
Interpret depthDeep/adaptive你想要摘要、導讀、深度專欄,還是依章節密度調整?
Context lensRelevant only要不要連到你的公司策略、專案、Vault?哪些資料夾安全?
ProgressLocal JSON進度要存在 JSON、agent memory、還是 Obsidian note?
Knowledge captureAsk first讀到好的 principle,要不要累積到公司共同知識?
OutputChat要只回聊天,還是產生 Markdown reading note?

Agent brainstorming prompts

  1. 當你說一篇解讀「有幫助」,它應該幫你讀完後做什麼?
  2. 這個 companion 比較像老師、策略夥伴、研究助理,還是讀書筆記助手?
  3. 哪些公司脈絡應該影響解讀?哪些東西不該被帶進來?
  4. 每章是否要抽出 reusable principles?要存到哪裡?
  5. 如果書中的觀點和你現在的策略衝突,agent 要不要直接指出?
  6. 你自然會輸入哪些閱讀指令?

10. End-To-End 驗證

不要只確認檔案存在。必須用一本真實 EPUB 跑完 auth、書庫、parser、interpret、progress。

e2e-check.sh
kobodl user list
kobodl book list --read --export-library /tmp/kobo-library.json
jq 'length' /tmp/kobo-library.json

EPUB="$HOME/Books/kobo/example.epub"
PY="$HOME/clawd/.venvs/ebook-manage/bin/python"
READER="$HOME/clawd/.claude/skills/ebook-manage/scripts/epub_reader.py"

"$PY" "$READER" info "$EPUB"
"$PY" "$READER" toc "$EPUB"
"$PY" "$READER" chapter "$EPUB" 0
Setup is done when

Kobo auth 可用、JSON export 成功、至少一本 EPUB 可被 parser 抽出章節、progress 可保存並續讀。

Agent final report should include

安裝了什麼、auth 狀態、書籍資料夾、測試 EPUB、parser 輸出摘要、使用者選的客製化設定。