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.
Files changed (100) hide show
  1. semantic_code_review/__init__.py +5 -0
  2. semantic_code_review/augment/__init__.py +3 -0
  3. semantic_code_review/augment/agents.py +95 -0
  4. semantic_code_review/augment/console.py +440 -0
  5. semantic_code_review/augment/extra_review.py +237 -0
  6. semantic_code_review/augment/fold_summary.py +447 -0
  7. semantic_code_review/augment/hunks.py +264 -0
  8. semantic_code_review/augment/mcp_server.py +156 -0
  9. semantic_code_review/augment/overview.py +233 -0
  10. semantic_code_review/augment/pass_.py +171 -0
  11. semantic_code_review/augment/pipeline.py +571 -0
  12. semantic_code_review/augment/progress.py +223 -0
  13. semantic_code_review/augment/prompts.py +93 -0
  14. semantic_code_review/augment/schemas.py +408 -0
  15. semantic_code_review/augment/tools.py +475 -0
  16. semantic_code_review/augment/trace_adapter.py +369 -0
  17. semantic_code_review/backends/__init__.py +95 -0
  18. semantic_code_review/backends/_cli_driver.py +571 -0
  19. semantic_code_review/backends/anthropic_sdk.py +56 -0
  20. semantic_code_review/backends/base.py +104 -0
  21. semantic_code_review/backends/claude_cli.py +421 -0
  22. semantic_code_review/backends/google_sdk.py +73 -0
  23. semantic_code_review/backends/openai_compat.py +42 -0
  24. semantic_code_review/cache/__init__.py +3 -0
  25. semantic_code_review/cache/store.py +87 -0
  26. semantic_code_review/cli/__init__.py +70 -0
  27. semantic_code_review/cli/_shared.py +157 -0
  28. semantic_code_review/cli/augment.py +74 -0
  29. semantic_code_review/cli/config_cmd.py +266 -0
  30. semantic_code_review/cli/fetch.py +33 -0
  31. semantic_code_review/cli/lint.py +27 -0
  32. semantic_code_review/cli/pr.py +98 -0
  33. semantic_code_review/cli/review.py +104 -0
  34. semantic_code_review/cli/runs_cmd.py +17 -0
  35. semantic_code_review/cli/show.py +19 -0
  36. semantic_code_review/cli/strip.py +20 -0
  37. semantic_code_review/config.py +563 -0
  38. semantic_code_review/config_template.py +154 -0
  39. semantic_code_review/fetch/__init__.py +61 -0
  40. semantic_code_review/fetch/anchor.py +244 -0
  41. semantic_code_review/fetch/github.py +229 -0
  42. semantic_code_review/fetch/github_comments.py +404 -0
  43. semantic_code_review/fetch/local.py +443 -0
  44. semantic_code_review/fetch/run_source.py +70 -0
  45. semantic_code_review/format/__init__.py +3 -0
  46. semantic_code_review/format/emit.py +163 -0
  47. semantic_code_review/format/lint.py +74 -0
  48. semantic_code_review/format/parse.py +574 -0
  49. semantic_code_review/format/sidecar.py +19 -0
  50. semantic_code_review/format/strip.py +20 -0
  51. semantic_code_review/git_ops.py +252 -0
  52. semantic_code_review/paths.py +61 -0
  53. semantic_code_review/review/__init__.py +3 -0
  54. semantic_code_review/review/comments.py +176 -0
  55. semantic_code_review/review/github.py +284 -0
  56. semantic_code_review/review/github_graphql.py +433 -0
  57. semantic_code_review/review/pr_flow.py +299 -0
  58. semantic_code_review/review/runner.py +407 -0
  59. semantic_code_review/review/server.py +1043 -0
  60. semantic_code_review/structural/__init__.py +41 -0
  61. semantic_code_review/structural/diff.py +102 -0
  62. semantic_code_review/structural/parse.py +330 -0
  63. semantic_code_review/structural/symbols.py +43 -0
  64. semantic_code_review/viewer/__init__.py +3 -0
  65. semantic_code_review/viewer/assets/annotations.ts +547 -0
  66. semantic_code_review/viewer/assets/boot.ts +273 -0
  67. semantic_code_review/viewer/assets/comment_store.ts +121 -0
  68. semantic_code_review/viewer/assets/comments.ts +624 -0
  69. semantic_code_review/viewer/assets/console.ts +426 -0
  70. semantic_code_review/viewer/assets/console_render.ts +213 -0
  71. semantic_code_review/viewer/assets/console_selection.ts +102 -0
  72. semantic_code_review/viewer/assets/data_store.ts +156 -0
  73. semantic_code_review/viewer/assets/file_rows.ts +49 -0
  74. semantic_code_review/viewer/assets/folds.ts +560 -0
  75. semantic_code_review/viewer/assets/index.html +62 -0
  76. semantic_code_review/viewer/assets/post_modal.ts +383 -0
  77. semantic_code_review/viewer/assets/progress.ts +138 -0
  78. semantic_code_review/viewer/assets/render.ts +937 -0
  79. semantic_code_review/viewer/assets/sidebar.ts +429 -0
  80. semantic_code_review/viewer/assets/sse.ts +78 -0
  81. semantic_code_review/viewer/assets/text_highlight.ts +215 -0
  82. semantic_code_review/viewer/assets/types.d.ts +388 -0
  83. semantic_code_review/viewer/assets/vendor/LICENSE +29 -0
  84. semantic_code_review/viewer/assets/vendor/VENDOR.md +55 -0
  85. semantic_code_review/viewer/assets/vendor/github-dark.min.css +10 -0
  86. semantic_code_review/viewer/assets/vendor/github.min.css +10 -0
  87. semantic_code_review/viewer/assets/vendor/highlight.min.js +1244 -0
  88. semantic_code_review/viewer/assets/vendor/mermaid.LICENSE +21 -0
  89. semantic_code_review/viewer/assets/vendor/mermaid.min.js +3587 -0
  90. semantic_code_review/viewer/assets/vendor/refresh.sh +65 -0
  91. semantic_code_review/viewer/assets/viewer.css +1202 -0
  92. semantic_code_review/viewer/assets/viewer.js +10588 -0
  93. semantic_code_review/viewer/build_json.py +546 -0
  94. semantic_code_review/viewer/hunk_layout.py +471 -0
  95. semantic_code_review-0.24.2.dist-info/METADATA +348 -0
  96. semantic_code_review-0.24.2.dist-info/RECORD +100 -0
  97. semantic_code_review-0.24.2.dist-info/WHEEL +5 -0
  98. semantic_code_review-0.24.2.dist-info/entry_points.txt +2 -0
  99. semantic_code_review-0.24.2.dist-info/licenses/LICENSE +21 -0
  100. semantic_code_review-0.24.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,171 @@
1
+ """Shared recipe for a single LLM pass.
2
+
3
+ The four passes (`overview`, `hunk`, `fold-summary`, `extra-review-pr`)
4
+ all wrap the same five-step recipe around a per-pass prompt and apply
5
+ step: cache lookup → ``agent.iter()`` driver → trace write → usage
6
+ accounting → cache put. This module owns the recipe; each pass file
7
+ owns only the prompt assembly, agent construction, and the apply step
8
+ that folds the returned payload into an ``AnnotatedDiff``.
9
+
10
+ Driving via ``agent.iter()`` rather than ``agent.run()`` keeps the
11
+ partial message history accessible on the failure path — without it,
12
+ a mid-run ``UsageLimitExceeded`` leaves no trace, which is exactly the
13
+ case that most needs one.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import logging
20
+ from dataclasses import dataclass
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ from pydantic_ai import Agent
25
+
26
+ from ..cache.store import CacheStore
27
+ from .agents import Client
28
+ from .trace_adapter import (
29
+ submit_args_from_result,
30
+ write_partial_trace,
31
+ write_pydantic_ai_trace,
32
+ )
33
+
34
+ log = logging.getLogger(__name__)
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class PassMeta:
39
+ """Static identity of a pass.
40
+
41
+ ``name`` is the cache-key prefix and the ``pass`` field in cache-hit
42
+ trace markers. ``submit_tool`` and ``tool_names`` are recorded in
43
+ the trace envelope so a trace reader can see what tools were in
44
+ play. ``swallow_errors`` switches the failure policy: when true,
45
+ :func:`run_pass` logs and returns ``None`` instead of re-raising —
46
+ used by the extra-review pass, which is best-effort and must not
47
+ poison the main-pass output.
48
+ """
49
+
50
+ name: str
51
+ submit_tool: str
52
+ tool_names: tuple[str, ...] = ()
53
+ swallow_errors: bool = False
54
+
55
+
56
+ async def run_pass(
57
+ meta: PassMeta,
58
+ *,
59
+ client: Client,
60
+ agent: Agent[Any, Any],
61
+ user_content: Any,
62
+ system: str,
63
+ model: str,
64
+ cache_inputs: tuple[Any, ...],
65
+ deps: Any = None,
66
+ model_settings: Any = None,
67
+ cache: CacheStore | None = None,
68
+ trace_path: Path | None = None,
69
+ cache_request: dict[str, Any] | None = None,
70
+ ) -> dict[str, Any] | None:
71
+ """Run one LLM pass through the shared recipe.
72
+
73
+ Returns the response payload as a JSON-shaped dict (the same shape
74
+ persisted in the cache), or ``None`` when ``meta.swallow_errors`` is
75
+ set and the agent run raised. Callers convert the dict into
76
+ domain shapes themselves and fold the result into the diff.
77
+
78
+ The cache key is ``(meta.name, model, system, *cache_inputs)`` —
79
+ ``model`` is the user-facing model string so the key stays stable
80
+ across the SDK/CLI driver split for the same logical model. The
81
+ trace envelope records ``str(client.model)`` instead so the
82
+ diagnostic surface carries the precise model identifier.
83
+ """
84
+ key = None
85
+ if cache is not None:
86
+ key = cache.key(meta.name, model, system, *cache_inputs)
87
+ entry = cache.get(key)
88
+ if entry is not None:
89
+ if trace_path is not None:
90
+ _write_cache_hit_marker(trace_path, meta.name, entry)
91
+ return entry["response"]
92
+
93
+ async with agent.iter(
94
+ user_content,
95
+ deps=deps,
96
+ model_settings=model_settings,
97
+ ) as agent_run:
98
+ try:
99
+ async for _ in agent_run:
100
+ pass
101
+ except BaseException as exc:
102
+ if trace_path is not None:
103
+ write_partial_trace(
104
+ list(agent_run.all_messages()),
105
+ trace_path=trace_path,
106
+ model=str(client.model),
107
+ system=system,
108
+ tool_names=list(meta.tool_names),
109
+ submit_tool=meta.submit_tool,
110
+ error=exc,
111
+ )
112
+ if meta.swallow_errors:
113
+ log.warning(
114
+ "%s pass failed: %s: %s",
115
+ meta.name,
116
+ type(exc).__name__,
117
+ exc,
118
+ )
119
+ return None
120
+ raise
121
+ run_result = agent_run.result
122
+
123
+ assert run_result is not None # the agent run completed without an early return
124
+
125
+ if trace_path is not None:
126
+ write_pydantic_ai_trace(
127
+ run_result,
128
+ trace_path=trace_path,
129
+ model=str(client.model),
130
+ system=system,
131
+ tool_names=list(meta.tool_names),
132
+ submit_tool=meta.submit_tool,
133
+ )
134
+
135
+ payload = submit_args_from_result(run_result)
136
+
137
+ if cache is not None and key is not None:
138
+ usage = run_result.usage()
139
+ cache.put(
140
+ key,
141
+ request=cache_request or {},
142
+ response=payload,
143
+ tokens_in=usage.input_tokens or 0,
144
+ tokens_out=usage.output_tokens or 0,
145
+ )
146
+ return payload
147
+
148
+
149
+ def _write_cache_hit_marker(
150
+ path: Path,
151
+ pass_name: str,
152
+ entry: dict[str, Any],
153
+ ) -> None:
154
+ """Write the cache-hit envelope at ``path``.
155
+
156
+ Mirrors the live-run trace shape just enough that a trace reader
157
+ can identify the pass and see the cached response — the full
158
+ iteration history isn't reconstructible from a cache entry.
159
+ """
160
+ path.parent.mkdir(parents=True, exist_ok=True)
161
+ path.write_text(
162
+ json.dumps(
163
+ {"cache_hit": True, "pass": pass_name, "response": entry.get("response")},
164
+ indent=2,
165
+ ensure_ascii=False,
166
+ ),
167
+ encoding="utf-8",
168
+ )
169
+
170
+
171
+ __all__ = ["PassMeta", "run_pass"]