code-mower 0.5.0b5__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.
- code_mower/__init__.py +3 -0
- code_mower/adapters/__init__.py +39 -0
- code_mower/adapters/_base.py +148 -0
- code_mower/adapters/cursor_bugbot.py +97 -0
- code_mower/adapters/gitar.py +111 -0
- code_mower/adapters/greptile.py +191 -0
- code_mower/adapters/qodo.py +140 -0
- code_mower/antigravity_cli_audit_pr.py +241 -0
- code_mower/audit_handoff_log.py +345 -0
- code_mower/audit_labeler_lib.py +311 -0
- code_mower/audit_progress.py +203 -0
- code_mower/blind_review_artifacts.py +562 -0
- code_mower/blind_review_coordinator.py +276 -0
- code_mower/bootstrap.py +524 -0
- code_mower/builder_experiment.py +539 -0
- code_mower/calibration/__init__.py +181 -0
- code_mower/calibration/arms.py +267 -0
- code_mower/calibration/auto_discovery.py +348 -0
- code_mower/calibration/commands.py +192 -0
- code_mower/calibration/context_inputs.py +199 -0
- code_mower/calibration/corpus.py +91 -0
- code_mower/calibration/evidence.py +20 -0
- code_mower/calibration/evidence_report.py +360 -0
- code_mower/calibration/identity.py +27 -0
- code_mower/calibration/metrics.py +18 -0
- code_mower/calibration/overlap.py +103 -0
- code_mower/calibration/planning.py +309 -0
- code_mower/calibration/policy.py +207 -0
- code_mower/calibration/results.py +315 -0
- code_mower/calibration/run_results.py +147 -0
- code_mower/calibration/run_status.py +64 -0
- code_mower/calibration/runner.py +360 -0
- code_mower/calibration/truth.py +188 -0
- code_mower/calibration/value_report.py +142 -0
- code_mower/checks.py +402 -0
- code_mower/claude_audit_pr.py +1126 -0
- code_mower/claude_cli_bounce.py +303 -0
- code_mower/claude_cli_environment.py +73 -0
- code_mower/clear_stale.py +374 -0
- code_mower/cli.py +537 -0
- code_mower/cloud.py +674 -0
- code_mower/cloud_client/__init__.py +173 -0
- code_mower/cloud_client/bundle.py +155 -0
- code_mower/cloud_client/doctor.py +206 -0
- code_mower/cloud_client/dogfood.py +78 -0
- code_mower/cloud_client/endpoints.py +114 -0
- code_mower/cloud_client/errors.py +7 -0
- code_mower/cloud_client/events.py +278 -0
- code_mower/cloud_client/export.py +272 -0
- code_mower/cloud_client/git_metadata.py +46 -0
- code_mower/cloud_client/manifest.py +39 -0
- code_mower/cloud_client/operations.py +448 -0
- code_mower/cloud_client/reports.py +45 -0
- code_mower/cloud_client/setup.py +205 -0
- code_mower/cloud_client/upload.py +97 -0
- code_mower/code_mower_calibration.py +598 -0
- code_mower/code_mower_context_packs.py +591 -0
- code_mower/code_mower_merge.py +227 -0
- code_mower/code_mower_telemetry.py +561 -0
- code_mower/coderabbit_cli_audit_pr.py +526 -0
- code_mower/codex_audit_env_preflight.py +220 -0
- code_mower/codex_audit_pr.py +1738 -0
- code_mower/codex_audit_schema_smoke.py +160 -0
- code_mower/codex_audit_verdict.schema.json +44 -0
- code_mower/config.py +655 -0
- code_mower/doctor.py +161 -0
- code_mower/doctor_checks/__init__.py +104 -0
- code_mower/doctor_checks/cloud.py +129 -0
- code_mower/doctor_checks/common.py +215 -0
- code_mower/doctor_checks/github.py +128 -0
- code_mower/doctor_checks/github_actions.py +11 -0
- code_mower/doctor_checks/github_actions_cost.py +99 -0
- code_mower/doctor_checks/github_actions_cost_summary.py +111 -0
- code_mower/doctor_checks/github_actions_failure_annotations.py +27 -0
- code_mower/doctor_checks/github_actions_failure_models.py +47 -0
- code_mower/doctor_checks/github_actions_failure_scan.py +200 -0
- code_mower/doctor_checks/github_actions_failure_selection.py +63 -0
- code_mower/doctor_checks/github_actions_failures.py +103 -0
- code_mower/doctor_checks/github_actions_permissions.py +55 -0
- code_mower/doctor_checks/github_api.py +79 -0
- code_mower/doctor_checks/github_branch.py +56 -0
- code_mower/doctor_checks/github_config.py +25 -0
- code_mower/doctor_checks/github_provider.py +61 -0
- code_mower/doctor_checks/github_repo.py +120 -0
- code_mower/doctor_checks/groups.py +36 -0
- code_mower/doctor_checks/models.py +96 -0
- code_mower/doctor_checks/output.py +86 -0
- code_mower/doctor_checks/presets.py +64 -0
- code_mower/doctor_checks/privacy.py +20 -0
- code_mower/doctor_checks/provider_api_model.py +138 -0
- code_mower/doctor_checks/provider_api_model_openai.py +29 -0
- code_mower/doctor_checks/provider_api_model_profiles.py +137 -0
- code_mower/doctor_checks/provider_env.py +113 -0
- code_mower/doctor_checks/provider_env_required.py +56 -0
- code_mower/doctor_checks/provider_env_tokens.py +100 -0
- code_mower/doctor_checks/provider_local_cli.py +162 -0
- code_mower/doctor_checks/provider_local_cli_commands.py +47 -0
- code_mower/doctor_checks/provider_local_cli_probe_config.py +70 -0
- code_mower/doctor_checks/provider_probe.py +20 -0
- code_mower/doctor_checks/provider_probe_auth.py +52 -0
- code_mower/doctor_checks/provider_probe_evaluation.py +109 -0
- code_mower/doctor_checks/provider_probe_json.py +45 -0
- code_mower/doctor_checks/provider_probe_remediation.py +39 -0
- code_mower/doctor_checks/providers.py +159 -0
- code_mower/doctor_checks/registry.py +69 -0
- code_mower/doctor_checks/runner.py +188 -0
- code_mower/doctor_checks/runtime.py +89 -0
- code_mower/doctor_checks/runtime_github_auth.py +148 -0
- code_mower/gemini_cli_audit_pr.py +897 -0
- code_mower/hermes_cli_audit_pr.py +436 -0
- code_mower/init.py +888 -0
- code_mower/lane_configs/__init__.py +37 -0
- code_mower/lane_configs/aider.py +32 -0
- code_mower/lane_configs/antigravity_cli.py +35 -0
- code_mower/lane_configs/claude.py +35 -0
- code_mower/lane_configs/codex.py +32 -0
- code_mower/lane_configs/devin.py +33 -0
- code_mower/lane_configs/gemini_cli.py +35 -0
- code_mower/lane_configs/hermes_cli.py +35 -0
- code_mower/lane_configs/local_llm.py +31 -0
- code_mower/local_llm_audit_pr.py +1364 -0
- code_mower/local_llm_bakeoff.py +458 -0
- code_mower/local_llm_calibration.py +441 -0
- code_mower/local_llm_profiles.py +66 -0
- code_mower/migration.py +508 -0
- code_mower/migration_install.py +292 -0
- code_mower/migration_mirror.py +392 -0
- code_mower/migration_readiness.py +237 -0
- code_mower/migration_rehearsal.py +718 -0
- code_mower/next_steps.py +441 -0
- code_mower/package.py +673 -0
- code_mower/package_content.py +444 -0
- code_mower/package_manifest.py +452 -0
- code_mower/package_paths.py +53 -0
- code_mower/package_rendering.py +90 -0
- code_mower/package_static.py +585 -0
- code_mower/prompts.py +267 -0
- code_mower/provider_registry.py +469 -0
- code_mower/provider_runners/__init__.py +60 -0
- code_mower/provider_runners/comments.py +31 -0
- code_mower/provider_runners/git.py +46 -0
- code_mower/provider_runners/github_auth.py +61 -0
- code_mower/provider_runners/github_pr.py +120 -0
- code_mower/provider_runners/process.py +58 -0
- code_mower/provider_runners/repo_paths.py +23 -0
- code_mower/provider_runners/text_schema.py +41 -0
- code_mower/provider_runners/verdict_artifacts.py +103 -0
- code_mower/provider_runners/workspace.py +57 -0
- code_mower/release_readiness.py +549 -0
- code_mower/reviewer_metrics.py +389 -0
- code_mower/saas_reviewer_labeler.py +809 -0
- code_mower/secrets.py +89 -0
- code_mower/templates/builder-experiment.example.json +55 -0
- code_mower/templates/calibration-corpus.example.json +129 -0
- code_mower/templates/calibration-corpus.json +129 -0
- code_mower/templates/code-mower.example.yml +423 -0
- code_mower/templates/context-packs.example.json +150 -0
- code_mower/templates/lane_prompts/base-audit.md +22 -0
- code_mower/templates/lane_prompts/calibration-policy.md +21 -0
- code_mower/templates/lane_prompts/context-driven-quality.md +21 -0
- code_mower/templates/lane_prompts/docs-design.md +12 -0
- code_mower/templates/lane_prompts/generic-programming.md +21 -0
- code_mower/templates/lane_prompts/operability.md +22 -0
- code_mower/templates/lane_prompts/package-runtime.md +12 -0
- code_mower/templates/lane_prompts/security-threat-model.md +22 -0
- code_mower/templates/product-support/code_mower +216 -0
- code_mower/templates/product-support/code_mower_standalone_pin.env +7 -0
- code_mower/templates/product-support/code_mower_standalone_shadow.sh +151 -0
- code_mower/templates/product-support/run_claude_audit_pr.sh +32 -0
- code_mower/templates/product-support/run_codex_audit_pr.sh +32 -0
- code_mower/templates/product-support/safe_gh_comment.py +96 -0
- code_mower/templates/providers.yml +454 -0
- code_mower/templates/reviewer-spend.example.json +28 -0
- code_mower/templates/reviewer-value-report.example.md +20 -0
- code_mower/templates/workflows/private-standalone-shadow.yml.j2 +106 -0
- code_mower/templates/workflows/review-clear-stale.yml.j2 +83 -0
- code_mower/trailer_comment_labeler.py +207 -0
- code_mower/versioning.py +32 -0
- code_mower-0.5.0b5.dist-info/METADATA +302 -0
- code_mower-0.5.0b5.dist-info/RECORD +185 -0
- code_mower-0.5.0b5.dist-info/WHEEL +5 -0
- code_mower-0.5.0b5.dist-info/entry_points.txt +2 -0
- code_mower-0.5.0b5.dist-info/licenses/LICENSE +202 -0
- code_mower-0.5.0b5.dist-info/licenses/NOTICE +10 -0
- code_mower-0.5.0b5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""Reference Code Mower provider registry.
|
|
2
|
+
|
|
3
|
+
This module is intentionally declarative. It documents the provider/lane shape
|
|
4
|
+
that a future `code-mower.yml` parser should produce without making the shared
|
|
5
|
+
labelers know provider-specific semantics.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from types import MappingProxyType
|
|
12
|
+
from typing import Any, Mapping
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from . import local_llm_profiles
|
|
16
|
+
except ImportError: # pragma: no cover - direct repo execution fallback.
|
|
17
|
+
from tools import local_llm_profiles
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class LaneLabels:
|
|
22
|
+
needs: str
|
|
23
|
+
done: str
|
|
24
|
+
blocked: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class ProviderLane:
|
|
29
|
+
lane_id: str
|
|
30
|
+
lane_type: str
|
|
31
|
+
driver: str
|
|
32
|
+
provider: str
|
|
33
|
+
labels: LaneLabels
|
|
34
|
+
adapter: str | None = None
|
|
35
|
+
events: tuple[str, ...] = ()
|
|
36
|
+
token_env: tuple[str, ...] = ()
|
|
37
|
+
token_env_any: tuple[tuple[str, ...], ...] = ()
|
|
38
|
+
result_sources: tuple[str, ...] = ()
|
|
39
|
+
informational: bool = False
|
|
40
|
+
merge_authority: bool = False
|
|
41
|
+
enabled_by_default: bool = True
|
|
42
|
+
trigger_policy: str = "label"
|
|
43
|
+
spend_policy: str = "none"
|
|
44
|
+
provider_config: Mapping[str, Any] = field(default_factory=dict)
|
|
45
|
+
|
|
46
|
+
def __post_init__(self) -> None:
|
|
47
|
+
object.__setattr__(self, "provider_config", _freeze_mapping(self.provider_config))
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def is_paid_optional(self) -> bool:
|
|
51
|
+
return (
|
|
52
|
+
self.spend_policy == "paid"
|
|
53
|
+
and not self.enabled_by_default
|
|
54
|
+
and self.trigger_policy == "manual"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _freeze_value(value: Any) -> Any:
|
|
59
|
+
if isinstance(value, Mapping):
|
|
60
|
+
return _freeze_mapping(value)
|
|
61
|
+
if isinstance(value, (list, tuple)):
|
|
62
|
+
return tuple(_freeze_value(item) for item in value)
|
|
63
|
+
if isinstance(value, set):
|
|
64
|
+
return frozenset(_freeze_value(item) for item in value)
|
|
65
|
+
return value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _freeze_mapping(value: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
69
|
+
return MappingProxyType({key: _freeze_value(item) for key, item in value.items()})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
REFERENCE_PROVIDERS: dict[str, ProviderLane] = {
|
|
73
|
+
"codex": ProviderLane(
|
|
74
|
+
lane_id="codex",
|
|
75
|
+
lane_type="audit",
|
|
76
|
+
driver="local_cli",
|
|
77
|
+
provider="codex",
|
|
78
|
+
labels=LaneLabels(
|
|
79
|
+
needs="needs-codex-audit",
|
|
80
|
+
done="codex-audit-done",
|
|
81
|
+
blocked="codex-audit-blocked",
|
|
82
|
+
),
|
|
83
|
+
token_env=("CODEX_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
84
|
+
result_sources=("trailer_comment",),
|
|
85
|
+
merge_authority=True,
|
|
86
|
+
provider_config={
|
|
87
|
+
"command": "codex",
|
|
88
|
+
"doctor_probe_args": ("--version",),
|
|
89
|
+
},
|
|
90
|
+
),
|
|
91
|
+
"claude_review": ProviderLane(
|
|
92
|
+
lane_id="claude_review",
|
|
93
|
+
lane_type="review",
|
|
94
|
+
driver="manual",
|
|
95
|
+
provider="claude",
|
|
96
|
+
labels=LaneLabels(
|
|
97
|
+
needs="needs-claude-review",
|
|
98
|
+
done="claude-review-done",
|
|
99
|
+
blocked="claude-review-blocked",
|
|
100
|
+
),
|
|
101
|
+
result_sources=("post_review",),
|
|
102
|
+
informational=True,
|
|
103
|
+
merge_authority=False,
|
|
104
|
+
trigger_policy="manual",
|
|
105
|
+
),
|
|
106
|
+
"claude_audit": ProviderLane(
|
|
107
|
+
lane_id="claude_audit",
|
|
108
|
+
lane_type="audit",
|
|
109
|
+
driver="local_cli",
|
|
110
|
+
provider="claude",
|
|
111
|
+
labels=LaneLabels(
|
|
112
|
+
needs="needs-claude-audit",
|
|
113
|
+
done="claude-audit-done",
|
|
114
|
+
blocked="claude-audit-blocked",
|
|
115
|
+
),
|
|
116
|
+
token_env=("CLAUDE_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
117
|
+
result_sources=("trailer_comment",),
|
|
118
|
+
merge_authority=True,
|
|
119
|
+
provider_config={
|
|
120
|
+
"command": "claude",
|
|
121
|
+
"doctor_probe_args": (
|
|
122
|
+
"--print",
|
|
123
|
+
"--output-format",
|
|
124
|
+
"json",
|
|
125
|
+
"--no-session-persistence",
|
|
126
|
+
"--setting-sources",
|
|
127
|
+
"local",
|
|
128
|
+
"--strict-mcp-config",
|
|
129
|
+
"--mcp-config",
|
|
130
|
+
'{"mcpServers":{}}',
|
|
131
|
+
"--disable-slash-commands",
|
|
132
|
+
"--tools",
|
|
133
|
+
"",
|
|
134
|
+
"--model",
|
|
135
|
+
"sonnet",
|
|
136
|
+
"--max-budget-usd",
|
|
137
|
+
"0.25",
|
|
138
|
+
"Reply with exactly: ok",
|
|
139
|
+
),
|
|
140
|
+
"doctor_probe_timeout_seconds": 30,
|
|
141
|
+
"doctor_probe_expect_json": True,
|
|
142
|
+
"doctor_probe_expect_json_field": "result",
|
|
143
|
+
"doctor_probe_expect_json_value": "ok",
|
|
144
|
+
"doctor_probe_error_fields": ("is_error", "api_error_status"),
|
|
145
|
+
"doctor_probe_auth_status_fields": ("api_error_status",),
|
|
146
|
+
},
|
|
147
|
+
),
|
|
148
|
+
"devin": ProviderLane(
|
|
149
|
+
lane_id="devin",
|
|
150
|
+
lane_type="audit",
|
|
151
|
+
driver="hosted_bridge",
|
|
152
|
+
provider="devin",
|
|
153
|
+
labels=LaneLabels(
|
|
154
|
+
needs="needs-devin-audit",
|
|
155
|
+
done="devin-audit-done",
|
|
156
|
+
blocked="devin-audit-blocked",
|
|
157
|
+
),
|
|
158
|
+
token_env=("DEVIN_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
159
|
+
result_sources=("trailer_comment",),
|
|
160
|
+
merge_authority=True,
|
|
161
|
+
enabled_by_default=False,
|
|
162
|
+
trigger_policy="manual",
|
|
163
|
+
spend_policy="paid",
|
|
164
|
+
),
|
|
165
|
+
"greptile": ProviderLane(
|
|
166
|
+
lane_id="greptile",
|
|
167
|
+
lane_type="audit",
|
|
168
|
+
driver="saas_event",
|
|
169
|
+
provider="greptile",
|
|
170
|
+
adapter="greptile",
|
|
171
|
+
events=("pull_request_review", "check_run"),
|
|
172
|
+
labels=LaneLabels(
|
|
173
|
+
needs="needs-greptile-audit",
|
|
174
|
+
done="greptile-audit-done",
|
|
175
|
+
blocked="greptile-audit-blocked",
|
|
176
|
+
),
|
|
177
|
+
token_env=("GREPTILE_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
178
|
+
result_sources=("pull_request_review", "check_run"),
|
|
179
|
+
informational=True,
|
|
180
|
+
provider_config={
|
|
181
|
+
"bot_authors": ("greptile-apps[bot]", "greptile-apps"),
|
|
182
|
+
"check_runs": ({"app_slug": "greptile-apps", "name": "Greptile Review"},),
|
|
183
|
+
},
|
|
184
|
+
),
|
|
185
|
+
"gitar": ProviderLane(
|
|
186
|
+
lane_id="gitar",
|
|
187
|
+
lane_type="audit",
|
|
188
|
+
driver="saas_event",
|
|
189
|
+
provider="gitar",
|
|
190
|
+
adapter="gitar",
|
|
191
|
+
events=("issue_comment",),
|
|
192
|
+
labels=LaneLabels(
|
|
193
|
+
needs="needs-gitar-audit",
|
|
194
|
+
done="gitar-audit-done",
|
|
195
|
+
blocked="gitar-audit-blocked",
|
|
196
|
+
),
|
|
197
|
+
token_env=("GITAR_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
198
|
+
result_sources=("issue_comment",),
|
|
199
|
+
informational=True,
|
|
200
|
+
provider_config={
|
|
201
|
+
"bot_authors": ("gitar-ai[bot]", "gitar-bot", "gitar-bot[bot]"),
|
|
202
|
+
},
|
|
203
|
+
),
|
|
204
|
+
"qodo": ProviderLane(
|
|
205
|
+
lane_id="qodo",
|
|
206
|
+
lane_type="audit",
|
|
207
|
+
driver="saas_event",
|
|
208
|
+
provider="qodo",
|
|
209
|
+
adapter="qodo",
|
|
210
|
+
events=("issue_comment",),
|
|
211
|
+
labels=LaneLabels(
|
|
212
|
+
needs="needs-qodo-audit",
|
|
213
|
+
done="qodo-audit-done",
|
|
214
|
+
blocked="qodo-audit-blocked",
|
|
215
|
+
),
|
|
216
|
+
token_env=("QODO_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
217
|
+
result_sources=("issue_comment",),
|
|
218
|
+
informational=True,
|
|
219
|
+
enabled_by_default=False,
|
|
220
|
+
trigger_policy="manual",
|
|
221
|
+
spend_policy="paid",
|
|
222
|
+
provider_config={
|
|
223
|
+
"bot_authors": ("qodo-code-review[bot]", "qodo-code-review"),
|
|
224
|
+
"spend_policy": "never trigger automatically from the reference workflows",
|
|
225
|
+
},
|
|
226
|
+
),
|
|
227
|
+
"cursor_bugbot": ProviderLane(
|
|
228
|
+
lane_id="cursor_bugbot",
|
|
229
|
+
lane_type="audit",
|
|
230
|
+
driver="saas_event",
|
|
231
|
+
provider="cursor_bugbot",
|
|
232
|
+
adapter="cursor_bugbot",
|
|
233
|
+
events=("issue_comment",),
|
|
234
|
+
labels=LaneLabels(
|
|
235
|
+
needs="needs-cursor-bugbot-audit",
|
|
236
|
+
done="cursor-bugbot-audit-done",
|
|
237
|
+
blocked="cursor-bugbot-audit-blocked",
|
|
238
|
+
),
|
|
239
|
+
token_env=("CURSOR_BUGBOT_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
240
|
+
result_sources=("issue_comment",),
|
|
241
|
+
informational=True,
|
|
242
|
+
enabled_by_default=False,
|
|
243
|
+
trigger_policy="manual",
|
|
244
|
+
spend_policy="paid",
|
|
245
|
+
provider_config={
|
|
246
|
+
"bot_authors": ("cursor[bot]", "cursor"),
|
|
247
|
+
"trigger_comments": ("bugbot run", "@cursor review"),
|
|
248
|
+
"rules_file": ".cursor/BUGBOT.md",
|
|
249
|
+
"status": (
|
|
250
|
+
"manual informational lane; keep calibration-only until enabled "
|
|
251
|
+
"BugBot output shape is captured and adjudicated"
|
|
252
|
+
),
|
|
253
|
+
},
|
|
254
|
+
),
|
|
255
|
+
"local_llm": ProviderLane(
|
|
256
|
+
lane_id="local_llm",
|
|
257
|
+
lane_type="audit",
|
|
258
|
+
driver="api_model",
|
|
259
|
+
provider="openai_compatible",
|
|
260
|
+
labels=LaneLabels(
|
|
261
|
+
needs="needs-local-llm-audit",
|
|
262
|
+
done="local-llm-audit-done",
|
|
263
|
+
blocked="local-llm-audit-blocked",
|
|
264
|
+
),
|
|
265
|
+
token_env=("LOCAL_LLM_AUDIT_LABEL_TOKEN", "LOCAL_LLM_API_KEY", "GITHUB_TOKEN"),
|
|
266
|
+
result_sources=("trailer_comment",),
|
|
267
|
+
informational=True,
|
|
268
|
+
enabled_by_default=False,
|
|
269
|
+
trigger_policy="manual",
|
|
270
|
+
spend_policy="local",
|
|
271
|
+
provider_config={
|
|
272
|
+
"api_base_env": "LOCAL_LLM_API_BASE",
|
|
273
|
+
"model_env": "LOCAL_LLM_MODEL",
|
|
274
|
+
"bot_authors_env": "LOCAL_LLM_BOT_AUTHORS",
|
|
275
|
+
"default_api_base": "http://localhost:1234/v1",
|
|
276
|
+
"profile_env": "LOCAL_LLM_PROFILE",
|
|
277
|
+
# Keep provider metadata synced with the canonical profile registry.
|
|
278
|
+
"profiles": local_llm_profiles.profile_ids(),
|
|
279
|
+
"status": "informational calibration lane, not merge authority",
|
|
280
|
+
},
|
|
281
|
+
),
|
|
282
|
+
"aider": ProviderLane(
|
|
283
|
+
lane_id="aider",
|
|
284
|
+
lane_type="audit",
|
|
285
|
+
driver="local_cli",
|
|
286
|
+
provider="aider",
|
|
287
|
+
labels=LaneLabels(
|
|
288
|
+
needs="needs-aider-audit",
|
|
289
|
+
done="aider-audit-done",
|
|
290
|
+
blocked="aider-audit-blocked",
|
|
291
|
+
),
|
|
292
|
+
token_env=("AIDER_AUDIT_LABEL_TOKEN", "GITHUB_TOKEN"),
|
|
293
|
+
result_sources=("trailer_comment",),
|
|
294
|
+
informational=True,
|
|
295
|
+
enabled_by_default=False,
|
|
296
|
+
trigger_policy="manual",
|
|
297
|
+
spend_policy="local",
|
|
298
|
+
provider_config={
|
|
299
|
+
"command": "aider",
|
|
300
|
+
"mode": "review-only",
|
|
301
|
+
"model_env": "AIDER_MODEL",
|
|
302
|
+
"api_base_env": "AIDER_API_BASE",
|
|
303
|
+
"status": "optional tool lane; provider-specific invocation belongs in an adapter",
|
|
304
|
+
},
|
|
305
|
+
),
|
|
306
|
+
"gemini_cli": ProviderLane(
|
|
307
|
+
lane_id="gemini_cli",
|
|
308
|
+
lane_type="audit",
|
|
309
|
+
driver="local_cli",
|
|
310
|
+
provider="gemini",
|
|
311
|
+
labels=LaneLabels(
|
|
312
|
+
needs="needs-gemini-cli-audit",
|
|
313
|
+
done="gemini-cli-audit-done",
|
|
314
|
+
blocked="gemini-cli-audit-blocked",
|
|
315
|
+
),
|
|
316
|
+
token_env=("GITHUB_TOKEN",),
|
|
317
|
+
token_env_any=(("GEMINI_API_KEY", "GOOGLE_API_KEY"),),
|
|
318
|
+
result_sources=("trailer_comment",),
|
|
319
|
+
informational=True,
|
|
320
|
+
enabled_by_default=False,
|
|
321
|
+
trigger_policy="manual",
|
|
322
|
+
spend_policy="included",
|
|
323
|
+
provider_config={
|
|
324
|
+
"command": "gemini",
|
|
325
|
+
"command_env": "GEMINI_CLI_COMMAND",
|
|
326
|
+
"model_env": "GEMINI_MODEL",
|
|
327
|
+
"prompt_lenses": ("base-audit",),
|
|
328
|
+
"doctor_probe_args": (
|
|
329
|
+
"-p",
|
|
330
|
+
"Reply with exactly: ok",
|
|
331
|
+
"--output-format",
|
|
332
|
+
"json",
|
|
333
|
+
"--skip-trust",
|
|
334
|
+
),
|
|
335
|
+
"doctor_probe_timeout_seconds": 45,
|
|
336
|
+
"doctor_probe_expect_json": True,
|
|
337
|
+
"doctor_probe_expect_json_field": "response",
|
|
338
|
+
"doctor_probe_expect_json_value": "ok",
|
|
339
|
+
"status": "optional third-peer CLI lane; not merge authority until calibrated",
|
|
340
|
+
},
|
|
341
|
+
),
|
|
342
|
+
"antigravity_cli": ProviderLane(
|
|
343
|
+
lane_id="antigravity_cli",
|
|
344
|
+
lane_type="audit",
|
|
345
|
+
driver="local_cli",
|
|
346
|
+
provider="antigravity",
|
|
347
|
+
labels=LaneLabels(
|
|
348
|
+
needs="needs-antigravity-cli-audit",
|
|
349
|
+
done="antigravity-cli-audit-done",
|
|
350
|
+
blocked="antigravity-cli-audit-blocked",
|
|
351
|
+
),
|
|
352
|
+
token_env=("GITHUB_TOKEN",),
|
|
353
|
+
result_sources=("trailer_comment",),
|
|
354
|
+
informational=True,
|
|
355
|
+
enabled_by_default=False,
|
|
356
|
+
trigger_policy="manual",
|
|
357
|
+
spend_policy="included",
|
|
358
|
+
provider_config={
|
|
359
|
+
"command": "agy",
|
|
360
|
+
"alternate_commands": ("antigravity",),
|
|
361
|
+
"command_env": "ANTIGRAVITY_CLI_COMMAND",
|
|
362
|
+
"model_env": "ANTIGRAVITY_MODEL",
|
|
363
|
+
"prompt_lenses": ("base-audit",),
|
|
364
|
+
"required_env_truthy": ("ANTIGRAVITY_CLI_USE_AMBIENT_HOME",),
|
|
365
|
+
"doctor_probe_args": ("--version",),
|
|
366
|
+
"auth": (
|
|
367
|
+
"run `agy` login/install locally; set "
|
|
368
|
+
"ANTIGRAVITY_CLI_USE_AMBIENT_HOME=1 to opt into local OAuth "
|
|
369
|
+
"state in trusted environments"
|
|
370
|
+
),
|
|
371
|
+
"status": (
|
|
372
|
+
"forward Google CLI research lane; not merge authority until "
|
|
373
|
+
"calibrated against Gemini compatibility records"
|
|
374
|
+
),
|
|
375
|
+
},
|
|
376
|
+
),
|
|
377
|
+
"hermes_cli": ProviderLane(
|
|
378
|
+
lane_id="hermes_cli",
|
|
379
|
+
lane_type="audit",
|
|
380
|
+
driver="local_cli",
|
|
381
|
+
provider="hermes",
|
|
382
|
+
labels=LaneLabels(
|
|
383
|
+
needs="needs-hermes-cli-audit",
|
|
384
|
+
done="hermes-cli-audit-done",
|
|
385
|
+
blocked="hermes-cli-audit-blocked",
|
|
386
|
+
),
|
|
387
|
+
token_env=("GITHUB_TOKEN",),
|
|
388
|
+
result_sources=("trailer_comment",),
|
|
389
|
+
informational=True,
|
|
390
|
+
enabled_by_default=False,
|
|
391
|
+
trigger_policy="manual",
|
|
392
|
+
spend_policy="included",
|
|
393
|
+
provider_config={
|
|
394
|
+
"command": "hermes",
|
|
395
|
+
"command_env": "HERMES_CLI_COMMAND",
|
|
396
|
+
"model_env": "HERMES_INFERENCE_MODEL",
|
|
397
|
+
"provider_env": "HERMES_PROVIDER",
|
|
398
|
+
"prompt_lenses": ("base-audit",),
|
|
399
|
+
"required_env_truthy": ("HERMES_CLI_USE_AMBIENT_HOME",),
|
|
400
|
+
"doctor_probe_args": ("--version",),
|
|
401
|
+
"auth": (
|
|
402
|
+
"run `hermes setup` or provider login locally; set "
|
|
403
|
+
"HERMES_CLI_USE_AMBIENT_HOME=1 to opt into local Hermes "
|
|
404
|
+
"auth/session state in trusted environments"
|
|
405
|
+
),
|
|
406
|
+
"status": (
|
|
407
|
+
"informational Hermes Agent one-shot lane; not merge authority "
|
|
408
|
+
"until calibrated and kept stateless enough for blind review"
|
|
409
|
+
),
|
|
410
|
+
},
|
|
411
|
+
),
|
|
412
|
+
"coderabbit_cli": ProviderLane(
|
|
413
|
+
lane_id="coderabbit_cli",
|
|
414
|
+
lane_type="audit",
|
|
415
|
+
driver="local_cli",
|
|
416
|
+
provider="coderabbit",
|
|
417
|
+
labels=LaneLabels(
|
|
418
|
+
needs="needs-coderabbit-cli-audit",
|
|
419
|
+
done="coderabbit-cli-audit-done",
|
|
420
|
+
blocked="coderabbit-cli-audit-blocked",
|
|
421
|
+
),
|
|
422
|
+
token_env=("GITHUB_TOKEN",),
|
|
423
|
+
result_sources=("trailer_comment",),
|
|
424
|
+
informational=True,
|
|
425
|
+
enabled_by_default=False,
|
|
426
|
+
trigger_policy="manual",
|
|
427
|
+
spend_policy="included",
|
|
428
|
+
provider_config={
|
|
429
|
+
"command": "coderabbit",
|
|
430
|
+
"alternate_commands": ("cr",),
|
|
431
|
+
"status": "optional CLI lane for local/on-demand review capture",
|
|
432
|
+
},
|
|
433
|
+
),
|
|
434
|
+
"acp_bridge": ProviderLane(
|
|
435
|
+
lane_id="acp_bridge",
|
|
436
|
+
lane_type="audit",
|
|
437
|
+
driver="local_cli",
|
|
438
|
+
provider="acp",
|
|
439
|
+
labels=LaneLabels(
|
|
440
|
+
needs="needs-acp-audit",
|
|
441
|
+
done="acp-audit-done",
|
|
442
|
+
blocked="acp-audit-blocked",
|
|
443
|
+
),
|
|
444
|
+
token_env=("GITHUB_TOKEN",),
|
|
445
|
+
result_sources=("trailer_comment",),
|
|
446
|
+
informational=True,
|
|
447
|
+
enabled_by_default=False,
|
|
448
|
+
trigger_policy="manual",
|
|
449
|
+
spend_policy="none",
|
|
450
|
+
provider_config={
|
|
451
|
+
"command_env": "CODE_MOWER_ACP_COMMAND",
|
|
452
|
+
"protocol": "agent-client-protocol",
|
|
453
|
+
"prompt_lenses": ("base-audit",),
|
|
454
|
+
"status": "protocol research lane; no automatic merge authority",
|
|
455
|
+
},
|
|
456
|
+
),
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def get_provider(lane_id: str) -> ProviderLane:
|
|
461
|
+
return REFERENCE_PROVIDERS[lane_id]
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def audit_queue_labels() -> tuple[str, ...]:
|
|
465
|
+
return tuple(
|
|
466
|
+
lane.labels.needs
|
|
467
|
+
for lane in REFERENCE_PROVIDERS.values()
|
|
468
|
+
if lane.lane_type in {"audit", "review"}
|
|
469
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Shared provider-runner primitives.
|
|
2
|
+
|
|
3
|
+
Provider-specific modules remain responsible for prompts and verdict parsing.
|
|
4
|
+
This package holds common process/auth helpers used by Codex, Claude,
|
|
5
|
+
Gemini/Antigravity, Hermes, and local reviewer lanes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .comments import MAX_GITHUB_COMMENT_CHARS, limit_comment_body
|
|
9
|
+
from .git import fetch_local_checkout_diff, local_head_sha, run_git
|
|
10
|
+
from .github_auth import (
|
|
11
|
+
pop_github_token_env,
|
|
12
|
+
resolve_github_token_from_env_or_gh,
|
|
13
|
+
resolve_github_token_from_stdin_or_env,
|
|
14
|
+
)
|
|
15
|
+
from .github_pr import (
|
|
16
|
+
fetch_pull_request,
|
|
17
|
+
fetch_pull_request_diff,
|
|
18
|
+
fetch_pull_request_files,
|
|
19
|
+
post_pr_comment,
|
|
20
|
+
)
|
|
21
|
+
from .process import DEFAULT_HOME_ENV_KEYS, build_allowlisted_child_env
|
|
22
|
+
from .repo_paths import parse_repo_paths
|
|
23
|
+
from .text_schema import clip_text, one_line, require_exact_keys
|
|
24
|
+
from .verdict_artifacts import (
|
|
25
|
+
load_audit_verdict_artifact,
|
|
26
|
+
repost_audit_verdict_artifact,
|
|
27
|
+
write_audit_verdict_artifact,
|
|
28
|
+
)
|
|
29
|
+
from .workspace import (
|
|
30
|
+
ProviderWorkspaceError,
|
|
31
|
+
verify_checkout_at_head,
|
|
32
|
+
working_tree_status,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"fetch_pull_request",
|
|
37
|
+
"fetch_pull_request_diff",
|
|
38
|
+
"fetch_pull_request_files",
|
|
39
|
+
"fetch_local_checkout_diff",
|
|
40
|
+
"load_audit_verdict_artifact",
|
|
41
|
+
"local_head_sha",
|
|
42
|
+
"limit_comment_body",
|
|
43
|
+
"MAX_GITHUB_COMMENT_CHARS",
|
|
44
|
+
"clip_text",
|
|
45
|
+
"DEFAULT_HOME_ENV_KEYS",
|
|
46
|
+
"one_line",
|
|
47
|
+
"parse_repo_paths",
|
|
48
|
+
"pop_github_token_env",
|
|
49
|
+
"post_pr_comment",
|
|
50
|
+
"ProviderWorkspaceError",
|
|
51
|
+
"repost_audit_verdict_artifact",
|
|
52
|
+
"require_exact_keys",
|
|
53
|
+
"resolve_github_token_from_env_or_gh",
|
|
54
|
+
"resolve_github_token_from_stdin_or_env",
|
|
55
|
+
"run_git",
|
|
56
|
+
"verify_checkout_at_head",
|
|
57
|
+
"working_tree_status",
|
|
58
|
+
"build_allowlisted_child_env",
|
|
59
|
+
"write_audit_verdict_artifact",
|
|
60
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Shared GitHub comment formatting helpers for provider wrappers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
MAX_GITHUB_COMMENT_CHARS = 64_000
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def limit_comment_body(
|
|
10
|
+
body: str,
|
|
11
|
+
trailer: str,
|
|
12
|
+
*,
|
|
13
|
+
provider_name: str,
|
|
14
|
+
max_chars: int = MAX_GITHUB_COMMENT_CHARS,
|
|
15
|
+
) -> str:
|
|
16
|
+
"""Keep an audit comment under GitHub's body-size limit without losing trailer state."""
|
|
17
|
+
|
|
18
|
+
if len(body) <= max_chars:
|
|
19
|
+
return body
|
|
20
|
+
|
|
21
|
+
note = (
|
|
22
|
+
f"\n\n[{provider_name} audit comment truncated to stay under "
|
|
23
|
+
"GitHub's comment-size limit.]\n\n"
|
|
24
|
+
)
|
|
25
|
+
suffix = note + trailer + "\n"
|
|
26
|
+
allowed_prefix_len = max_chars - len(suffix)
|
|
27
|
+
if allowed_prefix_len < 0:
|
|
28
|
+
return suffix[-max_chars:]
|
|
29
|
+
|
|
30
|
+
prefix = body.rsplit(trailer, 1)[0] if trailer in body else body
|
|
31
|
+
return prefix[:allowed_prefix_len].rstrip() + suffix
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Shared git helpers for provider-runner wrappers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Sequence
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_git(
|
|
11
|
+
repo_path: Path,
|
|
12
|
+
args: Sequence[str],
|
|
13
|
+
*,
|
|
14
|
+
check: bool = True,
|
|
15
|
+
timeout: int = 30,
|
|
16
|
+
) -> subprocess.CompletedProcess[str]:
|
|
17
|
+
"""Run ``git -C repo_path`` and return captured text output."""
|
|
18
|
+
|
|
19
|
+
return subprocess.run(
|
|
20
|
+
["git", "-C", str(repo_path), *args],
|
|
21
|
+
capture_output=True,
|
|
22
|
+
text=True,
|
|
23
|
+
check=check,
|
|
24
|
+
timeout=timeout,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def local_head_sha(repo_path: Path) -> str:
|
|
29
|
+
"""Return the current HEAD SHA for a local git checkout."""
|
|
30
|
+
|
|
31
|
+
return run_git(repo_path, ["rev-parse", "HEAD"]).stdout.strip()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def fetch_local_checkout_diff(repo_path: Path, *, base_ref: str) -> tuple[str, str]:
|
|
35
|
+
"""Return ``(head_sha, diff)`` for a local checkout against ``base_ref``."""
|
|
36
|
+
|
|
37
|
+
resolved_repo_path = repo_path.expanduser().resolve()
|
|
38
|
+
if not resolved_repo_path.is_dir():
|
|
39
|
+
raise ValueError(f"repo path does not exist: {resolved_repo_path}")
|
|
40
|
+
head_sha = local_head_sha(resolved_repo_path)
|
|
41
|
+
diff = run_git(
|
|
42
|
+
resolved_repo_path,
|
|
43
|
+
["diff", "--no-ext-diff", "--find-renames", f"{base_ref}...HEAD"],
|
|
44
|
+
timeout=120,
|
|
45
|
+
).stdout
|
|
46
|
+
return head_sha, diff
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""GitHub token resolution helpers for provider runner CLIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from typing import TextIO
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def pop_github_token_env() -> str | None:
|
|
12
|
+
"""Return GITHUB_TOKEN and clear GitHub token aliases from this process.
|
|
13
|
+
|
|
14
|
+
This preserves the legacy direct-invocation behavior for provider wrappers
|
|
15
|
+
while keeping the cleanup logic in one place.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
token = os.environ.pop("GITHUB_TOKEN", None)
|
|
19
|
+
os.environ.pop("GH_TOKEN", None)
|
|
20
|
+
return token
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def resolve_github_token_from_stdin_or_env(
|
|
24
|
+
read_from_stdin: bool,
|
|
25
|
+
*,
|
|
26
|
+
stdin: TextIO | None = None,
|
|
27
|
+
) -> str | None:
|
|
28
|
+
"""Resolve a GitHub token from stdin or the legacy process environment."""
|
|
29
|
+
|
|
30
|
+
if read_from_stdin:
|
|
31
|
+
source = stdin if stdin is not None else sys.stdin
|
|
32
|
+
line = source.readline()
|
|
33
|
+
os.environ.pop("GITHUB_TOKEN", None)
|
|
34
|
+
os.environ.pop("GH_TOKEN", None)
|
|
35
|
+
if not line:
|
|
36
|
+
return None
|
|
37
|
+
return line.rstrip("\r\n")
|
|
38
|
+
return pop_github_token_env()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def resolve_github_token_from_env_or_gh() -> str:
|
|
42
|
+
"""Resolve a GitHub token from env or `gh auth token` for local lanes."""
|
|
43
|
+
|
|
44
|
+
token = os.environ.get("GITHUB_TOKEN")
|
|
45
|
+
if token:
|
|
46
|
+
return token
|
|
47
|
+
try:
|
|
48
|
+
completed = subprocess.run(
|
|
49
|
+
["gh", "auth", "token"],
|
|
50
|
+
check=True,
|
|
51
|
+
capture_output=True,
|
|
52
|
+
text=True,
|
|
53
|
+
timeout=10,
|
|
54
|
+
)
|
|
55
|
+
except (
|
|
56
|
+
FileNotFoundError,
|
|
57
|
+
subprocess.CalledProcessError,
|
|
58
|
+
subprocess.TimeoutExpired,
|
|
59
|
+
):
|
|
60
|
+
return ""
|
|
61
|
+
return completed.stdout.strip()
|