@spardutti/claude-skills 2.19.4 → 2.20.0

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 (2) hide show
  1. package/lib/setup-hook.mjs +57 -10
  2. package/package.json +1 -1
@@ -579,7 +579,7 @@ const SHIP_GATE_SCRIPT = `#!/usr/bin/env bash
579
579
  # what makes a fix re-gate itself instead of riding on the previous verdict.
580
580
  #
581
581
  # Exit codes:
582
- # 0 clean — nothing over the line limit, no surviving mutants
582
+ # 0 clean — nothing over the line limit, nothing survived, nothing uncovered
583
583
  # 1 findings that must be dealt with before shipping
584
584
  # 2 ran, but could not prove the tests (a mutation tool is missing) — report,
585
585
  # don't block; the output names exactly what to install and where
@@ -799,13 +799,24 @@ for owner in $OWNERS; do
799
799
  # when that one file had no tests, Stryker exited on "No tests were executed"
800
800
  # and the gate reported UNPROVEN, which writes a receipt and blocks nothing.
801
801
  FLAGS=""
802
+ GLOBBED=","
802
803
  while IFS= read -r f; do
803
804
  hunks=$(git diff -U0 "$BASE" -- "$f" 2>/dev/null \\
804
805
  | grep -oE '^@@ -[0-9,]+ \\+[0-9]+(,[0-9]+)?' | sed 's/.*+//' \\
805
806
  | while IFS=, read -r s l; do l=\${l:-1}; [ "$l" -gt 0 ] && echo "$s-$((s+l-1))"; done)
806
807
  [ -z "$hunks" ] && hunks="1-$(wc -l < "$f")"
807
808
  rel=\${f#$owner/}
808
- for r in $hunks; do FLAGS="$FLAGS,$rel:$r"; done
809
+ # A Next.js route folder — [id], [[...slug]] — is a glob expression to
810
+ # Stryker, which refuses "Cannot combine a glob expression with a mutation
811
+ # range" and fails the entire run, not just that file. Escaping the brackets
812
+ # stops the path resolving at all. Replacing each with ? keeps it matching
813
+ # that one file and drops the range, so the file is mutated whole: a
814
+ # superset of the diff, slower than a range but never less than what changed.
815
+ case "$rel" in
816
+ *[][*?{}]*) FLAGS="$FLAGS,$(printf '%s' "$rel" | sed 's/[][*?{}]/?/g')"
817
+ GLOBBED="$GLOBBED$rel," ;;
818
+ *) for r in $hunks; do FLAGS="$FLAGS,$rel:$r"; done ;;
819
+ esac
809
820
  done <<< "$OWNED"
810
821
 
811
822
  base=""; RUN=""
@@ -870,12 +881,39 @@ for owner in $OWNERS; do
870
881
  # HEADER every run and repeats the word in its table, so a bare grep reports
871
882
  # survivors on a clean run — worse than not running at all. Stryker marks each
872
883
  # real finding \`[Survived]\`; table and border lines are excluded outright.
873
- case "$TOOL" in
874
- stryker) PATTERN='\\[Survived\\]' ;;
875
- *) PATTERN='[Ss]urvived' ;;
876
- esac
877
- SURVIVED=$(printf '%s\\n' "$OUT" | grep -E "$PATTERN" \\
878
- | grep -vE '^[[:space:]]*[#│|+-]|# *surviv' | head -20)
884
+ # NoCoverage is printed exactly like Survived and means something worse: not
885
+ # "a test missed this mutation" but "no test executes this line at all". The
886
+ # gate matched only [Survived], so a diff whose files have no tests came back
887
+ # PASS — 306 uncovered mutants reported as proven on one commit. It is judged
888
+ # only for files that went in WITH a range: a glob-shaped path is mutated
889
+ # whole, so its untouched lines would report uncovered forever, and a finding
890
+ # nobody can close is what teaches people to --force.
891
+ #
892
+ # The status line carries no file name; the line under it does, as
893
+ # file:line:column. They are paired here rather than grepped separately.
894
+ if [ "$TOOL" = stryker ]; then
895
+ ALL=$(printf '%s\\n' "$OUT" | awk -v globbed="$GLOBBED" '
896
+ /^\\[Survived\\]/ { pend = $0; kind = "s"; next }
897
+ /^\\[NoCoverage\\]/ { pend = $0; kind = "n"; next }
898
+ pend != "" {
899
+ file = $0; sub(/:[0-9]+:[0-9]+$/, "", file)
900
+ if (kind == "s" || index(globbed, "," file ",") == 0) print pend " " $0
901
+ pend = ""
902
+ }
903
+ # A survivor with no location under it is still a survivor, never dropped.
904
+ END { if (pend != "" && kind == "s") print pend }
905
+ ')
906
+ else
907
+ ALL=$(printf '%s\\n' "$OUT" | grep -E '[Ss]urvived' \\
908
+ | grep -vE '^[[:space:]]*[#│|+-]|# *surviv')
909
+ fi
910
+ # The detail list is capped so one bad file cannot bury the output. The cap
911
+ # used to be the whole story, and it hid the WORST file: 20 findings from one
912
+ # file filled it, and a second file with 54 uncovered mutants never appeared
913
+ # at all. A gate whose biggest finding is invisible gets under-fixed. Every
914
+ # file with findings is now named below the cut, worst first.
915
+ FOUND=$(printf '%s\\n' "$ALL" | grep -c .)
916
+ SURVIVED=$(printf '%s\\n' "$ALL" | head -20)
879
917
  FIXED=0
880
918
  NOTE=""
881
919
 
@@ -916,8 +954,17 @@ for owner in $OWNERS; do
916
954
  fi
917
955
 
918
956
  if [ -n "$SURVIVED" ]; then
919
- echo " $label $TOOL — surviving mutants; these lines can break and no test notices:"
957
+ echo " $label $TOOL — these lines can break and no test notices:"
920
958
  printf '%s\\n' "$SURVIVED" | sed 's/^/ /'
959
+ if [ "$FOUND" -gt 20 ]; then
960
+ echo " ... $((FOUND - 20)) more not shown. Every file with findings:"
961
+ if [ "$TOOL" = stryker ]; then
962
+ printf '%s\\n' "$ALL" \\
963
+ | awk '{ f = $NF; sub(/:[0-9]+:[0-9]+$/, "", f); n[f]++ }
964
+ END { for (k in n) printf "%8d %s\\n", n[k], k }' \\
965
+ | sort -rn | sed 's/^/ /'
966
+ fi
967
+ fi
921
968
  [ "$FIXED" -gt 0 ] && echo " ($FIXED baselined survivor(s) now killed — --baseline banks them)"
922
969
  STATUS=1
923
970
  elif [ $RC -ne 0 ]; then
@@ -927,7 +974,7 @@ for owner in $OWNERS; do
927
974
  elif [ -n "$NOTE" ]; then
928
975
  printf '%s\\n' "$NOTE"
929
976
  else
930
- echo " $label $TOOL — ok, no surviving mutants in the changed lines"
977
+ echo " $label $TOOL — ok, nothing survived and nothing was uncovered"
931
978
  [ "$FIXED" -gt 0 ] && echo " ($FIXED baselined survivor(s) now killed — --baseline banks them)"
932
979
  fi
933
980
  done
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spardutti/claude-skills",
3
- "version": "2.19.4",
3
+ "version": "2.20.0",
4
4
  "description": "CLI to install Claude Code skills from the claude-skills collection",
5
5
  "type": "module",
6
6
  "repository": {