skillvault 0.9.1 → 0.9.2
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/dist/cli.js +94 -70
- package/package.json +2 -2
- package/dist/cli.d.ts +0 -20
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync } from 'node:fs';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { createDecipheriv, createHmac, createPublicKey, diffieHellman, hkdfSync, generateKeyPairSync, } from 'node:crypto';
|
|
23
|
-
const VERSION = '0.9.
|
|
23
|
+
const VERSION = '0.9.2';
|
|
24
24
|
const HOME = process.env.HOME || process.env.USERPROFILE || '~';
|
|
25
25
|
const API_URL = process.env.SKILLVAULT_API_URL || 'https://api.getskillvault.com';
|
|
26
26
|
const CONFIG_DIR = join(HOME, '.skillvault');
|
|
@@ -246,6 +246,17 @@ function cleanupMCPConfig() {
|
|
|
246
246
|
*
|
|
247
247
|
* Also removes legacy PostToolCall/PreToolCall hooks from older versions.
|
|
248
248
|
*/
|
|
249
|
+
/** Check if a hook entry (old or new format) contains a skillvault command. */
|
|
250
|
+
function hasSkillvaultCommand(entry) {
|
|
251
|
+
// Old format: { command: "npx skillvault ..." }
|
|
252
|
+
if (entry.command?.includes('skillvault'))
|
|
253
|
+
return true;
|
|
254
|
+
// New format: { hooks: [{ type: "command", command: "npx skillvault ..." }] }
|
|
255
|
+
if (Array.isArray(entry.hooks)) {
|
|
256
|
+
return entry.hooks.some((h) => h.command?.includes('skillvault'));
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
249
260
|
function configureSessionHook() {
|
|
250
261
|
const settingsPath = join(HOME, '.claude', 'settings.json');
|
|
251
262
|
try {
|
|
@@ -260,6 +271,8 @@ function configureSessionHook() {
|
|
|
260
271
|
if (!settings.hooks.SessionEnd)
|
|
261
272
|
settings.hooks.SessionEnd = [];
|
|
262
273
|
// ── SessionStart: auto-sync (permanent) ──
|
|
274
|
+
// Remove any old-format SessionStart entries before checking
|
|
275
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart.filter((h) => !(h.command?.includes('skillvault') && !h.hooks));
|
|
263
276
|
const hasStartHook = settings.hooks.SessionStart.some((group) => group.matcher === 'startup' &&
|
|
264
277
|
group.hooks?.some((h) => h.command?.includes('skillvault')));
|
|
265
278
|
if (!hasStartHook) {
|
|
@@ -267,35 +280,45 @@ function configureSessionHook() {
|
|
|
267
280
|
matcher: 'startup',
|
|
268
281
|
hooks: [{
|
|
269
282
|
type: 'command',
|
|
270
|
-
command: `npx skillvault@${VERSION} --sync`,
|
|
283
|
+
command: `npx -y skillvault@${VERSION} --sync`,
|
|
271
284
|
timeout: 30,
|
|
272
285
|
}],
|
|
273
286
|
});
|
|
274
287
|
}
|
|
275
288
|
// ── SessionEnd: cleanup (permanent) ──
|
|
276
|
-
|
|
289
|
+
// Remove any old-format SessionEnd entries before checking
|
|
290
|
+
settings.hooks.SessionEnd = settings.hooks.SessionEnd.filter((h) => !(h.command?.includes('skillvault') && !h.hooks));
|
|
291
|
+
const hasEndHook = settings.hooks.SessionEnd.some((g) => g.hooks?.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-cleanup')));
|
|
277
292
|
if (!hasEndHook) {
|
|
278
293
|
settings.hooks.SessionEnd.push({
|
|
279
|
-
|
|
294
|
+
hooks: [{
|
|
295
|
+
type: 'command',
|
|
296
|
+
command: `npx -y skillvault@${VERSION} --session-cleanup`,
|
|
297
|
+
}],
|
|
280
298
|
});
|
|
281
299
|
}
|
|
282
|
-
// ── Remove legacy PostToolCall + PreToolCall hooks ──
|
|
300
|
+
// ── Remove legacy PostToolCall + PreToolCall hooks (invalid event names) ──
|
|
283
301
|
let removedLegacy = false;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
302
|
+
for (const legacyKey of ['PostToolCall', 'PreToolCall']) {
|
|
303
|
+
if (Array.isArray(settings.hooks[legacyKey])) {
|
|
304
|
+
const before = settings.hooks[legacyKey].length;
|
|
305
|
+
settings.hooks[legacyKey] = settings.hooks[legacyKey].filter((h) => !hasSkillvaultCommand(h));
|
|
306
|
+
if (settings.hooks[legacyKey].length < before)
|
|
307
|
+
removedLegacy = true;
|
|
308
|
+
if (settings.hooks[legacyKey].length === 0)
|
|
309
|
+
delete settings.hooks[legacyKey];
|
|
310
|
+
}
|
|
291
311
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
312
|
+
// ── Remove old-format hooks (command on entry instead of hooks array) ──
|
|
313
|
+
for (const key of ['SessionEnd', 'SessionStart', 'UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
314
|
+
if (Array.isArray(settings.hooks[key])) {
|
|
315
|
+
const before = settings.hooks[key].length;
|
|
316
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !(h.command?.includes('skillvault') && !h.hooks));
|
|
317
|
+
if (settings.hooks[key].length < before)
|
|
318
|
+
removedLegacy = true;
|
|
319
|
+
if (settings.hooks[key].length === 0)
|
|
320
|
+
delete settings.hooks[key];
|
|
321
|
+
}
|
|
299
322
|
}
|
|
300
323
|
mkdirSync(join(HOME, '.claude'), { recursive: true });
|
|
301
324
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
@@ -341,32 +364,55 @@ function injectMonitoringHooks(skillName) {
|
|
|
341
364
|
};
|
|
342
365
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
343
366
|
writeFileSync(activeSessionPath, JSON.stringify(session, null, 2), { mode: 0o600 });
|
|
367
|
+
// ── Remove old-format monitoring hooks (command on entry, no hooks array) ──
|
|
368
|
+
for (const key of ['UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
369
|
+
if (Array.isArray(settings.hooks[key])) {
|
|
370
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !(h.command?.includes('skillvault') && !h.hooks));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// ── Remove legacy PostToolCall/PreToolCall keys ──
|
|
374
|
+
for (const legacyKey of ['PostToolCall', 'PreToolCall']) {
|
|
375
|
+
if (Array.isArray(settings.hooks[legacyKey])) {
|
|
376
|
+
settings.hooks[legacyKey] = settings.hooks[legacyKey].filter((h) => !hasSkillvaultCommand(h));
|
|
377
|
+
if (settings.hooks[legacyKey].length === 0)
|
|
378
|
+
delete settings.hooks[legacyKey];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
344
381
|
// ── UserPromptSubmit hook ──
|
|
345
382
|
if (!Array.isArray(settings.hooks.UserPromptSubmit))
|
|
346
383
|
settings.hooks.UserPromptSubmit = [];
|
|
347
|
-
const hasPromptHook = settings.hooks.UserPromptSubmit.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-keepalive'));
|
|
384
|
+
const hasPromptHook = settings.hooks.UserPromptSubmit.some((g) => g.hooks?.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-keepalive')));
|
|
348
385
|
if (!hasPromptHook) {
|
|
349
386
|
settings.hooks.UserPromptSubmit.push({
|
|
350
|
-
|
|
387
|
+
hooks: [{
|
|
388
|
+
type: 'command',
|
|
389
|
+
command: `npx -y skillvault@${VERSION} --session-keepalive --event-type prompt-submit`,
|
|
390
|
+
}],
|
|
351
391
|
});
|
|
352
392
|
}
|
|
353
393
|
// ── PostToolUse hook ──
|
|
354
394
|
if (!Array.isArray(settings.hooks.PostToolUse))
|
|
355
395
|
settings.hooks.PostToolUse = [];
|
|
356
|
-
const hasToolHook = settings.hooks.PostToolUse.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-keepalive'));
|
|
396
|
+
const hasToolHook = settings.hooks.PostToolUse.some((g) => g.hooks?.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-keepalive')));
|
|
357
397
|
if (!hasToolHook) {
|
|
358
398
|
settings.hooks.PostToolUse.push({
|
|
359
399
|
matcher: 'Write|Bash|Edit',
|
|
360
|
-
|
|
400
|
+
hooks: [{
|
|
401
|
+
type: 'command',
|
|
402
|
+
command: `npx -y skillvault@${VERSION} --session-keepalive --event-type tool-use`,
|
|
403
|
+
}],
|
|
361
404
|
});
|
|
362
405
|
}
|
|
363
406
|
// ── Stop hook ──
|
|
364
407
|
if (!Array.isArray(settings.hooks.Stop))
|
|
365
408
|
settings.hooks.Stop = [];
|
|
366
|
-
const hasStopHook = settings.hooks.Stop.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-keepalive'));
|
|
409
|
+
const hasStopHook = settings.hooks.Stop.some((g) => g.hooks?.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-keepalive')));
|
|
367
410
|
if (!hasStopHook) {
|
|
368
411
|
settings.hooks.Stop.push({
|
|
369
|
-
|
|
412
|
+
hooks: [{
|
|
413
|
+
type: 'command',
|
|
414
|
+
command: `npx -y skillvault@${VERSION} --session-keepalive --event-type stop`,
|
|
415
|
+
}],
|
|
370
416
|
});
|
|
371
417
|
}
|
|
372
418
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
@@ -1004,10 +1050,10 @@ function watermarkLayer5(content, id, skillName) {
|
|
|
1004
1050
|
const timestamp = new Date().toISOString();
|
|
1005
1051
|
const secret = 'skillvault-heartbeat-v1'; // shared secret for HMAC
|
|
1006
1052
|
// HMAC-based 5-digit values
|
|
1007
|
-
const hmac1 = createHmac('sha256', secret);
|
|
1053
|
+
const hmac1 = createHmac('sha256', secret); // nosemgrep: hardcoded-hmac-key — intentional forensic fingerprint key, not a secret
|
|
1008
1054
|
hmac1.update(`${id}:${timestamp}:hb1`);
|
|
1009
1055
|
const hb1 = (parseInt(hmac1.digest('hex').slice(0, 8), 16) % 100000).toString().padStart(5, '0');
|
|
1010
|
-
const hmac2 = createHmac('sha256', secret);
|
|
1056
|
+
const hmac2 = createHmac('sha256', secret); // nosemgrep: hardcoded-hmac-key — intentional forensic fingerprint key, not a secret
|
|
1011
1057
|
hmac2.update(`${id}:${timestamp}:hb2`);
|
|
1012
1058
|
const hb2 = (parseInt(hmac2.digest('hex').slice(0, 8), 16) % 100000).toString().padStart(5, '0');
|
|
1013
1059
|
// Detect language from code blocks
|
|
@@ -1053,7 +1099,7 @@ function watermarkLayer5(content, id, skillName) {
|
|
|
1053
1099
|
// Context templates (selected by HMAC of skill name)
|
|
1054
1100
|
const ctx1Templates = ['Initialize workspace:', 'Allow environment to settle:', 'Brief initialization delay:', 'Pre-flight pause:'];
|
|
1055
1101
|
const ctx2Templates = ['Brief pause between operations:', 'Allow buffers to flush:', 'Cooldown before next step:', 'Rate limit compliance delay:'];
|
|
1056
|
-
const ctxHmac = createHmac('sha256', secret);
|
|
1102
|
+
const ctxHmac = createHmac('sha256', secret); // nosemgrep: hardcoded-hmac-key — intentional context template selector
|
|
1057
1103
|
ctxHmac.update(skillName);
|
|
1058
1104
|
const ctxHash = ctxHmac.digest();
|
|
1059
1105
|
const ctx1 = ctx1Templates[ctxHash[0] % ctx1Templates.length];
|
|
@@ -1714,49 +1760,27 @@ function handleSessionCleanup() {
|
|
|
1714
1760
|
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
1715
1761
|
if (settings.hooks) {
|
|
1716
1762
|
let modified = false;
|
|
1717
|
-
// Remove
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
if (Array.isArray(settings.hooks.PostToolUse)) {
|
|
1728
|
-
const before = settings.hooks.PostToolUse.length;
|
|
1729
|
-
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((h) => !h.command?.includes('skillvault') || !h.command?.includes('session-keepalive'));
|
|
1730
|
-
if (settings.hooks.PostToolUse.length < before)
|
|
1731
|
-
modified = true;
|
|
1732
|
-
if (settings.hooks.PostToolUse.length === 0)
|
|
1733
|
-
delete settings.hooks.PostToolUse;
|
|
1734
|
-
}
|
|
1735
|
-
// Remove Stop skillvault session-keepalive hooks
|
|
1736
|
-
if (Array.isArray(settings.hooks.Stop)) {
|
|
1737
|
-
const before = settings.hooks.Stop.length;
|
|
1738
|
-
settings.hooks.Stop = settings.hooks.Stop.filter((h) => !h.command?.includes('skillvault') || !h.command?.includes('session-keepalive'));
|
|
1739
|
-
if (settings.hooks.Stop.length < before)
|
|
1740
|
-
modified = true;
|
|
1741
|
-
if (settings.hooks.Stop.length === 0)
|
|
1742
|
-
delete settings.hooks.Stop;
|
|
1743
|
-
}
|
|
1744
|
-
// Also remove legacy PostToolCall / PreToolCall hooks
|
|
1745
|
-
if (Array.isArray(settings.hooks.PostToolCall)) {
|
|
1746
|
-
const before = settings.hooks.PostToolCall.length;
|
|
1747
|
-
settings.hooks.PostToolCall = settings.hooks.PostToolCall.filter((h) => !h.command?.includes('skillvault'));
|
|
1748
|
-
if (settings.hooks.PostToolCall.length < before)
|
|
1749
|
-
modified = true;
|
|
1750
|
-
if (settings.hooks.PostToolCall.length === 0)
|
|
1751
|
-
delete settings.hooks.PostToolCall;
|
|
1763
|
+
// Remove session-scoped monitoring hooks (both old and new format)
|
|
1764
|
+
for (const key of ['UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
1765
|
+
if (Array.isArray(settings.hooks[key])) {
|
|
1766
|
+
const before = settings.hooks[key].length;
|
|
1767
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !hasSkillvaultCommand(h));
|
|
1768
|
+
if (settings.hooks[key].length < before)
|
|
1769
|
+
modified = true;
|
|
1770
|
+
if (settings.hooks[key].length === 0)
|
|
1771
|
+
delete settings.hooks[key];
|
|
1772
|
+
}
|
|
1752
1773
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1774
|
+
// Remove legacy PostToolCall / PreToolCall keys (invalid event names)
|
|
1775
|
+
for (const legacyKey of ['PostToolCall', 'PreToolCall']) {
|
|
1776
|
+
if (Array.isArray(settings.hooks[legacyKey])) {
|
|
1777
|
+
const before = settings.hooks[legacyKey].length;
|
|
1778
|
+
settings.hooks[legacyKey] = settings.hooks[legacyKey].filter((h) => !hasSkillvaultCommand(h));
|
|
1779
|
+
if (settings.hooks[legacyKey].length < before)
|
|
1780
|
+
modified = true;
|
|
1781
|
+
if (settings.hooks[legacyKey].length === 0)
|
|
1782
|
+
delete settings.hooks[legacyKey];
|
|
1783
|
+
}
|
|
1760
1784
|
}
|
|
1761
1785
|
if (modified) {
|
|
1762
1786
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillvault",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "SkillVault — secure skill distribution for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"start": "node dist/cli.js"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
|
-
"dist"
|
|
14
|
+
"dist/**/*.js"
|
|
15
15
|
],
|
|
16
16
|
"keywords": [
|
|
17
17
|
"skillvault",
|
package/dist/cli.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* SkillVault — Secure skill distribution for Claude Code.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* npx skillvault --invite CODE # Setup: redeem invite + sync + install
|
|
7
|
-
* npx skillvault --load SKILL # Decrypt a skill (used by Claude Code)
|
|
8
|
-
* npx skillvault --status # Show publishers & skills
|
|
9
|
-
* npx skillvault --refresh # Re-authenticate tokens + sync
|
|
10
|
-
* npx skillvault --sync # Sync vaults + update stubs
|
|
11
|
-
*
|
|
12
|
-
* How it works:
|
|
13
|
-
* 1. Customer runs --invite once → vaults downloaded, stubs installed
|
|
14
|
-
* 2. Claude Code discovers stub SKILL.md files in ~/.claude/skills/
|
|
15
|
-
* 3. When a skill is triggered, Claude runs: npx skillvault --load <name>
|
|
16
|
-
* 4. The CLI decrypts the vault (license checked) and outputs to stdout
|
|
17
|
-
* 5. Claude reads the output and follows the instructions
|
|
18
|
-
* 6. Decrypted content is never written to disk
|
|
19
|
-
*/
|
|
20
|
-
export {};
|