@danmoisan/drm-copilot-mcp 1.1.14 → 1.1.15
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/package.json +1 -1
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +330 -0
- package/resources/claude-customizations/.claude/rules/shell.md +8 -7
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +8 -8
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +234 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_actions_lib.sh +451 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +301 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_dirt_lib.sh +495 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +252 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_lib.sh +496 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_eol_lib.sh +212 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +492 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_report_records_lib.sh +476 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +182 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +1 -1
- package/resources/claude-customizations/pack-manifests/core.json +11 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cleanup_worktrees_detached_lib.sh: sourceable function group for detached-HEAD
|
|
3
|
+
# worktree registrations in the cleanup-worktrees tool. A branch-backed worktree is
|
|
4
|
+
# classified by branch name through classify_branch; a detached registration has no
|
|
5
|
+
# branch to classify, so this library classifies it on its own HEAD SHA and reports it
|
|
6
|
+
# as the five-field record WORKTREE|<path>|DETACHED|<state>|<flags>. The branch-backed
|
|
7
|
+
# four-field record WORKTREE|<path>|<branch>|<flags> is unchanged; the detached record
|
|
8
|
+
# replaces it for a detached registration rather than adding a second line.
|
|
9
|
+
#
|
|
10
|
+
# Sourcing contract: this file defines functions only; it never runs work at source
|
|
11
|
+
# time, so the wrapper and the bats suites can source it without side effects. It MUST
|
|
12
|
+
# be sourced after cleanup_worktrees_enumerate_lib.sh and cleanup_worktrees_lib.sh in
|
|
13
|
+
# every context, and additionally after cleanup_worktrees_actions_lib.sh in any context
|
|
14
|
+
# that calls remove_detached_worktree or apply_detached_worktrees, because it calls
|
|
15
|
+
# cleanup_wt_git, parse_worktree_list, normalize_wt_path, compute_protected, the four
|
|
16
|
+
# classification ladder rungs (classify_ancestry, classify_content_neutral,
|
|
17
|
+
# classify_cherry_equivalent, classify_residual_commit), and remove_worktree_safe. Bash
|
|
18
|
+
# resolves function names at call time, so run_report and run_apply may call into this
|
|
19
|
+
# file even though .claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh sources it last.
|
|
20
|
+
#
|
|
21
|
+
# Git exit-code capture rule: every git-backed read is captured in the PARENT shell with
|
|
22
|
+
# `|| rc=$?` and fails closed, matching the invariant documented at
|
|
23
|
+
# .claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_actions_lib.sh:19-35. A hard git failure always maps to
|
|
24
|
+
# the ANCESTRY_ERROR state and a non-zero return, never to a MERGED_* verdict and never
|
|
25
|
+
# to a removal.
|
|
26
|
+
#
|
|
27
|
+
# Deliberate non-reuse of classify_branch: its worktree lookup matches on branch name,
|
|
28
|
+
# and a detached record's branch field is the literal DETACHED, so the caller's own
|
|
29
|
+
# detached worktree would escape PROTECTED_CURRENT. It also emits into the BRANCH|
|
|
30
|
+
# namespace and can fabricate SHA-keyed COMMIT| records that cherry_pick_candidates
|
|
31
|
+
# would misread. Protection for a detached candidate is therefore decided BY NORMALIZED
|
|
32
|
+
# PATH against compute_protected's protected-path| records, and only the four ladder
|
|
33
|
+
# rungs (which accept a bare committish) are reused.
|
|
34
|
+
#
|
|
35
|
+
# Detached state vocabulary, exactly one token per classification:
|
|
36
|
+
# MERGED_CLEAN | MERGED_CONTENT_NEUTRAL | MERGED_EQUIVALENT | NOT_MERGED |
|
|
37
|
+
# HAS_UNIQUE_RESIDUALS | PROTECTED_CURRENT | ANCESTRY_ERROR
|
|
38
|
+
# The first three are the delete-eligible allowlist. Removal is never forced and
|
|
39
|
+
# `git worktree prune` is never invoked by this tool.
|
|
40
|
+
|
|
41
|
+
is_detached_candidate() {
|
|
42
|
+
# Return 0 iff a porcelain flag set marks a detached-HEAD removal candidate.
|
|
43
|
+
#
|
|
44
|
+
# The predicate is the porcelain FLAG, not the branch field: emit_record writes the
|
|
45
|
+
# literal DETACHED into the branch field whenever the branch accumulator is empty
|
|
46
|
+
# (.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh:115-116), which is also true of a
|
|
47
|
+
# bare-repository stanza, and a local branch literally named DETACHED would produce
|
|
48
|
+
# the same branch field with no `detached` flag.
|
|
49
|
+
#
|
|
50
|
+
# Returns 0 iff <flags> contains the `detached` token and contains neither `main`
|
|
51
|
+
# (the main worktree is never a candidate) nor `bare`. Makes no git call.
|
|
52
|
+
#
|
|
53
|
+
# Args: $1 = comma-separated flag set (may be empty).
|
|
54
|
+
local flags=${1:-}
|
|
55
|
+
case ",$flags," in
|
|
56
|
+
*,main,* | *,bare,*) return 1 ;;
|
|
57
|
+
esac
|
|
58
|
+
case ",$flags," in
|
|
59
|
+
*,detached,*) return 0 ;;
|
|
60
|
+
esac
|
|
61
|
+
return 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
classify_detached_head() {
|
|
65
|
+
# Classify one detached-HEAD worktree on its own HEAD SHA and echo one state token.
|
|
66
|
+
#
|
|
67
|
+
# Order (load-bearing): the PROTECTED_CURRENT check runs FIRST, by normalized path
|
|
68
|
+
# against compute_protected's protected-path| records, and returns BEFORE any
|
|
69
|
+
# classification git call, so the caller's own detached worktree is never probed for
|
|
70
|
+
# ancestry. The remaining rungs are the ladder from cleanup_worktrees_lib.sh applied
|
|
71
|
+
# to the bare SHA:
|
|
72
|
+
# 1. ancestry -> MERGED_CLEAN
|
|
73
|
+
# 2. content-neutral -> MERGED_CONTENT_NEUTRAL
|
|
74
|
+
# 3. cherry equivalence -> MERGED_EQUIVALENT
|
|
75
|
+
# 4. rename-aware blob fallback: all residuals CONTENT_ON_MAIN -> MERGED_EQUIVALENT
|
|
76
|
+
# 5. remaining unique residuals -> HAS_UNIQUE_RESIDUALS when the HEAD was partially
|
|
77
|
+
# incorporated on main (a cherry `-` line or a CONTENT_ON_MAIN residual), else
|
|
78
|
+
# NOT_MERGED.
|
|
79
|
+
# No BRANCH| or COMMIT| record is emitted, and classify_branch is never called.
|
|
80
|
+
#
|
|
81
|
+
# Args: $1 = detached HEAD sha, $2 = worktree path.
|
|
82
|
+
# Returns 0 normally; 2 on any hard git failure, which always echoes ANCESTRY_ERROR.
|
|
83
|
+
local head="$1" path="$2"
|
|
84
|
+
local cpout cprc=0 pline norm
|
|
85
|
+
# Guarded parent-shell capture: a compute_protected hard failure must fail closed as
|
|
86
|
+
# ANCESTRY_ERROR, not degrade to a weakened (empty) protected set that could let the
|
|
87
|
+
# caller's own worktree reach a delete-eligible verdict.
|
|
88
|
+
cpout=$(compute_protected) || cprc=$?
|
|
89
|
+
if ((cprc != 0)); then
|
|
90
|
+
printf 'ANCESTRY_ERROR\n'
|
|
91
|
+
return 2
|
|
92
|
+
fi
|
|
93
|
+
norm=$(normalize_wt_path "$path")
|
|
94
|
+
while IFS= read -r pline; do
|
|
95
|
+
[[ $pline == protected-path\|* ]] || continue
|
|
96
|
+
if [[ -n $norm && ${pline#protected-path|} == "$norm" ]]; then
|
|
97
|
+
printf 'PROTECTED_CURRENT\n'
|
|
98
|
+
return 0
|
|
99
|
+
fi
|
|
100
|
+
done <<<"$cpout"
|
|
101
|
+
local v
|
|
102
|
+
v=$(classify_ancestry "$head")
|
|
103
|
+
case "$v" in
|
|
104
|
+
MERGED_CLEAN)
|
|
105
|
+
printf 'MERGED_CLEAN\n'
|
|
106
|
+
return 0
|
|
107
|
+
;;
|
|
108
|
+
ANCESTRY_ERROR)
|
|
109
|
+
printf 'ANCESTRY_ERROR\n'
|
|
110
|
+
return 2
|
|
111
|
+
;;
|
|
112
|
+
esac
|
|
113
|
+
v=$(classify_content_neutral "$head")
|
|
114
|
+
case "$v" in
|
|
115
|
+
MERGED_CONTENT_NEUTRAL)
|
|
116
|
+
printf 'MERGED_CONTENT_NEUTRAL\n'
|
|
117
|
+
return 0
|
|
118
|
+
;;
|
|
119
|
+
CONTENT_NEUTRAL_ERROR)
|
|
120
|
+
printf 'ANCESTRY_ERROR\n'
|
|
121
|
+
return 2
|
|
122
|
+
;;
|
|
123
|
+
esac
|
|
124
|
+
local ce
|
|
125
|
+
ce=$(classify_cherry_equivalent "$head")
|
|
126
|
+
if [[ $ce == "CHERRY_ERROR" || $ce == "DIFF_TREE_ERROR" ]]; then
|
|
127
|
+
printf 'ANCESTRY_ERROR\n'
|
|
128
|
+
return 2
|
|
129
|
+
fi
|
|
130
|
+
if [[ $ce == "MERGED_EQUIVALENT" ]]; then
|
|
131
|
+
printf 'MERGED_EQUIVALENT\n'
|
|
132
|
+
return 0
|
|
133
|
+
fi
|
|
134
|
+
# Residual `+` commits exist; resolve each via the rename-aware blob tier.
|
|
135
|
+
local sha verdict unique_count=0 content_count=0
|
|
136
|
+
while IFS= read -r pline; do
|
|
137
|
+
[[ $pline == RESIDUAL\ * ]] || continue
|
|
138
|
+
sha=${pline#RESIDUAL }
|
|
139
|
+
verdict=$(classify_residual_commit "$head" "$sha")
|
|
140
|
+
if [[ $verdict == "RESIDUAL_ERROR" ]]; then
|
|
141
|
+
printf 'ANCESTRY_ERROR\n'
|
|
142
|
+
return 2
|
|
143
|
+
fi
|
|
144
|
+
if [[ $verdict == UNIQUE\|* ]]; then
|
|
145
|
+
unique_count=$((unique_count + 1))
|
|
146
|
+
elif [[ $verdict == "CONTENT_ON_MAIN" ]]; then
|
|
147
|
+
content_count=$((content_count + 1))
|
|
148
|
+
fi
|
|
149
|
+
done <<<"$ce"
|
|
150
|
+
if ((unique_count == 0)); then
|
|
151
|
+
# Every residual was content-on-main; the HEAD is content-equivalent-merged.
|
|
152
|
+
printf 'MERGED_EQUIVALENT\n'
|
|
153
|
+
return 0
|
|
154
|
+
fi
|
|
155
|
+
# Partial-merge signal, read from the already-captured cherry verdict rather than a
|
|
156
|
+
# second `git cherry` invocation whose exit code would be discarded in a pipeline.
|
|
157
|
+
local minus_present=0
|
|
158
|
+
[[ $ce == *MINUS_PRESENT* ]] && minus_present=1
|
|
159
|
+
if ((minus_present == 1)) || ((content_count > 0)); then
|
|
160
|
+
printf 'HAS_UNIQUE_RESIDUALS\n'
|
|
161
|
+
else
|
|
162
|
+
printf 'NOT_MERGED\n'
|
|
163
|
+
fi
|
|
164
|
+
return 0
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
report_detached_worktrees() {
|
|
168
|
+
# Emit one five-field detached registration record per detached candidate.
|
|
169
|
+
#
|
|
170
|
+
# Consumes parse_worktree_list output (the caller's already-guarded capture, so a
|
|
171
|
+
# worktree-list hard failure has aborted upstream before this function is reached).
|
|
172
|
+
# Records are emitted in parse_worktree_list order; no sort is applied, because the
|
|
173
|
+
# branch-backed emission loop this replaces is likewise porcelain-ordered.
|
|
174
|
+
#
|
|
175
|
+
# The record's head field must be bound in the `read`; both pre-existing emission
|
|
176
|
+
# sites discard it with the `_` placeholder.
|
|
177
|
+
#
|
|
178
|
+
# Args: $1 = parse_worktree_list output.
|
|
179
|
+
# Returns the maximum classify_detached_head return code (0, or 2 when any candidate
|
|
180
|
+
# hard-failed), so the caller can fold a detached hard failure into its own rc.
|
|
181
|
+
local wlout="$1" record wpath whead wflags state crc rc=0
|
|
182
|
+
while IFS= read -r record; do
|
|
183
|
+
[[ -z $record ]] && continue
|
|
184
|
+
IFS='|' read -r wpath whead _ wflags <<<"$record"
|
|
185
|
+
is_detached_candidate "$wflags" || continue
|
|
186
|
+
crc=0
|
|
187
|
+
state=$(classify_detached_head "$whead" "$wpath") || crc=$?
|
|
188
|
+
printf 'WORKTREE|%s|DETACHED|%s|%s\n' "$wpath" "$state" "$wflags"
|
|
189
|
+
if ((crc > rc)); then
|
|
190
|
+
rc=$crc
|
|
191
|
+
fi
|
|
192
|
+
done <<<"$wlout"
|
|
193
|
+
return "$rc"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
reverify_detached_delete_eligible() {
|
|
197
|
+
# Same-process re-verification immediately before a destructive action.
|
|
198
|
+
#
|
|
199
|
+
# Re-runs classify_detached_head in-process and confirms the FRESH state is still
|
|
200
|
+
# delete-eligible: MERGED_CLEAN | MERGED_CONTENT_NEUTRAL | MERGED_EQUIVALENT. Any
|
|
201
|
+
# other verdict, and any hard git failure, emits
|
|
202
|
+
# ACTION|worktree-remove|<path>|BLOCKED-REVERIFY and returns 1. This is the detached
|
|
203
|
+
# counterpart of reverify_delete_eligible, which re-verifies by branch name.
|
|
204
|
+
#
|
|
205
|
+
# Args: $1 = detached HEAD sha, $2 = worktree path.
|
|
206
|
+
# Returns 0 when the fresh verdict is delete-eligible; 1 otherwise.
|
|
207
|
+
local head="$1" path="$2" state crc=0
|
|
208
|
+
# Guarded parent-shell capture: a hard failure maps explicitly to BLOCKED-REVERIFY
|
|
209
|
+
# and return 1, in addition to the state-token allowlist below.
|
|
210
|
+
state=$(classify_detached_head "$head" "$path") || crc=$?
|
|
211
|
+
if ((crc != 0)); then
|
|
212
|
+
printf 'ACTION|worktree-remove|%s|BLOCKED-REVERIFY\n' "$path"
|
|
213
|
+
return 1
|
|
214
|
+
fi
|
|
215
|
+
case "$state" in
|
|
216
|
+
MERGED_CLEAN | MERGED_CONTENT_NEUTRAL | MERGED_EQUIVALENT)
|
|
217
|
+
return 0
|
|
218
|
+
;;
|
|
219
|
+
esac
|
|
220
|
+
printf 'ACTION|worktree-remove|%s|BLOCKED-REVERIFY\n' "$path"
|
|
221
|
+
return 1
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
remove_detached_worktree() {
|
|
225
|
+
# Remove one delete-eligible detached worktree, in a load-bearing order:
|
|
226
|
+
# 1. locked -> emit ACTION|worktree-remove|<path>|BLOCKED-LOCKED, invoke NO git
|
|
227
|
+
# command, return 1.
|
|
228
|
+
# 2. prunable -> report-only: return 0 with no output and no git command. This tool
|
|
229
|
+
# never executes worktree pruning.
|
|
230
|
+
# 3. re-verify with reverify_detached_delete_eligible; return 1 on a flipped verdict.
|
|
231
|
+
# 4. remove_worktree_safe "<path>" and propagate its return.
|
|
232
|
+
# Steps 1 and 2 are pure string tests over the already-read porcelain flags and MUST
|
|
233
|
+
# precede every git invocation, so "no removal was attempted" is observable as an
|
|
234
|
+
# empty argv log rather than inferred from a report line.
|
|
235
|
+
#
|
|
236
|
+
# remove_worktree_safe is consumed as an opaque contract and is not modified: it owns
|
|
237
|
+
# the non-forced `git worktree remove`, the DIRTY| lines, and the BLOCKED-DIRTY result
|
|
238
|
+
# token. This function emits no DIRTY| line of its own. The forced-removal flag is
|
|
239
|
+
# never passed.
|
|
240
|
+
#
|
|
241
|
+
# Args: $1 = worktree path, $2 = detached HEAD sha, $3 = porcelain flag set.
|
|
242
|
+
# Returns 0 on successful removal or a prunable skip; 1 when blocked.
|
|
243
|
+
local path="$1" head="$2" flags="$3"
|
|
244
|
+
case ",$flags," in
|
|
245
|
+
*,locked,*)
|
|
246
|
+
printf 'ACTION|worktree-remove|%s|BLOCKED-LOCKED\n' "$path"
|
|
247
|
+
return 1
|
|
248
|
+
;;
|
|
249
|
+
esac
|
|
250
|
+
case ",$flags," in
|
|
251
|
+
*,prunable,*)
|
|
252
|
+
return 0
|
|
253
|
+
;;
|
|
254
|
+
esac
|
|
255
|
+
reverify_detached_delete_eligible "$head" "$path" || return 1
|
|
256
|
+
remove_worktree_safe "$path"
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
apply_detached_worktrees() {
|
|
260
|
+
# Apply-mode driver half for detached registrations: classify, report, then remove
|
|
261
|
+
# the delete-eligible ones.
|
|
262
|
+
#
|
|
263
|
+
# For each detached candidate it classifies once, emits
|
|
264
|
+
# WORKTREE|<path>|DETACHED|<state>|<flags> from THAT verdict before deciding removal,
|
|
265
|
+
# and only then acts. The record emission is part of this function's contract, not an
|
|
266
|
+
# optional extra: the apply-mode emission loop in cleanup_worktrees_actions_lib.sh
|
|
267
|
+
# skips detached candidates, so this is the only producer of a detached registration
|
|
268
|
+
# record in apply mode and each mode therefore emits exactly one record per detached
|
|
269
|
+
# registration. classify_detached_head is not called a second time for the record.
|
|
270
|
+
# The separate re-classification inside reverify_detached_delete_eligible is the
|
|
271
|
+
# same-process re-verification gate, not a duplicate of this reporting call.
|
|
272
|
+
#
|
|
273
|
+
# A hard failure sets rc=1 and performs no removal. A delete-eligible verdict calls
|
|
274
|
+
# remove_detached_worktree and sets rc=1 if it fails. Any other state performs no
|
|
275
|
+
# removal and emits no further line.
|
|
276
|
+
#
|
|
277
|
+
# Args: $1 = parse_worktree_list output.
|
|
278
|
+
# Returns 0 when every candidate was handled without failure or block; 1 otherwise.
|
|
279
|
+
local wlout="$1" record wpath whead wflags state crc rc=0
|
|
280
|
+
while IFS= read -r record; do
|
|
281
|
+
[[ -z $record ]] && continue
|
|
282
|
+
IFS='|' read -r wpath whead _ wflags <<<"$record"
|
|
283
|
+
is_detached_candidate "$wflags" || continue
|
|
284
|
+
crc=0
|
|
285
|
+
state=$(classify_detached_head "$whead" "$wpath") || crc=$?
|
|
286
|
+
printf 'WORKTREE|%s|DETACHED|%s|%s\n' "$wpath" "$state" "$wflags"
|
|
287
|
+
if ((crc != 0)); then
|
|
288
|
+
# A hard classification failure never triggers a removal; its state is an
|
|
289
|
+
# error state, not on the delete-eligible allowlist.
|
|
290
|
+
rc=1
|
|
291
|
+
continue
|
|
292
|
+
fi
|
|
293
|
+
case "$state" in
|
|
294
|
+
MERGED_CLEAN | MERGED_CONTENT_NEUTRAL | MERGED_EQUIVALENT)
|
|
295
|
+
remove_detached_worktree "$wpath" "$whead" "$wflags" || rc=1
|
|
296
|
+
;;
|
|
297
|
+
*) : ;;
|
|
298
|
+
esac
|
|
299
|
+
done <<<"$wlout"
|
|
300
|
+
return "$rc"
|
|
301
|
+
}
|