diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for staversion
+
+## 0.1.0.0  -- 2016-10-16
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2016, Toshio Ito
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Toshio Ito nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+# staversion
+
+![travis status](https://api.travis-ci.org/debug-ito/staversion.png)
+
+staversion is a command-line tool to look for version numbers for Haskell packages in specific stackage resolvers. It answers to questions like "What version is the package X in stackage lts-Y.ZZ?" It aims to make it easier to write `build-depends` section in YOUR_PACKAGE.cabal.
+
+    $ staversion --resolver lts-4.2 conduit
+    ------ lts-4.2
+    conduit ==1.2.6.1
+    
+    $ staversion --resolver lts-4.2 --resolver lts-7.0 conduit
+    ------ lts-4.2
+    conduit ==1.2.6.1
+    
+    ------ lts-7.0
+    conduit ==1.2.7
+    
+    $ staversion --resolver lts-4.2 --resolver lts-7.0 conduit base
+    ------ lts-4.2
+    conduit ==1.2.6.1,
+    base ==4.8.2.0
+    
+    ------ lts-7.0
+    conduit ==1.2.7,
+    base ==4.9.0.0
+
+Currently staversion reads build plan YAML files that are stored locally in your computer.
+
+
+## TODO
+
+- Fetch build plan YAML files from github.com
+- Cache build plans in some local storage (SQLite?)
+- Expand major version resolvers (lts-X) into full resolvers (lts-X.YY)
+- Search for the latest version numbers hosted in hackage.
+- Read `build-depends` sections .cabal files for package name queries.
+- Show version number ranges supported by the given resolvers.
+
+## Author
+
+Toshio Ito <debug.ito@gmail.com>
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,4 @@
+module Main
+       (main) where
+
+import Staversion.Internal.Exec (main)
diff --git a/src/Staversion/Internal/BuildPlan.hs b/src/Staversion/Internal/BuildPlan.hs
new file mode 100644
--- /dev/null
+++ b/src/Staversion/Internal/BuildPlan.hs
@@ -0,0 +1,65 @@
+-- |
+-- Module: Staversion.Internal.BuildPlan
+-- Description:  Handle build plan YAML files.
+-- Maintainer: Toshio Ito <debug.ito@gmail.com>
+--
+-- __This is an internal module. End-users should not use it.__
+module Staversion.Internal.BuildPlan
+       ( PackageName,
+         BuildPlan,
+         loadBuildPlanYAML,
+         packageVersion,
+         parseVersionText
+       ) where
+
+import Control.Applicative (empty, (<$>), (<*>))
+import Control.Exception (throwIO)
+import Data.Aeson (FromJSON(..), (.:), Value(..), Object)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Data.HashMap.Strict as HM
+import Data.Maybe (listToMaybe)
+import Data.Monoid ((<>))
+import Data.Text (Text, unpack)
+import Data.Traversable (Traversable(traverse))
+import Data.Version (Version, parseVersion)
+import qualified Data.Yaml as Yaml
+import Text.Read (readMaybe)
+import Text.ParserCombinators.ReadP (readP_to_S)
+
+import Staversion.Internal.Query (PackageName)
+
+-- | A data structure that keeps a map between package names and their
+-- versions.
+newtype BuildPlan = BuildPlan (HM.HashMap PackageName Version)
+
+instance FromJSON BuildPlan where
+  parseJSON (Object object) = (\p1 p2 -> BuildPlan $ p1 <> p2) <$> core_packages <*> other_packages where
+    core_packages = parseSysInfo =<< (object .: "system-info")
+    parseSysInfo (Object o) = parseCorePackages =<< (o .: "core-packages")
+    parseSysInfo _ = empty
+    parseCorePackages (Object o) = traverse (\v -> versionParser =<< parseJSON v) o
+    parseCorePackages _ = empty
+
+    other_packages = parsePackages =<< (object .: "packages")
+    parsePackages (Object o) = traverse parsePackageObject o
+    parsePackages _ = empty
+    parsePackageObject (Object o) = versionParser =<< (o .: "version")
+    parsePackageObject _ = empty
+    versionParser = maybe empty return . parseVersionText
+  parseJSON _ = empty
+
+
+-- | Load a 'BuildPlan' from a file.
+loadBuildPlanYAML :: FilePath -> IO BuildPlan
+loadBuildPlanYAML yaml_file = (toException . Yaml.decodeEither') =<< BS.readFile yaml_file where -- TODO: make it memory-efficient!
+  toException = either (throwIO) return
+
+packageVersion :: BuildPlan -> PackageName -> Maybe Version
+packageVersion (BuildPlan bp_map) name = HM.lookup name bp_map
+
+-- | Parse a version text. There must not be any trailing characters
+-- after a valid version text.
+parseVersionText :: Text -> Maybe Version
+parseVersionText = extractResult . (readP_to_S parseVersion) . unpack where
+  extractResult = listToMaybe . map fst . filter (\pair -> snd pair == "")
diff --git a/src/Staversion/Internal/Command.hs b/src/Staversion/Internal/Command.hs
new file mode 100644
--- /dev/null
+++ b/src/Staversion/Internal/Command.hs
@@ -0,0 +1,95 @@
+-- |
+-- Module: Staversion.Internal.Command
+-- Description: Command from the user.
+-- Maintainer: Toshio Ito <debug.ito@gmail.com>
+--
+-- __This is an internal module. End-users should not use it.__
+module Staversion.Internal.Command
+       ( Command(..),
+         parseCommandArgs
+       ) where
+
+import Control.Applicative ((<$>), (<*>), optional, some)
+import Data.Monoid (mconcat)
+import Data.Text (pack)
+import Data.Version (showVersion)
+import qualified Options.Applicative as Opt
+import qualified Paths_staversion as MyInfo
+import System.Directory (getHomeDirectory)
+import System.FilePath ((</>))
+
+import Staversion.Internal.Log
+  ( LogLevel(..), Logger(loggerThreshold), defaultLogger
+  )
+import Staversion.Internal.Query
+  ( Resolver,
+    PackageName,
+    Query(..),
+    PackageSource(..)
+  )
+
+-- | Command from the user.
+data Command =
+  Command { commBuildPlanDir :: FilePath,
+            -- ^ path to the directory where build plan files are stored.
+            commLogger :: Logger,
+            -- ^ the logger
+            commSources :: [PackageSource],
+            -- ^ package sources to search
+            commQueries :: [Query]
+            -- ^ package queries
+          } deriving (Show,Eq)
+
+-- | Default values for 'Command'.
+data DefCommand = DefCommand { defBuildPlanDir :: FilePath
+                             } deriving (Show,Eq,Ord)
+
+defCommand :: IO DefCommand
+defCommand = DefCommand <$> def_build_plan_dir where
+  def_build_plan_dir = do
+    home <- getHomeDirectory
+    return $ home </> ".stack" </> "build-plan"
+
+
+commandParser :: DefCommand -> Opt.Parser Command
+commandParser def_comm = Command <$> build_plan_dir <*> logger <*> sources <*> queries where
+  logger = makeLogger <$> is_verbose
+  makeLogger True = defaultLogger { loggerThreshold = Just LogDebug }
+  makeLogger False = defaultLogger
+  is_verbose = Opt.switch $ mconcat [ Opt.long "verbose",
+                                      Opt.short 'v',
+                                      Opt.help "Verbose messages."
+                                    ]
+  build_plan_dir = Opt.strOption
+                   $ mconcat [ Opt.long "build-plan-dir",
+                               Opt.help ( "Directory where build plan YAML files are stored. Default: "
+                                          ++ defBuildPlanDir def_comm
+                                        ),
+                               Opt.metavar "DIR",
+                               Opt.value (defBuildPlanDir def_comm)
+                             ]
+  sources = some $ SourceStackage <$> resolver
+  resolver = Opt.strOption
+             $ mconcat [ Opt.long "resolver",
+                         Opt.short 'r',
+                         Opt.help "Stackage resolver to search. e.g. \"lts-6.15\"",
+                         Opt.metavar "RESOLVER_NAME"
+                       ]
+  queries = some $ QueryName <$> package
+  package = fmap pack $ Opt.strArgument
+            $ mconcat [ Opt.help "Name of package whose version you want to check.",
+                        Opt.metavar "PACKAGE_NAME"
+                      ]
+
+programDescription :: Opt.Parser a -> Opt.ParserInfo a
+programDescription parser =
+  Opt.info (Opt.helper <*> parser)
+  $ mconcat [ Opt.fullDesc,
+              Opt.progDesc ( "Look for version numbers for Haskell packages in specific stackage resolvers"
+                             ++ " (or possibly other package sources)"
+                           ),
+              Opt.footer ("Version: " ++ (showVersion MyInfo.version))
+            ]
+
+parseCommandArgs :: IO Command
+parseCommandArgs = Opt.execParser . programDescription . commandParser =<< defCommand
diff --git a/src/Staversion/Internal/Exec.hs b/src/Staversion/Internal/Exec.hs
new file mode 100644
--- /dev/null
+++ b/src/Staversion/Internal/Exec.hs
@@ -0,0 +1,72 @@
+-- |
+-- Module: Staversion.Internal.Exec
+-- Description: executable
+-- Maintainer: Toshio Ito <debug.ito@gmail.com>
+--
+-- __This is an internal module. End-users should not use it.__
+module Staversion.Internal.Exec
+       ( main,
+         processCommand
+       ) where
+
+import Control.Applicative ((<$>))
+import Control.Exception (catchJust, IOException)
+import Data.Function (on)
+import Data.List (groupBy)
+import Data.Text (unpack)
+import qualified Data.Text.Lazy.IO as TLIO
+import System.FilePath ((</>), (<.>))
+import qualified System.IO.Error as IOE
+
+import Staversion.Internal.BuildPlan
+  ( BuildPlan, loadBuildPlanYAML, packageVersion
+  )
+import Staversion.Internal.Command
+  ( parseCommandArgs,
+    Command(..)
+  )
+import Staversion.Internal.Format (formatResultsCabal)
+import Staversion.Internal.Log (logDebug, logWarn)
+import Staversion.Internal.Query
+  ( Query(..), Result(..), PackageSource(..),
+    resultVersionsFromList, ResultVersions,
+    ErrorMsg
+  )
+
+main :: IO ()
+main = do
+  comm <- parseCommandArgs
+  (TLIO.putStr . formatResultsCabal) =<< (processCommand comm)
+
+processCommand :: Command -> IO [Result]
+processCommand comm = fmap concat $ mapM processQueriesIn $ commSources comm where
+  logger = commLogger comm
+  processQueriesIn source = do
+    logDebug logger ("Retrieve package source " ++ show source)
+    e_build_plan <- loadBuildPlan comm source
+    logBuildPlanResult e_build_plan
+    return $ map (makeResult source e_build_plan) $ commQueries comm
+  makeResult source e_build_plan query = case e_build_plan of
+    Left error_msg -> Result { resultIn = source, resultFor = query, resultVersions = Left error_msg }
+    Right build_plan -> Result { resultIn = source, resultFor = query,
+                                 resultVersions = Right $ searchVersions build_plan query
+                               }
+  logBuildPlanResult (Right _) = logDebug logger ("Successfully retrieved build plan.")
+  logBuildPlanResult (Left error_msg) = logWarn logger ("Failed to load build plan: " ++ error_msg)
+
+loadBuildPlan ::  Command -> PackageSource -> IO (Either ErrorMsg BuildPlan)
+loadBuildPlan comm source@(SourceStackage resolver) = catchJust handleIOError (Right <$> doLoad) (return . Left) where
+  yaml_file = commBuildPlanDir comm </> resolver <.> "yaml"
+  doLoad = do
+    logDebug (commLogger comm) ("Read " ++ yaml_file ++ " for build plan.")
+    loadBuildPlanYAML yaml_file
+  handleIOError :: IOException -> Maybe ErrorMsg
+  handleIOError e | IOE.isDoesNotExistError e = Just $ makeErrorMsg e (yaml_file ++ " not found.")
+                  | IOE.isPermissionError e = Just $ makeErrorMsg e ("you cannot open " ++ yaml_file ++ ".")
+                  | otherwise = Just $ makeErrorMsg e ("some error.")
+  makeErrorMsg exception body = "Loading build plan for package source " ++ show source ++ " failed: " ++ body ++ "\n" ++ show exception
+
+
+searchVersions :: BuildPlan -> Query -> ResultVersions
+searchVersions build_plan (QueryName package_name) =
+  resultVersionsFromList [(package_name, packageVersion build_plan package_name)]
diff --git a/src/Staversion/Internal/Format.hs b/src/Staversion/Internal/Format.hs
new file mode 100644
--- /dev/null
+++ b/src/Staversion/Internal/Format.hs
@@ -0,0 +1,62 @@
+-- |
+-- Module: Staversion.Internal.Format
+-- Description: formatting Result output.
+-- Maintainer: Toshio Ito <debug.ito@gmail.com>
+--
+-- __This is an internal module. End-users should not use it.__
+module Staversion.Internal.Format
+       ( formatResultsCabal
+       ) where
+
+import Data.Function (on)
+import Data.List (intersperse)
+import Data.Monoid (mempty, mconcat, (<>))
+import qualified Data.Text.Lazy as TL
+import Data.Text.Lazy.Builder (Builder, toLazyText, fromText, fromString)
+import Data.Version (showVersion)
+
+import Staversion.Internal.Query
+  ( Result(..), Query(..),
+    sourceDesc,
+    ResultVersions,
+    resultVersionsToList
+  )
+
+-- | format 'Result's like it's in build-depends in .cabal files.
+formatResultsCabal :: [Result] -> TL.Text
+formatResultsCabal = toLazyText . mconcat . map formatGroupedResultsCabal . groupAllPreservingOrderBy ((==) `on` resultIn)
+
+groupAllPreservingOrderBy :: (a -> a -> Bool) -> [a] -> [[a]]
+groupAllPreservingOrderBy sameGroup = map snd  . foldr f [] where
+  f item acc = update [] acc where
+    update heads [] = (item, [item]) : heads
+    update heads (cur@(cur_item, cur_list) : rest) =
+      if sameGroup item cur_item
+      then heads ++ ( (cur_item, item : cur_list) : rest )
+      else update (heads ++ [cur]) rest
+
+formatGroupedResultsCabal :: [Result] -> Builder
+formatGroupedResultsCabal [] = mempty
+formatGroupedResultsCabal results@(head_ret : _) = header <> (concatLines $ single_result_output =<< results) where
+  header = "------ " <> (fromText $ sourceDesc $ resultIn head_ret) <> "\n"
+  single_result_output ret = case resultVersions ret of
+    Left _ -> [Left $ error_result ret]
+    Right versions -> formatVersionsCabal (resultFor ret) versions
+  error_result ret = case resultFor ret of
+    QueryName query_name -> "-- " <> fromText query_name <> " ERROR"
+  concatLines ebuilder_lines = (mconcat $ intersperse "\n" $ map (either id id) $ tailCommas ebuilder_lines) <> "\n\n"
+  tailCommas = fst . foldr f ([], False) where
+    -- flag: True if it has already encountered the last Right element in the list.
+    f eb (ret, flag) = let (next_e, next_flag) = getNext ret flag eb
+                       in (next_e:ret, next_flag)
+    getNext [] flag e@(Left _) = (e, flag)
+    getNext _ flag (Left b) = (Left (b <> ","), flag)
+    getNext _ False e@(Right _) = (e, True)
+    getNext _ True (Right b) = (Right (b <> ","), True)
+
+formatVersionsCabal :: Query -> ResultVersions -> [Either Builder Builder]
+formatVersionsCabal (QueryName _) rvers = map format $ resultVersionsToList rvers where
+  format (name, mver) = case mver of
+    Nothing -> Left $ "-- " <> fromText name <> " N/A"
+    Just ver -> Right $ fromText name <> " ==" <> (fromString $ showVersion ver)
+
diff --git a/src/Staversion/Internal/Log.hs b/src/Staversion/Internal/Log.hs
new file mode 100644
--- /dev/null
+++ b/src/Staversion/Internal/Log.hs
@@ -0,0 +1,57 @@
+-- |
+-- Module: Staversion.Internal.Log
+-- Description: types and functions about logging
+-- Maintainer: Toshio Ito <debug.ito@gmail.com>
+--
+-- 
+module Staversion.Internal.Log
+       ( LogLevel(..),
+         Logger(loggerThreshold),
+         defaultLogger,
+         putLog,
+         logDebug,
+         logInfo,
+         logWarn
+       ) where
+
+import Control.Monad (when)
+import System.IO (Handle, stderr, hPutStrLn)
+
+data LogLevel = LogDebug
+              | LogInfo
+              | LogWarn
+              | LogError
+              deriving (Show,Eq,Ord,Enum,Bounded)
+
+data Logger = Logger { loggerThreshold :: Maybe LogLevel,
+                       -- ^ If 'Nothing', logging is disabled.
+                       loggerHandle :: Handle
+                     } deriving (Show,Eq)
+
+defaultLogger :: Logger
+defaultLogger = Logger { loggerThreshold = Just LogInfo,
+                         loggerHandle = stderr
+                       }
+
+toLabel :: LogLevel -> String
+toLabel l = case l of
+  LogDebug -> "[debug]"
+  LogInfo ->  "[info]"
+  LogWarn ->  "[warn]"
+  LogError -> "[error]"
+
+putLog :: Logger -> LogLevel -> String -> IO ()
+putLog logger level raw_msg = when (fmap (level >=) mthreshold == Just True) $ hPutStrLn log_handle msg where
+  mthreshold = loggerThreshold logger
+  log_handle = loggerHandle logger
+  msg = toLabel level ++ " " ++ raw_msg
+
+logDebug :: Logger -> String -> IO ()
+logDebug = flip putLog $ LogDebug
+
+logInfo :: Logger -> String -> IO ()
+logInfo = flip putLog $ LogInfo
+
+logWarn :: Logger -> String -> IO ()
+logWarn = flip putLog $ LogWarn
+
diff --git a/src/Staversion/Internal/Query.hs b/src/Staversion/Internal/Query.hs
new file mode 100644
--- /dev/null
+++ b/src/Staversion/Internal/Query.hs
@@ -0,0 +1,58 @@
+-- |
+-- Module: Staversion.Internal.Query
+-- Description: Query, Result and related symbols.
+-- Maintainer: Toshio Ito <debug.ito@gmail.com>
+--
+-- __This is an internal module. End-users should not use it.__
+module Staversion.Internal.Query
+       ( PackageName,
+         Resolver,
+         PackageSource(..),
+         Query(..),
+         ErrorMsg,
+         Result(..),
+         ResultVersions,
+         resultVersionsFromList,
+         resultVersionsToList,
+         sourceDesc
+       ) where
+
+import qualified Data.HashMap.Strict as HM
+import Data.Text (Text, pack)
+import Data.Version (Version)
+
+type PackageName = Text
+
+-- | Resolver name at stackage like "lts-4.1".
+type Resolver = String
+
+-- | Source of packages.
+data PackageSource = SourceStackage Resolver -- ^ stackage.
+                   deriving (Show,Eq,Ord)
+
+-- | Query for package version(s).
+data Query = QueryName PackageName
+           deriving (Show,Eq,Ord)
+
+type ErrorMsg = String
+
+-- | Result for a query.
+data Result = Result { resultIn :: PackageSource,
+                       resultFor :: Query,
+                       resultVersions :: Either ErrorMsg ResultVersions
+                     } deriving (Show,Eq)
+
+
+-- | The obtained version map.
+newtype ResultVersions = ResultVersions (HM.HashMap PackageName (Maybe Version))
+                       deriving (Show,Eq)
+
+resultVersionsFromList :: [(PackageName, Maybe Version)] -> ResultVersions
+resultVersionsFromList = ResultVersions . HM.fromList
+
+resultVersionsToList :: ResultVersions -> [(PackageName, Maybe Version)]
+resultVersionsToList (ResultVersions m) = HM.toList m
+
+-- | description of a 'PackageSource'.
+sourceDesc :: PackageSource -> Text
+sourceDesc (SourceStackage r) = pack r
diff --git a/staversion.cabal b/staversion.cabal
new file mode 100644
--- /dev/null
+++ b/staversion.cabal
@@ -0,0 +1,72 @@
+name:                   staversion
+version:                0.1.0.0
+author:                 Toshio Ito <debug.ito@gmail.com>
+maintainer:             Toshio Ito <debug.ito@gmail.com>
+license:                BSD3
+license-file:           LICENSE
+synopsis:               What version is the package X in stackage lts-Y.ZZ?
+description:            A command-line tool to look for version numbers for Haskell packages in specific stackage resolvers. See README.md
+category:               Development
+cabal-version:          >= 1.10
+build-type:             Simple
+extra-source-files:     README.md, ChangeLog.md,
+                        test/data/conpact_build_plan.yaml,
+                        -- Cabal's wild-card (*) doesn't match periods (.)
+                        test/data/lts-7.0_conpact.yaml,
+                        test/data/lts-4.2.yaml,
+                        test/data/lts-2.22_conpact.yaml
+                        
+homepage:               https://github.com/debug-ito/staversion
+bug-reports:            https://github.com/debug-ito/staversion/issues
+
+library
+  default-language:     Haskell2010
+  hs-source-dirs:       src
+  ghc-options:          -Wall -fno-warn-unused-imports
+  default-extensions:   OverloadedStrings
+  -- other-extensions:     
+  exposed-modules:      Staversion.Internal.BuildPlan,
+                        Staversion.Internal.Query,
+                        Staversion.Internal.Command,
+                        Staversion.Internal.Log,
+                        Staversion.Internal.Exec,
+                        Staversion.Internal.Format
+  other-modules:        Paths_staversion
+  build-depends:        base >=4.6 && <4.10,
+                        unordered-containers >=0.2.3 && <0.3,
+                        aeson >=0.8.0 && <1.1,
+                        text >=0.11.3 && <1.3,
+                        bytestring >=0.10.0 && <0.11,
+                        yaml >=0.8.3 && <0.9,
+                        filepath >=1.3.0 && <1.5,
+                        directory >=1.2.0 && <1.3,
+                        optparse-applicative >=0.11.0 && <0.14
+
+executable staversion
+  default-language:     Haskell2010
+  hs-source-dirs:       app
+  main-is:              Main.hs
+  ghc-options:          -Wall -fno-warn-unused-imports -rtsopts -threaded "-with-rtsopts=-N"
+  -- other-modules:        
+  -- default-extensions:   
+  -- other-extensions:     
+  build-depends:        base, staversion
+
+test-suite spec
+  type:                 exitcode-stdio-1.0
+  default-language:     Haskell2010
+  hs-source-dirs:       test
+  ghc-options:          -Wall -fno-warn-unused-imports "-with-rtsopts=-M512m"
+  main-is:              Spec.hs
+  default-extensions:   OverloadedStrings
+  -- other-extensions:     
+  other-modules:        Staversion.Internal.BuildPlanSpec,
+                        Staversion.Internal.ExecSpec,
+                        Staversion.Internal.FormatSpec,
+                        Staversion.Internal.TestUtil
+  build-depends:        base, staversion, text, filepath,
+                        hspec >=2.1.7 && <2.4
+                        
+source-repository head
+  type:                 git
+  location:             https://github.com/debug-ito/staversion.git
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/Staversion/Internal/BuildPlanSpec.hs b/test/Staversion/Internal/BuildPlanSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Staversion/Internal/BuildPlanSpec.hs
@@ -0,0 +1,58 @@
+module Staversion.Internal.BuildPlanSpec (main,spec) where
+
+import Data.Text (Text, pack)
+import Data.Version (Version(..))
+import System.FilePath ((</>), (<.>))
+import Test.Hspec
+
+import Staversion.Internal.BuildPlan
+  ( PackageName,
+    BuildPlan, 
+    loadBuildPlanYAML, 
+    packageVersion,
+    parseVersionText
+  )
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+  parseVersionText_spec
+  packageVersion_spec
+
+parseVersionText_spec :: Spec
+parseVersionText_spec = describe "parseVersionText" $ do
+  spec_v "0.1.0.6" $ Just (Version [0,1,0,6] [])
+  spec_v "10.11" $ Just (Version [10,11] [])
+  where
+    spec_v input_v expected = specify input_v $ do
+      parseVersionText (pack input_v) `shouldBe` expected
+
+packageVersion_spec :: Spec
+packageVersion_spec = describe "packageVersion" $ do
+  forBuildPlan "conpact_build_plan" $ \loader -> do
+    specify "drawille -> 0.1.0.6" $ do
+      loadVersion "drawille" loader `shouldReturn` Just (Version [0,1,0,6] [])
+    specify "unknown -> Nothing" $ do
+      loadVersion "unknown" loader `shouldReturn` Nothing
+    specify "ghc -> 7.10.3" $ do
+      loadVersion "ghc" loader `shouldReturn` Just (Version [7,10,3] [])
+    specify "base -> 4.8.2.0" $ do
+      loadVersion "base" loader `shouldReturn` Just (Version [4,8,2,0] [])
+
+  forBuildPlan "lts-4.2" $ \loader -> do
+    specify "conduit -> 1.2.6.1" $ do
+      loadVersion "conduit" loader `shouldReturn` Just (Version [1,2,6,1] [])
+    specify "transformers -> 0.4.2.0" $ do
+      loadVersion "transformers" loader `shouldReturn` Just (Version [0,4,2,0] [])
+
+
+forBuildPlan :: String -> (IO BuildPlan -> Spec) -> Spec
+forBuildPlan build_plan_base testWith = describe build_plan_base (testWith loader) where
+  loader = loadBuildPlanYAML ("test" </> "data" </> build_plan_base <.> "yaml")
+
+loadVersion :: PackageName -> IO BuildPlan -> IO (Maybe Version)
+loadVersion package_name loader = do
+  plan <- loader
+  return $ packageVersion plan package_name
diff --git a/test/Staversion/Internal/ExecSpec.hs b/test/Staversion/Internal/ExecSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Staversion/Internal/ExecSpec.hs
@@ -0,0 +1,81 @@
+module Staversion.Internal.ExecSpec (main,spec) where
+
+import Data.Version (Version(Version))
+import System.FilePath ((</>))
+import Test.Hspec
+
+import Staversion.Internal.Command (Command(..))
+import Staversion.Internal.Exec (processCommand)
+import Staversion.Internal.Query
+  ( PackageName,
+    Query(..),
+    PackageSource(..),
+    Result(..),
+    ResultVersions,
+    resultVersionsFromList,
+    ErrorMsg
+  )
+import Staversion.Internal.Log (defaultLogger, Logger(loggerThreshold), LogLevel(LogError))
+
+import Staversion.Internal.TestUtil (ver, rvers)
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = describe "processCommand" $ do
+  specify "QueryName, SourceStackage, hit" $ do
+    singleCase (SourceStackage "conpact_build_plan") (QueryName "drawille")
+      (Right $ rvers [("drawille", Just $ ver [0,1,0,6])])
+  specify "QueryName, SourceStackage, miss" $ do
+    singleCase (SourceStackage "conpact_build_plan") (QueryName "unknown")
+      (Right $ rvers [("unknown", Nothing)])
+  specify "QueryName, SourceStackage, source not found" $ do
+    singleCase' (SourceStackage "unknown") (QueryName "drawille") $ \got -> case got of
+      Right _ -> expectationFailure "it should fail"
+      Left msg -> do
+        msg `shouldContain` "unknown.yaml"
+        msg `shouldContain` "not found"
+  specify "QueryName, SourceStackage, full-mesh" $ do
+    let src2 = SourceStackage "lts-2.22_conpact"
+        src7 = SourceStackage "lts-7.0_conpact"
+        qc = QueryName "conduit"
+        qa = QueryName "aeson"
+        comm = baseCommand { commSources = [src2, src7], commQueries = [qc, qa] }
+        expected = [ Result { resultIn = src2, resultFor = qc,
+                              resultVersions = Right $ rvers [("conduit", Just $ ver [1,2,5])]
+                            },
+                     Result { resultIn = src2, resultFor = qa,
+                              resultVersions = Right $ rvers [("aeson", Just $ ver [0,8,0,2])]
+                            },
+                     Result { resultIn = src7, resultFor = qc,
+                              resultVersions = Right $ rvers [("conduit", Just $ ver [1,2,7])]
+                            },
+                     Result { resultIn = src7, resultFor = qa,
+                              resultVersions = Right $ rvers [("aeson", Just $ ver [0,11,2,1])]
+                            }
+                   ]
+    got <- processCommand comm
+    got `shouldBe` expected
+        
+
+singleCase :: PackageSource -> Query -> Either ErrorMsg ResultVersions -> IO ()
+singleCase src query exp_ret_vers = singleCase' src query (`shouldBe` exp_ret_vers)
+
+singleCase' :: PackageSource -> Query -> (Either ErrorMsg ResultVersions -> IO a) -> IO a
+singleCase' src query checker = do
+  [got_ret] <- processCommand comm
+  resultIn got_ret `shouldBe` src
+  resultFor got_ret `shouldBe` query
+  checker $ resultVersions got_ret
+  where
+    comm =  baseCommand { commSources = [src],
+                          commQueries = [query]
+                        }
+
+baseCommand :: Command
+baseCommand = Command { commBuildPlanDir = "test" </> "data",
+                        commLogger = defaultLogger { loggerThreshold = Nothing },
+                        commSources = [],
+                        commQueries = []
+                      }
diff --git a/test/Staversion/Internal/FormatSpec.hs b/test/Staversion/Internal/FormatSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Staversion/Internal/FormatSpec.hs
@@ -0,0 +1,84 @@
+module Staversion.Internal.FormatSpec (main,spec) where
+
+import Data.Monoid ((<>))
+import Test.Hspec
+
+import Staversion.Internal.Format (formatResultsCabal)
+import Staversion.Internal.Query
+  ( Result(..), PackageSource(..), Query(..),
+    Resolver, PackageName
+  )
+
+import Staversion.Internal.TestUtil (ver, rvers)
+       
+
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = describe "formatResultsCabal" $ do
+  it "should return empty text for empty list" $ do
+    formatResultsCabal [] `shouldBe` ""
+  it "should format a Result in a Cabal way" $ do
+    let input = [ Result { resultIn = SourceStackage "lts-6.10",
+                           resultFor = QueryName "hoge",
+                           resultVersions = Right $ rvers [("hoge", Just $ ver [3,4,5])]
+                         }
+                ]
+        expected = ( "------ lts-6.10\n"
+                     <> "hoge ==3.4.5\n"
+                     <> "\n"
+                   )
+    formatResultsCabal input `shouldBe` expected
+  
+  it "should group Results by PackageSource with preserved order" $ do
+    let input = [ simpleResult "source_c" "hoge" [0,5,7],
+                  simpleResult "source_a" "hoge" [1,0,0],
+                  simpleResult "source_b" "foobar" [0,2,5,3],
+                  simpleResult "source_b" "hoge" [1,2,0],
+                  simpleResult "source_a" "quux" [300,5],
+                  simpleResult "source_a" "foobar" [0,3,2],
+                  simpleResult "source_b" "quux" [299,10]
+                ]
+        expected = ( "------ source_c\n"
+                     <> "hoge ==0.5.7\n"
+                     <> "\n"
+                     <> "------ source_a\n"
+                     <> "hoge ==1.0.0,\n"
+                     <> "quux ==300.5,\n"
+                     <> "foobar ==0.3.2\n"
+                     <> "\n"
+                     <> "------ source_b\n"
+                     <> "foobar ==0.2.5.3,\n"
+                     <> "hoge ==1.2.0,\n"
+                     <> "quux ==299.10\n"
+                     <> "\n"
+                   )
+    formatResultsCabal input `shouldBe` expected
+  it "should not put comma at the last non-N/A entry even if it is followed by N/A entries"  $ do
+    let input = [ simpleResult "s" "hoge" [1,0,0],
+                  simpleResult "s" "not-found-1" [],
+                  simpleResult "s" "foobar" [2,0,0],
+                  simpleResult "s" "not-found-2" [],
+                  simpleResult "s" "not-found-3" []
+                ]
+        expected = ( "------ s\n"
+                     <> "hoge ==1.0.0,\n"
+                     <> "-- not-found-1 N/A,\n"
+                     <> "foobar ==2.0.0\n"
+                     <> "-- not-found-2 N/A,\n"
+                     <> "-- not-found-3 N/A\n"
+                     <> "\n"
+                   )
+    formatResultsCabal input `shouldBe` expected
+
+simpleResult :: Resolver -> PackageName -> [Int] -> Result
+simpleResult res name vs = Result { resultIn = SourceStackage res,
+                                    resultFor = QueryName name,
+                                    resultVersions = Right $ rvers [(name, mversion)]
+                                  }
+  where
+    mversion = case vs of
+      [] -> Nothing
+      _ -> Just $ ver vs
diff --git a/test/Staversion/Internal/TestUtil.hs b/test/Staversion/Internal/TestUtil.hs
new file mode 100644
--- /dev/null
+++ b/test/Staversion/Internal/TestUtil.hs
@@ -0,0 +1,13 @@
+module Staversion.Internal.TestUtil
+       ( ver, rvers
+       ) where
+
+import Data.Version (Version(..))
+import Staversion.Internal.Query (PackageName, ResultVersions, resultVersionsFromList)
+
+ver :: [Int] -> Version
+ver vs = Version vs []
+
+rvers :: [(PackageName, Maybe Version)] -> ResultVersions
+rvers = resultVersionsFromList
+
diff --git a/test/data/conpact_build_plan.yaml b/test/data/conpact_build_plan.yaml
new file mode 100644
--- /dev/null
+++ b/test/data/conpact_build_plan.yaml
@@ -0,0 +1,97 @@
+github-users:
+  yesodweb:
+  - snoyberg
+system-info:
+  core-packages:
+    ghc: '7.10.3'
+    hoopl: '3.10.0.2'
+    bytestring: '0.10.6.0'
+    unix: '2.7.1.0'
+    base: '4.8.2.0'
+    time: '1.5.0.1'
+    hpc: '0.6.0.2'
+    filepath: '1.4.0.0'
+    process: '1.2.3.0'
+    array: '0.5.1.0'
+    integer-gmp: '1.0.0.0'
+    bin-package-db: '0.0.0.0'
+    containers: '0.5.6.2'
+    binary: '0.7.5.0'
+    ghc-prim: '0.4.0.0'
+    transformers: '0.4.2.0'
+    deepseq: '1.4.1.1'
+    pretty: '1.1.2.0'
+    template-haskell: '2.10.0.0'
+    directory: '1.2.2.0'
+  ghc-version: '7.10.3'
+  os: linux
+  core-executables:
+  - ghc
+  - ghc-7.10.3
+  - ghc-pkg
+  - ghc-pkg-7.10.3
+  - ghci
+  - ghci-7.10.3
+  - haddock
+  - haddock-ghc-7.10.3
+  - hp2ps
+  - hpc
+  - hsc2hs
+  - runghc
+  - runghc-7.10.3
+  - runhaskell
+  arch: x86_64
+packages:
+  drawille:
+    users: []
+    constraints:
+      library-profiling: true
+      flags: {}
+      build-benchmarks: true
+      skip-build: false
+      tests: expect-success
+      haddocks: expect-success
+      maintainer: Pedro Tacla Yamada <tacla.yamada@gmail.com> @yamadapc
+      version-range: ! '>=0.1.0.6 && <0.2'
+    version: '0.1.0.6'
+    github-pings:
+    - yamadapc
+    description:
+      modules:
+      - System.Drawille
+      provided-exes:
+      - image2term
+      - senoid
+      packages:
+        base:
+          components:
+          - library
+          - test-suite
+          range: ==4.*
+        hspec:
+          components:
+          - test-suite
+          range: ! '>=1.11 && <2.3'
+        containers:
+          components:
+          - library
+          - test-suite
+          range: ==0.5.*
+        QuickCheck:
+          components:
+          - test-suite
+          range: ! '>=2.6 && <3'
+      tools: {}
+tools:
+- name: alex
+  version: '3.1.7'
+- name: c2hs
+  version: '0.27.1'
+- name: cpphs
+  version: '1.19.3'
+- name: happy
+  version: '1.19.5'
+- name: hsx2hs
+  version: '0.13.4'
+- name: gtk2hs-buildtools
+  version: '0.13.0.5'
diff --git a/test/data/lts-2.22_conpact.yaml b/test/data/lts-2.22_conpact.yaml
new file mode 100644
--- /dev/null
+++ b/test/data/lts-2.22_conpact.yaml
@@ -0,0 +1,497 @@
+github-users:
+  yesodweb:
+  - snoyberg
+  silkapp:
+  - bergmark
+  - hesselink
+  elm-lang:
+  - JoeyEremondi
+  snapframework:
+  - mightybyte
+  haskell-ro:
+  - mihaimaruseac
+  faylang:
+  - bergmark
+  fpco:
+  - snoyberg
+  diagrams:
+  - bergey
+  - byorgey
+  - fryguybob
+  - jeffreyrosenbluth
+system-info:
+  core-packages:
+    ghc: '7.8.4'
+    hoopl: '3.10.0.1'
+    bytestring: '0.10.4.0'
+    unix: '2.7.0.1'
+    haskeline: '0.7.1.2'
+    Cabal: '1.18.1.5'
+    base: '4.7.0.2'
+    time: '1.4.2'
+    xhtml: '3000.2.1'
+    haskell98: '2.0.0.3'
+    hpc: '0.6.0.1'
+    filepath: '1.3.0.2'
+    process: '1.2.0.0'
+    array: '0.5.0.0'
+    integer-gmp: '0.5.1.0'
+    bin-package-db: '0.0.0.0'
+    containers: '0.5.5.1'
+    haskell2010: '1.1.2.0'
+    binary: '0.7.1.0'
+    ghc-prim: '0.3.1.0'
+    old-time: '1.1.0.2'
+    old-locale: '1.0.0.6'
+    rts: '1.0'
+    terminfo: '0.4.0.0'
+    transformers: '0.3.0.0'
+    deepseq: '1.3.0.2'
+    pretty: '1.1.1.1'
+    template-haskell: '2.9.0.0'
+    directory: '1.2.1.0'
+  ghc-version: '7.8.4'
+  os: linux
+  core-executables:
+  - ghc
+  - ghc-7.8.4
+  - ghc-pkg
+  - ghc-pkg-7.8.4
+  - ghci
+  - ghci-7.8.4
+  - haddock
+  - haddock-ghc-7.8.4
+  - hp2ps
+  - hpc
+  - hsc2hs
+  - runghc
+  - runghc-7.8.4
+  - runhaskell
+  arch: x86_64
+packages:
+  conduit:
+    users:
+    - BlastHTTP
+    - MFlow
+    - MusicBrainz
+    - amazonka
+    - amazonka-core
+    - authenticate
+    - aws
+    - binary-conduit
+    - bloodhound
+    - bzlib-conduit
+    - cabal-src
+    - cereal-conduit
+    - classy-prelude-conduit
+    - conduit
+    - conduit-combinators
+    - conduit-extra
+    - cryptohash-conduit
+    - csv-conduit
+    - esqueleto
+    - fb
+    - gitlib
+    - gitlib-libgit2
+    - gitlib-test
+    - hOpenPGP
+    - hackage-mirror
+    - hoogle
+    - hopenpgp-tools
+    - html-conduit
+    - http-conduit
+    - http-reverse-proxy
+    - imagesize-conduit
+    - keter
+    - lzma-conduit
+    - markdown
+    - mime-mail-ses
+    - monad-logger
+    - network-conduit
+    - network-conduit-tls
+    - osdkeys
+    - pagerduty
+    - persistent
+    - persistent-mongoDB
+    - persistent-mysql
+    - persistent-postgresql
+    - persistent-sqlite
+    - project-template
+    - shell-conduit
+    - simple-sendfile
+    - soap
+    - stackage-cli
+    - stackage-curator
+    - stm-conduit
+    - tagstream-conduit
+    - wai-conduit
+    - wai-middleware-consul
+    - xml-conduit
+    - yackage
+    - yaml
+    - yesod-auth
+    - yesod-auth-fb
+    - yesod-bin
+    - yesod-core
+    - yesod-eventsource
+    - yesod-fb
+    - yesod-persistent
+    - yesod-sitemap
+    - yesod-static
+    - yesod-websockets
+    constraints:
+      library-profiling: true
+      flags: {}
+      build-benchmarks: true
+      skip-build: false
+      tests: expect-success
+      haddocks: expect-success
+      version-range: ! '>=1.2.5 && <1.3'
+    version: '1.2.5'
+    github-pings:
+    - snoyberg
+    description:
+      modules:
+      - Data.Conduit
+      - Data.Conduit.Internal
+      - Data.Conduit.Internal.Fusion
+      - Data.Conduit.Internal.List.Stream
+      - Data.Conduit.Lift
+      - Data.Conduit.List
+      provided-exes: []
+      packages:
+        exceptions:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.6'
+        void:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.5.5'
+        mwc-random:
+          components:
+          - benchmark
+          range: -any
+        base:
+          components:
+          - library
+          - test-suite
+          - benchmark
+          range: ! '>=4.3 && <5'
+        hspec:
+          components:
+          - test-suite
+          - benchmark
+          range: ! '>=1.3'
+        kan-extensions:
+          components:
+          - benchmark
+          range: -any
+        criterion:
+          components:
+          - benchmark
+          range: -any
+        lifted-base:
+          components:
+          - library
+          range: ! '>=0.1'
+        conduit:
+          components:
+          - test-suite
+          - benchmark
+          range: -any
+        containers:
+          components:
+          - test-suite
+          - benchmark
+          range: -any
+        mtl:
+          components:
+          - library
+          - test-suite
+          range: -any
+        mmorph:
+          components:
+          - library
+          range: -any
+        transformers-base:
+          components:
+          - library
+          range: ! '>=0.4.1 && <0.5'
+        transformers:
+          components:
+          - library
+          - test-suite
+          - benchmark
+          range: ! '>=0.2.2 && <0.5'
+        deepseq:
+          components:
+          - benchmark
+          range: -any
+        QuickCheck:
+          components:
+          - test-suite
+          range: ! '>=2.5'
+        resourcet:
+          components:
+          - library
+          - test-suite
+          range: ==1.1.*
+        safe:
+          components:
+          - test-suite
+          range: -any
+        vector:
+          components:
+          - benchmark
+          range: -any
+      tools: {}
+  aeson:
+    users:
+    - HTF
+    - MusicBrainz
+    - Spock
+    - aeson
+    - aeson-pretty
+    - aeson-qq
+    - aeson-utils
+    - amazonka-core
+    - arbtt
+    - authenticate
+    - aws
+    - bake
+    - blank-canvas
+    - bloodhound
+    - classy-prelude-yesod
+    - consul-haskell
+    - criterion
+    - descriptive
+    - ede
+    - fay
+    - fb
+    - generic-aeson
+    - gipeda
+    - gitson
+    - groundhog
+    - groundhog-th
+    - hOpenPGP
+    - haskell-names
+    - haskell-packages
+    - hasql-postgres
+    - hdocs
+    - heist
+    - hoauth2
+    - holy-project
+    - hoogle
+    - hopenpgp-tools
+    - hpc-coveralls
+    - hsdev
+    - hspec-wai-json
+    - ide-backend
+    - ide-backend-common
+    - jmacro
+    - jmacro-rpc
+    - jmacro-rpc-happstack
+    - jmacro-rpc-snap
+    - jose-jwt
+    - json-autotype
+    - json-schema
+    - kansas-comet
+    - keter
+    - koofr-client
+    - lens-aeson
+    - mandrill
+    - monad-logger-json
+    - pagerduty
+    - pandoc
+    - pandoc-citeproc
+    - pandoc-types
+    - persistent
+    - persistent-mongoDB
+    - persistent-mysql
+    - persistent-postgresql
+    - persistent-sqlite
+    - persistent-template
+    - pipes-aeson
+    - postgresql-simple
+    - present
+    - quandl-api
+    - rest-core
+    - rest-gen
+    - rest-stringmap
+    - rest-types
+    - rethinkdb-client-driver
+    - scotty
+    - servant-client
+    - servant-docs
+    - servant-jquery
+    - servant-server
+    - shakespeare
+    - snap
+    - snaplet-fay
+    - sourcemap
+    - stackage-build-plan
+    - stackage-curator
+    - stackage-install
+    - stackage-types
+    - stackage-upload
+    - statistics
+    - stylish-haskell
+    - thyme
+    - tttool
+    - users
+    - users-postgresql-simple
+    - users-test
+    - waitra
+    - webdriver
+    - wreq
+    - xml-to-json
+    - yaml
+    - yesod
+    - yesod-auth
+    - yesod-auth-deskcom
+    - yesod-auth-fb
+    - yesod-auth-oauth2
+    - yesod-core
+    - yesod-fay
+    - yesod-fb
+    - yesod-form
+    - yesod-text-markdown
+    constraints:
+      library-profiling: true
+      flags:
+        old-locale: true
+      build-benchmarks: true
+      skip-build: false
+      tests: expect-success
+      haddocks: expect-success
+      version-range: ! '>=0.8.0.2 && <0.8.1'
+    version: '0.8.0.2'
+    github-pings:
+    - bos
+    description:
+      modules:
+      - Data.Aeson
+      - Data.Aeson.Encode
+      - Data.Aeson.Generic
+      - Data.Aeson.Parser
+      - Data.Aeson.TH
+      - Data.Aeson.Types
+      provided-exes: []
+      packages:
+        test-framework-hunit:
+          components:
+          - test-suite
+          range: -any
+        bytestring:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.10.4.0'
+        test-framework:
+          components:
+          - test-suite
+          range: -any
+        base:
+          components:
+          - library
+          - test-suite
+          range: ==4.*
+        time:
+          components:
+          - library
+          - test-suite
+          range: <1.5
+        unordered-containers:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.2.3.0'
+        text:
+          components:
+          - library
+          - test-suite
+          range: ! '>=1.1.1.0'
+        syb:
+          components:
+          - library
+          range: -any
+        test-framework-quickcheck2:
+          components:
+          - test-suite
+          range: -any
+        dlist:
+          components:
+          - library
+          range: ! '>=0.2'
+        HUnit:
+          components:
+          - test-suite
+          range: -any
+        containers:
+          components:
+          - library
+          - test-suite
+          range: -any
+        ghc-prim:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.2'
+        old-locale:
+          components:
+          - library
+          range: -any
+        mtl:
+          components:
+          - library
+          range: -any
+        hashable:
+          components:
+          - library
+          range: ! '>=1.1.2.0'
+        attoparsec:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.11.3.4'
+        deepseq:
+          components:
+          - library
+          range: -any
+        scientific:
+          components:
+          - library
+          range: ! '>=0.3.1 && <0.4'
+        QuickCheck:
+          components:
+          - test-suite
+          range: -any
+        aeson:
+          components:
+          - test-suite
+          range: -any
+        template-haskell:
+          components:
+          - library
+          - test-suite
+          range: ! '>=2.4'
+        vector:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.7.1'
+      tools: {}
+tools:
+- name: alex
+  version: '3.1.4'
+- name: c2hs
+  version: '0.25.2'
+- name: cpphs
+  version: '1.19.2'
+- name: happy
+  version: '1.19.5'
+- name: gtk2hs-buildtools
+  version: '0.13.0.4'
diff --git a/test/data/lts-4.2.yaml b/test/data/lts-4.2.yaml
new file mode 100644
# file too large to diff: test/data/lts-4.2.yaml
diff --git a/test/data/lts-7.0_conpact.yaml b/test/data/lts-7.0_conpact.yaml
new file mode 100644
--- /dev/null
+++ b/test/data/lts-7.0_conpact.yaml
@@ -0,0 +1,676 @@
+all-cabal-hashes-commit: 5d00e91223715777b5551a98a2a0863e72c7fc0f
+github-users:
+  yesodweb:
+  - snoyberg
+  telegram-api:
+  - klappvisor
+  stackbuilders:
+  - jpvillaisaza
+  - jsantos17
+  - jsl
+  - mrkkrp
+  - sestrella
+  silkapp:
+  - bergmark
+  - hesselink
+  Happstack:
+  - stepcut
+system-info:
+  core-packages:
+    ghc: '8.0.1'
+    hoopl: '3.10.2.1'
+    bytestring: '0.10.8.1'
+    unix: '2.7.2.0'
+    base: '4.9.0.0'
+    time: '1.6.0.1'
+    hpc: '0.6.0.3'
+    filepath: '1.4.1.0'
+    process: '1.4.2.0'
+    array: '0.5.1.1'
+    integer-gmp: '1.0.0.1'
+    containers: '0.5.7.1'
+    ghc-boot: '8.0.1'
+    binary: '0.8.3.0'
+    ghc-prim: '0.5.0.0'
+    ghci: '8.0.1'
+    rts: '1.0'
+    transformers: '0.5.2.0'
+    deepseq: '1.4.2.0'
+    ghc-boot-th: '8.0.1'
+    pretty: '1.1.3.3'
+    template-haskell: '2.11.0.0'
+    directory: '1.2.6.2'
+  ghc-version: '8.0.1'
+  os: linux
+  core-executables:
+  - ghc
+  - ghc-8.0.1
+  - ghc-pkg
+  - ghc-pkg-8.0.1
+  - ghci
+  - ghci-8.0.1
+  - haddock
+  - haddock-ghc-8.0.1
+  - hp2ps
+  - hpc
+  - hsc2hs
+  - runghc
+  - runghc-8.0.1
+  - runhaskell
+  arch: x86_64
+packages:
+  conduit:
+    users:
+    - BlastHTTP
+    - HTTP
+    - MFlow
+    - MusicBrainz
+    - amazonka
+    - amazonka-core
+    - amazonka-test
+    - atndapi
+    - atom-conduit
+    - authenticate
+    - aws
+    - b9
+    - binary-conduit
+    - bitcoin-api-extra
+    - bzlib-conduit
+    - cabal-src
+    - cassava-conduit
+    - cereal-conduit
+    - classy-prelude-conduit
+    - conduit
+    - conduit-combinators
+    - conduit-extra
+    - conduit-iconv
+    - conduit-parse
+    - cryptohash-conduit
+    - cryptonite-conduit
+    - dns
+    - editor-open
+    - fb
+    - fold-debounce-conduit
+    - fsnotify-conduit
+    - gitlib
+    - gitlib-libgit2
+    - gitlib-test
+    - gogol
+    - gogol-core
+    - hOpenPGP
+    - hackage-mirror
+    - haskell-spacegoo
+    - haskoin-core
+    - hoogle
+    - hopenpgp-tools
+    - html-conduit
+    - http-conduit
+    - http-reverse-proxy
+    - hw-conduit
+    - hw-rankselect
+    - hw-succinct
+    - imagesize-conduit
+    - imm
+    - irc-client
+    - irc-conduit
+    - keter
+    - machines
+    - markdown
+    - mime-mail-ses
+    - monad-logger
+    - ndjson-conduit
+    - network-conduit-tls
+    - opml-conduit
+    - osdkeys
+    - pager
+    - pagerduty
+    - persistent
+    - persistent-postgresql
+    - persistent-sqlite
+    - project-template
+    - rss-conduit
+    - sandi
+    - serf
+    - shell-conduit
+    - simple-sendfile
+    - soap
+    - stack
+    - stackage-curator
+    - stm-conduit
+    - store
+    - tagstream-conduit
+    - thumbnail-plus
+    - twitter-conduit
+    - wai-conduit
+    - wai-middleware-consul
+    - withdependencies
+    - xlsior
+    - xlsx
+    - xml-conduit
+    - xml-conduit-parse
+    - yackage
+    - yaml
+    - yesod-auth
+    - yesod-bin
+    - yesod-core
+    - yesod-eventsource
+    - yesod-fb
+    - yesod-persistent
+    - yesod-sitemap
+    - yesod-static
+    - yesod-websockets
+    - zip
+    constraints:
+      benches: expect-success
+      library-profiling: true
+      flags: {}
+      build-benchmarks: true
+      skip-build: false
+      tests: expect-success
+      haddocks: expect-success
+      configure-args: []
+      version-range: -any
+    version: '1.2.7'
+    cabal-file-info:
+      size: 4041
+      hashes:
+        MD5: 3cac157771a3fe620548760f44a27285
+        Skein512_512: 70ecee198d5e0281de4163b8ec8f9e06231dd82d1fe8163a0632ea91ed7c0e35313f622cfc4cb250b05fd893d89901b67a7ec4e4fb3c019b6f25a7d23ecc3018
+        SHA1: 147aae20b2b1a425833ddf7825580475105b86eb
+        SHA512: 888471c06ba9db0a84d7212af48461f33277fa61668391a61cd7c2e8cda95d768c10b7cf8c9f1570249859a05e4eb8c6ab9125ecda37cb3963f63651844fd530
+        SHA256: f5e83ea27ee3cc4a6c190336143d38a468c228f53342a49c63d1fac18e4079c7
+        GitSHA1: 7750ddac3f1edf2499d37dbbcbcfac37db6a89db
+    github-pings:
+    - snoyberg
+    description:
+      cabal-version: '1.8'
+      modules:
+      - Data.Conduit
+      - Data.Conduit.Internal
+      - Data.Conduit.Internal.Fusion
+      - Data.Conduit.Internal.List.Stream
+      - Data.Conduit.Lift
+      - Data.Conduit.List
+      provided-exes: []
+      packages:
+        exceptions:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.6'
+        mwc-random:
+          components:
+          - benchmark
+          range: -any
+        base:
+          components:
+          - library
+          - test-suite
+          - benchmark
+          range: ! '>=4.5 && <5'
+        hspec:
+          components:
+          - test-suite
+          - benchmark
+          range: ! '>=1.3'
+        kan-extensions:
+          components:
+          - benchmark
+          range: -any
+        criterion:
+          components:
+          - benchmark
+          range: -any
+        lifted-base:
+          components:
+          - library
+          range: ! '>=0.1'
+        conduit:
+          components:
+          - test-suite
+          - benchmark
+          range: -any
+        containers:
+          components:
+          - test-suite
+          - benchmark
+          range: -any
+        mtl:
+          components:
+          - library
+          - test-suite
+          range: -any
+        mmorph:
+          components:
+          - library
+          range: -any
+        transformers-base:
+          components:
+          - library
+          range: ! '>=0.4.1 && <0.5'
+        transformers:
+          components:
+          - library
+          - test-suite
+          - benchmark
+          range: ! '>=0.2.2'
+        deepseq:
+          components:
+          - benchmark
+          range: -any
+        QuickCheck:
+          components:
+          - test-suite
+          range: ! '>=2.7'
+        resourcet:
+          components:
+          - library
+          - test-suite
+          range: ==1.1.*
+        safe:
+          components:
+          - test-suite
+          range: -any
+        vector:
+          components:
+          - benchmark
+          range: -any
+      tools: {}
+  aeson:
+    users:
+    - HTF
+    - MusicBrainz
+    - Spock-api
+    - Spock-core
+    - aeson
+    - aeson-better-errors
+    - aeson-casing
+    - aeson-compat
+    - aeson-injector
+    - aeson-pretty
+    - aeson-qq
+    - aeson-utils
+    - amazonka-core
+    - amazonka-test
+    - api-field-json-th
+    - arbtt
+    - atndapi
+    - authenticate
+    - aws
+    - b9
+    - bake
+    - base32string
+    - base58string
+    - bimap-server
+    - binary-orphans
+    - binary-tagged
+    - bitcoin-api
+    - bitcoin-payment-channel
+    - bitx-bitcoin
+    - blank-canvas
+    - bloodhound
+    - bower-json
+    - cabal2nix
+    - cacophony
+    - cayley-client
+    - cheapskate
+    - clash-lib
+    - classy-prelude-yesod
+    - clckwrks
+    - clckwrks-plugin-page
+    - configuration-tools
+    - consul-haskell
+    - criterion
+    - descriptive
+    - distribution-nixpkgs
+    - docopt
+    - doctest-discover
+    - ede
+    - ekg
+    - ekg-json
+    - elm-bridge
+    - emailaddress
+    - envelope
+    - etcd
+    - eventstore
+    - fast-builder
+    - fay
+    - fb
+    - find-clumpiness
+    - forecast-io
+    - generic-aeson
+    - gipeda
+    - giphy-api
+    - github-types
+    - github-webhook-handler
+    - gitson
+    - glabrous
+    - gogol
+    - gogol-core
+    - google-cloud
+    - graylog
+    - hOpenPGP
+    - hailgun
+    - handwriting
+    - happstack-authenticate
+    - haskell-names
+    - haskell-neo4j-client
+    - haskell-packages
+    - haskell-spacegoo
+    - haskoin-core
+    - hasql
+    - hexstring
+    - hjsonpointer
+    - hjsonschema
+    - hoauth2
+    - holy-project
+    - hoogle
+    - hopenpgp-tools
+    - hpack
+    - hpack-convert
+    - hpc-coveralls
+    - hruby
+    - hsebaysdk
+    - hspec-expectations-pretty-diff
+    - hspec-golden-aeson
+    - hspec-wai-json
+    - hspec-webdriver
+    - htoml
+    - http-conduit
+    - http-streams
+    - http2
+    - hworker
+    - hworker-ses
+    - ical
+    - idris
+    - imm
+    - inline-r
+    - insert-ordered-containers
+    - jmacro
+    - jmacro-rpc
+    - jmacro-rpc-happstack
+    - jose
+    - jose-jwt
+    - json-autotype
+    - json-rpc-generic
+    - json-schema
+    - jwt
+    - kansas-comet
+    - kawhi
+    - keter
+    - koofr-client
+    - kraken
+    - language-puppet
+    - lens-aeson
+    - mandrill
+    - microformats2-parser
+    - microlens-aeson
+    - moesocks
+    - monad-logger-json
+    - mustache
+    - ndjson-conduit
+    - octane
+    - omnifmt
+    - opaleye
+    - opensource
+    - pagerduty
+    - pandoc
+    - pandoc-citeproc
+    - pandoc-types
+    - path
+    - persistent
+    - persistent-postgresql
+    - persistent-redis
+    - persistent-sqlite
+    - persistent-template
+    - pinboard
+    - pipes-aeson
+    - postgresql-binary
+    - postgresql-query
+    - postgresql-simple
+    - profiteur
+    - purescript
+    - quantum-random
+    - ratel
+    - rest-core
+    - rest-gen
+    - rest-stringmap
+    - rest-types
+    - rethinkdb
+    - rethinkdb-client-driver
+    - scotty
+    - servant
+    - servant-aeson-specs
+    - servant-client
+    - servant-docs
+    - servant-js
+    - servant-mock
+    - servant-purescript
+    - servant-server
+    - servant-subscriber
+    - servant-swagger
+    - servant-swagger-ui
+    - servant-yaml
+    - serversession
+    - shakespeare
+    - simple
+    - simple-templates
+    - slug
+    - smoothie
+    - smsaero
+    - solga
+    - sourcemap
+    - stache
+    - stack
+    - stackage-curator
+    - stackage-types
+    - statistics
+    - stratosphere
+    - strict-base-types
+    - stripe-core
+    - strive
+    - stylish-haskell
+    - swagger
+    - swagger2
+    - text-region
+    - these
+    - thyme
+    - tttool
+    - twitter-conduit
+    - twitter-feed
+    - twitter-types
+    - ua-parser
+    - userid
+    - users
+    - wai-extra
+    - wai-middleware-content-type
+    - waitra
+    - webdriver
+    - webdriver-angular
+    - werewolf
+    - werewolf-slack
+    - wreq
+    - xlsx-tabular
+    - yahoo-finance-api
+    - yaml
+    - yesod
+    - yesod-auth
+    - yesod-auth-oauth2
+    - yesod-core
+    - yesod-fay
+    - yesod-fb
+    - yesod-form
+    - yesod-gitrev
+    - yesod-job-queue
+    - yesod-static-angular
+    constraints:
+      benches: expect-success
+      library-profiling: true
+      flags: {}
+      build-benchmarks: true
+      skip-build: false
+      tests: expect-success
+      haddocks: expect-success
+      maintainer: Adam Bergmark <adam@bergmark.nl> @bergmark
+      configure-args: []
+      version-range: <1.0
+    version: '0.11.2.1'
+    cabal-file-info:
+      size: 4634
+      hashes:
+        MD5: b2b4ef66114e351b788af8df16513df9
+        Skein512_512: 2c1a12cacd8bb458c11d2b94125f3fcdc976f81daf2cf7f6d4795a734ea0accbbc950916be70703cd088be646032c1aef3108f3b220baf8c931efd4e6a1dec2d
+        SHA1: 92b374f030103c92dbf9ce22240dde0109e28209
+        SHA512: 33bc7d601f800354ce5b8087122b4d9cc40d1d62623f5a89e355bf04ea4a2d92688ff3941cb0d8586179bdd01c902e99eb504ca66681540f21cdab1fc00e54d7
+        SHA256: 9d08400d818255b7ee068ed6cfe2d4db1270066e4699f8c85afa6efc63787821
+        GitSHA1: 06f8123070664e3d80d39baf5e3e8c1f92c3003c
+    github-pings:
+    - bos
+    description:
+      cabal-version: '1.10'
+      modules:
+      - Data.Aeson
+      - Data.Aeson.Encode
+      - Data.Aeson.Internal
+      - Data.Aeson.Internal.Time
+      - Data.Aeson.Parser
+      - Data.Aeson.TH
+      - Data.Aeson.Types
+      provided-exes: []
+      packages:
+        test-framework-hunit:
+          components:
+          - test-suite
+          range: -any
+        bytestring:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.10.4.0'
+        test-framework:
+          components:
+          - test-suite
+          range: -any
+        fail:
+          components:
+          - library
+          range: ==4.9.*
+        base:
+          components:
+          - library
+          - test-suite
+          range: ! '>=4.5 && <5'
+        time:
+          components:
+          - library
+          - test-suite
+          range: ! '>=1.5'
+        unordered-containers:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.2.5.0'
+        text:
+          components:
+          - library
+          - test-suite
+          range: ! '>=1.1.1.0'
+        syb:
+          components:
+          - library
+          range: -any
+        test-framework-quickcheck2:
+          components:
+          - test-suite
+          range: -any
+        dlist:
+          components:
+          - library
+          range: ! '>=0.2'
+        HUnit:
+          components:
+          - test-suite
+          range: -any
+        base-orphans:
+          components:
+          - test-suite
+          range: ! '>=0.5.3 && <0.6'
+        tagged:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.8.3 && <0.9'
+        containers:
+          components:
+          - library
+          - test-suite
+          range: -any
+        quickcheck-instances:
+          components:
+          - test-suite
+          range: ! '>=0.3.12'
+        ghc-prim:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.2'
+        mtl:
+          components:
+          - library
+          range: -any
+        hashable:
+          components:
+          - library
+          - test-suite
+          range: ! '>=1.1.2.0'
+        attoparsec:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.13.0.1'
+        transformers:
+          components:
+          - library
+          range: -any
+        deepseq:
+          components:
+          - library
+          range: -any
+        scientific:
+          components:
+          - library
+          range: ! '>=0.3.1 && <0.4'
+        QuickCheck:
+          components:
+          - test-suite
+          range: ! '>=2.7 && <2.10'
+        aeson:
+          components:
+          - test-suite
+          range: -any
+        template-haskell:
+          components:
+          - library
+          - test-suite
+          range: ! '>=2.7'
+        vector:
+          components:
+          - library
+          - test-suite
+          range: ! '>=0.8'
+      tools: {}
+tools:
+- name: alex
+  version: '3.1.7'
+- name: c2hs
+  version: '0.28.1'
+- name: cabal-install
+  version: '1.24.0.0'
+- name: cpphs
+  version: '1.20.2'
+- name: happy
+  version: '1.19.5'
+- name: hsx2hs
+  version: '0.13.5'
+- name: gtk2hs-buildtools
+  version: '0.13.2.1'
