semantic-code-review 0.24.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- semantic_code_review/__init__.py +5 -0
- semantic_code_review/augment/__init__.py +3 -0
- semantic_code_review/augment/agents.py +95 -0
- semantic_code_review/augment/console.py +440 -0
- semantic_code_review/augment/extra_review.py +237 -0
- semantic_code_review/augment/fold_summary.py +447 -0
- semantic_code_review/augment/hunks.py +264 -0
- semantic_code_review/augment/mcp_server.py +156 -0
- semantic_code_review/augment/overview.py +233 -0
- semantic_code_review/augment/pass_.py +171 -0
- semantic_code_review/augment/pipeline.py +571 -0
- semantic_code_review/augment/progress.py +223 -0
- semantic_code_review/augment/prompts.py +93 -0
- semantic_code_review/augment/schemas.py +408 -0
- semantic_code_review/augment/tools.py +475 -0
- semantic_code_review/augment/trace_adapter.py +369 -0
- semantic_code_review/backends/__init__.py +95 -0
- semantic_code_review/backends/_cli_driver.py +571 -0
- semantic_code_review/backends/anthropic_sdk.py +56 -0
- semantic_code_review/backends/base.py +104 -0
- semantic_code_review/backends/claude_cli.py +421 -0
- semantic_code_review/backends/google_sdk.py +73 -0
- semantic_code_review/backends/openai_compat.py +42 -0
- semantic_code_review/cache/__init__.py +3 -0
- semantic_code_review/cache/store.py +87 -0
- semantic_code_review/cli/__init__.py +70 -0
- semantic_code_review/cli/_shared.py +157 -0
- semantic_code_review/cli/augment.py +74 -0
- semantic_code_review/cli/config_cmd.py +266 -0
- semantic_code_review/cli/fetch.py +33 -0
- semantic_code_review/cli/lint.py +27 -0
- semantic_code_review/cli/pr.py +98 -0
- semantic_code_review/cli/review.py +104 -0
- semantic_code_review/cli/runs_cmd.py +17 -0
- semantic_code_review/cli/show.py +19 -0
- semantic_code_review/cli/strip.py +20 -0
- semantic_code_review/config.py +563 -0
- semantic_code_review/config_template.py +154 -0
- semantic_code_review/fetch/__init__.py +61 -0
- semantic_code_review/fetch/anchor.py +244 -0
- semantic_code_review/fetch/github.py +229 -0
- semantic_code_review/fetch/github_comments.py +404 -0
- semantic_code_review/fetch/local.py +443 -0
- semantic_code_review/fetch/run_source.py +70 -0
- semantic_code_review/format/__init__.py +3 -0
- semantic_code_review/format/emit.py +163 -0
- semantic_code_review/format/lint.py +74 -0
- semantic_code_review/format/parse.py +574 -0
- semantic_code_review/format/sidecar.py +19 -0
- semantic_code_review/format/strip.py +20 -0
- semantic_code_review/git_ops.py +252 -0
- semantic_code_review/paths.py +61 -0
- semantic_code_review/review/__init__.py +3 -0
- semantic_code_review/review/comments.py +176 -0
- semantic_code_review/review/github.py +284 -0
- semantic_code_review/review/github_graphql.py +433 -0
- semantic_code_review/review/pr_flow.py +299 -0
- semantic_code_review/review/runner.py +407 -0
- semantic_code_review/review/server.py +1043 -0
- semantic_code_review/structural/__init__.py +41 -0
- semantic_code_review/structural/diff.py +102 -0
- semantic_code_review/structural/parse.py +330 -0
- semantic_code_review/structural/symbols.py +43 -0
- semantic_code_review/viewer/__init__.py +3 -0
- semantic_code_review/viewer/assets/annotations.ts +547 -0
- semantic_code_review/viewer/assets/boot.ts +273 -0
- semantic_code_review/viewer/assets/comment_store.ts +121 -0
- semantic_code_review/viewer/assets/comments.ts +624 -0
- semantic_code_review/viewer/assets/console.ts +426 -0
- semantic_code_review/viewer/assets/console_render.ts +213 -0
- semantic_code_review/viewer/assets/console_selection.ts +102 -0
- semantic_code_review/viewer/assets/data_store.ts +156 -0
- semantic_code_review/viewer/assets/file_rows.ts +49 -0
- semantic_code_review/viewer/assets/folds.ts +560 -0
- semantic_code_review/viewer/assets/index.html +62 -0
- semantic_code_review/viewer/assets/post_modal.ts +383 -0
- semantic_code_review/viewer/assets/progress.ts +138 -0
- semantic_code_review/viewer/assets/render.ts +937 -0
- semantic_code_review/viewer/assets/sidebar.ts +429 -0
- semantic_code_review/viewer/assets/sse.ts +78 -0
- semantic_code_review/viewer/assets/text_highlight.ts +215 -0
- semantic_code_review/viewer/assets/types.d.ts +388 -0
- semantic_code_review/viewer/assets/vendor/LICENSE +29 -0
- semantic_code_review/viewer/assets/vendor/VENDOR.md +55 -0
- semantic_code_review/viewer/assets/vendor/github-dark.min.css +10 -0
- semantic_code_review/viewer/assets/vendor/github.min.css +10 -0
- semantic_code_review/viewer/assets/vendor/highlight.min.js +1244 -0
- semantic_code_review/viewer/assets/vendor/mermaid.LICENSE +21 -0
- semantic_code_review/viewer/assets/vendor/mermaid.min.js +3587 -0
- semantic_code_review/viewer/assets/vendor/refresh.sh +65 -0
- semantic_code_review/viewer/assets/viewer.css +1202 -0
- semantic_code_review/viewer/assets/viewer.js +10588 -0
- semantic_code_review/viewer/build_json.py +546 -0
- semantic_code_review/viewer/hunk_layout.py +471 -0
- semantic_code_review-0.24.2.dist-info/METADATA +348 -0
- semantic_code_review-0.24.2.dist-info/RECORD +100 -0
- semantic_code_review-0.24.2.dist-info/WHEEL +5 -0
- semantic_code_review-0.24.2.dist-info/entry_points.txt +2 -0
- semantic_code_review-0.24.2.dist-info/licenses/LICENSE +21 -0
- semantic_code_review-0.24.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Per-hunk progress meter, rendered to stderr in truecolor TTYs.
|
|
2
|
+
|
|
3
|
+
Shows a single redrawn line:
|
|
4
|
+
|
|
5
|
+
overview ✓ hunks 7/24 ●●●●●●●■■···· 03s
|
|
6
|
+
|
|
7
|
+
Each in-flight hunk is one block character coloured by elapsed time —
|
|
8
|
+
cyan when fresh, yellow around 15 s, red past ~30 s. A 429 retry sleep
|
|
9
|
+
naturally pushes a hunk's elapsed time higher, so the colour reflects
|
|
10
|
+
back-pressure without us having to attribute httpx events to the
|
|
11
|
+
right concurrent hunk.
|
|
12
|
+
|
|
13
|
+
Completed hunks fade from green to dim grey over a couple of seconds
|
|
14
|
+
but stay visible so the cumulative progress impression is preserved.
|
|
15
|
+
|
|
16
|
+
Disabled when stderr isn't a TTY, when the terminal doesn't advertise
|
|
17
|
+
truecolor, or when the caller passes `enabled=False` (we use this to
|
|
18
|
+
suppress the meter under `--verbose`, where it would fight the log
|
|
19
|
+
stream).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import asyncio
|
|
25
|
+
import contextlib
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
import time
|
|
29
|
+
import types
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from typing import Self, TextIO
|
|
32
|
+
|
|
33
|
+
# ANSI helpers ---------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
ESC = "\x1b["
|
|
36
|
+
HIDE_CURSOR = ESC + "?25l"
|
|
37
|
+
SHOW_CURSOR = ESC + "?25h"
|
|
38
|
+
CLEAR_LINE = ESC + "2K"
|
|
39
|
+
RESET = ESC + "0m"
|
|
40
|
+
DIM = ESC + "2m"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _rgb(r: int, g: int, b: int) -> str:
|
|
44
|
+
return f"{ESC}38;2;{r};{g};{b}m"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _lerp(a: tuple[int, int, int], b: tuple[int, int, int], t: float) -> tuple[int, int, int]:
|
|
48
|
+
return (
|
|
49
|
+
int(a[0] + (b[0] - a[0]) * t),
|
|
50
|
+
int(a[1] + (b[1] - a[1]) * t),
|
|
51
|
+
int(a[2] + (b[2] - a[2]) * t),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Heat curve stops. Cool when fresh, hot when stale.
|
|
56
|
+
COOL = (96, 200, 240) # cyan
|
|
57
|
+
WARM = (240, 200, 80) # yellow
|
|
58
|
+
HOT = (240, 80, 60) # red
|
|
59
|
+
|
|
60
|
+
DONE_FRESH = (100, 220, 120) # green
|
|
61
|
+
DONE_FADED = (90, 90, 90) # dim grey
|
|
62
|
+
FAILED = (220, 80, 60) # red
|
|
63
|
+
|
|
64
|
+
HEAT_FULL_SECONDS = 30.0 # ~p99 hunk latency target.
|
|
65
|
+
DONE_FADE_SECONDS = 4.0 # how long a closed square stays prominent.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _heat_color(heat: float) -> tuple[int, int, int]:
|
|
69
|
+
"""Heat ∈ [0,1] → RGB along COOL → WARM → HOT."""
|
|
70
|
+
h = max(0.0, min(1.0, heat))
|
|
71
|
+
if h < 0.5:
|
|
72
|
+
return _lerp(COOL, WARM, h * 2)
|
|
73
|
+
return _lerp(WARM, HOT, (h - 0.5) * 2)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_truecolor_tty(stream: TextIO | None = None) -> bool:
|
|
77
|
+
"""True if `stream` is a TTY *and* the terminal advertises truecolor.
|
|
78
|
+
|
|
79
|
+
Uses the de-facto-standard `COLORTERM=truecolor|24bit` signal, plus
|
|
80
|
+
a fallback for terminals known to support it but inconsistent about
|
|
81
|
+
advertising (kitty, alacritty, iTerm).
|
|
82
|
+
"""
|
|
83
|
+
s = stream if stream is not None else sys.stderr
|
|
84
|
+
if not getattr(s, "isatty", lambda: False)():
|
|
85
|
+
return False
|
|
86
|
+
if os.environ.get("NO_COLOR"):
|
|
87
|
+
return False
|
|
88
|
+
colorterm = os.environ.get("COLORTERM", "").lower()
|
|
89
|
+
if colorterm in ("truecolor", "24bit"):
|
|
90
|
+
return True
|
|
91
|
+
term = os.environ.get("TERM_PROGRAM", "") or os.environ.get("TERM", "")
|
|
92
|
+
return any(t in term.lower() for t in ("kitty", "alacritty", "iterm"))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class _HunkState:
|
|
97
|
+
started: float | None = None
|
|
98
|
+
finished: float | None = None
|
|
99
|
+
ok: bool = True
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ProgressMeter:
|
|
103
|
+
"""Live per-hunk progress meter. Render-only; safe to drive from
|
|
104
|
+
one asyncio loop.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
total: int,
|
|
110
|
+
*,
|
|
111
|
+
stream: TextIO | None = None,
|
|
112
|
+
enabled: bool | None = None,
|
|
113
|
+
) -> None:
|
|
114
|
+
self.total = total
|
|
115
|
+
self.stream = stream if stream is not None else sys.stderr
|
|
116
|
+
self.enabled = is_truecolor_tty(self.stream) if enabled is None else enabled
|
|
117
|
+
self.start_time = time.monotonic()
|
|
118
|
+
self.hunks: list[_HunkState] = [_HunkState() for _ in range(total)]
|
|
119
|
+
self.overview_started: float | None = None
|
|
120
|
+
self.overview_finished: float | None = None
|
|
121
|
+
self.overview_ok: bool = True
|
|
122
|
+
self._task: asyncio.Task | None = None
|
|
123
|
+
|
|
124
|
+
# ---- lifecycle -------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
async def __aenter__(self) -> Self:
|
|
127
|
+
if self.enabled:
|
|
128
|
+
self.stream.write(HIDE_CURSOR)
|
|
129
|
+
self.stream.flush()
|
|
130
|
+
self._task = asyncio.create_task(self._tick())
|
|
131
|
+
return self
|
|
132
|
+
|
|
133
|
+
async def __aexit__(
|
|
134
|
+
self,
|
|
135
|
+
exc_type: type[BaseException] | None,
|
|
136
|
+
exc: BaseException | None,
|
|
137
|
+
tb: types.TracebackType | None,
|
|
138
|
+
) -> None:
|
|
139
|
+
if self._task is not None:
|
|
140
|
+
self._task.cancel()
|
|
141
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
142
|
+
await self._task
|
|
143
|
+
if self.enabled:
|
|
144
|
+
# One final paint with current state, then advance to the
|
|
145
|
+
# next line so any subsequent stderr writes don't overwrite.
|
|
146
|
+
self._render()
|
|
147
|
+
self.stream.write("\n" + SHOW_CURSOR)
|
|
148
|
+
self.stream.flush()
|
|
149
|
+
|
|
150
|
+
async def _tick(self) -> None:
|
|
151
|
+
try:
|
|
152
|
+
while True:
|
|
153
|
+
self._render()
|
|
154
|
+
await asyncio.sleep(0.25)
|
|
155
|
+
except asyncio.CancelledError:
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
# ---- events ----------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
def start_overview(self) -> None:
|
|
161
|
+
self.overview_started = time.monotonic()
|
|
162
|
+
|
|
163
|
+
def finish_overview(self, ok: bool = True) -> None:
|
|
164
|
+
self.overview_finished = time.monotonic()
|
|
165
|
+
self.overview_ok = ok
|
|
166
|
+
|
|
167
|
+
def start_hunk(self, idx: int) -> None:
|
|
168
|
+
if 0 <= idx < self.total:
|
|
169
|
+
self.hunks[idx].started = time.monotonic()
|
|
170
|
+
|
|
171
|
+
def finish_hunk(self, idx: int, ok: bool = True) -> None:
|
|
172
|
+
if 0 <= idx < self.total:
|
|
173
|
+
self.hunks[idx].finished = time.monotonic()
|
|
174
|
+
self.hunks[idx].ok = ok
|
|
175
|
+
|
|
176
|
+
# ---- render ----------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
def render_line(self) -> str:
|
|
179
|
+
"""Build the line as it would be drawn. Public for testing."""
|
|
180
|
+
now = time.monotonic()
|
|
181
|
+
parts: list[str] = []
|
|
182
|
+
|
|
183
|
+
# Overview indicator.
|
|
184
|
+
if self.overview_finished is not None:
|
|
185
|
+
mark = "✓" if self.overview_ok else "✗"
|
|
186
|
+
parts.append(f"overview {mark}")
|
|
187
|
+
elif self.overview_started is not None:
|
|
188
|
+
r, g, b = _heat_color((now - self.overview_started) / HEAT_FULL_SECONDS)
|
|
189
|
+
parts.append(f"overview {_rgb(r, g, b)}■{RESET}")
|
|
190
|
+
else:
|
|
191
|
+
parts.append(f"overview {DIM}·{RESET}")
|
|
192
|
+
|
|
193
|
+
# Hunks.
|
|
194
|
+
done = sum(1 for h in self.hunks if h.finished is not None)
|
|
195
|
+
squares: list[str] = []
|
|
196
|
+
for h in self.hunks:
|
|
197
|
+
if h.finished is not None:
|
|
198
|
+
if h.ok:
|
|
199
|
+
fade = max(0.0, min(1.0, (now - h.finished) / DONE_FADE_SECONDS))
|
|
200
|
+
r, g, b = _lerp(DONE_FRESH, DONE_FADED, fade)
|
|
201
|
+
squares.append(f"{_rgb(r, g, b)}■{RESET}")
|
|
202
|
+
else:
|
|
203
|
+
squares.append(f"{_rgb(*FAILED)}■{RESET}")
|
|
204
|
+
elif h.started is not None:
|
|
205
|
+
r, g, b = _heat_color((now - h.started) / HEAT_FULL_SECONDS)
|
|
206
|
+
squares.append(f"{_rgb(r, g, b)}■{RESET}")
|
|
207
|
+
else:
|
|
208
|
+
squares.append(f"{DIM}·{RESET}")
|
|
209
|
+
parts.append(f"hunks {done}/{self.total} {''.join(squares)}")
|
|
210
|
+
|
|
211
|
+
elapsed = int(now - self.start_time)
|
|
212
|
+
parts.append(f"{elapsed:02d}s")
|
|
213
|
+
return " ".join(parts)
|
|
214
|
+
|
|
215
|
+
def _render(self) -> None:
|
|
216
|
+
if not self.enabled:
|
|
217
|
+
return
|
|
218
|
+
# \r + clear-line + line. No trailing newline (we redraw in place).
|
|
219
|
+
self.stream.write(f"\r{CLEAR_LINE}{self.render_line()}")
|
|
220
|
+
self.stream.flush()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
__all__ = ["ProgressMeter", "is_truecolor_tty"]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""System prompts for the two LLM passes.
|
|
2
|
+
|
|
3
|
+
Bump `PROMPT_VERSION` when a prompt changes — the cache layer keys on
|
|
4
|
+
it so a bump forces a full re-run.
|
|
5
|
+
|
|
6
|
+
The wire format the model emits is constrained by the Pydantic models
|
|
7
|
+
in `schemas.py` (`OverviewSubmission`, `HunkAnnotations`) via
|
|
8
|
+
pydantic-ai's `output_type=ToolOutput(...)`. The prompts describe
|
|
9
|
+
*what* fields to populate; the schema enforces *how* they're shaped.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from .schemas import SMELL_TAGS_TEXT
|
|
15
|
+
|
|
16
|
+
PROMPT_VERSION = "p11"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
OVERVIEW_SYSTEM = (
|
|
20
|
+
"You are preparing a structured overview of a pull request (or a local diff) to "
|
|
21
|
+
"help a human reviewer understand its shape at a glance.\n\n"
|
|
22
|
+
"You receive the PR title and body, a diffstat, and the hunk headers of each "
|
|
23
|
+
"changed file (no bodies), each numbered by its `hunk_index` within its file. "
|
|
24
|
+
"Produce a concise overview.\n\n"
|
|
25
|
+
"Guidelines:\n"
|
|
26
|
+
"- Lead with WHY, not WHAT.\n"
|
|
27
|
+
"- Symbol kinds are: function, method, class, constant.\n"
|
|
28
|
+
"- If the user prompt has a `# Symbols changed` section, it is a deterministic "
|
|
29
|
+
" parse of what actually changed. Populate `symbols_added`/`symbols_modified`/"
|
|
30
|
+
" `symbols_removed` from it verbatim — that list is ground truth; do not invent, "
|
|
31
|
+
" drop, or rename entries. With no such section, infer symbols from the hunk "
|
|
32
|
+
" headers as before.\n"
|
|
33
|
+
"- `callgraph_edges` are introduced or modified calls (best-effort — omit if unsure).\n"
|
|
34
|
+
"- `themes` are short keyword tags (e.g. 'pagination', 'api-surface').\n"
|
|
35
|
+
"- Per-file `summary` is one sentence; `lang` only when the extension is ambiguous.\n"
|
|
36
|
+
"- `files` must include one entry per changed file in the diff.\n"
|
|
37
|
+
"- Favour clarity over completeness: the reviewer uses this to decide where to look.\n"
|
|
38
|
+
"- If the PR body contains a specification markdown block (look for a `# Spec` "
|
|
39
|
+
" heading or similar), treat it as GROUND TRUTH for what the change was meant to "
|
|
40
|
+
" accomplish. Call out in `summary` and `themes` any parts of the spec that look "
|
|
41
|
+
" under-implemented, not implemented at all, or diverged from. Do not invent spec "
|
|
42
|
+
" requirements that aren't in the body.\n\n"
|
|
43
|
+
"Groups — semantic clusters for reviewer navigation:\n"
|
|
44
|
+
"- Aim for 2–6 groups on a typical PR; larger changes can justify more. A group "
|
|
45
|
+
" should represent ONE concrete purpose (e.g. 'annotation arrow geometry', "
|
|
46
|
+
" 'node toolchain setup'), not a whole file or a whole theme.\n"
|
|
47
|
+
"- A hunk can appear in multiple groups when it genuinely serves multiple "
|
|
48
|
+
" purposes. Don't force every hunk into a group — a hunk that stands alone is "
|
|
49
|
+
" fine and should simply be omitted.\n"
|
|
50
|
+
"- Cite members by `{path, hunk_index}` using the indices shown in the "
|
|
51
|
+
" `# Hunk headers` section of the user prompt.\n"
|
|
52
|
+
"- Titles are lowercase noun phrases, ≤ 6 words, no trailing period.\n"
|
|
53
|
+
"- `rationale` is one sentence naming what the grouped hunks together accomplish. "
|
|
54
|
+
" Not the mechanics — the reviewer already sees the hunks.\n"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
HUNK_SYSTEM = (
|
|
59
|
+
"You are reviewing one hunk of a pull request. Your FIRST job is to help a human "
|
|
60
|
+
"reviewer UNDERSTAND what this change does and why. Critique (smells, risks) is "
|
|
61
|
+
"SECONDARY — only raise concerns when you can name a concrete risk.\n\n"
|
|
62
|
+
"BEFORE ANYTHING ELSE: read the hunk body. The full `- ...` / `+ ...` diff is in "
|
|
63
|
+
"the user prompt. Your `intent` must name what the hunk ACTUALLY does, grounded "
|
|
64
|
+
"in what you see — not what it plausibly does given the file path or header. "
|
|
65
|
+
"If the hunk is one line, quote the before/after tokens. If you're unsure, call "
|
|
66
|
+
"tools (`read_file`, `read_file_at`, `grep`). If you're still unsure after using "
|
|
67
|
+
"tools, lower `confidence` below 50 and state the exact missing piece in "
|
|
68
|
+
"`context`. Never write 'likely', 'probably', 'appears to', 'seems to', "
|
|
69
|
+
"'looks like' — those are signals you're guessing from the header instead of "
|
|
70
|
+
"reading the body or investigating.\n\n"
|
|
71
|
+
"You have tools to read other files in the head worktree and at the base SHA, to "
|
|
72
|
+
"grep, to list directories, and to check git history. Use them when the hunk depends "
|
|
73
|
+
"on code outside the diff; skip them if the hunk is self-contained.\n\n"
|
|
74
|
+
"Populate the following fields:\n"
|
|
75
|
+
"- `intent`: 1-2 sentences. MOTIVE, not mechanics. Name the exact change (what was "
|
|
76
|
+
"X, is now Y), not 'probably'. Bad: 'one-line tweak to the compose file (likely an "
|
|
77
|
+
"image bump)'. Good: 'bumps the postgres image tag from 15.3 to 15.5'.\n"
|
|
78
|
+
"- `segments`: when the hunk contains semantically distinct edits (e.g. a refactor "
|
|
79
|
+
"plus an unrelated fix, or a changed if-branch alongside a new else-branch), split "
|
|
80
|
+
"them. Each segment has POST-IMAGE `new_start`/`new_count` and its own intent. Omit "
|
|
81
|
+
"segments if the hunk is single-intent.\n"
|
|
82
|
+
"- `smells`: list of {tag, note}. Tags are from the closed vocabulary: "
|
|
83
|
+
f"{SMELL_TAGS_TEXT}. Attach each smell to a segment when it's segment-local, or to the "
|
|
84
|
+
"hunk when it spans the whole change.\n"
|
|
85
|
+
"- `context`: cross-file dependencies the reviewer can't see from the diff.\n"
|
|
86
|
+
"- `refs`: {path, line, reason} for other files the reviewer should look at.\n"
|
|
87
|
+
"- `confidence`: 0-100 integer. Low is fine and honest.\n"
|
|
88
|
+
"- `line_notes`: {line, body} for notes too specific for intent. `line` is post-image.\n\n"
|
|
89
|
+
"Fold-region summaries (indent-based collapsed blocks inside the diff) are NOT "
|
|
90
|
+
"produced here — the review server fires a focused one-shot call for each region "
|
|
91
|
+
"the reviewer actually collapses. Leave `fold_descriptions` empty.\n\n"
|
|
92
|
+
"Tone: explanatory, not evaluative. Comprehension first."
|
|
93
|
+
)
|