diff --git a/Commando.cabal b/Commando.cabal
new file mode 100644
--- /dev/null
+++ b/Commando.cabal
@@ -0,0 +1,23 @@
+name:                Commando
+version:             0.1.0.0
+synopsis:            Watch some files; Rerun a command
+description:         Watch some files; Rerun a command
+homepage:            https://github.com/sordina/Commando
+license:             MIT
+license-file:        LICENSE
+author:              Lyndon Maydwell
+maintainer:          lyndon@sordina.net
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.8
+
+executable commando
+  main-is: Commando.hs
+  build-depends:
+    base                   >= 4.0    && < 5.0,
+    text                   >= 0.10   && < 0.12,
+    system-filepath        >= 0.4    && < 0.5,
+    optparse-applicative   >= 0.5    && < 0.6,
+    system-fileio          >= 0.3    && < 0.4,
+    fsnotify               >= 0.0.10 && < 0.1,
+    process                >= 1.1    && < 1.2
diff --git a/Commando.hs b/Commando.hs
new file mode 100644
--- /dev/null
+++ b/Commando.hs
@@ -0,0 +1,101 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Prelude hiding            (FilePath)
+import Control.Monad             (void, when)
+import System.Process            (rawSystem, runCommand, runInteractiveCommand)
+import System.FSNotify           (startManager, watchTree, stopManager)
+import Filesystem.Path.CurrentOS (FilePath, fromText)
+import Data.Text                 (pack)
+import System.IO                 (hPutStrLn, hGetContents, hSetBuffering, BufferMode(..))
+import GHC.IO.Handle             (hClose, hFlush)
+import GHC.IO.Handle.Types       (Handle)
+import System.Process.Internals  (ProcessHandle)
+import Control.Applicative       ((<$>), (<*>))
+import Data.Monoid               (mempty, Monoid, (<>))
+import Control.Concurrent        (forkIO)
+import Data.Maybe                (fromMaybe)
+
+import qualified Options.Applicative.Builder.Internal as X
+import qualified Options.Applicative                  as O
+
+type RunningProcess = (Handle, Handle, Handle, ProcessHandle)
+
+data Options = Options { command   :: String
+                       , quiet     :: Bool
+                       , consumer  :: Bool
+                       , stdin     :: Bool
+                       , persist   :: Bool
+                       , directory :: FilePath
+                       } deriving Show
+
+main :: IO ()
+main = O.execParser (O.info (O.helper <*> options) mempty) >>= start
+
+options :: O.Parser Options
+options = Options
+  <$> defStr "echo event" ( O.metavar "COMMAND"              <> O.help "Command run on events")
+  <*> O.switch            ( O.short 'q' <> O.long "quiet"    <> O.help "Hide non-essential output")
+  <*> O.switch            ( O.short 'c' <> O.long "consumer" <> O.help "Pass events as argument to command")
+  <*> O.switch            ( O.short 'i' <> O.long "stdin"    <> O.help "Pipe events to command")
+  <*> O.switch            ( O.short 'p' <> O.long "persist"  <> O.help "Pipe events to persistent command")
+  <*> (dir <$> defStr "." ( O.metavar "DIRECTORY"            <> O.help "Directory to monitor" ))
+
+defStr :: String -> X.Mod X.ArgumentFields String -> O.Parser String
+defStr a = def a . O.argument O.str
+
+def :: a -> O.Parser a -> O.Parser a
+def a = fmap (fromMaybe a) . O.optional
+
+dir :: String -> FilePath
+dir = fromText . pack
+
+start :: Options -> IO ()
+start o = do
+  man <- startManager
+  rc  <- if persist o then Just <$> startPipe (command o)
+                      else return Nothing
+
+  void $ forkIO $ whenM rc $ \(_,so,_,_) -> hGetContents so >>= putStr
+
+  when (not $ quiet o) $ putStrLn "press return to stop"
+
+  let cmd = command o
+
+  void $ watchTree man (directory o) (const True)
+       $ case (consumer o, stdin o || persist o )
+           of (True      , _    ) -> void . rawSystem cmd . return . show
+              (_         , True ) -> void . pipe rc cmd . show
+              (_         , _    ) -> const $ void $ runCommand cmd
+
+  void $ getLine
+
+  void $ stopManager man
+
+  whenM rc pipeClose
+
+whenM :: Monad m => Maybe a -> (a -> m ()) -> m ()
+whenM m f = maybe (return ()) f m
+
+startPipe :: String -> IO RunningProcess
+startPipe cmd = do
+  rc@(hStdIn,_,_,_) <- runInteractiveCommand cmd
+  hSetBuffering hStdIn NoBuffering
+  return rc
+
+pipe :: Maybe RunningProcess -> String -> String -> IO ()
+pipe Nothing   cmd arg = startPipe cmd >>= pipeSend arg >>= pipeOutput >>= pipeClose
+pipe (Just rp) _   arg = void $ pipeSend arg rp
+
+pipeOutput :: RunningProcess -> IO RunningProcess
+pipeOutput r@(_,so,_,_) = hGetContents so >>= putStr >> return r
+
+pipeClose :: RunningProcess -> IO ()
+pipeClose (hStdIn, _, _, _) = hClose hStdIn
+
+pipeSend :: String -> RunningProcess -> IO RunningProcess
+pipeSend param rc@(hStdIn, _hStdOut, _stderr, _process) = do
+  hPutStrLn hStdIn param
+  hFlush hStdIn
+  return rc
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Lyndon Maydwell
+
+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/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
