Skip to content

Repository files navigation

org-glance

A personal knowledge graph for org-mode. Headlines become nodes in a durable plain-text store; typed references between them become edges; fast and flexible views show it all without opening a single file.

How it works

If you know org-mode, you know almost everything already:

  1. Headlines are the unit. Every captured headline lives as its own small org file in a hidden store (.org-glance/ under org-glance-directory), keyed by an :ORG_GLANCE_ID: property org-glance adds for you.
  2. Tags are collections. An ordinary org tag (:book:, :article:) groups headlines; a tag exists the moment you capture something with it. Optionally give a tag its own config: todo cycle and capture template (C in the transient — or C in that tag’s overview / table to edit it directly).
  3. Views are projections. The table and the overview are generated, read-only summaries built from an index — fast, and always rebuildable. You never edit a view: you open the headline behind it, edit ordinary org, save; views refresh themselves.
  4. Links are edges. A link from one headline to another is indexed as a typed relation, so the graph can answer “what references this?”.

Usage

Entrypoint is org-glance-transient (C-x j).

1. Capture a headline

C-x j + → pick (or type) a tag → an org-capture-style buffer opens → write an ordinary headline, C-c C-c:

* TODO [#A] Kebena Decaf :coffee:
DEADLINE: <2026-08-10 Mon> SCHEDULED: <2026-08-01 Sat>
Light roast, very floral.  [[https://example.com][Homepage]]

2. Browse a tag

C-x j o → pick a tag → its table: one row per headline, colour-coded todo states:

State  Title         Scheduled         Deadline          Pri  Tags
TODO   Kebena Decaf  <2026-08-01 Sat>  <2026-08-10 Mon>  A    coffee

O toggles to the org-text overview — a plain org buffer, so folding, C-x j a (agenda) and link clicking all behave exactly as in org. Each overview heading is self-sufficient:

* TODO [#A] Kebena Decaf :coffee:
DEADLINE: <2026-08-10 Mon> SCHEDULED: <2026-08-01 Sat>
- [[https://example.com][Homepage]]

Act on the row/heading at point without opening anything:

KeyOn the single row at pointWith C-uWith marked rows (m)Pressed again (toggle)
RETmaterialize (open for editing, step 3)
mmark (u unmarks)unmark
M / Umark all visible / unmark all
C-c C-tchange todo stateone prompt sets all
C-c C-s / C-c C-dschedule / deadlineclear it
+ / -add a headline to the view’s tag (capture) / remove one from it
C-c +add a column (drawer property or relation kind)
C-c -remove the column at point (prompts when point is off a column)always prompt for the column
:add another tag to the headlineremove one
j (or !)open a link inside the headline
@table of everything the headline relates to (both directions)
iedit the cell at point (state: todo prompt; dates: calendar; text: minibuffer)
C-c pcopy the headline (a fresh id, content verbatim)
ecopy a KEY: value from its body
#encryptchange passworddecrypt
llog occurrences of a repeating headline (step 6)
Ddelete
Oswitch to the org overviewback to the table
/filter by substringclear the filternarrow to the marked rowswiden the narrowed view
^sort by the column at pointflip asc ⇄ desc; off a column: cycle columns
grefresh (table); resets the / filter / narrow to the view’s own filter
?show the key legend (table)hide it
C-c C-csave the layout (reference tables: prompt for a scope)

Column layout, sort and added columns persist per tag.

3. Edit a headline

RET on a row/heading (or C-x j m anywhere) materializes it: its file opens in a normal org buffer. Everything org can do works here — this IS org-mode. C-x C-s saves and re-indexes; open views refresh on their next display. (The :ORG_GLANCE_ID: drawer is concealed and protected: hand edits to it revert on save with a warning.)

4. Connect headlines

In a materialized buffer — or while capturing (step 1) — @ at the start of a word prompts for another headline and inserts a link to it; C-u @ also asks a reference kind:

roasted by [[org-glance-material:0e904834?kind=roasted-by][Manhattan Coffee Roasters]]

Clicking the link opens the roaster; the graph indexes it as a typed edge, and the views pick it up. The overview heading gains a relation line:

- roasted by [[org-glance-material:0e904834][Manhattan Coffee Roasters]]

and C-c + in the table now offers a Roasted by column with the target titles:

State  Title         ...  Roasted by
TODO   Kebena Decaf  ...  Manhattan Coffee Roasters

In a materialized buffer:

KeyAction
@insert a reference (completion, at word start)
C-u @same, plus a reference kind (roasted by, …)
C-c @table of everything this headline relates to

A relation table lists both directions in one view — the headlines this one refers to AND the ones referring to it — and its Relation column names each row’s direction and kind (> roasted by, <). @ on a row in the table or the overview opens the same view for that headline.

Relation tables open with the default layout; edits to it are not saved automatically (an echo-area nudge reminds you) — C-c C-c applies the layout (columns, order, sort) to a scope of your choice: this headline, or a tag pair (coffee → roaster, the anchor’s tag first). Every later relation table matching the scope restores it.

A link can also address a whole view: [[org-glance-overview:coffee?done=nil][Active coffees]].

5. Keep secrets

Wrap a region in a crypt block with C-c # (no region: the whole body):

#+begin_crypt
bank: 1234-5678
#+end_crypt

On disk it is ciphertext, and a materialized headline opens THAT WAY – no password prompt until you ask for one. Unseal with C-c # in the buffer, or materialize with the transient’s -d switch to open decrypted; then it re-seals on every save (auto-save and backups disabled). Text OUTSIDE the blocks stays public and searchable.

C-c # is the only crypt key; what it does depends on where point is:

Point / buffer stateC-c #
active regionwrap it in a crypt block
sealed buffer (ciphertext)unseal it (prompts, hardens the buffer)
inside a plaintext blockunwrap that block – public on save
decrypted, outside any blockseal it back, forgetting the password
plaintext headlineencrypt the whole body as one block

6. Repeating headlines keep their history

A headline with a repeater (SCHEDULED: <… +1d>) stays ONE headline. With org-glance-repeat-history-depth set (N newest, t = all, per-headline :ORG_GLANCE_REPEAT_HISTORY_DEPTH: wins), each completion snapshots the done state before org resets it; the table’s Rep column marks repeaters, and l (C-c h in the buffer) opens any past occurrence read-only.

Customization

VariableDefault
org-glance-directoryorg-directoryroot of the managed content (store lives in .org-glance/)
org-glance-overview-default-vieworg-glance-tablewhat o opens; org-glance-overview for org-text
org-glance-filter-specactive, -archived, -commentedambient filter every view starts from (nil = everything)
org-glance-view-fill-frametviews take the whole frame; nil keeps your windows
org-glance-repeat-history-depth0occurrence snapshots kept: N / t = all / 0 = off
org-glance-material-password-ttl300seconds a crypt password stays cached; 0 = forever
org-glance-material-hidden-propertiesid + hashdrawer keys concealed AND edit-protected in material buffers
org-glance-table-state-colorstokyo-night-ishper-state badge colours (falls back to org-todo-keyword-faces, then …-default-state-color)
org-glance-pluginsnilplugins to load at init (e.g. (llm)); I enables one, U disables
org-glance-conflict-resolutionaskgit-conflict healing: ask / union / nil (error)
org-glance-tag-config-dirniloverride dir of per-tag config files (config/tags/)
org-glance-graph-segment-max-bytes256kWAL segment size before sealing
org-glance-graph-compact-segment-count4sealed segments that trigger auto-compaction

Example — org-text views, everything visible, daily history for a year:

(setq org-glance-overview-default-view 'org-glance-overview
      org-glance-filter-spec nil               ; show DONE too
      org-glance-repeat-history-depth 365)

The store, briefly

A segmented, append-only JSONL log with atomic commits, compaction, crash recovery and git-sync conflict healing. The per-headline org files are canonical; every index is derived and rebuildable (M-x org-glance-reindex). Deletion (D, M-x org-glance-delete) writes a tombstone after a confirmation that names any headlines whose links would dangle.

Install

The published package archive is the simplest route. Add the archive, then let use-package install and set it up:

(add-to-list 'package-archives
             '("rails-to-cosmos" . "https://rails-to-cosmos.github.io/elpa/"))

(use-package org-glance
  :bind (("C-x j" . org-glance-transient))
  :config (org-glance-init)
  :ensure t)

C-x j opens org-glance-transient, the transient dispatcher (see Highlights); org-glance-init bootstraps the store.

The rails-to-cosmos archive provides org-glance and its table-view dependency; the remaining deps (dash, f, s, aes, transient) come from MELPA, so keep a MELPA entry in package-archives too. Everything else (org and its submodules, cl-lib, thunk, subr-x) ships with Emacs 29.1+.

Plugins

Optional features ship as SEPARATE packages, so a plain install stays minimal. Two steps, and they belong to different tools: your package manager installs the package, org-glance enables it. Install it, then list it in org-glance-plugins — or press I in the transient (U disables). The transient’s System heading lists what is enabled and marks any plugin whose library failed to load:

(use-package org-glance-llm
  :after org-glance
  :ensure t
  :init (setq org-glance-plugins '(llm)))
  • *org-glance-llm* — LLM sessions per headline. l opens an agnostic-llm session in the headline’s data directory (or its :ORG_GLANCE_PROJECT_DIR:, set with C-c d in a materialized buffer); L lists every session (running / exited / stopped) from a persisted cache; C-c l opens the session for the headline at hand. It depends on agnostic-llm (also on the rails-to-cosmos archive), which pulls in vterm — loaded lazily on first use. Core org-glance does NOT depend on it.

Development

For hacking on org-glance, clone the repo and load it directly. It is a multi-directory package: org-glance.el is at the root, modules under src/data and src/view; it does NOT add those to load-path itself, so all three must be on it. table-view and agnostic-llm are external dependencies from the rails-to-cosmos ELPA archive (not on MELPA); the MELPA deps above are still needed. (Eask is the dev/test harness, not required to use the package.)

(dolist (dir '("" "src/data" "src/view"))
  (add-to-list 'load-path (expand-file-name dir "~/path/to/org-glance")))
(require 'org-glance)

With straight.el (also needs recipes for table-view and agnostic-llm):

(use-package org-glance
  :straight (org-glance :type git :host github :repo "rails-to-cosmos/org-glance"
                        :files ("*.el" "src/data/*.el" "src/view/*.el")))
(use-package table-view
  :straight (table-view :type git :host github :repo "rails-to-cosmos/table-view"))

(use-package agnostic-llm
  :straight (agnostic-llm :type git :host github :repo "rails-to-cosmos/agnostic-llm"))

About

Manage your org-mode entries as materialized views

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages