@danmoisan/drm-copilot-mcp 1.1.14 → 1.1.16
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/agents/powershell-typed-engineer.md +4 -5
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget-route.ps1 +137 -0
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +95 -66
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +330 -0
- package/resources/claude-customizations/.claude/rules/powershell.md +3 -3
- 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/invoke-powershell-engineer/SKILL.md +3 -4
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +6 -6
- package/resources/claude-customizations/pack-manifests/core.json +11 -0
- package/resources/claude-customizations/pack-manifests/powershell.json +1 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +1 -2
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +2 -3
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +2 -3
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +2 -3
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +2 -3
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +2 -3
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +2 -3
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +285 -80
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +7 -7
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +8 -10
- package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +2 -2
- package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +3 -3
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +6 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cleanup_worktrees_enumerate_lib.sh: sourceable enumeration/protection function
|
|
3
|
+
# group for the cleanup-worktrees tool, split out of cleanup_worktrees_lib.sh to keep
|
|
4
|
+
# every file within the 500-line cap. Provides the git-binary test seam
|
|
5
|
+
# (cleanup_wt_git), branch/worktree enumeration (enumerate_branches,
|
|
6
|
+
# parse_worktree_list), path normalization (normalize_wt_path), the
|
|
7
|
+
# current-worktree/branch and base-branch protection set (compute_protected), and
|
|
8
|
+
# the main-freshness warning (check_main_freshness). The classification ladder,
|
|
9
|
+
# consolidation, deletion, and the CLI live in sibling files (cleanup_worktrees_lib.sh,
|
|
10
|
+
# cleanup_worktrees_actions_lib.sh, cleanup-worktrees.sh).
|
|
11
|
+
#
|
|
12
|
+
# Sourcing contract: defines functions and the single constant CLEANUP_WT_BASE_BRANCH
|
|
13
|
+
# and runs no other work at source time, so wrapper and bats suites source it safely.
|
|
14
|
+
# It MUST be sourced before cleanup_worktrees_lib.sh, whose classification functions
|
|
15
|
+
# call cleanup_wt_git, parse_worktree_list, compute_protected, and normalize_wt_path
|
|
16
|
+
# defined here.
|
|
17
|
+
#
|
|
18
|
+
# All git commands go through cleanup_wt_git so tests can stub the git binary via
|
|
19
|
+
# CLEANUP_WT_GIT_BIN. Git exit-code capture rule: the `git worktree list --porcelain`
|
|
20
|
+
# output consumed by parse_worktree_list is captured in the PARENT shell
|
|
21
|
+
# (`out=$(cmd) || rc=$?`, then iterated over `<<<"$out"`) so a non-zero exit is
|
|
22
|
+
# observed here rather than lost inside a process substitution. On a hard failure
|
|
23
|
+
# parse_worktree_list emits no records and returns git's non-zero exit code, and
|
|
24
|
+
# compute_protected propagates that failure rather than degrading to an empty
|
|
25
|
+
# (weakened) protected set. An empty porcelain output with exit 0 remains a valid
|
|
26
|
+
# empty list. enumerate_branches captures `git for-each-ref` in the parent shell
|
|
27
|
+
# (`out=$(...) || rc=$?`) BEFORE sorting, so a for-each-ref hard failure is observed
|
|
28
|
+
# even when the caller lacks pipefail; it returns git's exit code with no stdout rather
|
|
29
|
+
# than an empty branch list. compute_protected captures `git rev-parse --abbrev-ref
|
|
30
|
+
# HEAD` and `git rev-parse --show-toplevel`; a hard failure of either is fatal (git's
|
|
31
|
+
# exit code, no weakened fallback), and the base-branch record is emitted only after
|
|
32
|
+
# both guards pass. The detached-HEAD case (rev-parse prints HEAD) is unaffected.
|
|
33
|
+
|
|
34
|
+
cleanup_wt_git() {
|
|
35
|
+
# Resolve the git binary honoring the CLEANUP_WT_GIT_BIN override seam, then
|
|
36
|
+
# execute it with the caller's arguments.
|
|
37
|
+
#
|
|
38
|
+
# When CLEANUP_WT_GIT_BIN is set to a non-empty value it must point to an
|
|
39
|
+
# existing executable; an empty or nonexistent value is treated as missing and
|
|
40
|
+
# falls back to `command -v git`. This mirrors the SHELL_QC_<TOOL>_BIN seam in
|
|
41
|
+
# scripts/bash/shell_qc_lib.sh so the bats suites can stub git deterministically.
|
|
42
|
+
#
|
|
43
|
+
# Args: the git subcommand and its arguments.
|
|
44
|
+
# Returns git's exit code, or 127 when no git binary can be resolved.
|
|
45
|
+
local override=${CLEANUP_WT_GIT_BIN:-}
|
|
46
|
+
local git_bin=""
|
|
47
|
+
if [[ -n $override && -x $override ]]; then
|
|
48
|
+
git_bin=$override
|
|
49
|
+
else
|
|
50
|
+
git_bin=$(command -v git 2>/dev/null) || git_bin=""
|
|
51
|
+
fi
|
|
52
|
+
if [[ -z $git_bin ]]; then
|
|
53
|
+
printf 'cleanup-worktrees: no git binary resolved (CLEANUP_WT_GIT_BIN=%s)\n' "$override" >&2
|
|
54
|
+
return 127
|
|
55
|
+
fi
|
|
56
|
+
"$git_bin" "$@"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
enumerate_branches() {
|
|
60
|
+
# Enumerate local branches via plumbing, one "name sha" pair per line.
|
|
61
|
+
#
|
|
62
|
+
# Uses `git for-each-ref` rather than `git branch` so loose refs and packed-refs
|
|
63
|
+
# are read uniformly and the output carries no decoration markers (`*`, `+`) or
|
|
64
|
+
# column padding. Output is LC_ALL=C sorted for deterministic ordering and
|
|
65
|
+
# deterministic cross-branch cherry-pick order.
|
|
66
|
+
#
|
|
67
|
+
# The for-each-ref result is captured in the parent shell (`out=$(...) || rc=$?`) so
|
|
68
|
+
# a non-zero for-each-ref exit is observed here even when the caller lacks pipefail
|
|
69
|
+
# (the bats harness); piping straight into sort would attribute only sort's exit to
|
|
70
|
+
# rc and lose the for-each-ref failure. On a hard failure it prints a diagnostic to
|
|
71
|
+
# stderr, emits no stdout, and returns git's non-zero exit code. Returns git's exit
|
|
72
|
+
# code from for-each-ref (0 on success).
|
|
73
|
+
local rc=0 out
|
|
74
|
+
out=$(cleanup_wt_git for-each-ref \
|
|
75
|
+
--format='%(refname:short) %(objectname)' refs/heads/) || rc=$?
|
|
76
|
+
if ((rc != 0)); then
|
|
77
|
+
printf 'cleanup-worktrees: git for-each-ref refs/heads/ failed (rc=%s)\n' "$rc" >&2
|
|
78
|
+
return "$rc"
|
|
79
|
+
fi
|
|
80
|
+
# printf-pipe (not a herestring) so an empty ref list emits nothing rather than one
|
|
81
|
+
# empty line.
|
|
82
|
+
printf '%s' "$out" | LC_ALL=C sort
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
parse_worktree_list() {
|
|
86
|
+
# Parse `git worktree list --porcelain` stanza-wise into pipe-delimited records.
|
|
87
|
+
#
|
|
88
|
+
# Emits one record per worktree: `path|head|branch-or-DETACHED|flags`. A new
|
|
89
|
+
# stanza begins at each `worktree ` line; a blank line closes the current stanza.
|
|
90
|
+
# The branch field carries the short branch name (refs/heads/ stripped) or the
|
|
91
|
+
# literal DETACHED when the stanza had a `detached` line. flags is a
|
|
92
|
+
# comma-separated set drawn from main,detached,bare,locked,prunable; the first
|
|
93
|
+
# stanza always carries the `main` flag (the main worktree is never a candidate).
|
|
94
|
+
#
|
|
95
|
+
# The porcelain listing is captured in the parent shell (`out=$(...) || rc=$?`) so
|
|
96
|
+
# a non-zero git exit is observed here rather than being lost inside a process
|
|
97
|
+
# substitution. Returns 0 on success (including an empty porcelain output with exit
|
|
98
|
+
# 0, which is a valid empty list); on a hard git failure it prints a diagnostic to
|
|
99
|
+
# stderr, emits no records, and returns git's non-zero exit code.
|
|
100
|
+
local rc=0 line out
|
|
101
|
+
local path="" head="" branch="" detached=0 bare=0 locked=0 prunable=0
|
|
102
|
+
local first=1 have=0
|
|
103
|
+
emit_record() {
|
|
104
|
+
# Flush the accumulated stanza as one record; no-op when none accumulated.
|
|
105
|
+
((have == 0)) && return 0
|
|
106
|
+
local -a flag_parts=()
|
|
107
|
+
((first == 1)) && flag_parts+=("main")
|
|
108
|
+
((detached == 1)) && flag_parts+=("detached")
|
|
109
|
+
((bare == 1)) && flag_parts+=("bare")
|
|
110
|
+
((locked == 1)) && flag_parts+=("locked")
|
|
111
|
+
((prunable == 1)) && flag_parts+=("prunable")
|
|
112
|
+
local flags=""
|
|
113
|
+
local IFS=,
|
|
114
|
+
flags="${flag_parts[*]}"
|
|
115
|
+
local branch_field="DETACHED"
|
|
116
|
+
[[ -n $branch ]] && branch_field=$branch
|
|
117
|
+
printf '%s|%s|%s|%s\n' "$path" "$head" "$branch_field" "$flags"
|
|
118
|
+
first=0
|
|
119
|
+
}
|
|
120
|
+
# Guarded parent-shell capture: a non-zero git exit is observed here (an unguarded
|
|
121
|
+
# `out=$(...)` would abort under the wrapper's set -euo pipefail). On a hard
|
|
122
|
+
# failure, return before emitting any record so the caller cannot mistake a git
|
|
123
|
+
# failure for an empty worktree list.
|
|
124
|
+
out=$(cleanup_wt_git worktree list --porcelain) || rc=$?
|
|
125
|
+
if ((rc != 0)); then
|
|
126
|
+
printf 'cleanup-worktrees: git worktree list --porcelain failed (rc=%s)\n' "$rc" >&2
|
|
127
|
+
return "$rc"
|
|
128
|
+
fi
|
|
129
|
+
while IFS= read -r line || [[ -n $line ]]; do
|
|
130
|
+
case "$line" in
|
|
131
|
+
"worktree "*)
|
|
132
|
+
emit_record
|
|
133
|
+
path=${line#worktree }
|
|
134
|
+
head="" branch="" detached=0 bare=0 locked=0 prunable=0
|
|
135
|
+
have=1
|
|
136
|
+
;;
|
|
137
|
+
"HEAD "*) head=${line#HEAD } ;;
|
|
138
|
+
"branch "*) branch=${line#branch refs/heads/} ;;
|
|
139
|
+
detached) detached=1 ;;
|
|
140
|
+
bare) bare=1 ;;
|
|
141
|
+
"locked"*) locked=1 ;;
|
|
142
|
+
"prunable"*) prunable=1 ;;
|
|
143
|
+
"") ;; # stanza separator; the next `worktree ` flushes the record
|
|
144
|
+
esac
|
|
145
|
+
done <<<"$out"
|
|
146
|
+
emit_record
|
|
147
|
+
return "$rc"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
normalize_wt_path() {
|
|
151
|
+
# Normalize a worktree path for comparison across Windows/WSL and git output.
|
|
152
|
+
#
|
|
153
|
+
# Converts backslashes to forward slashes, lowercases (case-insensitive match on
|
|
154
|
+
# Windows), and strips a single trailing slash. Echoes the normalized value;
|
|
155
|
+
# echoes nothing for an empty input.
|
|
156
|
+
#
|
|
157
|
+
# Args: $1 = path.
|
|
158
|
+
local p=${1:-}
|
|
159
|
+
[[ -z $p ]] && return 0
|
|
160
|
+
p=${p//\\//}
|
|
161
|
+
p=${p,,}
|
|
162
|
+
p=${p%/}
|
|
163
|
+
printf '%s\n' "$p"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# The ladder's fixed comparison base. compute_protected protects it by name in every
|
|
167
|
+
# checkout topology and delete_candidate refuses it. It is deliberately not read from
|
|
168
|
+
# the environment, so no override can unprotect the base branch (issue #594).
|
|
169
|
+
CLEANUP_WT_BASE_BRANCH="main"
|
|
170
|
+
|
|
171
|
+
compute_protected() {
|
|
172
|
+
# Compute the protected branch and protected worktree paths (PROTECTED_CURRENT).
|
|
173
|
+
#
|
|
174
|
+
# Dual exclusion, both checks required:
|
|
175
|
+
# 1. Current branch via `git rev-parse --abbrev-ref HEAD`. A `HEAD` result means
|
|
176
|
+
# detached: no branch name to protect (only the worktree path is protected).
|
|
177
|
+
# 2. Current worktree path via `git rev-parse --show-toplevel`, compared after
|
|
178
|
+
# slash/case normalization against each porcelain worktree path.
|
|
179
|
+
# The main worktree (first porcelain stanza) is always protected regardless of the
|
|
180
|
+
# above. Emits `protected-branch|<name>` (omitted when detached) and one
|
|
181
|
+
# `protected-path|<normalized-path>` line per protected worktree.
|
|
182
|
+
#
|
|
183
|
+
# The base branch CLEANUP_WT_BASE_BRANCH is always protected by name, whichever
|
|
184
|
+
# worktree (if any) has it checked out. Its `protected-branch|` record is emitted
|
|
185
|
+
# only after both rev-parse guards pass, so the fail-closed contract below is
|
|
186
|
+
# unchanged, and it is omitted when the current branch already equals the base, so
|
|
187
|
+
# no duplicate record is emitted.
|
|
188
|
+
#
|
|
189
|
+
# Returns 0 on success. A parse_worktree_list hard failure propagates as its
|
|
190
|
+
# non-zero return; the caller must treat that as fatal, not as an empty (weakened)
|
|
191
|
+
# protected set. A hard failure of `git rev-parse --abbrev-ref HEAD` or
|
|
192
|
+
# `git rev-parse --show-toplevel` is likewise fatal (return git's exit code), never a
|
|
193
|
+
# weakened protection fallback; the detached-HEAD case (rev-parse succeeds printing
|
|
194
|
+
# HEAD) is unaffected and keeps its branch-name omission.
|
|
195
|
+
local rc=0 current_branch current_top norm_cur cbrc=0 ctrc=0
|
|
196
|
+
current_branch=$(cleanup_wt_git rev-parse --abbrev-ref HEAD) || cbrc=$?
|
|
197
|
+
if ((cbrc != 0)); then
|
|
198
|
+
printf 'cleanup-worktrees: git rev-parse --abbrev-ref HEAD failed (rc=%s)\n' "$cbrc" >&2
|
|
199
|
+
return "$cbrc"
|
|
200
|
+
fi
|
|
201
|
+
current_top=$(cleanup_wt_git rev-parse --show-toplevel) || ctrc=$?
|
|
202
|
+
if ((ctrc != 0)); then
|
|
203
|
+
printf 'cleanup-worktrees: git rev-parse --show-toplevel failed (rc=%s)\n' "$ctrc" >&2
|
|
204
|
+
return "$ctrc"
|
|
205
|
+
fi
|
|
206
|
+
norm_cur=$(normalize_wt_path "$current_top")
|
|
207
|
+
if [[ -n $current_branch && $current_branch != HEAD ]]; then
|
|
208
|
+
printf 'protected-branch|%s\n' "$current_branch"
|
|
209
|
+
fi
|
|
210
|
+
# Base-branch protection is unconditional and emitted only after both rev-parse
|
|
211
|
+
# guards pass; it is skipped when the current branch already equals the base.
|
|
212
|
+
if [[ $current_branch != "$CLEANUP_WT_BASE_BRANCH" ]]; then
|
|
213
|
+
printf 'protected-branch|%s\n' "$CLEANUP_WT_BASE_BRANCH"
|
|
214
|
+
fi
|
|
215
|
+
local first=1 record path norm pout prc=0
|
|
216
|
+
# Guarded parent-shell capture: a parse_worktree_list hard failure must abort here,
|
|
217
|
+
# not degrade to an empty protected set. Compare the current toplevel against each
|
|
218
|
+
# porcelain worktree path; the first stanza (main worktree) is always protected.
|
|
219
|
+
pout=$(parse_worktree_list) || prc=$?
|
|
220
|
+
if ((prc != 0)); then
|
|
221
|
+
return "$prc"
|
|
222
|
+
fi
|
|
223
|
+
while IFS= read -r record; do
|
|
224
|
+
[[ -z $record ]] && continue
|
|
225
|
+
path=${record%%|*}
|
|
226
|
+
norm=$(normalize_wt_path "$path")
|
|
227
|
+
if ((first == 1)); then
|
|
228
|
+
printf 'protected-path|%s\n' "$norm"
|
|
229
|
+
elif [[ -n $norm_cur && $norm == "$norm_cur" ]]; then
|
|
230
|
+
printf 'protected-path|%s\n' "$norm"
|
|
231
|
+
fi
|
|
232
|
+
first=0
|
|
233
|
+
done <<<"$pout"
|
|
234
|
+
return "$rc"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
check_main_freshness() {
|
|
238
|
+
# Warn when local `main` diverges from `origin/main`; never block classification.
|
|
239
|
+
#
|
|
240
|
+
# Compares `git rev-parse main` with `git rev-parse origin/main`. On mismatch it
|
|
241
|
+
# emits the report line `WARN|main-divergence|<local-sha>|<origin-sha>`. A stale
|
|
242
|
+
# local `main` can only produce a false "not merged" (the safe direction), so this
|
|
243
|
+
# is advisory only and always returns 0. If either rev-parse fails (e.g. no
|
|
244
|
+
# origin/main configured), the check is skipped without error.
|
|
245
|
+
local local_sha origin_sha
|
|
246
|
+
local_sha=$(cleanup_wt_git rev-parse main 2>/dev/null) || return 0
|
|
247
|
+
origin_sha=$(cleanup_wt_git rev-parse origin/main 2>/dev/null) || return 0
|
|
248
|
+
if [[ -n $local_sha && -n $origin_sha && $local_sha != "$origin_sha" ]]; then
|
|
249
|
+
printf 'WARN|main-divergence|%s|%s\n' "$local_sha" "$origin_sha"
|
|
250
|
+
fi
|
|
251
|
+
return 0
|
|
252
|
+
}
|