diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Stephen Diehl
+
+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,88 @@
+Repline
+-------
+
+Slightly higher level wrapper for creating GHCi-like REPL monads that are composable with normal MTL
+transformers. Mostly exists because I got tired of implementing the same interface for simple shells over and
+over, and because vanilla Haskeline has a kind of quirky API.
+
+[![Build Status](https://travis-ci.org/sdiehl/repline.svg)](https://travis-ci.org/sdiehl/repline)
+
+Usage
+-----
+
+```haskell
+type Repl a = HaskelineT IO a
+
+-- Evaluation : handle each line user inputs
+cmd :: String -> Repl ()
+cmd input = liftIO $ print input
+
+
+-- Tab Completion: return a completion for partial words entered
+completer :: Monad m => WordCompleter m
+completer n = do
+  let names = ["kirk", "spock", "mccoy"]
+  let matches = filter (isPrefixOf n) names
+  return $ map simpleCompletion matches
+
+-- Commands
+help :: [String] -> Repl ()
+help args = liftIO $ print $ "Help: " ++ show args
+
+say :: [String] -> Repl ()
+say args = do
+  _ <- liftIO $ system $ "cowsay" ++ " " ++ (unwords args)
+  return ()
+
+
+options :: [(String, [String] -> Repl ())]
+options = [
+    ("help", help)  -- :help
+  , ("say", say)    -- :say
+  ]
+
+
+ini :: Repl ()
+ini = liftIO $ putStrLn "Welcome!"
+
+main :: IO ()
+main = evalRepl ">>> " cmd options (Word completer) ini
+```
+
+Trying it out:
+
+```haskell
+$ runhaskell Main.hs
+Welcome!
+>>> <TAB>
+kirk spock mccoy
+
+>>> k<TAB>
+kirk
+
+>>> spam
+"spam"
+
+>>> :say Hello Haskell
+ _______________ 
+< Hello Haskell >
+ --------------- 
+        \   ^__^
+         \  (oo)\_______
+            (__)\       )\/\
+                ||----w |
+                ||     ||
+```
+
+Installation
+------------
+
+```bash
+$ cabal install repline
+```
+
+License
+-------
+
+Copyright (c) 2014, Stephen Diehl
+Released under the MIT License
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/repline.cabal b/repline.cabal
new file mode 100644
--- /dev/null
+++ b/repline.cabal
@@ -0,0 +1,37 @@
+name:                repline
+version:             0.1.0.0
+license:             MIT
+license-file:        LICENSE
+author:              Stephen Diehl
+maintainer:          stephen.m.diehl@gmail.com
+copyright:           2014 Stephen Diehl
+category:            User Interfaces
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+tested-with:         GHC == 7.6.1, GHC == 7.6.3, GHC == 7.8.3
+Bug-Reports:         https://github.com/sdiehl/repline/issues
+
+Description:
+  Haskeline wrapper for GHCi-like REPL interfaces. Composable with normal mtl transformers.
+Source-Repository head
+    Type: git
+    Location: git@github.com:sdiehl/repline.git
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     System.Console.Repline
+  -- other-modules:       
+  build-depends:
+    base       >= 4.6 && <4.7,
+    containers >= 0.5 && <0.6,
+    mtl        >= 2.2 && <2.3,
+    haskeline  >= 0.7 && <0.8
+  other-extensions:
+    FlexibleInstances,
+    FlexibleContexts,
+    TypeSynonymInstances,
+    UndecidableInstances,
+    MultiParamTypeClasses,
+    GeneralizedNewtypeDeriving
+  default-language:    Haskell2010
diff --git a/src/System/Console/Repline.hs b/src/System/Console/Repline.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Console/Repline.hs
@@ -0,0 +1,269 @@
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+{- |
+
+Repline exposes an additional monad transformer on top of Haskeline called 'HaskelineT'. It simplifies several
+aspects of composing Haskeline with State and Exception monads in modern versions of mtl.
+
+> type Repl a = HaskelineT IO a
+
+The evaluator 'evalRepl' evaluates a 'HaskelineT' monad transformer by constructing a shell with several
+custom functions and evaluating it inside of IO:
+
+  * Commands: Handled on ordinary input.
+
+  * Completions: Handled when tab key is pressed.
+
+  * Options: Handled when a command prefixed by a colon is entered.
+
+  * Banner: Text Displayed at initialization.
+
+  * Initializer: Run at initialization.
+
+A simple evaluation function might simply echo the output back to the screen.
+
+> -- Evaluation : handle each line user inputs
+> cmd :: String -> Repl ()
+> cmd input = liftIO $ print input
+
+Several tab completion options are available, the most common is the 'WordCompleter' which completes on single
+words separated by spaces from a list of matches. The internal logic can be whatever is required and can also
+access a StateT instance to query application state.
+
+> -- Tab Completion: return a completion for partial words entered
+> completer :: Monad m => WordCompleter m
+> completer n = do
+>   let names = ["kirk", "spock", "mccoy"]
+>   let matches = filter (isPrefixOf n) names
+>   return $ map simpleCompletion matches
+
+Input which is prefixed by a colon (commands like \":type\" and \":help\") queries an association list of
+functions which map to custom logic. The function takes a space-separated list of augments in it's first
+argument. If the entire line is desired then the 'unwords' function can be used to concatenate.
+
+> -- Commands
+> help :: [String] -> Repl ()
+> help args = liftIO $ print $ "Help: " ++ show args
+>
+> say :: [String] -> Repl ()
+> say args = do
+>   _ <- liftIO $ system $ "cowsay" ++ " " ++ (unwords args)
+>   return ()
+
+Now we need only map these functions to their commands.
+
+> options :: [(String, [String] -> Repl ())]
+> options = [
+>     ("help", help)  -- :help
+>   , ("say", say)    -- :say
+>   ]
+
+The banner function is simply an IO action that is called at the start of the shell.
+
+> ini :: Repl ()
+> ini = liftIO $ putStrLn "Welcome!"
+
+Putting it all together we have a little shell.
+
+> main :: IO ()
+> main = evalRepl ">>> " cmd options (Word completer) ini
+
+Putting this in a file we can test out our cow-trek shell.
+
+> $ runhaskell Main.hs
+> Welcome!
+> >>> <TAB>
+> kirk spock mccoy
+>
+> >>> k<TAB>
+> kirk
+>
+> >>> spam
+> "spam"
+>
+> >>> :say Hello Haskell
+>  _______________
+> < Hello Haskell >
+>  ---------------
+>         \   ^__^
+>          \  (oo)\_______
+>             (__)\       )\/\
+>                 ||----w |
+>                 ||     ||
+
+See <https://github.com/sdiehl/repline> for more examples.
+
+-}
+
+module System.Console.Repline (
+  HaskelineT,
+  runHaskelineT,
+
+  Cmd,
+  Options,
+  WordCompleter,
+  LineCompleter,
+  CompleterStyle(..),
+  Command,
+
+  --ReplSettings(ReplSettings),
+  --emptySettings,
+
+  evalRepl,
+  --evalReplWith,
+
+  listFiles, -- re-export
+  trimComplete,
+) where
+
+import System.Console.Haskeline.Completion
+import System.Console.Haskeline.MonadException
+import qualified System.Console.Haskeline as H
+
+import Data.List (isPrefixOf)
+import Control.Applicative
+import Control.Monad.State.Strict
+
+-------------------------------------------------------------------------------
+-- Haskeline Transformer
+-------------------------------------------------------------------------------
+
+newtype HaskelineT (m :: * -> *) a = HaskelineT { unHaskeline :: H.InputT m a }
+ deriving (Monad, Functor, Applicative, MonadIO, MonadException, MonadTrans, MonadHaskeline)
+
+runHaskelineT :: MonadException m => H.Settings m -> HaskelineT m a -> m a
+runHaskelineT s m = H.runInputT s (H.withInterrupt (unHaskeline m))
+
+class MonadException m => MonadHaskeline m where
+  getInputLine :: String -> m (Maybe String)
+  getInputChar :: String -> m (Maybe Char)
+  outputStr    :: String -> m ()
+  outputStrLn  :: String -> m ()
+
+instance MonadException m => MonadHaskeline (H.InputT m) where
+  getInputLine = H.getInputLine
+  getInputChar = H.getInputChar
+  outputStr    = H.outputStr
+  outputStrLn  = H.outputStrLn
+
+instance MonadState s m => MonadState s (HaskelineT m) where
+  get = lift get
+  put = lift . put
+
+instance (MonadHaskeline m) => MonadHaskeline (StateT s m) where
+  getInputLine = lift . getInputLine
+  getInputChar = lift . getInputChar
+  outputStr    = lift . outputStr
+  outputStrLn  = lift . outputStrLn
+
+-------------------------------------------------------------------------------
+-- Repl
+-------------------------------------------------------------------------------
+
+type Cmd m = [String] -> m ()
+type Options m = [(String, Cmd m)]
+type Command m = String -> m ()
+
+type WordCompleter m = (String -> m [Completion])
+type LineCompleter m = (String -> String -> m [Completion])
+
+data ReplSettings m = ReplSettings
+  { _cmd       :: Command (HaskelineT m) -- ^ Command function
+  , _opts      :: Options (HaskelineT m) -- ^ Options map
+  , _compstyle :: CompleterStyle m       -- ^ Internal completer function
+  , _init      :: m ()                   -- ^ Initializer function.
+  , _banner    :: String                 -- ^ Banner string
+  }
+
+emptySettings :: MonadHaskeline m => ReplSettings m
+emptySettings = ReplSettings (const $ return ()) [] File (return ()) ""
+
+-- | Wrap a HasklineT action so that if an interrupt is thrown the shell continues as normal.
+tryAction :: MonadException m => HaskelineT m a -> HaskelineT m a
+tryAction (HaskelineT f) = HaskelineT (H.withInterrupt loop)
+    where loop = handle (\H.Interrupt -> loop) f
+
+-- | Completion loop.
+replLoop :: MonadException m
+         => String
+         -> Command (HaskelineT m)
+         -> Options (HaskelineT m)
+         -> HaskelineT m ()
+replLoop banner cmdM opts = loop
+  where
+    loop = do
+      minput <- H.handleInterrupt (return (Just "")) $ getInputLine banner
+      case minput of
+        Nothing -> outputStrLn "Goodbye."
+
+        Just "" -> loop
+        Just ":" -> loop
+
+        Just (':' : cmds) -> do
+          let (cmd:args) = words cmds
+          optMatcher cmd opts args
+          loop
+
+        Just input -> do
+          H.handleInterrupt (liftIO $ putStrLn "Ctrl-C") $ cmdM input
+          loop
+
+-- | Match the options.
+optMatcher :: MonadHaskeline m => String -> Options m -> [String] -> m ()
+optMatcher s [] _ = outputStrLn $ "No such command :" ++ s
+optMatcher s ((x, m):xs) args
+  | s `isPrefixOf` x = m args
+  | otherwise = optMatcher s xs args
+
+-- | Evaluate the REPL logic into a MonadException context.
+evalRepl :: MonadException m             -- Terminal monad ( often IO ).
+         => String                       -- ^ Banner
+         -> Command (HaskelineT m)       -- ^ Command function
+         -> Options (HaskelineT m)       -- ^ Options list and commands
+         -> CompleterStyle m             -- ^ Tab completion function
+         -> HaskelineT m a               -- ^ Initializer
+         -> m ()
+evalRepl banner cmd opts comp initz = runHaskelineT _readline (initz >> monad)
+  where
+    monad = replLoop banner cmd opts
+    _readline = H.Settings
+      { H.complete       = mkCompleter comp
+      , H.historyFile    = Just ".history"
+      , H.autoAddHistory = True
+      }
+
+-- | Evaluate the REPL logic with the settings record.
+evalReplWith :: MonadException m => ReplSettings m -> m ()
+evalReplWith settings = runHaskelineT _readline (tryAction monad)
+  where
+    monad = replLoop (_banner settings) (_cmd settings) (_opts settings)
+    _readline = H.Settings
+      { H.complete       = mkCompleter (_compstyle settings)
+      , H.historyFile    = Just ".history"
+      , H.autoAddHistory = True
+      }
+
+-------------------------------------------------------------------------------
+-- Completions
+-------------------------------------------------------------------------------
+
+data CompleterStyle m
+  = Word (WordCompleter m)       -- ^ Completion function takes single word.
+  | Cursor (LineCompleter m)     -- ^ Completion function takes tuple of full line.
+  | File                         -- ^ Completion function completes files in CWD.
+  | Mixed (CompletionFunc m)     -- ^ Function manually manipulates readline result.
+
+mkCompleter :: MonadIO m => CompleterStyle m -> CompletionFunc m
+mkCompleter (Word f)   = completeWord (Just '\\') " \t()[]" f
+mkCompleter (Cursor f) = completeWordWithPrev (Just '\\') " \t()[]" f
+mkCompleter File       = completeFilename
+mkCompleter (Mixed f)  = f
+
+trimComplete :: String -> Completion -> Completion
+trimComplete prefix (Completion a b c) = Completion (drop (length prefix) a) b c
