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,397 @@
|
|
|
1
|
+
"""autoship_state — shared round-state helpers for /dev-team:autoship (#989).
|
|
2
|
+
|
|
3
|
+
Shared by `autoship_discover.py`, `autoship_group.py`, and
|
|
4
|
+
`autoship_reclaim.py` so none has to inspect another's code to reuse common
|
|
5
|
+
logic: the `autoship:*` label constants scripts filter on, the
|
|
6
|
+
`--input-file`/`--now-override` CLI seam, the positive-value CLI validators,
|
|
7
|
+
and (as of the Slice 1 grouping work) the shared `gh issue list` fetch +
|
|
8
|
+
eligibility-filter pipeline (`fetch_eligible_issues`) that both
|
|
9
|
+
`autoship_discover.py` and `autoship_group.py` build on.
|
|
10
|
+
`is_stale`/`format_round_timestamp` are consumed only by
|
|
11
|
+
`autoship_reclaim.py`'s staleness check — discovery/grouping have no
|
|
12
|
+
staleness concept of their own.
|
|
13
|
+
|
|
14
|
+
Stdlib-only. See docs/python-hook-contract.md.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
import json
|
|
21
|
+
import subprocess
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
READY_LABEL = "autoship:ready"
|
|
26
|
+
IN_PROGRESS_LABEL = "autoship:in-progress"
|
|
27
|
+
BLOCKED_LABEL = "autoship:blocked"
|
|
28
|
+
|
|
29
|
+
# The label a human confirmation applies to an agent-proposed batch (Step 2c,
|
|
30
|
+
# `skills/autoship/SKILL.md`) — recognized by `autoship_group.py`'s
|
|
31
|
+
# `has_batch_confirmed_override` signal function, and stripped by
|
|
32
|
+
# `autoship_reclaim.py`'s reclaim relabel. Lives here, alongside the other
|
|
33
|
+
# three `autoship:*` label constants, so both scripts import one definition
|
|
34
|
+
# instead of each redefining it independently.
|
|
35
|
+
BATCH_CONFIRMED_LABEL = "autoship:batch-confirmed"
|
|
36
|
+
|
|
37
|
+
# `gh issue list` applies a default result cap (typically 30) — every caller
|
|
38
|
+
# that needs the FULL matching pool (discovery/grouping's eligible pool,
|
|
39
|
+
# reclaim's in-progress pool) passes this explicit override instead of each
|
|
40
|
+
# hardcoding its own "500" literal.
|
|
41
|
+
GH_LIST_LIMIT = "500"
|
|
42
|
+
|
|
43
|
+
# Same fields `gh issue list --json` returns for these names — no schema
|
|
44
|
+
# invention. `title` is required because `autoship_discover.py`'s stdout
|
|
45
|
+
# contract emits it; `state` is required because `is_eligible` checks it
|
|
46
|
+
# directly. Callers needing richer data (e.g. `autoship_group.py`'s
|
|
47
|
+
# dependency/parent signals) pass their own, larger `required_fields` tuple
|
|
48
|
+
# to `fetch_eligible_issues`/`validate_issues`/`load_issues_from_file`.
|
|
49
|
+
BASE_REQUIRED_FIELDS = (
|
|
50
|
+
"number",
|
|
51
|
+
"title",
|
|
52
|
+
"state",
|
|
53
|
+
"createdAt",
|
|
54
|
+
"labels",
|
|
55
|
+
"closedByPullRequestsReferences",
|
|
56
|
+
"subIssuesSummary",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# gh subprocess calls get a hard timeout so a hung/stalled network call never
|
|
60
|
+
# blocks a caller script indefinitely.
|
|
61
|
+
_GH_TIMEOUT_SECONDS = 30
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class FetchError(Exception):
|
|
65
|
+
"""A discovery/grouping-input or `gh` fetch problem that must surface as
|
|
66
|
+
a clear CLI error message, not an uncaught exception/traceback."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def format_round_timestamp(dt: datetime) -> str:
|
|
70
|
+
"""Format `dt` as an ISO-8601 UTC string ending in "Z".
|
|
71
|
+
|
|
72
|
+
Matches the timestamp shape already used in `metrics/config-changelog.jsonl`.
|
|
73
|
+
"""
|
|
74
|
+
return dt.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def is_stale(labeled_at: datetime, stale_after_hours: float, now: datetime) -> bool:
|
|
78
|
+
"""True when `labeled_at` is at or beyond `stale_after_hours` before `now`.
|
|
79
|
+
|
|
80
|
+
Inclusive at the boundary: exactly `stale_after_hours` counts as stale —
|
|
81
|
+
"past" in the issue's own language means "at or beyond".
|
|
82
|
+
"""
|
|
83
|
+
elapsed_hours = (now - labeled_at).total_seconds() / 3600
|
|
84
|
+
return elapsed_hours >= stale_after_hours
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _iso8601(raw: str) -> datetime:
|
|
88
|
+
"""`argparse` `type=` validator for an ISO-8601 timestamp string."""
|
|
89
|
+
# Deliberately naive: `is_stale`'s `now` (autoship_reclaim.py) strips
|
|
90
|
+
# tzinfo from its UTC clock read to match this, so both sides of the
|
|
91
|
+
# subtraction stay naive-UTC. Making this tz-aware would break that pairing.
|
|
92
|
+
return datetime.strptime(raw, "%Y-%m-%dT%H:%M:%SZ") # noqa: DTZ007
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def positive_float_validator(flag_name: str):
|
|
96
|
+
"""Return an `argparse` `type=` validator that rejects `<= 0` for `flag_name`.
|
|
97
|
+
|
|
98
|
+
Shared by `autoship_discover.py`'s `--max-cost-usd` and
|
|
99
|
+
`autoship_reclaim.py`'s `--stale-after-hours` so both fail loud at the
|
|
100
|
+
CLI boundary the same way, per `evals/code-review-benchmark/cli.py`'s
|
|
101
|
+
`_positive_float` precedent — one implementation instead of two
|
|
102
|
+
independently-copied ones.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def _validator(raw: str) -> float:
|
|
106
|
+
value = float(raw)
|
|
107
|
+
if value <= 0:
|
|
108
|
+
raise argparse.ArgumentTypeError(
|
|
109
|
+
f"{flag_name} must be a positive number, got {raw!r}"
|
|
110
|
+
)
|
|
111
|
+
return value
|
|
112
|
+
|
|
113
|
+
return _validator
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def positive_int_validator(flag_name: str):
|
|
117
|
+
"""Return an `argparse` `type=` validator that rejects `<= 0` for `flag_name`.
|
|
118
|
+
|
|
119
|
+
Same shape as `positive_float_validator`, for integer-valued flags —
|
|
120
|
+
shared by `autoship_discover.py`'s `--max-issues` so the CLI boundary
|
|
121
|
+
fails loud the same way `--max-cost-usd` does.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def _validator(raw: str) -> int:
|
|
125
|
+
value = int(raw)
|
|
126
|
+
if value <= 0:
|
|
127
|
+
raise argparse.ArgumentTypeError(
|
|
128
|
+
f"{flag_name} must be a positive integer, got {raw!r}"
|
|
129
|
+
)
|
|
130
|
+
return value
|
|
131
|
+
|
|
132
|
+
return _validator
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _label_names(issue: dict[str, Any]) -> list[str]:
|
|
136
|
+
return [label["name"] for label in issue["labels"]]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _is_epic(issue: dict[str, Any]) -> bool:
|
|
140
|
+
return issue["subIssuesSummary"].get("total", 0) > 0
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _has_open_linked_pr(issue: dict[str, Any]) -> bool:
|
|
144
|
+
"""True when any entry in `closedByPullRequestsReferences` is itself
|
|
145
|
+
still open — a single open PR anywhere in the list excludes the issue,
|
|
146
|
+
even when other entries in the same list are closed/merged."""
|
|
147
|
+
return any(
|
|
148
|
+
pr.get("state") == "OPEN" for pr in issue["closedByPullRequestsReferences"]
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def is_eligible(issue: dict[str, Any], label: str) -> bool:
|
|
153
|
+
"""True when `issue` qualifies for autoship dispatch: open, carries
|
|
154
|
+
`label`, is not in-progress/blocked, is not an epic, and has no open
|
|
155
|
+
linked pull request."""
|
|
156
|
+
if issue["state"] != "OPEN":
|
|
157
|
+
return False
|
|
158
|
+
labels = _label_names(issue)
|
|
159
|
+
if label not in labels:
|
|
160
|
+
return False
|
|
161
|
+
if IN_PROGRESS_LABEL in labels or BLOCKED_LABEL in labels:
|
|
162
|
+
return False
|
|
163
|
+
if _is_epic(issue):
|
|
164
|
+
return False
|
|
165
|
+
return not _has_open_linked_pr(issue)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _validate_shape(issue: dict[str, Any], identifier: str) -> None:
|
|
169
|
+
"""Validate the structural shape of `labels`, `subIssuesSummary`, and
|
|
170
|
+
`closedByPullRequestsReferences` when present on `issue`.
|
|
171
|
+
|
|
172
|
+
`is_eligible` reads these three fields structurally, not just by key
|
|
173
|
+
presence: `_label_names` indexes every `labels` entry with `label["name"]`,
|
|
174
|
+
`_is_epic` calls `.get("total", 0)` on `subIssuesSummary`, and
|
|
175
|
+
`_has_open_linked_pr` calls `.get("state")` on every
|
|
176
|
+
`closedByPullRequestsReferences` entry. A malformed `--input-file` (e.g.
|
|
177
|
+
`labels` as bare strings instead of `{"name": ...}` objects) reached an
|
|
178
|
+
uncaught `TypeError`/`AttributeError` deep inside `is_eligible` instead of
|
|
179
|
+
a clean `FetchError` here.
|
|
180
|
+
|
|
181
|
+
Raises `FetchError` naming `identifier` and the offending field.
|
|
182
|
+
"""
|
|
183
|
+
if "labels" in issue:
|
|
184
|
+
labels = issue["labels"]
|
|
185
|
+
if not isinstance(labels, list):
|
|
186
|
+
raise FetchError(
|
|
187
|
+
f"malformed --input-file entry ({identifier}): field "
|
|
188
|
+
f'"labels" must be a JSON array, got {type(labels).__name__}'
|
|
189
|
+
)
|
|
190
|
+
for label_idx, label in enumerate(labels):
|
|
191
|
+
if not isinstance(label, dict) or not isinstance(label.get("name"), str):
|
|
192
|
+
raise FetchError(
|
|
193
|
+
f"malformed --input-file entry ({identifier}): "
|
|
194
|
+
f'"labels[{label_idx}]" must be a JSON object with a '
|
|
195
|
+
'string "name" field'
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if "subIssuesSummary" in issue:
|
|
199
|
+
summary = issue["subIssuesSummary"]
|
|
200
|
+
if not isinstance(summary, dict):
|
|
201
|
+
raise FetchError(
|
|
202
|
+
f"malformed --input-file entry ({identifier}): field "
|
|
203
|
+
f'"subIssuesSummary" must be a JSON object, got '
|
|
204
|
+
f"{type(summary).__name__}"
|
|
205
|
+
)
|
|
206
|
+
if "total" in summary and not isinstance(summary["total"], int):
|
|
207
|
+
raise FetchError(
|
|
208
|
+
f"malformed --input-file entry ({identifier}): field "
|
|
209
|
+
f'"subIssuesSummary.total" must be a number, got '
|
|
210
|
+
f"{type(summary['total']).__name__}"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if "closedByPullRequestsReferences" in issue:
|
|
214
|
+
refs = issue["closedByPullRequestsReferences"]
|
|
215
|
+
if not isinstance(refs, list):
|
|
216
|
+
raise FetchError(
|
|
217
|
+
f"malformed --input-file entry ({identifier}): field "
|
|
218
|
+
'"closedByPullRequestsReferences" must be a JSON array, got '
|
|
219
|
+
f"{type(refs).__name__}"
|
|
220
|
+
)
|
|
221
|
+
for ref_idx, ref in enumerate(refs):
|
|
222
|
+
if not isinstance(ref, dict):
|
|
223
|
+
raise FetchError(
|
|
224
|
+
f"malformed --input-file entry ({identifier}): "
|
|
225
|
+
f'"closedByPullRequestsReferences[{ref_idx}]" must be a '
|
|
226
|
+
"JSON object"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def validate_issues(
|
|
231
|
+
issues: Any, required_fields: tuple[str, ...] = BASE_REQUIRED_FIELDS
|
|
232
|
+
) -> None:
|
|
233
|
+
"""Validate `issues` is a JSON array of objects each carrying every
|
|
234
|
+
`required_fields` entry, and that `labels`/`subIssuesSummary`/
|
|
235
|
+
`closedByPullRequestsReferences` (when present) have the structural shape
|
|
236
|
+
`is_eligible` expects (see `_validate_shape`).
|
|
237
|
+
|
|
238
|
+
Raises `FetchError` naming the malformed entry (by issue number if
|
|
239
|
+
present, by index otherwise) rather than letting a `KeyError`/`TypeError`/
|
|
240
|
+
`AttributeError` propagate uncaught.
|
|
241
|
+
"""
|
|
242
|
+
if not isinstance(issues, list):
|
|
243
|
+
raise FetchError(
|
|
244
|
+
"--input-file must contain a JSON array of issue objects, got "
|
|
245
|
+
f"{type(issues).__name__}"
|
|
246
|
+
)
|
|
247
|
+
for idx, issue in enumerate(issues):
|
|
248
|
+
if not isinstance(issue, dict):
|
|
249
|
+
raise FetchError(
|
|
250
|
+
f"malformed --input-file entry at index {idx}: not a JSON object"
|
|
251
|
+
)
|
|
252
|
+
identifier = f"#{issue['number']}" if "number" in issue else f"index {idx}"
|
|
253
|
+
missing = [field for field in required_fields if field not in issue]
|
|
254
|
+
if missing:
|
|
255
|
+
raise FetchError(
|
|
256
|
+
f"malformed --input-file entry ({identifier}): missing required "
|
|
257
|
+
f"field(s) {', '.join(missing)}"
|
|
258
|
+
)
|
|
259
|
+
_validate_shape(issue, identifier)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def load_issues_from_file(
|
|
263
|
+
path: str, required_fields: tuple[str, ...] = BASE_REQUIRED_FIELDS
|
|
264
|
+
) -> list[dict[str, Any]]:
|
|
265
|
+
"""Read and validate an `--input-file` JSON array of issue objects.
|
|
266
|
+
|
|
267
|
+
Raises `FetchError` on unreadable/malformed JSON or a schema violation —
|
|
268
|
+
never an uncaught exception.
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
with open(path, encoding="utf-8") as fh:
|
|
272
|
+
issues = json.load(fh)
|
|
273
|
+
except json.JSONDecodeError as exc:
|
|
274
|
+
raise FetchError(f"--input-file {path!r} is not valid JSON: {exc}") from exc
|
|
275
|
+
except OSError as exc:
|
|
276
|
+
raise FetchError(f"--input-file {path!r} could not be read: {exc}") from exc
|
|
277
|
+
validate_issues(issues, required_fields)
|
|
278
|
+
return issues
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def fetch_issues_from_gh(
|
|
282
|
+
label: str,
|
|
283
|
+
required_fields: tuple[str, ...] = BASE_REQUIRED_FIELDS,
|
|
284
|
+
extra_fields: tuple[str, ...] = (),
|
|
285
|
+
) -> list[dict[str, Any]]:
|
|
286
|
+
"""Fetch open issues labeled `label` via a live `gh issue list` call,
|
|
287
|
+
using `gh`'s cwd-based repo auto-detection (no `--repo` flag).
|
|
288
|
+
|
|
289
|
+
`--json` requests `required_fields` plus any `extra_fields` a caller
|
|
290
|
+
needs on top of that base set (e.g. `autoship_group.py`'s dependency/
|
|
291
|
+
parent fields), deduplicated while preserving order.
|
|
292
|
+
|
|
293
|
+
The `--state open` server-side filter and the requested `state` field
|
|
294
|
+
are intentionally redundant with `is_eligible`'s own `state == "OPEN"`
|
|
295
|
+
check — this is what makes the `--input-file` and live paths behave
|
|
296
|
+
identically.
|
|
297
|
+
|
|
298
|
+
`--limit 500` is required here: `gh issue list` applies a default
|
|
299
|
+
result cap (typically 30), and this function is documented (see
|
|
300
|
+
`fetch_eligible_issues`) as returning the FULL eligible pool — without
|
|
301
|
+
an explicit override that claim is false on any repo with more than a
|
|
302
|
+
page of matching issues.
|
|
303
|
+
|
|
304
|
+
Raises `FetchError` on a non-zero `gh` exit, a timeout, or malformed
|
|
305
|
+
JSON output — never an uncaught
|
|
306
|
+
`CalledProcessError`/`TimeoutExpired`/`JSONDecodeError`/traceback.
|
|
307
|
+
"""
|
|
308
|
+
fields = list(required_fields)
|
|
309
|
+
for field in extra_fields:
|
|
310
|
+
if field not in fields:
|
|
311
|
+
fields.append(field)
|
|
312
|
+
gh_json_fields = ",".join(fields)
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
result = subprocess.run(
|
|
316
|
+
[
|
|
317
|
+
"gh",
|
|
318
|
+
"issue",
|
|
319
|
+
"list",
|
|
320
|
+
"--state",
|
|
321
|
+
"open",
|
|
322
|
+
"--label",
|
|
323
|
+
label,
|
|
324
|
+
"--limit",
|
|
325
|
+
GH_LIST_LIMIT,
|
|
326
|
+
"--json",
|
|
327
|
+
gh_json_fields,
|
|
328
|
+
],
|
|
329
|
+
capture_output=True,
|
|
330
|
+
text=True,
|
|
331
|
+
check=True,
|
|
332
|
+
timeout=_GH_TIMEOUT_SECONDS,
|
|
333
|
+
)
|
|
334
|
+
except FileNotFoundError as exc:
|
|
335
|
+
raise FetchError(f"gh CLI not found: {exc}") from exc
|
|
336
|
+
except subprocess.TimeoutExpired as exc:
|
|
337
|
+
raise FetchError(
|
|
338
|
+
f"gh issue list timed out after {_GH_TIMEOUT_SECONDS}s: {exc}"
|
|
339
|
+
) from exc
|
|
340
|
+
except subprocess.CalledProcessError as exc:
|
|
341
|
+
stderr = (exc.stderr or "").strip()
|
|
342
|
+
raise FetchError(
|
|
343
|
+
f"gh issue list failed (exit {exc.returncode}): {stderr}"
|
|
344
|
+
) from exc
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
issues = json.loads(result.stdout)
|
|
348
|
+
except json.JSONDecodeError as exc:
|
|
349
|
+
raise FetchError(f"gh issue list returned malformed JSON: {exc}") from exc
|
|
350
|
+
|
|
351
|
+
validate_issues(issues, required_fields)
|
|
352
|
+
return issues
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def fetch_eligible_issues(
|
|
356
|
+
label: str,
|
|
357
|
+
extra_fields: tuple[str, ...] = (),
|
|
358
|
+
input_file: str | None = None,
|
|
359
|
+
required_fields: tuple[str, ...] = BASE_REQUIRED_FIELDS,
|
|
360
|
+
) -> list[dict[str, Any]]:
|
|
361
|
+
"""Return the FULL eligible pool for `label`, oldest-first by
|
|
362
|
+
`createdAt` — no `--max-issues`-style truncation (that stays each
|
|
363
|
+
caller's own concern, applied after this returns).
|
|
364
|
+
|
|
365
|
+
Sources issues from `input_file` when given (bypassing the live `gh`
|
|
366
|
+
call, the shared `--input-file` test seam), otherwise from a live
|
|
367
|
+
`gh issue list` call requesting `required_fields` plus `extra_fields`.
|
|
368
|
+
Raises `FetchError` on any fetch/validation problem (never an uncaught
|
|
369
|
+
exception).
|
|
370
|
+
"""
|
|
371
|
+
if input_file:
|
|
372
|
+
issues = load_issues_from_file(input_file, required_fields)
|
|
373
|
+
else:
|
|
374
|
+
issues = fetch_issues_from_gh(label, required_fields, extra_fields)
|
|
375
|
+
|
|
376
|
+
eligible = [issue for issue in issues if is_eligible(issue, label)]
|
|
377
|
+
eligible.sort(key=lambda issue: issue["createdAt"])
|
|
378
|
+
return eligible
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def add_input_seam_args(parser: argparse.ArgumentParser) -> None:
|
|
382
|
+
"""Add the shared `--input-file`/`--now-override` test seam to `parser`.
|
|
383
|
+
|
|
384
|
+
Both flags are optional at this shared level; each caller decides its
|
|
385
|
+
own default file-source behavior. `--input-file` lets a caller skip its
|
|
386
|
+
live `gh` fetch entirely (JSON array of issue objects); `--now-override`
|
|
387
|
+
feeds `is_stale` an explicit "now" instead of the real clock.
|
|
388
|
+
"""
|
|
389
|
+
parser.add_argument(
|
|
390
|
+
"--input-file",
|
|
391
|
+
help="Path to a JSON array of issue objects, bypassing the live gh fetch.",
|
|
392
|
+
)
|
|
393
|
+
parser.add_argument(
|
|
394
|
+
"--now-override",
|
|
395
|
+
type=_iso8601,
|
|
396
|
+
help="ISO-8601 UTC timestamp (e.g. 2026-07-08T12:00:00Z) to use as 'now'.",
|
|
397
|
+
)
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Snapshot/diff/restore guards for tools that touch a project's `CLAUDE.md`.
|
|
2
|
+
|
|
3
|
+
Two distinct guards live here, for two distinct contracts:
|
|
4
|
+
|
|
5
|
+
**`run_install_with_guard`** — for `graphify install --project` (#846), which
|
|
6
|
+
rewrites the target repo's `CLAUDE.md` to add a `## graphify` section. Its
|
|
7
|
+
updater matches the literal `## graphify` header text and replaces everything
|
|
8
|
+
between it and the next `##` heading — a known bug can over-delete, removing
|
|
9
|
+
pre-existing unrelated content along with the stale graphify section.
|
|
10
|
+
|
|
11
|
+
1. Snapshot `CLAUDE.md` before running the installer.
|
|
12
|
+
2. Diff the snapshot against the post-install file.
|
|
13
|
+
3. If any pre-existing line was lost, restore the snapshot and append the
|
|
14
|
+
canonical `## graphify` section text at EOF instead of trusting the
|
|
15
|
+
installer's in-place edit.
|
|
16
|
+
4. If nothing was lost, leave the installer's output as-is.
|
|
17
|
+
|
|
18
|
+
**`run_install_reverting_unexpected_writes`** — general-purpose guard for an
|
|
19
|
+
installer that is expected to leave `CLAUDE.md` **completely untouched**: ANY
|
|
20
|
+
diff — added or removed lines, even a write followed by the installer itself
|
|
21
|
+
raising — is reverted in full. The reverted-or-not outcome and any added
|
|
22
|
+
lines are returned separately, so a deletion-only write is never mistaken for
|
|
23
|
+
a no-op. Originally written for `repowise init` (#1670 item 3), whose
|
|
24
|
+
`## Codebase Intelligence for <project> (Repowise)` write into
|
|
25
|
+
`.claude/CLAUDE.md` turned out to be intended, expected behavior, not a bug —
|
|
26
|
+
so `project-init/SKILL.md` does not call this function today. Kept as
|
|
27
|
+
reviewed, tested infra for a future installer that genuinely needs a
|
|
28
|
+
zero-tolerance guard (unlike `run_install_with_guard`'s scoped-section
|
|
29
|
+
tolerance above), rather than deleted, since the two guards' contracts are
|
|
30
|
+
otherwise easy to conflate and re-deriving this one from scratch later would
|
|
31
|
+
repeat work already done once.
|
|
32
|
+
|
|
33
|
+
Stdlib-only, per ADR 0014/0015 (Python for cross-OS scripts).
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import traceback
|
|
39
|
+
from collections import Counter
|
|
40
|
+
from collections.abc import Callable
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
|
|
43
|
+
_GRAPHIFY_HEADER = "## graphify"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _protected_lines(text: str) -> list[str]:
|
|
47
|
+
"""All lines of `text` except the existing `## graphify` section's body.
|
|
48
|
+
|
|
49
|
+
The graphify section is expected to change on every install (that's the
|
|
50
|
+
point of re-running the installer) so it is excluded from the
|
|
51
|
+
corruption check — only lines *outside* that section must survive.
|
|
52
|
+
"""
|
|
53
|
+
lines = text.splitlines()
|
|
54
|
+
start = next(
|
|
55
|
+
(i for i, line in enumerate(lines) if line.strip() == _GRAPHIFY_HEADER),
|
|
56
|
+
None,
|
|
57
|
+
)
|
|
58
|
+
if start is None:
|
|
59
|
+
return lines
|
|
60
|
+
|
|
61
|
+
end = len(lines)
|
|
62
|
+
for i in range(start + 1, len(lines)):
|
|
63
|
+
if lines[i].startswith("## "):
|
|
64
|
+
end = i
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
return lines[:start] + lines[end:]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _lines_only_in(candidate_lines: list[str], baseline_counts: Counter) -> list[str]:
|
|
71
|
+
"""Multiset-diff kernel shared by `find_missing_lines`/`find_added_lines`:
|
|
72
|
+
lines in `candidate_lines` that occur more often there than in
|
|
73
|
+
`baseline_counts`. Order-preserving relative to `candidate_lines`. A line
|
|
74
|
+
appearing twice in the candidate but only once in the baseline is
|
|
75
|
+
reported once.
|
|
76
|
+
"""
|
|
77
|
+
diff: list[str] = []
|
|
78
|
+
seen = Counter()
|
|
79
|
+
for line in candidate_lines:
|
|
80
|
+
seen[line] += 1
|
|
81
|
+
if seen[line] > baseline_counts[line]:
|
|
82
|
+
diff.append(line)
|
|
83
|
+
return diff
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def find_missing_lines(old_text: str, new_text: str) -> list[str]:
|
|
87
|
+
"""Return lines present in `old_text` (outside its `## graphify` section)
|
|
88
|
+
but missing (by multiset) from `new_text`.
|
|
89
|
+
|
|
90
|
+
Order-preserving relative to `old_text`. A line that appears twice in
|
|
91
|
+
`old_text` but only once in `new_text` is reported as missing once.
|
|
92
|
+
"""
|
|
93
|
+
return _lines_only_in(_protected_lines(old_text), Counter(new_text.splitlines()))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def run_install_with_guard(
|
|
97
|
+
claude_md_path: Path,
|
|
98
|
+
installer: Callable[[], None],
|
|
99
|
+
canonical_section: str,
|
|
100
|
+
) -> bool:
|
|
101
|
+
"""Run `installer()` against `claude_md_path` under the corruption guard.
|
|
102
|
+
|
|
103
|
+
Returns True if corruption was detected and repaired (snapshot restored
|
|
104
|
+
+ canonical section appended), False if the installer's output was left
|
|
105
|
+
as-is because nothing pre-existing was lost.
|
|
106
|
+
|
|
107
|
+
The compare-and-repair check runs even if `installer()` itself raises
|
|
108
|
+
partway through a write (a build error after the `CLAUDE.md` edit already
|
|
109
|
+
landed, for instance) — it happens in a `finally` block, and the original
|
|
110
|
+
exception still propagates once the repair completes. Reads go through
|
|
111
|
+
`_read_or_none` rather than assuming `claude_md_path` exists, since an
|
|
112
|
+
installer can fail before ever creating the file.
|
|
113
|
+
"""
|
|
114
|
+
snapshot = _read_or_none(claude_md_path)
|
|
115
|
+
was_repaired = False
|
|
116
|
+
installer_raised = False
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
installer()
|
|
120
|
+
except BaseException:
|
|
121
|
+
# Bound to THIS call, not read from ambient `sys.exc_info()` inside
|
|
122
|
+
# `finally` below — the ambient form would report whatever exception
|
|
123
|
+
# is being handled if this function is ever called from inside an
|
|
124
|
+
# active `except:` block elsewhere, which is not necessarily
|
|
125
|
+
# installer()'s own.
|
|
126
|
+
installer_raised = True
|
|
127
|
+
raise
|
|
128
|
+
finally:
|
|
129
|
+
try:
|
|
130
|
+
post_install = _read_or_none(claude_md_path)
|
|
131
|
+
missing = find_missing_lines(snapshot or "", post_install or "")
|
|
132
|
+
|
|
133
|
+
if missing:
|
|
134
|
+
was_repaired = True
|
|
135
|
+
repaired = snapshot or ""
|
|
136
|
+
if not repaired.endswith("\n"):
|
|
137
|
+
repaired += "\n"
|
|
138
|
+
if not canonical_section.startswith("\n"):
|
|
139
|
+
repaired += "\n"
|
|
140
|
+
repaired += canonical_section
|
|
141
|
+
claude_md_path.write_text(repaired, encoding="utf-8")
|
|
142
|
+
except Exception:
|
|
143
|
+
# A secondary failure while repairing must never displace
|
|
144
|
+
# installer()'s own in-flight exception — that would mask the
|
|
145
|
+
# real failure with an unrelated one from the repair path itself
|
|
146
|
+
# (e.g. a permission error writing the repair back). If
|
|
147
|
+
# installer() did NOT raise, this secondary failure is the only
|
|
148
|
+
# one there is, so it must propagate normally.
|
|
149
|
+
if installer_raised:
|
|
150
|
+
traceback.print_exc()
|
|
151
|
+
else:
|
|
152
|
+
raise
|
|
153
|
+
|
|
154
|
+
return was_repaired
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def find_added_lines(old_text: str, new_text: str) -> list[str]:
|
|
158
|
+
"""Return lines present in `new_text` but missing (by multiset) from
|
|
159
|
+
`old_text`. Order-preserving relative to `new_text`.
|
|
160
|
+
|
|
161
|
+
Unlike `find_missing_lines`, this does NOT exclude any section — the
|
|
162
|
+
zero-tolerance contract this serves (see `run_install_reverting_unexpected_writes`
|
|
163
|
+
and the module docstring) tolerates no section at all, so excluding a
|
|
164
|
+
`## graphify` body here would under-report a genuine addition landing
|
|
165
|
+
inside one. Not a strict mirror of `find_missing_lines` for that reason;
|
|
166
|
+
both share their multiset-diff logic via `_lines_only_in`.
|
|
167
|
+
"""
|
|
168
|
+
return _lines_only_in(new_text.splitlines(), Counter(old_text.splitlines()))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _read_or_none(path: Path) -> str | None:
|
|
172
|
+
"""Read `path`'s text, or `None` if it does not exist.
|
|
173
|
+
|
|
174
|
+
A single call rather than a separate `exists()` + `read_text()` pair, so
|
|
175
|
+
there is no window between the two where the file could be removed out
|
|
176
|
+
from under this guard.
|
|
177
|
+
"""
|
|
178
|
+
try:
|
|
179
|
+
return path.read_text(encoding="utf-8")
|
|
180
|
+
except FileNotFoundError:
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def run_install_reverting_unexpected_writes(
|
|
185
|
+
claude_md_path: Path,
|
|
186
|
+
installer: Callable[[], None],
|
|
187
|
+
) -> tuple[bool, list[str]]:
|
|
188
|
+
"""Run `installer()` against `claude_md_path`, reverting ANY write it made.
|
|
189
|
+
|
|
190
|
+
No shipped caller today — kept deliberately, see module docstring
|
|
191
|
+
(#1670 item 3).
|
|
192
|
+
|
|
193
|
+
Unlike `run_install_with_guard`, which expects and tolerates changes
|
|
194
|
+
scoped to one known section, this treats `claude_md_path` as a file the
|
|
195
|
+
installer must leave completely untouched. If `installer()` changed it at
|
|
196
|
+
all (added or removed lines, or created/deleted the file), the pre-run
|
|
197
|
+
state is restored — the file is rewritten to its snapshot, or deleted if
|
|
198
|
+
it did not exist beforehand.
|
|
199
|
+
|
|
200
|
+
Returns `(reverted, added)`: `reverted` is True iff anything changed and
|
|
201
|
+
was undone. `added` lists any lines the installer had added — it is empty
|
|
202
|
+
both when nothing changed AND when the installer only removed lines, so
|
|
203
|
+
callers MUST branch on `reverted`, never on `bool(added)`, to decide
|
|
204
|
+
whether anything happened (a deletion-only write still needs reporting).
|
|
205
|
+
|
|
206
|
+
The revert runs even if `installer()` itself raises partway through a
|
|
207
|
+
write — the compare-and-restore happens in a `finally` block, and the
|
|
208
|
+
original exception still propagates once it completes.
|
|
209
|
+
"""
|
|
210
|
+
snapshot = _read_or_none(claude_md_path)
|
|
211
|
+
reverted = False
|
|
212
|
+
added: list[str] = []
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
installer()
|
|
216
|
+
finally:
|
|
217
|
+
post_install = _read_or_none(claude_md_path)
|
|
218
|
+
if post_install != snapshot:
|
|
219
|
+
reverted = True
|
|
220
|
+
added = find_added_lines(snapshot or "", post_install or "")
|
|
221
|
+
if snapshot is not None:
|
|
222
|
+
claude_md_path.write_text(snapshot, encoding="utf-8")
|
|
223
|
+
elif post_install is not None:
|
|
224
|
+
claude_md_path.unlink()
|
|
225
|
+
|
|
226
|
+
return reverted, added
|