diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for record-shell-to-markdown
+
+## Unreleased changes
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Mo Kweon (c) 2018
+
+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 Mo Kweon 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# sh2md
+
+Record your shell and print in the markdown format
+
+<div align="center">
+    <img src="./assets/demo.gif" width="100%">
+</div>
+
+```
+sh2md --help
+```
+
+```
+Usage: sh2md [--stdout]
+  Record shell and print in markdown
+
+Available options:
+  --stdout                 Instead of copying to the clipboard, print the result
+                           to stdout
+  -h,--help                Show this help text
+```
+
+
+## Build & Install
+
+You need a [stack](https://www.haskellstack.org/)
+
+```
+stack build
+stack install
+```
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,9 @@
+module Main where
+
+import RecordShellToMarkdown (startShell)
+import RecordShellToMarkdown.CLI (runCLIParser)
+
+main :: IO ()
+main = do
+  parserInfo <- runCLIParser
+  startShell parserInfo
diff --git a/sh2md.cabal b/sh2md.cabal
new file mode 100644
--- /dev/null
+++ b/sh2md.cabal
@@ -0,0 +1,88 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.31.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: b3f2502d47043be2e62c6be650b0898ad16793fce66f66ce33b928e2eb2a08a7
+
+name:           sh2md
+version:        0.1.0.0
+synopsis:       Record your shell session and print in the markdown format
+description:    Please see the README on GitHub at <https://github.com/kkweon/sh2md#readme>
+category:       Utilities
+homepage:       https://github.com/kkweon/sh2md#readme
+bug-reports:    https://github.com/kkweon/sh2md/issues
+author:         Mo Kweon
+maintainer:     kkweon@gmail.com
+copyright:      MIT
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/kkweon/sh2md
+
+library
+  exposed-modules:
+      RecordShellToMarkdown
+      RecordShellToMarkdown.CLI
+      RecordShellToMarkdown.Data
+      RecordShellToMarkdown.Print
+  other-modules:
+      Paths_sh2md
+  hs-source-dirs:
+      src
+  build-depends:
+      Hclip >=3.0.0 && <3.1
+    , base >=4.7 && <5
+    , containers >=0.5.11 && <0.6
+    , optparse-applicative >=0.14.3 && <0.15
+    , process >=1.6.3 && <1.7
+    , text >=1.2.3 && <1.3
+    , transformers >=0.5.5 && <0.6
+    , unix >=2.7.2 && <2.8
+  default-language: Haskell2010
+
+executable sh2md
+  main-is: Main.hs
+  other-modules:
+      Paths_sh2md
+  hs-source-dirs:
+      app
+  ghc-options: -Wall -threaded -O3 -rtsopts -with-rtsopts=-N
+  build-depends:
+      Hclip >=3.0.0 && <3.1
+    , base >=4.7 && <5
+    , containers >=0.5.11 && <0.6
+    , optparse-applicative >=0.14.3 && <0.15
+    , process >=1.6.3 && <1.7
+    , sh2md
+    , text >=1.2.3 && <1.3
+    , transformers >=0.5.5 && <0.6
+    , unix >=2.7.2 && <2.8
+  default-language: Haskell2010
+
+test-suite sh2md-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_sh2md
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      Hclip >=3.0.0 && <3.1
+    , base >=4.7 && <5
+    , containers >=0.5.11 && <0.6
+    , optparse-applicative >=0.14.3 && <0.15
+    , process >=1.6.3 && <1.7
+    , sh2md
+    , text >=1.2.3 && <1.3
+    , transformers >=0.5.5 && <0.6
+    , unix >=2.7.2 && <2.8
+  default-language: Haskell2010
diff --git a/src/RecordShellToMarkdown.hs b/src/RecordShellToMarkdown.hs
new file mode 100644
--- /dev/null
+++ b/src/RecordShellToMarkdown.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE NamedFieldPuns #-}
+
+{-|
+Module      : RecordShellToMarkdown
+Description : Create an interactive shell and record the inputs & outputs. When an user interrupts, print the result in markdown format
+Copyright   : (c) Mo Kweon
+License     : MIT
+Maintainer  : kkweon@gmail.com
+Stability   : experimental
+Portability : POSIX
+
+This module exports 'startShell' which starts an interactive shell
+
+> startShell
+
+CTRL + C if you want to stop recording
+
+Then it will copy the result to the system clipboard.
+-}
+module RecordShellToMarkdown
+  ( startShell
+  , runShellCommand
+  , runCommand
+  ) where
+
+import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, readMVar)
+import Control.Monad (unless)
+import Control.Monad.IO.Class (liftIO)
+import Control.Monad.Trans.State (execStateT, get, modify, put)
+import Data.Sequence (Seq((:|>), Empty))
+import qualified Data.Sequence as Seq
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+import GHC.IO.Handle (BufferMode(NoBuffering), hGetContents, hSetBuffering)
+import System.Hclip (setClipboard)
+import System.IO (stdout)
+import System.Posix.Signals (Handler(Catch), installHandler, sigINT)
+import qualified System.Process as Process
+
+import qualified RecordShellToMarkdown.CLI as CLI
+import RecordShellToMarkdown.Data (RecordShellToMarkdownState, ShellIO(..))
+import RecordShellToMarkdown.Print (joinSequence)
+
+-- | Start an Interactive shell
+--
+-- When an user interrupt __CTRL+C__ ('sigINT') then it will stop the session and print markdown
+startShell :: CLI.CLIOption -> IO ()
+startShell CLI.CLIOption {cli_stdout} = do
+  hSetBuffering stdout NoBuffering
+  mVar <- newMVar False
+  _ <- installHandler sigINT (Catch $ handleInterrupt mVar) Nothing
+  shellIOs <- execStateT (foreverRunShellCommand mVar) Empty
+  let result = Seq.foldMapWithIndex (\_ x -> buildMarkdown x) shellIOs
+  if cli_stdout
+    then TIO.putStrLn result
+    else setClipboard (T.unpack result) >>
+         TIO.putStrLn "The output is copied to your clipboard"
+  where
+    printAsMarkdown :: ShellIO -> IO ()
+    printAsMarkdown = TIO.putStrLn . buildMarkdown
+    buildMarkdown :: ShellIO -> T.Text
+    buildMarkdown ShellIO {..} =
+      let inputs = "```\n" <> T.strip (joinSequence shell_inputs) <> "\n```\n"
+       in if T.null shell_output
+            then inputs
+            else inputs <> "```\n" <> T.strip shell_output <> "\n```\n"
+
+-- | Handle CTRL-C Interrupt
+handleInterrupt :: MVar Bool -> IO ()
+handleInterrupt mVar = do
+  putStrLn "\nPress any key to complete"
+  modifyMVar_ mVar (\_ -> return True)
+
+-- | Run shell until CTRL-C is handled
+foreverRunShellCommand :: MVar Bool -> RecordShellToMarkdownState ()
+foreverRunShellCommand mVar = do
+  liftIO $ TIO.putStr "$ "
+  cmd <- T.pack <$> liftIO getLine
+  unless (T.null cmd) $ runShellCommand cmd
+  interrupted <- liftIO $ readMVar mVar
+  unless interrupted $ foreverRunShellCommand mVar
+
+-- | Run Shell Command
+-- This is a wrapper 'runCommand'
+runShellCommand :: T.Text -> RecordShellToMarkdownState ()
+runShellCommand cmd = do
+  stack <- get
+  shellIO <-
+    case stack of
+      Empty -> runCommand cmd Nothing
+      (_ :|> x) -> runCommand cmd (Just . shell_wd $ x)
+  unless (T.null . shell_output $ shellIO) $
+    liftIO . TIO.putStrLn . T.strip $ shell_output shellIO
+
+-- | Run a shell command
+--
+-- If the current working directory(CWD) is given, then run the command in the CWD
+runCommand ::
+     T.Text -- ^ Shell command
+  -> Maybe T.Text -- ^ Current working directory
+  -> RecordShellToMarkdownState ShellIO
+runCommand cmd cwd = do
+  (_, maybeHout, _, _) <-
+    liftIO $
+    Process.createProcess
+      (Process.shell (T.unpack cmd ++ ";pwd"))
+        { Process.std_out = Process.CreatePipe
+        , Process.new_session = False
+        , Process.cwd = T.unpack <$> cwd
+        }
+  case maybeHout of
+    Just hout -> do
+      content <- liftIO . (T.lines . T.pack <$>) $ hGetContents hout
+      handleContent cmd content
+    Nothing -> fail "Failed to create a stdout handle"
+
+handleContent :: T.Text -> [T.Text] -> RecordShellToMarkdownState ShellIO
+handleContent cmd content = do
+  shellIOs <- get
+  case shellIOs of
+    (rest :|> lastShellIO@ShellIO {..}) ->
+      if T.null shell_output
+        then let newShellIO =
+                   lastShellIO
+                     { shell_inputs = shell_inputs :|> cmd
+                     , shell_output = shell_output <> T.unlines (init content)
+                     , shell_wd = last content
+                     }
+              in put (rest :|> newShellIO) >> return newShellIO
+        else addNewShellIO cmd content
+    _ -> addNewShellIO cmd content
+
+-- | Create a new ShellIO and add to the state
+addNewShellIO :: T.Text -> [T.Text] -> RecordShellToMarkdownState ShellIO
+addNewShellIO cmd content = do
+  let shellIO =
+        ShellIO (Seq.singleton cmd) (T.unlines (init content)) (last content)
+  modify (:|> shellIO)
+  return shellIO
diff --git a/src/RecordShellToMarkdown/CLI.hs b/src/RecordShellToMarkdown/CLI.hs
new file mode 100644
--- /dev/null
+++ b/src/RecordShellToMarkdown/CLI.hs
@@ -0,0 +1,23 @@
+module RecordShellToMarkdown.CLI where
+
+import Options.Applicative
+
+newtype CLIOption = CLIOption
+  { cli_stdout :: Bool
+  } deriving (Show)
+
+cliOptionParser :: Parser CLIOption
+cliOptionParser =
+  CLIOption <$>
+  switch
+    (long "stdout" <>
+     help "Instead of copying to the clipboard, print the result to stdout")
+
+cliOptionParserInfo :: ParserInfo CLIOption
+cliOptionParserInfo =
+  info
+    (cliOptionParser <**> helper)
+    (fullDesc <> progDesc "Record shell and print in markdown")
+
+runCLIParser :: IO CLIOption
+runCLIParser = execParser cliOptionParserInfo
diff --git a/src/RecordShellToMarkdown/Data.hs b/src/RecordShellToMarkdown/Data.hs
new file mode 100644
--- /dev/null
+++ b/src/RecordShellToMarkdown/Data.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module RecordShellToMarkdown.Data
+  ( ShellIO(..)
+  , RecordShellToMarkdownState
+  ) where
+
+import Control.Monad.Trans.State (StateT)
+import Data.Sequence (Seq(..))
+import qualified Data.Sequence as Seq
+import qualified Data.Text as T
+
+import RecordShellToMarkdown.Print (joinSequence)
+
+-- | Type Alias
+--
+-- This contains the entire shell session
+type RecordShellToMarkdownState a = StateT (Seq ShellIO) IO a
+
+-- | Shell Input and Output
+data ShellIO = ShellIO
+  { shell_inputs :: Seq T.Text -- ^ commands. If a command doesn't have stdout, it will be combined. For example, cd dir doesn't have an output so the next command will be concatenated to the command
+  , shell_output :: !T.Text -- ^ output.
+  , shell_wd :: T.Text -- ^ working directory when the output was printed
+  }
+
+instance Show ShellIO where
+  show ShellIO {..} =
+    T.unpack $
+    joinSequence (fmap ("> " <>) shell_inputs) <> "\n" <> shell_output
diff --git a/src/RecordShellToMarkdown/Print.hs b/src/RecordShellToMarkdown/Print.hs
new file mode 100644
--- /dev/null
+++ b/src/RecordShellToMarkdown/Print.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module RecordShellToMarkdown.Print
+  ( joinSequence
+  ) where
+
+import Data.Sequence (Seq)
+import qualified Data.Sequence as Seq
+import qualified Data.Text as T
+
+-- | Concat @Seq T.Text@ into 'T.Text'
+joinSequence :: Seq T.Text -> T.Text
+joinSequence = Seq.foldrWithIndex folder ""
+  where
+    folder :: Int -> T.Text -> T.Text -> T.Text
+    folder _ value accumulator =
+      if T.null value
+        then accumulator
+        else value <> "\n" <> accumulator
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"
