diff --git a/.travis.yml b/.travis.yml
index b157cd7..89b21dd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,12 +20,12 @@ install:
   - pip install -r requirements.txt
   - pip install pytest-cov
   - sed "s/mysql:\/\/test:test123@/mysql:\/\/root:@/" config.example.py > config.py
-  - python db_create.py
+  - ./db_create.py
   - ./db_migrate.py stamp head
 
 script:
-  - pytest --cov=nyaa --cov-report=term tests
-  - ./lint.sh --check
+  - ./dev.py test --cov=nyaa --cov-report=term tests
+  - ./dev.py lint
 
 notifications:
   email: false
diff --git a/README.md b/README.md
index 62b7251..93a4440 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,14 @@ This guide also assumes you 1) are using Linux and 2) are somewhat capable with
 It's not impossible to run Nyaa on Windows, but this guide doesn't focus on that.
 
 ### Code Quality:
-- Before we get any deeper, remember to follow PEP8 style guidelines and run `./lint.sh` before committing.
-    - You may also use `./lint.sh -c` to see a list of warnings/problems instead of having `lint.sh` making modifications for you
+- Before we get any deeper, remember to follow PEP8 style guidelines and run `./dev.py lint` before committing to see a list of warnings/problems.
+    - You may also use `./dev.py fix && ./dev.py isort` to automatically fix some of the issues reported by the previous command.
 - Other than PEP8, try to keep your code clean and easy to understand, as well. It's only polite!
 
 ### Running Tests
 The `tests` folder contains tests for the the `nyaa` module and the webserver. To run the tests:
 - Make sure that you are in the python virtual environment.
-- Run `pytest tests` while in the repository directory.
+- Run `./dev.py test` while in the repository directory.
 
 ### Setting up Pyenv
 pyenv eases the use of different Python versions, and as not all Linux distros offer 3.6 packages, it's right up our alley.
diff --git a/dev.py b/dev.py
new file mode 100755
index 0000000..7560784
--- /dev/null
+++ b/dev.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+This tool is designed to assist developers run common tasks, such as
+checking the code for lint issues, auto fixing some lint issues and running tests.
+It imports modules lazily (as-needed basis), so it runs faster!
+"""
+import sys
+
+LINT_PATHS = [
+    'nyaa/',
+    'utils/',
+]
+TEST_PATHS = ['tests']
+
+
+def print_cmd(cmd, args):
+    """ Prints the command and args as you would run them manually. """
+    print('Running: {0}\n'.format(
+        ' '.join([('\'' + a + '\'' if ' ' in a else a) for a in [cmd] + args])))
+    sys.stdout.flush()  # Make sure stdout is flushed before continuing.
+
+
+def check_config_values():
+    """ Verify that all max_line_length values match. """
+    import configparser
+    config = configparser.ConfigParser()
+    config.read('setup.cfg')
+
+    # Max line length:
+    flake8 = config.get('flake8', 'max_line_length', fallback=None)
+    autopep8 = config.get('pycodestyle', 'max_line_length', fallback=None)
+    isort = config.get('isort', 'line_length', fallback=None)
+
+    values = (v for v in (flake8, autopep8, isort) if v is not None)
+    found = next(values, False)
+    if not found:
+        print('Warning: No max line length setting set in setup.cfg.')
+        return False
+    elif any(v != found for v in values):
+        print('Warning: Max line length settings differ in setup.cfg.')
+        return False
+
+    return True
+
+
+def print_help():
+    print('Nyaa Development Helper')
+    print('=======================\n')
+    print('Usage: {0} command [different arguments]'.format(sys.argv[0]))
+    print('Command can be one of the following:\n')
+    print('  lint | check       : do a lint check (flake8 + flake8-isort)')
+    print('  fix  | autolint    : try and auto-fix lint (autopep8)')
+    print('  isort              : fix import sorting (isort)')
+    print('  test | pytest      : run tests (pytest)')
+    print('  help | -h | --help : show this help and exit')
+    print('')
+    print('You may pass different arguments to the script that is being run.')
+    print('For example: {0} test tests/ --verbose'.format(sys.argv[0]))
+    print('')
+    return 1
+
+
+if __name__ == '__main__':
+    assert sys.version_info >= (3, 6), "Python 3.6 is required"
+
+    check_config_values()
+
+    if len(sys.argv) < 2:
+        sys.exit(print_help())
+
+    cmd = sys.argv[1].lower()
+    if cmd in ('help', '-h', '--help'):
+        sys.exit(print_help())
+
+    args = sys.argv[2:]
+    run_default = not (args or set(('--version', '-h', '--help')).intersection(args))
+
+    # Flake8 - lint and common errors checker
+    # When combined with flake8-isort, also checks for unsorted imports.
+    if cmd in ('lint', 'check'):
+        if run_default:
+            # Putting format in the setup.cfg file breaks `pip install flake8`
+            settings = ['--format', '%(path)s [%(row)s:%(col)s] %(code)s: %(text)s',
+                        '--show-source']
+            args = LINT_PATHS + settings + args
+
+        print_cmd('flake8', args)
+        try:
+            from flake8.main.application import Application as Flake8
+        except ImportError as err:
+            print('Unable to load module: {0!r}'.format(err))
+            result = False
+        else:
+            f8 = Flake8()
+            f8.run(args)
+            result = f8.result_count == 0
+
+            if not result:
+                print("The code requires some changes.")
+            else:
+                print("Looks good!")
+        finally:
+            sys.exit(int(not result))
+
+    # AutoPEP8 - auto code linter for most simple errors.
+    if cmd in ('fix', 'autolint'):
+        if run_default:
+            args = LINT_PATHS + args
+
+        print_cmd('autopep8', args)
+        try:
+            from autopep8 import main as autopep8
+        except ImportError as err:
+            print('Unable to load module: {0!r}'.format(err))
+            result = False
+        else:
+            args = [''] + args  # Workaround
+            result = autopep8(args)
+        finally:
+            sys.exit(result)
+
+    # isort - automate import sorting.
+    if cmd in ('isort', ):
+        if run_default:
+            args = LINT_PATHS + ['-rc'] + args
+
+        print_cmd('isort', args)
+        try:
+            from isort.main import main as isort
+        except ImportError as err:
+            print('Unable to load module: {0!r}'.format(err))
+            result = False
+        else:
+            # Need to patch sys.argv for argparse in isort
+            sys.argv.remove(cmd)
+            sys.argv = [sys.argv[0] + ' ' + cmd] + args
+            result = isort()
+        finally:
+            sys.exit(result)
+
+    # py.test - test runner
+    if cmd in ('test', 'pytest'):
+        if run_default:
+            args = TEST_PATHS + args
+
+        print_cmd('pytest', args)
+        try:
+            from pytest import main as pytest
+        except ImportError as err:
+            print('Unable to load module: {0!r}'.format(err))
+            result = False
+        else:
+            result = pytest(args)
+            result = result == 0
+        finally:
+            sys.exit(int(not result))
+
+    sys.exit(print_help())
diff --git a/lint.sh b/lint.sh
index 9ab789b..af1a6f8 100755
--- a/lint.sh
+++ b/lint.sh
@@ -1,39 +1,27 @@
+#!/bin/bash
 # Lint checker/fixer
+# This script is deprecated, but still works.
 
-check_paths="nyaa/ utils/"
-isort_paths="nyaa/" # just nyaa/ for now
-max_line_length=100
-
-function auto_pep8() {
-  autopep8 ${check_paths} \
-    --recursive \
-    --in-place \
-    --pep8-passes 2000 \
-    --max-line-length ${max_line_length} \
-    --verbose \
-  && \
-  isort ${isort_paths} \
-    --recursive
+function auto_fix() {
+  ./dev.py fix && ./dev.py isort
 }
 
+
 function check_lint() {
-  pycodestyle ${check_paths} \
-    --show-source \
-    --max-line-length=${max_line_length} \
-    --format '%(path)s [%(row)s:%(col)s] %(code)s: %(text)s' \
-  && \
-  isort ${isort_paths} \
-    --recursive \
-    --diff \
-    --check-only
+  ./dev.py lint
 }
 
 # MAIN
-action=auto_pep8 # default action
+action=auto_fix # default action
 for arg in "$@"
 do
   case "$arg" in
   "-h" | "--help")
+    echo "+ ========================= +"
+    echo "+ This script is deprecated +"
+    echo "+    Please use ./dev.py    +"
+    echo "+ ========================= +"
+    echo ""
     echo "Lint checker/fixer"
     echo ""
     echo "Usage: $0 [-c|--check] [-h|--help]"
@@ -49,14 +37,4 @@ do
 done
 
 ${action} # run selected action
-result=$?
-
-if [[ ${action} == check_lint ]]; then
-  if [[ ${result} == 0 ]]; then
-    echo "Looks good!"
-  else
-    echo "The code requires some changes."
-  fi
-fi
-
-if [[ ${result} -ne 0 ]]; then exit 1; fi
+if [[ $? -ne 0 ]]; then exit 1; fi
diff --git a/requirements.txt b/requirements.txt
index 0d6c90b..007fced 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,6 +8,8 @@ click==6.7
 dominate==2.3.1
 elasticsearch==5.3.0
 elasticsearch-dsl==5.2.0
+flake8==3.3.0
+flake8-isort==2.2.1
 Flask==0.12.2
 Flask-Assets==0.12
 Flask-DebugToolbar==0.10.1
@@ -18,8 +20,8 @@ Flask-SQLAlchemy==2.2
 Flask-WTF==0.14.2
 gevent==1.2.1
 greenlet==0.4.12
-itsdangerous==0.24
 isort==4.2.15
+itsdangerous==0.24
 Jinja2==2.9.6
 libsass==0.12.3
 Mako==1.0.6
diff --git a/setup.cfg b/setup.cfg
index 1d0a38c..c93c316 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,16 @@
+[flake8]
+max_line_length = 100
+# The following line raises an exception on `pip install flake8`
+# So we're using the command line argument instead.
+# format = %(path)s [%(row)s:%(col)s] %(code)s: %(text)s
+
+[pep8]
+max_line_length = 100
+pep8_passes = 2000
+in_place = 1
+recursive = 1
+verbose = 1
+
 [isort]
 line_length = 100
 not_skip = __init__.py