diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 0.2.2 (2024-05-06)
+- new add group and groupinfo commands
+- help: order commands with better grouping
+- update to Haskell pagure-0.2.0
+
 ## 0.2.1 (2022-02-17)
 - new commands: 'project', 'issue', 'userinfo'
 - drop --url and use aeson-pretty
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -5,97 +5,120 @@
 [![Stackage Lts](http://stackage.org/package/pagure-cli/badge/lts)](http://stackage.org/lts/package/pagure-cli)
 [![Stackage Nightly](http://stackage.org/package/pagure-cli/badge/nightly)](http://stackage.org/nightly/package/pagure-cli)
 
-A [pagure](https://docs.pagure.org/pagure/) client for querying
-projects and users.
+A [pagure](https://docs.pagure.org/pagure/) gitforge client for querying
+and listing projects (repos), users, groups, and issues.
 
 ## Usage
 By default it uses the [src.fedoraproject.org](https://src.fedoraproject.org/)
 Pagure server instance (aka Fedora dist-git), but this can be overriden
 by the `--server` option.
 
-## List/search for repos
+### List/search for repos
 
+`$ pagure list rac\*`
+
 ```
-$ pagure list emacs\*
-emacs
-:
+rpms/raceintospace
+rpms/racket
 ```
+
 Note by default 'orphan' owned packages are excluded.
 
 One can also filter by owner or committer.
 
-## List user's projects
+### List user's projects
 
 Count projects of a user:
+
+`$ pagure user --server pagure.io --count mattdm`
+
 ```
-$ pagure user -s pagure.io --count mattdm
-93
+120
 ```
 
-## List groups:
+### List groups:
 
 ```
 $ pagure groups
 :
 ```
 
-## More commands
+### List group packages
+`$ pagure group budgie-sig`
 
-- `branches` lists the git branches of a repo
+```
+rpms/libxfce4windowing
+rpms/sassc
+rpms/wlrctl
+```
 
-- `issues` to query and list issues
+`$ pagure group -c rust-sig`
 
-- `users` to list usernames of users
+```
+3093
+```
 
-- `username` shows the fullname of a user
+## Help
 
-- `git-url` shows the git urls for a repo
+`$ pagure --version`
 
-## Help
+```
+0.2.2
+```
 
-Use `--help` to get help on individual commands:
+`$ pagure --help`
 
 ```
-$ pagure list --help
-Usage: pagure list [-s|--server SERVER] [-c|--count] [(-j|--json) | (-y|--yaml)]
-                   [(-F|--only-forks) | (-f|--include-forks)]
-                   [-n|--namespace NAMESPACE]
-                   [(-o|--owner OWNER) | (-u|--username USERNAME)] [PATTERN]
-  list projects
+Pagure client
 
+Usage: pagure [--version] COMMAND
+
+  Simple pagure CLI
+
 Available options:
-  -s,--server SERVER       Pagure server
-  -c,--count               Show number only
-  -j,--json                Output JSON
-  -y,--yaml                YAML output
-  -F,--only-forks          Only list forks
-  -f,--include-forks       Include forks [default: ignore forks]
-  -n,--namespace NAMESPACE Specify project repo namespace
-  -o,--owner OWNER         Projects with certain owner
-  -u,--username USERNAME   Projects to which username can commit
   -h,--help                Show this help text
+  --version                Show version
+
+Available commands:
+  list                     list projects
+  user                     list user repos
+  group                    list group repos
+  project                  show project details
+  branches                 list project branches
+  git-url                  show project repo's git urls
+  issues                   list project issues
+  issue                    show project issue
+  users                    list users
+  username                 fullname of user
+  userinfo                 show user details
+  groups                   list groups
+  groupinfo                show group details
 ```
+Use `--help` to get help on individual commands:
 
+
 ## Installation
+pagure-cli is packaged in Fedora and EPEL 9: <https://src.fedoraproject.org/rpms/pagure-cli>
 
+## Build from source
 To build the latest release you will need cabal-install and ghc, or stack:
 
-`stack install pagure-cli` or `cabal new-install pagure-cli`.
-
-Or to build from the git source, run directly without the package name.
-
-## Binaries
+`stack install pagure-cli` or `cabal install pagure-cli`.
 
-pagure-cli is packaged in Fedora (since F34).
+Or to build from the git source, run the commands without the package name.
 
 ## Contributions
-
 Pagure rest API can be found on pagure servers: eg <https://pagure.io/api/0>.
 There are still many unsupported commands and options.
-Please open a ticket or PR to request adding more.
+Please open a ticket or PR to request adding more at
+<https://github.com/juhp/pagure-cli>.
 
-## Other clients
+Also part of this client uses the simple
+[pagure-hs](https://hackage.haskell.org/package/pagure) bindings.
+
+## Other pagure clients
 After writing the initial version I discovered that
 Ricky Elrod (relrod) had made <https://github.com/fedora-infra/pagure-cli>.
 
-I discovered a client in Rust <https://pagure.io/ironthree/bodhi-cli> by decathorpe.
+There is a library client in Rust <https://pagure.io/ironthree/pagure-rs>
+by decathorpe.
diff --git a/pagure-cli.cabal b/pagure-cli.cabal
--- a/pagure-cli.cabal
+++ b/pagure-cli.cabal
@@ -1,21 +1,23 @@
 cabal-version:       1.18
 name:                pagure-cli
-version:             0.2.1
-synopsis:            Pagure client
-description:         A command-line Pagure client for querying projects and users.
+version:             0.2.2
+synopsis:            A Pagure gitforge query tool
+description:         A command-line Pagure (gitforge) client for querying
+                     and listing projects, users, groups, and issues.
 homepage:            https://github.com/juhp/pagure-cli
 bug-reports:         https://github.com/juhp/pagure-cli/issues
 license:             GPL-2
 license-file:        LICENSE
 author:              Jens Petersen <juhpetersen@gmail.com>
 maintainer:          Jens Petersen <juhpetersen@gmail.com>
-copyright:           2019-2020,2022 Jens Petersen
+copyright:           2019-2020,2022,2024 Jens Petersen
 category:            Utility
 build-type:          Simple
 extra-doc-files:     README.md
-                   , CHANGELOG.md
+                     CHANGELOG.md
 tested-with:         GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5,
-                     GHC == 8.8.4, GHC == 8.10.4, GHC == 9.0.2
+                     GHC == 8.8.4, GHC == 8.10.4, GHC == 9.0.2, GHC == 9.2.8,
+                     GHC == 9.4.8, GHC == 9.6.5, GHC == 9.8.2
 
 source-repository head
   type:                git
@@ -29,8 +31,10 @@
                      , base < 5
                      , bytestring
                      , filepath
+                     , http-query
                      , optparse-applicative
-                     , pagure >= 0.1.1
+                     , pagure >= 0.2
+                     , simple-cmd
                      , simple-cmd-args >= 0.1.6
                      , text
                      , unordered-containers
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -22,16 +22,13 @@
 import qualified Data.ByteString.Char8 as B
 import qualified Data.ByteString.Lazy.Char8 as BL
 import Data.Maybe
-#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,11,0))
-#else
-import Data.Semigroup ((<>))
-#endif
 import Data.Text (Text)
 import qualified Data.Text as T
 import qualified Data.Text.IO as T
 import Data.Yaml (encode)
+import Network.HTTP.Query ((+/+))
+import SimpleCmd (error', (+-+))
 import SimpleCmdArgs
-import System.FilePath ((</>))
 import Fedora.Pagure
 
 import Paths_pagure_cli (version)
@@ -41,27 +38,83 @@
   simpleCmdArgs (Just version) "Pagure client" "Simple pagure CLI" $
   subcommands
   [ Subcommand "list" "list projects" $
-    listProjects <$> serverOpt <*> countOpt <*> formatOpt <*> forksOpt <*> optional namespaceOpt <*> optional packagerOpt <*> optional (strArg "PATTERN")
-  , Subcommand "user" "user repos" $
-    userRepos <$> serverOpt <*> countOpt <*> switchWith 'f' "forks" "List user's forks" <*> strArg "USER"
+    listProjects
+    <$> serverOpt
+    <*> countOpt
+    <*> formatOpt
+    <*> forksOpt
+    <*> optional namespaceOpt
+    <*> optional packagerOpt
+    <*> optional (strArg "PATTERN")
+  , Subcommand "user" "list user repos" $
+    userRepos
+    <$> serverOpt
+    <*> countOpt
+    <*> switchWith 'f' "forks" "List user's forks"
+    <*> strArg "USER"
+  , Subcommand "group" "list group repos" $
+    groupProjects
+    <$> serverOpt
+    <*> switchWith 'c' "count" "Count projects"
+    <*> strArg "GROUP"
+  , Subcommand "project" "show project details" $
+    projectInfo
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "PROJECT"
   , Subcommand "branches" "list project branches" $
-    repoBranches <$> serverOpt <*> formatOpt <*> strArg "REPO"
+    repoBranches
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "REPO"
+  , Subcommand "git-url" "show project repo's git urls" $
+    gitUrl
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "REPO"
   , Subcommand "issues" "list project issues" $
-    projectIssues <$> serverOpt <*> countOpt <*> formatOpt <*> strArg "REPO" <*> switchWith 'A' "all" "list Open and Closed issues" <*> optional (strOptionWith 'a' "author" "AUTHOR" "Filter issues by creator") <*> optional (strOptionWith 'S' "since" "Y-M-D" "Filter issues updated after date") <*> optional (strOptionWith 't' "title" "pattern" "Filter issues by title")
+    projectIssues
+    <$> serverOpt
+    <*> countOpt
+    <*> formatOpt
+    <*> strArg "REPO"
+    <*> switchWith 'A' "all" "list Open and Closed issues"
+    <*> optional (strOptionWith 'a' "author" "AUTHOR" "Filter issues by creator")
+    <*> optional (strOptionWith 'S' "since" "Y-M-D" "Filter issues updated after date")
+    <*> optional (strOptionWith 't' "title" "pattern" "Filter issues by title")
+  , Subcommand "issue" "show project issue" $
+    projectIssue
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "REPO"
+    <*> argumentWith auto "ISSUE"
   , Subcommand "users" "list users" $
-    users <$> serverOpt <*> formatOpt <*> strArg "PATTERN"
+    users
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "PATTERN"
   , Subcommand "username" "fullname of user" $
-    username <$> serverOpt <*> formatOpt <*> strArg "USERNAME"
-  , Subcommand "groups" "list groups" $
-    groups <$> serverOpt <*> countOpt <*> formatOpt <*> optional (strArg "PATTERN")
-  , Subcommand "git-url" "show project repo's git urls" $
-    gitUrl <$> serverOpt <*> formatOpt <*> strArg "REPO"
-  , Subcommand "project" "show project details" $
-    projectInfo <$> serverOpt <*> formatOpt <*> strArg "PROJECT"
-  , Subcommand "issue" "show project issue" $
-    projectIssue <$> serverOpt <*> formatOpt <*> strArg "REPO" <*> argumentWith auto "ISSUE"
+    username
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "USERNAME"
   , Subcommand "userinfo" "show user details" $
-    userInfo <$> serverOpt <*> formatOpt <*> strArg "USERNAME"
+    userInfo
+    <$> serverOpt
+    <*> formatOpt
+    <*> strArg "USERNAME"
+  , Subcommand "groups" "list groups" $
+    groups
+    <$> serverOpt
+    <*> countOpt
+    <*> formatOpt
+    <*> optional (strArg "PATTERN")
+  , Subcommand "groupinfo" "show group details" $
+    groupInfo
+    <$> serverOpt
+    <*> switchWith 'p' "projects" "List projects"
+    <*> formatOpt
+    <*> strArg "GROUP"
   ]
   where
     countOpt = switchWith 'c' "count" "Show number only"
@@ -94,7 +147,7 @@
     error' "Please give a package pattern, --count, or --owner/--username"
   let path = "projects"
       params = makeKey "short" "1" ++ fork ++ packager ++ maybeKey "namespace" mnamespace ++ maybeKey "pattern" mpattern
-  pages <- queryPaged server count path params ("pagination", "page")
+  pages <- queryPagureCountPaged server count path params ("pagination", "page")
   mapM_ (defaultPrinter format printPage) pages
   where
     -- (!orphan only works on pagure >=0.29)
@@ -114,27 +167,21 @@
           projects = lookupKey' "projects" result
       (mapM_ T.putStrLn . mapMaybe (lookupKey key')) projects
 
+-- FIXME duplicates subset of listProjects
 userRepos :: String -> Bool -> Bool -> String -> IO ()
 userRepos server count forks user =
   if count then do
-    let path = "user" </> user
+    let path = "user" +/+ user
     mcnt <- queryPagureCount server path [] $
             if forks then "forks_pagination" else "repos_pagination"
     print $
       fromMaybe
-      (error' ("number of " ++ (if forks then "forks" else "repos") ++ " could not be determined"))
+      (error' ("number of" +-+ (if forks then "forks" else "repos") +-+ "could not be determined"))
       mcnt
     else do
     repos <- (if forks then pagureUserForks else pagureUserRepos) server user
     mapM_ T.putStrLn repos
 
--- maybeKey :: String -> Maybe String -> Query
--- maybeKey _ Nothing = []
--- maybeKey k mval = [(B.pack k, fmap B.pack mval)]
-
--- makeKey :: String -> String -> Query
--- makeKey k val = [(B.pack k, Just (B.pack val))]
-
 boolKey :: String -> Bool -> String -> Query
 boolKey _ False _ = []
 boolKey k True val = makeKey k val
@@ -142,10 +189,10 @@
 -- FIXME limit max number of issues
 projectIssues :: String -> Bool -> OutputFormat -> String -> Bool -> Maybe String -> Maybe String -> Maybe String -> IO ()
 projectIssues server count format repo allstatus mauthor msince mpat = do
-  let path = repo </> "issues"
-      params = [("status", Just "all") | allstatus] ++
+  let path = repo +/+ "issues"
+      params = [makeItem "status" "all" | allstatus] ++
                maybeKey "author" mauthor ++ maybeKey "since" msince
-  pages <- queryPaged server count path params ("pagination", "page")
+  pages <- queryPagureCountPaged server count path params ("pagination", "page")
   mapM_ (defaultPrinter format printIssues) pages
   where
     printIssues :: Object -> IO ()
@@ -160,7 +207,7 @@
         Nothing -> putStrLn "parsing issue failed"
         Just (id',title,status) ->
           when (isNothing mpat || T.pack (fromJust mpat) `T.isInfixOf` title) $
-          putStrLn $ "https://" <> server </> repo </> "issue" </> show id' <> " (" <> T.unpack status <> "): " <> T.unpack title
+          putStrLn $ "https://" ++ server +/+ repo +/+ "issue" +/+ show id' +-+ "(" ++ T.unpack status ++ "):" +-+ T.unpack title
 
     parseIssue :: Object -> Maybe (Integer, Text, Text)
     parseIssue =
@@ -170,24 +217,12 @@
         status <- obj .: "status"
         return (id',title,status)
 
--- FIXME limit max number of pages (10?) or --pages
-queryPaged :: String -> Bool -> String -> Query -> (String,String)
-           -> IO [Object]
-queryPaged server count path params (pagination,paging) =
-  if count
-    then do
-    mnum <- queryPagureCount server path params pagination
-    print $ fromMaybe (error' "pages not found") mnum
-    return []
-    else
-    queryPagurePaged server path params (pagination,paging)
-
 repoBranches :: String -> OutputFormat -> String -> IO ()
 repoBranches server format repo = do
   let namespace =
         if server == srcFedoraprojectOrg && '/' `notElem` repo
         then "rpms/" else ""
-      path = namespace ++ repo </> "git/branches"
+      path = namespace ++ repo +/+ "git/branches"
   eres <- queryPagureSingle server path []
   either error' (defaultPrinter format (printKeyList "branches")) eres
 
@@ -200,9 +235,11 @@
 
 username :: String -> OutputFormat -> String -> IO ()
 username server format user = do
-  let path = "user" </> user
-  res <- queryPagure' server path $ makeKey "per_page" "1"
-  defaultPrinter format printName res
+  let path = "user" +/+ user
+  eres <- queryPagureSingle server path $ makeKey "per_page" "1"
+  case eres of
+    Left err -> error' err
+    Right res -> defaultPrinter format printName res
   where
     printName res =
       case lookupKey "user" res >>= lookupKey "fullname" of
@@ -213,7 +250,7 @@
 groups server count format mpat = do
   let path = "groups"
       params = maybeKey "pattern" mpat
-  pages <- queryPaged server count path params ("pagination", "page")
+  pages <- queryPagureCountPaged server count path params ("pagination", "page")
   mapM_ (defaultPrinter format (printKeyList "groups")) pages
 
 printKeyList :: String -> Object -> IO ()
@@ -225,7 +262,7 @@
   let namespace =
         if server == srcFedoraprojectOrg && '/' `notElem` repo
         then "rpms/" else ""
-      path = namespace ++ repo </> "git/urls"
+      path = namespace ++ repo +/+ "git/urls"
   res <- queryPagure server path []
   defaultPrinter format printURLs res
   where
@@ -241,14 +278,6 @@
 #endif
     localLookupKey = lookupKey' . T.pack
 
--- from simple-cmd
-error' :: String -> a
-#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,9,0))
-error' = errorWithoutStackTrace
-#else
-error' = error
-#endif
-
 yamlPrinter :: OutputFormat -> Object -> IO ()
 yamlPrinter FormatDefault = yamlPrinter FormatYaml
 yamlPrinter FormatJson = BL.putStrLn . encodePretty
@@ -272,3 +301,28 @@
 userInfo server format user = do
   eval <- pagureUserInfo server user []
   either error' (yamlPrinter format) eval
+
+groupInfo :: String -> Bool -> OutputFormat -> String -> IO ()
+groupInfo server projects format group = do
+  let params = [makeItem "projects" "1" | projects]
+  eval <- pagureGroupInfo server group params
+  either error' (yamlPrinter format) eval
+
+-- FIXME support acl parameter (admin, commit or ticket)
+groupProjects :: String -> Bool -> String -> IO ()
+groupProjects server count group = do
+  let path = "group" +/+ group
+      params = makeKey "projects" "1"
+  pages <- queryPagureCountPaged server count path params ("pagination", "page")
+  mapM_ (defaultPrinter FormatDefault printPage) pages
+  where
+    -- acl
+    -- packager = case mpackager of
+    --   Nothing -> boolKey "owner" (server == srcFedoraprojectOrg) "!orphan"
+    --   Just (Owner o) -> makeKey "owner" o
+    --   Just (Committer c) -> makeKey "username" c
+
+    printPage :: Object -> IO ()
+    printPage result = do
+     let projects = lookupKey' "projects" result
+     (mapM_ T.putStrLn . mapMaybe (lookupKey "fullname")) projects
