@spardutti/claude-skills 2.51.2 → 2.51.3

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/README.md CHANGED
@@ -36,7 +36,7 @@ npx @spardutti/claude-skills
36
36
  ```
37
37
 
38
38
  ```text
39
- Claude Skills Installer v2.25.0
39
+ Claude Skills Installer v2.51.2
40
40
 
41
41
  ── Frontend ──────────────────────────────
42
42
  ◉ react ◯ tanstack-query
@@ -76,7 +76,8 @@ install adds the part that does not negotiate:
76
76
  - A `Stop` hook that runs your typecheck and tests on the changed files every turn,
77
77
  and refuses to let the turn end red.
78
78
  - A delivery gate that **changes your code on purpose and checks whether any test
79
- notices.** When none do, the PR does not open.
79
+ notices.** When none do, the PR does not open. The same gate runs one
80
+ `/code-review` round for logic bugs and fixes what it finds.
80
81
 
81
82
  That last one is the difference. Your agent says the tests pass. This finds out.
82
83
 
@@ -110,3 +110,22 @@ stryker_findings() { # stryker_findings <output> <globbed paths, comma-wrapped>
110
110
  END { if (pend != "" && kind == "s") print pend }
111
111
  '
112
112
  }
113
+
114
+ # A survivor needs a test run against it. Survivors with 0 tests per mutant and
115
+ # nothing killed mean the runner filtered every test out (stryker-js#6210, Vitest 5).
116
+ stryker_zero_tests() { # stryker_zero_tests <output> <label> <seconds>
117
+ grep -q '^\[Survived\]' <<< "$1" && grep -q 'Ran 0\.00 tests per mutant' <<< "$1" || return 1
118
+ [ "$(awk -F'|' '/^ *All files/ { gsub(/ /, "", $4); print $4 }' <<< "$1")" = 0 ] || return 1
119
+ echo " $2 stryker (${3}s) — its survivors ran 0 tests, so they prove nothing and are dropped."
120
+ echo " On Vitest 5 this is stryker-js#6210: pin vitest@4 until a fixed runner ships."
121
+ }
122
+
123
+ # Sets ALL from a Stryker run. Fails when a broken run leaves nothing provable.
124
+ stryker_judge() { # uses OUT GLOBBED FCTX RC owner label T from ship-gate.sh
125
+ ALL=$(stryker_findings "$OUT" "$GLOBBED")
126
+ if stryker_zero_tests "$OUT" "$label" "$T"; then
127
+ ALL=$(grep -v '^\[Survived\]' <<< "$ALL"); [ -n "$ALL" ]; return
128
+ fi
129
+ [ -n "$FCTX" ] && [ "$RC" -eq 0 ] && files_record "$FCTX" "$owner" "$ALL"
130
+ return 0
131
+ }
@@ -324,8 +324,7 @@ for owner in $OWNERS; do
324
324
  [ "$TOOL" = mutmut ] && [ $RC -eq 0 ] && { printf '%s\n' "$OUT" > "$PKEY.out"; cp "$MLOG" "$PKEY.log" 2>/dev/null; }
325
325
  [ -n "$SCOPE_RE" ] && [ $RC -eq 0 ] && OUT=$(printf '%s\n' "$OUT" | grep -E "^[[:space:]]*($SCOPE_RE)\.x")
326
326
  if [ "$TOOL" = stryker ]; then
327
- ALL=$(stryker_findings "$OUT" "$GLOBBED")
328
- [ -n "$FCTX" ] && [ $RC -eq 0 ] && files_record "$FCTX" "$owner" "$ALL"
327
+ stryker_judge || { [ "$STATUS" = 0 ] && STATUS=2; continue; }
329
328
  else
330
329
  ALL=$(printf '%s\n' "$OUT" | grep -E '[Ss]urvived' \
331
330
  | grep -vE '^[[:space:]]*[#│|+-]|# *surviv')
@@ -35,6 +35,7 @@ const CONFIG = {
35
35
  tempDirName: ".stryker-tmp",
36
36
  };
37
37
  import { IGNORER, IGNORER_VERSION } from "./stryker-ignorer.mjs";
38
+ import { TS7_TSCONFIG, usesTs7 } from "./stryker-ts7.mjs";
38
39
 
39
40
  async function exists(p) {
40
41
  try {
@@ -69,6 +70,7 @@ export async function scaffoldStryker(projectDir, rel) {
69
70
  const kept = [];
70
71
  const patched = [];
71
72
  const unreadable = [];
73
+ const ts7 = await usesTs7(dir, projectDir);
72
74
 
73
75
  // A project that already runs Stryker keeps every setting it chose. The two
74
76
  // entries that wire the ignorer in are added, because a config written before
@@ -95,8 +97,9 @@ export async function scaffoldStryker(projectDir, rel) {
95
97
  const coverage = cfg.coverageAnalysis;
96
98
  const canSetStatic = coverage === undefined || coverage === "perTest";
97
99
  const needsStatic = cfg.ignoreStatic !== true && canSetStatic;
100
+ const needsTsconfig = ts7 && cfg.tsconfigFile === undefined;
98
101
 
99
- if (!needsIgnorer && !needsStatic) {
102
+ if (!needsIgnorer && !needsStatic && !needsTsconfig) {
100
103
  kept.push(`${at}stryker.config.json`);
101
104
  } else {
102
105
  if (needsIgnorer) {
@@ -107,12 +110,14 @@ export async function scaffoldStryker(projectDir, rel) {
107
110
  cfg.coverageAnalysis = "perTest";
108
111
  cfg.ignoreStatic = true;
109
112
  }
113
+ if (needsTsconfig) cfg.tsconfigFile = TS7_TSCONFIG;
110
114
  await writeFile(configPath, JSON.stringify(cfg, null, 2) + "\n");
111
115
  patched.push(`${at}stryker.config.json`);
112
116
  }
113
117
  }
114
118
  } else {
115
- await writeFile(configPath, JSON.stringify(CONFIG, null, 2) + "\n");
119
+ const config = ts7 ? { ...CONFIG, tsconfigFile: TS7_TSCONFIG } : CONFIG;
120
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
116
121
  wrote.push(`${at}stryker.config.json`);
117
122
  }
118
123
 
@@ -0,0 +1,26 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ // Stryker rewrites tsconfig through TypeScript's JS API, which 7 no longer has
5
+ // (stryker-js#6111). Naming a file that does not exist skips the rewrite.
6
+ export const TS7_TSCONFIG = "no-tsconfig-rewrite-typescript-7-has-no-compiler-api.json";
7
+
8
+ async function readJson(p) {
9
+ try {
10
+ return JSON.parse(await readFile(p, "utf-8"));
11
+ } catch {
12
+ return null;
13
+ }
14
+ }
15
+
16
+ // A range like "catalog:" or "workspace:*" carries no number, so the installed copy wins.
17
+ export async function usesTs7(...dirs) {
18
+ for (const d of dirs) {
19
+ const installed = await readJson(join(d, "node_modules", "typescript", "package.json"));
20
+ const pkg = await readJson(join(d, "package.json"));
21
+ const version = installed?.version ?? pkg?.devDependencies?.typescript ?? pkg?.dependencies?.typescript;
22
+ const major = Number(version?.match(/\d+/)?.[0]);
23
+ if (major) return major >= 7;
24
+ }
25
+ return false;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spardutti/claude-skills",
3
- "version": "2.51.2",
3
+ "version": "2.51.3",
4
4
  "description": "Guardrails for Claude Code — best-practice skills, planning commands, and delivery gates.",
5
5
  "type": "module",
6
6
  "repository": {