@danmoisan/drm-copilot-mcp 1.1.21 → 1.1.23

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.
Files changed (91) hide show
  1. package/out/mcp-server.js +2568 -1261
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +1 -1
  4. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +4 -4
  5. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +1 -1
  6. package/resources/claude-customizations/.claude/agents/feature-review.md +8 -0
  7. package/resources/claude-customizations/.claude/agents/orchestrator.md +1 -1
  8. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +46 -6
  9. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
  10. package/resources/claude-customizations/.claude/agents/pr-author.md +32 -7
  11. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +1 -1
  12. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +4 -3
  13. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +2 -1
  14. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-green-ci.ps1 +325 -0
  15. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +22 -6
  16. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +23 -10
  17. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +29 -21
  18. package/resources/claude-customizations/.claude/hooks/enforce-file-size-limit.ps1 +244 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +48 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +22 -11
  21. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +2 -1
  22. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +8 -2
  23. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +15 -10
  24. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.current-branch.ps1 +149 -0
  25. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +2 -1
  26. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-feature-folder.ps1 +146 -0
  27. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +25 -3
  28. package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +33 -6
  29. package/resources/claude-customizations/.claude/lib/file-size-policy/FileSizePolicy.psm1 +434 -0
  30. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateFeatureFolderRecord.psm1 +166 -0
  31. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +8 -3
  32. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunRecordPath.psm1 +185 -0
  33. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +11 -21
  34. package/resources/claude-customizations/.claude/rules/general-code-change.md +4 -1
  35. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +28 -5
  36. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +2 -2
  37. package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
  38. package/resources/claude-customizations/.claude/rules/shell.md +1 -1
  39. package/resources/claude-customizations/.claude/settings.json +4 -0
  40. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +2 -2
  41. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +2 -1
  42. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -0
  43. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +11 -4
  44. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +4 -1
  45. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +90 -14
  46. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +3 -1
  47. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +39 -9
  48. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +1 -1
  49. package/resources/claude-customizations/pack-manifests/core.json +14 -0
  50. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +2 -1
  51. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  52. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +8 -0
  53. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +4 -1
  54. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +1 -1
  55. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +1 -1
  56. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +1 -1
  57. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +1 -1
  58. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +1 -1
  59. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +1 -1
  60. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +1 -1
  61. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +8 -0
  62. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +1 -1
  63. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +1 -1
  64. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +1 -1
  65. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +1 -1
  66. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +1 -1
  67. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
  68. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +1 -1
  69. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +1 -1
  70. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +1 -1
  71. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +1 -1
  72. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +1 -1
  73. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +1 -1
  74. package/resources/codex-and-agents-customizations/.codex/config.toml +7 -1
  75. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-file-size-limit.ps1 +282 -0
  76. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +2 -1
  77. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +2 -1
  78. package/resources/codex-and-agents-customizations/.codex/hooks/file-size-policy.ps1 +421 -0
  79. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +1 -1
  80. package/resources/codex-and-agents-customizations/AGENTS.md +4 -1
  81. package/resources/codex-and-agents-customizations/pack-manifests/core.json +3 -0
  82. package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
  83. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +1 -1
  84. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +1 -1
  85. package/resources/customizations/.github/instructions/general-code-change.instructions.md +5 -2
  86. package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +1 -1
  87. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +1 -1
  88. package/resources/customizations/.github/skills/execution-environment/SKILL.md +2 -1
  89. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +8 -0
  90. package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +4 -4
  91. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +4 -4
@@ -87,7 +87,7 @@ If the marker is missing or malformed, fail closed to `full-feature`.
87
87
  Stop implementation and return to the user when:
88
88
 
89
89
  - the scope estimate exceeds 3 production files in direct mode,
90
- - a file is near or would exceed the 500-line limit,
90
+ - a file is above or would exceed the 800-line hard limit of the tiered file-size policy (split it before proceeding; at 500 lines look for a logical split, and at 700 lines split more aggressively),
91
91
  - any QA gate delta is non-zero after self-correction,
92
92
  - the toolchain cannot be executed in the current environment (mark the change **unverified**),
93
93
  - policy instructions conflict.
@@ -87,7 +87,7 @@ If the marker is missing or malformed, fail closed to `full-feature`.
87
87
  Stop implementation and return to the user when:
88
88
 
89
89
  - the scope estimate exceeds 3 production files in direct mode,
90
- - a file is near or would exceed the 500-line limit,
90
+ - a file is above or would exceed the 800-line hard limit of the tiered file-size policy (split it before proceeding; at 500 lines look for a logical split, and at 700 lines split more aggressively),
91
91
  - any QA gate delta is non-zero after self-correction,
92
92
  - the toolchain cannot be executed in the current environment (mark the change **unverified**),
93
93
  - policy instructions conflict.
@@ -87,7 +87,7 @@ If the marker is missing or malformed, fail closed to `full-feature`.
87
87
  Stop implementation and return to the user when:
88
88
 
89
89
  - the scope estimate exceeds 3 production files in direct mode,
90
- - a file is near or would exceed the 500-line limit,
90
+ - a file is above or would exceed the 800-line hard limit of the tiered file-size policy (split it before proceeding; at 500 lines look for a logical split, and at 700 lines split more aggressively),
91
91
  - any QA gate delta is non-zero after self-correction,
92
92
  - the toolchain cannot be executed in the current environment (mark the change **unverified**),
93
93
  - policy instructions conflict.
@@ -2,7 +2,7 @@ default_permissions = ":danger-full-access"
2
2
 
3
3
  [mcp_servers.drm-copilot]
4
4
  command = "npx"
5
- args = ["-y", "@danmoisan/drm-copilot-mcp@1.1.21"]
5
+ args = ["-y", "@danmoisan/drm-copilot-mcp@1.1.23"]
6
6
  required = true
7
7
  enabled_tools = [
8
8
  "collect_commit_context",
@@ -215,6 +215,12 @@ command = 'pwsh -NoProfile -File "$(git rev-parse --show-toplevel)/.codex/hooks/
215
215
  command_windows = 'pwsh -NoProfile -File "$(git rev-parse --show-toplevel)/.codex/hooks/enforce-evidence-locations.ps1"'
216
216
  timeout = 30
217
217
 
218
+ [[hooks.PreToolUse.hooks]]
219
+ type = "command"
220
+ command = 'pwsh -NoProfile -File "$(git rev-parse --show-toplevel)/.codex/hooks/enforce-file-size-limit.ps1"'
221
+ command_windows = 'pwsh -NoProfile -File "$(git rev-parse --show-toplevel)/.codex/hooks/enforce-file-size-limit.ps1"'
222
+ timeout = 30
223
+
218
224
  [[hooks.PreToolUse.hooks]]
219
225
  type = "command"
220
226
  command = 'pwsh -NoProfile -File "$(git rev-parse --show-toplevel)/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1"'
@@ -0,0 +1,282 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Pre-tool-use hook that blocks a Write, Edit, or apply_patch leaving a covered file above 800 lines.
4
+
5
+ .DESCRIPTION
6
+ This script is invoked by the Codex PreToolUse hook under the ^(apply_patch|Edit|Write)$
7
+ matcher. It enforces the hard tier of the tiered file-size policy: 500 lines is
8
+ guidance, 700 lines is advisory, and above 800 lines a split is enforced regardless of
9
+ logical separability. The policy functions are the dot-sourced Codex twin
10
+ file-size-policy.ps1, which is held text-identical to the Claude module.
11
+
12
+ The stdin payload is parsed by ConvertFrom-CodexPreToolUsePayload and mapped to
13
+ file-edit records by ConvertTo-CodexFileEditInput. Per record:
14
+ - A path that is not covered, and a Delete, allow with no file read.
15
+ - Write and apply_patch Add use the record content.
16
+ - Edit resolves old_string to new_string in memory against the pre-edit file read
17
+ through the ReadFileContent seam; a missing or unreadable file, or an edit that
18
+ cannot be resolved, denies.
19
+ - apply_patch Update is reconstructed by a second ConvertTo-CodexFileEditInput call
20
+ with -ResolveUpdateContent and -GovernedPath set to the patched path; empty
21
+ reconstructed content denies.
22
+ - A rename evaluates the destination, and the source supplies the pre-edit size.
23
+ A result above 800 lines is denied unless the file already existed and does not grow.
24
+
25
+ Allowed payloads produce NO stdout and exit 0. A deny writes the PreToolUse JSON
26
+ envelope to stdout and exits 0. Empty, malformed, or tool_input-less stdin writes the
27
+ reason to stderr and exits 2.
28
+
29
+ .NOTES
30
+ Compatible with PowerShell 7+.
31
+ This script must not modify any state; it is a read-only validation gate.
32
+ #>
33
+ [CmdletBinding()]
34
+ param()
35
+ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot 'hook-dependency-guard.ps1') } catch { $script:HookDependencyGuardLoadFailed = $true }
36
+
37
+ # Shared Codex PreToolUse transport: stdin payload parsing and tool_input-to-file
38
+ # mapping for every tool name the ^(apply_patch|Edit|Write)$ matcher admits.
39
+ try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
40
+ try { . (Join-Path $PSScriptRoot 'file-size-policy.ps1') } catch { Add-HookDependencyFailure -Name 'file-size-policy.ps1' -ErrorRecord $_ }
41
+
42
+ function Get-FileSizeLimitBlockDecision {
43
+ <#
44
+ .SYNOPSIS
45
+ Constructs a deny-decision ordered dictionary carrying the supplied reason.
46
+ .PARAMETER Reason
47
+ The deny reason; it starts with FILE_SIZE_LIMIT_BLOCKED:.
48
+ #>
49
+ [CmdletBinding()]
50
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
51
+ param(
52
+ [Parameter(Mandatory)]
53
+ [string] $Reason
54
+ )
55
+
56
+ return [ordered]@{
57
+ hookSpecificOutput = [ordered]@{
58
+ hookEventName = 'PreToolUse'
59
+ permissionDecision = 'deny'
60
+ permissionDecisionReason = $Reason
61
+ }
62
+ }
63
+ }
64
+
65
+ function Get-CodexFileSizeRecordValue {
66
+ <#
67
+ .SYNOPSIS
68
+ Returns a named property of a file-edit record, or $null when it is absent.
69
+ #>
70
+ [CmdletBinding()]
71
+ [OutputType([object])]
72
+ param(
73
+ [Parameter(Mandatory)]
74
+ [AllowNull()]
75
+ $Record,
76
+
77
+ [Parameter(Mandatory)]
78
+ [string] $Name
79
+ )
80
+
81
+ if ($null -eq $Record -or $Record.PSObject.Properties.Name -notcontains $Name) {
82
+ return $null
83
+ }
84
+ return $Record.$Name
85
+ }
86
+
87
+ function Get-CodexFileSizeReplaceAllFlag {
88
+ <#
89
+ .SYNOPSIS
90
+ Returns the boolean value of a record's replace_all property.
91
+ .DESCRIPTION
92
+ A boolean is returned as is; a string is true only when its trimmed value is
93
+ 'true' (case-insensitive); every other value is false.
94
+ #>
95
+ [CmdletBinding()]
96
+ [OutputType([bool])]
97
+ param(
98
+ [Parameter(Mandatory)]
99
+ [AllowNull()]
100
+ $Record
101
+ )
102
+
103
+ $value = Get-CodexFileSizeRecordValue -Record $Record -Name 'replace_all'
104
+ if ($value -is [bool]) {
105
+ return $value
106
+ }
107
+ if ($value -is [string]) {
108
+ return ($value.Trim() -ieq 'true')
109
+ }
110
+ return $false
111
+ }
112
+
113
+ function Get-CodexFileSizeUpdatedContent {
114
+ <#
115
+ .SYNOPSIS
116
+ Reconstructs the post-patch content of an apply_patch Update for one patched path.
117
+ .OUTPUTS
118
+ The reconstructed content, or an empty string when it cannot be reconstructed.
119
+ #>
120
+ [CmdletBinding()]
121
+ [OutputType([string])]
122
+ param(
123
+ [Parameter(Mandatory)]
124
+ $Payload,
125
+
126
+ [Parameter(Mandatory)]
127
+ [string] $SourcePath
128
+ )
129
+
130
+ $resolved = @(
131
+ ConvertTo-CodexFileEditInput -Payload $Payload -ResolveUpdateContent -GovernedPath $SourcePath |
132
+ Where-Object { $_.operation -eq 'Update' -and $_.source_path -eq $SourcePath }
133
+ )
134
+ if ($resolved.Count -eq 0) {
135
+ return ''
136
+ }
137
+ return [string]$resolved[0].content
138
+ }
139
+
140
+ function Invoke-CodexFileSizeLimitDecision {
141
+ <#
142
+ .SYNOPSIS
143
+ Returns the deny decision for one file-edit record, or $null when it is allowed.
144
+ .PARAMETER Record
145
+ One record from ConvertTo-CodexFileEditInput.
146
+ .PARAMETER Payload
147
+ The parsed payload, used to reconstruct an apply_patch Update.
148
+ .PARAMETER RepositoryRoot
149
+ The repository root used to resolve relative paths. Defaults to the current location.
150
+ .PARAMETER ReadFileContent
151
+ Seam that returns the text of a file, $null when the file does not exist, and
152
+ throws when it cannot be read. Tests supply in-memory content.
153
+ #>
154
+ [CmdletBinding()]
155
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
156
+ param(
157
+ [Parameter(Mandatory)]
158
+ $Record,
159
+
160
+ [Parameter(Mandatory)]
161
+ $Payload,
162
+
163
+ [string] $RepositoryRoot = (Get-Location).ProviderPath,
164
+
165
+ [scriptblock] $ReadFileContent = {
166
+ param([string] $Path)
167
+ if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }
168
+ return [System.IO.File]::ReadAllText($Path)
169
+ }
170
+ )
171
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'FILE_SIZE_LIMIT_BLOCKED:'
172
+ if ($null -ne $dependencyDecision) { return $dependencyDecision }
173
+
174
+ $operation = [string](Get-CodexFileSizeRecordValue -Record $Record -Name 'operation')
175
+ $sourcePath = [string](Get-CodexFileSizeRecordValue -Record $Record -Name 'source_path')
176
+ $relativePath = ConvertTo-FileSizeRelativePath -Path ([string]$Record.file_path) -RepositoryRoot $RepositoryRoot
177
+ if ($operation -eq 'Delete' -or -not (Test-FileSizeCoveredPath -RelativePath $relativePath)) {
178
+ return $null
179
+ }
180
+
181
+ # The pre-edit size comes from the source path, which differs from the target only for a rename.
182
+ $sourceRelative = ConvertTo-FileSizeRelativePath -Path $sourcePath -RepositoryRoot $RepositoryRoot
183
+ $isRooted = ($sourcePath -match '^[\\/]') -or ($sourcePath -match '^[A-Za-z]:[\\/]')
184
+ $readPath = if ($isRooted -or $null -eq $sourceRelative) { $sourcePath } else { Join-Path $RepositoryRoot $sourceRelative }
185
+ $hardLimit = (Get-FileSizePolicyLimit).HardLimit
186
+
187
+ $resultCount = 0
188
+ if ($operation -eq 'Update') {
189
+ $updated = Get-CodexFileSizeUpdatedContent -Payload $Payload -SourcePath $sourcePath
190
+ if ([string]::IsNullOrEmpty($updated)) {
191
+ return Get-FileSizeLimitBlockDecision -Reason "FILE_SIZE_LIMIT_BLOCKED: the apply_patch Update to '$relativePath' could not be reconstructed to measure its size. The gate fails closed for a covered file."
192
+ }
193
+ $resultCount = Get-FileSizeLogicalLineCount -Content $updated
194
+ } elseif ($operation -ne 'Edit') {
195
+ $resultCount = Get-FileSizeLogicalLineCount -Content ([string](Get-CodexFileSizeRecordValue -Record $Record -Name 'content'))
196
+ }
197
+ if ($operation -ne 'Edit' -and $resultCount -le $hardLimit) {
198
+ return $null
199
+ }
200
+
201
+ try {
202
+ $preEditContent = & $ReadFileContent $readPath
203
+ } catch {
204
+ return Get-FileSizeLimitBlockDecision -Reason "FILE_SIZE_LIMIT_BLOCKED: '$relativePath' could not be read to measure its size ($($_.Exception.Message)). The gate fails closed for a covered file."
205
+ }
206
+ $preEditCount = $null
207
+ if ($null -ne $preEditContent) {
208
+ $preEditCount = Get-FileSizeLogicalLineCount -Content ([string]$preEditContent)
209
+ }
210
+
211
+ if ($operation -eq 'Edit') {
212
+ if ($null -eq $preEditContent) {
213
+ return Get-FileSizeLimitBlockDecision -Reason "FILE_SIZE_LIMIT_BLOCKED: '$relativePath' does not exist, so the Edit cannot be measured. The gate fails closed for a covered file."
214
+ }
215
+ $edit = Resolve-FileSizeEditContent -Content ([string]$preEditContent) `
216
+ -OldString ([string](Get-CodexFileSizeRecordValue -Record $Record -Name 'old_string')) `
217
+ -NewString ([string](Get-CodexFileSizeRecordValue -Record $Record -Name 'new_string')) `
218
+ -ReplaceAll (Get-CodexFileSizeReplaceAllFlag -Record $Record)
219
+ if ($null -ne $edit.Failure) {
220
+ return Get-FileSizeLimitBlockDecision -Reason "FILE_SIZE_LIMIT_BLOCKED: the Edit to '$relativePath' could not be resolved in memory ($($edit.Failure)). The gate fails closed for a covered file."
221
+ }
222
+ $resultCount = Get-FileSizeLogicalLineCount -Content $edit.Content
223
+ }
224
+
225
+ $decision = Get-FileSizeHookDecision -RelativePath $relativePath -ResultLineCount $resultCount -PreEditLineCount $preEditCount
226
+ if ($decision.Allowed) {
227
+ return $null
228
+ }
229
+ return Get-FileSizeLimitBlockDecision -Reason $decision.Reason
230
+ }
231
+
232
+ function Invoke-CodexFileSizeLimitEntryPoint {
233
+ <#
234
+ .SYNOPSIS
235
+ Reads one Codex PreToolUse payload and returns the process exit code.
236
+ .PARAMETER PayloadRaw
237
+ The raw stdin text. AllowEmptyString is required so empty input reaches the shared
238
+ parser and fails closed with exit 2.
239
+ .PARAMETER RepositoryRoot
240
+ The repository root passed to the per-record decision.
241
+ .PARAMETER ReadFileContent
242
+ Optional file-reader seam passed to the per-record decision.
243
+ #>
244
+ [CmdletBinding()]
245
+ [OutputType([int])]
246
+ param(
247
+ [Parameter(Mandatory)]
248
+ [AllowEmptyString()]
249
+ [string] $PayloadRaw,
250
+
251
+ [string] $RepositoryRoot = (Get-Location).ProviderPath,
252
+
253
+ [scriptblock] $ReadFileContent
254
+ )
255
+
256
+ try {
257
+ $payload = ConvertFrom-CodexPreToolUsePayload -PayloadRaw $PayloadRaw -HookName 'enforce-file-size-limit'
258
+ foreach ($record in @(ConvertTo-CodexFileEditInput -Payload $payload)) {
259
+ $decisionArgs = @{ Record = $record; Payload = $payload; RepositoryRoot = $RepositoryRoot }
260
+ if ($PSBoundParameters.ContainsKey('ReadFileContent')) { $decisionArgs['ReadFileContent'] = $ReadFileContent }
261
+ $decision = Invoke-CodexFileSizeLimitDecision @decisionArgs
262
+ if ($null -ne $decision) {
263
+ [Console]::Out.WriteLine(($decision | ConvertTo-Json -Compress -Depth 5))
264
+ return 0
265
+ }
266
+ }
267
+ return 0
268
+ } catch {
269
+ [Console]::Error.WriteLine([string]$_)
270
+ return 2
271
+ }
272
+ }
273
+
274
+ # Guard allows dot-sourcing in tests without executing the entrypoint.
275
+ if ($MyInvocation.InvocationName -eq '.') {
276
+ return
277
+ }
278
+ if ($script:HookDependencyGuardLoadFailed) { [Console]::Error.WriteLine('FILE_SIZE_LIMIT_BLOCKED: hook-dependency-guard.ps1 failed to load; the gate fails closed.'); exit 2 }
279
+ $dependencyDecision = Get-HookDependencyFailureDecision -HookEvent PreToolUse -ReasonPrefix 'FILE_SIZE_LIMIT_BLOCKED:'
280
+ if ($null -ne $dependencyDecision) { $dependencyDecision | ConvertTo-Json -Compress -Depth 5 | Write-Output; exit 0 }
281
+
282
+ exit (Invoke-CodexFileSizeLimitEntryPoint -PayloadRaw ([Console]::In.ReadToEnd()))
@@ -52,7 +52,7 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
52
52
  try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
53
53
 
54
54
  # Shared route helpers; this file is byte-identical to the Claude runtime copy.
55
- try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-batch-budget-route.ps1' -ErrorRecord $_ }
55
+ $script:PowerShellBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PowerShellBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
56
56
 
57
57
  function Get-PowerShellBatchBudgetState {
58
58
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'TestCap', Justification = 'Accepted and ignored for callers written against the removed test-file cap.')]
@@ -216,6 +216,7 @@ function Invoke-PowerShellBatchBudgetHook {
216
216
  if ($normalized -notmatch '\.(ps1|psm1|psd1)$') {
217
217
  return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
218
218
  }
219
+ 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." }
219
220
 
220
221
  $stateDir = Join-Path -Path $Root -ChildPath '.codex/state'
221
222
  $stateFile = Join-Path -Path $stateDir -ChildPath ("powershell-batch-budget.$SessionId.json")
@@ -52,7 +52,7 @@ $script:HookDependencyGuardLoadFailed = $false; try { . (Join-Path $PSScriptRoot
52
52
  try { . (Join-Path $PSScriptRoot 'codex-pretooluse-file-mapping.ps1') } catch { Add-HookDependencyFailure -Name 'codex-pretooluse-file-mapping.ps1' -ErrorRecord $_ }
53
53
 
54
54
  # Shared route helpers; this file is byte-identical to the Claude runtime copy.
55
- try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { Add-HookDependencyFailure -Name 'enforce-batch-budget-route.ps1' -ErrorRecord $_ }
55
+ $script:PythonBatchBudgetRouteImportFailure = $null; try { . (Join-Path $PSScriptRoot 'enforce-batch-budget-route.ps1') } catch { $script:PythonBatchBudgetRouteImportFailure = 'enforce-batch-budget-route.ps1' }
56
56
 
57
57
  function Get-PythonBatchBudgetState {
58
58
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'TestCap', Justification = 'Accepted and ignored for callers written against the removed test-file cap.')]
@@ -219,6 +219,7 @@ function Invoke-PythonBatchBudgetHook {
219
219
  if ($normalized -notmatch '\.py$') {
220
220
  return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
221
221
  }
222
+ 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." }
222
223
 
223
224
  $stateDir = Join-Path -Path $Root -ChildPath '.codex/state'
224
225
  $stateFile = Join-Path -Path $stateDir -ChildPath ("python-batch-budget.$SessionId.json")