pi-dev-team 0.2.0
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.
- package/LICENSE +21 -0
- package/PORTING.md +134 -0
- package/README.md +207 -0
- package/UPSTREAM.json +64 -0
- package/agents/Explore.md +15 -0
- package/agents/a11y-review.md +118 -0
- package/agents/adr-author.md +70 -0
- package/agents/ai-provenance-review.md +120 -0
- package/agents/angular-reactivity-review.md +95 -0
- package/agents/arch-review.md +135 -0
- package/agents/architect.md +78 -0
- package/agents/autoship-batch-proposer.md +69 -0
- package/agents/claude-setup-review.md +136 -0
- package/agents/codebase-recon.md +184 -0
- package/agents/component-architecture-review.md +119 -0
- package/agents/concurrency-review.md +109 -0
- package/agents/correctness-review.md +290 -0
- package/agents/data-flow-tracer.md +120 -0
- package/agents/doc-review.md +165 -0
- package/agents/domain-review.md +136 -0
- package/agents/general-purpose.md +10 -0
- package/agents/gherkin-quality-critic.md +113 -0
- package/agents/js-fp-review.md +114 -0
- package/agents/mutation-kill.md +684 -0
- package/agents/naming-review.md +142 -0
- package/agents/orchestrator.md +339 -0
- package/agents/performance-review.md +105 -0
- package/agents/plan-review-acceptance.md +115 -0
- package/agents/plan-review-design.md +90 -0
- package/agents/plan-review-parallelization.md +84 -0
- package/agents/plan-review-strategic.md +96 -0
- package/agents/plan-review-ux.md +110 -0
- package/agents/platform-engineer.md +64 -0
- package/agents/product-manager.md +68 -0
- package/agents/progress-guardian.md +79 -0
- package/agents/qa-engineer.md +289 -0
- package/agents/quality-reviewer.md +132 -0
- package/agents/react-reactivity-review.md +102 -0
- package/agents/refactor-opportunity-review.md +128 -0
- package/agents/security-engineer.md +60 -0
- package/agents/security-review.md +218 -0
- package/agents/session-analysis.md +95 -0
- package/agents/software-engineer.md +105 -0
- package/agents/spec-compliance-review.md +100 -0
- package/agents/spec-reviewer.md +114 -0
- package/agents/structure-review.md +146 -0
- package/agents/tech-writer.md +84 -0
- package/agents/test-review.md +246 -0
- package/agents/test-smell-review.md +188 -0
- package/agents/token-efficiency-review.md +139 -0
- package/agents/ui-ux-designer.md +54 -0
- package/agents/vue-reactivity-review.md +95 -0
- package/bin/__pycache__/claudecpython-314.pyc +0 -0
- package/bin/claude +258 -0
- package/docs/upstream/.pages +1 -0
- package/docs/upstream/CHANGELOG.md +2586 -0
- package/docs/upstream/README.md +155 -0
- package/docs/upstream/agent-architecture.md +214 -0
- package/docs/upstream/agent_info.md +187 -0
- package/docs/upstream/artifact-migration.md +124 -0
- package/docs/upstream/code-intelligence-nudge.md +149 -0
- package/docs/upstream/code-review-process.md +294 -0
- package/docs/upstream/concurrent-use.md +73 -0
- package/docs/upstream/context-management.md +111 -0
- package/docs/upstream/developer-notes.md +280 -0
- package/docs/upstream/diagrams/architecture-overview.svg +101 -0
- package/docs/upstream/diagrams/review-dispatch.svg +139 -0
- package/docs/upstream/diagrams/team-agents.svg +128 -0
- package/docs/upstream/diagrams/test-improve-flow.svg +166 -0
- package/docs/upstream/diagrams/workflow-linear.svg +66 -0
- package/docs/upstream/diagrams/workflow-three-phase.svg +200 -0
- package/docs/upstream/eval-maintenance.md +95 -0
- package/docs/upstream/eval-running-guide.md +147 -0
- package/docs/upstream/eval-system.md +291 -0
- package/docs/upstream/session-review-oss-complements.md +75 -0
- package/docs/upstream/session-review.md +212 -0
- package/docs/upstream/skills.md +188 -0
- package/docs/upstream/team-structure.md +21 -0
- package/docs/upstream/telemetry-ci-access.md +129 -0
- package/docs/upstream/telemetry-repo-security.md +120 -0
- package/docs/upstream/test-evaluation.md +277 -0
- package/docs/upstream/test-improve.md +154 -0
- package/docs/upstream/triage-workflow.md +282 -0
- package/docs/upstream/workflows.md +289 -0
- package/extensions/dev-team/index.ts +539 -0
- package/extensions/dev-team/lib/agents.ts +272 -0
- package/extensions/dev-team/lib/ai-credits.ts +92 -0
- package/extensions/dev-team/lib/autocompact.ts +81 -0
- package/extensions/dev-team/lib/child-run.ts +102 -0
- package/extensions/dev-team/lib/config.ts +236 -0
- package/extensions/dev-team/lib/gh-command.ts +103 -0
- package/extensions/dev-team/lib/github-style.ts +307 -0
- package/extensions/dev-team/lib/hooks.ts +350 -0
- package/extensions/dev-team/lib/metrics.ts +115 -0
- package/extensions/dev-team/lib/safe-read.ts +49 -0
- package/extensions/dev-team/lib/session-files.ts +57 -0
- package/extensions/dev-team/lib/session-spend.ts +123 -0
- package/extensions/dev-team/lib/shell-scan.ts +205 -0
- package/extensions/dev-team/lib/skills.ts +213 -0
- package/extensions/dev-team/lib/subagent-render.ts +245 -0
- package/extensions/dev-team/lib/subagent-types.ts +164 -0
- package/extensions/dev-team/lib/subagent.ts +596 -0
- package/extensions/dev-team/lib/terminal-text.ts +54 -0
- package/extensions/dev-team/lib/tools-misc.ts +152 -0
- package/extensions/dev-team/lib/transcript.ts +110 -0
- package/extensions/dev-team/lib/trust.ts +52 -0
- package/extensions/dev-team/lib/usage-breakdown.ts +176 -0
- package/extensions/dev-team/lib/usage-chart.ts +153 -0
- package/extensions/dev-team/lib/usage-command.ts +107 -0
- package/extensions/dev-team/lib/usage-history.ts +203 -0
- package/extensions/dev-team/lib/usage-render.ts +225 -0
- package/extensions/dev-team/lib/usage-split-bar.ts +127 -0
- package/extensions/dev-team/lib/usage-state.ts +116 -0
- package/extensions/dev-team/lib/usage-text.ts +159 -0
- package/extensions/dev-team/lib/usage-view.ts +109 -0
- package/hooks/__pycache__/refactor_test_freeze_guard.cpython-314.pyc +0 -0
- package/hooks/agent_dispatch_ledger.py +190 -0
- package/hooks/autocompact_setup_nudge.py +99 -0
- package/hooks/bash_retry_guard.py +228 -0
- package/hooks/boundary_events_write_guard.py +352 -0
- package/hooks/code_intelligence_nudge.py +293 -0
- package/hooks/code_intelligence_turn_mark.py +317 -0
- package/hooks/codegraph_bootstrap.py +139 -0
- package/hooks/contract_version_guard.py +362 -0
- package/hooks/cost_meter.py +106 -0
- package/hooks/destructive-commands.json +62 -0
- package/hooks/destructive_guard.py +477 -0
- package/hooks/eval_compliance_check.py +440 -0
- package/hooks/guards.json +17 -0
- package/hooks/hooks.json +323 -0
- package/hooks/internal_double_gate.py +296 -0
- package/hooks/js_fp_review.py +212 -0
- package/hooks/knowledge_index.py +119 -0
- package/hooks/lib/__pycache__/artifact_paths.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/atomic_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/autocompact_config.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/boundary_events.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/doc_classification.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/gh_pr_create_detect.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/git_safe_diff.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/instrument_log.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/metrics_query.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/plugin_version.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/pre_commit_doc_classifier.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_agent_registry.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_corroboration.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_hash.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_verdicts.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stdin_json.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stryker_invocation.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/telemetry_consent.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/test_file_classify.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/token_efficiency_limits.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/verify_guard_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/xunit_v3_operator_gate.cpython-314.pyc +0 -0
- package/hooks/lib/agent_skill_hints.py +74 -0
- package/hooks/lib/artifact_paths.py +263 -0
- package/hooks/lib/atomic_state.py +557 -0
- package/hooks/lib/autocompact_config.py +103 -0
- package/hooks/lib/autoship_log.py +106 -0
- package/hooks/lib/banned_scripts_policy.py +51 -0
- package/hooks/lib/boundary_events.py +436 -0
- package/hooks/lib/build_knowledge_index.py +504 -0
- package/hooks/lib/build_skills_index.py +361 -0
- package/hooks/lib/build_state.py +116 -0
- package/hooks/lib/classify_ship_outcome.py +126 -0
- package/hooks/lib/config_changelog_schema.py +115 -0
- package/hooks/lib/cost_meter.py +955 -0
- package/hooks/lib/doc_classification.py +116 -0
- package/hooks/lib/gh_pr_create_detect.py +136 -0
- package/hooks/lib/git_safe_diff.py +123 -0
- package/hooks/lib/instrument_log.py +66 -0
- package/hooks/lib/iteration_journal_gate.py +197 -0
- package/hooks/lib/knowledge_index_paths.py +88 -0
- package/hooks/lib/mcp_json_repowise.py +177 -0
- package/hooks/lib/metrics_query.py +202 -0
- package/hooks/lib/minimal_yaml.py +434 -0
- package/hooks/lib/plugin_version.py +142 -0
- package/hooks/lib/pre_commit_detect.py +537 -0
- package/hooks/lib/pre_commit_doc_classifier.py +126 -0
- package/hooks/lib/pricing.py +118 -0
- package/hooks/lib/report_pdf.py +371 -0
- package/hooks/lib/review_agent_registry.py +142 -0
- package/hooks/lib/review_dispatch_ledger.py +101 -0
- package/hooks/lib/review_gate_corroboration.py +521 -0
- package/hooks/lib/review_gate_hash.py +252 -0
- package/hooks/lib/review_gate_normalized_hash.py +1115 -0
- package/hooks/lib/review_verdicts.py +301 -0
- package/hooks/lib/run_report.py +160 -0
- package/hooks/lib/skill_categories.yaml +125 -0
- package/hooks/lib/stdin_json.py +57 -0
- package/hooks/lib/stryker_invocation.py +102 -0
- package/hooks/lib/telemetry_consent.py +41 -0
- package/hooks/lib/telemetry_report.py +108 -0
- package/hooks/lib/test_file_classify.py +160 -0
- package/hooks/lib/token_efficiency_limits.py +51 -0
- package/hooks/lib/turn_identity.py +77 -0
- package/hooks/lib/verify_guard_state.py +110 -0
- package/hooks/lib/workflow_state.py +206 -0
- package/hooks/lib/xunit_v3_operator_gate.py +596 -0
- package/hooks/mcp_json_repowise_nudge.py +74 -0
- package/hooks/mutation_adapters/__init__.py +7 -0
- package/hooks/mutation_adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/lib.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/mutmut.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/pitest.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker_net.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/lib.py +478 -0
- package/hooks/mutation_adapters/mutmut.py +188 -0
- package/hooks/mutation_adapters/pitest.py +266 -0
- package/hooks/mutation_adapters/stryker.py +157 -0
- package/hooks/mutation_adapters/stryker_net.py +264 -0
- package/hooks/mutation_gate.py +193 -0
- package/hooks/mutation_testing_smoke_gate.py +371 -0
- package/hooks/pending_review_notify.py +121 -0
- package/hooks/phase_marker.py +138 -0
- package/hooks/post_compact_state_reinject.py +180 -0
- package/hooks/post_format.py +115 -0
- package/hooks/pre_commit_knowledge_index.py +128 -0
- package/hooks/pre_commit_review.py +66 -0
- package/hooks/pre_pr_review.py +694 -0
- package/hooks/pre_tool_guard.py +405 -0
- package/hooks/py.sh +73 -0
- package/hooks/refactor-bash-write-patterns.json +29 -0
- package/hooks/refactor_test_bash_guard.py +253 -0
- package/hooks/refactor_test_freeze_guard.py +139 -0
- package/hooks/refactor_test_revert_guard.py +186 -0
- package/hooks/repo_review_nudge.py +287 -0
- package/hooks/review_verdict_recorder.py +464 -0
- package/hooks/scan_bash_command_for_banned_scripts.py +428 -0
- package/hooks/scan_worktree_for_banned_scripts.py +238 -0
- package/hooks/session_learning_trigger.py +248 -0
- package/hooks/skills_index.py +126 -0
- package/hooks/stryker_xunit_shim_guard.py +571 -0
- package/hooks/subagent_completion_guard.py +309 -0
- package/hooks/subagent_skill_context.py +139 -0
- package/hooks/task_completion_metrics.py +216 -0
- package/hooks/tdd_guard.py +229 -0
- package/hooks/telemetry.py +341 -0
- package/hooks/token_efficiency_review.py +194 -0
- package/hooks/verify_guard.py +183 -0
- package/hooks/verify_guard_edit_marker.py +73 -0
- package/hooks/version_check.py +173 -0
- package/knowledge/accepted-risks-schema.md +98 -0
- package/knowledge/adr-decision-criteria.md +64 -0
- package/knowledge/adversarial-review-protocol.md +139 -0
- package/knowledge/agent-registry.md +228 -0
- package/knowledge/agent-review-methodology.md +80 -0
- package/knowledge/ai-friendly-repo-guidelines.md +67 -0
- package/knowledge/architecture-assessment.md +96 -0
- package/knowledge/artifact-lifecycle.md +57 -0
- package/knowledge/cd-maturity-model.md +82 -0
- package/knowledge/cd-test-architecture.md +190 -0
- package/knowledge/ci-cd-file-scope.md +24 -0
- package/knowledge/codegraph-vs-graphify.md +192 -0
- package/knowledge/component-test-patterns.md +139 -0
- package/knowledge/database-change-management.md +80 -0
- package/knowledge/database-test-patterns.md +79 -0
- package/knowledge/decision-defaults.md +88 -0
- package/knowledge/dependency-breaking-techniques.md +116 -0
- package/knowledge/deployment-pipeline.md +86 -0
- package/knowledge/design-smells.md +122 -0
- package/knowledge/directory-enumeration.md +38 -0
- package/knowledge/domain-modeling.md +123 -0
- package/knowledge/evidence-bundle.md +90 -0
- package/knowledge/exploratory-testing-field-guide.md +122 -0
- package/knowledge/failure-routing.md +28 -0
- package/knowledge/fixture-construction.md +56 -0
- package/knowledge/frontend-component-architecture.md +139 -0
- package/knowledge/gherkin-quality-review-dispatch.md +135 -0
- package/knowledge/index.json +6766 -0
- package/knowledge/internal-collaborator-doubling.md +101 -0
- package/knowledge/legacy-test-strategy.md +71 -0
- package/knowledge/long-run-waiting.md +66 -0
- package/knowledge/microservice-testing.md +71 -0
- package/knowledge/model-pricing.json +23 -0
- package/knowledge/mutation-score-formulas.md +60 -0
- package/knowledge/object-calisthenics.md +147 -0
- package/knowledge/oracle-provenance.md +94 -0
- package/knowledge/orchestrator-script-implementation.md +185 -0
- package/knowledge/owasp-detection.md +148 -0
- package/knowledge/plan-review-rubric.md +56 -0
- package/knowledge/proxy-connectivity.md +62 -0
- package/knowledge/reactive-effect-patterns.md +73 -0
- package/knowledge/recon-inventory-excludes.txt +32 -0
- package/knowledge/references/bdd-value-guide.md +61 -0
- package/knowledge/references/csharp-http-client-testing.md +264 -0
- package/knowledge/release-strategies.md +74 -0
- package/knowledge/report-output-location.md +117 -0
- package/knowledge/report-pdf-integration.md +63 -0
- package/knowledge/report-print.css +129 -0
- package/knowledge/report-template.md +114 -0
- package/knowledge/report-to-pdf.md +69 -0
- package/knowledge/request-processing-flow.md +63 -0
- package/knowledge/result-verification.md +52 -0
- package/knowledge/review-agent-output-contract.md +121 -0
- package/knowledge/review-lens-classification.md +113 -0
- package/knowledge/review-rubric.md +62 -0
- package/knowledge/review-template.md +104 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/negative.js +1 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/positive.js +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/negative.py +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/positive.py +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/positive.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/negative.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/negative.cs +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/positive.cs +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/negative.js +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/negative.java +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/positive.java +1 -0
- package/knowledge/schemas/disposition-register-v1.json +65 -0
- package/knowledge/schemas/recon-envelope-v1.json +198 -0
- package/knowledge/schemas/unified-finding-v1.json +72 -0
- package/knowledge/security-primitives-contract.md +301 -0
- package/knowledge/security-review-rule-map.yaml +107 -0
- package/knowledge/skills-registry.md +72 -0
- package/knowledge/task-size-classifier.md +103 -0
- package/knowledge/telemetry-schema.md +881 -0
- package/knowledge/test-automation-maturity.md +56 -0
- package/knowledge/test-automation-principles.md +71 -0
- package/knowledge/test-cadence-tradeoffs.md +68 -0
- package/knowledge/test-doubles.md +105 -0
- package/knowledge/test-file-indicators.md +22 -0
- package/knowledge/test-layer-gates.md +35 -0
- package/knowledge/test-matrix-examples/django-batch.md +24 -0
- package/knowledge/test-matrix-examples/dotnet-grpc-fronting-api.md +90 -0
- package/knowledge/test-matrix-examples/dotnet-http-consumer.md +131 -0
- package/knowledge/test-matrix-examples/react-node-spa.md +24 -0
- package/knowledge/test-matrix-examples/spring-boot-service.md +25 -0
- package/knowledge/test-matrix-examples/ssr-htmx.md +24 -0
- package/knowledge/test-organization.md +70 -0
- package/knowledge/test-pyramid.md +84 -0
- package/knowledge/test-refactoring.md +67 -0
- package/knowledge/test-review-division-of-labor.md +85 -0
- package/knowledge/test-smells.md +80 -0
- package/knowledge/test-stack-profiles/bdd-frameworks.md +235 -0
- package/knowledge/test-stack-profiles/django.md +13 -0
- package/knowledge/test-stack-profiles/dotnet.md +18 -0
- package/knowledge/test-stack-profiles/go.md +16 -0
- package/knowledge/test-stack-profiles/node.md +16 -0
- package/knowledge/test-stack-profiles/react.md +12 -0
- package/knowledge/test-stack-profiles/spring-boot.md +16 -0
- package/knowledge/test-stack-profiles/ssr-htmx.md +14 -0
- package/knowledge/test-stack-profiles/vue.md +12 -0
- package/knowledge/test-strategy.md +70 -0
- package/knowledge/testability-patterns.md +240 -0
- package/knowledge/testing-quadrants.md +44 -0
- package/knowledge/testing-techniques/approval.md +15 -0
- package/knowledge/testing-techniques/chaos.md +17 -0
- package/knowledge/testing-techniques/fuzz.md +15 -0
- package/knowledge/testing-techniques/property-based.md +15 -0
- package/knowledge/testing-techniques/schema-validation.md +15 -0
- package/knowledge/testing-techniques/screenshot.md +15 -0
- package/knowledge/three-phase-workflow.md +198 -0
- package/knowledge/value-patterns.md +55 -0
- package/knowledge/verification-mode.md +116 -0
- package/knowledge/virtual-service-libraries.md +75 -0
- package/knowledge/wave-consolidation-guidance.md +21 -0
- package/overrides/agents/Explore.md +15 -0
- package/overrides/agents/general-purpose.md +10 -0
- package/overrides/notes/autoship.md +6 -0
- package/overrides/notes/issues-from-assessment.md +3 -0
- package/overrides/notes/issues-from-plan.md +3 -0
- package/overrides/notes/mutation-night-watch.md +3 -0
- package/overrides/notes/mutation-testing.md +3 -0
- package/overrides/notes/pr.md +7 -0
- package/overrides/notes/project-init.md +6 -0
- package/overrides/notes/setup.md +13 -0
- package/overrides/notes/specs.md +3 -0
- package/overrides/skills/headless-run/SKILL.md +45 -0
- package/overrides/skills/upgrade/SKILL.md +30 -0
- package/overrides/skills/version/SKILL.md +25 -0
- package/package.json +36 -0
- package/scripts/authoring_digest.py +93 -0
- package/scripts/autoship_discover.py +121 -0
- package/scripts/autoship_group.py +409 -0
- package/scripts/autoship_proposals.py +494 -0
- package/scripts/autoship_queue.py +291 -0
- package/scripts/autoship_reclaim.py +495 -0
- package/scripts/build_jobs.py +108 -0
- package/scripts/build_rollback_point.py +240 -0
- package/scripts/build_slice_scope.py +157 -0
- package/scripts/build_wave.py +109 -0
- package/scripts/build_wave_reconcile.py +252 -0
- package/scripts/build_worktree_baseref.py +113 -0
- package/scripts/check_agent_scope.py +117 -0
- package/scripts/check_agent_tool_mapping.py +213 -0
- package/scripts/check_review_agent_mcp_tools.py +317 -0
- package/scripts/check_security_assessment_mcp_tools.py +165 -0
- package/scripts/checkpoint_abort.py +502 -0
- package/scripts/claude_setup_review.py +438 -0
- package/scripts/codebase_recon.py +556 -0
- package/scripts/coverage_config.py +623 -0
- package/scripts/coverage_delta_steering.py +330 -0
- package/scripts/coverage_discovery_dotnet.py +315 -0
- package/scripts/coverage_discovery_java.py +742 -0
- package/scripts/coverage_discovery_js.py +546 -0
- package/scripts/coverage_gap_ranking.py +556 -0
- package/scripts/coverage_readiness.py +455 -0
- package/scripts/coverage_report_parse.py +521 -0
- package/scripts/detect_bdd_convention.py +252 -0
- package/scripts/eval_ablation.py +376 -0
- package/scripts/gherkin_analysis_coverage_gate.py +306 -0
- package/scripts/gherkin_cross_feature_duplicate_titles_gate.py +173 -0
- package/scripts/gherkin_effectiveness_rollup.py +238 -0
- package/scripts/gherkin_failure_path_gate.py +206 -0
- package/scripts/gherkin_feature_merge.py +720 -0
- package/scripts/gherkin_stub_gate.py +163 -0
- package/scripts/gherkin_stub_merge.py +479 -0
- package/scripts/git_origin_host.py +88 -0
- package/scripts/install-java-static-analysis.py +110 -0
- package/scripts/issue_deps.py +74 -0
- package/scripts/lib/_bdd_markers.py +28 -0
- package/scripts/lib/_gherkin_text.py +93 -0
- package/scripts/lib/_vendored_tree.py +70 -0
- package/scripts/lib/autoship_state.py +397 -0
- package/scripts/lib/claude_md_guard.py +226 -0
- package/scripts/lib/deterministic_recon.py +446 -0
- package/scripts/lib/mcp_tool_grants.py +211 -0
- package/scripts/lib/plan_parse.py +386 -0
- package/scripts/lib/review_result.py +84 -0
- package/scripts/lib/review_roster.py +86 -0
- package/scripts/lib/session_log/__init__.py +34 -0
- package/scripts/lib/session_log/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/__pycache__/records.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/classify.py +231 -0
- package/scripts/lib/session_log/corrections.py +194 -0
- package/scripts/lib/session_log/discovery.py +108 -0
- package/scripts/lib/session_log/records.py +218 -0
- package/scripts/lib/session_log/redact.py +76 -0
- package/scripts/lib/session_log/signals.py +373 -0
- package/scripts/lib/session_report_downstream.py +614 -0
- package/scripts/lib/session_report_maintainer.py +1273 -0
- package/scripts/lib/session_report_shared.py +262 -0
- package/scripts/lib/settings_hook_guard.py +157 -0
- package/scripts/lib/slug.py +33 -0
- package/scripts/lib/stub_extractors/__init__.py +82 -0
- package/scripts/lib/stub_extractors/_common.py +328 -0
- package/scripts/lib/stub_extractors/csharp.py +19 -0
- package/scripts/lib/stub_extractors/go.py +173 -0
- package/scripts/lib/stub_extractors/java.py +18 -0
- package/scripts/lib/stub_extractors/jsts.py +126 -0
- package/scripts/mutation_stack_sections.py +149 -0
- package/scripts/mutation_yield_steering.py +345 -0
- package/scripts/orchestrator.py +895 -0
- package/scripts/plan_gherkin_export.py +227 -0
- package/scripts/plan_waves.py +208 -0
- package/scripts/pr_close_keyword_lint.py +108 -0
- package/scripts/progress_guardian.py +888 -0
- package/scripts/recon_inventory.py +273 -0
- package/scripts/review_findings_log.py +93 -0
- package/scripts/run_invariants.py +124 -0
- package/scripts/select_lenses.py +640 -0
- package/scripts/session_report.py +486 -0
- package/scripts/set_autocompact_env.py +221 -0
- package/scripts/ship_resume_guard.py +135 -0
- package/scripts/ship_review_gate.py +63 -0
- package/scripts/specs_convention_marker.py +103 -0
- package/scripts/test_improve_resume.py +277 -0
- package/scripts/test_review_mechanics.py +958 -0
- package/scripts/token_efficiency_review.py +322 -0
- package/scripts/verdict_scope.py +285 -0
- package/scripts/verify_gherkin_quality_critic_isolation.py +296 -0
- package/scripts/verify_tier.py +157 -0
- package/skills/adr-tools/SKILL.md +118 -0
- package/skills/agent-readiness/SKILL.md +105 -0
- package/skills/agent-readiness/ai_friendly_analyzers.py +326 -0
- package/skills/agent-readiness/scanner.py +441 -0
- package/skills/agent-readiness/scorecard.yaml +88 -0
- package/skills/api-design/SKILL.md +115 -0
- package/skills/apply-fixes/SKILL.md +171 -0
- package/skills/apply-test-doubles/SKILL.md +321 -0
- package/skills/artifact-lifecycle/SKILL.md +127 -0
- package/skills/autoship/SKILL.md +1124 -0
- package/skills/benchmark/SKILL.md +105 -0
- package/skills/branch-workflow/SKILL.md +89 -0
- package/skills/browse/SKILL.md +184 -0
- package/skills/browser-testing/SKILL.md +62 -0
- package/skills/browser-testing/references/playwright-patterns.md +216 -0
- package/skills/build/SKILL.md +422 -0
- package/skills/build/references/static-self-heal.md +245 -0
- package/skills/careful/SKILL.md +72 -0
- package/skills/cd-test-architecture/SKILL.md +371 -0
- package/skills/ci-debugging/SKILL.md +105 -0
- package/skills/co-evolution-audit/SKILL.md +269 -0
- package/skills/code-review/SKILL.md +1015 -0
- package/skills/code-review/examples/aggregated-sample.json +56 -0
- package/skills/code-review/examples/sample-report.md +41 -0
- package/skills/code-review/output-format.md +478 -0
- package/skills/code-review/scripts/activation.py +86 -0
- package/skills/code-review/scripts/change_impact.py +357 -0
- package/skills/code-review/scripts/change_shape.py +372 -0
- package/skills/code-review/scripts/change_size.py +212 -0
- package/skills/code-review/scripts/changed_file_list.py +141 -0
- package/skills/code-review/scripts/closing_pass.py +187 -0
- package/skills/code-review/scripts/consolidate.py +277 -0
- package/skills/code-review/scripts/contract_failure_report.py +185 -0
- package/skills/code-review/scripts/dispatch_reconcile.py +66 -0
- package/skills/code-review/scripts/dispatch_waves.py +164 -0
- package/skills/code-review/scripts/finding_signature.py +446 -0
- package/skills/code-review/scripts/ledger.py +283 -0
- package/skills/code-review/scripts/partition.py +169 -0
- package/skills/code-review/scripts/render_tiered_findings.py +274 -0
- package/skills/code-review/scripts/repo_invariants.py +1066 -0
- package/skills/code-review/scripts/review_context_pack.py +306 -0
- package/skills/code-review/scripts/review_round_log.py +345 -0
- package/skills/code-review/scripts/review_value_coverage.py +297 -0
- package/skills/code-review/scripts/validate_review_output.py +467 -0
- package/skills/code-review/sliced-mode.md +205 -0
- package/skills/competitive-analysis/SKILL.md +191 -0
- package/skills/context-loading-protocol/SKILL.md +157 -0
- package/skills/continue/SKILL.md +90 -0
- package/skills/cost-report/SKILL.md +178 -0
- package/skills/coverage-baseline/SKILL.md +335 -0
- package/skills/coverage-baseline/references/multi-project-discovery.md +202 -0
- package/skills/coverage-delta/SKILL.md +181 -0
- package/skills/coverage-delta/references/mutation-gate.md +70 -0
- package/skills/design-doc/SKILL.md +95 -0
- package/skills/design-interrogation/SKILL.md +89 -0
- package/skills/design-it-twice/SKILL.md +91 -0
- package/skills/docker-image-audit/SKILL.md +108 -0
- package/skills/docker-image-audit/references/install-guide.md +64 -0
- package/skills/docker-image-audit/references/report-template.md +73 -0
- package/skills/docker-image-create/SKILL.md +185 -0
- package/skills/domain-analysis/SKILL.md +183 -0
- package/skills/domain-driven-design/SKILL.md +194 -0
- package/skills/exploratory-testing/SKILL.md +108 -0
- package/skills/explore/SKILL.md +51 -0
- package/skills/farley-score/SKILL.md +165 -0
- package/skills/feature-file-validation/SKILL.md +78 -0
- package/skills/feature-file-validation/references/validation-rules.md +115 -0
- package/skills/feedback-learning/SKILL.md +414 -0
- package/skills/fix/SKILL.md +450 -0
- package/skills/freeze/SKILL.md +68 -0
- package/skills/frontend-architecture/SKILL.md +113 -0
- package/skills/gherkin-derive/SKILL.md +630 -0
- package/skills/gherkin-public/SKILL.md +266 -0
- package/skills/governance-compliance/SKILL.md +150 -0
- package/skills/guard/SKILL.md +75 -0
- package/skills/handoff/SKILL.md +139 -0
- package/skills/handoff/references/summary-templates.md +242 -0
- package/skills/harness-audit/SKILL.md +751 -0
- package/skills/harness-audit/scripts/lesson_validate.py +386 -0
- package/skills/harness-audit/scripts/redundancy_criterion.py +188 -0
- package/skills/headless-run/SKILL.md +45 -0
- package/skills/headless-run/scripts/isolated_dispatch.py +381 -0
- package/skills/help/SKILL.md +72 -0
- package/skills/hexagonal-architecture/SKILL.md +85 -0
- package/skills/human-oversight-protocol/SKILL.md +224 -0
- package/skills/issues-from-assessment/SKILL.md +223 -0
- package/skills/issues-from-plan/SKILL.md +133 -0
- package/skills/legacy-code/SKILL.md +132 -0
- package/skills/mermaid-diagramming/SKILL.md +120 -0
- package/skills/mutation-night-watch/SKILL.md +154 -0
- package/skills/mutation-night-watch/references/scheduling.md +135 -0
- package/skills/mutation-testing/SKILL.md +396 -0
- package/skills/mutation-testing/references/languages/csharp-stryker-net.md +676 -0
- package/skills/mutation-testing/references/languages/go-go-mutesting.md +95 -0
- package/skills/mutation-testing/references/languages/java-pitest.md +77 -0
- package/skills/mutation-testing/references/languages/javascript-stryker.md +188 -0
- package/skills/mutation-testing/references/languages/python-mutmut.md +97 -0
- package/skills/mutation-testing/references/time-estimation.md +34 -0
- package/skills/mutation-testing/references/tool-detection.md +15 -0
- package/skills/mutation-testing/references/workflow-callers.md +23 -0
- package/skills/mutation-testing/scripts/__pycache__/xunit_v3_feature_detector.cpython-314.pyc +0 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_slice_runner.py +635 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_status_loop.py +525 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_wrapper.py +681 -0
- package/skills/mutation-testing/scripts/mutation_baseline_reuse.py +292 -0
- package/skills/mutation-testing/scripts/mutation_exclude_policy.py +268 -0
- package/skills/mutation-testing/scripts/mutation_feasibility_gate.py +463 -0
- package/skills/mutation-testing/scripts/mutation_kill_headless.py +331 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert.py +199 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert_python.py +150 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop.py +869 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop_python.py +949 -0
- package/skills/mutation-testing/scripts/mutation_kill_retry.py +592 -0
- package/skills/mutation-testing/scripts/mutation_kill_shared.py +620 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch.py +462 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch_stacks.py +425 -0
- package/skills/mutation-testing/scripts/mutation_report.py +743 -0
- package/skills/mutation-testing/scripts/mutation_report_cli.py +175 -0
- package/skills/mutation-testing/scripts/mutation_safety_gate.py +69 -0
- package/skills/mutation-testing/scripts/stryker_shard_pipeline.py +847 -0
- package/skills/mutation-testing/scripts/stryker_shard_setup.py +440 -0
- package/skills/mutation-testing/scripts/stryker_timeout_retry.py +142 -0
- package/skills/mutation-testing/scripts/xunit_v3_feature_detector.py +341 -0
- package/skills/performance-benchmark/SKILL.md +174 -0
- package/skills/performance-benchmark/examples/report-format.md +43 -0
- package/skills/performance-benchmark/references/benchmark-script.md +169 -0
- package/skills/performance-metrics/SKILL.md +265 -0
- package/skills/plan/SKILL.md +199 -0
- package/skills/plan/references/gherkin-persistence.md +43 -0
- package/skills/plan/references/plan-template.md +182 -0
- package/skills/pr/SKILL.md +289 -0
- package/skills/pr/scripts/gate_retry_state.py +368 -0
- package/skills/project-init/README.md +141 -0
- package/skills/project-init/SKILL.md +1197 -0
- package/skills/project-init/evals/evals.json +200 -0
- package/skills/project-init/references/capability-tools.md +55 -0
- package/skills/project-init/references/configs.md +221 -0
- package/skills/property-based-testing/SKILL.md +121 -0
- package/skills/property-based-testing/fixtures/invariant_fixture.py +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/README.md +42 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/README.md +263 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.js +12147 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.js +12011 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +13 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/package.json +94 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/README.md +168 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.js +38 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.js +18 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.js +22 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.js +134 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +37 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +17 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.js +44 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.js +90 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +78 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.js +8 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.js +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.js +46 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.js +92 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +82 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.js +10 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.js +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/package.json +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package-lock.json +1179 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package.json +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.js +29 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.properties.test.js +16 -0
- package/skills/property-based-testing/fixtures/no_property_fixture.py +10 -0
- package/skills/property-based-testing/fixtures/roundtrip_fixture.py +16 -0
- package/skills/property-based-testing/references/languages/javascript.md +54 -0
- package/skills/property-based-testing/scripts/detect_and_dispatch.py +80 -0
- package/skills/property-based-testing/scripts/hypothesis_scaffold.py +276 -0
- package/skills/proxy-resilience/SKILL.md +84 -0
- package/skills/quality-gate-pipeline/SKILL.md +184 -0
- package/skills/quality-targets-converge/SKILL.md +254 -0
- package/skills/repo-review/SKILL.md +159 -0
- package/skills/report-pdf/SKILL.md +66 -0
- package/skills/review/SKILL.md +47 -0
- package/skills/review-agent/SKILL.md +152 -0
- package/skills/review-summary/SKILL.md +73 -0
- package/skills/run-report/SKILL.md +70 -0
- package/skills/semantic-duplication-scan/SKILL.md +337 -0
- package/skills/semantic-scan/SKILL.md +53 -0
- package/skills/semgrep-analyze/SKILL.md +139 -0
- package/skills/setup/SKILL.md +1122 -0
- package/skills/ship/SKILL.md +240 -0
- package/skills/source-verification/SKILL.md +210 -0
- package/skills/source-verification/scripts/claim_extractor.py +155 -0
- package/skills/specs/.size-baseline.json +4 -0
- package/skills/specs/SKILL.md +243 -0
- package/skills/specs/references/completeness-checklist.md +83 -0
- package/skills/specs/references/extraction.md +58 -0
- package/skills/specs/references/glossary.md +59 -0
- package/skills/specs/references/persistence.md +115 -0
- package/skills/specs/references/predictability-check.md +77 -0
- package/skills/static-analysis-integration/SKILL.md +235 -0
- package/skills/static-analysis-integration/adapters/_envelope.py +26 -0
- package/skills/static-analysis-integration/adapters/jscpd-adapter.py +66 -0
- package/skills/static-analysis-integration/adapters/lizard-adapter.py +81 -0
- package/skills/static-analysis-integration/adapters/mypy-adapter.py +50 -0
- package/skills/static-analysis-integration/adapters/mypy-src-layout.py +93 -0
- package/skills/static-analysis-integration/adapters/security-review-adapter.py +212 -0
- package/skills/static-analysis-integration/maintenance.md +23 -0
- package/skills/static-analysis-integration/references/language-setup.md +228 -0
- package/skills/static-analysis-integration/references/sarif-parser.md +124 -0
- package/skills/static-analysis-integration/references/security-review-adapter.md +118 -0
- package/skills/static-analysis-integration/references/tool-configs.md +617 -0
- package/skills/static-analysis-integration/rulesets/pmd-quickstart.xml +24 -0
- package/skills/stryker-xunit-v2-shim/SKILL.md +274 -0
- package/skills/stryker-xunit-v2-shim/references/shim-howto.md +256 -0
- package/skills/stryker-xunit-v2-shim/scripts/generate_shim.py +143 -0
- package/skills/systematic-debugging/SKILL.md +130 -0
- package/skills/telemetry/SKILL.md +75 -0
- package/skills/test-audit-disable/SKILL.md +129 -0
- package/skills/test-design/SKILL.md +177 -0
- package/skills/test-design/scripts/__pycache__/internal_double_detector.cpython-314.pyc +0 -0
- package/skills/test-design/scripts/internal_double_detector.py +631 -0
- package/skills/test-design-advisor/SKILL.md +166 -0
- package/skills/test-driven-development/SKILL.md +169 -0
- package/skills/test-health/SKILL.md +262 -0
- package/skills/test-improve/SKILL.md +239 -0
- package/skills/test-improve/references/phase-0-approach-contract.md +228 -0
- package/skills/test-improve/references/phase-1-analyze.md +131 -0
- package/skills/test-improve/references/phase-2-baseline.md +121 -0
- package/skills/test-improve/references/phase-3-derive-gherkin.md +53 -0
- package/skills/test-improve/references/phase-4-plan-fixes.md +34 -0
- package/skills/test-improve/references/phase-5-improve.md +215 -0
- package/skills/test-improve/references/phase-6-refactor-decision.md +45 -0
- package/skills/test-improve/references/phase-7-refactor.md +44 -0
- package/skills/test-improve/references/phase-8-validate.md +66 -0
- package/skills/test-improve/references/phase-9-close-out-prompt.md +11 -0
- package/skills/test-improve/references/phase-9-report.md +62 -0
- package/skills/test-improve/references/review-loop.md +92 -0
- package/skills/test-improve/templates/executive-summary.md +123 -0
- package/skills/threat-modeling/SKILL.md +108 -0
- package/skills/triage/SKILL.md +211 -0
- package/skills/ubiquitous-language/SKILL.md +192 -0
- package/skills/ubiquitous-language/scripts/collect_domain_signals.py +300 -0
- package/skills/unfreeze/SKILL.md +37 -0
- package/skills/upgrade/SKILL.md +31 -0
- package/skills/upgrade/scripts/check_version_drift.py +113 -0
- package/skills/upgrade/scripts/enable_autoupdate.py +149 -0
- package/skills/version/SKILL.md +25 -0
- package/sync/__pycache__/sync_upstream.cpython-314.pyc +0 -0
- package/sync/sync_upstream.py +293 -0
- package/templates/ACCEPTED-RISKS.md.tmpl +46 -0
- package/templates/agents/agent-template.md +151 -0
- package/templates/agents/angular-testing.md +66 -0
- package/templates/agents/csharp-quality.md +63 -0
- package/templates/agents/esm-enforcer.md +52 -0
- package/templates/agents/front-end-testing.md +65 -0
- package/templates/agents/go-quality.md +65 -0
- package/templates/agents/python-quality.md +62 -0
- package/templates/agents/react-testing.md +61 -0
- package/templates/agents/ts-enforcer.md +60 -0
- package/templates/agents/twelve-factor-audit.md +49 -0
- package/tools/entropy-check.py +250 -0
- package/tools/model-hash-verify.py +213 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Resolve which review lenses apply to a set of changed files (#1516).
|
|
3
|
+
|
|
4
|
+
Given changed file paths, return the review-agent lenses whose ``Scope:``
|
|
5
|
+
declaration matches, ordered cheap-first (non-opus before opus). ``/build``'s
|
|
6
|
+
inline review checkpoints call this to avoid dispatching lenses with no
|
|
7
|
+
matching surface in the diff (a backend-only diff should not spend tokens on
|
|
8
|
+
frontend/UI lenses). ``/code-review`` Step 3 also adopted this resolver
|
|
9
|
+
(#1533) — both live call sites now share every rule below, not just the
|
|
10
|
+
ones present at #1523.
|
|
11
|
+
A ``Scope: always`` lens named in ``NON_EXECUTABLE_SKIP_ELIGIBLE`` is
|
|
12
|
+
additionally excluded when every changed file is non-executable
|
|
13
|
+
(docs/config/assets/lockfiles, never functional Claude-config markdown —
|
|
14
|
+
see ``NON_EXECUTABLE_EXTENSIONS`` and ``doc_classification.is_functional_config``), since
|
|
15
|
+
that lens's own Skip clause would otherwise just self-report
|
|
16
|
+
``{"status": "skip"}`` at opus, high-effort cost.
|
|
17
|
+
|
|
18
|
+
Roster source: the **Review Agents** table in ``knowledge/agent-registry.md``
|
|
19
|
+
(the curated lens list), minus the shared ``NON_REVIEW_AGENTS`` boundary and
|
|
20
|
+
the manifest-governed framework-reactivity agents — see ``MANIFEST_GOVERNED``.
|
|
21
|
+
|
|
22
|
+
Design: ``parse_scope`` and ``applicable_lenses`` are pure (unit-testable with
|
|
23
|
+
a synthetic roster). ``build_review_roster`` and ``_read_lines_from`` are the
|
|
24
|
+
I/O boundaries; both fail open (a read error never raises) and both report
|
|
25
|
+
the failure in ``warnings`` rather than silently narrowing the result —
|
|
26
|
+
``unreadable-registry:<file>`` / ``unreadable-files-from:<path>`` /
|
|
27
|
+
``unreadable-added-from:<path>``. An affected *agent* additionally stays
|
|
28
|
+
include-biased (``build_review_roster``'s unreadable-agent-file case); an
|
|
29
|
+
affected *file list* does not fabricate paths, so its warning is the only
|
|
30
|
+
signal that the returned list is short.
|
|
31
|
+
|
|
32
|
+
Stdlib-only. See docs/python-hook-contract.md.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import argparse
|
|
38
|
+
import json
|
|
39
|
+
import re
|
|
40
|
+
import sys
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
|
|
43
|
+
_HERE = Path(__file__).resolve().parent
|
|
44
|
+
sys.path.insert(0, str(_HERE / "lib"))
|
|
45
|
+
|
|
46
|
+
import review_roster
|
|
47
|
+
|
|
48
|
+
# skills/code-review/scripts/change_shape.py's runtime-surface gate already
|
|
49
|
+
# solves "is this path functional Claude-config, never skippable regardless
|
|
50
|
+
# of extension" — reuse its shared predicate (#1477, extended #1923) rather
|
|
51
|
+
# than adding a fourth, independently-drifting copy of the same
|
|
52
|
+
# classification (a real defect a prior draft of this filter had: `.md`
|
|
53
|
+
# alone made `plugins/dev-team/agents/*.md` — this very repo's shipped
|
|
54
|
+
# agent behavior — read as "non-executable"). Same cross-boundary import
|
|
55
|
+
# pattern that module already uses.
|
|
56
|
+
_HOOKS_LIB_DIR = _HERE.parent / "hooks" / "lib"
|
|
57
|
+
if str(_HOOKS_LIB_DIR) not in sys.path:
|
|
58
|
+
sys.path.insert(0, str(_HOOKS_LIB_DIR))
|
|
59
|
+
|
|
60
|
+
# No `except ImportError` fallback (#1968). The fallback this replaced was a
|
|
61
|
+
# hand-written second copy of `is_functional_config`, and nothing could catch
|
|
62
|
+
# it drifting: the guard that claimed to (`test_select_lenses.py`'s
|
|
63
|
+
# `test_is_functional_config_matches_doc_classification_module`) compared the
|
|
64
|
+
# canonical implementation against itself whenever the import succeeded — which
|
|
65
|
+
# is always, here — so sabotaging the fallback to `return False` left the whole
|
|
66
|
+
# suite green. `hooks/lib/` ships inside this same plugin; an unreachable one is
|
|
67
|
+
# a broken install, not a supported degraded mode.
|
|
68
|
+
try:
|
|
69
|
+
from doc_classification import (
|
|
70
|
+
is_functional_config, # type: ignore[import-not-found]
|
|
71
|
+
)
|
|
72
|
+
except ImportError as exc: # pragma: no cover - broken install, not a supported mode
|
|
73
|
+
raise ImportError(
|
|
74
|
+
f"{__name__} requires the shared classifier 'doc_classification' from "
|
|
75
|
+
f"the dev-team plugin's hooks/lib, which could not be imported.\n"
|
|
76
|
+
f" searched: {_HOOKS_LIB_DIR}\n"
|
|
77
|
+
f" exists: {_HOOKS_LIB_DIR.is_dir()}\n"
|
|
78
|
+
"This module deliberately has no fallback copy of the classifier "
|
|
79
|
+
"(#1968) — a divergent stand-in silently mis-classifies files. Fix the "
|
|
80
|
+
"install or the path rather than re-adding a local implementation."
|
|
81
|
+
) from exc
|
|
82
|
+
|
|
83
|
+
# `knowledge/test-file-indicators.md`'s single encoding, shared with the
|
|
84
|
+
# refactor test-freeze guards and `change_shape.py` (#1978). Same no-fallback
|
|
85
|
+
# rule as above: a hand-written second copy of "is this a test file?" is
|
|
86
|
+
# exactly the drift #1968 removed.
|
|
87
|
+
try:
|
|
88
|
+
# `_read_probe` is imported rather than re-implemented for the same reason
|
|
89
|
+
# `is_test_file` is: it owns the read cap and the decode-error policy the
|
|
90
|
+
# classifier's content branches expect, and a local copy is a second
|
|
91
|
+
# implementation free to drift (#1968).
|
|
92
|
+
from test_file_classify import ( # type: ignore[import-not-found]
|
|
93
|
+
_read_probe,
|
|
94
|
+
is_test_file,
|
|
95
|
+
)
|
|
96
|
+
except ImportError as exc: # pragma: no cover - broken install, not a supported mode
|
|
97
|
+
raise ImportError(
|
|
98
|
+
f"{__name__} requires the shared classifier 'test_file_classify' from "
|
|
99
|
+
f"the dev-team plugin's hooks/lib, which could not be imported.\n"
|
|
100
|
+
f" searched: {_HOOKS_LIB_DIR}\n"
|
|
101
|
+
f" exists: {_HOOKS_LIB_DIR.is_dir()}\n"
|
|
102
|
+
"This module deliberately has no fallback copy of the classifier "
|
|
103
|
+
"(#1968) — a divergent stand-in silently mis-classifies files. Fix the "
|
|
104
|
+
"install or the path rather than re-adding a local implementation."
|
|
105
|
+
) from exc
|
|
106
|
+
|
|
107
|
+
# Framework-reactivity lenses declare bare ``**/*.ts`` / ``**/*.js`` globs, but
|
|
108
|
+
# their real trigger is a dependency-manifest match (``/code-review`` Step 3's
|
|
109
|
+
# separate rule), not a file-path match. Keep them OUT of this resolver's remit
|
|
110
|
+
# so a plain Node/TS backend with no React/Vue/Angular does not get reactivity
|
|
111
|
+
# lenses. MAINTENANCE: any new manifest-governed framework-reactivity agent
|
|
112
|
+
# with bare ``**/*.ts|js`` globs must be added here.
|
|
113
|
+
MANIFEST_GOVERNED = {
|
|
114
|
+
"react-reactivity-review",
|
|
115
|
+
"vue-reactivity-review",
|
|
116
|
+
"angular-reactivity-review",
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_SCOPE_RE = review_roster.SCOPE_LINE_RE
|
|
120
|
+
_MODEL_RE = re.compile(r"^\s*model\s*:\s*(.*)$")
|
|
121
|
+
_BULLET_RE = re.compile(r"^\s*-\s*(\S+)")
|
|
122
|
+
_EXT_TOKEN_RE = re.compile(r"`?(\.\w[\w.]*)`?")
|
|
123
|
+
# Review Agents table rows look like: | <name> | `agents/<name>.md` | ... |
|
|
124
|
+
# The name must start with a letter so the markdown separator row
|
|
125
|
+
# (| ------- | ------ | ... |) is not captured as a bogus "-------" lens.
|
|
126
|
+
_TABLE_ROW_RE = re.compile(r"^\|\s*([a-z][a-z0-9-]*)\s*\|")
|
|
127
|
+
|
|
128
|
+
# The sentinel an agent uses to declare it applies to every diff (`Scope: always`).
|
|
129
|
+
SCOPE_ALWAYS = "always"
|
|
130
|
+
# The sentinel an agent uses to declare it should only fire when a matching
|
|
131
|
+
# file is newly *added*, not merely modified (#1733's dual-placement rule for
|
|
132
|
+
# `component-architecture-review`: unconditional whole-inventory pass in
|
|
133
|
+
# `/repo-review`, narrowed to added-only in `/code-review`'s per-diff panel).
|
|
134
|
+
# Parsed the same way as the empty-inline-value case (bullet block of globs)
|
|
135
|
+
# and encoded as `(SCOPE_ADDED_ONLY, globs)` so callers can tell it apart from
|
|
136
|
+
# a plain glob-list scope.
|
|
137
|
+
SCOPE_ADDED_ONLY = "added-only"
|
|
138
|
+
# The sentinel an agent uses to declare it is a genuine review agent whose
|
|
139
|
+
# findings are properties of the whole repository, not any single diff, and
|
|
140
|
+
# so must never be dispatched by the per-diff resolver — only by name (e.g.
|
|
141
|
+
# `/claude-setup-review`) or by the whole-tree `/repo-review` skill (#1735).
|
|
142
|
+
# A bare declaration, no bullet block: unlike `added-only`, there is no glob
|
|
143
|
+
# data to carry, so the resolver just excludes the lens outright. Replaces a
|
|
144
|
+
# prior, competing mechanism (listing the agent in `NON_REVIEW_AGENTS`) that
|
|
145
|
+
# also incorrectly exempted it from the `Scope:` declaration requirement it
|
|
146
|
+
# actually satisfies — see `scripts/lib/review_roster.py`'s docstring.
|
|
147
|
+
SCOPE_ON_DEMAND = "on-demand"
|
|
148
|
+
# The sentinel an agent uses to declare it applies only when the changeset
|
|
149
|
+
# touches a test file (#1978). A bare declaration with no bullet block, like
|
|
150
|
+
# `on-demand` — deliberately NOT a glob list, because the glob vocabulary
|
|
151
|
+
# `_matches` supports (a literal suffix) cannot express three of the five
|
|
152
|
+
# families in `knowledge/test-file-indicators.md`: Python's `test_*.py`
|
|
153
|
+
# prefix, JS/TS's `__tests__/` directory, and C#/Java's annotation-based
|
|
154
|
+
# indicators. A hand-maintained glob list would therefore be both incomplete
|
|
155
|
+
# and a second copy of that knowledge file, free to drift from it. This
|
|
156
|
+
# sentinel instead delegates to `test_file_classify.is_test_file` — the same
|
|
157
|
+
# single encoding the refactor test-freeze guards and `change_shape.py` use —
|
|
158
|
+
# so the answer cannot diverge from the canonical one.
|
|
159
|
+
SCOPE_TEST_FILES = "test-files"
|
|
160
|
+
# Heading substring that marks the Review Agents section of agent-registry.md.
|
|
161
|
+
# Named so a heading rename over there is greppable from here (the coupling).
|
|
162
|
+
_REVIEW_AGENTS_HEADING_MARKER = "review agent"
|
|
163
|
+
|
|
164
|
+
# Lenses whose own Skip clause covers "static assets, configuration,
|
|
165
|
+
# markup, or documentation with no executable logic" / "generated code,
|
|
166
|
+
# vendored dependencies, or lockfiles" verbatim, checked directly against
|
|
167
|
+
# each candidate's own agent-body text before being added here (#1923
|
|
168
|
+
# review round): each would only ever self-report `{"status": "skip"}` on
|
|
169
|
+
# an all-non-executable diff, so dispatching an opus, high-effort model
|
|
170
|
+
# just to hear "skip" is pure waste.
|
|
171
|
+
#
|
|
172
|
+
# Deliberately ONE member today, not "every opus-tier always lens" —
|
|
173
|
+
# `security-review`, `arch-review`, and `domain-review` were considered and
|
|
174
|
+
# REJECTED: their own Skip clauses do not authorize this. security-review's
|
|
175
|
+
# clause covers only "static assets, images, or documentation", not
|
|
176
|
+
# config/lockfiles (`NON_EXECUTABLE_EXTENSIONS` includes both), and its own
|
|
177
|
+
# "## Scope — files always in scope" section mandates scanning some of the
|
|
178
|
+
# very file classes this filter would suppress (infra manifests, configs)
|
|
179
|
+
# for hardcoded credentials — a real, exploitable coverage gap this filter
|
|
180
|
+
# would otherwise create for A08.review-manipulation/A05 detection.
|
|
181
|
+
# arch-review's clause is about ADR/module-boundary *absence*, not a
|
|
182
|
+
# docs-only diff — a pure-markdown ADR update is exactly the case its own
|
|
183
|
+
# `## Detect` section (ADR compliance) exists to catch, so filtering it on
|
|
184
|
+
# `.md` would drop the one check aimed at that content. domain-review's
|
|
185
|
+
# clause is "infra-only code" / "no business logic present", which is a
|
|
186
|
+
# file-content judgment, not a file-type one this filter can safely proxy.
|
|
187
|
+
#
|
|
188
|
+
# An explicit allowlist (matching this file's own MANIFEST_GOVERNED
|
|
189
|
+
# precedent above) rather than an implicit `is_opus` check: a future opus
|
|
190
|
+
# `Scope: always` lens defaults to NOT being filtered until it is added
|
|
191
|
+
# here deliberately, after the same verification against its own Skip
|
|
192
|
+
# clause — so a Skip-clause change on `correctness-review` (or a new
|
|
193
|
+
# candidate with different semantics) can't silently start/stop being
|
|
194
|
+
# filtered with nothing forcing that decision. MAINTENANCE: re-verify
|
|
195
|
+
# against the named agent's own Skip clause before adding or keeping a name
|
|
196
|
+
# here; remove it the moment that clause no longer matches.
|
|
197
|
+
#
|
|
198
|
+
# Relationship to `skills/code-review/scripts/change_shape.py`'s
|
|
199
|
+
# `LOW_YIELD_LENSES` (`["performance-review", "correctness-review"]`):
|
|
200
|
+
# that gate is `/code-review`-Step-3-specific and already drops
|
|
201
|
+
# correctness-review on a no-runtime-surface changeset (with this same
|
|
202
|
+
# functional-config carve-out). This allowlist is NOT redundant with it —
|
|
203
|
+
# `/build`'s inline review checkpoints call `applicable_lenses` directly
|
|
204
|
+
# and never consult `change_shape.py`, so without this, correctness-review
|
|
205
|
+
# still paid full opus cost on every doc-only `/build` step. The two lists
|
|
206
|
+
# legitimately overlap on `correctness-review` rather than duplicating by
|
|
207
|
+
# accident; `performance-review` is haiku-tier and not this filter's cost
|
|
208
|
+
# concern, so it is intentionally absent here.
|
|
209
|
+
# frozenset (not a plain set), matching `doc_classification.py`'s own
|
|
210
|
+
# convention for these shared-literal-shaped tables: this allowlist governs
|
|
211
|
+
# which review lenses may be dropped, so it must not be mutable by any
|
|
212
|
+
# importer after module load.
|
|
213
|
+
NON_EXECUTABLE_SKIP_ELIGIBLE = frozenset({
|
|
214
|
+
"correctness-review",
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
# Extensions/basenames the NON_EXECUTABLE_SKIP_ELIGIBLE lens(es) above would
|
|
218
|
+
# only ever self-report `{"status": "skip"}` against. Deliberately
|
|
219
|
+
# conservative: `.yaml`/`.yml` are excluded because CI workflow files
|
|
220
|
+
# routinely embed real conditionals/shell logic correctness-review
|
|
221
|
+
# legitimately checks (see its category 5); a lockfile written in YAML is
|
|
222
|
+
# instead named explicitly in NON_EXECUTABLE_BASENAMES. Safe to keep `.json`
|
|
223
|
+
# here even though it is a real security/execution surface elsewhere in this
|
|
224
|
+
# repo (`settings.json` hook registrations, `package.json` scripts) —
|
|
225
|
+
# `security-review` is not on the allowlist above, and correctness-review's
|
|
226
|
+
# "does this code do what it evidently intends" lens has no bearing on that
|
|
227
|
+
# concern. Deliberately DIFFERENT from `change_shape.py`'s own runtime-surface
|
|
228
|
+
# classifier, not merely a copy of it: the binary/vector asset extensions
|
|
229
|
+
# below (`.svg`/`.png`/etc.) appear in neither that module's `_DOC_EXTENSIONS`
|
|
230
|
+
# nor its `_CONFIG_EXTENSIONS`, so an asset-only diff is "runtime surface,
|
|
231
|
+
# keep every lens" there but "non-executable, skip correctness-review" here.
|
|
232
|
+
# That asymmetry is safe today (it can only make this module drop a lens
|
|
233
|
+
# `change_shape.py` would have kept, never the reverse, and only for
|
|
234
|
+
# correctness-review — no other lens reads this constant), but re-check it
|
|
235
|
+
# against `change_shape.py` before adding a second name to the allowlist
|
|
236
|
+
# above, per that constant's own MAINTENANCE note.
|
|
237
|
+
NON_EXECUTABLE_EXTENSIONS = frozenset({
|
|
238
|
+
".md", ".mdx", ".rst", ".txt",
|
|
239
|
+
".json", ".toml", ".ini", ".cfg",
|
|
240
|
+
".svg", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".webp", ".pdf",
|
|
241
|
+
".lock",
|
|
242
|
+
})
|
|
243
|
+
NON_EXECUTABLE_BASENAMES = frozenset({
|
|
244
|
+
"LICENSE", "LICENSE.txt", ".gitignore", ".editorconfig", ".gitattributes",
|
|
245
|
+
"pnpm-lock.yaml",
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
def _is_non_executable_file(file_path: str) -> bool:
|
|
249
|
+
"""True if ``file_path`` is a static asset, inert config/data,
|
|
250
|
+
documentation, or a lockfile — see NON_EXECUTABLE_EXTENSIONS above.
|
|
251
|
+
Never true for functional Claude-config markdown/paths (checked
|
|
252
|
+
first, via the shared `is_functional_config` — same predicate
|
|
253
|
+
`change_shape.py` uses), regardless of extension."""
|
|
254
|
+
if is_functional_config(file_path):
|
|
255
|
+
return False
|
|
256
|
+
name = Path(file_path).name
|
|
257
|
+
if name in NON_EXECUTABLE_BASENAMES:
|
|
258
|
+
return True
|
|
259
|
+
return Path(file_path).suffix.lower() in NON_EXECUTABLE_EXTENSIONS
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _is_all_non_executable(changed_files) -> bool:
|
|
263
|
+
"""True iff every file in ``changed_files`` is non-executable. Callers
|
|
264
|
+
already special-case an empty list before reaching this."""
|
|
265
|
+
return bool(changed_files) and all(
|
|
266
|
+
_is_non_executable_file(f) for f in changed_files
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _should_skip_non_executable(name: str, changed_files) -> bool:
|
|
271
|
+
"""True if ``name`` is on the skip allowlist and every changed file is
|
|
272
|
+
non-executable — mirrors ``_added_only_eligible``'s role below for the
|
|
273
|
+
added-only branch, hoisting a compound condition out of the
|
|
274
|
+
``applicable_lenses`` dispatcher's own branch body."""
|
|
275
|
+
return name in NON_EXECUTABLE_SKIP_ELIGIBLE and _is_all_non_executable(changed_files)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _consume_bullet_block(lines, start_index):
|
|
279
|
+
"""Collect a contiguous ``- <glob>`` bullet block starting after
|
|
280
|
+
``start_index``; stop at the first non-bullet line. Returns the globs."""
|
|
281
|
+
globs = []
|
|
282
|
+
for nxt in lines[start_index + 1:]:
|
|
283
|
+
b = _BULLET_RE.match(nxt)
|
|
284
|
+
if not b:
|
|
285
|
+
break
|
|
286
|
+
globs.append(b.group(1).strip("`"))
|
|
287
|
+
return globs
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def parse_scope(text: str):
|
|
291
|
+
"""Return ``"always"`` | ``"on-demand"`` | ``"test-files"`` | ``list[str]``
|
|
292
|
+
| ``(SCOPE_ADDED_ONLY, list[str])`` | ``None`` from agent markdown.
|
|
293
|
+
|
|
294
|
+
The **first** ``Scope:`` line is authoritative. An empty inline value
|
|
295
|
+
followed by a ``- **/*.ext`` bullet block yields that structured,
|
|
296
|
+
fnmatch-ready list (preferred over the free-text second ``Scope:`` line some
|
|
297
|
+
agents also carry). ``Scope: always`` -> ``"always"``. ``Scope: on-demand``
|
|
298
|
+
-> ``SCOPE_ON_DEMAND`` (a bare declaration — no bullet block, unlike
|
|
299
|
+
``added-only``). ``Scope: added-only`` followed by the same bullet-block
|
|
300
|
+
shape -> ``(SCOPE_ADDED_ONLY, globs)`` (no bullet block -> ``None``, same
|
|
301
|
+
fail-open rule as a missing declaration). Other inline prose -> ``.ext``
|
|
302
|
+
tokens extracted as a fallback (``None`` if it names no extension). No
|
|
303
|
+
``Scope:`` line -> ``None``.
|
|
304
|
+
"""
|
|
305
|
+
lines = text.splitlines()
|
|
306
|
+
for i, line in enumerate(lines):
|
|
307
|
+
m = _SCOPE_RE.match(line)
|
|
308
|
+
if not m:
|
|
309
|
+
continue
|
|
310
|
+
value = m.group(1).strip()
|
|
311
|
+
if value.lower() == SCOPE_ALWAYS:
|
|
312
|
+
return SCOPE_ALWAYS
|
|
313
|
+
if value.lower() == SCOPE_ON_DEMAND:
|
|
314
|
+
return SCOPE_ON_DEMAND
|
|
315
|
+
if value.lower() == SCOPE_TEST_FILES:
|
|
316
|
+
return SCOPE_TEST_FILES
|
|
317
|
+
if value.lower() == SCOPE_ADDED_ONLY:
|
|
318
|
+
globs = _consume_bullet_block(lines, i)
|
|
319
|
+
return (SCOPE_ADDED_ONLY, globs) if globs else None
|
|
320
|
+
if value:
|
|
321
|
+
tokens = _EXT_TOKEN_RE.findall(value)
|
|
322
|
+
return [f"**/*{t}" for t in tokens] or None
|
|
323
|
+
# Empty inline value: consume the following bullet block (authoritative).
|
|
324
|
+
return _consume_bullet_block(lines, i) or None
|
|
325
|
+
return None
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def parse_model_is_opus(text: str) -> bool:
|
|
329
|
+
"""True if the agent's ``model:`` frontmatter resolves to an opus tier."""
|
|
330
|
+
for line in text.splitlines():
|
|
331
|
+
m = _MODEL_RE.match(line)
|
|
332
|
+
if m:
|
|
333
|
+
return "opus" in m.group(1).strip().lower()
|
|
334
|
+
return False
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _matches(file_path: str, pattern: str) -> bool:
|
|
338
|
+
"""Extension-aware glob match, robust to directory depth.
|
|
339
|
+
|
|
340
|
+
``**/*.tsx`` -> match any path ending ``.tsx``; ``**/*.component.ts`` ->
|
|
341
|
+
ending ``.component.ts``. Directory-insensitive on purpose (fnmatch's ``*``
|
|
342
|
+
would otherwise require a ``/`` for a ``**/`` prefix and miss top-level
|
|
343
|
+
files). A non-glob pattern matches by exact path or basename.
|
|
344
|
+
"""
|
|
345
|
+
if "*" in pattern:
|
|
346
|
+
suffix = pattern.rsplit("*", 1)[-1]
|
|
347
|
+
return bool(suffix) and file_path.endswith(suffix)
|
|
348
|
+
return file_path == pattern or Path(file_path).name == pattern
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _scope_matches(scope, changed_files) -> bool:
|
|
352
|
+
"""True if a glob-list ``scope`` matches any changed file (``"always"`` is
|
|
353
|
+
handled by the caller, not here)."""
|
|
354
|
+
return any(_matches(f, g) for f in changed_files for g in scope)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _is_added_only_scope(scope) -> bool:
|
|
358
|
+
"""True if ``scope`` is the ``(SCOPE_ADDED_ONLY, globs)`` shape."""
|
|
359
|
+
return isinstance(scope, tuple) and scope[0] == SCOPE_ADDED_ONLY
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _added_only_eligible(globs, changed_files, added_files) -> bool:
|
|
363
|
+
"""True if an added-only scope's ``globs`` match within the eligible set.
|
|
364
|
+
|
|
365
|
+
``added_files=None`` (no change-type data supplied) falls back to
|
|
366
|
+
matching any changed file — the fail-safe direction every other
|
|
367
|
+
ambiguity in this module already resolves toward. A supplied set (even
|
|
368
|
+
empty) narrows eligibility to only files present in it.
|
|
369
|
+
"""
|
|
370
|
+
eligible = changed_files if added_files is None else [
|
|
371
|
+
f for f in changed_files if f in added_files
|
|
372
|
+
]
|
|
373
|
+
return bool(eligible) and _scope_matches(globs, eligible)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
#: Extensions whose test-ness `test_file_classify` decides from file CONTENT
|
|
377
|
+
#: (C# `[Fact]`, Java `@Test`) rather than from the name, by reading the file.
|
|
378
|
+
#: When such a file is not on disk — a deleted path, or a caller running
|
|
379
|
+
#: outside the checkout — that read yields "" and the classifier answers "not
|
|
380
|
+
#: a test", which would silently drop a lens. `test_file_subset` treats those
|
|
381
|
+
#: as possible tests instead: the same include-biased direction every other
|
|
382
|
+
#: ambiguity in this module already resolves toward.
|
|
383
|
+
_CONTENT_PROBED_SUFFIXES = (".cs", ".java")
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def test_file_subset(changed_files, root=None):
|
|
387
|
+
"""I/O boundary: which of ``changed_files`` are test files.
|
|
388
|
+
|
|
389
|
+
Reads from disk (via the shared classifier) for the content-probed
|
|
390
|
+
languages; every other family resolves by name alone. Returns a set
|
|
391
|
+
suitable for ``applicable_lenses(..., test_files=...)``. Never raises —
|
|
392
|
+
an unreadable content-probed file counts as a possible test, per
|
|
393
|
+
``_CONTENT_PROBED_SUFFIXES``.
|
|
394
|
+
"""
|
|
395
|
+
base = Path(root) if root else Path.cwd()
|
|
396
|
+
found = set()
|
|
397
|
+
for path in changed_files:
|
|
398
|
+
# ALWAYS classify the repo-relative path, never the absolute one: the
|
|
399
|
+
# classifier's `__tests__/` rule matches any path segment, so a
|
|
400
|
+
# checkout living under a directory named `__tests__` (a CI workspace
|
|
401
|
+
# at /build/__tests__/repo, say) would otherwise classify every file
|
|
402
|
+
# in the repo as a test. Content is supplied separately below so the
|
|
403
|
+
# on-disk read still happens against the real file.
|
|
404
|
+
if not path.lower().endswith(_CONTENT_PROBED_SUFFIXES):
|
|
405
|
+
if is_test_file(path):
|
|
406
|
+
found.add(path)
|
|
407
|
+
continue
|
|
408
|
+
full = base / path
|
|
409
|
+
# An empty probe means "could not read it", not "read it and found no
|
|
410
|
+
# test marker": `_read_probe` returns "" both for a file that is not
|
|
411
|
+
# there and for one that exists but raises (permissions, or a file
|
|
412
|
+
# replaced between this check and the open). Both are unprovable, so
|
|
413
|
+
# both include-bias — anything else silently drops the lens, which is
|
|
414
|
+
# the failure mode `_CONTENT_PROBED_SUFFIXES` exists to prevent.
|
|
415
|
+
probe = _read_probe(full) if full.is_file() else ""
|
|
416
|
+
if not probe or is_test_file(path, content=probe):
|
|
417
|
+
found.add(path)
|
|
418
|
+
return found
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def applicable_lenses(changed_files, roster, added_files=None, test_files=None):
|
|
422
|
+
"""Pure resolver. ``roster`` = ``[(name, scope, is_opus)]``. Returns
|
|
423
|
+
``(lenses, warnings)`` with lenses ordered cheap-first (non-opus, then opus).
|
|
424
|
+
|
|
425
|
+
An empty ``changed_files`` yields ``([], [])`` — nothing to review, so no
|
|
426
|
+
lens (not even ``Scope: always``) is selected. ``scope is None`` ->
|
|
427
|
+
include-biased + warn (never non-executable-filtered, regardless of
|
|
428
|
+
``name`` or ``is_opus`` — that filter applies only inside this
|
|
429
|
+
``SCOPE_ALWAYS`` branch); ``"always"`` -> include, UNLESS ``name`` is on
|
|
430
|
+
the ``NON_EXECUTABLE_SKIP_ELIGIBLE`` allowlist and every changed file is
|
|
431
|
+
non-executable (see ``_should_skip_non_executable``) — that combination
|
|
432
|
+
is excluded and warned as ``skipped-non-executable:<name>`` rather than
|
|
433
|
+
paying an opus round trip for a self-reported skip; glob list -> include
|
|
434
|
+
iff any changed file matches; ``SCOPE_ON_DEMAND`` -> never include, no
|
|
435
|
+
warning (a deliberate, self-declared exclusion, not a defect to
|
|
436
|
+
surface); ``SCOPE_TEST_FILES`` -> include iff a changed file is in
|
|
437
|
+
``test_files``.
|
|
438
|
+
|
|
439
|
+
``test_files`` (#1978) is the ``SCOPE_TEST_FILES`` counterpart of
|
|
440
|
+
``added_files`` below, and follows the same fail-safe rule: ``None`` (a
|
|
441
|
+
caller with no test-file classification to offer) includes the lens and
|
|
442
|
+
records ``unnarrowed-test-files:<name>`` in ``warnings`` rather than
|
|
443
|
+
silently dropping a lens the caller never asked to narrow. A supplied
|
|
444
|
+
set — even an empty one — narrows. Callers that can reach the
|
|
445
|
+
filesystem get the set from ``test_file_subset``.
|
|
446
|
+
|
|
447
|
+
``added_files`` (#1733) narrows a ``(SCOPE_ADDED_ONLY, globs)`` scope to
|
|
448
|
+
only the changed files that are also newly *added* — but **only when the
|
|
449
|
+
caller actually supplies it**. ``added_files=None`` (the default — a
|
|
450
|
+
caller, e.g. ``/build``'s inline checkpoints, that has no change-type data
|
|
451
|
+
to offer) makes an added-only scope behave exactly like a plain glob list,
|
|
452
|
+
matching any changed file. This is deliberately fail-safe in the same
|
|
453
|
+
direction every other gate in this module already leans: ambiguity always
|
|
454
|
+
resolves toward *keeping* a lens, never toward silently dropping one a
|
|
455
|
+
caller never asked to narrow. When that fallback fires (a lens selected
|
|
456
|
+
via an added-only scope with no ``added_files`` supplied), it is recorded
|
|
457
|
+
in ``warnings`` as ``unnarrowed-added-only:<name>`` — the same "never
|
|
458
|
+
silently dropped" transparency the missing-``Scope:`` case already gets,
|
|
459
|
+
applied to a silent *widening* instead.
|
|
460
|
+
"""
|
|
461
|
+
if not changed_files:
|
|
462
|
+
return [], []
|
|
463
|
+
selected: list[tuple[str, bool]] = []
|
|
464
|
+
warnings: list[str] = []
|
|
465
|
+
for name, scope, is_opus in roster:
|
|
466
|
+
if scope is None:
|
|
467
|
+
warnings.append(name)
|
|
468
|
+
selected.append((name, is_opus))
|
|
469
|
+
elif scope == SCOPE_ALWAYS:
|
|
470
|
+
if _should_skip_non_executable(name, changed_files):
|
|
471
|
+
warnings.append(f"skipped-non-executable:{name}")
|
|
472
|
+
continue
|
|
473
|
+
selected.append((name, is_opus))
|
|
474
|
+
elif scope == SCOPE_ON_DEMAND:
|
|
475
|
+
continue # never dispatched by the per-diff resolver, by design
|
|
476
|
+
elif scope == SCOPE_TEST_FILES:
|
|
477
|
+
if test_files is None:
|
|
478
|
+
selected.append((name, is_opus))
|
|
479
|
+
warnings.append(f"unnarrowed-test-files:{name}")
|
|
480
|
+
elif any(f in test_files for f in changed_files):
|
|
481
|
+
selected.append((name, is_opus))
|
|
482
|
+
elif _is_added_only_scope(scope):
|
|
483
|
+
if not _added_only_eligible(scope[1], changed_files, added_files):
|
|
484
|
+
continue
|
|
485
|
+
selected.append((name, is_opus))
|
|
486
|
+
if added_files is None:
|
|
487
|
+
warnings.append(f"unnarrowed-added-only:{name}")
|
|
488
|
+
elif _scope_matches(scope, changed_files):
|
|
489
|
+
selected.append((name, is_opus))
|
|
490
|
+
# Stable cheap-first: False (non-opus) sorts before True (opus).
|
|
491
|
+
selected.sort(key=lambda pair: pair[1])
|
|
492
|
+
return [name for name, _ in selected], warnings
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def _registry_lens_names(registry_text: str) -> list[str]:
|
|
496
|
+
"""Names in the Review Agents table section of agent-registry.md."""
|
|
497
|
+
names: list[str] = []
|
|
498
|
+
in_section = False
|
|
499
|
+
for line in registry_text.splitlines():
|
|
500
|
+
if line.startswith("## "):
|
|
501
|
+
in_section = _REVIEW_AGENTS_HEADING_MARKER in line.lower()
|
|
502
|
+
continue
|
|
503
|
+
if in_section:
|
|
504
|
+
m = _TABLE_ROW_RE.match(line)
|
|
505
|
+
if m:
|
|
506
|
+
names.append(m.group(1))
|
|
507
|
+
return names
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def build_review_roster(agents_dir: Path, registry_path: Path):
|
|
511
|
+
"""I/O boundary: build ``roster = [(name, scope, is_opus)]`` from disk.
|
|
512
|
+
|
|
513
|
+
Fails open: an unreadable registry yields ``([], [warning])``; an unreadable
|
|
514
|
+
agent file is include-biased (``scope=None`` -> included + warned) rather
|
|
515
|
+
than raising.
|
|
516
|
+
"""
|
|
517
|
+
warnings: list[str] = []
|
|
518
|
+
try:
|
|
519
|
+
registry_text = registry_path.read_text(encoding="utf-8")
|
|
520
|
+
except OSError:
|
|
521
|
+
return [], [f"unreadable-registry:{registry_path.name}"]
|
|
522
|
+
roster = []
|
|
523
|
+
for name in _registry_lens_names(registry_text):
|
|
524
|
+
if name in review_roster.NON_REVIEW_AGENTS or name in MANIFEST_GOVERNED:
|
|
525
|
+
continue
|
|
526
|
+
try:
|
|
527
|
+
text = (agents_dir / f"{name}.md").read_text(encoding="utf-8")
|
|
528
|
+
except OSError:
|
|
529
|
+
roster.append((name, None, False)) # include-biased fail-open
|
|
530
|
+
continue
|
|
531
|
+
roster.append((name, parse_scope(text), parse_model_is_opus(text)))
|
|
532
|
+
return roster, warnings
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _read_lines_from(path_or_dash: str):
|
|
536
|
+
"""Read newline-separated, non-blank lines from a file or stdin (``-``).
|
|
537
|
+
|
|
538
|
+
Returns ``(lines, error)``. Never raises — a missing or unreadable file
|
|
539
|
+
contributes no lines, matching `changed_file_list.py`'s identical helper
|
|
540
|
+
— but unlike that helper, the failure is reported back via ``error``
|
|
541
|
+
(``None`` on success, including a genuinely empty readable source) so
|
|
542
|
+
the caller can tell "nothing was there" apart from "couldn't read it"
|
|
543
|
+
and warn on the latter. Silently treating the two the same would make an
|
|
544
|
+
unreadable ``--files-from`` indistinguishable from a legitimate
|
|
545
|
+
"nothing changed" result.
|
|
546
|
+
"""
|
|
547
|
+
try:
|
|
548
|
+
if path_or_dash == "-":
|
|
549
|
+
text = sys.stdin.read()
|
|
550
|
+
else:
|
|
551
|
+
with open(path_or_dash, encoding="utf-8") as handle:
|
|
552
|
+
text = handle.read()
|
|
553
|
+
except (OSError, ValueError) as exc:
|
|
554
|
+
return [], type(exc).__name__
|
|
555
|
+
return [line for line in text.splitlines() if line.strip()], None
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def main(argv=None) -> int:
|
|
559
|
+
parser = argparse.ArgumentParser(description="Resolve applicable review lenses.")
|
|
560
|
+
parser.add_argument("--files", nargs="*", default=[], help="Changed file paths")
|
|
561
|
+
parser.add_argument(
|
|
562
|
+
"--files-from", default=None,
|
|
563
|
+
help=(
|
|
564
|
+
"Read newline-separated changed-file paths from this file "
|
|
565
|
+
"('-' for stdin), combined with --files. Prefer this for a "
|
|
566
|
+
"machine-generated or caller-uncontrolled list: it avoids "
|
|
567
|
+
"argparse's own argv-parsing hazards (a path starting with '-' "
|
|
568
|
+
"reinterpreted as a flag, word-splitting on an unquoted space) "
|
|
569
|
+
"— it does NOT by itself protect against a caller that "
|
|
570
|
+
"builds the file this reads from by shell-interpolating "
|
|
571
|
+
"untrusted path text; that boundary is the caller's to hold "
|
|
572
|
+
"(mirrors changed_file_list.py's --name-status-from)."
|
|
573
|
+
),
|
|
574
|
+
)
|
|
575
|
+
parser.add_argument(
|
|
576
|
+
"--added", nargs="*", default=None,
|
|
577
|
+
help=(
|
|
578
|
+
"Paths that are newly added (git change-type A) in this "
|
|
579
|
+
"changeset, e.g. from changed_file_list.py's 'added' list. "
|
|
580
|
+
"Narrows any 'Scope: added-only' lens to only these paths; "
|
|
581
|
+
"omit both this and --added-from when the caller has no "
|
|
582
|
+
"change-type data (fail-safe: added-only scopes then match "
|
|
583
|
+
"like a plain glob list)."
|
|
584
|
+
),
|
|
585
|
+
)
|
|
586
|
+
parser.add_argument(
|
|
587
|
+
"--added-from", default=None,
|
|
588
|
+
help=(
|
|
589
|
+
"Read newline-separated added-file paths from this file "
|
|
590
|
+
"('-' for stdin), combined with --added. Same caveat as "
|
|
591
|
+
"--files-from."
|
|
592
|
+
),
|
|
593
|
+
)
|
|
594
|
+
parser.add_argument("--agents-dir", type=Path, default=_HERE.parent / "agents")
|
|
595
|
+
parser.add_argument(
|
|
596
|
+
"--registry",
|
|
597
|
+
type=Path,
|
|
598
|
+
default=_HERE.parent / "knowledge" / "agent-registry.md",
|
|
599
|
+
)
|
|
600
|
+
args = parser.parse_args(argv)
|
|
601
|
+
if args.files_from == "-" and args.added_from == "-":
|
|
602
|
+
parser.error(
|
|
603
|
+
"--files-from and --added-from cannot both read stdin ('-') — "
|
|
604
|
+
"the second read gets nothing, silently. Use a file path or "
|
|
605
|
+
"process substitution for at least one."
|
|
606
|
+
)
|
|
607
|
+
roster, roster_warnings = build_review_roster(args.agents_dir, args.registry)
|
|
608
|
+
|
|
609
|
+
resolver_warnings: list[str] = []
|
|
610
|
+
|
|
611
|
+
files = list(args.files)
|
|
612
|
+
if args.files_from:
|
|
613
|
+
extra, error = _read_lines_from(args.files_from)
|
|
614
|
+
files.extend(extra)
|
|
615
|
+
if error:
|
|
616
|
+
resolver_warnings.append(f"unreadable-files-from:{args.files_from}")
|
|
617
|
+
|
|
618
|
+
added_files = None
|
|
619
|
+
if args.added is not None or args.added_from:
|
|
620
|
+
added_files = set(args.added or [])
|
|
621
|
+
if args.added_from:
|
|
622
|
+
extra, error = _read_lines_from(args.added_from)
|
|
623
|
+
added_files.update(extra)
|
|
624
|
+
if error:
|
|
625
|
+
resolver_warnings.append(f"unreadable-added-from:{args.added_from}")
|
|
626
|
+
|
|
627
|
+
# Computed here, never asked of the caller: unlike `added_files` (a git
|
|
628
|
+
# change-type only the caller's diff knows), test-ness is derivable from
|
|
629
|
+
# the checkout this process can already see, so requiring a flag for it
|
|
630
|
+
# would add a way for a caller to get it wrong for no information gain.
|
|
631
|
+
lenses, warnings = applicable_lenses(
|
|
632
|
+
files, roster, added_files=added_files, test_files=test_file_subset(files)
|
|
633
|
+
)
|
|
634
|
+
all_warnings = roster_warnings + resolver_warnings + warnings
|
|
635
|
+
print(json.dumps({"lenses": lenses, "warnings": all_warnings}))
|
|
636
|
+
return 0
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
if __name__ == "__main__":
|
|
640
|
+
raise SystemExit(main())
|