From: martin f. krafft Date: Mon, 18 Nov 2019 03:57:53 +0000 (+1300) Subject: Add 'code/taskwarrior/tasklib/' from commit '9a7dac4599f198c52f268ae8bcc0267743ef934f' X-Git-Url: https://git.madduck.net/etc/taskwarrior.git/commitdiff_plain/8078ceed9b50dad2d319a74e2d3014b6ff649b47?hp=9a7dac4599f198c52f268ae8bcc0267743ef934f Add 'code/taskwarrior/tasklib/' from commit '9a7dac4599f198c52f268ae8bcc0267743ef934f' git-subtree-dir: code/taskwarrior/tasklib git-subtree-mainline: 31d9529fe14bb21ff87c6e0030890fb32df15834 git-subtree-split: 9a7dac4599f198c52f268ae8bcc0267743ef934f --- diff --git a/.bin/task b/.bin/task new file mode 100755 index 0000000..8ce793d --- /dev/null +++ b/.bin/task @@ -0,0 +1,16 @@ +#!/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. diff --git a/.bin/task_attach b/.bin/task_attach new file mode 100755 index 0000000..7df89a6 --- /dev/null +++ b/.bin/task_attach @@ -0,0 +1,41 @@ +#!/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 diff --git a/.bin/task_call b/.bin/task_call new file mode 100755 index 0000000..71c4f76 --- /dev/null +++ b/.bin/task_call @@ -0,0 +1,48 @@ +#!/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 '~(=i\"$DATAPAYL\")=i\"$DATAPAYL\"'" + + 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 diff --git a/.config/taskwarrior/.gitignore b/.config/taskwarrior/.gitignore new file mode 100644 index 0000000..645470a --- /dev/null +++ b/.config/taskwarrior/.gitignore @@ -0,0 +1,2 @@ +- /*.pem +- /taskd-credentials.rc diff --git a/.config/taskwarrior/hooks/on-add-pirate b/.config/taskwarrior/hooks/on-add-pirate new file mode 120000 index 0000000..88ed7b8 --- /dev/null +++ b/.config/taskwarrior/hooks/on-add-pirate @@ -0,0 +1 @@ +../taskpirate/on-add-pirate \ No newline at end of file diff --git a/.config/taskwarrior/hooks/on-modify-pirate b/.config/taskwarrior/hooks/on-modify-pirate new file mode 120000 index 0000000..006bb1e --- /dev/null +++ b/.config/taskwarrior/hooks/on-modify-pirate @@ -0,0 +1 @@ +../taskpirate/on-modify-pirate \ No newline at end of file diff --git a/.config/taskwarrior/hooks/task.shift-recurrence/.gitignore b/.config/taskwarrior/hooks/task.shift-recurrence/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.config/taskwarrior/hooks/task.shift-recurrence/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/.config/taskwarrior/hooks/task.shift-recurrence/LICENCE b/.config/taskwarrior/hooks/task.shift-recurrence/LICENCE new file mode 100644 index 0000000..f90622c --- /dev/null +++ b/.config/taskwarrior/hooks/task.shift-recurrence/LICENCE @@ -0,0 +1,21 @@ +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. diff --git a/.config/taskwarrior/hooks/task.shift-recurrence/README.md b/.config/taskwarrior/hooks/task.shift-recurrence/README.md new file mode 100644 index 0000000..58792c0 --- /dev/null +++ b/.config/taskwarrior/hooks/task.shift-recurrence/README.md @@ -0,0 +1,50 @@ +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. diff --git a/.config/taskwarrior/hooks/task.shift-recurrence/pirate_add_shift_recurrence.py b/.config/taskwarrior/hooks/task.shift-recurrence/pirate_add_shift_recurrence.py new file mode 100644 index 0000000..0266fc5 --- /dev/null +++ b/.config/taskwarrior/hooks/task.shift-recurrence/pirate_add_shift_recurrence.py @@ -0,0 +1,29 @@ +#!/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 diff --git a/.config/taskwarrior/rc b/.config/taskwarrior/rc new file mode 100644 index 0000000..f207d9a --- /dev/null +++ b/.config/taskwarrior/rc @@ -0,0 +1,37 @@ +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 diff --git a/.config/taskwarrior/taskpirate/LICENCE b/.config/taskwarrior/taskpirate/LICENCE new file mode 100644 index 0000000..e3e582e --- /dev/null +++ b/.config/taskwarrior/taskpirate/LICENCE @@ -0,0 +1,21 @@ +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. diff --git a/.config/taskwarrior/taskpirate/README.md b/.config/taskwarrior/taskpirate/README.md new file mode 100644 index 0000000..4d49a00 --- /dev/null +++ b/.config/taskwarrior/taskpirate/README.md @@ -0,0 +1,57 @@ +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. diff --git a/.config/taskwarrior/taskpirate/on-add-pirate b/.config/taskwarrior/taskpirate/on-add-pirate new file mode 100755 index 0000000..9fae1c4 --- /dev/null +++ b/.config/taskwarrior/taskpirate/on-add-pirate @@ -0,0 +1,43 @@ +#!/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_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()) diff --git a/.config/taskwarrior/taskpirate/on-modify-pirate b/.config/taskwarrior/taskpirate/on-modify-pirate new file mode 100755 index 0000000..b7b4986 --- /dev/null +++ b/.config/taskwarrior/taskpirate/on-modify-pirate @@ -0,0 +1,43 @@ +#!/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_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()) diff --git a/.crontab.d/taskwarrior b/.crontab.d/taskwarrior new file mode 100644 index 0000000..d26dae7 --- /dev/null +++ b/.crontab.d/taskwarrior @@ -0,0 +1 @@ +*/30 * * * * ( task sync && task next && task sync ) >/dev/null 2>&1 diff --git a/.gitignore.d/taskwarrior b/.gitignore.d/taskwarrior new file mode 100644 index 0000000..ff65ee9 --- /dev/null +++ b/.gitignore.d/taskwarrior @@ -0,0 +1,22 @@ +* +!/.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 diff --git a/.taskrc b/.taskrc new file mode 120000 index 0000000..3113449 --- /dev/null +++ b/.taskrc @@ -0,0 +1 @@ +.config/taskwarrior/rc \ No newline at end of file diff --git a/.var/taskwarrior/.gitignore b/.var/taskwarrior/.gitignore new file mode 100644 index 0000000..216f0fe --- /dev/null +++ b/.var/taskwarrior/.gitignore @@ -0,0 +1 @@ +- /*.data diff --git a/.var/taskwarrior/hooks b/.var/taskwarrior/hooks new file mode 120000 index 0000000..729b1ca --- /dev/null +++ b/.var/taskwarrior/hooks @@ -0,0 +1 @@ +../../.config/taskwarrior/hooks \ No newline at end of file diff --git a/.zsh/zshrc/parts.d/50-taskwarrior b/.zsh/zshrc/parts.d/50-taskwarrior new file mode 100644 index 0000000..2ca20b9 --- /dev/null +++ b/.zsh/zshrc/parts.d/50-taskwarrior @@ -0,0 +1,60 @@ +# +# taskwarrior shell integration +# +# Copyright © 2018 martin f. krafft +# 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 diff --git a/.coveragerc b/code/taskwarrior/tasklib/.coveragerc similarity index 100% rename from .coveragerc rename to code/taskwarrior/tasklib/.coveragerc diff --git a/.gitignore b/code/taskwarrior/tasklib/.gitignore similarity index 100% rename from .gitignore rename to code/taskwarrior/tasklib/.gitignore diff --git a/.travis.yml b/code/taskwarrior/tasklib/.travis.yml similarity index 100% rename from .travis.yml rename to code/taskwarrior/tasklib/.travis.yml diff --git a/AUTHORS b/code/taskwarrior/tasklib/AUTHORS similarity index 100% rename from AUTHORS rename to code/taskwarrior/tasklib/AUTHORS diff --git a/LICENSE b/code/taskwarrior/tasklib/LICENSE similarity index 100% rename from LICENSE rename to code/taskwarrior/tasklib/LICENSE diff --git a/MANIFEST.in b/code/taskwarrior/tasklib/MANIFEST.in similarity index 100% rename from MANIFEST.in rename to code/taskwarrior/tasklib/MANIFEST.in diff --git a/README.rst b/code/taskwarrior/tasklib/README.rst similarity index 100% rename from README.rst rename to code/taskwarrior/tasklib/README.rst diff --git a/docs/Makefile b/code/taskwarrior/tasklib/docs/Makefile similarity index 100% rename from docs/Makefile rename to code/taskwarrior/tasklib/docs/Makefile diff --git a/docs/conf.py b/code/taskwarrior/tasklib/docs/conf.py similarity index 100% rename from docs/conf.py rename to code/taskwarrior/tasklib/docs/conf.py diff --git a/docs/index.rst b/code/taskwarrior/tasklib/docs/index.rst similarity index 100% rename from docs/index.rst rename to code/taskwarrior/tasklib/docs/index.rst diff --git a/setup.py b/code/taskwarrior/tasklib/setup.py similarity index 100% rename from setup.py rename to code/taskwarrior/tasklib/setup.py diff --git a/tasklib/__init__.py b/code/taskwarrior/tasklib/tasklib/__init__.py similarity index 100% rename from tasklib/__init__.py rename to code/taskwarrior/tasklib/tasklib/__init__.py diff --git a/tasklib/backends.py b/code/taskwarrior/tasklib/tasklib/backends.py similarity index 100% rename from tasklib/backends.py rename to code/taskwarrior/tasklib/tasklib/backends.py diff --git a/tasklib/filters.py b/code/taskwarrior/tasklib/tasklib/filters.py similarity index 100% rename from tasklib/filters.py rename to code/taskwarrior/tasklib/tasklib/filters.py diff --git a/tasklib/lazy.py b/code/taskwarrior/tasklib/tasklib/lazy.py similarity index 100% rename from tasklib/lazy.py rename to code/taskwarrior/tasklib/tasklib/lazy.py diff --git a/tasklib/serializing.py b/code/taskwarrior/tasklib/tasklib/serializing.py similarity index 100% rename from tasklib/serializing.py rename to code/taskwarrior/tasklib/tasklib/serializing.py diff --git a/tasklib/task.py b/code/taskwarrior/tasklib/tasklib/task.py similarity index 100% rename from tasklib/task.py rename to code/taskwarrior/tasklib/tasklib/task.py diff --git a/tasklib/tests.py b/code/taskwarrior/tasklib/tasklib/tests.py similarity index 100% rename from tasklib/tests.py rename to code/taskwarrior/tasklib/tasklib/tests.py