diff --git a/Distribution/PackDeps.hs b/Distribution/PackDeps.hs
new file mode 100644
--- /dev/null
+++ b/Distribution/PackDeps.hs
@@ -0,0 +1,174 @@
+module Distribution.PackDeps
+    ( -- * Data types
+      Newest
+    , CheckDepsRes (..)
+    , DescInfo
+      -- * Read package database
+    , loadNewest
+    , loadNewestFrom
+    , parseNewest
+      -- * Check a package
+    , checkDeps
+      -- * Get a single package
+    , getPackage
+    , parsePackage
+    , loadPackage
+      -- * Get multiple packages
+    , filterPackages
+    ) where
+
+import System.Directory (getAppUserDataDirectory)
+import qualified Data.Map as Map
+import Data.List (foldl', group, sort, isInfixOf)
+import Data.Time (UTCTime (UTCTime), addUTCTime)
+import Data.Maybe (mapMaybe)
+
+import Distribution.Package
+import Distribution.PackageDescription
+import Distribution.PackageDescription.Parse
+import Distribution.Version
+import Distribution.Text
+
+import Data.Char (toLower)
+import qualified Data.Text.Lazy as T
+import qualified Data.Text.Lazy.Encoding as T
+import qualified Data.Text.Encoding.Error as T
+import qualified Data.ByteString.Lazy as L
+
+import Data.List.Split (splitOn)
+import qualified Codec.Archive.Tar as Tar
+import qualified Codec.Archive.Tar.Entry as Tar
+
+loadNewest :: IO Newest
+loadNewest = do
+    c <- getAppUserDataDirectory "cabal"
+    let fn = c ++ "/packages/hackage.haskell.org/00-index.tar"
+    loadNewestFrom fn
+
+loadNewestFrom :: FilePath -> IO Newest
+loadNewestFrom = fmap parseNewest . L.readFile
+
+parseNewest :: L.ByteString -> Newest
+parseNewest = foldl' addPackage Map.empty . entriesToList . Tar.read
+
+entriesToList :: Tar.Entries -> [Tar.Entry]
+entriesToList Tar.Done = []
+entriesToList (Tar.Fail s) = error s
+entriesToList (Tar.Next e es) = e : entriesToList es
+
+addPackage :: Newest -> Tar.Entry -> Newest
+addPackage m entry =
+    case splitOn "/" $ Tar.entryPath entry of
+        [package', versionS, _] ->
+            case simpleParse versionS of
+                Just version ->
+                    case Map.lookup package' m of
+                        Nothing -> go package' version
+                        Just PackInfo { piVersion = oldv } ->
+                            if version > oldv
+                                then go package' version
+                                else m
+                Nothing -> m
+        _ -> m
+  where
+    go package' version =
+        case Tar.entryContent entry of
+            Tar.NormalFile bs _ ->
+                 Map.insert package' PackInfo
+                        { piVersion = version
+                        , piDesc = parsePackage bs
+                        , piEpoch = Tar.entryTime entry
+                        } m
+            _ -> m
+
+data PackInfo = PackInfo
+    { piVersion :: Version
+    , piDesc :: Maybe DescInfo
+    , piEpoch :: Tar.EpochTime
+    }
+    deriving (Show, Read)
+
+-- | The newest version of every package.
+type Newest = Map.Map String PackInfo
+
+-- | Information on a single package.
+data DescInfo = DescInfo
+    { diHaystack :: String
+    , diDeps :: [Dependency]
+    , diPackage :: PackageIdentifier
+    }
+    deriving (Show, Read)
+
+getDescInfo :: GenericPackageDescription -> DescInfo
+getDescInfo gpd = DescInfo
+    { diHaystack = map toLower $ author p ++ maintainer p ++ name
+    , diDeps = getDeps gpd
+    , diPackage = pi'
+    }
+  where
+    p = packageDescription gpd
+    pi'@(PackageIdentifier (PackageName name) _) = package p
+
+getDeps :: GenericPackageDescription -> [Dependency]
+getDeps x = concat
+          $ maybe id ((:) . condTreeConstraints) (condLibrary x)
+          $ map (condTreeConstraints . snd) (condExecutables x)
+
+checkDeps :: Newest -> DescInfo
+          -> (PackageName, Version, CheckDepsRes)
+checkDeps newest desc =
+    case mapMaybe (notNewest newest) $ diDeps desc of
+        [] -> (name, version, AllNewest)
+        x -> let y = map head $ group $ sort $ map fst x
+                 et = maximum $ map snd x
+              in (name, version, WontAccept y $ epochToTime et)
+  where
+    PackageIdentifier name version = diPackage desc
+
+-- | Whether or not a package can accept all of the newest versions of its
+-- dependencies. If not, it returns a list of packages which are not accepted,
+-- and a timestamp of the most recently updated package.
+data CheckDepsRes = AllNewest
+                  | WontAccept [(String, String)] UTCTime
+    deriving Show
+
+epochToTime :: Tar.EpochTime -> UTCTime
+epochToTime e = addUTCTime (fromIntegral e) $ UTCTime (read "1970-01-01") 0
+
+notNewest :: Newest -> Dependency -> Maybe ((String, String), Tar.EpochTime)
+notNewest newest (Dependency (PackageName s) range) =
+    case Map.lookup s newest of
+        Nothing -> Just ((s, " no version found"), 0)
+        Just PackInfo { piVersion = version, piEpoch = e } ->
+            if withinRange version range
+                then Nothing
+                else Just ((s, display version), e)
+
+-- | Loads up the newest version of a package from the 'Newest' list, if
+-- available.
+getPackage :: String -> Newest -> Maybe DescInfo
+getPackage s n = Map.lookup s n >>= piDesc
+
+-- | Parse information on a package from the contents of a cabal file.
+parsePackage :: L.ByteString -> Maybe DescInfo
+parsePackage lbs =
+    case parsePackageDescription $ T.unpack
+       $ T.decodeUtf8With T.lenientDecode lbs of
+        ParseOk _ x -> Just $ getDescInfo x
+        _ -> Nothing
+
+-- | Load a single package from a cabal file.
+loadPackage :: FilePath -> IO (Maybe DescInfo)
+loadPackage = fmap parsePackage . L.readFile
+
+-- | Find all of the packages matching a given search string.
+filterPackages :: String -> Newest -> [DescInfo]
+filterPackages needle =
+    mapMaybe go . Map.elems
+  where
+    needle' = map toLower needle
+    go PackInfo { piDesc = Just desc } =
+        if needle' `isInfixOf` diHaystack desc
+            then Just desc
+            else Nothing
+    go _ = Nothing
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+The following license covers this documentation, and the source code, except
+where otherwise indicated.
+
+Copyright 2010, Michael Snoyman. 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "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 HOLDERS 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/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/packdeps-cli.hs b/packdeps-cli.hs
new file mode 100644
--- /dev/null
+++ b/packdeps-cli.hs
@@ -0,0 +1,36 @@
+import Distribution.PackDeps
+import System.Environment (getArgs)
+import Distribution.Text (display)
+import Distribution.Package (PackageName (PackageName))
+
+main :: IO ()
+main = do
+    newest <- loadNewest
+    getArgs >>= mapM_ (go newest)
+  where
+    go newest fp = do
+        mdi <- loadPackage fp
+        di <-
+            case mdi of
+                Just di -> return di
+                Nothing -> error $ "Could not parse cabal file: " ++ fp
+        case checkDeps newest di of
+            (pn, v, AllNewest) ->
+                putStrLn $ concat
+                    [ unPackageName pn
+                    , "-"
+                    , display v
+                    , ": Can use newest versions of all dependencies"
+                    ]
+            (pn, v, WontAccept p _) -> do
+                putStrLn $ concat
+                    [ unPackageName pn
+                    , "-"
+                    , display v
+                    , ": Cannot accept the following packages"
+                    ]
+                flip mapM_ p $ \(x, y) -> putStrLn $ x ++ " " ++ y
+        putStrLn ""
+
+unPackageName :: PackageName -> String
+unPackageName (PackageName n) = n
diff --git a/packdeps.cabal b/packdeps.cabal
new file mode 100644
--- /dev/null
+++ b/packdeps.cabal
@@ -0,0 +1,37 @@
+name:            packdeps
+version:         0.0.0
+license:         BSD3
+license-file:    LICENSE
+author:          Michael Snoyman <michael@snoyman.com>
+maintainer:      Michael Snoyman <michael@snoyman.com>
+synopsis:        Check your cabal packages for lagging dependencies.
+description:
+    This provides a library and command line tool for checking if the upper bounds in your package's dependency list excludes the newest package available. The code was originally available only as a web interface at <http://packdeps.haskellers.com/>, but is now available for standalone use as well.
+    .
+    The command line tool has an incredibly simple interface: simply pass it a list of cabal files, and it will tell you what dependencies- if any- are restricted.
+category:        Distribution
+stability:       Stable
+cabal-version:   >= 1.6
+build-type:      Simple
+homepage:        http://packdeps.haskellers.com/
+
+library
+    build-depends:   base                      >= 4        && < 5
+                   , tar                       >= 0.3.1    && < 0.4
+                   , split                     >= 0.1.2.3  && < 0.2
+                   , bytestring                >= 0.9      && < 0.10
+                   , text                      >= 0.7      && < 0.12
+                   , Cabal                     >= 1.8      && < 1.11
+                   , time                      >= 1.1.4    && < 1.3
+                   , containers                >= 0.2      && < 0.5
+                   , directory                 >= 1.0      && < 1.2
+    exposed-modules: Distribution.PackDeps
+    ghc-options:     -Wall
+
+executable             packdeps
+    ghc-options:       -Wall
+    main-is:           packdeps-cli.hs
+
+source-repository head
+  type:     git
+  location: git://github.com/snoyberg/packdeps.git
