diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for slate
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 evuez
+
+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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,69 @@
+# slate - a note taking tool.
+
+A simple tool to take notes from your terminal.
+
+Generates markdown [task lists](https://help.github.com/articles/about-task-lists/).
+
+Lists are stored in `~/.config/slate/` and their default name is the name of your current directory. You can use any other name you want using the `--name` option.
+
+## Install
+
+```shell
+$ stack install
+```
+
+## Usage
+
+<pre>
+$ slate --help
+
+slate - a note taking tool.
+
+Usage: slate COMMAND
+  Slate
+
+Available options:
+  -h,--help                Show this help text
+
+Available commands:
+  add                      Add a note.
+  done                     Mark a note as done.
+  todo                     Mark a note as to-do.
+  remove                   Remove a note.
+  display                  Display a slate.
+  rename                   Rename a slate.
+  wipe                     Wipe a slate.
+
+$ slate add "My first note."
+$ slate add "New note!"
+$ slate display
+00 - My first note.
+01 - New note!
+
+$ slate done 0
+$ slate display
+<s>00 - My first note.</s>
+01 - New note!
+
+$ slate display --only=todo
+01 - New note!
+
+$ slate add "Fake note"
+$ slate display
+<s>00 - My first note.</s>
+01 - New note!
+02 - Fake note
+
+$ slate remove 2
+$ slate display
+<s>00 - My first note.</s>
+01 - New note!
+
+$ slate wipe --only=todo
+$ slate display
+<s>00 - My first note.</s>
+
+$ slate todo 0
+$ slate display
+00 - My first note.
+</pre>
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,16 @@
+module Main where
+
+import Data.Semigroup ((<>))
+import Lib
+import Options.Applicative
+
+main :: IO ()
+main = do
+  initialize
+  parsedOpts <- execParser opts
+  execute parsedOpts
+  where
+    opts =
+      info
+        (parser <**> helper)
+        (fullDesc <> progDesc "Slate" <> header "slate - a note taking tool.")
diff --git a/slate.cabal b/slate.cabal
new file mode 100644
--- /dev/null
+++ b/slate.cabal
@@ -0,0 +1,72 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 0c4dd43c3f2d0caa98d5db89c38d2085527502780a7656087a24d835af4321e8
+
+name:           slate
+version:        0.2.0.0
+synopsis:       A note taking CLI tool.
+description:    Please see the README on Github at <https://github.com/evuez/slate#readme>
+homepage:       https://github.com/evuez/slate#readme
+bug-reports:    https://github.com/evuez/slate/issues
+author:         evuez
+maintainer:     helloevuez@gmail.com
+copyright:      Copyright (c) 2017, evuez
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+
+extra-source-files:
+    ChangeLog.md
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/evuez/slate
+
+library
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.7 && <5
+    , directory >=1.3
+    , filepath >=1.4
+    , optparse-applicative >=0.14
+  exposed-modules:
+      Lib
+  other-modules:
+      Paths_slate
+  default-language: Haskell2010
+
+executable slate
+  main-is: Main.hs
+  hs-source-dirs:
+      app
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , directory >=1.3
+    , filepath >=1.4
+    , optparse-applicative >=0.14
+    , slate
+  other-modules:
+      Paths_slate
+  default-language: Haskell2010
+
+test-suite slate-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , directory >=1.3
+    , filepath >=1.4
+    , optparse-applicative >=0.14
+    , slate
+  other-modules:
+      Paths_slate
+  default-language: Haskell2010
diff --git a/src/Lib.hs b/src/Lib.hs
new file mode 100644
--- /dev/null
+++ b/src/Lib.hs
@@ -0,0 +1,211 @@
+module Lib
+  ( initialize
+  , execute
+  , parser
+  ) where
+
+import Data.Semigroup ((<>))
+import Options.Applicative
+import System.Directory
+  ( createDirectoryIfMissing
+  , getCurrentDirectory
+  , getHomeDirectory
+  , removeFile
+  , renameFile
+  )
+import System.FilePath.Posix (takeBaseName)
+
+type Slate = String
+
+type Note = String
+
+type NoteId = Int
+
+type Filter = String
+
+data Command
+  = Add Slate
+        Note
+  | Done Slate
+         NoteId
+  | Todo Slate
+         NoteId
+  | Remove Slate
+           NoteId
+  | Display Slate
+            Filter
+  | Rename Slate
+           Slate
+  | Wipe Slate
+         Filter
+  deriving (Eq, Show)
+
+-- Parsers
+name :: Parser String
+name =
+  option
+    str
+    (long "name" <> short 'n' <> metavar "SLATE" <> help "Name of the slate." <>
+     value "")
+
+add :: Parser Command
+add = Add <$> name <*> argument str (metavar "NOTE")
+
+done :: Parser Command
+done = Done <$> name <*> argument auto (metavar "NOTE ID")
+
+todo :: Parser Command
+todo = Todo <$> name <*> argument auto (metavar "NOTE ID")
+
+remove :: Parser Command
+remove = Remove <$> name <*> argument auto (metavar "NOTE ID")
+
+display :: Parser Command
+display =
+  Display <$> name <*>
+  option
+    str
+    (long "only" <> short 'o' <> help "Display only done / todo notes." <>
+     value "")
+
+rename :: Parser Command
+rename =
+  Rename <$> argument str (metavar "CURRENT" <> help "Current name.") <*>
+  argument str (metavar "NEW" <> help "New name.")
+
+wipe :: Parser Command
+wipe =
+  Wipe <$> name <*>
+  option
+    str
+    (long "only" <> short 'o' <> help "Wipe only done / todo notes." <> value "")
+
+parser :: Parser Command
+parser =
+  subparser
+    (command "add" (info add (progDesc "Add a note.")) <>
+     command "done" (info done (progDesc "Mark a note as done.")) <>
+     command "todo" (info todo (progDesc "Mark a note as to-do.")) <>
+     command "remove" (info remove (progDesc "Remove a note.")) <>
+     command "display" (info display (progDesc "Display a slate.")) <>
+     command "rename" (info rename (progDesc "Rename a slate.")) <>
+     command "wipe" (info wipe (progDesc "Wipe a slate.")))
+
+-- Commands
+execute :: Command -> IO ()
+execute (Add "" n) = getSlateName >>= (\s -> execute (Add s n))
+execute (Add s n) =
+  getSlatePath s >>= (\x -> appendFile x (" - [ ] " ++ n ++ "\n"))
+execute (Done "" n) = getSlateName >>= (\x -> execute (Done x n))
+execute (Done s n) = getSlatePath s >>= (\x -> markAsDone x n)
+execute (Todo "" n) = getSlateName >>= (\x -> execute (Todo x n))
+execute (Todo s n) = getSlatePath s >>= (\x -> markAsTodo x n)
+execute (Remove "" n) = getSlateName >>= (\x -> execute (Remove x n))
+execute (Remove s n) = getSlatePath s >>= (\x -> removeNote x n)
+execute (Display "" f) = getSlateName >>= (\x -> execute (Display x f))
+execute (Display s f) = getSlatePath s >>= (\x -> displaySlate x f)
+execute (Rename sc sn) = renameSlate sc sn
+execute (Wipe "" f) = getSlateName >>= (\x -> execute (Wipe x f))
+execute (Wipe s "") = getSlatePath s >>= removeFile
+execute (Wipe s f) = getSlatePath s >>= (\x -> wipeSlate x f)
+
+-- Helpers
+initialize :: IO ()
+initialize = getConfigDirectory >>= (\c -> createDirectoryIfMissing True c)
+
+getSlateName :: IO String
+getSlateName = do
+  directory <- getCurrentDirectory
+  return $ takeBaseName directory
+
+getConfigDirectory :: IO String
+getConfigDirectory = do
+  home <- getHomeDirectory
+  return $ home ++ "/.config/slate/"
+
+getSlatePath :: String -> IO FilePath
+getSlatePath s = do
+  dir <- getConfigDirectory
+  return $ dir ++ s ++ ".md"
+
+displaySlate :: String -> String -> IO ()
+displaySlate s "" = do
+  contents <- readFile s
+  let notes = zipWith displayNote [0 ..] (lines contents)
+  putStr $ unlines notes
+displaySlate s "done" = do
+  contents <- readFile s
+  let notes = zipWith displayNote [0 ..] (lines contents)
+  putStr $ unlines $ filter isNoteDone notes
+displaySlate s "todo" = do
+  contents <- readFile s
+  let notes = zipWith displayNote [0 ..] (lines contents)
+  putStr $ unlines $ filter (not . isNoteDone) notes
+displaySlate _ f = putStr $ "\"" ++ f ++ "\" is not a valid filter."
+
+displayNote :: Int -> String -> String
+displayNote line (' ':'-':' ':'[':' ':']':note) = padInt line 2 ++ " -" ++ note
+displayNote line (' ':'-':' ':'[':'x':']':note) =
+  "\x1B[9m" ++ padInt line 2 ++ " -" ++ note ++ "\x1B[0m"
+displayNote line _ =
+  "\x1B[31m" ++
+  padInt line 2 ++ " - Parsing error: line is malformed" ++ "\x1B[0m"
+
+isNoteDone :: String -> Bool
+isNoteDone (' ':'-':' ':'[':'x':']':_) = True
+isNoteDone ('\x1B':_) = True
+isNoteDone _ = False
+
+padInt :: Int -> Int -> String
+padInt n s = replicate (s - length (show n)) '0' ++ show n
+
+markAsDone :: FilePath -> Int -> IO ()
+markAsDone s n = do
+  contents <- readFile s
+  let (x, y:t) = splitAt n (lines contents)
+      c =
+        case y of
+          ' ':'-':' ':'[':' ':']':note -> " - [x]" ++ note
+          note -> note
+      tmp = s ++ ".tmp"
+  writeFile (s ++ ".tmp") (unlines $ x ++ c : t)
+  renameFile tmp s
+
+markAsTodo :: FilePath -> Int -> IO ()
+markAsTodo s n = do
+  contents <- readFile s
+  let (x, y:t) = splitAt n (lines contents)
+      c =
+        case y of
+          ' ':'-':' ':'[':'x':']':note -> " - [ ]" ++ note
+          note -> note
+      tmp = s ++ ".tmp"
+  writeFile tmp (unlines $ x ++ c : t)
+  renameFile tmp s
+
+removeNote :: FilePath -> Int -> IO ()
+removeNote s n = do
+  contents <- readFile s
+  let (x, _:t) = splitAt n (lines contents)
+      tmp = s ++ ".tmp"
+  writeFile tmp (unlines $ x ++ t)
+  renameFile tmp s
+
+renameSlate :: String -> String -> IO ()
+renameSlate sc sn = do
+  current <- getSlatePath sc
+  new <- getSlatePath sn
+  renameFile current new
+
+wipeSlate :: FilePath -> String -> IO ()
+wipeSlate s "done" = do
+  contents <- readFile s
+  let tmp = s ++ ".tmp"
+  writeFile tmp $ unlines $ filter (not . isNoteDone) (lines contents)
+  renameFile tmp s
+wipeSlate s "todo" = do
+  contents <- readFile s
+  let tmp = s ++ ".tmp"
+  writeFile tmp $ unlines $ filter isNoteDone (lines contents)
+  renameFile tmp s
+wipeSlate _ f = putStr $ "\"" ++ f ++ "\" is not a valid filter."
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Test suite not yet implemented"
