@spardutti/claude-skills 2.33.6 → 2.33.8

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/bin/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { confirm } from "@inquirer/prompts";
4
4
  import chalk from "chalk";
5
- import { readFileSync } from "node:fs";
5
+ import { readFileSync, existsSync } from "node:fs";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { dirname, join } from "node:path";
8
8
  import { fetchSkills, fetchCommands, fetchAgents } from "../lib/github.mjs";
@@ -10,6 +10,7 @@ import { promptSkillSelection, promptCommandSelection, promptRemoval } from "../
10
10
  import { installSkills, installCommands, installRequiredAgents } from "../lib/install.mjs";
11
11
  import { makeLocalSource } from "../lib/local.mjs";
12
12
  import { runPostInstall } from "../lib/post-install.mjs";
13
+ import { setupHook } from "../lib/setup-hook.mjs";
13
14
  import {
14
15
  readManifest, writeManifest, computeOrphans, computeRemovals, scanInstalled, removeArtifacts,
15
16
  MANIFEST_FILE,
@@ -20,7 +21,7 @@ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-
20
21
  const CWD = process.cwd();
21
22
 
22
23
  // `--sync`: re-install everything the manifest records (refreshed to the latest
23
- // catalog) and prune anything removed upstream — no interactive menu.
24
+ // catalog), refresh hooks the project already has, and prune anything removed upstream.
24
25
  async function runSync(manifest, catalog) {
25
26
  if (!manifest) {
26
27
  console.log(` ${chalk.yellow("Nothing to sync")} — no manifest in this project. Run without --sync first.\n`);
@@ -38,6 +39,7 @@ async function runSync(manifest, catalog) {
38
39
  if (skills.length > 0) { console.log(); await installSkills(skills); }
39
40
  if (commands.length > 0) { console.log(); await installCommands(commands); }
40
41
  const { installed } = await installRequiredAgents(commands, catalog.agents, CWD);
42
+ if (existsSync(join(CWD, ".claude", "hooks", "skill-gate.sh"))) { console.log(); await setupHook(CWD); }
41
43
 
42
44
  await writeManifest(CWD, {
43
45
  catalogVersion: pkg.version,
@@ -26,6 +26,12 @@ if [ -n "$SKILL_NAME" ]; then
26
26
  SAFE_NAME=$(printf '%s' "$SKILL_NAME" | tr -cd 'A-Za-z0-9_-')
27
27
  if [ -n "$SAFE_NAME" ]; then
28
28
  touch "/tmp/claude-skill-loaded-$KEY-$SAFE_NAME"
29
+ # The ack path holds a key the model cannot know, so without this line its
30
+ # first write is always denied just to learn it.
31
+ if [ ! -f "/tmp/claude-skill-acked-$KEY-$SAFE_NAME" ]; then
32
+ CTX="Skill $SAFE_NAME is loaded. Before your next code write, say which of its rules bear on that file, or that it does not apply. Then run this Bash call, or the write is blocked:\\n touch /tmp/claude-skill-acked-$KEY-$SAFE_NAME\\nLoaded several skills? Put every path in one touch."
33
+ printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$CTX"
34
+ fi
29
35
  fi
30
36
  fi
31
37
 
@@ -108,6 +108,7 @@ fi
108
108
  # without both fields keep the old behaviour, so this tightens nothing that was
109
109
  # not deliberately declared.
110
110
  MISSING=""
111
+ ACKS=""
111
112
  MANIFESTS=""
112
113
  if [ -n "$TARGETS" ]; then
113
114
  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)
@@ -168,7 +169,7 @@ if [ -n "$TARGETS" ]; then
168
169
 
169
170
  SAFE=$(printf '%s' "$SNAME" | tr -cd 'A-Za-z0-9_-')
170
171
  [ -f "/tmp/claude-skill-loaded-$KEY-$SAFE" ] && continue
171
- case " $MISSING " in *" $SNAME "*) ;; *) MISSING="$MISSING $SNAME" ;; esac
172
+ case " $MISSING " in *" $SNAME "*) ;; *) MISSING="$MISSING $SNAME"; ACKS="$ACKS /tmp/claude-skill-acked-$KEY-$SAFE" ;; esac
172
173
  done
173
174
  fi
174
175
 
@@ -176,7 +177,7 @@ if [ -n "$MISSING" ]; then
176
177
  LIST=$(printf '%s' "$MISSING" | sed 's/^ //; s/ /, /g')
177
178
  FIRST=$(printf '%s' "$MISSING" | awk '{print $1}')
178
179
  cat <<EOF
179
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"BLOCKED: this file's stack has mandatory skills that are not loaded: $LIST.\n\nThere is no SKIP for these. The file matches each skill's declared paths AND this project depends on a package that skill tracks, so they apply as a matter of fact, not judgement. Touching the gate marker will not clear them.\n\nCall Skill($FIRST) now — then every other name in the list — and retry the edit."}}
180
+ {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"BLOCKED: this file's stack has mandatory skills that are not loaded: $LIST.\n\nThere is no SKIP for these. The file matches each skill's declared paths AND this project depends on a package that skill tracks, so they apply as a matter of fact, not judgement. Touching the gate marker will not clear them.\n\nCall Skill($FIRST) now — then every other name in the list. Then say which of their rules bear on this file, ack them in one Bash call, and retry the edit:\n touch$ACKS"}}
180
181
  EOF
181
182
  exit 0
182
183
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spardutti/claude-skills",
3
- "version": "2.33.6",
3
+ "version": "2.33.8",
4
4
  "description": "Guardrails for Claude Code — best-practice skills, planning commands, and delivery gates.",
5
5
  "type": "module",
6
6
  "repository": {