maintainability-agent 2.0.0__tar.gz → 2.1.0__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.
- {maintainability_agent-2.0.0/src/maintainability_agent.egg-info → maintainability_agent-2.1.0}/PKG-INFO +2 -2
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/README.md +1 -1
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/pyproject.toml +1 -1
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0/src/maintainability_agent.egg-info}/PKG-INFO +2 -2
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_agent.egg-info/SOURCES.txt +4 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/__init__.py +1 -1
- maintainability_agent-2.1.0/src/maintainability_audit/_conformance.py +183 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/cli.py +113 -10
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/config.py +1 -1
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/git_tools.py +38 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/instructions.py +57 -12
- maintainability_agent-2.1.0/tests/test_added_lines.py +123 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_anticipated_refusals.py +7 -0
- maintainability_agent-2.1.0/tests/test_instruction_targets.py +154 -0
- maintainability_agent-2.1.0/tests/test_scope_conformance.py +299 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/LICENSE +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/setup.cfg +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_agent.egg-info/dependency_links.txt +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_agent.egg-info/entry_points.txt +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_agent.egg-info/requires.txt +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_agent.egg-info/top_level.txt +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/__main__.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_analysis.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_aspects.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_assets/analyzer-catalog.json +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_assets/standard.md +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_backfill.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_bands.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_built_ins.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_calibration.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_catalog.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_charts.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_cognitive.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_config_defaults.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_corroborate.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_coverage_notes.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_derive.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_discovery.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_documents.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_economics.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_economics_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_environment.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_evidence_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_finding_match.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_first_run.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_formula.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_generic.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_grant_ledger.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_history_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_hostile_prompt.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_hotspots.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_html_report_sections.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_html_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_identity.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_jvm_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_markdown_sections.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_masking.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_mcp_audit.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_mcp_gate.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_mcp_grants.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_mcp_refusals.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_mcp_resources.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_mcp_setup.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_metric_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_metrics_types.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_pillars.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_practice.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_pressures.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_c.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_core.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_cpp.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_csharp.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_fortran.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_java.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_ranges_js.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_recurrence.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_runner.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_safe_write.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_scan_history.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_scan_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_second_source.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_selection.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_semantic.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_semantic_policy.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_semantic_ts.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_semantic_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_setup_errors.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_setup_persist.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_data/SKILL.md +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_data/agents/anthropic.yaml +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_data/agents/copilot.yaml +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_data/agents/openai.yaml +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_data/copilot/maintainability-agent.prompt.md +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_data/references/finding-taxonomy.md +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_skill_install.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_stored_grants.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_tdd_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_test_execution.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_test_pairing.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_tokens.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_tool_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_trends.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_user_config.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_verdict_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_verification.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_work_order.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_work_order_view.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_work_order_weights.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/_xml.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/baseline.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/deadcode.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/duplication.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/evidence.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/history.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/idioms.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/mcp_server.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/metrics.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/prompts.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/renderers.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/report.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/sarif.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/scoring.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/similarity.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/source.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_acquisition_trust.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_adapter_docs.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_adapter_recordings.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analysis_coverage.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analysis_execution.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_bridge.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_config_isolation.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_estimate_claims.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_pinning.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_primary_estimate.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_provenance.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_provenance_exclusions.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_scoring_path.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_spread_range.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_analyzer_xml_bounds.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_annotations_match_behaviour.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_architecture.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_artifact_write_route_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_aspect_coverage.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_asset_snapshot_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_ast_reading_resolves_spellings.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_audit_components.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_authorization_freshness.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_authorship_gates.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_backfill.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_band_concealment_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_band_concealment_p3.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_band_pressures.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_bands.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_body_or_signature_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_c_declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_calibration_corpus.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_calibration_drift.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_calibration_tooling.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_catalog_selection.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_catalog_tier_promise.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_chat_primary_docs.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_checkstyle_adapter.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_checkstyle_catalog.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_ci_history_recipe.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_ci_installs_the_analyzer_pool.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_claimed_languages.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_cli.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_clone_group_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_cognitive_complexity.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_cohort_statistics.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_concealment_properties.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_config_shape.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_consent_and_grant.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_consumer_migration.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_contract_claims.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_corroborate.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_coverage_by_language.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_cpp_declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_csharp_declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_d15_composition.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_d15_goal_directed.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_dead_code.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_declaration_edge_cases.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_declaration_grading.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_declaration_ranges.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_declaration_source.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_declared_authorship.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_declared_imports.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_derivation_mix.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_determinism.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_direct_runner_availability_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_discovery.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_doc_claims.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_docs_links.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_economic_context.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_empty_output_not_clean.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_empty_output_not_clean_adapters.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_environment_work_order.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_evidence_invariants.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_evidence_normalization.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_evidence_properties.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_exclusion_dialects.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_falsifier_standard.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_file_list_cap_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_finding_identity.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_first_run_elicitation.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_first_run_prompt.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_first_run_reelicitation.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_fix_breadth_window.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_fixed_form_fortran.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_format_ask.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_fortran_declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_fortran_metrics.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_fortran_practice.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_git_argv.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_git_read_only.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_git_readonly_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_grant_only_user_tier.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_history.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_history_boundary.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_history_manifest.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_history_schema2.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_history_window.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_hostile_prompt.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_html_executive.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_html_section_parity_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_identity_docs.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_identity_resolution.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_idiom_divergence.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_installed_wheel_runtime.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_java_range_guard.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_java_ranges.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_java_scored.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_java_wiring.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_js_complexity_operators.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_js_declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_language_coverage.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_mcp_baseline_payload.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_mcp_history.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_mcp_server.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_migration_1_0.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_near_duplicates.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_network_disclosure.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_one_setup_across_surfaces_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_pattern_budget.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_phase6_claims.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_pillars_and_practice.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_platform_claim.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_pmd_adapter.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_pmd_audit_closures.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_pool_runs_by_config.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_population_floors.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_practice_nested_gates_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_presentation_refusals.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_promises.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_prompt_escalation_once.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_prompt_honesty.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_prompt_location_escalation_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_prove_falsifiers.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_python_declarations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_read_boundary.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_readme_claims.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_recurrence.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_release_plan.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_release_plan_status.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_report_remedy_copy.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_roles_recorded.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_root_grants.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_rubric_ignores_project_lint_config.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_runner.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_sarif.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_scan_history.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_scan_scope.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_scanning.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_schema_fidelity.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_scoring_calibration.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_semantic_policy.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_setup_gate_completeness.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_setup_gate_independent_contract.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_setup_precondition.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_skill_install.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_source_index.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_spotbugs_adapter.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_spotbugs_catalog.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_stage8_contract.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_tdd_structure.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_test_execution_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_three_presentations.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_trends.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_type_fields_not_functions_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_uat_falsifiers.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_unread_code.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_user_config_tier.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_verification_audit_closures.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_verified_grade.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_version_claim.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_wheel_contents.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_work_order.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_work_order_items.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_work_order_selection.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_workflow_supply_chain.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_write_boundary.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_write_route_class.py +0 -0
- {maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/tests/test_written_record.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maintainability-agent
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Maintainability gate and AI remediation prompt generator for CI.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -44,7 +44,7 @@ Dynamic: license-file
|
|
|
44
44
|
**A deterministic, offline maintainability audit whose output is a _bounded
|
|
45
45
|
work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
|
|
46
46
|
says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
|
|
47
|
-
Version **2.
|
|
47
|
+
Version **2.1.0**.
|
|
48
48
|
|
|
49
49
|
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form *and*
|
|
50
50
|
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**A deterministic, offline maintainability audit whose output is a _bounded
|
|
6
6
|
work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
|
|
7
7
|
says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
|
|
8
|
-
Version **2.
|
|
8
|
+
Version **2.1.0**.
|
|
9
9
|
|
|
10
10
|
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form *and*
|
|
11
11
|
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "maintainability-agent"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.1.0"
|
|
8
8
|
description = "Maintainability gate and AI remediation prompt generator for CI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# 3.11, not 3.10. `_discovery`, `_pillars` and `_runner` all import
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: maintainability-agent
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Maintainability gate and AI remediation prompt generator for CI.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -44,7 +44,7 @@ Dynamic: license-file
|
|
|
44
44
|
**A deterministic, offline maintainability audit whose output is a _bounded
|
|
45
45
|
work order_ for an AI coding agent** — a copy-paste prompt, per finding, that
|
|
46
46
|
says *fix exactly these and refactor nothing else*. Chat-primary; CLI for CI.
|
|
47
|
-
Version **2.
|
|
47
|
+
Version **2.1.0**.
|
|
48
48
|
|
|
49
49
|
**Languages parsed:** Python, Java, C, C++, C#, Fortran (free-form *and*
|
|
50
50
|
fixed-form), and the JS/TS family — each by a scanner written for it, and
|
|
@@ -20,6 +20,7 @@ src/maintainability_audit/_catalog.py
|
|
|
20
20
|
src/maintainability_audit/_charts.py
|
|
21
21
|
src/maintainability_audit/_cognitive.py
|
|
22
22
|
src/maintainability_audit/_config_defaults.py
|
|
23
|
+
src/maintainability_audit/_conformance.py
|
|
23
24
|
src/maintainability_audit/_corroborate.py
|
|
24
25
|
src/maintainability_audit/_coverage_notes.py
|
|
25
26
|
src/maintainability_audit/_derive.py
|
|
@@ -121,6 +122,7 @@ tests/test_acquisition_trust.py
|
|
|
121
122
|
tests/test_adapter_docs.py
|
|
122
123
|
tests/test_adapter_recordings.py
|
|
123
124
|
tests/test_adapters.py
|
|
125
|
+
tests/test_added_lines.py
|
|
124
126
|
tests/test_analysis_coverage.py
|
|
125
127
|
tests/test_analysis_execution.py
|
|
126
128
|
tests/test_analyzer_bridge.py
|
|
@@ -225,6 +227,7 @@ tests/test_identity_docs.py
|
|
|
225
227
|
tests/test_identity_resolution.py
|
|
226
228
|
tests/test_idiom_divergence.py
|
|
227
229
|
tests/test_installed_wheel_runtime.py
|
|
230
|
+
tests/test_instruction_targets.py
|
|
228
231
|
tests/test_java_range_guard.py
|
|
229
232
|
tests/test_java_ranges.py
|
|
230
233
|
tests/test_java_scored.py
|
|
@@ -270,6 +273,7 @@ tests/test_scan_history.py
|
|
|
270
273
|
tests/test_scan_scope.py
|
|
271
274
|
tests/test_scanning.py
|
|
272
275
|
tests/test_schema_fidelity.py
|
|
276
|
+
tests/test_scope_conformance.py
|
|
273
277
|
tests/test_scoring_calibration.py
|
|
274
278
|
tests/test_semantic_policy.py
|
|
275
279
|
tests/test_setup_gate_completeness.py
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Did the diff stay inside the work order it was given?
|
|
2
|
+
|
|
3
|
+
The product's central claim is a bounded work order: *fix exactly these
|
|
4
|
+
findings and refactor nothing else*. Until now that bound was an
|
|
5
|
+
**instruction**. The prompt said it, and nothing checked it — so an agent
|
|
6
|
+
that rewrote half the tree while closing one finding produced a diff
|
|
7
|
+
indistinguishable, to this tool, from one that did as it was told.
|
|
8
|
+
|
|
9
|
+
This is the check that makes the bound verifiable. It reads the finished
|
|
10
|
+
report and a revspec, and reports which changed files the work order
|
|
11
|
+
actually named. It is the first of the three mechanisms behind the
|
|
12
|
+
attestation artifact, and the one a code-generating agent cannot honestly
|
|
13
|
+
perform about itself.
|
|
14
|
+
|
|
15
|
+
**What it must not do is cry wolf.** A correct remediation almost always
|
|
16
|
+
touches a file the work order does not name: the test that proves the fix.
|
|
17
|
+
A check that flagged every good remediation would be turned off within a
|
|
18
|
+
week, so a test file is in scope when it pairs to a named path by the same
|
|
19
|
+
convention `_test_pairing` already uses for coverage — `test_foo.py` pairs
|
|
20
|
+
to `foo.py`. A test paired to nothing named is still reported, because
|
|
21
|
+
"while I was here I rewrote the suite" is exactly the drift being watched
|
|
22
|
+
for.
|
|
23
|
+
|
|
24
|
+
Three deliberate limits:
|
|
25
|
+
|
|
26
|
+
- **It never scores.** Whether a diff was obedient is a fact about an
|
|
27
|
+
agent's behaviour, not evidence about the code's condition, so nothing
|
|
28
|
+
here reaches scoring or moves a grade.
|
|
29
|
+
- **It reports, it does not gate**, unless a caller asks it to. A finding
|
|
30
|
+
can legitimately require touching a caller the work order did not name,
|
|
31
|
+
and a check that cannot be argued with becomes a check that is bypassed.
|
|
32
|
+
- **It compares paths, not intent.** A file being in scope says the work
|
|
33
|
+
order named it, not that the change to it was the right one. Review is
|
|
34
|
+
still review.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import re
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
from ._metrics_types import is_test_path
|
|
43
|
+
from ._test_pairing import subject_stem
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _named_paths(work_order: list[dict[str, Any]]) -> set[str]:
|
|
47
|
+
"""Every path the work order put in front of the agent."""
|
|
48
|
+
return {
|
|
49
|
+
str(item["path"]) for item in work_order
|
|
50
|
+
if isinstance(item, dict) and item.get("path")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _pairs_to_named(path: str, named: set[str]) -> bool:
|
|
55
|
+
"""Whether a test file covers something the work order named.
|
|
56
|
+
|
|
57
|
+
Uses the pairing convention the coverage aspect already applies, so a
|
|
58
|
+
remediation's own test is in scope without the caller declaring it —
|
|
59
|
+
and a test that covers nothing in the work order is not.
|
|
60
|
+
"""
|
|
61
|
+
if not is_test_path(path):
|
|
62
|
+
return False
|
|
63
|
+
subject = subject_stem(path)
|
|
64
|
+
if not subject:
|
|
65
|
+
return False
|
|
66
|
+
return any(subject_stem(candidate) == subject for candidate in named)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
#: Markers that switch a checker off rather than satisfy it. Per language,
|
|
70
|
+
#: because the vocabulary differs and a single regex over all of them
|
|
71
|
+
#: matches prose: "# type: ignore" in a docstring explaining the convention
|
|
72
|
+
#: is not a suppression, and neither is this comment.
|
|
73
|
+
#:
|
|
74
|
+
#: Deliberately narrow. A marker here has to be a real directive that a
|
|
75
|
+
#: real tool obeys; guessing wider would report ordinary comments as
|
|
76
|
+
#: evasion, and a check that cries wolf is a check that gets switched off —
|
|
77
|
+
#: the same reasoning as the test-pairing rule above.
|
|
78
|
+
SUPPRESSION_MARKERS: tuple[tuple[str, str], ...] = (
|
|
79
|
+
(r"#\s*noqa\b", "noqa"),
|
|
80
|
+
(r"#\s*type:\s*ignore\b", "type: ignore"),
|
|
81
|
+
(r"#\s*pragma:\s*no\s*cover\b", "pragma: no cover"),
|
|
82
|
+
(r"#\s*nosec\b", "nosec"),
|
|
83
|
+
(r"//\s*eslint-disable", "eslint-disable"),
|
|
84
|
+
(r"/\*\s*eslint-disable", "eslint-disable"),
|
|
85
|
+
(r"//\s*@ts-(ignore|expect-error)\b", "ts-ignore"),
|
|
86
|
+
(r"@SuppressWarnings\b", "SuppressWarnings"),
|
|
87
|
+
(r"//\s*NOSONAR\b", "NOSONAR"),
|
|
88
|
+
(r"#\s*pylint:\s*disable\b", "pylint: disable"),
|
|
89
|
+
(r"@pytest\.mark\.(skip|xfail)\b", "skipped test"),
|
|
90
|
+
(r"@unittest\.skip\b", "skipped test"),
|
|
91
|
+
(r"\bit\.skip\(|\bdescribe\.skip\(", "skipped test"),
|
|
92
|
+
(r"@Disabled\b", "disabled test"),
|
|
93
|
+
(r"@Ignore\b", "ignored test"),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
_SUPPRESSION = tuple(
|
|
97
|
+
(re.compile(pattern, re.IGNORECASE), label) for pattern, label in SUPPRESSION_MARKERS
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def suppressions_added(
|
|
102
|
+
added: dict[str, list[tuple[int, str]]], named: set[str]
|
|
103
|
+
) -> list[dict[str, Any]]:
|
|
104
|
+
"""Suppressions this change introduced, and whether they land on a finding.
|
|
105
|
+
|
|
106
|
+
The failure being watched for: a finding is closed by making it
|
|
107
|
+
invisible rather than by fixing it. Add `# noqa` to the flagged line and
|
|
108
|
+
the next audit reports nothing — the scan goes green over code nobody
|
|
109
|
+
repaired, and scope conformance alone would call that diff obedient,
|
|
110
|
+
because the file it touched is exactly the file the work order named.
|
|
111
|
+
|
|
112
|
+
Only *added* lines are read. A suppression already in the tree is not
|
|
113
|
+
evidence about this change, and treating a decade of accumulated
|
|
114
|
+
directives as newly written would drown the signal that matters.
|
|
115
|
+
|
|
116
|
+
`on_named_path` is that signal: a suppression added to a file the work
|
|
117
|
+
order flagged is the shape of a finding being silenced, while one added
|
|
118
|
+
elsewhere is ordinary engineering that a reviewer may still want to see.
|
|
119
|
+
"""
|
|
120
|
+
found: list[dict[str, Any]] = []
|
|
121
|
+
for path in sorted(added):
|
|
122
|
+
for line_number, text in added[path]:
|
|
123
|
+
for pattern, label in _SUPPRESSION:
|
|
124
|
+
if pattern.search(text):
|
|
125
|
+
found.append({
|
|
126
|
+
"path": path,
|
|
127
|
+
"line": line_number,
|
|
128
|
+
"marker": label,
|
|
129
|
+
"on_named_path": path in named,
|
|
130
|
+
})
|
|
131
|
+
break
|
|
132
|
+
return found
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def scope_conformance(
|
|
136
|
+
report: dict[str, Any],
|
|
137
|
+
changed: set[str],
|
|
138
|
+
revspec: str,
|
|
139
|
+
added: dict[str, list[tuple[int, str]]] | None = None,
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""How a diff relates to the work order the agent was handed.
|
|
142
|
+
|
|
143
|
+
Returns the record, never a verdict on the code. `in_scope`,
|
|
144
|
+
`paired_tests` and `out_of_scope` partition the changed files;
|
|
145
|
+
`unaddressed` names work-order paths the diff did not touch, which is
|
|
146
|
+
not a failure — a bounded change may take one item at a time.
|
|
147
|
+
"""
|
|
148
|
+
work_order = report.get("work_order") or []
|
|
149
|
+
named = _named_paths(work_order)
|
|
150
|
+
|
|
151
|
+
in_scope = sorted(path for path in changed if path in named)
|
|
152
|
+
remaining = {path for path in changed if path not in named}
|
|
153
|
+
paired = sorted(path for path in remaining if _pairs_to_named(path, named))
|
|
154
|
+
out_of_scope = sorted(remaining - set(paired))
|
|
155
|
+
suppressions = suppressions_added(added or {}, named)
|
|
156
|
+
silenced = [item for item in suppressions if item["on_named_path"]]
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"revspec": revspec,
|
|
160
|
+
"work_order_items": len(work_order),
|
|
161
|
+
"named_paths": sorted(named),
|
|
162
|
+
"changed_paths": sorted(changed),
|
|
163
|
+
"in_scope": in_scope,
|
|
164
|
+
"paired_tests": paired,
|
|
165
|
+
"out_of_scope": out_of_scope,
|
|
166
|
+
"unaddressed": sorted(named - set(in_scope)),
|
|
167
|
+
"suppressions_added": suppressions,
|
|
168
|
+
"suppressions_on_named_paths": len(silenced),
|
|
169
|
+
# Two questions, kept apart. `conformant` answers "did it change
|
|
170
|
+
# only what it was asked to"; `clean` also answers "and without
|
|
171
|
+
# switching a checker off in a file that was flagged". Scope alone
|
|
172
|
+
# is satisfiable by adding `# noqa` to the named file and changing
|
|
173
|
+
# nothing else, which is the evasion this pairing exists to catch.
|
|
174
|
+
"conformant": not out_of_scope,
|
|
175
|
+
"clean": not out_of_scope and not silenced,
|
|
176
|
+
# Stated so a reader does not infer more than was checked. The
|
|
177
|
+
# record says which files the work order named, not whether the
|
|
178
|
+
# edits inside them were the right ones.
|
|
179
|
+
"note": (
|
|
180
|
+
"Paths and added lines. A file in scope means the work order "
|
|
181
|
+
"named it, not that the change to it was correct."
|
|
182
|
+
),
|
|
183
|
+
}
|
{maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/cli.py
RENAMED
|
@@ -36,14 +36,43 @@ from .config import (
|
|
|
36
36
|
load_config,
|
|
37
37
|
repository_path,
|
|
38
38
|
)
|
|
39
|
-
from .git_tools import changed_paths
|
|
40
|
-
from .instructions import
|
|
39
|
+
from .git_tools import added_lines, changed_paths
|
|
40
|
+
from .instructions import (
|
|
41
|
+
INSTRUCTION_TARGETS,
|
|
42
|
+
UnknownTarget,
|
|
43
|
+
instruction_path_for_target,
|
|
44
|
+
write_instruction_pack,
|
|
45
|
+
)
|
|
41
46
|
from .prompts import render_agent_instructions, render_ai_prompt, render_hostile_audit_prompt
|
|
42
47
|
from .renderers import render_html, render_markdown, render_pr_comment
|
|
43
48
|
from .report import build_report
|
|
44
49
|
from .sarif import read_sarif_inputs, report_to_sarif
|
|
45
50
|
|
|
46
51
|
|
|
52
|
+
def _add_gate_arguments(parser: argparse.ArgumentParser) -> None:
|
|
53
|
+
"""What can make a run fail, and the conformance record behind one.
|
|
54
|
+
|
|
55
|
+
Split from `add_arguments` when the conformance flags took it past this
|
|
56
|
+
project's own function-length gate — which is the gate doing its job on
|
|
57
|
+
the change that added a gate.
|
|
58
|
+
"""
|
|
59
|
+
parser.add_argument("--fail-on-gate", action="store_true", help="Exit 1 when hard gates fail.")
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--conformance",
|
|
62
|
+
metavar="REVSPEC",
|
|
63
|
+
help="Report how a diff relates to the work order, e.g. `main...HEAD`. "
|
|
64
|
+
"The bounded work order says 'fix exactly these and refactor nothing "
|
|
65
|
+
"else'; without this the bound is an instruction nothing checks. "
|
|
66
|
+
"Reports only unless --fail-on-out-of-scope is passed.",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--fail-on-out-of-scope", action="store_true",
|
|
70
|
+
help="With --conformance, exit 1 when the diff touched files the work "
|
|
71
|
+
"order did not name and that do not pair to one as its test, or "
|
|
72
|
+
"when it added a suppression to a file the work order flagged.",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
47
76
|
def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
48
77
|
parser.add_argument("--version", action="version", version=f"maintainability-agent {VERSION}")
|
|
49
78
|
parser.add_argument("--root", default=".", help="Repository root to scan.")
|
|
@@ -110,7 +139,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
110
139
|
"The shape of a series is what a trend reads, and a thousand "
|
|
111
140
|
"commits is hours of work nobody asked for.",
|
|
112
141
|
)
|
|
113
|
-
parser
|
|
142
|
+
_add_gate_arguments(parser)
|
|
114
143
|
_add_setup_actions(parser)
|
|
115
144
|
|
|
116
145
|
|
|
@@ -133,10 +162,19 @@ def _add_setup_actions(parser: argparse.ArgumentParser) -> None:
|
|
|
133
162
|
"and remove files the package no longer ships. Without it, a "
|
|
134
163
|
"differing copy is refused with the list of differences.",
|
|
135
164
|
)
|
|
165
|
+
parser.add_argument(
|
|
166
|
+
"--target-path",
|
|
167
|
+
action="append",
|
|
168
|
+
metavar="TARGET=PATH",
|
|
169
|
+
help="Where a target's standing instructions go, e.g. "
|
|
170
|
+
"`--target-path bob=.bob/instructions.md`. Required for any agent "
|
|
171
|
+
"this tool has no built-in convention for; overrides the built-in "
|
|
172
|
+
"path for one it does. The target list will always trail the "
|
|
173
|
+
"market, and guessing a path writes a file the agent never opens.",
|
|
174
|
+
)
|
|
136
175
|
parser.add_argument(
|
|
137
176
|
"--target",
|
|
138
177
|
action="append",
|
|
139
|
-
choices=["generic", "claude-code", "codex", "cursor", "copilot", "windsurf"],
|
|
140
178
|
help="Instruction target. Repeatable. Used with --init-agent-standards.",
|
|
141
179
|
)
|
|
142
180
|
parser.add_argument("--instructions-output-dir", default=".", help="Directory for generated instruction files.")
|
|
@@ -188,6 +226,25 @@ def write_outputs(args: argparse.Namespace, report: dict, rendered: str) -> None
|
|
|
188
226
|
json.dumps(report_to_sarif(report), indent=2) + "\n", json_artifact=True)
|
|
189
227
|
|
|
190
228
|
|
|
229
|
+
def attach_conformance(args: argparse.Namespace, report: dict) -> None:
|
|
230
|
+
"""Record how the diff relates to the work order, when asked.
|
|
231
|
+
|
|
232
|
+
Assembly, not scoring: the record is attached to the report and reaches
|
|
233
|
+
no dimension, because whether a diff was obedient is a fact about an
|
|
234
|
+
agent's behaviour and not evidence about the code's condition.
|
|
235
|
+
"""
|
|
236
|
+
if not args.conformance:
|
|
237
|
+
return
|
|
238
|
+
from ._conformance import scope_conformance
|
|
239
|
+
|
|
240
|
+
root = Path(report["root"])
|
|
241
|
+
changed = changed_paths(root, args.conformance)
|
|
242
|
+
added = added_lines(root, args.conformance)
|
|
243
|
+
report["scope_conformance"] = scope_conformance(
|
|
244
|
+
report, changed, args.conformance, added
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
191
248
|
def audit_exit_code(args: argparse.Namespace, report: dict) -> int:
|
|
192
249
|
if args.fail_on_new:
|
|
193
250
|
# Structured matching, never a label-set difference: a label
|
|
@@ -198,6 +255,16 @@ def audit_exit_code(args: argparse.Namespace, report: dict) -> int:
|
|
|
198
255
|
return 1
|
|
199
256
|
if args.fail_on_gate and report["hard_gate_failures"]:
|
|
200
257
|
return 1
|
|
258
|
+
if args.fail_on_out_of_scope:
|
|
259
|
+
record = report.get("scope_conformance")
|
|
260
|
+
if record is None:
|
|
261
|
+
print("--fail-on-out-of-scope needs --conformance REVSPEC", file=sys.stderr)
|
|
262
|
+
return 2
|
|
263
|
+
# `clean`, not `conformant`: staying in scope while silencing the
|
|
264
|
+
# finding is the evasion the pairing exists to catch, and a gate
|
|
265
|
+
# that accepted it would be satisfied by the thing it guards against.
|
|
266
|
+
if not record["clean"]:
|
|
267
|
+
return 1
|
|
201
268
|
return 0
|
|
202
269
|
|
|
203
270
|
|
|
@@ -258,6 +325,35 @@ def _install_skill_action(args: argparse.Namespace) -> int:
|
|
|
258
325
|
return 0
|
|
259
326
|
|
|
260
327
|
|
|
328
|
+
|
|
329
|
+
def _target_paths(pairs: list[str] | None) -> dict[str, str]:
|
|
330
|
+
"""`TARGET=PATH` arguments, as a mapping.
|
|
331
|
+
|
|
332
|
+
An absolute path is refused: the instruction pack is written relative
|
|
333
|
+
to `--instructions-output-dir`, so an absolute one would silently
|
|
334
|
+
ignore the directory the caller named.
|
|
335
|
+
"""
|
|
336
|
+
overrides: dict[str, str] = {}
|
|
337
|
+
for pair in pairs or []:
|
|
338
|
+
target, separator, path = pair.partition("=")
|
|
339
|
+
if not separator or not target.strip() or not path.strip():
|
|
340
|
+
raise ValueError(f"--target-path expects TARGET=PATH, got {pair!r}")
|
|
341
|
+
if Path(path).is_absolute():
|
|
342
|
+
raise ValueError(
|
|
343
|
+
f"--target-path {pair!r} is absolute; paths are relative to "
|
|
344
|
+
"--instructions-output-dir"
|
|
345
|
+
)
|
|
346
|
+
overrides[target.strip()] = path.strip()
|
|
347
|
+
return overrides
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _parse(argv: list[str]) -> tuple[argparse.ArgumentParser, argparse.Namespace]:
|
|
351
|
+
"""The parser comes back too: `main` still needs it for `parser.error`."""
|
|
352
|
+
parser = argparse.ArgumentParser()
|
|
353
|
+
add_arguments(parser)
|
|
354
|
+
return parser, parser.parse_args(argv)
|
|
355
|
+
|
|
356
|
+
|
|
261
357
|
def main(argv: list[str] | None = None) -> int:
|
|
262
358
|
if argv is None:
|
|
263
359
|
argv = sys.argv[1:]
|
|
@@ -266,18 +362,22 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
266
362
|
|
|
267
363
|
return mcp_server.main(argv[1:])
|
|
268
364
|
|
|
269
|
-
parser =
|
|
270
|
-
add_arguments(parser)
|
|
271
|
-
args = parser.parse_args(argv)
|
|
272
|
-
|
|
365
|
+
parser, args = _parse(argv)
|
|
273
366
|
if args.install_skill:
|
|
274
367
|
return _install_skill_action(args)
|
|
275
368
|
|
|
276
369
|
root = Path(args.root).resolve()
|
|
277
370
|
config = _interactive_config(root, args.config)
|
|
278
371
|
if args.init_agent_standards:
|
|
279
|
-
targets = args.target or
|
|
280
|
-
|
|
372
|
+
targets = args.target or list(INSTRUCTION_TARGETS)
|
|
373
|
+
try:
|
|
374
|
+
overrides = _target_paths(args.target_path)
|
|
375
|
+
write_instruction_pack(
|
|
376
|
+
targets, Path(args.instructions_output_dir).resolve(), config, overrides
|
|
377
|
+
)
|
|
378
|
+
except (UnknownTarget, ValueError) as failure:
|
|
379
|
+
print(str(failure), file=sys.stderr)
|
|
380
|
+
return 2
|
|
281
381
|
return 0
|
|
282
382
|
|
|
283
383
|
if args.backfill:
|
|
@@ -327,6 +427,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
327
427
|
# escalated, and only delivered advice is recorded as targeted.
|
|
328
428
|
record_scan_and_attach(report, config, history_path, Path(report["root"]),
|
|
329
429
|
record=bool(record), want_targets=bool(args.prompt_output))
|
|
430
|
+
# Before rendering, so the record is in the report every presentation
|
|
431
|
+
# and every consumer reads, rather than only in the exit code.
|
|
432
|
+
attach_conformance(args, report)
|
|
330
433
|
rendered = _render_presentation(args, report, history_path)
|
|
331
434
|
write_outputs(args, report, rendered)
|
|
332
435
|
mark_repo_seen(root) # completed audit: not a first run now, whatever the gate says (D13)
|
{maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/config.py
RENAMED
|
@@ -11,7 +11,7 @@ from ._config_defaults import DEFAULT_CONFIG, DEFAULT_IDIOM_GROUPS
|
|
|
11
11
|
# still the door every caller comes through.
|
|
12
12
|
__all__ = ["DEFAULT_CONFIG", "DEFAULT_IDIOM_GROUPS", "PROJECT_URL", "VERSION"]
|
|
13
13
|
|
|
14
|
-
VERSION = "2.
|
|
14
|
+
VERSION = "2.1.0"
|
|
15
15
|
|
|
16
16
|
PROJECT_URL = "https://github.com/marshallguillory86/maintainability-agent"
|
|
17
17
|
|
{maintainability_agent-2.0.0 → maintainability_agent-2.1.0}/src/maintainability_audit/git_tools.py
RENAMED
|
@@ -229,6 +229,44 @@ def changed_paths(root: Path, revspec: str) -> set[str]:
|
|
|
229
229
|
return {line.strip().replace(os.sep, "/") for line in output.splitlines() if line.strip()}
|
|
230
230
|
|
|
231
231
|
|
|
232
|
+
def added_lines(root: Path, revspec: str) -> dict[str, list[tuple[int, str]]]:
|
|
233
|
+
"""Lines a revspec *adds*, per path, as (line number, text).
|
|
234
|
+
|
|
235
|
+
Added lines only. A suppression that was already in the tree is not
|
|
236
|
+
evidence about this change, and reading whole files instead of the diff
|
|
237
|
+
would report a decade of accumulated `# noqa` as though this agent had
|
|
238
|
+
just written them.
|
|
239
|
+
|
|
240
|
+
`--unified=0` keeps context lines out, so every `+` is genuinely new.
|
|
241
|
+
The revspec is validated and `--` terminates the revision list, as in
|
|
242
|
+
`changed_paths`: nothing after it can be read as a path or an option.
|
|
243
|
+
"""
|
|
244
|
+
# `--no-ext-diff` is load-bearing, not defensive. The read-only config
|
|
245
|
+
# this module runs under neutralises `diff.external`, which costs
|
|
246
|
+
# nothing for `--name-only` because no diff is produced — and kills a
|
|
247
|
+
# content diff outright, because git then tries to execute the empty
|
|
248
|
+
# string: "cannot run : No such file or directory".
|
|
249
|
+
output = run_git(
|
|
250
|
+
["diff", "--no-ext-diff", "--unified=0", validate_revspec(revspec), "--"], root
|
|
251
|
+
)
|
|
252
|
+
added: dict[str, list[tuple[int, str]]] = {}
|
|
253
|
+
path = ""
|
|
254
|
+
line_number = 0
|
|
255
|
+
for line in output.splitlines():
|
|
256
|
+
if line.startswith("+++ b/"):
|
|
257
|
+
path = line[6:].strip().replace(os.sep, "/")
|
|
258
|
+
elif line.startswith("@@"):
|
|
259
|
+
# @@ -old,count +new,count @@
|
|
260
|
+
marker = line.split("+", 1)[1].split("@@", 1)[0].strip()
|
|
261
|
+
start = marker.split(",", 1)[0]
|
|
262
|
+
line_number = int(start) if start.lstrip("-").isdigit() else 0
|
|
263
|
+
elif line.startswith("+") and not line.startswith("+++"):
|
|
264
|
+
if path:
|
|
265
|
+
added.setdefault(path, []).append((line_number, line[1:]))
|
|
266
|
+
line_number += 1
|
|
267
|
+
return added
|
|
268
|
+
|
|
269
|
+
|
|
232
270
|
def worktree_status(root: Path) -> str | None:
|
|
233
271
|
"""Porcelain status, or `None` when git cannot answer.
|
|
234
272
|
|
|
@@ -81,19 +81,64 @@ def instruction_body(target: str, config: dict[str, Any]) -> str:
|
|
|
81
81
|
return "\n".join(lines)
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
#: Each agent and the file it actually reads for repo standing
|
|
85
|
+
#: instructions. One declaration, because this list was written out three
|
|
86
|
+
#: times — here, and twice in `cli.py` as `choices` and as the default —
|
|
87
|
+
#: so adding a target meant remembering all three or shipping one the CLI
|
|
88
|
+
#: refused to accept.
|
|
89
|
+
#:
|
|
90
|
+
#: A target belongs here only when the path it writes is the path that
|
|
91
|
+
#: agent genuinely reads. Emitting a plausible-looking file an agent never
|
|
92
|
+
#: opens is worse than having no target: it looks configured and does
|
|
93
|
+
#: nothing.
|
|
94
|
+
INSTRUCTION_TARGETS: dict[str, str] = {
|
|
95
|
+
"generic": "AI-MAINTAINABILITY.md",
|
|
96
|
+
"claude-code": "CLAUDE.md",
|
|
97
|
+
"codex": "AGENTS.md",
|
|
98
|
+
"cursor": ".cursor/rules/maintainability.mdc",
|
|
99
|
+
"copilot": ".github/copilot-instructions.md",
|
|
100
|
+
"windsurf": ".windsurf/rules/maintainability.md",
|
|
101
|
+
}
|
|
94
102
|
|
|
95
103
|
|
|
96
|
-
|
|
104
|
+
class UnknownTarget(ValueError):
|
|
105
|
+
"""A target with no built-in convention and no path supplied."""
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def instruction_path_for_target(
|
|
109
|
+
target: str, output_dir: Path, overrides: dict[str, str] | None = None
|
|
110
|
+
) -> Path:
|
|
111
|
+
"""Where this agent's standing instructions go.
|
|
112
|
+
|
|
113
|
+
An override wins over the built-in table, because a convention can
|
|
114
|
+
change or be site-specific, and the person running the command knows
|
|
115
|
+
their agent better than this table does.
|
|
116
|
+
|
|
117
|
+
A target nobody has a convention for is **refused** rather than given a
|
|
118
|
+
generated filename. The fallback that used to sit here —
|
|
119
|
+
``{target}-maintainability.md`` — was unreachable anyway, because the
|
|
120
|
+
CLI's `choices` rejected unknown targets first; and had it been
|
|
121
|
+
reachable it would have written a plausible-looking file the agent
|
|
122
|
+
never opens, which is worse than no target at all. It looks configured
|
|
123
|
+
and does nothing.
|
|
124
|
+
"""
|
|
125
|
+
if overrides and target in overrides:
|
|
126
|
+
return output_dir / overrides[target]
|
|
127
|
+
if target in INSTRUCTION_TARGETS:
|
|
128
|
+
return output_dir / INSTRUCTION_TARGETS[target]
|
|
129
|
+
raise UnknownTarget(
|
|
130
|
+
f"no known instructions path for {target!r}. Pass "
|
|
131
|
+
f"--target-path {target}=PATH to say where that agent reads its "
|
|
132
|
+
f"standing instructions. Known targets: {', '.join(INSTRUCTION_TARGETS)}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def write_instruction_pack(
|
|
137
|
+
targets: list[str],
|
|
138
|
+
output_dir: Path,
|
|
139
|
+
config: dict[str, Any],
|
|
140
|
+
overrides: dict[str, str] | None = None,
|
|
141
|
+
) -> list[str]:
|
|
97
142
|
# Each instruction file is a product-artifact write: `output_dir`
|
|
98
143
|
# comes from a person (`--instructions-output-dir`), so the write is
|
|
99
144
|
# bound to that directory and refuses a symlinked route the audited
|
|
@@ -102,7 +147,7 @@ def write_instruction_pack(targets: list[str], output_dir: Path, config: dict[st
|
|
|
102
147
|
|
|
103
148
|
written: list[str] = []
|
|
104
149
|
for target in targets:
|
|
105
|
-
path = instruction_path_for_target(target, output_dir)
|
|
150
|
+
path = instruction_path_for_target(target, output_dir, overrides)
|
|
106
151
|
write_artifact(output_dir, path, instruction_body(target, config) + "\n")
|
|
107
152
|
written.append(str(path))
|
|
108
153
|
return written
|