diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Jonathan Fischoff
+
+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
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/Twitch.hs b/src/Twitch.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch.hs
@@ -0,0 +1,23 @@
+module Twitch 
+  ( Dep
+  , DepM
+  , run
+  , defaultMain
+  , (|+)
+  , (|%)
+  , (|-)
+  , (|>)
+  , (|#) 
+  , add'
+  , modify'
+  , delete'
+  , addModify
+  , Rule (..)
+  , RuleAction
+  , Name
+  , PatternText
+  ) where
+import Twitch.Internal 
+import Twitch.Main
+import Twitch.Run
+import Twitch.Rule (Rule, RuleAction, Name, PatternText)
diff --git a/src/Twitch/Internal.hs b/src/Twitch/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch/Internal.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE RecordWildCards                 #-}
+{-# LANGUAGE LambdaCase                      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving      #-}
+{-# LANGUAGE OverloadedStrings               #-}
+{-# LANGUAGE FlexibleInstances               #-}
+module Twitch.Internal where
+import Data.Map (Map)
+import Control.Applicative
+import Control.Monad
+import Filesystem.Path
+import Filesystem.Path.CurrentOS
+import Prelude hiding (FilePath)
+import Data.List 
+import Data.Text (Text)
+import qualified Data.Text as T
+import Control.Monad.Reader
+import Control.Monad.State as State
+import System.Directory
+import System.FilePath.Glob
+import qualified Twitch.Rule as Rule
+import Twitch.Rule (Rule)
+import Data.Monoid
+import Data.Time.Clock
+import Data.String
+import Data.Default
+import Control.Arrow
+import Data.Either
+import System.FSNotify (WatchManager)
+
+newtype DepM a = DepM { unDepM :: State [Rule] a}
+  deriving ( Functor
+           , Applicative
+           , Monad
+           , MonadState [Rule]
+           )
+
+runDep :: Dep -> [Rule]
+runDep = runDepWithState mempty
+
+runDepWithState :: [Rule] -> Dep -> [Rule] 
+runDepWithState xs = flip execState xs . unDepM 
+
+type Dep = DepM ()
+
+instance IsString Dep where
+  fromString = addRule . fromString  
+  
+addRule r = State.modify (r :)
+
+-- TODO this should probably issue a warning 
+modHeadRule :: Dep -> (Rule -> Rule) -> Dep
+modHeadRule dep f = do 
+  let res = runDep dep
+  case res of
+    x:xs -> put $ f x : xs
+    r    -> put r
+
+infixl 8 |+, |%, |-, |>, |#
+(|+), (|%), (|-), (|>) :: Dep -> (FilePath -> IO a) -> Dep
+-- | Set the 'add' field
+x |+ f = modHeadRule x $ Rule.add' f
+-- | Set the modify field
+x |% f = modHeadRule x $ Rule.modify' f
+-- | Set the delete field
+x |- f = modHeadRule x $ Rule.delete' f
+-- | Set both the 'add' and 'modify' field to the same value
+x |> f = x |+ f |% f
+
+-- | Set the name
+(|#) :: Dep -> Text -> Dep
+r |# p = modHeadRule r $ Rule.name' p
+
+-- Prefix API -----------------------------------------------------------------
+add', modify', delete', addModify :: (FilePath -> IO a) -> Dep -> Dep
+add'      = flip (|+)
+modify'   = flip (|%)
+delete'   = flip (|-)
+addModify = flip (|>)
diff --git a/src/Twitch/InternalRule.hs b/src/Twitch/InternalRule.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch/InternalRule.hs
@@ -0,0 +1,142 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Twitch.InternalRule where
+import Filesystem.Path
+import Data.Time.Clock
+import System.FSNotify ( Event (..)
+                       , WatchConfig
+                       , watchDir
+                       , startManagerConf
+                       , WatchManager
+                       , defaultConfig
+                       )
+import Data.Default
+import Control.Monad
+import Data.Monoid
+import qualified Data.Text as T
+import Data.Text (Text)
+import Prelude hiding (FilePath)
+import Twitch.Rule (Rule, RuleIssue)
+import qualified Twitch.Rule as Rule
+
+-- | The actions that are run when file events are triggered
+type Action   = FilePath -> UTCTime -> IO ()
+-- | The test function to determine if a event 'Action' should get fired
+type FileTest = FilePath -> UTCTime -> Bool
+
+data InternalRule = InternalRule 
+  { name     :: Text
+  -- ^ A name for debugging mostly
+  , fileTest :: FileTest
+  -- ^ The test to determine if the rule actions should fire
+  , modify   :: Action 
+  -- ^ The action to run on Modify events
+  , add      :: Action 
+  -- ^ The action to run on Add events
+  , delete   :: Action 
+  -- ^ The action to run on Delete events
+  }
+  
+instance Default InternalRule where
+  def = InternalRule 
+    { name     = mempty
+    , fileTest = \_ _ -> False
+    , modify   = def
+    , add      = def
+    , delete   = def
+    }
+
+instance Show InternalRule where
+  show InternalRule {..} 
+    =  T.unpack $ "Rule { name = " 
+    <> name 
+    <> " }"
+
+toInternalRule :: FilePath -> Rule -> Either RuleIssue InternalRule
+toInternalRule currentDir rule = do
+  test <- Rule.compilePattern currentDir $ Rule.pattern rule
+  return $ InternalRule 
+    { name     = Rule.name rule
+    , fileTest = \x _ -> test x
+    , add      = \x _ -> Rule.add    rule x
+    , modify   = \x _ -> Rule.modify rule x
+    , delete   = \x _ -> Rule.delete rule x
+    }
+
+-- | Configuration to run the file watcher
+data Config = Config 
+  { log         :: Issue -> IO ()
+  -- ^ A logger for the issues 
+  , dirsToWatch :: [FilePath]
+  -- ^ The directories to watch
+  , watchConfig :: WatchConfig
+  -- ^ config for the file watcher
+  } 
+  
+instance Show Config where
+  show Config {..} 
+    =  "Config { dirsToWatch = " 
+    ++ show dirsToWatch
+    ++ "}"
+
+instance Default Config where
+  def = Config
+    { log         = def
+    , dirsToWatch = def
+    , watchConfig = defaultConfig
+    }
+    
+-- | A sum type for the various issues that can be logged
+data Issue 
+  = IEvent     Event
+  -- ^ logged every time an event is fired
+  | IRuleFired Event InternalRule
+  -- ^ logged every time an rule is fired
+  deriving Show
+
+-- | Retrieve the filePath of an Event
+filePath :: Event -> FilePath
+filePath = \case
+  Added    x _ -> x
+  Modified x _ -> x
+  Removed  x _ -> x
+
+-- | Retrieve the time of an Event
+time :: Event -> UTCTime
+time = \case
+  Added    _ x -> x
+  Modified _ x -> x
+  Removed  _ x -> x
+
+-- | Run the Rule action associated with the an event 
+fireRule :: Event -> InternalRule -> IO ()
+fireRule event rule = case event of
+  Added    file time -> modify rule file time
+  Modified file time -> add    rule file time
+  Removed  file time -> delete rule file time
+
+-- | Test to see if the rule should fire and fire it
+testAndFireRule :: Config -> Event -> InternalRule -> IO ()
+testAndFireRule Config {..} event rule = do
+  let shouldFire = fileTest rule (filePath event) (time event)
+  when shouldFire $ do 
+    log $ IRuleFired event rule
+    fireRule event rule 
+
+-- TODO in the future this should use the recursive directory functions
+--      when appropiate
+-- | Start watching a directory, and run the rules on it.
+setupRuleForDir :: Config -> WatchManager -> [InternalRule] -> FilePath -> IO ()
+setupRuleForDir config@(Config {..}) man rules dirPath = do
+  -- TODO Instead of const True, this should use the rule's fileTests
+  void $ watchDir man dirPath (const True) $ \event -> do 
+    log $ IEvent event
+    forM_ rules $ testAndFireRule config event
+
+-- | Setup all of the directory watches using the rules
+setupRules :: Config -> [InternalRule] -> IO WatchManager
+setupRules config@(Config {..}) rules = do 
+  man <- startManagerConf watchConfig
+  forM_ dirsToWatch $ setupRuleForDir config man rules
+  return man
diff --git a/src/Twitch/Main.hs b/src/Twitch/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch/Main.hs
@@ -0,0 +1,197 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Twitch.Main where
+import Data.Monoid
+import Options.Applicative
+import Data.Default
+import qualified System.FSNotify as FS
+import Twitch.Path
+import qualified Twitch.InternalRule as IR
+import System.IO
+import Data.Foldable (for_)
+import Twitch.Run
+import Twitch.Internal
+import System.Directory
+import Data.Maybe
+import Prelude hiding (log)
+import qualified Filesystem.Path as F
+import qualified Filesystem.Path.CurrentOS as F
+import Data.Time.Clock
+import Control.Concurrent.STM.TBMQueue
+import Control.Concurrent
+-- parse the command line
+-- 
+
+
+concatMapM f = fmap concat . mapM f
+
+data LoggerType 
+  = LogToStdout
+  | LogToFile
+  | NoLogger
+  deriving (Eq, Show, Read, Ord)
+
+toLogger :: FilePath 
+         -> LoggerType
+         -> IO (IR.Issue -> IO (), Maybe Handle)
+toLogger filePath = \case
+  LogToStdout -> return (print, Nothing)
+  LogToFile -> do 
+    handle <- openFile filePath AppendMode
+    return (hPutStrLn handle . show, Just handle)
+  NoLogger -> return (const $ return (), Nothing)
+
+data Options = Options 
+  { log          :: LoggerType
+  , logFile      :: Maybe FilePath
+  -- ^ A logger for the issues 
+  , dirsToWatch  :: [FilePath]
+  -- ^ The directories to watch
+  , recurseThroughDirectories :: Bool
+  , debounce     :: DebounceType
+  , debounceAmount :: Double
+  -- ^ Debounce configuration
+  , pollInterval :: Int
+  -- ^ poll interval
+  , usePolling   :: Bool
+  -- ^ config for the file watch
+  , currentDir   :: Maybe FilePath
+  }
+
+data DebounceType 
+  = DebounceDefault
+  | Debounce
+  | NoDebounce
+  deriving (Eq, Show, Read, Ord)
+
+instance Default Options where
+  def = Options
+    { log                       = NoLogger
+    , logFile                   = Nothing
+    , dirsToWatch               = []
+    , recurseThroughDirectories = True
+    , debounce                  = DebounceDefault
+    , debounceAmount            = 0
+    , pollInterval              = 10^(6 :: Int) -- 1 second
+    , usePolling                = False
+    , currentDir                = Nothing
+    }
+
+pOptions :: Parser Options
+pOptions 
+   =  Options
+  <$> option
+        ( long "log"
+       <> short 'l'
+       <> metavar "LOG_TYPE"
+       <> help "Type of logger. Valid options are LogToStdout | LogToFile | NoLogger" 
+       <> value (log def)
+        )
+  <*> option
+        ( long "log-file"
+       <> short 'f'
+       <> metavar "LOG_FILE"
+       <> help "Log file" 
+       <> value (logFile def)
+        )
+  <*> option
+        ( long "directories"
+       <> short 'd'
+       <> metavar "DIRECTORIES"
+       <> help "Directories to watch"
+       <> value (dirsToWatch def)
+        )
+  <*> option
+        ( long "recurse"
+       <> short 'r'
+       <> metavar "RECURSE"
+       <> help "Boolean to recurse or directories or not" 
+       <> value (recurseThroughDirectories def)
+        )
+  <*> option
+        ( long "debounce"
+       <> short 'b'
+       <> metavar "DEBOUNCE"
+       <> help "Target for the greeting" 
+       <> value (debounce def)
+        )
+  <*> option
+        ( long "debounce-amount"
+       <> short 'a'
+       <> metavar "DEBOUNCE_AMOUNT"
+       <> help "Target for the greeting" 
+       <> value (debounceAmount def)
+        )
+  <*> option
+        ( long "poll-interval"
+       <> short 'i'
+       <> metavar "POLL_INTERVAL"
+       <> help "Poll interval if polling is used"
+       <> value (pollInterval def)
+        )
+  <*> option
+        ( long "poll"
+       <> short 'p'
+       <> metavar "POLL"
+       <> help "Whether to use polling or not" 
+       <> value (usePolling def)
+        )
+  <*> option
+        ( long "current-dir"
+       <> short 'c'
+       <> metavar "CURRENT_DIR"
+       <> help "Director to append to the glob patterns" 
+       <> value (currentDir def)
+        )
+
+-- This is like run, but the config params can be over written from the defaults
+
+toDB amount = \case
+  DebounceDefault -> FS.DebounceDefault
+  Debounce        -> FS.Debounce $ fromRational $ toRational amount
+  NoDebounce      -> FS.NoDebounce
+
+optionsToConfig :: Options -> IO (FilePath, IR.Config, Maybe Handle)
+optionsToConfig Options {..} = do 
+  actualCurrentDir <- getCurrentDirectory
+  let currentDir' = fromMaybe actualCurrentDir currentDir
+      dirsToWatch' = if null dirsToWatch then
+                       [currentDir']
+                     else
+                       dirsToWatch
+                       
+  (logger, mhandle) <- toLogger (fromMaybe "log.txt" logFile) log 
+  dirsToWatch'' <- if recurseThroughDirectories then 
+                   concatMapM findAllDirs $ map F.decodeString dirsToWatch'
+                 else 
+                   return $ map F.decodeString dirsToWatch'
+  
+  let watchConfig = FS.WatchConfig
+        { FS.confDebounce     = toDB debounceAmount debounce
+        , FS.confPollInterval = pollInterval
+        , FS.confUsePolling   = usePolling
+        }
+  
+  let config = IR.Config
+        { log         = logger
+        , dirsToWatch = dirsToWatch''
+        , watchConfig = watchConfig
+        }
+  return (currentDir', config, mhandle)
+
+defaultMain :: Dep -> IO ()
+defaultMain dep = do
+  let opts = info (helper <*> pOptions)
+        ( fullDesc
+       <> progDesc "Print a greeting for TARGET"
+       <> header "hello - a test for optparse-applicative" 
+        )
+  (currentDir, config, mhandle) <- optionsToConfig =<< execParser opts
+  let currentDir' = F.decodeString currentDir
+  manager <- runWithConfig currentDir' config dep 
+  putStrLn "Type anything to quit"
+  _ <- getLine
+  for_ mhandle hClose
+  FS.stopManager manager
+  
diff --git a/src/Twitch/Path.hs b/src/Twitch/Path.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch/Path.hs
@@ -0,0 +1,89 @@
+--
+-- Copyright (c) 2012 Mark Dittmer - http://www.markdittmer.org
+-- Developed for a Google Summer of Code project - http://gsoc2012.markdittmer.org
+--
+{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}
+
+module Twitch.Path
+       ( fp
+       , findFiles
+       , findDirs
+       , findAllDirs
+       , canonicalizeDirPath
+       , canonicalizePath
+       ) where
+
+import Prelude hiding (FilePath)
+
+import Control.Monad
+-- import Filesystem
+-- import Filesystem.Path hiding (concat)
+import Filesystem.Path.CurrentOS hiding (concat)
+
+import qualified Filesystem as FS
+import qualified Filesystem.Path as FP
+
+-- This will ensure than any calls to fp for type coercion in FSNotify will not
+-- break when/if the dependent package moves from using String to the more
+-- efficient Filesystem.Path.FilePath
+class ConvertFilePath a b where
+  fp :: a -> b
+instance ConvertFilePath FilePath String where fp   = encodeString
+instance ConvertFilePath String FilePath where fp   = decodeString
+instance ConvertFilePath String String where fp     = id
+instance ConvertFilePath FilePath FilePath where fp = id
+
+getDirectoryContentsPath :: FilePath -> IO [FilePath]
+getDirectoryContentsPath path = fmap (map (path </>)) $ FS.listDirectory path
+
+fileDirContents :: FilePath -> IO ([FilePath],[FilePath])
+fileDirContents path = do
+  contents <- getDirectoryContentsPath path
+  files <- filterM FS.isFile contents
+  dirs <- filterM FS.isDirectory contents
+  return (files, dirs)
+
+findAllFiles :: FilePath -> IO [FilePath]
+findAllFiles path = do
+  (files, dirs) <- fileDirContents path
+  nestedFiles <- mapM findAllFiles dirs
+  return (files ++ concat nestedFiles)
+
+findImmediateFiles, findImmediateDirs :: FilePath -> IO [FilePath]
+findImmediateFiles = getDirectoryContentsPath >=> filterM FS.isFile >=> canonicalize
+  where
+    canonicalize :: [FilePath] -> IO [FilePath]
+    canonicalize files = mapM FS.canonicalizePath files
+findImmediateDirs  = getDirectoryContentsPath >=> filterM FS.isDirectory >=> canonicalize
+  where
+    canonicalize :: [FilePath] -> IO [FilePath]
+    canonicalize dirs = mapM canonicalizeDirPath dirs
+
+findAllDirs :: FilePath -> IO [FilePath]
+findAllDirs path = do
+  dirs <- findImmediateDirs path
+  nestedDirs <- mapM findAllDirs dirs
+  return (dirs ++ concat nestedDirs)
+
+findFiles :: Bool -> FilePath -> IO [FilePath]
+findFiles True path  = findAllFiles       =<< canonicalizeDirPath path
+findFiles False path = findImmediateFiles =<<  canonicalizeDirPath path
+
+findDirs :: Bool -> FilePath -> IO [FilePath]
+findDirs True path  = findAllDirs       =<< canonicalizeDirPath path
+findDirs False path = findImmediateDirs =<< canonicalizeDirPath path
+
+-- | add a trailing slash to ensure the path indicates a directory
+addTrailingSlash :: FilePath -> FilePath
+addTrailingSlash p =
+ if FP.null (FP.filename p) then p else
+   p FP.</> FP.empty
+
+canonicalizeDirPath :: FilePath -> IO FilePath
+canonicalizeDirPath path = addTrailingSlash `fmap` FS.canonicalizePath path
+
+-- | bugfix older version of canonicalizePath (system-fileio <= 0.3.7) loses trailing slash
+canonicalizePath :: FilePath -> IO FilePath
+canonicalizePath path = let was_dir = FP.null (FP.filename path) in
+  if not was_dir then FS.canonicalizePath path
+  else canonicalizeDirPath path
diff --git a/src/Twitch/Rule.hs b/src/Twitch/Rule.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch/Rule.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE RecordWildCards                 #-}
+{-# LANGUAGE LambdaCase                      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving      #-}
+{-# LANGUAGE OverloadedStrings               #-}
+{-# LANGUAGE FlexibleInstances               #-}
+module Twitch.Rule where
+import Data.Map (Map)
+import Control.Applicative
+import Control.Monad
+import Filesystem.Path
+import Filesystem.Path.CurrentOS
+import Prelude hiding (FilePath)
+import Data.Text (Text)
+import qualified Data.Text as T
+import System.Directory
+import System.FilePath.Glob
+import Data.Monoid
+import Data.Time.Clock
+import Data.String
+import Data.Default
+import Control.Arrow
+import Data.Either
+import Debug.Trace
+import System.FSNotify (WatchManager)
+
+-- It doesn't appear that the current directory is need in the monad
+-- The new way I am thinking about this
+-- This is a Rule type and what is currently called Rule is called 
+-- InternalRule
+
+type Name        = Text
+type PatternText = Text
+
+-- | TODO maybe change this to have the timestamp
+type RuleAction = FilePath -> IO ()
+
+-- | The pattern entity holds a name and pattern that is compiled 
+-- when the rules are evaluated
+
+-- It is worth noting that the entire API could just be this
+-- Record with Default
+-- There are actually three apis
+-- "foo.x" .# "name" .$ \x -> print x
+-- "foo.x" |> \x -> print x
+-- "foo.txt" 
+--    { add    = \x -> print x
+--    , modify = \x -> print x
+--    }
+
+data Rule = Rule 
+  { name          :: Text
+  , pattern       :: PatternText
+  , add           :: RuleAction
+  , modify        :: RuleAction
+  , delete        :: RuleAction
+  }
+
+instance Default Rule where
+  def = Rule 
+          { name    = ""
+          , pattern = ""
+          , add     = def
+          , modify  = def
+          , delete  = def
+          }
+
+instance IsString Rule where
+  fromString x = let packed = T.pack x in def { pattern = packed, name = packed} 
+
+--- Infix API------------------------------------------------------------------
+infixl 8 |+, |%, |-, |>, |#
+(|+), (|%), (|-), (|>) :: Rule -> (FilePath -> IO a) -> Rule
+-- | Set the 'add' field
+x |+ f = x { add = void . f }
+-- | Set the modify field
+x |% f = x { modify = void . f }
+-- | Set the delete field
+x |- f = x { delete = void . f }
+-- | Set both the 'add' and 'modify' field to the same value
+x |> f = x |+ f |% f
+
+-- | Set the name
+(|#) :: Rule -> Text -> Rule
+r |# p = r { name = p }
+
+-- Prefix API -----------------------------------------------------------------
+add', modify', delete', addModify :: (FilePath -> IO a) -> Rule -> Rule
+add'      = flip (|+)
+modify'   = flip (|%)
+delete'   = flip (|-)
+name'     = flip (|#)
+addModify = flip (|>)
+
+-- def & add foo & modify foo & delete foo & test tester
+-- def & add foo & modify foo & delete foo & pattern tester
+
+data RuleIssue
+  = PatternCompliationFailed PatternText String
+  deriving Show
+
+compilePattern :: FilePath -> PatternText -> Either RuleIssue (FilePath -> Bool)
+compilePattern currentDir pattern = left (PatternCompliationFailed pattern) $ do 
+   -- TODO is this way of adding the current directory cross platfrom okay?
+   -- Does the globbing even work on windows
+   
+   let absolutePattern = traceShowId $ encodeString currentDir <> "/" <> T.unpack pattern
+   p <- tryCompileWith compDefault absolutePattern
+   let test = match $ simplify p
+   return $ \x -> test $ encodeString x
diff --git a/src/Twitch/Run.hs b/src/Twitch/Run.hs
new file mode 100644
--- /dev/null
+++ b/src/Twitch/Run.hs
@@ -0,0 +1,45 @@
+module Twitch.Run where
+import Twitch.Internal
+import Twitch.InternalRule
+import Twitch.Rule (RuleIssue)
+import Data.Either
+import Prelude hiding (FilePath, log)
+import Filesystem.Path
+import Filesystem.Path.CurrentOS
+import Control.Applicative
+import System.FSNotify
+import System.Directory
+import Twitch.Path
+import Data.Default
+import Debug.Trace
+
+-- This the main interface for running a Dep
+
+run :: Dep -> IO WatchManager
+run dep =  do
+  currentDir <- decodeString <$> getCurrentDirectory
+  dirs       <- findAllDirs currentDir
+  runWithConfig currentDir (def { log = print, dirsToWatch = dirs }) dep
+
+runWithConfig :: FilePath -> Config -> Dep -> IO WatchManager
+runWithConfig currentDir config dep = do
+  let (issues, rules) = depToRules currentDir dep
+  -- TODO handle the issues somehow
+  -- Log and perhaps error
+  setupRules config rules
+
+depToRulesWithCurrentDir :: Dep -> IO ([RuleIssue], [InternalRule])
+depToRulesWithCurrentDir dep = do 
+  currentDir <- decodeString <$> getCurrentDirectory 
+  return $ depToRules currentDir dep
+
+depToRules :: FilePath -> Dep -> ([RuleIssue], [InternalRule])
+depToRules currentDir 
+  = partitionEithers . map (toInternalRule currentDir) . runDep
+
+
+  
+
+
+
+  
diff --git a/twitch.cabal b/twitch.cabal
new file mode 100644
--- /dev/null
+++ b/twitch.cabal
@@ -0,0 +1,42 @@
+-- Initial twitch.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                twitch
+version:             0.1.0.0
+synopsis:            A high level file watcher API
+-- description:         
+homepage:            https://github.com/jfischoff/twitch
+license:             MIT
+license-file:        LICENSE
+author:              Jonathan Fischoff
+maintainer:          jonathangfischoff@gmail.com
+-- copyright:           
+category:            System
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Twitch 
+  other-modules: Twitch.Internal
+               , Twitch.InternalRule
+               , Twitch.Main
+               , Twitch.Path
+               , Twitch.Rule
+               , Twitch.Run
+  other-extensions:    RecordWildCards, LambdaCase, GeneralizedNewtypeDeriving, OverloadedStrings, FlexibleInstances, RankNTypes, DeriveDataTypeable, ScopedTypeVariables, MultiParamTypeClasses, TypeSynonymInstances, TemplateHaskell
+  build-depends: base >=4.7 && <4.8
+               , containers >=0.5 && <0.6
+               , system-filepath >=0.4 && <0.5
+               , text >=1.1 && <1.2
+               , mtl >=2.1 && <2.2
+               , directory >=1.2 && <1.3
+               , Glob >=0.7 && <0.8
+               , time >=1.4 && <1.5
+               , data-default >=0.5 && <0.6
+               , fsnotify >=0.1 && <0.2
+               , optparse-applicative >=0.8 && <0.9
+               , stm-chans >=3.0 && <3.1
+               , system-fileio >=0.3 && <0.4
+  hs-source-dirs:      src
+  default-language:    Haskell2010
