fix(review): language-tagged suggestion fence for Gitea syntax highlighting

Gitea 1.26.x has no GitHub-style 'Apply suggestion' button — a ```suggestion
fence is just an unknown-language code block, so chroma does not highlight it
and there is no apply control. Switch inline_comment_body to wrap the suggested
fix in a fence tagged with the file's language (new _lang_for_path helper,
.java→java, .ts→typescript, .py→python, ...), so Gitea syntax-highlights the
code. No capability lost (there was never an apply button on this Gitea
version). Correct the docstrings/skills/README that wrongly claimed an
apply-button was rendered.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Marcos
2026-08-18 01:26:37 +00:00
parent 4129f217fa
commit 76b6752f48
7 changed files with 72 additions and 23 deletions
+47 -11
View File
@@ -5,8 +5,9 @@ Runs as a Gitea Actions step OR is called by the central webhook server
(`webhook_server.py`). Fetches a PR diff, asks glm-5.2:cloud (via the on-network
headroom proxy, Anthropic /v1/messages format) to review it, and posts the
findings back as `pragent-bot` — as a **review summary** plus **inline line
comments** with apply-able ```suggestion blocks where the model could produce
them and the line anchors cleanly to the post-change file.
comments** with a fenced suggested-fix block (tagged with the file's language so
Gitea syntax-highlights it) where the model could produce one and the line
anchors cleanly to the post-change file.
Features (pilot v2):
- **Dedupe / persistence:** Gitea itself is the source of truth. Before
@@ -22,9 +23,12 @@ Features (pilot v2):
findings with `path`/`line`. We parse the diff hunks to learn which
`(path, new_line)` pairs are valid post-change anchors and post each
anchored finding as a positional review comment; the `suggestion` field, if
non-empty, is wrapped in a ```suggestion fence so Gitea renders an
apply-button. Findings that don't anchor (bad line, unchanged file, etc.)
are folded into the summary body as plain bullets.
non-empty, is wrapped in a fenced code block tagged with the file's language
(via `_lang_for_path`) so Gitea syntax-highlights it. Gitea 1.26.x has no
GitHub-style "Apply suggestion" button, so a language-tagged block is used
for highlighting instead of a ```suggestion fence. Findings that don't
anchor (bad line, unchanged file, etc.) are folded into the summary body as
plain bullets.
Fail-open by design: any error becomes a short "review failed" review comment,
and review_pr never raises. Stdlib only — no pip install.
@@ -431,19 +435,51 @@ def split_findings(findings: list[dict], anchors: dict[str, set[int]]) -> tuple[
return anchored, unanchored
def _lang_for_path(path: str) -> str:
"""Map a file extension to a chroma language tag for fenced code blocks.
Used so the suggested-fix block is syntax-highlighted in Gitea. Gitea 1.26.x
has no GitHub-style "Apply suggestion" button (the ```suggestion fence is
just an unknown-language code block → plain monospace, no apply), so we tag
the block with the file's real language for highlighting instead.
"""
ext = path.rsplit(".", 1)[-1].lower() if "." in path else ""
return {
"java": "java", "kt": "kotlin", "scala": "scala", "groovy": "groovy",
"ts": "typescript", "tsx": "tsx", "js": "javascript", "jsx": "jsx",
"mjs": "javascript", "cjs": "javascript",
"py": "python", "pyi": "python",
"go": "go", "rs": "rust", "rb": "ruby", "php": "php",
"c": "c", "h": "c", "cpp": "cpp", "cc": "cpp", "hpp": "cpp",
"cs": "csharp", "swift": "swift", "m": "objc",
"sh": "bash", "bash": "bash", "zsh": "bash",
"yml": "yaml", "yaml": "yaml", "json": "json", "jsonc": "json",
"toml": "toml", "ini": "ini", "cfg": "ini",
"html": "html", "htm": "html", "css": "css", "scss": "scss",
"xml": "xml", "svg": "xml", "sql": "sql",
"md": "markdown", "dockerfile": "dockerfile",
}.get(ext, "")
def inline_comment_body(f: dict) -> str:
"""Render one finding as a positional review-comment body.
Includes a ```suggestion fence only if the model produced non-empty
replacement code. Gitea renders that as an apply-able suggestion. Appends a
`📎 ref:` link when the finding carries a `reference` URL.
Includes a fenced suggested-fix block only if the model produced non-empty
replacement code. The fence is tagged with the file's language (via
`_lang_for_path`) so Gitea syntax-highlights it — Gitea 1.26.x has no
GitHub-style "Apply suggestion" button (```suggestion is just an
unknown-language block there → plain monospace), so a language-tagged block
is strictly more readable and loses nothing. Appends a `📎 ref:` link when
the finding carries a `reference` URL.
"""
sev = f["severity"].upper()
body = f"**[{sev}]** {f['problem']}"
if f["fix"]:
body += f"\n\nFix: {f['fix']}"
if f["suggestion"]:
body += f"\n\n```suggestion\n{f['suggestion']}\n```"
lang = _lang_for_path(f.get("path", ""))
fence = f"```{lang}" if lang else "```"
body += f"\n\n{fence}\n{f['suggestion']}\n```"
ref = f.get("reference", "")
if ref:
body += f"\n\n📎 ref: {ref}"
@@ -632,8 +668,8 @@ def post_inline_review(
honored here and silently leave the comment unpositioned (Gitea then renders
a file-level comment on EVERY diff line of the file, which is the flood we
hit). `f["line"]` is already a validated post-change (RIGHT-side) line from
`split_findings`, so it maps directly to `new_position`. The body carries the
```suggestion fence when the model produced replacement code.
`split_findings`, so it maps directly to `new_position`. The body carries a
language-tagged fenced code block when the model produced replacement code.
"""
comments = [
{