@spardutti/claude-skills 1.28.0 → 1.29.1

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
@@ -101,18 +101,22 @@ The CLI will:
101
101
 
102
102
  After installing skills, the CLI asks if you want to set up automatic skill evaluation. If you say yes, it will:
103
103
 
104
- - **Create a PreToolUse gate hook** at `.claude/hooks/skill-gate.sh`
104
+ - **Install two hooks** in `.claude/hooks/`:
105
+ - `skill-gate.sh` — PreToolUse gate on `Write|Edit|MultiEdit`
106
+ - `skill-gate-automark.sh` — PostToolUse on `Skill` that auto-clears the gate
105
107
  - **Update your `CLAUDE.md`** with the skill-evaluation rule
106
108
 
107
- The gate hard-blocks `Write`, `Edit`, and `MultiEdit` tool calls until Claude has evaluated the available skills and emitted the literal token `[skills-checked]` in its response. Once emitted, every subsequent edit in that turn passes through. The next user prompt resets the gate.
109
+ The gate hard-blocks `Write`, `Edit`, and `MultiEdit` until a per-session marker file exists at `/tmp/claude-skill-gate-<SESSION_ID>`. The marker is created automatically the first time Claude invokes any `Skill()` in the session so the normal flow is: Claude lists skills as ACTIVATE/SKIP, calls `Skill()` for the ACTIVATE ones, and the gate clears for the rest of the session. If every skill is SKIP, Claude clears the gate by running `touch /tmp/claude-skill-gate-<SESSION_ID>`.
110
+
111
+ The marker is **per-session, not per-turn** — short follow-ups like "yes" don't re-lock the gate after evaluation has already happened.
108
112
 
109
113
  Unlike a soft reminder injected into context (which Claude can ignore), the gate denies the tool call outright — so the only path forward is to actually evaluate skills.
110
114
 
111
- The gate auto-passes when the project has no skills installed, so it's safe to leave on globally.
115
+ The gate auto-passes when the project has no `.claude/skills/*/SKILL.md` files, so it's safe to leave on globally.
112
116
 
113
117
  ### What gets created
114
118
 
115
- **`.claude/settings.json`** — Registers the gate on file-writing tools:
119
+ **`.claude/settings.json`** — Registers both hooks:
116
120
 
117
121
  ```json
118
122
  {
@@ -127,12 +131,23 @@ The gate auto-passes when the project has no skills installed, so it's safe to l
127
131
  }
128
132
  ]
129
133
  }
134
+ ],
135
+ "PostToolUse": [
136
+ {
137
+ "matcher": "Skill",
138
+ "hooks": [
139
+ {
140
+ "type": "command",
141
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/skill-gate-automark.sh"
142
+ }
143
+ ]
144
+ }
130
145
  ]
131
146
  }
132
147
  }
133
148
  ```
134
149
 
135
- **`CLAUDE.md`** — Appends the evaluation rule, including the `[skills-checked]` sentinel that the gate looks for.
150
+ **`CLAUDE.md`** — Appends the skill-evaluation rule that tells Claude to enumerate skills as ACTIVATE/SKIP and call `Skill()` for ACTIVATE entries before writing code.
136
151
 
137
152
  ## Manual Install
138
153
 
@@ -2,25 +2,15 @@ import { mkdir, writeFile, readFile, chmod } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
3
 
4
4
  // PreToolUse gate on Write|Edit|MultiEdit. Blocks the tool call unless
5
- // a per-prompt marker file exists at /tmp/claude-skill-gate-<UUID>,
6
- // where <UUID> is the uuid of the most recent typed user prompt. The
7
- // assistant creates the marker via Bash; Bash output is flushed
8
- // synchronously, so the marker is race-free against the message-buffering
9
- // behavior that broke the earlier text-sentinel approach (Claude Code
10
- // writes assistant content blocks to JSONL only after the turn completes,
11
- // so [skills-checked] text emitted in the same message as a tool_use is
12
- // invisible to PreToolUse). /tmp keeps the marker out of the project
13
- // directory so it can't leak into commits.
14
- //
15
- // LAST_PROMPT_UUID is detected by matching user-role lines whose content
16
- // is a JSON string ("role":"user","content":"..."), which excludes
17
- // tool_results, skill loads, task notifications, and slash-command
18
- // payloads (all of which use array content).
5
+ // a per-session marker file exists at /tmp/claude-skill-gate-<SESSION_ID>.
6
+ // Per-session (not per-prompt) so simple confirmations like "yes" don't
7
+ // re-lock the gate after evaluation has already happened in the session.
8
+ // The PostToolUse hook on Skill creates the marker automatically; for
9
+ // all-SKIP cases the model can `touch` the path manually.
19
10
  //
20
11
  // Pass-through cases:
21
12
  // - project has no .claude/skills/*/SKILL.md files
22
- // - transcript_path missing or unreadable
23
- // - no typed user prompt found in transcript
13
+ // - session_id missing from hook input
24
14
  const GATE_SCRIPT = `#!/bin/bash
25
15
  # PreToolUse gate: forces skill evaluation before file-writing tools run.
26
16
 
@@ -33,48 +23,34 @@ if ! find "$PROJECT_DIR" -path '*/.claude/skills/*/SKILL.md' 2>/dev/null | grep
33
23
  exit 0
34
24
  fi
35
25
 
36
- TRANSCRIPT=$(printf '%s' "$INPUT" | grep -o '"transcript_path":"[^"]*"' | head -1 | sed 's/"transcript_path":"//; s/"$//')
37
- if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
38
- exit 0
39
- fi
40
-
41
- LAST_LINE=$(grep -E '"role":"user","content":"' "$TRANSCRIPT" 2>/dev/null | tail -1)
42
- LAST_PROMPT_UUID=$(printf '%s' "$LAST_LINE" | grep -o '"uuid":"[^"]*"' | head -1 | sed 's/"uuid":"//;s/"$//')
43
- if [ -z "$LAST_PROMPT_UUID" ]; then
26
+ SESSION_ID=$(printf '%s' "$INPUT" | grep -o '"session_id":"[^"]*"' | head -1 | sed 's/"session_id":"//; s/"$//')
27
+ if [ -z "$SESSION_ID" ]; then
44
28
  exit 0
45
29
  fi
46
30
 
47
- MARKER="/tmp/claude-skill-gate-$LAST_PROMPT_UUID"
31
+ MARKER="/tmp/claude-skill-gate-$SESSION_ID"
48
32
  if [ -f "$MARKER" ]; then
49
- find /tmp -maxdepth 1 -name 'claude-skill-gate-*' ! -name "claude-skill-gate-$LAST_PROMPT_UUID" -delete 2>/dev/null
50
33
  exit 0
51
34
  fi
52
35
 
53
36
  cat <<EOF
54
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Skill evaluation required before file edits. (1) List each available skill as ACTIVATE or SKIP with a one-line reason. (2) Call Skill() for any ACTIVATE entries this auto-clears the gate. If all skills are SKIP, run this exact Bash command instead: touch /tmp/claude-skill-gate-$LAST_PROMPT_UUID (3) Then retry the file edit. The marker is unique to this user prompt and is auto-cleaned on the next prompt."}}
37
+ {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"BLOCKED: skill evaluation required before file edits in this session.\\n\\nStep 1 evaluate every available skill as ACTIVATE or SKIP with a one-line reason.\\n\\nStep 2 you MUST take EXACTLY ONE of these tool actions to clear the gate. Listing skills in text is NOT enough; retrying the edit without doing one of these will be denied again:\\n (a) If any skill is ACTIVATE call Skill(name) for it. This auto-clears the gate.\\n (b) If ALL skills are SKIP run this Bash tool call: touch /tmp/claude-skill-gate-$SESSION_ID\\n\\nStep 3 only after Step 2 completes, retry the file edit."}}
55
38
  EOF
56
39
  exit 0
57
40
  `;
58
41
 
59
- // PostToolUse on Skill: auto-creates the gate marker so the model doesn't
60
- // need an explicit touch. Uses the same UUID-detection logic as the gate.
42
+ // PostToolUse on Skill: auto-creates the per-session gate marker.
61
43
  const AUTO_MARK_SCRIPT = `#!/bin/bash
62
- # PostToolUse on Skill: auto-marks the skill-gate as satisfied.
44
+ # PostToolUse on Skill: auto-marks the skill-gate as satisfied for the session.
63
45
 
64
46
  INPUT=$(cat)
65
47
 
66
- TRANSCRIPT=$(printf '%s' "$INPUT" | grep -o '"transcript_path":"[^"]*"' | head -1 | sed 's/"transcript_path":"//; s/"$//')
67
- if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
68
- exit 0
69
- fi
70
-
71
- LAST_LINE=$(grep -E '"role":"user","content":"' "$TRANSCRIPT" 2>/dev/null | tail -1)
72
- LAST_PROMPT_UUID=$(printf '%s' "$LAST_LINE" | grep -o '"uuid":"[^"]*"' | head -1 | sed 's/"uuid":"//;s/"$//')
73
- if [ -z "$LAST_PROMPT_UUID" ]; then
48
+ SESSION_ID=$(printf '%s' "$INPUT" | grep -o '"session_id":"[^"]*"' | head -1 | sed 's/"session_id":"//; s/"$//')
49
+ if [ -z "$SESSION_ID" ]; then
74
50
  exit 0
75
51
  fi
76
52
 
77
- touch "/tmp/claude-skill-gate-$LAST_PROMPT_UUID"
53
+ touch "/tmp/claude-skill-gate-$SESSION_ID"
78
54
  exit 0
79
55
  `;
80
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spardutti/claude-skills",
3
- "version": "1.28.0",
3
+ "version": "1.29.1",
4
4
  "description": "CLI to install Claude Code skills from the claude-skills collection",
5
5
  "type": "module",
6
6
  "bin": {