diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,8 +2,11 @@
 =========
 
 [![Build Status][ci-badge]][ci]
+[![Hackage][hackage-badge]][hackage]
 
 Generate checklists relevant to a given patch.
 
 [ci-badge]: https://travis-ci.org/spoqa/checkmate.svg?branch=master
 [ci]: https://travis-ci.org/spoqa/checkmate
+[hackage-badge]: https://img.shields.io/hackage/v/checkmate.svg
+[hackage]: https://hackage.haskell.org/package/checkmate
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -25,6 +25,7 @@
 
 import Checkmate.Check
 import Checkmate.Discover
+import Checkmate.Renderer
 
 type Command = App -> Checklist -> IO ()
 
@@ -61,14 +62,18 @@
     progDesc "Print a checklist as CommonMark (i.e. Markdown) format."
   where
     cmd :: Command
-    cmd _ = TIO.putStr . toCommonMark
+    cmd _ checklist = do
+        cwd <- getCurrentDirectory
+        TIO.putStr $ toCommonMark cwd 1 checklist
 
 gfmPI :: ParserInfo Command
 gfmPI = info (pure cmd) $
     progDesc "Print a checklist as GitHub Flavored Markdown format."
   where
     cmd :: Command
-    cmd _ = TIO.putStr . toGFMarkdown
+    cmd _ checklist = do
+        cwd <- getCurrentDirectory
+        TIO.putStr $ toGFMarkdown cwd 1 checklist
 
 githubPI :: ParserInfo Command
 githubPI = info (parser <**> helper) $
@@ -90,8 +95,9 @@
                 Nothing -> createComment auth owner repo pr
                 Just IssueComment { issueCommentId = cid } ->
                     editComment auth owner repo $ Id cid
+        cwd <- getCurrentDirectory
         Comment { commentHtmlUrl = leftCommentUrl } <-
-            leave (signature `append` toGFMarkdown checklist) >>= error
+            leave (signature `append` toGFMarkdown cwd 3 checklist) >>= error
         case leftCommentUrl of
             Just (URL u) -> TIO.putStrLn u
             _ -> return ()
@@ -163,20 +169,6 @@
     (  fullDesc
     <> progDesc "Generate checklists relevant to a given patch."
     )
-
-toCommonMark' :: Checklist -> Text -> Text
-toCommonMark' checklist itemPrefix =
-    "### Checklist  \x1f914\n\n" `append` intercalate
-        "\n"
-        [ " -  " `append` itemPrefix `append` replace "\n" "\n    " t
-        | Check { checkText = t } <- toList checklist
-        ]
-
-toGFMarkdown :: Checklist -> Text
-toGFMarkdown = (`toCommonMark'` "[ ] ")
-
-toCommonMark :: Checklist -> Text
-toCommonMark = (`toCommonMark'` "")
 
 printError :: Text -> IO a
 printError message = do
diff --git a/checkmate.cabal b/checkmate.cabal
--- a/checkmate.cabal
+++ b/checkmate.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           checkmate
-version:        0.1.0
+version:        0.1.1
 synopsis:       Generate checklists relevant to a given patch
 category:       Development
 stability:      alpha
@@ -44,13 +44,14 @@
       Checkmate.Discover
       Checkmate.Parser.CheckFile
       Checkmate.Parser.IndentBlock
+      Checkmate.Renderer
   default-language: Haskell2010
 
 executable checkmate
   main-is: Main.hs
   hs-source-dirs:
       app
-  ghc-options: -Wall -fwarn-incomplete-uni-patterns -threaded -rtsopts -with-rtsopts=-N -static -optl-static -optl-pthread -optc-Os -fPIC
+  ghc-options: -Wall -fwarn-incomplete-uni-patterns -threaded -rtsopts -with-rtsopts=-N -static
   build-depends:
       base >= 4.7 && < 5
     , diff-parse >= 0.2.1 && < 0.3.0
@@ -85,6 +86,7 @@
       Checkmate.DiscoverSpec
       Checkmate.Parser.CheckFileSpec
       Checkmate.Parser.IndentBlockSpec
+      Checkmate.RendererSpec
       Spec
   default-language: Haskell2010
 
@@ -117,5 +119,6 @@
       Checkmate.DiscoverSpec
       Checkmate.Parser.CheckFileSpec
       Checkmate.Parser.IndentBlockSpec
+      Checkmate.RendererSpec
       HLint
   default-language: Haskell2010
diff --git a/src/Checkmate/Check.hs b/src/Checkmate/Check.hs
--- a/src/Checkmate/Check.hs
+++ b/src/Checkmate/Check.hs
@@ -5,7 +5,7 @@
     , union
     ) where
 
-import Data.Set hiding (toList)
+import Data.Set
 import Data.Text
 import Data.Range.Range hiding (union)
 import System.FilePath
diff --git a/src/Checkmate/Renderer.hs b/src/Checkmate/Renderer.hs
new file mode 100644
--- /dev/null
+++ b/src/Checkmate/Renderer.hs
@@ -0,0 +1,55 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Checkmate.Renderer
+    ( toCommonMark
+    , toGFMarkdown
+    ) where
+
+import Data.List
+
+import Data.Set
+import Data.Text hiding (groupBy, isSuffixOf, zip)
+import System.FilePath
+
+import Checkmate.Check
+
+toCommonMark :: FilePath -> Int -> Checklist -> Text
+toCommonMark = toCommonMark' ""
+
+toGFMarkdown :: FilePath -> Int -> Checklist -> Text
+toGFMarkdown = toCommonMark' "[ ] "
+
+toCommonMark' :: Text -> FilePath -> Int -> Checklist -> Text
+toCommonMark' itemPrefix basePath headingLevel checklist = cat $
+    [heading, " Checklist  \x1f914\n"] ++
+    [ cat $
+        (if i == (1 :: Int)
+            then ["\n", heading, "# `", titlePath s, "`\n\n"]
+            else []
+            ) ++
+          [ " -  "
+          , itemPrefix
+          , replace "\n" "\n    " t
+          , "\n"
+          ]
+    | fileChecklist <- checks
+    , (i, Check { checkScope = s, checkText = t }) <- zip [1..] fileChecklist
+    ]
+  where
+    checks :: [[Check]]
+    checks = groupBy scopePathEquals $ toAscList checklist
+    scopePathEquals :: Check -> Check -> Bool
+    scopePathEquals Check { checkScope = a } Check { checkScope = b } =
+        scopePath a `equalFilePath` scopePath b
+    cat :: [Text] -> Text
+    cat = Data.Text.concat
+    heading :: Text
+    heading = pack ['#' | _ <- [1..headingLevel]]
+    titlePath :: Scope -> Text
+    titlePath scope = Data.Text.map
+        (\ c -> if c == pathSeparator then '/' else c) $
+        pack $ makeRelative basePath $ normalise p
+      where
+        p :: FilePath
+        p = case scope of
+                Directory p' -> addTrailingPathSeparator p'
+                FileBlock { scopePath = p' } -> p'
diff --git a/test/Checkmate/RendererSpec.hs b/test/Checkmate/RendererSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Checkmate/RendererSpec.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+module Checkmate.RendererSpec where
+
+import Data.Range.Range
+import Data.Text
+import System.FilePath
+import Test.Hspec
+import Text.InterpolatedString.Perl6
+
+import Checkmate.Check
+import Checkmate.Renderer
+
+fixture :: Checklist
+fixture =
+    [ Check (Directory $ "b" </> "foo" </> "bar" </> ".") 1
+            "Lorem ipsum dolor sit amet,"
+    , Check (Directory $ "b" </> "foo" </> "bar" </> ".") 2
+            "consectetur adipiscing elit,"
+    , Check (Directory $ "b" </> "foo" </> "bar" </> ".") 3
+            "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
+    , Check (FileBlock ("b" </> "foo" </> "bar" </> "baz.c") $ SpanRange 8 21) 1
+            "Ut enim ad minim veniam,"
+    , Check (FileBlock ("b" </> "foo" </> "bar" </> "baz.c") $ SpanRange 9 21) 2 $
+            "quis nostrud exercitation ullamco laboris nisi ut\n" `append`
+            "aliquip ex ea commodo consequat."
+    ]
+
+thinkingFace :: Text
+thinkingFace = "\x1f914"
+
+spec :: Spec
+spec = do
+    specify "toCommonMark" $
+        toCommonMark "b" 1 fixture `shouldBe` [qq|# Checklist  $thinkingFace
+
+## `foo/bar/`
+
+ -  Lorem ipsum dolor sit amet,
+ -  consectetur adipiscing elit,
+ -  sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+## `foo/bar/baz.c`
+
+ -  Ut enim ad minim veniam,
+ -  quis nostrud exercitation ullamco laboris nisi ut
+    aliquip ex ea commodo consequat.
+|]
+    specify "toGFMarkdown" $
+        toGFMarkdown "b" 3 fixture `shouldBe` [qq|### Checklist  $thinkingFace
+
+#### `foo/bar/`
+
+ -  [ ] Lorem ipsum dolor sit amet,
+ -  [ ] consectetur adipiscing elit,
+ -  [ ] sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+#### `foo/bar/baz.c`
+
+ -  [ ] Ut enim ad minim veniam,
+ -  [ ] quis nostrud exercitation ullamco laboris nisi ut
+    aliquip ex ea commodo consequat.
+|]
