diff options
| author | Szymon Szukalski <szymon@szymonszukalski.com> | 2026-04-09 10:53:27 +1000 |
|---|---|---|
| committer | Szymon Szukalski <szymon@szymonszukalski.com> | 2026-04-09 10:53:27 +1000 |
| commit | bc75732b9d37b77945a977ee9f7892cf6efc79c3 (patch) | |
| tree | 4d9273ccc12c29eccc44fdc12372bea047414353 /README.md | |
| parent | 12a5b1464bb919ba23f2aa6c22d44de81e382151 (diff) | |
Refactor Emacs config into modules
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 408 |
1 files changed, 193 insertions, 215 deletions
@@ -1,243 +1,207 @@ # Emacs Configuration -This repository contains a literate Emacs configuration built around Org mode, Denote, a PARA-style note layout, and a small completion stack. The hand-edited configuration source is `config.org`; `init.el` and `early-init.el` are generated from it. +This repository contains a modular Emacs configuration built around Org mode, +Denote, a PARA-style note layout, a people CRM, and a small completion stack. +`init.el` is the hand-edited entry point, `early-init.el` handles true early +startup concerns, and the runtime implementation is hand-edited across the +domain modules under `lisp/`. ## System Model -This repository configures Emacs. It does not define, create, or validate the `~/org` note system. +This repository configures Emacs. It does not define, create, or validate the +`~/org` note system. -- `config.org` is the source of truth for Emacs configuration only. +- `init.el` is the source of truth for runtime composition and feature + selection. +- `early-init.el` is the source of truth for true early startup settings. +- `lisp/ss-*.el` is the source of truth for the runtime implementation by + domain. - `~/org` is external to this repository and must already exist. -- The configuration may open files in `~/org`, but it must not create directories, create files, or validate note structure. -- `~/org/journal.org` is the operational journal. It must already exist, and the configuration may open it but must not create or manage it. -- `~/org/moc.org` is a normal note. It must already exist, and the configuration may open it but must not create or manage it. -- `~/org/areas/people/people.org` is the people CRM file. It must already exist, and the configuration may open it but must not create or manage it. -- PARA is the organising model for durable notes. Folder placement carries meaning, and workflows must respect that placement. -- The system optimises for speed of capture and minimal friction. -- The system must prefer explicit rules over implicit behaviour and must avoid over-structuring, manual overhead, and inconsistency. - -## External Invariants - -These are external note-system invariants. The configuration assumes them, but it does not manage them. - -- `~/org` must exist. -- `~/org/journal.org` must exist. -- `~/org/moc.org` must exist. -- `~/org/areas/people/people.org` must exist. - -## Emacs Setup - -### Source of truth and generated files - -`config.org` is the only file intended for manual Emacs configuration edits. The generated startup files are: - -- `early-init.el` for settings that must exist before the first GUI frame. -- `init.el` for the main runtime configuration. - -Both generated files are tangled from `config.org` and should be treated as build artifacts. - -### Package bootstrap - -The config bootstraps packages with built-in `package.el` and uses `use-package` for declaration and load order. Package archives are configured with GNU, NonGNU ELPA, and MELPA, with GNU given highest priority. - -### Core packages and built-in modules - -The current setup uses these packages and built-in modules: - -- `org` and `org-capture` for agenda, journal capture, and the literate configuration itself. -- `denote` for durable notes, naming, keywords, and linking. -- `git-auto-commit-mode` for optional automatic commits inside `~/org` when enabled by directory-local settings. -- `vertico` for minibuffer completion UI. -- `orderless` for flexible completion matching. -- `marginalia` for minibuffer annotations. -- `gptel` with the GitHub Copilot backend, currently being trialled as an experimental tool rather than a defined workflow. -- `dired` with a macOS-safe `ls` configuration. -- `time` for the modeline clock. -- `modus-themes`, using `modus-vivendi` in the current config. +- The configuration may open files in `~/org`, but it must not create + directories, create files, or validate note structure. +- `~/org/journal.org` is the operational journal. It must already exist, and + the configuration may open it but must not create or manage it. +- `~/org/moc.org` is a normal note. It must already exist, and the + configuration may open it but must not create or manage it. +- `~/org/areas/people/people.org` is the people CRM file. It must already + exist, and the configuration may open it but must not create or manage it. +- PARA is the organising model for durable notes. Folder placement carries + meaning, and workflows must respect that placement. + +## Repository Layout + +The runtime architecture is: + +```text +early-init.el +init.el +lisp/ + ss-core.el + ss-ui.el + ss-org.el + ss-agenda.el + ss-capture.el + ss-denote.el + ss-crm.el + ss-gptel.el + ss-keys.el +``` -### Org mode and note layout +The module responsibilities are: + +- `ss-core.el` bootstraps packages, defines shared paths and helpers, and + applies shared editor defaults. +- `ss-ui.el` owns theme, fonts, frame behavior, modeline, and completion UI. +- `ss-org.el` owns base Org setup, startup MOC behavior, and shared note + helpers. +- `ss-agenda.el` owns agenda discovery and agenda commands. +- `ss-capture.el` owns journal capture structure and capture templates. +- `ss-denote.el` owns Denote setup and durable-note capture helpers. +- `ss-crm.el` owns all people CRM behavior. +- `ss-gptel.el` owns the experimental GitHub Copilot-backed `gptel` setup. +- `ss-keys.el` owns global keybindings only. + +`init.el` enables high-level features centrally through `ss-enabled-features`. +Feature toggling works by including or excluding a module there. + +## Package Model + +The config bootstraps packages with built-in `package.el` and uses +`use-package` for declaration and load order. Package archives are configured +with GNU, NonGNU ELPA, and MELPA, with GNU given highest priority. + +The current setup uses: + +- `org` and `org-capture` for agenda and journal capture +- `denote` for durable notes, naming, keywords, and linking +- `git-auto-commit-mode` for optional automatic commits inside `~/org` when + enabled by directory-local settings +- `vertico` for minibuffer completion UI +- `orderless` for flexible completion matching +- `marginalia` for minibuffer annotations +- `corfu` for in-buffer completion popups in text and Org buffers +- `gptel` with the GitHub Copilot backend as an experimental tool +- `dired` with a macOS-safe `ls` configuration +- `time` for the modeline clock +- `modus-themes`, using `modus-vivendi` + +## Org Layout The note system lives under `~/org/` and is organised like this: -- `journal.org` for the operational journal. -- `daily/` for older plain daily Org files that may still exist outside the active capture workflow. -- `projects/` for project notes. -- `areas/` for area notes. -- `areas/people/people.org` for the people CRM. -- `resources/` for reference material. -- `archives/` for archived notes. - -This is a PARA-style layout. Folder placement carries meaning. Denote keywords are used sparingly, with `project` kept as the only built-in structural keyword because project titles are often ambiguous outside their folder. - -### Agenda Rules +- `journal.org` for the operational journal +- `daily/` for older daily Org files that may still exist +- `projects/` for project notes +- `areas/` for area notes +- `areas/people/people.org` for the people CRM +- `resources/` for reference material +- `archives/` for archived notes -The agenda is rule-based. +Agenda discovery is rule-based: -- The agenda must include `~/org/journal.org`. -- The agenda must include recursive scans of `.org` files under `~/org/projects/`, `~/org/areas/`, and `~/org/resources/`. -- The agenda must exclude `~/org/archives/`. -- The agenda must use recursive discovery and explicit include and exclude rules rather than heuristic selection. - -### Completion setup - -The minibuffer stack is intentionally small: - -- `vertico` provides the completion UI. -- `orderless` handles matching. -- `marginalia` adds annotations. -- `corfu` handles in-buffer completion popups for text and Org buffers. +- include `~/org/journal.org` +- recursively scan `.org` files under `~/org/projects/`, `~/org/areas/`, and + `~/org/resources/` +- exclude `~/org/archives/` ## People CRM The people workflow is a CRM rooted at `~/org/areas/people/people.org`. -- Each top-level heading represents one person. -- Entries are strictly structured around heading text and flat properties. -- The system generates abbrevs from the people file. -- The system provides canonical-name completion through a CAPF. -- The system provides reporting views grouped by person properties. -- The system rebuilds on any people-file change, whether the change comes from manual editing or from a command. -- The system must remain up to date after changes. -- Failures must surface visibly. The system must not silently fall back to stale state. -- The CRM is individual-focused: `TEAM` captures the current working team and `MANAGER` captures the formal organisational manager. -- Team structure, org charts, and matrix relationships belong in separate notes, not in the CRM. -- Person cards use `ROLE`, `TEAM`, `MANAGER`, `ENGAGEMENT`, `SUPPLIER`, `LOCATION`, and `CURRENT_FOCUS` in that order. -- `ENGAGEMENT` and `SUPPLIER` both use fixed lookup lists in `ss/crm-add` so reports stay consistent. -- `CURRENT_FOCUS` must stay short and phrase-like so summaries and completion annotations remain readable. - -Name entry uses fixed abbrevs plus the people CRM: - -- `abbrev` provides deterministic one-shot shortcuts for fixed name expansions. -- people-specific abbrevs are generated from top-level cards in `~/org/areas/people/people.org`. -- a CAPF feeds Corfu canonical names from `~/org/areas/people/people.org`, while alias matching stays available for lookup and completion. -- Marginalia annotates person candidates with `role | team | engagement | current focus`. -- `M-x ss/crm-open` opens `~/org/areas/people/people.org` in overview mode through `ss/crm-overview`. -- `M-x ss/crm-find` opens a person card narrowed to that subtree. -- `M-x ss/crm-overview` opens the people file in overview mode, resetting the file by widening and restoring the overview when leaving card view. -- `M-x ss/crm-insert-name` inserts the canonical name at point. -- `M-x ss/crm-insert-summary` inserts a compact single-line summary at point. -- `M-x ss/crm-add` adds a new person card directly to `~/org/areas/people/people.org`. -- `M-x ss/crm-report-by-role`, `M-x ss/crm-report-by-team`, `M-x ss/crm-report-by-manager`, `M-x ss/crm-report-by-engagement`, `M-x ss/crm-report-by-supplier`, and `M-x ss/crm-report-by-location` render grouped Org reports in a read-only buffer. - -### Persistent abbrevs - -Persistent abbrevs live in `abbrev_defs` at the repository root. The config loads that file on startup, enables abbrev mode only in text-like buffers, and saves learned abbrevs back to the same file silently when buffers are saved. -People-specific abbrevs are not stored in a separate file. They are rebuilt from `~/org/areas/people/people.org` whenever the people file changes, and missing `ABBREV` properties fall back to a generated default trigger. - -### Babel tangle process - -The literate config uses Org Babel to generate the runtime files. Most Emacs Lisp blocks inherit `:tangle init.el` from the file header, while early-startup blocks explicitly tangle to `early-init.el`. - -To regenerate the generated files from the repo root: - -```sh -emacs --batch -Q --eval '(progn (require (quote ob-tangle)) (org-babel-tangle-file "config.org"))' -``` - -Or use the helper script: - -```sh -./build -``` - -To verify that the generated main config still loads: - -```sh -emacs --batch -Q --load ./init.el -``` - -To remove generated startup files, `custom.el`, and common working directories -from the repo root: - -```sh -./reset -``` +- each top-level heading represents one person +- entries are structured around heading text and flat properties +- the system rebuilds people abbrevs from the CRM file +- a CAPF provides canonical-name completion while alias matching remains + available for lookup +- Marginalia annotates people with `role | team | engagement | current focus` +- reports are available by role, team, manager, engagement, supplier, and + location +- `TEAM` captures the current working team and `MANAGER` captures the formal + organisational manager +- person cards use `ROLE`, `TEAM`, `MANAGER`, `ENGAGEMENT`, `SUPPLIER`, + `LOCATION`, and `CURRENT_FOCUS` in that order + +The CRM commands are: + +- `M-x ss-crm-open` +- `M-x ss-crm-overview` +- `M-x ss-crm-find` +- `M-x ss-crm-insert-name` +- `M-x ss-crm-insert-summary` +- `M-x ss-crm-add` +- `M-x ss-crm-report-by-role` +- `M-x ss-crm-report-by-team` +- `M-x ss-crm-report-by-manager` +- `M-x ss-crm-report-by-engagement` +- `M-x ss-crm-report-by-supplier` +- `M-x ss-crm-report-by-location` + +Persistent abbrevs live in `abbrev_defs` at the repository root. The config +loads that file on startup, enables abbrev mode only in text-like buffers, and +saves learned abbrevs back to the same file silently when buffers are saved. +People-specific abbrevs are rebuilt from `~/org/areas/people/people.org` +whenever that file changes. ## Workflow -### MOC - -`~/org/moc.org` is a normal note. It serves as a small curated navigation surface rather than an exhaustive index or system of record, and the configuration assumes the file already exists. - -The configuration may open the MOC automatically on startup, and `C-c n M` opens it manually. The configuration must not create or manage the file. - -Its Quick Access section provides actionable links for opening the agenda, the journal, capture, and a new note, while the rest of the file stays lightweight and curated around active projects, areas, and a few high-leverage resources. - -### Capture Model - -The capture model has two distinct modes. +`~/org/moc.org` is a normal note. It is treated as a curated navigation note, +not a generated system file. The config may open it on startup, and `C-c n M` +opens it manually. -### Journal +The capture model has two distinct paths: -The operational journal lives in `~/org/journal.org`. +- fast operational capture goes to `~/org/journal.org` +- durable notes use Denote in the PARA directories under `~/org/` -- Journal capture uses a Year -> Day outline inside `journal.org`. -- Each day keeps explicit `Tasks`, `Notes`, and `Meetings` headings beneath the day entry. -- Journal capture is the fast path for operational work. -- The configuration assumes `~/org/journal.org` already exists and does not create it. +Journal capture uses a Year -> Day outline in `journal.org` with explicit +`Tasks`, `Notes`, and `Meetings` headings beneath each day entry. -### Durable notes - -Durable notes use Denote and live in the PARA directories under `~/org/`. - -- Durable notes are created through capture plus Denote. -- Durable notes are structured at creation time. -- Folder placement carries meaning. -- The workflow must respect PARA placement rather than relying on later manual cleanup. - -### Agenda usage - -The agenda is opened through `ss/open-agenda`, bound to `C-c a`. That command explicitly loads `org-agenda`, and the config refreshes `org-agenda-files` immediately before each `org-agenda` invocation. -Agenda file selection follows the canonical rules in the `Agenda Rules` section above. - -### Capture flow - -`C-c c` opens capture. The configured templates cover: +The configured capture templates cover: - journal tasks - journal notes - journal meetings - Denote-backed captures for generic notes, projects, areas, and resources -Journal task capture writes under the current day's `Tasks` heading. Journal note capture writes under `Notes`. Journal meeting capture writes under `Meetings`, and the meeting template prefixes the heading with the capture time. - -Denote captures still prompt for title, keywords, and subdirectory placement where appropriate, but folder placement does most of the classification work. The project capture template prepopulates the `project` keyword. Area and resource captures do not inject structural keywords automatically, and there is no Denote-backed meeting capture template. - -The people CRM is intentionally outside `org-capture`: `M-x ss/crm-add` writes a compact card directly into `~/org/areas/people/people.org`, keeping the people file as a fast reference file instead of another capture sink. - -### Note creation and linking - -Denote handles long-lived notes. The main bindings are: - -- `C-c n n` to open or create a Denote note. -- `C-c n l` to insert a Denote link. -- `C-c n M` to open the central MOC note. -- `C-c n E` to show people grouped by engagement. -- `C-c n f` to find a person card. -- `C-c n i` to insert a canonical person name. -- `C-c n I` to insert a compact person summary. -- `C-c n L` to show people grouped by location. -- `C-c n d` to open `~/org/journal.org`. -- `C-c n o` to restore the people overview. -- `C-c n O` to show people grouped by role. -- `C-c n p` to open `~/org/areas/people/people.org`. -- `C-c n P` to add a new person card. -- `C-c n R` to show people grouped by manager. -- `C-c n S` to show people grouped by supplier. -- `C-c n T` to show people grouped by team. - -Keyword prompts and directory placement are part of the workflow, not an afterthought. The config is set up so structure is created first, then capture writes into it, with folder placement carrying most of the durable type information. - -The intended split is explicit: - -- `~/org/journal.org` is for fast operational capture. -- Denote notes in the PARA directories are for durable content. - -### Automatic note commits - -The configuration provides `git-auto-commit-mode` capability. Behaviour is defined in `~/org/.dir-locals.el`. - -When enabled in `~/org/.dir-locals.el`, saving a file in `~/org/` makes Emacs try to commit that change. The configuration must not enforce policy for add, push, debounce, or commit-message behaviour. +The people CRM remains outside `org-capture`: `M-x ss-crm-add` writes directly +to `~/org/areas/people/people.org`. + +## Keybindings + +The main bindings are: + +- `C-c a` for the agenda +- `C-c c` for capture +- `C-c n n` to open or create a Denote note +- `C-c n l` to insert a Denote link +- `C-c n M` to open the MOC +- `C-c n d` to open `~/org/journal.org` +- `C-c n p` to open the people CRM +- `C-c n P` to add a new person card +- `C-c n f` to find a person card +- `C-c n i` to insert a canonical person name +- `C-c n I` to insert a compact person summary +- `C-c n o` to restore the people overview +- `C-c n O` to show people grouped by role +- `C-c n T` to show people grouped by team +- `C-c n R` to show people grouped by manager +- `C-c n E` to show people grouped by engagement +- `C-c n S` to show people grouped by supplier +- `C-c n L` to show people grouped by location +- `C-c n g` to start `gptel` +- `C-c n s` to send in `gptel` +- `C-c n r` to rewrite with `gptel` +- `C-c n a` to add context in `gptel` + +## Automatic Note Commits + +The configuration provides `git-auto-commit-mode` capability. Behaviour is +defined in `~/org/.dir-locals.el`. + +When enabled in `~/org/.dir-locals.el`, saving a file in `~/org/` makes Emacs +try to commit that change. The Emacs config supplies the package and selects +the shell command separator based on the active shell, while the note tree +defines add, push, debounce, and commit-message behavior. Place this file at `~/org/.dir-locals.el`: @@ -254,19 +218,33 @@ Place this file at `~/org/.dir-locals.el`: (git-auto-commit-mode 1)))))) ``` -That applies to buffers visiting files under `~/org/` and its subdirectories. The repository-local settings control whether new files are added, whether pushes occur, how long commits are debounced, and what commit message is used, while the Emacs config supplies the package itself and picks the correct shell command separator for the active shell. +## Validation + +The primary verification command for the runtime path is: + +```sh +emacs --batch -Q --load ./init.el +``` + +`early-init.el` and `init.el` are hand-edited source files, not generated +artifacts. -### Terminal and GUI behavior +## Terminal and GUI Behavior GUI Emacs and terminal Emacs are handled slightly differently. - GUI frames get the preferred frame size, font setup, and UI trimming. -- In `emacs -nw`, the menu bar is disabled on `emacs-startup-hook` rather than earlier in startup, because changing that timing too early caused interactive terminal regressions in kitty. +- In `emacs -nw`, the menu bar is disabled on `emacs-startup-hook` rather than + earlier in startup, because changing that timing too early caused + interactive terminal regressions in kitty. -If you change terminal behavior, test it in a real `emacs -nw` session. Batch load checks are necessary, but they are not enough for tty input and UI behavior. +If you change terminal behavior, test it in a real `emacs -nw` session. Batch +load checks are necessary, but they are not enough for tty input and UI +behavior. ## Maintenance Rules -- Update `config.org` first, then regenerate `init.el` and `early-init.el`. -- Keep this README aligned with the current configuration. If package usage, startup behavior, keybindings, or workflow changes, update this file in the same change. +- Edit `init.el`, `early-init.el`, and `lisp/ss-*.el` directly. +- Keep this README aligned with the current configuration. +- Keep `README.md` and `AGENTS.md` in sync. - Do not document planned behavior as if it already exists. |
