diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,20 +1,20 @@
-Copyright (c) 2017 Richard Cook
-
-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.
+Copyright (c) 2017 Richard Cook
+
+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
--- a/README.md
+++ b/README.md
@@ -1,15 +1,49 @@
-# `hidden-char`
-
-[![Travis branch](https://img.shields.io/travis/rcook/hidden-char/master.svg)](https://travis-ci.org/rcook/hidden-char)
-[![Hackage](https://img.shields.io/hackage/v/hidden-char.svg)](http://hackage.haskell.org/package/hidden-char)
-[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/rcook/hidden-char/master/LICENSE)
-
-Provides a `getHiddenChar` function that works on Windows, Linux and macOS
-
-## Licence
-
-[MIT License][licence]
-
-Copyright &copy; 2017, Richard Cook.
-
-[licence]: LICENSE
+# `hidden-char`
+
+[![Travis branch](https://img.shields.io/travis/rcook/hidden-char/master.svg)](https://travis-ci.org/rcook/hidden-char)
+[![Hackage](https://img.shields.io/hackage/v/hidden-char.svg)](http://hackage.haskell.org/package/hidden-char)
+[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/rcook/hidden-char/master/LICENSE)
+
+Provides a `getHiddenChar` function that works on Windows, Linux and macOS
+
+## Dev guide
+
+This project is built using the [Stack][stack] build tool.
+
+### Build
+
+```
+stack build
+```
+
+### Test
+
+```
+stack test
+```
+
+### Upload package
+
+```
+stack upload .
+```
+
+### Upload documentation
+
+```
+script/upload-haddocks
+```
+
+## Releases
+
+[View change log for more information][change-log]
+
+## Licence
+
+[MIT License][licence]
+
+Copyright &copy; 2017, Richard Cook.
+
+[change-log]: CHANGELOG.md
+[licence]: LICENSE
+[stack]: http://haskellstack.org/
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,16 +1,16 @@
-{-|
-Module      : Main
-Description : Setup entrypoint for @hidden-char@
-Copyright   : (C) Richard Cook, 2017
-Licence     : MIT
-Maintainer  : rcook@rcook.org
-Stability   : stable
-Portability : portable
--}
-
-module Main (main) where
-
-import           Distribution.Simple
-
-main :: IO ()
-main = defaultMain
+{-|
+Module      : Main
+Description : Setup entrypoint for @hidden-char@
+Copyright   : (C) Richard Cook, 2017
+Licence     : MIT
+Maintainer  : rcook@rcook.org
+Stability   : stable
+Portability : portable
+-}
+
+module Main (main) where
+
+import           Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/hidden-char.cabal b/hidden-char.cabal
--- a/hidden-char.cabal
+++ b/hidden-char.cabal
@@ -1,47 +1,47 @@
-name:                               hidden-char
-version:                            0.1.0.1
-synopsis:                           Provides getHiddenChar function
-description:                        Provides a @getHiddenChar@ function that works on Windows, Linux and macOS
-homepage:                           https://github.com/rcook/hidden-char#readme
-license:                            MIT
-license-file:                       LICENSE
-author:                             Richard Cook
-maintainer:                         rcook@rcook.org
-copyright:                          2017 Richard Cook
-category:                           Command Line Tool
-build-type:                         Simple
-cabal-version:                      >= 1.10
-extra-source-files:                 README.md
-
-library
-  default-language:                 Haskell2010
-  ghc-options:                      -W -Wall
-  hs-source-dirs:                   src
-  build-depends:                    base >= 4.7 && < 5
-  exposed-modules:                  System.IO.HiddenChar
-  if os(linux)
-    cpp-options:                    -DOS_LINUX
-    other-modules:                  System.IO.HiddenChar.Posix
-  if os(windows)
-    cpp-options:                    -DOS_WINDOWS
-    other-modules:                  System.IO.HiddenChar.Windows
-  if os(darwin)
-    cpp-options:                    -DOS_MACOS
-    other-modules:                  System.IO.HiddenChar.Posix
-
-test-suite hidden-char-test
-  type:                             exitcode-stdio-1.0
-  default-language:                 Haskell2010
-  ghc-options:                      -W -Wall -threaded
-  hs-source-dirs:                   test
-  main-is:                          Main.hs
-  build-depends:                    base >= 4.7 && < 5
-                                  , hidden-char
-                                  , hspec
-  if os(linux)
-    cpp-options:                    -DOS_LINUX
-  if os(windows)
-    cpp-options:                    -DOS_WINDOWS
-  if os(darwin)
-    cpp-options:                    -DOS_MACOS
-  other-modules:                    HiddenCharSpec
+name:                               hidden-char
+version:                            0.1.0.2
+synopsis:                           Provides getHiddenChar function
+description:                        Provides a @getHiddenChar@ function that works on Windows, Linux and macOS
+homepage:                           https://github.com/rcook/hidden-char#readme
+license:                            MIT
+license-file:                       LICENSE
+author:                             Richard Cook
+maintainer:                         rcook@rcook.org
+copyright:                          2017 Richard Cook
+category:                           Command Line Tool
+build-type:                         Simple
+cabal-version:                      >= 1.10
+extra-source-files:                 README.md
+
+library
+  default-language:                 Haskell2010
+  ghc-options:                      -W -Wall
+  hs-source-dirs:                   src
+  build-depends:                    base >= 4.7 && < 5
+  exposed-modules:                  System.IO.HiddenChar
+  if os(linux)
+    cpp-options:                    -DOS_LINUX
+    other-modules:                  System.IO.HiddenChar.Posix
+  if os(windows)
+    cpp-options:                    -DOS_WINDOWS
+    other-modules:                  System.IO.HiddenChar.Windows
+  if os(darwin)
+    cpp-options:                    -DOS_MACOS
+    other-modules:                  System.IO.HiddenChar.Posix
+
+test-suite hidden-char-test
+  type:                             exitcode-stdio-1.0
+  default-language:                 Haskell2010
+  ghc-options:                      -W -Wall -threaded
+  hs-source-dirs:                   test
+  main-is:                          Main.hs
+  build-depends:                    base >= 4.7 && < 5
+                                  , hidden-char
+                                  , hspec
+  if os(linux)
+    cpp-options:                    -DOS_LINUX
+  if os(windows)
+    cpp-options:                    -DOS_WINDOWS
+  if os(darwin)
+    cpp-options:                    -DOS_MACOS
+  other-modules:                    HiddenCharSpec
diff --git a/src/System/IO/HiddenChar.hs b/src/System/IO/HiddenChar.hs
--- a/src/System/IO/HiddenChar.hs
+++ b/src/System/IO/HiddenChar.hs
@@ -1,26 +1,26 @@
-{-|
-Module      : System.IO.HiddenChar
-Description : Umbrella module for @System.IO.HiddenChar@
-Copyright   : (C) Richard Cook, 2017
-Licence     : MIT
-Maintainer  : rcook@rcook.org
-Stability   : stable
-Portability : portable
-
-This package provides a @getHiddenChar@ function which works reasonably
-consistently across the Windows, Linux and macOS platforms. @getHiddenChar@
-yields a single character from the standard input device with buffering and
-echoing to standard output disabled.
--}
-
-{-# LANGUAGE CPP #-}
-
-module System.IO.HiddenChar (getHiddenChar) where
-
-#if defined(OS_LINUX) || defined(OS_MACOS)
-import           System.IO.HiddenChar.Posix
-#elif defined(OS_WINDOWS)
-import           System.IO.HiddenChar.Windows
-#else
-#error Unsupported platform
-#endif
+{-|
+Module      : System.IO.HiddenChar
+Description : Umbrella module for @System.IO.HiddenChar@
+Copyright   : (C) Richard Cook, 2017
+Licence     : MIT
+Maintainer  : rcook@rcook.org
+Stability   : stable
+Portability : portable
+
+This package provides a @getHiddenChar@ function which works reasonably
+consistently across the Windows, Linux and macOS platforms. @getHiddenChar@
+yields a single character from the standard input device with buffering and
+echoing to standard output disabled.
+-}
+
+{-# LANGUAGE CPP #-}
+
+module System.IO.HiddenChar (getHiddenChar) where
+
+#if defined(OS_LINUX) || defined(OS_MACOS)
+import           System.IO.HiddenChar.Posix
+#elif defined(OS_WINDOWS)
+import           System.IO.HiddenChar.Windows
+#else
+#error Unsupported platform
+#endif
diff --git a/src/System/IO/HiddenChar/Posix.hs b/src/System/IO/HiddenChar/Posix.hs
--- a/src/System/IO/HiddenChar/Posix.hs
+++ b/src/System/IO/HiddenChar/Posix.hs
@@ -1,41 +1,41 @@
-{-|
-Module      : System.IO.HiddenChar.Posix
-Description : Posix implementation of @getHiddenChar@
-Copyright   : (C) Richard Cook, 2017
-Licence     : MIT
-Maintainer  : rcook@rcook.org
-Stability   : stable
-Portability : portable
--}
-
-module System.IO.HiddenChar.Posix (getHiddenChar) where
-
-import           Control.Exception
-import           System.IO
-
-data HandleState = HandleState BufferMode Bool
-
-hGetState :: Handle -> IO HandleState
-hGetState h = do
-    bufferMode <- hGetBuffering h
-    isEcho <- hGetEcho h
-    return $ HandleState bufferMode isEcho
-
-hSetState :: Handle -> HandleState -> IO ()
-hSetState h (HandleState mode isEcho) = do
-    hSetEcho h isEcho
-    hSetBuffering h mode
-
-bracketHandle :: Handle -> (IO a -> IO a)
-bracketHandle h action = bracket
-    (hGetState h)
-    (hSetState h)
-    (const action)
-
--- | Read a character from the standard input device with buffering and echoing disabled
-getHiddenChar ::
-    IO Char -- ^ returned character
-getHiddenChar = bracketHandle stdin $ do
-    hSetBuffering stdin NoBuffering
-    hSetEcho stdin False
-    getChar
+{-|
+Module      : System.IO.HiddenChar.Posix
+Description : Posix implementation of @getHiddenChar@
+Copyright   : (C) Richard Cook, 2017
+Licence     : MIT
+Maintainer  : rcook@rcook.org
+Stability   : stable
+Portability : portable
+-}
+
+module System.IO.HiddenChar.Posix (getHiddenChar) where
+
+import           Control.Exception
+import           System.IO
+
+data HandleState = HandleState BufferMode Bool
+
+hGetState :: Handle -> IO HandleState
+hGetState h = do
+    bufferMode <- hGetBuffering h
+    isEcho <- hGetEcho h
+    return $ HandleState bufferMode isEcho
+
+hSetState :: Handle -> HandleState -> IO ()
+hSetState h (HandleState mode isEcho) = do
+    hSetEcho h isEcho
+    hSetBuffering h mode
+
+bracketHandle :: Handle -> (IO a -> IO a)
+bracketHandle h action = bracket
+    (hGetState h)
+    (hSetState h)
+    (const action)
+
+-- | Read a character from the standard input device with buffering and echoing disabled
+getHiddenChar ::
+    IO Char -- ^ returned character
+getHiddenChar = bracketHandle stdin $ do
+    hSetBuffering stdin NoBuffering
+    hSetEcho stdin False
+    getChar
diff --git a/src/System/IO/HiddenChar/Windows.hs b/src/System/IO/HiddenChar/Windows.hs
--- a/src/System/IO/HiddenChar/Windows.hs
+++ b/src/System/IO/HiddenChar/Windows.hs
@@ -1,26 +1,26 @@
-{-|
-Module      : System.IO.HiddenChar.Windows
-Description : Windows implementation of @getHiddenChar@
-Copyright   : (C) Richard Cook, 2017
-Licence     : MIT
-Maintainer  : rcook@rcook.org
-Stability   : stable
-Portability : portable
--}
-
-{-# LANGUAGE ForeignFunctionInterface #-}
-
-module System.IO.HiddenChar.Windows (getHiddenChar) where
-
-import           Data.Char
-import           Foreign.C.Types
-
--- Must import as "safe" in order to prevent FFI call from blocking other threads
--- https://github.com/rcook/hidden-char/issues/1
-foreign import ccall safe "conio.h getch" c_getch :: IO CInt
-
--- | Read a character from the standard input device with buffering and echoing disabled
--- Hack based on http://stackoverflow.com/questions/2983974/haskell-read-input-character-from-console-immediately-not-after-newline
-getHiddenChar ::
-    IO Char -- ^ returned character
-getHiddenChar = fmap (chr . fromEnum) c_getch
+{-|
+Module      : System.IO.HiddenChar.Windows
+Description : Windows implementation of @getHiddenChar@
+Copyright   : (C) Richard Cook, 2017
+Licence     : MIT
+Maintainer  : rcook@rcook.org
+Stability   : stable
+Portability : portable
+-}
+
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+module System.IO.HiddenChar.Windows (getHiddenChar) where
+
+import           Data.Char
+import           Foreign.C.Types
+
+-- Must import as "safe" in order to prevent FFI call from blocking other threads
+-- https://github.com/rcook/hidden-char/issues/1
+foreign import ccall safe "conio.h _getch" c_getch :: IO CInt
+
+-- | Read a character from the standard input device with buffering and echoing disabled
+-- Hack based on http://stackoverflow.com/questions/2983974/haskell-read-input-character-from-console-immediately-not-after-newline
+getHiddenChar ::
+    IO Char -- ^ returned character
+getHiddenChar = fmap (chr . fromEnum) c_getch
diff --git a/test/HiddenCharSpec.hs b/test/HiddenCharSpec.hs
--- a/test/HiddenCharSpec.hs
+++ b/test/HiddenCharSpec.hs
@@ -1,45 +1,45 @@
-{-|
-Module      : HiddenCharSpec
-Description : Tests for @System.IO.HiddenChar@ module
-Copyright   : (C) Richard Cook, 2017
-Licence     : MIT
-Maintainer  : rcook@rcook.org
-Stability   : stable
-Portability : portable
--}
-
-{-# LANGUAGE ScopedTypeVariables #-}
-
-module HiddenCharSpec (spec) where
-
-import           Control.Concurrent
-import           Control.Exception
-import           System.IO.HiddenChar
-import           System.Timeout
-import           Test.Hspec
-
-spec :: Spec
-spec = do
-    describe "concurrent use of getHiddenChar" $
-        it "doesn't block other threads" $ do
-            -- Run getHiddenChar in a bound thread and swallow all exceptions
-            _ <- forkOS $ do
-                _ <- getHiddenChar `catch` (\(_ :: SomeException) -> return ' ')
-                return ()
-
-            -- On another bound thread, count up to a given value with a small
-            -- delay at each step. runInBoundThread should terminate and return
-            -- the expected value if getHiddenChar works correctly. In the presence
-            -- of calls to functions via the "unsafe", then this thread will block.
-            -- This is a behaviour we would like to avoid.
-            -- https://github.com/rcook/hidden-char/issues/1
-            let countUpTo :: Int -> IO Int
-                countUpTo n = helper n n
-                    where
-                        helper n' i
-                            | i == 0 = return n'
-                            | otherwise = do
-                                threadDelay 1
-                                helper n' (i - 1)
-            result <- timeout 1000000 $ runInBoundThread (countUpTo 5)
+{-|
+Module      : HiddenCharSpec
+Description : Tests for @System.IO.HiddenChar@ module
+Copyright   : (C) Richard Cook, 2017
+Licence     : MIT
+Maintainer  : rcook@rcook.org
+Stability   : stable
+Portability : portable
+-}
+
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module HiddenCharSpec (spec) where
+
+import           Control.Concurrent
+import           Control.Exception
+import           System.IO.HiddenChar
+import           System.Timeout
+import           Test.Hspec
+
+spec :: Spec
+spec = do
+    describe "concurrent use of getHiddenChar" $
+        it "doesn't block other threads" $ do
+            -- Run getHiddenChar in a bound thread and swallow all exceptions
+            _ <- forkOS $ do
+                _ <- getHiddenChar `catch` (\(_ :: SomeException) -> return ' ')
+                return ()
+
+            -- On another bound thread, count up to a given value with a small
+            -- delay at each step. runInBoundThread should terminate and return
+            -- the expected value if getHiddenChar works correctly. In the presence
+            -- of calls to functions via the "unsafe" FFI, then this thread will
+            -- block. This is a behaviour we would like to avoid.
+            -- https://github.com/rcook/hidden-char/issues/1
+            let countUpTo :: Int -> IO Int
+                countUpTo n = helper n n
+                    where
+                        helper n' i
+                            | i == 0 = return n'
+                            | otherwise = do
+                                threadDelay 1
+                                helper n' (i - 1)
+            result <- timeout 1000000 $ runInBoundThread (countUpTo 5)
             result `shouldBe` Just 5
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,11 +1,11 @@
-{-|
-Module      : Main
-Description : Main entrypoint for @hidden-char@ tests
-Copyright   : (C) Richard Cook, 2017
-Licence     : MIT
-Maintainer  : rcook@rcook.org
-Stability   : stable
-Portability : portable
--}
-
-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+{-|
+Module      : Main
+Description : Main entrypoint for @hidden-char@ tests
+Copyright   : (C) Richard Cook, 2017
+Licence     : MIT
+Maintainer  : rcook@rcook.org
+Stability   : stable
+Portability : portable
+-}
+
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
