@spardutti/claude-skills 2.60.1 → 2.60.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 +23 -1
- package/hooks/registry-lib.mjs +2 -1
- package/hooks/registry.mjs +7 -1
- package/hooks/ship-prep.sh +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,6 +149,7 @@ The CLI installs three kinds of artifact into your project's `.claude/` director
|
|
|
149
149
|
- **Skills** → `.claude/skills/` — playbooks Claude loads while coding.
|
|
150
150
|
- **Commands** → `.claude/commands/` — slash commands you invoke directly.
|
|
151
151
|
- **Subagents** → `.claude/agents/` — declared by commands via `requires-agents`, installed for you.
|
|
152
|
+
A command that needs another names it in `requires-commands`, and that one is installed too.
|
|
152
153
|
|
|
153
154
|
It then offers to install the hooks. Two guard the **front**: nothing is written
|
|
154
155
|
until your skills have been evaluated *and* applied to the file being written.
|
|
@@ -159,6 +160,27 @@ receipt proving the delivery gate passed on exactly that code.
|
|
|
159
160
|
Full details — every hook, every marker, every config key, and what to do when one
|
|
160
161
|
blocks something it shouldn't — are in **[HOOKS.md](./HOOKS.md)**.
|
|
161
162
|
|
|
163
|
+
### The registry: systems a plan must reuse
|
|
164
|
+
|
|
165
|
+
Grep only finds the words a request uses. Ask for "a skill that fires at the nearby
|
|
166
|
+
enemy" and nothing matches the aiming code, so Claude writes new aiming code. A
|
|
167
|
+
registry entry is a small file beside the system that names it once:
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
# sim/aim.registry.yaml
|
|
171
|
+
name: aim-rules
|
|
172
|
+
summary: how a skill picks which enemy to hit
|
|
173
|
+
location: sim/aim.gd
|
|
174
|
+
extend_by: add a value to `Aim.Rule` and its branch in `Aim.pick`
|
|
175
|
+
never: sort or filter enemies inside a skill
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`/plan-feature` reads every entry and must mark each one `reuse` or `skip`. A plan
|
|
179
|
+
fails if it says `reuse` while its stubs never use the names in `extend_by`, and an
|
|
180
|
+
entry whose names left the code is flagged as stale. Nothing else reads the registry.
|
|
181
|
+
`/registry aim` drafts an entry for your yes. Replayed on a real game feature, the
|
|
182
|
+
targeting rule went inside the skill without an entry, and into `Aim` with one, 5 runs of 5.
|
|
183
|
+
|
|
162
184
|
### Tracking & Updates
|
|
163
185
|
|
|
164
186
|
Every install writes a manifest at `.claude/.claude-skills.json` recording what the CLI installed and the catalog version. On the next run it uses the manifest to:
|
|
@@ -172,7 +194,7 @@ Every install writes a manifest at `.claude/.claude-skills.json` recording what
|
|
|
172
194
|
npx @spardutti/claude-skills --sync
|
|
173
195
|
```
|
|
174
196
|
|
|
175
|
-
`--sync` refreshes every tracked item, and any hooks the project already has, to the latest catalog and prunes stale ones in one shot — no menu. For a project that predates the manifest, the first normal run offers a one-time cleanup of `.claude/` content no longer in the catalog.
|
|
197
|
+
`--sync` refreshes every tracked item, and any hooks the project already has, to the latest catalog and prunes stale ones in one shot — no menu. It adds any command a tracked command requires, and names each skill or command new to the catalog once, so you can pick it. For a project that predates the manifest, the first normal run offers a one-time cleanup of `.claude/` content no longer in the catalog.
|
|
176
198
|
|
|
177
199
|
`--sync-all[=dir]` does the same for every project under `dir` (default: your home folder) that has a manifest, fetching the catalog once:
|
|
178
200
|
|
package/hooks/registry-lib.mjs
CHANGED
|
@@ -57,7 +57,8 @@ export function loadRegistry(root, files) {
|
|
|
57
57
|
// Code-shaped words (AimStrategy, pick_target, aimRegistry) or `backticked` — never "implement".
|
|
58
58
|
export function symbolsOf(extendBy = "") {
|
|
59
59
|
const ticked = [...extendBy.matchAll(/`([\w.]+)`/g)].map((m) => m[1]);
|
|
60
|
-
const
|
|
60
|
+
const bare = extendBy.replace(/`[^`]*`/g, " ");
|
|
61
|
+
const shaped = bare.match(/\b(?:[A-Z][a-z0-9]+[A-Z]\w*|[a-z]\w*[A-Z]\w*|\w+_\w+)\b/g) ?? [];
|
|
61
62
|
return [...new Set([...ticked, ...shaped])];
|
|
62
63
|
}
|
|
63
64
|
|
package/hooks/registry.mjs
CHANGED
|
@@ -9,6 +9,12 @@ const root = projectRoot();
|
|
|
9
9
|
const files = projectFiles(root);
|
|
10
10
|
const entries = loadRegistry(root, files);
|
|
11
11
|
const uses = (text, word) => new RegExp(`\\b${escape(word)}\\b`).test(text);
|
|
12
|
+
// Code calls a method on a value (`e.chill(2)`), so `EnemyState.chill` is the type named plus `.chill` used.
|
|
13
|
+
const usesIn = (text, name) => {
|
|
14
|
+
const parts = name.split(".");
|
|
15
|
+
if (parts.length === 1 || uses(text, name)) return uses(text, name);
|
|
16
|
+
return uses(text, parts[0]) && new RegExp(`\\.${escape(parts.at(-1))}\\b`).test(text);
|
|
17
|
+
};
|
|
12
18
|
const fromCwd = (f) => relative(root, resolve(f));
|
|
13
19
|
|
|
14
20
|
function list() {
|
|
@@ -38,7 +44,7 @@ function checkPlan(planPath, stubs) {
|
|
|
38
44
|
for (const e of entries) {
|
|
39
45
|
const mark = marks.get(e.name);
|
|
40
46
|
if (!mark) fails.push(`registry FAIL ${e.name}: missing from ## Registry — mark it reuse or skip`);
|
|
41
|
-
else if (mark === "reuse" && !symbolsOf(e.extend_by).some((s) =>
|
|
47
|
+
else if (mark === "reuse" && !symbolsOf(e.extend_by).some((s) => usesIn(added, s))) {
|
|
42
48
|
fails.push(`registry FAIL ${e.name}: marked reuse, but no stub uses ${symbolsOf(e.extend_by).join(", ") || "a name from extend_by"}`);
|
|
43
49
|
} else if (mark === "skip") {
|
|
44
50
|
const hits = triggerHits(e, added);
|
package/hooks/ship-prep.sh
CHANGED
|
@@ -17,6 +17,8 @@ start() {
|
|
|
17
17
|
local branch base dirty behind ahead
|
|
18
18
|
branch=$(git branch --show-current)
|
|
19
19
|
[ -n "$branch" ] || stop "Detached HEAD — check out a branch first."
|
|
20
|
+
# Before any output: `start | head -3` closed the pipe and killed the script before a last-line mark.
|
|
21
|
+
bash "$HERE/ship-mark.sh" begin || stop "ship-mark.sh begin failed."
|
|
20
22
|
if git remote | grep -qx origin; then git fetch -q origin 2>/dev/null; else echo "remote none: pr, merge and release cannot run"; fi
|
|
21
23
|
base=$(base_branch)
|
|
22
24
|
echo "branch $branch"
|
|
@@ -35,7 +37,6 @@ start() {
|
|
|
35
37
|
else
|
|
36
38
|
echo "gh NOT logged in: pr, merge and release cannot run"
|
|
37
39
|
fi
|
|
38
|
-
bash "$HERE/ship-mark.sh" begin || stop "ship-mark.sh begin failed."
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
skills_report() { # skills_report <files...>
|