The Emacs secretary that helps you through all your inboxes and tasks.
el-secretario is available on melpa and is divided into different packages:
el-secretario: The main package
el-secretario-org: Org-mode integration
el-secretario-notmuch: Notmuch integration
el-secretario-mu4e: Mu4e integration
el-secretario-elfeed: Elfeed integration
el-secretario depends on a newer version of which-key than might be on elpa. See Bug: which-key: No bindings found in el-secretario-org-keymap
No calls to
require (explicit or with
use-package) is needed since all relevant functions are properly autoloaded.
If you are using doom:
Install manually from MELPA:
M-x package-install RET el-secretario RET
(use-package el-secretario :defer t)
I post announcements about breaking changes on https://lists.sr.ht/~zetagon/el-secretario-announce . If you don’t have an sr.ht account, see https://man.sr.ht/lists.sr.ht/#email-controls for how to subscribe using your email account.
There are at least two fundamental ways of reading email. The first, and the one I think is more common, is to open the inbox and choose an email to read from the list. Let’s call it the random access method. The other method is to open an email, preferably the oldest unread one, and when you are done open the next one by pressing a “next email” button. Let’s call it the linked list method.
This package was born from the realization that I like the linked list method, and that I would like to handle more things this way. So what el secretario does is that he turns different sources (e.g. org-mode todo items or RSS feeds) into linked list-style inboxes. And he doesn’t stop there, he can also link different lists together so that email, org-mode items and RSS feeds come under the same unified inbox. El secretario can already turn many different things into inboxes but he can also learn new things if you teach him.
This was all very abstract so let’s move on to a concrete example:
;; Create a function to start the review (defun el-secretario-daily-review () (interactive) (el-secretario-start-session (lambda () (list ;; First take care of email (el-secretario-notmuch-make-source "tag:lists/emacs-orgmode") ;; Then Take care of inbox (el-secretario-org-make-source nil ("~/org/Inbox.org")) ;; Go through TODOs (el-secretario-org-make-source '(todo "TODO") '("~/org/Todo.org"))))))
Above is a sample configuration, and here is a gif where its used in action.
el-secretario-daily-review will open up your oldest email. Pressing
n in the which-key prompt will take you to the next email sorted chronologically.
In this way your secretary will make sure you go through all your email without
having to worry about which or how many emails you will read.
Then when your secretary has gone through all your email for you, they will next
go through your todo inbox. Pressing
n will in a similar way take you to the
next item in your inbox. When you’ve refiled your tasks appropriately your
secretary will go through all your existing todos over in
Pressing a key that isn’t in the
which-key menu will disable the menu, which
essentially pauses the review. You can reactivate it with
A more complete configuration can be found at my configuration.
The fundamental building block. Items are the thing that you review from each source, for example an email or a todo.
A session consists of a list of sources. Each source consists of a list of items.
The example above has one notmuch source, and two org sources.
el-secretario comes with a set of modules that will help you with reviewing
various parts of your system.
In general functions and variables that are for users follow the format
el-secretario-MODULE-NAME and names for developers follow the format
el-secretario-MODULE--NAME (notice the two dashes).
A very simple module that goes through your email in chronological order. The
relevant function is just
el-secretario-notmuch-make-source, look at its
docstring for more info.
el-secretario-previous-item will go to the next
and previous thread respectively. If you want more granularity I recommend
el-secretario-notmuch-advance-and-archive instead of
el-secretario-next-item which will call
i.e. scroll through the thread until you are at the end, at which point it will
archive the thread and go to the next thread.
Very similar to the notmuch module. It goes through your mu4e email. The
relevant function is just
el-secretario-mu4e-make-source, look at its
docstring for more info.
Very similar to the notmuch module. It goes through your elfeed items in
chronological order, oldest first. The relevant function is just
el-secretario-elfeed-make-source, look at its docstring for more info.
A very simple module that goes through your todos. The relevant function is just
el-secretario-org-make-source, look at its docstring for more info.
El secretario can update tags of headings according to a state machine. The
first time you review an item one state transition is done. The state machine is
defined per source with the
TAG-TRANSITIONS argument to
el-secretario-org-make-source. It is a list of
(TAG . NEW-TAG) cons pairs.
Each reviewed heading that has the tag
TAG gets the tag
TAG removed and
NEW-TAG added. If
TAG is the empty string
NEW-TAG is always added.
(el-secretario-org-make-source '(todo) "~/org/Todo.org" :tag-transitions '(("a" . "b") ("b" . "c") ("" . "d") ("d" . ""))) * TODO Foo :a: * TODO Bar :b:
With the el-secretario source and org file above, one review will result in the org file below. All “a” tags have turned into “b” tags, and all “b” tags have turned into “c” tags. “d” is added to both.
* TODO Foo :b:d: * TODO Bar :c:d:
A second review will have converted all tags to “c”.
* TODO Foo :c: * TODO Bar :c:
This module has some convenience functions:
Property hooks are similar to normal hooks in that they allow the user to run
custom code at specific points in time. The difference is that property hooks
are defined by setting a property to a headline which means that they are local
to the headline. You can set a property hook by adding the corresponding
property with an unquoted lisp function as value. You can run your own property
hooks with the function
|EL-SECRETARIO-REVIEW-TASK-HOOK||When shown in a review in the org source|
This will call the function
review-item-fun when the Foo entry is shown in a review:
* Foo :PROPERTIES: :EL-SECRETARIO-REVIEW-TASK-HOOK: review-item-fun :END:
In order to run a function when a specific task is done, you can add the following to your config.
(add-hook 'org-after-todo-state-change-hook #'el-secretario-tasks--finish-task-hook) (defun my/el-secretario-run-finish-task-hook () (when (member org-state org-done-keywords) (el-secretario-org--run-property-hook (el-secretario-org--parse-headline) :EL-SECRETARIO-FINISH-TASK-HOOK)))
A spaced repetition module for tasks (and not memorization!). When you begin to
have lots of todos it becomes very tiring to review all of them all the time.
This module provides a way to defer todos into the future using a crude spaced
repetition algorithm (the length of the deferral is incremented by one day each
time). Note that it uses org-mode’s
SCHEDULE property so it will mess with
items you have scheduled.
Passing this function as a comparison function to
will ensure that you review your items sorted so that the earliest scheduled
items comes first. This can be useful to create a queue of tasks that are
roughly sorted by how relevant they are.
A simple module that goes through a list of files in order.
el-secretario-files-make-source is the entry point.
Visit all your downloaded files:
(el-secretario-start-session (el-secretario-files-make-source (directory-files "~/Downloads")))
An extremely simple source for when you want a function to be called
automatically during a specific time in the review. It calls the provided
function each time the source is activated and goes to the next source
el-secretario-next-item is called. To use it put
(el-secretario-function-source :func #'YOUR-FUNCTION) in your source list.
It’s easy to add your own keybindings! Use whatever keybinding mechanism you use to add keybindings the respective source’s keymap.
For example to bind
org-capture in the org keymap:
(define-key el-secretario-org-keymap "c" '("Capture" . org-capture))
If you want different keybindings for different instances of the same source type you can provide your own keymap. The example below has two different keymaps for the two sources.
(defvar my/el-secretario-org-map (make-sparse-keymap)) (define-key my/el-secretario-org-keymap "c" '("Capture with template a" . (lambda () (interactive) (org-capture nil "a")))) (defvar my/el-secretario-org-map-2 (make-sparse-keymap)) (define-key my/el-secretario-org-keymap "c" '("Capture with template b" . (lambda () (interactive) (org-capture nil "b")))) (defun el-secretario-review () (el-secretario-start-session (lambda () (list (el-secretario-org-make-source '(todo "TODO") '("~/org/Todo.org") :keymap my/el-secretario-org-map) (el-secretario-org-make-source '(todo "TODO") '("~/org/Inbox.org") :keymap my/el-secretario-org-map-2)))))
map! macro doesn’t play well with
will cause some descriptions for keybindings to be overwritten. Instead I
define-key as shown above.
A source is a eieio class that inherits from
el-secretario-source. It needs to
implement the following methods:
el-secretario-source-init can be implemented if your source needs
to do some setup only once (e.g. setup some state).
See the docstrings for respective method for what they are supposed to do.
Each source can fill the
keymap slot (as defined in
with a keymap. Otherwise the default keymap will be used.
el-secretario is mostly a glue-package and it couldn’t exist without all the
fantastic things it glues together! Huge thanks to the creators of:
There are three ways to contribute to this project:
Any feedback is very welcome! Documentation, usability, features etc.
el-secretario is designed to be extensible. Write your own sources and
contribute them, or improve the existing ones.
I have a ko-fi page if you want to throw money at me: https://ko-fi.com/zetagon
It would be very nice to have a mascot for the project, so I would be very happy if you would contribute with a nice drawing.