@danmoisan/drm-copilot-mcp 1.1.22 → 1.1.24
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/out/mcp-server.js +18758 -12030
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +1 -1
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +4 -4
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/discovery-capability-sentinel.md +34 -0
- package/resources/claude-customizations/.claude/agents/feature-review.md +8 -0
- package/resources/claude-customizations/.claude/agents/legacy-parity-analyst.md +101 -43
- package/resources/claude-customizations/.claude/agents/migration-coverage-reviewer.md +107 -43
- package/resources/claude-customizations/.claude/agents/orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +60 -6
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +21 -1
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +48 -7
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/requirements-reconciler.md +117 -42
- package/resources/claude-customizations/.claude/agents/runtime-characterization-analyst.md +103 -43
- package/resources/claude-customizations/.claude/hooks/discovery-capability-probe.ps1 +102 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +69 -194
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-planning-gate.ps1 +87 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-green-ci.ps1 +325 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +29 -7
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +23 -10
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +29 -21
- package/resources/claude-customizations/.claude/hooks/enforce-file-size-limit.ps1 +244 -0
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +51 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +168 -48
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +1 -1
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +22 -11
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +59 -20
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +51 -4
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +72 -11
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.current-branch.ps1 +149 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +4 -2
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +59 -20
- package/resources/claude-customizations/.claude/hooks/record-discovery-agent-start.ps1 +132 -0
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +56 -212
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-feature-folder.ps1 +146 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-rejection.ps1 +172 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +34 -4
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +9 -3
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusAggregate.psm1 +154 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusCombination.psm1 +228 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +4 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +63 -16
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +56 -1
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +4 -1
- package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +33 -6
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +5 -1
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +25 -2
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +188 -341
- package/resources/claude-customizations/.claude/lib/file-size-policy/FileSizePolicy.psm1 +434 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateFeatureFolderRecord.psm1 +166 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +8 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1 +391 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunRecordPath.psm1 +185 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +11 -21
- package/resources/claude-customizations/.claude/rules/general-code-change.md +4 -1
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +29 -5
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +152 -20
- package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
- package/resources/claude-customizations/.claude/rules/shell.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +36 -3
- package/resources/claude-customizations/.claude/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/claude-customizations/.claude/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/discovery-behavior-reconciliation/SKILL.md +28 -47
- package/resources/claude-customizations/.claude/skills/discovery-coverage-ledger/SKILL.md +32 -48
- package/resources/claude-customizations/.claude/skills/discovery-parity-matrix/SKILL.md +28 -45
- package/resources/claude-customizations/.claude/skills/discovery-repo-inventory/SKILL.md +28 -62
- package/resources/claude-customizations/.claude/skills/discovery-runtime-characterization/SKILL.md +28 -45
- package/resources/claude-customizations/.claude/skills/discovery-validate-artifacts/SKILL.md +28 -61
- package/resources/claude-customizations/.claude/skills/discovery-workflow/SKILL.md +55 -139
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +25 -10
- package/resources/claude-customizations/.claude/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -0
- package/resources/claude-customizations/.claude/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/claude-customizations/.claude/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +15 -7
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +25 -2
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +11 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +124 -14
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +72 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +56 -9
- package/resources/claude-customizations/.claude/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/claude-customizations/.claude/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +1 -1
- package/resources/claude-customizations/config/blast-radius.json +6 -5
- package/resources/claude-customizations/config/orchestration-routing.json +61 -1
- package/resources/claude-customizations/pack-manifests/core.json +40 -1
- package/resources/codex-and-agents-customizations/.agents/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +7 -0
- package/resources/codex-and-agents-customizations/.agents/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-coverage-ledger/SKILL.md +50 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-parity-matrix/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-repo-inventory/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-runtime-characterization/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-validate-artifacts/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-workflow/SKILL.md +62 -0
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +25 -10
- package/resources/codex-and-agents-customizations/.agents/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +4 -1
- package/resources/codex-and-agents-customizations/.agents/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/codex-and-agents-customizations/.agents/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/codex-and-agents-customizations/.agents/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/discovery-capability-sentinel.toml +51 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +8 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c1.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c2.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3-elevated.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c4.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c1.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c2.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3-elevated.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c4.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c1.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c2.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3-elevated.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c4.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c1.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c2.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3-elevated.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c4.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +39 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +22 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/discovery-capability-probe.ps1 +97 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/discovery-start-attestation.ps1 +171 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +4 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-artifact-gate.ps1 +187 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-planning-gate.ps1 +149 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-file-size-limit.ps1 +282 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +239 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +54 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +54 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/file-size-policy.ps1 +421 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-target-worktree.ps1 +369 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +8 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +22 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-discovery-agent-output.ps1 +178 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +11 -1
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/AGENTS.md +4 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +65 -1
- package/resources/config/orchestration-routing.json +61 -1
- package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
- package/resources/customizations/.github/agents/discovery-capability-sentinel.agent.md +34 -0
- package/resources/customizations/.github/agents/legacy-parity-analyst.agent.md +122 -0
- package/resources/customizations/.github/agents/migration-coverage-reviewer.agent.md +128 -0
- package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +1 -1
- package/resources/customizations/.github/agents/python-typed-engineer.agent.md +1 -1
- package/resources/customizations/.github/agents/requirements-reconciler.agent.md +138 -0
- package/resources/customizations/.github/agents/runtime-characterization-analyst.agent.md +124 -0
- package/resources/customizations/.github/hooks/discovery-agent-start.ps1 +139 -0
- package/resources/customizations/.github/hooks/discovery-agent-stop.ps1 +123 -0
- package/resources/customizations/.github/hooks/discovery-capability-probe.ps1 +98 -0
- package/resources/customizations/.github/hooks/discovery-planning.ps1 +91 -0
- package/resources/customizations/.github/hooks/discovery-pre-tool.ps1 +123 -0
- package/resources/customizations/.github/hooks/discovery.json +35 -0
- package/resources/customizations/.github/instructions/general-code-change.instructions.md +5 -2
- package/resources/customizations/.github/instructions/mermaid.instructions.md +85 -0
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +1 -1
- package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +105 -0
- package/resources/customizations/.github/prompts/remediate-comments.prompt.md +1 -1
- package/resources/customizations/.github/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/customizations/.github/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/customizations/.github/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-coverage-ledger/SKILL.md +50 -0
- package/resources/customizations/.github/skills/discovery-parity-matrix/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-repo-inventory/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-runtime-characterization/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-validate-artifacts/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-workflow/SKILL.md +62 -0
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +25 -10
- package/resources/customizations/.github/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +8 -0
- package/resources/customizations/.github/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/customizations/.github/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/customizations/.github/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/customizations/.github/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/customizations/.github/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/customizations/.github/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/lib/codex-routing/CodexDeployment.psm1 +5 -1
- package/resources/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +25 -2
- package/resources/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
- package/resources/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
- package/resources/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
- package/resources/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
- package/resources/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
- package/resources/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
- package/resources/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
- package/resources/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
- package/resources/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
- package/resources/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
- package/resources/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
- package/resources/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
- package/resources/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
- package/resources/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
- package/resources/powershell/PoshQC/PoshQC.Parallel.psm1 +452 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +106 -8
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +12 -0
- package/resources/powershell/PoshQC/PoshQC.psd1 +6 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +7 -1
- package/resources/powershell/PoshQC/README.md +20 -1
- package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +4 -4
- package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +4 -4
package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1
CHANGED
|
@@ -8,14 +8,26 @@
|
|
|
8
8
|
operation. When the target file is a PowerShell source file (.ps1, .psm1, .psd1),
|
|
9
9
|
it decides whether the change may proceed in direct mode.
|
|
10
10
|
|
|
11
|
-
Direct mode counts distinct production PowerShell paths per Codex session
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
Direct mode counts distinct production PowerShell paths per Codex session and per
|
|
12
|
+
resolved target worktree, and repeated edits to the same file are counted once. The
|
|
13
|
+
4th distinct production path in one worktree is denied with a
|
|
14
|
+
POWERSHELL_LARGE_PATH_REQUIRED reason that instructs the caller to route the change
|
|
15
|
+
through .codex/prompts/orchestrate-work.md.
|
|
16
|
+
|
|
17
|
+
Each evaluated path is placed with Resolve-HookTargetWorktree from the dot-sourced
|
|
18
|
+
hook-target-worktree.ps1. An absolute path resolves to its nearest enclosing
|
|
19
|
+
registered git worktree; a relative path, a path inside no registered worktree, and
|
|
20
|
+
an ambiguous result use the session root. The route is read from
|
|
21
|
+
artifacts/orchestration/orchestrator-state.json in the resolved target worktree.
|
|
22
|
+
The running set is kept under the session root, in
|
|
23
|
+
.codex/state/powershell-batch-budget.<session_id>.json when the resolved target worktree
|
|
24
|
+
is the session root and in .codex/state/powershell-batch-budget.<session_id>.<worktree key>.json
|
|
25
|
+
otherwise; no state is written under another worktree. A path under the root-level
|
|
26
|
+
artifacts/ directory of the resolved target worktree is never counted and writes no
|
|
27
|
+
state. No containment filter applies: a path outside every registered worktree is
|
|
28
|
+
counted against the session root, as before.
|
|
29
|
+
|
|
30
|
+
The selected route is the
|
|
19
31
|
route_id value when that key is present, otherwise the path_selected value, and it
|
|
20
32
|
is usable only as a non-blank string. When the selected route is large,
|
|
21
33
|
remediation, or preparation and the checkpoint is not terminal (next_step is not
|
|
@@ -36,9 +48,12 @@
|
|
|
36
48
|
hookSpecificOutput.permissionDecision = 'deny' and exits 0. Files already counted
|
|
37
49
|
are always allowed through.
|
|
38
50
|
|
|
39
|
-
Known
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
Known limitations: an 8.3 short-name path, or a path reached through a junction,
|
|
52
|
+
that differs textually from the long path git reports matches no registered
|
|
53
|
+
worktree and falls back to the session root. A stale non-terminal large-path
|
|
54
|
+
checkpoint in a worktree exempts a later direct-mode session editing that worktree;
|
|
55
|
+
orchestrator checkpoint hygiene (issue #673) moves foreign checkpoints aside before
|
|
56
|
+
a new run.
|
|
42
57
|
|
|
43
58
|
.NOTES
|
|
44
59
|
Compatible with PowerShell 7+.
|
|
@@ -51,6 +66,9 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
|
|
|
51
66
|
# mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
|
|
52
67
|
try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
|
|
53
68
|
|
|
69
|
+
# Shared target-worktree resolver; the Codex twin of HookTargetWorktree.psm1.
|
|
70
|
+
try { . (Join-Path $PSScriptRoot 'hook-target-worktree.ps1') } catch { Add-HookDependencyFailure -Name 'hook-target-worktree.ps1' -ErrorRecord $_ }
|
|
71
|
+
|
|
54
72
|
# Shared route helpers; this file is byte-identical to the Claude runtime copy.
|
|
55
73
|
$script:PowerShellBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PowerShellBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
|
|
56
74
|
|
|
@@ -194,7 +212,8 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
194
212
|
[scriptblock] $WriteState = {
|
|
195
213
|
param([string] $Path, [System.Collections.IDictionary] $State)
|
|
196
214
|
$State | ConvertTo-Json -Depth 5 | Set-Content -Path $Path -Encoding UTF8
|
|
197
|
-
}
|
|
215
|
+
},
|
|
216
|
+
[scriptblock] $WorktreeListInvoker
|
|
198
217
|
)
|
|
199
218
|
|
|
200
219
|
if (-not $ToolInputRaw) {
|
|
@@ -218,12 +237,31 @@ function Invoke-PowerShellBatchBudgetHook {
|
|
|
218
237
|
}
|
|
219
238
|
if ($script:PowerShellBatchBudgetRouteImportFailure) { return Get-PowerShellBatchBudgetBlockDecision -Reason "POWERSHELL_LARGE_PATH_REQUIRED: the route helper '$($script:PowerShellBatchBudgetRouteImportFailure)' failed to load, so the batch budget for '$normalized' cannot be evaluated; the gate fails closed." }
|
|
220
239
|
|
|
240
|
+
# The path is evaluated against the registered worktree that holds it; the session root
|
|
241
|
+
# is the fallback for a relative path, a path in no registered worktree, and an ambiguous result.
|
|
242
|
+
$resolverArguments = @{ SessionRoot = $Root; FilePath = @($filePath) }
|
|
243
|
+
if ($PSBoundParameters.ContainsKey('WorktreeListInvoker')) { $resolverArguments['WorktreeListInvoker'] = $WorktreeListInvoker }
|
|
244
|
+
$resolution = Resolve-HookTargetWorktree @resolverArguments
|
|
245
|
+
$targetRoot = $Root
|
|
246
|
+
$isSessionRoot = $true
|
|
247
|
+
if (-not $resolution.Ambiguous -and $null -ne $resolution.WorktreeRoot -and -not [string]::Equals($resolution.WorktreeRoot, (ConvertTo-HookTargetWorktreeNormalizedPath -Path $Root), [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
248
|
+
$targetRoot = $resolution.WorktreeRoot
|
|
249
|
+
$isSessionRoot = $false
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# The root-level artifacts/ directory is gitignored scratch, never a production file.
|
|
253
|
+
$relativePath = if ($normalized -match '^(/|[A-Za-z]:/)') { Get-HookTargetWorktreeRelativePath -Path $normalized -WorktreeRoot $targetRoot } else { $normalized }
|
|
254
|
+
if ($null -ne $relativePath -and $relativePath.StartsWith('artifacts/', [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
255
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
256
|
+
}
|
|
257
|
+
|
|
221
258
|
$stateDir = Join-Path -Path $Root -ChildPath '.codex/state'
|
|
222
|
-
$
|
|
259
|
+
$stateLeaf = if ($isSessionRoot) { "powershell-batch-budget.$SessionId.json" } else { "powershell-batch-budget.$SessionId.$(Get-HookTargetWorktreeKey -WorktreeRoot $targetRoot).json" }
|
|
260
|
+
$stateFile = Join-Path -Path $stateDir -ChildPath $stateLeaf
|
|
223
261
|
|
|
224
262
|
# The checkpoint is read before any state operation, so the large path neither
|
|
225
263
|
# creates the state directory nor reads or writes the state file.
|
|
226
|
-
$checkpointPath = Join-Path -Path $
|
|
264
|
+
$checkpointPath = Join-Path -Path $targetRoot -ChildPath 'artifacts/orchestration/orchestrator-state.json'
|
|
227
265
|
$checkpointText = ''
|
|
228
266
|
try {
|
|
229
267
|
$checkpointText = [string](& $ReadCheckpoint $checkpointPath)
|
|
@@ -279,7 +317,8 @@ function Invoke-PowerShellBatchBudgetCodexEntryPoint {
|
|
|
279
317
|
.PARAMETER PayloadRaw
|
|
280
318
|
Raw PreToolUse payload text.
|
|
281
319
|
.PARAMETER RepositoryRoot
|
|
282
|
-
|
|
320
|
+
Session root: it locates the Codex state directory and is the fallback root. The
|
|
321
|
+
orchestrator checkpoint comes from the resolved target worktree of each path.
|
|
283
322
|
.PARAMETER HookSeams
|
|
284
323
|
Optional seam overrides splatted into Invoke-PowerShellBatchBudgetHook.
|
|
285
324
|
#>
|
package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1
CHANGED
|
@@ -8,14 +8,26 @@
|
|
|
8
8
|
operation. When the target file is a Python file (.py), it decides whether the
|
|
9
9
|
change may proceed in direct mode.
|
|
10
10
|
|
|
11
|
-
Direct mode counts distinct production Python paths per Codex session
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
Direct mode counts distinct production Python paths per Codex session and per
|
|
12
|
+
resolved target worktree, and repeated edits to the same file are counted once. The
|
|
13
|
+
4th distinct production path in one worktree is denied with a
|
|
14
|
+
PYTHON_LARGE_PATH_REQUIRED reason that instructs the caller to route the change
|
|
15
|
+
through .codex/prompts/orchestrate-work.md.
|
|
16
|
+
|
|
17
|
+
Each evaluated path is placed with Resolve-HookTargetWorktree from the dot-sourced
|
|
18
|
+
hook-target-worktree.ps1. An absolute path resolves to its nearest enclosing
|
|
19
|
+
registered git worktree; a relative path, a path inside no registered worktree, and
|
|
20
|
+
an ambiguous result use the session root. The route is read from
|
|
21
|
+
artifacts/orchestration/orchestrator-state.json in the resolved target worktree.
|
|
22
|
+
The running set is kept under the session root, in
|
|
23
|
+
.codex/state/python-batch-budget.<session_id>.json when the resolved target worktree
|
|
24
|
+
is the session root and in .codex/state/python-batch-budget.<session_id>.<worktree key>.json
|
|
25
|
+
otherwise; no state is written under another worktree. A path under the root-level
|
|
26
|
+
artifacts/ directory of the resolved target worktree is never counted and writes no
|
|
27
|
+
state. No containment filter applies: a path outside every registered worktree is
|
|
28
|
+
counted against the session root, as before.
|
|
29
|
+
|
|
30
|
+
The selected route is the
|
|
19
31
|
route_id value when that key is present, otherwise the path_selected value, and it
|
|
20
32
|
is usable only as a non-blank string. When the selected route is large,
|
|
21
33
|
remediation, or preparation and the checkpoint is not terminal (next_step is not
|
|
@@ -36,9 +48,12 @@
|
|
|
36
48
|
hookSpecificOutput.permissionDecision = 'deny' and exits 0. Files already counted
|
|
37
49
|
are always allowed through.
|
|
38
50
|
|
|
39
|
-
Known
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
Known limitations: an 8.3 short-name path, or a path reached through a junction,
|
|
52
|
+
that differs textually from the long path git reports matches no registered
|
|
53
|
+
worktree and falls back to the session root. A stale non-terminal large-path
|
|
54
|
+
checkpoint in a worktree exempts a later direct-mode session editing that worktree;
|
|
55
|
+
orchestrator checkpoint hygiene (issue #673) moves foreign checkpoints aside before
|
|
56
|
+
a new run.
|
|
42
57
|
|
|
43
58
|
.NOTES
|
|
44
59
|
Compatible with PowerShell 7+.
|
|
@@ -51,6 +66,9 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
|
|
|
51
66
|
# mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
|
|
52
67
|
try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
|
|
53
68
|
|
|
69
|
+
# Shared target-worktree resolver; the Codex twin of HookTargetWorktree.psm1.
|
|
70
|
+
try { . (Join-Path $PSScriptRoot 'hook-target-worktree.ps1') } catch { Add-HookDependencyFailure -Name 'hook-target-worktree.ps1' -ErrorRecord $_ }
|
|
71
|
+
|
|
54
72
|
# Shared route helpers; this file is byte-identical to the Claude runtime copy.
|
|
55
73
|
$script:PythonBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PythonBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
|
|
56
74
|
|
|
@@ -197,7 +215,8 @@ function Invoke-PythonBatchBudgetHook {
|
|
|
197
215
|
[scriptblock] $WriteState = {
|
|
198
216
|
param([string] $Path, [System.Collections.IDictionary] $State)
|
|
199
217
|
$State | ConvertTo-Json -Depth 5 | Set-Content -Path $Path -Encoding UTF8
|
|
200
|
-
}
|
|
218
|
+
},
|
|
219
|
+
[scriptblock] $WorktreeListInvoker
|
|
201
220
|
)
|
|
202
221
|
|
|
203
222
|
if (-not $ToolInputRaw) {
|
|
@@ -221,12 +240,31 @@ function Invoke-PythonBatchBudgetHook {
|
|
|
221
240
|
}
|
|
222
241
|
if ($script:PythonBatchBudgetRouteImportFailure) { return Get-PythonBatchBudgetBlockDecision -Reason "PYTHON_LARGE_PATH_REQUIRED: the route helper '$($script:PythonBatchBudgetRouteImportFailure)' failed to load, so the batch budget for '$normalized' cannot be evaluated; the gate fails closed." }
|
|
223
242
|
|
|
243
|
+
# The path is evaluated against the registered worktree that holds it; the session root
|
|
244
|
+
# is the fallback for a relative path, a path in no registered worktree, and an ambiguous result.
|
|
245
|
+
$resolverArguments = @{ SessionRoot = $Root; FilePath = @($filePath) }
|
|
246
|
+
if ($PSBoundParameters.ContainsKey('WorktreeListInvoker')) { $resolverArguments['WorktreeListInvoker'] = $WorktreeListInvoker }
|
|
247
|
+
$resolution = Resolve-HookTargetWorktree @resolverArguments
|
|
248
|
+
$targetRoot = $Root
|
|
249
|
+
$isSessionRoot = $true
|
|
250
|
+
if (-not $resolution.Ambiguous -and $null -ne $resolution.WorktreeRoot -and -not [string]::Equals($resolution.WorktreeRoot, (ConvertTo-HookTargetWorktreeNormalizedPath -Path $Root), [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
251
|
+
$targetRoot = $resolution.WorktreeRoot
|
|
252
|
+
$isSessionRoot = $false
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# The root-level artifacts/ directory is gitignored scratch, never a production file.
|
|
256
|
+
$relativePath = if ($normalized -match '^(/|[A-Za-z]:/)') { Get-HookTargetWorktreeRelativePath -Path $normalized -WorktreeRoot $targetRoot } else { $normalized }
|
|
257
|
+
if ($null -ne $relativePath -and $relativePath.StartsWith('artifacts/', [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
258
|
+
return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
259
|
+
}
|
|
260
|
+
|
|
224
261
|
$stateDir = Join-Path -Path $Root -ChildPath '.codex/state'
|
|
225
|
-
$
|
|
262
|
+
$stateLeaf = if ($isSessionRoot) { "python-batch-budget.$SessionId.json" } else { "python-batch-budget.$SessionId.$(Get-HookTargetWorktreeKey -WorktreeRoot $targetRoot).json" }
|
|
263
|
+
$stateFile = Join-Path -Path $stateDir -ChildPath $stateLeaf
|
|
226
264
|
|
|
227
265
|
# The checkpoint is read before any state operation, so the large path neither
|
|
228
266
|
# creates the state directory nor reads or writes the state file.
|
|
229
|
-
$checkpointPath = Join-Path -Path $
|
|
267
|
+
$checkpointPath = Join-Path -Path $targetRoot -ChildPath 'artifacts/orchestration/orchestrator-state.json'
|
|
230
268
|
$checkpointText = ''
|
|
231
269
|
try {
|
|
232
270
|
$checkpointText = [string](& $ReadCheckpoint $checkpointPath)
|
|
@@ -282,7 +320,8 @@ function Invoke-PythonBatchBudgetCodexEntryPoint {
|
|
|
282
320
|
.PARAMETER PayloadRaw
|
|
283
321
|
Raw PreToolUse payload text.
|
|
284
322
|
.PARAMETER RepositoryRoot
|
|
285
|
-
|
|
323
|
+
Session root: it locates the Codex state directory and is the fallback root. The
|
|
324
|
+
orchestrator checkpoint comes from the resolved target worktree of each path.
|
|
286
325
|
.PARAMETER HookSeams
|
|
287
326
|
Optional seam overrides splatted into Invoke-PythonBatchBudgetHook.
|
|
288
327
|
#>
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Codex twin of the tiered file-size policy functions (500 guidance, 700 advisory, 800 hard limit).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Dot-sourced by .codex/hooks/enforce-file-size-limit.ps1 as:
|
|
7
|
+
. (Join-Path $PSScriptRoot 'file-size-policy.ps1')
|
|
8
|
+
|
|
9
|
+
Codex hooks cannot import .claude/lib in a consumer repository, so the policy
|
|
10
|
+
functions of .claude/lib/file-size-policy/FileSizePolicy.psm1 are mirrored here.
|
|
11
|
+
Every function definition in this file is text-identical to the definition of the
|
|
12
|
+
same name in that module; tests/scripts/codex-hooks/file-size-policy.Tests.ps1
|
|
13
|
+
parses both files and fails on any difference, and runs the shared case rows
|
|
14
|
+
against this file. Edit the module first, then copy each changed definition here.
|
|
15
|
+
|
|
16
|
+
.NOTES
|
|
17
|
+
Compatible with PowerShell 7+.
|
|
18
|
+
|
|
19
|
+
Entrypoint-free by design: this file contains only function definitions, so
|
|
20
|
+
dot-sourcing it has no side effects and no exit-code semantics. It never reads
|
|
21
|
+
standard input and never reads any legacy Claude environment variable. Every
|
|
22
|
+
function is pure: no filesystem, process, network, environment, or clock access.
|
|
23
|
+
#>
|
|
24
|
+
|
|
25
|
+
function Get-FileSizePolicyLimit {
|
|
26
|
+
<#
|
|
27
|
+
.SYNOPSIS
|
|
28
|
+
Return the three tier limits of the file-size policy.
|
|
29
|
+
.OUTPUTS
|
|
30
|
+
A PSCustomObject with Guidance (500), Advisory (700), and HardLimit (800).
|
|
31
|
+
#>
|
|
32
|
+
[CmdletBinding()]
|
|
33
|
+
[OutputType([pscustomobject])]
|
|
34
|
+
param()
|
|
35
|
+
|
|
36
|
+
return [pscustomobject]@{
|
|
37
|
+
Guidance = 500
|
|
38
|
+
Advisory = 700
|
|
39
|
+
HardLimit = 800
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Get-FileSizeExcludedSegment {
|
|
44
|
+
<#
|
|
45
|
+
.SYNOPSIS
|
|
46
|
+
Return the directory names excluded from the file-size policy.
|
|
47
|
+
.DESCRIPTION
|
|
48
|
+
A path with any of these names as a directory segment is not covered. This
|
|
49
|
+
function is the single source of the excluded names: Test-FileSizeCoveredPath
|
|
50
|
+
and the repository gate traversal both read it. Each call returns a new array.
|
|
51
|
+
.OUTPUTS
|
|
52
|
+
System.String[].
|
|
53
|
+
#>
|
|
54
|
+
[CmdletBinding()]
|
|
55
|
+
[OutputType([string[]])]
|
|
56
|
+
param()
|
|
57
|
+
|
|
58
|
+
return [string[]]@('.git', 'node_modules', 'artifacts', 'coverage', 'dist', 'out', 'virtual', 'docs',
|
|
59
|
+
'build', '.venv', 'venv', 'env', '__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache', '.tox',
|
|
60
|
+
'.nox', 'htmlcov', '.vscode-test')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function Get-FileSizeLogicalLineCount {
|
|
64
|
+
<#
|
|
65
|
+
.SYNOPSIS
|
|
66
|
+
Count the logical lines of a text.
|
|
67
|
+
.DESCRIPTION
|
|
68
|
+
Splits the content on CRLF or LF. A trailing line terminator does not add a
|
|
69
|
+
line, and empty or null content has zero lines.
|
|
70
|
+
.PARAMETER Content
|
|
71
|
+
The file content.
|
|
72
|
+
.OUTPUTS
|
|
73
|
+
System.Int32.
|
|
74
|
+
#>
|
|
75
|
+
[CmdletBinding()]
|
|
76
|
+
[OutputType([int])]
|
|
77
|
+
param(
|
|
78
|
+
[Parameter(Mandatory)]
|
|
79
|
+
[AllowNull()]
|
|
80
|
+
[AllowEmptyString()]
|
|
81
|
+
[string] $Content
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if ([string]::IsNullOrEmpty($Content)) {
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
$count = [regex]::Split($Content, '\r?\n').Count
|
|
89
|
+
if ($Content.EndsWith("`n")) {
|
|
90
|
+
# The split yields one empty trailing element after a final terminator.
|
|
91
|
+
$count--
|
|
92
|
+
}
|
|
93
|
+
return $count
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function Get-FileSizeTier {
|
|
97
|
+
<#
|
|
98
|
+
.SYNOPSIS
|
|
99
|
+
Classify a logical line count into a policy tier.
|
|
100
|
+
.DESCRIPTION
|
|
101
|
+
Returns 'Below' (under 500), 'Guidance' (500 through 699), 'Advisory' (700
|
|
102
|
+
through 799), or 'HardLimit' (800 and above). The hard block applies only
|
|
103
|
+
above 800; a count of exactly 800 is in the HardLimit tier and still passes.
|
|
104
|
+
.PARAMETER LineCount
|
|
105
|
+
A non-negative logical line count.
|
|
106
|
+
.OUTPUTS
|
|
107
|
+
System.String.
|
|
108
|
+
#>
|
|
109
|
+
[CmdletBinding()]
|
|
110
|
+
[OutputType([string])]
|
|
111
|
+
param(
|
|
112
|
+
[Parameter(Mandatory)]
|
|
113
|
+
[int] $LineCount
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if ($LineCount -lt 0) {
|
|
117
|
+
throw "Get-FileSizeTier: LineCount must be non-negative; received $LineCount."
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
$limit = Get-FileSizePolicyLimit
|
|
121
|
+
if ($LineCount -ge $limit.HardLimit) { return 'HardLimit' }
|
|
122
|
+
if ($LineCount -ge $limit.Advisory) { return 'Advisory' }
|
|
123
|
+
if ($LineCount -ge $limit.Guidance) { return 'Guidance' }
|
|
124
|
+
return 'Below'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function ConvertTo-FileSizeRelativePath {
|
|
128
|
+
<#
|
|
129
|
+
.SYNOPSIS
|
|
130
|
+
Convert a file path to a repository-relative forward-slash path.
|
|
131
|
+
.DESCRIPTION
|
|
132
|
+
Backslashes are normalized to forward slashes. A relative path is returned
|
|
133
|
+
without a leading './'. An absolute path under the repository root is
|
|
134
|
+
returned relative to the root (compared case-insensitively, because the
|
|
135
|
+
root may come from a case-insensitive file system). A path outside the root,
|
|
136
|
+
a relative path that climbs above the root, and the root itself return
|
|
137
|
+
$null, which callers treat as not covered.
|
|
138
|
+
.PARAMETER Path
|
|
139
|
+
The file path from a tool payload or a discovery.
|
|
140
|
+
.PARAMETER RepositoryRoot
|
|
141
|
+
The repository root directory.
|
|
142
|
+
.OUTPUTS
|
|
143
|
+
System.String, or $null for a path outside the repository root.
|
|
144
|
+
#>
|
|
145
|
+
[CmdletBinding()]
|
|
146
|
+
[OutputType([string])]
|
|
147
|
+
param(
|
|
148
|
+
[Parameter(Mandatory)]
|
|
149
|
+
[AllowNull()]
|
|
150
|
+
[AllowEmptyString()]
|
|
151
|
+
[string] $Path,
|
|
152
|
+
|
|
153
|
+
[Parameter(Mandatory)]
|
|
154
|
+
[string] $RepositoryRoot
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if ([string]::IsNullOrWhiteSpace($Path)) {
|
|
158
|
+
return $null
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
$normalized = $Path.Replace('\', '/')
|
|
162
|
+
$isRooted = $normalized.StartsWith('/') -or ($normalized -match '^[A-Za-z]:/')
|
|
163
|
+
if (-not $isRooted) {
|
|
164
|
+
while ($normalized.StartsWith('./')) {
|
|
165
|
+
$normalized = $normalized.Substring(2)
|
|
166
|
+
}
|
|
167
|
+
if ($normalized -eq '..' -or $normalized.StartsWith('../') -or $normalized.Length -eq 0) {
|
|
168
|
+
return $null
|
|
169
|
+
}
|
|
170
|
+
return $normalized
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
$root = $RepositoryRoot.Replace('\', '/').TrimEnd('/')
|
|
174
|
+
$prefix = $root + '/'
|
|
175
|
+
if (-not $normalized.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
176
|
+
return $null
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
$relative = $normalized.Substring($prefix.Length)
|
|
180
|
+
if ($relative.Length -eq 0) {
|
|
181
|
+
return $null
|
|
182
|
+
}
|
|
183
|
+
return $relative
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function Test-FileSizeCoveredPath {
|
|
187
|
+
<#
|
|
188
|
+
.SYNOPSIS
|
|
189
|
+
Report whether a repository-relative path is covered by the file-size policy.
|
|
190
|
+
.DESCRIPTION
|
|
191
|
+
A path is covered when its extension (case-insensitive) is in the allowlist
|
|
192
|
+
(py, ps1, psm1, ts, tsx, js, cjs, mjs, cs, sh, and bats), no directory segment
|
|
193
|
+
is an excluded name, and it is not under the worktrees folder of .claude.
|
|
194
|
+
Excluded directory names are matched exactly. Null or empty input is not covered.
|
|
195
|
+
.PARAMETER RelativePath
|
|
196
|
+
A repository-relative path, for example from ConvertTo-FileSizeRelativePath.
|
|
197
|
+
.OUTPUTS
|
|
198
|
+
System.Boolean.
|
|
199
|
+
#>
|
|
200
|
+
[CmdletBinding()]
|
|
201
|
+
[OutputType([bool])]
|
|
202
|
+
param(
|
|
203
|
+
[Parameter(Mandatory)]
|
|
204
|
+
[AllowNull()]
|
|
205
|
+
[AllowEmptyString()]
|
|
206
|
+
[string] $RelativePath
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if ([string]::IsNullOrWhiteSpace($RelativePath)) {
|
|
210
|
+
return $false
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
$allowedExtension = @('.py', '.ps1', '.psm1', '.ts', '.tsx', '.js', '.cjs', '.mjs', '.cs', '.sh', '.bats')
|
|
214
|
+
$excludedSegment = @(Get-FileSizeExcludedSegment)
|
|
215
|
+
|
|
216
|
+
$normalized = $RelativePath.Replace('\', '/')
|
|
217
|
+
if ($normalized.StartsWith('.claude/worktrees/')) {
|
|
218
|
+
return $false
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
$segment = @($normalized.Split('/'))
|
|
222
|
+
for ($index = 0; $index -lt ($segment.Count - 1); $index++) {
|
|
223
|
+
if ($excludedSegment -ccontains $segment[$index]) {
|
|
224
|
+
return $false
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
$extension = [System.IO.Path]::GetExtension($segment[-1]).ToLowerInvariant()
|
|
229
|
+
return ($allowedExtension -contains $extension)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function Compare-FileSizeBaselineEntry {
|
|
233
|
+
<#
|
|
234
|
+
.SYNOPSIS
|
|
235
|
+
Compare one file's current size with its baseline entry under the strict ratchet.
|
|
236
|
+
.DESCRIPTION
|
|
237
|
+
Rows (S is the current size, B the recorded size):
|
|
238
|
+
S <= 800, no entry -> pass
|
|
239
|
+
S <= 800, entry -> 'stale-entry' (remove the entry)
|
|
240
|
+
S > 800, no entry -> 'new-over-limit'
|
|
241
|
+
S > 800, entry, S > B -> 'grew'
|
|
242
|
+
S > 800, entry, S == B -> pass
|
|
243
|
+
S > 800, entry, S < B -> 'tighten' (tighten the entry to S)
|
|
244
|
+
no file, entry -> 'missing-file' (stale entry for a deleted or renamed file)
|
|
245
|
+
no file, no entry -> pass
|
|
246
|
+
.PARAMETER Path
|
|
247
|
+
The repository-relative path, used in the violation message.
|
|
248
|
+
.PARAMETER CurrentSize
|
|
249
|
+
The current logical line count, or $null when no file exists on disk.
|
|
250
|
+
.PARAMETER BaselineSize
|
|
251
|
+
The recorded size, or $null when the baseline has no entry.
|
|
252
|
+
.OUTPUTS
|
|
253
|
+
$null on pass; otherwise a PSCustomObject with Path, Rule, and Message.
|
|
254
|
+
#>
|
|
255
|
+
[CmdletBinding()]
|
|
256
|
+
[OutputType([pscustomobject])]
|
|
257
|
+
param(
|
|
258
|
+
[Parameter(Mandatory)]
|
|
259
|
+
[string] $Path,
|
|
260
|
+
|
|
261
|
+
[AllowNull()]
|
|
262
|
+
[Nullable[int]] $CurrentSize,
|
|
263
|
+
|
|
264
|
+
[AllowNull()]
|
|
265
|
+
[Nullable[int]] $BaselineSize
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
$hardLimit = (Get-FileSizePolicyLimit).HardLimit
|
|
269
|
+
$rule = $null
|
|
270
|
+
$message = $null
|
|
271
|
+
|
|
272
|
+
if ($null -eq $CurrentSize) {
|
|
273
|
+
if ($null -ne $BaselineSize) {
|
|
274
|
+
$rule = 'missing-file'
|
|
275
|
+
$message = "stale baseline entry: '$Path' has no file on disk (deleted or renamed); remove the entry or move it with its recorded size unchanged."
|
|
276
|
+
}
|
|
277
|
+
} elseif ($CurrentSize -le $hardLimit) {
|
|
278
|
+
if ($null -ne $BaselineSize) {
|
|
279
|
+
$rule = 'stale-entry'
|
|
280
|
+
$message = "stale baseline entry: '$Path' has $CurrentSize lines, at or below the $hardLimit-line hard limit; remove the entry."
|
|
281
|
+
}
|
|
282
|
+
} elseif ($null -eq $BaselineSize) {
|
|
283
|
+
$rule = 'new-over-limit'
|
|
284
|
+
$message = "new file over the hard limit: '$Path' has $CurrentSize lines, above the $hardLimit-line hard limit; split the file."
|
|
285
|
+
} elseif ($CurrentSize -gt $BaselineSize) {
|
|
286
|
+
$rule = 'grew'
|
|
287
|
+
$message = "baselined file grew: '$Path' has $CurrentSize lines, above its recorded $BaselineSize; a baselined file may not grow."
|
|
288
|
+
} elseif ($CurrentSize -lt $BaselineSize) {
|
|
289
|
+
$rule = 'tighten'
|
|
290
|
+
$message = "baselined file shrank: '$Path' has $CurrentSize lines, below its recorded $BaselineSize; tighten the entry to $CurrentSize."
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if ($null -eq $rule) {
|
|
294
|
+
return $null
|
|
295
|
+
}
|
|
296
|
+
return [pscustomobject]@{ Path = $Path; Rule = $rule; Message = $message }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function Resolve-FileSizeEditContent {
|
|
300
|
+
<#
|
|
301
|
+
.SYNOPSIS
|
|
302
|
+
Apply an Edit-tool old_string to new_string replacement to pre-edit content in memory.
|
|
303
|
+
.DESCRIPTION
|
|
304
|
+
Follows the replacement approach of Resolve-EditedCheckpointContent in
|
|
305
|
+
.claude/hooks/enforce-completion-consistency.ps1: CRLF is normalized to LF in
|
|
306
|
+
all three strings, matching is ordinal (no regular expression, so '$' in the
|
|
307
|
+
new text stays literal), one occurrence is replaced unless ReplaceAll is true,
|
|
308
|
+
and a second match after the first is ambiguous. Failure is 'no-old-string'
|
|
309
|
+
(absent or empty old string), 'not-found', or 'ambiguous'.
|
|
310
|
+
.PARAMETER Content
|
|
311
|
+
The pre-edit file content.
|
|
312
|
+
.PARAMETER OldString
|
|
313
|
+
The text to replace.
|
|
314
|
+
.PARAMETER NewString
|
|
315
|
+
The replacement text; null is treated as empty.
|
|
316
|
+
.PARAMETER ReplaceAll
|
|
317
|
+
Replace every occurrence instead of exactly one.
|
|
318
|
+
.OUTPUTS
|
|
319
|
+
A PSCustomObject with Content (the edited text, or $null on failure) and
|
|
320
|
+
Failure (a failure code, or $null on success).
|
|
321
|
+
#>
|
|
322
|
+
[CmdletBinding()]
|
|
323
|
+
[OutputType([pscustomobject])]
|
|
324
|
+
param(
|
|
325
|
+
[Parameter(Mandatory)]
|
|
326
|
+
[AllowNull()]
|
|
327
|
+
[AllowEmptyString()]
|
|
328
|
+
[string] $Content,
|
|
329
|
+
|
|
330
|
+
[Parameter(Mandatory)]
|
|
331
|
+
[AllowNull()]
|
|
332
|
+
[AllowEmptyString()]
|
|
333
|
+
[string] $OldString,
|
|
334
|
+
|
|
335
|
+
[AllowNull()]
|
|
336
|
+
[AllowEmptyString()]
|
|
337
|
+
[string] $NewString = '',
|
|
338
|
+
|
|
339
|
+
[bool] $ReplaceAll = $false
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
if ([string]::IsNullOrEmpty($OldString)) {
|
|
343
|
+
return [pscustomobject]@{ Content = $null; Failure = 'no-old-string' }
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
$text = ([string]$Content).Replace("`r`n", "`n")
|
|
347
|
+
$old = $OldString.Replace("`r`n", "`n")
|
|
348
|
+
$new = ([string]$NewString).Replace("`r`n", "`n")
|
|
349
|
+
|
|
350
|
+
$first = $text.IndexOf($old, [System.StringComparison]::Ordinal)
|
|
351
|
+
if ($first -lt 0) {
|
|
352
|
+
return [pscustomobject]@{ Content = $null; Failure = 'not-found' }
|
|
353
|
+
}
|
|
354
|
+
if ($ReplaceAll) {
|
|
355
|
+
return [pscustomobject]@{ Content = $text.Replace($old, $new); Failure = $null }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
$second = $text.IndexOf($old, $first + $old.Length, [System.StringComparison]::Ordinal)
|
|
359
|
+
if ($second -ge 0) {
|
|
360
|
+
return [pscustomobject]@{ Content = $null; Failure = 'ambiguous' }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
$edited = $text.Substring(0, $first) + $new + $text.Substring($first + $old.Length)
|
|
364
|
+
return [pscustomobject]@{ Content = $edited; Failure = $null }
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function Get-FileSizeHookDecision {
|
|
368
|
+
<#
|
|
369
|
+
.SYNOPSIS
|
|
370
|
+
Decide whether a Write or Edit may leave a file at its resulting size.
|
|
371
|
+
.DESCRIPTION
|
|
372
|
+
Baseline-free pre-edit-size ratchet. A path that is not covered is allowed.
|
|
373
|
+
A covered result of at most 800 lines is allowed. A covered result above 800
|
|
374
|
+
lines is allowed only when the file already existed and the result is not
|
|
375
|
+
larger than its pre-edit size, so an already-large file may shrink or stay
|
|
376
|
+
the same size but may not grow; every other case is denied. The deny reason
|
|
377
|
+
starts with 'FILE_SIZE_LIMIT_BLOCKED:' and names the path, the resulting
|
|
378
|
+
count, the three tiers, and the split requirement.
|
|
379
|
+
.PARAMETER RelativePath
|
|
380
|
+
The repository-relative path; $null or empty (outside the repository) is not covered.
|
|
381
|
+
.PARAMETER ResultLineCount
|
|
382
|
+
The logical line count the file would have after the operation.
|
|
383
|
+
.PARAMETER PreEditLineCount
|
|
384
|
+
The logical line count before the operation, or $null for a new file.
|
|
385
|
+
.OUTPUTS
|
|
386
|
+
A PSCustomObject with Allowed (bool) and Reason ($null when allowed).
|
|
387
|
+
#>
|
|
388
|
+
[CmdletBinding()]
|
|
389
|
+
[OutputType([pscustomobject])]
|
|
390
|
+
param(
|
|
391
|
+
[Parameter(Mandatory)]
|
|
392
|
+
[AllowNull()]
|
|
393
|
+
[AllowEmptyString()]
|
|
394
|
+
[string] $RelativePath,
|
|
395
|
+
|
|
396
|
+
[Parameter(Mandatory)]
|
|
397
|
+
[int] $ResultLineCount,
|
|
398
|
+
|
|
399
|
+
[AllowNull()]
|
|
400
|
+
[Nullable[int]] $PreEditLineCount
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
$limit = Get-FileSizePolicyLimit
|
|
404
|
+
$isAllowed = (-not (Test-FileSizeCoveredPath -RelativePath $RelativePath)) -or
|
|
405
|
+
($ResultLineCount -le $limit.HardLimit) -or
|
|
406
|
+
(($null -ne $PreEditLineCount) -and ($ResultLineCount -le $PreEditLineCount))
|
|
407
|
+
if ($isAllowed) {
|
|
408
|
+
return [pscustomobject]@{ Allowed = $true; Reason = $null }
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
$growth = ''
|
|
412
|
+
if ($null -ne $PreEditLineCount) {
|
|
413
|
+
$growth = " It has $PreEditLineCount lines now; a file already above the hard limit may shrink or stay the same size but may not grow."
|
|
414
|
+
}
|
|
415
|
+
$reason = "FILE_SIZE_LIMIT_BLOCKED: '$RelativePath' would have $ResultLineCount logical lines, above the " +
|
|
416
|
+
"$($limit.HardLimit)-line hard limit.$growth File-size policy tiers: $($limit.Guidance) lines is guidance " +
|
|
417
|
+
"(look for a logical split), $($limit.Advisory) lines is advisory (split more aggressively; reviewers report " +
|
|
418
|
+
"a non-blocking Warning), and above $($limit.HardLimit) lines a split is enforced regardless of logical " +
|
|
419
|
+
"separability. Split the file into smaller files so each stays at or below $($limit.HardLimit) lines."
|
|
420
|
+
return [pscustomobject]@{ Allowed = $false; Reason = $reason }
|
|
421
|
+
}
|