Skip to main content

Reminders Tickler / CLI and Textual UI

Project description

tklr

The term tickler file originally referred to a file system for reminders which used 12 monthly files and 31 daily files. Tklr, pronounced "tickler", is a digital version that ranks tasks by urgency, goals by priority, and generally facilitates the same purpose - managing what you need to know quickly and easily. It supports a wide variety of reminder types, a simple, text-based entry format with timely, automatic assistance, the datetime parsing and recurrence features of dateutil and provides both command line (Click) and graphical (Textual) user interfaces.

Make the most of your time!

tklr logo

This introduction to tklr is best viewed at GitHub.io - tklr itself is available from PyPi and GitHub and further information at Tklr Discussions.

This README is a work-in-progress. This notice will be removed when all the major sections have been completed.

The ↩︎ links at the end of major sections lead back to the following Table of Contents:

Table of Contents

1. What makes tklr different

1.1. Form-Free entry

Description

Rather than filling out fields in a form to create or edit reminders, a simple entry field is provided for text input together with a prompt area which provides instantaneous feedback.

Here a new reminder is being created. Below the entry area, the prompt indicates that the first step is to enter the type character for the reminder.

Description

After the type character is entered, the prompt changes to indicate that the next step is to enter the subject of the reminder.

The prompt is updated as the entry changes to assist with the editing of the reminder. This does not interfere with the entry process but, like the speedometer in a car, ensures that the relevant information is there if wanted.

Description

As the subject is entered, the prompt changes to reflect the current value of the entry.

Description

After the subject is entered, adding an @ character changes the prompt to a list of the required and optional attributes which can still be entered.

Description

Entering an available key changes the prompt to a description of the attribute.

Here @s, has been selected and the prompt changes to show that this attibute, which is required for an event, specifies the scheduled datetime at which the event begins.

Description

Fuzzy parsing is supported for entering dates or datetimes in tklr. Since it was January 5, 2026 when this entry was made, the interpretation is that 12p means 12:00pm on Jan 5.

Description

Adding fri changes the interpretation to Friday of the current week.

Description

Adding @ again shows the current list of required and optional attributes, but this time with @s removed since it has already been entered.

Description

Adding e changes the prompt to indicate that this attribute is used to specify the extent of the event, i.e., how long the event lasts.

Description

Adding 1h specifies an extent of one hour. With this setting, the event would last from 12pm until 1pm.

In addition to h for hours, other options include m for minutes, d for days and w for weeks. These can be combinded so that, e.g., 2h30m would represent two hours and thirty minutes.

↩︎

1.2. Support for wide variety of reminder types and attributes

tklr has six item types, each with a corresponding type character:

item type character
event *
task ~
project ^
goal !
note %
draft ?

Here are some illustrations of how the various types and attributes can be put to use.

1.2.1. An event: lunch with Ed (extended)

* lunch with Ed
  @s 12p fri @e 1h
  @a 30m: n

  

The * makes this reminder an event with whatever follows until the next @ character as the subject. The @s attribute sets the scheduled or starting time for 12pm on the first Friday after today and the @e 1h attribute sets the extent for one hour. This event will thus be displayed as occupying the period 12-1pm on that Friday. The distinguishing feature of an event is that it occurs at a particular time and the @s attribute is therefore required.

Provided that tklr ui is running, @a 30m: n will trigger a built-in notify alert thirty minutes before the start of the event which sounds a bell and posts a message on the tklr display showing the subject and time of the event.

↩︎

1.2.2. A task: pick up milk

~ pick up milk

  

The beginning ~ type character makes this reminder a task with the following pick up milk as the subject.

Using an @s attribute is optional and, when specified, it sets the time at which the task should be completed, not begun. The @e attribute is also optional and, when given, is intepreted as the estimated time period required for completion.

↩︎

1.2.3. A repeating event: trash pickup

* trash pickup @s 8a mon @n 1d @r w &w MO

  

This event repeats because of the @r w &w MO each week on Mondays. Because of the @n 1d a notice will be posted in Agenda View when the current date is within one day of the scheduled datetime or, in this case, on Sundays. This serves as a reminder to put the trash at the curb before 8am Mondays. Why not use a task for this? A task would require being marked finished each week to avoid accumulating past due instances - even when out of town with neither trash nor opportunity for placement at the curb.

↩︎

1.2.4. An event that repeats irregularly: dental appointment

* dental exam and cleaning
  @s 2p feb 5
  @e 45m
  @+ 9am Sep 3

  

This event specifies an appointment for a 45 minute dental exam and cleaning starting at 2pm on February 5 and then again, because of the @+ attribute, at 9am on September 3.

Need to add another datetime to an existing reminder? Just add an @+ attribute with a comma separated list of as many additional dates or datetimes as needed.

↩︎

1.2.5. A complicated but regularly repeating task: vote for president

~ vote for president
  @s nov 1 2020
  @r y &i 4 &w TU &d 2, 3, 4, 5, 6, 7, 8 &m 11

  

Here is another, more complicated, but still regularly repeating reminder. Beginning with November, 2020, this task repeats every 4 years on the first Tuesday after a Monday in November (a Tuesday whose month day falls between 2 and 8 in the 11th month).

This is a good illustration of the power of the dateutil library. Note that the only role of @s nov 1 2020 is to limit the repetitions generated by @r to those falling on or after November 1, 2020 and occur on that year or a multiple of 4 years after that year.

↩︎

1.2.6. An offset task: fill bird feeders

~ fill birdfeeders @s 3p sat @n 1d @o 12d

  

Because of the @o 12d offset attribute, when this task is completed the @s scheduled datetime will automatically reset to the datetime that falls precisely 12 days after the completion datetime. Whether they are filled early or late, they will still need to be refilled 12 days after they were last filled. Because of the @n 1d notice attribute, this task will not appear in the Agenda View task list until the the current datetime is within one day of the scheduled datetime.

Since the @o attribute involves resetting attibutes in a way that effectively repeats the task:

  1. @o can only be used with tasks
  2. Using @o precludes the use of @r

It is worth noting the different roles of two attributes in events and tasks.

  1. The scheduled datetime attribute describes when an event begins but when a task should be completed.
  2. The notice attribute provides an early warning for an event but postpones the disclosure of a task.

↩︎

1.2.7. A note: a favorite Churchill quotation

% Give me a pig - #Churchill
  @d Dogs look up at you.
  Cats look down at you.
  Give me a pig - they look you in the eye
    and treat you as an equal.
  @b quotations

  

The beginning % makes this reminder a note with the subject, Give me a pig - #Churchill. The optional details attribute follows the @d and is meant to be more expansive - analogous to the body of an email. The hash character that precedes 'Churchill' in the subject makes that word a hash tag for listing in Tags View. The @b entry adds this reminder to the 'quotations' bin for listing in Bins View.

↩︎

1.2.8. A project: build a dog house with component tasks

^ Build dog house
  @~ pick up materials &r 1 &e 4h
  @~ cut pieces &r 2: 1 &e 3h
  @~ assemble &r 3: 2 &e 2h
  @~ sand &r 4: 3 &e 1h
  @~ paint &r 5: 4 &e 4h

  

The beginning ^ makes this a project. This is a collection of related tasks specified by the @~ entries. In each task, the &r X: Y requires attribute sets X as the label for the task and sets the task labeled Y as a requirement or prerequisite for X. E.g., &r 3: 2 establishes "3" as the label for assemble and "2" (cut pieces) as a prerequisite. The &e extent entries give estimates of the times required to complete the various tasks.

↩︎

1.2.9. A goal: interval training 3 times each week

! interval training @s 2026-01-05 @o 3/1w

  

The beginning ! type character makes this reminder a goal with the following interval training as the subject. The @t 3/1w attribute is required and sets the target to be 3 completions during the period of one week starting at midnight on '2026-01-05', because of the @s attribute, and ending one week later at midnight on '2026-01-12', because of the '1w' target period.

When a goal is created, the attribute @k 0 is automatically added to indicate that the current completion count is zero. When a completion is recorded for the goal, this count is automatically increased by one. This process continues until

  1. the period allowed for completing the goal expires or
  2. the completion count reaches the target number of completions

In either case, @k is reset to zero and @s is reset to the previous value plus the period allowed for completion of the goal, i.e, to the end of the period originally allowed for completion.

↩︎

1.2.10. A draft reminder: meet Alex for coffee - time to be determined

? Coffee with Alex @s fri @e 1h

  

The beginning ? type character makes this a draft reminder. This can be changed to an event when the details are confirmed by replacing the ? with an * and adding the time to @s fri.

This is a reminder that is not yet finished and, in almost every respect, will be ignored by tklr. The exception is that it will appear highlighted on the current day in Agenda View until it is revised. It can be changed to an event when the details are confirmed by replacing the ? with an * and adding the time to @s.

1.2.11. Attributes

{XXX}: Anniversary Substitution
* Max's {XXX} birthday
  @s 2016-10-23
  @r y &m 10 &d 23

  

The anniversary expression, {XXX}, will be replaced in the listing of the reminder with an @r entry in Agenda, Next and Weeks views by the relevant number of the anniversary. For example, in the listing for October 23, 2026, the subject of this reminder would appear as Max's 10th birthday. Note that the appropriate suffix - st, nd, rd or th - is automatically applied.

↩︎

1.3. Mouse-Free navigation

Each of the main views in tklr can be opened by entering the first letter of the view's name.

View Key Displays
Agenda A events, goals, tasks
Bins B Tree view of Bins
Completed C Datetimes for completed tasks
Find F Case insenstive search in subjects and details
Last L The last instance of reminders before today
Modified M All reminders sorted by the modified timestamp
Next N The next instance of reminders after today
Query Q List matches for a specified query
Remaining R List remaining alerts for the today
Tags T List reminders with tags grouped by tag
Weeks W List scheduled reminders by week and weekday

Each of these views displays a vertical list of reminders, with each reminder row beginning with a tag from "a", "b", ..., "z", followed by the pertinent details of the reminder including its subject. When necessary, lists are split into pages so that no more than 26 reminders appear on any one page and the left and right cursor keys are used to move back and forth between pages.

These View keys and tags are the key to navigating tklr.

On any page, pressing the key corresponding to a tag will open a display with all the details of the corresponding reminder. This is worth emphasizing. You don't select a reminder with the cursor keys or the mouse and then press enter to see its details - just press the key corresponding to its tag.

When the details of reminder are being displayed, pressing enter will open a menu of various commands applicable to the selected reminder, pressing the key corresponding to the tag of another reminder will switch the details display to that reminder or pressing the upper case letter corresponding to another view will switch to that view.

Everything you might want to do to a reminder, to edit, finish, reschedule, delete or whatever is available by

  1. pressing the key corresponding to the tag of the reminder you want to select
  2. pressing enter to open the menu of commands for the selected reminder
  3. pressing the first letter (any case) of the desired command or escape to cancel and close the details command menu

1.4. What you need to know now: Agenda View

Each of the main views in tklr can be opened by entering the first letter of the view's name, e.g., pressing A (shift+a) will open Agenda View, B (shift+b) will open Bins View and so forth.

Each of these views display vertical lists of reminders, with each reminder row beginning with a tag from "a", "b", ..., "z", followed by the pertinent details of the reminder including its subject. When necessary, lists are split into pages so that no more than 26 reminders appear on any one page and the left and right cursor keys are used to move back and forth between pages.

These tags are the key to navigating tklr. Please give the next two paragraphs your careful attention.

On any page, pressing the key corresponding to a tag will open a display with all the details of the corresponding reminder. This is worth emphasizing. You don't select a reminder with the cursor keys or the mouse and then press enter to see its details - just press the key corresponding to its tag.

When the details of reminder are being displayed, pressing the key corresponding to the tag of another reminder will switch the details display to that reminder, pressing the upper case letter corresponding to another view will open that view or pressing enter will open a menu of various commands applicable to the selected reminder. Everything you might want to do to a reminder, edit, finish, reschedule, delete, ... is available by

  1. pressing the key corresponding to the tag of the reminder you want to select
  2. pressing enter to open the menu of commands for the selected reminder
  3. pressing the first letter (any case) of the desired command or escape to cancel and close the menu

The Agenda view itself displays

  1. the next few days of events begining today
  2. goals ordered by their priority
  3. tasks ordered by their urgency
Description

While the listing of events begins with the current day, any all-day events or events whose ending times have already passed such as the one tagged a will be dimmed. Additionally an event, such as the one tagged b whose active period overlaps the current moment will be highlighted.

The first day of events will always include any notices of upcomming events or draft reminders needing completion in addition to any scheduled events for today. In this case the reminder tagged d indicates that there is an event beginning in 5 days (`+5d`) with a subject beginning with "Quaerat amet ..." a notice attribute, @n x, in which x > 5d. This attribute is the reason this notice of the event is being displayed before its scheduled datetime - it will continue to be displayed on the first day (current date) of Agenda View each day until the day of the event.

There is also a draft entry displayed in red. This is simply a reminder whose item type is ?. This is used to flag a reminder as incomplete as would be the case, e.g., if a final datetime for the event had not yet been finalized. Draft reminders are also displayed on the current, first day in Agenda view until the item type is changed.

The list for goals includes all goals which have not been completed on the current date, sorted and color coded by their priorty, which is listed in the first column after the tags. The details for precisely how priority is calculated will be described later but the basic idea involves comparing

  1. the rate at which completions would currently need to occur to complete the goal
  2. the rate originally specified in the goal

The higher the current rate relative to the original, the higher the priority.

The list for tasks includes all tasks with the possible exception of tasks with both an @s (specifying a due datetime) and an @n entry (specifying a notification period). Suppose, for example, that @s 2026-01-30 and @n 2d. The role of these combined entries is to say that the task needs to be finished by 2026-01-30 but you don't want to be bothered about it until two days before that date. This task won't appear in the list until 2026-01-28.

Tasks are sorted by their urgency. This calculation is fairly complicated and will be described later. Many factors are involved including the priority of the task, its due datetime, how many tags it has, whether it has a details attribute and so forth. The weights attached to these and other characteristics are options which can be set in the user configuration file.

Agenda is the default view and represents the place to go for what you need to know right now.

↩︎

2.2. Bins View

Hierarchical display of bins and reminders.

Many note taking applications provide a means for establishing links between notes. The terms Zettelkasten and Second Brain come to mind. A different approach is taken in tklr where bins serve as containers for both reminders and other bins. While a system of links between reminders might be broken by the removal of a reminder, when a reminder is removed from tklr, it simply disappears from the relevant bin membership lists. Bins themselves and their membership lists are otherwise unaffected.

These are the important facts:

  1. Bin names are unique.
  2. A bin can contain many other bins (children)
  3. A bin can belong to at most one other bin (parent).
  4. A reminder can belong to many bins.

The first three are illustrated by Bins view:

Bins: root is the active parent
Bins: root is the active parent

You may think in the journal branch the years 2025, 2026 and so forth may be unique, but surely the months 11, 12, and so forth, will be repeated. Similarly, under people, the children names A, B, ... might be needed for other purposes as well. How is uniqueness to be maintained? The solution is that 11, 12, A, B, ... are aliases for the actual bin names which are, in fact, 2025:11, 2025:12, people:A, people:B, .... The general rule is this: whenever a bin named XXX:YYY is the child of a bin named XXX, the child will be displayed using the alias YYY. Why this insistence upon uniqueness? It means that specifying membership in the bin root / journal / 2025 / 2025:11 can be done by just specifying @b 2025:11.

As an illustration of the power of being able to place a reminder in many bins consider a note describing a visit to Lille, France on November 11, 2025 which involved meeting a dear friend, Mary Smith for lunch. Bins to which this might belong:

  • travel (in activities)
  • 2025:11 (in journal)
  • Smith,Mary (in people)
  • Lille (in places)

As with the other views, key presses corresponding to tags control the action. Note that the children of root are tagged. Pressing the tag for journal, b, makes it the active parent:

Agenda view in Tklr
Bins: journal is the active parent

Note the bread crumb header, 0 root / journal, in which root now has the tag 0 and is followed by journal which is now the active parent with its children now displayed with tags. Pressing 0 would restore root as the active parent and pressing the tag for one of the children would make that the active parent. Here's the view that results from pressing a, the tag for 2025:

Agenda view in Tklr
Bins: 2025 is the active parent

Pressing a again, now the tag for 2025:11, makes this the active parent:

Agenda view in Tklr
Bins: 2025 is the active parent

Notice in the bread crumb header that there are integer tags going backward for each of the ancestors and the active parent is, as usual, the last element of the path. But it is listed here not using the alias, 11, but the actual bin name, 2025:11.

↩︎

2.3. Completed View - TO BE DONE

↩︎

2.4. Find View

Reminders whose subject or detail entries contain a case-insensitive match for an entered expression.

When you need an exhaustive search across all reminders for a case-insensitive match in either the subject or the details, this is the view. Just press "F" in any view to activate the search bar:

Agenda view in Tklr
Find: initializing the search

When submitted, the matching reminders are listed:

Agenda view in Tklr
Find: matching reminders

↩︎

2.5. Goals View

Press G to open Goal View displaying a tagged list of goals sorted by their priority.

How is priority calculated? Suppose, for example, @t 3/1w is specified in a goal, then n = 3 is the specified number of completions and t = 1w is the time period allowed for their completion. Further suppose that at a particular moment, n' is the number of instances remaining unfinished and t' is the time remaining in the period for their completion. Initially, the needed rate of completions to satisfy the goal is n/t. At the moment being considered, the needed rate of completions goal is n'/t'.

Now consider these possibilities:

  • n'/t' > n/t: the needed completion rate has increased - completions are behind schedule
  • n'/t' = n/t: the needed completion rate is unchanged - completions are on schedule
  • n'/t' < n/t: the needed completion rate has decreased - completions are ahead of schedule

If priority is defined the current rate of completion as a percentage of the original rate, 100 (n' / t') / (n / t) = 100 (n' t) / (t' n), then these possibilites can be restated as

  • priority > 100: the needed completion rate has increased - completions are behind schedule
  • priority = 100: the needed completion rate is unchanged - completions are on schedule
  • priority < 100: the needed completion rate has decreased - completions are ahead of schedule

Consider a goal a goal with the target @t n/t so that n is the number of completions intended for the period t, n' the number of instances remaining this period, and t' the time remaining before the period ends. The ratio priority = 100 * (n' * t) / (n * t') indicates how far ahead or behind the schedule you are:

  • priority > 100: the goal is behind schedule, so it floats to the top of the view.
  • priority = 100: you are on schedule.
  • priority < 100: you are ahead of schedule.

The time remaining column uses the current datetime, so refreshing the view immediately reflects newly completed instances.

  • 00:00-05:59 night
  • 06:00-11:59 morning
  • 12:00-17:59 afternoon
  • 18:00-23:59 evening

If the busy period for an event overlaps one or more of these periods then those periods are tentatively colored green. If the busy periods for two events overlap within one or more periods, then those periods are colored red to indicate the conflict. E.g., the red afternoon cell for Wednesday, reflects the conflict between the reminders tagged g and h

Pressing g displays the details for that reminder.

Agenda view in Tklr
Weeks View: details for the reminder tagged g

↩︎

2.6. Last View

The last instance of every scheduled reminder occurring before the current moment listed in descending order by date and time.

Agenda view in Tklr
Agenda View: The second and last page

↩︎

2.7. Modified View - TO BE DONE

↩︎

2.8. Next View

The first instance of every scheduled reminder occurring after the current moment listed in ascending order by date and time.

Agenda view in Tklr
Agenda View: The second and last page

Need to find, say, your next dental appointment - this is the view. Just press / to activate search and enter the expression.

Agenda view in Tklr
Search: initializing the search

The reminders with case-insensitive matches will be highlighted:

Agenda view in Tklr
Search: highlighted matches

↩︎

2.9. Query View - TO BE DONE

↩︎

2.10. Remaining Alerts View - TO BE DONE

↩︎

2.11. Tags View - TO BE DONE

↩︎

2.12. Weeks View

Scheduled Reminders for the Week with busy times displayed by a leading busy bar.

Agenda view in Tklr
Weeks View: busy bar and scheduled reminders for week

The left and right cursor keys shift the display backward and forward one week at a time. Adding shift to these cursor keys shifts by four weeks at a time. The space key restores the display to the current week.

There are 5 cells in the busy bar for each week day. The first (furthest left) displays a yellow square if an all day event such as a holiday is scheduled for that date. The remaining 4 cells correspond to the 6-hour periods during the day:

↩︎

Details

DateTime details

Intelligent parsing of the user's entry of a datetime is supported. Suppose it is Thursday, November 6 2025 in the US/Eastern timezone. When a datetime is entered it is interpreted relative to the current date, time and timezone. When entering the scheduled datetime for a reminder using @s, the following table illustrates how various entries would be interpreted and the resulting user feedback.

@s entry interpretation user feedback
wed 2025-11-12 Wed, Nov 12 2025
9a 2025-11-06 09:00 EST Thu, Nov 6 2025 09:00 EST
9a fri 2025-11-07 09:00 EST Fri, Nov 7 2025 09:00 EST
10 9p z none 2025-11-10 09:00 Mon, Nov 10 2025 21:00
3p z US/Pacific 2025-11-06 18:00 EST Thu, Nov 6 2025 18:00 EST
10 13:30 z CET 2025-11-10 07:30 EST Mon, Nov 10 2025 07:30 EST
10 20h z none 2025-11-23 20:00 Mon, Nov 10 2025 20:00

Datetimes entered with "z none" and dates are naive - have no timezone information. Datetimes entered with "z TIMEZONE" are interpreted as aware datetimes in TIMEZONE. Datetimes without a "z" entry are also interpreted as aware but in the timezone of the user's computer. Aware datetimes are always reported using the timezone of the user's computer, wherever it might be. Times can be entered using the suffix of either a/p or am/pm for AM/PM times or h for 24-hour times. Times are reported using the preference of the user, here as 24-hour times.

Why would you want to use a "z" in specifying a time? Suppose a colleague in Europe at asked you to call Friday at 18:00 CET time. Then setting "@s fri 18h z CET" will schedule your reminder for the correct time to call wherever you might be. In the US/Eastern timezone, this would be "Fri, Nov 12 2025 12:00 EST". As a second example, suppose you want to take a daily medication at 4pm in whatever timezone you happen to be. Then you will want to schedule the reminder for "@s 4p z none".

When dates and datetimes are recorded, aware datetimes are first converted to UTC time and then stored with a "Z" appended. E.g., the "3p z US/Pacific" datetime would be interpreted as "2025-11-06 18:00 EST" but would be recorded as "20251106T2300Z". Dates and naive datetimes are recorded without conversion and without the trailing "Z". When aware datetimes are displayed to the user, they are first converted to the timezone of the user's computer. Thus the "PST" example would be displayed as scheduled for 6pm today in US/Eastern. Dates and naive datetimes are displayed without change in every timezone.

When an @s scheduled entry specifies a date without a time, i.e., a date instead of a datetime, the interpretation is that the task is due sometime on that day. Specifically, it is not due until 00:00 on that day and not past due until 00:00 on the following day. The interpretation of @b and @u in this circumstance is similar. For example, if @s 2025-04-06 is specified with @b 3d and @u 2d then the task status would change from waiting to pending at 2025-04-03 00:00 and, if not completed, to deleted at 2025-04-09 00:00.

Note that times can only be specified, stored and displayed in hours and minutes - seconds and microseconds are not supported. Internally datetimes are interpreted as having seconds equal to 0.

Interval details

An interval is just a period of time and is entered in tklr using expressions such as

entry period of time
2h 2 hours
-2h - 2 hours
1w7d 1 week and 7 days
2h30m 2 hours and 30 minutes
1m27s 1 minute and 27 seconds

Note that w (weeks), d (days), h (hours), m (minutes) and s (seconds) are the available units for entering intervals. Seconds are ignored save for their use in alerts - more on alerts later.

An interval, I, can be added to a datetime, T, to get a datetime, T + I, that will be after T if I > 0 and before T if I < 0. Similarly, one datetime, A, can be subtracted from another, B, to get an interval, I = B - A, with I > 0 if B is after (greater than) A and I < 0 if B is before (less than) A.

Scheduled datetime details

For the discussion that follows, it will be assumed that the current date is 2025-10-01 and that the scheduled datetime for the illustrative reminder is

@s 2025-10-21 10:00am

extent

The entry @e 2h30m would set the extent for the reminder to two hours and 30 minutes.

If the reminder were an event, this would schedule the "busy time" for the event to extend from 10am until 12:30pm.

For a task, this same entry would indicate that attention to completing the task should begin no later than 10am and that 2 hours and 30 minutes is the estimate of the time required for completion. The period from 10am until 12:30pm is not displayed as a busy time, however, since the task could be begun before or after 10am and could take more or less than 2 hours and 30 minutes to complete. For a task, both @s and @e are best regarded as estimates.

For a project, this same entry would similarly indicate that attention to completing the project should begin no later than 10am and that two hours and 30 minutes is estimated for completion subject to additional times specified in the jobs. A job entry containing &s 2d &e 3h, for example, would set the scheduled time for this job to be two days after the @s entry for the project and would add three hours to the estimate of total time required for the project.

notice

The entry @n I where I is a positive interval specifies that a notice for the reminder should begin on the date in which scheduled - I falls. For the example, adding @b 1d12h would set notice to the date corresponding to

  2025-10-21 10am - 1d12h = 2025-10-19 10pm

so notices would begin on 2025-10-19.

If the reminder is an event, then the agenda view would display an notice for the event beginning on 25-10-19 and continuing on the 25-10-20, i.e., from the date of the notice through the date before the scheduled datetime. For an event think of this notice as a visual alert of the proximity of the event.

If the reminder is a task, then the task would not appear in the agenda view until 25-10-19, i.e., it would be hidden before that date.

wrap

The entry @w BEFORE, AFTER, where BEFORE and AFTER are intervals, can be used to wrap the scheduled datetime of a reminder. Possible entries and the resulting values of BEFORE and AFTER are illustrated below:

entry before after
@w 1h, 30m 1 hour 30 minutes
@w 1h, 1 hour None
@w , 30m None 30 minutes

Consider an event with @s 2025-10-21 10am @e 2h30m, which starts at 10am and ends at 12:30pm and suppose that it will take an hour to travel to the location of the event and 30 minutes to travel from the event to the next location. The entry @w 1h, 30m could be used to indicate these travel periods from 9am until 10am before the event begins and from 12:30pm until 1pm after the event ends.

alert

An alert is specified using @a <list of invervals> : <list of commands>. An @s <datetime> is required and the result is to execute the commands in <list of commands> at the datetimes resulting from subtracting the intervals in <list of intervals> from <datetime>. E.g., with @s 17:00 fri and @a 1h, -15m: c, d, the commands c and d would each be executed at 17:00 - 1h = 16:00 and 17:00 + 15m = 17:15 on Friday.

A command such as d in the example must be specified in the user configuration file. This is the relevant section:

[alerts]
# dict[str, str]: character -> command_str.
# E.g., this entry
#   d: '/usr/bin/say -v Alex "[[volm 0.5]] {subject}, {when}"'
# would, on my macbook, invoke the system voice to speak the subject
# of the reminder and the time remaining until the scheduled datetime.
# The character "d" would be associated with this command so that, e.g.,
# the alert entry "@a 30m, 15m: d" would trigger this command 30
# minutes before and again 15 minutes before the scheduled datetime.

Recurrence details

@r and, by requirement, @s are given

When an item is specified with an @r entry, an @s entry is required and is used as the DTSTART entry in the recurrence rule. E.g.,

* datetime repeating @s 2025-11-06 14:00 @r d &i 2

With this entry, the @s 2025-11-06 14:00 and @r d &i 2 parts would be combined by tklr to generate this rruleset:

      "rruleset": "DTSTART:20251106T1900Z\nRRULE:FREQ=DAILY;INTERVAL=2"

Two aspects of this rruleset are worth emphasizing

  1. "DTSTART:20251106T1900Z\nRRULE:FREQ=DAILY;INTERVAL=2" is a string and can therefore be stored without conversion in SQLite3 - the database used for tklr.
  2. Even though it is only 50 characters long, it actually represents an infinite number of datetimes - every datetime matching the recurrence rule which occurs on or after 2025-11-06 19:00 UTC.

In the hands of the wonderful python library dateutil, this rruleset string can be asked a variety of useful questions which will be answered almost instantly. E.g, What datetimes does it represent which lie between 2025-06-23 08:00 and 2026-01-01 00:00?, What is the first datetime after 2025-10-15 00:00? What is the last datetime before 2025-12-15 00:00? And so forth.

For every reminder in tklr which involves datetimes, a rruleset is used to represent all of those datetimes.

Note: The datetimes generated by the rruleset correspond to datetimes matching the specification of @r which occur on or after the datetime specified by @s. The datetime corresponding to @s itself will only be generated if it matches the specification of @r.

@s is given but not @r

On the other hand, if an @s entry is specified, but @r is not, then the @s entry would be stored as an RDATE in the recurrence rule. E.g.,

* datetime only @s 2025-11-06 14:00

would be serialized (stored) as

  "rruleset": "RDATE:20251106T1900Z"

The datetime corresponding to @s itself is, of course, generated in this case.

@+ is specified, with or without @r

When @s is specified, an @+ entry can be used to specify one or more, comma separated datetimes. When @r is given, these datetimes are added to those generated by the @r specification. Otherwise, they are added to the datetime specified by @s. E.g., is a special case. It is used to specify a datetime that is relative to the current datetime. E.g.,

   ... @s 2025-11-06 14:00 @+ 2025-11-13 21:00

would be serialized (stored) as

  "rruleset": "RDATE:20251106T1900Z, 20251114T0200Z"

This option is particularly useful for irregular recurrences such as annual doctor visits. After the initial visit, subsequent visits can simply be added to the @+ entry of the existing event once the new appointment is made.

Note: Without @r, the @s datetime is included in the datetimes generated but with @r, it is only used to set the beginning of the recurrence and otherwise ignored.

Timezone considerations

When a datetime is specified without an z component, the timezone is assumed to be aware and in the local timezone. The datetime is converted to UTC for storage in the database. When a datetime is displayed, it is displayed using the local timezone of the computer.

This remains true with recurrence and daylight savings time but is a little more complicated. As always, the recurrence rules are stored in UTC and the datetimes generated by the rules are also in UTC. When these datetimes are displayed, they are converted to the local timezone.

   ... @s 2025-10-31 14:00 @r d &i 1 &c 4

With this entry, the rruleset and datetimes generated show the effect of the transition from daylight to standard time:

"rruleset": "DTSTART:20251031T1800Z\nRRULE:FREQ=DAILY;INTERVAL=1;COUNT=4"

  Fri 2025-10-31 14:00 EDT -0400
  Sat 2025-11-01 14:00 EDT -0400
  Sun 2025-11-02 13:00 EST -0500
  Mon 2025-11-03 13:00 EST -0500

Urgency

Since urgency values are used ultimately to give an ordinal ranking of tasks, all that matters is the relative values used to compute the urgency scores. Accordingly, all urgency scores are constrained to fall within the interval from -1.0 to 1.0. The default urgency is 0.0 for a task with no urgency components.

There are some situations in which a task will not be displayed in the "urgency list" and there is no need, therefore, to compute its urgency:

  • Completed tasks are not displayed.
  • Hidden tasks are not displayed. The task is hidden if it has an @s entry and an @b entry and the date corresponding to @s - @b falls sometime after the current date.
  • Waiting tasks are not displayed. A task is waiting if it belongs to a project and has unfinished prerequisites.
  • Only the first unfinished instance of a repeating task is displayed. Subsequent instances are not displayed.

There is one other circumstance in which urgency need not be computed. When the pinned status of the task is toggled on in the user interface, the task is treated as if the computed urgency were equal to 1.0 without any actual computations.

All other tasks will be displayed and ordered by their computed urgency scores. Many of these computations involve datetimes and/or intervals and it is necessary to understand both are represented by integer numbers of seconds - datetimes by the integer number of seconds since the epoch (1970-01-01 00:00:00 UTC) and intervals by the integer numbers of seconds it spans. E.g., for the datetime "2025-01-01 00:00 UTC" this would be 1735689600 and for the interval "1w" this would be the number of seconds in 1 week, 7*24*60*60 = 604800. This means that an interval can be subtracted from a datetime to obtain another datetime which is "interval" earlier or added to get a datetime "interval" later. One datetime can also be subtracted from another to get the "interval" between the two, with the sign indicating whether the first is later (positive) or earlier (negative). (Adding datetimes, on the other hand, is meaningless.)

Briefly, here is the essence of this method used to compute the urgency scores using "due" as an example. Here is the relevant section from config.toml with the default values:

[urgency.due]
# The "due" urgency increases from 0.0 to "max" as now passes from
# due - interval to due.
interval = "1w"
max = 8.0

The "due" urgency of a task with an @s entry is computed from now (the current datetime), due (the datetime specified by @s) and the interval and max settings from urgency.due. The computation returns:

  • 0.0 if now < due - interval
  • max * (1.0 - (now - due) / interval) if due - interval < now <= due
  • max if now > due

For a task without an @s entry, the "due" urgency is 0.0.

Other contributions of the task to urgency are computed similarly. Depending on the configuration settings and the characteristics of the task, the value can be either positive or negative or 0.0 when missing the requisite characteristic(s).

Once all the contributions of a task have been computed, they are aggregated into a single urgency value in the following way. The process begins by setting the initial values of variables Wn = 1.0 and Wp = 1.0. Then for each of the urgency contributions, v, the value is added to Wp if v > 0 or abs(v) is added to Wn if v negative. Thus either Wp or Wn is increased by each addition unless v = 0. When each contribution has been added, the urgency value of the task is computed as follows:

urgency = (Wp - Wn) / (Wp + Wn)

Equivalently, urgency can be regarded as a weighted average of -1.0 and 1.0 with Wn/(Wn + Wp) and Wp/(Wn + Wp) as the weights:

urgency = -1.0 * Wn / (Wn + Wp) + 1.0 * Wp / (Wn + Wp) = (Wp - Wn) / (Wn + Wp)

Observations from the weighted average perspective and the fact that Wn >= 1 and Wp >= 1:

  • -1.0 < urgency < 1
  • urgency = 0.0 if and only if Wn = Wp
  • urgency is always increasing in Wp and always decreasing in Wn
  • urgency approaches 1.0 as Wn/Wp approaches 0.0 - as Wp increases relative to Wn
  • urgency approaches -1.0 as Wp/Wn approaches 0.0 - as Wn increases relative to Wp

Thus positive contributions always increase urgency and negative contributions always decrease urgency. The fact that the urgency derived from contributions is always less than 1.0 means that pinned tasks with urgency = 1 will always be listed first.

Getting Started

Developer Install Guide

This guide walks you through setting up a development environment for tklr using uv and a local virtual environment. Eventually the normal python installation procedures using pip or pipx will be available.

✅ Step 1: Clone the repository

This step will create a directory named tklr-dgrham in your current working directory that contains a clone of the github repository for tklr.

git clone https://github.com/dagraham/tklr-dgraham.git
cd tklr-dgraham

✅ Step 2: Install uv (if needed)

which uv || curl -LsSf https://astral.sh/uv/install.sh | sh

✅ Step 3: Create a virtual environment with uv

This will create a .venv/ directory inside your project to hold all the relevant imports.

uv venv

✅ Step 4: Install the project in editable mode

uv pip install -e .

✅ Step 5: Use the CLI

You have two options for activating the virtual environment for the CLI:

☑️ Option 1: Manual activation (every session)
source .venv/bin/activate

Then you can run:

tklr --version
tklr add "- test task @s 2025-08-01"
tklr ui

To deactivate:

deactivate
☑️ Option 2: Automatic activation with direnv (recommended)
1. Install direnv
brew install direnv        # macOS
sudo apt install direnv    # Ubuntu/Debian
2. Add the shell hook to your ~/.zshrc or ~/.bashrc
eval "$(direnv hook zsh)"   # or bash

Restart your shell or run source ~/.zshrc.

3. In the project directory, create a .envrc file
echo 'export PATH="$PWD/.venv/bin:$PATH"' > .envrc
4. Allow it
direnv allow

Now every time you cd into the project, your environment is activated automatically and, as with the manual option, test your setup with

tklr --version
tklr add "- test task @s 2025-08-01"
tklr ui

You're now ready to develop, test, and run tklr locally with full CLI and UI support.

✅ Step 6: Updating your repository

To update your local copy of Tklr to the latest version:

# Navigate to your project directory
cd ~/Projects/tklr-dgraham  # adjust this path as needed

# Pull the latest changes from GitHub
git pull origin master

# Reinstall in editable mode (picks up new code and dependencies)
uv pip install -e .

Starting tklr for the first time

Tklr needs a home directory to store its files - most importantly these two:

  • config.toml: An editable file that holds user configuration settings
  • tkrl.db: An SQLite3 database file that holds all the records for events, tasks and other reminders created when using tklr

Any directory can be used for home. These are the options:

  1. If started using the command tklr --home <path_to_home> and the directory <path_to_home> exists then tklr will use this directory and, if necessary, create the files config.toml and tklr.db in this directory.

  2. If the --home <path_to_home> is not passed to tklr then the home will be selected in this order:

    • If the current working directory contains files named config.toml and tklr.db then it will be used as home
    • Else if the environmental variable TKLR_HOME is set and specifies a path to an existing directory then it will be used as home
    • Else if the environmental variable XDG_CONFIG_HOME is set, and specifies a path to an existing directory which contains a directory named tklr, then that directory will be used.
    • Else the directory ~/.config/tklr will be used.

Configuration

The default settings are in config.toml in your tklr home directory together with detailed explanations for each setting.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tklr_dgraham-0.0.19.tar.gz (290.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tklr_dgraham-0.0.19-py3-none-any.whl (244.9 kB view details)

Uploaded Python 3

File details

Details for the file tklr_dgraham-0.0.19.tar.gz.

File metadata

  • Download URL: tklr_dgraham-0.0.19.tar.gz
  • Upload date:
  • Size: 290.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for tklr_dgraham-0.0.19.tar.gz
Algorithm Hash digest
SHA256 57bb9f2d0bf610afaf06a57dd742320b2c59b2adcfd13159a7e9bface07f472f
MD5 717e9e7cf67bfabf3731349c5071bbc5
BLAKE2b-256 f705a95968a350c1feca79bf0148505afe69b4a1c481a25a9493783bb0495e30

See more details on using hashes here.

File details

Details for the file tklr_dgraham-0.0.19-py3-none-any.whl.

File metadata

  • Download URL: tklr_dgraham-0.0.19-py3-none-any.whl
  • Upload date:
  • Size: 244.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for tklr_dgraham-0.0.19-py3-none-any.whl
Algorithm Hash digest
SHA256 0e24ec776adc7715d712d857a1e36116a60bb33c4e23339cb445ae4aaf7d3c08
MD5 a70155e38224dd17df3663a84a3be33d
BLAKE2b-256 11fe89e86a9a6bff2c5ce4184013c6182465da8ae555e1205e7d4f9859d7f0fd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page