Skip to content

feat(providers): add tokenless diff provider#2467

Open
vectorkovacspeter wants to merge 19 commits into
The-PR-Agent:mainfrom
vectorkovacspeter:feature/tokenless-diff-provider
Open

feat(providers): add tokenless diff provider#2467
vectorkovacspeter wants to merge 19 commits into
The-PR-Agent:mainfrom
vectorkovacspeter:feature/tokenless-diff-provider

Conversation

@vectorkovacspeter

Copy link
Copy Markdown

feature: tokenless local diff mode (--stdin / --diff-file)

Closes #2457

Summary

Adds a purely local, stateless way to run PR-Agent against a unified diff supplied on
stdin or from a file — no git-platform API token and no PR URL required. The
generated review/improvements/description are printed to stdout (markdown), and can
additionally be written to a file with --output.

# pipe a diff from stdin
git diff main...feature-branch | pr-agent --stdin review

# or pass a diff file and also save the output
git diff main...feature-branch > changes.diff
pr-agent --diff-file changes.diff --output review.md review

Supported commands: review, improve, describe, ask.

Why

Today PR-Agent requires a platform-specific token (GitHub PAT/App, GitLab token, …)
because it talks to the hosting platform's API to fetch PR data and publish comments.
In security-first / restricted environments, teams often manage repositories over SSH
and deliberately avoid issuing HTTP access tokens, since every static token is an extra
credential that can leak.

This feature lets those users get AI code reviews while keeping a strict, token-free
posture:

  • No token attack surface — nothing platform-related is read or required.
  • Air-gapped friendly — works where only the LLM endpoint is reachable and the code
    host's API is blocked.
  • Local workflow integration — easy to wire into pre-commit / pre-push hooks.

How to use

Flag Meaning
--stdin Read a unified diff from standard input
--diff-file <path> Read a unified diff from a file
--output <path> Also write the result to this file (in addition to stdout)

The provider is selected automatically when --stdin or --diff-file is present, and
the usual --pr_url / --issue_url requirement is bypassed.

git diff main...feature | pr-agent --stdin review
git diff main...feature | pr-agent --stdin improve
git diff main...feature | pr-agent --stdin describe
git diff main...feature | pr-agent --stdin ask "What is the riskiest change here?"
pr-agent --diff-file changes.diff --output review.md review

Any LLM supported by PR-Agent works, including a fully local / OpenAI-compatible
endpoint (e.g. Ollama or vLLM) for an end-to-end air-gapped setup.

How it works

  • A new git provider, git_provider = "diff" (DiffGitProvider), parses the unified
    diff with the unidiff library into PR-Agent's FilePatchInfo objects.
  • When run inside the repository working tree, it reads each changed file and
    reverse-applies the diff to reconstruct the original (base) file, so each hunk
    gets full surrounding context (extend_patch) — matching the quality of the normal
    API path rather than only the diff's few context lines.
  • When the working tree isn't available, or has drifted from the diff, it cleanly falls
    back to a patch-only review instead of producing a wrong base file.
  • Output goes to stdout, and to --output <path> when provided.

Design notes / scope

  • This is a new, dedicated provider, intentionally separate from the existing
    branch-based local provider (which requires a clean repo and a target branch). The
    local provider is left untouched.
  • Operations that have no meaning without a hosting platform (inline comments,
    committable code suggestions, labels, reactions) are no-ops / NotImplementedError,
    mirroring LocalGitProvider; is_supported() reports them as unsupported so the
    tools degrade gracefully.
  • Working-tree reads are constrained to the repository root — absolute paths and
    ..-escapes from the diff are rejected — so an untrusted diff cannot make the tool
    read files outside the repo.
  • One new runtime dependency: unidiff (MIT), added to requirements.txt.

Testing

New tests under tests/unittest/:

  • test_diff_parsing.py — parsing (modify/add/delete/rename/binary) and base-file
    reverse-apply (success, drift → patch-only, multi-hunk, CRLF, EOF-newline).
  • test_diff_provider.py — provider behavior, stdout/file output, temporary-comment
    suppression, malformed-diff guard, capability gating, and a path-traversal guard
    (with a sentinel test verified to fail if the guard is removed).
  • test_cli_diff_mode.py — CLI flag parsing.
  • test_diff_mode_e2e.py — base reconstruction from the working tree and a mocked-LLM
    run through PRReviewer.

All diff-feature tests pass. Unrelated pre-existing test-collection errors in some
test_mosaico_* files (optional dependencies not installed) are not affected by this
change.

Documentation

Adds docs/docs/usage-guide/tokenless_diff_mode.md and a navigation entry in
docs/mkdocs.yml, including usage, the working-tree vs. patch-only quality trade-off,
and how this differs from the local provider.

AI assistance disclosure

In the interest of transparency: this contribution was developed with substantial help
from an AI coding assistant — it contains AI-generated and/or AI-assisted code,
tests, and documentation. All of it was reviewed, tested, and validated by me before
submission, and I take full responsibility for the contents of this PR. I'm happy to
adjust anything to fit the project's conventions and review feedback.

…Agent#2457)

- Add publish_file_comments to is_supported() deny-list so describe
  walkthrough is not silently dropped with inline_file_summary=true
- Guard get_diff_files() against path traversal: reject absolute paths
  and relative paths that resolve outside the repo root; log and fall
  back to patch-only mode instead of raising
- Fix tokenless_diff_mode.md comparison table: local provider inline
  comments are Not supported (raises NotImplementedError)
- Promote page title from H2 to H1 to match other usage-guide pages
- Add tests: test_publish_file_comments_not_supported,
  test_path_traversal_file_not_read
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add tokenless unified-diff provider (stdin/file) with stdout output
✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

Description

• Add --stdin / --diff-file diff mode to run PR-Agent without PR URL or platform token.
• Implement DiffGitProvider to parse unified diffs and optionally reconstruct base files from
 working tree.
• Add dependency, tests, and docs for safe tokenless diff workflows (stdout/--output).
Diagram

graph TD
  A["CLI flags (--stdin/--diff-file/--output)"] --> B["Dynaconf settings (diff.content)"] --> C["DiffGitProvider"]
  C --> D["Diff parsing (unidiff)"]
  C --> E[("Working tree files")]
  C --> F["PR tools (review/describe/improve/ask)"] --> G["stdout + optional output file"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Shell out to `git apply --reverse` / `patch` for base reconstruction
  • ➕ Leverages battle-tested patch application semantics (offsets/fuzz/edge cases).
  • ➕ May better handle complex diffs than a custom reverse-apply implementation.
  • ➖ Adds reliance on external binaries/OS differences; harder to keep fully portable.
  • ➖ More security surface area if invoking shell commands on untrusted diffs.
  • ➖ Harder to guarantee consistent behavior across environments/containers.
2. Extend existing `LocalGitProvider` to accept diff input
  • ➕ Avoids introducing a new provider type and reduces duplicated capability gating.
  • ➕ May reuse existing local provider output conventions.
  • ➖ Local provider semantics differ (branch comparison + clean tree requirement); mixing modes risks confusing UX and codepaths.
  • ➖ Still needs separate stdout/file publishing behavior and safety checks.
3. Patch-only diff mode (no working-tree enrichment)
  • ➕ Much simpler implementation; no file reads, no path traversal concerns.
  • ➕ Deterministic output purely from provided artifact.
  • ➖ Lower-quality LLM prompts due to limited context lines in hunks.
  • ➖ Cannot match API/provider quality when full file context is important.

Recommendation: Current approach (dedicated diff provider + optional working-tree enrichment + safe fallback to patch-only) is the best trade-off for tokenless, portable usage. Keep the pure-Python reconstruction for portability, but consider documenting known reconstruction limitations vs. git apply semantics as follow-up if users report diff edge cases.

Files changed (11) +770 / -3

Enhancement (4) +274 / -3
cli.pyAdd CLI diff-mode flags and route execution without PR/issue URL +24/-2

Add CLI diff-mode flags and route execution without PR/issue URL

• Introduces '--stdin', '--diff-file', and '--output' flags; reads unified diff content from stdin or a file and stores it in settings while switching 'config.git_provider' to 'diff'. Updates invocation so a sentinel target ('local_diff') is used when no '--pr_url' is provided.

pr_agent/cli.py

__init__.pyRegister the new 'diff' git provider +3/-1

Register the new 'diff' git provider

• Imports 'DiffGitProvider' and adds it to the '_GIT_PROVIDERS' registry so it can be selected via configuration.

pr_agent/git_providers/init.py

diff_parsing.pyParse unified diffs and reverse-apply patches to reconstruct base files +95/-0

Parse unified diffs and reverse-apply patches to reconstruct base files

• Adds 'parse_unified_diff()' using 'unidiff.PatchSet' to convert diff text into 'FilePatchInfo' objects, classifying added/deleted/renamed/modified files and skipping binaries. Implements 'reconstruct_base_file()' to reverse-apply a single-file patch against head content with drift detection and newline/CRLF handling.

pr_agent/git_providers/diff_parsing.py

diff_provider.pyImplement 'DiffGitProvider' with safe working-tree enrichment and stdout/file output +152/-0

Implement 'DiffGitProvider' with safe working-tree enrichment and stdout/file output

• Adds a tokenless GitProvider implementation that consumes diff content from settings, parses it into diff files, and optionally reads working-tree files to reconstruct base versions. Includes safety guards against absolute paths and '..' traversal outside the repo root, suppresses temporary comments, gates unsupported platform capabilities, and writes results to stdout and optional '--output' file.

pr_agent/git_providers/diff_provider.py

Tests (4) +419 / -0
test_cli_diff_mode.pyAdd CLI parsing tests for diff-mode flags +15/-0

Add CLI parsing tests for diff-mode flags

• Verifies that '--diff-file'/'--output' and '--stdin' are accepted by the CLI parser and populate expected arguments.

tests/unittest/test_cli_diff_mode.py

test_diff_mode_e2e.pyAdd end-to-end tests covering provider output and mocked LLM integration +122/-0

Add end-to-end tests covering provider output and mocked LLM integration

• Covers DiffGitProvider behavior with and without a working tree file and verifies stdout publishing. Adds an async integration test that runs 'PRReviewer' with a fake AI handler to assert prompts include diff-derived content, proving wiring from diff provider to LLM boundary.

tests/unittest/test_diff_mode_e2e.py

test_diff_parsing.pyAdd unit tests for diff parsing and base reconstruction edge cases +155/-0

Add unit tests for diff parsing and base reconstruction edge cases

• Tests modify/add/delete/rename parsing plus base reconstruction success/failure (drift), multi-hunk behavior, and newline handling expectations.

tests/unittest/test_diff_parsing.py

test_diff_provider.pyAdd DiffGitProvider unit tests for registration, safety, and capability gating +127/-0

Add DiffGitProvider unit tests for registration, safety, and capability gating

• Validates provider registration, diff parsing, stdout/file publishing, and empty/malformed diff error handling. Includes a sentinel path-traversal test to ensure the provider will not read files outside the repo root and checks 'publish_file_comments' is reported unsupported.

tests/unittest/test_diff_provider.py

Documentation (2) +76 / -0
tokenless_diff_mode.mdDocument tokenless unified-diff mode usage and behavior +75/-0

Document tokenless unified-diff mode usage and behavior

• Adds a new usage guide page describing '--stdin' / '--diff-file' / '--output', supported commands, and the working-tree enrichment vs patch-only fallback trade-off. Clarifies differences from the existing 'local' provider, including unsupported inline comments.

docs/docs/usage-guide/tokenless_diff_mode.md

mkdocs.ymlAdd navigation entry for tokenless diff mode docs +1/-0

Add navigation entry for tokenless diff mode docs

• Registers the new documentation page in the MkDocs navigation under the usage guide.

docs/mkdocs.yml

Other (1) +1 / -0
requirements.txtAdd 'unidiff' runtime dependency +1/-0

Add 'unidiff' runtime dependency

• Adds 'unidiff==0.7.5' to support parsing unified diffs for the new diff provider.

requirements.txt

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (4) 📎 Requirement gaps (0) 📜 Skill insights (0)

Context used

Grey Divider


Action required

1. Overlong diff_parsing import line 📘 Rule violation ⚙ Maintainability ⭐ New
Description
The new import line in plain_diff_provider.py exceeds the repo’s 120-character limit and is likely
to fail Ruff (E501) / isort formatting expectations. This can break CI/lint checks and reduce
maintainability.
Code

pr_agent/git_providers/plain_diff_provider.py[R8-10]

+from pr_agent.config_loader import _find_repository_root, get_settings
+from pr_agent.git_providers.diff_parsing import parse_unified_diff, reconstruct_base_file, to_hunk_only_patch
+from pr_agent.git_providers.git_provider import GitProvider
Evidence
Ruff is configured for 120-character lines and isort checks; the added long import line violates
that formatting requirement in newly-added code.

AGENTS.md: Python Code Must Conform to Ruff Configuration (Including isort Ordering) and Prefer Double Quotes
pr_agent/git_providers/plain_diff_provider.py[8-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pr_agent/git_providers/plain_diff_provider.py` includes a long single-line import that exceeds the configured Ruff line-length (120) and may fail linting.

## Issue Context
Ruff is configured with `line-length = 120` and `lint.select` includes `E` and `I001` (isort checks). New code should comply to avoid CI failures.

## Fix Focus Areas
- pr_agent/git_providers/plain_diff_provider.py[8-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Overlong asserts in e2e test 📘 Rule violation ⚙ Maintainability
Description
Several new assertions use single lines that exceed the project's 120-character limit, which can
cause Ruff/CI formatting failures. This reduces consistency and increases review churn for future
edits.
Code

tests/unittest/test_plain_diff_mode_e2e.py[R80-84]

+    assert files[0].head_file != "", "head_file should be populated from the working-tree file"
+    assert "line2-changed" in files[0].head_file
+
+    # base_file must be reconstructed (non-empty)
+    assert files[0].base_file != "", "base_file should be reconstructed by reversing the patch"
Evidence
PR Compliance ID 9 requires 120-character lines; the new test file includes long one-line `assert
... , "..."` statements (and another long assert message later) that violate this requirement.

AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes)
tests/unittest/test_plain_diff_mode_e2e.py[80-84]
tests/unittest/test_plain_diff_mode_e2e.py[162-162]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New test lines exceed the 120-character line-length limit required by the project's Ruff formatting standards.
## Issue Context
This can fail formatting/lint checks (e.g., E501) and makes diffs harder to review.
## Fix Focus Areas
- tests/unittest/test_plain_diff_mode_e2e.py[80-84]
- tests/unittest/test_plain_diff_mode_e2e.py[162-162]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Overlong diff string literal 📘 Rule violation ⚙ Maintainability
Description
A new test embeds a long diff string in a single line that exceeds the 120-character limit, which
can trigger Ruff/CI failures. It should be wrapped or split into concatenated strings for compliance
with project formatting standards.
Code

tests/unittest/test_plain_diff_provider.py[R166-169]

+    # A hunk with no file header triggers UnidiffParseError inside parse_unified_diff,
+    # which the provider must re-raise as ValueError with a clear message.
+    cfg("plain_diff.content", "@@ -1,3 +1,3 @@\n line1\n-line2\n+line2-changed\n line3\n")
+    cfg("plain_diff.output_path", None)
Evidence
PR Compliance ID 9 requires a 120-character maximum line length; the test uses a long single-line
cfg("plain_diff.content", "@@ ...") string containing embedded newlines which exceeds this limit.

AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes): AGENTS.md: Follow Ruff/Project Formatting Standards (120-char lines, isort import grouping, double quotes)
tests/unittest/test_plain_diff_provider.py[165-171]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A long inline diff string exceeds the project's 120-character line-length requirement.
## Issue Context
This can fail Ruff/CI checks and reduces readability.
## Fix Focus Areas
- tests/unittest/test_plain_diff_provider.py[165-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (4)
4. Improve crashes on comments 🐞 Bug ☼ Reliability
Description
PlainDiffGitProvider.get_issue_comments() raises NotImplementedError, but the /improve flow calls
git_provider.get_issue_comments() unconditionally when building persistent suggestion history, so
--stdin/--diff-file improve can crash before emitting output.
Code

pr_agent/git_providers/plain_diff_provider.py[R202-204]

+    def get_issue_comments(self):
+        raise NotImplementedError("Issue comments are not supported by the plain-diff provider")
+
Evidence
The plain-diff provider explicitly marks issue-comments unsupported and raises when called, while
the improve tool calls get_issue_comments() to build persistent history without checking
is_supported(), creating a deterministic runtime failure path in plain-diff mode.

pr_agent/git_providers/plain_diff_provider.py[123-128]
pr_agent/git_providers/plain_diff_provider.py[202-204]
pr_agent/tools/pr_code_suggestions.py[272-276]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PlainDiffGitProvider.get_issue_comments()` raises `NotImplementedError`, but `PRCodeSuggestions.publish_persistent_comment_with_history()` calls `list(git_provider.get_issue_comments())` without a capability guard. In plain-diff mode this can abort the supported `improve` command.
### Issue Context
Plain-diff mode relies on stdout/`--output` as its only sink; a crash here means the user gets no suggestions at all.
### Fix Focus Areas
- pr_agent/git_providers/plain_diff_provider.py[202-204]
- pr_agent/tools/pr_code_suggestions.py[272-276]
### Expected fix
- Make `PlainDiffGitProvider.get_issue_comments()` return an empty iterable (e.g., `return []`) instead of raising, so persistent-history code can safely treat “no comments” as the default.
- (Optional hardening) In `publish_persistent_comment_with_history`, catch `NotImplementedError`/`Exception` around `get_issue_comments()` and treat it as `[]` to avoid similar failures for future providers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Ask bypasses diff provider ✓ Resolved 🐞 Bug ≡ Correctness
Description
PRQuestions constructs the provider via get_git_provider()(pr_url), which lacks the plain-diff
override, so --stdin/--diff-file ask can instantiate a hosted provider after apply_repo_settings()
overwrites config.git_provider and break tokenless mode.
Code

pr_agent/git_providers/init.py[R60-64]

+            # Plain-diff mode is keyed on loaded diff content; it must not be
+            # overridden by an extra/repo config file that sets a different
+            # git_provider (apply_repo_settings merges those before this call).
+            if get_settings().get("plain_diff.content", None):
+                provider_id = "plain-diff"
Evidence
The request handler applies repo settings before tool construction, get_git_provider() reads only
config.git_provider (no plain-diff forcing), while the plain-diff override exists only in
get_git_provider_with_context(). Since PRQuestions uses the former, it can miss the override and
select the wrong provider in diff mode.

pr_agent/tools/pr_questions.py[18-24]
pr_agent/agent/pr_agent.py[55-58]
pr_agent/git_providers/init.py[30-37]
pr_agent/git_providers/init.py[58-65]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ask` is documented as supported in plain-diff mode, but `PRQuestions` uses `get_git_provider()(pr_url)` instead of `get_git_provider_with_context(pr_url)`. Only `get_git_provider_with_context()` contains the new “force plain-diff when `plain_diff.content` is set” logic.
Because `PRAgent._handle_request()` calls `apply_repo_settings(pr_url)` before tool instantiation, repo/extra config can overwrite `config.git_provider` away from `plain-diff`, and `ask` then routes to the wrong provider.
### Issue Context
This is specific to the new plain-diff routing behavior added in this PR.
### Fix Focus Areas
- pr_agent/tools/pr_questions.py[18-24]
- pr_agent/agent/pr_agent.py[55-58]
- pr_agent/git_providers/__init__.py[30-67]
### Expected fix
- Change `PRQuestions` to construct the provider via `get_git_provider_with_context(pr_url)` (matching reviewer/describe/improve).
- (Optional) Apply the same change to `pr_line_questions.py` if you want parity for `ask_line`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Diff output can be suppressed 🐞 Bug ☼ Reliability
Description
In plain-diff mode, cli.run() forces config.publish_output=True, but PRAgent._handle_request() calls
apply_repo_settings() which can overwrite that setting back to False from repo/extra config. Since
tools gate all publishing on config.publish_output, the run can silently produce no stdout/--output
content in repos that disable publishing.
Code

pr_agent/cli.py[R109-114]

+        get_settings().set("config.git_provider", "plain-diff")
+        get_settings().set("plain_diff.content", diff_content)
+        get_settings().set("plain_diff.output_path", getattr(args, "output", None))
+        # Plain-diff mode's whole purpose is to emit the result to stdout/--output, so
+        # force publishing on even if a config/env set publish_output=false.
+        get_settings().set("config.publish_output", True)
Evidence
The CLI sets publish_output to True, but apply_repo_settings runs afterward and merges repo settings
by overwriting section keys, which can flip it back. PRReviewer (and other tools) will then skip
publish_comment entirely when publish_output is False, resulting in no stdout/file output despite
being in plain-diff mode.

pr_agent/cli.py[89-114]
pr_agent/agent/pr_agent.py[55-58]
pr_agent/git_providers/utils.py[225-258]
pr_agent/git_providers/utils.py[306-315]
pr_agent/tools/pr_reviewer.py[152-164]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Plain-diff mode depends on `config.publish_output=True` to emit output, but `apply_repo_settings()` can overwrite it after the CLI sets it.
## Issue Context
`cli.run()` sets `config.publish_output=True` in diff mode, but later `PRAgent._handle_request()` calls `apply_repo_settings(pr_url)` which merges repo settings by overwriting section keys. Tools like `PRReviewer` check `config.publish_output` before calling provider publish methods, so output can be suppressed.
## Fix Focus Areas
- pr_agent/git_providers/utils.py[225-339]
- pr_agent/cli.py[89-114]
## Suggested fix
After `apply_repo_settings()` finishes applying extra/repo settings, if `plain_diff.content` is set, explicitly set `config.publish_output=True` again (and consider also forcing any other publish gates needed for stdout mode, if applicable).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. diff_parsing import not isort ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
diff_provider.py wraps the diff_parsing import in a parenthesized/continuation style that is not
what Ruff’s isort check will format for a short two-name import. This can cause Ruff/CI lint
failures on the new provider file.
Code

pr_agent/git_providers/diff_provider.py[R9-10]

+from pr_agent.git_providers.diff_parsing import (parse_unified_diff,
+                                                 reconstruct_base_file)
Evidence
Ruff is configured to enforce isort import formatting (I001). The new diff_provider.py uses a
wrapped/parenthesized import for just two names, which isort would normally rewrite (thus triggering
I001).

AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatting and Import/String Conventions: AGENTS.md: Python Code Must Conform to Ruff Formatti...

Comment thread pr_agent/cli.py
Comment thread pr_agent/git_providers/diff_parsing.py
Comment thread pr_agent/git_providers/plain_diff_provider.py
Comment thread tests/unittest/test_diff_mode_e2e.py
Comment thread pr_agent/git_providers/diff_parsing.py
- cli: handle OSError/UnicodeDecodeError when reading --diff-file, failing
  fast with a clear parser.error instead of an uncaught traceback
- diff_parsing: catch the specific UnidiffParseError (not bare Exception) in
  reconstruct_base_file; keep an empty reconstructed base as "" (never "\n")
  so downstream extend_patch() treats it as having no original file
- diff_provider: narrow exception handling to UnidiffParseError for parsing
  and (OSError, UnicodeDecodeError) for working-tree reads; resolve diff
  paths against the repository root (_find_repository_root) instead of the
  raw CWD so enrichment still works when run from a subdirectory
- tests: update add-to-empty base expectation to ""; add regression tests
  for subdirectory enrichment and missing --diff-file fail-fast; remove
  unused imports (F401)
Comment thread pr_agent/git_providers/plain_diff_provider.py
Comment thread pr_agent/git_providers/diff_provider.py Outdated
Comment thread pr_agent/git_providers/diff_provider.py Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 304384d

@naorpeled

Copy link
Copy Markdown
Member

Hey @vectorkovacspeter, thanks for taking this!
Can you please address Qodo's comments?

@vectorkovacspeter

Copy link
Copy Markdown
Author

Thanks for the review. I've addressed all six findings in 304384d:

  • Fail-fast handling for unreadable/invalid --diff-file (OSError/UnicodeDecodeError)
  • Narrowed broad except Exception to UnidiffParseError / (OSError, UnicodeDecodeError) in parsing and working-tree reads
  • Empty reconstructed base now stays "" (never "\n") so extend_patch() treats it correctly
  • Diff paths resolved against the repo root, so enrichment works when run from a subdirectory
  • Removed the unused AsyncMock/MagicMock imports

Added regression tests for the subdirectory case and the missing-file fail-fast; suite is now 25 passing.

@naorpeled

Copy link
Copy Markdown
Member

Hey @vectorkovacspeter,
Thanks!

Could you also please address the first 3 that Qodo hasn't marked as resolved?

… mode

Addresses the new findings raised after the previous fix round:

- improve crash (correctness): DiffGitProvider now implements
  publish_code_suggestions()/publish_code_suggestion() to render suggestions
  as a markdown document to stdout/--output instead of raising
  NotImplementedError, so `--stdin/--diff-file improve` works as documented
- CWD file-disclosure risk (security): when no .git repository root is
  detected, working-tree enrichment is disabled (patch-only) instead of
  reading files from an arbitrary current directory
- broad except (reliability): _write_output() now catches only
  (OSError, UnicodeError) and re-raises, since --output is an explicit
  request whose failure must not be swallowed
- style: double quotes for the new CLI args; wrapped the long
  publish_inline_comment signature to satisfy Ruff
- docs: note that improve renders suggestions as markdown in this mode
- tests: add coverage for code-suggestion rendering (and empty no-op),
  no-repo-root patch-only mode; harden the path-traversal sentinel with a
  real .git root so it isolates the traversal guard
@vectorkovacspeter

Copy link
Copy Markdown
Author

Done — pushed dfb19b2.

Addressed the new findings:

  • improve now renders code suggestions as markdown to stdout/--output (it previously crashed)
  • enrichment is disabled to patch-only when no .git root is found (avoids reading arbitrary CWD files)
  • narrowed the _write_output exception handling
  • fixed the quote/line-length style nits.

Added tests for the suggestion rendering and the no-repo-root path; suite is 28 passing.

Comment thread tests/unittest/test_diff_mode_e2e.py Outdated
Comment thread pr_agent/git_providers/plain_diff_provider.py
Comment thread pr_agent/cli.py
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit dfb19b2

@vectorkovacspeter

Copy link
Copy Markdown
Author

Oh man, this bot is a hard one , but i working on it 😄

…ff mode

- incremental crash (correctness): DiffGitProvider.get_incremental_commits()
  now disables incremental mode (-i has no meaning for a standalone diff),
  preventing a TypeError when PRReviewer takes len() of an empty commits_range
- extra-config override (correctness): provider selection now forces the diff
  provider whenever diff content is loaded, so an extra/repo config setting
  config.git_provider can no longer silently derail tokenless mode
- suppressed output (correctness): CLI diff mode forces config.publish_output
  =True so stdout/--output is always produced even if publishing was disabled
- global mutation (reliability): removed the dead
  pr_reviewer.inline_code_comments=False write in __init__ (never read
  anywhere; was copied from LocalGitProvider)
- test isolation (reliability): added an autouse fixture to each diff test
  module that snapshots and restores the mutated global settings keys
- tests: added coverage for incremental-disabled, diff-content-forces-provider,
  and publish_output forcing
@vectorkovacspeter

Copy link
Copy Markdown
Author

Pushed a05bce3 addressing the new findings:

  • disabled incremental mode in diff mode (was a -i crash)
  • made the diff provider sticky against extra-config git_provider overrides
  • forced publish_output=True so stdout/--output is never suppressed
  • removed the dead inline_code_comments global write
  • added test-isolation fixtures. Suite is 31 passing.

Comment thread pr_agent/git_providers/diff_provider.py Outdated
Comment thread tests/unittest/test_diff_mode_e2e.py Outdated
Comment thread pr_agent/git_providers/diff_parsing.py
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit a05bce3

…und 4)

- patch misparsed as hunks (correctness): the stored FilePatchInfo.patch was
  str(pf) from unidiff, which keeps the 'diff --git'/'index'/'---'/'+++'
  headers before the first '@@'. The shared hunk/line-number converter treats
  '+'/'-' lines as content, so those headers were emitted as a bogus leading
  hunk with invalid line numbers into the LLM prompt. Added
  to_hunk_only_patch() and normalize f.patch after base reconstruction (which
  still needs the full headers to parse), matching platform providers.
- isort (I001): collapsed the two-name diff_parsing import to one line and
  sorted imports in the diff test modules; verified with ruff.
- test isolation: tests now mutate settings only through an autouse `cfg`
  fixture that snapshots and restores the diff-mode keys (covers run()'s
  indirect mutations too); no bare get_settings().set() in test bodies.
- test style: replaced `assert False` with pytest.raises (B011).
- tests: added a hunk-only-patch assertion.
@vectorkovacspeter

Copy link
Copy Markdown
Author

Pushed ae473c4. Addressed the findings:

  • Diff metadata misparsed as hunks — good catch; the patch now strips the file headers to hunk-only form (matching the platform providers) after base reconstruction, so no phantom hunk reaches the prompt. Added a regression test.
  • isort (1, 2) — fixed and verified with ruff check.
  • Test state isolation — tests now go through an autouse cfg fixture that snapshots and restores the settings keys; no bare get_settings().set() left in the bodies.

32 tests pass.

Note: I scoped changes to this feature and left pre-existing repo lint (e.g. the duplicate GiteaProvider import, some long lines) untouched to keep the diff focused — happy to fold those in separately if you'd like.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit ae473c4

@vectorkovacspeter

Copy link
Copy Markdown
Author

@naorpeled all of Qodo's findings are now resolved — on the latest push it finally came back empty-handed. 🎉

Genuinely impressed with that review bot, by the way: it caught a couple of real ones I'd have shipped (the improve crash and the diff-metadata-misparsed-as-hunks bug were proper catches). It's also… thorough. Four rounds deep I'm fairly sure it would flag the Mona Lisa for an unsorted import. 😄 No complaints though — the code is better for it.

Ready for your review whenever you have a chance, and happy to make any further changes you'd like.

@naorpeled naorpeled changed the title Feature/tokenless diff provider feat(providers): add tokenless diff provider Jun 26, 2026
@naorpeled

Copy link
Copy Markdown
Member

Hey @vectorkovacspeter,
Wdyt about renaming this to stdin provider instead of tokenless?
I feel tokenless is a bit more confusing

@vectorkovacspeter

Copy link
Copy Markdown
Author

Hey @vectorkovacspeter,
Wdyt about renaming this to stdin provider instead of tokenless?
I feel tokenless is a bit more confusing

Yeah not the best name. stdin may not cover input diff file based method.

Maybe just call it diff provider or offline provider or something like this...
But calling it stdin provider is also not a deal breaker for me it it's fits better for the project.

@naorpeled

Copy link
Copy Markdown
Member

Hey @vectorkovacspeter,
Wdyt about renaming this to stdin provider instead of tokenless?
I feel tokenless is a bit more confusing

Yeah not the best name. stdin may not cover input diff file based method.

Maybe just call it diff provider or offline provider or something like this... But calling it stdin provider is also not a deal breaker for me it it's fits better for the project.

What about plain-diff?

@vectorkovacspeter

Copy link
Copy Markdown
Author

Hey @vectorkovacspeter,
Wdyt about renaming this to stdin provider instead of tokenless?
I feel tokenless is a bit more confusing

Yeah not the best name. stdin may not cover input diff file based method.

Maybe just call it diff provider or offline provider or something like this... But calling it stdin provider is also not a deal breaker for me it it's fits better for the project.

What about plain-diff?

Approved 😁 thanks for asking.

@naorpeled

Copy link
Copy Markdown
Member

Hey @vectorkovacspeter,
Can you please update the naming based on our discussion?

Thanks in advance!
Would love to release this in the next release.

…he-PR-Agent#2457)

Rename the provider per maintainer request (@naorpeled): registry key
"diff" -> "plain-diff", DiffGitProvider -> PlainDiffGitProvider, settings
namespace diff.* -> plain_diff.*, docs/tests/CLI help updated. Also drop a
pre-existing duplicate GiteaProvider import and fix import ordering.
@github-actions github-actions Bot added the feature 💡 label Jul 6, 2026
@vectorkovacspeter

Copy link
Copy Markdown
Author

Done, @naorpeled — renamed to plain-diff as we discussed. 🎉

  • git_provider key diffplain-diff
  • DiffGitProviderPlainDiffGitProvider (file diff_provider.pyplain_diff_provider.py)
  • settings namespace diff.*plain_diff.*
  • docs page (plain_diff_mode.md) + mkdocs nav + CLI help text updated
  • tests renamed to test_plain_diff_*

Kept the --stdin/--diff-file/--output flags as-is, since they name the input method rather than the provider. Also fixed a stray duplicate import + import ordering in git_providers/__init__.py while I was in there. 32 tests green. Ready for the release whenever you are 🚀

Comment thread pr_agent/cli.py
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit c362734

…he-PR-Agent#2457)

apply_repo_settings() runs after cli.run() forces config.publish_output=True
and can overwrite it back to False from an extra config (--extra_config_url),
because it merges section keys with overwrite. Since plain-diff mode's only
output channel is stdout/--output and all tools gate publishing on this flag,
the review could silently produce no output.

PlainDiffGitProvider is constructed after apply_repo_settings, so re-assert
config.publish_output=True in its __init__. Adds a regression test.

(Note: the repo-local .pr_agent.toml path cannot reach this in plain-diff mode
since get_repo_settings() returns None; the extra-config path can.)
@vectorkovacspeter

Copy link
Copy Markdown
Author

Addressed the new Qodo finding (#1 "Diff output can be suppressed") in d0f41973.

Nice catch, but worth splitting the two override vectors in apply_repo_settings():

  • Repo-local .pr_agent.toml — cannot reach it in plain-diff mode: PlainDiffGitProvider.get_repo_settings() returns None, so the repo-merge block is skipped entirely.
  • --extra_config_url — this one can overwrite config.publish_output back to false (it merges section keys with overwrite, and runs after cli.run() set it True). Since stdout/--output is plain-diff mode’s only output channel and every tool gates publishing on that flag, the run could silently produce nothing.

Fix: PlainDiffGitProvider is constructed after apply_repo_settings(), so I re-assert config.publish_output=True in its __init__ — the last hook before any tool publishes, so output can’t be silently dropped regardless of what an extra config sets. Added a regression test (test_init_forces_publish_output). 33 tests green ✅

Comment on lines +80 to +84
assert files[0].head_file != "", "head_file should be populated from the working-tree file"
assert "line2-changed" in files[0].head_file

# base_file must be reconstructed (non-empty)
assert files[0].base_file != "", "base_file should be reconstructed by reversing the patch"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Overlong asserts in e2e test 📘 Rule violation ⚙ Maintainability

Several new assertions use single lines that exceed the project's 120-character limit, which can
cause Ruff/CI formatting failures. This reduces consistency and increases review churn for future
edits.
Agent Prompt
## Issue description
New test lines exceed the 120-character line-length limit required by the project's Ruff formatting standards.

## Issue Context
This can fail formatting/lint checks (e.g., E501) and makes diffs harder to review.

## Fix Focus Areas
- tests/unittest/test_plain_diff_mode_e2e.py[80-84]
- tests/unittest/test_plain_diff_mode_e2e.py[162-162]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +166 to +169
# A hunk with no file header triggers UnidiffParseError inside parse_unified_diff,
# which the provider must re-raise as ValueError with a clear message.
cfg("plain_diff.content", "@@ -1,3 +1,3 @@\n line1\n-line2\n+line2-changed\n line3\n")
cfg("plain_diff.output_path", None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Overlong diff string literal 📘 Rule violation ⚙ Maintainability

A new test embeds a long diff string in a single line that exceeds the 120-character limit, which
can trigger Ruff/CI failures. It should be wrapped or split into concatenated strings for compliance
with project formatting standards.
Agent Prompt
## Issue description
A long inline diff string exceeds the project's 120-character line-length requirement.

## Issue Context
This can fail Ruff/CI checks and reduces readability.

## Fix Focus Areas
- tests/unittest/test_plain_diff_provider.py[165-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +202 to +204
def get_issue_comments(self):
raise NotImplementedError("Issue comments are not supported by the plain-diff provider")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Improve crashes on comments 🐞 Bug ☼ Reliability

PlainDiffGitProvider.get_issue_comments() raises NotImplementedError, but the /improve flow calls
git_provider.get_issue_comments() unconditionally when building persistent suggestion history, so
--stdin/--diff-file improve can crash before emitting output.
Agent Prompt
### Issue description
`PlainDiffGitProvider.get_issue_comments()` raises `NotImplementedError`, but `PRCodeSuggestions.publish_persistent_comment_with_history()` calls `list(git_provider.get_issue_comments())` without a capability guard. In plain-diff mode this can abort the supported `improve` command.

### Issue Context
Plain-diff mode relies on stdout/`--output` as its only sink; a crash here means the user gets no suggestions at all.

### Fix Focus Areas
- pr_agent/git_providers/plain_diff_provider.py[202-204]
- pr_agent/tools/pr_code_suggestions.py[272-276]

### Expected fix
- Make `PlainDiffGitProvider.get_issue_comments()` return an empty iterable (e.g., `return []`) instead of raising, so persistent-history code can safely treat “no comments” as the default.
- (Optional hardening) In `publish_persistent_comment_with_history`, catch `NotImplementedError`/`Exception` around `get_issue_comments()` and treat it as `[]` to avoid similar failures for future providers.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread pr_agent/git_providers/__init__.py
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit d0f4197

…omments (The-PR-Agent#2457)

Two Qodo findings on the renamed provider:

- ask routing (correctness): PRQuestions builds its provider via
  get_git_provider(), which lacked the plain-diff override that
  get_git_provider_with_context() already has. If an extra config overwrote
  config.git_provider after apply_repo_settings(), 'ask' could route to a
  hosted provider. Add the same 'force plain-diff when plain_diff.content is
  set' keying to get_git_provider().
- issue comments (reliability): get_issue_comments() raised
  NotImplementedError, which the improve persistent-comment path caught and
  logged as a spurious traceback on every run. Return [] instead (a raw diff
  has no comment history); suggestions still reach stdout.

Adds regression tests. 35 diff-mode tests pass.
@vectorkovacspeter

Copy link
Copy Markdown
Author

Went through the four new findings — two fixed in b736fbd7, two I believe are false positives:

#3 Improve crashes on comments (fixed). Slight correction: it doesn't actually crash — publish_persistent_comment_with_history() wraps the get_issue_comments() call in try/except Exception, so the NotImplementedError was caught and the flow fell through to publish_comment() (→ stdout). But it logged a misleading Failed to update persistent review traceback on every improve run. Fixed properly by returning [] from get_issue_comments() (a raw diff has no comment history) — no more spurious traceback, output unchanged.

#4 Ask bypasses diff provider (fixed). Good catch. PRQuestions builds its provider via get_git_provider(), which lacked the plain-diff override that get_git_provider_with_context() has, so an extra config overwriting config.git_provider after apply_repo_settings() could route ask to a hosted provider. Added the same keying to get_git_provider() — fixes ask and any other tool using the base getter.

#1 Overlong asserts / ❌ #2 Overlong diff literal — false positives. These lines are within the 120-char limit (line-length = 120 in pyproject.toml):

  • test_plain_diff_mode_e2e.py L80/L84 = 95 chars, L162 = 108 chars
  • test_plain_diff_provider.py L168 = 90 chars

ruff check --select E501 passes clean on both files. I suspect the \n escapes inside the inline diff string got counted as visual line breaks. Left these as-is — happy to wrap them anyway if you'd prefer.

35 diff-mode tests green ✅

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit b736fbd

never suppressed by a config/env that disabled publishing."""
import io

import pr_agent.cli as cli
@naorpeled

Copy link
Copy Markdown
Member

Hey @vectorkovacspeter,
Thanks!

Can you please also address "Improve crashes on comments"?
I think that's an actual issue

CodeQL flagged test_cli_plain_diff_mode.py importing pr_agent.cli both as
`from pr_agent.cli import run, set_parser` and `import pr_agent.cli as cli`.
The module import only existed to monkeypatch PRAgent; switch to a string
target (`monkeypatch.setattr("pr_agent.cli.PRAgent", ...)`) and drop the
duplicate import. 35 diff-mode tests pass.
@vectorkovacspeter

Copy link
Copy Markdown
Author

Hey @naorpeled — good news, that one's already fixed! It went in with b736fbd7 (the current PR HEAD), same push where I wrote up the four findings, so it's easy to miss.

Quick recap of what I meant earlier: the finding's title ("crashes") isn't literally accurate — publish_persistent_comment_with_history() wraps the get_issue_comments() call in try/except Exception, so the NotImplementedError was caught and improve still fell through to stdout. But you're right that it was a real problem: it logged a misleading Failed to update persistent review traceback on every improve run. So I didn't wave it off — I fixed it properly by returning [] (a raw diff has no comment history):

def get_issue_comments(self):
    # A raw diff has no issue-comment history. Return an empty iterable rather
    # than raising: the improve persistent-comment path calls this without a
    # capability guard, and treating "no comments" as the default lets it fall
    # through to publishing the suggestions to stdout without a spurious
    # traceback in the log.
    return []

No more spurious traceback, output unchanged, plus a regression test (test_get_issue_comments_returns_empty).

Also just pushed 6ce6de51 clearing the new CodeQL alert on test_cli_plain_diff_mode.py (pr_agent.cli was imported both ways — dropped the duplicate module import in favor of a string monkeypatch target).

35 diff-mode tests green ✅ Ready for the release whenever you are 🚀

Comment on lines +8 to +10
from pr_agent.config_loader import _find_repository_root, get_settings
from pr_agent.git_providers.diff_parsing import parse_unified_diff, reconstruct_base_file, to_hunk_only_patch
from pr_agent.git_providers.git_provider import GitProvider

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Overlong diff_parsing import line 📘 Rule violation ⚙ Maintainability

The new import line in plain_diff_provider.py exceeds the repo’s 120-character limit and is likely
to fail Ruff (E501) / isort formatting expectations. This can break CI/lint checks and reduce
maintainability.
Agent Prompt
## Issue description
`pr_agent/git_providers/plain_diff_provider.py` includes a long single-line import that exceeds the configured Ruff line-length (120) and may fail linting.

## Issue Context
Ruff is configured with `line-length = 120` and `lint.select` includes `E` and `I001` (isort checks). New code should comply to avoid CI failures.

## Fix Focus Areas
- pr_agent/git_providers/plain_diff_provider.py[8-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 6ce6de5

@vectorkovacspeter

Copy link
Copy Markdown
Author

Checked this one — it's a false positive (the line-length limit here is 120, not 79/100):

  • The diff_parsing import is 109 characters, comfortably under 120.
  • ruff check --select E501,I001 passes clean on plain_diff_provider.py.

Worth noting the suggested fix would actually break the build: isort/Ruff only wraps an import when it exceeds line-length, so a 109-char line gets collapsed back to a single line — forcing a parenthesized multi-line form would introduce a real I001 failure. So I've left it as-is.

(Same category as the two earlier "overlong line" flags on the e2e/provider tests — those were 95/108/90 chars, also within 120.) Happy to wrap anything if you'd prefer a tighter house limit, @naorpeled, but under the current config these are compliant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI-only / Tokenless Local Mode via Git Diff (stdin/file)

3 participants