Full Code of qodo-ai/pr-agent for AI

main aaf8fbe21836 cached
216 files
1.5 MB
345.0k tokens
1226 symbols
1 requests
Download .txt
Showing preview only (1,584K chars total). Download the full file or copy to clipboard to get everything.
Repository: qodo-ai/pr-agent
Branch: main
Commit: aaf8fbe21836
Files: 216
Total size: 1.5 MB

Directory structure:
gitextract_ey74c0jr/

├── .dockerignore
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   ├── config.yml
│   │   ├── feature-request.yml
│   │   └── miscellaneous.yml
│   └── workflows/
│       ├── build-and-test.yaml
│       ├── code_coverage.yaml
│       ├── docs-ci.yaml
│       ├── e2e_tests.yaml
│       ├── pr-agent-review.yaml
│       └── pre-commit.yml
├── .gitignore
├── .pr_agent.toml
├── .pre-commit-config.yaml
├── AGENTS.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile.github_action
├── Dockerfile.github_action_dockerhub
├── LICENSE
├── MANIFEST.in
├── README.md
├── RELEASE_NOTES.md
├── SECURITY.md
├── action.yaml
├── codecov.yml
├── docker/
│   ├── Dockerfile
│   └── Dockerfile.lambda
├── docs/
│   ├── README.md
│   ├── docs/
│   │   ├── .gitbook.yaml
│   │   ├── CNAME
│   │   ├── core-abilities/
│   │   │   ├── compression_strategy.md
│   │   │   ├── dynamic_context.md
│   │   │   ├── fetching_ticket_context.md
│   │   │   ├── index.md
│   │   │   ├── interactivity.md
│   │   │   ├── metadata.md
│   │   │   └── self_reflection.md
│   │   ├── css/
│   │   │   └── custom.css
│   │   ├── faq/
│   │   │   └── index.md
│   │   ├── index.md
│   │   ├── installation/
│   │   │   ├── azure.md
│   │   │   ├── bitbucket.md
│   │   │   ├── gitea.md
│   │   │   ├── github.md
│   │   │   ├── gitlab.md
│   │   │   ├── index.md
│   │   │   ├── locally.md
│   │   │   └── pr_agent.md
│   │   ├── overview/
│   │   │   └── data_privacy.md
│   │   ├── summary.md
│   │   ├── tools/
│   │   │   ├── add_docs.md
│   │   │   ├── ask.md
│   │   │   ├── describe.md
│   │   │   ├── generate_labels.md
│   │   │   ├── help.md
│   │   │   ├── help_docs.md
│   │   │   ├── improve.md
│   │   │   ├── index.md
│   │   │   ├── review.md
│   │   │   ├── similar_issues.md
│   │   │   └── update_changelog.md
│   │   └── usage-guide/
│   │       ├── EXAMPLE_BEST_PRACTICE.md
│   │       ├── additional_configurations.md
│   │       ├── automations_and_usage.md
│   │       ├── changing_a_model.md
│   │       ├── configuration_options.md
│   │       ├── index.md
│   │       ├── introduction.md
│   │       └── mail_notifications.md
│   ├── mkdocs.yml
│   └── overrides/
│       ├── main.html
│       └── partials/
│           ├── footer.html
│           └── integrations/
│               └── analytics/
│                   └── custom.html
├── github_action/
│   └── entrypoint.sh
├── pr_agent/
│   ├── __init__.py
│   ├── agent/
│   │   ├── __init__.py
│   │   └── pr_agent.py
│   ├── algo/
│   │   ├── __init__.py
│   │   ├── ai_handlers/
│   │   │   ├── base_ai_handler.py
│   │   │   ├── langchain_ai_handler.py
│   │   │   ├── litellm_ai_handler.py
│   │   │   ├── litellm_helpers.py
│   │   │   └── openai_ai_handler.py
│   │   ├── cli_args.py
│   │   ├── file_filter.py
│   │   ├── git_patch_processing.py
│   │   ├── language_handler.py
│   │   ├── pr_processing.py
│   │   ├── token_handler.py
│   │   ├── types.py
│   │   └── utils.py
│   ├── cli.py
│   ├── cli_pip.py
│   ├── config_loader.py
│   ├── custom_merge_loader.py
│   ├── git_providers/
│   │   ├── __init__.py
│   │   ├── azuredevops_provider.py
│   │   ├── bitbucket_provider.py
│   │   ├── bitbucket_server_provider.py
│   │   ├── codecommit_client.py
│   │   ├── codecommit_provider.py
│   │   ├── gerrit_provider.py
│   │   ├── git_provider.py
│   │   ├── gitea_provider.py
│   │   ├── github_provider.py
│   │   ├── gitlab_provider.py
│   │   ├── local_git_provider.py
│   │   └── utils.py
│   ├── identity_providers/
│   │   ├── __init__.py
│   │   ├── default_identity_provider.py
│   │   └── identity_provider.py
│   ├── log/
│   │   └── __init__.py
│   ├── secret_providers/
│   │   ├── __init__.py
│   │   ├── aws_secrets_manager_provider.py
│   │   ├── google_cloud_storage_secret_provider.py
│   │   └── secret_provider.py
│   ├── servers/
│   │   ├── __init__.py
│   │   ├── atlassian-connect-qodo-merge.json
│   │   ├── atlassian-connect.json
│   │   ├── azuredevops_server_webhook.py
│   │   ├── bitbucket_app.py
│   │   ├── bitbucket_server_webhook.py
│   │   ├── gerrit_server.py
│   │   ├── gitea_app.py
│   │   ├── github_action_runner.py
│   │   ├── github_app.py
│   │   ├── github_lambda_webhook.py
│   │   ├── github_polling.py
│   │   ├── gitlab_lambda_webhook.py
│   │   ├── gitlab_webhook.py
│   │   ├── gunicorn_config.py
│   │   ├── help.py
│   │   └── utils.py
│   ├── settings/
│   │   ├── .secrets_template.toml
│   │   ├── code_suggestions/
│   │   │   ├── pr_code_suggestions_prompts.toml
│   │   │   ├── pr_code_suggestions_prompts_not_decoupled.toml
│   │   │   └── pr_code_suggestions_reflect_prompts.toml
│   │   ├── configuration.toml
│   │   ├── custom_labels.toml
│   │   ├── generated_code_ignore.toml
│   │   ├── ignore.toml
│   │   ├── language_extensions.toml
│   │   ├── pr_add_docs.toml
│   │   ├── pr_custom_labels.toml
│   │   ├── pr_description_prompts.toml
│   │   ├── pr_evaluate_prompt_response.toml
│   │   ├── pr_help_docs_headings_prompts.toml
│   │   ├── pr_help_docs_prompts.toml
│   │   ├── pr_help_prompts.toml
│   │   ├── pr_information_from_user_prompts.toml
│   │   ├── pr_line_questions_prompts.toml
│   │   ├── pr_questions_prompts.toml
│   │   ├── pr_reviewer_prompts.toml
│   │   └── pr_update_changelog_prompts.toml
│   └── tools/
│       ├── __init__.py
│       ├── pr_add_docs.py
│       ├── pr_code_suggestions.py
│       ├── pr_config.py
│       ├── pr_description.py
│       ├── pr_generate_labels.py
│       ├── pr_help_docs.py
│       ├── pr_help_message.py
│       ├── pr_line_questions.py
│       ├── pr_questions.py
│       ├── pr_reviewer.py
│       ├── pr_similar_issue.py
│       ├── pr_update_changelog.py
│       └── ticket_pr_compliance_check.py
├── pr_compliance_checklist.yaml
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── setup.py
└── tests/
    ├── e2e_tests/
    │   ├── e2e_utils.py
    │   ├── langchain_ai_handler.py
    │   ├── test_bitbucket_app.py
    │   ├── test_gitea_app.py
    │   ├── test_github_app.py
    │   └── test_gitlab_webhook.py
    ├── health_test/
    │   └── main.py
    └── unittest/
        ├── test_add_docs_trigger.py
        ├── test_aws_secrets_manager_provider.py
        ├── test_azure_devops_comment.py
        ├── test_azure_devops_parsing.py
        ├── test_bitbucket_provider.py
        ├── test_clip_tokens.py
        ├── test_codecommit_client.py
        ├── test_codecommit_provider.py
        ├── test_config_loader_secrets.py
        ├── test_convert_to_markdown.py
        ├── test_delete_hunks.py
        ├── test_extend_patch.py
        ├── test_extract_issue_from_branch.py
        ├── test_fetching_sub_issues.py
        ├── test_file_filter.py
        ├── test_find_line_number_of_relevant_line_in_file.py
        ├── test_fix_json_escape_char.py
        ├── test_fix_output.py
        ├── test_fresh_vars_functionality.py
        ├── test_get_max_tokens.py
        ├── test_gitea_provider.py
        ├── test_github_action_output.py
        ├── test_gitlab_provider.py
        ├── test_gitlab_webhook_port.py
        ├── test_handle_patch_deletions.py
        ├── test_ignore_repositories.py
        ├── test_language_handler.py
        ├── test_litellm_reasoning_effort.py
        ├── test_load_yaml.py
        ├── test_parse_code_suggestion.py
        ├── test_pr_update_changelog.py
        ├── test_secret_provider_factory.py
        ├── test_similar_issue_non_github.py
        └── test_try_fix_yaml.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
.venv/
venv/
pr_agent/settings/.secrets.toml
pics/
pr_agent.egg-info/
build/


================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
name: "\U0001FAB2 Bug Report"
description: Submit a bug report
labels: ["bug"]
body:

  - type: dropdown
    id: information-git-provider
    attributes:
      label: Git provider
      description: 'The problem arises when using:'
      options:
        - "Github Cloud"
        - "Github Enterprise"
        - "Gitlab"
        - "Bitbucket Cloud"
        - "Bitbucket Server"
        - "Azure"
        - "Other"
    validations:
      required: true

  - type: textarea
    id: system-info
    attributes:
      label: System Info
      description: Please share your system info with us.
      placeholder: model used, deployment type (action/app/cli/...), etc...
    validations:
      required: true

  - type: textarea
    id: bug-details
    attributes:
      label: Bug details
      description: Please describe the problem.
      placeholder: Describe the problem
    validations:
      required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
version: 0.1
contact_links:
  - name: Discussions
    url: https://github.com/qodo-ai/pr-agent/discussions
    about: GitHub Discussions

  - name: Discord community
    url: https://discord.com/channels/1057273017547378788/1126104260430528613
    about: Join our discord community


================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
name: "\U0001F4A1 Feature request"
description: Submit a proposal/request for a new PR-Agent feature
labels: ["feature"]
body:
  - type: textarea
    id: feature-request
    validations:
      required: true
    attributes:
      label: Feature request
      description: |
        Description of the feature proposal.

  - type: textarea
    id: motivation
    validations:
      required: true
    attributes:
      label: Motivation
      description: |
        Outline the motivation for the proposal.


================================================
FILE: .github/ISSUE_TEMPLATE/miscellaneous.yml
================================================
name: "❔ General Issue"
description: Submit a general issue
labels: ["general"]
body:

  - type: dropdown
    id: information-git-provider
    attributes:
      label: Git provider (optional)
      description: 'Git Provider:'
      options:
        - "Github Cloud"
        - "Github Enterprise"
        - "Gitlab"
        - "Bitbucket Cloud"
        - "Bitbucket Server"
        - "Azure"
        - "Other"

  - type: textarea
    id: system-info
    attributes:
      label: System Info (optional)
      description: Please share your system info with us.
      placeholder: model used, deployment type (action/app/cli/...), etc...
    validations:
      required: false

  - type: textarea
    id: issues-details
    attributes:
      label: Issues details
      description: Please share the issues details.
      placeholder: Describe the issue
    validations:
      required: true


================================================
FILE: .github/workflows/build-and-test.yaml
================================================
name: Build-and-test

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - id: checkout
        uses: actions/checkout@v6

      - id: dockerx
        name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3

      - id: build
        name: Build dev docker
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./docker/Dockerfile
          push: false
          load: true
          tags: codiumai/pr-agent:test
          cache-from: type=gha,scope=dev
          cache-to: type=gha,mode=max,scope=dev
          target: test

      - id: test
        name: Test dev docker
        run: |
          docker run --rm codiumai/pr-agent:test pytest -v tests/unittest


================================================
FILE: .github/workflows/code_coverage.yaml
================================================
name: Code-coverage

on:
  workflow_dispatch:
  # push:
  #   branches:
  #     - main
  pull_request:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - id: checkout
        uses: actions/checkout@v6

      - id: dockerx
        name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3

      - id: build
        name: Build dev docker
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./docker/Dockerfile
          push: false
          load: true
          tags: codiumai/pr-agent:test
          cache-from: type=gha,scope=dev
          cache-to: type=gha,mode=max,scope=dev
          target: test

      - id: code_cov
        name: Test dev docker
        run: |
          docker run --name test_container codiumai/pr-agent:test  pytest  tests/unittest --cov=pr_agent --cov-report term --cov-report xml:coverage.xml
          docker cp test_container:/app/coverage.xml coverage.xml
          docker rm test_container

      - name: Validate coverage report
        run: |
          if [ ! -f coverage.xml ]; then
            echo "Coverage report not found"
            exit 1
          fi
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}


================================================
FILE: .github/workflows/docs-ci.yaml
================================================
name: docs-ci
on:
  push:
    branches:
      - main
      - add-docs-portal
    paths:
      - docs/**
permissions:
  contents: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Configure Git Credentials
        run: |
          git config user.name github-actions[bot]
          git config user.email 41898282+github-actions[bot]@users.noreply.github.com
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
      - uses: actions/cache@v4
        with:
          key: mkdocs-material-${{ env.cache_id }}
          path: .cache
          restore-keys: |
            mkdocs-material-
      - run: pip install mkdocs-material
      - run: pip install "mkdocs-material[imaging]"
      - run: pip install mkdocs-glightbox
      - run: mkdocs gh-deploy -f docs/mkdocs.yml --force


================================================
FILE: .github/workflows/e2e_tests.yaml
================================================
name: PR-Agent E2E tests

on:
  workflow_dispatch:
#  schedule:
#    - cron: '0 0 * * *' # This cron expression runs the workflow every night at midnight UTC

jobs:
  pr_agent_job:
    runs-on: ubuntu-latest
    name: PR-Agent E2E GitHub App Test
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3

      - id: build
        name: Build dev docker
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ./docker/Dockerfile
          push: false
          load: true
          tags: codiumai/pr-agent:test
          cache-from: type=gha,scope=dev
          cache-to: type=gha,mode=max,scope=dev
          target: test

      - id: test1
        name: E2E test github app
        run: |
          docker run -e GITHUB.USER_TOKEN=${{ secrets.TOKEN_GITHUB }} --rm codiumai/pr-agent:test pytest -v tests/e2e_tests/test_github_app.py

      - id: test2
        name: E2E gitlab webhook
        run: |
          docker run -e gitlab.PERSONAL_ACCESS_TOKEN=${{ secrets.TOKEN_GITLAB }} --rm codiumai/pr-agent:test pytest -v tests/e2e_tests/test_gitlab_webhook.py

      - id: test3
        name: E2E bitbucket app
        run: |
          docker run -e BITBUCKET.USERNAME=${{ secrets.BITBUCKET_USERNAME }}  -e BITBUCKET.PASSWORD=${{ secrets.BITBUCKET_PASSWORD }} --rm codiumai/pr-agent:test pytest -v tests/e2e_tests/test_bitbucket_app.py


================================================
FILE: .github/workflows/pr-agent-review.yaml
================================================
# This workflow enables developers to call PR-Agents `/[actions]` in PR's comments and upon PR creation.
# Learn more at https://www.codium.ai/pr-agent/
# This is v0.2 of this workflow file

name: PR-Agent

on:
# pull_request:
# issue_comment:
  workflow_dispatch:

permissions:
  issues: write
  pull-requests: write

jobs:
  pr_agent_job:
    runs-on: ubuntu-latest
    name: Run pr agent on every pull request
    steps:
      - name: PR Agent action step
        id: pragent
        uses: Codium-ai/pr-agent@main
        env:
          OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
          OPENAI_ORG: ${{ secrets.OPENAI_ORG }} # optional
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PINECONE.API_KEY: ${{ secrets.PINECONE_API_KEY }}
          PINECONE.ENVIRONMENT: ${{ secrets.PINECONE_ENVIRONMENT }}
          GITHUB_ACTION_CONFIG.AUTO_DESCRIBE: true
          GITHUB_ACTION_CONFIG.AUTO_REVIEW: true
          GITHUB_ACTION_CONFIG.AUTO_IMPROVE: true


================================================
FILE: .github/workflows/pre-commit.yml
================================================
# disabled. We might run it manually if needed.
name: pre-commit

on:
  workflow_dispatch:
#  pull_request:
#  push:
#    branches: [main]

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v5
      # SEE https://github.com/pre-commit/action
      - uses: pre-commit/action@v3.0.1


================================================
FILE: .gitignore
================================================
.idea/
.lsp/
.vscode/
.env
.venv/
venv/
pr_agent/settings/.secrets.toml
__pycache__
dist/
*.egg-info/
build/
.DS_Store
docs/.cache/
.qodo
poetry.lock


================================================
FILE: .pr_agent.toml
================================================
[pr_reviewer]
enable_review_labels_effort = true
enable_auto_approval = true

[github_app]
pr_commands = [
    "/describe --pr_description.publish_description_as_comment=true",
    "/improve",
    "/agentic_review"
]

handle_push_trigger = true
push_commands = [
    "/improve",
    "/agentic_review"
]

[review_agent]
enabled = true
publish_output = true


================================================
FILE: .pre-commit-config.yaml
================================================
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks

default_language_version:
  python: python3

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: check-added-large-files
      - id: check-toml
      - id: check-yaml
      - id: end-of-file-fixer
      - id: trailing-whitespace
  # - repo: https://github.com/rhysd/actionlint
  #   rev: v1.7.3
  #   hooks:
  #     - id: actionlint
  - repo: https://github.com/pycqa/isort
    # rev must match what's in dev-requirements.txt
    rev: 5.13.2
    hooks:
      - id: isort
  # - repo: https://github.com/PyCQA/bandit
  #   rev: 1.7.10
  #   hooks:
  #     - id: bandit
  #       args: [
  #         "-c", "pyproject.toml",
  #       ]
  # - repo: https://github.com/astral-sh/ruff-pre-commit
  #   rev: v0.7.1
  #   hooks:
  #     - id: ruff
  #       args:
  #         - --fix
  #     - id: ruff-format
  # -   repo: https://github.com/PyCQA/autoflake
  #     rev: v2.3.1
  #     hooks:
  #     -   id: autoflake
  #         args:
  #           - --in-place
  #           - --remove-all-unused-imports
  #           - --remove-unused-variables


================================================
FILE: AGENTS.md
================================================
# Repository Guidelines

## Dos and Don’ts

- **Do** match the interpreter requirement declared in `pyproject.toml` (Python ≥ 3.12) and install `requirements.txt` plus `requirements-dev.txt` before running tools.
- **Do** run tests with `PYTHONPATH=.` set to keep imports functional (for example `PYTHONPATH=. ./.venv/bin/pytest tests/unittest/test_fix_json_escape_char.py -q`).
- **Do** adjust configuration through `.pr_agent.toml` or files under `pr_agent/settings/` instead of hard-coding values.
- **Don’t** commit secrets or access tokens; rely on environment variables as shown in the health and e2e tests.
- **Don’t** reformat or reorder files globally; match existing 120-character lines, import ordering, and docstring style.
- **Don’t** delete or rename configuration, prompt, or workflow files without maintainer approval.

## Project Structure and Module Organization

PR-Agent automates AI-assisted reviews for pull requests across multiple git providers.

- `pr_agent/agent/` orchestrates commands (`review`, `describe`, `improve`, etc.) via `pr_agent/agent/pr_agent.py`.
- `pr_agent/tools/` implements individual capabilities such as reviewers, code suggestions, docs updates, and label generation.
- `pr_agent/git_providers/` and `pr_agent/identity_providers/` handle integrations with GitHub, GitLab, Bitbucket, Azure DevOps, and secrets.
- `pr_agent/settings/` stores Dynaconf defaults (prompts, configuration templates, ignore lists) respected at runtime; `.pr_agent.toml` overrides repository-level behavior.
- `tests/unittest/`, `tests/e2e_tests/`, and `tests/health_test/` contain pytest-based unit, end-to-end, and smoke checks.
- `docs/` holds the MkDocs site (`docs/mkdocs.yml` plus content under `docs/docs/`); overrides live in `docs/overrides/`.
- `.github/workflows/` defines CI pipelines for unit tests, coverage, docs deployment, pre-commit, and PR-agent self-review.
- `docker/` and the root Dockerfiles provide build targets for services (`github_app`, `gitlab_webhook`, etc.) and the `test` stage used in CI.

## Build, Test, and Development Commands

- Create or activate a virtual environment, then install runtime dependencies with `pip install -r requirements.txt`; add development tooling via `pip install -r requirements-dev.txt`.
- Run a single unit test (verified): `PYTHONPATH=. ./.venv/bin/pytest tests/unittest/test_fix_json_escape_char.py -q`.
- Run the full unit suite: `PYTHONPATH=. ./.venv/bin/pytest tests/unittest -v`.
- Execute the CLI locally once dependencies and API keys are available: `python -m pr_agent.cli --pr_url <https://host/org/repo/pull/123> review`.
- Build the test Docker target mirror of CI when containerizing: `docker build -f docker/Dockerfile --target test .` (loads dev dependencies and copies `tests/`).
- Generate and deploy documentation with MkDocs after installing the same extras as CI (`mkdocs-material`, `mkdocs-glightbox`): `mkdocs serve -f docs/mkdocs.yml` for previews and `mkdocs gh-deploy -f docs/mkdocs.yml` for publication.

## Coding Style and Naming Conventions

- Python sources follow the Ruff configuration in `pyproject.toml` (`line-length = 120`, Pyflakes plus `flake8-bugbear` checks, and isort ordering). Keep imports grouped as isort would produce and prefer double quotes for strings.
- Pre-commit (`.pre-commit-config.yaml`) enforces trailing whitespace cleanup, final newlines, TOML/YAML validity, and optional `isort`; run `pre-commit run --all-files` before submitting patches if installed.
- Match existing docstring and comment style—concise English comments using imperative phrasing only where necessary.
- Configuration files in `pr_agent/settings/` are TOML; preserve formatting, section order, and comments when editing prompts or defaults.
- Markdown in `docs/` uses MkDocs conventions (YAML front matter absent; rely on heading hierarchy already in place).

## Testing Guidelines

- Pytest is the standard framework; keep new tests under the closest matching directory (`tests/unittest/` for unit logic, `tests/e2e_tests/` for integration flows, `tests/health_test/` for smoke coverage).
- Prefer focused unit tests that isolate helpers in `pr_agent/algo/`, `pr_agent/tools/`, or provider adapters; use parameterized tests where existing files already do so.
- Set `PYTHONPATH=.` when invoking pytest from the repository root to avoid import errors.
- End-to-end suites require provider tokens (`TOKEN_GITHUB`, `TOKEN_GITLAB`, `BITBUCKET_USERNAME`, `BITBUCKET_PASSWORD`) and may take several minutes; run them only when credentials and sandboxes are configured.
- The health test (`tests/health_test/main.py`) exercises `/describe`, `/review`, and `/improve`; update expected artifacts if prompts change meaningfully.

## Commit and Pull Request Guidelines

- Follow `CONTRIBUTING.md`: keep changes focused, add or update tests, and use Conventional Commit-style messages (e.g., `fix: handle missing repo settings gracefully`).
- Target branch names follow `feature/<name>` or `fix/<issue>` patterns for substantial work.
- Reference related issues and update README or docs when user-facing behavior shifts.
- Ensure CI workflows (`build-and-test`, `code-coverage`, `docs-ci`) succeed locally or in draft PRs before requesting review; reproduce failures with the documented commands above.
- Include screenshots or terminal captures when modifying user-visible output or documentation previews.

## Safety and Permissions

- Ask for confirmation before adding dependencies, renaming files, or changing workflow definitions; many consumers embed these paths and prompts.
- Stay within existing formatting and directory conventions—avoid mass refactors, re-sorting of prompts, or reformatting Markdown beyond the touched sections.
- You may read files, list directories, and run targeted lint/test/doc commands without prior approval; coordinate before launching full Docker builds or e2e suites that rely on external credentials.
- Never commit cached credentials, API keys, or coverage artifacts; CI already handles secrets through GitHub Actions.
- Treat prompt and configuration files as single sources of truth—update mirrors (`.pr_agent.toml`, `pr_agent/settings/*.toml`) together when behavior changes.

## Security and Configuration Tips

- Secrets should be supplied through environment variables (see usages in `tests/e2e_tests/test_github_app.py` and `tests/health_test/main.py`); do not persist them in code or configuration files.
- Adjust runtime behavior by overriding keys in `.pr_agent.toml` or by supplying repository-specific Dynaconf files; keep overrides minimal and documented inside the PR description.
- Review `SECURITY.md` before disclosing vulnerabilities and follow its contact instructions for responsible reporting.


================================================
FILE: CHANGELOG.md
================================================
## 2023-08-03

### Optimized

- Optimized PR diff processing by introducing caching for diff files, reducing the number of API calls.
- Refactored `load_large_diff` function to generate a patch only when necessary.
- Fixed a bug in the GitLab provider where the new file was not retrieved correctly.

## 2023-08-02

### Enhanced

- Updated several tools in the `pr_agent` package to use commit messages in their functionality.
- Commit messages are now retrieved and stored in the `vars` dictionary for each tool.
- Added a section to display the commit messages in the prompts of various tools.

## 2023-08-01

### Enhanced

- Introduced the ability to retrieve commit messages from pull requests across different git providers.
- Implemented commit messages retrieval for GitHub and GitLab providers.
- Updated the PR description template to include a section for commit messages if they exist.
- Added support for repository-specific configuration files (.pr_agent.yaml) for the PR Agent.
- Implemented this feature for both GitHub and GitLab providers.
- Added a new configuration option 'use_repo_settings_file' to enable or disable the use of a repo-specific settings file.

## 2023-07-30

### Enhanced

- Added the ability to modify any configuration parameter from 'configuration.toml' on-the-fly.
- Updated the command line interface and bot commands to accept configuration changes as arguments.
- Improved the PR agent to handle additional arguments for each action.

## 2023-07-28

### Improved

- Enhanced error handling and logging in the GitLab provider.
- Improved handling of inline comments and code suggestions in GitLab.
- Fixed a bug where an additional unneeded line was added to code suggestions in GitLab.

## 2023-07-26

### Added

- New feature for updating the CHANGELOG.md based on the contents of a PR.
- Added support for this feature for the Github provider.
- New configuration settings and prompts for the changelog update feature.


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Code of Conduct

As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.

We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
  without explicit permission
* Other unethical or unprofessional conduct

Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.

By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.

This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at dana.f@qodo.ai . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.

This Code of Conduct is adapted from the
[Contributor Covenant](https://contributor-covenant.org), version 1.3.0, available at
[contributor-covenant.org/version/1/3/0/](https://contributor-covenant.org/version/1/3/0/)


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to PR-Agent

Thank you for your interest in contributing to the PR-Agent project!

## Getting Started

1. Fork the repository and clone your fork
2. Install Python 3.10 or higher
3. Install dependencies (`requirements.txt` and `requirements-dev.txt`)
4. Create a new branch for your contribution:
   - For new features: `git checkout -b feature/your-feature-name`
   - For bug fixes: `git checkout -b fix/issue-description`
5. Make your changes
6. Write or update tests as needed
7. Run tests locally to ensure everything passes
8. Commit your changes using conventional commit messages
9. Push to your fork and submit a pull request

## Development Guidelines

- Keep pull requests focused on a single feature or fix
- Follow the existing code style and formatting conventions
- Add unit tests for any new functionality using pytest
- Ensure test coverage for your changes
- Update documentation as needed

## Pull Request Process

1. Ensure your PR includes a clear description of the changes
2. Link any related issues
3. Update the README.md if needed
4. Wait for review from maintainers

## Questions or Need Help?

- Join our [Discord community](https://discord.com/channels/1057273017547378788/1126104260430528613) for questions and discussions
- Check the [documentation](https://qodo-merge-docs.qodo.ai/) for detailed information
- Report bugs or request features through [GitHub Issues](https://github.com/qodo-ai/pr-agent/issues)


================================================
FILE: Dockerfile.github_action
================================================
FROM python:3.12.10-slim AS base

RUN apt-get update && apt-get install --no-install-recommends -y git curl && apt-get clean && rm -rf /var/lib/apt/lists/*

WORKDIR /app
ADD pyproject.toml .
ADD requirements.txt .
RUN pip install --no-cache-dir . && rm pyproject.toml requirements.txt
ENV PYTHONPATH=/app
ADD docs docs
ADD pr_agent pr_agent
ADD github_action/entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]


================================================
FILE: Dockerfile.github_action_dockerhub
================================================
FROM codiumai/pr-agent:github_action


================================================
FILE: LICENSE
================================================
                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.


================================================
FILE: MANIFEST.in
================================================
recursive-include pr_agent *.toml
recursive-exclude pr_agent *.secrets.toml


================================================
FILE: README.md
================================================
<a href="https://github.com/Codium-ai/pr-agent/commits/main">
<img alt="GitHub" src="https://img.shields.io/github/last-commit/Codium-ai/pr-agent/main?style=for-the-badge" height="20">
</a>

<br />

# 🚀 PR Agent - The Original Open-Source PR Reviewer.

 This repository contains the open-source PR Agent Project. 
 It is not the Qodo free tier.
 
Try the free version on our website.

👉[Get Started Now](www.qodo.ai/get-started/)

PR-Agent is an open-source, AI-powered code review agent and a community-maintained legacy project of Qodo. It is distinct from Qodo’s primary AI code review offering, which provides a feature-rich, context-aware experience. Qodo now offers a free tier that integrates seamlessly with GitHub, GitLab, Bitbucket, and Azure DevOps for high-quality automated reviews.

## Table of Contents

- [Getting Started](#getting-started)
- [Why Use PR-Agent?](#why-use-pr-agent)
- [Features](#features)
- [See It in Action](#see-it-in-action)
- [Try It Now](#try-it-now)
- [How It Works](#how-it-works)
- [Data Privacy](#data-privacy)
- [Contributing](#contributing)

## Getting Started

### 🚀 Quick Start for PR-Agent

#### 1. Try it Instantly (No Setup)
Test PR-Agent on any public GitHub repository by commenting `@CodiumAI-Agent /improve`

#### 2. GitHub Action (Recommended)
Add automated PR reviews to your repository with a simple workflow file:
```yaml
# .github/workflows/pr-agent.yml
name: PR Agent
on:
  pull_request:
    types: [opened, synchronize]
jobs:
  pr_agent_job:
    runs-on: ubuntu-latest
    steps:
    - name: PR Agent action step
      uses: Codium-ai/pr-agent@main
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
[Full GitHub Action setup guide](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action)

#### 3. CLI Usage (Local Development)
Run PR-Agent locally on your repository:
```bash
pip install pr-agent
export OPENAI_KEY=your_key_here
pr-agent --pr_url https://github.com/owner/repo/pull/123 review
```
[Complete CLI setup guide](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli)

#### 4. Other Platforms
- [GitLab webhook setup](https://qodo-merge-docs.qodo.ai/installation/gitlab/)
- [BitBucket app installation](https://qodo-merge-docs.qodo.ai/installation/bitbucket/)
- [Azure DevOps setup](https://qodo-merge-docs.qodo.ai/installation/azure/)

[//]: # (## News and Updates)

[//]: # ()
[//]: # (## Aug 8, 2025)

[//]: # ()
[//]: # ()
[//]: # ()
[//]: # (## Jul 17, 2025)

[//]: # ()
[//]: # (Introducing `/compliance`, a new Qodo Merge 💎 tool that runs comprehensive checks for security, ticket requirements, codebase duplication, and custom organizational rules. )

[//]: # ()
[//]: # (<img width="384" alt="compliance-image" src="https://codium.ai/images/pr_agent/compliance_partial.png"/>)

[//]: # ()
[//]: # (Read more about it [here]&#40;https://qodo-merge-docs.qodo.ai/tools/compliance/&#41;)

[//]: # ()
[//]: # ()
[//]: # (## Jul 1, 2025)

[//]: # (You can now receive automatic feedback from Qodo Merge in your local IDE after each commit. Read more about it [here]&#40;https://github.com/qodo-ai/agents/tree/main/agents/qodo-merge-post-commit&#41;.)

[//]: # ()
[//]: # ()
[//]: # (## Jun 21, 2025)

[//]: # ()
[//]: # (v0.30 was [released]&#40;https://github.com/qodo-ai/pr-agent/releases&#41;)

[//]: # ()
[//]: # ()
[//]: # (## Jun 3, 2025)

[//]: # ()
[//]: # (Qodo Merge now offers a simplified free tier 💎.)

[//]: # (Organizations can use Qodo Merge at no cost, with a [monthly limit]&#40;https://qodo-merge-docs.qodo.ai/installation/qodo_merge/#cloud-users&#41; of 75 PR reviews per organization.)

[//]: # ()
[//]: # ()
[//]: # (## Apr 30, 2025)

[//]: # ()
[//]: # (A new feature is now available in the `/improve` tool for Qodo Merge 💎 - Chat on code suggestions.)

[//]: # ()
[//]: # (<img width="512" alt="image" src="https://codium.ai/images/pr_agent/improve_chat_on_code_suggestions_ask.png" />)

[//]: # ()
[//]: # (Read more about it [here]&#40;https://qodo-merge-docs.qodo.ai/tools/improve/#chat-on-code-suggestions&#41;.)

[//]: # ()
[//]: # ()
[//]: # (## Apr 16, 2025)

[//]: # ()
[//]: # (New tool for Qodo Merge 💎 - `/scan_repo_discussions`.)

[//]: # ()
[//]: # (<img width="635" alt="image" src="https://codium.ai/images/pr_agent/scan_repo_discussions_2.png" />)

[//]: # ()
[//]: # (Read more about it [here]&#40;https://qodo-merge-docs.qodo.ai/tools/scan_repo_discussions/&#41;.)

## Why Use PR-Agent?

### 🎯 Built for Real Development Teams

**Fast & Affordable**: Each tool (`/review`, `/improve`, `/ask`) uses a single LLM call (~30 seconds, low cost)

**Handles Any PR Size**: Our [PR Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/#pr-compression-strategy) effectively processes both small and large PRs

**Highly Customizable**: JSON-based prompting allows easy customization of review categories and behavior via [configuration files](pr_agent/settings/configuration.toml)

**Platform Agnostic**: 
- **Git Providers**: GitHub, GitLab, BitBucket, Azure DevOps, Gitea
- **Deployment**: CLI, GitHub Actions, Docker, self-hosted, webhooks
- **AI Models**: OpenAI GPT, Claude, Deepseek, and more

**Open Source Benefits**:
- Full control over your data and infrastructure
- Customize prompts and behavior for your team's needs
- No vendor lock-in
- Community-driven development

## Features

<div style="text-align:left;">

PR-Agent offers comprehensive pull request functionalities integrated with various git providers:

|                                                         |                                                                                        | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea |
|---------------------------------------------------------|----------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:|
| [TOOLS](https://qodo-merge-docs.qodo.ai/tools/)         | [Describe](https://qodo-merge-docs.qodo.ai/tools/describe/)                            |   ✅   |   ✅   |    ✅     |      ✅      |  ✅   |
|                                                         | [Review](https://qodo-merge-docs.qodo.ai/tools/review/)                                |   ✅   |   ✅   |    ✅     |      ✅      |  ✅   |
|                                                         | [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/)                              |   ✅   |   ✅   |    ✅     |      ✅      |  ✅   |
|                                                         | [Ask](https://qodo-merge-docs.qodo.ai/tools/ask/)                                      |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         | ⮑ [Ask on code lines](https://qodo-merge-docs.qodo.ai/tools/ask/#ask-lines)            |   ✅   |   ✅   |           |              |       |
|                                                         | [Help Docs](https://qodo-merge-docs.qodo.ai/tools/help_docs/?h=auto#auto-approval)     |   ✅   |   ✅   |    ✅     |              |       |
|                                                         | [Update CHANGELOG](https://qodo-merge-docs.qodo.ai/tools/update_changelog/)            |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         |                                                                                                                     |        |        |           |              |       |
| [USAGE](https://qodo-merge-docs.qodo.ai/usage-guide/)   | [CLI](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#local-repo-cli)                            |   ✅   |   ✅   |    ✅     |      ✅      |  ✅   |
|                                                         | [App / webhook](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-app)                      |   ✅   |   ✅   |    ✅     |      ✅      |  ✅   |
|                                                         | [Tagging bot](https://github.com/Codium-ai/pr-agent#try-it-now)                                                     |   ✅   |        |           |              |       |
|                                                         | [Actions](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action)                              |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         |                                                                                                                     |        |        |           |              |       |
| [CORE](https://qodo-merge-docs.qodo.ai/core-abilities/) | [Adaptive and token-aware file patch fitting](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/) |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         | [Chat on code suggestions](https://qodo-merge-docs.qodo.ai/core-abilities/chat_on_code_suggestions/)                |   ✅   |  ✅   |           |              |       |
|                                                         | [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/)                                  |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         | [Fetching ticket context](https://qodo-merge-docs.qodo.ai/core-abilities/fetching_ticket_context/)                  |   ✅    |  ✅    |     ✅     |              |       |
|                                                         | [Incremental Update](https://qodo-merge-docs.qodo.ai/core-abilities/incremental_update/)                            |   ✅    |       |           |              |       |
|                                                         | [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/)                                      |   ✅   |  ✅   |           |              |       |
|                                                         | [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/)                               |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         | [Multiple models support](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/)                            |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         | [PR compression](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/)                              |   ✅   |   ✅   |    ✅     |      ✅      |       |
|                                                         | [RAG context enrichment](https://qodo-merge-docs.qodo.ai/core-abilities/rag_context_enrichment/)                    |   ✅    |       |    ✅     |              |       |
|                                                         | [Self reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/)                                  |   ✅   |   ✅   |    ✅     |      ✅      |       |

[//]: # (- Support for additional git providers is described in [here]&#40;./docs/Full_environments.md&#41;)
___

## See It in Action

</div>
<h4><a href="https://github.com/Codium-ai/pr-agent/pull/530">/describe</a></h4>
<div align="center">
<p float="center">
<img src="https://www.codium.ai/images/pr_agent/describe_new_short_main.png" width="512">
</p>
</div>
<hr>

<h4><a href="https://github.com/Codium-ai/pr-agent/pull/732#issuecomment-1975099151">/review</a></h4>
<div align="center">
<p float="center">
<kbd>
<img src="https://www.codium.ai/images/pr_agent/review_new_short_main.png" width="512">
</kbd>
</p>
</div>
<hr>

<h4><a href="https://github.com/Codium-ai/pr-agent/pull/732#issuecomment-1975099159">/improve</a></h4>
<div align="center">
<p float="center">
<kbd>
<img src="https://www.codium.ai/images/pr_agent/improve_new_short_main.png" width="512">
</kbd>
</p>
</div>

<div align="left">

</div>
<hr>

## Try It Now

Try the GPT-5 powered PR-Agent instantly on _your public GitHub repository_. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command.
For example, add a comment to any pull request with the following text:

```
@CodiumAI-Agent /review
```

and the agent will respond with a review of your PR.

Note that this is a promotional bot, suitable only for initial experimentation.
It does not have 'edit' access to your repo, for example, so it cannot update the PR description or add labels (`@CodiumAI-Agent /describe` will publish PR description as a comment). In addition, the bot cannot be used on private repositories, as it does not have access to the files there.


## How It Works

The following diagram illustrates PR-Agent tools and their flow:

![PR-Agent Tools](https://www.qodo.ai/images/pr_agent/diagram-v0.9.png)

## Data Privacy

### Self-hosted PR-Agent

- If you host PR-Agent with your OpenAI API key, it is between you and OpenAI. You can read their API data privacy policy here:
https://openai.com/enterprise-privacy

## Contributing

To contribute to the project, get started by reading our [Contributing Guide](https://github.com/qodo-ai/pr-agent/blob/b09eec265ef7d36c232063f76553efb6b53979ff/CONTRIBUTING.md).


## ❤️ Community

This open-source release remains here as a community contribution from Qodo — the origin of modern AI-powered code collaboration. We’re proud to share it and inspire developers worldwide.

The project now has its first external maintainer, Naor ([@naorpeled](https://github.com/naorpeled)), and is currently in the process of being donated to an open-source foundation.


================================================
FILE: RELEASE_NOTES.md
================================================
## [Version 0.11] - 2023-12-07

- codiumai/pr-agent:0.11
- codiumai/pr-agent:0.11-github_app
- codiumai/pr-agent:0.11-bitbucket-app
- codiumai/pr-agent:0.11-gitlab_webhook
- codiumai/pr-agent:0.11-github_polling
- codiumai/pr-agent:0.11-github_action

### Added::Algo

- New section in `/describe` tool - [PR changes walkthrough](https://github.com/Codium-ai/pr-agent/pull/509)
- Improving PR Agent [prompts](https://github.com/Codium-ai/pr-agent/pull/501)
- Persistent tools (`/review`, `/describe`) now send an [update message](https://github.com/Codium-ai/pr-agent/pull/499) after finishing
- Add Amazon Bedrock [support](https://github.com/Codium-ai/pr-agent/pull/483)

### Fixed

- Update [dependencies](https://github.com/Codium-ai/pr-agent/pull/503) in requirements.txt for Python 3.12

## [Version 0.10] - 2023-11-15

- codiumai/pr-agent:0.10
- codiumai/pr-agent:0.10-github_app
- codiumai/pr-agent:0.10-bitbucket-app
- codiumai/pr-agent:0.10-gitlab_webhook
- codiumai/pr-agent:0.10-github_polling
- codiumai/pr-agent:0.10-github_action

### Added::Algo

- Review tool now works with [persistent comments](https://github.com/Codium-ai/pr-agent/pull/451) by default
- Bitbucket now publishes review suggestions with [code links](https://github.com/Codium-ai/pr-agent/pull/428)
- Enabling to limit [max number of tokens](https://github.com/Codium-ai/pr-agent/pull/437/files)
- Support ['gpt-4-1106-preview'](https://github.com/Codium-ai/pr-agent/pull/437/files) model
- Support for Google's [Vertex AI](https://github.com/Codium-ai/pr-agent/pull/436)
- Implementing [thresholds](https://github.com/Codium-ai/pr-agent/pull/423) for incremental PR reviews
- Decoupled custom labels from [PR type](https://github.com/Codium-ai/pr-agent/pull/431)

### Fixed

- Fixed bug in [parsing quotes](https://github.com/Codium-ai/pr-agent/pull/446) in CLI
- Preserve [user-added labels](https://github.com/Codium-ai/pr-agent/pull/433) in pull requests
- Bug fixes in GitLab and BitBucket

## [Version 0.9] - 2023-10-29

- codiumai/pr-agent:0.9
- codiumai/pr-agent:0.9-github_app
- codiumai/pr-agent:0.9-bitbucket-app
- codiumai/pr-agent:0.9-gitlab_webhook
- codiumai/pr-agent:0.9-github_polling
- codiumai/pr-agent:0.9-github_action

### Added::Algo

- New tool - [generate_labels](https://github.com/Codium-ai/pr-agent/blob/main/docs/GENERATE_CUSTOM_LABELS.md)
- New ability to use [customize labels](https://github.com/Codium-ai/pr-agent/blob/main/docs/GENERATE_CUSTOM_LABELS.md#how-to-enable-custom-labels) on the `review` and `describe` tools.
- New tool - [add_docs](https://github.com/Codium-ai/pr-agent/blob/main/docs/ADD_DOCUMENTATION.md)
- GitHub Action: Can now use a `.pr_agent.toml` file to control configuration parameters (see [Usage Guide](./Usage.md#working-with-github-action)).
- GitHub App: Added ability to trigger tools on [push events](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools-for-new-code-pr-push)
- Support custom domain URLs for Azure devops integration (see [link](https://github.com/Codium-ai/pr-agent/pull/381)).
- PR Description default mode is now in [bullet points](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L35).

### Added::Documentation

Significant documentation updates (see [Installation Guide](https://github.com/Codium-ai/pr-agent/blob/main/INSTALL.md), [Usage Guide](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md), and [Tools Guide](https://github.com/Codium-ai/pr-agent/blob/main/docs/TOOLS_GUIDE.md))

### Fixed

- Fixed support for BitBucket pipeline (see [link](https://github.com/Codium-ai/pr-agent/pull/386))
- Fixed a bug in `review -i` tool
- Added blacklist for specific file extensions in `add_docs` tool (see [link](https://github.com/Codium-ai/pr-agent/pull/385/))

## [Version 0.8] - 2023-09-27

- codiumai/pr-agent:0.8
- codiumai/pr-agent:0.8-github_app
- codiumai/pr-agent:0.8-bitbucket-app
- codiumai/pr-agent:0.8-gitlab_webhook
- codiumai/pr-agent:0.8-github_polling
- codiumai/pr-agent:0.8-github_action

### Added::Algo

- GitHub Action: Can control which tools will run automatically when a new PR is created. (see usage guide: https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-action)
- Code suggestion tool: Will try to avoid an 'add comments' suggestion  (see https://github.com/Codium-ai/pr-agent/pull/327)

### Fixed

- Gitlab: Fixed a bug of improper usage of pr_id

## [Version 0.7] - 2023-09-20

### Docker Tags

- codiumai/pr-agent:0.7
- codiumai/pr-agent:0.7-github_app
- codiumai/pr-agent:0.7-bitbucket-app
- codiumai/pr-agent:0.7-gitlab_webhook
- codiumai/pr-agent:0.7-github_polling
- codiumai/pr-agent:0.7-github_action

### Added::Algo

- New tool /similar_issue - Currently on GitHub app and CLI: indexes the issues in the repo, find the most similar issues to the target issue.
- Describe markers: Empower the /describe tool with a templating capability (see more details in https://github.com/Codium-ai/pr-agent/pull/273).
- New feature in the /review tool - added an estimated effort estimation to the review (https://github.com/Codium-ai/pr-agent/pull/306).

### Added::Infrastructure

- Implementation of a GitLab webhook.
- Implementation of a BitBucket app.

### Fixed

- Protection against no code suggestions generated.
- Resilience to repositories where the languages cannot be automatically detected.


================================================
FILE: SECURITY.md
================================================
# Security Policy

PR-Agent is an open-source tool to help efficiently review and handle pull requests. Qodo Merge is a paid version of PR-Agent, designed for companies and teams that require additional features and capabilities.

This document describes the security policy of PR-Agent. For Qodo Merge's security policy, see [here](https://qodo-merge-docs.qodo.ai/overview/data_privacy/#qodo-merge).

## PR-Agent Self-Hosted Solutions

When using PR-Agent with your OpenAI (or other LLM provider) API key, the security relationship is directly between you and the provider. We do not send your code to Qodo servers.

Types of [self-hosted solutions](https://qodo-merge-docs.qodo.ai/installation):

- Locally
- GitHub integration
- GitLab integration
- BitBucket integration
- Azure DevOps integration

## PR-Agent Supported Versions

This section outlines which versions of PR-Agent are currently supported with security updates.

### Docker Deployment Options

#### Latest Version

For the most recent updates, use our latest Docker image which is automatically built nightly:

```yaml
uses: qodo-ai/pr-agent@main
```

#### Specific Release Version

For a fixed version, you can pin your action to a specific release version. Browse available releases at:
[PR-Agent Releases](https://github.com/qodo-ai/pr-agent/releases)

For example, to github action:

```yaml
steps:
  - name: PR Agent action step
    id: pragent
    uses: docker://codiumai/pr-agent:0.26-github_action
```

#### Enhanced Security with Docker Digest

For maximum security, you can specify the Docker image using its digest:

```yaml
steps:
  - name: PR Agent action step
    id: pragent
    uses: docker://codiumai/pr-agent@sha256:14165e525678ace7d9b51cda8652c2d74abb4e1d76b57c4a6ccaeba84663cc64
```

## Reporting a Vulnerability

We take the security of PR-Agent seriously. If you discover a security vulnerability, please report it immediately to:

Email: security@qodo.ai

Please include a description of the vulnerability, steps to reproduce, and the affected PR-Agent version.


================================================
FILE: action.yaml
================================================
name: 'Codium PR Agent'
description: 'Summarize, review and suggest improvements for pull requests'
branding:
  icon: 'award'
  color: 'green'
runs:
  using: 'docker'
  image: 'Dockerfile.github_action_dockerhub'


================================================
FILE: codecov.yml
================================================
comment: false
coverage:
  status:
    patch: false
    project: false


================================================
FILE: docker/Dockerfile
================================================
FROM python:3.12.10-slim AS base

RUN apt update && apt install --no-install-recommends -y git curl && apt-get clean && rm -rf /var/lib/apt/lists/*

WORKDIR /app
ADD pyproject.toml .
ADD requirements.txt .
ADD docs docs
RUN pip install --no-cache-dir . && rm pyproject.toml requirements.txt
ENV PYTHONPATH=/app

FROM base AS github_app
ADD pr_agent pr_agent
CMD ["python", "-m", "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "pr_agent/servers/gunicorn_config.py", "--forwarded-allow-ips", "*", "pr_agent.servers.github_app:app"]

FROM base AS bitbucket_app
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/bitbucket_app.py"]

FROM base AS bitbucket_server_webhook
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/bitbucket_server_webhook.py"]

FROM base AS github_polling
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/github_polling.py"]

FROM base AS gitlab_webhook
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/gitlab_webhook.py"]

FROM base AS azure_devops_webhook
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/azuredevops_server_webhook.py"]

FROM base AS gitea_app
ADD pr_agent pr_agent
CMD ["python", "-m", "gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-c", "pr_agent/servers/gunicorn_config.py","pr_agent.servers.gitea_app:app"]


FROM base AS test
ADD requirements-dev.txt .
RUN pip install --no-cache-dir -r requirements-dev.txt && rm requirements-dev.txt
ADD pr_agent pr_agent
ADD tests tests

FROM base AS cli
ADD pr_agent pr_agent
ENTRYPOINT ["python", "pr_agent/cli.py"]


================================================
FILE: docker/Dockerfile.lambda
================================================
FROM public.ecr.aws/lambda/python:3.12 AS base

RUN dnf update -y && \
    dnf install -y gcc python3-devel git && \
    dnf clean all

ADD pyproject.toml requirements.txt ./
RUN pip install --no-cache-dir . && rm pyproject.toml
RUN pip install --no-cache-dir mangum==0.17.0
COPY pr_agent/ ${LAMBDA_TASK_ROOT}/pr_agent/

FROM base AS github_lambda
CMD ["pr_agent.servers.github_lambda_webhook.lambda_handler"]

FROM base AS gitlab_lambda
CMD ["pr_agent.servers.gitlab_lambda_webhook.lambda_handler"]

FROM github_lambda


================================================
FILE: docs/README.md
================================================
# [Visit Our Docs Portal](https://qodo-merge-docs.qodo.ai/)


================================================
FILE: docs/docs/.gitbook.yaml
================================================
root: ./

structure:
  readme: ../README.md
  summary: ./summary.md


================================================
FILE: docs/docs/CNAME
================================================
qodo-merge-docs.qodo.ai


================================================
FILE: docs/docs/core-abilities/compression_strategy.md
================================================

`Supported Git Platforms: GitHub, GitLab, Bitbucket`


## Overview

There are two scenarios:

1. The PR is small enough to fit in a single prompt (including system and user prompt)
2. The PR is too large to fit in a single prompt (including system and user prompt)

For both scenarios, we first use the following strategy

#### Repo language prioritization strategy

We prioritize the languages of the repo based on the following criteria:

1. Exclude binary files and non code files (e.g. images, pdfs, etc)
2. Given the main languages used in the repo
3. We sort the PR files by the most common languages in the repo (in descending order):
   * ```[[file.py, file2.py],[file3.js, file4.jsx],[readme.md]]```

### Small PR

In this case, we can fit the entire PR in a single prompt:

1. Exclude binary files and non code files (e.g. images, pdfs, etc)
2. We Expand the surrounding context of each patch to 3 lines above and below the patch

### Large PR

#### Motivation

Pull Requests can be very long and contain a lot of information with varying degree of relevance to the pr-agent.
We want to be able to pack as much information as possible in a single LMM prompt, while keeping the information relevant to the pr-agent.

#### Compression strategy

We prioritize additions over deletions:

* Combine all deleted files into a single list (`deleted files`)
* File patches are a list of hunks, remove all hunks of type deletion-only from the hunks in the file patch

#### Adaptive and token-aware file patch fitting

We use [tiktoken](https://github.com/openai/tiktoken) to tokenize the patches after the modifications described above, and we use the following strategy to fit the patches into the prompt:

1. Within each language we sort the files by the number of tokens in the file (in descending order):
    * ```[[file2.py, file.py],[file4.jsx, file3.js],[readme.md]]```
2. Iterate through the patches in the order described above
3. Add the patches to the prompt until the prompt reaches a certain buffer from the max token length
4. If there are still patches left, add the remaining patches as a list called `other modified files` to the prompt until the prompt reaches the max token length (hard stop), skip the rest of the patches.
5. If we haven't reached the max token length, add the `deleted files` to the prompt until the prompt reaches the max token length (hard stop), skip the rest of the patches.

#### Example

![Core Abilities](https://codium.ai/images/git_patch_logic.png){width=768}


================================================
FILE: docs/docs/core-abilities/dynamic_context.md
================================================

`Supported Git Platforms: GitHub, GitLab, Bitbucket`

PR-Agent uses an **asymmetric and dynamic context strategy** to improve AI analysis of code changes in pull requests.
It provides more context before changes than after, and dynamically adjusts the context based on code structure (e.g., enclosing functions or classes).
This approach balances providing sufficient context for accurate analysis, while avoiding needle-in-the-haystack information overload that could degrade AI performance or exceed token limits.

## Introduction

Pull request code changes are retrieved in a unified diff format, showing three lines of context before and after each modified section, with additions marked by '+' and deletions by '-'.

```diff
@@ -12,5 +12,5 @@ def func1():
 code line that already existed in the file...
 code line that already existed in the file...
 code line that already existed in the file....
-code line that was removed in the PR
+new code line added in the PR
 code line that already existed in the file...
 code line that already existed in the file...
 code line that already existed in the file...

@@ -26,2 +26,4 @@ def func2():
...
```

This unified diff format can be challenging for AI models to interpret accurately, as it provides limited context for understanding the full scope of code changes.
The presentation of code using '+', '-', and ' ' symbols to indicate additions, deletions, and unchanged lines respectively also differs from the standard code formatting typically used to train AI models.

## Challenges of expanding the context window

While expanding the context window is technically feasible, it presents a more fundamental trade-off:

Pros:

- Enhanced context allows the model to better comprehend and localize the code changes, results (potentially) in more precise analysis and suggestions. Without enough context, the model may struggle to understand the code changes and provide relevant feedback.

Cons:

- Excessive context may overwhelm the model with extraneous information, creating a "needle in a haystack" scenario where focusing on the relevant details (the code that actually changed) becomes challenging.
LLM quality is known to degrade when the context gets larger.
Pull requests often encompass multiple changes across many files, potentially spanning hundreds of lines of modified code. This complexity presents a genuine risk of overwhelming the model with excessive context.

- Increased context expands the token count, increasing processing time and cost, and may prevent the model from processing the entire pull request in a single pass.

## Asymmetric and dynamic context

To address these challenges, PR-Agent employs an **asymmetric** and **dynamic** context strategy, providing the model with more focused and relevant context information for each code change.

**Asymmetric:**

We start by recognizing that the context preceding a code change is typically more crucial for understanding the modification than the context following it.
Consequently, PR-Agent implements an asymmetric context policy, decoupling the context window into two distinct segments: one for the code before the change and another for the code after.

By independently adjusting each context window, PR-Agent can supply the model with a more tailored and pertinent context for individual code changes.

**Dynamic:**

We also employ a "dynamic" context strategy.
We start by recognizing that the optimal context for a code change often corresponds to its enclosing code component (e.g., function, class), rather than a fixed number of lines.
Consequently, we dynamically adjust the context window based on the code's structure, ensuring the model receives the most pertinent information for each modification.

To prevent overwhelming the model with excessive context, we impose a limit on the number of lines searched when identifying the enclosing component.
This balance allows for comprehensive understanding while maintaining efficiency and limiting context token usage.

## Appendix - relevant configuration options

```toml
[config]
patch_extension_skip_types =[".md",".txt"]  # Skip files with these extensions when trying to extend the context
allow_dynamic_context=true                  # Allow dynamic context extension
max_extra_lines_before_dynamic_context = 8  # will try to include up to X extra lines before the hunk in the patch, until we reach an enclosing function or class
patch_extra_lines_before = 3                # Number of extra lines (+3 default ones) to include before each hunk in the patch
patch_extra_lines_after = 1                 # Number of extra lines (+3 default ones) to include after each hunk in the patch
```


================================================
FILE: docs/docs/core-abilities/fetching_ticket_context.md
================================================
# Fetching Ticket Context for PRs

`Supported Git Platforms: GitHub, GitLab, Bitbucket`

!!! note "Branch-name issue linking: GitHub only (for now)"
    Extracting issue links from the **branch name** (and the optional `branch_issue_regex` setting) is currently implemented for **GitHub only**. Support for GitLab, Bitbucket, and other platforms is planned for a later release. The GitHub flow was the most relevant to implement first; other providers will follow.

## Overview

PR-Agent streamlines code review workflows by seamlessly connecting with multiple ticket management systems.
This integration enriches the review process by automatically surfacing relevant ticket information and context alongside code changes.

**Ticket systems supported**:

- [GitHub/Gitlab Issues](#githubgitlab-issues-integration)
- [Jira](#jira-integration)

**Ticket data fetched:**

1. Ticket Title
2. Ticket Description
3. Custom Fields (Acceptance criteria)
4. Subtasks (linked tasks)
5. Labels
6. Attached Images/Screenshots

## Affected Tools

Ticket Recognition Requirements:

- The PR description should contain a link to the ticket or if the branch name starts with the ticket id / number.
- For Jira tickets, you should follow the instructions in [Jira Integration](#jira-integration) in order to authenticate with Jira.

### Describe tool

PR-Agent will recognize the ticket and use the ticket content (title, description, labels) to provide additional context for the code changes.
By understanding the reasoning and intent behind modifications, the LLM can offer more insightful and relevant code analysis.

### Review tool

Similarly to the `describe` tool, the `review` tool will use the ticket content to provide additional context for the code changes.

In addition, this feature will evaluate how well a Pull Request (PR) adheres to its original purpose/intent as defined by the associated ticket or issue mentioned in the PR description.
Each ticket will be assigned a label (Compliance/Alignment level), Indicates the degree to which the PR fulfills its original purpose:

- Fully Compliant
- Partially Compliant
- Not Compliant
- PR Code Verified

![Ticket Compliance](https://www.qodo.ai/images/pr_agent/ticket_compliance_review.png){width=768}

A `PR Code Verified` label indicates the PR code meets ticket requirements, but requires additional manual testing beyond the code scope. For example - validating UI display across different environments (Mac, Windows, mobile, etc.).


#### Configuration options

-

    By default, the `review` tool will automatically validate if the PR complies with the referenced ticket.
    If you want to disable this feedback, add the following line to your configuration file:

    ```toml
    [pr_reviewer]
    require_ticket_analysis_review=false
    ```

-

    If you set:
    ```toml
    [pr_reviewer]
    check_pr_additional_content=true
    ```
    (default: `false`)

    the `review` tool will also validate that the PR code doesn't contain any additional content that is not related to the ticket. If it does, the PR will be labeled at best as `PR Code Verified`, and the `review` tool will provide a comment with the additional unrelated content found in the PR code.

## GitHub/Gitlab Issues Integration

PR-Agent will automatically recognize GitHub/Gitlab issues mentioned in the PR description and fetch the issue content.
Examples of valid GitHub/Gitlab issue references:

- `https://github.com/<ORG_NAME>/<REPO_NAME>/issues/<ISSUE_NUMBER>` or `https://gitlab.com/<ORG_NAME>/<REPO_NAME>/-/issues/<ISSUE_NUMBER>`
- `#<ISSUE_NUMBER>`
- `<ORG_NAME>/<REPO_NAME>#<ISSUE_NUMBER>`

Branch names can also be used to link issues, for example:
- `123-fix-bug` (where `123` is the issue number)

This branch-name detection applies **only when the git provider is GitHub**. Support for other platforms is planned for later.

Since PR-Agent is integrated with GitHub, it doesn't require any additional configuration to fetch GitHub issues.

## Jira Integration

We support both Jira Cloud and Jira Server/Data Center.

### Jira Cloud

#### Email/Token Authentication

You can create an API token from your Atlassian account:

1. Log in to https://id.atlassian.com/manage-profile/security/api-tokens.

2. Click Create API token.

3. From the dialog that appears, enter a name for your new token and click Create.

4. Click Copy to clipboard.

![Jira Cloud API Token](https://images.ctfassets.net/zsv3d0ugroxu/1RYvh9lqgeZjjNe5S3Hbfb/155e846a1cb38f30bf17512b6dfd2229/screenshot_NewAPIToken){width=384}

5. In your [configuration file](../usage-guide/configuration_options.md) add the following lines:

```toml
[jira]
jira_api_token = "YOUR_API_TOKEN"
jira_api_email = "YOUR_EMAIL"
```

### Jira Data Center/Server

#### Using Basic Authentication for Jira Data Center/Server

You can use your Jira username and password to authenticate with Jira Data Center/Server.

In your Configuration file/Environment variables/Secrets file, add the following lines:

```toml
jira_api_email = "your_username"
jira_api_token = "your_password"
```

(Note that indeed the 'jira_api_email' field is used for the username, and the 'jira_api_token' field is used for the user password.)

##### Validating Basic authentication via Python script

If you are facing issues retrieving tickets in PR-Agent with Basic auth, you can validate the flow using a Python script.
This following steps will help you check if the basic auth is working correctly, and if you can access the Jira ticket details:

1. run `pip install jira==3.8.0`

2. run the following Python script (after replacing the placeholders with your actual values):

???- example "Script to validate basic auth"

    ```python
    from jira import JIRA
    
    
    if __name__ == "__main__":
        try:
            # Jira server URL
            server = "https://..."
            # Basic auth
            username = "..."
            password = "..."
            # Jira ticket code (e.g. "PROJ-123")
            ticket_id = "..."
    
            print("Initializing JiraServerTicketProvider with JIRA server")
            # Initialize JIRA client
            jira = JIRA(
                server=server,
                basic_auth=(username, password),
                timeout=30
            )
            if jira:
                print(f"JIRA client initialized successfully")
            else:
                print("Error initializing JIRA client")
    
            # Fetch ticket details
            ticket = jira.issue(ticket_id)
            print(f"Ticket title: {ticket.fields.summary}")
    
        except Exception as e:
            print(f"Error fetching JIRA ticket details: {e}")
    ```

#### Using a Personal Access Token (PAT) for Jira Data Center/Server

1. Create a [Personal Access Token (PAT)](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html) in your Jira account
2. In your Configuration file/Environment variables/Secrets file, add the following lines:

```toml
[jira]
jira_base_url = "YOUR_JIRA_BASE_URL" # e.g. https://jira.example.com
jira_api_token = "YOUR_API_TOKEN"
```

##### Validating PAT token via Python script

If you are facing issues retrieving tickets in PR-Agent with PAT token, you can validate the flow using a Python script.
This following steps will help you check if the token is working correctly, and if you can access the Jira ticket details:

1. run `pip install jira==3.8.0`

2. run the following Python script (after replacing the placeholders with your actual values):

??? example- "Script to validate PAT token"

    ```python
    from jira import JIRA
    
    
    if __name__ == "__main__":
        try:
            # Jira server URL
            server = "https://..."
            # Jira PAT token
            token_auth = "..."
            # Jira ticket code (e.g. "PROJ-123")
            ticket_id = "..."
    
            print("Initializing JiraServerTicketProvider with JIRA server")
            # Initialize JIRA client
            jira = JIRA(
                server=server,
                token_auth=token_auth,
                timeout=30
            )
            if jira:
                print(f"JIRA client initialized successfully")
            else:
                print("Error initializing JIRA client")
    
            # Fetch ticket details
            ticket = jira.issue(ticket_id)
            print(f"Ticket title: {ticket.fields.summary}")
    
        except Exception as e:
            print(f"Error fetching JIRA ticket details: {e}")
    ```


### Multi-JIRA Server Configuration

PR-Agent supports connecting to multiple JIRA servers using different authentication methods.

=== "Email/Token (Basic Auth)"

    Configure multiple servers using Email/Token authentication:

    - `jira_servers`: List of JIRA server URLs
    - `jira_api_token`: List of API tokens (for Cloud) or passwords (for Data Center)
    - `jira_api_email`: List of emails (for Cloud) or usernames (for Data Center)
    - `jira_base_url`: Default server for ticket IDs like `PROJ-123`, Each repository can configure (local config file) its own `jira_base_url` to choose which server to use by default.

    **Example Configuration:**
    ```toml
    [jira]
    # Server URLs
    jira_servers = ["https://company.atlassian.net", "https://datacenter.jira.com"]

    # API tokens/passwords
    jira_api_token = ["cloud_api_token_here", "datacenter_password"]

    # Emails/usernames (both required)
    jira_api_email = ["user@company.com", "datacenter_username"]

    # Default server for ticket IDs
    jira_base_url = "https://company.atlassian.net"
    ```

=== "PAT Auth"

    Configure multiple servers using Personal Access Token authentication:

    - `jira_servers`: List of JIRA server URLs
    - `jira_api_token`: List of PAT tokens
    - `jira_api_email`: Not needed (can be omitted or left empty)
    - `jira_base_url`: Default server for ticket IDs like `PROJ-123`, Each repository can configure (local config file) its own `jira_base_url` to choose which server to use by default.

    **Example Configuration:**
    ```toml
    [jira]
    # Server URLs
    jira_servers = ["https://server1.jira.com", "https://server2.jira.com"]

    # PAT tokens only
    jira_api_token = ["pat_token_1", "pat_token_2"]

    # Default server for ticket IDs
    jira_base_url = "https://server1.jira.com"
    ```

    **Mixed Authentication (Email/Token + PAT):**
    ```toml
    [jira]
    jira_servers = ["https://company.atlassian.net", "https://server.jira.com"]
    jira_api_token = ["cloud_api_token", "server_pat_token"]
    jira_api_email = ["user@company.com", ""]  # Empty for PAT
    ```




### How to link a PR to a Jira ticket

To integrate with Jira, you can link your PR to a ticket using either of these methods:

**Method 1: Description Reference:**

Include a ticket reference in your PR description, using either the complete URL format `https://<JIRA_ORG>.atlassian.net/browse/ISSUE-123` or the shortened ticket ID `ISSUE-123` (without prefix or suffix for the shortened ID).

**Method 2: Branch Name Detection:**

Name your branch with the ticket ID as a prefix (e.g., `ISSUE-123-feature-description` or `ISSUE-123/feature-description`).

!!! note "Jira Base URL"
    For shortened ticket IDs or branch detection (method 2 for JIRA cloud), you must configure the Jira base URL in your configuration file under the [jira] section:

    ```toml
    [jira]
    jira_base_url = "https://<JIRA_ORG>.atlassian.net"
    ```
    Where `<JIRA_ORG>` is your Jira organization identifier (e.g., `mycompany` for `https://mycompany.atlassian.net`).


================================================
FILE: docs/docs/core-abilities/index.md
================================================
# Core Abilities

PR-Agent utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include:

- [Compression strategy](./compression_strategy.md)
- [Dynamic context](./dynamic_context.md)
- [Fetching ticket context](./fetching_ticket_context.md)
- [Interactivity](./interactivity.md)
- [Local and global metadata](./metadata.md)
- [Self-reflection](./self_reflection.md)

## Blogs

Here are some additional technical blogs from Qodo, that delve deeper into the core capabilities and features of Large Language Models (LLMs) when applied to coding tasks.
These resources provide more comprehensive insights into leveraging LLMs for software development.

### Code Generation and LLMs

- [Effective AI code suggestions: less is more](https://www.codium.ai/blog/effective-code-suggestions-llms-less-is-more/)
- [State-of-the-art Code Generation with AlphaCodium – From Prompt Engineering to Flow Engineering](https://www.codium.ai/blog/qodoflow-state-of-the-art-code-generation-for-code-contests/)
- [RAG for a Codebase with 10k Repos](https://www.codium.ai/blog/rag-for-large-scale-code-repos/)

### Development Processes

- [Understanding the Challenges and Pain Points of the Pull Request Cycle](https://www.codium.ai/blog/understanding-the-challenges-and-pain-points-of-the-pull-request-cycle/)
- [Introduction to Code Coverage Testing](https://www.codium.ai/blog/introduction-to-code-coverage-testing/)

### Cost Optimization

- [Reduce Your Costs by 30% When Using GPT for Python Code](https://www.codium.ai/blog/reduce-your-costs-by-30-when-using-gpt-3-for-python-code/)


================================================
FILE: docs/docs/core-abilities/interactivity.md
================================================
# Interactivity

`Supported Git Platforms: GitHub, GitLab`

## Overview

PR-Agent transforms static code reviews into interactive experiences by enabling direct actions from pull request (PR) comments.
Developers can immediately trigger actions and apply changes with simple checkbox clicks.

This focused workflow maintains context while dramatically reducing the time between PR creation and final merge.
The approach eliminates manual steps, provides clear visual indicators, and creates immediate feedback loops all within the same interface.

## Key Interactive Features

### 1\. Interactive `/improve` Tool

The [`/improve`](../tools/improve.md) command delivers a comprehensive interactive experience:

- _**Apply this suggestion**_: Clicking this checkbox instantly converts a suggestion into a committable code change. When committed to the PR, changes made to code that was flagged for improvement will be marked with a check mark, allowing developers to easily track and review implemented recommendations.

- _**More**_: Triggers additional suggestions generation while keeping each suggestion focused and relevant as the original set

- _**Update**_: Triggers a re-analysis of the code, providing updated suggestions based on the latest changes

- _**Author self-review**_: Interactive acknowledgment that developers have opened and reviewed collapsed suggestions

### 2\. Interactive `/help` Tool

The [`/help`](../tools/help.md) command not only lists available tools and their descriptions but also enables immediate tool invocation through interactive checkboxes.
When a user checks a tool's checkbox, PR-Agent instantly triggers that tool without requiring additional commands.
This transforms the standard help menu into an interactive launch pad for all PR-Agent capabilities, eliminating context switching by keeping developers within their PR workflow.


================================================
FILE: docs/docs/core-abilities/metadata.md
================================================
# Local and global metadata injection with multi-stage analysis

`Supported Git Platforms: GitHub, GitLab, Bitbucket`

1\.
PR-Agent initially retrieves for each PR the following data:

- PR title and branch name
- PR original description
- Commit messages history
- PR diff patches, in [hunk diff](https://loicpefferkorn.net/2014/02/diff-files-what-are-hunks-and-how-to-extract-them/) format
- The entire content of the files that were modified in the PR

!!! tip "Tip: Organization-level metadata"
    In addition to the inputs above, PR-Agent can incorporate supplementary preferences provided by the user, like [`extra_instructions` and `organization best practices`](../tools/improve.md#extra-instructions-and-best-practices). This information can be used to enhance the PR analysis.

2\.
By default, the first command that PR-Agent executes is [`describe`](../tools/describe.md), which generates three types of outputs:

- PR Type (e.g. bug fix, feature, refactor, etc)
- PR Description - a bullet point summary of the PR
- Changes walkthrough - for each modified file, provide a one-line summary followed by a detailed bullet point list of the changes.

These AI-generated outputs are now considered as part of the PR metadata, and can be used in subsequent commands like `review` and `improve`.
This effectively enables multi-stage chain-of-thought analysis, without doing any additional API calls which will cost time and money.

For example, when generating code suggestions for different files, PR-Agent can inject the AI-generated ["Changes walkthrough"](https://github.com/qodo-ai/pr-agent/pull/1202#issue-2511546839) file summary in the prompt:

```diff
## File: 'src/file1.py'
### AI-generated file summary:
- edited function `func1` that does X
- Removed function `func2` that was not used
- ....

@@ ... @@ def func1():
__new hunk__
11  unchanged code line0
12  unchanged code line1
13 +new code line2 added
14  unchanged code line3
__old hunk__
 unchanged code line0
 unchanged code line1
-old code line2 removed
 unchanged code line3

@@ ... @@ def func2():
__new hunk__
...
__old hunk__
...
```

3\. The entire PR files that were retrieved are also used to expand and enhance the PR context (see [Dynamic Context](./dynamic_context.md)).

4\. All the metadata described above represents several level of cumulative analysis - ranging from hunk level, to file level, to PR level, to organization level.
This comprehensive approach enables PR-Agent AI models to generate more precise and contextually relevant suggestions and feedback.


================================================
FILE: docs/docs/core-abilities/self_reflection.md
================================================
`Supported Git Platforms: GitHub, GitLab, Bitbucket`

PR-Agent implements a **self-reflection** process where the AI model reflects, scores, and re-ranks its own suggestions, eliminating irrelevant or incorrect ones.
This approach improves the quality and relevance of suggestions, saving users time and enhancing their experience.
Configuration options allow users to set a score threshold for further filtering out suggestions.

## Introduction - Efficient Review with Hierarchical Presentation

Given that not all generated code suggestions will be relevant, it is crucial to enable users to review them in a fast and efficient way, allowing quick identification and filtering of non-applicable ones.

To achieve this goal, PR-Agent offers a dedicated hierarchical structure when presenting suggestions to users:

- A "category" section groups suggestions by their category, allowing users to quickly dismiss irrelevant suggestions.
- Each suggestion is first described by a one-line summary, which can be expanded to a full description by clicking on a collapsible.
- Upon expanding a suggestion, the user receives a more comprehensive description, and a code snippet demonstrating the recommendation.

!!! note "Fast Review"
    This hierarchical structure is designed to facilitate rapid review of each suggestion, with users spending an average of ~5-10 seconds per item.

## Self-reflection and Re-ranking

The AI model is initially tasked with generating suggestions, and outputting them in order of importance.
However, in practice we observe that models often struggle to simultaneously generate high-quality code suggestions and rank them well in a single pass.
Furthermore, the initial set of generated suggestions sometimes contains easily identifiable errors.

To address these issues, we implemented a "self-reflection" process that refines suggestion ranking and eliminates irrelevant or incorrect proposals.
This process consists of the following steps:

1. Presenting the generated suggestions to the model in a follow-up call.
2. Instructing the model to score each suggestion on a scale of 0-10 and provide a rationale for the assigned score.
3. Utilizing these scores to re-rank the suggestions and filter out incorrect ones (with a score of 0).
4. Optionally, filtering out all suggestions below a user-defined score threshold.

Note that presenting all generated suggestions simultaneously provides the model with a comprehensive context, enabling it to make more informed decisions compared to evaluating each suggestion individually.

To conclude, the self-reflection process enables PR-Agent to prioritize suggestions based on their importance, eliminate inaccurate or irrelevant proposals, and optionally exclude suggestions that fall below a specified threshold of significance.
This results in a more refined and valuable set of suggestions for the user, saving time and improving the overall experience.

## Example Results

![self_reflection](https://codium.ai/images/pr_agent/self_reflection1.png){width=768}
![self_reflection](https://codium.ai/images/pr_agent/self_reflection2.png){width=768}

## Appendix - Relevant Configuration Options

```toml
[pr_code_suggestions]
suggestions_score_threshold = 0 # Filter out suggestions with a score below this threshold (0-10)
```


================================================
FILE: docs/docs/css/custom.css
================================================
/* Neutral color scheme - ready for future branding */
:root {
    --md-primary-fg-color: #0f172a;
    --md-accent-fg-color: #1d4ed8;
    --md-typeset-a-color: #1e40af;
}

[data-md-color-scheme="slate"] {
    --md-primary-fg-color: #0b1220;
    --md-accent-fg-color: #38bdf8;
    --md-typeset-a-color: #7dd3fc;
    --md-default-bg-color: #0b1220;
    --md-default-fg-color: #e5e7eb;
    --md-default-fg-color--light: rgba(229, 231, 235, 0.7);
    --md-default-fg-color--lighter: rgba(229, 231, 235, 0.5);
    --md-default-fg-color--lightest: rgba(229, 231, 235, 0.3);
    --md-code-bg-color: #0f172a;
}

.md-nav--primary {
    .md-nav__link {
    font-size: 18px;
    }
}

.md-nav--primary {
    position: relative;
}

.md-nav--primary::before {
    content: "";
    position: absolute;
    top: 0;
    right: 10px;
    width: 2px;
    height: 100%;
    background-color: #e5e7eb;
}

[data-md-color-scheme="slate"] .md-nav--primary::before {
    background-color: #1f2937;
}

[data-md-color-scheme="slate"] .md-header {
    background-color: #0d1b36;
}

[data-md-color-scheme="slate"] .md-tabs {
    background-color: #0b1220;
    border-top: 1px solid rgba(148, 163, 184, 0.25);
}

[data-md-color-scheme="slate"] .md-tabs__link {
    color: #e2e8f0;
}

[data-md-color-scheme="slate"] .md-tabs__link--active,
[data-md-color-scheme="slate"] .md-tabs__link:hover {
    color: #ffffff;
    text-decoration: underline;
    text-underline-offset: 0.25rem;
}

[data-md-color-scheme="slate"] .md-search__form {
    background-color: #0f172a;
    border: 1px solid rgba(148, 163, 184, 0.4);
}

[data-md-color-scheme="slate"] .md-search__input {
    color: #e2e8f0;
}

[data-md-color-scheme="slate"] .md-search__input::placeholder {
    color: rgba(226, 232, 240, 0.7);
}

[data-md-color-scheme="slate"] .md-search__icon {
    color: rgba(226, 232, 240, 0.85);
}

.md-tabs__link  {
    font-size: 18px;
}

.md-header__title {
    font-size: 20px;
    margin-left: 12px !important;
}

.md-header__button.md-logo,
.md-nav__title .md-logo {
    display: none;
}

.md-content img {
    border-width: 1px;
    border-style: solid;
    border-color: rgba(15, 23, 42, 0.2);
    outline-width: 1px;
    outline-style: solid;
    outline-color: rgba(15, 23, 42, 0.25);
}

[data-md-color-scheme="slate"] .md-content img {
    border-color: rgba(226, 232, 240, 0.2);
    outline-color: rgba(226, 232, 240, 0.3);
}

.md-banner {
    background-color: #1d4ed8;
}

[data-md-color-scheme="slate"] .md-banner {
    background-color: #2563eb;
}

.md-banner .md-typeset a,
.md-banner .md-typeset a:hover,
.md-banner .md-typeset a:focus {
    color: currentColor;
    text-decoration: underline;
}


================================================
FILE: docs/docs/faq/index.md
================================================
# FAQ

??? note "Q: Can PR-Agent serve as a substitute for a human reviewer?"
    #### Answer:<span style="display:none;">1</span>

    PR-Agent is designed to assist, not replace, human reviewers.

    Reviewing PRs is a tedious and time-consuming task often seen as a "chore". In addition, the longer the PR – the shorter the relative feedback, since long PRs can overwhelm reviewers, both in terms of technical difficulty, and the actual review time.
    PR-Agent aims to address these pain points, and to assist and empower both the PR author and reviewer.

    However, PR-Agent has built-in safeguards to ensure the developer remains in the driver's seat. For example:

    1. Preserves user's original PR header
    2. Places user's description above the AI-generated PR description
    3. Won't approve PRs; approval remains reviewer's responsibility
    4. The code suggestions are optional, and aim to:
        - Encourage self-review and self-reflection
        - Highlight potential bugs or oversights
        - Enhance code quality and promote best practices

    Read more about this issue in our [blog](https://www.qodo.ai/blog/understanding-the-challenges-and-pain-points-of-the-pull-request-cycle/)

___

??? note "Q: I received an incorrect or irrelevant suggestion. Why?"

    #### Answer:<span style="display:none;">2</span>

    - Modern AI models, like Claude Sonnet and GPT-5, are improving rapidly but remain imperfect. Users should critically evaluate all suggestions rather than accepting them automatically.
    - AI errors are rare, but possible. A main value from reviewing the code suggestions lies in their high probability of catching **mistakes or bugs made by the PR author**. We believe it's worth spending 30-60 seconds reviewing suggestions, even if some aren't relevant, as this practice can enhance code quality and prevent bugs in production.


    - The hierarchical structure of the suggestions is designed to help the user _quickly_ understand them, and to decide which ones are relevant and which are not:

        - Only if the `Category` header is relevant, the user should move to the summarized suggestion description.
        - Only if the summarized suggestion description is relevant, the user should click on the collapsible, to read the full suggestion description with a code preview example.

    - In addition, we recommend to use the [`extra_instructions`](../tools/improve.md#extra-instructions-and-best-practices) field to guide the model to suggestions that are more relevant to the specific needs of the project.

___

??? note "Q: How can I get more tailored suggestions?"
    #### Answer:<span style="display:none;">3</span>

    See [here](../tools/improve.md#extra-instructions-and-best-practices) for more information on how to use the `extra_instructions` and `best_practices` configuration options, to guide the model to more tailored suggestions.

___

??? note "Q: Will you store my code? Are you using my code to train models?"
    #### Answer:<span style="display:none;">4</span>

    No. PR-Agent strict privacy policy ensures that your code is not stored or used for training purposes.

    For a detailed overview of our data privacy policy, please refer to [this link](../overview/data_privacy.md)

___

??? note "Q: Can PR-Agent review draft/offline PRs?"
    #### Answer:<span style="display:none;">6</span>

    Yes. While PR-Agent won't automatically review draft PRs, you can still get feedback by manually requesting it through [online commenting](../usage-guide/automations_and_usage.md#online-usage).

    For active PRs, you can customize the automatic feedback settings [here](../usage-guide/automations_and_usage.md#pr-agent-automatic-feedback) to match your team's workflow.
___

??? note "Q: Can the 'Review effort' feedback be calibrated or customized?"
    #### Answer:<span style="display:none;">7</span>

    Yes, you can customize review effort estimates using the `extra_instructions` configuration option (see [documentation](../tools/review.md#configuration-options)).
    
    Example mapping:

    - Effort 1: < 30 minutes review time
    - Effort 2: 30-60 minutes review time
    - Effort 3: 60-90 minutes review time
    - ...
    
    Note: The effort levels (1-5) are primarily meant for _comparative_ purposes, helping teams prioritize reviewing smaller PRs first. The actual review duration may vary, as the focus is on providing consistent relative effort estimates.

___

??? note "Q: How to reduce the noise generated by PR-Agent?"
    #### Answer:<span style="display:none;">3</span>

    The default configuration of PR-Agent is designed to balance helpful feedback with noise reduction. It reduces noise through several approaches:

    - Auto-feedback uses three highly structured tools (`/describe`, `/review`, and `/improve`), designed to be accessible at a glance without creating large visual overload
    - Suggestions are presented in a table format rather than as committable comments, which are far noisier
    - The 'File Walkthrough' section is folded by default, as it tends to be verbose
    - Intermediate comments are avoided when creating new PRs (like "PR-Agent is now reviewing your PR..."), which would generate email noise
    
    From our experience, especially in large teams or organizations, complaints about "noise" sometimes stem from the following issues:

    - **Feedback from multiple bots**: When multiple bots provide feedback on the same PR, it creates confusion and noise. We recommend using PR-Agent as the primary feedback tool to streamline the process and reduce redundancy.
    - **Getting familiar with the tool**: Unlike many tools that provide feedback only on demand, PR-Agent automatically analyzes and suggests improvements for every code change. While this proactive approach can feel intimidating at first, it's designed to continuously enhance code quality and catch bugs and problems when they occur. We recommend reviewing [this guide](../tools/improve.md#understanding-ai-code-suggestions) to help align expectations and maximize the value of PR-Agent's auto-feedback.

    Therefore, at a global configuration level, we recommend using the default configuration, which is designed to reduce noise while providing valuable feedback.
    
    However, if you still find the feedback too noisy, you can adjust the configuration. Since each user and team has different needs, it's definitely possible - and even recommended - to adjust configurations for specific repos as needed.
    Ways to adjust the configuration for noise reduction include for example:

    - [Score thresholds for code suggestions](../tools/improve.md#configuration-options)
    - [Utilizing the `extra_instructions` field for more tailored feedback](../tools/improve.md#extra-instructions)
    - [Controlling which tools run automatically](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened)

    Note that some users may prefer the opposite - more thorough and detailed feedback. PR-Agent is designed to be flexible and customizable, allowing you to tailor the feedback to your team's specific needs and preferences.
    Examples of ways to increase feedback include:

    - [Dual-publishing mode](../tools/improve.md#dual-publishing-mode)
    - [Interactive usage](../core-abilities/interactivity.md)
___


================================================
FILE: docs/docs/index.md
================================================
# Overview

[PR-Agent](https://github.com/qodo-ai/pr-agent) is an open-source, AI-powered code review agent and a community-maintained legacy project of Qodo. It is distinct from Qodo's primary AI code review offering, which provides a feature-rich, context-aware experience. Qodo now offers a free tier that integrates seamlessly with GitHub, GitLab, Bitbucket, and Azure DevOps for high-quality automated reviews.

- See the [Installation Guide](./installation/index.md) for instructions on installing and running the tool on different git platforms.

- See the [Usage Guide](./usage-guide/index.md) for instructions on running commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened.

- See the [Tools Guide](./tools/index.md) for a detailed description of the different tools.

## Docs Smart Search

To search the documentation site using natural language:

1) Comment `/help "your question"` in a pull request where PR-Agent is installed

2) The bot will respond with an [answer](https://github.com/qodo-ai/pr-agent/pull/1241#issuecomment-2365259334) that includes relevant documentation links.

## Features

PR-Agent offers comprehensive pull request functionalities integrated with various git providers:

|       |                                                                                       | GitHub | GitLab | Bitbucket | Azure DevOps | Gitea |
| ----- |---------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|:-----:|
| [TOOLS](./tools/index.md) | [Describe](./tools/describe.md)                                     |   ✅   |   ✅   |    ✅     |      ✅       |  ✅   |
|       | [Review](./tools/review.md)                                                           |   ✅   |   ✅   |    ✅     |      ✅       |  ✅   |
|       | [Improve](./tools/improve.md)                                                         |   ✅   |   ✅   |    ✅     |      ✅       |  ✅   |
|       | [Ask](./tools/ask.md)                                                                 |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | ⮑ [Ask on code lines](./tools/ask.md#ask-lines)                                       |   ✅   |   ✅   |           |              |       |
|       | [Add Docs](./tools/add_docs.md)                                                       |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Generate Labels](./tools/generate_labels.md)                                         |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Similar Issues](./tools/similar_issues.md)                                           |   ✅   |        |           |              |       |
|       | [Help](./tools/help.md)                                                               |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Help Docs](./tools/help_docs.md)                                                     |   ✅   |   ✅   |    ✅     |              |       |
|       | [Update CHANGELOG](./tools/update_changelog.md)                                       |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       |                                                                                       |        |        |           |              |       |
| [USAGE](./usage-guide/index.md) | [CLI](./usage-guide/automations_and_usage.md#local-repo-cli)      |   ✅   |   ✅   |    ✅     |      ✅       |  ✅   |
|       | [App / webhook](./usage-guide/automations_and_usage.md#github-app)                    |   ✅   |   ✅   |    ✅     |      ✅       |  ✅   |
|       | [Tagging bot](https://github.com/qodo-ai/pr-agent#try-it-now)                       |   ✅   |        |           |              |       |
|       | [Actions](./installation/github.md#run-as-a-github-action)                            |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       |                                                                                       |        |        |           |              |       |
| [CORE](./core-abilities/index.md) | [Adaptive and token-aware file patch fitting](./core-abilities/compression_strategy.md) |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Chat on code suggestions](./core-abilities/interactivity.md)                         |   ✅   |  ✅   |           |              |       |
|       | [Compression strategy](./core-abilities/compression_strategy.md)                      |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Dynamic context](./core-abilities/dynamic_context.md)                                |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Fetching ticket context](./core-abilities/fetching_ticket_context.md)                |   ✅   |  ✅   |    ✅     |              |       |
|       | [Interactivity](./core-abilities/interactivity.md)                                    |   ✅   |  ✅   |           |              |       |
|       | [Local and global metadata](./core-abilities/metadata.md)                             |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Multiple models support](./usage-guide/changing_a_model.md)                          |   ✅   |   ✅   |    ✅     |      ✅       |       |
|       | [Self reflection](./core-abilities/self_reflection.md)                                |   ✅   |   ✅   |    ✅     |      ✅       |       |

## Example Results

<hr>

#### [/describe](https://github.com/qodo-ai/pr-agent/pull/530)

<figure markdown="1">
![/describe](https://www.codium.ai/images/pr_agent/describe_new_short_main.png){width=512}
</figure>
<hr>

#### [/review](https://github.com/qodo-ai/pr-agent/pull/732#issuecomment-1975099151)

<figure markdown="1">
![/review](https://www.codium.ai/images/pr_agent/review_new_short_main.png){width=512}
</figure>
<hr>

#### [/improve](https://github.com/qodo-ai/pr-agent/pull/732#issuecomment-1975099159)

<figure markdown="1">
![/improve](https://www.codium.ai/images/pr_agent/improve_new_short_main.png){width=512}
</figure>
<hr>

## How it Works

The following diagram illustrates PR-Agent tools and their flow:

![PR-Agent Tools](https://codium.ai/images/pr_agent/diagram-v0.9.png)

Check out the [PR Compression strategy](core-abilities/index.md) page for more details on how we convert a code diff to a manageable LLM prompt


================================================
FILE: docs/docs/installation/azure.md
================================================
## Azure DevOps Pipeline

You can use a pre-built Action Docker image to run PR-Agent as an Azure DevOps pipeline.
Add the following file to your repository under `azure-pipelines.yml`:

```yaml
# Opt out of CI triggers
trigger: none

# Configure PR trigger
# pr:
#   branches:
#     include:
#     - '*'
#   autoCancel: true
#   drafts: false

# NOTE for Azure Repos Git:
# Azure Repos does not honor YAML pr: triggers. Configure Build Validation
# via Branch Policies instead (see note below). You can safely omit pr:.

stages:
- stage: pr_agent
  displayName: 'PR Agent Stage'
  jobs:
  - job: pr_agent_job
    displayName: 'PR Agent Job'
    pool:
      vmImage: 'ubuntu-latest'
    container:
      image: codiumai/pr-agent:latest
      options: --entrypoint ""
    variables:
      - group: pr_agent
    steps:
    - script: |
        echo "Running PR Agent action step"

        # Construct PR_URL
        PR_URL="${SYSTEM_COLLECTIONURI}${SYSTEM_TEAMPROJECT}/_git/${BUILD_REPOSITORY_NAME}/pullrequest/${SYSTEM_PULLREQUEST_PULLREQUESTID}"
        echo "PR_URL=$PR_URL"

        # Extract organization URL from System.CollectionUri
        ORG_URL=$(echo "$(System.CollectionUri)" | sed 's/\/$//') # Remove trailing slash if present
        echo "Organization URL: $ORG_URL"

        export azure_devops__org="$ORG_URL"
        export config__git_provider="azure"

        pr-agent --pr_url="$PR_URL" describe
        pr-agent --pr_url="$PR_URL" review
        pr-agent --pr_url="$PR_URL" improve
      env:
        azure_devops__pat: $(azure_devops_pat)
        openai__key: $(OPENAI_KEY)
      displayName: 'Run PR-Agent'
```

This script will run PR-Agent on every new merge request, with the `improve`, `review`, and `describe` commands.
Note that you need to export the `azure_devops__pat` and `OPENAI_KEY` variables in the Azure DevOps pipeline settings (Pipelines -> Library -> + Variable group):

![PR-Agent](https://codium.ai/images/pr_agent/azure_devops_pipeline_secrets.png){width=468}

Make sure to give pipeline permissions to the `pr_agent` variable group.

> Note that Azure Pipelines lacks support for triggering workflows from PR comments. If you find a viable solution, please contribute it to our [issue tracker](https://github.com/qodo-ai/pr-agent/issues)

### Azure Repos Git PR triggers and Build Validation

Azure Repos Git does not use YAML `pr:` triggers for pipelines. Instead, configure Build Validation on the target branch to run the PR Agent pipeline for pull requests:

1. Go to Project Settings → Repositories → Branches.
2. Select the target branch and open Branch Policies.
3. Under Build Validation, add a policy:
   - Select the PR Agent pipeline (the `azure-pipelines.yml` above).
   - Set it as Required.
4. Remove the `pr:` section from your YAML (not needed for Azure Repos Git).

This distinction applies specifically to Azure Repos Git. Other providers like GitHub and Bitbucket Cloud can use YAML-based PR triggers.

## Azure DevOps from CLI

To use Azure DevOps provider use the following settings in configuration.toml:

```toml
[config]
git_provider="azure"
```

Azure DevOps provider supports [PAT token](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows) or [DefaultAzureCredential](https://learn.microsoft.com/en-us/azure/developer/python/sdk/authentication-overview#authentication-in-server-environments) authentication.
PAT is faster to create, but has built-in expiration date, and will use the user identity for API calls.
Using DefaultAzureCredential you can use managed identity or Service principle, which are more secure and will create separate ADO user identity (via AAD) to the agent.

If PAT was chosen, you can assign the value in .secrets.toml.
If DefaultAzureCredential was chosen, you can assigned the additional env vars like AZURE_CLIENT_SECRET directly,
or use managed identity/az cli (for local development) without any additional configuration.
in any case, 'org' value must be assigned in .secrets.toml:

```toml
[azure_devops]
org = "https://dev.azure.com/YOUR_ORGANIZATION/"
# pat = "YOUR_PAT_TOKEN" needed only if using PAT for authentication
```

## Azure DevOps Webhook

To trigger from an Azure webhook, you need to manually [add a webhook](https://learn.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=azure-devops).
Use the "Pull request created" type to trigger a review, or "Pull request commented on" to trigger any supported comment with /<command> <args> comment on the relevant PR. Note that for the "Pull request commented on" trigger, only API v2.0 is supported.

For webhook security, create a sporadic username/password pair and configure the webhook username and password on both the server and Azure DevOps webhook. These will be sent as basic Auth data by the webhook with each request:

```toml
[azure_devops_server]
webhook_username = "<basic auth user>"
webhook_password = "<basic auth password>"
```

> :warning: **Ensure that the webhook endpoint is only accessible over HTTPS** to mitigate the risk of credential interception when using basic authentication.


================================================
FILE: docs/docs/installation/bitbucket.md
================================================
## Run as a Bitbucket Pipeline

You can use the Bitbucket Pipeline system to run PR-Agent on every pull request open or update.

1. Add the following file in your repository bitbucket-pipelines.yml

```yaml
pipelines:
    pull-requests:
      '**':
        - step:
            name: PR Agent Review
            image: codiumai/pr-agent:latest
            script:
              - pr-agent --pr_url=https://bitbucket.org/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pull-requests/$BITBUCKET_PR_ID review
```

2. Add the following secure variables to your repository under Repository settings > Pipelines > Repository variables.

   - CONFIG__GIT_PROVIDER: `bitbucket`
   - OPENAI__KEY: `<your key>`
   - BITBUCKET__AUTH_TYPE: `basic` or `bearer` (default is `bearer`)
   - BITBUCKET__BEARER_TOKEN: `<your token>` (required when auth_type is bearer)
   - BITBUCKET__BASIC_TOKEN: `<your token>` (required when auth_type is basic)

You can get a Bitbucket token for your repository by following Repository Settings -> Security -> Access Tokens.
For basic auth, you can generate a base64 encoded token from your username:password combination.

Note that comments on a PR are not supported in Bitbucket Pipeline.

## Bitbucket Server and Data Center

Login into your on-prem instance of Bitbucket with your service account username and password.
Navigate to `Manage account`, `HTTP Access tokens`, `Create Token`.
Generate the token and add it to .secret.toml under `bitbucket_server` section

```toml
[bitbucket_server]
bearer_token = "<your key>"
```

Don't forget to also set the URL of your Bitbucket Server instance (either in `.secret.toml` or in `configuration.toml`):

```toml
[bitbucket_server]
url = "<full URL to your Bitbucket instance, e.g.: https://git.bitbucket.com>"
```

### Run it as CLI

Modify `configuration.toml`:

```toml
git_provider="bitbucket_server"
```



and pass the Pull request URL:

```shell
python cli.py --pr_url https://git.on-prem-instance-of-bitbucket.com/projects/PROJECT/repos/REPO/pull-requests/1 review
```

### Run it as service

To run PR-Agent as webhook, build the docker image:

```bash
docker build . -t codiumai/pr-agent:bitbucket_server_webhook --target bitbucket_server_webhook -f docker/Dockerfile
docker push codiumai/pr-agent:bitbucket_server_webhook  # Push to your Docker repository
```

Navigate to `Projects` or `Repositories`, `Settings`, `Webhooks`, `Create Webhook`.
Fill in the name and URL. For Authentication, select 'None'. Select the 'Pull Request Opened' checkbox to receive that event as a webhook.

The URL should end with `/webhook`, for example: https://domain.com/webhook


================================================
FILE: docs/docs/installation/gitea.md
================================================
## Run a Gitea webhook server

1. In Gitea create a new user and give it "Reporter" role for the intended group or project.

2. For the user from step 1. generate a `personal_access_token` with `api` access.

3. Generate a random secret for your app, and save it for later (`webhook_secret`). For example, you can use:

```bash
WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))")
```

4. Clone this repository:

```bash
git clone https://github.com/qodo-ai/pr-agent.git
```

5. Prepare variables and secrets. Skip this step if you plan on setting these as environment variables when running the agent:
    - In the configuration file/variables:
        - Set `config.git_provider` to "gitea"
    - In the secrets file/variables:
        - Set your AI model key in the respective section
        - In the [Gitea] section, set `personal_access_token` (with token from step 2) and `webhook_secret` (with secret from step 3)

6. Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example:

```bash
docker build -f /docker/Dockerfile -t pr-agent:gitea_app --target gitea_app .
docker push codiumai/pr-agent:gitea_webhook  # Push to your Docker repository
```

7. Set the environmental variables, the method depends on your docker runtime. Skip this step if you included your secrets/configuration directly in the Docker image.

```bash
CONFIG__GIT_PROVIDER=gitea
GITEA__PERSONAL_ACCESS_TOKEN=<personal_access_token>
GITEA__WEBHOOK_SECRET=<webhook_secret>
GITEA__URL=https://gitea.com # Or self host
OPENAI__KEY=<your_openai_api_key>
GITEA__SKIP_SSL_VERIFICATION=false # or true
GITEA__SSL_CA_CERT=/path/to/cacert.pem
```

8. Create a webhook in your Gitea project. Set the URL to `http[s]://<PR_AGENT_HOSTNAME>/api/v1/gitea_webhooks`, the secret token to the generated secret from step 3, and enable the triggers `push`, `comments` and `merge request events`.

9. Test your installation by opening a merge request or commenting on a merge request using one of PR Agent's commands.


================================================
FILE: docs/docs/installation/github.md
================================================
In this page we will cover how to install and run PR-Agent as a GitHub Action or GitHub App, and how to configure it for your needs.

## Run as a GitHub Action

You can use our pre-built Github Action Docker image to run PR-Agent as a Github Action.

1) Add the following file to your repository under `.github/workflows/pr_agent.yml`:

```yaml
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    name: Run pr agent on every pull request, respond to user comments
    steps:
      - name: PR Agent action step
        id: pragent
        uses: qodo-ai/pr-agent@main
        env:
          OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

2) Add the following secret to your repository under `Settings > Secrets and variables > Actions > New repository secret > Add secret`:

```
Name = OPENAI_KEY
Secret = <your key>
```

The GITHUB_TOKEN secret is automatically created by GitHub.

3) Merge this change to your main branch.
When you open your next PR, you should see a comment from `github-actions` bot with a review of your PR, and instructions on how to use the rest of the tools.

4) You may configure PR-Agent by adding environment variables under the env section corresponding to any configurable property in the [configuration](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) file. Some examples:

```yaml
      env:
        # ... previous environment values
        OPENAI.ORG: "<Your organization name under your OpenAI account>"
        PR_REVIEWER.REQUIRE_TESTS_REVIEW: "false" # Disable tests review
        PR_CODE_SUGGESTIONS.NUM_CODE_SUGGESTIONS: 6 # Increase number of code suggestions
```

See detailed usage instructions in the [USAGE GUIDE](../usage-guide/automations_and_usage.md#github-action)

### Configuration Examples

This section provides detailed, step-by-step examples for configuring PR-Agent with different models and advanced options in GitHub Actions.

#### Quick Start Examples

##### Basic Setup (OpenAI Default)

Copy this minimal workflow to get started with the default OpenAI models:

```yaml
name: PR Agent
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    steps:
      - name: PR Agent action step
        uses: qodo-ai/pr-agent@main
        env:
          OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

##### Gemini Setup

Ready-to-use workflow for Gemini models:

```yaml
name: PR Agent (Gemini)
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    steps:
      - name: PR Agent action step
        uses: qodo-ai/pr-agent@main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          config.model: "gemini/gemini-1.5-flash"
          config.fallback_models: '["gemini/gemini-1.5-flash"]'
          GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          github_action_config.auto_review: "true"
          github_action_config.auto_describe: "true"
          github_action_config.auto_improve: "true"
```

#### Claude Setup

Ready-to-use workflow for Claude models:

```yaml
name: PR Agent (Claude)
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    steps:
      - name: PR Agent action step
        uses: qodo-ai/pr-agent@main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          config.model: "anthropic/claude-3-opus-20240229"
          config.fallback_models: '["anthropic/claude-3-haiku-20240307"]'
          ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }}
          github_action_config.auto_review: "true"
          github_action_config.auto_describe: "true"
          github_action_config.auto_improve: "true"
```

#### Basic Configuration with Tool Controls

Start with this enhanced workflow that includes tool configuration:

```yaml
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    name: Run pr agent on every pull request, respond to user comments
    steps:
      - name: PR Agent action step
        id: pragent
        uses: qodo-ai/pr-agent@main
        env:
          OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # Enable/disable automatic tools
          github_action_config.auto_review: "true"
          github_action_config.auto_describe: "true"
          github_action_config.auto_improve: "true"
          # Configure which PR events trigger the action
          github_action_config.pr_actions: '["opened", "reopened", "ready_for_review", "review_requested"]'
```

#### Switching Models

##### Using Gemini (Google AI Studio)

To use Gemini models instead of the default OpenAI models:

```yaml
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Set the model to Gemini
        config.model: "gemini/gemini-1.5-flash"
        config.fallback_models: '["gemini/gemini-1.5-flash"]'
        # Add your Gemini API key
        GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
        # Tool configuration
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

**Required Secrets:**

- Add `GEMINI_API_KEY` to your repository secrets (get it from [Google AI Studio](https://aistudio.google.com/))

**Note:** When using non-OpenAI models like Gemini, you don't need to set `OPENAI_KEY` - only the model-specific API key is required.

##### Using Claude (Anthropic)

To use Claude models:

```yaml
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Set the model to Claude
        config.model: "anthropic/claude-3-opus-20240229"
        config.fallback_models: '["anthropic/claude-3-haiku-20240307"]'
        # Add your Anthropic API key
        ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }}
        # Tool configuration
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

**Required Secrets:**

- Add `ANTHROPIC_KEY` to your repository secrets (get it from [Anthropic Console](https://console.anthropic.com/))

**Note:** When using non-OpenAI models like Claude, you don't need to set `OPENAI_KEY` - only the model-specific API key is required.

##### Using Azure OpenAI

To use Azure OpenAI services:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.AZURE_OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Azure OpenAI configuration
        OPENAI.API_TYPE: "azure"
        OPENAI.API_VERSION: "2023-05-15"
        OPENAI.API_BASE: ${{ secrets.AZURE_OPENAI_ENDPOINT }}
        OPENAI.DEPLOYMENT_ID: ${{ secrets.AZURE_OPENAI_DEPLOYMENT }}
        # Set the model to match your Azure deployment
        config.model: "gpt-4o"
        config.fallback_models: '["gpt-4o"]'
        # Tool configuration
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

**Required Secrets:**

- `AZURE_OPENAI_KEY`: Your Azure OpenAI API key
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint URL
- `AZURE_OPENAI_DEPLOYMENT`: Your deployment name

##### Using Local Models (Ollama)

To use local models via Ollama:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Set the model to a local Ollama model
        config.model: "ollama/qwen2.5-coder:32b"
        config.fallback_models: '["ollama/qwen2.5-coder:32b"]'
        config.custom_model_max_tokens: "128000"
        # Ollama configuration
        OLLAMA.API_BASE: "http://localhost:11434"
        # Tool configuration
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

**Note:** For local models, you'll need to use a self-hosted runner with Ollama installed, as GitHub Actions hosted runners cannot access localhost services.

#### Advanced Configuration Options

##### Custom Review Instructions

Add specific instructions for the review process:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Custom review instructions
        pr_reviewer.extra_instructions: "Focus on security vulnerabilities and performance issues. Check for proper error handling."
        # Tool configuration
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

##### Language-Specific Configuration

Configure for specific programming languages:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Language-specific settings
        pr_reviewer.extra_instructions: "Focus on Python best practices, type hints, and docstrings."
        pr_code_suggestions.num_code_suggestions: "8"
        pr_code_suggestions.suggestions_score_threshold: "7"
        # Tool configuration
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

##### Selective Tool Execution

Run only specific tools automatically:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Only run review and describe, skip improve
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "false"
        # Only trigger on PR open and reopen
        github_action_config.pr_actions: '["opened", "reopened"]'
```

#### Using Configuration Files

Instead of setting all options via environment variables, you can use a `.pr_agent.toml` file in your repository root:

1. Create a `.pr_agent.toml` file in your repository root:

```toml
[config]
model = "gemini/gemini-1.5-flash"
fallback_models = ["anthropic/claude-3-opus-20240229"]

[pr_reviewer]
extra_instructions = "Focus on security issues and code quality."

[pr_code_suggestions]
num_code_suggestions = 6
suggestions_score_threshold = 7
```

2. Use a simpler workflow file:

```yaml
on:
  pull_request:
    types: [opened, reopened, ready_for_review]
  issue_comment:
jobs:
  pr_agent_job:
    if: ${{ github.event.sender.type != 'Bot' }}
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
      contents: write
    name: Run pr agent on every pull request, respond to user comments
    steps:
      - name: PR Agent action step
        id: pragent
        uses: qodo-ai/pr-agent@main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GOOGLE_AI_STUDIO.GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          ANTHROPIC.KEY: ${{ secrets.ANTHROPIC_KEY }}
          github_action_config.auto_review: "true"
          github_action_config.auto_describe: "true"
          github_action_config.auto_improve: "true"
```

#### Troubleshooting Common Issues

##### Model Not Found Errors

If you get model not found errors:

1. **Check model name format**: Ensure you're using the correct model identifier format (e.g., `gemini/gemini-1.5-flash`, not just `gemini-1.5-flash`)

2. **Verify API keys**: Make sure your API keys are correctly set as repository secrets

3. **Check model availability**: Some models may not be available in all regions or may require specific access

##### Environment Variable Format

Remember these key points about environment variables:

- Use dots (`.`) or double underscores (`__`) to separate sections and keys
- Boolean values should be strings: `"true"` or `"false"`
- Arrays should be JSON strings: `'["item1", "item2"]'`
- Model names are case-sensitive

##### Rate Limiting

If you encounter rate limiting:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Add fallback models for better reliability
        config.fallback_models: '["gpt-4o", "gpt-3.5-turbo"]'
        # Increase timeout for slower models
        config.ai_timeout: "300"
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

##### Common Error Messages and Solutions

**Error: "Model not found"**
- **Solution**: Check the model name format and ensure it matches the exact identifier. See the [Changing a model in PR-Agent](../usage-guide/changing_a_model.md) guide for supported models and their correct identifiers.

**Error: "API key not found"**
- **Solution**: Verify that your API key is correctly set as a repository secret and the environment variable name matches exactly
- **Note**: For non-OpenAI models (Gemini, Claude, etc.), you only need the model-specific API key, not `OPENAI_KEY`

**Error: "Rate limit exceeded"**
- **Solution**: Add fallback models or increase the `config.ai_timeout` value

**Error: "Permission denied"**
- **Solution**: Ensure your workflow has the correct permissions set:
  ```yaml
  permissions:
    issues: write
    pull-requests: write
    contents: write
  ```

**Error: "Invalid JSON format"**

- **Solution**: Check that arrays are properly formatted as JSON strings:

```yaml

Correct:
config.fallback_models: '["model1", "model2"]'
Incorrect (interpreted as a YAML list, not a string):
config.fallback_models: ["model1", "model2"]
```

##### Debugging Tips

1. **Enable verbose logging**: Add `config.verbosity_level: "2"` to see detailed logs
2. **Check GitHub Actions logs**: Look at the step output for specific error messages
3. **Test with minimal configuration**: Start with just the basic setup and add options one by one
4. **Verify secrets**: Double-check that all required secrets are set in your repository settings

##### Performance Optimization

For better performance with large repositories:

```yaml
      env:
        OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Optimize for large PRs
        config.large_patch_policy: "clip"
        config.max_model_tokens: "32000"
        config.patch_extra_lines_before: "3"
        config.patch_extra_lines_after: "1"
        github_action_config.auto_review: "true"
        github_action_config.auto_describe: "true"
        github_action_config.auto_improve: "true"
```

#### Reference

For more detailed configuration options, see:

- [Changing a model in PR-Agent](../usage-guide/changing_a_model.md)
- [Configuration options](../usage-guide/configuration_options.md)
- [Automations and usage](../usage-guide/automations_and_usage.md#github-action)

### Using a specific release

!!! tip ""
    if you want to pin your action to a specific release (v0.23 for example) for stability reasons, use:
    ```yaml
    ...
        steps:
          - name: PR Agent action step
            id: pragent
            uses: docker://codiumai/pr-agent:0.23-github_action
    ...
    ```

    For enhanced security, you can also specify the Docker image by its [digest](https://hub.docker.com/repository/docker/codiumai/pr-agent/tags):
    ```yaml
    ...
        steps:
          - name: PR Agent action step
            id: pragent
            uses: docker://codiumai/pr-agent@sha256:14165e525678ace7d9b51cda8652c2d74abb4e1d76b57c4a6ccaeba84663cc64
    ...
    ```

### Action for GitHub enterprise server

!!! tip ""
    To use the action with a GitHub enterprise server, add an environment variable `GITHUB.BASE_URL` with the API URL of your GitHub server.

    For example, if your GitHub server is at `https://github.mycompany.com`, add the following to your workflow file:
    ```yaml
          env:
            # ... previous environment values
            GITHUB.BASE_URL: "https://github.mycompany.com/api/v3"
    ```

---

## Run as a GitHub App

Allowing you to automate the review process on your private or public repositories.

1) Create a GitHub App from the [Github Developer Portal](https://docs.github.com/en/developers/apps/creating-a-github-app).

   - Set the following permissions:
     - Pull requests: Read & write
     - Issue comment: Read & write
     - Metadata: Read-only
     - Contents: Read-only
   - Set the following events:
     - Issue comment
     - Pull request
     - Push (if you need to enable triggering on PR update)

2) Generate a random secret for your app, and save it for later. For example, you can use:

```bash
WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))")
```

3) Acquire the following pieces of information from your app's settings page:

   - App private key (click "Generate a private key" and save the file)
   - App ID

4) Clone this repository:

```bash
git clone https://github.com/qodo-ai/pr-agent.git
```

5) Copy the secrets template file and fill in the following:

```bash
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
# Edit .secrets.toml file
```

- Your OpenAI key.
- Copy your app's private key to the private_key field.
- Copy your app's ID to the app_id field.
- Copy your app's webhook secret to the webhook_secret field.
- Set deployment_type to 'app' in [configuration.toml](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml)

    > The .secrets.toml file is not copied to the Docker image by default, and is only used for local development.
    > If you want to use the .secrets.toml file in your Docker image, you can add remove it from the .dockerignore file.
    > In most production environments, you would inject the secrets file as environment variables or as mounted volumes.
    > For example, in order to inject a secrets file as a volume in a Kubernetes environment you can update your pod spec to include the following,
    > assuming you have a secret named `pr-agent-settings` with a key named `.secrets.toml`:

    ```
           volumes:
            - name: settings-volume
              secret:
                secretName: pr-agent-settings
    // ...
           containers:
    // ...
              volumeMounts:
                - mountPath: /app/pr_agent/settings_prod
                  name: settings-volume
    ```

    > Another option is to set the secrets as environment variables in your deployment environment, for example `OPENAI.KEY` and `GITHUB.USER_TOKEN`.

6) Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example:

    ```bash
    docker build . -t codiumai/pr-agent:github_app --target github_app -f docker/Dockerfile
    docker push codiumai/pr-agent:github_app  # Push to your Docker repository
    ```

7. Host the app using a server, serverless function, or container environment. Alternatively, for development and
   debugging, you may use tools like smee.io to forward webhooks to your local machine.
    You can check [Deploy as a Lambda Function](#deploy-as-a-lambda-function)

8. Go back to your app's settings, and set the following:

   - Webhook URL: The URL of your app's server or the URL of the smee.io channel.
   - Webhook secret: The secret you generated earlier.

9. Install the app by navigating to the "Install App" tab and selecting your desired repositories.

> **Note:** When running PR-Agent from GitHub app, the default configuration file (configuration.toml) will be loaded.
> However, you can override the default tool parameters by uploading a local configuration file `.pr_agent.toml`
> For more information please check out the [USAGE GUIDE](../usage-guide/automations_and_usage.md#github-app)
---

## Additional deployment methods

### Deploy as a Lambda Function

Note that since AWS Lambda env vars cannot have "." in the name, you can replace each "." in an env variable with "__".<br>
For example: `GITHUB.WEBHOOK_SECRET` --> `GITHUB__WEBHOOK_SECRET`

1. Follow steps 1-5 from [here](#run-as-a-github-app).
2. Build a docker image that can be used as a lambda function

    ```shell
    docker buildx build --platform=linux/amd64 . -t codiumai/pr-agent:github_lambda --target github_lambda -f docker/Dockerfile.lambda
   ```
   (Note: --target github_lambda is optional as it's the default target)


3. Push image to ECR

    ```shell
    docker tag codiumai/pr-agent:github_lambda <AWS_ACCOUNT>.dkr.ecr.<AWS_REGION>.amazonaws.com/codiumai/pr-agent:github_lambda
    docker push <AWS_ACCOUNT>.dkr.ecr.<AWS_REGION>.amazonaws.com/codiumai/pr-agent:github_lambda
    ```

4. Create a lambda function that uses the uploaded image. Set the lambda timeout to be at least 3m.
5. Configure the lambda function to have a Function URL.
6. In the environment variables of the Lambda function, specify `AZURE_DEVOPS_CACHE_DIR` to a writable location such as /tmp. (see [link](https://github.com/qodo-ai/pr-agent/pull/450#issuecomment-1840242269))
7. Go back to steps 8-9 of [Method 5](#run-as-a-github-app) with the function url as your Webhook URL.
    The Webhook URL would look like `https://<LAMBDA_FUNCTION_URL>/api/v1/github_webhooks`

#### Using AWS Secrets Manager

For production Lambda deployments, use AWS Secrets Manager instead of environment variables:

1. Create a secret in AWS Secrets Manager with JSON format like this:

```json
{
  "openai.key": "sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "github.webhook_secret": "your-webhook-secret-from-step-2",
  "github.private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----"
}
```

2. Add IAM permission `secretsmanager:GetSecretValue` to your Lambda execution role
3. Set these environment variables in your Lambda:

```bash
AWS_SECRETS_MANAGER__SECRET_ARN=arn:aws:secretsmanager:us-east-1:123456789012:secret:pr-agent-secrets-AbCdEf
CONFIG__SECRET_PROVIDER=aws_secrets_manager
```

---

### AWS CodeCommit Setup

Not all features have been added to CodeCommit yet.  As of right now, CodeCommit has been implemented to run the PR-Agent CLI on the command line, using AWS credentials stored in environment variables.  (More features will be added in the future.)  The following is a set of instructions to have PR-Agent do a review of your CodeCommit pull request from the command line:

1. Create an IAM user that you will use to read CodeCommit pull requests and post comments
    - Note: That user should have CLI access only, not Console access
2. Add IAM permissions to that user, to allow access to CodeCommit (see IAM Role example below)
3. Generate an Access Key for your IAM user
4. Set the Access Key and Secret using environment variables (see Access Key example below)
5. Set the `git_provider` value to `codecommit` in the `pr_agent/settings/configuration.toml` settings file
6. Set the `PYTHONPATH` to include your `pr-agent` project directory
    - Option A: Add `PYTHONPATH="/PATH/TO/PROJECTS/pr-agent` to your `.env` file
    - Option B: Set `PYTHONPATH` and run the CLI in one command, for example:
        - `PYTHONPATH="/PATH/TO/PROJECTS/pr-agent python pr_agent/cli.py [--ARGS]`

---

##### AWS CodeCommit IAM Role Example

Example IAM permissions to that user to allow access to CodeCommit:

- Note: The following is a working example of IAM permissions that has read access to the repositories and write access to allow posting comments
- Note: If you only want pr-agent to review your pull requests, you can tighten the IAM permissions further, however this IAM example will work, and allow the pr-agent to post comments to the PR
- Note: You may want to replace the `"Resource": "*"` with your list of repos, to limit access to only those repos

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "codecommit:BatchDescribe*",
                "codecommit:BatchGet*",
                "codecommit:Describe*",
                "codecommit:EvaluatePullRequestApprovalRules",
                "codecommit:Get*",
                "codecommit:List*",
                "codecommit:PostComment*",
                "codecommit:PutCommentReaction",
                "codecommit:UpdatePullRequestDescription",
                "codecommit:UpdatePullRequestTitle"
            ],
            "Resource": "*"
        }
    ]
}
```

##### AWS CodeCommit Access Key and Secret

Example setting the Access Key and Secret using environment variables

```sh
export AWS_ACCESS_KEY_ID="XXXXXXXXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXXXXXXXXX"
export AWS_DEFAULT_REGION="us-east-1"
```

##### AWS CodeCommit CLI Example

After you set up AWS CodeCommit using the instructions above, here is an example CLI run that tells pr-agent to **review** a given pull request.
(Replace your specific PYTHONPATH and PR URL in the example)

```sh
PYTHONPATH="/PATH/TO/PROJECTS/pr-agent" python pr_agent/cli.py \
  --pr_url https://us-east-1.console.aws.amazon.com/codesuite/codecommit/repositories/MY_REPO_NAME/pull-requests/321 \
  review
```

================================================
FILE: docs/docs/installation/gitlab.md
================================================
## Run as a GitLab Pipeline

You can use a pre-built Action Docker image to run PR-Agent as a GitLab pipeline. This is a simple way to get started with PR-Agent without setting up your own server.

(1) Add the following file to your repository under `.gitlab-ci.yml`:

```yaml
stages:
  - pr_agent

pr_agent_job:
  stage: pr_agent
  image:
    name: codiumai/pr-agent:latest
    entrypoint: [""]
  script:
    - cd /app
    - echo "Running PR Agent action step"
    - export MR_URL="$CI_MERGE_REQUEST_PROJECT_URL/merge_requests/$CI_MERGE_REQUEST_IID"
    - echo "MR_URL=$MR_URL"
    - export gitlab__url=$CI_SERVER_PROTOCOL://$CI_SERVER_FQDN
    - export gitlab__PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN
    - export config__git_provider="gitlab"
    - export openai__key=$OPENAI_KEY
    - python -m pr_agent.cli --pr_url="$MR_URL" describe
    - python -m pr_agent.cli --pr_url="$MR_URL" review
    - python -m pr_agent.cli --pr_url="$MR_URL" improve
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
```

This script will run PR-Agent on every new merge request. You can modify the `rules` section to run PR-Agent on different events.
You can also modify the `script` section to run different PR-Agent commands, or with different parameters by exporting different environment variables.

(2) Add the following masked variables to your GitLab repository (CI/CD -> Variables):

- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.

- `OPENAI_KEY`: Your OpenAI key.

Note that if your base branches are not protected, don't set the variables as `protected`, since the pipeline will not have access to them.

> **Note**: The `$CI_SERVER_FQDN` variable is available starting from GitLab version 16.10. If you're using an earlier version, this variable will not be available. However, you can combine `$CI_SERVER_HOST` and `$CI_SERVER_PORT` to achieve the same result. Please ensure you're using a compatible version or adjust your configuration.

> **Note**: The `gitlab__SSL_VERIFY` environment variable can be used to specify the path to a custom CA certificate bundle for SSL verification. GitLab exposes the `$CI_SERVER_TLS_CA_FILE` variable, which points to the custom CA certificate file configured in your GitLab instance.
> Alternatively, SSL verification can be disabled entirely by setting `gitlab__SSL_VERIFY=false`, although this is not recommended.

## Run a GitLab webhook server

1. In GitLab create a new user and give it "Reporter" role for the intended group or project.

2. For the user from step 1, generate a `personal_access_token` with `api` access.

3. Generate a random secret for your app, and save it for later (`shared_secret`). For example, you can use:

```bash
SHARED_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))")
```

4. Clone this repository:

```bash
git clone https://github.com/qodo-ai/pr-agent.git
```

5. Prepare variables and secrets. Skip this step if you plan on setting these as environment variables when running the agent:
    1. In the configuration file/variables:
        - Set `config.git_provider` to "gitlab"

    2. In the secrets file/variables:
        - Set your AI model key in the respective section
        - In the [gitlab] section, set `personal_access_token` (with token from step 2) and `shared_secret` (with secret from step 3)
        - **Authentication type**: Set `auth_type` to `"private_token"` for older GitLab versions (e.g., 11.x) or private deployments. Default is `"oauth_token"` for gitlab.com and newer versions.

6. Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example:

```bash
docker build . -t gitlab_pr_agent --target gitlab_webhook -f docker/Dockerfile
docker push codiumai/pr-agent:gitlab_webhook  # Push to your Docker repository
```

7. Set the environmental variables, the method depends on your docker runtime. Skip this step if you included your secrets/configuration directly in the Docker image.

```bash
CONFIG__GIT_PROVIDER=gitlab
GITLAB__PERSONAL_ACCESS_TOKEN=<personal_access_token>
GITLAB__SHARED_SECRET=<shared_secret>
GITLAB__URL=https://gitlab.com
GITLAB__AUTH_TYPE=oauth_token  # Use "private_token" for older GitLab versions
OPENAI__KEY=<your_openai_api_key>
PORT=3000  # Optional: override the webhook server port
```

8. Create a webhook in your GitLab project. Set the URL to `http[s]://<PR_AGENT_HOSTNAME>/webhook`, the secret token to the generated secret from step 3, and enable the triggers `push`, `comments` and `merge request events`.

9. Test your installation by opening a merge request or commenting on a merge request using one of PR Agent's commands.

## Deploy as a Lambda Function

Note that since AWS Lambda env vars cannot have "." in the name, you can replace each "." in an env variable with "__".<br>
For example: `GITLAB.PERSONAL_ACCESS_TOKEN` --> `GITLAB__PERSONAL_ACCESS_TOKEN`

1. Follow steps 1-5 from [Run a GitLab webhook server](#run-a-gitlab-webhook-server).
2. Build a docker image that can be used as a lambda function

    ```shell
    docker buildx build --platform=linux/amd64 . -t codiumai/pr-agent:gitlab_lambda --target gitlab_lambda -f docker/Dockerfile.lambda
   ```

3. Push image to ECR

    ```shell
    docker tag codiumai/pr-agent:gitlab_lambda <AWS_ACCOUNT>.dkr.ecr.<AWS_REGION>.amazonaws.com/codiumai/pr-agent:gitlab_lambda
    docker push <AWS_ACCOUNT>.dkr.ecr.<AWS_REGION>.amazonaws.com/codiumai/pr-agent:gitlab_lambda
    ```

4. Create a lambda function that uses the uploaded image. Set the lambda timeout to be at least 3m.
5. Configure the lambda function to have a Function URL.
6. In the environment variables of the Lambda function, specify `AZURE_DEVOPS_CACHE_DIR` to a writable location such as /tmp. (see [link](https://github.com/qodo-ai/pr-agent/pull/450#issuecomment-1840242269))
7. Go back to steps 8-9 of [Run a GitLab webhook server](#run-a-gitlab-webhook-server) with the function URL as your Webhook URL.
    The Webhook URL would look like `https://<LAMBDA_FUNCTION_URL>/webhook`

### Using AWS Secrets Manager

For production Lambda deployments, use AWS Secrets Manager instead of environment variables:

1. Create individual secrets for each GitLab webhook with this JSON format (e.g., secret name: `project-webhook-secret-001`)

```json
{
  "gitlab_token": "glpat-xxxxxxxxxxxxxxxxxxxxxxxx",
  "token_name": "project-webhook-001"
}
```

2. Create a main configuration secret for common settings (e.g., secret name: `pr-agent-main-config`)

```json
{
  "openai.key": "sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

3. Set these environment variables in your Lambda:

```bash
CONFIG__SECRET_PROVIDER=aws_secrets_manager
AWS_SECRETS_MANAGER__SECRET_ARN=arn:aws:secretsmanager:us-east-1:123456789012:secret:pr-agent-main-config-AbCdEf
```

4. In your GitLab webhook configuration, set the **Secret Token** to the **Secret name** created in step 1:
   - Example: `project-webhook-secret-001`

**Important**: When using Secrets Manager, GitLab's webhook secret must be the Secrets Manager secret name.

5. Add IAM permission `secretsmanager:GetSecretValue` to your Lambda execution role


================================================
FILE: docs/docs/installation/index.md
================================================
# Installation

There are several ways to use PR-Agent:

- [Locally](./locally.md)
- [GitHub integration](./github.md)
- [GitLab integration](./gitlab.md)
- [BitBucket integration](./bitbucket.md)
- [Azure DevOps integration](./azure.md)
- [Gitea integration](./gitea.md)


================================================
FILE: docs/docs/installation/locally.md
================================================
To run PR-Agent locally, you first need to acquire two keys:

1. An OpenAI key from [here](https://platform.openai.com/api-keys){:target="_blank"}, with access to GPT-4 and o4-mini (or a key for other [language models](../usage-guide/changing_a_model.md), if you prefer).
2. A personal access token from your Git platform (GitHub, GitLab, BitBucket,Gitea) with repo scope. GitHub token, for example, can be issued from [here](https://github.com/settings/tokens){:target="_blank"}

## Using Docker image

A list of the relevant tools can be found in the [tools guide](../tools/).

To invoke a tool (for example `review`), you can run PR-Agent directly from the Docker image. Here's how:

- For GitHub:

    ```bash
    docker run --rm -it -e OPENAI.KEY=<your_openai_key> -e GITHUB.USER_TOKEN=<your_github_token> codiumai/pr-agent:latest --pr_url <pr_url> review
    ```

    If you are using GitHub enterprise server, you need to specify the custom url as variable.
    For example, if your GitHub server is at `https://github.mycompany.com`, add the following to the command:

    ```bash
    -e GITHUB.BASE_URL=https://github.mycompany.com/api/v3
    ```

- For GitLab:

    ```bash
    docker run --rm -it -e OPENAI.KEY=<your key> -e CONFIG.GIT_PROVIDER=gitlab -e GITLAB.PERSONAL_ACCESS_TOKEN=<your token> codiumai/pr-agent:latest --pr_url <pr_url> review
    ```

    If you have a dedicated GitLab instance, you need to specify the custom url as variable:

    ```bash
    -e GITLAB.URL=<your gitlab instance url>
    ```

- For BitBucket:

    ```bash
    docker run --rm -it -e CONFIG.GIT_PROVIDER=bitbucket -e OPENAI.KEY=$OPENAI_API_KEY -e BITBUCKET.BEARER_TOKEN=$BITBUCKET_BEARER_TOKEN codiumai/pr-agent:latest --pr_url=<pr_url> review
    ```

- For Gitea:

    ```bash
    docker run --rm -it -e OPENAI.KEY=<your key> -e CONFIG.GIT_PROVIDER=gitea -e GITEA.PERSONAL_ACCESS_TOKEN=<your token> codiumai/pr-agent:latest --pr_url <pr_url> review
    ```

    If you have a dedicated Gitea instance, you need to specify the custom url as variable:

    ```bash
    -e GITEA.URL=<your gitea instance url>
    ```


For other git providers, update `CONFIG.GIT_PROVIDER` accordingly and check the [`pr_agent/settings/.secrets_template.toml`](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/.secrets_template.toml) file for environment variables expected names and values.

### Utilizing environment variables

It is also possible to provide or override the configuration by setting the corresponding environment variables.
You can define the corresponding environment variables by following this convention: `<TABLE>__<KEY>=<VALUE>` or `<TABLE>.<KEY>=<VALUE>`.
The `<TABLE>` refers to a table/section in a configuration file and `<KEY>=<VALUE>` refers to the key/value pair of a setting in the configuration file.

For example, suppose you want to run `pr_agent` that connects to a self-hosted GitLab instance similar to an example above.
You can define the environment variables in a plain text file named `.env` with the following content:

```bash
CONFIG__GIT_PROVIDER="gitlab"
GITLAB__URL="<your url>"
GITLAB__PERSONAL_ACCESS_TOKEN="<your token>"
OPENAI__KEY="<your key>"
```

Then, you can run `pr_agent` using Docker with the following command:

```shell
docker run --rm -it --env-file .env codiumai/pr-agent:latest <tool> <tool parameter>
```

---

### I get an error when running the Docker image. What should I do?

If you encounter an error when running the Docker image, it is almost always due to a misconfiguration of api keys or tokens.

Note that litellm, which is used by pr-agent, sometimes returns non-informative error messages such as `APIError: OpenAIException - Connection error.`
Carefully check the api keys and tokens you provided and make sure they are correct.
Adjustments may be needed depending on your llm provider.

For example, for Azure OpenAI, additional keys are [needed](../usage-guide/changing_a_model.md#azure).
Same goes for other providers, make sure to check the [documentation](../usage-guide/changing_a_model.md#changing-a-model)

## Using pip package

Install the package:

```bash
pip install pr-agent
```

Then run the relevant tool with the script below.
<br>
Make sure to fill in the required parameters (`user_token`, `openai_key`, `pr_url`, `command`):

```python
from pr_agent import cli
from pr_agent.config_loader import get_settings

def main():
    # Fill in the following values
    provider = "github" # github/gitlab/bitbucket/azure_devops
    user_token = "..."  #  user token
    openai_key = "..."  # OpenAI key
    pr_url = "..."      # PR URL, for example 'https://github.com/qodo-ai/pr-agent/pull/809'
    command = "/review" # Command to run (e.g. '/review', '/describe', '/ask="What is the purpose of this PR?"', ...)

    # Setting the configurations
    get_settings().set("CONFIG.git_provider", provider)
    get_settings().set("openai.key", openai_key)
    get_settings().set("github.user_token", user_token)

    # Run the command. Feedback will appear in GitHub PR comments
    cli.run_command(pr_url, command)


if __name__ == '__main__':
    main()
```

## Run from source

1. Clone this repository:

```bash
git clone https://github.com/qodo-ai/pr-agent.git
```

2. Navigate to the `/pr-agent` folder and install the requirements in your favorite virtual environment:

```bash
pip install -e .
```

*Note: If you get an error related to Rust in the dependency installation then make sure Rust is installed and in your `PATH`, instructions: https://rustup.rs*

3. Copy the secrets template file and fill in your OpenAI key and your GitHub user token:

```bash
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
chmod 600 pr_agent/settings/.secrets.toml
# Edit .secrets.toml file
```

4. Run the cli.py script:

```bash
python3 -m pr_agent.cli --pr_url <pr_url> review
python3 -m pr_agent.cli --pr_url <pr_url> ask <your question>
python3 -m pr_agent.cli --pr_url <pr_url> describe
python3 -m pr_agent.cli --pr_url <pr_url> improve
python3 -m pr_agent.cli --pr_url <pr_url> add_docs
python3 -m pr_agent.cli --pr_url <pr_url> generate_labels
python3 -m pr_agent.cli --issue_url <issue_url> similar_issue
...
```

[Optional] Add the pr_agent folder to your PYTHONPATH

```bash
export PYTHONPATH=$PYTHONPATH:<PATH to pr_agent folder>
```


================================================
FILE: docs/docs/installation/pr_agent.md
================================================
# PR-Agent Installation Guide

PR-Agent can be deployed in various environments and platforms. Choose the installation method that best suits your needs:

## 🖥️ Local Installation

Learn how to run PR-Agent locally using:

- Docker image
- pip package
- CLI from source code

[View Local Installation Guide →](./locally.md)

## 🐙 GitHub Integration

Set up PR-Agent with GitHub as:

- GitHub Action
- Local GitHub App

[View GitHub Integration Guide →](./github.md)

## 🦊 GitLab Integration

Deploy PR-Agent on GitLab as:

- GitLab pipeline job
- Local GitLab webhook server

[View GitLab Integration Guide →](./gitlab.md)

## 🟦 BitBucket Integration

Implement PR-Agent in BitBucket as:

- BitBucket pipeline job
- Local BitBucket server

[View BitBucket Integration Guide →](./bitbucket.md)

## 🔷  Azure DevOps Integration

Configure PR-Agent with Azure DevOps as:

- Azure DevOps pipeline job
- Local Azure DevOps webhook

[View Azure DevOps Integration Guide →](./azure.md)


================================================
FILE: docs/docs/overview/data_privacy.md
================================================
## Self-hosted PR-Agent

- If you self-host PR-Agent with your OpenAI (or other LLM provider) API key, it is between you and the provider.


================================================
FILE: docs/docs/summary.md
================================================
# Table of contents

* [Overview](index.md)
  * [Data Privacy](overview/data_privacy.md)

## Installation

* [Installation](installation/index.md)
* [PR-Agent](installation/pr_agent.md)

## Usage Guide

* [Usage Guide](usage-guide/index.md)
* [Introduction](usage-guide/introduction.md)
* [Configuration File](usage-guide/configuration_options.md)
* [Usage and Automation](usage-guide/automations_and_usage.md)
* [Managing Mail Notifications](usage-guide/mail_notifications.md)
* [Changing a Model](usage-guide/changing_a_model.md)
* [Additional Configurations](usage-guide/additional_configurations.md)
* [Frequently Asked Questions](faq/index.md)

## Tools

* [Tools](tools/index.md)
* [Describe](tools/describe.md)
* [Review](tools/review.md)
* [Improve](tools/improve.md)
* [Ask](tools/ask.md)
* [Add Docs](tools/add_docs.md)
* [Generate Labels](tools/generate_labels.md)
* [Similar Issues](tools/similar_issues.md)
* [Help](tools/help.md)
* [Help Docs](tools/help_docs.md)
* [Update Changelog](tools/update_changelog.md)

## Core Abilities

* [Core Abilities](core-abilities/index.md)
* [Chat on code suggestions](core-abilities/interactivity.md)
* [Compression strategy](core-abilities/compression_strategy.md)
* [Dynamic context](core-abilities/dynamic_context.md)
* [Fetching ticket context](core-abilities/fetching_ticket_context.md)
* [Interactivity](core-abilities/interactivity.md)
* [Local and global metadata](core-abilities/metadata.md)
* [Self-reflection](core-abilities/self_reflection.md)


================================================
FILE: docs/docs/tools/add_docs.md
================================================
## Overview

The `add_docs` tool scans the PR code changes and suggests documentation for any code components that are missing documentation, such as functions, classes, and methods.

It can be invoked manually by commenting on any PR:

```
/add_docs
```

## Example usage

Invoke the tool manually by commenting `/add_docs` on any PR:

![Add Docs](https://codium.ai/images/pr_agent/add_docs_comment.png){width=512}

The tool will generate documentation suggestions as inline code suggestions:

![Add Docs Result](https://codium.ai/images/pr_agent/add_docs_result.png){width=512}

### Language-specific documentation styles

The tool automatically detects the programming language and generates documentation in the appropriate format:

| Language | Documentation Format |
|----------|---------------------|
| Python | Docstrings (Sphinx, Google, Numpy styles) |
| Java | Javadocs |
| JavaScript/TypeScript | JSdocs |
| C++ | Doxygen |
| Other | Generic documentation |

## Configuration options

Under the section `[pr_add_docs]`, the following options are available:

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `extra_instructions` | string | `""` | Additional instructions for the AI model |
| `docs_style` | string | `"Sphinx"` | Documentation style for Python. Options: `"Sphinx"`, `"Google Style with Args, Returns, Attributes...etc"`, `"Numpy Style"`, `"PEP257"`, `"reStructuredText"` |
| `file` | string | `""` | Specific file to document (useful when multiple components have the same name) |
| `class_name` | string | `""` | Specific class name to target (useful when methods have the same name in the same file) |

### Example configuration

To customize the documentation style, add the following to your configuration file:

```toml
[pr_add_docs]
docs_style = "Google Style with Args, Returns, Attributes...etc"
extra_instructions = "Focus on documenting public methods and include usage examples"
```

### Command line options

You can pass configuration options directly in the command:

```
/add_docs --pr_add_docs.docs_style="Numpy Style"
```

## How it works

1. The tool analyzes the PR diff to identify code components (functions, classes, methods) that lack documentation
2. It uses AI to generate appropriate documentation based on the code context and language
3. Documentation suggestions are published as inline code suggestions that can be applied with a single click


================================================
FILE: docs/docs/tools/ask.md
================================================
## Overview

The `ask` tool answers questions about the PR, based on the PR code changes. Make sure to be specific and clear in your questions.
It can be invoked manually by commenting on any PR:

```
/ask "..."
```

## Example usage

![Ask Comment](https://codium.ai/images/pr_agent/ask_comment.png){width=512}

![Ask](https://codium.ai/images/pr_agent/ask.png){width=512}

## Ask lines

You can run `/ask` on specific lines of code in the PR from the PR's diff view. The tool will answer questions based on the code changes in the selected lines.

- Click on the '+' sign next to the line number to select the line.
- To select multiple lines, click on the '+' sign of the first line and then hold and drag to select the rest of the lines.
- write `/ask "..."` in the comment box and press `Add single comment` button.

![Ask Line](https://codium.ai/images/pr_agent/Ask_line.png){width=512}

Note that the tool does not have "memory" of previous questions, and answers each question independently.

## Ask on images

You can also ask questions about images that appear in the comment, where the entire PR code will be used as context.
<br>
The basic syntax is:

```
/ask "..."

[Image](https://real_link_to_image)
```

where `https://real_link_to_image` is the direct link to the image.

Note that GitHub has a built-in mechanism of pasting images in comments. However, pasted image does not provide a direct link.
To get a direct link to an image, we recommend using the following scheme:

1\. First, post a comment that contains **only** the image:

![Ask image1](https://codium.ai/images/pr_agent/ask_images1.png){width=512}

2\. Quote reply to that comment:

![Ask image2](https://codium.ai/images/pr_agent/ask_images2.png){width=512}

3\. In the screen opened, type the question below the image:

![Ask image3](https://codium.ai/images/pr_agent/ask_images3.png){width=512}
![Ask image4](https://codium.ai/images/pr_agent/ask_images4.png){width=512}

4\. Post the comment, and receive the answer:

![Ask image5](https://codium.ai/images/pr_agent/ask_images5.png){width=512}

See a full video tutorial [here](https://codium.ai/images/pr_agent/ask_image_video.mov)


================================================
FILE: docs/docs/tools/describe.md
================================================
## Overview

The `describe` tool scans the PR code changes, and generates a description for the PR - title, type, summary, walkthrough and labels.

The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR:

```
/describe
```

## Example usage

### Manual triggering

Invoke the tool manually by commenting `/describe` on any PR:

![Describe comment](https://codium.ai/images/pr_agent/describe_comment.png){width=512}

After ~30 seconds, the tool will generate a description for the PR:

![Describe New](https://codium.ai/images/pr_agent/describe_new.png){width=512}

If you want to edit [configurations](#configuration-options), add the relevant ones to the command:

```
/describe --pr_description.some_config1=... --pr_description.some_config2=...
```

### Automatic triggering

To run the `describe` automatically when a PR is opened, define in a [configuration file](../usage-guide/configuration_options.md#wiki-configuration-file):

```
[github_app]
pr_commands = [
    "/describe",
    ...
]

[pr_description]
publish_labels = true
...
```

- The `pr_commands` lists commands that will be executed automatically when a PR is opened.
- The `[pr_description]` section contains the configurations for the `describe` tool you want to edit (if any).

## Preserving the original user description

By default, PR-Agent tries to preserve your original PR description by placing it above the generated content.
This requires including your description during the initial PR creation.

"PR-Agent removed the original description from the PR. Why"?

From our experience, there are two possible reasons:

- If you edit the description _while_ the automated tool is running, a race condition may occur, potentially causing your original description to be lost. Hence, create a description before launching the PR.

- When _updating_ PR descriptions, the `/describe` tool considers everything above the "PR Type" field as user content and will preserve it.
Everything below this marker is treated as previously auto-generated content and will be replaced.

![Describe comment](https://codium.ai/images/pr_agent/pr_description_user_description.png){width=512}

## Sequence Diagram Support 
The `/describe` tool includes a Mermaid sequence diagram showing component/function interactions. 

This option is enabled by default via the `pr_description.enable_pr_diagram` param.


[//]: # (### How to enable\disable)

[//]: # ()
[//]: # (In your configuration:)

[//]: # ()
[//]: # (```)

[//]: # (toml)

[//]: # ([pr_description])

[//]: # (enable_pr_diagram = true)

[//]: # (```)

## Configuration options

???+ example "Possible configurations"

    <table>
      <tr>
        <td><b>publish_labels</b></td>
        <td>If set to true, the tool will publish labels to the PR. Default is false.</td>
      </tr>
      <tr>
        <td><b>publish_description_as_comment</b></td>
        <td>If set to true, the tool will publish the description as a comment to the PR. If false, it will overwrite the original description. Default is false.</td>
      </tr>
      <tr>
        <td><b>publish_description_as_comment_persistent</b></td>
        <td>If set to true and `publish_description_as_comment` is true, the tool will publish the description as a persistent comment to the PR. Default is true.</td>
      </tr>
      <tr>
        <td><b>add_original_user_description</b></td>
        <td>If set to true, the tool will add the original user description to the generated description. Default is true.</td>
      </tr>
      <tr>
        <td><b>generate_ai_title</b></td>
        <td>If set to true, the tool will also generate an AI title for the PR. Default is false.</td>
      </tr>
      <tr>
        <td><b>extra_instructions</b></td>
        <td>Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ..."</td>
      </tr>
      <tr>
        <td><b>enable_pr_type</b></td>
        <td>If set to false, it will not show the `PR type` as a text value in the description content. Default is true.</td>
      </tr>
      <tr>
        <td><b>final_update_message</b></td>
        <td>If set to true, it will add a comment message [`PR Description updated to latest commit...`](https://github.com/qodo-ai/pr-agent/pull/499#issuecomment-1837412176) after finishing calling `/describe`. Default is true.</td>
      </tr>
      <tr>
        <td><b>enable_semantic_files_types</b></td>
        <td>If set to true, "Changes walkthrough" section will be generated. Default is true.</td>
      </tr>
      <tr>
            <td><b>file_table_collapsible_open_by_default</b></td>
            <td>If set to true, the file list in the "Changes walkthrough" section will be open by default. If set to false, it will be closed by default. Default is false.</td>
      </tr>
      <tr>
        <td><b>collapsible_file_list</b></td>
        <td>If set to true, the file list in the "Changes walkthrough" section will be collapsible. If set to "adaptive", the file list will be collapsible only if there are more than 8 files. Default is "adaptive".</td>
      </tr>
      <tr>
        <td><b>enable_large_pr_handling</b></td>
        <td>If set to true, in case of a large PR the tool will make several calls to the AI and combine them to be able to cover more files. Default is true.</td>
      </tr>
      <tr>
        <td><b>enable_help_text</b></td>
        <td>If set to true, the tool will display a help text in the comment. Default is false.</td>
      </tr>
      <tr>
        <td><b>enable_pr_diagram</b></td>
        <td>If set to true, the tool will generate a horizontal Mermaid flowchart summarizing the main pull request changes. This field remains empty if not applicable. Default is true.</td>
      </tr>
      <tr>
        <td><b>auto_create_ticket</b></td>
        <td>If set to true, this will automatically create a ticket in the ticketing system when a PR is opened. Default is false.</td>
      </tr>
    </table>

## Markers template

To enable markers, set `pr_description.use_description_markers=true`.
Markers enable to easily integrate user's content and auto-generated content, with a template-like mechanism.

For example, if the PR original description was:

```
User content...

## PR Type:
pr_agent:type

## PR Description:
pr_agent:summary

## PR Walkthrough:
pr_agent:walkthrough

## PR Diagram:
pr_agent:diagram
```

The marker `pr_agent:type` will be replaced with the PR type, `pr_agent:summary` will be replaced with the PR summary, `pr_agent:walkthrough` will be replaced with the PR walkthrough, and `pr_agent:diagram` will be replaced with the sequence diagram (if enabled).

![Describe markers before](https://codium.ai/images/pr_agent/describe_markers_before.png){width=512}

becomes

![Describe markers after](https://codium.ai/images/pr_agent/describe_markers_after.png){width=512}

**Configuration params**:

- `use_description_markers`: if set to true, the tool will use markers template. It replaces every marker of the form `pr_agent:marker_name` with the relevant content. Default is false.
- `include_generated_by_header`: if set to true, the tool will add a dedicated header: 'Generated by PR Agent at ...' to any automatic content. Default is true.
- `diagram`: if present as a marker, will be replaced by the PR sequence diagram (if enabled).

## Custom labels

The default labels of the describe tool are quite generic, since they are meant to be used in any repo: [`Bug fix`, `Tests`, `Enhancement`, `Documentation`, `Other`].

You can define custom labels that are relevant for your repo and use cases.
Custom labels can be defined in a configuration file, or directly in the repo's [labels page](#handle-custom-labels-from-the-repos-labels-page).

Make sure to provide proper title, and a detailed and well-phrased description for each label, so the tool will know when to suggest it.
Each label description should be a **conditional statement**, that indicates if to add the label to the PR or not, according to the PR content.

???+ tip "Auto-remove custom label when no longer relevant"
    If the custom label is no longer relevant, it will be automatically removed from the PR by running the `generate_labels` tool or the `describe` tool.


### Handle custom labels from a configuration file

Example for a custom labels configuration setup in a configuration file:

```
[config]
enable_custom_labels=true


[custom_labels."sql_changes"]
description = "Use when a PR contains changes to SQL queries"

[custom_labels."test"]
description = "use when a PR primarily contains new tests"

...
```

### Handle custom labels from the Repo's labels page

You can also control the custom labels that will be suggested by the `describe` tool from the repo's labels page:

- GitHub : go to `https://github.com/{owner}/{repo}/labels` (or click on the "Labels" tab in the issues or PRs page)
- GitLab : go to `https://gitlab.com/{owner}/{repo}/-/labels` (or click on "Manage" -> "Labels" on the left menu)

Now add/edit the custom labels. they should be formatted as follows:

- Label name: The name of the custom label.
- Description: Start the description of with prefix `pr_agent:`, for example: `pr_agent: Description of when
Download .txt
gitextract_ey74c0jr/

├── .dockerignore
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   ├── config.yml
│   │   ├── feature-request.yml
│   │   └── miscellaneous.yml
│   └── workflows/
│       ├── build-and-test.yaml
│       ├── code_coverage.yaml
│       ├── docs-ci.yaml
│       ├── e2e_tests.yaml
│       ├── pr-agent-review.yaml
│       └── pre-commit.yml
├── .gitignore
├── .pr_agent.toml
├── .pre-commit-config.yaml
├── AGENTS.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile.github_action
├── Dockerfile.github_action_dockerhub
├── LICENSE
├── MANIFEST.in
├── README.md
├── RELEASE_NOTES.md
├── SECURITY.md
├── action.yaml
├── codecov.yml
├── docker/
│   ├── Dockerfile
│   └── Dockerfile.lambda
├── docs/
│   ├── README.md
│   ├── docs/
│   │   ├── .gitbook.yaml
│   │   ├── CNAME
│   │   ├── core-abilities/
│   │   │   ├── compression_strategy.md
│   │   │   ├── dynamic_context.md
│   │   │   ├── fetching_ticket_context.md
│   │   │   ├── index.md
│   │   │   ├── interactivity.md
│   │   │   ├── metadata.md
│   │   │   └── self_reflection.md
│   │   ├── css/
│   │   │   └── custom.css
│   │   ├── faq/
│   │   │   └── index.md
│   │   ├── index.md
│   │   ├── installation/
│   │   │   ├── azure.md
│   │   │   ├── bitbucket.md
│   │   │   ├── gitea.md
│   │   │   ├── github.md
│   │   │   ├── gitlab.md
│   │   │   ├── index.md
│   │   │   ├── locally.md
│   │   │   └── pr_agent.md
│   │   ├── overview/
│   │   │   └── data_privacy.md
│   │   ├── summary.md
│   │   ├── tools/
│   │   │   ├── add_docs.md
│   │   │   ├── ask.md
│   │   │   ├── describe.md
│   │   │   ├── generate_labels.md
│   │   │   ├── help.md
│   │   │   ├── help_docs.md
│   │   │   ├── improve.md
│   │   │   ├── index.md
│   │   │   ├── review.md
│   │   │   ├── similar_issues.md
│   │   │   └── update_changelog.md
│   │   └── usage-guide/
│   │       ├── EXAMPLE_BEST_PRACTICE.md
│   │       ├── additional_configurations.md
│   │       ├── automations_and_usage.md
│   │       ├── changing_a_model.md
│   │       ├── configuration_options.md
│   │       ├── index.md
│   │       ├── introduction.md
│   │       └── mail_notifications.md
│   ├── mkdocs.yml
│   └── overrides/
│       ├── main.html
│       └── partials/
│           ├── footer.html
│           └── integrations/
│               └── analytics/
│                   └── custom.html
├── github_action/
│   └── entrypoint.sh
├── pr_agent/
│   ├── __init__.py
│   ├── agent/
│   │   ├── __init__.py
│   │   └── pr_agent.py
│   ├── algo/
│   │   ├── __init__.py
│   │   ├── ai_handlers/
│   │   │   ├── base_ai_handler.py
│   │   │   ├── langchain_ai_handler.py
│   │   │   ├── litellm_ai_handler.py
│   │   │   ├── litellm_helpers.py
│   │   │   └── openai_ai_handler.py
│   │   ├── cli_args.py
│   │   ├── file_filter.py
│   │   ├── git_patch_processing.py
│   │   ├── language_handler.py
│   │   ├── pr_processing.py
│   │   ├── token_handler.py
│   │   ├── types.py
│   │   └── utils.py
│   ├── cli.py
│   ├── cli_pip.py
│   ├── config_loader.py
│   ├── custom_merge_loader.py
│   ├── git_providers/
│   │   ├── __init__.py
│   │   ├── azuredevops_provider.py
│   │   ├── bitbucket_provider.py
│   │   ├── bitbucket_server_provider.py
│   │   ├── codecommit_client.py
│   │   ├── codecommit_provider.py
│   │   ├── gerrit_provider.py
│   │   ├── git_provider.py
│   │   ├── gitea_provider.py
│   │   ├── github_provider.py
│   │   ├── gitlab_provider.py
│   │   ├── local_git_provider.py
│   │   └── utils.py
│   ├── identity_providers/
│   │   ├── __init__.py
│   │   ├── default_identity_provider.py
│   │   └── identity_provider.py
│   ├── log/
│   │   └── __init__.py
│   ├── secret_providers/
│   │   ├── __init__.py
│   │   ├── aws_secrets_manager_provider.py
│   │   ├── google_cloud_storage_secret_provider.py
│   │   └── secret_provider.py
│   ├── servers/
│   │   ├── __init__.py
│   │   ├── atlassian-connect-qodo-merge.json
│   │   ├── atlassian-connect.json
│   │   ├── azuredevops_server_webhook.py
│   │   ├── bitbucket_app.py
│   │   ├── bitbucket_server_webhook.py
│   │   ├── gerrit_server.py
│   │   ├── gitea_app.py
│   │   ├── github_action_runner.py
│   │   ├── github_app.py
│   │   ├── github_lambda_webhook.py
│   │   ├── github_polling.py
│   │   ├── gitlab_lambda_webhook.py
│   │   ├── gitlab_webhook.py
│   │   ├── gunicorn_config.py
│   │   ├── help.py
│   │   └── utils.py
│   ├── settings/
│   │   ├── .secrets_template.toml
│   │   ├── code_suggestions/
│   │   │   ├── pr_code_suggestions_prompts.toml
│   │   │   ├── pr_code_suggestions_prompts_not_decoupled.toml
│   │   │   └── pr_code_suggestions_reflect_prompts.toml
│   │   ├── configuration.toml
│   │   ├── custom_labels.toml
│   │   ├── generated_code_ignore.toml
│   │   ├── ignore.toml
│   │   ├── language_extensions.toml
│   │   ├── pr_add_docs.toml
│   │   ├── pr_custom_labels.toml
│   │   ├── pr_description_prompts.toml
│   │   ├── pr_evaluate_prompt_response.toml
│   │   ├── pr_help_docs_headings_prompts.toml
│   │   ├── pr_help_docs_prompts.toml
│   │   ├── pr_help_prompts.toml
│   │   ├── pr_information_from_user_prompts.toml
│   │   ├── pr_line_questions_prompts.toml
│   │   ├── pr_questions_prompts.toml
│   │   ├── pr_reviewer_prompts.toml
│   │   └── pr_update_changelog_prompts.toml
│   └── tools/
│       ├── __init__.py
│       ├── pr_add_docs.py
│       ├── pr_code_suggestions.py
│       ├── pr_config.py
│       ├── pr_description.py
│       ├── pr_generate_labels.py
│       ├── pr_help_docs.py
│       ├── pr_help_message.py
│       ├── pr_line_questions.py
│       ├── pr_questions.py
│       ├── pr_reviewer.py
│       ├── pr_similar_issue.py
│       ├── pr_update_changelog.py
│       └── ticket_pr_compliance_check.py
├── pr_compliance_checklist.yaml
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── setup.py
└── tests/
    ├── e2e_tests/
    │   ├── e2e_utils.py
    │   ├── langchain_ai_handler.py
    │   ├── test_bitbucket_app.py
    │   ├── test_gitea_app.py
    │   ├── test_github_app.py
    │   └── test_gitlab_webhook.py
    ├── health_test/
    │   └── main.py
    └── unittest/
        ├── test_add_docs_trigger.py
        ├── test_aws_secrets_manager_provider.py
        ├── test_azure_devops_comment.py
        ├── test_azure_devops_parsing.py
        ├── test_bitbucket_provider.py
        ├── test_clip_tokens.py
        ├── test_codecommit_client.py
        ├── test_codecommit_provider.py
        ├── test_config_loader_secrets.py
        ├── test_convert_to_markdown.py
        ├── test_delete_hunks.py
        ├── test_extend_patch.py
        ├── test_extract_issue_from_branch.py
        ├── test_fetching_sub_issues.py
        ├── test_file_filter.py
        ├── test_find_line_number_of_relevant_line_in_file.py
        ├── test_fix_json_escape_char.py
        ├── test_fix_output.py
        ├── test_fresh_vars_functionality.py
        ├── test_get_max_tokens.py
        ├── test_gitea_provider.py
        ├── test_github_action_output.py
        ├── test_gitlab_provider.py
        ├── test_gitlab_webhook_port.py
        ├── test_handle_patch_deletions.py
        ├── test_ignore_repositories.py
        ├── test_language_handler.py
        ├── test_litellm_reasoning_effort.py
        ├── test_load_yaml.py
        ├── test_parse_code_suggestion.py
        ├── test_pr_update_changelog.py
        ├── test_secret_provider_factory.py
        ├── test_similar_issue_non_github.py
        └── test_try_fix_yaml.py
Download .txt
SYMBOL INDEX (1226 symbols across 104 files)

FILE: pr_agent/agent/pr_agent.py
  class PRAgent (line 50) | class PRAgent:
    method __init__ (line 51) | def __init__(self, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHand...
    method _handle_request (line 54) | async def _handle_request(self, pr_url, request, notify=None) -> bool:
    method handle_request (line 121) | async def handle_request(self, pr_url, request, notify=None) -> bool:

FILE: pr_agent/algo/ai_handlers/base_ai_handler.py
  class BaseAiHandler (line 4) | class BaseAiHandler(ABC):
    method __init__ (line 10) | def __init__(self):
    method deployment_id (line 15) | def deployment_id(self):
    method chat_completion (line 19) | async def chat_completion(self, model: str, system: str, user: str, te...

FILE: pr_agent/algo/ai_handlers/langchain_ai_handler.py
  class LangChainOpenAIHandler (line 23) | class LangChainOpenAIHandler(BaseAiHandler):
    method __init__ (line 24) | def __init__(self):
    method deployment_id (line 34) | def deployment_id(self):
    method _create_chat_async (line 40) | async def _create_chat_async(self, deployment_id=None):
    method chat_completion (line 70) | async def chat_completion(self, model: str, system: str, user: str, te...

FILE: pr_agent/algo/ai_handlers/litellm_ai_handler.py
  class LiteLLMAIHandler (line 20) | class LiteLLMAIHandler(BaseAiHandler):
    method __init__ (line 27) | def __init__(self):
    method prepare_logs (line 153) | def prepare_logs(self, response, system, user, resp, finish_reason):
    method _configure_claude_extended_thinking (line 165) | def _configure_claude_extended_thinking(self, model: str, kwargs: dict...
    method add_litellm_callbacks (line 202) | def add_litellm_callbacks(self, kwargs) -> dict:
    method deployment_id (line 257) | def deployment_id(self):
    method chat_completion (line 267) | async def chat_completion(self, model: str, system: str, user: str, te...
    method _get_completion (line 432) | async def _get_completion(self, **kwargs):

FILE: pr_agent/algo/ai_handlers/litellm_helpers.py
  function _handle_streaming_response (line 9) | async def _handle_streaming_response(response):
  class MockResponse (line 45) | class MockResponse:
    method __init__ (line 48) | def __init__(self, resp, finish_reason):
    method dict (line 58) | def dict(self):
  function _get_azure_ad_token (line 62) | def _get_azure_ad_token():
  function _process_litellm_extra_body (line 83) | def _process_litellm_extra_body(kwargs: dict) -> dict:

FILE: pr_agent/algo/ai_handlers/openai_ai_handler.py
  class OpenAIHandler (line 14) | class OpenAIHandler(BaseAiHandler):
    method __init__ (line 15) | def __init__(self):
    method deployment_id (line 35) | def deployment_id(self):
    method chat_completion (line 45) | async def chat_completion(self, model: str, system: str, user: str, te...

FILE: pr_agent/algo/cli_args.py
  class CliArgs (line 4) | class CliArgs:
    method validate_user_args (line 6) | def validate_user_args(args: list) -> (bool, str):

FILE: pr_agent/algo/file_filter.py
  function filter_ignored (line 8) | def filter_ignored(files, platform = 'github'):
  function translate_globs_to_regexes (line 83) | def translate_globs_to_regexes(globs: list):

FILE: pr_agent/algo/git_patch_processing.py
  function extend_patch (line 11) | def extend_patch(original_file_str, patch_str, patch_extra_lines_before=0,
  function decode_if_bytes (line 34) | def decode_if_bytes(original_file_str):
  function should_skip_patch (line 49) | def should_skip_patch(filename):
  function process_patch_lines (line 56) | def process_patch_lines(patch_str, original_file_str, patch_extra_lines_...
  function check_if_hunk_lines_matches_to_file (line 187) | def check_if_hunk_lines_matches_to_file(i, original_lines, patch_lines, ...
  function extract_hunk_headers (line 214) | def extract_hunk_headers(match):
  function omit_deletion_hunks (line 228) | def omit_deletion_hunks(patch_lines) -> str:
  function handle_patch_deletions (line 267) | def handle_patch_deletions(patch: str, original_file_content_str: str,
  function decouple_and_convert_to_hunks_with_lines_numbers (line 300) | def decouple_and_convert_to_hunks_with_lines_numbers(patch: str, file) -...
  function extract_hunk_lines_from_patch (line 415) | def extract_hunk_lines_from_patch(patch: str, file_name, line_start, lin...

FILE: pr_agent/algo/language_handler.py
  function filter_bad_extensions (line 7) | def filter_bad_extensions(files):
  function is_valid_file (line 15) | def is_valid_file(filename:str, bad_extensions=None) -> bool:
  function sort_files_by_main_languages (line 31) | def sort_files_by_main_languages(languages: Dict, files: list):

FILE: pr_agent/algo/pr_processing.py
  function cap_and_log_extra_lines (line 31) | def cap_and_log_extra_lines(value, direction) -> int:
  function get_pr_diff (line 38) | def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler,
  function get_pr_diff_multiple_patchs (line 145) | def get_pr_diff_multiple_patchs(git_provider: GitProvider, token_handler...
  function pr_generate_extended_diff (line 167) | def pr_generate_extended_diff(pr_languages: list,
  function pr_generate_compressed_diff (line 210) | def pr_generate_compressed_diff(top_langs: list, token_handler: TokenHan...
  function generate_full_patch (line 279) | def generate_full_patch(convert_hunks_to_line_numbers, file_dict, max_to...
  function retry_with_fallback_models (line 320) | async def retry_with_fallback_models(f: Callable, model_type: ModelType ...
  function _get_all_models (line 341) | def _get_all_models(model_type: ModelType = ModelType.REGULAR) -> List[s...
  function _get_all_deployments (line 357) | def _get_all_deployments(all_models: List[str]) -> List[str]:
  function get_pr_multi_diffs (line 372) | def get_pr_multi_diffs(git_provider: GitProvider,
  function add_ai_metadata_to_diff_files (line 503) | def add_ai_metadata_to_diff_files(git_provider, pr_description_files):
  function add_ai_summary_top_patch (line 527) | def add_ai_summary_top_patch(file, full_extended_patch):

FILE: pr_agent/algo/token_handler.py
  class ModelTypeValidator (line 12) | class ModelTypeValidator:
    method is_openai_model (line 14) | def is_openai_model(model_name: str) -> bool:
    method is_anthropic_model (line 18) | def is_anthropic_model(model_name: str) -> bool:
  class TokenEncoder (line 22) | class TokenEncoder:
    method get_token_encoder (line 28) | def get_token_encoder(cls):
  class TokenHandler (line 42) | class TokenHandler:
    method __init__ (line 59) | def __init__(self, pr=None, vars: dict = {}, system="", user=""):
    method _get_system_user_tokens (line 74) | def _get_system_user_tokens(self, pr, encoder, vars: dict, system, user):
    method _calc_claude_tokens (line 99) | def _calc_claude_tokens(self, patch: str) -> int:
    method _apply_estimation_factor (line 127) | def _apply_estimation_factor(self, model_name: str, default_estimate: ...
    method _get_token_count_by_model_type (line 133) | def _get_token_count_by_model_type(self, patch: str, default_estimate:...
    method count_tokens (line 154) | def count_tokens(self, patch: str, force_accurate: bool = False) -> int:

FILE: pr_agent/algo/types.py
  class EDIT_TYPE (line 6) | class EDIT_TYPE(Enum):
  class FilePatchInfo (line 15) | class FilePatchInfo:

FILE: pr_agent/algo/utils.py
  function get_model (line 34) | def get_model(model_type: str = "model_weak") -> str:
  class Range (line 42) | class Range(BaseModel):
  class ModelType (line 49) | class ModelType(str, Enum):
  class TodoItem (line 55) | class TodoItem(TypedDict):
  class PRReviewHeader (line 61) | class PRReviewHeader(str, Enum):
  class ReasoningEffort (line 66) | class ReasoningEffort(str, Enum):
  class PRDescriptionHeader (line 75) | class PRDescriptionHeader(str, Enum):
  function get_setting (line 80) | def get_setting(key: str) -> Any:
  function emphasize_header (line 88) | def emphasize_header(text: str, only_markdown=False, reference_link=None...
  function unique_strings (line 116) | def unique_strings(input_list: List[str]) -> List[str]:
  function convert_to_markdown_v2 (line 128) | def convert_to_markdown_v2(output_data: dict,
  function extract_relevant_lines_str (line 328) | def extract_relevant_lines_str(end_line, files, relevant_file, start_lin...
  function ticket_markdown_logic (line 368) | def ticket_markdown_logic(emoji, markdown_text, value, gfm_supported) ->...
  function process_can_be_split (line 465) | def process_can_be_split(emoji, value):
  function parse_code_suggestion (line 519) | def parse_code_suggestion(code_suggestion: dict, i: int = 0, gfm_support...
  function try_fix_json (line 581) | def try_fix_json(review, max_iter=10, code_suggestions=False):
  function fix_json_escape_char (line 639) | def fix_json_escape_char(json_message=None):
  function convert_str_to_datetime (line 666) | def convert_str_to_datetime(date_str):
  function load_large_diff (line 684) | def load_large_diff(filename, new_file_content_str: str, original_file_c...
  function update_settings_from_args (line 706) | def update_settings_from_args(args: List[str]) -> List[str]:
  function _fix_key_value (line 742) | def _fix_key_value(key: str, value: str):
  function load_yaml (line 752) | def load_yaml(response_text: str, keys_fix_yaml: List[str] = [], first_k...
  function try_fix_yaml (line 771) | def try_fix_yaml(response_text: str,
  function set_custom_labels (line 941) | def set_custom_labels(variables, git_provider=None):
  function get_user_labels (line 966) | def get_user_labels(current_labels: List[str] = None):
  function get_max_tokens (line 991) | def get_max_tokens(model):
  function clip_tokens (line 1015) | def clip_tokens(text: str, max_tokens: int, add_three_dots=True, num_inp...
  function replace_code_tags (line 1113) | def replace_code_tags(text):
  function find_line_number_of_relevant_line_in_file (line 1124) | def find_line_number_of_relevant_line_in_file(diff_files: List[FilePatch...
  function get_rate_limit_status (line 1198) | def get_rate_limit_status(github_token) -> dict:
  function validate_rate_limit_github (line 1220) | def validate_rate_limit_github(github_token, installation_id=None, thres...
  function validate_and_await_rate_limit (line 1238) | def validate_and_await_rate_limit(github_token):
  function github_action_output (line 1257) | def github_action_output(output_data: dict, key_name: str):
  function show_relevant_configurations (line 1270) | def show_relevant_configurations(relevant_section: str) -> str:
  function is_value_no (line 1295) | def is_value_no(value):
  function set_pr_string (line 1304) | def set_pr_string(repo_name, pr_number):
  function string_to_uniform_number (line 1308) | def string_to_uniform_number(s: str) -> float:
  function process_description (line 1323) | def process_description(description_full: str) -> Tuple[str, List]:
  function get_version (line 1420) | def get_version() -> str:
  function set_file_languages (line 1442) | def set_file_languages(diff_files) -> List[FilePatchInfo]:
  function format_todo_item (line 1465) | def format_todo_item(todo_item: TodoItem, git_provider, gfm_supported) -...
  function format_todo_items (line 1484) | def format_todo_items(value: list[TodoItem] | TodoItem, git_provider, gf...

FILE: pr_agent/cli.py
  function set_parser (line 14) | def set_parser():
  function run_command (line 60) | def run_command(pr_url, command):
  function run (line 69) | def run(inargs=None, args=None):

FILE: pr_agent/cli_pip.py
  function main (line 5) | def main():

FILE: pr_agent/config_loader.py
  function get_settings (line 47) | def get_settings(use_context=False):
  function _find_repository_root (line 64) | def _find_repository_root() -> Optional[Path]:
  function _find_pyproject (line 78) | def _find_pyproject() -> Optional[Path]:
  function apply_secrets_manager_config (line 94) | def apply_secrets_manager_config():
  function apply_secrets_to_config (line 125) | def apply_secrets_to_config(secrets: dict):

FILE: pr_agent/custom_merge_loader.py
  function load (line 8) | def load(obj, env=None, silent=True, key=None, filename=None):
  function validate_file_security (line 101) | def validate_file_security(file_data, filename):

FILE: pr_agent/git_providers/__init__.py
  function get_git_provider (line 30) | def get_git_provider():
  function get_git_provider_with_context (line 40) | def get_git_provider_with_context(pr_url) -> GitProvider:

FILE: pr_agent/git_providers/azuredevops_provider.py
  class AzureDevopsProvider (line 35) | class AzureDevopsProvider(GitProvider):
    method __init__ (line 37) | def __init__(
    method publish_code_suggestions (line 57) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method reply_to_comment_from_comment_id (line 97) | def reply_to_comment_from_comment_id(self, comment_id: int, body: str,...
    method get_pr_description_full (line 101) | def get_pr_description_full(self) -> str:
    method edit_comment (line 104) | def edit_comment(self, comment: Comment, body: str):
    method remove_comment (line 117) | def remove_comment(self, comment: Comment):
    method publish_labels (line 129) | def publish_labels(self, pr_types):
    method get_pr_labels (line 141) | def get_pr_labels(self, update=False):
    method is_supported (line 153) | def is_supported(self, capability: str) -> bool:
    method set_pr (line 156) | def set_pr(self, pr_url: str):
    method get_repo_settings (line 161) | def get_repo_settings(self):
    method get_files (line 177) | def get_files(self):
    method get_diff_files (line 194) | def get_diff_files(self) -> list[FilePatchInfo]:
    method publish_comment (line 352) | def publish_comment(self, pr_comment: str, is_temporary: bool = False,...
    method publish_persistent_comment (line 372) | def publish_persistent_comment(self, pr_comment: str,
    method publish_description (line 379) | def publish_description(self, pr_title: str, pr_body: str):
    method remove_initial_comment (line 412) | def remove_initial_comment(self):
    method publish_inline_comment (line 419) | def publish_inline_comment(self, body: str, relevant_file: str, releva...
    method create_inline_comment (line 422) | def create_inline_comment(self, body: str, relevant_file: str, relevan...
    method publish_inline_comments (line 437) | def publish_inline_comments(self, comments: list[dict], disable_fallba...
    method get_title (line 463) | def get_title(self):
    method get_languages (line 466) | def get_languages(self):
    method get_pr_branch (line 495) | def get_pr_branch(self):
    method get_user_id (line 502) | def get_user_id(self):
    method get_issue_comments (line 505) | def get_issue_comments(self) -> list[Comment]:
    method add_eyes_reaction (line 517) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 520) | def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> ...
    method set_like (line 523) | def set_like(self, thread_id: int, comment_id: int, create: bool = True):
    method set_thread_status (line 529) | def set_thread_status(self, thread_id: int, status: str):
    method reply_to_thread (line 535) | def reply_to_thread(self, thread_id: int, body: str, is_temporary: boo...
    method get_thread_context (line 546) | def get_thread_context(self, thread_id: int) -> CommentThreadContext:
    method _parse_pr_url (line 554) | def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]:
    method _get_azure_devops_client (line 575) | def _get_azure_devops_client() -> Tuple[GitClient, WorkItemTrackingCli...
    method _get_repo (line 604) | def _get_repo(self):
    method _get_pr (line 611) | def _get_pr(self):
    method get_commit_messages (line 617) | def get_commit_messages(self):
    method get_pr_id (line 620) | def get_pr_id(self):
    method publish_file_comments (line 629) | def publish_file_comments(self, file_comments: list) -> bool:
    method get_line_link (line 632) | def get_line_link(self, relevant_file: str, relevant_line_start: int, ...
    method get_comment_url (line 635) | def get_comment_url(self, comment) -> str:
    method get_latest_commit_url (line 638) | def get_latest_commit_url(self) -> str:
    method get_linked_work_items (line 644) | def get_linked_work_items(self) -> list:
    method get_work_items (line 663) | def get_work_items(self, work_item_ids: list) -> list:

FILE: pr_agent/git_providers/bitbucket_provider.py
  function _gef_filename (line 21) | def _gef_filename(diff):
  class BitbucketProvider (line 27) | class BitbucketProvider(GitProvider):
    method __init__ (line 28) | def __init__(
    method get_repo_settings (line 80) | def get_repo_settings(self):
    method get_git_repo_url (line 92) | def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket does n...
    method get_canonical_url_parts (line 103) | def get_canonical_url_parts(self, repo_git_url:str=None, desired_branc...
    method publish_code_suggestions (line 123) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method publish_file_comments (line 188) | def publish_file_comments(self, file_comments: list) -> bool:
    method is_supported (line 191) | def is_supported(self, capability: str) -> bool:
    method set_pr (line 197) | def set_pr(self, pr_url: str):
    method get_files (line 201) | def get_files(self):
    method get_diff_files (line 214) | def get_diff_files(self) -> list[FilePatchInfo]:
    method get_latest_commit_url (line 346) | def get_latest_commit_url(self):
    method get_comment_url (line 349) | def get_comment_url(self, comment):
    method publish_persistent_comment (line 352) | def publish_persistent_comment(self, pr_comment: str,
    method publish_comment (line 380) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method edit_comment (line 390) | def edit_comment(self, comment, body: str):
    method remove_initial_comment (line 397) | def remove_initial_comment(self):
    method remove_comment (line 404) | def remove_comment(self, comment):
    method create_inline_comment (line 411) | def create_inline_comment(self, body: str, relevant_file: str, relevan...
    method publish_inline_comment (line 427) | def publish_inline_comment(self, comment: str, from_line: int, file: s...
    method get_line_link (line 443) | def get_line_link(self, relevant_file: str, relevant_line_start: int, ...
    method generate_link_to_relevant_line_number (line 450) | def generate_link_to_relevant_line_number(self, suggestion) -> str:
    method publish_inline_comments (line 470) | def publish_inline_comments(self, comments: list[dict]):
    method get_title (line 482) | def get_title(self):
    method get_languages (line 485) | def get_languages(self):
    method get_pr_branch (line 489) | def get_pr_branch(self):
    method get_repo_default_branch (line 494) | def get_repo_default_branch(self):
    method get_pr_owner_id (line 502) | def get_pr_owner_id(self) -> str | None:
    method get_pr_description_full (line 505) | def get_pr_description_full(self):
    method get_user_id (line 508) | def get_user_id(self):
    method get_issue_comments (line 511) | def get_issue_comments(self):
    method add_eyes_reaction (line 516) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 519) | def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> ...
    method _parse_pr_url (line 523) | def _parse_pr_url(pr_url: str) -> Tuple[str, int, int]:
    method _get_repo (line 545) | def _get_repo(self):
    method _get_pr (line 552) | def _get_pr(self):
    method get_pr_file_content (line 555) | def get_pr_file_content(self, file_path: str, branch: str) -> str:
    method create_or_update_pr_file (line 571) | def create_or_update_pr_file(self, file_path: str, branch: str, conten...
    method _get_pr_file_content (line 589) | def _get_pr_file_content(self, remote_link: str):
    method get_commit_messages (line 599) | def get_commit_messages(self):
    method publish_description (line 603) | def publish_description(self, pr_title: str, description: str):
    method publish_labels (line 619) | def publish_labels(self, pr_types: list):
    method get_pr_labels (line 623) | def get_pr_labels(self, update=False):
    method _prepare_clone_url_with_token (line 626) | def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str...

FILE: pr_agent/git_providers/bitbucket_server_provider.py
  class BitbucketServerProvider (line 24) | class BitbucketServerProvider(GitProvider):
    method __init__ (line 25) | def __init__(
    method get_git_repo_url (line 71) | def get_git_repo_url(self, pr_url: str=None) -> str: #bitbucket server...
    method get_canonical_url_parts (line 82) | def get_canonical_url_parts(self, repo_git_url:str=None, desired_branc...
    method get_repo_settings (line 106) | def get_repo_settings(self):
    method get_pr_id (line 119) | def get_pr_id(self):
    method publish_code_suggestions (line 122) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method publish_file_comments (line 189) | def publish_file_comments(self, file_comments: list) -> bool:
    method is_supported (line 192) | def is_supported(self, capability: str) -> bool:
    method set_pr (line 197) | def set_pr(self, pr_url: str):
    method get_file (line 201) | def get_file(self, path: str, commit_id: str):
    method get_files (line 212) | def get_files(self):
    method get_best_common_ancestor (line 219) | def get_best_common_ancestor(source_commits_list, destination_commits_...
    method get_diff_files (line 229) | def get_diff_files(self) -> list[FilePatchInfo]:
    method publish_comment (line 309) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method remove_initial_comment (line 313) | def remove_initial_comment(self):
    method remove_comment (line 320) | def remove_comment(self, comment):
    method create_inline_comment (line 324) | def create_inline_comment(self, body: str, relevant_file: str, relevan...
    method publish_inline_comment (line 342) | def publish_inline_comment(self, comment: str, from_line: int, file: s...
    method get_line_link (line 361) | def get_line_link(self, relevant_file: str, relevant_line_start: int, ...
    method generate_link_to_relevant_line_number (line 368) | def generate_link_to_relevant_line_number(self, suggestion) -> str:
    method publish_inline_comments (line 399) | def publish_inline_comments(self, comments: list[dict]):
    method get_title (line 411) | def get_title(self):
    method get_languages (line 414) | def get_languages(self):
    method get_pr_branch (line 417) | def get_pr_branch(self):
    method get_pr_owner_id (line 420) | def get_pr_owner_id(self) -> str | None:
    method get_pr_description_full (line 423) | def get_pr_description_full(self):
    method get_user_id (line 429) | def get_user_id(self):
    method get_issue_comments (line 432) | def get_issue_comments(self):
    method add_eyes_reaction (line 437) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 440) | def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> ...
    method _parse_bitbucket_server (line 444) | def _parse_bitbucket_server(url: str) -> str:
    method _parse_pr_url (line 454) | def _parse_pr_url(pr_url: str) -> Tuple[str, str, int]:
    method _get_repo (line 494) | def _get_repo(self):
    method _get_pr (line 499) | def _get_pr(self):
    method _get_pr_file_content (line 508) | def _get_pr_file_content(self, remote_link: str):
    method get_commit_messages (line 511) | def get_commit_messages(self):
    method publish_description (line 515) | def publish_description(self, pr_title: str, description: str):
    method publish_labels (line 529) | def publish_labels(self, pr_types: list):
    method get_pr_labels (line 533) | def get_pr_labels(self, update=False):
    method _get_pr_comments_path (line 536) | def _get_pr_comments_path(self):
    method _get_merge_base (line 539) | def _get_merge_base(self):
    method _prepare_clone_url_with_token (line 542) | def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str...
    method _clone_inner (line 555) | def _clone_inner(self, repo_url: str, dest_folder: str, operation_time...

FILE: pr_agent/git_providers/codecommit_client.py
  class CodeCommitDifferencesResponse (line 5) | class CodeCommitDifferencesResponse:
    method __init__ (line 11) | def __init__(self, json: dict):
  class CodeCommitPullRequestResponse (line 22) | class CodeCommitPullRequestResponse:
    method __init__ (line 28) | def __init__(self, json: dict):
    class CodeCommitPullRequestTarget (line 36) | class CodeCommitPullRequestTarget:
      method __init__ (line 42) | def __init__(self, json: dict):
  class CodeCommitClient (line 49) | class CodeCommitClient:
    method __init__ (line 54) | def __init__(self):
    method is_supported (line 57) | def is_supported(self, capability: str) -> bool:
    method _connect_boto_client (line 62) | def _connect_boto_client(self):
    method get_differences (line 68) | def get_differences(self, repo_name: int, destination_commit: str, sou...
    method get_file (line 109) | def get_file(self, repo_name: str, file_path: str, sha_hash: str, opti...
    method get_pr (line 147) | def get_pr(self, repo_name: str, pr_number: int):
    method publish_description (line 181) | def publish_description(self, pr_number: int, pr_title: str, pr_body: ...
    method publish_comment (line 218) | def publish_comment(self, repo_name: str, pr_number: int, destination_...

FILE: pr_agent/git_providers/codecommit_provider.py
  class PullRequestCCMimic (line 17) | class PullRequestCCMimic:
    method __init__ (line 22) | def __init__(self, title: str, diff_files: List[FilePatchInfo]):
  class CodeCommitFile (line 32) | class CodeCommitFile:
    method __init__ (line 37) | def __init__(
  class CodeCommitProvider (line 53) | class CodeCommitProvider(GitProvider):
    method __init__ (line 58) | def __init__(self, pr_url: Optional[str] = None, incremental: Optional...
    method provider_name (line 70) | def provider_name(self):
    method is_supported (line 73) | def is_supported(self, capability: str) -> bool:
    method set_pr (line 84) | def set_pr(self, pr_url: str):
    method get_files (line 88) | def get_files(self) -> list[CodeCommitFile]:
    method get_diff_files (line 103) | def get_diff_files(self) -> list[FilePatchInfo]:
    method publish_description (line 159) | def publish_description(self, pr_title: str, pr_body: str):
    method publish_comment (line 169) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method publish_code_suggestions (line 188) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method publish_labels (line 218) | def publish_labels(self, labels):
    method get_pr_labels (line 221) | def get_pr_labels(self, update=False):
    method remove_initial_comment (line 224) | def remove_initial_comment(self):
    method remove_comment (line 227) | def remove_comment(self, comment):
    method publish_inline_comment (line 230) | def publish_inline_comment(self, body: str, relevant_file: str, releva...
    method publish_inline_comments (line 234) | def publish_inline_comments(self, comments: list[dict]):
    method get_title (line 237) | def get_title(self):
    method get_pr_id (line 240) | def get_pr_id(self):
    method get_languages (line 252) | def get_languages(self):
    method get_pr_branch (line 285) | def get_pr_branch(self):
    method get_pr_description_full (line 288) | def get_pr_description_full(self) -> str:
    method get_user_id (line 291) | def get_user_id(self):
    method get_issue_comments (line 294) | def get_issue_comments(self):
    method get_repo_settings (line 297) | def get_repo_settings(self):
    method add_eyes_reaction (line 302) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 306) | def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> ...
    method _parse_pr_url (line 311) | def _parse_pr_url(pr_url: str) -> Tuple[str, int]:
    method _is_valid_codecommit_hostname (line 349) | def _is_valid_codecommit_hostname(hostname: str) -> bool:
    method _get_pr (line 364) | def _get_pr(self):
    method get_commit_messages (line 388) | def get_commit_messages(self):
    method _add_additional_newlines (line 392) | def _add_additional_newlines(body: str) -> str:
    method _remove_markdown_html (line 408) | def _remove_markdown_html(comment: str) -> str:
    method _get_edit_type (line 428) | def _get_edit_type(codecommit_change_type: str):
    method _get_file_extensions (line 452) | def _get_file_extensions(filenames):
    method _get_language_percentages (line 475) | def _get_language_percentages(extensions):

FILE: pr_agent/git_providers/gerrit_provider.py
  function _call (line 22) | def _call(*command, **kwargs) -> (int, str, str):
  function clone (line 33) | def clone(url, directory):
  function fetch (line 39) | def fetch(url, refspec, cwd):
  function checkout (line 48) | def checkout(cwd):
  function show (line 54) | def show(*args, cwd=None):
  function diff (line 59) | def diff(*args, cwd=None):
  function reset_local_changes (line 68) | def reset_local_changes(cwd):
  function add_comment (line 73) | def add_comment(url: urllib3.util.Url, refspec, message):
  function list_comments (line 87) | def list_comments(url: urllib3.util.Url, refspec):
  function prepare_repo (line 102) | def prepare_repo(url: urllib3.util.Url, project, refspec):
  function adopt_to_gerrit_message (line 112) | def adopt_to_gerrit_message(message):
  function add_suggestion (line 138) | def add_suggestion(src_filename, context: str, start, end: int):
  function upload_patch (line 153) | def upload_patch(patch, path):
  class GerritProvider (line 175) | class GerritProvider(GitProvider):
    method __init__ (line 177) | def __init__(self, key: str, incremental=False):
    method get_pr_title (line 199) | def get_pr_title(self):
    method get_issue_comments (line 205) | def get_issue_comments(self):
    method get_pr_labels (line 211) | def get_pr_labels(self, update=False):
    method add_eyes_reaction (line 215) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 219) | def remove_reaction(self, issue_comment_id: int, reaction_id: int):
    method get_commit_messages (line 223) | def get_commit_messages(self):
    method get_repo_settings (line 226) | def get_repo_settings(self):
    method get_diff_files (line 234) | def get_diff_files(self) -> list[FilePatchInfo]:
    method get_files (line 276) | def get_files(self):
    method get_languages (line 285) | def get_languages(self):
    method get_pr_description_full (line 303) | def get_pr_description_full(self):
    method get_user_id (line 306) | def get_user_id(self):
    method is_supported (line 309) | def is_supported(self, capability: str) -> bool:
    method split_suggestion (line 320) | def split_suggestion(self, msg) -> tuple[str, str]:
    method publish_code_suggestions (line 343) | def publish_code_suggestions(self, code_suggestions: list):
    method publish_comment (line 364) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method publish_description (line 369) | def publish_description(self, pr_title: str, pr_body: str):
    method publish_inline_comments (line 373) | def publish_inline_comments(self, comments: list[dict]):
    method publish_inline_comment (line 378) | def publish_inline_comment(self, body: str, relevant_file: str,
    method publish_labels (line 385) | def publish_labels(self, labels):
    method remove_initial_comment (line 390) | def remove_initial_comment(self):
    method remove_comment (line 395) | def remove_comment(self, comment):
    method get_pr_branch (line 398) | def get_pr_branch(self):

FILE: pr_agent/git_providers/git_provider.py
  function get_git_ssl_env (line 15) | def get_git_ssl_env() -> dict[str, str]:
  class GitProvider (line 74) | class GitProvider(ABC):
    method is_supported (line 76) | def is_supported(self, capability: str) -> bool:
    method get_git_repo_url (line 80) | def get_git_repo_url(self, issues_or_pr_url: str) -> str:
    method get_canonical_url_parts (line 87) | def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str...
    class ScopedClonedRepo (line 99) | class ScopedClonedRepo(object):
      method __init__ (line 100) | def __init__(self, dest_folder):
      method __del__ (line 103) | def __del__(self):
    method _prepare_clone_url_with_token (line 108) | def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str...
    method _clone_inner (line 114) | def _clone_inner(self, repo_url: str, dest_folder: str, operation_time...
    method clone (line 139) | def clone(self, repo_url_to_clone: str, dest_folder: str, remove_dest_...
    method get_files (line 158) | def get_files(self) -> list:
    method get_diff_files (line 162) | def get_diff_files(self) -> list[FilePatchInfo]:
    method get_incremental_commits (line 165) | def get_incremental_commits(self, is_incremental):
    method publish_description (line 169) | def publish_description(self, pr_title: str, pr_body: str):
    method publish_code_suggestions (line 173) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method get_languages (line 177) | def get_languages(self):
    method get_pr_branch (line 181) | def get_pr_branch(self):
    method get_user_id (line 185) | def get_user_id(self):
    method get_pr_description_full (line 189) | def get_pr_description_full(self) -> str:
    method edit_comment (line 192) | def edit_comment(self, comment, body: str):
    method edit_comment_from_comment_id (line 195) | def edit_comment_from_comment_id(self, comment_id: int, body: str):
    method get_comment_body_from_comment_id (line 198) | def get_comment_body_from_comment_id(self, comment_id: int) -> str:
    method reply_to_comment_from_comment_id (line 201) | def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
    method get_pr_description (line 204) | def get_pr_description(self, full: bool = True, split_changes_walkthro...
    method get_user_description (line 219) | def get_user_description(self) -> str:
    method _possible_headers (line 265) | def _possible_headers(self):
    method _is_generated_by_pr_agent (line 269) | def _is_generated_by_pr_agent(self, description_lowercase: str) -> bool:
    method get_repo_settings (line 274) | def get_repo_settings(self):
    method get_workspace_name (line 277) | def get_workspace_name(self):
    method get_pr_id (line 280) | def get_pr_id(self):
    method get_line_link (line 283) | def get_line_link(self, relevant_file: str, relevant_line_start: int, ...
    method get_lines_link_original_file (line 286) | def get_lines_link_original_file(self, filepath:str, component_range: ...
    method publish_comment (line 291) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method publish_persistent_comment (line 294) | def publish_persistent_comment(self, pr_comment: str,
    method publish_persistent_comment_full (line 301) | def publish_persistent_comment_full(self, pr_comment: str,
    method publish_inline_comment (line 330) | def publish_inline_comment(self, body: str, relevant_file: str, releva...
    method create_inline_comment (line 333) | def create_inline_comment(self, body: str, relevant_file: str, relevan...
    method publish_inline_comments (line 338) | def publish_inline_comments(self, comments: list[dict]):
    method remove_initial_comment (line 342) | def remove_initial_comment(self):
    method remove_comment (line 346) | def remove_comment(self, comment):
    method get_issue_comments (line 350) | def get_issue_comments(self):
    method get_comment_url (line 353) | def get_comment_url(self, comment) -> str:
    method get_review_thread_comments (line 356) | def get_review_thread_comments(self, comment_id: int) -> list[dict]:
    method publish_labels (line 361) | def publish_labels(self, labels):
    method get_pr_labels (line 365) | def get_pr_labels(self, update=False):
    method get_repo_labels (line 368) | def get_repo_labels(self):
    method add_eyes_reaction (line 372) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 376) | def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> ...
    method get_commit_messages (line 381) | def get_commit_messages(self):
    method get_pr_url (line 384) | def get_pr_url(self) -> str:
    method get_latest_commit_url (line 389) | def get_latest_commit_url(self) -> str:
    method auto_approve (line 392) | def auto_approve(self) -> bool:
    method calc_pr_statistics (line 395) | def calc_pr_statistics(self, pull_request_data: dict):
    method get_num_of_files (line 398) | def get_num_of_files(self):
    method limit_output_characters (line 404) | def limit_output_characters(self, output: str, max_chars: int):
  function get_main_pr_language (line 408) | def get_main_pr_language(languages, files) -> str:
  class IncrementalPR (line 477) | class IncrementalPR:
    method __init__ (line 478) | def __init__(self, is_incremental: bool = False):
    method first_new_commit_sha (line 485) | def first_new_commit_sha(self):
    method last_seen_commit_sha (line 489) | def last_seen_commit_sha(self):

FILE: pr_agent/git_providers/gitea_provider.py
  class GiteaProvider (line 20) | class GiteaProvider(GitProvider):
    method __init__ (line 21) | def __init__(self, url: Optional[str] = None):
    method __add_file_content (line 104) | def __add_file_content(self):
    method __add_file_diff (line 124) | def __add_file_diff(self):
    method _parse_pr_url (line 154) | def _parse_pr_url(self, pr_url: str) -> Tuple[str, str, int]:
    method _parse_issue_url (line 174) | def _parse_issue_url(self, issue_url: str) -> Tuple[str, str, int]:
    method __set_repo_and_owner_from_pr (line 194) | def __set_repo_and_owner_from_pr(self):
    method __set_repo_and_owner_from_issue (line 207) | def __set_repo_and_owner_from_issue(self):
    method get_pr_url (line 220) | def get_pr_url(self) -> str:
    method get_issue_url (line 223) | def get_issue_url(self) -> str:
    method get_latest_commit_url (line 226) | def get_latest_commit_url(self) -> str:
    method get_comment_url (line 229) | def get_comment_url(self, comment) -> str:
    method publish_persistent_comment (line 232) | def publish_persistent_comment(self, pr_comment: str,
    method publish_comment (line 239) | def publish_comment(self, comment: str,is_temporary: bool = False) -> ...
    method edit_comment (line 277) | def edit_comment(self, comment, body : str):
    method publish_inline_comment (line 294) | def publish_inline_comment(self,body: str, relevant_file: str, relevan...
    method publish_inline_comments (line 312) | def publish_inline_comments(self, comments: List[Dict[str, Any]],body ...
    method publish_code_suggestions (line 328) | def publish_code_suggestions(self, suggestions: List[Dict[str, Any]]):
    method add_eyes_reaction (line 347) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 384) | def remove_reaction(self, comment_id: int) -> None:
    method get_commit_messages (line 399) | def get_commit_messages(self)-> str:
    method _get_file_content_from_base (line 428) | def _get_file_content_from_base(self, filename: str) -> str:
    method _get_file_content_from_latest_commit (line 436) | def _get_file_content_from_latest_commit(self, filename: str) -> str:
    method get_diff_files (line 444) | def get_diff_files(self) -> List[FilePatchInfo]:
    method get_line_link (line 520) | def get_line_link(self, relevant_file, relevant_line_start, relevant_l...
    method get_pr_id (line 531) | def get_pr_id(self):
    method get_files (line 538) | def get_files(self) -> List[Dict[str, Any]]:
    method get_num_of_files (line 542) | def get_num_of_files(self) -> int:
    method get_issue_comments (line 546) | def get_issue_comments(self) -> List[Dict[str, Any]]:
    method get_languages (line 560) | def get_languages(self) -> Set[str]:
    method get_pr_branch (line 569) | def get_pr_branch(self) -> str:
    method get_pr_description_full (line 581) | def get_pr_description_full(self) -> str:
    method get_pr_labels (line 589) | def get_pr_labels(self,update=False) -> List[str]:
    method get_repo_settings (line 608) | def get_repo_settings(self) -> str:
    method get_user_id (line 626) | def get_user_id(self) -> str:
    method is_supported (line 630) | def is_supported(self, capability) -> bool:
    method get_git_repo_url (line 634) | def get_git_repo_url(self, issues_or_pr_url: str) -> str:
    method publish_description (line 637) | def publish_description(self, pr_title: str, pr_body: str) -> None:
    method publish_labels (line 659) | def publish_labels(self, labels: List[int]) -> None:
    method remove_comment (line 675) | def remove_comment(self, comment) -> None:
    method remove_initial_comment (line 699) | def remove_initial_comment(self) -> None:
    method _prepare_clone_url_with_token (line 712) | def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str...
  class RepoApi (line 741) | class RepoApi(giteapy.RepositoryApi):
    method __init__ (line 742) | def __init__(self, client: giteapy.ApiClient):
    method create_inline_comment (line 748) | def create_inline_comment(self, owner: str, repo: str, pr_number: int,...
    method create_comment (line 763) | def create_comment(self, owner: str, repo: str, index: int, comment: s...
    method edit_comment (line 774) | def edit_comment(self, owner: str, repo: str, comment_id: int, comment...
    method remove_comment (line 785) | def remove_comment(self, owner: str, repo: str, comment_id: int):
    method list_all_comments (line 792) | def list_all_comments(self, owner: str, repo: str, index: int):
    method get_pull_request_diff (line 799) | def get_pull_request_diff(self, owner: str, repo: str, pr_number: int)...
    method get_pull_request (line 832) | def get_pull_request(self, owner: str, repo: str, pr_number: int):
    method edit_pull_request (line 840) | def edit_pull_request(self, owner: str, repo: str, pr_number: int,titl...
    method get_change_file_pull_request (line 853) | def get_change_file_pull_request(self, owner: str, repo: str, pr_numbe...
    method get_languages (line 886) | def get_languages(self, owner: str, repo: str):
    method get_file_content (line 917) | def get_file_content(self, owner: str, repo: str, commit_sha: str, fil...
    method get_issue_labels (line 953) | def get_issue_labels(self, owner: str, repo: str, issue_number: int):
    method list_all_commits (line 961) | def list_all_commits(self, owner: str, repo: str):
    method add_reviewer (line 967) | def add_reviewer(self, owner: str, repo: str, pr_number: int, reviewer...
    method add_reaction_comment (line 980) | def add_reaction_comment(self, owner: str, repo: str, comment_id: int,...
    method remove_reaction_comment (line 993) | def remove_reaction_comment(self, owner: str, repo: str, comment_id: i...
    method add_labels (line 1002) | def add_labels(self, owner: str, repo: str, issue_number: int, labels:...
    method get_pr_commits (line 1013) | def get_pr_commits(self, owner: str, repo: str, pr_number: int):

FILE: pr_agent/git_providers/github_provider.py
  class GithubProvider (line 32) | class GithubProvider(GitProvider):
    method __init__ (line 33) | def __init__(self, pr_url: Optional[str] = None):
    method _get_issue_handle (line 61) | def _get_issue_handle(self, issue_url) -> Optional[Issue]:
    method get_incremental_commits (line 79) | def get_incremental_commits(self, incremental=IncrementalPR(False)):
    method is_supported (line 85) | def is_supported(self, capability: str) -> bool:
    method _get_owner_and_repo_path (line 88) | def _get_owner_and_repo_path(self, given_url: str) -> str:
    method get_git_repo_url (line 106) | def get_git_repo_url(self, issues_or_pr_url: str) -> str:
    method get_canonical_url_parts (line 116) | def get_canonical_url_parts(self, repo_git_url:str, desired_branch:str...
    method get_pr_url (line 145) | def get_pr_url(self) -> str:
    method set_pr (line 148) | def set_pr(self, pr_url: str):
    method _get_incremental_commits (line 152) | def _get_incremental_commits(self):
    method get_commit_range (line 170) | def get_commit_range(self):
    method get_previous_review (line 182) | def get_previous_review(self, *, full: bool, incremental: bool):
    method get_files (line 196) | def get_files(self):
    method get_num_of_files (line 211) | def get_num_of_files(self):
    method get_diff_files (line 222) | def get_diff_files(self) -> list[FilePatchInfo]:
    method publish_description (line 355) | def publish_description(self, pr_title: str, pr_body: str):
    method get_latest_commit_url (line 358) | def get_latest_commit_url(self) -> str:
    method get_comment_url (line 361) | def get_comment_url(self, comment) -> str:
    method publish_persistent_comment (line 364) | def publish_persistent_comment(self, pr_comment: str,
    method publish_comment (line 371) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method publish_inline_comment (line 394) | def publish_inline_comment(self, body: str, relevant_file: str, releva...
    method create_inline_comment (line 399) | def create_inline_comment(self, body: str, relevant_file: str, relevan...
    method publish_inline_comments (line 414) | def publish_inline_comments(self, comments: list[dict], disable_fallba...
    method get_review_thread_comments (line 432) | def get_review_thread_comments(self, comment_id: int) -> list[dict]:
    method _publish_inline_comments_fallback_with_verification (line 466) | def _publish_inline_comments_fallback_with_verification(self, comments...
    method _verify_code_comment (line 492) | def _verify_code_comment(self, comment: dict):
    method _verify_code_comments (line 513) | def _verify_code_comments(self, comments: list[dict]) -> tuple[list[di...
    method _try_fix_invalid_inline_comments (line 526) | def _try_fix_invalid_inline_comments(self, invalid_comments: list[dict...
    method publish_code_suggestions (line 551) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method edit_comment (line 600) | def edit_comment(self, comment, body: str):
    method edit_comment_from_comment_id (line 613) | def edit_comment_from_comment_id(self, comment_id: int, body: str):
    method reply_to_comment_from_comment_id (line 624) | def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
    method get_comment_body_from_comment_id (line 635) | def get_comment_body_from_comment_id(self, comment_id: int):
    method publish_file_comments (line 646) | def publish_file_comments(self, file_comments: list) -> bool:
    method remove_initial_comment (line 680) | def remove_initial_comment(self):
    method remove_comment (line 688) | def remove_comment(self, comment):
    method get_title (line 694) | def get_title(self):
    method get_languages (line 697) | def get_languages(self):
    method get_pr_branch (line 701) | def get_pr_branch(self):
    method get_pr_owner_id (line 704) | def get_pr_owner_id(self) -> str | None:
    method get_pr_description_full (line 709) | def get_pr_description_full(self):
    method get_user_id (line 712) | def get_user_id(self):
    method get_notifications (line 721) | def get_notifications(self, since: datetime):
    method get_issue_comments (line 730) | def get_issue_comments(self):
    method get_repo_settings (line 733) | def get_repo_settings(self):
    method get_workspace_name (line 743) | def get_workspace_name(self):
    method add_eyes_reaction (line 746) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 759) | def remove_reaction(self, issue_comment_id: int, reaction_id: str) -> ...
    method _parse_pr_url (line 771) | def _parse_pr_url(self, pr_url: str) -> Tuple[str, int]:
    method _parse_issue_url (line 799) | def _parse_issue_url(self, issue_url: str) -> Tuple[str, int]:
    method _get_github_client (line 827) | def _get_github_client(self):
    method _get_repo (line 854) | def _get_repo(self):
    method _get_pr (line 864) | def _get_pr(self):
    method get_pr_file_content (line 867) | def get_pr_file_content(self, file_path: str, branch: str) -> str:
    method create_or_update_pr_file (line 878) | def create_or_update_pr_file(
    method _get_pr_file_content (line 894) | def _get_pr_file_content(self, file: FilePatchInfo, sha: str) -> str:
    method publish_labels (line 897) | def publish_labels(self, pr_types):
    method get_pr_labels (line 912) | def get_pr_labels(self, update=False):
    method get_repo_labels (line 926) | def get_repo_labels(self):
    method get_commit_messages (line 930) | def get_commit_messages(self):
    method generate_link_to_relevant_line_number (line 948) | def generate_link_to_relevant_line_number(self, suggestion) -> str:
    method get_line_link (line 972) | def get_line_link(self, relevant_file: str, relevant_line_start: int, ...
    method get_lines_link_original_file (line 982) | def get_lines_link_original_file(self, filepath: str, component_range:...
    method get_pr_id (line 1009) | def get_pr_id(self):
    method fetch_sub_issues (line 1016) | def fetch_sub_issues(self, issue_url):
    method auto_approve (line 1094) | def auto_approve(self) -> bool:
    method calc_pr_statistics (line 1104) | def calc_pr_statistics(self, pull_request_data: dict):
    method validate_comments_inside_hunks (line 1107) | def validate_comments_inside_hunks(self, code_suggestions):
    method _prepare_clone_url_with_token (line 1193) | def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str...

FILE: pr_agent/git_providers/gitlab_provider.py
  class DiffNotFoundError (line 26) | class DiffNotFoundError(Exception):
  class GitLabProvider (line 30) | class GitLabProvider(GitProvider):
    method __init__ (line 32) | def __init__(self, merge_request_url: Optional[str] = None, incrementa...
    method _get_gitmodules_map (line 81) | def _get_gitmodules_map(self) -> dict[str, str]:
    method _url_to_project_path (line 153) | def _url_to_project_path(self, url: str) -> str | None:
    method _project_by_path (line 168) | def _project_by_path(self, proj_path: str):
    method _compare_submodule (line 209) | def _compare_submodule(self, proj_path: str, old_sha: str, new_sha: st...
    method _expand_submodule_changes (line 234) | def _expand_submodule_changes(self, changes: list[dict]) -> list[dict]:
    method is_supported (line 289) | def is_supported(self, capability: str) -> bool:
    method _get_project_path_from_pr_or_issue_url (line 295) | def _get_project_path_from_pr_or_issue_url(self, pr_or_issue_url: str)...
    method get_git_repo_url (line 307) | def get_git_repo_url(self, issues_or_pr_url: str) -> str:
    method get_canonical_url_parts (line 318) | def get_canonical_url_parts(self, repo_git_url:str=None, desired_branc...
    method pr (line 337) | def pr(self):
    method _set_merge_request (line 341) | def _set_merge_request(self, merge_request_url: str):
    method get_pr_file_content (line 350) | def get_pr_file_content(self, file_path: str, branch: str) -> str:
    method create_or_update_pr_file (line 363) | def create_or_update_pr_file(self, file_path: str, branch: str, conten...
    method get_diff_files (line 395) | def get_diff_files(self) -> list[FilePatchInfo]:
    method get_files (line 479) | def get_files(self) -> list:
    method publish_description (line 486) | def publish_description(self, pr_title: str, pr_body: str):
    method get_latest_commit_url (line 494) | def get_latest_commit_url(self):
    method get_comment_url (line 503) | def get_comment_url(self, comment):
    method publish_persistent_comment (line 506) | def publish_persistent_comment(self, pr_comment: str,
    method publish_comment (line 513) | def publish_comment(self, mr_comment: str, is_temporary: bool = False):
    method edit_comment (line 523) | def edit_comment(self, comment, body: str):
    method edit_comment_from_comment_id (line 527) | def edit_comment_from_comment_id(self, comment_id: int, body: str):
    method reply_to_comment_from_comment_id (line 533) | def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
    method publish_inline_comment (line 538) | def publish_inline_comment(self, body: str, relevant_file: str, releva...
    method create_inline_comment (line 545) | def create_inline_comment(self, body: str, relevant_file: str, relevan...
    method create_inline_comments (line 548) | def create_inline_comments(self, comments: list[dict]):
    method get_comment_body_from_comment_id (line 551) | def get_comment_body_from_comment_id(self, comment_id: int):
    method send_inline_comment (line 555) | def send_inline_comment(self, body: str, edit_type: str, found: bool, ...
    method get_relevant_diff (line 638) | def get_relevant_diff(self, relevant_file: str, relevant_line_in_file:...
    method publish_code_suggestions (line 657) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method publish_file_comments (line 697) | def publish_file_comments(self, file_comments: list) -> bool:
    method search_line (line 700) | def search_line(self, relevant_file, relevant_line_in_file):
    method find_in_file (line 710) | def find_in_file(self, file, relevant_line_in_file):
    method get_edit_type (line 746) | def get_edit_type(self, relevant_line_in_file):
    method remove_initial_comment (line 754) | def remove_initial_comment(self):
    method remove_comment (line 761) | def remove_comment(self, comment):
    method get_title (line 767) | def get_title(self):
    method get_languages (line 770) | def get_languages(self):
    method get_pr_branch (line 774) | def get_pr_branch(self):
    method get_pr_owner_id (line 777) | def get_pr_owner_id(self) -> str | None:
    method get_pr_description_full (line 786) | def get_pr_description_full(self):
    method get_issue_comments (line 789) | def get_issue_comments(self):
    method get_repo_settings (line 792) | def get_repo_settings(self):
    method get_workspace_name (line 800) | def get_workspace_name(self):
    method add_eyes_reaction (line 803) | def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool ...
    method remove_reaction (line 826) | def remove_reaction(self, issue_comment_id: int, reaction_id: str) -> ...
    method _parse_merge_request_url (line 851) | def _parse_merge_request_url(self, merge_request_url: str) -> Tuple[st...
    method _get_merge_request (line 876) | def _get_merge_request(self):
    method get_user_id (line 880) | def get_user_id(self):
    method publish_labels (line 883) | def publish_labels(self, pr_types):
    method publish_inline_comments (line 890) | def publish_inline_comments(self, comments: list[dict]):
    method get_pr_labels (line 893) | def get_pr_labels(self, update=False):
    method get_repo_labels (line 896) | def get_repo_labels(self):
    method get_commit_messages (line 899) | def get_commit_messages(self):
    method get_pr_id (line 916) | def get_pr_id(self):
    method get_line_link (line 923) | def get_line_link(self, relevant_file: str, relevant_line_start: int, ...
    method generate_link_to_relevant_line_number (line 933) | def generate_link_to_relevant_line_number(self, suggestion) -> str:
    method _prepare_clone_url_with_token (line 957) | def _prepare_clone_url_with_token(self, repo_url_to_clone: str) -> str...

FILE: pr_agent/git_providers/local_git_provider.py
  class PullRequestMimic (line 13) | class PullRequestMimic:
    method __init__ (line 18) | def __init__(self, title: str, diff_files: List[FilePatchInfo]):
  class LocalGitProvider (line 23) | class LocalGitProvider(GitProvider):
    method __init__ (line 32) | def __init__(self, target_branch_name, incremental=False):
    method _prepare_repo (line 49) | def _prepare_repo(self):
    method is_supported (line 59) | def is_supported(self, capability: str) -> bool:
    method get_diff_files (line 65) | def get_diff_files(self) -> list[FilePatchInfo]:
    method get_files (line 100) | def get_files(self) -> List[str]:
    method publish_description (line 112) | def publish_description(self, pr_title: str, pr_body: str):
    method publish_comment (line 117) | def publish_comment(self, pr_comment: str, is_temporary: bool = False):
    method publish_inline_comment (line 122) | def publish_inline_comment(self, body: str, relevant_file: str, releva...
    method publish_inline_comments (line 125) | def publish_inline_comments(self, comments: list[dict]):
    method publish_code_suggestion (line 128) | def publish_code_suggestion(self, body: str, relevant_file: str,
    method publish_code_suggestions (line 132) | def publish_code_suggestions(self, code_suggestions: list) -> bool:
    method publish_labels (line 135) | def publish_labels(self, labels):
    method remove_initial_comment (line 138) | def remove_initial_comment(self):
    method remove_comment (line 141) | def remove_comment(self, comment):
    method add_eyes_reaction (line 144) | def add_eyes_reaction(self, comment):
    method get_commit_messages (line 147) | def get_commit_messages(self):
    method get_repo_settings (line 150) | def get_repo_settings(self):
    method remove_reaction (line 153) | def remove_reaction(self, comment):
    method get_languages (line 156) | def get_languages(self):
    method get_pr_branch (line 169) | def get_pr_branch(self):
    method get_user_id (line 172) | def get_user_id(self):
    method get_pr_description_full (line 175) | def get_pr_description_full(self):
    method get_pr_title (line 182) | def get_pr_title(self):
    method get_issue_comments (line 188) | def get_issue_comments(self):
    method get_pr_labels (line 191) | def get_pr_labels(self, update=False):

FILE: pr_agent/git_providers/utils.py
  function apply_repo_settings (line 14) | def apply_repo_settings(pr_url):
  function handle_configurations_errors (line 93) | def handle_configurations_errors(config_errors, git_provider):
  function set_claude_model (line 123) | def set_claude_model():

FILE: pr_agent/identity_providers/__init__.py
  function get_identity_provider (line 10) | def get_identity_provider():

FILE: pr_agent/identity_providers/default_identity_provider.py
  class DefaultIdentityProvider (line 5) | class DefaultIdentityProvider(IdentityProvider):
    method verify_eligibility (line 6) | def verify_eligibility(self, git_provider, git_provider_id, pr_url):
    method inc_invocation_count (line 9) | def inc_invocation_count(self, git_provider, git_provider_id):

FILE: pr_agent/identity_providers/identity_provider.py
  class Eligibility (line 5) | class Eligibility(Enum):
  class IdentityProvider (line 11) | class IdentityProvider(ABC):
    method verify_eligibility (line 13) | def verify_eligibility(self, git_provider, git_provider_id, pr_url):
    method inc_invocation_count (line 17) | def inc_invocation_count(self, git_provider, git_provider_id):

FILE: pr_agent/log/__init__.py
  class LoggingFormat (line 13) | class LoggingFormat(str, Enum):
  function json_format (line 18) | def json_format(record: dict) -> str:
  function analytics_filter (line 22) | def analytics_filter(record: dict) -> bool:
  function inv_analytics_filter (line 26) | def inv_analytics_filter(record: dict) -> bool:
  function setup_logger (line 30) | def setup_logger(level: str = "INFO", fmt: LoggingFormat = LoggingFormat...
  function get_logger (line 65) | def get_logger(*args, **kwargs):

FILE: pr_agent/secret_providers/__init__.py
  function get_secret_provider (line 4) | def get_secret_provider():

FILE: pr_agent/secret_providers/aws_secrets_manager_provider.py
  class AWSSecretsManagerProvider (line 10) | class AWSSecretsManagerProvider(SecretProvider):
    method __init__ (line 11) | def __init__(self):
    method get_secret (line 27) | def get_secret(self, secret_name: str) -> str:
    method get_all_secrets (line 38) | def get_all_secrets(self) -> dict:
    method store_secret (line 49) | def store_secret(self, secret_name: str, secret_value: str):

FILE: pr_agent/secret_providers/google_cloud_storage_secret_provider.py
  class GoogleCloudStorageSecretProvider (line 9) | class GoogleCloudStorageSecretProvider(SecretProvider):
    method __init__ (line 10) | def __init__(self):
    method get_secret (line 20) | def get_secret(self, secret_name: str) -> str:
    method store_secret (line 28) | def store_secret(self, secret_name: str, secret_value: str):

FILE: pr_agent/secret_providers/secret_provider.py
  class SecretProvider (line 4) | class SecretProvider(ABC):
    method get_secret (line 7) | def get_secret(self, secret_name: str) -> str:
    method store_secret (line 11) | def store_secret(self, secret_name: str, secret_value: str):

FILE: pr_agent/servers/azuredevops_server_webhook.py
  function handle_request_comment (line 38) | async def handle_request_comment(url: str, body: str, thread_id: int, co...
  function handle_line_comment (line 54) | def handle_line_comment(body: str, thread_id: int, provider: AzureDevops...
  function authorize (line 80) | def authorize(credentials: HTTPBasicCredentials = Depends(security)):
  function _perform_commands_azure (line 94) | async def _perform_commands_azure(commands_conf: str, agent: PRAgent, ap...
  function handle_request_azure (line 118) | async def handle_request_azure(data, log_context):
  function handle_webhook (line 172) | async def handle_webhook(background_tasks: BackgroundTasks, request: Req...
  function root (line 184) | async def root():
  function start (line 187) | def start():

FILE: pr_agent/servers/bitbucket_app.py
  function get_bearer_token (line 33) | async def get_bearer_token(shared_secret: str, client_key: str):
  function handle_manifest (line 62) | async def handle_manifest(request: Request, response: Response):
  function _get_username (line 74) | def _get_username(data):
  function _validate_time_from_last_commit_to_pr_update (line 86) | async def _validate_time_from_last_commit_to_pr_update(data: dict) -> bool:
  function _perform_commands_bitbucket (line 139) | async def _perform_commands_bitbucket(commands_conf: str, agent: PRAgent...
  function is_bot_user (line 173) | def is_bot_user(data) -> bool:
  function should_process_pr_logic (line 186) | def should_process_pr_logic(data) -> bool:
  function handle_github_webhooks (line 235) | async def handle_github_webhooks(background_tasks: BackgroundTasks, requ...
  function handle_github_webhooks (line 311) | async def handle_github_webhooks(request: Request, response: Response):
  function handle_installed_webhooks (line 315) | async def handle_installed_webhooks(request: Request, response: Response):
  function handle_uninstalled_webhooks (line 334) | async def handle_uninstalled_webhooks(request: Request, response: Respon...
  function start (line 341) | def start():

FILE: pr_agent/servers/bitbucket_server_webhook.py
  function handle_request (line 29) | def handle_request(
  function should_process_pr_logic (line 44) | def should_process_pr_logic(data) -> bool:
  function redirect_to_webhook (line 127) | async def redirect_to_webhook():
  function handle_webhook (line 131) | async def handle_webhook(background_tasks: BackgroundTasks, request: Req...
  function _run_commands_sequentially (line 203) | async def _run_commands_sequentially(commands: List[str], url: str, log_...
  function _process_command (line 220) | def _process_command(command: str, url) -> str:
  function _to_list (line 233) | def _to_list(command_string: str) -> list:
  function _get_commands_list_from_settings (line 246) | def _get_commands_list_from_settings(setting_key:str ) -> list:
  function root (line 254) | async def root():
  function start (line 258) | def start():

FILE: pr_agent/servers/gerrit_server.py
  class Action (line 20) | class Action(str, Enum):
  class Item (line 29) | class Item(BaseModel):
  function handle_gerrit_request (line 36) | async def handle_gerrit_request(action: Action, item: Item):
  function get_body (line 52) | async def get_body(request):
  function root (line 62) | async def root():
  function start (line 66) | def start():

FILE: pr_agent/servers/gitea_app.py
  function handle_gitea_webhooks (line 24) | async def handle_gitea_webhooks(background_tasks: BackgroundTasks, reque...
  function get_body (line 38) | async def get_body(request: Request):
  function handle_request (line 64) | async def handle_request(body: Dict[str, Any], event: str):
  function handle_pr_event (line 86) | async def handle_pr_event(body: Dict[str, Any], event: str, action: str,...
  function handle_comment_event (line 114) | async def handle_comment_event(body: Dict[str, Any], event: str, action:...
  function _perform_commands_gitea (line 130) | async def _perform_commands_gitea(commands_conf: str, agent: PRAgent, bo...
  function should_process_pr_logic (line 151) | def should_process_pr_logic(body) -> bool:
  function start (line 214) | def start():

FILE: pr_agent/servers/github_action_runner.py
  function is_true (line 17) | def is_true(value: Union[str, bool]) -> bool:
  function get_setting_or_env (line 25) | def get_setting_or_env(key: str, default: Union[str, bool] = None) -> Un...
  function run_action (line 33) | async def run_action():

FILE: pr_agent/servers/github_app.py
  function handle_github_webhooks (line 39) | async def handle_github_webhooks(background_tasks: BackgroundTasks, requ...
  function handle_marketplace_webhooks (line 58) | async def handle_marketplace_webhooks(request: Request, response: Respon...
  function get_body (line 63) | async def get_body(request):
  function handle_comments_on_pr (line 80) | async def handle_comments_on_pr(body: Dict[str, Any],
  function handle_new_pr_opened (line 124) | async def handle_new_pr_opened(body: Dict[str, Any],
  function handle_push_trigger_for_new_commits (line 145) | async def handle_push_trigger_for_new_commits(body: Dict[str, Any],
  function handle_closed_pr (line 209) | def handle_closed_pr(body, event, action, log_context):
  function get_log_context (line 220) | def get_log_context(body, event, action, build_number):
  function is_bot_user (line 241) | def is_bot_user(sender, sender_type):
  function should_process_pr_logic (line 253) | def should_process_pr_logic(body) -> bool:
  function handle_request (line 312) | async def handle_request(body: Dict[str, Any], event: str):
  function handle_line_comments (line 361) | def handle_line_comments(body: Dict, comment_body: [str, Any]) -> str:
  function _check_pull_request_event (line 378) | def _check_pull_request_event(action: str, body: dict, log_context: dict...
  function _perform_auto_commands_github (line 395) | async def _perform_auto_commands_github(commands_conf: str, agent: PRAge...
  function root (line 419) | async def root():
  function start (line 432) | def start():

FILE: pr_agent/servers/github_lambda_webhook.py
  function lambda_handler (line 26) | def lambda_handler(event, context):

FILE: pr_agent/servers/github_polling.py
  function mark_notification_as_read (line 20) | async def mark_notification_as_read(headers, notification, session):
  function now (line 29) | def now() -> str:
  function async_handle_request (line 40) | async def async_handle_request(pr_url, rest_of_comment, comment_id, git_...
  function run_handle_request (line 49) | def run_handle_request(pr_url, rest_of_comment, comment_id, git_provider):
  function process_comment_sync (line 53) | def process_comment_sync(pr_url, rest_of_comment, comment_id):
  function process_comment (line 62) | async def process_comment(pr_url, rest_of_comment, comment_id):
  function is_valid_notification (line 76) | async def is_valid_notification(notification, headers, handled_ids, sess...
  function polling_loop (line 145) | async def polling_loop():

FILE: pr_agent/servers/gitlab_lambda_webhook.py
  function lambda_handler (line 26) | def lambda_handler(event, context):

FILE: pr_agent/servers/gitlab_webhook.py
  function handle_request (line 30) | async def handle_request(api_url: str, body: str, log_context: dict, sen...
  function _perform_commands_gitlab (line 39) | async def _perform_commands_gitlab(commands_conf: str, agent: PRAgent, a...
  function is_bot_user (line 63) | def is_bot_user(data) -> bool:
  function is_draft (line 75) | def is_draft(data) -> bool:
  function is_draft_ready (line 87) | def is_draft_ready(data) -> bool:
  function should_process_pr_logic (line 111) | def should_process_pr_logic(data) -> bool:
  function gitlab_webhook (line 171) | async def gitlab_webhook(background_tasks: BackgroundTasks, request: Req...
  function handle_ask_line (line 279) | def handle_ask_line(body, data):
  function root (line 300) | async def root():
  function start (line 312) | def start():

FILE: pr_agent/servers/help.py
  class HelpMessage (line 1) | class HelpMessage:
    method get_general_commands_text (line 3) | def get_general_commands_text():
    method get_general_bot_help_text (line 18) | def get_general_bot_help_text():
    method get_review_usage_guide (line 23) | def get_review_usage_guide():
    method get_describe_usage_guide (line 47) | def get_describe_usage_guide():
    method get_ask_usage_guide (line 142) | def get_ask_usage_guide():
    method get_improve_usage_guide (line 169) | def get_improve_usage_guide():
    method get_help_docs_usage_guide (line 196) | def get_help_docs_usage_guide():

FILE: pr_agent/servers/utils.py
  function verify_signature (line 10) | def verify_signature(payload_body, secret_token, signature_header):
  class RateLimitExceeded (line 28) | class RateLimitExceeded(Exception):
  class DefaultDictWithTimeout (line 33) | class DefaultDictWithTimeout(defaultdict):
    method __init__ (line 36) | def __init__(
    method __time (line 60) | def __time():
    method __refresh (line 63) | def __refresh(self):
    method __getitem__ (line 74) | def __getitem__(self, __key):
    method __setitem__ (line 80) | def __setitem__(self, __key, __value):
    method __delitem__ (line 84) | def __delitem__(self, __key):

FILE: pr_agent/tools/pr_add_docs.py
  class PRAddDocs (line 19) | class PRAddDocs:
    method __init__ (line 20) | def __init__(self, pr_url: str, cli_mode=False, args: list = None,
    method run (line 50) | async def run(self):
    method _prepare_prediction (line 71) | async def _prepare_prediction(self, model: str):
    method _get_prediction (line 83) | async def _get_prediction(self, model: str):
    method _prepare_pr_code_docs (line 97) | def _prepare_pr_code_docs(self) -> Dict:
    method push_inline_docs (line 104) | def push_inline_docs(self, data):
    method dedent_code (line 136) | def dedent_code(self, relevant_file, relevant_lines_start, new_code_sn...
  function get_docs_for_language (line 169) | def get_docs_for_language(language, style):

FILE: pr_agent/tools/pr_code_suggestions.py
  class PRCodeSuggestions (line 33) | class PRCodeSuggestions:
    method __init__ (line 34) | def __init__(self, pr_url: str, cli_mode=False, args: list = None,
    method run (line 93) | async def run(self):
    method add_self_review_text (line 197) | async def add_self_review_text(self, pr_body):
    method publish_no_suggestions (line 210) | async def publish_no_suggestions(self):
    method dual_publishing (line 223) | async def dual_publishing(self, data):
    method publish_persistent_comment_with_history (line 243) | def publish_persistent_comment_with_history(git_provider: GitProvider,
    method extract_link (line 358) | def extract_link(self, s):
    method _prepare_prediction (line 367) | async def _prepare_prediction(self, model: str) -> dict:
    method _get_prediction (line 386) | async def _get_prediction(self, model: str, patches_diff: str, patches...
    method analyze_self_reflection_response (line 422) | async def analyze_self_reflection_response(self, data, response_reflect):
    method _truncate_if_needed (line 477) | def _truncate_if_needed(suggestion):
    method _prepare_pr_code_suggestions (line 488) | def _prepare_pr_code_suggestions(self, predictions: str) -> Dict:
    method push_inline_code_suggestions (line 539) | async def push_inline_code_suggestions(self, data):
    method dedent_code (line 581) | def dedent_code(self, relevant_file, relevant_lines_start, new_code_sn...
    method validate_one_liner_suggestion_not_repeating_code (line 621) | def validate_one_liner_suggestion_not_repeating_code(self, suggestion):
    method remove_line_numbers (line 648) | def remove_line_numbers(self, patches_diff_list: List[str]) -> List[str]:
    method prepare_prediction_main (line 670) | async def prepare_prediction_main(self, model: str) -> dict:
    method convert_to_decoupled_with_line_numbers (line 736) | async def convert_to_decoupled_with_line_numbers(self, patches_diff_li...
    method generate_summarized_suggestions (line 772) | def generate_summarized_suggestions(self, data: Dict) -> str:
    method get_score_str (line 892) | def get_score_str(self, score: int) -> str:
    method self_reflect_on_suggestions (line 902) | async def self_reflect_on_suggestions(self,

FILE: pr_agent/tools/pr_config.py
  class PRConfig (line 8) | class PRConfig:
    method __init__ (line 12) | def __init__(self, pr_url: str, args=None, ai_handler=None):
    method run (line 22) | async def run(self):
    method _prepare_pr_configs (line 32) | def _prepare_pr_configs(self) -> str:

FILE: pr_agent/tools/pr_description.py
  class PRDescription (line 33) | class PRDescription:
    method __init__ (line 34) | def __init__(self, pr_url: str, args: list = None,
    method run (line 95) | async def run(self):
    method _prepare_prediction (line 203) | async def _prepare_prediction(self, model: str) -> None:
    method extend_uncovered_files (line 324) | async def extend_uncovered_files(self, original_prediction: str) -> str:
    method extend_additional_files (line 396) | async def extend_additional_files(self, remaining_files_list) -> str:
    method _get_prediction (line 425) | async def _get_prediction(self, model: str, patches_diff: str, prompt=...
    method _prepare_data (line 445) | def _prepare_data(self):
    method _prepare_labels (line 472) | def _prepare_labels(self) -> List[str]:
    method _prepare_pr_answer_with_markers (line 499) | def _prepare_pr_answer_with_markers(self) -> Tuple[str, str, str, List...
    method _prepare_pr_answer (line 550) | def _prepare_pr_answer(self) -> Tuple[str, str, str, List[dict]]:
    method _prepare_file_labels (line 626) | def _prepare_file_labels(self):
    method process_pr_files_prediction (line 660) | def process_pr_files_prediction(self, pr_body, value):
    method add_file_data (line 740) | def add_file_data(self, delta_nbsp, diff_plus_minus, file_change_descr...
  function count_chars_without_html (line 774) | def count_chars_without_html(string):
  function insert_br_after_x_chars (line 781) | def insert_br_after_x_chars(text: str, x=70):
  function replace_code_tags (line 855) | def replace_code_tags(text):

FILE: pr_agent/tools/pr_generate_labels.py
  class PRGenerateLabels (line 19) | class PRGenerateLabels:
    method __init__ (line 20) | def __init__(self, pr_url: str, args: list = None,
    method run (line 65) | async def run(self):
    method _prepare_prediction (line 104) | async def _prepare_prediction(self, model: str) -> None:
    method _get_prediction (line 124) | async def _get_prediction(self, model: str) -> str:
    method _prepare_data (line 153) | def _prepare_data(self):
    method _prepare_labels (line 159) | def _prepare_labels(self) -> List[str]:

FILE: pr_agent/tools/pr_help_docs.py
  function modify_answer_section (line 23) | def modify_answer_section(ai_response: str) -> str | None:
  function extract_model_answer_and_relevant_sources (line 48) | def extract_model_answer_and_relevant_sources(ai_response: str) -> str |...
  function get_maximal_text_input_length_for_token_count_estimation (line 75) | def get_maximal_text_input_length_for_token_count_estimation():
  function return_document_headings (line 81) | def return_document_headings(text: str, ext: str) -> str:
  function map_documentation_files_to_contents (line 120) | def map_documentation_files_to_contents(base_path: str, doc_files: list[...
  function aggregate_documentation_files_for_prompt_contents (line 147) | def aggregate_documentation_files_for_prompt_contents(file_path_to_conte...
  function format_markdown_q_and_a_response (line 169) | def format_markdown_q_and_a_response(question_str: str, response_str: st...
  function format_markdown_header (line 194) | def format_markdown_header(header: str) -> str:
  function clean_markdown_content (line 221) | def clean_markdown_content(content: str) -> str:
  class PredictionPreparator (line 259) | class PredictionPreparator:
    method __init__ (line 260) | def __init__(self, ai_handler, vars, system_prompt, user_prompt):
    method __call__ (line 272) | async def __call__(self, model: str) -> str:
  class PRHelpDocs (line 285) | class PRHelpDocs(object):
    method __init__ (line 286) | def __init__(self, ctx_url, ai_handler:partial[BaseAiHandler,] = LiteL...
    method run (line 329) | async def run(self):
    method _find_all_document_files_matching_exts (line 396) | def _find_all_document_files_matching_exts(self, abs_docs_path: str, i...
    method _gen_filenames_to_contents_map_from_repo (line 421) | def _gen_filenames_to_contents_map_from_repo(self) -> dict[str, str]:
    method _trim_docs_input (line 457) | def _trim_docs_input(self, docs_input: str, max_allowed_txt_input: int...
    method _rank_docs_and_return_them_as_prompt (line 494) | async def _rank_docs_and_return_them_as_prompt(self, docs_filepath_to_...
    method _format_model_answer (line 536) | def _format_model_answer(self, response_str: str, relevant_sections: l...

FILE: pr_agent/tools/pr_help_message.py
  function extract_header (line 19) | def extract_header(snippet):
  class PRHelpMessage (line 32) | class PRHelpMessage:
    method __init__ (line 33) | def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiH...
    method _prepare_prediction (line 48) | async def _prepare_prediction(self, model: str):
    method parse_args (line 61) | def parse_args(self, args):
    method format_markdown_header (line 68) | def format_markdown_header(self, header: str) -> str:
    method run (line 96) | async def run(self):
    method prepare_relevant_snippets (line 272) | async def prepare_relevant_snippets(self, sim_results):
  function generate_bbdc_table (line 293) | def generate_bbdc_table(column_arr_1, column_arr_2):

FILE: pr_agent/tools/pr_line_questions.py
  class PR_LineQuestions (line 21) | class PR_LineQuestions:
    method __init__ (line 22) | def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiH...
    method parse_args (line 47) | def parse_args(self, args):
    method run (line 55) | async def run(self):
    method _load_conversation_history (line 103) | def _load_conversation_history(self) -> str:
    method _get_prediction (line 151) | async def _get_prediction(self, model: str):

FILE: pr_agent/tools/pr_questions.py
  class PRQuestions (line 18) | class PRQuestions:
    method __init__ (line 19) | def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiH...
    method parse_args (line 46) | def parse_args(self, args):
    method run (line 53) | async def run(self):
    method identify_image_in_comment (line 81) | def identify_image_in_comment(self):
    method _prepare_prediction (line 94) | async def _prepare_prediction(self, model: str):
    method _get_prediction (line 103) | async def _get_prediction(self, model: str):
    method gitlab_protections (line 119) | def gitlab_protections(self, model_answer: str) -> str:
    method _prepare_pr_answer (line 128) | def _prepare_pr_answer(self) -> str:

FILE: pr_agent/tools/pr_reviewer.py
  class PRReviewer (line 30) | class PRReviewer:
    method __init__ (line 35) | def __init__(self, pr_url: str, is_answer: bool = False, is_auto: bool...
    method parse_incremental (line 111) | def parse_incremental(self, args: List[str]):
    method run (line 120) | async def run(self) -> None:
    method _should_publish_review_no_suggestions (line 186) | def _should_publish_review_no_suggestions(self, pr_review: str) -> bool:
    method _prepare_prediction (line 189) | async def _prepare_prediction(self, model: str) -> None:
    method _get_prediction (line 203) | async def _get_prediction(self, model: str) -> str:
    method _prepare_pr_review (line 229) | def _prepare_pr_review(self) -> str:
    method _get_user_answers (line 281) | def _get_user_answers(self) -> Tuple[str, str]:
    method _get_previous_review_comment (line 305) | def _get_previous_review_comment(self):
    method _remove_previous_review_comment (line 318) | def _remove_previous_review_comment(self, comment):
    method _can_run_incremental_review (line 328) | def _can_run_incremental_review(self) -> bool:
    method set_review_labels (line 365) | def set_review_labels(self, data):
    method auto_approve_logic (line 417) | def auto_approve_logic(self):

FILE: pr_agent/tools/pr_similar_issue.py
  class PRSimilarIssue (line 18) | class PRSimilarIssue:
    method __init__ (line 19) | def __init__(self, issue_url: str, ai_handler, args: list = None):
    method run (line 260) | async def run(self):
    method _process_issue (line 390) | def _process_issue(self, issue):
    method _update_index_with_issues (line 401) | def _update_index_with_issues(self, issues_list, repo_name_for_index, ...
    method _update_table_with_issues (line 496) | def _update_table_with_issues(self, issues_list, repo_name_for_index, ...
    method _update_qdrant_with_issues (line 587) | def _update_qdrant_with_issues(self, issues_list, repo_name_for_index,...
  class IssueLevel (line 682) | class IssueLevel(str, Enum):
  class Metadata (line 687) | class Metadata(BaseModel):
    class Config (line 693) | class Config:
  class Record (line 697) | class Record(BaseModel):
  class Corpus (line 703) | class Corpus(BaseModel):
    method append (line 706) | def append(self, r: Record):

FILE: pr_agent/tools/pr_update_changelog.py
  class PRUpdateChangelog (line 22) | class PRUpdateChangelog:
    method __init__ (line 23) | def __init__(self, pr_url: str, cli_mode=False, args=None, ai_handler:...
    method run (line 55) | async def run(self):
    method _prepare_prediction (line 94) | async def _prepare_prediction(self, model: str):
    method _get_prediction (line 103) | async def _get_prediction(self, model: str):
    method _prepare_changelog_update (line 125) | def _prepare_changelog_update(self) -> Tuple[str, str]:
    method _push_changelog_update (line 143) | def _push_changelog_update(self, new_file_content, answer):
    method _get_default_changelog (line 170) | def _get_default_changelog(self):
    method _get_changelog_file (line 185) | def _get_changelog_file(self):

FILE: pr_agent/tools/ticket_pr_compliance_check.py
  function find_jira_tickets (line 16) | def find_jira_tickets(text):
  function extract_ticket_links_from_pr_description (line 38) | def extract_ticket_links_from_pr_description(pr_description, repo_path, ...
  function extract_ticket_links_from_branch_name (line 68) | def extract_ticket_links_from_branch_name(branch_name, repo_path, base_u...
  function extract_tickets (line 108) | async def extract_tickets(git_provider):
  function extract_and_cache_pr_tickets (line 224) | async def extract_and_cache_pr_tickets(git_provider, vars):
  function check_tickets_relevancy (line 252) | def check_tickets_relevancy():

FILE: tests/e2e_tests/langchain_ai_handler.py
  function check_settings (line 9) | def check_settings():
  function measure_performance (line 25) | async def measure_performance(handler, num_requests=3):
  function test (line 53) | async def test():

FILE: tests/e2e_tests/test_bitbucket_app.py
  function test_e2e_run_bitbucket_app (line 27) | def test_e2e_run_bitbucket_app():

FILE: tests/e2e_tests/test_gitea_app.py
  function test_e2e_run_gitea_app (line 22) | def test_e2e_run_gitea_app():

FILE: tests/e2e_tests/test_github_app.py
  function test_e2e_run_github_app (line 23) | def test_e2e_run_github_app():

FILE: tests/e2e_tests/test_gitlab_webhook.py
  function test_e2e_run_github_app (line 24) | def test_e2e_run_github_app():

FILE: tests/health_test/main.py
  function run_async (line 19) | async def run_async():
  function run (line 62) | def run():

FILE: tests/unittest/test_add_docs_trigger.py
  function test_add_docs_trigger (line 21) | async def test_add_docs_trigger(monkeypatch, action, draft, state, shoul...

FILE: tests/unittest/test_aws_secrets_manager_provider.py
  class TestAWSSecretsManagerProvider (line 10) | class TestAWSSecretsManagerProvider:
    method _provider (line 12) | def _provider(self):
    method test_get_secret_success (line 35) | def test_get_secret_success(self):
    method test_get_all_secrets_success (line 43) | def test_get_all_secrets_success(self):
    method test_get_secret_failure (line 52) | def test_get_secret_failure(self):
    method test_get_all_secrets_failure (line 59) | def test_get_all_secrets_failure(self):
    method test_store_secret_update_existing (line 66) | def test_store_secret_update_existing(self):
    method test_init_failure_invalid_config (line 76) | def test_init_failure_invalid_config(self):
    method test_store_secret_failure (line 85) | def test_store_secret_failure(self):

FILE: tests/unittest/test_azure_devops_comment.py
  class TestAzureDevopsProviderPublishComment (line 8) | class TestAzureDevopsProviderPublishComment(unittest.TestCase):
    method test_publish_comment_default_closed (line 10) | def test_publish_comment_default_closed(self, mock_get_settings):
    method test_publish_comment_active (line 34) | def test_publish_comment_active(self, mock_get_settings):
    method test_default_comment_status_from_config_file (line 57) | def test_default_comment_status_from_config_file(self):

FILE: tests/unittest/test_azure_devops_parsing.py
  class TestAzureDevOpsParsing (line 4) | class TestAzureDevOpsParsing:
    method test_regular_address (line 5) | def test_regular_address(self):
    method test_visualstudio_address (line 11) | def test_visualstudio_address(self):
    method test_self_hosted_address (line 17) | def test_self_hosted_address(self):

FILE: tests/unittest/test_bitbucket_provider.py
  class TestBitbucketProvider (line 10) | class TestBitbucketProvider:
    method test_parse_pr_url (line 11) | def test_parse_pr_url(self):
  class TestBitbucketServerProvider (line 19) | class TestBitbucketServerProvider:
    method test_parse_pr_url (line 20) | def test_parse_pr_url(self):
    method test_parse_pr_url_with_users (line 27) | def test_parse_pr_url_with_users(self):
    method mock_get_content_of_file (line 34) | def mock_get_content_of_file(self, project_key, repository_slug, filen...
    method mock_get_from_bitbucket_60 (line 47) | def mock_get_from_bitbucket_60(self, url):
    method mock_get_from_bitbucket_70 (line 55) | def mock_get_from_bitbucket_70(self, url):
    method mock_get_from_bitbucket_816 (line 63) | def mock_get_from_bitbucket_816(self, url):
    method test_get_diff_files_simple_diverge_70 (line 84) | def test_get_diff_files_simple_diverge_70(self):
    method test_get_diff_files_diverge_with_merge_commit_70 (line 137) | def test_get_diff_files_diverge_with_merge_commit_70(self):
    method get_multi_merge_diverge_mock_client (line 199) | def get_multi_merge_diverge_mock_client(self, api_version):
    method test_get_diff_files_multi_merge_diverge_60 (line 242) | def test_get_diff_files_multi_merge_diverge_60(self):
    method test_get_diff_files_multi_merge_diverge_70 (line 264) | def test_get_diff_files_multi_merge_diverge_70(self):
    method test_get_diff_files_multi_merge_diverge_816 (line 286) | def test_get_diff_files_multi_merge_diverge_816(self):

FILE: tests/unittest/test_clip_tokens.py
  class TestClipTokens (line 9) | class TestClipTokens:
    method test_empty_input_text (line 12) | def test_empty_input_text(self):
    method test_text_under_token_limit (line 17) | def test_text_under_token_limit(self):
    method test_text_exactly_at_token_limit (line 24) | def test_text_exactly_at_token_limit(self):
    method test_text_over_token_limit_with_three_dots (line 36) | def test_text_over_token_limit_with_three_dots(self):
    method test_text_over_token_limit_without_three_dots (line 50) | def test_text_over_token_limit_without_three_dots(self):
    method test_negative_max_tokens (line 64) | def test_negative_max_tokens(self):
    method test_zero_max_tokens (line 73) | def test_zero_max_tokens(self):
    method test_delete_last_line_functionality (line 79) | def test_delete_last_line_functionality(self):
    method test_pre_computed_num_input_tokens (line 98) | def test_pre_computed_num_input_tokens(self):
    method test_pre_computed_tokens_under_limit (line 112) | def test_pre_computed_tokens_under_limit(self):
    method test_special_characters_and_unicode (line 125) | def test_special_characters_and_unicode(self):
    method test_multiline_text_handling (line 139) | def test_multiline_text_handling(self):
    method test_very_long_text (line 152) | def test_very_long_text(self):
    method test_encoder_exception_handling (line 166) | def test_encoder_exception_handling(self):
    method test_zero_division_scenario (line 178) | def test_zero_division_scenario(self):
    method test_various_edge_cases (line 192) | def test_various_edge_cases(self):
    method test_parameter_combinations (line 217) | def test_parameter_combinations(self):
    method test_num_output_chars_zero_scenario (line 243) | def test_num_output_chars_zero_scenario(self):
    method test_logging_on_exception (line 257) | def test_logging_on_exception(self):
    method test_factor_safety_calculation (line 277) | def test_factor_safety_calculation(self):
    method test_clip_original_functionality (line 300) | def test_clip_original_functionality(self):

FILE: tests/unittest/test_codecommit_client.py
  class TestCodeCommitProvider (line 6) | class TestCodeCommitProvider:
    method test_get_differences (line 7) | def test_get_differences(self):
    method test_get_file (line 82) | def test_get_file(self):
    method test_get_pr (line 106) | def test_get_pr(self):

FILE: tests/unittest/test_codecommit_provider.py
  class TestCodeCommitFile (line 9) | class TestCodeCommitFile:
    method test_valid_parameters (line 12) | def test_valid_parameters(self):
  class TestCodeCommitProvider (line 29) | class TestCodeCommitProvider:
    method test_get_title (line 30) | def test_get_title(self):
    method test_get_pr_id (line 37) | def test_get_pr_id(self):
    method test_parse_pr_url (line 45) | def test_parse_pr_url(self):
    method test_is_valid_codecommit_hostname (line 52) | def test_is_valid_codecommit_hostname(self):
    method test_invalid_codecommit_url (line 90) | def test_invalid_codecommit_url(self):
    method test_get_file_extensions (line 95) | def test_get_file_extensions(self):
    method test_get_language_percentages (line 125) | def test_get_language_percentages(self):
    method test_get_edit_type (line 162) | def test_get_edit_type(self):
    method test_add_additional_newlines (line 176) | def test_add_additional_newlines(self):
    method test_remove_markdown_html (line 186) | def test_remove_markdown_html(self):

FILE: tests/unittest/test_config_loader_secrets.py
  class TestConfigLoaderSecrets (line 6) | class TestConfigLoaderSecrets:
    method test_apply_secrets_manager_config_success (line 8) | def test_apply_secrets_manager_config_success(self):
    method test_apply_secrets_manager_config_no_provider (line 27) | def test_apply_secrets_manager_config_no_provider(self):
    method test_apply_secrets_manager_config_not_aws (line 34) | def test_apply_secrets_manager_config_not_aws(self):
    method test_apply_secrets_to_config_nested_keys (line 54) | def test_apply_secrets_to_config_nested_keys(self):
    method test_apply_secrets_to_config_existing_value_preserved (line 72) | def test_apply_secrets_to_config_existing_value_preserved(self):
    method test_apply_secrets_to_config_single_key (line 86) | def test_apply_secrets_to_config_single_key(self):
    method test_apply_secrets_to_config_multiple_dots (line 100) | def test_apply_secrets_to_config_multiple_dots(self):
    method test_apply_secrets_manager_config_exception_handling (line 114) | def test_apply_secrets_manager_config_exception_handling(self):

FILE: tests/unittest/test_convert_to_markdown.py
  class TestConvertToMarkdown (line 48) | class TestConvertToMarkdown:
    method test_simple_dictionary_input (line 50) | def test_simple_dictionary_input(self):
    method test_simple_dictionary_input_without_gfm_supported (line 71) | def test_simple_dictionary_input_without_gfm_supported(self):
    method test_key_issues_to_review (line 93) | def test_key_issues_to_review(self):
    method test_ticket_compliance (line 126) | def test_ticket_compliance(self):
    method test_can_be_split (line 166) | def test_can_be_split(self):
    method test_contribution_time_cost_estimate (line 225) | def test_contribution_time_cost_estimate(self):
    method test_empty_dictionary_input (line 260) | def test_empty_dictionary_input(self):
    method test_dictionary_with_empty_dictionaries (line 267) | def test_dictionary_with_empty_dictionaries(self):
  class TestBR (line 275) | class TestBR:
    method test_br1 (line 276) | def test_br1(self):
    method test_br2 (line 286) | def test_br2(self):
    method test_br3 (line 298) | def test_br3(self):

FILE: tests/unittest/test_delete_hunks.py
  class TestOmitDeletionHunks (line 36) | class TestOmitDeletionHunks:
    method test_simple_patch_additions (line 38) | def test_simple_patch_additions(self):
    method test_patch_multiple_hunks (line 44) | def test_patch_multiple_hunks(self):
    method test_patch_only_deletions (line 52) | def test_patch_only_deletions(self):
    method test_empty_patch (line 69) | def test_empty_patch(self):
    method test_patch_one_hunk (line 75) | def test_patch_one_hunk(self):
    method test_patch_deletions_no_additions (line 81) | def test_patch_deletions_no_additions(self):

FILE: tests/unittest/test_extend_patch.py
  class TestExtendPatch (line 13) | class TestExtendPatch:
    method test_happy_path (line 15) | def test_happy_path(self):
    method test_empty_patch (line 25) | def test_empty_patch(self):
    method test_zero_num_lines (line 34) | def test_zero_num_lines(self):
    method test_no_hunks (line 42) | def test_no_hunks(self):
    method test_single_hunk (line 50) | def test_single_hunk(self):
    method test_multiple_hunks (line 61) | def test_multiple_hunks(self):
    method test_dynamic_context (line 81) | def test_dynamic_context(self):
  class TestExtendedPatchMoreLines (line 112) | class TestExtendedPatchMoreLines:
    class File (line 113) | class File:
      method __init__ (line 114) | def __init__(self, base_file, patch, head_file, filename, ai_file_su...
    method token_handler (line 122) | def token_handler(self):
    method pr_languages (line 129) | def pr_languages(self):
    method test_extend_patches_with_extra_lines (line 146) | def test_extend_patches_with_extra_lines(self, token_handler, pr_langu...
  class TestLoadLargeDiff (line 168) | class TestLoadLargeDiff:
    method test_no_newline (line 169) | def test_no_newline(self):
    method test_empty_inputs (line 192) | def test_empty_inputs(self):

FILE: tests/unittest/test_extract_issue_from_branch.py
  class TestExtractTicketsLinkFromBranchName (line 6) | class TestExtractTicketsLinkFromBranchName:
    method test_feature_slash_number_suffix (line 9) | def test_feature_slash_number_suffix(self):
    method test_fix_slash_number_suffix (line 16) | def test_fix_slash_number_suffix(self):
    method test_number_at_start_no_slash (line 23) | def test_number_at_start_no_slash(self):
    method test_empty_branch_returns_empty (line 30) | def test_empty_branch_returns_empty(self):
    method test_none_branch_returns_empty (line 35) | def test_none_branch_returns_empty(self):
    method test_no_digits_in_segment_returns_empty (line 40) | def test_no_digits_in_segment_returns_empty(self):
    method test_base_url_no_trailing_slash (line 47) | def test_base_url_no_trailing_slash(self):
    method test_disable_via_config_returns_empty (line 54) | def test_disable_via_config_returns_empty(self, monkeypatch):
    method test_invalid_custom_regex_returns_empty (line 69) | def test_invalid_custom_regex_returns_empty(self, monkeypatch):
    method test_custom_regex_without_capturing_group_falls_back_to_default (line 84) | def test_custom_regex_without_capturing_group_falls_back_to_default(se...
    method test_empty_repo_path_returns_empty (line 99) | def test_empty_repo_path_returns_empty(self):
    method test_multiple_matches_deduplicated (line 104) | def test_multiple_matches_deduplicated(self):

FILE: tests/unittest/test_file_filter.py
  class TestIgnoreFilter (line 5) | class TestIgnoreFilter:
    method test_no_ignores (line 6) | def test_no_ignores(self):
    method test_glob_ignores (line 19) | def test_glob_ignores(self, monkeypatch):
    method test_regex_ignores (line 40) | def test_regex_ignores(self, monkeypatch):
    method test_invalid_regex (line 61) | def test_invalid_regex(self, monkeypatch):
    method test_language_framework_ignores (line 82) | def test_language_framework_ignores(self, monkeypatch):
    method test_skip_invalid_ignore_language_framework (line 107) | def test_skip_invalid_ignore_language_framework(self, monkeypatch):

FILE: tests/unittest/test_find_line_number_of_relevant_line_in_file.py
  class TestFindLineNumberOfRelevantLineInFile (line 7) | class TestFindLineNumberOfRelevantLineInFile:
    method test_relevant_line_found_in_patch (line 9) | def test_relevant_line_found_in_patch(self):
    method test_similar_line_found_using_difflib (line 19) | def test_similar_line_found_using_difflib(self):
    method test_relevant_line_not_found (line 29) | def test_relevant_line_not_found(self):
    method test_relevant_file_not_found (line 39) | def test_relevant_file_not_found(self):
    method test_empty_relevant_line (line 49) | def test_empty_relevant_line(self):
    method test_relevant_line_found_but_deleted (line 59) | def test_relevant_line_found_but_deleted(self):

FILE: tests/unittest/test_fix_json_escape_char.py
  class TestFixJsonEscapeChar (line 4) | class TestFixJsonEscapeChar:
    method test_valid_json (line 5) | def test_valid_json(self):
    method test_single_control_char (line 11) | def test_single_control_char(self):
    method test_multiple_control_chars (line 17) | def test_multiple_control_chars(self):

FILE: tests/unittest/test_fix_output.py
  class TestTryFixJson (line 6) | class TestTryFixJson:
    method test_incomplete_code_suggestions (line 8) | def test_incomplete_code_suggestions(self):
    method test_incomplete_code_suggestions_new_line (line 27) | def test_incomplete_code_suggestions_new_line(self):
    method test_incomplete_code_suggestions_many_close_brackets (line 46) | def test_incomplete_code_suggestions_many_close_brackets(self):
    method test_incomplete_code_suggestions_relevant_file (line 65) | def test_incomplete_code_suggestions_relevant_file(self):

FILE: tests/unittest/test_fresh_vars_functionality.py
  function create_dynaconf_with_custom_loader (line 26) | def create_dynaconf_with_custom_loader(temp_dir, secrets_file):
  class TestFreshVarsGitLabScenario (line 56) | class TestFreshVarsGitLabScenario:
    method setup_method (line 66) | def setup_method(self):
    method teardown_method (line 71) | def teardown_method(self):
    method create_secrets_toml (line 78) | def create_secrets_toml(self, personal_access_token="initial_token", s...
    method test_gitlab_personal_access_token_reload (line 92) | def test_gitlab_personal_access_token_reload(self):
    method test_gitlab_multiple_fields_reload (line 125) | def test_gitlab_multiple_fields_reload(self):
  class TestFreshVarsCustomLoaderIntegration (line 163) | class TestFreshVarsCustomLoaderIntegration:
    method setup_method (line 171) | def setup_method(self):
    method teardown_method (line 176) | def teardown_method(self):
    method create_secrets_toml (line 183) | def create_secrets_toml(self, personal_access_token="initial_token", s...
    method test_fresh_vars_without_core_loaders (line 191) | def test_fresh_vars_without_core_loaders(self):
    method test_custom_loader_respects_fresh_vars (line 228) | def test_custom_loader_respects_fresh_vars(self):
  class TestFreshVarsBasicFunctionality (line 275) | class TestFreshVarsBasicFunctionality:
    method setup_method (line 283) | def setup_method(self):
    method teardown_method (line 288) | def teardown_method(self):
    method create_secrets_toml (line 295) | def create_secrets_toml(self, personal_access_token="initial_token"):
    method test_gitlab_credentials_not_cached_when_fresh (line 302) | def test_gitlab_credentials_not_cached_when_fresh(self):
    method test_fresh_vars_works_with_default_loaders (line 348) | def test_fresh_vars_works_with_default_loaders(self):

FILE: tests/unittest/test_get_max_tokens.py
  class TestGetMaxTokens (line 7) | class TestGetMaxTokens:
    method test_model_max_tokens (line 10) | def test_model_max_tokens(self, monkeypatch):
    method test_gpt54_model_max_tokens (line 26) | def test_gpt54_model_max_tokens(self, monkeypatch, model):
    method test_model_has_custom (line 39) | def test_model_has_custom(self, monkeypatch):
    method test_gpt_codex_models_max_tokens (line 59) | def test_gpt_codex_models_max_tokens(self, monkeypatch, model):
    method test_model_not_max_tokens_and_not_has_custom (line 73) | def test_model_not_max_tokens_and_not_has_custom(self, monkeypatch):
    method test_model_max_tokens_with__limit (line 88) | def test_model_max_tokens_with__limit(self, monkeypatch):
    method test_gemini_3_and_3_1_pro_preview (line 111) | def test_gemini_3_and_3_1_pro_preview(self, monkeypatch, model):
    method test_claude_opus_4_6_model_max_tokens (line 132) | def test_claude_opus_4_6_model_max_tokens(self, monkeypatch, model):
    method test_claude_sonnet_4_6_model_max_tokens (line 158) | def test_claude_sonnet_4_6_model_max_tokens(self, monkeypatch, model):

FILE: tests/unittest/test_gitea_provider.py
  class TestGiteaProvider (line 5) | class TestGiteaProvider:
    method test_gitea_provider_auth_header (line 8) | def test_gitea_provider_auth_header(self, mock_api_client_cls, mock_ge...

FILE: tests/unittest/test_github_action_output.py
  class TestGitHubOutput (line 7) | class TestGitHubOutput:
    method test_github_action_output_enabled (line 8) | def test_github_action_output_enabled(self, monkeypatch, tmp_path):
    method test_github_action_output_disabled (line 25) | def test_github_action_output_disabled(self, monkeypatch, tmp_path):
    method test_github_action_output_notset (line 35) | def test_github_action_output_notset(self, monkeypatch, tmp_path):
    method test_github_action_output_error_case (line 45) | def test_github_action_output_error_case(self, monkeypatch, tmp_path):

FILE: tests/unittest/test_gitlab_provider.py
  class TestGitLabProvider (line 11) | class TestGitLabProvider:
    method mock_gitlab_client (line 15) | def mock_gitlab_client(self):
    method mock_project (line 20) | def mock_project(self):
    method gitlab_provider (line 25) | def gitlab_provider(self, mock_gitlab_client, mock_project):
    method test_get_pr_file_content_success (line 40) | def test_get_pr_file_content_success(self, gitlab_provider, mock_proje...
    method test_get_pr_file_content_with_bytes (line 51) | def test_get_pr_file_content_with_bytes(self, gitlab_provider, mock_pr...
    method test_get_pr_file_content_file_not_found (line 61) | def test_get_pr_file_content_file_not_found(self, gitlab_provider, moc...
    method test_get_pr_file_content_other_exception (line 69) | def test_get_pr_file_content_other_exception(self, gitlab_provider, mo...
    method test_create_or_update_pr_file_create_new (line 76) | def test_create_or_update_pr_file_create_new(self, gitlab_provider, mo...
    method test_create_or_update_pr_file_update_existing (line 96) | def test_create_or_update_pr_file_update_existing(self, gitlab_provide...
    method test_create_or_update_pr_file_update_exception (line 112) | def test_create_or_update_pr_file_update_exception(self, gitlab_provid...
    method test_has_create_or_update_pr_file_method (line 120) | def test_has_create_or_update_pr_file_method(self, gitlab_provider):
    method test_method_signature_compatibility (line 124) | def test_method_signature_compatibility(self, gitlab_provider):
    method test_content_encoding_handling (line 141) | def test_content_encoding_handling(self, gitlab_provider, mock_project...
    method test_get_gitmodules_map_parsing (line 150) | def test_get_gitmodules_map_parsing(self, gitlab_provider, mock_project):
    method test_project_by_path_requires_exact_match (line 173) | def test_project_by_path_requires_exact_match(self, gitlab_provider):
    method test_compare_submodule_cached (line 185) | def test_compare_submodule_cached(self, gitlab_provider):

FILE: tests/unittest/test_gitlab_webhook_port.py
  function test_start_uses_port_env (line 8) | def test_start_uses_port_env(monkeypatch):
  function test_start_invalid_port_env (line 19) | def test_start_invalid_port_env(monkeypatch):
  function test_start_default_port (line 29) | def test_start_default_port(monkeypatch):
  function test_start_invalid_port_range (line 39) | def test_start_invalid_port_range(monkeypatch):

FILE: tests/unittest/test_handle_patch_deletions.py
  class TestHandlePatchDeletions (line 36) | class TestHandlePatchDeletions:
    method test_handle_patch_deletions_happy_path_new_file_content_exists (line 38) | def test_handle_patch_deletions_happy_path_new_file_content_exists(self):
    method test_handle_patch_deletions_edge_case_new_file_content_empty (line 47) | def test_handle_patch_deletions_edge_case_new_file_content_empty(self):
    method test_handle_patch_deletions_edge_case_patch_and_patch_new_are_equal (line 56) | def test_handle_patch_deletions_edge_case_patch_and_patch_new_are_equa...
    method test_handle_patch_deletions_edge_case_patch_and_patch_new_are_not_equal (line 65) | def test_handle_patch_deletions_edge_case_patch_and_patch_new_are_not_...

FILE: tests/unittest/test_ignore_repositories.py
  function make_bitbucket_payload (line 9) | def make_bitbucket_payload(full_name):
  function make_github_body (line 24) | def make_github_body(full_name):
  function make_gitlab_body (line 31) | def make_gitlab_body(full_name):
  class TestIgnoreRepositories (line 43) | class TestIgnoreRepositories:
    method setup_method (line 44) | def setup_method(self):
    method test_should_ignore_matching_repository (line 48) | def test_should_ignore_matching_repository(self, provider_name, provid...
    method test_should_not_ignore_non_matching_repository (line 60) | def test_should_not_ignore_non_matching_repository(self, provider_name...
    method test_should_not_ignore_when_config_empty (line 72) | def test_should_not_ignore_when_config_empty(self, provider_name, prov...

FILE: tests/unittest/test_language_handler.py
  class TestSortFilesByMainLanguages (line 38) | class TestSortFilesByMainLanguages:
    method test_happy_path_sort_files_by_main_languages (line 40) | def test_happy_path_sort_files_by_main_languages(self):
    method test_edge_case_empty_languages (line 58) | def test_edge_case_empty_languages(self):
    method test_edge_case_empty_files (line 68) | def test_edge_case_empty_files(self):
    method test_edge_case_languages_with_no_extensions (line 77) | def test_edge_case_languages_with_no_extensions(self):
    method test_edge_case_files_with_bad_extensions_only (line 94) | def test_edge_case_files_with_bad_extensions_only(self):
    method test_general_behaviour_sort_files_by_main_languages (line 105) | def test_general_behaviour_sort_files_by_main_languages(self):

FILE: tests/unittest/test_litellm_reasoning_effort.py
  function create_mock_settings (line 9) | def create_mock_settings(reasoning_effort_value):
  function create_mock_acompletion_response (line 27) | def create_mock_acompletion_response():
  function mock_logger (line 38) | def mock_logger():
  class TestLiteLLMReasoningEffort (line 46) | class TestLiteLLMReasoningEffort:
    method test_gpt5_valid_reasoning_effort_none (line 63) | async def test_gpt5_valid_reasoning_effort_none(self, monkeypatch, moc...
    method test_gpt5_valid_reasoning_effort_low (line 89) | async def test_gpt5_valid_reasoning_effort_low(self, monkeypatch, mock...
    method test_gpt5_valid_reasoning_effort_medium (line 110) | async def test_gpt5_valid_reasoning_effort_medium(self, monkeypatch, m...
    method test_gpt5_valid_reasoning_effort_high (line 131) | async def test_gpt5_valid_reasoning_effort_high(self, monkeypatch, moc...
    method test_gpt5_valid_reasoning_effort_xhigh (line 152) | async def test_gpt5_valid_reasoning_effort_xhigh(self, monkeypatch, mo...
    method test_gpt5_valid_reasoning_effort_minimal (line 173) | async def test_gpt5_valid_reasoning_effort_minimal(self, monkeypatch, ...
    method test_gpt5_invalid_reasoning_effort_with_warning (line 196) | async def test_gpt5_invalid_reasoning_effort_with_warning(self, monkey...
    method test_gpt5_invalid_reasoning_effort_thinking_model (line 225) | async def test_gpt5_invalid_reasoning_effort_thinking_model(self, monk...
    method test_gpt5_none_config_defaults_to_medium (line 251) | async def test_gpt5_none_config_defaults_to_medium(self, monkeypatch, ...
    method test_gpt5_none_config_thinking_model_defaults_to_medium (line 277) | async def test_gpt5_none_config_thinking_model_defaults_to_medium(self...
    method test_gpt5_model_detection_various_versions (line 305) | async def test_gpt5_model_detection_various_versions(self, monkeypatch...
    method test_non_gpt5_model_no_thinking_kwargs (line 337) | async def test_non_gpt5_model_no_thinking_kwargs(self, monkeypatch, mo...
    method test_gpt5_suffix_removal (line 360) | async def test_gpt5_suffix_removal(self, monkeypatch, mock_logger):
    method test_gpt5_thinking_suffix_default_medium (line 382) | async def test_gpt5_thinking_suffix_default_medium(self, monkeypatch, ...
    method test_gpt5_regular_suffix_default_medium (line 402) | async def test_gpt5_regular_suffix_default_medium(self, monkeypatch, m...
    method test_gpt5_thinking_suffix_config_overrides_default (line 422) | async def test_gpt5_thinking_suffix_config_overrides_default(self, mon...
    method test_gpt5_info_logging_configured_value (line 445) | async def test_gpt5_info_logging_configured_value(self, monkeypatch, m...
    method test_gpt5_info_logging_default_value (line 464) | async def test_gpt5_info_logging_default_value(self, monkeypatch, mock...
    method test_gpt5_warning_only_for_invalid_non_none (line 483) | async def test_gpt5_warning_only_for_invalid_non_none(self, monkeypatc...
    method test_thinking_kwargs_gpt5_structure (line 525) | async def test_thinking_kwargs_gpt5_structure(self, monkeypatch, mock_...
    method test_thinking_kwargs_not_created_for_non_gpt5 (line 550) | async def test_thinking_kwargs_not_created_for_non_gpt5(self, monkeypa...
    method test_empty_string_reasoning_effort (line 574) | async def test_empty_string_reasoning_effort(self, monkeypatch, mock_l...
    method test_case_sensitive_reasoning_effort (line 595) | async def test_case_sensitive_reasoning_effort(self, monkeypatch, mock...
    method test_whitespace_reasoning_effort (line 616) | async def test_whitespace_reasoning_effort(self, monkeypatch, mock_log...
    method test_gpt5_prefix_match_only (line 637) | async def test_gpt5_prefix_match_only(self, monkeypatch, mock_logger):

FILE: tests/unittest/test_load_yaml.py
  class TestLoadYaml (line 11) | class TestLoadYaml:
    method test_load_valid_yaml (line 13) | def test_load_valid_yaml(self):
    method test_load_invalid_yaml1 (line 18) | def test_load_invalid_yaml1(self):
    method test_load_invalid_yaml2 (line 40) | def test_load_invalid_yaml2(self):

FILE: tests/unittest/test_parse_code_suggestion.py
  class TestParseCodeSuggestion (line 37) | class TestParseCodeSuggestion:
    method test_empty_dict (line 39) | def test_empty_dict(self):
    method test_non_string_before_or_after (line 46) | def test_non_string_before_or_after(self):
    method test_no_code_example_key (line 57) | def test_no_code_example_key(self):
    method test_with_code_example_key (line 68) | def test_with_code_example_key(self):

FILE: tests/unittest/test_pr_update_changelog.py
  class TestPRUpdateChangelog (line 8) | class TestPRUpdateChangelog:
    method mock_git_provider (line 12) | def mock_git_provider(self):
    method mock_ai_handler (line 25) | def mock_ai_handler(self):
    method changelog_tool (line 32) | def changelog_tool(self, mock_git_provider, mock_ai_handler):
    method test_get_changelog_file_with_existing_content (line 48) | def test_get_changelog_file_with_existing_content(self, changelog_tool...
    method test_get_changelog_file_with_no_existing_content (line 61) | def test_get_changelog_file_with_no_existing_content(self, changelog_t...
    method test_get_changelog_file_with_bytes_content (line 73) | def test_get_changelog_file_with_bytes_content(self, changelog_tool, m...
    method test_get_changelog_file_with_exception (line 86) | def test_get_changelog_file_with_exception(self, changelog_tool, mock_...
    method test_prepare_changelog_update_with_existing_content (line 98) | def test_prepare_changelog_update_with_existing_content(self, changelo...
    method test_prepare_changelog_update_without_existing_content (line 113) | def test_prepare_changelog_update_without_existing_content(self, chang...
    method test_prepare_changelog_update_no_commit (line 127) | def test_prepare_changelog_update_no_commit(self, changelog_tool):
    method test_run_without_push_support (line 142) | async def test_run_without_push_support(self, changelog_tool, mock_git...
    method test_run_with_push_support (line 160) | async def test_run_with_push_support(self, changelog_tool, mock_git_pr...
    method test_push_changelog_update (line 186) | def test_push_changelog_update(self, changelog_tool, mock_git_provider):
    method test_gitlab_provider_method_detection (line 210) | def test_gitlab_provider_method_detection(self, changelog_tool, mock_g...
    method test_changelog_order_preservation (line 235) | def test_changelog_order_preservation(self, changelog_tool, existing_c...

FILE: tests/unittest/test_secret_provider_factory.py
  class TestSecretProviderFactory (line 8) | class TestSecretProviderFactory:
    method test_get_secret_provider_none_when_not_configured (line 10) | def test_get_secret_provider_none_when_not_configured(self):
    method test_get_secret_provider_google_cloud_storage (line 19) | def test_get_secret_provider_google_cloud_storage(self):
    method test_get_secret_provider_aws_secrets_manager (line 34) | def test_get_secret_provider_aws_secrets_manager(self):
    method test_get_secret_provider_unknown_provider (line 49) | def test_get_secret_provider_unknown_provider(self):
    method test_get_secret_provider_initialization_error (line 59) | def test_get_secret_provider_initialization_error(self):

FILE: tests/unittest/test_similar_issue_non_github.py
  function test_similar_issue_non_github_publishes_message (line 7) | async def test_similar_issue_non_github_publishes_message(monkeypatch):
  function test_similar_issue_non_github_no_publish (line 38) | async def test_similar_issue_non_github_no_publish(monkeypatch):

FILE: tests/unittest/test_try_fix_yaml.py
  class TestTryFixYaml (line 6) | class TestTryFixYaml:
    method test_valid_yaml (line 9) | def test_valid_yaml(self):
    method test_add_relevant_line (line 15) | def test_add_relevant_line(self):
    method test_extract_snippet (line 21) | def test_extract_snippet(self):
    method test_empty_yaml_fixed (line 35) | def test_empty_yaml_fixed(self):
    method test_no_initial_yaml (line 41) | def test_no_initial_yaml(self):
    method test_with_initial_yaml (line 63) | def test_with_initial_yaml(self):
    method test_with_brackets_yaml_content (line 86) | def test_with_brackets_yaml_content(self):
    method test_tab_indent_yaml (line 104) | def test_tab_indent_yaml(self):
    method test_leading_plus_mark_code (line 121) | def test_leading_plus_mark_code(self):
    method test_inconsistent_indentation_in_block_scalar_yaml (line 142) | def test_inconsistent_indentation_in_block_scalar_yaml(self):
    method test_inconsistent_and_insufficient_indentation_in_block_scalar_yaml (line 182) | def test_inconsistent_and_insufficient_indentation_in_block_scalar_yam...
    method test_wrong_indentation_code_block_scalar (line 220) | def test_wrong_indentation_code_block_scalar(self):
Condensed preview — 216 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,616K chars).
[
  {
    "path": ".dockerignore",
    "chars": 77,
    "preview": ".venv/\nvenv/\npr_agent/settings/.secrets.toml\npics/\npr_agent.egg-info/\nbuild/\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "chars": 912,
    "preview": "name: \"\\U0001FAB2 Bug Report\"\ndescription: Submit a bug report\nlabels: [\"bug\"]\nbody:\n\n  - type: dropdown\n    id: informa"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 310,
    "preview": "blank_issues_enabled: false\nversion: 0.1\ncontact_links:\n  - name: Discussions\n    url: https://github.com/qodo-ai/pr-age"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "chars": 506,
    "preview": "name: \"\\U0001F4A1 Feature request\"\ndescription: Submit a proposal/request for a new PR-Agent feature\nlabels: [\"feature\"]"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/miscellaneous.yml",
    "chars": 889,
    "preview": "name: \"❔ General Issue\"\ndescription: Submit a general issue\nlabels: [\"general\"]\nbody:\n\n  - type: dropdown\n    id: inform"
  },
  {
    "path": ".github/workflows/build-and-test.yaml",
    "chars": 816,
    "preview": "name: Build-and-test\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  build-a"
  },
  {
    "path": ".github/workflows/code_coverage.yaml",
    "chars": 1340,
    "preview": "name: Code-coverage\n\non:\n  workflow_dispatch:\n  # push:\n  #   branches:\n  #     - main\n  pull_request:\n    branches:\n   "
  },
  {
    "path": ".github/workflows/docs-ci.yaml",
    "chars": 927,
    "preview": "name: docs-ci\non:\n  push:\n    branches:\n      - main\n      - add-docs-portal\n    paths:\n      - docs/**\npermissions:\n  c"
  },
  {
    "path": ".github/workflows/e2e_tests.yaml",
    "chars": 1474,
    "preview": "name: PR-Agent E2E tests\n\non:\n  workflow_dispatch:\n#  schedule:\n#    - cron: '0 0 * * *' # This cron expression runs the"
  },
  {
    "path": ".github/workflows/pr-agent-review.yaml",
    "chars": 967,
    "preview": "# This workflow enables developers to call PR-Agents `/[actions]` in PR's comments and upon PR creation.\n# Learn more at"
  },
  {
    "path": ".github/workflows/pre-commit.yml",
    "chars": 358,
    "preview": "# disabled. We might run it manually if needed.\nname: pre-commit\n\non:\n  workflow_dispatch:\n#  pull_request:\n#  push:\n#  "
  },
  {
    "path": ".gitignore",
    "chars": 150,
    "preview": ".idea/\n.lsp/\n.vscode/\n.env\n.venv/\nvenv/\npr_agent/settings/.secrets.toml\n__pycache__\ndist/\n*.egg-info/\nbuild/\n.DS_Store\nd"
  },
  {
    "path": ".pr_agent.toml",
    "chars": 356,
    "preview": "[pr_reviewer]\nenable_review_labels_effort = true\nenable_auto_approval = true\n\n[github_app]\npr_commands = [\n    \"/describ"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 1200,
    "preview": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\n\ndefault_langua"
  },
  {
    "path": "AGENTS.md",
    "chars": 6760,
    "preview": "# Repository Guidelines\n\n## Dos and Don’ts\n\n- **Do** match the interpreter requirement declared in `pyproject.toml` (Pyt"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1965,
    "preview": "## 2023-08-03\n\n### Optimized\n\n- Optimized PR diff processing by introducing caching for diff files, reducing the number "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 2338,
    "preview": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, and in the interest of fostering an open"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1456,
    "preview": "# Contributing to PR-Agent\n\nThank you for your interest in contributing to the PR-Agent project!\n\n## Getting Started\n\n1."
  },
  {
    "path": "Dockerfile.github_action",
    "chars": 433,
    "preview": "FROM python:3.12.10-slim AS base\n\nRUN apt-get update && apt-get install --no-install-recommends -y git curl && apt-get c"
  },
  {
    "path": "Dockerfile.github_action_dockerhub",
    "chars": 37,
    "preview": "FROM codiumai/pr-agent:github_action\n"
  },
  {
    "path": "LICENSE",
    "chars": 34523,
    "preview": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C)"
  },
  {
    "path": "MANIFEST.in",
    "chars": 76,
    "preview": "recursive-include pr_agent *.toml\nrecursive-exclude pr_agent *.secrets.toml\n"
  },
  {
    "path": "README.md",
    "chars": 13862,
    "preview": "<a href=\"https://github.com/Codium-ai/pr-agent/commits/main\">\n<img alt=\"GitHub\" src=\"https://img.shields.io/github/last-"
  },
  {
    "path": "RELEASE_NOTES.md",
    "chars": 5404,
    "preview": "## [Version 0.11] - 2023-12-07\n\n- codiumai/pr-agent:0.11\n- codiumai/pr-agent:0.11-github_app\n- codiumai/pr-agent:0.11-bi"
  },
  {
    "path": "SECURITY.md",
    "chars": 2054,
    "preview": "# Security Policy\n\nPR-Agent is an open-source tool to help efficiently review and handle pull requests. Qodo Merge is a "
  },
  {
    "path": "action.yaml",
    "chars": 213,
    "preview": "name: 'Codium PR Agent'\ndescription: 'Summarize, review and suggest improvements for pull requests'\nbranding:\n  icon: 'a"
  },
  {
    "path": "codecov.yml",
    "chars": 71,
    "preview": "comment: false\ncoverage:\n  status:\n    patch: false\n    project: false\n"
  },
  {
    "path": "docker/Dockerfile",
    "chars": 1540,
    "preview": "FROM python:3.12.10-slim AS base\n\nRUN apt update && apt install --no-install-recommends -y git curl && apt-get clean && "
  },
  {
    "path": "docker/Dockerfile.lambda",
    "chars": 520,
    "preview": "FROM public.ecr.aws/lambda/python:3.12 AS base\n\nRUN dnf update -y && \\\n    dnf install -y gcc python3-devel git && \\\n   "
  },
  {
    "path": "docs/README.md",
    "chars": 60,
    "preview": "# [Visit Our Docs Portal](https://qodo-merge-docs.qodo.ai/)\n"
  },
  {
    "path": "docs/docs/.gitbook.yaml",
    "chars": 68,
    "preview": "root: ./\n\nstructure:\n  readme: ../README.md\n  summary: ./summary.md\n"
  },
  {
    "path": "docs/docs/CNAME",
    "chars": 24,
    "preview": "qodo-merge-docs.qodo.ai\n"
  },
  {
    "path": "docs/docs/core-abilities/compression_strategy.md",
    "chars": 2508,
    "preview": "\n`Supported Git Platforms: GitHub, GitLab, Bitbucket`\n\n\n## Overview\n\nThere are two scenarios:\n\n1. The PR is small enough"
  },
  {
    "path": "docs/docs/core-abilities/dynamic_context.md",
    "chars": 4693,
    "preview": "\n`Supported Git Platforms: GitHub, GitLab, Bitbucket`\n\nPR-Agent uses an **asymmetric and dynamic context strategy** to i"
  },
  {
    "path": "docs/docs/core-abilities/fetching_ticket_context.md",
    "chars": 11639,
    "preview": "# Fetching Ticket Context for PRs\n\n`Supported Git Platforms: GitHub, GitLab, Bitbucket`\n\n!!! note \"Branch-name issue lin"
  },
  {
    "path": "docs/docs/core-abilities/index.md",
    "chars": 1636,
    "preview": "# Core Abilities\n\nPR-Agent utilizes a variety of core abilities to provide a comprehensive and efficient code review exp"
  },
  {
    "path": "docs/docs/core-abilities/interactivity.md",
    "chars": 1875,
    "preview": "# Interactivity\n\n`Supported Git Platforms: GitHub, GitLab`\n\n## Overview\n\nPR-Agent transforms static code reviews into in"
  },
  {
    "path": "docs/docs/core-abilities/metadata.md",
    "chars": 2553,
    "preview": "# Local and global metadata injection with multi-stage analysis\n\n`Supported Git Platforms: GitHub, GitLab, Bitbucket`\n\n1"
  },
  {
    "path": "docs/docs/core-abilities/self_reflection.md",
    "chars": 3307,
    "preview": "`Supported Git Platforms: GitHub, GitLab, Bitbucket`\n\nPR-Agent implements a **self-reflection** process where the AI mod"
  },
  {
    "path": "docs/docs/css/custom.css",
    "chars": 2670,
    "preview": "/* Neutral color scheme - ready for future branding */\n:root {\n    --md-primary-fg-color: #0f172a;\n    --md-accent-fg-co"
  },
  {
    "path": "docs/docs/faq/index.md",
    "chars": 7382,
    "preview": "# FAQ\n\n??? note \"Q: Can PR-Agent serve as a substitute for a human reviewer?\"\n    #### Answer:<span style=\"display:none;"
  },
  {
    "path": "docs/docs/index.md",
    "chars": 6421,
    "preview": "# Overview\n\n[PR-Agent](https://github.com/qodo-ai/pr-agent) is an open-source, AI-powered code review agent and a commun"
  },
  {
    "path": "docs/docs/installation/azure.md",
    "chars": 5178,
    "preview": "## Azure DevOps Pipeline\n\nYou can use a pre-built Action Docker image to run PR-Agent as an Azure DevOps pipeline.\nAdd t"
  },
  {
    "path": "docs/docs/installation/bitbucket.md",
    "chars": 2634,
    "preview": "## Run as a Bitbucket Pipeline\n\nYou can use the Bitbucket Pipeline system to run PR-Agent on every pull request open or "
  },
  {
    "path": "docs/docs/installation/gitea.md",
    "chars": 2054,
    "preview": "## Run a Gitea webhook server\n\n1. In Gitea create a new user and give it \"Reporter\" role for the intended group or proje"
  },
  {
    "path": "docs/docs/installation/github.md",
    "chars": 26029,
    "preview": "In this page we will cover how to install and run PR-Agent as a GitHub Action or GitHub App, and how to configure it for"
  },
  {
    "path": "docs/docs/installation/gitlab.md",
    "chars": 7183,
    "preview": "## Run as a GitLab Pipeline\n\nYou can use a pre-built Action Docker image to run PR-Agent as a GitLab pipeline. This is a"
  },
  {
    "path": "docs/docs/installation/index.md",
    "chars": 272,
    "preview": "# Installation\n\nThere are several ways to use PR-Agent:\n\n- [Locally](./locally.md)\n- [GitHub integration](./github.md)\n-"
  },
  {
    "path": "docs/docs/installation/locally.md",
    "chars": 6350,
    "preview": "To run PR-Agent locally, you first need to acquire two keys:\n\n1. An OpenAI key from [here](https://platform.openai.com/a"
  },
  {
    "path": "docs/docs/installation/pr_agent.md",
    "chars": 978,
    "preview": "# PR-Agent Installation Guide\n\nPR-Agent can be deployed in various environments and platforms. Choose the installation m"
  },
  {
    "path": "docs/docs/overview/data_privacy.md",
    "chars": 139,
    "preview": "## Self-hosted PR-Agent\n\n- If you self-host PR-Agent with your OpenAI (or other LLM provider) API key, it is between you"
  },
  {
    "path": "docs/docs/summary.md",
    "chars": 1507,
    "preview": "# Table of contents\n\n* [Overview](index.md)\n  * [Data Privacy](overview/data_privacy.md)\n\n## Installation\n\n* [Installati"
  },
  {
    "path": "docs/docs/tools/add_docs.md",
    "chars": 2437,
    "preview": "## Overview\n\nThe `add_docs` tool scans the PR code changes and suggests documentation for any code components that are m"
  },
  {
    "path": "docs/docs/tools/ask.md",
    "chars": 2169,
    "preview": "## Overview\n\nThe `ask` tool answers questions about the PR, based on the PR code changes. Make sure to be specific and c"
  },
  {
    "path": "docs/docs/tools/describe.md",
    "chars": 10920,
    "preview": "## Overview\n\nThe `describe` tool scans the PR code changes, and generates a description for the PR - title, type, summar"
  },
  {
    "path": "docs/docs/tools/generate_labels.md",
    "chars": 2565,
    "preview": "## Overview\n\nThe `generate_labels` tool scans the PR code changes and generates custom labels for the PR based on the co"
  },
  {
    "path": "docs/docs/tools/help.md",
    "chars": 525,
    "preview": "## Overview\n\nThe `help` tool provides a list of all the available tools and their descriptions.\nFor PR-Agent users, it a"
  },
  {
    "path": "docs/docs/tools/help_docs.md",
    "chars": 4626,
    "preview": "## Overview\n\nThe `help_docs` tool can answer a free-text question based on a git documentation folder.\n\nIt can be invoke"
  },
  {
    "path": "docs/docs/tools/improve.md",
    "chars": 15900,
    "preview": "## Overview\n\nThe `improve` tool scans the PR code changes, and automatically generates meaningful suggestions for improv"
  },
  {
    "path": "docs/docs/tools/index.md",
    "chars": 2920,
    "preview": "# Tools\n\nHere is a list of PR-Agent tools, each with a dedicated page that explains how to use it:\n\n| Tool              "
  },
  {
    "path": "docs/docs/tools/review.md",
    "chars": 9219,
    "preview": "## Overview\n\nThe `review` tool scans the PR code changes, and generates feedback about the PR, aiming to aid the reviewi"
  },
  {
    "path": "docs/docs/tools/similar_issues.md",
    "chars": 2148,
    "preview": "## Overview\n\nThe similar issue tool retrieves the most similar issues to the current issue.\nIt can be invoked manually b"
  },
  {
    "path": "docs/docs/tools/update_changelog.md",
    "chars": 1203,
    "preview": "## Overview\n\nThe `update_changelog` tool automatically updates the CHANGELOG.md file with the PR changes.\nIt can be invo"
  },
  {
    "path": "docs/docs/usage-guide/EXAMPLE_BEST_PRACTICE.md",
    "chars": 6632,
    "preview": "## Recommend Python Best Practices\n\nThis document outlines a series of recommended best practices for Python development"
  },
  {
    "path": "docs/docs/usage-guide/additional_configurations.md",
    "chars": 11191,
    "preview": "## Show possible configurations\n\nThe possible configurations of PR-Agent are stored in [here](https://github.com/qodo-ai"
  },
  {
    "path": "docs/docs/usage-guide/automations_and_usage.md",
    "chars": 15083,
    "preview": "## Local repo (CLI)\n\nWhen running from your locally cloned PR-Agent repo (CLI), your local configuration file will be us"
  },
  {
    "path": "docs/docs/usage-guide/changing_a_model.md",
    "chars": 13793,
    "preview": "## Changing a model in PR-Agent\n\nSee [here](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/algo/__init__.py) for"
  },
  {
    "path": "docs/docs/usage-guide/configuration_options.md",
    "chars": 5566,
    "preview": "The different tools and sub-tools used by PR-Agent are adjustable via a Git configuration file.\nThere are three main way"
  },
  {
    "path": "docs/docs/usage-guide/index.md",
    "chars": 1387,
    "preview": "# Usage guide\n\nThis section provides a detailed guide on how to use PR-Agent.\nIt includes information on how to adjust P"
  },
  {
    "path": "docs/docs/usage-guide/introduction.md",
    "chars": 1051,
    "preview": "After [installation](../installation/index.md), there are three basic ways to invoke PR-Agent:\n\n1. Locally running a CLI"
  },
  {
    "path": "docs/docs/usage-guide/mail_notifications.md",
    "chars": 1087,
    "preview": "\nUnfortunately, it is not possible in GitHub to disable mail notifications from a specific user.\nIf you are subscribed t"
  },
  {
    "path": "docs/mkdocs.yml",
    "chars": 3508,
    "preview": "site_name: PR-Agent\nrepo_url: https://github.com/qodo-ai/pr-agent\nrepo_name: Qodo-ai/pr-agent\n\nnav:\n  - Overview:\n    - "
  },
  {
    "path": "docs/overrides/main.html",
    "chars": 534,
    "preview": "{% extends \"base.html\" %}\n\n{% block announce %}\n  Open source PR Agent documentation. For the Qodo free version, Get Sta"
  },
  {
    "path": "docs/overrides/partials/footer.html",
    "chars": 3371,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initia"
  },
  {
    "path": "docs/overrides/partials/integrations/analytics/custom.html",
    "chars": 440,
    "preview": "<!-- Google Tag Manager -->\n<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':\n    new Date().getTime(),"
  },
  {
    "path": "github_action/entrypoint.sh",
    "chars": 65,
    "preview": "#!/bin/bash\npython /app/pr_agent/servers/github_action_runner.py\n"
  },
  {
    "path": "pr_agent/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pr_agent/agent/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pr_agent/agent/pr_agent.py",
    "chars": 5603,
    "preview": "import shlex\nfrom functools import partial\n\nfrom pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler\nfrom pr_"
  },
  {
    "path": "pr_agent/algo/__init__.py",
    "chars": 13256,
    "preview": "MAX_TOKENS = {\n    'text-embedding-ada-002': 8000,\n    'gpt-3.5-turbo': 16000,\n    'gpt-3.5-turbo-0125': 16000,\n    'gpt"
  },
  {
    "path": "pr_agent/algo/ai_handlers/base_ai_handler.py",
    "chars": 901,
    "preview": "from abc import ABC, abstractmethod\n\n\nclass BaseAiHandler(ABC):\n    \"\"\"\n    This class defines the interface for an AI h"
  },
  {
    "path": "pr_agent/algo/ai_handlers/langchain_ai_handler.py",
    "chars": 4746,
    "preview": "_LANGCHAIN_INSTALLED = False\n\ntry:\n    from langchain_core.messages import HumanMessage, SystemMessage\n    from langchai"
  },
  {
    "path": "pr_agent/algo/ai_handlers/litellm_ai_handler.py",
    "chars": 22183,
    "preview": "import os\nimport litellm\nimport openai\nimport requests\nfrom litellm import acompletion\nfrom tenacity import retry, retry"
  },
  {
    "path": "pr_agent/algo/ai_handlers/litellm_helpers.py",
    "chars": 4120,
    "preview": "import json\n\nimport openai\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent.log import get_logger\n\n\nasync "
  },
  {
    "path": "pr_agent/algo/ai_handlers/openai_ai_handler.py",
    "chars": 3201,
    "preview": "from os import environ\nfrom pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler\nimport openai\nfrom openai imp"
  },
  {
    "path": "pr_agent/algo/cli_args.py",
    "chars": 1862,
    "preview": "from base64 import b64decode, encode, b64encode\nimport hashlib\n\nclass CliArgs:\n    @staticmethod\n    def validate_user_a"
  },
  {
    "path": "pr_agent/algo/file_filter.py",
    "chars": 3815,
    "preview": "import fnmatch\nimport re\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent.log import get_logger\n\n\ndef filt"
  },
  {
    "path": "pr_agent/algo/git_patch_processing.py",
    "chars": 22174,
    "preview": "from __future__ import annotations\n\nimport re\nimport traceback\n\nfrom pr_agent.algo.types import EDIT_TYPE, FilePatchInfo"
  },
  {
    "path": "pr_agent/algo/language_handler.py",
    "chars": 3461,
    "preview": "# Language Selection, source: https://github.com/bigcode-project/bigcode-dataset/blob/main/language_selection/programmin"
  },
  {
    "path": "pr_agent/algo/pr_processing.py",
    "chars": 26304,
    "preview": "from __future__ import annotations\n\nimport traceback\nfrom typing import Callable, List, Tuple\n\nfrom github import RateLi"
  },
  {
    "path": "pr_agent/algo/token_handler.py",
    "chars": 6576,
    "preview": "from threading import Lock\nfrom math import ceil\nimport re\n\nfrom jinja2 import Environment, StrictUndefined\nfrom tiktoke"
  },
  {
    "path": "pr_agent/algo/types.py",
    "chars": 514,
    "preview": "from dataclasses import dataclass\nfrom enum import Enum\nfrom typing import Optional\n\n\nclass EDIT_TYPE(Enum):\n    ADDED ="
  },
  {
    "path": "pr_agent/algo/utils.py",
    "chars": 68630,
    "preview": "from __future__ import annotations\n\nimport ast\nimport copy\nimport difflib\nimport hashlib\nimport html\nimport json\nimport "
  },
  {
    "path": "pr_agent/cli.py",
    "chars": 4041,
    "preview": "import argparse\nimport asyncio\nimport os\n\nfrom pr_agent.agent.pr_agent import PRAgent, commands\nfrom pr_agent.algo.utils"
  },
  {
    "path": "pr_agent/cli_pip.py",
    "chars": 790,
    "preview": "from pr_agent import cli\nfrom pr_agent.config_loader import get_settings\n\n\ndef main():\n    # Fill in the following value"
  },
  {
    "path": "pr_agent/config_loader.py",
    "chars": 6014,
    "preview": "from os.path import abspath, dirname, join\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom dynaconf import Dy"
  },
  {
    "path": "pr_agent/custom_merge_loader.py",
    "chars": 8092,
    "preview": "from pathlib import Path\nimport tomllib #tomllib should be used instead of Py toml for Python 3.11+\n\nfrom jinja2.excepti"
  },
  {
    "path": "pr_agent/git_providers/__init__.py",
    "chars": 2748,
    "preview": "from starlette_context import context\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent.git_providers.azure"
  },
  {
    "path": "pr_agent/git_providers/azuredevops_provider.py",
    "chars": 29988,
    "preview": "from __future__ import annotations\n\nimport os\nfrom typing import Optional, Tuple\nfrom urllib.parse import urlparse\n\nfrom"
  },
  {
    "path": "pr_agent/git_providers/bitbucket_provider.py",
    "chars": 28591,
    "preview": "import difflib\nimport json\nimport re\nfrom typing import Optional, Tuple\nfrom urllib.parse import urlparse\n\nimport reques"
  },
  {
    "path": "pr_agent/git_providers/bitbucket_server_provider.py",
    "chars": 25799,
    "preview": "import difflib\nimport re\n\nfrom packaging.version import parse as parse_version\nfrom typing import Optional, Tuple\nfrom u"
  },
  {
    "path": "pr_agent/git_providers/codecommit_client.py",
    "chars": 12545,
    "preview": "import boto3\nimport botocore\n\n\nclass CodeCommitDifferencesResponse:\n    \"\"\"\n    CodeCommitDifferencesResponse is the res"
  },
  {
    "path": "pr_agent/git_providers/codecommit_provider.py",
    "chars": 19369,
    "preview": "import os\nimport re\nfrom collections import Counter\nfrom typing import List, Optional, Tuple\nfrom urllib.parse import ur"
  },
  {
    "path": "pr_agent/git_providers/gerrit_provider.py",
    "chars": 12814,
    "preview": "import json\nimport os\nimport pathlib\nimport shutil\nimport subprocess\nimport uuid\nfrom collections import Counter, namedt"
  },
  {
    "path": "pr_agent/git_providers/git_provider.py",
    "chars": 21893,
    "preview": "from abc import ABC, abstractmethod\n# enum EDIT_TYPE (ADDED, DELETED, MODIFIED, RENAMED)\nimport os\nimport shutil\nimport "
  },
  {
    "path": "pr_agent/git_providers/gitea_provider.py",
    "chars": 38506,
    "preview": "import json\nfrom typing import Any, Dict, List, Optional, Set, Tuple\nfrom urllib.parse import urlparse\n\nimport giteapy\nf"
  },
  {
    "path": "pr_agent/git_providers/github_provider.py",
    "chars": 57626,
    "preview": "import copy\nimport difflib\nimport hashlib\nimport itertools\nimport re\nimport time\nimport traceback\nimport json\nfrom datet"
  },
  {
    "path": "pr_agent/git_providers/gitlab_provider.py",
    "chars": 44202,
    "preview": "import difflib\nimport hashlib\nimport re\nimport urllib.parse\nfrom typing import Any, Optional, Tuple, Union\nfrom urllib.p"
  },
  {
    "path": "pr_agent/git_providers/local_git_provider.py",
    "chars": 8555,
    "preview": "from collections import Counter\nfrom pathlib import Path\nfrom typing import List\n\nfrom git import Repo\n\nfrom pr_agent.al"
  },
  {
    "path": "pr_agent/git_providers/utils.py",
    "chars": 6770,
    "preview": "import copy\nimport os\nimport tempfile\nimport traceback\n\nfrom dynaconf import Dynaconf\nfrom starlette_context import cont"
  },
  {
    "path": "pr_agent/identity_providers/__init__.py",
    "chars": 516,
    "preview": "from pr_agent.config_loader import get_settings\nfrom pr_agent.identity_providers.default_identity_provider import \\\n    "
  },
  {
    "path": "pr_agent/identity_providers/default_identity_provider.py",
    "chars": 390,
    "preview": "from pr_agent.identity_providers.identity_provider import (Eligibility,\n                                                "
  },
  {
    "path": "pr_agent/identity_providers/identity_provider.py",
    "chars": 375,
    "preview": "from abc import ABC, abstractmethod\nfrom enum import Enum\n\n\nclass Eligibility(Enum):\n    NOT_ELIGIBLE = 0\n    ELIGIBLE ="
  },
  {
    "path": "pr_agent/log/__init__.py",
    "chars": 1747,
    "preview": "import os\nos.environ[\"AUTO_CAST_FOR_DYNACONF\"] = \"false\"\nimport json\nimport logging\nimport sys\nfrom enum import Enum\n\nfr"
  },
  {
    "path": "pr_agent/secret_providers/__init__.py",
    "chars": 1039,
    "preview": "from pr_agent.config_loader import get_settings\n\n\ndef get_secret_provider():\n    if not get_settings().get(\"CONFIG.SECRE"
  },
  {
    "path": "pr_agent/secret_providers/aws_secrets_manager_provider.py",
    "chars": 2213,
    "preview": "import json\nimport boto3\nfrom botocore.exceptions import ClientError\n\nfrom pr_agent.config_loader import get_settings\nfr"
  },
  {
    "path": "pr_agent/secret_providers/google_cloud_storage_secret_provider.py",
    "chars": 1444,
    "preview": "import ujson\nfrom google.cloud import storage\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent.log import "
  },
  {
    "path": "pr_agent/secret_providers/secret_provider.py",
    "chars": 249,
    "preview": "from abc import ABC, abstractmethod\n\n\nclass SecretProvider(ABC):\n\n    @abstractmethod\n    def get_secret(self, secret_na"
  },
  {
    "path": "pr_agent/servers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pr_agent/servers/atlassian-connect-qodo-merge.json",
    "chars": 644,
    "preview": "{\n  \"name\": \"Qodo Merge\",\n  \"description\": \"Qodo Merge\",\n  \"key\": \"app_key\",\n  \"vendor\": {\n    \"name\": \"Qodo\",\n    \"url\""
  },
  {
    "path": "pr_agent/servers/atlassian-connect.json",
    "chars": 664,
    "preview": "{\n  \"name\": \"CodiumAI PR-Agent\",\n  \"description\": \"CodiumAI PR-Agent\",\n  \"key\": \"app_key\",\n  \"vendor\": {\n    \"name\": \"Co"
  },
  {
    "path": "pr_agent/servers/azuredevops_server_webhook.py",
    "chars": 8882,
    "preview": "# This file contains the code for the Azure DevOps Server webhook server.\n# The server listens for incoming webhooks fro"
  },
  {
    "path": "pr_agent/servers/bitbucket_app.py",
    "chars": 16337,
    "preview": "import base64\nimport copy\nimport hashlib\nimport json\nimport os\nimport re\nimport time\n\nimport jwt\nimport requests\nimport "
  },
  {
    "path": "pr_agent/servers/bitbucket_server_webhook.py",
    "chars": 11716,
    "preview": "import ast\nimport json\nimport os\nimport re\nfrom typing import List\n\nimport uvicorn\nfrom fastapi import APIRouter, FastAP"
  },
  {
    "path": "pr_agent/servers/gerrit_server.py",
    "chars": 1861,
    "preview": "import copy\nfrom enum import Enum\nfrom json import JSONDecodeError\n\nimport uvicorn\nfrom fastapi import APIRouter, FastAP"
  },
  {
    "path": "pr_agent/servers/gitea_app.py",
    "chars": 9632,
    "preview": "import copy\nimport os\nimport re\nfrom typing import Any, Dict\n\nfrom fastapi import APIRouter, FastAPI, HTTPException, Req"
  },
  {
    "path": "pr_agent/servers/github_action_runner.py",
    "chars": 8776,
    "preview": "import asyncio\nimport json\nimport os\nfrom typing import Union\n\nfrom pr_agent.agent.pr_agent import PRAgent\nfrom pr_agent"
  },
  {
    "path": "pr_agent/servers/github_app.py",
    "chars": 21332,
    "preview": "import asyncio.locks\nimport copy\nimport os\nimport re\nimport uuid\nfrom typing import Any, Dict, Tuple\n\nimport uvicorn\nfro"
  },
  {
    "path": "pr_agent/servers/github_lambda_webhook.py",
    "chars": 825,
    "preview": "from fastapi import FastAPI\nfrom mangum import Mangum\nfrom starlette.middleware import Middleware\nfrom starlette_context"
  },
  {
    "path": "pr_agent/servers/github_polling.py",
    "chars": 11555,
    "preview": "import asyncio\nimport multiprocessing\nimport time\nimport traceback\nfrom collections import deque\nfrom datetime import da"
  },
  {
    "path": "pr_agent/servers/gitlab_lambda_webhook.py",
    "chars": 829,
    "preview": "from fastapi import FastAPI\nfrom mangum import Mangum\nfrom starlette.middleware import Middleware\nfrom starlette_context"
  },
  {
    "path": "pr_agent/servers/gitlab_webhook.py",
    "chars": 16195,
    "preview": "import copy\nimport json\nimport os\nimport re\nfrom datetime import datetime\n\nimport uvicorn\nfrom fastapi import APIRouter,"
  },
  {
    "path": "pr_agent/servers/gunicorn_config.py",
    "chars": 5840,
    "preview": "import multiprocessing\nimport os\n\n# from prometheus_client import multiprocess\n\n# Sample Gunicorn configuration file.\n\n#"
  },
  {
    "path": "pr_agent/servers/help.py",
    "chars": 10446,
    "preview": "class HelpMessage:\n    @staticmethod\n    def get_general_commands_text():\n       commands_text = \"> - **/review**: Reque"
  },
  {
    "path": "pr_agent/servers/utils.py",
    "chars": 3085,
    "preview": "import hashlib\nimport hmac\nimport time\nfrom collections import defaultdict\nfrom typing import Any, Callable\n\nfrom fastap"
  },
  {
    "path": "pr_agent/settings/.secrets_template.toml",
    "chars": 4641,
    "preview": "# QUICKSTART:\n# Copy this file to .secrets.toml in the same folder.\n# The minimum workable settings - set openai.key to "
  },
  {
    "path": "pr_agent/settings/code_suggestions/pr_code_suggestions_prompts.toml",
    "chars": 6826,
    "preview": "[pr_code_suggestions_prompt]\nsystem=\"\"\"You are PR-Reviewer, an AI specializing in Pull Request (PR) code analysis and su"
  },
  {
    "path": "pr_agent/settings/code_suggestions/pr_code_suggestions_prompts_not_decoupled.toml",
    "chars": 6507,
    "preview": "[pr_code_suggestions_prompt_not_decoupled]\nsystem=\"\"\"You are PR-Reviewer, an AI specializing in Pull Request (PR) code a"
  },
  {
    "path": "pr_agent/settings/code_suggestions/pr_code_suggestions_reflect_prompts.toml",
    "chars": 7490,
    "preview": "[pr_code_suggestions_reflect_prompt]\nsystem=\"\"\"You are an AI language model specialized in reviewing and evaluating code"
  },
  {
    "path": "pr_agent/settings/configuration.toml",
    "chars": 14209,
    "preview": "# Important: This file contains all available configuration options.\n# Do not copy this entire file to your repository c"
  },
  {
    "path": "pr_agent/settings/custom_labels.toml",
    "chars": 606,
    "preview": "[config]\nenable_custom_labels=false\n\n## template for custom labels\n#[custom_labels.\"Bug fix\"]\n#description = \"\"\"Fixes a "
  },
  {
    "path": "pr_agent/settings/generated_code_ignore.toml",
    "chars": 695,
    "preview": "[generated_code]\n\n# Protocol Buffers\nprotobuf = [\n  \"**/*.pb.go\",\n  \"**/*.pb.cc\",\n  \"**/*_pb2.py\",\n  \"**/*.pb.swift\",\n  "
  },
  {
    "path": "pr_agent/settings/ignore.toml",
    "chars": 344,
    "preview": "[ignore]\n\nglob = [\n    # Ignore files and directories matching these glob patterns.\n    # See https://docs.python.org/3/"
  },
  {
    "path": "pr_agent/settings/language_extensions.toml",
    "chars": 13682,
    "preview": "[bad_extensions]\ndefault = [\n    'app',\n    'bin',\n    'bmp',\n    'bz2',\n    'class',\n    'csv',\n    'dat',\n    'db',\n  "
  },
  {
    "path": "pr_agent/settings/pr_add_docs.toml",
    "chars": 3287,
    "preview": "[pr_add_docs_prompt]\nsystem=\"\"\"You are PR-Doc, a language model that specializes in generating documentation for code co"
  },
  {
    "path": "pr_agent/settings/pr_custom_labels.toml",
    "chars": 1802,
    "preview": "[pr_custom_labels_prompt]\nsystem=\"\"\"You are PR-Reviewer, a language model designed to review a Git Pull Request (PR).\nYo"
  },
  {
    "path": "pr_agent/settings/pr_description_prompts.toml",
    "chars": 5405,
    "preview": "[pr_description_prompt]\nsystem=\"\"\"You are PR-Reviewer, a language model designed to review a Git Pull Request (PR).\nYour"
  },
  {
    "path": "pr_agent/settings/pr_evaluate_prompt_response.toml",
    "chars": 2592,
    "preview": "[pr_evaluate_prompt]\nprompt=\"\"\"\\\nYou are the PR-task-evaluator, a language model that compares and ranks the quality of "
  },
  {
    "path": "pr_agent/settings/pr_help_docs_headings_prompts.toml",
    "chars": 2396,
    "preview": "\n[pr_help_docs_headings_prompts]\nsystem=\"\"\"You are Doc-helper, a language model that ranks documentation files based on "
  },
  {
    "path": "pr_agent/settings/pr_help_docs_prompts.toml",
    "chars": 2467,
    "preview": "[pr_help_docs_prompts]\nsystem=\"\"\"You are Doc-helper, a language model designed to answer questions about a documentation"
  },
  {
    "path": "pr_agent/settings/pr_help_prompts.toml",
    "chars": 2152,
    "preview": "[pr_help_prompts]\nsystem=\"\"\"You are Doc-helper, a language models designed to answer questions about a documentation web"
  },
  {
    "path": "pr_agent/settings/pr_information_from_user_prompts.toml",
    "chars": 1243,
    "preview": "[pr_information_from_user_prompt]\nsystem=\"\"\"You are PR-Reviewer, a language model designed to review a Git Pull Request "
  },
  {
    "path": "pr_agent/settings/pr_line_questions_prompts.toml",
    "chars": 1929,
    "preview": "[pr_line_questions_prompt]\nsystem=\"\"\"You are PR-Reviewer, a language model designed to answer questions about a Git Pull"
  },
  {
    "path": "pr_agent/settings/pr_questions_prompts.toml",
    "chars": 1030,
    "preview": "[pr_questions_prompt]\nsystem=\"\"\"You are PR-Reviewer, a language model designed to answer questions about a Git Pull Requ"
  },
  {
    "path": "pr_agent/settings/pr_reviewer_prompts.toml",
    "chars": 12871,
    "preview": "[pr_review_prompt]\nsystem=\"\"\"You are PR-Reviewer, a language model designed to review a Git Pull Request (PR).\nYour task"
  },
  {
    "path": "pr_agent/settings/pr_update_changelog_prompts.toml",
    "chars": 1414,
    "preview": "[pr_update_changelog_prompt]\nsystem=\"\"\"You are a language model called PR-Changelog-Updater.\nYour task is to add a brief"
  },
  {
    "path": "pr_agent/tools/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "pr_agent/tools/pr_add_docs.py",
    "chars": 8660,
    "preview": "import copy\nimport textwrap\nfrom functools import partial\nfrom typing import Dict\n\nfrom jinja2 import Environment, Stric"
  },
  {
    "path": "pr_agent/tools/pr_code_suggestions.py",
    "chars": 54671,
    "preview": "import asyncio\nimport copy\nimport difflib\nimport re\nimport textwrap\nimport traceback\nfrom datetime import datetime\nfrom "
  },
  {
    "path": "pr_agent/tools/pr_config.py",
    "chars": 4497,
    "preview": "from dynaconf import Dynaconf\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent.git_providers import get_gi"
  },
  {
    "path": "pr_agent/tools/pr_description.py",
    "chars": 43745,
    "preview": "import asyncio\nimport copy\nimport re\nimport traceback\nfrom functools import partial\nfrom typing import List, Tuple\n\nimpo"
  },
  {
    "path": "pr_agent/tools/pr_generate_labels.py",
    "chars": 7166,
    "preview": "import copy\nimport re\nfrom functools import partial\nfrom typing import List, Tuple\n\nfrom jinja2 import Environment, Stri"
  },
  {
    "path": "pr_agent/tools/pr_help_docs.py",
    "chars": 32332,
    "preview": "import copy\nfrom functools import partial\n\nfrom jinja2 import Environment, StrictUndefined\nimport math\nimport os\nimport "
  },
  {
    "path": "pr_agent/tools/pr_help_message.py",
    "chars": 17358,
    "preview": "import copy\nimport re\nfrom functools import partial\nfrom pathlib import Path\n\nfrom jinja2 import Environment, StrictUnde"
  },
  {
    "path": "pr_agent/tools/pr_line_questions.py",
    "chars": 8322,
    "preview": "import argparse\nimport copy\nfrom functools import partial\n\nfrom jinja2 import Environment, StrictUndefined\n\nfrom pr_agen"
  },
  {
    "path": "pr_agent/tools/pr_questions.py",
    "chars": 7085,
    "preview": "import copy\nfrom functools import partial\n\nfrom jinja2 import Environment, StrictUndefined\n\nfrom pr_agent.algo.ai_handle"
  },
  {
    "path": "pr_agent/tools/pr_reviewer.py",
    "chars": 22247,
    "preview": "import copy\nimport datetime\nimport traceback\nfrom collections import OrderedDict\nfrom functools import partial\nfrom typi"
  },
  {
    "path": "pr_agent/tools/pr_similar_issue.py",
    "chars": 31930,
    "preview": "import time\nfrom enum import Enum\nfrom typing import List\n\nimport openai\nfrom pydantic import BaseModel, Field\n\nfrom pr_"
  },
  {
    "path": "pr_agent/tools/pr_update_changelog.py",
    "chars": 8722,
    "preview": "import copy\nfrom datetime import date\nfrom functools import partial\nfrom time import sleep\nfrom typing import Tuple\n\nfro"
  },
  {
    "path": "pr_agent/tools/ticket_pr_compliance_check.py",
    "chars": 10991,
    "preview": "import re\nimport traceback\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent.git_providers import GithubPro"
  },
  {
    "path": "pr_compliance_checklist.yaml",
    "chars": 1880,
    "preview": "pr_compliances:\n  - title: \"Consistent Naming Conventions\"\n    compliance_label: false\n    objective: \"All new variables"
  },
  {
    "path": "pyproject.toml",
    "chars": 1986,
    "preview": "[build-system]\nrequires = [\"setuptools>=61.0\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"pr-ag"
  },
  {
    "path": "requirements-dev.txt",
    "chars": 66,
    "preview": "pytest==9.0.2\npytest-asyncio>=1.3.0\npoetry\ntwine\npre-commit>=4,<5\n"
  },
  {
    "path": "requirements.txt",
    "chars": 1007,
    "preview": "aiohttp==3.12.15\nanthropic>=0.69.0\n#anthropic[vertex]==0.47.1\natlassian-python-api==3.41.4\nazure-devops==7.1.0b4\nazure-i"
  },
  {
    "path": "setup.py",
    "chars": 152,
    "preview": "# for compatibility with legacy tools\n# see: https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html\nfrom s"
  },
  {
    "path": "tests/e2e_tests/e2e_utils.py",
    "chars": 1271,
    "preview": "FILE_PATH = \"pr_agent/cli_pip.py\"\n\nPR_HEADER_START_WITH = '### **User description**\\nupdate cli_pip.py\\n\\n\\n___\\n\\n### *"
  },
  {
    "path": "tests/e2e_tests/langchain_ai_handler.py",
    "chars": 2700,
    "preview": "import asyncio\nimport os\nimport time\n\nfrom pr_agent.algo.ai_handlers.langchain_ai_handler import LangChainOpenAIHandler\n"
  },
  {
    "path": "tests/e2e_tests/test_bitbucket_app.py",
    "chars": 3821,
    "preview": "import hashlib\nimport os\nimport re\nimport time\nfrom datetime import datetime\n\nimport jwt\nimport requests\nfrom atlassian."
  },
  {
    "path": "tests/e2e_tests/test_gitea_app.py",
    "chars": 6646,
    "preview": "import os\nimport time\nfrom datetime import datetime\n\nimport requests\n\nfrom pr_agent.config_loader import get_settings\nfr"
  },
  {
    "path": "tests/e2e_tests/test_github_app.py",
    "chars": 3855,
    "preview": "import os\nimport re\nimport time\nfrom datetime import datetime\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_a"
  },
  {
    "path": "tests/e2e_tests/test_gitlab_webhook.py",
    "chars": 3905,
    "preview": "import os\nimport re\nimport time\nfrom datetime import datetime\n\nimport gitlab\n\nfrom pr_agent.config_loader import get_set"
  },
  {
    "path": "tests/health_test/main.py",
    "chars": 2926,
    "preview": "import argparse\nimport asyncio\nimport copy\nimport os\nfrom pathlib import Path\n\nfrom starlette_context import context, re"
  },
  {
    "path": "tests/unittest/test_add_docs_trigger.py",
    "chars": 3016,
    "preview": "import pytest\n\nfrom pr_agent.agent.pr_agent import PRAgent\nfrom pr_agent.config_loader import get_settings\nfrom pr_agent"
  },
  {
    "path": "tests/unittest/test_aws_secrets_manager_provider.py",
    "chars": 3821,
    "preview": "import json\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\nfrom botocore.exceptions import ClientError\n\nfrom "
  },
  {
    "path": "tests/unittest/test_azure_devops_comment.py",
    "chars": 3128,
    "preview": "import unittest\nfrom unittest.mock import MagicMock, patch\n\nfrom pr_agent.config_loader import get_settings\nfrom pr_agen"
  },
  {
    "path": "tests/unittest/test_azure_devops_parsing.py",
    "chars": 871,
    "preview": "from pr_agent.git_providers import AzureDevopsProvider\n\n\nclass TestAzureDevOpsParsing:\n    def test_regular_address(self"
  },
  {
    "path": "tests/unittest/test_bitbucket_provider.py",
    "chars": 12767,
    "preview": "from unittest.mock import MagicMock\n\nfrom atlassian.bitbucket import Bitbucket\n\nfrom pr_agent.algo.types import EDIT_TYP"
  },
  {
    "path": "tests/unittest/test_clip_tokens.py",
    "chars": 12660,
    "preview": "from unittest.mock import MagicMock, patch\n\nimport pytest\n\nfrom pr_agent.algo.token_handler import TokenEncoder\nfrom pr_"
  },
  {
    "path": "tests/unittest/test_codecommit_client.py",
    "chars": 5951,
    "preview": "from unittest.mock import MagicMock\n\nfrom pr_agent.git_providers.codecommit_client import CodeCommitClient\n\n\nclass TestC"
  },
  {
    "path": "tests/unittest/test_codecommit_provider.py",
    "chars": 9953,
    "preview": "from unittest.mock import patch\n\nimport pytest\n\nfrom pr_agent.algo.types import EDIT_TYPE, FilePatchInfo\nfrom pr_agent.g"
  },
  {
    "path": "tests/unittest/test_config_loader_secrets.py",
    "chars": 4910,
    "preview": "from unittest.mock import MagicMock, patch\n\nfrom pr_agent.config_loader import apply_secrets_manager_config, apply_secre"
  },
  {
    "path": "tests/unittest/test_convert_to_markdown.py",
    "chars": 11459,
    "preview": "# Generated by CodiumAI\nimport textwrap\nfrom unittest.mock import Mock\n\nfrom pr_agent.algo.utils import PRReviewHeader, "
  },
  {
    "path": "tests/unittest/test_delete_hunks.py",
    "chars": 3862,
    "preview": "# Generated by CodiumAI\n\nfrom pr_agent.algo.git_patch_processing import omit_deletion_hunks\n\n\"\"\"\nCode Analysis\n\nObjectiv"
  },
  {
    "path": "tests/unittest/test_extend_patch.py",
    "chars": 10288,
    "preview": "import pytest\n\nfrom pr_agent.algo.git_patch_processing import extend_patch\nfrom pr_agent.algo.pr_processing import pr_ge"
  },
  {
    "path": "tests/unittest/test_extract_issue_from_branch.py",
    "chars": 5031,
    "preview": "import pytest\n\nfrom pr_agent.tools.ticket_pr_compliance_check import extract_ticket_links_from_branch_name\n\n\nclass TestE"
  },
  {
    "path": "tests/unittest/test_fetching_sub_issues.py",
    "chars": 5041,
    "preview": "# Currently doing API calls - wrong !\n\n\n# import unittest\n# import asyncio\n# from unittest.mock import AsyncMock, patch\n"
  },
  {
    "path": "tests/unittest/test_file_filter.py",
    "chars": 5047,
    "preview": "from pr_agent.algo.file_filter import filter_ignored\nfrom pr_agent.config_loader import global_settings\n\n\nclass TestIgno"
  },
  {
    "path": "tests/unittest/test_find_line_number_of_relevant_line_in_file.py",
    "chars": 3728,
    "preview": "# Generated by CodiumAI\n\nfrom pr_agent.algo.types import FilePatchInfo\nfrom pr_agent.algo.utils import find_line_number_"
  },
  {
    "path": "tests/unittest/test_fix_json_escape_char.py",
    "chars": 806,
    "preview": "from pr_agent.algo.utils import fix_json_escape_char\n\n\nclass TestFixJsonEscapeChar:\n    def test_valid_json(self):\n     "
  },
  {
    "path": "tests/unittest/test_fix_output.py",
    "chars": 3870,
    "preview": "# Generated by CodiumAI\n\nfrom pr_agent.algo.utils import try_fix_json\n\n\nclass TestTryFixJson:\n    # Tests that JSON with"
  }
]

// ... and 16 more files (download for full content)

About this extraction

This page contains the full source code of the qodo-ai/pr-agent GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 216 files (1.5 MB), approximately 345.0k tokens, and a symbol index with 1226 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!