diff --git a/CmdLine.hs b/CmdLine.hs
deleted file mode 100644
--- a/CmdLine.hs
+++ /dev/null
@@ -1,260 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, PatternGuards #-}
-
--- | Module for parsing command line options and build queries
-module CmdLine
-  (parseCmdLine,
-   glob,
-   buildQuery,
-   composeAll,
-   usage)
-  where
-
-import Prelude hiding (putStrLn,readFile,getContents,print)
-import IO
-import Codec.Binary.UTF8.String
-import System (getArgs)
-import System.Console.GetOpt
-import System.FilePath.Glob
-import Data.Maybe
-import Data.List (sort)
-import Control.Monad.Reader
-
-import Unicode
-import Types
-import TodoTree
-import Dates (parseDate)
-
--- | Default limit for tree height
-pruneByDefault ∷  Limit
-pruneByDefault = Limit 20
-
--- | Compose predicate from Composed
-compose ∷ DateTime       -- ^ Current date/time
-        → Composed       -- ^ Composed query
-        → (TodoItem → 𝔹)
-compose _ Empty             = const True
-compose _ (Pred NoFilter)   = const True
-compose _ (Pred (Tag s))    = tagPred s
-compose _ (Pred (Name s))   = grepPred s
-compose _ (Pred (Description s))   = descPred s
-compose _ (Pred (Status s)) = statusPred s
-compose _ (Pred (IdIs s)) = idPred s
-compose dt (Pred (StartDateIs d)) = datePred startDate dt d
-compose dt (Pred (EndDateIs d)) = datePred endDate dt d
-compose dt (Pred (DeadlineIs d)) = datePred deadline dt d
-compose dt (Not p)           = not ∘ (compose dt p)
-compose dt (And (Pred NoFilter) p) = compose dt p
-compose dt (And p (Pred NoFilter)) = compose dt p
-compose dt (And p1 p2)      = \item → (compose dt p1 item) ∧ (compose dt p2 item)
-compose dt (Or (Pred NoFilter) p) = compose dt p
-compose dt (Or p (Pred NoFilter)) = compose dt p
-compose dt (Or p1 p2)       = \item → (compose dt p1 item) ∨ (compose dt p2 item)
-compose _ x = error $ show x
-
-concatMapM ∷ (Monad m) ⇒ m (t → [t]) → m ([t] → [t])
-concatMapM = liftM concatMap
-
--- | Make a list transformer
-composeAll ∷ DateTime → ListTransformer
-composeAll date = do
-  pred ← asks ((compose date) ∘ query)
-  concatMapM (pruneSelector pred)
-
-appendC ∷ Composed → QueryFlag → Composed
-appendC (Not (Pred NoFilter))   f = Not (Pred f)
-appendC Empty OrCons              = (Pred NoFilter) `Or` (Pred NoFilter)
-appendC Empty AndCons             = (Pred NoFilter) `And` (Pred NoFilter)
-appendC Empty NotCons             = Not (Pred NoFilter)
-appendC Empty f                   = Pred f
-appendC c NoFilter                = c
-appendC c AndCons                 = c `And` (Pred NoFilter)
-appendC c OrCons                  = c `Or`  (Pred NoFilter)
-appendC c NotCons                 = c `And` (Pred NoFilter)
-appendC (And c (Pred NoFilter)) f = c `And` (Pred f) 
-appendC (And (Pred NoFilter) c) f = c `And` (Pred f) 
-appendC c@(And _ _)             f = c `And` (Pred f)
-appendC (Or c (Pred NoFilter))  f = c `Or`  (Pred f)
-appendC (Or (Pred NoFilter) c)  f = c `Or`  (Pred f)
-appendC c@(Or _ _)              f = c `Or`  (Pred f)
-appendC c@(Pred _)              f = c `And` (Pred f)
-appendC c                       f = c `And` (Pred f)
-
-appendF (O q m o l) (QF f) = O (f:q) m o l
-appendF (O q m o l) (MF f) = O q (f:m) o l
-appendF (O q m o l) (OF f) = O q m (f:o) l
-appendF (O q m o l) (LF f) = O q m o (f:l)
-appendF _ HelpF = Help
-
-parseFlags ∷ [CmdLineFlag] → Options
-parseFlags lst | HelpF ∈ lst = Help
-parseFlags [] = O [] [] [] []
-parseFlags (f:fs) = (parseFlags fs) `appendF` f
-
--- | Build Config (with query etc) from Options
-buildQuery ∷ Options → Config
-buildQuery (O qflags mflags oflags lflags) = Config onlyFirst colors showIds srt limitP limitM command aprefix dformat noStatus setStatus composedFlags 
-  where
-    composedFlags = parseQuery qflags
-    (limitP,limitM) = parseLimits lflags
-
-    onlyFirst = OnlyFirst ∈ oflags
-    colors = Colors ∈ oflags
-    showIds = Ids ∈ oflags
-    srtFlags = filter isSort oflags
-    srt | null srtFlags = DoNotSort 
-        | otherwise = getSorting (last srtFlags)
-
-    cmdFlags  = filter isCommand mflags
-    command | DotExport ∈ oflags = ShowAsDot
-            | null cmdFlags      = JustShow
-            | otherwise          = SystemCommand $ unExecute (last cmdFlags)
-
-    prefixFlags = filter isPrefix mflags
-    aprefix | null prefixFlags = Nothing
-            | otherwise        = Just $ unPrefix (last prefixFlags)
-
-    dflags = filter isDescribe mflags
-    dformat | null dflags = "%d"
-            | otherwise   = unDescribe $ last dflags
-
-    noStatus = not $ null $ filter isNoStatus mflags
-    newStatusFlags = filter isSetStatus mflags
-    setStatus | null newStatusFlags = Nothing
-              | otherwise           = Just $ newStatus $ last newStatusFlags
-
-    isSort (Sort _) = True
-    isSort _        = False
-    isDescribe (Describe _) = True
-    isDescribe _            = False
-    isCommand (Execute _) = True
-    isCommand _           = False
-    isPrefix (Prefix _) = True
-    isPrefix _          = False
-    isNoStatus DoNotReadStatus = True
-    isNoStatus _               = False
-    isSetStatus (SetStatus _)  = True
-    isSetStatus _              = False
-
-parseLimits ∷ [LimitFlag] → (Limit,Limit)
-parseLimits flags = (limitP,limitM)
-  where
-    pruneFlags = filter isPrune flags
-    minFlags   = filter isMin flags
-
-    limitP'       = foldl min Unlimited $ map (Limit ∘ unPrune) pruneFlags
-    limitP | Unlimited ← limitP' = pruneByDefault
-           | otherwise           = limitP'
-
-    limitM'       = foldl max (Limit 0) $ map (Limit ∘ unMin) minFlags
-    limitM | Unlimited ← limitM' = pruneByDefault
-           | otherwise           = limitM'
-
-    isPrune (Prune _) = True
-    isPrune _         = False
-
-    isMin   (Start x) = True
-    isMin   _         = False
-
-parseQuery ∷ [QueryFlag] → Composed
-parseQuery flags = foldl appendC Empty flags
-
--- | Parse command line
-parseCmdLine ∷ DateTime              -- ^ Current date/time
-             → [String]              -- ^ Command line args
-             → (Options, [FilePath]) -- ^ (Options, list of files)
-parseCmdLine currDate args = 
-  case getOpt Permute (options currDate) (map decodeString args) of
-        (flags, [],      [])     → (parseFlags flags, ["TODO"])
-        (flags, nonOpts, [])     → (parseFlags flags, nonOpts)
-        (_,     _,       msgs)   → error $ concat msgs ⧺ usage
-
-isPattern s = ('*' ∈ s) || ('?' ∈ s)
-
-glob ∷ [FilePath] → IO [FilePath]
-glob list = do
-  let patterns = filter isPattern list
-      files = filter (not ∘ isPattern) list
-  (matches, _) ← globDir (map compile patterns) "." 
-  return $ sort $ files ⧺ concat matches
-
-usage ∷  String
-usage = usageInfo header (options undefined)
-  where 
-    header = "Usage: todos [OPTION...] [INPUT FILES]"
-
-options ∷ DateTime → [OptDescr CmdLineFlag]
-options currDate = [
-    Option "1" ["only-first"] (NoArg (OF OnlyFirst))                 "show only first matching entry",
-    Option "c" ["color"]      (NoArg (OF Colors))                    "show colored output",
-    Option "I" ["show-ids"]   (NoArg (OF Ids))                       "show IDs of todos",
-    Option "A" ["prefix"]     (OptArg mkPrefix "PREFIX")             "use alternate parser: read only lines starting with PREFIX",
-    Option ""  ["dot"]        (NoArg (OF DotExport))                 "output entries in DOT (graphviz) format",
-    Option "D" ["describe"]   (OptArg mkDescribe "FORMAT")           "use FORMAT for descriptions",
-    Option "w" ["no-status"]  (NoArg (MF DoNotReadStatus))           "do not read status field from TODOs",
-    Option ""  ["set-status"] (ReqArg mkSetStatus "STRING")          "force all TODOs status to be equal to STRING",
-    Option "p" ["prune"]      (ReqArg mkPrune "N")                   "limit tree height to N",
-    Option "m" ["min-depth"]  (ReqArg mkMin "N")                     "show first N levels of tree unconditionally",
-    Option "t" ["tag"]        (ReqArg mkTag "TAG")                   "find items marked with TAG",
-    Option "g" ["grep"]       (ReqArg mkName "PATTERN")              "find items with PATTERN in name",
-    Option "G" ["description"] (ReqArg mkDescr "PATTERN")            "find items with PATTERN in description",
-    Option "s" ["status"]     (ReqArg mkStatus "STRING")             "find items with status equal to STRING",
-    Option "i" ["id"]         (ReqArg mkIdQ "STRING")                "find items with ID equal to STRING",
-    Option "a" ["and"]        (NoArg (QF AndCons))                   "logical AND",
-    Option "o" ["or"]         (NoArg (QF OrCons))                    "logical OR",
-    Option "n" ["not"]        (NoArg (QF NotCons))                   "logical NOT",
-    Option ""  ["sort"]       (ReqArg mkSort "FIELD")                "specify sorting",
-    Option "e" ["exec"]       (OptArg mkExecute "COMMAND")           "run COMMAND on each matching entry",
-    Option "S" ["start-date"] (ReqArg (mkStartDate currDate) "DATE") "find items with start date bounded with DATE",
-    Option "E" ["end-date"]   (ReqArg (mkEndDate currDate) "DATE")   "find items with end date bounded with DATE",
-    Option "d" ["deadline"]   (ReqArg (mkDeadline currDate) "DATE")  "find items with deadline bounded with DATE",
-    Option "h" ["help"]       (NoArg HelpF)                          "display this help"
-  ]
-
-mkSort s = OF $ Sort $ readSort s
-
-mkTag ∷  String → CmdLineFlag
-mkTag t = QF $ Tag t
-
-mkName ∷  String → CmdLineFlag
-mkName n = QF $ Name n
-
-mkStatus ∷  String → CmdLineFlag
-mkStatus s = QF $ Status s
-
-mkIdQ ∷  String → CmdLineFlag
-mkIdQ s = QF $ IdIs s
-
-mkDescr ∷  String → CmdLineFlag
-mkDescr s = QF $ Description s
-
-forceEither ∷  (Show t) ⇒ Either t b → b
-forceEither (Right x) = x
-forceEither (Left x) = error $ show x
-
-mkStartDate ∷  DateTime → String → CmdLineFlag
-mkStartDate dt s = QF $ StartDateIs $ forceEither $ parseDate emptyConfig dt s
-
-mkEndDate ∷  DateTime → String → CmdLineFlag
-mkEndDate dt s = QF $ EndDateIs $ forceEither $ parseDate emptyConfig dt s
-
-mkDeadline ∷  DateTime → String → CmdLineFlag
-mkDeadline dt s = QF $ DeadlineIs $ forceEither $ parseDate emptyConfig dt s
-
-mkDescribe ∷  Maybe String → CmdLineFlag
-mkDescribe Nothing = MF $ Describe "%d"
-mkDescribe (Just f) = MF $ Describe f
-
-mkSetStatus ∷ String → CmdLineFlag
-mkSetStatus st = MF $ SetStatus st
-
-mkPrune ∷  String → CmdLineFlag
-mkPrune s = LF $ Prune (read s)
-
-mkMin ∷  String → CmdLineFlag
-mkMin s = LF $ Start (read s)
-
-mkPrefix ∷  Maybe [Char] → CmdLineFlag
-mkPrefix = MF ∘ Prefix ∘ fromMaybe "TODO:"
-
-mkExecute ∷  Maybe [Char] → CmdLineFlag
-mkExecute = MF ∘ Execute ∘ fromMaybe "echo %n %d"
diff --git a/Color.hs b/Color.hs
deleted file mode 100644
--- a/Color.hs
+++ /dev/null
@@ -1,102 +0,0 @@
-{-# LANGUAGE UnicodeSyntax #-}
-module Color where
-
-import qualified Data.Map as M
-import Text.Printf
-import Data.Word
-import Data.Hash
-
-import Unicode
-import Types
-
-data HSV = HSV {
-  colorHue ∷ Double,
-  colorSaturation ∷ Double,
-  colorValue ∷ Double }
-  deriving (Eq)
-
-instance Show HSV where
-  show (HSV h s v) = printf "\"%.3f %.3f %.3f\"" h s v
-
-(<#>) ∷ HSV → HSV → HSV
-(HSV h1 s1 v1) <#> (HSV h2 s2 v2) = HSV ((h1+h2)/2) ((s1+s2)/2) ((v1+v2)/2)
-
-tagHues ∷ M.Map String Double
-tagHues = M.fromList $ [
-  ("BUG",   0.07),
-  ("NOTE",  0.25),
-  ("ERROR", 0.0),
-  ("TODO",  0.55)]
-
-hashAsDouble ∷ Hashable a ⇒ a → Double
-hashAsDouble x = (fromIntegral $ asWord64 $ hash x) / (fromIntegral (maxBound :: Word64))
-
-tagHue ∷ String → Double
-tagHue tag =
-  case M.lookup tag tagHues of
-    Nothing → hashAsDouble tag
-    Just h  → h
-
-statusSats ∷ M.Map String Double
-statusSats = M.fromList $ [
-  ("*",      1.0),
-  ("URGENT", 1.0),
-  ("+",      0.9),
-  ("W",      0.9),
-  ("?",      0.8),
-  ("Q",      0.8),
-  ("M",      0.7),
-  ("L",      0.6),
-  ("DONE",   0.5),
-  ("FIXED",  0.5),
-  ("WORKSFORME", 0.45),
-  (":",      0.4),
-  ("-",      0.3),
-  ("INVALID", 0.3),
-  ("o",      0.3),
-  ("x",      0.2)]
-
-statusSat ∷ String → Double
-statusSat st = 
-  case M.lookup st statusSats of
-    Nothing → 0.4* hashAsDouble st
-    Just s  → s
-
-statusHues ∷ M.Map String Double
-statusHues = M.fromList $ [
-  ("*",      0.0),
-  ("URGENT", 0.0),
-  ("+",      0.16),
-  ("W",      0.08),
-  ("?",      0.7),
-  ("Q",      0.8),
-  ("M",      0.2),
-  ("L",      0.6),
-  ("DONE",   0.25),
-  ("FIXED",  0.25),
-  ("NOTE",   0.3),
-  ("WORKSFORME", 0.1),
-  ("INVALID", 0.45),
-  (":",      0.4),
-  ("-",      0.09),
-  ("o",      0.8),
-  ("x",      0.2)]
-
-statusHue ∷ String → Double
-statusHue st =
-  case M.lookup st statusHues of
-    Nothing → hashAsDouble st
-    Just h  → h
-
-getColor ∷ TodoItem → HSV
-getColor item = HSV h s v 
-  where
-    tags = itemTags item
-    n    = length tags
-    st   = itemStatus item
-    h    = case n of
-             0 → statusHue st
-             _ → (statusHue st + sum (map tagHue tags))/fromIntegral (n+1)
-    s    = statusSat st
-    v    = 0.8
-
diff --git a/CommandParser.hs b/CommandParser.hs
deleted file mode 100644
--- a/CommandParser.hs
+++ /dev/null
@@ -1,26 +0,0 @@
-{-# LANGUAGE UnicodeSyntax #-}
-
-module CommandParser where
-
-import Unicode
-import Types
-
--- | Format item info
-printfItem ∷ String      -- ^ Format string
-           → TodoItem 
-           → String
-printfItem pattern item = printf pattern
-  where
-    printf "" = ""
-    printf [x] = [x]
-    printf ('%':c:xs) = (itemPart c) ⧺ printf xs
-    printf (x:xs) = x:printf xs
-
-    itemPart 'L' = show $ itemLevel item
-    itemPart 'n' = itemName item
-    itemPart 't' = unwords $ itemTags item
-    itemPart 's' = itemStatus item
-    itemPart 'd' = itemDescr item
-    itemPart 'f' = fileName item
-    itemPart 'l' = show $ lineNr item
-    itemPart x   = [x]
diff --git a/Config.hs b/Config.hs
deleted file mode 100644
--- a/Config.hs
+++ /dev/null
@@ -1,88 +0,0 @@
-{-# LANGUAGE UnicodeSyntax #-}
-
--- | Module for parsing config files
-module Config
-  (readConfig)
-  where
-
-import Prelude hiding (putStrLn,readFile,getContents,print)
-import IO
-import System.Environment
-import System.FilePath 
-import System.Directory (doesFileExist)
-import Data.Maybe
-import Data.Either
-import Text.ParserCombinators.Parsec
-
-import Unicode
-import Types
-
-word ∷ Parser String
-word = choice $ map try [quotedOption, simpleOption, quoted, simpleWord]
-
-simpleWord = many1 $ noneOf " \t\r\n=\"'"
-
-quotedOption = (try quotedLongOption) <|> quotedShortOption
-
-quotedLongOption ∷ Parser String
-quotedLongOption = do
-  string "--"
-  o ← simpleWord
-  char '='
-  v ← quoted
-  return ("--" ⧺ o ⧺ "=" ⧺ v)
-
-quotedShortOption ∷ Parser String
-quotedShortOption = do
-  string "-"
-  o ← simpleWord
-  v ← quoted
-  return ("-" ⧺ o ⧺ v)
-
-simpleOption = do
-  o ← simpleWord
-  optional $ char '='
-  v ← simpleWord
-  return (o ⧺ "=" ⧺ v)
-
-quoted = quoted1 <|> quoted2
-
-quoted1 = do
-  char '\''
-  s ← many1 $ noneOf "'"
-  char '\''
-  return s
-
-quoted2 = do
-  char '"'
-  s ← many1 $ noneOf "\""
-  char '"'
-  return s
-
-pConfig ∷ Parser [String]
-pConfig = word `sepBy` space
-
-parseConfig ∷ String → [String]
-parseConfig str = 
-  case parse pConfig "config file" str of
-    Right lst → lst
-    Left err → error $ show err
-
-readFile' ∷ FilePath → IO [String]
-readFile' path = 
-  do b ← doesFileExist path
-     if not b
-       then return []
-       else do
-              str ← readFile path
-              return $ parseConfig (unwords $ lines str)
-
--- | Read list of options from config files
-readConfig ∷ IO [String]
-readConfig = do
-  home ← getEnv "HOME"
-  let homepath = home </> ".config" </> "todos"
-  homecfg ← readFile' homepath
-  localcfg ← readFile' ".todos.conf"
-  return $ homecfg ⧺ localcfg
-  
diff --git a/ConstrSet.hs b/ConstrSet.hs
deleted file mode 100644
--- a/ConstrSet.hs
+++ /dev/null
@@ -1,40 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable, NoMonomorphismRestriction #-}
-
-module ConstrSet 
-  (CSet,
-   empty,
-   fromList, toList,
-   insert, append,
-   elemC,
-   selectByConstrOf,
-   selectByConstrOf'
-  ) where
-
-import Data.Generics
-import Data.Function (on)
-import Data.List (nubBy)
-import Data.Maybe (fromMaybe)
-
-teq = (==) `on` toConstr
-
-data (Data a) ⇒ CSet a = CSet [a]
-
-empty = CSet []
-
-fromList lst = CSet $ nubBy teq lst
-toList (CSet lst) = lst
-
-insert item (CSet []) = CSet [item]
-insert item (CSet lst) = CSet $ nubBy teq (item:lst)
-
-selectByConstrOf (CSet []) _ = Nothing
-selectByConstrOf (CSet (x:xs)) y | x `teq` y = Just x
-                                 | otherwise = selectByConstrOf (CSet xs) y
-
-selectByConstrOf' def cset x = fromMaybe def $ selectByConstrOf cset x
-
-elemC _ (CSet []) = False
-elemC x (CSet (y:ys)) | x `teq` y = True
-                      | otherwise = elemC x (CSet ys)
-
-append (CSet xs) (CSet ys) = CSet $ nubBy teq (xs++ys)
diff --git a/Dates.hs b/Dates.hs
deleted file mode 100644
--- a/Dates.hs
+++ /dev/null
@@ -1,301 +0,0 @@
-{-# LANGUAGE UnicodeSyntax #-}
--- | Operations with dates
-module Dates
-  (parseDate, getCurrentDateTime,
-   pSpecDates)
-  where
-
-import Data.Char (toUpper)
-import Data.Function (on)
-import Data.List
-import Data.Time.Calendar
-import Data.Time.Clock
-import Data.Time.LocalTime
-import Text.ParserCombinators.Parsec
-
-import Types
-import Unicode
-
-getCurrentDateTime ∷  IO DateTime
-getCurrentDateTime = do
-  zt ← getZonedTime
-  let lt = zonedTimeToLocalTime zt
-      ld = localDay lt
-      ltod = localTimeOfDay lt
-      (y,m,d) = toGregorian ld
-      h = todHour ltod
-      min = todMin ltod
-      s = round $ todSec ltod
-  return $ DateTime (fromIntegral y) m d h min s
-
-uppercase ∷ String → String
-uppercase = map toUpper
-
-isPrefixOfI ∷  String → String → Bool
-p `isPrefixOfI` s = (uppercase p) `isPrefixOf` (uppercase s)
-
-lookupS ∷ String → [(String,a)] → Maybe a
-lookupS _ [] = Nothing
-lookupS k ((k',v):other) | k `isPrefixOfI` k' = Just v
-                         | otherwise          = lookupS k other
-
-monthsN ∷ [(String,Int)]
-monthsN = zip months [1..]
-
-lookupMonth ∷ String → Maybe Int
-lookupMonth n = lookupS n monthsN
-
-date ∷  Int → Int → Int → DateTime
-date y m d = DateTime y m d 0 0 0
-
-addTime ∷  DateTime → Time → DateTime
-addTime dt t = dt {
-                 hour = tHour t + hour dt,
-                 minute = tMinute t + minute dt,
-                 second = tSecond t + second dt }
-
-times ∷ Int → TParser t → TParser [t]
-times 0 _ = return []
-times n p = do
-  ts ← times (n-1) p
-  t ← optionMaybe p
-  case t of
-    Just t' → return (ts ++ [t'])
-    Nothing → return ts
-                               
-number ∷ Int → Int → TParser Int
-number n m = do
-  t ← read `fmap` (n `times` digit)
-  if t > m
-    then fail "number too large"
-    else return t
-
-pYear ∷ TParser Int
-pYear = do
-  y ← number 4 10000
-  if y < 2000
-    then return (y+2000)
-    else return y
-
-pMonth ∷ TParser Int
-pMonth = number 2 12
-
-pDay ∷ TParser Int
-pDay = number 2 31
-
-euroNumDate ∷ TParser DateTime
-euroNumDate = do
-  d ← pDay
-  char '.'
-  m ← pMonth
-  char '.'
-  y ← pYear
-  return $ date y m d
-
-americanDate ∷ TParser DateTime
-americanDate = do
-  y ← pYear
-  char '/'
-  m ← pMonth
-  char '/'
-  d ← pDay
-  return $ date y m d
-
-euroNumDate' ∷ Int → TParser DateTime
-euroNumDate' year = do
-  d ← pDay
-  char '.'
-  m ← pMonth
-  return $ date year m d
-
-americanDate' ∷ Int → TParser DateTime
-americanDate' year = do
-  m ← pMonth
-  char '/'
-  d ← pDay
-  return $ date year m d
-
-strDate ∷ TParser DateTime
-strDate = do
-  d ← pDay
-  space
-  ms ← many1 letter
-  case lookupMonth ms of
-    Nothing → fail $ "unknown month: "++ms
-    Just m  → do
-      space
-      y ← pYear
-      notFollowedBy $ char ':'
-      return $ date y m d
-
-strDate' ∷ Int → TParser DateTime
-strDate' year = do
-  d ← pDay
-  space
-  ms ← many1 letter
-  case lookupMonth ms of
-    Nothing → fail $ "unknown month: "++ms
-    Just m  → return $ date year m d
-
-time24 ∷ TParser Time
-time24 = do
-  h ← number 2 23
-  char ':'
-  m ← number 2 59
-  x ← optionMaybe $ char ':'
-  case x of
-    Nothing → return $ Time h m 0
-    Just _ → do
-      s ← number 2 59
-      notFollowedBy letter
-      return $ Time h m s
-
-ampm ∷ TParser Int
-ampm = do
-  s ← many1 letter
-  case map toUpper s of
-    "AM" → return 0
-    "PM" → return 12
-    _ → fail "AM/PM expected"
-
-time12 ∷ TParser Time
-time12 = do
-  h ← number 2 12
-  char ':'
-  m ← number 2 59
-  x ← optionMaybe $ char ':'
-  s ← case x of
-            Nothing → return 0
-            Just s' → number 2 59
-  optional space
-  hd ← ampm
-  return $ Time (h+hd) m s
-
-pAbsDate ∷ Int → TParser DateTime
-pAbsDate year = do
-  date ← choice $ map try $ map ($ year) $ [
-                              const euroNumDate,
-                              const americanDate,
-                              const strDate,
-                              strDate',
-                              euroNumDate',
-                              americanDate']
-  optional $ char ','
-  s ← optionMaybe space
-  case s of
-    Nothing → return date
-    Just _ → do
-      t ← choice $ map try [time12,time24]
-      return $ date `addTime` t
-
-data DateIntervalType = Day | Week | Month | Year
-  deriving (Eq,Show,Read)
-
-data DateInterval = Days ℤ
-                  | Weeks ℤ
-                  | Months ℤ
-                  | Years ℤ
-  deriving (Eq,Show)
-
-convertTo ∷  DateTime → Day
-convertTo dt = fromGregorian (fromIntegral $ year dt) (month dt) (day dt)
-
-convertFrom ∷  Day → DateTime
-convertFrom dt = 
-  let (y,m,d) = toGregorian dt
-  in  date (fromIntegral y) m d
-
-modifyDate ∷  (t → Day → Day) → t → DateTime → DateTime
-modifyDate fn x dt = convertFrom $ fn x $ convertTo dt
-
-addInterval ∷  DateTime → DateInterval → DateTime
-addInterval dt (Days ds) = modifyDate addDays ds dt
-addInterval dt (Weeks ws) = modifyDate addDays (ws*7) dt
-addInterval dt (Months ms) = modifyDate addGregorianMonthsClip ms dt
-addInterval dt (Years ys) = modifyDate addGregorianYearsClip ys dt
-
-maybePlural ∷ String → TParser String
-maybePlural str = do
-  r ← string str
-  optional $ char 's'
-  return (capitalize r)
-
-pDateInterval ∷ TParser DateIntervalType
-pDateInterval = do
-  s ← choice $ map maybePlural ["day", "week", "month", "year"]
-  return $ read s
-
-pRelDate ∷ DateTime → TParser DateTime
-pRelDate date = do
-  offs ← (try futureDate) <|> (try passDate) <|> (try today) <|> (try tomorrow) <|> yesterday
-  return $ date `addInterval` offs
-
-futureDate ∷ TParser DateInterval
-futureDate = do
-  string "in "
-  n ← many1 digit
-  char ' '
-  tp ← pDateInterval
-  case tp of
-    Day →   return $ Days (read n)
-    Week →  return $ Weeks (read n)
-    Month → return $ Months (read n)
-    Year →  return $ Years (read n)
-
-passDate ∷ TParser DateInterval
-passDate = do
-  n ← many1 digit
-  char ' '
-  tp ← pDateInterval
-  string " ago"
-  case tp of
-    Day →   return $ Days $ - (read n)
-    Week →  return $ Weeks $ - (read n)
-    Month → return $ Months $ - (read n)
-    Year →  return $ Years $ - (read n)
-
-today ∷ TParser DateInterval
-today = do
-  string "today"
-  return $ Days 0
-
-tomorrow ∷ TParser DateInterval
-tomorrow = do
-  string "tomorrow"
-  return $ Days 1
-
-yesterday ∷ TParser DateInterval
-yesterday = do
-  string "yesterday"
-  return $ Days (-1)
-
-pDate ∷ DateTime → TParser DateTime
-pDate date =  (try $ pRelDate date) <|> (try $ pAbsDate $ year date)
-
-dateType ∷ String → DateType
-dateType "start" = StartDate
-dateType "end"   = EndDate
-dateType "deadline" = Deadline
-dateType _ = error "unknown date type"
-
-pSpecDate ∷ DateTime → TParser (DateType, DateTime)
-pSpecDate date = do
-  tp ← choice $ map string ["start","end","deadline"]
-  string ": "
-  dt ← pDate date
-  return (dateType tp, dt)
-
-pSpecDates ∷ DateTime → TParser [(DateType, DateTime)]
-pSpecDates date = do
-  char '('
-  pairs ← (pSpecDate date) `sepBy1` (string "; ")
-  string ") "
-  return pairs
-
--- | Parse date/time
-parseDate ∷ Config
-          → DateTime  -- ^ Current date/time
-          → String    -- ^ String to parse
-          → Either ParseError DateTime
-parseDate conf date s = runParser (pDate date) conf "" s
-
diff --git a/Dot.hs b/Dot.hs
deleted file mode 100644
--- a/Dot.hs
+++ /dev/null
@@ -1,57 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, TypeSynonymInstances, FlexibleInstances #-}
-module Dot
-  (showAsDot)
-  where
-
-import Data.List
-import Data.Tree
-import Text.Printf
-
-import Unicode
-import Types
-import Color
-import Shapes
-
-data Dot = Dot {
-  dotVertices ∷ [TodoItem],
-  dotEdges ∷ [(TodoItem, TodoItem)]
-  }
-
-toDot ∷ Todo → Dot
-toDot todo = Dot (getVertices todo) (getEdges todo)
-
-getVertices ∷ Todo → [TodoItem]
-getVertices (Node item forest) =
-  [item] ⧺ concatMap getVertices forest
-
-getEdges ∷ Todo → [(TodoItem, TodoItem)]
-getEdges (Node item forest) =
-  [(item, rootLabel child) | child ← forest] ⧺ concatMap getEdges forest
-
-instance Show Dot where
-  show (Dot vs es) = "digraph Todo {\n"
-                   ⧺ unlines (map showDotNode vs)
-                   ⧺ unlines (map showDotEdge es)
-                   ⧺ "}\n"
-
-showD ∷ [Dot] → String
-showD dots = "digraph Todo {\n"
-            ⧺ "  rankdir = \"RL\";\n"
-            ⧺ "  node [shape=\"box\", style=\"filled\"];\n"
-            ⧺ unlines (map showDotNode $ nub $ sort $ concatMap dotVertices dots)
-            ⧺ unlines (map showDotEdge $ nub $ sort $ concatMap dotEdges dots)
-            ⧺ "}\n"
-
-makeName ∷ TodoItem → String
-makeName item = "\"" ⧺ makeId item ⧺ "\""
-
-showDotNode ∷ TodoItem → String
-showDotNode item =
-  printf "  %s [label=\"%s\\n%s\\n%s\", fillcolor=%s, shape=\"%s\"];" (makeName item) (itemStatus item) (unwords $ itemTags item) (itemName item) (show $ getColor item) (show $ getShape item)
-
-showDotEdge ∷ (TodoItem, TodoItem) → String
-showDotEdge (x,y) = printf "  %s -> %s;" (makeName y) (makeName x)
-
-showAsDot ∷ [Todo] → String
-showAsDot todos = showD (map toDot todos)
-
diff --git a/IO.hs b/IO.hs
deleted file mode 100644
--- a/IO.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-{-# LANGUAGE CPP #-}
--- | Wrapper to support unicode IO with GHC 6.10 and 6.12
-module IO 
-#if __GLASGOW_HASKELL__ < 612
-  (module System.IO.UTF8)
-#else
-  (module Prelude)
-#endif
-  where
-
-#if __GLASGOW_HASKELL__ < 612
-
-   import Prelude hiding (putStr, putStrLn,readFile,getContents,print)
-   import System.IO.UTF8
-
-#else
-
-  import Prelude
-
-#endif
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -5,4 +5,6 @@
 	$(GHC) $(GHCFLAGS) --make todos.hs
 
 clean:
-	rm -f *.hi *.o *~
+	find . -name \*.hi -delete
+	find . -name \*.o -delete
+	find . -name \*~ -delete
diff --git a/README b/README
--- a/README
+++ b/README
@@ -28,8 +28,9 @@
 (see below), «TAGS» is a comma-separated list of tags in brackets, «title» is
 title of record, «depends» — comma-separated list of depends (titles of other
 records) in parenthesis, «description» is description of the record. All fields
-except status and title are optional. Description is separated from title with
-not less than 2 spaces.
+except status and title are optional. If «-w» option is specified, status field
+is not read (it's optional; when it's present, it will be part of title).
+Description is separated from title with not less than 2 spaces. 
 
 Using different indents one can create sub-records (and, so, trees of records).
 Moreover, depends are interpreted in same way as sub-records. So, one can
@@ -40,37 +41,57 @@
 Dates information is described in parenthesis, as not more than 3 records,
 separated with semicolon. Each records has format «date type: date». Date type
 can be one of: start, end, deadline. For dates, many formats are supported; for
-example, «18 Feb», или «02/18/2010», «2010/02/18» are all valid dates. One can
+example, «18 Feb», or «02/18/2010», «2010/02/18» are all valid dates. One can
 even use relative dates specification here, such as «in 2 weeks» or «2 days
 ago», but there is no point to this: all dates are counted from program start
 date.
 
 Next commandline options are supported:
 
-  -1           --only-first         show only first matching entry
-  -c           --color              show colored output
-  -A[PREFIX]   --prefix[=PREFIX]    use alternate parser: read only lines starting with PREFIX
-  -D[FORMAT]   --describe[=FORMAT]  use FORMAT for descriptions
-  -p N         --prune=N            limit tree height to N
-  -m N         --min-depth=N        show first N levels of tree unconditionally
-  -t TAG       --tag=TAG            find items marked with TAG
-  -g PATTERN   --grep=PATTERN       find items with PATTERN in name
-  -s STRING    --status=STRING      find items with status equal to STRING
-  -a           --and                logical AND
-  -o           --or                 logical OR
-  -n           --not                logical NOT
-               --sort=FIELD         specify sorting
-  -e[COMMAND]  --exec[=COMMAND]     run COMMAND on each matching entry
-  -S DATE      --start-date=DATE    find items with start date bounded with DATE
-  -E DATE      --end-date=DATE      find items with end date bounded with DATE
-  -d DATE      --deadline=DATE      find items with deadline bounded with DATE
-  -h           --help               display this help
+  -1           --only-first              show only first matching entry
+  -c           --color                   show colored output
+  -H           --highlight               instead of filtering TODOs, just highlight matching the query
+  -I           --show-ids                show IDs of todos
+  -A[PREFIX]   --prefix[=PREFIX]         use alternate parser: read only lines starting with PREFIX
+               --dot                     output entries in DOT (graphviz) format
+  -D[FORMAT]   --describe[=FORMAT]       use FORMAT for descriptions
+  -w           --no-status               do not read status field from TODOs
+               --set-status=STRING       force all TODOs status to be equal to STRING
+               --set-root-status=STRING  force statuses of root TODOs to be equal to STRING
+  -F           --by-file                 group TODOs by source file
+  -T           --by-tag                  group TODOs by tag
+  -Z           --by-status               group TODOs by status
+  -p N         --prune=N                 limit tree height to N
+  -m N         --min-depth=N             show first N levels of tree unconditionally
+  -t TAG       --tag=TAG                 find items marked with TAG
+  -g PATTERN   --grep=PATTERN            find items with PATTERN in name
+  -G PATTERN   --description=PATTERN     find items with PATTERN in description
+  -s STRING    --status=STRING           find items with status equal to STRING
+  -i STRING    --id=STRING               find items with ID equal to STRING
+  -a           --and                     logical AND
+  -o           --or                      logical OR
+  -n           --not                     logical NOT
+               --sort=FIELD              specify sorting
+  -e[COMMAND]  --exec[=COMMAND]          run COMMAND on each matching entry
+  -S DATE      --start-date=DATE         find items with start date bounded with DATE
+  -E DATE      --end-date=DATE           find items with end date bounded with DATE
+  -d DATE      --deadline=DATE           find items with deadline bounded with DATE
+  -h           --help                    display this help
 
 Dates in the commandline are specified in any of supported formats. There
 relative dates specifications are useful: how about something like «todos
 --deadline=yesterday»? ;)
 
-For --describe and --exec options, printf-style format strings should be
+When «-H» option is specified, items are not filtered at all (all items will be
+printed). Instead, items matching to your query will be highlighted. This works
+only with «-c» option.
+
+When «-I» option is specified, for each TODO it's unique identifier is shown.
+That ID is simply hash  from item's title, status, tags and description, so
+only identical items will have identical ID. One can ask todos to show item
+with specified ID using «-i» option.
+
+For «--describe» and «--exec» options, printf-style format strings should be
 specified. Following substitution sequences are supported:
 
 %n :: title of the record
@@ -83,9 +104,43 @@
 For example, «todos -tBUG -e"vi %f +%l"» command for each record with «BUG» tag
 will open vi with corresponding file on corresponding line.
 
-todos can read configuration files: ~/.config/todos («global») and
-./.todos.conf («local»). Config files contain command line options, in the same
+When «--dot» option is specified, todos will output a graph definition for DOT
+(graphviz), instead of normal output. For example, you can try
+
+  todos --dot | dot -Tpng -o todos.png
+
+and then see the file todos.png. Items with status «GROUP» will become DOT's
+clusters. More precisely, if one item have «GROUP» status, it will become a
+cluster, and all it's children will be inside that cluster. There is a
+limitation in DOT: it can not draw graphs where one item is in several clusters
+properly, it will draw such item only in one cluster.
+
+todos can read configuration files: `~/.config/todos/todos.conf` («global») and
+`./.todos.conf` («local»). Config files contain command line options, in the same
 format as in the commandline. Resulting commandline is composed as: (options in
 global config) + (options in local config) + (options in the actual command
 line). If there is no one of configs, todos will ignore it.
+
+Moreover, todos could be customized in «xmonad way». You write file
+`~/.config/todos/todos.hs` and run todos. todos will compile and run that file.
+Simplest example of `todos.hs` possible is `todos.hs` in this directory. A bit
+more complex example:
+
+.................................................................
+    import System.Console.ANSI
+
+    import Todos
+
+    main :: IO ()
+    main = todos $ defaultConfig {
+                      itemConsoleColor = myItemColor
+                   }
+
+    myItemColor item =
+      if itemStatus item == ":"
+         then Just (Dull, Blue)
+         else Nothing
+.................................................................
+
+So, items with ":" status will be printed in blue color.
 
diff --git a/README.ru b/README.ru
--- a/README.ru
+++ b/README.ru
@@ -27,7 +27,8 @@
 информация о датах (см. ниже), TAGS — список тегов через пробел в квадратных
 скобках, title — заголовок записи, depends — список зависимостей (заголовков
 других записей) в скобках через запятую, description — описание записи. Все
-поля кроме статуса и заголовка необязательны. Описание отделяется от заголовка
+поля кроме статуса и заголовка необязательны. В случае, если указана опция
+«-w», поле статуса не читается. Описание отделяется от заголовка
 не менее чем двумя пробелами.
 
 С помощью разных отступов можно создавать вложенные записи. Кроме того,
@@ -51,13 +52,24 @@
 
 -1, --only-first :: показывать только первую запись из всех, что должны быть показаны
 -c, --color  ::     раскрашивать вывод todos (заголовки записей — полужирным, некоторые статусы выделяются цветом)
+-H, --highlight ::  подсвечивать подходящие записи, вместо того чтобы фильтровать их
+-I, --show-ids :: показывать идентификаторы записей
 -A, --prefix= ::    использовать альтернативный формат файлов, тут же указывается префикс
+--dot :: вывести записи в виде графа для DOT (graphviz)
 -D, --describe ::   можно указать формат для выводимого описания (см. ниже)
+-w, --no-status ::  не читать поле статуса из файлов
+--set-status=STRING :: установить статус всех TODO равным STRING
+--set-root-status=STRING :: установить статусы корневых TODO равным STRING
+-F, --by-file :: группировать записи по исходному файлу
+-T, --by-tag  :: группировать записи по тегам
+-Z, --by-status :: группировать записи по статусам
 -p, --prune ::      ограничить максимальную высоту дерева (для каждой из отобранных записей будет показано не более чем N уровней вложенности)
 -m, --min-depth ::  ограничить минимальную высоту дерева
 -t, --tag ::        вывести только записи с заданным тегом и их вложенные записи
 -g, --grep ::       искать по заголовкам записей
+-G, --description :: искать по описаниям записей
 -s, --status ::     искать по статусам записей
+-i, --id ::  искать по идентификаторам записей
 -a, --and ::        логическое И; например, todos -s! -a -tBUG — искать записи со статусом «!» и тегом «BUG»
 -o, --or ::         логическое ИЛИ;
 -n, --not ::        логическое НЕ;
@@ -75,6 +87,10 @@
 «меньше или равно»; например, todos --start-date="in 2 weeks" будет искать
 записи с датой начала через 2 недели или раньше (в т.ч. и в прошлом).
 
+С указанной опцией «-H» записи не фильтруются совсем. Вместо этого те записи,
+которые подходят под ваш запрос, будут подсвечены цветом (только с включённой
+опцией «-c»).
+
 Для опций --describe и --exec указываются строки формата с символами
 подстановки в стиле printf. Поддерживаются следующие символы подстановки:
 
@@ -88,9 +104,44 @@
 Например, todos -tBUG -e"vi %f +%l" для каждой записи с тегом BUG откроет vi на
 строчке с этой записью.
 
-todos может читать конфигурационные файлы: ~/.config/todos (глобальный) и
-.todos.conf в текущей директории (локальный). Конфигурационные файлы содержат
+С опцией «--dot» todos выведет на стандартный выход описание графа для DOT
+(graphviz) вместо обычного вывода. Можно попробовать, например, так:
+
+  todos --dot | dot -Tpng -o todos.png
+
+и см. получившийся файл todos.png. Записи со статусом «GROUP» становятся
+кластерами. Точнее говоря, если какая-то запись имеет статус «GROUP», она будет
+отображаться как кластер, а все её дочерние записи будут находиться внутри
+этого кластера. При этом есть одно ограничение, накладываемое DOT: DOT не умеет
+правильно рисовать графы, в которых один узел находится сразу в нескольких
+кластерах; в таких случаях узел будет нарисован только в одном кластере.
+
+todos может читать конфигурационные файлы: `~/.config/todos/todos.conf`  (глобальный) и
+`.todos.conf` в текущей директории (локальный). Конфигурационные файлы содержат
 ключи командной строки. «Эффективная командная строка» составляется как (ключи
 в глобальном конфиге) + (ключи в локальном конфиге) + ключи в командной строке.
 Если какого-то из конфигов нет, todos это проигнорирует.
+
+Кроме того, todos можно настраивать под себя «в стиле xmonad». Вы пишете файл
+`~/.config/todos/todos.hs` и запускаете todos. todos компилирует и запускает
+тот файл. Простейший возможный пример этого `todos.hs` — это файл `todos.hs` в
+этой директории. Вот чуть более сложный пример:
+
+.................................................................
+    import System.Console.ANSI
+
+    import Todos
+
+    main :: IO ()
+    main = todos $ defaultConfig {
+                      itemConsoleColor = myItemColor
+                   }
+
+    myItemColor item =
+      if itemStatus item == ":"
+         then Just (Dull, Blue)
+         else Nothing
+.................................................................
+
+Таким образом, записи со статусом «:» будут напечатаны синим цветом.
 
diff --git a/Shapes.hs b/Shapes.hs
deleted file mode 100644
--- a/Shapes.hs
+++ /dev/null
@@ -1,42 +0,0 @@
-{-# LANGUAGE UnicodeSyntax #-}
-module Shapes where
-
-import qualified Data.Map as M
-
-import Unicode
-import Types
-
-data Shape = 
-    Box 
-  | Ellipse
-  | Diamond
-  | DCircle
-  | Note
-  | Parallelogram
-  | Folder
-  deriving (Eq)
-
-instance Show Shape where
-  show Box           = "box"
-  show Ellipse       = "ellipse"
-  show Diamond       = "diamond"
-  show DCircle       = "doublecircle"
-  show Note          = "note"
-  show Parallelogram = "parallelogram"
-  show Folder        = "folder"
-
-shapes ∷ M.Map String Shape
-shapes = M.fromList $ [
-  ("o", Ellipse),
-  ("O", DCircle),
-  (":", Folder),
-  ("*", Diamond),
-  ("/", Parallelogram),
-  ("NOTE", Note) ]
-
-getShape ∷ TodoItem → Shape
-getShape item = 
-  case M.lookup (itemStatus item) shapes of
-    Nothing → Box
-    Just s  → s
-
diff --git a/TodoLoader.hs b/TodoLoader.hs
deleted file mode 100644
--- a/TodoLoader.hs
+++ /dev/null
@@ -1,104 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, PatternGuards #-}
--- | Read TODOs from files and construct corresponding ADTs.
-module TodoLoader
-  (loadTodo)
-  where
-
-import Prelude hiding (putStrLn,readFile,getContents,print)
-import IO
-import Control.Monad (forM)
-import qualified Data.Map as M
-import Text.ParserCombinators.Parsec
-import Data.Maybe
-import Data.Tree
-
-import Unicode
-import Types
-import TodoParser
-
-todoName ∷ Todo → String
-todoName todo = itemName ⋄ rootLabel todo
-
-getDepends ∷ TodoMap → TodoItem → [Todo]
-getDepends m item = catMaybes [M.lookup name m | name ← depends item] 
-
-normalizeItem ∷ TodoMap → TodoItem → Todo
-normalizeItem m item = Node item (map (normalize m) ⋄ getDepends m item)
-
-normalize ∷ TodoMap → Todo → Todo 
-normalize m todo = Node item' ((map (normalize m) subTodos) ⧺ (map (normalize m) deps))
-  where
-    item ∷ TodoItem
-    item = rootLabel todo
-
-    item' ∷ TodoItem
-    item' = item {depends=[]}
-
-    subTodos ∷ [Todo]
-    subTodos = subForest todo
-
-    deps ∷ [Todo]
-    deps = getDepends m item
-
-normalizeList ∷ TodoMap → [Todo] → [Todo]
-normalizeList m todos = map (normalize m) todos
-
-readFile' ∷ FilePath → IO String
-readFile' "-"  = getContents
-readFile' file = readFile file
-
-loadFile ∷ Config
-         → DateTime
-         → FilePath
-         → IO [TodoItem]
-loadFile conf year path =
-  case prefix conf of
-    Nothing → do
-        text ← readFile' path
-        return $ parsePlain conf year path text
-    Just p  → do
-        text ← readFile' path
-        return $ parseAlternate conf 2 p year path text
-
-(~-) ∷  TodoItem → ℤ → TodoItem
-i@(Item {itemLevel=n}) ~- k = i {itemLevel=n-k}
-
-iszero ∷  TodoItem → 𝔹
-iszero item = (itemLevel item)==0
-
-group' ∷  [TodoItem] → [[TodoItem]]
-group' [] = []
-group' (x:xs) = let (one,other) = break iszero xs
-                in (x:one):group' other
-
-mkTodo ∷ [TodoItem] → [Todo]
-mkTodo = (map mkTodo') ∘ group'
-
-mkTodo' ∷ [TodoItem] → Todo
-mkTodo' (x:xs) = Node x other
-    where other = mkTodo ⋄ map (~-lvl) xs
-          lvl = itemLevel (head xs)
-          
-consTodoMap ∷ [Todo] → TodoMap
-consTodoMap todos = M.fromList (cons1 100 todos)
-  where
-    cons1 ∷ Int → [Todo] → [(String,Todo)]
-    cons1 0 _ = []
-    cons1 max trees = [(todoName todo, todo) | todo ← trees] ⧺ cons1 (max-1) (children trees)
-    children ∷ [Todo] → [Todo]
-    children trees = concatMap subForest trees
-
-stitchTodos ∷ [TodoItem] → [Todo]
-stitchTodos items = 
-  let m = consTodoMap t
-      t = mkTodo items
-  in  normalizeList m t
-
--- | Load list of TODO trees from files
-loadTodo ∷ Config
-         → DateTime      -- ^ Current date/time
-         → [FilePath]    -- ^ List of files
-         → IO [Todo]
-loadTodo conf date paths = do
-    tss ← forM paths (loadFile conf date)
-    return $ stitchTodos (concat tss)
diff --git a/TodoParser.hs b/TodoParser.hs
deleted file mode 100644
--- a/TodoParser.hs
+++ /dev/null
@@ -1,150 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, NoMonomorphismRestriction, TypeSynonymInstances, DeriveDataTypeable #-}
-module TodoParser
-    (parsePlain, parseAlternate)
-    where
-
-import Prelude hiding (putStrLn,readFile,getContents,print)
-import IO
-import Control.Monad
-import Data.List
-import Text.ParserCombinators.Parsec
-import qualified Data.Map as M
-import Data.Tree
-import Data.Maybe
-import Data.Char
-import Data.Function
-
-import Unicode
-import Types
-import Dates
-
-strip ∷  String → String
-strip = reverse ∘ p ∘ reverse ∘ p
-  where
-    p = dropWhile isSpace
-
-pSpace ∷ TParser Char
-pSpace = oneOf " \t"
-
-pSpace' ∷ TParser String
-pSpace' = do
-    pSpace
-    return " "
-
-pSpaces ∷ TParser String
-pSpaces = many pSpace
-
-pDeps ∷ TParser [String]
-pDeps = do
-    string "("
-    ws ← (many1 ⋄ noneOf ",)\n\r") `sepBy` (char ',')
-    string ")"
-    return $ map strip ws
-
-pTags ∷ TParser [String]
-pTags = do
-    ts ← between (char '[') (char ']') $ word `sepBy1` pSpace
-    pSpaces
-    return ts
-  where
-    word = many1 (noneOf " \t\n\r]")
-
-pItem ∷ DateTime → TParser TodoItem
-pItem date = do
-    pos ← getPosition
-    s ← pSpaces
-    conf ← getState
-    stat ← if skipStatus conf
-             then case forcedStatus conf of
-                    Just fs → return fs
-                    Nothing → return "*"
-            else do 
-                rs ← pWord
-                case forcedStatus conf of
-                  Just fs → return fs
-                  Nothing → return rs
-    dates ← (try (pSpecDates date) <|> return [])
-    tags ← (try pTags <|> return [])
-    namew ← many1 pWord
-    pSpaces
-    deps ← (try pDeps <|> return [])
-    pSpaces
-    descr ← many (noneOf "\n\r")
-    pSpaces
-    many ⋄ oneOf "\n\r"
-    return ⋄ Item {
-        itemLevel = fromIntegral $ length s,
-        itemName = unwords namew,
-        itemTags = tags,
-        depends = deps,
-        itemStatus = stat,
-        itemDescr = descr,
-        startDate = lookup StartDate dates,
-        endDate = lookup EndDate dates,
-        deadline = lookup Deadline dates,
-        fileName = sourceName pos,
-        lineNr = sourceLine pos }
-
-pWord ∷ TParser String
-pWord = do
-    w ← many1 (noneOf " \t\n\r")
-    (try pSpace') <|> (return w)
-    return w
-
-pItems ∷ DateTime → TParser [TodoItem]
-pItems date = do
-  its ← many (pItem date)
-  eof
-  return its
-
-unwords' ∷  String → [String] → String
-unwords' prefix lst =
-  let (hd:tl) = map (filter (/='\r')) lst
-      addLines = filter (not ∘ (prefix `isPrefixOf`)) tl
-  in  case addLines of
-        [] → hd
-        _  → hd ⧺ "    {" ++ (unwords addLines) ++ "}"
-
-filterN ∷ (Num a, Enum a) ⇒ Int → String → [String] → ([a], [String])
-filterN n prefix lst = 
-  let zipped = zip [0..] lst
-      good   = filter (isGood ∘ snd) zipped
-      lns    = map fst good
-      sub k l = (take l) ∘ (drop k)
-      ans = map (unwords' prefix) [sub j n lst | j ← lns]
-      isGood x = prefix `isPrefixOf` x
-      cut = drop (1+length prefix) 
-  in (map (+1) lns, map cut ans)
-
-filterJoin ∷ Int → String → String → ([Int], String)
-filterJoin n prefix str = 
-  let (ns, lns) = filterN n prefix (lines str)
-  in  (ns, unlines lns)
-
--- | Read list of TODO items from plain format 
-parsePlain ∷ Config
-           → DateTime   -- ^ Current date/time
-           → SourceName -- ^ Source file name
-           → String     -- ^ String to parse
-           → [TodoItem]
-parsePlain conf date path text = 
-  case runParser (pItems date) conf path text of
-      Right items → items
-      Left e → error $ show e
-
--- | Read list of TODO items from alternate format
-parseAlternate ∷ Config 
-               → Int        -- ^ Number of lones after matching to include to item's description
-               → String     -- ^ Prefix to match
-               → DateTime   -- ^ Current date/time
-               → SourceName -- ^ Source file name
-               → String     -- ^ String to parse
-               → [TodoItem]
-parseAlternate conf next prefix date path text = 
-  let (ns, filtered) = filterJoin next prefix text
-      renumber lst = zipWith renumber1 ns lst
-      renumber1 n item = item {lineNr=n}
-  in case runParser (pItems date) conf path filtered of
-       Right items → renumber items
-       Left e      → error $ show e
-
diff --git a/TodoTree.hs b/TodoTree.hs
deleted file mode 100644
--- a/TodoTree.hs
+++ /dev/null
@@ -1,153 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, NoMonomorphismRestriction, FlexibleInstances, TypeSynonymInstances #-}
-module TodoTree 
-  (delTag,
-   pruneSelector,
-   tagPred, statusPred, grepPred, descPred, datePred, idPred,
-   forT, mapT,
-   printTodos)
-  where
-
-import Prelude hiding (putStrLn,readFile,getContents,print)
-import IO
-import System.Console.ANSI
-import Control.Monad
-import Control.Monad.Reader
-import qualified Data.Map as M
-import Data.Generics
-import Data.List
-import Data.Function (on)
-import Data.Tree
-import Data.Maybe
-import Text.Regex.PCRE
-import Data.Hash
-import Numeric
-
-import Types
-import TodoLoader
-import Unicode
-
-sortBy' s | s == DoNotSort = id
-          | otherwise = sortBy sorter
-  where
-    sorter = compare `on` (f ∘ rootLabel)
-    f = case s of
-          ByTitle → itemName
-          ByStatus → itemStatus
-          ByTags → unwords ∘ itemTags
-          ByStartDate → show ∘ startDate
-          ByEndDate → show ∘ endDate
-          ByDeadline → show ∘ deadline 
-
-showT ∷ SortingType → Int → Todo → [Formatter]
-showT s n (Node item todos) = 
-    (startFormat <++> showId item <++> replicate n ' ' <++> configShow item) :
-      (concatMap (showT s (n+2)) $ sortBy' s todos)
-  where
-    showId :: TodoItem → Formatter
-    showId item = do
-      s ← asks outIds
-      c ← asks outColors
-      if s
-        then if c 
-               then return [OutSetColor Yellow, OutString $ makeId item ++ " ", ResetAll]
-               else return [OutString $ makeId item ++ " "]
-        else return [OutString ""]
-
-unlines'' ∷ [Formatter] → Formatter
-unlines'' lst = concat `fmap` (sequence $ intersperse newLine lst)
-
-showTodo ∷ Todo → Formatter
-showTodo t = do
-  conf ← ask
-  let f = case outOnlyFirst conf of
-            False → unlines''
-            True  → head
-  f $ showT (sorting conf) 0 t
-
-showTodos ∷ [Todo] → Formatter
-showTodos lst = do
-  conf ← ask
-  let f = case outOnlyFirst conf of
-            False → unlines''
-            True  → head
-  f $ map showTodo $ sortBy' (sorting conf) $ nub lst
-
-printTodos ∷ Config → [Todo] → IO ()
-printTodos conf lst = 
-  let lst' = runReader (showTodos lst) conf
-  in  mapM_ outItem lst'
-
-mapTags ∷  (Data a) ⇒ ([String] → [String]) → [a] → [a]
-mapTags f = map ⋄ everywhere ⋄ mkT changeTags
-  where
-    changeTags item@(Item {itemTags=ts}) = item {itemTags = f ts}
-        
-addTag ∷  (Data a) ⇒ String → [a] → [a]
-addTag t = mapTags (t:)
-
-delTag ∷  (Data a) ⇒ String → [a] → [a]
-delTag t = mapTags (delete t)
-
-pruneSelector ∷ (TodoItem → 𝔹) → Transformer
-pruneSelector pred = do
-  (Limit n) ← asks pruneL
-  (Limit m) ← asks minL
-  return $ pruneSelector' n m pred
-        
-pruneSelector' ∷ ℤ → ℤ → (TodoItem → 𝔹) → (Todo → [Todo])
-pruneSelector' n m pred = select n 0 False
-    where
-        select k t b (Node item trees) | t < m       = [Node item ⋄ concatMap (select (n-1) (t+1) True) trees]
-                                       | pred item   = [Node item ⋄ concatMap (select (n-1) (t+1) True) trees]
-                                       | (k > 0) ∧ b = [Node item ⋄ concatMap (select (k-1) (t+1) True) trees]
-                                       | k > 0       = concatMap (select (k-1) (t+1) False) trees
-                                       | otherwise   = []                                               
-
-addS ∷  (Show a) ⇒ a → TodoItem → TodoItem
-addS s item@(Item {itemName=name}) = item {itemName = name ⧺ " — " ⧺ show s}
-
-tagPred ∷  String → TodoItem → 𝔹
-tagPred tag = \item → tag ∈ itemTags item
-
-statusPred ∷  String → TodoItem → 𝔹
-statusPred st = \item → st == itemStatus item
-        
-grepPred ∷ String → TodoItem → 𝔹
-grepPred pattern = \item → itemName item =~ pattern
-
-descPred ∷ String → TodoItem → 𝔹
-descPred pattern = \item → itemDescr item =~ pattern
-
-idPred :: String → TodoItem → 𝔹
-idPred hash = \item → makeId item == hash
-
-isLT ∷  (Ord t) ⇒ Maybe t → t → 𝔹
-isLT Nothing _ = False
-isLT (Just x) y = x <= y
-
-isGT ∷  (Ord t) ⇒ Maybe t → t → 𝔹
-isGT Nothing _ = False
-isGT (Just x) y = x >= y
-
-datePred ∷  (Ord a) ⇒ (t → Maybe a) → a → a → t → 𝔹
-datePred selector curr dt | dt >= curr = \item → selector item `isLT` dt
-                          | otherwise  = \item → selector item `isGT` dt
-
-flattern ∷ [Todo] → [Todo]
-flattern = concatMap flat
-    where
-        flat ∷ Todo → [Todo]
-        flat (Node item trees) = (Node item []):(concatMap flat trees)
-
-forT ∷ (Monad m, Eq t) ⇒ [Tree t] → (t → m a) → m [b]
-forT todos f = forM (nub todos) forT'
-  where
-    forT' (Node item trees) =
-      do f item
-         res ← forM trees forT'
-         return $ last res
-
-mapT ∷ (t → t) → [Tree t] → [Tree t]
-mapT f todos = map mapT' todos
-  where
-    mapT' (Node item trees) = Node (f item) (mapT f trees)
diff --git a/Todos.hs b/Todos.hs
new file mode 100644
--- /dev/null
+++ b/Todos.hs
@@ -0,0 +1,25 @@
+module Todos 
+  (module Todos.Unicode,
+   module Todos.Types,
+   module Todos.Loader,
+   module Todos.Tree,
+   module Todos.CommandParser,
+   module Todos.Config,
+   module Todos.ConfigUtils,
+   module Todos.CmdLine,
+   module Todos.Dot,
+   module Todos.Main,
+   getCurrentDateTime) where
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Loader
+import Todos.Tree
+import Todos.CommandParser
+import Todos.Config
+import Todos.ConfigUtils
+import Todos.CmdLine
+import Todos.Dates (getCurrentDateTime)
+import Todos.Dot
+import Todos.Main
+
diff --git a/Todos/CmdLine.hs b/Todos/CmdLine.hs
new file mode 100644
--- /dev/null
+++ b/Todos/CmdLine.hs
@@ -0,0 +1,297 @@
+{-# LANGUAGE UnicodeSyntax, PatternGuards #-}
+
+-- | Module for parsing command line options and build queries
+module Todos.CmdLine
+  (parseCmdLine',
+   glob,
+   buildQuery,
+   compose,
+   usage)
+  where
+
+import Prelude hiding (putStrLn,readFile,getContents,print)
+import Todos.IO
+import System.Console.GetOpt
+import System.FilePath.Glob
+import Data.Maybe
+import Data.List (sort)
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Tree
+import Todos.Config
+import Todos.Dates (parseDate)
+
+-- | Compose predicate from Composed
+compose ∷ DateTime       -- ^ Current date/time
+        → Composed       -- ^ Composed query
+        → (TodoItem → 𝔹)
+compose _ Empty             = const True
+compose _ (Pred NoFilter)   = const True
+compose _ (Pred (Tag s))    = tagPred s
+compose _ (Pred (Name s))   = grepPred s
+compose _ (Pred (Description s))   = descPred s
+compose _ (Pred (Status s)) = statusPred s
+compose _ (Pred (IdIs s)) = idPred s
+compose dt (Pred (StartDateIs d)) = datePred startDate dt d
+compose dt (Pred (EndDateIs d)) = datePred endDate dt d
+compose dt (Pred (DeadlineIs d)) = datePred deadline dt d
+compose dt (Not p)           = not ∘ (compose dt p)
+compose dt (And (Pred NoFilter) p) = compose dt p
+compose dt (And p (Pred NoFilter)) = compose dt p
+compose dt (And p1 p2)      = \item → (compose dt p1 item) ∧ (compose dt p2 item)
+compose dt (Or (Pred NoFilter) p) = compose dt p
+compose dt (Or p (Pred NoFilter)) = compose dt p
+compose dt (Or p1 p2)       = \item → (compose dt p1 item) ∨ (compose dt p2 item)
+compose _ x = error $ show x
+
+appendC ∷ Composed → QueryFlag → Composed
+appendC (Not (Pred NoFilter))   f = Not (Pred f)
+appendC Empty OrCons              = (Pred NoFilter) `Or` (Pred NoFilter)
+appendC Empty AndCons             = (Pred NoFilter) `And` (Pred NoFilter)
+appendC Empty NotCons             = Not (Pred NoFilter)
+appendC Empty f                   = Pred f
+appendC c NoFilter                = c
+appendC c AndCons                 = c `And` (Pred NoFilter)
+appendC c OrCons                  = c `Or`  (Pred NoFilter)
+appendC c NotCons                 = c `And` (Pred NoFilter)
+appendC (And c (Pred NoFilter)) f = c `And` (Pred f) 
+appendC (And (Pred NoFilter) c) f = c `And` (Pred f) 
+appendC c@(And _ _)             f = c `And` (Pred f)
+appendC (Or c (Pred NoFilter))  f = c `Or`  (Pred f)
+appendC (Or (Pred NoFilter) c)  f = c `Or`  (Pred f)
+appendC c@(Or _ _)              f = c `Or`  (Pred f)
+appendC c@(Pred _)              f = c `And` (Pred f)
+appendC c                       f = c `And` (Pred f)
+
+appendF ∷ Options → CmdLineFlag → Options
+appendF (O q m o l) (QF f) = O (f:q) m o l
+appendF (O q m o l) (MF f) = O q (f:m) o l
+appendF (O q m o l) (OF f) = O q m (f:o) l
+appendF (O q m o l) (LF f) = O q m o (f:l)
+appendF _ HelpF = Help
+appendF Help _  = Help
+
+parseFlags ∷ [CmdLineFlag] → Options
+parseFlags lst | HelpF ∈ lst = Help
+parseFlags [] = O [] [] [] []
+parseFlags (f:fs) = (parseFlags fs) `appendF` f
+
+-- | Build DefaultConfig (with query etc) from Options
+buildQuery ∷ BaseConfig    -- ^ Default config
+           → Options       -- ^ Cmdline options
+           → DefaultConfig
+buildQuery _ Help = error "Internal error: buildQuery does no sense for Help!"
+buildQuery dc (O qflags mflags oflags lflags) =
+    DConfig {
+      baseConfig = BConfig {
+          outOnlyFirst = update outOnlyFirst onlyFirst,
+          outColors    = update outColors    colors,
+          outIds       = update outIds       showIds,
+          outHighlight = update outHighlight highlight,
+          sorting      = update sorting      srt,
+          pruneL       = update pruneL       limitP,
+          minL         = update minL         limitM,
+          commandToRun = update commandToRun command,
+          prefix       = update prefix       aprefix,
+          descrFormat  = update descrFormat  dformat,
+          skipStatus   = update skipStatus   noStatus,
+          groupByFile  = update groupByFile  doGroupByFile,
+          groupByTag   = update groupByTag   doGroupByTag,
+          groupByStatus = update groupByStatus doGroupByStatus,
+          forcedStatus = update forcedStatus setStatus,
+          topStatus    = update topStatus    setTopStatus },
+      query        = fromMaybe Empty composedFlags }
+  where
+    update fn Nothing  = fn dc
+    update _  (Just x) = x
+
+    x ? lst | x ∈ lst   = Just True
+            | otherwise = Nothing
+
+    composedFlags | null qflags = Nothing
+                  | otherwise   = Just $ parseQuery qflags
+    (limitP,limitM) | null lflags = (Nothing, Nothing)
+                    | otherwise   = parseLimits (unLimit $ pruneL dc) (unLimit $ minL dc) lflags
+
+    onlyFirst = OnlyFirst ? oflags
+    colors    = Colors    ? oflags
+    highlight = Highlight ? oflags
+    showIds   = Ids       ? oflags
+
+    srtFlags = filter isSort oflags
+    srt | null srtFlags = Nothing
+        | otherwise     = Just $ getSorting (last srtFlags)
+
+    doGroupByFile   = GroupByFile   ? mflags
+    doGroupByTag    = GroupByTag    ? mflags
+    doGroupByStatus = GroupByStatus ? mflags
+
+    cmdFlags  = filter isCommand mflags
+    command | DotExport ∈ oflags = Just $ ShowAsDot
+            | null cmdFlags      = Nothing
+            | otherwise          = Just $ SystemCommand $ unExecute (last cmdFlags)
+
+    prefixFlags = filter isPrefix mflags
+    aprefix | null prefixFlags = Nothing
+            | otherwise        = Just $ Just $ unPrefix (last prefixFlags)
+
+    dflags = filter isDescribe mflags
+    dformat | null dflags = Nothing
+            | otherwise   = Just $unDescribe $ last dflags
+
+    noStatus = DoNotReadStatus ? mflags
+    newStatusFlags = filter isSetStatus mflags
+    setStatus | null newStatusFlags = Nothing
+              | otherwise           = Just $ Just $ newStatus $ last newStatusFlags
+
+    topStatusFlags = filter isTopStatus mflags
+    setTopStatus | null topStatusFlags = Nothing
+                 | otherwise           = Just $ Just $ newTopStatus $ last topStatusFlags
+
+    isSort (Sort _) = True
+    isSort _        = False
+    isDescribe (Describe _) = True
+    isDescribe _            = False
+    isCommand (Execute _) = True
+    isCommand _           = False
+    isPrefix (Prefix _) = True
+    isPrefix _          = False
+    isSetStatus (SetStatus _)  = True
+    isSetStatus _              = False
+    isTopStatus (SetTopStatus _) = True
+    isTopStatus _                = False
+
+parseLimits ∷ ℤ → ℤ → [LimitFlag] → (Maybe Limit,Maybe Limit)
+parseLimits dlp dlm flags = (Just limitP, Just limitM)
+  where
+    pruneFlags = filter isPrune flags
+    minFlags   = filter isMin flags
+
+    limitP'       = foldl min Unlimited $ map (Limit ∘ unPrune) pruneFlags
+    limitP | Unlimited ← limitP' = Limit dlp
+           | otherwise           = limitP'
+
+    limitM'       = foldl max (Limit 0) $ map (Limit ∘ unMin) minFlags
+    limitM | Unlimited ← limitM' = Limit dlm
+           | otherwise           = limitM'
+
+    isPrune (Prune _) = True
+    isPrune _         = False
+
+    isMin   (Start _) = True
+    isMin   _         = False
+
+parseQuery ∷ [QueryFlag] → Composed
+parseQuery flags = foldl appendC Empty flags
+
+-- | Parse command line
+parseCmdLine' ∷ DateTime             -- ^ Current date/time
+             → [String]              -- ^ Command line args
+             → Either String (Options, [FilePath]) -- ^ Error message or (Options, list of files)
+parseCmdLine' currDate args = 
+  case getOpt Permute (options currDate) (map decodeString args) of
+        (flags, [],      [])     → Right (parseFlags flags, ["TODO"])
+        (flags, nonOpts, [])     → Right (parseFlags flags, nonOpts)
+        (_,     _,       msgs)   → Left $ concat msgs ⧺ usage
+
+isPattern s = ('*' ∈ s) || ('?' ∈ s)
+
+-- | For given list of glob masks, return list of matching files
+glob ∷ [FilePath] → IO [FilePath]
+glob list = do
+  let patterns = filter isPattern list
+      files = filter (not ∘ isPattern) list
+  (matches, _) ← globDir (map compile patterns) "." 
+  return $ sort $ files ⧺ concat matches
+
+-- | Usage help for default command line options
+usage ∷  String
+usage = usageInfo header (options undefined)
+  where 
+    header = "Usage: todos [OPTION...] [INPUT FILES]"
+
+options ∷ DateTime → [OptDescr CmdLineFlag]
+options currDate = [
+    Option "1" ["only-first"] (NoArg (OF OnlyFirst))                 "show only first matching entry",
+    Option "c" ["color"]      (NoArg (OF Colors))                    "show colored output",
+    Option "H" ["highlight"]  (NoArg (OF Highlight))                 "instead of filtering TODOs, just highlight matching the query",
+    Option "I" ["show-ids"]   (NoArg (OF Ids))                       "show IDs of todos",
+    Option "A" ["prefix"]     (OptArg mkPrefix "PREFIX")             "use alternate parser: read only lines starting with PREFIX",
+    Option ""  ["dot"]        (NoArg (OF DotExport))                 "output entries in DOT (graphviz) format",
+    Option "D" ["describe"]   (OptArg mkDescribe "FORMAT")           "use FORMAT for descriptions",
+    Option "w" ["no-status"]  (NoArg (MF DoNotReadStatus))           "do not read status field from TODOs",
+    Option ""  ["set-status"] (ReqArg mkSetStatus "STRING")          "force all TODOs status to be equal to STRING",
+    Option ""  ["set-root-status"] (ReqArg mkTopStatus "STRING")     "force statuses of root TODOs to be equal to STRING",
+    Option "F" ["by-file"]    (NoArg (MF GroupByFile))               "group TODOs by source file",
+    Option "T" ["by-tag"]     (NoArg (MF GroupByTag))                "group TODOs by tag",
+    Option "Z" ["by-status"]  (NoArg (MF GroupByStatus))             "group TODOs by status",
+    Option "p" ["prune"]      (ReqArg mkPrune "N")                   "limit tree height to N",
+    Option "m" ["min-depth"]  (ReqArg mkMin "N")                     "show first N levels of tree unconditionally",
+    Option "t" ["tag"]        (ReqArg mkTag "TAG")                   "find items marked with TAG",
+    Option "g" ["grep"]       (ReqArg mkName "PATTERN")              "find items with PATTERN in name",
+    Option "G" ["description"] (ReqArg mkDescr "PATTERN")            "find items with PATTERN in description",
+    Option "s" ["status"]     (ReqArg mkStatus "STRING")             "find items with status equal to STRING",
+    Option "i" ["id"]         (ReqArg mkIdQ "STRING")                "find items with ID equal to STRING",
+    Option "a" ["and"]        (NoArg (QF AndCons))                   "logical AND",
+    Option "o" ["or"]         (NoArg (QF OrCons))                    "logical OR",
+    Option "n" ["not"]        (NoArg (QF NotCons))                   "logical NOT",
+    Option ""  ["sort"]       (ReqArg mkSort "FIELD")                "specify sorting",
+    Option "e" ["exec"]       (OptArg mkExecute "COMMAND")           "run COMMAND on each matching entry",
+    Option "S" ["start-date"] (ReqArg (mkStartDate currDate) "DATE") "find items with start date bounded with DATE",
+    Option "E" ["end-date"]   (ReqArg (mkEndDate currDate) "DATE")   "find items with end date bounded with DATE",
+    Option "d" ["deadline"]   (ReqArg (mkDeadline currDate) "DATE")  "find items with deadline bounded with DATE",
+    Option "h" ["help"]       (NoArg HelpF)                          "display this help"
+  ]
+
+mkSort s = OF $ Sort $ readSort s
+
+mkTag ∷  String → CmdLineFlag
+mkTag t = QF $ Tag t
+
+mkName ∷  String → CmdLineFlag
+mkName n = QF $ Name n
+
+mkStatus ∷  String → CmdLineFlag
+mkStatus s = QF $ Status s
+
+mkIdQ ∷  String → CmdLineFlag
+mkIdQ s = QF $ IdIs s
+
+mkDescr ∷  String → CmdLineFlag
+mkDescr s = QF $ Description s
+
+forceEither ∷  (Show t) ⇒ Either t b → b
+forceEither (Right x) = x
+forceEither (Left x) = error $ show x
+
+mkStartDate ∷  DateTime → String → CmdLineFlag
+mkStartDate dt s = QF $ StartDateIs $ forceEither $ parseDate undefined dt s
+
+mkEndDate ∷  DateTime → String → CmdLineFlag
+mkEndDate dt s = QF $ EndDateIs $ forceEither $ parseDate undefined dt s
+
+mkDeadline ∷  DateTime → String → CmdLineFlag
+mkDeadline dt s = QF $ DeadlineIs $ forceEither $ parseDate undefined dt s
+
+mkDescribe ∷  Maybe String → CmdLineFlag
+mkDescribe Nothing = MF $ Describe "%d"
+mkDescribe (Just f) = MF $ Describe f
+
+mkSetStatus ∷ String → CmdLineFlag
+mkSetStatus st = MF $ SetStatus st
+
+mkTopStatus ∷ String → CmdLineFlag
+mkTopStatus st = MF $ SetTopStatus st
+
+mkPrune ∷  String → CmdLineFlag
+mkPrune s = LF $ Prune (read s)
+
+mkMin ∷  String → CmdLineFlag
+mkMin s = LF $ Start (read s)
+
+mkPrefix ∷  Maybe [Char] → CmdLineFlag
+mkPrefix = MF ∘ Prefix ∘ fromMaybe "TODO:"
+
+mkExecute ∷  Maybe [Char] → CmdLineFlag
+mkExecute = MF ∘ Execute ∘ fromMaybe "echo %n %d"
diff --git a/Todos/Color.hs b/Todos/Color.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Color.hs
@@ -0,0 +1,145 @@
+{-# LANGUAGE UnicodeSyntax #-}
+module Todos.Color where
+
+import qualified Data.Map as M
+import Text.Printf
+import Data.Word
+import Data.Hash
+import Data.List (minimumBy)
+import Data.Function (on)
+import qualified System.Console.ANSI as ANSI
+
+import Todos.Types
+
+-- | Hue, saturation and value; all are in [0; 1] range.
+data HSV = HSV {
+  colorHue ∷ Double,
+  colorSaturation ∷ Double,
+  colorValue ∷ Double }
+  deriving (Eq)
+
+instance Show HSV where
+  show (HSV h s v) = printf "\"%.3f %.3f %.3f\"" h s v
+
+-- | Correspondence between console colors and HSV colors
+consoleColors ∷ [((ANSI.ColorIntensity, ANSI.Color), HSV)]
+consoleColors = 
+  [((ANSI.Dull, ANSI.Black),  HSV 0 0 0),
+   ((ANSI.Dull, ANSI.Red),    HSV 0 1 0.67),
+   ((ANSI.Dull, ANSI.Green),  HSV 0.333 1 0.67),
+   ((ANSI.Dull, ANSI.Yellow), HSV (1.0/12.0) 1 0.67),
+   ((ANSI.Dull, ANSI.Blue),   HSV 0.667 1 0.67),
+   ((ANSI.Dull, ANSI.Magenta), HSV (5.0/6.0) 1 0.67),
+   ((ANSI.Dull, ANSI.Cyan),   HSV 0.5 1 0.67),
+   ((ANSI.Dull, ANSI.White),  HSV 0 0 0.67),
+   ((ANSI.Vivid, ANSI.Black),  HSV 0 0 0.33),
+   ((ANSI.Vivid, ANSI.Red),    HSV 0 0.67 1),
+   ((ANSI.Vivid, ANSI.Green),  HSV 0.333 0.67 1),
+   ((ANSI.Vivid, ANSI.Yellow), HSV (1.0/12.0) 0.67 1),
+   ((ANSI.Vivid, ANSI.Blue),   HSV 0.667 0.67 1),
+   ((ANSI.Vivid, ANSI.Magenta), HSV (5.0/6.0) 0.67 1),
+   ((ANSI.Vivid, ANSI.Cyan),   HSV 0.5 0.67 1),
+   ((ANSI.Vivid, ANSI.White),  HSV 0 0 1) ]
+
+-- | Get console color which is nearest to given HSV color
+consoleColor ∷ HSV → (ANSI.ColorIntensity, ANSI.Color)
+consoleColor hsv = fst $ minimumBy (compare `on` ρ) consoleColors
+  where ρ (_, clr) = sum $ map (^2) [colorHue hsv - colorHue clr,
+                                     colorSaturation hsv - colorSaturation clr,
+                                     colorValue hsv - colorValue clr ]
+
+-- | Hue values for some common tags
+tagHues ∷ M.Map String Double
+tagHues = M.fromList $ [
+  ("BUG",   0.07),
+  ("NOTE",  0.25),
+  ("ERROR", 0.0),
+  ("TAG",   0.16),
+  ("TODO",  0.55)]
+
+hashAsDouble ∷ Hashable a ⇒ a → Double
+hashAsDouble x = (fromIntegral $ asWord64 $ hash x) / (fromIntegral (maxBound :: Word64))
+
+-- | Get color hue from tag name
+tagHue ∷ String → Double
+tagHue tag =
+  case M.lookup tag tagHues of
+    Nothing → hashAsDouble tag
+    Just h  → h
+
+-- | Color saturation values for some common statuses
+statusSats ∷ M.Map String Double
+statusSats = M.fromList $ [
+  ("*",      1.0),
+  ("URGENT", 1.0),
+  ("+",      0.9),
+  ("W",      0.9),
+  ("?",      0.8),
+  ("Q",      0.8),
+  ("M",      0.7),
+  ("L",      0.6),
+  ("DONE",   0.5),
+  ("FIXED",  0.5),
+  ("WORKSFORME", 0.45),
+  (":",      0.4),
+  ("-",      0.3),
+  ("INVALID", 0.3),
+  ("o",      0.3),
+  ("x",      0.2)]
+
+-- | Get color saturation from item status
+statusSat ∷ String → Double
+statusSat st = 
+  case M.lookup st statusSats of
+    Nothing → 0.4* hashAsDouble st
+    Just s  → s
+
+-- | Color hue values for some common item statuses
+statusHues ∷ M.Map String Double
+statusHues = M.fromList $ [
+  ("*",      0.0),
+  ("URGENT", 0.0),
+  ("+",      0.16),
+  ("W",      0.08),
+  ("?",      0.7),
+  ("Q",      0.8),
+  ("M",      0.2),
+  ("L",      0.6),
+  ("DONE",   0.25),
+  ("FIXED",  0.25),
+  ("NOTE",   0.3),
+  ("WORKSFORME", 0.1),
+  ("INVALID", 0.45),
+  (":",      0.4),
+  ("-",      0.09),
+  ("o",      0.8),
+  ("x",      0.2)]
+
+-- | Get color hue from item status
+statusHue ∷ String → Double
+statusHue st =
+  case M.lookup st statusHues of
+    Nothing → hashAsDouble st
+    Just h  → h
+
+-- | Get console color for item status
+statusColor ∷ String → (ANSI.ColorIntensity, ANSI.Color)
+statusColor st = consoleColor $ HSV (statusHue st) (statusSat st) 0.2
+
+-- | Get console color for item name (this is const Nothing)
+defItemConsoleColor ∷ TodoItem → Maybe (ANSI.ColorIntensity, ANSI.Color)
+defItemConsoleColor _ = Nothing
+
+-- | Get color for item (this is used in DOT output)
+getColor ∷ TodoItem → HSV
+getColor item = HSV h s v 
+  where
+    tags = itemTags item
+    n    = length tags
+    st   = itemStatus item
+    h    = case n of
+             0 → statusHue st
+             _ → (statusHue st + sum (map tagHue tags))/fromIntegral (n+1)
+    s    = statusSat st
+    v    = 0.8
+
diff --git a/Todos/CommandParser.hs b/Todos/CommandParser.hs
new file mode 100644
--- /dev/null
+++ b/Todos/CommandParser.hs
@@ -0,0 +1,26 @@
+{-# LANGUAGE UnicodeSyntax #-}
+
+module Todos.CommandParser where
+
+import Todos.Unicode
+import Todos.Types
+
+-- | Format item info
+printfItem ∷ String      -- ^ Format string
+           → TodoItem 
+           → String
+printfItem pattern item = printf pattern
+  where
+    printf "" = ""
+    printf [x] = [x]
+    printf ('%':c:xs) = (itemPart c) ⧺ printf xs
+    printf (x:xs) = x:printf xs
+
+    itemPart 'L' = show $ itemLevel item
+    itemPart 'n' = itemName item
+    itemPart 't' = unwords $ itemTags item
+    itemPart 's' = itemStatus item
+    itemPart 'd' = itemDescr item
+    itemPart 'f' = fileName item
+    itemPart 'l' = show $ lineNr item
+    itemPart x   = [x]
diff --git a/Todos/Config.hs b/Todos/Config.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Config.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE UnicodeSyntax, MultiParamTypeClasses #-}
+
+module Todos.Config where
+
+import Control.Monad.Reader
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Color
+import Todos.Shapes
+import qualified System.Console.ANSI as ANSI
+
+-- | Any user-specified runtime config type should belong to this class
+class RuntimeConfig c where
+  -- | Does given TODO item match query?
+  getPredicate ∷ DateTime → c → (TodoItem → 𝔹) 
+  -- | Get basic configuration
+  toBaseConfig ∷ c → BaseConfig
+
+-- | Any user-specified runtime config type should include at least this properties
+data BaseConfig = BConfig {
+      outOnlyFirst ∷ 𝔹,           -- ^ Output only first matching entry
+      outColors ∷ 𝔹,              -- ^ Show colored output
+      outIds :: 𝔹,                -- ^ Show IDs
+      outHighlight ∷ 𝔹,           -- ^ Highlight matching items
+      sorting ∷ SortingType,      -- ^ How to sort items
+      pruneL ∷ Limit, 
+      minL   ∷ Limit,
+      commandToRun ∷ TodoCommand,
+      prefix ∷ Maybe String,      -- ^ Nothing — use default parser, Just p — use alternate parser with prefix «p»
+      descrFormat ∷ String,
+      skipStatus ∷ 𝔹,             -- ^ Skip status field in input
+      groupByFile ∷ 𝔹,
+      groupByTag ∷ 𝔹,
+      groupByStatus ∷ 𝔹,
+      forcedStatus ∷ Maybe String,
+      topStatus ∷ Maybe String
+      }
+    deriving (Eq, Show)
+
+-- | Default runtime configuration type. Is read from command line and configs.
+data DefaultConfig = DConfig {
+      baseConfig ∷ BaseConfig,
+      query ∷ Composed }
+    deriving (Eq,Show)
+
+-- | Configuration for console output. Is generated in runtime from TodosConfig and Config.
+data PrintConfig c = PConfig {
+  printConfig ∷ c,
+  printStatusColor ∷  String → (ANSI.ColorIntensity, ANSI.Color),       -- ^ Color of status field from status
+  printItemColor ∷  TodoItem → Maybe (ANSI.ColorIntensity, ANSI.Color), -- ^ Color of item name
+  printHighlightColor ∷ (ANSI.ColorIntensity, ANSI.Color),              -- ^ Color to use for highlighting
+  doHighlight ∷ TodoItem → 𝔹                                            -- ^ Whether to highlight given item
+  }
+
+-- | User Todos config. User can specify it in @~/.config/todos/todos.hs@.
+data TodosConfig c = Todos {
+     parseCommandLine ∷ DateTime → c → [String] → CmdLineParseResult c,     -- ^ Function to parse command line
+     filterTodos ∷ DateTime → c → [Todo] → [Todo],                          -- ^ Any function to be run to transform read TODOs tree
+     statusConsoleColor ∷ String → (ANSI.ColorIntensity, ANSI.Color),       -- ^ Function to select a color of item's status field in console output
+     itemConsoleColor ∷ TodoItem → Maybe (ANSI.ColorIntensity, ANSI.Color), -- ^ Function to select a color of item's name in console output
+     highlightColor ∷ (ANSI.ColorIntensity, ANSI.Color),                    -- ^ Color to use for highlighting
+     itemColor ∷ TodoItem → HSV,                                            -- ^ Function to select color for item's node in DOT output
+     itemShape ∷ TodoItem → Shape,                                          -- ^ Function to select shape for item's node in DOT output
+     printTodos ∷ PrintConfig c → [Todo] → IO (),                           -- ^ Any function to output TODOs list
+     nullConfig ∷ c                                                         -- ^ Default Config (to be used without any options in command line and configs)
+}
+
+-- | Result of parsing command line
+data CmdLineParseResult c = 
+     Parsed c [FilePath]       -- ^ Parsed successfully, got Config and list of source files
+   | ParseError String         -- ^ Some error occured
+   | CmdLineHelp               -- ^ User asked for help
+   deriving (Eq,Show)
+
+-- | ask field from BaseConfig
+askBase ∷ (RuntimeConfig c) ⇒ (BaseConfig → a) → Reader c a
+askBase field = asks (field ∘ toBaseConfig)
+
diff --git a/Todos/ConfigInstances.hs b/Todos/ConfigInstances.hs
new file mode 100644
--- /dev/null
+++ b/Todos/ConfigInstances.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE UnicodeSyntax #-}
+-- | This module contains instances of RuntimeConfig class for DefaultConfig and PrintConfig
+module Todos.ConfigInstances where
+
+import Todos.Unicode
+import Todos.Config
+import Todos.CmdLine
+
+instance RuntimeConfig DefaultConfig where
+  getPredicate dt conf = compose dt $ query conf
+  toBaseConfig = baseConfig
+
+instance (RuntimeConfig c) ⇒ RuntimeConfig (PrintConfig c) where
+  getPredicate = const doHighlight
+  toBaseConfig = toBaseConfig ∘ printConfig
+
diff --git a/Todos/ConfigUtils.hs b/Todos/ConfigUtils.hs
new file mode 100644
--- /dev/null
+++ b/Todos/ConfigUtils.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE UnicodeSyntax, MultiParamTypeClasses #-}
+-- | This module contains some empty configs definitions and some function fields of defaultConfig
+module Todos.ConfigUtils where
+
+import System.Console.ANSI
+
+import Todos.Types
+import Todos.Config
+import Todos.Color
+import Todos.Shapes
+import Todos.CmdLine
+import Todos.Print
+import Todos.Tree
+
+-- | Empty BaseConfig
+emptyBaseConfig ∷ BaseConfig
+emptyBaseConfig = BConfig {
+  outOnlyFirst = False,
+  outColors = False,
+  outIds = False,
+  outHighlight = False,
+  sorting = DoNotSort,
+  pruneL = Limit 20,
+  minL = Limit 0,
+  commandToRun = JustShow,
+  prefix = Nothing,
+  descrFormat = "%d",
+  skipStatus = False,
+  groupByFile = False,
+  groupByTag = False,
+  groupByStatus = False,
+  forcedStatus = Nothing,
+  topStatus = Nothing
+}
+
+-- | Default empty DefaultConfig (nullConfig field of defaultConfig)
+emptyConfig ∷ DefaultConfig
+emptyConfig = DConfig {
+  baseConfig = emptyBaseConfig,
+  query = Empty }
+
+-- | Default Todos config
+defaultConfig ∷ TodosConfig DefaultConfig
+defaultConfig = Todos {
+  parseCommandLine = parseCmdLine,
+  filterTodos = defaultTodosFilter,
+  statusConsoleColor = statusColor,
+  itemConsoleColor = defItemConsoleColor,
+  highlightColor = (Vivid, Magenta),
+  itemColor = getColor,
+  itemShape = getShape,
+  printTodos = defaultPrintTodos,
+  nullConfig = emptyConfig
+}
+
+-- | Make a list transformer
+composeAll ∷ DateTime → DefaultConfig → (Todo → [Todo])
+composeAll date conf =
+  let pred = compose date $ query conf
+      bc = baseConfig conf
+  in  pruneSelector bc pred
+
+-- | Default filter for TODOs (filterTodos field of defaultConfig)
+defaultTodosFilter ∷ DateTime → DefaultConfig → [Todo] → [Todo]
+defaultTodosFilter dt conf todos =
+  let t = delTag "-" todos
+      bc = baseConfig conf
+  in  if outHighlight bc
+        then t
+        else concatMap (composeAll dt conf) t
+
+-- | Parse command line (default function)
+parseCmdLine ∷ DateTime              -- ^ Current date/time
+             → DefaultConfig         -- ^ Default config
+             → [String]              -- ^ Command line args
+             → CmdLineParseResult DefaultConfig
+parseCmdLine currDate dc args = 
+  case parseCmdLine' currDate args of
+    Right (opts, files) → case opts of
+                           Help → CmdLineHelp
+                           _    → Parsed (buildQuery (baseConfig dc) opts) files
+    Left str            → ParseError str
+
+-- | Prepare PrintConfig for console output functions. Is called from realTodos.
+mkPrintConfig ∷ (RuntimeConfig c) ⇒ DateTime → c → TodosConfig c → PrintConfig c
+mkPrintConfig dt conf tcfg = PConfig {
+  printConfig      = conf,
+  printStatusColor = statusConsoleColor tcfg,
+  printItemColor   = itemConsoleColor tcfg,
+  printHighlightColor = highlightColor tcfg,
+  doHighlight      = getPredicate dt conf }
+
diff --git a/Todos/Dates.hs b/Todos/Dates.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Dates.hs
@@ -0,0 +1,303 @@
+{-# LANGUAGE UnicodeSyntax #-}
+-- | Operations with dates
+module Todos.Dates
+  (parseDate, getCurrentDateTime,
+   pSpecDates)
+  where
+
+import Data.Char (toUpper)
+import Data.List
+import Data.Time.Calendar
+import Data.Time.LocalTime
+import Text.ParserCombinators.Parsec
+
+import Todos.Types
+import Todos.Unicode
+import Todos.Config
+import Todos.ParserTypes
+
+getCurrentDateTime ∷  IO DateTime
+getCurrentDateTime = do
+  zt ← getZonedTime
+  let lt = zonedTimeToLocalTime zt
+      ld = localDay lt
+      ltod = localTimeOfDay lt
+      (y,m,d) = toGregorian ld
+      h = todHour ltod
+      min = todMin ltod
+      s = round $ todSec ltod
+  return $ DateTime (fromIntegral y) m d h min s
+
+uppercase ∷ String → String
+uppercase = map toUpper
+
+isPrefixOfI ∷  String → String → Bool
+p `isPrefixOfI` s = (uppercase p) `isPrefixOf` (uppercase s)
+
+lookupS ∷ String → [(String,a)] → Maybe a
+lookupS _ [] = Nothing
+lookupS k ((k',v):other) | k `isPrefixOfI` k' = Just v
+                         | otherwise          = lookupS k other
+
+monthsN ∷ [(String,Int)]
+monthsN = zip months [1..]
+
+lookupMonth ∷ String → Maybe Int
+lookupMonth n = lookupS n monthsN
+
+date ∷  Int → Int → Int → DateTime
+date y m d = DateTime y m d 0 0 0
+
+addTime ∷  DateTime → Time → DateTime
+addTime dt t = dt {
+                 hour = tHour t + hour dt,
+                 minute = tMinute t + minute dt,
+                 second = tSecond t + second dt }
+
+times ∷ Int → TParser t → TParser [t]
+times 0 _ = return []
+times n p = do
+  ts ← times (n-1) p
+  t ← optionMaybe p
+  case t of
+    Just t' → return (ts ++ [t'])
+    Nothing → return ts
+                               
+number ∷ Int → Int → TParser Int
+number n m = do
+  t ← read `fmap` (n `times` digit)
+  if t > m
+    then fail "number too large"
+    else return t
+
+pYear ∷ TParser Int
+pYear = do
+  y ← number 4 10000
+  if y < 2000
+    then return (y+2000)
+    else return y
+
+pMonth ∷ TParser Int
+pMonth = number 2 12
+
+pDay ∷ TParser Int
+pDay = number 2 31
+
+euroNumDate ∷ TParser DateTime
+euroNumDate = do
+  d ← pDay
+  char '.'
+  m ← pMonth
+  char '.'
+  y ← pYear
+  return $ date y m d
+
+americanDate ∷ TParser DateTime
+americanDate = do
+  y ← pYear
+  char '/'
+  m ← pMonth
+  char '/'
+  d ← pDay
+  return $ date y m d
+
+euroNumDate' ∷ Int → TParser DateTime
+euroNumDate' year = do
+  d ← pDay
+  char '.'
+  m ← pMonth
+  return $ date year m d
+
+americanDate' ∷ Int → TParser DateTime
+americanDate' year = do
+  m ← pMonth
+  char '/'
+  d ← pDay
+  return $ date year m d
+
+strDate ∷ TParser DateTime
+strDate = do
+  d ← pDay
+  space
+  ms ← many1 letter
+  case lookupMonth ms of
+    Nothing → fail $ "unknown month: "++ms
+    Just m  → do
+      space
+      y ← pYear
+      notFollowedBy $ char ':'
+      return $ date y m d
+
+strDate' ∷ Int → TParser DateTime
+strDate' year = do
+  d ← pDay
+  space
+  ms ← many1 letter
+  case lookupMonth ms of
+    Nothing → fail $ "unknown month: "++ms
+    Just m  → return $ date year m d
+
+time24 ∷ TParser Time
+time24 = do
+  h ← number 2 23
+  char ':'
+  m ← number 2 59
+  x ← optionMaybe $ char ':'
+  case x of
+    Nothing → return $ Time h m 0
+    Just _ → do
+      s ← number 2 59
+      notFollowedBy letter
+      return $ Time h m s
+
+ampm ∷ TParser Int
+ampm = do
+  s ← many1 letter
+  case map toUpper s of
+    "AM" → return 0
+    "PM" → return 12
+    _ → fail "AM/PM expected"
+
+time12 ∷ TParser Time
+time12 = do
+  h ← number 2 12
+  char ':'
+  m ← number 2 59
+  x ← optionMaybe $ char ':'
+  s ← case x of
+            Nothing → return 0
+            Just s' → number 2 59
+  optional space
+  hd ← ampm
+  return $ Time (h+hd) m s
+
+pAbsDate ∷ Int → TParser DateTime
+pAbsDate year = do
+  date ← choice $ map try $ map ($ year) $ [
+                              const euroNumDate,
+                              const americanDate,
+                              const strDate,
+                              strDate',
+                              euroNumDate',
+                              americanDate']
+  optional $ char ','
+  s ← optionMaybe space
+  case s of
+    Nothing → return date
+    Just _ → do
+      t ← choice $ map try [time12,time24]
+      return $ date `addTime` t
+
+data DateIntervalType = Day | Week | Month | Year
+  deriving (Eq,Show,Read)
+
+data DateInterval = Days ℤ
+                  | Weeks ℤ
+                  | Months ℤ
+                  | Years ℤ
+  deriving (Eq,Show)
+
+convertTo ∷  DateTime → Day
+convertTo dt = fromGregorian (fromIntegral $ year dt) (month dt) (day dt)
+
+convertFrom ∷  Day → DateTime
+convertFrom dt = 
+  let (y,m,d) = toGregorian dt
+  in  date (fromIntegral y) m d
+
+modifyDate ∷  (t → Day → Day) → t → DateTime → DateTime
+modifyDate fn x dt = convertFrom $ fn x $ convertTo dt
+
+addInterval ∷  DateTime → DateInterval → DateTime
+addInterval dt (Days ds) = modifyDate addDays ds dt
+addInterval dt (Weeks ws) = modifyDate addDays (ws*7) dt
+addInterval dt (Months ms) = modifyDate addGregorianMonthsClip ms dt
+addInterval dt (Years ys) = modifyDate addGregorianYearsClip ys dt
+
+maybePlural ∷ String → TParser String
+maybePlural str = do
+  r ← string str
+  optional $ char 's'
+  return (capitalize r)
+
+pDateInterval ∷ TParser DateIntervalType
+pDateInterval = do
+  s ← choice $ map maybePlural ["day", "week", "month", "year"]
+  return $ read s
+
+pRelDate ∷ DateTime → TParser DateTime
+pRelDate date = do
+  offs ← (try futureDate) <|> (try passDate) <|> (try today) <|> (try tomorrow) <|> yesterday
+  return $ date `addInterval` offs
+
+futureDate ∷ TParser DateInterval
+futureDate = do
+  string "in "
+  n ← many1 digit
+  char ' '
+  tp ← pDateInterval
+  case tp of
+    Day →   return $ Days (read n)
+    Week →  return $ Weeks (read n)
+    Month → return $ Months (read n)
+    Year →  return $ Years (read n)
+
+passDate ∷ TParser DateInterval
+passDate = do
+  n ← many1 digit
+  char ' '
+  tp ← pDateInterval
+  string " ago"
+  case tp of
+    Day →   return $ Days $ - (read n)
+    Week →  return $ Weeks $ - (read n)
+    Month → return $ Months $ - (read n)
+    Year →  return $ Years $ - (read n)
+
+today ∷ TParser DateInterval
+today = do
+  string "today"
+  return $ Days 0
+
+tomorrow ∷ TParser DateInterval
+tomorrow = do
+  string "tomorrow"
+  return $ Days 1
+
+yesterday ∷ TParser DateInterval
+yesterday = do
+  string "yesterday"
+  return $ Days (-1)
+
+pDate ∷ DateTime → TParser DateTime
+pDate date =  (try $ pRelDate date) <|> (try $ pAbsDate $ year date)
+
+dateType ∷ String → DateType
+dateType "start" = StartDate
+dateType "end"   = EndDate
+dateType "deadline" = Deadline
+dateType _ = error "unknown date type"
+
+-- | Parse date/time with date type
+pSpecDate ∷ DateTime → TParser (DateType, DateTime)
+pSpecDate date = do
+  tp ← choice $ map string ["start","end","deadline"]
+  string ": "
+  dt ← pDate date
+  return (dateType tp, dt)
+
+-- | Parse set of dates with types (in parenthesis)
+pSpecDates ∷ DateTime → TParser [(DateType, DateTime)]
+pSpecDates date = do
+  char '('
+  pairs ← (pSpecDate date) `sepBy1` (string "; ")
+  string ") "
+  return pairs
+
+-- | Parse date/time
+parseDate ∷ BaseConfig
+          → DateTime  -- ^ Current date/time
+          → String    -- ^ String to parse
+          → Either ParseError DateTime
+parseDate conf date s = runParser (pDate date) conf "" s
+
diff --git a/Todos/Dot.hs b/Todos/Dot.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Dot.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE UnicodeSyntax, TypeSynonymInstances, FlexibleInstances #-}
+module Todos.Dot
+  (showAsDot)
+  where
+
+import Data.List
+import Data.Tree
+import Text.Printf
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Color
+import Todos.Shapes
+
+data Dot = Dot {
+  dotVertices ∷ [TodoItem],
+  dotEdges ∷ [(TodoItem, TodoItem)],
+  dotSubgraphs ∷ [Subgraph]
+  }
+
+data Subgraph = Subgraph {
+  subLabel ∷ String,
+  subItems ∷ [TodoItem] }
+  deriving (Eq)
+
+toDot ∷ Todo → Dot
+toDot todo = Dot (getVertices todo) (getEdges todo) (getSubgraphs todo)
+
+getVertices ∷ Todo → [TodoItem]
+getVertices (Node item forest) 
+  | itemStatus item == "GROUP" = concatMap getVertices forest
+  | otherwise = [item] ⧺ concatMap getVertices forest
+
+getEdges ∷ Todo → [(TodoItem, TodoItem)]
+getEdges (Node item forest) 
+  | itemStatus item == "GROUP" = concatMap getEdges forest
+  | otherwise = [(item, rootLabel child) | child ← forest] ⧺ concatMap getEdges forest
+
+getSubgraphs ∷ Todo → [Subgraph]
+getSubgraphs (Node item forest)
+  | itemStatus item == "GROUP" = Subgraph (showItem item) (nub $ sort $ concatMap flattern forest) :
+                                  concatMap getSubgraphs forest
+  | otherwise = []
+  where 
+    showItem item = showTags item ⧺ itemName item ⧺ "\\n" ⧺ itemDescr item
+    showTags item | null (itemTags item) = ""
+                  | otherwise = "[" ⧺ unwords (itemTags item) ⧺ "] "
+    flattern (Node item children) = item: concatMap flattern children
+
+instance Show Dot where
+  show (Dot vs es subs) = "digraph Todo {\n"
+                   ⧺ unlines (map (showDotNode getColor getShape) vs)
+                   ⧺ unlines (map showDotEdge es)
+                   ⧺ unlines (map showSubgraph subs)
+                   ⧺ "}\n"
+
+showD ∷ (TodoItem → HSV) → (TodoItem → Shape) → [Dot] → String
+showD colorFn shapeFn dots
+            = "digraph Todo {\n"
+            ⧺ "  rankdir = \"RL\";\n"
+            ⧺ "  node [shape=\"box\", style=\"filled\"];\n"
+            ⧺ unlines (map (showDotNode colorFn shapeFn) $ nub $ sort $ concatMap dotVertices dots)
+            ⧺ unlines (map showDotEdge $ nub $ sort $ concatMap dotEdges dots)
+            ⧺ unlines (map showSubgraph $ nub $ concatMap dotSubgraphs dots)
+            ⧺ "}\n"
+
+makeName ∷ TodoItem → String
+makeName item = "\"" ⧺ makeId item ⧺ "\""
+
+showDotNode ∷ (TodoItem → HSV) → (TodoItem → Shape) → TodoItem → String
+showDotNode colorFn shapeFn item =
+  printf "  %s [label=\"%s\\n%s\\n%s\", fillcolor=%s, shape=\"%s\"];" (makeName item) (itemStatus item) (unwords $ itemTags item) (itemName item) (show $ colorFn item) (show $ shapeFn item)
+
+showDotEdge ∷ (TodoItem, TodoItem) → String
+showDotEdge (x,y) 
+  | itemStatus x == "GROUP" = ""
+  | otherwise               = printf "  %s -> %s;" (makeName y) (makeName x)
+
+showSubgraph ∷ Subgraph → String
+showSubgraph (Subgraph label items) 
+  | null items = ""
+  | otherwise = "  subgraph \"cluster_" ⧺ makeId label ⧺ "\" {\n" 
+              ⧺ "    label=\"" ⧺ label ⧺ "\";\n"
+              ⧺ (unlines $ map ("    " ⧺) $ map showItem items)
+              ⧺ "\n  }"
+  where
+    showItem item = makeName item ⧺ ";"
+
+-- | Return DOT output for Todos
+showAsDot ∷ (TodoItem → HSV)   -- ^ Function to determine node color
+          → (TodoItem → Shape) -- ^ Function to determine node shape
+          → [Todo]             -- ^ Todo list
+          → String
+showAsDot colorFn shapeFn todos = (showD colorFn shapeFn) (map toDot todos)
+
diff --git a/Todos/Formatters.hs b/Todos/Formatters.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Formatters.hs
@@ -0,0 +1,122 @@
+{-# LANGUAGE UnicodeSyntax, TypeSynonymInstances, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-}
+module Todos.Formatters 
+  (OutItem (..),
+   Formatter,
+   outItem,
+   startFormat, newLine,
+   ConfigShow (..),
+   ConfigAdd (..)
+  ) where
+
+import Control.Monad
+import Control.Monad.Reader
+import System.Console.ANSI
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Config
+import Todos.ConfigInstances ()
+
+-- | Item which could be printed to the console
+data OutItem = OutString String
+             | OutSetColor ColorIntensity Color
+             | SetBold
+             | ResetAll
+    deriving (Show)
+
+-- | Produce a list of OutItem's depending on PrintConfig
+type Formatter c = Reader (PrintConfig c) [OutItem]
+
+-- | Empty Formatter
+startFormat ∷ Formatter c
+startFormat = return []
+
+-- | Output given string
+outString ∷ String → Formatter c
+outString s = return [OutString s]
+
+-- | Output new line
+newLine ∷ Formatter c
+newLine = outString "\n"
+
+class ConfigAdd c a where
+  -- | Execute Formatter and a consequently
+  (<++>) ∷ Formatter c → a → Formatter c
+
+instance ConfigAdd c (Formatter c) where
+  (<++>) = liftM2 (⧺)
+
+instance ConfigAdd c String where
+  cm <++> s = cm <++> ((return [OutString s]) ∷ Formatter c)
+
+setBold ∷  IO ()
+setBold = setSGR [SetConsoleIntensity BoldIntensity]
+
+setColor ∷ ColorIntensity → Color → IO ()
+setColor int clr = setSGR [SetColor Foreground int clr]
+
+-- | Reset all (color, bold, ...) attributes
+reset ∷  IO ()
+reset = setSGR []
+
+-- | Print OutItem to console
+outItem ∷  OutItem → IO ()
+outItem (OutString s)   = putStr s
+outItem (OutSetColor i c) = setColor i c
+outItem SetBold         = setBold
+outItem ResetAll        = reset
+
+-- | Similar to Show, but output can depend on PrintConfig
+class ConfigShow c s where
+  configShow ∷ s → Formatter c
+
+instance ConfigShow c String where
+  configShow s = return [OutString s]
+
+instance ConfigShow c (Formatter c) where
+  configShow = id
+  
+-- | Output bold (and maybe colored) item name
+bold ∷ (RuntimeConfig c) ⇒ TodoItem → Formatter c
+bold item = do
+  let s = itemName item
+  showColors ← askBase outColors
+  hlOn ← askBase outHighlight
+  getclr ← asks printItemColor
+  hlPred ← asks doHighlight
+  (hlInt, hlClr) ← asks printHighlightColor
+  return $ if showColors
+              then if hlOn && hlPred item
+                     then [SetBold, OutSetColor hlInt hlClr, OutString s, ResetAll]
+                     else case getclr item of
+                           Nothing        → [SetBold, OutString s, ResetAll]
+                           Just (int,clr) → [SetBold, OutSetColor int clr, OutString s, ResetAll]
+              else [OutString s]
+
+-- | Output colored item status
+colorStatus ∷ (RuntimeConfig c) ⇒ String → Formatter c
+colorStatus st = do
+  getclr ← asks printStatusColor
+  let (int, clr) = getclr st
+  col ← askBase outColors
+  if col
+    then return [OutSetColor int clr, OutString st, ResetAll]
+    else return [OutString st]
+
+instance (RuntimeConfig c) ⇒ ConfigShow c TodoItem where
+    configShow item = sf <++> (colorStatus s ∷ Formatter c) <++> " " <++> dates <++> tags <++> title <++> (if null descr then "" else "    "⧺descr)
+      where
+        sf ∷ Formatter c
+        sf = startFormat
+        ts = filter (not ∘ null) $ itemTags item
+        s = itemStatus item
+        descr = itemDescr item
+        dates | null dates' = ""
+              | otherwise = "(" ⧺ dates' ⧺ ") "
+        dates' = showDates [StartDate `is` startDate item, EndDate `is` endDate item, Deadline `is` deadline item]
+        tags = if null ts
+                 then ""
+                 else "[" ⧺ (unwords ts) ⧺ "] "
+        title ∷ Formatter c
+        title = bold item
+
diff --git a/Todos/IO.hs b/Todos/IO.hs
new file mode 100644
--- /dev/null
+++ b/Todos/IO.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE CPP #-}
+-- | Wrapper to support unicode IO with GHC 6.10 and 6.12
+module Todos.IO 
+  (putStr, putStrLn,readFile,getContents,print,
+   encodeString, decodeString)
+  where
+
+import Codec.Binary.UTF8.String (encodeString, decodeString)
+
+#if __GLASGOW_HASKELL__ < 612
+
+import Prelude hiding (putStr, putStrLn,readFile,getContents,print)
+import System.IO.UTF8
+
+#else
+
+import Prelude
+
+#endif
diff --git a/Todos/Loader.hs b/Todos/Loader.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Loader.hs
@@ -0,0 +1,227 @@
+{-# LANGUAGE UnicodeSyntax, PatternGuards #-}
+-- | Read TODOs from files and construct corresponding ADTs.
+module Todos.Loader
+  (loadTodo)
+  where
+
+import Prelude hiding (putStrLn,readFile,getContents,print)
+import Todos.IO
+import Control.Monad (forM)
+import qualified Data.Map as M
+import System.FilePath
+import Data.Maybe
+import Data.Tree
+import Data.List (nub, sort)
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Config
+import Todos.Parser
+
+todoName ∷ Todo → String
+todoName todo = itemName ⋄ rootLabel todo
+
+getDepends ∷ TodoMap → TodoItem → [Todo]
+getDepends m item = catMaybes [M.lookup name m | name ← depends item] 
+
+normalizeItem ∷ TodoMap → TodoItem → Todo
+normalizeItem m item = Node item (map (normalize m) ⋄ getDepends m item)
+
+normalize ∷ TodoMap → Todo → Todo 
+normalize m todo = Node item' ((map (normalize m) subTodos) ⧺ (map (normalize m) deps))
+  where
+    item ∷ TodoItem
+    item = rootLabel todo
+
+    item' ∷ TodoItem
+    item' = item {depends=[]}
+
+    subTodos ∷ [Todo]
+    subTodos = subForest todo
+
+    deps ∷ [Todo]
+    deps = getDepends m item
+
+normalizeList ∷ TodoMap → [Todo] → [Todo]
+normalizeList m todos = map (normalize m) todos
+
+-- | Almost same that readFile, but also works for special "-" file (stdin)
+readFile' ∷ FilePath → IO String
+readFile' "-"  = getContents
+readFile' file = readFile file
+
+-- | Load items from given file
+loadFile ∷ BaseConfig
+         → DateTime       -- ^ Current date/time
+         → FilePath       -- ^ Path to file
+         → IO [TodoItem]
+loadFile conf year path =
+  case prefix conf of
+    Nothing → do
+        text ← readFile' path
+        return $ parsePlain conf year path text
+    Just p  → do
+        text ← readFile' path
+        return $ parseAlternate conf 2 p year path text
+
+-- | Decrease item level
+(~-) ∷  TodoItem → ℤ → TodoItem
+i@(Item {itemLevel=n}) ~- k = i {itemLevel=n-k}
+
+-- | Increase item level
+(~+) ∷  TodoItem → ℤ → TodoItem
+i@(Item {itemLevel=n}) ~+ k = i {itemLevel=n+k}
+
+-- | Check if item level is 0
+iszero ∷  TodoItem → 𝔹
+iszero item = (itemLevel item)==0
+
+group' ∷  [TodoItem] → [[TodoItem]]
+group' [] = []
+group' (x:xs) = let (one,other) = break iszero xs
+                in (x:one):group' other
+
+mkTodo ∷ [TodoItem] → [Todo]
+mkTodo = (map mkTodo') ∘ group'
+
+mkTodo' ∷ [TodoItem] → Todo
+mkTodo' [] = error "Internal error: mkTodo' does not sense for empty list!"
+mkTodo' (x:xs) = Node x other
+    where other = mkTodo ⋄ map (~-lvl) xs
+          lvl = itemLevel (head xs)
+          
+consTodoMap ∷ [Todo] → TodoMap
+consTodoMap todos = M.fromList (cons1 100 todos)
+  where
+    cons1 ∷ Int → [Todo] → [(String,Todo)]
+    cons1 0 _ = []
+    cons1 max trees = [(todoName todo, todo) | todo ← trees] ⧺ cons1 (max-1) (children trees)
+    children ∷ [Todo] → [Todo]
+    children trees = concatMap subForest trees
+
+stitchTodos ∷ [TodoItem] → [Todo]
+stitchTodos items = 
+  let m = consTodoMap t
+      t = mkTodo items
+  in  normalizeList m t
+
+-- | Get all (different) tags from Todo list
+allTags ∷ [Todo] → [String]
+allTags todos = nub $ sort $ concatMap getTags todos
+  where
+    getTags (Node item children) = itemTags item ⧺ concatMap getTags children
+
+-- | Get all (different) statuses from Todo list
+allStatuses ∷ [Todo] → [String]
+allStatuses todos = nub $ sort $ concatMap getStatus todos
+  where
+    getStatus (Node item children) = itemStatus item: concatMap getStatus children
+
+grepBy ∷ (TodoItem → 𝔹) → [Todo] → [Todo]
+grepBy cond todos = concatMap grep todos
+  where
+    grep ∷ Todo → [Todo]
+    grep n@(Node _ children) = test n ⧺ concatMap grep children
+
+    test ∷ Todo → [Todo]
+    test n@(Node item _)
+      | cond item = [n]
+      | otherwise = []
+
+grepByTag ∷ String → [Todo] → [Todo]
+grepByTag tag todos = grepBy (\item → tag ∈ itemTags item) todos
+
+grepByStatus ∷ String → [Todo] → [Todo]
+grepByStatus st todos = grepBy (\item → st == itemStatus item) todos
+
+tagTodo ∷ String → [Todo] → Todo
+tagTodo tag todos = Node item $ grepByTag tag todos
+  where
+    item = Item {
+      itemLevel = 0,
+      itemName = tag,
+      itemTags = ["TAG"],
+      depends = [],
+      itemStatus = ":",
+      itemDescr = "",
+      startDate = Nothing,
+      endDate = Nothing,
+      deadline = Nothing,
+      fileName = "(no file)",
+      lineNr = 0 }
+
+groupByTag' ∷ [Todo] → [Todo]
+groupByTag' todos = 
+  map (\t → tagTodo t todos) (allTags todos) ⧺ todos
+
+statusTodo ∷ String → [Todo] → Todo
+statusTodo st todos = Node item $ grepByStatus st todos
+  where
+    item = Item {
+      itemLevel = 0,
+      itemName = st,
+      itemTags = ["STATUS"],
+      depends = [],
+      itemStatus = ":",
+      itemDescr = "",
+      startDate = Nothing,
+      endDate = Nothing,
+      deadline = Nothing,
+      fileName = "(no file)",
+      lineNr = 0 }
+
+groupByStatus' ∷ [Todo] → [Todo]
+groupByStatus' todos =
+  map (\s → statusTodo s todos) (allStatuses todos) ⧺ todos
+
+dirname ∷ FilePath → FilePath
+dirname path =
+  case dropFileName path of
+    [] → []
+    dir → takeFileName (init dir)
+
+fileTodo ∷ FilePath → TodoItem
+fileTodo path = Item {
+  itemLevel = 0,
+  itemName = takeFileName path,
+  itemTags = [dirname path],
+  depends = [],
+  itemStatus = ":",
+  itemDescr = path,
+  startDate = Nothing,
+  endDate = Nothing,
+  deadline = Nothing,
+  fileName = path,
+  lineNr = 0 }
+
+todosGroup ∷ FilePath → [TodoItem] -> [TodoItem]
+todosGroup path items =
+  if null items
+    then []
+    else fileTodo path: map (~+ 1) items
+
+changeTopStatus ∷ Maybe String → [Todo] → [Todo]
+changeTopStatus Nothing   todos = todos
+changeTopStatus (Just st) todos = map setStatus todos
+  where
+    setStatus (Node item children) = Node (item {itemStatus = st}) children
+
+-- | Load list of TODO trees from files
+loadTodo ∷ BaseConfig
+         → DateTime      -- ^ Current date/time
+         → [FilePath]    -- ^ List of files
+         → IO [Todo]
+loadTodo conf date paths = do
+    let grp = if groupByFile conf
+                then todosGroup
+                else const id
+    tss ← forM paths $ \path → grp path `fmap` loadFile conf date path
+    let todos = stitchTodos (concat tss)
+        byTag = if groupByTag conf
+                  then groupByTag' todos
+                  else todos
+        byStatus = if groupByStatus conf
+                     then groupByStatus' byTag
+                     else byTag
+    return $ changeTopStatus (topStatus conf) byStatus
+
diff --git a/Todos/Main.hs b/Todos/Main.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Main.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE UnicodeSyntax #-}
+
+module Todos.Main (todos) where
+
+import Prelude hiding (putStrLn,readFile,getContents,print)
+import IO
+import Data.Tree
+import System.Exit
+import System.Cmd (system)
+
+import System.Environment
+import Config.Dyre
+
+import Todos.Types
+import Todos.Unicode
+import Todos.Dates
+import Todos.Dot
+import Todos.CmdLine
+import Todos.Tree
+import Todos.ReadConfig
+import Todos.Loader
+import Todos.CommandParser
+import Todos.Config
+import Todos.ConfigUtils
+
+realTodos ∷ (RuntimeConfig c) ⇒ TodosConfig c → IO ()
+realTodos tcfg = do
+  currDate ← getCurrentDateTime 
+  config ← readConfig
+  args ← getArgs
+  let pres = (parseCommandLine tcfg) currDate (nullConfig tcfg) (config ⧺ args)
+  case pres of
+    Parsed q files' → do
+      let bc = toBaseConfig q
+      files ← glob files'
+      todos ← loadTodo bc currDate files
+      let queried  = (filterTodos tcfg) currDate q todos
+          format item = item {itemDescr = printfItem (descrFormat bc) item}
+      case commandToRun bc of
+        JustShow  → printTodos tcfg (mkPrintConfig currDate q tcfg) (mapT format queried)
+        ShowAsDot → 
+             putStrLn $ showAsDot (itemColor tcfg) (itemShape tcfg) (mapT format queried)
+        SystemCommand cmd → do
+             forT selected (\item → system $ printfItem cmd (format item))
+             return ()
+          where selected | outOnlyFirst bc = [Node (rootLabel $ head queried) []]
+                         | otherwise       = queried
+
+    ParseError str → error str
+    CmdLineHelp → do putStrLn usage
+                     exitWith ExitSuccess
+
+-- | Main function to run. User can specify TodosConfig with any runtime config
+-- type. By default (in todos.hs) defaultConfig is used, which uses DefaultConfig type.
+todos ∷ (RuntimeConfig c) ⇒ TodosConfig c → IO ()
+todos = wrapMain $ defaultParams {
+    projectName = "todos",
+    realMain    = realTodos,
+    statusOut   = const (return ())
+    }
+
diff --git a/Todos/Parser.hs b/Todos/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Parser.hs
@@ -0,0 +1,146 @@
+{-# LANGUAGE UnicodeSyntax, NoMonomorphismRestriction, TypeSynonymInstances, DeriveDataTypeable #-}
+module Todos.Parser
+    (parsePlain, parseAlternate)
+    where
+
+import Prelude hiding (putStrLn,readFile,getContents,print)
+import Data.List
+import Text.ParserCombinators.Parsec
+import Data.Char
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Dates
+import Todos.ParserTypes
+import Todos.Config
+
+strip ∷  String → String
+strip = reverse ∘ p ∘ reverse ∘ p
+  where
+    p = dropWhile isSpace
+
+pSpace ∷ TParser Char
+pSpace = oneOf " \t"
+
+pSpace' ∷ TParser String
+pSpace' = do
+    pSpace
+    return " "
+
+pSpaces ∷ TParser String
+pSpaces = many pSpace
+
+pDeps ∷ TParser [String]
+pDeps = do
+    string "("
+    ws ← (many1 ⋄ noneOf ",)\n\r") `sepBy` (char ',')
+    string ")"
+    return $ map strip ws
+
+pTags ∷ TParser [String]
+pTags = do
+    ts ← between (char '[') (char ']') $ word `sepBy1` pSpace
+    pSpaces
+    return ts
+  where
+    word = many1 (noneOf " \t\n\r]")
+
+pItem ∷ DateTime → TParser TodoItem
+pItem date = do
+    pos ← getPosition
+    s ← pSpaces
+    conf ← getState
+    stat ← if skipStatus conf
+             then case forcedStatus conf of
+                    Just fs → return fs
+                    Nothing → return "*"
+            else do 
+                rs ← pWord
+                case forcedStatus conf of
+                  Just fs → return fs
+                  Nothing → return rs
+    dates ← (try (pSpecDates date) <|> return [])
+    tags ← (try pTags <|> return [])
+    namew ← many1 pWord
+    pSpaces
+    deps ← (try pDeps <|> return [])
+    pSpaces
+    descr ← many (noneOf "\n\r")
+    pSpaces
+    many ⋄ oneOf "\n\r"
+    return ⋄ Item {
+        itemLevel = fromIntegral $ length s,
+        itemName = unwords namew,
+        itemTags = tags,
+        depends = deps,
+        itemStatus = stat,
+        itemDescr = descr,
+        startDate = lookup StartDate dates,
+        endDate = lookup EndDate dates,
+        deadline = lookup Deadline dates,
+        fileName = sourceName pos,
+        lineNr = sourceLine pos }
+
+pWord ∷ TParser String
+pWord = do
+    w ← many1 (noneOf " \t\n\r")
+    (try pSpace') <|> (return w)
+    return w
+
+pItems ∷ DateTime → TParser [TodoItem]
+pItems date = do
+  its ← many (pItem date)
+  eof
+  return its
+
+unwords' ∷  String → [String] → String
+unwords' prefix lst =
+  let (hd:tl) = map (filter (/='\r')) lst
+      addLines = filter (not ∘ (prefix `isPrefixOf`)) tl
+  in  case addLines of
+        [] → hd
+        _  → hd ⧺ "    {" ++ (unwords addLines) ++ "}"
+
+filterN ∷ (Num a, Enum a) ⇒ Int → String → [String] → ([a], [String])
+filterN n prefix lst = 
+  let zipped = zip [0..] lst
+      good   = filter (isGood ∘ snd) zipped
+      lns    = map fst good
+      sub k l = (take l) ∘ (drop k)
+      ans = map (unwords' prefix) [sub j n lst | j ← lns]
+      isGood x = prefix `isPrefixOf` x
+      cut = drop (1+length prefix) 
+  in (map (+1) lns, map cut ans)
+
+filterJoin ∷ Int → String → String → ([Int], String)
+filterJoin n prefix str = 
+  let (ns, lns) = filterN n prefix (lines str)
+  in  (ns, unlines lns)
+
+-- | Read list of TODO items from plain format 
+parsePlain ∷ BaseConfig
+           → DateTime   -- ^ Current date/time
+           → SourceName -- ^ Source file name
+           → String     -- ^ String to parse
+           → [TodoItem]
+parsePlain conf date path text = 
+  case runParser (pItems date) conf path text of
+      Right items → items
+      Left e → error $ show e
+
+-- | Read list of TODO items from alternate format
+parseAlternate ∷ BaseConfig 
+               → Int        -- ^ Number of lines after matching to include to item's description
+               → String     -- ^ Prefix to match
+               → DateTime   -- ^ Current date/time
+               → SourceName -- ^ Source file name
+               → String     -- ^ String to parse
+               → [TodoItem]
+parseAlternate conf next prefix date path text = 
+  let (ns, filtered) = filterJoin next prefix text
+      renumber lst = zipWith renumber1 ns lst
+      renumber1 n item = item {lineNr=n}
+  in case runParser (pItems date) conf path filtered of
+       Right items → renumber items
+       Left e      → error $ show e
+
diff --git a/Todos/ParserTypes.hs b/Todos/ParserTypes.hs
new file mode 100644
--- /dev/null
+++ b/Todos/ParserTypes.hs
@@ -0,0 +1,10 @@
+{-# LANGUAGE UnicodeSyntax #-}
+module Todos.ParserTypes where
+
+import Text.ParserCombinators.Parsec
+
+import Todos.Config
+
+
+type TParser a = GenParser Char BaseConfig a
+
diff --git a/Todos/Print.hs b/Todos/Print.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Print.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE UnicodeSyntax #-}
+module Todos.Print
+  (defaultPrintTodos, showTodos)
+  where
+
+import Control.Monad
+import Control.Monad.Reader
+import Data.List
+import Data.Tree
+import Data.Function (on)
+import System.Console.ANSI
+
+import Todos.Unicode
+import Todos.Types
+import Todos.Config
+import Todos.ConfigInstances ()
+import Todos.Formatters
+
+sortBy' ∷ SortingType → [Todo] → [Todo]
+sortBy' s | s == DoNotSort = id
+          | otherwise = sortBy sorter
+  where
+    sorter = compare `on` (f ∘ rootLabel)
+    f = case s of
+          DoNotSort → error "Internal error: sortBy' should not be called when DoNotSort is specified!"
+          ByTitle → itemName
+          ByStatus → itemStatus
+          ByTags → unwords ∘ itemTags
+          ByStartDate → show ∘ startDate
+          ByEndDate → show ∘ endDate
+          ByDeadline → show ∘ deadline 
+
+showT ∷ SortingType → Int → Todo → [Formatter DefaultConfig]
+showT s n (Node item todos) = 
+    (sf <++> showId item <++> replicate n ' ' <++> item') :
+      (concatMap (showT s (n+2)) $ sortBy' s todos)
+  where
+    sf ∷ Formatter DefaultConfig
+    sf = startFormat
+
+    item' ∷ Formatter DefaultConfig
+    item' = configShow item
+
+    showId :: TodoItem → Formatter DefaultConfig
+    showId item = do
+      s ← askBase outIds
+      c ← askBase outColors
+      if s
+        then if c 
+               then return [OutSetColor Dull Yellow, OutString $ makeId item ++ " ", ResetAll]
+               else return [OutString $ makeId item ++ " "]
+        else return [OutString ""]
+
+unlines'' ∷ [Formatter c] → Formatter c
+unlines'' lst = concat `fmap` (sequence $ intersperse newLine lst)
+
+showTodo ∷ Todo → Formatter DefaultConfig
+showTodo t = do
+  conf ← asks toBaseConfig
+  let f = case outOnlyFirst conf of
+            False → unlines''
+            True  → head
+  f $ showT (sorting conf) 0 t
+
+-- | Prepare TODOs for console output
+showTodos ∷ [Todo] → Formatter DefaultConfig
+showTodos lst = do
+  conf ← asks toBaseConfig
+  let f = case outOnlyFirst conf of
+            False → unlines''
+            True  → head
+  f $ map showTodo $ sortBy' (sorting conf) $ nub lst
+
+-- | Default function to output TODOs to console
+defaultPrintTodos ∷ PrintConfig DefaultConfig → [Todo] → IO ()
+defaultPrintTodos cfg lst = 
+  let lst' = runReader (showTodos lst) cfg
+  in  forM lst' outItem >> putStrLn ""
+
diff --git a/Todos/ReadConfig.hs b/Todos/ReadConfig.hs
new file mode 100644
--- /dev/null
+++ b/Todos/ReadConfig.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE UnicodeSyntax #-}
+
+-- | Module for parsing config files
+module Todos.ReadConfig
+  (readConfig)
+  where
+
+import Prelude hiding (putStrLn,readFile,getContents,print)
+import Todos.IO
+import System.Environment
+import System.FilePath 
+import System.Directory (doesFileExist)
+import Text.ParserCombinators.Parsec
+
+import Todos.Unicode
+
+word ∷ Parser String
+word = choice $ map try [quotedOption, simpleOption, quoted, simpleWord]
+
+simpleWord = many1 $ noneOf " \t\r\n=\"'"
+
+quotedOption = (try quotedLongOption) <|> quotedShortOption
+
+quotedLongOption ∷ Parser String
+quotedLongOption = do
+  string "--"
+  o ← simpleWord
+  char '='
+  v ← quoted
+  return ("--" ⧺ o ⧺ "=" ⧺ v)
+
+quotedShortOption ∷ Parser String
+quotedShortOption = do
+  string "-"
+  o ← simpleWord
+  v ← quoted
+  return ("-" ⧺ o ⧺ v)
+
+simpleOption = do
+  o ← simpleWord
+  optional $ char '='
+  v ← simpleWord
+  return (o ⧺ "=" ⧺ v)
+
+quoted = quoted1 <|> quoted2
+
+quoted1 = do
+  char '\''
+  s ← many1 $ noneOf "'"
+  char '\''
+  return s
+
+quoted2 = do
+  char '"'
+  s ← many1 $ noneOf "\""
+  char '"'
+  return s
+
+pConfig ∷ Parser [String]
+pConfig = word `sepBy` space
+
+parseConfig ∷ String → [String]
+parseConfig str = 
+  case parse pConfig "config file" str of
+    Right lst → lst
+    Left err → error $ show err
+
+readFile' ∷ FilePath → IO [String]
+readFile' path = 
+  do b ← doesFileExist path
+     if not b
+       then return []
+       else do
+              str ← readFile path
+              return $ parseConfig (unwords $ lines str)
+
+-- | Read list of options from config files
+readConfig ∷ IO [String]
+readConfig = do
+  home ← getEnv "HOME"
+  let homepath = home </> ".config" </> "todos" </> "todos.conf"
+  homecfg ← readFile' homepath
+  localcfg ← readFile' ".todos.conf"
+  return $ homecfg ⧺ localcfg
+  
diff --git a/Todos/Shapes.hs b/Todos/Shapes.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Shapes.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE UnicodeSyntax #-}
+module Todos.Shapes where
+
+import qualified Data.Map as M
+
+import Todos.Types
+
+-- | Supported node shapes for DOT output
+data Shape = 
+    Box 
+  | Ellipse
+  | Diamond
+  | DCircle
+  | Note
+  | Parallelogram
+  | Folder
+  deriving (Eq)
+
+instance Show Shape where
+  show Box           = "box"
+  show Ellipse       = "ellipse"
+  show Diamond       = "diamond"
+  show DCircle       = "doublecircle"
+  show Note          = "note"
+  show Parallelogram = "parallelogram"
+  show Folder        = "folder"
+
+-- | Node shapes for some common item statuses
+shapes ∷ M.Map String Shape
+shapes = M.fromList $ [
+  ("o", Ellipse),
+  ("O", DCircle),
+  (":", Folder),
+  ("*", Diamond),
+  ("/", Parallelogram),
+  ("NOTE", Note) ]
+
+-- | Get item shape for this item (default funciton)
+getShape ∷ TodoItem → Shape
+getShape item = 
+  case M.lookup (itemStatus item) shapes of
+    Nothing → Box
+    Just s  → s
+
diff --git a/Todos/Tree.hs b/Todos/Tree.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Tree.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE UnicodeSyntax, NoMonomorphismRestriction, FlexibleInstances, TypeSynonymInstances #-}
+module Todos.Tree 
+  (delTag,
+   pruneSelector,
+   tagPred, statusPred, grepPred, descPred, datePred, idPred,
+   forT, mapT)
+  where
+
+import Prelude hiding (putStrLn,readFile,getContents,print)
+import Control.Monad
+import Data.Generics
+import Data.List
+import Data.Tree
+import Text.Regex.PCRE
+
+import Todos.Types
+import Todos.Unicode
+import Todos.Config
+
+mapTags ∷  (Data a) ⇒ ([String] → [String]) → [a] → [a]
+mapTags f = map ⋄ everywhere ⋄ mkT changeTags
+  where
+    changeTags item@(Item {itemTags=ts}) = item {itemTags = f ts}
+        
+addTag ∷  (Data a) ⇒ String → [a] → [a]
+addTag t = mapTags (t:)
+
+delTag ∷  (Data a) ⇒ String → [a] → [a]
+delTag t = mapTags (delete t)
+
+pruneSelector ∷  BaseConfig → (TodoItem → 𝔹) → (Todo → [Todo])
+pruneSelector bc pred =
+  let Limit n = pruneL bc
+      Limit m = minL   bc
+  in  pruneSelector' n m pred
+        
+pruneSelector' ∷ ℤ → ℤ → (TodoItem → 𝔹) → (Todo → [Todo])
+pruneSelector' n m pred = select n 0 False
+    where
+        select k t b (Node item trees) | t < m       = [Node item ⋄ concatMap (select (n-1) (t+1) True) trees]
+                                       | pred item   = [Node item ⋄ concatMap (select (n-1) (t+1) True) trees]
+                                       | (k > 0) ∧ b = [Node item ⋄ concatMap (select (k-1) (t+1) True) trees]
+                                       | k > 0       = concatMap (select (k-1) (t+1) False) trees
+                                       | otherwise   = []                                               
+
+addS ∷  (Show a) ⇒ a → TodoItem → TodoItem
+addS s item@(Item {itemName=name}) = item {itemName = name ⧺ " — " ⧺ show s}
+
+-- | Check if item has given tag
+tagPred ∷  String → TodoItem → 𝔹
+tagPred tag = \item → tag ∈ itemTags item
+
+-- | Check if item has given status
+statusPred ∷  String → TodoItem → 𝔹
+statusPred st = \item → st == itemStatus item
+        
+-- | Check if item's title matches to given regexp
+grepPred ∷ String → TodoItem → 𝔹
+grepPred pattern = \item → itemName item =~ pattern
+
+-- | Check if item's description matches to given regexp
+descPred ∷ String → TodoItem → 𝔹
+descPred pattern = \item → itemDescr item =~ pattern
+
+-- | Check if item has given ID
+idPred :: String → TodoItem → 𝔹
+idPred hash = \item → makeId item == hash
+
+isLT ∷  (Ord t) ⇒ Maybe t → t → 𝔹
+isLT Nothing _ = False
+isLT (Just x) y = x <= y
+
+isGT ∷  (Ord t) ⇒ Maybe t → t → 𝔹
+isGT Nothing _ = False
+isGT (Just x) y = x >= y
+
+datePred ∷  (Ord a) ⇒ (t → Maybe a) → a → a → t → 𝔹
+datePred selector curr dt | dt >= curr = \item → selector item `isLT` dt
+                          | otherwise  = \item → selector item `isGT` dt
+
+-- | Flattern the tree
+flattern ∷ [Todo] → [Todo]
+flattern = concatMap flat
+    where
+        flat ∷ Todo → [Todo]
+        flat (Node item trees) = (Node item []):(concatMap flat trees)
+
+-- | For each item in the tree, execute given monadic action (this is similar
+-- to forM, but for trees instead of lists).
+forT ∷ (Monad m, Eq t) ⇒ [Tree t] → (t → m a) → m [b]
+forT todos f = forM (nub todos) forT'
+  where
+    forT' (Node item trees) =
+      do f item
+         res ← forM trees forT'
+         return $ last res
+
+-- | Similar to map, but for trees instead of lists.
+mapT ∷ (t → t) → [Tree t] → [Tree t]
+mapT f todos = map mapT' todos
+  where
+    mapT' (Node item trees) = Node (f item) (mapT f trees)
+
diff --git a/Todos/Types.hs b/Todos/Types.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Types.hs
@@ -0,0 +1,247 @@
+{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable, TypeSynonymInstances, FlexibleInstances, NoMonomorphismRestriction #-}
+
+module Todos.Types where
+
+import Prelude hiding (putStr, putStrLn,readFile,getContents,print)
+import Data.Hash
+
+import Data.Function 
+import Data.Generics hiding (GT)
+import Data.Char (toUpper)
+import Data.Maybe
+import Data.Tree
+import Data.List
+import qualified Data.Map as M
+import Text.ParserCombinators.Parsec
+import Numeric
+
+import Todos.Unicode
+
+-- | Kind of date
+data DateType = StartDate
+              | EndDate
+              | Deadline
+  deriving (Eq)
+
+instance Show DateType where
+  show StartDate = "start"
+  show EndDate = "end"
+  show Deadline = "deadline"
+
+data DateTime =
+  DateTime {
+    year ∷ Int,
+    month ∷ Int,
+    day ∷ Int,
+    hour ∷ Int,
+    minute ∷ Int,
+    second ∷ Int }
+  deriving (Eq,Ord,Data,Typeable)
+
+-- | 12 months names.
+months ∷ [String]
+months = ["january",
+          "february",
+          "march",
+          "april",
+          "may",
+          "june",
+          "july",
+          "august",
+          "september",
+          "october",
+          "november",
+          "december"]
+
+-- | capitalize first letter of the string
+capitalize ∷ String → String
+capitalize [] = []
+capitalize (x:xs) = (toUpper x):xs
+
+-- | Show name of given month
+showMonth ∷  Int → String
+showMonth i = capitalize $ months !! (i-1)
+
+instance Show DateTime where
+  show (DateTime y m d h min s) = 
+    show d ⧺ " " ⧺ showMonth m ⧺ " " ⧺ show y ⧺ ", " ⧺
+      show h ⧺ ":" ⧺ show min ⧺ ":" ⧺ show s
+
+-- | Only time, without date
+data Time = 
+  Time {
+    tHour ∷ Int,
+    tMinute ∷ Int,
+    tSecond ∷ Int }
+  deriving (Eq,Ord,Show,Data,Typeable)
+
+-- | TODO item itself.
+data TodoItem = Item {
+    itemLevel ∷ ℤ,               -- ^ Indentation level (from source file)
+    itemName ∷ String,           -- ^ Name (title) of the item
+    itemTags ∷ [String],         -- ^ Tags of the item
+    depends ∷ [String],          -- ^ Names (titles) of item's depends
+    itemStatus ∷ String,         -- ^ Status of the item
+    itemDescr ∷ String,          -- ^ Description of the item
+    startDate ∷ Maybe DateTime,  -- ^ Date when TODO is planned to start
+    endDate ∷ Maybe DateTime,    -- ^ Date when TODO is planned to end
+    deadline ∷ Maybe DateTime,   -- ^ Deadline for this TODO
+    fileName ∷ FilePath,         -- ^ Path to the source file
+    lineNr ∷ Line                -- ^ Line in the source file, where this item was defined
+  }
+  deriving (Eq,Data,Typeable)
+
+instance Hashable TodoItem where
+    hash item = foldl1 combine $ map ($ item) [hash ∘ itemName, hash ∘ itemDescr,
+                                               hash ∘ itemTags, hash ∘ itemStatus]
+
+-- | Make an ID for any hashable item. 16 hexadecimal digits.
+makeId :: (Hashable a) ⇒ a → String
+makeId item =
+  let s = showHex (asWord64 $ hash item) ""
+      l = length s 
+  in  if l < 16 
+        then replicate (16-l) '0' ++ s
+        else s
+
+-- | Tree of TODO items.
+type Todo = Tree TodoItem
+
+type TodoMap = M.Map String Todo
+
+data Limit = Unlimited
+           | Limit {unLimit ∷ ℤ}
+  deriving (Eq,Show)
+
+instance Ord Limit where
+    compare Unlimited Unlimited = EQ
+    compare Unlimited _ = GT
+    compare _ Unlimited = LT
+    compare (Limit x) (Limit y) = compare x y
+
+-- | Command line flag
+data CmdLineFlag = QF {queryFlag ∷ QueryFlag}
+                 | MF {modeFlag ∷ ModeFlag}
+                 | OF {outFlag ∷ OutFlag}
+                 | LF {limFlag ∷ LimitFlag}
+                 | HelpF
+    deriving (Eq,Show)
+
+-- | Flags to specify query
+data QueryFlag = Tag String
+               | Name {unName ∷ String}
+               | IdIs String
+               | Status String
+               | Description String
+               | StartDateIs DateTime
+               | EndDateIs DateTime
+               | DeadlineIs DateTime
+               | AndCons
+               | OrCons
+               | NotCons
+               | NoFilter
+     deriving (Eq,Ord,Show)        
+
+data LimitFlag = Prune {unPrune ∷ ℤ}
+               | Start {unMin ∷ ℤ}
+    deriving (Eq,Show)
+
+-- | Flags to specify parsing mode
+data ModeFlag = Execute {unExecute ∷ String}
+              | Prefix {unPrefix ∷ String}
+              | Describe {unDescribe ∷ String}
+              | DoNotReadStatus
+              | SetStatus {newStatus ∷ String}
+              | SetTopStatus {newTopStatus ∷ String}
+              | GroupByFile
+              | GroupByTag
+              | GroupByStatus
+    deriving (Eq,Ord,Show)
+
+-- | Flags to control output
+data OutFlag = OnlyFirst 
+             | Colors
+             | Highlight
+             | Ids
+             | DotExport
+             | Sort {getSorting ∷ SortingType}
+    deriving (Eq,Ord,Show)
+
+-- | Type of sorting
+data SortingType = DoNotSort
+                 | ByTitle
+                 | ByStatus
+                 | ByTags 
+                 | ByStartDate
+                 | ByEndDate
+                 | ByDeadline
+    deriving (Eq,Ord,Show)
+
+readSort ∷ String → SortingType
+readSort "no" = DoNotSort
+readSort "title" = ByTitle
+readSort "status" = ByStatus
+readSort "tags" = ByTags
+readSort "start-date" = ByStartDate
+readSort "end-date" = ByEndDate
+readSort "deadline" = ByDeadline
+readSort s = error $ "Unknown sorting type: "++s
+
+instance (Ord a) ⇒ Ord (Tree a) where
+  compare = compare `on` rootLabel
+
+-- TODO: - rename Options → QueryOptions or similar
+-- | Result of parsing command line
+data Options = O [QueryFlag] [ModeFlag] [OutFlag] [LimitFlag]
+             | Help
+
+-- | What to do with selected items
+data TodoCommand =
+    JustShow              -- ^ Just output items to console
+  | ShowAsDot             -- ^ Output graph in DOT format
+  | SystemCommand String  -- ^ Execute this system command for each item
+  deriving (Eq, Show)
+
+-- | Data type to store complex queries
+data Composed = Pred QueryFlag            -- ^ Simple query
+              | And Composed Composed     -- ^ Logical AND
+              | Or Composed Composed      -- ^ Logical OR
+              | Not Composed              -- ^ Logical NOT
+              | Empty                     -- ^ Empty query
+              | HelpC                     -- ^ User requests help
+    deriving (Eq,Show)
+
+is ∷  (Functor f) ⇒ t → f a → f (t, a)
+t `is`  x = (\a → (t,a)) `fmap` x
+
+showDate ∷  (DateType, DateTime) → String
+showDate (t,d) = show t ⧺ ": " ⧺ show d
+
+showDates ∷  [Maybe (DateType, DateTime)] → String
+showDates = intercalate "; " ∘ map showDate ∘ catMaybes
+
+instance Show TodoItem where
+    show item = s ⧺ " " ⧺ dates ⧺ tags ⧺ name ⧺ (if null descr then "" else "    "⧺descr)
+      where
+        name = itemName item
+        ts = itemTags item
+        s = itemStatus item
+        descr = itemDescr item
+        dates | null dates' = ""
+              | otherwise = "(" ⧺ dates' ⧺ ") "
+        dates' = showDates [StartDate `is` startDate item, EndDate `is` endDate item, Deadline `is` deadline item]
+        tags = if null ts
+                 then ""
+                 else "[" ⧺ (unwords ts) ⧺ "] "
+
+instance Ord TodoItem where
+  compare item1 item2 = 
+      let c1 = (compare `on` itemLevel) item1 item2
+          c2 = (compare `on` itemStatus) item1 item2
+          c3 = (compare `on` itemName) item1 item2
+      in  if c1 == EQ
+            then if c2 == EQ 
+                   then c3
+                   else c2
+            else c1
+
diff --git a/Todos/Unicode.hs b/Todos/Unicode.hs
new file mode 100644
--- /dev/null
+++ b/Todos/Unicode.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE UnicodeSyntax #-}
+module Todos.Unicode where
+
+type ℝ = Float
+type ℤ = Integer
+type 𝔹 = Bool
+
+-- | Concatenate lists 
+(⧺) ∷ [a] → [a] → [a]
+(⧺) = (++)
+
+(⋄) ∷ (a -> b) -> a -> b
+(⋄) = ($)
+infixr 0 ⋄
+
+(∘) :: (b -> c) -> (a -> b) -> a -> c
+(∘) = (.)
+
+(∨) :: Bool -> Bool -> Bool
+(∨) = (||)
+
+(∧) :: Bool -> Bool -> Bool
+(∧) = (&&)
+
+(∈) :: (Eq a) => a -> [a] -> Bool
+x ∈ lst = elem x lst
+
diff --git a/Types.hs b/Types.hs
deleted file mode 100644
--- a/Types.hs
+++ /dev/null
@@ -1,373 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable, TypeSynonymInstances, FlexibleInstances, NoMonomorphismRestriction #-}
-
-module Types where
-
-import Prelude hiding (putStr, putStrLn,readFile,getContents,print)
-import IO
-import System.Console.ANSI
-import Data.Hash
-
-import Control.Monad.Reader
-import Data.Function 
-import Data.Generics hiding (GT)
-import Data.Char (toUpper)
-import Data.Maybe
-import Data.Tree
-import Data.List
-import qualified Data.Map as M
-import Text.ParserCombinators.Parsec
-import Numeric
-
-import Unicode
-
-data DateType = StartDate
-              | EndDate
-              | Deadline
-  deriving (Eq)
-
-instance Show DateType where
-  show StartDate = "start"
-  show EndDate = "end"
-  show Deadline = "deadline"
-
-data DateTime =
-  DateTime {
-    year ∷ Int,
-    month ∷ Int,
-    day ∷ Int,
-    hour ∷ Int,
-    minute ∷ Int,
-    second ∷ Int }
-  deriving (Eq,Ord,Data,Typeable)
-
-months ∷ [String]
-months = ["january",
-          "february",
-          "march",
-          "april",
-          "may",
-          "june",
-          "july",
-          "august",
-          "september",
-          "october",
-          "november",
-          "december"]
-
-capitalize ∷ String → String
-capitalize [] = []
-capitalize (x:xs) = (toUpper x):xs
-
-showMonth ∷  Int → String
-showMonth i = capitalize $ months !! (i-1)
-
-instance Show DateTime where
-  show (DateTime y m d h min s) = 
-    show d ⧺ " " ⧺ showMonth m ⧺ " " ⧺ show y ⧺ ", " ⧺
-      show h ⧺ ":" ⧺ show min ⧺ ":" ⧺ show s
-
-data Time = 
-  Time {
-    tHour ∷ Int,
-    tMinute ∷ Int,
-    tSecond ∷ Int }
-  deriving (Eq,Ord,Show,Data,Typeable)
-
-data TodoItem = Item {
-    itemLevel ∷ ℤ,
-    itemName ∷ String,
-    itemTags ∷ [String],
-    depends ∷ [String],
-    itemStatus ∷ String,
-    itemDescr ∷ String,
-    startDate ∷ Maybe DateTime,
-    endDate ∷ Maybe DateTime,
-    deadline ∷ Maybe DateTime,
-    fileName ∷ FilePath,
-    lineNr ∷ Line}
-  deriving (Eq,Data,Typeable)
-
-instance Hashable TodoItem where
-    hash item = foldl1 combine $ map ($ item) [hash ∘ itemName, hash ∘ itemDescr,
-                                               hash ∘ itemTags, hash ∘ itemStatus]
-
-makeId :: TodoItem → String
-makeId item =
-  let s = showHex (asWord64 $ hash item) ""
-      l = length s 
-  in  if l < 16 
-        then replicate (16-l) '0' ++ s
-        else s
-
-type Todo = Tree TodoItem
-
-type TodoMap = M.Map String Todo
-
-data Limit = Unlimited
-           | Limit ℤ
-  deriving (Eq,Show)
-
-instance Ord Limit where
-    compare Unlimited Unlimited = EQ
-    compare Unlimited _ = GT
-    compare _ Unlimited = LT
-    compare (Limit x) (Limit y) = compare x y
-
-data CmdLineFlag = QF {queryFlag ∷ QueryFlag}
-                 | MF {modeFlag ∷ ModeFlag}
-                 | OF {outFlag ∷ OutFlag}
-                 | LF {limFlag ∷ LimitFlag}
-                 | HelpF
-    deriving (Eq,Show)
-
-data QueryFlag = Tag String
-               | Name {unName ∷ String}
-               | IdIs String
-               | Status String
-               | Description String
-               | StartDateIs DateTime
-               | EndDateIs DateTime
-               | DeadlineIs DateTime
-               | AndCons
-               | OrCons
-               | NotCons
-               | NoFilter
-     deriving (Eq,Ord,Show)        
-
-data LimitFlag = Prune {unPrune ∷ ℤ}
-               | Start {unMin ∷ ℤ}
-    deriving (Eq,Show)
-
-data ModeFlag = Execute {unExecute ∷ String}
-              | Prefix {unPrefix ∷ String}
-              | Describe {unDescribe ∷ String}
-              | DoNotReadStatus
-              | SetStatus {newStatus ∷ String}
-    deriving (Eq,Ord,Show)
-
-data OutFlag = OnlyFirst 
-             | Colors
-             | Ids
-             | DotExport
-             | Sort {getSorting ∷ SortingType}
-    deriving (Eq,Ord,Show)
-
-data SortingType = DoNotSort
-                 | ByTitle
-                 | ByStatus
-                 | ByTags 
-                 | ByStartDate
-                 | ByEndDate
-                 | ByDeadline
-    deriving (Eq,Ord,Show)
-
-readSort ∷ String → SortingType
-readSort "no" = DoNotSort
-readSort "title" = ByTitle
-readSort "status" = ByStatus
-readSort "tags" = ByTags
-readSort "start-date" = ByStartDate
-readSort "end-date" = ByEndDate
-readSort "deadline" = ByDeadline
-readSort s = error $ "Unknown sorting type: "++s
-
-type Transformer = Reader Config (Todo → [Todo])
-type ListTransformer = Reader Config ([Todo] → [Todo])
-
-transformList ∷  r → Reader r (t → a) → t → a
-transformList conf tr list = do
-    f ← tr
-    return (f list)
-  `runReader` conf
-
-data OutItem = OutString String
-             | OutSetColor Color
-             | SetBold
-             | ResetAll
-    deriving (Show)
-
-type Formatter = Reader Config [OutItem]
-
-newtype IOList = IOL [Formatter]
-
-startFormat ∷ Formatter
-startFormat = return []
-
-outString ∷ String → Formatter
-outString s = return [OutString s]
-
-newLine ∷ Formatter
-newLine = outString "\n"
-
-class ConfigAdd a where
-  (<++>) ∷ Formatter → a → Formatter
-
-instance ConfigAdd Formatter where
-  (<++>) = liftM2 (⧺)
-
-instance ConfigAdd String where
-  cm <++> s = cm <++> ((return [OutString s]) ∷ Formatter)
-
-setBold ∷  IO ()
-setBold = setSGR [SetConsoleIntensity BoldIntensity]
-
-setColor ∷  Color → IO ()
-setColor clr = setSGR [SetColor Foreground Dull clr]
-
-reset ∷  IO ()
-reset = setSGR []
-
-outItem ∷  OutItem → IO ()
-outItem (OutString s)   = putStr s
-outItem (OutSetColor c) = setColor c
-outItem SetBold         = setBold
-outItem ResetAll        = reset
-
-runFormatter ∷ Config → Formatter → IO ()
-runFormatter conf cm = 
-  let lst = runReader cm conf
-  in  mapM_ outItem lst
-
-class ConfigShow s where
-  configShow ∷ s → Formatter
-
-instance ConfigShow String where
-  configShow s = return [OutString s]
-
-instance ConfigShow Formatter where
-  configShow = id
-  
-showIO ∷ (ConfigShow a) ⇒ Config → a → IO ()
-showIO conf = (runFormatter conf) ∘ configShow
-
-instance (Ord a) ⇒ Ord (Tree a) where
-  compare = compare `on` rootLabel
-
--- TODO: - rename Options → QueryOptions or similar
-data Options = O [QueryFlag] [ModeFlag] [OutFlag] [LimitFlag]
-             | Help
-
-data TodoCommand =
-    JustShow
-  | ShowAsDot
-  | SystemCommand String
-  deriving (Eq, Show)
-
-data Config = Config {
-      outOnlyFirst ∷ 𝔹,
-      outColors ∷ 𝔹,
-      outIds :: 𝔹,
-      sorting ∷ SortingType,
-      pruneL ∷ Limit,
-      minL   ∷ Limit,
-      commandToRun ∷ TodoCommand,
-      prefix ∷ Maybe String,
-      descrFormat ∷ String,
-      skipStatus ∷ 𝔹,
-      forcedStatus ∷ Maybe String,
-      query ∷ Composed }
-    deriving (Eq,Show)
-
-emptyConfig = Config {
-  outOnlyFirst = False,
-  outColors = False,
-  outIds = False,
-  sorting = DoNotSort,
-  pruneL = Unlimited,
-  minL = Unlimited,
-  commandToRun = JustShow,
-  prefix = Nothing,
-  descrFormat = "%d",
-  skipStatus = False,
-  forcedStatus = Nothing,
-  query = Empty }
-
-data Composed = Pred QueryFlag
-              | And Composed Composed
-              | Or Composed Composed
-              | Not Composed
-              | Empty
-              | HelpC
-    deriving (Eq,Show)
-
-is ∷  (Functor f) ⇒ t → f a → f (t, a)
-t `is`  x = (\a → (t,a)) `fmap` x
-
-showDate ∷  (Show t, Show t1) ⇒ (t, t1) → [Char]
-showDate (t,d) = show t ⧺ ": " ⧺ show d
-
-showDates ∷  (Show t, Show t1) ⇒ [Maybe (t, t1)] → [Char]
-showDates = intercalate "; " ∘ map showDate ∘ catMaybes
-
-instance Show TodoItem where
-    show item = s ⧺ " " ⧺ dates ⧺ tags ⧺ name ⧺ (if null descr then "" else "    "⧺descr)
-      where
-        n = itemLevel item
-        name = itemName item
-        ts = itemTags item
-        s = itemStatus item
-        descr = itemDescr item
-        dates | null dates' = ""
-              | otherwise = "(" ⧺ dates' ⧺ ") "
-        dates' = showDates [StartDate `is` startDate item, EndDate `is` endDate item, Deadline `is` deadline item]
-        tags = if null ts
-                 then ""
-                 else "[" ⧺ (unwords ts) ⧺ "] "
-
-bold ∷ String → Formatter
-bold s = do
-  col ← asks outColors 
-  if col
-    then return [SetBold, OutString s, ResetAll]
-    else return [OutString s]
-
-lookupC ∷  (Eq a1) ⇒ a1 → [([a1], a)] → Maybe a
-lookupC k [] = Nothing
-lookupC k ((lst,c):other) | k ∈ lst   = Just c
-                          | otherwise = lookupC k other
-
-statusColors ∷  [([String], Color)]
-statusColors = 
-  [(["FIXED", "DONE"], Green),
-   (["INVALID"],       Magenta),
-   (["*"],             Red),
-   (["?"],             Blue)]
-
-colorStatus ∷ String → Formatter
-colorStatus st =
-  case lookupC st statusColors of
-    Nothing → return [OutString st]
-    Just clr → do
-      col ← asks outColors 
-      if col
-        then return [OutSetColor clr, OutString st, ResetAll]
-        else return [OutString st]
-
-instance ConfigShow TodoItem where
-    configShow item = startFormat <++> colorStatus s <++> " " <++> dates <++> tags <++> bold name <++> (if null descr then "" else "    "⧺descr)
-      where
-        n = itemLevel item
-        name = itemName item
-        ts = itemTags item
-        s = itemStatus item
-        descr = itemDescr item
-        dates | null dates' = ""
-              | otherwise = "(" ⧺ dates' ⧺ ") "
-        dates' = showDates [StartDate `is` startDate item, EndDate `is` endDate item, Deadline `is` deadline item]
-        tags = if null ts
-                 then ""
-                 else "[" ⧺ (unwords ts) ⧺ "] "
-
-instance Ord TodoItem where
-  compare item1 item2 = 
-      let c1 = (compare `on` itemLevel) item1 item2
-          c2 = (compare `on` itemStatus) item1 item2
-          c3 = (compare `on` itemName) item1 item2
-      in  if c1 == EQ
-            then if c2 == EQ 
-                   then c3
-                   else c2
-            else c1
-
-type TParser a = GenParser Char Config a
-
diff --git a/Unicode.hs b/Unicode.hs
deleted file mode 100644
--- a/Unicode.hs
+++ /dev/null
@@ -1,13 +0,0 @@
-module Unicode where
-
-type ℝ = Float
-type ℤ = Integer
-type 𝔹 = Bool
-
-(⧺) = (++)
-(⋄) = ($)
-infixr 0 ⋄
-(∘) = (.)
-(∨) = (||)
-(∧) = (&&)
-x ∈ lst = elem x lst
diff --git a/test.hs b/test.hs
deleted file mode 100644
--- a/test.hs
+++ /dev/null
@@ -1,23 +0,0 @@
-{-# LANGUAGE UnicodeSyntax, PatternGuards #-}
-module Test where
-
-import Control.Monad.Reader
-import Data.Maybe
-import Data.Tree
-import Data.List
-
-import Unicode
-import Types
-import TodoLoader
-import TodoTree
-import CommandParser
-import Config
-import CmdLine
-
-cfg = Config False False (Limit 20) (Limit 20) Nothing Nothing "%d" Empty
-
-test file = do
-  todos ← loadTodo Nothing [file]
-  printTodos cfg todos
-  putStrLn ""
-
diff --git a/test.txt b/test.txt
deleted file mode 100644
--- a/test.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-Line of text
-Text
-TODO: * one todo
-description
-text
-text
-TODO: * another todo
-descr
-text
-text
-TODO: + last todo
diff --git a/todos.cabal b/todos.cabal
--- a/todos.cabal
+++ b/todos.cabal
@@ -1,5 +1,5 @@
 Name:           todos
-Version:        0.2
+Version:        0.3
 Cabal-Version:  >= 1.6
 License:        BSD3
 License-File:   LICENSE
@@ -13,16 +13,60 @@
                 plain-text file, and todos allows you to show only needed of
                 them. So, todos works as specialized `grep' utility.
 
-Extra-source-files: CmdLine.hs CommandParser.hs Config.hs ConstrSet.hs Dates.hs IO.hs
-                    Makefile README README.ru Setup.hs test.hs test.txt TODO
-                    TodoLoader.hs TodoParser.hs todos.hs TodoTree.hs Types.hs
-                    Unicode.hs Dot.hs Color.hs Shapes.hs
+Extra-source-files: Makefile README README.ru TODO
 
-Executable todos
-  Build-Depends:  base >= 3 && <= 5, haskell98, utf8-string, containers, parsec >= 2 && < 3,
+library
+  Exposed-Modules: Todos
+                   Todos.CmdLine
+                   Todos.Color
+                   Todos.ParserTypes
+                   Todos.Dates
+                   Todos.CommandParser
+                   Todos.Parser
+                   Todos.Config
+                   Todos.ConfigInstances
+                   Todos.ConfigUtils
+                   Todos.ReadConfig
+                   Todos.Dot
+                   Todos.IO
+                   Todos.Main
+                   Todos.Shapes
+                   Todos.Loader
+                   Todos.Tree
+                   Todos.Types
+                   Todos.Unicode
+                   Todos.Formatters
+                   Todos.Print
+
+  Build-Depends:  base >= 3 && <= 5, haskell98, containers, parsec >= 2 && < 3,
                   syb, mtl, ansi-terminal, Glob, time, regex-pcre, directory, filepath,
-                  process, data-hash
-  Main-Is:        todos.hs
+                  process, data-hash, dyre, utf8-string
+
+  ghc-options: -fwarn-unused-imports
+
+Executable todos
+  Main-Is:       todos.hs
+  Other-Modules: Todos
+                 Todos.CmdLine
+                 Todos.Color
+                 Todos.ParserTypes
+                 Todos.Dates
+                 Todos.CommandParser
+                 Todos.Parser
+                 Todos.Config
+                 Todos.ConfigUtils
+                 Todos.ConfigInstances
+                 Todos.ReadConfig
+                 Todos.Dot
+                 Todos.IO
+                 Todos.Main
+                 Todos.Shapes
+                 Todos.Loader
+                 Todos.Tree
+                 Todos.Types
+                 Todos.Unicode
+                 Todos.Formatters
+                 Todos.Print
 
 Source-repository head
   type:     git
diff --git a/todos.hs b/todos.hs
--- a/todos.hs
+++ b/todos.hs
@@ -1,55 +1,7 @@
-{-# LANGUAGE UnicodeSyntax, PatternGuards #-}
-
-import Prelude hiding (putStrLn,readFile,getContents,print)
-import IO
-import Codec.Binary.UTF8.String
-import System (getArgs)
-import System.Exit
-import System.Cmd (system)
-
-import Control.Monad.Reader
-import Data.Maybe
-import Data.Tree
-import Data.List
+{-# LANGUAGE UnicodeSyntax #-}
 
-import Unicode
-import Types
-import TodoLoader
-import TodoTree
-import CommandParser
-import Config
-import CmdLine
-import Dates (getCurrentDateTime)
-import Dot
+import Todos
 
 main ∷  IO ()
-main = do
-  currDate ← getCurrentDateTime 
-  config ← readConfig
-  args ← getArgs
-  let (loptions, files') = parseCmdLine currDate (config ⧺ args)
-  files ← glob files'
-  case loptions of
-    O qflags mflags oflags lflags → 
-      do
-        let options = O qflags mflags oflags lflags
-            q = buildQuery options
-        todos ← loadTodo q currDate files
-        let todos'  = delTag "-" todos
-            queried = transformList q (composeAll currDate) todos'
-            format item = item {itemDescr = printfItem (descrFormat q) item}
-        case commandToRun q of
-          JustShow  → do
-               printTodos q (mapT format queried)
-               putStrLn ""
-          ShowAsDot → 
-               putStrLn $ showAsDot (mapT format queried)
-          SystemCommand cmd → do
-               forT selected (\item → system $ printfItem cmd (format item))
-               return ()
-            where selected | outOnlyFirst q = [Node (rootLabel $ head queried) []]
-                           | otherwise       = queried
-
-    Help → do putStrLn usage
-              exitWith ExitSuccess
+main = todos defaultConfig
 
