diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 10d782d30b..5eac443f29 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,7 +1,7 @@
 name: 🛠 Bug report
 description: Create a report to help us improve
 title: Good bug title tells us about precise symptom, not about the root cause.
-labels: [bug]
+labels: ['type: bug']
 body:
   - type: textarea
     id: description
@@ -48,14 +48,22 @@ body:
     attributes:
       label: Environment
       description: |
-        For older commitizen versions, please include the output of the following commands manually
-      placeholder: |
-        - commitizen version: `cz version`
-        - python version: `python --version`
-        - operating system: `python3 -c "import platform; print(platform.system())"`
+        Please use the following command to retrieve environment information
 
         ```bash
         cz version --report
         ```
+
+        If `cz version --report` is not available, please run the following commands to retrieve your environment information:
+
+        ```bash
+        echo "Commitizen Version: $(cz version)"
+        echo "Python Version: $(python3 -c 'import sys; print(sys.version)')"
+        echo "Operating System: $(python3 -c 'import platform; print(platform.system())')"
+        ```
+      placeholder: |
+        Commitizen Version: 4.0.0
+        Python Version: 3.13.3 (main, Apr  8 2025, 13:54:08) [Clang 16.0.0 (clang-1600.0.26.6)]
+        Operating System: Darwin
     validations:
       required: true
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
index 51d378b747..8fe40136c3 100644
--- a/.github/ISSUE_TEMPLATE/documentation.yml
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -1,7 +1,7 @@
 name: 📖 Documentation
 description: Suggest an improvement for the documentation of this project
 title: Content to be added or fixed
-labels: [documentation]
+labels: ['type: documentation']
 body:
   - type: checkboxes
     id: type
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 7d67eb18af..da3bd5af8a 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,7 +1,7 @@
 name: 🚀 Feature request
 description: Suggest an idea for this project
 title: "<One feature request per issue>"
-labels: [feature]
+labels: ['type: feature']
 body:
   - type: textarea
     id: description
@@ -26,6 +26,6 @@ body:
   - type: textarea
     id: related-issue
     attributes:
-      label: Additional context
+      label: Related issues
       description: |
         If applicable, add link to existing issue also help us know better.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 14bee6b434..e37148c0a1 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -9,12 +9,31 @@ Please fill in the following content to let us know better about this change.
 
 ## Checklist
 
+- [ ] I have read the [contributing guidelines](https://commitizen-tools.github.io/commitizen/contributing/)
+
+### Code Changes
+
 - [ ] Add test cases to all the changes you introduce
-- [ ] Run `./scripts/format` and `./scripts/test` locally to ensure this change passes linter check and test
-- [ ] Test the changes on the local machine manually
+- [ ] Run `poetry all` locally to ensure this change passes linter check and tests
+- [ ] Manually test the changes:
+  - [ ] Verify the feature/bug fix works as expected in real-world scenarios
+  - [ ] Test edge cases and error conditions
+  - [ ] Ensure backward compatibility is maintained
+  - [ ] Document any manual testing steps performed
 - [ ] Update the documentation for the changes
 
-## Expected behavior
+### Documentation Changes
+
+- [ ] Run `poetry doc` locally to ensure the documentation pages renders correctly
+- [ ] Check and fix any broken links (internal or external) in the documentation
+
+> When running `poetry doc`, any broken internal documentation links will be reported in the console output like this:
+>
+> ```text
+> INFO    -  Doc file 'config.md' contains a link 'commands/bump.md#-post_bump_hooks', but the doc 'commands/bump.md' does not contain an anchor '#-post_bump_hooks'.
+> ```
+
+## Expected Behavior
 <!-- A clear and concise description of what you expected to happen -->
 
 
@@ -25,5 +44,5 @@ Please fill in the following content to let us know better about this change.
 3. ... -->
 
 
-## Additional context
+## Additional Context
 <!-- Add any other RELATED ISSUE, context or screenshots about the pull request here. -->
diff --git a/.github/workflows/bumpversion.yml b/.github/workflows/bumpversion.yml
index ed010b4158..ba48d677e0 100644
--- a/.github/workflows/bumpversion.yml
+++ b/.github/workflows/bumpversion.yml
@@ -12,10 +12,10 @@ jobs:
     name: "Bump version and create changelog with commitizen"
     steps:
       - name: Check out
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           fetch-depth: 0
-          token: '${{ secrets.PERSONAL_ACCESS_TOKEN }}'
+          token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
       - name: Create bump and changelog
         uses: commitizen-tools/commitizen-action@master
         with:
diff --git a/.github/workflows/docspublish.yml b/.github/workflows/docspublish.yml
index 6aa1a75e2e..7a2109266f 100644
--- a/.github/workflows/docspublish.yml
+++ b/.github/workflows/docspublish.yml
@@ -4,35 +4,74 @@ on:
   push:
     branches:
       - master
+  workflow_dispatch:
 
 jobs:
+  update-cli-screenshots:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v5
+        with:
+          token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
+          fetch-depth: 0
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+      - name: Install dependencies
+        run: |
+          python -m pip install -U pip poetry poethepoet
+          poetry --version
+          poetry install --only main,script
+      - name: Update CLI screenshots
+        run: |
+          poetry doc:screenshots
+      - name: Commit and push updated CLI screenshots
+        run: |
+          git config --global user.name "github-actions[bot]"
+          git config --global user.email "github-actions[bot]@users.noreply.github.com"
+          git add docs/images/cli_help
+
+          if [[ -n "$(git status --porcelain)" ]]; then
+            git commit -m "docs(cli/screenshots): update CLI screenshots" -m "[skip ci]"
+            git push
+          else
+            echo "No changes to commit. Skipping."
+          fi
+
   publish-documentation:
     runs-on: ubuntu-latest
+    needs: update-cli-screenshots
     steps:
-    - uses: actions/checkout@v4
-      with:
-        token: '${{ secrets.PERSONAL_ACCESS_TOKEN }}'
-        fetch-depth: 0
-    - name: Set up Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-    - name: Install dependencies
-      run: |
-        python -m pip install -U mkdocs mkdocs-material
-    - name: Build docs
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      run: |
-        python -m mkdocs build
-    - name: Generate Sponsors 💖
-      uses: JamesIves/github-sponsors-readme-action@v1
-      with:
-        token: ${{ secrets.PERSONAL_ACCESS_TOKEN_FOR_ORG }}
-        file: 'docs/README.md'
-    - name: Push doc to Github Page
-      uses: peaceiris/actions-gh-pages@v2
-      env:
-        PERSONAL_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
-        PUBLISH_BRANCH: gh-pages
-        PUBLISH_DIR: ./site
+      - uses: actions/checkout@v5
+        with:
+          token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
+          fetch-depth: 0
+      - name: Pull latest changes
+        run: |
+          git pull origin master
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+      - name: Install dependencies
+        run: |
+          python -m pip install -U pip poetry poethepoet
+          poetry --version
+          poetry install --no-root --only documentation
+      - name: Generate Sponsors 💖
+        uses: JamesIves/github-sponsors-readme-action@v1
+        with:
+          token: ${{ secrets.PERSONAL_ACCESS_TOKEN_FOR_ORG }}
+          file: "docs/README.md"
+          organization: true
+      - name: Build docs
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          poetry doc:build
+      - name: Deploy 🚀
+        uses: JamesIves/github-pages-deploy-action@v4
+        with:
+          folder: ./site # The folder the action should deploy.
+          branch: gh-pages
diff --git a/.github/workflows/homebrewpublish.yml b/.github/workflows/homebrewpublish.yml
index a2610229bc..c9a1b35081 100644
--- a/.github/workflows/homebrewpublish.yml
+++ b/.github/workflows/homebrewpublish.yml
@@ -11,22 +11,22 @@ jobs:
     runs-on: macos-latest
     if: ${{ github.event.workflow_run.conclusion == 'success' }}
     steps:
-    - name: Checkout
-      uses: actions/checkout@v4
-    - name: Set up Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-    - name: Install dependencies
-      run: |
-        python -m pip install -U commitizen
-    - name: Set Project version env variable
-      run: |
-        echo "project_version=$(cz version --project)" >> $GITHUB_ENV
-    - name: Update Homebrew formula
-      uses: dawidd6/action-homebrew-bump-formula@v3
-      with:
-        token: ${{secrets.PERSONAL_ACCESS_TOKEN}}
-        formula: commitizen
-        tag: v${{ env.project_version }}
-        force: true
+      - name: Checkout
+        uses: actions/checkout@v5
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+      - name: Install dependencies
+        run: |
+          python -m pip install -U commitizen
+      - name: Set Project version env variable
+        run: |
+          echo "project_version=$(cz version --project)" >> $GITHUB_ENV
+      - name: Update Homebrew formula
+        uses: dawidd6/action-homebrew-bump-formula@v5
+        with:
+          token: ${{secrets.PERSONAL_ACCESS_TOKEN}}
+          formula: commitizen
+          tag: v${{ env.project_version }}
+          force: true
diff --git a/.github/workflows/label_issues.yml b/.github/workflows/label_issues.yml
index 45ca450f89..e74a36c4bd 100644
--- a/.github/workflows/label_issues.yml
+++ b/.github/workflows/label_issues.yml
@@ -15,9 +15,32 @@ jobs:
       - uses: actions/github-script@v7
         with:
           script: |
-            github.rest.issues.addLabels({
+            const issue = await github.rest.issues.get({
               issue_number: context.issue.number,
               owner: context.repo.owner,
               repo: context.repo.repo,
-              labels: ['issue-status: needs-triage']
-            })
+            });
+
+            const body = issue.data.body || '';
+
+            const osLabels = new Set(); // Use a Set to avoid duplicates
+
+            // Parse the "Environment" section, output of `cz version --report`
+            if (body.includes('Operating System: Darwin')) {
+              osLabels.add('os: macOS');
+            }
+
+            if (body.includes('Operating System: Linux')) {
+              osLabels.add('os: Linux');
+            }
+
+            if (body.includes('Operating System: Windows')) {
+              osLabels.add('os: Windows');
+            }
+
+            await github.rest.issues.addLabels({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              labels: ['issue-status: needs-triage', ...osLabels],
+            });
diff --git a/.github/workflows/label_pr.yml b/.github/workflows/label_pr.yml
index b409c8b757..a74a507bba 100644
--- a/.github/workflows/label_pr.yml
+++ b/.github/workflows/label_pr.yml
@@ -9,7 +9,7 @@ jobs:
       pull-requests: write
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
       with:
         sparse-checkout: |
           .github/labeler.yml
@@ -17,3 +17,31 @@ jobs:
     - uses: actions/labeler@v5
       with:
         configuration-path: .github/labeler.yml
+    - name: Label based on PR title
+      uses: actions/github-script@v7
+      with:
+        script: |
+          const title = context.payload.pull_request.title.toLowerCase();
+          const labels = [];
+
+          if (title.includes("fix") || title.includes("bug")) {
+            labels.push("type: bug");
+          }
+          if (title.includes("feat")) {
+            labels.push("type: feature");
+          }
+          if (title.includes("doc")) {
+            labels.push("type: documentation");
+          }
+          if (title.includes("refactor")) {
+            labels.push("type: refactor");
+          }
+
+          if (labels.length > 0) {
+            await github.rest.issues.addLabels({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: context.payload.pull_request.number,
+              labels
+            });
+          }
diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml
index 00ab9a765d..00794babbf 100644
--- a/.github/workflows/pythonpackage.yml
+++ b/.github/workflows/pythonpackage.yml
@@ -1,16 +1,16 @@
 name: Python package
 
-on: [ workflow_dispatch, pull_request ]
+on: [workflow_dispatch, pull_request]
 
 jobs:
   python-check:
     strategy:
       matrix:
-        python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
-        platform: [ ubuntu-20.04, macos-latest, windows-latest ]
+        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
+        platform: [ubuntu-22.04, macos-latest, windows-latest]
     runs-on: ${{ matrix.platform }}
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v5
         with:
           fetch-depth: 0
       - name: Set up Python ${{ matrix.python-version }}
@@ -19,21 +19,20 @@ jobs:
           python-version: ${{ matrix.python-version }}
       - name: Install dependencies
         run: |
-          # pin poetry to 1.3.2 due to https://github.com/python-poetry/poetry/issues/7611
-          python -m pip install -U pip poetry==1.3.2
+          python -m pip install -U pip poetry poethepoet
           poetry --version
-          poetry install
+          poetry install --only main,linters,test
       - name: Run tests and linters
         run: |
           git config --global user.email "action@github.com"
           git config --global user.name "GitHub Action"
-          ./scripts/test
+          poetry ci
         shell: bash
       - name: Upload coverage to Codecov
         if: runner.os == 'Linux'
-        uses: codecov/codecov-action@v3
+        uses: codecov/codecov-action@v5
         with:
-          token: ${{secrets.CODECOV_TOKEN}}
-          file: ./coverage.xml
+          token: ${{ secrets.CODECOV_TOKEN }}
+          files: ./coverage.xml
           flags: unittests
           name: codecov-umbrella
diff --git a/.github/workflows/pythonpublish.yml b/.github/workflows/pythonpublish.yml
index f31691a50b..3976d309dd 100644
--- a/.github/workflows/pythonpublish.yml
+++ b/.github/workflows/pythonpublish.yml
@@ -3,29 +3,26 @@ name: Upload Python Package
 on:
   push:
     tags:
-      - 'v*'
+      - "v*"
 
 jobs:
   deploy:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v5
         with:
-          token: '${{ secrets.PERSONAL_ACCESS_TOKEN }}'
+          token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
           fetch-depth: 0
       - name: Set up Python
         uses: actions/setup-python@v5
         with:
-          python-version: '3.x'
+          python-version: "3.x"
       - name: Install dependencies
         run: |
-          # pin poetry to 1.3.2 due to https://github.com/python-poetry/poetry/issues/7611
-          python -m pip install -U pip poetry==1.3.2 mkdocs mkdocs-material
+          python -m pip install -U pip poetry
           poetry --version
-          poetry install
       - name: Publish
         env:
-          PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
-          PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
-        run: |
-          ./scripts/publish
+          POETRY_HTTP_BASIC_PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
+          POETRY_HTTP_BASIC_PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+        run: poetry publish --build
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index dbe313c5da..9e09fb63e8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,8 +4,7 @@ default_install_hook_types:
   - pre-push
 
 default_stages:
-  - commit
-  - push
+  - pre-commit
 
 repos:
   - repo: meta
@@ -14,13 +13,14 @@ repos:
       - id: check-useless-excludes
 
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.4.0
+    rev: v5.0.0
     hooks:
       - id: check-vcs-permalinks
       - id: end-of-file-fixer
-        exclude: "tests/((commands|data)/|test_).+"
+        exclude: "tests/((commands|data|providers/test_uv_provider)/|test_).+"
       - id: trailing-whitespace
         args: [ --markdown-linebreak-ext=md ]
+        exclude: '\.svg$'
       - id: debug-statements
       - id: no-commit-to-branch
       - id: check-merge-conflict
@@ -30,43 +30,43 @@ repos:
       - id: detect-private-key
 
   - repo: https://github.com/asottile/blacken-docs
-    rev: 1.13.0
+    rev: 1.19.1
     hooks:
       - id: blacken-docs
         additional_dependencies: [ black~=23.11 ]
 
   - repo: https://github.com/codespell-project/codespell
-    rev: v2.2.4
+    rev: v2.4.1
     hooks:
       - id: codespell
         name: Run codespell to check for common misspellings in files
+        # config section is within pyproject.toml
         language: python
         types: [ text ]
-        args: [ "--write-changes", "--ignore-words-list", "asend" ]
-        exclude: "poetry.lock"
+        args: [ "--write-changes" ]
+        additional_dependencies:
+          - tomli
 
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v3.13.0 # automatically updated by Commitizen
+    rev: v4.8.3 # automatically updated by Commitizen
     hooks:
       - id: commitizen
       - id: commitizen-branch
         stages:
           - post-commit
-          - push
 
   - repo: local
     hooks:
       - id: format
-        name: format
+        name: Format
         language: system
         pass_filenames: false
-        entry: ./scripts/format
+        entry: poetry format
         types: [ python ]
 
       - id: linter and test
-        name: linter and test
+        name: Linters
         language: system
         pass_filenames: false
-        stages: [ push ]
-        entry: ./scripts/test
+        entry: poetry lint
         types: [ python ]
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index 619e58622b..2a3a088484 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -1,7 +1,7 @@
 - id: commitizen
   name: commitizen check
   description: >
-    Check whether the current commit message follows commiting rules. Allow
+    Check whether the current commit message follows committing rules. Allow
     empty commit messages by default, because they typically indicate to Git
     that the commit should be aborted.
   entry: cz check
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c071ba9075..9d7a31cc40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,449 @@
+## v4.8.3 (2025-06-09)
+
+### Fix
+
+- **cli**: update description for deprecate warning
+- **commit**: emit deprecated warning of cz commit -s
+- **Check**: make parameters backward compatible
+- **BaseConfig**: mypy error
+- **deprecated**: mark deprecate in v5
+- **defaults**: add non-capitalized default constants back and deprecated warning
+
+### Refactor
+
+- **jira**: refactor message
+- **conventional_commits**: use TypedDict for answers
+- **conventional_commits**: make schema_pattern more readable
+- do not guess if changelog format is provided
+- **check**: compile once and rename variable
+- **questions**: type questions with TypedDict
+- **bump**: simplify nested if
+- **git**: retype get_commits parameter to make it more friendly to call sites
+- **git**: simplify tag logic
+- **bump**: eliminate similar patterns in code
+- **bump**: use any to replace 'or' chain
+- remove unnecessary bool() and remove Any type from TypedDict get
+- **bump**: improve readability and still bypass mypy check
+- **commands**: remove unused args, type version command args
+- **commit**: type commit args
+- **check**: type CheckArgs arguments
+- **check**: remove unused argument
+- **changelog**: type untyped arguments
+- **bump**: TypedDict for bump argument
+- make methods protected, better type
+- **conventional_commits**: remove unnecessary checks
+- fix mypy output and better type
+- **BaseCommitizen**: remove unused process_commit
+- remove `TypeError` handling since `Python >=3.9` is required
+- add comment clarifying `no_raise` parsing to `list[int]`
+- **cli.py**: add type hints
+- **mypy**: remove `unused-ignore`
+- **changelog**: better typing, yield
+- **cli**: early return and improve test coverage
+- **git**: extract _create_commit_cmd_string
+- misc cleanup
+- **bump**: clean up
+- **bump**: add type for out, replace function with re escape
+- **BaseConfig**: use setter
+- **changelog**: minor cleanup
+- **git**: refactor get_tag_names
+- **EOLType**: add eol enum back and reorganize methods
+- **git**: code cleanup and better test coverage
+- **commit**: simplify call
+- **version_scheme**: cleanup
+- improve readability and fix typos
+
+### Perf
+
+- **bump**: avoid unnecessary list construction and rename variable to avoid confusion
+- **tags**: use set
+
+## v4.8.2 (2025-05-22)
+
+### Refactor
+
+- **check**: simplify code
+- **check**: remove unnecessary variable
+
+## v4.8.1 (2025-05-22)
+
+### Refactor
+
+- **customize**: improve code readability
+
+## v4.8.0 (2025-05-20)
+
+### Feat
+
+- **cli**: add --tag-format argument to changelog command
+
+## v4.7.2 (2025-05-18)
+
+### Refactor
+
+- **default**: capitalize all constants and remove unnecessary variable
+
+## v4.7.1 (2025-05-16)
+
+### Fix
+
+- **bump**: don't fail if an invalid version tag is present (fix #1410) (#1418)
+
+## v4.7.0 (2025-05-10)
+
+### Feat
+
+- **providers**: add support for `Cargo.lock`
+
+### Refactor
+
+- **tests**: increase verbosity of variables
+
+## v4.6.3 (2025-05-07)
+
+### Fix
+
+- **changelog.py**: cross-platform path handling using os.path.join and modify the path linter and test parameter
+- **changelog.py**: modify the CHANGELOG.md generated by cz bump --changelog to the right place
+
+## v4.6.2 (2025-05-05)
+
+### Fix
+
+- **docs**: fix url link and table formatting in the customization docs (#1399)
+
+## v4.6.1 (2025-05-05)
+
+### Fix
+
+- **commit**: use os.unlink to remove temp file
+
+## v4.6.0 (2025-04-13)
+
+### Feat
+
+- **changelog**: expose commit parents' digests when processing commits
+- **git**: add parents' digests in commit information
+
+## v4.5.1 (2025-04-09)
+
+### Fix
+
+- print which tag is invalid
+
+## v4.5.0 (2025-04-04)
+
+### Feat
+
+- **init**: set uv to default value if both pyproject.toml and uv.lock present
+
+### Fix
+
+- **commands/init**: add missing uv provider to "cz init"
+
+## v4.4.1 (2025-03-02)
+
+### Fix
+
+- **tags**: fixes ImportError on Python >=3.11 (#1363) (#1364)
+
+## v4.4.0 (2025-03-02)
+
+### Feat
+
+- **tags**: adds `legacy_tag_formats` and `ignored_tag_formats` settings
+
+### Refactor
+
+- **get_tag_regexes**: dedup tag regexes definition
+
+## v4.3.0 (2025-02-28)
+
+### Feat
+
+- **providers**: add uv_provider
+
+## v4.2.2 (2025-02-18)
+
+### Fix
+
+- **bump**: manual version bump if prerelease offset is configured
+
+## v4.2.1 (2025-02-08)
+
+### Fix
+
+- **bump**: add debugging to bump
+
+## v4.2.0 (2025-02-07)
+
+### Feat
+
+- draft of the --empty parameter
+
+### Refactor
+
+- **bump**: rename --empty as --allow-no-commit
+
+## v4.1.1 (2025-01-26)
+
+### Fix
+
+- **get-next-bump**: add a test case
+- **get-next-bump**: fix to permit usage of --get-next options even when update_changelog_on_bump is set to true
+
+## v4.1.0 (2024-12-06)
+
+### Feat
+
+- **commit**: allow '-- --allow-empty' to create empty commits
+
+## v4.0.0 (2024-11-26)
+
+## v3.31.0 (2024-11-16)
+
+### Feat
+
+- **commitizen**: document '--' double dash in '--help'
+
+### Fix
+
+- **commit**: avoid warnings with 'always_signoff' configuration
+- **commit**: resolve 'always_signoff' configuration and '-s' CLI issues
+
+## v3.30.1 (2024-11-10)
+
+### Refactor
+
+- **cli**: replace magic number 0 with ExitCode.EXPECTED_EXIT
+- **defaults**: disallow style as None
+- **cz_customize**: return empty string for info, example, schema and schema_pattern if not provided
+
+## v3.30.0 (2024-10-23)
+
+### Feat
+
+- **commands/commit**: add force-edit functionality after answering questions
+
+### Refactor
+
+- remove redundant return None
+
+## v3.29.1 (2024-09-26)
+
+### Fix
+
+- **changelog**: Factorized TAG_FORMAT_REGEXES
+- **changelog**: Handle tag format without version pattern
+- **changelog**: handle custom tag_format in changelog generation
+
+### Refactor
+
+- Use format strings
+
+## v3.29.0 (2024-08-11)
+
+### Feat
+
+- **bump**: add functionality to write the next version to stdout
+
+## v3.28.0 (2024-07-17)
+
+### Feat
+
+- add argument to limit length of commit message in checks
+
+## v3.27.0 (2024-05-22)
+
+### Feat
+
+- **config_files**: add support for "cz.toml" config file
+
+## v3.26.2 (2024-05-22)
+
+### Fix
+
+- **base.py**: add encoding when open changlelog_file
+
+## v3.26.1 (2024-05-22)
+
+### Fix
+
+- **cli/commands**: add description for subcommands
+
+### Refactor
+
+- **KNOWN_SCHEMES**: replace set comprehension for list comprehension
+- **tests/commands**: move "other" tests for the correspondent file
+
+## v3.26.0 (2024-05-18)
+
+### Feat
+
+- **ci/cd**: automates the generation of CLI screenshots
+
+## v3.25.1 (2024-05-15)
+
+### Refactor
+
+- strip possessive from note about ci option
+
+## v3.25.0 (2024-04-30)
+
+### Feat
+
+- add an argument to limit the length of commit message
+
+### Fix
+
+- strip the commit message for calculating length
+- resolve test error by removing defaults
+
+### Refactor
+
+- **commands/commit**: replace comparison with chained comparison
+- check the length in Commit instead of Commitizen
+
+## v3.24.0 (2024-04-18)
+
+### Feat
+
+- **schemes**: adds support for SemVer 2.0 (dot in pre-releases) (fix #1025) (#1072)
+
+## v3.23.0 (2024-04-18)
+
+### Feat
+
+- **bump**: `version_files` now support glob patterns (fix #1067) (#1070)
+
+## v3.22.0 (2024-04-11)
+
+### Feat
+
+- **cli**: add config option to specify config file path
+
+## v3.21.3 (2024-03-30)
+
+### Refactor
+
+- **defaults**: move cz_conventional_commit defaults out of defaults.py
+
+## v3.21.2 (2024-03-30)
+
+### Fix
+
+- **commitizen/git.py,-tests/test_git.py**: Resolve tempfile path spaces issue in git commit function
+
+## v3.21.1 (2024-03-30)
+
+### Fix
+
+- **command-init**: "cz init" should list existing tag in reverse order
+
+## v3.21.0 (2024-03-30)
+
+### Feat
+
+- **commit**: add retry_after_failure config option and --no-retry flag
+
+### Refactor
+
+- **utils**: convert git project root to posix path for backup file name
+- **commit**: use Optional[str] instead of str | None
+- **commit**: remove unused tempfile import
+- **git-hooks**: make git hooks use get_backup_file_path
+- **utils**: move backup path creation to utils
+
+## v3.20.0 (2024-03-19)
+
+### Feat
+
+- **changelog**: expose commits `sha1`, `author` and `author_email` in changelog tree (fix #987) (#1013)
+
+## v3.19.0 (2024-03-19)
+
+### Feat
+
+- **changelog**: adds a `changelog_release_hook` called for each release in the changelog (#1018)
+
+## v3.18.4 (2024-03-14)
+
+### Fix
+
+- **changelog**: include latest change when dry run and incremental
+
+## v3.18.3 (2024-03-11)
+
+### Fix
+
+- **warnings**: all warnings should go to `stdout`
+
+## v3.18.2 (2024-03-11)
+
+### Fix
+
+- **git**: force the default git locale on methods relying on parsing the output (#1012)
+
+## v3.18.1 (2024-03-11)
+
+### Fix
+
+- **changelog**: changelog hook was not called on dry run
+
+## v3.18.0 (2024-03-07)
+
+### Feat
+
+- **changelog**: `changelog_message_build_hook` can now generate multiple changelog entries from a single commit (#1003)
+
+## v3.17.2 (2024-03-07)
+
+### Fix
+
+- **changelog**: ensure `changelog_message_builder_hook` can access and modify `change_type` (#1002)
+
+## v3.17.1 (2024-03-07)
+
+### Fix
+
+- **bump**: pre and post bump hooks were failing when an increment was provided (fix #1004)
+
+## v3.17.0 (2024-03-06)
+
+### Feat
+
+- **changelog**: `changelog_message_build_hook` can remove message by returning a falsy value
+
+## v3.16.0 (2024-02-26)
+
+### Feat
+
+- **commands**: add bump --exact
+
+### Fix
+
+- **bump**: change --exact-increment to --increment-mode
+- **bump**:  only get and validate commits if increment is not provided
+-  Improve type annotations
+
+## v3.15.0 (2024-02-17)
+
+### Feat
+
+- **bump**: functionality to add build-metadata to version string
+
+## v3.14.1 (2024-02-04)
+
+### Fix
+
+- **bump**: remove unused method
+- **scm**: only search tags that are reachable by the current commit
+
+## v3.14.0 (2024-02-01)
+
+### Feat
+
+- properly bump versions between prereleases (#799)
 
 ## v3.13.0 (2023-12-03)
 
@@ -25,17 +471,12 @@
 - **formats**: expose some new customizable changelog formats on the `commitizen.changelog_format` endpoint (Textile, AsciiDoc and RestructuredText)
 - **template**: add `changelog --export-template` command
 - **template**: allow to override the template from cli, configuration and plugins
+- **cli.py**: Added support for extra git CLI args after -- separator for `cz commit` command
 
 ### Fix
 
 - **filename**: ensure `file_name` can be passed to `changelog` from `bump` command
 
-## v3.11.0 (2023-10-17)
-
-### Feat
-
-- **cli.py**: Added support for extra git CLI args after -- separator for `cz commit` command
-
 ### Refactor
 
 - **git.py**: Removed 'extra_args' from git.commit
@@ -243,6 +684,7 @@
 ### BREAKING CHANGE
 
 - Plugins are now exposed as `commitizen.plugin` entrypoints
+- Python 3.6 is not officially supported anymore. Please migrate from 3.6 to 3.7 or greater.
 
 ### Feat
 
@@ -430,16 +872,16 @@
 
 ## v2.31.0 (2022-08-14)
 
+### Feat
+
+- new file
+
 ### Fix
 
 - **pyproject.toml**: remove test added configurations
 - **changelog**: use defaults.change_type_order in conventional commit
 - capitalize types in default change_type_order
 
-### Feat
-
-- new file
-
 ## v2.30.0 (2022-08-14)
 
 ### Feat
@@ -544,14 +986,14 @@
 
 ## v2.24.0 (2022-04-15)
 
-### Fix
-
-- change error code for NoneIncrementExit
-
 ### Feat
 
 - add --no-raise to avoid raising error codes
 
+### Fix
+
+- change error code for NoneIncrementExit
+
 ## v2.23.0 (2022-03-29)
 
 ### Feat
@@ -560,15 +1002,15 @@
 
 ## v2.22.0 (2022-03-29)
 
+### Feat
+
+- **changelog**: add support for single version and version range
+
 ### Refactor
 
 - speed up testing and wait for tags
 - **git**: use date as a function in GitTag to easily patch
 
-### Feat
-
-- **changelog**: add support for single version and version range
-
 ## v2.21.2 (2022-02-22)
 
 ### Fix
@@ -587,32 +1029,29 @@
 
 ### Feat
 
-- skip merge messages that start with Pull request
 - skip merge messages that start with Pull request
 
 ## v2.20.5 (2022-02-07)
 
-### Refactor
-
-- iter_modules only accepts str
-
 ### Fix
 
 - Ignore packages that are not plugins
-- Ignore packages that are not plugins
+
+### Refactor
+
+- iter_modules only accepts str
 
 ## v2.20.4 (2022-01-17)
 
 ### Fix
 
-- **bump**: raise non zero error code when there's no eligible commit to bump
 - **bump**: raise non zero error code when there's no eligible commit to bump
 
 ## v2.20.3 (2021-12-20)
 
 ### Fix
 
-- **check**: filter out comment messege when checking
+- **check**: filter out comment message when checking
 
 ## v2.20.2 (2021-12-14)
 
@@ -622,6 +1061,10 @@
 
 ## v2.20.1 (2021-12-14)
 
+### Fix
+
+- import TypedDict from type_extensions for backward compatibility
+
 ### Refactor
 
 - **conventional_commits**: remove duplicate patterns and import from defaults
@@ -629,10 +1072,6 @@
 - **defaults**: add Settings typeddict
 - **defaults**: move bump_map, bump_pattern, commit_parser from defaults to ConventionalCommitsCz
 
-### Fix
-
-- import TypedDict from type_extensions for backward compatibility
-
 ## v2.20.0 (2021-10-06)
 
 ### Feat
@@ -660,14 +1099,14 @@
 
 ## v2.18.0 (2021-08-13)
 
-### Refactor
-
-- **shortcuts**: move check for shortcut config setting to apply to any list select
-
 ### Feat
 
 - **prompt**: add keyboard shortcuts with config option
 
+### Refactor
+
+- **shortcuts**: move check for shortcut config setting to apply to any list select
+
 ## v2.17.13 (2021-07-14)
 
 ## v2.17.12 (2021-07-06)
@@ -796,16 +1235,16 @@
 
 ## v2.13.0 (2021-01-01)
 
+### Feat
+
+- **#319**: add optional change_type_order
+
 ### Refactor
 
 - raise an InvalidConfigurationError
 - **#323**: address PR feedback
 - move expected COMMITS_TREE to global
 
-### Feat
-
-- **#319**: add optional change_type_order
-
 ## v2.12.1 (2020-12-30)
 
 ### Fix
@@ -829,10 +1268,12 @@
 ### Feat
 
 - add yaml as a config option
+- **config**: add support for the new class YAMLConfig at the root of the confi internal package
+- **init**: add support for yaml config file at init
 
-### feat
+### Fix
 
-- **config**: add support for the new class YAMLConfig at the root of the confi internal package
+- **YAMLConfig**: add a TypeError exception to handle in _parse_settings method
 
 ## v2.10.0 (2020-12-02)
 
@@ -840,17 +1281,17 @@
 
 - **commitizen/cli**: add the integration with argcomplete
 
-## v2.9.0 (2020-12-02)
-
-### Fix
-
-- **json_config**: fix the emtpy_config_content method
+## v2.9.0 (2020-12-02)
 
 ### Feat
 
 - **Init**: add the json config support as an option at Init
 - **commitizen/config/json_config**: add json support for configuration
 
+### Fix
+
+- **json_config**: fix the emtpy_config_content method
+
 ## v2.8.2 (2020-11-21)
 
 ### Fix
@@ -861,7 +1302,7 @@
 
 ### Fix
 
-- prevent prerelase from creating a bump when there are no commits
+- prevent prerelease from creating a bump when there are no commits
 
 ## v2.8.0 (2020-11-15)
 
@@ -918,15 +1359,15 @@
 
 ## v2.3.0 (2020-09-03)
 
+### Feat
+
+- **cli**: rewrite cli instructions to be more succinct about what they require
+
 ### Fix
 
 - **cli**: add guideline for subject input
 - **cli**: wrap the word enter with brackets
 
-### Feat
-
-- **cli**: rewrite cli instructions to be more succinct about what they require
-
 ## v2.2.0 (2020-08-31)
 
 ### Feat
@@ -935,15 +1376,15 @@
 
 ## v2.1.0 (2020-08-06)
 
-### Refactor
-
-- **cz_check**: Refactor _get_commits to return GitCommit instead of dict
-
 ### Feat
 
 - **cz_check**: Add rev to all displayed ill-formatted commits
 - **cz_check**: Update to show all ill-formatted commits
 
+### Refactor
+
+- **cz_check**: Refactor _get_commits to return GitCommit instead of dict
+
 ## v2.0.2 (2020-08-03)
 
 ### Fix
@@ -959,27 +1400,28 @@
 
 ## v2.0.0 (2020-07-26)
 
-### Fix
+### BREAKING CHANGE
 
-- add missing `pyyaml` dependency
-- **cli**: make command required for commitizen
+- setup.cfg, .cz and .cz.cfg are no longer supported
+- Use "cz version" instead
+- "cz --debug" will no longer work
+ #47
 
 ### Feat
 
 - **init**: enable setting up pre-commit hook through "cz init"
 
+### Fix
+
+- add missing `pyyaml` dependency
+- **cli**: make command required for commitizen
+
 ### Refactor
 
 - **config**: drop "files" configure support. Please use "version_files" instead
 - **config**: remove ini configuration support
 - **cli**: remove "--version" argument
 
-### BREAKING CHANGE
-
-- setup.cfg, .cz and .cz.cfg are no longer supported
-- Use "cz version" instead
-- "cz --debug" will no longer work
-
 ## v1.25.0 (2020-07-26)
 
 ### Feat
@@ -1023,6 +1465,10 @@
 
 ## v1.23.0 (2020-06-14)
 
+### Feat
+
+- **cli**: enable displaying all traceback for CommitizenException when --debug flag is used
+
 ### Refactor
 
 - **exception**: rename MissingConfigError as MissingCzCustomizeConfigError
@@ -1035,10 +1481,6 @@
 - use custom exception for error handling
 - **error_codes**: remove unused NO_COMMIT_MSG error code
 
-### Feat
-
-- **cli**: enable displaying all traceback for CommitizenException when --debug flag is used
-
 ## v1.22.3 (2020-06-10)
 
 ## v1.22.2 (2020-05-29)
@@ -1055,16 +1497,16 @@
 
 ## v1.22.0 (2020-05-13)
 
-### Fix
-
-- **changelog**: rename `message_hook` -> `changelog_message_builder_hook`
-
 ### Feat
 
 - **changelog**: add support for `changelog_hook` when changelog finishes the generation
 - **changelog**: add support for `message_hook` method
 - **changelog**: add support for modifying the change_type in the title of the changelog
 
+### Fix
+
+- **changelog**: rename `message_hook` -> `changelog_message_builder_hook`
+
 ## v1.21.0 (2020-05-09)
 
 ### Feat
@@ -1102,27 +1544,31 @@
 
 ## v1.19.0 (2020-05-02)
 
-### Fix
-
-- **git**: missing dependency removed
-- **changelog**: check get_metadata for existing changelog file
-
 ### Feat
 
 - **changelog**: add support for any commit rule system
 - **changelog**: add incremental flag
+- **commands/changelog**: make changelog_file an option in config
+- **commands/changelog**: exit when there is no commit exists
+- **commands/changelog**: add --start-rev argument to `cz changelog`
+- **changelog**: generate changelog based on git log
+- **commands/changelog**: generate changelog_tree from all past commits
+- **cz/conventinal_commits**: add changelog_map, changelog_pattern and implement process_commit
+- **cz/base**: add default process_commit for processing commit message
+- **changelog**: changelog tree generation from markdown
 
-## v1.18.3 (2020-04-22)
-
-### Refactor
-
-- **commands/init**: fix typo
+### Fix
 
-## v1.18.2 (2020-04-22)
+- **git**: missing dependency removed
+- **changelog**: check get_metadata for existing changelog file
+- **cz/conventional_commits**: fix schema_pattern break due to rebase
+- **changelog_template**: fix list format
+- **commitizen/cz**: set changelog_map, changelog_pattern to none as default
+- **commands/changelog**: remove --skip-merge argument
+- **cli**: add changelog arguments
 
 ### Refactor
 
-- **git**: replace GitCommit.message code with one-liner
 - **changelog**: use functions from changelog.py
 - **changelog**: rename category to change_type to fit 'keep a changelog'
 - **templates**: rename as "keep_a_changelog_template.j2"
@@ -1133,25 +1579,21 @@
 - **commands/changelog**: remove redundant if statement
 - **commands/changelog**: use jinja2 template instead of string concatenation to build changelog
 
+## v1.18.3 (2020-04-22)
+
+### Refactor
+
+- **commands/init**: fix typo
+
+## v1.18.2 (2020-04-22)
+
 ### Fix
 
 - **git**: fix returned value for GitCommit.message when body is empty
-- **cz/conventional_commits**: fix schema_pattern break due to rebase
-- **changelog_template**: fix list format
-- **commitizen/cz**: set changelog_map, changelog_pattern to none as default
-- **commands/changelog**: remove --skip-merge argument
-- **cli**: add changelog arguments
 
-### Feat
+### Refactor
 
-- **commands/changelog**: make changelog_file an option in config
-- **commands/changelog**: exit when there is no commit exists
-- **commands/changelog**: add --start-rev argument to `cz changelog`
-- **changelog**: generate changelog based on git log
-- **commands/changelog**: generate changelog_tree from all past commits
-- **cz/conventinal_commits**: add changelog_map, changelog_pattern and implement process_commit
-- **cz/base**: add default process_commit for processing commit message
-- **changelog**: changelog tree generation from markdown
+- **git**: replace GitCommit.message code with one-liner
 
 ## v1.18.1 (2020-04-16)
 
@@ -1161,6 +1603,14 @@
 
 ## v1.18.0 (2020-04-13)
 
+### Feat
+
+- **bump**: support for ! as BREAKING change in commit message
+
+### Fix
+
+- **cz/customize**: add error handling when customize detail is not set
+
 ### Refactor
 
 - **cz/customize**: remove unused mypy ignore
@@ -1172,14 +1622,6 @@
 - **cz**: add type annotation for each function in cz
 - **config**: fix mypy warning for _conf
 
-### Fix
-
-- **cz/customize**: add error handling when customize detail is not set
-
-### Feat
-
-- **bump**: support for ! as BREAKING change in commit message
-
 ## v1.17.1 (2020-03-24)
 
 ### Fix
@@ -1192,19 +1634,19 @@
 
 ## v1.17.0 (2020-03-15)
 
-### Refactor
+### Feat
 
-- **tests/bump**: use parameterize to group similliar tests
-- **cz/connventional_commit**: use \S to check scope
-- **git**: remove unnecessary dot between git range
+- **commands/check**: add --rev-range argument for checking commits within some range
 
 ### Fix
 
 - **bump**: fix bump find_increment error
 
-### Feat
+### Refactor
 
-- **commands/check**: add --rev-range argument for checking commits within some range
+- **cz/connventional_commit**: use \S to check scope
+- **git**: remove unnecessary dot between git range
+- **tests/bump**: use parameterize to group similliar tests
 
 ## v1.16.4 (2020-03-03)
 
@@ -1240,6 +1682,10 @@
 
 ## v1.16.0 (2020-01-21)
 
+### Feat
+
+- **git**: get_commits default from first_commit
+
 ### Refactor
 
 - **commands/bump**: rename parameter into bump_setting to distinguish bump_setting and argument
@@ -1249,37 +1695,39 @@
 - **git**: make arguments other then start and end in get_commit keyword arguments
 - **git**: Change get_commits into returning commits instead of lines of messages
 
-### Feat
-
-- **git**: get_commits default from first_commit
-
 ## v1.15.1 (2020-01-20)
 
-## v1.15.0 (2020-01-20)
+### Fix
+
+- **cli**: fix --version not functional
 
 ### Refactor
 
 - **tests/commands/bump**: use tmp_dir to replace self implemented tmp dir behavior
-- **git**: make find_git_project_root return None if it's not a git project
-- **config/base_config**: make set_key not implemented
-- **error_codes**: move all the error_codes to a module
-- **config**: replace string type path with pathlib.Path
 - **test_bump_command**: rename camel case variables
 - **tests/commands/check**: use pytest fixture tmpdir replace self implemented contextmanager
 - **test/commands/other**: replace unit test style mock with mocker fixture
 - **tests/commands**: separate command unit tests into modules
 - **tests/commands**: make commands related tests a module
 
-### Fix
-
-- **git**: remove breakline in the return value of find_git_project_root
-- **cli**: fix --version not functional
+## v1.15.0 (2020-01-20)
 
 ### Feat
 
 - **config**: look up configuration in git project root
 - **git**: add find_git_project_root
 
+### Fix
+
+- **git**: remove breakline in the return value of find_git_project_root
+
+### Refactor
+
+- **git**: make find_git_project_root return None if it's not a git project
+- **config/base_config**: make set_key not implemented
+- **error_codes**: move all the error_codes to a module
+- **config**: replace string type path with pathlib.Path
+
 ## v1.14.2 (2020-01-14)
 
 ### Fix
@@ -1288,20 +1736,20 @@
 
 ## v1.14.1 (2020-01-11)
 
-## v1.14.0 (2020-01-06)
+### Fix
 
-### Refactor
+- **cli**: fix the way default handled for name argument
+- **cli**: fix name cannot be overwritten through config in newly refactored config design
 
-- **pre-commit-hooks**: add metadata for the check hook
+## v1.14.0 (2020-01-06)
 
 ### Feat
 
 - **pre-commit-hooks**: add pre-commit hook
 
-### Fix
+### Refactor
 
-- **cli**: fix the way default handled for name argument
-- **cli**: fix name cannot be overwritten through config in newly refactored config design
+- **pre-commit-hooks**: add metadata for the check hook
 
 ## v1.13.1 (2019-12-31)
 
@@ -1331,11 +1779,17 @@
 
 ## v1.10.2 (2019-12-27)
 
+### Fix
+
+- **config**: handle empty config file
+- **config**: fix load global_conf even if it doesn't exist
+- **config/ini_config**: replace outdated _parse_ini_settings with _parse_settings
+
 ### Refactor
 
 - new config system where each config type has its own class
 - **config**: add type annotation to config property
-- **config**: fix wrongly type annoated functions
+- **config**: fix wrongly type annotated functions
 - **config/ini_config**: move deprecation warning into class initialization
 - **config**: use add_path instead of directly assigning _path
 - **all**: replace all the _settings invoke with settings.update
@@ -1343,12 +1797,6 @@
 - **config**: move default settings back to defaults
 - **config**: Make config a class and each type of config (e.g., toml, ini) a child class
 
-### Fix
-
-- **config**: handle empty config file
-- **config**: fix load global_conf even if it doesn't exist
-- **config/ini_config**: replace outdated _parse_ini_settings with _parse_settings
-
 ## v1.10.1 (2019-12-10)
 
 ### Fix
@@ -1383,12 +1831,20 @@
 - **config**: add deprecation warning for loading config from ini files
 - **cz/customize**: add jinja support to enhance template flexibility
 - **cz/filters**: add required_validator and multiple_line_breaker
+- **Commands/commit**: add ´--dry-run´ flag to the Commit command
 - **cz/cz_customize**: implement info to support info and info_path
 - **cz/cz_customize**: enable bump_pattern bump_map customization
 - **cz/cz_customize**: implement customizable cz
-- **Commands/commit**: add ´--dry-run´ flag to the Commit command
 - new 'git-cz' entrypoint
 
+### Fix
+
+- commit dry-run doesn't require staging to be clean
+- **scripts**: add back the delete poetry prefix
+- correct typo to spell "convention"
+- removing folder in windows throwing a PermissionError
+- **test_cli**: testing the version command
+
 ### Refactor
 
 - **config**: remove has_pyproject which is no longer used
@@ -1396,15 +1852,13 @@
 - **cz/utils**: rename filters as utils
 - **cli**: add back --version and remove subcommand required constraint
 
-### Fix
+## v1.8.0 (2019-11-12)
 
-- commit dry-run doesn't require staging to be clean
-- correct typo to spell "convention"
-- removing folder in windows throwing a PermissionError
-- **scripts**: add back the delete poetry prefix
-- **test_cli**: testing the version command
+### Feat
 
-## v1.8.0 (2019-11-12)
+- **cz**: add a base exception for cz customization
+- **commands/commit**: abort commit if there is nothing to commit
+- **git**: add is_staging_clean to check if there is any file in git staging
 
 ### Fix
 
@@ -1418,13 +1872,13 @@
 - **command/version**: use out.write instead of out.line
 - **command**: make version a command instead of an argument
 
-### Feat
+## v1.7.0 (2019-11-08)
 
-- **cz**: add a base exception for cz customization
-- **commands/commit**: abort commit if there is nothing to commit
-- **git**: add is_staging_clean to check if there is any file in git staging
+### Feat
 
-## v1.7.0 (2019-11-08)
+- **config**: update style instead of overwrite
+- **config**: parse style in config
+- **commit**: make style configurable for commit command
 
 ### Fix
 
@@ -1436,12 +1890,6 @@
 
 - **cz**: change the color of default style
 
-### Feat
-
-- **config**: update style instead of overwrite
-- **config**: parse style in config
-- **commit**: make style configurable for commit command
-
 ## v1.6.0 (2019-11-05)
 
 ### Feat
@@ -1462,14 +1910,14 @@
 
 ## v1.4.0 (2019-04-26)
 
-### Fix
-
-- **bump**: handle commit and create tag failure
-
 ### Feat
 
 - added argument yes to bump in order to accept questions
 
+### Fix
+
+- **bump**: handle commit and create tag failure
+
 ## v1.3.0 (2019-04-24)
 
 ### Feat
@@ -1490,6 +1938,11 @@
 
 ## v1.1.1 (2019-04-18)
 
+### Fix
+
+- **bump**: commit message now fits better with semver
+- conventional commit 'breaking change' in body instead of title
+
 ### Refactor
 
 - changed stdout statements
@@ -1498,11 +1951,6 @@
 - **example**: command logic removed from commitizen base
 - **commit**: moved most of the commit logic to the commit command
 
-### Fix
-
-- **bump**: commit message now fits better with semver
-- conventional commit 'breaking change' in body instead of title
-
 ## v1.1.0 (2019-04-14)
 
 ### Feat
@@ -1569,14 +2017,14 @@
 
 ## v0.9.6 (2018-09-19)
 
-### Refactor
-
-- **conventionalCommit**: moved filters to questions instead of message
-
 ### Fix
 
 - **manifest**: included missing files
 
+### Refactor
+
+- **conventionalCommit**: moved filters to questions instead of message
+
 ## v0.9.5 (2018-08-24)
 
 ### Fix
diff --git a/commitizen/__init__.py b/commitizen/__init__.py
index f16def4441..6db9e6e7db 100644
--- a/commitizen/__init__.py
+++ b/commitizen/__init__.py
@@ -1,7 +1,7 @@
 import logging
 import logging.config
 
-from colorama import init  # type: ignore
+from colorama import init
 
 from commitizen.cz.base import BaseCommitizen
 
diff --git a/commitizen/__version__.py b/commitizen/__version__.py
index 62ee17b836..af82c57223 100644
--- a/commitizen/__version__.py
+++ b/commitizen/__version__.py
@@ -1 +1 @@
-__version__ = "3.13.0"
+__version__ = "4.8.3"
diff --git a/commitizen/bump.py b/commitizen/bump.py
index 74d08381dc..6d6b6dc069 100644
--- a/commitizen/bump.py
+++ b/commitizen/bump.py
@@ -3,19 +3,25 @@
 import os
 import re
 from collections import OrderedDict
+from collections.abc import Iterable
+from glob import iglob
+from logging import getLogger
 from string import Template
+from typing import cast
 
-from commitizen.defaults import MAJOR, MINOR, PATCH, bump_message, encoding
+from commitizen.defaults import BUMP_MESSAGE, ENCODING, MAJOR, MINOR, PATCH
 from commitizen.exceptions import CurrentVersionNotFoundError
 from commitizen.git import GitCommit, smart_open
-from commitizen.version_schemes import DEFAULT_SCHEME, Version, VersionScheme
+from commitizen.version_schemes import Increment, Version
 
 VERSION_TYPES = [None, PATCH, MINOR, MAJOR]
 
+logger = getLogger("commitizen")
+
 
 def find_increment(
     commits: list[GitCommit], regex: str, increments_map: dict | OrderedDict
-) -> str | None:
+) -> Increment | None:
     if isinstance(increments_map, dict):
         increments_map = OrderedDict(increments_map)
 
@@ -36,39 +42,44 @@ def find_increment(
                         new_increment = increments_map[match_pattern]
                         break
 
+                if new_increment is None:
+                    logger.debug(
+                        f"no increment needed for '{found_keyword}' in '{message}'"
+                    )
+
                 if VERSION_TYPES.index(increment) < VERSION_TYPES.index(new_increment):
+                    logger.debug(
+                        f"increment detected is '{new_increment}' due to '{found_keyword}' in '{message}'"
+                    )
                     increment = new_increment
 
                 if increment == MAJOR:
                     break
 
-    return increment
+    return cast(Increment, increment)
 
 
 def update_version_in_files(
     current_version: str,
     new_version: str,
-    files: list[str],
+    files: Iterable[str],
     *,
     check_consistency: bool = False,
-    encoding: str = encoding,
-) -> None:
+    encoding: str = ENCODING,
+) -> list[str]:
     """Change old version to the new one in every file given.
 
     Note that this version is not the tag formatted one.
     So for example, your tag could look like `v1.0.0` while your version in
     the package like `1.0.0`.
+
+    Returns the list of updated files.
     """
     # TODO: separate check step and write step
-    for location in files:
-        drive, tail = os.path.splitdrive(location)
-        path, _, regex = tail.partition(":")
-        filepath = drive + path
-        if not regex:
-            regex = _version_to_regex(current_version)
-
+    updated = []
+    for path, regex in _files_and_regexes(files, current_version):
         current_version_found, version_file = _bump_with_regex(
-            filepath,
+            path,
             current_version,
             new_version,
             regex,
@@ -77,14 +88,34 @@ def update_version_in_files(
 
         if check_consistency and not current_version_found:
             raise CurrentVersionNotFoundError(
-                f"Current version {current_version} is not found in {location}.\n"
+                f"Current version {current_version} is not found in {path}.\n"
                 "The version defined in commitizen configuration and the ones in "
                 "version_files are possibly inconsistent."
             )
 
         # Write the file out again
-        with smart_open(filepath, "w", encoding=encoding) as file:
+        with smart_open(path, "w", encoding=encoding) as file:
             file.write(version_file)
+        updated.append(path)
+    return updated
+
+
+def _files_and_regexes(patterns: Iterable[str], version: str) -> list[tuple[str, str]]:
+    """
+    Resolve all distinct files with their regexp from a list of glob patterns with optional regexp
+    """
+    out: set[tuple[str, str]] = set()
+    for pattern in patterns:
+        drive, tail = os.path.splitdrive(pattern)
+        path, _, regex = tail.partition(":")
+        filepath = drive + path
+        if not regex:
+            regex = re.escape(version)
+
+        for file in iglob(filepath):
+            out.add((file, regex))
+
+    return sorted(out)
 
 
 def _bump_with_regex(
@@ -92,53 +123,23 @@ def _bump_with_regex(
     current_version: str,
     new_version: str,
     regex: str,
-    encoding: str = encoding,
+    encoding: str = ENCODING,
 ) -> tuple[bool, str]:
     current_version_found = False
     lines = []
     pattern = re.compile(regex)
     with open(version_filepath, encoding=encoding) as f:
         for line in f:
-            if pattern.search(line):
-                bumped_line = line.replace(current_version, new_version)
-                if bumped_line != line:
-                    current_version_found = True
-                lines.append(bumped_line)
-            else:
+            if not pattern.search(line):
                 lines.append(line)
-    return current_version_found, "".join(lines)
+                continue
 
+            bumped_line = line.replace(current_version, new_version)
+            if bumped_line != line:
+                current_version_found = True
+            lines.append(bumped_line)
 
-def _version_to_regex(version: str) -> str:
-    return version.replace(".", r"\.").replace("+", r"\+")
-
-
-def normalize_tag(
-    version: Version | str,
-    tag_format: str,
-    scheme: VersionScheme | None = None,
-) -> str:
-    """The tag and the software version might be different.
-
-    That's why this function exists.
-
-    Example:
-    | tag | version (PEP 0440) |
-    | --- | ------- |
-    | v0.9.0 | 0.9.0 |
-    | ver1.0.0 | 1.0.0 |
-    | ver1.0.0.a0 | 1.0.0a0 |
-    """
-    scheme = scheme or DEFAULT_SCHEME
-    version = scheme(version) if isinstance(version, str) else version
-
-    major, minor, patch = version.release
-    prerelease = version.prerelease or ""
-
-    t = Template(tag_format)
-    return t.safe_substitute(
-        version=version, major=major, minor=minor, patch=patch, prerelease=prerelease
-    )
+    return current_version_found, "".join(lines)
 
 
 def create_commit_message(
@@ -147,6 +148,6 @@ def create_commit_message(
     message_template: str | None = None,
 ) -> str:
     if message_template is None:
-        message_template = bump_message
+        message_template = BUMP_MESSAGE
     t = Template(message_template)
     return t.safe_substitute(current_version=current_version, new_version=new_version)
diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index cd9e76a320..ba6fbbc6b3 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -24,13 +24,15 @@
 - [x] hook after changelog is generated (api calls)
 - [x] add support for change_type maps
 """
+
 from __future__ import annotations
 
 import re
 from collections import OrderedDict, defaultdict
+from collections.abc import Generator, Iterable, Mapping, Sequence
 from dataclasses import dataclass
 from datetime import date
-from typing import TYPE_CHECKING, Callable, Iterable
+from typing import TYPE_CHECKING, Any
 
 from jinja2 import (
     BaseLoader,
@@ -40,18 +42,13 @@
     Template,
 )
 
-from commitizen import out
-from commitizen.bump import normalize_tag
+from commitizen.cz.base import ChangelogReleaseHook
 from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError
 from commitizen.git import GitCommit, GitTag
-from commitizen.version_schemes import (
-    DEFAULT_SCHEME,
-    BaseVersion,
-    InvalidVersion,
-)
+from commitizen.tags import TagRules
 
 if TYPE_CHECKING:
-    from commitizen.version_schemes import VersionScheme
+    from commitizen.cz.base import MessageBuilderHook
 
 
 @dataclass
@@ -64,45 +61,19 @@ class Metadata:
     unreleased_end: int | None = None
     latest_version: str | None = None
     latest_version_position: int | None = None
+    latest_version_tag: str | None = None
+
+    def __post_init__(self) -> None:
+        if self.latest_version and not self.latest_version_tag:
+            # Test syntactic sugar
+            # latest version tag is optional if same as latest version
+            self.latest_version_tag = self.latest_version
 
 
 def get_commit_tag(commit: GitCommit, tags: list[GitTag]) -> GitTag | None:
     return next((tag for tag in tags if tag.rev == commit.rev), None)
 
 
-def tag_included_in_changelog(
-    tag: GitTag,
-    used_tags: list,
-    merge_prerelease: bool,
-    scheme: VersionScheme = DEFAULT_SCHEME,
-) -> bool:
-    if tag in used_tags:
-        return False
-
-    try:
-        version = scheme(tag.name)
-    except InvalidVersion:
-        return False
-
-    if merge_prerelease and version.is_prerelease:
-        return False
-
-    return True
-
-
-def get_version_tags(scheme: type[BaseVersion], tags: list[GitTag]) -> list[GitTag]:
-    valid_tags: list[GitTag] = []
-    for tag in tags:
-        try:
-            scheme(tag.name)
-        except InvalidVersion:
-            out.warn(f"InvalidVersion {tag}")
-        else:
-            valid_tags.append(tag)
-
-    return valid_tags
-
-
 def generate_tree_from_commits(
     commits: list[GitCommit],
     tags: list[GitTag],
@@ -110,14 +81,15 @@ def generate_tree_from_commits(
     changelog_pattern: str,
     unreleased_version: str | None = None,
     change_type_map: dict[str, str] | None = None,
-    changelog_message_builder_hook: Callable | None = None,
-    merge_prerelease: bool = False,
-    scheme: VersionScheme = DEFAULT_SCHEME,
-) -> Iterable[dict]:
+    changelog_message_builder_hook: MessageBuilderHook | None = None,
+    changelog_release_hook: ChangelogReleaseHook | None = None,
+    rules: TagRules | None = None,
+) -> Generator[dict[str, Any], None, None]:
     pat = re.compile(changelog_pattern)
     map_pat = re.compile(commit_parser, re.MULTILINE)
     body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL)
     current_tag: GitTag | None = None
+    rules = rules or TagRules()
 
     # Check if the latest commit is not tagged
     if commits:
@@ -137,15 +109,20 @@ def generate_tree_from_commits(
     for commit in commits:
         commit_tag = get_commit_tag(commit, tags)
 
-        if commit_tag is not None and tag_included_in_changelog(
-            commit_tag, used_tags, merge_prerelease, scheme=scheme
+        if (
+            commit_tag
+            and commit_tag not in used_tags
+            and rules.include_in_changelog(commit_tag)
         ):
             used_tags.append(commit_tag)
-            yield {
+            release = {
                 "version": current_tag_name,
                 "date": current_tag_date,
                 "changes": changes,
             }
+            if changelog_release_hook:
+                release = changelog_release_hook(release, commit_tag)
+            yield release
             current_tag_name = commit_tag.name
             current_tag_date = commit_tag.date
             changes = defaultdict(list)
@@ -155,52 +132,82 @@ def generate_tree_from_commits(
             continue
 
         # Process subject from commit message
-        message = map_pat.match(commit.message)
-        if message:
-            parsed_message: dict = message.groupdict()
-            # change_type becomes optional by providing None
-            change_type = parsed_message.pop("change_type", None)
-
-            if change_type_map:
-                change_type = change_type_map.get(change_type, change_type)
-            if changelog_message_builder_hook:
-                parsed_message = changelog_message_builder_hook(parsed_message, commit)
-            changes[change_type].append(parsed_message)
+        if message := map_pat.match(commit.message):
+            process_commit_message(
+                changelog_message_builder_hook,
+                message,
+                commit,
+                changes,
+                change_type_map,
+            )
 
         # Process body from commit message
         body_parts = commit.body.split("\n\n")
         for body_part in body_parts:
-            message_body = body_map_pat.match(body_part)
-            if not message_body:
-                continue
-            parsed_message_body: dict = message_body.groupdict()
-
-            change_type = parsed_message_body.pop("change_type", None)
+            if message := body_map_pat.match(body_part):
+                process_commit_message(
+                    changelog_message_builder_hook,
+                    message,
+                    commit,
+                    changes,
+                    change_type_map,
+                )
+
+    release = {
+        "version": current_tag_name,
+        "date": current_tag_date,
+        "changes": changes,
+    }
+    if changelog_release_hook:
+        release = changelog_release_hook(release, commit_tag)
+    yield release
+
+
+def process_commit_message(
+    hook: MessageBuilderHook | None,
+    parsed: re.Match[str],
+    commit: GitCommit,
+    changes: dict[str | None, list],
+    change_type_map: dict[str, str] | None = None,
+) -> None:
+    message: dict[str, Any] = {
+        "sha1": commit.rev,
+        "parents": commit.parents,
+        "author": commit.author,
+        "author_email": commit.author_email,
+        **parsed.groupdict(),
+    }
+
+    if processed := hook(message, commit) if hook else message:
+        messages = [processed] if isinstance(processed, dict) else processed
+        for msg in messages:
+            change_type = msg.pop("change_type", None)
             if change_type_map:
                 change_type = change_type_map.get(change_type, change_type)
-            changes[change_type].append(parsed_message_body)
+            changes[change_type].append(msg)
 
-    yield {"version": current_tag_name, "date": current_tag_date, "changes": changes}
 
-
-def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable:
+def generate_ordered_changelog_tree(
+    tree: Iterable[Mapping[str, Any]], change_type_order: list[str]
+) -> Generator[dict[str, Any], None, None]:
     if len(set(change_type_order)) != len(change_type_order):
         raise InvalidConfigurationError(
-            f"Change types contain duplicates types ({change_type_order})"
+            f"Change types contain duplicated types ({change_type_order})"
         )
 
-    sorted_tree = []
     for entry in tree:
-        ordered_change_types = change_type_order + sorted(
-            set(entry["changes"].keys()) - set(change_type_order)
-        )
-        changes = [
-            (ct, entry["changes"][ct])
-            for ct in ordered_change_types
-            if ct in entry["changes"]
-        ]
-        sorted_tree.append({**entry, **{"changes": OrderedDict(changes)}})
-    return sorted_tree
+        yield {
+            **entry,
+            "changes": _calculate_sorted_changes(change_type_order, entry["changes"]),
+        }
+
+
+def _calculate_sorted_changes(
+    change_type_order: list[str], changes: Mapping[str, Any]
+) -> OrderedDict[str, Any]:
+    remaining_change_types = set(changes.keys()) - set(change_type_order)
+    sorted_change_types = change_type_order + sorted(remaining_change_types)
+    return OrderedDict((ct, changes[ct]) for ct in sorted_change_types if ct in changes)
 
 
 def get_changelog_template(loader: BaseLoader, template: str) -> Template:
@@ -218,7 +225,7 @@ def render_changelog(
     tree: Iterable,
     loader: BaseLoader,
     template: str,
-    **kwargs,
+    **kwargs: Any,
 ) -> str:
     jinja_template = get_changelog_template(loader, template)
     changelog: str = jinja_template.render(tree=tree, **kwargs)
@@ -275,7 +282,7 @@ def incremental_build(
 
 
 def get_smart_tag_range(
-    tags: list[GitTag], newest: str, oldest: str | None = None
+    tags: Sequence[GitTag], newest: str, oldest: str | None = None
 ) -> list[GitTag]:
     """Smart because it finds the N+1 tag.
 
@@ -301,11 +308,10 @@ def get_smart_tag_range(
 
 
 def get_oldest_and_newest_rev(
-    tags: list[GitTag],
+    tags: Sequence[GitTag],
     version: str,
-    tag_format: str,
-    scheme: VersionScheme | None = None,
-) -> tuple[str | None, str | None]:
+    rules: TagRules,
+) -> tuple[str | None, str]:
     """Find the tags for the given version.
 
     `version` may come in different formats:
@@ -318,23 +324,28 @@ def get_oldest_and_newest_rev(
         oldest, newest = version.split("..")
     except ValueError:
         newest = version
-
-    newest_tag = normalize_tag(newest, tag_format=tag_format, scheme=scheme)
+    if not (newest_tag := rules.find_tag_for(tags, newest)):
+        raise NoCommitsFoundError("Could not find a valid revision range.")
 
     oldest_tag = None
+    oldest_tag_name = None
     if oldest:
-        oldest_tag = normalize_tag(oldest, tag_format=tag_format, scheme=scheme)
+        if not (oldest_tag := rules.find_tag_for(tags, oldest)):
+            raise NoCommitsFoundError("Could not find a valid revision range.")
+        oldest_tag_name = oldest_tag.name
 
-    tags_range = get_smart_tag_range(tags, newest=newest_tag, oldest=oldest_tag)
+    tags_range = get_smart_tag_range(
+        tags, newest=newest_tag.name, oldest=oldest_tag_name
+    )
     if not tags_range:
         raise NoCommitsFoundError("Could not find a valid revision range.")
 
     oldest_rev: str | None = tags_range[-1].name
-    newest_rev = newest_tag
+    newest_rev = newest_tag.name
 
     # check if it's the first tag created
     # and it's also being requested as part of the range
-    if oldest_rev == tags[-1].name and oldest_rev == oldest_tag:
+    if oldest_rev == tags[-1].name and oldest_rev == oldest_tag_name:
         return None, newest_rev
 
     # when they are the same, and it's also the
diff --git a/commitizen/changelog_formats/__init__.py b/commitizen/changelog_formats/__init__.py
index 85df4b5144..9a5eea7ab2 100644
--- a/commitizen/changelog_formats/__init__.py
+++ b/commitizen/changelog_formats/__init__.py
@@ -1,13 +1,16 @@
 from __future__ import annotations
 
-from typing import ClassVar, Protocol
+import sys
+from typing import Callable, ClassVar, Protocol
 
-import importlib_metadata as metadata
+if sys.version_info >= (3, 10):
+    from importlib import metadata
+else:
+    import importlib_metadata as metadata
 
 from commitizen.changelog import Metadata
-from commitizen.exceptions import ChangelogFormatUnknown
 from commitizen.config.base_config import BaseConfig
-
+from commitizen.exceptions import ChangelogFormatUnknown
 
 CHANGELOG_FORMAT_ENTRYPOINT = "commitizen.changelog_format"
 TEMPLATE_EXTENSION = "j2"
@@ -22,7 +25,7 @@ class ChangelogFormat(Protocol):
 
     config: BaseConfig
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         self.config = config
 
     @property
@@ -61,10 +64,9 @@ def get_changelog_format(
     :raises FormatUnknown: if a non-empty name is provided but cannot be found in the known formats
     """
     name: str | None = config.settings.get("changelog_format")
-    format: type[ChangelogFormat] | None = guess_changelog_format(filename)
-
-    if name and name in KNOWN_CHANGELOG_FORMATS:
-        format = KNOWN_CHANGELOG_FORMATS[name]
+    format = (
+        name and KNOWN_CHANGELOG_FORMATS.get(name) or _guess_changelog_format(filename)
+    )
 
     if not format:
         raise ChangelogFormatUnknown(f"Unknown changelog format '{name}'")
@@ -72,7 +74,7 @@ def get_changelog_format(
     return format(config)
 
 
-def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None:
+def _guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None:
     """
     Try guessing the file format from the filename.
 
@@ -88,3 +90,9 @@ def guess_changelog_format(filename: str | None) -> type[ChangelogFormat] | None
             if filename.endswith(f".{alt_extension}"):
                 return format
     return None
+
+
+def __getattr__(name: str) -> Callable[[str], type[ChangelogFormat] | None]:
+    if name == "guess_changelog_format":
+        return _guess_changelog_format
+    raise AttributeError(f"module {__name__} has no attribute {name}")
diff --git a/commitizen/changelog_formats/asciidoc.py b/commitizen/changelog_formats/asciidoc.py
index d738926f6e..ed3e8607bd 100644
--- a/commitizen/changelog_formats/asciidoc.py
+++ b/commitizen/changelog_formats/asciidoc.py
@@ -1,24 +1,25 @@
 from __future__ import annotations
 
 import re
+from typing import TYPE_CHECKING
 
 from .base import BaseFormat
 
+if TYPE_CHECKING:
+    from commitizen.tags import VersionTag
+
 
 class AsciiDoc(BaseFormat):
     extension = "adoc"
 
     RE_TITLE = re.compile(r"^(?P<level>=+) (?P<title>.*)$")
 
-    def parse_version_from_title(self, line: str) -> str | None:
+    def parse_version_from_title(self, line: str) -> VersionTag | None:
         m = self.RE_TITLE.match(line)
         if not m:
             return None
         # Capture last match as AsciiDoc use postfixed URL labels
-        matches = list(re.finditer(self.version_parser, m.group("title")))
-        if not matches:
-            return None
-        return matches[-1].group("version")
+        return self.tag_rules.search_version(m.group("title"), last=True)
 
     def parse_title_level(self, line: str) -> int | None:
         m = self.RE_TITLE.match(line)
diff --git a/commitizen/changelog_formats/base.py b/commitizen/changelog_formats/base.py
index d0dfd9ec55..cb5d385bf8 100644
--- a/commitizen/changelog_formats/base.py
+++ b/commitizen/changelog_formats/base.py
@@ -2,11 +2,11 @@
 
 import os
 from abc import ABCMeta
-from re import Pattern
 from typing import IO, Any, ClassVar
 
 from commitizen.changelog import Metadata
 from commitizen.config.base_config import BaseConfig
+from commitizen.tags import TagRules, VersionTag
 from commitizen.version_schemes import get_version_scheme
 
 from . import ChangelogFormat
@@ -20,20 +20,24 @@ class BaseFormat(ChangelogFormat, metaclass=ABCMeta):
     extension: ClassVar[str] = ""
     alternative_extensions: ClassVar[set[str]] = set()
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         # Constructor needs to be redefined because `Protocol` prevent instantiation by default
         # See: https://bugs.python.org/issue44807
         self.config = config
-
-    @property
-    def version_parser(self) -> Pattern:
-        return get_version_scheme(self.config).parser
+        self.encoding = self.config.settings["encoding"]
+        self.tag_format = self.config.settings["tag_format"]
+        self.tag_rules = TagRules(
+            scheme=get_version_scheme(self.config.settings),
+            tag_format=self.tag_format,
+            legacy_tag_formats=self.config.settings["legacy_tag_formats"],
+            ignored_tag_formats=self.config.settings["ignored_tag_formats"],
+        )
 
     def get_metadata(self, filepath: str) -> Metadata:
         if not os.path.isfile(filepath):
             return Metadata()
 
-        with open(filepath) as changelog_file:
+        with open(filepath, encoding=self.encoding) as changelog_file:
             return self.get_metadata_from_file(changelog_file)
 
     def get_metadata_from_file(self, file: IO[Any]) -> Metadata:
@@ -54,9 +58,10 @@ def get_metadata_from_file(self, file: IO[Any]) -> Metadata:
                 meta.unreleased_end = index
 
             # Try to find the latest release done
-            version = self.parse_version_from_title(line)
-            if version:
-                meta.latest_version = version
+            parsed = self.parse_version_from_title(line)
+            if parsed:
+                meta.latest_version = parsed.version
+                meta.latest_version_tag = parsed.tag
                 meta.latest_version_position = index
                 break  # there's no need for more info
         if meta.unreleased_start is not None and meta.unreleased_end is None:
@@ -64,7 +69,7 @@ def get_metadata_from_file(self, file: IO[Any]) -> Metadata:
 
         return meta
 
-    def parse_version_from_title(self, line: str) -> str | None:
+    def parse_version_from_title(self, line: str) -> VersionTag | None:
         """
         Extract the version from a title line if any
         """
diff --git a/commitizen/changelog_formats/markdown.py b/commitizen/changelog_formats/markdown.py
index a5a0f42de3..e3d30fe174 100644
--- a/commitizen/changelog_formats/markdown.py
+++ b/commitizen/changelog_formats/markdown.py
@@ -1,9 +1,13 @@
 from __future__ import annotations
 
 import re
+from typing import TYPE_CHECKING
 
 from .base import BaseFormat
 
+if TYPE_CHECKING:
+    from commitizen.tags import VersionTag
+
 
 class Markdown(BaseFormat):
     extension = "md"
@@ -12,14 +16,11 @@ class Markdown(BaseFormat):
 
     RE_TITLE = re.compile(r"^(?P<level>#+) (?P<title>.*)$")
 
-    def parse_version_from_title(self, line: str) -> str | None:
+    def parse_version_from_title(self, line: str) -> VersionTag | None:
         m = self.RE_TITLE.match(line)
         if not m:
             return None
-        m = re.search(self.version_parser, m.group("title"))
-        if not m:
-            return None
-        return m.group("version")
+        return self.tag_rules.search_version(m.group("title"))
 
     def parse_title_level(self, line: str) -> int | None:
         m = self.RE_TITLE.match(line)
diff --git a/commitizen/changelog_formats/restructuredtext.py b/commitizen/changelog_formats/restructuredtext.py
index a252fe1514..b7e4e105a1 100644
--- a/commitizen/changelog_formats/restructuredtext.py
+++ b/commitizen/changelog_formats/restructuredtext.py
@@ -1,9 +1,8 @@
 from __future__ import annotations
 
-import re
 import sys
 from itertools import zip_longest
-from typing import IO, TYPE_CHECKING, Any, Union, Tuple
+from typing import IO, TYPE_CHECKING, Any, Union
 
 from commitizen.changelog import Metadata
 
@@ -18,7 +17,7 @@
 
 
 # Can't use `|` operator and native type because of https://bugs.python.org/issue42233 only fixed in 3.10
-TitleKind: TypeAlias = Union[str, Tuple[str, str]]
+TitleKind: TypeAlias = Union[str, tuple[str, str]]
 
 
 class RestructuredText(BaseFormat):
@@ -46,7 +45,6 @@ def get_metadata_from_file(self, file: IO[Any]) -> Metadata:
             third = third.strip().lower()
             title: str | None = None
             kind: TitleKind | None = None
-
             if self.is_overlined_title(first, second, third):
                 title = second
                 kind = (first[0], third[0])
@@ -65,12 +63,11 @@ def get_metadata_from_file(self, file: IO[Any]) -> Metadata:
                 elif unreleased_title_kind and unreleased_title_kind == kind:
                     meta.unreleased_end = index
                 # Try to find the latest release done
-                m = re.search(self.version_parser, title)
-                if m:
-                    version = m.group("version")
-                    meta.latest_version = version
+                if version := self.tag_rules.search_version(title):
+                    meta.latest_version = version[0]
+                    meta.latest_version_tag = version[1]
                     meta.latest_version_position = index
-                    break  # there's no need for more info
+                    break
         if meta.unreleased_start is not None and meta.unreleased_end is None:
             meta.unreleased_end = (
                 meta.latest_version_position if meta.latest_version else index + 1
diff --git a/commitizen/changelog_formats/textile.py b/commitizen/changelog_formats/textile.py
index 80118cdb3c..6693e5e002 100644
--- a/commitizen/changelog_formats/textile.py
+++ b/commitizen/changelog_formats/textile.py
@@ -1,22 +1,23 @@
 from __future__ import annotations
 
 import re
+from typing import TYPE_CHECKING
 
 from .base import BaseFormat
 
+if TYPE_CHECKING:
+    from commitizen.tags import VersionTag
+
 
 class Textile(BaseFormat):
     extension = "textile"
 
     RE_TITLE = re.compile(r"^h(?P<level>\d)\. (?P<title>.*)$")
 
-    def parse_version_from_title(self, line: str) -> str | None:
+    def parse_version_from_title(self, line: str) -> VersionTag | None:
         if not self.RE_TITLE.match(line):
             return None
-        m = re.search(self.version_parser, line)
-        if not m:
-            return None
-        return m.group("version")
+        return self.tag_rules.search_version(line)
 
     def parse_title_level(self, line: str) -> int | None:
         m = self.RE_TITLE.match(line)
diff --git a/commitizen/cli.py b/commitizen/cli.py
index 511f745652..6f7556df49 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -7,7 +7,7 @@
 from functools import partial
 from pathlib import Path
 from types import TracebackType
-from typing import Any, Sequence
+from typing import TYPE_CHECKING, cast
 
 import argcomplete
 from decli import cli
@@ -47,17 +47,17 @@ def __call__(
         self,
         parser: argparse.ArgumentParser,
         namespace: argparse.Namespace,
-        kwarg: str | Sequence[Any] | None,
+        values: object,
         option_string: str | None = None,
-    ):
-        if not isinstance(kwarg, str):
+    ) -> None:
+        if not isinstance(values, str):
             return
-        if "=" not in kwarg:
+        if "=" not in values:
             raise InvalidCommandArgumentError(
                 f"Option {option_string} expect a key=value format"
             )
         kwargs = getattr(namespace, self.dest, None) or {}
-        key, value = kwarg.split("=", 1)
+        key, value = values.split("=", 1)
         if not key:
             raise InvalidCommandArgumentError(
                 f"Option {option_string} expect a key=value format"
@@ -70,8 +70,7 @@ def __call__(
     {
         "name": ["--template", "-t"],
         "help": (
-            "changelog template file name "
-            "(relative to the current working directory)"
+            "changelog template file name (relative to the current working directory)"
         ),
     },
     {
@@ -86,12 +85,15 @@ def __call__(
 data = {
     "prog": "cz",
     "description": (
-        "Commitizen is a cli tool to generate conventional commits.\n"
-        "For more information about the topic go to "
-        "https://conventionalcommits.org/"
+        "Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.\n"
+        "For more information, please visit https://commitizen-tools.github.io/commitizen"
     ),
     "formatter_class": argparse.RawDescriptionHelpFormatter,
     "arguments": [
+        {
+            "name": "--config",
+            "help": "the path of configuration file",
+        },
         {"name": "--debug", "action": "store_true", "help": "use debug mode"},
         {
             "name": ["-n", "--name"],
@@ -101,7 +103,7 @@ def __call__(
             "name": ["-nr", "--no-raise"],
             "type": str,
             "required": False,
-            "help": "comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/",
+            "help": "comma separated error codes that won't raise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-tools.github.io/commitizen/exit_codes/",
         },
     ],
     "subcommands": {
@@ -110,11 +112,13 @@ def __call__(
         "commands": [
             {
                 "name": ["init"],
+                "description": "init commitizen configuration",
                 "help": "init commitizen configuration",
                 "func": commands.Init,
             },
             {
                 "name": ["commit", "c"],
+                "description": "create new commit",
                 "help": "create new commit",
                 "func": commands.Commit,
                 "arguments": [
@@ -123,6 +127,12 @@ def __call__(
                         "action": "store_true",
                         "help": "retry last commit",
                     },
+                    {
+                        "name": ["--no-retry"],
+                        "action": "store_true",
+                        "default": False,
+                        "help": "skip retry if retry_after_failure is set to true",
+                    },
                     {
                         "name": "--dry-run",
                         "action": "store_true",
@@ -137,33 +147,60 @@ def __call__(
                     {
                         "name": ["-s", "--signoff"],
                         "action": "store_true",
-                        "help": "sign off the commit",
+                        "help": "Deprecated, use 'cz commit -- -s' instead",
                     },
                     {
                         "name": ["-a", "--all"],
                         "action": "store_true",
                         "help": "Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected.",
                     },
+                    {
+                        "name": ["-e", "--edit"],
+                        "action": "store_true",
+                        "default": False,
+                        "help": "edit the commit message before committing",
+                    },
+                    {
+                        "name": ["-l", "--message-length-limit"],
+                        "type": int,
+                        "default": 0,
+                        "help": "length limit of the commit message; 0 for no limit",
+                    },
+                    {
+                        "name": ["--"],
+                        "action": "store_true",
+                        "dest": "double_dash",
+                        "help": "Positional arguments separator (recommended)",
+                    },
                 ],
             },
             {
                 "name": "ls",
+                "description": "show available commitizens",
                 "help": "show available commitizens",
                 "func": commands.ListCz,
             },
             {
                 "name": "example",
+                "description": "show commit example",
                 "help": "show commit example",
                 "func": commands.Example,
             },
             {
                 "name": "info",
+                "description": "show information about the cz",
                 "help": "show information about the cz",
                 "func": commands.Info,
             },
-            {"name": "schema", "help": "show commit schema", "func": commands.Schema},
+            {
+                "name": "schema",
+                "description": "show commit schema",
+                "help": "show commit schema",
+                "func": commands.Schema,
+            },
             {
                 "name": "bump",
+                "description": "bump semantic version based on the git log",
                 "help": "bump semantic version based on the git log",
                 "func": commands.Bump,
                 "arguments": [
@@ -230,6 +267,19 @@ def __call__(
                         "choices": ["MAJOR", "MINOR", "PATCH"],
                         "type": str.upper,
                     },
+                    {
+                        "name": ["--increment-mode"],
+                        "choices": ["linear", "exact"],
+                        "default": "linear",
+                        "help": (
+                            "set the method by which the new version is chosen. "
+                            "'linear' (default) guesses the next version based on typical linear version progression, "
+                            "such that bumping of a pre-release with lower precedence than the current pre-release "
+                            "phase maintains the current phase of higher precedence. "
+                            "'exact' applies the changes that have been specified (or determined from the commit log) "
+                            "without interpretation, such that the increment and pre-release are always honored"
+                        ),
+                    },
                     {
                         "name": ["--check-consistency", "-cc"],
                         "help": (
@@ -297,7 +347,7 @@ def __call__(
                     },
                     {
                         "name": ["--version-type"],
-                        "help": "Deprecated, use --version-scheme",
+                        "help": "Deprecated, use --version-scheme instead",
                         "default": None,
                         "choices": version_schemes.KNOWN_SCHEMES,
                     },
@@ -308,10 +358,30 @@ def __call__(
                         "help": "bump to the given version (e.g: 1.5.3)",
                         "metavar": "MANUAL_VERSION",
                     },
+                    {
+                        "name": ["--build-metadata"],
+                        "help": "Add additional build-metadata to the version-number",
+                        "default": None,
+                    },
+                    {
+                        "name": ["--get-next"],
+                        "action": "store_true",
+                        "help": "Determine the next version and write to stdout",
+                        "default": False,
+                    },
+                    {
+                        "name": ["--allow-no-commit"],
+                        "default": False,
+                        "help": "bump version without eligible commits",
+                        "action": "store_true",
+                    },
                 ],
             },
             {
                 "name": ["changelog", "ch"],
+                "description": (
+                    "generate changelog (note that it will overwrite existing file)"
+                ),
                 "help": (
                     "generate changelog (note that it will overwrite existing file)"
                 ),
@@ -378,10 +448,15 @@ def __call__(
                         "help": "Export the changelog template into this file instead of rendering it",
                     },
                     *deepcopy(tpl_arguments),
+                    {
+                        "name": "--tag-format",
+                        "help": "The format of the tag, wrap around simple quotes",
+                    },
                 ],
             },
             {
                 "name": ["check"],
+                "description": "validates that a commit message matches the commitizen schema",
                 "help": "validates that a commit message matches the commitizen schema",
                 "func": commands.Check,
                 "arguments": [
@@ -417,10 +492,20 @@ def __call__(
                         "If the message starts by one of these prefixes, "
                         "the message won't be checked against the regex",
                     },
+                    {
+                        "name": ["-l", "--message-length-limit"],
+                        "type": int,
+                        "default": 0,
+                        "help": "length limit of the commit message; 0 for no limit",
+                    },
                 ],
             },
             {
                 "name": ["version"],
+                "description": (
+                    "get the version of the installed commitizen or the current project"
+                    " (default: installed commitizen)"
+                ),
                 "help": (
                     "get the version of the installed commitizen or the current project"
                     " (default: installed commitizen)"
@@ -464,22 +549,27 @@ def __call__(
 
 
 def commitizen_excepthook(
-    type, value, traceback, debug=False, no_raise: list[int] | None = None
-):
+    type: type[BaseException],
+    value: BaseException,
+    traceback: TracebackType | None,
+    debug: bool = False,
+    no_raise: list[int] | None = None,
+) -> None:
     traceback = traceback if isinstance(traceback, TracebackType) else None
+    if not isinstance(value, CommitizenException):
+        original_excepthook(type, value, traceback)
+        return
+
     if not no_raise:
         no_raise = []
-    if isinstance(value, CommitizenException):
-        if value.message:
-            value.output_method(value.message)
-        if debug:
-            original_excepthook(type, value, traceback)
-        exit_code = value.exit_code
-        if exit_code in no_raise:
-            exit_code = 0
-        sys.exit(exit_code)
-    else:
+    if value.message:
+        value.output_method(value.message)
+    if debug:
         original_excepthook(type, value, traceback)
+    exit_code = value.exit_code
+    if exit_code in no_raise:
+        exit_code = ExitCode.EXPECTED_EXIT
+    sys.exit(exit_code)
 
 
 commitizen_debug_excepthook = partial(commitizen_excepthook, debug=True)
@@ -494,7 +584,7 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]:
     represents the exit code found in exceptions.
     """
     no_raise_items: list[str] = comma_separated_no_raise.split(",")
-    no_raise_codes = []
+    no_raise_codes: list[int] = []
     for item in no_raise_items:
         if item.isdecimal():
             no_raise_codes.append(int(item))
@@ -509,10 +599,33 @@ def parse_no_raise(comma_separated_no_raise: str) -> list[int]:
     return no_raise_codes
 
 
-def main():
-    conf = config.read_cfg()
-    parser = cli(data)
-
+if TYPE_CHECKING:
+
+    class Args(argparse.Namespace):
+        config: str | None = None
+        debug: bool = False
+        name: str | None = None
+        no_raise: str | None = None  # comma-separated string, later parsed as list[int]
+        report: bool = False
+        project: bool = False
+        commitizen: bool = False
+        verbose: bool = False
+        func: type[
+            commands.Init  # init
+            | commands.Commit  # commit (c)
+            | commands.ListCz  # ls
+            | commands.Example  # example
+            | commands.Info  # info
+            | commands.Schema  # schema
+            | commands.Bump  # bump
+            | commands.Changelog  # changelog (ch)
+            | commands.Check  # check
+            | commands.Version  # version
+        ]
+
+
+def main() -> None:
+    parser: argparse.ArgumentParser = cli(data)
     argcomplete.autocomplete(parser)
     # Show help if no arg provided
     if len(sys.argv) == 1:
@@ -522,11 +635,8 @@ def main():
     # This is for the command required constraint in 2.0
     try:
         args, unknown_args = parser.parse_known_args()
-    except (TypeError, SystemExit) as e:
-        # https://github.com/commitizen-tools/commitizen/issues/429
-        # argparse raises TypeError when non exist command is provided on Python < 3.9
-        # but raise SystemExit with exit code == 2 on Python 3.9
-        if isinstance(e, TypeError) or (isinstance(e, SystemExit) and e.code == 2):
+    except SystemExit as e:
+        if e.code == 2:
             raise NoCommandFoundError()
         raise e
 
@@ -552,9 +662,11 @@ def main():
         extra_args = " ".join(unknown_args[1:])
         arguments["extra_cli_args"] = extra_args
 
+    conf = config.read_cfg(args.config)
+    args = cast("Args", args)
     if args.name:
         conf.update({"name": args.name})
-    elif not args.name and not conf.path:
+    elif not conf.path:
         conf.update({"name": "cz_conventional_commits"})
 
     if args.debug:
@@ -567,7 +679,7 @@ def main():
         )
         sys.excepthook = no_raise_debug_excepthook
 
-    args.func(conf, arguments)()
+    args.func(conf, arguments)()  # type: ignore[arg-type]
 
 
 if __name__ == "__main__":
diff --git a/commitizen/cmd.py b/commitizen/cmd.py
index 51ef4523ff..3f13087233 100644
--- a/commitizen/cmd.py
+++ b/commitizen/cmd.py
@@ -1,4 +1,8 @@
+from __future__ import annotations
+
+import os
 import subprocess
+from collections.abc import Mapping
 from typing import NamedTuple
 
 from charset_normalizer import from_bytes
@@ -27,7 +31,9 @@ def _try_decode(bytes_: bytes) -> str:
             raise CharacterSetDecodeError() from e
 
 
-def run(cmd: str, env=None) -> Command:
+def run(cmd: str, env: Mapping[str, str] | None = None) -> Command:
+    if env is not None:
+        env = {**os.environ, **env}
     process = subprocess.Popen(
         cmd,
         shell=True,
diff --git a/commitizen/commands/__init__.py b/commitizen/commands/__init__.py
index 806e384522..58b18297dc 100644
--- a/commitizen/commands/__init__.py
+++ b/commitizen/commands/__init__.py
@@ -11,13 +11,13 @@
 
 __all__ = (
     "Bump",
+    "Changelog",
     "Check",
     "Commit",
-    "Changelog",
     "Example",
     "Info",
+    "Init",
     "ListCz",
     "Schema",
     "Version",
-    "Init",
 )
diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index af365ecd28..2a84483c94 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -1,19 +1,22 @@
 from __future__ import annotations
 
-import os
 import warnings
 from logging import getLogger
+from typing import cast
 
 import questionary
 
-from commitizen import bump, cmd, factory, git, hooks, out
+from commitizen import bump, factory, git, hooks, out
+from commitizen.changelog_formats import get_changelog_format
 from commitizen.commands.changelog import Changelog
 from commitizen.config import BaseConfig
+from commitizen.defaults import Settings
 from commitizen.exceptions import (
     BumpCommitFailedError,
     BumpTagFailedError,
     DryRunExit,
     ExpectedExit,
+    GetNextExit,
     InvalidManualVersion,
     NoCommitsFoundError,
     NoneIncrementExit,
@@ -22,47 +25,78 @@
     NotAllowed,
     NoVersionSpecifiedError,
 )
-from commitizen.changelog_formats import get_changelog_format
 from commitizen.providers import get_provider
-from commitizen.version_schemes import InvalidVersion, get_version_scheme
+from commitizen.tags import TagRules
+from commitizen.version_schemes import (
+    Increment,
+    InvalidVersion,
+    Prerelease,
+    get_version_scheme,
+)
 
 logger = getLogger("commitizen")
 
 
+class BumpArgs(Settings, total=False):
+    allow_no_commit: bool | None
+    annotated_tag_message: str | None
+    build_metadata: str | None
+    changelog_to_stdout: bool
+    changelog: bool
+    check_consistency: bool
+    devrelease: int | None
+    dry_run: bool
+    file_name: str
+    files_only: bool | None
+    get_next: bool
+    git_output_to_stderr: bool
+    increment_mode: str
+    increment: Increment | None
+    local_version: bool
+    manual_version: str | None
+    no_verify: bool
+    prerelease: Prerelease | None
+    retry: bool
+    yes: bool
+
+
 class Bump:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: dict):
+    def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.arguments: dict = arguments
-        self.bump_settings: dict = {
-            **config.settings,
-            **{
-                key: arguments[key]
-                for key in [
-                    "tag_format",
-                    "prerelease",
-                    "increment",
-                    "bump_message",
-                    "gpg_sign",
-                    "annotated_tag",
-                    "annotated_tag_message",
-                    "major_version_zero",
-                    "prerelease_offset",
-                    "template",
-                    "file_name",
-                ]
-                if arguments[key] is not None
+        self.arguments = arguments
+        self.bump_settings = cast(
+            BumpArgs,
+            {
+                **config.settings,
+                **{
+                    k: v
+                    for k in (
+                        "annotated_tag_message",
+                        "annotated_tag",
+                        "bump_message",
+                        "file_name",
+                        "gpg_sign",
+                        "increment_mode",
+                        "increment",
+                        "major_version_zero",
+                        "prerelease_offset",
+                        "prerelease",
+                        "tag_format",
+                        "template",
+                    )
+                    if (v := arguments.get(k)) is not None
+                },
             },
-        }
-        self.cz = factory.commiter_factory(self.config)
-        self.changelog = arguments["changelog"] or self.config.settings.get(
-            "update_changelog_on_bump"
         )
+        self.cz = factory.committer_factory(self.config)
+        self.changelog_flag = arguments["changelog"]
+        self.changelog_config = self.config.settings.get("update_changelog_on_bump")
         self.changelog_to_stdout = arguments["changelog_to_stdout"]
         self.git_output_to_stderr = arguments["git_output_to_stderr"]
         self.no_verify = arguments["no_verify"]
@@ -74,12 +108,12 @@ def __init__(self, config: BaseConfig, arguments: dict):
         if deprecated_version_type:
             warnings.warn(
                 DeprecationWarning(
-                    "`--version-type` parameter is deprecated and will be removed in commitizen 4. "
+                    "`--version-type` parameter is deprecated and will be removed in v5. "
                     "Please use `--version-scheme` instead"
                 )
             )
         self.scheme = get_version_scheme(
-            self.config, arguments["version_scheme"] or deprecated_version_type
+            self.config.settings, arguments["version_scheme"] or deprecated_version_type
         )
         self.file_name = arguments["file_name"] or self.config.settings.get(
             "changelog_file"
@@ -93,29 +127,29 @@ def __init__(self, config: BaseConfig, arguments: dict):
         )
         self.extras = arguments["extras"]
 
-    def is_initial_tag(self, current_tag_version: str, is_yes: bool = False) -> bool:
+    def _is_initial_tag(
+        self, current_tag: git.GitTag | None, is_yes: bool = False
+    ) -> bool:
         """Check if reading the whole git tree up to HEAD is needed."""
-        is_initial = False
-        if not git.tag_exist(current_tag_version):
-            if is_yes:
-                is_initial = True
-            else:
-                out.info(f"Tag {current_tag_version} could not be found. ")
-                out.info(
-                    "Possible causes:\n"
-                    "- version in configuration is not the current version\n"
-                    "- tag_format is missing, check them using 'git tag --list'\n"
-                )
-                is_initial = questionary.confirm("Is this the first tag created?").ask()
-        return is_initial
+        if current_tag:
+            return False
+        if is_yes:
+            return True
+
+        out.info("No tag matching configuration could be found.")
+        out.info(
+            "Possible causes:\n"
+            "- version in configuration is not the current version\n"
+            "- tag_format or legacy_tag_formats is missing, check them using 'git tag --list'\n"
+        )
+        return bool(questionary.confirm("Is this the first tag created?").ask())
 
-    def find_increment(self, commits: list[git.GitCommit]) -> str | None:
+    def _find_increment(self, commits: list[git.GitCommit]) -> Increment | None:
         # Update the bump map to ensure major version doesn't increment.
-        is_major_version_zero: bool = self.bump_settings["major_version_zero"]
         # self.cz.bump_map = defaults.bump_map_major_version_zero
         bump_map = (
             self.cz.bump_map_major_version_zero
-            if is_major_version_zero
+            if self.bump_settings["major_version_zero"]
             else self.cz.bump_map
         )
         bump_pattern = self.cz.bump_pattern
@@ -124,12 +158,9 @@ def find_increment(self, commits: list[git.GitCommit]) -> str | None:
             raise NoPatternMapError(
                 f"'{self.config.settings['name']}' rule does not support bump"
             )
-        increment = bump.find_increment(
-            commits, regex=bump_pattern, increments_map=bump_map
-        )
-        return increment
+        return bump.find_increment(commits, regex=bump_pattern, increments_map=bump_map)
 
-    def __call__(self):  # noqa: C901
+    def __call__(self) -> None:
         """Steps executed to bump."""
         provider = get_provider(self.config)
 
@@ -138,20 +169,14 @@ def __call__(self):  # noqa: C901
         except TypeError:
             raise NoVersionSpecifiedError()
 
-        tag_format: str = self.bump_settings["tag_format"]
-        bump_commit_message: str = self.bump_settings["bump_message"]
-        version_files: list[str] = self.bump_settings["version_files"]
-        major_version_zero: bool = self.bump_settings["major_version_zero"]
-        prerelease_offset: int = self.bump_settings["prerelease_offset"]
-
-        dry_run: bool = self.arguments["dry_run"]
-        is_yes: bool = self.arguments["yes"]
-        increment: str | None = self.arguments["increment"]
-        prerelease: str | None = self.arguments["prerelease"]
-        devrelease: int | None = self.arguments["devrelease"]
-        is_files_only: bool | None = self.arguments["files_only"]
-        is_local_version: bool | None = self.arguments["local_version"]
+        increment = self.arguments["increment"]
+        prerelease = self.arguments["prerelease"]
+        devrelease = self.arguments["devrelease"]
+        is_local_version = self.arguments["local_version"]
         manual_version = self.arguments["manual_version"]
+        build_metadata = self.arguments["build_metadata"]
+        get_next = self.arguments["get_next"]
+        allow_no_commit = self.arguments["allow_no_commit"]
 
         if manual_version:
             if increment:
@@ -168,42 +193,51 @@ def __call__(self):  # noqa: C901
                     "--local-version cannot be combined with MANUAL_VERSION"
                 )
 
-            if major_version_zero:
+            if build_metadata:
                 raise NotAllowed(
-                    "--major-version-zero cannot be combined with MANUAL_VERSION"
+                    "--build-metadata cannot be combined with MANUAL_VERSION"
                 )
 
-            if prerelease_offset:
+            if self.bump_settings["major_version_zero"]:
                 raise NotAllowed(
-                    "--prerelease-offset cannot be combined with MANUAL_VERSION"
+                    "--major-version-zero cannot be combined with MANUAL_VERSION"
                 )
 
-        if major_version_zero:
-            if not current_version.release[0] == 0:
-                raise NotAllowed(
-                    f"--major-version-zero is meaningless for current version {current_version}"
-                )
+            if get_next:
+                raise NotAllowed("--get-next cannot be combined with MANUAL_VERSION")
 
-        current_tag_version: str = bump.normalize_tag(
-            current_version,
-            tag_format=tag_format,
-            scheme=self.scheme,
-        )
+        if self.bump_settings["major_version_zero"] and current_version.release[0]:
+            raise NotAllowed(
+                f"--major-version-zero is meaningless for current version {current_version}"
+            )
 
-        is_initial = self.is_initial_tag(current_tag_version, is_yes)
-        if is_initial:
-            commits = git.get_commits()
+        if build_metadata and is_local_version:
+            raise NotAllowed("--local-version cannot be combined with --build-metadata")
+
+        if get_next:
+            # if trying to use --get-next, we should not allow --changelog or --changelog-to-stdout
+            if self.changelog_flag or self.changelog_to_stdout:
+                raise NotAllowed(
+                    "--changelog or --changelog-to-stdout is not allowed with --get-next"
+                )
+            # --get-next is a special case, taking precedence over config for 'update_changelog_on_bump'
+            self.changelog_config = False
+            # Setting dry_run to prevent any unwanted changes to the repo or files
+            self.dry_run = True
         else:
-            commits = git.get_commits(current_tag_version)
+            # If user specified changelog_to_stdout, they probably want the
+            # changelog to be generated as well, this is the most intuitive solution
+            self.changelog_flag = any(
+                (self.changelog_flag, self.changelog_to_stdout, self.changelog_config)
+            )
 
-        # If user specified changelog_to_stdout, they probably want the
-        # changelog to be generated as well, this is the most intuitive solution
-        self.changelog = self.changelog or bool(self.changelog_to_stdout)
+        rules = TagRules.from_settings(cast(Settings, self.bump_settings))
+        current_tag = rules.find_tag_for(git.get_tags(), current_version)
+        current_tag_version = getattr(
+            current_tag, "name", rules.normalize_tag(current_version)
+        )
 
-        # No commits, there is no need to create an empty tag.
-        # Unless we previously had a prerelease.
-        if not commits and not current_version.is_prerelease:
-            raise NoCommitsFoundError("[NO_COMMITS_FOUND]\n" "No new commits found.")
+        is_initial = self._is_initial_tag(current_tag, self.arguments["yes"])
 
         if manual_version:
             try:
@@ -215,7 +249,20 @@ def __call__(self):  # noqa: C901
                 ) from exc
         else:
             if increment is None:
-                increment = self.find_increment(commits)
+                commits = git.get_commits(current_tag.name if current_tag else None)
+
+                # No commits, there is no need to create an empty tag.
+                # Unless we previously had a prerelease.
+                if (
+                    not commits
+                    and not current_version.is_prerelease
+                    and not allow_no_commit
+                ):
+                    raise NoCommitsFoundError(
+                        "[NO_COMMITS_FOUND]\nNo new commits found."
+                    )
+
+                increment = self._find_increment(commits)
 
             # It may happen that there are commits, but they are not eligible
             # for an increment, this generates a problem when using prerelease (#281)
@@ -226,30 +273,37 @@ def __call__(self):  # noqa: C901
                     "To avoid this error, manually specify the type of increment with `--increment`"
                 )
 
-            # Increment is removed when current and next version
-            # are expected to be prereleases.
-            if prerelease and current_version.is_prerelease:
-                increment = None
+            # we create an empty PATCH increment for empty tag
+            if increment is None and allow_no_commit:
+                increment = "PATCH"
 
             new_version = current_version.bump(
                 increment,
                 prerelease=prerelease,
-                prerelease_offset=prerelease_offset,
+                prerelease_offset=self.bump_settings["prerelease_offset"],
                 devrelease=devrelease,
                 is_local_version=is_local_version,
+                build_metadata=build_metadata,
+                exact_increment=self.arguments["increment_mode"] == "exact",
             )
 
-        new_tag_version = bump.normalize_tag(
-            new_version,
-            tag_format=tag_format,
-            scheme=self.scheme,
-        )
+        new_tag_version = rules.normalize_tag(new_version)
         message = bump.create_commit_message(
-            current_version, new_version, bump_commit_message
+            current_version, new_version, self.bump_settings["bump_message"]
         )
 
+        if get_next:
+            if increment is None and new_tag_version == current_tag_version:
+                raise NoneIncrementExit(
+                    "[NO_COMMITS_TO_BUMP]\n"
+                    "The commits found are not eligible to be bumped"
+                )
+
+            out.write(str(new_version))
+            raise GetNextExit()
+
         # Report found information
-        information = f"{message}\n" f"tag to create: {new_tag_version}\n"
+        information = f"{message}\ntag to create: {new_tag_version}\n"
         if increment:
             information += f"increment detected: {increment}\n"
 
@@ -263,60 +317,43 @@ def __call__(self):  # noqa: C901
 
         if increment is None and new_tag_version == current_tag_version:
             raise NoneIncrementExit(
-                "[NO_COMMITS_TO_BUMP]\n"
-                "The commits found are not eligible to be bumped"
+                "[NO_COMMITS_TO_BUMP]\nThe commits found are not eligible to be bumped"
             )
 
-        if self.changelog:
+        files: list[str] = []
+        dry_run = self.arguments["dry_run"]
+        if self.changelog_flag:
+            args = {
+                "unreleased_version": new_tag_version,
+                "template": self.template,
+                "extras": self.extras,
+                "incremental": True,
+                "dry_run": dry_run,
+            }
             if self.changelog_to_stdout:
-                changelog_cmd = Changelog(
-                    self.config,
-                    {
-                        "unreleased_version": new_tag_version,
-                        "template": self.template,
-                        "extras": self.extras,
-                        "incremental": True,
-                        "dry_run": True,
-                    },
-                )
+                changelog_cmd = Changelog(self.config, {**args, "dry_run": True})  # type: ignore[typeddict-item]
                 try:
                     changelog_cmd()
                 except DryRunExit:
                     pass
-            changelog_cmd = Changelog(
-                self.config,
-                {
-                    "unreleased_version": new_tag_version,
-                    "incremental": True,
-                    "dry_run": dry_run,
-                    "template": self.template,
-                    "extras": self.extras,
-                    "file_name": self.file_name,
-                },
-            )
+
+            args["file_name"] = self.file_name
+            changelog_cmd = Changelog(self.config, args)  # type: ignore[arg-type]
             changelog_cmd()
-            file_names = []
-            for file_name in version_files:
-                drive, tail = os.path.splitdrive(file_name)
-                path, _, regex = tail.partition(":")
-                path = drive + path if path != "" else drive + regex
-                file_names.append(path)
-            git_add_changelog_and_version_files_command = (
-                f"git add {changelog_cmd.file_name} "
-                f"{' '.join(name for name in file_names)}"
-            )
-            c = cmd.run(git_add_changelog_and_version_files_command)
+            files.append(changelog_cmd.file_name)
 
         # Do not perform operations over files or git.
         if dry_run:
             raise DryRunExit()
 
-        bump.update_version_in_files(
-            str(current_version),
-            str(new_version),
-            version_files,
-            check_consistency=self.check_consistency,
-            encoding=self.encoding,
+        files.extend(
+            bump.update_version_in_files(
+                str(current_version),
+                str(new_version),
+                self.bump_settings["version_files"],
+                check_consistency=self.check_consistency,
+                encoding=self.encoding,
+            )
         )
 
         provider.set_version(str(new_version))
@@ -332,41 +369,47 @@ def __call__(self):  # noqa: C901
                 new_tag_version=new_tag_version,
                 message=message,
                 increment=increment,
-                changelog_file_name=changelog_cmd.file_name if self.changelog else None,
+                changelog_file_name=changelog_cmd.file_name
+                if self.changelog_flag
+                else None,
             )
 
-        if is_files_only:
+        if self.arguments["files_only"]:
             raise ExpectedExit()
 
+        # FIXME: check if any changes have been staged
+        git.add(*files)
         c = git.commit(message, args=self._get_commit_args())
-        if self.retry and c.return_code != 0 and self.changelog:
+        if self.retry and c.return_code != 0 and self.changelog_flag:
             # Maybe pre-commit reformatted some files? Retry once
             logger.debug("1st git.commit error: %s", c.err)
             logger.info("1st commit attempt failed; retrying once")
-            cmd.run(git_add_changelog_and_version_files_command)
+            git.add(*files)
             c = git.commit(message, args=self._get_commit_args())
         if c.return_code != 0:
             err = c.err.strip() or c.out
             raise BumpCommitFailedError(f'2nd git.commit error: "{err}"')
 
-        if c.out:
-            if self.git_output_to_stderr:
-                out.diagnostic(c.out)
-            else:
-                out.write(c.out)
-        if c.err:
-            if self.git_output_to_stderr:
-                out.diagnostic(c.err)
-            else:
-                out.write(c.err)
+        for msg in (c.out, c.err):
+            if msg:
+                out_func = out.diagnostic if self.git_output_to_stderr else out.write
+                out_func(msg)
 
         c = git.tag(
             new_tag_version,
-            signed=self.bump_settings.get("gpg_sign", False)
-            or bool(self.config.settings.get("gpg_sign", False)),
-            annotated=self.bump_settings.get("annotated_tag", False)
-            or bool(self.config.settings.get("annotated_tag", False))
-            or bool(self.bump_settings.get("annotated_tag_message", False)),
+            signed=any(
+                (
+                    self.bump_settings.get("gpg_sign"),
+                    self.config.settings.get("gpg_sign"),
+                )
+            ),
+            annotated=any(
+                (
+                    self.bump_settings.get("annotated_tag"),
+                    self.config.settings.get("annotated_tag"),
+                    self.bump_settings.get("annotated_tag_message"),
+                )
+            ),
             msg=self.bump_settings.get("annotated_tag_message", None),
             # TODO: also get from self.config.settings?
         )
@@ -384,7 +427,9 @@ def __call__(self):  # noqa: C901
                 current_tag_version=new_tag_version,
                 message=message,
                 increment=increment,
-                changelog_file_name=changelog_cmd.file_name if self.changelog else None,
+                changelog_file_name=changelog_cmd.file_name
+                if self.changelog_flag
+                else None,
             )
 
         # TODO: For v3 output this only as diagnostic and remove this if
@@ -393,7 +438,7 @@ def __call__(self):  # noqa: C901
         else:
             out.success("Done!")
 
-    def _get_commit_args(self):
+    def _get_commit_args(self) -> str:
         commit_args = ["-a"]
         if self.no_verify:
             commit_args.append("--no-verify")
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index 8b3c309636..dc50eb3ad2 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -1,14 +1,18 @@
 from __future__ import annotations
 
+import os
 import os.path
+from collections.abc import Generator, Iterable
 from difflib import SequenceMatcher
 from operator import itemgetter
 from pathlib import Path
-from typing import Callable
-
-from commitizen import bump, changelog, defaults, factory, git, out
+from typing import Any, TypedDict, cast
 
+from commitizen import changelog, defaults, factory, git, out
+from commitizen.changelog_formats import get_changelog_format
 from commitizen.config import BaseConfig
+from commitizen.cz.base import ChangelogReleaseHook, MessageBuilderHook
+from commitizen.cz.utils import strip_local_version
 from commitizen.exceptions import (
     DryRunExit,
     NoCommitsFoundError,
@@ -17,74 +21,111 @@
     NotAGitProjectError,
     NotAllowed,
 )
-from commitizen.changelog_formats import get_changelog_format
 from commitizen.git import GitTag, smart_open
+from commitizen.tags import TagRules
 from commitizen.version_schemes import get_version_scheme
 
 
+class ChangelogArgs(TypedDict, total=False):
+    change_type_map: dict[str, str]
+    change_type_order: list[str]
+    current_version: str
+    dry_run: bool
+    file_name: str
+    incremental: bool
+    merge_prerelease: bool
+    rev_range: str
+    start_rev: str
+    tag_format: str
+    unreleased_version: str | None
+    version_scheme: str
+    template: str
+    extras: dict[str, Any]
+    export_template: str
+
+
 class Changelog:
     """Generate a changelog based on the commit history."""
 
-    def __init__(self, config: BaseConfig, args):
+    def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
-        self.config: BaseConfig = config
-        self.encoding = self.config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.config = config
 
-        self.start_rev = args.get("start_rev") or self.config.settings.get(
-            "changelog_start_rev"
-        )
-        self.file_name = args.get("file_name") or self.config.settings.get(
+        changelog_file_name = arguments.get("file_name") or self.config.settings.get(
             "changelog_file"
         )
-        if not isinstance(self.file_name, str):
+        if not isinstance(changelog_file_name, str):
             raise NotAllowed(
                 "Changelog file name is broken.\n"
                 "Check the flag `--file-name` in the terminal "
                 f"or the setting `changelog_file` in {self.config.path}"
             )
+        self.file_name = (
+            os.path.join(str(self.config.path.parent), changelog_file_name)
+            if self.config.path is not None
+            else changelog_file_name
+        )
+
+        self.encoding = self.config.settings["encoding"]
+        self.cz = factory.committer_factory(self.config)
+
+        self.start_rev = arguments.get("start_rev") or self.config.settings.get(
+            "changelog_start_rev"
+        )
+
         self.changelog_format = get_changelog_format(self.config, self.file_name)
 
-        self.incremental = args["incremental"] or self.config.settings.get(
-            "changelog_incremental"
+        self.incremental = bool(
+            arguments.get("incremental")
+            or self.config.settings.get("changelog_incremental")
         )
-        self.dry_run = args["dry_run"]
+        self.dry_run = bool(arguments.get("dry_run"))
 
-        self.scheme = get_version_scheme(self.config, args.get("version_scheme"))
+        self.scheme = get_version_scheme(
+            self.config.settings, arguments.get("version_scheme")
+        )
 
         current_version = (
-            args.get("current_version", config.settings.get("version")) or ""
+            arguments.get("current_version")
+            or self.config.settings.get("version")
+            or ""
         )
         self.current_version = self.scheme(current_version) if current_version else None
 
-        self.unreleased_version = args["unreleased_version"]
+        self.unreleased_version = arguments["unreleased_version"]
         self.change_type_map = (
             self.config.settings.get("change_type_map") or self.cz.change_type_map
         )
-        self.change_type_order = (
+        self.change_type_order = cast(
+            list[str],
             self.config.settings.get("change_type_order")
             or self.cz.change_type_order
-            or defaults.change_type_order
+            or defaults.CHANGE_TYPE_ORDER,
         )
-        self.rev_range = args.get("rev_range")
-        self.tag_format: str = (
-            args.get("tag_format") or self.config.settings["tag_format"]
+        self.rev_range = arguments.get("rev_range")
+        self.tag_format = (
+            arguments.get("tag_format") or self.config.settings["tag_format"]
+        )
+        self.tag_rules = TagRules(
+            scheme=self.scheme,
+            tag_format=self.tag_format,
+            legacy_tag_formats=self.config.settings["legacy_tag_formats"],
+            ignored_tag_formats=self.config.settings["ignored_tag_formats"],
+            merge_prereleases=arguments.get("merge_prerelease")
+            or self.config.settings["changelog_merge_prerelease"],
         )
-        self.merge_prerelease = args.get(
-            "merge_prerelease"
-        ) or self.config.settings.get("changelog_merge_prerelease")
 
         self.template = (
-            args.get("template")
+            arguments.get("template")
             or self.config.settings.get("template")
             or self.changelog_format.template
         )
-        self.extras = args.get("extras") or {}
-        self.export_template_to = args.get("export_template")
+        self.extras = arguments.get("extras") or {}
+        self.export_template_to = arguments.get("export_template")
 
-    def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str:
+    def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str:
         """Try to find the 'start_rev'.
 
         We use a similarity approach. We know how to parse the version from the markdown
@@ -97,23 +138,28 @@ def _find_incremental_rev(self, latest_version: str, tags: list[GitTag]) -> str:
         on our experience.
         """
         SIMILARITY_THRESHOLD = 0.89
-        tag_ratio = map(
-            lambda tag: (SequenceMatcher(None, latest_version, tag.name).ratio(), tag),
-            tags,
+        scores_and_tag_names: Generator[tuple[float, str]] = (
+            (
+                score,
+                tag.name,
+            )
+            for tag in tags
+            if (
+                score := SequenceMatcher(
+                    None, latest_version, strip_local_version(tag.name)
+                ).ratio()
+            )
+            >= SIMILARITY_THRESHOLD
         )
         try:
-            score, tag = max(tag_ratio, key=itemgetter(0))
+            _, start_rev = max(scores_and_tag_names, key=itemgetter(0))
         except ValueError:
             raise NoRevisionError()
-        if score < SIMILARITY_THRESHOLD:
-            raise NoRevisionError()
-        start_rev = tag.name
         return start_rev
 
-    def write_changelog(
+    def _write_changelog(
         self, changelog_out: str, lines: list[str], changelog_meta: changelog.Metadata
-    ):
-        changelog_hook: Callable | None = self.cz.changelog_hook
+    ) -> None:
         with smart_open(self.file_name, "w", encoding=self.encoding) as changelog_file:
             partial_changelog: str | None = None
             if self.incremental:
@@ -123,29 +169,33 @@ def write_changelog(
                 changelog_out = "".join(new_lines)
                 partial_changelog = changelog_out
 
-            if changelog_hook:
-                changelog_out = changelog_hook(changelog_out, partial_changelog)
+            if self.cz.changelog_hook:
+                changelog_out = self.cz.changelog_hook(changelog_out, partial_changelog)
+
             changelog_file.write(changelog_out)
 
-    def export_template(self):
+    def _export_template(self) -> None:
         tpl = changelog.get_changelog_template(self.cz.template_loader, self.template)
-        src = Path(tpl.filename)
-        Path(self.export_template_to).write_text(src.read_text())
+        # TODO: fix the following type ignores
+        src = Path(tpl.filename)  # type: ignore[arg-type]
+        Path(self.export_template_to).write_text(src.read_text())  # type: ignore[arg-type]
 
-    def __call__(self):
+    def __call__(self) -> None:
         commit_parser = self.cz.commit_parser
         changelog_pattern = self.cz.changelog_pattern
         start_rev = self.start_rev
         unreleased_version = self.unreleased_version
         changelog_meta = changelog.Metadata()
-        change_type_map: dict | None = self.change_type_map
-        changelog_message_builder_hook: Callable | None = (
+        change_type_map: dict[str, str] | None = self.change_type_map
+        changelog_message_builder_hook: MessageBuilderHook | None = (
             self.cz.changelog_message_builder_hook
         )
-        merge_prerelease = self.merge_prerelease
+        changelog_release_hook: ChangelogReleaseHook | None = (
+            self.cz.changelog_release_hook
+        )
 
         if self.export_template_to:
-            return self.export_template()
+            return self._export_template()
 
         if not changelog_pattern or not commit_parser:
             raise NoPatternMapError(
@@ -158,33 +208,25 @@ def __call__(self):
         # Don't continue if no `file_name` specified.
         assert self.file_name
 
-        tags = changelog.get_version_tags(self.scheme, git.get_tags()) or []
-
+        tags = self.tag_rules.get_version_tags(git.get_tags(), warn=True)
         end_rev = ""
         if self.incremental:
             changelog_meta = self.changelog_format.get_metadata(self.file_name)
             if changelog_meta.latest_version:
-                latest_tag_version: str = bump.normalize_tag(
-                    changelog_meta.latest_version,
-                    tag_format=self.tag_format,
-                    scheme=self.scheme,
+                start_rev = self._find_incremental_rev(
+                    strip_local_version(changelog_meta.latest_version_tag or ""), tags
                 )
-                start_rev = self._find_incremental_rev(latest_tag_version, tags)
-
         if self.rev_range:
             start_rev, end_rev = changelog.get_oldest_and_newest_rev(
                 tags,
-                version=self.rev_range,
-                tag_format=self.tag_format,
-                scheme=self.scheme,
+                self.rev_range,
+                self.tag_rules,
             )
-
         commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order")
         if not commits and (
             self.current_version is None or not self.current_version.is_prerelease
         ):
             raise NoCommitsFoundError("No commits found")
-
         tree = changelog.generate_tree_from_commits(
             commits,
             tags,
@@ -193,21 +235,25 @@ def __call__(self):
             unreleased_version,
             change_type_map=change_type_map,
             changelog_message_builder_hook=changelog_message_builder_hook,
-            merge_prerelease=merge_prerelease,
-            scheme=self.scheme,
+            changelog_release_hook=changelog_release_hook,
+            rules=self.tag_rules,
         )
         if self.change_type_order:
-            tree = changelog.order_changelog_tree(tree, self.change_type_order)
+            tree = changelog.generate_ordered_changelog_tree(
+                tree, self.change_type_order
+            )
 
         extras = self.cz.template_extras.copy()
         extras.update(self.config.settings["extras"])
         extras.update(self.extras)
         changelog_out = changelog.render_changelog(
             tree, loader=self.cz.template_loader, template=self.template, **extras
-        )
-        changelog_out = changelog_out.lstrip("\n")
+        ).lstrip("\n")
 
+        # Dry_run is executed here to avoid checking and reading the files
         if self.dry_run:
+            if self.cz.changelog_hook:
+                changelog_out = self.cz.changelog_hook(changelog_out, "")
             out.write(changelog_out)
             raise DryRunExit()
 
@@ -216,4 +262,4 @@ def __call__(self):
             with open(self.file_name, encoding=self.encoding) as changelog_file:
                 lines = changelog_file.readlines()
 
-        self.write_changelog(changelog_out, lines, changelog_meta)
+        self._write_changelog(changelog_out, lines, changelog_meta)
diff --git a/commitizen/commands/check.py b/commitizen/commands/check.py
index 6e98f8cb3f..69147bcfbe 100644
--- a/commitizen/commands/check.py
+++ b/commitizen/commands/check.py
@@ -1,9 +1,8 @@
 from __future__ import annotations
 
-import os
 import re
 import sys
-from typing import Any
+from typing import TypedDict
 
 from commitizen import factory, git, out
 from commitizen.config import BaseConfig
@@ -14,10 +13,20 @@
 )
 
 
+class CheckArgs(TypedDict, total=False):
+    commit_msg_file: str
+    commit_msg: str
+    rev_range: str
+    allow_abort: bool
+    message_length_limit: int
+    allowed_prefixes: list[str]
+    message: str
+
+
 class Check:
     """Check if the current commit msg matches the commitizen format."""
 
-    def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd()):
+    def __init__(self, config: BaseConfig, arguments: CheckArgs, *args: object) -> None:
         """Initial check command.
 
         Args:
@@ -25,15 +34,15 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd(
             arguments: All the flags provided by the user
             cwd: Current work directory
         """
-        self.commit_msg_file: str | None = arguments.get("commit_msg_file")
-        self.commit_msg: str | None = arguments.get("message")
-        self.rev_range: str | None = arguments.get("rev_range")
-        self.allow_abort: bool = bool(
+        self.commit_msg_file = arguments.get("commit_msg_file")
+        self.commit_msg = arguments.get("message")
+        self.rev_range = arguments.get("rev_range")
+        self.allow_abort = bool(
             arguments.get("allow_abort", config.settings["allow_abort"])
         )
+        self.max_msg_length = arguments.get("message_length_limit", 0)
 
         # we need to distinguish between None and [], which is a valid value
-
         allowed_prefixes = arguments.get("allowed_prefixes")
         self.allowed_prefixes: list[str] = (
             allowed_prefixes
@@ -45,22 +54,22 @@ def __init__(self, config: BaseConfig, arguments: dict[str, Any], cwd=os.getcwd(
 
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
-    def _valid_command_argument(self):
+    def _valid_command_argument(self) -> None:
         num_exclusive_args_provided = sum(
             arg is not None
             for arg in (self.commit_msg_file, self.commit_msg, self.rev_range)
         )
         if num_exclusive_args_provided == 0 and not sys.stdin.isatty():
-            self.commit_msg: str | None = sys.stdin.read()
+            self.commit_msg = sys.stdin.read()
         elif num_exclusive_args_provided != 1:
             raise InvalidCommandArgumentError(
                 "Only one of --rev-range, --message, and --commit-msg-file is permitted by check command! "
                 "See 'cz check -h' for more information"
             )
 
-    def __call__(self):
+    def __call__(self) -> None:
         """Validate if commit messages follows the conventional pattern.
 
         Raises:
@@ -70,40 +79,34 @@ def __call__(self):
         if not commits:
             raise NoCommitsFoundError(f"No commit found with range: '{self.rev_range}'")
 
-        pattern = self.cz.schema_pattern()
-        ill_formated_commits = [
-            commit
+        pattern = re.compile(self.cz.schema_pattern())
+        invalid_msgs_content = "\n".join(
+            f'commit "{commit.rev}": "{commit.message}"'
             for commit in commits
-            if not self.validate_commit_message(commit.message, pattern)
-        ]
-        displayed_msgs_content = "\n".join(
-            [
-                f'commit "{commit.rev}": "{commit.message}"'
-                for commit in ill_formated_commits
-            ]
+            if not self._validate_commit_message(commit.message, pattern)
         )
-        if displayed_msgs_content:
+        if invalid_msgs_content:
+            # TODO: capitalize the first letter of the error message for consistency in v5
             raise InvalidCommitMessageError(
                 "commit validation: failed!\n"
                 "please enter a commit message in the commitizen format.\n"
-                f"{displayed_msgs_content}\n"
-                f"pattern: {pattern}"
+                f"{invalid_msgs_content}\n"
+                f"pattern: {pattern.pattern}"
             )
         out.success("Commit validation: successful!")
 
-    def _get_commits(self):
-        msg = None
-        # Get commit message from file (--commit-msg-file)
-        if self.commit_msg_file is not None:
-            # Enter this branch if commit_msg_file is "".
-            with open(self.commit_msg_file, encoding=self.encoding) as commit_file:
-                msg = commit_file.read()
-        # Get commit message from command line (--message)
-        elif self.commit_msg is not None:
-            msg = self.commit_msg
-        if msg is not None:
-            msg = self._filter_comments(msg)
-            return [git.GitCommit(rev="", title="", body=msg)]
+    def _get_commit_message(self) -> str | None:
+        if self.commit_msg_file is None:
+            # Get commit message from command line (--message)
+            return self.commit_msg
+
+        with open(self.commit_msg_file, encoding=self.encoding) as commit_file:
+            # Get commit message from file (--commit-msg-file)
+            return commit_file.read()
+
+    def _get_commits(self) -> list[git.GitCommit]:
+        if (msg := self._get_commit_message()) is not None:
+            return [git.GitCommit(rev="", title="", body=self._filter_comments(msg))]
 
         # Get commit messages from git log (--rev-range)
         return git.get_commits(end=self.rev_range)
@@ -139,10 +142,18 @@ def _filter_comments(msg: str) -> str:
                 lines.append(line)
         return "\n".join(lines)
 
-    def validate_commit_message(self, commit_msg: str, pattern: str) -> bool:
+    def _validate_commit_message(
+        self, commit_msg: str, pattern: re.Pattern[str]
+    ) -> bool:
         if not commit_msg:
             return self.allow_abort
 
         if any(map(commit_msg.startswith, self.allowed_prefixes)):
             return True
-        return bool(re.match(pattern, commit_msg))
+
+        if self.max_msg_length:
+            msg_len = len(commit_msg.partition("\n")[0].strip())
+            if msg_len > self.max_msg_length:
+                return False
+
+        return bool(pattern.match(commit_msg))
diff --git a/commitizen/commands/commit.py b/commitizen/commands/commit.py
index b8e5eed6d4..19bb72fb00 100644
--- a/commitizen/commands/commit.py
+++ b/commitizen/commands/commit.py
@@ -1,14 +1,22 @@
+from __future__ import annotations
+
 import contextlib
 import os
+import shutil
+import subprocess
 import tempfile
+from pathlib import Path
+from typing import TypedDict
 
 import questionary
 
 from commitizen import factory, git, out
 from commitizen.config import BaseConfig
 from commitizen.cz.exceptions import CzException
+from commitizen.cz.utils import get_backup_file_path
 from commitizen.exceptions import (
     CommitError,
+    CommitMessageLengthExceededError,
     CustomError,
     DryRunExit,
     NoAnswersError,
@@ -20,36 +28,45 @@
 from commitizen.git import smart_open
 
 
+class CommitArgs(TypedDict, total=False):
+    all: bool
+    dry_run: bool
+    edit: bool
+    extra_cli_args: str
+    message_length_limit: int
+    no_retry: bool
+    signoff: bool
+    write_message_to_file: Path | None
+    retry: bool
+
+
 class Commit:
     """Show prompt for the user to create a guided commit."""
 
-    def __init__(self, config: BaseConfig, arguments: dict):
+    def __init__(self, config: BaseConfig, arguments: CommitArgs) -> None:
         if not git.is_git_project():
             raise NotAGitProjectError()
 
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
         self.arguments = arguments
-        self.temp_file: str = os.path.join(
-            tempfile.gettempdir(),
-            "cz.commit{user}.backup".format(user=os.environ.get("USER", "")),
-        )
+        self.temp_file: str = get_backup_file_path()
 
-    def read_backup_message(self) -> str:
+    def _read_backup_message(self) -> str | None:
         # Check the commit backup file exists
         if not os.path.isfile(self.temp_file):
-            raise NoCommitBackupError()
+            return None
 
         # Read commit message from backup
         with open(self.temp_file, encoding=self.encoding) as f:
             return f.read().strip()
 
-    def prompt_commit_questions(self) -> str:
+    def _prompt_commit_questions(self) -> str:
         # Prompt user for the commit message
         cz = self.cz
         questions = cz.questions()
-        for question in filter(lambda q: q["type"] == "list", questions):
+        for question in (q for q in questions if q["type"] == "list"):
             question["use_shortcuts"] = self.config.settings["use_shortcuts"]
         try:
             answers = questionary.prompt(questions, style=cz.style)
@@ -61,28 +78,70 @@ def prompt_commit_questions(self) -> str:
 
         if not answers:
             raise NoAnswersError()
-        return cz.message(answers)
 
-    def __call__(self):
-        dry_run: bool = self.arguments.get("dry_run")
+        message = cz.message(answers)
+        message_len = len(message.partition("\n")[0].strip())
+        message_length_limit = self.arguments.get("message_length_limit", 0)
+        if 0 < message_length_limit < message_len:
+            raise CommitMessageLengthExceededError(
+                f"Length of commit message exceeds limit ({message_len}/{message_length_limit})"
+            )
+
+        return message
+
+    def manual_edit(self, message: str) -> str:
+        editor = git.get_core_editor()
+        if editor is None:
+            raise RuntimeError("No 'editor' value given and no default available.")
+        exec_path = shutil.which(editor)
+        if exec_path is None:
+            raise RuntimeError(f"Editor '{editor}' not found.")
+        with tempfile.NamedTemporaryFile(mode="w", delete=False) as file:
+            file.write(message)
+        file_path = file.name
+        argv = [exec_path, file_path]
+        subprocess.call(argv)
+        with open(file_path) as temp_file:
+            message = temp_file.read().strip()
+        os.unlink(file.name)
+        return message
+
+    def _get_message(self) -> str:
+        if self.arguments.get("retry"):
+            m = self._read_backup_message()
+            if m is None:
+                raise NoCommitBackupError()
+            return m
+
+        if self.config.settings.get("retry_after_failure") and not self.arguments.get(
+            "no_retry"
+        ):
+            return self._read_backup_message() or self._prompt_commit_questions()
+        return self._prompt_commit_questions()
+
+    def __call__(self) -> None:
+        extra_args = self.arguments.get("extra_cli_args", "")
+        dry_run = bool(self.arguments.get("dry_run"))
         write_message_to_file = self.arguments.get("write_message_to_file")
+        signoff = bool(self.arguments.get("signoff"))
+
+        if signoff:
+            out.warn(
+                "Deprecated warning: `cz commit -s` is deprecated and will be removed in v5, please use `cz commit -- -s` instead."
+            )
 
-        is_all: bool = self.arguments.get("all")
-        if is_all:
-            c = git.add("-u")
+        if self.arguments.get("all"):
+            git.add("-u")
 
-        if git.is_staging_clean() and not dry_run:
+        if git.is_staging_clean() and not (dry_run or "--allow-empty" in extra_args):
             raise NothingToCommitError("No files added to staging!")
 
         if write_message_to_file is not None and write_message_to_file.is_dir():
             raise NotAllowed(f"{write_message_to_file} is a directory")
 
-        retry: bool = self.arguments.get("retry")
-
-        if retry:
-            m = self.read_backup_message()
-        else:
-            m = self.prompt_commit_questions()
+        m = self._get_message()
+        if self.arguments.get("edit"):
+            m = self.manual_edit(m)
 
         out.info(f"\n{m}\n")
 
@@ -93,20 +152,10 @@ def __call__(self):
         if dry_run:
             raise DryRunExit()
 
-        signoff: bool = (
-            self.arguments.get("signoff") or self.config.settings["always_signoff"]
-        )
-
-        if signoff:
-            out.warn(
-                "signoff mechanic is deprecated, please use `cz commit -- -s` instead."
-            )
-            extra_args = self.arguments.get("extra_cli_args", "--") + " -s"
-        else:
-            extra_args = self.arguments.get("extra_cli_args", "")
+        if self.config.settings["always_signoff"] or signoff:
+            extra_args = f"{extra_args} -s".strip()
 
         c = git.commit(m, args=extra_args)
-
         if c.return_code != 0:
             out.error(c.err)
 
@@ -116,11 +165,12 @@ def __call__(self):
 
             raise CommitError()
 
-        if "nothing added" in c.out or "no changes added to commit" in c.out:
+        if any(s in c.out for s in ("nothing added", "no changes added to commit")):
             out.error(c.out)
-        else:
-            with contextlib.suppress(FileNotFoundError):
-                os.remove(self.temp_file)
-            out.write(c.err)
-            out.write(c.out)
-            out.success("Commit successful!")
+            return
+
+        with contextlib.suppress(FileNotFoundError):
+            os.remove(self.temp_file)
+        out.write(c.err)
+        out.write(c.out)
+        out.success("Commit successful!")
diff --git a/commitizen/commands/example.py b/commitizen/commands/example.py
index e7abe7b318..ba9f34adc4 100644
--- a/commitizen/commands/example.py
+++ b/commitizen/commands/example.py
@@ -5,9 +5,9 @@
 class Example:
     """Show an example so people understands the rules."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write(self.cz.example())
diff --git a/commitizen/commands/info.py b/commitizen/commands/info.py
index afac9797e4..5ea8227313 100644
--- a/commitizen/commands/info.py
+++ b/commitizen/commands/info.py
@@ -5,9 +5,9 @@
 class Info:
     """Show in depth explanation of your rules."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write(self.cz.info())
diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py
index 62b3aec971..1207cd02ee 100644
--- a/commitizen/commands/init.py
+++ b/commitizen/commands/init.py
@@ -6,11 +6,12 @@
 
 import questionary
 import yaml
+
 from commitizen import cmd, factory, out
 from commitizen.__version__ import __version__
 from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig
 from commitizen.cz import registry
-from commitizen.defaults import DEFAULT_SETTINGS, config_files
+from commitizen.defaults import CONFIG_FILES, DEFAULT_SETTINGS
 from commitizen.exceptions import InitFailedError, NoAnswersError
 from commitizen.git import get_latest_tag_name, get_tag_names, smart_open
 from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme
@@ -23,6 +24,10 @@ class ProjectInfo:
     def has_pyproject(self) -> bool:
         return os.path.isfile("pyproject.toml")
 
+    @property
+    def has_uv_lock(self) -> bool:
+        return os.path.isfile("uv.lock")
+
     @property
     def has_setup(self) -> bool:
         return os.path.isfile("setup.py")
@@ -31,6 +36,10 @@ def has_setup(self) -> bool:
     def has_pre_commit_config(self) -> bool:
         return os.path.isfile(".pre-commit-config.yaml")
 
+    @property
+    def is_python_uv(self) -> bool:
+        return self.has_pyproject and self.has_uv_lock
+
     @property
     def is_python_poetry(self) -> bool:
         if not self.has_pyproject:
@@ -70,13 +79,13 @@ def is_pre_commit_installed(self) -> bool:
 
 
 class Init:
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
         self.encoding = config.settings["encoding"]
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
         self.project_info = ProjectInfo()
 
-    def __call__(self):
+    def __call__(self) -> None:
         if self.config.path:
             out.line(f"Config file {self.config.path} already exists")
             return
@@ -97,7 +106,7 @@ def __call__(self):
             version_provider = self._ask_version_provider()  # select
             tag = self._ask_tag()  # confirm & select
             version_scheme = self._ask_version_scheme()  # select
-            version = get_version_scheme(self.config, version_scheme)(tag)
+            version = get_version_scheme(self.config.settings, version_scheme)(tag)
             tag_format = self._ask_tag_format(tag)  # confirm & text
             update_changelog_on_bump = self._ask_update_changelog_on_bump()  # confirm
             major_version_zero = self._ask_major_version_zero(version)  # confirm
@@ -111,7 +120,7 @@ def __call__(self):
             self.config = JsonConfig(data="{}", path=config_path)
         elif "yaml" in config_path:
             self.config = YAMLConfig(data="", path=config_path)
-        values_to_add = {}
+        values_to_add: dict[str, Any] = {}
         values_to_add["name"] = cz_name
         values_to_add["tag_format"] = tag_format
         values_to_add["version_scheme"] = version_scheme
@@ -156,7 +165,7 @@ def _ask_config_path(self) -> str:
 
         name: str = questionary.select(
             "Please choose a supported config file: ",
-            choices=config_files,
+            choices=CONFIG_FILES,
             default=default_path,
             style=self.cz.style,
         ).unsafe_ask()
@@ -186,9 +195,11 @@ def _ask_tag(self) -> str:
                 out.error("No Existing Tag. Set tag to v0.0.1")
                 return "0.0.1"
 
+            # the latest tag is most likely with the largest number. Thus list the tags in reverse order makes more sense
+            sorted_tags = sorted(tags, reverse=True)
             latest_tag = questionary.select(
                 "Please choose the latest tag: ",
-                choices=tags,
+                choices=sorted_tags,
                 style=self.cz.style,
             ).unsafe_ask()
 
@@ -196,7 +207,7 @@ def _ask_tag(self) -> str:
                 raise NoAnswersError("Tag is required!")
         return latest_tag
 
-    def _ask_tag_format(self, latest_tag) -> str:
+    def _ask_tag_format(self, latest_tag: str) -> str:
         is_correct_format = False
         if latest_tag.startswith("v"):
             tag_format = r"v$version"
@@ -225,6 +236,7 @@ def _ask_version_provider(self) -> str:
             "npm": "npm: Get and set version from package.json:project.version field",
             "pep621": "pep621: Get and set version from pyproject.toml:project.version field",
             "poetry": "poetry: Get and set version from pyproject.toml:tool.poetry.version field",
+            "uv": "uv: Get and Get and set version from pyproject.toml and uv.lock",
             "scm": "scm: Fetch the version from git and does not need to set it back",
         }
 
@@ -232,6 +244,8 @@ def _ask_version_provider(self) -> str:
         if self.project_info.is_python:
             if self.project_info.is_python_poetry:
                 default_val = "poetry"
+            elif self.project_info.is_python_uv:
+                default_val = "uv"
             else:
                 default_val = "pep621"
         elif self.project_info.is_rust_cargo:
@@ -288,7 +302,7 @@ def _ask_update_changelog_on_bump(self) -> bool:
         ).unsafe_ask()
         return update_changelog_on_bump
 
-    def _exec_install_pre_commit_hook(self, hook_types: list[str]):
+    def _exec_install_pre_commit_hook(self, hook_types: list[str]) -> None:
         cmd_str = self._gen_pre_commit_cmd(hook_types)
         c = cmd.run(cmd_str)
         if c.return_code != 0:
@@ -309,7 +323,7 @@ def _gen_pre_commit_cmd(self, hook_types: list[str]) -> str:
         )
         return cmd_str
 
-    def _install_pre_commit_hook(self, hook_types: list[str] | None = None):
+    def _install_pre_commit_hook(self, hook_types: list[str] | None = None) -> None:
         pre_commit_config_filename = ".pre-commit-config.yaml"
         cz_hook_config = {
             "repo": "https://github.com/commitizen-tools/commitizen",
@@ -355,6 +369,6 @@ def _install_pre_commit_hook(self, hook_types: list[str] | None = None):
         self._exec_install_pre_commit_hook(hook_types)
         out.write("commitizen pre-commit hook is now installed in your '.git'\n")
 
-    def _update_config_file(self, values: dict[str, Any]):
+    def _update_config_file(self, values: dict[str, Any]) -> None:
         for key, value in values.items():
             self.config.set_key(key, value)
diff --git a/commitizen/commands/list_cz.py b/commitizen/commands/list_cz.py
index 99701865af..412266f6c3 100644
--- a/commitizen/commands/list_cz.py
+++ b/commitizen/commands/list_cz.py
@@ -6,8 +6,8 @@
 class ListCz:
     """List currently installed rules."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write("\n".join(registry.keys()))
diff --git a/commitizen/commands/schema.py b/commitizen/commands/schema.py
index 0940648cde..a7aeb53569 100644
--- a/commitizen/commands/schema.py
+++ b/commitizen/commands/schema.py
@@ -5,9 +5,9 @@
 class Schema:
     """Show structure of the rule."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, *args: object) -> None:
         self.config: BaseConfig = config
-        self.cz = factory.commiter_factory(self.config)
+        self.cz = factory.committer_factory(self.config)
 
-    def __call__(self):
+    def __call__(self) -> None:
         out.write(self.cz.schema())
diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py
index 45d553c710..6b0aa331ad 100644
--- a/commitizen/commands/version.py
+++ b/commitizen/commands/version.py
@@ -1,5 +1,6 @@
 import platform
 import sys
+from typing import TypedDict
 
 from commitizen import out
 from commitizen.__version__ import __version__
@@ -7,16 +8,22 @@
 from commitizen.providers import get_provider
 
 
+class VersionArgs(TypedDict, total=False):
+    report: bool
+    project: bool
+    verbose: bool
+
+
 class Version:
     """Get the version of the installed commitizen or the current project."""
 
-    def __init__(self, config: BaseConfig, *args):
+    def __init__(self, config: BaseConfig, arguments: VersionArgs) -> None:
         self.config: BaseConfig = config
-        self.parameter = args[0]
+        self.parameter = arguments
         self.operating_system = platform.system()
         self.python_version = sys.version
 
-    def __call__(self):
+    def __call__(self) -> None:
         if self.parameter.get("report"):
             out.write(f"Commitizen Version: {__version__}")
             out.write(f"Python Version: {self.python_version}")
diff --git a/commitizen/config/__init__.py b/commitizen/config/__init__.py
index 09e38ca96e..9dfd591c40 100644
--- a/commitizen/config/__init__.py
+++ b/commitizen/config/__init__.py
@@ -3,6 +3,7 @@
 from pathlib import Path
 
 from commitizen import defaults, git
+from commitizen.exceptions import ConfigFileIsEmpty, ConfigFileNotFound
 
 from .base_config import BaseConfig
 from .json_config import JsonConfig
@@ -10,19 +11,26 @@
 from .yaml_config import YAMLConfig
 
 
-def read_cfg() -> BaseConfig:
+def read_cfg(filepath: str | None = None) -> BaseConfig:
     conf = BaseConfig()
 
-    git_project_root = git.find_git_project_root()
-    cfg_search_paths = [Path(".")]
-    if git_project_root:
-        cfg_search_paths.append(git_project_root)
+    if filepath is not None:
+        if not Path(filepath).exists():
+            raise ConfigFileNotFound()
+
+        cfg_paths = (path for path in (Path(filepath),))
+    else:
+        git_project_root = git.find_git_project_root()
+        cfg_search_paths = [Path(".")]
+        if git_project_root:
+            cfg_search_paths.append(git_project_root)
+
+        cfg_paths = (
+            path / Path(filename)
+            for path in cfg_search_paths
+            for filename in defaults.CONFIG_FILES
+        )
 
-    cfg_paths = (
-        path / Path(filename)
-        for path in cfg_search_paths
-        for filename in defaults.config_files
-    )
     for filename in cfg_paths:
         if not filename.exists():
             continue
@@ -39,7 +47,9 @@ def read_cfg() -> BaseConfig:
         elif "yaml" in filename.suffix:
             _conf = YAMLConfig(data=data, path=filename)
 
-        if _conf.is_empty_config:
+        if filepath is not None and _conf.is_empty_config:
+            raise ConfigFileIsEmpty()
+        elif _conf.is_empty_config:
             continue
         else:
             conf = _conf
diff --git a/commitizen/config/base_config.py b/commitizen/config/base_config.py
index 478691aa14..4b8f5f05fc 100644
--- a/commitizen/config/base_config.py
+++ b/commitizen/config/base_config.py
@@ -1,12 +1,22 @@
 from __future__ import annotations
 
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 from commitizen.defaults import DEFAULT_SETTINGS, Settings
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class BaseConfig:
-    def __init__(self):
+    def __init__(self) -> None:
         self._settings: Settings = DEFAULT_SETTINGS.copy()
         self.encoding = self.settings["encoding"]
         self._path: Path | None = None
@@ -16,10 +26,14 @@ def settings(self) -> Settings:
         return self._settings
 
     @property
-    def path(self) -> Path | None:
-        return self._path
+    def path(self) -> Path:
+        return self._path  # type: ignore[return-value]
+
+    @path.setter
+    def path(self, path: str | Path) -> None:
+        self._path = Path(path)
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
@@ -30,8 +44,8 @@ def set_key(self, key, value):
     def update(self, data: Settings) -> None:
         self._settings.update(data)
 
-    def add_path(self, path: str | Path) -> None:
-        self._path = Path(path)
-
     def _parse_setting(self, data: bytes | str) -> None:
         raise NotImplementedError()
+
+    def init_empty_config_content(self) -> None:
+        raise NotImplementedError()
diff --git a/commitizen/config/json_config.py b/commitizen/config/json_config.py
index cf554922d6..be1f1c36b0 100644
--- a/commitizen/config/json_config.py
+++ b/commitizen/config/json_config.py
@@ -2,25 +2,35 @@
 
 import json
 from pathlib import Path
-from commitizen.exceptions import InvalidConfigurationError
+from typing import TYPE_CHECKING, Any
 
+from commitizen.exceptions import InvalidConfigurationError
 from commitizen.git import smart_open
 
 from .base_config import BaseConfig
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class JsonConfig(BaseConfig):
-    def __init__(self, *, data: bytes | str, path: Path | str):
+    def __init__(self, *, data: bytes | str, path: Path | str) -> None:
         super().__init__()
         self.is_empty_config = False
-        self.add_path(path)
+        self.path = path
         self._parse_setting(data)
 
-    def init_empty_config_content(self):
+    def init_empty_config_content(self) -> None:
         with smart_open(self.path, "a", encoding=self.encoding) as json_file:
             json.dump({"commitizen": {}}, json_file)
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
diff --git a/commitizen/config/toml_config.py b/commitizen/config/toml_config.py
index d45860d650..2164d3f992 100644
--- a/commitizen/config/toml_config.py
+++ b/commitizen/config/toml_config.py
@@ -2,21 +2,32 @@
 
 import os
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 from tomlkit import exceptions, parse, table
 
 from commitizen.exceptions import InvalidConfigurationError
+
 from .base_config import BaseConfig
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class TomlConfig(BaseConfig):
-    def __init__(self, *, data: bytes | str, path: Path | str):
+    def __init__(self, *, data: bytes | str, path: Path | str) -> None:
         super().__init__()
         self.is_empty_config = False
-        self.add_path(path)
+        self.path = path
         self._parse_setting(data)
 
-    def init_empty_config_content(self):
+    def init_empty_config_content(self) -> None:
         if os.path.isfile(self.path):
             with open(self.path, "rb") as input_toml_file:
                 parser = parse(input_toml_file.read())
@@ -26,10 +37,10 @@ def init_empty_config_content(self):
         with open(self.path, "wb") as output_toml_file:
             if parser.get("tool") is None:
                 parser["tool"] = table()
-            parser["tool"]["commitizen"] = table()
+            parser["tool"]["commitizen"] = table()  # type: ignore[index]
             output_toml_file.write(parser.as_string().encode(self.encoding))
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
@@ -38,7 +49,7 @@ def set_key(self, key, value):
         with open(self.path, "rb") as f:
             parser = parse(f.read())
 
-        parser["tool"]["commitizen"][key] = value
+        parser["tool"]["commitizen"][key] = value  # type: ignore[index]
         with open(self.path, "wb") as f:
             f.write(parser.as_string().encode(self.encoding))
         return self
@@ -57,6 +68,6 @@ def _parse_setting(self, data: bytes | str) -> None:
             raise InvalidConfigurationError(f"Failed to parse {self.path}: {e}")
 
         try:
-            self.settings.update(doc["tool"]["commitizen"])  # type: ignore
+            self.settings.update(doc["tool"]["commitizen"])  # type: ignore[index,typeddict-item] # TODO: fix this
         except exceptions.NonExistentKey:
             self.is_empty_config = True
diff --git a/commitizen/config/yaml_config.py b/commitizen/config/yaml_config.py
index 396e2eba11..f2a79e6937 100644
--- a/commitizen/config/yaml_config.py
+++ b/commitizen/config/yaml_config.py
@@ -1,23 +1,33 @@
 from __future__ import annotations
 
 from pathlib import Path
+from typing import TYPE_CHECKING, Any
 
 import yaml
 
-from commitizen.git import smart_open
 from commitizen.exceptions import InvalidConfigurationError
+from commitizen.git import smart_open
 
 from .base_config import BaseConfig
 
+if TYPE_CHECKING:
+    import sys
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
 
 class YAMLConfig(BaseConfig):
-    def __init__(self, *, data: bytes | str, path: Path | str):
+    def __init__(self, *, data: bytes | str, path: Path | str) -> None:
         super().__init__()
         self.is_empty_config = False
-        self.add_path(path)
+        self.path = path
         self._parse_setting(data)
 
-    def init_empty_config_content(self):
+    def init_empty_config_content(self) -> None:
         with smart_open(self.path, "a", encoding=self.encoding) as json_file:
             yaml.dump({"commitizen": {}}, json_file, explicit_start=True)
 
@@ -41,7 +51,7 @@ def _parse_setting(self, data: bytes | str) -> None:
         except (KeyError, TypeError):
             self.is_empty_config = True
 
-    def set_key(self, key, value):
+    def set_key(self, key: str, value: Any) -> Self:
         """Set or update a key in the conf.
 
         For now only strings are supported.
diff --git a/commitizen/cz/__init__.py b/commitizen/cz/__init__.py
index f889dbd842..cb17fe32cd 100644
--- a/commitizen/cz/__init__.py
+++ b/commitizen/cz/__init__.py
@@ -2,10 +2,14 @@
 
 import importlib
 import pkgutil
+import sys
 import warnings
-from typing import Iterable
+from collections.abc import Iterable
 
-import importlib_metadata as metadata
+if sys.version_info >= (3, 10):
+    from importlib import metadata
+else:
+    import importlib_metadata as metadata
 
 from commitizen.cz.base import BaseCommitizen
 
diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py
index d9bf5ea4a0..cdc1476694 100644
--- a/commitizen/cz/base.py
+++ b/commitizen/cz/base.py
@@ -1,14 +1,27 @@
 from __future__ import annotations
 
 from abc import ABCMeta, abstractmethod
-from typing import Any, Callable
+from collections.abc import Iterable, Mapping
+from typing import Any, Callable, Protocol
 
 from jinja2 import BaseLoader, PackageLoader
 from prompt_toolkit.styles import Style, merge_styles
 
 from commitizen import git
 from commitizen.config.base_config import BaseConfig
-from commitizen.defaults import Questions
+from commitizen.question import CzQuestion
+
+
+class MessageBuilderHook(Protocol):
+    def __call__(
+        self, message: dict[str, Any], commit: git.GitCommit
+    ) -> dict[str, Any] | Iterable[dict[str, Any]] | None: ...
+
+
+class ChangelogReleaseHook(Protocol):
+    def __call__(
+        self, release: dict[str, Any], tag: git.GitTag | None
+    ) -> dict[str, Any]: ...
 
 
 class BaseCommitizen(metaclass=ABCMeta):
@@ -37,58 +50,52 @@ class BaseCommitizen(metaclass=ABCMeta):
     change_type_order: list[str] | None = None
 
     # Executed per message parsed by the commitizen
-    changelog_message_builder_hook: None | (
-        Callable[[dict, git.GitCommit], dict]
-    ) = None
+    changelog_message_builder_hook: MessageBuilderHook | None = None
 
     # Executed only at the end of the changelog generation
     changelog_hook: Callable[[str, str | None], str] | None = None
 
+    # Executed for each release in the changelog
+    changelog_release_hook: ChangelogReleaseHook | None = None
+
     # Plugins can override templates and provide extra template data
     template_loader: BaseLoader = PackageLoader("commitizen", "templates")
     template_extras: dict[str, Any] = {}
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         self.config = config
         if not self.config.settings.get("style"):
             self.config.settings.update({"style": BaseCommitizen.default_style_config})
 
     @abstractmethod
-    def questions(self) -> Questions:
+    def questions(self) -> Iterable[CzQuestion]:
         """Questions regarding the commit message."""
 
     @abstractmethod
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping[str, Any]) -> str:
         """Format your git message."""
 
     @property
-    def style(self):
+    def style(self) -> Style:
         return merge_styles(
             [
                 Style(BaseCommitizen.default_style_config),
                 Style(self.config.settings["style"]),
             ]
-        )
+        )  # type: ignore[return-value]
 
-    def example(self) -> str | None:
+    def example(self) -> str:
         """Example of the commit message."""
         raise NotImplementedError("Not Implemented yet")
 
-    def schema(self) -> str | None:
+    def schema(self) -> str:
         """Schema definition of the commit message."""
         raise NotImplementedError("Not Implemented yet")
 
-    def schema_pattern(self) -> str | None:
+    def schema_pattern(self) -> str:
         """Regex matching the schema used for message validation."""
         raise NotImplementedError("Not Implemented yet")
 
-    def info(self) -> str | None:
+    def info(self) -> str:
         """Information about the standardized commit message."""
         raise NotImplementedError("Not Implemented yet")
-
-    def process_commit(self, commit: str) -> str:
-        """Process commit for changelog.
-
-        If not overwritten, it returns the first line of commit.
-        """
-        return commit.split("\n")[0]
diff --git a/commitizen/cz/conventional_commits/conventional_commits.py b/commitizen/cz/conventional_commits/conventional_commits.py
index f927a94fdd..6893423478 100644
--- a/commitizen/cz/conventional_commits/conventional_commits.py
+++ b/commitizen/cz/conventional_commits/conventional_commits.py
@@ -1,47 +1,46 @@
 import os
-import re
+from typing import TypedDict
 
 from commitizen import defaults
 from commitizen.cz.base import BaseCommitizen
 from commitizen.cz.utils import multiple_line_breaker, required_validator
-from commitizen.defaults import Questions
+from commitizen.question import CzQuestion
 
 __all__ = ["ConventionalCommitsCz"]
 
 
-def parse_scope(text):
-    if not text:
-        return ""
+def _parse_scope(text: str) -> str:
+    return "-".join(text.strip().split())
 
-    scope = text.strip().split()
-    if len(scope) == 1:
-        return scope[0]
 
-    return "-".join(scope)
+def _parse_subject(text: str) -> str:
+    return required_validator(text.strip(".").strip(), msg="Subject is required.")
 
 
-def parse_subject(text):
-    if isinstance(text, str):
-        text = text.strip(".").strip()
-
-    return required_validator(text, msg="Subject is required.")
+class ConventionalCommitsAnswers(TypedDict):
+    prefix: str
+    scope: str
+    subject: str
+    body: str
+    footer: str
+    is_breaking_change: bool
 
 
 class ConventionalCommitsCz(BaseCommitizen):
-    bump_pattern = defaults.bump_pattern
-    bump_map = defaults.bump_map
-    bump_map_major_version_zero = defaults.bump_map_major_version_zero
-    commit_parser = defaults.commit_parser
+    bump_pattern = defaults.BUMP_PATTERN
+    bump_map = defaults.BUMP_MAP
+    bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO
+    commit_parser = r"^((?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?|\w+!):\s(?P<message>.*)?"
     change_type_map = {
         "feat": "Feat",
         "fix": "Fix",
         "refactor": "Refactor",
         "perf": "Perf",
     }
-    changelog_pattern = defaults.bump_pattern
+    changelog_pattern = defaults.BUMP_PATTERN
 
-    def questions(self) -> Questions:
-        questions: Questions = [
+    def questions(self) -> list[CzQuestion]:
+        return [
             {
                 "type": "list",
                 "name": "prefix",
@@ -86,9 +85,7 @@ def questions(self) -> Questions:
                     },
                     {
                         "value": "test",
-                        "name": (
-                            "test: Adding missing or correcting " "existing tests"
-                        ),
+                        "name": ("test: Adding missing or correcting existing tests"),
                         "key": "t",
                     },
                     {
@@ -102,7 +99,7 @@ def questions(self) -> Questions:
                     {
                         "value": "ci",
                         "name": (
-                            "ci: Changes to our CI configuration files and "
+                            "ci: Changes to CI configuration files and "
                             "scripts (example scopes: GitLabCI)"
                         ),
                         "key": "c",
@@ -115,12 +112,12 @@ def questions(self) -> Questions:
                 "message": (
                     "What is the scope of this change? (class or file name): (press [enter] to skip)\n"
                 ),
-                "filter": parse_scope,
+                "filter": _parse_scope,
             },
             {
                 "type": "input",
                 "name": "subject",
-                "filter": parse_subject,
+                "filter": _parse_subject,
                 "message": (
                     "Write a short and imperative summary of the code changes: (lower case and no period)\n"
                 ),
@@ -135,8 +132,8 @@ def questions(self) -> Questions:
             },
             {
                 "type": "confirm",
-                "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer",
                 "name": "is_breaking_change",
+                "message": "Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer",
                 "default": False,
             },
             {
@@ -148,9 +145,8 @@ def questions(self) -> Questions:
                 ),
             },
         ]
-        return questions
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: ConventionalCommitsAnswers) -> str:  # type: ignore[override]
         prefix = answers["prefix"]
         scope = answers["scope"]
         subject = answers["subject"]
@@ -167,9 +163,7 @@ def message(self, answers: dict) -> str:
         if footer:
             footer = f"\n\n{footer}"
 
-        message = f"{prefix}{scope}: {subject}{body}{footer}"
-
-        return message
+        return f"{prefix}{scope}: {subject}{body}{footer}"
 
     def example(self) -> str:
         return (
@@ -190,25 +184,32 @@ def schema(self) -> str:
         )
 
     def schema_pattern(self) -> str:
-        PATTERN = (
+        change_types = (
+            "build",
+            "bump",
+            "chore",
+            "ci",
+            "docs",
+            "feat",
+            "fix",
+            "perf",
+            "refactor",
+            "revert",
+            "style",
+            "test",
+        )
+        return (
             r"(?s)"  # To explicitly make . match new line
-            r"(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert|bump)"  # type
-            r"(\(\S+\))?!?:"  # scope
-            r"( [^\n\r]+)"  # subject
+            r"(" + "|".join(change_types) + r")"  # type
+            r"(\(\S+\))?"  # scope
+            r"!?"
+            r": "
+            r"([^\n\r]+)"  # subject
             r"((\n\n.*)|(\s*))?$"
         )
-        return PATTERN
 
     def info(self) -> str:
         dir_path = os.path.dirname(os.path.realpath(__file__))
         filepath = os.path.join(dir_path, "conventional_commits_info.txt")
         with open(filepath, encoding=self.config.settings["encoding"]) as f:
-            content = f.read()
-        return content
-
-    def process_commit(self, commit: str) -> str:
-        pat = re.compile(self.schema_pattern())
-        m = re.match(pat, commit)
-        if m is None:
-            return ""
-        return m.group(3).strip()
+            return f.read()
diff --git a/commitizen/cz/customize/__init__.py b/commitizen/cz/customize/__init__.py
index c5af001a79..0aedb9337a 100644
--- a/commitizen/cz/customize/__init__.py
+++ b/commitizen/cz/customize/__init__.py
@@ -1 +1 @@
-from .customize import CustomizeCommitsCz  # noqa
+from .customize import CustomizeCommitsCz  # noqa: F401
diff --git a/commitizen/cz/customize/customize.py b/commitizen/cz/customize/customize.py
index 5c3b4e76b4..dde2685496 100644
--- a/commitizen/cz/customize/customize.py
+++ b/commitizen/cz/customize/customize.py
@@ -1,89 +1,72 @@
 from __future__ import annotations
 
-try:
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, Any
+
+from commitizen.question import CzQuestion
+
+if TYPE_CHECKING:
     from jinja2 import Template
-except ImportError:
-    from string import Template  # type: ignore
+else:
+    try:
+        from jinja2 import Template
+    except ImportError:
+        from string import Template
 
 
 from commitizen import defaults
 from commitizen.config import BaseConfig
 from commitizen.cz.base import BaseCommitizen
-from commitizen.defaults import Questions
 from commitizen.exceptions import MissingCzCustomizeConfigError
 
 __all__ = ["CustomizeCommitsCz"]
 
 
 class CustomizeCommitsCz(BaseCommitizen):
-    bump_pattern = defaults.bump_pattern
-    bump_map = defaults.bump_map
-    bump_map_major_version_zero = defaults.bump_map_major_version_zero
-    change_type_order = defaults.change_type_order
+    bump_pattern = defaults.BUMP_PATTERN
+    bump_map = defaults.BUMP_MAP
+    bump_map_major_version_zero = defaults.BUMP_MAP_MAJOR_VERSION_ZERO
+    change_type_order = defaults.CHANGE_TYPE_ORDER
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         super().__init__(config)
 
         if "customize" not in self.config.settings:
             raise MissingCzCustomizeConfigError()
         self.custom_settings = self.config.settings["customize"]
 
-        custom_bump_pattern = self.custom_settings.get("bump_pattern")
-        if custom_bump_pattern:
-            self.bump_pattern = custom_bump_pattern
-
-        custom_bump_map = self.custom_settings.get("bump_map")
-        if custom_bump_map:
-            self.bump_map = custom_bump_map
-
-        custom_bump_map_major_version_zero = self.custom_settings.get(
-            "bump_map_major_version_zero"
-        )
-        if custom_bump_map_major_version_zero:
-            self.bump_map_major_version_zero = custom_bump_map_major_version_zero
-
-        custom_change_type_order = self.custom_settings.get("change_type_order")
-        if custom_change_type_order:
-            self.change_type_order = custom_change_type_order
-
-        commit_parser = self.custom_settings.get("commit_parser")
-        if commit_parser:
-            self.commit_parser = commit_parser
-
-        changelog_pattern = self.custom_settings.get("changelog_pattern")
-        if changelog_pattern:
-            self.changelog_pattern = changelog_pattern
-
-        change_type_map = self.custom_settings.get("change_type_map")
-        if change_type_map:
-            self.change_type_map = change_type_map
-
-    def questions(self) -> Questions:
-        return self.custom_settings.get("questions", [{}])
-
-    def message(self, answers: dict) -> str:
+        for attr_name in [
+            "bump_pattern",
+            "bump_map",
+            "bump_map_major_version_zero",
+            "change_type_order",
+            "commit_parser",
+            "changelog_pattern",
+            "change_type_map",
+        ]:
+            if value := self.custom_settings.get(attr_name):
+                setattr(self, attr_name, value)
+
+    def questions(self) -> list[CzQuestion]:
+        return self.custom_settings.get("questions", [{}])  # type: ignore[return-value]
+
+    def message(self, answers: Mapping[str, Any]) -> str:
         message_template = Template(self.custom_settings.get("message_template", ""))
         if getattr(Template, "substitute", None):
-            return message_template.substitute(**answers)  # type: ignore
-        else:
-            return message_template.render(**answers)
+            return message_template.substitute(**answers)  # type: ignore[attr-defined,no-any-return] # pragma: no cover # TODO: check if we can fix this
+        return message_template.render(**answers)
 
-    def example(self) -> str | None:
-        return self.custom_settings.get("example")
+    def example(self) -> str:
+        return self.custom_settings.get("example") or ""
 
-    def schema_pattern(self) -> str | None:
-        return self.custom_settings.get("schema_pattern")
+    def schema_pattern(self) -> str:
+        return self.custom_settings.get("schema_pattern") or ""
 
-    def schema(self) -> str | None:
-        return self.custom_settings.get("schema")
+    def schema(self) -> str:
+        return self.custom_settings.get("schema") or ""
 
-    def info(self) -> str | None:
-        info_path = self.custom_settings.get("info_path")
-        info = self.custom_settings.get("info")
-        if info_path:
+    def info(self) -> str:
+        if info_path := self.custom_settings.get("info_path"):
             with open(info_path, encoding=self.config.settings["encoding"]) as f:
-                content = f.read()
-            return content
-        elif info:
-            return info
-        return None
+                return f.read()
+        return self.custom_settings.get("info") or ""
diff --git a/commitizen/cz/exceptions.py b/commitizen/cz/exceptions.py
index 2597b68813..d74f39aaaa 100644
--- a/commitizen/cz/exceptions.py
+++ b/commitizen/cz/exceptions.py
@@ -1,6 +1,4 @@
-class CzException(Exception):
-    ...
+class CzException(Exception): ...
 
 
-class AnswerRequiredError(CzException):
-    ...
+class AnswerRequiredError(CzException): ...
diff --git a/commitizen/cz/jira/jira.py b/commitizen/cz/jira/jira.py
index a4fdcfa09e..4e6024ff74 100644
--- a/commitizen/cz/jira/jira.py
+++ b/commitizen/cz/jira/jira.py
@@ -1,14 +1,15 @@
 import os
+from collections.abc import Mapping
 
 from commitizen.cz.base import BaseCommitizen
-from commitizen.defaults import Questions
+from commitizen.question import CzQuestion
 
 __all__ = ["JiraSmartCz"]
 
 
 class JiraSmartCz(BaseCommitizen):
-    def questions(self) -> Questions:
-        questions = [
+    def questions(self) -> list[CzQuestion]:
+        return [
             {
                 "type": "input",
                 "name": "message",
@@ -42,20 +43,12 @@ def questions(self) -> Questions:
                 "filter": lambda x: "#comment " + x if x else "",
             },
         ]
-        return questions
 
-    def message(self, answers) -> str:
+    def message(self, answers: Mapping[str, str]) -> str:
         return " ".join(
-            filter(
-                bool,
-                [
-                    answers["message"],
-                    answers["issues"],
-                    answers["workflow"],
-                    answers["time"],
-                    answers["comment"],
-                ],
-            )
+            x
+            for k in ("message", "issues", "workflow", "time", "comment")
+            if (x := answers.get(k))
         )
 
     def example(self) -> str:
@@ -68,7 +61,7 @@ def example(self) -> str:
         )
 
     def schema(self) -> str:
-        return "<ignored text> <ISSUE_KEY> <ignored text> #<COMMAND> <optional COMMAND_ARGUMENTS>"  # noqa
+        return "<ignored text> <ISSUE_KEY> <ignored text> #<COMMAND> <optional COMMAND_ARGUMENTS>"
 
     def schema_pattern(self) -> str:
         return r".*[A-Z]{2,}\-[0-9]+( #| .* #).+( #.+)*"
@@ -77,5 +70,4 @@ def info(self) -> str:
         dir_path = os.path.dirname(os.path.realpath(__file__))
         filepath = os.path.join(dir_path, "jira_info.txt")
         with open(filepath, encoding=self.config.settings["encoding"]) as f:
-            content = f.read()
-        return content
+            return f.read()
diff --git a/commitizen/cz/utils.py b/commitizen/cz/utils.py
index ea13be22c5..a6f687226c 100644
--- a/commitizen/cz/utils.py
+++ b/commitizen/cz/utils.py
@@ -1,11 +1,31 @@
+import os
+import re
+import tempfile
+
+from commitizen import git
 from commitizen.cz import exceptions
 
+_RE_LOCAL_VERSION = re.compile(r"\+.+")
+
 
-def required_validator(answer, msg=None):
+def required_validator(answer: str, msg: object = None) -> str:
     if not answer:
         raise exceptions.AnswerRequiredError(msg)
     return answer
 
 
-def multiple_line_breaker(answer, sep="|"):
+def multiple_line_breaker(answer: str, sep: str = "|") -> str:
     return "\n".join(line.strip() for line in answer.split(sep) if line)
+
+
+def strip_local_version(version: str) -> str:
+    return _RE_LOCAL_VERSION.sub("", version)
+
+
+def get_backup_file_path() -> str:
+    project_root = git.find_git_project_root()
+    project = project_root.as_posix().replace("/", "%") if project_root else ""
+
+    user = os.environ.get("USER", "")
+
+    return os.path.join(tempfile.gettempdir(), f"cz.commit%{user}%{project}.backup")
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index 7aa2c793c5..94d4d97b22 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -1,13 +1,12 @@
 from __future__ import annotations
 
 import pathlib
+import warnings
 from collections import OrderedDict
-from typing import Any, Iterable, MutableMapping
+from collections.abc import Iterable, MutableMapping, Sequence
+from typing import Any, TypedDict
 
-from typing import TypedDict
-
-# Type
-Questions = Iterable[MutableMapping[str, Any]]
+from commitizen.question import CzQuestion
 
 
 class CzSettings(TypedDict, total=False):
@@ -16,7 +15,7 @@ class CzSettings(TypedDict, total=False):
     bump_map_major_version_zero: OrderedDict[str, str]
     change_type_order: list[str]
 
-    questions: Questions
+    questions: Iterable[CzQuestion]
     example: str | None
     schema_pattern: str | None
     schema: str | None
@@ -29,54 +28,63 @@ class CzSettings(TypedDict, total=False):
 
 
 class Settings(TypedDict, total=False):
-    name: str
-    version: str | None
-    version_files: list[str]
-    version_provider: str | None
-    version_scheme: str | None
-    version_type: str | None
-    tag_format: str
-    bump_message: str | None
     allow_abort: bool
     allowed_prefixes: list[str]
+    always_signoff: bool
+    annotated_tag: bool
+    bump_message: str | None
+    change_type_map: dict[str, str]
     changelog_file: str
     changelog_format: str | None
     changelog_incremental: bool
-    changelog_start_rev: str | None
     changelog_merge_prerelease: bool
-    update_changelog_on_bump: bool
-    use_shortcuts: bool
-    style: list[tuple[str, str]] | None
+    changelog_start_rev: str | None
     customize: CzSettings
+    encoding: str
+    extras: dict[str, Any]
+    gpg_sign: bool
+    ignored_tag_formats: Sequence[str]
+    legacy_tag_formats: Sequence[str]
     major_version_zero: bool
-    pre_bump_hooks: list[str] | None
+    name: str
     post_bump_hooks: list[str] | None
+    pre_bump_hooks: list[str] | None
     prerelease_offset: int
-    encoding: str
-    always_signoff: bool
+    retry_after_failure: bool
+    style: list[tuple[str, str]]
+    tag_format: str
     template: str | None
-    extras: dict[str, Any]
+    update_changelog_on_bump: bool
+    use_shortcuts: bool
+    version_files: list[str]
+    version_provider: str | None
+    version_scheme: str | None
+    version_type: str | None
+    version: str | None
 
 
-name: str = "cz_conventional_commits"
-config_files: list[str] = [
+CONFIG_FILES: list[str] = [
     "pyproject.toml",
     ".cz.toml",
     ".cz.json",
     "cz.json",
     ".cz.yaml",
     "cz.yaml",
+    "cz.toml",
 ]
-encoding: str = "utf-8"
+ENCODING = "utf-8"
 
 DEFAULT_SETTINGS: Settings = {
-    "name": name,
+    "name": "cz_conventional_commits",
     "version": None,
     "version_files": [],
     "version_provider": "commitizen",
     "version_scheme": None,
     "tag_format": "$version",  # example v$version
+    "legacy_tag_formats": [],
+    "ignored_tag_formats": [],
     "bump_message": None,  # bumped v$current_version to $new_version
+    "retry_after_failure": False,
     "allow_abort": False,
     "allowed_prefixes": [
         "Merge",
@@ -96,7 +104,7 @@ class Settings(TypedDict, total=False):
     "pre_bump_hooks": [],
     "post_bump_hooks": [],
     "prerelease_offset": 0,
-    "encoding": encoding,
+    "encoding": ENCODING,
     "always_signoff": False,
     "template": None,  # default provided by plugin
     "extras": {},
@@ -108,8 +116,8 @@ class Settings(TypedDict, total=False):
 
 CHANGELOG_FORMAT = "markdown"
 
-bump_pattern = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):"
-bump_map = OrderedDict(
+BUMP_PATTERN = r"^((BREAKING[\-\ ]CHANGE|\w+)(\(.+\))?!?):"
+BUMP_MAP = OrderedDict(
     (
         (r"^.+!$", MAJOR),
         (r"^BREAKING[\-\ ]CHANGE", MAJOR),
@@ -119,7 +127,7 @@ class Settings(TypedDict, total=False):
         (r"^perf", PATCH),
     )
 )
-bump_map_major_version_zero = OrderedDict(
+BUMP_MAP_MAJOR_VERSION_ZERO = OrderedDict(
     (
         (r"^.+!$", MINOR),
         (r"^BREAKING[\-\ ]CHANGE", MINOR),
@@ -129,7 +137,55 @@ class Settings(TypedDict, total=False):
         (r"^perf", PATCH),
     )
 )
-change_type_order = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]
-bump_message = "bump: version $current_version → $new_version"
+CHANGE_TYPE_ORDER = ["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]
+BUMP_MESSAGE = "bump: version $current_version → $new_version"
+
+
+def get_tag_regexes(
+    version_regex: str,
+) -> dict[str, str]:
+    regexes = {
+        "version": version_regex,
+        "major": r"(?P<major>\d+)",
+        "minor": r"(?P<minor>\d+)",
+        "patch": r"(?P<patch>\d+)",
+        "prerelease": r"(?P<prerelease>\w+\d+)?",
+        "devrelease": r"(?P<devrelease>\.dev\d+)?",
+    }
+    return {
+        **{f"${k}": v for k, v in regexes.items()},
+        **{f"${{{k}}}": v for k, v in regexes.items()},
+    }
+
+
+# Type
+Questions = Iterable[MutableMapping[str, Any]]  # TODO: remove this in v5
+
+
+def __getattr__(name: str) -> Any:
+    # PEP-562: deprecate module-level variable
 
-commit_parser = r"^((?P<change_type>feat|fix|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?|\w+!):\s(?P<message>.*)?"  # noqa
+    # {"deprecated key": (value, "new key")}
+    deprecated_vars = {
+        "bump_pattern": (BUMP_PATTERN, "BUMP_PATTERN"),
+        "bump_map": (BUMP_MAP, "BUMP_MAP"),
+        "bump_map_major_version_zero": (
+            BUMP_MAP_MAJOR_VERSION_ZERO,
+            "BUMP_MAP_MAJOR_VERSION_ZERO",
+        ),
+        "bump_message": (BUMP_MESSAGE, "BUMP_MESSAGE"),
+        "change_type_order": (CHANGE_TYPE_ORDER, "CHANGE_TYPE_ORDER"),
+        "encoding": (ENCODING, "ENCODING"),
+        "name": (DEFAULT_SETTINGS["name"], "DEFAULT_SETTINGS['name']"),
+        "Questions": (Questions, "Iterable[CzQuestion]"),
+    }
+    if name in deprecated_vars:
+        value, replacement = deprecated_vars[name]
+        warnings.warn(
+            f"{name} is deprecated and will be removed in v5. "
+            f"Use {replacement} instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return value
+    raise AttributeError(f"{name} is not an attribute of {__name__}")
diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py
index 31b867b8f9..8c0956be53 100644
--- a/commitizen/exceptions.py
+++ b/commitizen/exceptions.py
@@ -1,4 +1,5 @@
 import enum
+from typing import Any
 
 from commitizen import out
 
@@ -34,12 +35,15 @@ class ExitCode(enum.IntEnum):
     VERSION_PROVIDER_UNKNOWN = 27
     VERSION_SCHEME_UNKNOWN = 28
     CHANGELOG_FORMAT_UNKNOWN = 29
+    CONFIG_FILE_NOT_FOUND = 30
+    CONFIG_FILE_IS_EMPTY = 31
+    COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED = 32
 
 
 class CommitizenException(Exception):
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args: str, **kwargs: Any) -> None:
         self.output_method = kwargs.get("output_method") or out.error
-        self.exit_code = self.__class__.exit_code
+        self.exit_code: ExitCode = self.__class__.exit_code
         if args:
             self.message = args[0]
         elif hasattr(self.__class__, "message"):
@@ -47,14 +51,14 @@ def __init__(self, *args, **kwargs):
         else:
             self.message = ""
 
-    def __str__(self):
+    def __str__(self) -> str:
         return self.message
 
 
 class ExpectedExit(CommitizenException):
     exit_code = ExitCode.EXPECTED_EXIT
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, *args: str, **kwargs: Any) -> None:
         output_method = kwargs.get("output_method") or out.write
         kwargs["output_method"] = output_method
         super().__init__(*args, **kwargs)
@@ -64,6 +68,10 @@ class DryRunExit(ExpectedExit):
     pass
 
 
+class GetNextExit(ExpectedExit):
+    pass
+
+
 class NoneIncrementExit(CommitizenException):
     exit_code = ExitCode.NO_INCREMENT
 
@@ -189,3 +197,18 @@ class VersionSchemeUnknown(CommitizenException):
 class ChangelogFormatUnknown(CommitizenException):
     exit_code = ExitCode.CHANGELOG_FORMAT_UNKNOWN
     message = "Unknown changelog format identifier"
+
+
+class ConfigFileNotFound(CommitizenException):
+    exit_code = ExitCode.CONFIG_FILE_NOT_FOUND
+    message = "Cannot found the config file, please check your file path again."
+
+
+class ConfigFileIsEmpty(CommitizenException):
+    exit_code = ExitCode.CONFIG_FILE_IS_EMPTY
+    message = "Config file is empty, please check your file path again."
+
+
+class CommitMessageLengthExceededError(CommitizenException):
+    exit_code = ExitCode.COMMIT_MESSAGE_LENGTH_LIMIT_EXCEEDED
+    message = "Length of commit message exceeds the given limit."
diff --git a/commitizen/factory.py b/commitizen/factory.py
index 09af5fd0f7..d9e99fb771 100644
--- a/commitizen/factory.py
+++ b/commitizen/factory.py
@@ -4,16 +4,14 @@
 from commitizen.exceptions import NoCommitizenFoundException
 
 
-def commiter_factory(config: BaseConfig) -> BaseCommitizen:
+def committer_factory(config: BaseConfig) -> BaseCommitizen:
     """Return the correct commitizen existing in the registry."""
     name: str = config.settings["name"]
     try:
-        _cz = registry[name](config)
+        return registry[name](config)
     except KeyError:
         msg_error = (
             "The committer has not been found in the system.\n\n"
             f"Try running 'pip install {name}'\n"
         )
         raise NoCommitizenFoundException(msg_error)
-    else:
-        return _cz
diff --git a/commitizen/git.py b/commitizen/git.py
index 4c4dfdb961..8025041abb 100644
--- a/commitizen/git.py
+++ b/commitizen/git.py
@@ -2,77 +2,152 @@
 
 import os
 from enum import Enum
-from os import linesep
+from functools import lru_cache
 from pathlib import Path
 from tempfile import NamedTemporaryFile
 
 from commitizen import cmd, out
 from commitizen.exceptions import GitCommandError
 
-UNIX_EOL = "\n"
-WINDOWS_EOL = "\r\n"
 
-
-class EOLTypes(Enum):
+class EOLType(Enum):
     """The EOL type from `git config core.eol`."""
 
     LF = "lf"
     CRLF = "crlf"
     NATIVE = "native"
 
-    def get_eol_for_open(self) -> str:
+    @classmethod
+    def for_open(cls) -> str:
+        c = cmd.run("git config core.eol")
+        eol = c.out.strip().upper()
+        return cls._char_for_open()[cls._safe_cast(eol)]
+
+    @classmethod
+    def _safe_cast(cls, eol: str) -> EOLType:
+        try:
+            return cls[eol]
+        except KeyError:
+            return cls.NATIVE
+
+    @classmethod
+    @lru_cache
+    def _char_for_open(cls) -> dict[EOLType, str]:
         """Get the EOL character for `open()`."""
-        map = {
-            EOLTypes.CRLF: WINDOWS_EOL,
-            EOLTypes.LF: UNIX_EOL,
-            EOLTypes.NATIVE: linesep,
+        return {
+            cls.LF: "\n",
+            cls.CRLF: "\r\n",
+            cls.NATIVE: os.linesep,
         }
 
-        return map[self]
-
 
 class GitObject:
     rev: str
     name: str
     date: str
 
-    def __eq__(self, other) -> bool:
-        if not hasattr(other, "rev"):
-            return False
-        return self.rev == other.rev  # type: ignore
+    def __eq__(self, other: object) -> bool:
+        return hasattr(other, "rev") and self.rev == other.rev
 
 
 class GitCommit(GitObject):
     def __init__(
-        self, rev, title, body: str = "", author: str = "", author_email: str = ""
-    ):
+        self,
+        rev: str,
+        title: str,
+        body: str = "",
+        author: str = "",
+        author_email: str = "",
+        parents: list[str] | None = None,
+    ) -> None:
         self.rev = rev.strip()
         self.title = title.strip()
         self.body = body.strip()
         self.author = author.strip()
         self.author_email = author_email.strip()
+        self.parents = parents or []
 
     @property
-    def message(self):
+    def message(self) -> str:
         return f"{self.title}\n\n{self.body}".strip()
 
-    def __repr__(self):
+    @classmethod
+    def from_rev_and_commit(cls, rev_and_commit: str) -> GitCommit:
+        """Create a GitCommit instance from a formatted commit string.
+
+        This method parses a multi-line string containing commit information in the following format:
+        ```
+        <rev>
+        <parents>
+        <title>
+        <author>
+        <author_email>
+        <body_line_1>
+        <body_line_2>
+        ...
+        ```
+
+        Args:
+            rev_and_commit (str): A string containing commit information with fields separated by newlines.
+                - rev: The commit hash/revision
+                - parents: Space-separated list of parent commit hashes
+                - title: The commit title/message
+                - author: The commit author's name
+                - author_email: The commit author's email
+                - body: Optional multi-line commit body
+
+        Returns:
+            GitCommit: A new GitCommit instance with the parsed information.
+
+        Example:
+            >>> commit_str = '''abc123
+            ... def456 ghi789
+            ... feat: add new feature
+            ... John Doe
+            ... john@example.com
+            ... This is a detailed description
+            ... of the new feature'''
+            >>> commit = GitCommit.from_rev_and_commit(commit_str)
+            >>> commit.rev
+            'abc123'
+            >>> commit.title
+            'feat: add new feature'
+            >>> commit.parents
+            ['def456', 'ghi789']
+        """
+        rev, parents, title, author, author_email, *body_list = rev_and_commit.split(
+            "\n"
+        )
+        return cls(
+            rev=rev.strip(),
+            title=title.strip(),
+            body="\n".join(body_list).strip(),
+            author=author,
+            author_email=author_email,
+            parents=[p for p in parents.strip().split(" ") if p],
+        )
+
+    def __repr__(self) -> str:
         return f"{self.title} ({self.rev})"
 
 
 class GitTag(GitObject):
-    def __init__(self, name, rev, date):
+    def __init__(self, name: str, rev: str, date: str) -> None:
         self.rev = rev.strip()
         self.name = name.strip()
         self._date = date.strip()
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return f"GitTag('{self.name}', '{self.rev}', '{self.date}')"
 
     @property
-    def date(self):
+    def date(self) -> str:
         return self._date
 
+    @date.setter
+    def date(self, value: str) -> None:
+        self._date = value
+
     @classmethod
     def from_line(cls, line: str, inner_delimiter: str) -> GitTag:
         name, objectname, date, obj = line.split(inner_delimiter)
@@ -85,22 +160,18 @@ def from_line(cls, line: str, inner_delimiter: str) -> GitTag:
 def tag(
     tag: str, annotated: bool = False, signed: bool = False, msg: str | None = None
 ) -> cmd.Command:
-    _opt = ""
-    if annotated:
-        _opt = f"-a {tag} -m"
-    if signed:
-        _opt = f"-s {tag} -m"
+    if not annotated and not signed:
+        return cmd.run(f"git tag {tag}")
 
     # according to https://git-scm.com/book/en/v2/Git-Basics-Tagging,
     # we're not able to create lightweight tag with message.
     # by adding message, we make it a annotated tags
-    c = cmd.run(f'git tag {_opt} "{tag if _opt == "" or msg is None else msg}"')
-    return c
+    option = "-s" if signed else "-a"  # The else case is for annotated tags
+    return cmd.run(f'git tag {option} {tag} -m "{msg or tag}"')
 
 
-def add(args: str = "") -> cmd.Command:
-    c = cmd.run(f"git add {args}")
-    return c
+def add(*args: str) -> cmd.Command:
+    return cmd.run(f"git add {' '.join(args)}")
 
 
 def commit(
@@ -112,45 +183,40 @@ def commit(
     f.write(message.encode("utf-8"))
     f.close()
 
-    command = f"git commit {args} -F {f.name}"
-
-    if committer_date and os.name == "nt":  # pragma: no cover
-        # Using `cmd /v /c "{command}"` sets environment variables only for that command
-        command = f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"'
-    elif committer_date:
-        command = f"GIT_COMMITTER_DATE={committer_date} {command}"
-
+    command = _create_commit_cmd_string(args, committer_date, f.name)
     c = cmd.run(command)
     os.unlink(f.name)
     return c
 
 
+def _create_commit_cmd_string(args: str, committer_date: str | None, name: str) -> str:
+    command = f'git commit {args} -F "{name}"'
+    if not committer_date:
+        return command
+    if os.name != "nt":
+        return f"GIT_COMMITTER_DATE={committer_date} {command}"
+    # Using `cmd /v /c "{command}"` sets environment variables only for that command
+    return f'cmd /v /c "set GIT_COMMITTER_DATE={committer_date}&& {command}"'
+
+
 def get_commits(
     start: str | None = None,
-    end: str = "HEAD",
+    end: str | None = None,
     *,
     args: str = "",
 ) -> list[GitCommit]:
     """Get the commits between start and end."""
+    if end is None:
+        end = "HEAD"
     git_log_entries = _get_log_as_str_list(start, end, args)
-    git_commits = []
-    for rev_and_commit in git_log_entries:
-        if not rev_and_commit:
-            continue
-        rev, title, author, author_email, *body_list = rev_and_commit.split("\n")
-        if rev_and_commit:
-            git_commit = GitCommit(
-                rev=rev.strip(),
-                title=title.strip(),
-                body="\n".join(body_list).strip(),
-                author=author,
-                author_email=author_email,
-            )
-            git_commits.append(git_commit)
-    return git_commits
-
-
-def get_filenames_in_commit(git_reference: str = ""):
+    return [
+        GitCommit.from_rev_and_commit(rev_and_commit)
+        for rev_and_commit in git_log_entries
+        if rev_and_commit
+    ]
+
+
+def get_filenames_in_commit(git_reference: str = "") -> list[str]:
     """Get the list of files that were committed in the requested git reference.
 
     :param git_reference: a git reference as accepted by `git show`, default: the last commit
@@ -160,11 +226,12 @@ def get_filenames_in_commit(git_reference: str = ""):
     c = cmd.run(f"git show --name-only --pretty=format: {git_reference}")
     if c.return_code == 0:
         return c.out.strip().split("\n")
-    else:
-        raise GitCommandError(c.err)
+    raise GitCommandError(c.err)
 
 
-def get_tags(dateformat: str = "%Y-%m-%d") -> list[GitTag]:
+def get_tags(
+    dateformat: str = "%Y-%m-%d", reachable_only: bool = False
+) -> list[GitTag]:
     inner_delimiter = "---inner_delimiter---"
     formatter = (
         f'"%(refname:lstrip=2){inner_delimiter}'
@@ -172,23 +239,24 @@ def get_tags(dateformat: str = "%Y-%m-%d") -> list[GitTag]:
         f"%(creatordate:format:{dateformat}){inner_delimiter}"
         f'%(object)"'
     )
-    c = cmd.run(f"git tag --format={formatter} --sort=-creatordate")
+    extra = "--merged" if reachable_only else ""
+    # Force the default language for parsing
+    env = {"LC_ALL": "C", "LANG": "C", "LANGUAGE": "C"}
+    c = cmd.run(f"git tag --format={formatter} --sort=-creatordate {extra}", env=env)
     if c.return_code != 0:
+        if reachable_only and c.err == "fatal: malformed object name HEAD\n":
+            # this can happen if there are no commits in the repo yet
+            return []
         raise GitCommandError(c.err)
 
     if c.err:
         out.warn(f"Attempting to proceed after: {c.err}")
 
-    if not c.out:
-        return []
-
-    git_tags = [
+    return [
         GitTag.from_line(line=line, inner_delimiter=inner_delimiter)
         for line in c.out.split("\n")[:-1]
     ]
 
-    return git_tags
-
 
 def tag_exist(tag: str) -> bool:
     c = cmd.run(f"git tag --list {tag}")
@@ -213,18 +281,18 @@ def get_tag_message(tag: str) -> str | None:
     return c.out.strip()
 
 
-def get_tag_names() -> list[str | None]:
+def get_tag_names() -> list[str]:
     c = cmd.run("git tag --list")
     if c.err:
         return []
-    return [tag.strip() for tag in c.out.split("\n") if tag.strip()]
+    return [tag for raw in c.out.split("\n") if (tag := raw.strip())]
 
 
 def find_git_project_root() -> Path | None:
     c = cmd.run("git rev-parse --show-toplevel")
-    if not c.err:
-        return Path(c.out.strip())
-    return None
+    if c.err:
+        return None
+    return Path(c.out.strip())
 
 
 def is_staging_clean() -> bool:
@@ -235,50 +303,28 @@ def is_staging_clean() -> bool:
 
 def is_git_project() -> bool:
     c = cmd.run("git rev-parse --is-inside-work-tree")
-    if c.out.strip() == "true":
-        return True
-    return False
-
-
-def get_eol_style() -> EOLTypes:
-    c = cmd.run("git config core.eol")
-    eol = c.out.strip().lower()
-
-    # We enumerate the EOL types of the response of
-    # `git config core.eol`, and map it to our enumration EOLTypes.
-    #
-    # It is just like the variant of the "match" syntax.
-    map = {
-        "lf": EOLTypes.LF,
-        "crlf": EOLTypes.CRLF,
-        "native": EOLTypes.NATIVE,
-    }
-
-    # If the response of `git config core.eol` is in the map:
-    if eol in map:
-        return map[eol]
-    else:
-        # The default value is "native".
-        # https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreeol
-        return map["native"]
-
-
-def smart_open(*args, **kargs):
+    return c.out.strip() == "true"
+
+
+def get_core_editor() -> str | None:
+    c = cmd.run("git var GIT_EDITOR")
+    if c.out:
+        return c.out.strip()
+    return None
+
+
+def smart_open(*args, **kwargs):  # type: ignore[no-untyped-def,unused-ignore] # noqa: ANN201
     """Open a file with the EOL style determined from Git."""
-    return open(*args, newline=get_eol_style().get_eol_for_open(), **kargs)
+    return open(*args, newline=EOLType.for_open(), **kwargs)
 
 
 def _get_log_as_str_list(start: str | None, end: str, args: str) -> list[str]:
     """Get string representation of each log entry"""
     delimiter = "----------commit-delimiter----------"
-    log_format: str = "%H%n%s%n%an%n%ae%n%b"
-    git_log_cmd = (
-        f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args}"
-    )
-    if start:
-        command = f"{git_log_cmd} {start}..{end}"
-    else:
-        command = f"{git_log_cmd} {end}"
+    log_format: str = "%H%n%P%n%s%n%an%n%ae%n%b"
+    command_range = f"{start}..{end}" if start else end
+    command = f"git -c log.showSignature=False log --pretty={log_format}{delimiter} {args} {command_range}"
+
     c = cmd.run(command)
     if c.return_code != 0:
         raise GitCommandError(c.err)
diff --git a/commitizen/hooks.py b/commitizen/hooks.py
index f5505d0e82..f60bd9b43e 100644
--- a/commitizen/hooks.py
+++ b/commitizen/hooks.py
@@ -1,12 +1,13 @@
 from __future__ import annotations
 
 import os
+from collections.abc import Mapping
 
 from commitizen import cmd, out
 from commitizen.exceptions import RunHookError
 
 
-def run(hooks, _env_prefix="CZ_", **env):
+def run(hooks: str | list[str], _env_prefix: str = "CZ_", **env: object) -> None:
     if isinstance(hooks, str):
         hooks = [hooks]
 
@@ -24,7 +25,7 @@ def run(hooks, _env_prefix="CZ_", **env):
             raise RunHookError(f"Running hook '{hook}' failed")
 
 
-def _format_env(prefix: str, env: dict[str, str]) -> dict[str, str]:
+def _format_env(prefix: str, env: Mapping[str, object]) -> dict[str, str]:
     """_format_env() prefixes all given environment variables with the given
     prefix so it can be passed directly to cmd.run()."""
     penv = dict(os.environ)
diff --git a/commitizen/out.py b/commitizen/out.py
index 3801ad4c15..1bbfe4329d 100644
--- a/commitizen/out.py
+++ b/commitizen/out.py
@@ -1,5 +1,6 @@
 import io
 import sys
+from typing import Any
 
 from termcolor import colored
 
@@ -8,12 +9,12 @@
         sys.stdout.reconfigure(encoding="utf-8")
 
 
-def write(value: str, *args) -> None:
+def write(value: str, *args: object) -> None:
     """Intended to be used when value is multiline."""
     print(value, *args)
 
 
-def line(value: str, *args, **kwargs) -> None:
+def line(value: str, *args: object, **kwargs: Any) -> None:
     """Wrapper in case I want to do something different later."""
     print(value, *args, **kwargs)
 
@@ -33,10 +34,10 @@ def info(value: str) -> None:
     line(message)
 
 
-def diagnostic(value: str):
+def diagnostic(value: str) -> None:
     line(value, file=sys.stderr)
 
 
 def warn(value: str) -> None:
     message = colored(value, "magenta")
-    line(message)
+    line(message, file=sys.stderr)
diff --git a/commitizen/providers/__init__.py b/commitizen/providers/__init__.py
index 51302d2b37..3e01fe22f8 100644
--- a/commitizen/providers/__init__.py
+++ b/commitizen/providers/__init__.py
@@ -1,12 +1,15 @@
 from __future__ import annotations
 
+import sys
 from typing import cast
 
-import importlib_metadata as metadata
+if sys.version_info >= (3, 10):
+    from importlib import metadata
+else:
+    import importlib_metadata as metadata
 
 from commitizen.config.base_config import BaseConfig
 from commitizen.exceptions import VersionProviderUnknown
-
 from commitizen.providers.base_provider import VersionProvider
 from commitizen.providers.cargo_provider import CargoProvider
 from commitizen.providers.commitizen_provider import CommitizenProvider
@@ -15,9 +18,9 @@
 from commitizen.providers.pep621_provider import Pep621Provider
 from commitizen.providers.poetry_provider import PoetryProvider
 from commitizen.providers.scm_provider import ScmProvider
+from commitizen.providers.uv_provider import UvProvider
 
 __all__ = [
-    "get_provider",
     "CargoProvider",
     "CommitizenProvider",
     "ComposerProvider",
@@ -25,6 +28,8 @@
     "Pep621Provider",
     "PoetryProvider",
     "ScmProvider",
+    "UvProvider",
+    "get_provider",
 ]
 
 PROVIDER_ENTRYPOINT = "commitizen.provider"
diff --git a/commitizen/providers/base_provider.py b/commitizen/providers/base_provider.py
index 34048318e2..c91bfdae20 100644
--- a/commitizen/providers/base_provider.py
+++ b/commitizen/providers/base_provider.py
@@ -2,6 +2,7 @@
 
 import json
 from abc import ABC, abstractmethod
+from collections.abc import Mapping
 from pathlib import Path
 from typing import Any, ClassVar
 
@@ -19,7 +20,7 @@ class VersionProvider(ABC):
 
     config: BaseConfig
 
-    def __init__(self, config: BaseConfig):
+    def __init__(self, config: BaseConfig) -> None:
         self.config = config
 
     @abstractmethod
@@ -29,7 +30,7 @@ def get_version(self) -> str:
         """
 
     @abstractmethod
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         """
         Set the new current version
         """
@@ -58,15 +59,15 @@ def get_version(self) -> str:
         document = json.loads(self.file.read_text())
         return self.get(document)
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         document = json.loads(self.file.read_text())
         self.set(document, version)
         self.file.write_text(json.dumps(document, indent=self.indent) + "\n")
 
-    def get(self, document: dict[str, Any]) -> str:
-        return document["version"]  # type: ignore
+    def get(self, document: Mapping[str, str]) -> str:
+        return document["version"]
 
-    def set(self, document: dict[str, Any], version: str):
+    def set(self, document: dict[str, Any], version: str) -> None:
         document["version"] = version
 
 
@@ -79,13 +80,13 @@ def get_version(self) -> str:
         document = tomlkit.parse(self.file.read_text())
         return self.get(document)
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         document = tomlkit.parse(self.file.read_text())
         self.set(document, version)
         self.file.write_text(tomlkit.dumps(document))
 
     def get(self, document: tomlkit.TOMLDocument) -> str:
-        return document["project"]["version"]  # type: ignore
+        return document["project"]["version"]  # type: ignore[index,return-value]
 
-    def set(self, document: tomlkit.TOMLDocument, version: str):
-        document["project"]["version"] = version  # type: ignore
+    def set(self, document: tomlkit.TOMLDocument, version: str) -> None:
+        document["project"]["version"] = version  # type: ignore[index]
diff --git a/commitizen/providers/cargo_provider.py b/commitizen/providers/cargo_provider.py
index f64c003edd..d453a4aa5b 100644
--- a/commitizen/providers/cargo_provider.py
+++ b/commitizen/providers/cargo_provider.py
@@ -1,7 +1,8 @@
 from __future__ import annotations
 
-import tomlkit
+from pathlib import Path
 
+import tomlkit
 
 from commitizen.providers.base_provider import TomlProvider
 
@@ -14,18 +15,43 @@ class CargoProvider(TomlProvider):
     """
 
     filename = "Cargo.toml"
+    lock_filename = "Cargo.lock"
+
+    @property
+    def lock_file(self) -> Path:
+        return Path() / self.lock_filename
 
     def get(self, document: tomlkit.TOMLDocument) -> str:
         try:
-            return document["package"]["version"]  # type: ignore
+            return document["package"]["version"]  # type: ignore[index,return-value]
         except tomlkit.exceptions.NonExistentKey:
             ...
-        return document["workspace"]["package"]["version"]  # type: ignore
+        return document["workspace"]["package"]["version"]  # type: ignore[index,return-value]
 
-    def set(self, document: tomlkit.TOMLDocument, version: str):
+    def set(self, document: tomlkit.TOMLDocument, version: str) -> None:
         try:
-            document["workspace"]["package"]["version"] = version  # type: ignore
+            document["workspace"]["package"]["version"] = version  # type: ignore[index]
             return
         except tomlkit.exceptions.NonExistentKey:
             ...
-        document["package"]["version"] = version  # type: ignore
+        document["package"]["version"] = version  # type: ignore[index]
+
+    def set_version(self, version: str) -> None:
+        super().set_version(version)
+        if self.lock_file.exists():
+            self.set_lock_version(version)
+
+    def set_lock_version(self, version: str) -> None:
+        cargo_toml_content = tomlkit.parse(self.file.read_text())
+        try:
+            package_name = cargo_toml_content["package"]["name"]  # type: ignore[index]
+        except tomlkit.exceptions.NonExistentKey:
+            package_name = cargo_toml_content["workspace"]["package"]["name"]  # type: ignore[index]
+
+        cargo_lock_content = tomlkit.parse(self.lock_file.read_text())
+        packages: tomlkit.items.AoT = cargo_lock_content["package"]  # type: ignore[assignment]
+        for i, package in enumerate(packages):
+            if package["name"] == package_name:
+                cargo_lock_content["package"][i]["version"] = version  # type: ignore[index]
+                break
+        self.lock_file.write_text(tomlkit.dumps(cargo_lock_content))
diff --git a/commitizen/providers/commitizen_provider.py b/commitizen/providers/commitizen_provider.py
index fd0cdb3ebf..d9207b19ff 100644
--- a/commitizen/providers/commitizen_provider.py
+++ b/commitizen/providers/commitizen_provider.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-
 from commitizen.providers.base_provider import VersionProvider
 
 
@@ -10,7 +9,7 @@ class CommitizenProvider(VersionProvider):
     """
 
     def get_version(self) -> str:
-        return self.config.settings["version"]  # type: ignore
+        return self.config.settings["version"]  # type: ignore[return-value] # TODO: check if we can fix this by tweaking the `Settings` type
 
-    def set_version(self, version: str):
+    def set_version(self, version: str) -> None:
         self.config.set_key("version", version)
diff --git a/commitizen/providers/composer_provider.py b/commitizen/providers/composer_provider.py
index ef36af5a72..495357d8f0 100644
--- a/commitizen/providers/composer_provider.py
+++ b/commitizen/providers/composer_provider.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-
 from commitizen.providers.base_provider import JsonProvider
 
 
diff --git a/commitizen/providers/npm_provider.py b/commitizen/providers/npm_provider.py
index f625c3c6c3..3125447250 100644
--- a/commitizen/providers/npm_provider.py
+++ b/commitizen/providers/npm_provider.py
@@ -1,10 +1,10 @@
 from __future__ import annotations
 
 import json
+from collections.abc import Mapping
 from pathlib import Path
 from typing import Any, ClassVar
 
-
 from commitizen.providers.base_provider import VersionProvider
 
 
@@ -59,8 +59,8 @@ def set_version(self, version: str) -> None:
                 json.dumps(shrinkwrap_document, indent=self.indent) + "\n"
             )
 
-    def get_package_version(self, document: dict[str, Any]) -> str:
-        return document["version"]  # type: ignore
+    def get_package_version(self, document: Mapping[str, str]) -> str:
+        return document["version"]
 
     def set_package_version(
         self, document: dict[str, Any], version: str
diff --git a/commitizen/providers/pep621_provider.py b/commitizen/providers/pep621_provider.py
index b6d32f1a63..0b8b5c4b87 100644
--- a/commitizen/providers/pep621_provider.py
+++ b/commitizen/providers/pep621_provider.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-
 from commitizen.providers.base_provider import TomlProvider
 
 
diff --git a/commitizen/providers/poetry_provider.py b/commitizen/providers/poetry_provider.py
index d301131115..f63b13b793 100644
--- a/commitizen/providers/poetry_provider.py
+++ b/commitizen/providers/poetry_provider.py
@@ -2,7 +2,6 @@
 
 import tomlkit
 
-
 from commitizen.providers.base_provider import TomlProvider
 
 
@@ -14,7 +13,7 @@ class PoetryProvider(TomlProvider):
     filename = "pyproject.toml"
 
     def get(self, pyproject: tomlkit.TOMLDocument) -> str:
-        return pyproject["tool"]["poetry"]["version"]  # type: ignore
+        return pyproject["tool"]["poetry"]["version"]  # type: ignore[index,return-value]
 
-    def set(self, pyproject: tomlkit.TOMLDocument, version: str):
-        pyproject["tool"]["poetry"]["version"] = version  # type: ignore
+    def set(self, pyproject: tomlkit.TOMLDocument, version: str) -> None:
+        pyproject["tool"]["poetry"]["version"] = version  # type: ignore[index]
diff --git a/commitizen/providers/scm_provider.py b/commitizen/providers/scm_provider.py
index bc9dda4b8a..3085b16efa 100644
--- a/commitizen/providers/scm_provider.py
+++ b/commitizen/providers/scm_provider.py
@@ -1,13 +1,8 @@
 from __future__ import annotations
 
-import re
-from typing import Callable, cast
-
-
 from commitizen.git import get_tags
-from commitizen.version_schemes import get_version_scheme
-
 from commitizen.providers.base_provider import VersionProvider
+from commitizen.tags import TagRules
 
 
 class ScmProvider(VersionProvider):
@@ -19,54 +14,15 @@ class ScmProvider(VersionProvider):
     It is meant for `setuptools-scm` or any package manager `*-scm` provider.
     """
 
-    TAG_FORMAT_REGEXS = {
-        "$version": r"(?P<version>.+)",
-        "$major": r"(?P<major>\d+)",
-        "$minor": r"(?P<minor>\d+)",
-        "$patch": r"(?P<patch>\d+)",
-        "$prerelease": r"(?P<prerelease>\w+\d+)?",
-        "$devrelease": r"(?P<devrelease>\.dev\d+)?",
-    }
-
-    def _tag_format_matcher(self) -> Callable[[str], str | None]:
-        version_scheme = get_version_scheme(self.config)
-        pattern = self.config.settings["tag_format"]
-        if pattern == "$version":
-            pattern = version_scheme.parser.pattern
-        for var, tag_pattern in self.TAG_FORMAT_REGEXS.items():
-            pattern = pattern.replace(var, tag_pattern)
-
-        regex = re.compile(f"^{pattern}$", re.VERBOSE)
-
-        def matcher(tag: str) -> str | None:
-            match = regex.match(tag)
-            if not match:
-                return None
-            groups = match.groupdict()
-            if "version" in groups:
-                return groups["version"]
-            elif "major" in groups:
-                return "".join(
-                    (
-                        groups["major"],
-                        f".{groups['minor']}" if groups.get("minor") else "",
-                        f".{groups['patch']}" if groups.get("patch") else "",
-                        groups["prerelease"] if groups.get("prerelease") else "",
-                        groups["devrelease"] if groups.get("devrelease") else "",
-                    )
-                )
-            elif pattern == version_scheme.parser.pattern:
-                return str(version_scheme(tag))
-            return None
-
-        return matcher
-
     def get_version(self) -> str:
-        matcher = self._tag_format_matcher()
-        return next(
-            (cast(str, matcher(t.name)) for t in get_tags() if matcher(t.name)), "0.0.0"
-        )
-
-    def set_version(self, version: str):
+        rules = TagRules.from_settings(self.config.settings)
+        tags = get_tags(reachable_only=True)
+        version_tags = rules.get_version_tags(tags)
+        versions = sorted(rules.extract_version(t) for t in version_tags)
+        if not versions:
+            return "0.0.0"
+        return str(versions[-1])
+
+    def set_version(self, version: str) -> None:
         # Not necessary
         pass
diff --git a/commitizen/providers/uv_provider.py b/commitizen/providers/uv_provider.py
new file mode 100644
index 0000000000..36c8a49ad3
--- /dev/null
+++ b/commitizen/providers/uv_provider.py
@@ -0,0 +1,41 @@
+from __future__ import annotations
+
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+import tomlkit
+
+from commitizen.providers.base_provider import TomlProvider
+
+if TYPE_CHECKING:
+    import tomlkit.items
+
+
+class UvProvider(TomlProvider):
+    """
+    uv.lock and pyproject.tom version management
+    """
+
+    filename = "pyproject.toml"
+    lock_filename = "uv.lock"
+
+    @property
+    def lock_file(self) -> Path:
+        return Path() / self.lock_filename
+
+    def set_version(self, version: str) -> None:
+        super().set_version(version)
+        self.set_lock_version(version)
+
+    def set_lock_version(self, version: str) -> None:
+        pyproject_toml_content = tomlkit.parse(self.file.read_text())
+        project_name = pyproject_toml_content["project"]["name"]  # type: ignore[index]
+
+        document = tomlkit.parse(self.lock_file.read_text())
+
+        packages: tomlkit.items.AoT = document["package"]  # type: ignore[assignment]
+        for i, package in enumerate(packages):
+            if package["name"] == project_name:
+                document["package"][i]["version"] = version  # type: ignore[index]
+                break
+        self.lock_file.write_text(tomlkit.dumps(document))
diff --git a/commitizen/py.typed b/commitizen/py.typed
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/commitizen/question.py b/commitizen/question.py
new file mode 100644
index 0000000000..393e386092
--- /dev/null
+++ b/commitizen/question.py
@@ -0,0 +1,32 @@
+from typing import Callable, Literal, TypedDict, Union
+
+
+class Choice(TypedDict, total=False):
+    value: str
+    name: str
+    key: str
+
+
+class ListQuestion(TypedDict, total=False):
+    type: Literal["list"]
+    name: str
+    message: str
+    choices: list[Choice]
+    use_shortcuts: bool
+
+
+class InputQuestion(TypedDict, total=False):
+    type: Literal["input"]
+    name: str
+    message: str
+    filter: Callable[[str], str]
+
+
+class ConfirmQuestion(TypedDict):
+    type: Literal["confirm"]
+    name: str
+    message: str
+    default: bool
+
+
+CzQuestion = Union[ListQuestion, InputQuestion, ConfirmQuestion]
diff --git a/commitizen/tags.py b/commitizen/tags.py
new file mode 100644
index 0000000000..b19bb89e09
--- /dev/null
+++ b/commitizen/tags.py
@@ -0,0 +1,265 @@
+from __future__ import annotations
+
+import re
+import warnings
+from collections.abc import Iterable, Sequence
+from dataclasses import dataclass, field
+from functools import cached_property
+from string import Template
+from typing import TYPE_CHECKING, NamedTuple
+
+from commitizen import out
+from commitizen.defaults import DEFAULT_SETTINGS, Settings, get_tag_regexes
+from commitizen.git import GitTag
+from commitizen.version_schemes import (
+    DEFAULT_SCHEME,
+    InvalidVersion,
+    Version,
+    VersionScheme,
+    get_version_scheme,
+)
+
+if TYPE_CHECKING:
+    import sys
+
+    from commitizen.version_schemes import VersionScheme
+
+    # Self is Python 3.11+ but backported in typing-extensions
+    if sys.version_info < (3, 11):
+        from typing_extensions import Self
+    else:
+        from typing import Self
+
+
+class VersionTag(NamedTuple):
+    """Represent a version and its matching tag form."""
+
+    version: str
+    tag: str
+
+
+@dataclass
+class TagRules:
+    """
+    Encapsulate tag-related rules.
+
+    It allows to filter or match tags according to rules provided in settings:
+    - `tag_format`: the current format of the tags generated on `bump`
+    - `legacy_tag_formats`: previous known formats of the tag
+    - `ignored_tag_formats`: known formats that should be ignored
+    - `merge_prereleases`: if `True`, prereleases will be merged with their release counterpart
+    - `version_scheme`: the version scheme to use, which will be used to parse and format versions
+
+    This class is meant to abstract and centralize all the logic related to tags.
+    To ensure consistency, it is recommended to use this class to handle tags.
+
+    Example:
+
+    ```python
+    settings = DEFAULT_SETTINGS.clone()
+    settings.update(
+        {
+            "tag_format": "v{version}",
+            "legacy_tag_formats": ["version{version}", "ver{version}"],
+            "ignored_tag_formats": ["ignored{version}"],
+        }
+    )
+
+    rules = TagRules.from_settings(settings)
+
+    assert rules.is_version_tag("v1.0.0")
+    assert rules.is_version_tag("version1.0.0")
+    assert rules.is_version_tag("ver1.0.0")
+    assert not rules.is_version_tag("ignored1.0.0", warn=True)  # Does not warn
+    assert not rules.is_version_tag("warn1.0.0", warn=True)  # Does warn
+
+    assert rules.search_version("# My v1.0.0 version").version == "1.0.0"
+    assert rules.extract_version("v1.0.0") == Version("1.0.0")
+    try:
+        assert rules.extract_version("not-a-v1.0.0")
+    except InvalidVersion:
+        print("Does not match a tag format")
+    ```
+    """
+
+    scheme: VersionScheme = DEFAULT_SCHEME
+    tag_format: str = DEFAULT_SETTINGS["tag_format"]
+    legacy_tag_formats: Sequence[str] = field(default_factory=list)
+    ignored_tag_formats: Sequence[str] = field(default_factory=list)
+    merge_prereleases: bool = False
+
+    @cached_property
+    def version_regexes(self) -> list[re.Pattern]:
+        """Regexes for all legit tag formats, current and legacy"""
+        tag_formats = [self.tag_format, *self.legacy_tag_formats]
+        regexes = (self._format_regex(p) for p in tag_formats)
+        return [re.compile(r) for r in regexes]
+
+    @cached_property
+    def ignored_regexes(self) -> list[re.Pattern]:
+        """Regexes for known but ignored tag formats"""
+        regexes = (self._format_regex(p, star=True) for p in self.ignored_tag_formats)
+        return [re.compile(r) for r in regexes]
+
+    def _format_regex(self, tag_pattern: str, star: bool = False) -> str:
+        """
+        Format a tag pattern into a regex pattern.
+
+        If star is `True`, the `*` character will be considered as a wildcard.
+        """
+        tag_regexes = get_tag_regexes(self.scheme.parser.pattern)
+        format_regex = tag_pattern.replace("*", "(?:.*?)") if star else tag_pattern
+        for pattern, regex in tag_regexes.items():
+            format_regex = format_regex.replace(pattern, regex)
+        return format_regex
+
+    def _version_tag_error(self, tag: str) -> str:
+        """Format the error message for an invalid version tag"""
+        return f"Invalid version tag: '{tag}' does not match any configured tag format"
+
+    def is_version_tag(self, tag: str | GitTag, warn: bool = False) -> bool:
+        """
+        True if a given tag is a legit version tag.
+
+        if `warn` is `True`, it will print a warning message if the tag is not a version tag.
+        """
+        tag = tag.name if isinstance(tag, GitTag) else tag
+        is_legit = any(regex.fullmatch(tag) for regex in self.version_regexes)
+        if warn and not is_legit and not self.is_ignored_tag(tag):
+            out.warn(self._version_tag_error(tag))
+        return is_legit
+
+    def is_ignored_tag(self, tag: str | GitTag) -> bool:
+        """True if a given tag can be ignored"""
+        tag = tag.name if isinstance(tag, GitTag) else tag
+        return any(regex.match(tag) for regex in self.ignored_regexes)
+
+    def get_version_tags(
+        self, tags: Iterable[GitTag], warn: bool = False
+    ) -> list[GitTag]:
+        """Filter in version tags and warn on unexpected tags"""
+        return [tag for tag in tags if self.is_version_tag(tag, warn)]
+
+    def extract_version(self, tag: GitTag) -> Version:
+        """
+        Extract a version from the tag as defined in tag formats.
+
+        Raises `InvalidVersion` if the tag does not match any format.
+        """
+        candidates = (
+            m for regex in self.version_regexes if (m := regex.fullmatch(tag.name))
+        )
+        if not (m := next(candidates, None)):
+            raise InvalidVersion(self._version_tag_error(tag.name))
+        if "version" in m.groupdict():
+            return self.scheme(m.group("version"))
+
+        parts = m.groupdict()
+        version = parts["major"]
+
+        if minor := parts.get("minor"):
+            version = f"{version}.{minor}"
+        if patch := parts.get("patch"):
+            version = f"{version}.{patch}"
+
+        if parts.get("prerelease"):
+            version = f"{version}-{parts['prerelease']}"
+        if parts.get("devrelease"):
+            version = f"{version}{parts['devrelease']}"
+        return self.scheme(version)
+
+    def include_in_changelog(self, tag: GitTag) -> bool:
+        """Check if a tag should be included in the changelog"""
+        try:
+            version = self.extract_version(tag)
+        except InvalidVersion:
+            return False
+        return not (self.merge_prereleases and version.is_prerelease)
+
+    def search_version(self, text: str, last: bool = False) -> VersionTag | None:
+        """
+        Search the first or last version tag occurrence in text.
+
+        It searches for complete versions only (aka `major`, `minor` and `patch`)
+        """
+        candidates = (
+            m for regex in self.version_regexes if len(m := list(regex.finditer(text)))
+        )
+        if not (matches := next(candidates, [])):
+            return None
+
+        match = matches[-1 if last else 0]
+
+        if "version" in match.groupdict():
+            return VersionTag(match.group("version"), match.group(0))
+
+        parts = match.groupdict()
+        try:
+            version = f"{parts['major']}.{parts['minor']}.{parts['patch']}"
+        except KeyError:
+            return None
+
+        if parts.get("prerelease"):
+            version = f"{version}-{parts['prerelease']}"
+        if parts.get("devrelease"):
+            version = f"{version}{parts['devrelease']}"
+        return VersionTag(version, match.group(0))
+
+    def normalize_tag(
+        self, version: Version | str, tag_format: str | None = None
+    ) -> str:
+        """
+        The tag and the software version might be different.
+
+        That's why this function exists.
+
+        Example:
+        | tag | version (PEP 0440) |
+        | --- | ------- |
+        | v0.9.0 | 0.9.0 |
+        | ver1.0.0 | 1.0.0 |
+        | ver1.0.0.a0 | 1.0.0a0 |
+        """
+        version = self.scheme(version) if isinstance(version, str) else version
+        tag_format = tag_format or self.tag_format
+
+        major, minor, patch = version.release
+        prerelease = version.prerelease or ""
+
+        t = Template(tag_format)
+        return t.safe_substitute(
+            version=version,
+            major=major,
+            minor=minor,
+            patch=patch,
+            prerelease=prerelease,
+        )
+
+    def find_tag_for(
+        self, tags: Iterable[GitTag], version: Version | str
+    ) -> GitTag | None:
+        """Find the first matching tag for a given version."""
+        version = self.scheme(version) if isinstance(version, str) else version
+        possible_tags = set(
+            self.normalize_tag(version, f)
+            for f in (self.tag_format, *self.legacy_tag_formats)
+        )
+        candidates = [t for t in tags if t.name in possible_tags]
+        if len(candidates) > 1:
+            warnings.warn(
+                UserWarning(
+                    f"Multiple tags found for version {version}: {', '.join(t.name for t in candidates)}"
+                )
+            )
+        return next(iter(candidates), None)
+
+    @classmethod
+    def from_settings(cls, settings: Settings) -> Self:
+        """Extract tag rules from settings"""
+        return cls(
+            scheme=get_version_scheme(settings),
+            tag_format=settings["tag_format"],
+            legacy_tag_formats=settings["legacy_tag_formats"],
+            ignored_tag_formats=settings["ignored_tag_formats"],
+            merge_prereleases=settings["changelog_merge_prerelease"],
+        )
diff --git a/commitizen/version_schemes.py b/commitizen/version_schemes.py
index d7967ae928..e9f99c5514 100644
--- a/commitizen/version_schemes.py
+++ b/commitizen/version_schemes.py
@@ -4,14 +4,25 @@
 import sys
 import warnings
 from itertools import zip_longest
-from typing import TYPE_CHECKING, ClassVar, Protocol, Type, cast, runtime_checkable
-
-import importlib_metadata as metadata
-from packaging.version import InvalidVersion  # noqa: F401: Rexpose the common exception
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    ClassVar,
+    Literal,
+    Protocol,
+    cast,
+    runtime_checkable,
+)
+
+if sys.version_info >= (3, 10):
+    from importlib import metadata
+else:
+    import importlib_metadata as metadata
+
+from packaging.version import InvalidVersion  # noqa: F401 (expose the common exception)
 from packaging.version import Version as _BaseVersion
 
-from commitizen.config.base_config import BaseConfig
-from commitizen.defaults import MAJOR, MINOR, PATCH
+from commitizen.defaults import MAJOR, MINOR, PATCH, Settings
 from commitizen.exceptions import VersionSchemeUnknown
 
 if TYPE_CHECKING:
@@ -28,7 +39,11 @@
         from typing import Self
 
 
-DEFAULT_VERSION_PARSER = r"v?(?P<version>([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?(\w+)?)"
+Increment: TypeAlias = Literal["MAJOR", "MINOR", "PATCH"]
+Prerelease: TypeAlias = Literal["alpha", "beta", "rc"]
+_DEFAULT_VERSION_PARSER = re.compile(
+    r"v?(?P<version>([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.]+)?(\w+)?)"
+)
 
 
 @runtime_checkable
@@ -36,7 +51,7 @@ class VersionProtocol(Protocol):
     parser: ClassVar[re.Pattern]
     """Regex capturing this version scheme into a `version` group"""
 
-    def __init__(self, version: str):
+    def __init__(self, version: str) -> None:
         """
         Initialize a version object from its string representation.
 
@@ -65,7 +80,7 @@ def is_prerelease(self) -> bool:
 
     @property
     def prerelease(self) -> str | None:
-        """The prelease potion of the version is this is a prerelease."""
+        """The prerelease potion of the version is this is a prerelease."""
         raise NotImplementedError("must be implemented")
 
     @property
@@ -93,22 +108,49 @@ def micro(self) -> int:
         """The third item of :attr:`release` or ``0`` if unavailable."""
         raise NotImplementedError("must be implemented")
 
+    def __lt__(self, other: Any) -> bool:
+        raise NotImplementedError("must be implemented")
+
+    def __le__(self, other: Any) -> bool:
+        raise NotImplementedError("must be implemented")
+
+    def __eq__(self, other: object) -> bool:
+        raise NotImplementedError("must be implemented")
+
+    def __ge__(self, other: Any) -> bool:
+        raise NotImplementedError("must be implemented")
+
+    def __gt__(self, other: Any) -> bool:
+        raise NotImplementedError("must be implemented")
+
+    def __ne__(self, other: object) -> bool:
+        raise NotImplementedError("must be implemented")
+
     def bump(
         self,
-        increment: str,
-        prerelease: str | None = None,
+        increment: Increment | None,
+        prerelease: Prerelease | None = None,
         prerelease_offset: int = 0,
         devrelease: int | None = None,
         is_local_version: bool = False,
+        build_metadata: str | None = None,
+        exact_increment: bool = False,
     ) -> Self:
         """
         Based on the given increment, generate the next bumped version according to the version scheme
+
+        Args:
+            increment: The component to increase
+            prerelease: The type of prerelease, if Any
+            is_local_version: Whether to increment the local version instead
+            exact_increment: Treat the increment and prerelease arguments explicitly.  Disables logic
+                that attempts to deduce the correct increment when a prerelease suffix is present.
         """
 
 
 # With PEP 440 and SemVer semantic, Scheme is the type, Version is an instance
 Version: TypeAlias = VersionProtocol
-VersionScheme: TypeAlias = Type[VersionProtocol]
+VersionScheme: TypeAlias = type[VersionProtocol]
 
 
 class BaseVersion(_BaseVersion):
@@ -116,7 +158,7 @@ class BaseVersion(_BaseVersion):
     A base class implementing the `VersionProtocol` for PEP440-like versions.
     """
 
-    parser: ClassVar[re.Pattern] = re.compile(DEFAULT_VERSION_PARSER)
+    parser: ClassVar[re.Pattern] = _DEFAULT_VERSION_PARSER
     """Regex capturing this version scheme into a `version` group"""
 
     @property
@@ -146,6 +188,12 @@ def generate_prerelease(
         if not prerelease:
             return ""
 
+        # prevent down-bumping the pre-release phase, e.g. from 'b1' to 'a2'
+        # https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases
+        # https://semver.org/#spec-item-11
+        if self.is_prerelease and self.pre:
+            prerelease = max(prerelease, self.pre[0])
+
         # version.pre is needed for mypy check
         if self.is_prerelease and self.pre and prerelease.startswith(self.pre[0]):
             prev_prerelease: int = self.pre[1]
@@ -166,35 +214,43 @@ def generate_devrelease(self, devrelease: int | None) -> str:
 
         return f"dev{devrelease}"
 
-    def increment_base(self, increment: str | None = None) -> str:
+    def generate_build_metadata(self, build_metadata: str | None) -> str:
+        """Generate build-metadata
+
+        Build-metadata (local version) is not used in version calculations
+        but added after + statically.
+        """
+        if build_metadata is None:
+            return ""
+
+        return f"+{build_metadata}"
+
+    def increment_base(self, increment: Increment | None = None) -> str:
         prev_release = list(self.release)
         increments = [MAJOR, MINOR, PATCH]
         base = dict(zip_longest(increments, prev_release, fillvalue=0))
 
-        # This flag means that current version
-        # must remove its prerelease tag,
-        # so it doesn't matter the increment.
-        # Example: 1.0.0a0 with PATCH/MINOR -> 1.0.0
-        if not self.is_prerelease:
-            if increment == MAJOR:
-                base[MAJOR] += 1
-                base[MINOR] = 0
-                base[PATCH] = 0
-            elif increment == MINOR:
-                base[MINOR] += 1
-                base[PATCH] = 0
-            elif increment == PATCH:
-                base[PATCH] += 1
+        if increment == MAJOR:
+            base[MAJOR] += 1
+            base[MINOR] = 0
+            base[PATCH] = 0
+        elif increment == MINOR:
+            base[MINOR] += 1
+            base[PATCH] = 0
+        elif increment == PATCH:
+            base[PATCH] += 1
 
         return f"{base[MAJOR]}.{base[MINOR]}.{base[PATCH]}"
 
     def bump(
         self,
-        increment: str,
-        prerelease: str | None = None,
+        increment: Increment | None,
+        prerelease: Prerelease | None = None,
         prerelease_offset: int = 0,
         devrelease: int | None = None,
         is_local_version: bool = False,
+        build_metadata: str | None = None,
+        exact_increment: bool = False,
     ) -> Self:
         """Based on the given increment a proper semver will be generated.
 
@@ -210,13 +266,36 @@ def bump(
 
         if self.local and is_local_version:
             local_version = self.scheme(self.local).bump(increment)
-            return self.scheme(f"{self.public}+{local_version}")  # type: ignore
-        else:
-            base = self.increment_base(increment)
-            dev_version = self.generate_devrelease(devrelease)
-            pre_version = self.generate_prerelease(prerelease, offset=prerelease_offset)
-            # TODO: post version
-            return self.scheme(f"{base}{pre_version}{dev_version}")  # type: ignore
+            return self.scheme(f"{self.public}+{local_version}")  # type: ignore[return-value]
+
+        base = self._get_increment_base(increment, exact_increment)
+        dev_version = self.generate_devrelease(devrelease)
+
+        release = list(self.release)
+        if len(release) < 3:
+            release += [0] * (3 - len(release))
+        current_base = ".".join(str(part) for part in release)
+
+        pre_version = (
+            self if base == current_base else cast(BaseVersion, self.scheme(base))
+        ).generate_prerelease(prerelease, offset=prerelease_offset)
+
+        # TODO: post version
+        return self.scheme(
+            f"{base}{pre_version}{dev_version}{self.generate_build_metadata(build_metadata)}"
+        )  # type: ignore[return-value]
+
+    def _get_increment_base(
+        self, increment: Increment | None, exact_increment: bool
+    ) -> str:
+        if (
+            not self.is_prerelease
+            or exact_increment
+            or (increment == MINOR and self.micro != 0)
+            or (increment == MAJOR and (self.minor != 0 or self.micro != 0))
+        ):
+            return self.increment_base(increment)
+        return f"{self.major}.{self.minor}.{self.micro}"
 
 
 class Pep440(BaseVersion):
@@ -231,11 +310,11 @@ class SemVer(BaseVersion):
     """
     Semantic Versioning (SemVer) scheme
 
-    See: https://semver.org/
+    See: https://semver.org/spec/v1.0.0.html
     """
 
     def __str__(self) -> str:
-        parts = []
+        parts: list[str] = []
 
         # Epoch
         if self.epoch != 0:
@@ -245,9 +324,8 @@ def __str__(self) -> str:
         parts.append(".".join(str(x) for x in self.release))
 
         # Pre-release
-        if self.pre:
-            pre = "".join(str(x) for x in self.pre)
-            parts.append(f"-{pre}")
+        if self.prerelease:
+            parts.append(f"-{self.prerelease}")
 
         # Post-release
         if self.post is not None:
@@ -264,31 +342,88 @@ def __str__(self) -> str:
         return "".join(parts)
 
 
+class SemVer2(SemVer):
+    """
+    Semantic Versioning 2.0 (SemVer2) schema
+
+    See: https://semver.org/spec/v2.0.0.html
+    """
+
+    _STD_PRERELEASES = {
+        "a": "alpha",
+        "b": "beta",
+    }
+
+    @property
+    def prerelease(self) -> str | None:
+        if self.is_prerelease and self.pre:
+            prerelease_type = self._STD_PRERELEASES.get(self.pre[0], self.pre[0])
+            return f"{prerelease_type}.{self.pre[1]}"
+        return None
+
+    def __str__(self) -> str:
+        parts: list[str] = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{self.epoch}!")
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        if prerelease := self._get_prerelease():
+            parts.append(f"-{prerelease}")
+
+        # Local version segment
+        if self.local:
+            parts.append(f"+{self.local}")
+
+        return "".join(parts)
+
+    def _get_prerelease(self) -> str:
+        # Pre-release identifiers
+        # See: https://semver.org/spec/v2.0.0.html#spec-item-9
+        prerelease_parts: list[str] = []
+        if self.prerelease:
+            prerelease_parts.append(f"{self.prerelease}")
+
+        # Post-release
+        if self.post is not None:
+            prerelease_parts.append(f"post.{self.post}")
+
+        # Development release
+        if self.dev is not None:
+            prerelease_parts.append(f"dev.{self.dev}")
+
+        return ".".join(prerelease_parts)
+
+
 DEFAULT_SCHEME: VersionScheme = Pep440
 
 SCHEMES_ENTRYPOINT = "commitizen.scheme"
 """Schemes entrypoints group"""
 
-KNOWN_SCHEMES = {ep.name for ep in metadata.entry_points(group=SCHEMES_ENTRYPOINT)}
+KNOWN_SCHEMES = [ep.name for ep in metadata.entry_points(group=SCHEMES_ENTRYPOINT)]
 """All known registered version schemes"""
 
 
-def get_version_scheme(config: BaseConfig, name: str | None = None) -> VersionScheme:
+def get_version_scheme(settings: Settings, name: str | None = None) -> VersionScheme:
     """
     Get the version scheme as defined in the configuration
     or from an overridden `name`
 
     :raises VersionSchemeUnknown: if the version scheme is not found.
     """
-    deprecated_setting: str | None = config.settings.get("version_type")
+    # TODO: Remove the deprecated `version_type` handling
+    deprecated_setting: str | None = settings.get("version_type")
     if deprecated_setting:
         warnings.warn(
             DeprecationWarning(
-                "`version_type` setting is deprecated and will be removed in commitizen 4. "
+                "`version_type` setting is deprecated and will be removed in v5. "
                 "Please use `version_scheme` instead"
             )
         )
-    name = name or config.settings.get("version_scheme") or deprecated_setting
+    name = name or settings.get("version_scheme") or deprecated_setting
     if not name:
         return DEFAULT_SCHEME
 
diff --git a/docs/README.md b/docs/README.md
index e48062e168..fa3fab2239 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -3,155 +3,316 @@
 [![PyPI Package latest release](https://img.shields.io/pypi/v/commitizen.svg?style=flat-square)](https://pypi.org/project/commitizen/)
 [![PyPI Package download count (per month)](https://img.shields.io/pypi/dm/commitizen?style=flat-square)](https://pypi.org/project/commitizen/)
 [![Supported versions](https://img.shields.io/pypi/pyversions/commitizen.svg?style=flat-square)](https://pypi.org/project/commitizen/)
+[![Conda Version](https://img.shields.io/conda/vn/conda-forge/commitizen?style=flat-square)](https://anaconda.org/conda-forge/commitizen)
 [![homebrew](https://img.shields.io/homebrew/v/commitizen?color=teal&style=flat-square)](https://formulae.brew.sh/formula/commitizen)
 [![Codecov](https://img.shields.io/codecov/c/github/commitizen-tools/commitizen.svg?style=flat-square)](https://codecov.io/gh/commitizen-tools/commitizen)
 [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?style=flat-square&logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
 
-![Using commitizen cli](images/demo.gif)
+![Using Commitizen cli](images/demo.gif)
 
 ---
 
-**Documentation:** [https://commitizen-tools.github.io/commitizen/](https://commitizen-tools.github.io/commitizen/)
+[**Commitizen Documentation Site**](https://commitizen-tools.github.io/commitizen/)
 
 ---
 
 ## About
 
-Commitizen is release management tool designed for teams.
+Commitizen is a powerful release management tool that helps teams maintain consistent and meaningful commit messages while automating version management.
 
-Commitizen assumes your team uses a standard way of committing rules
-and from that foundation, it can bump your project's version, create
-the changelog, and update files.
+### What Commitizen Does
 
-By default, commitizen uses [conventional commits][conventional_commits], but you
-can build your own set of rules, and publish them.
+By enforcing standardized commit conventions (defaulting to [Conventional Commits][conventional_commits]), Commitizen helps teams:
 
-Using a standardized set of rules to write commits, makes commits easier to read, and enforces writing
-descriptive commits.
+- Write clear, structured commit messages
+- Automatically manage version numbers using semantic versioning
+- Generate and maintain changelogs
+- Streamline the release process
+
+### Key Benefits
+
+With just a simple `cz bump` command, Commitizen handles:
+
+1. **Version Management**: Automatically bumps version numbers and updates version files based on your commit history
+2. **Changelog Generation**: Creates and updates changelogs following the [Keep a changelog][keepchangelog] format
+3. **Commit Standardization**: Enforces consistent commit message formats across your team
+
+This standardization makes your commit history more readable and meaningful, while the automation reduces manual work and potential errors in the release process.
 
 ### Features
 
-- Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits]
-- Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](./bump.md)
-- Generate a changelog using [Keep a changelog][keepchangelog]
-- Update your project's version files automatically
-- Display information about your commit rules (commands: schema, example, info)
-- Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md)
+- Interactive CLI for standardized commits with default [Conventional Commits][conventional_commits] support
+- Intelligent [version bumping](https://commitizen-tools.github.io/commitizen/commands/bump/) using [Semantic Versioning][semver]
+- Automatic [keep a changelog][keepchangelog] generation
+- Built-in commit validation with pre-commit hooks
+- [Customizable](https://commitizen-tools.github.io/commitizen/customization/) commit rules and templates
+- Multi-format version file support
+- Custom rules and plugins via pip
+
+## Getting Started
 
-## Requirements
+### Requirements
 
-[Python](https://www.python.org/downloads/) `3.8+`
+Before installing Commitizen, ensure you have:
 
-[Git][gitscm] `1.8.5.2+`
+- [Python](https://www.python.org/downloads/) `3.9+`
+- [Git][gitscm] `1.8.5.2+`
 
-## Installation
+### Installation
 
-To make commitizen available in your system
+#### Global Installation (Recommended)
 
+The recommended way to install Commitizen is using [`pipx`](https://pipx.pypa.io/) or [`uv`](https://docs.astral.sh/uv/), which ensures a clean, isolated installation:
+
+**Using pipx:**
 ```bash
-pip install --user -U Commitizen
+# Install Commitizen
+pipx install commitizen
+
+# Keep it updated
+pipx upgrade commitizen
 ```
 
-### Python project
+**Using uv:**
+```bash
+# Install commitizen
+uv tool install commitizen
 
-You can add it to your local project using one of these:
+# Keep it updated
+uv tool upgrade commitizen
+```
 
+**(For macOS users) Using Homebrew:**
+```bash
+brew install commitizen
+```
+
+#### Project-Specific Installation
+
+You can add Commitizen to your Python project using any of these package managers:
+
+**Using pip:**
 ```bash
 pip install -U commitizen
 ```
 
-for Poetry >= 1.2.0:
+**Using conda:**
+```bash
+conda install -c conda-forge commitizen
+```
 
+**Using Poetry:**
 ```bash
+# For Poetry >= 1.2.0
 poetry add commitizen --group dev
+
+# For Poetry < 1.2.0
+poetry add commitizen --dev
 ```
 
-for Poetry < 1.2.0:
+**Using uv:**
+```bash
+uv add --dev commitizen
+```
 
+**Using pdm:**
 ```bash
-poetry add commitizen --dev
+pdm add -d commitizen
 ```
 
-### macOS
+### Basic Commands
 
-via [homebrew](https://formulae.brew.sh/formula/commitizen):
+#### Initialize Commitizen
 
-```bash
-brew install commitizen
+To get started, you'll need to set up your configuration. You have two options:
+
+1. Use the interactive setup:
+```sh
+cz init
+```
+
+2. Manually create a configuration file (`.cz.toml` or `cz.toml`):
+```toml
+[tool.commitizen]
+version = "0.1.0"
+update_changelog_on_bump = true
 ```
 
-## Usage
+#### Create Commits
 
-Most of the time this is the only command you'll run:
+Create standardized commits using:
+```sh
+cz commit
+# or use the shortcut
+cz c
+```
 
+To sign off your commits:
+```sh
+cz commit -- --signoff
+# or use the shortcut
+cz commit -- -s
+```
+
+For more commit options, run `cz commit --help`.
+
+#### Version Management
+
+The most common command you'll use is:
 ```sh
 cz bump
 ```
 
-On top of that, you can use commitizen to assist you with the creation of commits:
+This command:
+
+- Bumps your project's version
+- Creates a git tag
+- Updates the changelog (if `update_changelog_on_bump` is enabled)
+- Updates version files
+
+You can customize:
+
+- [Version files](https://commitizen-tools.github.io/commitizen/commands/bump/#version_files)
+- [Version scheme](https://commitizen-tools.github.io/commitizen/commands/bump/#version_scheme)
+- [Version provider](https://commitizen-tools.github.io/commitizen/config/#version-providers)
+
+For all available options, see the [bump command documentation](https://commitizen-tools.github.io/commitizen/commands/bump/).
+
+### Advanced Usage
+
+#### Get Project Version
 
 ```sh
-cz commit
+# Get your project's version (instead of Commitizen's version)
+cz version -p
+# Preview changelog changes
+cz changelog --dry-run "$(cz version -p)"
+```
+
+This command is particularly useful for automation scripts and CI/CD pipelines.
+
+For example, you can use the output of the command `cz changelog --dry-run "$(cz version -p)"` to notify your team about a new release in Slack.
+
+#### Pre-commit Integration
+
+Commitizen can automatically validate your commit messages using pre-commit hooks.
+
+1. Add to your `.pre-commit-config.yaml`:
+```yaml
+---
+repos:
+  - repo: https://github.com/commitizen-tools/commitizen
+    rev: master  # Replace with latest tag
+    hooks:
+      - id: commitizen
+      - id: commitizen-branch
+        stages: [pre-push]
 ```
 
-Read more in the section [Getting Started](./getting_started.md).
+2. Install the hooks:
+```sh
+pre-commit install --hook-type commit-msg --hook-type pre-push
+```
+
+| Hook              | Recommended Stage |
+| ----------------- | ----------------- |
+| commitizen        | commit-msg        |
+| commitizen-branch | pre-push          |
+
+> **Note**: Replace `master` with the [latest tag](https://github.com/commitizen-tools/commitizen/tags) to avoid warnings. You can automatically update this with:
+> ```sh
+> pre-commit autoupdate
+> ```
+
+For more details about commit validation, see the [check command documentation](https://commitizen-tools.github.io/commitizen/commands/check/).
 
-### Help
+## Help & Reference
+
+### Command Line Interface
+
+Commitizen provides a comprehensive CLI with various commands. Here's the complete reference:
+
+![cz --help](images/cli_help/cz___help.svg)
+
+### Quick Reference
+
+| Command | Description | Alias |
+|---------|-------------|-------|
+| `cz init` | Initialize Commitizen configuration | - |
+| `cz commit` | Create a new commit | `cz c` |
+| `cz bump` | Bump version and update changelog | - |
+| `cz changelog` | Generate changelog | `cz ch` |
+| `cz check` | Validate commit messages | - |
+| `cz version` | Show version information | - |
+
+### Additional Resources
+
+- [Conventional Commits Specification][conventional_commits]
+- [Exit Codes Reference](https://commitizen-tools.github.io/commitizen/exit_codes/)
+- [Configuration Guide](https://commitizen-tools.github.io/commitizen/config/)
+- [Command Documentation](https://commitizen-tools.github.io/commitizen/commands/init/)
+
+### Getting Help
+
+For each command, you can get detailed help by adding `--help`:
 
 ```sh
-$ cz --help
-usage: cz [-h] [--debug] [-n NAME] [-nr NO_RAISE] {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version} ...
-
-Commitizen is a cli tool to generate conventional commits.
-For more information about the topic go to https://conventionalcommits.org/
-
-optional arguments:
-  -h, --help            show this help message and exit
-  --debug               use debug mode
-  -n NAME, --name NAME  use the given commitizen (default: cz_conventional_commits)
-  -nr NO_RAISE, --no-raise NO_RAISE
-                        comma separated error codes that won't rise error, e.g: cz -nr 1,2,3 bump. See codes at https://commitizen-
-                        tools.github.io/commitizen/exit_codes/
-
-commands:
-  {init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version}
-    init                init commitizen configuration
-    commit (c)          create new commit
-    ls                  show available commitizens
-    example             show commit example
-    info                show information about the cz
-    schema              show commit schema
-    bump                bump semantic version based on the git log
-    changelog (ch)      generate changelog (note that it will overwrite existing file)
-    check               validates that a commit message matches the commitizen schema
-    version             get the version of the installed commitizen or the current project (default: installed commitizen)
+cz commit --help
+cz bump --help
+cz changelog --help
 ```
 
+For more details, visit our [documentation site](https://commitizen-tools.github.io/commitizen/).
+
 ## Setting up bash completion
 
-When using bash as your shell (limited support for zsh, fish, and tcsh is available), Commitizen can use [argcomplete](https://kislyuk.github.io/argcomplete/) for auto-completion. For this argcomplete needs to be enabled.
+Commitizen supports command-line completion through [argcomplete](https://kislyuk.github.io/argcomplete/), which is automatically installed as a dependency. This feature provides intelligent auto-completion for all Commitizen commands and options.
+
+### Supported Shells
+
+- **Bash**: Full support
+- **Zsh**: Limited support
+- **Fish**: Limited support
+- **Tcsh**: Limited support
 
-argcomplete is installed when you install Commitizen since it's a dependency.
+### Installation Methods
 
-If Commitizen is installed globally, global activation can be executed:
+#### Global Installation (Recommended)
+
+If you installed Commitizen globally (e.g., using `pipx` or `brew`), you can enable global completion:
 
 ```bash
+# Enable global completion for all Python applications
 sudo activate-global-python-argcomplete
 ```
 
-For permanent (but not global) Commitizen activation, use:
+#### User-Specific Installation
+
+For a user-specific installation that persists across sessions:
 
 ```bash
+# Add to your shell's startup file (e.g., ~/.bashrc, ~/.zshrc)
 register-python-argcomplete cz >> ~/.bashrc
 ```
 
-For one-time activation of argcomplete for Commitizen only, use:
+#### Temporary Installation
+
+For one-time activation in your current shell session:
 
 ```bash
+# Activate completion for current session only
 eval "$(register-python-argcomplete cz)"
 ```
 
-For further information on activation, please visit the [argcomplete website](https://kislyuk.github.io/argcomplete/).
+### Verification
+
+After installation, you can verify the completion is working by:
+
+1. Opening a new terminal session
+2. Typing `cz` followed by a space and pressing `TAB` twice
+3. You should see a list of available commands
+
+For more detailed information about argcomplete configuration and troubleshooting, visit the [argcomplete documentation](https://kislyuk.github.io/argcomplete/).
 
 ## Sponsors
 
diff --git a/docs/bump.md b/docs/bump.md
deleted file mode 100644
index d0ceb90ea0..0000000000
--- a/docs/bump.md
+++ /dev/null
@@ -1,566 +0,0 @@
-![Bump version](images/bump.gif)
-
-## About
-
-`cz bump` **automatically** increases the version, based on the commits.
-
-The commits should follow the rules established by the committer in order to be parsed correctly.
-
-**prerelease** versions are supported (alpha, beta, release candidate).
-
-The version can also be **manually** bumped.
-
-The version format follows [PEP 0440][pep440] and [semantic versioning][semver].
-
-This means `MAJOR.MINOR.PATCH`
-
-| Increment | Description                 | Conventional commit map |
-| --------- | --------------------------- | ----------------------- |
-| `MAJOR`   | Breaking changes introduced | `BREAKING CHANGE`       |
-| `MINOR`   | New features                | `feat`                  |
-| `PATCH`   | Fixes                       | `fix` + everything else |
-
-[PEP 0440][pep440] is the default, you can switch by using the setting `version_scheme` or the cli:
-
-```sh
-cz bump --version-scheme semver
-```
-
-Some examples of pep440:
-
-```bash
-0.9.0
-0.9.1
-0.9.2
-0.9.10
-0.9.11
-1.0.0a0  # alpha
-1.0.0a1
-1.0.0b0  # beta
-1.0.0rc0 # release candidate
-1.0.0rc1
-1.0.0
-1.0.1
-1.1.0
-2.0.0
-2.0.1a
-```
-
-`post` releases are not supported yet.
-
-## Usage
-
-```bash
-$ cz bump --help
-usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog] [--no-verify] [--yes] [--tag-format TAG_FORMAT]
-               [--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}] [--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}]
-               [--check-consistency] [--annotated-tag] [--gpg-sign] [--changelog-to-stdout] [--git-output-to-stderr] [--retry] [--major-version-zero]
-               [--prerelease-offset PRERELEASE_OFFSET] [--version-scheme {semver,pep440}] [--version-type {semver,pep440}]
-               [MANUAL_VERSION]
-
-positional arguments:
-  MANUAL_VERSION        bump to the given version (e.g: 1.5.3)
-
-options:
-  -h, --help            show this help message and exit
-  --dry-run             show output to stdout, no commit, no modified files
-  --files-only          bump version in the files from the config
-  --local-version       bump only the local version portion
-  --changelog, -ch      generate the changelog for the newest version
-  --no-verify           this option bypasses the pre-commit and commit-msg hooks
-  --yes                 accept automatically questions done
-  --tag-format TAG_FORMAT
-                        the format used to tag the commit and read it, use it in existing projects, wrap around simple quotes
-  --bump-message BUMP_MESSAGE
-                        template used to create the release commit, useful when working with CI
-  --prerelease {alpha,beta,rc}, -pr {alpha,beta,rc}
-                        choose type of prerelease
-  --devrelease DEVRELEASE, -d DEVRELEASE
-                        specify non-negative integer for dev. release
-  --increment {MAJOR,MINOR,PATCH}
-                        manually specify the desired increment
-  --check-consistency, -cc
-                        check consistency among versions defined in commitizen configuration and version_files
-  --annotated-tag, -at  create annotated tag instead of lightweight one
-  --gpg-sign, -s        sign tag instead of lightweight one
-  --changelog-to-stdout
-                        Output changelog to the stdout
-  --git-output-to-stderr
-                        Redirect git output to stderr
-  --retry               retry commit if it fails the 1st time
-  --major-version-zero  keep major version at zero, even for breaking changes
-  --prerelease-offset PRERELEASE_OFFSET
-                        start pre-releases with this offset
-  --version-scheme {semver,pep440}
-                        choose version scheme
-  --version-type {semver,pep440}
-                        Deprecated, use --version-scheme
-```
-
-### `--files-only`
-
-Bumps the version in the files defined in `version_files` without creating a commit and tag on the git repository,
-
-```bash
-cz bump --files-only
-```
-
-### `--changelog`
-
-Generate a **changelog** along with the new version and tag when bumping.
-
-```bash
-cz bump --changelog
-```
-
-### `--check-consistency`
-
-Check whether the versions defined in `version_files` and the version in commitizen
-configuration are consistent before bumping version.
-
-```bash
-cz bump --check-consistency
-```
-
-For example, if we have `pyproject.toml`
-
-```toml
-[tool.commitizen]
-version = "1.21.0"
-version_files = [
-    "src/__version__.py",
-    "setup.py",
-]
-```
-
-`src/__version__.py`,
-
-```python
-__version__ = "1.21.0"
-```
-
-and `setup.py`.
-
-```python
-from setuptools import setup
-
-setup(..., version="1.0.5", ...)
-```
-
-If `--check-consistency` is used, commitizen will check whether the current version in `pyproject.toml`
-exists in all version_files and find out it does not exist in `setup.py` and fails.
-However, it will still update `pyproject.toml` and `src/__version__.py`.
-
-To fix it, you'll first `git checkout .` to reset to the status before trying to bump and update
-the version in `setup.py` to `1.21.0`
-
-### `--local-version`
-
-Bump the local portion of the version.
-
-```bash
-cz bump --local-version
-```
-
-For example, if we have `pyproject.toml`
-
-```toml
-[tool.commitizen]
-version = "5.3.5+0.1.0"
-```
-
-If `--local-version` is used, it will bump only the local version `0.1.0` and keep the public version `5.3.5` intact, bumping to the version `5.3.5+0.2.0`.
-
-### `--annotated-tag`
-
-If `--annotated-tag` is used, commitizen will create annotated tags. Also available via configuration, in `pyproject.toml` or `.cz.toml`.
-
-### `--annotated-tag-message`
-If `--annotated-tag-message` is used, commitizen will create annotated tags with the given message.
-
-### `--changelog-to-stdout`
-
-If `--changelog-to-stdout` is used, the incremental changelog generated by the bump
-will be sent to the stdout, and any other message generated by the bump will be
-sent to stderr.
-
-If `--changelog` is not used with this command, it is still smart enough to
-understand that the user wants to create a changelog. It is recommended to be
-explicit and use `--changelog` (or the setting `update_changelog_on_bump`).
-
-This command is useful to "transport" the newly created changelog.
-It can be sent to an auditing system, or to create a Github Release.
-
-Example:
-
-```bash
-cz bump --changelog --changelog-to-stdout > body.md
-```
-
-### `--git-output-to-stderr`
-
-If `--git-output-to-stderr` is used, git commands output is redirected to stderr.
-
-This command is useful when used with `--changelog-to-stdout` and piping the output to a file,
-and you don't want the `git commit` output polluting  the stdout.
-
-### `--retry`
-
-If you use tools like [pre-commit](https://pre-commit.com/), add this flag.
-It will retry the commit if it fails the 1st time.
-
-Useful to combine with code formatters, like [Prettier](https://prettier.io/).
-
-### `--major-version-zero`
-
-A project in its initial development should have a major version zero, and even breaking changes
-should not bump that major version from zero. This command ensures that behavior.
-
-If `--major-version-zero` is used for projects that have a version number greater than zero it fails.
-If used together with a manual version the command also fails.
-
-We recommend setting `major_version_zero = true` in your configuration file while a project
-is in its initial development. Remove that configuration using a breaking-change commit to bump
-your project’s major version to `v1.0.0` once your project has reached maturity.
-
-### `--version-scheme`
-
-Choose the version format, options: `pep440`, `semver`.
-
-Default: `pep440`
-
-Recommended for python: `pep440`
-
-Recommended for other: `semver`
-
-You can also set this in the [configuration](#version_scheme) with `version_scheme = "semver"`.
-
-[pep440][pep440] and [semver][semver] are quite similar, their difference lies in
-how the prereleases look.
-
-| schemes        | pep440         | semver          |
-| -------------- | -------------- | --------------- |
-| non-prerelease | `0.1.0`        | `0.1.0`         |
-| prerelease     | `0.3.1a0`      | `0.3.1-a0`      |
-| devrelease     | `0.1.1.dev1`   | `0.1.1-dev1`    |
-| dev and pre    | `1.0.0a3.dev1` | `1.0.0-a3-dev1` |
-
-Can I transition from one to the other?
-
-Yes, you shouldn't have any issues.
-
-### `--template`
-
-Provides your own changelog jinja template.
-See [the template customization section](customization.md#customizing-the-changelog-template)
-
-### `--extra`
-
-Provides your own changelog extra variables by using the `extras` settings or the `--extra/-e` parameter.
-
-```bash
-cz bump --changelog --extra key=value -e short="quoted value"
-```
-
-See [the template customization section](customization.md#customizing-the-changelog-template).
-
-## Avoid raising errors
-
-Some situations from commitizen raise an exit code different than 0.
-If the error code is different than 0, any CI or script running commitizen might be interrupted.
-
-If you have a special use case, where you don't want to raise one of this error codes, you can
-tell commitizen to not raise them.
-
-### Recommended use case
-
-At the moment, we've identified that the most common error code to skip is
-
-| Error name        | Exit code |
-| ----------------- | --------- |
-| NoneIncrementExit | 21        |
-
-There are some situations where you don't want to get an error code when some
-commits do not match your rules, you just want those commits to be skipped.
-
-```sh
-cz -nr 21 bump
-```
-
-### Easy way
-
-Check which error code was raised by commitizen by running in the terminal
-
-```sh
-echo $?
-```
-
-The output should be an integer like this
-
-```sh
-3
-```
-
-And then you can tell commitizen to ignore it:
-
-```sh
-cz --no-raise 3
-```
-
-You can tell commitizen to skip more than one if needed:
-
-```sh
-cz --no-raise 3,4,5
-```
-
-### Longer way
-
-Check the list of [exit_codes](./exit_codes.md) and understand which one you have
-to skip and why.
-
-Remember to document somewhere this, because you'll forget.
-
-For example if the system raises a `NoneIncrementExit` error, you look it up
-on the list and then you can use the exit code:
-
-```sh
-cz -nr 21 bump
-```
-
-## Configuration
-
-### `tag_format`
-
-`tag_format` and `version_scheme` are combined to make Git tag names from versions.
-
-These are used in:
-
-* `cz bump`: Find previous release tag (exact match) and generate new tag.
-* Find previous release tags in `cz changelog`.
-  * If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match.
-  * `--rev-range` is converted to Git tag names with `tag_format` before searching Git history.
-* If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags:
-  * If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix)
-  * If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases)
-
-Commitizen supports 2 types of formats, a simple and a more complex.
-
-```bash
-cz bump --tag-format="v$version"
-```
-
-```bash
-cz bump --tag-format="v$minor.$major.$patch$prerelease.$devrelease"
-```
-
-In your `pyproject.toml` or `.cz.toml`
-
-```toml
-[tool.commitizen]
-tag_format = "v$major.$minor.$patch$prerelease"
-```
-
-The variables must be preceded by a `$` sign. Default is `$version`.
-
-Supported variables:
-
-| Variable      | Description                                 |
-| ------------- | ------------------------------------------- |
-| `$version`    | full generated version                      |
-| `$major`      | MAJOR increment                             |
-| `$minor`      | MINOR increment                             |
-| `$patch`      | PATCH increment                             |
-| `$prerelease` | Prerelease (alpha, beta, release candidate) |
-| `$devrelease` | Development release                         |
-
----
-
-### `version_files` \*
-
-It is used to identify the files which should be updated with the new version.
-It is also possible to provide a pattern for each file, separated by colons (`:`).
-
-Commitizen will update its configuration file automatically (`pyproject.toml`, `.cz`) when bumping,
-regarding if the file is present or not in `version_files`.
-
-\* Renamed from `files` to `version_files`.
-
-Some examples
-
-`pyproject.toml` or `.cz.toml`
-
-```toml
-[tool.commitizen]
-version_files = [
-    "src/__version__.py",
-    "setup.py:version"
-]
-```
-
-In the example above, we can see the reference `"setup.py:version"`.
-This means that it will find a file `setup.py` and will only make a change
-in a line containing the `version` substring.
-
----
-
-### `bump_message`
-
-Template used to specify the commit message generated when bumping.
-
-defaults to: `bump: version $current_version → $new_version`
-
-| Variable           | Description                         |
-| ------------------ | ----------------------------------- |
-| `$current_version` | the version existing before bumping |
-| `$new_version`     | version generated after bumping     |
-
-Some examples
-
-`pyproject.toml` or `.cz.toml`
-
-```toml
-[tool.commitizen]
-bump_message = "release $current_version → $new_version [skip-ci]"
-```
-
----
-
-### `update_changelog_on_bump`
-
-When set to `true` the changelog is always updated incrementally when running `cz bump`, so the user does not have to provide the `--changelog` flag every time.
-
-defaults to: `false`
-
-```toml
-[tool.commitizen]
-update_changelog_on_bump = true
-```
-
----
-
-### `annotated_tag`
-
-When set to `true` commitizen will create annotated tags.
-
-```toml
-[tool.commitizen]
-annotated_tag = true
-```
-
----
-
-### `gpg_sign`
-
-When set to `true` commitizen will create gpg signed tags.
-
-```toml
-[tool.commitizen]
-gpg_sign = true
-```
-
----
-
-### `major_version_zero`
-
-When set to `true` commitizen will keep the major version at zero.
-Useful during the initial development stage of your project.
-
-Defaults to: `false`
-
-```toml
-[tool.commitizen]
-major_version_zero = true
-```
-
----
-
-### `pre_bump_hooks`
-
-A list of optional commands that will run right _after_ updating `version_files`
-and _before_ actual committing and tagging the release.
-
-Useful when you need to generate documentation based on the new version. During
-execution of the script, some environment variables are available:
-
-| Variable                     | Description                                                |
-| ---------------------------- | ---------------------------------------------------------- |
-| `CZ_PRE_IS_INITIAL`          | `True` when this is the initial release, `False` otherwise |
-| `CZ_PRE_CURRENT_VERSION`     | Current version, before the bump                           |
-| `CZ_PRE_CURRENT_TAG_VERSION` | Current version tag, before the bump                       |
-| `CZ_PRE_NEW_VERSION`         | New version, after the bump                                |
-| `CZ_PRE_NEW_TAG_VERSION`     | New version tag, after the bump                            |
-| `CZ_PRE_MESSAGE`             | Commit message of the bump                                 |
-| `CZ_PRE_INCREMENT`           | Whether this is a `MAJOR`, `MINOR` or `PATH` release       |
-| `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available                   |
-
-```toml
-[tool.commitizen]
-pre_bump_hooks = [
-  "scripts/generate_documentation.sh"
-]
-```
-
----
-
-### `post_bump_hooks`
-
-A list of optional commands that will run right _after_ committing and tagging the release.
-
-Useful when you need to send notifications about a release, or further automate deploying the
-release. During execution of the script, some environment variables are available:
-
-| Variable                       | Description                                                 |
-| ------------------------------ | ----------------------------------------------------------- |
-| `CZ_POST_WAS_INITIAL`          | `True` when this was the initial release, `False` otherwise |
-| `CZ_POST_PREVIOUS_VERSION`     | Previous version, before the bump                           |
-| `CZ_POST_PREVIOUS_TAG_VERSION` | Previous version tag, before the bump                       |
-| `CZ_POST_CURRENT_VERSION`      | Current version, after the bump                             |
-| `CZ_POST_CURRENT_TAG_VERSION`  | Current version tag, after the bump                         |
-| `CZ_POST_MESSAGE`              | Commit message of the bump                                  |
-| `CZ_POST_INCREMENT`            | Whether this was a `MAJOR`, `MINOR` or `PATH` release      |
-| `CZ_POST_CHANGELOG_FILE_NAME`  | Path to the changelog file, if available                    |
-
-```toml
-[tool.commitizen]
-post_bump_hooks = [
-  "scripts/slack_notification.sh"
-]
-```
-
-### `prerelease_offset`
-
-Offset with which to start counting prereleses.
-
-Defaults to: `0`
-
-```toml
-[tool.commitizen]
-prerelease_offset = 1
-```
-
-### `version_scheme`
-
-Choose version scheme
-
-| schemes        | pep440         | semver          |
-| -------------- | -------------- | --------------- |
-| non-prerelease | `0.1.0`        | `0.1.0`         |
-| prerelease     | `0.3.1a0`      | `0.3.1-a0`      |
-| devrelease     | `0.1.1.dev1`   | `0.1.1-dev1`    |
-| dev and pre    | `1.0.0a3.dev1` | `1.0.0-a3-dev1` |
-
-Options: `sever`, `pep440`
-
-Defaults to: `pep440`
-
-```toml
-[tool.commitizen]
-version_scheme = "semver"
-```
-
-## Custom bump
-
-Read the [customizing section](./customization.md).
-
-[pep440]: https://www.python.org/dev/peps/pep-0440/
-[semver]: https://semver.org/
diff --git a/docs/commands/bump.md b/docs/commands/bump.md
new file mode 100644
index 0000000000..548f6e3616
--- /dev/null
+++ b/docs/commands/bump.md
@@ -0,0 +1,665 @@
+![Bump version](../images/bump.gif)
+
+## About
+
+`cz bump` is a powerful command that **automatically** determines and increases your project's version number based on your commit history. It analyzes your commits to determine the appropriate version increment according to semantic versioning principles.
+
+### Key Features
+
+- **Automatic Version Detection**: Analyzes commit history to determine the appropriate version bump
+- **Manual Version Control**: Supports manual version specification when needed
+- **Pre-release Support**: Handles alpha, beta, and release candidate versions
+- **Multiple Version Schemes**: Supports both [PEP 0440][pep440] and [semantic versioning][semver] formats
+
+### Version Increment Rules
+
+The version follows the `MAJOR.MINOR.PATCH` format, with increments determined by your commit types:
+
+| Increment | Description                 | Conventional commit map |
+| --------- | --------------------------- | ----------------------- |
+| `MAJOR`   | Breaking changes introduced | `BREAKING CHANGE`, bang (e.g. `feat!`)|
+| `MINOR`   | New features                | `feat`                  |
+| `PATCH`   | Fixes and improvements      | `fix`, `perf`, `refactor`|
+
+### Version Schemes
+
+By default, Commitizen uses [PEP 0440][pep440] for version formatting. You can switch to semantic versioning using either:
+
+1. Command line:
+```sh
+cz bump --version-scheme semver
+```
+
+2. Configuration file:
+```toml title="pyproject.toml"
+[tool.commitizen]
+version_scheme = "semver"
+```
+
+### PEP440 Version Examples
+
+Commitizen supports the [PEP 440][pep440] version format, which includes several version types. Here are examples of each:
+
+#### Standard Releases
+```text
+0.9.0    # Initial development release
+0.9.1    # Patch release
+0.9.2    # Another patch release
+0.9.10   # Tenth patch release
+0.9.11   # Eleventh patch release
+1.0.0    # First stable release
+1.0.1    # Patch release after stable
+1.1.0    # Minor feature release
+2.0.0    # Major version release
+```
+
+#### Pre-releases
+```text
+1.0.0a0  # Alpha release 0
+1.0.0a1  # Alpha release 1
+1.0.0b0  # Beta release 0
+1.0.0rc0 # Release candidate 0
+1.0.0rc1 # Release candidate 1
+```
+
+#### Development Releases
+```text
+1.0.0.dev0  # Development release 0
+1.0.0.dev1  # Development release 1
+```
+
+#### Combined Pre-release and Development
+```text
+1.0.0a1.dev0  # Development release 0 of alpha 1
+1.0.0b2.dev1  # Development release 1 of beta 2
+```
+
+> **Note**: `post` releases (e.g., `1.0.0.post1`) are not currently supported.
+
+## Usage
+
+![cz bump --help](../images/cli_help/cz_bump___help.svg)
+
+### `--files-only`
+
+Bumps the version in the files defined in `version_files` without creating a commit and tag on the git repository,
+
+```bash
+cz bump --files-only
+```
+
+### `--changelog`
+
+Generate a **changelog** along with the new version and tag when bumping.
+
+```bash
+cz bump --changelog
+```
+
+### `--prerelease`
+
+The bump is a pre-release bump, meaning that in addition to a possible version bump the new version receives a
+pre-release segment compatible with the bump’s version scheme, where the segment consist of a _phase_ and a
+non-negative number. Supported options for `--prerelease` are the following phase names `alpha`, `beta`, or
+`rc` (release candidate). For more details, refer to the
+[Python Packaging User Guide](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases).
+
+Note that as per [semantic versioning spec](https://semver.org/#spec-item-9)
+
+> Pre-release versions have a lower precedence than the associated normal version. A pre-release version
+> indicates that the version is unstable and might not satisfy the intended compatibility requirements
+> as denoted by its associated normal version.
+
+For example, the following versions (using the [PEP 440](https://peps.python.org/pep-0440/) scheme) are ordered
+by their precedence and showcase how a release might flow through a development cycle:
+
+- `1.0.0` is the currently published version
+- `1.0.1a0` after committing a `fix:` for pre-release
+- `1.1.0a1` after committing an additional `feat:` for pre-release
+- `1.1.0b0` after bumping a beta release
+- `1.1.0rc0` after bumping the release candidate
+- `1.1.0` next feature release
+
+### `--increment-mode`
+
+By default, `--increment-mode` is set to `linear`, which ensures that bumping pre-releases _maintains linearity_:
+bumping of a pre-release with lower precedence than the current pre-release phase maintains the current phase of
+higher precedence. For example, if the current version is `1.0.0b1` then bumping with `--prerelease alpha` will
+continue to bump the "beta" phase.
+
+Setting `--increment-mode` to `exact` instructs `cz bump` to instead apply the
+exact changes that have been specified with `--increment` or determined from the commit log. For example,
+`--prerelease beta` will always result in a `b` tag, and `--increment PATCH` will always increase the patch component.
+
+Below are some examples that illustrate the difference in behavior:
+
+| Increment | Pre-release | Start Version | `--increment-mode=linear` | `--increment-mode=exact` |
+|-----------|-------------|---------------|---------------------------|--------------------------|
+| `MAJOR`   |             | `2.0.0b0`     | `2.0.0`                   | `3.0.0`                  |
+| `MINOR`   |             | `2.0.0b0`     | `2.0.0`                   | `2.1.0`                  |
+| `PATCH`   |             | `2.0.0b0`     | `2.0.0`                   | `2.0.1`                  |
+| `MAJOR`   | `alpha`     | `2.0.0b0`     | `3.0.0a0`                 | `3.0.0a0`                |
+| `MINOR`   | `alpha`     | `2.0.0b0`     | `2.0.0b1`                 | `2.1.0a0`                |
+| `PATCH`   | `alpha`     | `2.0.0b0`     | `2.0.0b1`                 | `2.0.1a0`                |
+
+### `--check-consistency`
+
+Check whether the versions defined in `version_files` and the version in Commitizen
+configuration are consistent before bumping version.
+
+```bash
+cz bump --check-consistency
+```
+
+For example, if we have `pyproject.toml`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+version = "1.21.0"
+version_files = [
+    "src/__version__.py",
+    "setup.py",
+]
+```
+
+`src/__version__.py`
+
+
+```python title="src/__version__.py"
+__version__ = "1.21.0"
+```
+
+and `setup.py`
+
+```python title="setup.py"
+from setuptools import setup
+
+setup(..., version="1.0.5", ...)
+```
+
+If `--check-consistency` is used, Commitizen will check whether the current version in `pyproject.toml`
+exists in all version_files and find out it does not exist in `setup.py` and fails.
+However, it will still update `pyproject.toml` and `src/__version__.py`.
+
+To fix it, you'll first `git checkout .` to reset to the status before trying to bump and update
+the version in `setup.py` to `1.21.0`
+
+### `--local-version`
+
+Bump the local portion of the version.
+
+```bash
+cz bump --local-version
+```
+
+For example, if we have `pyproject.toml`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+version = "5.3.5+0.1.0"
+```
+
+If `--local-version` is used, it will bump only the local version `0.1.0` and keep the public version `5.3.5` intact, bumping to the version `5.3.5+0.2.0`.
+
+### `--annotated-tag`
+
+If `--annotated-tag` is used, Commitizen will create annotated tags. It is also available via configuration, in `pyproject.toml` or `.cz.toml`.
+
+### `--annotated-tag-message`
+
+If `--annotated-tag-message` is used, Commitizen will create annotated tags with the given message.
+
+### `--changelog-to-stdout`
+
+If `--changelog-to-stdout` is used, the incremental changelog generated by the bump
+will be sent to the stdout, and any other message generated by the bump will be
+sent to stderr.
+
+If `--changelog` is not used with this command, it is still smart enough to
+understand that the user wants to create a changelog. It is recommended to be
+explicit and use `--changelog` (or the setting `update_changelog_on_bump`).
+
+This command is useful to "transport" the newly created changelog.
+It can be sent to an auditing system, or to create a GitHub Release.
+
+Example:
+
+```bash
+cz bump --changelog --changelog-to-stdout > body.md
+```
+
+### `--git-output-to-stderr`
+
+If `--git-output-to-stderr` is used, git commands output is redirected to stderr.
+
+This command is useful when used with `--changelog-to-stdout` and piping the output to a file,
+and you don't want the `git commit` output polluting  the stdout.
+
+### `--retry`
+
+If you use tools like [pre-commit](https://pre-commit.com/), add this flag.
+It will retry the commit if it fails the 1st time.
+
+Useful to combine with code formatters, like [Prettier](https://prettier.io/).
+
+### `--major-version-zero`
+
+A project in its initial development should have a major version zero, and even breaking changes
+should not bump that major version from zero. This command ensures that behavior.
+
+If `--major-version-zero` is used for projects that have a version number greater than zero it fails.
+If used together with a manual version the command also fails.
+
+We recommend setting `major_version_zero = true` in your configuration file while a project
+is in its initial development. Remove that configuration using a breaking-change commit to bump
+your project's major version to `v1.0.0` once your project has reached maturity.
+
+### `--version-scheme`
+
+Choose the version format, options: `pep440`, `semver`.
+
+Default: `pep440`
+
+Recommended for python: `pep440`
+
+Recommended for other: `semver`
+
+You can also set this in the [configuration](#version_scheme) with `version_scheme = "semver"`.
+
+[pep440][pep440] and [semver][semver] are quite similar, their difference lies in
+how the prereleases look.
+
+| schemes        | pep440         | semver          |
+| -------------- | -------------- | --------------- |
+| non-prerelease | `0.1.0`        | `0.1.0`         |
+| prerelease     | `0.3.1a0`      | `0.3.1-a0`      |
+| devrelease     | `0.1.1.dev1`   | `0.1.1-dev1`    |
+| dev and pre    | `1.0.0a3.dev1` | `1.0.0-a3-dev1` |
+
+Can I transition from one to the other?
+
+Yes, you shouldn't have any issues.
+
+### `--template`
+
+Provides your own changelog jinja template.
+See [the template customization section](../customization.md#customizing-the-changelog-template)
+
+### `--extra`
+
+Provides your own changelog extra variables by using the `extras` settings or the `--extra/-e` parameter.
+
+```bash
+cz bump --changelog --extra key=value -e short="quoted value"
+```
+
+See [the template customization section](../customization.md#customizing-the-changelog-template).
+
+### `--build-metadata`
+
+Provides a way to specify additional metadata in the version string. This parameter is not compatible with `--local-version` as it uses the same part of the version string.
+
+```bash
+cz bump --build-metadata yourmetadata
+```
+
+Will create a version like `1.1.2+yourmetadata`.
+This can be useful for multiple things
+- Git hash in version
+- Labeling the version with additional metadata.
+
+Note that Commitizen ignores everything after `+` when it bumps the version. It is therefore safe to write different build-metadata between versions.
+
+You should normally not use this functionality, but if you decide to do, keep in mind that
+- Version `1.2.3+a`, and `1.2.3+b` are the same version! Tools should not use the string after `+` for version calculation. This is probably not a guarantee (example in helm) even tho it is in the spec.
+- It might be problematic having the metadata in place when doing upgrades depending on what tool you use.
+
+### `--get-next`
+
+Provides a way to determine the next version and write it to stdout. This parameter is not compatible with `--changelog`
+and `manual version`.
+
+```bash
+cz bump --get-next
+```
+
+Will output the next version, e.g., `1.2.3`. This can be useful for determining the next version based on CI for non
+production environments/builds.
+
+This behavior differs from the `--dry-run` flag. The `--dry-run` flag provides a more detailed output and can also show
+the changes as they would appear in the changelog file.
+
+The following output is the result of `cz bump --dry-run`:
+
+```
+bump: version 3.28.0 → 3.29.0
+tag to create: v3.29.0
+increment detected: MINOR
+```
+
+The following output is the result of `cz bump --get-next`:
+
+```
+3.29.0
+```
+
+The `--get-next` flag will raise a `NoneIncrementExit` if the found commits are not eligible for a version bump.
+
+For information on how to suppress this exit, see [avoid raising errors](#avoid-raising-errors).
+
+### `--allow-no-commit`
+
+Allow the project version to be bumped even when there's no eligible version. This is most useful when used with `--increment {MAJOR,MINOR,PATCH}` or `[MANUL_VERSION]`
+
+```sh
+# bump a minor version even when there's only bug fixes, documentation changes or even no commits
+cz bump --incremental MINOR --allow-no-commit
+
+# bump version to 2.0.0 even when there's no breaking changes changes or even no commits
+cz bump --allow-no-commit 2.0.0
+```
+
+## Avoid raising errors
+
+Some situations from Commitizen raise an exit code different from 0.
+If the error code is different from 0, any CI or script running Commitizen might be interrupted.
+
+If you have a special use case, where you don't want to raise one of this error codes, you can
+tell Commitizen to not raise them.
+
+### Recommended use case
+
+At the moment, we've identified that the most common error code to skip is
+
+| Error name        | Exit code |
+| ----------------- | --------- |
+| NoneIncrementExit | 21        |
+
+There are some situations where you don't want to get an error code when some
+commits do not match your rules, you just want those commits to be skipped.
+
+```sh
+cz -nr 21 bump
+```
+
+### Easy way
+
+Check which error code was raised by Commitizen by running in the terminal
+
+```sh
+echo $?
+```
+
+The output should be an integer like this
+
+```sh
+3
+```
+
+And then you can tell Commitizen to ignore it:
+
+```sh
+cz --no-raise 3
+```
+
+You can tell Commitizen to skip more than one if needed:
+
+```sh
+cz --no-raise 3,4,5
+```
+
+### Longer way
+
+Check the list of [exit_codes](../exit_codes.md) and understand which one you have
+to skip and why.
+
+Remember to document somewhere this, because you'll forget.
+
+For example if the system raises a `NoneIncrementExit` error, you look it up
+on the list, and then you can use the exit code:
+
+```sh
+cz -nr 21 bump
+```
+
+## Configuration
+
+### `tag_format`
+
+`tag_format` and `version_scheme` are combined to make Git tag names from versions.
+
+These are used in:
+
+- `cz bump`: Find previous release tag (exact match) and generate new tag.
+- Find previous release tags in `cz changelog`.
+  - If `--incremental`: Using the latest version found in the changelog, scan existing Git tags with 89\% similarity match.
+  - `--rev-range` is converted to Git tag names with `tag_format` before searching Git history.
+- If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags:
+  - If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix)
+  - If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases)
+
+Commitizen supports 2 types of formats, a simple and a more complex.
+
+```bash
+cz bump --tag-format="v$version"
+```
+
+```bash
+cz bump --tag-format="v$minor.$major.$patch$prerelease.$devrelease"
+```
+
+In your `pyproject.toml` or `.cz.toml`
+
+```toml
+[tool.commitizen]
+tag_format = "v$major.$minor.$patch$prerelease"
+```
+
+The variables must be preceded by a `$` sign and optionally can be wrapped in `{}`. The default is `$version`.
+
+Supported variables:
+
+| Variable                       | Description                                 |
+|--------------------------------|---------------------------------------------|
+| `$version`, `${version}`       | full generated version                      |
+| `$major`, `${major}`           | MAJOR increment                             |
+| `$minor`, `${minor}`           | MINOR increment                             |
+| `$patch`, `${patch}`           | PATCH increment                             |
+| `$prerelease`, `${prerelease}` | Prerelease (alpha, beta, release candidate) |
+| `$devrelease`, ${devrelease}`  | Development release                         |
+
+---
+
+### `version_files` \*
+
+It is used to identify the files or glob patterns which should be updated with the new version.
+It is also possible to provide a pattern for each file, separated by colons (`:`).
+
+Commitizen will update its configuration file automatically (`pyproject.toml`, `.cz`) when bumping,
+regarding if the file is present or not in `version_files`.
+
+\* Renamed from `files` to `version_files`.
+
+Some examples
+
+`pyproject.toml`, `.cz.toml` or `cz.toml`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+version_files = [
+    "src/__version__.py",
+    "packages/*/pyproject.toml:version",
+    "setup.py:version",
+]
+```
+
+In the example above, we can see the reference `"setup.py:version"`.
+This means that it will find a file `setup.py` and will only make a change
+in a line containing the `version` substring.
+
+!!! note
+    Files can be specified using relative (to the execution) paths, absolute paths, or glob patterns.
+
+---
+
+### `bump_message`
+
+Template used to specify the commit message generated when bumping.
+
+Defaults to: `bump: version $current_version → $new_version`
+
+| Variable           | Description                         |
+| ------------------ | ----------------------------------- |
+| `$current_version` | the version existing before bumping |
+| `$new_version`     | version generated after bumping     |
+
+Some examples
+
+`pyproject.toml`, `.cz.toml` or `cz.toml`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+bump_message = "release $current_version → $new_version [skip-ci]"
+```
+
+---
+
+### `update_changelog_on_bump`
+
+When set to `true` the changelog is always updated incrementally when running `cz bump`, so the user does not have to provide the `--changelog` flag every time.
+
+Defaults to: `false`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+update_changelog_on_bump = true
+```
+
+---
+
+### `annotated_tag`
+
+When set to `true`, Commitizen will create annotated tags.
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+annotated_tag = true
+```
+
+---
+
+### `gpg_sign`
+
+When set to `true`, Commitizen will create gpg signed tags.
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+gpg_sign = true
+```
+
+---
+
+### `major_version_zero`
+
+When set to `true`, Commitizen will keep the major version at zero.
+Useful during the initial development stage of your project.
+
+Defaults to: `false`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+major_version_zero = true
+```
+
+---
+
+### `pre_bump_hooks`
+
+A list of optional commands that will run right _after_ updating `version_files`
+and _before_ actual committing and tagging the release.
+
+Useful when you need to generate documentation based on the new version. During
+execution of the script, some environment variables are available:
+
+| Variable                     | Description                                                |
+| ---------------------------- | ---------------------------------------------------------- |
+| `CZ_PRE_IS_INITIAL`          | `True` when this is the initial release, `False` otherwise |
+| `CZ_PRE_CURRENT_VERSION`     | Current version, before the bump                           |
+| `CZ_PRE_CURRENT_TAG_VERSION` | Current version tag, before the bump                       |
+| `CZ_PRE_NEW_VERSION`         | New version, after the bump                                |
+| `CZ_PRE_NEW_TAG_VERSION`     | New version tag, after the bump                            |
+| `CZ_PRE_MESSAGE`             | Commit message of the bump                                 |
+| `CZ_PRE_INCREMENT`           | Whether this is a `MAJOR`, `MINOR` or `PATH` release       |
+| `CZ_PRE_CHANGELOG_FILE_NAME` | Path to the changelog file, if available                   |
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+pre_bump_hooks = [
+  "scripts/generate_documentation.sh"
+]
+```
+
+---
+
+### `post_bump_hooks`
+
+A list of optional commands that will run right _after_ committing and tagging the release.
+
+Useful when you need to send notifications about a release, or further automate deploying the
+release. During execution of the script, some environment variables are available:
+
+| Variable                       | Description                                                 |
+| ------------------------------ | ----------------------------------------------------------- |
+| `CZ_POST_WAS_INITIAL`          | `True` when this was the initial release, `False` otherwise |
+| `CZ_POST_PREVIOUS_VERSION`     | Previous version, before the bump                           |
+| `CZ_POST_PREVIOUS_TAG_VERSION` | Previous version tag, before the bump                       |
+| `CZ_POST_CURRENT_VERSION`      | Current version, after the bump                             |
+| `CZ_POST_CURRENT_TAG_VERSION`  | Current version tag, after the bump                         |
+| `CZ_POST_MESSAGE`              | Commit message of the bump                                  |
+| `CZ_POST_INCREMENT`            | Whether this was a `MAJOR`, `MINOR` or `PATH` release      |
+| `CZ_POST_CHANGELOG_FILE_NAME`  | Path to the changelog file, if available                    |
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+post_bump_hooks = [
+  "scripts/slack_notification.sh"
+]
+```
+
+### `prerelease_offset`
+
+Offset with which to start counting prereleases.
+
+Defaults to: `0`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+prerelease_offset = 1
+```
+
+### `version_scheme`
+
+Choose version scheme
+
+| schemes        | pep440         | semver          | semver2               |
+| -------------- | -------------- | --------------- | --------------------- |
+| non-prerelease | `0.1.0`        | `0.1.0`         | `0.1.0`               |
+| prerelease     | `0.3.1a0`      | `0.3.1-a0`      | `0.3.1-alpha.0`       |
+| devrelease     | `0.1.1.dev1`   | `0.1.1-dev1`    | `0.1.1-dev.1`         |
+| dev and pre    | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | `1.0.0-alpha.3.dev.1` |
+
+Options: `pep440`, `semver`, `semver2`
+
+Defaults to: `pep440`
+
+```toml title="pyproject.toml"
+[tool.commitizen]
+version_scheme = "semver"
+```
+
+## Custom bump
+
+Read the [customizing section](../customization.md).
+
+[pep440]: https://www.python.org/dev/peps/pep-0440/
+[semver]: https://semver.org/
diff --git a/docs/changelog.md b/docs/commands/changelog.md
similarity index 71%
rename from docs/changelog.md
rename to docs/commands/changelog.md
index 029882c12b..aee1a0e075 100644
--- a/docs/changelog.md
+++ b/docs/commands/changelog.md
@@ -11,33 +11,7 @@ update_changelog_on_bump = true
 
 ## Usage
 
-```bash
-$ cz changelog --help
-usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME] [--unreleased-version UNRELEASED_VERSION] [--incremental] [--start-rev START_REV]
-                    [--template TEMPLATE] [--extra EXTRA]
-                    [rev_range]
-
-positional arguments:
-  rev_range             generates changelog for the given version (e.g: 1.5.3) or version range (e.g: 1.5.3..1.7.9)
-
-optional arguments:
-  -h, --help            show this help message and exit
-  --dry-run             show changelog to stdout
-  --file-name FILE_NAME
-                        file name of changelog (default: 'CHANGELOG.md')
-  --unreleased-version UNRELEASED_VERSION
-                        set the value for the new version (use the tag value), instead of using unreleased
-  --incremental         generates changelog from last created version, useful if the changelog has been manually modified
-  --start-rev START_REV
-                        start rev of the changelog. If not set, it will generate changelog from the start
-  --merge-prerelease
-                        collect all changes from prereleases into next non-prerelease. If not set, it will include prereleases in the changelog
-                        start rev of the changelog.If not set, it will generate changelog from the start
-  --template TEMPLATE, -t TEMPLATE
-                        changelog template file name (relative to the current working directory)
-  --extra EXTRA, -e EXTRA
-                        a changelog extra variable (in the form 'key=value')
-```
+![cz changelog --help](../images/cli_help/cz_changelog___help.svg)
 
 ### Examples
 
@@ -82,7 +56,7 @@ These are the variables used by the changelog generator.
 It will create a full block like above per version found in the tags.
 And it will create a list of the commits found.
 The `change_type` and the `scope` are optional, they don't need to be provided,
-but if your regex does they will be rendered.
+but if your regex does, they will be rendered.
 
 The format followed by the changelog is the one from [keep a changelog][keepachangelog]
 and the following variables are expected:
@@ -134,7 +108,7 @@ cz bump --changelog
 
 This value can be updated in the `toml` file with the key `changelog_file` under `tools.commitizen`
 
-Specify the name of the output file, remember that changelog only works with markdown.
+Specify the name of the output file, remember that changelog only works with Markdown.
 
 ```bash
 cz changelog --file-name="CHANGES.md"
@@ -146,7 +120,7 @@ This flag can be set in the `toml` file with the key `changelog_incremental` und
 
 Benefits:
 
-- Build from latest version found in changelog, this is useful if you have a different changelog and want to use commitizen
+- Build from the latest version found in changelog, this is useful if you have a different changelog and want to use commitizen
 - Update unreleased area
 - Allows users to manually touch the changelog without being rewritten.
 
@@ -195,7 +169,7 @@ changelog_merge_prerelease = true
 ### `template`
 
 Provides your own changelog jinja template by using the `template` settings or the `--template` parameter.
-See [the template customization section](customization.md#customizing-the-changelog-template)
+See [the template customization section](../customization.md#customizing-the-changelog-template)
 
 ### `extras`
 
@@ -205,17 +179,17 @@ Provides your own changelog extra variables by using the `extras` settings or th
 cz changelog --extra key=value -e short="quoted value"
 ```
 
-See [the template customization section](customization.md#customizing-the-changelog-template)
+See [the template customization section](../customization.md#customizing-the-changelog-template)
 
 ## Hooks
 
 Supported hook methods:
 
-- per parsed message: useful to add links
-- end of changelog generation: useful to send slack or chat message, or notify another department
+- Per parsed message: Useful to add links
+- End of changelog generation: Useful to send Slack or chat messages, or notify another department
 
 Read more about hooks in the [customization page][customization]
 
 [keepachangelog]: https://keepachangelog.com/
 [semver]: https://semver.org/
-[customization]: ./customization.md
+[customization]: ../customization.md
diff --git a/docs/check.md b/docs/commands/check.md
similarity index 52%
rename from docs/check.md
rename to docs/commands/check.md
index 1cd42c74a9..33e41e04f8 100644
--- a/docs/check.md
+++ b/docs/commands/check.md
@@ -2,13 +2,15 @@
 
 ## About
 
-This feature checks whether the commit message follows the given committing rules. And comment in git message will be ignored.
+This feature checks whether the commit message follows the given committing rules. Comments in git messages will be ignored.
 
-If you want to setup an automatic check before every git commit, please refer to
-[Automatically check message before commit](./tutorials/auto_check.md).
+If you want to set up an automatic check before every git commit, please refer to
+[Automatically check message before commit](../tutorials/auto_check.md).
 
 ## Usage
 
+![cz check --help](../images/cli_help/cz_check___help.svg)
+
 There are three mutually exclusive ways to use `cz check`:
 
 - with `--rev-range` to check a range of pre-existing commits
@@ -25,7 +27,7 @@ $ cz check --rev-range REV_RANGE
 
 For example, if you'd like to check all commits on a branch, you can use `--rev-range master..HEAD`. Or, if you'd like to check all commits starting from when you first implemented commit message linting, you can use `--rev-range <first_commit_sha>..HEAD`.
 
-For more info on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges).
+For more information on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges).
 
 ### Commit Message
 
@@ -45,7 +47,7 @@ In this option, MESSAGE is the commit message to be checked.
 $ echo MESSAGE | cz check
 ```
 
-In this option, MESSAGE is piped to cz check and would be checked.
+In this option, MESSAGE is piped to cz check and will be checked.
 
 ### Commit Message File
 
@@ -53,8 +55,8 @@ In this option, MESSAGE is piped to cz check and would be checked.
 $ cz check --commit-msg-file COMMIT_MSG_FILE
 ```
 
-In this option, COMMIT_MSG_FILE is the path of the temporal file that contains the commit message.
-This argument can be useful when cooperating with git hook, please check [Automatically check message before commit](./tutorials/auto_check.md) for more information about how to use this argument with git hook.
+In this option, COMMIT_MSG_FILE is the path of the temporary file that contains the commit message.
+This argument can be useful when cooperating with git hooks. Please check [Automatically check message before commit](../tutorials/auto_check.md) for more information about how to use this argument with git hooks.
 
 ### Allow Abort
 
@@ -67,9 +69,19 @@ permit them. Since `git commit` accepts an `--allow-empty-message` flag (primari
 
 ### Allowed Prefixes
 
-If the commit message starts by some specific prefixes, `cz check` returns `True` without checkign the regex.
-By default, the the following prefixes are allowed: `Merge`, `Revert`, `Pull Request`, `fixup!` and `squash!`.
+If the commit message starts with some specific prefixes, `cz check` returns `True` without checking the regex.
+By default, the following prefixes are allowed: `Merge`, `Revert`, `Pull request`, `fixup!` and `squash!`.
 
 ```bash
 cz check --message MESSAGE --allowed-prefixes 'Merge' 'Revert' 'Custom Prefix'
 ```
+
+### Commit message length limit
+
+The argument `-l` (or `--message-length-limit`) followed by a positive number can limit the length of commit messages.
+For example, `cz check --message MESSAGE -l 3` would fail the check, since `MESSAGE` is more than 3 characters long.
+By default, the limit is set to 0, which means no limit on the length.
+
+**Note that the limit applies only to the first line of the message.**
+Specifically, for `ConventionalCommitsCz` the length only counts from the type of change to the subject,
+while the body and the footer are not counted.
diff --git a/docs/commands/commit.md b/docs/commands/commit.md
new file mode 100644
index 0000000000..be9d193b97
--- /dev/null
+++ b/docs/commands/commit.md
@@ -0,0 +1,65 @@
+## Usage
+
+![cz commit --help](../images/cli_help/cz_commit___help.svg)
+
+## Overview
+
+![Using Commitizen cli](../images/demo.gif)
+
+The `commit` command provides an interactive way to create structured commits. Use either:
+
+- `cz commit`
+- `cz c` (shortcut)
+
+By default, Commitizen uses conventional commits, but you can customize the commit rules to match your project's needs. See the [customization guide](../customization.md) for details.
+
+## Basic Usage
+
+### Interactive Commit Creation
+
+Simply run `cz commit` in your terminal to start the interactive commit creation process. The command will guide you through creating a properly formatted commit message according to your configured rules.
+
+### Writing Messages to File
+
+You can save the generated commit message to a file using:
+```sh
+cz commit --write-message-to-file COMMIT_MSG_FILE
+```
+
+This can be combined with `--dry-run` to only write the message without creating a commit. This is particularly useful for [automatically preparing commit messages](../tutorials/auto_prepare_commit_message.md).
+
+## Advanced Features
+
+### Git Command Options
+
+You can pass any git commit options using the `--` syntax:
+```sh
+cz commit <commitizen-args> -- <git-cli-args>
+
+# Examples:
+cz c --dry-run -- -a -S  # Stage all changes and sign the commit
+cz c -a -- -n            # Stage all changes and skip the pre-commit and commit-msg hooks
+```
+
+!!! warning
+    The `--signoff` option (or `-s`) is now recommended being used with the new syntax: `cz commit -- -s`. The old syntax `cz commit --signoff` is deprecated and will be removed in v5.
+
+### Retry
+
+- Use `cz commit --retry` to reuse the last commit message after a failed commit attempt
+- Set `retry_after_failure: true` in your configuration to automatically retry
+- Use `cz commit --no-retry` to force a new commit message prompt
+
+### Message Length Control
+
+Control the length of your commit messages using the `-l` or `--message-length-limit` option:
+```sh
+cz commit -l 72  # Limits message length to 72 characters
+```
+
+!!! note
+    The length limit only applies to the first line of the commit message. For conventional commits, this means the limit applies from the type of change through the subject. The body and footer are not counted.
+
+## Technical Notes
+
+For platform compatibility, the `commit` command disables ANSI escaping in its output. This means pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417).
diff --git a/docs/commands/example.md b/docs/commands/example.md
new file mode 100644
index 0000000000..8243453916
--- /dev/null
+++ b/docs/commands/example.md
@@ -0,0 +1,5 @@
+Show commit example
+
+## Usage
+
+![cz example --help](../images/cli_help/cz_example___help.svg)
diff --git a/docs/commands/info.md b/docs/commands/info.md
new file mode 100644
index 0000000000..5f816ba88d
--- /dev/null
+++ b/docs/commands/info.md
@@ -0,0 +1,5 @@
+Show information about the cz
+
+## Usage
+
+![cz info --help](../images/cli_help/cz_info___help.svg)
diff --git a/docs/commands/init.md b/docs/commands/init.md
new file mode 100644
index 0000000000..4d92112d34
--- /dev/null
+++ b/docs/commands/init.md
@@ -0,0 +1,66 @@
+The `cz init` command helps you set up Commitizen in your project by creating a configuration file with your preferred settings.
+
+## Usage
+
+![cz init --help](../images/cli_help/cz_init___help.svg)
+
+## Command
+
+```sh
+cz init
+```
+
+## Interactive Configuration
+
+When you run `cz init`, Commitizen will guide you through an interactive setup process:
+
+![init](../images/init.gif)
+
+## Configuration File
+
+The initialization process will create a configuration file in your project root.
+
+Choose the configuration file format based on your project type:
+
+- Use `pyproject.toml` for Python projects
+- Use `.cz.toml`, `.cz.yaml`, `.cz.json`, etc. for other projects.
+
+## Configuration Options
+
+During the initialization process, you'll be prompted to configure the following settings:
+
+1. **Convention Rules**: Select the commit message convention to follow (e.g., conventional commits)
+2. **Version Provider**: Choose how to manage versioning in your project. Commitizen supports multiple version management systems:
+    - `commitizen`: Uses Commitizen's built-in version management system
+    - `npm`: Manages version in `package.json` for Node.js projects
+    - `cargo`: Manages version in `Cargo.toml` for Rust projects
+    - `composer`: Manages version in `composer.json` for PHP projects
+    - `pep621`: Uses `pyproject.toml` with PEP 621 standard
+    - `poetry`: Uses `pyproject.toml` with Poetry configuration
+    - `uv`: Uses `pyproject.toml` and `uv.lock` for Python projects
+    - `scm`: Reads version directly from git tags without modifying files
+3. **Project Version**: The current version of your project will be detected automatically
+4. **Tag Format**: The format used for version tags in your repository
+5. **Version Type**: Choose between:
+    - `semver` or `semver2`: Semantic Versioning (MAJOR.MINOR.PATCH)
+    - `pep440`: Python Package Versioning
+6. **Changelog Generation**: Configure whether to automatically generate changelog during version bumps
+7. **Alpha Versioning**: Option to keep major version at 0 for alpha/beta software
+8. **Pre-commit Hooks**: Set up Git pre-commit hooks for automated commit message validation
+
+## Example
+
+```sh
+# Start the initialization process
+cz init
+
+# Follow the interactive prompts to configure your project
+```
+
+## Next Steps
+
+After initialization, you can:
+
+1. Start using `cz commit` to create conventional commits
+2. Use `cz bump` to manage versioning
+3. Configure additional settings in your project's configuration file
diff --git a/docs/commands/ls.md b/docs/commands/ls.md
new file mode 100644
index 0000000000..f255ca5444
--- /dev/null
+++ b/docs/commands/ls.md
@@ -0,0 +1,3 @@
+## Usage
+
+![cz ls --help](../images/cli_help/cz_ls___help.svg)
diff --git a/docs/commands/schema.md b/docs/commands/schema.md
new file mode 100644
index 0000000000..bd6fa85195
--- /dev/null
+++ b/docs/commands/schema.md
@@ -0,0 +1,5 @@
+Show commit schema
+
+## Usage
+
+![cz schema --help](../images/cli_help/cz_schema___help.svg)
diff --git a/docs/commands/version.md b/docs/commands/version.md
new file mode 100644
index 0000000000..4d2e6a0323
--- /dev/null
+++ b/docs/commands/version.md
@@ -0,0 +1,5 @@
+Get the version of the installed Commitizen or the current project (default: installed commitizen)
+
+## Usage
+
+![cz version --help](../images/cli_help/cz_version___help.svg)
diff --git a/docs/commit.md b/docs/commit.md
deleted file mode 100644
index 2215e0d805..0000000000
--- a/docs/commit.md
+++ /dev/null
@@ -1,31 +0,0 @@
-![Using commitizen cli](images/demo.gif)
-
-## About
-
-In your terminal run `cz commit` or the shortcut `cz c` to generate a guided git commit.
-
-You can run `cz commit --write-message-to-file COMMIT_MSG_FILE` to additionally save the
-generated message to a file. This can be combined with the `--dry-run` flag to only
-write the message to a file and not modify files and create a commit. A possible use
-case for this is to [automatically prepare a commit message](./tutorials/auto_prepare_commit_message.md).
-
-
-!!! note
-    To maintain platform compatibility, the `commit` command disable ANSI escaping in its output.
-    In particular pre-commit hooks coloring will be deactivated as discussed in [commitizen-tools/commitizen#417](https://github.com/commitizen-tools/commitizen/issues/417).
-
-
-### git options
-
-`git` command options that are not implemented by commitizen can be use via the `--` syntax for the `commit` command.
-The syntax separates commitizen arguments from `git commit` arguments by a double dash. This is the resulting syntax:
-```sh
-cz commit <commitizen-args> -- <git-cli-args>
-
-# e.g., cz commit --dry-run -- -a -S
-```
-For example, using the `-S` option on `git commit` to sign a commit is now commitizen compatible: `cz c -- -S`
-
-!!! note
-    Deprecation warning: A commit can be signed off using `cz commit --signoff` or the shortcut `cz commit -s`.
-    This syntax is now deprecated in favor of the new `cz commit -- -s` syntax.
diff --git a/docs/config.md b/docs/config.md
index 391c20f0fc..05f45a1a80 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -4,229 +4,238 @@
 
 ### `name`
 
-Type: `str`
+- Type: `str`
+- Default: `"cz_conventional_commits"`
 
-Default: `"cz_conventional_commits"`
-
-Name of the committing rules to use
+Name of the committing rules to use.
 
 ### `version`
 
-Type: `str`
-
-Default: `None`
+- Type: `str`
+- Default: `None`
 
-Current version. Example: "0.1.2". Required if you use `version_provider = "commitizen"`.
+Current version. Example: `"0.1.2"`. Required if you use `version_provider = "commitizen"`.
 
 ### `version_files`
 
-Type: `list`
-
-Default: `[ ]`
+- Type: `list`
+- Default: `[]`
 
-Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files]
+Files (or glob patterns) where the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files]
 
 ### `version_provider`
 
-Type: `str`
+- Type: `str`
+- Default: `commitizen`
 
-Default: `commitizen`
-
-Version provider used to read and write version [Read more](#version-providers)
+Version provider used to read and write version. [Read more](#version-providers)
 
 ### `version_scheme`
 
-Type: `str`
+- Type: `str`
+- Default: `pep440`
 
-Default: `pep440`
+Select a version scheme from the following options:
 
-Select a version scheme from the following options [`pep440`, `semver`]. Useful for non-python projects. [Read more][version-scheme]
+- `pep440`
+- `semver`
+- `semver2`
 
-### `tag_format`
+Useful for non-python projects. [Read more][version-scheme]
 
-Type: `str`
+### `tag_format`
 
-Default: `$version`
+- Type: `str`
+- Default: `$version`
 
 Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format]
 
-### `update_changelog_on_bump`
+### `legacy_tag_formats`
 
-Type: `bool`
+- Type: `list`
+- Default: `[]`
 
-Default: `false`
+Legacy git tag formats, useful for old projects that changed tag format.
+Tags matching those formats will be recognized as version tags and be included in the changelog.
+Each entry uses the syntax as [`tag_format`](#tag_format). [Read more][tag_format]
 
-Create changelog when running `cz bump`
+### `ignored_tag_formats`
 
-### `gpg_sign`
+- Type: `list`
+- Default: `[]`
+
+Tags matching those formats will be totally ignored and won't raise a warning.
+Each entry uses the syntax as [`tag_format`](#tag_format) with the addition of `*`
+that will match everything (non-greedy). [Read more][tag_format]
 
-Type: `bool`
+### `update_changelog_on_bump`
+
+- Type: `bool`
+- Default: `False`
+
+Create changelog when running `cz bump`.
+
+### `gpg_sign`
 
-Default: `false`
+- Type: `bool`
+- Default: `False`
 
 Use gpg signed tags instead of lightweight tags.
 
 ### `annotated_tag`
 
-Type: `bool`
-
-Default: `false`
+- Type: `bool`
+- Default: `False`
 
 Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight]
 
 ### `bump_message`
 
-Type: `str`
+- Type: `str`
+- Default: `None`
 
-Default: `None`
+Create custom commit message. Useful to skip CI. [Read more][bump_message]
 
-Create custom commit message, useful to skip ci. [Read more][bump_message]
+### `retry_after_failure`
 
-### `allow_abort`
+- Type: `bool`
+- Default: `False`
 
-Type: `bool`
+Automatically retry failed commit when running `cz commit`. [Read more][retry_after_failure]
+
+### `allow_abort`
 
-Default: `false`
+- Type: `bool`
+- Default: `False`
 
-Disallow empty commit messages, useful in ci. [Read more][allow_abort]
+Disallow empty commit messages. Useful in CI. [Read more][allow_abort]
 
 ### `allowed_prefixes`
 
-Type: `list`
-Default: `[ "Merge", "Revert", "Pull request", "fixup!", "squash!"]`
-Allow some prefixes and do not try to match the regex when checking the message [Read more][allowed_prefixes]
+- Type: `list`
+- Default: `["Merge", "Revert", "Pull request", "fixup!", "squash!"]`
 
-### `changelog_file`
+List of prefixes that commitizen ignores when verifying messages. [Read more][allowed_prefixes]
 
-Type: `str`
+### `changelog_file`
 
-Default: `CHANGELOG.md`
+- Type: `str`
+- Default: `CHANGELOG.md`
 
 Filename of exported changelog
 
 ### `changelog_format`
 
-Type: `str`
-
-Default: None
+- Type: `str`
+- Default: `None`
 
-Format used to parse and generate the changelog, If not specified, guessed from [`changelog_file`](#changelog_file).
+Format used to parse and generate the changelog. If not specified, guessed from [`changelog_file`](#changelog_file).
 
 ### `changelog_incremental`
 
-Type: `bool`
+- Type: `bool`
+- Default: `False`
 
-Default: `false`
+Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file.
 
-Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true`
+!!! note
+    When doing `cz bump --changelog` this is automatically set to `True`
 
 ### `changelog_start_rev`
 
-Type: `str`
-
-Default: `None`
+- Type: `str`
+- Default: `None`
 
 Start from a given git rev to generate the changelog
 
 ### `changelog_merge_prerelease`
 
-Type: `bool`
-
-Default: `false`
+- Type: `bool`
+- Default: `False`
 
 Collect all changes of prerelease versions into the next non-prerelease version when creating the changelog.
 
 ### `style`
 
-Type: `list`
-
-see above
+- Type: `list`
+- Default: `[]`
 
 Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features]
 
 ### `customize`
 
-Type: `dict`
-
-Default: `None`
+- Type: `dict`
+- Default: `None`
 
 **This is only supported when config through `toml`.** Custom rules for committing and bumping. [Read more][customization]
 
 ### `use_shortcuts`
 
-Type: `bool`
-
-Default: `false`
+- Type: `bool`
+- Default: `False`
 
-If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts]
+If enabled, Commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts]
 
 ### `major_version_zero`
 
-Type: `bool`
+- Type: `bool`
+- Default: `False`
 
-Default: `false`
-
-When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero]
+If enabled, breaking changes on a `0.x` will remain as a `0.x` version. Otherwise, a breaking change will bump a `0.x` version to `1.0`. [Read more][major-version-zero]
 
 ### `prerelease_offset`
 
-Type: `int`
-
-Default: `0`
+- Type: `int`
+- Default: `0`
 
-In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset]
+In some circumstances, a prerelease cannot start with `0`-for example, in embedded projects where individual characters are encoded as bytes. You can specify an offset from which to start counting. [Read more][prerelease-offset]
 
 ### `pre_bump_hooks`
 
-Type: `list[str]`
-
-Default: `[]`
+- Type: `list[str]`
+- Default: `[]`
 
 Calls the hook scripts **before** bumping version. [Read more][pre_bump_hooks]
 
 ### `post_bump_hooks`
 
-Type: `list[str]`
-
-Default: `[]`
+- Type: `list[str]`
+- Default: `[]`
 
 Calls the hook scripts **after** bumping the version. [Read more][post_bump_hooks]
 
 ### `encoding`
 
-Type: `str`
-
-Default: `utf-8`
+- Type: `str`
+- Default: `"utf-8"`
 
 Sets the character encoding to be used when parsing commit messages. [Read more][encoding]
 
 ### `template`
 
-Type: `str`
-
-Default: `None` (provided by plugin)
+- Type: `str`
+- Default: `None` (provided by plugin)
 
 Provide custom changelog jinja template path relative to the current working directory. [Read more][template-customization]
 
 ### `extras`
 
-Type: `dict[str, Any]`
-
-Default: `{}`
+- Type: `dict[str, Any]`
+- Default: `{}`
 
 Provide extra variables to the changelog template. [Read more][template-customization]
 
 ## Configuration file
 
-### pyproject.toml or .cz.toml
+### `pyproject.toml`, `.cz.toml` or `cz.toml`
 
 Default and recommended configuration format for a project.
 For a **python** project, we recommend adding an entry to your `pyproject.toml`.
-You can also create a `.cz.toml` file at the root of your project folder.
+You can also create a `.cz.toml` or `cz.toml` file at the root of your project folder.
 
 Example configuration:
 
-```toml
+```toml title=".cz.toml"
 [tool.commitizen]
 name = "cz_conventional_commits"
 version = "0.1.0"
@@ -249,11 +258,11 @@ style = [
 ]
 ```
 
-### .cz.json or cz.json
+### `.cz.json` or `cz.json`
 
 Commitizen has support for JSON configuration. Recommended for `NodeJS` projects.
 
-```json
+```json title=".cz.json"
 {
   "commitizen": {
     "name": "cz_conventional_commits",
@@ -275,11 +284,11 @@ Commitizen has support for JSON configuration. Recommended for `NodeJS` projects
 }
 ```
 
-### .cz.yaml or cz.yaml
+### `.cz.yaml` or `cz.yaml`
 
 YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects.
 
-```yaml
+```yaml title=".cz.yaml"
 commitizen:
   name: cz_conventional_commits
   version: 0.1.0
@@ -312,25 +321,26 @@ commitizen:
 ## Version providers
 
 Commitizen can read and write version from different sources.
-By default, it use the `commitizen` one which is using the `version` field from the commitizen settings.
+By default, it uses the `commitizen` one which is using the `version` field from the Commitizen settings.
 But you can use any `commitizen.provider` entrypoint as value for `version_provider`.
 
 Commitizen provides some version providers for some well known formats:
 
 | name         | description                                                                                                                                                                                                             |
 | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `commitizen` | Default version provider: Fetch and set version in commitizen config.                                                                                                                                                   |
+| `commitizen` | Default version provider: Fetch and set version in Commitizen config.                                                                                                                                                   |
 | `scm`        | Fetch the version from git and does not need to set it back                                                                                                                                                             |
 | `pep621`     | Get and set version from `pyproject.toml` `project.version` field                                                                                                                                                       |
 | `poetry`     | Get and set version from `pyproject.toml` `tool.poetry.version` field                                                                                                                                                   |
-| `cargo`      | Get and set version from `Cargo.toml` `project.version` field                                                                                                                                                           |
+| `uv`         | Get and set version from `pyproject.toml` `project.version` field and `uv.lock` `package.version` field whose `package.name` field is the same as `pyproject.toml` `project.name` field                                 |
+| `cargo`      | Get and set version from `Cargo.toml` `package.version` field and `Cargo.lock` `package.version` field whose `package.name` field is the same as `Cargo.toml` `package.name` field                                      |
 | `npm`        | Get and set version from `package.json` `version` field, `package-lock.json` `version,packages.''.version` fields if the file exists, and `npm-shrinkwrap.json` `version,packages.''.version` fields if the file exists |
 | `composer`   | Get and set version from `composer.json` `project.version` field                                                                                                                                                        |
 
 !!! note
-The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin.
+    The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin.
 
-An example in your `.cz.toml` would look like this:
+An example in your `.cz.toml` or `cz.toml` would look like this:
 
 ```toml
 [tool.commitizen]
@@ -339,9 +349,9 @@ version_provider = "pep621"
 
 ### Custom version provider
 
-You can add you own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint.
+You can add your own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint.
 
-Here a quick example of a `my-provider` provider reading and writing version in a `VERSION` file.
+Here is a quick example of a provider `my_provider.py`, reading and writing version in a `VERSION` file.
 
 ```python title="my_provider.py"
 from pathlib import Path
@@ -356,35 +366,35 @@ class MyProvider(VersionProvider):
 
     def set_version(self, version: str):
         self.file.write_text(version)
-
 ```
 
 ```python title="setup.py"
 from setuptools import setup
 
 setup(
-    name='my-commitizen-provider',
-    version='0.1.0',
-    py_modules=['my_provider'],
-    install_requires=['commitizen'],
-    entry_points = {
-        'commitizen.provider': [
-            'my-provider = my_provider:MyProvider',
+    name="my-commitizen-provider",
+    version="0.1.0",
+    py_modules=["my_provider"],
+    install_requires=["commitizen"],
+    entry_points={
+        "commitizen.provider": [
+            "my-provider = my_provider:MyProvider",
         ]
-    }
+    },
 )
 ```
 
-[version_files]: bump.md#version_files
-[tag_format]: bump.md#tag_format
-[bump_message]: bump.md#bump_message
-[major-version-zero]: bump.md#-major-version-zero
-[prerelease-offset]: bump.md#-prerelease_offset
-[allow_abort]: check.md#allow-abort
-[version-scheme]: bump.md#version-scheme
-[pre_bump_hooks]: bump.md#pre_bump_hooks
-[post_bump_hooks]: bump.md#post_bump_hooks
-[allowed_prefixes]: check.md#allowed-prefixes
+[version_files]: commands/bump.md#version_files
+[tag_format]: commands/bump.md#tag_format
+[bump_message]: commands/bump.md#bump_message
+[major-version-zero]: commands/bump.md#-major-version-zero
+[prerelease-offset]: commands/bump.md#prerelease_offset
+[retry_after_failure]: commands/commit.md#retry
+[allow_abort]: commands/check.md#allow-abort
+[version-scheme]: commands/bump.md#-version-scheme
+[pre_bump_hooks]: commands/bump.md#pre_bump_hooks
+[post_bump_hooks]: commands/bump.md#post_bump_hooks
+[allowed_prefixes]: commands/check.md#allowed-prefixes
 [additional-features]: https://github.com/tmbo/questionary#additional-features
 [customization]: customization.md
 [shortcuts]: customization.md#shortcut-keys
diff --git a/docs/contributing.md b/docs/contributing.md
index 77ee1f44fe..8389e9370d 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -1,32 +1,82 @@
-## Contributing to commitizen
-
-First of all, thank you for taking the time to contribute! 🎉
-
-When contributing to [commitizen](https://github.com/commitizen-tools/commitizen), please first create an [issue](https://github.com/commitizen-tools/commitizen/issues) to discuss the change you wish to make before making a change.
-
-If you're a first-time contributor, you can check the issues with [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag.
-
-## Install before contributing
-
-1. Install [poetry](https://python-poetry.org/) `1.2.0+`, installation [pages](https://python-poetry.org/docs/#installing-with-the-official-installer)
-2. Install [gpg](https://gnupg.org), installation [pages](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation). For Mac users, you could try [homebrew](https://brew.sh/).
-
-## Before making a pull request
-
-1. Fork [the repository](https://github.com/commitizen-tools/commitizen).
-2. Clone the repository from your GitHub.
-3. Setup development environment through [poetry](https://python-poetry.org/) (`poetry install`).
-4. Setup [pre-commit](https://pre-commit.com/) hook (`poetry run pre-commit install`)
-5. Check out a new branch and add your modification.
-6. Add test cases for all your changes.
-   (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.)
-7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commits](https://www.conventionalcommits.org/).
-8. Run `./scripts/format` and `./scripts/test` to ensure you follow the coding style and the tests pass.
-9. Optionally, update the `./docs/README.md`.
-9. **Do not** update the `CHANGELOG.md`, it will be automatically created after merging to `master`.
-10. **Do not** update the versions in the project, they will be automatically updated.
-10. If your changes are about documentation. Run `poetry run mkdocs serve` to serve documentation locally and check whether there is any warning or error.
-11. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏
+## Contributing to Commitizen
+
+First, thank you for taking the time to contribute! 🎉 Your contributions help make Commitizen better for everyone.
+
+When contributing to Commitizen, we encourage you to:
+
+1. First, check out the [issues](https://github.com/commitizen-tools/commitizen/issues) and [Features we won't add](faq.md#features-we-wont-add) to see if there's already a discussion about the change you wish to make.
+2. If there's no discussion, [create an issue](https://github.com/commitizen-tools/commitizen/issues/new) to discuss your proposed changes.
+3. Follow our [development workflow](#development-workflow) and guidelines below.
+
+If you're a first-time contributor, please check out issues labeled [good first issue](https://github.com/commitizen-tools/commitizen/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - these are specifically chosen to be beginner-friendly.
+
+## Prerequisites & Setup
+
+### Required Tools
+
+1. **Python Environment**
+    - Python `>=3.9`
+    - [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0`
+2. **Version Control & Security**
+    - Git
+    - Commitizen
+    - [GPG](https://gnupg.org) for commit signing
+        - [Installation page](https://gnupg.org/documentation/manuals/gnupg/Installation.html#Installation)
+        - For Mac users: `brew install gnupg`
+        - For Windows users: Download from [Gpg4win](https://www.gpg4win.org/)
+        - For Linux users: Use your distribution's package manager (e.g., `apt install gnupg` for Ubuntu)
+
+### Getting Started
+
+1. Fork [Commitizen](https://github.com/commitizen-tools/commitizen)
+2. Clone your fork:
+    ```bash
+    git clone https://github.com/YOUR_USERNAME/commitizen.git
+    cd commitizen
+    ```
+3. Add the upstream repository:
+    ```bash
+    git remote add upstream https://github.com/commitizen-tools/commitizen.git
+    ```
+4. Set up the development environment:
+    ```bash
+    poetry install
+    ```
+5. Set up pre-commit hooks:
+    ```bash
+    poetry setup-pre-commit
+    ```
+
+## Development Workflow
+
+1. **Create a New Branch**
+    ```bash
+    git switch -c feature/your-feature-name
+    # or
+    git switch -c fix/your-bug-fix
+    ```
+2. **Make Your Changes**
+    - Write your code
+    - Add tests for new functionalities or fixes
+    - Update documentation if needed
+    - Follow the existing code style
+3. **Testing**
+    - Run the full test suite: `poetry all`
+    - Ensure test coverage doesn't drop (we use [CodeCov](https://app.codecov.io/gh/commitizen-tools/commitizen))
+    - For documentation changes, run `poetry doc` to check for warnings/errors
+4. **Committing Changes**
+    - Use Commitizen to make commits (we follow [conventional commits](https://www.conventionalcommits.org/))
+    - Example: `cz commit`
+5. **Documentation**
+    - Update `docs/README.md` if needed
+    - For CLI help screenshots: `poetry doc:screenshots`
+    - **DO NOT** update `CHANGELOG.md` (automatically generated)
+    - **DO NOT** update version numbers (automatically handled)
+6. **Pull Request**
+    - Push your changes: `git push origin your-branch-name`
+    - Create a pull request on GitHub
+    - Ensure CI checks pass
+    - Wait for review and address any feedback
 
 ## Use of GitHub Labels
 
@@ -45,8 +95,8 @@ If you're a first-time contributor, you can check the issues with [good first is
 * pr-status: wait-for-modification
 * pr-status: wait-for-response
 * pr-status: ready-to-merge
-* needs: test-case *(pr only)*
-* needs: documentation *(pr only)*
+* needs: test-case *(PR only)*
+* needs: documentation *(PR only)*
 * type: feature
 * type: bug
 * type: documentation
@@ -57,7 +107,7 @@ If you're a first-time contributor, you can check the issues with [good first is
 * os: macOS
 
 
-### Issue life cycle
+## Issue life cycle
 
 ```mermaid
 graph TD
@@ -75,7 +125,7 @@ graph TD
     close --> output[/close/]
 ```
 
-### Pull request life cycle
+## Pull request life cycle
 
 ```mermaid
 flowchart TD
@@ -103,6 +153,3 @@ flowchart TD
     --modification-received-->
     review
 ```
-
-
-[conventional-commmits]: https://www.conventionalcommits.org/
diff --git a/docs/contributing_tldr.md b/docs/contributing_tldr.md
new file mode 100644
index 0000000000..f5483cddcd
--- /dev/null
+++ b/docs/contributing_tldr.md
@@ -0,0 +1,37 @@
+Feel free to send a PR to update this file if you find anything useful. 🙇
+
+## Environment
+
+- Python `>=3.9`
+- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) `>=2.0.0`
+
+## Useful commands
+
+Please check the [pyproject.toml](https://github.com/commitizen-tools/commitizen/blob/master/pyproject.toml) for a comprehensive list of commands.
+
+### Code Changes
+
+```bash
+# Ensure you have the correct dependencies
+poetry install
+
+# Make ruff happy
+poetry format
+
+# Check if ruff and mypy are happy
+poetry lint
+
+# Check if mypy is happy in python 3.9
+mypy --python-version 3.9
+
+# Run tests in parallel.
+pytest -n auto # This may take a while.
+pytest -n auto <test_suite>
+```
+
+### Documentation Changes
+
+```bash
+# Build the documentation locally and check for broken links
+poetry doc
+```
diff --git a/docs/customization.md b/docs/customization.md
index 0c86f60d20..df77171077 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -1,4 +1,4 @@
-Customizing commitizen is not hard at all.
+Customizing Commitizen is not hard at all.
 We have two different ways to do so.
 
 ## 1. Customize in configuration file
@@ -6,11 +6,11 @@ We have two different ways to do so.
 The basic steps are:
 
 1. Define your custom committing or bumping rules in the configuration file.
-2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running commitizen.
+2. Declare `name = "cz_customize"` in your configuration file, or add `-n cz_customize` when running Commitizen.
 
 Example:
 
-```toml
+```toml title="pyproject.toml"
 [tool.commitizen]
 name = "cz_customize"
 
@@ -50,7 +50,7 @@ message = "Do you want to add body message in commit?"
 
 The equivalent example for a json config file:
 
-```json
+```json title=".cz.json"
 {
     "commitizen": {
         "name": "cz_customize",
@@ -104,9 +104,9 @@ The equivalent example for a json config file:
 }
 ```
 
-And the correspondent example for a yaml json file:
+And the correspondent example for a yaml file:
 
-```yaml
+```yaml title=".cz.yaml"
 commitizen:
   name: cz_customize
   customize:
@@ -115,8 +115,8 @@ commitizen:
     schema: "<type>: <body>"
     schema_pattern: "(feature|bug fix):(\\s.*)"
     bump_pattern: "^(break|new|fix|hotfix)"
-    commit_parser: "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?",
-    changelog_pattern: "^(feature|bug fix)?(!)?",
+    commit_parser: "^(?P<change_type>feature|bug fix):\\s(?P<message>.*)?"
+    changelog_pattern: "^(feature|bug fix)?(!)?"
     change_type_map:
       feature: Feat
       bug fix: Fix
@@ -139,61 +139,65 @@ commitizen:
       message: Select the type of change you are committing
     - type: input
       name: message
-      message: Body.
+      message: 'Body.'
     - type: confirm
       name: show_message
-      message: Do you want to add body message in commit?
+      message: 'Do you want to add body message in commit?'
 ```
 
 ### Customize configuration
 
 | Parameter           | Type   | Default | Description                                                                                                                                                                                                                      |
 | ------------------- | ------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `questions`         | `Questions` | `None`  | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries. |
+| `questions`         | `Questions` | `None`  | Questions regarding the commit message. Detailed below. The type `Questions` is an alias to `Iterable[MutableMapping[str, Any]]` which is defined in `commitizen.defaults`. It expects a list of dictionaries.              |
 | `message_template`  | `str`  | `None`  | The template for generating message from the given answers. `message_template` should either follow [Jinja2][jinja2] formatting specification, and all the variables in this template should be defined in `name` in `questions` |
-| `example`           | `str`  | `None`  | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`.                                                                                                                                                |
-| `schema`            | `str`  | `None`  | (OPTIONAL) Show the schema used. Used by `cz schema`.                                                                                                                                                                            |
-| `schema_pattern`    | `str`  | `None`  | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`.                                                                                                                                      |
-| `info_path`         | `str`  | `None`  | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead.                                                                           |
-| `info`              | `str`  | `None`  | (OPTIONAL) Explanation of the commit rules. Used by `cz info`.                                                                                                                                                                   |
+| `example`           | `str`  | `""`    | (OPTIONAL) Provide an example to help understand the style. Used by `cz example`.                                                                                                                                                |
+| `schema`            | `str`  | `""`    | (OPTIONAL) Show the schema used. Used by `cz schema`.                                                                                                                                                                            |
+| `schema_pattern`    | `str`  | `""`    | (OPTIONAL) The regular expression used to do commit message validation. Used by `cz check`.                                                                                                                                      |
+| `info_path`         | `str`  | `""`    | (OPTIONAL) The path to the file that contains explanation of the commit rules. Used by `cz info`. If not provided `cz info`, will load `info` instead.                                                                           |
+| `info`              | `str`  | `""`    | (OPTIONAL) Explanation of the commit rules. Used by `cz info`.                                                                                                                                                                   |
 | `bump_map`          | `dict` | `None`  | (OPTIONAL) Dictionary mapping the extracted information to a `SemVer` increment type (`MAJOR`, `MINOR`, `PATCH`)                                                                                                                 |
 | `bump_pattern`      | `str`  | `None`  | (OPTIONAL) Regex to extract information from commit (subject and body)                                                                                                                                                           |
-| `change_type_order`| `str`  | `None` | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]`                                                                                             |
+| `change_type_order` | `str`  | `None`  | (OPTIONAL) List of strings used to order the Changelog. All other types will be sorted alphabetically. Default is `["BREAKING CHANGE", "Feat", "Fix", "Refactor", "Perf"]`                                                       |
 | `commit_parser`     | `str`  | `None`  | (OPTIONAL) Regex to extract information used in creating changelog. [See more][changelog-spec]                                                                                                                                   |
 | `changelog_pattern` | `str`  | `None`  | (OPTIONAL) Regex to understand which commits to include in the changelog                                                                                                                                                         |
 | `change_type_map`   | `dict` | `None`  | (OPTIONAL) Dictionary mapping the type of the commit to a changelog entry                                                                                                                                                        |
 
 [jinja2]: https://jinja.palletsprojects.com/en/2.10.x/
-[changelog-spec]: https://commitizen-tools.github.io/commitizen/changelog/
+[changelog-spec]: https://commitizen-tools.github.io/commitizen/commands/changelog/
 
 #### Detailed `questions` content
 
-| Parameter | Type   | Default | Description                                                                                                                                                                                     |
-| --------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `type`    | `str`  | `None`  | The type of questions. Valid type: `list`, `input` and etc. [See More][different-question-types]                                                                                                |
-| `name`    | `str`  | `None`  | The key for the value answered by user. It's used in `message_template`                                                                                                                         |
-| `message` | `str`  | `None`  | Detail description for the question.                                                                                                                                                            |
-| `choices` | `list` | `None`  | (OPTIONAL) The choices when `type = list`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. |
-| `default` | `Any`  | `None`  | (OPTIONAL) The default value for this question.                                                                                                                                                 |
-| `filter`  | `str`  | `None`  | (Optional) Validator for user's answer. **(Work in Progress)**                                                                                                                                  |
+| Parameter   | Type   | Default | Description                                                                                                                                                                                     |
+| ----------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `type`      | `str`  | `None`  | The type of questions. Valid types: `list`, `select`, `input`, etc. The `select` type provides an interactive searchable list interface. [See More][different-question-types]                  |
+| `name`      | `str`  | `None`  | The key for the value answered by user. It's used in `message_template`                                                                                                                         |
+| `message`   | `str`  | `None`  | Detail description for the question.                                                                                                                                                            |
+| `choices`   | `list` | `None`  | (OPTIONAL) The choices when `type = list` or `type = select`. Either use a list of values or a list of dictionaries with `name` and `value` keys. Keyboard shortcuts can be defined via `key`. See examples above. |
+| `default`   | `Any`  | `None`  | (OPTIONAL) The default value for this question.                                                                                                                                                 |
+| `filter`    | `str`  | `None`  | (OPTIONAL) Validator for user's answer. **(Work in Progress)**                                                                                                                                  |
+| `multiline` | `bool` | `False` | (OPTIONAL) Enable multiline support when `type = input`.                                                                                                                                        |
+| `use_search_filter` | `bool` | `False` | (OPTIONAL) Enable search/filter functionality for list/select type questions. This allows users to type and filter through the choices.                                                  |
+| `use_jk_keys` | `bool` | `True` | (OPTIONAL) Enable/disable j/k keys for navigation in list/select type questions. Set to false if you prefer arrow keys only.                                                                    |
+
 [different-question-types]: https://github.com/tmbo/questionary#different-question-types
 
 #### Shortcut keys
 
-When the [`use_shortcuts`](config.md#settings) config option is enabled, commitizen can show and use keyboard shortcuts to select items from lists directly.
-For example, when using the `cz_conventional_commits` commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically.
+When the [`use_shortcuts`](config.md#settings) config option is enabled, Commitizen can show and use keyboard shortcuts to select items from lists directly.
+For example, when using the `cz_conventional_commits` Commitizen template, shortcut keys are shown when selecting the commit type. Unless otherwise defined, keyboard shortcuts will be numbered automatically.
 To specify keyboard shortcuts for your custom choices, provide the shortcut using the `key` parameter in dictionary form for each choice you would like to customize.
 
 ## 2. Customize through customizing a class
 
 The basic steps are:
 
-1. Inheriting from `BaseCommitizen`
+1. Inheriting from `BaseCommitizen`.
 2. Give a name to your rules.
-3. Create a python package using `setup.py`, `poetry`, etc
-4. Expose the class as a `commitizen.plugin` entrypoint
+3. Create a python package using `setup.py`, `poetry`, etc.
+4. Expose the class as a `commitizen.plugin` entrypoint.
 
-Check an [example](convcomms) on how to configure `BaseCommitizen`.
+Check an [example][convcomms] on how to configure `BaseCommitizen`.
 
 You can also automate the steps above through [cookiecutter](https://cookiecutter.readthedocs.io/en/1.7.0/).
 
@@ -211,7 +215,7 @@ Create a Python module, for example `cz_jira.py`.
 
 Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional.
 
-```python
+```python title="cz_jira.py"
 from commitizen.cz.base import BaseCommitizen
 from commitizen.defaults import Questions
 
@@ -255,7 +259,7 @@ class JiraCz(BaseCommitizen):
 
 The next file required is `setup.py` modified from flask version.
 
-```python
+```python title="setup.py"
 from setuptools import setup
 
 setup(
@@ -291,7 +295,7 @@ You need to define 2 parameters inside your custom `BaseCommitizen`.
 
 Let's see an example.
 
-```python
+```python title="cz_strange.py"
 from commitizen.cz.base import BaseCommitizen
 
 
@@ -300,7 +304,7 @@ class StrangeCommitizen(BaseCommitizen):
     bump_map = {"break": "MAJOR", "new": "MINOR", "fix": "PATCH", "hotfix": "PATCH"}
 ```
 
-That's it, your commitizen now supports custom rules, and you can run.
+That's it, your Commitizen now supports custom rules, and you can run.
 
 ```bash
 cz -n cz_strange bump
@@ -311,17 +315,18 @@ cz -n cz_strange bump
 ### Custom changelog generator
 
 The changelog generator should just work in a very basic manner without touching anything.
-You can customize it of course, and this are the variables you need to add to your custom `BaseCommitizen`.
+You can customize it of course, and the following variables are the ones you need to add to your custom `BaseCommitizen`.
 
 | Parameter                        | Type                                                                     | Required | Description                                                                                                                                                                                                         |
 | -------------------------------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | `commit_parser`                  | `str`                                                                    | NO       | Regex which should provide the variables explained in the [changelog description][changelog-des]                                                                                                                    |
 | `changelog_pattern`              | `str`                                                                    | NO       | Regex to validate the commits, this is useful to skip commits that don't meet your ruling standards like a Merge. Usually the same as bump_pattern                                                                  |
 | `change_type_map`                | `dict`                                                                   | NO       | Convert the title of the change type that will appear in the changelog, if a value is not found, the original will be provided                                                                                      |
-| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict`                                  | NO       | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email` |
+| `changelog_message_builder_hook` | `method: (dict, git.GitCommit) -> dict | list | None`                  | NO       | Customize with extra information your message output, like adding links, this function is executed per parsed commit. Each GitCommit contains the following attrs: `rev`, `title`, `body`, `author`, `author_email`. Returning a falsy value ignore the commit. |
 | `changelog_hook`                 | `method: (full_changelog: str, partial_changelog: Optional[str]) -> str` | NO       | Receives the whole and partial (if used incremental) changelog. Useful to send slack messages or notify a compliance department. Must return the full_changelog                                                     |
+| `changelog_release_hook` | `method: (release: dict, tag: git.GitTag) -> dict` | NO | Receives each generated changelog release and its associated tag. Useful to enrich releases before they are rendered. Must return the update release
 
-```python
+```python title="cz_strange.py"
 from commitizen.cz.base import BaseCommitizen
 import chat
 import compliance
@@ -339,7 +344,7 @@ class StrangeCommitizen(BaseCommitizen):
 
     def changelog_message_builder_hook(
         self, parsed_message: dict, commit: git.GitCommit
-    ) -> dict:
+    ) -> dict | list | None:
         rev = commit.rev
         m = parsed_message["message"]
         parsed_message[
@@ -347,6 +352,10 @@ class StrangeCommitizen(BaseCommitizen):
         ] = f"{m} {rev} [{commit.author}]({commit.author_email})"
         return parsed_message
 
+    def changelog_release_hook(self, release: dict, tag: git.GitTag) -> dict:
+        release["author"] = tag.author
+        return release
+
     def changelog_hook(
         self, full_changelog: str, partial_changelog: Optional[str]
     ) -> str:
@@ -367,8 +376,6 @@ class StrangeCommitizen(BaseCommitizen):
         return full_changelog
 ```
 
-[changelog-des]: ./changelog.md#description
-
 ### Raise Customize Exception
 
 If you want `commitizen` to catch your exception and print the message, you'll have to inherit `CzException`.
@@ -386,7 +393,7 @@ class NoSubjectProvidedException(CzException):
 Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`.
 Migration should be straight-forward for legacy plugins:
 
-- Remove the `discover_this` line from you plugin module
+- Remove the `discover_this` line from your plugin module
 - Expose the plugin class under as a `commitizen.plugin` entrypoint.
 
 The name of the plugin is now determined by the name of the entrypoint.
@@ -438,20 +445,20 @@ Commitizen gives you the possibility to provide your own changelog template, by:
 
 - providing one with your customization class
 - providing one from the current working directory and setting it:
-    - as [configuration][template-config]
-    - as `--template` parameter to both `bump` and `changelog` commands
+  - as [configuration][template-config]
+  - as `--template` parameter to both `bump` and `changelog` commands
 - either by providing a template with the same name as the default template
 
-By default, the template used is the `CHANGELOG.md.j2` file from the commitizen repository.
+By default, the template used is the `CHANGELOG.md.j2` file from the Commitizen repository.
 
 ### Providing a template with your customization class
 
-There is 3 parameters available to change the template rendering from your custom `BaseCommitizen`.
+There are 3 parameters available to change the template rendering from your custom `BaseCommitizen`.
 
 | Parameter         | Type   | Default | Description                                                                                           |
 | ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- |
-| `template`        | `str`  | `None`  | Provide your own template name (default to `CHANGELOG.md.j2`)                            |
-| `template_loader` | `str`  | `None`  | Override the default template loader (so you can provide template from you customization class)       |
+| `template`        | `str`  | `None`  | Provide your own template name (default to `CHANGELOG.md.j2`)                                         |
+| `template_loader` | `str`  | `None`  | Override the default template loader (so you can provide template from your customization class)      |
 | `template_extras` | `dict` | `None`  | Provide some extra template parameters                                                                |
 
 Let's see an example.
@@ -475,14 +482,14 @@ This snippet will:
 
 ### Providing a template from the current working directory
 
-Users can provides their own template from their current working directory (your project root) by:
+Users can provide their own template from their current working directory (your project root) by:
 
 - providing a template with the same name (`CHANGELOG.md.j2` unless overridden by your custom class)
 - setting your template path as `template` configuration
 - giving your template path as `--template` parameter to `bump` and `changelog` commands
 
-!!! Note
-    The path is relative to the current working directory, aka. your project root most of the time.
+!!! note
+    The path is relative to the current working directory, aka your project root most of the time.
 
 ### Template variables
 
@@ -500,15 +507,23 @@ Each `Change` has the following fields:
 | ---- | ---- | ----------- |
 | scope | `str | None` | An optional scope |
 | message | `str` | The commit message body |
+| sha1 | `str` | The commit `sha1` |
+| parents | `list[str]` | The parent commit(s) `sha1`(s) |
+| author | `str` | The commit author name |
+| author_email | `str` | The commit author email |
 
-!!! Note
+!!! note
     The field values depend on the customization class and/or the settings you provide
 
+The `parents` field can be used to identify merge commits and generate a changelog based on those. Another use case
+is listing commits that belong to the same pull request.
+
 When using another template (either provided by a plugin or by yourself), you can also pass extra template variables
 by:
 
 - defining them in your configuration with the [`extras` settings][extras-config]
-- providing them on the commandline with the `--extra/-e` parameter to `bump` and `changelog` commands
+- providing them on the command line with the `--extra/-e` parameter to `bump` and `changelog` commands
 
 [template-config]: config.md#template
 [extras-config]: config.md#extras
+[changelog-des]: ./commands/changelog.md#description
diff --git a/docs/exit_codes.md b/docs/exit_codes.md
index af9cb83627..fd92961d38 100644
--- a/docs/exit_codes.md
+++ b/docs/exit_codes.md
@@ -20,7 +20,7 @@ These exit codes can be found in `commitizen/exceptions.py::ExitCode`.
 | NoCommitBackupError         | 10        | Commit back up file cannot be found                                                                         |
 | NothingToCommitError        | 11        | Nothing in staging to be committed                                                                          |
 | CustomError                 | 12        | `CzException` raised                                                                                        |
-| NoCommandFoundError         | 13        | No command found when running commitizen cli (e.g., `cz --debug`)                                           |
+| NoCommandFoundError         | 13        | No command found when running Commitizen cli (e.g., `cz --debug`)                                           |
 | InvalidCommitMessageError   | 14        | The commit message does not pass `cz check`                                                                 |
 | MissingConfigError          | 15        | Configuration missed for `cz_customize`                                                                     |
 | NoRevisionError             | 16        | No revision found                                                                                           |
diff --git a/docs/external_links.md b/docs/external_links.md
index 388bcc8dea..a90bbd085b 100644
--- a/docs/external_links.md
+++ b/docs/external_links.md
@@ -1,10 +1,10 @@
-> If you have written over commitizen, make a PR and add the link here 💪
+> If you have written over Commitizen, make a PR and add the link here 💪
 
 ## Talks
 
 | Name                                                                      | Speaker         | Occasion                                             | Language | Extra                                                                                                                            |
 | ------------------------------------------------------------------------- | --------------- | ---------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
-| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee         | Taipey.py 2020 June Meetup, Remote Python Pizza 2020 | English  | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) |
+| commitizen-tools: What can we gain from crafting a git message convention | Wei Lee         | Taipei.py 2020 June Meetup, Remote Python Pizza 2020 | English  | [slides](https://speakerdeck.com/leew/commitizen-tools-what-can-we-gain-from-crafting-a-git-message-convention-at-taipey-dot-py) |
 | Automating release cycles                                                 | Santiago Fraire | PyAmsterdam June 24, 2020, Online                    | English  | [slides](https://woile.github.io/commitizen-presentation/)                                                                       |
 | [Automatizando Releases con Commitizen y Github Actions][automatizando]   | Santiago Fraire | PyConAr 2020, Remote                                 | Español  | [slides](https://woile.github.io/automating-releases-github-actions-presentation/#/)                                             |
 
diff --git a/docs/faq.md b/docs/faq.md
index 060de78c30..ee9d97068d 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -1,3 +1,11 @@
+## Features we won't add
+
+For a comprehensive list of features that have been considered but won't be implemented, please refer to our [issue tracker](https://github.com/commitizen-tools/commitizen/issues?q=is:issue%20state:closed%20label:%22issue-status:%20wont-fix%22%20OR%20label:%22issue-status:%20wont-implement%22).
+
+- Enable multiple locations of config file `.cz.*` [#955](https://github.com/commitizen-tools/commitizen/issues/955)
+- Create a flag to build the changelog from commits in multiple git repositories [#790](https://github.com/commitizen-tools/commitizen/issues/790)
+- Global Configuration [#597](https://github.com/commitizen-tools/commitizen/issues/597)
+
 ## Support for PEP621
 
 PEP621 establishes a `[project]` definition inside `pyproject.toml`
@@ -52,18 +60,18 @@ It is not affiliated.
 Both are used for similar purposes, parsing commits, generating changelog and version we presume.
 This one is written in python to make integration easier for python projects and the other serves the JS packages.
 
-They differ a bit in design, not sure if cz-js does any of this, but these are some of the stuff you can do with this repo (python's commitizen):
+They differ a bit in design, not sure if cz-js does any of this, but these are some things you can do with this repo (python's commitizen):
 
 - create custom rules, version bumps and changelog generation, by default we use the popular conventional commits (I think cz-js allows this).
 - single package, install one thing and it will work (cz-js is a monorepo, but you have to install different dependencies AFAIK)
 - pre-commit integration
-- works on any language project, as long as you create the `.cz.toml` file.
+- works on any language project, as long as you create the `.cz.toml` or `cz.toml` file.
 
 Where do they cross paths?
 
 If you are using conventional commits in your git history, then you could swap one with the other in theory.
 
-Regarding the name, [cz-js][cz-js] came first, they used the word commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen".
+Regarding the name, [cz-js][cz-js] came first, they used the word Commitizen first. When this project was created originally, the creator read "be a good commitizen", and thought it was just a cool word that made sense, and this would be a package that helps you be a good "commit citizen".
 
 [cz-js]: https://github.com/commitizen/cz-cli
 
@@ -74,23 +82,37 @@ git revert --no-commit <SHA>
 git commit -m "revert: foo bar"
 ```
 
+## Why don't we use Pydantic?
+
+While Pydantic is a powerful and popular library for data validation, we intentionally avoid using it in this project to keep our dependency tree minimal and maintainable.
+
+Including Pydantic would increase the chances of version conflicts for users - especially with major changes introduced in Pydantic v3. Because we pin dependencies tightly, adding Pydantic could unintentionally restrict what other tools or libraries users can install alongside `commitizen`.
+
+Moreover we don't rely on the full feature set of Pydantic. Simpler alternatives like Python's built-in `TypedDict` offer sufficient type safety for our use cases, without the runtime overhead or dependency burden.
+
+In short, avoiding Pydantic helps us:
+- Keep dependencies lightweight
+- Reduce compatibility issues for users
+- Maintain clarity about what contributors should and shouldn't use
+
+
 ## I got `Exception [WinError 995] The I/O operation ...` error
 
 This error was caused by a Python bug on Windows. It's been fixed by [this PR](https://github.com/python/cpython/pull/22017), and according to Python's changelog, [3.8.6rc1](https://docs.python.org/3.8/whatsnew/changelog.html#python-3-8-6-release-candidate-1) and [3.9.0rc2](https://docs.python.org/3.9/whatsnew/changelog.html#python-3-9-0-release-candidate-2) should be the accurate versions first contain this fix. In conclusion, upgrade your Python version might solve this issue.
 
 More discussion can be found in issue [#318](https://github.com/commitizen-tools/commitizen/issues/318).
 
-## Why does commitizen not support CalVer?
+## Why does Commitizen not support CalVer?
 
 `commitizen` could support CalVer alongside SemVer, but in practice implementing CalVer
-creates numerous edge cases that are difficult to maintain ([#385]) and more generally
+creates numerous edge cases that are difficult to maintain ([#385]) and more generally,
 mixing the two version schemes may not be a good idea. If CalVer or other custom
 versioning scheme is needed, `commitizen` could still be used to standardize commits
 and create changelogs, but a separate package should be used for version increments.
 
 Mixing CalVer and SemVer is generally not recommended because each versioning scheme
-serves a different purposes. Diverging from either specification can be confusing to
-users and cause errors with third party tools that don't expect the non-standard format.
+serves a different purpose. Diverging from either specification can be confusing to
+users and cause errors with third-party tools that don't expect the non-standard format.
 
 In the future, `commitizen` may support some implementation of CalVer, but at the time
 of writing, there are no plans to implement the feature ([#173]).
@@ -107,3 +129,36 @@ If you would like to learn more about both schemes, there are plenty of good res
 
 [#173]: https://github.com/commitizen-tools/commitizen/issues/173
 [#385]: https://github.com/commitizen-tools/commitizen/pull/385
+
+## How to change the tag format ?
+
+You can use the [`legacy_tag_formats`](config.md#legacy_tag_formats) to list old tag formats.
+New bumped tags will be in the new format but old ones will still work for:
+- changelog generation (full, incremental and version range)
+- bump new version computation (automatically guessed or increment given)
+
+
+So given if you change from `myproject-$version` to `${version}` and then `v${version}`,
+your Commitizen configuration will look like this:
+
+```toml
+tag_format = "v${version}"
+legacy_tag_formats = [
+    "${version}",
+    "myproject-$version",
+]
+```
+
+## How to avoid warnings for expected non-version tags
+
+You can explicitly ignore them with [`ignored_tag_formats`](config.md#ignored_tag_formats).
+
+```toml
+tag_format = "v${version}"
+ignored_tag_formats = [
+    "stable",
+    "component-*",
+    "env/*",
+    "v${major}.${minor}",
+]
+```
diff --git a/docs/getting_started.md b/docs/getting_started.md
deleted file mode 100644
index afc2586412..0000000000
--- a/docs/getting_started.md
+++ /dev/null
@@ -1,119 +0,0 @@
-## Initialize commitizen
-
-If it's your first time, you'll need to create a commitizen configuration file.
-
-The assistant utility will help you set up everything
-
-```sh
-cz init
-```
-
-Alternatively, create a file `.cz.toml` in your project's directory.
-
-```toml
-[tool.commitizen]
-version = "0.1.0"
-update_changelog_on_bump = true
-```
-
-## Usage
-
-### Bump version
-
-```sh
-cz bump
-```
-
-This command will bump your project's version, and it will create a tag.
-
-Because of the setting `update_changelog_on_bump`, bump will also create the **changelog**.
-You can also [update files](./bump.md#version_files).
-You can configure the [version type](./bump.md#version-type) and [version provider](./config.md#version-providers).
-
-There are many more options available, please read the docs for the [bump command](./bump.md).
-
-### Committing
-
-Run in your terminal
-
-```bash
-cz commit
-```
-
-or the shortcut
-
-```bash
-cz c
-```
-
-#### Sign off the commit
-
-Run in the terminal
-
-```bash
-cz commit --signoff
-```
-
-or the shortcut
-
-```bash
-cz commit -s
-```
-
-### Get project version
-
-Running `cz version` will return the version of commitizen, but if you want
-your project's version you can run:
-
-```sh
-cz version -p
-```
-
-This can be useful in many situations, where otherwise, you would require a way
-to parse the version of your project. Maybe it's simple if you use a `VERSION` file,
-but once you start working with many different projects, it becomes tricky.
-
-A common example is, when you need to send to slack, the changes for the version that you
-just created:
-
-```sh
-cz changelog --dry-run "$(cz version -p)"
-```
-
-### Integration with Pre-commit
-
-Commitizen can lint your commit message for you with `cz check`.
-
-You can integrate this in your [pre-commit](https://pre-commit.com/) config with:
-
-```yaml
----
-repos:
-  - repo: https://github.com/commitizen-tools/commitizen
-    rev: master
-    hooks:
-      - id: commitizen
-      - id: commitizen-branch
-        stages: [push]
-```
-
-After the configuration is added, you'll need to run:
-
-```sh
-pre-commit install --hook-type commit-msg --hook-type pre-push
-```
-
-If you aren't using both hooks, you needn't install both stages.
-
-| Hook              | Recommended Stage |
-| ----------------- | ----------------- |
-| commitizen        | commit-msg        |
-| commitizen-branch | pre-push          |
-
-Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with:
-
-```sh
-pre-commit autoupdate
-```
-
-Read more about the `check` command [here](check.md).
diff --git a/docs/images/bump.gif b/docs/images/bump.gif
index f9c9e0c8a3..0e97550ade 100644
Binary files a/docs/images/bump.gif and b/docs/images/bump.gif differ
diff --git a/docs/images/cli_help/cz___help.svg b/docs/images/cli_help/cz___help.svg
new file mode 100644
index 0000000000..d580cfe619
--- /dev/null
+++ b/docs/images/cli_help/cz___help.svg
@@ -0,0 +1,206 @@
+<svg class="rich-terminal" viewBox="0 0 994 952.8" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-1389600277-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-1389600277-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-1389600277-r1 { fill: #c5c8c6 }
+.terminal-1389600277-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-1389600277-r3 { fill: #d0b344 }
+.terminal-1389600277-r4 { fill: #1984e9;text-decoration: underline; }
+.terminal-1389600277-r5 { fill: #68a0b3;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-1389600277-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="901.8" />
+    </clipPath>
+    <clipPath id="terminal-1389600277-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-22">
+    <rect x="0" y="538.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-23">
+    <rect x="0" y="562.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-24">
+    <rect x="0" y="587.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-25">
+    <rect x="0" y="611.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-26">
+    <rect x="0" y="635.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-27">
+    <rect x="0" y="660.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-28">
+    <rect x="0" y="684.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-29">
+    <rect x="0" y="709.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-30">
+    <rect x="0" y="733.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-31">
+    <rect x="0" y="757.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-32">
+    <rect x="0" y="782.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-33">
+    <rect x="0" y="806.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-34">
+    <rect x="0" y="831.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1389600277-line-35">
+    <rect x="0" y="855.5" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="950.8" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1389600277-clip-terminal)">
+    
+    <g class="terminal-1389600277-matrix">
+    <text class="terminal-1389600277-r1" x="0" y="20" textLength="134.2" clip-path="url(#terminal-1389600277-line-0)">$&#160;cz&#160;--help</text><text class="terminal-1389600277-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1389600277-line-0)">
+</text><text class="terminal-1389600277-r1" x="0" y="44.4" textLength="122" clip-path="url(#terminal-1389600277-line-1)">usage:&#160;cz&#160;</text><text class="terminal-1389600277-r2" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="134.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1389600277-line-1)">-h</text><text class="terminal-1389600277-r2" x="158.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="195.2" y="44.4" textLength="183" clip-path="url(#terminal-1389600277-line-1)">--config&#160;CONFIG</text><text class="terminal-1389600277-r2" x="378.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="414.8" y="44.4" textLength="85.4" clip-path="url(#terminal-1389600277-line-1)">--debug</text><text class="terminal-1389600277-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="536.8" y="44.4" textLength="85.4" clip-path="url(#terminal-1389600277-line-1)">-n&#160;NAME</text><text class="terminal-1389600277-r2" x="622.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r2" x="646.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">[</text><text class="terminal-1389600277-r1" x="658.8" y="44.4" textLength="146.4" clip-path="url(#terminal-1389600277-line-1)">-nr&#160;NO_RAISE</text><text class="terminal-1389600277-r2" x="805.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">]</text><text class="terminal-1389600277-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-1)">
+</text><text class="terminal-1389600277-r2" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">{</text><text class="terminal-1389600277-r1" x="134.2" y="68.8" textLength="829.6" clip-path="url(#terminal-1389600277-line-2)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-1389600277-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">}</text><text class="terminal-1389600277-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-2)">
+</text><text class="terminal-1389600277-r3" x="0" y="93.2" textLength="36.6" clip-path="url(#terminal-1389600277-line-3)">...</text><text class="terminal-1389600277-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-3)">
+</text><text class="terminal-1389600277-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-4)">
+</text><text class="terminal-1389600277-r1" x="0" y="142" textLength="915" clip-path="url(#terminal-1389600277-line-5)">Commitizen&#160;is&#160;a&#160;powerful&#160;release&#160;management&#160;tool&#160;that&#160;helps&#160;teams&#160;maintain&#160;</text><text class="terminal-1389600277-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1389600277-line-5)">
+</text><text class="terminal-1389600277-r1" x="0" y="166.4" textLength="951.6" clip-path="url(#terminal-1389600277-line-6)">consistent&#160;and&#160;meaningful&#160;commit&#160;messages&#160;while&#160;automating&#160;version&#160;management.</text><text class="terminal-1389600277-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-6)">
+</text><text class="terminal-1389600277-r1" x="0" y="190.8" textLength="427" clip-path="url(#terminal-1389600277-line-7)">For&#160;more&#160;information,&#160;please&#160;visit&#160;</text><text class="terminal-1389600277-r4" x="427" y="190.8" textLength="549" clip-path="url(#terminal-1389600277-line-7)">https://commitizen-tools.github.io/commitizen</text><text class="terminal-1389600277-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-7)">
+</text><text class="terminal-1389600277-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-8)">
+</text><text class="terminal-1389600277-r1" x="0" y="239.6" textLength="97.6" clip-path="url(#terminal-1389600277-line-9)">options:</text><text class="terminal-1389600277-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-9)">
+</text><text class="terminal-1389600277-r1" x="0" y="264" textLength="671" clip-path="url(#terminal-1389600277-line-10)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1389600277-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1389600277-line-10)">
+</text><text class="terminal-1389600277-r1" x="0" y="288.4" textLength="658.8" clip-path="url(#terminal-1389600277-line-11)">&#160;&#160;--config&#160;CONFIG&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;path&#160;of&#160;configuration&#160;file</text><text class="terminal-1389600277-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-11)">
+</text><text class="terminal-1389600277-r1" x="0" y="312.8" textLength="463.6" clip-path="url(#terminal-1389600277-line-12)">&#160;&#160;--debug&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;use&#160;debug&#160;mode</text><text class="terminal-1389600277-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-12)">
+</text><text class="terminal-1389600277-r1" x="0" y="337.2" textLength="597.8" clip-path="url(#terminal-1389600277-line-13)">&#160;&#160;-n,&#160;--name&#160;NAME&#160;&#160;&#160;&#160;&#160;&#160;&#160;use&#160;the&#160;given&#160;commitizen&#160;</text><text class="terminal-1389600277-r2" x="597.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-13)">(</text><text class="terminal-1389600277-r1" x="610" y="337.2" textLength="97.6" clip-path="url(#terminal-1389600277-line-13)">default:</text><text class="terminal-1389600277-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-13)">
+</text><text class="terminal-1389600277-r1" x="0" y="361.6" textLength="573.4" clip-path="url(#terminal-1389600277-line-14)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;cz_conventional_commits</text><text class="terminal-1389600277-r2" x="573.4" y="361.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-14)">)</text><text class="terminal-1389600277-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-14)">
+</text><text class="terminal-1389600277-r1" x="0" y="386" textLength="317.2" clip-path="url(#terminal-1389600277-line-15)">&#160;&#160;-nr,&#160;--no-raise&#160;NO_RAISE</text><text class="terminal-1389600277-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1389600277-line-15)">
+</text><text class="terminal-1389600277-r1" x="0" y="410.4" textLength="915" clip-path="url(#terminal-1389600277-line-16)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;comma&#160;separated&#160;error&#160;codes&#160;that&#160;won&#x27;t&#160;raise&#160;error,</text><text class="terminal-1389600277-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-16)">
+</text><text class="terminal-1389600277-r1" x="0" y="434.8" textLength="439.2" clip-path="url(#terminal-1389600277-line-17)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;e.g:&#160;cz&#160;-nr&#160;</text><text class="terminal-1389600277-r5" x="439.2" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">1</text><text class="terminal-1389600277-r1" x="451.4" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">,</text><text class="terminal-1389600277-r5" x="463.6" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">2</text><text class="terminal-1389600277-r1" x="475.8" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">,</text><text class="terminal-1389600277-r5" x="488" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">3</text><text class="terminal-1389600277-r1" x="500.2" y="434.8" textLength="231.8" clip-path="url(#terminal-1389600277-line-17)">&#160;bump.&#160;See&#160;codes&#160;at</text><text class="terminal-1389600277-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-17)">
+</text><text class="terminal-1389600277-r4" x="292.8" y="459.2" textLength="231.8" clip-path="url(#terminal-1389600277-line-18)">https://commitizen-</text><text class="terminal-1389600277-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-18)">
+</text><text class="terminal-1389600277-r1" x="0" y="483.6" textLength="756.4" clip-path="url(#terminal-1389600277-line-19)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;tools.github.io/commitizen/exit_codes/</text><text class="terminal-1389600277-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-19)">
+</text><text class="terminal-1389600277-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1389600277-line-20)">
+</text><text class="terminal-1389600277-r1" x="0" y="532.4" textLength="109.8" clip-path="url(#terminal-1389600277-line-21)">commands:</text><text class="terminal-1389600277-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-21)">
+</text><text class="terminal-1389600277-r2" x="24.4" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">{</text><text class="terminal-1389600277-r1" x="36.6" y="556.8" textLength="829.6" clip-path="url(#terminal-1389600277-line-22)">init,commit,c,ls,example,info,schema,bump,changelog,ch,check,version</text><text class="terminal-1389600277-r2" x="866.2" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">}</text><text class="terminal-1389600277-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-22)">
+</text><text class="terminal-1389600277-r1" x="0" y="581.2" textLength="646.6" clip-path="url(#terminal-1389600277-line-23)">&#160;&#160;&#160;&#160;init&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;init&#160;commitizen&#160;configuration</text><text class="terminal-1389600277-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-23)">
+</text><text class="terminal-1389600277-r1" x="0" y="605.6" textLength="134.2" clip-path="url(#terminal-1389600277-line-24)">&#160;&#160;&#160;&#160;commit&#160;</text><text class="terminal-1389600277-r2" x="134.2" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">(</text><text class="terminal-1389600277-r1" x="146.4" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">c</text><text class="terminal-1389600277-r2" x="158.6" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">)</text><text class="terminal-1389600277-r1" x="170.8" y="605.6" textLength="329.4" clip-path="url(#terminal-1389600277-line-24)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;create&#160;new&#160;commit</text><text class="terminal-1389600277-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-24)">
+</text><text class="terminal-1389600277-r1" x="0" y="630" textLength="610" clip-path="url(#terminal-1389600277-line-25)">&#160;&#160;&#160;&#160;ls&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;available&#160;commitizens</text><text class="terminal-1389600277-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1389600277-line-25)">
+</text><text class="terminal-1389600277-r1" x="0" y="654.4" textLength="524.6" clip-path="url(#terminal-1389600277-line-26)">&#160;&#160;&#160;&#160;example&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;commit&#160;example</text><text class="terminal-1389600277-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-26)">
+</text><text class="terminal-1389600277-r1" x="0" y="678.8" textLength="646.6" clip-path="url(#terminal-1389600277-line-27)">&#160;&#160;&#160;&#160;info&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;information&#160;about&#160;the&#160;cz</text><text class="terminal-1389600277-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-27)">
+</text><text class="terminal-1389600277-r1" x="0" y="703.2" textLength="512.4" clip-path="url(#terminal-1389600277-line-28)">&#160;&#160;&#160;&#160;schema&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;commit&#160;schema</text><text class="terminal-1389600277-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-28)">
+</text><text class="terminal-1389600277-r1" x="0" y="727.6" textLength="805.2" clip-path="url(#terminal-1389600277-line-29)">&#160;&#160;&#160;&#160;bump&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bump&#160;semantic&#160;version&#160;based&#160;on&#160;the&#160;git&#160;log</text><text class="terminal-1389600277-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-29)">
+</text><text class="terminal-1389600277-r1" x="0" y="752" textLength="170.8" clip-path="url(#terminal-1389600277-line-30)">&#160;&#160;&#160;&#160;changelog&#160;</text><text class="terminal-1389600277-r2" x="170.8" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">(</text><text class="terminal-1389600277-r1" x="183" y="752" textLength="24.4" clip-path="url(#terminal-1389600277-line-30)">ch</text><text class="terminal-1389600277-r2" x="207.4" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">)</text><text class="terminal-1389600277-r1" x="219.6" y="752" textLength="305" clip-path="url(#terminal-1389600277-line-30)">&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;changelog&#160;</text><text class="terminal-1389600277-r2" x="524.6" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">(</text><text class="terminal-1389600277-r1" x="536.8" y="752" textLength="329.4" clip-path="url(#terminal-1389600277-line-30)">note&#160;that&#160;it&#160;will&#160;overwrite</text><text class="terminal-1389600277-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1389600277-line-30)">
+</text><text class="terminal-1389600277-r1" x="0" y="776.4" textLength="451.4" clip-path="url(#terminal-1389600277-line-31)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;existing&#160;file</text><text class="terminal-1389600277-r2" x="451.4" y="776.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-31)">)</text><text class="terminal-1389600277-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-31)">
+</text><text class="terminal-1389600277-r1" x="0" y="800.8" textLength="951.6" clip-path="url(#terminal-1389600277-line-32)">&#160;&#160;&#160;&#160;check&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;validates&#160;that&#160;a&#160;commit&#160;message&#160;matches&#160;the&#160;commitizen</text><text class="terminal-1389600277-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1389600277-line-32)">
+</text><text class="terminal-1389600277-r1" x="0" y="825.2" textLength="366" clip-path="url(#terminal-1389600277-line-33)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;schema</text><text class="terminal-1389600277-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1389600277-line-33)">
+</text><text class="terminal-1389600277-r1" x="0" y="849.6" textLength="902.8" clip-path="url(#terminal-1389600277-line-34)">&#160;&#160;&#160;&#160;version&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;get&#160;the&#160;version&#160;of&#160;the&#160;installed&#160;commitizen&#160;or&#160;the</text><text class="terminal-1389600277-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1389600277-line-34)">
+</text><text class="terminal-1389600277-r1" x="0" y="874" textLength="488" clip-path="url(#terminal-1389600277-line-35)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;current&#160;project&#160;</text><text class="terminal-1389600277-r2" x="488" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">(</text><text class="terminal-1389600277-r1" x="500.2" y="874" textLength="353.8" clip-path="url(#terminal-1389600277-line-35)">default:&#160;installed&#160;commitizen</text><text class="terminal-1389600277-r2" x="854" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">)</text><text class="terminal-1389600277-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1389600277-line-35)">
+</text><text class="terminal-1389600277-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1389600277-line-36)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_bump___help.svg b/docs/images/cli_help/cz_bump___help.svg
new file mode 100644
index 0000000000..7d007df312
--- /dev/null
+++ b/docs/images/cli_help/cz_bump___help.svg
@@ -0,0 +1,389 @@
+<svg class="rich-terminal" viewBox="0 0 994 2075.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-1655558888-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-1655558888-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-1655558888-r1 { fill: #c5c8c6 }
+.terminal-1655558888-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-1655558888-r3 { fill: #68a0b3;font-weight: bold }
+.terminal-1655558888-r4 { fill: #98a84b }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-1655558888-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="2024.1999999999998" />
+    </clipPath>
+    <clipPath id="terminal-1655558888-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-22">
+    <rect x="0" y="538.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-23">
+    <rect x="0" y="562.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-24">
+    <rect x="0" y="587.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-25">
+    <rect x="0" y="611.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-26">
+    <rect x="0" y="635.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-27">
+    <rect x="0" y="660.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-28">
+    <rect x="0" y="684.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-29">
+    <rect x="0" y="709.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-30">
+    <rect x="0" y="733.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-31">
+    <rect x="0" y="757.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-32">
+    <rect x="0" y="782.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-33">
+    <rect x="0" y="806.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-34">
+    <rect x="0" y="831.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-35">
+    <rect x="0" y="855.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-36">
+    <rect x="0" y="879.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-37">
+    <rect x="0" y="904.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-38">
+    <rect x="0" y="928.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-39">
+    <rect x="0" y="953.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-40">
+    <rect x="0" y="977.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-41">
+    <rect x="0" y="1001.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-42">
+    <rect x="0" y="1026.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-43">
+    <rect x="0" y="1050.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-44">
+    <rect x="0" y="1075.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-45">
+    <rect x="0" y="1099.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-46">
+    <rect x="0" y="1123.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-47">
+    <rect x="0" y="1148.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-48">
+    <rect x="0" y="1172.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-49">
+    <rect x="0" y="1197.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-50">
+    <rect x="0" y="1221.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-51">
+    <rect x="0" y="1245.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-52">
+    <rect x="0" y="1270.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-53">
+    <rect x="0" y="1294.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-54">
+    <rect x="0" y="1319.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-55">
+    <rect x="0" y="1343.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-56">
+    <rect x="0" y="1367.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-57">
+    <rect x="0" y="1392.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-58">
+    <rect x="0" y="1416.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-59">
+    <rect x="0" y="1441.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-60">
+    <rect x="0" y="1465.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-61">
+    <rect x="0" y="1489.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-62">
+    <rect x="0" y="1514.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-63">
+    <rect x="0" y="1538.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-64">
+    <rect x="0" y="1563.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-65">
+    <rect x="0" y="1587.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-66">
+    <rect x="0" y="1611.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-67">
+    <rect x="0" y="1636.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-68">
+    <rect x="0" y="1660.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-69">
+    <rect x="0" y="1685.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-70">
+    <rect x="0" y="1709.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-71">
+    <rect x="0" y="1733.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-72">
+    <rect x="0" y="1758.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-73">
+    <rect x="0" y="1782.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-74">
+    <rect x="0" y="1807.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-75">
+    <rect x="0" y="1831.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-76">
+    <rect x="0" y="1855.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-77">
+    <rect x="0" y="1880.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-78">
+    <rect x="0" y="1904.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-79">
+    <rect x="0" y="1929.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-80">
+    <rect x="0" y="1953.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1655558888-line-81">
+    <rect x="0" y="1977.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="2073.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1655558888-clip-terminal)">
+    
+    <g class="terminal-1655558888-matrix">
+    <text class="terminal-1655558888-r1" x="0" y="20" textLength="195.2" clip-path="url(#terminal-1655558888-line-0)">$&#160;cz&#160;bump&#160;--help</text><text class="terminal-1655558888-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1655558888-line-0)">
+</text><text class="terminal-1655558888-r1" x="0" y="44.4" textLength="183" clip-path="url(#terminal-1655558888-line-1)">usage:&#160;cz&#160;bump&#160;</text><text class="terminal-1655558888-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="195.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1655558888-line-1)">-h</text><text class="terminal-1655558888-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="256.2" y="44.4" textLength="109.8" clip-path="url(#terminal-1655558888-line-1)">--dry-run</text><text class="terminal-1655558888-r2" x="366" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="390.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="402.6" y="44.4" textLength="146.4" clip-path="url(#terminal-1655558888-line-1)">--files-only</text><text class="terminal-1655558888-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="573.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="585.6" y="44.4" textLength="183" clip-path="url(#terminal-1655558888-line-1)">--local-version</text><text class="terminal-1655558888-r2" x="768.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r2" x="793" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">[</text><text class="terminal-1655558888-r1" x="805.2" y="44.4" textLength="134.2" clip-path="url(#terminal-1655558888-line-1)">--changelog</text><text class="terminal-1655558888-r2" x="939.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">]</text><text class="terminal-1655558888-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-1)">
+</text><text class="terminal-1655558888-r2" x="183" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">[</text><text class="terminal-1655558888-r1" x="195.2" y="68.8" textLength="134.2" clip-path="url(#terminal-1655558888-line-2)">--no-verify</text><text class="terminal-1655558888-r2" x="329.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">]</text><text class="terminal-1655558888-r2" x="353.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">[</text><text class="terminal-1655558888-r1" x="366" y="68.8" textLength="61" clip-path="url(#terminal-1655558888-line-2)">--yes</text><text class="terminal-1655558888-r2" x="427" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">]</text><text class="terminal-1655558888-r2" x="451.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">[</text><text class="terminal-1655558888-r1" x="463.6" y="68.8" textLength="280.6" clip-path="url(#terminal-1655558888-line-2)">--tag-format&#160;TAG_FORMAT</text><text class="terminal-1655558888-r2" x="744.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">]</text><text class="terminal-1655558888-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-2)">
+</text><text class="terminal-1655558888-r2" x="183" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">[</text><text class="terminal-1655558888-r1" x="195.2" y="93.2" textLength="329.4" clip-path="url(#terminal-1655558888-line-3)">--bump-message&#160;BUMP_MESSAGE</text><text class="terminal-1655558888-r2" x="524.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">]</text><text class="terminal-1655558888-r2" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">[</text><text class="terminal-1655558888-r1" x="561.2" y="93.2" textLength="158.6" clip-path="url(#terminal-1655558888-line-3)">--prerelease&#160;</text><text class="terminal-1655558888-r2" x="719.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">{</text><text class="terminal-1655558888-r1" x="732" y="93.2" textLength="158.6" clip-path="url(#terminal-1655558888-line-3)">alpha,beta,rc</text><text class="terminal-1655558888-r2" x="890.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">}</text><text class="terminal-1655558888-r2" x="902.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">]</text><text class="terminal-1655558888-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-3)">
+</text><text class="terminal-1655558888-r2" x="183" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">[</text><text class="terminal-1655558888-r1" x="195.2" y="117.6" textLength="280.6" clip-path="url(#terminal-1655558888-line-4)">--devrelease&#160;DEVRELEASE</text><text class="terminal-1655558888-r2" x="475.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">]</text><text class="terminal-1655558888-r2" x="500.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">[</text><text class="terminal-1655558888-r1" x="512.4" y="117.6" textLength="146.4" clip-path="url(#terminal-1655558888-line-4)">--increment&#160;</text><text class="terminal-1655558888-r2" x="658.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">{</text><text class="terminal-1655558888-r1" x="671" y="117.6" textLength="207.4" clip-path="url(#terminal-1655558888-line-4)">MAJOR,MINOR,PATCH</text><text class="terminal-1655558888-r2" x="878.4" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">}</text><text class="terminal-1655558888-r2" x="890.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">]</text><text class="terminal-1655558888-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-4)">
+</text><text class="terminal-1655558888-r2" x="183" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">[</text><text class="terminal-1655558888-r1" x="195.2" y="142" textLength="207.4" clip-path="url(#terminal-1655558888-line-5)">--increment-mode&#160;</text><text class="terminal-1655558888-r2" x="402.6" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">{</text><text class="terminal-1655558888-r1" x="414.8" y="142" textLength="146.4" clip-path="url(#terminal-1655558888-line-5)">linear,exact</text><text class="terminal-1655558888-r2" x="561.2" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">}</text><text class="terminal-1655558888-r2" x="573.4" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">]</text><text class="terminal-1655558888-r2" x="597.8" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">[</text><text class="terminal-1655558888-r1" x="610" y="142" textLength="231.8" clip-path="url(#terminal-1655558888-line-5)">--check-consistency</text><text class="terminal-1655558888-r2" x="841.8" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">]</text><text class="terminal-1655558888-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1655558888-line-5)">
+</text><text class="terminal-1655558888-r2" x="183" y="166.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-6)">[</text><text class="terminal-1655558888-r1" x="195.2" y="166.4" textLength="183" clip-path="url(#terminal-1655558888-line-6)">--annotated-tag</text><text class="terminal-1655558888-r2" x="378.2" y="166.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-6)">]</text><text class="terminal-1655558888-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-6)">
+</text><text class="terminal-1655558888-r2" x="183" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">[</text><text class="terminal-1655558888-r1" x="195.2" y="190.8" textLength="549" clip-path="url(#terminal-1655558888-line-7)">--annotated-tag-message&#160;ANNOTATED_TAG_MESSAGE</text><text class="terminal-1655558888-r2" x="744.2" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">]</text><text class="terminal-1655558888-r2" x="768.6" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">[</text><text class="terminal-1655558888-r1" x="780.8" y="190.8" textLength="122" clip-path="url(#terminal-1655558888-line-7)">--gpg-sign</text><text class="terminal-1655558888-r2" x="902.8" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">]</text><text class="terminal-1655558888-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-7)">
+</text><text class="terminal-1655558888-r2" x="183" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">[</text><text class="terminal-1655558888-r1" x="195.2" y="215.2" textLength="256.2" clip-path="url(#terminal-1655558888-line-8)">--changelog-to-stdout</text><text class="terminal-1655558888-r2" x="451.4" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">]</text><text class="terminal-1655558888-r2" x="475.8" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">[</text><text class="terminal-1655558888-r1" x="488" y="215.2" textLength="268.4" clip-path="url(#terminal-1655558888-line-8)">--git-output-to-stderr</text><text class="terminal-1655558888-r2" x="756.4" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">]</text><text class="terminal-1655558888-r2" x="780.8" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">[</text><text class="terminal-1655558888-r1" x="793" y="215.2" textLength="85.4" clip-path="url(#terminal-1655558888-line-8)">--retry</text><text class="terminal-1655558888-r2" x="878.4" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">]</text><text class="terminal-1655558888-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-8)">
+</text><text class="terminal-1655558888-r2" x="183" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">[</text><text class="terminal-1655558888-r1" x="195.2" y="239.6" textLength="244" clip-path="url(#terminal-1655558888-line-9)">--major-version-zero</text><text class="terminal-1655558888-r2" x="439.2" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">]</text><text class="terminal-1655558888-r2" x="463.6" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">[</text><text class="terminal-1655558888-r1" x="475.8" y="239.6" textLength="231.8" clip-path="url(#terminal-1655558888-line-9)">--template&#160;TEMPLATE</text><text class="terminal-1655558888-r2" x="707.6" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">]</text><text class="terminal-1655558888-r2" x="732" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">[</text><text class="terminal-1655558888-r1" x="744.2" y="239.6" textLength="158.6" clip-path="url(#terminal-1655558888-line-9)">--extra&#160;EXTRA</text><text class="terminal-1655558888-r2" x="902.8" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">]</text><text class="terminal-1655558888-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-9)">
+</text><text class="terminal-1655558888-r2" x="183" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">[</text><text class="terminal-1655558888-r1" x="195.2" y="264" textLength="256.2" clip-path="url(#terminal-1655558888-line-10)">--file-name&#160;FILE_NAME</text><text class="terminal-1655558888-r2" x="451.4" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">]</text><text class="terminal-1655558888-r2" x="475.8" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">[</text><text class="terminal-1655558888-r1" x="488" y="264" textLength="451.4" clip-path="url(#terminal-1655558888-line-10)">--prerelease-offset&#160;PRERELEASE_OFFSET</text><text class="terminal-1655558888-r2" x="939.4" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">]</text><text class="terminal-1655558888-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1655558888-line-10)">
+</text><text class="terminal-1655558888-r2" x="183" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">[</text><text class="terminal-1655558888-r1" x="195.2" y="288.4" textLength="207.4" clip-path="url(#terminal-1655558888-line-11)">--version-scheme&#160;</text><text class="terminal-1655558888-r2" x="402.6" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">{</text><text class="terminal-1655558888-r1" x="414.8" y="288.4" textLength="256.2" clip-path="url(#terminal-1655558888-line-11)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="671" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">}</text><text class="terminal-1655558888-r2" x="683.2" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">]</text><text class="terminal-1655558888-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-11)">
+</text><text class="terminal-1655558888-r2" x="183" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">[</text><text class="terminal-1655558888-r1" x="195.2" y="312.8" textLength="183" clip-path="url(#terminal-1655558888-line-12)">--version-type&#160;</text><text class="terminal-1655558888-r2" x="378.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">{</text><text class="terminal-1655558888-r1" x="390.4" y="312.8" textLength="256.2" clip-path="url(#terminal-1655558888-line-12)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="646.6" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">}</text><text class="terminal-1655558888-r2" x="658.8" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">]</text><text class="terminal-1655558888-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-12)">
+</text><text class="terminal-1655558888-r2" x="183" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">[</text><text class="terminal-1655558888-r1" x="195.2" y="337.2" textLength="378.2" clip-path="url(#terminal-1655558888-line-13)">--build-metadata&#160;BUILD_METADATA</text><text class="terminal-1655558888-r2" x="573.4" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">]</text><text class="terminal-1655558888-r2" x="597.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">[</text><text class="terminal-1655558888-r1" x="610" y="337.2" textLength="122" clip-path="url(#terminal-1655558888-line-13)">--get-next</text><text class="terminal-1655558888-r2" x="732" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">]</text><text class="terminal-1655558888-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-13)">
+</text><text class="terminal-1655558888-r2" x="183" y="361.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-14)">[</text><text class="terminal-1655558888-r1" x="195.2" y="361.6" textLength="207.4" clip-path="url(#terminal-1655558888-line-14)">--allow-no-commit</text><text class="terminal-1655558888-r2" x="402.6" y="361.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-14)">]</text><text class="terminal-1655558888-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-14)">
+</text><text class="terminal-1655558888-r2" x="183" y="386" textLength="12.2" clip-path="url(#terminal-1655558888-line-15)">[</text><text class="terminal-1655558888-r1" x="195.2" y="386" textLength="170.8" clip-path="url(#terminal-1655558888-line-15)">MANUAL_VERSION</text><text class="terminal-1655558888-r2" x="366" y="386" textLength="12.2" clip-path="url(#terminal-1655558888-line-15)">]</text><text class="terminal-1655558888-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1655558888-line-15)">
+</text><text class="terminal-1655558888-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-16)">
+</text><text class="terminal-1655558888-r1" x="0" y="434.8" textLength="512.4" clip-path="url(#terminal-1655558888-line-17)">bump&#160;semantic&#160;version&#160;based&#160;on&#160;the&#160;git&#160;log</text><text class="terminal-1655558888-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-17)">
+</text><text class="terminal-1655558888-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-18)">
+</text><text class="terminal-1655558888-r1" x="0" y="483.6" textLength="256.2" clip-path="url(#terminal-1655558888-line-19)">positional&#160;arguments:</text><text class="terminal-1655558888-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-19)">
+</text><text class="terminal-1655558888-r1" x="0" y="508" textLength="610" clip-path="url(#terminal-1655558888-line-20)">&#160;&#160;MANUAL_VERSION&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bump&#160;to&#160;the&#160;given&#160;version&#160;</text><text class="terminal-1655558888-r2" x="610" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">(</text><text class="terminal-1655558888-r1" x="622.2" y="508" textLength="61" clip-path="url(#terminal-1655558888-line-20)">e.g:&#160;</text><text class="terminal-1655558888-r3" x="683.2" y="508" textLength="36.6" clip-path="url(#terminal-1655558888-line-20)">1.5</text><text class="terminal-1655558888-r1" x="719.8" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">.</text><text class="terminal-1655558888-r3" x="732" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">3</text><text class="terminal-1655558888-r2" x="744.2" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">)</text><text class="terminal-1655558888-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1655558888-line-20)">
+</text><text class="terminal-1655558888-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-21)">
+</text><text class="terminal-1655558888-r1" x="0" y="556.8" textLength="97.6" clip-path="url(#terminal-1655558888-line-22)">options:</text><text class="terminal-1655558888-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-22)">
+</text><text class="terminal-1655558888-r1" x="0" y="581.2" textLength="671" clip-path="url(#terminal-1655558888-line-23)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1655558888-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-23)">
+</text><text class="terminal-1655558888-r1" x="0" y="605.6" textLength="915" clip-path="url(#terminal-1655558888-line-24)">&#160;&#160;--dry-run&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;output&#160;to&#160;stdout,&#160;no&#160;commit,&#160;no&#160;modified&#160;files</text><text class="terminal-1655558888-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-24)">
+</text><text class="terminal-1655558888-r1" x="0" y="630" textLength="793" clip-path="url(#terminal-1655558888-line-25)">&#160;&#160;--files-only&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;bump&#160;version&#160;in&#160;the&#160;files&#160;from&#160;the&#160;config</text><text class="terminal-1655558888-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1655558888-line-25)">
+</text><text class="terminal-1655558888-r1" x="0" y="654.4" textLength="719.8" clip-path="url(#terminal-1655558888-line-26)">&#160;&#160;--local-version&#160;&#160;&#160;&#160;&#160;&#160;&#160;bump&#160;only&#160;the&#160;local&#160;version&#160;portion</text><text class="terminal-1655558888-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-26)">
+</text><text class="terminal-1655558888-r1" x="0" y="678.8" textLength="841.8" clip-path="url(#terminal-1655558888-line-27)">&#160;&#160;--changelog,&#160;-ch&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;the&#160;changelog&#160;for&#160;the&#160;newest&#160;version</text><text class="terminal-1655558888-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-27)">
+</text><text class="terminal-1655558888-r1" x="0" y="703.2" textLength="902.8" clip-path="url(#terminal-1655558888-line-28)">&#160;&#160;--no-verify&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;this&#160;option&#160;bypasses&#160;the&#160;pre-commit&#160;and&#160;commit-msg</text><text class="terminal-1655558888-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-28)">
+</text><text class="terminal-1655558888-r1" x="0" y="727.6" textLength="353.8" clip-path="url(#terminal-1655558888-line-29)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;hooks</text><text class="terminal-1655558888-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-29)">
+</text><text class="terminal-1655558888-r1" x="0" y="752" textLength="719.8" clip-path="url(#terminal-1655558888-line-30)">&#160;&#160;--yes&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;accept&#160;automatically&#160;questions&#160;done</text><text class="terminal-1655558888-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-1655558888-line-30)">
+</text><text class="terminal-1655558888-r1" x="0" y="776.4" textLength="305" clip-path="url(#terminal-1655558888-line-31)">&#160;&#160;--tag-format&#160;TAG_FORMAT</text><text class="terminal-1655558888-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-31)">
+</text><text class="terminal-1655558888-r1" x="0" y="800.8" textLength="939.4" clip-path="url(#terminal-1655558888-line-32)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;format&#160;used&#160;to&#160;tag&#160;the&#160;commit&#160;and&#160;read&#160;it,&#160;use&#160;it</text><text class="terminal-1655558888-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-32)">
+</text><text class="terminal-1655558888-r1" x="0" y="825.2" textLength="866.2" clip-path="url(#terminal-1655558888-line-33)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;in&#160;existing&#160;projects,&#160;wrap&#160;around&#160;simple&#160;quotes</text><text class="terminal-1655558888-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-33)">
+</text><text class="terminal-1655558888-r1" x="0" y="849.6" textLength="353.8" clip-path="url(#terminal-1655558888-line-34)">&#160;&#160;--bump-message&#160;BUMP_MESSAGE</text><text class="terminal-1655558888-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-34)">
+</text><text class="terminal-1655558888-r1" x="0" y="874" textLength="902.8" clip-path="url(#terminal-1655558888-line-35)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;template&#160;used&#160;to&#160;create&#160;the&#160;release&#160;commit,&#160;useful</text><text class="terminal-1655558888-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-1655558888-line-35)">
+</text><text class="terminal-1655558888-r1" x="0" y="898.4" textLength="536.8" clip-path="url(#terminal-1655558888-line-36)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;when&#160;working&#160;with&#160;CI</text><text class="terminal-1655558888-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-36)">
+</text><text class="terminal-1655558888-r1" x="0" y="922.8" textLength="244" clip-path="url(#terminal-1655558888-line-37)">&#160;&#160;--prerelease,&#160;-pr&#160;</text><text class="terminal-1655558888-r2" x="244" y="922.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-37)">{</text><text class="terminal-1655558888-r1" x="256.2" y="922.8" textLength="158.6" clip-path="url(#terminal-1655558888-line-37)">alpha,beta,rc</text><text class="terminal-1655558888-r2" x="414.8" y="922.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-37)">}</text><text class="terminal-1655558888-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-37)">
+</text><text class="terminal-1655558888-r1" x="0" y="947.2" textLength="597.8" clip-path="url(#terminal-1655558888-line-38)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;choose&#160;type&#160;of&#160;prerelease</text><text class="terminal-1655558888-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-38)">
+</text><text class="terminal-1655558888-r1" x="0" y="971.6" textLength="353.8" clip-path="url(#terminal-1655558888-line-39)">&#160;&#160;--devrelease,&#160;-d&#160;DEVRELEASE</text><text class="terminal-1655558888-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-39)">
+</text><text class="terminal-1655558888-r1" x="0" y="996" textLength="841.8" clip-path="url(#terminal-1655558888-line-40)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;specify&#160;non-negative&#160;integer&#160;for&#160;dev.&#160;release</text><text class="terminal-1655558888-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-1655558888-line-40)">
+</text><text class="terminal-1655558888-r1" x="0" y="1020.4" textLength="170.8" clip-path="url(#terminal-1655558888-line-41)">&#160;&#160;--increment&#160;</text><text class="terminal-1655558888-r2" x="170.8" y="1020.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-41)">{</text><text class="terminal-1655558888-r1" x="183" y="1020.4" textLength="207.4" clip-path="url(#terminal-1655558888-line-41)">MAJOR,MINOR,PATCH</text><text class="terminal-1655558888-r2" x="390.4" y="1020.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-41)">}</text><text class="terminal-1655558888-r1" x="976" y="1020.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-41)">
+</text><text class="terminal-1655558888-r1" x="0" y="1044.8" textLength="756.4" clip-path="url(#terminal-1655558888-line-42)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;manually&#160;specify&#160;the&#160;desired&#160;increment</text><text class="terminal-1655558888-r1" x="976" y="1044.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-42)">
+</text><text class="terminal-1655558888-r1" x="0" y="1069.2" textLength="231.8" clip-path="url(#terminal-1655558888-line-43)">&#160;&#160;--increment-mode&#160;</text><text class="terminal-1655558888-r2" x="231.8" y="1069.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-43)">{</text><text class="terminal-1655558888-r1" x="244" y="1069.2" textLength="146.4" clip-path="url(#terminal-1655558888-line-43)">linear,exact</text><text class="terminal-1655558888-r2" x="390.4" y="1069.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-43)">}</text><text class="terminal-1655558888-r1" x="976" y="1069.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-43)">
+</text><text class="terminal-1655558888-r1" x="0" y="1093.6" textLength="902.8" clip-path="url(#terminal-1655558888-line-44)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;set&#160;the&#160;method&#160;by&#160;which&#160;the&#160;new&#160;version&#160;is&#160;chosen.</text><text class="terminal-1655558888-r1" x="976" y="1093.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-44)">
+</text><text class="terminal-1655558888-r4" x="292.8" y="1118" textLength="97.6" clip-path="url(#terminal-1655558888-line-45)">&#x27;linear&#x27;</text><text class="terminal-1655558888-r2" x="402.6" y="1118" textLength="12.2" clip-path="url(#terminal-1655558888-line-45)">(</text><text class="terminal-1655558888-r1" x="414.8" y="1118" textLength="85.4" clip-path="url(#terminal-1655558888-line-45)">default</text><text class="terminal-1655558888-r2" x="500.2" y="1118" textLength="12.2" clip-path="url(#terminal-1655558888-line-45)">)</text><text class="terminal-1655558888-r1" x="512.4" y="1118" textLength="414.8" clip-path="url(#terminal-1655558888-line-45)">&#160;guesses&#160;the&#160;next&#160;version&#160;based&#160;on</text><text class="terminal-1655558888-r1" x="976" y="1118" textLength="12.2" clip-path="url(#terminal-1655558888-line-45)">
+</text><text class="terminal-1655558888-r1" x="0" y="1142.4" textLength="939.4" clip-path="url(#terminal-1655558888-line-46)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;typical&#160;linear&#160;version&#160;progression,&#160;such&#160;that&#160;bumping</text><text class="terminal-1655558888-r1" x="976" y="1142.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-46)">
+</text><text class="terminal-1655558888-r1" x="0" y="1166.8" textLength="866.2" clip-path="url(#terminal-1655558888-line-47)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;of&#160;a&#160;pre-release&#160;with&#160;lower&#160;precedence&#160;than&#160;the</text><text class="terminal-1655558888-r1" x="976" y="1166.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-47)">
+</text><text class="terminal-1655558888-r1" x="0" y="1191.2" textLength="939.4" clip-path="url(#terminal-1655558888-line-48)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;current&#160;pre-release&#160;phase&#160;maintains&#160;the&#160;current&#160;phase</text><text class="terminal-1655558888-r1" x="976" y="1191.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-48)">
+</text><text class="terminal-1655558888-r1" x="0" y="1215.6" textLength="561.2" clip-path="url(#terminal-1655558888-line-49)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;of&#160;higher&#160;precedence.&#160;</text><text class="terminal-1655558888-r4" x="561.2" y="1215.6" textLength="85.4" clip-path="url(#terminal-1655558888-line-49)">&#x27;exact&#x27;</text><text class="terminal-1655558888-r1" x="646.6" y="1215.6" textLength="305" clip-path="url(#terminal-1655558888-line-49)">&#160;applies&#160;the&#160;changes&#160;that</text><text class="terminal-1655558888-r1" x="976" y="1215.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-49)">
+</text><text class="terminal-1655558888-r1" x="0" y="1240" textLength="536.8" clip-path="url(#terminal-1655558888-line-50)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;have&#160;been&#160;specified&#160;</text><text class="terminal-1655558888-r2" x="536.8" y="1240" textLength="12.2" clip-path="url(#terminal-1655558888-line-50)">(</text><text class="terminal-1655558888-r1" x="549" y="1240" textLength="353.8" clip-path="url(#terminal-1655558888-line-50)">or&#160;determined&#160;from&#160;the&#160;commit</text><text class="terminal-1655558888-r1" x="976" y="1240" textLength="12.2" clip-path="url(#terminal-1655558888-line-50)">
+</text><text class="terminal-1655558888-r1" x="0" y="1264.4" textLength="329.4" clip-path="url(#terminal-1655558888-line-51)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;log</text><text class="terminal-1655558888-r2" x="329.4" y="1264.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-51)">)</text><text class="terminal-1655558888-r1" x="341.6" y="1264.4" textLength="585.6" clip-path="url(#terminal-1655558888-line-51)">&#160;without&#160;interpretation,&#160;such&#160;that&#160;the&#160;increment</text><text class="terminal-1655558888-r1" x="976" y="1264.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-51)">
+</text><text class="terminal-1655558888-r1" x="0" y="1288.8" textLength="707.6" clip-path="url(#terminal-1655558888-line-52)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;and&#160;pre-release&#160;are&#160;always&#160;honored</text><text class="terminal-1655558888-r1" x="976" y="1288.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-52)">
+</text><text class="terminal-1655558888-r1" x="0" y="1313.2" textLength="317.2" clip-path="url(#terminal-1655558888-line-53)">&#160;&#160;--check-consistency,&#160;-cc</text><text class="terminal-1655558888-r1" x="976" y="1313.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-53)">
+</text><text class="terminal-1655558888-r1" x="0" y="1337.6" textLength="951.6" clip-path="url(#terminal-1655558888-line-54)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;check&#160;consistency&#160;among&#160;versions&#160;defined&#160;in&#160;commitizen</text><text class="terminal-1655558888-r1" x="976" y="1337.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-54)">
+</text><text class="terminal-1655558888-r1" x="0" y="1362" textLength="671" clip-path="url(#terminal-1655558888-line-55)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;configuration&#160;and&#160;version_files</text><text class="terminal-1655558888-r1" x="976" y="1362" textLength="12.2" clip-path="url(#terminal-1655558888-line-55)">
+</text><text class="terminal-1655558888-r1" x="0" y="1386.4" textLength="866.2" clip-path="url(#terminal-1655558888-line-56)">&#160;&#160;--annotated-tag,&#160;-at&#160;&#160;create&#160;annotated&#160;tag&#160;instead&#160;of&#160;lightweight&#160;one</text><text class="terminal-1655558888-r1" x="976" y="1386.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-56)">
+</text><text class="terminal-1655558888-r1" x="0" y="1410.8" textLength="646.6" clip-path="url(#terminal-1655558888-line-57)">&#160;&#160;--annotated-tag-message,&#160;-atm&#160;ANNOTATED_TAG_MESSAGE</text><text class="terminal-1655558888-r1" x="976" y="1410.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-57)">
+</text><text class="terminal-1655558888-r1" x="0" y="1435.2" textLength="634.4" clip-path="url(#terminal-1655558888-line-58)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;create&#160;annotated&#160;tag&#160;message</text><text class="terminal-1655558888-r1" x="976" y="1435.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-58)">
+</text><text class="terminal-1655558888-r1" x="0" y="1459.6" textLength="719.8" clip-path="url(#terminal-1655558888-line-59)">&#160;&#160;--gpg-sign,&#160;-s&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;sign&#160;tag&#160;instead&#160;of&#160;lightweight&#160;one</text><text class="terminal-1655558888-r1" x="976" y="1459.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-59)">
+</text><text class="terminal-1655558888-r1" x="0" y="1484" textLength="280.6" clip-path="url(#terminal-1655558888-line-60)">&#160;&#160;--changelog-to-stdout</text><text class="terminal-1655558888-r1" x="976" y="1484" textLength="12.2" clip-path="url(#terminal-1655558888-line-60)">
+</text><text class="terminal-1655558888-r1" x="0" y="1508.4" textLength="658.8" clip-path="url(#terminal-1655558888-line-61)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Output&#160;changelog&#160;to&#160;the&#160;stdout</text><text class="terminal-1655558888-r1" x="976" y="1508.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-61)">
+</text><text class="terminal-1655558888-r1" x="0" y="1532.8" textLength="292.8" clip-path="url(#terminal-1655558888-line-62)">&#160;&#160;--git-output-to-stderr</text><text class="terminal-1655558888-r1" x="976" y="1532.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-62)">
+</text><text class="terminal-1655558888-r1" x="0" y="1557.2" textLength="646.6" clip-path="url(#terminal-1655558888-line-63)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Redirect&#160;git&#160;output&#160;to&#160;stderr</text><text class="terminal-1655558888-r1" x="976" y="1557.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-63)">
+</text><text class="terminal-1655558888-r1" x="0" y="1581.6" textLength="744.2" clip-path="url(#terminal-1655558888-line-64)">&#160;&#160;--retry&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;retry&#160;commit&#160;if&#160;it&#160;fails&#160;the&#160;1st&#160;time</text><text class="terminal-1655558888-r1" x="976" y="1581.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-64)">
+</text><text class="terminal-1655558888-r1" x="0" y="1606" textLength="939.4" clip-path="url(#terminal-1655558888-line-65)">&#160;&#160;--major-version-zero&#160;&#160;keep&#160;major&#160;version&#160;at&#160;zero,&#160;even&#160;for&#160;breaking&#160;changes</text><text class="terminal-1655558888-r1" x="976" y="1606" textLength="12.2" clip-path="url(#terminal-1655558888-line-65)">
+</text><text class="terminal-1655558888-r1" x="0" y="1630.4" textLength="305" clip-path="url(#terminal-1655558888-line-66)">&#160;&#160;--template,&#160;-t&#160;TEMPLATE</text><text class="terminal-1655558888-r1" x="976" y="1630.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-66)">
+</text><text class="terminal-1655558888-r1" x="0" y="1654.8" textLength="646.6" clip-path="url(#terminal-1655558888-line-67)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;changelog&#160;template&#160;file&#160;name&#160;</text><text class="terminal-1655558888-r2" x="646.6" y="1654.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-67)">(</text><text class="terminal-1655558888-r1" x="658.8" y="1654.8" textLength="280.6" clip-path="url(#terminal-1655558888-line-67)">relative&#160;to&#160;the&#160;current</text><text class="terminal-1655558888-r1" x="976" y="1654.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-67)">
+</text><text class="terminal-1655558888-r1" x="0" y="1679.2" textLength="500.2" clip-path="url(#terminal-1655558888-line-68)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;working&#160;directory</text><text class="terminal-1655558888-r2" x="500.2" y="1679.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-68)">)</text><text class="terminal-1655558888-r1" x="976" y="1679.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-68)">
+</text><text class="terminal-1655558888-r1" x="0" y="1703.6" textLength="622.2" clip-path="url(#terminal-1655558888-line-69)">&#160;&#160;--extra,&#160;-e&#160;EXTRA&#160;&#160;&#160;&#160;&#160;a&#160;changelog&#160;extra&#160;variable&#160;</text><text class="terminal-1655558888-r2" x="622.2" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">(</text><text class="terminal-1655558888-r1" x="634.4" y="1703.6" textLength="146.4" clip-path="url(#terminal-1655558888-line-69)">in&#160;the&#160;form&#160;</text><text class="terminal-1655558888-r4" x="780.8" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">&#x27;</text><text class="terminal-1655558888-r4" x="793" y="1703.6" textLength="36.6" clip-path="url(#terminal-1655558888-line-69)">key</text><text class="terminal-1655558888-r4" x="829.6" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">=</text><text class="terminal-1655558888-r4" x="841.8" y="1703.6" textLength="61" clip-path="url(#terminal-1655558888-line-69)">value</text><text class="terminal-1655558888-r4" x="902.8" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">&#x27;</text><text class="terminal-1655558888-r2" x="915" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">)</text><text class="terminal-1655558888-r1" x="976" y="1703.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-69)">
+</text><text class="terminal-1655558888-r1" x="0" y="1728" textLength="280.6" clip-path="url(#terminal-1655558888-line-70)">&#160;&#160;--file-name&#160;FILE_NAME</text><text class="terminal-1655558888-r1" x="976" y="1728" textLength="12.2" clip-path="url(#terminal-1655558888-line-70)">
+</text><text class="terminal-1655558888-r1" x="0" y="1752.4" textLength="573.4" clip-path="url(#terminal-1655558888-line-71)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;file&#160;name&#160;of&#160;changelog&#160;</text><text class="terminal-1655558888-r2" x="573.4" y="1752.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-71)">(</text><text class="terminal-1655558888-r1" x="585.6" y="1752.4" textLength="109.8" clip-path="url(#terminal-1655558888-line-71)">default:&#160;</text><text class="terminal-1655558888-r4" x="695.4" y="1752.4" textLength="170.8" clip-path="url(#terminal-1655558888-line-71)">&#x27;CHANGELOG.md&#x27;</text><text class="terminal-1655558888-r2" x="866.2" y="1752.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-71)">)</text><text class="terminal-1655558888-r1" x="976" y="1752.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-71)">
+</text><text class="terminal-1655558888-r1" x="0" y="1776.8" textLength="475.8" clip-path="url(#terminal-1655558888-line-72)">&#160;&#160;--prerelease-offset&#160;PRERELEASE_OFFSET</text><text class="terminal-1655558888-r1" x="976" y="1776.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-72)">
+</text><text class="terminal-1655558888-r1" x="0" y="1801.2" textLength="719.8" clip-path="url(#terminal-1655558888-line-73)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;start&#160;pre-releases&#160;with&#160;this&#160;offset</text><text class="terminal-1655558888-r1" x="976" y="1801.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-73)">
+</text><text class="terminal-1655558888-r1" x="0" y="1825.6" textLength="231.8" clip-path="url(#terminal-1655558888-line-74)">&#160;&#160;--version-scheme&#160;</text><text class="terminal-1655558888-r2" x="231.8" y="1825.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-74)">{</text><text class="terminal-1655558888-r1" x="244" y="1825.6" textLength="256.2" clip-path="url(#terminal-1655558888-line-74)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="500.2" y="1825.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-74)">}</text><text class="terminal-1655558888-r1" x="976" y="1825.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-74)">
+</text><text class="terminal-1655558888-r1" x="0" y="1850" textLength="549" clip-path="url(#terminal-1655558888-line-75)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;choose&#160;version&#160;scheme</text><text class="terminal-1655558888-r1" x="976" y="1850" textLength="12.2" clip-path="url(#terminal-1655558888-line-75)">
+</text><text class="terminal-1655558888-r1" x="0" y="1874.4" textLength="207.4" clip-path="url(#terminal-1655558888-line-76)">&#160;&#160;--version-type&#160;</text><text class="terminal-1655558888-r2" x="207.4" y="1874.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-76)">{</text><text class="terminal-1655558888-r1" x="219.6" y="1874.4" textLength="256.2" clip-path="url(#terminal-1655558888-line-76)">pep440,semver,semver2</text><text class="terminal-1655558888-r2" x="475.8" y="1874.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-76)">}</text><text class="terminal-1655558888-r1" x="976" y="1874.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-76)">
+</text><text class="terminal-1655558888-r1" x="0" y="1898.8" textLength="780.8" clip-path="url(#terminal-1655558888-line-77)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Deprecated,&#160;use&#160;--version-scheme&#160;instead</text><text class="terminal-1655558888-r1" x="976" y="1898.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-77)">
+</text><text class="terminal-1655558888-r1" x="0" y="1923.2" textLength="402.6" clip-path="url(#terminal-1655558888-line-78)">&#160;&#160;--build-metadata&#160;BUILD_METADATA</text><text class="terminal-1655558888-r1" x="976" y="1923.2" textLength="12.2" clip-path="url(#terminal-1655558888-line-78)">
+</text><text class="terminal-1655558888-r1" x="0" y="1947.6" textLength="915" clip-path="url(#terminal-1655558888-line-79)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Add&#160;additional&#160;build-metadata&#160;to&#160;the&#160;version-number</text><text class="terminal-1655558888-r1" x="976" y="1947.6" textLength="12.2" clip-path="url(#terminal-1655558888-line-79)">
+</text><text class="terminal-1655558888-r1" x="0" y="1972" textLength="854" clip-path="url(#terminal-1655558888-line-80)">&#160;&#160;--get-next&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Determine&#160;the&#160;next&#160;version&#160;and&#160;write&#160;to&#160;stdout</text><text class="terminal-1655558888-r1" x="976" y="1972" textLength="12.2" clip-path="url(#terminal-1655558888-line-80)">
+</text><text class="terminal-1655558888-r1" x="0" y="1996.4" textLength="744.2" clip-path="url(#terminal-1655558888-line-81)">&#160;&#160;--allow-no-commit&#160;&#160;&#160;&#160;&#160;bump&#160;version&#160;without&#160;eligible&#160;commits</text><text class="terminal-1655558888-r1" x="976" y="1996.4" textLength="12.2" clip-path="url(#terminal-1655558888-line-81)">
+</text><text class="terminal-1655558888-r1" x="976" y="2020.8" textLength="12.2" clip-path="url(#terminal-1655558888-line-82)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_changelog___help.svg b/docs/images/cli_help/cz_changelog___help.svg
new file mode 100644
index 0000000000..69304f40cf
--- /dev/null
+++ b/docs/images/cli_help/cz_changelog___help.svg
@@ -0,0 +1,229 @@
+<svg class="rich-terminal" viewBox="0 0 994 1099.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-2926696453-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-2926696453-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-2926696453-r1 { fill: #c5c8c6 }
+.terminal-2926696453-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-2926696453-r3 { fill: #68a0b3;font-weight: bold }
+.terminal-2926696453-r4 { fill: #98a84b }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-2926696453-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="1048.2" />
+    </clipPath>
+    <clipPath id="terminal-2926696453-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-22">
+    <rect x="0" y="538.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-23">
+    <rect x="0" y="562.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-24">
+    <rect x="0" y="587.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-25">
+    <rect x="0" y="611.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-26">
+    <rect x="0" y="635.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-27">
+    <rect x="0" y="660.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-28">
+    <rect x="0" y="684.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-29">
+    <rect x="0" y="709.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-30">
+    <rect x="0" y="733.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-31">
+    <rect x="0" y="757.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-32">
+    <rect x="0" y="782.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-33">
+    <rect x="0" y="806.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-34">
+    <rect x="0" y="831.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-35">
+    <rect x="0" y="855.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-36">
+    <rect x="0" y="879.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-37">
+    <rect x="0" y="904.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-38">
+    <rect x="0" y="928.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-39">
+    <rect x="0" y="953.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-40">
+    <rect x="0" y="977.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2926696453-line-41">
+    <rect x="0" y="1001.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="1097.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-2926696453-clip-terminal)">
+    
+    <g class="terminal-2926696453-matrix">
+    <text class="terminal-2926696453-r1" x="0" y="20" textLength="256.2" clip-path="url(#terminal-2926696453-line-0)">$&#160;cz&#160;changelog&#160;--help</text><text class="terminal-2926696453-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2926696453-line-0)">
+</text><text class="terminal-2926696453-r1" x="0" y="44.4" textLength="244" clip-path="url(#terminal-2926696453-line-1)">usage:&#160;cz&#160;changelog&#160;</text><text class="terminal-2926696453-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="256.2" y="44.4" textLength="24.4" clip-path="url(#terminal-2926696453-line-1)">-h</text><text class="terminal-2926696453-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r2" x="305" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="317.2" y="44.4" textLength="109.8" clip-path="url(#terminal-2926696453-line-1)">--dry-run</text><text class="terminal-2926696453-r2" x="427" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r2" x="451.4" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">[</text><text class="terminal-2926696453-r1" x="463.6" y="44.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-1)">--file-name&#160;FILE_NAME</text><text class="terminal-2926696453-r2" x="719.8" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">]</text><text class="terminal-2926696453-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-1)">
+</text><text class="terminal-2926696453-r2" x="244" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">[</text><text class="terminal-2926696453-r1" x="256.2" y="68.8" textLength="475.8" clip-path="url(#terminal-2926696453-line-2)">--unreleased-version&#160;UNRELEASED_VERSION</text><text class="terminal-2926696453-r2" x="732" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">]</text><text class="terminal-2926696453-r2" x="756.4" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">[</text><text class="terminal-2926696453-r1" x="768.6" y="68.8" textLength="158.6" clip-path="url(#terminal-2926696453-line-2)">--incremental</text><text class="terminal-2926696453-r2" x="927.2" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">]</text><text class="terminal-2926696453-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-2)">
+</text><text class="terminal-2926696453-r2" x="244" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">[</text><text class="terminal-2926696453-r1" x="256.2" y="93.2" textLength="256.2" clip-path="url(#terminal-2926696453-line-3)">--start-rev&#160;START_REV</text><text class="terminal-2926696453-r2" x="512.4" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">]</text><text class="terminal-2926696453-r2" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">[</text><text class="terminal-2926696453-r1" x="549" y="93.2" textLength="219.6" clip-path="url(#terminal-2926696453-line-3)">--merge-prerelease</text><text class="terminal-2926696453-r2" x="768.6" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">]</text><text class="terminal-2926696453-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-3)">
+</text><text class="terminal-2926696453-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">[</text><text class="terminal-2926696453-r1" x="256.2" y="117.6" textLength="207.4" clip-path="url(#terminal-2926696453-line-4)">--version-scheme&#160;</text><text class="terminal-2926696453-r2" x="463.6" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">{</text><text class="terminal-2926696453-r1" x="475.8" y="117.6" textLength="256.2" clip-path="url(#terminal-2926696453-line-4)">pep440,semver,semver2</text><text class="terminal-2926696453-r2" x="732" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">}</text><text class="terminal-2926696453-r2" x="744.2" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">]</text><text class="terminal-2926696453-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-4)">
+</text><text class="terminal-2926696453-r2" x="244" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">[</text><text class="terminal-2926696453-r1" x="256.2" y="142" textLength="402.6" clip-path="url(#terminal-2926696453-line-5)">--export-template&#160;EXPORT_TEMPLATE</text><text class="terminal-2926696453-r2" x="658.8" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">]</text><text class="terminal-2926696453-r2" x="683.2" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">[</text><text class="terminal-2926696453-r1" x="695.4" y="142" textLength="231.8" clip-path="url(#terminal-2926696453-line-5)">--template&#160;TEMPLATE</text><text class="terminal-2926696453-r2" x="927.2" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">]</text><text class="terminal-2926696453-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2926696453-line-5)">
+</text><text class="terminal-2926696453-r2" x="244" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">[</text><text class="terminal-2926696453-r1" x="256.2" y="166.4" textLength="158.6" clip-path="url(#terminal-2926696453-line-6)">--extra&#160;EXTRA</text><text class="terminal-2926696453-r2" x="414.8" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">]</text><text class="terminal-2926696453-r2" x="439.2" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">[</text><text class="terminal-2926696453-r1" x="451.4" y="166.4" textLength="280.6" clip-path="url(#terminal-2926696453-line-6)">--tag-format&#160;TAG_FORMAT</text><text class="terminal-2926696453-r2" x="732" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">]</text><text class="terminal-2926696453-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-6)">
+</text><text class="terminal-2926696453-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-7)">
+</text><text class="terminal-2926696453-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-8)">
+</text><text class="terminal-2926696453-r1" x="0" y="239.6" textLength="231.8" clip-path="url(#terminal-2926696453-line-9)">generate&#160;changelog&#160;</text><text class="terminal-2926696453-r2" x="231.8" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">(</text><text class="terminal-2926696453-r1" x="244" y="239.6" textLength="500.2" clip-path="url(#terminal-2926696453-line-9)">note&#160;that&#160;it&#160;will&#160;overwrite&#160;existing&#160;file</text><text class="terminal-2926696453-r2" x="744.2" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">)</text><text class="terminal-2926696453-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-9)">
+</text><text class="terminal-2926696453-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2926696453-line-10)">
+</text><text class="terminal-2926696453-r1" x="0" y="288.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-11)">positional&#160;arguments:</text><text class="terminal-2926696453-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-11)">
+</text><text class="terminal-2926696453-r1" x="0" y="312.8" textLength="805.2" clip-path="url(#terminal-2926696453-line-12)">&#160;&#160;rev_range&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generates&#160;changelog&#160;for&#160;the&#160;given&#160;version&#160;</text><text class="terminal-2926696453-r2" x="805.2" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">(</text><text class="terminal-2926696453-r1" x="817.4" y="312.8" textLength="61" clip-path="url(#terminal-2926696453-line-12)">e.g:&#160;</text><text class="terminal-2926696453-r3" x="878.4" y="312.8" textLength="36.6" clip-path="url(#terminal-2926696453-line-12)">1.5</text><text class="terminal-2926696453-r1" x="915" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">.</text><text class="terminal-2926696453-r3" x="927.2" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">3</text><text class="terminal-2926696453-r2" x="939.4" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">)</text><text class="terminal-2926696453-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-12)">
+</text><text class="terminal-2926696453-r1" x="0" y="337.2" textLength="500.2" clip-path="url(#terminal-2926696453-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;or&#160;version&#160;range&#160;</text><text class="terminal-2926696453-r2" x="500.2" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">(</text><text class="terminal-2926696453-r1" x="512.4" y="337.2" textLength="61" clip-path="url(#terminal-2926696453-line-13)">e.g:&#160;</text><text class="terminal-2926696453-r3" x="573.4" y="337.2" textLength="36.6" clip-path="url(#terminal-2926696453-line-13)">1.5</text><text class="terminal-2926696453-r1" x="610" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">.</text><text class="terminal-2926696453-r3" x="622.2" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">3</text><text class="terminal-2926696453-r1" x="634.4" y="337.2" textLength="24.4" clip-path="url(#terminal-2926696453-line-13)">..</text><text class="terminal-2926696453-r3" x="658.8" y="337.2" textLength="36.6" clip-path="url(#terminal-2926696453-line-13)">1.7</text><text class="terminal-2926696453-r1" x="695.4" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">.</text><text class="terminal-2926696453-r3" x="707.6" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">9</text><text class="terminal-2926696453-r2" x="719.8" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">)</text><text class="terminal-2926696453-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-13)">
+</text><text class="terminal-2926696453-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-14)">
+</text><text class="terminal-2926696453-r1" x="0" y="386" textLength="97.6" clip-path="url(#terminal-2926696453-line-15)">options:</text><text class="terminal-2926696453-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2926696453-line-15)">
+</text><text class="terminal-2926696453-r1" x="0" y="410.4" textLength="671" clip-path="url(#terminal-2926696453-line-16)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-2926696453-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-16)">
+</text><text class="terminal-2926696453-r1" x="0" y="434.8" textLength="585.6" clip-path="url(#terminal-2926696453-line-17)">&#160;&#160;--dry-run&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;changelog&#160;to&#160;stdout</text><text class="terminal-2926696453-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-17)">
+</text><text class="terminal-2926696453-r1" x="0" y="459.2" textLength="280.6" clip-path="url(#terminal-2926696453-line-18)">&#160;&#160;--file-name&#160;FILE_NAME</text><text class="terminal-2926696453-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-18)">
+</text><text class="terminal-2926696453-r1" x="0" y="483.6" textLength="573.4" clip-path="url(#terminal-2926696453-line-19)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;file&#160;name&#160;of&#160;changelog&#160;</text><text class="terminal-2926696453-r2" x="573.4" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">(</text><text class="terminal-2926696453-r1" x="585.6" y="483.6" textLength="109.8" clip-path="url(#terminal-2926696453-line-19)">default:&#160;</text><text class="terminal-2926696453-r4" x="695.4" y="483.6" textLength="170.8" clip-path="url(#terminal-2926696453-line-19)">&#x27;CHANGELOG.md&#x27;</text><text class="terminal-2926696453-r2" x="866.2" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">)</text><text class="terminal-2926696453-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-19)">
+</text><text class="terminal-2926696453-r1" x="0" y="508" textLength="500.2" clip-path="url(#terminal-2926696453-line-20)">&#160;&#160;--unreleased-version&#160;UNRELEASED_VERSION</text><text class="terminal-2926696453-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2926696453-line-20)">
+</text><text class="terminal-2926696453-r1" x="0" y="532.4" textLength="707.6" clip-path="url(#terminal-2926696453-line-21)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;set&#160;the&#160;value&#160;for&#160;the&#160;new&#160;version&#160;</text><text class="terminal-2926696453-r2" x="707.6" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">(</text><text class="terminal-2926696453-r1" x="719.8" y="532.4" textLength="207.4" clip-path="url(#terminal-2926696453-line-21)">use&#160;the&#160;tag&#160;value</text><text class="terminal-2926696453-r2" x="927.2" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">)</text><text class="terminal-2926696453-r1" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">,</text><text class="terminal-2926696453-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-21)">
+</text><text class="terminal-2926696453-r1" x="0" y="556.8" textLength="622.2" clip-path="url(#terminal-2926696453-line-22)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;instead&#160;of&#160;using&#160;unreleased</text><text class="terminal-2926696453-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-22)">
+</text><text class="terminal-2926696453-r1" x="0" y="581.2" textLength="939.4" clip-path="url(#terminal-2926696453-line-23)">&#160;&#160;--incremental&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generates&#160;changelog&#160;from&#160;last&#160;created&#160;version,&#160;useful</text><text class="terminal-2926696453-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-23)">
+</text><text class="terminal-2926696453-r1" x="0" y="605.6" textLength="817.4" clip-path="url(#terminal-2926696453-line-24)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;if&#160;the&#160;changelog&#160;has&#160;been&#160;manually&#160;modified</text><text class="terminal-2926696453-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-24)">
+</text><text class="terminal-2926696453-r1" x="0" y="630" textLength="280.6" clip-path="url(#terminal-2926696453-line-25)">&#160;&#160;--start-rev&#160;START_REV</text><text class="terminal-2926696453-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-2926696453-line-25)">
+</text><text class="terminal-2926696453-r1" x="0" y="654.4" textLength="866.2" clip-path="url(#terminal-2926696453-line-26)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;start&#160;rev&#160;of&#160;the&#160;changelog.&#160;If&#160;not&#160;set,&#160;it&#160;will</text><text class="terminal-2926696453-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-26)">
+</text><text class="terminal-2926696453-r1" x="0" y="678.8" textLength="695.4" clip-path="url(#terminal-2926696453-line-27)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;generate&#160;changelog&#160;from&#160;the&#160;start</text><text class="terminal-2926696453-r1" x="976" y="678.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-27)">
+</text><text class="terminal-2926696453-r1" x="0" y="703.2" textLength="915" clip-path="url(#terminal-2926696453-line-28)">&#160;&#160;--merge-prerelease&#160;&#160;&#160;&#160;collect&#160;all&#160;changes&#160;from&#160;prereleases&#160;into&#160;next&#160;non-</text><text class="terminal-2926696453-r1" x="976" y="703.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-28)">
+</text><text class="terminal-2926696453-r1" x="0" y="727.6" textLength="951.6" clip-path="url(#terminal-2926696453-line-29)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;prerelease.&#160;If&#160;not&#160;set,&#160;it&#160;will&#160;include&#160;prereleases&#160;in</text><text class="terminal-2926696453-r1" x="976" y="727.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-29)">
+</text><text class="terminal-2926696453-r1" x="0" y="752" textLength="451.4" clip-path="url(#terminal-2926696453-line-30)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;changelog</text><text class="terminal-2926696453-r1" x="976" y="752" textLength="12.2" clip-path="url(#terminal-2926696453-line-30)">
+</text><text class="terminal-2926696453-r1" x="0" y="776.4" textLength="231.8" clip-path="url(#terminal-2926696453-line-31)">&#160;&#160;--version-scheme&#160;</text><text class="terminal-2926696453-r2" x="231.8" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">{</text><text class="terminal-2926696453-r1" x="244" y="776.4" textLength="256.2" clip-path="url(#terminal-2926696453-line-31)">pep440,semver,semver2</text><text class="terminal-2926696453-r2" x="500.2" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">}</text><text class="terminal-2926696453-r1" x="976" y="776.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-31)">
+</text><text class="terminal-2926696453-r1" x="0" y="800.8" textLength="549" clip-path="url(#terminal-2926696453-line-32)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;choose&#160;version&#160;scheme</text><text class="terminal-2926696453-r1" x="976" y="800.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-32)">
+</text><text class="terminal-2926696453-r1" x="0" y="825.2" textLength="427" clip-path="url(#terminal-2926696453-line-33)">&#160;&#160;--export-template&#160;EXPORT_TEMPLATE</text><text class="terminal-2926696453-r1" x="976" y="825.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-33)">
+</text><text class="terminal-2926696453-r1" x="0" y="849.6" textLength="927.2" clip-path="url(#terminal-2926696453-line-34)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Export&#160;the&#160;changelog&#160;template&#160;into&#160;this&#160;file&#160;instead</text><text class="terminal-2926696453-r1" x="976" y="849.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-34)">
+</text><text class="terminal-2926696453-r1" x="0" y="874" textLength="475.8" clip-path="url(#terminal-2926696453-line-35)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;of&#160;rendering&#160;it</text><text class="terminal-2926696453-r1" x="976" y="874" textLength="12.2" clip-path="url(#terminal-2926696453-line-35)">
+</text><text class="terminal-2926696453-r1" x="0" y="898.4" textLength="305" clip-path="url(#terminal-2926696453-line-36)">&#160;&#160;--template,&#160;-t&#160;TEMPLATE</text><text class="terminal-2926696453-r1" x="976" y="898.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-36)">
+</text><text class="terminal-2926696453-r1" x="0" y="922.8" textLength="646.6" clip-path="url(#terminal-2926696453-line-37)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;changelog&#160;template&#160;file&#160;name&#160;</text><text class="terminal-2926696453-r2" x="646.6" y="922.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-37)">(</text><text class="terminal-2926696453-r1" x="658.8" y="922.8" textLength="280.6" clip-path="url(#terminal-2926696453-line-37)">relative&#160;to&#160;the&#160;current</text><text class="terminal-2926696453-r1" x="976" y="922.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-37)">
+</text><text class="terminal-2926696453-r1" x="0" y="947.2" textLength="500.2" clip-path="url(#terminal-2926696453-line-38)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;working&#160;directory</text><text class="terminal-2926696453-r2" x="500.2" y="947.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-38)">)</text><text class="terminal-2926696453-r1" x="976" y="947.2" textLength="12.2" clip-path="url(#terminal-2926696453-line-38)">
+</text><text class="terminal-2926696453-r1" x="0" y="971.6" textLength="622.2" clip-path="url(#terminal-2926696453-line-39)">&#160;&#160;--extra,&#160;-e&#160;EXTRA&#160;&#160;&#160;&#160;&#160;a&#160;changelog&#160;extra&#160;variable&#160;</text><text class="terminal-2926696453-r2" x="622.2" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">(</text><text class="terminal-2926696453-r1" x="634.4" y="971.6" textLength="146.4" clip-path="url(#terminal-2926696453-line-39)">in&#160;the&#160;form&#160;</text><text class="terminal-2926696453-r4" x="780.8" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">&#x27;</text><text class="terminal-2926696453-r4" x="793" y="971.6" textLength="36.6" clip-path="url(#terminal-2926696453-line-39)">key</text><text class="terminal-2926696453-r4" x="829.6" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">=</text><text class="terminal-2926696453-r4" x="841.8" y="971.6" textLength="61" clip-path="url(#terminal-2926696453-line-39)">value</text><text class="terminal-2926696453-r4" x="902.8" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">&#x27;</text><text class="terminal-2926696453-r2" x="915" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">)</text><text class="terminal-2926696453-r1" x="976" y="971.6" textLength="12.2" clip-path="url(#terminal-2926696453-line-39)">
+</text><text class="terminal-2926696453-r1" x="0" y="996" textLength="305" clip-path="url(#terminal-2926696453-line-40)">&#160;&#160;--tag-format&#160;TAG_FORMAT</text><text class="terminal-2926696453-r1" x="976" y="996" textLength="12.2" clip-path="url(#terminal-2926696453-line-40)">
+</text><text class="terminal-2926696453-r1" x="0" y="1020.4" textLength="878.4" clip-path="url(#terminal-2926696453-line-41)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;The&#160;format&#160;of&#160;the&#160;tag,&#160;wrap&#160;around&#160;simple&#160;quotes</text><text class="terminal-2926696453-r1" x="976" y="1020.4" textLength="12.2" clip-path="url(#terminal-2926696453-line-41)">
+</text><text class="terminal-2926696453-r1" x="976" y="1044.8" textLength="12.2" clip-path="url(#terminal-2926696453-line-42)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_check___help.svg b/docs/images/cli_help/cz_check___help.svg
new file mode 100644
index 0000000000..690bfec684
--- /dev/null
+++ b/docs/images/cli_help/cz_check___help.svg
@@ -0,0 +1,165 @@
+<svg class="rich-terminal" viewBox="0 0 994 708.8" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-1360575461-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-1360575461-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-1360575461-r1 { fill: #c5c8c6 }
+.terminal-1360575461-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-1360575461-r3 { fill: #d0b344 }
+.terminal-1360575461-r4 { fill: #68a0b3;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-1360575461-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="657.8" />
+    </clipPath>
+    <clipPath id="terminal-1360575461-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-22">
+    <rect x="0" y="538.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-23">
+    <rect x="0" y="562.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-24">
+    <rect x="0" y="587.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1360575461-line-25">
+    <rect x="0" y="611.5" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="706.8" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1360575461-clip-terminal)">
+    
+    <g class="terminal-1360575461-matrix">
+    <text class="terminal-1360575461-r1" x="0" y="20" textLength="207.4" clip-path="url(#terminal-1360575461-line-0)">$&#160;cz&#160;check&#160;--help</text><text class="terminal-1360575461-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1360575461-line-0)">
+</text><text class="terminal-1360575461-r1" x="0" y="44.4" textLength="195.2" clip-path="url(#terminal-1360575461-line-1)">usage:&#160;cz&#160;check&#160;</text><text class="terminal-1360575461-r2" x="195.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">[</text><text class="terminal-1360575461-r1" x="207.4" y="44.4" textLength="24.4" clip-path="url(#terminal-1360575461-line-1)">-h</text><text class="terminal-1360575461-r2" x="231.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">]</text><text class="terminal-1360575461-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">[</text><text class="terminal-1360575461-r1" x="268.4" y="44.4" textLength="427" clip-path="url(#terminal-1360575461-line-1)">--commit-msg-file&#160;COMMIT_MSG_FILE&#160;|</text><text class="terminal-1360575461-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-1)">
+</text><text class="terminal-1360575461-r1" x="0" y="68.8" textLength="610" clip-path="url(#terminal-1360575461-line-2)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;--rev-range&#160;REV_RANGE&#160;|&#160;-m&#160;MESSAGE</text><text class="terminal-1360575461-r2" x="610" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">]</text><text class="terminal-1360575461-r2" x="634.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">[</text><text class="terminal-1360575461-r1" x="646.6" y="68.8" textLength="158.6" clip-path="url(#terminal-1360575461-line-2)">--allow-abort</text><text class="terminal-1360575461-r2" x="805.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">]</text><text class="terminal-1360575461-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-2)">
+</text><text class="terminal-1360575461-r2" x="195.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">[</text><text class="terminal-1360575461-r1" x="207.4" y="93.2" textLength="231.8" clip-path="url(#terminal-1360575461-line-3)">--allowed-prefixes&#160;</text><text class="terminal-1360575461-r2" x="439.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">[</text><text class="terminal-1360575461-r1" x="451.4" y="93.2" textLength="207.4" clip-path="url(#terminal-1360575461-line-3)">ALLOWED_PREFIXES&#160;</text><text class="terminal-1360575461-r3" x="658.8" y="93.2" textLength="36.6" clip-path="url(#terminal-1360575461-line-3)">...</text><text class="terminal-1360575461-r2" x="695.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">]</text><text class="terminal-1360575461-r2" x="707.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">]</text><text class="terminal-1360575461-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-3)">
+</text><text class="terminal-1360575461-r2" x="195.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-4)">[</text><text class="terminal-1360575461-r1" x="207.4" y="117.6" textLength="280.6" clip-path="url(#terminal-1360575461-line-4)">-l&#160;MESSAGE_LENGTH_LIMIT</text><text class="terminal-1360575461-r2" x="488" y="117.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-4)">]</text><text class="terminal-1360575461-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-4)">
+</text><text class="terminal-1360575461-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1360575461-line-5)">
+</text><text class="terminal-1360575461-r1" x="0" y="166.4" textLength="744.2" clip-path="url(#terminal-1360575461-line-6)">validates&#160;that&#160;a&#160;commit&#160;message&#160;matches&#160;the&#160;commitizen&#160;schema</text><text class="terminal-1360575461-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-6)">
+</text><text class="terminal-1360575461-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-7)">
+</text><text class="terminal-1360575461-r1" x="0" y="215.2" textLength="97.6" clip-path="url(#terminal-1360575461-line-8)">options:</text><text class="terminal-1360575461-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-8)">
+</text><text class="terminal-1360575461-r1" x="0" y="239.6" textLength="671" clip-path="url(#terminal-1360575461-line-9)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1360575461-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-9)">
+</text><text class="terminal-1360575461-r1" x="0" y="264" textLength="427" clip-path="url(#terminal-1360575461-line-10)">&#160;&#160;--commit-msg-file&#160;COMMIT_MSG_FILE</text><text class="terminal-1360575461-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1360575461-line-10)">
+</text><text class="terminal-1360575461-r1" x="0" y="288.4" textLength="915" clip-path="url(#terminal-1360575461-line-11)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;ask&#160;for&#160;the&#160;name&#160;of&#160;the&#160;temporal&#160;file&#160;that&#160;contains</text><text class="terminal-1360575461-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-11)">
+</text><text class="terminal-1360575461-r1" x="0" y="312.8" textLength="902.8" clip-path="url(#terminal-1360575461-line-12)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;the&#160;commit&#160;message.&#160;Using&#160;it&#160;in&#160;a&#160;git&#160;hook&#160;script:</text><text class="terminal-1360575461-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-12)">
+</text><text class="terminal-1360575461-r3" x="292.8" y="337.2" textLength="97.6" clip-path="url(#terminal-1360575461-line-13)">MSG_FILE</text><text class="terminal-1360575461-r1" x="390.4" y="337.2" textLength="24.4" clip-path="url(#terminal-1360575461-line-13)">=$</text><text class="terminal-1360575461-r4" x="414.8" y="337.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-13)">1</text><text class="terminal-1360575461-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-13)">
+</text><text class="terminal-1360575461-r1" x="0" y="361.6" textLength="280.6" clip-path="url(#terminal-1360575461-line-14)">&#160;&#160;--rev-range&#160;REV_RANGE</text><text class="terminal-1360575461-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-14)">
+</text><text class="terminal-1360575461-r1" x="0" y="386" textLength="854" clip-path="url(#terminal-1360575461-line-15)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;a&#160;range&#160;of&#160;git&#160;rev&#160;to&#160;check.&#160;e.g,&#160;master..HEAD</text><text class="terminal-1360575461-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1360575461-line-15)">
+</text><text class="terminal-1360575461-r1" x="0" y="410.4" textLength="280.6" clip-path="url(#terminal-1360575461-line-16)">&#160;&#160;-m,&#160;--message&#160;MESSAGE</text><text class="terminal-1360575461-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-16)">
+</text><text class="terminal-1360575461-r1" x="0" y="434.8" textLength="768.6" clip-path="url(#terminal-1360575461-line-17)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;commit&#160;message&#160;that&#160;needs&#160;to&#160;be&#160;checked</text><text class="terminal-1360575461-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-17)">
+</text><text class="terminal-1360575461-r1" x="0" y="459.2" textLength="927.2" clip-path="url(#terminal-1360575461-line-18)">&#160;&#160;--allow-abort&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;allow&#160;empty&#160;commit&#160;messages,&#160;which&#160;typically&#160;abort&#160;a</text><text class="terminal-1360575461-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-18)">
+</text><text class="terminal-1360575461-r1" x="0" y="483.6" textLength="366" clip-path="url(#terminal-1360575461-line-19)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;commit</text><text class="terminal-1360575461-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-19)">
+</text><text class="terminal-1360575461-r1" x="0" y="508" textLength="256.2" clip-path="url(#terminal-1360575461-line-20)">&#160;&#160;--allowed-prefixes&#160;</text><text class="terminal-1360575461-r2" x="256.2" y="508" textLength="12.2" clip-path="url(#terminal-1360575461-line-20)">[</text><text class="terminal-1360575461-r1" x="268.4" y="508" textLength="207.4" clip-path="url(#terminal-1360575461-line-20)">ALLOWED_PREFIXES&#160;</text><text class="terminal-1360575461-r3" x="475.8" y="508" textLength="36.6" clip-path="url(#terminal-1360575461-line-20)">...</text><text class="terminal-1360575461-r2" x="512.4" y="508" textLength="12.2" clip-path="url(#terminal-1360575461-line-20)">]</text><text class="terminal-1360575461-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1360575461-line-20)">
+</text><text class="terminal-1360575461-r1" x="0" y="532.4" textLength="951.6" clip-path="url(#terminal-1360575461-line-21)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;allowed&#160;commit&#160;message&#160;prefixes.&#160;If&#160;the&#160;message&#160;starts</text><text class="terminal-1360575461-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-21)">
+</text><text class="terminal-1360575461-r1" x="0" y="556.8" textLength="951.6" clip-path="url(#terminal-1360575461-line-22)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;by&#160;one&#160;of&#160;these&#160;prefixes,&#160;the&#160;message&#160;won&#x27;t&#160;be&#160;checked</text><text class="terminal-1360575461-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1360575461-line-22)">
+</text><text class="terminal-1360575461-r1" x="0" y="581.2" textLength="500.2" clip-path="url(#terminal-1360575461-line-23)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;against&#160;the&#160;regex</text><text class="terminal-1360575461-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-1360575461-line-23)">
+</text><text class="terminal-1360575461-r1" x="0" y="605.6" textLength="597.8" clip-path="url(#terminal-1360575461-line-24)">&#160;&#160;-l,&#160;--message-length-limit&#160;MESSAGE_LENGTH_LIMIT</text><text class="terminal-1360575461-r1" x="976" y="605.6" textLength="12.2" clip-path="url(#terminal-1360575461-line-24)">
+</text><text class="terminal-1360575461-r1" x="0" y="630" textLength="732" clip-path="url(#terminal-1360575461-line-25)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;length&#160;limit&#160;of&#160;the&#160;commit&#160;message;&#160;</text><text class="terminal-1360575461-r4" x="732" y="630" textLength="12.2" clip-path="url(#terminal-1360575461-line-25)">0</text><text class="terminal-1360575461-r1" x="744.2" y="630" textLength="158.6" clip-path="url(#terminal-1360575461-line-25)">&#160;for&#160;no&#160;limit</text><text class="terminal-1360575461-r1" x="976" y="630" textLength="12.2" clip-path="url(#terminal-1360575461-line-25)">
+</text><text class="terminal-1360575461-r1" x="976" y="654.4" textLength="12.2" clip-path="url(#terminal-1360575461-line-26)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_commit___help.svg b/docs/images/cli_help/cz_commit___help.svg
new file mode 100644
index 0000000000..59cdd12b9b
--- /dev/null
+++ b/docs/images/cli_help/cz_commit___help.svg
@@ -0,0 +1,153 @@
+<svg class="rich-terminal" viewBox="0 0 994 635.5999999999999" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-905765158-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-905765158-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-905765158-r1 { fill: #c5c8c6 }
+.terminal-905765158-r2 { fill: #c5c8c6;font-weight: bold }
+.terminal-905765158-r3 { fill: #98a84b }
+.terminal-905765158-r4 { fill: #68a0b3;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-905765158-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="584.5999999999999" />
+    </clipPath>
+    <clipPath id="terminal-905765158-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-905765158-line-22">
+    <rect x="0" y="538.3" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-905765158-clip-terminal)">
+    
+    <g class="terminal-905765158-matrix">
+    <text class="terminal-905765158-r1" x="0" y="20" textLength="219.6" clip-path="url(#terminal-905765158-line-0)">$&#160;cz&#160;commit&#160;--help</text><text class="terminal-905765158-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-905765158-line-0)">
+</text><text class="terminal-905765158-r1" x="0" y="44.4" textLength="207.4" clip-path="url(#terminal-905765158-line-1)">usage:&#160;cz&#160;commit&#160;</text><text class="terminal-905765158-r2" x="207.4" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="219.6" y="44.4" textLength="24.4" clip-path="url(#terminal-905765158-line-1)">-h</text><text class="terminal-905765158-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r2" x="268.4" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="280.6" y="44.4" textLength="85.4" clip-path="url(#terminal-905765158-line-1)">--retry</text><text class="terminal-905765158-r2" x="366" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r2" x="390.4" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="402.6" y="44.4" textLength="122" clip-path="url(#terminal-905765158-line-1)">--no-retry</text><text class="terminal-905765158-r2" x="524.6" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r2" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">[</text><text class="terminal-905765158-r1" x="561.2" y="44.4" textLength="109.8" clip-path="url(#terminal-905765158-line-1)">--dry-run</text><text class="terminal-905765158-r2" x="671" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">]</text><text class="terminal-905765158-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-905765158-line-1)">
+</text><text class="terminal-905765158-r2" x="207.4" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="219.6" y="68.8" textLength="402.6" clip-path="url(#terminal-905765158-line-2)">--write-message-to-file&#160;FILE_PATH</text><text class="terminal-905765158-r2" x="622.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r2" x="646.6" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="658.8" y="68.8" textLength="24.4" clip-path="url(#terminal-905765158-line-2)">-s</text><text class="terminal-905765158-r2" x="683.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r2" x="707.6" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="719.8" y="68.8" textLength="24.4" clip-path="url(#terminal-905765158-line-2)">-a</text><text class="terminal-905765158-r2" x="744.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r2" x="768.6" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">[</text><text class="terminal-905765158-r1" x="780.8" y="68.8" textLength="24.4" clip-path="url(#terminal-905765158-line-2)">-e</text><text class="terminal-905765158-r2" x="805.2" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">]</text><text class="terminal-905765158-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-905765158-line-2)">
+</text><text class="terminal-905765158-r2" x="207.4" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">[</text><text class="terminal-905765158-r1" x="219.6" y="93.2" textLength="280.6" clip-path="url(#terminal-905765158-line-3)">-l&#160;MESSAGE_LENGTH_LIMIT</text><text class="terminal-905765158-r2" x="500.2" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">]</text><text class="terminal-905765158-r2" x="524.6" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">[</text><text class="terminal-905765158-r1" x="536.8" y="93.2" textLength="24.4" clip-path="url(#terminal-905765158-line-3)">--</text><text class="terminal-905765158-r2" x="561.2" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">]</text><text class="terminal-905765158-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-905765158-line-3)">
+</text><text class="terminal-905765158-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-905765158-line-4)">
+</text><text class="terminal-905765158-r1" x="0" y="142" textLength="207.4" clip-path="url(#terminal-905765158-line-5)">create&#160;new&#160;commit</text><text class="terminal-905765158-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-905765158-line-5)">
+</text><text class="terminal-905765158-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-905765158-line-6)">
+</text><text class="terminal-905765158-r1" x="0" y="190.8" textLength="97.6" clip-path="url(#terminal-905765158-line-7)">options:</text><text class="terminal-905765158-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-905765158-line-7)">
+</text><text class="terminal-905765158-r1" x="0" y="215.2" textLength="671" clip-path="url(#terminal-905765158-line-8)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-905765158-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-905765158-line-8)">
+</text><text class="terminal-905765158-r1" x="0" y="239.6" textLength="500.2" clip-path="url(#terminal-905765158-line-9)">&#160;&#160;--retry&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;retry&#160;last&#160;commit</text><text class="terminal-905765158-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-905765158-line-9)">
+</text><text class="terminal-905765158-r1" x="0" y="264" textLength="878.4" clip-path="url(#terminal-905765158-line-10)">&#160;&#160;--no-retry&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;skip&#160;retry&#160;if&#160;retry_after_failure&#160;is&#160;set&#160;to&#160;true</text><text class="terminal-905765158-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-905765158-line-10)">
+</text><text class="terminal-905765158-r1" x="0" y="288.4" textLength="915" clip-path="url(#terminal-905765158-line-11)">&#160;&#160;--dry-run&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;output&#160;to&#160;stdout,&#160;no&#160;commit,&#160;no&#160;modified&#160;files</text><text class="terminal-905765158-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-905765158-line-11)">
+</text><text class="terminal-905765158-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-905765158-line-12)">&#160;&#160;--write-message-to-file&#160;FILE_PATH</text><text class="terminal-905765158-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-905765158-line-12)">
+</text><text class="terminal-905765158-r1" x="0" y="337.2" textLength="780.8" clip-path="url(#terminal-905765158-line-13)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;write&#160;message&#160;to&#160;file&#160;before&#160;committing&#160;</text><text class="terminal-905765158-r2" x="780.8" y="337.2" textLength="12.2" clip-path="url(#terminal-905765158-line-13)">(</text><text class="terminal-905765158-r1" x="793" y="337.2" textLength="73.2" clip-path="url(#terminal-905765158-line-13)">can&#160;be</text><text class="terminal-905765158-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-905765158-line-13)">
+</text><text class="terminal-905765158-r1" x="0" y="361.6" textLength="573.4" clip-path="url(#terminal-905765158-line-14)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;combined&#160;with&#160;--dry-run</text><text class="terminal-905765158-r2" x="573.4" y="361.6" textLength="12.2" clip-path="url(#terminal-905765158-line-14)">)</text><text class="terminal-905765158-r1" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-905765158-line-14)">
+</text><text class="terminal-905765158-r1" x="0" y="386" textLength="488" clip-path="url(#terminal-905765158-line-15)">&#160;&#160;-s,&#160;--signoff&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Deprecated,&#160;use&#160;</text><text class="terminal-905765158-r3" x="488" y="386" textLength="207.4" clip-path="url(#terminal-905765158-line-15)">&#x27;cz&#160;commit&#160;--&#160;-s&#x27;</text><text class="terminal-905765158-r1" x="695.4" y="386" textLength="97.6" clip-path="url(#terminal-905765158-line-15)">&#160;instead</text><text class="terminal-905765158-r1" x="976" y="386" textLength="12.2" clip-path="url(#terminal-905765158-line-15)">
+</text><text class="terminal-905765158-r1" x="0" y="410.4" textLength="902.8" clip-path="url(#terminal-905765158-line-16)">&#160;&#160;-a,&#160;--all&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Tell&#160;the&#160;command&#160;to&#160;automatically&#160;stage&#160;files&#160;that</text><text class="terminal-905765158-r1" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-905765158-line-16)">
+</text><text class="terminal-905765158-r1" x="0" y="434.8" textLength="951.6" clip-path="url(#terminal-905765158-line-17)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;have&#160;been&#160;modified&#160;and&#160;deleted,&#160;but&#160;new&#160;files&#160;you&#160;have</text><text class="terminal-905765158-r1" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-905765158-line-17)">
+</text><text class="terminal-905765158-r1" x="0" y="459.2" textLength="732" clip-path="url(#terminal-905765158-line-18)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;not&#160;told&#160;Git&#160;about&#160;are&#160;not&#160;affected.</text><text class="terminal-905765158-r1" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-905765158-line-18)">
+</text><text class="terminal-905765158-r1" x="0" y="483.6" textLength="793" clip-path="url(#terminal-905765158-line-19)">&#160;&#160;-e,&#160;--edit&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;edit&#160;the&#160;commit&#160;message&#160;before&#160;committing</text><text class="terminal-905765158-r1" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-905765158-line-19)">
+</text><text class="terminal-905765158-r1" x="0" y="508" textLength="597.8" clip-path="url(#terminal-905765158-line-20)">&#160;&#160;-l,&#160;--message-length-limit&#160;MESSAGE_LENGTH_LIMIT</text><text class="terminal-905765158-r1" x="976" y="508" textLength="12.2" clip-path="url(#terminal-905765158-line-20)">
+</text><text class="terminal-905765158-r1" x="0" y="532.4" textLength="732" clip-path="url(#terminal-905765158-line-21)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;length&#160;limit&#160;of&#160;the&#160;commit&#160;message;&#160;</text><text class="terminal-905765158-r4" x="732" y="532.4" textLength="12.2" clip-path="url(#terminal-905765158-line-21)">0</text><text class="terminal-905765158-r1" x="744.2" y="532.4" textLength="158.6" clip-path="url(#terminal-905765158-line-21)">&#160;for&#160;no&#160;limit</text><text class="terminal-905765158-r1" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-905765158-line-21)">
+</text><text class="terminal-905765158-r1" x="0" y="556.8" textLength="671" clip-path="url(#terminal-905765158-line-22)">&#160;&#160;--&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Positional&#160;arguments&#160;separator&#160;</text><text class="terminal-905765158-r2" x="671" y="556.8" textLength="12.2" clip-path="url(#terminal-905765158-line-22)">(</text><text class="terminal-905765158-r1" x="683.2" y="556.8" textLength="134.2" clip-path="url(#terminal-905765158-line-22)">recommended</text><text class="terminal-905765158-r2" x="817.4" y="556.8" textLength="12.2" clip-path="url(#terminal-905765158-line-22)">)</text><text class="terminal-905765158-r1" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-905765158-line-22)">
+</text><text class="terminal-905765158-r1" x="976" y="581.2" textLength="12.2" clip-path="url(#terminal-905765158-line-23)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_example___help.svg b/docs/images/cli_help/cz_example___help.svg
new file mode 100644
index 0000000000..9fe4fd659a
--- /dev/null
+++ b/docs/images/cli_help/cz_example___help.svg
@@ -0,0 +1,87 @@
+<svg class="rich-terminal" viewBox="0 0 994 245.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-1643610534-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-1643610534-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-1643610534-r1 { fill: #c5c8c6 }
+.terminal-1643610534-r2 { fill: #c5c8c6;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-1643610534-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="194.2" />
+    </clipPath>
+    <clipPath id="terminal-1643610534-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1643610534-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1643610534-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1643610534-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1643610534-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1643610534-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1643610534-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="243.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1643610534-clip-terminal)">
+    
+    <g class="terminal-1643610534-matrix">
+    <text class="terminal-1643610534-r1" x="0" y="20" textLength="231.8" clip-path="url(#terminal-1643610534-line-0)">$&#160;cz&#160;example&#160;--help</text><text class="terminal-1643610534-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1643610534-line-0)">
+</text><text class="terminal-1643610534-r1" x="0" y="44.4" textLength="219.6" clip-path="url(#terminal-1643610534-line-1)">usage:&#160;cz&#160;example&#160;</text><text class="terminal-1643610534-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1643610534-line-1)">[</text><text class="terminal-1643610534-r1" x="231.8" y="44.4" textLength="24.4" clip-path="url(#terminal-1643610534-line-1)">-h</text><text class="terminal-1643610534-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1643610534-line-1)">]</text><text class="terminal-1643610534-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1643610534-line-1)">
+</text><text class="terminal-1643610534-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1643610534-line-2)">
+</text><text class="terminal-1643610534-r1" x="0" y="93.2" textLength="231.8" clip-path="url(#terminal-1643610534-line-3)">show&#160;commit&#160;example</text><text class="terminal-1643610534-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1643610534-line-3)">
+</text><text class="terminal-1643610534-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1643610534-line-4)">
+</text><text class="terminal-1643610534-r1" x="0" y="142" textLength="97.6" clip-path="url(#terminal-1643610534-line-5)">options:</text><text class="terminal-1643610534-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1643610534-line-5)">
+</text><text class="terminal-1643610534-r1" x="0" y="166.4" textLength="549" clip-path="url(#terminal-1643610534-line-6)">&#160;&#160;-h,&#160;--help&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1643610534-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1643610534-line-6)">
+</text><text class="terminal-1643610534-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1643610534-line-7)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_info___help.svg b/docs/images/cli_help/cz_info___help.svg
new file mode 100644
index 0000000000..b8827e34c2
--- /dev/null
+++ b/docs/images/cli_help/cz_info___help.svg
@@ -0,0 +1,87 @@
+<svg class="rich-terminal" viewBox="0 0 994 245.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-4196041424-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-4196041424-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-4196041424-r1 { fill: #c5c8c6 }
+.terminal-4196041424-r2 { fill: #c5c8c6;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-4196041424-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="194.2" />
+    </clipPath>
+    <clipPath id="terminal-4196041424-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4196041424-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4196041424-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4196041424-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4196041424-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4196041424-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4196041424-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="243.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-4196041424-clip-terminal)">
+    
+    <g class="terminal-4196041424-matrix">
+    <text class="terminal-4196041424-r1" x="0" y="20" textLength="195.2" clip-path="url(#terminal-4196041424-line-0)">$&#160;cz&#160;info&#160;--help</text><text class="terminal-4196041424-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-4196041424-line-0)">
+</text><text class="terminal-4196041424-r1" x="0" y="44.4" textLength="183" clip-path="url(#terminal-4196041424-line-1)">usage:&#160;cz&#160;info&#160;</text><text class="terminal-4196041424-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-4196041424-line-1)">[</text><text class="terminal-4196041424-r1" x="195.2" y="44.4" textLength="24.4" clip-path="url(#terminal-4196041424-line-1)">-h</text><text class="terminal-4196041424-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-4196041424-line-1)">]</text><text class="terminal-4196041424-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-4196041424-line-1)">
+</text><text class="terminal-4196041424-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-4196041424-line-2)">
+</text><text class="terminal-4196041424-r1" x="0" y="93.2" textLength="353.8" clip-path="url(#terminal-4196041424-line-3)">show&#160;information&#160;about&#160;the&#160;cz</text><text class="terminal-4196041424-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-4196041424-line-3)">
+</text><text class="terminal-4196041424-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-4196041424-line-4)">
+</text><text class="terminal-4196041424-r1" x="0" y="142" textLength="97.6" clip-path="url(#terminal-4196041424-line-5)">options:</text><text class="terminal-4196041424-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-4196041424-line-5)">
+</text><text class="terminal-4196041424-r1" x="0" y="166.4" textLength="549" clip-path="url(#terminal-4196041424-line-6)">&#160;&#160;-h,&#160;--help&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-4196041424-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-4196041424-line-6)">
+</text><text class="terminal-4196041424-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-4196041424-line-7)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_init___help.svg b/docs/images/cli_help/cz_init___help.svg
new file mode 100644
index 0000000000..41a950ebdb
--- /dev/null
+++ b/docs/images/cli_help/cz_init___help.svg
@@ -0,0 +1,87 @@
+<svg class="rich-terminal" viewBox="0 0 994 245.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-1838121835-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-1838121835-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-1838121835-r1 { fill: #c5c8c6 }
+.terminal-1838121835-r2 { fill: #c5c8c6;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-1838121835-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="194.2" />
+    </clipPath>
+    <clipPath id="terminal-1838121835-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1838121835-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1838121835-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1838121835-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1838121835-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1838121835-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1838121835-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="243.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1838121835-clip-terminal)">
+    
+    <g class="terminal-1838121835-matrix">
+    <text class="terminal-1838121835-r1" x="0" y="20" textLength="195.2" clip-path="url(#terminal-1838121835-line-0)">$&#160;cz&#160;init&#160;--help</text><text class="terminal-1838121835-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1838121835-line-0)">
+</text><text class="terminal-1838121835-r1" x="0" y="44.4" textLength="183" clip-path="url(#terminal-1838121835-line-1)">usage:&#160;cz&#160;init&#160;</text><text class="terminal-1838121835-r2" x="183" y="44.4" textLength="12.2" clip-path="url(#terminal-1838121835-line-1)">[</text><text class="terminal-1838121835-r1" x="195.2" y="44.4" textLength="24.4" clip-path="url(#terminal-1838121835-line-1)">-h</text><text class="terminal-1838121835-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1838121835-line-1)">]</text><text class="terminal-1838121835-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1838121835-line-1)">
+</text><text class="terminal-1838121835-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1838121835-line-2)">
+</text><text class="terminal-1838121835-r1" x="0" y="93.2" textLength="353.8" clip-path="url(#terminal-1838121835-line-3)">init&#160;commitizen&#160;configuration</text><text class="terminal-1838121835-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1838121835-line-3)">
+</text><text class="terminal-1838121835-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1838121835-line-4)">
+</text><text class="terminal-1838121835-r1" x="0" y="142" textLength="97.6" clip-path="url(#terminal-1838121835-line-5)">options:</text><text class="terminal-1838121835-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1838121835-line-5)">
+</text><text class="terminal-1838121835-r1" x="0" y="166.4" textLength="549" clip-path="url(#terminal-1838121835-line-6)">&#160;&#160;-h,&#160;--help&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1838121835-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1838121835-line-6)">
+</text><text class="terminal-1838121835-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1838121835-line-7)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_ls___help.svg b/docs/images/cli_help/cz_ls___help.svg
new file mode 100644
index 0000000000..3ec3532ef1
--- /dev/null
+++ b/docs/images/cli_help/cz_ls___help.svg
@@ -0,0 +1,87 @@
+<svg class="rich-terminal" viewBox="0 0 994 245.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-589791338-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-589791338-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-589791338-r1 { fill: #c5c8c6 }
+.terminal-589791338-r2 { fill: #c5c8c6;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-589791338-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="194.2" />
+    </clipPath>
+    <clipPath id="terminal-589791338-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-589791338-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-589791338-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-589791338-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-589791338-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-589791338-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-589791338-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="243.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-589791338-clip-terminal)">
+    
+    <g class="terminal-589791338-matrix">
+    <text class="terminal-589791338-r1" x="0" y="20" textLength="170.8" clip-path="url(#terminal-589791338-line-0)">$&#160;cz&#160;ls&#160;--help</text><text class="terminal-589791338-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-589791338-line-0)">
+</text><text class="terminal-589791338-r1" x="0" y="44.4" textLength="158.6" clip-path="url(#terminal-589791338-line-1)">usage:&#160;cz&#160;ls&#160;</text><text class="terminal-589791338-r2" x="158.6" y="44.4" textLength="12.2" clip-path="url(#terminal-589791338-line-1)">[</text><text class="terminal-589791338-r1" x="170.8" y="44.4" textLength="24.4" clip-path="url(#terminal-589791338-line-1)">-h</text><text class="terminal-589791338-r2" x="195.2" y="44.4" textLength="12.2" clip-path="url(#terminal-589791338-line-1)">]</text><text class="terminal-589791338-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-589791338-line-1)">
+</text><text class="terminal-589791338-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-589791338-line-2)">
+</text><text class="terminal-589791338-r1" x="0" y="93.2" textLength="317.2" clip-path="url(#terminal-589791338-line-3)">show&#160;available&#160;commitizens</text><text class="terminal-589791338-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-589791338-line-3)">
+</text><text class="terminal-589791338-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-589791338-line-4)">
+</text><text class="terminal-589791338-r1" x="0" y="142" textLength="97.6" clip-path="url(#terminal-589791338-line-5)">options:</text><text class="terminal-589791338-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-589791338-line-5)">
+</text><text class="terminal-589791338-r1" x="0" y="166.4" textLength="549" clip-path="url(#terminal-589791338-line-6)">&#160;&#160;-h,&#160;--help&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-589791338-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-589791338-line-6)">
+</text><text class="terminal-589791338-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-589791338-line-7)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_schema___help.svg b/docs/images/cli_help/cz_schema___help.svg
new file mode 100644
index 0000000000..afe8982e9a
--- /dev/null
+++ b/docs/images/cli_help/cz_schema___help.svg
@@ -0,0 +1,87 @@
+<svg class="rich-terminal" viewBox="0 0 994 245.2" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-1497071669-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-1497071669-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-1497071669-r1 { fill: #c5c8c6 }
+.terminal-1497071669-r2 { fill: #c5c8c6;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-1497071669-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="194.2" />
+    </clipPath>
+    <clipPath id="terminal-1497071669-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1497071669-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1497071669-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1497071669-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1497071669-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1497071669-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-1497071669-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="243.2" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-1497071669-clip-terminal)">
+    
+    <g class="terminal-1497071669-matrix">
+    <text class="terminal-1497071669-r1" x="0" y="20" textLength="219.6" clip-path="url(#terminal-1497071669-line-0)">$&#160;cz&#160;schema&#160;--help</text><text class="terminal-1497071669-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1497071669-line-0)">
+</text><text class="terminal-1497071669-r1" x="0" y="44.4" textLength="207.4" clip-path="url(#terminal-1497071669-line-1)">usage:&#160;cz&#160;schema&#160;</text><text class="terminal-1497071669-r2" x="207.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1497071669-line-1)">[</text><text class="terminal-1497071669-r1" x="219.6" y="44.4" textLength="24.4" clip-path="url(#terminal-1497071669-line-1)">-h</text><text class="terminal-1497071669-r2" x="244" y="44.4" textLength="12.2" clip-path="url(#terminal-1497071669-line-1)">]</text><text class="terminal-1497071669-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1497071669-line-1)">
+</text><text class="terminal-1497071669-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1497071669-line-2)">
+</text><text class="terminal-1497071669-r1" x="0" y="93.2" textLength="219.6" clip-path="url(#terminal-1497071669-line-3)">show&#160;commit&#160;schema</text><text class="terminal-1497071669-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1497071669-line-3)">
+</text><text class="terminal-1497071669-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1497071669-line-4)">
+</text><text class="terminal-1497071669-r1" x="0" y="142" textLength="97.6" clip-path="url(#terminal-1497071669-line-5)">options:</text><text class="terminal-1497071669-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1497071669-line-5)">
+</text><text class="terminal-1497071669-r1" x="0" y="166.4" textLength="549" clip-path="url(#terminal-1497071669-line-6)">&#160;&#160;-h,&#160;--help&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1497071669-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1497071669-line-6)">
+</text><text class="terminal-1497071669-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1497071669-line-7)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/cli_help/cz_version___help.svg b/docs/images/cli_help/cz_version___help.svg
new file mode 100644
index 0000000000..c7777db4df
--- /dev/null
+++ b/docs/images/cli_help/cz_version___help.svg
@@ -0,0 +1,111 @@
+<svg class="rich-terminal" viewBox="0 0 994 391.59999999999997" xmlns="http://www.w3.org/2000/svg">
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-4023877003-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-4023877003-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-4023877003-r1 { fill: #c5c8c6 }
+.terminal-4023877003-r2 { fill: #c5c8c6;font-weight: bold }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-4023877003-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="340.59999999999997" />
+    </clipPath>
+    <clipPath id="terminal-4023877003-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-4023877003-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="389.6" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" clip-path="url(#terminal-4023877003-clip-terminal)">
+    
+    <g class="terminal-4023877003-matrix">
+    <text class="terminal-4023877003-r1" x="0" y="20" textLength="231.8" clip-path="url(#terminal-4023877003-line-0)">$&#160;cz&#160;version&#160;--help</text><text class="terminal-4023877003-r1" x="976" y="20" textLength="12.2" clip-path="url(#terminal-4023877003-line-0)">
+</text><text class="terminal-4023877003-r1" x="0" y="44.4" textLength="219.6" clip-path="url(#terminal-4023877003-line-1)">usage:&#160;cz&#160;version&#160;</text><text class="terminal-4023877003-r2" x="219.6" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">[</text><text class="terminal-4023877003-r1" x="231.8" y="44.4" textLength="24.4" clip-path="url(#terminal-4023877003-line-1)">-h</text><text class="terminal-4023877003-r2" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">]</text><text class="terminal-4023877003-r2" x="280.6" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">[</text><text class="terminal-4023877003-r1" x="292.8" y="44.4" textLength="207.4" clip-path="url(#terminal-4023877003-line-1)">-r&#160;|&#160;-p&#160;|&#160;-c&#160;|&#160;-v</text><text class="terminal-4023877003-r2" x="500.2" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">]</text><text class="terminal-4023877003-r1" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-1)">
+</text><text class="terminal-4023877003-r1" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-4023877003-line-2)">
+</text><text class="terminal-4023877003-r1" x="0" y="93.2" textLength="817.4" clip-path="url(#terminal-4023877003-line-3)">get&#160;the&#160;version&#160;of&#160;the&#160;installed&#160;commitizen&#160;or&#160;the&#160;current&#160;project&#160;</text><text class="terminal-4023877003-r2" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-3)">(</text><text class="terminal-4023877003-r1" x="829.6" y="93.2" textLength="97.6" clip-path="url(#terminal-4023877003-line-3)">default:</text><text class="terminal-4023877003-r1" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-3)">
+</text><text class="terminal-4023877003-r1" x="0" y="117.6" textLength="244" clip-path="url(#terminal-4023877003-line-4)">installed&#160;commitizen</text><text class="terminal-4023877003-r2" x="244" y="117.6" textLength="12.2" clip-path="url(#terminal-4023877003-line-4)">)</text><text class="terminal-4023877003-r1" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-4023877003-line-4)">
+</text><text class="terminal-4023877003-r1" x="976" y="142" textLength="12.2" clip-path="url(#terminal-4023877003-line-5)">
+</text><text class="terminal-4023877003-r1" x="0" y="166.4" textLength="97.6" clip-path="url(#terminal-4023877003-line-6)">options:</text><text class="terminal-4023877003-r1" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-6)">
+</text><text class="terminal-4023877003-r1" x="0" y="190.8" textLength="622.2" clip-path="url(#terminal-4023877003-line-7)">&#160;&#160;-h,&#160;--help&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-4023877003-r1" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-4023877003-line-7)">
+</text><text class="terminal-4023877003-r1" x="0" y="215.2" textLength="744.2" clip-path="url(#terminal-4023877003-line-8)">&#160;&#160;-r,&#160;--report&#160;&#160;&#160;&#160;&#160;&#160;get&#160;system&#160;information&#160;for&#160;reporting&#160;bugs</text><text class="terminal-4023877003-r1" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-8)">
+</text><text class="terminal-4023877003-r1" x="0" y="239.6" textLength="707.6" clip-path="url(#terminal-4023877003-line-9)">&#160;&#160;-p,&#160;--project&#160;&#160;&#160;&#160;&#160;get&#160;the&#160;version&#160;of&#160;the&#160;current&#160;project</text><text class="terminal-4023877003-r1" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-4023877003-line-9)">
+</text><text class="terminal-4023877003-r1" x="0" y="264" textLength="768.6" clip-path="url(#terminal-4023877003-line-10)">&#160;&#160;-c,&#160;--commitizen&#160;&#160;get&#160;the&#160;version&#160;of&#160;the&#160;installed&#160;commitizen</text><text class="terminal-4023877003-r1" x="976" y="264" textLength="12.2" clip-path="url(#terminal-4023877003-line-10)">
+</text><text class="terminal-4023877003-r1" x="0" y="288.4" textLength="927.2" clip-path="url(#terminal-4023877003-line-11)">&#160;&#160;-v,&#160;--verbose&#160;&#160;&#160;&#160;&#160;get&#160;the&#160;version&#160;of&#160;both&#160;the&#160;installed&#160;commitizen&#160;and&#160;the</text><text class="terminal-4023877003-r1" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-4023877003-line-11)">
+</text><text class="terminal-4023877003-r1" x="0" y="312.8" textLength="427" clip-path="url(#terminal-4023877003-line-12)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;current&#160;project</text><text class="terminal-4023877003-r1" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-4023877003-line-12)">
+</text><text class="terminal-4023877003-r1" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-4023877003-line-13)">
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/docs/images/commit.yml b/docs/images/commit.yml
index a239bc3aab..fd10ed777f 100644
--- a/docs/images/commit.yml
+++ b/docs/images/commit.yml
@@ -136,9 +136,9 @@ records:
   - delay: 209
     content: "\e[?1l\e[6n"
   - delay: 7
-    content: "\e[?2004h\e[?25l\e[0m\e[?7l\e[0m\e[J\e[0;38;5;67m?\e[0;1m Select the type of change you are committing \e[0m (Use arrow keys)                                    \r\e[100C \e[0m\r\r\n\e[0m » fix: A bug fix. Correlates with PATCH in SemVer\e[0m\r\r\n\e[0m   feat: A new feature. Correlates with MINOR in SemVer\e[0m\r\r\n\e[0m   docs: Documentation only changes\e[0m\r\r\n\e[0m   style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-\r\e[100Cc\e[0m\r\r\n\e[0m   refactor: A code change that neither fixes a bug nor adds a feature\e[0m\r\r\n\e[0m   perf: A code change that improves performance\e[0m\r\r\n\e[0m   test: Adding missing or correcting existing tests\e[0m\r\r\n\e[0m   build: Changes that affect the build system or external dependencies (example scopes: pip, docker\r\e[100C,\e[0m\r\r\n\e[0m   ci: Changes to our CI configuration files and scripts (example scopes: GitLabCI)                 \r\e[100C \r\e[9A\e[64C\e[?7h\e[0m\e[?12l\e[?25h"
+    content: "\e[?2004h\e[?25l\e[0m\e[?7l\e[0m\e[J\e[0;38;5;67m?\e[0;1m Select the type of change you are committing \e[0m (Use arrow keys)                                    \r\e[100C \e[0m\r\r\n\e[0m » fix: A bug fix. Correlates with PATCH in SemVer\e[0m\r\r\n\e[0m   feat: A new feature. Correlates with MINOR in SemVer\e[0m\r\r\n\e[0m   docs: Documentation only changes\e[0m\r\r\n\e[0m   style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-\r\e[100Cc\e[0m\r\r\n\e[0m   refactor: A code change that neither fixes a bug nor adds a feature\e[0m\r\r\n\e[0m   perf: A code change that improves performance\e[0m\r\r\n\e[0m   test: Adding missing or correcting existing tests\e[0m\r\r\n\e[0m   build: Changes that affect the build system or external dependencies (example scopes: pip, docker\r\e[100C,\e[0m\r\r\n\e[0m   ci: Changes to CI configuration files and scripts (example scopes: GitLabCI)                 \r\e[100C \r\e[9A\e[64C\e[?7h\e[0m\e[?12l\e[?25h"
   - delay: 17
-    content: "\e[?25l\e[?7l\e[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\e[0m   ci: Changes to our CI configuration files and scripts (example scopes: GitLabCI)\e[0m\e[K\e[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\e[0m                                                                                                    \r\e[100C \r\e[19A\e[64C\e[?7h\e[0m\e[?12l\e[?25h"
+    content: "\e[?25l\e[?7l\e[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\e[0m   ci: Changes to CI configuration files and scripts (example scopes: GitLabCI)\e[0m\e[K\e[0m\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\r\r\n\e[0m                                                                                                    \r\e[100C \r\e[19A\e[64C\e[?7h\e[0m\e[?12l\e[?25h"
   - delay: 647
     content: "\e[?25l\e[?7l\e[0m\r\r\n\e[0m   \e[0m\r\r\n\e[0m » \e[2A\e[61C\e[?7h\e[0m\e[?12l\e[?25h"
   - delay: 574
diff --git a/docs/images/demo.gif b/docs/images/demo.gif
index 0f2b9f2487..39dcdc9e91 100644
Binary files a/docs/images/demo.gif and b/docs/images/demo.gif differ
diff --git a/docs/images/init.gif b/docs/images/init.gif
new file mode 100644
index 0000000000..f2cdf6b310
Binary files /dev/null and b/docs/images/init.gif differ
diff --git a/docs/init.md b/docs/init.md
deleted file mode 100644
index f818dac62f..0000000000
--- a/docs/init.md
+++ /dev/null
@@ -1,18 +0,0 @@
-To start using commitizen, the recommended approach is to run
-
-```sh
-cz init
-```
-
-This command will ask you for information about the project and will
-configure the selected file type (`pyproject.toml`, `.cz.toml`, etc.).
-
-The `init` will help you with
-
-1. Choose a convention rules (`name`)
-2. Choosing a version provider (`commitizen` or for example `Cargo.toml`)
-3. Detecting your project's version
-4. Detecting the tag format used
-5. Choosing a version type (`semver` or `pep440`)
-6. Whether to create the changelog automatically or not during bump
-7. Whether you want to keep the major as zero while building alpha software.
diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md
index 671cdc0fd1..0c35a31557 100644
--- a/docs/third-party-commitizen.md
+++ b/docs/third-party-commitizen.md
@@ -3,13 +3,29 @@
 In addition to the native templates, some alternative commit format templates
 are available as PyPI packages (installable with `pip`).
 
+### [cz-ai](https://github.com/watadarkstar/cz_ai)
+
+A Commitizen plugin that leverages OpenAI's GPT-4o to automatically generate clear, concise, and conventional commit messages based on your staged git changes.
+
+#### Installation
+
+```sh
+pip install cz-ai
+```
+
+#### Usage
+
+```sh
+cz --name cz_ai commit
+```
+
 ### [Conventional JIRA](https://pypi.org/project/conventional-JIRA/)
 
-Just like *conventional commit* format, but the scope has been restricted to a
+Just like _conventional commit_ format, but the scope has been restricted to a
 JIRA issue format, i.e. `project-issueNumber`. This standardises scopes in a
 meaningful way.
 
-### Installation
+#### Installation
 
 ```sh
 pip install conventional-JIRA
@@ -17,52 +33,168 @@ pip install conventional-JIRA
 
 ### [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/)
 
-This plugin extends the commitizen tools by:
-- requiring a JIRA issue id in the commit message
+This plugin extends the Commitizen tools by:
+
+- requiring a JIRA issue ID in the commit message
 - creating links to GitHub commits in the CHANGELOG.md
 - creating links to JIRA issues in the CHANGELOG.md
 
-### Installation
+#### Installation
 
 ```sh
 pip install cz-github-jira-conventional
 ```
 
-For installation instructions (configuration and pre-commit) please visit https://github.com/apheris/cz-github-jira-conventional
+For installation instructions (configuration and pre-commit) please visit [https://github.com/apheris/cz-github-jira-conventional](https://github.com/apheris/cz-github-jira-conventional)
 
 ### [cz-emoji](https://github.com/adam-grant-hendry/cz-emoji)
 
-*conventional commit* format, but with emojis
+_conventional commit_ format, but with emojis
 
-### Installation
+#### Installation
 
 ```sh
 pip install cz-emoji
 ```
 
-### Usage
+#### Usage
 
 ```sh
 cz --name cz_emoji commit
 ```
 
+### [cz-conventional-gitmoji](https://github.com/ljnsn/cz-conventional-gitmoji)
+
+*conventional commit*s, but with [gitmojis](https://gitmoji.dev).
+
+Includes a pre-commit hook that automatically adds the correct gitmoji to the commit message based on the conventional type.
+
+#### Installation
+
+```sh
+pip install cz-conventional-gitmoji
+```
+
+#### Usage
+
+```sh
+cz --name cz_gitmoji commit
+```
 
 ### [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) (Unmaintained)
 
-Just like *conventional commit* format, but with emojis and optionally time spent and related tasks.
+Just like _conventional commit_ format, but with emojis and optionally time spent and related tasks.
+
+#### Installation
 
-It can be installed with `pip install commitizen-emoji`.
+```sh
+pip install commitizen-emoji
+```
 
-Usage: `cz --name cz_commitizen_emoji commit`.
+#### Usage
+
+```sh
+cz --name cz_commitizen_emoji commit
+```
 
 ### [Conventional Legacy (cz_legacy)][1]
 
-An extension of the *conventional commit* format to include user-specified
+An extension of the _conventional commit_ format to include user-specified
 legacy change types in the `CHANGELOG` while preventing the legacy change types
 from being used in new commit messages
 
-`cz_legacy` can be installed with `pip install cz_legacy`
+#### Installation
+
+```sh
+pip install cz_legacy
+```
+
+#### Usage
 
 See the [README][1] for instructions on configuration
 
-  [1]: https://pypi.org/project/cz_legacy
+[1]: https://pypi.org/project/cz_legacy
+
+## Third-Party Commitizen Providers
+
+Commitizen can read and write version from different sources. In addition to the native providers, some alternative version sources are available as PyPI packages (installable with `pip`).
+
+### [commitizen-deno-provider](https://pypi.org/project/commitizen-deno-provider/)
+
+A provider for **Deno** projects. The provider updates the version in deno.json and jsr.json files.
+
+#### Installation
+
+```sh
+pip install commitizen-deno-provider
+```
+
+#### Usage
+
+Add `deno-provider` to your configuration file.
+
+Example for `.cz.yaml`:
+
+```yaml
+---
+commitizen:
+  major_version_zero: true
+  name: cz_conventional_commits
+  tag_format: $version
+  update_changelog_on_bump: true
+  version_provider: deno-provider
+  version_scheme: semver
+```
+
+### [cz-path](https://pypi.org/project/cz-path/)
+
+Provides prefix choices for commit messages based on staged files (Git only).
+For example, if the staged files are `component/z/a.ts` and `component/z/b.ts`,
+the path prefix option will be `component/z` and commit message might look like:
+`component/z/: description of changes`. If only one file is staged, the extension
+is removed in the prefix.
+
+#### Installation
+
+```sh
+pip install cz-path
+```
+
+#### Usage
+
+Add `cz-path` to your configuration file.
+
+Example for `.cz.json`:
+
+```json
+{
+  "commitizen": {
+    "name": "cz_path",
+    "remove_path_prefixes": ["src", "module_name"]
+  }
+}
+```
+
+The default value for `remove_path_prefixes` is `["src"]`. Adding `/` to the
+prefixes is not required.
+
+#### Example session
+
+```plain
+ $ git add .vscode/
+ $ cz -n cz_path c
+? Prefix: (Use arrow keys)
+ » .vscode
+   .vscode/
+   project
+   (empty)
+? Prefix: .vscode
+? Commit title: adjust settings
+
+.vscode: adjust settings
+
+[main 0000000] .vscode: adjust settings
+ 2 files changed, 1 insertion(+), 11 deletions(-)
+
+Commit successful!
+```
diff --git a/docs/tutorials/auto_check.md b/docs/tutorials/auto_check.md
index ede8759e68..d143528767 100644
--- a/docs/tutorials/auto_check.md
+++ b/docs/tutorials/auto_check.md
@@ -6,9 +6,9 @@ To automatically check a commit message prior to committing, you can use a [git
 
 ## How to
 
-There are two common methods for installing the hook:
+There are two common methods for installing the hooks:
 
-### Method 1: Add git hook through [pre-commit](https://pre-commit.com/)
+### Method 1: Add a git hook through [pre-commit](https://pre-commit.com/)
 
 - Step 1: Install [pre-commit](https://pre-commit.com/)
 
@@ -16,7 +16,7 @@ There are two common methods for installing the hook:
 python -m pip install pre-commit
 ```
 
-- Step 2: Create `.pre-commit-config.yaml` at your root directory with the following content
+- Step 2: Create `.pre-commit-config.yaml` in your root directory with the following content
 
 ```yaml
 ---
@@ -25,21 +25,22 @@ repos:
     rev: v1.17.0
     hooks:
       - id: commitizen
+        stages: [commit-msg]
 ```
 
-- Step 3: Install the configuration into git hook through `pre-commit`
+- Step 3: Install the configuration into the git hook through `pre-commit`
 
 ```bash
 pre-commit install --hook-type commit-msg
 ```
 
-### Method 2: Manually add git hook
+### Method 2: Manually add a git hook
 
-The command might be included inside of a Git hook (inside of `.git/hooks/` at the root of the project).
+The command might be included inside a Git hook (inside `.git/hooks/` at the root of the project).
 
 The selected hook might be the file called commit-msg.
 
-This example shows how to use the check command inside of commit-msg.
+This example shows how to use the check command inside commit-msg.
 
 At the root of the project:
 
@@ -61,7 +62,7 @@ Where `$1` is the name of the temporary file that contains the current commit me
 
 The `--commit-msg-file` flag is required, not optional.
 
-Each time you create a commit, automatically, this hook will analyze it.
-If the commit message is invalid, it'll be rejected.
+Each time you create a commit, this hook will automatically analyze it.
+If the commit message is invalid, it will be rejected.
 
 The commit should follow the given committing rules; otherwise, it won't be accepted.
diff --git a/docs/tutorials/auto_prepare_commit_message.md b/docs/tutorials/auto_prepare_commit_message.md
index 80a1b211d7..84ac62b689 100644
--- a/docs/tutorials/auto_prepare_commit_message.md
+++ b/docs/tutorials/auto_prepare_commit_message.md
@@ -2,12 +2,12 @@
 
 ## About
 
-It can be desirable to use commitizen for all types of commits (i.e. regular, merge,
+It can be desirable to use Commitizen for all types of commits (i.e. regular, merge,
 squash) so that the complete git history adheres to the commit message convention
 without ever having to call `cz commit`.
 
 To automatically prepare a commit message prior to committing, you can
-use a [prepare-commit-msg Git hook](prepare-commit-msg-docs):
+use a [prepare-commit-msg Git hook][prepare-commit-msg-docs]:
 
 > This hook is invoked by git-commit right after preparing the
 > default log message, and before the editor is started.
@@ -18,14 +18,14 @@ To automatically perform arbitrary cleanup steps after a successful commit you c
 > This hook is invoked by git-commit. It takes no parameters, and is invoked after a
 > commit is made.
 
-A combination of these two hooks allows for enforcing the usage of commitizen so that
-whenever a commit is about to be created, commitizen is used for creating the commit
+A combination of these two hooks allows for enforcing the usage of Commitizen so that
+whenever a commit is about to be created, Commitizen is used for creating the commit
 message. Running `git commit` or `git commit -m "..."` for example, would trigger
-commitizen and use the generated commit message for the commit.
+Commitizen and use the generated commit message for the commit.
 
 ## Installation
 
-Copy the hooks from [here](https://github.com/commitizen-tools/hooks) into the `.git/hooks` folder and make them
+Copy the hooks from [here](https://github.com/commitizen-tools/commitizen/tree/master/hooks) into the `.git/hooks` folder and make them
   executable by running the following commands from the root of your Git repository:
 
 ```bash
diff --git a/docs/tutorials/dev_releases.md b/docs/tutorials/dev_releases.md
index 8142334754..e2b29fb191 100644
--- a/docs/tutorials/dev_releases.md
+++ b/docs/tutorials/dev_releases.md
@@ -5,9 +5,7 @@
 To make use of a `.dev` suffix, as per
 [PEP440](https://peps.python.org/pep-0440/#developmental-releases).
 
-If more than one active branch attempts to create a tag, relative to the main
-branch, there is the possibility that each will attempt to create the _same_
-tag, resulting in a collision.
+If multiple active branches attempt to create a tag relative to the main branch, there is a possibility that they will attempt to create the _same_ tag, resulting in a collision.
 
 Developmental releases aim to avoid this by including a `.dev` segment which
 includes a non-negative integer unique to that workflow:
@@ -19,9 +17,7 @@ X.Y.devN
 !!! note
     As noted in
     [PEP440](https://peps.python.org/pep-0440/#developmental-releases),
-    although developmental releases are useful in avoiding the situation
-    described above, depending on the value passed as the developmental
-    release, they can be _"difficult to parse for human readers"_.
+    while developmental releases help avoid the situation described above, they can be _"difficult to parse for human readers"_ depending on the value passed as the developmental release.
 
 ## How to
 
@@ -64,8 +60,7 @@ Equally, as the developmental release needs only a non-negative integer, it is
 possible to use the Unix time (i.e. the number of seconds since 1st January
 1970 UTC).
 
-This would create the possibility of a collision if two builds occur at
-precisely the same second but this may be sufficient for many cases:
+This approach could potentially create a collision if two builds occur at precisely the same second, but it may be sufficient for many use cases:
 
 ```sh
 --devrelease $(date +%s)
diff --git a/docs/tutorials/github_actions.md b/docs/tutorials/github_actions.md
index 3cb92725a5..2cb58cfee6 100644
--- a/docs/tutorials/github_actions.md
+++ b/docs/tutorials/github_actions.md
@@ -1,4 +1,4 @@
-## Create a new release with Github Actions
+## Create a new release with GitHub Actions
 
 ### Automatic bumping of version
 
@@ -41,17 +41,17 @@ jobs:
 
 Push to master and that's it.
 
-### Creating a github release
+### Creating a GitHub release
 
 You can modify the previous action.
 
 Add the variable `changelog_increment_filename` in the `commitizen-action`, specifying
 where to output the content of the changelog for the newly created version.
 
-And then add a step using a github action to create the release: `softprops/action-gh-release`
+And then add a step using a GitHub action to create the release: `softprops/action-gh-release`
 
-The commitizen action creates an env variable called `REVISION`, containing the
-newely created version.
+Commitizen action creates an env variable called `REVISION`, containing the
+newly created version.
 
 ```yaml
 - name: Create bump and changelog
@@ -72,7 +72,7 @@ newely created version.
 
 Once the new tag is created, triggering an automatic publish command would be desired.
 
-In order to do so, the crendetial needs to be added with the information of our PyPI account.
+In order to do so, the credential needs to be added with the information of our PyPI account.
 
 Instead of using username and password, we suggest using [api token](https://pypi.org/help/#apitoken) generated from PyPI.
 
@@ -119,7 +119,7 @@ jobs:
           ./scripts/publish
 ```
 
-Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and the publish with your tools (twine, poetry, etc.). Check [commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish)
+Notice that we are using poetry, and we are calling a bash script in `./scripts/publish`. You should configure the action, and publish with your tools (twine, poetry, etc.). Check [Commitizen example](https://github.com/commitizen-tools/commitizen/blob/master/scripts/publish)
 You can also use [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) to publish your package.
 
 Push the changes and that's it.
diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md
index 7b6e2032df..6f6d53a57e 100644
--- a/docs/tutorials/gitlab_ci.md
+++ b/docs/tutorials/gitlab_ci.md
@@ -10,11 +10,11 @@ _Goal_: Bump a new version every time that a change occurs on the `master` branc
 2. A developer creates a merge request (MR) against `master` branch
 3. When the `MR` is merged into master, the 2 stages of the CI are executed
 4. For simplification, we store the software version in a file called `VERSION`. You can use any file that you want as `commitizen` supports it.
-5. The commit message executed automatically by the `CI` must include `[skip-ci]` in the message; otherwise, the process will generate a loop. You can define the message structure in [commitizen](../bump.md) as well.
+5. The commit message executed automatically by the `CI` must include `[skip-ci]` in the message; otherwise, the process will generate a loop. You can define the message structure in [commitizen](../commands/bump.md) as well.
 
-### Gitlab Configuration
+### GitLab Configuration
 
-To be able to change files and push new changes with `Gitlab CI` runners, we need to have a `ssh` key and configure a git user.
+To be able to change files and push new changes with `GitLab CI` runners, we need to have a `ssh` key and configure a git user.
 
 First, let's create a `ssh key`. The only requirement is to create it without a passphrase:
 
@@ -38,7 +38,7 @@ The latest step is to create a `deploy key.` To do this, we should create it und
 
 If you have more projects under the same organization, you can reuse the deploy key created before, but you will have to repeat the step where we have created the environment variables (ssh key, email, and username).
 
-tip: If the CI raise some errors, try to unprotected the private key.
+Tip: If the CI raise some errors, try to unprotect the private key.
 
 ### Defining GitLab CI Pipeline
 
@@ -74,12 +74,12 @@ test:
 
 auto-bump:
   stage: auto-bump
-  image: python:3.8
+  image: python:3.9
   before_script:
     - "which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy )"
     - eval `ssh-agent -s`
     - echo "${SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - > /dev/null # add ssh key
-    - pip3 install -U Commitizen # install commitizen
+    - pip3 install -U commitizen # install commitizen
     - mkdir -p ~/.ssh
     - chmod 700 ~/.ssh
     - echo "$SSH_PUBLIC_KEY" >> ~/.ssh/id_rsa.pub
@@ -105,9 +105,9 @@ auto-bump:
       - variables
 ```
 
-So, every time that a developer push to any branch, the `test` job is executed. If the branch is `master` and the test jobs success, the `auto-bump` takes place.
-To be able to push using the Gitlab runner, we have to set the ssh key, configure git, and finally execute the auto bump.
+So, every time that a developer pushes to any branch, the `test` job is executed. If the branch is `master` and the test jobs succeed, the `auto-bump` takes place.
+To be able to push using the GitLab runner, we have to set the SSH key, configure git, and finally execute the auto bump.
 
-After merging the new changed into master, we have the final result:
+After merging the new changes into master, we have the final result:
 
 ![gitlab final ci result](../images/gitlab_ci/gitlab_final_ci_result.png)
diff --git a/docs/tutorials/jenkins_pipeline.md b/docs/tutorials/jenkins_pipeline.md
index fb87820c4c..2b9ad173d3 100644
--- a/docs/tutorials/jenkins_pipeline.md
+++ b/docs/tutorials/jenkins_pipeline.md
@@ -47,7 +47,7 @@ def useCz(String authorName = 'Jenkins CI Server', String authorEmail = 'your-je
 ```
 
 !!! warning
-    Using jenkins pipeline with any git plugin may require many different configurations,
+    Using jenkins pipeline with any git plugin may require many configurations,
     you'll have to tinker with it until your pipelines properly detects git events. Check your
     webhook in your git repository and check the "behaviors" and "build strategies" in
     your pipeline settings.
diff --git a/docs/tutorials/monorepo_guidance.md b/docs/tutorials/monorepo_guidance.md
new file mode 100644
index 0000000000..6f15a87247
--- /dev/null
+++ b/docs/tutorials/monorepo_guidance.md
@@ -0,0 +1,81 @@
+# Configuring Commitizen in a monorepo
+
+This tutorial assumes the monorepo layout is designed with multiple components that can be released independently of each
+other, it also assumes that conventional commits with scopes are in use. Some suggested layouts:
+
+```shell-session
+.
+├── library-b
+│   └── .cz.toml
+└── library-z
+    └── .cz.toml
+```
+
+```shell-session
+src
+├── library-b
+│   └── .cz.toml
+└── library-z
+    └── .cz.toml
+```
+
+Sample `.cz.toml` for each component:
+
+```toml
+# library-b/.cz.toml
+[tool.commitizen]
+name = "cz_customize"
+version = "0.0.0"
+tag_format = "${version}-library-b" # the component name can be a prefix or suffix with or without a separator
+ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags
+update_changelog_on_bump = true
+```
+
+```toml
+# library-z/.cz.toml
+[tool.commitizen]
+name = "cz_customize"
+version = "0.0.0"
+tag_format = "${version}-library-z"
+ignored_tag_formats = ["${version}-library-*"] # Avoid noise from other tags
+update_changelog_on_bump = true
+```
+
+And finally, to bump each of these:
+
+```sh
+cz --config library-b/.cz.toml bump --yes
+cz --config library-z/.cz.toml bump --yes
+```
+
+
+## Changelog per component
+
+In order to filter the correct commits for each component, you'll have to come up with a strategy.
+
+For example:
+
+- Trigger the pipeline based on the changed path, which can have some downsides, as you'll rely on the developer not including files from other files
+    - [GitHub actions](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore) uses `path`
+    - [Jenkins](https://www.jenkins.io/doc/book/pipeline/syntax/#built-in-conditions) uses `changeset`
+    - [GitLab](https://docs.gitlab.com/ee/ci/yaml/#ruleschanges) uses `rules:changes`
+- Filter certain pattern of the commit message (recommended)
+
+
+### Example with scope in conventional commits
+
+For this example, to include the message in the changelog, we will require commits to use a specific scope.
+This way, only relevant commits will be included in the appropriate change log for a given component, and any other commit will be ignored.
+
+Example config and commit for `library-b`:
+
+```toml
+[tool.commitizen.customize]
+changelog_pattern = "^(feat|fix)\\(library-b\\)(!)?:" #the pattern on types can be a wild card or any types you wish to include
+```
+
+A commit message looking like this, would be included:
+
+```
+fix(library-b): Some awesome message
+```
diff --git a/docs/tutorials/tag_format.md b/docs/tutorials/tag_format.md
new file mode 100644
index 0000000000..8408b4c801
--- /dev/null
+++ b/docs/tutorials/tag_format.md
@@ -0,0 +1,101 @@
+# Managing tag formats
+
+## Tag format and version scheme
+
+For most projects, the tag format is simply the version number which is set like this:
+
+```yaml
+[tool.commitizen]
+tag_format: $version
+version_scheme: pep440
+```
+
+As this is the default value, you don't have to specify it.
+
+This setting means that:
+
+- The tag generated on bump will have this format: `1.0.0` :
+    - the version is generated following PEP440 scheme
+    - the tag is exactly the generated version
+- All tags having this format will be recognized as version tag when:
+    - searching the last while bumping a release
+    - searching previous versions while:
+        - generating incremental changelog
+        - generating a changelog for a version range
+- The changelog versions (section titles) will have this format
+- The `scm` version provider will identify the current version using this tag format
+
+The version format will change depending on your configured version scheme.
+For most, it will only impact pre-releases and [developmental releases](dev_releases.md) formats (i.e. `1.0.0-rc.1` vs. `1.0.0.rc1`)
+
+But you may need a different tagging convention, let's say using `semver` and prefixed with a `v`.
+In this case you will define your settings like this:
+
+```yaml
+[tool.commitizen]
+tag_format: v${version}
+version_scheme: semver
+```
+
+As a result, the tag generated on bump will have this format: `v1.0.0` and the version will be generated following `semver` scheme.
+
+!!! note
+    Both `$version` and `${version}` syntaxes are strictly equivalent. You can use the one you prefer.
+
+See [the `version_scheme` section in `bump` command documentation](../commands/bump.md#version_scheme) for more details on version schemes and how to define your own.
+See [`tag_format`](../config.md#tag_format) and [`version_scheme`](../config.md#version_scheme) settings in [Configuration reference](../config.md) for more details on these settings.
+
+## Changing convention
+
+Now, let's say you need to change the tag format for some reason (company convention, [migration to a monorepo](monorepo_guidance.md)...).
+You will obviously want to keep all those features working as expected.
+
+Commitizen can deal with it as long as you provide the legacy tag format in the configuration.
+
+Using the previous example, let's say you want to move from `v${version}` to `component-${version}`.
+Then `component-${version}` will be the new tag format and `v${version}` the legacy one.
+
+```yaml
+[tool.commitizen]
+tag_format: component-${version}
+legacy_tag_formats:
+ - v${version}
+```
+
+This way, you won't lose your version history, you'll still be able to generate your changelog properly,
+and on the next version bump, your last version in the form `v${version}` will be properly recognized if you use the `scm` version provider.
+Your new tag will be in the form `component-${version}`.
+
+## Known tags to ignore
+
+Now let's say you have some known tags you want to ignore, either because they are not versions, or because they are not versions of the component you are dealing with.
+As a consequence, you don't want them to trigger a warning because Commitizen detected an unknown tag format.
+
+Then you can tell Commitizen about it using the [`ignored_tag_formats`](../config.md#ignored_tag_formats) setting:
+
+```yaml
+[tool.commitizen]
+ignored_tag_formats:
+  - prod
+  - other-component-${version}
+  - prefix-*
+```
+
+This will ignore:
+
+- The `prod` tag
+- Any version tag prefixed with `other-component-`
+- Any tag prefixed with `prefix-`
+
+
+!!! tip
+    Note the `*` in the `prefix-*` pattern. This is a wildcard and only exists for `ignored_tag_formats`.
+    It will match any string from any length. This allows to exclude by prefix, whether it is followed by a version or not.
+
+!!! tip
+    If you don't want to be warned when Commitizen detects an unknown tag, you can do so by setting:
+    ```
+    [tool.commitizen]
+    ignored_tag_formats = ["*"]
+    ```
+    But be aware that you will not be warned if you have a typo in your tag formats.
diff --git a/docs/tutorials/writing_commits.md b/docs/tutorials/writing_commits.md
index 9ba151cc37..7d9139929c 100644
--- a/docs/tutorials/writing_commits.md
+++ b/docs/tutorials/writing_commits.md
@@ -1,21 +1,21 @@
 For this project to work well in your pipeline, a commit convention must be followed.
 
-By default commitizen uses the known [conventional commits][conventional_commits], but
-you can create your own following the docs information over at
+By default, Commitizen uses the known [conventional commits][conventional_commits], but
+you can create your own following the documentation information over at
 [customization][customization].
 
 ## Conventional commits
 
 If you are using [conventional commits][conventional_commits], the most important
 thing to know is that you must begin your commits with at least one of these tags:
-`fix`, `feat`. And if you introduce a breaking change, then, you must
+`fix`, `feat`. And if you introduce a breaking change, then you must
 add to your commit body the following `BREAKING CHANGE`.
-Using these 3 keywords will allow the proper identification of the semantic version.
+Using these three keywords will allow the proper identification of the semantic version.
 Of course, there are other keywords, but I'll leave it to the reader to explore them.
 
 ## Writing commits
 
-Now to the important part, when writing commits, it's important to think about:
+Now to the important part: when writing commits, it's important to think about:
 
 - Your future self
 - Your colleagues
@@ -23,16 +23,16 @@ Now to the important part, when writing commits, it's important to think about:
 You may think this is trivial, but it's not. It's important for the reader to
 understand what happened.
 
-Emojis may be added as well (e.g. see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration].
+Emojis may be added as well (e.g., see [cz-emoji][cz_emoji]), which requires the `utf-8`, or equivalent, character encoding to support unicode characters. By default, `commitizen` uses the `utf-8` character encoding, but a different encoding may be set through the `encoding` [configuration option][configuration].
 
 ### Recommendations
 
 - **Keep the message short**: Makes the list of commits more readable (~50 chars).
 - **Talk imperative**: Follow this rule: `If applied, this commit will <commit message>`
-- **Think about the CHANGELOG**: Your commits will probably end up in the changelog
+- **Think about the CHANGELOG**: Your commits will probably end up in the changelog,
   so try writing for it, but also keep in mind that you can skip sending commits to the
   CHANGELOG by using different keywords (like `build`).
-- **Use a commit per new feature**: if you introduce multiple things related to the same
+- **Use a commit per new feature**: If you introduce multiple things related to the same
   commit, squash them. This is useful for auto-generating CHANGELOG.
 
 | Do's | Don'ts |
diff --git a/hooks/post-commit.py b/hooks/post-commit.py
index c2faebb738..c7dea825bd 100755
--- a/hooks/post-commit.py
+++ b/hooks/post-commit.py
@@ -1,13 +1,15 @@
 #!/usr/bin/env python
-import os
-import tempfile
 from pathlib import Path
 
+try:
+    from commitizen.cz.utils import get_backup_file_path
+except ImportError as error:
+    print(f"could not import commitizen:\n{error}")
+    exit(1)
 
-def post_commit():
-    backup_file = Path(
-        tempfile.gettempdir(), f"cz.commit{os.environ.get('USER', '')}.backup"
-    )
+
+def post_commit() -> None:
+    backup_file = Path(get_backup_file_path())
 
     # remove backup file if it exists
     if backup_file.is_file():
@@ -15,4 +17,5 @@ def post_commit():
 
 
 if __name__ == "__main__":
-    exit(post_commit())
+    post_commit()
+    exit(0)
diff --git a/hooks/prepare-commit-msg.py b/hooks/prepare-commit-msg.py
index 58beb3a0f8..e666fa673b 100755
--- a/hooks/prepare-commit-msg.py
+++ b/hooks/prepare-commit-msg.py
@@ -1,36 +1,31 @@
 #!/usr/bin/env python
-import os
 import shutil
 import subprocess
 import sys
-import tempfile
 from pathlib import Path
 from subprocess import CalledProcessError
 
+try:
+    from commitizen.cz.utils import get_backup_file_path
+except ImportError as error:
+    print("could not import commitizen:")
+    print(error)
+    exit(1)
 
-def prepare_commit_msg(commit_msg_file: Path) -> int:
-    # check that commitizen is installed
-    if shutil.which("cz") is None:
-        print("commitizen is not installed!")
-        return 0
 
+def prepare_commit_msg(commit_msg_file: str) -> int:
     # check if the commit message needs to be generated using commitizen
-    if (
-        subprocess.run(
-            [
-                "cz",
-                "check",
-                "--commit-msg-file",
-                commit_msg_file,
-            ],
-            capture_output=True,
-        ).returncode
-        != 0
-    ):
-        backup_file = Path(
-            tempfile.gettempdir(), f"cz.commit{os.environ.get('USER', '')}.backup"
-        )
-
+    exit_code = subprocess.run(
+        [
+            "cz",
+            "check",
+            "--commit-msg-file",
+            commit_msg_file,
+        ],
+        capture_output=True,
+    ).returncode
+    if exit_code != 0:
+        backup_file = Path(get_backup_file_path())
         if backup_file.is_file():
             # confirm if commit message from backup file should be reused
             answer = input("retry with previous message? [y/N]: ")
@@ -56,6 +51,7 @@ def prepare_commit_msg(commit_msg_file: Path) -> int:
 
         # write message to backup file
         shutil.copyfile(commit_msg_file, backup_file)
+    return 0
 
 
 if __name__ == "__main__":
diff --git a/mkdocs.yml b/mkdocs.yml
index 1f76605153..dec8d3fd5d 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -31,27 +31,34 @@ edit_uri: ""
 
 nav:
   - Introduction: "README.md"
-  - Getting Started: "getting_started.md"
   - Commands:
-      - Init: "init.md"
-      - Commit: "commit.md"
-      - Bump: "bump.md"
-      - Check: "check.md"
-      - Changelog: "changelog.md"
+      - init: "commands/init.md"
+      - commit: "commands/commit.md"
+      - bump: "commands/bump.md"
+      - check: "commands/check.md"
+      - changelog: "commands/changelog.md"
+      - example: "commands/example.md"
+      - info: "commands/info.md"
+      - ls: "commands/ls.md"
+      - schema: "commands/schema.md"
+      - version: "commands/version.md"
   - Configuration: "config.md"
   - Customization: "customization.md"
   - Tutorials:
       - Writing commits: "tutorials/writing_commits.md"
+      - Managing tags formats: "tutorials/tag_format.md"
       - Auto check commits: "tutorials/auto_check.md"
       - Auto prepare commit message: "tutorials/auto_prepare_commit_message.md"
       - GitLab CI: "tutorials/gitlab_ci.md"
-      - Github Actions: "tutorials/github_actions.md"
+      - GitHub Actions: "tutorials/github_actions.md"
       - Jenkins pipeline: "tutorials/jenkins_pipeline.md"
       - Developmental releases: "tutorials/dev_releases.md"
+      - Monorepo support: "tutorials/monorepo_guidance.md"
   - FAQ: "faq.md"
   - Exit Codes: "exit_codes.md"
   - Third-Party Commitizen Templates: "third-party-commitizen.md"
   - Contributing: "contributing.md"
+  - Contributing TL;DR: "contributing_tldr.md"
   - Resources: "external_links.md"
 
 markdown_extensions:
diff --git a/poetry.lock b/poetry.lock
index 29409aa76b..e7f0e96e51 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,25 +1,15 @@
-# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
-
-[[package]]
-name = "appnope"
-version = "0.1.3"
-description = "Disable App Nap on macOS >= 10.9"
-optional = false
-python-versions = "*"
-files = [
-    {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
-    {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
-]
+# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
 
 [[package]]
 name = "argcomplete"
-version = "3.2.1"
+version = "3.6.2"
 description = "Bash tab completion for argparse"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
-    {file = "argcomplete-3.2.1-py3-none-any.whl", hash = "sha256:30891d87f3c1abe091f2142613c9d33cac84a5e15404489f033b20399b691fec"},
-    {file = "argcomplete-3.2.1.tar.gz", hash = "sha256:437f67fb9b058da5a090df505ef9be0297c4883993f3f56cb186ff087778cfb4"},
+    {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"},
+    {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"},
 ]
 
 [package.extras]
@@ -27,105 +17,76 @@ test = ["coverage", "mypy", "pexpect", "ruff", "wheel"]
 
 [[package]]
 name = "asttokens"
-version = "2.4.1"
+version = "3.0.0"
 description = "Annotate AST trees with source code positions"
 optional = false
-python-versions = "*"
+python-versions = ">=3.8"
+groups = ["dev"]
 files = [
-    {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
-    {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
+    {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
+    {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
 ]
 
-[package.dependencies]
-six = ">=1.12.0"
-
 [package.extras]
-astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
-test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
+astroid = ["astroid (>=2,<4)"]
+test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"]
 
 [[package]]
 name = "babel"
-version = "2.14.0"
+version = "2.17.0"
 description = "Internationalization utilities"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["documentation"]
 files = [
-    {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"},
-    {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"},
+    {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"},
+    {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"},
 ]
 
-[package.dependencies]
-pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
-
 [package.extras]
-dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
+dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
 
 [[package]]
-name = "backcall"
-version = "0.2.0"
-description = "Specifications for callback functions passed in to an API"
+name = "backrefs"
+version = "5.8"
+description = "A wrapper around re and regex that adds additional back references."
 optional = false
-python-versions = "*"
+python-versions = ">=3.9"
+groups = ["documentation"]
 files = [
-    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
-    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
+    {file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"},
+    {file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"},
+    {file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"},
+    {file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"},
+    {file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"},
+    {file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"},
 ]
 
+[package.extras]
+extras = ["regex"]
+
 [[package]]
-name = "black"
-version = "23.12.1"
-description = "The uncompromising code formatter."
+name = "cachetools"
+version = "6.0.0"
+description = "Extensible memoizing collections and decorators"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev"]
 files = [
-    {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"},
-    {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"},
-    {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"},
-    {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"},
-    {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"},
-    {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"},
-    {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"},
-    {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"},
-    {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"},
-    {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"},
-    {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"},
-    {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"},
-    {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"},
-    {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"},
-    {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"},
-    {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"},
-    {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"},
-    {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"},
-    {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"},
-    {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"},
-    {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"},
-    {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"},
+    {file = "cachetools-6.0.0-py3-none-any.whl", hash = "sha256:82e73ba88f7b30228b5507dce1a1f878498fc669d972aef2dde4f3a3c24f103e"},
+    {file = "cachetools-6.0.0.tar.gz", hash = "sha256:f225782b84438f828328fc2ad74346522f27e5b1440f4e9fd18b20ebfd1aa2cf"},
 ]
 
-[package.dependencies]
-click = ">=8.0.0"
-mypy-extensions = ">=0.4.3"
-packaging = ">=22.0"
-pathspec = ">=0.9.0"
-platformdirs = ">=2"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
-
-[package.extras]
-colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
-jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
-uvloop = ["uvloop (>=0.15.2)"]
-
 [[package]]
 name = "certifi"
-version = "2023.11.17"
+version = "2025.4.26"
 description = "Python package for providing Mozilla's CA Bundle."
 optional = false
 python-versions = ">=3.6"
+groups = ["documentation"]
 files = [
-    {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
-    {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
+    {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"},
+    {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"},
 ]
 
 [[package]]
@@ -134,119 +95,136 @@ version = "3.4.0"
 description = "Validate configuration and produce human readable error messages."
 optional = false
 python-versions = ">=3.8"
+groups = ["linters"]
 files = [
     {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
     {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
 ]
 
+[[package]]
+name = "chardet"
+version = "5.2.0"
+description = "Universal encoding detector for Python 3"
+optional = false
+python-versions = ">=3.7"
+groups = ["dev"]
+files = [
+    {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
+    {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
+]
+
 [[package]]
 name = "charset-normalizer"
-version = "3.3.2"
+version = "3.4.2"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 optional = false
-python-versions = ">=3.7.0"
-files = [
-    {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
-    {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
-    {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
-    {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
-    {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
-    {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
-    {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
-    {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
+python-versions = ">=3.7"
+groups = ["main", "documentation"]
+files = [
+    {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"},
+    {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"},
+    {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"},
+    {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"},
+    {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"},
+    {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"},
+    {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"},
+    {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"},
+    {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"},
+    {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"},
 ]
 
 [[package]]
 name = "click"
-version = "8.1.7"
+version = "8.1.8"
 description = "Composable command line interface toolkit"
 optional = false
 python-versions = ">=3.7"
+groups = ["documentation"]
 files = [
-    {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
-    {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
+    {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
+    {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
 ]
 
 [package.dependencies]
@@ -258,6 +236,7 @@ version = "0.4.6"
 description = "Cross-platform colored terminal text."
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["main", "dev", "documentation", "test"]
 files = [
     {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -265,144 +244,170 @@ files = [
 
 [[package]]
 name = "coverage"
-version = "7.3.3"
+version = "7.8.2"
 description = "Code coverage measurement for Python"
 optional = false
-python-versions = ">=3.8"
-files = [
-    {file = "coverage-7.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d874434e0cb7b90f7af2b6e3309b0733cde8ec1476eb47db148ed7deeb2a9494"},
-    {file = "coverage-7.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee6621dccce8af666b8c4651f9f43467bfbf409607c604b840b78f4ff3619aeb"},
-    {file = "coverage-7.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1367aa411afb4431ab58fd7ee102adb2665894d047c490649e86219327183134"},
-    {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f0f8f0c497eb9c9f18f21de0750c8d8b4b9c7000b43996a094290b59d0e7523"},
-    {file = "coverage-7.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0338c4b0951d93d547e0ff8d8ea340fecf5885f5b00b23be5aa99549e14cfd"},
-    {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d31650d313bd90d027f4be7663dfa2241079edd780b56ac416b56eebe0a21aab"},
-    {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9437a4074b43c177c92c96d051957592afd85ba00d3e92002c8ef45ee75df438"},
-    {file = "coverage-7.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e17d9cb06c13b4f2ef570355fa45797d10f19ca71395910b249e3f77942a837"},
-    {file = "coverage-7.3.3-cp310-cp310-win32.whl", hash = "sha256:eee5e741b43ea1b49d98ab6e40f7e299e97715af2488d1c77a90de4a663a86e2"},
-    {file = "coverage-7.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:593efa42160c15c59ee9b66c5f27a453ed3968718e6e58431cdfb2d50d5ad284"},
-    {file = "coverage-7.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c944cf1775235c0857829c275c777a2c3e33032e544bcef614036f337ac37bb"},
-    {file = "coverage-7.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eda7f6e92358ac9e1717ce1f0377ed2b9320cea070906ece4e5c11d172a45a39"},
-    {file = "coverage-7.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c854c1d2c7d3e47f7120b560d1a30c1ca221e207439608d27bc4d08fd4aeae8"},
-    {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:222b038f08a7ebed1e4e78ccf3c09a1ca4ac3da16de983e66520973443b546bc"},
-    {file = "coverage-7.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff4800783d85bff132f2cc7d007426ec698cdce08c3062c8d501ad3f4ea3d16c"},
-    {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fc200cec654311ca2c3f5ab3ce2220521b3d4732f68e1b1e79bef8fcfc1f2b97"},
-    {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:307aecb65bb77cbfebf2eb6e12009e9034d050c6c69d8a5f3f737b329f4f15fb"},
-    {file = "coverage-7.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ffb0eacbadb705c0a6969b0adf468f126b064f3362411df95f6d4f31c40d31c1"},
-    {file = "coverage-7.3.3-cp311-cp311-win32.whl", hash = "sha256:79c32f875fd7c0ed8d642b221cf81feba98183d2ff14d1f37a1bbce6b0347d9f"},
-    {file = "coverage-7.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:243576944f7c1a1205e5cd658533a50eba662c74f9be4c050d51c69bd4532936"},
-    {file = "coverage-7.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a2ac4245f18057dfec3b0074c4eb366953bca6787f1ec397c004c78176a23d56"},
-    {file = "coverage-7.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f9191be7af41f0b54324ded600e8ddbcabea23e1e8ba419d9a53b241dece821d"},
-    {file = "coverage-7.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c0b1b8b5a4aebf8fcd227237fc4263aa7fa0ddcd4d288d42f50eff18b0bac4"},
-    {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee453085279df1bac0996bc97004771a4a052b1f1e23f6101213e3796ff3cb85"},
-    {file = "coverage-7.3.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1191270b06ecd68b1d00897b2daddb98e1719f63750969614ceb3438228c088e"},
-    {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:007a7e49831cfe387473e92e9ff07377f6121120669ddc39674e7244350a6a29"},
-    {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:af75cf83c2d57717a8493ed2246d34b1f3398cb8a92b10fd7a1858cad8e78f59"},
-    {file = "coverage-7.3.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:811ca7373da32f1ccee2927dc27dc523462fd30674a80102f86c6753d6681bc6"},
-    {file = "coverage-7.3.3-cp312-cp312-win32.whl", hash = "sha256:733537a182b5d62184f2a72796eb6901299898231a8e4f84c858c68684b25a70"},
-    {file = "coverage-7.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:e995efb191f04b01ced307dbd7407ebf6e6dc209b528d75583277b10fd1800ee"},
-    {file = "coverage-7.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbd8a5fe6c893de21a3c6835071ec116d79334fbdf641743332e442a3466f7ea"},
-    {file = "coverage-7.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:50c472c1916540f8b2deef10cdc736cd2b3d1464d3945e4da0333862270dcb15"},
-    {file = "coverage-7.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e9223a18f51d00d3ce239c39fc41410489ec7a248a84fab443fbb39c943616c"},
-    {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f501e36ac428c1b334c41e196ff6bd550c0353c7314716e80055b1f0a32ba394"},
-    {file = "coverage-7.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:475de8213ed95a6b6283056d180b2442eee38d5948d735cd3d3b52b86dd65b92"},
-    {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afdcc10c01d0db217fc0a64f58c7edd635b8f27787fea0a3054b856a6dff8717"},
-    {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:fff0b2f249ac642fd735f009b8363c2b46cf406d3caec00e4deeb79b5ff39b40"},
-    {file = "coverage-7.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a1f76cfc122c9e0f62dbe0460ec9cc7696fc9a0293931a33b8870f78cf83a327"},
-    {file = "coverage-7.3.3-cp38-cp38-win32.whl", hash = "sha256:757453848c18d7ab5d5b5f1827293d580f156f1c2c8cef45bfc21f37d8681069"},
-    {file = "coverage-7.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad2453b852a1316c8a103c9c970db8fbc262f4f6b930aa6c606df9b2766eee06"},
-    {file = "coverage-7.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b15e03b8ee6a908db48eccf4e4e42397f146ab1e91c6324da44197a45cb9132"},
-    {file = "coverage-7.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89400aa1752e09f666cc48708eaa171eef0ebe3d5f74044b614729231763ae69"},
-    {file = "coverage-7.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c59a3e59fb95e6d72e71dc915e6d7fa568863fad0a80b33bc7b82d6e9f844973"},
-    {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ede881c7618f9cf93e2df0421ee127afdfd267d1b5d0c59bcea771cf160ea4a"},
-    {file = "coverage-7.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3bfd2c2f0e5384276e12b14882bf2c7621f97c35320c3e7132c156ce18436a1"},
-    {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7f3bad1a9313401ff2964e411ab7d57fb700a2d5478b727e13f156c8f89774a0"},
-    {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:65d716b736f16e250435473c5ca01285d73c29f20097decdbb12571d5dfb2c94"},
-    {file = "coverage-7.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a702e66483b1fe602717020a0e90506e759c84a71dbc1616dd55d29d86a9b91f"},
-    {file = "coverage-7.3.3-cp39-cp39-win32.whl", hash = "sha256:7fbf3f5756e7955174a31fb579307d69ffca91ad163467ed123858ce0f3fd4aa"},
-    {file = "coverage-7.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cad9afc1644b979211989ec3ff7d82110b2ed52995c2f7263e7841c846a75348"},
-    {file = "coverage-7.3.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:d299d379b676812e142fb57662a8d0d810b859421412b4d7af996154c00c31bb"},
-    {file = "coverage-7.3.3.tar.gz", hash = "sha256:df04c64e58df96b4427db8d0559e95e2df3138c9916c96f9f6a4dd220db2fdb7"},
+python-versions = ">=3.9"
+groups = ["test"]
+files = [
+    {file = "coverage-7.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd8ec21e1443fd7a447881332f7ce9d35b8fbd2849e761bb290b584535636b0a"},
+    {file = "coverage-7.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26c2396674816deaeae7ded0e2b42c26537280f8fe313335858ffff35019be"},
+    {file = "coverage-7.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1aec326ed237e5880bfe69ad41616d333712c7937bcefc1343145e972938f9b3"},
+    {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e818796f71702d7a13e50c70de2a1924f729228580bcba1607cccf32eea46e6"},
+    {file = "coverage-7.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:546e537d9e24efc765c9c891328f30f826e3e4808e31f5d0f87c4ba12bbd1622"},
+    {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab9b09a2349f58e73f8ebc06fac546dd623e23b063e5398343c5270072e3201c"},
+    {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fd51355ab8a372d89fb0e6a31719e825cf8df8b6724bee942fb5b92c3f016ba3"},
+    {file = "coverage-7.8.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0774df1e093acb6c9e4d58bce7f86656aeed6c132a16e2337692c12786b32404"},
+    {file = "coverage-7.8.2-cp310-cp310-win32.whl", hash = "sha256:00f2e2f2e37f47e5f54423aeefd6c32a7dbcedc033fcd3928a4f4948e8b96af7"},
+    {file = "coverage-7.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:145b07bea229821d51811bf15eeab346c236d523838eda395ea969d120d13347"},
+    {file = "coverage-7.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b99058eef42e6a8dcd135afb068b3d53aff3921ce699e127602efff9956457a9"},
+    {file = "coverage-7.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5feb7f2c3e6ea94d3b877def0270dff0947b8d8c04cfa34a17be0a4dc1836879"},
+    {file = "coverage-7.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:670a13249b957bb9050fab12d86acef7bf8f6a879b9d1a883799276e0d4c674a"},
+    {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0bdc8bf760459a4a4187b452213e04d039990211f98644c7292adf1e471162b5"},
+    {file = "coverage-7.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07a989c867986c2a75f158f03fdb413128aad29aca9d4dbce5fc755672d96f11"},
+    {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2db10dedeb619a771ef0e2949ccba7b75e33905de959c2643a4607bef2f3fb3a"},
+    {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e6ea7dba4e92926b7b5f0990634b78ea02f208d04af520c73a7c876d5a8d36cb"},
+    {file = "coverage-7.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ef2f22795a7aca99fc3c84393a55a53dd18ab8c93fb431004e4d8f0774150f54"},
+    {file = "coverage-7.8.2-cp311-cp311-win32.whl", hash = "sha256:641988828bc18a6368fe72355df5f1703e44411adbe49bba5644b941ce6f2e3a"},
+    {file = "coverage-7.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8ab4a51cb39dc1933ba627e0875046d150e88478dbe22ce145a68393e9652975"},
+    {file = "coverage-7.8.2-cp311-cp311-win_arm64.whl", hash = "sha256:8966a821e2083c74d88cca5b7dcccc0a3a888a596a04c0b9668a891de3a0cc53"},
+    {file = "coverage-7.8.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2f6fe3654468d061942591aef56686131335b7a8325684eda85dacdf311356c"},
+    {file = "coverage-7.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76090fab50610798cc05241bf83b603477c40ee87acd358b66196ab0ca44ffa1"},
+    {file = "coverage-7.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd0a0a5054be160777a7920b731a0570284db5142abaaf81bcbb282b8d99279"},
+    {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da23ce9a3d356d0affe9c7036030b5c8f14556bd970c9b224f9c8205505e3b99"},
+    {file = "coverage-7.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9392773cffeb8d7e042a7b15b82a414011e9d2b5fdbbd3f7e6a6b17d5e21b20"},
+    {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:876cbfd0b09ce09d81585d266c07a32657beb3eaec896f39484b631555be0fe2"},
+    {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3da9b771c98977a13fbc3830f6caa85cae6c9c83911d24cb2d218e9394259c57"},
+    {file = "coverage-7.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9a990f6510b3292686713bfef26d0049cd63b9c7bb17e0864f133cbfd2e6167f"},
+    {file = "coverage-7.8.2-cp312-cp312-win32.whl", hash = "sha256:bf8111cddd0f2b54d34e96613e7fbdd59a673f0cf5574b61134ae75b6f5a33b8"},
+    {file = "coverage-7.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:86a323a275e9e44cdf228af9b71c5030861d4d2610886ab920d9945672a81223"},
+    {file = "coverage-7.8.2-cp312-cp312-win_arm64.whl", hash = "sha256:820157de3a589e992689ffcda8639fbabb313b323d26388d02e154164c57b07f"},
+    {file = "coverage-7.8.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ea561010914ec1c26ab4188aef8b1567272ef6de096312716f90e5baa79ef8ca"},
+    {file = "coverage-7.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cb86337a4fcdd0e598ff2caeb513ac604d2f3da6d53df2c8e368e07ee38e277d"},
+    {file = "coverage-7.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a4636ddb666971345541b59899e969f3b301143dd86b0ddbb570bd591f1e85"},
+    {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5040536cf9b13fb033f76bcb5e1e5cb3b57c4807fef37db9e0ed129c6a094257"},
+    {file = "coverage-7.8.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc67994df9bcd7e0150a47ef41278b9e0a0ea187caba72414b71dc590b99a108"},
+    {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e6c86888fd076d9e0fe848af0a2142bf606044dc5ceee0aa9eddb56e26895a0"},
+    {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:684ca9f58119b8e26bef860db33524ae0365601492e86ba0b71d513f525e7050"},
+    {file = "coverage-7.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8165584ddedb49204c4e18da083913bdf6a982bfb558632a79bdaadcdafd0d48"},
+    {file = "coverage-7.8.2-cp313-cp313-win32.whl", hash = "sha256:34759ee2c65362163699cc917bdb2a54114dd06d19bab860725f94ef45a3d9b7"},
+    {file = "coverage-7.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:2f9bc608fbafaee40eb60a9a53dbfb90f53cc66d3d32c2849dc27cf5638a21e3"},
+    {file = "coverage-7.8.2-cp313-cp313-win_arm64.whl", hash = "sha256:9fe449ee461a3b0c7105690419d0b0aba1232f4ff6d120a9e241e58a556733f7"},
+    {file = "coverage-7.8.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8369a7c8ef66bded2b6484053749ff220dbf83cba84f3398c84c51a6f748a008"},
+    {file = "coverage-7.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:159b81df53a5fcbc7d45dae3adad554fdbde9829a994e15227b3f9d816d00b36"},
+    {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6fcbbd35a96192d042c691c9e0c49ef54bd7ed865846a3c9d624c30bb67ce46"},
+    {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05364b9cc82f138cc86128dc4e2e1251c2981a2218bfcd556fe6b0fbaa3501be"},
+    {file = "coverage-7.8.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d532db4e5ff3979ce47d18e2fe8ecad283eeb7367726da0e5ef88e4fe64740"},
+    {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4000a31c34932e7e4fa0381a3d6deb43dc0c8f458e3e7ea6502e6238e10be625"},
+    {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:43ff5033d657cd51f83015c3b7a443287250dc14e69910577c3e03bd2e06f27b"},
+    {file = "coverage-7.8.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94316e13f0981cbbba132c1f9f365cac1d26716aaac130866ca812006f662199"},
+    {file = "coverage-7.8.2-cp313-cp313t-win32.whl", hash = "sha256:3f5673888d3676d0a745c3d0e16da338c5eea300cb1f4ada9c872981265e76d8"},
+    {file = "coverage-7.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:2c08b05ee8d7861e45dc5a2cc4195c8c66dca5ac613144eb6ebeaff2d502e73d"},
+    {file = "coverage-7.8.2-cp313-cp313t-win_arm64.whl", hash = "sha256:1e1448bb72b387755e1ff3ef1268a06617afd94188164960dba8d0245a46004b"},
+    {file = "coverage-7.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:496948261eaac5ac9cf43f5d0a9f6eb7a6d4cb3bedb2c5d294138142f5c18f2a"},
+    {file = "coverage-7.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eacd2de0d30871eff893bab0b67840a96445edcb3c8fd915e6b11ac4b2f3fa6d"},
+    {file = "coverage-7.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b039ffddc99ad65d5078ef300e0c7eed08c270dc26570440e3ef18beb816c1ca"},
+    {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e49824808d4375ede9dd84e9961a59c47f9113039f1a525e6be170aa4f5c34d"},
+    {file = "coverage-7.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b069938961dfad881dc2f8d02b47645cd2f455d3809ba92a8a687bf513839787"},
+    {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de77c3ba8bb686d1c411e78ee1b97e6e0b963fb98b1637658dd9ad2c875cf9d7"},
+    {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1676628065a498943bd3f64f099bb573e08cf1bc6088bbe33cf4424e0876f4b3"},
+    {file = "coverage-7.8.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8e1a26e7e50076e35f7afafde570ca2b4d7900a491174ca357d29dece5aacee7"},
+    {file = "coverage-7.8.2-cp39-cp39-win32.whl", hash = "sha256:6782a12bf76fa61ad9350d5a6ef5f3f020b57f5e6305cbc663803f2ebd0f270a"},
+    {file = "coverage-7.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1efa4166ba75ccefd647f2d78b64f53f14fb82622bc94c5a5cb0a622f50f1c9e"},
+    {file = "coverage-7.8.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:ec455eedf3ba0bbdf8f5a570012617eb305c63cb9f03428d39bf544cb2b94837"},
+    {file = "coverage-7.8.2-py3-none-any.whl", hash = "sha256:726f32ee3713f7359696331a18daf0c3b3a70bb0ae71141b9d3c52be7c595e32"},
+    {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"},
 ]
 
 [package.dependencies]
 tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
 
 [package.extras]
-toml = ["tomli"]
+toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
 
 [[package]]
 name = "decli"
-version = "0.6.1"
+version = "0.6.3"
 description = "Minimal, easy-to-use, declarative cli tool"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
+groups = ["main"]
 files = [
-    {file = "decli-0.6.1-py3-none-any.whl", hash = "sha256:7815ac58617764e1a200d7cadac6315fcaacc24d727d182f9878dd6378ccf869"},
-    {file = "decli-0.6.1.tar.gz", hash = "sha256:ed88ccb947701e8e5509b7945fda56e150e2ac74a69f25d47ac85ef30ab0c0f0"},
+    {file = "decli-0.6.3-py3-none-any.whl", hash = "sha256:5152347c7bb8e3114ad65db719e5709b28d7f7f45bdb709f70167925e55640f3"},
+    {file = "decli-0.6.3.tar.gz", hash = "sha256:87f9d39361adf7f16b9ca6e3b614badf7519da13092f2db3c80ca223c53c7656"},
 ]
 
 [[package]]
 name = "decorator"
-version = "5.1.1"
+version = "5.2.1"
 description = "Decorators for Humans"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
+groups = ["dev"]
 files = [
-    {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
-    {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
+    {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
+    {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
 ]
 
 [[package]]
 name = "deprecated"
-version = "1.2.14"
+version = "1.2.18"
 description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+groups = ["test"]
 files = [
-    {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
-    {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
+    {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"},
+    {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"},
 ]
 
 [package.dependencies]
 wrapt = ">=1.10,<2"
 
 [package.extras]
-dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"]
 
 [[package]]
 name = "distlib"
-version = "0.3.8"
+version = "0.3.9"
 description = "Distribution utilities"
 optional = false
 python-versions = "*"
+groups = ["dev", "linters"]
 files = [
-    {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
-    {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
+    {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
+    {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
 ]
 
 [[package]]
 name = "exceptiongroup"
-version = "1.2.0"
+version = "1.3.0"
 description = "Backport of PEP 654 (exception groups)"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev", "test"]
+markers = "python_version < \"3.11\""
 files = [
-    {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
-    {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+    {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
+    {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
 ]
 
+[package.dependencies]
+typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
+
 [package.extras]
 test = ["pytest (>=6)"]
 
 [[package]]
 name = "execnet"
-version = "2.0.2"
+version = "2.1.1"
 description = "execnet: rapid multi-Python deployment"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["test"]
 files = [
-    {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"},
-    {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"},
+    {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"},
+    {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"},
 ]
 
 [package.extras]
@@ -410,43 +415,46 @@ testing = ["hatch", "pre-commit", "pytest", "tox"]
 
 [[package]]
 name = "executing"
-version = "2.0.1"
+version = "2.2.0"
 description = "Get the currently executing AST node of a frame, and other information"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
+groups = ["dev"]
 files = [
-    {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"},
-    {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"},
+    {file = "executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa"},
+    {file = "executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755"},
 ]
 
 [package.extras]
-tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
+tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
 
 [[package]]
 name = "filelock"
-version = "3.13.1"
+version = "3.18.0"
 description = "A platform independent file lock."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev", "linters"]
 files = [
-    {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
-    {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
+    {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"},
+    {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"},
 ]
 
 [package.extras]
-docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
-typing = ["typing-extensions (>=4.8)"]
+docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
+typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
 
 [[package]]
 name = "freezegun"
-version = "1.3.1"
+version = "1.5.2"
 description = "Let your Python tests travel through time"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["test"]
 files = [
-    {file = "freezegun-1.3.1-py3-none-any.whl", hash = "sha256:065e77a12624d05531afa87ade12a0b9bdb53495c4573893252a055b545ce3ea"},
-    {file = "freezegun-1.3.1.tar.gz", hash = "sha256:48984397b3b58ef5dfc645d6a304b0060f612bcecfdaaf45ce8aff0077a6cb6a"},
+    {file = "freezegun-1.5.2-py3-none-any.whl", hash = "sha256:5aaf3ba229cda57afab5bd311f0108d86b6fb119ae89d2cd9c43ec8c1733c85b"},
+    {file = "freezegun-1.5.2.tar.gz", hash = "sha256:a54ae1d2f9c02dbf42e02c18a3ab95ab4295818b549a34dac55592d72a905181"},
 ]
 
 [package.dependencies]
@@ -458,6 +466,7 @@ version = "2.1.0"
 description = "Copy your docs directly to the gh-pages branch."
 optional = false
 python-versions = "*"
+groups = ["documentation"]
 files = [
     {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
     {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
@@ -471,13 +480,14 @@ dev = ["flake8", "markdown", "twine", "wheel"]
 
 [[package]]
 name = "identify"
-version = "2.5.33"
+version = "2.6.12"
 description = "File identification library for Python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["linters"]
 files = [
-    {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
-    {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
+    {file = "identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2"},
+    {file = "identify-2.6.12.tar.gz", hash = "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6"},
 ]
 
 [package.extras]
@@ -485,112 +495,124 @@ license = ["ukkonen"]
 
 [[package]]
 name = "idna"
-version = "3.6"
+version = "3.10"
 description = "Internationalized Domain Names in Applications (IDNA)"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
+groups = ["documentation"]
 files = [
-    {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
-    {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+    {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
+    {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
 ]
 
+[package.extras]
+all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
+
 [[package]]
 name = "importlib-metadata"
-version = "7.0.1"
+version = "8.6.1"
 description = "Read metadata from Python packages"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["main", "documentation"]
+markers = "python_version == \"3.9\""
 files = [
-    {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
-    {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
+    {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
+    {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
 ]
 
 [package.dependencies]
-zipp = ">=0.5"
+zipp = ">=3.20"
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
 perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
+type = ["pytest-mypy"]
 
 [[package]]
 name = "iniconfig"
-version = "2.0.0"
+version = "2.1.0"
 description = "brain-dead simple config-ini parsing"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["test"]
 files = [
-    {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
-    {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+    {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
+    {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
 ]
 
 [[package]]
 name = "ipython"
-version = "8.12.3"
+version = "8.18.1"
 description = "IPython: Productive Interactive Computing"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev"]
 files = [
-    {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"},
-    {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"},
+    {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"},
+    {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"},
 ]
 
 [package.dependencies]
-appnope = {version = "*", markers = "sys_platform == \"darwin\""}
-backcall = "*"
 colorama = {version = "*", markers = "sys_platform == \"win32\""}
 decorator = "*"
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
 jedi = ">=0.16"
 matplotlib-inline = "*"
 pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
-pickleshare = "*"
-prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0"
+prompt-toolkit = ">=3.0.41,<3.1.0"
 pygments = ">=2.4.0"
 stack-data = "*"
 traitlets = ">=5"
 typing-extensions = {version = "*", markers = "python_version < \"3.10\""}
 
 [package.extras]
-all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
+all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
 black = ["black"]
-doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
+doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
 kernel = ["ipykernel"]
 nbconvert = ["nbconvert"]
 nbformat = ["nbformat"]
 notebook = ["ipywidgets", "notebook"]
 parallel = ["ipyparallel"]
 qtconsole = ["qtconsole"]
-test = ["pytest (<7.1)", "pytest-asyncio", "testpath"]
-test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"]
+test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"]
+test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"]
 
 [[package]]
 name = "jedi"
-version = "0.19.1"
+version = "0.19.2"
 description = "An autocompletion tool for Python that can be used for text editors."
 optional = false
 python-versions = ">=3.6"
+groups = ["dev"]
 files = [
-    {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
-    {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
+    {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
+    {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
 ]
 
 [package.dependencies]
-parso = ">=0.8.3,<0.9.0"
+parso = ">=0.8.4,<0.9.0"
 
 [package.extras]
 docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
 qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
-testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
+testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
 
 [[package]]
 name = "jinja2"
-version = "3.1.3"
+version = "3.1.6"
 description = "A very fast and expressive template engine."
 optional = false
 python-versions = ">=3.7"
+groups = ["main", "documentation"]
 files = [
-    {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"},
-    {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"},
+    {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
+    {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
 ]
 
 [package.dependencies]
@@ -601,111 +623,153 @@ i18n = ["Babel (>=2.7)"]
 
 [[package]]
 name = "markdown"
-version = "3.5.1"
+version = "3.8"
 description = "Python implementation of John Gruber's Markdown."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["documentation"]
 files = [
-    {file = "Markdown-3.5.1-py3-none-any.whl", hash = "sha256:5874b47d4ee3f0b14d764324d2c94c03ea66bee56f2d929da9f2508d65e722dc"},
-    {file = "Markdown-3.5.1.tar.gz", hash = "sha256:b65d7beb248dc22f2e8a31fb706d93798093c308dc1aba295aedeb9d41a813bd"},
+    {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"},
+    {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"},
 ]
 
 [package.dependencies]
 importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
 
 [package.extras]
-docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
+docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
 testing = ["coverage", "pyyaml"]
 
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+description = "Python port of markdown-it. Markdown parsing, done right!"
+optional = false
+python-versions = ">=3.8"
+groups = ["script"]
+files = [
+    {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
+    {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
+]
+
+[package.dependencies]
+mdurl = ">=0.1,<1.0"
+
+[package.extras]
+benchmarking = ["psutil", "pytest", "pytest-benchmark"]
+code-style = ["pre-commit (>=3.0,<4.0)"]
+compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
+linkify = ["linkify-it-py (>=1,<3)"]
+plugins = ["mdit-py-plugins"]
+profiling = ["gprof2dot"]
+rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
+testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+
 [[package]]
 name = "markupsafe"
-version = "2.1.3"
+version = "3.0.2"
 description = "Safely add untrusted strings to HTML/XML markup."
 optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
-    {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
-    {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
-    {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
-    {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
-    {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
-    {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
-    {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
+python-versions = ">=3.9"
+groups = ["main", "documentation"]
+files = [
+    {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
+    {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
+    {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
+    {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
+    {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
+    {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
+    {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
 ]
 
 [[package]]
 name = "matplotlib-inline"
-version = "0.1.6"
+version = "0.1.7"
 description = "Inline Matplotlib backend for Jupyter"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
+groups = ["dev"]
 files = [
-    {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
-    {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
+    {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
+    {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
 ]
 
 [package.dependencies]
 traitlets = "*"
 
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+description = "Markdown URL utilities"
+optional = false
+python-versions = ">=3.7"
+groups = ["script"]
+files = [
+    {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
+    {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
+]
+
 [[package]]
 name = "mergedeep"
 version = "1.3.4"
 description = "A deep merge function for 🐍."
 optional = false
 python-versions = ">=3.6"
+groups = ["documentation"]
 files = [
     {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
     {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
@@ -713,62 +777,82 @@ files = [
 
 [[package]]
 name = "mkdocs"
-version = "1.5.3"
+version = "1.6.1"
 description = "Project documentation with Markdown."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["documentation"]
 files = [
-    {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"},
-    {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"},
+    {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"},
+    {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"},
 ]
 
 [package.dependencies]
 click = ">=7.0"
 colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""}
 ghp-import = ">=1.0"
-importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
+importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
 jinja2 = ">=2.11.1"
-markdown = ">=3.2.1"
+markdown = ">=3.3.6"
 markupsafe = ">=2.0.1"
 mergedeep = ">=1.3.4"
+mkdocs-get-deps = ">=0.2.0"
 packaging = ">=20.5"
 pathspec = ">=0.11.1"
-platformdirs = ">=2.2.0"
 pyyaml = ">=5.1"
 pyyaml-env-tag = ">=0.1"
 watchdog = ">=2.0"
 
 [package.extras]
 i18n = ["babel (>=2.9.0)"]
-min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"]
+min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
+
+[[package]]
+name = "mkdocs-get-deps"
+version = "0.2.0"
+description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file"
+optional = false
+python-versions = ">=3.8"
+groups = ["documentation"]
+files = [
+    {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"},
+    {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"},
+]
+
+[package.dependencies]
+importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
+mergedeep = ">=1.3.4"
+platformdirs = ">=2.2.0"
+pyyaml = ">=5.1"
 
 [[package]]
 name = "mkdocs-material"
-version = "9.5.3"
+version = "9.6.14"
 description = "Documentation that simply works"
 optional = false
 python-versions = ">=3.8"
+groups = ["documentation"]
 files = [
-    {file = "mkdocs_material-9.5.3-py3-none-any.whl", hash = "sha256:76c93a8525cceb0b395b9cedab3428bf518cf6439adef2b940f1c1574b775d89"},
-    {file = "mkdocs_material-9.5.3.tar.gz", hash = "sha256:5899219f422f0a6de784232d9d40374416302ffae3c160cacc72969fcc1ee372"},
+    {file = "mkdocs_material-9.6.14-py3-none-any.whl", hash = "sha256:3b9cee6d3688551bf7a8e8f41afda97a3c39a12f0325436d76c86706114b721b"},
+    {file = "mkdocs_material-9.6.14.tar.gz", hash = "sha256:39d795e90dce6b531387c255bd07e866e027828b7346d3eba5ac3de265053754"},
 ]
 
 [package.dependencies]
 babel = ">=2.10,<3.0"
+backrefs = ">=5.7.post1,<6.0"
 colorama = ">=0.4,<1.0"
-jinja2 = ">=3.0,<4.0"
+jinja2 = ">=3.1,<4.0"
 markdown = ">=3.2,<4.0"
-mkdocs = ">=1.5.3,<1.6.0"
+mkdocs = ">=1.6,<2.0"
 mkdocs-material-extensions = ">=1.3,<2.0"
 paginate = ">=0.5,<1.0"
 pygments = ">=2.16,<3.0"
 pymdown-extensions = ">=10.2,<11.0"
-regex = ">=2022.4"
 requests = ">=2.26,<3.0"
 
 [package.extras]
-git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2,<2.0)"]
-imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=9.4,<10.0)"]
+git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"]
+imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"]
 recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"]
 
 [[package]]
@@ -777,6 +861,7 @@ version = "1.3.1"
 description = "Extension pack for Python Markdown and MkDocs Material."
 optional = false
 python-versions = ">=3.8"
+groups = ["documentation"]
 files = [
     {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"},
     {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"},
@@ -784,111 +869,138 @@ files = [
 
 [[package]]
 name = "mypy"
-version = "1.8.0"
+version = "1.16.0"
 description = "Optional static typing for Python"
 optional = false
-python-versions = ">=3.8"
-files = [
-    {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"},
-    {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"},
-    {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"},
-    {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"},
-    {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"},
-    {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"},
-    {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"},
-    {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"},
-    {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"},
-    {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"},
-    {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"},
-    {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"},
-    {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"},
-    {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"},
-    {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"},
-    {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"},
-    {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"},
-    {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"},
-    {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"},
-    {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"},
-    {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"},
-    {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"},
-    {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"},
-    {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"},
-    {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"},
-    {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"},
-    {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"},
+python-versions = ">=3.9"
+groups = ["linters"]
+files = [
+    {file = "mypy-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7909541fef256527e5ee9c0a7e2aeed78b6cda72ba44298d1334fe7881b05c5c"},
+    {file = "mypy-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e71d6f0090c2256c713ed3d52711d01859c82608b5d68d4fa01a3fe30df95571"},
+    {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:936ccfdd749af4766be824268bfe22d1db9eb2f34a3ea1d00ffbe5b5265f5491"},
+    {file = "mypy-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4086883a73166631307fdd330c4a9080ce24913d4f4c5ec596c601b3a4bdd777"},
+    {file = "mypy-1.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:feec38097f71797da0231997e0de3a58108c51845399669ebc532c815f93866b"},
+    {file = "mypy-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:09a8da6a0ee9a9770b8ff61b39c0bb07971cda90e7297f4213741b48a0cc8d93"},
+    {file = "mypy-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9f826aaa7ff8443bac6a494cf743f591488ea940dd360e7dd330e30dd772a5ab"},
+    {file = "mypy-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82d056e6faa508501af333a6af192c700b33e15865bda49611e3d7d8358ebea2"},
+    {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089bedc02307c2548eb51f426e085546db1fa7dd87fbb7c9fa561575cf6eb1ff"},
+    {file = "mypy-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a2322896003ba66bbd1318c10d3afdfe24e78ef12ea10e2acd985e9d684a666"},
+    {file = "mypy-1.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:021a68568082c5b36e977d54e8f1de978baf401a33884ffcea09bd8e88a98f4c"},
+    {file = "mypy-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:54066fed302d83bf5128632d05b4ec68412e1f03ef2c300434057d66866cea4b"},
+    {file = "mypy-1.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5436d11e89a3ad16ce8afe752f0f373ae9620841c50883dc96f8b8805620b13"},
+    {file = "mypy-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f2622af30bf01d8fc36466231bdd203d120d7a599a6d88fb22bdcb9dbff84090"},
+    {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d045d33c284e10a038f5e29faca055b90eee87da3fc63b8889085744ebabb5a1"},
+    {file = "mypy-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4968f14f44c62e2ec4a038c8797a87315be8df7740dc3ee8d3bfe1c6bf5dba8"},
+    {file = "mypy-1.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb14a4a871bb8efb1e4a50360d4e3c8d6c601e7a31028a2c79f9bb659b63d730"},
+    {file = "mypy-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:bd4e1ebe126152a7bbaa4daedd781c90c8f9643c79b9748caa270ad542f12bec"},
+    {file = "mypy-1.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e056237c89f1587a3be1a3a70a06a698d25e2479b9a2f57325ddaaffc3567b"},
+    {file = "mypy-1.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b07e107affb9ee6ce1f342c07f51552d126c32cd62955f59a7db94a51ad12c0"},
+    {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6fb60cbd85dc65d4d63d37cb5c86f4e3a301ec605f606ae3a9173e5cf34997b"},
+    {file = "mypy-1.16.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7e32297a437cc915599e0578fa6bc68ae6a8dc059c9e009c628e1c47f91495d"},
+    {file = "mypy-1.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:afe420c9380ccec31e744e8baff0d406c846683681025db3531b32db56962d52"},
+    {file = "mypy-1.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:55f9076c6ce55dd3f8cd0c6fff26a008ca8e5131b89d5ba6d86bd3f47e736eeb"},
+    {file = "mypy-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f56236114c425620875c7cf71700e3d60004858da856c6fc78998ffe767b73d3"},
+    {file = "mypy-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:15486beea80be24ff067d7d0ede673b001d0d684d0095803b3e6e17a886a2a92"},
+    {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2ed0e0847a80655afa2c121835b848ed101cc7b8d8d6ecc5205aedc732b1436"},
+    {file = "mypy-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb5fbc8063cb4fde7787e4c0406aa63094a34a2daf4673f359a1fb64050e9cb2"},
+    {file = "mypy-1.16.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a5fcfdb7318c6a8dd127b14b1052743b83e97a970f0edb6c913211507a255e20"},
+    {file = "mypy-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e7e0ad35275e02797323a5aa1be0b14a4d03ffdb2e5f2b0489fa07b89c67b21"},
+    {file = "mypy-1.16.0-py3-none-any.whl", hash = "sha256:29e1499864a3888bca5c1542f2d7232c6e586295183320caa95758fc84034031"},
+    {file = "mypy-1.16.0.tar.gz", hash = "sha256:84b94283f817e2aa6350a14b4a8fb2a35a53c286f97c9d30f53b63620e7af8ab"},
 ]
 
 [package.dependencies]
-mypy-extensions = ">=1.0.0"
+mypy_extensions = ">=1.0.0"
+pathspec = ">=0.9.0"
 tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = ">=4.1.0"
+typing_extensions = ">=4.6.0"
 
 [package.extras]
 dmypy = ["psutil (>=4.0)"]
+faster-cache = ["orjson"]
 install-types = ["pip"]
 mypyc = ["setuptools (>=50)"]
 reports = ["lxml"]
 
 [[package]]
 name = "mypy-extensions"
-version = "1.0.0"
+version = "1.1.0"
 description = "Type system extensions for programs checked with the mypy type checker."
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
+groups = ["linters"]
 files = [
-    {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
-    {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
+    {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"},
+    {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"},
 ]
 
 [[package]]
 name = "nodeenv"
-version = "1.8.0"
+version = "1.9.1"
 description = "Node.js virtual environment builder"
 optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["linters"]
 files = [
-    {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
-    {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
+    {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
+    {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
 ]
 
-[package.dependencies]
-setuptools = "*"
-
 [[package]]
 name = "packaging"
-version = "23.2"
+version = "25.0"
 description = "Core utilities for Python packages"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["main", "dev", "documentation", "test"]
 files = [
-    {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
-    {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+    {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
+    {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
 ]
 
 [[package]]
 name = "paginate"
-version = "0.5.6"
+version = "0.5.7"
 description = "Divides large result sets into pages for easier browsing"
 optional = false
 python-versions = "*"
+groups = ["documentation"]
 files = [
-    {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"},
+    {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"},
+    {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"},
 ]
 
+[package.extras]
+dev = ["pytest", "tox"]
+lint = ["black"]
+
 [[package]]
 name = "parso"
-version = "0.8.3"
+version = "0.8.4"
 description = "A Python Parser"
 optional = false
 python-versions = ">=3.6"
+groups = ["dev"]
 files = [
-    {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
-    {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
+    {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"},
+    {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"},
 ]
 
 [package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["docopt", "pytest (<6.0.0)"]
+qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
+testing = ["docopt", "pytest"]
+
+[[package]]
+name = "pastel"
+version = "0.2.1"
+description = "Bring colors to your terminal."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["dev"]
+files = [
+    {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"},
+    {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"},
+]
 
 [[package]]
 name = "pathspec"
@@ -896,6 +1008,7 @@ version = "0.12.1"
 description = "Utility library for gitignore style pattern matching of file paths."
 optional = false
 python-versions = ">=3.8"
+groups = ["documentation", "linters"]
 files = [
     {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
     {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@@ -907,6 +1020,8 @@ version = "4.9.0"
 description = "Pexpect allows easy control of interactive console applications."
 optional = false
 python-versions = "*"
+groups = ["dev"]
+markers = "sys_platform != \"win32\""
 files = [
     {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
     {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
@@ -916,55 +1031,68 @@ files = [
 ptyprocess = ">=0.5"
 
 [[package]]
-name = "pickleshare"
-version = "0.7.5"
-description = "Tiny 'shelve'-like database with concurrency support"
+name = "platformdirs"
+version = "4.3.8"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 optional = false
-python-versions = "*"
+python-versions = ">=3.9"
+groups = ["dev", "documentation", "linters"]
 files = [
-    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
-    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
+    {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"},
+    {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"},
 ]
 
+[package.extras]
+docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
+type = ["mypy (>=1.14.1)"]
+
 [[package]]
-name = "platformdirs"
-version = "4.1.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+name = "pluggy"
+version = "1.6.0"
+description = "plugin and hook calling mechanisms for python"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev", "test"]
 files = [
-    {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"},
-    {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"},
+    {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
+    {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
 ]
 
 [package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+dev = ["pre-commit", "tox"]
+testing = ["coverage", "pytest", "pytest-benchmark"]
 
 [[package]]
-name = "pluggy"
-version = "1.3.0"
-description = "plugin and hook calling mechanisms for python"
+name = "poethepoet"
+version = "0.34.0"
+description = "A task runner that works well with poetry."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["dev"]
 files = [
-    {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
-    {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
+    {file = "poethepoet-0.34.0-py3-none-any.whl", hash = "sha256:c472d6f0fdb341b48d346f4ccd49779840c15b30dfd6bc6347a80d6274b5e34e"},
+    {file = "poethepoet-0.34.0.tar.gz", hash = "sha256:86203acce555bbfe45cb6ccac61ba8b16a5784264484195874da457ddabf5850"},
 ]
 
+[package.dependencies]
+pastel = ">=0.2.1,<0.3.0"
+pyyaml = ">=6.0.2,<7.0"
+tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""}
+
 [package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
+poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""]
 
 [[package]]
 name = "pre-commit"
-version = "3.5.0"
+version = "4.2.0"
 description = "A framework for managing and maintaining multi-language pre-commit hooks."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["linters"]
 files = [
-    {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"},
-    {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"},
+    {file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"},
+    {file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"},
 ]
 
 [package.dependencies]
@@ -976,13 +1104,14 @@ virtualenv = ">=20.10.0"
 
 [[package]]
 name = "prompt-toolkit"
-version = "3.0.36"
+version = "3.0.51"
 description = "Library for building powerful interactive command lines in Python"
 optional = false
-python-versions = ">=3.6.2"
+python-versions = ">=3.8"
+groups = ["main", "dev"]
 files = [
-    {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"},
-    {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"},
+    {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"},
+    {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"},
 ]
 
 [package.dependencies]
@@ -994,6 +1123,8 @@ version = "0.7.0"
 description = "Run a subprocess in a pseudo terminal"
 optional = false
 python-versions = "*"
+groups = ["dev"]
+markers = "sys_platform != \"win32\""
 files = [
     {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
     {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
@@ -1001,13 +1132,14 @@ files = [
 
 [[package]]
 name = "pure-eval"
-version = "0.2.2"
+version = "0.2.3"
 description = "Safely evaluate AST nodes without side effects"
 optional = false
 python-versions = "*"
+groups = ["dev"]
 files = [
-    {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
-    {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
+    {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
+    {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
 ]
 
 [package.extras]
@@ -1015,159 +1147,192 @@ tests = ["pytest"]
 
 [[package]]
 name = "pygments"
-version = "2.17.2"
+version = "2.19.1"
 description = "Pygments is a syntax highlighting package written in Python."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["dev", "documentation", "script", "test"]
 files = [
-    {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
-    {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
+    {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
+    {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
 ]
 
 [package.extras]
-plugins = ["importlib-metadata"]
 windows-terminal = ["colorama (>=0.4.6)"]
 
 [[package]]
 name = "pymdown-extensions"
-version = "10.5"
+version = "10.15"
 description = "Extension pack for Python Markdown."
 optional = false
 python-versions = ">=3.8"
+groups = ["documentation"]
 files = [
-    {file = "pymdown_extensions-10.5-py3-none-any.whl", hash = "sha256:1f0ca8bb5beff091315f793ee17683bc1390731f6ac4c5eb01e27464b80fe879"},
-    {file = "pymdown_extensions-10.5.tar.gz", hash = "sha256:1b60f1e462adbec5a1ed79dac91f666c9c0d241fa294de1989f29d20096cfd0b"},
+    {file = "pymdown_extensions-10.15-py3-none-any.whl", hash = "sha256:46e99bb272612b0de3b7e7caf6da8dd5f4ca5212c0b273feb9304e236c484e5f"},
+    {file = "pymdown_extensions-10.15.tar.gz", hash = "sha256:0e5994e32155f4b03504f939e501b981d306daf7ec2aa1cd2eb6bd300784f8f7"},
 ]
 
 [package.dependencies]
-markdown = ">=3.5"
+markdown = ">=3.6"
 pyyaml = "*"
 
 [package.extras]
-extra = ["pygments (>=2.12)"]
+extra = ["pygments (>=2.19.1)"]
+
+[[package]]
+name = "pyproject-api"
+version = "1.9.1"
+description = "API to interact with the python pyproject.toml based projects"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
+files = [
+    {file = "pyproject_api-1.9.1-py3-none-any.whl", hash = "sha256:7d6238d92f8962773dd75b5f0c4a6a27cce092a14b623b811dba656f3b628948"},
+    {file = "pyproject_api-1.9.1.tar.gz", hash = "sha256:43c9918f49daab37e302038fc1aed54a8c7a91a9fa935d00b9a485f37e0f5335"},
+]
+
+[package.dependencies]
+packaging = ">=25"
+tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=3.2)"]
+testing = ["covdefaults (>=2.3)", "pytest (>=8.3.5)", "pytest-cov (>=6.1.1)", "pytest-mock (>=3.14)", "setuptools (>=80.3.1)"]
 
 [[package]]
 name = "pytest"
-version = "7.4.4"
+version = "8.4.0"
 description = "pytest: simple powerful testing with Python"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
+groups = ["test"]
 files = [
-    {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
-    {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
+    {file = "pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e"},
+    {file = "pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6"},
 ]
 
 [package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
+iniconfig = ">=1"
+packaging = ">=20"
+pluggy = ">=1.5,<2"
+pygments = ">=2.7.2"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
 
 [package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
 
 [[package]]
 name = "pytest-cov"
-version = "4.1.0"
+version = "6.1.1"
 description = "Pytest plugin for measuring coverage."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
+groups = ["test"]
 files = [
-    {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
-    {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
+    {file = "pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde"},
+    {file = "pytest_cov-6.1.1.tar.gz", hash = "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a"},
 ]
 
 [package.dependencies]
-coverage = {version = ">=5.2.1", extras = ["toml"]}
+coverage = {version = ">=7.5", extras = ["toml"]}
 pytest = ">=4.6"
 
 [package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
 
 [[package]]
 name = "pytest-datadir"
-version = "1.5.0"
+version = "1.7.2"
 description = "pytest plugin for test data directories and files"
 optional = false
 python-versions = ">=3.8"
+groups = ["test"]
 files = [
-    {file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"},
-    {file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"},
+    {file = "pytest_datadir-1.7.2-py3-none-any.whl", hash = "sha256:8392ba0e9eaf37030e663dcd91cc5123dec99c44300f0c5eac44f35f13f0e086"},
+    {file = "pytest_datadir-1.7.2.tar.gz", hash = "sha256:15f5228a35d0a3205e4968e75d3b9cca91762424e1eafc21eb637d380a48443e"},
 ]
 
 [package.dependencies]
-pytest = ">=5.0"
+pytest = ">=7.0"
+
+[package.extras]
+dev = ["pre-commit", "pytest-datadir[testing]"]
+testing = ["pytest", "tox"]
 
 [[package]]
 name = "pytest-freezer"
-version = "0.4.8"
+version = "0.4.9"
 description = "Pytest plugin providing a fixture interface for spulec/freezegun"
 optional = false
-python-versions = ">= 3.6"
+python-versions = ">=3.6"
+groups = ["test"]
 files = [
-    {file = "pytest_freezer-0.4.8-py3-none-any.whl", hash = "sha256:644ce7ddb8ba52b92a1df0a80a699bad2b93514c55cf92e9f2517b68ebe74814"},
-    {file = "pytest_freezer-0.4.8.tar.gz", hash = "sha256:8ee2f724b3ff3540523fa355958a22e6f4c1c819928b78a7a183ae4248ce6ee6"},
+    {file = "pytest_freezer-0.4.9-py3-none-any.whl", hash = "sha256:8b6c50523b7d4aec4590b52bfa5ff766d772ce506e2bf4846c88041ea9ccae59"},
+    {file = "pytest_freezer-0.4.9.tar.gz", hash = "sha256:21bf16bc9cc46bf98f94382c4b5c3c389be7056ff0be33029111ae11b3f1c82a"},
 ]
 
 [package.dependencies]
-freezegun = ">=1.0"
+freezegun = ">=1.1"
 pytest = ">=3.6"
 
 [[package]]
 name = "pytest-mock"
-version = "3.12.0"
+version = "3.14.1"
 description = "Thin-wrapper around the mock package for easier use with pytest"
 optional = false
 python-versions = ">=3.8"
+groups = ["test"]
 files = [
-    {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"},
-    {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"},
+    {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"},
+    {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"},
 ]
 
 [package.dependencies]
-pytest = ">=5.0"
+pytest = ">=6.2.5"
 
 [package.extras]
 dev = ["pre-commit", "pytest-asyncio", "tox"]
 
 [[package]]
 name = "pytest-regressions"
-version = "2.5.0"
+version = "2.8.0"
 description = "Easy to use fixtures to write regression tests."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["test"]
 files = [
-    {file = "pytest-regressions-2.5.0.tar.gz", hash = "sha256:818c7884c1cff3babf89eebb02cbc27b307856b1985427c24d52cb812a106fd9"},
-    {file = "pytest_regressions-2.5.0-py3-none-any.whl", hash = "sha256:8c4e5c4037325fdb0825bc1fdcb75e17e03adf3407049f0cb704bb996d496255"},
+    {file = "pytest_regressions-2.8.0-py3-none-any.whl", hash = "sha256:2926f37efa5fd6793806b10352e274c5284a5469a845aeab6243e86f9214766f"},
+    {file = "pytest_regressions-2.8.0.tar.gz", hash = "sha256:775044e17117f5427df2caad3ab1c66889abe770a0ce2bc3f24fdeac99af76fe"},
 ]
 
 [package.dependencies]
 pytest = ">=6.2.0"
-pytest-datadir = ">=1.2.0"
+pytest-datadir = ">=1.7.0"
 pyyaml = "*"
 
 [package.extras]
 dataframe = ["numpy", "pandas"]
-dev = ["matplotlib", "mypy", "numpy", "pandas", "pillow", "pre-commit", "restructuredtext-lint", "tox"]
+dev = ["matplotlib", "mypy", "numpy", "pandas", "pillow", "pre-commit", "pyarrow", "restructuredtext-lint", "tox"]
 image = ["numpy", "pillow"]
 num = ["numpy", "pandas"]
 
 [[package]]
 name = "pytest-xdist"
-version = "3.5.0"
+version = "3.7.0"
 description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.9"
+groups = ["test"]
 files = [
-    {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"},
-    {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"},
+    {file = "pytest_xdist-3.7.0-py3-none-any.whl", hash = "sha256:7d3fbd255998265052435eb9daa4e99b62e6fb9cfb6efd1f858d4d8c0c7f0ca0"},
+    {file = "pytest_xdist-3.7.0.tar.gz", hash = "sha256:f9248c99a7c15b7d2f90715df93610353a485827bc06eefb6566d23f6400f126"},
 ]
 
 [package.dependencies]
-execnet = ">=1.1"
-pytest = ">=6.2.0"
+execnet = ">=2.1"
+pytest = ">=7.0.0"
 
 [package.extras]
 psutil = ["psutil (>=3.0)"]
@@ -1176,97 +1341,92 @@ testing = ["filelock"]
 
 [[package]]
 name = "python-dateutil"
-version = "2.8.2"
+version = "2.9.0.post0"
 description = "Extensions to the standard Python datetime module"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["documentation", "test"]
 files = [
-    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
-    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+    {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
+    {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
 ]
 
 [package.dependencies]
 six = ">=1.5"
 
-[[package]]
-name = "pytz"
-version = "2023.3.post1"
-description = "World timezone definitions, modern and historical"
-optional = false
-python-versions = "*"
-files = [
-    {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
-    {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
-]
-
 [[package]]
 name = "pyyaml"
-version = "6.0.1"
+version = "6.0.2"
 description = "YAML parser and emitter for Python"
 optional = false
-python-versions = ">=3.6"
-files = [
-    {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
-    {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
-    {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
-    {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
-    {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
-    {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
-    {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
-    {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
-    {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
-    {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
-    {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
-    {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
-    {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
-    {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
-    {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
-    {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
-    {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
-    {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
-    {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
-    {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
-    {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
-    {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
-    {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
-    {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
-    {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
-    {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
-    {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
-    {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
-    {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
-    {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
-    {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
-    {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
-    {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
-    {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
-    {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
-    {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
-    {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
-    {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
-    {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
-    {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
-    {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
-    {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
-    {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
-    {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
-    {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
-    {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
-    {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
-    {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
-    {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
-    {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+python-versions = ">=3.8"
+groups = ["main", "dev", "documentation", "linters", "test"]
+files = [
+    {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
+    {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
+    {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
+    {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
+    {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
+    {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
+    {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
+    {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
+    {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
+    {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
+    {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
+    {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
+    {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
+    {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
+    {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
+    {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
+    {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
+    {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
+    {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
+    {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
+    {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
+    {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
+    {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
+    {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
+    {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
+    {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
+    {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
+    {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
+    {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
+    {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
+    {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
+    {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
+    {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
+    {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
+    {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
+    {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
+    {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
+    {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
+    {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
+    {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
+    {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
+    {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
+    {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
+    {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
+    {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
+    {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
+    {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
+    {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
+    {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
+    {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
+    {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
+    {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
+    {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
 ]
 
 [[package]]
 name = "pyyaml-env-tag"
-version = "0.1"
-description = "A custom YAML tag for referencing environment variables in YAML files. "
+version = "1.1"
+description = "A custom YAML tag for referencing environment variables in YAML files."
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.9"
+groups = ["documentation"]
 files = [
-    {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
-    {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
+    {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"},
+    {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"},
 ]
 
 [package.dependencies]
@@ -1274,124 +1434,29 @@ pyyaml = "*"
 
 [[package]]
 name = "questionary"
-version = "2.0.1"
+version = "2.1.0"
 description = "Python library to build pretty command line user prompts ⭐️"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
 files = [
-    {file = "questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2"},
-    {file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"},
+    {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"},
+    {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"},
 ]
 
 [package.dependencies]
-prompt_toolkit = ">=2.0,<=3.0.36"
-
-[[package]]
-name = "regex"
-version = "2023.10.3"
-description = "Alternative regular expression module, to replace re."
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "regex-2023.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c34d4f73ea738223a094d8e0ffd6d2c1a1b4c175da34d6b0de3d8d69bee6bcc"},
-    {file = "regex-2023.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8f4e49fc3ce020f65411432183e6775f24e02dff617281094ba6ab079ef0915"},
-    {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cd1bccf99d3ef1ab6ba835308ad85be040e6a11b0977ef7ea8c8005f01a3c29"},
-    {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81dce2ddc9f6e8f543d94b05d56e70d03a0774d32f6cca53e978dc01e4fc75b8"},
-    {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c6b4d23c04831e3ab61717a707a5d763b300213db49ca680edf8bf13ab5d91b"},
-    {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c15ad0aee158a15e17e0495e1e18741573d04eb6da06d8b84af726cfc1ed02ee"},
-    {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6239d4e2e0b52c8bd38c51b760cd870069f0bdf99700a62cd509d7a031749a55"},
-    {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4a8bf76e3182797c6b1afa5b822d1d5802ff30284abe4599e1247be4fd6b03be"},
-    {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9c727bbcf0065cbb20f39d2b4f932f8fa1631c3e01fcedc979bd4f51fe051c5"},
-    {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3ccf2716add72f80714b9a63899b67fa711b654be3fcdd34fa391d2d274ce767"},
-    {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:107ac60d1bfdc3edb53be75e2a52aff7481b92817cfdddd9b4519ccf0e54a6ff"},
-    {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:00ba3c9818e33f1fa974693fb55d24cdc8ebafcb2e4207680669d8f8d7cca79a"},
-    {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0a47efb1dbef13af9c9a54a94a0b814902e547b7f21acb29434504d18f36e3a"},
-    {file = "regex-2023.10.3-cp310-cp310-win32.whl", hash = "sha256:36362386b813fa6c9146da6149a001b7bd063dabc4d49522a1f7aa65b725c7ec"},
-    {file = "regex-2023.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:c65a3b5330b54103e7d21cac3f6bf3900d46f6d50138d73343d9e5b2900b2353"},
-    {file = "regex-2023.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90a79bce019c442604662d17bf69df99090e24cdc6ad95b18b6725c2988a490e"},
-    {file = "regex-2023.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c7964c2183c3e6cce3f497e3a9f49d182e969f2dc3aeeadfa18945ff7bdd7051"},
-    {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef80829117a8061f974b2fda8ec799717242353bff55f8a29411794d635d964"},
-    {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5addc9d0209a9afca5fc070f93b726bf7003bd63a427f65ef797a931782e7edc"},
-    {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c148bec483cc4b421562b4bcedb8e28a3b84fcc8f0aa4418e10898f3c2c0eb9b"},
-    {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d1f21af4c1539051049796a0f50aa342f9a27cde57318f2fc41ed50b0dbc4ac"},
-    {file = "regex-2023.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b9ac09853b2a3e0d0082104036579809679e7715671cfbf89d83c1cb2a30f58"},
-    {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ebedc192abbc7fd13c5ee800e83a6df252bec691eb2c4bedc9f8b2e2903f5e2a"},
-    {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d8a993c0a0ffd5f2d3bda23d0cd75e7086736f8f8268de8a82fbc4bd0ac6791e"},
-    {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:be6b7b8d42d3090b6c80793524fa66c57ad7ee3fe9722b258aec6d0672543fd0"},
-    {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4023e2efc35a30e66e938de5aef42b520c20e7eda7bb5fb12c35e5d09a4c43f6"},
-    {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d47840dc05e0ba04fe2e26f15126de7c755496d5a8aae4a08bda4dd8d646c54"},
-    {file = "regex-2023.10.3-cp311-cp311-win32.whl", hash = "sha256:9145f092b5d1977ec8c0ab46e7b3381b2fd069957b9862a43bd383e5c01d18c2"},
-    {file = "regex-2023.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:b6104f9a46bd8743e4f738afef69b153c4b8b592d35ae46db07fc28ae3d5fb7c"},
-    {file = "regex-2023.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff507ae210371d4b1fe316d03433ac099f184d570a1a611e541923f78f05037"},
-    {file = "regex-2023.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be5e22bbb67924dea15039c3282fa4cc6cdfbe0cbbd1c0515f9223186fc2ec5f"},
-    {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a992f702c9be9c72fa46f01ca6e18d131906a7180950958f766c2aa294d4b41"},
-    {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7434a61b158be563c1362d9071358f8ab91b8d928728cd2882af060481244c9e"},
-    {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2169b2dcabf4e608416f7f9468737583ce5f0a6e8677c4efbf795ce81109d7c"},
-    {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9e908ef5889cda4de038892b9accc36d33d72fb3e12c747e2799a0e806ec841"},
-    {file = "regex-2023.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12bd4bc2c632742c7ce20db48e0d99afdc05e03f0b4c1af90542e05b809a03d9"},
-    {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc72c231f5449d86d6c7d9cc7cd819b6eb30134bb770b8cfdc0765e48ef9c420"},
-    {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bce8814b076f0ce5766dc87d5a056b0e9437b8e0cd351b9a6c4e1134a7dfbda9"},
-    {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ba7cd6dc4d585ea544c1412019921570ebd8a597fabf475acc4528210d7c4a6f"},
-    {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b0c7d2f698e83f15228ba41c135501cfe7d5740181d5903e250e47f617eb4292"},
-    {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5a8f91c64f390ecee09ff793319f30a0f32492e99f5dc1c72bc361f23ccd0a9a"},
-    {file = "regex-2023.10.3-cp312-cp312-win32.whl", hash = "sha256:ad08a69728ff3c79866d729b095872afe1e0557251da4abb2c5faff15a91d19a"},
-    {file = "regex-2023.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:39cdf8d141d6d44e8d5a12a8569d5a227f645c87df4f92179bd06e2e2705e76b"},
-    {file = "regex-2023.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4a3ee019a9befe84fa3e917a2dd378807e423d013377a884c1970a3c2792d293"},
-    {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76066d7ff61ba6bf3cb5efe2428fc82aac91802844c022d849a1f0f53820502d"},
-    {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe50b61bab1b1ec260fa7cd91106fa9fece57e6beba05630afe27c71259c59b"},
-    {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fd88f373cb71e6b59b7fa597e47e518282455c2734fd4306a05ca219a1991b0"},
-    {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ab05a182c7937fb374f7e946f04fb23a0c0699c0450e9fb02ef567412d2fa3"},
-    {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dac37cf08fcf2094159922edc7a2784cfcc5c70f8354469f79ed085f0328ebdf"},
-    {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e54ddd0bb8fb626aa1f9ba7b36629564544954fff9669b15da3610c22b9a0991"},
-    {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3367007ad1951fde612bf65b0dffc8fd681a4ab98ac86957d16491400d661302"},
-    {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:16f8740eb6dbacc7113e3097b0a36065a02e37b47c936b551805d40340fb9971"},
-    {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:f4f2ca6df64cbdd27f27b34f35adb640b5d2d77264228554e68deda54456eb11"},
-    {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:39807cbcbe406efca2a233884e169d056c35aa7e9f343d4e78665246a332f597"},
-    {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7eece6fbd3eae4a92d7c748ae825cbc1ee41a89bb1c3db05b5578ed3cfcfd7cb"},
-    {file = "regex-2023.10.3-cp37-cp37m-win32.whl", hash = "sha256:ce615c92d90df8373d9e13acddd154152645c0dc060871abf6bd43809673d20a"},
-    {file = "regex-2023.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0f649fa32fe734c4abdfd4edbb8381c74abf5f34bc0b3271ce687b23729299ed"},
-    {file = "regex-2023.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b98b7681a9437262947f41c7fac567c7e1f6eddd94b0483596d320092004533"},
-    {file = "regex-2023.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:91dc1d531f80c862441d7b66c4505cd6ea9d312f01fb2f4654f40c6fdf5cc37a"},
-    {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82fcc1f1cc3ff1ab8a57ba619b149b907072e750815c5ba63e7aa2e1163384a4"},
-    {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7979b834ec7a33aafae34a90aad9f914c41fd6eaa8474e66953f3f6f7cbd4368"},
-    {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef71561f82a89af6cfcbee47f0fabfdb6e63788a9258e913955d89fdd96902ab"},
-    {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd829712de97753367153ed84f2de752b86cd1f7a88b55a3a775eb52eafe8a94"},
-    {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00e871d83a45eee2f8688d7e6849609c2ca2a04a6d48fba3dff4deef35d14f07"},
-    {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:706e7b739fdd17cb89e1fbf712d9dc21311fc2333f6d435eac2d4ee81985098c"},
-    {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cc3f1c053b73f20c7ad88b0d1d23be7e7b3901229ce89f5000a8399746a6e039"},
-    {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f85739e80d13644b981a88f529d79c5bdf646b460ba190bffcaf6d57b2a9863"},
-    {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:741ba2f511cc9626b7561a440f87d658aabb3d6b744a86a3c025f866b4d19e7f"},
-    {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e77c90ab5997e85901da85131fd36acd0ed2221368199b65f0d11bca44549711"},
-    {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:979c24cbefaf2420c4e377ecd1f165ea08cc3d1fbb44bdc51bccbbf7c66a2cb4"},
-    {file = "regex-2023.10.3-cp38-cp38-win32.whl", hash = "sha256:58837f9d221744d4c92d2cf7201c6acd19623b50c643b56992cbd2b745485d3d"},
-    {file = "regex-2023.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:c55853684fe08d4897c37dfc5faeff70607a5f1806c8be148f1695be4a63414b"},
-    {file = "regex-2023.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c54e23836650bdf2c18222c87f6f840d4943944146ca479858404fedeb9f9af"},
-    {file = "regex-2023.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69c0771ca5653c7d4b65203cbfc5e66db9375f1078689459fe196fe08b7b4930"},
-    {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ac965a998e1388e6ff2e9781f499ad1eaa41e962a40d11c7823c9952c77123e"},
-    {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c0e8fae5b27caa34177bdfa5a960c46ff2f78ee2d45c6db15ae3f64ecadde14"},
-    {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c56c3d47da04f921b73ff9415fbaa939f684d47293f071aa9cbb13c94afc17d"},
-    {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef1e014eed78ab650bef9a6a9cbe50b052c0aebe553fb2881e0453717573f52"},
-    {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d29338556a59423d9ff7b6eb0cb89ead2b0875e08fe522f3e068b955c3e7b59b"},
-    {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9c6d0ced3c06d0f183b73d3c5920727268d2201aa0fe6d55c60d68c792ff3588"},
-    {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:994645a46c6a740ee8ce8df7911d4aee458d9b1bc5639bc968226763d07f00fa"},
-    {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:66e2fe786ef28da2b28e222c89502b2af984858091675044d93cb50e6f46d7af"},
-    {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:11175910f62b2b8c055f2b089e0fedd694fe2be3941b3e2633653bc51064c528"},
-    {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:06e9abc0e4c9ab4779c74ad99c3fc10d3967d03114449acc2c2762ad4472b8ca"},
-    {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fb02e4257376ae25c6dd95a5aec377f9b18c09be6ebdefa7ad209b9137b73d48"},
-    {file = "regex-2023.10.3-cp39-cp39-win32.whl", hash = "sha256:3b2c3502603fab52d7619b882c25a6850b766ebd1b18de3df23b2f939360e1bd"},
-    {file = "regex-2023.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:adbccd17dcaff65704c856bd29951c58a1bd4b2b0f8ad6b826dbd543fe740988"},
-    {file = "regex-2023.10.3.tar.gz", hash = "sha256:3fef4f844d2290ee0ba57addcec17eec9e3df73f10a2748485dfd6a3a188cc0f"},
-]
+prompt_toolkit = ">=2.0,<4.0"
 
 [[package]]
 name = "requests"
-version = "2.31.0"
+version = "2.32.3"
 description = "Python HTTP for Humans."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["documentation"]
 files = [
-    {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
-    {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
+    {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
+    {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
 ]
 
 [package.dependencies]
@@ -1405,56 +1470,63 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"]
 use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 
 [[package]]
-name = "ruff"
-version = "0.1.12"
-description = "An extremely fast Python linter and code formatter, written in Rust."
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "ruff-0.1.12-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:544038693543c11edc56bb94a9875df2dc249e3616f90c15964c720dcccf0745"},
-    {file = "ruff-0.1.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8a0e3ef6299c4eab75a7740730e4b4bd4a36e0bd8102ded01553403cad088fd4"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f6d939461e3273f10f4cd059fd0b83c249d73f1736032fffbac83a62939395"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25be18abc1fc3f3d3fb55855c41ed5d52063316defde202f413493bb3888218c"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d41e9f100b50526d80b076fc9c103c729387ff3f10f63606ed1038c30a372a40"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:472a0548738d4711549c7874b43fab61aacafb1fede29c5232d4cfb8e2d13f69"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46685ef2f106b827705df876d38617741ed4f858bbdbc0817f94476c45ab6669"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf6073749c70b616d7929897b14824ec6713a6c3a8195dfd2ffdcc66594d880c"},
-    {file = "ruff-0.1.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bdf26e5a2efab4c3aaf6b61648ea47a525dc12775810a85c285dc9ca03e5ac0"},
-    {file = "ruff-0.1.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b631c6a95e4b6d5c4299e599067b5a89f5b18e2f2d9a6c22b879b3c4b077c96e"},
-    {file = "ruff-0.1.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f193f460e231e63af5fc7516897cf5ab257cbda72ae83cf9a654f1c80c3b758a"},
-    {file = "ruff-0.1.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:718523c3a0b787590511f212d30cc9b194228ef369c8bdd72acd1282cc27c468"},
-    {file = "ruff-0.1.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1c49e826de55d81a6ef93808b760925e492bad7cc470aaa114a3be158b2c7f99"},
-    {file = "ruff-0.1.12-py3-none-win32.whl", hash = "sha256:fbb1c002eeacb60161e51d77b2274c968656599477a1c8c65066953276e8ee2b"},
-    {file = "ruff-0.1.12-py3-none-win_amd64.whl", hash = "sha256:7fe06ba77e5b7b78db1d058478c47176810f69bb5be7c1b0d06876af59198203"},
-    {file = "ruff-0.1.12-py3-none-win_arm64.whl", hash = "sha256:bb29f8e3e6c95024902eaec5a9ce1fd5ac4e77f4594f4554e67fbb0f6d9a2f37"},
-    {file = "ruff-0.1.12.tar.gz", hash = "sha256:97189f38c655e573f6bea0d12e9f18aad5539fd08ab50651449450999f45383a"},
-]
-
-[[package]]
-name = "setuptools"
-version = "69.0.2"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
+name = "rich"
+version = "13.9.4"
+description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.8.0"
+groups = ["script"]
 files = [
-    {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"},
-    {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"},
+    {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
+    {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"},
 ]
 
+[package.dependencies]
+markdown-it-py = ">=2.2.0"
+pygments = ">=2.13.0,<3.0.0"
+typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""}
+
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+jupyter = ["ipywidgets (>=7.5.1,<9)"]
+
+[[package]]
+name = "ruff"
+version = "0.11.13"
+description = "An extremely fast Python linter and code formatter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+groups = ["linters"]
+files = [
+    {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"},
+    {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"},
+    {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"},
+    {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"},
+    {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"},
+    {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"},
+    {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"},
+    {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"},
+    {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"},
+]
 
 [[package]]
 name = "six"
-version = "1.16.0"
+version = "1.17.0"
 description = "Python 2 and 3 compatibility utilities"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["documentation", "test"]
 files = [
-    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
-    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+    {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
+    {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
 ]
 
 [[package]]
@@ -1463,6 +1535,7 @@ version = "0.6.3"
 description = "Extract data from python stack frames and tracebacks for informative displays"
 optional = false
 python-versions = "*"
+groups = ["dev"]
 files = [
     {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
     {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
@@ -1478,13 +1551,14 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
 
 [[package]]
 name = "termcolor"
-version = "2.4.0"
+version = "3.1.0"
 description = "ANSI color formatting for output in terminal"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["main"]
 files = [
-    {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"},
-    {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"},
+    {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"},
+    {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"},
 ]
 
 [package.extras]
@@ -1492,72 +1566,149 @@ tests = ["pytest", "pytest-cov"]
 
 [[package]]
 name = "tomli"
-version = "2.0.1"
+version = "2.2.1"
 description = "A lil' TOML parser"
 optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
-    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
+python-versions = ">=3.8"
+groups = ["dev", "linters", "test"]
+files = [
+    {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
+    {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
+    {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
+    {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
+    {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
+    {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
+    {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
+    {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
+    {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
+    {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
+    {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
+    {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
+    {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
+    {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
+    {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
+    {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
+    {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
+    {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
+    {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
+    {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
+    {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
+    {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
+    {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
+    {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
+    {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
+    {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
+    {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
+    {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
+    {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
+    {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
+    {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
+    {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
+]
+markers = {dev = "python_version < \"3.11\"", linters = "python_version < \"3.11\"", test = "python_full_version <= \"3.11.0a6\""}
 
 [[package]]
 name = "tomlkit"
-version = "0.12.3"
+version = "0.13.3"
 description = "Style preserving TOML library"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["main"]
+files = [
+    {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"},
+    {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"},
+]
+
+[[package]]
+name = "tox"
+version = "4.26.0"
+description = "tox is a generic virtualenv management and test command line tool"
+optional = false
+python-versions = ">=3.9"
+groups = ["dev"]
 files = [
-    {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"},
-    {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"},
+    {file = "tox-4.26.0-py3-none-any.whl", hash = "sha256:75f17aaf09face9b97bd41645028d9f722301e912be8b4c65a3f938024560224"},
+    {file = "tox-4.26.0.tar.gz", hash = "sha256:a83b3b67b0159fa58e44e646505079e35a43317a62d2ae94725e0586266faeca"},
 ]
 
+[package.dependencies]
+cachetools = ">=5.5.1"
+chardet = ">=5.2"
+colorama = ">=0.4.6"
+filelock = ">=3.16.1"
+packaging = ">=24.2"
+platformdirs = ">=4.3.6"
+pluggy = ">=1.5"
+pyproject-api = ">=1.8"
+tomli = {version = ">=2.2.1", markers = "python_version < \"3.11\""}
+typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""}
+virtualenv = ">=20.31"
+
+[package.extras]
+test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.4)", "pytest-mock (>=3.14)"]
+
 [[package]]
 name = "traitlets"
-version = "5.14.0"
+version = "5.14.3"
 description = "Traitlets Python configuration system"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
 files = [
-    {file = "traitlets-5.14.0-py3-none-any.whl", hash = "sha256:f14949d23829023013c47df20b4a76ccd1a85effb786dc060f34de7948361b33"},
-    {file = "traitlets-5.14.0.tar.gz", hash = "sha256:fcdaa8ac49c04dfa0ed3ee3384ef6dfdb5d6f3741502be247279407679296772"},
+    {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
+    {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
 ]
 
 [package.extras]
 docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"]
+test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
+
+[[package]]
+name = "types-colorama"
+version = "0.4.15.20240311"
+description = "Typing stubs for colorama"
+optional = false
+python-versions = ">=3.8"
+groups = ["linters"]
+files = [
+    {file = "types-colorama-0.4.15.20240311.tar.gz", hash = "sha256:a28e7f98d17d2b14fb9565d32388e419f4108f557a7d939a66319969b2b99c7a"},
+    {file = "types_colorama-0.4.15.20240311-py3-none-any.whl", hash = "sha256:6391de60ddc0db3f147e31ecb230006a6823e81e380862ffca1e4695c13a0b8e"},
+]
 
 [[package]]
 name = "types-deprecated"
-version = "1.2.9.20240106"
+version = "1.2.15.20250304"
 description = "Typing stubs for Deprecated"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["linters"]
 files = [
-    {file = "types-Deprecated-1.2.9.20240106.tar.gz", hash = "sha256:afeb819e9a03d0a5795f18c88fe6207c48ed13c639e93281bd9d9b7bb6d34310"},
-    {file = "types_Deprecated-1.2.9.20240106-py3-none-any.whl", hash = "sha256:9dcb258493b5be407574ee21e50ddac9e429072d39b576126bf1ac00764fb9a8"},
+    {file = "types_deprecated-1.2.15.20250304-py3-none-any.whl", hash = "sha256:86a65aa550ea8acf49f27e226b8953288cd851de887970fbbdf2239c116c3107"},
+    {file = "types_deprecated-1.2.15.20250304.tar.gz", hash = "sha256:c329030553029de5cc6cb30f269c11f4e00e598c4241290179f63cda7d33f719"},
 ]
 
 [[package]]
 name = "types-python-dateutil"
-version = "2.8.19.20240106"
+version = "2.9.0.20250516"
 description = "Typing stubs for python-dateutil"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["linters"]
 files = [
-    {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"},
-    {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"},
+    {file = "types_python_dateutil-2.9.0.20250516-py3-none-any.whl", hash = "sha256:2b2b3f57f9c6a61fba26a9c0ffb9ea5681c9b83e69cd897c6b5f668d9c0cab93"},
+    {file = "types_python_dateutil-2.9.0.20250516.tar.gz", hash = "sha256:13e80d6c9c47df23ad773d54b2826bd52dbbb41be87c3f339381c1700ad21ee5"},
 ]
 
 [[package]]
 name = "types-pyyaml"
-version = "6.0.12.12"
+version = "6.0.12.20250516"
 description = "Typing stubs for PyYAML"
 optional = false
-python-versions = "*"
+python-versions = ">=3.9"
+groups = ["linters"]
 files = [
-    {file = "types-PyYAML-6.0.12.12.tar.gz", hash = "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062"},
-    {file = "types_PyYAML-6.0.12.12-py3-none-any.whl", hash = "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24"},
+    {file = "types_pyyaml-6.0.12.20250516-py3-none-any.whl", hash = "sha256:8478208feaeb53a34cb5d970c56a7cd76b72659442e733e268a94dc72b2d0530"},
+    {file = "types_pyyaml-6.0.12.20250516.tar.gz", hash = "sha256:9f21a70216fc0fa1b216a8176db5f9e0af6eb35d2f2932acb87689d03a5bf6ba"},
 ]
 
 [[package]]
@@ -1566,6 +1717,7 @@ version = "0.1.1"
 description = "Typing stubs for termcolor"
 optional = false
 python-versions = "*"
+groups = ["linters"]
 files = [
     {file = "types-termcolor-0.1.1.tar.gz", hash = "sha256:4d9e09ce7f3267985f5280b22e25790c98cb64628b6466e1fb915dbb52ad7136"},
     {file = "types_termcolor-0.1.1-py2.py3-none-any.whl", hash = "sha256:3694c312e32f71fdc0f469c334ea21645f3130d90c93cd53bcb06b1233e174d5"},
@@ -1573,40 +1725,45 @@ files = [
 
 [[package]]
 name = "typing-extensions"
-version = "4.9.0"
-description = "Backported and Experimental Type Hints for Python 3.8+"
+version = "4.14.0"
+description = "Backported and Experimental Type Hints for Python 3.9+"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["main", "dev", "linters", "script", "test"]
 files = [
-    {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
-    {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
+    {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"},
+    {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"},
 ]
+markers = {main = "python_version < \"3.11\"", dev = "python_version < \"3.11\"", script = "python_version < \"3.11\"", test = "python_version < \"3.11\""}
 
 [[package]]
 name = "urllib3"
-version = "2.1.0"
+version = "2.4.0"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["documentation"]
 files = [
-    {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"},
-    {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"},
+    {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"},
+    {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"},
 ]
 
 [package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
+h2 = ["h2 (>=4,<5)"]
 socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
 zstd = ["zstandard (>=0.18.0)"]
 
 [[package]]
 name = "virtualenv"
-version = "20.25.0"
+version = "20.31.2"
 description = "Virtual Python Environment builder"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
+groups = ["dev", "linters"]
 files = [
-    {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
-    {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
+    {file = "virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11"},
+    {file = "virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af"},
 ]
 
 [package.dependencies]
@@ -1615,43 +1772,47 @@ filelock = ">=3.12.2,<4"
 platformdirs = ">=3.9.1,<5"
 
 [package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
 
 [[package]]
 name = "watchdog"
-version = "3.0.0"
+version = "6.0.0"
 description = "Filesystem events monitoring"
 optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"},
-    {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"},
-    {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"},
-    {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"},
-    {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"},
-    {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"},
-    {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"},
-    {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"},
-    {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"},
-    {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"},
-    {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"},
-    {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"},
-    {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"},
-    {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"},
-    {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"},
-    {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"},
-    {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"},
-    {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"},
-    {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"},
-    {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"},
-    {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"},
+python-versions = ">=3.9"
+groups = ["documentation"]
+files = [
+    {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"},
+    {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"},
+    {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"},
+    {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"},
+    {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"},
+    {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"},
+    {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"},
+    {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"},
+    {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"},
+    {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"},
+    {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"},
+    {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"},
+    {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"},
+    {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"},
+    {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"},
+    {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"},
+    {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"},
+    {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"},
+    {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"},
+    {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"},
+    {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"},
+    {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"},
+    {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"},
+    {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"},
 ]
 
 [package.extras]
@@ -1659,110 +1820,127 @@ watchmedo = ["PyYAML (>=3.10)"]
 
 [[package]]
 name = "wcwidth"
-version = "0.2.12"
+version = "0.2.13"
 description = "Measures the displayed width of unicode strings in a terminal"
 optional = false
 python-versions = "*"
+groups = ["main", "dev"]
 files = [
-    {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"},
-    {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"},
+    {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
+    {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
 ]
 
 [[package]]
 name = "wrapt"
-version = "1.16.0"
+version = "1.17.2"
 description = "Module for decorators, wrappers and monkey patching."
 optional = false
-python-versions = ">=3.6"
-files = [
-    {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
-    {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
-    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
-    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
-    {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
-    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
-    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
-    {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
-    {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
-    {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
-    {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
-    {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
-    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
-    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
-    {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
-    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
-    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
-    {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
-    {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
-    {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
-    {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
-    {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
-    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
-    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
-    {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
-    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
-    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
-    {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
-    {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
-    {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
-    {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
-    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
-    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
-    {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
-    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
-    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
-    {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
-    {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
-    {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
-    {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
-    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
-    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
-    {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
-    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
-    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
-    {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
-    {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
-    {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
-    {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
-    {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
-    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
-    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
-    {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
-    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
-    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
-    {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
-    {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
-    {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
-    {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
-    {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
-    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
-    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
-    {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
-    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
-    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
-    {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
-    {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
-    {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
-    {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
-    {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
+python-versions = ">=3.8"
+groups = ["test"]
+files = [
+    {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"},
+    {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"},
+    {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"},
+    {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"},
+    {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"},
+    {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"},
+    {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"},
+    {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"},
+    {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"},
+    {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"},
+    {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"},
+    {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"},
+    {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"},
+    {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"},
+    {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"},
+    {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"},
+    {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"},
+    {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"},
+    {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"},
+    {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"},
+    {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"},
+    {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"},
+    {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"},
+    {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"},
+    {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"},
+    {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"},
+    {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"},
+    {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"},
+    {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"},
+    {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"},
+    {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"},
+    {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"},
+    {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"},
+    {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"},
+    {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"},
+    {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"},
+    {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"},
+    {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"},
+    {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"},
+    {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"},
+    {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"},
+    {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"},
+    {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"},
+    {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"},
+    {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"},
+    {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"},
+    {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"},
+    {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"},
+    {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"},
+    {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"},
+    {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"},
+    {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"},
+    {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"},
+    {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"},
+    {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"},
+    {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"},
+    {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"},
+    {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"},
+    {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"},
+    {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"},
+    {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"},
+    {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"},
+    {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"},
+    {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"},
+    {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"},
+    {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"},
+    {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"},
+    {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"},
+    {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"},
+    {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"},
+    {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"},
+    {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"},
+    {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"},
+    {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"},
+    {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"},
+    {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"},
+    {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"},
+    {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"},
+    {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"},
 ]
 
 [[package]]
 name = "zipp"
-version = "3.17.0"
+version = "3.22.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
+groups = ["main", "documentation"]
+markers = "python_version == \"3.9\""
 files = [
-    {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
-    {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
+    {file = "zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343"},
+    {file = "zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5"},
 ]
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
+check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
+cover = ["pytest-cov"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+enabler = ["pytest-enabler (>=2.2)"]
+test = ["big-O", "importlib_resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
+type = ["pytest-mypy"]
 
 [metadata]
-lock-version = "2.0"
-python-versions = ">=3.8"
-content-hash = "a5c0fbba4345989709fd201b2755ec9445972072108b5962666f41d451c7e6e2"
+lock-version = "2.1"
+python-versions = ">=3.9,<4.0"
+content-hash = "c98af83d4ce726bbfba04eea3de90e642fb7bdb08bedf0bf1b00b92a1b140e76"
diff --git a/pyproject.toml b/pyproject.toml
index b644753b1b..ab3ba39ee1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,21 +1,31 @@
-[tool.commitizen]
-version = "3.13.0"
-tag_format = "v$version"
-version_files = [
-  "pyproject.toml:version",
-  "commitizen/__version__.py",
-  ".pre-commit-config.yaml:rev:.+Commitizen",
-]
-
-[tool.poetry]
+[project]
 name = "commitizen"
-version = "3.13.0"
+version = "4.8.3"
 description = "Python commitizen client tool"
-authors = ["Santiago Fraire <santiwilly@gmail.com>"]
-license = "MIT"
-keywords = ["commitizen", "conventional", "commits", "git"]
+authors = [{ name = "Santiago Fraire", email = "santiwilly@gmail.com" }]
+maintainers = [
+    { name = "Wei Lee", email = "weilee.rx@gmail.com" },
+    { name = "Axel H.", email = "noirbizarre@gmail.com" },
+]
+license = { file = "LICENSE" }
 readme = "docs/README.md"
-homepage = "https://github.com/commitizen-tools/commitizen"
+requires-python = ">=3.9,<4.0"
+dependencies = [
+    "questionary (>=2.0,<3.0)",
+    "decli (>=0.6.0,<1.0)",
+    "colorama (>=0.4.1,<1.0)",
+    "termcolor (>=1.1.0,<4.0.0)",
+    "packaging>=19",
+    "tomlkit (>=0.5.3,<1.0.0)",
+    "jinja2>=2.10.3",
+    "pyyaml>=3.08",
+    "argcomplete >=1.12.1,<3.7",
+    "typing-extensions (>=4.0.1,<5.0.0) ; python_version < '3.11'",
+    "charset-normalizer (>=2.1.0,<4)",
+    # Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility
+    "importlib-metadata >=8.0.0,<8.7.0 ; python_version < '3.10'",
+]
+keywords = ["commitizen", "conventional", "commits", "git"]
 # See also: https://pypi.org/classifiers/
 classifiers = [
     "Development Status :: 5 - Production/Stable",
@@ -25,71 +35,38 @@ classifiers = [
     "Operating System :: OS Independent",
     "Programming Language :: Python",
     "Programming Language :: Python :: 3",
-    "Programming Language :: Python :: 3.8",
     "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Programming Language :: Python :: Implementation :: CPython",
+    "License :: OSI Approved :: MIT License",
 ]
 
-[tool.poetry.dependencies]
-python = ">=3.8"
-questionary = "^2.0"
-decli = "^0.6.0"
-colorama = "^0.4.1"
-termcolor = ">= 1.1, < 3"
-packaging = ">=19"
-tomlkit = ">=0.5.3,<1.0.0"
-jinja2 = ">=2.10.3"
-pyyaml = ">=3.08"
-argcomplete = ">=1.12.1,<3.3"
-typing-extensions = { version = "^4.0.1", python = "<3.8" }
-charset-normalizer = ">=2.1.0,<4"
-# Use the Python 3.11 and 3.12 compatible API: https://github.com/python/importlib_metadata#compatibility
-importlib_metadata = { version = ">=4.13,<8"}
+[project.urls]
+Homepage = "https://github.com/commitizen-tools/commitizen"
+Documentation = "https://commitizen-tools.github.io/commitizen/"
+Repository = "https://github.com/commitizen-tools/commitizen"
+Issues = "https://github.com/commitizen-tools/commitizen/issues"
+Changelog = "https://github.com/commitizen-tools/commitizen/blob/master/CHANGELOG.md"
 
-[tool.poetry.group.dev.dependencies]
-# dev tool
-ipython = "^8.0"
-# test
-pytest = "^7.2.0"
-pytest-cov = "^4.0"
-pytest-mock = "^3.10"
-pytest-regressions = "^2.4.0"
-pytest-freezer = "^0.4.6"
-pytest-xdist = "^3.1.0"
-# code formatter
-black = "~=23.11"
-# linter
-ruff = "~=0.1.6"
-pre-commit = ">=2.18,<4.0"
-mypy = "^1.4"
-types-PyYAML = ">=5.4.3,<7.0.0"
-types-termcolor = "^0.1.1"
-# documentation
-mkdocs = "^1.4.2"
-mkdocs-material = "^9.1.6"
-deprecated = "^1.2.13"
-types-deprecated = "^1.2.9.2"
-types-python-dateutil = "^2.8.19.13"
-
-
-[tool.poetry.scripts]
+[project.scripts]
 cz = "commitizen.cli:main"
 git-cz = "commitizen.cli:main"
 
-[tool.poetry.plugins."commitizen.plugin"]
+[project.entry-points."commitizen.plugin"]
 cz_conventional_commits = "commitizen.cz.conventional_commits:ConventionalCommitsCz"
 cz_jira = "commitizen.cz.jira:JiraSmartCz"
 cz_customize = "commitizen.cz.customize:CustomizeCommitsCz"
 
-[tool.poetry.plugins."commitizen.changelog_format"]
+[project.entry-points."commitizen.changelog_format"]
 markdown = "commitizen.changelog_formats.markdown:Markdown"
 asciidoc = "commitizen.changelog_formats.asciidoc:AsciiDoc"
 textile = "commitizen.changelog_formats.textile:Textile"
 restructuredtext = "commitizen.changelog_formats.restructuredtext:RestructuredText"
 
-[tool.poetry.plugins."commitizen.provider"]
+[project.entry-points."commitizen.provider"]
 cargo = "commitizen.providers:CargoProvider"
 commitizen = "commitizen.providers:CommitizenProvider"
 composer = "commitizen.providers:ComposerProvider"
@@ -97,64 +74,151 @@ npm = "commitizen.providers:NpmProvider"
 pep621 = "commitizen.providers:Pep621Provider"
 poetry = "commitizen.providers:PoetryProvider"
 scm = "commitizen.providers:ScmProvider"
+uv = "commitizen.providers:UvProvider"
 
-[tool.poetry.plugins."commitizen.scheme"]
+[project.entry-points."commitizen.scheme"]
 pep440 = "commitizen.version_schemes:Pep440"
 semver = "commitizen.version_schemes:SemVer"
-
-[tool.coverage]
-    [tool.coverage.report]
-    show_missing = true
-    exclude_lines = [
-        # Have to re-enable the standard pragma
-        'pragma: no cover',
-
-        # Don't complain about missing debug-only code:
-        'def __repr__',
-        'if self\.debug',
-
-        # Don't complain if tests don't hit defensive assertion code:
-        'raise AssertionError',
-        'raise NotImplementedError',
-
-        # Don't complain if non-runnable code isn't run:
-        'if 0:',
-        'if __name__ == .__main__.:',
-        'if TYPE_CHECKING:',
-    ]
-    omit = [
-        'env/*',
-        'venv/*',
-        '.venv/*',
-        '*/virtualenv/*',
-        '*/virtualenvs/*',
-        '*/tests/*',
-    ]
+semver2 = "commitizen.version_schemes:SemVer2"
 
 [build-system]
-requires = ["poetry_core>=1.0.0"]
+requires = ["poetry-core>=2.0"]
 build-backend = "poetry.core.masonry.api"
 
+
+[tool.commitizen]
+version = "4.8.3"
+tag_format = "v$version"
+version_files = [
+    "pyproject.toml:version",
+    "commitizen/__version__.py",
+    ".pre-commit-config.yaml:rev:.+Commitizen",
+]
+version_scheme = "pep440"
+
+
+[tool.poetry]
+packages = [{ include = "commitizen" }, { include = "commitizen/py.typed" }]
+
+[tool.poetry.requires-plugins]
+"poethepoet" = ">=0.32.2"
+
+[tool.poetry.group.dev.dependencies]
+ipython = "^8.0"
+tox = ">4"
+poethepoet = "^0.34.0"
+
+[tool.poetry.group.test.dependencies]
+pytest = ">=7.2,<9.0"
+pytest-cov = ">=4,<7"
+pytest-mock = "^3.10"
+pytest-regressions = "^2.4.0"
+pytest-freezer = "^0.4.6"
+pytest-xdist = "^3.1.0"
+deprecated = "^1.2.13"
+
+[tool.poetry.group.linters.dependencies]
+ruff = "^0.11.5"
+pre-commit = ">=2.18,<5.0"
+mypy = "^1.16.0"
+types-deprecated = "^1.2.9.2"
+types-python-dateutil = "^2.8.19.13"
+types-PyYAML = ">=5.4.3,<7.0.0"
+types-termcolor = "^0.1.1"
+types-colorama = "^0.4.15.20240311"
+
+[tool.poetry.group.documentation.dependencies]
+mkdocs = "^1.4.2"
+mkdocs-material = "^9.1.6"
+
+[tool.poetry.group.script.dependencies]
+# for scripts/gen_cli_help_screenshots.py
+rich = "^13.7.1"
+
+
+[tool.coverage]
+[tool.coverage.report]
+show_missing = true
+exclude_lines = [
+    # Have to re-enable the standard pragma
+    'pragma: no cover',
+
+    # Don't complain about missing debug-only code:
+    'def __repr__',
+    'if self\.debug',
+
+    # Don't complain if tests don't hit defensive assertion code:
+    'raise AssertionError',
+    'raise NotImplementedError',
+
+    # Don't complain if non-runnable code isn't run:
+    'if 0:',
+    'if __name__ == .__main__.:',
+    'if TYPE_CHECKING:',
+]
+omit = [
+    'env/*',
+    'venv/*',
+    '.venv/*',
+    '*/virtualenv/*',
+    '*/virtualenvs/*',
+    '*/tests/*',
+]
+
+
 [tool.pytest.ini_options]
 addopts = "--strict-markers"
+testpaths = ["tests/"]
+
+[tool.tox]
+requires = ["tox>=4.22"]
+env_list = ["3.9", "3.10", "3.11", "3.12", "3.13"]
+
+[tool.tox.env_run_base]
+description = "Run tests suite against Python {base_python}"
+skip_install = true
+deps = ["poetry>=2.0"]
+commands_pre = [["poetry", "install", "--only", "main,test"]]
+commands = [["pytest", { replace = "posargs", extend = true }]]
 
 [tool.ruff]
+required-version = ">=0.11.5"
 line-length = 88
-select = ["E", "F", "UP"]
-ignore = [
-    "E501",
-    "D1",
-    "D415"
+
+[tool.ruff.lint]
+select = [
+    # flake8-annotations
+    "ANN001",
+    "ANN2",
+    # pycodestyle
+    "E",
+    # Pyflakes
+    "F",
+    # pyupgrade
+    "UP",
+    # isort
+    "I",
+    # pygrep-hooks
+    "PGH003",
+    "PGH004",
+    # unsorted-dunder-all
+    "RUF022",
+    # unused-noqa
+    "RUF100",
 ]
+ignore = ["E501", "D1", "D415"]
 
-[tool.ruff.isort]
+[tool.ruff.lint.per-file-ignores]
+"tests/*" = ["ANN"]
+
+[tool.ruff.lint.isort]
 known-first-party = ["commitizen", "tests"]
 
-[tool.ruff.pydocstyle]
+[tool.ruff.lint.pydocstyle]
 convention = "google"
 
 [tool.mypy]
-files = "commitizen"
+files = ["commitizen", "tests"]
 disallow_untyped_decorators = true
 disallow_subclassing_any = true
 warn_return_any = true
@@ -163,5 +227,52 @@ warn_unused_ignores = true
 warn_unused_configs = true
 
 [[tool.mypy.overrides]]
-module = "py.*"  # Legacy pytest dependencies
+module = "py.*"               # Legacy pytest dependencies
 ignore_missing_imports = true
+
+[tool.codespell]
+# Ref: https://github.com/codespell-project/codespell#using-a-config-file
+skip = '.git*,*.svg,*.lock'
+check-hidden = true
+ignore-words-list = 'asend'
+
+[tool.poe]
+poetry_command = ""
+
+[tool.poe.tasks]
+format.help = "Format the code"
+format.sequence = [{ cmd = "ruff check --fix" }, { cmd = "ruff format" }]
+
+lint.help = "Lint the code"
+lint.sequence = [{ cmd = "ruff check" }, { cmd = "mypy" }]
+
+check-commit.help = "Check the commit messages"
+check-commit.cmd = "poetry run cz --no-raise 3 check --rev-range origin/master.."
+
+test.help = "Run the test suite"
+test.cmd = "pytest -n 3 --dist=loadfile"
+
+"test:all".help = "Run the test suite on all supported Python versions"
+"test:all".cmd = "tox --parallel"
+
+cover.help = "Run the test suite with coverage"
+cover.ref = "test --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen"
+
+all.help = "Run all tasks"
+all.sequence = ["format", "lint", "check-commit", "cover"]
+
+"doc:screenshots".help = "Render documentation screenshots"
+"doc:screenshots".script = "scripts.gen_cli_help_screenshots:gen_cli_help_screenshots"
+
+"doc:build".help = "Build the documentation"
+"doc:build".cmd = "mkdocs build"
+
+doc.help = "Live documentation server"
+doc.cmd = "mkdocs serve"
+
+ci.help = "Run all tasks in CI"
+ci.sequence = ["check-commit", { cmd = "pre-commit run --all-files" }, "cover"]
+ci.env = { SKIP = "no-commit-to-branch" }
+
+setup-pre-commit.help = "Install pre-commit hooks"
+setup-pre-commit.cmd = "pre-commit install"
diff --git a/scripts/format b/scripts/format
deleted file mode 100755
index e1282f598d..0000000000
--- a/scripts/format
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env sh
-set -e
-
-export PREFIX="poetry run python -m "
-
-set -x
-
-${PREFIX}ruff format commitizen tests
-${PREFIX}black commitizen tests
diff --git a/scripts/gen_cli_help_screenshots.py b/scripts/gen_cli_help_screenshots.py
new file mode 100644
index 0000000000..0706612391
--- /dev/null
+++ b/scripts/gen_cli_help_screenshots.py
@@ -0,0 +1,42 @@
+import os
+import subprocess
+from pathlib import Path
+
+from rich.console import Console
+
+from commitizen.cli import data
+
+project_root = Path(__file__).parent.parent.absolute()
+images_root = project_root / Path("docs") / Path("images") / Path("cli_help")
+
+
+def gen_cli_help_screenshots() -> None:
+    """Generate the screenshot for help message on each cli command and save them as svg files."""
+    if not os.path.exists(images_root):
+        os.makedirs(images_root)
+        print(f"Created {images_root}")
+
+    help_cmds = _list_help_cmds()
+    for cmd in help_cmds:
+        file_name = f"{cmd.replace(' ', '_').replace('-', '_')}.svg"
+        _export_cmd_as_svg(cmd, f"{images_root}/{file_name}")
+
+
+def _list_help_cmds() -> list[str]:
+    cmds = [f"{data['prog']} --help"] + [
+        f"{data['prog']} {sub_c['name'] if isinstance(sub_c['name'], str) else sub_c['name'][0]} --help"
+        for sub_c in data["subcommands"]["commands"]
+    ]
+
+    return cmds
+
+
+def _export_cmd_as_svg(cmd: str, file_name: str) -> None:
+    stdout = subprocess.run(cmd, shell=True, capture_output=True).stdout.decode("utf-8")
+    console = Console(record=True, width=80)
+    console.print(f"$ {cmd}\n{stdout}")
+    console.save_svg(file_name, title="")
+
+
+if __name__ == "__main__":
+    gen_cli_help_screenshots()
diff --git a/scripts/publish b/scripts/publish
deleted file mode 100755
index 4d31f1188e..0000000000
--- a/scripts/publish
+++ /dev/null
@@ -1,2 +0,0 @@
-# Publish to pypi
-poetry publish --build -u $PYPI_USERNAME -p $PYPI_PASSWORD
diff --git a/scripts/test b/scripts/test
deleted file mode 100755
index e2fa065142..0000000000
--- a/scripts/test
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env sh
-set -e
-
-export PREFIX='poetry run python -m '
-export REGEX='^(?![.]|venv).*'
-
-${PREFIX}pytest -n 3 --dist=loadfile --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen tests/
-${PREFIX}black commitizen tests --check
-${PREFIX}ruff commitizen/ tests/
-${PREFIX}mypy commitizen/ tests/
-${PREFIX}commitizen -nr 3 check --rev-range origin/master..
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index 774ec0a95f..59297b1726 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -7,11 +7,14 @@
 from textwrap import dedent
 from unittest.mock import MagicMock, call
 
+import py
 import pytest
 from pytest_mock import MockFixture
 
 import commitizen.commands.bump as bump
-from commitizen import cli, cmd, git, hooks
+from commitizen import cli, cmd, defaults, git, hooks
+from commitizen.changelog_formats import ChangelogFormat
+from commitizen.config.base_config import BaseConfig
 from commitizen.cz.base import BaseCommitizen
 from commitizen.exceptions import (
     BumpTagFailedError,
@@ -20,6 +23,7 @@
     DryRunExit,
     ExitCode,
     ExpectedExit,
+    GetNextExit,
     InvalidManualVersion,
     NoCommitsFoundError,
     NoneIncrementExit,
@@ -28,8 +32,7 @@
     NotAllowed,
     NoVersionSpecifiedError,
 )
-from commitizen.changelog_formats import ChangelogFormat
-from tests.utils import create_file_and_commit, create_tag
+from tests.utils import create_file_and_commit, create_tag, skip_below_py_3_13
 
 
 @pytest.mark.parametrize(
@@ -39,8 +42,8 @@
         "fix(user): username exception",
         "refactor: remove ini configuration support",
         "refactor(config): remove ini configuration support",
-        "perf: update to use multiproess",
-        "perf(worker): update to use multiproess",
+        "perf: update to use multiprocess",
+        "perf(worker): update to use multiprocess",
     ),
 )
 @pytest.mark.usefixtures("tmp_commitizen_project")
@@ -101,7 +104,7 @@ def test_bump_minor_increment_annotated_config_file(
 ):
     tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml")
     tmp_commitizen_cfg_file.write(
-        f"{tmp_commitizen_cfg_file.read()}\n" f"annotated_tag = 1"
+        f"{tmp_commitizen_cfg_file.read()}\nannotated_tag = 1"
     )
     create_file_and_commit(commit_msg)
     testargs = ["cz", "bump", "--yes"]
@@ -120,7 +123,7 @@ def test_bump_minor_increment_signed_config_file(
     commit_msg, mocker: MockFixture, tmp_commitizen_project_with_gpg
 ):
     tmp_commitizen_cfg_file = tmp_commitizen_project_with_gpg.join("pyproject.toml")
-    tmp_commitizen_cfg_file.write(f"{tmp_commitizen_cfg_file.read()}\n" f"gpg_sign = 1")
+    tmp_commitizen_cfg_file.write(f"{tmp_commitizen_cfg_file.read()}\ngpg_sign = 1")
     create_file_and_commit(commit_msg)
     testargs = ["cz", "bump", "--yes"]
     mocker.patch.object(sys, "argv", testargs)
@@ -207,10 +210,10 @@ def test_bump_command_increment_option(
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_command_prelease(mocker: MockFixture):
-    # PRERELEASE
+def test_bump_command_prerelease(mocker: MockFixture):
     create_file_and_commit("feat: location")
 
+    # Create an alpha pre-release.
     testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
     mocker.patch.object(sys, "argv", testargs)
     cli.main()
@@ -218,7 +221,58 @@ def test_bump_command_prelease(mocker: MockFixture):
     tag_exists = git.tag_exist("0.2.0a0")
     assert tag_exists is True
 
-    # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE
+    # Create a beta pre-release.
+    testargs = ["cz", "bump", "--prerelease", "beta", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0b0")
+    assert tag_exists is True
+
+    # With a current beta pre-release, bumping alpha must bump beta
+    # because we can't bump "backwards".
+    testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0a1")
+    assert tag_exists is False
+    tag_exists = git.tag_exist("0.2.0b1")
+    assert tag_exists is True
+
+    # Create a rc pre-release.
+    testargs = ["cz", "bump", "--prerelease", "rc", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0rc0")
+    assert tag_exists is True
+
+    # With a current rc pre-release, bumping alpha must bump rc.
+    testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0a1")
+    assert tag_exists is False
+    tag_exists = git.tag_exist("0.2.0b2")
+    assert tag_exists is False
+    tag_exists = git.tag_exist("0.2.0rc1")
+    assert tag_exists is True
+
+    # With a current rc pre-release, bumping beta must bump rc.
+    testargs = ["cz", "bump", "--prerelease", "beta", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0a2")
+    assert tag_exists is False
+    tag_exists = git.tag_exist("0.2.0b2")
+    assert tag_exists is False
+    tag_exists = git.tag_exist("0.2.0rc2")
+    assert tag_exists is True
+
+    # Create a final release from the current pre-release.
     testargs = ["cz", "bump"]
     mocker.patch.object(sys, "argv", testargs)
     cli.main()
@@ -227,12 +281,111 @@ def test_bump_command_prelease(mocker: MockFixture):
     assert tag_exists is True
 
 
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_command_prerelease_increment(mocker: MockFixture):
+    # FINAL RELEASE
+    create_file_and_commit("fix: location")
+
+    testargs = ["cz", "bump", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    assert git.tag_exist("0.1.1")
+
+    # PRERELEASE
+    create_file_and_commit("fix: location")
+
+    testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    assert git.tag_exist("0.1.2a0")
+
+    create_file_and_commit("feat: location")
+
+    testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    assert git.tag_exist("0.2.0a0")
+
+    create_file_and_commit("feat!: breaking")
+
+    testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    assert git.tag_exist("1.0.0a0")
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_command_prerelease_exact_mode(mocker: MockFixture):
+    # PRERELEASE
+    create_file_and_commit("feat: location")
+
+    testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0a0")
+    assert tag_exists is True
+
+    # PRERELEASE + PATCH BUMP
+    testargs = [
+        "cz",
+        "bump",
+        "--prerelease",
+        "alpha",
+        "--yes",
+        "--increment-mode=exact",
+    ]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0a1")
+    assert tag_exists is True
+
+    # PRERELEASE + MINOR BUMP
+    # --increment-mode allows the minor version to bump, and restart the prerelease
+    create_file_and_commit("feat: location")
+
+    testargs = [
+        "cz",
+        "bump",
+        "--prerelease",
+        "alpha",
+        "--yes",
+        "--increment-mode=exact",
+    ]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.3.0a0")
+    assert tag_exists is True
+
+    # PRERELEASE + MAJOR BUMP
+    # --increment-mode=exact allows the major version to bump, and restart the prerelease
+    testargs = [
+        "cz",
+        "bump",
+        "--prerelease",
+        "alpha",
+        "--yes",
+        "--increment=MAJOR",
+        "--increment-mode=exact",
+    ]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("1.0.0a0")
+    assert tag_exists is True
+
+
 @pytest.mark.usefixtures("tmp_commitizen_project")
 def test_bump_on_git_with_hooks_no_verify_disabled(mocker: MockFixture):
     """Bump commit without --no-verify"""
     cmd.run("mkdir .git/hooks")
     with open(".git/hooks/pre-commit", "w", encoding="utf-8") as f:
-        f.write("#!/usr/bin/env bash\n" 'echo "0.1.0"')
+        f.write('#!/usr/bin/env bash\necho "0.1.0"')
     cmd.run("chmod +x .git/hooks/pre-commit")
 
     # MINOR
@@ -251,7 +404,7 @@ def test_bump_on_git_with_hooks_no_verify_disabled(mocker: MockFixture):
 def test_bump_tag_exists_raises_exception(mocker: MockFixture):
     cmd.run("mkdir .git/hooks")
     with open(".git/hooks/post-commit", "w", encoding="utf-8") as f:
-        f.write("#!/usr/bin/env bash\n" "exit 9")
+        f.write("#!/usr/bin/env bash\nexit 9")
     cmd.run("chmod +x .git/hooks/post-commit")
 
     # MINOR
@@ -270,7 +423,7 @@ def test_bump_tag_exists_raises_exception(mocker: MockFixture):
 def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture):
     cmd.run("mkdir .git/hooks")
     with open(".git/hooks/pre-commit", "w", encoding="utf-8") as f:
-        f.write("#!/usr/bin/env bash\n" 'echo "0.1.0"')
+        f.write('#!/usr/bin/env bash\necho "0.1.0"')
     cmd.run("chmod +x .git/hooks/pre-commit")
 
     # MINOR
@@ -285,7 +438,7 @@ def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture):
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_when_bumpping_is_not_support(mocker: MockFixture):
+def test_bump_when_bumping_is_not_support(mocker: MockFixture):
     create_file_and_commit(
         "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported"
     )
@@ -327,7 +480,7 @@ def test_bump_when_no_new_commit(mocker: MockFixture):
     with pytest.raises(NoCommitsFoundError) as excinfo:
         cli.main()
 
-    expected_error_message = "[NO_COMMITS_FOUND]\n" "No new commits found."
+    expected_error_message = "[NO_COMMITS_FOUND]\nNo new commits found."
     assert expected_error_message in str(excinfo.value)
 
 
@@ -559,7 +712,7 @@ def test_prevent_prerelease_when_no_increment_detected(mocker: MockFixture, caps
         cli.main()
 
     expected_error_message = (
-        "[NO_COMMITS_FOUND]\n" "No commits found to generate a pre-release."
+        "[NO_COMMITS_FOUND]\nNo commits found to generate a pre-release."
     )
     assert expected_error_message in str(excinfo.value)
 
@@ -662,6 +815,16 @@ def test_bump_with_git_to_stdout_arg(mocker: MockFixture, capsys, changelog_path
 """,
             id="version in __init__.py with regex",
         ),
+        pytest.param(
+            "pyproject.toml",
+            "*.toml:^version",
+            """
+[tool.poetry]
+name = "my_package"
+version = "0.1.0"
+""",
+            id="version in pyproject.toml with glob and regex",
+        ),
     ],
 )
 @pytest.mark.parametrize(
@@ -701,7 +864,7 @@ def test_bump_changelog_command_commits_untracked_changelog_and_version_files(
         mode="a",
         encoding="utf-8",
     ) as commitizen_config:
-        commitizen_config.write(f"version_files = [\n" f"'{version_regex}'\n]")
+        commitizen_config.write(f"version_files = [\n'{version_regex}'\n]")
 
     with tmp_commitizen_project.join(version_filepath).open(
         mode="a+", encoding="utf-8"
@@ -726,6 +889,8 @@ def test_bump_changelog_command_commits_untracked_changelog_and_version_files(
         ["cz", "bump", "--devrelease", "0", "1.2.3"],
         ["cz", "bump", "--devrelease", "1", "1.2.3"],
         ["cz", "bump", "--increment", "PATCH", "1.2.3"],
+        ["cz", "bump", "--build-metadata=a.b.c", "1.2.3"],
+        ["cz", "bump", "--local-version", "--build-metadata=a.b.c"],
     ],
 )
 @pytest.mark.usefixtures("tmp_commitizen_project")
@@ -754,7 +919,7 @@ def test_bump_invalid_manual_version_raises_exception(mocker, manual_version):
         cli.main()
 
     expected_error_message = (
-        "[INVALID_MANUAL_VERSION]\n" f"Invalid manual version: '{manual_version}'"
+        f"[INVALID_MANUAL_VERSION]\nInvalid manual version: '{manual_version}'"
     )
     assert expected_error_message in str(excinfo.value)
 
@@ -855,21 +1020,27 @@ def test_bump_with_pre_bump_hooks(
     )
 
 
-@pytest.mark.usefixtures("tmp_commitizen_project")
-def test_bump_manual_version_disallows_prerelease_offset(mocker):
-    create_file_and_commit("feat: new file")
+def test_bump_with_hooks_and_increment(mocker: MockFixture, tmp_commitizen_project):
+    pre_bump_hook = "scripts/pre_bump_hook.sh"
+    post_bump_hook = "scripts/post_bump_hook.sh"
 
-    manual_version = "0.2.0"
-    testargs = ["cz", "bump", "--yes", "--prerelease-offset", "42", manual_version]
-    mocker.patch.object(sys, "argv", testargs)
+    tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml")
+    tmp_commitizen_cfg_file.write(
+        f"{tmp_commitizen_cfg_file.read()}\n"
+        f'pre_bump_hooks = ["{pre_bump_hook}"]\n'
+        f'post_bump_hooks = ["{post_bump_hook}"]\n'
+    )
 
-    with pytest.raises(NotAllowed) as excinfo:
-        cli.main()
+    run_mock = mocker.Mock()
+    mocker.patch.object(hooks, "run", run_mock)
 
-    expected_error_message = (
-        "--prerelease-offset cannot be combined with MANUAL_VERSION"
-    )
-    assert expected_error_message in str(excinfo.value)
+    create_file_and_commit("test: some test")
+    testargs = ["cz", "bump", "--yes", "--increment", "MINOR"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    tag_exists = git.tag_exist("0.2.0")
+    assert tag_exists is True
 
 
 @pytest.mark.usefixtures("tmp_git_project")
@@ -892,7 +1063,7 @@ def test_bump_use_version_provider(mocker: MockFixture):
     mock.set_version.assert_called_once_with("0.0.1")
 
 
-def test_bump_command_prelease_scheme_via_cli(
+def test_bump_command_prerelease_scheme_via_cli(
     tmp_commitizen_project_initial, mocker: MockFixture
 ):
     tmp_commitizen_project = tmp_commitizen_project_initial()
@@ -931,7 +1102,7 @@ def test_bump_command_prelease_scheme_via_cli(
             assert "0.2.0" in f.read()
 
 
-def test_bump_command_prelease_scheme_via_config(
+def test_bump_command_prerelease_scheme_via_config(
     tmp_commitizen_project_initial, mocker: MockFixture
 ):
     tmp_commitizen_project = tmp_commitizen_project_initial(
@@ -975,7 +1146,7 @@ def test_bump_command_prelease_scheme_via_config(
             assert "0.2.0" in f.read()
 
 
-def test_bump_command_prelease_scheme_check_old_tags(
+def test_bump_command_prerelease_scheme_check_old_tags(
     tmp_commitizen_project_initial, mocker: MockFixture
 ):
     tmp_commitizen_project = tmp_commitizen_project_initial(
@@ -1115,7 +1286,7 @@ def test_bump_command_version_scheme_priority_over_version_type(mocker: MockFixt
         ),
     ),
 )
-def test_bump_template_option_precedance(
+def test_bump_template_option_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     any_changelog_format: ChangelogFormat,
@@ -1157,7 +1328,7 @@ def test_bump_template_option_precedance(
     assert out == expected
 
 
-def test_bump_template_extras_precedance(
+def test_bump_template_extras_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     any_changelog_format: ChangelogFormat,
@@ -1231,3 +1402,338 @@ def test_bump_template_extra_quotes(
 
     changelog = project_root / any_changelog_format.default_changelog_file
     assert changelog.read_text() == "no-quote - single quotes - double quotes"
+
+
+def test_bump_changelog_contains_increment_only(mocker, tmp_commitizen_project, capsys):
+    """Issue 1024"""
+    # Initialize commitizen up to v1.0.0
+    project_root = Path(tmp_commitizen_project)
+    tmp_commitizen_cfg_file = project_root / "pyproject.toml"
+    tmp_commitizen_cfg_file.write_text(
+        '[tool.commitizen]\nversion="1.0.0"\nupdate_changelog_on_bump = true\n'
+    )
+    tmp_changelog_file = project_root / "CHANGELOG.md"
+    tmp_changelog_file.write_text("## v1.0.0")
+    create_file_and_commit("feat(user): new file")
+    create_tag("v1.0.0")
+
+    # Add a commit and bump to v2.0.0
+    create_file_and_commit("feat(user)!: new file")
+    testargs = ["cz", "bump", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    _ = capsys.readouterr()
+
+    # Add a commit and create the incremental changelog to v3.0.0
+    # it should only include v3 changes
+    create_file_and_commit("feat(next)!: next version")
+    testargs = ["cz", "bump", "--yes", "--files-only", "--changelog-to-stdout"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(ExpectedExit):
+        cli.main()
+    out, _ = capsys.readouterr()
+
+    assert "3.0.0" in out
+    assert "2.0.0" not in out
+
+
+@skip_below_py_3_13
+def test_bump_command_shows_description_when_use_help_option(
+    mocker: MockFixture, capsys, file_regression
+):
+    testargs = ["cz", "bump", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_get_next(mocker: MockFixture, capsys):
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--yes", "--get-next"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(GetNextExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    assert "0.2.0" in out
+
+    tag_exists = git.tag_exist("0.2.0")
+    assert tag_exists is False
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_get_next_update_changelog_on_bump(
+    mocker: MockFixture, capsys, config_path
+):
+    create_file_and_commit("feat: new file")
+    with open(config_path, "a", encoding="utf-8") as fp:
+        fp.write("update_changelog_on_bump = true\n")
+
+    testargs = ["cz", "bump", "--yes", "--get-next"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(GetNextExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    assert "0.2.0" in out
+
+    tag_exists = git.tag_exist("0.2.0")
+    assert tag_exists is False
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_get_next__changelog_is_not_allowed(mocker: MockFixture):
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--yes", "--get-next", "--changelog"]
+    mocker.patch.object(sys, "argv", testargs)
+
+    with pytest.raises(NotAllowed):
+        cli.main()
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_get_next__changelog_to_stdout_is_not_allowed(mocker: MockFixture):
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--yes", "--get-next", "--changelog-to-stdout"]
+    mocker.patch.object(sys, "argv", testargs)
+
+    with pytest.raises(NotAllowed):
+        cli.main()
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_get_next__manual_version_is_not_allowed(mocker: MockFixture):
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--yes", "--get-next", "0.2.1"]
+    mocker.patch.object(sys, "argv", testargs)
+
+    with pytest.raises(NotAllowed):
+        cli.main()
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_get_next__no_eligible_commits_raises(mocker: MockFixture):
+    create_file_and_commit("chore: new commit")
+
+    testargs = ["cz", "bump", "--yes", "--get-next"]
+    mocker.patch.object(sys, "argv", testargs)
+
+    with pytest.raises(NoneIncrementExit):
+        cli.main()
+
+
+def test_bump_allow_no_commit_with_no_commit(mocker, tmp_commitizen_project, capsys):
+    with tmp_commitizen_project.as_cwd():
+        # Create the first commit and bump to 1.0.0
+        create_file_and_commit("feat(user)!: new file")
+        testargs = ["cz", "bump", "--yes"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+
+        # Verify NoCommitsFoundError should be raised
+        # when there's no new commit and "--allow-no-commit" is not set
+        with pytest.raises(NoCommitsFoundError):
+            testargs = ["cz", "bump"]
+            mocker.patch.object(sys, "argv", testargs)
+            cli.main()
+
+        # bump to 1.0.1 with new commit when "--allow-no-commit" is set
+        testargs = ["cz", "bump", "--allow-no-commit"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+        out, _ = capsys.readouterr()
+        assert "bump: version 1.0.0 → 1.0.1" in out
+
+
+def test_bump_allow_no_commit_with_no_eligible_commit(
+    mocker, tmp_commitizen_project, capsys
+):
+    with tmp_commitizen_project.as_cwd():
+        # Create the first commit and bump to 1.0.0
+        create_file_and_commit("feat(user)!: new file")
+        testargs = ["cz", "bump", "--yes"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+
+        # Create a commit that is ineligible to bump
+        create_file_and_commit("docs(bump): add description for allow no commit")
+
+        # Verify NoneIncrementExit should be raised
+        # when there's no eligible bumping commit and "--allow-no-commit" is not set
+        with pytest.raises(NoneIncrementExit):
+            testargs = ["cz", "bump", "--yes"]
+            mocker.patch.object(sys, "argv", testargs)
+            cli.main()
+
+        # bump to 1.0.1 with ineligible commit when "--allow-no-commit" is set
+        testargs = ["cz", "bump", "--allow-no-commit"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+        out, _ = capsys.readouterr()
+        assert "bump: version 1.0.0 → 1.0.1" in out
+
+
+def test_bump_allow_no_commit_with_increment(mocker, tmp_commitizen_project, capsys):
+    with tmp_commitizen_project.as_cwd():
+        # # Create the first commit and bump to 1.0.0
+        create_file_and_commit("feat(user)!: new file")
+        testargs = ["cz", "bump", "--yes"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+
+        # Verify NoCommitsFoundError should be raised
+        # when there's no new commit and "--allow-no-commit" is not set
+        with pytest.raises(NoCommitsFoundError):
+            testargs = ["cz", "bump", "--yes"]
+            mocker.patch.object(sys, "argv", testargs)
+            cli.main()
+
+        # bump to 1.1.0 with no new commit when "--allow-no-commit" is set
+        # and increment is specified
+        testargs = ["cz", "bump", "--yes", "--allow-no-commit", "--increment", "MINOR"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+        out, _ = capsys.readouterr()
+        assert "bump: version 1.0.0 → 1.1.0" in out
+
+
+def test_bump_allow_no_commit_with_manual_version(
+    mocker, tmp_commitizen_project, capsys
+):
+    with tmp_commitizen_project.as_cwd():
+        # # Create the first commit and bump to 1.0.0
+        create_file_and_commit("feat(user)!: new file")
+        testargs = ["cz", "bump", "--yes"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+
+        # Verify NoCommitsFoundError should be raised
+        # when there's no new commit and "--allow-no-commit" is not set
+        with pytest.raises(NoCommitsFoundError):
+            testargs = ["cz", "bump", "--yes"]
+            mocker.patch.object(sys, "argv", testargs)
+            cli.main()
+
+        # bump to 1.1.0 with no new commit when "--allow-no-commit" is set
+        # and increment is specified
+        testargs = ["cz", "bump", "--yes", "--allow-no-commit", "2.0.0"]
+        mocker.patch.object(sys, "argv", testargs)
+        cli.main()
+        out, _ = capsys.readouterr()
+        assert "bump: version 1.0.0 → 2.0.0" in out
+
+
+def test_bump_detect_legacy_tags_from_scm(
+    tmp_commitizen_project: py.path.local, mocker: MockFixture
+):
+    project_root = Path(tmp_commitizen_project)
+    tmp_commitizen_cfg_file = project_root / "pyproject.toml"
+    tmp_commitizen_cfg_file.write_text(
+        "\n".join(
+            [
+                "[tool.commitizen]",
+                'version_provider = "scm"',
+                'tag_format = "v$version"',
+                "legacy_tag_formats = [",
+                '  "legacy-${version}"',
+                "]",
+            ]
+        ),
+    )
+    create_file_and_commit("feat: new file")
+    create_tag("legacy-0.4.2")
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--increment", "patch", "--changelog"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    assert git.tag_exist("v0.4.3")
+
+
+def test_bump_warn_but_dont_fail_on_invalid_tags(
+    tmp_commitizen_project: py.path.local,
+    mocker: MockFixture,
+    capsys: pytest.CaptureFixture,
+):
+    project_root = Path(tmp_commitizen_project)
+    tmp_commitizen_cfg_file = project_root / "pyproject.toml"
+    tmp_commitizen_cfg_file.write_text(
+        "\n".join(
+            [
+                "[tool.commitizen]",
+                'version_provider = "scm"',
+                'version_scheme = "pep440"',
+            ]
+        ),
+    )
+    create_file_and_commit("feat: new file")
+    create_tag("0.4.2")
+    create_file_and_commit("feat: new file")
+    create_tag("0.4.3.deadbeaf")
+    create_file_and_commit("feat: new file")
+
+    testargs = ["cz", "bump", "--increment", "patch", "--changelog"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+
+    _, err = capsys.readouterr()
+
+    assert err.count("Invalid version tag: '0.4.3.deadbeaf'") == 1
+    assert git.tag_exist("0.4.3")
+
+
+def test_is_initial_tag(mocker: MockFixture, tmp_commitizen_project):
+    """Test the _is_initial_tag method behavior."""
+    # Create a commit but no tags
+    create_file_and_commit("feat: initial commit")
+
+    # Initialize Bump with minimal config
+    config = BaseConfig()
+    config.settings.update(
+        {
+            "name": defaults.DEFAULT_SETTINGS["name"],
+            "encoding": "utf-8",
+            "pre_bump_hooks": [],
+            "post_bump_hooks": [],
+        }
+    )
+
+    # Initialize with required arguments
+    arguments = {
+        "changelog": False,
+        "changelog_to_stdout": False,
+        "git_output_to_stderr": False,
+        "no_verify": False,
+        "check_consistency": False,
+        "retry": False,
+        "version_scheme": None,
+        "file_name": None,
+        "template": None,
+        "extras": None,
+    }
+
+    bump_cmd = bump.Bump(config, arguments)  # type: ignore[arg-type]
+
+    # Test case 1: No current tag, not yes mode
+    mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: True))
+    assert bump_cmd._is_initial_tag(None, is_yes=False) is True
+
+    # Test case 2: No current tag, yes mode
+    assert bump_cmd._is_initial_tag(None, is_yes=True) is True
+
+    # Test case 3: Has current tag
+    mock_tag = mocker.Mock()
+    assert bump_cmd._is_initial_tag(mock_tag, is_yes=False) is False
+
+    # Test case 4: No current tag, user denies
+    mocker.patch("questionary.confirm", return_value=mocker.Mock(ask=lambda: False))
+    assert bump_cmd._is_initial_tag(None, is_yes=False) is False
diff --git a/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..4cf8e6c91b
--- /dev/null
+++ b/tests/commands/test_bump_command/test_bump_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,81 @@
+usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog]
+               [--no-verify] [--yes] [--tag-format TAG_FORMAT]
+               [--bump-message BUMP_MESSAGE] [--prerelease {alpha,beta,rc}]
+               [--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}]
+               [--increment-mode {linear,exact}] [--check-consistency]
+               [--annotated-tag]
+               [--annotated-tag-message ANNOTATED_TAG_MESSAGE] [--gpg-sign]
+               [--changelog-to-stdout] [--git-output-to-stderr] [--retry]
+               [--major-version-zero] [--template TEMPLATE] [--extra EXTRA]
+               [--file-name FILE_NAME] [--prerelease-offset PRERELEASE_OFFSET]
+               [--version-scheme {pep440,semver,semver2}]
+               [--version-type {pep440,semver,semver2}]
+               [--build-metadata BUILD_METADATA] [--get-next]
+               [--allow-no-commit]
+               [MANUAL_VERSION]
+
+bump semantic version based on the git log
+
+positional arguments:
+  MANUAL_VERSION        bump to the given version (e.g: 1.5.3)
+
+options:
+  -h, --help            show this help message and exit
+  --dry-run             show output to stdout, no commit, no modified files
+  --files-only          bump version in the files from the config
+  --local-version       bump only the local version portion
+  --changelog, -ch      generate the changelog for the newest version
+  --no-verify           this option bypasses the pre-commit and commit-msg
+                        hooks
+  --yes                 accept automatically questions done
+  --tag-format TAG_FORMAT
+                        the format used to tag the commit and read it, use it
+                        in existing projects, wrap around simple quotes
+  --bump-message BUMP_MESSAGE
+                        template used to create the release commit, useful
+                        when working with CI
+  --prerelease, -pr {alpha,beta,rc}
+                        choose type of prerelease
+  --devrelease, -d DEVRELEASE
+                        specify non-negative integer for dev. release
+  --increment {MAJOR,MINOR,PATCH}
+                        manually specify the desired increment
+  --increment-mode {linear,exact}
+                        set the method by which the new version is chosen.
+                        'linear' (default) guesses the next version based on
+                        typical linear version progression, such that bumping
+                        of a pre-release with lower precedence than the
+                        current pre-release phase maintains the current phase
+                        of higher precedence. 'exact' applies the changes that
+                        have been specified (or determined from the commit
+                        log) without interpretation, such that the increment
+                        and pre-release are always honored
+  --check-consistency, -cc
+                        check consistency among versions defined in commitizen
+                        configuration and version_files
+  --annotated-tag, -at  create annotated tag instead of lightweight one
+  --annotated-tag-message, -atm ANNOTATED_TAG_MESSAGE
+                        create annotated tag message
+  --gpg-sign, -s        sign tag instead of lightweight one
+  --changelog-to-stdout
+                        Output changelog to the stdout
+  --git-output-to-stderr
+                        Redirect git output to stderr
+  --retry               retry commit if it fails the 1st time
+  --major-version-zero  keep major version at zero, even for breaking changes
+  --template, -t TEMPLATE
+                        changelog template file name (relative to the current
+                        working directory)
+  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value')
+  --file-name FILE_NAME
+                        file name of changelog (default: 'CHANGELOG.md')
+  --prerelease-offset PRERELEASE_OFFSET
+                        start pre-releases with this offset
+  --version-scheme {pep440,semver,semver2}
+                        choose version scheme
+  --version-type {pep440,semver,semver2}
+                        Deprecated, use --version-scheme instead
+  --build-metadata BUILD_METADATA
+                        Add additional build-metadata to the version-number
+  --get-next            Determine the next version and write to stdout
+  --allow-no-commit     bump version without eligible commits
diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py
index a0183e1cd9..0eb29cdb04 100644
--- a/tests/commands/test_changelog_command.py
+++ b/tests/commands/test_changelog_command.py
@@ -8,10 +8,13 @@
 from dateutil import relativedelta
 from jinja2 import FileSystemLoader
 from pytest_mock import MockFixture
+from pytest_regressions.file_regression import FileRegressionFixture
 
 from commitizen import __file__ as commitizen_init
 from commitizen import cli, git
+from commitizen.changelog_formats import ChangelogFormat
 from commitizen.commands.changelog import Changelog
+from commitizen.config.base_config import BaseConfig
 from commitizen.cz.base import BaseCommitizen
 from commitizen.exceptions import (
     DryRunExit,
@@ -21,13 +24,13 @@
     NotAGitProjectError,
     NotAllowed,
 )
-from commitizen.changelog_formats import ChangelogFormat
 from tests.utils import (
     create_branch,
     create_file_and_commit,
     create_tag,
     get_current_branch,
     merge_branch,
+    skip_below_py_3_13,
     switch_branch,
     wait_for_tag,
 )
@@ -275,7 +278,8 @@ def test_changelog_incremental_keep_a_changelog_sample(
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
-def test_changelog_hook(mocker: MockFixture, config):
+@pytest.mark.parametrize("dry_run", [True, False])
+def test_changelog_hook(mocker: MockFixture, config: BaseConfig, dry_run: bool):
     changelog_hook_mock = mocker.Mock()
     changelog_hook_mock.return_value = "cool changelog hook"
 
@@ -283,17 +287,24 @@ def test_changelog_hook(mocker: MockFixture, config):
     create_file_and_commit("refactor: is in changelog")
     create_file_and_commit("Merge into master")
 
-    config.settings["change_type_order"] = ["Refactor", "Feat"]
+    config.settings["change_type_order"] = ["Refactor", "Feat"]  # type: ignore[typeddict-unknown-key]
     changelog = Changelog(
-        config, {"unreleased_version": None, "incremental": True, "dry_run": False}
+        config, {"unreleased_version": None, "incremental": True, "dry_run": dry_run}
     )
     mocker.patch.object(changelog.cz, "changelog_hook", changelog_hook_mock)
-    changelog()
+    try:
+        changelog()
+    except DryRunExit:
+        pass
+
     full_changelog = (
         "## Unreleased\n\n### Refactor\n\n- is in changelog\n\n### Feat\n\n- new file\n"
     )
+    partial_changelog = full_changelog
+    if dry_run:
+        partial_changelog = ""
 
-    changelog_hook_mock.assert_called_with(full_changelog, full_changelog)
+    changelog_hook_mock.assert_called_with(full_changelog, partial_changelog)
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
@@ -318,6 +329,28 @@ def test_changelog_hook_customize(mocker: MockFixture, config_customize):
     changelog_hook_mock.assert_called_with(full_changelog, full_changelog)
 
 
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_changelog_release_hook(mocker: MockFixture, config):
+    def changelog_release_hook(release: dict, tag: git.GitTag) -> dict:
+        return release
+
+    for i in range(3):
+        create_file_and_commit("feat: new file")
+        create_file_and_commit("refactor: is in changelog")
+        create_file_and_commit("Merge into master")
+        git.tag(f"0.{i + 1}.0")
+
+    # changelog = Changelog(config, {})
+    changelog = Changelog(
+        config, {"unreleased_version": None, "incremental": True, "dry_run": False}
+    )
+    mocker.patch.object(changelog.cz, "changelog_release_hook", changelog_release_hook)
+    spy = mocker.spy(changelog.cz, "changelog_release_hook")
+    changelog()
+
+    assert spy.call_count == 3
+
+
 @pytest.mark.usefixtures("tmp_commitizen_project")
 def test_changelog_with_non_linear_merges_commit_order(
     mocker: MockFixture, config_customize
@@ -922,8 +955,17 @@ def test_changelog_from_rev_latest_version_from_arg(
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
 @pytest.mark.freeze_time("2022-02-13")
-def test_changelog_from_rev_single_version_not_found(
-    mocker: MockFixture, config_path, changelog_path
+@pytest.mark.parametrize(
+    "rev_range,tag",
+    (
+        pytest.param("0.8.0", "0.2.0", id="single-not-found"),
+        pytest.param("0.1.0..0.3.0", "0.3.0", id="lower-bound-not-found"),
+        pytest.param("0.1.0..0.3.0", "0.1.0", id="upper-bound-not-found"),
+        pytest.param("0.3.0..0.4.0", "0.2.0", id="none-found"),
+    ),
+)
+def test_changelog_from_rev_range_not_found(
+    mocker: MockFixture, config_path, rev_range: str, tag: str
 ):
     """Provides an invalid revision ID to changelog command"""
     with open(config_path, "a", encoding="utf-8") as f:
@@ -931,26 +973,46 @@ def test_changelog_from_rev_single_version_not_found(
 
     # create commit and tag
     create_file_and_commit("feat: new file")
-    testargs = ["cz", "bump", "--yes"]
+    create_tag(tag)
+    create_file_and_commit("feat: new file")
+    create_tag("1.0.0")
+
+    testargs = ["cz", "changelog", rev_range]  # it shouldn't exist
     mocker.patch.object(sys, "argv", testargs)
-    cli.main()
+    with pytest.raises(NoCommitsFoundError) as excinfo:
+        cli.main()
 
-    wait_for_tag()
+    assert "Could not find a valid revision" in str(excinfo)
 
-    create_file_and_commit("feat: after 0.2.0")
-    create_file_and_commit("feat: another feature")
 
-    testargs = ["cz", "bump", "--yes"]
-    mocker.patch.object(sys, "argv", testargs)
-    cli.main()
-    wait_for_tag()
+@pytest.mark.usefixtures("tmp_commitizen_project")
+@pytest.mark.freeze_time("2022-02-13")
+def test_changelog_multiple_matching_tags(
+    mocker: MockFixture, config_path, changelog_path
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.write('tag_format = "new-$version"\nlegacy_tag_formats = ["legacy-$version"]')
+
+    create_file_and_commit("feat: new file")
+    create_tag("legacy-1.0.0")
+    create_file_and_commit("feat: new file")
+    create_tag("legacy-2.0.0")
+    create_tag("new-2.0.0")
 
-    testargs = ["cz", "changelog", "0.8.0"]  # it shouldn't exist
+    testargs = ["cz", "changelog", "1.0.0..2.0.0"]  # it shouldn't exist
     mocker.patch.object(sys, "argv", testargs)
-    with pytest.raises(NoCommitsFoundError) as excinfo:
+    with pytest.warns() as warnings:
         cli.main()
 
-    assert "Could not find a valid revision" in str(excinfo)
+    assert len(warnings) == 1
+    warning = warnings[0]
+    assert "Multiple tags found for version 2.0.0" in str(warning.message)
+
+    with open(changelog_path) as f:
+        out = f.read()
+
+    # Ensure only one tag is rendered
+    assert out.count("2.0.0") == 1
 
 
 @pytest.mark.usefixtures("tmp_commitizen_project")
@@ -984,34 +1046,6 @@ def test_changelog_from_rev_range_default_tag_format(
     assert "new file" not in out
 
 
-@pytest.mark.usefixtures("tmp_commitizen_project")
-@pytest.mark.freeze_time("2022-02-13")
-def test_changelog_from_rev_range_version_not_found(mocker: MockFixture, config_path):
-    """Provides an invalid end revision ID to changelog command"""
-    with open(config_path, "a", encoding="utf-8") as f:
-        f.write('tag_format = "$version"\n')
-
-    # create commit and tag
-    create_file_and_commit("feat: new file")
-    testargs = ["cz", "bump", "--yes"]
-    mocker.patch.object(sys, "argv", testargs)
-    cli.main()
-
-    create_file_and_commit("feat: after 0.2.0")
-    create_file_and_commit("feat: another feature")
-
-    testargs = ["cz", "bump", "--yes"]
-    mocker.patch.object(sys, "argv", testargs)
-    cli.main()
-
-    testargs = ["cz", "changelog", "0.5.0..0.8.0"]  # it shouldn't exist
-    mocker.patch.object(sys, "argv", testargs)
-    with pytest.raises(NoCommitsFoundError) as excinfo:
-        cli.main()
-
-    assert "Could not find a valid revision" in str(excinfo)
-
-
 @pytest.mark.usefixtures("tmp_commitizen_project")
 @pytest.mark.freeze_time("2022-02-13")
 def test_changelog_from_rev_version_range_including_first_tag(
@@ -1084,6 +1118,41 @@ def test_changelog_from_rev_version_range_from_arg(
     file_regression.check(out, extension=".md")
 
 
+@pytest.mark.usefixtures("tmp_commitizen_project")
+@pytest.mark.freeze_time("2022-02-13")
+def test_changelog_from_rev_version_range_with_legacy_tags(
+    mocker: MockFixture, config_path, changelog_path, file_regression
+):
+    mocker.patch("commitizen.git.GitTag.date", "2022-02-13")
+
+    changelog = Path(changelog_path)
+    Path(config_path).write_text(
+        "\n".join(
+            [
+                "[tool.commitizen]",
+                'version_provider = "scm"',
+                'tag_format = "v$version"',
+                "legacy_tag_formats = [",
+                '  "legacy-${version}",',
+                '  "old-${version}",',
+                "]",
+            ]
+        ),
+    )
+
+    create_file_and_commit("feat: new file")
+    create_tag("old-0.2.0")
+    create_file_and_commit("feat: new file")
+    create_tag("legacy-0.3.0")
+    create_file_and_commit("feat: new file")
+    create_tag("legacy-0.4.0")
+
+    testargs = ["cz", "changelog", "0.2.0..0.4.0"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    file_regression.check(changelog.read_text(), extension=".md")
+
+
 @pytest.mark.usefixtures("tmp_commitizen_project")
 @pytest.mark.freeze_time("2022-02-13")
 def test_changelog_from_rev_version_with_big_range_from_arg(
@@ -1263,7 +1332,7 @@ def test_changelog_prerelease_rev_with_use_scheme_semver(
     mocker.patch("commitizen.git.GitTag.date", "2022-02-13")
 
     with open(config_path, "a") as f:
-        f.write('tag_format = "$version"\n' 'version_scheme = "semver"')
+        f.write('tag_format = "$version"\nversion_scheme = "semver"')
 
     # create commit and tag
     create_file_and_commit("feat: new file")
@@ -1412,7 +1481,7 @@ def test_changelog_from_current_version_tag_with_nonversion_tag(
         ),
     ),
 )
-def test_changelog_template_option_precedance(
+def test_changelog_template_option_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     any_changelog_format: ChangelogFormat,
@@ -1454,7 +1523,7 @@ def test_changelog_template_option_precedance(
     assert out == expected
 
 
-def test_changelog_template_extras_precedance(
+def test_changelog_template_extras_precedence(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
     mock_plugin: BaseCommitizen,
@@ -1491,6 +1560,243 @@ def test_changelog_template_extras_precedance(
     assert changelog.read_text() == "from-command - from-config - from-plugin"
 
 
+@pytest.mark.usefixtures("tmp_commitizen_project")
+@pytest.mark.freeze_time("2021-06-11")
+def test_changelog_only_tag_matching_tag_format_included_prefix(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.write('\ntag_format = "custom${version}"\n')
+    create_file_and_commit("feat: new file")
+    git.tag("v0.2.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("0.2.0")
+    git.tag("random0.2.0")
+    testargs = ["cz", "bump", "--changelog", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    wait_for_tag()
+    create_file_and_commit("feat: another new file")
+    cli.main()
+    with open(changelog_path) as f:
+        out = f.read()
+    assert out.startswith("## custom0.3.0 (2021-06-11)")
+    assert "## v0.2.0 (2021-06-11)" not in out
+    assert "## 0.2.0  (2021-06-11)" not in out
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_changelog_only_tag_matching_tag_format_included_prefix_sep(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.write('\ntag_format = "custom-${version}"\n')
+    create_file_and_commit("feat: new file")
+    git.tag("v0.2.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("0.2.0")
+    git.tag("random0.2.0")
+    wait_for_tag()
+    testargs = ["cz", "bump", "--changelog", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    with open(changelog_path) as f:
+        out = f.read()
+    create_file_and_commit("feat: new version another new file")
+    create_file_and_commit("feat: new version some new file")
+    testargs = ["cz", "bump", "--changelog"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    with open(changelog_path) as f:
+        out = f.read()
+    assert out.startswith("## custom-0.3.0")
+    assert "## v0.2.0" not in out
+    assert "## 0.2.0" not in out
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+@pytest.mark.freeze_time("2021-06-11")
+def test_changelog_only_tag_matching_tag_format_included_suffix(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.write('\ntag_format = "${version}custom"\n')
+    create_file_and_commit("feat: new file")
+    git.tag("v0.2.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("0.2.0")
+    git.tag("random0.2.0")
+    testargs = ["cz", "bump", "--changelog", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    # bump to 0.2.0custom
+    cli.main()
+    wait_for_tag()
+    create_file_and_commit("feat: another new file")
+    # bump to 0.3.0custom
+    cli.main()
+    wait_for_tag()
+    with open(changelog_path) as f:
+        out = f.read()
+    assert out.startswith("## 0.3.0custom (2021-06-11)")
+    assert "## v0.2.0 (2021-06-11)" not in out
+    assert "## 0.2.0  (2021-06-11)" not in out
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+@pytest.mark.freeze_time("2021-06-11")
+def test_changelog_only_tag_matching_tag_format_included_suffix_sep(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.write('\ntag_format = "${version}-custom"\n')
+    create_file_and_commit("feat: new file")
+    git.tag("v0.2.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("0.2.0")
+    git.tag("random0.2.0")
+    testargs = ["cz", "bump", "--changelog", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    wait_for_tag()
+    create_file_and_commit("feat: another new file")
+    cli.main()
+    wait_for_tag()
+    with open(changelog_path) as f:
+        out = f.read()
+    assert out.startswith("## 0.3.0-custom (2021-06-11)")
+    assert "## v0.2.0 (2021-06-11)" not in out
+    assert "## 0.2.0  (2021-06-11)" not in out
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_changelog_legacy_tags(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.writelines(
+            [
+                'tag_format = "v${version}"\n',
+                "legacy_tag_formats = [\n",
+                '  "older-${version}",\n',
+                '  "oldest-${version}",\n',
+                "]\n",
+            ]
+        )
+    create_file_and_commit("feat: new file")
+    git.tag("oldest-0.1.0")
+    create_file_and_commit("feat: new file")
+    git.tag("older-0.2.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("v0.3.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("not-0.3.1")
+    testargs = ["cz", "bump", "--changelog", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    out = open(changelog_path).read()
+    assert "## v0.3.0" in out
+    assert "## older-0.2.0" in out
+    assert "## oldest-0.1.0" in out
+    assert "## v0.3.0" in out
+    assert "## not-0.3.1" not in out
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+@pytest.mark.freeze_time("2024-11-18")
+def test_changelog_incremental_change_tag_format(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+    file_regression: FileRegressionFixture,
+):
+    mocker.patch("commitizen.git.GitTag.date", "2024-11-18")
+    config = Path(config_path)
+    base_config = config.read_text()
+    config.write_text(
+        "\n".join(
+            (
+                base_config,
+                'tag_format = "older-${version}"',
+            )
+        )
+    )
+    create_file_and_commit("feat: new file")
+    git.tag("older-0.1.0")
+    create_file_and_commit("feat: new file")
+    git.tag("older-0.2.0")
+    mocker.patch.object(sys, "argv", ["cz", "changelog"])
+    cli.main()
+
+    config.write_text(
+        "\n".join(
+            (
+                base_config,
+                'tag_format = "v${version}"',
+                'legacy_tag_formats = ["older-${version}"]',
+            )
+        )
+    )
+    create_file_and_commit("feat: another new file")
+    git.tag("v0.3.0")
+    mocker.patch.object(sys, "argv", ["cz", "changelog", "--incremental"])
+    cli.main()
+    out = open(changelog_path).read()
+    assert "## v0.3.0" in out
+    assert "## older-0.2.0" in out
+    assert "## older-0.1.0" in out
+    file_regression.check(out, extension=".md")
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_changelog_ignored_tags(
+    mocker: MockFixture,
+    changelog_path: Path,
+    config_path: Path,
+    capsys: pytest.CaptureFixture,
+):
+    with open(config_path, "a", encoding="utf-8") as f:
+        f.writelines(
+            [
+                'tag_format = "v${version}"\n',
+                "ignored_tag_formats = [\n",
+                '  "ignored",\n',
+                '  "ignore-${version}",\n',
+                "]\n",
+            ]
+        )
+    create_file_and_commit("feat: new file")
+    git.tag("ignore-0.1.0")
+    create_file_and_commit("feat: new file")
+    git.tag("ignored")
+    create_file_and_commit("feat: another new file")
+    git.tag("v0.3.0")
+    create_file_and_commit("feat: another new file")
+    git.tag("not-ignored")
+    testargs = ["cz", "bump", "--changelog", "--yes"]
+    mocker.patch.object(sys, "argv", testargs)
+    cli.main()
+    out = open(changelog_path).read()
+    _, err = capsys.readouterr()
+    assert "## ignore-0.1.0" not in out
+    assert "Invalid version tag: 'ignore-0.1.0'" not in err
+    assert "## ignored" not in out
+    assert "Invalid version tag: 'ignored'" not in err
+    assert "## not-ignored" not in out
+    assert "Invalid version tag: 'not-ignored'" in err
+    assert "## v0.3.0" in out
+    assert "Invalid version tag: 'v0.3.0'" not in err
+
+
 def test_changelog_template_extra_quotes(
     mocker: MockFixture,
     tmp_commitizen_project: Path,
@@ -1606,3 +1912,16 @@ def test_export_changelog_template_from_plugin(
 
     assert target.exists()
     assert target.read_text() == tpl
+
+
+@skip_below_py_3_13
+def test_changelog_command_shows_description_when_use_help_option(
+    mocker: MockFixture, capsys, file_regression
+):
+    testargs = ["cz", "changelog", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..91b7f389b5
--- /dev/null
+++ b/tests/commands/test_changelog_command/test_changelog_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,41 @@
+usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME]
+                    [--unreleased-version UNRELEASED_VERSION] [--incremental]
+                    [--start-rev START_REV] [--merge-prerelease]
+                    [--version-scheme {pep440,semver,semver2}]
+                    [--export-template EXPORT_TEMPLATE] [--template TEMPLATE]
+                    [--extra EXTRA] [--tag-format TAG_FORMAT]
+                    [rev_range]
+
+generate changelog (note that it will overwrite existing file)
+
+positional arguments:
+  rev_range             generates changelog for the given version (e.g: 1.5.3)
+                        or version range (e.g: 1.5.3..1.7.9)
+
+options:
+  -h, --help            show this help message and exit
+  --dry-run             show changelog to stdout
+  --file-name FILE_NAME
+                        file name of changelog (default: 'CHANGELOG.md')
+  --unreleased-version UNRELEASED_VERSION
+                        set the value for the new version (use the tag value),
+                        instead of using unreleased
+  --incremental         generates changelog from last created version, useful
+                        if the changelog has been manually modified
+  --start-rev START_REV
+                        start rev of the changelog. If not set, it will
+                        generate changelog from the start
+  --merge-prerelease    collect all changes from prereleases into next non-
+                        prerelease. If not set, it will include prereleases in
+                        the changelog
+  --version-scheme {pep440,semver,semver2}
+                        choose version scheme
+  --export-template EXPORT_TEMPLATE
+                        Export the changelog template into this file instead
+                        of rendering it
+  --template, -t TEMPLATE
+                        changelog template file name (relative to the current
+                        working directory)
+  --extra, -e EXTRA     a changelog extra variable (in the form 'key=value')
+  --tag-format TAG_FORMAT
+                        The format of the tag, wrap around simple quotes
diff --git a/tests/commands/test_changelog_command/test_changelog_from_rev_version_range_with_legacy_tags.md b/tests/commands/test_changelog_command/test_changelog_from_rev_version_range_with_legacy_tags.md
new file mode 100644
index 0000000000..5d37333aa5
--- /dev/null
+++ b/tests/commands/test_changelog_command/test_changelog_from_rev_version_range_with_legacy_tags.md
@@ -0,0 +1,17 @@
+## legacy-0.4.0 (2022-02-13)
+
+### Feat
+
+- new file
+
+## legacy-0.3.0 (2022-02-13)
+
+### Feat
+
+- new file
+
+## old-0.2.0 (2022-02-13)
+
+### Feat
+
+- new file
diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_change_tag_format.md b/tests/commands/test_changelog_command/test_changelog_incremental_change_tag_format.md
new file mode 100644
index 0000000000..2f0cc2909e
--- /dev/null
+++ b/tests/commands/test_changelog_command/test_changelog_incremental_change_tag_format.md
@@ -0,0 +1,17 @@
+## v0.3.0 (2024-11-18)
+
+### Feat
+
+- another new file
+
+## older-0.2.0 (2024-11-18)
+
+### Feat
+
+- new file
+
+## older-0.1.0 (2024-11-18)
+
+### Feat
+
+- new file
diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_beta_alpha_.md b/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_beta_alpha_.md
index 300ca95756..ec9ab11d7b 100644
--- a/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_beta_alpha_.md
+++ b/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_beta_alpha_.md
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## 0.2.0a0 (2021-06-11)
+## 0.2.0b1 (2021-06-11)
 
 ## 0.2.0b0 (2021-06-11)
 
diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_alpha_.md b/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_alpha_.md
index 9a7e9f161f..e1c20b7e0d 100644
--- a/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_alpha_.md
+++ b/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_alpha_.md
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## 0.2.0a0 (2021-06-11)
+## 0.2.0rc1 (2021-06-11)
 
 ## 0.2.0rc0 (2021-06-11)
 
diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_beta_.md b/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_beta_.md
index b3be6f3c1d..e1c20b7e0d 100644
--- a/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_beta_.md
+++ b/tests/commands/test_changelog_command/test_changelog_incremental_with_prerelease_version_to_prerelease_version_rc_beta_.md
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## 0.2.0b0 (2021-06-11)
+## 0.2.0rc1 (2021-06-11)
 
 ## 0.2.0rc0 (2021-06-11)
 
diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py
index e328f8d346..d95a173d8a 100644
--- a/tests/commands/test_check_command.py
+++ b/tests/commands/test_check_command.py
@@ -12,7 +12,7 @@
     InvalidCommitMessageError,
     NoCommitsFoundError,
 )
-from tests.utils import create_file_and_commit
+from tests.utils import create_file_and_commit, skip_below_py_3_13
 
 COMMIT_LOG = [
     "refactor: A code change that neither fixes a bug nor adds a feature",
@@ -238,7 +238,7 @@ def test_check_a_range_of_failed_git_commits(config, mocker: MockFixture):
     ill_formated_commits_msgs = [
         "First commit does not follow rule",
         "Second commit does not follow rule",
-        ("Third commit does not follow rule\n" "Ill-formatted commit with body"),
+        ("Third commit does not follow rule\nIll-formatted commit with body"),
     ]
     mocker.patch(
         "commitizen.git.get_commits",
@@ -365,7 +365,7 @@ def test_check_command_with_pipe_message_and_failed(mocker: MockFixture):
     assert "commit validation: failed!" in str(excinfo.value)
 
 
-def test_check_command_with_comment_in_messege_file(mocker: MockFixture, capsys):
+def test_check_command_with_comment_in_message_file(mocker: MockFixture, capsys):
     testargs = ["cz", "check", "--commit-msg-file", "some_file"]
     mocker.patch.object(sys, "argv", testargs)
     mocker.patch(
@@ -414,3 +414,41 @@ def test_check_conventional_commit_succeed_with_git_diff(mocker, capsys):
     cli.main()
     out, _ = capsys.readouterr()
     assert "Commit validation: successful!" in out
+
+
+@skip_below_py_3_13
+def test_check_command_shows_description_when_use_help_option(
+    mocker: MockFixture, capsys, file_regression
+):
+    testargs = ["cz", "check", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
+
+
+def test_check_command_with_message_length_limit(config, mocker: MockFixture):
+    success_mock = mocker.patch("commitizen.out.success")
+    message = "fix(scope): some commit message"
+    check_cmd = commands.Check(
+        config=config,
+        arguments={"message": message, "message_length_limit": len(message) + 1},
+    )
+
+    check_cmd()
+    success_mock.assert_called_once()
+
+
+def test_check_command_with_message_length_limit_exceeded(config, mocker: MockFixture):
+    error_mock = mocker.patch("commitizen.out.error")
+    message = "fix(scope): some commit message"
+    check_cmd = commands.Check(
+        config=config,
+        arguments={"message": message, "message_length_limit": len(message) - 1},
+    )
+
+    with pytest.raises(InvalidCommitMessageError):
+        check_cmd()
+        error_mock.assert_called_once()
diff --git a/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt b/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..85f42f6d2a
--- /dev/null
+++ b/tests/commands/test_check_command/test_check_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,25 @@
+usage: cz check [-h] [--commit-msg-file COMMIT_MSG_FILE |
+                --rev-range REV_RANGE | -m MESSAGE] [--allow-abort]
+                [--allowed-prefixes [ALLOWED_PREFIXES ...]]
+                [-l MESSAGE_LENGTH_LIMIT]
+
+validates that a commit message matches the commitizen schema
+
+options:
+  -h, --help            show this help message and exit
+  --commit-msg-file COMMIT_MSG_FILE
+                        ask for the name of the temporal file that contains
+                        the commit message. Using it in a git hook script:
+                        MSG_FILE=$1
+  --rev-range REV_RANGE
+                        a range of git rev to check. e.g, master..HEAD
+  -m, --message MESSAGE
+                        commit message that needs to be checked
+  --allow-abort         allow empty commit messages, which typically abort a
+                        commit
+  --allowed-prefixes [ALLOWED_PREFIXES ...]
+                        allowed commit message prefixes. If the message starts
+                        by one of these prefixes, the message won't be checked
+                        against the regex
+  -l, --message-length-limit MESSAGE_LENGTH_LIMIT
+                        length limit of the commit message; 0 for no limit
diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py
index e3f9989823..930e1a7a9b 100644
--- a/tests/commands/test_commit_command.py
+++ b/tests/commands/test_commit_command.py
@@ -1,13 +1,16 @@
 import os
+import sys
+from unittest.mock import ANY
 
 import pytest
 from pytest_mock import MockFixture
-from unittest.mock import ANY
 
-from commitizen import cmd, commands
+from commitizen import cli, cmd, commands
 from commitizen.cz.exceptions import CzException
+from commitizen.cz.utils import get_backup_file_path
 from commitizen.exceptions import (
     CommitError,
+    CommitMessageLengthExceededError,
     CustomError,
     DryRunExit,
     NoAnswersError,
@@ -16,6 +19,7 @@
     NotAllowed,
     NothingToCommitError,
 )
+from tests.utils import skip_below_py_3_13
 
 
 @pytest.fixture
@@ -25,6 +29,12 @@ def staging_is_clean(mocker: MockFixture, tmp_git_project):
     return tmp_git_project
 
 
+@pytest.fixture
+def backup_file(tmp_git_project):
+    with open(get_backup_file_path(), "w") as backup_file:
+        backup_file.write("backup commit")
+
+
 @pytest.mark.usefixtures("staging_is_clean")
 def test_commit(config, mocker: MockFixture):
     prompt_mock = mocker.patch("questionary.prompt")
@@ -45,6 +55,32 @@ def test_commit(config, mocker: MockFixture):
     success_mock.assert_called_once()
 
 
+@pytest.mark.usefixtures("staging_is_clean")
+def test_commit_backup_on_failure(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prompt_mock.return_value = {
+        "prefix": "feat",
+        "subject": "user created",
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "closes #21",
+        "footer": "",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("", "error", b"", b"", 9)
+    error_mock = mocker.patch("commitizen.out.error")
+
+    with pytest.raises(CommitError):
+        commit_cmd = commands.Commit(config, {})
+        temp_file = commit_cmd.temp_file
+        commit_cmd()
+
+    prompt_mock.assert_called_once()
+    error_mock.assert_called_once()
+    assert os.path.isfile(temp_file)
+
+
 @pytest.mark.usefixtures("staging_is_clean")
 def test_commit_retry_fails_no_backup(config, mocker: MockFixture):
     commit_mock = mocker.patch("commitizen.git.commit")
@@ -56,9 +92,27 @@ def test_commit_retry_fails_no_backup(config, mocker: MockFixture):
     assert NoCommitBackupError.message in str(excinfo.value)
 
 
-@pytest.mark.usefixtures("staging_is_clean")
+@pytest.mark.usefixtures("staging_is_clean", "backup_file")
 def test_commit_retry_works(config, mocker: MockFixture):
     prompt_mock = mocker.patch("questionary.prompt")
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
+
+    commit_cmd = commands.Commit(config, {"retry": True})
+    temp_file = commit_cmd.temp_file
+    commit_cmd()
+
+    commit_mock.assert_called_with("backup commit", args="")
+    prompt_mock.assert_not_called()
+    success_mock.assert_called_once()
+    assert not os.path.isfile(temp_file)
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+def test_commit_retry_after_failure_no_backup(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
     prompt_mock.return_value = {
         "prefix": "feat",
         "subject": "user created",
@@ -69,24 +123,56 @@ def test_commit_retry_works(config, mocker: MockFixture):
     }
 
     commit_mock = mocker.patch("commitizen.git.commit")
-    commit_mock.return_value = cmd.Command("", "error", b"", b"", 9)
-    error_mock = mocker.patch("commitizen.out.error")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
 
-    with pytest.raises(CommitError):
-        commit_cmd = commands.Commit(config, {})
-        temp_file = commit_cmd.temp_file
-        commit_cmd()
+    config.settings["retry_after_failure"] = True
+    commands.Commit(config, {})()
 
+    commit_mock.assert_called_with("feat: user created\n\ncloses #21", args="")
     prompt_mock.assert_called_once()
-    error_mock.assert_called_once()
-    assert os.path.isfile(temp_file)
+    success_mock.assert_called_once()
+
+
+@pytest.mark.usefixtures("staging_is_clean", "backup_file")
+def test_commit_retry_after_failure_works(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
 
-    # Previous commit failed, so retry should pick up the backup commit
-    # commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock = mocker.patch("commitizen.git.commit")
     commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
     success_mock = mocker.patch("commitizen.out.success")
 
-    commands.Commit(config, {"retry": True})()
+    config.settings["retry_after_failure"] = True
+    commit_cmd = commands.Commit(config, {})
+    temp_file = commit_cmd.temp_file
+    commit_cmd()
+
+    commit_mock.assert_called_with("backup commit", args="")
+    prompt_mock.assert_not_called()
+    success_mock.assert_called_once()
+    assert not os.path.isfile(temp_file)
+
+
+@pytest.mark.usefixtures("staging_is_clean", "backup_file")
+def test_commit_retry_after_failure_with_no_retry_works(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prompt_mock.return_value = {
+        "prefix": "feat",
+        "subject": "user created",
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "closes #21",
+        "footer": "",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
+
+    config.settings["retry_after_failure"] = True
+    commit_cmd = commands.Commit(config, {"no_retry": True})
+    temp_file = commit_cmd.temp_file
+    commit_cmd()
 
     commit_mock.assert_called_with("feat: user created\n\ncloses #21", args="")
     prompt_mock.assert_called_once()
@@ -174,7 +260,7 @@ def test_commit_command_with_signoff_option(config, mocker: MockFixture):
 
     commands.Commit(config, {"signoff": True})()
 
-    commit_mock.assert_called_once_with(ANY, args="-- -s")
+    commit_mock.assert_called_once_with(ANY, args="-s")
     success_mock.assert_called_once()
 
 
@@ -197,7 +283,32 @@ def test_commit_command_with_always_signoff_enabled(config, mocker: MockFixture)
     config.settings["always_signoff"] = True
     commands.Commit(config, {})()
 
-    commit_mock.assert_called_once_with(ANY, args="-- -s")
+    commit_mock.assert_called_once_with(ANY, args="-s")
+    success_mock.assert_called_once()
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+def test_commit_command_with_gpgsign_and_always_signoff_enabled(
+    config, mocker: MockFixture
+):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prompt_mock.return_value = {
+        "prefix": "feat",
+        "subject": "user created",
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "",
+        "footer": "",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
+
+    config.settings["always_signoff"] = True
+    commands.Commit(config, {"extra_cli_args": "-S"})()
+
+    commit_mock.assert_called_once_with(ANY, args="-S -s")
     success_mock.assert_called_once()
 
 
@@ -213,6 +324,55 @@ def test_commit_when_nothing_to_commit(config, mocker: MockFixture):
     assert "No files added to staging!" in str(excinfo.value)
 
 
+@pytest.mark.usefixtures("staging_is_clean")
+def test_commit_with_allow_empty(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prompt_mock.return_value = {
+        "prefix": "feat",
+        "subject": "user created",
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "closes #21",
+        "footer": "",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
+
+    commands.Commit(config, {"extra_cli_args": "--allow-empty"})()
+
+    commit_mock.assert_called_with(
+        "feat: user created\n\ncloses #21", args="--allow-empty"
+    )
+    success_mock.assert_called_once()
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+def test_commit_with_signoff_and_allow_empty(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prompt_mock.return_value = {
+        "prefix": "feat",
+        "subject": "user created",
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "closes #21",
+        "footer": "",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
+
+    config.settings["always_signoff"] = True
+    commands.Commit(config, {"extra_cli_args": "--allow-empty"})()
+
+    commit_mock.assert_called_with(
+        "feat: user created\n\ncloses #21", args="--allow-empty -s"
+    )
+    success_mock.assert_called_once()
+
+
 @pytest.mark.usefixtures("staging_is_clean")
 def test_commit_when_customized_expected_raised(config, mocker: MockFixture, capsys):
     _err = ValueError()
@@ -296,3 +456,101 @@ def test_commit_command_with_extra_args(config, mocker: MockFixture):
     commands.Commit(config, {"extra_cli_args": "-- -extra-args1 -extra-arg2"})()
     commit_mock.assert_called_once_with(ANY, args="-- -extra-args1 -extra-arg2")
     success_mock.assert_called_once()
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+def test_commit_command_with_message_length_limit(config, mocker: MockFixture):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prefix = "feat"
+    subject = "random subject"
+    message_length = len(prefix) + len(": ") + len(subject)
+    prompt_mock.return_value = {
+        "prefix": prefix,
+        "subject": subject,
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "random body",
+        "footer": "random footer",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command("success", "", b"", b"", 0)
+    success_mock = mocker.patch("commitizen.out.success")
+
+    commands.Commit(config, {"message_length_limit": message_length})()
+    success_mock.assert_called_once()
+
+    with pytest.raises(CommitMessageLengthExceededError):
+        commands.Commit(config, {"message_length_limit": message_length - 1})()
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+@pytest.mark.parametrize("editor", ["vim", None])
+def test_manual_edit(editor, config, mocker: MockFixture, tmp_path):
+    mocker.patch("commitizen.git.get_core_editor", return_value=editor)
+    subprocess_mock = mocker.patch("subprocess.call")
+
+    mocker.patch("shutil.which", return_value=editor)
+
+    test_message = "Initial commit message"
+    temp_file = tmp_path / "temp_commit_message"
+    temp_file.write_text(test_message)
+
+    mock_temp_file = mocker.patch("tempfile.NamedTemporaryFile")
+    mock_temp_file.return_value.__enter__.return_value.name = str(temp_file)
+
+    commit_cmd = commands.Commit(config, {"edit": True})
+
+    if editor is None:
+        with pytest.raises(RuntimeError):
+            commit_cmd.manual_edit(test_message)
+    else:
+        edited_message = commit_cmd.manual_edit(test_message)
+
+        subprocess_mock.assert_called_once_with(["vim", str(temp_file)])
+
+        assert edited_message == test_message.strip()
+
+
+@skip_below_py_3_13
+def test_commit_command_shows_description_when_use_help_option(
+    mocker: MockFixture, capsys, file_regression
+):
+    testargs = ["cz", "commit", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
+
+
+@pytest.mark.usefixtures("staging_is_clean")
+@pytest.mark.parametrize(
+    "out", ["no changes added to commit", "nothing added to commit"]
+)
+def test_commit_when_nothing_added_to_commit(config, mocker: MockFixture, out):
+    prompt_mock = mocker.patch("questionary.prompt")
+    prompt_mock.return_value = {
+        "prefix": "feat",
+        "subject": "user created",
+        "scope": "",
+        "is_breaking_change": False,
+        "body": "",
+        "footer": "",
+    }
+
+    commit_mock = mocker.patch("commitizen.git.commit")
+    commit_mock.return_value = cmd.Command(
+        out=out,
+        err="",
+        stdout=out.encode(),
+        stderr=b"",
+        return_code=0,
+    )
+    error_mock = mocker.patch("commitizen.out.error")
+
+    commands.Commit(config, {})()
+
+    commit_mock.assert_called_once()
+    error_mock.assert_called_once_with(out)
diff --git a/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..ba531042aa
--- /dev/null
+++ b/tests/commands/test_commit_command/test_commit_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,22 @@
+usage: cz commit [-h] [--retry] [--no-retry] [--dry-run]
+                 [--write-message-to-file FILE_PATH] [-s] [-a] [-e]
+                 [-l MESSAGE_LENGTH_LIMIT] [--]
+
+create new commit
+
+options:
+  -h, --help            show this help message and exit
+  --retry               retry last commit
+  --no-retry            skip retry if retry_after_failure is set to true
+  --dry-run             show output to stdout, no commit, no modified files
+  --write-message-to-file FILE_PATH
+                        write message to file before committing (can be
+                        combined with --dry-run)
+  -s, --signoff         Deprecated, use 'cz commit -- -s' instead
+  -a, --all             Tell the command to automatically stage files that
+                        have been modified and deleted, but new files you have
+                        not told Git about are not affected.
+  -e, --edit            edit the commit message before committing
+  -l, --message-length-limit MESSAGE_LENGTH_LIMIT
+                        length limit of the commit message; 0 for no limit
+  --                    Positional arguments separator (recommended)
diff --git a/tests/commands/test_example_command.py b/tests/commands/test_example_command.py
new file mode 100644
index 0000000000..0521679f1c
--- /dev/null
+++ b/tests/commands/test_example_command.py
@@ -0,0 +1,26 @@
+import sys
+
+import pytest
+from pytest_mock import MockerFixture
+
+from commitizen import cli, commands
+from tests.utils import skip_below_py_3_10
+
+
+def test_example(config, mocker: MockerFixture):
+    write_mock = mocker.patch("commitizen.out.write")
+    commands.Example(config)()
+    write_mock.assert_called_once()
+
+
+@skip_below_py_3_10
+def test_example_command_shows_description_when_use_help_option(
+    mocker: MockerFixture, capsys, file_regression
+):
+    testargs = ["cz", "example", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_example_command/test_example_command_shows_description_when_use_help_option.txt b/tests/commands/test_example_command/test_example_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..b9bf7f84fc
--- /dev/null
+++ b/tests/commands/test_example_command/test_example_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,6 @@
+usage: cz example [-h]
+
+show commit example
+
+options:
+  -h, --help  show this help message and exit
diff --git a/tests/commands/test_info_command.py b/tests/commands/test_info_command.py
new file mode 100644
index 0000000000..2bd1553679
--- /dev/null
+++ b/tests/commands/test_info_command.py
@@ -0,0 +1,26 @@
+import sys
+
+import pytest
+from pytest_mock import MockerFixture
+
+from commitizen import cli, commands
+from tests.utils import skip_below_py_3_10
+
+
+def test_info(config, mocker: MockerFixture):
+    write_mock = mocker.patch("commitizen.out.write")
+    commands.Info(config)()
+    write_mock.assert_called_once()
+
+
+@skip_below_py_3_10
+def test_info_command_shows_description_when_use_help_option(
+    mocker: MockerFixture, capsys, file_regression
+):
+    testargs = ["cz", "info", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_info_command/test_info_command_shows_description_when_use_help_option.txt b/tests/commands/test_info_command/test_info_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..99b1ba8a4a
--- /dev/null
+++ b/tests/commands/test_info_command/test_info_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,6 @@
+usage: cz info [-h]
+
+show information about the cz
+
+options:
+  -h, --help  show this help message and exit
diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py
index ba77a12e3c..3f12d0bd7f 100644
--- a/tests/commands/test_init_command.py
+++ b/tests/commands/test_init_command.py
@@ -2,15 +2,17 @@
 
 import json
 import os
+import sys
 from typing import Any
 
 import pytest
 import yaml
 from pytest_mock import MockFixture
 
-from commitizen import commands
+from commitizen import cli, commands
 from commitizen.__version__ import __version__
 from commitizen.exceptions import InitFailedError, NoAnswersError
+from tests.utils import skip_below_py_3_10
 
 
 class FakeQuestion:
@@ -84,7 +86,7 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config)
 def test_init_when_config_already_exists(config, capsys):
     # Set config path
     path = os.sep.join(["tests", "pyproject.toml"])
-    config.add_path(path)
+    config.path = path
 
     commands.Init(config)()
     captured = capsys.readouterr()
@@ -93,7 +95,7 @@ def test_init_when_config_already_exists(config, capsys):
 
 def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir):
     mocker.patch(
-        "commitizen.commands.init.get_tag_names", return_value=["0.0.1", "0.0.2"]
+        "commitizen.commands.init.get_tag_names", return_value=["0.0.2", "0.0.1"]
     )
     mocker.patch("commitizen.commands.init.get_latest_tag_name", return_value="0.0.2")
     mocker.patch(
@@ -181,7 +183,7 @@ def check_pre_commit_config(expected: list[dict[str, Any]]):
 
 @pytest.mark.usefixtures("pre_commit_installed")
 class TestPreCommitCases:
-    def test_no_existing_pre_commit_conifg(_, default_choice, tmpdir, config):
+    def test_no_existing_pre_commit_config(_, default_choice, tmpdir, config):
         with tmpdir.as_cwd():
             commands.Init(config)()
             check_cz_config(default_choice)
@@ -251,3 +253,16 @@ def test_pre_commit_exec_failed(
         with tmpdir.as_cwd():
             with pytest.raises(InitFailedError):
                 commands.Init(config)()
+
+
+@skip_below_py_3_10
+def test_init_command_shows_description_when_use_help_option(
+    mocker: MockFixture, capsys, file_regression
+):
+    testargs = ["cz", "init", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_init_command/test_init_command_shows_description_when_use_help_option.txt b/tests/commands/test_init_command/test_init_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..0f72042f88
--- /dev/null
+++ b/tests/commands/test_init_command/test_init_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,6 @@
+usage: cz init [-h]
+
+init commitizen configuration
+
+options:
+  -h, --help  show this help message and exit
diff --git a/tests/commands/test_ls_command.py b/tests/commands/test_ls_command.py
new file mode 100644
index 0000000000..7225d2a85c
--- /dev/null
+++ b/tests/commands/test_ls_command.py
@@ -0,0 +1,26 @@
+import sys
+
+import pytest
+from pytest_mock import MockerFixture
+
+from commitizen import cli, commands
+from tests.utils import skip_below_py_3_10
+
+
+def test_list_cz(config, mocker: MockerFixture):
+    write_mock = mocker.patch("commitizen.out.write")
+    commands.ListCz(config)()
+    write_mock.assert_called_once()
+
+
+@skip_below_py_3_10
+def test_ls_command_shows_description_when_use_help_option(
+    mocker: MockerFixture, capsys, file_regression
+):
+    testargs = ["cz", "ls", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_ls_command/test_ls_command_shows_description_when_use_help_option.txt b/tests/commands/test_ls_command/test_ls_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..5fa8fe1f79
--- /dev/null
+++ b/tests/commands/test_ls_command/test_ls_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,6 @@
+usage: cz ls [-h]
+
+show available commitizens
+
+options:
+  -h, --help  show this help message and exit
diff --git a/tests/commands/test_other_commands.py b/tests/commands/test_other_commands.py
deleted file mode 100644
index 3f7f855d54..0000000000
--- a/tests/commands/test_other_commands.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from pytest_mock import MockFixture
-
-from commitizen import commands
-
-
-def test_example(config, mocker: MockFixture):
-    write_mock = mocker.patch("commitizen.out.write")
-    commands.Example(config)()
-    write_mock.assert_called_once()
-
-
-def test_info(config, mocker: MockFixture):
-    write_mock = mocker.patch("commitizen.out.write")
-    commands.Info(config)()
-    write_mock.assert_called_once()
-
-
-def test_schema(config, mocker: MockFixture):
-    write_mock = mocker.patch("commitizen.out.write")
-    commands.Schema(config)()
-    write_mock.assert_called_once()
-
-
-def test_list_cz(config, mocker: MockFixture):
-    write_mock = mocker.patch("commitizen.out.write")
-    commands.ListCz(config)()
-    write_mock.assert_called_once()
diff --git a/tests/commands/test_schema_command.py b/tests/commands/test_schema_command.py
new file mode 100644
index 0000000000..5e571721c5
--- /dev/null
+++ b/tests/commands/test_schema_command.py
@@ -0,0 +1,26 @@
+import sys
+
+import pytest
+from pytest_mock import MockerFixture
+
+from commitizen import cli, commands
+from tests.utils import skip_below_py_3_10
+
+
+def test_schema(config, mocker: MockerFixture):
+    write_mock = mocker.patch("commitizen.out.write")
+    commands.Schema(config)()
+    write_mock.assert_called_once()
+
+
+@skip_below_py_3_10
+def test_schema_command_shows_description_when_use_help_option(
+    mocker: MockerFixture, capsys, file_regression
+):
+    testargs = ["cz", "schema", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_schema_command/test_schema_command_shows_description_when_use_help_option.txt b/tests/commands/test_schema_command/test_schema_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..6666db4d41
--- /dev/null
+++ b/tests/commands/test_schema_command/test_schema_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,6 @@
+usage: cz schema [-h]
+
+show commit schema
+
+options:
+  -h, --help  show this help message and exit
diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py
index 3f9de50d00..3dcbed168b 100644
--- a/tests/commands/test_version_command.py
+++ b/tests/commands/test_version_command.py
@@ -4,9 +4,10 @@
 import pytest
 from pytest_mock import MockerFixture
 
-from commitizen import commands
+from commitizen import cli, commands
 from commitizen.__version__ import __version__
 from commitizen.config.base_config import BaseConfig
+from tests.utils import skip_below_py_3_10
 
 
 def test_version_for_showing_project_version(config, capsys):
@@ -60,7 +61,7 @@ def test_version_for_showing_both_versions(config, capsys):
     )()
     captured = capsys.readouterr()
     expected_out = (
-        f"Installed Commitizen Version: {__version__}\n" f"Project Version: v0.0.1"
+        f"Installed Commitizen Version: {__version__}\nProject Version: v0.0.1"
     )
     assert expected_out in captured.out
 
@@ -96,7 +97,6 @@ def test_version_use_version_provider(
         {
             "report": False,
             "project": project,
-            "commitizen": False,
             "verbose": not project,
         },
     )()
@@ -106,3 +106,16 @@ def test_version_use_version_provider(
     get_provider.assert_called_once()
     mock.get_version.assert_called_once()
     mock.set_version.assert_not_called()
+
+
+@skip_below_py_3_10
+def test_version_command_shows_description_when_use_help_option(
+    mocker: MockerFixture, capsys, file_regression
+):
+    testargs = ["cz", "version", "--help"]
+    mocker.patch.object(sys, "argv", testargs)
+    with pytest.raises(SystemExit):
+        cli.main()
+
+    out, _ = capsys.readouterr()
+    file_regression.check(out, extension=".txt")
diff --git a/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt
new file mode 100644
index 0000000000..c461b10bcd
--- /dev/null
+++ b/tests/commands/test_version_command/test_version_command_shows_description_when_use_help_option.txt
@@ -0,0 +1,12 @@
+usage: cz version [-h] [-r | -p | -c | -v]
+
+get the version of the installed commitizen or the current project (default:
+installed commitizen)
+
+options:
+  -h, --help        show this help message and exit
+  -r, --report      get system information for reporting bugs
+  -p, --project     get the version of the current project
+  -c, --commitizen  get the version of the installed commitizen
+  -v, --verbose     get the version of both the installed commitizen and the
+                    current project
diff --git a/tests/conftest.py b/tests/conftest.py
index 76d2e53fb7..324ef9bebb 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,20 +3,21 @@
 import os
 import re
 import tempfile
+from collections.abc import Iterator, Mapping
 from pathlib import Path
-from typing import Iterator
 
 import pytest
 from pytest_mock import MockerFixture
 
 from commitizen import cmd, defaults
-from commitizen.config import BaseConfig
-from commitizen.cz import registry
-from commitizen.cz.base import BaseCommitizen
 from commitizen.changelog_formats import (
     ChangelogFormat,
     get_changelog_format,
 )
+from commitizen.config import BaseConfig
+from commitizen.cz import registry
+from commitizen.cz.base import BaseCommitizen
+from commitizen.question import CzQuestion
 from tests.utils import create_file_and_commit
 
 SIGNER = "GitHub Action"
@@ -35,12 +36,19 @@ def git_sandbox(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
     gitconfig = tmp_path / ".git" / "config"
     if not gitconfig.parent.exists():
         gitconfig.parent.mkdir()
+
     monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(gitconfig))
+
     r = cmd.run(f"git config --file {gitconfig} user.name {SIGNER}")
     assert r.return_code == 0, r.err
     r = cmd.run(f"git config --file {gitconfig} user.email {SIGNER_MAIL}")
     assert r.return_code == 0, r.err
-    cmd.run("git config --global init.defaultBranch master")
+
+    r = cmd.run(f"git config --file {gitconfig} safe.directory '*'")
+    assert r.return_code == 0, r.err
+
+    r = cmd.run("git config --global init.defaultBranch master")
+    assert r.return_code == 0, r.err
 
 
 @pytest.fixture
@@ -62,7 +70,7 @@ def tmp_git_project(tmpdir):
 @pytest.fixture(scope="function")
 def tmp_commitizen_project(tmp_git_project):
     tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml")
-    tmp_commitizen_cfg_file.write("[tool.commitizen]\n" 'version="0.1.0"\n')
+    tmp_commitizen_cfg_file.write('[tool.commitizen]\nversion="0.1.0"\n')
 
     yield tmp_git_project
 
@@ -76,9 +84,7 @@ def _initial(
     ):
         with tmp_git_project.as_cwd():
             tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml")
-            tmp_commitizen_cfg_file.write(
-                f"[tool.commitizen]\n" f'version="{version}"\n'
-            )
+            tmp_commitizen_cfg_file.write(f'[tool.commitizen]\nversion="{version}"\n')
             tmp_version_file = tmp_git_project.join("__version__.py")
             tmp_version_file.write(version)
             tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml")
@@ -164,7 +170,7 @@ class SemverCommitizen(BaseCommitizen):
         "patch": "PATCH",
     }
     changelog_pattern = r"^(patch|minor|major)"
-    commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)"  # noqa
+    commit_parser = r"^(?P<change_type>patch|minor|major)(?:\((?P<scope>[^()\r\n]*)\)|\()?:?\s(?P<message>.+)"
     change_type_map = {
         "major": "Breaking Changes",
         "minor": "Features",
@@ -204,7 +210,7 @@ def questions(self) -> list:
             },
         ]
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping) -> str:
         prefix = answers["prefix"]
         subject = answers.get("subject", "default message").trim()
         return f"{prefix}: {subject}"
@@ -217,17 +223,17 @@ def use_cz_semver(mocker):
 
 
 class MockPlugin(BaseCommitizen):
-    def questions(self) -> defaults.Questions:
+    def questions(self) -> list[CzQuestion]:
         return []
 
-    def message(self, answers: dict) -> str:
+    def message(self, answers: Mapping) -> str:
         return ""
 
 
 @pytest.fixture
 def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen:
     mock = MockPlugin(config)
-    mocker.patch("commitizen.factory.commiter_factory", return_value=mock)
+    mocker.patch("commitizen.factory.committer_factory", return_value=mock)
     return mock
 
 
@@ -244,7 +250,7 @@ def changelog_format(
     if "tmp_commitizen_project" in request.fixturenames:
         tmp_commitizen_project = request.getfixturevalue("tmp_commitizen_project")
         pyproject = tmp_commitizen_project / "pyproject.toml"
-        pyproject.write(f"{pyproject.read()}\n" f'changelog_format = "{format}"\n')
+        pyproject.write(f'{pyproject.read()}\nchangelog_format = "{format}"\n')
     return get_changelog_format(config)
 
 
diff --git a/tests/providers/conftest.py b/tests/providers/conftest.py
index b4432ca524..f73cdb72a5 100644
--- a/tests/providers/conftest.py
+++ b/tests/providers/conftest.py
@@ -1,8 +1,8 @@
 from __future__ import annotations
 
 import os
+from collections.abc import Iterator
 from pathlib import Path
-from typing import Iterator
 
 import pytest
 
diff --git a/tests/providers/test_base_provider.py b/tests/providers/test_base_provider.py
index 09a2bd71cd..482bd698ea 100644
--- a/tests/providers/test_base_provider.py
+++ b/tests/providers/test_base_provider.py
@@ -1,6 +1,5 @@
 from __future__ import annotations
 
-
 import pytest
 
 from commitizen.config.base_config import BaseConfig
diff --git a/tests/providers/test_cargo_provider.py b/tests/providers/test_cargo_provider.py
index cde868deb5..4b20c6ea53 100644
--- a/tests/providers/test_cargo_provider.py
+++ b/tests/providers/test_cargo_provider.py
@@ -9,14 +9,13 @@
 from commitizen.providers import get_provider
 from commitizen.providers.cargo_provider import CargoProvider
 
-
 CARGO_TOML = """\
 [package]
 name = "whatever"
 version = "0.1.0"
 """
 
-CARGO_EXPECTED = """\
+CARGO_TOML_EXPECTED = """\
 [package]
 name = "whatever"
 version = "42.1"
@@ -28,18 +27,44 @@
 version = "0.1.0"
 """
 
-CARGO_WORKSPACE_EXPECTED = """\
+CARGO_WORKSPACE_TOML_EXPECTED = """\
 [workspace.package]
 name = "whatever"
 version = "42.1"
 """
 
+CARGO_LOCK = """\
+[[package]]
+name = "whatever"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "123abc"
+dependencies = [
+ "packageA",
+ "packageB",
+ "packageC",
+]
+"""
+
+CARGO_LOCK_EXPECTED = """\
+[[package]]
+name = "whatever"
+version = "42.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "123abc"
+dependencies = [
+ "packageA",
+ "packageB",
+ "packageC",
+]
+"""
+
 
 @pytest.mark.parametrize(
     "content, expected",
     (
-        (CARGO_TOML, CARGO_EXPECTED),
-        (CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_EXPECTED),
+        (CARGO_TOML, CARGO_TOML_EXPECTED),
+        (CARGO_WORKSPACE_TOML, CARGO_WORKSPACE_TOML_EXPECTED),
     ),
 )
 def test_cargo_provider(
@@ -59,3 +84,46 @@ def test_cargo_provider(
 
     provider.set_version("42.1")
     assert file.read_text() == dedent(expected)
+
+
+@pytest.mark.parametrize(
+    "toml_content, lock_content, toml_expected, lock_expected",
+    (
+        (
+            CARGO_TOML,
+            CARGO_LOCK,
+            CARGO_TOML_EXPECTED,
+            CARGO_LOCK_EXPECTED,
+        ),
+        (
+            CARGO_WORKSPACE_TOML,
+            CARGO_LOCK,
+            CARGO_WORKSPACE_TOML_EXPECTED,
+            CARGO_LOCK_EXPECTED,
+        ),
+    ),
+)
+def test_cargo_provider_with_lock(
+    config: BaseConfig,
+    chdir: Path,
+    toml_content: str,
+    lock_content: str,
+    toml_expected: str,
+    lock_expected: str,
+):
+    filename = CargoProvider.filename
+    file = chdir / filename
+    file.write_text(dedent(toml_content))
+
+    lock_filename = CargoProvider.lock_filename
+    lock_file = chdir / lock_filename
+    lock_file.write_text(dedent(lock_content))
+    config.settings["version_provider"] = "cargo"
+
+    provider = get_provider(config)
+    assert isinstance(provider, CargoProvider)
+    assert provider.get_version() == "0.1.0"
+
+    provider.set_version("42.1")
+    assert file.read_text() == dedent(toml_expected)
+    assert lock_file.read_text() == dedent(lock_expected)
diff --git a/tests/providers/test_commitizen_provider.py b/tests/providers/test_commitizen_provider.py
index 887adf3d12..b8df60da93 100644
--- a/tests/providers/test_commitizen_provider.py
+++ b/tests/providers/test_commitizen_provider.py
@@ -2,11 +2,9 @@
 
 from typing import TYPE_CHECKING
 
-
 from commitizen.config.base_config import BaseConfig
 from commitizen.providers.commitizen_provider import CommitizenProvider
 
-
 if TYPE_CHECKING:
     from pytest_mock import MockerFixture
 
diff --git a/tests/providers/test_composer_provider.py b/tests/providers/test_composer_provider.py
index ce72ae4703..45cbc8afa4 100644
--- a/tests/providers/test_composer_provider.py
+++ b/tests/providers/test_composer_provider.py
@@ -9,7 +9,6 @@
 from commitizen.providers import get_provider
 from commitizen.providers.composer_provider import ComposerProvider
 
-
 COMPOSER_JSON = """\
 {
     "name": "whatever",
diff --git a/tests/providers/test_npm_provider.py b/tests/providers/test_npm_provider.py
index 2e5ceb42d5..bc9399916d 100644
--- a/tests/providers/test_npm_provider.py
+++ b/tests/providers/test_npm_provider.py
@@ -9,7 +9,6 @@
 from commitizen.providers import get_provider
 from commitizen.providers.npm_provider import NpmProvider
 
-
 NPM_PACKAGE_JSON = """\
 {
   "name": "whatever",
diff --git a/tests/providers/test_pep621_provider.py b/tests/providers/test_pep621_provider.py
index 9e82213294..16bb479cc4 100644
--- a/tests/providers/test_pep621_provider.py
+++ b/tests/providers/test_pep621_provider.py
@@ -9,7 +9,6 @@
 from commitizen.providers import get_provider
 from commitizen.providers.pep621_provider import Pep621Provider
 
-
 PEP621_TOML = """\
 [project]
 version = "0.1.0"
diff --git a/tests/providers/test_poetry_provider.py b/tests/providers/test_poetry_provider.py
index 9e327db6ad..e26e2a44fb 100644
--- a/tests/providers/test_poetry_provider.py
+++ b/tests/providers/test_poetry_provider.py
@@ -9,7 +9,6 @@
 from commitizen.providers import get_provider
 from commitizen.providers.poetry_provider import PoetryProvider
 
-
 POETRY_TOML = """\
 [tool.poetry]
 version = "0.1.0"
diff --git a/tests/providers/test_scm_provider.py b/tests/providers/test_scm_provider.py
index a0bfc46474..9d955b2323 100644
--- a/tests/providers/test_scm_provider.py
+++ b/tests/providers/test_scm_provider.py
@@ -1,12 +1,17 @@
 from __future__ import annotations
 
-
 import pytest
 
 from commitizen.config.base_config import BaseConfig
 from commitizen.providers import get_provider
 from commitizen.providers.scm_provider import ScmProvider
-from tests.utils import create_file_and_commit, create_tag
+from tests.utils import (
+    create_branch,
+    create_file_and_commit,
+    create_tag,
+    merge_branch,
+    switch_branch,
+)
 
 
 @pytest.mark.parametrize(
@@ -22,7 +27,8 @@
         # much more lenient but require a v prefix.
         ("v$version", "v0.1.0", "0.1.0"),
         ("v$version", "no-match-because-no-v-prefix", "0.0.0"),
-        ("v$version", "v-match-TAG_FORMAT_REGEXS", "-match-TAG_FORMAT_REGEXS"),
+        # no match because not a valid version
+        ("v$version", "v-match-TAG_FORMAT_REGEXS", "0.0.0"),
         ("version-$version", "version-0.1.0", "0.1.0"),
         ("version-$version", "version-0.1", "0.1"),
         ("version-$version", "version-0.1.0rc1", "0.1.0rc1"),
@@ -62,3 +68,71 @@ def test_scm_provider_default_without_commits_and_tags(config: BaseConfig):
     provider = get_provider(config)
     assert isinstance(provider, ScmProvider)
     assert provider.get_version() == "0.0.0"
+
+
+@pytest.mark.usefixtures("tmp_git_project")
+def test_scm_provider_default_with_commits_and_tags(config: BaseConfig):
+    config.settings["version_provider"] = "scm"
+
+    provider = get_provider(config)
+    assert isinstance(provider, ScmProvider)
+    assert provider.get_version() == "0.0.0"
+
+    create_file_and_commit("Initial state")
+    create_tag("1.0.0")
+    # create develop
+    create_branch("develop")
+    switch_branch("develop")
+
+    # add a feature to develop
+    create_file_and_commit("develop: add beta feature1")
+    assert provider.get_version() == "1.0.0"
+    create_tag("1.1.0b0")
+
+    # create staging
+    create_branch("staging")
+    switch_branch("staging")
+    create_file_and_commit("staging: Starting release candidate")
+    assert provider.get_version() == "1.1.0b0"
+    create_tag("1.1.0rc0")
+
+    # add another feature to develop
+    switch_branch("develop")
+    create_file_and_commit("develop: add beta feature2")
+    assert provider.get_version() == "1.1.0b0"
+    create_tag("1.2.0b0")
+
+    # add a hotfix to master
+    switch_branch("master")
+    create_file_and_commit("master: add hotfix")
+    assert provider.get_version() == "1.0.0"
+    create_tag("1.0.1")
+
+    # merge the hotfix to staging
+    switch_branch("staging")
+    merge_branch("master")
+
+    assert provider.get_version() == "1.1.0rc0"
+
+
+@pytest.mark.usefixtures("tmp_git_project")
+def test_scm_provider_detect_legacy_tags(config: BaseConfig):
+    config.settings["version_provider"] = "scm"
+    config.settings["tag_format"] = "v${version}"
+    config.settings["legacy_tag_formats"] = [
+        "legacy-${version}",
+        "old-${version}",
+    ]
+    provider = get_provider(config)
+
+    create_file_and_commit("test: fake commit")
+    create_tag("old-0.4.1")
+    assert provider.get_version() == "0.4.1"
+
+    create_file_and_commit("test: fake commit")
+    create_tag("legacy-0.4.2")
+    assert provider.get_version() == "0.4.2"
+
+    create_file_and_commit("test: fake commit")
+    create_tag("v0.5.0")
+    assert provider.get_version() == "0.5.0"
diff --git a/tests/providers/test_uv_provider.py b/tests/providers/test_uv_provider.py
new file mode 100644
index 0000000000..4093709376
--- /dev/null
+++ b/tests/providers/test_uv_provider.py
@@ -0,0 +1,97 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from commitizen.config.base_config import BaseConfig
+from commitizen.providers import get_provider
+from commitizen.providers.uv_provider import UvProvider
+
+if TYPE_CHECKING:
+    from pytest_regressions.file_regression import FileRegressionFixture
+
+
+PYPROJECT_TOML = """
+[project]
+name = "test-uv"
+version = "4.2.1"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.13"
+dependencies = ["commitizen==4.2.1"]
+"""
+
+UV_LOCK_SIMPLIFIED = """
+version = 1
+revision = 1
+requires-python = ">=3.13"
+
+[[package]]
+name = "commitizen"
+version = "4.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "argcomplete" },
+    { name = "charset-normalizer" },
+    { name = "colorama" },
+    { name = "decli" },
+    { name = "jinja2" },
+    { name = "packaging" },
+    { name = "pyyaml" },
+    { name = "questionary" },
+    { name = "termcolor" },
+    { name = "tomlkit" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d8/a3/77ffc9aee014cbf46c84c9f156a1ddef2d4c7cfb87d567decf2541464245/commitizen-4.2.1.tar.gz", hash = "sha256:5255416f6d6071068159f0b97605777f3e25d00927ff157b7a8d01efeda7b952", size = 50645 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/57/ce/2f5d8ebe8376991b5f805e9f33d20c7f4c9ca6155bdbda761117dc41dff1/commitizen-4.2.1-py3-none-any.whl", hash = "sha256:a347889e0fe408c3b920a34130d8f35616be3ea8ac6b7b20c5b9aac19762661b", size = 72646 },
+]
+
+[[package]]
+name = "decli"
+version = "0.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3d/a0/a4658f93ecb589f479037b164dc13c68d108b50bf6594e54c820749f97ac/decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f", size = 7424 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 },
+]
+
+[[package]]
+name = "test-uv"
+version = "4.2.1"
+source = { virtual = "." }
+dependencies = [
+    { name = "commitizen" },
+]
+"""
+
+
+def test_uv_provider(
+    config: BaseConfig, tmpdir, file_regression: FileRegressionFixture
+):
+    with tmpdir.as_cwd():
+        pyproject_toml_file = tmpdir / UvProvider.filename
+        pyproject_toml_file.write_text(PYPROJECT_TOML, encoding="utf-8")
+
+        uv_lock_file = tmpdir / UvProvider.lock_filename
+        uv_lock_file.write_text(UV_LOCK_SIMPLIFIED, encoding="utf-8")
+
+        config.settings["version_provider"] = "uv"
+
+        provider = get_provider(config)
+        assert isinstance(provider, UvProvider)
+        assert provider.get_version() == "4.2.1"
+
+        provider.set_version("100.100.100")
+        assert provider.get_version() == "100.100.100"
+
+        updated_pyproject_toml_content = pyproject_toml_file.read_text(encoding="utf-8")
+        updated_uv_lock_content = uv_lock_file.read_text(encoding="utf-8")
+
+        for content in (updated_pyproject_toml_content, updated_uv_lock_content):
+            # updated project version
+            assert "100.100.100" in content
+            # commitizen version which was the same as project version and should not be affected
+            assert "4.2.1" in content
+
+        file_regression.check(updated_pyproject_toml_content, extension=".toml")
+        file_regression.check(updated_uv_lock_content, extension=".lock")
diff --git a/tests/providers/test_uv_provider/test_uv_provider.lock b/tests/providers/test_uv_provider/test_uv_provider.lock
new file mode 100644
index 0000000000..d353763ce3
--- /dev/null
+++ b/tests/providers/test_uv_provider/test_uv_provider.lock
@@ -0,0 +1,42 @@
+
+version = 1
+revision = 1
+requires-python = ">=3.13"
+
+[[package]]
+name = "commitizen"
+version = "4.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "argcomplete" },
+    { name = "charset-normalizer" },
+    { name = "colorama" },
+    { name = "decli" },
+    { name = "jinja2" },
+    { name = "packaging" },
+    { name = "pyyaml" },
+    { name = "questionary" },
+    { name = "termcolor" },
+    { name = "tomlkit" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d8/a3/77ffc9aee014cbf46c84c9f156a1ddef2d4c7cfb87d567decf2541464245/commitizen-4.2.1.tar.gz", hash = "sha256:5255416f6d6071068159f0b97605777f3e25d00927ff157b7a8d01efeda7b952", size = 50645 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/57/ce/2f5d8ebe8376991b5f805e9f33d20c7f4c9ca6155bdbda761117dc41dff1/commitizen-4.2.1-py3-none-any.whl", hash = "sha256:a347889e0fe408c3b920a34130d8f35616be3ea8ac6b7b20c5b9aac19762661b", size = 72646 },
+]
+
+[[package]]
+name = "decli"
+version = "0.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3d/a0/a4658f93ecb589f479037b164dc13c68d108b50bf6594e54c820749f97ac/decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f", size = 7424 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 },
+]
+
+[[package]]
+name = "test-uv"
+version = "100.100.100"
+source = { virtual = "." }
+dependencies = [
+    { name = "commitizen" },
+]
diff --git a/tests/providers/test_uv_provider/test_uv_provider.toml b/tests/providers/test_uv_provider/test_uv_provider.toml
new file mode 100644
index 0000000000..9fdb6eb5aa
--- /dev/null
+++ b/tests/providers/test_uv_provider/test_uv_provider.toml
@@ -0,0 +1,8 @@
+
+[project]
+name = "test-uv"
+version = "100.100.100"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.13"
+dependencies = ["commitizen==4.2.1"]
diff --git a/tests/test_bump_create_commit_message.py b/tests/test_bump_create_commit_message.py
index c096f23c39..0002659396 100644
--- a/tests/test_bump_create_commit_message.py
+++ b/tests/test_bump_create_commit_message.py
@@ -24,7 +24,19 @@ def test_create_tag(test_input, expected):
     assert new_tag == expected
 
 
-@pytest.mark.parametrize("retry", (True, False))
+@pytest.mark.parametrize(
+    "retry",
+    (
+        pytest.param(
+            True,
+            marks=pytest.mark.skipif(
+                sys.version_info >= (3, 13),
+                reason="mirrors-prettier is not supported with Python 3.13 or higher",
+            ),
+        ),
+        False,
+    ),
+)
 @pytest.mark.usefixtures("tmp_commitizen_project")
 def test_bump_pre_commit_changelog(mocker: MockFixture, freezer, retry):
     freezer.move_to("2022-04-01")
@@ -99,3 +111,50 @@ def test_bump_pre_commit_changelog_fails_always(mocker: MockFixture, freezer, re
     cmd.run("pre-commit install")
     with pytest.raises(exceptions.BumpCommitFailedError):
         cli.main()
+
+
+@pytest.mark.usefixtures("tmp_commitizen_project")
+def test_bump_with_build_metadata(mocker: MockFixture, freezer):
+    def _add_entry(test_str: str, args: list):
+        Path(test_str).write_text("")
+        cmd.run("git add -A")
+        cmd.run(f'git commit -m "fix: test-{test_str}"')
+        cz_args = ["cz", "bump", "--changelog", "--yes"] + args
+        mocker.patch.object(sys, "argv", cz_args)
+        cli.main()
+
+    freezer.move_to("2024-01-01")
+
+    _add_entry("a", ["--build-metadata", "a.b.c"])
+    _add_entry("b", [])
+    _add_entry("c", ["--build-metadata", "alongmetadatastring"])
+    _add_entry("d", [])
+
+    # Pre-commit fixed last line adding extra indent and "\" char
+    assert Path("CHANGELOG.md").read_text() == dedent(
+        """\
+        ## 0.1.4 (2024-01-01)
+
+        ### Fix
+
+        - test-d
+
+        ## 0.1.3+alongmetadatastring (2024-01-01)
+
+        ### Fix
+
+        - test-c
+
+        ## 0.1.2 (2024-01-01)
+
+        ### Fix
+
+        - test-b
+
+        ## 0.1.1+a.b.c (2024-01-01)
+
+        ### Fix
+
+        - test-a
+        """
+    )
diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py
index 8e8fc2705e..77e11c78c7 100644
--- a/tests/test_bump_find_increment.py
+++ b/tests/test_bump_find_increment.py
@@ -2,6 +2,7 @@
 CC: Conventional commits
 SVE: Semantic version at the end
 """
+
 import pytest
 
 from commitizen import bump
@@ -31,14 +32,14 @@
 MAJOR_INCREMENTS_BREAKING_CHANGE_CC = [
     "feat(cli): added version",
     "docs(README): motivation",
-    "BREAKING CHANGE: `extends` key in config file is now used for extending other config files",  # noqa
+    "BREAKING CHANGE: `extends` key in config file is now used for extending other config files",
     "fix(setup.py): future is now required for every python version",
 ]
 
 MAJOR_INCREMENTS_BREAKING_CHANGE_ALT_CC = [
     "feat(cli): added version",
     "docs(README): motivation",
-    "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files",  # noqa
+    "BREAKING-CHANGE: `extends` key in config file is now used for extending other config files",
     "fix(setup.py): future is now required for every python version",
 ]
 
diff --git a/tests/test_bump_normalize_tag.py b/tests/test_bump_normalize_tag.py
index c1eb696afd..895acbd71a 100644
--- a/tests/test_bump_normalize_tag.py
+++ b/tests/test_bump_normalize_tag.py
@@ -1,6 +1,6 @@
 import pytest
 
-from commitizen import bump
+from commitizen.tags import TagRules
 
 conversion = [
     (("1.2.3", "v$version"), "v1.2.3"),
@@ -18,5 +18,6 @@
 @pytest.mark.parametrize("test_input,expected", conversion)
 def test_create_tag(test_input, expected):
     version, format = test_input
-    new_tag = bump.normalize_tag(version, format)
+    rules = TagRules()
+    new_tag = rules.normalize_tag(version, format)
     assert new_tag == expected
diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py
index 9a80d4cfd9..c14e4ad1cb 100644
--- a/tests/test_bump_update_version_in_files.py
+++ b/tests/test_bump_update_version_in_files.py
@@ -197,7 +197,7 @@ def test_file_version_inconsistent_error(
     assert expected_msg in str(excinfo.value)
 
 
-def test_multiplt_versions_to_bump(
+def test_multiple_versions_to_bump(
     multiple_versions_to_update_poetry_lock, file_regression
 ):
     old_version = "1.2.9"
@@ -207,3 +207,21 @@ def test_multiplt_versions_to_bump(
     bump.update_version_in_files(old_version, new_version, [location], encoding="utf-8")
     with open(multiple_versions_to_update_poetry_lock, encoding="utf-8") as f:
         file_regression.check(f.read(), extension=".toml")
+
+
+def test_update_version_in_globbed_files(commitizen_config_file, file_regression):
+    old_version = "1.2.3"
+    new_version = "2.0.0"
+    other = commitizen_config_file.dirpath("other.toml")
+    print(commitizen_config_file, other)
+    copyfile(commitizen_config_file, other)
+
+    # Prepend full path as test assume absolute paths or cwd-relative
+    version_files = [commitizen_config_file.dirpath("*.toml")]
+
+    bump.update_version_in_files(
+        old_version, new_version, version_files, encoding="utf-8"
+    )
+
+    for file in commitizen_config_file, other:
+        file_regression.check(file.read_text("utf-8"), extension=".toml")
diff --git a/tests/test_bump_update_version_in_files/test_update_version_in_globbed_files.toml b/tests/test_bump_update_version_in_files/test_update_version_in_globbed_files.toml
new file mode 100644
index 0000000000..bf82cfe859
--- /dev/null
+++ b/tests/test_bump_update_version_in_files/test_update_version_in_globbed_files.toml
@@ -0,0 +1,3 @@
+[tool.poetry]
+name = "commitizen"
+version = "2.0.0"
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index 8aef10a31f..ed90ed08e4 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1,18 +1,29 @@
+from __future__ import annotations
+
+import os
+import re
+from dataclasses import dataclass
 from pathlib import Path
+from typing import Any
+from unittest.mock import Mock
 
 import pytest
 from jinja2 import FileSystemLoader
 
-from commitizen import changelog, defaults, git
+from commitizen import changelog, git
+from commitizen.changelog_formats import ChangelogFormat
+from commitizen.commands.changelog import Changelog
+from commitizen.config import BaseConfig
 from commitizen.cz.conventional_commits.conventional_commits import (
     ConventionalCommitsCz,
 )
 from commitizen.exceptions import InvalidConfigurationError
-from commitizen.changelog_formats import ChangelogFormat
+from commitizen.version_schemes import Pep440
 
-COMMITS_DATA = [
+COMMITS_DATA: list[dict[str, Any]] = [
     {
         "rev": "141ee441c9c9da0809c554103a558eb17c30ed17",
+        "parents": ["6c4948501031b7d6405b54b21d3d635827f9421b"],
         "title": "bump: version 1.1.1 → 1.2.0",
         "body": "",
         "author": "Commitizen",
@@ -20,6 +31,7 @@
     },
     {
         "rev": "6c4948501031b7d6405b54b21d3d635827f9421b",
+        "parents": ["ddd220ad515502200fe2dde443614c1075d26238"],
         "title": "docs: how to create custom bumps",
         "body": "",
         "author": "Commitizen",
@@ -27,6 +39,7 @@
     },
     {
         "rev": "ddd220ad515502200fe2dde443614c1075d26238",
+        "parents": ["ad17acff2e3a2e141cbc3c6efd7705e4e6de9bfc"],
         "title": "feat: custom cz plugins now support bumping version",
         "body": "",
         "author": "Commitizen",
@@ -34,6 +47,7 @@
     },
     {
         "rev": "ad17acff2e3a2e141cbc3c6efd7705e4e6de9bfc",
+        "parents": ["56c8a8da84e42b526bcbe130bd194306f7c7e813"],
         "title": "docs: added bump gif",
         "body": "",
         "author": "Commitizen",
@@ -41,6 +55,7 @@
     },
     {
         "rev": "56c8a8da84e42b526bcbe130bd194306f7c7e813",
+        "parents": ["74c6134b1b2e6bb8b07ed53410faabe99b204f36"],
         "title": "bump: version 1.1.0 → 1.1.1",
         "body": "",
         "author": "Commitizen",
@@ -48,6 +63,7 @@
     },
     {
         "rev": "74c6134b1b2e6bb8b07ed53410faabe99b204f36",
+        "parents": ["cbc7b5f22c4e74deff4bc92d14e19bd93524711e"],
         "title": "refactor: changed stdout statements",
         "body": "",
         "author": "Commitizen",
@@ -55,6 +71,7 @@
     },
     {
         "rev": "cbc7b5f22c4e74deff4bc92d14e19bd93524711e",
+        "parents": ["1ba46f2a63cb9d6e7472eaece21528c8cd28b118"],
         "title": "fix(bump): commit message now fits better with semver",
         "body": "",
         "author": "Commitizen",
@@ -62,6 +79,7 @@
     },
     {
         "rev": "1ba46f2a63cb9d6e7472eaece21528c8cd28b118",
+        "parents": ["c35dbffd1bb98bb0b3d1593797e79d1c3366af8f"],
         "title": "fix: conventional commit 'breaking change' in body instead of title",
         "body": "closes #16",
         "author": "Commitizen",
@@ -69,6 +87,7 @@
     },
     {
         "rev": "c35dbffd1bb98bb0b3d1593797e79d1c3366af8f",
+        "parents": ["25313397a4ac3dc5b5c986017bee2a614399509d"],
         "title": "refactor(schema): command logic removed from commitizen base",
         "body": "",
         "author": "Commitizen",
@@ -76,6 +95,7 @@
     },
     {
         "rev": "25313397a4ac3dc5b5c986017bee2a614399509d",
+        "parents": ["d2f13ac41b4e48995b3b619d931c82451886e6ff"],
         "title": "refactor(info): command logic removed from commitizen base",
         "body": "",
         "author": "Commitizen",
@@ -83,6 +103,7 @@
     },
     {
         "rev": "d2f13ac41b4e48995b3b619d931c82451886e6ff",
+        "parents": ["d839e317e5b26671b010584ad8cc6bf362400fa1"],
         "title": "refactor(example): command logic removed from commitizen base",
         "body": "",
         "author": "Commitizen",
@@ -90,6 +111,7 @@
     },
     {
         "rev": "d839e317e5b26671b010584ad8cc6bf362400fa1",
+        "parents": ["12d0e65beda969f7983c444ceedc2a01584f4e08"],
         "title": "refactor(commit): moved most of the commit logic to the commit command",
         "body": "",
         "author": "Commitizen",
@@ -97,6 +119,7 @@
     },
     {
         "rev": "12d0e65beda969f7983c444ceedc2a01584f4e08",
+        "parents": ["fb4c85abe51c228e50773e424cbd885a8b6c610d"],
         "title": "docs(README): updated documentation url)",
         "body": "",
         "author": "Commitizen",
@@ -104,6 +127,7 @@
     },
     {
         "rev": "fb4c85abe51c228e50773e424cbd885a8b6c610d",
+        "parents": ["17efb44d2cd16f6621413691a543e467c7d2dda6"],
         "title": "docs: mkdocs documentation",
         "body": "",
         "author": "Commitizen",
@@ -111,6 +135,7 @@
     },
     {
         "rev": "17efb44d2cd16f6621413691a543e467c7d2dda6",
+        "parents": ["6012d9eecfce8163d75c8fff179788e9ad5347da"],
         "title": "Bump version 1.0.0 → 1.1.0",
         "body": "",
         "author": "Commitizen",
@@ -118,6 +143,7 @@
     },
     {
         "rev": "6012d9eecfce8163d75c8fff179788e9ad5347da",
+        "parents": ["0c7fb0ca0168864dfc55d83c210da57771a18319"],
         "title": "test: fixed issues with conf",
         "body": "",
         "author": "Commitizen",
@@ -125,6 +151,7 @@
     },
     {
         "rev": "0c7fb0ca0168864dfc55d83c210da57771a18319",
+        "parents": ["cb1dd2019d522644da5bdc2594dd6dee17122d7f"],
         "title": "docs(README): some new information about bump",
         "body": "",
         "author": "Commitizen",
@@ -132,6 +159,7 @@
     },
     {
         "rev": "cb1dd2019d522644da5bdc2594dd6dee17122d7f",
+        "parents": ["9c7450f85df6bf6be508e79abf00855a30c3c73c"],
         "title": "feat: new working bump command",
         "body": "",
         "author": "Commitizen",
@@ -139,6 +167,7 @@
     },
     {
         "rev": "9c7450f85df6bf6be508e79abf00855a30c3c73c",
+        "parents": ["9f3af3772baab167e3fd8775d37f041440184251"],
         "title": "feat: create version tag",
         "body": "",
         "author": "Commitizen",
@@ -146,6 +175,7 @@
     },
     {
         "rev": "9f3af3772baab167e3fd8775d37f041440184251",
+        "parents": ["b0d6a3defbfde14e676e7eb34946409297d0221b"],
         "title": "docs: added new changelog",
         "body": "",
         "author": "Commitizen",
@@ -153,6 +183,7 @@
     },
     {
         "rev": "b0d6a3defbfde14e676e7eb34946409297d0221b",
+        "parents": ["d630d07d912e420f0880551f3ac94e933f9d3beb"],
         "title": "feat: update given files with new version",
         "body": "",
         "author": "Commitizen",
@@ -160,6 +191,7 @@
     },
     {
         "rev": "d630d07d912e420f0880551f3ac94e933f9d3beb",
+        "parents": ["1792b8980c58787906dbe6836f93f31971b1ec2d"],
         "title": "fix: removed all from commit",
         "body": "",
         "author": "Commitizen",
@@ -167,6 +199,7 @@
     },
     {
         "rev": "1792b8980c58787906dbe6836f93f31971b1ec2d",
+        "parents": ["52def1ea3555185ba4b936b463311949907e31ec"],
         "title": "feat(config): new set key, used to set version to cfg",
         "body": "",
         "author": "Commitizen",
@@ -174,6 +207,7 @@
     },
     {
         "rev": "52def1ea3555185ba4b936b463311949907e31ec",
+        "parents": ["3127e05077288a5e2b62893345590bf1096141b7"],
         "title": "feat: support for pyproject.toml",
         "body": "",
         "author": "Commitizen",
@@ -181,6 +215,7 @@
     },
     {
         "rev": "3127e05077288a5e2b62893345590bf1096141b7",
+        "parents": ["fd480ed90a80a6ffa540549408403d5b60d0e90c"],
         "title": "feat: first semantic version bump implementation",
         "body": "",
         "author": "Commitizen",
@@ -188,6 +223,7 @@
     },
     {
         "rev": "fd480ed90a80a6ffa540549408403d5b60d0e90c",
+        "parents": ["e4840a059731c0bf488381ffc77e989e85dd81ad"],
         "title": "fix: fix config file not working",
         "body": "",
         "author": "Commitizen",
@@ -195,6 +231,7 @@
     },
     {
         "rev": "e4840a059731c0bf488381ffc77e989e85dd81ad",
+        "parents": ["aa44a92d68014d0da98965c0c2cb8c07957d4362"],
         "title": "refactor: added commands folder, better integration with decli",
         "body": "",
         "author": "Commitizen",
@@ -202,6 +239,7 @@
     },
     {
         "rev": "aa44a92d68014d0da98965c0c2cb8c07957d4362",
+        "parents": ["58bb709765380dbd46b74ce6e8978515764eb955"],
         "title": "Bump version: 1.0.0b2 → 1.0.0",
         "body": "",
         "author": "Commitizen",
@@ -209,6 +247,7 @@
     },
     {
         "rev": "58bb709765380dbd46b74ce6e8978515764eb955",
+        "parents": ["97afb0bb48e72b6feca793091a8a23c706693257"],
         "title": "docs(README): new badges",
         "body": "",
         "author": "Commitizen",
@@ -216,6 +255,10 @@
     },
     {
         "rev": "97afb0bb48e72b6feca793091a8a23c706693257",
+        "parents": [
+            "9cecb9224aa7fa68d4afeac37eba2a25770ef251",
+            "e004a90b81ea5b374f118759bce5951202d03d69",
+        ],
         "title": "Merge pull request #10 from Woile/feat/decli",
         "body": "Feat/decli",
         "author": "Commitizen",
@@ -223,6 +266,7 @@
     },
     {
         "rev": "9cecb9224aa7fa68d4afeac37eba2a25770ef251",
+        "parents": ["f5781d1a2954d71c14ade2a6a1a95b91310b2577"],
         "title": "style: black to files",
         "body": "",
         "author": "Commitizen",
@@ -230,6 +274,7 @@
     },
     {
         "rev": "f5781d1a2954d71c14ade2a6a1a95b91310b2577",
+        "parents": ["80105fb3c6d45369bc0cbf787bd329fba603864c"],
         "title": "ci: added travis",
         "body": "",
         "author": "Commitizen",
@@ -237,6 +282,7 @@
     },
     {
         "rev": "80105fb3c6d45369bc0cbf787bd329fba603864c",
+        "parents": ["a96008496ffefb6b1dd9b251cb479eac6a0487f7"],
         "title": "refactor: removed delegator, added decli and many tests",
         "body": "BREAKING CHANGE: API is stable",
         "author": "Commitizen",
@@ -244,6 +290,7 @@
     },
     {
         "rev": "a96008496ffefb6b1dd9b251cb479eac6a0487f7",
+        "parents": ["aab33d13110f26604fb786878856ec0b9e5fc32b"],
         "title": "docs: updated test command",
         "body": "",
         "author": "Commitizen",
@@ -251,6 +298,7 @@
     },
     {
         "rev": "aab33d13110f26604fb786878856ec0b9e5fc32b",
+        "parents": ["b73791563d2f218806786090fb49ef70faa51a3a"],
         "title": "Bump version: 1.0.0b1 → 1.0.0b2",
         "body": "",
         "author": "Commitizen",
@@ -258,6 +306,7 @@
     },
     {
         "rev": "b73791563d2f218806786090fb49ef70faa51a3a",
+        "parents": ["7aa06a454fb717408b3657faa590731fb4ab3719"],
         "title": "docs(README): updated to reflect current state",
         "body": "",
         "author": "Commitizen",
@@ -265,6 +314,10 @@
     },
     {
         "rev": "7aa06a454fb717408b3657faa590731fb4ab3719",
+        "parents": [
+            "7c7e96b723c2aaa1aec3a52561f680adf0b60e97",
+            "9589a65880016996cff156b920472b9d28d771ca",
+        ],
         "title": "Merge pull request #9 from Woile/dev",
         "body": "feat: py3 only, tests and conventional commits 1.0",
         "author": "Commitizen",
@@ -272,6 +325,7 @@
     },
     {
         "rev": "7c7e96b723c2aaa1aec3a52561f680adf0b60e97",
+        "parents": ["ed830019581c83ba633bfd734720e6758eca6061"],
         "title": "Bump version: 0.9.11 → 1.0.0b1",
         "body": "",
         "author": "Commitizen",
@@ -279,6 +333,7 @@
     },
     {
         "rev": "ed830019581c83ba633bfd734720e6758eca6061",
+        "parents": ["c52eca6f74f844ab3ffbde61d98ef96071e132b7"],
         "title": "feat: py3 only, tests and conventional commits 1.0",
         "body": "more tests\npyproject instead of Pipfile\nquestionary instead of whaaaaat (promptkit 2.0.0 support)",
         "author": "Commitizen",
@@ -286,6 +341,7 @@
     },
     {
         "rev": "c52eca6f74f844ab3ffbde61d98ef96071e132b7",
+        "parents": ["0326652b2657083929507ee66d4d1a0899e861ba"],
         "title": "Bump version: 0.9.10 → 0.9.11",
         "body": "",
         "author": "Commitizen",
@@ -293,6 +349,7 @@
     },
     {
         "rev": "0326652b2657083929507ee66d4d1a0899e861ba",
+        "parents": ["b3f89892222340150e32631ae6b7aab65230036f"],
         "title": "fix(config): load config reads in order without failing if there is no commitizen section",
         "body": "Closes #8",
         "author": "Commitizen",
@@ -300,6 +357,7 @@
     },
     {
         "rev": "b3f89892222340150e32631ae6b7aab65230036f",
+        "parents": ["5e837bf8ef0735193597372cd2d85e31a8f715b9"],
         "title": "Bump version: 0.9.9 → 0.9.10",
         "body": "",
         "author": "Commitizen",
@@ -307,6 +365,7 @@
     },
     {
         "rev": "5e837bf8ef0735193597372cd2d85e31a8f715b9",
+        "parents": ["684e0259cc95c7c5e94854608cd3dcebbd53219e"],
         "title": "fix: parse scope (this is my punishment for not having tests)",
         "body": "",
         "author": "Commitizen",
@@ -314,6 +373,7 @@
     },
     {
         "rev": "684e0259cc95c7c5e94854608cd3dcebbd53219e",
+        "parents": ["ca38eac6ff09870851b5c76a6ff0a2a8e5ecda15"],
         "title": "Bump version: 0.9.8 → 0.9.9",
         "body": "",
         "author": "Commitizen",
@@ -321,6 +381,7 @@
     },
     {
         "rev": "ca38eac6ff09870851b5c76a6ff0a2a8e5ecda15",
+        "parents": ["64168f18d4628718c49689ee16430549e96c5d4b"],
         "title": "fix: parse scope empty",
         "body": "",
         "author": "Commitizen",
@@ -328,6 +389,7 @@
     },
     {
         "rev": "64168f18d4628718c49689ee16430549e96c5d4b",
+        "parents": ["9d4def716ef235a1fa5ae61614366423fbc8256f"],
         "title": "Bump version: 0.9.7 → 0.9.8",
         "body": "",
         "author": "Commitizen",
@@ -335,6 +397,7 @@
     },
     {
         "rev": "9d4def716ef235a1fa5ae61614366423fbc8256f",
+        "parents": ["33b0bf1a0a4dc60aac45ed47476d2e5473add09e"],
         "title": "fix(scope): parse correctly again",
         "body": "",
         "author": "Commitizen",
@@ -342,6 +405,7 @@
     },
     {
         "rev": "33b0bf1a0a4dc60aac45ed47476d2e5473add09e",
+        "parents": ["696885e891ec35775daeb5fec3ba2ab92c2629e1"],
         "title": "Bump version: 0.9.6 → 0.9.7",
         "body": "",
         "author": "Commitizen",
@@ -349,6 +413,7 @@
     },
     {
         "rev": "696885e891ec35775daeb5fec3ba2ab92c2629e1",
+        "parents": ["bef4a86761a3bda309c962bae5d22ce9b57119e4"],
         "title": "fix(scope): parse correctly",
         "body": "",
         "author": "Commitizen",
@@ -356,6 +421,7 @@
     },
     {
         "rev": "bef4a86761a3bda309c962bae5d22ce9b57119e4",
+        "parents": ["72472efb80f08ee3fd844660afa012c8cb256e4b"],
         "title": "Bump version: 0.9.5 → 0.9.6",
         "body": "",
         "author": "Commitizen",
@@ -363,6 +429,7 @@
     },
     {
         "rev": "72472efb80f08ee3fd844660afa012c8cb256e4b",
+        "parents": ["b5561ce0ab3b56bb87712c8f90bcf37cf2474f1b"],
         "title": "refactor(conventionalCommit): moved filters to questions instead of message",
         "body": "",
         "author": "Commitizen",
@@ -370,6 +437,7 @@
     },
     {
         "rev": "b5561ce0ab3b56bb87712c8f90bcf37cf2474f1b",
+        "parents": ["3e31714dc737029d96898f412e4ecd2be1bcd0ce"],
         "title": "fix(manifest): included missing files",
         "body": "",
         "author": "Commitizen",
@@ -377,6 +445,7 @@
     },
     {
         "rev": "3e31714dc737029d96898f412e4ecd2be1bcd0ce",
+        "parents": ["9df721e06595fdd216884c36a28770438b4f4a39"],
         "title": "Bump version: 0.9.4 → 0.9.5",
         "body": "",
         "author": "Commitizen",
@@ -384,6 +453,7 @@
     },
     {
         "rev": "9df721e06595fdd216884c36a28770438b4f4a39",
+        "parents": ["0cf6ada372470c8d09e6c9e68ebf94bbd5a1656f"],
         "title": "fix(config): home path for python versions between 3.0 and 3.5",
         "body": "",
         "author": "Commitizen",
@@ -391,6 +461,7 @@
     },
     {
         "rev": "0cf6ada372470c8d09e6c9e68ebf94bbd5a1656f",
+        "parents": ["973c6b3e100f6f69a3fe48bd8ee55c135b96c318"],
         "title": "Bump version: 0.9.3 → 0.9.4",
         "body": "",
         "author": "Commitizen",
@@ -398,6 +469,7 @@
     },
     {
         "rev": "973c6b3e100f6f69a3fe48bd8ee55c135b96c318",
+        "parents": ["dacc86159b260ee98eb5f57941c99ba731a01399"],
         "title": "feat(cli): added version",
         "body": "",
         "author": "Commitizen",
@@ -405,6 +477,7 @@
     },
     {
         "rev": "dacc86159b260ee98eb5f57941c99ba731a01399",
+        "parents": ["4368f3c3cbfd4a1ced339212230d854bc5bab496"],
         "title": "Bump version: 0.9.2 → 0.9.3",
         "body": "",
         "author": "Commitizen",
@@ -412,6 +485,7 @@
     },
     {
         "rev": "4368f3c3cbfd4a1ced339212230d854bc5bab496",
+        "parents": ["da94133288727d35dae9b91866a25045038f2d38"],
         "title": "feat(committer): conventional commit is a bit more intelligent now",
         "body": "",
         "author": "Commitizen",
@@ -419,6 +493,7 @@
     },
     {
         "rev": "da94133288727d35dae9b91866a25045038f2d38",
+        "parents": ["1541f54503d2e1cf39bd777c0ca5ab5eb78772ba"],
         "title": "docs(README): motivation",
         "body": "",
         "author": "Commitizen",
@@ -426,6 +501,7 @@
     },
     {
         "rev": "1541f54503d2e1cf39bd777c0ca5ab5eb78772ba",
+        "parents": ["ddc855a637b7879108308b8dbd85a0fd27c7e0e7"],
         "title": "Bump version: 0.9.1 → 0.9.2",
         "body": "",
         "author": "Commitizen",
@@ -433,6 +509,7 @@
     },
     {
         "rev": "ddc855a637b7879108308b8dbd85a0fd27c7e0e7",
+        "parents": ["46e9032e18a819e466618c7a014bcb0e9981af9e"],
         "title": "refactor: renamed conventional_changelog to conventional_commits, not backward compatible",
         "body": "",
         "author": "Commitizen",
@@ -440,6 +517,7 @@
     },
     {
         "rev": "46e9032e18a819e466618c7a014bcb0e9981af9e",
+        "parents": ["0fef73cd7dc77a25b82e197e7c1d3144a58c1350"],
         "title": "Bump version: 0.9.0 → 0.9.1",
         "body": "",
         "author": "Commitizen",
@@ -447,6 +525,7 @@
     },
     {
         "rev": "0fef73cd7dc77a25b82e197e7c1d3144a58c1350",
+        "parents": [],
         "title": "fix(setup.py): future is now required for every python version",
         "body": "",
         "author": "Commitizen",
@@ -477,24 +556,23 @@
 
 
 @pytest.fixture
-def gitcommits() -> list:
-    commits = [
+def gitcommits() -> list[git.GitCommit]:
+    return [
         git.GitCommit(
             commit["rev"],
             commit["title"],
             commit["body"],
             commit["author"],
             commit["author_email"],
+            commit["parents"],
         )
         for commit in COMMITS_DATA
     ]
-    return commits
 
 
 @pytest.fixture
-def tags() -> list:
-    tags = [git.GitTag(*tag) for tag in TAGS]
-    return tags
+def tags() -> list[git.GitTag]:
+    return [git.GitTag(*tag) for tag in TAGS]
 
 
 @pytest.fixture
@@ -520,12 +598,14 @@ def test_get_commit_tag_is_None(gitcommits, tags):
 @pytest.mark.parametrize("test_input", TAGS)
 def test_valid_tag_included_in_changelog(test_input):
     tag = git.GitTag(*test_input)
-    assert changelog.tag_included_in_changelog(tag, [], False)
+    rules = changelog.TagRules()
+    assert rules.include_in_changelog(tag)
 
 
 def test_invalid_tag_included_in_changelog():
     tag = git.GitTag("not_a_version", "rev", "date")
-    assert not changelog.tag_included_in_changelog(tag, [], False)
+    rules = changelog.TagRules()
+    assert not rules.include_in_changelog(tag)
 
 
 COMMITS_TREE = (
@@ -1076,21 +1156,39 @@ def test_invalid_tag_included_in_changelog():
 
 @pytest.mark.parametrize("merge_prereleases", (True, False))
 def test_generate_tree_from_commits(gitcommits, tags, merge_prereleases):
-    parser = defaults.commit_parser
-    changelog_pattern = defaults.bump_pattern
+    parser = ConventionalCommitsCz.commit_parser
+    changelog_pattern = ConventionalCommitsCz.bump_pattern
+    rules = changelog.TagRules(
+        merge_prereleases=merge_prereleases,
+    )
     tree = changelog.generate_tree_from_commits(
-        gitcommits, tags, parser, changelog_pattern, merge_prerelease=merge_prereleases
+        gitcommits, tags, parser, changelog_pattern, rules=rules
     )
-    if merge_prereleases:
-        assert tuple(tree) == COMMITS_TREE_AFTER_MERGED_PRERELEASES
-    else:
-        assert tuple(tree) == COMMITS_TREE
+    expected = (
+        COMMITS_TREE_AFTER_MERGED_PRERELEASES if merge_prereleases else COMMITS_TREE
+    )
+
+    for release, expected_release in zip(tree, expected):
+        assert release["version"] == expected_release["version"]
+        assert release["date"] == expected_release["date"]
+        assert release["changes"].keys() == expected_release["changes"].keys()
+        for change_type in release["changes"]:
+            changes = release["changes"][change_type]
+            expected_changes = expected_release["changes"][change_type]
+            for change, expected_change in zip(changes, expected_changes):
+                assert change["scope"] == expected_change["scope"]
+                assert change["breaking"] == expected_change["breaking"]
+                assert change["message"] == expected_change["message"]
+                assert change["author"] == "Commitizen"
+                assert change["author_email"] in "author@cz.dev"
+                assert "sha1" in change
+                assert "parents" in change
 
 
 def test_generate_tree_from_commits_with_no_commits(tags):
     gitcommits = []
-    parser = defaults.commit_parser
-    changelog_pattern = defaults.bump_pattern
+    parser = ConventionalCommitsCz.commit_parser
+    changelog_pattern = ConventionalCommitsCz.bump_pattern
     tree = changelog.generate_tree_from_commits(
         gitcommits, tags, parser, changelog_pattern
     )
@@ -1117,28 +1215,28 @@ def test_generate_tree_from_commits_with_no_commits(tags):
         ),
     ),
 )
-def test_order_changelog_tree(change_type_order, expected_reordering):
-    tree = changelog.order_changelog_tree(COMMITS_TREE, change_type_order)
+def test_generate_ordered_changelog_tree(change_type_order, expected_reordering):
+    tree = changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order)
 
     for index, entry in enumerate(tuple(tree)):
-        version = tree[index]["version"]
+        version = entry["version"]
         if version in expected_reordering:
             # Verify that all keys are present
-            assert [*tree[index].keys()] == [*COMMITS_TREE[index].keys()]
+            assert [*entry.keys()] == [*COMMITS_TREE[index].keys()]
             # Verify that the reorder only impacted the returned dict and not the original
             expected = expected_reordering[version]
-            assert [*tree[index]["changes"].keys()] == expected["sorted"]
+            assert [*entry["changes"].keys()] == expected["sorted"]
             assert [*COMMITS_TREE[index]["changes"].keys()] == expected["original"]
         else:
-            assert [*entry["changes"].keys()] == [*tree[index]["changes"].keys()]
+            assert [*entry["changes"].keys()] == [*entry["changes"].keys()]
 
 
-def test_order_changelog_tree_raises():
+def test_generate_ordered_changelog_tree_raises():
     change_type_order = ["BREAKING CHANGE", "feat", "refactor", "feat"]
     with pytest.raises(InvalidConfigurationError) as excinfo:
-        changelog.order_changelog_tree(COMMITS_TREE, change_type_order)
+        list(changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order))
 
-    assert "Change types contain duplicates types" in str(excinfo)
+    assert "Change types contain duplicated types" in str(excinfo)
 
 
 def test_render_changelog(
@@ -1298,9 +1396,9 @@ def test_render_changelog_with_changelog_message_builder_hook(
     gitcommits, tags, any_changelog_format: ChangelogFormat
 ):
     def changelog_message_builder_hook(message: dict, commit: git.GitCommit) -> dict:
-        message[
-            "message"
-        ] = f"{message['message']} [link](github.com/232323232) {commit.author} {commit.author_email}"
+        message["message"] = (
+            f"{message['message']} [link](github.com/232323232) {commit.author} {commit.author_email}"
+        )
         return message
 
     parser = ConventionalCommitsCz.commit_parser
@@ -1319,6 +1417,109 @@ def changelog_message_builder_hook(message: dict, commit: git.GitCommit) -> dict
     assert "[link](github.com/232323232) Commitizen author@cz.dev" in result
 
 
+def test_changelog_message_builder_hook_can_remove_commits(
+    gitcommits, tags, any_changelog_format: ChangelogFormat
+):
+    def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
+        return None
+
+    parser = ConventionalCommitsCz.commit_parser
+    changelog_pattern = ConventionalCommitsCz.changelog_pattern
+    loader = ConventionalCommitsCz.template_loader
+    template = any_changelog_format.template
+    tree = changelog.generate_tree_from_commits(
+        gitcommits,
+        tags,
+        parser,
+        changelog_pattern,
+        changelog_message_builder_hook=changelog_message_builder_hook,
+    )
+    result = changelog.render_changelog(tree, loader, template)
+
+    RE_HEADER = re.compile(r"^## v?\d+\.\d+\.\d+(\w)* \(\d{4}-\d{2}-\d{2}\)$")
+    # Rendered changelog should be empty, only containing version headers
+    for no, line in enumerate(result.splitlines()):
+        if line := line.strip():
+            assert RE_HEADER.match(line), f"Line {no} should not be there: {line}"
+
+
+def test_render_changelog_with_changelog_message_builder_hook_multiple_entries(
+    gitcommits, tags, any_changelog_format: ChangelogFormat
+):
+    def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
+        messages = [message.copy(), message.copy(), message.copy()]
+        for idx, msg in enumerate(messages):
+            msg["message"] = "Message #{idx}"
+        return messages
+
+    parser = ConventionalCommitsCz.commit_parser
+    changelog_pattern = ConventionalCommitsCz.changelog_pattern
+    loader = ConventionalCommitsCz.template_loader
+    template = any_changelog_format.template
+    tree = changelog.generate_tree_from_commits(
+        gitcommits,
+        tags,
+        parser,
+        changelog_pattern,
+        changelog_message_builder_hook=changelog_message_builder_hook,
+    )
+    result = changelog.render_changelog(tree, loader, template)
+
+    for idx in range(3):
+        assert "Message #{idx}" in result
+
+
+def test_changelog_message_builder_hook_can_access_and_modify_change_type(
+    gitcommits, tags, any_changelog_format: ChangelogFormat
+):
+    def changelog_message_builder_hook(message: dict, commit: git.GitCommit):
+        assert "change_type" in message
+        message["change_type"] = "overridden"
+        return message
+
+    parser = ConventionalCommitsCz.commit_parser
+    changelog_pattern = ConventionalCommitsCz.changelog_pattern
+    loader = ConventionalCommitsCz.template_loader
+    template = any_changelog_format.template
+    tree = changelog.generate_tree_from_commits(
+        gitcommits,
+        tags,
+        parser,
+        changelog_pattern,
+        changelog_message_builder_hook=changelog_message_builder_hook,
+    )
+    result = changelog.render_changelog(tree, loader, template)
+
+    RE_HEADER = re.compile(r"^### (?P<type>.+)$")
+    # There should be only "overridden" change type headers
+    for no, line in enumerate(result.splitlines()):
+        if (line := line.strip()) and (match := RE_HEADER.match(line)):
+            change_type = match.group("type")
+            assert change_type == "overridden", (
+                f"Line {no}: type {change_type} should have been overridden"
+            )
+
+
+def test_render_changelog_with_changelog_release_hook(
+    gitcommits, tags, any_changelog_format: ChangelogFormat
+):
+    def changelog_release_hook(release: dict, tag: git.GitTag | None) -> dict:
+        release["extra"] = "whatever"
+        return release
+
+    parser = ConventionalCommitsCz.commit_parser
+    changelog_pattern = ConventionalCommitsCz.changelog_pattern
+    tree = changelog.generate_tree_from_commits(
+        gitcommits,
+        tags,
+        parser,
+        changelog_pattern,
+        changelog_release_hook=changelog_release_hook,
+    )
+    for release in tree:
+        assert release["extra"] == "whatever"
+
+
 def test_get_smart_tag_range_returns_an_extra_for_a_range(tags):
     start, end = (
         tags[0],
@@ -1332,3 +1533,140 @@ def test_get_smart_tag_range_returns_an_extra_for_a_single_tag(tags):
     start = tags[0]  # len here is 1, but we expect one more tag as designed
     res = changelog.get_smart_tag_range(tags, start.name)
     assert 2 == len(res)
+
+
+@dataclass
+class TagDef:
+    name: str
+    is_version: bool
+    is_legacy: bool
+    is_ignored: bool
+
+
+TAGS_PARAMS = (
+    pytest.param(TagDef("1.2.3", True, False, False), id="version"),
+    # We test with `v-` prefix as `v` prefix is a special case kept for backward compatibility
+    pytest.param(TagDef("v-1.2.3", False, True, False), id="v-prefix"),
+    pytest.param(TagDef("project-1.2.3", False, True, False), id="project-prefix"),
+    pytest.param(TagDef("ignored", False, False, True), id="ignored"),
+    pytest.param(TagDef("unknown", False, False, False), id="unknown"),
+)
+
+
+@pytest.mark.parametrize("tag", TAGS_PARAMS)
+def test_tag_rules_tag_format_only(tag: TagDef):
+    rules = changelog.TagRules(Pep440, "$version")
+    assert rules.is_version_tag(tag.name) is tag.is_version
+
+
+@pytest.mark.parametrize("tag", TAGS_PARAMS)
+def test_tag_rules_with_legacy_tags(tag: TagDef):
+    rules = changelog.TagRules(
+        scheme=Pep440,
+        tag_format="$version",
+        legacy_tag_formats=["v-$version", "project-${version}"],
+    )
+    assert rules.is_version_tag(tag.name) is tag.is_version or tag.is_legacy
+
+
+@pytest.mark.parametrize("tag", TAGS_PARAMS)
+def test_tag_rules_with_ignored_tags(tag: TagDef):
+    rules = changelog.TagRules(
+        scheme=Pep440, tag_format="$version", ignored_tag_formats=["ignored"]
+    )
+    assert rules.is_ignored_tag(tag.name) is tag.is_ignored
+
+
+def test_tags_rules_get_version_tags(capsys: pytest.CaptureFixture):
+    tags = [
+        git.GitTag("v1.1.0", "17efb44d2cd16f6621413691a543e467c7d2dda6", "2019-04-14"),
+        git.GitTag("v1.0.0", "aa44a92d68014d0da98965c0c2cb8c07957d4362", "2019-03-01"),
+        git.GitTag("1.0.0b2", "aab33d13110f26604fb786878856ec0b9e5fc32b", "2019-01-18"),
+        git.GitTag(
+            "project-not-a-version",
+            "7c7e96b723c2aaa1aec3a52561f680adf0b60e97",
+            "2019-01-17",
+        ),
+        git.GitTag(
+            "not-a-version", "c52eca6f74f844ab3ffbde61d98ef96071e132b7", "2018-12-17"
+        ),
+        git.GitTag(
+            "star-something", "c52eca6f74f844ab3ffbde61d98fe96071e132b2", "2018-11-12"
+        ),
+        git.GitTag("known", "b3f89892222340150e32631ae6b7aab65230036f", "2018-09-22"),
+        git.GitTag(
+            "ignored-0.9.3", "684e0259cc95c7c5e94854608cd3dcebbd53219e", "2018-09-22"
+        ),
+        git.GitTag(
+            "project-0.9.3", "dacc86159b260ee98eb5f57941c99ba731a01399", "2018-07-28"
+        ),
+        git.GitTag(
+            "anything-0.9", "5141f54503d2e1cf39bd666c0ca5ab5eb78772ab", "2018-01-10"
+        ),
+        git.GitTag(
+            "project-0.9.2", "1541f54503d2e1cf39bd777c0ca5ab5eb78772ba", "2017-11-11"
+        ),
+        git.GitTag(
+            "ignored-0.9.1", "46e9032e18a819e466618c7a014bcb0e9981af9e", "2017-11-11"
+        ),
+    ]
+
+    rules = changelog.TagRules(
+        scheme=Pep440,
+        tag_format="v$version",
+        legacy_tag_formats=["$version", "project-${version}"],
+        ignored_tag_formats=[
+            "known",
+            "ignored-${version}",
+            "star-*",
+            "*-${major}.${minor}",
+        ],
+    )
+
+    version_tags = rules.get_version_tags(tags, warn=True)
+    assert {t.name for t in version_tags} == {
+        "v1.1.0",
+        "v1.0.0",
+        "1.0.0b2",
+        "project-0.9.3",
+        "project-0.9.2",
+    }
+
+    captured = capsys.readouterr()
+    assert captured.err.count("Invalid version tag:") == 2
+    assert captured.err.count("not-a-version") == 2
+
+
+def test_changelog_file_name_from_args_and_config():
+    mock_config = Mock(spec=BaseConfig)
+    mock_path = Mock(spec=Path)
+    mock_path.parent = Path("/my/project")
+    mock_config.path = mock_path
+    mock_config.settings = {
+        "name": "cz_conventional_commits",
+        "changelog_file": "CHANGELOG.md",
+        "encoding": "utf-8",
+        "changelog_start_rev": "v1.0.0",
+        "tag_format": "$version",
+        "legacy_tag_formats": [],
+        "ignored_tag_formats": [],
+        "incremental": True,
+        "changelog_merge_prerelease": True,
+    }
+
+    args = {
+        "file_name": "CUSTOM.md",
+        "incremental": None,
+        "dry_run": False,
+        "unreleased_version": "1.0.1",
+    }
+    changelog = Changelog(mock_config, args)
+    assert os.path.normpath(changelog.file_name) == os.path.normpath(
+        os.path.join("/my/project", "CUSTOM.md")
+    )
+
+    args = {"incremental": None, "dry_run": False, "unreleased_version": "1.0.1"}
+    changelog = Changelog(mock_config, args)
+    assert os.path.normpath(changelog.file_name) == os.path.normpath(
+        os.path.join("/my/project", "CHANGELOG.md")
+    )
diff --git a/tests/test_changelog_format_asciidoc.py b/tests/test_changelog_format_asciidoc.py
index df9c28f9d7..cc81a24f2a 100644
--- a/tests/test_changelog_format_asciidoc.py
+++ b/tests/test_changelog_format_asciidoc.py
@@ -5,9 +5,8 @@
 import pytest
 
 from commitizen.changelog import Metadata
-from commitizen.config.base_config import BaseConfig
 from commitizen.changelog_formats.asciidoc import AsciiDoc
-
+from commitizen.config.base_config import BaseConfig
 
 CHANGELOG_A = """
 = Changelog
@@ -56,6 +55,7 @@
 """
 EXPECTED_C = Metadata(
     latest_version="1.0.0",
+    latest_version_tag="v1.0.0",
     latest_version_position=3,
     unreleased_end=3,
     unreleased_start=1,
@@ -73,23 +73,54 @@
     unreleased_start=1,
 )
 
+CHANGELOG_E = """
+= Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on https://keepachangelog.com/en/1.0.0/[Keep a Changelog],
+and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Versioning].
+
+== [Unreleased]
+* Start using "changelog" over "change log" since it's the common usage.
+
+== [{tag_formatted_version}] - 2017-06-20
+=== Added
+* New visual identity by https://github.com/tylerfortune8[@tylerfortune8].
+* Version navigation.
+""".strip()
+
+EXPECTED_E = Metadata(
+    latest_version="1.0.0",
+    latest_version_position=10,
+    unreleased_end=10,
+    unreleased_start=7,
+)
+
 
 @pytest.fixture
 def format(config: BaseConfig) -> AsciiDoc:
     return AsciiDoc(config)
 
 
+@pytest.fixture
+def format_with_tags(config: BaseConfig, request) -> AsciiDoc:
+    config.settings["tag_format"] = request.param
+    config.settings["legacy_tag_formats"] = ["legacy-${version}"]
+    return AsciiDoc(config)
+
+
 VERSIONS_EXAMPLES = [
-    ("== [1.0.0] - 2017-06-20", "1.0.0"),
+    ("== [1.0.0] - 2017-06-20", ("1.0.0", "1.0.0")),
     (
         "= https://github.com/angular/angular/compare/10.0.0-next.2...10.0.0-next.3[10.0.0-next.3] (2020-04-22)",
-        "10.0.0-next.3",
+        ("10.0.0-next.3", "10.0.0-next.3"),
     ),
-    ("=== 0.19.1 (Jan 7, 2020)", "0.19.1"),
-    ("== 1.0.0", "1.0.0"),
-    ("== v1.0.0", "1.0.0"),
-    ("== v1.0.0 - (2012-24-32)", "1.0.0"),
-    ("= version 2020.03.24", "2020.03.24"),
+    ("=== 0.19.1 (Jan 7, 2020)", ("0.19.1", "0.19.1")),
+    ("== 1.0.0", ("1.0.0", "1.0.0")),
+    ("== v1.0.0", ("1.0.0", "v1.0.0")),
+    ("== v1.0.0 - (2012-24-32)", ("1.0.0", "v1.0.0")),
+    ("= version 2020.03.24", ("2020.03.24", "2020.03.24")),
     ("== [Unreleased]", None),
     ("All notable changes to this project will be documented in this file.", None),
     ("= Changelog", None),
@@ -99,7 +130,7 @@ def format(config: BaseConfig) -> AsciiDoc:
 
 @pytest.mark.parametrize("line_from_changelog,output_version", VERSIONS_EXAMPLES)
 def test_changelog_detect_version(
-    line_from_changelog: str, output_version: str, format: AsciiDoc
+    line_from_changelog: str, output_version: tuple[str, str] | None, format: AsciiDoc
 ):
     version = format.parse_version_from_title(line_from_changelog)
     assert version == output_version
@@ -129,10 +160,42 @@ def test_parse_title_type_of_line(
         pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
     ),
 )
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: AsciiDoc, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
     changelog.write_text(content)
 
     assert format.get_metadata(str(changelog)) == expected
+
+
+@pytest.mark.parametrize(
+    "format_with_tags, tag_string, expected, ",
+    (
+        pytest.param("${version}-example", "1.0.0-example", "1.0.0"),
+        pytest.param("${version}example", "1.0.0example", "1.0.0"),
+        pytest.param("example${version}", "example1.0.0", "1.0.0"),
+        pytest.param("example-${version}", "example-1.0.0", "1.0.0"),
+        pytest.param("example-${major}-${minor}-${patch}", "example-1-0-0", "1.0.0"),
+        pytest.param("example-${major}-${minor}", "example-1-0-0", None),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}-example",
+            "1-0-0-rc1-example",
+            "1.0.0-rc1",
+        ),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}${devrelease}-example",
+            "1-0-0-a1.dev1-example",
+            "1.0.0-a1.dev1",
+        ),
+        pytest.param("new-${version}", "legacy-1.0.0", "1.0.0"),
+    ),
+    indirect=["format_with_tags"],
+)
+def test_get_metadata_custom_tag_format(
+    tmp_path: Path, format_with_tags: AsciiDoc, tag_string: str, expected: Metadata
+):
+    content = CHANGELOG_E.format(tag_formatted_version=tag_string)
+    changelog = tmp_path / format_with_tags.default_changelog_file
+    changelog.write_text(content)
+    assert format_with_tags.get_metadata(str(changelog)).latest_version == expected
diff --git a/tests/test_changelog_format_markdown.py b/tests/test_changelog_format_markdown.py
index 2e1ee69977..1abc63f29f 100644
--- a/tests/test_changelog_format_markdown.py
+++ b/tests/test_changelog_format_markdown.py
@@ -5,9 +5,8 @@
 import pytest
 
 from commitizen.changelog import Metadata
-from commitizen.config.base_config import BaseConfig
 from commitizen.changelog_formats.markdown import Markdown
-
+from commitizen.config.base_config import BaseConfig
 
 CHANGELOG_A = """
 # Changelog
@@ -56,6 +55,7 @@
 """
 EXPECTED_C = Metadata(
     latest_version="1.0.0",
+    latest_version_tag="v1.0.0",
     latest_version_position=3,
     unreleased_end=3,
     unreleased_start=1,
@@ -73,23 +73,54 @@
     unreleased_start=1,
 )
 
+CHANGELOG_E = """
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+- Start using "changelog" over "change log" since it's the common usage.
+
+## {tag_formatted_version} - 2017-06-20
+### Added
+- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
+- Version navigation.
+""".strip()
+
+EXPECTED_E = Metadata(
+    latest_version="1.0.0",
+    latest_version_position=10,
+    unreleased_end=10,
+    unreleased_start=7,
+)
+
 
 @pytest.fixture
 def format(config: BaseConfig) -> Markdown:
     return Markdown(config)
 
 
+@pytest.fixture
+def format_with_tags(config: BaseConfig, request) -> Markdown:
+    config.settings["tag_format"] = request.param
+    config.settings["legacy_tag_formats"] = ["legacy-${version}"]
+    return Markdown(config)
+
+
 VERSIONS_EXAMPLES = [
-    ("## [1.0.0] - 2017-06-20", "1.0.0"),
+    ("## [1.0.0] - 2017-06-20", ("1.0.0", "1.0.0")),
     (
         "# [10.0.0-next.3](https://github.com/angular/angular/compare/10.0.0-next.2...10.0.0-next.3) (2020-04-22)",
-        "10.0.0-next.3",
+        ("10.0.0-next.3", "10.0.0-next.3"),
     ),
-    ("### 0.19.1 (Jan 7, 2020)", "0.19.1"),
-    ("## 1.0.0", "1.0.0"),
-    ("## v1.0.0", "1.0.0"),
-    ("## v1.0.0 - (2012-24-32)", "1.0.0"),
-    ("# version 2020.03.24", "2020.03.24"),
+    ("### 0.19.1 (Jan 7, 2020)", ("0.19.1", "0.19.1")),
+    ("## 1.0.0", ("1.0.0", "1.0.0")),
+    ("## v1.0.0", ("1.0.0", "v1.0.0")),
+    ("## v1.0.0 - (2012-24-32)", ("1.0.0", "v1.0.0")),
+    ("# version 2020.03.24", ("2020.03.24", "2020.03.24")),
     ("## [Unreleased]", None),
     ("All notable changes to this project will be documented in this file.", None),
     ("# Changelog", None),
@@ -99,7 +130,7 @@ def format(config: BaseConfig) -> Markdown:
 
 @pytest.mark.parametrize("line_from_changelog,output_version", VERSIONS_EXAMPLES)
 def test_changelog_detect_version(
-    line_from_changelog: str, output_version: str, format: Markdown
+    line_from_changelog: str, output_version: tuple[str, str] | None, format: Markdown
 ):
     version = format.parse_version_from_title(line_from_changelog)
     assert version == output_version
@@ -129,10 +160,48 @@ def test_parse_title_type_of_line(
         pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
     ),
 )
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: Markdown, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
     changelog.write_text(content)
 
     assert format.get_metadata(str(changelog)) == expected
+
+
+@pytest.mark.parametrize(
+    "format_with_tags, tag_string, expected, ",
+    (
+        pytest.param("${version}-example", "1.0.0-example", "1.0.0"),
+        pytest.param("${version}example", "1.0.0example", "1.0.0"),
+        pytest.param("example${version}", "example1.0.0", "1.0.0"),
+        pytest.param("example-${version}", "example-1.0.0", "1.0.0"),
+        pytest.param("example-${major}-${minor}-${patch}", "example-1-0-0", "1.0.0"),
+        pytest.param("example-${major}-${minor}", "example-1-0-0", None),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}-example",
+            "1-0-0-rc1-example",
+            "1.0.0-rc1",
+        ),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}-example",
+            "1-0-0-a1-example",
+            "1.0.0-a1",
+        ),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}${devrelease}-example",
+            "1-0-0-a1.dev1-example",
+            "1.0.0-a1.dev1",
+        ),
+        pytest.param("new-${version}", "legacy-1.0.0", "1.0.0"),
+    ),
+    indirect=["format_with_tags"],
+)
+def test_get_metadata_custom_tag_format(
+    tmp_path: Path, format_with_tags: Markdown, tag_string: str, expected: Metadata
+):
+    content = CHANGELOG_E.format(tag_formatted_version=tag_string)
+    changelog = tmp_path / format_with_tags.default_changelog_file
+    changelog.write_text(content)
+
+    assert format_with_tags.get_metadata(str(changelog)).latest_version == expected
diff --git a/tests/test_changelog_format_restructuredtext.py b/tests/test_changelog_format_restructuredtext.py
index 7c5969f51d..14bc15ec09 100644
--- a/tests/test_changelog_format_restructuredtext.py
+++ b/tests/test_changelog_format_restructuredtext.py
@@ -7,8 +7,8 @@
 import pytest
 
 from commitizen.changelog import Metadata
-from commitizen.config.base_config import BaseConfig
 from commitizen.changelog_formats.restructuredtext import RestructuredText
+from commitizen.config.base_config import BaseConfig
 
 if TYPE_CHECKING:
     from _pytest.mark.structures import ParameterSet
@@ -22,6 +22,7 @@ def case(
     content: str,
     latest_version: str | None = None,
     latest_version_position: int | None = None,
+    latest_version_tag: str | None = None,
     unreleased_start: int | None = None,
     unreleased_end: int | None = None,
 ):
@@ -30,6 +31,7 @@ def case(
             dedent(content).strip(),
             Metadata(
                 latest_version=latest_version,
+                latest_version_tag=latest_version_tag,
                 latest_version_position=latest_version_position,
                 unreleased_start=unreleased_start,
                 unreleased_end=unreleased_end,
@@ -93,6 +95,7 @@ def case(
     ======
     """,
     latest_version="1.0.0",
+    latest_version_tag="v1.0.0",
     latest_version_position=3,
     unreleased_start=0,
     unreleased_end=3,
@@ -273,14 +276,42 @@ def case(
     """,
 )
 
+CHANGELOG = """
+Changelog
+    #########
+
+    All notable changes to this project will be documented in this file.
+
+    The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`,
+    and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`.
+
+    Unreleased
+    ==========
+    * Start using "changelog" over "change log" since it's the common usage.
+
+    {tag_formatted_version} - 2017-06-20
+    {underline}
+    Added
+    -----
+    * New visual identity by `@tylerfortune8 <https://github.com/tylerfortune8>`.
+    * Version navigation.
+""".strip()
+
 
 @pytest.fixture
 def format(config: BaseConfig) -> RestructuredText:
     return RestructuredText(config)
 
 
+@pytest.fixture
+def format_with_tags(config: BaseConfig, request) -> RestructuredText:
+    config.settings["tag_format"] = request.param
+    config.settings["legacy_tag_formats"] = ["legacy-${version}"]
+    return RestructuredText(config)
+
+
 @pytest.mark.parametrize("content, expected", CASES)
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: RestructuredText, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
@@ -308,3 +339,43 @@ def test_is_overlined_title(format: RestructuredText, text: str, expected: bool)
     _, first, second, third = dedent(text).splitlines()
 
     assert format.is_overlined_title(first, second, third) is expected
+
+
+@pytest.mark.parametrize(
+    "format_with_tags, tag_string, expected, ",
+    (
+        pytest.param("${version}-example", "1.0.0-example", "1.0.0"),
+        pytest.param("${version}", "1.0.0", "1.0.0"),
+        pytest.param("${version}example", "1.0.0example", "1.0.0"),
+        pytest.param("example${version}", "example1.0.0", "1.0.0"),
+        pytest.param("example-${version}", "example-1.0.0", "1.0.0"),
+        pytest.param("example-${major}-${minor}-${patch}", "example-1-0-0", "1.0.0"),
+        pytest.param("example-${major}-${minor}", "example-1-0-0", None),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}-example",
+            "1-0-0-rc1-example",
+            "1.0.0-rc1",
+        ),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}${devrelease}-example",
+            "1-0-0-a1.dev1-example",
+            "1.0.0-a1.dev1",
+        ),
+        pytest.param("new-${version}", "legacy-1.0.0", "1.0.0"),
+    ),
+    indirect=["format_with_tags"],
+)
+def test_get_metadata_custom_tag_format(
+    tmp_path: Path,
+    format_with_tags: RestructuredText,
+    tag_string: str,
+    expected: Metadata,
+):
+    content = CHANGELOG.format(
+        tag_formatted_version=tag_string,
+        underline="=" * len(tag_string) + "=============",
+    )
+    changelog = tmp_path / format_with_tags.default_changelog_file
+    changelog.write_text(content)
+
+    assert format_with_tags.get_metadata(str(changelog)).latest_version == expected
diff --git a/tests/test_changelog_format_textile.py b/tests/test_changelog_format_textile.py
index 5176243ba0..812fa6bf60 100644
--- a/tests/test_changelog_format_textile.py
+++ b/tests/test_changelog_format_textile.py
@@ -5,9 +5,8 @@
 import pytest
 
 from commitizen.changelog import Metadata
-from commitizen.config.base_config import BaseConfig
 from commitizen.changelog_formats.textile import Textile
-
+from commitizen.config.base_config import BaseConfig
 
 CHANGELOG_A = """
 h1. Changelog
@@ -56,6 +55,7 @@
 """
 EXPECTED_C = Metadata(
     latest_version="1.0.0",
+    latest_version_tag="v1.0.0",
     latest_version_position=3,
     unreleased_end=3,
     unreleased_start=1,
@@ -73,23 +73,47 @@
     unreleased_start=1,
 )
 
+CHANGELOG_E = """
+h1. Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on "Keep a Changelog":https://keepachangelog.com/en/1.0.0/,
+and this project adheres to "Semantic Versioning":https://semver.org/spec/v2.0.0.html.
+
+h2. [Unreleased]
+- Start using "changelog" over "change log" since it's the common usage.
+
+h2. [{tag_formatted_version}] - 2017-06-20
+h3. Added
+* New visual identity by [@tylerfortune8](https://github.com/tylerfortune8).
+* Version navigation.
+""".strip()
+
 
 @pytest.fixture
 def format(config: BaseConfig) -> Textile:
     return Textile(config)
 
 
+@pytest.fixture
+def format_with_tags(config: BaseConfig, request) -> Textile:
+    config.settings["tag_format"] = request.param
+    config.settings["legacy_tag_formats"] = ["legacy-${version}"]
+    return Textile(config)
+
+
 VERSIONS_EXAMPLES = [
-    ("h2. [1.0.0] - 2017-06-20", "1.0.0"),
+    ("h2. [1.0.0] - 2017-06-20", ("1.0.0", "1.0.0")),
     (
         'h1. "10.0.0-next.3":https://github.com/angular/angular/compare/10.0.0-next.2...10.0.0-next.3 (2020-04-22)',
-        "10.0.0-next.3",
+        ("10.0.0-next.3", "10.0.0-next.3"),
     ),
-    ("h3. 0.19.1 (Jan 7, 2020)", "0.19.1"),
-    ("h2. 1.0.0", "1.0.0"),
-    ("h2. v1.0.0", "1.0.0"),
-    ("h2. v1.0.0 - (2012-24-32)", "1.0.0"),
-    ("h1. version 2020.03.24", "2020.03.24"),
+    ("h3. 0.19.1 (Jan 7, 2020)", ("0.19.1", "0.19.1")),
+    ("h2. 1.0.0", ("1.0.0", "1.0.0")),
+    ("h2. v1.0.0", ("1.0.0", "v1.0.0")),
+    ("h2. v1.0.0 - (2012-24-32)", ("1.0.0", "v1.0.0")),
+    ("h1. version 2020.03.24", ("2020.03.24", "2020.03.24")),
     ("h2. [Unreleased]", None),
     ("All notable changes to this project will be documented in this file.", None),
     ("h1. Changelog", None),
@@ -99,7 +123,7 @@ def format(config: BaseConfig) -> Textile:
 
 @pytest.mark.parametrize("line_from_changelog,output_version", VERSIONS_EXAMPLES)
 def test_changelog_detect_version(
-    line_from_changelog: str, output_version: str, format: Textile
+    line_from_changelog: str, output_version: tuple[str, str] | None, format: Textile
 ):
     version = format.parse_version_from_title(line_from_changelog)
     assert version == output_version
@@ -129,10 +153,43 @@ def test_parse_title_type_of_line(
         pytest.param(CHANGELOG_D, EXPECTED_D, id="D"),
     ),
 )
-def test_get_matadata(
+def test_get_metadata(
     tmp_path: Path, format: Textile, content: str, expected: Metadata
 ):
     changelog = tmp_path / format.default_changelog_file
     changelog.write_text(content)
 
     assert format.get_metadata(str(changelog)) == expected
+
+
+@pytest.mark.parametrize(
+    "format_with_tags, tag_string, expected, ",
+    (
+        pytest.param("${version}-example", "1.0.0-example", "1.0.0"),
+        pytest.param("${version}example", "1.0.0example", "1.0.0"),
+        pytest.param("example${version}", "example1.0.0", "1.0.0"),
+        pytest.param("example-${version}", "example-1.0.0", "1.0.0"),
+        pytest.param("example-${major}-${minor}-${patch}", "example-1-0-0", "1.0.0"),
+        pytest.param("example-${major}-${minor}", "example-1-0-0", None),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}-example",
+            "1-0-0-rc1-example",
+            "1.0.0-rc1",
+        ),
+        pytest.param(
+            "${major}-${minor}-${patch}-${prerelease}${devrelease}-example",
+            "1-0-0-a1.dev1-example",
+            "1.0.0-a1.dev1",
+        ),
+        pytest.param("new-${version}", "legacy-1.0.0", "1.0.0"),
+    ),
+    indirect=["format_with_tags"],
+)
+def test_get_metadata_custom_tag_format(
+    tmp_path: Path, format_with_tags: Textile, tag_string: str, expected: Metadata
+):
+    content = CHANGELOG_E.format(tag_formatted_version=tag_string)
+    changelog = tmp_path / format_with_tags.default_changelog_file
+    changelog.write_text(content)
+
+    assert format_with_tags.get_metadata(str(changelog)).latest_version == expected
diff --git a/tests/test_changelog_formats.py b/tests/test_changelog_formats.py
index 7da87f16ca..e0d99e0325 100644
--- a/tests/test_changelog_formats.py
+++ b/tests/test_changelog_formats.py
@@ -1,27 +1,28 @@
 from __future__ import annotations
 
 import pytest
+
 from commitizen import defaults
-from commitizen.config.base_config import BaseConfig
 from commitizen.changelog_formats import (
     KNOWN_CHANGELOG_FORMATS,
     ChangelogFormat,
+    _guess_changelog_format,
     get_changelog_format,
-    guess_changelog_format,
 )
+from commitizen.config.base_config import BaseConfig
 from commitizen.exceptions import ChangelogFormatUnknown
 
 
 @pytest.mark.parametrize("format", KNOWN_CHANGELOG_FORMATS.values())
 def test_guess_format(format: type[ChangelogFormat]):
-    assert guess_changelog_format(f"CHANGELOG.{format.extension}") is format
+    assert _guess_changelog_format(f"CHANGELOG.{format.extension}") is format
     for ext in format.alternative_extensions:
-        assert guess_changelog_format(f"CHANGELOG.{ext}") is format
+        assert _guess_changelog_format(f"CHANGELOG.{ext}") is format
 
 
 @pytest.mark.parametrize("filename", ("CHANGELOG", "NEWS", "file.unknown", None))
 def test_guess_format_unknown(filename: str):
-    assert guess_changelog_format(filename) is None
+    assert _guess_changelog_format(filename) is None
 
 
 @pytest.mark.parametrize(
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 93f6c16ddd..31371caea4 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,6 +1,7 @@
 import os
 import subprocess
 import sys
+import types
 from functools import partial
 
 import pytest
@@ -8,10 +9,11 @@
 
 from commitizen import cli
 from commitizen.exceptions import (
+    ConfigFileNotFound,
     ExpectedExit,
+    InvalidCommandArgumentError,
     NoCommandFoundError,
     NotAGitProjectError,
-    InvalidCommandArgumentError,
 )
 
 
@@ -25,6 +27,15 @@ def test_sysexit_no_argv(mocker: MockFixture, capsys):
         assert out.startswith("usage")
 
 
+def test_cz_config_file_without_correct_file_path(mocker: MockFixture, capsys):
+    testargs = ["cz", "--config", "./config/pyproject.toml", "example"]
+    mocker.patch.object(sys, "argv", testargs)
+
+    with pytest.raises(ConfigFileNotFound) as excinfo:
+        cli.main()
+    assert "Cannot found the config file" in str(excinfo.value)
+
+
 def test_cz_with_arg_but_without_command(mocker: MockFixture):
     testargs = ["cz", "--name", "cz_jira"]
     mocker.patch.object(sys, "argv", testargs)
@@ -78,7 +89,7 @@ def test_commitizen_excepthook(capsys):
     with pytest.raises(SystemExit) as excinfo:
         cli.commitizen_excepthook(NotAGitProjectError, NotAGitProjectError(), "")
 
-    assert excinfo.type == SystemExit
+    assert excinfo.type is SystemExit
     assert excinfo.value.code == NotAGitProjectError.exit_code
 
 
@@ -91,7 +102,7 @@ def test_commitizen_debug_excepthook(capsys):
             debug=True,
         )
 
-    assert excinfo.type == SystemExit
+    assert excinfo.type is SystemExit
     assert excinfo.value.code == NotAGitProjectError.exit_code
     assert "NotAGitProjectError" in str(excinfo.traceback[0])
 
@@ -122,7 +133,7 @@ def test_commitizen_excepthook_no_raises(capsys):
             no_raise=[NotAGitProjectError.exit_code],
         )
 
-    assert excinfo.type == SystemExit
+    assert excinfo.type is SystemExit
     assert excinfo.value.code == 0
 
 
@@ -172,3 +183,59 @@ def test_unknown_args_before_double_dash_raises(mocker: MockFixture):
     assert "Invalid commitizen arguments were found before -- separator" in str(
         excinfo.value
     )
+
+
+def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture):
+    """Test that commitizen_excepthook delegates to original_excepthook for non-CommitizenException."""
+    # Mock the original excepthook
+    mock_original_excepthook = mocker.Mock()
+    mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook)
+
+    # Create a regular exception
+    test_exception = ValueError("test error")
+
+    # Call commitizen_excepthook with the regular exception
+    cli.commitizen_excepthook(ValueError, test_exception, None)
+
+    # Verify original_excepthook was called with correct arguments
+    mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None)
+
+
+def test_commitizen_excepthook_non_commitizen_exception_with_traceback(
+    mocker: MockFixture,
+):
+    """Test that commitizen_excepthook handles traceback correctly for non-CommitizenException."""
+    # Mock the original excepthook
+    mock_original_excepthook = mocker.Mock()
+    mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook)
+
+    # Create a regular exception with a traceback
+    test_exception = ValueError("test error")
+    test_traceback = mocker.Mock(spec=types.TracebackType)
+
+    # Call commitizen_excepthook with the regular exception and traceback
+    cli.commitizen_excepthook(ValueError, test_exception, test_traceback)
+
+    # Verify original_excepthook was called with correct arguments including traceback
+    mock_original_excepthook.assert_called_once_with(
+        ValueError, test_exception, test_traceback
+    )
+
+
+def test_commitizen_excepthook_non_commitizen_exception_with_invalid_traceback(
+    mocker: MockFixture,
+):
+    """Test that commitizen_excepthook handles invalid traceback correctly for non-CommitizenException."""
+    # Mock the original excepthook
+    mock_original_excepthook = mocker.Mock()
+    mocker.patch("commitizen.cli.original_excepthook", mock_original_excepthook)
+
+    # Create a regular exception with an invalid traceback
+    test_exception = ValueError("test error")
+    test_traceback = mocker.Mock()  # Not a TracebackType
+
+    # Call commitizen_excepthook with the regular exception and invalid traceback
+    cli.commitizen_excepthook(ValueError, test_exception, test_traceback)
+
+    # Verify original_excepthook was called with None as traceback
+    mock_original_excepthook.assert_called_once_with(ValueError, test_exception, None)
diff --git a/tests/test_conf.py b/tests/test_conf.py
index dcac8e015c..f89a0049f4 100644
--- a/tests/test_conf.py
+++ b/tests/test_conf.py
@@ -9,7 +9,7 @@
 import yaml
 
 from commitizen import config, defaults, git
-from commitizen.exceptions import InvalidConfigurationError
+from commitizen.exceptions import ConfigFileIsEmpty, InvalidConfigurationError
 
 PYPROJECT = """
 [tool.commitizen]
@@ -44,6 +44,27 @@
     }
 }
 
+JSON_STR = r"""
+    {
+        "commitizen": {
+            "name": "cz_jira",
+            "version": "1.0.0",
+            "version_files": [
+                "commitizen/__version__.py",
+                "pyproject.toml"
+            ]
+        }
+    }
+"""
+
+YAML_STR = """
+commitizen:
+  name: cz_jira
+  version: 1.0.0
+  version_files:
+  - commitizen/__version__.py
+  - pyproject.toml
+"""
 
 _settings: dict[str, Any] = {
     "name": "cz_jira",
@@ -51,7 +72,10 @@
     "version_provider": "commitizen",
     "version_scheme": None,
     "tag_format": "$version",
+    "legacy_tag_formats": [],
+    "ignored_tag_formats": [],
     "bump_message": None,
+    "retry_after_failure": False,
     "allow_abort": False,
     "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"],
     "version_files": ["commitizen/__version__.py", "pyproject.toml"],
@@ -79,7 +103,10 @@
     "version_provider": "commitizen",
     "version_scheme": None,
     "tag_format": "$version",
+    "legacy_tag_formats": [],
+    "ignored_tag_formats": [],
     "bump_message": None,
+    "retry_after_failure": False,
     "allow_abort": False,
     "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"],
     "version_files": ["commitizen/__version__.py", "pyproject.toml"],
@@ -124,7 +151,7 @@ def test_find_git_project_root(tmpdir):
 
 
 @pytest.mark.parametrize(
-    "config_files_manager", defaults.config_files.copy(), indirect=True
+    "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True
 )
 def test_set_key(config_files_manager):
     _conf = config.read_cfg()
@@ -135,7 +162,7 @@ def test_set_key(config_files_manager):
 
 class TestReadCfg:
     @pytest.mark.parametrize(
-        "config_files_manager", defaults.config_files.copy(), indirect=True
+        "config_files_manager", defaults.CONFIG_FILES.copy(), indirect=True
     )
     def test_load_conf(_, config_files_manager):
         cfg = config.read_cfg()
@@ -156,20 +183,65 @@ def test_load_empty_pyproject_toml_and_cz_toml_with_config(_, tmpdir):
             cfg = config.read_cfg()
             assert cfg.settings == _settings
 
+    def test_load_pyproject_toml_from_config_argument(_, tmpdir):
+        with tmpdir.as_cwd():
+            _not_root_path = tmpdir.mkdir("not_in_root").join("pyproject.toml")
+            _not_root_path.write(PYPROJECT)
+
+            cfg = config.read_cfg(filepath="./not_in_root/pyproject.toml")
+            assert cfg.settings == _settings
+
+    def test_load_cz_json_not_from_config_argument(_, tmpdir):
+        with tmpdir.as_cwd():
+            _not_root_path = tmpdir.mkdir("not_in_root").join(".cz.json")
+            _not_root_path.write(JSON_STR)
+
+            cfg = config.read_cfg(filepath="./not_in_root/.cz.json")
+            json_cfg_by_class = config.JsonConfig(data=JSON_STR, path=_not_root_path)
+            assert cfg.settings == json_cfg_by_class.settings
+
+    def test_load_cz_yaml_not_from_config_argument(_, tmpdir):
+        with tmpdir.as_cwd():
+            _not_root_path = tmpdir.mkdir("not_in_root").join(".cz.yaml")
+            _not_root_path.write(YAML_STR)
 
+            cfg = config.read_cfg(filepath="./not_in_root/.cz.yaml")
+            yaml_cfg_by_class = config.YAMLConfig(data=YAML_STR, path=_not_root_path)
+            assert cfg.settings == yaml_cfg_by_class._settings
+
+    def test_load_empty_pyproject_toml_from_config_argument(_, tmpdir):
+        with tmpdir.as_cwd():
+            _not_root_path = tmpdir.mkdir("not_in_root").join("pyproject.toml")
+            _not_root_path.write("")
+
+            with pytest.raises(ConfigFileIsEmpty):
+                config.read_cfg(filepath="./not_in_root/pyproject.toml")
+
+
+@pytest.mark.parametrize(
+    "config_file, exception_string",
+    [
+        (".cz.toml", r"\.cz\.toml"),
+        ("cz.toml", r"cz\.toml"),
+        ("pyproject.toml", r"pyproject\.toml"),
+    ],
+    ids=[".cz.toml", "cz.toml", "pyproject.toml"],
+)
 class TestTomlConfig:
-    def test_init_empty_config_content(self, tmpdir):
-        path = tmpdir.mkdir("commitizen").join(".cz.toml")
+    def test_init_empty_config_content(self, tmpdir, config_file, exception_string):
+        path = tmpdir.mkdir("commitizen").join(config_file)
         toml_config = config.TomlConfig(data="", path=path)
         toml_config.init_empty_config_content()
 
         with open(path, encoding="utf-8") as toml_file:
             assert toml_file.read() == "[tool.commitizen]\n"
 
-    def test_init_empty_config_content_with_existing_content(self, tmpdir):
-        existing_content = "[tool.black]\n" "line-length = 88\n"
+    def test_init_empty_config_content_with_existing_content(
+        self, tmpdir, config_file, exception_string
+    ):
+        existing_content = "[tool.black]\nline-length = 88\n"
 
-        path = tmpdir.mkdir("commitizen").join(".cz.toml")
+        path = tmpdir.mkdir("commitizen").join(config_file)
         path.write(existing_content)
         toml_config = config.TomlConfig(data="", path=path)
         toml_config.init_empty_config_content()
@@ -177,43 +249,63 @@ def test_init_empty_config_content_with_existing_content(self, tmpdir):
         with open(path, encoding="utf-8") as toml_file:
             assert toml_file.read() == existing_content + "\n[tool.commitizen]\n"
 
-    def test_init_with_invalid_config_content(self, tmpdir):
+    def test_init_with_invalid_config_content(
+        self, tmpdir, config_file, exception_string
+    ):
         existing_content = "invalid toml content"
-        path = tmpdir.mkdir("commitizen").join(".cz.toml")
+        path = tmpdir.mkdir("commitizen").join(config_file)
 
-        with pytest.raises(InvalidConfigurationError, match=r"\.cz\.toml"):
+        with pytest.raises(InvalidConfigurationError, match=exception_string):
             config.TomlConfig(data=existing_content, path=path)
 
 
+@pytest.mark.parametrize(
+    "config_file, exception_string",
+    [
+        (".cz.json", r"\.cz\.json"),
+        ("cz.json", r"cz\.json"),
+    ],
+    ids=[".cz.json", "cz.json"],
+)
 class TestJsonConfig:
-    def test_init_empty_config_content(self, tmpdir):
-        path = tmpdir.mkdir("commitizen").join(".cz.json")
+    def test_init_empty_config_content(self, tmpdir, config_file, exception_string):
+        path = tmpdir.mkdir("commitizen").join(config_file)
         json_config = config.JsonConfig(data="{}", path=path)
         json_config.init_empty_config_content()
 
         with open(path, encoding="utf-8") as json_file:
             assert json.load(json_file) == {"commitizen": {}}
 
-    def test_init_with_invalid_config_content(self, tmpdir):
+    def test_init_with_invalid_config_content(
+        self, tmpdir, config_file, exception_string
+    ):
         existing_content = "invalid json content"
-        path = tmpdir.mkdir("commitizen").join(".cz.json")
+        path = tmpdir.mkdir("commitizen").join(config_file)
 
-        with pytest.raises(InvalidConfigurationError, match=r"\.cz\.json"):
+        with pytest.raises(InvalidConfigurationError, match=exception_string):
             config.JsonConfig(data=existing_content, path=path)
 
 
+@pytest.mark.parametrize(
+    "config_file, exception_string",
+    [
+        (".cz.yaml", r"\.cz\.yaml"),
+        ("cz.yaml", r"cz\.yaml"),
+    ],
+    ids=[".cz.yaml", "cz.yaml"],
+)
 class TestYamlConfig:
-    def test_init_empty_config_content(self, tmpdir):
-        path = tmpdir.mkdir("commitizen").join(".cz.yaml")
+    def test_init_empty_config_content(self, tmpdir, config_file, exception_string):
+        path = tmpdir.mkdir("commitizen").join(config_file)
         yaml_config = config.YAMLConfig(data="{}", path=path)
         yaml_config.init_empty_config_content()
 
         with open(path) as yaml_file:
             assert yaml.safe_load(yaml_file) == {"commitizen": {}}
 
-    def test_init_with_invalid_content(self, tmpdir):
+    def test_init_with_invalid_content(self, tmpdir, config_file, exception_string):
         existing_content = "invalid: .cz.yaml: content: maybe?"
-        path = tmpdir.mkdir("commitizen").join(".cz.yaml")
+        path = tmpdir.mkdir("commitizen").join(config_file)
 
-        with pytest.raises(InvalidConfigurationError, match=r"\.cz\.yaml"):
+        with pytest.raises(InvalidConfigurationError, match=exception_string):
             config.YAMLConfig(data=existing_content, path=path)
diff --git a/tests/test_cz_base.py b/tests/test_cz_base.py
index 891ee01167..0ee5a23fb8 100644
--- a/tests/test_cz_base.py
+++ b/tests/test_cz_base.py
@@ -1,3 +1,5 @@
+from collections.abc import Mapping
+
 import pytest
 
 from commitizen.cz.base import BaseCommitizen
@@ -7,7 +9,7 @@ class DummyCz(BaseCommitizen):
     def questions(self):
         return [{"type": "input", "name": "commit", "message": "Initial commit:\n"}]
 
-    def message(self, answers):
+    def message(self, answers: Mapping):
         return answers["commit"]
 
 
@@ -42,9 +44,3 @@ def test_info(config):
     cz = DummyCz(config)
     with pytest.raises(NotImplementedError):
         cz.info()
-
-
-def test_process_commit(config):
-    cz = DummyCz(config)
-    message = cz.process_commit("test(test_scope): this is test msg")
-    assert message == "test(test_scope): this is test msg"
diff --git a/tests/test_cz_conventional_commits.py b/tests/test_cz_conventional_commits.py
index 04d0522174..c96e036707 100644
--- a/tests/test_cz_conventional_commits.py
+++ b/tests/test_cz_conventional_commits.py
@@ -2,48 +2,42 @@
 
 from commitizen.cz.conventional_commits.conventional_commits import (
     ConventionalCommitsCz,
-    parse_scope,
-    parse_subject,
+    _parse_scope,
+    _parse_subject,
 )
 from commitizen.cz.exceptions import AnswerRequiredError
 
-valid_scopes = ["", "simple", "dash-separated", "camelCase" "UPPERCASE"]
 
-scopes_transformations = [["with spaces", "with-spaces"], [None, ""]]
-
-valid_subjects = ["this is a normal text", "aword"]
-
-subjects_transformations = [["with dot.", "with dot"]]
-
-invalid_subjects = ["", "   ", ".", "   .", "", None]
-
-
-def test_parse_scope_valid_values():
-    for valid_scope in valid_scopes:
-        assert valid_scope == parse_scope(valid_scope)
+@pytest.mark.parametrize(
+    "valid_scope", ["", "simple", "dash-separated", "camelCaseUPPERCASE"]
+)
+def test_parse_scope_valid_values(valid_scope):
+    assert valid_scope == _parse_scope(valid_scope)
 
 
-def test_scopes_transformations():
-    for scopes_transformation in scopes_transformations:
-        invalid_scope, transformed_scope = scopes_transformation
-        assert transformed_scope == parse_scope(invalid_scope)
+@pytest.mark.parametrize(
+    "scopes_transformation", [["with spaces", "with-spaces"], ["", ""]]
+)
+def test_scopes_transformations(scopes_transformation):
+    invalid_scope, transformed_scope = scopes_transformation
+    assert transformed_scope == _parse_scope(invalid_scope)
 
 
-def test_parse_subject_valid_values():
-    for valid_subject in valid_subjects:
-        assert valid_subject == parse_subject(valid_subject)
+@pytest.mark.parametrize("valid_subject", ["this is a normal text", "aword"])
+def test_parse_subject_valid_values(valid_subject):
+    assert valid_subject == _parse_subject(valid_subject)
 
 
-def test_parse_subject_invalid_values():
-    for valid_subject in invalid_subjects:
-        with pytest.raises(AnswerRequiredError):
-            parse_subject(valid_subject)
+@pytest.mark.parametrize("invalid_subject", ["", "   ", ".", "   .", "\t\t."])
+def test_parse_subject_invalid_values(invalid_subject):
+    with pytest.raises(AnswerRequiredError):
+        _parse_subject(invalid_subject)
 
 
-def test_subject_transformations():
-    for subject_transformation in subjects_transformations:
-        invalid_subject, transformed_subject = subject_transformation
-        assert transformed_subject == parse_subject(invalid_subject)
+@pytest.mark.parametrize("subject_transformation", [["with dot.", "with dot"]])
+def test_subject_transformations(subject_transformation):
+    invalid_subject, transformed_subject = subject_transformation
+    assert transformed_subject == _parse_subject(invalid_subject)
 
 
 def test_questions(config):
@@ -89,7 +83,7 @@ def test_long_answer(config):
     message = conventional_commits.message(answers)
     assert (
         message
-        == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24"  # noqa
+        == "fix(users): email pattern corrected\n\ncomplete content\n\ncloses #24"
     )
 
 
@@ -107,7 +101,7 @@ def test_breaking_change_in_footer(config):
     print(message)
     assert (
         message
-        == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users"  # noqa
+        == "fix(users): email pattern corrected\n\ncomplete content\n\nBREAKING CHANGE: migrate by renaming user to users"
     )
 
 
@@ -130,26 +124,3 @@ def test_info(config):
     conventional_commits = ConventionalCommitsCz(config)
     info = conventional_commits.info()
     assert isinstance(info, str)
-
-
-@pytest.mark.parametrize(
-    ("commit_message", "expected_message"),
-    [
-        (
-            "test(test_scope): this is test msg",
-            "this is test msg",
-        ),
-        (
-            "test(test_scope)!: this is test msg",
-            "this is test msg",
-        ),
-        (
-            "test!(test_scope): this is test msg",
-            "",
-        ),
-    ],
-)
-def test_process_commit(commit_message, expected_message, config):
-    conventional_commits = ConventionalCommitsCz(config)
-    message = conventional_commits.process_commit(commit_message)
-    assert message == expected_message
diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py
index 9f3556fce8..933b1aa065 100644
--- a/tests/test_cz_customize.py
+++ b/tests/test_cz_customize.py
@@ -473,16 +473,16 @@ def test_answer(config):
     cz = CustomizeCommitsCz(config)
     answers = {
         "change_type": "feature",
-        "message": "this feature enaable customize through config file",
+        "message": "this feature enable customize through config file",
         "show_message": True,
     }
     message = cz.message(answers)
-    assert message == "feature: this feature enaable customize through config file"
+    assert message == "feature: this feature enable customize through config file"
 
     cz = CustomizeCommitsCz(config)
     answers = {
         "change_type": "feature",
-        "message": "this feature enaable customize through config file",
+        "message": "this feature enable customize through config file",
         "show_message": False,
     }
     message = cz.message(answers)
@@ -498,7 +498,8 @@ def test_answer_unicode(config_with_unicode):
     }
     message = cz.message(answers)
     assert (
-        message == "✨ feature: this feature enables customization through a config file"
+        message
+        == "✨ feature: this feature enables customization through a config file"
     )
 
     cz = CustomizeCommitsCz(config_with_unicode)
@@ -563,7 +564,7 @@ def test_info_with_info_path(tmpdir, config_info):
 
 def test_info_without_info(config_without_info):
     cz = CustomizeCommitsCz(config_without_info)
-    assert cz.info() is None
+    assert cz.info() == ""
 
 
 def test_commit_parser(config):
@@ -574,7 +575,8 @@ def test_commit_parser(config):
 def test_commit_parser_unicode(config_with_unicode):
     cz = CustomizeCommitsCz(config_with_unicode)
     assert (
-        cz.commit_parser == "^(?P<change_type>✨ feature|🐛 bug fix):\\s(?P<message>.*)?"
+        cz.commit_parser
+        == "^(?P<change_type>✨ feature|🐛 bug fix):\\s(?P<message>.*)?"
     )
 
 
diff --git a/tests/test_cz_search_filter.py b/tests/test_cz_search_filter.py
new file mode 100644
index 0000000000..0e70e3104a
--- /dev/null
+++ b/tests/test_cz_search_filter.py
@@ -0,0 +1,76 @@
+import pytest
+
+from commitizen.config import TomlConfig
+from commitizen.cz.customize import CustomizeCommitsCz
+
+TOML_WITH_SEARCH_FILTER = r"""
+[tool.commitizen]
+name = "cz_customize"
+
+[tool.commitizen.customize]
+message_template = "{{change_type}}:{% if scope %} ({{scope}}){% endif %}{% if breaking %}!{% endif %} {{message}}"
+
+[[tool.commitizen.customize.questions]]
+type = "select"
+name = "change_type"
+message = "Select the type of change you are committing"
+use_search_filter = true
+use_jk_keys = false
+choices = [
+    {value = "fix", name = "fix: A bug fix. Correlates with PATCH in SemVer"},
+    {value = "feat", name = "feat: A new feature. Correlates with MINOR in SemVer"},
+    {value = "docs", name = "docs: Documentation only changes"},
+    {value = "style", name = "style: Changes that do not affect the meaning of the code"},
+    {value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature"},
+    {value = "perf", name = "perf: A code change that improves performance"},
+    {value = "test", name = "test: Adding missing or correcting existing tests"},
+    {value = "build", name = "build: Changes that affect the build system or external dependencies"},
+    {value = "ci", name = "ci: Changes to CI configuration files and scripts"}
+]
+
+[[tool.commitizen.customize.questions]]
+type = "input"
+name = "scope"
+message = "What is the scope of this change? (class or file name): (press [enter] to skip)"
+
+[[tool.commitizen.customize.questions]]
+type = "input"
+name = "message"
+message = "Write a short and imperative summary of the code changes: (lower case and no period)"
+"""
+
+
+@pytest.fixture
+def config():
+    return TomlConfig(data=TOML_WITH_SEARCH_FILTER, path="not_exist.toml")
+
+
+def test_questions_with_search_filter(config):
+    """Test that questions are properly configured with search filter"""
+    cz = CustomizeCommitsCz(config)
+    questions = cz.questions()
+
+    # Test that the first question (change_type) has search filter enabled
+    assert questions[0]["type"] == "select"
+    assert questions[0]["name"] == "change_type"
+    assert questions[0]["use_search_filter"] is True
+    assert questions[0]["use_jk_keys"] is False
+
+    # Test that the choices are properly configured
+    choices = questions[0]["choices"]
+    assert len(choices) == 9  # We have 9 commit types
+    assert choices[0]["value"] == "fix"
+    assert choices[1]["value"] == "feat"
+
+
+def test_message_template(config):
+    """Test that the message template is properly configured"""
+    cz = CustomizeCommitsCz(config)
+    template = cz.message(
+        {
+            "change_type": "feat",
+            "scope": "search",
+            "message": "add search filter support",
+        }
+    )
+    assert template == "feat: (search) add search filter support"
diff --git a/tests/test_cz_utils.py b/tests/test_cz_utils.py
index 94cc7f5b87..25c960c9a7 100644
--- a/tests/test_cz_utils.py
+++ b/tests/test_cz_utils.py
@@ -1,4 +1,5 @@
 import pytest
+from pytest_mock import MockFixture
 
 from commitizen.cz import exceptions, utils
 
@@ -17,3 +18,9 @@ def test_multiple_line_breaker():
 
     result = utils.multiple_line_breaker(message, "is")
     assert result == "th\n\nthe first line    | and th\n\nthe second line"
+
+
+def test_get_backup_file_path_no_project_root(mocker: MockFixture):
+    project_root_mock = mocker.patch("commitizen.git.find_git_project_root")
+    project_root_mock.return_value = None
+    assert utils.get_backup_file_path()
diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py
new file mode 100644
index 0000000000..41bea81a73
--- /dev/null
+++ b/tests/test_deprecated.py
@@ -0,0 +1,33 @@
+import pytest
+
+from commitizen import changelog_formats, defaults
+
+
+def test_getattr_deprecated_vars():
+    # Test each deprecated variable
+    with pytest.warns(DeprecationWarning) as record:
+        assert defaults.bump_pattern == defaults.BUMP_PATTERN
+        assert defaults.bump_map == defaults.BUMP_MAP
+        assert (
+            defaults.bump_map_major_version_zero == defaults.BUMP_MAP_MAJOR_VERSION_ZERO
+        )
+        assert defaults.bump_message == defaults.BUMP_MESSAGE
+        assert defaults.change_type_order == defaults.CHANGE_TYPE_ORDER
+        assert defaults.encoding == defaults.ENCODING
+        assert defaults.name == defaults.DEFAULT_SETTINGS["name"]
+        assert (
+            changelog_formats._guess_changelog_format
+            == changelog_formats.guess_changelog_format
+        )
+
+    # Verify warning messages
+    assert len(record) == 7
+    for warning in record:
+        assert "is deprecated and will be removed" in str(warning.message)
+
+
+def test_getattr_non_existent():
+    # Test non-existent attribute
+    with pytest.raises(AttributeError) as exc_info:
+        _ = defaults.non_existent_attribute
+    assert "is not an attribute of" in str(exc_info.value)
diff --git a/tests/test_factory.py b/tests/test_factory.py
index 0ac8c0b275..d81a84b3d5 100644
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -1,7 +1,11 @@
 import sys
 from textwrap import dedent
 
-import importlib_metadata as metadata
+if sys.version_info >= (3, 10):
+    from importlib import metadata
+else:
+    import importlib_metadata as metadata
+
 import pytest
 
 from commitizen import BaseCommitizen, defaults, factory
@@ -24,7 +28,7 @@ class OtherPlugin:
 def test_factory():
     config = BaseConfig()
     config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]})
-    r = factory.commiter_factory(config)
+    r = factory.committer_factory(config)
     assert isinstance(r, BaseCommitizen)
 
 
@@ -32,7 +36,7 @@ def test_factory_fails():
     config = BaseConfig()
     config.settings.update({"name": "Nothing"})
     with pytest.raises(NoCommitizenFoundException) as excinfo:
-        factory.commiter_factory(config)
+        factory.committer_factory(config)
 
     assert "The committer has not been found in the system." in str(excinfo)
 
diff --git a/tests/test_git.py b/tests/test_git.py
index 4ae11a45dd..e242b3a2ae 100644
--- a/tests/test_git.py
+++ b/tests/test_git.py
@@ -6,10 +6,23 @@
 import shutil
 
 import pytest
-from commitizen import cmd, exceptions, git
 from pytest_mock import MockFixture
 
-from tests.utils import FakeCommand, create_file_and_commit, create_tag
+from commitizen import cmd, exceptions, git
+from tests.utils import (
+    FakeCommand,
+    create_branch,
+    create_file_and_commit,
+    create_tag,
+    switch_branch,
+)
+
+
+@pytest.mark.parametrize("date", ["2020-01-21", "1970-01-01"])
+def test_git_tag_date(date: str):
+    git_tag = git.GitTag(rev="sha1-code", name="0.0.1", date="2025-05-30")
+    git_tag.date = date
+    assert git_tag.date == date
 
 
 def test_git_object_eq():
@@ -42,8 +55,42 @@ def test_get_tags(mocker: MockFixture):
     assert git.get_tags() == []
 
 
+def test_get_reachable_tags(tmp_commitizen_project):
+    with tmp_commitizen_project.as_cwd():
+        create_file_and_commit("Initial state")
+        create_tag("1.0.0")
+        # create develop
+        create_branch("develop")
+        switch_branch("develop")
+
+        # add a feature to develop
+        create_file_and_commit("develop")
+        create_tag("1.1.0b0")
+
+        # create staging
+        switch_branch("master")
+        create_file_and_commit("master")
+        create_tag("1.0.1")
+
+        tags = git.get_tags(reachable_only=True)
+        tag_names = set([t.name for t in tags])
+        # 1.1.0b0 is not present
+        assert tag_names == {"1.0.0", "1.0.1"}
+
+
+@pytest.mark.parametrize("locale", ["en_US", "fr_FR"])
+def test_get_reachable_tags_with_commits(
+    tmp_commitizen_project, locale: str, monkeypatch: pytest.MonkeyPatch
+):
+    monkeypatch.setenv("LANG", f"{locale}.UTF-8")
+    monkeypatch.setenv("LANGUAGE", f"{locale}.UTF-8")
+    monkeypatch.setenv("LC_ALL", f"{locale}.UTF-8")
+    with tmp_commitizen_project.as_cwd():
+        assert git.get_tags(reachable_only=True) == []
+
+
 def test_get_tag_names(mocker: MockFixture):
-    tag_str = "v1.0.0\n" "v0.5.0\n" "v0.0.1\n"
+    tag_str = "v1.0.0\nv0.5.0\nv0.0.1\n"
     mocker.patch("commitizen.cmd.run", return_value=FakeCommand(out=tag_str))
 
     assert git.get_tag_names() == ["v1.0.0", "v0.5.0", "v0.0.1"]
@@ -91,11 +138,13 @@ def test_get_commits_author_and_email():
 def test_get_commits_without_email(mocker: MockFixture):
     raw_commit = (
         "a515bb8f71c403f6f7d1c17b9d8ebf2ce3959395\n"
+        "95bbfc703eb99cb49ba0d6ffd8469911303dbe63 12d3b4bdaa996ea7067a07660bb5df4772297bdd\n"
         "\n"
         "user name\n"
         "\n"
         "----------commit-delimiter----------\n"
         "12d3b4bdaa996ea7067a07660bb5df4772297bdd\n"
+        "de33bc5070de19600f2f00262b3c15efea762408\n"
         "feat(users): add username\n"
         "user name\n"
         "\n"
@@ -118,16 +167,19 @@ def test_get_commits_without_email(mocker: MockFixture):
 def test_get_commits_without_breakline_in_each_commit(mocker: MockFixture):
     raw_commit = (
         "ae9ba6fc5526cf478f52ef901418d85505109744\n"
+        "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
         "bump: version 2.13.0 → 2.14.0\n"
         "GitHub Action\n"
         "action@github.com\n"
         "----------commit-delimiter----------\n"
         "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
+        "b4dc83284dc8c9729032a774a037df1d1f2397d5 20a54bf1b82cd7b573351db4d1e8814dd0be205d\n"
         "Merge pull request #332 from cliles/feature/271-redux\n"
         "User\n"
         "user@email.com\n"
         "Feature/271 redux----------commit-delimiter----------\n"
         "20a54bf1b82cd7b573351db4d1e8814dd0be205d\n"
+        "658f38c3fe832cdab63ed4fb1f7b3a0969a583be\n"
         "feat(#271): enable creation of annotated tags when bumping\n"
         "User 2\n"
         "user@email.edu\n"
@@ -152,6 +204,55 @@ def test_get_commits_without_breakline_in_each_commit(mocker: MockFixture):
     )
 
 
+def test_get_commits_with_and_without_parents(mocker: MockFixture):
+    raw_commit = (
+        "4206e661bacf9643373255965f34bbdb382cb2b9\n"
+        "ae9ba6fc5526cf478f52ef901418d85505109744 bf8479e7aa1a5b9d2f491b79e3a4d4015519903e\n"
+        "Merge pull request from someone\n"
+        "Maintainer\n"
+        "maintainer@email.com\n"
+        "This is a much needed feature----------commit-delimiter----------\n"
+        "ae9ba6fc5526cf478f52ef901418d85505109744\n"
+        "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
+        "Release 0.1.0\n"
+        "GitHub Action\n"
+        "action@github.com\n"
+        "----------commit-delimiter----------\n"
+        "ff2f56ca844de72a9d59590831087bf5a97bac84\n"
+        "\n"
+        "Initial commit\n"
+        "User\n"
+        "user@email.com\n"
+        "----------commit-delimiter----------\n"
+    )
+    mocker.patch("commitizen.cmd.run", return_value=FakeCommand(out=raw_commit))
+
+    commits = git.get_commits()
+
+    assert commits[0].author == "Maintainer"
+    assert commits[1].author == "GitHub Action"
+    assert commits[2].author == "User"
+
+    assert commits[0].author_email == "maintainer@email.com"
+    assert commits[1].author_email == "action@github.com"
+    assert commits[2].author_email == "user@email.com"
+
+    assert commits[0].title == "Merge pull request from someone"
+    assert commits[1].title == "Release 0.1.0"
+    assert commits[2].title == "Initial commit"
+
+    assert commits[0].body == "This is a much needed feature"
+    assert commits[1].body == ""
+    assert commits[2].body == ""
+
+    assert commits[0].parents == [
+        "ae9ba6fc5526cf478f52ef901418d85505109744",
+        "bf8479e7aa1a5b9d2f491b79e3a4d4015519903e",
+    ]
+    assert commits[1].parents == ["ff2f56ca844de72a9d59590831087bf5a97bac84"]
+    assert commits[2].parents == []
+
+
 def test_get_commits_with_signature():
     config_file = ".git/config"
     config_backup = ".git/config.bak"
@@ -176,7 +277,7 @@ def test_get_commits_with_signature():
 def test_get_tag_names_has_correct_arrow_annotation():
     arrow_annotation = inspect.getfullargspec(git.get_tag_names).annotations["return"]
 
-    assert arrow_annotation == "list[str | None]"
+    assert arrow_annotation == "list[str]"
 
 
 def test_get_latest_tag_name(tmp_commitizen_project):
@@ -222,24 +323,38 @@ def test_is_staging_clean_when_updating_file(tmp_commitizen_project):
         assert git.is_staging_clean() is False
 
 
-def test_git_eol_style(tmp_commitizen_project):
+def test_get_eol_for_open(tmp_commitizen_project):
     with tmp_commitizen_project.as_cwd():
-        assert git.get_eol_style() == git.EOLTypes.NATIVE
+        assert git.EOLType.for_open() == os.linesep
 
         cmd.run("git config core.eol lf")
-        assert git.get_eol_style() == git.EOLTypes.LF
+        assert git.EOLType.for_open() == "\n"
 
         cmd.run("git config core.eol crlf")
-        assert git.get_eol_style() == git.EOLTypes.CRLF
+        assert git.EOLType.for_open() == "\r\n"
 
         cmd.run("git config core.eol native")
-        assert git.get_eol_style() == git.EOLTypes.NATIVE
+        assert git.EOLType.for_open() == os.linesep
+
+
+def test_get_core_editor(mocker):
+    mocker.patch.dict(os.environ, {"GIT_EDITOR": "nano"})
+    assert git.get_core_editor() == "nano"
 
+    mocker.patch.dict(os.environ, clear=True)
+    mocker.patch(
+        "commitizen.cmd.run",
+        return_value=cmd.Command(
+            out="vim", err="", stdout=b"", stderr=b"", return_code=0
+        ),
+    )
+    assert git.get_core_editor() == "vim"
 
-def test_eoltypes_get_eol_for_open():
-    assert git.EOLTypes.get_eol_for_open(git.EOLTypes.NATIVE) == os.linesep
-    assert git.EOLTypes.get_eol_for_open(git.EOLTypes.LF) == "\n"
-    assert git.EOLTypes.get_eol_for_open(git.EOLTypes.CRLF) == "\r\n"
+    mocker.patch(
+        "commitizen.cmd.run",
+        return_value=cmd.Command(out="", err="", stdout=b"", stderr=b"", return_code=1),
+    )
+    assert git.get_core_editor() is None
 
 
 def test_create_tag_with_message(tmp_commitizen_project):
@@ -252,3 +367,116 @@ def test_create_tag_with_message(tmp_commitizen_project):
         assert git.get_tag_message(tag_name) == (
             tag_message if platform.system() != "Windows" else f"'{tag_message}'"
         )
+
+
+@pytest.mark.parametrize(
+    "file_path,expected_cmd",
+    [
+        (
+            "/tmp/temp file",
+            'git commit --signoff -F "/tmp/temp file"',
+        ),
+        (
+            "/tmp dir/temp file",
+            'git commit --signoff -F "/tmp dir/temp file"',
+        ),
+        (
+            "/tmp/tempfile",
+            'git commit --signoff -F "/tmp/tempfile"',
+        ),
+    ],
+    ids=[
+        "File contains spaces",
+        "Path contains spaces",
+        "Path does not contain spaces",
+    ],
+)
+def test_commit_with_spaces_in_path(mocker, file_path, expected_cmd):
+    mock_run = mocker.patch("commitizen.cmd.run", return_value=FakeCommand())
+    mock_unlink = mocker.patch("os.unlink")
+    mock_temp_file = mocker.patch("commitizen.git.NamedTemporaryFile")
+    mock_temp_file.return_value.name = file_path
+
+    git.commit("feat: new feature", "--signoff")
+
+    mock_run.assert_called_once_with(expected_cmd)
+    mock_unlink.assert_called_once_with(file_path)
+
+
+def test_get_filenames_in_commit_error(mocker: MockFixture):
+    """Test that GitCommandError is raised when git command fails."""
+    mocker.patch(
+        "commitizen.cmd.run",
+        return_value=FakeCommand(out="", err="fatal: bad object HEAD", return_code=1),
+    )
+    with pytest.raises(exceptions.GitCommandError) as excinfo:
+        git.get_filenames_in_commit()
+    assert str(excinfo.value) == "fatal: bad object HEAD"
+
+
+def test_git_commit_from_rev_and_commit():
+    # Test data with all fields populated
+    rev_and_commit = (
+        "abc123\n"  # rev
+        "def456 ghi789\n"  # parents
+        "feat: add new feature\n"  # title
+        "John Doe\n"  # author
+        "john@example.com\n"  # author_email
+        "This is a detailed description\n"  # body
+        "of the new feature\n"
+        "with multiple lines"
+    )
+
+    commit = git.GitCommit.from_rev_and_commit(rev_and_commit)
+
+    assert commit.rev == "abc123"
+    assert commit.title == "feat: add new feature"
+    assert (
+        commit.body
+        == "This is a detailed description\nof the new feature\nwith multiple lines"
+    )
+    assert commit.author == "John Doe"
+    assert commit.author_email == "john@example.com"
+    assert commit.parents == ["def456", "ghi789"]
+
+    # Test with minimal data
+    minimal_commit = (
+        "abc123\n"  # rev
+        "\n"  # no parents
+        "feat: minimal commit\n"  # title
+        "John Doe\n"  # author
+        "john@example.com\n"  # author_email
+    )
+
+    commit = git.GitCommit.from_rev_and_commit(minimal_commit)
+
+    assert commit.rev == "abc123"
+    assert commit.title == "feat: minimal commit"
+    assert commit.body == ""
+    assert commit.author == "John Doe"
+    assert commit.author_email == "john@example.com"
+    assert commit.parents == []
+
+
+@pytest.mark.parametrize(
+    "os_name,committer_date,expected_cmd",
+    [
+        (
+            "nt",
+            "2024-03-20",
+            'cmd /v /c "set GIT_COMMITTER_DATE=2024-03-20&& git commit  -F "temp.txt""',
+        ),
+        (
+            "posix",
+            "2024-03-20",
+            'GIT_COMMITTER_DATE=2024-03-20 git commit  -F "temp.txt"',
+        ),
+        ("nt", None, 'git commit  -F "temp.txt"'),
+        ("posix", None, 'git commit  -F "temp.txt"'),
+    ],
+)
+def test_create_commit_cmd_string(mocker, os_name, committer_date, expected_cmd):
+    """Test the OS-specific behavior of _create_commit_cmd_string"""
+    mocker.patch("os.name", os_name)
+    result = git._create_commit_cmd_string("", committer_date, "temp.txt")
+    assert result == expected_cmd
diff --git a/tests/test_version_scheme_pep440.py b/tests/test_version_scheme_pep440.py
index 89f5a137ad..a983dad14a 100644
--- a/tests/test_version_scheme_pep440.py
+++ b/tests/test_version_scheme_pep440.py
@@ -1,6 +1,8 @@
 import itertools
+import random
 
 import pytest
+
 from commitizen.version_schemes import Pep440, VersionProtocol
 
 simple_flow = [
@@ -43,10 +45,11 @@
     (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"),
 ]
 
-# this cases should be handled gracefully
-unexpected_cases = [
-    (("0.1.1rc0", None, "alpha", 0, None), "0.1.1a0"),
-    (("0.1.1b1", None, "alpha", 0, None), "0.1.1a0"),
+# never bump backwards on pre-releases
+linear_prerelease_cases = [
+    (("0.1.1b1", None, "alpha", 0, None), "0.1.1b2"),
+    (("0.1.1rc0", None, "alpha", 0, None), "0.1.1rc1"),
+    (("0.1.1rc0", None, "beta", 0, None), "0.1.1rc1"),
 ]
 
 weird_cases = [
@@ -78,10 +81,118 @@
     (("1.0.0rc1", None, "rc", 0, None), "1.0.0rc2"),
 ]
 
+# additional pre-release tests run through various release scenarios
+prerelease_cases = [
+    #
+    (("3.3.3", "PATCH", "alpha", 0, None), "3.3.4a0"),
+    (("3.3.4a0", "PATCH", "alpha", 0, None), "3.3.4a1"),
+    (("3.3.4a1", "MINOR", "alpha", 0, None), "3.4.0a0"),
+    (("3.4.0a0", "PATCH", "alpha", 0, None), "3.4.0a1"),
+    (("3.4.0a1", "MINOR", "alpha", 0, None), "3.4.0a2"),
+    (("3.4.0a2", "MAJOR", "alpha", 0, None), "4.0.0a0"),
+    (("4.0.0a0", "PATCH", "alpha", 0, None), "4.0.0a1"),
+    (("4.0.0a1", "MINOR", "alpha", 0, None), "4.0.0a2"),
+    (("4.0.0a2", "MAJOR", "alpha", 0, None), "4.0.0a3"),
+    #
+    (("1.0.0", "PATCH", "alpha", 0, None), "1.0.1a0"),
+    (("1.0.1a0", "PATCH", "alpha", 0, None), "1.0.1a1"),
+    (("1.0.1a1", "MINOR", "alpha", 0, None), "1.1.0a0"),
+    (("1.1.0a0", "PATCH", "alpha", 0, None), "1.1.0a1"),
+    (("1.1.0a1", "MINOR", "alpha", 0, None), "1.1.0a2"),
+    (("1.1.0a2", "MAJOR", "alpha", 0, None), "2.0.0a0"),
+    #
+    (("1.0.0", "MINOR", "alpha", 0, None), "1.1.0a0"),
+    (("1.1.0a0", "PATCH", "alpha", 0, None), "1.1.0a1"),
+    (("1.1.0a1", "MINOR", "alpha", 0, None), "1.1.0a2"),
+    (("1.1.0a2", "PATCH", "alpha", 0, None), "1.1.0a3"),
+    (("1.1.0a3", "MAJOR", "alpha", 0, None), "2.0.0a0"),
+    #
+    (("1.0.0", "MAJOR", "alpha", 0, None), "2.0.0a0"),
+    (("2.0.0a0", "MINOR", "alpha", 0, None), "2.0.0a1"),
+    (("2.0.0a1", "PATCH", "alpha", 0, None), "2.0.0a2"),
+    (("2.0.0a2", "MAJOR", "alpha", 0, None), "2.0.0a3"),
+    (("2.0.0a3", "MINOR", "alpha", 0, None), "2.0.0a4"),
+    (("2.0.0a4", "PATCH", "alpha", 0, None), "2.0.0a5"),
+    (("2.0.0a5", "MAJOR", "alpha", 0, None), "2.0.0a6"),
+    #
+    (("2.0.0b0", "MINOR", "alpha", 0, None), "2.0.0b1"),
+    (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.0b1"),
+    #
+    (("1.0.1a0", "PATCH", None, 0, None), "1.0.1"),
+    (("1.0.1a0", "MINOR", None, 0, None), "1.1.0"),
+    (("1.0.1a0", "MAJOR", None, 0, None), "2.0.0"),
+    #
+    (("1.1.0a0", "PATCH", None, 0, None), "1.1.0"),
+    (("1.1.0a0", "MINOR", None, 0, None), "1.1.0"),
+    (("1.1.0a0", "MAJOR", None, 0, None), "2.0.0"),
+    #
+    (("2.0.0a0", "MINOR", None, 0, None), "2.0.0"),
+    (("2.0.0a0", "MAJOR", None, 0, None), "2.0.0"),
+    (("2.0.0a0", "PATCH", None, 0, None), "2.0.0"),
+    #
+    (("3.0.0a1", None, None, 0, None), "3.0.0"),
+    (("3.0.0b1", None, None, 0, None), "3.0.0"),
+    (("3.0.0rc1", None, None, 0, None), "3.0.0"),
+    #
+    (("3.1.4", None, "alpha", 0, None), "3.1.4a0"),
+    (("3.1.4", None, "beta", 0, None), "3.1.4b0"),
+    (("3.1.4", None, "rc", 0, None), "3.1.4rc0"),
+    #
+    (("3.1.4", None, "alpha", 0, None), "3.1.4a0"),
+    (("3.1.4a0", "PATCH", "alpha", 0, None), "3.1.4a1"),  # UNEXPECTED!
+    (("3.1.4a0", "MINOR", "alpha", 0, None), "3.2.0a0"),
+    (("3.1.4a0", "MAJOR", "alpha", 0, None), "4.0.0a0"),
+]
+
+exact_cases = [
+    (("1.0.0", "PATCH", None, 0, None), "1.0.1"),
+    (("1.0.0", "MINOR", None, 0, None), "1.1.0"),
+    # with exact_increment=False: "1.0.0b0"
+    (("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1b0"),
+    # with exact_increment=False: "1.0.0b1"
+    (("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1b0"),
+    # with exact_increment=False: "1.0.0rc0"
+    (("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1rc0"),
+    # with exact_increment=False: "1.0.0-rc1"
+    (("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1rc0"),
+    # with exact_increment=False: "1.0.0rc1-dev1"
+    (("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1rc0.dev1"),
+    # with exact_increment=False: "1.0.0b0"
+    (("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0b0"),
+    # with exact_increment=False: "1.0.0b1"
+    (("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0b0"),
+    # with exact_increment=False: "1.0.0b1"
+    (("1.0.0b0", "MINOR", "alpha", 0, None), "1.1.0a0"),
+    # with exact_increment=False: "1.0.0rc0"
+    (("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0rc0"),
+    # with exact_increment=False: "1.0.0rc1"
+    (("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0rc0"),
+    # with exact_increment=False: "1.0.0rc1-dev1"
+    (("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0rc0.dev1"),
+    # with exact_increment=False: "2.0.0"
+    (("2.0.0b0", "MAJOR", None, 0, None), "3.0.0"),
+    # with exact_increment=False: "2.0.0"
+    (("2.0.0b0", "MINOR", None, 0, None), "2.1.0"),
+    # with exact_increment=False: "2.0.0"
+    (("2.0.0b0", "PATCH", None, 0, None), "2.0.1"),
+    # same with exact_increment=False
+    (("2.0.0b0", "MAJOR", "alpha", 0, None), "3.0.0a0"),
+    # with exact_increment=False: "2.0.0b1"
+    (("2.0.0b0", "MINOR", "alpha", 0, None), "2.1.0a0"),
+    # with exact_increment=False: "2.0.0b1"
+    (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.1a0"),
+]
+
 
 @pytest.mark.parametrize(
     "test_input,expected",
-    itertools.chain(tdd_cases, weird_cases, simple_flow, unexpected_cases),
+    itertools.chain(
+        tdd_cases,
+        weird_cases,
+        simple_flow,
+        linear_prerelease_cases,
+        prerelease_cases,
+    ),
 )
 def test_bump_pep440_version(test_input, expected):
     current_version = test_input[0]
@@ -102,6 +213,27 @@ def test_bump_pep440_version(test_input, expected):
     )
 
 
+@pytest.mark.parametrize("test_input, expected", exact_cases)
+def test_bump_pep440_version_force(test_input, expected):
+    current_version = test_input[0]
+    increment = test_input[1]
+    prerelease = test_input[2]
+    prerelease_offset = test_input[3]
+    devrelease = test_input[4]
+    assert (
+        str(
+            Pep440(current_version).bump(
+                increment=increment,
+                prerelease=prerelease,
+                prerelease_offset=prerelease_offset,
+                devrelease=devrelease,
+                exact_increment=True,
+            )
+        )
+        == expected
+    )
+
+
 @pytest.mark.parametrize("test_input,expected", local_versions)
 def test_bump_pep440_version_local(test_input, expected):
     current_version = test_input[0]
@@ -131,3 +263,76 @@ def test_pep440_scheme_property():
 
 def test_pep440_implement_version_protocol():
     assert isinstance(Pep440("0.0.1"), VersionProtocol)
+
+
+def test_pep440_sortable():
+    test_input = [x[0][0] for x in simple_flow]
+    test_input.extend([x[1] for x in simple_flow])
+    # randomize
+    random_input = [Pep440(x) for x in random.sample(test_input, len(test_input))]
+    assert len(random_input) == len(test_input)
+    sorted_result = [str(x) for x in sorted(random_input)]
+    assert sorted_result == [
+        "0.1.0",
+        "0.1.0",
+        "0.1.1.dev1",
+        "0.1.1",
+        "0.1.1",
+        "0.2.0",
+        "0.2.0",
+        "0.2.0",
+        "0.3.0.dev1",
+        "0.3.0",
+        "0.3.0",
+        "0.3.0",
+        "0.3.0",
+        "0.3.1a0",
+        "0.3.1a0",
+        "0.3.1a0",
+        "0.3.1a0",
+        "0.3.1a1",
+        "0.3.1a1",
+        "0.3.1a1",
+        "0.3.1",
+        "0.3.1",
+        "0.3.1",
+        "0.3.2",
+        "0.4.2",
+        "1.0.0a0",
+        "1.0.0a0",
+        "1.0.0a1",
+        "1.0.0a1",
+        "1.0.0a1",
+        "1.0.0a1",
+        "1.0.0a2.dev0",
+        "1.0.0a2.dev0",
+        "1.0.0a2.dev1",
+        "1.0.0a2",
+        "1.0.0a3.dev0",
+        "1.0.0a3.dev0",
+        "1.0.0a3.dev1",
+        "1.0.0b0",
+        "1.0.0b0",
+        "1.0.0b0",
+        "1.0.0b1",
+        "1.0.0b1",
+        "1.0.0rc0",
+        "1.0.0rc0",
+        "1.0.0rc0",
+        "1.0.0rc0",
+        "1.0.0rc1.dev1",
+        "1.0.0rc1",
+        "1.0.0",
+        "1.0.0",
+        "1.0.1",
+        "1.0.1",
+        "1.0.2",
+        "1.0.2",
+        "1.1.0",
+        "1.1.0",
+        "1.2.0",
+        "1.2.0",
+        "1.2.1",
+        "1.2.1",
+        "2.0.0",
+    ]
diff --git a/tests/test_version_scheme_semver.py b/tests/test_version_scheme_semver.py
index 82ed33c229..8785717a34 100644
--- a/tests/test_version_scheme_semver.py
+++ b/tests/test_version_scheme_semver.py
@@ -1,4 +1,5 @@
 import itertools
+import random
 
 import pytest
 
@@ -44,10 +45,11 @@
     (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"),
 ]
 
-# this cases should be handled gracefully
-unexpected_cases = [
-    (("0.1.1rc0", None, "alpha", 0, None), "0.1.1-a0"),
-    (("0.1.1b1", None, "alpha", 0, None), "0.1.1-a0"),
+# never bump backwards on pre-releases
+linear_prerelease_cases = [
+    (("0.1.1b1", None, "alpha", 0, None), "0.1.1-b2"),
+    (("0.1.1rc0", None, "alpha", 0, None), "0.1.1-rc1"),
+    (("0.1.1rc0", None, "beta", 0, None), "0.1.1-rc1"),
 ]
 
 weird_cases = [
@@ -81,10 +83,47 @@
     (("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"),
 ]
 
+exact_cases = [
+    (("1.0.0", "PATCH", None, 0, None), "1.0.1"),
+    (("1.0.0", "MINOR", None, 0, None), "1.1.0"),
+    # with exact_increment=False: "1.0.0-b0"
+    (("1.0.0a1", "PATCH", "beta", 0, None), "1.0.1-b0"),
+    # with exact_increment=False: "1.0.0-b1"
+    (("1.0.0b0", "PATCH", "beta", 0, None), "1.0.1-b0"),
+    # with exact_increment=False: "1.0.0-rc0"
+    (("1.0.0b1", "PATCH", "rc", 0, None), "1.0.1-rc0"),
+    # with exact_increment=False: "1.0.0-rc1"
+    (("1.0.0rc0", "PATCH", "rc", 0, None), "1.0.1-rc0"),
+    # with exact_increment=False: "1.0.0-rc1-dev1"
+    (("1.0.0rc0", "PATCH", "rc", 0, 1), "1.0.1-rc0-dev1"),
+    # with exact_increment=False: "1.0.0-b0"
+    (("1.0.0a1", "MINOR", "beta", 0, None), "1.1.0-b0"),
+    # with exact_increment=False: "1.0.0-b1"
+    (("1.0.0b0", "MINOR", "beta", 0, None), "1.1.0-b0"),
+    # with exact_increment=False: "1.0.0-rc0"
+    (("1.0.0b1", "MINOR", "rc", 0, None), "1.1.0-rc0"),
+    # with exact_increment=False: "1.0.0-rc1"
+    (("1.0.0rc0", "MINOR", "rc", 0, None), "1.1.0-rc0"),
+    # with exact_increment=False: "1.0.0-rc1-dev1"
+    (("1.0.0rc0", "MINOR", "rc", 0, 1), "1.1.0-rc0-dev1"),
+    # with exact_increment=False: "2.0.0"
+    (("2.0.0b0", "MAJOR", None, 0, None), "3.0.0"),
+    # with exact_increment=False: "2.0.0"
+    (("2.0.0b0", "MINOR", None, 0, None), "2.1.0"),
+    # with exact_increment=False: "2.0.0"
+    (("2.0.0b0", "PATCH", None, 0, None), "2.0.1"),
+    # same with exact_increment=False
+    (("2.0.0b0", "MAJOR", "alpha", 0, None), "3.0.0-a0"),
+    # with exact_increment=False: "2.0.0b1"
+    (("2.0.0b0", "MINOR", "alpha", 0, None), "2.1.0-a0"),
+    # with exact_increment=False: "2.0.0b1"
+    (("2.0.0b0", "PATCH", "alpha", 0, None), "2.0.1-a0"),
+]
+
 
 @pytest.mark.parametrize(
     "test_input, expected",
-    itertools.chain(tdd_cases, weird_cases, simple_flow, unexpected_cases),
+    itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases),
 )
 def test_bump_semver_version(test_input, expected):
     current_version = test_input[0]
@@ -105,6 +144,27 @@ def test_bump_semver_version(test_input, expected):
     )
 
 
+@pytest.mark.parametrize("test_input, expected", exact_cases)
+def test_bump_semver_version_force(test_input, expected):
+    current_version = test_input[0]
+    increment = test_input[1]
+    prerelease = test_input[2]
+    prerelease_offset = test_input[3]
+    devrelease = test_input[4]
+    assert (
+        str(
+            SemVer(current_version).bump(
+                increment=increment,
+                prerelease=prerelease,
+                prerelease_offset=prerelease_offset,
+                devrelease=devrelease,
+                exact_increment=True,
+            )
+        )
+        == expected
+    )
+
+
 @pytest.mark.parametrize("test_input,expected", local_versions)
 def test_bump_semver_version_local(test_input, expected):
     current_version = test_input[0]
@@ -134,3 +194,76 @@ def test_semver_scheme_property():
 
 def test_semver_implement_version_protocol():
     assert isinstance(SemVer("0.0.1"), VersionProtocol)
+
+
+def test_semver_sortable():
+    test_input = [x[0][0] for x in simple_flow]
+    test_input.extend([x[1] for x in simple_flow])
+    # randomize
+    random_input = [SemVer(x) for x in random.sample(test_input, len(test_input))]
+    assert len(random_input) == len(test_input)
+    sorted_result = [str(x) for x in sorted(random_input)]
+    assert sorted_result == [
+        "0.1.0",
+        "0.1.0",
+        "0.1.1-dev1",
+        "0.1.1",
+        "0.1.1",
+        "0.2.0",
+        "0.2.0",
+        "0.2.0",
+        "0.3.0-dev1",
+        "0.3.0",
+        "0.3.0",
+        "0.3.0",
+        "0.3.0",
+        "0.3.1-a0",
+        "0.3.1-a0",
+        "0.3.1-a0",
+        "0.3.1-a0",
+        "0.3.1-a1",
+        "0.3.1-a1",
+        "0.3.1-a1",
+        "0.3.1",
+        "0.3.1",
+        "0.3.1",
+        "0.3.2",
+        "0.4.2",
+        "1.0.0-a0",
+        "1.0.0-a0",
+        "1.0.0-a1",
+        "1.0.0-a1",
+        "1.0.0-a1",
+        "1.0.0-a1",
+        "1.0.0-a2-dev0",
+        "1.0.0-a2-dev0",
+        "1.0.0-a2-dev1",
+        "1.0.0-a2",
+        "1.0.0-a3-dev0",
+        "1.0.0-a3-dev0",
+        "1.0.0-a3-dev1",
+        "1.0.0-b0",
+        "1.0.0-b0",
+        "1.0.0-b0",
+        "1.0.0-b1",
+        "1.0.0-b1",
+        "1.0.0-rc0",
+        "1.0.0-rc0",
+        "1.0.0-rc0",
+        "1.0.0-rc0",
+        "1.0.0-rc1-dev1",
+        "1.0.0-rc1",
+        "1.0.0",
+        "1.0.0",
+        "1.0.1",
+        "1.0.1",
+        "1.0.2",
+        "1.0.2",
+        "1.1.0",
+        "1.1.0",
+        "1.2.0",
+        "1.2.0",
+        "1.2.1",
+        "1.2.1",
+        "2.0.0",
+    ]
diff --git a/tests/test_version_scheme_semver2.py b/tests/test_version_scheme_semver2.py
new file mode 100644
index 0000000000..d18a058a7c
--- /dev/null
+++ b/tests/test_version_scheme_semver2.py
@@ -0,0 +1,211 @@
+import itertools
+import random
+
+import pytest
+
+from commitizen.version_schemes import SemVer2, VersionProtocol
+
+simple_flow = [
+    (("0.1.0", "PATCH", None, 0, None), "0.1.1"),
+    (("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev.1"),
+    (("0.1.1", "MINOR", None, 0, None), "0.2.0"),
+    (("0.2.0", "MINOR", None, 0, None), "0.3.0"),
+    (("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev.1"),
+    (("0.3.0", "PATCH", None, 0, None), "0.3.1"),
+    (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-alpha.0"),
+    (("0.3.1-alpha.0", None, "alpha", 0, None), "0.3.1-alpha.1"),
+    (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-alpha.1"),
+    (("0.3.1-alpha.0", None, "alpha", 1, None), "0.3.1-alpha.1"),
+    (("0.3.1-alpha.0", None, None, 0, None), "0.3.1"),
+    (("0.3.1", "PATCH", None, 0, None), "0.3.2"),
+    (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"),
+    (("1.0.0-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"),
+    (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"),
+    (("1.0.0-alpha.1", None, "alpha", 0, 1), "1.0.0-alpha.2.dev.1"),
+    (("1.0.0-alpha.2.dev.0", None, "alpha", 0, 1), "1.0.0-alpha.3.dev.1"),
+    (("1.0.0-alpha.2.dev.0", None, "alpha", 0, 0), "1.0.0-alpha.3.dev.0"),
+    (("1.0.0-alpha.1", None, "beta", 0, None), "1.0.0-beta.0"),
+    (("1.0.0-beta.0", None, "beta", 0, None), "1.0.0-beta.1"),
+    (("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"),
+    (("1.0.0-rc.0", None, "rc", 0, None), "1.0.0-rc.1"),
+    (("1.0.0-rc.0", None, "rc", 0, 1), "1.0.0-rc.1.dev.1"),
+    (("1.0.0-rc.0", "PATCH", None, 0, None), "1.0.0"),
+    (("1.0.0-alpha.3.dev.0", None, "beta", 0, None), "1.0.0-beta.0"),
+    (("1.0.0", "PATCH", None, 0, None), "1.0.1"),
+    (("1.0.1", "PATCH", None, 0, None), "1.0.2"),
+    (("1.0.2", "MINOR", None, 0, None), "1.1.0"),
+    (("1.1.0", "MINOR", None, 0, None), "1.2.0"),
+    (("1.2.0", "PATCH", None, 0, None), "1.2.1"),
+    (("1.2.1", "MAJOR", None, 0, None), "2.0.0"),
+]
+
+local_versions = [
+    (("4.5.0+0.1.0", "PATCH", None, 0, None), "4.5.0+0.1.1"),
+    (("4.5.0+0.1.1", "MINOR", None, 0, None), "4.5.0+0.2.0"),
+    (("4.5.0+0.2.0", "MAJOR", None, 0, None), "4.5.0+1.0.0"),
+]
+
+# never bump backwards on pre-releases
+linear_prerelease_cases = [
+    (("0.1.1-beta.1", None, "alpha", 0, None), "0.1.1-beta.2"),
+    (("0.1.1-rc.0", None, "alpha", 0, None), "0.1.1-rc.1"),
+    (("0.1.1-rc.0", None, "beta", 0, None), "0.1.1-rc.1"),
+]
+
+weird_cases = [
+    (("1.1", "PATCH", None, 0, None), "1.1.1"),
+    (("1", "MINOR", None, 0, None), "1.1.0"),
+    (("1", "MAJOR", None, 0, None), "2.0.0"),
+    (("1-alpha.0", None, "alpha", 0, None), "1.0.0-alpha.1"),
+    (("1-alpha.0", None, "alpha", 1, None), "1.0.0-alpha.1"),
+    (("1", None, "beta", 0, None), "1.0.0-beta.0"),
+    (("1", None, "beta", 1, None), "1.0.0-beta.1"),
+    (("1-beta", None, "beta", 0, None), "1.0.0-beta.1"),
+    (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"),
+    (("1", None, "rc", 0, None), "1.0.0-rc.0"),
+    (("1.0.0-rc.1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"),
+]
+
+# test driven development
+tdd_cases = [
+    (("0.1.1", "PATCH", None, 0, None), "0.1.2"),
+    (("0.1.1", "MINOR", None, 0, None), "0.2.0"),
+    (("2.1.1", "MAJOR", None, 0, None), "3.0.0"),
+    (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-alpha.0"),
+    (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-alpha.0"),
+    (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-alpha.0"),
+    (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-alpha.1"),
+    (("1.0.0-alpha.2", None, "beta", 0, None), "1.0.0-beta.0"),
+    (("1.0.0-alpha.2", None, "beta", 1, None), "1.0.0-beta.1"),
+    (("1.0.0-beta.1", None, "rc", 0, None), "1.0.0-rc.0"),
+    (("1.0.0-rc.1", None, "rc", 0, None), "1.0.0-rc.2"),
+    (("1.0.0-alpha.0", None, "rc", 0, None), "1.0.0-rc.0"),
+    (("1.0.0-alpha.1", None, "alpha", 0, None), "1.0.0-alpha.2"),
+]
+
+
+@pytest.mark.parametrize(
+    "test_input, expected",
+    itertools.chain(tdd_cases, weird_cases, simple_flow, linear_prerelease_cases),
+)
+def test_bump_semver_version(test_input, expected):
+    current_version = test_input[0]
+    increment = test_input[1]
+    prerelease = test_input[2]
+    prerelease_offset = test_input[3]
+    devrelease = test_input[4]
+    assert (
+        str(
+            SemVer2(current_version).bump(
+                increment=increment,
+                prerelease=prerelease,
+                prerelease_offset=prerelease_offset,
+                devrelease=devrelease,
+            )
+        )
+        == expected
+    )
+
+
+@pytest.mark.parametrize("test_input,expected", local_versions)
+def test_bump_semver_version_local(test_input, expected):
+    current_version = test_input[0]
+    increment = test_input[1]
+    prerelease = test_input[2]
+    prerelease_offset = test_input[3]
+    devrelease = test_input[4]
+    is_local_version = True
+    assert (
+        str(
+            SemVer2(current_version).bump(
+                increment=increment,
+                prerelease=prerelease,
+                prerelease_offset=prerelease_offset,
+                devrelease=devrelease,
+                is_local_version=is_local_version,
+            )
+        )
+        == expected
+    )
+
+
+def test_semver_scheme_property():
+    version = SemVer2("0.0.1")
+    assert version.scheme is SemVer2
+
+
+def test_semver_implement_version_protocol():
+    assert isinstance(SemVer2("0.0.1"), VersionProtocol)
+
+
+def test_semver_sortable():
+    test_input = [x[0][0] for x in simple_flow]
+    test_input.extend([x[1] for x in simple_flow])
+    # randomize
+    random_input = [SemVer2(x) for x in random.sample(test_input, len(test_input))]
+    assert len(random_input) == len(test_input)
+    sorted_result = [str(x) for x in sorted(random_input)]
+    assert sorted_result == [
+        "0.1.0",
+        "0.1.0",
+        "0.1.1-dev.1",
+        "0.1.1",
+        "0.1.1",
+        "0.2.0",
+        "0.2.0",
+        "0.2.0",
+        "0.3.0-dev.1",
+        "0.3.0",
+        "0.3.0",
+        "0.3.0",
+        "0.3.0",
+        "0.3.1-alpha.0",
+        "0.3.1-alpha.0",
+        "0.3.1-alpha.0",
+        "0.3.1-alpha.0",
+        "0.3.1-alpha.1",
+        "0.3.1-alpha.1",
+        "0.3.1-alpha.1",
+        "0.3.1",
+        "0.3.1",
+        "0.3.1",
+        "0.3.2",
+        "0.4.2",
+        "1.0.0-alpha.0",
+        "1.0.0-alpha.0",
+        "1.0.0-alpha.1",
+        "1.0.0-alpha.1",
+        "1.0.0-alpha.1",
+        "1.0.0-alpha.1",
+        "1.0.0-alpha.2.dev.0",
+        "1.0.0-alpha.2.dev.0",
+        "1.0.0-alpha.2.dev.1",
+        "1.0.0-alpha.2",
+        "1.0.0-alpha.3.dev.0",
+        "1.0.0-alpha.3.dev.0",
+        "1.0.0-alpha.3.dev.1",
+        "1.0.0-beta.0",
+        "1.0.0-beta.0",
+        "1.0.0-beta.0",
+        "1.0.0-beta.1",
+        "1.0.0-beta.1",
+        "1.0.0-rc.0",
+        "1.0.0-rc.0",
+        "1.0.0-rc.0",
+        "1.0.0-rc.0",
+        "1.0.0-rc.1.dev.1",
+        "1.0.0-rc.1",
+        "1.0.0",
+        "1.0.0",
+        "1.0.1",
+        "1.0.1",
+        "1.0.2",
+        "1.0.2",
+        "1.1.0",
+        "1.1.0",
+        "1.2.0",
+        "1.2.0",
+        "1.2.1",
+        "1.2.1",
+        "2.0.0",
+    ]
diff --git a/tests/test_version_schemes.py b/tests/test_version_schemes.py
index e30c780283..8e2dae9027 100644
--- a/tests/test_version_schemes.py
+++ b/tests/test_version_schemes.py
@@ -1,8 +1,14 @@
 from __future__ import annotations
 
+import sys
+
+if sys.version_info >= (3, 10):
+    from importlib import metadata
+else:
+    import importlib_metadata as metadata
+
 import pytest
 from pytest_mock import MockerFixture
-import importlib_metadata as metadata
 
 from commitizen.config.base_config import BaseConfig
 from commitizen.exceptions import VersionSchemeUnknown
@@ -10,31 +16,31 @@
 
 
 def test_default_version_scheme_is_pep440(config: BaseConfig):
-    scheme = get_version_scheme(config)
+    scheme = get_version_scheme(config.settings)
     assert scheme is Pep440
 
 
 def test_version_scheme_from_config(config: BaseConfig):
     config.settings["version_scheme"] = "semver"
-    scheme = get_version_scheme(config)
+    scheme = get_version_scheme(config.settings)
     assert scheme is SemVer
 
 
 def test_version_scheme_from_name(config: BaseConfig):
     config.settings["version_scheme"] = "pep440"
-    scheme = get_version_scheme(config, "semver")
+    scheme = get_version_scheme(config.settings, "semver")
     assert scheme is SemVer
 
 
 def test_raise_for_unknown_version_scheme(config: BaseConfig):
     with pytest.raises(VersionSchemeUnknown):
-        get_version_scheme(config, "unknown")
+        get_version_scheme(config.settings, "unknown")
 
 
 def test_version_scheme_from_deprecated_config(config: BaseConfig):
     config.settings["version_type"] = "semver"
     with pytest.warns(DeprecationWarning):
-        scheme = get_version_scheme(config)
+        scheme = get_version_scheme(config.settings)
     assert scheme is SemVer
 
 
@@ -42,7 +48,7 @@ def test_version_scheme_from_config_priority(config: BaseConfig):
     config.settings["version_scheme"] = "pep440"
     config.settings["version_type"] = "semver"
     with pytest.warns(DeprecationWarning):
-        scheme = get_version_scheme(config)
+        scheme = get_version_scheme(config.settings)
     assert scheme is Pep440
 
 
@@ -57,4 +63,4 @@ class NotVersionProtocol:
     mocker.patch.object(metadata, "entry_points", return_value=(ep,))
 
     with pytest.warns(match="VersionProtocol"):
-        get_version_scheme(config, "any")
+        get_version_scheme(config.settings, "any")
diff --git a/tests/utils.py b/tests/utils.py
index 08d4414ba3..5e26b2d70a 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,12 +1,25 @@
 from __future__ import annotations
 
+import sys
 import time
 import uuid
 from pathlib import Path
+
+import pytest
 from deprecated import deprecated
 
 from commitizen import cmd, exceptions, git
 
+skip_below_py_3_10 = pytest.mark.skipif(
+    sys.version_info < (3, 10),
+    reason="The output message of argparse is different between Python 3.10 and lower than Python 3.10",
+)
+
+skip_below_py_3_13 = pytest.mark.skipif(
+    sys.version_info < (3, 13),
+    reason="The output message of argparse is different between Python 3.13 and lower than Python 3.13",
+)
+
 
 class FakeCommand:
     def __init__(self, out=None, err=None, return_code=0):
