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,207 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generic labeler for trailer-comment audit lanes.
|
|
3
|
+
|
|
4
|
+
Codex, Devin, and Local LLM audit comments all use the same state machine:
|
|
5
|
+
trusted comment author -> final verdict trailer/prose -> reviewed head check ->
|
|
6
|
+
terminal or needs label. Lane-specific values live in tools/lane_configs/.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Dict, Optional, Sequence
|
|
18
|
+
|
|
19
|
+
if __package__ and __package__.startswith("code_mower"):
|
|
20
|
+
from .audit_labeler_lib import (
|
|
21
|
+
LaneConfig,
|
|
22
|
+
LabelDecision,
|
|
23
|
+
apply_label_decision,
|
|
24
|
+
extract_reviewed_sha,
|
|
25
|
+
fetch_pull_request,
|
|
26
|
+
load_json,
|
|
27
|
+
sha_matches_reviewed_head,
|
|
28
|
+
)
|
|
29
|
+
from .lane_configs import load_lane_config
|
|
30
|
+
else:
|
|
31
|
+
try:
|
|
32
|
+
from tools.audit_labeler_lib import (
|
|
33
|
+
LaneConfig,
|
|
34
|
+
LabelDecision,
|
|
35
|
+
apply_label_decision,
|
|
36
|
+
extract_reviewed_sha,
|
|
37
|
+
fetch_pull_request,
|
|
38
|
+
load_json,
|
|
39
|
+
sha_matches_reviewed_head,
|
|
40
|
+
)
|
|
41
|
+
from tools.lane_configs import load_lane_config
|
|
42
|
+
except ImportError: # pragma: no cover - direct `python tools/foo.py` execution
|
|
43
|
+
from audit_labeler_lib import (
|
|
44
|
+
LaneConfig,
|
|
45
|
+
LabelDecision,
|
|
46
|
+
apply_label_decision,
|
|
47
|
+
extract_reviewed_sha,
|
|
48
|
+
fetch_pull_request,
|
|
49
|
+
load_json,
|
|
50
|
+
sha_matches_reviewed_head,
|
|
51
|
+
)
|
|
52
|
+
from lane_configs import load_lane_config
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
HEAD_CHANGED_PATTERN = re.compile(
|
|
56
|
+
r"HEAD_CHANGED_DURING_REVIEW\s*:\s*reviewed\b",
|
|
57
|
+
flags=re.IGNORECASE,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def classify_audit_comment(body: str, config: LaneConfig) -> Optional[str]:
|
|
62
|
+
"""Return "done", "blocked", "needs", or None for a lane comment body."""
|
|
63
|
+
trailers = list(config.trailer_pattern().finditer(body))
|
|
64
|
+
if trailers:
|
|
65
|
+
label = trailers[-1].group(1).lower()
|
|
66
|
+
if label == config.done_label:
|
|
67
|
+
return "done"
|
|
68
|
+
if label == config.blocked_label:
|
|
69
|
+
return "blocked"
|
|
70
|
+
return "needs"
|
|
71
|
+
|
|
72
|
+
if HEAD_CHANGED_PATTERN.search(body):
|
|
73
|
+
return "needs"
|
|
74
|
+
|
|
75
|
+
if any(pattern.search(body) for pattern in config.pass_patterns):
|
|
76
|
+
return "done"
|
|
77
|
+
if config.label_state_fallbacks and _has_label_fallback(body, config.done_label):
|
|
78
|
+
return "done"
|
|
79
|
+
|
|
80
|
+
if any(pattern.search(body) for pattern in config.blocked_patterns):
|
|
81
|
+
return "blocked"
|
|
82
|
+
if config.label_state_fallbacks and _has_label_fallback(body, config.blocked_label):
|
|
83
|
+
return "blocked"
|
|
84
|
+
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _has_label_fallback(body: str, label: str) -> bool:
|
|
89
|
+
escaped = re.escape(label)
|
|
90
|
+
return bool(
|
|
91
|
+
re.search(
|
|
92
|
+
rf"\**(?:Intended\s+|Expected\s+)?Label state:\**\s*`?\b{escaped}\b",
|
|
93
|
+
body,
|
|
94
|
+
flags=re.IGNORECASE,
|
|
95
|
+
)
|
|
96
|
+
or re.search(
|
|
97
|
+
rf"(?:Intended|Expected)\s+labels?:\s*(?:add\s+)?`?\b{escaped}\b",
|
|
98
|
+
body,
|
|
99
|
+
flags=re.IGNORECASE,
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def resolve_label_decision(
|
|
105
|
+
event: Dict[str, Any],
|
|
106
|
+
*,
|
|
107
|
+
current_head_sha: Optional[str],
|
|
108
|
+
config: LaneConfig,
|
|
109
|
+
) -> tuple[Optional[LabelDecision], str]:
|
|
110
|
+
if event.get("action") != "created":
|
|
111
|
+
return None, f"unsupported action: {event.get('action')}"
|
|
112
|
+
|
|
113
|
+
issue = event.get("issue") or {}
|
|
114
|
+
if "pull_request" not in issue:
|
|
115
|
+
return None, "comment is not on a pull request"
|
|
116
|
+
|
|
117
|
+
comment = event.get("comment") or {}
|
|
118
|
+
author = (comment.get("user") or {}).get("login", "")
|
|
119
|
+
if author.lower() not in config.comment_authors():
|
|
120
|
+
return None, f"ignored comment author: {author}"
|
|
121
|
+
|
|
122
|
+
body = comment.get("body") or ""
|
|
123
|
+
status = classify_audit_comment(body, config)
|
|
124
|
+
if status is None:
|
|
125
|
+
return None, f"comment is not a final {config.display_name} audit result"
|
|
126
|
+
|
|
127
|
+
issue_number = int(issue["number"])
|
|
128
|
+
reviewed_sha = extract_reviewed_sha(body)
|
|
129
|
+
if status != "needs" and not reviewed_sha:
|
|
130
|
+
return None, "audit result is missing Head SHA"
|
|
131
|
+
if reviewed_sha and current_head_sha and not sha_matches_reviewed_head(reviewed_sha, current_head_sha):
|
|
132
|
+
return LabelDecision(
|
|
133
|
+
issue_number=issue_number,
|
|
134
|
+
add_label=config.needs_label,
|
|
135
|
+
remove_labels=(config.done_label, config.blocked_label),
|
|
136
|
+
reviewed_sha=reviewed_sha,
|
|
137
|
+
reason=f"reviewed SHA {reviewed_sha} does not match current head {current_head_sha}",
|
|
138
|
+
), "label needs audit"
|
|
139
|
+
|
|
140
|
+
if status == "needs":
|
|
141
|
+
return LabelDecision(
|
|
142
|
+
issue_number=issue_number,
|
|
143
|
+
add_label=config.needs_label,
|
|
144
|
+
remove_labels=(config.done_label, config.blocked_label),
|
|
145
|
+
reviewed_sha=reviewed_sha,
|
|
146
|
+
reason="audit reported head changed during review",
|
|
147
|
+
), "label needs audit"
|
|
148
|
+
|
|
149
|
+
if status == "done":
|
|
150
|
+
return LabelDecision(
|
|
151
|
+
issue_number=issue_number,
|
|
152
|
+
add_label=config.done_label,
|
|
153
|
+
remove_labels=(config.needs_label, config.blocked_label),
|
|
154
|
+
reviewed_sha=reviewed_sha,
|
|
155
|
+
reason="audit passed",
|
|
156
|
+
), "label done"
|
|
157
|
+
|
|
158
|
+
return LabelDecision(
|
|
159
|
+
issue_number=issue_number,
|
|
160
|
+
add_label=config.blocked_label,
|
|
161
|
+
remove_labels=(config.needs_label, config.done_label),
|
|
162
|
+
reviewed_sha=reviewed_sha,
|
|
163
|
+
reason="audit blocked or incomplete",
|
|
164
|
+
), "label blocked"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
168
|
+
parser = argparse.ArgumentParser()
|
|
169
|
+
parser.add_argument("--lane", required=True, help="Trailer audit lane from tools/lane_configs/.")
|
|
170
|
+
args = parser.parse_args(argv)
|
|
171
|
+
config = load_lane_config(args.lane)
|
|
172
|
+
|
|
173
|
+
event_path = Path(os.environ["GITHUB_EVENT_PATH"])
|
|
174
|
+
repo = os.environ["GITHUB_REPOSITORY"]
|
|
175
|
+
event = load_json(event_path)
|
|
176
|
+
|
|
177
|
+
current_head_sha = os.environ.get("DRY_RUN_HEAD_SHA")
|
|
178
|
+
tokens = config.github_tokens_from_env()
|
|
179
|
+
if not os.environ.get("DRY_RUN"):
|
|
180
|
+
if not tokens:
|
|
181
|
+
token_names = " or ".join(config.token_env_vars)
|
|
182
|
+
print(f"error: {token_names} is required", file=sys.stderr)
|
|
183
|
+
return 1
|
|
184
|
+
issue = event.get("issue") or {}
|
|
185
|
+
issue_number = int(issue.get("number", 0))
|
|
186
|
+
if issue_number and "pull_request" in issue:
|
|
187
|
+
current_head_sha = fetch_pull_request(repo, issue_number, tokens=tokens)["head"]["sha"]
|
|
188
|
+
|
|
189
|
+
decision, reason = resolve_label_decision(event, current_head_sha=current_head_sha, config=config)
|
|
190
|
+
if decision is None:
|
|
191
|
+
print(f"skip: {reason}")
|
|
192
|
+
return 0
|
|
193
|
+
|
|
194
|
+
if os.environ.get("DRY_RUN"):
|
|
195
|
+
print(json.dumps({"decision": decision.__dict__, "reason": reason}, sort_keys=True))
|
|
196
|
+
return 0
|
|
197
|
+
|
|
198
|
+
apply_label_decision(repo, decision, tokens=tokens)
|
|
199
|
+
print(
|
|
200
|
+
f"applied: add {decision.add_label}; remove {', '.join(decision.remove_labels)} "
|
|
201
|
+
f"on {repo}#{decision.issue_number} ({decision.reason})"
|
|
202
|
+
)
|
|
203
|
+
return 0
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
raise SystemExit(main())
|
code_mower/versioning.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Version and public package-spec helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
PUBLIC_REPO_URL = "https://github.com/codemower-ai/code-mower"
|
|
9
|
+
STAGE_TAG_NAMES = {
|
|
10
|
+
"a": "alpha",
|
|
11
|
+
"b": "beta",
|
|
12
|
+
"rc": "rc",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def release_tag_for_version(version: str) -> str:
|
|
17
|
+
match = re.fullmatch(
|
|
18
|
+
r"(?P<base>\d+\.\d+\.\d+)(?:(?P<stage>a|b|rc)(?P<num>\d+))?",
|
|
19
|
+
version,
|
|
20
|
+
)
|
|
21
|
+
if not match:
|
|
22
|
+
return f"v{version}"
|
|
23
|
+
base = match.group("base")
|
|
24
|
+
stage = match.group("stage")
|
|
25
|
+
number = match.group("num")
|
|
26
|
+
if not stage or not number:
|
|
27
|
+
return f"v{base}"
|
|
28
|
+
return f"v{base}-{STAGE_TAG_NAMES[stage]}.{number}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def public_package_spec(version: str, repo_url: str = PUBLIC_REPO_URL) -> str:
|
|
32
|
+
return f"git+{repo_url}.git@{release_tag_for_version(version)}"
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-mower
|
|
3
|
+
Version: 0.5.0b5
|
|
4
|
+
Summary: Multi-reviewer AI code audit orchestration
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: NOTICE
|
|
10
|
+
Requires-Dist: PyYAML>=6.0
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Code Mower
|
|
14
|
+
|
|
15
|
+
Code Mower helps teams set up AI peer-programmer and reviewer lanes on real
|
|
16
|
+
GitHub pull requests, then measure which builders and reviewers are useful on
|
|
17
|
+
their actual codebase.
|
|
18
|
+
|
|
19
|
+
The short version:
|
|
20
|
+
|
|
21
|
+
- create safe, manual-first reviewer lanes for Codex, Claude, Gitar, and other
|
|
22
|
+
AI review tools;
|
|
23
|
+
- run setup diagnostics before a lane can surprise you with spend, source
|
|
24
|
+
exposure, or GitHub Actions churn;
|
|
25
|
+
- generate local reviewer value reports from known-clean and known-blocked PRs;
|
|
26
|
+
and
|
|
27
|
+
- optionally share sanitized metadata with [CodeMower.com](https://codemower.com)
|
|
28
|
+
for private team dashboards today and aggregate benchmarks as that dataset
|
|
29
|
+
becomes useful.
|
|
30
|
+
|
|
31
|
+
Code Mower is local-first. The OSS tool works without the hosted service.
|
|
32
|
+
Default cloud bundles exclude source code, raw diffs, raw model transcripts,
|
|
33
|
+
raw stdout/stderr, auth output, and secrets.
|
|
34
|
+
|
|
35
|
+
## Design Principles
|
|
36
|
+
|
|
37
|
+
Code Mower should feel like an engineering tool, not a demo harness:
|
|
38
|
+
|
|
39
|
+
- **Local first:** install, diagnose, audit, and report without a hosted
|
|
40
|
+
account.
|
|
41
|
+
- **Manual first:** new reviewer lanes start explicit and observable before
|
|
42
|
+
they can affect merge policy.
|
|
43
|
+
- **Evidence first:** promote lanes from real calibration data on your
|
|
44
|
+
repository, not from generic benchmark claims.
|
|
45
|
+
- **Privacy first:** cloud sharing is opt-in metadata by default, with source,
|
|
46
|
+
diffs, transcripts, auth output, and secrets excluded.
|
|
47
|
+
- **Composable by design:** providers, lenses, context packs, calibration, and
|
|
48
|
+
cloud upload stay separate so teams can adopt only the parts they trust.
|
|
49
|
+
|
|
50
|
+
## What It Looks Like
|
|
51
|
+
|
|
52
|
+
`code-mower doctor --preflight` is the first useful command. It checks your
|
|
53
|
+
runtime, GitHub setup, provider CLIs, token posture, optional cloud setup, and
|
|
54
|
+
private-repo Actions cost traps. `--preflight` is the friendly name for the
|
|
55
|
+
versioned v0.5 first-run preset, so `doctor --v05` remains equivalent for
|
|
56
|
+
scripts.
|
|
57
|
+
|
|
58
|
+
Example, shortened:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
$ code-mower doctor --preflight
|
|
62
|
+
PASS config.validate config validates
|
|
63
|
+
PASS profile.select selected profile: codex, claude_audit, gitar
|
|
64
|
+
PASS runtime.python Python 3.12 satisfies Code Mower requirements
|
|
65
|
+
PASS runtime.github_auth GitHub CLI auth probe succeeded
|
|
66
|
+
PASS runtime.local_cli codex codex found
|
|
67
|
+
PASS runtime.local_cli claude claude auth smoke probe succeeded
|
|
68
|
+
WARN env.tokens codex missing CODEX_AUDIT_LABEL_TOKEN or GITHUB_TOKEN
|
|
69
|
+
WARN github.actions_cost private repo has high-frequency metadata workflows
|
|
70
|
+
PASS cloud.token optional Code Mower Cloud token file is configured
|
|
71
|
+
|
|
72
|
+
Summary: warn, 20 checks, 0 failures, 5 warnings
|
|
73
|
+
Next: fix token warnings, keep paid lanes manual, then generate a value report.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The warnings are the point: Code Mower should make setup, cost, and trust
|
|
77
|
+
boundaries visible before you promote any reviewer lane.
|
|
78
|
+
|
|
79
|
+
See a fuller static transcript: [docs/first-run-transcript.md](docs/first-run-transcript.md).
|
|
80
|
+
|
|
81
|
+
## See The Value Shape First
|
|
82
|
+
|
|
83
|
+
If you want to understand the product before installing anything, start with
|
|
84
|
+
the checked-in demo calibration package:
|
|
85
|
+
|
|
86
|
+
- [examples/demo-calibration/README.md](examples/demo-calibration/README.md)
|
|
87
|
+
- [examples/demo-calibration/reviewer-value-report.md](examples/demo-calibration/reviewer-value-report.md)
|
|
88
|
+
- [docs/first-user-demo-transcript.md](docs/first-user-demo-transcript.md)
|
|
89
|
+
|
|
90
|
+
The example is intentionally tiny and synthetic: one known-clean control, one
|
|
91
|
+
known-blocked control, and three reviewer lanes. It shows the decision Code
|
|
92
|
+
Mower is built to support: which AI reviewers are useful, noisy, expensive,
|
|
93
|
+
fast, or eligible for stronger merge policy on your actual codebase.
|
|
94
|
+
|
|
95
|
+
## Try It First
|
|
96
|
+
|
|
97
|
+
Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
python3.12 --version
|
|
101
|
+
pipx install --python python3.12 "git+https://github.com/codemower-ai/code-mower.git@v0.5.0-beta.5"
|
|
102
|
+
code-mower --version
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
From the repository you want to pilot:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
code-mower init --easy
|
|
109
|
+
code-mower doctor --preflight
|
|
110
|
+
code-mower checks detect --json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
When those look sane, write the generated setup to a reviewable folder and
|
|
114
|
+
produce the starter local report:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
code-mower init --easy --apply --output-dir .code-mower.generated
|
|
118
|
+
code-mower calibration value-report .code-mower.generated/calibration-corpus.json \
|
|
119
|
+
--output .code-mower/reviewer-value-report.md
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The generated starter corpus proves the command path. To bootstrap a draft from
|
|
123
|
+
your repository history:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
code-mower calibration auto-discover \
|
|
127
|
+
--repo OWNER/REPO \
|
|
128
|
+
--last-n 20 \
|
|
129
|
+
--output .code-mower/draft-calibration-corpus.json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Auto-discovery uses recent merged PR metadata, structured audit trailers, and
|
|
133
|
+
review-request signals to propose known-clean and known-blocked cases. Review
|
|
134
|
+
every disposition before using it for lane promotion or merge policy.
|
|
135
|
+
|
|
136
|
+
Full walkthrough: [docs/try-in-10-minutes.md](docs/try-in-10-minutes.md).
|
|
137
|
+
First-time command map: [docs/launch-command-surface.md](docs/launch-command-surface.md).
|
|
138
|
+
|
|
139
|
+
## Why Not Just Run Codex Or Claude Yourself?
|
|
140
|
+
|
|
141
|
+
You should, at first. Code Mower is not a replacement for a good local agent or
|
|
142
|
+
reviewer CLI.
|
|
143
|
+
|
|
144
|
+
Code Mower adds the operating layer around those tools:
|
|
145
|
+
|
|
146
|
+
- consistent reviewer lanes on real pull requests;
|
|
147
|
+
- setup checks for auth, Python, GitHub permissions, private-repo Actions cost,
|
|
148
|
+
provider CLIs, and cloud-token posture;
|
|
149
|
+
- calibration against known-clean and known-blocked PRs instead of vibes;
|
|
150
|
+
- evidence-gated lane promotion: informational, selective, or merge-gating;
|
|
151
|
+
- spend/latency/usefulness reporting across providers and lenses; and
|
|
152
|
+
- privacy boundaries for optional metadata sharing.
|
|
153
|
+
|
|
154
|
+
The goal is to learn which AI builders and reviewers are worth trusting on your
|
|
155
|
+
actual codebase, at what cost, and under which merge policy.
|
|
156
|
+
|
|
157
|
+
## Optional Cloud Sharing
|
|
158
|
+
|
|
159
|
+
Code Mower Cloud currently provides private team dashboards from opt-in
|
|
160
|
+
metadata. Cross-team cohort benchmarking is a roadmap feature that becomes
|
|
161
|
+
valuable only as enough teams contribute sanitized data. The local OSS path
|
|
162
|
+
stays useful without the hosted service.
|
|
163
|
+
|
|
164
|
+
The cloud value loop is:
|
|
165
|
+
|
|
166
|
+
1. run local Code Mower reports;
|
|
167
|
+
2. inspect the metadata-only bundle or dogfood preview;
|
|
168
|
+
3. upload only with `--yes`; and
|
|
169
|
+
4. use [CodeMower.com](https://codemower.com) to see repo rollups, provider/lens
|
|
170
|
+
signal, cost/latency, noisy lanes, and next-lane recommendations over time.
|
|
171
|
+
|
|
172
|
+
Start with a dry run:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
code-mower cloud dogfood --json
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Nothing uploads unless you pass `--yes`.
|
|
179
|
+
|
|
180
|
+
To connect to [CodeMower.com](https://codemower.com), sign in at
|
|
181
|
+
[https://codemower.com/login](https://codemower.com/login), create or receive a
|
|
182
|
+
team token, then run:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
code-mower cloud setup \
|
|
186
|
+
--token-stdin \
|
|
187
|
+
--team-id "YOUR_TEAM_SLUG" \
|
|
188
|
+
--install-id "your-laptop" \
|
|
189
|
+
--out ~/.config/code-mower/tokens/your-laptop.env
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Cloud sharing details, historical catch-up, and repo-sync commands live in
|
|
193
|
+
[docs/cloud-sharing.md](docs/cloud-sharing.md).
|
|
194
|
+
|
|
195
|
+
## Provider Posture
|
|
196
|
+
|
|
197
|
+
The first recommended lanes are local/manual:
|
|
198
|
+
|
|
199
|
+
| Lane | Default role | Merge posture |
|
|
200
|
+
| --- | --- | --- |
|
|
201
|
+
| Codex audit | structured local peer audit | merge-gating eligible after setup |
|
|
202
|
+
| Claude audit | structured local peer audit | merge-gating eligible after setup |
|
|
203
|
+
| Gitar | advisory third signal | informational until calibrated |
|
|
204
|
+
|
|
205
|
+
Everything else starts manual or informational until your own calibration data
|
|
206
|
+
proves it is useful: Antigravity/Gemini, Hermes, CodeRabbit CLI, Cursor BugBot,
|
|
207
|
+
Qodo, Greptile, Devin, local LLMs, and future ACP bridges.
|
|
208
|
+
|
|
209
|
+
Provider details: [docs/provider-matrix.md](docs/provider-matrix.md).
|
|
210
|
+
Setup/auth fixes: [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
211
|
+
|
|
212
|
+
## Road To v1.0
|
|
213
|
+
|
|
214
|
+
The v1.0 bar is not "every provider works." The v1.0 bar is that a fresh senior
|
|
215
|
+
engineer can:
|
|
216
|
+
|
|
217
|
+
1. install Code Mower in a clean repo;
|
|
218
|
+
2. understand the local/cloud trust boundary;
|
|
219
|
+
3. run `init --easy` and `doctor --preflight`;
|
|
220
|
+
4. detect and run the repo's native lint/test/build surface instead of assuming
|
|
221
|
+
every project uses the same tools;
|
|
222
|
+
5. produce a local value report from known PR outcomes;
|
|
223
|
+
6. decide which lanes should stay informational, selective, or merge-gating;
|
|
224
|
+
and
|
|
225
|
+
7. optionally upload sanitized metadata to CodeMower.com and see useful team
|
|
226
|
+
dashboard signal.
|
|
227
|
+
|
|
228
|
+
Future builder/orchestrator experiments extend the same loop from "who reviews
|
|
229
|
+
best?" to "which AI builder plus reviewer loop ships best on this product?" See
|
|
230
|
+
[docs/builder-experiments.md](docs/builder-experiments.md) and
|
|
231
|
+
[docs/authoring-intelligence.md](docs/authoring-intelligence.md).
|
|
232
|
+
|
|
233
|
+
## Installation Status
|
|
234
|
+
|
|
235
|
+
The current public beta is `v0.5.0-beta.5` from
|
|
236
|
+
[codemower-ai/code-mower](https://github.com/codemower-ai/code-mower). PyPI
|
|
237
|
+
distribution builds now run from GitHub Releases. Publishing to PyPI remains
|
|
238
|
+
off by default until trusted publishing is configured, so use the tagged GitHub
|
|
239
|
+
install command above for this beta.
|
|
240
|
+
|
|
241
|
+
For source checkout development and release rehearsal, use:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
scripts/dev-python
|
|
245
|
+
scripts/dev-python -m venv .venv
|
|
246
|
+
.venv/bin/python -m pip install -e . ruff
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The wrapper resolves Python 3.12+ and refuses stale or old system Python shims.
|
|
250
|
+
|
|
251
|
+
## Known Limitations
|
|
252
|
+
|
|
253
|
+
- PyPI distribution builds exist, but publishing is gated until trusted
|
|
254
|
+
publishing is configured; use the tagged GitHub install command. See
|
|
255
|
+
[docs/pypi-release.md](docs/pypi-release.md) for the TestPyPI/PyPI
|
|
256
|
+
activation path.
|
|
257
|
+
- GitHub is the primary supported forge. GitLab, Bitbucket, and ACP bridges are
|
|
258
|
+
roadmap items.
|
|
259
|
+
- Hosted/SaaS reviewers start informational or manual until calibration data
|
|
260
|
+
supports promotion.
|
|
261
|
+
- `calibration auto-discover` is a bootstrap tool, not an adjudicator. It
|
|
262
|
+
proposes a draft corpus from PR history; humans still confirm the ground truth.
|
|
263
|
+
- CodeMower.com currently provides private team dashboards; cohort benchmarks
|
|
264
|
+
are roadmap work and should not be treated as live product value yet.
|
|
265
|
+
- Self-service cloud data deletion/export basics are live. Retention remains
|
|
266
|
+
conservative and team-controlled while automated retention jobs are roadmap
|
|
267
|
+
work.
|
|
268
|
+
- Advanced/provider/operator commands remain available behind
|
|
269
|
+
`code-mower --help-all`. The default help path stays focused on `init`,
|
|
270
|
+
`doctor`, calibration, value reports, and optional cloud export/upload.
|
|
271
|
+
|
|
272
|
+
## Docs Map
|
|
273
|
+
|
|
274
|
+
- [Try Code Mower In 10 Minutes](docs/try-in-10-minutes.md)
|
|
275
|
+
- [Quickstart](docs/quickstart.md)
|
|
276
|
+
- [First Run Transcript](docs/first-run-transcript.md)
|
|
277
|
+
- [First-User Demo Transcript](docs/first-user-demo-transcript.md)
|
|
278
|
+
- [First-User Install Rehearsal](docs/first-user-install-rehearsal.md)
|
|
279
|
+
- [Launch Command Surface](docs/launch-command-surface.md)
|
|
280
|
+
- [Demo Calibration Example](examples/demo-calibration/README.md)
|
|
281
|
+
- [PyPI Release Runbook](docs/pypi-release.md)
|
|
282
|
+
- [Sample Doctor Output](docs/sample-doctor-output.md)
|
|
283
|
+
- [Architecture](docs/architecture.md)
|
|
284
|
+
- [Provider Matrix](docs/provider-matrix.md)
|
|
285
|
+
- [GitHub Setup](docs/github-setup.md)
|
|
286
|
+
- [Cloud Sharing](docs/cloud-sharing.md)
|
|
287
|
+
- [Cloud Data Contract](docs/cloud-data-contract.md)
|
|
288
|
+
- [Privacy And Threat Model](docs/privacy-threat-model.md)
|
|
289
|
+
- [Current State And Roadmap](docs/current-state-and-roadmap.md)
|
|
290
|
+
- [Public Release Checklist](docs/public-release-checklist.md)
|
|
291
|
+
- [Changelog](CHANGELOG.md)
|
|
292
|
+
- [Contributing](CONTRIBUTING.md)
|
|
293
|
+
- [Support](SUPPORT.md)
|
|
294
|
+
- [Security Policy](SECURITY.md)
|
|
295
|
+
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
296
|
+
|
|
297
|
+
## License
|
|
298
|
+
|
|
299
|
+
The Code Mower open-source core is licensed under Apache-2.0. Hosted
|
|
300
|
+
benchmarking and reporting, managed integrations, private telemetry and
|
|
301
|
+
benchmark data products, enterprise controls, and support are commercial
|
|
302
|
+
surfaces unless licensed otherwise.
|