The Emacs Secretary

eee4a64 Update names

a month ago

1ff61e2 fixup! Add simple org-roam source

a month ago

The Emacs secretary that helps you through all your inboxes and tasks.

#Requirements and Installation

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

Note: 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:

(package! el-secretario)

Install manually from MELPA: M-x package-install RET el-secretario RET

Or via use-package:

(use-package el-secretario
  :defer t)

#Breaking Changes

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.

#Introducing my secretary

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:

#Simple Configuration

;; Create a function to start the review
(defun el-secretario-daily-review ()
   (lambda ()

      ;; 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.

Calling 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 Todo.org

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 el-secretario-activate-keymap.

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.

#The modules

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.

#Note on commands

el-secretario-next-item and el-secretario-previous-item will go to the next and previous thread respectively. If you want more granularity I recommend using el-secretario-notmuch-advance-and-archive instead of el-secretario-next-item which will call notmuch-show-advance-and-archive, 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.

#Tag state machine

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.

  1. Example

    (el-secretario-org-make-source '(todo)
                                   '(("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:

#Convenience functions

This module has some convenience functions:

  • el-secretario-org-remove-tag
  • el-secretario-org-up-heading

#Property hooks

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-org--run-property-hook.

Property Run condition
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

#Run property hook when marking a task as finished

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)


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.

Currently this module doesn’t stand on it’s own and serves more as a library that augments the org module. See my config for an example of how to use it.

#Relevant variables

  • el-secretario-org-space-increment-percentage

#Relevant functions

  • el-secretario-org-space-reschedule

  • el-secretario-org-space-schedule-and-reset

  • el-secretario-org-space-compare-le

    Passing this function as a comparison function to make-el-secretario-source 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-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 immediately when 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 ()
   (lambda ()
      (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)))))

#Note for Doom Emacs users

Doom’s map! macro doesn’t play well with el-secretario and which-key. It will cause some descriptions for keybindings to be overwritten. Instead I recommend using define-key as shown above.

#Creating a new source

A source is a eieio class that inherits from el-secretario-source. It needs to implement the following methods:

  • el-secretario-source-next-item
  • el-secretario-source-previous-item
  • el-secretario-source-activate

Optionally 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 el-secretario-source) with a keymap. Otherwise the default keymap will be used.

See the example source and its unit tests for an easy to read example.


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:

  • Feedback

    Any feedback is very welcome! Documentation, usability, features etc.

  • Patches

    el-secretario is designed to be extensible. Write your own sources and contribute them, or improve the existing ones.

  • Money

    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.