diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Ricky Elrod
+
+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 Ricky Elrod nor the names of other
+      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
+OWNER 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.
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/gitignore.cabal b/gitignore.cabal
new file mode 100644
--- /dev/null
+++ b/gitignore.cabal
@@ -0,0 +1,24 @@
+-- Initial gitignore.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                gitignore
+version:             1.0.0
+synopsis:            Apply GitHub .gitignore templates to already existing repositories.
+description:         Apply GitHub .gitignore templates to already existing repositories.
+homepage:            https://github.com/CodeBlock/gitignore
+license:             BSD3
+license-file:        LICENSE
+author:              Ricky Elrod
+maintainer:          ricky@elrod.me
+copyright:           2013 Ricky Elrod
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.8
+
+source-repository head
+  type:         git
+  location:     git://github.com:CodeBlock/gitignore.git
+
+executable gitignore
+  main-is:              gitignore.hs
+  build-depends:        aeson ==0.6.*, base >=4.6 && <4.7, base64-bytestring ==1.0.*, bytestring ==0.10.*, http-conduit ==1.9.5.*, network ==2.4.*, safe ==0.3.*, text ==0.11.*
diff --git a/gitignore.hs b/gitignore.hs
new file mode 100644
--- /dev/null
+++ b/gitignore.hs
@@ -0,0 +1,89 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Control.Applicative
+import Data.Aeson
+import Data.Char (toLower)
+import Data.List (dropWhileEnd, find, sort)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Base64 as Base64
+import qualified Data.ByteString.Char8 as C8
+import Network (withSocketsDo)
+import Network.HTTP.Conduit hiding (path)
+import Safe (headMay)
+import System.Environment (getArgs)
+
+data GitIgnoreIndex = GitIgnoreIndex {
+    gitIgnores :: [GitIgnore]
+ } deriving Show
+
+data GitIgnore = GitIgnore {
+    path :: String
+  , url :: String
+} deriving Show
+
+data GitIgnoreFile = GitIgnoreFile {
+    content :: B.ByteString
+}
+
+instance FromJSON GitIgnoreIndex where
+  parseJSON (Object v) = GitIgnoreIndex <$>
+                         v .: "tree"
+
+instance FromJSON GitIgnore where
+  parseJSON (Object v) = GitIgnore <$>
+                         v .: "path" <*>
+                         v .: "url"
+
+instance FromJSON GitIgnoreFile where
+  parseJSON (Object v) = GitIgnoreFile <$>
+                         v .: "content"
+
+-- | Get the current list of gitignore templates from the github/gitignore
+--   repository, via the GitHub API.
+getIgnoresIndex :: IO [GitIgnore]
+getIgnoresIndex = withSocketsDo $ do
+  x <- parseUrl "https://api.github.com/repos/github/gitignore/git/trees/master"
+  let r = x { requestHeaders = [("User-Agent", "gitignore-hs")] }
+  l <- withManager $ httpLbs r
+  let decodedBody = decode (responseBody l) :: Maybe GitIgnoreIndex
+  case decodedBody of
+    Nothing -> error "JSON decode failed"
+    Just g -> return $ gitIgnores g
+
+-- | Given a URL obtained from the gitignores index, get the contents of the
+--   file, after decoding (base64) its contents.
+getIgnoreFile :: String -> IO B.ByteString
+getIgnoreFile url = withSocketsDo $ do
+  x <- parseUrl url
+  let r = x { requestHeaders = [("User-Agent", "gitignore-hs")] }
+  l <- withManager $ httpLbs r
+  let decodedBody = decode (responseBody l) :: Maybe GitIgnoreFile
+  case decodedBody of
+    Nothing -> error "JSON decode failed"
+    Just g -> return $ Base64.decodeLenient $ content g
+
+main :: IO ()
+main = do
+  a <- getArgs
+  case headMay a of
+    Nothing -> do
+      putStrLn "Get gitignore files from GitHub's gitignore repository."
+      error "Usage: gitignore [<language> | list] > .gitignore"
+    Just s -> do
+      x <- getIgnoresIndex
+      let ignores =
+            fmap (\x -> x { Main.path = fmap toLower (init $ Main.path x) }) $
+            filter ((/= "") . Main.path) $
+            fmap (\x -> x { Main.path = dropWhileEnd (/= '.') (Main.path x) }) x
+      case s of
+        "list" ->
+          mapM_ putStrLn $ sort $ fmap path ignores
+        _ -> do
+          let selected = find (\y -> fmap toLower (Main.path y) == s) ignores
+          case selected of
+            Nothing -> error "No such gitignore was found. Use 'list' for all possible gitignores."
+            Just f -> do
+              decoded <- getIgnoreFile $ url f
+              putStr $ C8.unpack decoded
