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,322 @@
|
|
|
1
|
+
"""token_efficiency_review.py — Metrics-based token efficiency reviewer.
|
|
2
|
+
|
|
3
|
+
Checks files for token-efficiency violations:
|
|
4
|
+
- CLAUDE.md: character count > 5000 (error), top-level rule count > 200 (error)
|
|
5
|
+
- Any file: line count > 500 (warning)
|
|
6
|
+
- .md files: optional LLM quality review (unless --skip-llm)
|
|
7
|
+
|
|
8
|
+
Exit codes (matches review_result contract):
|
|
9
|
+
0 = pass (clean)
|
|
10
|
+
1 = fail (hard errors)
|
|
11
|
+
2 = warnings only OR LLM check skipped
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
python3 ${CLAUDE_PLUGIN_ROOT}/scripts/token_efficiency_review.py --files <path> [<path>...] [--skip-llm]
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import re
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
26
|
+
from lib.review_result import build_result, main_exit, make_issue, skipped_llm_warning
|
|
27
|
+
|
|
28
|
+
sys.path.insert(
|
|
29
|
+
0,
|
|
30
|
+
str(Path(__file__).resolve().parent.parent / "hooks" / "lib"),
|
|
31
|
+
)
|
|
32
|
+
from token_efficiency_limits import (
|
|
33
|
+
CLAUDE_MD_CHAR_LIMIT,
|
|
34
|
+
CLAUDE_MD_RULE_LIMIT,
|
|
35
|
+
FILE_LINE_LIMIT,
|
|
36
|
+
is_claude_md_file,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# CLAUDE.md checks
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _strip_frontmatter(text: str) -> str:
|
|
45
|
+
"""Remove YAML frontmatter (--- ... ---) from the top of text."""
|
|
46
|
+
lines = text.splitlines()
|
|
47
|
+
if not lines or lines[0].strip() != "---":
|
|
48
|
+
return text
|
|
49
|
+
# Find closing ---
|
|
50
|
+
for idx in range(1, len(lines)):
|
|
51
|
+
if lines[idx].strip() == "---":
|
|
52
|
+
return "\n".join(lines[idx + 1 :])
|
|
53
|
+
# No closing marker found — treat entire text as body
|
|
54
|
+
return text
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _count_top_level_bullets(text: str) -> int:
|
|
58
|
+
"""Count top-level bullet/numbered list items at indent level 0.
|
|
59
|
+
|
|
60
|
+
Patterns counted:
|
|
61
|
+
- ^- (unordered bullet)
|
|
62
|
+
- ^\\d+\\. (ordered list item)
|
|
63
|
+
|
|
64
|
+
Excludes lines inside YAML frontmatter (already stripped by caller).
|
|
65
|
+
"""
|
|
66
|
+
count = 0
|
|
67
|
+
bullet_re = re.compile(r"^(-\s|\d+\.\s)")
|
|
68
|
+
for line in text.splitlines():
|
|
69
|
+
if bullet_re.match(line):
|
|
70
|
+
count += 1
|
|
71
|
+
return count
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def check_claude_md(path: Path) -> list[dict]:
|
|
75
|
+
"""Run CLAUDE.md-specific checks. Returns list of issue dicts."""
|
|
76
|
+
issues: list[dict] = []
|
|
77
|
+
try:
|
|
78
|
+
text = path.read_text(encoding="utf-8", errors="replace")
|
|
79
|
+
except OSError as exc:
|
|
80
|
+
issues.append(
|
|
81
|
+
make_issue(
|
|
82
|
+
"error",
|
|
83
|
+
"high",
|
|
84
|
+
str(path),
|
|
85
|
+
0,
|
|
86
|
+
f"Cannot read file: {exc}",
|
|
87
|
+
rule_id="read-error",
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
return issues
|
|
91
|
+
|
|
92
|
+
# 1. Character count
|
|
93
|
+
char_count = len(text)
|
|
94
|
+
if char_count > CLAUDE_MD_CHAR_LIMIT:
|
|
95
|
+
issues.append(
|
|
96
|
+
make_issue(
|
|
97
|
+
"error",
|
|
98
|
+
"high",
|
|
99
|
+
str(path),
|
|
100
|
+
0,
|
|
101
|
+
(
|
|
102
|
+
f"CLAUDE.md exceeds character limit: {char_count} chars "
|
|
103
|
+
f"(limit {CLAUDE_MD_CHAR_LIMIT}). Trim or move content to skills."
|
|
104
|
+
),
|
|
105
|
+
"Extract procedures into skill files; remove duplicate or verbose sections.",
|
|
106
|
+
rule_id="claude-md-char-limit",
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# 2. Top-level rule/bullet count
|
|
111
|
+
body = _strip_frontmatter(text)
|
|
112
|
+
rule_count = _count_top_level_bullets(body)
|
|
113
|
+
if rule_count > CLAUDE_MD_RULE_LIMIT:
|
|
114
|
+
issues.append(
|
|
115
|
+
make_issue(
|
|
116
|
+
"error",
|
|
117
|
+
"high",
|
|
118
|
+
str(path),
|
|
119
|
+
0,
|
|
120
|
+
(
|
|
121
|
+
f"CLAUDE.md has {rule_count} top-level bullet/rule items "
|
|
122
|
+
f"(limit {CLAUDE_MD_RULE_LIMIT}). Consolidate or move items to skills."
|
|
123
|
+
),
|
|
124
|
+
"Group related rules into a skill file; remove duplicate instructions.",
|
|
125
|
+
rule_id="claude-md-rule-limit",
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return issues
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
# Line count check
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def check_line_counts(files: list[Path]) -> list[dict]:
|
|
138
|
+
"""Return warning findings for files exceeding FILE_LINE_LIMIT lines."""
|
|
139
|
+
issues: list[dict] = []
|
|
140
|
+
for path in files:
|
|
141
|
+
try:
|
|
142
|
+
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
143
|
+
except OSError:
|
|
144
|
+
continue
|
|
145
|
+
line_count = len(lines)
|
|
146
|
+
if line_count > FILE_LINE_LIMIT:
|
|
147
|
+
issues.append(
|
|
148
|
+
make_issue(
|
|
149
|
+
"warning",
|
|
150
|
+
"high",
|
|
151
|
+
str(path),
|
|
152
|
+
0,
|
|
153
|
+
(
|
|
154
|
+
f"File exceeds line limit: {line_count} lines "
|
|
155
|
+
f"(limit {FILE_LINE_LIMIT}). Consider splitting."
|
|
156
|
+
),
|
|
157
|
+
"Extract sections into separate files or skills.",
|
|
158
|
+
rule_id="file-line-limit",
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
return issues
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ---------------------------------------------------------------------------
|
|
165
|
+
# LLM review
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def is_prose_file(path: Path) -> bool:
|
|
170
|
+
"""Return True when the file is a prose/markdown file suitable for LLM review."""
|
|
171
|
+
return path.suffix.lower() == ".md"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def run_llm_review(path: Path) -> list[dict]:
|
|
175
|
+
"""Call claude CLI to review a prose file. Returns warning-severity findings.
|
|
176
|
+
|
|
177
|
+
LLM unavailability → returns a single warning finding (exit 2, not 1).
|
|
178
|
+
"""
|
|
179
|
+
prompt = (
|
|
180
|
+
"Review this file for LLM anti-patterns that waste tokens in Claude Code config files:\n"
|
|
181
|
+
"- Role preambles (You are a..., Act as...)\n"
|
|
182
|
+
"- Conversational filler (Please note, It's important to)\n"
|
|
183
|
+
"- Redundant context (same info repeated)\n"
|
|
184
|
+
"- Hedging language (You might want to, Consider)\n"
|
|
185
|
+
"- Verbose explanations before instructions\n\n"
|
|
186
|
+
f"File: {path}\n"
|
|
187
|
+
f"Content:\n{path.read_text(encoding='utf-8', errors='replace')[:3000]}\n\n"
|
|
188
|
+
"Output a JSON array of findings (may be empty []). Each finding: "
|
|
189
|
+
'{"message": "...", "line": 0, "suggestedFix": "..."}. '
|
|
190
|
+
"Do NOT include any text outside the JSON array."
|
|
191
|
+
)
|
|
192
|
+
issues: list[dict] = []
|
|
193
|
+
try:
|
|
194
|
+
result = subprocess.run(
|
|
195
|
+
["claude", "-p", prompt],
|
|
196
|
+
capture_output=True,
|
|
197
|
+
text=True,
|
|
198
|
+
timeout=60,
|
|
199
|
+
check=False,
|
|
200
|
+
)
|
|
201
|
+
if result.returncode != 0 or not result.stdout.strip():
|
|
202
|
+
issues.append(
|
|
203
|
+
make_issue(
|
|
204
|
+
"warning",
|
|
205
|
+
"high",
|
|
206
|
+
str(path),
|
|
207
|
+
0,
|
|
208
|
+
"LLM review unavailable (claude exited non-zero or no output)",
|
|
209
|
+
rule_id="llm-unavailable",
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
return issues
|
|
213
|
+
# Parse JSON output from LLM
|
|
214
|
+
import json
|
|
215
|
+
|
|
216
|
+
raw = result.stdout.strip()
|
|
217
|
+
# Extract JSON array if surrounded by extra text
|
|
218
|
+
start = raw.find("[")
|
|
219
|
+
end = raw.rfind("]")
|
|
220
|
+
if start == -1 or end == -1:
|
|
221
|
+
return issues
|
|
222
|
+
findings = json.loads(raw[start : end + 1])
|
|
223
|
+
for f in findings:
|
|
224
|
+
if not isinstance(f, dict):
|
|
225
|
+
continue
|
|
226
|
+
issues.append(
|
|
227
|
+
make_issue(
|
|
228
|
+
"warning", # LLM findings are always warning severity
|
|
229
|
+
"medium",
|
|
230
|
+
str(path),
|
|
231
|
+
int(f.get("line", 0)),
|
|
232
|
+
str(f.get("message", "LLM finding")),
|
|
233
|
+
str(f.get("suggestedFix", "")),
|
|
234
|
+
rule_id="llm-quality",
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
238
|
+
issues.append(
|
|
239
|
+
make_issue(
|
|
240
|
+
"warning",
|
|
241
|
+
"high",
|
|
242
|
+
str(path),
|
|
243
|
+
0,
|
|
244
|
+
"LLM review skipped (claude not found or timed out)",
|
|
245
|
+
rule_id="llm-unavailable",
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
return issues
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
# Main entry point
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def main(argv: list[str] | None = None) -> int:
|
|
257
|
+
parser = argparse.ArgumentParser(
|
|
258
|
+
description=__doc__,
|
|
259
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
260
|
+
)
|
|
261
|
+
parser.add_argument(
|
|
262
|
+
"--files",
|
|
263
|
+
nargs="+",
|
|
264
|
+
metavar="PATH",
|
|
265
|
+
default=[],
|
|
266
|
+
help="Files to review (required)",
|
|
267
|
+
)
|
|
268
|
+
parser.add_argument(
|
|
269
|
+
"--skip-llm",
|
|
270
|
+
action="store_true",
|
|
271
|
+
help="Skip LLM quality review; add an llm-skipped warning instead",
|
|
272
|
+
)
|
|
273
|
+
args = parser.parse_args(argv)
|
|
274
|
+
|
|
275
|
+
if not args.files:
|
|
276
|
+
print(
|
|
277
|
+
'{"status":"fail","issues":[{"severity":"error","confidence":"high","file":"","line":0,"message":"No files specified. Use --files <path>...","suggestedFix":"","rule_id":"no-files"}],"summary":""}'
|
|
278
|
+
)
|
|
279
|
+
return 1
|
|
280
|
+
|
|
281
|
+
files = [Path(p) for p in args.files]
|
|
282
|
+
errors: list[dict] = []
|
|
283
|
+
warnings: list[dict] = []
|
|
284
|
+
|
|
285
|
+
for path in files:
|
|
286
|
+
# CLAUDE.md-specific checks
|
|
287
|
+
if is_claude_md_file(path):
|
|
288
|
+
for issue in check_claude_md(path):
|
|
289
|
+
if issue["severity"] == "error":
|
|
290
|
+
errors.append(issue)
|
|
291
|
+
else:
|
|
292
|
+
warnings.append(issue)
|
|
293
|
+
|
|
294
|
+
# Line count checks for all files
|
|
295
|
+
warnings.extend(check_line_counts(files))
|
|
296
|
+
|
|
297
|
+
# LLM review for prose (.md) files
|
|
298
|
+
prose_files = [p for p in files if is_prose_file(p)]
|
|
299
|
+
if args.skip_llm:
|
|
300
|
+
# Unconditional by design: --skip-llm is explicit intent to skip, so
|
|
301
|
+
# the warning is recorded whether or not any prose file was in scope.
|
|
302
|
+
# This was previously an if/else whose two branches were identical
|
|
303
|
+
# (#1651) — the condition read as though the prose-file case differed,
|
|
304
|
+
# and it never did.
|
|
305
|
+
warnings.append(skipped_llm_warning())
|
|
306
|
+
else:
|
|
307
|
+
for path in prose_files:
|
|
308
|
+
warnings.extend(run_llm_review(path))
|
|
309
|
+
|
|
310
|
+
summary_parts = []
|
|
311
|
+
if errors:
|
|
312
|
+
summary_parts.append(f"{len(errors)} error(s)")
|
|
313
|
+
if warnings:
|
|
314
|
+
summary_parts.append(f"{len(warnings)} warning(s)")
|
|
315
|
+
summary = ", ".join(summary_parts) if summary_parts else "No issues found."
|
|
316
|
+
|
|
317
|
+
result = build_result(errors, warnings, summary)
|
|
318
|
+
return main_exit(result)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
if __name__ == "__main__":
|
|
322
|
+
sys.exit(main())
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Scope a review dispatch to the delta the verdict ledger hasn't already
|
|
3
|
+
cleared (#2167).
|
|
4
|
+
|
|
5
|
+
Given a candidate `{lens: [file, ...], ...}` dispatch plan, consult
|
|
6
|
+
`hooks/lib/review_verdicts.py`'s per-lens verdict ledger
|
|
7
|
+
(`.claude/metrics/review-verdicts.jsonl`, written by
|
|
8
|
+
`hooks/review_verdict_recorder.py` on every real review-agent dispatch) and
|
|
9
|
+
split each lens's file list into the files that still need a live dispatch
|
|
10
|
+
and the ones an exact `(lens, file_path, current file_content_hash)` match
|
|
11
|
+
already cleared with outcome `"pass"`.
|
|
12
|
+
|
|
13
|
+
Shared by both call sites the epic (#2164) names: `/code-review` step 4 (a
|
|
14
|
+
repeat run over an unchanged target set should dispatch nothing) and
|
|
15
|
+
`/build`'s own inline checkpoints (sub-steps 4/6) plus its Step 6 backstop
|
|
16
|
+
(a slice already cleared at a checkpoint has nothing left for the backstop
|
|
17
|
+
to review) -- one resolver, not two independently-drifting copies, mirroring
|
|
18
|
+
`select_lenses.py`'s own shared-between-both-skills precedent.
|
|
19
|
+
|
|
20
|
+
## Fail-closed, by design (#2167 acceptance criteria)
|
|
21
|
+
|
|
22
|
+
A file is skipped ONLY on an exact `(lens, file_path, file_content_hash)`
|
|
23
|
+
match whose most-recent ledger row (rows are append-only; the last matching
|
|
24
|
+
row in file order is the most recent) has `outcome == "pass"`. Every other
|
|
25
|
+
case dispatches normally:
|
|
26
|
+
|
|
27
|
+
- No ledger, an unreadable ledger, or a line `load_verdicts` couldn't parse
|
|
28
|
+
-- `load_verdicts` itself already degrades these to "no usable rows"
|
|
29
|
+
(see that function's own docstring); this module inherits that posture
|
|
30
|
+
without adding a second one.
|
|
31
|
+
- A row whose `plugin_version` is older than the version running now --
|
|
32
|
+
same source: `load_verdicts` has already excluded it before this module
|
|
33
|
+
ever sees the row.
|
|
34
|
+
- The file can't be hashed right now (deleted, unreadable, not a regular
|
|
35
|
+
file, or over `review_verdicts.MAX_HASH_FILE_BYTES`) -- `hash_file`
|
|
36
|
+
returns `None`, and a `None` hash never matches anything, so the file
|
|
37
|
+
always stays in `toDispatch`.
|
|
38
|
+
- The most-recent matching row's outcome is `"findings"`, not `"pass"`.
|
|
39
|
+
- No row at all shares this exact `(lens, file_path, file_content_hash)`
|
|
40
|
+
triple -- a different lens, a different file, or the same file at
|
|
41
|
+
different content (a hash mismatch) are all "no match".
|
|
42
|
+
|
|
43
|
+
This is the same ambiguity-resolves-toward-more-work posture
|
|
44
|
+
`select_lenses.py`'s own docstring already documents for its resolver --
|
|
45
|
+
just applied to *whether* a lens/file needs a fresh dispatch rather than
|
|
46
|
+
*which* lenses apply at all.
|
|
47
|
+
|
|
48
|
+
## Canonicalization (#2167 correctness/security/arch review)
|
|
49
|
+
|
|
50
|
+
The ledger's own `file_path` is always the writer's canonical, `root`-
|
|
51
|
+
relative POSIX form (`hooks/review_verdict_recorder.py`'s own
|
|
52
|
+
`_resolve_under_cwd`/`canonical_path`). Every candidate file this module is
|
|
53
|
+
handed is canonicalized the SAME way (`_canonicalize_lens_files`,
|
|
54
|
+
`compute_file_hashes`) before either hashing or matching it against a
|
|
55
|
+
ledger row -- comparing a caller's raw spelling (`./a.py`, an absolute
|
|
56
|
+
path) against the writer's canonical one would otherwise silently never
|
|
57
|
+
match, defeating the whole point of this module without ever being wrong
|
|
58
|
+
(a missed skip, never a false one). A file that can't be canonicalized
|
|
59
|
+
(escapes `root`, unresolvable) keeps its raw string as a deliberately
|
|
60
|
+
unmatchable placeholder rather than raising or guessing.
|
|
61
|
+
|
|
62
|
+
Stdlib-only. See ADR 0014 / ADR 0015.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
from __future__ import annotations
|
|
66
|
+
|
|
67
|
+
import argparse
|
|
68
|
+
import json
|
|
69
|
+
import sys
|
|
70
|
+
from pathlib import Path
|
|
71
|
+
|
|
72
|
+
_HERE = Path(__file__).resolve().parent
|
|
73
|
+
_HOOKS_LIB_DIR = _HERE.parent / "hooks" / "lib"
|
|
74
|
+
if str(_HOOKS_LIB_DIR) not in sys.path:
|
|
75
|
+
sys.path.insert(0, str(_HOOKS_LIB_DIR))
|
|
76
|
+
|
|
77
|
+
from instrument_log import append_row # type: ignore[import-not-found]
|
|
78
|
+
|
|
79
|
+
# No `except ImportError` fallback (matches `select_lenses.py`'s own rule,
|
|
80
|
+
# #1968): `hooks/lib/` ships inside this same plugin, always present
|
|
81
|
+
# wherever this script runs. A hand-written second copy of
|
|
82
|
+
# `hash_file`/`load_verdicts` is exactly the kind of independently-drifting
|
|
83
|
+
# duplicate that rule exists to prevent -- here it would silently turn every
|
|
84
|
+
# lookup into a guaranteed miss (safe, but defeats the entire point of this
|
|
85
|
+
# module) rather than raising loudly.
|
|
86
|
+
try:
|
|
87
|
+
from review_verdicts import ( # type: ignore[import-not-found]
|
|
88
|
+
canonical_path,
|
|
89
|
+
hash_file,
|
|
90
|
+
load_verdicts,
|
|
91
|
+
)
|
|
92
|
+
except ImportError as exc: # pragma: no cover - broken install, not a supported mode
|
|
93
|
+
raise ImportError(
|
|
94
|
+
f"{__name__} requires 'review_verdicts' from the dev-team plugin's "
|
|
95
|
+
f"hooks/lib, which could not be imported.\n"
|
|
96
|
+
f" searched: {_HOOKS_LIB_DIR}\n"
|
|
97
|
+
f" exists: {_HOOKS_LIB_DIR.is_dir()}\n"
|
|
98
|
+
"This module deliberately has no fallback copy of the hashing/ledger "
|
|
99
|
+
"read logic -- a divergent stand-in would make every skip decision "
|
|
100
|
+
"silently wrong. Fix the install or the path rather than re-adding a "
|
|
101
|
+
"local implementation."
|
|
102
|
+
) from exc
|
|
103
|
+
|
|
104
|
+
_PASS_OUTCOME = "pass"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _latest_matching_verdict(
|
|
108
|
+
verdicts: list[dict], lens: str, file_path: str, file_hash: str
|
|
109
|
+
) -> dict | None:
|
|
110
|
+
"""The most-recent row (last one in append order) sharing the exact
|
|
111
|
+
`(lens, file_path, file_content_hash)` triple, or `None` if no row
|
|
112
|
+
matches at all."""
|
|
113
|
+
match: dict | None = None
|
|
114
|
+
for row in verdicts:
|
|
115
|
+
if (
|
|
116
|
+
row.get("lens") == lens
|
|
117
|
+
and row.get("file_path") == file_path
|
|
118
|
+
and row.get("file_content_hash") == file_hash
|
|
119
|
+
):
|
|
120
|
+
match = row
|
|
121
|
+
return match
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def resolve_dispatch(
|
|
125
|
+
lens_files: dict[str, list[str]],
|
|
126
|
+
verdicts: list[dict],
|
|
127
|
+
file_hashes: dict[str, str | None],
|
|
128
|
+
) -> dict:
|
|
129
|
+
"""Pure resolver. `lens_files` maps a lens name to the candidate files
|
|
130
|
+
already matched to it (by `Scope:`/change-shape/etc. gating upstream --
|
|
131
|
+
this function narrows *within* that set, it never widens it).
|
|
132
|
+
`file_hashes` maps each candidate file to its current content hash, or
|
|
133
|
+
`None` when it couldn't be hashed (see module docstring).
|
|
134
|
+
|
|
135
|
+
Returns `{"toDispatch": {lens: [file, ...]}, "skipped": {lens: [{"file":
|
|
136
|
+
..., "verdict": <the matched row>}, ...]}, "fullySkippedLenses":
|
|
137
|
+
[lens, ...]}`. A lens with at least one file left to dispatch appears in
|
|
138
|
+
`toDispatch` with only those files (its ledger-cleared files are simply
|
|
139
|
+
absent from that list, not present-but-empty); a lens with EVERY
|
|
140
|
+
candidate file cleared is absent from `toDispatch` entirely and named in
|
|
141
|
+
`fullySkippedLenses` -- the caller's signal to not dispatch that lens
|
|
142
|
+
this round at all. `lens_files` is preserved as the source of truth for
|
|
143
|
+
"was this lens even a candidate": a lens with an empty candidate list to
|
|
144
|
+
begin with never appears in any of the three outputs.
|
|
145
|
+
"""
|
|
146
|
+
to_dispatch: dict[str, list[str]] = {}
|
|
147
|
+
skipped: dict[str, list[dict]] = {}
|
|
148
|
+
|
|
149
|
+
for lens, files in lens_files.items():
|
|
150
|
+
keep: list[str] = []
|
|
151
|
+
skip_entries: list[dict] = []
|
|
152
|
+
for file_path in files:
|
|
153
|
+
file_hash = file_hashes.get(file_path)
|
|
154
|
+
row = (
|
|
155
|
+
_latest_matching_verdict(verdicts, lens, file_path, file_hash)
|
|
156
|
+
if file_hash
|
|
157
|
+
else None
|
|
158
|
+
)
|
|
159
|
+
if row is not None and row.get("outcome") == _PASS_OUTCOME:
|
|
160
|
+
skip_entries.append({"file": file_path, "verdict": row})
|
|
161
|
+
else:
|
|
162
|
+
keep.append(file_path)
|
|
163
|
+
if keep:
|
|
164
|
+
to_dispatch[lens] = keep
|
|
165
|
+
if skip_entries:
|
|
166
|
+
skipped[lens] = skip_entries
|
|
167
|
+
|
|
168
|
+
fully_skipped = [lens for lens in lens_files if lens in skipped and lens not in to_dispatch]
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
"toDispatch": to_dispatch,
|
|
172
|
+
"skipped": skipped,
|
|
173
|
+
"fullySkippedLenses": fully_skipped,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def compute_file_hashes(files, root: Path) -> dict[str, str | None]:
|
|
178
|
+
"""I/O boundary: hash every file in `files` (deduped), canonicalizing
|
|
179
|
+
each against `root` first (`review_verdicts.canonical_path` -- #2167
|
|
180
|
+
review: an absolute path, a `./`-prefixed path, or one outside `root`
|
|
181
|
+
entirely must never be naively joined onto `root` and hashed as-is). A
|
|
182
|
+
file that can't be canonicalized, doesn't exist under `root`, or that
|
|
183
|
+
`hash_file` otherwise can't read, maps to `None` -- never omitted, so a
|
|
184
|
+
caller iterating `file_hashes` can always find every candidate file's
|
|
185
|
+
entry."""
|
|
186
|
+
root_resolved = Path(root).resolve()
|
|
187
|
+
hashes: dict[str, str | None] = {}
|
|
188
|
+
for f in dict.fromkeys(files):
|
|
189
|
+
canonical = canonical_path(f, root)
|
|
190
|
+
hashes[f] = hash_file(root_resolved / canonical) if canonical is not None else None
|
|
191
|
+
return hashes
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _load_json_arg(value: str):
|
|
195
|
+
"""Load JSON from an inline JSON string, or from a file path when it
|
|
196
|
+
isn't one.
|
|
197
|
+
|
|
198
|
+
Tries the inline parse FIRST (#2167 correctness review): the previous
|
|
199
|
+
check-path-first order called `Path(value).exists()` on the raw
|
|
200
|
+
argument, and `Path.exists()` raises `OSError` (`ENAMETOOLONG` on
|
|
201
|
+
Linux) rather than returning `False` once `value` is a string longer
|
|
202
|
+
than `PATH_MAX` -- which any real `--lens-files` JSON for a multi-lens,
|
|
203
|
+
multi-file round easily is (a ~30-file, 10-lens round is already
|
|
204
|
+
several KB). A JSON literal is never also a path a caller would pass
|
|
205
|
+
here, so trying the inline parse first costs nothing in the common case
|
|
206
|
+
and never stats the filesystem with an oversized string."""
|
|
207
|
+
try:
|
|
208
|
+
return json.loads(value)
|
|
209
|
+
except ValueError:
|
|
210
|
+
return json.loads(Path(value).read_text())
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _canonicalize_lens_files(
|
|
214
|
+
lens_files: dict[str, list[str]], root: Path
|
|
215
|
+
) -> dict[str, list[str]]:
|
|
216
|
+
"""Canonicalize every candidate file against `root` before matching it
|
|
217
|
+
to the ledger (#2167 review: correctness/security/arch all
|
|
218
|
+
independently flagged that the writer always stores a canonical,
|
|
219
|
+
`root`-relative POSIX `file_path`, so comparing a caller's raw string
|
|
220
|
+
against it -- `./a.py`, an absolute path -- silently never matches a
|
|
221
|
+
genuine row). A file that can't be canonicalized keeps its RAW string
|
|
222
|
+
as a deliberately unmatchable placeholder: no genuine ledger row's
|
|
223
|
+
`file_path` can ever equal an un-canonicalizable string, so it always
|
|
224
|
+
falls through to `toDispatch` (fail-closed, never a false skip).
|
|
225
|
+
|
|
226
|
+
The caller sees the CANONICAL form back in `toDispatch`/`skipped`, not
|
|
227
|
+
its original spelling -- for every real call site (SKILL.md's own
|
|
228
|
+
already-`root`-relative file lists, from `changed_file_list.py`'s git
|
|
229
|
+
diff output) canonicalization is a no-op, so this is not a behavior
|
|
230
|
+
change there; only a caller passing an absolute or `./`-prefixed path
|
|
231
|
+
sees its spelling normalized in the response, which still names the
|
|
232
|
+
same file."""
|
|
233
|
+
return {
|
|
234
|
+
lens: [canonical_path(f, root) or f for f in files] for lens, files in lens_files.items()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def resolve_for_root(lens_files: dict[str, list[str]], root: Path) -> dict:
|
|
239
|
+
"""Canonicalize, hash, read the ledger under ``root``, and resolve -- the
|
|
240
|
+
same pipeline ``main`` runs, exposed so in-process callers (e.g.
|
|
241
|
+
``ship_review_gate.py``, #2212) never touch the ledger reader directly."""
|
|
242
|
+
lens_files = _canonicalize_lens_files(lens_files, root)
|
|
243
|
+
all_files = [f for files in lens_files.values() for f in files]
|
|
244
|
+
return resolve_dispatch(lens_files, load_verdicts(root), compute_file_hashes(all_files, root))
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _log_skips(lens_files: dict[str, list[str]], result: dict, root: Path) -> None:
|
|
248
|
+
"""Persist this consult's skip counts (#2201) -- fail-open, stdout untouched."""
|
|
249
|
+
skipped = sum(len(v) for v in result["skipped"].values())
|
|
250
|
+
append_row(
|
|
251
|
+
"ledger-skips",
|
|
252
|
+
{
|
|
253
|
+
"candidate_pairs": sum(len(v) for v in lens_files.values()),
|
|
254
|
+
"skipped_pairs": skipped,
|
|
255
|
+
"fully_skipped_lenses": result["fullySkippedLenses"],
|
|
256
|
+
},
|
|
257
|
+
cwd=root,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def main(argv: list[str] | None = None) -> int:
|
|
262
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
263
|
+
parser.add_argument("--root", default=".", help="Repo/worktree root the ledger lives under")
|
|
264
|
+
parser.add_argument(
|
|
265
|
+
"--lens-files",
|
|
266
|
+
required=True,
|
|
267
|
+
help='JSON (file or inline) mapping lens name to its candidate file list: {"<lens>": ["<file>", ...], ...}',
|
|
268
|
+
)
|
|
269
|
+
args = parser.parse_args(argv)
|
|
270
|
+
|
|
271
|
+
root = Path(args.root)
|
|
272
|
+
lens_files = _canonicalize_lens_files(_load_json_arg(args.lens_files), root)
|
|
273
|
+
|
|
274
|
+
all_files = [f for files in lens_files.values() for f in files]
|
|
275
|
+
file_hashes = compute_file_hashes(all_files, root)
|
|
276
|
+
verdicts = load_verdicts(root)
|
|
277
|
+
|
|
278
|
+
result = resolve_dispatch(lens_files, verdicts, file_hashes)
|
|
279
|
+
_log_skips(lens_files, result, root)
|
|
280
|
+
print(json.dumps(result))
|
|
281
|
+
return 0
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
if __name__ == "__main__":
|
|
285
|
+
raise SystemExit(main())
|