diff --git a/GhcUsage.hs b/GhcUsage.hs
new file mode 100644
--- /dev/null
+++ b/GhcUsage.hs
@@ -0,0 +1,107 @@
+module GhcUsage (frontendPlugin) where
+
+import qualified GHC
+import GhcPlugins
+import DriverPhases
+import GhcMonad
+import UniqDFM
+import UniqDSet
+import Avail
+import Outputable
+
+import System.IO
+import System.Exit
+import Control.Monad
+import Data.Maybe
+import Data.List
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+frontendPlugin :: FrontendPlugin
+frontendPlugin = defaultFrontendPlugin {
+  frontend = usage
+  }
+
+usage :: [String] -> [(String, Maybe Phase)] -> Ghc ()
+usage _flags args = do
+    dflags0 <- getDynFlags
+    -- NB: frontend plugin defaults to OneShot
+    _ <- GHC.setSessionDynFlags dflags0 { ghcMode = CompManager }
+    dflags <- getDynFlags
+    let (hs_srcs, non_hs_srcs) = partition isHaskellishTarget args
+    targets <- mapM (uncurry GHC.guessTarget) hs_srcs
+    GHC.setTargets targets
+    ok_flag <- GHC.load GHC.LoadAllTargets
+    when (failed ok_flag) (liftIO $ exitWith (ExitFailure 1))
+    hsc_env <- GHC.getSession
+    let ifaces = map hm_iface
+               . eltsHpt
+               $ hsc_HPT hsc_env
+        usages = Map.unionsWith unionUniqDSets
+               . map usageModIface
+               $ ifaces
+    liftIO . printForUser dflags stdout neverQualify
+           . vcat
+           . vpunctuate (text "")
+           . map (renderHeader usages)
+           -- . filter ((==HsigFile) . mi_hsc_src)
+           $ ifaces
+    return ()
+
+vpunctuate :: SDoc   -- ^ The punctuation
+           -> [SDoc] -- ^ The list that will have punctuation added between every adjacent pair of elements
+           -> [SDoc] -- ^ Punctuated list
+vpunctuate _ []     = []
+vpunctuate p (d:ds) = go d ds
+                   where
+                     go d [] = [d]
+                     go d (e:es) = (d $$ p) : go e es
+
+usageModIface :: ModIface -> Map ModuleName (UniqDSet OccName)
+usageModIface iface = Map.fromList $
+    [ (mod_name, mkUniqDSet (map fst occs_w_fp))
+    | UsageHomeModule{ usg_mod_name = mod_name
+                     , usg_entities = occs_w_fp } <- mi_usages iface ]
+
+renderName :: Name -> SDoc
+renderName n = let occ = occName n
+               in parenSymOcc occ (ppr occ)
+
+renderField :: FieldLabel -> SDoc
+renderField f = ftext (flLabel f)
+
+renderAvailInfo :: AvailInfo -> SDoc
+renderAvailInfo a
+    = renderName n <>
+        if null ns && null fs
+            then empty
+            else parens . fsep . punctuate comma
+               $ map renderName (sortOn (occNameFS . occName) ns)
+              ++ map renderField (sortOn flLabel fs)
+  where
+    n  = availName a
+    ns = filter (n /=) (availNonFldNames a)
+    fs = availFlds a
+
+renderHeader :: Map ModuleName (UniqDSet OccName) -> ModIface -> SDoc
+renderHeader usages iface
+    = header <+> ppr mod_name <+> text "(" $$
+      nest 4 pp_exports $$
+      text ") where"
+  where
+    mod_name = moduleName (mi_module iface)
+    usage = fromMaybe emptyUniqDSet (Map.lookup mod_name usages)
+    header | HsigFile <- mi_hsc_src iface = text "signature"
+           | otherwise                    = text "module"
+    pp_exports = vcat
+               . punctuate comma
+               . map renderAvailInfo
+               . sortOn availSortKey
+               $ exports
+    exports = filterAvails ((`elementOfUniqDSet` usage) . occName)
+            $ mi_exports iface
+
+availSortKey :: AvailInfo -> String
+availSortKey a
+    -- Fortuitously, capital letters sort first.
+    = occNameString . occName $ availName a
diff --git a/GhcUsageWrapper.hs b/GhcUsageWrapper.hs
new file mode 100644
--- /dev/null
+++ b/GhcUsageWrapper.hs
@@ -0,0 +1,15 @@
+import GHC.Paths
+import System.Posix.Process
+import System.Environment
+
+main = do
+  args <- getArgs
+  let interactive = "--interactive" `elem` args
+      args' = do
+        arg <- args
+        case arg of
+          "--interactive" ->
+            ["--frontend", "GhcUsage",
+             "-plugin-package", "ghc-usage"]
+          _ -> return arg
+  executeFile ghc False (args' ++ if interactive then ["-user-package-db"] else []) Nothing
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2017 Edward Z. Yang
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holder nor the names of its
+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
+HOLDER 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/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/ghc-usage.cabal b/ghc-usage.cabal
new file mode 100644
--- /dev/null
+++ b/ghc-usage.cabal
@@ -0,0 +1,33 @@
+name:                ghc-usage
+version:             0.1.0.0
+license:             BSD3
+license-file:        LICENSE
+author:              Edward Z. Yang
+maintainer:          ezyang@cs.stanford.edu
+synopsis:            Print minimal export lists
+category:            Development
+bug-reports:         https://github.com/ezyang/ghc-usage/issues
+description:         A frontend plugin for computing the minimal export list
+                     of a module or signature within a package.  This can
+                     be used in conjunction with Backpack to easily determine
+                     the true set of identifiers which are required from a
+                     signature, so that the requirement can be pared down
+                     to precisely what is required.
+build-type:          Simple
+cabal-version:       >=1.10
+
+source-repository head
+    type:     git
+    location: https://github.com/ezyang/ghc-usage
+
+library
+  exposed-modules:     GhcUsage
+  build-depends:       base >= 4.10 && < 4.11,
+                       ghc  >= 8.1  && < 8.3,
+                       containers
+  default-language:    Haskell2010
+
+executable ghc-usage
+  main-is:             GhcUsageWrapper.hs
+  build-depends:       base, ghc-paths, unix
+  default-language:    Haskell2010
