diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+# stack uses this directory for build artifacts
+/.stack-work/
+
+# Made by 'hasktags --ctags .'
+tags
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+1.0.0 (2025-09-05)
+
+  * Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+Copyright (c) 2025, Dino Morelli <dino@ui3.info>
+
+Permission to use, copy, modify, and/or distribute this software
+for any purpose with or without fee is hereby granted, provided
+that the above copyright notice and this permission notice appear
+in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,58 @@
+# optparse-applicative-dex
+
+
+## Synopsis
+
+Extra functions for working with optparse-applicative
+
+
+## Description
+
+optparse-applicative is a fantastic library
+
+My usage of it has often used the same features wrapped in a `parseOpts`
+function along with a version helper.
+
+This package abstracts that code into a re-usable library, minimizing pasting
+between projects.
+
+In most CLI projects I almost always want:
+
+- A header consisting of the program name and a short description
+- A helper parser to provide -h, --help
+- A version helper to respond to a --version option with shorter output than -h, --help
+- Some additional usage info below the generated switch usage
+
+The parseOpts' and parseOptsWithWidth functions in this library take a parser
+and arguments for header, footer and the app version as generated by cabal and
+do all these things automatically.
+
+A simple example:
+
+    {- # LANGUAGE OverloadedStrings #-}
+
+    import Options.Applicative.Dex
+    import Paths_your_app (version)
+
+    data Options = Options { optA :: String , optB :: Bool }
+      deriving Show
+
+    parser :: Parser Options
+    parser = Options
+      <$> option str ( short 'a' <> metavar "STR" <> help "The a option, a string" )
+      <*> switch ( short 'b' <> help "The b option, a switch")
+
+    main :: IO ()
+    main = do
+      opts <- parseOpts' parser "header-info" "footer-info" version
+      print opts
+
+
+## Getting source
+
+Source code is available from codeberg at the [optparse-applicative-dex](https://codeberg.org/dinofp/optparse-applicative-dex) project page.
+
+
+## Contact
+
+Dino Morelli <dino@ui3.info>
diff --git a/optparse-applicative-dex.cabal b/optparse-applicative-dex.cabal
new file mode 100644
--- /dev/null
+++ b/optparse-applicative-dex.cabal
@@ -0,0 +1,54 @@
+cabal-version: 2.2
+
+name: optparse-applicative-dex
+version: 1.0.0
+synopsis: Extra functions for working with optparse-applicative
+description:
+  optparse-applicative is a fantastic library
+  .
+  My usage of it has often used the same features wrapped in a `parseOpts`
+  function along with a version helper.
+  .
+  This package abstracts that code into a re-usable library, minimizing pasting
+  between projects.
+author: Dino Morelli
+maintainer: dino@ui3.info
+copyright: 2025 Dino Morelli
+category: CLI, Console, Options, Parsing
+license: ISC
+license-file: LICENSE
+build-type: Simple
+extra-source-files:
+  .gitignore
+  README.md
+  stack.yaml
+extra-doc-files:
+  CHANGELOG.md
+
+source-repository head
+  type: git
+  location: https://codeberg.org/dinofp/optparse-applicative-dex
+
+common lang
+  default-language: Haskell2010
+  ghc-options:
+    -fwarn-tabs
+    -Wall
+    -Wcompat
+    -Wderiving-typeable
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+    -Wpartial-fields
+    -Wredundant-constraints
+
+library
+  import: lang
+  exposed-modules:
+      Options.Applicative.Dex
+  hs-source-dirs:
+    src/lib
+  build-depends:
+      base >=3 && <5
+    , optparse-applicative >=0.17.1 && <0.19
+    , prettyprinter >=1.7.1 && <2
+    , text >=1.11.1 && <3
diff --git a/src/lib/Options/Applicative/Dex.hs b/src/lib/Options/Applicative/Dex.hs
new file mode 100644
--- /dev/null
+++ b/src/lib/Options/Applicative/Dex.hs
@@ -0,0 +1,108 @@
+{-|
+optparse-applicative is a fantastic library
+
+My usage of it has often used the same features wrapped in a `parseOpts`
+function along with a version helper.
+
+This package abstracts that code into a re-usable library, minimizing pasting
+between projects.
+
+In most projects I almost always want:
+
+- A header consisting of the program name and a short description
+- A helper parser to provide -h, --help
+- A version helper to respond to a --version option with shorter output than -h, --help
+- Some additional usage info below the generated switch usage
+
+The parseOpts' and parseOptsWithWidth functions in this library take a parser
+and arguments for header, footer and the app version as generated by cabal and
+do all these things automatically.
+
+A simple example:
+
+@
+{- # LANGUAGE OverloadedStrings #-}
+
+import Options.Applicative.Dex
+import Paths_your_app (version)
+
+data Options = Options { optA :: String , optB :: Bool }
+  deriving Show
+
+parser :: Parser Options
+parser = Options
+  <$> option str ( short 'a' <> metavar "STR" <> help "The a option, a string" )
+  <*> switch ( short 'b' <> help "The b option, a switch")
+
+main :: IO ()
+main = do
+  opts <- parseOpts' parser "header-info" "footer-info" version
+  print opts
+@
+-}
+
+module Options.Applicative.Dex
+  (
+    -- * Option parser functions
+    parseOpts'
+  , parseOptsWithWidth
+
+    -- * Re-exporting everything from optparse-applicative
+  , module Options.Applicative
+  )
+  where
+
+import Data.Text.Lazy (Text, unpack)
+import Data.Version (Version, showVersion)
+import Options.Applicative
+import Prettyprinter (pretty)
+import System.Environment (getProgName)
+
+
+versionHelper :: String -> Version -> Parser (a -> a)
+versionHelper progName version =
+  infoOption (progName <> " " <> showVersion version) $ mconcat
+  [ long "version"
+  , help "Show version information"
+  , hidden
+  ]
+
+
+{-|
+  Main opts parsing entry point
+
+  This is named with a ' so the `parseOpts` identifier can be re-used by your
+  code. For example in an Opts module where you define your parser and other
+  usage content.
+-}
+parseOpts'
+  :: Parser a   -- ^ CLI arguments parser made with optparse-applicative API
+  -> Text       -- ^ One-line app description for the usage header
+  -> Text       -- ^ Content for usage footer (after switches)
+  -> Version    -- ^ Application version
+  -> IO a
+parseOpts' = parseOptsWithWidth 100
+
+
+{-|
+  Opts parsing with column limit as an argument
+
+  This library defaults to setting the columns to 100 instead of the
+  optparse-applicative default of 80 columns. I found 80 to be kind of
+  restrictive, 100 seems like a better default. Use this function to set a
+  custom width.
+-}
+parseOptsWithWidth
+  :: Int        -- ^ Wrap column
+  -> Parser a   -- ^ CLI arguments parser made with optparse-applicative API
+  -> Text       -- ^ One-line app description for the usage header
+  -> Text       -- ^ Content for usage footer (after switches)
+  -> Version    -- ^ Application version
+  -> IO a
+parseOptsWithWidth cols parser headerContent footerContent version = do
+  pn <- getProgName
+  customExecParser (prefs $ columns cols)
+    $ info (parser <**> helper <**> versionHelper pn version)
+    (  header (pn <> " - " <> unpack headerContent)
+    <> (footerDoc . Just . pretty $ footerContent)
+    )
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,4 @@
+snapshot: lts-22.6
+
+packages:
+- .
