@spardutti/claude-skills 2.33.3 → 2.33.5

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.
@@ -61,6 +61,22 @@ survivor_list() { # survivor_list <survivors> <tool>
61
61
  echo " not equivalent — see testing-best-practices/MUTATION-TESTING.md."
62
62
  }
63
63
 
64
+ changed_files() {
65
+ { git diff "$BASE"...HEAD --name-only 2>/dev/null
66
+ git diff HEAD --name-only 2>/dev/null
67
+ git ls-files --others --exclude-standard 2>/dev/null; } | sort -u
68
+ }
69
+
70
+ # Ignored extensions are left out; see the comment where ship-gate.sh first calls this.
71
+ receipt_key() {
72
+ { printf '%s\n' "$PROJECT_DIR"
73
+ changed_files | grep -viE "\.($GAUNTLET_IGNORE_EXT)$" | while IFS= read -r f; do
74
+ [ -n "$f" ] || continue
75
+ printf '%s\n' "$f"
76
+ [ -f "$f" ] && cat "$f"
77
+ done; } | git hash-object --stdin
78
+ }
79
+
64
80
  # A scoped --baseline replaces only the changed modules' names. Rebuilding the whole
65
81
  # file re-mutated an entire API to accept eleven of them.
66
82
  baseline_write() { # baseline_write <baseline file> <survivors file> <scope regex, or empty>
@@ -9,8 +9,8 @@
9
9
  # bash .claude/hooks/ship-gate.sh [base-ref]
10
10
  # bash .claude/hooks/ship-gate.sh --key print the receipt key and stop
11
11
  # bash .claude/hooks/ship-gate.sh --force write a FORCED receipt, run nothing
12
- # bash .claude/hooks/ship-gate.sh --baseline record today's mutmut survivors as
13
- # accepted debt, write no receipt
12
+ # bash .claude/hooks/ship-gate.sh --baseline accept the survivors as debt, reusing
13
+ # this tree's last run, and write the receipt
14
14
  #
15
15
  # On completion it writes a RECEIPT at /tmp/claude-shipgate-<key>, and the
16
16
  # PreToolUse hook refuses `git commit` / `git push` without a matching one. The
@@ -75,7 +75,7 @@ fi
75
75
  GAUNTLET_MAX_LINES=200
76
76
  GAUNTLET_MUTATE=""
77
77
  GAUNTLET_SOURCE_EXT="ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|rb|php|c|h|cpp|hpp|cs|swift|gd"
78
- GAUNTLET_IGNORE_EXT="md|mdx|txt|rst|adoc|json|ya?ml|toml|lock|cfg|ini|env|csv|tsv|sql|html|css|scss|svg|png|jpg|jpeg|gif|webp|ico|pdf|woff2?|ttf|otf|mp3|mp4|wav|zip|gz|tres|tscn|import|godot"
78
+ GAUNTLET_IGNORE_EXT="md|mdx|txt|rst|adoc|jsonc?|ya?ml|toml|lock|cfg|ini|env|csv|tsv|sql|html|css|scss|svg|png|jpg|jpeg|gif|webp|ico|pdf|woff2?|ttf|otf|mp3|mp4|wav|zip|gz|tres|tscn|import|godot"
79
79
  GAUNTLET_IGNORE_FILES="*.gen.ts *.gen.tsx *.generated.* */migrations/*.py */alembic/versions/*.py */components/ui/*.tsx */*.config.*"
80
80
  # Length-checked and skill-audited like everything else, but not mutated.
81
81
  # Mutation pays on logic and burns time on presentation: a component's mutants
@@ -95,9 +95,7 @@ if [ -z "$BASE" ]; then
95
95
  || git rev-list --max-parents=0 HEAD | head -1)
96
96
  fi
97
97
 
98
- CHANGED=$( { git diff "$BASE"...HEAD --name-only 2>/dev/null
99
- git diff HEAD --name-only 2>/dev/null
100
- git ls-files --others --exclude-standard 2>/dev/null; } | sort -u )
98
+ CHANGED=$(changed_files)
101
99
 
102
100
  # Only the files this gate can actually read go into the key. It used to be every
103
101
  # changed file, so a version bump in package.json or a line in a changelog threw
@@ -106,8 +104,7 @@ CHANGED=$( { git diff "$BASE"...HEAD --name-only 2>/dev/null
106
104
  # a release is trying to ship. An ignored extension is one no check here opens,
107
105
  # so it cannot change a verdict. Unknown extensions stay in, because UNPROVEN is
108
106
  # a verdict about exactly those.
109
- KEYED=$(printf '%s\n' "$CHANGED" | grep -viE "\.($GAUNTLET_IGNORE_EXT)$")
110
-
107
+ #
111
108
  # The repo path plus the names and contents of the changed files, in a fixed
112
109
  # order. The path is in there because the key is otherwise pure content: two
113
110
  # repos holding the same file would share a receipt, and one would be waved
@@ -115,12 +112,7 @@ KEYED=$(printf '%s\n' "$CHANGED" | grep -viE "\.($GAUNTLET_IGNORE_EXT)$")
115
112
  # diff-based key changes when the content did not, and the receipt would go stale
116
113
  # the moment it was committed. This is identical either side of a commit, and
117
114
  # changes the instant any of those files is edited.
118
- RECEIPT_KEY=$( { printf '%s\n' "$PROJECT_DIR"
119
- printf '%s\n' "$KEYED" | while IFS= read -r f; do
120
- [ -n "$f" ] || continue
121
- printf '%s\n' "$f"
122
- [ -f "$f" ] && cat "$f"
123
- done; } | git hash-object --stdin )
115
+ RECEIPT_KEY=$(receipt_key)
124
116
  RECEIPT="/tmp/claude-shipgate-$RECEIPT_KEY"
125
117
 
126
118
  if [ "$MODE" = key ]; then printf '%s\n' "$RECEIPT_KEY"; exit 0; fi
@@ -324,13 +316,16 @@ for owner in $OWNERS; do
324
316
  continue
325
317
  fi
326
318
 
327
- # Only a clean run is recorded, and a baseline run always asks mutmut afresh.
319
+ # Only a clean run is recorded. A baseline replays this tree's last mutmut run, log
320
+ # included, rather than repeating it: accepting survivors used to cost two more runs.
328
321
  PKEY=""; [ "$TOOL" != config ] && PKEY="/tmp/claude-shipgate-project-$(project_key "$owner" "$TOOL")"
329
322
  if [ -n "$PKEY" ] && [ -f "$PKEY" ] && { [ "$TOOL" != mutmut ] || [ "$MODE" != baseline ]; }; then
330
323
  echo " $label $TOOL — ok, not re-run: nothing of its kind changed since it passed"
331
324
  continue
332
325
  fi
326
+ [ "$TOOL" = mutmut ] && [ "$MODE" = baseline ] && [ -f "$PKEY.out" ] && CMD="cp '$PKEY.log' '$MLOG' 2>/dev/null; cat '$PKEY.out'"
333
327
  OUT=$(eval "$CMD" 2>&1); RC=$?
328
+ [ "$TOOL" = mutmut ] && [ $RC -eq 0 ] && { printf '%s\n' "$OUT" > "$PKEY.out"; cp "$MLOG" "$PKEY.log" 2>/dev/null; }
334
329
  [ -n "$SCOPE_RE" ] && [ $RC -eq 0 ] && OUT=$(printf '%s\n' "$OUT" | grep -E "^[[:space:]]*($SCOPE_RE)\.x")
335
330
  # Match a finding, never a summary row. Stryker prints a `# survived` COLUMN
336
331
  # HEADER every run and repeats the word in its table, so a bare grep reports
@@ -458,11 +453,8 @@ if [ -n "$MISSING" ]; then
458
453
  [ "$STATUS" = 0 ] && STATUS=2
459
454
  fi
460
455
 
461
- if [ "$MODE" = baseline ]; then
462
- echo
463
- echo "ship-gate: baselines updated. No receipt written — run the gate for real."
464
- exit 0
465
- fi
456
+ # .mutmut-baseline is part of the key, so a baseline's receipt is keyed after it is written.
457
+ [ "$MODE" = baseline ] && RECEIPT="/tmp/claude-shipgate-$(receipt_key)"
466
458
 
467
459
  # The receipt is written by this script and nothing else. A hand-rolled check
468
460
  # produces no receipt, which is the whole point: substituting a weaker check is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spardutti/claude-skills",
3
- "version": "2.33.3",
3
+ "version": "2.33.5",
4
4
  "description": "Guardrails for Claude Code — best-practice skills, planning commands, and delivery gates.",
5
5
  "type": "module",
6
6
  "repository": {