Skip to content


Folders and files

Last commit message
Last commit date

Latest commit



29 Commits

Repository files navigation


A tool for finding out how unproductive I really am.


Unproductive has four major parts.


The first part runs constantly and polls the X server, extracting the title of the currently selected window and printing it, along with the current timestamp, what SSID you are connected to, and if you are running a vpn, to standard out. Expected usage is:

$ unproductive | tee -a ~/activity-log.txt

An example recorded line is:

1528639301	Station	false	work:vim:/home/frew/code/unproductive


filter filters the data before rendering it. The following flags are built in:

  • --time
  • --ssid
  • --vpn

So you might be run the following command to see the past week of data when you were working from home:

$ < ~/activity-log.txt filter --time 7d --ssid Station --vpn

There is also a standalone filter tool called measurements. Where the --time flag from filter allows you to limit to only the past 1h of data, measurements just takes the last 1h of data, no matter when it occurred. So for example, if your laptop was asleep for the past hour and you turned it on, filter --time 1h would return almost no data. measurements 1h would return a full hour, but it would have a gap.


retitle is responsible for taking the raw titles and producing some form of structure. The default retitle simply turns each title into it's own top level item, but far more nuance can be expressed. My custom retitle can be seen here.

If you are writing your own retitle you simply need to produce JSON lists expressing your heirarchy. So let's say you wanted to split all of your time by whether it was "Work" or "Play" and then have categories within those; you might write code like this:


use JSON::XS 'encode_json';

while (<STDIN>) {

   my @v;

   if (m/fogbugz/) {
      @v = (qw(Work fogbugz));
   } elsif (m/Issues - (frioux/dotfiles|frioux/unproductive) - Mozilla Firefox) {
      @v = ('Play, 'Github Issues', $1);
   } else {
      @v = ($_)

   print encode_json(\@v) . "\n";

Of course it can be any language you like.


Finally, the report tool reads the JSON and sums and outputs the values in a fashion that should be reminiscent of du(1). Here's an example bit of output, including all the tools discussed:

$ < ~/activity-log.txt filter --time 1h |
   retitle |
   report -show-percents |
   sort -n
1       (0%)    Coffee/Writing
1       (0%)    Email
1       (0%)    Email/ZR
1       (0%)    Email/ZR/mutt
2       (0%)    Chat/IRC
3       (0%)    Coffee/Reading
4       (0%)    Chat/Play/Discord
4       (0%)    Chat/Play/Discord/general
4       (0%)    Coffee
14      (0%)    Strategy/Notes/Reading
19      (1%)    Chrome
31      (2%)    Chat/Play
33      (2%)    Chat
50      (3%)    Strategy/Notes/Writing
64      (4%)    Strategy
64      (4%)    Strategy/Notes
557     (38%)   Firefox
768     (53%)   Fun
768     (53%)   Fun/Project
768     (53%)   Fun/Project/Unproductive
1446    (100%)


For this to work well you need to set titles of your windows. This mostly means that terminal apps need to be configured a little. I use tmux, so I needed to put the following two settings in my .tmux.conf:

set -g set-titles on
set -g set-titles-string "#{session_name}:#{window_name}:#{pane_current_path}"

The first line tells tmux to set the title; the second changes the format to something more useful than the default.

I also run terminator directly without tmux underneath. In that case it is worth directly setting the title. Here's how I do that:

$ terminator -e "vim -S $session" -T "vim:$session"

Other terminals will have similar settings and techniques.


Measure your personal productivity and report on it







No releases published


No packages published