code-mower 0.5.0b40__tar.gz → 0.5.0b42__tar.gz
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-0.5.0b40 → code_mower-0.5.0b42}/PKG-INFO +31 -12
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/README.md +30 -11
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/pyproject.toml +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/__init__.py +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/audit_labeler_lib.py +227 -6
- code_mower-0.5.0b42/src/code_mower/builder_runs.py +752 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/evidence.py +1 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/evidence_report.py +66 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/value_report.py +17 -5
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/checks.py +339 -3
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/claude_audit_pr.py +212 -6
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cli.py +4 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud.py +59 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/bundle.py +1 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/operations.py +23 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_telemetry.py +26 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_pr.py +248 -12
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/config.py +22 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/__init__.py +2 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/common.py +1 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github.py +13 -1
- code_mower-0.5.0b42/src/code_mower/doctor_checks/github_branch.py +90 -0
- code_mower-0.5.0b42/src/code_mower/doctor_checks/github_config.py +57 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_repo.py +31 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runner.py +2 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/init.py +514 -5
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/claude.py +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/codex.py +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration.py +11 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_mirror.py +1 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_content.py +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_manifest.py +23 -0
- code_mower-0.5.0b42/src/code_mower/plan_context.py +443 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/__init__.py +6 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/pr_worktree.py +36 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/reviewer_metrics.py +78 -8
- code_mower-0.5.0b42/src/code_mower/reviewer_spend.py +283 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/saas_reviewer_labeler.py +72 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/code-mower.example.yml +32 -0
- code_mower-0.5.0b42/src/code_mower/templates/lane_prompts/plan-conformance.md +12 -0
- code_mower-0.5.0b42/src/code_mower/templates/product-support/status_report.py +232 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42/src/code_mower}/templates/reviewer-spend.example.json +3 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/reviewer-value-report.example.md +3 -3
- {code_mower-0.5.0b40 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/audit-label-cleanup.yml.j2 +1 -0
- code_mower-0.5.0b42/src/code_mower/templates/workflows/builder-provenance.yml.j2 +64 -0
- code_mower-0.5.0b42/src/code_mower/templates/workflows/code-mower-gate-health.yml.j2 +321 -0
- code_mower-0.5.0b42/src/code_mower/templates/workflows/code-mower-gate.yml.j2 +237 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42/src/code_mower}/templates/workflows/hosted-bridge.yml.j2 +1 -1
- code_mower-0.5.0b42/src/code_mower/templates/workflows/needs-owner-notify.yml.j2 +54 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/review-clear-stale.yml.j2 +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/saas-reviewer-labeler.yml.j2 +3 -1
- code_mower-0.5.0b42/src/code_mower/templates/workflows/self-hosted-local-audit.yml.j2 +214 -0
- code_mower-0.5.0b42/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +63 -0
- code_mower-0.5.0b42/src/code_mower/templates/workflows/weekly-status.yml.j2 +54 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/trailer_comment_labeler.py +73 -3
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/work_orders.py +83 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/PKG-INFO +31 -12
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/SOURCES.txt +21 -0
- {code_mower-0.5.0b40/src/code_mower → code_mower-0.5.0b42}/templates/reviewer-spend.example.json +3 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/reviewer-value-report.example.md +3 -3
- {code_mower-0.5.0b40/src/code_mower → code_mower-0.5.0b42}/templates/workflows/audit-label-cleanup.yml.j2 +1 -0
- code_mower-0.5.0b42/templates/workflows/builder-provenance.yml.j2 +64 -0
- code_mower-0.5.0b42/templates/workflows/code-mower-gate-health.yml.j2 +321 -0
- code_mower-0.5.0b42/templates/workflows/code-mower-gate.yml.j2 +237 -0
- {code_mower-0.5.0b40/src/code_mower → code_mower-0.5.0b42}/templates/workflows/hosted-bridge.yml.j2 +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/review-clear-stale.yml.j2 +1 -1
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/saas-reviewer-labeler.yml.j2 +3 -1
- code_mower-0.5.0b42/templates/workflows/self-hosted-local-audit.yml.j2 +214 -0
- code_mower-0.5.0b42/templates/workflows/trailer-comment-labeler.yml.j2 +63 -0
- code_mower-0.5.0b42/tests/test_audit_comment_posture.py +71 -0
- code_mower-0.5.0b42/tests/test_builder_identity.py +144 -0
- code_mower-0.5.0b42/tests/test_builder_runs.py +794 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_calibration_truth.py +52 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_checks.py +96 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_client.py +87 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_github_checks.py +78 -1
- code_mower-0.5.0b42/tests/test_plan_context.py +241 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_pr_worktree.py +58 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_release_hygiene.py +1007 -38
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_reviewer_metrics_core.py +73 -2
- code_mower-0.5.0b42/tests/test_reviewer_spend.py +234 -0
- code_mower-0.5.0b42/tests/test_trailer_comment_labeler.py +151 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_work_orders.py +22 -0
- code_mower-0.5.0b40/src/code_mower/doctor_checks/github_branch.py +0 -56
- code_mower-0.5.0b40/src/code_mower/doctor_checks/github_config.py +0 -25
- code_mower-0.5.0b40/src/code_mower/templates/workflows/trailer-comment-labeler.yml.j2 +0 -51
- code_mower-0.5.0b40/templates/workflows/trailer-comment-labeler.yml.j2 +0 -51
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/CODE_OF_CONDUCT.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/LICENSE +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/MANIFEST.in +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/NOTICE +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/SUPPORT.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/requirements/requirements.txt +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/setup.cfg +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/__init__.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/_base.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/cursor_bugbot.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/gitar.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/greptile.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/adapters/qodo.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/antigravity_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/audit_handoff_log.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/audit_progress.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/blind_review_artifacts.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/blind_review_coordinator.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/bootstrap.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/builder_experiment.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/__init__.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/arms.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/auto_discovery.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/commands.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/context_inputs.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/corpus.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/effect_report.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/identity.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/metrics.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/overlap.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/planning.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/policy.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/results.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/run_results.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/run_status.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/runner.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/calibration/truth.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/claude_cli_environment.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/clear_stale.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/__init__.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/doctor.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/dogfood.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/endpoints.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/errors.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/events.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/export.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/git_metadata.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/manifest.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/reports.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/setup.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/cloud_client/upload.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_calibration.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_context_packs.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/code_mower_merge.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_schema_smoke.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/codex_audit_verdict.schema.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/cloud.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_cost.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_annotations.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_models.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_scan.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_failures.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_actions_permissions.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_api.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/github_provider.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/groups.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/models.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/output.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/presets.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/privacy.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model_openai.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_api_model_profiles.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env_required.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_env_tokens.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli_commands.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_local_cli_probe_config.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_auth.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_evaluation.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_json.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_probe_remediation.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/providers.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/registry.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runtime.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/doctor_checks/runtime_github_auth.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/gemini_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/grok_build_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/hermes_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/__init__.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/aider.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/antigravity_cli.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/devin.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/gemini_cli.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/grok_build.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/hermes_cli.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/lane_configs/local_llm.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_bakeoff.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_calibration.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/local_llm_profiles.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_install.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_readiness.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/migration_rehearsal.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/next_steps.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_paths.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_rendering.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/package_static.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/prompts.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_registry.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/comments.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/git.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/github_auth.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/github_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/process.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/repo_paths.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/text_schema.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/verdict_artifacts.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/provider_runners/workspace.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/providers/__init__.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/providers/local_cli.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/providers/provenance.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/release_readiness.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/secrets.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/base-audit.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/calibration-policy.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/context-driven-quality.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/docs-design.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/generic-programming.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/operability.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/package-runtime.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/lane_prompts/security-threat-model.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower_standalone_pin.env +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/code_mower_standalone_shadow.sh +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/run_claude_audit_pr.sh +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/run_codex_audit_pr.sh +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/product-support/safe_gh_comment.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/providers.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower/versioning.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/dependency_links.txt +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/entry_points.txt +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/requires.txt +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/src/code_mower.egg-info/top_level.txt +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/builder-experiment.example.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/calibration-corpus.example.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/calibration-corpus.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/code-mower.yml.j2 +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/context-packs.example.json +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/cursor/BUGBOT.md +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/acp_bridge.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/aider.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/antigravity_cli.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/claude_audit.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/claude_review.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/coderabbit_cli.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/codex.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/cursor_bugbot.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/devin.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/gemini_cli.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/gitar.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/greptile.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/hermes_cli.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/local_llm.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers/qodo.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/providers.yml +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/blind-review-artifacts-dry-run.yml.j2 +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/local-cli-audit.yml.j2 +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/templates/workflows/private-standalone-shadow.yml.j2 +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_calibration_effect_report.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_claude_cli_bounce.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_clear_stale.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_git_metadata.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_manifest.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_cloud_reports.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_coderabbit_cli_audit_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_codex_audit_env_preflight.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_actions_cost_summary.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_actions_failure_selection.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_boundaries.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_output.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_privacy.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_env_required.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_env_tokens.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_probe.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_provider_review_hygiene.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_doctor_registry.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_local_llm_github_helpers.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_local_cli.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_comments.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_git.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_github_pr.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_process.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_repo_paths.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_text_schema.py +0 -0
- {code_mower-0.5.0b40 → code_mower-0.5.0b42}/tests/test_provider_runners_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-mower
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.0b42
|
|
4
4
|
Summary: Multi-reviewer AI code audit orchestration
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -103,11 +103,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
|
|
|
103
103
|
|
|
104
104
|
```bash
|
|
105
105
|
python3.12 --version
|
|
106
|
-
pipx install --python python3.12 code-mower==0.5.
|
|
106
|
+
pipx install --python python3.12 code-mower==0.5.0b42
|
|
107
107
|
code-mower --version
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
`0.5.
|
|
110
|
+
`0.5.0b42` is a beta release. Until Code Mower publishes a stable `1.0`
|
|
111
111
|
line, use the explicit beta version above or allow prereleases with:
|
|
112
112
|
|
|
113
113
|
```bash
|
|
@@ -122,6 +122,10 @@ code-mower doctor --preflight
|
|
|
122
122
|
code-mower checks detect --json
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
+
In Git worktrees, `checks detect` also offers `code-mower.pr-size`, a local
|
|
126
|
+
PR-size lint that defaults to 300 changed lines and flags large batches of
|
|
127
|
+
near-identical files.
|
|
128
|
+
|
|
125
129
|
When those look sane, write the generated setup to a reviewable folder and
|
|
126
130
|
produce the starter local report:
|
|
127
131
|
|
|
@@ -131,8 +135,9 @@ code-mower calibration value-report .code-mower.generated/calibration-corpus.jso
|
|
|
131
135
|
--output .code-mower/reviewer-value-report.md
|
|
132
136
|
```
|
|
133
137
|
|
|
134
|
-
The generated
|
|
135
|
-
|
|
138
|
+
The generated setup also includes the configurable owner escalation and weekly
|
|
139
|
+
status digest templates. The generated starter corpus proves the command path.
|
|
140
|
+
To bootstrap a draft from your repository history:
|
|
136
141
|
|
|
137
142
|
```bash
|
|
138
143
|
code-mower calibration auto-discover \
|
|
@@ -150,7 +155,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
|
|
|
150
155
|
|
|
151
156
|
The current PyPI beta has been rehearsed end-to-end from a clean install:
|
|
152
157
|
[First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
|
|
153
|
-
the latest 10/10 public-package readiness proof for `code-mower==0.5.
|
|
158
|
+
the latest 10/10 public-package readiness proof for `code-mower==0.5.0b42`.
|
|
154
159
|
|
|
155
160
|
## Optional: Plan Before Coding
|
|
156
161
|
|
|
@@ -184,9 +189,21 @@ code-mower work-order attach-delivery \
|
|
|
184
189
|
--from-github
|
|
185
190
|
```
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
If a hosted builder such as Grok Bot or Cursor Cloud Agents produced the PR,
|
|
193
|
+
record source-free builder provenance too:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
code-mower builder record \
|
|
197
|
+
--provider grok_bot \
|
|
198
|
+
--executor cursor_cloud_agent \
|
|
199
|
+
--work-order .code-mower/work-orders/billing-settings.md \
|
|
200
|
+
--pr OWNER/REPO#124
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
That lets CodeMower.com show `issue -> plan -> work order -> builder run -> PR
|
|
204
|
+
-> reviewer checks -> merge` lineage without receiving source, diffs,
|
|
205
|
+
transcripts, prompts, or issue body text. See
|
|
206
|
+
[docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
|
|
190
207
|
|
|
191
208
|
## Why Not Just Run Codex Or Claude Yourself?
|
|
192
209
|
|
|
@@ -287,13 +304,15 @@ engineer can:
|
|
|
287
304
|
Future builder/orchestrator experiments extend the same loop from "who reviews
|
|
288
305
|
best?" to "which AI builder plus reviewer loop ships best on this product?" See
|
|
289
306
|
[docs/builder-experiments.md](docs/builder-experiments.md) and
|
|
290
|
-
[docs/authoring-intelligence.md](docs/authoring-intelligence.md).
|
|
307
|
+
[docs/authoring-intelligence.md](docs/authoring-intelligence.md). For hosted
|
|
308
|
+
builder provenance, start with
|
|
309
|
+
[docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
|
|
291
310
|
|
|
292
311
|
## Installation Status
|
|
293
312
|
|
|
294
|
-
The current public beta is `v0.5.0-beta.
|
|
313
|
+
The current public beta is `v0.5.0-beta.42` from
|
|
295
314
|
[codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
|
|
296
|
-
as `code-mower==0.5.
|
|
315
|
+
as `code-mower==0.5.0b42` on [PyPI](https://pypi.org/project/code-mower/).
|
|
297
316
|
GitHub releases remain the auditable source for tags, build artifacts, and
|
|
298
317
|
release notes.
|
|
299
318
|
|
|
@@ -88,11 +88,11 @@ Code Mower currently targets Python 3.11+; Python 3.12 is recommended.
|
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
90
|
python3.12 --version
|
|
91
|
-
pipx install --python python3.12 code-mower==0.5.
|
|
91
|
+
pipx install --python python3.12 code-mower==0.5.0b42
|
|
92
92
|
code-mower --version
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
`0.5.
|
|
95
|
+
`0.5.0b42` is a beta release. Until Code Mower publishes a stable `1.0`
|
|
96
96
|
line, use the explicit beta version above or allow prereleases with:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
@@ -107,6 +107,10 @@ code-mower doctor --preflight
|
|
|
107
107
|
code-mower checks detect --json
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
In Git worktrees, `checks detect` also offers `code-mower.pr-size`, a local
|
|
111
|
+
PR-size lint that defaults to 300 changed lines and flags large batches of
|
|
112
|
+
near-identical files.
|
|
113
|
+
|
|
110
114
|
When those look sane, write the generated setup to a reviewable folder and
|
|
111
115
|
produce the starter local report:
|
|
112
116
|
|
|
@@ -116,8 +120,9 @@ code-mower calibration value-report .code-mower.generated/calibration-corpus.jso
|
|
|
116
120
|
--output .code-mower/reviewer-value-report.md
|
|
117
121
|
```
|
|
118
122
|
|
|
119
|
-
The generated
|
|
120
|
-
|
|
123
|
+
The generated setup also includes the configurable owner escalation and weekly
|
|
124
|
+
status digest templates. The generated starter corpus proves the command path.
|
|
125
|
+
To bootstrap a draft from your repository history:
|
|
121
126
|
|
|
122
127
|
```bash
|
|
123
128
|
code-mower calibration auto-discover \
|
|
@@ -135,7 +140,7 @@ First-time command map: [docs/launch-command-surface.md](docs/launch-command-sur
|
|
|
135
140
|
|
|
136
141
|
The current PyPI beta has been rehearsed end-to-end from a clean install:
|
|
137
142
|
[First-User Install Rehearsal](docs/first-user-install-rehearsal.md) records
|
|
138
|
-
the latest 10/10 public-package readiness proof for `code-mower==0.5.
|
|
143
|
+
the latest 10/10 public-package readiness proof for `code-mower==0.5.0b42`.
|
|
139
144
|
|
|
140
145
|
## Optional: Plan Before Coding
|
|
141
146
|
|
|
@@ -169,9 +174,21 @@ code-mower work-order attach-delivery \
|
|
|
169
174
|
--from-github
|
|
170
175
|
```
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
177
|
+
If a hosted builder such as Grok Bot or Cursor Cloud Agents produced the PR,
|
|
178
|
+
record source-free builder provenance too:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
code-mower builder record \
|
|
182
|
+
--provider grok_bot \
|
|
183
|
+
--executor cursor_cloud_agent \
|
|
184
|
+
--work-order .code-mower/work-orders/billing-settings.md \
|
|
185
|
+
--pr OWNER/REPO#124
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
That lets CodeMower.com show `issue -> plan -> work order -> builder run -> PR
|
|
189
|
+
-> reviewer checks -> merge` lineage without receiving source, diffs,
|
|
190
|
+
transcripts, prompts, or issue body text. See
|
|
191
|
+
[docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
|
|
175
192
|
|
|
176
193
|
## Why Not Just Run Codex Or Claude Yourself?
|
|
177
194
|
|
|
@@ -272,13 +289,15 @@ engineer can:
|
|
|
272
289
|
Future builder/orchestrator experiments extend the same loop from "who reviews
|
|
273
290
|
best?" to "which AI builder plus reviewer loop ships best on this product?" See
|
|
274
291
|
[docs/builder-experiments.md](docs/builder-experiments.md) and
|
|
275
|
-
[docs/authoring-intelligence.md](docs/authoring-intelligence.md).
|
|
292
|
+
[docs/authoring-intelligence.md](docs/authoring-intelligence.md). For hosted
|
|
293
|
+
builder provenance, start with
|
|
294
|
+
[docs/builders-grok-cursor.md](docs/builders-grok-cursor.md).
|
|
276
295
|
|
|
277
296
|
## Installation Status
|
|
278
297
|
|
|
279
|
-
The current public beta is `v0.5.0-beta.
|
|
298
|
+
The current public beta is `v0.5.0-beta.42` from
|
|
280
299
|
[codemower-ai/code-mower](https://github.com/codemower-ai/code-mower), published
|
|
281
|
-
as `code-mower==0.5.
|
|
300
|
+
as `code-mower==0.5.0b42` on [PyPI](https://pypi.org/project/code-mower/).
|
|
282
301
|
GitHub releases remain the auditable source for tags, build artifacts, and
|
|
283
302
|
release notes.
|
|
284
303
|
|
|
@@ -17,11 +17,16 @@ import urllib.error
|
|
|
17
17
|
import urllib.request
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
from pathlib import Path
|
|
20
|
-
from typing import Any, Dict, Optional, Pattern, Sequence
|
|
20
|
+
from typing import Any, Callable, Dict, Mapping, Optional, Pattern, Sequence
|
|
21
21
|
from urllib.parse import quote
|
|
22
22
|
import re
|
|
23
23
|
|
|
24
24
|
MIN_ABBREVIATED_SHA_LENGTH = 7
|
|
25
|
+
AUTHOR_EXCLUSION_ENV = "CODE_MOWER_AUTHOR_EXCLUSION_JSON"
|
|
26
|
+
ACTIONS_RUN_MARKER_RE = re.compile(
|
|
27
|
+
r"<!--\s*CODE_MOWER_AUDIT_RUN:\s*run_id=([0-9]+)\s*-->"
|
|
28
|
+
)
|
|
29
|
+
TRUSTED_ACTIONS_AUDIT_EVENTS = frozenset({"pull_request_target"})
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
@dataclass(frozen=True)
|
|
@@ -65,12 +70,22 @@ class LaneConfig:
|
|
|
65
70
|
flags=re.IGNORECASE,
|
|
66
71
|
)
|
|
67
72
|
|
|
73
|
+
def default_comment_authors(self) -> frozenset[str]:
|
|
74
|
+
return _author_csv_set(",".join(self.default_authors))
|
|
75
|
+
|
|
76
|
+
def configured_comment_authors(self) -> frozenset[str]:
|
|
77
|
+
if not self.authors_env_var:
|
|
78
|
+
return frozenset()
|
|
79
|
+
return _author_csv_set(os.environ.get(self.authors_env_var) or "")
|
|
80
|
+
|
|
68
81
|
def comment_authors(self) -> frozenset[str]:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
return self.default_comment_authors() | self.configured_comment_authors()
|
|
83
|
+
|
|
84
|
+
def is_default_comment_author(self, login: str) -> bool:
|
|
85
|
+
return login.strip().lower() in self.default_comment_authors()
|
|
86
|
+
|
|
87
|
+
def is_configured_comment_author(self, login: str) -> bool:
|
|
88
|
+
return login.strip().lower() in self.configured_comment_authors()
|
|
74
89
|
|
|
75
90
|
def github_tokens_from_env(self) -> tuple[GitHubToken, ...]:
|
|
76
91
|
tokens = []
|
|
@@ -84,6 +99,14 @@ class LaneConfig:
|
|
|
84
99
|
return tuple(tokens)
|
|
85
100
|
|
|
86
101
|
|
|
102
|
+
def _author_csv_set(raw_authors: str) -> frozenset[str]:
|
|
103
|
+
return frozenset(
|
|
104
|
+
author.strip().lower()
|
|
105
|
+
for author in raw_authors.split(",")
|
|
106
|
+
if author.strip()
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
87
110
|
class GitHubRequestError(RuntimeError):
|
|
88
111
|
def __init__(self, method: str, path: str, code: int, response_body: str) -> None:
|
|
89
112
|
self.method = method
|
|
@@ -97,6 +120,80 @@ class IssueCommentPaginationLimitExceeded(RuntimeError):
|
|
|
97
120
|
"""Raised when issue comments exceed the configured safe pagination cap."""
|
|
98
121
|
|
|
99
122
|
|
|
123
|
+
def _string_mapping(value: Any) -> dict[str, str]:
|
|
124
|
+
if not isinstance(value, Mapping):
|
|
125
|
+
return {}
|
|
126
|
+
return {
|
|
127
|
+
str(key): str(item)
|
|
128
|
+
for key, item in value.items()
|
|
129
|
+
if str(key) and str(item)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def load_author_exclusion_config(raw: str | None = None) -> Mapping[str, Any]:
|
|
134
|
+
text = raw if raw is not None else os.environ.get(AUTHOR_EXCLUSION_ENV, "")
|
|
135
|
+
if not text:
|
|
136
|
+
return {"enabled": False}
|
|
137
|
+
try:
|
|
138
|
+
parsed = json.loads(text)
|
|
139
|
+
except json.JSONDecodeError:
|
|
140
|
+
return {"enabled": False}
|
|
141
|
+
return parsed if isinstance(parsed, Mapping) else {"enabled": False}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def builder_identity_matches(
|
|
145
|
+
*,
|
|
146
|
+
labels: Sequence[str],
|
|
147
|
+
author: str,
|
|
148
|
+
text: str,
|
|
149
|
+
config: Mapping[str, Any],
|
|
150
|
+
) -> tuple[str, ...]:
|
|
151
|
+
if not bool(config.get("enabled")):
|
|
152
|
+
return ()
|
|
153
|
+
label_map = _string_mapping(config.get("labels"))
|
|
154
|
+
author_map = {
|
|
155
|
+
key.lower(): value
|
|
156
|
+
for key, value in _string_mapping(config.get("authors")).items()
|
|
157
|
+
}
|
|
158
|
+
matches: list[str] = []
|
|
159
|
+
|
|
160
|
+
for label in labels:
|
|
161
|
+
lane = label_map.get(str(label))
|
|
162
|
+
if lane:
|
|
163
|
+
matches.append(lane)
|
|
164
|
+
|
|
165
|
+
lane = author_map.get(author.lower())
|
|
166
|
+
if lane:
|
|
167
|
+
matches.append(lane)
|
|
168
|
+
|
|
169
|
+
return tuple(dict.fromkeys(matches))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def author_exclusion_reason(
|
|
173
|
+
*,
|
|
174
|
+
lane_name: str,
|
|
175
|
+
labels: Sequence[str],
|
|
176
|
+
author: str,
|
|
177
|
+
text: str,
|
|
178
|
+
config: Mapping[str, Any] | None = None,
|
|
179
|
+
) -> str | None:
|
|
180
|
+
exclusion_config = config or load_author_exclusion_config()
|
|
181
|
+
matches = builder_identity_matches(
|
|
182
|
+
labels=labels,
|
|
183
|
+
author=author,
|
|
184
|
+
text=text,
|
|
185
|
+
config=exclusion_config,
|
|
186
|
+
)
|
|
187
|
+
if not matches:
|
|
188
|
+
return None
|
|
189
|
+
if len(set(matches)) > 1:
|
|
190
|
+
return "conflicting builder identity; skipping author-excluded label update"
|
|
191
|
+
builder_lane = matches[0]
|
|
192
|
+
if builder_lane == lane_name:
|
|
193
|
+
return f"{lane_name} lane excluded for builder-authored PR"
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
|
|
100
197
|
def load_json(path: Path) -> Dict[str, Any]:
|
|
101
198
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
102
199
|
|
|
@@ -198,6 +295,130 @@ def github_request_with_fallback(
|
|
|
198
295
|
raise RuntimeError(f"no GitHub tokens available for {method} {path}")
|
|
199
296
|
|
|
200
297
|
|
|
298
|
+
def parse_csv_set(raw: str) -> frozenset[str]:
|
|
299
|
+
return frozenset(item.strip() for item in raw.split(",") if item.strip())
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def parse_audit_run_id(body: str) -> str:
|
|
303
|
+
match = ACTIONS_RUN_MARKER_RE.search(body)
|
|
304
|
+
return match.group(1) if match else ""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def workflow_path_matches(path: str, trusted_workflows: Sequence[str]) -> bool:
|
|
308
|
+
if not path:
|
|
309
|
+
return False
|
|
310
|
+
return any(path == item or path.endswith("/" + item) for item in trusted_workflows)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _pr_item_matches_head(
|
|
314
|
+
item: Mapping[str, Any],
|
|
315
|
+
*,
|
|
316
|
+
issue_number: int,
|
|
317
|
+
head_sha: str,
|
|
318
|
+
fallback_head_sha: str = "",
|
|
319
|
+
) -> bool:
|
|
320
|
+
if str(item.get("number") or "") != str(issue_number):
|
|
321
|
+
return False
|
|
322
|
+
pr_head = str(((item.get("head") or {}).get("sha")) or "")
|
|
323
|
+
if pr_head:
|
|
324
|
+
return sha_matches(pr_head, head_sha)
|
|
325
|
+
return bool(fallback_head_sha) and sha_matches(fallback_head_sha, head_sha)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def fetch_actions_run(
|
|
329
|
+
repo: str,
|
|
330
|
+
run_id: str,
|
|
331
|
+
*,
|
|
332
|
+
tokens: Sequence[GitHubToken],
|
|
333
|
+
) -> dict[str, Any]:
|
|
334
|
+
response = github_request_with_fallback(
|
|
335
|
+
"GET",
|
|
336
|
+
f"/repos/{repo}/actions/runs/{run_id}",
|
|
337
|
+
tokens=tokens,
|
|
338
|
+
)
|
|
339
|
+
return response if isinstance(response, dict) else {}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def fetch_pull_requests_for_commit(
|
|
343
|
+
repo: str,
|
|
344
|
+
head_sha: str,
|
|
345
|
+
*,
|
|
346
|
+
tokens: Sequence[GitHubToken],
|
|
347
|
+
) -> list[dict[str, Any]]:
|
|
348
|
+
response = github_request_with_fallback(
|
|
349
|
+
"GET",
|
|
350
|
+
f"/repos/{repo}/commits/{head_sha}/pulls?per_page=100",
|
|
351
|
+
tokens=tokens,
|
|
352
|
+
)
|
|
353
|
+
return response if isinstance(response, list) else []
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def github_actions_comment_attested(
|
|
357
|
+
*,
|
|
358
|
+
repo: str,
|
|
359
|
+
body: str,
|
|
360
|
+
issue_number: int,
|
|
361
|
+
head_sha: str,
|
|
362
|
+
workflow_paths: Sequence[str],
|
|
363
|
+
tokens: Sequence[GitHubToken],
|
|
364
|
+
actions_run_lookup: Optional[Callable[[str], Mapping[str, Any]]] = None,
|
|
365
|
+
commit_pull_requests_lookup: Optional[Callable[[str], Sequence[Mapping[str, Any]]]] = None,
|
|
366
|
+
) -> bool:
|
|
367
|
+
"""Return whether a github-actions[bot] audit comment is tied to this PR head."""
|
|
368
|
+
trusted_workflows = tuple(parse_csv_set(",".join(workflow_paths)))
|
|
369
|
+
token_list = tuple(token for token in tokens if token.value)
|
|
370
|
+
run_id = parse_audit_run_id(body)
|
|
371
|
+
if not trusted_workflows or not token_list or not run_id or not head_sha:
|
|
372
|
+
return False
|
|
373
|
+
try:
|
|
374
|
+
if actions_run_lookup is None:
|
|
375
|
+
run = fetch_actions_run(repo, run_id, tokens=token_list)
|
|
376
|
+
else:
|
|
377
|
+
run = dict(actions_run_lookup(run_id))
|
|
378
|
+
except Exception:
|
|
379
|
+
return False
|
|
380
|
+
|
|
381
|
+
path = str(run.get("path") or "")
|
|
382
|
+
if not workflow_path_matches(path, trusted_workflows):
|
|
383
|
+
return False
|
|
384
|
+
if str(run.get("event") or "") not in TRUSTED_ACTIONS_AUDIT_EVENTS:
|
|
385
|
+
return False
|
|
386
|
+
|
|
387
|
+
run_head_sha = str(run.get("head_sha") or "")
|
|
388
|
+
pull_requests = run.get("pull_requests")
|
|
389
|
+
if isinstance(pull_requests, list) and pull_requests:
|
|
390
|
+
return any(
|
|
391
|
+
isinstance(item, Mapping)
|
|
392
|
+
and _pr_item_matches_head(
|
|
393
|
+
item,
|
|
394
|
+
issue_number=issue_number,
|
|
395
|
+
head_sha=head_sha,
|
|
396
|
+
fallback_head_sha=run_head_sha,
|
|
397
|
+
)
|
|
398
|
+
for item in pull_requests
|
|
399
|
+
)
|
|
400
|
+
if not run_head_sha or not sha_matches(run_head_sha, head_sha):
|
|
401
|
+
return False
|
|
402
|
+
|
|
403
|
+
try:
|
|
404
|
+
if commit_pull_requests_lookup is None:
|
|
405
|
+
associated_prs = fetch_pull_requests_for_commit(repo, run_head_sha, tokens=token_list)
|
|
406
|
+
else:
|
|
407
|
+
associated_prs = commit_pull_requests_lookup(run_head_sha)
|
|
408
|
+
except Exception:
|
|
409
|
+
return False
|
|
410
|
+
return any(
|
|
411
|
+
isinstance(item, Mapping)
|
|
412
|
+
and _pr_item_matches_head(
|
|
413
|
+
item,
|
|
414
|
+
issue_number=issue_number,
|
|
415
|
+
head_sha=head_sha,
|
|
416
|
+
fallback_head_sha=run_head_sha,
|
|
417
|
+
)
|
|
418
|
+
for item in associated_prs
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
201
422
|
def fetch_pull_request(
|
|
202
423
|
repo: str,
|
|
203
424
|
issue_number: int,
|