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
code_mower/next_steps.py
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render the next Code Mower actions after first-run setup."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
import shlex
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Mapping
|
|
13
|
+
|
|
14
|
+
if __package__ in {None, ""}:
|
|
15
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
16
|
+
|
|
17
|
+
if __package__ in {None, "", "tools"}:
|
|
18
|
+
from code_mower import __version__ as CODE_MOWER_VERSION
|
|
19
|
+
from tools import code_mower_package
|
|
20
|
+
from tools.code_mower_config import ConfigError
|
|
21
|
+
try:
|
|
22
|
+
from tools import code_mower_versioning
|
|
23
|
+
except ImportError: # pragma: no cover - package-installed fallback.
|
|
24
|
+
from code_mower import versioning as code_mower_versioning
|
|
25
|
+
else: # pragma: no cover - exercised after package extraction.
|
|
26
|
+
from . import __version__ as CODE_MOWER_VERSION
|
|
27
|
+
from . import package as code_mower_package
|
|
28
|
+
from .config import ConfigError
|
|
29
|
+
from . import versioning as code_mower_versioning
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
DEFAULT_PROVIDER_TEMPLATES = code_mower_package.DEFAULT_PROVIDER_TEMPLATES
|
|
33
|
+
DEFAULT_PROFILE = "recommended"
|
|
34
|
+
PUBLIC_REPO_URL = code_mower_versioning.PUBLIC_REPO_URL
|
|
35
|
+
GITHUB_REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$")
|
|
36
|
+
PR_NUMBER_RE = re.compile(r"^[1-9][0-9]*$")
|
|
37
|
+
REQUEST_LABEL_RE = re.compile(r"^needs-[A-Za-z0-9][A-Za-z0-9_-]*-(audit|review)$")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _as_mapping(value: Any, name: str) -> Mapping[str, Any]:
|
|
41
|
+
if isinstance(value, Mapping):
|
|
42
|
+
return value
|
|
43
|
+
raise ConfigError(f"{name} must be a mapping")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _profile_lanes(templates: Mapping[str, Any], profile_id: str) -> tuple[str, ...]:
|
|
47
|
+
profiles = _as_mapping(templates.get("profiles"), "profiles")
|
|
48
|
+
profile = profiles.get(profile_id)
|
|
49
|
+
if not isinstance(profile, Mapping):
|
|
50
|
+
available = ", ".join(sorted(str(key) for key in profiles)) or "none"
|
|
51
|
+
raise ConfigError(f"unknown profile {profile_id!r}; available profiles: {available}")
|
|
52
|
+
lanes = profile.get("lanes", [])
|
|
53
|
+
if not isinstance(lanes, list):
|
|
54
|
+
raise ConfigError(f"profile {profile_id!r} lanes must be a list")
|
|
55
|
+
return tuple(str(lane) for lane in lanes)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _lane_catalog(templates: Mapping[str, Any]) -> Mapping[str, Mapping[str, Any]]:
|
|
59
|
+
catalog = _as_mapping(templates.get("provider_templates"), "provider_templates")
|
|
60
|
+
result: dict[str, Mapping[str, Any]] = {}
|
|
61
|
+
for lane_id, lane in catalog.items():
|
|
62
|
+
if isinstance(lane, Mapping):
|
|
63
|
+
result[str(lane_id)] = lane
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _lane_classes(
|
|
68
|
+
lanes: tuple[str, ...],
|
|
69
|
+
catalog: Mapping[str, Mapping[str, Any]],
|
|
70
|
+
) -> dict[str, tuple[str, ...]]:
|
|
71
|
+
merge_gating: list[str] = []
|
|
72
|
+
informational: list[str] = []
|
|
73
|
+
paid_or_manual: list[str] = []
|
|
74
|
+
local_or_cli: list[str] = []
|
|
75
|
+
for lane_id in lanes:
|
|
76
|
+
lane = catalog.get(lane_id, {})
|
|
77
|
+
if lane.get("merge_authority"):
|
|
78
|
+
merge_gating.append(lane_id)
|
|
79
|
+
if lane.get("informational"):
|
|
80
|
+
informational.append(lane_id)
|
|
81
|
+
if lane.get("spend_policy") == "paid" or lane.get("trigger_policy") == "manual":
|
|
82
|
+
paid_or_manual.append(lane_id)
|
|
83
|
+
if lane.get("driver") in {"local_cli", "api_model"}:
|
|
84
|
+
local_or_cli.append(lane_id)
|
|
85
|
+
return {
|
|
86
|
+
"merge_gating": tuple(merge_gating),
|
|
87
|
+
"informational": tuple(informational),
|
|
88
|
+
"paid_or_manual": tuple(paid_or_manual),
|
|
89
|
+
"local_or_cli": tuple(local_or_cli),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _request_label(lane_id: str, lane: Mapping[str, Any]) -> str:
|
|
94
|
+
lane_type = str(lane.get("type") or "audit")
|
|
95
|
+
stem = lane_id
|
|
96
|
+
if stem.endswith("_audit"):
|
|
97
|
+
stem = stem[: -len("_audit")]
|
|
98
|
+
if stem.endswith("_review"):
|
|
99
|
+
stem = stem[: -len("_review")]
|
|
100
|
+
suffix = "review" if lane_type == "review" else "audit"
|
|
101
|
+
label = f"needs-{stem.replace('_', '-')}-{suffix}"
|
|
102
|
+
if not REQUEST_LABEL_RE.fullmatch(label):
|
|
103
|
+
raise ConfigError(f"generated request label is not shell-safe: {label!r}")
|
|
104
|
+
return label
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _validate_repo_and_pr(repo: str, pr: str) -> None:
|
|
108
|
+
if not GITHUB_REPO_RE.fullmatch(repo):
|
|
109
|
+
raise ConfigError(
|
|
110
|
+
"repo must be a GitHub OWNER/REPO slug containing only letters, "
|
|
111
|
+
"numbers, '.', '_', or '-'"
|
|
112
|
+
)
|
|
113
|
+
if not PR_NUMBER_RE.fullmatch(pr):
|
|
114
|
+
raise ConfigError("pr must be a positive numeric pull request number")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def current_public_tag(version: str = CODE_MOWER_VERSION) -> str:
|
|
118
|
+
"""Return the GitHub release tag for the current package version."""
|
|
119
|
+
|
|
120
|
+
return code_mower_versioning.release_tag_for_version(version)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def current_alpha_package_spec(version: str = CODE_MOWER_VERSION) -> str:
|
|
124
|
+
"""Return the documented installable package spec for prerelease users."""
|
|
125
|
+
|
|
126
|
+
return code_mower_versioning.public_package_spec(version, repo_url=PUBLIC_REPO_URL)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def build_next_steps(
|
|
130
|
+
templates: Mapping[str, Any],
|
|
131
|
+
*,
|
|
132
|
+
profile: str = DEFAULT_PROFILE,
|
|
133
|
+
repo: str = "owner/repo",
|
|
134
|
+
pr: str = "123",
|
|
135
|
+
) -> Mapping[str, Any]:
|
|
136
|
+
_validate_repo_and_pr(repo, pr)
|
|
137
|
+
quoted_repo = shlex.quote(repo)
|
|
138
|
+
lanes = _profile_lanes(templates, profile)
|
|
139
|
+
catalog = _lane_catalog(templates)
|
|
140
|
+
classes = _lane_classes(lanes, catalog)
|
|
141
|
+
audit_lanes = classes["merge_gating"] or lanes[:1]
|
|
142
|
+
first_audit_lane = audit_lanes[0] if audit_lanes else "codex"
|
|
143
|
+
first_audit_label = _request_label(first_audit_lane, catalog.get(first_audit_lane, {}))
|
|
144
|
+
alpha_package_spec = shlex.quote(current_alpha_package_spec())
|
|
145
|
+
quoted_profile = shlex.quote(profile)
|
|
146
|
+
if profile == DEFAULT_PROFILE:
|
|
147
|
+
init_plan_command = "code-mower init --easy"
|
|
148
|
+
init_apply_command = "code-mower init --easy --apply --output-dir .code-mower.generated"
|
|
149
|
+
doctor_command = "code-mower doctor --v05 --json"
|
|
150
|
+
else:
|
|
151
|
+
init_plan_command = f"code-mower init --profile {quoted_profile} --dry-run"
|
|
152
|
+
init_apply_command = (
|
|
153
|
+
"code-mower init "
|
|
154
|
+
f"--profile {quoted_profile} --apply --output-dir .code-mower.generated"
|
|
155
|
+
)
|
|
156
|
+
doctor_command = (
|
|
157
|
+
f"code-mower doctor --easy --profile {quoted_profile} "
|
|
158
|
+
"--probe-runtime --json"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
steps: list[dict[str, Any]] = [
|
|
162
|
+
{
|
|
163
|
+
"id": "render-easy-config",
|
|
164
|
+
"title": "Render the easy-mode starter plan",
|
|
165
|
+
"command": init_plan_command,
|
|
166
|
+
"why": (
|
|
167
|
+
"Shows labels, secrets, workflows, and smoke tests without mutating "
|
|
168
|
+
"the repository."
|
|
169
|
+
),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"id": "write-reviewable-config",
|
|
173
|
+
"title": "Write generated setup files to a reviewable directory",
|
|
174
|
+
"command": init_apply_command,
|
|
175
|
+
"why": "Creates files the user can inspect before installing workflows.",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": "doctor-easy",
|
|
179
|
+
"title": "Verify first-run readiness",
|
|
180
|
+
"command": doctor_command,
|
|
181
|
+
"why": (
|
|
182
|
+
"Checks GitHub auth, Python, provider catalog coverage, CLI "
|
|
183
|
+
"availability, token env, optional cloud-token setup, Actions "
|
|
184
|
+
"cost traps, and harmless runtime probes."
|
|
185
|
+
),
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"id": "wrapper-rehearsal",
|
|
189
|
+
"title": "Compare standalone package behavior with any repo-local Code Mower tools",
|
|
190
|
+
"command": "code-mower migration wrapper-rehearsal --repo-path . --json",
|
|
191
|
+
"why": (
|
|
192
|
+
"Gives existing product repos a shadow-mode migration check before "
|
|
193
|
+
"they replace mirrored tool files with a pinned standalone package."
|
|
194
|
+
),
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"id": "package-install-rehearsal",
|
|
198
|
+
"title": "Prove the package-installed path in a fresh toy repo",
|
|
199
|
+
"command": (
|
|
200
|
+
"code-mower migration package-install-rehearsal "
|
|
201
|
+
f"--package-spec {alpha_package_spec} --json"
|
|
202
|
+
),
|
|
203
|
+
"why": (
|
|
204
|
+
"Installs Code Mower into a clean venv, verifies the easy-mode "
|
|
205
|
+
"starter path in a fresh toy repo, writes the first value-report "
|
|
206
|
+
"and cloud dry-run artifacts, emits `first_user_readiness`, and "
|
|
207
|
+
"optionally checks a real repo when --repo-path is provided. "
|
|
208
|
+
"Wrapper-bearing repos get parity checks; fresh external repos "
|
|
209
|
+
"get installed-CLI readiness checks. The tagged release path uses the "
|
|
210
|
+
"current GitHub tag until PyPI publishing is promoted."
|
|
211
|
+
),
|
|
212
|
+
"artifacts": [
|
|
213
|
+
"outputs/package-install-rehearsal.json",
|
|
214
|
+
"outputs/first-user-readiness.json",
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "first-audit",
|
|
219
|
+
"title": "Run the first head-bound audit on a real PR",
|
|
220
|
+
"command": f"gh pr edit {pr} --repo {repo} --add-label {first_audit_label}",
|
|
221
|
+
"why": "Gets a structured reviewer result against the user's actual codebase.",
|
|
222
|
+
"lanes": list(audit_lanes),
|
|
223
|
+
"label": first_audit_label,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"id": "calibration-run",
|
|
227
|
+
"title": "Run a starter calibration corpus",
|
|
228
|
+
"command": (
|
|
229
|
+
"code-mower calibration run .code-mower.generated/calibration-corpus.json "
|
|
230
|
+
"--lanes antigravity-cli --results-dir .code-mower/calibration-results --json"
|
|
231
|
+
),
|
|
232
|
+
"why": (
|
|
233
|
+
"Persists raw reviewer results for quality, latency, and cost analysis. "
|
|
234
|
+
"Antigravity is the forward Google CLI lane; use gemini-cli only for "
|
|
235
|
+
"legacy/API-key compatibility or historical comparisons."
|
|
236
|
+
),
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "calibration-auto-discover",
|
|
240
|
+
"title": "Bootstrap a project-specific draft corpus from recent PRs",
|
|
241
|
+
"command": (
|
|
242
|
+
f"code-mower calibration auto-discover --repo {repo} --last-n 20 "
|
|
243
|
+
"--output .code-mower/draft-calibration-corpus.json"
|
|
244
|
+
),
|
|
245
|
+
"why": (
|
|
246
|
+
"Uses recent merged PR metadata and structured audit trailers to "
|
|
247
|
+
"create a reviewable starter corpus for your actual codebase. "
|
|
248
|
+
"Confirm every disposition before using it for lane promotion."
|
|
249
|
+
),
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"id": "value-report",
|
|
253
|
+
"title": "Generate the first reviewer value report",
|
|
254
|
+
"command": (
|
|
255
|
+
"code-mower calibration value-report .code-mower.generated/calibration-corpus.json "
|
|
256
|
+
"--runs .code-mower/calibration-results/calibration-run-results.json "
|
|
257
|
+
"--spend .code-mower.generated/reviewer-spend.json "
|
|
258
|
+
"--output reviewer-value-report.md"
|
|
259
|
+
),
|
|
260
|
+
"why": "Converts reviewer outcomes into useful-rate, precision, latency, and spend evidence.",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"id": "calibration-evidence",
|
|
264
|
+
"title": "Write adjudicated calibration evidence for metrics",
|
|
265
|
+
"command": (
|
|
266
|
+
"code-mower calibration evidence .code-mower.generated/calibration-corpus.json "
|
|
267
|
+
"--json > calibration-evidence.json"
|
|
268
|
+
),
|
|
269
|
+
"why": "Creates the reviewer evidence input consumed by metrics and lane policy.",
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"id": "lane-policy",
|
|
273
|
+
"title": "Decide informational, selective, and merge-gating lanes from evidence",
|
|
274
|
+
"command": (
|
|
275
|
+
"code-mower reviewer-metrics calibration-evidence.json "
|
|
276
|
+
"--spend .code-mower.generated/reviewer-spend.json "
|
|
277
|
+
"--json > reviewer-metrics.json && "
|
|
278
|
+
"code-mower calibration policy reviewer-metrics.json --json > lane-policy.json"
|
|
279
|
+
),
|
|
280
|
+
"why": "Keeps new reviewers informational until measured on the real codebase.",
|
|
281
|
+
"informational_lanes": list(classes["informational"]),
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"id": "context-packs",
|
|
285
|
+
"title": "Add selective context packs for repeated review blind spots",
|
|
286
|
+
"command": "code-mower context-packs .code-mower.generated/context-packs.json --json",
|
|
287
|
+
"why": "Lets lanes request important surrounding files without bloating every audit.",
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "cloud-export",
|
|
291
|
+
"title": "Export a local benchmark bundle when ready to share aggregate data",
|
|
292
|
+
"command": (
|
|
293
|
+
"code-mower cloud export "
|
|
294
|
+
"--report reviewer-metrics=reviewer-metrics.json "
|
|
295
|
+
"--report lane-policy=lane-policy.json "
|
|
296
|
+
"--report value-report=reviewer-value-report.md "
|
|
297
|
+
"--output-dir .code-mower/cloud-benchmark-bundle --json"
|
|
298
|
+
),
|
|
299
|
+
"why": "Produces a local, source-free bundle for future opt-in benchmarking/reporting.",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"id": "cloud-setup",
|
|
303
|
+
"title": "Store a team ingest token locally when opting into cloud sharing",
|
|
304
|
+
"command": (
|
|
305
|
+
"code-mower cloud setup --token-stdin "
|
|
306
|
+
"--team-id YOUR_TEAM_SLUG "
|
|
307
|
+
"--install-id YOUR_INSTALL_ID "
|
|
308
|
+
"--out ~/.config/code-mower/tokens/YOUR_INSTALL_ID.env"
|
|
309
|
+
),
|
|
310
|
+
"why": (
|
|
311
|
+
"Writes a private 0600 token file and avoids echoing the full "
|
|
312
|
+
"token in logs."
|
|
313
|
+
),
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"id": "cloud-upload-dry-run",
|
|
317
|
+
"title": "Preview cloud sharing without sending data",
|
|
318
|
+
"command": (
|
|
319
|
+
"code-mower cloud upload .code-mower/cloud-benchmark-bundle "
|
|
320
|
+
"--dry-run --json"
|
|
321
|
+
),
|
|
322
|
+
"why": (
|
|
323
|
+
"Shows the exact metadata-only upload shape before any network "
|
|
324
|
+
"transfer."
|
|
325
|
+
),
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"id": "cloud-dogfood-dry-run",
|
|
329
|
+
"title": "Preview the routine dogfood upload path",
|
|
330
|
+
"command": (
|
|
331
|
+
"source ~/.config/code-mower/tokens/YOUR_INSTALL_ID.env && "
|
|
332
|
+
"code-mower cloud dogfood --json"
|
|
333
|
+
),
|
|
334
|
+
"why": (
|
|
335
|
+
"Uses the same token file and auto-detected repo metadata that "
|
|
336
|
+
"a regular team upload will use, while still avoiding network "
|
|
337
|
+
"transfer."
|
|
338
|
+
),
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
"id": "cloud-dogfood-upload",
|
|
342
|
+
"title": "Send routine dogfood metadata after the preview is clean",
|
|
343
|
+
"command": (
|
|
344
|
+
"source ~/.config/code-mower/tokens/YOUR_INSTALL_ID.env && "
|
|
345
|
+
"code-mower cloud dogfood --yes --json"
|
|
346
|
+
),
|
|
347
|
+
"why": (
|
|
348
|
+
"Uploads sanitized metadata and a dogfood event so CodeMower.com "
|
|
349
|
+
"can show repo, provider/lens, cost, latency, and recommendation "
|
|
350
|
+
"rows over time."
|
|
351
|
+
),
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"id": "cloud-catch-up-dry-run",
|
|
355
|
+
"title": "Preview recent GitHub Actions catch-up metadata",
|
|
356
|
+
"command": (
|
|
357
|
+
"source ~/.config/code-mower/tokens/YOUR_INSTALL_ID.env && "
|
|
358
|
+
f"code-mower cloud catch-up --repo-slug {quoted_repo} --limit 50 --json"
|
|
359
|
+
),
|
|
360
|
+
"why": (
|
|
361
|
+
"Backfills recent workflow_run events from GitHub Actions while "
|
|
362
|
+
"remaining dry-run by default. Branch names and commit SHAs stay "
|
|
363
|
+
"excluded unless --include-git-ref is explicit."
|
|
364
|
+
),
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"id": "cloud-catch-up-upload",
|
|
368
|
+
"title": "Upload catch-up metadata after reviewing the dry run",
|
|
369
|
+
"command": (
|
|
370
|
+
"source ~/.config/code-mower/tokens/YOUR_INSTALL_ID.env && "
|
|
371
|
+
f"code-mower cloud catch-up --repo-slug {quoted_repo} --limit 50 --yes --json"
|
|
372
|
+
),
|
|
373
|
+
"why": (
|
|
374
|
+
"Helps a new dashboard become useful quickly by adding sanitized "
|
|
375
|
+
"historical workflow metadata without uploading source, diffs, "
|
|
376
|
+
"transcripts, branch names, or commit SHAs by default."
|
|
377
|
+
),
|
|
378
|
+
},
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
"schema": "code_mower.nextSteps.v1",
|
|
383
|
+
"profile": profile,
|
|
384
|
+
"lanes": list(lanes),
|
|
385
|
+
"lane_classes": {key: list(value) for key, value in classes.items()},
|
|
386
|
+
"steps": steps,
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def render_next_steps_text(payload: Mapping[str, Any]) -> str:
|
|
391
|
+
lines = [
|
|
392
|
+
f"Code Mower next steps ({payload['profile']})",
|
|
393
|
+
"",
|
|
394
|
+
"Lanes: " + ", ".join(payload.get("lanes", [])),
|
|
395
|
+
"",
|
|
396
|
+
]
|
|
397
|
+
for index, step in enumerate(payload.get("steps", []), start=1):
|
|
398
|
+
lines.append(f"{index}. {step['title']}")
|
|
399
|
+
lines.append(f" command: {step['command']}")
|
|
400
|
+
lines.append(f" why: {step['why']}")
|
|
401
|
+
return "\n".join(lines) + "\n"
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _resolve_provider_templates(path_text: str | None) -> Path:
|
|
405
|
+
if path_text is None:
|
|
406
|
+
return code_mower_package.resolve_provider_templates_path(DEFAULT_PROVIDER_TEMPLATES)
|
|
407
|
+
return Path(path_text)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def main(argv: list[str] | None = None) -> int:
|
|
411
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
412
|
+
parser.add_argument("--profile", default=DEFAULT_PROFILE)
|
|
413
|
+
parser.add_argument("--repo", default="owner/repo")
|
|
414
|
+
parser.add_argument("--pr", default="123")
|
|
415
|
+
parser.add_argument("--provider-templates", default=None)
|
|
416
|
+
parser.add_argument("--json", action="store_true")
|
|
417
|
+
args = parser.parse_args(argv)
|
|
418
|
+
|
|
419
|
+
try:
|
|
420
|
+
templates = code_mower_package.load_provider_templates(
|
|
421
|
+
_resolve_provider_templates(args.provider_templates)
|
|
422
|
+
)
|
|
423
|
+
payload = build_next_steps(
|
|
424
|
+
templates,
|
|
425
|
+
profile=args.profile,
|
|
426
|
+
repo=args.repo,
|
|
427
|
+
pr=args.pr,
|
|
428
|
+
)
|
|
429
|
+
except ConfigError as exc:
|
|
430
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
431
|
+
return 1
|
|
432
|
+
|
|
433
|
+
if args.json:
|
|
434
|
+
print(json.dumps(payload, indent=2, sort_keys=True))
|
|
435
|
+
else:
|
|
436
|
+
print(render_next_steps_text(payload), end="")
|
|
437
|
+
return 0
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
if __name__ == "__main__":
|
|
441
|
+
raise SystemExit(main())
|