@spardutti/claude-skills 2.44.4 → 2.45.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/skill-gate-automark.sh +2 -2
- package/hooks/skill-gate.sh +2 -2
- package/lib/manifest.mjs +4 -1
- package/package.json +1 -1
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
|
|
@@ -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
|
}
|