Skip to content

brfid/readings

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

109 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Reading Tracker

GitHub Issues are the source of truth. Any agent with the gh CLI, authenticated with repo scope against this repository, can run every operation below directly — no local clone required.

Commands below assume export GH_REPO=brfid/readings, which lets gh drop the repeated --repo flag.

Design principles

  • Labels are the queryable dimensions; everything else is a comment. Type and started-or-not are labels. Never store the same fact in two places.
  • Store only what you can't derive. The issue already records when it was logged, when it closed, and how. Don't restate any of that.
  • The body is the author, then optionally the link. One line always, a second line only when there is a URL. Nothing else, ever.
  • No files. No per-reading files or folders, no saved copies. The issue is the whole record.

Schema

Labels (the queryable dimensions)

  • type:{book,paper,web}
  • reading — you've started it

Status (derived from issue state, never a label)

Issue state Close reason Meaning
open queued
open + reading in progress
closed completed finished
closed not planned abandoned

Closing touches no labels, so reading stays on a finished item as a record that you started it. That's why the in-progress query is --label reading with no state flag — gh issue list is open-only by default, and the closed ones drop out on their own.

A reread is a reopen. It needs no flag: the issue goes back to open, reading goes back on, and the label-change events record that it happened twice.

Body

Line 1 is the author's name, bare, always present:

Ursula K. Le Guin

No **Author(s):** prefix. Multiple authors are one comma-separated line; et al. is fine. The title lives in the issue title.

Line 2 is a bare URL, and exists only when there is one:

Martin Fowler
https://martinfowler.com/bliki/ContinuousDelivery.html

No **Location:** prefix either. A field name earns its bytes only when there is a second field to tell it apart from, and a URL tells itself apart — https:// is the field name. That's also why nothing here can go blank: an item with no link has a one-line body, not an empty field.

The link lives in the body rather than a comment because the body is what reaches people. A GitHub feed card is generated by the issue-opened event and carries the title plus the opening of the body; comments generate no card at all. Line 2 is inside the excerpt window on any item with a normal-length author line.

What counts as a link. A way to read the text, not a way to find out about it. Papers and web items: the canonical URL. Books: only a full text or a substantial selection — an author's posted chapter, an archive scan. Never a publisher, bookstore, Goodreads, or Wikipedia page. If no such link exists, there is no second line, and for most books there won't be.

Body text is searchable, so this is a real queryable path, and it means the link is to this domain rather than this domain got mentioned somewhere:

gh issue list --state all --search "martinfowler.com in:body"

Comments

Everything that isn't the schema: where a physical copy is, reactions, quotes worth keeping, follow-ups. A comment exists only once you write one, so there is nothing blank to maintain.

A copy location that isn't a URL — Books app, a shelf, a library — is a comment. Only URLs go on line 2, which is what keeps reading the body a matter of position and never of pattern-matching.

Setup (one-time)

for label in type:book type:paper type:web reading; do
  gh label create "$label" --force
done

Keep the label list at exactly these four. Every open issue is assumed to be a reading item — that's what makes the queued query below a single term — so meta-issues about the tracker itself belong somewhere other than this repo.

Operations (gh CLI)

# Add item — no link
gh issue create --title "TITLE" --label "type:TYPE" --body "NAME"

# Add item — with a link (a literal newline inside the quotes)
gh issue create --title "TITLE" --label "type:TYPE" --body "NAME
URL"

# Find an item's number — an exact title match wins, else the top hit
N=$(gh issue list --search "TITLE in:title" --json number,title \
      --jq '(map(select(.title=="TITLE")) + .)[0].number')

# Start reading
gh issue edit $N --add-label reading

# Finish
gh issue close $N --reason completed

# Abandon
gh issue close $N --reason "not planned"

# Reread
gh issue reopen $N && gh issue edit $N --add-label reading

# Add a link to an item that already exists.
# --body replaces the whole body, so keep line 1 and append line 2.
gh issue edit $N --body "$(gh issue view $N --json body --jq '.body|split("\n")[0]')
URL"

# Comment (your commentary lives here, never in the body or a file)
gh issue comment $N --body "COMMENT"

# Timeline (the "when" — logged / started / closed)
gh api "/repos/brfid/readings/issues/$N/events" --jq '.[] | "\(.created_at)  \(.event)  \(.label.name // "")"'

The exact-match preference in the lookup matters: GitHub ranks title search by relevance, not by exactness, so a plain .[0] returns Grokking Continuous Delivery when you asked for Continuous Delivery. Add --state all when the item may already be closed — gh issue list is open-only by default.

Note that closing from the web UI can leave the reason unset; a closed issue with no reason has no determinable status. Close with the CLI, or set the reason afterwards.

Queries

# Status
gh issue list --label reading                              # in progress
gh issue list --search "is:open -label:reading"            # queued
gh issue list --search "is:closed reason:completed"        # finished
gh issue list --search 'is:closed reason:"not planned"'    # abandoned

# Type
gh issue list --label type:paper --state all

# Search
gh issue list --search "keyword in:title"
gh issue list --search "keyword in:body"          # author, or the link's domain
gh issue list --search "keyword in:comments"      # your commentary

# Every item as a record: number, title, author, link (null when there isn't one)
gh issue list --state all --json number,title,body \
  --jq '.[] | {n:.number, title, author:(.body|split("\n")[0]), url:(.body|split("\n")[1] // null)}'

About

What I'm reading. LLM-driven; uses Issues.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors