--- /dev/null
+Copyright (c) 2013, Rob Golding. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of Rob Golding, nor the names of its contributors may
+      be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
--- /dev/null
+from setuptools import setup, find_packages
+
+setup(
+    name='tasklib',
+    version='0.1',
+    description='Python Task Warrior library',
+    long_description=open('README.md').read(),
+    author='Rob Golding',
+    author_email='rob@robgolding.com',
+    license='BSD',
+    url='https://github.com/robgolding63/tasklib',
+    download_url='https://github.com/robgolding63/tasklib/downloads',
+    packages=find_packages(),
+    include_package_data=True,
+    classifiers=[
+        'Development Status :: 3 - Alpha',
+        'License :: OSI Approved :: BSD License',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+        'Programming Language :: Python',
+        'Intended Audience :: Developers',
+    ],
+)
 
--- /dev/null
+import json
+import os
+import subprocess
+import tempfile
+import uuid
+
+
+PENDING = 'pending'
+
+
+class TaskWarriorException(Exception):
+    pass
+
+
+class Task(object):
+
+    def __init__(self, warrior, data={}):
+        self.warrior = warrior
+        self._data = data
+
+    def __getitem__(self, key):
+        return self._data.get(key)
+
+    def __setitem__(self, key, val):
+        self._data[key] = val
+
+    def __unicode__(self):
+        return self._data.get('description')
+
+    def regenerate_uuid(self):
+        self['uuid'] = str(uuid.uuid4())
+
+    def delete(self):
+        self.warrior.delete_task(self['uuid'])
+
+    def done(self):
+        self.warrior.complete_task(self['uuid'])
+
+    def save(self, delete_first=True):
+        if self['uuid'] and delete_first:
+            self.delete()
+        if not self['uuid'] or delete_first:
+            self.regenerate_uuid()
+        self.warrior.import_tasks([self._data])
+
+    __repr__ = __unicode__
+
+
+class TaskWarrior(object):
+
+    def __init__(self, data_location='~/.task', create=True):
+        if not os.path.exists(data_location):
+            os.makedirs(data_location)
+        self.config = {
+            'data.location': os.path.expanduser(data_location),
+        }
+
+    def _generate_command(self, command):
+        args = ['task', 'rc:/']
+        for item in self.config.items():
+            args.append('rc.{0}={1}'.format(*item))
+        args.append(command)
+        return ' '.join(args)
+
+    def _execute(self, command):
+        p = subprocess.Popen(self._generate_command(command), shell=True,
+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        if p.returncode:
+            raise TaskWarriorException(stderr.strip())
+        return stdout.strip().split('\n')
+
+    def get_tasks(self, project=None, status=PENDING):
+        command = 'export status:{0}'.format(status)
+        if project is not None:
+            command += ' project:{0}'.format(project)
+        tasks = []
+        for line in self._execute(command):
+            if line:
+                tasks.append(Task(self, json.loads(line.strip(','))))
+        return tasks
+
+    def get_task(self, task_id):
+        command = '{0} export'.format(task_id)
+        return Task(self, json.loads(self._execute(command)[0]))
+
+    def add_task(self, description, project=None):
+        args = ['add', description]
+        if project is not None:
+            args.append('project:{0}'.format(project))
+        self._execute(' '.join(args))
+
+    def delete_task(self, task_id):
+        self._execute('{0} rc.confirmation:no delete'.format(task_id))
+
+    def complete_task(self, task_id):
+        self._execute('{0} done'.format(task_id))
+
+    def import_tasks(self, tasks):
+        fd, path = tempfile.mkstemp()
+        with open(path, 'w') as f:
+            f.write(json.dumps(tasks))
+        self._execute('import {0}'.format(path))