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,537 @@
|
|
|
1
|
+
"""pre_commit_detect — shared `git commit` detection for PreToolUse:Bash hooks.
|
|
2
|
+
|
|
3
|
+
Python port of hooks/lib/pre-commit-detect.sh (#576 / #572 Cluster B).
|
|
4
|
+
Extended under #709 to recognize bare `-n` as a bypass flag identically to
|
|
5
|
+
`--no-verify` (parity fix — `hooks/telemetry.py`'s bypass detection already
|
|
6
|
+
matched bare `-n`; this detector did not).
|
|
7
|
+
|
|
8
|
+
Extended again (#1801, independent security-review finding surfaced during
|
|
9
|
+
#1761/#1762/#1763's Slice 3 build, then through THREE rounds of
|
|
10
|
+
correctness-review/security-review re-review before this version shipped):
|
|
11
|
+
the original detector only matched `git commit` at the very start of the
|
|
12
|
+
command string, so a handful of behaviorally-identical invocations skipped
|
|
13
|
+
the gate with no bypass-audit trail at all. Round 1 widened the match with
|
|
14
|
+
segment-split-then-regex-match; round 2 replaced that with per-segment
|
|
15
|
+
shlex tokenization; each round's attempt FAILED its own re-review and was
|
|
16
|
+
replaced rather than patched — regex-over-raw-text couldn't see through
|
|
17
|
+
shell quoting, per-segment tokenization still split on raw-string
|
|
18
|
+
separator characters BEFORE tokenizing (letting a quoted separator inside
|
|
19
|
+
a git-option value bisect the invocation), and neither modeled a
|
|
20
|
+
clustered shell flag (`bash -lc '...'`), a wrapper's own options
|
|
21
|
+
(`sudo -u <user>`), a path-qualified interpreter (`/bin/bash -c '...'`),
|
|
22
|
+
nested `<shell> -c` wrapping more than one level deep, or a bare newline
|
|
23
|
+
as a real statement separator without also breaking a multi-line quoted
|
|
24
|
+
value. This version:
|
|
25
|
+
|
|
26
|
+
- masks every BARE (non-quoted) newline to `;` before tokenizing
|
|
27
|
+
(`_mask_bare_newlines`, a small quote-tracking character scan — not a
|
|
28
|
+
full shell parser, just enough to know "am I inside a quote right
|
|
29
|
+
now") so a bare newline acts as a real statement separator while one
|
|
30
|
+
embedded in a quoted value (a multi-line commit message) stays part
|
|
31
|
+
of that value's own token instead of splitting it;
|
|
32
|
+
- tokenizes the WHOLE (masked) command in ONE quote-aware
|
|
33
|
+
`shlex.shlex(..., punctuation_chars=True)` pass with `commenters`
|
|
34
|
+
explicitly cleared (`shlex.split`'s convenience wrapper clears this by
|
|
35
|
+
default; constructing `shlex.shlex` directly does not, so an
|
|
36
|
+
un-cleared `#` mid-line silently discarded the rest of that line and
|
|
37
|
+
everything after it — a real regression the second round introduced
|
|
38
|
+
and this version closes);
|
|
39
|
+
- splits the resulting token list on separator TOKENS
|
|
40
|
+
(`_split_on_separators`) rather than raw-string characters — a
|
|
41
|
+
separator inside a quoted value is never a boundary — and further
|
|
42
|
+
decomposes any token shlex glued into a run of adjacent control
|
|
43
|
+
characters (`;(`, `)&&(`, `|&`) back into its constituent one/two-
|
|
44
|
+
character operators (`_split_control_token`) so a separator directly
|
|
45
|
+
adjacent to unrelated punctuation still creates a boundary;
|
|
46
|
+
- recognizes a shell name (matched on basename, so `/bin/bash` and
|
|
47
|
+
`bash` are treated alike) followed by a bare `-c` or a clustered
|
|
48
|
+
short-flag group ending in `c` (`-lc`, `-ec`, `-ic`) and recurses
|
|
49
|
+
GENUINELY into that argument's own segments — including any FURTHER
|
|
50
|
+
nested `<shell> -c` wrapping inside it, up to a small depth bound
|
|
51
|
+
(`_MAX_NEST_DEPTH`) as a defensive backstop, not a realistic ceiling;
|
|
52
|
+
- walks git's OWN global-option grammar token-by-token
|
|
53
|
+
(`_match_commit_tokens`) rather than enumerating every option's exact
|
|
54
|
+
spelling in a regex — an unrecognized boolean option is still safely
|
|
55
|
+
skipped (the "invert the allowlist" choice), and `git`/wrapper-word
|
|
56
|
+
matching is basename-aware the same way shell-name matching is
|
|
57
|
+
(`/usr/bin/git commit`, `/usr/bin/sudo git commit` both match);
|
|
58
|
+
- skips a wrapper word's OWN options using a table KEYED BY WRAPPER
|
|
59
|
+
(`_WRAPPER_VALUE_TAKING_OPTS`), not one flat set shared across all
|
|
60
|
+
wrappers — `-n` takes a value for `nice` but is boolean for `sudo`,
|
|
61
|
+
and a shared set would have misparsed `sudo -n git commit` by
|
|
62
|
+
swallowing `git` as `-n`'s value;
|
|
63
|
+
- resolved EVERY matching segment's `-C` chain independently (not just
|
|
64
|
+
the first) via `resolve_commit_targets` (deleted #1904 — see "Exposes"
|
|
65
|
+
above — once its sole caller, `pre_commit_review.py`'s per-target
|
|
66
|
+
commit-gating, was retired to a no-op by #1886; this paragraph
|
|
67
|
+
documents the #1801 hardening history, not current exposed surface),
|
|
68
|
+
each segment falling back to
|
|
69
|
+
the caller's own `payload_cwd` when it has no usable `-C` — this is
|
|
70
|
+
what closes the decoy-redirection bypass without over-blocking the
|
|
71
|
+
everyday single-segment case: a prepended `git -C <decoy> commit ...;
|
|
72
|
+
git commit -m real` has a SECOND matching segment with no `-C` of its
|
|
73
|
+
own, which resolves to `payload_cwd` on its own merits, so the payload
|
|
74
|
+
repo's own review can no longer be skipped by a decoy elsewhere in the
|
|
75
|
+
same command — while a lone `git -C <dir> commit` run from a
|
|
76
|
+
`payload_cwd` that isn't a git repository at all (ordinary, legitimate
|
|
77
|
+
`-C` usage) still resolves to `<dir>` alone, not a forced, failing
|
|
78
|
+
check of an unrelated non-repo directory.
|
|
79
|
+
|
|
80
|
+
This is still a best-effort heuristic, not a real shell parser — see the
|
|
81
|
+
"KNOWN RESIDUAL GAPS" list in `hooks/pre_commit_review.py` for what remains
|
|
82
|
+
uncovered (a `git`/shell alias, a genuinely PATH-shadowed non-standard
|
|
83
|
+
binary named `git`/`bash`/etc — as opposed to the absolute-path spelling
|
|
84
|
+
of the REAL one, which this module now recognizes — `eval`, `xargs`,
|
|
85
|
+
compound-command keywords this module doesn't enumerate, `cd`/`pushd`
|
|
86
|
+
changing a LATER segment's effective target, a wrapper option taking a
|
|
87
|
+
separate-token value before `-c` this module doesn't model
|
|
88
|
+
(`bash -o pipefail -c '...'`), and a wrapper or git global option needing
|
|
89
|
+
a separate-token value and not enumerated here at all). Widening the match
|
|
90
|
+
can only make DETECTION more eager, never less — a segment that looks like
|
|
91
|
+
`git ... commit` inside an unrecognized wrapper's argument can
|
|
92
|
+
false-POSITIVE into "this is a commit", which is the safe direction for a
|
|
93
|
+
fail-closed gate; it can't false-negative a real commit a narrower matcher
|
|
94
|
+
would have caught. Target RESOLUTION was different: `resolve_commit_targets`
|
|
95
|
+
(deleted #1904) could only ADD targets to check per matching segment, never
|
|
96
|
+
remove a segment's own real target from the set that gets evaluated, including
|
|
97
|
+
the one disclosed exception (a `-C`
|
|
98
|
+
chain that can't be safely resolved falls back to `payload_cwd`, which can
|
|
99
|
+
under-cover a commit genuinely aimed elsewhere).
|
|
100
|
+
|
|
101
|
+
Imported by the Python sibling of its one remaining live caller:
|
|
102
|
+
- hooks/pre_commit_knowledge_index.py (via `is_git_commit_invocation`)
|
|
103
|
+
|
|
104
|
+
`resolve_commit_targets()` (the multi-target `-C`/`--git-dir`/`--work-tree`
|
|
105
|
+
resolution this module used to expose) was deleted in #1904: its sole
|
|
106
|
+
caller, `hooks/pre_commit_review.py`'s per-target commit-gating, was
|
|
107
|
+
already retired to a no-op by #1886's PR-time gate migration (`gh pr
|
|
108
|
+
create` has no `-C`-shaped multi-repository ambiguity for
|
|
109
|
+
`hooks/pre_pr_review.py`, the successor hook, to resolve — see that
|
|
110
|
+
module's own docstring). Confirmed orphaned by repo-wide grep before
|
|
111
|
+
deletion, not merely unused by one caller.
|
|
112
|
+
|
|
113
|
+
Exposes:
|
|
114
|
+
|
|
115
|
+
is_git_commit_command(cmd: str) -> bool
|
|
116
|
+
True when `cmd` is a `git commit` invocation, regardless of any
|
|
117
|
+
bypass flag. Callers that need to distinguish "gate-worthy commit"
|
|
118
|
+
from "bypassed commit" should also consult `has_bypass_flag`.
|
|
119
|
+
|
|
120
|
+
has_bypass_flag(cmd: str) -> bool
|
|
121
|
+
True when `cmd` carries `--no-verify` or a bare `-n` argument — git's
|
|
122
|
+
two spellings of "skip hooks". Substring/word-bounded match; does not
|
|
123
|
+
require `cmd` to be a `git commit` invocation.
|
|
124
|
+
|
|
125
|
+
bypass_flag_name(cmd: str) -> Optional[str]
|
|
126
|
+
"--no-verify" or "-n" (whichever matched; `--no-verify` preferred when
|
|
127
|
+
both are present), or None when `cmd` carries no bypass flag.
|
|
128
|
+
|
|
129
|
+
is_git_commit_invocation(cmd: str) -> bool
|
|
130
|
+
True when `cmd` is a `git commit` we should gate on (no bypass flag).
|
|
131
|
+
Kept for backward compatibility with existing callers/tests.
|
|
132
|
+
|
|
133
|
+
Stdlib-only. See docs/python-hook-contract.md.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
from __future__ import annotations
|
|
137
|
+
|
|
138
|
+
import re
|
|
139
|
+
import shlex
|
|
140
|
+
|
|
141
|
+
# Recognized shell interpreters whose own `-c`/clustered-`-c` flag takes a
|
|
142
|
+
# nested command as its argument (`bash -c '...'`, `bash -lc '...'`).
|
|
143
|
+
# Matched on basename (see `_word`) so `/bin/bash` is recognized too.
|
|
144
|
+
_SHELL_NAMES = frozenset({"bash", "sh", "zsh", "dash", "ksh"})
|
|
145
|
+
|
|
146
|
+
# Token forms that separate one command from the next within a single
|
|
147
|
+
# already-tokenized command. `_tokenize_command` (via `punctuation_chars=
|
|
148
|
+
# True`) emits `&&`/`||` as single two-character tokens and `;`/`|`/`&` as
|
|
149
|
+
# single characters — never split out of a quoted value. A bare (non-
|
|
150
|
+
# quoted) newline is masked to `;` before tokenizing (`_mask_bare_
|
|
151
|
+
# newlines`) rather than relying on shlex's own newline handling, which
|
|
152
|
+
# treats every newline as plain whitespace regardless of quoting.
|
|
153
|
+
_SEPARATOR_TOKENS = frozenset({";", "&&", "||", "|", "&"})
|
|
154
|
+
|
|
155
|
+
# Characters shlex's `punctuation_chars` mode can glue into ONE token when
|
|
156
|
+
# they appear adjacent with no separating whitespace (`;(`, `)&&(`, `|&`).
|
|
157
|
+
# `_split_control_token` decomposes such a token back into its constituent
|
|
158
|
+
# one/two-character operators so a separator adjacent to unrelated
|
|
159
|
+
# punctuation still creates a segment boundary.
|
|
160
|
+
_CONTROL_CHARS = frozenset(";&|()<>")
|
|
161
|
+
|
|
162
|
+
# Leading shell "noise" tokens that can precede `git` without changing what
|
|
163
|
+
# it targets: command wrappers that just re-exec their remaining argv
|
|
164
|
+
# (`env`, `exec`, `command`, `nohup`, `time`, `nice`, `sudo`, `setsid`,
|
|
165
|
+
# `doas`), and compound-command keywords that can immediately precede a
|
|
166
|
+
# command word
|
|
167
|
+
# (`then`, `do`, `else`, `elif`) plus grouping/negation tokens (`!`, `(`,
|
|
168
|
+
# `{`) that tokenize as standalone tokens when space-separated.
|
|
169
|
+
_WRAPPER_WORDS = frozenset(
|
|
170
|
+
{"env", "exec", "command", "nohup", "time", "nice", "sudo", "setsid", "doas"}
|
|
171
|
+
)
|
|
172
|
+
_LEADING_NOISE_TOKENS = frozenset({"then", "do", "else", "elif", "!", "(", "{"})
|
|
173
|
+
|
|
174
|
+
# Wrapper options that take a separate-token value, KEYED BY WRAPPER WORD
|
|
175
|
+
# rather than one flat set shared across all of them (#1801 third-round
|
|
176
|
+
# re-review): `-n` takes a value for `nice` but is boolean for `sudo`, and
|
|
177
|
+
# a shared set misparsed `sudo -n git commit` by swallowing `git` itself
|
|
178
|
+
# as `-n`'s value. A wrapper/option pair not listed here is safely
|
|
179
|
+
# boolean-skipped (same "invert the allowlist" choice as git's own
|
|
180
|
+
# options) — it only misparses if it ALSO takes a separate value this
|
|
181
|
+
# table doesn't know about.
|
|
182
|
+
_WRAPPER_VALUE_TAKING_OPTS: dict[str, frozenset[str]] = {
|
|
183
|
+
"sudo": frozenset({"-u", "-g", "-p", "-h", "-C"}),
|
|
184
|
+
"doas": frozenset({"-u", "-C", "-a"}),
|
|
185
|
+
"nice": frozenset({"-n"}),
|
|
186
|
+
"env": frozenset({"-u", "-C", "-S"}),
|
|
187
|
+
"exec": frozenset({"-a"}),
|
|
188
|
+
"time": frozenset({"-o", "-f", "--output", "--format"}),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# A bare `NAME=value` shell-assignment token — no `env` keyword required
|
|
192
|
+
# (an earlier version required it, missing the far more common bare
|
|
193
|
+
# `X=1 git commit` spelling — correctness/security re-review finding).
|
|
194
|
+
_ASSIGNMENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
|
|
195
|
+
|
|
196
|
+
# git global options that consume exactly one following token as their
|
|
197
|
+
# argument when not already attached via `=` in the same token (`-c` always
|
|
198
|
+
# consumes its next token even when valueless — `-c core.pager` sets a
|
|
199
|
+
# boolean true — matching git's own arg parser, not just the `name=value`
|
|
200
|
+
# case a fixed regex atom previously required).
|
|
201
|
+
_VALUE_TAKING_OPTS = frozenset(
|
|
202
|
+
{"-c", "--namespace", "--super-prefix", "--exec-path", "--attr-source", "--config-env"}
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Defensive backstop for `<shell> -c`-wrapping nesting depth
|
|
206
|
+
# (`_all_segments`) — genuinely recursive, not a realistic ceiling on how
|
|
207
|
+
# deep real commands nest; just insurance against a pathological input.
|
|
208
|
+
_MAX_NEST_DEPTH = 4
|
|
209
|
+
|
|
210
|
+
# The documented bypass. Substring match (no word boundary) — mirrors the
|
|
211
|
+
# .sh's `grep -qE -- '--no-verify'` byte-for-byte: `--no-verifying`, though
|
|
212
|
+
# not a real git flag, also short-circuits under the .sh, so the port does
|
|
213
|
+
# the same. Byte-parity is the migration contract.
|
|
214
|
+
_NO_VERIFY_RE = re.compile(r"--no-verify")
|
|
215
|
+
|
|
216
|
+
# git's short-form bypass flag. Word-bounded (whitespace or string edges) so
|
|
217
|
+
# it doesn't false-positive inside `-nx` or `--number`. Mirrors
|
|
218
|
+
# `hooks/telemetry.py`'s `_NO_VERIFY_RE` pattern.
|
|
219
|
+
_BARE_N_RE = re.compile(r"(?:^|\s)-n(?:\s|$)")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _mask_bare_newlines(cmd: str) -> str:
|
|
223
|
+
"""Replace every newline OUTSIDE a single/double-quoted region with
|
|
224
|
+
`;` — a real statement separator, the same as what a shell itself
|
|
225
|
+
treats a bare newline as — while a newline INSIDE a quoted region
|
|
226
|
+
(a multi-line commit message) is left untouched so it stays part of
|
|
227
|
+
that value's own token instead of being cut in half. A POSIX
|
|
228
|
+
backslash-newline pair (a line continuation — `git add -A && \\`
|
|
229
|
+
followed by a newline then `git commit -m x`) is DELETED entirely,
|
|
230
|
+
matching what a real shell does with it, rather than preserved: an
|
|
231
|
+
earlier version of this function treated `\\`+newline the same as any
|
|
232
|
+
other escaped character (kept both, verbatim), which fed
|
|
233
|
+
`_tokenize_command`'s shlex pass a literal embedded newline instead of
|
|
234
|
+
nothing — silently gluing the following word onto the newline and
|
|
235
|
+
hiding `git`/`commit` from every downstream check (#1801 fifth-round
|
|
236
|
+
re-review).
|
|
237
|
+
|
|
238
|
+
A small character-by-character quote-tracking scan, not a full shell
|
|
239
|
+
parser: both backslash branches are gated on being OUTSIDE a quote
|
|
240
|
+
(`quote is None`) — it performs no in-quote escape handling at all,
|
|
241
|
+
while POSIX double-quote escaping is actually BROADER than this scan
|
|
242
|
+
(`\\"`, `\\$`, `` \\` `` are all recognized escapes there this scan
|
|
243
|
+
doesn't treat as such). That's exactly why a double-quoted string
|
|
244
|
+
containing an escaped copy of its OWN quote character can end this
|
|
245
|
+
scan's quote-tracking early — a known, disclosed limitation (see
|
|
246
|
+
KNOWN RESIDUAL GAPS in hooks/pre_commit_review.py) rather than
|
|
247
|
+
something this function claims to handle."""
|
|
248
|
+
out = []
|
|
249
|
+
quote: str | None = None
|
|
250
|
+
i, n = 0, len(cmd)
|
|
251
|
+
while i < n:
|
|
252
|
+
ch = cmd[i]
|
|
253
|
+
if quote is None and ch == "\\" and i + 1 < n and cmd[i + 1] == "\n":
|
|
254
|
+
i += 2 # line continuation: delete the pair, emit nothing
|
|
255
|
+
continue
|
|
256
|
+
if quote is None and ch == "\\" and i + 1 < n:
|
|
257
|
+
out.append(ch)
|
|
258
|
+
out.append(cmd[i + 1])
|
|
259
|
+
i += 2
|
|
260
|
+
continue
|
|
261
|
+
if quote is not None and ch == quote:
|
|
262
|
+
quote = None
|
|
263
|
+
out.append(ch)
|
|
264
|
+
elif quote is None and ch in ("'", '"'):
|
|
265
|
+
quote = ch
|
|
266
|
+
out.append(ch)
|
|
267
|
+
elif quote is None and ch == "\n":
|
|
268
|
+
out.append(";")
|
|
269
|
+
else:
|
|
270
|
+
out.append(ch)
|
|
271
|
+
i += 1
|
|
272
|
+
return "".join(out)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _tokenize_command(cmd: str) -> list[str]:
|
|
276
|
+
"""POSIX-shell-tokenize the WHOLE (newline-masked) `cmd` in one pass,
|
|
277
|
+
resolving quotes/escapes the same way a real shell would — this is
|
|
278
|
+
what closes the quoting-evasion class (`git "commit"`, `'git' commit`,
|
|
279
|
+
`\\git commit`) a plain string search cannot, keeps a quoted value
|
|
280
|
+
containing whitespace or a separator character (`git -C "/a b"
|
|
281
|
+
commit`, `git -c user.name="A; B" commit`) as ONE token instead of
|
|
282
|
+
splitting it, and — via `commenters = \"\"` — never discards the rest
|
|
283
|
+
of a line at an unquoted `#` the way `shlex.shlex`'s default
|
|
284
|
+
`commenters` would (`shlex.split`'s convenience wrapper clears this;
|
|
285
|
+
constructing `shlex.shlex` directly does not, and an earlier version
|
|
286
|
+
of this module didn't either — a real regression this version
|
|
287
|
+
closes). Falls back to a naive whitespace split on unbalanced quotes
|
|
288
|
+
— an edge case a real shell would itself reject as malformed, so
|
|
289
|
+
treating it as "can't tokenize, don't try to guess" is the safe
|
|
290
|
+
posture."""
|
|
291
|
+
masked = _mask_bare_newlines(cmd)
|
|
292
|
+
try:
|
|
293
|
+
lex = shlex.shlex(masked, posix=True, punctuation_chars=True)
|
|
294
|
+
lex.whitespace_split = True
|
|
295
|
+
lex.commenters = ""
|
|
296
|
+
return list(lex)
|
|
297
|
+
except ValueError:
|
|
298
|
+
return masked.split()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _split_control_token(tok: str) -> list[str]:
|
|
302
|
+
"""Split a token shlex glued from adjacent control characters (`;(`,
|
|
303
|
+
`)&&(`, `|&`) back into its constituent one- or two-character shell
|
|
304
|
+
operators, greedily preferring the two-character forms (`&&`, `||`)
|
|
305
|
+
— the operators a real shell lexer would recognize, just not glued in
|
|
306
|
+
this token's raw form."""
|
|
307
|
+
pieces = []
|
|
308
|
+
i, n = 0, len(tok)
|
|
309
|
+
while i < n:
|
|
310
|
+
two = tok[i : i + 2]
|
|
311
|
+
if two in ("&&", "||"):
|
|
312
|
+
pieces.append(two)
|
|
313
|
+
i += 2
|
|
314
|
+
else:
|
|
315
|
+
pieces.append(tok[i])
|
|
316
|
+
i += 1
|
|
317
|
+
return pieces
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _split_on_separators(tokens: list[str]) -> list[list[str]]:
|
|
321
|
+
"""Split an already-tokenized command into segments at
|
|
322
|
+
`_SEPARATOR_TOKENS` boundaries, first decomposing any token composed
|
|
323
|
+
entirely of `_CONTROL_CHARS` via `_split_control_token` so a
|
|
324
|
+
separator glued to adjacent, unrelated punctuation (`;(`, `|&`) still
|
|
325
|
+
creates a boundary. Empty segments (a leading/trailing/doubled
|
|
326
|
+
separator) are dropped."""
|
|
327
|
+
segments: list[list[str]] = [[]]
|
|
328
|
+
for tok in tokens:
|
|
329
|
+
if tok and all(c in _CONTROL_CHARS for c in tok):
|
|
330
|
+
pieces = _split_control_token(tok)
|
|
331
|
+
else:
|
|
332
|
+
pieces = [tok]
|
|
333
|
+
for piece in pieces:
|
|
334
|
+
if piece in _SEPARATOR_TOKENS:
|
|
335
|
+
segments.append([])
|
|
336
|
+
else:
|
|
337
|
+
segments[-1].append(piece)
|
|
338
|
+
return [segment for segment in segments if segment]
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _word(tok: str) -> str:
|
|
342
|
+
"""The basename of `tok` when it looks path-qualified, else `tok`
|
|
343
|
+
itself — so `/bin/bash`, `/usr/bin/git`, and `/usr/bin/sudo` are
|
|
344
|
+
recognized the same as their bare spellings (#1801 third-round
|
|
345
|
+
re-review: exact-token comparisons elsewhere in this module used to
|
|
346
|
+
miss every absolute-path spelling of the same binary)."""
|
|
347
|
+
return tok.rsplit("/", 1)[-1]
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _extract_shell_dash_c_bodies(tokens: list[str]) -> list[str]:
|
|
351
|
+
"""Scan an already-tokenized segment for a recognized shell name
|
|
352
|
+
(matched via `_word`, so path-qualified spellings count too) followed
|
|
353
|
+
by a bare `-c` or a clustered short-flag group ENDING in `c`
|
|
354
|
+
(`bash -lc '...'`, `sh -ec "..."`, `zsh -ic '...'`) and return each
|
|
355
|
+
such argument string found — the nested command a real shell would
|
|
356
|
+
separately execute. Best-effort: a wrapper option that itself takes a
|
|
357
|
+
separate-token value before `-c` (`bash -o pipefail -c '...'`) is not
|
|
358
|
+
modeled and remains a disclosed residual gap (see
|
|
359
|
+
`pre_commit_review.py`'s KNOWN RESIDUAL GAPS) — modeling every shell's
|
|
360
|
+
exact getopt grammar is out of scope for this heuristic."""
|
|
361
|
+
bodies = []
|
|
362
|
+
for i, tok in enumerate(tokens):
|
|
363
|
+
if _word(tok) not in _SHELL_NAMES:
|
|
364
|
+
continue
|
|
365
|
+
j = i + 1
|
|
366
|
+
while j < len(tokens) and tokens[j].startswith("-"):
|
|
367
|
+
flag = tokens[j]
|
|
368
|
+
if flag == "-c" or (len(flag) > 2 and flag[-1] == "c" and flag[1:-1].isalpha()):
|
|
369
|
+
if j + 1 < len(tokens):
|
|
370
|
+
bodies.append(tokens[j + 1])
|
|
371
|
+
break
|
|
372
|
+
j += 1
|
|
373
|
+
return bodies
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _all_segments(cmd: str, _depth: int = 0) -> list[list[str]]:
|
|
377
|
+
"""Every already-tokenized segment worth checking for a `git ...
|
|
378
|
+
commit` match: `cmd` tokenized and split on separator tokens, plus the
|
|
379
|
+
same treatment GENUINELY recursively applied to every `<shell> -c`/
|
|
380
|
+
clustered-`-c` argument `_extract_shell_dash_c_bodies` finds within
|
|
381
|
+
those segments — including further nesting inside THOSE bodies too,
|
|
382
|
+
up to `_MAX_NEST_DEPTH` (a defensive backstop, not a realistic
|
|
383
|
+
ceiling; an earlier version of this function only extracted one level
|
|
384
|
+
despite its own docstring claiming otherwise — #1801 third-round
|
|
385
|
+
re-review)."""
|
|
386
|
+
if _depth > _MAX_NEST_DEPTH:
|
|
387
|
+
return []
|
|
388
|
+
segments = _split_on_separators(_tokenize_command(cmd))
|
|
389
|
+
all_found = list(segments)
|
|
390
|
+
for segment in segments:
|
|
391
|
+
for body in _extract_shell_dash_c_bodies(segment):
|
|
392
|
+
all_found.extend(_all_segments(body, _depth + 1))
|
|
393
|
+
return all_found
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _advance_past_value(tokens: list[str], i: int, has_attached_value: bool) -> int:
|
|
397
|
+
"""Advance past the option token at `tokens[i]` and, unless its value
|
|
398
|
+
was `=`-attached to the same token, past its separate-token value too
|
|
399
|
+
(when one remains). Shared by the `--git-dir`/`--work-tree` and
|
|
400
|
+
`_VALUE_TAKING_OPTS` branches of `_match_commit_tokens`, which
|
|
401
|
+
otherwise repeat this exact sequence (structure-review + complexity-
|
|
402
|
+
review both independently flagged the duplication, #1801 re-review).
|
|
403
|
+
|
|
404
|
+
`has_attached_value` must reflect whether `=` was PRESENT in the
|
|
405
|
+
token, not the truthiness of the value after it (#1801 sixth-round
|
|
406
|
+
re-review: `tok.partition("=")`'s value is `""` for BOTH "no `=` at
|
|
407
|
+
all" and "`=` present with an empty value", e.g. `--namespace=`; the
|
|
408
|
+
caller must pass the separator's own truthiness, not the value's, or
|
|
409
|
+
an option with a present-but-empty attached value gets treated as
|
|
410
|
+
needing a separate-token value and wrongly consumes the next token —
|
|
411
|
+
which, for the LAST option before `commit`, silently swallows
|
|
412
|
+
`commit` itself)."""
|
|
413
|
+
i += 1
|
|
414
|
+
if not has_attached_value and i < len(tokens):
|
|
415
|
+
i += 1
|
|
416
|
+
return i
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _match_commit_tokens(tokens: list[str]) -> tuple[bool, list[str], bool]:
|
|
420
|
+
"""Return `(is_commit, dash_c_chain, has_repo_override)` for an
|
|
421
|
+
already-tokenized command. `dash_c_chain` is every `-C` argument found,
|
|
422
|
+
IN ORDER (git's own left-to-right `-C` chaining — NOT "last wins").
|
|
423
|
+
`has_repo_override` is True when `--git-dir`/`--work-tree` also
|
|
424
|
+
appeared. Both were consumed by the now-deleted `resolve_commit_targets()`
|
|
425
|
+
(#1904, orphaned once `pre_commit_review.py`'s per-target commit-gating
|
|
426
|
+
was retired) — `is_git_commit_command()` below only needs the boolean
|
|
427
|
+
`is_commit` element, but the tuple shape is left unchanged rather than
|
|
428
|
+
narrowed, since narrowing it is an unrelated refactor with no behavioral
|
|
429
|
+
payoff for this module's one remaining consumer."""
|
|
430
|
+
i, n = 0, len(tokens)
|
|
431
|
+
while i < n:
|
|
432
|
+
if _ASSIGNMENT_RE.match(tokens[i]) or tokens[i] in _LEADING_NOISE_TOKENS:
|
|
433
|
+
i += 1
|
|
434
|
+
continue
|
|
435
|
+
wrapper = _word(tokens[i])
|
|
436
|
+
if wrapper in _WRAPPER_WORDS:
|
|
437
|
+
i += 1
|
|
438
|
+
# Skip the wrapper's OWN options too (#1801 third-round
|
|
439
|
+
# re-review) — `sudo -u dev`, `nice -n 10`, `env -i` used to
|
|
440
|
+
# stop this loop one token early, leaving `git` unreachable.
|
|
441
|
+
# The value-taking set is keyed by THIS wrapper specifically
|
|
442
|
+
# (`-n` is nice's value-taking option, sudo's boolean one).
|
|
443
|
+
value_opts = _WRAPPER_VALUE_TAKING_OPTS.get(wrapper, frozenset())
|
|
444
|
+
while i < n and tokens[i].startswith("-"):
|
|
445
|
+
if tokens[i] in value_opts and i + 1 < n:
|
|
446
|
+
i += 2
|
|
447
|
+
else:
|
|
448
|
+
i += 1
|
|
449
|
+
continue
|
|
450
|
+
break
|
|
451
|
+
if i >= n or _word(tokens[i]) != "git":
|
|
452
|
+
return False, [], False
|
|
453
|
+
i += 1
|
|
454
|
+
dash_c_chain: list[str] = []
|
|
455
|
+
has_repo_override = False
|
|
456
|
+
while i < n:
|
|
457
|
+
tok = tokens[i]
|
|
458
|
+
if tok == "commit":
|
|
459
|
+
return True, dash_c_chain, has_repo_override
|
|
460
|
+
if not tok.startswith("-"):
|
|
461
|
+
return False, [], False
|
|
462
|
+
opt, sep, value = tok.partition("=")
|
|
463
|
+
if opt == "-C":
|
|
464
|
+
i += 1
|
|
465
|
+
if sep:
|
|
466
|
+
dash_c_chain.append(value)
|
|
467
|
+
elif i < n:
|
|
468
|
+
dash_c_chain.append(tokens[i])
|
|
469
|
+
i += 1
|
|
470
|
+
continue
|
|
471
|
+
if opt in ("--git-dir", "--work-tree"):
|
|
472
|
+
has_repo_override = True
|
|
473
|
+
i = _advance_past_value(tokens, i, bool(sep))
|
|
474
|
+
continue
|
|
475
|
+
if opt in _VALUE_TAKING_OPTS:
|
|
476
|
+
i = _advance_past_value(tokens, i, bool(sep))
|
|
477
|
+
continue
|
|
478
|
+
# Unknown or known-boolean `-`-prefixed token: skip it alone.
|
|
479
|
+
# "Invert the allowlist" (#1801 re-review): an unrecognized FUTURE
|
|
480
|
+
# git global option that takes no value is still safely skipped;
|
|
481
|
+
# one that takes a separate-token value and isn't enumerated above
|
|
482
|
+
# remains a disclosed residual gap.
|
|
483
|
+
i += 1
|
|
484
|
+
return False, [], False
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _all_commit_matches(cmd: str) -> list[tuple[list[str], bool]]:
|
|
488
|
+
"""Every matching segment's `(dash_c_chain, has_repo_override)` — ALL
|
|
489
|
+
of them, not just the first (#1801 re-review: returning only the first
|
|
490
|
+
match let a prepended decoy `-C` segment become the sole thing
|
|
491
|
+
resolved — a concern for the now-deleted `resolve_commit_targets()`,
|
|
492
|
+
#1904; `is_git_commit_command()` below only needs the length of this
|
|
493
|
+
list, not which segment's `-C` chain "won")."""
|
|
494
|
+
if not cmd:
|
|
495
|
+
return []
|
|
496
|
+
matches = []
|
|
497
|
+
for segment in _all_segments(cmd):
|
|
498
|
+
matched, dash_c_chain, has_repo_override = _match_commit_tokens(segment)
|
|
499
|
+
if matched:
|
|
500
|
+
matches.append((dash_c_chain, has_repo_override))
|
|
501
|
+
return matches
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def is_git_commit_command(cmd: str) -> bool:
|
|
505
|
+
"""Return True iff `cmd` is a `git commit` invocation (bypass or not)."""
|
|
506
|
+
return bool(_all_commit_matches(cmd))
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def has_bypass_flag(cmd: str) -> bool:
|
|
510
|
+
"""Return True iff `cmd` carries `--no-verify` or a bare `-n` flag."""
|
|
511
|
+
return bypass_flag_name(cmd) is not None
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def bypass_flag_name(cmd: str) -> str | None:
|
|
515
|
+
"""Return which bypass flag matched (`--no-verify` preferred), or None."""
|
|
516
|
+
if not cmd:
|
|
517
|
+
return None
|
|
518
|
+
if _NO_VERIFY_RE.search(cmd):
|
|
519
|
+
return "--no-verify"
|
|
520
|
+
if _BARE_N_RE.search(cmd):
|
|
521
|
+
return "-n"
|
|
522
|
+
return None
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def is_git_commit_invocation(cmd: str) -> bool:
|
|
526
|
+
"""Return True iff `cmd` is a gate-worthy `git commit` invocation."""
|
|
527
|
+
if not is_git_commit_command(cmd):
|
|
528
|
+
return False
|
|
529
|
+
return not has_bypass_flag(cmd)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
__all__ = (
|
|
533
|
+
"bypass_flag_name",
|
|
534
|
+
"has_bypass_flag",
|
|
535
|
+
"is_git_commit_command",
|
|
536
|
+
"is_git_commit_invocation",
|
|
537
|
+
)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""hooks/lib/pre_commit_doc_classifier.py — the `.review-passed` gate's
|
|
2
|
+
doc-only exemption predicate (#1461, extracted from `pre_commit_review.py`
|
|
3
|
+
under #1477).
|
|
4
|
+
|
|
5
|
+
Moved out of `pre_commit_review.py` (which had grown to ~759 lines, mixing
|
|
6
|
+
the hook stdin/exit-code contract, git querying, plugin-version resolution,
|
|
7
|
+
bypass-audit recording, boundary-event emission, doc-only classification,
|
|
8
|
+
and the gate-verdict decision engine all in one file) so that module can
|
|
9
|
+
stay to hook-contract plumbing and wiring, with this predicate as its own
|
|
10
|
+
independently testable, independently reviewable unit.
|
|
11
|
+
|
|
12
|
+
Re-derives the doc-only exemption's OWN predicate (#1461 security review)
|
|
13
|
+
rather than trusting the claim in the ledger event: `--event doc-only`
|
|
14
|
+
(`boundary_events.py`) is written by the SAME untrusted party this gate
|
|
15
|
+
exists to constrain, so an unconditional "the event exists, therefore trust
|
|
16
|
+
it" check is the classic trust-the-client pattern — nothing previously
|
|
17
|
+
verified the exempted diff was actually documentation.
|
|
18
|
+
|
|
19
|
+
Deliberately a TIGHTER re-implementation of `code-review/SKILL.md`'s
|
|
20
|
+
doc-only short-circuit and `skills/code-review/scripts/change_shape.py`'s
|
|
21
|
+
`_is_documentation` (built on `hooks/lib/doc_classification.py`'s SHARED
|
|
22
|
+
literal tables, but not a lockstep copy of `change_shape.py`'s matching
|
|
23
|
+
logic — #1461 second security re-review found the original copy
|
|
24
|
+
exploitable): those two only ever ADD a review lens or skip everything on
|
|
25
|
+
already-honest input; this function is the sole gate deciding whether a
|
|
26
|
+
"record zero dispatch evidence" exemption is honored at all, so it must be
|
|
27
|
+
a STRICT SUBSET of what SKILL.md/change_shape.py would call documentation,
|
|
28
|
+
never a superset. Concretely: (1) a root-doc EXACT NAME (README, LICENSE,
|
|
29
|
+
...) only counts at the repository root (`len(parts) == 1`) AND only when
|
|
30
|
+
the stem carries NO extension at all — `license_manager.py` staged at the
|
|
31
|
+
repo root must never match merely because its basename starts with
|
|
32
|
+
"license", and (#1461 FOURTH security re-review) `license-check` or
|
|
33
|
+
`readme-lint` must never match either, despite also being extensionless: an
|
|
34
|
+
earlier fix used `stem.startswith(prefix)`, which still admitted any
|
|
35
|
+
extensionless name merely beginning with a root-doc prefix. Exact
|
|
36
|
+
membership in the doc-root word set is the only form that matches this
|
|
37
|
+
comment's own stated intent ("the genuinely extensionless case: LICENSE,
|
|
38
|
+
NOTICE, AUTHORS") — a `README.txt`-shaped file is already covered by the
|
|
39
|
+
`DOC_EXTENSIONS` branch above, so this branch exists ONLY for those exact
|
|
40
|
+
extensionless root docs, nothing glob-shaped; (2) a `docs/` directory
|
|
41
|
+
membership alone is NOT sufficient — a doc extension is still required,
|
|
42
|
+
since `docs/conf.py` or a doc-site build script is executable code that
|
|
43
|
+
happens to live under `docs/`. Path segments are lower-cased once, matching
|
|
44
|
+
the stem, so a case-insensitive checkout (`Agents/foo.md`, `.Claude/x.md`)
|
|
45
|
+
can't escape the functional-config exclusion below by case alone.
|
|
46
|
+
`CORE_FUNCTIONAL_CONFIG_SEGMENTS`'s bare `"agents"` entry already matches
|
|
47
|
+
`templates/agents/`, and any other `.../agents/...` path, exactly per
|
|
48
|
+
`code-review/SKILL.md`'s literal stated rule — no separate
|
|
49
|
+
templates-specific helper is needed. If this rule ever needs to change,
|
|
50
|
+
change it here first and only loosen the lens-narrowing siblings to
|
|
51
|
+
match — never the reverse.
|
|
52
|
+
|
|
53
|
+
Stdlib only.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
from __future__ import annotations
|
|
57
|
+
|
|
58
|
+
import sys
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
|
|
61
|
+
_LIB_DIR = Path(__file__).resolve().parent
|
|
62
|
+
if str(_LIB_DIR) not in sys.path:
|
|
63
|
+
sys.path.insert(0, str(_LIB_DIR))
|
|
64
|
+
|
|
65
|
+
from doc_classification import (
|
|
66
|
+
CORE_FUNCTIONAL_CONFIG_SEGMENTS,
|
|
67
|
+
DOC_EXTENSIONS,
|
|
68
|
+
DOC_ROOT_WORDS,
|
|
69
|
+
FUNCTIONAL_CONFIG_NAMES,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
_DOC_ROOT_NAMES = frozenset(DOC_ROOT_WORDS)
|
|
73
|
+
|
|
74
|
+
# `.txt` is in `DOC_EXTENSIONS` for real plain-text docs (README.txt), but a
|
|
75
|
+
# `.txt`/`.md`-shaped dependency manifest or build file is executable/
|
|
76
|
+
# supply-chain surface, not documentation (#1461 fourth security re-review) —
|
|
77
|
+
# denylisted by exact name regardless of matching a doc extension above.
|
|
78
|
+
_NON_DOC_NAMES_DESPITE_EXTENSION = {
|
|
79
|
+
"requirements.txt", "requirements-dev.txt", "constraints.txt",
|
|
80
|
+
"cmakelists.txt",
|
|
81
|
+
}
|
|
82
|
+
# Same denylist, by directory instead of exact basename (#1461 fifth
|
|
83
|
+
# security re-review): the common multi-file pip layout
|
|
84
|
+
# (`requirements/base.txt`, `constraints/pins.txt`) escapes the exact-name
|
|
85
|
+
# check above while still matching `DOC_EXTENSIONS`.
|
|
86
|
+
_NON_DOC_DIR_SEGMENTS = {"requirements", "constraints"}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def is_doc_only_changeset(files: list[str]) -> bool:
|
|
90
|
+
"""True only when EVERY given path is provably documentation and none is
|
|
91
|
+
functional Claude-config markdown. An empty list — or a list whose every
|
|
92
|
+
entry is blank/unusable — is not doc-only: there is nothing to exempt,
|
|
93
|
+
so this must never vacuously pass."""
|
|
94
|
+
saw_any = False
|
|
95
|
+
for raw in files:
|
|
96
|
+
name = raw.strip()
|
|
97
|
+
if not name:
|
|
98
|
+
continue
|
|
99
|
+
parts = [p.lower() for p in name.replace("\\", "/").split("/") if p]
|
|
100
|
+
if not parts:
|
|
101
|
+
continue
|
|
102
|
+
saw_any = True
|
|
103
|
+
stem = parts[-1]
|
|
104
|
+
suffix = "." + stem.rsplit(".", 1)[-1] if "." in stem else ""
|
|
105
|
+
in_non_doc_dir = suffix in DOC_EXTENSIONS and any(
|
|
106
|
+
seg in _NON_DOC_DIR_SEGMENTS for seg in parts[:-1]
|
|
107
|
+
)
|
|
108
|
+
if (
|
|
109
|
+
stem in FUNCTIONAL_CONFIG_NAMES
|
|
110
|
+
or stem in _NON_DOC_NAMES_DESPITE_EXTENSION
|
|
111
|
+
or in_non_doc_dir
|
|
112
|
+
or any(seg in CORE_FUNCTIONAL_CONFIG_SEGMENTS for seg in parts)
|
|
113
|
+
):
|
|
114
|
+
return False
|
|
115
|
+
if suffix in DOC_EXTENSIONS:
|
|
116
|
+
continue
|
|
117
|
+
# A root-doc EXACT NAME (README, LICENSE, ...) only counts at the
|
|
118
|
+
# repo root — never at any depth, never with an extension, and
|
|
119
|
+
# never merely as a name PREFIX (`license-check` is not "license").
|
|
120
|
+
if len(parts) == 1 and stem in _DOC_ROOT_NAMES:
|
|
121
|
+
continue
|
|
122
|
+
return False
|
|
123
|
+
return saw_any
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
__all__ = ("is_doc_only_changeset",)
|