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.
Files changed (185) hide show
  1. code_mower/__init__.py +3 -0
  2. code_mower/adapters/__init__.py +39 -0
  3. code_mower/adapters/_base.py +148 -0
  4. code_mower/adapters/cursor_bugbot.py +97 -0
  5. code_mower/adapters/gitar.py +111 -0
  6. code_mower/adapters/greptile.py +191 -0
  7. code_mower/adapters/qodo.py +140 -0
  8. code_mower/antigravity_cli_audit_pr.py +241 -0
  9. code_mower/audit_handoff_log.py +345 -0
  10. code_mower/audit_labeler_lib.py +311 -0
  11. code_mower/audit_progress.py +203 -0
  12. code_mower/blind_review_artifacts.py +562 -0
  13. code_mower/blind_review_coordinator.py +276 -0
  14. code_mower/bootstrap.py +524 -0
  15. code_mower/builder_experiment.py +539 -0
  16. code_mower/calibration/__init__.py +181 -0
  17. code_mower/calibration/arms.py +267 -0
  18. code_mower/calibration/auto_discovery.py +348 -0
  19. code_mower/calibration/commands.py +192 -0
  20. code_mower/calibration/context_inputs.py +199 -0
  21. code_mower/calibration/corpus.py +91 -0
  22. code_mower/calibration/evidence.py +20 -0
  23. code_mower/calibration/evidence_report.py +360 -0
  24. code_mower/calibration/identity.py +27 -0
  25. code_mower/calibration/metrics.py +18 -0
  26. code_mower/calibration/overlap.py +103 -0
  27. code_mower/calibration/planning.py +309 -0
  28. code_mower/calibration/policy.py +207 -0
  29. code_mower/calibration/results.py +315 -0
  30. code_mower/calibration/run_results.py +147 -0
  31. code_mower/calibration/run_status.py +64 -0
  32. code_mower/calibration/runner.py +360 -0
  33. code_mower/calibration/truth.py +188 -0
  34. code_mower/calibration/value_report.py +142 -0
  35. code_mower/checks.py +402 -0
  36. code_mower/claude_audit_pr.py +1126 -0
  37. code_mower/claude_cli_bounce.py +303 -0
  38. code_mower/claude_cli_environment.py +73 -0
  39. code_mower/clear_stale.py +374 -0
  40. code_mower/cli.py +537 -0
  41. code_mower/cloud.py +674 -0
  42. code_mower/cloud_client/__init__.py +173 -0
  43. code_mower/cloud_client/bundle.py +155 -0
  44. code_mower/cloud_client/doctor.py +206 -0
  45. code_mower/cloud_client/dogfood.py +78 -0
  46. code_mower/cloud_client/endpoints.py +114 -0
  47. code_mower/cloud_client/errors.py +7 -0
  48. code_mower/cloud_client/events.py +278 -0
  49. code_mower/cloud_client/export.py +272 -0
  50. code_mower/cloud_client/git_metadata.py +46 -0
  51. code_mower/cloud_client/manifest.py +39 -0
  52. code_mower/cloud_client/operations.py +448 -0
  53. code_mower/cloud_client/reports.py +45 -0
  54. code_mower/cloud_client/setup.py +205 -0
  55. code_mower/cloud_client/upload.py +97 -0
  56. code_mower/code_mower_calibration.py +598 -0
  57. code_mower/code_mower_context_packs.py +591 -0
  58. code_mower/code_mower_merge.py +227 -0
  59. code_mower/code_mower_telemetry.py +561 -0
  60. code_mower/coderabbit_cli_audit_pr.py +526 -0
  61. code_mower/codex_audit_env_preflight.py +220 -0
  62. code_mower/codex_audit_pr.py +1738 -0
  63. code_mower/codex_audit_schema_smoke.py +160 -0
  64. code_mower/codex_audit_verdict.schema.json +44 -0
  65. code_mower/config.py +655 -0
  66. code_mower/doctor.py +161 -0
  67. code_mower/doctor_checks/__init__.py +104 -0
  68. code_mower/doctor_checks/cloud.py +129 -0
  69. code_mower/doctor_checks/common.py +215 -0
  70. code_mower/doctor_checks/github.py +128 -0
  71. code_mower/doctor_checks/github_actions.py +11 -0
  72. code_mower/doctor_checks/github_actions_cost.py +99 -0
  73. code_mower/doctor_checks/github_actions_cost_summary.py +111 -0
  74. code_mower/doctor_checks/github_actions_failure_annotations.py +27 -0
  75. code_mower/doctor_checks/github_actions_failure_models.py +47 -0
  76. code_mower/doctor_checks/github_actions_failure_scan.py +200 -0
  77. code_mower/doctor_checks/github_actions_failure_selection.py +63 -0
  78. code_mower/doctor_checks/github_actions_failures.py +103 -0
  79. code_mower/doctor_checks/github_actions_permissions.py +55 -0
  80. code_mower/doctor_checks/github_api.py +79 -0
  81. code_mower/doctor_checks/github_branch.py +56 -0
  82. code_mower/doctor_checks/github_config.py +25 -0
  83. code_mower/doctor_checks/github_provider.py +61 -0
  84. code_mower/doctor_checks/github_repo.py +120 -0
  85. code_mower/doctor_checks/groups.py +36 -0
  86. code_mower/doctor_checks/models.py +96 -0
  87. code_mower/doctor_checks/output.py +86 -0
  88. code_mower/doctor_checks/presets.py +64 -0
  89. code_mower/doctor_checks/privacy.py +20 -0
  90. code_mower/doctor_checks/provider_api_model.py +138 -0
  91. code_mower/doctor_checks/provider_api_model_openai.py +29 -0
  92. code_mower/doctor_checks/provider_api_model_profiles.py +137 -0
  93. code_mower/doctor_checks/provider_env.py +113 -0
  94. code_mower/doctor_checks/provider_env_required.py +56 -0
  95. code_mower/doctor_checks/provider_env_tokens.py +100 -0
  96. code_mower/doctor_checks/provider_local_cli.py +162 -0
  97. code_mower/doctor_checks/provider_local_cli_commands.py +47 -0
  98. code_mower/doctor_checks/provider_local_cli_probe_config.py +70 -0
  99. code_mower/doctor_checks/provider_probe.py +20 -0
  100. code_mower/doctor_checks/provider_probe_auth.py +52 -0
  101. code_mower/doctor_checks/provider_probe_evaluation.py +109 -0
  102. code_mower/doctor_checks/provider_probe_json.py +45 -0
  103. code_mower/doctor_checks/provider_probe_remediation.py +39 -0
  104. code_mower/doctor_checks/providers.py +159 -0
  105. code_mower/doctor_checks/registry.py +69 -0
  106. code_mower/doctor_checks/runner.py +188 -0
  107. code_mower/doctor_checks/runtime.py +89 -0
  108. code_mower/doctor_checks/runtime_github_auth.py +148 -0
  109. code_mower/gemini_cli_audit_pr.py +897 -0
  110. code_mower/hermes_cli_audit_pr.py +436 -0
  111. code_mower/init.py +888 -0
  112. code_mower/lane_configs/__init__.py +37 -0
  113. code_mower/lane_configs/aider.py +32 -0
  114. code_mower/lane_configs/antigravity_cli.py +35 -0
  115. code_mower/lane_configs/claude.py +35 -0
  116. code_mower/lane_configs/codex.py +32 -0
  117. code_mower/lane_configs/devin.py +33 -0
  118. code_mower/lane_configs/gemini_cli.py +35 -0
  119. code_mower/lane_configs/hermes_cli.py +35 -0
  120. code_mower/lane_configs/local_llm.py +31 -0
  121. code_mower/local_llm_audit_pr.py +1364 -0
  122. code_mower/local_llm_bakeoff.py +458 -0
  123. code_mower/local_llm_calibration.py +441 -0
  124. code_mower/local_llm_profiles.py +66 -0
  125. code_mower/migration.py +508 -0
  126. code_mower/migration_install.py +292 -0
  127. code_mower/migration_mirror.py +392 -0
  128. code_mower/migration_readiness.py +237 -0
  129. code_mower/migration_rehearsal.py +718 -0
  130. code_mower/next_steps.py +441 -0
  131. code_mower/package.py +673 -0
  132. code_mower/package_content.py +444 -0
  133. code_mower/package_manifest.py +452 -0
  134. code_mower/package_paths.py +53 -0
  135. code_mower/package_rendering.py +90 -0
  136. code_mower/package_static.py +585 -0
  137. code_mower/prompts.py +267 -0
  138. code_mower/provider_registry.py +469 -0
  139. code_mower/provider_runners/__init__.py +60 -0
  140. code_mower/provider_runners/comments.py +31 -0
  141. code_mower/provider_runners/git.py +46 -0
  142. code_mower/provider_runners/github_auth.py +61 -0
  143. code_mower/provider_runners/github_pr.py +120 -0
  144. code_mower/provider_runners/process.py +58 -0
  145. code_mower/provider_runners/repo_paths.py +23 -0
  146. code_mower/provider_runners/text_schema.py +41 -0
  147. code_mower/provider_runners/verdict_artifacts.py +103 -0
  148. code_mower/provider_runners/workspace.py +57 -0
  149. code_mower/release_readiness.py +549 -0
  150. code_mower/reviewer_metrics.py +389 -0
  151. code_mower/saas_reviewer_labeler.py +809 -0
  152. code_mower/secrets.py +89 -0
  153. code_mower/templates/builder-experiment.example.json +55 -0
  154. code_mower/templates/calibration-corpus.example.json +129 -0
  155. code_mower/templates/calibration-corpus.json +129 -0
  156. code_mower/templates/code-mower.example.yml +423 -0
  157. code_mower/templates/context-packs.example.json +150 -0
  158. code_mower/templates/lane_prompts/base-audit.md +22 -0
  159. code_mower/templates/lane_prompts/calibration-policy.md +21 -0
  160. code_mower/templates/lane_prompts/context-driven-quality.md +21 -0
  161. code_mower/templates/lane_prompts/docs-design.md +12 -0
  162. code_mower/templates/lane_prompts/generic-programming.md +21 -0
  163. code_mower/templates/lane_prompts/operability.md +22 -0
  164. code_mower/templates/lane_prompts/package-runtime.md +12 -0
  165. code_mower/templates/lane_prompts/security-threat-model.md +22 -0
  166. code_mower/templates/product-support/code_mower +216 -0
  167. code_mower/templates/product-support/code_mower_standalone_pin.env +7 -0
  168. code_mower/templates/product-support/code_mower_standalone_shadow.sh +151 -0
  169. code_mower/templates/product-support/run_claude_audit_pr.sh +32 -0
  170. code_mower/templates/product-support/run_codex_audit_pr.sh +32 -0
  171. code_mower/templates/product-support/safe_gh_comment.py +96 -0
  172. code_mower/templates/providers.yml +454 -0
  173. code_mower/templates/reviewer-spend.example.json +28 -0
  174. code_mower/templates/reviewer-value-report.example.md +20 -0
  175. code_mower/templates/workflows/private-standalone-shadow.yml.j2 +106 -0
  176. code_mower/templates/workflows/review-clear-stale.yml.j2 +83 -0
  177. code_mower/trailer_comment_labeler.py +207 -0
  178. code_mower/versioning.py +32 -0
  179. code_mower-0.5.0b5.dist-info/METADATA +302 -0
  180. code_mower-0.5.0b5.dist-info/RECORD +185 -0
  181. code_mower-0.5.0b5.dist-info/WHEEL +5 -0
  182. code_mower-0.5.0b5.dist-info/entry_points.txt +2 -0
  183. code_mower-0.5.0b5.dist-info/licenses/LICENSE +202 -0
  184. code_mower-0.5.0b5.dist-info/licenses/NOTICE +10 -0
  185. code_mower-0.5.0b5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,237 @@
1
+ """First-user readiness scorecards for package-install rehearsals."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any, Sequence
8
+
9
+ FIRST_USER_ARTIFACTS = (
10
+ ("calibration_plan", ".code-mower/calibration-plan.json"),
11
+ ("draft_calibration_corpus", ".code-mower/draft-calibration-corpus.json"),
12
+ ("draft_reviewer_value_report", ".code-mower/draft-reviewer-value-report.md"),
13
+ ("calibration_evidence", "calibration-evidence.json"),
14
+ ("reviewer_metrics", "reviewer-metrics.json"),
15
+ ("lane_policy", "lane-policy.json"),
16
+ ("reviewer_value_report", "reviewer-value-report.md"),
17
+ ("cloud_export", "cloud-export.json"),
18
+ ("cloud_upload_dry_run", "cloud-upload-dry-run.json"),
19
+ ("cloud_dogfood_dry_run", "cloud-dogfood-dry-run.json"),
20
+ )
21
+ PRIVACY_EXCLUDED_CONTENT = frozenset(
22
+ {
23
+ "source_code",
24
+ "raw_diffs",
25
+ "raw_model_transcripts",
26
+ "raw_stdout_stderr",
27
+ "auth_probe_output",
28
+ "secrets",
29
+ }
30
+ )
31
+
32
+
33
+ def first_user_artifacts(toy_repo: Path) -> dict[str, str]:
34
+ return {key: str(toy_repo / relative_path) for key, relative_path in FIRST_USER_ARTIFACTS}
35
+
36
+
37
+ def _read_json_file(path: Path) -> Any | None:
38
+ try:
39
+ return json.loads(path.read_text(encoding="utf-8"))
40
+ except (OSError, json.JSONDecodeError):
41
+ return None
42
+
43
+
44
+ def _readiness_check(
45
+ *,
46
+ check_id: str,
47
+ title: str,
48
+ status: str,
49
+ evidence: str,
50
+ detail: dict[str, Any] | None = None,
51
+ ) -> dict[str, Any]:
52
+ check: dict[str, Any] = {
53
+ "id": check_id,
54
+ "title": title,
55
+ "status": status,
56
+ "evidence": evidence,
57
+ }
58
+ if detail:
59
+ check["detail"] = detail
60
+ return check
61
+
62
+
63
+ def _artifact_exists_check(
64
+ *,
65
+ check_id: str,
66
+ title: str,
67
+ path: Path,
68
+ min_bytes: int = 1,
69
+ ) -> dict[str, Any]:
70
+ exists = path.is_file()
71
+ size = path.stat().st_size if exists else 0
72
+ return _readiness_check(
73
+ check_id=check_id,
74
+ title=title,
75
+ status="pass" if exists and size >= min_bytes else "fail",
76
+ evidence=str(path),
77
+ detail={"exists": exists, "bytes": size},
78
+ )
79
+
80
+
81
+ def _step_succeeded(steps: Sequence[dict[str, Any]], *needles: str) -> bool:
82
+ for step in steps:
83
+ if step.get("returncode") != 0:
84
+ continue
85
+ command = " ".join(str(part) for part in step.get("command", ()))
86
+ if all(needle in command for needle in needles):
87
+ return True
88
+ return False
89
+
90
+
91
+ def _cloud_dry_run_check(
92
+ *,
93
+ check_id: str,
94
+ title: str,
95
+ path: Path,
96
+ upload_payload: dict[str, Any] | None = None,
97
+ ) -> dict[str, Any]:
98
+ payload = upload_payload if upload_payload is not None else _read_json_file(path)
99
+ upload = payload.get("upload") if isinstance(payload, dict) else None
100
+ if not isinstance(upload, dict):
101
+ upload = payload if isinstance(payload, dict) else {}
102
+ excluded = set(upload.get("excluded_content") or ())
103
+ missing_exclusions = sorted(PRIVACY_EXCLUDED_CONTENT - excluded)
104
+ detail = {
105
+ "mode": upload.get("mode"),
106
+ "privacy_mode": upload.get("privacy_mode"),
107
+ "requires_yes": upload.get("requires_yes"),
108
+ "would_upload": upload.get("would_upload"),
109
+ "missing_exclusions": missing_exclusions,
110
+ }
111
+ passes = (
112
+ path.is_file()
113
+ and upload.get("mode") == "cloud-upload-dry-run"
114
+ and upload.get("privacy_mode") == "metadata_and_reports"
115
+ and upload.get("requires_yes") is True
116
+ and upload.get("would_upload") is False
117
+ and not missing_exclusions
118
+ )
119
+ return _readiness_check(
120
+ check_id=check_id,
121
+ title=title,
122
+ status="pass" if passes else "fail",
123
+ evidence=str(path),
124
+ detail=detail,
125
+ )
126
+
127
+
128
+ def first_user_readiness_scorecard(
129
+ *,
130
+ toy_repo: Path,
131
+ outputs: Path,
132
+ version: str,
133
+ steps: Sequence[dict[str, Any]],
134
+ ) -> dict[str, Any]:
135
+ artifacts = first_user_artifacts(toy_repo)
136
+ generated_dir = toy_repo / ".code-mower.generated"
137
+ cloud_upload_path = Path(artifacts["cloud_upload_dry_run"])
138
+ dogfood_path = Path(artifacts["cloud_dogfood_dry_run"])
139
+ cloud_export_payload = _read_json_file(Path(artifacts["cloud_export"]))
140
+ dogfood_payload = _read_json_file(dogfood_path)
141
+ dogfood_upload = dogfood_payload.get("upload") if isinstance(dogfood_payload, dict) else None
142
+
143
+ checks = [
144
+ _readiness_check(
145
+ check_id="package-installed",
146
+ title="Package installs and exposes the CLI",
147
+ status="pass" if version.startswith("code-mower ") else "fail",
148
+ evidence=version,
149
+ ),
150
+ _readiness_check(
151
+ check_id="easy-init-generated",
152
+ title="Easy-mode setup writes reviewable generated files",
153
+ status=(
154
+ "pass"
155
+ if (
156
+ (generated_dir / "code-mower-init-plan.json").is_file()
157
+ and (generated_dir / "smoke-tests.sh").is_file()
158
+ and (generated_dir / "tools" / "code_mower").is_file()
159
+ )
160
+ else "fail"
161
+ ),
162
+ evidence=str(generated_dir),
163
+ ),
164
+ _readiness_check(
165
+ check_id="doctor-ran",
166
+ title="First-run doctor completes",
167
+ status="pass" if _step_succeeded(steps, "doctor", "--easy") else "fail",
168
+ evidence="code-mower doctor --easy --json",
169
+ ),
170
+ _artifact_exists_check(
171
+ check_id="draft-calibration-corpus",
172
+ title="Auto-discovery creates a reviewable draft corpus",
173
+ path=Path(artifacts["draft_calibration_corpus"]),
174
+ ),
175
+ _artifact_exists_check(
176
+ check_id="draft-value-report",
177
+ title="Draft corpus can produce a reviewer value report",
178
+ path=Path(artifacts["draft_reviewer_value_report"]),
179
+ ),
180
+ _artifact_exists_check(
181
+ check_id="starter-value-report",
182
+ title="Starter corpus can produce the first value report",
183
+ path=Path(artifacts["reviewer_value_report"]),
184
+ ),
185
+ _readiness_check(
186
+ check_id="cloud-export-metadata-bundle",
187
+ title="Cloud export creates a metadata/report bundle",
188
+ status=(
189
+ "pass"
190
+ if (
191
+ isinstance(cloud_export_payload, dict)
192
+ and cloud_export_payload.get("mode") == "cloud-export"
193
+ and len(cloud_export_payload.get("included_reports") or ()) >= 3
194
+ and cloud_export_payload.get("upload_ready") is False
195
+ )
196
+ else "fail"
197
+ ),
198
+ evidence=artifacts["cloud_export"],
199
+ ),
200
+ _cloud_dry_run_check(
201
+ check_id="cloud-upload-dry-run-privacy",
202
+ title="Cloud upload stays dry-run and excludes private content",
203
+ path=cloud_upload_path,
204
+ ),
205
+ _readiness_check(
206
+ check_id="cloud-dogfood-dry-run",
207
+ title="CodeMower.com dogfood path stays dry-run by default",
208
+ status=(
209
+ "pass"
210
+ if (
211
+ isinstance(dogfood_payload, dict) and dogfood_payload.get("status") == "dry_run"
212
+ )
213
+ else "fail"
214
+ ),
215
+ evidence=str(dogfood_path),
216
+ ),
217
+ _cloud_dry_run_check(
218
+ check_id="cloud-dogfood-upload-privacy",
219
+ title="Dogfood upload preview excludes private content",
220
+ path=dogfood_path,
221
+ upload_payload=dogfood_upload,
222
+ ),
223
+ ]
224
+ passed = sum(1 for check in checks if check["status"] == "pass")
225
+ failed = sum(1 for check in checks if check["status"] == "fail")
226
+ warnings = sum(1 for check in checks if check["status"] == "warn")
227
+ scorecard = {
228
+ "mode": "code-mower-first-user-readiness",
229
+ "status": "pass" if failed == 0 else "fail",
230
+ "passed": passed,
231
+ "failed": failed,
232
+ "warnings": warnings,
233
+ "total": len(checks),
234
+ "checks": checks,
235
+ "artifact": str(outputs / "first-user-readiness.json"),
236
+ }
237
+ return scorecard