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,118 @@
|
|
|
1
|
+
"""Model-pricing loader, rate lookup, and cost computation (issue #2045,
|
|
2
|
+
epic #2040).
|
|
3
|
+
|
|
4
|
+
Shared by `hooks/lib/cost_meter.py` (the Stop-hook cost meter) and
|
|
5
|
+
`session_report.py --profile maintainer` (originally a separate monorepo-only
|
|
6
|
+
session digest extractor, retired in #2048; #2045 unified the two). Before
|
|
7
|
+
this module existed each carried its
|
|
8
|
+
own near-identical copy of `_load_pricing`/`_rate`/`_cost`, and the two had
|
|
9
|
+
quietly drifted: cost_meter's loader raised on a missing/unreadable
|
|
10
|
+
pricing file where the extractor's degraded to an empty table, and
|
|
11
|
+
cost_meter's `_cost` required its caller to guard `if rate else 0.0` (it
|
|
12
|
+
indexed `rate["input"]` directly) where the extractor's guarded internally
|
|
13
|
+
and read `rate.get(...)`. Unified here on the more defensive shape throughout: an
|
|
14
|
+
absent/corrupt/`None` pricing path yields `{}` rather than raising, and
|
|
15
|
+
`cost()` itself returns `0.0` for an unpriced model rather than requiring
|
|
16
|
+
every caller to remember the ternary — consistent with this file's own
|
|
17
|
+
fail-open philosophy (see `cost_meter.py`'s `cmd_record`/`cmd_phase_mark`,
|
|
18
|
+
which return 0 on a missing transcript rather than raising: a hook must
|
|
19
|
+
never break a turn).
|
|
20
|
+
|
|
21
|
+
Placement — NOT `plugins/dev-team/scripts/lib/session_log/`, despite that
|
|
22
|
+
package's `__init__.py` briefly forward-declaring a `pricing` module for
|
|
23
|
+
this issue (corrected in the same commit that adds this file). Reason:
|
|
24
|
+
`hooks/lib/cost_meter.py` is a real Stop hook, registered in `settings.json`
|
|
25
|
+
and run on every session in every installed project.
|
|
26
|
+
`hooks/lib/review_agent_registry.py` and `hooks/lib/review_dispatch_ledger.py`
|
|
27
|
+
already established the rule this module follows — "a hook must be
|
|
28
|
+
import-safe without any `scripts/` module on its path; the dependency
|
|
29
|
+
direction is scripts/ -> hooks/lib/, never the reverse" (#1461) — so a
|
|
30
|
+
module a hook needs lives in `hooks/lib/`, and the `scripts/` side reaches
|
|
31
|
+
into it, exactly mirroring how `scripts/check_review_agent_mcp_tools.py`
|
|
32
|
+
already imports `review_agent_registry` from `hooks/lib/`. Putting pricing
|
|
33
|
+
here instead of `session_log/` keeps that boundary intact rather than
|
|
34
|
+
inverting it for this one module.
|
|
35
|
+
|
|
36
|
+
Downstream-cost decision (#2045): `session_report.py --profile downstream`
|
|
37
|
+
(originally a separate shipped script, retired in #2048; the shipped
|
|
38
|
+
report a downstream user hands to the plugin
|
|
39
|
+
maintainer) does NOT gain a cost figure as part of this issue, and does not
|
|
40
|
+
import this module. ADR 0036 already decided this narrower question — "The extractors'
|
|
41
|
+
divergent layers stay forked: discovery/CLI surface, **pricing and cost**,
|
|
42
|
+
rollup, escalation, and the report shapes... cost/pricing/rollup/escalation
|
|
43
|
+
exist only in the monorepo one. Divergence is not drift." — for exactly the
|
|
44
|
+
reason issue #2045 itself names as the argument against: a pricing table
|
|
45
|
+
shipped inside the plugin is unverifiable by the downstream user and goes
|
|
46
|
+
stale between releases, so it's presented in `cost_meter.py`'s own reports
|
|
47
|
+
(which run in the environment that produced the spend) rather than baked
|
|
48
|
+
into a portable summary file.
|
|
49
|
+
|
|
50
|
+
Stdlib only. See ADR 0014 / ADR 0015.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
from __future__ import annotations
|
|
54
|
+
|
|
55
|
+
import json
|
|
56
|
+
from pathlib import Path
|
|
57
|
+
|
|
58
|
+
DEFAULT_CACHE_WRITE_MULTIPLIER = 1.25
|
|
59
|
+
DEFAULT_CACHE_READ_MULTIPLIER = 0.1
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _usage_field(usage: dict, field: str) -> int | float:
|
|
63
|
+
"""`.get(field, 0) or 0` — the one canonical usage-field reader (matches
|
|
64
|
+
`scripts/lib/session_log/records.usage_field`'s null-handling contract;
|
|
65
|
+
duplicated here rather than imported, in service of the hooks/lib ->
|
|
66
|
+
scripts/ dependency direction described in the module docstring above —
|
|
67
|
+
this module must not reach INTO `scripts/lib/session_log/` any more than
|
|
68
|
+
`cost_meter.py` may)."""
|
|
69
|
+
return usage.get(field, 0) or 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def load_pricing(path: Path | None) -> dict:
|
|
73
|
+
"""Load a model-pricing.json table. Defensive: an absent path, a `None`
|
|
74
|
+
path, or malformed JSON all degrade to `{}` (every model then prices at
|
|
75
|
+
$0.00 and is flagged `unpriced`) rather than raising."""
|
|
76
|
+
if not path or not Path(path).is_file():
|
|
77
|
+
return {}
|
|
78
|
+
try:
|
|
79
|
+
return json.loads(Path(path).read_text())
|
|
80
|
+
except (OSError, json.JSONDecodeError):
|
|
81
|
+
return {}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def rate(pricing: dict, model: str) -> dict | None:
|
|
85
|
+
"""Resolve one model id to its `{"input": .., "output": ..}` rate,
|
|
86
|
+
following a pricing table's `aliases` indirection when the model id has
|
|
87
|
+
no direct entry."""
|
|
88
|
+
models = pricing.get("models", {})
|
|
89
|
+
if model in models:
|
|
90
|
+
return models[model]
|
|
91
|
+
alias = pricing.get("aliases", {}).get(model)
|
|
92
|
+
if alias and alias in models:
|
|
93
|
+
return models[alias]
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def cost(usage: dict, rate_entry: dict | None, pricing: dict) -> float:
|
|
98
|
+
"""Dollar cost of one usage block at `rate_entry`. `0.0` when no rate is
|
|
99
|
+
known for the model — an unpriced model contributes $0, never raises."""
|
|
100
|
+
if not rate_entry:
|
|
101
|
+
return 0.0
|
|
102
|
+
input_tokens = _usage_field(usage, "input_tokens")
|
|
103
|
+
output_tokens = _usage_field(usage, "output_tokens")
|
|
104
|
+
cache_write_tokens = _usage_field(usage, "cache_creation_input_tokens")
|
|
105
|
+
cache_read_tokens = _usage_field(usage, "cache_read_input_tokens")
|
|
106
|
+
in_rate = rate_entry.get("input", 0)
|
|
107
|
+
return (
|
|
108
|
+
input_tokens / 1e6 * in_rate
|
|
109
|
+
+ output_tokens / 1e6 * rate_entry.get("output", 0)
|
|
110
|
+
+ cache_write_tokens
|
|
111
|
+
/ 1e6
|
|
112
|
+
* in_rate
|
|
113
|
+
* pricing.get("cache_write_multiplier", DEFAULT_CACHE_WRITE_MULTIPLIER)
|
|
114
|
+
+ cache_read_tokens
|
|
115
|
+
/ 1e6
|
|
116
|
+
* in_rate
|
|
117
|
+
* pricing.get("cache_read_multiplier", DEFAULT_CACHE_READ_MULTIPLIER)
|
|
118
|
+
)
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render a Markdown report to a styled PDF.
|
|
3
|
+
|
|
4
|
+
Backs the ``/report-pdf`` command and the ``--pdf`` flag on report-producing
|
|
5
|
+
skills. Stdlib only, (ADR 0014 / 0015). Detects a PDF engine via.
|
|
6
|
+
runtime probes and converts Markdown -> PDF, **never raising for a missing
|
|
7
|
+
engine** — callers get a structured :class:`RenderResult` and the underlying
|
|
8
|
+
Markdown report is left untouched.
|
|
9
|
+
|
|
10
|
+
Two pipelines sit behind one abstraction:
|
|
11
|
+
|
|
12
|
+
- **pandoc-mediated** — Markdown -> self-contained HTML (with the bundled
|
|
13
|
+
``report-print.css``) via pandoc, then HTML -> PDF via headless
|
|
14
|
+
Chrome/Chromium, weasyprint, or wkhtmltopdf.
|
|
15
|
+
- **direct** — ``md-to-pdf`` consumes Markdown directly (no pandoc).
|
|
16
|
+
|
|
17
|
+
Fallback order: Chrome/Chromium -> weasyprint -> wkhtmltopdf -> md-to-pdf. The
|
|
18
|
+
first three require pandoc; md-to-pdf does not.
|
|
19
|
+
|
|
20
|
+
State is read off a single :class:`RenderResult`:
|
|
21
|
+
|
|
22
|
+
- success -> ``rendered=True``, ``engine``/``out_path`` set, reason fields None
|
|
23
|
+
- skip -> ``rendered=False``, ``reason``+``install_hint`` set, ``error`` None
|
|
24
|
+
- failure -> ``rendered=False``, ``error`` set, ``reason``/``install_hint`` None
|
|
25
|
+
|
|
26
|
+
CLI contract (``report_pdf.py <md> [--out <path>]``):
|
|
27
|
+
|
|
28
|
+
stdout : a ``Rendering …`` progress line, then one result line
|
|
29
|
+
exit 0 : rendered, or skipped (no engine) — the report is always safe
|
|
30
|
+
exit 1 : source file not found, or an engine was present but failed
|
|
31
|
+
exit 2 : usage error (no path given)
|
|
32
|
+
"""
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import argparse
|
|
36
|
+
import os
|
|
37
|
+
import platform
|
|
38
|
+
import shutil
|
|
39
|
+
import subprocess
|
|
40
|
+
import sys
|
|
41
|
+
import tempfile
|
|
42
|
+
from collections.abc import Callable
|
|
43
|
+
from dataclasses import dataclass
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
# Detection order. ``kind`` is the pipeline: "pandoc" engines consume HTML that
|
|
47
|
+
# pandoc produces; "direct" engines consume Markdown themselves.
|
|
48
|
+
_PANDOC = "pandoc"
|
|
49
|
+
_DIRECT = "direct"
|
|
50
|
+
|
|
51
|
+
# Upper bound (seconds) on any single engine/pandoc invocation. A wedged
|
|
52
|
+
# headless Chrome must surface as an engine failure, never an indefinite hang.
|
|
53
|
+
_TIMEOUT_SECONDS = 120
|
|
54
|
+
|
|
55
|
+
# PATH names probed for a Chrome/Chromium binary, in preference order.
|
|
56
|
+
_CHROME_NAMES = (
|
|
57
|
+
"google-chrome",
|
|
58
|
+
"google-chrome-stable",
|
|
59
|
+
"chromium",
|
|
60
|
+
"chromium-browser",
|
|
61
|
+
"chrome",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class Engine:
|
|
67
|
+
"""A resolved PDF engine: its ``name``, pipeline ``kind``, and (for Chrome)
|
|
68
|
+
the concrete ``binary`` path a PATH/app-path probe found."""
|
|
69
|
+
|
|
70
|
+
name: str
|
|
71
|
+
kind: str
|
|
72
|
+
binary: str | None = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class RenderResult:
|
|
77
|
+
"""Outcome of a render attempt — the single contract both surfaces key off.
|
|
78
|
+
|
|
79
|
+
See the module docstring for the field discriminant across
|
|
80
|
+
success / skip / failure.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
rendered: bool
|
|
84
|
+
engine: str | None = None
|
|
85
|
+
out_path: str | None = None
|
|
86
|
+
reason: str | None = None
|
|
87
|
+
install_hint: str | None = None
|
|
88
|
+
error: str | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _chrome_binary() -> str | None:
|
|
92
|
+
"""Locate a Chrome/Chromium binary via runtime probes only.
|
|
93
|
+
|
|
94
|
+
PATH names first (cross-OS), then OS-specific application paths chosen by
|
|
95
|
+
``platform.system()`` so the probe stays a runtime decision (mockable in
|
|
96
|
+
tests) rather than a hard-coded constant.
|
|
97
|
+
"""
|
|
98
|
+
for name in _CHROME_NAMES:
|
|
99
|
+
found = shutil.which(name)
|
|
100
|
+
if found:
|
|
101
|
+
return found
|
|
102
|
+
system = platform.system()
|
|
103
|
+
candidates: list[str] = []
|
|
104
|
+
if system == "Darwin":
|
|
105
|
+
candidates = [
|
|
106
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
107
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
108
|
+
]
|
|
109
|
+
elif system == "Windows":
|
|
110
|
+
for env in ("PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA"):
|
|
111
|
+
base = os.environ.get(env)
|
|
112
|
+
if base:
|
|
113
|
+
candidates.append(
|
|
114
|
+
os.path.join(base, "Google", "Chrome", "Application", "chrome.exe")
|
|
115
|
+
)
|
|
116
|
+
for candidate in candidates:
|
|
117
|
+
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
|
|
118
|
+
return candidate
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def detect_engine() -> Engine | None:
|
|
123
|
+
"""Return the first available engine in fallback order, or ``None``.
|
|
124
|
+
|
|
125
|
+
Chrome/weasyprint/wkhtmltopdf are only offered when pandoc is present (they
|
|
126
|
+
consume pandoc's HTML). md-to-pdf is offered whether or not pandoc exists —
|
|
127
|
+
it must never be gated behind pandoc.
|
|
128
|
+
"""
|
|
129
|
+
if shutil.which("pandoc"):
|
|
130
|
+
chrome = _chrome_binary()
|
|
131
|
+
if chrome:
|
|
132
|
+
return Engine("chrome", _PANDOC, binary=chrome)
|
|
133
|
+
if shutil.which("weasyprint"):
|
|
134
|
+
return Engine("weasyprint", _PANDOC)
|
|
135
|
+
if shutil.which("wkhtmltopdf"):
|
|
136
|
+
return Engine("wkhtmltopdf", _PANDOC)
|
|
137
|
+
if shutil.which("md-to-pdf"):
|
|
138
|
+
return Engine("md-to-pdf", _DIRECT)
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def install_hint() -> str:
|
|
143
|
+
"""A single, OS-appropriate install suggestion — one actionable path, not a
|
|
144
|
+
menu of every engine (the report is already saved; the PDF is the extra)."""
|
|
145
|
+
system = platform.system()
|
|
146
|
+
if system == "Darwin":
|
|
147
|
+
cmd = "brew install pandoc && brew install --cask google-chrome"
|
|
148
|
+
elif system == "Windows":
|
|
149
|
+
cmd = "install pandoc + Google Chrome, or `npm i -g md-to-pdf`"
|
|
150
|
+
else:
|
|
151
|
+
cmd = "apt-get install pandoc chromium (or `pip install weasyprint`)"
|
|
152
|
+
return (
|
|
153
|
+
f"No PDF engine found. Install one, e.g.: {cmd}. "
|
|
154
|
+
"Markdown report saved; PDF skipped."
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def default_css_path() -> Path:
|
|
159
|
+
"""The bundled print stylesheet shipped with the plugin."""
|
|
160
|
+
return Path(__file__).resolve().parents[2] / "knowledge" / "report-print.css"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _pandoc_html_cmd(src: Path, html: Path) -> list[str]:
|
|
164
|
+
# --sandbox blocks pandoc from fetching any external resource during
|
|
165
|
+
# conversion, so a report embedding an attacker-influenced remote URL
|
|
166
|
+
# (report content can quote code from a reviewed repo) cannot drive an
|
|
167
|
+
# outbound request at build time (SSRF). The stylesheet is injected in
|
|
168
|
+
# Python afterwards (_inject_css) — sandbox would otherwise block reading
|
|
169
|
+
# even an explicitly-passed --css file.
|
|
170
|
+
return [
|
|
171
|
+
"pandoc",
|
|
172
|
+
str(src),
|
|
173
|
+
"-f",
|
|
174
|
+
"gfm",
|
|
175
|
+
"-t",
|
|
176
|
+
"html5",
|
|
177
|
+
"--standalone",
|
|
178
|
+
"--sandbox",
|
|
179
|
+
"--metadata",
|
|
180
|
+
f"title={src.stem}",
|
|
181
|
+
"-o",
|
|
182
|
+
str(html),
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _inject_css(html: Path, css: Path | None) -> None:
|
|
187
|
+
"""Inline the bundled stylesheet into the pandoc HTML head.
|
|
188
|
+
|
|
189
|
+
Done in Python (not via pandoc ``--css``) because ``--sandbox`` blocks
|
|
190
|
+
pandoc from reading the CSS file. No-op when no stylesheet is available.
|
|
191
|
+
"""
|
|
192
|
+
if not (css and css.is_file()):
|
|
193
|
+
return
|
|
194
|
+
try:
|
|
195
|
+
css_text = css.read_text(encoding="utf-8")
|
|
196
|
+
except (OSError, UnicodeDecodeError):
|
|
197
|
+
# An unreadable/malformed stylesheet degrades to unstyled output —
|
|
198
|
+
# never raises, matching the module's graceful-degradation contract.
|
|
199
|
+
return
|
|
200
|
+
style = f"<style>\n{css_text}\n</style>"
|
|
201
|
+
doc = html.read_text(encoding="utf-8")
|
|
202
|
+
if "</head>" in doc:
|
|
203
|
+
doc = doc.replace("</head>", style + "\n</head>", 1)
|
|
204
|
+
else:
|
|
205
|
+
doc = style + "\n" + doc
|
|
206
|
+
html.write_text(doc, encoding="utf-8")
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _engine_pdf_cmd(engine: Engine, html: Path, out: Path) -> list[str]:
|
|
210
|
+
# Absolute output paths so a value beginning with '-' can never be parsed
|
|
211
|
+
# as an option by a positional-arg engine (weasyprint/wkhtmltopdf).
|
|
212
|
+
out_abs = str(out.resolve())
|
|
213
|
+
if engine.name == "chrome":
|
|
214
|
+
return [
|
|
215
|
+
engine.binary or "google-chrome",
|
|
216
|
+
"--headless=new",
|
|
217
|
+
"--disable-gpu",
|
|
218
|
+
# No JavaScript: a Markdown-derived report never needs it, and it
|
|
219
|
+
# closes the headless-Chrome JS-execution vector on report content.
|
|
220
|
+
"--disable-javascript",
|
|
221
|
+
"--no-pdf-header-footer",
|
|
222
|
+
"--run-all-compositor-stages-before-draw",
|
|
223
|
+
"--virtual-time-budget=10000",
|
|
224
|
+
f"--print-to-pdf={out_abs}",
|
|
225
|
+
html.resolve().as_uri(),
|
|
226
|
+
]
|
|
227
|
+
if engine.name == "weasyprint":
|
|
228
|
+
return ["weasyprint", str(html.resolve()), out_abs]
|
|
229
|
+
if engine.name == "wkhtmltopdf":
|
|
230
|
+
return ["wkhtmltopdf", str(html.resolve()), out_abs]
|
|
231
|
+
raise ValueError(f"not a pandoc-mediated engine: {engine.name}")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _mdtopdf_cmd(src: Path, css: Path | None) -> list[str]:
|
|
235
|
+
cmd = ["md-to-pdf", str(src)]
|
|
236
|
+
if css and css.is_file():
|
|
237
|
+
cmd += ["--stylesheet", str(css)]
|
|
238
|
+
return cmd
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _run(runner: Callable, cmd: list[str]) -> subprocess.CompletedProcess:
|
|
242
|
+
return runner(cmd, capture_output=True, text=True, timeout=_TIMEOUT_SECONDS)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _fail(engine_name: str, msg: str) -> RenderResult:
|
|
246
|
+
return RenderResult(rendered=False, engine=engine_name, error=msg)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _step_error(label: str, proc: subprocess.CompletedProcess) -> str:
|
|
250
|
+
return f"{label} failed: {(proc.stderr or '').strip()}"
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _render_pandoc(
|
|
254
|
+
engine: Engine, src: Path, out: Path, css: Path | None, runner: Callable
|
|
255
|
+
) -> str | None:
|
|
256
|
+
"""pandoc-mediated pipeline (Markdown → HTML+CSS → PDF). Returns an error
|
|
257
|
+
string on failure, or None on success. Always unlinks the temp HTML."""
|
|
258
|
+
with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as handle:
|
|
259
|
+
html = Path(handle.name)
|
|
260
|
+
try:
|
|
261
|
+
step1 = _run(runner, _pandoc_html_cmd(src, html))
|
|
262
|
+
if step1.returncode != 0:
|
|
263
|
+
return _step_error("pandoc", step1)
|
|
264
|
+
_inject_css(html, css)
|
|
265
|
+
step2 = _run(runner, _engine_pdf_cmd(engine, html, out))
|
|
266
|
+
if step2.returncode != 0:
|
|
267
|
+
return _step_error(engine.name, step2)
|
|
268
|
+
return None
|
|
269
|
+
finally:
|
|
270
|
+
html.unlink(missing_ok=True)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _render_direct(
|
|
274
|
+
engine: Engine, src: Path, out: Path, css: Path | None, runner: Callable
|
|
275
|
+
) -> str | None:
|
|
276
|
+
"""Direct md-to-pdf pipeline (no pandoc). Returns an error string on
|
|
277
|
+
failure, or None on success."""
|
|
278
|
+
produced = src.with_suffix(".pdf")
|
|
279
|
+
step = _run(runner, _mdtopdf_cmd(src, css))
|
|
280
|
+
if step.returncode != 0:
|
|
281
|
+
return _step_error(engine.name, step)
|
|
282
|
+
if produced.resolve() != out.resolve() and produced.is_file():
|
|
283
|
+
shutil.move(str(produced), str(out))
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def render(
|
|
288
|
+
md_path: str,
|
|
289
|
+
out_path: str | None = None,
|
|
290
|
+
*,
|
|
291
|
+
engine: Engine | None = None,
|
|
292
|
+
css_path: str | None = None,
|
|
293
|
+
runner: Callable | None = None,
|
|
294
|
+
) -> RenderResult:
|
|
295
|
+
"""Render ``md_path`` to a PDF, returning a :class:`RenderResult`.
|
|
296
|
+
|
|
297
|
+
``runner`` is injectable so tests never shell out to a real engine; it
|
|
298
|
+
defaults to the module's current ``subprocess.run`` (resolved at call time,
|
|
299
|
+
so a monkeypatch of it is honored).
|
|
300
|
+
"""
|
|
301
|
+
if runner is None:
|
|
302
|
+
runner = subprocess.run
|
|
303
|
+
src = Path(md_path)
|
|
304
|
+
if not src.is_file():
|
|
305
|
+
return RenderResult(rendered=False, error=f"file not found: {md_path}")
|
|
306
|
+
|
|
307
|
+
if engine is None:
|
|
308
|
+
engine = detect_engine()
|
|
309
|
+
if engine is None:
|
|
310
|
+
return RenderResult(
|
|
311
|
+
rendered=False, reason="no PDF engine available", install_hint=install_hint()
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
out = Path(out_path) if out_path else src.with_suffix(".pdf")
|
|
315
|
+
try:
|
|
316
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
317
|
+
except OSError as exc:
|
|
318
|
+
return RenderResult(rendered=False, engine=engine.name, error=str(exc))
|
|
319
|
+
|
|
320
|
+
css = Path(css_path) if css_path else default_css_path()
|
|
321
|
+
|
|
322
|
+
try:
|
|
323
|
+
pipeline = _render_pandoc if engine.kind == _PANDOC else _render_direct
|
|
324
|
+
err = pipeline(engine, src, out, css, runner)
|
|
325
|
+
if err is not None:
|
|
326
|
+
return _fail(engine.name, err)
|
|
327
|
+
except subprocess.TimeoutExpired:
|
|
328
|
+
return _fail(engine.name, f"{engine.name} timed out after {_TIMEOUT_SECONDS}s")
|
|
329
|
+
except OSError as exc:
|
|
330
|
+
return _fail(engine.name, str(exc))
|
|
331
|
+
|
|
332
|
+
if not out.is_file():
|
|
333
|
+
return _fail(engine.name, f"{engine.name} reported success but no PDF was produced")
|
|
334
|
+
return RenderResult(rendered=True, engine=engine.name, out_path=str(out))
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
338
|
+
parser = argparse.ArgumentParser(
|
|
339
|
+
prog="report_pdf.py",
|
|
340
|
+
description="Render a Markdown report to a styled PDF.",
|
|
341
|
+
)
|
|
342
|
+
parser.add_argument("path", help="path to the Markdown report (.md)")
|
|
343
|
+
parser.add_argument(
|
|
344
|
+
"--out", dest="out", default=None, help="output PDF path (default: next to source)"
|
|
345
|
+
)
|
|
346
|
+
return parser
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def main(argv: list[str] | None = None) -> int:
|
|
350
|
+
parser = _build_parser()
|
|
351
|
+
args = parser.parse_args(argv)
|
|
352
|
+
|
|
353
|
+
src = Path(args.path)
|
|
354
|
+
engine = detect_engine() if src.is_file() else None
|
|
355
|
+
if engine is not None:
|
|
356
|
+
print(f"Rendering PDF via {engine.name}…")
|
|
357
|
+
result = render(args.path, args.out, engine=engine)
|
|
358
|
+
|
|
359
|
+
if result.rendered:
|
|
360
|
+
print(f"PDF written: {result.out_path} (engine: {result.engine})")
|
|
361
|
+
return 0
|
|
362
|
+
if result.error is not None:
|
|
363
|
+
print(f"PDF not rendered: {result.error}", file=sys.stderr)
|
|
364
|
+
return 1
|
|
365
|
+
# skip (no engine) — non-fatal, the report is safe
|
|
366
|
+
print(result.install_hint or result.reason or "PDF skipped.")
|
|
367
|
+
return 0
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
if __name__ == "__main__":
|
|
371
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""hooks/lib/review_agent_registry.py — shared *-review agent glob discovery (#1461).
|
|
2
|
+
|
|
3
|
+
Both `scripts/check_review_agent_mcp_tools.py` (MCP tool-grant auditing) and
|
|
4
|
+
`hooks/agent_dispatch_ledger.py` (the dispatch-ledger PreToolUse hook, #1461)
|
|
5
|
+
need the same closed set of registered review-agent names: every
|
|
6
|
+
`agents/*-review.md` file's stem. This module is the single extraction point
|
|
7
|
+
so a hook never reaches into `scripts/` — the correct dependency direction is
|
|
8
|
+
`scripts/` -> `hooks/lib/`, never the reverse (a hook must be import-safe
|
|
9
|
+
without any `scripts/` module on its path).
|
|
10
|
+
|
|
11
|
+
Stdlib only. See ADR 0014 / ADR 0015.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
# This plugin's own marketplace-qualified prefix. A real Agent-tool dispatch
|
|
19
|
+
# of one of this plugin's registered review agents, once installed, is named
|
|
20
|
+
# "dev-team:<agent-name>" (e.g. "dev-team:doc-review") — the closed set below
|
|
21
|
+
# is built from bare `agents/*-review.md` file stems, so an unstripped
|
|
22
|
+
# qualified name never matches it (#1461 follow-up: this silently dropped
|
|
23
|
+
# every dispatch's ledger record for the plugin's normal, installed
|
|
24
|
+
# invocation form). A prefix for a DIFFERENT plugin is left untouched — it
|
|
25
|
+
# is never one of this registry's own agents.
|
|
26
|
+
_PLUGIN_PREFIX = "dev-team:"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def strip_plugin_prefix(subagent_type: str) -> str:
|
|
30
|
+
"""Normalize a dispatch's `subagent_type` to the bare agent name this
|
|
31
|
+
registry's closed set uses, stripping only this plugin's own
|
|
32
|
+
`dev-team:` qualifier."""
|
|
33
|
+
if subagent_type.startswith(_PLUGIN_PREFIX):
|
|
34
|
+
return subagent_type[len(_PLUGIN_PREFIX) :]
|
|
35
|
+
return subagent_type
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def find_review_agent_files(agents_dir: Path) -> list[Path]:
|
|
39
|
+
"""Return the read-only review agent files (agents/*-review.md), sorted.
|
|
40
|
+
|
|
41
|
+
Glob-discovery logic extracted verbatim from
|
|
42
|
+
`scripts/check_review_agent_mcp_tools.py::find_review_agents` (#1461) —
|
|
43
|
+
behavior-identical, so existing callers/tests of that function regress
|
|
44
|
+
against this implementation unchanged.
|
|
45
|
+
"""
|
|
46
|
+
return sorted(Path(agents_dir).glob("*-review.md"))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def registered_review_agent_names(agents_dir: Path) -> frozenset[str]:
|
|
50
|
+
"""Return the closed set of registered review-agent names (file stems).
|
|
51
|
+
|
|
52
|
+
This is the authoritative closed vocabulary `hooks/agent_dispatch_ledger.py`
|
|
53
|
+
checks an Agent-tool dispatch's `subagent_type` against before recording a
|
|
54
|
+
ledger event — a name outside this set is never a real review agent, and
|
|
55
|
+
is never written to the ledger, not even as a rejected/flagged entry.
|
|
56
|
+
"""
|
|
57
|
+
return frozenset(path.stem for path in find_review_agent_files(agents_dir))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def default_agents_dir() -> Path:
|
|
61
|
+
"""The plugin's own `agents/` directory (#1904 item 3) — the single
|
|
62
|
+
source of truth every review-agent registry consumer resolves against,
|
|
63
|
+
never the caller's own project `cwd`.
|
|
64
|
+
|
|
65
|
+
Always `.resolve()`d: several call sites that once built this path by
|
|
66
|
+
hand (`scripts/check_agent_tool_mapping.py`,
|
|
67
|
+
`scripts/check_review_agent_mcp_tools.py`,
|
|
68
|
+
`skills/code-review/scripts/repo_invariants.py`,
|
|
69
|
+
`scripts/check_agent_scope.py`) previously did so without `.resolve()` —
|
|
70
|
+
a latent behavioral difference under a symlinked checkout — now fixed by
|
|
71
|
+
construction, since each delegates here instead of re-deriving its own
|
|
72
|
+
copy. Not yet exhaustive: `scripts/verify_tier.py` still derives its own
|
|
73
|
+
`_AGENTS_DIR` independently (its `_PLUGIN_ROOT` is already resolved via
|
|
74
|
+
`Path(__file__).resolve()`, so it does not carry the same symlink bug,
|
|
75
|
+
but it remains a duplicate resolution path this module has not yet
|
|
76
|
+
absorbed).
|
|
77
|
+
"""
|
|
78
|
+
# hooks/lib/review_agent_registry.py -> hooks/lib -> hooks -> plugin root
|
|
79
|
+
# -> agents
|
|
80
|
+
return Path(__file__).resolve().parents[2] / "agents"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_registered_review_lens(subagent_type: str) -> bool:
|
|
84
|
+
"""True when `subagent_type` (raw, or plugin-qualified like
|
|
85
|
+
`dev-team:security-review`) names a registered `agents/*-review.md`
|
|
86
|
+
review lens.
|
|
87
|
+
|
|
88
|
+
Encapsulates the repeated three-step check — `strip_plugin_prefix` ->
|
|
89
|
+
read the registered review-agent set via `default_agents_dir()` ->
|
|
90
|
+
membership test — previously hand-rolled independently in
|
|
91
|
+
`hooks/agent_dispatch_ledger.py`, `hooks/lib/boundary_events.py`, and
|
|
92
|
+
`hooks/review_verdict_recorder.py` (backstop review finding, #2166 +
|
|
93
|
+
#2171 — this repo's own `hooks/lib/review_dispatch_ledger.py` module
|
|
94
|
+
docstring already applied the identical consolidation lesson to a
|
|
95
|
+
sibling predicate).
|
|
96
|
+
|
|
97
|
+
An unreadable registry (`read_registered_review_agent_names()` returns
|
|
98
|
+
`None`) collapses to `False`, matching every prior call site's own
|
|
99
|
+
"don't record"/"skip" posture exactly: a lost registry read only
|
|
100
|
+
narrows what counts as a registered review lens, never widens it.
|
|
101
|
+
"""
|
|
102
|
+
if not subagent_type:
|
|
103
|
+
return False
|
|
104
|
+
name = strip_plugin_prefix(subagent_type)
|
|
105
|
+
registered = read_registered_review_agent_names(default_agents_dir())
|
|
106
|
+
return bool(registered) and name in registered
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def read_registered_review_agent_names(agents_dir: Path) -> frozenset[str] | None:
|
|
110
|
+
"""Checked variant of `registered_review_agent_names()` that owns the
|
|
111
|
+
read-failure-vs-empty distinction (#1904 item 1) instead of forcing every
|
|
112
|
+
consumer to reinvent it.
|
|
113
|
+
|
|
114
|
+
Returns `None` on a read failure (a missing or unreadable `agents_dir`);
|
|
115
|
+
a genuine `frozenset()` on a successful read that legitimately finds zero
|
|
116
|
+
`*-review.md` files. The two states require OPPOSITE treatment depending
|
|
117
|
+
on which side of a corroboration check consumes them: for POSITIVE
|
|
118
|
+
evidence (e.g. "which agents dispatched"), a caller may safely collapse
|
|
119
|
+
`None` to an empty set — narrowing corroboration can only ever narrow,
|
|
120
|
+
never widen, what counts as a passing gate. For NEGATIVE evidence (e.g.
|
|
121
|
+
"which agents have an unsuperseded dispatch failure"), collapsing `None`
|
|
122
|
+
to empty would WIDEN the result instead — every genuine negative-evidence
|
|
123
|
+
entry would be filtered out by an empty "registered" set, producing an
|
|
124
|
+
all-clear indistinguishable from "provably none". See
|
|
125
|
+
`hooks/lib/review_gate_corroboration.py`'s own docstring for the full
|
|
126
|
+
account of this asymmetry.
|
|
127
|
+
|
|
128
|
+
Two failure modes are checked EXPLICITLY here, not left to an exception:
|
|
129
|
+
`registered_review_agent_names()` globs `agents_dir.glob("*-review.md")`,
|
|
130
|
+
and `Path.glob()` does not raise for either — it silently yields nothing
|
|
131
|
+
for a missing directory and swallows a permission error. Without the
|
|
132
|
+
explicit `is_dir()` check, a missing/unreadable `agents_dir` would
|
|
133
|
+
produce an empty `frozenset()`, not `None`, defeating the very
|
|
134
|
+
distinction this function's contract promises.
|
|
135
|
+
"""
|
|
136
|
+
path = Path(agents_dir)
|
|
137
|
+
if not path.is_dir():
|
|
138
|
+
return None
|
|
139
|
+
try:
|
|
140
|
+
return registered_review_agent_names(path)
|
|
141
|
+
except Exception: # noqa: BLE001 - caller decides how to fail closed for its own evidence direction
|
|
142
|
+
return None
|