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,585 @@
1
+ """Static generated package file contents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ STATIC_PACKAGE_FILES = (
6
+ ("src/code_mower/__init__.py", ""),
7
+ (
8
+ "README.md",
9
+ "\n".join(
10
+ [
11
+ "# Code Mower",
12
+ "",
13
+ "Code Mower is the fastest way to build a peer-programmer and "
14
+ "reviewer system around the best AI coding agents.",
15
+ "",
16
+ "It helps teams drive from plan to merge at maximum safe velocity "
17
+ "while preserving code quality, architecture, and deployment "
18
+ "confidence. It also turns your real codebase into a "
19
+ "quality-and-velocity benchmark, measuring which AI builders and "
20
+ "reviewers deliver the best quality, speed, and cost results for "
21
+ "your actual product.",
22
+ "",
23
+ "The Code Mower open-source core is licensed under Apache-2.0. "
24
+ "Hosted benchmarking and reporting, managed integrations, "
25
+ "private telemetry and benchmark data products, enterprise "
26
+ "controls, and support are commercial surfaces unless licensed "
27
+ "otherwise.",
28
+ "",
29
+ "Code Mower is extracted from a production multi-repo development "
30
+ "workflow and packaged as a standalone OSS tool. Start with "
31
+ "`code-mower init --easy`, then run "
32
+ "`code-mower doctor --easy` to verify local CLIs, "
33
+ "tokens, provider catalog coverage, and runtime probes.",
34
+ "",
35
+ "For existing repos that still carry product-local Code Mower "
36
+ "tools, run `code-mower migration wrapper-rehearsal "
37
+ "--repo-path /path/to/repo --json` before flipping to a pinned "
38
+ "standalone package. The rehearsal compares safe read-only "
39
+ "commands and gives a low-risk path away from mirrored "
40
+ "maintenance.",
41
+ "",
42
+ "For public release readiness, see `docs/repo-strategy.md`, "
43
+ "`docs/mirror-removal-runbook.md`, "
44
+ "`docs/commercial-boundary.md`, and "
45
+ "`docs/public-release-checklist.md`.",
46
+ "",
47
+ ]
48
+ ),
49
+ ),
50
+ (
51
+ "MANIFEST.in",
52
+ "\n".join(
53
+ [
54
+ "recursive-include src/code_mower/templates *.yml *.yaml *.json",
55
+ "recursive-include src/code_mower/templates *.md",
56
+ "recursive-include src/code_mower/templates *.j2",
57
+ "recursive-include src/code_mower/templates/product-support *",
58
+ "include src/code_mower/*.json",
59
+ "recursive-include templates *.j2 *.json *.md *.yml *.yaml",
60
+ "include requirements/*.txt",
61
+ "include LICENSE",
62
+ "include NOTICE",
63
+ "",
64
+ ]
65
+ ),
66
+ ),
67
+ (
68
+ ".gitignore",
69
+ "\n".join(
70
+ [
71
+ ".venv/",
72
+ "__pycache__/",
73
+ "*.py[cod]",
74
+ "*.egg-info/",
75
+ "build/",
76
+ "dist/",
77
+ ".pytest_cache/",
78
+ ".mypy_cache/",
79
+ ".ruff_cache/",
80
+ ".code-mower/",
81
+ ".code-mower.generated/",
82
+ "",
83
+ ]
84
+ ),
85
+ ),
86
+ (
87
+ ".github/workflows/ci.yml",
88
+ "\n".join(
89
+ [
90
+ "name: Code Mower CI",
91
+ "",
92
+ "on:",
93
+ " push:",
94
+ " branches: [main]",
95
+ " pull_request:",
96
+ " workflow_dispatch:",
97
+ "",
98
+ "jobs:",
99
+ " package:",
100
+ " runs-on: ubuntu-latest",
101
+ " steps:",
102
+ " - name: Check out",
103
+ " uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10",
104
+ "",
105
+ " - name: Set up Python",
106
+ " uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405",
107
+ " with:",
108
+ " python-version: '3.12'",
109
+ "",
110
+ " - name: Install package",
111
+ " run: |",
112
+ " python -m pip install --upgrade pip",
113
+ " python -m pip install -e .",
114
+ " python -m pip check",
115
+ "",
116
+ " - name: Compile sources",
117
+ " run: python -m compileall src scripts",
118
+ "",
119
+ " - name: Easy-mode smoke",
120
+ " run: python scripts/smoke_easy_mode.py --work-dir \"$RUNNER_TEMP/code-mower-smoke\" --json",
121
+ "",
122
+ " - name: Fresh-clone rehearsal",
123
+ " run: >-",
124
+ " python scripts/fresh_clone_rehearsal.py",
125
+ " --repo-url \"$GITHUB_WORKSPACE\"",
126
+ " --ref \"$GITHUB_SHA\"",
127
+ " --work-dir \"$RUNNER_TEMP/code-mower-fresh-clone\"",
128
+ " --json",
129
+ "",
130
+ ]
131
+ ),
132
+ ),
133
+ (
134
+ "scripts/smoke_easy_mode.py",
135
+ """#!/usr/bin/env python3
136
+ \"\"\"Smoke test the standalone Code Mower easy-mode package flow.\"\"\"
137
+
138
+ from __future__ import annotations
139
+
140
+ import argparse
141
+ import json
142
+ import os
143
+ import shutil
144
+ import subprocess
145
+ import sys
146
+ import tempfile
147
+ from pathlib import Path
148
+ from typing import Any
149
+
150
+
151
+ DEFAULT_CODE_MOWER_BIN = \"__CODE_MOWER_CONSOLE_SCRIPT__\"
152
+
153
+
154
+ def _run(
155
+ command: list[str],
156
+ *,
157
+ cwd: Path,
158
+ env: dict[str, str],
159
+ stdout_path: Path | None = None,
160
+ ) -> dict[str, Any]:
161
+ completed = subprocess.run(
162
+ command,
163
+ cwd=cwd,
164
+ env=env,
165
+ text=True,
166
+ stdout=subprocess.PIPE,
167
+ stderr=subprocess.PIPE,
168
+ )
169
+ if stdout_path is not None:
170
+ stdout_path.parent.mkdir(parents=True, exist_ok=True)
171
+ stdout_path.write_text(completed.stdout, encoding=\"utf-8\")
172
+ result = {
173
+ \"command\": command,
174
+ \"cwd\": str(cwd),
175
+ \"returncode\": completed.returncode,
176
+ \"stdout_path\": str(stdout_path) if stdout_path else None,
177
+ \"stderr\": completed.stderr[-4000:],
178
+ }
179
+ if completed.returncode != 0:
180
+ raise RuntimeError(json.dumps(result, indent=2, sort_keys=True))
181
+ return result
182
+
183
+
184
+ def _write_json(path: Path, data: Any) -> None:
185
+ path.parent.mkdir(parents=True, exist_ok=True)
186
+ path.write_text(json.dumps(data, indent=2, sort_keys=True) + \"\\n\", encoding=\"utf-8\")
187
+
188
+
189
+ def _resolve_executable(path_text: str) -> Path:
190
+ resolved = shutil.which(path_text)
191
+ if resolved:
192
+ return Path(resolved).resolve()
193
+ return Path(path_text).expanduser().resolve()
194
+
195
+
196
+ def _default_code_mower_bin() -> str:
197
+ resolved = shutil.which(DEFAULT_CODE_MOWER_BIN)
198
+ if resolved:
199
+ return resolved
200
+
201
+ sibling_script = Path(sys.executable).parent / DEFAULT_CODE_MOWER_BIN
202
+ if sibling_script.is_file():
203
+ return str(sibling_script)
204
+
205
+ resolved_sibling_script = Path(sys.executable).resolve().parent / DEFAULT_CODE_MOWER_BIN
206
+ if resolved_sibling_script.is_file():
207
+ return str(resolved_sibling_script)
208
+
209
+ return DEFAULT_CODE_MOWER_BIN
210
+
211
+
212
+ def run_smoke(*, code_mower_bin: Path, work_dir: Path) -> dict[str, Any]:
213
+ if not code_mower_bin.is_file():
214
+ raise RuntimeError(f\"code-mower executable not found: {code_mower_bin}\")
215
+
216
+ toy_repo = work_dir / \"toy-repo\"
217
+ outputs = work_dir / \"outputs\"
218
+ if toy_repo.exists():
219
+ shutil.rmtree(toy_repo)
220
+ toy_repo.mkdir(parents=True)
221
+ outputs.mkdir(parents=True, exist_ok=True)
222
+
223
+ env = os.environ.copy()
224
+ env[\"PATH\"] = f\"{code_mower_bin.parent}{os.pathsep}{env.get('PATH', '')}\"
225
+
226
+ git = shutil.which(\"git\")
227
+ if git:
228
+ _run([git, \"init\", \"-q\"], cwd=toy_repo, env=env)
229
+ _run([git, \"config\", \"user.name\", \"Code Mower Smoke\"], cwd=toy_repo, env=env)
230
+ _run([git, \"config\", \"user.email\", \"smoke@example.com\"], cwd=toy_repo, env=env)
231
+ _run([git, \"config\", \"commit.gpgSign\", \"false\"], cwd=toy_repo, env=env)
232
+ (toy_repo / \"README.md\").write_text(
233
+ \"# Code Mower smoke toy repo\\n\", encoding=\"utf-8\"
234
+ )
235
+ if git:
236
+ _run([git, \"add\", \"README.md\"], cwd=toy_repo, env=env)
237
+ _run(
238
+ [git, \"-c\", \"commit.gpgSign=false\", \"commit\", \"-q\", \"-m\", \"Initial smoke repo\"],
239
+ cwd=toy_repo,
240
+ env=env,
241
+ )
242
+
243
+ steps: list[dict[str, Any]] = []
244
+ cm = str(code_mower_bin)
245
+ steps.append(
246
+ _run([cm, \"providers\", \"list\"], cwd=toy_repo, env=env, stdout_path=outputs / \"providers.txt\")
247
+ )
248
+ steps.append(
249
+ _run(
250
+ [cm, \"init\", \"--easy\", \"--apply\", \"--output-dir\", \".code-mower.generated\", \"--json\"],
251
+ cwd=toy_repo,
252
+ env=env,
253
+ stdout_path=outputs / \"init-apply.json\",
254
+ )
255
+ )
256
+ steps.append(
257
+ _run(
258
+ [\"bash\", \".code-mower.generated/smoke-tests.sh\"],
259
+ cwd=toy_repo,
260
+ env=env,
261
+ stdout_path=outputs / \"generated-smoke-tests.txt\",
262
+ )
263
+ )
264
+ steps.append(
265
+ _run([cm, \"doctor\", \"--easy\", \"--json\"], cwd=toy_repo, env=env, stdout_path=outputs / \"doctor.json\")
266
+ )
267
+ steps.append(
268
+ _run(
269
+ [cm, \"next-steps\", \"--profile\", \"recommended\", \"--json\"],
270
+ cwd=toy_repo,
271
+ env=env,
272
+ stdout_path=outputs / \"next-steps.json\",
273
+ )
274
+ )
275
+ steps.append(
276
+ _run(
277
+ [
278
+ cm,
279
+ \"migration\",
280
+ \"wrapper-rehearsal\",
281
+ \"--repo-path\",
282
+ str(toy_repo),
283
+ \"--local-command\",
284
+ cm,
285
+ \"--package-command\",
286
+ cm,
287
+ \"--json\",
288
+ ],
289
+ cwd=toy_repo,
290
+ env=env,
291
+ stdout_path=outputs / \"wrapper-rehearsal.json\",
292
+ )
293
+ )
294
+
295
+ code_mower_dir = toy_repo / \".code-mower\"
296
+ code_mower_dir.mkdir(exist_ok=True)
297
+ steps.append(
298
+ _run(
299
+ [
300
+ cm,
301
+ \"calibration\",
302
+ \"plan\",
303
+ \".code-mower.generated/calibration-corpus.json\",
304
+ \"--replicates\",
305
+ \"2\",
306
+ \"--json\",
307
+ ],
308
+ cwd=toy_repo,
309
+ env=env,
310
+ stdout_path=code_mower_dir / \"calibration-plan.json\",
311
+ )
312
+ )
313
+ steps.append(
314
+ _run(
315
+ [cm, \"calibration\", \"evidence\", \".code-mower.generated/calibration-corpus.json\", \"--json\"],
316
+ cwd=toy_repo,
317
+ env=env,
318
+ stdout_path=toy_repo / \"calibration-evidence.json\",
319
+ )
320
+ )
321
+ steps.append(
322
+ _run(
323
+ [
324
+ cm,
325
+ \"reviewer-metrics\",
326
+ \"calibration-evidence.json\",
327
+ \"--spend\",
328
+ \".code-mower.generated/reviewer-spend.json\",
329
+ \"--json\",
330
+ ],
331
+ cwd=toy_repo,
332
+ env=env,
333
+ stdout_path=toy_repo / \"reviewer-metrics.json\",
334
+ )
335
+ )
336
+ steps.append(
337
+ _run(
338
+ [cm, \"calibration\", \"policy\", \"reviewer-metrics.json\", \"--json\"],
339
+ cwd=toy_repo,
340
+ env=env,
341
+ stdout_path=toy_repo / \"lane-policy.json\",
342
+ )
343
+ )
344
+ steps.append(
345
+ _run(
346
+ [
347
+ cm,
348
+ \"calibration\",
349
+ \"value-report\",
350
+ \".code-mower.generated/calibration-corpus.json\",
351
+ \"--spend\",
352
+ \".code-mower.generated/reviewer-spend.json\",
353
+ \"--output\",
354
+ \"reviewer-value-report.md\",
355
+ ],
356
+ cwd=toy_repo,
357
+ env=env,
358
+ stdout_path=outputs / \"value-report.txt\",
359
+ )
360
+ )
361
+ steps.append(
362
+ _run(
363
+ [
364
+ cm,
365
+ \"cloud\",
366
+ \"export\",
367
+ \"--report\",
368
+ \"reviewer-metrics=reviewer-metrics.json\",
369
+ \"--report\",
370
+ \"lane-policy=lane-policy.json\",
371
+ \"--report\",
372
+ \"value-report=reviewer-value-report.md\",
373
+ \"--output-dir\",
374
+ \".code-mower/cloud-benchmark-bundle\",
375
+ \"--json\",
376
+ ],
377
+ cwd=toy_repo,
378
+ env=env,
379
+ stdout_path=toy_repo / \"cloud-export.json\",
380
+ )
381
+ )
382
+ steps.append(
383
+ _run(
384
+ [
385
+ cm,
386
+ \"cloud\",
387
+ \"upload\",
388
+ \".code-mower/cloud-benchmark-bundle\",
389
+ \"--dry-run\",
390
+ \"--json\",
391
+ ],
392
+ cwd=toy_repo,
393
+ env=env,
394
+ stdout_path=toy_repo / \"cloud-upload-dry-run.json\",
395
+ )
396
+ )
397
+
398
+ summary = {
399
+ \"mode\": \"code-mower-easy-mode-smoke\",
400
+ \"status\": \"pass\",
401
+ \"code_mower_bin\": str(code_mower_bin),
402
+ \"work_dir\": str(work_dir),
403
+ \"toy_repo\": str(toy_repo),
404
+ \"outputs_dir\": str(outputs),
405
+ \"steps\": steps,
406
+ }
407
+ _write_json(outputs / \"smoke-summary.json\", summary)
408
+ return summary
409
+
410
+
411
+ def main(argv: list[str] | None = None) -> int:
412
+ parser = argparse.ArgumentParser(description=__doc__)
413
+ parser.add_argument(
414
+ \"--code-mower-bin\",
415
+ default=_default_code_mower_bin(),
416
+ help=\"Path to the installed code-mower executable.\",
417
+ )
418
+ parser.add_argument(
419
+ \"--work-dir\",
420
+ default=None,
421
+ help=\"Directory for the generated toy repo and outputs. Defaults to a temp dir.\",
422
+ )
423
+ parser.add_argument(\"--json\", action=\"store_true\")
424
+ args = parser.parse_args(argv)
425
+
426
+ work_dir = Path(args.work_dir) if args.work_dir else Path(
427
+ tempfile.mkdtemp(prefix=\"code-mower-easy-smoke-\")
428
+ )
429
+ try:
430
+ summary = run_smoke(code_mower_bin=_resolve_executable(args.code_mower_bin), work_dir=work_dir)
431
+ except RuntimeError as exc:
432
+ print(f\"error: {exc}\", file=sys.stderr)
433
+ return 1
434
+
435
+ if args.json:
436
+ print(json.dumps(summary, indent=2, sort_keys=True))
437
+ else:
438
+ print(f\"Code Mower easy-mode smoke passed: {work_dir}\")
439
+ return 0
440
+
441
+
442
+ if __name__ == \"__main__\":
443
+ raise SystemExit(main())
444
+ """,
445
+ ),
446
+ (
447
+ "scripts/fresh_clone_rehearsal.py",
448
+ """#!/usr/bin/env python3
449
+ \"\"\"Rehearse Code Mower from a fresh clone and clean virtual environment.\"\"\"
450
+
451
+ from __future__ import annotations
452
+
453
+ import argparse
454
+ import json
455
+ import subprocess
456
+ import sys
457
+ import tempfile
458
+ from pathlib import Path
459
+ from typing import Any
460
+
461
+
462
+ class RehearsalError(RuntimeError):
463
+ def __init__(self, message: str, steps: list[dict[str, Any]]) -> None:
464
+ super().__init__(message)
465
+ self.steps = steps
466
+
467
+
468
+ def _venv_python(venv_dir: Path) -> Path:
469
+ unix_python = venv_dir / \"bin\" / \"python\"
470
+ if unix_python.exists():
471
+ return unix_python
472
+ return venv_dir / \"Scripts\" / \"python.exe\"
473
+
474
+
475
+ def _default_repo_url() -> str:
476
+ package_root = Path(__file__).resolve().parents[1]
477
+ return str(package_root)
478
+
479
+
480
+ def _run(command: list[str], *, cwd: Path, steps: list[dict[str, Any]]) -> None:
481
+ completed = subprocess.run(
482
+ command,
483
+ cwd=cwd,
484
+ text=True,
485
+ stdout=subprocess.PIPE,
486
+ stderr=subprocess.PIPE,
487
+ check=False,
488
+ )
489
+ step = {
490
+ \"command\": command,
491
+ \"cwd\": str(cwd),
492
+ \"returncode\": completed.returncode,
493
+ \"stdout\": completed.stdout[-4000:],
494
+ \"stderr\": completed.stderr[-4000:],
495
+ }
496
+ steps.append(step)
497
+ if completed.returncode != 0:
498
+ raise RehearsalError(f\"command failed: {' '.join(command)}\", steps)
499
+
500
+
501
+ def run_rehearsal(args: argparse.Namespace) -> dict[str, Any]:
502
+ work_dir = (
503
+ Path(args.work_dir).expanduser().resolve()
504
+ if args.work_dir
505
+ else Path(tempfile.mkdtemp(prefix=\"code-mower-fresh-clone-\"))
506
+ )
507
+ clone_dir = work_dir / \"clone\"
508
+ venv_dir = work_dir / \"venv\"
509
+ smoke_dir = work_dir / \"smoke\"
510
+ steps: list[dict[str, Any]] = []
511
+
512
+ if clone_dir.exists() or venv_dir.exists() or smoke_dir.exists():
513
+ raise RuntimeError(f\"work directory is not clean: {work_dir}\")
514
+ work_dir.mkdir(parents=True, exist_ok=True)
515
+
516
+ repo_url = args.repo_url or _default_repo_url()
517
+ _run([\"git\", \"clone\", repo_url, str(clone_dir)], cwd=work_dir, steps=steps)
518
+ if args.ref:
519
+ _run([\"git\", \"checkout\", args.ref], cwd=clone_dir, steps=steps)
520
+
521
+ python_bin = Path(args.python).expanduser().resolve() if args.python else Path(sys.executable)
522
+ _run([str(python_bin), \"-m\", \"venv\", str(venv_dir)], cwd=work_dir, steps=steps)
523
+ venv_python = _venv_python(venv_dir)
524
+ _run([str(venv_python), \"-m\", \"pip\", \"install\", \"--upgrade\", \"pip\"], cwd=clone_dir, steps=steps)
525
+ _run([str(venv_python), \"-m\", \"pip\", \"install\", \"-e\", \".\"], cwd=clone_dir, steps=steps)
526
+ _run([str(venv_python), \"-m\", \"pip\", \"check\"], cwd=clone_dir, steps=steps)
527
+ _run(
528
+ [
529
+ str(venv_python),
530
+ \"scripts/smoke_easy_mode.py\",
531
+ \"--work-dir\",
532
+ str(smoke_dir),
533
+ \"--json\",
534
+ ],
535
+ cwd=clone_dir,
536
+ steps=steps,
537
+ )
538
+
539
+ return {
540
+ \"mode\": \"code-mower-fresh-clone-rehearsal\",
541
+ \"status\": \"pass\",
542
+ \"repo_url\": repo_url,
543
+ \"ref\": args.ref,
544
+ \"work_dir\": str(work_dir),
545
+ \"clone_dir\": str(clone_dir),
546
+ \"steps\": steps,
547
+ }
548
+
549
+
550
+ def main(argv: list[str] | None = None) -> int:
551
+ parser = argparse.ArgumentParser(description=__doc__)
552
+ parser.add_argument(\"--repo-url\", help=\"Repository URL or local path to clone.\")
553
+ parser.add_argument(\"--ref\", help=\"Branch, tag, or commit to check out after cloning.\")
554
+ parser.add_argument(\"--work-dir\", help=\"Clean work directory for clone, venv, and smoke output.\")
555
+ parser.add_argument(\"--python\", help=\"Python executable used to create the rehearsal venv.\")
556
+ parser.add_argument(\"--json\", action=\"store_true\", help=\"Emit JSON output.\")
557
+ args = parser.parse_args(argv)
558
+
559
+ try:
560
+ payload = run_rehearsal(args)
561
+ except Exception as exc: # pragma: no cover - exercised by CLI failure paths.
562
+ payload = {
563
+ \"mode\": \"code-mower-fresh-clone-rehearsal\",
564
+ \"status\": \"fail\",
565
+ \"error\": str(exc),
566
+ \"steps\": getattr(exc, \"steps\", []),
567
+ }
568
+ if args.json:
569
+ print(json.dumps(payload, indent=2, sort_keys=True))
570
+ else:
571
+ print(f\"fresh clone rehearsal failed: {exc}\", file=sys.stderr)
572
+ return 1
573
+
574
+ if args.json:
575
+ print(json.dumps(payload, indent=2, sort_keys=True))
576
+ else:
577
+ print(f\"fresh clone rehearsal passed: {payload['clone_dir']}\")
578
+ return 0
579
+
580
+
581
+ if __name__ == \"__main__\":
582
+ raise SystemExit(main())
583
+ """,
584
+ ),
585
+ )