--- /dev/null
+#!/bin/sh
+set -eu
+
+CODEDIR=~/code/taskwarrior
+
+[ -d $CODEDIR/tasklib ] && export PYTHONPATH=$CODEDIR/tasklib
+
+for dir in $CODEDIR/taskwarrior/src /usr/bin; do
+ if [ -x $dir/task ]; then
+ export PATH="$dir:$PATH"
+ exec task "$@"
+ fi
+
+done
+
+echo >&2 E: task binary not found.
--- /dev/null
+#!/bin/sh
+set -eu
+
+if [ $# != 2 ]; then
+ echo >&2 E: Need exactly two arguments.
+ return 1
+fi
+
+TSK="$1"
+LINK="$2"
+
+case "$LINK" in
+ (*@*|"<*>")
+ LINK="${LINK%>}"; LINK="${LINK#<}"
+ echo >&2 I: "Adding MsgID <$LINK> to task $TSK"
+ task "$TSK" mod data:msgid:"$LINK"
+ ;;
+
+ (*://*)
+ echo >&2 I: "Adding URL $LINK to task $TSK"
+ task "$TSK" mod data:url:"$LINK"
+ ;;
+
+ (*)
+ if [ -f "$LINK" ]; then
+
+ LINK="$(readlink -e "$LINK")"
+
+ case "$LINK" in
+ ($HOME/*) LINK="~${LINK#$HOME}";;
+ esac
+ echo >&2 I: "Adding file $LINK to task $TSK"
+ task "$TSK" mod data:file:"$LINK"
+
+ else
+ echo >&2 E: "Don't know how to handle link data $LINK"
+ return 1
+
+ fi
+ ;;
+esac
--- /dev/null
+#!/bin/sh
+set -eu
+
+if [ $# != 1 ]; then
+ echo >&2 E: Need exactly one argument.
+ return 1
+fi
+
+TSK="$1"
+
+DATA="$(task _get "$TSK".data)"
+DATATYPE="${DATA%%:*}"
+DATAPAYL="${DATA#*:}"
+
+case "$DATATYPE" in
+ ('')
+ echo >&2 I: No link data available for this task.
+ exec task "$TSK"
+ ;;
+
+ (msgid)
+ if command -v neomutt >/dev/null && command -v notmuch >/dev/null; then
+ exec neomutt -f "notmuch://$HOME/mail/?query=thread:{mid:$DATAPAYL}"
+
+ else
+ exec mutt -f =store -e "push '<limit>~(=i\"$DATAPAYL\")<enter><search>=i\"$DATAPAYL\"<enter>'"
+
+ fi
+ ;;
+
+ (url)
+ exec sensible-browser "$DATAPAYL"
+ ;;
+
+ (file)
+ if eval test -f "$DATAPAYL"; then
+ case "$(file --mime-type "$DATAPAYL")" in
+ (*": text/"*) eval exec sensible-editor "$DATAPAYL";;
+ (*) eval exec run-mailcap --action=view "$DATAPAYL";;
+ esac
+ fi
+ ;;
+
+ (*)
+ eval exec run-mailcap --action=view "$DATAPAYL"
+ ;;
+
+esac
--- /dev/null
+- /*.pem
+- /taskd-credentials.rc
--- /dev/null
+../taskpirate/on-add-pirate
\ No newline at end of file
--- /dev/null
+../taskpirate/on-modify-pirate
\ No newline at end of file
--- /dev/null
+Copyright 2015 Tomas Babej
+http://github.com/tbabej/taskwarrior-shift-all-recurrence-hook
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+Taskwarrior Shift All Recurrence Attributes Hook
+------------------------------------------------
+
+This is a hook for TaskWarrior (http://www.taskwarrior.org),
+which allow your recurrent tasks to inherit ``wait``, ``scheduled``
+and ``until`` attributes from the parent.
+
+Install
+-------
+
+Note: This hook has been rewritten to leverage taskpirate, for greater hook efficiency.
+Please see https://github.com/tbabej/taskpirate for instructions. Don't worry, it's straightforward.
+
+```
+git clone https://github.com/tbabej/taskwarrior-shift-all-recurrence-hook.git
+cp taskwarrior-shift-all-recurrence-hook/on-* ~/.task/hooks/
+```
+
+This hook leverages tasklib, so you need to install that too:
+
+```
+pip install tasklib
+```
+
+Use case
+--------
+
+Consider you have a periodic task, which is valid only for the certain day,
+e.g. on every Sunday you go running.
+
+```
+$ task add due:sunday recur:weekly
+```
+
+However, TaskWarrior will display the task right away, and you want to
+showing up only during the weekend, so that it does not distract your task
+list during the work week.
+
+If you try to add the recurrence again, this time with ``wait`` attribute,
+to hide the task until it is not relevant for you:
+
+```
+$ task add due:sunday recur:weekly wait:saturday
+```
+
+You will find out that TaskWarrior does not let tasks generated by this
+recurrence inherit the ``wait`` attribute, in the same manner as it does
+with the ``due`` attribute.
+
+This hook solves that.
--- /dev/null
+#!/usr/bin/python
+
+import sys
+import os
+from tasklib import TaskWarrior
+
+time_attributes = ('wait', 'scheduled')
+
+def is_new_local_recurrence_child_task(task):
+ # Do not affect tasks not spun by recurrence
+ if not task['parent']:
+ return False
+
+ # Newly created recurrence tasks actually have
+ # modified field copied from the parent, thus
+ # older than entry field (until their ID is generated)
+ if (task['modified'] - task['entry']).total_seconds() < 0:
+ return True
+
+tw = TaskWarrior(data_location=os.path.dirname(os.path.dirname(sys.argv[0])))
+tw.overrides.update(dict(recurrence="no", hooks="no"))
+
+def hook_shift_recurrence(task):
+ if is_new_local_recurrence_child_task(task):
+ parent = tw.tasks.get(uuid=task['parent']['uuid'])
+ parent_due_shift = task['due'] - parent['due']
+ for attr in time_attributes:
+ if parent[attr]:
+ task[attr] = parent[attr] + parent_due_shift
--- /dev/null
+include /usr/share/taskwarrior/light-256.theme
+data.location=~/.var/taskwarrior
+
+nag=
+recurrence.indicator=@
+recurrence=off
+hyphenate=off
+
+color.tag.TEMPLATE=gray16 on white
+color.alternate=
+color.tag.next=red
+
+include /usr/share/taskwarrior/holidays.de-DE.rc
+include /usr/share/taskwarrior/holidays.en-NZ.rc
+
+taskd.server=tasks.madduck.net:53589
+taskd.ca=/etc/ssl/certs/ca-certificates.crt
+taskd.certificate=~/.config/taskwarrior/client-cert.pem
+taskd.key=~/.config/taskwarrior/client-key.pem
+include ~/.config/taskwarrior/taskd-credentials.rc
+
+urgency.project.coefficient=0
+urgency.tags.coefficient=0
+urgency.annotations.coefficient=0
+
+due=1
+
+uda.data.type=string
+uda.data.label=Ext.data
+uda.data.indicator=→
+
+report.next.columns=id,data.indicator,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.relative,until.remaining,description,urgency
+report.next.labels=ID,X,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg
+report.next.sort=priority+,urgency-
+
+alias.call=execute task_call
+alias.attach=execute task_attach
--- /dev/null
+Copyright 2015-2017 Tomas Babej
+https://github.com/tbabej/taskpirate
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+Taskpirate
+----------
+
+Taskpirate is a pluggable system for TaskWarrior python hooks.
+
+Why?
+----
+
+Simpler hooks:
+
+ def hook_example(task):
+ task['description'] += "changed by a hook"
+
+The above is fully working example, no more boilerplate needed.
+
+Much faster execution time in case of multiple hooks (read the more details section).
+
+Install
+-------
+
+You'll need tasklib as a dependency:
+
+ pip install --user git+git://github.com/tbabej/tasklib@develop
+
+Then you need to drop ```on-add-pirate``` and ```on-modify-pirate``` into ~/.task/hooks/.
+
+After that, you can just clone any taskpirate-enabled hook as a subfolder into ~/.task/hooks/:
+
+ git clone https://github.com/tbabej/task.default-date-time ~/.task/hooks/default-date-time/
+
+How to write a taskpirate hook
+------------------------------
+
+In your hook's repository, any file matching ```pirate_add*.py``` will be searched for hooks in on-add event. In the same sense, any file matching ```pirate_mod*.py``` will be searched for hooks in on-modify event.
+
+Now, the pirate_add_example.py might look as follows:
+
+ def hook_example(task):
+ task['description'] += "changed by a hook"
+
+Any function in pirate_add_example that is called ```hook_*``` will be considered as a hook in on-add event. It will be passed the ```Task``` object corresponding to the current state of the task being added (as modified by the previous hooks).
+
+More details
+------------
+
+TaskWarrior hooks are intended to be simple, but they involve writing some boilerplate code (parsing/formatting json). To allow users to write dead simple code, they can leverage tasklib.
+
+Using tasklib simplifies things a lot, however, it's not a super-lightweight - usage of tasklib can slow down the hook by as much as 30-50ms (usual python hook can probably run in under 40ms), since it imports multiple libraries.
+
+This becomes a problem when user has multiple tasklib-based hooks, since the import time adds up.
+
+Also, note that taskpirate with arbitrary number of hooks will be most likely faster than 2-3 regular python hooks.
+
+Example hooks
+-------------
+
+You can look into my ```task.default-date-time``` or ```task.shift-recurrence``` hooks.
--- /dev/null
+#!/usr/bin/env python
+
+import glob
+import imp
+import os
+
+from tasklib import TaskWarrior, Task
+
+
+def find_hooks(file_prefix):
+ # Find all files in subdirectories whose names start with <file_prefix>
+ file_pattern = os.path.dirname(__file__) + '/*/' + file_prefix + "*.py"
+ module_paths = [f for f in glob.glob(file_pattern) if os.path.isfile(f)]
+ module_paths.sort()
+
+ # Gather all hooks in these files
+ hooks = []
+
+ for module_path in module_paths:
+ # Load the module
+ module_dir = os.path.dirname(module_path)
+ module_filename = os.path.basename(module_path)
+ module_name = 'pirate_{0}_{1}'.format(module_dir, module_filename)
+ module_name = module_name.replace('.', '_')
+ module = imp.load_source(module_name, module_path)
+
+ # Find all hook methods available
+ module_hooks = [
+ getattr(module, hook_name)
+ for hook_name in dir(module)
+ if hook_name.startswith('hook_')
+ ]
+
+ hooks += module_hooks
+
+ return hooks
+
+task = Task.from_input()
+
+for hook in find_hooks('pirate_add'):
+ hook(task)
+
+print(task.export_data())
--- /dev/null
+#!/usr/bin/env python
+
+import glob
+import imp
+import os
+
+from tasklib import TaskWarrior, Task
+
+
+def find_hooks(file_prefix):
+ # Find all files in subdirectories whose names start with <file_prefix>
+ file_pattern = os.path.dirname(__file__) + '/*/' + file_prefix + "*.py"
+ module_paths = [f for f in glob.glob(file_pattern) if os.path.isfile(f)]
+ module_paths.sort()
+
+ # Gather all hooks in these files
+ hooks = []
+
+ for module_path in module_paths:
+ # Load the module
+ module_dir = os.path.dirname(module_path)
+ module_filename = os.path.basename(module_path)
+ module_name = 'pirate_{0}_{1}'.format(module_dir, module_filename)
+ module_name = module_name.replace('.', '_')
+ module = imp.load_source(module_name, module_path)
+
+ # Find all hook methods available
+ module_hooks = [
+ getattr(module, hook_name)
+ for hook_name in dir(module)
+ if hook_name.startswith('hook_')
+ ]
+
+ hooks += module_hooks
+
+ return hooks
+
+task = Task.from_input()
+
+for hook in find_hooks('pirate_mod'):
+ hook(task)
+
+print(task.export_data())
--- /dev/null
+*/30 * * * * ( task sync && task next && task sync ) >/dev/null 2>&1
--- /dev/null
+*
+!/.bin/task
+!/.bin/task_attach
+!/.bin/task_call
+!/.config/taskwarrior/.gitignore
+!/.config/taskwarrior/hooks/on-add-pirate
+!/.config/taskwarrior/hooks/on-modify-pirate
+!/.config/taskwarrior/hooks/task.shift-recurrence/.gitignore
+!/.config/taskwarrior/hooks/task.shift-recurrence/LICENCE
+!/.config/taskwarrior/hooks/task.shift-recurrence/pirate_add_shift_recurrence.py
+!/.config/taskwarrior/hooks/task.shift-recurrence/README.md
+!/.config/taskwarrior/rc
+!/.config/taskwarrior/taskpirate/LICENCE
+!/.config/taskwarrior/taskpirate/on-add-pirate
+!/.config/taskwarrior/taskpirate/on-modify-pirate
+!/.config/taskwarrior/taskpirate/README.md
+!/.crontab.d/taskwarrior
+!/.gitignore.d/taskwarrior
+!/.taskrc
+!/.var/taskwarrior/.gitignore
+!/.var/taskwarrior/hooks
+!/.zsh/zshrc/parts.d/50-taskwarrior
--- /dev/null
+.config/taskwarrior/rc
\ No newline at end of file
--- /dev/null
+../../.config/taskwarrior/hooks
\ No newline at end of file
--- /dev/null
+#
+# taskwarrior shell integration
+#
+# Copyright © 2018 martin f. krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+# Source repository: http://git.madduck.net/v/etc/zsh.git
+#
+
+if whence task >/dev/null; then
+
+ function task_() {
+ local filter cmd args nofilter=0
+ cmd="$1"; shift
+
+ case "$cmd" in
+ (add|attach|call) nofilter=1;;
+ esac
+
+ for i in "$@"; do
+ case "$nofilter@$i" in
+ (0@<->) :;&
+ (0@/*/) filter="${filter:+$filter }$i";;
+ (*) args="${args:+$args }$i";;
+ esac
+ done
+ eval task $filter $cmd $args
+ }
+
+ function task_postpone() {
+ local args
+ for i in scheduled due until wait; do
+ if [[ -n "$(eval task _get "$1"."$i")" ]]; then
+ args="${args:+$args }${i}:${i}+$2"
+ fi
+ done
+ eval task "$1" modify $args
+ }
+
+ alias t\?='alias -rm t t[-+a-z] | sed -r "s,_ ?, ,"'
+ alias ta='task_ add'
+ alias tc='task_ call'
+ alias th='task_ attach'
+ alias tl='task_ all'
+ alias ty='task_ next rc.recurrence=on >/dev/null && task_ sync'
+ alias td='task_ done'
+ alias te='task_ edit'
+ alias tu='task_ undo'
+ alias tm='task_ modify'
+ alias ts='task_ start'
+ alias tw='task_ waiting'
+ alias tp='task_postpone'
+ alias t+='task_ annotate'
+ alias t='task_ ""'
+
+ run_at_most_every 5m \
+ t 2>/dev/null || :
+fi
+
+# vim:ft=zsh