Back to Blog

My Personal Task Manager

I've tried dozens of productivity and task mangement software but nothing's stuck. I keep returning to handwritten lists desperately wishing for a digital solution that works for me. So I decided to build one myself.

I wanted to:

  • minimize context switching required to edit/add tasks
  • mimic the experience of using pen and paper as much as possible
  • take no more than 30 minutes to write the first version.

My Task Manager

I wrote two shell commands: l(ong)t(ask), and, s(hort)t(ask):

  • All arguments to lt and st are appended to the long-tasks.md and short-tasks.md file respectively.
  • Executing lt or st without any arguments shows the first 10 lines from long-tasks.md or short-tasks.md respectively.
  • Executing t(asks) shows the first 10 lines from both long-tasks.md and short-tasks.md

Source

The core of my task manager is a simple function w(rite)t(ask) that takes a filename and the task as arguments.

wt() {
  file="$1"
 
  if [ ! -f "$file" ]; then
    touch "$file"
  fi
 
  # shift the args to remove the file name
  shift 1
 
  if [ "$#" -eq 0 ]; then
    # show the first 10 lines from $file
    head "$file" | bat -l markdown
    return 0
  fi
 
  # append to the end of file
  echo "- $*" >> "$file"
  return 0
}

I found this simple tool to be quite usable and I really loved how I could add tasks from my terminal without opening any file or switching to a new application.

But, after a while I found that I wanted to group similar tasks together in each file.

Tags and Task Context

Pending tasks with an active context

I decided to group similar tasks under markdown headers.

However, adding a task to the end of the file no longer works, we need to add a task to the correct sub-heading.

Users can specify the heading for task by setting a task context: st ctx Travel

Future invocations to st|lt will add the task under the right heading.

st done clear's the task context

Context Source

Setting and clearing a context is done via the TASK_CONTEXT environment variable. Adding the task under the correct heading for a context is handled by a single gsed command.

Overall, the new wt() function looks like the following:

wt() {
  file="$1"
 
  if [ ! -f "$file" ]; then
    touch "$file"
  fi
 
  # shift the args to remove the file name
  shift 1
 
  if [ "$#" -eq 0 ]; then
    # show the first 10 lines from $file
    head $file | bat -l markdown
    return 0
  fi
 
  if [ "$1" = "done" ]; then
    # clear the task context
    export TASK_CONTEXT=""
    return 0
  fi
 
  if [ "$1" = "ctx" ]; then
    # set the task context and shift the args
    shift 1
    export TASK_CONTEXT="$*"
    return 0
  fi
 
 
  if [ -z "$TASK_CONTEXT" ]; then
    # append to the end of file if no context is set
    echo "- $*" >> "$file"
    return 0
  fi
 
  # append task at the top of heading determined by $TASK_CONTEXT
  gsed -i "/${TASK_CONTEXT}/a - $*" "$file"
}

I also wrote a custom starship.rs plugin to display the current task manager context so I don't have to remember what I set the context to previously.

[custom.task-ps1]
command = "echo $TASK_CONTEXT"
when = ''' test $TASK_CONTEXT != "" '''
format = 'updating [$output]($style) '

Thoughts so far

tasks has replaced all other analog and digital tools I used to keep track of my to-do list. I can easily write and organize my thougts and tasks without loosing my train of thought and I don't have to put in a lot of effort to prune/maintain each list.

If you'd like to try it out, you can checkout the project here on GitHub.