diff --git a/checklist/README.md b/checklist/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e5ba94cd2864ccc13c932d86cfde7cfed268cbeb
--- /dev/null
+++ b/checklist/README.md
@@ -0,0 +1,16 @@
+#Checklist Extension
+
+###Overview
+
+This extension provides checklist support while reviewing requests to help users
+list all the aspects of the code they need to check. Checklists are created
+upon viewing a request; if one already exists for the review request and user
+pair, it is fetched from the back-end.
+
+###Requirements
+
+This extension requires Review Board Version 2.0.
+
+###Status
+
+This extension is in its alpha stage.
\ No newline at end of file
diff --git a/checklist/checklist/checklistResource.py b/checklist/checklist/checklistResource.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c1c592f144673c7f55bcec0f138eed9f1c25708
--- /dev/null
+++ b/checklist/checklist/checklistResource.py
@@ -0,0 +1,116 @@
+from django.contrib.auth.models import User
+from djblets.webapi.decorators import webapi_request_fields
+from reviewboard.webapi.base import WebAPIResource
+from reviewboard.webapi.decorators import (webapi_check_local_site,
+                                             webapi_login_required)
+from reviewboard.webapi.resources import resources
+
+from checklist.models import ReviewChecklist
+
+
+class ChecklistResource(WebAPIResource):
+    name = 'checklist'
+    model = ReviewChecklist
+    uri_object_key = 'checklist_id'
+    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
+
+    fields = {
+        'id': {
+            'type': int,
+            'description': 'The numeric ID of the checklist review.'
+        },
+        'items_counter': {
+            'type': int,
+            'description': 'Number of items added in the checklist.'
+        },
+        'checklist_items': {
+            'type': str,
+            'description': 'Items in checklist.'
+        }
+    }
+
+    def has_access_permissions(self, request, checklist, *args, **kwags):
+        return checklist.user == request.user
+
+    def has_delete_permissions(self, request, checklist, *args, **kwags):
+        return checklist.user == request.user
+
+    @webapi_request_fields(
+        required={
+            'user_id': {
+                'type': int,
+                'description': 'The id of the user creating the checklist.'
+            },
+            'review_request_id': {
+                'type': int,
+                'description': 'The id of the review request.'
+            }
+        }
+    )
+    @webapi_login_required
+    @webapi_check_local_site
+    def create(self, request, user_id=None, *args, **kwargs):
+        """Creates a new checklist."""
+        user = User.objects.get(pk=user_id)
+        review_request = resources.review_request.get_object(request,
+                                                             args, **kwargs)
+
+        new_checklist, _ = ReviewChecklist.objects.get_or_create(
+            user=user,
+            review_request=review_request)
+
+        return 201, {self.item_result_key: new_checklist}
+
+    @webapi_login_required
+    @webapi_request_fields(
+        optional={
+            'user_id': {
+                'type': int,
+                'description': 'The id of the user creating the checklist.'
+            },
+            'review_request_id': {
+                'type': int,
+                'description': 'The id of the review request.'
+            },
+            'checklist_item_id': {
+                'type': int,
+                'description': 'The id of the checklist item to edit or '
+                               'delete.'
+            },
+            'item_description': {
+                'type': str,
+                'description': 'The description of the checklist item.'
+            },
+            'toggle': {
+                'type': str,
+                'description': 'If present, status of the item should '
+                               'be toggled.'
+            }
+        }
+    )
+    @webapi_check_local_site
+    def update(self, request, checklist_item_id=None, user_id=None,
+               item_description=None, toggle=None, *args, **kwargs):
+        """Add, edit, delete or update the status of an item on the list"""
+        user = User.objects.get(pk=user_id)
+        review_request = resources.review_request.get_object(request, args,
+                                                             **kwargs)
+        checklist = ReviewChecklist.objects.filter(
+            user=user, review_request=review_request)
+        checklist = checklist[0]
+
+        # If there is no checklist_item_id, we are adding a new item
+        if checklist_item_id is None and item_description is not None:
+            checklist.add_item(item_description)
+        elif checklist_item_id is not None and item_description is not None:
+            checklist.edit_item_desc(checklist_item_id, item_description)
+        elif (checklist_item_id is not None and item_description is None
+              and toggle is None):
+            checklist.delete_item(checklist_item_id)
+        # If toggle is present, we toggle the status of the checklist item
+        elif (checklist_item_id is not None) and (toggle is not None):
+            checklist.toggle_item_status(checklist_item_id)
+
+        return 200, {self.item_result_key: checklist}
+
+checklist_resource = ChecklistResource()
diff --git a/checklist/checklist/extension.py b/checklist/checklist/extension.py
new file mode 100644
index 0000000000000000000000000000000000000000..49d400237bea45223e40e085f1c6c285067a7eab
--- /dev/null
+++ b/checklist/checklist/extension.py
@@ -0,0 +1,43 @@
+# Checklist Extension for Review Board.
+from djblets.webapi.resources import (register_resource_for_model,
+                                        unregister_resource_for_model)
+from reviewboard.extensions.base import Extension
+from reviewboard.extensions.hooks import TemplateHook
+
+from checklist.checklistResource import checklist_resource
+from checklist.models import ReviewChecklist
+
+
+class Checklist(Extension):
+
+    metadata = {
+        'Name': 'Review Checklist',
+    }
+
+    js_model_class = 'Checklist.Extension'
+
+    css_bundles = {
+        'css_default': {
+            'source_filenames': ['css/index.css']
+        }
+    }
+
+    js_bundles = {
+        'js_default': {
+            'source_filenames': ['js/models/checklist.js',
+                                 'js/models/checklistAPI.js',
+                                 'js/views/checklistView.js']
+        }
+    }
+
+    resources = [checklist_resource]
+
+    def __init__(self, *args, **kwargs):
+        super(Checklist, self).__init__(*args, **kwargs)
+        register_resource_for_model(ReviewChecklist, checklist_resource)
+        TemplateHook(self, "base-scripts-post", "checklist/template.html",
+                     apply_to=["view_diff", "view_diff_revisions",
+                               "file_attachment"])
+
+    def shutdown(self, *args, **kwargs):
+        unregister_resource_for_model(ReviewChecklist)
diff --git a/checklist/checklist/models.py b/checklist/checklist/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..866f55d5108082fc17fb8f57f098170c61c02a24
--- /dev/null
+++ b/checklist/checklist/models.py
@@ -0,0 +1,49 @@
+from django.contrib.auth.models import User
+from django.db import models
+from djblets.util.fields import JSONField
+from reviewboard.reviews.models import ReviewRequest
+
+
+class ReviewChecklist(models.Model):
+    """A checklist is a list of items to keep track of during a review. """
+
+    # The user making the review.
+    user = models.ForeignKey(User)
+
+    # The review request the user is reviewing.
+    review_request = models.ForeignKey(ReviewRequest)
+
+    # Keeps track of the number of items *added* in the
+    # checklist, not the current number of items in it. This
+    # allows us to give unique IDs to new items.
+    items_counter = models.IntegerField(default=0)
+
+    # A JSON of all the items in the checklist. Key = ID.
+    # Value = { id, finished status, description }
+    checklist_items = JSONField()
+
+    def add_item(self, item_description):
+        self.items_counter += 1
+        self.checklist_items[self.items_counter] = {
+            'id': self.items_counter,
+            'finished': False,
+            'description': item_description
+        }
+        self.save()
+
+    def edit_item_desc(self, itemID, item_description):
+        if (str(itemID) in self.checklist_items):
+            itemDict = self.checklist_items.get(str(itemID))
+            itemDict['description'] = item_description
+            self.save()
+
+    def toggle_item_status(self, itemID):
+        if (str(itemID) in self.checklist_items):
+            itemDict = self.checklist_items.get(str(itemID))
+            itemDict['finished'] = not itemDict['finished']
+            self.save()
+
+    def delete_item(self, itemID):
+        if (str(itemID) in self.checklist_items):
+            self.checklist_items.pop(str(itemID))
+            self.save()
diff --git a/checklist/checklist/static/css/index.css b/checklist/checklist/static/css/index.css
new file mode 100644
index 0000000000000000000000000000000000000000..de2eb31e48bcbc9decf839581ef72039dff33e85
--- /dev/null
+++ b/checklist/checklist/static/css/index.css
@@ -0,0 +1,76 @@
+/* Entire container for checklist. To avoid Backbone.js zombies, the actual
+ * view is stored in a div inside #checklist. */
+#checklist div {
+  margin: 10px;
+  background-color: #333333;
+  padding: 10px;
+  width: 20%;
+  max-height: 75%;
+  position: fixed;
+  z-index: 1000;
+  right: 0px;
+  top: 50px;
+  opacity: 0.75;
+}
+#checklist div p {
+  color: white;
+  margin-top: 0;
+  margin-bottom: -15px;
+  float: left;
+  font-size: 14px;
+}
+#checklist div ul {
+  list-style-type: none;
+}
+input[name="checklist_itemDesc"] {
+  width: 75%;
+  margin-top: 20px;
+}
+/* The minimize and maximize buttons */
+.checklist_min {
+  background-image: url('../images/min.png');
+}
+.checklist_max {
+  background-image: url('../images/max.png');
+}
+#checklist_toggle_size {
+  width: 16px;
+  height: 16px;
+  float: right;
+  margin-top: -2px;
+  margin-bottom: 2px;
+  margin-left: 2px;
+  clear: both;
+}
+/* Exit and delete checklist */
+#checklist_exit {
+  background-image: url('../images/exit.png');
+  width: 16px;
+  height: 16px;
+  float: right;
+  margin-top: -2px;
+  margin-bottom: 2px;
+  clear: left;
+}
+input[name="checklist_checkbox"] {
+  margin-right: 10px;
+  margin-left: 2px;
+}
+.checklist_item, .checklist_del_item {
+  font-size: 14px;
+  font-weight: bold;
+  color: #ffea38;
+  margin-left: -40px;
+}
+.checklist_item a {
+  font-size: 14px;
+  font-weight: bold;
+  color: #ffea38;
+  text-decoration: none;
+}
+.checklist_del_item {
+  background-image: url('../images/cross.png');
+  width: 16px;
+  height: 15px;
+  float: right;
+}
diff --git a/checklist/checklist/static/css/style.less b/checklist/checklist/static/css/style.less
new file mode 100644
index 0000000000000000000000000000000000000000..9817d9c0af016030f558d825fda65820a9890a02
--- /dev/null
+++ b/checklist/checklist/static/css/style.less
@@ -0,0 +1,94 @@
+@dark-grey: #333333;
+@cross-image: '../images/cross.png';
+@min-image: '../images/min.png';
+@max-image: '../images/max.png';
+@exit-image: '../images/exit.png';
+
+/* Entire container for checklist. To avoid Backbone.js zombies, the actual
+ * view is stored in a div inside #checklist. */
+#checklist div {
+  margin: 10px;
+  background-color: @dark-grey;
+  padding: 10px;
+  width: 20%;
+  max-height: 75%;
+  position: fixed;
+  z-index: 1000;
+  right: 0px;
+  top: 50px;
+  opacity: 0.75;
+}
+
+#checklist div p {
+  color: white;
+  margin-top: 0;
+  margin-bottom: -15px;
+  float: left;
+  font-size: 14px;
+}
+
+#checklist div ul {
+  list-style-type: none;
+}
+
+input[name="checklist_itemDesc"] {
+  width: 75%;
+  margin-top: 20px;
+}
+
+/* The minimize and maximize buttons */
+.checklist_min {
+  background-image: url(@min-image);
+}
+
+.checklist_max {
+  background-image: url(@max-image);
+}
+
+#checklist_toggle_size {
+  width: 16px;
+  height: 16px;
+  float: right;
+  margin-top: -2px;
+  margin-bottom: 2px;
+  margin-left: 2px;
+  clear: both;
+}
+
+/* Exit and delete checklist */
+#checklist_exit {
+  background-image: url(@exit-image);
+  width: 16px;
+  height: 16px;
+  float: right;
+  margin-top: -2px;
+  margin-bottom: 2px;
+  clear: left;
+}
+
+
+input[name="checklist_checkbox"] {
+  margin-right: 10px;
+  margin-left: 2px;
+}
+
+.checklist_item, .checklist_del_item {
+  font-size: 14px;
+  font-weight: bold;
+  color: #ffea38;
+  margin-left: -40px;
+}
+
+.checklist_item a {
+  font-size: 14px;
+  font-weight: bold;
+  color: #ffea38;
+  text-decoration: none;
+}
+
+.checklist_del_item {
+  background-image: url(@cross-image);
+  width: 16px;
+  height: 15px;
+  float: right;
+}
diff --git a/checklist/checklist/static/images/cross.png b/checklist/checklist/static/images/cross.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a51af98130cd1c1dcbf5cae297d1b03a126256c
Binary files /dev/null and b/checklist/checklist/static/images/cross.png differ
diff --git a/checklist/checklist/static/images/exit.png b/checklist/checklist/static/images/exit.png
new file mode 100644
index 0000000000000000000000000000000000000000..e36d807b12d937ccf286331ddbb4816f974d150d
Binary files /dev/null and b/checklist/checklist/static/images/exit.png differ
diff --git a/checklist/checklist/static/images/max.png b/checklist/checklist/static/images/max.png
new file mode 100644
index 0000000000000000000000000000000000000000..13e2903b2e90bfc9c99059ab84124f4788a24f83
Binary files /dev/null and b/checklist/checklist/static/images/max.png differ
diff --git a/checklist/checklist/static/images/min.png b/checklist/checklist/static/images/min.png
new file mode 100644
index 0000000000000000000000000000000000000000..04fa613d7731b43161803e2555d9bf077c53d10c
Binary files /dev/null and b/checklist/checklist/static/images/min.png differ
diff --git a/checklist/checklist/static/images/plus.png b/checklist/checklist/static/images/plus.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3c40885ef0ea279f6551d39cd648e70de65c421
Binary files /dev/null and b/checklist/checklist/static/images/plus.png differ
diff --git a/checklist/checklist/static/js/models/checklist.js b/checklist/checklist/static/js/models/checklist.js
new file mode 100644
index 0000000000000000000000000000000000000000..88fcbba047d3e1561d540f9c5443f604378bae97
--- /dev/null
+++ b/checklist/checklist/static/js/models/checklist.js
@@ -0,0 +1,15 @@
+/* A checklist is a collection of checklist items.
+ * A checklistItem is an individual item in a checklist. */
+var Checklist = {};
+
+Checklist.ChecklistItem = Backbone.Model.extend({
+    defaults: {
+        description: '',
+        id: '',
+        finished: false
+    }
+});
+
+Checklist.Checklist = Backbone.Collection.extend({
+    model: Checklist.ChecklistItem
+});
\ No newline at end of file
diff --git a/checklist/checklist/static/js/models/checklistAPI.js b/checklist/checklist/static/js/models/checklistAPI.js
new file mode 100644
index 0000000000000000000000000000000000000000..e57f103317d61dd01b28dcfbb43d5771c14040de
--- /dev/null
+++ b/checklist/checklist/static/js/models/checklistAPI.js
@@ -0,0 +1,47 @@
+/* An API for the Checklist Extension. */
+Checklist.ChecklistAPI = RB.BaseResource.extend({
+    rspNamespace: 'checklist',
+    checklist_items: null,
+
+    defaults: {
+        /* The id of the user interacting with the Checklist. */
+        user_id: null,
+
+        /* The id of the review request the checklist is for. */
+        review_request_id: null,
+
+        /* The item in the checklist being modified / deleted. */
+        checklist_item_id: null,
+
+        /* The description for the indicated checklist item. */
+        item_description: null,
+
+        /* Indicates whether the checklist item's status should be toggled. */
+        toggle: false
+    },
+
+    url: function () {
+        var baseURL = SITE_ROOT + 'api/extensions/checklist.extension.Checklist/checklists/';
+        return this.isNew() ? baseURL : (baseURL + this.id + '/');
+    },
+
+    toJSON: function () {
+        return {
+            user_id: this.get('user_id') || undefined,
+            review_request_id: this.get('review_request_id') || undefined,
+            checklist_item_id: this.get('checklist_item_id') || undefined,
+            item_description: this.get('item_description') || undefined,
+            toggle: this.get('toggle') || undefined
+        };
+    },
+
+    parseResourceData: function(rsp) {
+        this.checklist_items = rsp.checklist_items;
+        return {
+            id: rsp.id,
+            links: rsp.links,
+            checklist_item_id: rsp.items_counter,
+            loaded: true
+        };
+    }
+});
\ No newline at end of file
diff --git a/checklist/checklist/static/js/views/checklistView.js b/checklist/checklist/static/js/views/checklistView.js
new file mode 100644
index 0000000000000000000000000000000000000000..d764caba555d907e34ab56b0393897fc585b45e2
--- /dev/null
+++ b/checklist/checklist/static/js/views/checklistView.js
@@ -0,0 +1,321 @@
+/* A view for each item on the checklist. */
+Checklist.ChecklistItemView = Backbone.View.extend({
+    tagName: 'li',
+    className: 'checklist_item',
+
+    /* We pass in the checklistAPI as an argument so that each item has
+     * access to the API also. */
+
+    events: {
+        'click input[name="checklist_checkbox"]': 'toggleStatus',
+        'click a.checklist_edit_desc': 'editItemDesc',
+        'keydown input[name="checklist_editDesc"]': 'editItemDescDB'
+    },
+
+    initialize: function (options) {
+        _.bindAll(this, 'render', 'toggleStatus', 'toggleStatusDB');
+        this.checklistAPI = options.checklistAPI;
+        this.listenTo(this.model, 'change', this.render);
+    },
+
+    /* This tempate is for the regular look of an item. */
+    template: _.template([
+        '<input type="checkbox" name="checklist_checkbox" ',
+        '<% if (finished) { %> checked="checked" <% } %> >',
+        '<a class="checklist_edit_desc" href="#" size="5"><%= description %></a>',
+        '<a id=<%= id %> class="checklist_del_item" href="#"></a>'
+    ].join('')),
+
+    render: function () {
+        this.$el.attr('id', 'checklist_item' + this.model.get("id"));
+        this.$el.html(this.template(this.model.attributes));
+        return this;
+    },
+
+    /* Toggle the status of the Backbone object on the front-end. */
+    toggleStatus: function () {
+        this.model.set({
+            finished: !this.model.get("finished")
+        });
+
+        this.toggleStatusDB();
+    },
+
+    /* Toggle the status of the item on the back-end. */
+    toggleStatusDB: function () {
+        var item_id = this.model.get("id");
+        this.checklistAPI.set({
+            toggle: true,
+            checklist_item_id: item_id,
+            item_description: null
+        });
+
+        this.checklistAPI.save(null, this);
+    },
+
+    /* This template is for the textfield that allows the user to edit
+     * the item description. */
+    edit_template: _.template([
+        '<input type="checkbox" name="checklist_checkbox" ',
+        '<% if (finished) { %> checked="checked" <% } %> >',
+        '<input name="checklist_editDesc" value="<%= description %>"</input>',
+        '<a id=<%= id %> class="checklist_del_item" href="#"></a>'
+    ].join('')),
+
+    /* When an item is clicked, a textfield should show up. */
+    editItemDesc: function (event) {
+        event.stopPropagation();
+        this.$el.html(this.edit_template(this.model.attributes));
+
+        return false;
+    },
+
+    /* When the user presses enter on the textfield, the form should be
+     * submitted to the back-end. */
+    editItemDescDB: function (event) {
+        if (event.keyCode === 13) {
+            item_desc = $('input[name=checklist_editDesc]').val();
+
+            this.checklistAPI.set({
+                item_description: item_desc,
+                checklist_item_id: this.model.get("id")
+            });
+
+            var saveOptions = {
+                success: function (data) {
+                    /* Update the Backbone model's description. */
+                    this.model.set({
+                        description: item_desc
+                    });
+                    this.$el.html(this.template(this.model.attributes));
+                }
+            };
+
+            this.checklistAPI.save(saveOptions, this);
+
+            return false;
+        }
+    }
+
+});
+
+Checklist.ChecklistView = Backbone.View.extend({
+    events: {
+        'keydown input[name="checklist_itemDesc"]': 'addItemDB',
+        'click a.checklist_del_item': 'removeItem',
+        'click a.checklist_min': 'minimizeView',
+        'click a.checklist_max': 'maximizeView',
+        'click a#checklist_exit': 'deleteChecklist'
+    },
+
+    initialize: function (options) {
+        /* Bind every function that uses 'this' as current object. */
+        _.bindAll(this, 'render', 'addItemDB', 'addItem', 'appendItem',
+            'removeItemDB', 'removeItem', 'minimizeView', 'maximizeView',
+            'createNewChecklist', 'deleteChecklist');
+
+        this.review_request_id = options.review_request_id;
+        this.user_id = options.user_id;
+
+        this.checklistAPI = new Checklist.ChecklistAPI();
+        this.curHeight = 0;
+
+        /* Create a new checklist. */
+        this.collection = new Checklist.Checklist();
+        this.collection.bind('add', this.appendItem);
+        this.createNewChecklist();
+    },
+
+    /* Create or GET a checklist on the server side. Let the API handle
+    this one. */
+    createNewChecklist: function () {
+        this.checklistAPI.set({
+            user_id: this.user_id,
+            review_request_id: this.review_request_id,
+        });
+
+        var self = this;
+        var saveOptions = {
+            success: function (data) {
+                this.render();
+                /* The checklist we receive from the back-end may not be empty,
+                   so we need to add the items it already has to the collection. */
+                $.each(self.checklistAPI.checklist_items, function (ind, item) {
+                    self.addItem(item.id, item.description, item.finished);
+                });
+            }
+        };
+
+        this.checklistAPI.save(saveOptions, this);
+    },
+
+    checklist_template: _.template([
+        '<p>Checklist</p>',
+        '<a id="checklist_toggle_size" class="checklist_min" href="#"></a>',
+        '<a id="checklist_exit" href="#"></a>',
+        '<input name="checklist_itemDesc"/><ul></ul>'
+    ].join('')),
+
+    render: function () {
+        /* Render the checklist on the page. */
+        var self = this;
+        this.$el.html(this.checklist_template());
+        this._$ul = this.$('ul');
+
+        /* If collection is not empty, we want to render each item inside
+         * it. */
+        $(this.collection.models).each(function (item) {
+            this.appendItem(item);
+        }, this);
+
+        $('#checklist').empty().append(this.$el);
+    },
+
+    minimizeView: function () {
+        var children = this.$el.children();
+        $(children).each(function (index, element) {
+            $(element).css('visibility', 'hidden');
+        });
+
+        /* Show the toggle button, and change its class.*/
+
+        $('#checklist_toggle_size')
+            .css('visibility', 'visible')
+            .attr('class', 'checklist_max');
+
+        /* Narrow down the checklist. Remember the current height. */
+        this.curHeight = this.$el.height();
+        this.$el.animate({
+            height: "16px",
+            width: "16px"
+        }, 400);
+
+        this.$el.attr('overflow', 'hidden');
+
+        return false;
+    },
+
+    maximizeView: function () {
+        var children = this.$el.children();
+
+        /* Widen the checklist. */
+        this.$el.animate({
+            height: this.curHeight,
+            width: "20%"
+        }, 400, function () {
+            /* We don't want inline style attribute so remove.*/
+            $(this).removeAttr("style");
+        });
+
+        this.$el.attr('overflow', 'hidden');
+
+        $(children).each(function (index, element) {
+            $(element).css('visibility', 'visible');
+        });
+
+        /* Toggle the class of the toggle button. */
+        $("#checklist_toggle_size").attr('class', 'checklist_min');
+
+        return false;
+    },
+
+    /* Add the new item to the backend. */
+    addItemDB: function (event) {
+        if (event.keyCode === 13) {
+            var item_desc = $('input[name=checklist_itemDesc]').val();
+            $('input[name=checklist_itemDesc]').val('');
+
+            if (item_desc === '') {
+                alert("Please type a description");
+                return;
+            }
+
+            this.checklistAPI.set({
+                item_description: item_desc,
+                checklist_item_id: null,
+            });
+
+            var self = this;
+            var saveOptions = {
+                success: function (data) {
+                    var item_id = data.attributes.checklist_item_id;
+                    self.addItem(item_id, item_desc, false);
+                },
+            };
+
+            this.checklistAPI.save(saveOptions, this);
+        }
+    },
+
+    /* Add the new item to the collection on the front end. */
+    addItem: function (item_id, item_desc, status) {
+        var item = new Checklist.ChecklistItem();
+        item.set({
+            description: item_desc,
+            id: item_id,
+            finished: status
+        });
+
+        this.collection.add(item);
+
+        return false;
+    },
+
+    /* Create and render the new checklist item. */
+    appendItem: function (item) {
+        var self = this;
+        var checklistItemView = new Checklist.ChecklistItemView({
+            model: item,
+            checklistAPI: self.checklistAPI
+        });
+        this._$ul.append(checklistItemView.render().el);
+
+        return false;
+    },
+
+    /* First, find the item, and remove it from the models. */
+    removeItem: function (e) {
+        var id = $(e.currentTarget).attr("id"),
+            item = this.collection.get(id);
+
+        e.preventDefault();
+        this.collection.remove(item);
+
+        /* Then remove its view too. */
+        $("#checklist_item" + id).remove();
+
+        /* Then remove it from the database. */
+        this.removeItemDB(id);
+
+        return false;
+    },
+
+    removeItemDB: function (item_id) {
+        this.checklistAPI.set({
+            item_description: '',
+            checklist_item_id: item_id,
+            toggle: null,
+        });
+
+        this.checklistAPI.save(null, this);
+    },
+
+    deleteChecklist: function () {
+        var response = confirm("This action will delete the entire checklist." +
+            "This cannot be undone.");
+
+        if (response) {
+            var saveOptions = {
+                success: function (data) {
+                    this.remove();
+                }
+            };
+            this.checklistAPI.destroy(saveOptions, this);
+        }
+
+        return false;
+    }
+});
+
+/* For the instantiation method in the html page. */
+Checklist.Extension = RB.Extension.extend();
\ No newline at end of file
diff --git a/checklist/checklist/templates/checklist/template.html b/checklist/checklist/templates/checklist/template.html
new file mode 100644
index 0000000000000000000000000000000000000000..6e0c7d5d5bc84f8e5cde07e42a13746cf003fa18
--- /dev/null
+++ b/checklist/checklist/templates/checklist/template.html
@@ -0,0 +1,14 @@
+{% load djblets_extensions %}
+{% ext_js_bundle extension "js_default" %}
+{% ext_css_bundle extension "css_default" %}
+
+<div id="checklist"></div>
+
+<script>
+    $(document).ready(function() {
+        new Checklist.ChecklistView({
+            user_id: "{{user.pk}}",
+            review_request_id: "{{review_request.id}}"
+        });
+    });
+</script>
diff --git a/checklist/setup.py b/checklist/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..bae0ba50f9ddbf8dd2fddfe48943a8457f76179c
--- /dev/null
+++ b/checklist/setup.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+from reviewboard.extensions.packaging import setup
+
+
+PACKAGE = "checklist"
+VERSION = "0.1"
+
+setup(
+    name=PACKAGE,
+    version=VERSION,
+    description="Extension for Review Board which adds checklists for reviewers",
+    author="Mary Elaine Malit",
+    packages=["checklist"],
+    entry_points={
+        'reviewboard.extensions':
+            '%s = checklist.extension:Checklist' % PACKAGE,
+    },
+    package_data={
+        'checklist': [
+            'templates/checklist/*.txt',
+            'templates/checklist/*.html',
+        ],
+    }
+)
