@danmoisan/drm-copilot-mcp 1.1.10 → 1.1.12
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 +3517 -1104
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/epic-planner.md +4 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +15 -2
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +3 -0
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-authorization.ps1 +443 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +82 -51
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +52 -3
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +120 -5
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +127 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +21 -28
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +103 -6
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +124 -8
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +171 -15
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +50 -10
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +3 -1
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +332 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +211 -182
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +35 -7
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +483 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +483 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +218 -6
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +18 -75
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusConflict.psm1 +290 -0
- package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +415 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMerge.psm1 +355 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/ProjectFileMergeGrammar.psm1 +318 -0
- package/resources/claude-customizations/.claude/lib/project-file-merge/Resolve-MergeableConflict.ps1 +229 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeReadiness.psm1 +176 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1 +369 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +392 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeResolution.psm1 +500 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeTargetResolution.psm1 +347 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +27 -0
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +66 -2
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +323 -16
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +14 -1
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +4 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +46 -1
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +75 -8
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/powershell-orchestration-state-machine/SKILL.md +29 -1
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +221 -0
- package/resources/claude-customizations/config/blast-radius.json +7 -0
- package/resources/claude-customizations/pack-manifests/core.json +16 -0
- package/resources/codex-and-agents-customizations/.agents/skills/codex-model-routing/SKILL.md +13 -14
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +41 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +41 -0
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +26 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +242 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +67 -12
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +34 -8
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +163 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +34 -7
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +483 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +483 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +130 -2
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexDeployment.ps1 +87 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +96 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/codex-routing-cli-common.ps1 +370 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-contract.ps1 +475 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-launch-runtime.ps1 +445 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-persistence-runtime.ps1 +118 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/epic-child-sandbox-preflight.ps1 +69 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/launch-epic-child-wave.ps1 +471 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +280 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/resume-epic-child.ps1 +263 -0
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +2 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +23 -1
- package/resources/config/orchestration-handoff-registry.json +138 -0
- package/resources/config/orchestration-handoff.schema.json +472 -0
- package/resources/lib/codex-routing/CodexDeployment.psm1 +315 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +394 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +64 -0
package/resources/claude-customizations/.claude/lib/worktree-resolution/EpicScopeResolution.psm1
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Epic-scope resolution: decides whether a gated call is an epic-level operation (issue #663).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
An epic run keeps its state in artifacts/orchestration/epic-orchestrator-state.json at
|
|
7
|
+
the coordinating worktree root, not in a per-feature checkpoint. This module decides,
|
|
8
|
+
for one gated call, whether the call is an operation of that epic and, if so, returns
|
|
9
|
+
the absolute path and parsed content of the governing epic checkpoint.
|
|
10
|
+
|
|
11
|
+
A call is epic scope only when the session root's epic checkpoint parses, carries
|
|
12
|
+
route_id "epic" and a non-empty integration_branch, and that branch equals either the
|
|
13
|
+
call's branch signal (--head, --branch, or a branch: label) or, for a command or path
|
|
14
|
+
leg, the HEAD branch of the effective worktree (the -C selector worktree when present,
|
|
15
|
+
otherwise the session root). A command or path leg ignores any text branch signal, so
|
|
16
|
+
its merge probe inspects the worktree it operates on. Anything else is not epic scope,
|
|
17
|
+
so the caller's existing per-feature resolution runs unchanged.
|
|
18
|
+
|
|
19
|
+
The checkpoint path is always composed from the resolved session root and is never
|
|
20
|
+
read from command or prompt text (the issue #554 posture and the issue #673
|
|
21
|
+
invariant). Exactly one checkpoint read happens per resolution.
|
|
22
|
+
|
|
23
|
+
.NOTES
|
|
24
|
+
PowerShell 7+. Every filesystem contact passes through the exported seams of the
|
|
25
|
+
sibling WorktreeResolution.psm1, so tests model any topology by mocking those seams
|
|
26
|
+
inside this module and create no file. No subprocess, no network, no clock read, and
|
|
27
|
+
no environment read. Mirrored byte-identically under
|
|
28
|
+
extensions/drm-copilot/resources/claude-customizations/.
|
|
29
|
+
AUTHORITY: PowerShell-authoritative. The epic readiness predicates have no Python reference implementation, and no enforcement hook invokes Python (issue #663, D5).
|
|
30
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
31
|
+
#>
|
|
32
|
+
|
|
33
|
+
Set-StrictMode -Version Latest
|
|
34
|
+
$ErrorActionPreference = 'Stop'
|
|
35
|
+
|
|
36
|
+
Import-Module (Join-Path $PSScriptRoot 'WorktreeResolution.psm1') -ErrorAction Stop
|
|
37
|
+
Import-Module (Join-Path $PSScriptRoot 'WorktreeTargetResolution.psm1') -ErrorAction Stop
|
|
38
|
+
|
|
39
|
+
# The one definition of the epic checkpoint location relative to a worktree root.
|
|
40
|
+
$script:EpicCheckpointRelativePath = 'artifacts/orchestration/epic-orchestrator-state.json'
|
|
41
|
+
|
|
42
|
+
# The only route id that makes an epic checkpoint scope-defining. Compared case-sensitively.
|
|
43
|
+
$script:EpicRouteId = 'epic'
|
|
44
|
+
|
|
45
|
+
# Git layout names read from a worktree: the root marker, the linked-worktree pointer
|
|
46
|
+
# line, the HEAD file, the branch reference prefix, and the in-progress merge marker.
|
|
47
|
+
$script:GitEntryName = '.git'
|
|
48
|
+
$script:GitDirLinePattern = '^gitdir:\s*(?<target>.+)$'
|
|
49
|
+
$script:HeadFileName = 'HEAD'
|
|
50
|
+
$script:BranchReferencePrefix = 'ref: refs/heads/'
|
|
51
|
+
$script:MergeHeadFileName = 'MERGE_HEAD'
|
|
52
|
+
|
|
53
|
+
# A drive-rooted or slash-rooted normalised path.
|
|
54
|
+
$script:AbsolutePathPattern = '^([A-Za-z]:(/|$)|/)'
|
|
55
|
+
|
|
56
|
+
function Get-EpicScopePropertyValue {
|
|
57
|
+
# Pure: the named property of an object, or $null when the object or the property
|
|
58
|
+
# is absent. StrictMode forbids reading a missing property directly.
|
|
59
|
+
[OutputType([object])]
|
|
60
|
+
param([AllowNull()][object] $InputObject, [string] $Name)
|
|
61
|
+
|
|
62
|
+
if ($null -eq $InputObject) {
|
|
63
|
+
return $null
|
|
64
|
+
}
|
|
65
|
+
$property = $InputObject.PSObject.Properties[$Name]
|
|
66
|
+
if ($null -eq $property) {
|
|
67
|
+
return $null
|
|
68
|
+
}
|
|
69
|
+
return $property.Value
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function Get-EpicScopeFirstLine {
|
|
73
|
+
# Pure: the trimmed first line of a text, or $null when there is none.
|
|
74
|
+
[OutputType([string])]
|
|
75
|
+
param([AllowNull()][string] $Text)
|
|
76
|
+
|
|
77
|
+
if ([string]::IsNullOrWhiteSpace($Text)) {
|
|
78
|
+
return $null
|
|
79
|
+
}
|
|
80
|
+
return ($Text -split "`r?`n", 2)[0].Trim()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function New-EpicScopeResult {
|
|
84
|
+
# Pure: the single constructor of the resolution result shape.
|
|
85
|
+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Pure in-memory factory that changes no system state.')]
|
|
86
|
+
[OutputType([pscustomobject])]
|
|
87
|
+
param(
|
|
88
|
+
[bool] $IsEpicScope,
|
|
89
|
+
[AllowNull()][string] $CheckpointPath,
|
|
90
|
+
[AllowNull()][object] $Checkpoint,
|
|
91
|
+
[AllowNull()][string] $WorktreeRoot,
|
|
92
|
+
[AllowNull()][string] $Branch,
|
|
93
|
+
[bool] $MergeInProgress,
|
|
94
|
+
[string] $Reason
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return [pscustomobject]@{
|
|
98
|
+
IsEpicScope = $IsEpicScope
|
|
99
|
+
CheckpointPath = $CheckpointPath
|
|
100
|
+
Checkpoint = $Checkpoint
|
|
101
|
+
WorktreeRoot = $WorktreeRoot
|
|
102
|
+
Branch = $Branch
|
|
103
|
+
MergeInProgress = $MergeInProgress
|
|
104
|
+
Reason = $Reason
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function Get-EpicScopeCheckpointRelativePath {
|
|
109
|
+
<#
|
|
110
|
+
.SYNOPSIS
|
|
111
|
+
Return the repository-relative path of the epic checkpoint.
|
|
112
|
+
#>
|
|
113
|
+
[CmdletBinding()]
|
|
114
|
+
[OutputType([string])]
|
|
115
|
+
param()
|
|
116
|
+
|
|
117
|
+
return $script:EpicCheckpointRelativePath
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function Get-EpicScopeCheckpointText {
|
|
121
|
+
<#
|
|
122
|
+
.SYNOPSIS
|
|
123
|
+
Read the epic checkpoint text, or $null when no such file exists (seam).
|
|
124
|
+
.PARAMETER Path
|
|
125
|
+
The absolute path of the epic checkpoint.
|
|
126
|
+
#>
|
|
127
|
+
[CmdletBinding()]
|
|
128
|
+
[OutputType([string])]
|
|
129
|
+
param(
|
|
130
|
+
[Parameter(Mandatory = $true)]
|
|
131
|
+
[string] $Path
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if ((Get-WorktreeResolutionGitEntryKind -Path $Path) -ne 'File') {
|
|
135
|
+
return $null
|
|
136
|
+
}
|
|
137
|
+
return (Get-WorktreeResolutionGitFileText -Path $Path)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function ConvertFrom-EpicScopeCheckpointText {
|
|
141
|
+
<#
|
|
142
|
+
.SYNOPSIS
|
|
143
|
+
Parse epic checkpoint text into an object, or $null. Never throws.
|
|
144
|
+
.DESCRIPTION
|
|
145
|
+
Pure. Null, empty, unparseable, and non-object JSON (an array or a scalar) all
|
|
146
|
+
return $null, so an unreadable checkpoint is treated as absent.
|
|
147
|
+
.PARAMETER Text
|
|
148
|
+
The raw checkpoint text.
|
|
149
|
+
#>
|
|
150
|
+
[CmdletBinding()]
|
|
151
|
+
[OutputType([pscustomobject])]
|
|
152
|
+
param(
|
|
153
|
+
[Parameter(Mandatory = $true)]
|
|
154
|
+
[AllowNull()]
|
|
155
|
+
[AllowEmptyString()]
|
|
156
|
+
[string] $Text
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if ([string]::IsNullOrWhiteSpace($Text)) {
|
|
160
|
+
return $null
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
$parsed = ConvertFrom-Json -InputObject $Text -ErrorAction Stop
|
|
164
|
+
} catch {
|
|
165
|
+
# Unparseable text is a fail-closed "not epic scope", never a hook error.
|
|
166
|
+
Write-Debug "EPIC_SCOPE_CHECKPOINT_UNPARSEABLE: $($_.Exception.Message)"
|
|
167
|
+
return $null
|
|
168
|
+
}
|
|
169
|
+
if ($parsed -isnot [System.Management.Automation.PSCustomObject]) {
|
|
170
|
+
return $null
|
|
171
|
+
}
|
|
172
|
+
return $parsed
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function Get-EpicScopeWorktreeGitDirectory {
|
|
176
|
+
<#
|
|
177
|
+
.SYNOPSIS
|
|
178
|
+
Return a worktree's git directory, or $null.
|
|
179
|
+
.DESCRIPTION
|
|
180
|
+
For a main checkout the git entry is a directory and is returned as-is. For a
|
|
181
|
+
linked worktree the git entry is a file whose first line is a gitdir line; its
|
|
182
|
+
target is returned, joined to the worktree root when it is relative. Anything
|
|
183
|
+
else returns $null.
|
|
184
|
+
.PARAMETER WorktreeRoot
|
|
185
|
+
The absolute worktree root.
|
|
186
|
+
#>
|
|
187
|
+
[CmdletBinding()]
|
|
188
|
+
[OutputType([string])]
|
|
189
|
+
param(
|
|
190
|
+
[Parameter(Mandatory = $true)]
|
|
191
|
+
[AllowEmptyString()]
|
|
192
|
+
[string] $WorktreeRoot
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
$root = ConvertTo-WorktreeResolutionNormalizedPath -Path $WorktreeRoot
|
|
196
|
+
if ($null -eq $root -or $root -notmatch $script:AbsolutePathPattern) {
|
|
197
|
+
return $null
|
|
198
|
+
}
|
|
199
|
+
$gitEntry = Join-WorktreeResolutionPath -WorktreeRoot $root -RepoRelativePath $script:GitEntryName
|
|
200
|
+
$kind = Get-WorktreeResolutionGitEntryKind -Path $gitEntry
|
|
201
|
+
# Decide by entry kind: a directory is the git directory itself, a file points at it,
|
|
202
|
+
# and a missing entry means the root carries no git state.
|
|
203
|
+
if ($kind -eq 'Directory') {
|
|
204
|
+
return $gitEntry
|
|
205
|
+
} elseif ($kind -ne 'File') {
|
|
206
|
+
return $null
|
|
207
|
+
}
|
|
208
|
+
$firstLine = Get-EpicScopeFirstLine -Text (Get-WorktreeResolutionGitFileText -Path $gitEntry)
|
|
209
|
+
if ($null -eq $firstLine -or $firstLine -notmatch $script:GitDirLinePattern) {
|
|
210
|
+
return $null
|
|
211
|
+
}
|
|
212
|
+
$target = ConvertTo-WorktreeResolutionNormalizedPath -Path $Matches['target']
|
|
213
|
+
if ($target -match $script:AbsolutePathPattern) {
|
|
214
|
+
return $target
|
|
215
|
+
}
|
|
216
|
+
return (Join-WorktreeResolutionPath -WorktreeRoot $root -RepoRelativePath $target)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function Get-EpicScopeWorktreeHeadBranch {
|
|
220
|
+
<#
|
|
221
|
+
.SYNOPSIS
|
|
222
|
+
Return the branch checked out in a worktree, or $null for a detached or unreadable HEAD (seam).
|
|
223
|
+
.PARAMETER WorktreeRoot
|
|
224
|
+
The absolute worktree root.
|
|
225
|
+
#>
|
|
226
|
+
[CmdletBinding()]
|
|
227
|
+
[OutputType([string])]
|
|
228
|
+
param(
|
|
229
|
+
[Parameter(Mandatory = $true)]
|
|
230
|
+
[AllowEmptyString()]
|
|
231
|
+
[string] $WorktreeRoot
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
$gitDirectory = Get-EpicScopeWorktreeGitDirectory -WorktreeRoot $WorktreeRoot
|
|
235
|
+
if ($null -eq $gitDirectory) {
|
|
236
|
+
return $null
|
|
237
|
+
}
|
|
238
|
+
$headPath = Join-WorktreeResolutionPath -WorktreeRoot $gitDirectory -RepoRelativePath $script:HeadFileName
|
|
239
|
+
$firstLine = Get-EpicScopeFirstLine -Text (Get-WorktreeResolutionGitFileText -Path $headPath)
|
|
240
|
+
if ($null -eq $firstLine -or -not $firstLine.StartsWith($script:BranchReferencePrefix)) {
|
|
241
|
+
return $null
|
|
242
|
+
}
|
|
243
|
+
$branch = $firstLine.Substring($script:BranchReferencePrefix.Length).Trim()
|
|
244
|
+
if ($branch.Length -eq 0) {
|
|
245
|
+
return $null
|
|
246
|
+
}
|
|
247
|
+
return $branch
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function Test-EpicScopeMergeInProgress {
|
|
251
|
+
<#
|
|
252
|
+
.SYNOPSIS
|
|
253
|
+
Report whether a merge is in progress in a worktree (MERGE_HEAD exists) (seam).
|
|
254
|
+
.PARAMETER WorktreeRoot
|
|
255
|
+
The absolute worktree root.
|
|
256
|
+
#>
|
|
257
|
+
[CmdletBinding()]
|
|
258
|
+
[OutputType([bool])]
|
|
259
|
+
param(
|
|
260
|
+
[Parameter(Mandatory = $true)]
|
|
261
|
+
[AllowEmptyString()]
|
|
262
|
+
[string] $WorktreeRoot
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
$gitDirectory = Get-EpicScopeWorktreeGitDirectory -WorktreeRoot $WorktreeRoot
|
|
266
|
+
if ($null -eq $gitDirectory) {
|
|
267
|
+
return $false
|
|
268
|
+
}
|
|
269
|
+
$mergeHeadPath = Join-WorktreeResolutionPath -WorktreeRoot $gitDirectory -RepoRelativePath $script:MergeHeadFileName
|
|
270
|
+
return ((Get-WorktreeResolutionGitEntryKind -Path $mergeHeadPath) -eq 'File')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function Resolve-EpicScopeCheckpoint {
|
|
274
|
+
<#
|
|
275
|
+
.SYNOPSIS
|
|
276
|
+
Decide whether a gated call is an epic-level operation and return the governing epic checkpoint.
|
|
277
|
+
.DESCRIPTION
|
|
278
|
+
Returns an object with IsEpicScope, CheckpointPath, Checkpoint, WorktreeRoot,
|
|
279
|
+
Branch, MergeInProgress, and Reason. The first matching step decides: no branch
|
|
280
|
+
signal without head matching (no-branch-signal, and no checkpoint read); an
|
|
281
|
+
unresolvable session root (session-root-unresolved); an absent or unparseable epic
|
|
282
|
+
checkpoint (epic-checkpoint-absent-or-unparseable); a route_id other than epic
|
|
283
|
+
(route_id); an empty integration_branch (integration_branch); an unresolvable
|
|
284
|
+
selector (selector-unresolved); a branch that does not match (branch-mismatch).
|
|
285
|
+
.PARAMETER Text
|
|
286
|
+
The command or prompt text; only its branch signal is read.
|
|
287
|
+
.PARAMETER SessionRoot
|
|
288
|
+
The session directory; defaults to the current location.
|
|
289
|
+
.PARAMETER WorktreeSelector
|
|
290
|
+
The -C selector value of a command leg, when present.
|
|
291
|
+
.PARAMETER MatchWorktreeHead
|
|
292
|
+
Match the effective worktree's HEAD branch, ignoring any text branch signal, and
|
|
293
|
+
probe that worktree for a merge in progress.
|
|
294
|
+
#>
|
|
295
|
+
[CmdletBinding()]
|
|
296
|
+
[OutputType([pscustomobject])]
|
|
297
|
+
param(
|
|
298
|
+
[Parameter(Mandatory = $true)]
|
|
299
|
+
[AllowNull()]
|
|
300
|
+
[AllowEmptyString()]
|
|
301
|
+
[string] $Text,
|
|
302
|
+
|
|
303
|
+
[AllowNull()]
|
|
304
|
+
[AllowEmptyString()]
|
|
305
|
+
[string] $SessionRoot = (Get-Location).ProviderPath,
|
|
306
|
+
|
|
307
|
+
[AllowNull()]
|
|
308
|
+
[AllowEmptyString()]
|
|
309
|
+
[string] $WorktreeSelector,
|
|
310
|
+
|
|
311
|
+
[switch] $MatchWorktreeHead
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
$branch = Find-WorktreeResolutionBranchSignal -Text $Text
|
|
315
|
+
if ($null -eq $branch -and -not $MatchWorktreeHead) {
|
|
316
|
+
return (New-EpicScopeResult -IsEpicScope $false -Reason 'no-branch-signal')
|
|
317
|
+
}
|
|
318
|
+
$root = Find-WorktreeResolutionRoot -Path $SessionRoot
|
|
319
|
+
if ($null -eq $root) {
|
|
320
|
+
return (New-EpicScopeResult -IsEpicScope $false -Reason 'session-root-unresolved')
|
|
321
|
+
}
|
|
322
|
+
$path = Join-WorktreeResolutionPath -WorktreeRoot $root -RepoRelativePath (Get-EpicScopeCheckpointRelativePath)
|
|
323
|
+
$checkpoint = ConvertFrom-EpicScopeCheckpointText -Text (Get-EpicScopeCheckpointText -Path $path)
|
|
324
|
+
if ($null -eq $checkpoint) {
|
|
325
|
+
return (New-EpicScopeResult -IsEpicScope $false -CheckpointPath $path -Reason 'epic-checkpoint-absent-or-unparseable')
|
|
326
|
+
}
|
|
327
|
+
if ([string](Get-EpicScopePropertyValue -InputObject $checkpoint -Name 'route_id') -cne $script:EpicRouteId) {
|
|
328
|
+
return (New-EpicScopeResult -IsEpicScope $false -CheckpointPath $path -Reason 'route_id')
|
|
329
|
+
}
|
|
330
|
+
$integrationBranch = [string](Get-EpicScopePropertyValue -InputObject $checkpoint -Name 'integration_branch')
|
|
331
|
+
if ([string]::IsNullOrWhiteSpace($integrationBranch)) {
|
|
332
|
+
return (New-EpicScopeResult -IsEpicScope $false -CheckpointPath $path -Reason 'integration_branch')
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
# Decide the matched branch. A command or path leg (-MatchWorktreeHead) matches the
|
|
336
|
+
# effective worktree's HEAD, the selector worktree taking precedence, and ignores any
|
|
337
|
+
# text branch signal so the merge probe inspects the worktree the call operates on
|
|
338
|
+
# (issue #663 remediation CR-2); otherwise the explicit branch signal decides.
|
|
339
|
+
$effectiveRoot = $root
|
|
340
|
+
if (-not $MatchWorktreeHead) {
|
|
341
|
+
$candidate = $branch
|
|
342
|
+
} else {
|
|
343
|
+
if (-not [string]::IsNullOrWhiteSpace($WorktreeSelector)) {
|
|
344
|
+
$effectiveRoot = Find-WorktreeResolutionRoot -Path $WorktreeSelector
|
|
345
|
+
}
|
|
346
|
+
if ($null -eq $effectiveRoot) {
|
|
347
|
+
return (New-EpicScopeResult -IsEpicScope $false -CheckpointPath $path -Reason 'selector-unresolved')
|
|
348
|
+
}
|
|
349
|
+
$candidate = Get-EpicScopeWorktreeHeadBranch -WorktreeRoot $effectiveRoot
|
|
350
|
+
}
|
|
351
|
+
if ($null -eq $candidate -or $candidate -cne $integrationBranch) {
|
|
352
|
+
return (New-EpicScopeResult -IsEpicScope $false -CheckpointPath $path -Reason 'branch-mismatch')
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
$mergeInProgress = $false
|
|
356
|
+
if ($MatchWorktreeHead) {
|
|
357
|
+
$mergeInProgress = [bool](Test-EpicScopeMergeInProgress -WorktreeRoot $effectiveRoot)
|
|
358
|
+
}
|
|
359
|
+
return (New-EpicScopeResult -IsEpicScope $true -CheckpointPath $path -Checkpoint $checkpoint -WorktreeRoot $effectiveRoot -Branch $candidate -MergeInProgress $mergeInProgress -Reason 'epic-scope')
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
Export-ModuleMember -Function `
|
|
363
|
+
Get-EpicScopeCheckpointRelativePath, `
|
|
364
|
+
Get-EpicScopeCheckpointText, `
|
|
365
|
+
ConvertFrom-EpicScopeCheckpointText, `
|
|
366
|
+
Get-EpicScopeWorktreeGitDirectory, `
|
|
367
|
+
Get-EpicScopeWorktreeHeadBranch, `
|
|
368
|
+
Test-EpicScopeMergeInProgress, `
|
|
369
|
+
Resolve-EpicScopeCheckpoint
|