diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) <YEAR>, <OWNER>
+Copyright (c) 2009, Jinjing Wang
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
diff --git a/Nemesis.hs b/Nemesis.hs
new file mode 100644
--- /dev/null
+++ b/Nemesis.hs
@@ -0,0 +1,33 @@
+-- template nemesis file
+
+import System.Nemesis.DSL (clean)
+
+nemesis = do
+  
+  clean
+    [ "**/*.hi"
+    , "**/*.o"
+    , "manifest"
+    ]
+    
+  desc "learn Haskell"
+  task "learn-haskell: learn-fp" (putStrLn "Haskell is awesome!")
+  
+  desc "learn Functional Programming"
+  task "learn-fp : learn-lisp" $ do
+    sh "echo 'into FP'"
+
+  desc "learn LISP"
+  task "learn-lisp" $ do
+    sh "echo 'LISP is cool!'"
+    
+  task "dist" $ do
+    sh "cabal clean"
+    sh "cabal configure"
+    sh "cabal sdist"
+
+  task "i" (sh "ghci -isrc src/System/Nemesis.hs")
+
+  task "manifest" $ do
+    sh "find . | grep 'hs$' > manifest"
+    
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,9 +1,21 @@
+2009.6.13.1
+-----------
+
+### Feature
+
+* add `clean` keyword
+
+### Fix
+
+* allow space before ":" in task description
+* remove `.o .hi` for temp file
+
 2009.6.13
 ---------
 
 ### Feature
 
-* add `desc` dsl
+* add `desc` keyword
 
 ### Fix
 
diff --git a/knownissue.md b/knownissue.md
new file mode 100644
--- /dev/null
+++ b/knownissue.md
@@ -0,0 +1,1 @@
+* does not detect cycles
diff --git a/nemesis.cabal b/nemesis.cabal
--- a/nemesis.cabal
+++ b/nemesis.cabal
@@ -1,5 +1,5 @@
 Name:                 nemesis
-Version:              2009.6.13
+Version:              2009.6.13.1
 Build-type:           Simple
 Synopsis:             a rake like task management tool
 Description:
@@ -14,7 +14,7 @@
 Cabal-version:        >= 1.2
 category:             Web
 homepage:             http://github.com/nfjinjing/nemesis
-data-files:           readme.md, changelog.md
+data-files:           readme.md, changelog.md, knownissue.md, Nemesis.hs
 
 library
   ghc-options: -Wall
@@ -22,12 +22,13 @@
   hs-source-dirs: src/
   exposed-modules:  
                       System.Nemesis
+                      System.Nemesis.DSL
   other-modules:
-                      Nemesis.Util
+                      System.Nemesis.Util
 
 Executable            nemesis
   ghc-options:        -Wall
   build-depends:      base >= 4 && < 5, haskell98, mtl, process, containers, data-default, directory
   hs-source-dirs:     src/
-  main-is:            Nemesis/Runner.hs
-  other-modules:      Nemesis.Util
+  main-is:            System/Nemesis/Runner.hs
+  other-modules:      System.Nemesis.Util
diff --git a/readme.md b/readme.md
--- a/readme.md
+++ b/readme.md
@@ -1,15 +1,46 @@
 Nemesis: a rake like task management tool for haskell
 =====================================================
 
+Demo
+----
+  
+    import System.Nemesis.DSL (clean)
+    
+    nemesis = do
+
+      clean
+        [ "**/*.hi"
+        , "**/*.o"
+        , "manifest"
+        ]
+        
+      task "dist" $ do
+        sh "cabal clean"
+        sh "cabal configure"
+        sh "cabal sdist"
+
+      task "i" (sh "ghci -isrc src/System/Nemesis.hs")
+
+      task "manifest" $ do
+        sh "find . | grep 'hs$' > manifest"
+
 Tutorial
 --------
 
+### Install
+
+    cabal update; cabal install nemesis
+
 ### DSL
 
-in `nem.hs`
+Put the following code into a file named `Nemesis`
 
     nemesis = do
+    
+      -- desc is optional, it gives some description to the following task
       desc "learn Haskell"
+      
+      -- syntax: task "keyword: dependencies" io-action
       task "learn-haskell: learn-fp" (putStrLn "Haskell is awesome!")
 
       desc "learn Functional Programming"
@@ -52,17 +83,8 @@
     -- Nem
 
     nemesis = do
-
-      task "dist" $ do
-        sh "cabal clean"
-        sh "cabal configure"
-        sh "cabal sdist"
-
       task "i" (sh "ghci -isrc src/System/Nemesis.hs")
 
-      task "manifest" $ do
-        sh "find . | grep 'hs$' > manifest"
-
 currently the separator `-- Nem` is hard coded
 
 ### Build it yourself
@@ -71,11 +93,12 @@
 
     module Main where
     
-    import System.Nemesis
+    import System.Nemesis (run)
+    import System.Nemesis.DSL (desc, task, sh)
 
     nemesis = do
       task "i" (sh "ghci -isrc src/System/Nemesis.hs")
         
-    main = nemesis
+    main = run nemesis
 
 
diff --git a/src/Nemesis/Runner.hs b/src/Nemesis/Runner.hs
deleted file mode 100644
--- a/src/Nemesis/Runner.hs
+++ /dev/null
@@ -1,34 +0,0 @@
-{-# LANGUAGE QuasiQuotes #-}
-
-import Prelude hiding ((.), (>), (^))
-import System.Cmd
-import Nemesis.Util
-
-start, end :: String
-start = "\n\n\n\nmodule Main where\n" ++ "import System.Nemesis"
-end = "\n\n\nmain = run nemesis\n"
-
-main :: IO ()
-main = do
-  dir <- ls "."
-  src <- readFile $ dir.filter (belongs_to possible_source) .get_name
-  let h = src.lines.takeWhile (lower > starts_with sep > not) .unlines
-      t = src.lines.dropWhile (lower > starts_with sep > not) .unlines
-  if t.null
-    then output $ start ++ h ++ end
-    else output $ h ++ start ++ t ++ end
-  
-  system $ "ghc --make -O1 " ++ tmp_name ++ " -o " ++ bin
-  system $ "rm " ++ tmp_name
-  return ()
-  
-  where
-    get_name [] = error "Nemesis does not exists"
-    get_name xs = xs.first
-    possible_source = ["Nemesis", "nemesis", "nemesis.hs", "Nemesis.hs"]
-    sep = "-- nem"
-    output = writeFile tmp_name
-    tmp_name = "nemesis-tmp.hs"
-    bin = "nem"
- 
--- helper from mps
diff --git a/src/Nemesis/Util.hs b/src/Nemesis/Util.hs
deleted file mode 100644
--- a/src/Nemesis/Util.hs
+++ /dev/null
@@ -1,65 +0,0 @@
-{-# LANGUAGE NoMonomorphismRestriction #-}
-
-module Nemesis.Util where
-
-import Prelude hiding ((>), (.), (^))
-import qualified Data.List as L
-import Data.Function (on)
-import Control.Arrow ((>>>))
-import Data.Char (toLower)
-import Data.List (isPrefixOf, (\\))
-import System.Directory
-import Control.Category (Category)
-
-
--- utility functions from mps
-
--- base DSL
-(.) :: a -> (a -> b) -> b
-a . f = f a
-infixl 9 .
-
-(^) :: (Functor f) => f a -> (a -> b) -> f b
-(^) = flip fmap
-infixl 8 ^
-
-(>) :: (Category cat) => cat a b -> cat b c -> cat a c
-(>) = (>>>)
-infixl 8 >
-
-
-join :: [a] -> [[a]] -> [a]
-join    = L.intercalate
-
-join' :: [[a]] -> [a]
-join'   = concat
-
-ljust :: Int -> a -> [a] -> [a]
-ljust n x xs 
-  | n < xs.length = xs
-  | otherwise     = ( n.times x ++ xs ).reverse.take n.reverse
-
-compare_by :: (Ord b) => (a -> b) -> a -> a -> Ordering
-compare_by = on compare
-
-first :: [a] -> a
-first = head
-
-times :: b -> Int -> [b]
-times = flip replicate
-
-has :: (Eq a) => a -> [a] -> Bool
-has = elem
-
-belongs_to :: (Eq a) => [a] -> a -> Bool
-belongs_to = flip elem
-
-
-lower :: String -> String
-lower = map toLower
-
-starts_with :: String -> String -> Bool
-starts_with = isPrefixOf
-
-ls :: String -> IO [String]
-ls s = getDirectoryContents s ^ (\\ [".", ".."])
diff --git a/src/System/Nemesis.hs b/src/System/Nemesis.hs
--- a/src/System/Nemesis.hs
+++ b/src/System/Nemesis.hs
@@ -1,15 +1,13 @@
 {-# LANGUAGE NamedFieldPuns #-}
 
-module System.Nemesis (run, sh, task, desc) where
+module System.Nemesis where
 
--- import MPS hiding (empty)
 import Prelude hiding ((.), (>), (^), lookup)
 import Control.Monad.State hiding (State, join)
 import Data.Default
 import Data.Map (Map, insert, empty, lookup, elems)
 import System
-import GHC.IOBase hiding (liftIO)
-import Nemesis.Util
+import System.Nemesis.Util
 
 data Task = Task
   {
@@ -48,13 +46,6 @@
 
 type Unit = StateT Nemesis IO ()
 
--- sh :: String -> IO GHC.IOBase.ExitCode
-sh :: String -> IO ()
-sh s = do
-  status <- system s
-  case status of 
-    ExitSuccess -> return ()
-    ExitFailure code -> error $ s ++ " failed with status code: " ++ show code
 
 run :: Unit -> IO ()
 run unit = do
@@ -71,24 +62,6 @@
       br
     br = putStrLn ""
 
-desc :: String -> Unit
-desc s = do
-  n <- get
-  put n {current_desc = Just s}
-
-task :: String -> IO () -> Unit
-task s action = 
-  if s.has ':'
-    then
-      let h = s.takeWhile (/= ':')
-          t = s.dropWhile (/= ':') .tail
-      in
-      task' h (t.words)
-    else
-      task' s []
-  where
-    task' name deps = insert_task def {name, deps, action}
-
 insert_task :: Task -> Unit
 insert_task t = do
   n <- get
@@ -113,4 +86,3 @@
         revenge_and_say = do
           -- putStrLn $ "running: " ++ t.name
           t.action
-
diff --git a/src/System/Nemesis/DSL.hs b/src/System/Nemesis/DSL.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Nemesis/DSL.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE NamedFieldPuns #-}
+
+module System.Nemesis.DSL where
+
+import Control.Monad.State hiding (State, join)
+import Data.Default
+import Prelude hiding ((.), (>), (^), lookup)
+import System
+import System.Nemesis
+import System.Nemesis.Util
+
+desc :: String -> Unit
+desc s = do
+  n <- get
+  put n {current_desc = Just s}
+
+task :: String -> IO () -> Unit
+task s action = 
+  if s.has ':'
+    then
+      let h = s.takeWhile (/= ':')
+          t = s.dropWhile (/= ':') .tail
+      in
+      task' (h.strip) (t.words)
+    else
+      task' s []
+  where
+    task' name deps = insert_task def {name, deps, action}
+    strip = dropWhile (== ' ') > reverse > dropWhile (== ' ') > reverse
+
+sh :: String -> IO ()
+sh s = do
+  status <- system s
+  case status of 
+    ExitSuccess -> return ()
+    ExitFailure code -> error $ s ++ " failed with status code: " ++ show code
+
+clean :: [String] -> Unit
+clean xs = do
+  desc "Remove any temporary products. map (rm -rf)"
+  task "clean" $ mapM_ (\x -> sh ("rm  -rf " ++ x)) xs
diff --git a/src/System/Nemesis/Runner.hs b/src/System/Nemesis/Runner.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Nemesis/Runner.hs
@@ -0,0 +1,39 @@
+import Prelude hiding ((.), (>), (^))
+import System.Cmd
+import System.Nemesis.Util
+
+start, end, sep_block :: String
+sep_block = "\n\n\n\n"
+start = sep_block ++ start_nemesis ++ start_nemesis_dsl
+  where 
+    start_nemesis     = "import System.Nemesis (run)\n"
+    start_nemesis_dsl = "import System.Nemesis.DSL (sh, task, desc)\n"
+end = sep_block ++ "main = run nemesis\n"
+
+main :: IO ()
+main = do
+  dir <- ls "."
+  src <- readFile $ dir.filter (belongs_to possible_source) .get_name
+  let h = src.lines.takeWhile (lower > starts_with sep > not) .unlines
+      t = src.lines.dropWhile (lower > starts_with sep > not) .unlines
+  if t.null
+    then output $ start ++ h ++ end
+    else output $ h ++ start ++ t ++ end
+  
+  system $ "ghc --make -O1 " ++ tmp_name ++ " -o " ++ bin
+  system $ "rm " ++ tmp_name
+  system $ "rm " ++ tmp_o
+  system $ "rm " ++ tmp_hi
+  return ()
+  
+  where
+    get_name []     = error "Nemesis does not exists"
+    get_name xs     = xs.first
+    possible_source = ["Nemesis", "nemesis", "nemesis.hs", "Nemesis.hs"]
+    sep             = "-- nem"
+    output          = writeFile tmp_name
+    tmp_name        = "nemesis-tmp.hs"
+    tmp_o           = "nemesis-tmp.o"
+    tmp_hi          = "nemesis-tmp.hi"
+    bin             = "nem"
+
diff --git a/src/System/Nemesis/Util.hs b/src/System/Nemesis/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Nemesis/Util.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE NoMonomorphismRestriction #-}
+
+module System.Nemesis.Util where
+
+import Control.Arrow ((>>>))
+import Control.Category (Category)
+import Data.Char (toLower)
+import Data.Function (on)
+import Data.List (isPrefixOf, (\\))
+import Prelude hiding ((>), (.), (^))
+import System.Directory
+import qualified Data.List as L
+
+-- utility functions from mps
+
+-- base DSL
+(.) :: a -> (a -> b) -> b
+a . f = f a
+infixl 9 .
+
+(^) :: (Functor f) => f a -> (a -> b) -> f b
+(^) = flip fmap
+infixl 8 ^
+
+(>) :: (Category cat) => cat a b -> cat b c -> cat a c
+(>) = (>>>)
+infixl 8 >
+
+
+join :: [a] -> [[a]] -> [a]
+join    = L.intercalate
+
+join' :: [[a]] -> [a]
+join'   = concat
+
+ljust :: Int -> a -> [a] -> [a]
+ljust n x xs 
+  | n < xs.length = xs
+  | otherwise     = ( n.times x ++ xs ).reverse.take n.reverse
+
+compare_by :: (Ord b) => (a -> b) -> a -> a -> Ordering
+compare_by = on compare
+
+first :: [a] -> a
+first = head
+
+times :: b -> Int -> [b]
+times = flip replicate
+
+has :: (Eq a) => a -> [a] -> Bool
+has = elem
+
+belongs_to :: (Eq a) => [a] -> a -> Bool
+belongs_to = flip elem
+
+
+lower :: String -> String
+lower = map toLower
+
+starts_with :: String -> String -> Bool
+starts_with = isPrefixOf
+
+ls :: String -> IO [String]
+ls s = getDirectoryContents s ^ (\\ [".", ".."])
