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,203 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Small progress helpers for local Code Mower audit wrappers.
|
|
3
|
+
|
|
4
|
+
The wrappers can run a reviewer subprocess for many minutes. This module keeps
|
|
5
|
+
status reporting generic: providers pass a lane name and phase name, while the
|
|
6
|
+
core emits elapsed-time progress without knowing provider-specific semantics.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
import shlex
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import threading
|
|
16
|
+
import time
|
|
17
|
+
import urllib.parse
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Any, Callable, Optional, Sequence, TextIO
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
RunFn = Callable[..., subprocess.CompletedProcess]
|
|
23
|
+
|
|
24
|
+
SENSITIVE_VALUE_FLAGS = {
|
|
25
|
+
"--api-key",
|
|
26
|
+
"--apikey",
|
|
27
|
+
"--authorization",
|
|
28
|
+
"--bearer-token",
|
|
29
|
+
"--github-token",
|
|
30
|
+
"--password",
|
|
31
|
+
"--secret",
|
|
32
|
+
"--token",
|
|
33
|
+
}
|
|
34
|
+
SENSITIVE_KEYWORDS = (
|
|
35
|
+
"api-key",
|
|
36
|
+
"apikey",
|
|
37
|
+
"api_key",
|
|
38
|
+
"authorization",
|
|
39
|
+
"bearer",
|
|
40
|
+
"password",
|
|
41
|
+
"passwd",
|
|
42
|
+
"secret",
|
|
43
|
+
"token",
|
|
44
|
+
)
|
|
45
|
+
SENSITIVE_ASSIGNMENT_PATTERN = re.compile(
|
|
46
|
+
r"\b([A-Za-z0-9_.-]*(?:api[-_]?key|apikey|authorization|bearer|password|passwd|secret|token)[A-Za-z0-9_.-]*)=([^\s&]+)",
|
|
47
|
+
flags=re.IGNORECASE,
|
|
48
|
+
)
|
|
49
|
+
SENSITIVE_FLAG_PATTERN = re.compile(
|
|
50
|
+
r"(--(?:api-key|apikey|authorization|bearer-token|github-token|password|secret|token)\s+)(\S+)",
|
|
51
|
+
flags=re.IGNORECASE,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _looks_sensitive_key(value: str) -> bool:
|
|
56
|
+
normalized = value.lower().replace("_", "-")
|
|
57
|
+
return any(keyword.replace("_", "-") in normalized for keyword in SENSITIVE_KEYWORDS)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def redact_command(command: Sequence[str]) -> list[str]:
|
|
61
|
+
"""Mask common secret-bearing command arguments before progress logging."""
|
|
62
|
+
redacted: list[str] = []
|
|
63
|
+
mask_next = False
|
|
64
|
+
|
|
65
|
+
for part in command:
|
|
66
|
+
if mask_next:
|
|
67
|
+
redacted.append("REDACTED")
|
|
68
|
+
mask_next = False
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
key, sep, _value = part.partition("=")
|
|
72
|
+
if sep and _looks_sensitive_key(key):
|
|
73
|
+
redacted.append(f"{key}=REDACTED")
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
redacted.append(part)
|
|
77
|
+
if part in SENSITIVE_VALUE_FLAGS:
|
|
78
|
+
mask_next = True
|
|
79
|
+
|
|
80
|
+
return redacted
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def redact_detail(value: str) -> str:
|
|
84
|
+
"""Mask common secret-bearing key/value fragments in free-form detail text."""
|
|
85
|
+
value = SENSITIVE_ASSIGNMENT_PATTERN.sub(lambda match: f"{match.group(1)}=REDACTED", value)
|
|
86
|
+
return SENSITIVE_FLAG_PATTERN.sub(lambda match: f"{match.group(1)}REDACTED", value)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def format_command(command: Sequence[str], *, max_args: int = 16) -> str:
|
|
90
|
+
"""Return a compact shell-ish command string for progress logs."""
|
|
91
|
+
redacted = redact_command(command)
|
|
92
|
+
shown = list(redacted[:max_args])
|
|
93
|
+
suffix = ["..."] if len(redacted) > max_args else []
|
|
94
|
+
return " ".join(shlex.quote(part) for part in [*shown, *suffix])
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def format_detail(value: str) -> str:
|
|
98
|
+
"""Return a single-token detail value for key=value progress logs."""
|
|
99
|
+
return urllib.parse.quote(redact_detail(value), safe="/:._@=,+-#")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass
|
|
103
|
+
class AuditProgress:
|
|
104
|
+
lane: str
|
|
105
|
+
heartbeat_seconds: float = 60.0
|
|
106
|
+
stream: Optional[TextIO] = None
|
|
107
|
+
started_at: float = 0.0
|
|
108
|
+
|
|
109
|
+
def __post_init__(self) -> None:
|
|
110
|
+
if self.started_at <= 0:
|
|
111
|
+
self.started_at = time.monotonic()
|
|
112
|
+
|
|
113
|
+
def emit(
|
|
114
|
+
self,
|
|
115
|
+
phase: str,
|
|
116
|
+
*,
|
|
117
|
+
status: str,
|
|
118
|
+
detail: str = "",
|
|
119
|
+
elapsed: Optional[float] = None,
|
|
120
|
+
) -> None:
|
|
121
|
+
if elapsed is None:
|
|
122
|
+
elapsed = time.monotonic() - self.started_at
|
|
123
|
+
stream = self.stream if self.stream is not None else sys.stderr
|
|
124
|
+
detail_part = f" detail={format_detail(detail)}" if detail else ""
|
|
125
|
+
print(
|
|
126
|
+
f"audit-progress lane={self.lane} phase={phase} "
|
|
127
|
+
f"status={status} elapsed={elapsed:.0f}s{detail_part}",
|
|
128
|
+
file=stream,
|
|
129
|
+
flush=True,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def run_subprocess_with_progress(
|
|
134
|
+
command: Sequence[str],
|
|
135
|
+
*,
|
|
136
|
+
progress: AuditProgress,
|
|
137
|
+
phase: str,
|
|
138
|
+
run: RunFn = subprocess.run,
|
|
139
|
+
heartbeat_seconds: Optional[float] = None,
|
|
140
|
+
redacted_command: Optional[Sequence[str]] = None,
|
|
141
|
+
**kwargs: Any,
|
|
142
|
+
) -> subprocess.CompletedProcess:
|
|
143
|
+
"""Run a subprocess while emitting start/heartbeat/finish progress.
|
|
144
|
+
|
|
145
|
+
The `run` callable is injectable so existing wrapper tests can monkeypatch
|
|
146
|
+
their module-local `subprocess.run` without learning about this helper.
|
|
147
|
+
"""
|
|
148
|
+
interval = progress.heartbeat_seconds if heartbeat_seconds is None else heartbeat_seconds
|
|
149
|
+
display_command = format_command(redacted_command or command)
|
|
150
|
+
started_at = time.monotonic()
|
|
151
|
+
progress.emit(phase, status="start", detail=display_command, elapsed=0)
|
|
152
|
+
|
|
153
|
+
stop = threading.Event()
|
|
154
|
+
heartbeat: Optional[threading.Thread] = None
|
|
155
|
+
|
|
156
|
+
if interval > 0:
|
|
157
|
+
|
|
158
|
+
def heartbeat_loop() -> None:
|
|
159
|
+
while not stop.wait(interval):
|
|
160
|
+
progress.emit(
|
|
161
|
+
phase,
|
|
162
|
+
status="running",
|
|
163
|
+
detail=display_command,
|
|
164
|
+
elapsed=time.monotonic() - started_at,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
heartbeat = threading.Thread(
|
|
168
|
+
target=heartbeat_loop,
|
|
169
|
+
name=f"{progress.lane}-{phase}-progress",
|
|
170
|
+
daemon=True,
|
|
171
|
+
)
|
|
172
|
+
heartbeat.start()
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
result = run(list(command), **kwargs)
|
|
176
|
+
except subprocess.TimeoutExpired:
|
|
177
|
+
progress.emit(
|
|
178
|
+
phase,
|
|
179
|
+
status="timeout",
|
|
180
|
+
detail=display_command,
|
|
181
|
+
elapsed=time.monotonic() - started_at,
|
|
182
|
+
)
|
|
183
|
+
raise
|
|
184
|
+
except Exception:
|
|
185
|
+
progress.emit(
|
|
186
|
+
phase,
|
|
187
|
+
status="error",
|
|
188
|
+
detail=display_command,
|
|
189
|
+
elapsed=time.monotonic() - started_at,
|
|
190
|
+
)
|
|
191
|
+
raise
|
|
192
|
+
finally:
|
|
193
|
+
stop.set()
|
|
194
|
+
if heartbeat is not None:
|
|
195
|
+
heartbeat.join(timeout=1)
|
|
196
|
+
|
|
197
|
+
progress.emit(
|
|
198
|
+
phase,
|
|
199
|
+
status=f"exit-{result.returncode}",
|
|
200
|
+
detail=display_command,
|
|
201
|
+
elapsed=time.monotonic() - started_at,
|
|
202
|
+
)
|
|
203
|
+
return result
|