skilldb 0.4.2 → 0.5.0

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
@@ -221,6 +221,8 @@ function truncate2(str, max) {
221
221
  }
222
222
 
223
223
  // src/commands/add.ts
224
+ import fs3 from "fs";
225
+ import path3 from "path";
224
226
  import pc3 from "picocolors";
225
227
 
226
228
  // src/cache.ts
@@ -306,9 +308,38 @@ function updateGitignore(cwd) {
306
308
  }
307
309
 
308
310
  // src/commands/add.ts
309
- async function addCommand(packName) {
311
+ function getConfig() {
312
+ try {
313
+ return JSON.parse(fs3.readFileSync(path3.join(process.cwd(), ".skilldb", "config.json"), "utf-8"));
314
+ } catch {
315
+ return {};
316
+ }
317
+ }
318
+ function getSkillsDir() {
319
+ const config = getConfig();
320
+ const dir = config.skillsDir;
321
+ if (dir && fs3.existsSync(path3.dirname(dir))) return dir;
322
+ const cwd = process.cwd();
323
+ if (fs3.existsSync(path3.join(cwd, ".claude"))) return path3.join(cwd, ".claude", "skills", "skilldb");
324
+ if (fs3.existsSync(path3.join(cwd, ".cursor"))) return path3.join(cwd, ".cursor", "rules", "skilldb");
325
+ if (fs3.existsSync(path3.join(cwd, ".codex"))) return path3.join(cwd, ".codex", "skills", "skilldb");
326
+ return null;
327
+ }
328
+ async function addCommand(packName, options) {
310
329
  const client = new SkillDBClient();
330
+ const target = options?.target || getConfig().installMode || "cache";
311
331
  console.log(pc3.bold(`Adding pack: ${packName}`));
332
+ const useSkillsDir = target === "skills-dir" || target === "hybrid";
333
+ const skillsDir = useSkillsDir ? getSkillsDir() : null;
334
+ if (useSkillsDir && !skillsDir) {
335
+ console.error(pc3.red('No IDE skills directory found. Run "skilldb init" first.'));
336
+ process.exit(1);
337
+ }
338
+ if (useSkillsDir && skillsDir) {
339
+ console.log(`Target: ${pc3.cyan(skillsDir)} ${pc3.dim("(IDE auto-loads these)")}`);
340
+ } else {
341
+ console.log(`Target: ${pc3.dim(".skilldb/skills/ (cache)")}`);
342
+ }
312
343
  initCache();
313
344
  try {
314
345
  const res = await client.list({ pack: packName, limit: 500, includeContent: true });
@@ -319,19 +350,40 @@ async function addCommand(packName) {
319
350
  let added = 0;
320
351
  let skipped = 0;
321
352
  for (const skill of res.skills) {
322
- if (isCached(skill.id)) {
323
- skipped++;
324
- console.log(pc3.dim(` skip ${skill.id} (already cached)`));
325
- continue;
353
+ if (!isCached(skill.id)) {
354
+ cacheSkill(skill);
355
+ }
356
+ if (useSkillsDir && skillsDir && skill.content) {
357
+ const packDir = path3.join(skillsDir, skill.pack);
358
+ fs3.mkdirSync(packDir, { recursive: true });
359
+ const skillFile = path3.join(packDir, skill.name + ".md");
360
+ if (fs3.existsSync(skillFile)) {
361
+ skipped++;
362
+ console.log(pc3.dim(` skip ${skill.id} (already installed)`));
363
+ } else {
364
+ fs3.writeFileSync(skillFile, skill.content);
365
+ added++;
366
+ console.log(pc3.green(` add ${skill.id}`) + pc3.dim(` \u2192 ${path3.relative(process.cwd(), skillFile)}`));
367
+ }
368
+ } else if (!useSkillsDir) {
369
+ if (isCached(skill.id)) {
370
+ skipped++;
371
+ console.log(pc3.dim(` skip ${skill.id} (already cached)`));
372
+ } else {
373
+ added++;
374
+ console.log(pc3.green(` add ${skill.id}`));
375
+ }
326
376
  }
327
- const filePath = cacheSkill(skill);
328
- added++;
329
- console.log(pc3.green(` add ${skill.id}`) + pc3.dim(` \u2192 ${filePath}`));
330
377
  }
331
378
  console.log(
332
379
  `
333
380
  ${pc3.green(`${added} skill${added === 1 ? "" : "s"} added`)}` + (skipped > 0 ? pc3.dim(`, ${skipped} skipped`) : "")
334
381
  );
382
+ if (useSkillsDir && skillsDir) {
383
+ console.log(pc3.dim(`
384
+ Skills installed to ${path3.relative(process.cwd(), skillsDir)}`));
385
+ console.log(pc3.dim("Your IDE will auto-load them \u2014 no CLAUDE.md changes needed."));
386
+ }
335
387
  if (res.skills.some((s) => !s.content)) {
336
388
  console.log(pc3.yellow("\nNote: Some skills were cached without content (metadata only)."));
337
389
  console.log(pc3.yellow('Run "skilldb login" with a Pro key to download full content.'));
@@ -403,8 +455,8 @@ async function infoCommand(id) {
403
455
  }
404
456
 
405
457
  // src/commands/init.ts
406
- import fs3 from "fs";
407
- import path3 from "path";
458
+ import fs4 from "fs";
459
+ import path4 from "path";
408
460
  import pc6 from "picocolors";
409
461
  import readline from "readline";
410
462
  function prompt(question) {
@@ -417,48 +469,38 @@ function prompt(question) {
417
469
  });
418
470
  }
419
471
  function detectIDE(cwd) {
420
- if (fs3.existsSync(path3.join(cwd, "CLAUDE.md")) || fs3.existsSync(path3.join(cwd, ".claude"))) {
472
+ if (fs4.existsSync(path4.join(cwd, "CLAUDE.md")) || fs4.existsSync(path4.join(cwd, ".claude"))) {
421
473
  return {
422
474
  ide: "claude-code",
423
475
  label: "Claude Code",
424
- configFile: path3.join(cwd, "CLAUDE.md")
476
+ skillsDir: path4.join(cwd, ".claude", "skills", "skilldb"),
477
+ configFile: path4.join(cwd, "CLAUDE.md")
425
478
  };
426
479
  }
427
- if (fs3.existsSync(path3.join(cwd, ".cursor")) || fs3.existsSync(path3.join(cwd, ".cursorrules"))) {
480
+ if (fs4.existsSync(path4.join(cwd, ".cursor")) || fs4.existsSync(path4.join(cwd, ".cursorrules"))) {
428
481
  return {
429
482
  ide: "cursor",
430
483
  label: "Cursor",
431
- configFile: fs3.existsSync(path3.join(cwd, ".cursorrules")) ? path3.join(cwd, ".cursorrules") : path3.join(cwd, ".cursor", "rules")
484
+ skillsDir: path4.join(cwd, ".cursor", "rules", "skilldb"),
485
+ configFile: fs4.existsSync(path4.join(cwd, ".cursorrules")) ? path4.join(cwd, ".cursorrules") : path4.join(cwd, ".cursor", "rules", "skilldb.md")
432
486
  };
433
487
  }
434
- if (fs3.existsSync(path3.join(cwd, "codex.md")) || fs3.existsSync(path3.join(cwd, ".codex"))) {
488
+ if (fs4.existsSync(path4.join(cwd, "codex.md")) || fs4.existsSync(path4.join(cwd, ".codex"))) {
435
489
  return {
436
490
  ide: "codex",
437
491
  label: "Codex CLI",
438
- configFile: path3.join(cwd, "codex.md")
492
+ skillsDir: path4.join(cwd, ".codex", "skills", "skilldb"),
493
+ configFile: path4.join(cwd, "codex.md")
439
494
  };
440
495
  }
441
496
  return null;
442
497
  }
443
- function getIntegrationSnippet(ide) {
444
- const marker = "<!-- skilldb:start -->";
445
- const endMarker = "<!-- skilldb:end -->";
446
- const content = `
447
- ## SkillDB Skills
448
-
449
- Local skills are available in \`.skilldb/skills/\`. Use them as reference when working on tasks.
450
- Search: \`skilldb search react server components\` (supports multiple words)
451
- Download a skill: \`skilldb get <pack>/<skill>\` (e.g. \`skilldb get software-skills/code-review\`)
452
- Download a full pack: \`skilldb add <pack>\`
453
- `.trim();
454
- return `
455
-
456
- ${marker}
457
- ${content}
458
- ${endMarker}
459
- `;
460
- }
461
- async function initCommand() {
498
+ var IDE_SKILLS_DIRS = {
499
+ "claude-code": ".claude/skills/skilldb",
500
+ "cursor": ".cursor/rules/skilldb",
501
+ "codex": ".codex/skills/skilldb"
502
+ };
503
+ async function initCommand(options) {
462
504
  const cwd = process.cwd();
463
505
  console.log(pc6.bold("SkillDB Init\n"));
464
506
  let detection = detectIDE(cwd);
@@ -476,42 +518,131 @@ async function initCommand() {
476
518
  console.log(pc6.red("Invalid choice."));
477
519
  process.exit(1);
478
520
  }
479
- const configFiles = {
480
- "claude-code": path3.join(cwd, "CLAUDE.md"),
481
- "cursor": path3.join(cwd, ".cursorrules"),
482
- "codex": path3.join(cwd, "codex.md")
483
- };
484
521
  detection = {
485
522
  ide,
486
523
  label: ide === "claude-code" ? "Claude Code" : ide === "cursor" ? "Cursor" : "Codex CLI",
487
- configFile: configFiles[ide]
524
+ skillsDir: path4.join(cwd, IDE_SKILLS_DIRS[ide]),
525
+ configFile: path4.join(cwd, ide === "claude-code" ? "CLAUDE.md" : ide === "cursor" ? ".cursorrules" : "codex.md")
488
526
  };
489
527
  }
528
+ const targetArg = options?.target;
529
+ let mode;
530
+ if (targetArg === "mcp") mode = "mcp";
531
+ else if (targetArg === "skills-dir") mode = "skills-dir";
532
+ else if (targetArg === "claude-md" || targetArg === "config") mode = "claude-md";
533
+ else {
534
+ console.log(`
535
+ Where should skills be loaded from?
536
+ `);
537
+ console.log(` 1) ${pc6.cyan("MCP Server")} ${pc6.dim("\u2014 agent fetches on demand, nothing added to files (recommended)")}`);
538
+ console.log(` 2) ${pc6.cyan("Skills directory")} ${pc6.dim(`\u2014 installed to ${IDE_SKILLS_DIRS[detection.ide]}, auto-loaded by IDE`)}`);
539
+ console.log(` 3) ${pc6.cyan("Hybrid")} ${pc6.dim("\u2014 MCP for search + skills dir for always-on core skills")}`);
540
+ console.log(` 4) ${pc6.cyan(path4.basename(detection.configFile))} ${pc6.dim("\u2014 append to config file (legacy, can bloat)")}`);
541
+ const modeChoice = await prompt("\nChoice (1-4): ");
542
+ mode = { "1": "mcp", "2": "skills-dir", "3": "hybrid", "4": "claude-md" }[modeChoice] || "hybrid";
543
+ }
490
544
  const cacheDir = initCache(cwd);
491
- console.log(`Created ${pc6.dim(cacheDir)}`);
545
+ console.log(`
546
+ Created ${pc6.dim(cacheDir)}`);
492
547
  updateGitignore(cwd);
493
- console.log(`Updated ${pc6.dim(".gitignore")}`);
494
- const snippet = getIntegrationSnippet(detection.ide);
495
- const configFile = detection.configFile;
496
- let existing = "";
497
- if (fs3.existsSync(configFile)) {
498
- existing = fs3.readFileSync(configFile, "utf-8");
499
- }
500
- if (existing.includes("<!-- skilldb:start -->")) {
501
- console.log(pc6.dim(`${path3.basename(configFile)} already has SkillDB integration.`));
502
- } else {
503
- const dir = path3.dirname(configFile);
504
- if (!fs3.existsSync(dir)) {
505
- fs3.mkdirSync(dir, { recursive: true });
548
+ if (mode === "mcp" || mode === "hybrid") {
549
+ console.log(`
550
+ ${pc6.cyan("MCP Server setup:")}`);
551
+ console.log(` Install globally: ${pc6.dim("npm install -g skilldb")}`);
552
+ if (detection.ide === "claude-code") {
553
+ console.log(` Add to Claude: ${pc6.dim("claude mcp add skilldb -- skilldb-mcp")}`);
554
+ } else if (detection.ide === "cursor") {
555
+ console.log(` Add to Cursor: ${pc6.dim("Add to .cursor/mcp.json:")}`);
556
+ console.log(pc6.dim(` { "mcpServers": { "skilldb": { "command": "skilldb-mcp" } } }`));
557
+ } else {
558
+ console.log(` Run: ${pc6.dim("skilldb-mcp")}`);
559
+ }
560
+ const marker = "<!-- skilldb:start -->";
561
+ const snippet = `
562
+
563
+ ${marker}
564
+ ## SkillDB
565
+ Skills available via MCP server. Use \`skilldb_search\` to find skills and \`skilldb_get\` to load them.
566
+ <!-- skilldb:end -->
567
+ `;
568
+ const configFile = detection.configFile;
569
+ let existing = "";
570
+ if (fs4.existsSync(configFile)) existing = fs4.readFileSync(configFile, "utf-8");
571
+ if (!existing.includes("skilldb:start")) {
572
+ fs4.mkdirSync(path4.dirname(configFile), { recursive: true });
573
+ fs4.writeFileSync(configFile, existing + snippet);
574
+ console.log(`
575
+ Added MCP reference to ${pc6.cyan(path4.basename(configFile))} ${pc6.dim("(2 lines, no bloat)")}`);
506
576
  }
507
- fs3.writeFileSync(configFile, existing + snippet);
508
- console.log(`Added SkillDB snippet to ${pc6.cyan(path3.basename(configFile))}`);
509
577
  }
510
- console.log(pc6.green("\nDone! Next steps:"));
511
- console.log(` ${pc6.dim("$")} skilldb login ${pc6.dim("# save your API key")}`);
512
- console.log(` ${pc6.dim("$")} skilldb search react server components ${pc6.dim("# multi-word search")}`);
513
- console.log(` ${pc6.dim("$")} skilldb get software-skills/code-review ${pc6.dim("# download one skill")}`);
514
- console.log(` ${pc6.dim("$")} skilldb add game-design-skills ${pc6.dim("# download full pack")}`);
578
+ if (mode === "skills-dir" || mode === "hybrid") {
579
+ const skillsDir = detection.skillsDir;
580
+ fs4.mkdirSync(skillsDir, { recursive: true });
581
+ console.log(`
582
+ ${pc6.cyan("Skills directory:")} ${pc6.dim(skillsDir)}`);
583
+ console.log(` Skills installed here are auto-loaded by ${detection.label}.`);
584
+ console.log(` Add skills: ${pc6.dim("skilldb add software-skills --target skills-dir")}`);
585
+ fs4.writeFileSync(path4.join(skillsDir, "README.md"), `# SkillDB Skills
586
+
587
+ Skills in this directory are automatically loaded by ${detection.label}.
588
+
589
+ Manage with:
590
+ - \`skilldb add <pack> --target skills-dir\`
591
+ - \`skilldb remove <pack>\`
592
+ - \`skilldb use <profile>\`
593
+
594
+ Browse: https://skilldb.dev
595
+ `);
596
+ }
597
+ if (mode === "claude-md") {
598
+ const marker = "<!-- skilldb:start -->";
599
+ const endMarker = "<!-- skilldb:end -->";
600
+ const content = `
601
+ ## SkillDB Skills
602
+
603
+ Local skills: \`.skilldb/skills/\`. Use as reference for tasks.
604
+ Search: \`skilldb search <query>\`
605
+ Download: \`skilldb add <pack>\`
606
+ `;
607
+ const snippet = `
608
+
609
+ ${marker}${content}${endMarker}
610
+ `;
611
+ const configFile = detection.configFile;
612
+ let existing = "";
613
+ if (fs4.existsSync(configFile)) existing = fs4.readFileSync(configFile, "utf-8");
614
+ if (!existing.includes("skilldb:start")) {
615
+ fs4.mkdirSync(path4.dirname(configFile), { recursive: true });
616
+ fs4.writeFileSync(configFile, existing + snippet);
617
+ console.log(`
618
+ Added SkillDB snippet to ${pc6.cyan(path4.basename(configFile))}`);
619
+ }
620
+ console.log(pc6.yellow("\n\u26A0 Config-file mode can bloat over time. Consider --target mcp or --target skills-dir."));
621
+ }
622
+ const configPath = path4.join(cwd, ".skilldb", "config.json");
623
+ let config = {};
624
+ try {
625
+ config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
626
+ } catch {
627
+ }
628
+ config.installMode = mode;
629
+ config.ide = detection.ide;
630
+ config.skillsDir = detection.skillsDir;
631
+ fs4.writeFileSync(configPath, JSON.stringify(config, null, 2));
632
+ console.log(pc6.green("\n\u2713 Done! Next steps:"));
633
+ if (mode === "mcp" || mode === "hybrid") {
634
+ console.log(` ${pc6.dim("$")} npm install -g skilldb`);
635
+ if (detection.ide === "claude-code") {
636
+ console.log(` ${pc6.dim("$")} claude mcp add skilldb -- skilldb-mcp`);
637
+ }
638
+ console.log(` Then ask your agent: ${pc6.dim('"Search SkillDB for code review skills"')}`);
639
+ }
640
+ if (mode === "skills-dir" || mode === "hybrid") {
641
+ console.log(` ${pc6.dim("$")} skilldb add software-skills --target skills-dir`);
642
+ }
643
+ if (mode === "claude-md") {
644
+ console.log(` ${pc6.dim("$")} skilldb add software-skills`);
645
+ }
515
646
  }
516
647
 
517
648
  // src/commands/login.ts
@@ -552,8 +683,8 @@ async function loginCommand() {
552
683
  }
553
684
 
554
685
  // src/commands/use.ts
555
- import fs4 from "fs";
556
- import path4 from "path";
686
+ import fs5 from "fs";
687
+ import path5 from "path";
557
688
  import pc8 from "picocolors";
558
689
  var SKILLDB_DIR2 = ".skilldb";
559
690
  var ACTIVE_DIR = "active";
@@ -569,23 +700,23 @@ var PROFILES = {
569
700
  "ai-agent": ["ai-agent-skills", "prompt-engineering-skills", "llm-skills"]
570
701
  };
571
702
  function readConfig(cwd) {
572
- const p = path4.join(cwd, SKILLDB_DIR2, CONFIG_FILE);
703
+ const p = path5.join(cwd, SKILLDB_DIR2, CONFIG_FILE);
573
704
  try {
574
- return JSON.parse(fs4.readFileSync(p, "utf-8"));
705
+ return JSON.parse(fs5.readFileSync(p, "utf-8"));
575
706
  } catch {
576
707
  return {};
577
708
  }
578
709
  }
579
710
  function writeConfig(cwd, config) {
580
- const dir = path4.join(cwd, SKILLDB_DIR2);
581
- if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
582
- fs4.writeFileSync(path4.join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + "\n");
711
+ const dir = path5.join(cwd, SKILLDB_DIR2);
712
+ if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
713
+ fs5.writeFileSync(path5.join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + "\n");
583
714
  }
584
715
  function autoDetect(cwd) {
585
- const has = (f) => fs4.existsSync(path4.join(cwd, f));
716
+ const has = (f) => fs5.existsSync(path5.join(cwd, f));
586
717
  let pkgDeps = [];
587
718
  try {
588
- const pkg = JSON.parse(fs4.readFileSync(path4.join(cwd, "package.json"), "utf-8"));
719
+ const pkg = JSON.parse(fs5.readFileSync(path5.join(cwd, "package.json"), "utf-8"));
589
720
  pkgDeps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
590
721
  } catch {
591
722
  }
@@ -600,19 +731,19 @@ function autoDetect(cwd) {
600
731
  return "fullstack";
601
732
  }
602
733
  function activateProfile(cwd, profile) {
603
- const activeDir = path4.join(cwd, SKILLDB_DIR2, ACTIVE_DIR);
604
- if (fs4.existsSync(activeDir)) fs4.rmSync(activeDir, { recursive: true });
605
- fs4.mkdirSync(activeDir, { recursive: true });
734
+ const activeDir = path5.join(cwd, SKILLDB_DIR2, ACTIVE_DIR);
735
+ if (fs5.existsSync(activeDir)) fs5.rmSync(activeDir, { recursive: true });
736
+ fs5.mkdirSync(activeDir, { recursive: true });
606
737
  const packs = PROFILES[profile] || [];
607
- const skillsDir = path4.join(cwd, SKILLDB_DIR2, "skills");
738
+ const skillsDir = path5.join(cwd, SKILLDB_DIR2, "skills");
608
739
  let copied = 0;
609
740
  for (const pack of packs) {
610
- const packDir = path4.join(skillsDir, pack);
611
- if (!fs4.existsSync(packDir)) continue;
612
- const destDir = path4.join(activeDir, pack);
613
- fs4.mkdirSync(destDir, { recursive: true });
614
- for (const file of fs4.readdirSync(packDir)) {
615
- fs4.copyFileSync(path4.join(packDir, file), path4.join(destDir, file));
741
+ const packDir = path5.join(skillsDir, pack);
742
+ if (!fs5.existsSync(packDir)) continue;
743
+ const destDir = path5.join(activeDir, pack);
744
+ fs5.mkdirSync(destDir, { recursive: true });
745
+ for (const file of fs5.readdirSync(packDir)) {
746
+ fs5.copyFileSync(path5.join(packDir, file), path5.join(destDir, file));
616
747
  copied++;
617
748
  }
618
749
  }
@@ -647,8 +778,8 @@ async function useCommand(profile, options) {
647
778
  return;
648
779
  }
649
780
  if (profile === "none") {
650
- const activeDir = path4.join(cwd, SKILLDB_DIR2, ACTIVE_DIR);
651
- if (fs4.existsSync(activeDir)) fs4.rmSync(activeDir, { recursive: true });
781
+ const activeDir = path5.join(cwd, SKILLDB_DIR2, ACTIVE_DIR);
782
+ if (fs5.existsSync(activeDir)) fs5.rmSync(activeDir, { recursive: true });
652
783
  const config = readConfig(cwd);
653
784
  delete config.activeProfile;
654
785
  writeConfig(cwd, config);
@@ -669,42 +800,42 @@ async function useCommand(profile, options) {
669
800
  }
670
801
 
671
802
  // src/commands/budget.ts
672
- import fs5 from "fs";
673
- import path5 from "path";
803
+ import fs6 from "fs";
804
+ import path6 from "path";
674
805
  import pc9 from "picocolors";
675
806
  var SKILLDB_DIR3 = ".skilldb";
676
807
  var CONFIG_FILE2 = "config.json";
677
808
  var ACTIVE_DIR2 = "active";
678
809
  var TOKENS_PER_LINE = 10;
679
810
  function readConfig2(cwd) {
680
- const p = path5.join(cwd, SKILLDB_DIR3, CONFIG_FILE2);
811
+ const p = path6.join(cwd, SKILLDB_DIR3, CONFIG_FILE2);
681
812
  try {
682
- return JSON.parse(fs5.readFileSync(p, "utf-8"));
813
+ return JSON.parse(fs6.readFileSync(p, "utf-8"));
683
814
  } catch {
684
815
  return {};
685
816
  }
686
817
  }
687
818
  function writeConfig2(cwd, config) {
688
- const dir = path5.join(cwd, SKILLDB_DIR3);
689
- if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
690
- fs5.writeFileSync(path5.join(dir, CONFIG_FILE2), JSON.stringify(config, null, 2) + "\n");
819
+ const dir = path6.join(cwd, SKILLDB_DIR3);
820
+ if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
821
+ fs6.writeFileSync(path6.join(dir, CONFIG_FILE2), JSON.stringify(config, null, 2) + "\n");
691
822
  }
692
823
  function countActiveSkills(cwd) {
693
- const activeDir = path5.join(cwd, SKILLDB_DIR3, ACTIVE_DIR2);
824
+ const activeDir = path6.join(cwd, SKILLDB_DIR3, ACTIVE_DIR2);
694
825
  const files = [];
695
826
  let totalLines = 0;
696
- if (!fs5.existsSync(activeDir)) return { files, totalLines, totalTokens: 0 };
827
+ if (!fs6.existsSync(activeDir)) return { files, totalLines, totalTokens: 0 };
697
828
  function walk(dir) {
698
- for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
699
- const full = path5.join(dir, entry.name);
829
+ for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
830
+ const full = path6.join(dir, entry.name);
700
831
  if (entry.isDirectory()) {
701
832
  walk(full);
702
833
  continue;
703
834
  }
704
835
  if (!entry.name.endsWith(".md")) continue;
705
- const content = fs5.readFileSync(full, "utf-8");
836
+ const content = fs6.readFileSync(full, "utf-8");
706
837
  const lines = content.split("\n").length;
707
- files.push(path5.relative(path5.join(cwd, SKILLDB_DIR3), full));
838
+ files.push(path6.relative(path6.join(cwd, SKILLDB_DIR3), full));
708
839
  totalLines += lines;
709
840
  }
710
841
  }
@@ -733,14 +864,14 @@ async function budgetCommand(action, value) {
733
864
  }
734
865
  if (action === "optimize") {
735
866
  let walk2 = function(dir) {
736
- for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
737
- const full = path5.join(dir, entry.name);
867
+ for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
868
+ const full = path6.join(dir, entry.name);
738
869
  if (entry.isDirectory()) {
739
870
  walk2(full);
740
871
  continue;
741
872
  }
742
873
  if (!entry.name.endsWith(".md")) continue;
743
- const lines = fs5.readFileSync(full, "utf-8").split("\n").length;
874
+ const lines = fs6.readFileSync(full, "utf-8").split("\n").length;
744
875
  skillFiles.push({ path: full, lines });
745
876
  }
746
877
  };
@@ -754,7 +885,7 @@ async function budgetCommand(action, value) {
754
885
  console.log(pc9.green("Already within budget. No changes needed."));
755
886
  return;
756
887
  }
757
- const activeDir = path5.join(cwd, SKILLDB_DIR3, ACTIVE_DIR2);
888
+ const activeDir = path6.join(cwd, SKILLDB_DIR3, ACTIVE_DIR2);
758
889
  const skillFiles = [];
759
890
  walk2(activeDir);
760
891
  skillFiles.sort((a, b) => b.lines - a.lines);
@@ -762,10 +893,10 @@ async function budgetCommand(action, value) {
762
893
  let removed = 0;
763
894
  for (const sf of skillFiles) {
764
895
  if (current <= config.budget.max) break;
765
- fs5.unlinkSync(sf.path);
896
+ fs6.unlinkSync(sf.path);
766
897
  current -= config.budget.unit === "tokens" ? sf.lines * TOKENS_PER_LINE : sf.lines;
767
898
  removed++;
768
- console.log(pc9.yellow(` removed ${path5.basename(sf.path)}`) + pc9.dim(` (${sf.lines} lines)`));
899
+ console.log(pc9.yellow(` removed ${path6.basename(sf.path)}`) + pc9.dim(` (${sf.lines} lines)`));
769
900
  }
770
901
  console.log(pc9.green(`
771
902
  Optimized: removed ${removed} skill(s) to fit budget.`));
@@ -787,15 +918,15 @@ Optimized: removed ${removed} skill(s) to fit budget.`));
787
918
  }
788
919
 
789
920
  // src/commands/slim.ts
790
- import fs6 from "fs";
791
- import path6 from "path";
921
+ import fs7 from "fs";
922
+ import path7 from "path";
792
923
  import pc10 from "picocolors";
793
924
  var SKILLDB_DIR4 = ".skilldb";
794
925
  var ACTIVE_DIR3 = "active";
795
926
  var SLIM_DIR = "slim";
796
927
  var SKILLS_DIR2 = "skills";
797
928
  function ensureDir2(dir) {
798
- if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
929
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
799
930
  }
800
931
  function slimContent(content, ratio) {
801
932
  const lines = content.split("\n");
@@ -865,14 +996,14 @@ function slimContent(content, ratio) {
865
996
  function processDir(sourceDir, destDir, ratio) {
866
997
  ensureDir2(destDir);
867
998
  let count = 0;
868
- for (const entry of fs6.readdirSync(sourceDir, { withFileTypes: true })) {
869
- const srcPath = path6.join(sourceDir, entry.name);
999
+ for (const entry of fs7.readdirSync(sourceDir, { withFileTypes: true })) {
1000
+ const srcPath = path7.join(sourceDir, entry.name);
870
1001
  if (entry.isDirectory()) {
871
- count += processDir(srcPath, path6.join(destDir, entry.name), ratio);
1002
+ count += processDir(srcPath, path7.join(destDir, entry.name), ratio);
872
1003
  continue;
873
1004
  }
874
1005
  if (!entry.name.endsWith(".md")) continue;
875
- const content = fs6.readFileSync(srcPath, "utf-8");
1006
+ const content = fs7.readFileSync(srcPath, "utf-8");
876
1007
  const slimmed = slimContent(content, ratio);
877
1008
  const footer = `
878
1009
 
@@ -880,7 +1011,7 @@ function processDir(sourceDir, destDir, ratio) {
880
1011
  *Full skill: \`skilldb get ${entry.name.replace(".md", "")}\`*
881
1012
  `;
882
1013
  ensureDir2(destDir);
883
- fs6.writeFileSync(path6.join(destDir, entry.name), slimmed + footer);
1014
+ fs7.writeFileSync(path7.join(destDir, entry.name), slimmed + footer);
884
1015
  count++;
885
1016
  const origLines = content.split("\n").length;
886
1017
  const slimLines = slimmed.split("\n").length;
@@ -897,38 +1028,38 @@ async function slimCommand(pack, options) {
897
1028
  console.error(pc10.red("Ratio must be between 0 and 1 (e.g. 0.3 for 30%)."));
898
1029
  process.exit(1);
899
1030
  }
900
- const slimDir = path6.join(cwd, SKILLDB_DIR4, SLIM_DIR);
1031
+ const slimDir = path7.join(cwd, SKILLDB_DIR4, SLIM_DIR);
901
1032
  console.log(pc10.bold(`Generating slim versions (${Math.round(ratio * 100)}% ratio)
902
1033
  `));
903
1034
  let sourceDir;
904
1035
  if (pack) {
905
- sourceDir = path6.join(cwd, SKILLDB_DIR4, SKILLS_DIR2, pack);
906
- if (!fs6.existsSync(sourceDir)) {
907
- sourceDir = path6.join(cwd, SKILLDB_DIR4, ACTIVE_DIR3, pack);
1036
+ sourceDir = path7.join(cwd, SKILLDB_DIR4, SKILLS_DIR2, pack);
1037
+ if (!fs7.existsSync(sourceDir)) {
1038
+ sourceDir = path7.join(cwd, SKILLDB_DIR4, ACTIVE_DIR3, pack);
908
1039
  }
909
- if (!fs6.existsSync(sourceDir)) {
1040
+ if (!fs7.existsSync(sourceDir)) {
910
1041
  console.error(pc10.red(`Pack "${pack}" not found in skills/ or active/.`));
911
1042
  process.exit(1);
912
1043
  }
913
1044
  } else {
914
- sourceDir = path6.join(cwd, SKILLDB_DIR4, ACTIVE_DIR3);
915
- if (!fs6.existsSync(sourceDir)) {
916
- sourceDir = path6.join(cwd, SKILLDB_DIR4, SKILLS_DIR2);
1045
+ sourceDir = path7.join(cwd, SKILLDB_DIR4, ACTIVE_DIR3);
1046
+ if (!fs7.existsSync(sourceDir)) {
1047
+ sourceDir = path7.join(cwd, SKILLDB_DIR4, SKILLS_DIR2);
917
1048
  }
918
1049
  }
919
- if (!fs6.existsSync(sourceDir)) {
1050
+ if (!fs7.existsSync(sourceDir)) {
920
1051
  console.error(pc10.red('No skills found. Install skills first with "skilldb add <pack>".'));
921
1052
  process.exit(1);
922
1053
  }
923
- const destDir = pack ? path6.join(slimDir, pack) : slimDir;
1054
+ const destDir = pack ? path7.join(slimDir, pack) : slimDir;
924
1055
  const count = processDir(sourceDir, destDir, ratio);
925
1056
  console.log(pc10.green(`
926
1057
  ${count} skill(s) slimmed \u2192 .skilldb/slim/`));
927
1058
  }
928
1059
 
929
1060
  // src/commands/recommend.ts
930
- import fs7 from "fs";
931
- import path7 from "path";
1061
+ import fs8 from "fs";
1062
+ import path8 from "path";
932
1063
  import pc11 from "picocolors";
933
1064
  var TECH_MAP = {
934
1065
  react: ["react-patterns-skills", "web-polish-skills"],
@@ -954,7 +1085,7 @@ function scanProject(cwd) {
954
1085
  const detected = [];
955
1086
  const packs = /* @__PURE__ */ new Set();
956
1087
  try {
957
- const pkg = JSON.parse(fs7.readFileSync(path7.join(cwd, "package.json"), "utf-8"));
1088
+ const pkg = JSON.parse(fs8.readFileSync(path8.join(cwd, "package.json"), "utf-8"));
958
1089
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
959
1090
  for (const dep of Object.keys(allDeps)) {
960
1091
  const key = dep.replace(/^@[^/]+\//, "");
@@ -968,11 +1099,11 @@ function scanProject(cwd) {
968
1099
  }
969
1100
  } catch {
970
1101
  }
971
- if (fs7.existsSync(path7.join(cwd, "Dockerfile"))) {
1102
+ if (fs8.existsSync(path8.join(cwd, "Dockerfile"))) {
972
1103
  detected.push("docker");
973
1104
  (TECH_MAP.docker || []).forEach((p) => packs.add(p));
974
1105
  }
975
- if (fs7.existsSync(path7.join(cwd, "tsconfig.json"))) {
1106
+ if (fs8.existsSync(path8.join(cwd, "tsconfig.json"))) {
976
1107
  detected.push("typescript");
977
1108
  (TECH_MAP.typescript || []).forEach((p) => packs.add(p));
978
1109
  }
@@ -1034,8 +1165,8 @@ async function recommendCommand(options) {
1034
1165
  }
1035
1166
 
1036
1167
  // src/commands/doctor.ts
1037
- import fs8 from "fs";
1038
- import path8 from "path";
1168
+ import fs9 from "fs";
1169
+ import path9 from "path";
1039
1170
  import pc12 from "picocolors";
1040
1171
  var SKILLDB_DIR5 = ".skilldb";
1041
1172
  var SKILLS_DIR3 = "skills";
@@ -1043,26 +1174,26 @@ var ACTIVE_DIR4 = "active";
1043
1174
  var CONFIG_FILE3 = "config.json";
1044
1175
  var TOKENS_PER_LINE2 = 10;
1045
1176
  function readConfig3(cwd) {
1046
- const p = path8.join(cwd, SKILLDB_DIR5, CONFIG_FILE3);
1177
+ const p = path9.join(cwd, SKILLDB_DIR5, CONFIG_FILE3);
1047
1178
  try {
1048
- return JSON.parse(fs8.readFileSync(p, "utf-8"));
1179
+ return JSON.parse(fs9.readFileSync(p, "utf-8"));
1049
1180
  } catch {
1050
1181
  return {};
1051
1182
  }
1052
1183
  }
1053
1184
  function collectFiles(dir) {
1054
1185
  const result = [];
1055
- if (!fs8.existsSync(dir)) return result;
1186
+ if (!fs9.existsSync(dir)) return result;
1056
1187
  function walk(d) {
1057
- for (const entry of fs8.readdirSync(d, { withFileTypes: true })) {
1058
- const full = path8.join(d, entry.name);
1188
+ for (const entry of fs9.readdirSync(d, { withFileTypes: true })) {
1189
+ const full = path9.join(d, entry.name);
1059
1190
  if (entry.isDirectory()) {
1060
1191
  walk(full);
1061
1192
  continue;
1062
1193
  }
1063
1194
  if (!entry.name.endsWith(".md")) continue;
1064
- const lines = fs8.readFileSync(full, "utf-8").split("\n").length;
1065
- result.push({ name: path8.relative(dir, full), lines });
1195
+ const lines = fs9.readFileSync(full, "utf-8").split("\n").length;
1196
+ result.push({ name: path9.relative(dir, full), lines });
1066
1197
  }
1067
1198
  }
1068
1199
  walk(dir);
@@ -1074,29 +1205,29 @@ async function doctorCommand() {
1074
1205
  const config = readConfig3(cwd);
1075
1206
  let issues = 0;
1076
1207
  console.log(pc12.bold("SkillDB Doctor\n"));
1077
- const skilldbDir = path8.join(cwd, SKILLDB_DIR5);
1078
- if (!fs8.existsSync(skilldbDir)) {
1208
+ const skilldbDir = path9.join(cwd, SKILLDB_DIR5);
1209
+ if (!fs9.existsSync(skilldbDir)) {
1079
1210
  console.log(pc12.red(' [!] .skilldb/ not found. Run "skilldb init" first.'));
1080
1211
  return;
1081
1212
  }
1082
1213
  console.log(pc12.green(" [ok] .skilldb/ directory exists"));
1083
1214
  const installedIds = Object.keys(manifest.installed);
1084
- const skillsDir = path8.join(cwd, SKILLDB_DIR5, SKILLS_DIR3);
1215
+ const skillsDir = path9.join(cwd, SKILLDB_DIR5, SKILLS_DIR3);
1085
1216
  const skillFiles = collectFiles(skillsDir);
1086
1217
  console.log(pc12.green(` [ok] ${installedIds.length} skills in manifest, ${skillFiles.length} files on disk`));
1087
1218
  let orphaned = 0;
1088
1219
  for (const id of installedIds) {
1089
1220
  const [pack, file] = id.split("/");
1090
1221
  const name = file.replace(".md", "").replace(/[/\\:*?"<>|]/g, "-");
1091
- const filePath = path8.join(skillsDir, pack, `${name}.md`);
1092
- if (!fs8.existsSync(filePath)) {
1222
+ const filePath = path9.join(skillsDir, pack, `${name}.md`);
1223
+ if (!fs9.existsSync(filePath)) {
1093
1224
  orphaned++;
1094
1225
  if (orphaned <= 3) console.log(pc12.yellow(` [!] Missing file for manifest entry: ${id}`));
1095
1226
  }
1096
1227
  }
1097
1228
  if (orphaned > 3) console.log(pc12.yellow(` [!] ...and ${orphaned - 3} more missing files`));
1098
1229
  if (orphaned > 0) issues++;
1099
- const activeDir = path8.join(cwd, SKILLDB_DIR5, ACTIVE_DIR4);
1230
+ const activeDir = path9.join(cwd, SKILLDB_DIR5, ACTIVE_DIR4);
1100
1231
  const activeFiles = collectFiles(activeDir);
1101
1232
  if (config.activeProfile) {
1102
1233
  console.log(pc12.green(` [ok] Active profile: ${config.activeProfile} (${activeFiles.length} skills)`));
@@ -1133,8 +1264,8 @@ ${issues} issue(s) found. Run suggested commands to fix.`)
1133
1264
  }
1134
1265
 
1135
1266
  // src/commands/update.ts
1136
- import fs9 from "fs";
1137
- import path9 from "path";
1267
+ import fs10 from "fs";
1268
+ import path10 from "path";
1138
1269
  import pc13 from "picocolors";
1139
1270
  var SKILLDB_DIR6 = ".skilldb";
1140
1271
  var SKILLS_DIR4 = "skills";
@@ -1173,7 +1304,7 @@ async function updateCommand(pack) {
1173
1304
  console.log(pc13.dim(` skip ${id} (not found on remote)`));
1174
1305
  continue;
1175
1306
  }
1176
- const localPath = path9.join(
1307
+ const localPath = path10.join(
1177
1308
  cwd,
1178
1309
  SKILLDB_DIR6,
1179
1310
  SKILLS_DIR4,
@@ -1182,7 +1313,7 @@ async function updateCommand(pack) {
1182
1313
  );
1183
1314
  let localContent = "";
1184
1315
  try {
1185
- localContent = fs9.readFileSync(localPath, "utf-8");
1316
+ localContent = fs10.readFileSync(localPath, "utf-8");
1186
1317
  } catch {
1187
1318
  }
1188
1319
  if (remote.content && remote.content !== localContent) {
@@ -1211,19 +1342,19 @@ ${pc13.green(`${updated} updated`)}` + pc13.dim(`, ${unchanged} unchanged`) + (f
1211
1342
  }
1212
1343
 
1213
1344
  // src/commands/remove.ts
1214
- import fs10 from "fs";
1215
- import path10 from "path";
1345
+ import fs11 from "fs";
1346
+ import path11 from "path";
1216
1347
  import pc14 from "picocolors";
1217
1348
  var SKILLDB_DIR7 = ".skilldb";
1218
1349
  var SKILLS_DIR5 = "skills";
1219
1350
  var ACTIVE_DIR5 = "active";
1220
1351
  function collectActiveSkills(cwd) {
1221
- const activeDir = path10.join(cwd, SKILLDB_DIR7, ACTIVE_DIR5);
1352
+ const activeDir = path11.join(cwd, SKILLDB_DIR7, ACTIVE_DIR5);
1222
1353
  const result = /* @__PURE__ */ new Set();
1223
- if (!fs10.existsSync(activeDir)) return result;
1224
- for (const pack of fs10.readdirSync(activeDir, { withFileTypes: true })) {
1354
+ if (!fs11.existsSync(activeDir)) return result;
1355
+ for (const pack of fs11.readdirSync(activeDir, { withFileTypes: true })) {
1225
1356
  if (!pack.isDirectory()) continue;
1226
- for (const file of fs10.readdirSync(path10.join(activeDir, pack.name))) {
1357
+ for (const file of fs11.readdirSync(path11.join(activeDir, pack.name))) {
1227
1358
  if (file.endsWith(".md")) {
1228
1359
  result.add(`${pack.name}/${file}`);
1229
1360
  }
@@ -1234,12 +1365,12 @@ function collectActiveSkills(cwd) {
1234
1365
  function removeSkillFile(cwd, id) {
1235
1366
  const [pack, file] = id.split("/");
1236
1367
  const name = file.replace(".md", "").replace(/[/\\:*?"<>|]/g, "-");
1237
- const filePath = path10.join(cwd, SKILLDB_DIR7, SKILLS_DIR5, pack, `${name}.md`);
1238
- if (fs10.existsSync(filePath)) {
1239
- fs10.unlinkSync(filePath);
1240
- const packDir = path10.join(cwd, SKILLDB_DIR7, SKILLS_DIR5, pack);
1241
- if (fs10.existsSync(packDir) && fs10.readdirSync(packDir).length === 0) {
1242
- fs10.rmdirSync(packDir);
1368
+ const filePath = path11.join(cwd, SKILLDB_DIR7, SKILLS_DIR5, pack, `${name}.md`);
1369
+ if (fs11.existsSync(filePath)) {
1370
+ fs11.unlinkSync(filePath);
1371
+ const packDir = path11.join(cwd, SKILLDB_DIR7, SKILLS_DIR5, pack);
1372
+ if (fs11.existsSync(packDir) && fs11.readdirSync(packDir).length === 0) {
1373
+ fs11.rmdirSync(packDir);
1243
1374
  }
1244
1375
  return true;
1245
1376
  }
@@ -1314,32 +1445,32 @@ ${packIds.length} skill(s) from "${target}" removed.`));
1314
1445
  }
1315
1446
 
1316
1447
  // src/commands/export.ts
1317
- import fs11 from "fs";
1318
- import path11 from "path";
1448
+ import fs12 from "fs";
1449
+ import path12 from "path";
1319
1450
  import pc15 from "picocolors";
1320
1451
  var SKILLDB_DIR8 = ".skilldb";
1321
1452
  var ACTIVE_DIR6 = "active";
1322
1453
  var CONFIG_FILE4 = "config.json";
1323
1454
  function readConfig4(cwd) {
1324
- const p = path11.join(cwd, SKILLDB_DIR8, CONFIG_FILE4);
1455
+ const p = path12.join(cwd, SKILLDB_DIR8, CONFIG_FILE4);
1325
1456
  try {
1326
- return JSON.parse(fs11.readFileSync(p, "utf-8"));
1457
+ return JSON.parse(fs12.readFileSync(p, "utf-8"));
1327
1458
  } catch {
1328
1459
  return {};
1329
1460
  }
1330
1461
  }
1331
1462
  function collectActiveSkills2(cwd) {
1332
- const activeDir = path11.join(cwd, SKILLDB_DIR8, ACTIVE_DIR6);
1463
+ const activeDir = path12.join(cwd, SKILLDB_DIR8, ACTIVE_DIR6);
1333
1464
  const result = [];
1334
- if (!fs11.existsSync(activeDir)) return result;
1335
- for (const pack of fs11.readdirSync(activeDir, { withFileTypes: true })) {
1465
+ if (!fs12.existsSync(activeDir)) return result;
1466
+ for (const pack of fs12.readdirSync(activeDir, { withFileTypes: true })) {
1336
1467
  if (!pack.isDirectory()) continue;
1337
- for (const file of fs11.readdirSync(path11.join(activeDir, pack.name))) {
1468
+ for (const file of fs12.readdirSync(path12.join(activeDir, pack.name))) {
1338
1469
  if (file.endsWith(".md")) {
1339
1470
  result.push({
1340
1471
  pack: pack.name,
1341
1472
  name: file.replace(".md", ""),
1342
- path: path11.join(activeDir, pack.name, file)
1473
+ path: path12.join(activeDir, pack.name, file)
1343
1474
  });
1344
1475
  }
1345
1476
  }
@@ -1387,7 +1518,7 @@ function exportProfile(cwd) {
1387
1518
  exportedAt: (/* @__PURE__ */ new Date()).toISOString()
1388
1519
  };
1389
1520
  const filename = ".skilldb-profile.json";
1390
- fs11.writeFileSync(path11.join(cwd, filename), JSON.stringify(profile, null, 2) + "\n");
1521
+ fs12.writeFileSync(path12.join(cwd, filename), JSON.stringify(profile, null, 2) + "\n");
1391
1522
  console.log(pc15.green(`Exported profile to ${filename}`));
1392
1523
  console.log(pc15.dim('Share this file and import with "skilldb use --import .skilldb-profile.json"'));
1393
1524
  }
@@ -1398,7 +1529,7 @@ function exportInject(cwd, id) {
1398
1529
  console.error(pc15.red(`Skill "${id}" not found locally. Run "skilldb get ${id}" first.`));
1399
1530
  process.exit(1);
1400
1531
  }
1401
- const content = fs11.readFileSync(cachedPath, "utf-8");
1532
+ const content = fs12.readFileSync(cachedPath, "utf-8");
1402
1533
  process.stdout.write(content);
1403
1534
  }
1404
1535
  async function exportCommand(format, target) {
@@ -1427,8 +1558,8 @@ async function exportCommand(format, target) {
1427
1558
  }
1428
1559
 
1429
1560
  // src/commands/stats.ts
1430
- import fs12 from "fs";
1431
- import path12 from "path";
1561
+ import fs13 from "fs";
1562
+ import path13 from "path";
1432
1563
  import pc16 from "picocolors";
1433
1564
  var SKILLDB_DIR9 = ".skilldb";
1434
1565
  var SKILLS_DIR6 = "skills";
@@ -1436,25 +1567,25 @@ var ACTIVE_DIR7 = "active";
1436
1567
  var CONFIG_FILE5 = "config.json";
1437
1568
  var TOKENS_PER_LINE3 = 10;
1438
1569
  function readConfig5(cwd) {
1439
- const p = path12.join(cwd, SKILLDB_DIR9, CONFIG_FILE5);
1570
+ const p = path13.join(cwd, SKILLDB_DIR9, CONFIG_FILE5);
1440
1571
  try {
1441
- return JSON.parse(fs12.readFileSync(p, "utf-8"));
1572
+ return JSON.parse(fs13.readFileSync(p, "utf-8"));
1442
1573
  } catch {
1443
1574
  return {};
1444
1575
  }
1445
1576
  }
1446
1577
  function countDir(dir) {
1447
1578
  const result = { files: 0, lines: 0, packs: /* @__PURE__ */ new Set(), categories: /* @__PURE__ */ new Set() };
1448
- if (!fs12.existsSync(dir)) return result;
1449
- for (const pack of fs12.readdirSync(dir, { withFileTypes: true })) {
1579
+ if (!fs13.existsSync(dir)) return result;
1580
+ for (const pack of fs13.readdirSync(dir, { withFileTypes: true })) {
1450
1581
  if (!pack.isDirectory()) continue;
1451
1582
  result.packs.add(pack.name);
1452
1583
  const cat = pack.name.replace(/-skills$/, "");
1453
1584
  result.categories.add(cat);
1454
- for (const file of fs12.readdirSync(path12.join(dir, pack.name))) {
1585
+ for (const file of fs13.readdirSync(path13.join(dir, pack.name))) {
1455
1586
  if (!file.endsWith(".md")) continue;
1456
1587
  result.files++;
1457
- result.lines += fs12.readFileSync(path12.join(dir, pack.name, file), "utf-8").split("\n").length;
1588
+ result.lines += fs13.readFileSync(path13.join(dir, pack.name, file), "utf-8").split("\n").length;
1458
1589
  }
1459
1590
  }
1460
1591
  return result;
@@ -1463,8 +1594,8 @@ async function statsCommand() {
1463
1594
  const cwd = process.cwd();
1464
1595
  const config = readConfig5(cwd);
1465
1596
  const manifest = readManifest(cwd);
1466
- const skillsDir = path12.join(cwd, SKILLDB_DIR9, SKILLS_DIR6);
1467
- const activeDir = path12.join(cwd, SKILLDB_DIR9, ACTIVE_DIR7);
1597
+ const skillsDir = path13.join(cwd, SKILLDB_DIR9, SKILLS_DIR6);
1598
+ const activeDir = path13.join(cwd, SKILLDB_DIR9, ACTIVE_DIR7);
1468
1599
  const installed = countDir(skillsDir);
1469
1600
  const active = countDir(activeDir);
1470
1601
  console.log(pc16.bold("SkillDB Stats\n"));
@@ -1502,8 +1633,8 @@ async function statsCommand() {
1502
1633
  }
1503
1634
 
1504
1635
  // src/commands/diff.ts
1505
- import fs13 from "fs";
1506
- import path13 from "path";
1636
+ import fs14 from "fs";
1637
+ import path14 from "path";
1507
1638
  import pc17 from "picocolors";
1508
1639
  var SKILLDB_DIR10 = ".skilldb";
1509
1640
  var SKILLS_DIR7 = "skills";
@@ -1564,15 +1695,15 @@ async function diffCommand(target) {
1564
1695
  if (!id.includes(".md")) id = id + ".md";
1565
1696
  const [pack, file] = id.split("/");
1566
1697
  const name = file.replace(".md", "").replace(/[/\\:*?"<>|]/g, "-");
1567
- const localPath = path13.join(cwd, SKILLDB_DIR10, SKILLS_DIR7, pack, `${name}.md`);
1568
- if (!fs13.existsSync(localPath)) {
1698
+ const localPath = path14.join(cwd, SKILLDB_DIR10, SKILLS_DIR7, pack, `${name}.md`);
1699
+ if (!fs14.existsSync(localPath)) {
1569
1700
  console.error(pc17.red(`Skill "${id}" not found locally.`));
1570
1701
  process.exit(1);
1571
1702
  }
1572
1703
  console.log(pc17.bold("Comparing with remote...\n"));
1573
1704
  try {
1574
1705
  const remote = await client.get(id);
1575
- const localContent = fs13.readFileSync(localPath, "utf-8");
1706
+ const localContent = fs14.readFileSync(localPath, "utf-8");
1576
1707
  if (!remote.content) {
1577
1708
  console.log(pc17.yellow("Remote content not available (API key required)."));
1578
1709
  return;
@@ -1603,10 +1734,10 @@ async function diffCommand(target) {
1603
1734
  if (!remote?.content) continue;
1604
1735
  const [pack, file] = id.split("/");
1605
1736
  const name = file.replace(".md", "").replace(/[/\\:*?"<>|]/g, "-");
1606
- const localPath = path13.join(cwd, SKILLDB_DIR10, SKILLS_DIR7, pack, `${name}.md`);
1737
+ const localPath = path14.join(cwd, SKILLDB_DIR10, SKILLS_DIR7, pack, `${name}.md`);
1607
1738
  let localContent = "";
1608
1739
  try {
1609
- localContent = fs13.readFileSync(localPath, "utf-8");
1740
+ localContent = fs14.readFileSync(localPath, "utf-8");
1610
1741
  } catch {
1611
1742
  continue;
1612
1743
  }
@@ -1632,14 +1763,14 @@ ${pc17.green(`${upToDate} up to date`)}` + (changed > 0 ? pc17.yellow(`, ${chang
1632
1763
  // src/cli.ts
1633
1764
  var program = new Command();
1634
1765
  program.name("skilldb").description("SkillDB CLI \u2014 discover, install, and manage AI agent skills").version("0.3.0");
1635
- program.command("init").description("Initialize SkillDB in this project (detect IDE, create .skilldb/)").action(initCommand);
1766
+ program.command("init").description("Initialize SkillDB in this project (detect IDE, choose install mode)").option("-t, --target <mode>", "Install mode: mcp, skills-dir, claude-md, hybrid").action((opts) => initCommand(opts));
1636
1767
  program.command("login").description("Save your SkillDB API key").action(loginCommand);
1637
1768
  program.command("search <query...>").description("Search skills by keyword(s)").option("-c, --category <name>", "Filter by category").option("-l, --limit <n>", "Max results", "20").action((queryParts, options) => {
1638
1769
  searchCommand(queryParts.join(" "), options);
1639
1770
  });
1640
1771
  program.command("list").description("List skills, optionally filtered").option("-c, --category <name>", "Filter by category").option("-p, --pack <name>", "Filter by pack").option("-l, --limit <n>", "Max results", "50").action(listCommand);
1641
1772
  program.command("get <id>").description('Download a single skill to .skilldb/skills/ (e.g. "software-skills/code-review")').action(getCommand);
1642
- program.command("add <pack>").description("Download an entire skill pack to local .skilldb/skills/ cache").action(addCommand);
1773
+ program.command("add <pack>").description("Download skills (use --target to choose where)").option("-t, --target <mode>", "Where to install: cache (default), skills-dir, hybrid").action((pack, opts) => addCommand(pack, opts));
1643
1774
  program.command("info <id>").description('Show metadata and preview for a skill (e.g. "software-skills/code-review")').action(infoCommand);
1644
1775
  program.command("use [profile]").description("Activate a skill profile (frontend, backend, devops, security, data, fullstack, mobile, ai-agent, auto, none)").option("--list", "List available profiles").option("--current", "Show active profile").action((profile, options) => {
1645
1776
  if (!profile && !options.list && !options.current) {