I have two modes, completely distracted and dead focused. So naturally I need some help getting things done. I've tried expensive apps like Omnifocus, cli apps like Todo.txt or TaskWarrior and everything in between. In the end I realized that these apps are not for people like me. What works for me is big heavy tools for long term planning and collaboration like Jira or Trello. For my day to day stuff, the things I actually check regularly a simple checklist is more than enough.

A poor man's todo list

First off I need somewhere to store my todo lists. Dropbox is currently what I use for this.

export TDY_PATH="$HOME/Dropbox/tdy"

Then we need a function to create and open the file

function tdy() { # The today todo list
  category=${1:-work}
  tdy_folder="${TDY_PATH}/${category}"
  tdy_current_file="${tdy_folder}/$(date +'%Y/%m/%Y.%m.%d').wiki"
  tdy_current_folder="${tdy_current_file%/*}"
  tdy_previous_file=$(find "$tdy_folder" -type f -exec stat -f "%m %N" {} \; | sort -nr | head -n1 | cut -d ' '  -f 2)

  mkdir -p "${tdy_current_folder}"

  if [[ ! -f "$tdy_current_file" ]]; then
    # Replace the path and '.wiki' with equal signs
    printf '%s\n' "= $(date +'%Y.%m.%d') =" >> "$tdy_current_file"
    if [[ -f "$tdy_previous_file" ]]; then
      grep '\- \[[o ]' "$tdy_previous_file" | sed 's/\[o\]/[ ]/' >> "$tdy_current_file"
    fi
  fi

  if [[ -t 1 ]]; then
    "$EDITOR" "${tdy_current_file}"
  else
    # We're in a pipe, so let's cat this instead
    cat "${tdy_current_file}"
  fi
}

Basically we have a function tdy that takes an argument, which is the category for the todo list I want. I generate what should be the file of todays todo, and create the folder for it if it doesn't exist. I also need to find the previous todo file since I might have unfinished business there. If todays file doesn't exist I create it and add the stuff from the last file that was not completed. This code isn't beautiful, but because there is a bit of manual lifting I haven't found a good way to clean it up. At the end I check to see if I'm am being called from a pipe, in which case I just cat the list, if not I open it with whatever application is defined in $EDITOR.

I use Vimwiki for my lists (and notes), which is why I have the wiki extension on the list. It let's me make a bullet item into a todo item by pressing ctrl-space, pressing once more completes the task. And when a task has a subtask which is completed the parent gets an indicator for this. It looks good and works very well for me.

drawing

Since I now know where my task are defined, and what format they have, I can easily search for them, to figure out when they were completed.

function tdy_done() { # Search for done tasks
  grep -R -i "$1" ${TDY_PATH} | grep '\- \[X\]'
}

Visualizing the todo list

I keep my todo list open in a tmux pane at all times, but I also tend to have a main monitor, so my laptop screen has nothing on it. So while searching for a MacOS port of the old Conky I stumbled upon √úbersicht. It's an application that will let you write HTML/JavaScript to create an overlay on your desktop background. Mine is here And it looks pretty awesome to me, but I'm no front end wiz.

drawing

Making the most of the time at hand

I like music, but sometimes its just in the way because what I really want is to block everything out. My noise canceling headphones do help, but I also like to spin up some white noise. From the terminal.

alias noise='play -q -c 2 --null synth brownnoise band -n 2500 4000 tremolo 20 .1 reverb 50'

I found this somewhere once, and just adapted it. I don't really understand it all that well, but I like the sound it makes.

I'm also a big fan of setting timers. For everything. Being deeply focused means that you'll forget about moving on to other important stuff, or going to lunch. So I use this simple timer.

function timer() { # takes number of hours and minutes + message and notifies you
  time_string=$1
  if [[ "$time_string" =~ ^[0-9]+[:][0-9]+$ ]]; then
    hours=${time_string/:*/}
    minutes=${time_string/*:/}
    seconds=$(( ( (hours * 60) + minutes ) * 60 ))
    time_hm="${hours}h:${minutes}m"
  elif [[ "$time_string" =~ ^[0-9]+$ ]]; then
    seconds=$(( time_string * 60 ))
    time_hm="${time_string}m"
  else
    printf '%s' "error: $time_string is not a number" >&2; return 1
  fi

  shift
  message=$*
  if [[ -z "$message" ]]; then
    printf '%s' "error: ne need a message as well" >&2; return 1
  fi

  (nohup terminal-notifier -title "Timer: $message" -message "Waiting for ${time_hm}" > /dev/null &)
  (nohup sleep "$seconds" > /dev/null && terminal-notifier -title "${time_hm} has passed" -sound default -message "$message" &)
}

I think half of this is from a StackOverflow response, but it basically takes a number of minutes, or duration on the form HH:MM and sleeps for the appropriate amount of seconds. When the sleep is over, a notification is displayed. In this case using terminal-notifier which is MacOS specific, but there are plenty of options for Linux too. The only thing out of the ordinary is possible the use of nohup. It takes the following command and detaches is from my session so I can even log out and the command will still run until completed. Oh, and the >&2 is for writing to stderr

I did plan to add a sound to this timer, but I don't seem to need it, so maybe some other time.

Discussion: reddit.com/r/bash - post