skillvault 0.9.1 → 0.9.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/dist/cli.js +114 -110
- 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.3';
|
|
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 {
|
|
@@ -259,50 +270,52 @@ function configureSessionHook() {
|
|
|
259
270
|
settings.hooks.SessionStart = [];
|
|
260
271
|
if (!settings.hooks.SessionEnd)
|
|
261
272
|
settings.hooks.SessionEnd = [];
|
|
262
|
-
// ── SessionStart: auto-sync (permanent) ──
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
// ── Remove legacy PostToolCall + PreToolCall hooks ──
|
|
273
|
+
// ── SessionStart: auto-sync (permanent, unpinned so it always gets latest) ──
|
|
274
|
+
// Remove all existing skillvault SessionStart hooks and re-add with current format
|
|
275
|
+
settings.hooks.SessionStart = settings.hooks.SessionStart.filter((h) => !hasSkillvaultCommand(h));
|
|
276
|
+
settings.hooks.SessionStart.push({
|
|
277
|
+
matcher: 'startup',
|
|
278
|
+
hooks: [{
|
|
279
|
+
type: 'command',
|
|
280
|
+
command: 'npx -y skillvault --sync',
|
|
281
|
+
timeout: 30,
|
|
282
|
+
}],
|
|
283
|
+
});
|
|
284
|
+
// ── SessionEnd: cleanup (permanent, pinned to current version) ──
|
|
285
|
+
// Remove all existing skillvault SessionEnd hooks and re-add with current version
|
|
286
|
+
settings.hooks.SessionEnd = settings.hooks.SessionEnd.filter((h) => !hasSkillvaultCommand(h));
|
|
287
|
+
settings.hooks.SessionEnd.push({
|
|
288
|
+
hooks: [{
|
|
289
|
+
type: 'command',
|
|
290
|
+
command: `npx -y skillvault@${VERSION} --session-cleanup`,
|
|
291
|
+
}],
|
|
292
|
+
});
|
|
293
|
+
// ── Remove legacy PostToolCall + PreToolCall hooks (invalid event names) ──
|
|
283
294
|
let removedLegacy = false;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
295
|
+
for (const legacyKey of ['PostToolCall', 'PreToolCall']) {
|
|
296
|
+
if (Array.isArray(settings.hooks[legacyKey])) {
|
|
297
|
+
const before = settings.hooks[legacyKey].length;
|
|
298
|
+
settings.hooks[legacyKey] = settings.hooks[legacyKey].filter((h) => !hasSkillvaultCommand(h));
|
|
299
|
+
if (settings.hooks[legacyKey].length < before)
|
|
300
|
+
removedLegacy = true;
|
|
301
|
+
if (settings.hooks[legacyKey].length === 0)
|
|
302
|
+
delete settings.hooks[legacyKey];
|
|
303
|
+
}
|
|
291
304
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
305
|
+
// ── Remove old-format hooks (command on entry instead of hooks array) ──
|
|
306
|
+
for (const key of ['SessionEnd', 'SessionStart', 'UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
307
|
+
if (Array.isArray(settings.hooks[key])) {
|
|
308
|
+
const before = settings.hooks[key].length;
|
|
309
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !(h.command?.includes('skillvault') && !h.hooks));
|
|
310
|
+
if (settings.hooks[key].length < before)
|
|
311
|
+
removedLegacy = true;
|
|
312
|
+
if (settings.hooks[key].length === 0)
|
|
313
|
+
delete settings.hooks[key];
|
|
314
|
+
}
|
|
299
315
|
}
|
|
300
316
|
mkdirSync(join(HOME, '.claude'), { recursive: true });
|
|
301
317
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
302
|
-
|
|
303
|
-
console.error(' ✅ Auto-sync hook installed');
|
|
304
|
-
if (!hasEndHook)
|
|
305
|
-
console.error(' ✅ Session cleanup hook installed');
|
|
318
|
+
console.error(' ✅ Hooks updated');
|
|
306
319
|
if (removedLegacy)
|
|
307
320
|
console.error(' 🧹 Removed legacy monitoring hooks (now session-scoped)');
|
|
308
321
|
}
|
|
@@ -341,34 +354,45 @@ function injectMonitoringHooks(skillName) {
|
|
|
341
354
|
};
|
|
342
355
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
343
356
|
writeFileSync(activeSessionPath, JSON.stringify(session, null, 2), { mode: 0o600 });
|
|
344
|
-
// ──
|
|
345
|
-
|
|
346
|
-
settings.hooks
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
settings.hooks.UserPromptSubmit.push({
|
|
350
|
-
command: 'npx skillvault --session-keepalive --event-type prompt-submit',
|
|
351
|
-
});
|
|
357
|
+
// ── Remove old-format monitoring hooks (command on entry, no hooks array) ──
|
|
358
|
+
for (const key of ['UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
359
|
+
if (Array.isArray(settings.hooks[key])) {
|
|
360
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !(h.command?.includes('skillvault') && !h.hooks));
|
|
361
|
+
}
|
|
352
362
|
}
|
|
353
|
-
// ──
|
|
354
|
-
|
|
355
|
-
settings.hooks
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
command: 'npx skillvault --session-keepalive --event-type tool-use',
|
|
361
|
-
});
|
|
363
|
+
// ── Remove legacy PostToolCall/PreToolCall keys ──
|
|
364
|
+
for (const legacyKey of ['PostToolCall', 'PreToolCall']) {
|
|
365
|
+
if (Array.isArray(settings.hooks[legacyKey])) {
|
|
366
|
+
settings.hooks[legacyKey] = settings.hooks[legacyKey].filter((h) => !hasSkillvaultCommand(h));
|
|
367
|
+
if (settings.hooks[legacyKey].length === 0)
|
|
368
|
+
delete settings.hooks[legacyKey];
|
|
369
|
+
}
|
|
362
370
|
}
|
|
363
|
-
// ──
|
|
364
|
-
|
|
365
|
-
settings.hooks
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
settings.hooks.Stop.push({
|
|
369
|
-
command: 'npx skillvault --session-keepalive --event-type stop',
|
|
370
|
-
});
|
|
371
|
+
// ── Monitoring hooks: remove existing and re-add with current version ──
|
|
372
|
+
for (const key of ['UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
373
|
+
if (!Array.isArray(settings.hooks[key]))
|
|
374
|
+
settings.hooks[key] = [];
|
|
375
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !hasSkillvaultCommand(h));
|
|
371
376
|
}
|
|
377
|
+
settings.hooks.UserPromptSubmit.push({
|
|
378
|
+
hooks: [{
|
|
379
|
+
type: 'command',
|
|
380
|
+
command: `npx -y skillvault@${VERSION} --session-keepalive --event-type prompt-submit`,
|
|
381
|
+
}],
|
|
382
|
+
});
|
|
383
|
+
settings.hooks.PostToolUse.push({
|
|
384
|
+
matcher: 'Write|Bash|Edit',
|
|
385
|
+
hooks: [{
|
|
386
|
+
type: 'command',
|
|
387
|
+
command: `npx -y skillvault@${VERSION} --session-keepalive --event-type tool-use`,
|
|
388
|
+
}],
|
|
389
|
+
});
|
|
390
|
+
settings.hooks.Stop.push({
|
|
391
|
+
hooks: [{
|
|
392
|
+
type: 'command',
|
|
393
|
+
command: `npx -y skillvault@${VERSION} --session-keepalive --event-type stop`,
|
|
394
|
+
}],
|
|
395
|
+
});
|
|
372
396
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
373
397
|
}
|
|
374
398
|
catch {
|
|
@@ -1004,10 +1028,10 @@ function watermarkLayer5(content, id, skillName) {
|
|
|
1004
1028
|
const timestamp = new Date().toISOString();
|
|
1005
1029
|
const secret = 'skillvault-heartbeat-v1'; // shared secret for HMAC
|
|
1006
1030
|
// HMAC-based 5-digit values
|
|
1007
|
-
const hmac1 = createHmac('sha256', secret);
|
|
1031
|
+
const hmac1 = createHmac('sha256', secret); // nosemgrep: hardcoded-hmac-key — intentional forensic fingerprint key, not a secret
|
|
1008
1032
|
hmac1.update(`${id}:${timestamp}:hb1`);
|
|
1009
1033
|
const hb1 = (parseInt(hmac1.digest('hex').slice(0, 8), 16) % 100000).toString().padStart(5, '0');
|
|
1010
|
-
const hmac2 = createHmac('sha256', secret);
|
|
1034
|
+
const hmac2 = createHmac('sha256', secret); // nosemgrep: hardcoded-hmac-key — intentional forensic fingerprint key, not a secret
|
|
1011
1035
|
hmac2.update(`${id}:${timestamp}:hb2`);
|
|
1012
1036
|
const hb2 = (parseInt(hmac2.digest('hex').slice(0, 8), 16) % 100000).toString().padStart(5, '0');
|
|
1013
1037
|
// Detect language from code blocks
|
|
@@ -1053,7 +1077,7 @@ function watermarkLayer5(content, id, skillName) {
|
|
|
1053
1077
|
// Context templates (selected by HMAC of skill name)
|
|
1054
1078
|
const ctx1Templates = ['Initialize workspace:', 'Allow environment to settle:', 'Brief initialization delay:', 'Pre-flight pause:'];
|
|
1055
1079
|
const ctx2Templates = ['Brief pause between operations:', 'Allow buffers to flush:', 'Cooldown before next step:', 'Rate limit compliance delay:'];
|
|
1056
|
-
const ctxHmac = createHmac('sha256', secret);
|
|
1080
|
+
const ctxHmac = createHmac('sha256', secret); // nosemgrep: hardcoded-hmac-key — intentional context template selector
|
|
1057
1081
|
ctxHmac.update(skillName);
|
|
1058
1082
|
const ctxHash = ctxHmac.digest();
|
|
1059
1083
|
const ctx1 = ctx1Templates[ctxHash[0] % ctx1Templates.length];
|
|
@@ -1714,49 +1738,27 @@ function handleSessionCleanup() {
|
|
|
1714
1738
|
const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
1715
1739
|
if (settings.hooks) {
|
|
1716
1740
|
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;
|
|
1741
|
+
// Remove session-scoped monitoring hooks (both old and new format)
|
|
1742
|
+
for (const key of ['UserPromptSubmit', 'PostToolUse', 'Stop']) {
|
|
1743
|
+
if (Array.isArray(settings.hooks[key])) {
|
|
1744
|
+
const before = settings.hooks[key].length;
|
|
1745
|
+
settings.hooks[key] = settings.hooks[key].filter((h) => !hasSkillvaultCommand(h));
|
|
1746
|
+
if (settings.hooks[key].length < before)
|
|
1747
|
+
modified = true;
|
|
1748
|
+
if (settings.hooks[key].length === 0)
|
|
1749
|
+
delete settings.hooks[key];
|
|
1750
|
+
}
|
|
1752
1751
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1752
|
+
// Remove legacy PostToolCall / PreToolCall keys (invalid event names)
|
|
1753
|
+
for (const legacyKey of ['PostToolCall', 'PreToolCall']) {
|
|
1754
|
+
if (Array.isArray(settings.hooks[legacyKey])) {
|
|
1755
|
+
const before = settings.hooks[legacyKey].length;
|
|
1756
|
+
settings.hooks[legacyKey] = settings.hooks[legacyKey].filter((h) => !hasSkillvaultCommand(h));
|
|
1757
|
+
if (settings.hooks[legacyKey].length < before)
|
|
1758
|
+
modified = true;
|
|
1759
|
+
if (settings.hooks[legacyKey].length === 0)
|
|
1760
|
+
delete settings.hooks[legacyKey];
|
|
1761
|
+
}
|
|
1760
1762
|
}
|
|
1761
1763
|
if (modified) {
|
|
1762
1764
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
|
|
@@ -1850,6 +1852,8 @@ async function main() {
|
|
|
1850
1852
|
process.exit(1);
|
|
1851
1853
|
}
|
|
1852
1854
|
console.error('🔐 SkillVault Sync\n');
|
|
1855
|
+
// Re-install hooks with current version (auto-upgrades pinned commands)
|
|
1856
|
+
configureSessionHook();
|
|
1853
1857
|
await syncSkills();
|
|
1854
1858
|
const result = await installSkillStubs();
|
|
1855
1859
|
console.error(`\n ✅ ${result.installed} installed, ${result.skipped} up to date\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillvault",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
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 {};
|