skillvault 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/dist/cli.js +91 -5
  2. package/package.json +1 -1
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, createPublicKey, diffieHellman, hkdfSync, generateKeyPairSync, } from 'node:crypto';
23
- const VERSION = '0.5.0';
23
+ const VERSION = '0.5.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');
@@ -151,6 +151,8 @@ async function setup(code) {
151
151
  mkdirSync(join(VAULT_DIR, data.publisher_id), { recursive: true });
152
152
  // Clean up legacy MCP config if present
153
153
  cleanupMCPConfig();
154
+ // Install session hook for auto-sync
155
+ configureSessionHook();
154
156
  // Sync vaults and install stubs
155
157
  console.error('');
156
158
  console.error(' Syncing skills...');
@@ -161,6 +163,7 @@ async function setup(code) {
161
163
  console.error(` ✅ ${installResult.installed} skill${installResult.installed !== 1 ? 's' : ''} installed to ~/.claude/skills/`);
162
164
  }
163
165
  console.error(' ✅ Setup complete! Restart Claude Code to use your skills.');
166
+ console.error(' Skills will auto-sync at the start of each Claude Code session.');
164
167
  console.error('');
165
168
  }
166
169
  /** Remove legacy MCP server config from ~/.claude/.mcp.json */
@@ -178,6 +181,43 @@ function cleanupMCPConfig() {
178
181
  }
179
182
  catch { }
180
183
  }
184
+ /**
185
+ * Install a SessionStart hook in Claude Code settings so skills auto-sync
186
+ * at the start of each session. This discovers new skills from all
187
+ * existing publishers without the customer doing anything.
188
+ */
189
+ function configureSessionHook() {
190
+ const settingsPath = join(HOME, '.claude', 'settings.json');
191
+ try {
192
+ let settings = {};
193
+ if (existsSync(settingsPath)) {
194
+ settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
195
+ }
196
+ if (!settings.hooks)
197
+ settings.hooks = {};
198
+ if (!settings.hooks.SessionStart)
199
+ settings.hooks.SessionStart = [];
200
+ // Check if we already have a skillvault sync hook
201
+ const hasHook = settings.hooks.SessionStart.some((group) => group.matcher === 'startup' &&
202
+ group.hooks?.some((h) => h.command?.includes('skillvault')));
203
+ if (!hasHook) {
204
+ settings.hooks.SessionStart.push({
205
+ matcher: 'startup',
206
+ hooks: [{
207
+ type: 'command',
208
+ command: `npx skillvault@${VERSION} --sync`,
209
+ timeout: 30,
210
+ }],
211
+ });
212
+ mkdirSync(join(HOME, '.claude'), { recursive: true });
213
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { mode: 0o600 });
214
+ console.error(' ✅ Auto-sync hook installed');
215
+ }
216
+ }
217
+ catch {
218
+ console.error(' ⚠️ Could not install auto-sync hook — run npx skillvault --sync manually');
219
+ }
220
+ }
181
221
  async function showStatus() {
182
222
  const config = loadConfig();
183
223
  if (!config) {
@@ -439,14 +479,60 @@ async function installSkillStubs() {
439
479
  meta = JSON.parse(readFileSync(metaPath, 'utf8'));
440
480
  }
441
481
  catch { }
482
+ // Decrypt vault to extract frontmatter (name, description, triggers)
483
+ // The frontmatter is the "public storefront" — body stays encrypted
484
+ let frontmatter = '';
485
+ let frontmatterFields = {};
486
+ try {
487
+ const cek = await fetchCEK(skillName, pub.token);
488
+ const vaultData = readFileSync(vaultPath);
489
+ const vault = decryptVault(vaultData, cek);
490
+ cek.fill(0);
491
+ const skillMd = vault.files.find(f => f.path === 'SKILL.md');
492
+ if (skillMd) {
493
+ const fmMatch = skillMd.content.match(/^---\n([\s\S]*?)\n---/);
494
+ if (fmMatch) {
495
+ frontmatter = fmMatch[1];
496
+ // Parse YAML-like frontmatter fields
497
+ for (const line of frontmatter.split('\n')) {
498
+ const kv = line.match(/^(\S+):\s*(.+)$/);
499
+ if (kv)
500
+ frontmatterFields[kv[1]] = kv[2].replace(/^["']|["']$/g, '');
501
+ }
502
+ }
503
+ }
504
+ }
505
+ catch {
506
+ // If decrypt fails during stub install (e.g. offline), fall back to meta
507
+ }
508
+ const stubName = frontmatterFields['name'] || skillName;
509
+ const stubDescription = frontmatterFields['description'] || meta.description || '';
510
+ // Build frontmatter for stub — copy all fields except body-related ones
511
+ let stubFrontmatter = `name: ${stubName}\n`;
512
+ stubFrontmatter += `description: "${stubDescription.replace(/"/g, '\\"')}"\n`;
513
+ // Preserve publisher's allowed-tools, trigger config, etc. but always include our load tool
514
+ const publisherAllowedTools = frontmatterFields['allowed-tools'] || '';
515
+ const loadTool = `"Bash(npx skillvault@${VERSION} --load *)"`;
516
+ if (publisherAllowedTools && !publisherAllowedTools.includes('skillvault')) {
517
+ // Merge publisher's allowed-tools with ours
518
+ const merged = publisherAllowedTools.replace(/\]$/, `, ${loadTool}]`);
519
+ stubFrontmatter += `allowed-tools: ${merged}\n`;
520
+ }
521
+ else {
522
+ stubFrontmatter += `allowed-tools: [${loadTool}]\n`;
523
+ }
524
+ // Copy through other frontmatter fields the publisher set (for Claude triggering)
525
+ for (const [key, value] of Object.entries(frontmatterFields)) {
526
+ if (!['name', 'description', 'allowed-tools'].includes(key)) {
527
+ stubFrontmatter += `${key}: ${value}\n`;
528
+ }
529
+ }
442
530
  mkdirSync(skillDir, { recursive: true });
443
531
  const stub = `---
444
- name: ${skillName}
445
- description: "${(meta.description || '').replace(/"/g, '\\"')}"
446
- allowed-tools: ["Bash(npx skillvault@${VERSION} --load *)"]
532
+ ${stubFrontmatter.trimEnd()}
447
533
  ---
448
534
 
449
- # ${skillName}
535
+ # ${stubName}
450
536
 
451
537
  This is an encrypted SkillVault skill from **${meta.publisher_name || pub.name}**.
452
538
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillvault",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "SkillVault — secure skill distribution for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {