@spardutti/claude-skills 2.44.4 → 2.46.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.
- package/README.md +1 -0
- package/hooks/ship-gate-files.sh +112 -0
- package/hooks/ship-gate-projects.sh +13 -1
- package/hooks/ship-gate.sh +7 -29
- package/hooks/skill-gate-automark.sh +2 -2
- package/hooks/skill-gate.sh +2 -2
- package/lib/manifest.mjs +4 -1
- package/lib/setup-hook.mjs +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -162,6 +162,7 @@ Every install writes a manifest at `.claude/.claude-skills.json` recording what
|
|
|
162
162
|
- **Pre-check what you already have** in the picker — re-running doubles as an update screen; toggle to add or remove.
|
|
163
163
|
- **Detect stale items** — skills/commands renamed or removed from the catalog upstream are flagged, and the CLI offers to delete them.
|
|
164
164
|
- **Never touch what it didn't install** — the manifest is the CLI's own record; hand-written skills are invisible to it and always safe.
|
|
165
|
+
- **Gate your own skills** — list a hand-written skill under `"custom"` (`"custom": ["brand"]`) and the skill gate enforces its `gate-paths` like an installed one. The CLI keeps `"custom"` as written and never deletes those skills. Never add your own skill to `"skills"`: a sync deletes anything there that is not in the catalog.
|
|
165
166
|
|
|
166
167
|
```bash
|
|
167
168
|
npx @spardutti/claude-skills --sync
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Sourced by ship-gate.sh: which changed files a Stryker run can skip, and what its output says.
|
|
3
|
+
|
|
4
|
+
TEST_RE='(\.|_)(test|spec)\.[^.]+$|(^|/)tests?/'
|
|
5
|
+
|
|
6
|
+
stryker_owner() { # stryker_owner <owner> <owned files>
|
|
7
|
+
b=""; [ "$1" != "." ] && b="$1/"
|
|
8
|
+
[ -z "$GAUNTLET_MUTATE" ] && ! grep -q '\.py$' <<< "$2" \
|
|
9
|
+
&& grep -qs '@stryker-mutator/core' "${b}package.json"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
changed_tests() { # changed_tests <family>
|
|
13
|
+
printf '%s\n' "$CHANGED" | grep -E "$TEST_RE" | while IFS= read -r t; do
|
|
14
|
+
[ -n "$t" ] && [ "$(family_of "$t")" = "$1" ] && printf '%s\n' "$t"
|
|
15
|
+
done
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Everything a file's verdict rests on besides the file itself and the tests.
|
|
19
|
+
# Tests are left out: adding to them can only kill more, so only a lost line counts.
|
|
20
|
+
files_context() { # files_context <owner> <owned files>
|
|
21
|
+
fam=$(family_of "$1/.")
|
|
22
|
+
{ printf '%s\n' "$PROJECT_DIR" "$1" "$(git rev-parse "$BASE" 2>/dev/null)"
|
|
23
|
+
cat "$HERE/ship-gate.sh" "$HERE/ship-gate-projects.sh" "$HERE/ship-gate-files.sh"
|
|
24
|
+
printf '%s\n' "$CHANGED" | grep -vE '\.(md|mdx|txt|rst|adoc)$' | grep -vE "$TEST_RE" \
|
|
25
|
+
| grep -vxF -f <(printf '%s\n' "$2") | while IFS= read -r f; do
|
|
26
|
+
[ -n "$f" ] || continue
|
|
27
|
+
case "$(family_of "$f")" in "$fam"|any) printf '%s\n' "$f"; [ -f "$f" ] && cat "$f" ;; esac
|
|
28
|
+
done; } | git hash-object --stdin
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
file_receipt() { # file_receipt <context> <file>
|
|
32
|
+
printf '/tmp/claude-shipgate-file-%s\n' "$({ printf '%s\n' "$1" "$2"; cat "$2"; } | git hash-object --stdin)"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
test_then() { # test_then <snapshot> <test>: its content when the snapshot was taken
|
|
36
|
+
if grep -qxF "$2" "$1/manifest"; then cat "$1/files/$2" 2>/dev/null
|
|
37
|
+
else git show "$BASE:$2" 2>/dev/null; fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Never `| grep -q` under pipefail: diff exits 1 on any difference, and an early
|
|
41
|
+
# grep exit fails the writer, so a match reads as none.
|
|
42
|
+
tests_lost_lines() { # tests_lost_lines <snapshot> <family>
|
|
43
|
+
[ -f "$1/manifest" ] || return 0
|
|
44
|
+
while IFS= read -r t; do
|
|
45
|
+
[ -n "$t" ] || continue
|
|
46
|
+
case "$(diff <(test_then "$1" "$t") <(cat "$t" 2>/dev/null))" in *$'\n<'*) return 0 ;; esac
|
|
47
|
+
done <<< "$({ cat "$1/manifest"; changed_tests "$2"; } | sort -u)"
|
|
48
|
+
return 1
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Sets OWNED to the files that still need mutating.
|
|
52
|
+
files_split() { # files_split <context> <owner> <label>
|
|
53
|
+
fam=$(family_of "$2/."); kept=""; rest=""
|
|
54
|
+
while IFS= read -r f; do
|
|
55
|
+
[ -n "$f" ] || continue
|
|
56
|
+
r=$(file_receipt "$1" "$f")
|
|
57
|
+
if [ -f "$r" ] && ! tests_lost_lines "$(cat "$r")" "$fam"; then kept="$kept ${f#"$2"/}"
|
|
58
|
+
else rest="$rest$f"$'\n'; fi
|
|
59
|
+
done <<< "$OWNED"
|
|
60
|
+
OWNED=${rest%$'\n'}
|
|
61
|
+
[ -n "$kept" ] || return 0
|
|
62
|
+
echo " $3 stryker — ok, not re-run: passed and unchanged since:$kept"
|
|
63
|
+
echo " A change in a file they call is not re-checked."
|
|
64
|
+
b=""; [ "$2" != "." ] && b="$2/"
|
|
65
|
+
[ -z "$OWNED" ] && stryker_page_tests "$b" "$3"
|
|
66
|
+
return 0
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# A finding with no location cannot be pinned on one file, so it keeps none.
|
|
70
|
+
files_record() { # files_record <context> <owner> <findings>
|
|
71
|
+
[ -n "$(printf '%s\n' "$3" | grep . | grep -vE ':[0-9]+:[0-9]+$')" ] && return 0
|
|
72
|
+
fam=$(family_of "$2/.")
|
|
73
|
+
snap="/tmp/claude-shipgate-tests-$({ printf '%s\n' "$1"; changed_tests "$fam" \
|
|
74
|
+
| while IFS= read -r t; do printf '%s\n' "$t"; cat "$t" 2>/dev/null; done; } | git hash-object --stdin)"
|
|
75
|
+
mkdir -p "$snap/files"; changed_tests "$fam" > "$snap/manifest"
|
|
76
|
+
while IFS= read -r t; do
|
|
77
|
+
[ -f "$t" ] && mkdir -p "$snap/files/$(dirname "$t")" && cp "$t" "$snap/files/$t"
|
|
78
|
+
done < "$snap/manifest"
|
|
79
|
+
while IFS= read -r f; do
|
|
80
|
+
[ -n "$f" ] || continue
|
|
81
|
+
grep -qF " ${f#"$2"/}:" <<< "$3" && continue
|
|
82
|
+
printf '%s\n' "$snap" > "$(file_receipt "$1" "$f")"
|
|
83
|
+
done <<< "$OWNED"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Match a finding, never a summary row. Stryker prints a `# survived` COLUMN
|
|
87
|
+
# HEADER every run and repeats the word in its table, so a bare grep reports
|
|
88
|
+
# survivors on a clean run — worse than not running at all. Stryker marks each
|
|
89
|
+
# real finding `[Survived]`; table and border lines are excluded outright.
|
|
90
|
+
# NoCoverage is printed exactly like Survived and means something worse: not
|
|
91
|
+
# "a test missed this mutation" but "no test executes this line at all". The
|
|
92
|
+
# gate matched only [Survived], so a diff whose files have no tests came back
|
|
93
|
+
# PASS — 306 uncovered mutants reported as proven on one commit. It is judged
|
|
94
|
+
# only for files that went in WITH a range: a glob-shaped path is mutated
|
|
95
|
+
# whole, so its untouched lines would report uncovered forever, and a finding
|
|
96
|
+
# nobody can close is what teaches people to --force.
|
|
97
|
+
#
|
|
98
|
+
# The status line carries no file name; the line under it does, as
|
|
99
|
+
# file:line:column. They are paired here rather than grepped separately.
|
|
100
|
+
stryker_findings() { # stryker_findings <output> <globbed paths, comma-wrapped>
|
|
101
|
+
printf '%s\n' "$1" | awk -v globbed="$2" '
|
|
102
|
+
/^\[Survived\]/ { pend = $0; kind = "s"; next }
|
|
103
|
+
/^\[NoCoverage\]/ { pend = $0; kind = "n"; next }
|
|
104
|
+
pend != "" {
|
|
105
|
+
file = $0; sub(/:[0-9]+:[0-9]+$/, "", file)
|
|
106
|
+
if (kind == "s" || index(globbed, "," file ",") == 0) print pend " " $0
|
|
107
|
+
pend = ""
|
|
108
|
+
}
|
|
109
|
+
# A survivor with no location under it is still a survivor, never dropped.
|
|
110
|
+
END { if (pend != "" && kind == "s") print pend }
|
|
111
|
+
'
|
|
112
|
+
}
|
|
@@ -25,6 +25,18 @@ py_mutmut() {
|
|
|
25
25
|
command -v mutmut >/dev/null 2>&1 && printf 'mutmut\n'
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
# Node that lives only in Docker leaves no Stryker on the host. An executable
|
|
29
|
+
# .stryker-run runs it there instead, taking the arguments stryker would.
|
|
30
|
+
stryker_bin() { if [ -x "$1.stryker-run" ]; then echo ./.stryker-run; else echo npx --no-install stryker; fi; }
|
|
31
|
+
stryker_elsewhere() { # stryker_elsewhere <tool> <base>: after a failed run, say where Stryker could run
|
|
32
|
+
[ "$1" = stryker ] && [ ! -x "$2.stryker-run" ] || return 0
|
|
33
|
+
d=$(cd "${2:-.}" && pwd)
|
|
34
|
+
while [ "$d" != / ]; do [ -d "$d/node_modules/@stryker-mutator/core" ] && return 0; d=$(dirname "$d"); done
|
|
35
|
+
echo " Stryker is not installed on this machine. If Node runs only in Docker, add an"
|
|
36
|
+
echo " executable $2.stryker-run that runs it there and passes on its arguments:"
|
|
37
|
+
echo " docker compose run --rm -T <service> nice -n 19 npx stryker \"\$@\""
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
owner_of() {
|
|
29
41
|
d=$(dirname "$1")
|
|
30
42
|
while :; do
|
|
@@ -127,7 +139,7 @@ project_key() { # project_key <owner> <tool>
|
|
|
127
139
|
fam=$(family_of "$1/.")
|
|
128
140
|
{ printf '%s\n' "$PROJECT_DIR" "$1" "$2" "$(git rev-parse "$BASE" 2>/dev/null)" \
|
|
129
141
|
"$GAUNTLET_NO_MUTATE" "$GAUNTLET_IGNORE_FILES"
|
|
130
|
-
cat "$HERE/ship-gate.sh" "$HERE/ship-gate-projects.sh"
|
|
142
|
+
cat "$HERE/ship-gate.sh" "$HERE/ship-gate-projects.sh" "$HERE/ship-gate-files.sh"
|
|
131
143
|
printf '%s\n' "$CHANGED" | grep -vE '\.(md|mdx|txt|rst|adoc)$' | while IFS= read -r f; do
|
|
132
144
|
[ -n "$f" ] || continue
|
|
133
145
|
case "$(family_of "$f")" in "$fam"|any) printf '%s\n' "$f"; [ -f "$f" ] && cat "$f" ;; esac
|
package/hooks/ship-gate.sh
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
set -uo pipefail
|
|
47
47
|
|
|
48
48
|
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
49
|
-
. "$HERE/ship-gate-projects.sh" && . "$HERE/ship-gate-structure.sh" || { echo "ship-gate: cannot read its helpers in $HERE"; exit 1; }
|
|
49
|
+
. "$HERE/ship-gate-projects.sh" && . "$HERE/ship-gate-structure.sh" && . "$HERE/ship-gate-files.sh" || { echo "ship-gate: cannot read its helpers in $HERE"; exit 1; }
|
|
50
50
|
|
|
51
51
|
MODE=""
|
|
52
52
|
case "${1:-}" in
|
|
@@ -122,7 +122,7 @@ if [ "$MODE" = force ]; then
|
|
|
122
122
|
fi
|
|
123
123
|
gate_report "$RECEIPT_KEY"
|
|
124
124
|
SOURCED=$(printf '%s\n' "$CHANGED" | grep -E "\.($GAUNTLET_SOURCE_EXT)$" \
|
|
125
|
-
| grep -vE
|
|
125
|
+
| grep -vE "$TEST_RE|(^|/)\.stryker-tmp/" | while read -r f; do
|
|
126
126
|
[ -f "$f" ] && printf '%s\n' "$f"
|
|
127
127
|
done)
|
|
128
128
|
|
|
@@ -223,6 +223,7 @@ for owner in $OWNERS; do
|
|
|
223
223
|
OWNED=$(while IFS= read -r f; do
|
|
224
224
|
[ "$(owner_of "$f")" = "$owner" ] && printf '%s\n' "$f"
|
|
225
225
|
done <<< "$FILES" | grep -vE "$GAUNTLET_NO_MUTATE" || true)
|
|
226
|
+
FCTX=""; if stryker_owner "$owner" "$OWNED"; then FCTX=$(files_context "$owner" "$OWNED"); files_split "$FCTX" "$owner" "$label"; fi
|
|
226
227
|
[ -z "$OWNED" ] && continue
|
|
227
228
|
|
|
228
229
|
# ONE comma-joined --mutate value, never a flag per range. Stryker's CLI
|
|
@@ -271,7 +272,7 @@ for owner in $OWNERS; do
|
|
|
271
272
|
# 8 of them on one commit, all long since killed. --mutate already scopes
|
|
272
273
|
# the run to the changed hunks, so incremental buys nothing here and costs
|
|
273
274
|
# a cache that goes stale exactly the way mutmut's did.
|
|
274
|
-
CMD="${RUN}
|
|
275
|
+
CMD="${RUN}$(stryker_bin "$base") run --concurrency $(gate_workers) --mutate '${FLAGS#,}'"; stryker_page_tests "$base" "$label"
|
|
275
276
|
elif [ -z "$PY" ] && [ -f "$base"package.json ]; then
|
|
276
277
|
MISSING="$MISSING $label needs Stryker:
|
|
277
278
|
npm --prefix ${owner} i -D @stryker-mutator/core @stryker-mutator/vitest-runner
|
|
@@ -324,32 +325,9 @@ for owner in $OWNERS; do
|
|
|
324
325
|
T0=$SECONDS; OUT=$(export FILES="$(printf '%s' "$OWNED" | tr '\n' ' ')" MUTATE_FLAGS="--mutate ${FLAGS#,}"; eval "$CMD" 2>&1); RC=$?; T=$((SECONDS - T0))
|
|
325
326
|
[ "$TOOL" = mutmut ] && [ $RC -eq 0 ] && { printf '%s\n' "$OUT" > "$PKEY.out"; cp "$MLOG" "$PKEY.log" 2>/dev/null; }
|
|
326
327
|
[ -n "$SCOPE_RE" ] && [ $RC -eq 0 ] && OUT=$(printf '%s\n' "$OUT" | grep -E "^[[:space:]]*($SCOPE_RE)\.x")
|
|
327
|
-
# Match a finding, never a summary row. Stryker prints a `# survived` COLUMN
|
|
328
|
-
# HEADER every run and repeats the word in its table, so a bare grep reports
|
|
329
|
-
# survivors on a clean run — worse than not running at all. Stryker marks each
|
|
330
|
-
# real finding `[Survived]`; table and border lines are excluded outright.
|
|
331
|
-
# NoCoverage is printed exactly like Survived and means something worse: not
|
|
332
|
-
# "a test missed this mutation" but "no test executes this line at all". The
|
|
333
|
-
# gate matched only [Survived], so a diff whose files have no tests came back
|
|
334
|
-
# PASS — 306 uncovered mutants reported as proven on one commit. It is judged
|
|
335
|
-
# only for files that went in WITH a range: a glob-shaped path is mutated
|
|
336
|
-
# whole, so its untouched lines would report uncovered forever, and a finding
|
|
337
|
-
# nobody can close is what teaches people to --force.
|
|
338
|
-
#
|
|
339
|
-
# The status line carries no file name; the line under it does, as
|
|
340
|
-
# file:line:column. They are paired here rather than grepped separately.
|
|
341
328
|
if [ "$TOOL" = stryker ]; then
|
|
342
|
-
ALL=$(
|
|
343
|
-
|
|
344
|
-
/^\[NoCoverage\]/ { pend = $0; kind = "n"; next }
|
|
345
|
-
pend != "" {
|
|
346
|
-
file = $0; sub(/:[0-9]+:[0-9]+$/, "", file)
|
|
347
|
-
if (kind == "s" || index(globbed, "," file ",") == 0) print pend " " $0
|
|
348
|
-
pend = ""
|
|
349
|
-
}
|
|
350
|
-
# A survivor with no location under it is still a survivor, never dropped.
|
|
351
|
-
END { if (pend != "" && kind == "s") print pend }
|
|
352
|
-
')
|
|
329
|
+
ALL=$(stryker_findings "$OUT" "$GLOBBED")
|
|
330
|
+
[ -n "$FCTX" ] && [ $RC -eq 0 ] && files_record "$FCTX" "$owner" "$ALL"
|
|
353
331
|
else
|
|
354
332
|
ALL=$(printf '%s\n' "$OUT" | grep -E '[Ss]urvived' \
|
|
355
333
|
| grep -vE '^[[:space:]]*[#│|+-]|# *surviv')
|
|
@@ -433,7 +411,7 @@ for owner in $OWNERS; do
|
|
|
433
411
|
STATUS=1
|
|
434
412
|
elif [ $RC -ne 0 ]; then
|
|
435
413
|
echo " $label $TOOL (${T}s) — the run failed:"
|
|
436
|
-
printf '%s\n' "$OUT" | tail -12 | sed 's/^/ /'
|
|
414
|
+
printf '%s\n' "$OUT" | tail -12 | sed 's/^/ /'; stryker_elsewhere "$TOOL" "$base"
|
|
437
415
|
[ "$STATUS" = 0 ] && STATUS=2
|
|
438
416
|
elif [ -n "$NOTE" ]; then
|
|
439
417
|
printf '%s\n' "$NOTE"
|
|
@@ -24,9 +24,9 @@ SKILL_NAME=$(printf '%s' "$INPUT" | grep -o '"skill":"[^"]*"' | head -1 | sed 's
|
|
|
24
24
|
if [ -n "$SKILL_NAME" ]; then
|
|
25
25
|
# Sanitize: only allow [A-Za-z0-9_-] in the marker filename.
|
|
26
26
|
SAFE_NAME=$(printf '%s' "$SKILL_NAME" | tr -cd 'A-Za-z0-9_-')
|
|
27
|
-
# Only skills the CLI installed
|
|
27
|
+
# Only skills the CLI installed, or the user listed under "custom", get acked.
|
|
28
28
|
MANIFEST="$(dirname "$(cd "$(dirname "$0")" && pwd)")/.claude-skills.json"
|
|
29
|
-
if [ -n "$SAFE_NAME" ] && tr -d ' \n' 2>/dev/null < "$MANIFEST" | grep -
|
|
29
|
+
if [ -n "$SAFE_NAME" ] && tr -d ' \n' 2>/dev/null < "$MANIFEST" | grep -oE '"(skills|custom)":\[[^]]*\]' | grep -q "\"$SAFE_NAME\""; then
|
|
30
30
|
touch "/tmp/claude-skill-loaded-$KEY-$SAFE_NAME"
|
|
31
31
|
# The ack path holds a key the model cannot know, so without this line its
|
|
32
32
|
# first write is always denied just to learn it.
|
package/hooks/skill-gate.sh
CHANGED
|
@@ -116,7 +116,7 @@ MISSING=""
|
|
|
116
116
|
ACKS=""
|
|
117
117
|
MANIFESTS=""
|
|
118
118
|
if [ -n "$TARGETS" ]; then
|
|
119
|
-
INSTALLED=$(tr -d ' \n' 2>/dev/null < "$PROJECT_DIR/.claude/.claude-skills.json" | grep -
|
|
119
|
+
INSTALLED=$(tr -d ' \n' 2>/dev/null < "$PROJECT_DIR/.claude/.claude-skills.json" | grep -oE '"(skills|custom)":\[[^]]*\]')
|
|
120
120
|
MANIFESTS=$(find "$PROJECT_DIR" -maxdepth 4 \( -name node_modules -o -name .git -o -name .venv -o -name dist \) -prune -o \( -name package.json -o -name pyproject.toml -o -name requirements.txt \) -print 2>/dev/null)
|
|
121
121
|
fi
|
|
122
122
|
if [ -n "$TARGETS" ]; then
|
|
@@ -136,7 +136,7 @@ if [ -n "$TARGETS" ]; then
|
|
|
136
136
|
SNAME=$(sed -n 's/^name:[[:space:]]*//p' "$SKILL_FILE" | head -1 | sed "$UNQUOTE")
|
|
137
137
|
[ -z "$SPATHS" ] && continue
|
|
138
138
|
[ -z "$SNAME" ] && continue
|
|
139
|
-
case "$INSTALLED" in *"\"$SNAME\""*) ;; *) continue ;; esac # automark
|
|
139
|
+
case "$INSTALLED" in *"\"$SNAME\""*) ;; *) continue ;; esac # automark marks only installed and custom skills
|
|
140
140
|
|
|
141
141
|
HIT=""
|
|
142
142
|
for G in $(printf '%s' "$SPATHS" | tr ',' ' '); do
|
package/lib/manifest.mjs
CHANGED
|
@@ -16,6 +16,7 @@ export async function readManifest(targetDir = process.cwd()) {
|
|
|
16
16
|
skills: data.skills ?? [],
|
|
17
17
|
commands: data.commands ?? [],
|
|
18
18
|
agents: data.agents ?? [],
|
|
19
|
+
custom: data.custom ?? [],
|
|
19
20
|
};
|
|
20
21
|
} catch {
|
|
21
22
|
return null; // no manifest — a pre-manifest project or a fresh install
|
|
@@ -23,15 +24,17 @@ export async function readManifest(targetDir = process.cwd()) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
// Record exactly what the CLI has installed. This is the CLI's source of truth —
|
|
26
|
-
// it never deletes anything not listed here.
|
|
27
|
+
// it never deletes anything not listed here. `custom` is the user's own gated skills, carried over untouched.
|
|
27
28
|
export async function writeManifest(targetDir, { catalogVersion, skills, commands, agents }) {
|
|
28
29
|
await mkdir(join(targetDir, ".claude"), { recursive: true });
|
|
30
|
+
const custom = (await readManifest(targetDir))?.custom ?? [];
|
|
29
31
|
const data = {
|
|
30
32
|
catalogVersion,
|
|
31
33
|
installedAt: new Date().toISOString(),
|
|
32
34
|
skills: [...new Set(skills)].sort(),
|
|
33
35
|
commands: [...new Set(commands)].sort(),
|
|
34
36
|
agents: [...new Set(agents)].sort(),
|
|
37
|
+
custom,
|
|
35
38
|
};
|
|
36
39
|
await writeFile(manifestPath(targetDir), JSON.stringify(data, null, 2) + "\n");
|
|
37
40
|
}
|
package/lib/setup-hook.mjs
CHANGED
|
@@ -28,6 +28,7 @@ const GAUNTLET_FILENAME = "gauntlet.sh";
|
|
|
28
28
|
const SHIP_GATE_FILENAME = "ship-gate.sh";
|
|
29
29
|
const SHIP_GATE_PROJECTS_FILENAME = "ship-gate-projects.sh";
|
|
30
30
|
const SHIP_GATE_STRUCTURE_FILENAME = "ship-gate-structure.sh";
|
|
31
|
+
const SHIP_GATE_FILES_FILENAME = "ship-gate-files.sh";
|
|
31
32
|
const SHIP_GATE_HOOK_FILENAME = "ship-gate-hook.sh";
|
|
32
33
|
const VERSION_CHECK_FILENAME = "version-check.sh";
|
|
33
34
|
const LEGACY_EVAL_FILENAME = "skill-forced-eval-hook.sh";
|
|
@@ -108,6 +109,7 @@ export async function setupHook(targetDir = process.cwd()) {
|
|
|
108
109
|
SHIP_GATE_FILENAME,
|
|
109
110
|
SHIP_GATE_PROJECTS_FILENAME,
|
|
110
111
|
SHIP_GATE_STRUCTURE_FILENAME,
|
|
112
|
+
SHIP_GATE_FILES_FILENAME,
|
|
111
113
|
SHIP_GATE_HOOK_FILENAME,
|
|
112
114
|
VERSION_CHECK_FILENAME,
|
|
113
115
|
]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spardutti/claude-skills",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.46.0",
|
|
4
4
|
"description": "Guardrails for Claude Code — best-practice skills, planning commands, and delivery gates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"claude-skills": "bin/cli.mjs"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"prepack": "cp ../README.md README.md && mkdir -p hooks && cp ../scripts/skill-gate.sh ../scripts/skill-gate-automark.sh ../scripts/skill-application-gate.sh ../scripts/gauntlet.sh ../scripts/ship-gate.sh ../scripts/ship-gate-projects.sh ../scripts/ship-gate-structure.sh ../scripts/ship-gate-hook.sh ../scripts/version-check.sh hooks/",
|
|
14
|
+
"prepack": "cp ../README.md README.md && mkdir -p hooks && cp ../scripts/skill-gate.sh ../scripts/skill-gate-automark.sh ../scripts/skill-application-gate.sh ../scripts/gauntlet.sh ../scripts/ship-gate.sh ../scripts/ship-gate-projects.sh ../scripts/ship-gate-structure.sh ../scripts/ship-gate-files.sh ../scripts/ship-gate-hook.sh ../scripts/version-check.sh hooks/",
|
|
15
15
|
"postpack": "git checkout -- README.md && rm -rf hooks"
|
|
16
16
|
},
|
|
17
17
|
"engines": {
|