SkillAgentSearch skills...

Grit

Multitree-based personal task manager

Install / Use

/learn @climech/Grit
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Grit

Grit is an experimental personal task manager that represents tasks as nodes of a multitree, a class of directed acyclic graphs. The structure enables the subdivision of tasks, and supports short-term as well as long-term planning.

<p align="center"> <img src="docs/assets/demo.gif" width="683" /> </p>

Contents

Installation

From source

Make sure go (>=1.14) and gcc are installed on your system. Get the latest release, and run:

$ make && sudo make install

External repositories

Introduction

(This section is a little technical — you may want to skip over to Practical guide first.)

Grit's design is based on two premises:

  1. Breaking a problem up into smaller, more manageable parts is generally a good approach to problem-solving.
  2. Tracking progress across time improves focus by removing the mental overhead associated with many parallel tasks spanning multiple days.

Tasks may be represented as tree nodes, enabling their subdivision. By introducing date nodes into the structure, and viewing the trees in the context of a larger multitree, we can distribute work on root tasks across multiple days by creating cross links to their descendants.

Trees

A big task may be represented as a tree, e.g.:

[~] Digitize family photos
 ├──[x] Scan album 1
 ├──[x] Scan album 2
 ├──[ ] Scan album 3
 └──[ ] ...

In the example above, the parent task is divided into a number of subtasks. Completing the subtasks is equivalent to completing the parent task.

Multitrees

In Multitrees: Enriching and Reusing Hierarchical Structure, George W. Furnas and Jeff Zachs introduce the structure:

[...] a class of directed acyclic graphs (DAGs) with the unusual property that they have large easily identifiable substructures that are trees. These subtrees have a natural semantic interpretation providing alternate hierarchical contexts for information, as well as providing a natural model for hierarchical reuse.

Unlike tree nodes, nodes of a multitree can have multiple parents, allowing us to create cross links between different task trees (see Links below).

Multitrees are digraphs, so the nodes are connected to one another by directed links. For our purposes, the direction flows from parent tasks to their subtasks. From any node we can induce a valid tree by following the outgoing links:

<p align="center"> <img src="docs/assets/fig1.gif" height="275" /> </p>

We also get valid inverted trees by going in the opposite direction, from nodes to their parents! This property is used by Grit to propagate changes made at the lower levels all the way up to the roots.

States

At any given time, a Grit task is said to be in one of the three states:

  1. [ ]inactive; task is yet to be completed
  2. [~]in progress; some of the subtasks have been completed
  3. [x] or [*]completed; [*] is used when the task is viewed in the context of a date that is different from the task's completion date

Date nodes

To add a time dimension to the structure, the idea of a date node is introduced.

A date node is a root node with a special name that follows the standard date format YYYY-MM-DD. Descendants of date nodes are supposed to be completed on the stated date. Date nodes exist so long as they link to at least one descendant—they are created and destroyed automatically.

Practical guide

Basic usage

Let's add a few things we want to do today:

$ grit add Take out the trash
(1) -> (2)
$ grit add Do the laundry
(1) -> (3)
$ grit add Call Dad
(1) -> (4)

Run grit without arguments to display the current date tree:

$ grit
[ ] 2020-11-10 (1)
 ├──[ ] Call Dad (4)
 ├──[ ] Do the laundry (3)
 └──[ ] Take out the trash (2)

So far it looks like an old-fashioned to-do list. We can check a task to mark it as completed:

$ grit check 2
$ grit
[~] 2020-11-10 (1)
 ├──[ ] Call Dad (4)
 ├──[ ] Do the laundry (3)
 └──[x] Take out the trash (2)

The change is automatically propagated through the graph. We can see that the status of the parent task (the date node) has changed to in progress.

Subtasks

Let's add another task:

$ grit add Get groceries
(1) -> (5)

To divide it into subtasks, we have to specify the parent (when no parent is given, add defaults to the current date node):

$ grit add -p 5 Bread
(5) -> (6)
$ grit add -p 5 Milk
(5) -> (7)
$ grit add -p 5 Eggs
(5) -> (8)

Task 5 is now pointing to subtasks 6, 7 and 8. We can create infinitely many levels, if needed.

$ grit
[~] 2020-11-10 (1)
 ├──[ ] Call Dad (4)
 ├──[ ] Do the laundry (3)
 ├──[ ] Get groceries (5)
 │   ├──[ ] Bread (6)
 │   ├──[ ] Eggs (8)
 │   └──[ ] Milk (7)
 └──[x] Take out the trash (2)

Check the entire branch:

$ grit check 5
$ grit tree 5
 [x] Get groceries (5)
  ├──[x] Bread (6)
  ├──[x] Eggs (8)
  └──[x] Milk (7)

The tree command prints out a tree rooted at the given node. When running grit without arguments, tree is invoked implicitly, defaulting to the current date node.

Roots

Some tasks are big—they can't realistically be completed in one day, so we can't associate them with a single date node. The trick is to add it as a root task and break it up into smaller subtasks. Then we can associate the subtasks with specific dates.

To create a root, run add with the -r flag:

$ grit add -r Work through Higher Algebra - Henry S. Hall
(9)

It's useful to assign aliases to frequently used nodes. An alias is an alternative identifier that can be used in place of a numeric one.

$ grit alias 9 textbook

The book contains 35 chapters—adding each of them individually would be very laborious. We can use a Bash loop to make the job easier (a feature like this will probably be added in a future release):

$ for i in {1..35}; do grit add -p textbook "Chapter $i"; done
(9) -> (10)
(9) -> (11)
...
(9) -> (44)

Working through a chapter involves reading it and solving all the exercise problems included at the end. Chapter 1 has 28 exercises.

$ grit add -p 10 Read the chapter
(10) -> (45)
$ grit add -p 10 Solve the exercises
(10) -> (46)
$ for i in {1..28}; do grit add -p 46 "Solve ex. $i"; done
(46) -> (47)
(46) -> (48)
...
(46) -> (74)

Our tree so far:

$ grit tree textbook
[ ] Work through Higher Algebra - Henry S. Hall (9:textbook)
 ├──[ ] Chapter 1 (10)
 │   ├──[ ] Read the chapter (45)
 │   └──[ ] Solve the exercises (46)
 │       ├──[ ] Solve ex. 1 (47)
 │       ├──[ ] Solve ex. 2 (48)
 │       ├──[ ] ...
 │       └──[ ] Solve ex. 28 (74)
 ├──[ ] Chapter 2 (11)
 ├──[ ] Chapter ...
 └──[ ] Chapter 35 (44)

We can do this for each chapter, or leave it for later, building our tree as we go along. In any case, we are ready to use this tree to schedule our day.

Before we proceed, let's run stat to see some more information about the node:

$ grit stat textbook

(9) ───┬─── (10)
       ├─── (11)
       :
       └─── (44)

ID: 9
Name: Work through Higher Algebra - Henry S. Hall
Status: inactive (0/63)
Parents: 0
Children: 35
Alias: textbook

We can confirm that the node is a root—it has no parents. There's a little map showing the node's parents and children. Progress is also displayed, calculated by counting all the leaves reachable from the node.

Links

Say we want to read the first chapter of our Algebra book, and solve a few exercises today. Let's add a new task to the current date node:

$ grit add Work on ch. 1 of the Algebra textbook
(1) -> (75)

Create cross links from this node to the relevant textbook nodes (the first argument to link is the origin, the ones following it are targets):

$ grit link 75 45 47 48 49
$ grit
[~] 2020-11-10 (1)
 ├──[x] ...
 └──[ ] Work on ch. 1 of the Algebra textbook (75)
     ├··[ ] Read the chapter (45)
     ├··[ ] Solve ex. 1 (47)
     ├··[ ] Solve ex. 2 (48)
     └··[ ] Solve ex. 3 (49)

The dotted lines indicate that the node has multiple parents. We can confirm this by taking a closer look at one of them using stat:

$ grit stat 45

(10) ───┐
(75) ───┴─── (45)

ID: 45
Name: Read the chapter
Status: inactive
Parents: 2
Children: 0

If we wanted to draw an accurate representation of the entire multitree at this point, it might look something like this:

<p align="center"> <img src="docs/assets/fig2.png" width="750" /> </p>

This looks somewhat readable, but attempts to draw a complete representation of a structure even slightly more complex than this typically result in a tangled mess. Because of this, Grit only gives us glimpses of the digraph, one tree (or ls) at a time. Beyond that it relies on the user to fill in the gaps.

We can check the nodes and see how the changes propagate through the graph:

$ grit check 75
$ grit
[x] 2020-11-10 (1)
 ├──[x] ...
 └──[x] Work on ch. 1 of the algebra textbook (75)
 
View on GitHub
GitHub Stars1.7k
CategoryDevelopment
Updated3h ago
Forks52

Languages

Go

Security Score

100/100

Audited on Mar 27, 2026

No findings