diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for fswatch
+
+## 0.1.0.0  -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, -
+
+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 - 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/fswatch.cabal b/fswatch.cabal
new file mode 100644
--- /dev/null
+++ b/fswatch.cabal
@@ -0,0 +1,41 @@
+
+name:                fswatch
+version:             0.1.0.0
+synopsis:            File System watching tool with cli and slave functionalities.
+description:         File System watching tool with cli and slave lib (with os process) functionalities.
+category:            Filesystem
+homepage:            https://github.com/kelemzol/watch
+license:             BSD3
+license-file:        LICENSE
+author:              Zoltan Kelemen
+maintainer:          -
+build-type:          Simple
+extra-source-files:  ChangeLog.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     System.FSWatch
+                     , System.FSWatch.Repr
+                     , System.FSWatch.Slave
+  build-depends:       base >=4.9 && <4.10
+                     , haskeline >=0.7 && <0.8
+                     , directory >=1.3.0
+                     , fsnotify >=0.2.1
+                     , optparse-applicative >= 0.13.2
+                     , process >=1.4.3
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+executable hfswatch
+  ghc-options:         -threaded
+  main-is:             Main.hs
+  other-extensions:    ViewPatterns, RecordWildCards
+  build-depends:       base >=4.9 && <4.10
+                     , haskeline >=0.7 && <0.8
+                     , directory >=1.3.0
+                     , fsnotify >=0.2.1
+                     , optparse-applicative >= 0.13.2
+                     , process >=1.4.3
+                     , fswatch
+  hs-source-dirs:      src
+  default-language:    Haskell2010
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,9 @@
+
+module Main where
+
+import System.FSWatch (watchMain)
+
+
+main :: IO ()
+main = watchMain
+
diff --git a/src/System/FSWatch.hs b/src/System/FSWatch.hs
new file mode 100644
--- /dev/null
+++ b/src/System/FSWatch.hs
@@ -0,0 +1,254 @@
+
+{-# LANGUAGE ViewPatterns
+           , RecordWildCards
+           #-}
+
+module System.FSWatch where
+
+import Data.List
+import Data.Semigroup ((<>))
+
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Concurrent
+import Control.Concurrent.Chan
+import Control.Concurrent.MVar
+import Options.Applicative hiding (defaultPrefs)
+
+import System.Console.Haskeline
+import System.Console.Haskeline.History
+import System.Console.Haskeline.Completion
+import System.Directory
+import System.FSNotify
+import System.IO
+
+import System.FSWatch.Repr
+
+optParser :: Parser Opts
+optParser = Opts
+    <$> switch
+        ( long "slave"
+       <> help "cli - normal mode; slave - no buffering on std in/out, no prompt, one line records")
+    <*> option auto
+        ( long "fix-buffering"
+       <> metavar "NUMBER"
+       <> help "fix time loop; NUMBER in ms"
+       <> showDefault
+       <> value 0)
+    <*> option auto
+        ( long "delayed-buffering"
+       <> metavar "NUMBER"
+       <> help "delayed bufferint from last; NUMBER in ms"
+       <> showDefault
+       <> value 0)
+
+getOpts :: IO Opts
+getOpts = execParser opts
+  where
+    opts = info (optParser <**> helper)
+      ( fullDesc
+     <> progDesc "File watching tool"
+     <> header "[header]" )
+
+watchMain :: IO ()
+watchMain = do
+    opts <- getOpts
+    ch <- newChan
+    prompt <- newMVar "% "
+    printFormat <- newMVar MultiRecord
+    buffering <- newMVar $ case (oFixBufferMode opts, oDelayedBufferMode opts) of
+      (0,0) -> NoNotifyBuffer
+      (i,0) -> FixTimeBuffer i
+      (0,i) -> DelayedBuffer i
+    mode <- newMVar CLI
+    case oSlave opts of
+        True -> do
+            hSetNewlineMode stdin (NewlineMode LF LF)
+            hSetNewlineMode stdout (NewlineMode LF LF)
+            hSetNewlineMode stderr (NewlineMode LF LF)
+            hSetBuffering stdin NoBuffering
+            hSetBuffering stdout NoBuffering
+            hSetBuffering stderr NoBuffering
+            liftIO $ modifyMVarMasked_ prompt (\ _ -> return "")
+            liftIO $ modifyMVarMasked_ printFormat (\ _ -> return SingleRecord)
+            liftIO $ modifyMVarMasked_ mode (\ _ -> return SLAVE)
+        False -> return ()
+    killPrinter <- startPrinter (State {..}) printFormat ch
+    runInputTWithPrefs
+      defaultPrefs
+      Settings { complete = compl, historyFile = Nothing, autoAddHistory = False }
+      (loop (State {..}) (killPrinter, ch) [])
+  where
+    loop :: State -> P -> DB -> InputT IO ()
+    loop state@(State {..}) p db = do
+        promptStr <- liftIO $ readMVar prompt
+        minput <- getInputLine promptStr
+        case minput of
+            Nothing -> return ()
+            Just "" -> loop state p db
+            Just (words -> ["exit"])    -> do
+                liftIO (fst p)
+                printP p ""
+                return ()
+            Just (words -> ["watch", wfn]) -> do
+                wman <- watch p wfn
+                printP p ""
+                loop state p (DBE{..}:db)
+            Just (words -> ["list"]) -> do
+                list db
+                loop state p db
+            Just (words -> ["stop", fn]) -> do
+                stop p fn db
+                loop state p (filter (\ DBE{..} -> fn /= wfn) db)
+            Just (words -> ["history"]) -> getHistory >>= mapM_ outputStrLn . historyLines >> loop state p db
+            Just ('e':'c':'h':'o':' ':strs) -> do
+                printP p strs
+                loop state p db
+            Just (words -> ["buffering", "fix", parseInt -> (Just i)]) -> do
+                liftIO $ modifyMVarMasked_ buffering (\_-> return (FixTimeBuffer i))
+                loop state p db
+            Just (words -> ["buffering", "delayed", parseInt -> (Just i)]) -> do
+                liftIO $ modifyMVarMasked_ buffering (\_-> return (DelayedBuffer i))
+                loop state p db
+            Just (words -> ["no", "buffering"]) -> do
+                liftIO $ modifyMVarMasked_ buffering (\_-> return NoNotifyBuffer)
+                loop state p db
+            Just input -> do
+                printP p $ "not found command: `" ++ input ++ "`"
+                loop state p db
+
+parseInt :: String -> Maybe Int
+parseInt (reads -> [(i, _)]) = Just i
+parseInt _ = Nothing
+
+compl :: CompletionFunc IO
+compl = {- compl' -} completeWord Nothing {- (Just '\t') -} [] h
+  where
+    h "" = return [histC, exitC, wathC, listC, stopC, echoC, buffC, bufdC, nobuffC]
+    h ((`isPrefixOf` "history") -> True)    = return [histC]
+    h ((`isPrefixOf` "exit") -> True)       = return [exitC]
+    h ((`isPrefixOf` "watch") -> True)      = return [wathC]
+    h ((`isPrefixOf` "list") -> True)       = return [listC]
+    h ((`isPrefixOf` "stop") -> True)       = return [stopC]
+    h ((`isPrefixOf` "echo") -> True)       = return [echoC]
+    h (words -> [(`isPrefixOf` "buffering") -> True])                = return [buffC, bufdC]
+    h (words -> ["buffering", (`isPrefixOf` "fix") -> True])         = return [fixC]
+    h (words -> ["buffering", (`isPrefixOf` "delayed") -> True])     = return [delayedC]
+    h (words -> [(`isPrefixOf` "no") -> True])                       = return [nobuffC]
+    h (words -> ["no", (`isPrefixOf` "buffering") -> True])          = return [nobuff_C]
+    h _ = return []
+    histC =    Completion "history"            "history                    - display history" False
+    exitC =    Completion "exit"               "exit                       - exit" False
+    wathC =    Completion "watch"              "watch dir                  - starting watch the dir" True
+    listC =    Completion "list"               "list                       - list watched dirs" False
+    stopC =    Completion "stop"               "stop                       - stop whatching" True
+    echoC =    Completion "echo"               "echo                       - echo" True
+    buffC =    Completion "buffering "         "buffering fix [number]     - notify buffering in fix time (ms)" True
+    bufdC =    Completion "buffering "         "buffering delayed [number] - notify buffering in delayed time from last notify (ms)" True
+    nobuffC =  Completion "no"                 "no buffering               - no notify buffering" True
+    nobuff_C = Completion "no buffering"       "no buffering               - no notify buffering" False
+    fixC =     Completion "buffering fix"      "buffering fix [number]     - notify buffering in fix time (ms)" True
+    delayedC = Completion "buffering delayed"  "buffering delayed [number] - notify buffering in delayed time from last notify (ms)" True
+
+
+list :: DB -> InputT IO ()
+list = liftIO . putStrLn . unlines . map wfn
+
+watch :: P -> String -> InputT IO WatchManager
+watch p fn = liftIO $ do
+    man <- startManager
+    fn' <- canonicalizePath fn
+    watchTree
+        man
+        fn'
+        (const True)
+        (writeChan (snd p) . event2PE)
+    return man
+
+event2PE :: Event -> PE
+event2PE (Added str _) = Add str
+event2PE (Modified str _) = Mod str
+event2PE (Removed str _) = Rem str
+
+stop :: P -> String -> DB -> InputT IO ()
+stop p fn [] = printP p "no whatching this"
+stop p fn (DBE{..}:dbo) = if wfn /= fn then stop p fn dbo else liftIO (stopManager wman)
+
+startPrinter :: State -> MVar PrintFormat -> Chan PE -> IO (IO ())
+startPrinter (State {..}) pfm ch = do
+    mv <- newMVar []
+    noBufferClock <- newEmptyMVar
+    fixBufferClock <- newEmptyMVar
+    delayedBufferClock <- newEmptyMVar
+    bf <- readMVar buffering
+    lastBufferMode <- newMVar bf
+
+    t1 <- forkIO $ void $ forever $ do
+        e <- readChan ch
+        tryTakeMVar noBufferClock
+        tryTakeMVar fixBufferClock
+        tryTakeMVar delayedBufferClock
+        bf <- readMVar buffering
+        case bf of
+            NoNotifyBuffer -> putMVar noBufferClock ()
+            (FixTimeBuffer _) -> putMVar fixBufferClock ()
+            (DelayedBuffer _) -> putMVar delayedBufferClock ()
+        modifyMVarMasked_ mv (\ l -> return (e:l))
+
+    t2 <- forkIO $ void $ forever $ do
+        readMVar fixBufferClock
+        bf <- readMVar buffering
+        case bf of
+            (FixTimeBuffer i) -> do
+                threadDelay (1000*i)
+                printerOut (State {..}) pfm mv
+            _ -> return ()
+
+    t3 <- forkIO $ void $ forever $ do
+        takeMVar noBufferClock
+        bf <- readMVar buffering
+        case bf of
+            (NoNotifyBuffer) -> do
+                printerOut (State {..}) pfm mv
+            _ -> return ()
+
+    t4 <- forkIO $ void $ forever $ do
+        readMVar delayedBufferClock
+        bf <- readMVar buffering
+        case bf of
+            (DelayedBuffer i) -> do
+                printerOut (State {..}) pfm mv
+                elems <- readMVar mv
+                let loop elems = do
+                      threadDelay (1000*i)
+                      elems' <- readMVar mv
+                      if elems == elems'
+                        then printerOut (State {..}) pfm mv
+                        else loop elems'
+                loop elems
+            _ -> return ()
+
+    return $ do
+        killThread t1
+        killThread t2
+        killThread t3
+        killThread t4
+
+printerOut (State {..}) pfm mv = do
+    pf <- readMVar pfm
+    m <- readMVar mode
+    modifyMVarMasked_ mv $ \ l -> do
+        let prts = filter isPrt l
+            signals = nub (filter (not . isPrt) l)
+        case m of
+            CLI -> forM_ prts (hPutStr stdout . (++"\n") . fromPrt)
+            SLAVE -> return ()
+        case pf of
+            MultiRecord  -> forM_ signals (hPutStr stdout . (++"\n") . show)
+            SingleRecord -> if null signals then return () else hPutStr stdout ((show signals) ++"\n")
+        hFlush stdout
+        return []
+
+
+printP :: P -> String -> InputT IO ()
+printP (_,ch) = liftIO . writeChan ch . Prt
diff --git a/src/System/FSWatch/Repr.hs b/src/System/FSWatch/Repr.hs
new file mode 100644
--- /dev/null
+++ b/src/System/FSWatch/Repr.hs
@@ -0,0 +1,80 @@
+module System.FSWatch.Repr where
+
+import Data.List
+
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Concurrent
+import Control.Concurrent.Chan
+import Control.Concurrent.MVar
+
+import System.Console.Haskeline
+import System.Console.Haskeline.History
+import System.Console.Haskeline.Completion
+import System.Directory
+import System.FSNotify
+import System.IO
+import System.Environment
+import System.Process
+
+
+data WatchProcess = WatchProcess
+  { wPath :: String
+  , wProcessHandle :: ProcessHandle
+  , wStdin :: Handle
+  , wStdout :: Handle
+  , wNotifyMVar :: MVar [PE]
+  , wShutdown :: IO ()
+  }
+
+type Listener = PE -> IO ()
+
+data Opts = Opts
+  { oSlave :: Bool
+  , oFixBufferMode :: Int
+  , oDelayedBufferMode :: Int
+  }
+
+data State = State
+  { prompt :: MVar String
+  , printFormat :: MVar PrintFormat
+  , buffering :: MVar NotifyBuffering
+  , mode :: MVar Mode
+  }
+
+
+data DBE = DBE
+  { wman :: WatchManager
+  , wfn :: String
+  }
+
+type DB = [DBE]
+type P = (IO (), Chan PE)
+
+
+data PE
+  = Mod String
+  | Add String
+  | Rem String
+  | Prt { fromPrt :: String }
+  deriving (Eq, Show, Read)
+
+isPrt :: PE -> Bool
+isPrt (Prt _) = True
+isPrt _ = False
+
+data PrintFormat
+  = MultiRecord
+  | SingleRecord
+  deriving (Eq, Show)
+
+data Mode
+  = CLI
+  | SLAVE
+  deriving (Eq, Show)
+
+data NotifyBuffering
+  = NoNotifyBuffer
+  | FixTimeBuffer Int
+  | DelayedBuffer Int
+  deriving (Eq, Show)
diff --git a/src/System/FSWatch/Slave.hs b/src/System/FSWatch/Slave.hs
new file mode 100644
--- /dev/null
+++ b/src/System/FSWatch/Slave.hs
@@ -0,0 +1,78 @@
+
+
+{-# LANGUAGE ViewPatterns
+           , RecordWildCards
+           #-}
+
+module System.FSWatch.Slave where
+
+import Data.List
+import Data.Semigroup ((<>))
+
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Concurrent
+import Control.Concurrent.Chan
+import Control.Concurrent.MVar
+import Options.Applicative hiding (defaultPrefs)
+
+import System.Console.Haskeline
+import System.Console.Haskeline.History
+import System.Console.Haskeline.Completion
+import System.Directory
+import System.FSNotify
+import System.IO
+import System.Process
+
+import System.FSWatch.Repr
+
+
+createWatchProcess :: (MonadIO m) => String -> Int -> m WatchProcess
+createWatchProcess wPath dbi = createWatchProcessWL wPath dbi Nothing
+
+createWatchProcessWithListener :: (MonadIO m) => String -> Int -> Listener -> m WatchProcess
+createWatchProcessWithListener wPath dbi listener =createWatchProcessWL wPath dbi (Just listener)
+
+
+createWatchProcessWL :: (MonadIO m) => String -> Int -> Maybe Listener -> m WatchProcess
+createWatchProcessWL wPath dbi listener = liftIO $ do
+    (Just wStdin, Just wStdout, _, wProcessHandle)
+      <- createProcess (proc wPath ["--slave", "--delayed-buffering", show dbi]) { std_in = CreatePipe, std_out = CreatePipe }
+    hSetBuffering wStdin NoBuffering
+    hSetBuffering wStdout NoBuffering
+    hSetNewlineMode wStdin (NewlineMode LF LF)
+    hSetNewlineMode wStdout (NewlineMode LF LF)
+    wNotifyMVar <- newEmptyMVar
+    wPollerThreadId <- forkIO $ void $ forever $ do
+        line <- hGetLine wStdout
+        let recs = read line
+        case listener of
+            (Just lsnr) -> forM_ recs lsnr
+            _ -> return ()
+        ns <- tryTakeMVar wNotifyMVar
+        case ns of
+            (Just ns') -> putMVar wNotifyMVar (ns' ++ recs)
+            Nothing -> putMVar wNotifyMVar recs
+    let wShutdown = do
+            killThread wPollerThreadId
+            terminateProcess wProcessHandle
+    return WatchProcess {..}
+
+watch :: (MonadIO m) => WatchProcess -> FilePath -> m ()
+watch (WatchProcess {..}) fn = void $ liftIO $ do
+    hPutStrLn wStdin ("watch " ++ fn)
+
+stop :: (MonadIO m) => WatchProcess -> FilePath -> m ()
+stop (WatchProcess {..}) fn = void $ liftIO $ do
+    hPutStrLn wStdin ("stop " ++ fn)
+
+getNotifies :: WatchProcess -> IO [PE]
+getNotifies (WatchProcess {..}) = do
+    jpes <- tryTakeMVar wNotifyMVar
+    case jpes of
+        (Just pes) -> return pes
+        _ -> return []
+
+waitNotifies :: WatchProcess -> IO [PE]
+waitNotifies (WatchProcess {..}) = do
+    takeMVar wNotifyMVar
