Task manager
  • C 55.3%
  • C++ 40.9%
  • Roff 2%
  • Lua 1.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-01 11:35:45 +01:00
.task Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00
assets skill: note to run tatr init when .task is missing 2026-10-01 11:35:45 +01:00
src Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00
tests/unit Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00
.gitignore Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00
LICENSE Apply the GPL-2 license 2026-08-30 08:51:18 +07:00
README.md Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00
xmake-requires.lock Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00
xmake.lua Rewrite in C++ with xmake 2026-09-12 16:58:23 +01:00

Warning

This is a personal adaptation of tsoding/tatr that I modified for my own use. It is not intended for general use and comes with no support or stability guarantees. For the supported tool, use upstream.

Changes from upstream

This fork is a C++23 rewrite built with xmake:

  • The task folder is now .task/ (was tasks/), and closed tasks can be moved to .task/.archive/.
  • A modernized command set: list, new, find, summary, tag, untag, retag, close, reopen, archive, query, ref, graph, init.
  • TQL additions: bare-HUID matching, closed/open, boolean literals.
  • The original C implementation (nob.c, thirdparty/) was removed.

Task Tracker

Cover by rexim

This is an improvised Tasks system, because I needed something more powerful than just plane TODOs in the Source Code of my projects, yet I didn't want to install a full blown Issue Tracker System.

The Spec

Layout

Each project at the root has a .task/ folder which contains sub-folders for each task. The folder name defaults to .task (change kTasksDirName in src/core/paths.hpp, or set the TATR_TASKS_DIR environment variable, to use another name).

project/
+-...
+-.task/
| +-tags
| +-20260824-215300
| | +-TASK.md
| | +-...
| +-20260830-000403-rexim
| | +-TASK.md
| | +-screenshot.png
| | +-...
| +-...
+-...

HUID

Each task sub-folder is named with a Task ID. The Task ID format is [0-9]{8}-[0-9]{6}. Just grab the current date and time and use it as the Task ID. Use UTC timezone so the current timezone is irrelevant. If your Task ID collides with an existing one, just wait one second and try again.

The full format is actually [0-9]{8}-[0-9]{6}(-[a-zA-Z0-9\\-]*)?. So if you work in a team you can agree on unique suffixes per individual to slap at the end like 20260829-235855-rexim or 20260829-235902-01. Those are valid Task IDs too.

I call this ID system HUID (Human-Unique IDentifier). It is fairly unique if you generate IDs at "Human-speed". That is for me personally the speed at which I need to generate them is never below one second.

Having a Unique Task ID is beneficial under such control systems as git, because you can generate tasks in parallel branches and then relatively easily merge them together.

TASK.md

Inside of the task sub-folder there is one mandatory file TASK.md which is a markdown file describing the task. The folder may contain other files as attachments to the task. TASK.md should link to the attachments as necessary. Try to keep the size of the attachments small, since they are going to be committed to the git repo. Use ffmpeg to reencode any screencast to reduce their size as necessary.

The format of TASK.md:

# <title>

- STATUS: (OPEN|CLOSED)
- PRIORITY: <number>
- TAGS: <comma-and-whitespace-separated-list-of-tags>
[other properties]

[description]

As you work on the task feel free to append any discovered details about the task to the [description].

Use git-blame and git-log to learn about when, how and by whom any changes to the task were made.

Task Properties

The STATUS property defines whether the task is done or not. There could be only two statuses. If you need more it is generally recommended to use TAGS for whatever you are trying to do.

The PRIORITY is generally used for sorting the tasks by the external tools. One thing I've seen people online do is stressing out about what priority to put. Don't think of priority as an absolute value. Think of it as the means of getting your list of tasks sorted in a specific way you want them to see.

The TAGS property contains the list of tags separated by commas and whitespaces. TAGS: foo,bar,baz defines 3 tags. TAGS: foo,,, hello world also defines 3 tags. You can use these tags to group tasks into categories. Like bug or enhancement.

We allow to specify [other properties] (in addition to STATUS, PRIORITY, and TAGS) in a similar format (that is - [NAME]: [VALUE]), the official tool will ignore them but try its best to not disturb them too much during any mass update operations (like tatr-untag, etc).

Duplicated properties are not allowed. If a tasks specifies duplicated properties only the last value must be taken into account. Mass update operations of the official tool will remove any duplicated properties.

Tags description file

There might be an optional .task/tags file with the following format:

<tag-name> [,] <tag-description>
<tag-name> [,] <tag-description>
<tag-name> [,] <tag-description>
...

It serves as a documentation for each existing tag and the thirdparty tools may use it to display the tag descriptions.

The Tool

The repo comes with a command line tool that helps to navigate and manipulate the .task/ folder:

$ xmake f -y
$ xmake
$ sudo cp ./build/linux/x86_64/release/tatr /usr/local/bin/
$ tatr help

Run the test suite with xmake test. Debug builds (ASan + UBSan) are configured with xmake f -m debug. A fully static executable can be built with xmake f --static=y. xmake install -o ~/.local installs the binary, the tatr(1) man page, and the opencode skill under share/opencode/skills/tatr/SKILL.md.

The skill is also embedded in the binary, so it can be installed where opencode picks it up automatically:

$ tatr install-skill            # ~/.config/opencode/skills/tatr/SKILL.md
$ tatr install-skill --project  # ./.opencode/skills/tatr/SKILL.md

For editor tooling (clangd, etc.) generate compile_commands.json with xmake project -k compile_commands.

It is specifically optimized to be run in compilation mode of Emacs. Not sure how useful it is outside of this use case.

We only support Linux right now But I have tasks to add Windows and MacOS support in the future.

Commands

Command Description
tatr init Initialize the task database (.task/ by default)
tatr new [TITLE...] Create a new task (-t/--tag, -p/--priority, -s/--suffix)
tatr list [QUERY...] (alias ls) List tasks matching a query (-c/--closed, -a/--ascending, --all, --id, -n/--limit, --filtered)
tatr find <HUID> (alias show) Find a task by HUID (--path-only)
tatr summary Print a summary of the tasks (-c/--closed)
tatr tag -t <TAG>... [QUERY] Add tags to matching tasks
tatr untag -t <TAG>... [QUERY] Remove tags from matching tasks
tatr retag <OLD> <NEW> [QUERY] Rename a tag across tasks and the .task/tags file
tatr close [QUERY] Close matching tasks
tatr reopen [QUERY] Reopen matching tasks
tatr archive [QUERY] Move closed tasks into .task/.archive/
tatr query [QUERY] Print the tokens and parsed AST of a query
tatr ref [HUID] Grep the repo for references to a task
tatr graph Generate a graph of task cross-references
tatr install-skill [--project] Install the embedded opencode skill for the user or the project
tatr version Print the current version

Mutating commands support --dry-run and -v/--verbose.

You are welcome to make your own tools.

Tatr Query Language (TQL)

The Query language that is used in tatr ls command to select a set of tasks.

Examples

Query everything with tag bug:

$ tatr ls :bug

Everything with tag bug, but without tag ui:

$ tatr ls :bug and not :ui

Everything that is not tagged:

$ tatr ls not tagged

All the bugs with priority less than 50:

$ tatr ls :bug and priority lt 50

Syntax

Here is the Backus–Naur form of the language:

<expr>            ::= <or>
<or>              ::= <and> *('or' <and>)
<and>             ::= <compare> *('and' <compare>)
<compare>         ::= <primary> *(<compare-op> <primary>)
<compare-op>      ::= 'lt' | 'le' | 'gt' | 'ge' | 'eq' | 'ne'
<primary>         ::= <tag>
                    | '[' <expr> ']'
                    | 'not' <primary>
                    | 'any'
                    | 'tagged'
                    | 'priority'
                    | <number>
<tag>             ::= ':' 1*<any-character-except-whitespaces-and-square-brackets>
<number>          ::= ['-'] 1*<digit>

The syntax is designed to be used in shell environment without requiring any special escaping.

In shells parenthsis usually have special meaning. Because of that we use square brackets for grouping expressions '[' <expr> ']'.

Another problematic symbols are < and >, which are usually used for file redirecting. Which means we can't easily use traditional comparison operators. So we are following the test(1) utility convention: lt, le, gt, ge, eq, and ne.

Since brackets have a special meaning in TQL, if you have any tags that contain them, you probably won't be able to filter by them (even though the specs do not explicitly prohibit square brackets in the tags). Just don't use square brackets in the tags if you are using tatr I guess. I may do something about that later.

Reference

Expression Description
<a> or <b> True when <a> or <b> or both are true. Just a regular logical disjunction.
<a> and <b> True when both <a> and <b> are true. Just a regular logical conjunction.
:<tag> True when a task's TAGS property contains <tag>.
not <expr> True when <expr> is false.
tagged True when a task has at least one tag in its TAGS property.
any Always true for any task.
priority Priority of the task as an integer.
<a> lt <b> True when <a> is less than <b>.
<a> gt <b> True when <a> is greater than <b>.
<a> le <b> True when <a> is less or equal to <b>.
<a> ge <b> True when <a> is greater or equal to <b>.
<a> eq <b> True when <a> is equal to <b>.
<a> ne <b> True when <a> is not equal to <b>.

The License

All the code in this repo is released under GNU General Public License, version 2 license unless stated otherwise. Third-party dependencies are managed by xmake and keep their own licenses.

The cover art in the ./assets/cover/ folder is under the CC BY-NC.