diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+# 0.1.0.0
+
+* initial version
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Javran Cheng
+
+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,229 @@
+# xmonad-entryhelper
+
+[![Build Status](https://travis-ci.org/Javran/xmonad-entryhelper.svg?branch=master)](https://travis-ci.org/Javran/xmonad-entryhelper)
+[![Version](https://img.shields.io/hackage/v/xmonad-entryhelper.svg)](https://hackage.haskell.org/package/xmonad-entryhelper)
+
+xmonad-entryhelper makes your compiled XMonad config a standalone binary.
+
+It simulates the XMonad's argument handling
+and supports customized compliation.
+
+**Table of Contents**
+
+- [Introduction](#introduction)
+- [Simple setup](#simple-setup)
+- [Argument handling](#argument-handling)
+- [Advanced features](#advanced-features)
+- [Feedback](#feedback)
+
+## Introduction
+
+xmonad-entryhelper frees you from keeping a xmonad library as a system- or user- level dependency.
+Instead, you can keep your XMonad configurations either as a local
+[cabal project](https://www.haskell.org/cabal/) using
+[cabal sandbox](https://www.fpcomplete.com/school/to-infinity-and-beyond/older-but-still-interesting/an-introduction-to-cabal-sandboxes-copy) or within
+a protected environment like those created by [hsenv](https://github.com/tmhedberg/hsenv)
+
+## Simple setup
+
+1. After installation, modify your `xmonad.hs` accordingly:
+
+    Your xmonad config might look like:
+
+        -- some imports here
+        import ...
+
+        ...
+
+        -- your main entry point
+        main :: IO ()
+        main = _
+
+    Rename your `main` to something else, import `withHelper` from `XMonad.Util.EntryHelper`
+    and use it to call your old `main`:
+
+        -- some imports here
+        import ...
+        import XMonad.Util.EntryHelper (withHelper)
+
+        ...
+
+        -- your old main entry point
+        oldMain :: IO ()
+        oldMain = _
+
+        -- your new main entry point
+        main :: IO ()
+        main = withHelper oldMain
+
+    It is recommended to set the "restart xmonad" action (typically `mod-q` in your keybinding)
+    to just invoke `xmonad --restart`. Although the default action,
+    essentially `xmonad --recompile && xmonad --restart` should work properly,
+    argument `--recompile` forces the compilation (which might involve
+    removing all binaries and compiling everything).
+    If you are using a build system like `make` or `cabal`,
+    forcing a compilation might not be a desired behavior as build systems are in general designed
+    to prevent recompilation.
+
+2. Finally you need to have a writable local `PATH` directory.
+
+    For example you can make directory `$HOME/bin`:
+
+        mkdir -p ~/bin
+
+    And append the following lines in your shell profile file
+    (it's usually your `~/.bash_profile` file):
+
+        ...
+        # my local executable files
+        export PATH="${HOME}/bin:${PATH}"
+
+    Create soft link to your compiled `xmonad` binary:
+
+        # the binary name varies depending on your OS and architecture,
+        # check your ~/.xmonad/ directory to find out
+        $ ln -s ~/.xmonad/xmonad-x86_64-linux ~/bin/xmonad
+
+    And verify if `xmonad` is now leading to your compiled xmonad config:
+
+        $ source ~/.profile
+        $ which xmonad
+        /home/username/bin/xmonad
+
+    If this doesn't work, check articles like
+    [Zsh/Bash startup files loading order](https://shreevatsa.wordpress.com/2008/03/30/zshbash-startup-files-loading-order-bashrc-zshrc-etc/)
+    for troubleshooting.
+
+3. Now you are free to remove XMonad from your system- or user- level packages.
+Because your compiled XMonad will work on its own:
+
+        $ xmonad --help
+        xmonad-entryhelper - XMonad config entry point wrapper
+
+        Usage: xmonad [OPTION]
+        Options:
+          --help                       Print this message
+          --version                    Print XMonad's version number
+          --recompile                  Recompile XMonad
+          --replace                    Replace the running window manager with XMonad
+          --restart                    Request a running XMonad process to restart
+
+## Argument handling
+
+Although this projects tries to resemble the argument handling behavior of XMonad,
+there are not exact the same. The differences are:
+
+* When invoked without argument or with `--replace` or `--resume` (this argument is not documented,
+assumably intended for internal use only):
+
+    * XMonad always does up-to-date checking internally and compile source codes as needed before
+      invocations
+
+    * EntryHelper doesn't do up-to-date checking.
+
+        (TL;DR) This is because XMonad is using the following routing when executed:
+
+        1. XMonad started
+        2. Check for source files and recompile as needed
+        3. Execute the compiled binary
+        4. If any goes wrong, start xmonad using default configuration
+
+        This routing only works if XMonad binary and the binary compiled by XMonad
+        are two different programs. But as EntryHelper wants to make your compiled
+        binary and the XMonad program the same file, the same routing will cause
+        an infinite loop (`start => check => execute => start ...`).
+
+        To solve this problem, in EntryHelper the up-to-date checking
+        is considered one part of the compilation. And the compilation
+        will not be executed unless `--recompile` or `--restart` is given.
+
+        Additionally, if you are using a build system like `make` or `cabal` to handle compilation,
+        leaving the job of up-to-date checking
+        to the build system would be the simplest approach.
+
+* When invoked with `--restart`:
+
+    * EntryHelper will try to recompile (without forcing) before sending the request
+    * both XMonad and EntryHelper send the restart request
+
+## Advanced features
+
+* Customized compilation and post-compilation handling
+
+    By passing a `Config` (from `XMonad.Util.EntryHelper.Config`) to `withCustomHelper`,
+    it is possible to customize the compilation and post-compilation actions.
+    Read document of `Config` for detail.
+
+* Customized shell command compilation
+
+    You can invoke an arbitrary shell command to do the compilation using
+    `compileUsingShell` from `XMonad.Util.EntryHelper.Compile`, the working
+    directory for this shell command will be `~/.xmonad` and its `stdout` and `stderr`
+    outputs will be redirected into `~/.xmonad/xmonad.errors`.
+
+    Assuming you have set your environment variable `${XMONAD_HOME}`
+    to point to the project home directory,
+    and you are using `Makefile` to handle the compilation,
+    the following example should work for you:
+
+        import qualified XMonad.Util.EntryHelper as EH
+
+        main :: IO ()
+        main = EH.withCustomHelper mhConf
+          where
+            mhConf = EH.defaultConfig
+                     { EH.run = oldMain
+                     , EH.compile = \force -> do
+                             let cmd = if force
+                               then "cd ${XMONAD_HOME} && make clean && make all"
+                               else "cd ${XMONAD_HOME} && make all"
+                             EH.compileUsingShell cmd
+                     }
+
+* Parallel compilation protection
+
+    You might find `withLock` from `XMonad.Util.EntryHelper.Compile` useful
+    to prevent yourself from parallel compilation
+    (this is usually caused by hitting `mod-q` rapidly multiple times...).
+    It creates a temprary file (typically `/tmp/xmonad.{username}.lock`) before compiling
+    and deletes it after the compilation is done. When this temprary file exists,
+    no other protected action with the same
+    file lock is allowed to proceed and will return with a default value.
+
+    To protect an `action` from parallel execution, all you have to do is to
+    replace it with `withLock def action`, with `def` being a default value to return
+    when it hits a file lock.
+
+    Continue from the previous example:
+
+        import qualified XMonad.Util.EntryHelper as EH
+
+        main :: IO ()
+        main = EH.withCustomHelper mhConf
+          where
+            mhConf = EH.defaultConfig
+                     { EH.run = oldMain
+                     -- adding "EH.withLock ExitSuccess $"
+                     , EH.compile = \force -> EH.withLock ExitSuccess $ do
+                             let cmd = if force
+                               then "cd ${XMONAD_HOME} && make clean && make all"
+                               else "cd ${XMONAD_HOME} && make all"
+                             EH.compileUsingShell cmd
+                     }
+
+    Be careful not to protect an action more than once.
+
+* Sending restart request to current xmonad instance
+
+    `sendRestart` from `XMonad.Util.EntryHelper.Util` is an exact copy of the same
+    function found in XMonad (unfortunately XMonad doesn't export it).
+    Simply calling this function will sent a restart request to the running XMonad
+    instance.
+
+    Note that XMonad restarts by looking for the compiled binary to replace it,
+    which means the binary file (e.g. `~/.xmonad/xmonad-x86_64-linux`) has to exist
+    or otherwise your window manager session will crash.
+
+## Feedback
+
+Feel free to open issues for either bug report, enhancement or discussion.
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/src/XMonad/Util/EntryHelper.hs b/src/XMonad/Util/EntryHelper.hs
new file mode 100644
--- /dev/null
+++ b/src/XMonad/Util/EntryHelper.hs
@@ -0,0 +1,28 @@
+{-|
+  Copyright   : (c) 2015 Javran Cheng
+  License     : MIT
+  Maintainer  : Javran.C@gmail.com
+  Stability   : unstable
+  Portability : non-portable (requires X11)
+
+  Re-exporting submodules
+
+-}
+module XMonad.Util.EntryHelper
+  ( module XMonad.Util.EntryHelper.Util
+  , module XMonad.Util.EntryHelper.Compile
+  , module XMonad.Util.EntryHelper.File
+  , module XMonad.Util.EntryHelper.Config
+  , module XMonad.Util.EntryHelper.Generated
+  ) where
+
+import XMonad.Util.EntryHelper.Util
+import XMonad.Util.EntryHelper.Compile
+import XMonad.Util.EntryHelper.File
+import XMonad.Util.EntryHelper.Config
+import XMonad.Util.EntryHelper.Generated
+
+-- import/export shortcut is not justified here
+-- because it blocks document generation
+
+{-# ANN module "HLint: ignore Use import/export shortcut" #-}
diff --git a/src/XMonad/Util/EntryHelper/Compile.hs b/src/XMonad/Util/EntryHelper/Compile.hs
new file mode 100644
--- /dev/null
+++ b/src/XMonad/Util/EntryHelper/Compile.hs
@@ -0,0 +1,130 @@
+{-|
+  Copyright   : (c) 2015 Javran Cheng
+  License     : MIT
+  Maintainer  : Javran.C@gmail.com
+  Stability   : unstable
+  Portability : non-portable (requires X11)
+
+  Compiling-related functions
+
+-}
+module XMonad.Util.EntryHelper.Compile
+  ( defaultCompile
+  , defaultPostCompile
+  , compileUsingShell
+  , withFileLock
+  , withLock
+  ) where
+
+import Control.Applicative
+import System.IO
+import System.Posix.Process
+import System.Process
+import Control.Exception.Extensible
+import System.Exit
+import System.FilePath
+import System.Directory
+import Data.List
+import System.Posix.User
+
+import XMonad.Util.EntryHelper.File
+import XMonad.Util.EntryHelper.Util
+
+-- | the default compiling action.
+--   checks whether any of the sources files under @"~\/.xmonad\/"@
+--   is newer than the binary and recompiles XMonad if so.
+defaultCompile :: Bool -> IO ExitCode
+defaultCompile force = do
+    b <- isSourceNewer
+    if force || b
+      then do
+        bin <- binPath <$> getXMonadPaths
+        let cmd = "ghc --make xmonad.hs -i -ilib -fforce-recomp -o " ++ bin
+        compileUsingShell cmd
+      else return ExitSuccess
+
+-- | the default post-compiling action.
+--   prints out error log to stderr and pops up a message
+--   when the last compilation has failed.
+defaultPostCompile :: ExitCode -> IO ()
+defaultPostCompile ExitSuccess = return ()
+defaultPostCompile st@(ExitFailure _) = do
+    ghcErr <- readFile =<< getXMonadLog
+    src <- getXMonadSrc
+    let msg = unlines $
+              [ "Error detected while loading xmonad configuration file: " ++ src]
+              ++ lines (if null ghcErr then show st else ghcErr)
+              ++ ["","Please check the file for errors."]
+    hPutStrLn stderr msg
+    _ <- forkProcess $ executeFile "xmessage" True ["-default", "okay", msg] Nothing
+    return ()
+
+-- | @compileUsingShell cmd@ spawns a new process to run a shell command
+--   (shell expansion is applied).
+--   The working directory of the shell command is @"~\/.xmonad\/"@, and
+--   the process' stdout and stdout are redirected to @"~\/.xmonad\/xmonad.errors"@
+compileUsingShell :: String -> IO ExitCode
+compileUsingShell cmd = do
+    -- please make sure "installSignalHandlers" hasn't been executed
+    -- or has been undone by "uninstallSignalHandlers"
+    -- see also: https://ghc.haskell.org/trac/ghc/ticket/5212
+    dir <- getXMonadDir
+    compileLogPath <- getXMonadLog
+    hNullInput <- openFile "/dev/null" ReadMode
+    hCompileLog <- openFile compileLogPath WriteMode
+    hSetBuffering hCompileLog NoBuffering
+    let cp = (shell cmd)
+               { cwd     = Just dir
+               , std_in  = UseHandle hNullInput
+               , std_out = UseHandle hCompileLog
+               , std_err = UseHandle hCompileLog
+               }
+    -- std_out and std_err are closed automatically
+    -- so we don't need to take care of them.
+    (_,_,_,ph) <- createProcess cp
+    waitForProcess ph
+
+-- | @withLock def action@ is the same as @withFileLock fpath def action@ with
+--   @fpath@ being @"xmonad.${USERNAME}.lock"@ under your temporary directory.
+--   Wrapping an action with more than one @withLock@ will not work.
+--
+--   See also: `withFileLock`, 'getTemporaryDirectory', 'getEffectiveUserName'
+withLock :: a -> IO a -> IO a
+withLock def action = do
+    tmpDir <- getTemporaryDirectory
+    -- https://ghc.haskell.org/trac/ghc/ticket/1487
+    -- avoid using "getLoginName" here
+    usr <- getEffectiveUserName
+    let lockFile = tmpDir </> intercalate "." ["xmonad",usr,"lock"]
+    withFileLock lockFile def action
+
+-- | prevents an IO action from parallel execution by using a lock file.
+--   @withFileLock fpath def action@ checks whether the file indicated by @fpath@
+--   exists. And:
+--
+--   * returns @def@ if the file exists.
+--   * creates @fpath@, executes the action, and deletes @fpath@ when the action
+--     has completed. If @action@ has failed, @def@ will be returned instead.
+--
+--   Note that:
+--
+--   * the action will be protected by 'safeIO', meaning the lock file will be deleted
+--     regardless of any error.
+--   * No check on @fpath@ will be done by this function. Please make sure the lock file
+--     does not exist.
+--   * please prevent wrapping the action with same file lock multiple times,
+--     in which case the action will never be executed.
+withFileLock :: FilePath -> a -> IO a -> IO a
+withFileLock fPath def action = do
+    lock <- doesFileExist fPath
+    if lock
+      then skipCompile
+      else doCompile
+  where
+    skipCompile = do
+        putStrLn $ "Lock file " ++ fPath ++ " found, aborting ..."
+        putStrLn   "Delete lock file to continue."
+        return def
+    doCompile = bracket_ (writeFile fPath "")
+                         (removeFile fPath)
+                         (safeIO def action)
diff --git a/src/XMonad/Util/EntryHelper/Config.hs b/src/XMonad/Util/EntryHelper/Config.hs
new file mode 100644
--- /dev/null
+++ b/src/XMonad/Util/EntryHelper/Config.hs
@@ -0,0 +1,141 @@
+{-|
+  Copyright   : (c) 2015 Javran Cheng
+  License     : MIT
+  Maintainer  : Javran.C@gmail.com
+  Stability   : unstable
+  Portability : non-portable (requires X11)
+
+  Configuration for EntryHelper
+
+-}
+module XMonad.Util.EntryHelper.Config
+  ( Config(..)
+  , defaultConfig
+  , withHelper
+  , withCustomHelper
+  ) where
+
+import System.Environment
+import System.Exit
+import System.Info
+import Data.Version (showVersion)
+import Graphics.X11.Xinerama (compiledWithXinerama)
+import XMonad.Main
+import XMonad.Core hiding (recompile)
+
+import qualified XMonad.Config as XMC
+
+import XMonad.Util.EntryHelper.Generated
+import XMonad.Util.EntryHelper.Util
+import XMonad.Util.EntryHelper.Compile
+
+-- | the configuration for EntryHelper.
+--
+--   * @run@ should execute XMonad using a customized configuration.
+--   * @compile force@ should compile the source file and return a value which
+--     will lately be consumed by @postCompile@. @force@ is just a hint about whether
+--     the compilation should be forced. @compile@ is free to ignore it and do up-to-date check
+--     on its own.
+--   * @postCompile val@ should take action according to the @val@, usually produced by @compile@
+--
+--   Note that:
+--
+--   * @compile@ should create a new process for compilation, as otherwise things like `executeFile`
+--     will replace the current process image with a new process image, make it impossible
+--     for @postCompile@ to invoke.
+--   * @force@ is just a hint about whether the compilation should be forced.
+--     and @compile@ is free to ignore it and do up-to-date checking on its own.
+--   * don't remove the binary file when the compilation has failed, as XMonad restart relies
+--     on it.
+data Config a = Config
+  { run         :: IO ()        -- ^ the action for executing XMonad
+  , compile     :: Bool -> IO a -- ^ the action for compiling XMonad
+  , postCompile :: a -> IO ()   -- ^ the action after compiling XMonad
+  }
+
+-- | default config for xmonad-entryhelper,
+--   invokes xmonad with its default config file
+defaultConfig :: Config ExitCode
+defaultConfig = Config
+  { run = xmonad XMC.defaultConfig
+  , compile = defaultCompile
+  , postCompile = defaultPostCompile
+  }
+
+{-
+-- I don't think this is necessary,
+-- as users can use "xmonad config" or just their main entries for "run"
+-- depending on their needs.
+
+class Executable r where
+    execute :: r -> IO ()
+
+instance (LayoutClass l Window, Read (l Window)) => Executable (XConfig l) where
+    execute = xmonad
+
+instance Executable a => Executable (IO a) where
+    execute a = a >>= execute
+
+instance Executable () where
+    execute = const (return ())
+-}
+
+-- | @withHelper e@ is the same as calling `withCustomHelper` with default
+--   @compile@ and @postCompile actions@
+--
+--   Either of the following will work:
+--
+--   * replace your main entry with @main = withHelper yourOldMain@
+--   * use @main = withHelper (xmonad cfg)@ if you have only customized your `XConfig`
+withHelper :: IO () -> IO ()
+withHelper e = withCustomHelper defaultConfig { run = e }
+
+-- | simulates the way that XMonad handles its command line arguments.
+--
+--   * when called with no argument, the action in @run@ will be used
+--   * when called with a string prefixed with @"--resume"@, or when called with
+--     @"--replace"@, the action in @run@ will be used
+--   * when called with @"--recompile"@ or @"--restart"@, 'compile' will be called. And 'postCompile'
+--     will handle the results from compliation.
+--   * additionally when called with @"--restart"@ a restart request will be sent to the current
+--     XMonad instance after the compilation regardless of the compilation result.
+withCustomHelper :: Config a -> IO ()
+withCustomHelper cfg = do
+    -- since XMonad has hard-coded to call "xmonad --resume{..}" on restart
+    -- I guess there isn't much we can do to have more command line functionalities
+    -- TODO: a pontential plan might be:
+    -- try to simulate "xmonad" when the program name is "xmonad",
+    -- and otherwise an alternative command line argument parsing strategy will be applied.
+    args <- getArgs
+    let launch = installSignalHandlers >> run cfg
+        recompile force = compile cfg force >>= postCompile cfg
+    case args of
+        []                    -> launch
+        ("--resume":_)        -> launch
+        ["--help"]            -> printHelp
+        ["--recompile"]       -> recompile True
+        ["--replace"]         -> launch
+        ["--restart"]         -> safeIO () (recompile False) >> sendRestart
+        ["--version"]         -> putStrLn $ unwords shortVersion
+        ["--verbose-version"] -> putStrLn . unwords $ shortVersion ++ longVersion
+        _                     -> printHelp >> exitFailure
+ where
+    shortVersion = [ "xmonad", xmonadVersion ]
+    longVersion  = [ "compiled by", compilerName, showVersion compilerVersion
+                   , "for",  arch ++ "-" ++ os
+                   , "\nXinerama:", show compiledWithXinerama ]
+
+printHelp :: IO ()
+printHelp = do
+    self <- getProgName
+    putStr . unlines $
+      [ "xmonad-entryhelper - XMonad config entry point wrapper"
+      , ""
+      , "Usage: " ++ self ++ " [OPTION]"
+      , "Options:"
+      , "  --help                       Print this message"
+      , "  --version                    Print XMonad's version number"
+      , "  --recompile                  Recompile XMonad"
+      , "  --replace                    Replace the running window manager with XMonad"
+      , "  --restart                    Request a running XMonad process to restart"
+      ]
diff --git a/src/XMonad/Util/EntryHelper/File.hs b/src/XMonad/Util/EntryHelper/File.hs
new file mode 100644
--- /dev/null
+++ b/src/XMonad/Util/EntryHelper/File.hs
@@ -0,0 +1,77 @@
+{-|
+  Copyright   : (c) 2015 Javran Cheng
+  License     : MIT
+  Maintainer  : Javran.C@gmail.com
+  Stability   : unstable
+  Portability : non-portable (requires X11)
+
+  Dealing with XMonad-related files
+
+-}
+module XMonad.Util.EntryHelper.File
+  ( XMonadPaths(..)
+  , getXMonadDir
+  , getXMonadPaths
+  , getXMonadBin
+  , getXMonadLog
+  , getXMonadSrc
+  , getXMonadLibDir
+  , isSourceNewer
+  ) where
+
+import Control.Applicative
+import System.FilePath
+import qualified XMonad.Core as XC
+import System.Info
+import System.Directory
+
+import XMonad.Util.EntryHelper.Util
+
+-- | XMonad default file paths
+data XMonadPaths = XMonadPaths
+  { dirPath    :: FilePath -- ^ directory path
+  , binPath    :: FilePath -- ^ compiled binary path
+  , logPath    :: FilePath -- ^ error log path
+  , srcPath    :: FilePath -- ^ source file path (the path to "xmonad.hs")
+  , libDirPath :: FilePath -- ^ directory path for library files
+  } deriving (Show)
+
+-- | gets information about XMonad-related paths
+getXMonadPaths :: IO XMonadPaths
+getXMonadPaths = (XMonadPaths
+  <$> id                              -- dir
+  <*> (</> "xmonad-"++arch++"-"++os)  -- bin
+  <*> (</> "xmonad.errors")           -- log
+  <*> (</> "xmonad.hs")               -- src
+  <*> (</> "lib")) <$> getXMonadDir   -- lib
+
+-- | gets information about XMonad-related paths, see also: 'XMonadPaths'
+getXMonadBin, getXMonadLog, getXMonadSrc, getXMonadLibDir, getXMonadDir :: IO FilePath
+
+getXMonadDir = XC.getXMonadDir
+getXMonadBin = binPath <$> getXMonadPaths
+getXMonadLog = logPath <$> getXMonadPaths
+getXMonadSrc = srcPath <$> getXMonadPaths
+getXMonadLibDir = libDirPath <$> getXMonadPaths
+
+-- | returns true only when any of the followings is true:
+--
+--     * any of the source files under xmonad's default directory is newer than the binary
+--     * the binary does not exist
+isSourceNewer :: IO Bool
+isSourceNewer = do
+    paths <- getXMonadPaths
+    let bin = binPath paths
+        src = srcPath paths
+        lib = libDirPath paths
+    libTs <- mapM getModTime . filter isHaskellSourceFile =<< allFiles lib
+    srcT <- getModTime src
+    binT <- getModTime bin
+    -- should be at least one element in (srcT: libTs)
+    -- and "Just _" is always greater than "Nothing"
+    -- therefore, this procedure returns true when one of the following happens:
+    -- - when the binary file doesn't exist
+    -- - when there are some source files newer than the binary
+    return $ any (binT <) (srcT : libTs)
+  where
+    getModTime = safeIO' . getModificationTime
diff --git a/src/XMonad/Util/EntryHelper/Generated.hs b/src/XMonad/Util/EntryHelper/Generated.hs
new file mode 100644
--- /dev/null
+++ b/src/XMonad/Util/EntryHelper/Generated.hs
@@ -0,0 +1,22 @@
+{-|
+  Copyright   : (c) 2015 Javran Cheng
+  License     : MIT
+  Maintainer  : Javran.C@gmail.com
+  Stability   : unstable
+  Portability : non-portable (requires X11)
+
+  Information generated from cabal macros
+
+-}
+{-# LANGUAGE CPP #-}
+module XMonad.Util.EntryHelper.Generated
+  ( xmonadVersion
+  ) where
+
+-- | the XMonad version compiled with this library
+xmonadVersion :: String
+#ifdef VERSION_xmonad
+xmonadVersion = VERSION_xmonad
+#else
+xmonadVersion = "(unknown version)"
+#endif
diff --git a/src/XMonad/Util/EntryHelper/Util.hs b/src/XMonad/Util/EntryHelper/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/XMonad/Util/EntryHelper/Util.hs
@@ -0,0 +1,60 @@
+{-|
+  Copyright   : (c) 2015 Javran Cheng
+  License     : MIT
+  Maintainer  : Javran.C@gmail.com
+  Stability   : unstable
+  Portability : non-portable (requires X11)
+
+  Miscellaneous utilities for safe IO action, scanning files, sending X events, etc.
+
+-}
+module XMonad.Util.EntryHelper.Util
+  ( safeIO
+  , safeIO'
+  , isHaskellSourceFile
+  , allFiles
+  , sendRestart
+  ) where
+
+import Control.Applicative
+import Control.Monad
+import Control.Exception.Extensible
+import System.Directory
+import System.FilePath
+import Graphics.X11.Xlib
+import Graphics.X11.Xlib.Extras
+import Data.List
+
+-- | performs an IO action and captures all the exceptions,
+--   a default value is returned when there are exceptions.
+safeIO :: a -> IO a -> IO a
+safeIO def action =
+    catch action (\(SomeException _) -> return def)
+
+-- | performs an IO action and wraps the resulting value in Maybe
+safeIO' :: IO a -> IO (Maybe a)
+safeIO' action = safeIO Nothing (Just <$> action)
+
+-- | checks if a file name is a Haskell source file
+isHaskellSourceFile :: FilePath -> Bool
+isHaskellSourceFile = (`elem` words ".hs .lhs .hsc") . takeExtension
+
+-- | gets a list of all files under a given directory and its subdirectories
+allFiles :: FilePath -> IO [FilePath]
+allFiles t = do
+    let prep = map (t </>) . filter (`notElem` [".", ".."])
+    cs <- prep <$> safeIO [] (getDirectoryContents t)
+    ds <- filterM doesDirectoryExist cs
+    concat . ((cs \\ ds):) <$> mapM allFiles ds
+
+-- | sends restart request to the current XMonad instance
+sendRestart :: IO ()
+sendRestart = do
+    dpy <- openDisplay ""
+    rw <- rootWindow dpy $ defaultScreen dpy
+    xmonad_restart <- internAtom dpy "XMONAD_RESTART" False
+    allocaXEvent $ \e -> do
+        setEventType e clientMessage
+        setClientMessageEvent e rw xmonad_restart 32 0 currentTime
+        sendEvent dpy rw False structureNotifyMask e
+    sync dpy False
diff --git a/xmonad-entryhelper.cabal b/xmonad-entryhelper.cabal
new file mode 100644
--- /dev/null
+++ b/xmonad-entryhelper.cabal
@@ -0,0 +1,51 @@
+name:                xmonad-entryhelper
+version:             0.1.0.0
+synopsis:            XMonad config entry point wrapper
+description:
+  xmonad-entryhelper makes your compiled XMonad config a standalone binary.
+  .
+  It simulates the XMonad's argument handling and supports customized compliation.
+  .
+  Please check
+  <https://github.com/Javran/xmonad-entryhelper/blob/master/README.md README>
+  for details.
+
+homepage:            https://github.com/Javran/xmonad-entryhelper
+bug-reports:         https://github.com/Javran/xmonad-entryhelper/issues
+license:             MIT
+license-file:        LICENSE
+author:              Javran Cheng
+maintainer:          Javran.c@gmail.com
+copyright:           Copyright (c) 2015 Javran Cheng
+category:            XMonad
+build-type:          Simple
+extra-source-files:  README.md, CHANGELOG.md
+cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: https://github.com/Javran/xmonad-entryhelper.git
+
+library
+  exposed-modules:
+                       XMonad.Util.EntryHelper
+                       XMonad.Util.EntryHelper.Util
+                       XMonad.Util.EntryHelper.Compile
+                       XMonad.Util.EntryHelper.File
+                       XMonad.Util.EntryHelper.Config
+                       XMonad.Util.EntryHelper.Generated
+
+  build-depends:       base <5,
+                       mtl,
+                       extensible-exceptions,
+                       unix,
+                       filepath,
+                       process,
+                       directory,
+                       X11,
+                       xmonad,
+                       xmonad-contrib
+
+  hs-source-dirs:      src
+  ghc-options:         -Wall
+  default-language:    Haskell2010
