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 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.1';
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
- const hasEndHook = settings.hooks.SessionEnd.some((h) => h.command?.includes('skillvault') && h.command?.includes('session-cleanup'));
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
- command: 'npx skillvault --session-cleanup',
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
- if (Array.isArray(settings.hooks.PostToolCall)) {
285
- const before = settings.hooks.PostToolCall.length;
286
- settings.hooks.PostToolCall = settings.hooks.PostToolCall.filter((h) => !h.command?.includes('skillvault'));
287
- if (settings.hooks.PostToolCall.length < before)
288
- removedLegacy = true;
289
- if (settings.hooks.PostToolCall.length === 0)
290
- delete settings.hooks.PostToolCall;
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
- if (Array.isArray(settings.hooks.PreToolCall)) {
293
- const before = settings.hooks.PreToolCall.length;
294
- settings.hooks.PreToolCall = settings.hooks.PreToolCall.filter((h) => !h.command?.includes('skillvault'));
295
- if (settings.hooks.PreToolCall.length < before)
296
- removedLegacy = true;
297
- if (settings.hooks.PreToolCall.length === 0)
298
- delete settings.hooks.PreToolCall;
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
- command: 'npx skillvault --session-keepalive --event-type prompt-submit',
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
- command: 'npx skillvault --session-keepalive --event-type tool-use',
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
- command: 'npx skillvault --session-keepalive --event-type stop',
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 UserPromptSubmit skillvault hooks
1718
- if (Array.isArray(settings.hooks.UserPromptSubmit)) {
1719
- const before = settings.hooks.UserPromptSubmit.length;
1720
- settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter((h) => !h.command?.includes('skillvault'));
1721
- if (settings.hooks.UserPromptSubmit.length < before)
1722
- modified = true;
1723
- if (settings.hooks.UserPromptSubmit.length === 0)
1724
- delete settings.hooks.UserPromptSubmit;
1725
- }
1726
- // Remove PostToolUse skillvault session-keepalive hooks
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
- if (Array.isArray(settings.hooks.PreToolCall)) {
1754
- const before = settings.hooks.PreToolCall.length;
1755
- settings.hooks.PreToolCall = settings.hooks.PreToolCall.filter((h) => !h.command?.includes('skillvault'));
1756
- if (settings.hooks.PreToolCall.length < before)
1757
- modified = true;
1758
- if (settings.hooks.PreToolCall.length === 0)
1759
- delete settings.hooks.PreToolCall;
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.1",
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 {};