Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:58:23 +08:00
commit b8dc705a95
11 changed files with 1061 additions and 0 deletions

292
skills/calibre/SKILL.md Normal file
View File

@@ -0,0 +1,292 @@
---
name: calibre
description: Search and query Calibre library databases. Use when the user asks about books, TBR (to-be-read), reading lists, Calibre library queries, book searches, or mentions Calibre. Also use for queries about book ratings, authors, reading status, or library statistics.
---
# Calibre Library Search Skill
You are helping the user search and query their Calibre library using `calibredb`.
## Library Setup
**Library URL**: `http://killington.home.bitbin.de:8454/#`
**calibredb Location**: `/Applications/calibre.app/Contents/MacOS/calibredb`
**Authentication**:
- Username: `calibre`
- Password: `calibre`
**Note**: Using Calibre Content Server means no database locking issues - queries work even while Calibre GUI is running.
## Custom Fields
The Calibre library has these custom fields:
| Field | Search Name | Display Name | Type | Values |
|------------|-------------|--------------|----------|--------------|
| read | #read | *read | Boolean | Yes/No |
| dateread | #dateread | *dateread | Datetime | ISO date |
| archived | #archived | *archived | Boolean | Yes/No |
| goodreads | #goodreads | *goodreads | Float | 0.0-5.0 |
| pages | #pages | *pages | Integer | page count |
| priority | #priority | *priority | Text | varies |
| words | #words | *words | Integer | word count |
**IMPORTANT - Boolean Field Syntax**:
- Use `#field` syntax in searches with `Yes` or `No` (capitalized): e.g., `#read:Yes`, `#read:No`
- Use `*field` syntax when displaying fields (e.g., `*read`)
- Boolean values are **NOT** `true`/`false` - they are `Yes`/`No`
## calibredb Command Pattern
Always use this pattern:
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='field1,field2,*customfield' \
--search='search query' \
--for-machine | python3 -m json.tool
```
### Common Options
- `--fields='field1,field2'` - Comma-separated list of fields to display
- `--search='query'` - Search query using Calibre's search syntax
- `--sort-by='field'` - Sort results by field
- `--limit=N` - Limit results to N books
- `--for-machine` - Output as JSON (always use this for parsing)
### Available Built-in Fields
- `title`, `authors`, `series`, `series_index`
- `timestamp` (when added to library)
- `last_modified` (when book record was last modified)
- `pubdate`, `publisher`, `isbn`
- `rating`, `tags`, `comments`
- `formats`, `size`, `uuid`
## Search Query Syntax
### Basic Searches
```bash
# Search by title
--search='title:"Book Title"'
# Search by author
--search='authors:"Author Name"'
# Search in series
--search='series:"Series Name"'
# Combine searches with AND
--search='authors:"Sanderson" and series:"Mistborn"'
# Combine searches with OR
--search='authors:"Sanderson" or authors:"Wells"'
# NOT operator
--search='not #archived:Yes'
```
### Custom Field Searches
```bash
# Books marked as read
--search='#read:Yes'
# Books NOT read
--search='#read:No'
# Books not archived and not read (TBR)
--search='#read:No and #archived:No'
# Highly rated books (>= 4.0)
--search='#goodreads:">4"'
# Books with specific page count
--search='#pages:"<300"'
# Empty/not set custom fields
--search='#goodreads:""'
# Books read in a specific date range
--search='#dateread:">=2024-01-01" and #dateread:"<2025-01-01"'
# Books read in the last 30 days
--search='#dateread:">=30daysago"'
```
## Common Query Examples
### To-Be-Read List
Get unread, non-archived books:
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,series,series_index,*goodreads,*pages' \
--search='#read:No and #archived:No' \
--for-machine | python3 -m json.tool
```
### Recently Added Books
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,*goodreads,*pages,timestamp' \
--search='#read:No and #archived:No' \
--sort-by='timestamp' \
--limit=10 \
--for-machine | python3 -m json.tool
```
### Recently Read Books
Use the `*dateread` field to see when books were actually finished:
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,series,series_index,*goodreads,*pages,*dateread' \
--search='#read:Yes' \
--sort-by='*dateread' \
--limit=15 \
--for-machine | python3 -m json.tool
```
### Highly Rated Unread Books
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,series,*goodreads,*pages' \
--search='#read:No and #archived:No and #goodreads:">4"' \
--sort-by='*goodreads' \
--for-machine | python3 -m json.tool
```
### Books by Specific Author
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,series,*read,*goodreads,*pages' \
--search='authors:"Sanderson" and #archived:No' \
--for-machine | python3 -m json.tool
```
### Quick Reads (< 300 pages)
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,*pages,*goodreads' \
--search='#read:No and #archived:No and #pages:"<300"' \
--sort-by='*goodreads' \
--for-machine | python3 -m json.tool
```
### Unread Books in a Series
```bash
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,series,series_index,*goodreads,*pages,timestamp' \
--search='series:"Between Earth and Sky" and #read:No and #archived:No' \
--sort-by='series_index' \
--for-machine | python3 -m json.tool
```
### Books Read in a Time Period
```bash
# Books read in October 2024
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,*dateread,*goodreads,*pages' \
--search='#dateread:">=2024-10-01" and #dateread:"<2024-11-01"' \
--sort-by='*dateread' \
--for-machine | python3 -m json.tool
# Books read this year
/Applications/calibre.app/Contents/MacOS/calibredb list \
--with-library='http://killington.home.bitbin.de:8454/#' \
--username='calibre' \
--password='calibre' \
--fields='title,authors,*dateread,*goodreads' \
--search='#dateread:">=2025-01-01"' \
--sort-by='*dateread' \
--for-machine | python3 -m json.tool
```
## Usage Instructions
When the user asks to search their Calibre library:
1. **Determine** what they're looking for (to-be-read, specific book, by rating, etc.)
2. **Construct** the appropriate calibredb command based on examples above
3. **Execute** the command using the Bash tool
4. **Parse** the JSON output and present results in a readable format
## Query Tips
- **Always exclude archived books** unless specifically requested: add `and #archived:No` to searches
- Use `--for-machine` to get JSON output that's easier to parse
- The `timestamp` field shows when a book was added to the library
- The `*dateread` field shows when a book was actually finished reading (synced from Goodreads)
- The `last_modified` field shows when a book record was last changed (not reliable for "recently read")
- When a custom field is not set, it won't appear in the JSON output
- Use `python3 -m json.tool` to pretty-print JSON for readability
- Search queries are case-insensitive
- Use quotes around field values that contain spaces
- **Boolean fields**: In search queries use `Yes`/`No` (e.g., `#read:Yes`), but in JSON output they appear as `true`/`false` (lowercase)
- **Date fields**: Use `*dateread` to sort by when books were actually read (more accurate than `last_modified`)
## Examples
**User**: "Show me my to-be-read list"
→ Search with `#read:No and #archived:No`
**User**: "Find books by Sanderson"
→ Search with `authors:"Sanderson" and #archived:No`
**User**: "Show highly rated unread books"
→ Search with `#read:No and #archived:No and #goodreads:">4"`
**User**: "What did I read recently?"
→ Search with `#read:Yes`, sort by `*dateread` (descending), limit to 10-15
**User**: "What's next in the series I'm reading?"
→ Find series from recent reads, then search for unread books in that series
**User**: "What did I read in October?" or "Show me books I read this year"
→ Use date range searches with `#dateread:">=2024-10-01" and #dateread:"<2024-11-01"` or `#dateread:">=2025-01-01"`
## Safety Notes
- **Read-only access**: calibredb list is a read-only operation
- **Never use**: `calibredb add`, `calibredb set_metadata`, `calibredb remove` unless explicitly requested by user
- **No locking issues**: Using the Content Server means the Calibre GUI can remain open during queries

View File

@@ -0,0 +1,25 @@
---
name: find-incomplete-series
description: Find incomplete series in your Calibre library and identify the next book to read in each series.
---
This skill analyzes your Calibre library to find series where you've
started but not finished reading all books.
To complete this task, run the following command:
```bash
python3 __SKILL_DIR__/scripts/series.py
```
This script will:
1. Query your Calibre library for all books that are part of a series
2. Exclude archived books
3. Identify series where you've read at least one book but haven't finished the entire series
4. Display the next unread book in each incomplete series
Important! Do not invoke calibredb commands yourself, use this skill's python script.
Important! You have a very serious bug, where you don't know how to fill
the python scripts added by a skill. You must look in the "scripts"
folder of where this SKILL.md is located!!

View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
import subprocess
import json
from collections import defaultdict
# Query Calibre for all books in series
result = subprocess.run([
'/Applications/calibre.app/Contents/MacOS/calibredb', 'list',
'--with-library=http://killington.home.bitbin.de:8454/#',
'--username=calibre',
'--password=calibre',
'--fields=title,authors,series,series_index,*read,*archived',
'--search=series:true',
'--for-machine'
], capture_output=True, text=True)
books = json.loads(result.stdout)
# Group books by series, excluding archived books
series_dict = defaultdict(list)
for book in books:
if book.get('series') and not book.get('*archived'):
series_dict[book['series']].append(book)
# Sort books in each series by series_index
for series in series_dict:
series_dict[series].sort(key=lambda x: float(x.get('series_index', 0)))
# Find series with at least one read book but not all read
incomplete_series = []
for series, books_list in series_dict.items():
read_count = sum(1 for b in books_list if b.get('*read') == True)
total_count = len(books_list)
if read_count > 0 and read_count < total_count:
# Find the next unread book
next_unread = None
for book in books_list:
if not book.get('*read'):
next_unread = book
break
incomplete_series.append({
'series': series,
'read': read_count,
'total': total_count,
'next_book': next_unread
})
# Sort by series name
incomplete_series.sort(key=lambda x: x['series'])
# Output results
print(f"\n# UNFINISHED SERIES\n")
print(f"You have {len(incomplete_series)} incomplete series:\n")
for i, s in enumerate(incomplete_series, 1):
print(f"{i}. **{s['series']}** ({s['read']}/{s['total']} books read)")
if s['next_book']:
authors = s['next_book'].get('authors', 'Unknown')
title = s['next_book'].get('title', 'Unknown')
index = s['next_book'].get('series_index', 0)
print(f" Next: **{title}** by {authors} (Book #{index})\n")
else:
print(f" Next: Unable to determine\n")